MetacoSA / NBitcoin

Comprehensive Bitcoin library for the .NET framework.
MIT License
1.87k stars 844 forks source link

Unable to execute MultiSig Transaction : Error INVALID #1057

Closed AbdulSidd closed 2 years ago

AbdulSidd commented 2 years ago

Hello, I am trying to transact the funds from a MultiSig address to another address; but every time getting the error as "INVALID". Please help me to rectify the issue. I want to understand the multiSig wallet functions with their practical implementation.

static void Main(string[] args) { var MasterKey = new BitcoinExtKey("tprv8ZgxMBicQKsPesizPiLtZx7ZTieVuL35zHuSoka67gJEZZ4CLj5ZFuVrNemdqLdiK5qLmNiHbumaE5zFRgZWPrYKWn3mLgEtSoEtzueDN8U", Network.TestNet);

        ExtKey Alice_sc = MasterKey.Derive(1);
        ExtPubKey Alice_pk = Alice_sc.Neuter();
        var Alice_sc_str = Alice_sc.ToString(Network.TestNet);
        var Alice_pk_str = Alice_pk.ToString(Network.TestNet);

        ExtKey Bob_sc = MasterKey.Derive(2);
        ExtPubKey Bob_pk = Bob_sc.Neuter();
        var Bob_sc_str = Bob_sc.ToString(Network.TestNet);
        var Bob_pk_str = Bob_pk.ToString(Network.TestNet);

        ExtKey Nico_sc = MasterKey.Derive(3);
        ExtPubKey Nico_pk = Nico_sc.Neuter();
        var Nico_sc_str = Nico_sc.ToString(Network.TestNet);
        var Nico_pk_str = Nico_pk.ToString(Network.TestNet);

        Script multiSig_Script_1 = PayToMultiSigTemplate.Instance.GenerateScriptPubKey(2, new[] { Alice_pk.PubKey, Bob_pk.PubKey });
        var multiSig_Script_Address_1 = multiSig_Script_1.WitHash.ScriptPubKey.Hash.ScriptPubKey.GetDestinationAddress(Network.TestNet);

        string address = multiSig_Script_Address_1.ToString();

        Money ReceivedCoins = 0;
        Money SpentCoins = 0;

        var client = new QBitNinjaClient(Network.TestNet);
        var balance = client.GetBalance(multiSig_Script_Address_1).Result;

        var latestTransaction = balance.Operations.Last();

        var receivedCoins = latestTransaction.ReceivedCoins;
        OutPoint outpointToSpend = null;
        ScriptCoin coinToSpend = null;
        foreach (var c in receivedCoins)
        {
            try
            {
                // If we can make a ScriptCoin out of our redeemScript
                // we "own" this outpoint
                coinToSpend = new ScriptCoin(c, multiSig_Script_1);
                outpointToSpend = c.Outpoint;
            }
            catch { }
        }

        if (outpointToSpend == null)
            throw new Exception("TxOut doesn't contain any our ScriptPubKey");
        Console.WriteLine("We want to spend outpoint #{0}", outpointToSpend.N + 1);

        TransactionBuilder builder = Network.TestNet.CreateTransactionBuilder();
        var SatoshiAddress = Nico_pk.PubKey.GetAddress(ScriptPubKeyType.Segwit, Network.TestNet);

        var minerFee = new Money(0.200m, MoneyUnit.Satoshi);
        var txInAmount = (Money)receivedCoins[(int)outpointToSpend.N-1].Amount / 2;

        var sendAmount = txInAmount - minerFee;

        Transaction unsigned =
            builder
                .AddCoins(coinToSpend)
                .Send(SatoshiAddress, sendAmount)
                .SendFees(minerFee)
                .SetChange(SatoshiAddress)
                .BuildTransaction(sign: false);
        try
        {
            // Alice signs it
            Transaction aliceSigned =
                builder
                    //.AddCoins(coinToSpend)
                    .AddKeys(Alice_sc)
                    .SignTransaction(unsigned);

            // Gotta get Bob's approval too
            Transaction bobSigned =
                builder
                    //.AddCoins(coinToSpend)
                    .AddKeys(Bob_sc)
                    .SignTransaction(aliceSigned);

            // Program.cs (cont.)

            Transaction fullySigned =
                builder
                    //.AddCoins(coinToSpend)
                    .CombineSignatures(aliceSigned, bobSigned);

            Console.WriteLine(fullySigned);

            BroadcastResponse broadcastResponse = client.Broadcast(fullySigned).Result;
            if (!broadcastResponse.Success)
            {
                Console.WriteLine("ErrorCode: {0}", broadcastResponse.Error.ErrorCode);
            }
            else
            {
                Console.WriteLine("Success! You can check out the hash of the transaciton in any block explorer:");
            }

        }
        catch (Exception e)
        {
            Console.WriteLine(e);
            throw;
        }

    }
NicolasDorier commented 2 years ago

QBitNinja is outdated, the error you have may be unrelated to you. Your code seems good.

You can take a look at an easier example using NBXplorer: https://github.com/dgarage/NBXplorer/blob/master/Examples/MultiSig/Program.cs

Contrary to QBitNinja, it is easy to self-host, and can get you all the coins for a multi sig wallet directly ready to sign. Also, it creates a HD wallet, which is more in line with current Bitcoin best practices.

The example has instruction to try it on regtest.