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