diff --git a/.gitignore b/.gitignore index aa071c5..c2c8e35 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,4 @@ node_modules/ # Development **/TODO +.vscode diff --git a/Scarb.lock b/Scarb.lock index c302a5a..f42ddc5 100644 --- a/Scarb.lock +++ b/Scarb.lock @@ -42,6 +42,7 @@ dependencies = [ name = "shinigami_tests" version = "0.1.0" dependencies = [ + "ripemd160", "shinigami_compiler", "shinigami_engine", "shinigami_utils", diff --git a/packages/tests/Scarb.toml b/packages/tests/Scarb.toml index f73d320..9294604 100644 --- a/packages/tests/Scarb.toml +++ b/packages/tests/Scarb.toml @@ -4,6 +4,7 @@ version = "0.1.0" edition = "2024_07" [dependencies] +ripemd160.workspace = true shinigami_compiler = { path = "../compiler" } shinigami_engine = { path = "../engine" } shinigami_utils = { path = "../utils" } diff --git a/packages/tests/src/lib.cairo b/packages/tests/src/lib.cairo index 13110af..0cc3c2c 100644 --- a/packages/tests/src/lib.cairo +++ b/packages/tests/src/lib.cairo @@ -22,4 +22,5 @@ pub mod tests { mod test_p2wpkh; mod test_p2wsh; mod test_p2ms; + mod test_p2sh; } diff --git a/packages/tests/src/tests/test_p2sh.cairo b/packages/tests/src/tests/test_p2sh.cairo new file mode 100644 index 0000000..94a02dd --- /dev/null +++ b/packages/tests/src/tests/test_p2sh.cairo @@ -0,0 +1,129 @@ +use shinigami_engine::transaction::EngineInternalTransactionTrait; +use shinigami_engine::engine::EngineImpl; +use shinigami_engine::hash_cache::HashCacheImpl; +use crate::utxo::UTXO; +use crate::validate::validate_p2sh; +use shinigami_utils::bytecode::hex_to_bytecode; + + +#[test] +fn test_p2sh_transaction_1() { + //https://learnmeabitcoin.com/explorer/tx/4d8eabfc8e6c266fb0ccd815d37dd69246da634df0effd5a5c922e4ec37880f6 + let raw_transaction_hex = + "0x0100000003a5ee1a0fd80dfbc3142df136ab56e082b799c13aa977c048bdf8f61bd158652c000000006b48304502203b0160de302cded63589a88214fe499a25aa1d86a2ea09129945cd632476a12c022100c77727daf0718307e184d55df620510cf96d4b5814ae3258519c0482c1ca82fa0121024f4102c1f1cf662bf99f2b034eb03edd4e6c96793cb9445ff519aab580649120ffffffff0fce901eb7b7551ba5f414735ff93b83a2a57403df11059ec88245fba2aaf1a0000000006a47304402204089adb8a1de1a9e22aa43b94d54f1e54dc9bea745d57df1a633e03dd9ede3c2022037d1e53e911ed7212186028f2e085f70524930e22eb6184af090ba4ab779a5b90121030644cb394bf381dbec91680bdf1be1986ad93cfb35603697353199fb285a119effffffff0fce901eb7b7551ba5f414735ff93b83a2a57403df11059ec88245fba2aaf1a0010000009300493046022100a07b2821f96658c938fa9c68950af0e69f3b2ce5f8258b3a6ad254d4bc73e11e022100e82fab8df3f7e7a28e91b3609f91e8ebf663af3a4dc2fd2abd954301a5da67e701475121022afc20bf379bc96a2f4e9e63ffceb8652b2b6a097f63fbee6ecec2a49a48010e2103a767c7221e9f15f870f1ad9311f5ab937d79fcaeee15bb2c722bca515581b4c052aeffffffff02a3b81b00000000001976a914ea00917f128f569cbdf79da5efcd9001671ab52c88ac80969800000000001976a9143dec0ead289be1afa8da127a7dbdd425a05e25f688ac00000000"; + let raw_transaction = hex_to_bytecode(@raw_transaction_hex); + let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction); + + let prevout_pubkey = "0xa914748284390f9e263a4b766a75d0633c50426eb87587"; + let prev_out_1of2_invalid = UTXO { + amount: 10000000, pubkey_script: hex_to_bytecode(@prevout_pubkey), block_height: 177625 + }; + + let utxo_hints = array![prev_out_1of2_invalid]; + + let res = validate_p2sh(@transaction, 0, utxo_hints, 2); + assert!(res.is_ok(), "P2SH failed!:{:?}", res.unwrap_err()); +} + +#[test] +fn test_p2sh_transaction_2() { + //https://learnmeabitcoin.com/explorer/tx/7edb32d4ffd7a385b763c7a8e56b6358bcd729e747290624e18acdbe6209fc45 + let raw_transaction_hex = + "0x0100000001c8cc2b56525e734ff63a13bc6ad06a9e5664df8c67632253a8e36017aee3ee40000000009000483045022100ad0851c69dd756b45190b5a8e97cb4ac3c2b0fa2f2aae23aed6ca97ab33bf88302200b248593abc1259512793e7dea61036c601775ebb23640a0120b0dba2c34b79001455141042f90074d7a5bf30c72cf3a8dfd1381bdbd30407010e878f3a11269d5f74a58788505cdca22ea6eab7cfb40dc0e07aba200424ab0d79122a653ad0c7ec9896bdf51aefeffffff0120f40e00000000001976a9141d30342095961d951d306845ef98ac08474b36a088aca7270400"; + let raw_transaction = hex_to_bytecode(@raw_transaction_hex); + let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction); + + let prevout_pubkey = "0xa914e9c3dd0c07aac76179ebc76a6c78d4d67c6c160a87"; + + let prev_out_1of2_invalid = UTXO { + amount: 990000, pubkey_script: hex_to_bytecode(@prevout_pubkey), block_height: 272295 + }; + + let utxo_hints = array![prev_out_1of2_invalid]; + + let res = validate_p2sh(@transaction, 0, utxo_hints, 0); + assert!(res.is_ok(), "P2SH failed!:{:?}", res.unwrap_err()); +} + + +#[test] +fn test_p2sh_transaction_3() { + //https://learnmeabitcoin.com/explorer/tx/30c239f3ae062c5f1151476005fd0057adfa6922de1b38d0f11eb657a8157b30 + let raw_transaction_hex = + "0x010000000c3e10e0814786d6e02dfab4e2569d01a63191b8449bb0f5b9af580fc754ae83b9000000006c493046022100b387bc213db8a333f737e7e6b47ac5e56ba707e97682c1d6ae1d01e28fcfba620221009b7651bbf054babce6884937d598f845f533bac5dc0ec235b0e3408532b9c6e101210308b4492122999b36c09e50121544aa402cef45cd41970f1be6b71dcbd092a35effffffff40629a5c656e8a9fb80ae8a5d55b80fbb598a059bad06c50fcddf404e932d59e000000006a473044022011097b0f58e39fe1f0df7b3159456b12c3b244dcdf6a0fd138ec17d76d41eb5c02202fb5e7cec4f2efbcc90989693b7b6309fcaa27d6aac71eb3dcef60e27a7e7357012103c241a14762ef670d96c0afa470463512f5f356e0752216d34d55b6bfa38acd93ffffffff5763070e224f18dbc8211e60c05ae31543958b248eb08c9e5989167c60b3c570000000006c49304602210088db31bb970f2e77a745d15b8a31d64734c8a9eca3a24540ffa850c90f8a6f50022100bc43eb2a20d70da74cfb2be8eee69c0c1adf741130792aa882a0cda9f7df4b6f012102b5e2177732d3f19abd0e15ac5ff2d5546f70e3f91674b110ccdee8458554f1acffffffff5b4e96a245f6fbc2efb910e25e9dd7a26e0ef8486eebd50dc658ae7d9719e5fd000000006a4730440220656be7132d238e4a848f0da1c3bdc0e22b475e1b66011e1b0536e18cbfe553f502205c89da6c8dad09f5e171404bf66fc19c7d5d2066d4ff4eff3f0766d31688cc4d012102086323b48e87d7fcacb014a58889f20a9881956bf46898c4ffda84b23c965d31ffffffff6889fe551cb869bf20284c64fc3adc229fded6e11fc8b79ec11bb2e499bd0d6c290000006a4730440220226d97d92d855bb2dad731b0cf339727e0f4449c89b1cc1cff7a9432db2a53fb02203478f549e5997b0dccd6abbc5bb206ce40f706672e27b58e3bab210da105dbcf012103c241a14762ef670d96c0afa470463512f5f356e0752216d34d55b6bfa38acd93ffffffff6a1c310490053bfc791ec646907941d3df59bfa8db1b21789d8780c7489695c1000000006a473044022079913e50a223d46c3800f33a6071651aabeecbcc7c726a78aca04dd2832ebe92022075275dbfadcfcca48fa834e7130d24b1055e9ee1470e0bf7ecdf0d9091b27fdc012102fbb8f0fcb28163dd56e26fd7d4b85b71016e62696e577057ddeac36d08a03e26ffffffff79d87f7daedaee7c6e80059b38cde214fec5e4546fbdccc7c24c01c47dce1c23200000008c493046022100ec02daed0c2ab978f588a0486deef52e62b6aa82297b994fe5486d79f8457acb02210098750e260959d6bbd4d47a018b27ea15493d4cd4cb7c96136282745c41aa1c9b014104658e3e86e3740257ebf67085deb14b877955aac502a6b5dcec0cfe1f3026f27b3a772a189b1bb2c28d026bc626a48710edffa9d40830286b80b3ac5709509974ffffffff9a19e8ede8836c192fe816d80d392bb7bb5453f320a78854a83e46bd9f27bf1e000000006c4930460221008b06d1813afd4f368a9570405df7978dca0b4400d173c937931942d88776bfa4022100a7a85b09e50e12e474b634a22fbe6645227dc13cbba2aaa2a84bb1da5e1dc2f1012103c241a14762ef670d96c0afa470463512f5f356e0752216d34d55b6bfa38acd93ffffffffd3090eb0855eee3d1dba53d68edeca6c368a37d3bba9579da3ac675ece42d7680e0000008a47304402204e2518419626eb846e0ef96fb7eda1d7b954b2821482b771f372484c0e327e560220370108f1a7b4676973585c861f5365d8fc2b2b170d922d6fccb15216976a82f80141044884e2974c370394aae8121735a56eaa7215a6a46661f1ca9454c1b99611ae34903e9515b2902f2a22104d10bfd1c2303b38a14be5f2b62b0591ca0d8bbb6864fffffffff61ff40c78b3e12e7d1f9a9db04a7b7736510014fc15a950d575c159b4b0b7a5000000008c493046022100b9b7c3ac969ee98295ec063c84f05c4bf4ee0d4c25448847d44c8e4af3425af7022100cfc90b396f524c366d66a44fa77502dd6f338a584ce653332bcb8909d14360c00141048501beadf835ce4da4078dce8a9dd57964f91da9d675b3d23d45f0de71a03b24d0daf75f29cd521531d5b4389331fe6891e7e1214710cf73e7dbc91cd41cfcecffffffff4471e66e1622bf197ba49ab31d1bd29b4917af60ce103bb6713ffb709b300c45000000006b483045022100a84f83410eb3b40959830b444a85dc1251486afa6e27288bd22fb5771d09795302207d604b1d1c3f8f2d3a9c2ee1007f6b034f69339d0de4f567c12f54af14e208b6012102cbac13c0b22e24ab33131c69e36bdbbe0218cd7f43dcbf9a4b488aadc8ac23b4ffffffff4471e66e1622bf197ba49ab31d1bd29b4917af60ce103bb6713ffb709b300c45010000009100473044022100d0ed946330182916da16a6149cd313a4b1a7b41591ee52fb3e79d64e36139d66021f6ccf173040ef24cb45c4db3e9c771c938a1ba2cf8d2404416f70886e360af401475121022afc20bf379bc96a2f4e9e63ffceb8652b2b6a097f63fbee6ecec2a49a48010e2103a767c7221e9f15f870f1ad9311f5ab937d79fcaeee15bb2c722bca515581b4c052aeffffffff0196e756080000000017a914748284390f9e263a4b766a75d0633c50426eb8758700000000"; + let raw_transaction = hex_to_bytecode(@raw_transaction_hex); + let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction); + + let prevout_pubkey = "0xa914748284390f9e263a4b766a75d0633c50426eb87587"; + + let prev_out = UTXO { + amount: 10000000, pubkey_script: hex_to_bytecode(@prevout_pubkey), block_height: 183729 + }; + + let utxo_hints = array![prev_out]; + + let res = validate_p2sh(@transaction, 0, utxo_hints, 11); + assert!(res.is_ok(), "P2SH failed!:{:?}", res.unwrap_err()); +} + +// TODO: Fix this test +#[ignore] +#[test] +fn test_p2sh_transaction_4() { + //https://learnmeabitcoin.com/explorer/tx/cc11ca9e9dc188663c41eb23b15370f68eded56b7ec54dd5bc4f2d2ae93addb2 + let raw_transaction_hex = + "0x010000000113fc91b458c8ed3a826cfc984ab454246036f91a4beafb56b88b115e8db1add300000000fd5f0100483044022018b88c05ab571cf31bf317a98ed5909cf43218f472bfbeb82b6857d3b1edf4ee0220686627e66b368e298114a097b5814a76fdaac23f7728a42d38415552ba68c8220101493046022100b7a70f4c3b2b5d24475f9664bb77b6046c0251c89b446aad8a86b584b74ed414022100d53dd27b741801a908fdf7c2ee259f444f549b56d7a6c682599ef67b1edb6d24014cc9524104f3d35132084eb1b99b6506178c20adb42d26296012e452e392689bdb6553db33ba24b900000892805de1646821c7b0fb50b3d879c26e2b493b7041e6215356a04104ab4ecc9e8ea2da0562af25bcaede00c4d5a00db60edc17672376decf0a35a34fdc9f1ffad1fb74fd7b1b198b9231c25df88e0769bec49975649b4b3f40adafb04104f7149f270717c00f6cc09b9ce3c22791c4aab1af40a5107aacca85b6f644cc0d84459e308f998d801b8d9d355f8ec33b0e41866841e2870754cf667a9821703d53aeffffffff0140fa97000000000017a9146ebdbaf0840274a6b23b0643b75ef4e8e24f37b88700000000"; + let raw_transaction = hex_to_bytecode(@raw_transaction_hex); + let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction); + + let prevout_pubkey = "0xa914b4acb9d78d6a6256964a60484c95de490eaaae7587"; + + let prev_out = UTXO { + amount: 9980000, pubkey_script: hex_to_bytecode(@prevout_pubkey), block_height: 232593 + }; + + let utxo_hints = array![prev_out]; + + let res = validate_p2sh(@transaction, 0, utxo_hints, 0); + assert!(res.is_ok(), "P2SH failed!:{:?}", res.unwrap_err()); +} + +#[test] +fn test_p2sh_transaction_5() { + //https://learnmeabitcoin.com/explorer/tx/09afa3b1393f99bb01aa754dd4b89293fd8d6c9741488b537d14f7f81de1450e + let raw_transaction_hex = + "0x01000000017fa897c3556271c34cb28c03c196c2d912093264c9d293cb4980a2635474467d010000000f5355540b6f93598893578893588851ffffffff01501e0000000000001976a914aa2482ce71d219018ef334f6cc551ee88abd920888ac00000000"; + let raw_transaction = hex_to_bytecode(@raw_transaction_hex); + + let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction); + + let prevout_pubkey = "0xa914da5a92e670a66538be1c550af352646000b2367d87"; + + let prev_out = UTXO { + amount: 10000, pubkey_script: hex_to_bytecode(@prevout_pubkey), block_height: 384639 + }; + + let utxo_hints = array![prev_out]; + + let res = validate_p2sh(@transaction, 0, utxo_hints, 0); + assert!(res.is_ok(), "P2SH failed!:{:?}", res.unwrap_err()); +} + +#[test] +fn test_p2sh_transaction_6() { + //https://learnmeabitcoin.com/explorer/tx/8d31992805518fd62daa3bdd2a5c4fd2cd3054c9b3dca1d78055e9528cff6adc#input-4 + let raw_transaction_hex = + "0x010000000a838a1331f4a8c2c007f74d8b3621836a8c00cc8f2c520cf41a26f6e52d8ede0900000000fd8f024d4001255044462d312e330a25e2e3cfd30a0a0a312030206f626a0a3c3c2f57696474682032203020522f4865696768742033203020522f547970652034203020522f537562747970652035203020522f46696c7465722036203020522f436f6c6f7253706163652037203020522f4c656e6774682038203020522f42697473506572436f6d706f6e656e7420383e3e0a73747265616d0affd8fffe00245348412d3120697320646561642121212121852fec092339759c39b1a1c63c4c97e1fffe017f46dc93a6b67e013b029aaa1db2560b45ca67d688c7f84b8c4c791fe02b3df614f86db1690901c56b45c1530afedfb76038e972722fe7ad728f0e4904e046c230570fe9d41398abe12ef5bc942be33542a4802d98b5d70f2a332ec37fac3514e74ddc0f2cc1a874cd0c78305a21566461309789606bd0bf3f98cda8044629a14d4001255044462d312e330a25e2e3cfd30a0a0a312030206f626a0a3c3c2f57696474682032203020522f4865696768742033203020522f547970652034203020522f537562747970652035203020522f46696c7465722036203020522f436f6c6f7253706163652037203020522f4c656e6774682038203020522f42697473506572436f6d706f6e656e7420383e3e0a73747265616d0affd8fffe00245348412d3120697320646561642121212121852fec092339759c39b1a1c63c4c97e1fffe017346dc9166b67e118f029ab621b2560ff9ca67cca8c7f85ba84c79030c2b3de218f86db3a90901d5df45c14f26fedfb3dc38e96ac22fe7bd728f0e45bce046d23c570feb141398bb552ef5a0a82be331fea48037b8b5d71f0e332edf93ac3500eb4ddc0decc1a864790c782c76215660dd309791d06bd0af3f98cda4bc4629b1086e879169a77ca787ffffffffb076f0e08ab95182dd10af56a08c03d2643c7dbbf6c025abd2a3f815ee127f3900000000fd8f024d4001255044462d312e330a25e2e3cfd30a0a0a312030206f626a0a3c3c2f57696474682032203020522f4865696768742033203020522f547970652034203020522f537562747970652035203020522f46696c7465722036203020522f436f6c6f7253706163652037203020522f4c656e6774682038203020522f42697473506572436f6d706f6e656e7420383e3e0a73747265616d0affd8fffe00245348412d3120697320646561642121212121852fec092339759c39b1a1c63c4c97e1fffe017f46dc93a6b67e013b029aaa1db2560b45ca67d688c7f84b8c4c791fe02b3df614f86db1690901c56b45c1530afedfb76038e972722fe7ad728f0e4904e046c230570fe9d41398abe12ef5bc942be33542a4802d98b5d70f2a332ec37fac3514e74ddc0f2cc1a874cd0c78305a21566461309789606bd0bf3f98cda8044629a14d4001255044462d312e330a25e2e3cfd30a0a0a312030206f626a0a3c3c2f57696474682032203020522f4865696768742033203020522f547970652034203020522f537562747970652035203020522f46696c7465722036203020522f436f6c6f7253706163652037203020522f4c656e6774682038203020522f42697473506572436f6d706f6e656e7420383e3e0a73747265616d0affd8fffe00245348412d3120697320646561642121212121852fec092339759c39b1a1c63c4c97e1fffe017346dc9166b67e118f029ab621b2560ff9ca67cca8c7f85ba84c79030c2b3de218f86db3a90901d5df45c14f26fedfb3dc38e96ac22fe7bd728f0e45bce046d23c570feb141398bb552ef5a0a82be331fea48037b8b5d71f0e332edf93ac3500eb4ddc0decc1a864790c782c76215660dd309791d06bd0af3f98cda4bc4629b1086e879169a77ca787ffffffffc36275c301f09240cbb2372afa0ba9ab49c59827ed015e6bffe94e7a8cbb4e7400000000fd8f024d4001255044462d312e330a25e2e3cfd30a0a0a312030206f626a0a3c3c2f57696474682032203020522f4865696768742033203020522f547970652034203020522f537562747970652035203020522f46696c7465722036203020522f436f6c6f7253706163652037203020522f4c656e6774682038203020522f42697473506572436f6d706f6e656e7420383e3e0a73747265616d0affd8fffe00245348412d3120697320646561642121212121852fec092339759c39b1a1c63c4c97e1fffe017f46dc93a6b67e013b029aaa1db2560b45ca67d688c7f84b8c4c791fe02b3df614f86db1690901c56b45c1530afedfb76038e972722fe7ad728f0e4904e046c230570fe9d41398abe12ef5bc942be33542a4802d98b5d70f2a332ec37fac3514e74ddc0f2cc1a874cd0c78305a21566461309789606bd0bf3f98cda8044629a14d4001255044462d312e330a25e2e3cfd30a0a0a312030206f626a0a3c3c2f57696474682032203020522f4865696768742033203020522f547970652034203020522f537562747970652035203020522f46696c7465722036203020522f436f6c6f7253706163652037203020522f4c656e6774682038203020522f42697473506572436f6d706f6e656e7420383e3e0a73747265616d0affd8fffe00245348412d3120697320646561642121212121852fec092339759c39b1a1c63c4c97e1fffe017346dc9166b67e118f029ab621b2560ff9ca67cca8c7f85ba84c79030c2b3de218f86db3a90901d5df45c14f26fedfb3dc38e96ac22fe7bd728f0e45bce046d23c570feb141398bb552ef5a0a82be331fea48037b8b5d71f0e332edf93ac3500eb4ddc0decc1a864790c782c76215660dd309791d06bd0af3f98cda4bc4629b1086e879169a77ca787ffffffff7228177b29c6bb0e820b51c925522e3603fe189dc8147ad630c2bb42b63845c602000000fd8f024d4001255044462d312e330a25e2e3cfd30a0a0a312030206f626a0a3c3c2f57696474682032203020522f4865696768742033203020522f547970652034203020522f537562747970652035203020522f46696c7465722036203020522f436f6c6f7253706163652037203020522f4c656e6774682038203020522f42697473506572436f6d706f6e656e7420383e3e0a73747265616d0affd8fffe00245348412d3120697320646561642121212121852fec092339759c39b1a1c63c4c97e1fffe017f46dc93a6b67e013b029aaa1db2560b45ca67d688c7f84b8c4c791fe02b3df614f86db1690901c56b45c1530afedfb76038e972722fe7ad728f0e4904e046c230570fe9d41398abe12ef5bc942be33542a4802d98b5d70f2a332ec37fac3514e74ddc0f2cc1a874cd0c78305a21566461309789606bd0bf3f98cda8044629a14d4001255044462d312e330a25e2e3cfd30a0a0a312030206f626a0a3c3c2f57696474682032203020522f4865696768742033203020522f547970652034203020522f537562747970652035203020522f46696c7465722036203020522f436f6c6f7253706163652037203020522f4c656e6774682038203020522f42697473506572436f6d706f6e656e7420383e3e0a73747265616d0affd8fffe00245348412d3120697320646561642121212121852fec092339759c39b1a1c63c4c97e1fffe017346dc9166b67e118f029ab621b2560ff9ca67cca8c7f85ba84c79030c2b3de218f86db3a90901d5df45c14f26fedfb3dc38e96ac22fe7bd728f0e45bce046d23c570feb141398bb552ef5a0a82be331fea48037b8b5d71f0e332edf93ac3500eb4ddc0decc1a864790c782c76215660dd309791d06bd0af3f98cda4bc4629b1086e879169a77ca787ffffffff26704c110f6580950ed2dab835351cc5593d3d2eb6da01482be4c319133a6c2c00000000fd8f024d4001255044462d312e330a25e2e3cfd30a0a0a312030206f626a0a3c3c2f57696474682032203020522f4865696768742033203020522f547970652034203020522f537562747970652035203020522f46696c7465722036203020522f436f6c6f7253706163652037203020522f4c656e6774682038203020522f42697473506572436f6d706f6e656e7420383e3e0a73747265616d0affd8fffe00245348412d3120697320646561642121212121852fec092339759c39b1a1c63c4c97e1fffe017f46dc93a6b67e013b029aaa1db2560b45ca67d688c7f84b8c4c791fe02b3df614f86db1690901c56b45c1530afedfb76038e972722fe7ad728f0e4904e046c230570fe9d41398abe12ef5bc942be33542a4802d98b5d70f2a332ec37fac3514e74ddc0f2cc1a874cd0c78305a21566461309789606bd0bf3f98cda8044629a14d4001255044462d312e330a25e2e3cfd30a0a0a312030206f626a0a3c3c2f57696474682032203020522f4865696768742033203020522f547970652034203020522f537562747970652035203020522f46696c7465722036203020522f436f6c6f7253706163652037203020522f4c656e6774682038203020522f42697473506572436f6d706f6e656e7420383e3e0a73747265616d0affd8fffe00245348412d3120697320646561642121212121852fec092339759c39b1a1c63c4c97e1fffe017346dc9166b67e118f029ab621b2560ff9ca67cca8c7f85ba84c79030c2b3de218f86db3a90901d5df45c14f26fedfb3dc38e96ac22fe7bd728f0e45bce046d23c570feb141398bb552ef5a0a82be331fea48037b8b5d71f0e332edf93ac3500eb4ddc0decc1a864790c782c76215660dd309791d06bd0af3f98cda4bc4629b1086e879169a77ca787ffffffff167c4f7730dc7592104bd02bcf6dd060576aeb4e2131d66b8f6e5b9aead6e06301000000fd8f024d4001255044462d312e330a25e2e3cfd30a0a0a312030206f626a0a3c3c2f57696474682032203020522f4865696768742033203020522f547970652034203020522f537562747970652035203020522f46696c7465722036203020522f436f6c6f7253706163652037203020522f4c656e6774682038203020522f42697473506572436f6d706f6e656e7420383e3e0a73747265616d0affd8fffe00245348412d3120697320646561642121212121852fec092339759c39b1a1c63c4c97e1fffe017f46dc93a6b67e013b029aaa1db2560b45ca67d688c7f84b8c4c791fe02b3df614f86db1690901c56b45c1530afedfb76038e972722fe7ad728f0e4904e046c230570fe9d41398abe12ef5bc942be33542a4802d98b5d70f2a332ec37fac3514e74ddc0f2cc1a874cd0c78305a21566461309789606bd0bf3f98cda8044629a14d4001255044462d312e330a25e2e3cfd30a0a0a312030206f626a0a3c3c2f57696474682032203020522f4865696768742033203020522f547970652034203020522f537562747970652035203020522f46696c7465722036203020522f436f6c6f7253706163652037203020522f4c656e6774682038203020522f42697473506572436f6d706f6e656e7420383e3e0a73747265616d0affd8fffe00245348412d3120697320646561642121212121852fec092339759c39b1a1c63c4c97e1fffe017346dc9166b67e118f029ab621b2560ff9ca67cca8c7f85ba84c79030c2b3de218f86db3a90901d5df45c14f26fedfb3dc38e96ac22fe7bd728f0e45bce046d23c570feb141398bb552ef5a0a82be331fea48037b8b5d71f0e332edf93ac3500eb4ddc0decc1a864790c782c76215660dd309791d06bd0af3f98cda4bc4629b1086e879169a77ca787ffffffff76ebcc08c3fe67d5d9030f57ec5edd8148069fd94ff06974198ba885c6d7ca7c00000000fd8f024d4001255044462d312e330a25e2e3cfd30a0a0a312030206f626a0a3c3c2f57696474682032203020522f4865696768742033203020522f547970652034203020522f537562747970652035203020522f46696c7465722036203020522f436f6c6f7253706163652037203020522f4c656e6774682038203020522f42697473506572436f6d706f6e656e7420383e3e0a73747265616d0affd8fffe00245348412d3120697320646561642121212121852fec092339759c39b1a1c63c4c97e1fffe017f46dc93a6b67e013b029aaa1db2560b45ca67d688c7f84b8c4c791fe02b3df614f86db1690901c56b45c1530afedfb76038e972722fe7ad728f0e4904e046c230570fe9d41398abe12ef5bc942be33542a4802d98b5d70f2a332ec37fac3514e74ddc0f2cc1a874cd0c78305a21566461309789606bd0bf3f98cda8044629a14d4001255044462d312e330a25e2e3cfd30a0a0a312030206f626a0a3c3c2f57696474682032203020522f4865696768742033203020522f547970652034203020522f537562747970652035203020522f46696c7465722036203020522f436f6c6f7253706163652037203020522f4c656e6774682038203020522f42697473506572436f6d706f6e656e7420383e3e0a73747265616d0affd8fffe00245348412d3120697320646561642121212121852fec092339759c39b1a1c63c4c97e1fffe017346dc9166b67e118f029ab621b2560ff9ca67cca8c7f85ba84c79030c2b3de218f86db3a90901d5df45c14f26fedfb3dc38e96ac22fe7bd728f0e45bce046d23c570feb141398bb552ef5a0a82be331fea48037b8b5d71f0e332edf93ac3500eb4ddc0decc1a864790c782c76215660dd309791d06bd0af3f98cda4bc4629b1086e879169a77ca787ffffffffeeaebcc9229aca9a7e407f6ace9e90c52dc4cedb8764de8c517434d7376bf87d01000000fd8f024d4001255044462d312e330a25e2e3cfd30a0a0a312030206f626a0a3c3c2f57696474682032203020522f4865696768742033203020522f547970652034203020522f537562747970652035203020522f46696c7465722036203020522f436f6c6f7253706163652037203020522f4c656e6774682038203020522f42697473506572436f6d706f6e656e7420383e3e0a73747265616d0affd8fffe00245348412d3120697320646561642121212121852fec092339759c39b1a1c63c4c97e1fffe017f46dc93a6b67e013b029aaa1db2560b45ca67d688c7f84b8c4c791fe02b3df614f86db1690901c56b45c1530afedfb76038e972722fe7ad728f0e4904e046c230570fe9d41398abe12ef5bc942be33542a4802d98b5d70f2a332ec37fac3514e74ddc0f2cc1a874cd0c78305a21566461309789606bd0bf3f98cda8044629a14d4001255044462d312e330a25e2e3cfd30a0a0a312030206f626a0a3c3c2f57696474682032203020522f4865696768742033203020522f547970652034203020522f537562747970652035203020522f46696c7465722036203020522f436f6c6f7253706163652037203020522f4c656e6774682038203020522f42697473506572436f6d706f6e656e7420383e3e0a73747265616d0affd8fffe00245348412d3120697320646561642121212121852fec092339759c39b1a1c63c4c97e1fffe017346dc9166b67e118f029ab621b2560ff9ca67cca8c7f85ba84c79030c2b3de218f86db3a90901d5df45c14f26fedfb3dc38e96ac22fe7bd728f0e45bce046d23c570feb141398bb552ef5a0a82be331fea48037b8b5d71f0e332edf93ac3500eb4ddc0decc1a864790c782c76215660dd309791d06bd0af3f98cda4bc4629b1086e879169a77ca787ffffffff2394eed43fda81f728d2a66f123a30f2d5a9c6707a1662946182fa3fc5168f8a00000000fd8f024d4001255044462d312e330a25e2e3cfd30a0a0a312030206f626a0a3c3c2f57696474682032203020522f4865696768742033203020522f547970652034203020522f537562747970652035203020522f46696c7465722036203020522f436f6c6f7253706163652037203020522f4c656e6774682038203020522f42697473506572436f6d706f6e656e7420383e3e0a73747265616d0affd8fffe00245348412d3120697320646561642121212121852fec092339759c39b1a1c63c4c97e1fffe017f46dc93a6b67e013b029aaa1db2560b45ca67d688c7f84b8c4c791fe02b3df614f86db1690901c56b45c1530afedfb76038e972722fe7ad728f0e4904e046c230570fe9d41398abe12ef5bc942be33542a4802d98b5d70f2a332ec37fac3514e74ddc0f2cc1a874cd0c78305a21566461309789606bd0bf3f98cda8044629a14d4001255044462d312e330a25e2e3cfd30a0a0a312030206f626a0a3c3c2f57696474682032203020522f4865696768742033203020522f547970652034203020522f537562747970652035203020522f46696c7465722036203020522f436f6c6f7253706163652037203020522f4c656e6774682038203020522f42697473506572436f6d706f6e656e7420383e3e0a73747265616d0affd8fffe00245348412d3120697320646561642121212121852fec092339759c39b1a1c63c4c97e1fffe017346dc9166b67e118f029ab621b2560ff9ca67cca8c7f85ba84c79030c2b3de218f86db3a90901d5df45c14f26fedfb3dc38e96ac22fe7bd728f0e45bce046d23c570feb141398bb552ef5a0a82be331fea48037b8b5d71f0e332edf93ac3500eb4ddc0decc1a864790c782c76215660dd309791d06bd0af3f98cda4bc4629b1086e879169a77ca787ffffffffa091bd7645b6c560992ab7448fe419244a7f818dd13a0b4429af1544fce5a30301000000fd8f024d4001255044462d312e330a25e2e3cfd30a0a0a312030206f626a0a3c3c2f57696474682032203020522f4865696768742033203020522f547970652034203020522f537562747970652035203020522f46696c7465722036203020522f436f6c6f7253706163652037203020522f4c656e6774682038203020522f42697473506572436f6d706f6e656e7420383e3e0a73747265616d0affd8fffe00245348412d3120697320646561642121212121852fec092339759c39b1a1c63c4c97e1fffe017f46dc93a6b67e013b029aaa1db2560b45ca67d688c7f84b8c4c791fe02b3df614f86db1690901c56b45c1530afedfb76038e972722fe7ad728f0e4904e046c230570fe9d41398abe12ef5bc942be33542a4802d98b5d70f2a332ec37fac3514e74ddc0f2cc1a874cd0c78305a21566461309789606bd0bf3f98cda8044629a14d4001255044462d312e330a25e2e3cfd30a0a0a312030206f626a0a3c3c2f57696474682032203020522f4865696768742033203020522f547970652034203020522f537562747970652035203020522f46696c7465722036203020522f436f6c6f7253706163652037203020522f4c656e6774682038203020522f42697473506572436f6d706f6e656e7420383e3e0a73747265616d0affd8fffe00245348412d3120697320646561642121212121852fec092339759c39b1a1c63c4c97e1fffe017346dc9166b67e118f029ab621b2560ff9ca67cca8c7f85ba84c79030c2b3de218f86db3a90901d5df45c14f26fedfb3dc38e96ac22fe7bd728f0e45bce046d23c570feb141398bb552ef5a0a82be331fea48037b8b5d71f0e332edf93ac3500eb4ddc0decc1a864790c782c76215660dd309791d06bd0af3f98cda4bc4629b1086e879169a77ca787ffffffff01002ec80e000000001976a914976e57ab0ff46ed93d9649d6bfdf0c86f1853e6a88ac00000000"; + + let raw_transaction = hex_to_bytecode(@raw_transaction_hex); + let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction); + + let prevout_pubkey = "0xa9144266fc6f2c2861d7fe229b279a79803afca7ba3487"; + let prev_out = UTXO { + amount: 100000000, pubkey_script: hex_to_bytecode(@prevout_pubkey), block_height: 257797 + }; + + let utxo_hints = array![prev_out]; + let res = validate_p2sh(@transaction, 0, utxo_hints, 0); + assert!(res.is_ok(), "P2SH failed!:{:?}", res.unwrap_err()); +} diff --git a/packages/tests/src/tests/test_transactions.cairo b/packages/tests/src/tests/test_transactions.cairo index 86c8b08..f9ce9b0 100644 --- a/packages/tests/src/tests/test_transactions.cairo +++ b/packages/tests/src/tests/test_transactions.cairo @@ -121,6 +121,54 @@ fn test_deserialize_first_p2pkh_transaction() { assert_eq!(transaction.locktime, 0, "Lock time is not correct"); } +#[test] +fn test_deserialize_first_p2sh_transaction() { + //https://learnmeabitcoin.com/explorer/tx/a0f1aaa2fb4582c89e0511df0374a5a2833bf95f7314f4a51b55b7b71e90ce0f + let raw_transaction_hex = + "0x01000000014ce7153d92e3b24d9eea31f8cf391c3fb4c39f7742b341b2d36c6367e7546474000000006c493046022100c554360535b2ad3b1cb1b966a87807f7a7e45fa485348d662a1e7413dced8471022100d6bcfc4385b7ac41ca3968a73c4a28e38879192c3db1286b36e59ec9fce52bbd012103c96e3a9e63986801269d5f278246ed7cdc2d392595d0a25b102e04598f4b4fa9ffffffff02cb871a00000000001976a914c02ebae82202119f23f330781ff26b303edb7dbd88ac809698000000000017a914748284390f9e263a4b766a75d0633c50426eb8758700000000"; + let raw_transaction = hex_to_bytecode(@raw_transaction_hex); + let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction); + assert_eq!(transaction.version, 1, "Version is not correct"); + assert_eq!(transaction.transaction_inputs.len(), 1, "Transaction inputs length is not correct"); + let input0 = transaction.transaction_inputs[0]; + + let expected_txid_hex = "0x4ce7153d92e3b24d9eea31f8cf391c3fb4c39f7742b341b2d36c6367e7546474"; + let expected_txid = hex_to_bytecode(@expected_txid_hex); + + let expected_sig_script_hex = + "0x493046022100c554360535b2ad3b1cb1b966a87807f7a7e45fa485348d662a1e7413dced8471022100d6bcfc4385b7ac41ca3968a73c4a28e38879192c3db1286b36e59ec9fce52bbd012103c96e3a9e63986801269d5f278246ed7cdc2d392595d0a25b102e04598f4b4fa9"; + let expected_sig_script = hex_to_bytecode(@expected_sig_script_hex); + + assert_eq!( + input0.previous_outpoint.txid, + @u256_from_byte_array_with_offset(@expected_txid, 0, 32), + "Outpoint txid on input 2 is not correct" + ); + assert_eq!( + input0.signature_script, @expected_sig_script, "Script sig on input 1 is not correct" + ); + + let output0 = transaction.transaction_outputs[0]; + let expected_pk_script_hex_0 = "0x76a914c02ebae82202119f23f330781ff26b303edb7dbd88ac"; + let expected_pk_script_0 = hex_to_bytecode(@expected_pk_script_hex_0); + + assert_eq!(output0.value, @1738699, "Output 1 value is not correct"); + assert_eq!( + output0.publickey_script, @expected_pk_script_0, "Output 1 pk_script is not correct" + ); + + let output1 = transaction.transaction_outputs[1]; + let expected_pk_script_hex_1 = "0xa914748284390f9e263a4b766a75d0633c50426eb87587"; + let expected_pk_script_1 = hex_to_bytecode(@expected_pk_script_hex_1); + + assert_eq!(output1.value, @10000000, "Output 2 value is not correct"); + assert_eq!( + output1.publickey_script, @expected_pk_script_1, "Output 2 pk_script is not correct" + ); + + assert_eq!(transaction.locktime, 0, "Lock time is not correct"); +} + #[test] fn test_deserialize_p2wsh_transaction() { // https://learnmeabitcoin.com/explorer/tx/64f427122f7951687aea608b5474509a30616d4e5773a83bc1ed8b8271ad1991 diff --git a/packages/tests/src/utils.cairo b/packages/tests/src/utils.cairo index ff58dec..ecc9c8d 100644 --- a/packages/tests/src/utils.cairo +++ b/packages/tests/src/utils.cairo @@ -249,3 +249,19 @@ pub fn mock_transaction_legacy_sequence_v2( let outputs = ArrayTrait::::new(); return mock_transaction_with(2, inputs, outputs, 0); } + +//find last push_data opcode in a bytearray +pub fn find_last_index(sig: ByteArray) -> u32 { + let mut i = sig.len() - 1; + loop { + if 1 < sig[i] && sig[i] < 75 { + break; + } + i -= 1; + + if (i == 0) { + break; + } + }; + return i + 1; +} diff --git a/packages/tests/src/validate.cairo b/packages/tests/src/validate.cairo index 05f7827..ead62ef 100644 --- a/packages/tests/src/validate.cairo +++ b/packages/tests/src/validate.cairo @@ -3,6 +3,11 @@ use shinigami_engine::hash_cache::HashCacheImpl; use shinigami_engine::transaction::EngineTransaction; use shinigami_engine::opcodes::Opcode; use crate::utxo::UTXO; +use crate::utils::find_last_index; +use shinigami_utils::hash::sha256_byte_array; +use shinigami_utils::bytecode::bytecode_to_hex; +use shinigami_utils::bytecode::hex_to_bytecode; +use shinigami_utils::byte_array::sub_byte_array; // TODO: Move validate coinbase here @@ -143,3 +148,65 @@ pub fn validate_p2ms( Result::Ok(()) } } + + +pub fn validate_p2sh( + tx: @EngineTransaction, flags: u32, utxo_hints: Array, indx: u32 +) -> Result<(), felt252> { + if tx.transaction_inputs.len() == 0 { + return Result::Err('P2SH: No inputs'); + } + + let signature_script = bytecode_to_hex(tx.transaction_inputs[indx].signature_script); + let scriptSig_bytes = hex_to_bytecode(@signature_script); + + let mut redeem_Script_start_index = 0; + let mut redeem_script_size = 0; + if scriptSig_bytes[0] == 0 || scriptSig_bytes[0] == 1 || scriptSig_bytes[0] == 2 { + //OP_0 OP_PushData OP_PushData Standard locking scripts + redeem_Script_start_index = (2 + scriptSig_bytes[1] + 1).into(); + redeem_script_size = (scriptSig_bytes.len()) - redeem_Script_start_index; + } else { + // non-standard locking script containing a mathematical puzzle + redeem_Script_start_index = find_last_index(scriptSig_bytes.clone()); + redeem_script_size = (scriptSig_bytes.len()) - redeem_Script_start_index; + } + + let redeem_script = sub_byte_array( + @scriptSig_bytes, ref redeem_Script_start_index, redeem_script_size + ); + if redeem_script.len() == 0 { + return Result::Err('P2SH: Redeem Script size = 0'); + } + if redeem_script.len() > 520 { + return Result::Err('P2SH: Redeem Script size > 520'); + } + + let hashed_redeem_script: ByteArray = ripemd160::ripemd160_hash( + @sha256_byte_array(@redeem_script) + ) + .into(); + + let script_pubkey = utxo_hints[0].pubkey_script; + let mut script_hash_start_index = 2; + let script_hash: ByteArray = sub_byte_array(script_pubkey, ref script_hash_start_index, 20); + + if hashed_redeem_script != script_hash { + return Result::Err('P2SH: Signature mismatch'); + } + + let hash_cache = HashCacheImpl::new(tx); + let mut engine = EngineImpl::new( + script_pubkey, tx, indx, flags, *utxo_hints[0].amount, @hash_cache + ) + .unwrap(); + + let res = engine.execute(); + + if res.is_err() { + let err = res.unwrap_err(); + Result::Err(err) + } else { + Result::Ok(()) + } +}