public static Transaction SignPayment(Transaction payment, Key key, ScriptCoin fundingCoin) { return(new TransactionBuilder() .AddKeys(key) .AddCoins(fundingCoin) .SignTransaction(payment)); }
public override void ConfigureEscrowedCoin(ScriptCoin escrowedCoin, Key escrowKey) { AssertState(PromiseClientStates.WaitingEscrow); Logs.Tumbler.LogDebug($"PromiseClientSession.ConfigureEscrowedCoin() - escrowedCoin.Outpoint.Hash : {escrowedCoin.Outpoint.Hash}, escrowedCoin.Outpoint.N : {escrowedCoin.Outpoint.N}"); base.ConfigureEscrowedCoin(escrowedCoin, escrowKey); InternalState.Status = PromiseClientStates.WaitingSignatureRequest; }
public PromiseClientSession ReceiveTumblerEscrowedCoin(ScriptCoin escrowedCoin) { AssertState(TumblerClientSessionStates.WaitingTumblerEscrow); var escrow = EscrowScriptPubKeyParameters.GetFromCoin(escrowedCoin); if (escrow == null) { throw new PuzzleException("invalid-escrow"); } if (!escrowedCoin.IsP2SH || escrowedCoin.RedeemType != RedeemType.WitnessV0) { throw new PuzzleException("invalid-escrow"); } var expectedEscrow = GetTumblerEscrowParameters(escrow.Initiator); if (escrow != expectedEscrow) { throw new PuzzleException("invalid-escrow"); } if (escrowedCoin.Amount != Parameters.Denomination) { throw new PuzzleException("invalid-amount"); } InternalState.Status = TumblerClientSessionStates.PromisePhase; var session = new PromiseClientSession(Parameters.CreatePromiseParamaters()); session.SetChannelId(InternalState.ChannelId); session.ConfigureEscrowedCoin(escrowedCoin, InternalState.TumblerEscrowKey); InternalState.TumblerEscrowKey = null; return(session); }
public async Task AcceptToMemoryPool_WithP2WSHValidTxns_IsSuccessfullAsync() { string dataDir = Path.Combine("TestData", nameof(MempoolValidatorTest), nameof(this.AcceptToMemoryPool_WithP2WSHValidTxns_IsSuccessfullAsync)); Directory.CreateDirectory(dataDir); BitcoinSecret miner = new BitcoinSecret(new Key(), Network.PurpleRegTest); ITestChainContext context = await TestChainFactory.CreateAsync(Network.PurpleRegTest, miner.PubKey.ScriptPubKey.WitHash.ScriptPubKey, dataDir); IMempoolValidator validator = context.MempoolValidator; Assert.NotNull(validator); BitcoinSecret bob = new BitcoinSecret(new Key(), Network.PurpleRegTest); // Fund Bob // 50 Coins come from first tx on chain - send bob 42 and change back to miner ScriptCoin witnessCoin = new ScriptCoin(context.SrcTxs[0].GetHash(), 0, context.SrcTxs[0].TotalOut, miner.PubKey.ScriptPubKey.WitHash.ScriptPubKey, miner.PubKey.ScriptPubKey); TransactionBuilder txBuilder = new TransactionBuilder(); Transaction p2wshTx = txBuilder .AddCoins(witnessCoin) .AddKeys(miner) .Send(bob, "42.00") .SendFees("0.001") .SetChange(miner) .BuildTransaction(true); Assert.True(txBuilder.Verify(p2wshTx)); //check fully signed MempoolValidationState state = new MempoolValidationState(false); Assert.True(await validator.AcceptToMemoryPool(state, p2wshTx), $"Transaction: {nameof(p2wshTx)} failed mempool validation."); Directory.Delete(dataDir, true); }
public virtual void ConfigureEscrowedCoin(uint160 channelId, ScriptCoin escrowedCoin, Key escrowKey, Script redeemDestination) { if (escrowedCoin == null) { throw new ArgumentNullException(nameof(escrowedCoin)); } if (escrowKey == null) { throw new ArgumentNullException(nameof(escrowKey)); } var escrow = EscrowScriptPubKeyParameters.GetFromCoin(escrowedCoin); if (escrow == null || escrow.Initiator != escrowKey.PubKey) { throw new PuzzleException("Invalid escrow"); } InternalState.ChannelId = channelId ?? throw new ArgumentNullException(nameof(channelId)); InternalState.EscrowedCoin = escrowedCoin; InternalState.EscrowKey = escrowKey; InternalState.RedeemDestination = redeemDestination ?? throw new ArgumentNullException(nameof(redeemDestination)); Logs.Tumbler.LogDebug( $"EscrowInitiator.ConfigureEscrowedCoin() - ChannelId : {InternalState.ChannelId}, EscrowedCoin.Outpoint.Hash : {InternalState.EscrowedCoin.Outpoint.Hash}, EscrowedCoin.Outpoint.N : {InternalState.EscrowedCoin.Outpoint.N}, RedeemDestination : {InternalState.RedeemDestination}"); }
public Transaction CreatePayment(Money paid, ScriptCoin fundingCoin) { if (paid > GetFundAmount()) { throw new MicroPaymentException("Payment reached the maximum"); } var builder = new TransactionBuilder(); if (fundingCoin.Redeem != Redeem || fundingCoin.Amount != Amount + Fees) { throw new MicroPaymentException("Invalid funding coin"); } var fees = Money.Min(paid, Fees); var toPayer = GetFundAmount() - paid; var toPayee = paid - fees; return(builder .AddCoins(fundingCoin) .Send(Payee.ScriptPubkey, toPayee) .Send(Payer.ScriptPubkey, toPayer) .SendFees(fees) .Shuffle() .BuildTransaction(false)); }
public static void Execute() { // This P2SH scriptPubKey represents the hash of the // multi - sig script: redeemScript.Hash.ScriptPubKey Key bob = new Key(); Key alice = new Key(); Key satoshi = new Key(); PubKey[] pubKeys = new[] { bob.PubKey, alice.PubKey, satoshi.PubKey }; Script redeemScript = PayToMultiSigTemplate.Instance.GenerateScriptPubKey(2, pubKeys); Script paymentScript = redeemScript.PaymentScript; Console.WriteLine("RedeemScript : " + redeemScript); Console.WriteLine("PaymentScript: " + paymentScript); Console.WriteLine("ScriptPubKey : " + redeemScript.Hash.ScriptPubKey); Console.WriteLine("WalletAddress: " + redeemScript.Hash.GetAddress(Network.Main)); // Imagine that the multi-sig P2SH receives a coin in a transaction called received. Transaction received = new Transaction(); // Pay to the script hash // Warning: The payment is sent to redeemScript.Hash and not to redeemScript! received.Outputs.Add(new TxOut(Money.Coins(1.0m), redeemScript.Hash)); Console.WriteLine("receivedTxn: \n" + received); // any 2 of 3 owner that control the multi-sig address want to spend // instead of creating a Coin they will need to create a ScriptCoin // Give the redeemScript to the coin for Transaction construction and signing ScriptCoin coin = received.Outputs.AsCoins().First().ToScriptCoin(redeemScript); Console.WriteLine("ScriptCoin: " + coin); // The rest of the code concerning transaction generation and signing is exactly the same // as in the previous section about native multi sig }
public virtual void ConfigureEscrowedCoin(ScriptCoin escrowedCoin, Key escrowKey, Script redeemDestination) { if (escrowedCoin == null) { throw new ArgumentNullException(nameof(escrowedCoin)); } if (escrowKey == null) { throw new ArgumentNullException(nameof(escrowKey)); } if (redeemDestination == null) { throw new ArgumentNullException(nameof(redeemDestination)); } var escrow = EscrowScriptPubKeyParameters.GetFromCoin(escrowedCoin); if (escrow == null || escrow.Initiator != escrowKey.PubKey) { throw new PuzzleException("Invalid escrow"); } InternalState.EscrowedCoin = escrowedCoin; InternalState.EscrowKey = escrowKey; InternalState.RedeemDestination = redeemDestination; }
private void Code14() { Key bob = new Key(); Key alice = new Key(); Key satoshi = new Key(); Script redeemScript = PayToMultiSigTemplate .Instance .GenerateScriptPubKey(2, new[] { bob.PubKey, alice.PubKey, satoshi.PubKey }); Transaction received = new Transaction(); //Pay to the script hash received.Outputs.Add(new TxOut(Money.Coins(1.0m), redeemScript.Hash)); TransactionBuilder builder = new TransactionBuilder(); //Give the redeemScript to the coin for Transaction construction //and signing ScriptCoin coin = new ScriptCoin( new OutPoint(received.GetHash(), 0), received.Outputs[0], redeemScript ); }
public PromiseClientSession ReceiveTumblerEscrowedCoin(ScriptCoin escrowedCoin) { AssertState(TumblerClientSessionStates.WaitingTumblerEscrow); var escrow = EscrowScriptPubKeyParameters.GetFromCoin(escrowedCoin); var expectedEscrow = new EscrowScriptPubKeyParameters() { Initiator = escrow?.Initiator, Receiver = InternalState.TumblerEscrowKey.PubKey, LockTime = GetCycle().GetTumblerLockTime() }; if (escrow == null || escrow != expectedEscrow) { throw new PuzzleException("invalid-escrow"); } if (escrowedCoin.Amount != Parameters.Denomination) { throw new PuzzleException("invalid-amount"); } InternalState.Status = TumblerClientSessionStates.PromisePhase; var session = new PromiseClientSession(Parameters.CreatePromiseParamaters()); session.SetChannelId(InternalState.ChannelId); session.ConfigureEscrowedCoin(escrowedCoin, InternalState.TumblerEscrowKey); InternalState.TumblerEscrowKey = null; return(session); }
public byte[] GetSignatureHash(Script redeemScript, ITxOutput spentOutput) { var coin = ((BitcoinBasedTxOutput)spentOutput).Coin; var scriptCoint = new ScriptCoin(coin, redeemScript); return(Tx.GetSignatureHash(scriptCoint).ToBytes()); }
public virtual void ConfigureEscrowedCoin(ScriptCoin escrowedCoin, Key escrowKey) { InternalState.EscrowKey = escrowKey ?? throw new ArgumentNullException(nameof(escrowKey)); InternalState.EscrowedCoin = escrowedCoin ?? throw new ArgumentNullException(nameof(escrowedCoin)); Logs.Tumbler.LogDebug( $"EscrowReceiver.ConfigureEscrowedCoin() - EscrowedCoin.Outpoint.Hash : {InternalState.EscrowedCoin.Outpoint.Hash}, EscrowedCoin.Outpoint.N : {InternalState.EscrowedCoin.Outpoint.N}"); }
public override void ConfigureEscrowedCoin(uint160 channelId, ScriptCoin escrowedCoin, Key escrowKey, Script redeemDestination) { AssertState(SolverClientStates.WaitingEscrow); Logs.Tumbler.LogDebug($"SolverClientSession.ConfigureEscrowedCoin() - escrowedCoin.Outpoint.Hash : {escrowedCoin.Outpoint.Hash}, escrowedCoin.Outpoint.N : {escrowedCoin.Outpoint.N}"); base.ConfigureEscrowedCoin(channelId, escrowedCoin, escrowKey, redeemDestination); InternalState.Status = SolverClientStates.WaitingPuzzle; }
public async Task <Transaction> ReceiveAsync(ScriptCoin escrowedCoin, TransactionSignature clientSignature, Key escrowKey, FeeRate feeRate) { _ReceiveBatch.FeeRate = feeRate; return(await _ReceiveBatch.WaitTransactionAsync(new ClientEscapeData() { ClientSignature = clientSignature, EscrowedCoin = escrowedCoin, EscrowKey = escrowKey }).ConfigureAwait(false)); }
private void Code16() { BitcoinAddress address = new BitcoinPubKeyAddress("1KF8kUVHK42XzgcmJF4Lxz4wcL5WDL97PB"); var birth = Encoding.UTF8.GetBytes("18/07/1988"); var birthHash = Hashes.Hash256(birth); Script redeemScript = new Script( "OP_IF " + "OP_HASH256 " + Op.GetPushOp(birthHash.ToBytes()) + " OP_EQUAL " + "OP_ELSE " + address.ScriptPubKey + " " + "OP_ENDIF"); var tx = new Transaction(); tx.Outputs.Add(new TxOut(Money.Parse("0.0001"), redeemScript.Hash)); ScriptCoin scriptCoin = tx.Outputs.AsCoins().First().ToScriptCoin(redeemScript); //Create spending transaction Transaction spending = new Transaction(); spending.AddInput(new TxIn(new OutPoint(tx, 0))); ////Option 1 : Spender knows my birthdate ScriptEvaluationContext eval = new ScriptEvaluationContext(); Op pushBirthdate = Op.GetPushOp(birth); Op selectIf = OpcodeType.OP_1; //go to if Op redeemBytes = Op.GetPushOp(redeemScript.ToBytes()); Script scriptSig = new Script(pushBirthdate, selectIf, redeemBytes); spending.Inputs[0].ScriptSig = scriptSig; //Verify the script pass var result = eval.VerifyScript(scriptSig, tx.Outputs[0].ScriptPubKey, spending, 0, null); Console.WriteLine(result); /////////// ////Option 2 : Spender knows my private key eval = new ScriptEvaluationContext(); BitcoinSecret secret = new BitcoinSecret("..."); var sig = spending.SignInput(secret.PrivateKey, scriptCoin); var p2pkhProof = PayToPubkeyHashTemplate .Instance .GenerateScriptSig(sig, secret.PrivateKey.PubKey); selectIf = OpcodeType.OP_0; //go to else scriptSig = p2pkhProof + selectIf + redeemBytes; spending.Inputs[0].ScriptSig = scriptSig; //Verify the script pass result = eval.VerifyScript(scriptSig, tx.Outputs[0].ScriptPubKey, spending, 0, null); Console.WriteLine(result); /////////// }
/// <summary> /// NEW!! So far alright? /// </summary> public void SegwitTestNet() { //The faucet: https://testnet.manu.backend.hamburg/faucet //https://bitcoin.stackexchange.com/questions/59231/how-to-sign-a-segwit-transaction-via-nbitcoin var net = NBitcoin.Network.TestNet; BlockExplorer explorer = new BlockExplorer("https://testnet.blockexplorer.com/"); HDWallet wallet = new HDWallet("seed12345678ryan12345678", net); uint path = 0; var extkey = wallet.GetPrivateKey(path); Key k = extkey.PrivateKey; //This gives you a Bech32 address (currently not really interoperable in wallets, so you need to convert it into P2SH) var address = k.PubKey.WitHash.GetAddress(net); var p2sh = address.GetScriptAddress(); //p2sh is now an interoperable P2SH segwit address //SENT TO: 2NGSoYM3yLi9SXvZc5yYcSwHWyCWzgBrP9X //TXID 1397a4cc480879eae604ce871c47a4d690c6ea6a6dcfd7e38d95f31b81593556 var response = explorer.GetUnspent(p2sh.ToString()); List <ExplorerUnspent> unspent = response.Convert <List <ExplorerUnspent> >(); List <Transaction> transactions = new List <Transaction>(); foreach (var item in unspent) { string txcontent = "{\"txid\":\"6636b3fedb57be81232f92f80fa8d3df9a0f07305af2c7f705a7f353e516b1d7\",\"version\":1,\"locktime\":0,\"vin\":[{\"txid\":\"50b7d9a5fa1281e7020fa8a152835756e0d57d6b4d634b4251ab500e8630bc3e\",\"vout\":1,\"scriptSig\":{\"asm\":\"0014a16f4ba22e84c364ec4f8fe19d8a48762156b41e\",\"hex\":\"160014a16f4ba22e84c364ec4f8fe19d8a48762156b41e\"},\"sequence\":4294967295,\"n\":0,\"addr\":\"2N9viNVJ5MsAM8MXdUuATDwKeMjMLQkaXyR\",\"valueSat\":193987962850,\"value\":1939.8796285,\"doubleSpentTxID\":null}],\"vout\":[{\"value\":\"2.00000000\",\"n\":0,\"scriptPubKey\":{\"hex\":\"a9142f672b3ea4af55d9da43e507e3c060d2e34521ba87\",\"asm\":\"OP_HASH160 2f672b3ea4af55d9da43e507e3c060d2e34521ba OP_EQUAL\",\"addresses\":[\"2MwZsLbuB328gxHRfr1VDrfDrK6aWicQcAW\"],\"type\":\"scripthash\"},\"spentTxId\":null,\"spentIndex\":null,\"spentHeight\":null},{\"value\":\"1937.87862850\",\"n\":1,\"scriptPubKey\":{\"hex\":\"a914859695c2cb37ee30bf6a18943c8c27a3ac0e6faa87\",\"asm\":\"OP_HASH160 859695c2cb37ee30bf6a18943c8c27a3ac0e6faa OP_EQUAL\",\"addresses\":[\"2N5RaFdK3rgsNayXnkTQaSLKVBB3brW7G4m\"],\"type\":\"scripthash\"},\"spentTxId\":\"892e6facc4fc596852d41af367dd41f7a7ec1b11a319a202bc0d4c5b8f792f2f\",\"spentIndex\":0,\"spentHeight\":1255964}],\"blockhash\":\"000000000007dbc3ffd03559f2192b299bc0c20aa0aea8e1939f2731e01103e8\",\"blockheight\":1255946,\"confirmations\":123,\"time\":1514228293,\"blocktime\":1514228293,\"valueOut\":1939.8786285,\"size\":138,\"valueIn\":1939.8796285,\"fees\":0.001}"; ExplorerResponse txResponse = explorer.GetTransaction(item.txid); RawFormat format = RawFormat.Satoshi; var tx = Transaction.Parse(txResponse.data, format, net); transactions.Add(tx); } //For spending, it works the same as a a normal P2SH //You need to get the ScriptCoin, the RedeemScript of you script coin should be k.PubKey.WitHash.ScriptPubKey. var redeemScript = k.PubKey.WitHash.ScriptPubKey; var redeemScript = k.PubKey.WitHash.ScriptPubKey; Transaction received = transactions[0]; ScriptCoin coin = received.Outputs.AsCoins().First().ToScriptCoin(redeemScript); //1397a4cc480879eae604ce871c47a4d690c6ea6a6dcfd7e38d95f31b81593556 BitcoinAddress destination = BitcoinAddress.Create("2N8hwP1WmJrFF5QWABn38y63uYLhnJYJYTF"); //the faucet return address TransactionBuilder builder = new TransactionBuilder(); builder.AddCoins(coin); builder.AddKeys(k); builder.Send(destination, Money.Coins(1.99m)); builder.SendFees(Money.Coins(0.001m)); builder.SetChange(p2sh); var signedTx = builder.BuildTransaction(true); Console.WriteLine(signedTx.ToHex()); string x = ";;"; //Assert.True(builder.Verify(signedTx)); }
public virtual void ConfigureEscrowedCoin(ScriptCoin escrowedCoin, Key escrowKey) { if (escrowedCoin == null) { throw new ArgumentNullException(nameof(escrowedCoin)); } if (escrowKey == null) { throw new ArgumentNullException(nameof(escrowKey)); } InternalState.EscrowKey = escrowKey; InternalState.EscrowedCoin = escrowedCoin; }
static internal ICoin[] DummyFundsToCoins(IEnumerable <Transaction> txs, Script redeem, Key key) { var barecoins = txs.SelectMany(tx => tx.Outputs.AsCoins()).ToArray(); var coins = new ICoin[barecoins.Length]; coins[0] = barecoins[0]; coins[1] = barecoins[1]; coins[2] = new ScriptCoin(barecoins[2], redeem); // p2sh coins[3] = new ScriptCoin(barecoins[3], redeem); // p2wsh coins[4] = new ScriptCoin(barecoins[4], key.PubKey.WitHash.ScriptPubKey); // p2sh-p2wpkh coins[5] = new ScriptCoin(barecoins[5], redeem); // p2sh-p2wsh return(coins); }
public async Task <Transaction> ReceiveAsync(ScriptCoin escrowedCoin, TransactionSignature clientSignature, Key escrowKey, FeeRate feeRate) { _ReceiveBatch.FeeRate = feeRate; var task = _ReceiveBatch.WaitTransactionAsync(new ClientEscapeData() { ClientSignature = clientSignature, EscrowedCoin = escrowedCoin, EscrowKey = escrowKey }).ConfigureAwait(false); Logs.Tumbler.LogDebug($"ClientEscape batch count {_ReceiveBatch.BatchCount}"); return(await task); }
internal void AddRedeemInfo() { foreach (var match in MatchedRules) { var scriptRule = match.Rule as ScriptRule; if (scriptRule != null && scriptRule.RedeemScript != null) { CoinCollection collection = match.MatchType == MatchLocation.Input ? SpentCoins : ReceivedCoins; if (collection != null) { var outpoint = new OutPoint(TransactionId, match.Index); var coin = collection[outpoint]; collection[outpoint] = new ScriptCoin(coin.Outpoint, coin.TxOut, scriptRule.RedeemScript); } } } }
public uint256 CreateRealHash(Transaction tx, ScriptCoin _Escrow, Money feeVariation) { /* * Not sure if this is best way to do this, but had to add this for step 7 * when verifying valid Hashes, the server will have to make real hashes, but * it doesn't have access to RealHash class. So I created this function that * takes care of that */ var escrow = EscrowScriptPubKeyParameters.GetFromCoin(_Escrow); var coin = _Escrow.Clone(); coin.OverrideScriptCode(escrow.GetInitiatorScriptCode()); var Transaction = tx.Clone(); Transaction.Outputs[0].Value -= feeVariation; return(Transaction.GetSignatureHash(coin, SigHash.All)); }
public void ReadWrite(BitcoinStream stream) { if (stream.Serializing) { stream.ReadWrite(ScriptCoin.Redeem); stream.ReadWrite(ScriptCoin.Outpoint); stream.ReadWrite(ScriptCoin.TxOut); } else { Script redeem = null; OutPoint outpoint = null; TxOut txout = null; stream.ReadWrite(ref redeem); stream.ReadWrite(ref outpoint); stream.ReadWrite(ref txout); ScriptCoin = new ScriptCoin(outpoint, txout, redeem); } }
public Transaction CreatePayment(Money paid, ScriptCoin fundingCoin) { if(paid > GetFundAmount()) throw new MicroPaymentException("Payment reached the maximum"); var builder = new TransactionBuilder(); if(fundingCoin.Redeem != Redeem || fundingCoin.Amount != Amount + Fees) throw new MicroPaymentException("Invalid funding coin"); var fees = Money.Min(paid, Fees); var toPayer = GetFundAmount() - paid; var toPayee = paid - fees; return builder .AddCoins(fundingCoin) .Send(Payee.ScriptPubkey, toPayee) .Send(Payer.ScriptPubkey, toPayer) .SendFees(fees) .Shuffle() .BuildTransaction(false); }
private List <ScriptCoin> GetRewardCoins(Transaction coinStake) { var coins = new List <ScriptCoin>(); // Identify any outputs paying the reward script a nonzero amount. TxOut[] rewardOutputs = coinStake.Outputs.Where(o => o.ScriptPubKey == StraxCoinstakeRule.CirrusRewardScript && o.Value != 0).ToArray(); // This shouldn't be the case but check anyway. if (rewardOutputs.Length != 0) { foreach (TxOut txOutput in rewardOutputs) { // The reward script is P2SH, so we need to inform the builder of the corresponding redeem script to enable it to be spent. var coin = ScriptCoin.Create(this.network, coinStake, txOutput, StraxCoinstakeRule.CirrusRewardScriptRedeem); coins.Add(coin); } } return(coins); }
public async void AcceptToMemoryPool_WithSegWitValidTxns_IsSuccessfull() { string dataDir = Path.Combine("TestData", nameof(MempoolValidatorTest), nameof(this.AcceptToMemoryPool_WithSegWitValidTxns_IsSuccessfull)); Directory.CreateDirectory(dataDir); BitcoinSecret miner = new BitcoinSecret(new Key(), Network.RegTest); ITestChainContext context = TestChainFactory.Create(Network.RegTest, miner.PubKey.ScriptPubKey.WitHash.ScriptPubKey.Hash.ScriptPubKey, dataDir); IMempoolValidator validator = context.MempoolValidator; Assert.NotNull(validator); BitcoinSecret bob = new BitcoinSecret(new Key(), Network.RegTest); // Fund Bob // 50 Coins come from first tx on chain - send bob 42 and change back to miner ScriptCoin witnessCoin = new ScriptCoin(context.SrcTxs[0].GetHash(), 0, context.SrcTxs[0].TotalOut, miner.PubKey.ScriptPubKey.WitHash.ScriptPubKey.Hash.ScriptPubKey, miner.PubKey.ScriptPubKey); TransactionBuilder txBuilder = new TransactionBuilder(); Transaction p2shOverp2wpkh = txBuilder .AddCoins(witnessCoin) .AddKeys(miner) .Send(bob, "42.00") .SendFees("0.001") .SetChange(miner) .BuildTransaction(true); Assert.True(txBuilder.Verify(p2shOverp2wpkh)); //check fully signed // remove witness data from tx Transaction noWitTx = p2shOverp2wpkh.WithOptions(TransactionOptions.None); Assert.Equal(p2shOverp2wpkh.GetHash(), noWitTx.GetHash()); Assert.True(noWitTx.GetSerializedSize() < p2shOverp2wpkh.GetSerializedSize()); Assert.True(txBuilder.Verify(p2shOverp2wpkh)); //check fully signed MempoolValidationState state = new MempoolValidationState(false); Assert.True(await validator.AcceptToMemoryPool(state, p2shOverp2wpkh), $"Transaction: {nameof(p2shOverp2wpkh)} failed mempool validation."); Directory.Delete(dataDir, true); }
public PromiseClientSession ReceiveTumblerEscrowedCoin(ScriptCoin escrowedCoin) { AssertState(TumblerClientSessionStates.WaitingTumblerEscrow); var escrow = EscrowScriptBuilder.ExtractEscrowScriptPubKeyParameters(escrowedCoin.Redeem); if (escrow == null || !escrow.EscrowKeys.Contains(InternalState.TumblerEscrowKey.PubKey)) { throw new PuzzleException("invalid-escrow"); } if (escrowedCoin.Amount != Parameters.Denomination) { throw new PuzzleException("invalid-amount"); } InternalState.Status = TumblerClientSessionStates.PromisePhase; var session = new PromiseClientSession(Parameters.CreatePromiseParamaters()); session.ConfigureEscrowedCoin(escrowedCoin, InternalState.TumblerEscrowKey); InternalState.TumblerEscrowKey = null; return(session); }
public virtual void ConfigureEscrowedCoin(ScriptCoin escrowedCoin, Key escrowKey, Key redeemKey) { if (escrowedCoin == null) { throw new ArgumentNullException(nameof(escrowedCoin)); } if (escrowKey == null) { throw new ArgumentNullException(nameof(escrowKey)); } if (redeemKey == null) { throw new ArgumentNullException(nameof(redeemKey)); } var escrow = EscrowScriptBuilder.ExtractEscrowScriptPubKeyParameters(escrowedCoin.Redeem); if (escrow == null || !escrow.EscrowKeys.Any(e => e == escrowKey.PubKey)) { throw new PuzzleException("Invalid escrow"); } InternalState.EscrowedCoin = escrowedCoin; InternalState.EscrowKey = escrowKey; InternalState.RedeemKey = redeemKey; }
public override void ConfigureEscrowedCoin(ScriptCoin escrowedCoin, Key escrowKey, Key redeemKey) { AssertState(SolverClientStates.WaitingEscrow); base.ConfigureEscrowedCoin(escrowedCoin, escrowKey, redeemKey); InternalState.Status = SolverClientStates.WaitingPuzzle; }
private uint GetCorrelation(ScriptCoin escrowCoin) { return(new uint160(escrowCoin.Redeem.Hash.ToString()).GetLow32()); }
public static EscrowScriptPubKeyParameters GetFromCoin(ScriptCoin coin) { return(GetFromScript(coin.Redeem)); }
public static Transaction SignPayment(Transaction payment, Key key, ScriptCoin fundingCoin) { return new TransactionBuilder() .AddKeys(key) .AddCoins(fundingCoin) .SignTransaction(payment); }
public void Assert(Transaction payment, bool isRefund, Money paid, ScriptCoin fundCoin = null) { if(payment.Inputs.Count != 1) throw new MicroPaymentException("The payment should have one input"); var actualFees = GetFundAmount() - payment.TotalOut; var expectedFees = Money.Min(paid, Fees); if(actualFees < Money.Zero || !actualFees.Almost(expectedFees,0.1m)) throw new MicroPaymentException("Unexpected fees in the payment"); if(!GetPaid(payment).Almost(paid)) throw new MicroPaymentException("Unexpected amount in the payment"); if(paid > GetFundAmount()) throw new MicroPaymentException("Payment reached the maximum"); if(fundCoin != null) { if(payment.Inputs[0].PrevOut != fundCoin.Outpoint) throw new MicroPaymentException("The input reference is incorrect"); if(fundCoin.Amount != Fees + Amount) throw new MicroPaymentException("The fund coin is incorrect"); } if(isRefund) { if(payment.Outputs.Count != 1) throw new MicroPaymentException("The refund should have one output"); if(payment.Outputs[0].Value != Amount) throw new MicroPaymentException("Unexpected amount in the output of the refund transaction"); if(payment.Outputs[0].ScriptPubKey != Payer.ScriptPubkey) throw new MicroPaymentException("The refund address in the refund transaction is not equal to the expected one"); if(payment.LockTime != Expiration) throw new MicroPaymentException("The refund transaction has invalid locktime"); } else { if(payment.LockTime != default(LockTime)) throw new MicroPaymentException("The payment transaction has invalid locktime"); if(payment.Outputs.Count != 1 && payment.Outputs.Count != 2) throw new MicroPaymentException("The payment should have one or two outputs"); } }
static void Main() { //=========================================================================================== //Chapter8. Using the TransactionBuilder //You have seen how the TransactionBuilder works when you have signed your first P2SH and multi-sig transaction. //We will see how you can harness its full power, for signing more complicated transactions. //With the TransactionBuilder you can: //1.Spend any //P2PK, P2PKH, //multi-sig, //P2WPK, P2WSH. //2.Spend any P2SH on the previous redeem script. //3.Spend Stealth Coin(DarkWallet). //4.Issue and transfer Colored Coins(open asset, following chapter). //5.Combine partially signed transactions. //6.Estimate the final size of an unsigned transaction and its fees. //7.Verify if a transaction is fully signed. //The goal of the TransactionBuilder is to take Coins and Keys as input, and return back a signed or partially signed transaction. //Picture depiction: //Coins -> Signed transaction(or it could be a signed transactions) <-Keys. //The TransactionBuilder will figure out what Coin to use and what to sign by itself. //Examine TransactionBuilder class containing lots of properties and methods. //The usage of the builder is done in four steps: //1.You gather the Coins that spent, //2.You gather the Keys that you own, //3.You enumerate how much Money you want to send to where scriptPubKey indicates, //4.You build and sign the transaction, //5.Optional: you give the transaction to somebody else, then he will sign or continue to build it. //Now let’s gather some Coins. //For that, let us create a fake transaction with some funds on it. //Let’s say that the transaction has a P2PKH, P2PK, and multi-sig coin of Bob and Alice. RandomUtils.Random = new UnsecureRandom(); //Private key generator. Key privateKeyGenerator = new Key(); BitcoinSecret bitcoinSecretFromPrivateKeyGenerator = privateKeyGenerator.GetBitcoinSecret(Network.Main); Key privateKeyFromBitcoinSecret = bitcoinSecretFromPrivateKeyGenerator.PrivateKey; Console.WriteLine($"privateKeyFromBitcoinSecret.ToString(Network.Main): {privateKeyFromBitcoinSecret.ToString(Network.Main)}"); //L5DZpEdbDDhhk3EqtktmGXKv3L9GxttYTecxDhM5huLd82qd9uvo is for Alice //KxMrK5EJeUZ1z3Jyo2zPkurRVtYFefab4WQitV5CyjKApHsWfWg9 is for Bob //KyStsAHgSehHvewS5YfGwhQGfEWYd8qY2XZg6q2M6TqaM8Q8rayg is for Satoshi //L2f9Ntm8UUeTLZFv25oZ8WoRW8kAofUjdUdtCq9axCp1hZrsLZja is for Nico BitcoinSecret bitcoinSecretForAlice = new BitcoinSecret("L5DZpEdbDDhhk3EqtktmGXKv3L9GxttYTecxDhM5huLd82qd9uvo", Network.Main); BitcoinSecret bitcoinSecretForBob = new BitcoinSecret("KxMrK5EJeUZ1z3Jyo2zPkurRVtYFefab4WQitV5CyjKApHsWfWg9", Network.Main); BitcoinSecret bitcoinSecretForSatoshi = new BitcoinSecret("KyStsAHgSehHvewS5YfGwhQGfEWYd8qY2XZg6q2M6TqaM8Q8rayg", Network.Main); BitcoinSecret bitcoinSecretForScanKey = new BitcoinSecret("L2f9Ntm8UUeTLZFv25oZ8WoRW8kAofUjdUdtCq9axCp1hZrsLZja", Network.Main); Key bobPrivateKey = bitcoinSecretForAlice.PrivateKey; Key alicePrivateKey = bitcoinSecretForBob.PrivateKey; Key satoshiPrivateKey = bitcoinSecretForSatoshi.PrivateKey; Key privateKeyForScanKey = bitcoinSecretForScanKey.PrivateKey; Script scriptPubKeyOfBobAlice = PayToMultiSigTemplate.Instance.GenerateScriptPubKey(2, bobPrivateKey.PubKey, alicePrivateKey.PubKey); //This transaction will send money to Bob and Alice. //The thing you should notice is that this transaction is added by various types of scriptPubKey, such as P2PK(bobPrivateKey.PubKey), P2PKH(alicePrivateKey.PubKey.Hash), and multi-sig ScriptPubKey(scriptPubKeyOfBobAlice). var txGettingCoinForBobAlice = new Transaction(); txGettingCoinForBobAlice.Outputs.Add(new TxOut(Money.Coins(1m), bobPrivateKey.PubKey)); // P2PK txGettingCoinForBobAlice.Outputs.Add(new TxOut(Money.Coins(1m), alicePrivateKey.PubKey.Hash)); // P2PKH txGettingCoinForBobAlice.Outputs.Add(new TxOut(Money.Coins(1m), scriptPubKeyOfBobAlice)); //Now let’s say they want to use the coins of this transaction to pay Satoshi. //First they have to get their coins. Coin[] coins = txGettingCoinForBobAlice.Outputs.AsCoins().ToArray(); Coin bobCoin = coins[0]; Coin aliceCoin = coins[1]; Coin bobAliceCoin = coins[2]; //Now let’s say Bob wants to send 0.2 BTC, Alice 0.3 BTC, and they agree to use bobAlice to send 0.5 BTC. //Build the transaction by using the features of the TransactionBuilder class. var builderForSendingCoinToSatoshi = new TransactionBuilder(); Transaction txForSpendingCoinToSatoshi = builderForSendingCoinToSatoshi .AddCoins(bobCoin) //PriveteKey of Bob to be used for signing. .AddKeys(bobPrivateKey) .Send(satoshiPrivateKey, Money.Coins(0.2m)) .SetChange(bobPrivateKey) .Then() .AddCoins(aliceCoin) .AddKeys(alicePrivateKey) .Send(satoshiPrivateKey, Money.Coins(0.3m)) .SetChange(alicePrivateKey) .Then() .AddCoins(bobAliceCoin) .AddKeys(bobPrivateKey, alicePrivateKey) .Send(satoshiPrivateKey, Money.Coins(0.5m)) .SetChange(scriptPubKeyOfBobAlice) .SendFees(Money.Coins(0.0001m)) .BuildTransaction(sign: true); Console.WriteLine(txForSpendingCoinToSatoshi); //Then you can verify it is fully signed and ready to send to the network. //Verify you did not screw up. Console.WriteLine(builderForSendingCoinToSatoshi.Verify(txForSpendingCoinToSatoshi)); // True //============================================================================================ //Do with a ScriptCoin. //The nice thing about this model is that it works the same way for P2SH, P2WSH, P2SH(P2WSH), and P2SH(P2PKH) except you need to create ScriptCoin. //Illustration: //Coin-> ScriptCoin <-RedeemScript. var txGettingScriptCoinForBobAlice = new Transaction(); txGettingScriptCoinForBobAlice.Outputs.Add(new TxOut(Money.Coins(1.0m), scriptPubKeyOfBobAlice.Hash)); coins = txGettingScriptCoinForBobAlice.Outputs.AsCoins().ToArray(); ScriptCoin bobAliceScriptCoin = coins[0].ToScriptCoin(scriptPubKeyOfBobAlice); //Then the signature: var builderForSendingScriptCoinToSatoshi = new TransactionBuilder(); var txForSendingScriptCoinToSatoshi = builderForSendingScriptCoinToSatoshi .AddCoins(bobAliceScriptCoin) .AddKeys(bobPrivateKey, alicePrivateKey) .Send(satoshiPrivateKey, Money.Coins(0.9m)) .SetChange(scriptPubKeyOfBobAlice.Hash) .SendFees(Money.Coins(0.0001m)) .BuildTransaction(true); Console.WriteLine(builderForSendingScriptCoinToSatoshi.Verify(txForSendingScriptCoinToSatoshi)); //Output: //True //============================================================================================ //Do with a StealthCoin. //For Stealth Coin, this is basically the same thing except that, if you remember our introduction on Dark Wallet, you need a ScanKey to see the StealthCoin. //Illustration: //ScanKey + Transaction + StealthAddress => StealthCoin. //Let’s create darkAliceBob stealth address as in previous chapter: BitcoinStealthAddress bitcoinStealthAddressForBobAlice = new BitcoinStealthAddress ( scanKey: privateKeyForScanKey.PubKey, pubKeys: new[] { alicePrivateKey.PubKey, bobPrivateKey.PubKey }, signatureCount: 2, bitfield: null, network: Network.Main ); //Let’s say someone sent the coin to this transaction via the darkAliceBob which is a BitcoinStealthAddress: var txGettingCoinForBobAliceToBitcoinStealthAddress = new Transaction(); bitcoinStealthAddressForBobAlice .SendTo(txGettingCoinForBobAliceToBitcoinStealthAddress, Money.Coins(1.0m)); //The scanner will detect the StealthCoin: //Get the stealth coin with the scanKey. StealthCoin stealthCoin = StealthCoin.Find(txGettingCoinForBobAliceToBitcoinStealthAddress, bitcoinStealthAddressForBobAlice, privateKeyForScanKey); //And forward it to Bob and Alice, who will sign: //Let Bob and Alice sign and spend the coin. TransactionBuilder builderForBobAliceToBitcoinStealthAddress = new TransactionBuilder(); txGettingCoinForBobAliceToBitcoinStealthAddress = builderForBobAliceToBitcoinStealthAddress .AddCoins(stealthCoin) .AddKeys(bobPrivateKey, alicePrivateKey, privateKeyForScanKey) .Send(satoshiPrivateKey, Money.Coins(0.9m)) .SetChange(scriptPubKeyOfBobAlice.Hash) .SendFees(Money.Coins(0.0001m)) .BuildTransaction(true); Console.WriteLine(builderForBobAliceToBitcoinStealthAddress.Verify(txGettingCoinForBobAliceToBitcoinStealthAddress)); //Output: //True //Note: You need the scanKey for spending a StealthCoin }
public void CanBuildWitTransaction() { Key alice = new Key(); Key bob = new Key(); Transaction previousTx = null; Coin previousCoin = null; WitScriptCoin witnessCoin = null; TransactionBuilder builder = null; Transaction signedTx = null; ScriptCoin scriptCoin = null; //P2WPKH previousTx = new Transaction(); previousTx.Outputs.Add(new TxOut(Money.Coins(1.0m), alice.PubKey.WitHash)); previousCoin = previousTx.Outputs.AsCoins().First(); builder = new TransactionBuilder(); builder.AddKeys(alice); builder.AddCoins(previousCoin); builder.Send(bob, Money.Coins(0.4m)); builder.SendFees(Money.Satoshis(30000)); builder.SetChange(alice); signedTx = builder.BuildTransaction(true); Assert.True(builder.Verify(signedTx)); //P2WSH previousTx = new Transaction(); previousTx.Outputs.Add(new TxOut(Money.Coins(1.0m), alice.PubKey.ScriptPubKey.WitHash)); previousCoin = previousTx.Outputs.AsCoins().First(); witnessCoin = new WitScriptCoin(previousCoin, alice.PubKey.ScriptPubKey); builder = new TransactionBuilder(); builder.AddKeys(alice); builder.AddCoins(witnessCoin); builder.Send(bob, Money.Coins(0.4m)); builder.SendFees(Money.Satoshis(30000)); builder.SetChange(alice); signedTx = builder.BuildTransaction(true); Assert.True(builder.Verify(signedTx)); //P2SH(P2WPKH) previousTx = new Transaction(); previousTx.Outputs.Add(new TxOut(Money.Coins(1.0m), alice.PubKey.WitHash.ScriptPubKey.Hash)); previousCoin = previousTx.Outputs.AsCoins().First(); scriptCoin = new ScriptCoin(previousCoin, alice.PubKey.WitHash.ScriptPubKey); builder = new TransactionBuilder(); builder.AddKeys(alice); builder.AddCoins(scriptCoin); builder.Send(bob, Money.Coins(0.4m)); builder.SendFees(Money.Satoshis(30000)); builder.SetChange(alice); signedTx = builder.BuildTransaction(true); Assert.True(builder.Verify(signedTx)); //P2SH(P2WSH) previousTx = new Transaction(); previousTx.Outputs.Add(new TxOut(Money.Coins(1.0m), alice.PubKey.ScriptPubKey.WitHash.ScriptPubKey.Hash)); previousCoin = previousTx.Outputs.AsCoins().First(); witnessCoin = new WitScriptCoin(previousCoin, alice.PubKey.ScriptPubKey); builder = new TransactionBuilder(); builder.AddKeys(alice); builder.AddCoins(witnessCoin); builder.Send(bob, Money.Coins(0.4m)); builder.SendFees(Money.Satoshis(30000)); builder.SetChange(alice); signedTx = builder.BuildTransaction(true); Assert.True(builder.Verify(signedTx)); //Can remove witness data from tx var signedTx2 = signedTx.WithOptions(TransactionOptions.None); Assert.Equal(signedTx.GetHash(), signedTx2.GetHash()); Assert.True(signedTx2.GetSerializedSize() < signedTx.GetSerializedSize()); }