Post
Share your knowledge.
How do I send Bitcoin from 1 Taproot Address to Another?
I made a taproot bitcoin address (bech32m) and sent 1 UTXO from a segwit non-taproot address (bech32) to this taproot address. How would I write a c# script to send this btc to another taproot address? I don't need anything too complicated, just a simple script to send this btc from one taproot address to another. I think NBitcoin would be the most sensible package to use.
I found this in one of the NBitcoin tests:
public async Task CanBuildTaprootSingleSigTransactions()
{
using (var nodeBuilder = NodeBuilderEx.Create())
{
var rpc = nodeBuilder.CreateNode().CreateRPCClient();
nodeBuilder.StartAll();
rpc.Generate(102);
var change = new Key();
var rootKey = new ExtKey();
var accountKeyPath = new KeyPath("86'/0'/0'");
var accountRootKeyPath = new RootedKeyPath(rootKey.GetPublicKey().GetHDFingerPrint(), accountKeyPath);
var accountKey = rootKey.Derive(accountKeyPath);
var key = accountKey.Derive(new KeyPath("0/0")).PrivateKey;
var address = key.PubKey.GetAddress(ScriptPubKeyType.TaprootBIP86, nodeBuilder.Network);
var destination = new Key();
var amount = new Money(1, MoneyUnit.BTC);
uint256 id = null;
Transaction tx = null;
ICoin coin = null;
TransactionBuilder builder = null;
var rate = new FeeRate(Money.Satoshis(1), 1);
async Task RefreshCoin()
{
id = await rpc.SendToAddressAsync(address, Money.Coins(1));
tx = await rpc.GetRawTransactionAsync(id);
coin = tx.Outputs.AsCoins().Where(o => o.ScriptPubKey == address.ScriptPubKey).Single();
builder = Network.Main.CreateTransactionBuilder(0);
}
await RefreshCoin();
var signedTx = builder
.AddCoins(coin)
.AddKeys(key)
.Send(destination, amount)
.SubtractFees()
.SetChange(change)
.SendEstimatedFees(rate)
.BuildTransaction(true);
rpc.SendRawTransaction(signedTx);
await RefreshCoin();
// Let's try again, but this time with PSBT
var psbt = builder
.AddCoins(coin)
.Send(destination, amount)
.SubtractFees()
.SetChange(change)
.SendEstimatedFees(rate)
.BuildPSBT(false);
var tk = key.PubKey.GetTaprootFullPubKey();
psbt.Inputs[0].HDTaprootKeyPaths.Add(tk.OutputKey, new TaprootKeyPath(accountRootKeyPath.Derive(KeyPath.Parse("0/0"))));
psbt.SignAll(ScriptPubKeyType.TaprootBIP86, accountKey, accountRootKeyPath);
// Check if we can roundtrip
psbt = CanRoundtripPSBT(psbt);
psbt.Finalize();
rpc.SendRawTransaction(psbt.ExtractTransaction());
// Let's try again, but this time with BuildPSBT(true)
await RefreshCoin();
psbt = builder
.AddCoins(coin)
.AddKeys(key)
.Send(destination, amount)
.SubtractFees()
.SetChange(change)
.SendEstimatedFees(rate)
.BuildPSBT(true);
psbt.Finalize();
rpc.SendRawTransaction(psbt.ExtractTransaction());
// Let's try again, this time with a merkle root
var merkleRoot = RandomUtils.GetUInt256();
address = key.PubKey.GetTaprootFullPubKey(merkleRoot).GetAddress(nodeBuilder.Network);
await RefreshCoin();
psbt = builder
.AddCoins(coin)
.AddKeys(key.CreateTaprootKeyPair(merkleRoot))
.Send(destination, amount)
.SubtractFees()
.SetChange(change)
.SendEstimatedFees(rate)
.BuildPSBT(true);
Assert.NotNull(psbt.Inputs[0].TaprootMerkleRoot);
Assert.NotNull(psbt.Inputs[0].TaprootInternalKey);
Assert.NotNull(psbt.Inputs[0].TaprootKeySignature);
psbt = CanRoundtripPSBT(psbt);
psbt.Finalize();
rpc.SendRawTransaction(psbt.ExtractTransaction());
// Can we sign the PSBT separately?
await RefreshCoin();
psbt = builder
.AddCoins(coin)
.Send(destination, amount)
.SubtractFees()
.SetChange(change)
.SendEstimatedFees(rate)
.BuildPSBT(false);
var taprootKeyPair = key.CreateTaprootKeyPair(merkleRoot);
psbt.Inputs[0].Sign(taprootKeyPair);
Assert.NotNull(psbt.Inputs[0].TaprootMerkleRoot);
Assert.NotNull(psbt.Inputs[0].TaprootInternalKey);
Assert.NotNull(psbt.Inputs[0].TaprootKeySignature);
// This line is useless, we just use it to test the PSBT roundtrip
psbt.Inputs[0].HDTaprootKeyPaths.Add(taprootKeyPair.PubKey,
new TaprootKeyPath(RootedKeyPath.Parse("12345678/86'/0'/0'/0/0"),
new uint256[] { RandomUtils.GetUInt256() }));
psbt = CanRoundtripPSBT(psbt);
psbt.Finalize();
rpc.SendRawTransaction(psbt.ExtractTransaction());
// Can we sign the transaction separately?
await RefreshCoin();
var coin1 = coin;
await RefreshCoin();
var coin2 = coin;
builder = Network.Main.CreateTransactionBuilder(0);
signedTx = builder
.AddCoins(coin1, coin2)
.Send(destination, amount)
.SubtractFees()
.SetChange(change)
.SendEstimatedFees(rate)
.BuildTransaction(false);
var unsignedTx = signedTx.Clone();
builder = Network.Main.CreateTransactionBuilder(0);
builder.AddKeys(key.CreateTaprootKeyPair(merkleRoot));
builder.AddCoins(coin1);
var ex = Assert.Throws<InvalidOperationException>(() => builder.SignTransactionInPlace(signedTx));
Assert.Contains("taproot", ex.Message);
builder.AddCoin(coin2);
builder.SignTransactionInPlace(signedTx);
Assert.True(!WitScript.IsNullOrEmpty(signedTx.Inputs.FindIndexedInput(coin2.Outpoint).WitScript));
// Another solution is to set the precomputed transaction data.
signedTx = unsignedTx;
builder = Network.Main.CreateTransactionBuilder(0);
builder.AddKeys(key.CreateTaprootKeyPair(merkleRoot));
builder.AddCoins(coin2);
builder.SetSigningOptions(new SigningOptions() { PrecomputedTransactionData = signedTx.PrecomputeTransactionData(new ICoin[] { coin1, coin2 }) });
builder.SignTransactionInPlace(signedTx);
Assert.True(!WitScript.IsNullOrEmpty(signedTx.Inputs.FindIndexedInput(coin2.Outpoint).WitScript));
// Let's check if we estimate precisely the size of a taproot transaction.
await RefreshCoin();
signedTx = builder
.AddCoins(coin)
.AddKeys(key.CreateTaprootKeyPair(merkleRoot))
.Send(destination, amount)
.SubtractFees()
.SetChange(change)
.SendEstimatedFees(rate)
.BuildTransaction(false);
var actualvsize = builder.EstimateSize(signedTx, true);
builder.SignTransactionInPlace(signedTx);
var expectedvsize = signedTx.GetVirtualSize();
// The estimator can't assume the sighash to be default
// for all inputs, so we likely overestimate 1 bytes per input
Assert.Equal(expectedvsize, actualvsize - 1);
}
}
But I can't figure out how to write a simple C# console app in the format:
class Program
{
static async Task Main()
...
}
I have the private key, as well as the full mnemonic phrase for the private key, of the taproot bitcoin address (bech32m) that I funded with 1 UTXO. How would I write a simple C# console app that spends this UTXO to another taproot address?
And please MIT license your solution to me.
- blockchain
- cryptocurrency
Do you know the answer?
Please log in and share it.
Web3 (also known as Web 3.0) is an idea for a new iteration of the World Wide Web which incorporates concepts such as decentralization, blockchain technologies, and token-based economics.