diff --git a/README.md b/README.md index 35143599..c20b3339 100644 --- a/README.md +++ b/README.md @@ -51,15 +51,15 @@ This will run the test-suite for all opcodes, integration, and testing Scripts. ## Supported Opcodes -104/107 opcodes supported (97.20%). +106/107 opcodes supported (99.07%). ```mermaid %%{init: {"pie": {"textPosition": 0.75}, "themeVariables": {"pieOuterStrokeWidth": "5px"}} }%% pie showData title Opcode Implementation Status - "Implemented" : 89 + "Implemented" : 91 "Disabled" : 15 - "Not Implemented" : 3 + "Not Implemented" : 1 ``` | Opcode | Hex | Supported | Description | @@ -164,8 +164,8 @@ pie showData | OP_CODESEPARATOR | 0xab | ✅ | All of the signature checking words will only match signatures to the data after the most recently-executed OP_CODESEPARATOR. | | OP_CHECKSIG | 0xac | ✅ | The entire transaction's outputs, inputs, and script are hashed. The signature used by OP_CHECKSIG must be a valid signature for this hash and public key. If it is, 1 is returned, 0 otherwise. | | OP_CHECKSIGVERIFY | 0xad | ✅ | Same as OP_CHECKSIG, but OP_VERIFY is executed afterward. | -| OP_CHECKMULTISIG | 0xae | | Compares the first signature against each public key until it finds an ECDSA match. Starting with the subsequent public key, it compares the second signature against each remaining public key until it finds an ECDSA match. The process is repeated until all signatures have been checked or not enough public keys remain to produce a successful result. All signatures need to match a public key. If all signatures are valid, 1 is returned, 0 otherwise. Due to a bug, one extra unused value is removed from the stack. | -| OP_CHECKMULTISIGVERIFY | 0xaf | | Same as OP_CHECKMULTISIG, but OP_VERIFY is executed afterward. | +| OP_CHECKMULTISIG | 0xae | ✅ | Compares the first signature against each public key until it finds an ECDSA match. Starting with the subsequent public key, it compares the second signature against each remaining public key until it finds an ECDSA match. The process is repeated until all signatures have been checked or not enough public keys remain to produce a successful result. All signatures need to match a public key. If all signatures are valid, 1 is returned, 0 otherwise. Due to a bug, one extra unused value is removed from the stack. | +| OP_CHECKMULTISIGVERIFY | 0xaf | ✅ | Same as OP_CHECKMULTISIG, but OP_VERIFY is executed afterward. | | OP_NOP1 | 0xb0 | ✅ | The word is ignored. Does not mark transaction as invalid. | | OP_CHECKLOCKTIMEVERIFY | 0xb1 | ✅ | Marks transaction as invalid if the top stack item is greater than the transaction's nLockTime field, otherwise script evaluation continues as though an OP_NOP was executed. | | OP_CHECKSEQUENCEVERIFY | 0xb2 | ✅ | Marks transaction as invalid if the relative lock time of the input is not equal to or longer than the value of the top stack item. | diff --git a/Scarb.lock b/Scarb.lock index 74eaf8d8..5f7fbc37 100644 --- a/Scarb.lock +++ b/Scarb.lock @@ -4,12 +4,12 @@ version = 1 [[package]] name = "ripemd160" version = "0.1.0" -source = "git+https://github.com/j1mbo64/ripemd160_cairo.git#bebe67c235b12e0a3668f49c78423d4eff6d7131" +source = "git+https://github.com/j1mbo64/ripemd160_cairo.git#cdc5ab58b0acc64db87e0b03851fb18213977dc8" [[package]] name = "sha1" version = "0.1.0" -source = "git+https://github.com/j1mbo64/sha1_cairo.git#a2fbea0d47adeb7c9c60d6fb97580e4084834cf5" +source = "git+https://github.com/j1mbo64/sha1_cairo.git#280b4c64ae457fdc4bd7cd807efd17e8dced654e" [[package]] name = "shinigami" diff --git a/src/compiler.cairo b/src/compiler.cairo index 858fc78b..39716bc0 100644 --- a/src/compiler.cairo +++ b/src/compiler.cairo @@ -280,6 +280,8 @@ pub impl CompilerImpl of CompilerTrait { compiler.add_opcode('OP_HASH256', Opcode::OP_HASH256); compiler.add_opcode('OP_CHECKSIG', Opcode::OP_CHECKSIG); compiler.add_opcode('OP_CHECKSIGVERIFY', Opcode::OP_CHECKSIGVERIFY); + compiler.add_opcode('OP_CHECKMULTISIG', Opcode::OP_CHECKMULTISIG); + compiler.add_opcode('OP_CHECKMULTISIGVERIFY', Opcode::OP_CHECKMULTISIGVERIFY); compiler.add_opcode('OP_CODESEPARATOR', Opcode::OP_CODESEPARATOR); compiler.add_opcode('OP_CHECKLOCKTIMEVERIFY', Opcode::OP_CHECKLOCKTIMEVERIFY); compiler.add_opcode('OP_CLTV', Opcode::OP_CHECKLOCKTIMEVERIFY); diff --git a/src/errors.cairo b/src/errors.cairo index a75cee8d..f0f1081c 100644 --- a/src/errors.cairo +++ b/src/errors.cairo @@ -12,6 +12,7 @@ pub mod Error { pub const OPCODE_DISABLED: felt252 = 'Opcode is disabled'; pub const SCRIPT_DISCOURAGE_UPGRADABLE_NOPS: felt252 = 'Upgradable NOPs are discouraged'; pub const UNSATISFIED_LOCKTIME: felt252 = 'Unsatisfied locktime'; + pub const SCRIPT_STRICT_MULTISIG: felt252 = 'OP_CHECKMULTISIG invalid dummy'; pub const FINALIZED_TX_CLTV: felt252 = 'Finalized tx in OP_CLTV'; pub const INVALID_TX_VERSION: felt252 = 'Invalid transaction version'; pub const SCRIPT_INVALID: felt252 = 'Invalid script data'; diff --git a/src/lib.cairo b/src/lib.cairo index 133195ac..bd20499d 100644 --- a/src/lib.cairo +++ b/src/lib.cairo @@ -39,7 +39,7 @@ pub mod scriptnum { #[cfg(test)] mod test_scriptnum; } - pub(crate) use scriptnum::ScriptNum; + pub use scriptnum::ScriptNum; } pub mod scriptflags; pub mod signature { @@ -47,7 +47,7 @@ pub mod signature { pub mod sighash; pub mod constants; pub mod utils; - pub(crate) use signature::{BaseSigVerifier, BaseSigVerifierTrait}; + pub use signature::{BaseSigVerifier, BaseSigVerifierTrait}; } pub mod transaction; #[cfg(test)] diff --git a/src/opcodes/crypto.cairo b/src/opcodes/crypto.cairo index fa27844d..dcdb542b 100644 --- a/src/opcodes/crypto.cairo +++ b/src/opcodes/crypto.cairo @@ -1,8 +1,16 @@ +use crate::engine::{Engine, EngineTrait}; +use crate::stack::ScriptStackTrait; +use crate::scriptflags::ScriptFlags; +use crate::signature::signature; +use crate::signature::sighash; +use crate::signature::signature::BaseSigVerifierTrait; +use starknet::secp256_trait::{is_valid_signature}; use core::sha256::compute_sha256_byte_array; -use crate::engine::Engine; use crate::opcodes::utils; -use crate::signature::BaseSigVerifierTrait; -use crate::stack::ScriptStackTrait; +use crate::scriptnum::ScriptNum; +use crate::errors::Error; + +const MAX_KEYS_PER_MULTISIG: i64 = 20; pub fn opcode_sha256(ref engine: Engine) -> Result<(), felt252> { let arr = @engine.dstack.pop_byte_array()?; @@ -91,6 +99,114 @@ pub fn opcode_checksig(ref engine: Engine) -> Result<(), felt252> { return Result::Ok(()); } +pub fn opcode_checkmultisig(ref engine: Engine) -> Result<(), felt252> { + // TODO Error on taproot exec + // TODO Numops + + // Get number of public keys and construct array + let num_keys = engine.dstack.pop_int()?; + let mut num_pub_keys: i64 = ScriptNum::to_int32(num_keys).into(); + if num_pub_keys < 0 { + return Result::Err('check multisig: num pk < 0'); + } + if num_pub_keys > MAX_KEYS_PER_MULTISIG { + return Result::Err('check multisig: num pk > max'); + } + let mut pub_keys = ArrayTrait::::new(); + let mut i: i64 = 0; + let mut err: felt252 = 0; + while i < num_pub_keys { + match engine.dstack.pop_byte_array() { + Result::Ok(pk) => pub_keys.append(pk), + Result::Err(e) => err = e + }; + i += 1; + }; + if err != 0 { + return Result::Err(err); + } + + // Get number of required sigs and construct array + let num_sig_base = engine.dstack.pop_int()?; + let mut num_sigs: i64 = ScriptNum::to_int32(num_sig_base).into(); + if num_sigs < 0 { + return Result::Err('check multisig: num sigs < 0'); + } + if num_sigs > num_pub_keys { + return Result::Err('check multisig: num sigs > pk'); + } + let mut sigs = ArrayTrait::::new(); + i = 0; + err = 0; + while i < num_sigs { + match engine.dstack.pop_byte_array() { + Result::Ok(s) => sigs.append(s), + Result::Err(e) => err = e + }; + i += 1; + }; + if err != 0 { + return Result::Err(err); + } + + // Historical bug + let dummy = engine.dstack.pop_byte_array()?; + + if engine.has_flag(ScriptFlags::ScriptStrictMultiSig) && dummy.len() != 0 { + return Result::Err(Error::SCRIPT_STRICT_MULTISIG); + } + + let mut script = engine.sub_script(); + + // TODO: add witness context inside engine to check if witness is active + let mut s: u32 = 0; + while s < sigs.len() { + script = signature::remove_signature(script, sigs.at(s)); + s += 1; + }; + + let mut success = true; + num_pub_keys += 1; // Offset due to decrementing it in the loop + let mut pub_key_idx: i64 = -1; + let mut sig_idx: i64 = 0; + + while num_sigs > 0 { + pub_key_idx += 1; + num_pub_keys -= 1; + if num_sigs > num_pub_keys { + success = false; + break; + } + + let sig = sigs.at(sig_idx.try_into().unwrap()); + let pub_key = pub_keys.at(pub_key_idx.try_into().unwrap()); + if sig.len() == 0 { + continue; + } + + let res = signature::parse_base_sig_and_pk(ref engine, pub_key, sig); + if res.is_err() { + success = false; + err = res.unwrap_err(); + break; + } + let (parsed_pub_key, parsed_sig, hash_type) = res.unwrap(); + let sig_hash: u256 = sighash::calc_signature_hash( + @script, hash_type, ref engine.transaction, engine.tx_idx + ); + if is_valid_signature(sig_hash, parsed_sig.r, parsed_sig.s, parsed_pub_key) { + sig_idx += 1; + num_sigs -= 1; + } + }; + if err != 0 { + return Result::Err(err); + } + + engine.dstack.push_bool(success); + Result::Ok(()) +} + pub fn opcode_codeseparator(ref engine: Engine) -> Result<(), felt252> { engine.last_code_sep = engine.opcode_idx; @@ -110,6 +226,12 @@ pub fn opcode_checksigverify(ref engine: Engine) -> Result<(), felt252> { return Result::Ok(()); } +pub fn opcode_checkmultisigverify(ref engine: Engine) -> Result<(), felt252> { + opcode_checkmultisig(ref engine)?; + utils::abstract_verify(ref engine)?; + return Result::Ok(()); +} + pub fn opcode_sha1(ref engine: Engine) -> Result<(), felt252> { let m = engine.dstack.pop_byte_array()?; let h: ByteArray = sha1::sha1_hash(@m).into(); diff --git a/src/opcodes/opcodes.cairo b/src/opcodes/opcodes.cairo index c84cba77..55f68ace 100644 --- a/src/opcodes/opcodes.cairo +++ b/src/opcodes/opcodes.cairo @@ -174,6 +174,8 @@ pub mod Opcode { pub const OP_CODESEPARATOR: u8 = 171; pub const OP_CHECKSIG: u8 = 172; pub const OP_CHECKSIGVERIFY: u8 = 173; + pub const OP_CHECKMULTISIG: u8 = 174; + pub const OP_CHECKMULTISIGVERIFY: u8 = 175; pub const OP_NOP1: u8 = 176; pub const OP_CHECKLOCKTIMEVERIFY: u8 = 177; pub const OP_CHECKSEQUENCEVERIFY: u8 = 178; @@ -366,8 +368,8 @@ pub mod Opcode { 171 => crypto::opcode_codeseparator(ref engine), 172 => crypto::opcode_checksig(ref engine), 173 => crypto::opcode_checksigverify(ref engine), - 174 => utils::not_implemented(ref engine), - 175 => utils::not_implemented(ref engine), + 174 => crypto::opcode_checkmultisig(ref engine), + 175 => crypto::opcode_checkmultisigverify(ref engine), 176 => flow::opcode_nop(), 177 => locktime::opcode_checklocktimeverify(ref engine), 178 => locktime::opcode_checksequenceverify(ref engine), diff --git a/src/opcodes/tests/test_crypto.cairo b/src/opcodes/tests/test_crypto.cairo index 97998d28..e10b7b18 100644 --- a/src/opcodes/tests/test_crypto.cairo +++ b/src/opcodes/tests/test_crypto.cairo @@ -1,7 +1,8 @@ -use crate::errors::Error; use crate::opcodes::tests::utils; -use crate::scriptnum::ScriptNum; use crate::utils::hex_to_bytecode; +use crate::errors::Error; +use crate::scriptnum::ScriptNum; +use crate::scriptflags::ScriptFlags; fn test_opcode_sha256_1() { let program = "OP_1 OP_SHA256"; @@ -256,7 +257,7 @@ fn test_op_checksig_valid() { "OP_DATA_71 0x3044022008f4f37e2d8f74e18c1b8fde2374d5f28402fb8ab7fd1cc5b786aa40851a70cb02201f40afd1627798ee8529095ca4b205498032315240ac322c9d8ff0f205a93a5801 OP_DATA_33 0x024aeaf55040fa16de37303d13ca1dde85f4ca9baa36e2963a27a1c0c1165fe2b1"; let script_pubkey = "OP_DUP OP_HASH160 OP_DATA_20 0x4299ff317fcd12ef19047df66d72454691797bfc OP_EQUALVERIFY OP_CHECKSIG"; - let mut transaction = utils::mock_transaction(script_sig); + let mut transaction = utils::mock_transaction_legacy_p2pkh(script_sig); let mut engine = utils::test_compile_and_run_with_tx(script_pubkey, transaction); utils::check_dstack_size(ref engine, 1); let expected_stack = array![ScriptNum::wrap(1)]; @@ -269,7 +270,7 @@ fn test_op_checksig_wrong_signature() { "OP_DATA_71 0x3044022008f4f37e2d8f74f18c1b8fde2374d5f28402fb8ab7fd1cc5b786aa40851a70cb02201f40afd1627798ee8529095ca4b205498032315240ac322c9d8ff0f205a93a5801 OP_DATA_33 0x024aeaf55040fa16de37303d13ca1dde85f4ca9baa36e2963a27a1c0c1165fe2b1"; let script_pubkey = "OP_DUP OP_HASH160 OP_DATA_20 0x4299ff317fcd12ef19047df66d72454691797bfc OP_EQUALVERIFY OP_CHECKSIG"; - let mut transaction = utils::mock_transaction(script_sig); + let mut transaction = utils::mock_transaction_legacy_p2pkh(script_sig); let mut engine = utils::test_compile_and_run_with_tx_err( script_pubkey, transaction, Error::SCRIPT_FAILED ); @@ -284,7 +285,7 @@ fn test_op_checksig_invalid_hash_type() { "OP_DATA_71 0x3044022008f4f37e2d8f74e18c1b8fde2374d5f28402fb8ab7fd1cc5b786aa40851a70cb02201f40afd1627798ee8529095ca4b205498032315240ac322c9d8ff0f205a93a5807 OP_DATA_33 0x024aeaf55040fa16de37303d13ca1dde85f4ca9baa36e2963a27a1c0c1165fe2b1"; let script_pubkey = "OP_DUP OP_HASH160 OP_DATA_20 0x4299ff317fcd12ef19047df66d72454691797bfc OP_EQUALVERIFY OP_CHECKSIG"; - let mut transaction = utils::mock_transaction(script_sig); + let mut transaction = utils::mock_transaction_legacy_p2pkh(script_sig); let mut engine = utils::test_compile_and_run_with_tx_err( script_pubkey, transaction, Error::SCRIPT_FAILED ); @@ -299,7 +300,7 @@ fn test_op_checksig_empty_signature() { "OP_0 OP_DATA_33 0x024aeaf55040fa16de37303d13ca1dde85f4ca9baa36e2963a27a1c0c1165fe2b1"; let script_pubkey = "OP_DUP OP_HASH160 OP_DATA_20 0x4299ff317fcd12ef19047df66d72454691797bfc OP_EQUALVERIFY OP_CHECKSIG"; - let mut transaction = utils::mock_transaction(script_sig); + let mut transaction = utils::mock_transaction_legacy_p2pkh(script_sig); let mut engine = utils::test_compile_and_run_with_tx_err( script_pubkey, transaction, Error::SCRIPT_FAILED ); @@ -314,7 +315,7 @@ fn test_op_checksig_too_short_signature() { "OP_1 OP_DATA_33 0x024aeaf55040fa16de37303d13ca1dde85f4ca9baa36e2963a27a1c0c1165fe2b1"; let script_pubkey = "OP_DUP OP_HASH160 OP_DATA_20 0x4299ff317fcd12ef19047df66d72454691797bfc OP_EQUALVERIFY OP_CHECKSIG"; - let mut transaction = utils::mock_transaction(script_sig); + let mut transaction = utils::mock_transaction_legacy_p2pkh(script_sig); let mut engine = utils::test_compile_and_run_with_tx_err( script_pubkey, transaction, 'invalid sig fmt: too short' ); @@ -380,3 +381,85 @@ fn test_op_sha1_14_double_sha1() { let expected_stack = array![hex_to_bytecode(@"0xC0BDFDD54F44A37833C74DA7613B87A5BA9A8452")]; utils::check_expected_dstack(ref engine, expected_stack.span()); } + +#[test] +fn test_op_checkmultisig_valid() { + let script_sig = + "OP_0 OP_DATA_72 0x3045022100AF204EF91B8DBA5884DF50F87219CCEF22014C21DD05AA44470D4ED800B7F6E40220428FE058684DB1BB2BFB6061BFF67048592C574EFFC217F0D150DAEDCF36787601 OP_DATA_72 0x3045022100E8547AA2C2A2761A5A28806D3AE0D1BBF0AEFF782F9081DFEA67B86CACB321340220771A166929469C34959DAF726A2AC0C253F9AFF391E58A3C7CB46D8B7E0FDC4801"; + let script_pubkey = + "OP_2 OP_DATA_65 0x04D81FD577272BBE73308C93009EEC5DC9FC319FC1EE2E7066E17220A5D47A18314578BE2FAEA34B9F1F8CA078F8621ACD4BC22897B03DAA422B9BF56646B342A2 OP_DATA_65 0x04EC3AFFF0B2B66E8152E9018FE3BE3FC92B30BF886B3487A525997D00FD9DA2D012DCE5D5275854ADC3106572A5D1E12D4211B228429F5A7B2F7BA92EB0475BB1 OP_DATA_65 0x04B49B496684B02855BC32F5DAEFA2E2E406DB4418F3B86BCA5195600951C7D918CDBE5E6D3736EC2ABF2DD7610995C3086976B2C0C7B4E459D10B34A316D5A5E7 OP_3 OP_CHECKMULTISIG"; + let mut transaction = utils::mock_transaction_legacy_p2ms(script_sig); + let mut engine = utils::test_compile_and_run_with_tx(script_pubkey, transaction); + utils::check_dstack_size(ref engine, 1); + let expected_stack = array![ScriptNum::wrap(1)]; + utils::check_expected_dstack(ref engine, expected_stack.span()); +} + +#[test] +fn test_op_checkmultisig_wrong_signature() { + let script_sig = + "OP_0 OP_DATA_72 0x3045022100AF204EF91B8DBA5884DF50F87229CCEF22014C21DD05AA44470D4ED800B7F6E40220428FE058684DB1BB2BFB6061BFF67048592C574EFFC217F0D150DAEDCF36787601 OP_DATA_72 0x3045022100E8547AA2C2A2761A5A28806D3AE0D1BBF0AEFF782F9081DFEA67B86CACB321340220771A166929469C34959DAF726A2AC0C253F9AFF391E58A3C7CB46D8B7E0FDC4801"; + let script_pubkey = + "OP_2 OP_DATA_65 0x04D81FD577272BBE73308C93009EEC5DC9FC319FC1EE2E7066E17220A5D47A18314578BE2FAEA34B9F1F8CA078F8621ACD4BC22897B03DAA422B9BF56646B342A2 OP_DATA_65 0x04EC3AFFF0B2B66E8152E9018FE3BE3FC92B30BF886B3487A525997D00FD9DA2D012DCE5D5275854ADC3106572A5D1E12D4211B228429F5A7B2F7BA92EB0475BB1 OP_DATA_65 0x04B49B496684B02855BC32F5DAEFA2E2E406DB4418F3B86BCA5195600951C7D918CDBE5E6D3736EC2ABF2DD7610995C3086976B2C0C7B4E459D10B34A316D5A5E7 OP_3 OP_CHECKMULTISIG"; + let mut transaction = utils::mock_transaction_legacy_p2ms(script_sig); + let mut engine = utils::test_compile_and_run_with_tx_err( + script_pubkey, transaction, Error::SCRIPT_FAILED + ); + utils::check_dstack_size(ref engine, 1); + let expected_stack = array![ScriptNum::wrap(0)]; + utils::check_expected_dstack(ref engine, expected_stack.span()); +} + +#[test] +fn test_op_checkmultisig_not_enough_sig() { + let script_sig = + "OP_0 OP_DATA_72 0x3045022100E8547AA2C2A2761A5A28806D3AE0D1BBF0AEFF782F9081DFEA67B86CACB321340220771A166929469C34959DAF726A2AC0C253F9AFF391E58A3C7CB46D8B7E0FDC4801"; + let script_pubkey = + "OP_2 OP_DATA_65 0x04D81FD577272BBE73308C93009EEC5DC9FC319FC1EE2E7066E17220A5D47A18314578BE2FAEA34B9F1F8CA078F8621ACD4BC22897B03DAA422B9BF56646B342A2 OP_DATA_65 0x04EC3AFFF0B2B66E8152E9018FE3BE3FC92B30BF886B3487A525997D00FD9DA2D012DCE5D5275854ADC3106572A5D1E12D4211B228429F5A7B2F7BA92EB0475BB1 OP_DATA_65 0x04B49B496684B02855BC32F5DAEFA2E2E406DB4418F3B86BCA5195600951C7D918CDBE5E6D3736EC2ABF2DD7610995C3086976B2C0C7B4E459D10B34A316D5A5E7 OP_3 OP_CHECKMULTISIG"; + let mut transaction = utils::mock_transaction_legacy_p2ms(script_sig); + utils::test_compile_and_run_with_tx_err(script_pubkey, transaction, Error::STACK_UNDERFLOW); +} + +// Good signatures but bad order. Signatures must be in the same order as public keys. +#[test] +fn test_op_checkmultisig_bad_order() { + let script_sig = + "OP_0 OP_DATA_72 0x3045022100E8547AA2C2A2761A5A28806D3AE0D1BBF0AEFF782F9081DFEA67B86CACB321340220771A166929469C34959DAF726A2AC0C253F9AFF391E58A3C7CB46D8B7E0FDC4801 OP_DATA_72 0x3045022100AF204EF91B8DBA5884DF50F87219CCEF22014C21DD05AA44470D4ED800B7F6E40220428FE058684DB1BB2BFB6061BFF67048592C574EFFC217F0D150DAEDCF36787601"; + let script_pubkey = + "OP_2 OP_DATA_65 0x04D81FD577272BBE73308C93009EEC5DC9FC319FC1EE2E7066E17220A5D47A18314578BE2FAEA34B9F1F8CA078F8621ACD4BC22897B03DAA422B9BF56646B342A2 OP_DATA_65 0x04EC3AFFF0B2B66E8152E9018FE3BE3FC92B30BF886B3487A525997D00FD9DA2D012DCE5D5275854ADC3106572A5D1E12D4211B228429F5A7B2F7BA92EB0475BB1 OP_DATA_65 0x04B49B496684B02855BC32F5DAEFA2E2E406DB4418F3B86BCA5195600951C7D918CDBE5E6D3736EC2ABF2DD7610995C3086976B2C0C7B4E459D10B34A316D5A5E7 OP_3 OP_CHECKMULTISIG"; + let mut transaction = utils::mock_transaction_legacy_p2ms(script_sig); + let mut engine = utils::test_compile_and_run_with_tx_err( + script_pubkey, transaction, Error::SCRIPT_FAILED + ); + utils::check_dstack_size(ref engine, 1); + let expected_stack = array![ScriptNum::wrap(0)]; + utils::check_expected_dstack(ref engine, expected_stack.span()); +} + +// Test fail if dummy (OP_CHECKMULTISIG bug) is not present +#[test] +fn test_op_checkmultisig_miss_dummy() { + let script_sig = + "OP_DATA_72 0x3045022100AF204EF91B8DBA5884DF50F87219CCEF22014C21DD05AA44470D4ED800B7F6E40220428FE058684DB1BB2BFB6061BFF67048592C574EFFC217F0D150DAEDCF36787601 OP_DATA_72 0x3045022100E8547AA2C2A2761A5A28806D3AE0D1BBF0AEFF782F9081DFEA67B86CACB321340220771A166929469C34959DAF726A2AC0C253F9AFF391E58A3C7CB46D8B7E0FDC4801"; + let script_pubkey = + "OP_2 OP_DATA_65 0x04D81FD577272BBE73308C93009EEC5DC9FC319FC1EE2E7066E17220A5D47A18314578BE2FAEA34B9F1F8CA078F8621ACD4BC22897B03DAA422B9BF56646B342A2 OP_DATA_65 0x04EC3AFFF0B2B66E8152E9018FE3BE3FC92B30BF886B3487A525997D00FD9DA2D012DCE5D5275854ADC3106572A5D1E12D4211B228429F5A7B2F7BA92EB0475BB1 OP_DATA_65 0x04B49B496684B02855BC32F5DAEFA2E2E406DB4418F3B86BCA5195600951C7D918CDBE5E6D3736EC2ABF2DD7610995C3086976B2C0C7B4E459D10B34A316D5A5E7 OP_3 OP_CHECKMULTISIG"; + let mut transaction = utils::mock_transaction_legacy_p2ms(script_sig); + utils::test_compile_and_run_with_tx_err(script_pubkey, transaction, Error::STACK_UNDERFLOW); +} + +// Test fail if dummy is not an empty ByteArray with the flag 'ScriptScriptMultiSig' +#[test] +fn test_op_checkmultisig_dummy_not_zero() { + let script_sig = + "OP_1 OP_DATA_72 0x3045022100AF204EF91B8DBA5884DF50F87219CCEF22014C21DD05AA44470D4ED800B7F6E40220428FE058684DB1BB2BFB6061BFF67048592C574EFFC217F0D150DAEDCF36787601 OP_DATA_72 0x3045022100E8547AA2C2A2761A5A28806D3AE0D1BBF0AEFF782F9081DFEA67B86CACB321340220771A166929469C34959DAF726A2AC0C253F9AFF391E58A3C7CB46D8B7E0FDC4801"; + let script_pubkey = + "OP_2 OP_DATA_65 0x04D81FD577272BBE73308C93009EEC5DC9FC319FC1EE2E7066E17220A5D47A18314578BE2FAEA34B9F1F8CA078F8621ACD4BC22897B03DAA422B9BF56646B342A2 OP_DATA_65 0x04EC3AFFF0B2B66E8152E9018FE3BE3FC92B30BF886B3487A525997D00FD9DA2D012DCE5D5275854ADC3106572A5D1E12D4211B228429F5A7B2F7BA92EB0475BB1 OP_DATA_65 0x04B49B496684B02855BC32F5DAEFA2E2E406DB4418F3B86BCA5195600951C7D918CDBE5E6D3736EC2ABF2DD7610995C3086976B2C0C7B4E459D10B34A316D5A5E7 OP_3 OP_CHECKMULTISIG"; + let mut transaction = utils::mock_transaction_legacy_p2ms(script_sig); + let mut engine = utils::test_compile_and_run_with_tx_flags_err( + script_pubkey, + transaction, + ScriptFlags::ScriptStrictMultiSig.into(), + Error::SCRIPT_STRICT_MULTISIG + ); + utils::check_dstack_size(ref engine, 0); +} diff --git a/src/opcodes/tests/test_locktime.cairo b/src/opcodes/tests/test_locktime.cairo index 66bfb759..7b296c65 100644 --- a/src/opcodes/tests/test_locktime.cairo +++ b/src/opcodes/tests/test_locktime.cairo @@ -6,8 +6,7 @@ use crate::scriptflags::ScriptFlags; fn test_opcode_checklocktime() { let mut program = "OP_DATA_4 0x8036BE26 OP_CHECKLOCKTIMEVERIFY"; // 0x8036BE26 == 650000000 in ScriptNum - let mut tx = utils::mock_transaction_locktime(""); - tx.locktime = 700000000; + let mut tx = utils::mock_transaction_legacy_locktime("", 700000000); let flags: u32 = ScriptFlags::ScriptVerifyCheckLockTimeVerify.into(); let mut engine = utils::test_compile_and_run_with_tx_flags(program, tx, flags); @@ -18,8 +17,7 @@ fn test_opcode_checklocktime() { fn test_opcode_checklocktime_unsatisfied_fail() { let mut program = "OP_DATA_4 0x8036BE26 OP_CHECKLOCKTIMEVERIFY"; // 0x8036BE26 == 650000000 in ScriptNum - let mut tx = utils::mock_transaction_locktime(""); - tx.locktime = 600000000; + let mut tx = utils::mock_transaction_legacy_locktime("", 600000000); let flags: u32 = ScriptFlags::ScriptVerifyCheckLockTimeVerify.into(); let mut engine = utils::test_compile_and_run_with_tx_flags_err( @@ -32,8 +30,7 @@ fn test_opcode_checklocktime_unsatisfied_fail() { fn test_opcode_checklocktime_block() { let program = "OP_16 OP_CHECKLOCKTIMEVERIFY"; - let mut tx = utils::mock_transaction_locktime(""); - tx.locktime = 20; + let mut tx = utils::mock_transaction_legacy_locktime("", 20); let flags: u32 = ScriptFlags::ScriptVerifyCheckLockTimeVerify.into(); let mut engine = utils::test_compile_and_run_with_tx_flags(program, tx, flags); @@ -46,8 +43,7 @@ fn test_opcode_checklocktime_block() { fn test_opcode_checklocktime_as_op_nop() { let program = "OP_16 OP_CHECKLOCKTIMEVERIFY"; - let mut tx = utils::mock_transaction_locktime(""); - tx.locktime = 10; + let mut tx = utils::mock_transaction_legacy_locktime("", 10); // Running without the flag 'ScriptVerifyCheckLockTimeVerify' result as OP_NOP let mut engine = utils::test_compile_and_run_with_tx(program, tx); @@ -60,8 +56,7 @@ fn test_opcode_checklocktime_as_op_nop() { fn test_opcode_checklocktime_as_op_nop_fail() { let program = "OP_16 OP_CHECKLOCKTIMEVERIFY"; - let mut tx = utils::mock_transaction_locktime(""); - tx.locktime = 10; + let mut tx = utils::mock_transaction_legacy_locktime("", 10); // Running without the flag 'ScriptVerifyCheckLockTimeVerify' result as OP_NOP behavior // 'ScriptDiscourageUpgradableNops' prevents to have OP_NOP behavior @@ -91,7 +86,7 @@ fn test_opcode_checklocktime_max_sequence_fail() { fn test_opcode_checksequence_block() { let mut program = "OP_DATA_4 0x40000000 OP_CHECKSEQUENCEVERIFY"; // 0x40000000 == 64 in ScriptNum - let tx = utils::mock_transaction_v2_sequence("", 2048); + let tx = utils::mock_transaction_legacy_sequence_v2("", 2048); let flags: u32 = ScriptFlags::ScriptVerifyCheckSequenceVerify.into(); let mut engine = utils::test_compile_and_run_with_tx_flags(program, tx, flags); @@ -102,7 +97,7 @@ fn test_opcode_checksequence_block() { fn test_opcode_checksequence_time() { let mut program = "OP_DATA_4 0x00004000 OP_CHECKSEQUENCEVERIFY"; // 0x00004000 == 4194304 in ScriptNum - let tx = utils::mock_transaction_v2_sequence("", 5000000); + let tx = utils::mock_transaction_legacy_sequence_v2("", 5000000); let flags: u32 = ScriptFlags::ScriptVerifyCheckSequenceVerify.into(); let mut engine = utils::test_compile_and_run_with_tx_flags(program, tx, flags); @@ -113,7 +108,7 @@ fn test_opcode_checksequence_time() { fn test_opcode_checksequence_fail() { let mut program = "OP_DATA_4 0x40400000 OP_CHECKSEQUENCEVERIFY"; // 0x40400000 == 16448 in ScriptNum - let tx = utils::mock_transaction_v2_sequence("", 2048); + let tx = utils::mock_transaction_legacy_sequence_v2("", 2048); let flags: u32 = ScriptFlags::ScriptVerifyCheckSequenceVerify.into(); let mut engine = utils::test_compile_and_run_with_tx_flags_err( @@ -128,7 +123,7 @@ fn test_opcode_checksequence_fail() { fn test_opcode_checksequence_as_op_nop() { let mut program = "OP_DATA_4 0x40400000 OP_CHECKSEQUENCEVERIFY"; // 0x40400000 == 16448 in ScriptNum - let tx = utils::mock_transaction_v2_sequence("", 2048); + let tx = utils::mock_transaction_legacy_sequence_v2("", 2048); // Running without the flag 'ScriptVerifyCheckLockTimeVerify' result as OP_NOP let mut engine = utils::test_compile_and_run_with_tx(program, tx); @@ -141,7 +136,7 @@ fn test_opcode_checksequence_as_op_nop() { fn test_opcode_checksequence_as_op_nop_fail() { let mut program = "OP_DATA_4 0x40400000 OP_CHECKSEQUENCEVERIFY"; // 0x40400000 == 16448 in ScriptNum - let mut tx = utils::mock_transaction_v2_sequence("", 2048); + let mut tx = utils::mock_transaction_legacy_sequence_v2("", 2048); // Running without the flag 'ScriptVerifyCheckSequenceVerify' result as OP_NOP behavior // 'ScriptDiscourageUpgradableNops' prevents to have OP_NOP behavior @@ -169,7 +164,7 @@ fn test_opcode_checksequence_tx_version_fail() { #[test] fn test_opcode_checksequence_disabled_bit_stack() { let mut program = "OP_DATA_4 0x80000000 OP_CHECKSEQUENCEVERIFY"; - let tx = utils::mock_transaction_v2_sequence("", 2048); + let tx = utils::mock_transaction_legacy_sequence_v2("", 2048); let flags: u32 = ScriptFlags::ScriptVerifyCheckSequenceVerify.into(); let mut engine = utils::test_compile_and_run_with_tx_flags(program, tx, flags); @@ -180,7 +175,7 @@ fn test_opcode_checksequence_disabled_bit_stack() { fn test_opcode_checksequence_disabled_bit_tx_fail() { let mut program = "OP_DATA_4 0x00004000 OP_CHECKSEQUENCEVERIFY"; // 0x00004000 == 4194304 in ScriptNum - let mut tx = utils::mock_transaction_v2_sequence("", 2147483648); + let mut tx = utils::mock_transaction_legacy_sequence_v2("", 2147483648); // Run with tx v1 let flags: u32 = ScriptFlags::ScriptVerifyCheckSequenceVerify.into(); diff --git a/src/opcodes/tests/utils.cairo b/src/opcodes/tests/utils.cairo index 832f40d9..56ffba0d 100644 --- a/src/opcodes/tests/utils.cairo +++ b/src/opcodes/tests/utils.cairo @@ -96,43 +96,84 @@ pub fn check_expected_astack(ref engine: Engine, expected: Span) { assert_eq!(astack, expected, "Astack is not as expected"); } -pub fn mock_transaction_with(script_sig: ByteArray, sequence: u32, version: i32) -> Transaction { - let outpoint_0: OutPoint = OutPoint { - txid: 0xb7994a0db2f373a29227e1d90da883c6ce1cb0dd2d6812e4558041ebbbcfa54b, vout: 0 - }; +pub fn mock_transaction_input_with( + outpoint: OutPoint, script_sig: ByteArray, witness: Array, sequence: u32 +) -> TransactionInput { let mut compiler = CompilerImpl::new(); - let mut script_sig = compiler.compile(script_sig); - let transaction_input_0: TransactionInput = TransactionInput { - previous_outpoint: outpoint_0, + let script_sig = compiler.compile(script_sig); + TransactionInput { + previous_outpoint: outpoint, signature_script: script_sig, - witness: ArrayTrait::::new(), + witness: witness, sequence: sequence + } +} + +pub fn mock_transaction_input(script_sig: ByteArray) -> TransactionInput { + let outpoint: OutPoint = OutPoint { + txid: 0xb7994a0db2f373a29227e1d90da883c6ce1cb0dd2d6812e4558041ebbbcfa54b, vout: 0 }; - let mut transaction_inputs: Array = ArrayTrait::::new(); - transaction_inputs.append(transaction_input_0); - let oscript_u256: u256 = 0x76a914b3e2819b6262e0b1f19fc7229d75677f347c91ac88ac; - let mut oscript_byte: ByteArray = ""; + mock_transaction_input_with(outpoint, script_sig, ArrayTrait::new(), 0xffffffff) +} - oscript_byte.append_word(oscript_u256.high.into(), 9); - oscript_byte.append_word(oscript_u256.low.into(), 16); +pub fn mock_transaction_output_with(value: i64, script_pubkey: ByteArray) -> TransactionOutput { + TransactionOutput { value: value, publickey_script: script_pubkey } +} - // Little endian to i64 handle - let output_0: TransactionOutput = TransactionOutput { - value: 15000, publickey_script: oscript_byte - }; - let mut transaction_outputs: Array = ArrayTrait::::new(); - transaction_outputs.append(output_0); +pub fn mock_transaction_output() -> TransactionOutput { + let output_script_u256: u256 = 0x76a914b3e2819b6262e0b1f19fc7229d75677f347c91ac88ac; + let mut output_script: ByteArray = ""; + output_script.append_word(output_script_u256.high.into(), 9); + output_script.append_word(output_script_u256.low.into(), 16); + mock_transaction_output_with(15000, output_script) +} +pub fn mock_transaction_with( + version: i32, + tx_inputs: Array, + tx_outputs: Array, + locktime: u32 +) -> Transaction { Transaction { version: version, - transaction_inputs: transaction_inputs, - transaction_outputs: transaction_outputs, - locktime: 0, + transaction_inputs: tx_inputs, + transaction_outputs: tx_outputs, + locktime: locktime, } } +// Mock simple transaction '1d5308ff12cb6fdb670c3af673a6a1317e21fa14fc863d5827f9d704cd5e14dc' pub fn mock_transaction(script_sig: ByteArray) -> Transaction { - return mock_transaction_with(script_sig, 0xffffffff, 1); + let mut inputs = ArrayTrait::::new(); + inputs.append(mock_transaction_input(script_sig)); + let mut outputs = ArrayTrait::::new(); + outputs.append(mock_transaction_output()); + return mock_transaction_with(1, inputs, outputs, 0); +} + +// Mock transaction '1d5308ff12cb6fdb670c3af673a6a1317e21fa14fc863d5827f9d704cd5e14dc' +// Legacy P2PKH +pub fn mock_transaction_legacy_p2pkh(script_sig: ByteArray) -> Transaction { + mock_transaction(script_sig) +} + +// Mock transaction '949591ad468cef5c41656c0a502d9500671ee421fadb590fbc6373000039b693' +// Legacy P2MS +pub fn mock_transaction_legacy_p2ms(script_sig: ByteArray) -> Transaction { + let outpoint: OutPoint = OutPoint { + txid: 0x10a5fee9786a9d2d72c25525e52dd70cbd9035d5152fac83b62d3aa7e2301d58, vout: 0 + }; + let mut inputs = ArrayTrait::::new(); + inputs.append(mock_transaction_input_with(outpoint, script_sig, ArrayTrait::new(), 0xffffffff)); + + let mut outputs = ArrayTrait::::new(); + let output_script_u256: u256 = 0x76a914971802edf585cdbc4e57017d6e5142515c1e502888ac; + let mut output_script: ByteArray = ""; + output_script.append_word(output_script_u256.high.into(), 9); + output_script.append_word(output_script_u256.low.into(), 16); + outputs.append(mock_transaction_output_with(1680000, output_script)); + + return mock_transaction_with(1, inputs, outputs, 0); } pub fn mock_witness_transaction() -> Transaction { @@ -153,7 +194,6 @@ pub fn mock_witness_transaction() -> Transaction { script_byte.append_word(script_u256.high.into(), 9); script_byte.append_word(script_u256.low.into(), 16); - // Little endian to i64 handle let output_0: TransactionOutput = TransactionOutput { value: 15000, publickey_script: script_byte }; @@ -169,11 +209,21 @@ pub fn mock_witness_transaction() -> Transaction { } // Mock transaction with specified 'locktime' and with the 'sequence' field set to locktime -pub fn mock_transaction_locktime(script_sig: ByteArray) -> Transaction { - return mock_transaction_with(script_sig, 0xfffffffe, 1); +pub fn mock_transaction_legacy_locktime(script_sig: ByteArray, locktime: u32) -> Transaction { + let mut inputs = ArrayTrait::::new(); + let outpoint = OutPoint { txid: 0, vout: 0 }; + let input = mock_transaction_input_with(outpoint, script_sig, ArrayTrait::new(), 0xfffffffe); + inputs.append(input); + let outputs = ArrayTrait::::new(); + return mock_transaction_with(1, inputs, outputs, locktime); } // Mock transaction version 2 with the specified 'sequence' -pub fn mock_transaction_v2_sequence(script_sig: ByteArray, sequence: u32) -> Transaction { - return mock_transaction_with(script_sig, sequence, 2); +pub fn mock_transaction_legacy_sequence_v2(script_sig: ByteArray, sequence: u32) -> Transaction { + let mut inputs = ArrayTrait::::new(); + let outpoint = OutPoint { txid: 0, vout: 0 }; + let input = mock_transaction_input_with(outpoint, script_sig, ArrayTrait::new(), sequence); + inputs.append(input); + let outputs = ArrayTrait::::new(); + return mock_transaction_with(2, inputs, outputs, 0); } diff --git a/src/signature/sighash.cairo b/src/signature/sighash.cairo index 01d6f5ef..efd55b60 100644 --- a/src/signature/sighash.cairo +++ b/src/signature/sighash.cairo @@ -1,6 +1,6 @@ -use crate::signature::{constants, utils}; -use crate::transaction::{Transaction, TransactionTrait, TransactionInput, TransactionOutput}; use crate::utils::{int_size_in_bytes, double_sha256}; +use crate::transaction::{Transaction, TransactionTrait, TransactionInput, TransactionOutput}; +use crate::signature::{constants, utils}; // Calculates the signature hash for specified transaction data and hash type. pub fn calc_signature_hash( diff --git a/src/signature/signature.cairo b/src/signature/signature.cairo index ad24ecea..ea5124aa 100644 --- a/src/signature/signature.cairo +++ b/src/signature/signature.cairo @@ -1,10 +1,10 @@ use crate::engine::{Engine, EngineImpl}; -use crate::utils::{u256_from_byte_array_with_offset}; -use crate::signature::{sighash, constants}; -use crate::scriptflags::ScriptFlags; use starknet::SyscallResultTrait; use starknet::secp256_trait::{Secp256Trait, Signature, is_valid_signature}; use starknet::secp256k1::{Secp256k1Point}; +use crate::scriptflags::ScriptFlags; +use crate::utils::{u256_from_byte_array_with_offset}; +use crate::signature::{sighash, constants}; //`BaseSigVerifier` is used to verify ECDSA signatures encoded in DER or BER format (pre-SegWit sig) #[derive(Drop)] @@ -34,16 +34,16 @@ impl BaseSigVerifierImpl of BaseSigVerifierTrait { fn new( ref vm: Engine, sig_bytes: @ByteArray, pk_bytes: @ByteArray ) -> Result { + let mut sub_script = vm.sub_script(); + sub_script = remove_signature(sub_script, sig_bytes); let (pub_key, sig, hash_type) = parse_base_sig_and_pk(ref vm, pk_bytes, sig_bytes)?; - let sub_script = vm.sub_script(); - Result::Ok(BaseSigVerifier { pub_key, sig, sig_bytes, pk_bytes, sub_script, hash_type, }) + Result::Ok(BaseSigVerifier { pub_key, sig, sig_bytes, pk_bytes, sub_script, hash_type }) } // TODO: add signature cache mechanism for optimization fn verify(ref self: BaseSigVerifier, ref vm: Engine) -> bool { - let sub_script: @ByteArray = remove_signature(@self.sub_script, self.sig_bytes); let sig_hash: u256 = sighash::calc_signature_hash( - sub_script, self.hash_type, ref vm.transaction, vm.tx_idx + @self.sub_script, self.hash_type, ref vm.transaction, vm.tx_idx ); is_valid_signature(sig_hash, self.sig.r, self.sig.s, self.pub_key) @@ -273,7 +273,7 @@ pub fn parse_pub_key(pk_bytes: @ByteArray) -> Secp256k1Point { } else { // Extract X coordinate and determine parity from last byte. let pub_key: u256 = u256_from_byte_array_with_offset(@pk_bytes_uncompressed, 1, 32); - let parity = pk_bytes_uncompressed[64] & 1 == 0; + let parity = !(pk_bytes_uncompressed[64] & 1 == 0); return Secp256Trait::::secp256_ec_get_point_from_x_syscall(pub_key, parity) .unwrap_syscall() @@ -286,41 +286,57 @@ pub fn parse_pub_key(pk_bytes: @ByteArray) -> Secp256k1Point { // This function extracts the `r` and `s` values from a DER-encoded ECDSA signature (`sig_bytes`). // The function performs various checks to ensure the integrity and validity of the signature. pub fn parse_signature(sig_bytes: @ByteArray) -> Result { - let sig_len: usize = sig_bytes.len() - constants::HASH_TYPE_LEN; - let r_len: usize = sig_bytes[3].into(); - let s_len: usize = sig_bytes[r_len + 5].into(); - let r_sig: u256 = u256_from_byte_array_with_offset(sig_bytes, 4, r_len); - let s_sig: u256 = u256_from_byte_array_with_offset(sig_bytes, 6 + r_len, s_len); + let mut sig_len: usize = sig_bytes.len() - constants::HASH_TYPE_LEN; + let mut r_len: usize = sig_bytes[3].into(); + let mut s_len: usize = sig_bytes[r_len + 5].into(); + let mut r_offset = 4; + let mut s_offset = 6 + r_len; let order: u256 = Secp256Trait::::get_curve_size(); + let mut i = 0; + + //Strip leading zero + while s_len > 0 && sig_bytes[i + r_len + 6] == 0x00 { + sig_len -= 1; + s_len -= 1; + s_offset += 1; + i += 1; + }; + + let s_sig: u256 = u256_from_byte_array_with_offset(sig_bytes, s_offset, s_len); + + i = 0; + + while r_len > 0 && sig_bytes[i + 4] == 0x00 { + sig_len -= 1; + r_len -= 1; + r_offset += 1; + i += 1; + }; + + let r_sig: u256 = u256_from_byte_array_with_offset(sig_bytes, r_offset, r_len); + if r_len > 32 { return Result::Err('invalid sig: R > 256 bits'); } - if r_sig >= order { return Result::Err('invalid sig: R >= group order'); } - if r_sig == 0 { return Result::Err('invalid sig: R is zero'); } - if s_len > 32 { return Result::Err('invalid sig: S > 256 bits'); } - if s_sig >= order { return Result::Err('invalid sig: S >= group order'); } - if s_sig == 0 { return Result::Err('invalid sig: S is zero'); } - if sig_len != r_len + s_len + 6 { return Result::Err('invalid sig: bad final length'); } - return Result::Ok(Signature { r: r_sig, s: s_sig, y_parity: false, }); } @@ -347,7 +363,7 @@ pub fn parse_base_sig_and_pk( } // Removes the ECDSA signature from a given script. -pub fn remove_signature(script: @ByteArray, sig_bytes: @ByteArray) -> @ByteArray { +pub fn remove_signature(script: ByteArray, sig_bytes: @ByteArray) -> ByteArray { if script.len() == 0 || sig_bytes.len() == 0 { return script; } @@ -362,7 +378,7 @@ pub fn remove_signature(script: @ByteArray, sig_bytes: @ByteArray) -> @ByteArray let mut found: bool = false; if len == sig_bytes.len() { - found = compare_data(script, sig_bytes, i, push_data); + found = compare_data(@script, sig_bytes, i, push_data); } if i + len <= script.len() { @@ -385,5 +401,5 @@ pub fn remove_signature(script: @ByteArray, sig_bytes: @ByteArray) -> @ByteArray i += 1; }; - @processed_script + processed_script } diff --git a/src/signature/utils.cairo b/src/signature/utils.cairo index ce42b37e..0e6b8446 100644 --- a/src/signature/utils.cairo +++ b/src/signature/utils.cairo @@ -1,6 +1,6 @@ -use crate::opcodes::Opcode; use crate::signature::constants; use crate::transaction::{Transaction, TransactionInput, TransactionOutput}; +use crate::opcodes::Opcode; // Removes `OP_CODESEPARATOR` opcodes from the `script`. // By removing this opcode, the script becomes suitable for hashing and signature verification. diff --git a/src/tests/test_coinbase.cairo b/src/tests/test_coinbase.cairo index 4f508796..722b312a 100644 --- a/src/tests/test_coinbase.cairo +++ b/src/tests/test_coinbase.cairo @@ -1,5 +1,5 @@ -use shinigami::transaction::TransactionTrait; -use shinigami::utils; +use crate::transaction::TransactionTrait; +use crate::utils; #[test] fn test_block_subsidy_calculation() { diff --git a/src/tests/test_transactions.cairo b/src/tests/test_transactions.cairo index 2f0b7590..7a37a1e6 100644 --- a/src/tests/test_transactions.cairo +++ b/src/tests/test_transactions.cairo @@ -1,7 +1,7 @@ -use shinigami::transaction::TransactionTrait; -use shinigami::utxo::UTXO; -use shinigami::validate; -use shinigami::utils; +use crate::transaction::TransactionTrait; +use crate::utxo::UTXO; +use crate::validate; +use crate::utils; // TODO: txid byte order reverse @@ -70,7 +70,6 @@ fn test_deserialize_coinbase_transaction() { // TODO } #[test] -#[ignore] fn test_validate_transaction() { // First ever transaction from Satoshi -> Hal Finney // tx: f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16 @@ -87,6 +86,5 @@ fn test_validate_transaction() { }; let utxo_hints = array![prev_out]; let res = validate::validate_transaction(transaction, 0, utxo_hints); - println!("{:?}", res.unwrap_err()); assert!(res.is_ok(), "Transaction validation failed"); } diff --git a/src/validate.cairo b/src/validate.cairo index e49cf109..aad5efa2 100644 --- a/src/validate.cairo +++ b/src/validate.cairo @@ -1,6 +1,6 @@ -use shinigami::engine::EngineImpl; -use shinigami::transaction::Transaction; -use shinigami::utxo::UTXO; +use crate::engine::EngineImpl; +use crate::transaction::Transaction; +use crate::utxo::UTXO; // TODO: Move validate coinbase here