MetacoSA / NBitcoin

Comprehensive Bitcoin library for the .NET framework.
MIT License
1.86k stars 840 forks source link

Can't Create "Fake" Coins for Testing #1118

Open ChristianOConnor opened 1 year ago

ChristianOConnor commented 1 year ago

I am writing tests for an app I built that uses NBitcoin to send bitcoin and make bitcoin transactions.

I have the following code to create fake bitcoins for an integration test:

private ICoin[] CreateFakeCoins(TxInList inputs, Script scriptPubKey)
{
    var coins = inputs.Select(i => new Coin(i.PrevOut, inputs.Transaction.Outputs.CreateNewTxOut(Money.Coins(0.1m), scriptPubKey.WitHash.ScriptPubKey))).ToArray();
    for (int i = 0; i < coins.Length; i++)
    {
        coins[i] = coins[i].ToScriptCoin(scriptPubKey);
    }
    return coins;
}

My integration test has this piece of code that's supposed to send the fake bitcoins to another wallet.

var k = new BitcoinSecret(new Key(), Network.TestNet);
var moneyInWallet1 = BitcoinHelper.GetBitcoinAddress(walletTestNet?.Address, Network.TestNet);

Transaction tx = Network.TestNet.CreateTransaction();
var privkey = new BitcoinSecret(walletTestNet?.Address, Network.TestNet);
var scriptPubKey = PayToWitScriptHashTemplate.Instance.GenerateScriptPubKey(k.PubKey.ScriptPubKey.WitHash);
var scriptPubKeyRec = PayToWitScriptHashTemplate.Instance.GenerateScriptPubKey(privkey.PubKey.ScriptPubKey.WitHash);

tx.Inputs.Add(new OutPoint(tx.GetHash(), 0), scriptPubKey.ToWitScript());
tx.Inputs.Add(new OutPoint(tx.GetHash(), 1), scriptPubKey.ToWitScript());
tx.Outputs.Add("21", k.PubKey.WitHash);
tx.Sign(new[] { k }, CreateFakeCoins(tx.Inputs, scriptPubKey));

Assert.Equal(k.GetAddress(ScriptPubKeyType.Segwit).ToString().Substring(0, 3), "tb1");
Assert.Equal(tx.Outputs[0].Value.Satoshi / 100000000, 21);
Assert.Equal(moneyInWallet1.Address.ToString().Substring(0, 3), "tb1");

var builder = Network.TestNet.CreateTransactionBuilder();
var (receiveAddress, receiveBtcSecret) = BitcoinHelper.GetBitcoinAddress(privKeyCurrent, Network.TestNet);
var (fromAddressProcessed, fromBtcSecret) = BitcoinHelper.GetBitcoinAddress(walletTestNet?.Address, Network.TestNet);

var amountSatoshiMult = Math.Ceiling(0.0006*100000000.00);
Money amountConvertedtoMoney = new Money((long)amountSatoshiMult);

var testibleOutputs = tx.Outputs.AsCoins().Cast<ICoin>().ToArray();
var txBuild = builder
    .AddCoins(testibleOutputs)
    .AddKeys(fromBtcSecret)
    .Send(receiveAddress, amountConvertedtoMoney)
    .SetChange(fromAddressProcessed, ChangeType.All)
    .SendFees(Money.Coins(new decimal(0.0001)));

NBitcoin.Policy.TransactionPolicyError[] errors = null;
Transaction nextTx = txBuild.BuildTransaction(true);
var testerForBuilder = builder.Verify(nextTx, out errors);
Console.WriteLine(errors);
Console.WriteLine(testerForBuilder);
Assert.Equal(true, testerForBuilder);

In this code, the BitcoinHelper.GetBitcoinAddress function simply receives an address string and returns a proper NBitcoin BitcoinAddress object.

This is a picture of the error I get:
enter image description here

Notice the WitnessProgramMissmatch error.

The issue stems from me not being able to see where the actual error is coming from. I don't know which part of the transaction I get wrong. I don't know if it's the fees, Input, network settings, etc...

What is the problem? Is CreateFakeCoins not correctly creating fake coins for testing? How can I make this work?

NicolasDorier commented 1 year ago

Your addresses are p2wpkh, but you had coin which were p2wsh

var coins = inputs
.Select(i => 
new Coin(i.PrevOut, inputs.Transaction.Outputs.CreateNewTxOut(Money.Coins(0.1m),
-  scriptPubKey.WitHash.ScriptPubKey)))
+ scriptPubKey)))