/// <summary> /// Sign a specific coin with the given secret /// </summary> /// <param name="key">Private keys</param> /// <param name="coins">Coins to sign</param> public void Sign(Key[] keys, params ICoin[] coins) { TransactionBuilder builder = new TransactionBuilder(); builder.AddKeys(keys); builder.AddCoins(coins); builder.SignTransactionInPlace(this); }
/// <summary> /// Sign the transaction with a private key /// <para>ScriptSigs should be filled with either previous scriptPubKeys or redeem script (for P2SH)</para> /// <para>For more complex scenario, use TransactionBuilder</para> /// </summary> /// <param name="secret"></param> public void Sign(Key key, bool assumeP2SH) { TransactionBuilder builder = new TransactionBuilder(); builder.AddKeys(key); for (int i = 0; i < Inputs.Count; i++) { var txin = Inputs[i]; if (Script.IsNullOrEmpty(txin.ScriptSig)) { throw new InvalidOperationException("ScriptSigs should be filled with either previous scriptPubKeys or redeem script (for P2SH)"); } if (assumeP2SH) { var p2shSig = PayToScriptHashTemplate.Instance.ExtractScriptSigParameters(txin.ScriptSig); if (p2shSig == null) { builder.AddCoins(new ScriptCoin(txin.PrevOut, new TxOut() { ScriptPubKey = txin.ScriptSig.PaymentScript, }, txin.ScriptSig)); } else { builder.AddCoins(new ScriptCoin(txin.PrevOut, new TxOut() { ScriptPubKey = p2shSig.RedeemScript.PaymentScript }, p2shSig.RedeemScript)); } } else { builder.AddCoins(new Coin(txin.PrevOut, new TxOut() { ScriptPubKey = txin.ScriptSig })); } } builder.SignTransactionInPlace(this); }
static void Main(string[] args) { BitcoinSecret secret = new BitcoinSecret("cTfANynUR2MCPC8uBpfo2yUfAzX5PucDNuXaJ8jXT8CpQDgvfUNr", Network); BitcoinAddress address = new BitcoinAddress("n2upyU7axtvHHhAPTMvdaRSkP5Mjufoffx", Network); BlockrTransactionRepository blockr = new BlockrTransactionRepository(Network); List<Coin> unspentCoins = blockr.GetUnspentAsync(secret.GetAddress().ToString(), true).Result; TransactionBuilder builder = new TransactionBuilder(); builder.AddKeys(secret); builder.AddCoins(unspentCoins); builder.Send(address, Money.Coins(0.005m)); builder.SendFees(Money.Coins(0.00001m)); builder.SetChange(secret.ScriptPubKey); Transaction tx = builder.BuildTransaction(true); if (builder.Verify(tx)) SendTransaction(tx); }
public bool TryFinalizeInput(out IList <PSBTError> errors) { errors = null; if (IsFinalized()) { return(true); } var isSane = this.CheckSanity(); if (isSane.Count != 0) { errors = isSane; return(false); } if (witness_utxo == null && non_witness_utxo == null) { errors = new List <PSBTError>() { new PSBTError(Index, "Neither witness_utxo nor non_witness_output is set") }; return(false); } var coin = this.GetSignableCoin(out var getSignableCoinError) ?? this.GetCoin(); // GetCoin can't be null at this stage. TransactionBuilder transactionBuilder = Parent.CreateTransactionBuilder(); transactionBuilder.AddCoins(coin); foreach (var sig in PartialSigs) { transactionBuilder.AddKnownSignature(sig.Key, sig.Value, coin.Outpoint); } Transaction signed = null; try { var signedTx = Parent.Settings.IsSmart ? Parent.GetOriginalTransaction() : Transaction.Clone(); signed = transactionBuilder.SignTransaction(signedTx, SigHash.All); } catch (Exception ex) { errors = new List <PSBTError>() { new PSBTError(Index, $"Error while finalizing the input \"{getSignableCoinError ?? ex.Message}\"") }; return(false); } var indexedInput = signed.Inputs.FindIndexedInput(coin.Outpoint); if (!indexedInput.VerifyScript(coin, out var error)) { errors = new List <PSBTError>() { new PSBTError(Index, $"The finalized input script does not properly validate \"{error}\"") }; return(false); } FinalScriptSig = indexedInput.ScriptSig is Script oo && oo != Script.Empty ? oo : null; FinalScriptWitness = indexedInput.WitScript is WitScript o && o != WitScript.Empty ? o : null; if (transactionBuilder.FindSignableCoin(indexedInput) is ScriptCoin scriptCoin) { if (scriptCoin.IsP2SH) { RedeemScript = scriptCoin.GetP2SHRedeem(); } if (scriptCoin.RedeemType == RedeemType.WitnessV0) { WitnessScript = scriptCoin.Redeem; } } ClearForFinalize(); errors = null; return(true); }
static void Main(string[] args) { var goldGuy = new BitcoinSecret("KyuzoVnpsqW529yzozkzP629wUDBsPmm4QEkh9iKnvw3Dy5JJiNg"); var silverGuy = new BitcoinSecret("L4KvjpqDtdGEn7Lw6HdDQjbg74MwWRrFZMQTgJozeHAKJw5rQ2Kn"); var firstPerson = new BitcoinSecret("5Jnw9Td7PaG6PWBrU7ZCfxyVXsHSsNxdZ9sg5dnZstcr12DLVbJ"); var secondPerson = new BitcoinSecret("5Jn4zJkzS2BWNu7AMRTdSJ6mS7JYfJg27oXKAichaRBbp97ZKks"); var exchangeEntity = new BitcoinSecret("5KA7FeABKmMKerWmkJzYM9FdoqScZEMVcS9u6wvT3EhgF5ZUWv5"); var bitcoinProviderEntity = new BitcoinSecret("5Jcz2A17aAt4bcQP5GEn6itt72JsLwrksNRVKqazy7n284b1bKj"); var issuanceCoinsTransaction = new Transaction() { Outputs = { new TxOut("1.0", goldGuy.PubKey), new TxOut("1.0", silverGuy.PubKey), new TxOut("1.0", firstPerson.PubKey), new TxOut("1.0", secondPerson.PubKey), } }; IssuanceCoin[] issuanceCoins = issuanceCoinsTransaction .Outputs .Take(2) .Select((o, i) => new Coin(new OutPoint(issuanceCoinsTransaction.GetHash(), i), o)) .Select(c => new IssuanceCoin(c)) .ToArray(); var goldIssuanceCoin = issuanceCoins[0]; var silverIssuanceCoin = issuanceCoins[1]; var firstPersonInitialCoin = new Coin(new OutPoint(issuanceCoinsTransaction, 2), issuanceCoinsTransaction.Outputs[2]); var secondPersonInitialCoin = new Coin(new OutPoint(issuanceCoinsTransaction, 3), issuanceCoinsTransaction.Outputs[3]); var goldId = goldIssuanceCoin.AssetId; var silverId = silverIssuanceCoin.AssetId; var txRepo = new NoSqlTransactionRepository(); txRepo.Put(issuanceCoinsTransaction.GetHash(), issuanceCoinsTransaction); var ctxRepo = new NoSqlColoredTransactionRepository(txRepo); TransactionBuilder txBuilder = new TransactionBuilder(); // Issuing gold to first person // This happens in gold issuer client Transaction tx = txBuilder .AddKeys(goldGuy.PrivateKey) .AddCoins(goldIssuanceCoin) .IssueAsset(firstPerson.GetAddress(), new AssetMoney(goldId, 20)) .SetChange(goldGuy.GetAddress()) .BuildTransaction(true); txRepo.Put(tx.GetHash(), tx); var ctx = tx.GetColoredTransaction(ctxRepo); var coloredCoins = ColoredCoin.Find(tx, ctx).ToArray(); ColoredCoin firstPersonGoldCoin = coloredCoins[0]; // Issuing silver to second person // This happens in silver issuer client txBuilder = new TransactionBuilder(); tx = txBuilder .AddKeys(silverGuy.PrivateKey) .AddCoins(silverIssuanceCoin) .IssueAsset(secondPerson.GetAddress(), new AssetMoney(silverId, 30)) .SetChange(silverGuy.GetAddress()) .BuildTransaction(true); txRepo.Put(tx.GetHash(), tx); ctx = tx.GetColoredTransaction(ctxRepo); coloredCoins = ColoredCoin.Find(tx, ctx).ToArray(); ColoredCoin secondPersonSilverCoin = coloredCoins[0]; // Sending first person gold to exchange // This happens in first user client var bitcoinProviderCoin = CreateTransactionFeeCoin(bitcoinProviderEntity.PubKey, txRepo); txBuilder = new TransactionBuilder(); tx = txBuilder .AddCoins(bitcoinProviderCoin) .AddKeys(bitcoinProviderEntity.PrivateKey) .AddCoins(firstPersonGoldCoin) .AddKeys(firstPerson.PrivateKey) .SendAssetToExchange(exchangeEntity.GetAddress(), new AssetMoney(goldId, 5)) .SetChange(firstPerson.PubKey) .BuildTransaction(true); txRepo.Put(tx.GetHash(), tx); ctx = tx.GetColoredTransaction(ctxRepo); coloredCoins = ColoredCoin.Find(tx, ctx).ToArray(); ColoredCoin firstPersonColoredCoinInExchange = coloredCoins[1]; // Creating the time-locked transaction which the first user can post to the // network to claim his/her coin from exchange (it works if the exchange does not touch the coins // This happens in exchange and the transaction is delivered to first user client bitcoinProviderCoin = CreateTransactionFeeCoin(bitcoinProviderEntity.PubKey, txRepo); txBuilder = new TransactionBuilder(); tx = txBuilder .AddCoins(bitcoinProviderCoin) .AddKeys(bitcoinProviderEntity.PrivateKey) .AddCoins(firstPersonColoredCoinInExchange) .AddKeys(firstPerson.PrivateKey) .SendAsset(firstPerson.PubKey, new AssetMoney(firstPersonColoredCoinInExchange.Amount.Id, firstPersonColoredCoinInExchange.Amount.Quantity)) .SetChange(exchangeEntity.PubKey) .SetLockTime(new LockTime(1000000)) .BuildTransaction(true); string reclaimTransactionForFirstUser = tx.ToHex(); // Create first person exchange request // This happens in first person client JObject firstPersonRequestToExchange = CreateExchangeRequest("ExactMatch", goldId.ToString(), silverId.ToString(), 5, 2); var firstRequestSignature = firstPerson.PrivateKey.SignMessage(firstPersonRequestToExchange.ToString(Formatting.None)); // Sending second person silver to exchange // This happens in second person client bitcoinProviderCoin = CreateTransactionFeeCoin(bitcoinProviderEntity.PubKey, txRepo); txBuilder = new TransactionBuilder(); tx = txBuilder .AddCoins(bitcoinProviderCoin) .AddKeys(bitcoinProviderEntity.PrivateKey) .AddCoins(secondPersonSilverCoin) .AddKeys(secondPerson.PrivateKey) .SendAssetToExchange(exchangeEntity.GetAddress(), new AssetMoney(silverId, 12)) .SetChange(secondPerson.PubKey) .BuildTransaction(true); txRepo.Put(tx.GetHash(), tx); ctx = tx.GetColoredTransaction(ctxRepo); coloredCoins = ColoredCoin.Find(tx, ctx).ToArray(); ColoredCoin secondPersonColoredCoinInExchange = coloredCoins[1]; // Create second person exchange request // This happens in second person client JObject secondPersonRequestToExchange = CreateExchangeRequest("ExactMatch", silverId.ToString(), goldId.ToString(), 30, 0.5f); var secondRequestSignature = secondPerson.PrivateKey.SignMessage(secondPersonRequestToExchange.ToString(Formatting.None)); // Creating exchange reason // This happens in exchange var exchangeReason = CreateExchangeMatch(firstPersonRequestToExchange, firstRequestSignature, secondPersonRequestToExchange, secondRequestSignature, exchangeEntity.PrivateKey); // Performing the exchange operation // This happens in exchange bitcoinProviderCoin = CreateTransactionFeeCoin(bitcoinProviderEntity.PubKey, txRepo); txBuilder = new TransactionBuilder(); tx = txBuilder .AddCoins(bitcoinProviderCoin) .AddKeys(bitcoinProviderEntity.PrivateKey) .AddCoins(firstPersonColoredCoinInExchange) .AddKeys(exchangeEntity.PrivateKey) .AddCoins(secondPersonColoredCoinInExchange) .AddKeys(exchangeEntity.PrivateKey) .PerformExchangeOperation(firstPerson.GetAddress(), new AssetMoney(silverId, 10), secondPerson.GetAddress(), new AssetMoney(goldId, 5), exchangeReason.ToString(Formatting.None)) .SetChange(exchangeEntity.GetAddress()) .BuildTransaction(true); txRepo.Put(tx.GetHash(), tx); ctx = tx.GetColoredTransaction(ctxRepo); coloredCoins = ColoredCoin.Find(tx, ctx).ToArray(); txRepo.Put(tx.GetHash(), tx); }
/// <summary> /// Records a database anchor. /// </summary> /// <param name="anchor">The anchor to be recorded.</param> /// <returns>The task object representing the asynchronous operation.</returns> public async Task RecordAnchor(LedgerAnchor anchor) { byte[] anchorPayload = anchorMarker .Concat(BitConverter.GetBytes((ulong)anchor.TransactionCount).Reverse()) .Concat(anchor.FullStoreHash.ToByteArray()) .ToArray(); using (HttpClient client = new HttpClient()) { BitcoinAddress address = this.publishingAddress.ScriptPubKey.GetDestinationAddress(this.network); HttpResponseMessage response = await client.GetAsync(new Uri(url, $"addresses/{address.ToString()}/unspents")); response.EnsureSuccessStatusCode(); string body = await response.Content.ReadAsStringAsync(); JArray outputs = JArray.Parse(body); TransactionBuilder builder = new TransactionBuilder(); builder.AddKeys(publishingAddress.GetBitcoinSecret(network)); foreach (JObject output in outputs) { string transactionHash = (string)output["transaction_hash"]; uint outputIndex = (uint)output["output_index"]; long amount = (long)output["value"]; builder.AddCoins(new Coin(uint256.Parse(transactionHash), outputIndex, new Money(amount), publishingAddress.ScriptPubKey)); } Script opReturn = new Script(OpcodeType.OP_RETURN, Op.GetPushOp(anchorPayload)); builder.Send(opReturn, 0); builder.SendFees(satoshiFees); builder.SetChange(this.publishingAddress.ScriptPubKey, ChangeType.All); ByteString seriazliedTransaction = new ByteString(builder.BuildTransaction(true).ToBytes()); await SubmitTransaction(seriazliedTransaction); } }
public async Task<ByteString> IssueWithdrawal(IList<OutboundTransaction> transactions) { HttpClient client = new HttpClient(); BitcoinAddress address = storageKey.ScriptPubKey.GetDestinationAddress(this.Network); HttpResponseMessage response = await client.GetAsync(new Uri(url, $"addresses/{address.ToString()}/unspents")); string body = await response.Content.ReadAsStringAsync(); JArray outputs = JArray.Parse(body); TransactionBuilder builder = new TransactionBuilder(); builder.AddKeys(storageKey.GetBitcoinSecret(Network)); foreach (JObject output in outputs) { string transactionHash = (string)output["transaction_hash"]; uint outputIndex = (uint)output["output_index"]; long amount = (long)output["value"]; builder.AddCoins(new Coin(uint256.Parse(transactionHash), outputIndex, new Money(amount), storageKey.ScriptPubKey)); } foreach (OutboundTransaction outboundTransaction in transactions) { builder.Send(BitcoinAddress.Create(outboundTransaction.Target, Network).ScriptPubKey, new Money(outboundTransaction.Amount)); } builder.SendFees(defaultFees); builder.SetChange(storageKey.ScriptPubKey, ChangeType.All); NBitcoin.Transaction transaction = builder.BuildTransaction(true); return new ByteString(transaction.ToBytes()); }