Example #1
0
        public static IEnumerable <object[]> GetCases()
        {
            // https://github.com/bitcoin/bitcoin/blob/48725e64fbfb85200dd2226386fbf1cfc8fe6c1f/src/test/data/tx_invalid.json

            var mockMempool = new MockMempool(null);

            foreach (var item in Helper.ReadResource <JArray>("BitcoinCore.tx_invalid"))
            {
                var mockDB = new MockUtxoDatabase();
                if (item is JArray arr1 && arr1.Count == 3)
                {
                    // [
                    //   [
                    //     [prevout hash, prevout index, prevout scriptPubKey, amount?],
                    //     [input 2],
                    //     ...
                    //   ],
                    //   serializedTransaction,
                    //   verifyFlags
                    // ]
                    if (arr1[0] is not JArray arr2 || arr2.Any(x => x is not JArray arr3 || (arr3.Count != 3 && arr3.Count != 4)) ||
                        arr1[1].Type != JTokenType.String ||
                        arr1[2].Type != JTokenType.String)
                    {
                        Assert.True(false, $"Bad test found: {arr1}");
                    }

                    if (arr1[1].ToString() == "01000000010001000000000000000000000000000000000000000000000000000000000000000000006d483045022027deccc14aa6668e78a8c9da3484fbcd4f9dcc9bb7d1b85146314b21b9ae4d86022100d0b43dece8cfb07348de0ca8bc5b86276fa88f7f2138381128b7c36ab2e42264012321029bb13463ddd5d2cc05da6e84e37536cb9525703cfd8f43afdb414988987a92f6acffffffff020040075af075070001510001000000000000015100000000")
                    {
                        // This tx has 2 outputs one with max supply (21 million) and another with a small one
                        // Since total is higher than max supply the tx is invalid.
                        // We rely on the fact that our UTXO database is correct and reliable
                        continue;
                    }

                    if (arr1[1].ToString() == "0100000000010300010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000200000000ffffffff03e8030000000000000151d0070000000000000151b80b00000000000001510002483045022100a3cec69b52cba2d2de623ffffffffff1606184ea55476c0f8189fda231bc9cbb022003181ad597f7c380a7d1c740286b1d022b8b04ded028b833282e055e03b8efef812103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710000000000" ||
                        arr1[1].ToString() == "010000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff01e803000000000000015100000000")
                    {
                        // This has a higher than 0 witness program version and by "standard rules" is rejected
                        // We don't have this rule and probably won't add it either.
                        Assert.Contains("DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM", arr1[2].ToString());
                        continue;
                    }

                    string flags = arr1[2].ToString();
                    if (flags.Contains("CONST_SCRIPTCODE"))
                    {
                        // We don't reject CodeSeparator and signature in script being signed
                        continue;
                    }

                    MockConsensus consensus = GetConsensus(arr1[2].ToString(), MockHeight);

                    var tx = new Transaction();
                    if (!tx.TryDeserialize(new FastStreamReader(Helper.HexToBytes(arr1[1].ToString())), out string error))
                    {
                        if (error == "TxOut count cannot be zero.")
                        {
                            // This check takes place during deserialization of blocks/transactions not during verification.
                            // In other words there is no way to instantiate a transaction with zero inputs/outputs.
                            // When TryDeserialize fails with this error, we consider this test successful.
                            continue;
                        }
                        if (error == "Amount is bigger than total bitcoin supply.")
                        {
                            // Another check that should take place during deserialization not verification.
                            continue;
                        }

                        // Any other error and this test case is considered broken:
                        Assert.True(false, $"{error}{Environment.NewLine}{arr1[1]}");
                    }

                    ulong totalSpent = (ulong)tx.TxOutList.Sum(x => (long)x.Amount);

                    bool isCoinbase = false;
                    foreach (JArray tinArr in arr1[0])
                    {
                        var txHash = Helper.HexToBytes(tinArr[0].ToString(), true);
                        int index  = (int)tinArr[1];
                        isCoinbase = index == -1;
                        if (isCoinbase)
                        {
                            // There are 2 cases neither have BIP34 enabled
                            if (tx.GetTransactionId() == "bf93c6fe89592b2508f2876c6200d5ffadbd741e18c57b5e9fe5eff3101137b3" ||
                                tx.GetTransactionId() == "d12fb29f9b00aaa9e89f5c34a27f43dd73f729aa796b36da0738b97f00587d0b" ||
                                tx.GetTransactionId() == "a87439547de75492b9fa7299b0ba1cf729c2ae53e84979bb713175171e93fe74" ||
                                tx.GetTransactionId() == "2d3f5c65b0ae97f5f52bef7f3b9c8ffeaeed0957289b5750a1c75bf30dd4493a")
                            {
                                consensus.bip34 = false;
                            }
                            else
                            {
                                Assert.True(false, "A new coinbase test vector is detected");
                            }

                            break;
                        }
                        var utxo = new MockUtxo()
                        {
                            Index     = (uint)index,
                            PubScript = TxValidTests.GetPubScr(tinArr[2].ToString()),
                            // If amount is not set, it is changed to maximum spent by tx to satisfy the fee check
                            Amount = tinArr.Count == 4 ? (ulong)tinArr[3] : totalSpent
                        };

                        mockDB.Add(txHash, utxo);
                    }


                    yield return(new object[]
                    {
                        mockDB, mockMempool, consensus, tx, isCoinbase
                    });
                }
            }
        }
Example #2
0
        public static IEnumerable <object[]> GetCases()
        {
            // https://github.com/bitcoin/bitcoin/blob/48725e64fbfb85200dd2226386fbf1cfc8fe6c1f/src/test/data/tx_valid.json

            var mockMempool = new MockMempool(null);

            foreach (var item in Helper.ReadResource <JArray>("BitcoinCore.tx_valid"))
            {
                var mockDB = new MockUtxoDatabase();
                if (item is JArray arr1 && arr1.Count == 3)
                {
                    // [
                    //   [
                    //     [prevout hash, prevout index, prevout scriptPubKey, amount?],
                    //     [input 2],
                    //     ...
                    //   ],
                    //   serializedTransaction,
                    //   excluded verifyFlags
                    // ]
                    if (arr1[0] is not JArray arr2 || arr2.Any(x => x is not JArray arr3 || (arr3.Count != 3 && arr3.Count != 4)) ||
                        arr1[1].Type != JTokenType.String ||
                        arr1[2].Type != JTokenType.String)
                    {
                        Assert.True(false, $"Bad test found: {arr1}");
                    }

                    MockConsensus consensus = GetConsensus(arr1[2].ToString(), MockHeight);

                    var tx = new Transaction();
                    if (!tx.TryDeserialize(new FastStreamReader(Helper.HexToBytes(arr1[1].ToString())), out string error))
                    {
                        Assert.True(false, error);
                    }

                    ulong totalSpent = (ulong)tx.TxOutList.Sum(x => (long)x.Amount);

                    bool isCoinbase = false;
                    foreach (JArray tinArr in arr1[0])
                    {
                        var txHash = Helper.HexToBytes(tinArr[0].ToString(), true);
                        int index  = (int)tinArr[1];
                        isCoinbase = index == -1;
                        if (isCoinbase)
                        {
                            // Check if test vector actually has only 1 input as a coinbase transactions must
                            Assert.Single(arr1[0]);

                            // There are 2 cases neither have BIP34 enabled
                            if (tx.GetTransactionId() == "99d3825137602e577aeaf6a2e3c9620fd0e605323dc5265da4a570593be791d4" ||
                                tx.GetTransactionId() == "c0d67409923040cc766bbea12e4c9154393abef706db065ac2e07d91a9ba4f84")
                            {
                                consensus.bip34 = false;
                            }
                            else
                            {
                                Assert.True(false, "A new coinbase test vector is detected");
                            }

                            break;
                        }
                        var utxo = new MockUtxo()
                        {
                            Index     = (uint)index,
                            PubScript = GetPubScr(tinArr[2].ToString()),
                            // If amount is not set, it is changed to maximum spent by tx to satisfy the fee check
                            Amount = tinArr.Count == 4 ? (ulong)tinArr[3] : totalSpent
                        };

                        mockDB.Add(txHash, utxo);
                    }

                    yield return(new object[]
                    {
                        mockDB, mockMempool, consensus, tx, isCoinbase
                    });
                }
            }
        }