Wie signiere ich eine SegWit-Transaktion über NBitcoin?

Die meisten Tutorials da draußen zeigen nur, wie man es auf die alte Art und Weise signiert. Und ich weiß, dass NBitcoin SegWit bereits unterstützt, gibt es also einen Ausschnitt, wie man es auf diese Weise signiert?

Antworten (2)

Das Signieren mit Segwit unterscheidet sich in keiner Weise vom Signieren mit P2SH oder P2PKH.

Wenn Sie Interoperabilität wünschen, müssen Sie Segwit verwenden, das in P2SH verpackt ist. Weitere Informationen finden Sie unter https://programmingblockchain.gitbooks.io/programmingblockchain/content/other_types_of_ownership/ .

Aber kurz gesagt, wenn Sie ein P2WPKH-PS2SH haben möchten (Segwit P2PKH, das für die Interoperabilität in P2SH verpackt ist), dann fragen Sie die Coins basierend auf dieser Adresse ab.

[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));
}

Nikolaus,

Sie können Transaction.Sign(keys, coins)es auch verwenden, da es von TransactionBuilder unter der Haube abhängt.

Ich bin auch hier hängen geblieben und zusammen mit Nicolas Antwort haben die folgenden Codebeispiele dazu beigetragen, mein Verständnis zu klären.

Ich habe das einfachste Segwit-Beispiel von bip-0143 als Grundlage verwendet. Das folgende C#-Codebeispiel gibt die beiden Eingaben aus der oben verlinkten Beispieltransaktion korrekt aus. Beachten Sie, dass nur die zweite Eingabe (Hash ef51e ...) ein Segwit-SkriptPubKey verwendet, sodass theoretisch nur Coin1 im folgenden Beispiel eine Segwit-Ausgabe durchführt.

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));

Um die Signaturen zu überprüfen, habe ich die C++ bitcoinconsensus-Bibliothek verwendet, die als Teil des Bitcoin-Kerns erstellt wird.

#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;
}