3

Most tutorials out there just show how to sign it in the legacy way. And I know that NBitcoin already supports SegWit, so is there a snippet out there on how to sign it this way?

2 Answers 2

4

signing with Segwit is in no way different from signing with P2SH or P2PKH.

If you want interoperability, you need to use Segwit wrapped into P2SH. Please check https://programmingblockchain.gitbooks.io/programmingblockchain/content/other_types_of_ownership/ for more info.

But in a nutshell, if you want to have a P2WPKH-PS2SH (Segwit P2PKH wrapped into P2SH for interoperability), then query the coins based on this address.

[Fact]
[Trait("UnitTest", "UnitTest")]
public void CanGuessRedeemScriptWithInputKeys()
{
    var k = new Key();

    //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(Network.Main);
    var p2sh = address.GetScriptAddress();
    //p2sh is now an interoperable P2SH segwit address

    //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 coins =
        //Get coins from any block explorer.
        GetCoins(p2sh)
        //Nobody knows your redeem script, so you add here the information
        //This line is actually optional since 4.0.0.38, as the TransactionBuilder is smart enough to figure out
        //the redeems from the keys added by AddKeys.
        //However, explicitely having the redeem will make code more easy to update to other payment like 2-2
        .Select(c => c.ToScriptCoin(k.PubKey.WitHash.ScriptPubKey))
        .ToArray();

    TransactionBuilder builder = new TransactionBuilder();
    builder.AddCoins(coins);
    builder.AddKeys(k);
    builder.Send(new Key().ScriptPubKey, Money.Coins(1));
    builder.SendFees(Money.Coins(0.001m));
    builder.SetChange(p2sh);
    var signedTx = builder.BuildTransaction(true);
    Assert.True(builder.Verify(signedTx));
}

Nicolas,

1
  • You can use Transaction.Sign(keys, coins) as well, as it depends on TransactionBuilder under the hood. Sep 10, 2017 at 6:51
1

I got stuck on this as well and combined with Nicolas answer the code samples below are what helped sort out my understanding.

I used the simplest segwit example from bip-0143 as the basis. The c# code sample below correctly spends the two inputs from the sample transaction linked above. Note that only the second input (hash ef51e...) uses a segwit scriptPubKey so theoretically only coin1 in the sample below is doing a segwit spend.

Key txInKey0 = new Key(hex.DecodeData("bbc27228ddcb9209d7fd6f36b02f7dfa6252af40bb2f1cbc7a557da8027ff866"));
Key txInKey1 = new Key(hex.DecodeData("619c335025c7f4012e556c2a58b2506e30b8511b53ade95ea316fd8c3286feb9"));
Key recipient = new Key();

var coin0 = new Coin(uint256.Parse("fff7f7881a8099afa6940d42d1e7f6362bec38171ea3edf433541db4e4ad969f"), 0, Money.FromUnit(6.25m, MoneyUnit.BTC), new Script(hex.DecodeData("2103c9f4836b9a4f77fc0d81f7bcb01b7f1b35916864b9476c241ce9fc198bd25432ac")));
var coin1 = new Coin(uint256.Parse("ef51e1b804cc89d182d279655c3aa89e815b1b309fe287d9b2b55d57b90ec68a"), 1, Money.FromUnit(6.0m, MoneyUnit.BTC), new Script(hex.DecodeData("00141d0f172a0ecb48aee1be1f2687d2963ae33f71a1")));

var builder = new TransactionBuilder();
var tx = builder
        .AddCoins(coin0, coin1)
        .AddKeys(txInKey0, txInKey1)
        .Send(recipient.ScriptPubKey, Money.Coins(12.0m))
        .SetChange(txInKey1.ScriptPubKey)
        .SendFees(Money.Coins(0.0001m))
        .BuildTransaction(true);
Console.WriteLine(builder.Verify(tx));

logger.Debug(tx.ToHex());
logger.Debug(tx.ToString(RawFormat.BlockExplorer));

To double check the signatures I used the C++ bitcoinconsensus library that gets build as part of bitcoin core.

#include <iostream>
#include "bitcoinconsensus.h"

int char2int(char input)
{
    if (input >= '0' && input <= '9')
        return input - '0';
    if (input >= 'A' && input <= 'F')
        return input - 'A' + 10;
    if (input >= 'a' && input <= 'f')
        return input - 'a' + 10;
    throw std::invalid_argument("Invalid input string");
}

// This function assumes src to be a zero terminated sanitized string with
// an even number of [0-9a-f] characters, and target to be sufficiently large
void hex2bin(const char* src, unsigned char* target)
{
    while (*src && src[1])
    {
        *(target++) = char2int(*src) * 16 + char2int(src[1]);
        src += 2;
    }
}

int main()
{
    char script0PubKeyHex[] = "00141d0f172a0ecb48aee1be1f2687d2963ae33f71a1";
    int script0PubKeyLen = sizeof script0PubKeyHex / 2;
    unsigned char *script0PubKey = new unsigned char[script0PubKeyLen];
    hex2bin(script0PubKeyHex, script0PubKey);

    char script1PubKeyHex[] = "2103c9f4836b9a4f77fc0d81f7bcb01b7f1b35916864b9476c241ce9fc198bd25432ac";
    int script1PubKeyLen = sizeof script1PubKeyHex / 2;
    unsigned char *script1PubKey = new unsigned char[script1PubKeyLen];
    hex2bin(script1PubKeyHex, script1PubKey);

    char txSrcHex[] = "010000000001028ac60eb9575db5b2d987e29f301b5b819ea83a5c6579d282d189cc04b8e151ef0100000000ffffffff9f96ade4b41d5433f4eda31e1738ec2b36f6e7d1420d94a6af99801a88f7f7ff0000000048473044022037e0b64b6512e40f0b530420f8b51374e7778812fe642b43c61343d4672aab3402204f39311b20108c3f99d8b0a1b69c92844ccce1d45aeda9d95dcc7f7aed1717d201ffffffff0230517d01000000001976a9141d0f172a0ecb48aee1be1f2687d2963ae33f71a188ac008c864700000000160014b7cd046b6d522a3d61dbcb5235c0e9cc972654570247304402204ca3d7097e7ec004836574879c00059ccb7505de774832525d49c399c67dd77f022059feff13f7d2e808f47619ea4b3392528521a95d4470a9e2a2466cac5f94fe370121025476c2e83188368da1ff3e292e7acafcdb3566bb0ad253f62fc70f07aeee63570000000000";
    int txLen = sizeof txSrcHex / 2;
    unsigned char * txDest = new unsigned char[txLen];
    hex2bin(txSrcHex, txDest);

    auto spend0Result = bitcoinconsensus_verify_script_with_amount(script0PubKey, script0PubKeyLen, 600000000, txDest, txLen, 0, bitcoinconsensus_SCRIPT_FLAGS_VERIFY_ALL, &err);
    std::cout << "Spend txin[0] result: " << spend0Result << ", error code " << err << std::endl;

    auto spend1Result = bitcoinconsensus_verify_script_with_amount(script1PubKey, script1PubKeyLen, 625000000, txDest, txLen, 1, bitcoinconsensus_SCRIPT_FLAGS_VERIFY_ALL, &err);
    std::cout << "Spend txin[1] result: " << spend1Result << ", error code " << err << std::endl;

    getchar();

    return 0;
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.