diff --git a/.tool-versions b/.tool-versions index cc60fd62..f6645f9f 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1 +1 @@ -scarb 2.8.2 +scarb 2.9.2 diff --git a/frontend/src/utils/bitcoin-script.ts b/frontend/src/utils/bitcoin-script.ts index fba1c2ad..6f41d6c3 100644 --- a/frontend/src/utils/bitcoin-script.ts +++ b/frontend/src/utils/bitcoin-script.ts @@ -24,514 +24,527 @@ export const bitcoinScriptLanguage = { export const bitcoinScriptOpcodes = [ { label: "OP_0", - description: "An empty array of bytes is pushed onto the stack. (This is not a no-op: an item is added to the stack.)" + description: + "An empty array of bytes is pushed onto the stack. (This is not a no-op: an item is added to the stack.)", }, { label: "OP_DATA_1", - description: "Pushes 1 byte of data onto the stack." + description: "Pushes 1 byte of data onto the stack.", }, { label: "OP_DATA_2", - description: "Pushes 2 bytes of data onto the stack." + description: "Pushes 2 bytes of data onto the stack.", }, { label: "OP_DATA_3", - description: "Pushes 3 bytes of data onto the stack." + description: "Pushes 3 bytes of data onto the stack.", }, { label: "OP_DATA_4", - description: "Pushes 4 bytes of data onto the stack." + description: "Pushes 4 bytes of data onto the stack.", }, { label: "OP_DATA_5", - description: "Pushes 5 bytes of data onto the stack." + description: "Pushes 5 bytes of data onto the stack.", }, { label: "OP_DATA_6", - description: "Pushes 6 bytes of data onto the stack." + description: "Pushes 6 bytes of data onto the stack.", }, { label: "OP_DATA_7", - description: "Pushes 7 bytes of data onto the stack." + description: "Pushes 7 bytes of data onto the stack.", }, { label: "OP_DATA_8", - description: "Pushes 8 bytes of data onto the stack." + description: "Pushes 8 bytes of data onto the stack.", }, { label: "OP_DATA_9", - description: "Pushes 9 bytes of data onto the stack." + description: "Pushes 9 bytes of data onto the stack.", }, { label: "OP_DATA_10", - description: "Pushes 10 bytes of data onto the stack." + description: "Pushes 10 bytes of data onto the stack.", }, { label: "OP_DATA_11", - description: "Pushes 11 bytes of data onto the stack." + description: "Pushes 11 bytes of data onto the stack.", }, { label: "OP_DATA_12", - description: "Pushes 12 bytes of data onto the stack." + description: "Pushes 12 bytes of data onto the stack.", }, { label: "OP_DATA_13", - description: "Pushes 13 bytes of data onto the stack." + description: "Pushes 13 bytes of data onto the stack.", }, { label: "OP_DATA_14", - description: "Pushes 14 bytes of data onto the stack." + description: "Pushes 14 bytes of data onto the stack.", }, { label: "OP_DATA_15", - description: "Pushes 15 bytes of data onto the stack." + description: "Pushes 15 bytes of data onto the stack.", }, { label: "OP_DATA_16", - description: "Pushes 16 bytes of data onto the stack." + description: "Pushes 16 bytes of data onto the stack.", }, { label: "OP_PUSHDATA1", - description: "Pushes up to 255 bytes of data onto the stack." + description: "Pushes up to 255 bytes of data onto the stack.", }, { label: "OP_PUSHDATA2", - description: "Pushes up to 65535 bytes of data onto the stack." + description: "Pushes up to 65535 bytes of data onto the stack.", }, { label: "OP_PUSHDATA4", - description: "Pushes up to 4,294,967,295 bytes of data onto the stack." + description: "Pushes up to 4,294,967,295 bytes of data onto the stack.", }, { label: "OP_1NEGATE", - description: "Pushes the number -1 onto the stack." + description: "Pushes the number -1 onto the stack.", }, { label: "OP_RESERVED", - description: "Reserved opcode. Invalid if executed." + description: "Reserved opcode. Invalid if executed.", }, { label: "OP_TRUE", - description: "Pushes the number 1 onto the stack." + description: "Pushes the number 1 onto the stack.", }, { label: "OP_1", - description: "Alias for OP_TRUE. Pushes the number 1 onto the stack." + description: "Alias for OP_TRUE. Pushes the number 1 onto the stack.", }, { label: "OP_2", - description: "Pushes the number 2 onto the stack." + description: "Pushes the number 2 onto the stack.", }, { label: "OP_3", - description: "Pushes the number 3 onto the stack." + description: "Pushes the number 3 onto the stack.", }, { label: "OP_4", - description: "Pushes the number 4 onto the stack." + description: "Pushes the number 4 onto the stack.", }, { label: "OP_5", - description: "Pushes the number 5 onto the stack." + description: "Pushes the number 5 onto the stack.", }, { label: "OP_6", - description: "Pushes the number 6 onto the stack." + description: "Pushes the number 6 onto the stack.", }, { label: "OP_7", - description: "Pushes the number 7 onto the stack." + description: "Pushes the number 7 onto the stack.", }, { label: "OP_8", - description: "Pushes the number 8 onto the stack." + description: "Pushes the number 8 onto the stack.", }, { label: "OP_9", - description: "Pushes the number 9 onto the stack." + description: "Pushes the number 9 onto the stack.", }, { label: "OP_10", - description: "Pushes the number 10 onto the stack." + description: "Pushes the number 10 onto the stack.", }, { label: "OP_11", - description: "Pushes the number 11 onto the stack." + description: "Pushes the number 11 onto the stack.", }, { label: "OP_12", - description: "Pushes the number 12 onto the stack." + description: "Pushes the number 12 onto the stack.", }, { label: "OP_13", - description: "Pushes the number 13 onto the stack." + description: "Pushes the number 13 onto the stack.", }, { label: "OP_14", - description: "Pushes the number 14 onto the stack." + description: "Pushes the number 14 onto the stack.", }, { label: "OP_15", - description: "Pushes the number 15 onto the stack." + description: "Pushes the number 15 onto the stack.", }, { label: "OP_16", - description: "Pushes the number 16 onto the stack." + description: "Pushes the number 16 onto the stack.", }, { label: "OP_NOP", - description: "Does nothing." + description: "Does nothing.", }, { label: "OP_VER", - description: "Reserved opcode. Invalid if executed." + description: "Reserved opcode. Invalid if executed.", }, { label: "OP_IF", - description: "If the top stack value is not 0, the statements are executed. The value is removed." + description: + "If the top stack value is not 0, the statements are executed. The value is removed.", }, { label: "OP_NOTIF", - description: "If the top stack value is 0, the statements are executed. The value is removed." + description: + "If the top stack value is 0, the statements are executed. The value is removed.", }, { label: "OP_VERIF", - description: "Reserved opcode. Invalid if executed." + description: "Reserved opcode. Invalid if executed.", }, { label: "OP_VERNOTIF", - description: "Reserved opcode. Invalid if executed." + description: "Reserved opcode. Invalid if executed.", }, { label: "OP_ELSE", - description: "Marks the beginning of the else branch of an if statement." + description: "Marks the beginning of the else branch of an if statement.", }, { label: "OP_ENDIF", - description: "Marks the end of an if/else block." + description: "Marks the end of an if/else block.", }, { label: "OP_VERIFY", - description: "Fails the script if the top stack value is not true." + description: "Fails the script if the top stack value is not true.", }, { label: "OP_RETURN", - description: "Marks the end of a script. When executed, the script fails immediately." + description: + "Marks the end of a script. When executed, the script fails immediately.", }, { label: "OP_TOALTSTACK", - description: "Puts the top stack item onto the alternate stack." + description: "Puts the top stack item onto the alternate stack.", }, { label: "OP_FROMALTSTACK", - description: "Puts the top item on the alternate stack back onto the main stack." + description: + "Puts the top item on the alternate stack back onto the main stack.", }, { label: "OP_2DROP", - description: "Removes the top two stack items." + description: "Removes the top two stack items.", }, { label: "OP_2DUP", - description: "Duplicates the top two stack items." + description: "Duplicates the top two stack items.", }, { label: "OP_3DUP", - description: "Duplicates the top three stack items." + description: "Duplicates the top three stack items.", }, { label: "OP_2OVER", - description: "Copies the 3rd and 4th items to the top of the stack." + description: "Copies the 3rd and 4th items to the top of the stack.", }, { label: "OP_2ROT", - description: "Moves the 5th and 6th items to the top of the stack." + description: "Moves the 5th and 6th items to the top of the stack.", }, { label: "OP_2SWAP", - description: "Swaps the top two pairs of items." + description: "Swaps the top two pairs of items.", }, { label: "OP_IFDUP", - description: "Duplicates the top stack item if it is not 0." + description: "Duplicates the top stack item if it is not 0.", }, { label: "OP_DEPTH", - description: "Pushes the number of stack items onto the stack." + description: "Pushes the number of stack items onto the stack.", }, { label: "OP_DROP", - description: "Removes the top stack item." + description: "Removes the top stack item.", }, { label: "OP_DUP", - description: "Duplicates the top stack item." + description: "Duplicates the top stack item.", }, { label: "OP_NIP", - description: "Removes the second-to-top stack item." + description: "Removes the second-to-top stack item.", }, { label: "OP_OVER", - description: "Copies the second-to-top stack item to the top." + description: "Copies the second-to-top stack item to the top.", }, { label: "OP_PICK", - description: "Copies an item from the stack based on a provided index." + description: "Copies an item from the stack based on a provided index.", }, { label: "OP_ROLL", - description: "Moves an item from the stack to the top, based on a provided index." + description: + "Moves an item from the stack to the top, based on a provided index.", }, { label: "OP_ROT", - description: "Rotates the top three items on the stack." + description: "Rotates the top three items on the stack.", }, { label: "OP_SWAP", - description: "Swaps the top two items on the stack." + description: "Swaps the top two items on the stack.", }, { label: "OP_TUCK", - description: "Copies the top item and inserts it before the second-to-top item." + description: + "Copies the top item and inserts it before the second-to-top item.", }, { label: "OP_CAT", - description: "Concatenates two strings (Disabled)." + description: "Concatenates two strings (Disabled).", }, { label: "OP_SUBSTR", - description: "Returns a section of a string (Disabled)." + description: "Returns a section of a string (Disabled).", }, { label: "OP_LEFT", - description: "Returns the left part of a string (Disabled)." + description: "Returns the left part of a string (Disabled).", }, { label: "OP_RIGHT", - description: "Returns the right part of a string (Disabled)." + description: "Returns the right part of a string (Disabled).", }, { label: "OP_SIZE", - description: "Pushes the string length of the top element of the stack." + description: "Pushes the string length of the top element of the stack.", }, { label: "OP_INVERT", - description: "Flips all of the bits in the input (Disabled)." + description: "Flips all of the bits in the input (Disabled).", }, { label: "OP_AND", - description: "Boolean AND between each bit of the inputs (Disabled)." + description: "Boolean AND between each bit of the inputs (Disabled).", }, { label: "OP_OR", - description: "Boolean OR between each bit of the inputs (Disabled)." + description: "Boolean OR between each bit of the inputs (Disabled).", }, { label: "OP_XOR", - description: "Boolean XOR between each bit of the inputs (Disabled)." + description: "Boolean XOR between each bit of the inputs (Disabled).", }, { label: "OP_EQUAL", - description: "Returns 1 if the inputs are exactly equal, 0 otherwise." + description: "Returns 1 if the inputs are exactly equal, 0 otherwise.", }, { label: "OP_EQUALVERIFY", - description: "Same as OP_EQUAL, but runs OP_VERIFY afterward." + description: "Same as OP_EQUAL, but runs OP_VERIFY afterward.", }, { label: "OP_RESERVED1", - description: "Reserved opcode. Invalid if executed." + description: "Reserved opcode. Invalid if executed.", }, { label: "OP_RESERVED2", - description: "Reserved opcode. Invalid if executed." + description: "Reserved opcode. Invalid if executed.", }, { label: "OP_1ADD", - description: "Adds 1 to the top item." + description: "Adds 1 to the top item.", }, { label: "OP_1SUB", - description: "Subtracts 1 from the top item." + description: "Subtracts 1 from the top item.", }, { label: "OP_2MUL", - description: "Multiplies the top item by 2 (Disabled)." + description: "Multiplies the top item by 2 (Disabled).", }, { label: "OP_2DIV", - description: "Divides the top item by 2 (Disabled)." + description: "Divides the top item by 2 (Disabled).", }, { label: "OP_NEGATE", - description: "Negates the top item." + description: "Negates the top item.", }, { label: "OP_ABS", - description: "Pushes the absolute value of the top item." + description: "Pushes the absolute value of the top item.", }, { label: "OP_NOT", - description: "If the input is 0, pushes 1; otherwise, pushes 0." + description: "If the input is 0, pushes 1; otherwise, pushes 0.", }, { label: "OP_0NOTEQUAL", - description: "Returns 0 if the input is 0. Otherwise, returns 1." + description: "Returns 0 if the input is 0. Otherwise, returns 1.", }, { label: "OP_ADD", - description: "Adds the top two items." + description: "Adds the top two items.", }, { label: "OP_SUB", - description: "Subtracts the top two items." + description: "Subtracts the top two items.", }, { label: "OP_MUL", - description: "Multiplies the top two items (Disabled)." + description: "Multiplies the top two items (Disabled).", }, { label: "OP_DIV", - description: "Divides the top two items (Disabled)." + description: "Divides the top two items (Disabled).", }, { label: "OP_MOD", - description: "Returns the remainder after dividing the top two items (Disabled)." + description: + "Returns the remainder after dividing the top two items (Disabled).", }, { label: "OP_LSHIFT", - description: "Shifts the top item to the left (Disabled)." + description: "Shifts the top item to the left (Disabled).", }, { label: "OP_RSHIFT", - description: "Shifts the top item to the right (Disabled)." + description: "Shifts the top item to the right (Disabled).", }, { label: "OP_BOOLAND", - description: "Boolean AND across the top two items." + description: "Boolean AND across the top two items.", }, { label: "OP_BOOLOR", - description: "Boolean OR across the top two items." + description: "Boolean OR across the top two items.", }, { label: "OP_NUMEQUAL", - description: "Returns 1 if the top two items are equal, 0 otherwise." + description: "Returns 1 if the top two items are equal, 0 otherwise.", }, { label: "OP_NUMEQUALVERIFY", - description: "Same as OP_NUMEQUAL, but runs OP_VERIFY afterward." + description: "Same as OP_NUMEQUAL, but runs OP_VERIFY afterward.", }, { label: "OP_NUMNOTEQUAL", - description: "Returns 1 if the top two items are not equal." + description: "Returns 1 if the top two items are not equal.", }, { label: "OP_LESSTHAN", - description: "Returns 1 if the second item is less than the top item." + description: "Returns 1 if the second item is less than the top item.", }, { label: "OP_GREATERTHAN", - description: "Returns 1 if the second item is greater than the top item." + description: "Returns 1 if the second item is greater than the top item.", }, { label: "OP_LESSTHANOREQUAL", - description: "Returns 1 if the second item is less than or equal to the top item." + description: + "Returns 1 if the second item is less than or equal to the top item.", }, { label: "OP_GREATERTHANOREQUAL", - description: "Returns 1 if the second item is greater than or equal to the top item." + description: + "Returns 1 if the second item is greater than or equal to the top item.", }, { label: "OP_MIN", - description: "Returns the smaller of the top two items." + description: "Returns the smaller of the top two items.", }, { label: "OP_MAX", - description: "Returns the larger of the top two items." + description: "Returns the larger of the top two items.", }, { label: "OP_WITHIN", - description: "Returns 1 if the second item is between the top two items." + description: "Returns 1 if the second item is between the top two items.", }, { label: "OP_RIPEMD160", - description: "Hashes the top item using RIPEMD-160." + description: "Hashes the top item using RIPEMD-160.", }, { label: "OP_SHA1", - description: "Hashes the top item using SHA-1." + description: "Hashes the top item using SHA-1.", }, { label: "OP_SHA256", - description: "Hashes the top item using SHA-256." + description: "Hashes the top item using SHA-256.", }, { label: "OP_HASH160", - description: "Hashes the top item using SHA-256 and then RIPEMD-160." + description: "Hashes the top item using SHA-256 and then RIPEMD-160.", }, { label: "OP_HASH256", - description: "Hashes the top item twice using SHA-256." + description: "Hashes the top item twice using SHA-256.", }, { label: "OP_CODESEPARATOR", - description: "Marks a point after which the script is evaluated." + description: "Marks a point after which the script is evaluated.", }, { label: "OP_CHECKSIG", - description: "Checks whether the signature is valid for the top public key and message." + description: + "Checks whether the signature is valid for the top public key and message.", }, { label: "OP_CHECKSIGVERIFY", - description: "Same as OP_CHECKSIG, but runs OP_VERIFY afterward." + description: "Same as OP_CHECKSIG, but runs OP_VERIFY afterward.", }, { label: "OP_CHECKMULTISIG", - description: "Checks whether the signatures are valid for the top public keys and message." + description: + "Checks whether the signatures are valid for the top public keys and message.", }, { label: "OP_CHECKMULTISIGVERIFY", - description: "Same as OP_CHECKMULTISIG, but runs OP_VERIFY afterward." + description: "Same as OP_CHECKMULTISIG, but runs OP_VERIFY afterward.", }, { label: "OP_NOP1", - description: "Does nothing." + description: "Does nothing.", }, { label: "OP_CHECKLOCKTIMEVERIFY", - description: "Verifies the locktime before the transaction can be valid." + description: "Verifies the locktime before the transaction can be valid.", }, { label: "OP_CHECKSEQUENCEVERIFY", - description: "Verifies the relative locktime before the transaction can be valid." + description: + "Verifies the relative locktime before the transaction can be valid.", }, { label: "OP_NOP4", - description: "Does nothing." + description: "Does nothing.", }, { label: "OP_NOP5", - description: "Does nothing." + description: "Does nothing.", }, { label: "OP_NOP6", - description: "Does nothing." + description: "Does nothing.", }, { label: "OP_NOP7", - description: "Does nothing." + description: "Does nothing.", }, { label: "OP_NOP8", - description: "Does nothing." + description: "Does nothing.", }, { label: "OP_NOP9", - description: "Does nothing." + description: "Does nothing.", }, { label: "OP_NOP10", - description: "Does nothing." - } + description: "Does nothing.", + }, ]; diff --git a/packages/cmds/src/main.cairo b/packages/cmds/src/main.cairo index 294ce1cc..58cbd8dd 100644 --- a/packages/cmds/src/main.cairo +++ b/packages/cmds/src/main.cairo @@ -1,18 +1,20 @@ use shinigami_compiler::compiler::CompilerImpl; use shinigami_engine::engine::{EngineImpl, EngineInternalImpl}; -use shinigami_engine::transaction::{EngineInternalTransactionImpl, EngineInternalTransactionTrait}; +use shinigami_engine::transaction::{ + EngineInternalTransactionImpl, EngineInternalTransactionTrait, UTXO, +}; use shinigami_engine::flags; use shinigami_engine::witness; use shinigami_engine::hash_cache::HashCacheImpl; use shinigami_utils::byte_array::felt252_to_byte_array; use shinigami_utils::bytecode::hex_to_bytecode; -use shinigami_tests::utxo::UTXO; use shinigami_tests::validate; #[derive(Clone, Drop)] struct InputData { ScriptSig: ByteArray, ScriptPubKey: ByteArray, + txid: u256, } #[derive(Clone, Drop)] @@ -20,6 +22,7 @@ struct InputDataWithFlags { ScriptSig: ByteArray, ScriptPubKey: ByteArray, Flags: ByteArray, + txid: u256, } #[derive(Clone, Drop)] @@ -28,6 +31,7 @@ struct InputDataWithWitness { ScriptPubKey: ByteArray, Flags: ByteArray, Witness: ByteArray, + txid: u256, } fn run_with_flags(input: InputDataWithFlags) -> Result<(), felt252> { @@ -41,7 +45,9 @@ fn run_with_flags(input: InputDataWithFlags) -> Result<(), felt252> { let script_pubkey = compiler.compile(input.ScriptPubKey)?; let compiler = CompilerImpl::new(); let script_sig = compiler.compile(input.ScriptSig)?; - let tx = EngineInternalTransactionImpl::new_signed(script_sig, script_pubkey.clone()); + let tx = EngineInternalTransactionImpl::new_signed( + script_sig, script_pubkey.clone(), input.txid, array![], + ); let flags = flags::parse_flags(input.Flags); let hash_cache = HashCacheImpl::new(@tx); let mut engine = EngineImpl::new(@script_pubkey, @tx, 0, flags, 0, @hash_cache)?; @@ -64,7 +70,7 @@ fn run_with_witness(input: InputDataWithWitness) -> Result<(), felt252> { let witness = witness::parse_witness_input(input.Witness); let value = 1; // TODO let tx = EngineInternalTransactionImpl::new_signed_witness( - script_sig, script_pubkey.clone(), witness, value, + script_sig, script_pubkey.clone(), witness, value, input.txid, array![], ); let flags = flags::parse_flags(input.Flags); let hash_cache = HashCacheImpl::new(@tx); @@ -83,7 +89,9 @@ fn run(input: InputData) -> Result<(), felt252> { let script_pubkey = compiler.compile(input.ScriptPubKey)?; let compiler = CompilerImpl::new(); let script_sig = compiler.compile(input.ScriptSig)?; - let tx = EngineInternalTransactionImpl::new_signed(script_sig, script_pubkey.clone()); + let tx = EngineInternalTransactionImpl::new_signed( + script_sig, script_pubkey.clone(), input.txid, array![], + ); let hash_cache = HashCacheImpl::new(@tx); let mut engine = EngineImpl::new(@script_pubkey, @tx, 0, 0, 0, @hash_cache)?; let res = engine.execute(); @@ -109,7 +117,9 @@ fn run_with_json(input: InputData) -> Result<(), felt252> { let script_pubkey = compiler.compile(input.ScriptPubKey)?; let compiler = CompilerImpl::new(); let script_sig = compiler.compile(input.ScriptSig)?; - let tx = EngineInternalTransactionImpl::new_signed(script_sig, script_pubkey.clone()); + let tx = EngineInternalTransactionImpl::new_signed( + script_sig, script_pubkey.clone(), input.txid, array![], + ); let hash_cache = HashCacheImpl::new(@tx); let mut engine = EngineImpl::new(@script_pubkey, @tx, 0, 0, 0, @hash_cache)?; let _ = engine.execute()?; @@ -127,7 +137,9 @@ fn debug(input: InputData) -> Result { let script_pubkey = compiler.compile(input.ScriptPubKey)?; let compiler = CompilerImpl::new(); let script_sig = compiler.compile(input.ScriptSig)?; - let tx = EngineInternalTransactionImpl::new_signed(script_sig, script_pubkey.clone()); + let tx = EngineInternalTransactionImpl::new_signed( + script_sig, script_pubkey.clone(), input.txid, array![], + ); let hash_cache = HashCacheImpl::new(@tx); let mut engine = EngineImpl::new(@script_pubkey, @tx, 0, 0, 0, @hash_cache)?; let mut res = Result::Ok(true); @@ -205,12 +217,12 @@ struct ValidateRawInput { raw_transaction: ByteArray, utxo_hints: Array, flags: ByteArray, + txid: u256 // from raito or calculate from raw_transaction in decode ? } fn run_raw_transaction(mut input: ValidateRawInput) -> u8 { println!("Running Bitcoin Script with raw transaction: '{}'", input.raw_transaction); let raw_transaction = hex_to_bytecode(@input.raw_transaction); - let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction); // Parse the flags let script_flags = flags::parse_flags(input.flags); @@ -223,23 +235,25 @@ fn run_raw_transaction(mut input: ValidateRawInput) -> u8 { } let mut utxo_hints = array![]; + for hint in input.utxo_hints.span() { + println!("UTXO hint: 'amount: {}, script_pubkey: {}'", hint.amount, hint.pubkey_script); + let pubkey_script = hex_to_bytecode(hint.pubkey_script); + utxo_hints + .append( + UTXO { + amount: *hint.amount, + pubkey_script: pubkey_script, + block_height: *hint.block_height, + }, + ); + }; + + let transaction = EngineInternalTransactionTrait::deserialize( + raw_transaction, input.txid, utxo_hints, + ); + // transaction.set_utxos(utxo_hints); - for hint in input - .utxo_hints - .span() { - println!("UTXO hint: 'amount: {}, script_pubkey: {}'", hint.amount, hint.pubkey_script); - let pubkey_script = hex_to_bytecode(hint.pubkey_script); - utxo_hints - .append( - UTXO { - amount: *hint.amount, - pubkey_script: pubkey_script, - block_height: *hint.block_height, - }, - ); - }; - - let res = validate::validate_transaction(@transaction, script_flags, utxo_hints); + let res = validate::validate_transaction(@transaction, script_flags); match res { Result::Ok(_) => { println!("Execution successful"); diff --git a/packages/compiler/src/compiler.cairo b/packages/compiler/src/compiler.cairo index 1158b8bf..687ff04d 100644 --- a/packages/compiler/src/compiler.cairo +++ b/packages/compiler/src/compiler.cairo @@ -310,6 +310,7 @@ pub impl CompilerImpl of CompilerTrait { compiler.add_opcode('OP_CLTV', Opcode::OP_CHECKLOCKTIMEVERIFY); compiler.add_opcode('OP_CHECKSEQUENCEVERIFY', Opcode::OP_CHECKSEQUENCEVERIFY); compiler.add_opcode('OP_CSV', Opcode::OP_CHECKSEQUENCEVERIFY); + compiler.add_opcode('OP_CHECKSIGADD', Opcode::OP_CHECKSIGADD); compiler } diff --git a/packages/engine/src/engine.cairo b/packages/engine/src/engine.cairo index 5f05577a..954241dc 100644 --- a/packages/engine/src/engine.cairo +++ b/packages/engine/src/engine.cairo @@ -41,7 +41,7 @@ pub struct Engine { // Amount of the input being spent pub amount: i64, // The script to execute - scripts: Array<@ByteArray>, + pub scripts: Array<@ByteArray>, // Index of the current script being executed script_idx: usize, // Program counter within the current script @@ -66,6 +66,8 @@ pub struct Engine { pub last_code_sep: u32, // Count number of non-push opcodes pub num_ops: u32, + // + pub hash_cache: @HashCache, } // TODO: SigCache @@ -105,6 +107,7 @@ pub impl EngineImpl< +Drop, +Drop, +Drop, + +Default, > of EngineTrait { // Create a new Engine with the given script fn new( @@ -146,6 +149,7 @@ pub impl EngineImpl< saved_first_stack: array![].span(), last_code_sep: 0, num_ops: 0, + hash_cache: hash_cache, }; if engine.has_flag(ScriptFlags::ScriptVerifyCleanStack) @@ -375,6 +379,7 @@ pub impl EngineImpl< } self.opcode_idx += 1; }; + if err != '' { break; } @@ -482,6 +487,7 @@ pub impl EngineInternalImpl< +Drop, +Drop, +Drop, + +Default, > of EngineInternalTrait { fn pull_data(ref self: Engine, len: usize) -> Result { let script = *(self.scripts[self.script_idx]); @@ -698,14 +704,12 @@ pub impl EngineInternalImpl< || self.is_witness_active(TAPROOT_WITNESS_VERSION) { // Sanity checks let mut err = ''; - for w in self - .dstack - .stack_to_span() { - if w.len() > MAX_SCRIPT_ELEMENT_SIZE { - err = Error::SCRIPT_PUSH_SIZE; - break; - } - }; + for w in self.dstack.stack_to_span() { + if w.len() > MAX_SCRIPT_ELEMENT_SIZE { + err = Error::SCRIPT_PUSH_SIZE; + break; + } + }; if err != '' { return Result::Err(err); } diff --git a/packages/engine/src/errors.cairo b/packages/engine/src/errors.cairo index 5f1773ea..77160492 100644 --- a/packages/engine/src/errors.cairo +++ b/packages/engine/src/errors.cairo @@ -40,6 +40,8 @@ pub mod Error { pub const TAPROOT_INVALID_PUBKEY_SIZE: felt252 = 'Bad pubkey size in tapscript'; pub const TAPROOT_INVALID_CONTROL_BLOCK: felt252 = 'Invalid control block'; pub const TAPROOT_INVALID_SIG: felt252 = 'Invalid signature in tap script'; + pub const TAPROOT_INVALID_SIGHASH_TYPE: felt252 = 'Invalid taproot sighash type'; + pub const TAPROOT_INVALID_SIGHASH_MIDSTATE: felt252 = 'Invalid taproot sighash midstat'; pub const TAPROOT_PARITY_MISMATCH: felt252 = 'Parity mismatch in tap script'; pub const TAPROOT_INVALID_MERKLE_PROOF: felt252 = 'Invalid taproot merkle proof'; pub const SCHNORR_INVALID_SIG_SIZE: felt252 = 'Invalid schnorr sig size'; @@ -54,9 +56,10 @@ pub mod Error { pub const SECP256K1_INVALID_POINT: felt252 = 'Invalid secp256k1 point'; pub const PUBKEYTYPE: felt252 = 'Unsupported public key type'; pub const SIG_HIGH_S: felt252 = 'Sig not canonical high S value'; - pub const SIG_HASHTYPE: felt252 = 'invalid hash type'; + pub const SIG_HASHTYPE: felt252 = 'Invalid hash type'; pub const INVALID_PUBKEY_LEN: felt252 = 'Invalid public key length'; pub const NEGATIVE_LOCKTIME: felt252 = 'Stack top is negative'; + pub const INVALID_INDEX_INPUTS: felt252 = 'Invalid index for inputs'; } pub fn byte_array_err(err: felt252) -> ByteArray { diff --git a/packages/engine/src/hash_cache.cairo b/packages/engine/src/hash_cache.cairo index 1e21b88b..b9d24313 100644 --- a/packages/engine/src/hash_cache.cairo +++ b/packages/engine/src/hash_cache.cairo @@ -1,27 +1,41 @@ use crate::transaction::{ EngineTransactionInputTrait, EngineTransactionOutputTrait, EngineTransactionTrait, }; -use shinigami_utils::bytecode::write_var_int; -use shinigami_utils::hash::double_sha256; +use shinigami_utils::{bytecode::{write_var_int}, hash::{hash_to_u256, sha256_u256, simple_sha256}}; +use core::sha256::compute_sha256_byte_array; +use crate::signature::utils::is_witness_v1_pub_key_hash; +use core::dict::Felt252Dict; -#[derive(Clone, Copy, Drop)] +// use core::poseidon::PoseidonTrait; +// use core::hash::{HashStateTrait, HashStateExTrait}; + +// SegwitSigHashMidstate is the sighash midstate used in the base segwit +// sighash calculation as defined in BIP 143. +#[derive(Clone, Copy, Drop, Default)] pub struct SegwitSigHashMidstate { pub hash_prevouts_v0: u256, pub hash_sequence_v0: u256, pub hash_outputs_v0: u256, } -pub trait SigHashMidstateTrait< - I, - O, - T, - +EngineTransactionInputTrait, - +EngineTransactionOutputTrait, - +EngineTransactionTrait, -> { - fn new(transaction: @T) -> SegwitSigHashMidstate; +// TaprootSigHashMidState is the sighash midstate used to compute taproot and +// tapscript signatures as defined in BIP 341. +#[derive(Clone, Copy, Drop, Default)] +pub struct TaprootSigHashMidState { + pub hash_prevouts_v1: u256, + pub hash_sequence_v1: u256, + pub hash_outputs_v1: u256, + pub hash_input_scripts_v1: u256, + pub hash_input_amounts_v1: u256, +} + +pub trait SigHashMidstateTrait { + fn new(transaction: @T) -> TxSigHashes; + fn calc_hash_inputs_amount(transaction: @T) -> u256; + fn calc_hash_input_scripts(transaction: @T) -> u256; } +// TxSigHashes houses the partial set of sighashes introduced within BIP0143. pub impl SigHashMidstateImpl< I, O, @@ -31,8 +45,33 @@ pub impl SigHashMidstateImpl< impl IEngineTransaction: EngineTransactionTrait< T, I, O, IEngineTransactionInput, IEngineTransactionOutput, >, -> of SigHashMidstateTrait { - fn new(transaction: @T) -> SegwitSigHashMidstate { +> of SigHashMidstateTrait { + fn new(transaction: @T) -> TxSigHashes { + let mut hasV0Inputs = false; + let mut hasV1Inputs = false; + + for i in 0..transaction.get_transaction_inputs().len() { + let input = transaction.get_transaction_inputs()[i]; + let input_txid = input.get_prevout_txid(); + let input_vout = input.get_prevout_vout(); + + if (input_vout == 0xFFFFFFFF && input_txid == 0) { + hasV0Inputs = true; + continue; + } + + let utxo = transaction.get_input_utxo(i); + if is_witness_v1_pub_key_hash(@utxo.pubkey_script) { + hasV1Inputs = true; + } else { + hasV0Inputs = true; + } + + if hasV0Inputs && hasV1Inputs { + break; + } + }; + let mut prevouts_v0_bytes: ByteArray = ""; let inputs = transaction.get_transaction_inputs(); for input in inputs { @@ -52,11 +91,60 @@ pub impl SigHashMidstateImpl< write_var_int(ref outputs_v0_bytes, output.get_publickey_script().len().into()); outputs_v0_bytes.append(output.get_publickey_script()); }; - SegwitSigHashMidstate { - hash_prevouts_v0: double_sha256(@prevouts_v0_bytes), - hash_sequence_v0: double_sha256(@sequence_v0_bytes), - hash_outputs_v0: double_sha256(@outputs_v0_bytes), + + let hashPrevOutsV1: [u32; 8] = compute_sha256_byte_array(@prevouts_v0_bytes); + let hashSequenceV1: [u32; 8] = compute_sha256_byte_array(@sequence_v0_bytes); + let hashOutputsV1: [u32; 8] = compute_sha256_byte_array(@outputs_v0_bytes); + + let mut txSigHashes: TxSigHashes = Default::default(); + if hasV0Inputs { + txSigHashes + .set_v0_sighash( + SegwitSigHashMidstate { + hash_prevouts_v0: sha256_u256(hashPrevOutsV1), + hash_sequence_v0: sha256_u256(hashSequenceV1), + hash_outputs_v0: sha256_u256(hashOutputsV1), + }, + ); + } + if hasV1Inputs { + let hash_input_amounts_v1 = Self::calc_hash_inputs_amount(transaction); + let hash_input_scripts_v1 = Self::calc_hash_input_scripts(transaction); + + txSigHashes + .set_v1_sighash( + TaprootSigHashMidState { + hash_prevouts_v1: hash_to_u256(hashPrevOutsV1), + hash_sequence_v1: hash_to_u256(hashSequenceV1), + hash_outputs_v1: hash_to_u256(hashOutputsV1), + hash_input_scripts_v1: hash_input_scripts_v1, + hash_input_amounts_v1: hash_input_amounts_v1, + }, + ); } + txSigHashes + } + + // calcHashInputAmounts computes a hash digest of the input amounts of all + // inputs referenced in the passed transaction. + fn calc_hash_inputs_amount(transaction: @T) -> u256 { + let mut buffer: ByteArray = ""; + for utxo in transaction.get_transaction_utxos() { + buffer.append_word_rev(utxo.amount.into(), 8); + }; + return simple_sha256(@buffer); + } + + // calcHashInputScript computes the hash digest of all the previous input scripts + // referenced by the passed transaction. + fn calc_hash_input_scripts(transaction: @T) -> u256 { + let mut buffer: ByteArray = ""; + for utxo in transaction.get_transaction_utxos() { + write_var_int(ref buffer, utxo.pubkey_script.len().into()); + buffer.append(@utxo.pubkey_script); + }; + + return simple_sha256(@buffer); } } @@ -69,34 +157,57 @@ pub trait SigCacheTrait { fn add(sig_hash: u256, signature: ByteArray, pub_key: ByteArray); } -// TODO -#[derive(Drop)] -pub struct HashCache {} +#[derive(Drop, Default, Copy)] +pub struct TxSigHashes { + pub segwit: SegwitSigHashMidstate, + pub taproot: TaprootSigHashMidState, +} -// HashCache caches the midstate of segwit v0 and v1 sighashes -pub trait HashCacheTrait< - I, - O, - T, - +EngineTransactionInputTrait, - +EngineTransactionOutputTrait, - +EngineTransactionTrait, -> { - fn new(transaction: @T) -> HashCache; - - // v0 represents sighash midstate used in the base segwit signatures BIP-143 - fn get_hash_prevouts_v0(self: @HashCache) -> u256; - fn get_hash_sequence_v0(self: @HashCache) -> u256; - fn get_hash_outputs_v0(self: @HashCache) -> u256; - - // v1 represents sighash midstate used to compute taproot signatures BIP-341 - fn get_hash_prevouts_v1(self: @HashCache) -> u256; - fn get_hash_sequence_v1(self: @HashCache) -> u256; - fn get_hash_outputs_v1(self: @HashCache) -> u256; - fn get_hash_input_scripts_v1(self: @HashCache) -> u256; +#[generate_trait] +impl TxSigHashesImpl of TxSigHashesTrait { + fn new() -> TxSigHashes { + TxSigHashes { segwit: Default::default(), taproot: Default::default() } + } + + fn set_v0_sighash(ref self: TxSigHashes, sighash: SegwitSigHashMidstate) { + self.segwit = sighash; + } + + fn set_v1_sighash(ref self: TxSigHashes, sighash: TaprootSigHashMidState) { + self.taproot = sighash; + } +} + +#[derive(Destruct, Default)] +pub struct HashCache { + sigHashes: Felt252Dict>, } +// HashCache caches the midstate of segwit v0 and v1 sighashes +// pub trait HashCacheTrait< +// I, +// O, +// T, +// +EngineTransactionInputTrait, +// +EngineTransactionOutputTrait, +// +EngineTransactionTrait, +// > { +// fn new(transaction: @T) -> HashCache; +// // fn add_sig_hashes(ref self: HashCache, tx: @T); +// // fn get_sig_hashes(ref self: HashCache, tx_hash: felt252) -> Option; + +// // v0 represents sighash midstate used in the base segwit signatures BIP-143 +// fn get_hash_prevouts_v0(self: @HashCache) -> u256; +// fn get_hash_sequence_v0(self: @HashCache) -> u256; +// fn get_hash_outputs_v0(self: @HashCache) -> u256; +// // v1 represents sighash midstate used to compute taproot signatures BIP-341 +// fn get_hash_prevouts_v1(self: @HashCache) -> u256; +// fn get_hash_sequence_v1(self: @HashCache) -> u256; +// fn get_hash_outputs_v1(self: @HashCache) -> u256; +// fn get_hash_input_scripts_v1(self: @HashCache) -> u256; +// } +#[generate_trait] pub impl HashCacheImpl< I, O, @@ -108,9 +219,24 @@ pub impl HashCacheImpl< >, > of HashCacheTrait { fn new(transaction: @T) -> HashCache { - HashCache {} + HashCache { sigHashes: Default::default() } } + // fn set_v0_sighash(self: @HashCache, tx_hash: u256, sighash: SegwitSigHashMidstate) { + // self.sigHashes.insert(tx_hash, NullableTrait::new(TxSigHashes::Segwit(@sighash))); + // } + + // Add sighashes for a transaction + // fn add_sig_hashes(ref self: HashCache, tx: @T) { + // let txid_hash = PoseidonTrait::new().update_with(tx.get_txid()).finalize(); + // self.sigHashes.insert(txid_hash, NullableTrait::new(SigHashMidstateTrait::new(tx))); + // } + + // Get sighashes for a transaction + // fn get_sig_hashes(ref self: HashCache, tx_hash: felt252) -> Option { + // self.sig_hashes.get(tx_hash) + // } + fn get_hash_prevouts_v0(self: @HashCache) -> u256 { 0 } diff --git a/packages/engine/src/lib.cairo b/packages/engine/src/lib.cairo index 5ed3a8e1..2a70ac14 100644 --- a/packages/engine/src/lib.cairo +++ b/packages/engine/src/lib.cairo @@ -25,18 +25,17 @@ pub use scriptnum::ScriptNum; pub mod flags; pub mod signature { pub mod signature; + pub mod taproot_signature; pub mod sighash; pub mod constants; pub mod utils; pub mod schnorr; - pub use signature::{ - BaseSigVerifier, BaseSigVerifierTrait, BaseSegwitSigVerifierTrait, TaprootSigVerifier, - TaprootSigVerifierTrait, - }; } pub mod transaction; + #[cfg(test)] mod tests { mod test_scriptnum; mod test_schnorr; + mod test_taproot_hash; } diff --git a/packages/engine/src/opcodes/constants.cairo b/packages/engine/src/opcodes/constants.cairo index b122801e..a3ed388b 100644 --- a/packages/engine/src/opcodes/constants.cairo +++ b/packages/engine/src/opcodes/constants.cairo @@ -13,6 +13,7 @@ pub fn opcode_false>(ref engine: Engine) -> Result<(), felt252> { pub fn opcode_push_data< T, +Drop, + +Default, I, +Drop, impl IEngineTransactionInputTrait: EngineTransactionInputTrait, @@ -33,6 +34,7 @@ pub fn opcode_push_data< pub fn opcode_push_data_x< T, +Drop, + +Default, I, +Drop, impl IEngineTransactionInputTrait: EngineTransactionInputTrait, diff --git a/packages/engine/src/opcodes/crypto.cairo b/packages/engine/src/opcodes/crypto.cairo index fc8d9658..a092657b 100644 --- a/packages/engine/src/opcodes/crypto.cairo +++ b/packages/engine/src/opcodes/crypto.cairo @@ -6,9 +6,11 @@ use crate::stack::ScriptStackTrait; use crate::flags::ScriptFlags; use crate::signature::signature; use crate::signature::sighash; -use crate::signature::signature::{ - BaseSigVerifierTrait, TaprootSigVerifierTrait, BaseSegwitSigVerifierTrait, +use crate::signature::{ + signature::{BaseSigVerifierTrait, BaseSegwitSigVerifierTrait}, + taproot_signature::{TaprootSigVerifierTrait, TaprootSigVerifierImpl}, }; + use starknet::secp256_trait::{is_valid_signature}; use shinigami_utils::hash::{sha256_byte_array, double_sha256_bytearray}; use crate::opcodes::utils; @@ -52,6 +54,7 @@ pub fn opcode_ripemd160>(ref engine: Engine) -> Result<(), felt25 pub fn opcode_checksig< T, +Drop, + +Default, I, +Drop, impl IEngineTransactionInputTrait: EngineTransactionInputTrait, @@ -66,8 +69,7 @@ pub fn opcode_checksig< ) -> Result<(), felt252> { let pk_bytes = engine.dstack.pop_byte_array()?; let full_sig_bytes = engine.dstack.pop_byte_array()?; - - if full_sig_bytes.len() < 1 { + if !engine.use_taproot && full_sig_bytes.len() < 1 { engine.dstack.push_bool(false); return Result::Ok(()); } @@ -114,18 +116,34 @@ pub fn opcode_checksig< } } else if engine.use_taproot { // Taproot Signature Verification - engine.taproot_context.use_ops_budget()?; - if pk_bytes.len() == 0 { + + let pk_bytes_len = pk_bytes.len(); + if (pk_bytes_len > 0) { + engine.taproot_context.use_ops_budget()?; + } + + if pk_bytes_len == 0 { return Result::Err(Error::TAPROOT_EMPTY_PUBKEY); } - // TODO: Errors or false? - let mut verifier = TaprootSigVerifierTrait::< + if (full_sig_bytes.len() == 0) { + engine.dstack.push_byte_array(""); // TODO verify this + return Result::Ok(()); + } + + let verifier = TaprootSigVerifierTrait::< I, O, T, - >::new_base(ref engine, @full_sig_bytes, @pk_bytes)?; - is_valid = TaprootSigVerifierTrait::::verify(ref verifier); + >::new_base(@full_sig_bytes, @pk_bytes, ref engine)?; + + is_valid = TaprootSigVerifierTrait::::verify(verifier).is_ok(); } + // TODO already handle ? + // if vm.hasFlag(ScriptVerifyConstScriptCode) && result.sigMatch { + // str := "non-const script code" + // return scriptError(ErrNonConstScriptCode, str) + // } + if !is_valid && @engine.use_taproot == @true { return Result::Err(Error::SIG_NULLFAIL); } @@ -140,11 +158,12 @@ pub fn opcode_checksig< pub fn opcode_checkmultisig< T, +Drop, + +Default, I, +Drop, - impl IEngineTransactionInputTrait: EngineTransactionInputTrait, O, +Drop, + impl IEngineTransactionInputTrait: EngineTransactionInputTrait, impl IEngineTransactionOutputTrait: EngineTransactionOutputTrait, impl IEngineTransactionTrait: EngineTransactionTrait< T, I, O, IEngineTransactionInputTrait, IEngineTransactionOutputTrait, @@ -261,7 +280,7 @@ pub fn opcode_checkmultisig< let sig_hashes = SigHashMidstateTrait::new(transaction); sig_hash = sighash::calc_witness_signature_hash( - @script, @sig_hashes, hash_type, transaction, tx_idx, amount, + @script, sig_hashes, hash_type, transaction, tx_idx, amount, ); } else { sig_hash = sighash::calc_signature_hash(@script, hash_type, transaction, tx_idx); @@ -312,6 +331,7 @@ pub fn opcode_codeseparator< T, I, O, IEngineTransactionInputTrait, IEngineTransactionOutputTrait, >, +Drop, + +Default, +Drop, +Drop, >( @@ -333,6 +353,7 @@ pub fn opcode_codeseparator< pub fn opcode_checksigverify< T, +Drop, + +Default, I, +Drop, impl IEngineTransactionInputTrait: EngineTransactionInputTrait, @@ -353,6 +374,7 @@ pub fn opcode_checksigverify< pub fn opcode_checkmultisigverify< T, +Drop, + +Default, I, +Drop, impl IEngineTransactionInputTrait: EngineTransactionInputTrait, @@ -387,6 +409,7 @@ pub fn opcode_checksigadd< T, I, O, IEngineTransactionInputTrait, IEngineTransactionOutputTrait, >, +Drop, + +Default, +Drop, +Drop, >( @@ -415,8 +438,9 @@ pub fn opcode_checksigadd< let mut verifier = TaprootSigVerifierTrait::< I, O, T, - >::new(ref engine, @sig_bytes, @pk_bytes, engine.taproot_context.annex)?; - if !(TaprootSigVerifierTrait::::verify(ref verifier)) { + >::new(@sig_bytes, @pk_bytes, engine.taproot_context.annex, ref engine)?; + + if (TaprootSigVerifierTrait::::verify(verifier).is_err()) { return Result::Err(Error::TAPROOT_INVALID_SIG); } diff --git a/packages/engine/src/opcodes/flow.cairo b/packages/engine/src/opcodes/flow.cairo index 09f4a96f..883fe061 100644 --- a/packages/engine/src/opcodes/flow.cairo +++ b/packages/engine/src/opcodes/flow.cairo @@ -10,6 +10,7 @@ use crate::errors::Error; pub fn opcode_nop< T, +Drop, + +Default, I, +Drop, impl IEngineTransactionInputTrait: EngineTransactionInputTrait, @@ -38,6 +39,7 @@ const op_cond_skip: u8 = 2; pub fn opcode_if< T, +Drop, + +Default, I, +Drop, impl IEngineTransactionInputTrait: EngineTransactionInputTrait, @@ -67,6 +69,7 @@ pub fn opcode_if< pub fn opcode_notif< T, +Drop, + +Default, I, +Drop, impl IEngineTransactionInputTrait: EngineTransactionInputTrait, diff --git a/packages/engine/src/opcodes/locktime.cairo b/packages/engine/src/opcodes/locktime.cairo index b4e20143..83183267 100644 --- a/packages/engine/src/opcodes/locktime.cairo +++ b/packages/engine/src/opcodes/locktime.cairo @@ -31,6 +31,7 @@ fn verify_locktime(tx_locktime: i64, threshold: i64, stack_locktime: i64) -> Res pub fn opcode_checklocktimeverify< T, +Drop, + +Default, I, +Drop, impl IEngineTransactionInputTrait: EngineTransactionInputTrait, @@ -81,6 +82,7 @@ pub fn opcode_checklocktimeverify< pub fn opcode_checksequenceverify< T, +Drop, + +Default, I, +Drop, impl IEngineTransactionInputTrait: EngineTransactionInputTrait, diff --git a/packages/engine/src/opcodes/opcodes.cairo b/packages/engine/src/opcodes/opcodes.cairo index 03eca8e2..76e1935b 100644 --- a/packages/engine/src/opcodes/opcodes.cairo +++ b/packages/engine/src/opcodes/opcodes.cairo @@ -189,6 +189,7 @@ pub mod Opcode { pub const OP_NOP8: u8 = 183; pub const OP_NOP9: u8 = 184; pub const OP_NOP10: u8 = 185; + pub const OP_CHECKSIGADD: u8 = 186; use crate::engine::Engine; use crate::transaction::{ @@ -201,6 +202,7 @@ pub mod Opcode { pub fn execute< T, +Drop, + +Default, I, +Drop, impl IEngineTransactionInputTrait: EngineTransactionInputTrait, diff --git a/packages/engine/src/signature/constants.cairo b/packages/engine/src/signature/constants.cairo index 31c0107a..9bf8d62e 100644 --- a/packages/engine/src/signature/constants.cairo +++ b/packages/engine/src/signature/constants.cairo @@ -41,6 +41,8 @@ pub const HASH_TYPE_LEN: usize = 1; //including the version byte and the public key hash, ensuring correct data formatting and inclusion //in SegWit transactions. pub const WITNESS_V0_PUB_KEY_HASH_LEN: usize = 22; +// +pub const WITNESS_V1_PUB_KEY_HASH_LEN: usize = 34; //SignatureSize is the size of an encoded Schnorr signature. pub const SCHNORR_SIG_SIZE: usize = 64; //Secp256 field value. diff --git a/packages/engine/src/signature/schnorr.cairo b/packages/engine/src/signature/schnorr.cairo index e24558e2..6951bd69 100644 --- a/packages/engine/src/signature/schnorr.cairo +++ b/packages/engine/src/signature/schnorr.cairo @@ -28,17 +28,15 @@ pub fn parse_schnorr_signature(sig_bytes: @ByteArray) -> Result= constants::SECP256_FIELD_VAL { return Result::Err(Error::SCHNORR_INVALID_SIG_R_FIELD); } @@ -59,12 +57,14 @@ pub fn parse_schnorr_signature(sig_bytes: @ByteArray) -> Result Result { +pub fn verify_schnorr( + sig: Signature, hash: @ByteArray, pubkey: @ByteArray, +) -> Result { if hash.len() != 32 { return Result::Err(Error::SCHNORR_INVALID_MSG_SIZE); } - let P = parse_schnorr_pub_key(@pubkey)?; + let P = parse_schnorr_pub_key(pubkey)?; let n = Secp256Trait::::get_curve_size(); if sig.r >= p { @@ -76,8 +76,8 @@ pub fn verify_schnorr(sig: Signature, hash: ByteArray, pubkey: ByteArray) -> Res let mut msg: ByteArray = Default::default(); msg.append_word(sig.r.high.into(), 16); msg.append_word(sig.r.low.into(), 16); - msg.append(@pubkey); - msg.append(@hash); + msg.append(pubkey); + msg.append(hash); let e = tagged_hash(HashTag::Bip0340Challenge, @msg); let G = Secp256Trait::::get_generator_point(); diff --git a/packages/engine/src/signature/sighash.cairo b/packages/engine/src/signature/sighash.cairo index da992d4d..c0f50f1a 100644 --- a/packages/engine/src/signature/sighash.cairo +++ b/packages/engine/src/signature/sighash.cairo @@ -6,10 +6,13 @@ use crate::signature::constants; use crate::signature::utils::{ remove_opcodeseparator, transaction_procedure, is_witness_pub_key_hash, }; -use crate::hash_cache::SegwitSigHashMidstate; +use crate::transaction::{EngineTransactionOutput}; use shinigami_utils::bytecode::write_var_int; -use shinigami_utils::hash::{sha256_byte_array, double_sha256}; +use shinigami_utils::hash::{sha256_byte_array, simple_sha256, double_sha256}; use crate::opcodes::opcodes::Opcode; +use crate::hash_cache::{TxSigHashes}; +use crate::hash_tag::{HashTag, tagged_hash}; +use crate::errors::Error; // Calculates the signature hash for specified transaction data and hash type. pub fn calc_signature_hash< @@ -66,20 +69,26 @@ pub fn calc_witness_signature_hash< +Drop, >( sub_script: @ByteArray, - sig_hashes: @SegwitSigHashMidstate, + sig_hashes: TxSigHashes, hash_type: u32, transaction: @T, tx_idx: u32, amount: i64, ) -> u256 { - // TODO: Bounds check? + // let mut sig_hashes: @SegwitSigHashMidstate = Default::default(); + // match sig_hashes_enum { + // TxSigHashes::Segwit(segwit_midstate) => { sig_hashes = segwit_midstate; }, + // // Handle error ? + // _ => { return 0; }, + // } + // TODO: Bounds check? let mut sig_hash_bytes: ByteArray = ""; sig_hash_bytes.append_word_rev(transaction.get_version().into(), 4); let zero: u256 = 0; if hash_type & constants::SIG_HASH_ANYONECANPAY == 0 { - let hash_prevouts_v0: u256 = *sig_hashes.hash_prevouts_v0; + let hash_prevouts_v0: u256 = sig_hashes.segwit.hash_prevouts_v0; sig_hash_bytes.append_word(hash_prevouts_v0.high.into(), 16); sig_hash_bytes.append_word(hash_prevouts_v0.low.into(), 16); } else { @@ -90,7 +99,7 @@ pub fn calc_witness_signature_hash< if hash_type & constants::SIG_HASH_ANYONECANPAY == 0 && hash_type & constants::SIG_HASH_MASK != constants::SIG_HASH_SINGLE && hash_type & constants::SIG_HASH_MASK != constants::SIG_HASH_NONE { - let hash_sequence_v0: u256 = *sig_hashes.hash_sequence_v0; + let hash_sequence_v0: u256 = sig_hashes.segwit.hash_sequence_v0; sig_hash_bytes.append_word(hash_sequence_v0.high.into(), 16); sig_hash_bytes.append_word(hash_sequence_v0.low.into(), 16); } else { @@ -128,7 +137,7 @@ pub fn calc_witness_signature_hash< if hash_type & constants::SIG_HASH_MASK != constants::SIG_HASH_SINGLE && hash_type & constants::SIG_HASH_MASK != constants::SIG_HASH_NONE { - let hash_outputs_v0: u256 = *sig_hashes.hash_outputs_v0; + let hash_outputs_v0: u256 = sig_hashes.segwit.hash_outputs_v0; sig_hash_bytes.append_word(hash_outputs_v0.high.into(), 16); sig_hash_bytes.append_word(hash_outputs_v0.low.into(), 16); } else if hash_type & constants::SIG_HASH_MASK == constants::SIG_HASH_SINGLE @@ -165,17 +174,17 @@ pub const TAPSCRIPT_SIGHASH_EXT_FLAG: SighashExtFlag = 1; #[derive(Drop)] pub struct TaprootSighashOptions { // Denotes the current message digest extension being used. - ext_flag: SighashExtFlag, + pub ext_flag: SighashExtFlag, // Sha256 of the annix with a compact size lenght prefix. // sha256(compactsize(annex) || annex) - annex_hash: @ByteArray, + pub annex_hash: @ByteArray, // Hash of the tapscript leaf as defined in BIP-341. // h_tapleaf(version || compactsize(script) || script) - tap_leaf_hash: @ByteArray, + pub tap_leaf_hash: @ByteArray, // Key version as defined in BIP-341. Actually always 0. - key_version: u8, + pub key_version: u8, // Position of the last opcode separator. Used for BIP-342 sighash message extension. - code_sep_pos: u32, + pub code_sep_pos: u32, } #[generate_trait()] @@ -212,6 +221,10 @@ pub impl TaprootSighashOptionsImpl of TaprootSighashOptionsTrait { } } + fn set_annex(ref self: TaprootSighashOptions, annex: @ByteArray) { + self.annex_hash = @sha256_byte_array(annex); + } + // Write in msg the sihash message extension defined by the current active flag. fn write_digest_extensions(ref self: TaprootSighashOptions, ref msg: ByteArray) { // Base extension doesn'nt modify the digest at all. @@ -242,11 +255,127 @@ pub fn is_valid_taproot_sighash(hash_type: u32) -> bool { } } -pub fn calc_taproot_signature_hash() -> u256 { - 0 // TODO -} +pub fn calc_taproot_signature_hash< + T, + +Drop, + I, + +Drop, + O, + +Drop, + impl IEngineTransactionInputTrait: EngineTransactionInputTrait, + impl IEngineTransactionOutputTrait: EngineTransactionOutputTrait, + impl IEngineTransactionTrait: EngineTransactionTrait< + T, I, O, IEngineTransactionInputTrait, IEngineTransactionOutputTrait, + >, +>( + sig_hashes: TxSigHashes, + h_type: u32, + transaction: @T, + input_idx: u32, + prev_output: EngineTransactionOutput, + ref opts: TaprootSighashOptions, +) -> Result { + if !is_valid_taproot_sighash(h_type) { + return Result::Err(Error::TAPROOT_INVALID_SIGHASH_TYPE); + } -pub fn calc_tapscript_signature_hash() -> u256 { - 0 // TODO -} + // Check if the input index is valid + if input_idx > (transaction.get_transaction_inputs().len() - 1) { + return Result::Err(Error::INVALID_INDEX_INPUTS); + } + + let mut sig_msg: ByteArray = Default::default(); + // The final sighash always starts with 0x00, called sighash epoch + sig_msg.append_byte(0x00); + sig_msg.append_byte(h_type.try_into().unwrap()); + sig_msg.append_word_rev(transaction.get_version().into(), 4); + sig_msg.append_word_rev(transaction.get_locktime().into(), 4); + // let mut sig_hashes: @TaprootSigHashMidState = Default::default(); + // match sig_hashes_enum { + // TxSigHashes::Taproot(midstate) => { sig_hashes = midstate; }, + // _ => { return Result::Err(Error::TAPROOT_INVALID_SIGHASH_MIDSTATE); }, + // } + + if (h_type & constants::SIG_HASH_ANYONECANPAY) != constants::SIG_HASH_ANYONECANPAY { + let hash_prevouts_v1: u256 = sig_hashes.taproot.hash_prevouts_v1; + sig_msg.append_word(hash_prevouts_v1.high.into(), 16); + sig_msg.append_word(hash_prevouts_v1.low.into(), 16); + + let hash_input_amounts_v1: u256 = sig_hashes.taproot.hash_input_amounts_v1; + sig_msg.append_word(hash_input_amounts_v1.high.into(), 16); + sig_msg.append_word(hash_input_amounts_v1.low.into(), 16); + + let hash_input_scripts_v1: u256 = sig_hashes.taproot.hash_input_scripts_v1; + sig_msg.append_word(hash_input_scripts_v1.high.into(), 16); + sig_msg.append_word(hash_input_scripts_v1.low.into(), 16); + + let hash_sequence_v1: u256 = sig_hashes.taproot.hash_sequence_v1; + sig_msg.append_word(hash_sequence_v1.high.into(), 16); + sig_msg.append_word(hash_sequence_v1.low.into(), 16); + } + + // If SIGHASH_ALL or SIGHASH_DEFAULT, include all output digests + if (h_type & constants::SIG_HASH_SINGLE) != constants::SIG_HASH_SINGLE + && (h_type & constants::SIG_HASH_SINGLE) != constants::SIG_HASH_NONE { + let hash_outputs_v1: u256 = sig_hashes.taproot.hash_outputs_v1; + sig_msg.append_word(hash_outputs_v1.high.into(), 16); + sig_msg.append_word(hash_outputs_v1.low.into(), 16); + } + + // Write input-specific information + let input = transaction.get_transaction_inputs().at(input_idx); + let witness_has_annex = opts.annex_hash.len() != 0; + let mut spend_type: u8 = opts.ext_flag * 2; + if witness_has_annex { + spend_type += 1; + } + sig_msg.append_byte(spend_type); + + // Write input-specific data + if (h_type & constants::SIG_HASH_ANYONECANPAY) == constants::SIG_HASH_ANYONECANPAY { + // previous outpoint + sig_msg.append_word(input.get_prevout_txid().high.into(), 16); + sig_msg.append_word(input.get_prevout_txid().low.into(), 16); + sig_msg.append_word_rev(input.get_prevout_vout().into(), 4); + + // previous output (amount and script) + sig_msg.append_word_rev(prev_output.get_value().into(), 8); + write_var_int(ref sig_msg, prev_output.get_publickey_script().len().into()); + + // input sequence + sig_msg.append_word_rev(input.get_sequence().into(), 4); + } else { + // input index + sig_msg.append_word_rev(input_idx.into(), 4); + } + + if witness_has_annex { + sig_msg.append(opts.annex_hash); + } + + // If sighash single, include the output information + if (h_type & constants::SIG_HASH_MASK) == constants::SIG_HASH_SINGLE { + if input_idx >= transaction.get_transaction_outputs().len() { + return Result::Err(Error::INVALID_INDEX_INPUTS); + } + let output = transaction.get_transaction_outputs().at(input_idx); + + // Serialize the output + let mut output_bytes: ByteArray = Default::default(); + output_bytes.append_word_rev(output.get_value().into(), 8); + write_var_int(ref output_bytes, output.get_publickey_script().len().into()); + output_bytes.append(output.get_publickey_script()); + + // Hash the output + let hashed_output: u256 = simple_sha256(@output_bytes); + sig_msg.append_word(hashed_output.high.into(), 16); + sig_msg.append_word(hashed_output.low.into(), 16); + } + + // Write any digest extensions + opts.write_digest_extensions(ref sig_msg); + + // The final sighash is computed as: hash_TagSigHash(0x00 || sigMsg). + Result::Ok(tagged_hash(HashTag::TapSighash, @sig_msg)) +} diff --git a/packages/engine/src/signature/signature.cairo b/packages/engine/src/signature/signature.cairo index 60098231..db1ee456 100644 --- a/packages/engine/src/signature/signature.cairo +++ b/packages/engine/src/signature/signature.cairo @@ -6,9 +6,9 @@ use starknet::SyscallResultTrait; use starknet::secp256_trait::{Secp256Trait, Signature, is_valid_signature}; use starknet::secp256k1::{Secp256k1Point}; use crate::flags::ScriptFlags; -use crate::hash_cache::SigHashMidstateTrait; +use crate::hash_cache::{SigHashMidstateTrait}; use shinigami_utils::byte_array::u256_from_byte_array_with_offset; -use crate::signature::{sighash, constants, schnorr}; +use crate::signature::{sighash, constants}; use crate::errors::Error; use shinigami_utils::byte_array::{sub_byte_array}; use crate::parser; @@ -56,6 +56,7 @@ impl BaseSigVerifierImpl< +Drop, +Drop, +Drop, + +Default, > of BaseSigVerifierTrait { fn new( ref vm: Engine, sig_bytes: @ByteArray, pk_bytes: @ByteArray, @@ -104,7 +105,7 @@ impl BaseSegwitSigVerifierImpl< let sig_hashes = SigHashMidstateTrait::new(vm.transaction); let sig_hash: u256 = sighash::calc_witness_signature_hash::< I, O, T, - >(@self.sub_script, @sig_hashes, self.hash_type, vm.transaction, vm.tx_idx, vm.amount); + >(@self.sub_script, sig_hashes, self.hash_type, vm.transaction, vm.tx_idx, vm.amount); is_valid_signature(sig_hash, self.sig.r, self.sig.s, self.pub_key) } @@ -136,6 +137,7 @@ pub fn compare_data(script: @ByteArray, sig_bytes: @ByteArray, i: u32, push_data pub fn check_hash_type_encoding< T, +Drop, + +Default, I, +Drop, impl IEngineTransactionInputTrait: EngineTransactionInputTrait, @@ -176,6 +178,7 @@ pub fn check_hash_type_encoding< pub fn check_signature_encoding< T, +Drop, + +Default, I, +Drop, impl IEngineTransactionInputTrait: EngineTransactionInputTrait, @@ -331,6 +334,7 @@ fn is_supported_pub_key_type(pk_bytes: @ByteArray) -> bool { pub fn check_pub_key_encoding< T, +Drop, + +Default, I, +Drop, impl IEngineTransactionInputTrait: EngineTransactionInputTrait, @@ -487,6 +491,7 @@ pub fn parse_signature(sig_bytes: @ByteArray) -> Result { pub fn parse_base_sig_and_pk< T, +Drop, + +Default, I, +Drop, impl IEngineTransactionInputTrait: EngineTransactionInputTrait, @@ -592,161 +597,3 @@ pub fn remove_signature(script: @ByteArray, sig_bytes: @ByteArray) -> @ByteArray @processed_script } - -// Parses the public key and signature for taproot spend. -// Returning a tuple containing the parsed public key, signature, and hash type. -pub fn parse_taproot_sig_and_pk< - T, - +Drop, - I, - +Drop, - impl IEngineTransactionInputTrait: EngineTransactionInputTrait, - O, - +Drop, - impl IEngineTransactionOutputTrait: EngineTransactionOutputTrait, - impl IEngineTransactionTrait: EngineTransactionTrait< - T, I, O, IEngineTransactionInputTrait, IEngineTransactionOutputTrait, - >, ->( - ref vm: Engine, pk_bytes: @ByteArray, sig_bytes: @ByteArray, -) -> Result<(Secp256k1Point, Signature, u32), felt252> { - // Parse schnorr public key - let pk = schnorr::parse_schnorr_pub_key(pk_bytes)?; - - // Check the size of the signature and if the `sighash byte` is set. - let sig_len = sig_bytes.len(); - let (sig, sighash_type) = if sig_len == constants::SCHNORR_SIG_SIZE { - // Parse signature, `sighash_type` has default value - (schnorr::parse_schnorr_signature(sig_bytes)?, constants::SIG_HASH_DEFAULT) - } else if sig_len == constants::SCHNORR_SIG_SIZE + 1 && sig_bytes[64] != 0 { - // Extract `sighash_byte` and parse signature - let sighash_type = sig_bytes[64]; - let mut sig_bytes_truncate: ByteArray = ""; - for i in 0..constants::SCHNORR_SIG_SIZE { - sig_bytes_truncate.append_byte(sig_bytes[i]); - }; - (schnorr::parse_schnorr_signature(@sig_bytes_truncate)?, sighash_type.into()) - } else { - // Error on invalid signature size. - return Result::Err(Error::SCHNORR_INVALID_SIG_SIZE); - }; - - return Result::Ok((pk, sig, sighash_type)); -} - -#[derive(Drop)] -pub struct TaprootSigVerifier { - // public key as a point on the secp256k1 curve, used to verify the signature - pub_key: Secp256k1Point, - // ECDSA signature - sig: Signature, - // raw byte array of the signature - sig_bytes: @ByteArray, - // raw byte array of the public key - pk_bytes: @ByteArray, - // specifies how the transaction was hashed for signing - hash_type: u32, - // annex data used for taproot verification - annex: @ByteArray, -} - -pub trait TaprootSigVerifierTrait< - I, - O, - T, - +EngineTransactionInputTrait, - +EngineTransactionOutputTrait, - +EngineTransactionTrait, -> { - fn new( - ref vm: Engine, sig_bytes: @ByteArray, pk_bytes: @ByteArray, annex: @ByteArray, - ) -> Result; - fn new_base( - ref vm: Engine, sig_bytes: @ByteArray, pk_bytes: @ByteArray, - ) -> Result; - fn verify(ref self: TaprootSigVerifier) -> bool; - fn verify_base(ref self: TaprootSigVerifier) -> bool; -} - -pub impl TaprootSigVerifierImpl< - I, - O, - T, - impl IEngineTransactionInput: EngineTransactionInputTrait, - impl IEngineTransactionOutput: EngineTransactionOutputTrait, - impl IEngineTransaction: EngineTransactionTrait< - T, I, O, IEngineTransactionInput, IEngineTransactionOutput, - >, - +Drop, - +Drop, - +Drop, -> of TaprootSigVerifierTrait { - fn new( - ref vm: Engine, sig_bytes: @ByteArray, pk_bytes: @ByteArray, annex: @ByteArray, - ) -> Result { - let (pub_key, sig, hash_type) = parse_taproot_sig_and_pk(ref vm, pk_bytes, sig_bytes)?; - Result::Ok( - TaprootSigVerifier { - pub_key: pub_key, sig: sig, sig_bytes, pk_bytes, hash_type: hash_type, annex, - }, - ) - } - - fn new_base( - ref vm: Engine, sig_bytes: @ByteArray, pk_bytes: @ByteArray, - ) -> Result { - let pk_len = pk_bytes.len(); - // Fail immediately if public key length is zero - if pk_len == 0 { - return Result::Err(Error::TAPROOT_EMPTY_PUBKEY); - // If key is 32 byte, parse as normal - } else if pk_len == 32 { - let (pub_key, sig, hash_type) = parse_taproot_sig_and_pk(ref vm, pk_bytes, sig_bytes)?; - return (Result::Ok( - TaprootSigVerifier { - pub_key: pub_key, - sig: sig, - sig_bytes, - pk_bytes, - hash_type: hash_type, - annex: vm.taproot_context.annex, - }, - )); - // Otherwise, this is an unknown public key, assuming sig is valid - } else { - // However, return an error if the flags preventinf usage of unknown key type is set - if vm.has_flag(ScriptFlags::ScriptVerifyDiscourageUpgradeablePubkeyType) { - return Result::Err(Error::DISCOURAGE_UPGRADABLE_PUBKEY_TYPE); - } - - let pub_key: u256 = u256_from_byte_array_with_offset(pk_bytes, 0, 32); - let pk = Secp256Trait::< - Secp256k1Point, - >::secp256_ec_get_point_from_x_syscall(pub_key, false) - .unwrap_syscall() - .expect(Error::SECP256K1_INVALID_POINT); - return (Result::Ok( - TaprootSigVerifier { - pub_key: pk, - sig: Signature { r: 0, s: 0, y_parity: false }, - sig_bytes, - pk_bytes, - hash_type: constants::SIG_HASH_DEFAULT, - annex: @"", - }, - )); - } - } - - fn verify(ref self: TaprootSigVerifier) -> bool { - // let sig_hash = sighash::calc_taproot_signature_hash(); // TODO - // return schnorr::verify_schnorr(); - false - } - - fn verify_base(ref self: TaprootSigVerifier) -> bool { - // let sig_hash = sighash::calc_taproot_signature_hash(); // TODO - // return schnorr::verify_schnorr(); - false - } -} diff --git a/packages/engine/src/signature/taproot_signature.cairo b/packages/engine/src/signature/taproot_signature.cairo new file mode 100644 index 00000000..9da4f673 --- /dev/null +++ b/packages/engine/src/signature/taproot_signature.cairo @@ -0,0 +1,228 @@ +use crate::engine::{Engine, EngineInternalImpl}; +use crate::transaction::{ + EngineTransactionOutput, EngineTransactionInputTrait, EngineTransactionOutputTrait, + EngineTransactionTrait, +}; +use crate::flags::ScriptFlags; +use crate::signature::{constants, schnorr, sighash, sighash::{TaprootSighashOptionsTrait}}; +use crate::hash_cache::{TxSigHashes, SigHashMidstateTrait}; +use crate::errors::Error; + +use shinigami_utils::byte_array::{U256IntoByteArray}; +use starknet::secp256_trait::{Signature}; +use starknet::secp256k1::{Secp256k1Point}; + +pub const SCHNORR_SIGNATURE_LEN: usize = 64; + +// Parses the public key and signature for taproot spend. +// Returning a tuple containing the parsed public key, signature, and hash type. +pub fn parse_taproot_sig_and_pk< + T, + +Drop, + I, + +Drop, + impl IEngineTransactionInputTrait: EngineTransactionInputTrait, + O, + +Drop, + impl IEngineTransactionOutputTrait: EngineTransactionOutputTrait, + impl IEngineTransactionTrait: EngineTransactionTrait< + T, I, O, IEngineTransactionInputTrait, IEngineTransactionOutputTrait, + >, +>( + ref vm: Engine, pk_bytes: @ByteArray, sig_bytes: @ByteArray, +) -> Result<(Secp256k1Point, Signature, u32), felt252> { + let pk = schnorr::parse_schnorr_pub_key(pk_bytes)?; + + // Check the size of the signature and if the `sighash byte` is set. + let sig_len = sig_bytes.len(); + let (sig, sighash_type) = if sig_len == constants::SCHNORR_SIG_SIZE { + // Parse signature, `sighash_type` has default value + (schnorr::parse_schnorr_signature(sig_bytes)?, constants::SIG_HASH_DEFAULT) + } else if sig_len == constants::SCHNORR_SIG_SIZE + 1 && sig_bytes[64] != 0 { + // Extract `sighash_byte` and parse signature + let sighash_type = sig_bytes[64]; + let mut sig_bytes_truncate: ByteArray = ""; + for i in 0..constants::SCHNORR_SIG_SIZE { + sig_bytes_truncate.append_byte(sig_bytes[i]); + }; + (schnorr::parse_schnorr_signature(@sig_bytes_truncate)?, sighash_type.into()) + } else { + // Error on invalid signature size. + return Result::Err(Error::SCHNORR_INVALID_SIG_SIZE); + }; + + return Result::Ok((pk, sig, sighash_type)); +} + +// TODO: remplace sig result by VerifyResult ? +// #[derive(Clone, Copy, Drop, Default)] +// pub struct VerifyResult { +// sigValid: bool, +// sigMatch: bool, +// } + +#[derive(Drop)] +pub struct TaprootSigVerifier { + // public key as a point on the secp256k1 curve, used to verify the signature + pub pub_key: Option, + // ECDSA signature + pub sig: Signature, + // raw byte array of the signature + pub sig_bytes: @ByteArray, + // raw byte array of the public key + pub pk_bytes: @ByteArray, + // specifies how the transaction was hashed for signing + pub hash_type: u32, + // transaction being verified + pub tx: @T, + // index of the input being verified + pub inputIndex: u32, + // output being spent + pub prevOuts: EngineTransactionOutput, + // + // sigCache: SigCache TODO? + // + pub hashCache: TxSigHashes, + // annex data used for taproot verification + pub annex: @ByteArray, +} + +pub trait TaprootSigVerifierTrait< + I, + O, + T, + +EngineTransactionInputTrait, + +EngineTransactionOutputTrait, + +EngineTransactionTrait, +> { + fn empty() -> TaprootSigVerifier; + fn new( + sig_bytes: @ByteArray, pk_bytes: @ByteArray, annex: @ByteArray, ref engine: Engine, + ) -> Result, felt252>; + fn new_base( + sig_bytes: @ByteArray, pk_bytes: @ByteArray, ref engine: Engine, + ) -> Result, felt252>; + fn verify(self: TaprootSigVerifier) -> Result<(), felt252>; + fn verify_base(self: TaprootSigVerifier, ref engine: Engine) -> Result<(), felt252>; +} + +pub impl TaprootSigVerifierImpl< + T, + +Drop, + +Default, + I, + +Drop, + impl IEngineTransactionInputTrait: EngineTransactionInputTrait, + O, + +Drop, + impl IEngineTransactionOutputTrait: EngineTransactionOutputTrait, + impl IEngineTransactionTrait: EngineTransactionTrait< + T, I, O, IEngineTransactionInputTrait, IEngineTransactionOutputTrait, + >, +> of TaprootSigVerifierTrait { + fn empty() -> TaprootSigVerifier { + TaprootSigVerifier { + pub_key: Option::None, + sig: Signature { r: 0, s: 0, y_parity: false }, + sig_bytes: @"", + pk_bytes: @"", + hash_type: 0, + tx: @Default::default(), + inputIndex: 0, + prevOuts: Default::::default(), // verify this + hashCache: Default::default(), + annex: @"", + } + } + + fn new( + sig_bytes: @ByteArray, pk_bytes: @ByteArray, annex: @ByteArray, ref engine: Engine, + ) -> Result, felt252> { + let (pub_key, sig, hash_type) = parse_taproot_sig_and_pk(ref engine, pk_bytes, sig_bytes)?; + let sig_hashes = SigHashMidstateTrait::new(engine.transaction); + let prevOutput = EngineTransactionOutput { + value: engine.amount, publickey_script: (*engine.scripts[1]).clone(), + }; + + Result::Ok( + TaprootSigVerifier { + pub_key: Option::Some(pub_key), + sig, + sig_bytes, + pk_bytes, + hash_type, + tx: engine.transaction, + inputIndex: engine.tx_idx, + prevOuts: prevOutput, + hashCache: sig_hashes, + annex, + }, + ) + } + + fn new_base( + sig_bytes: @ByteArray, pk_bytes: @ByteArray, ref engine: Engine, + ) -> Result, felt252> { + let pk_bytes_len = pk_bytes.len(); + + // Fail immediately if public key length is zero + if pk_bytes_len == 0 { + return Result::Err(Error::TAPROOT_EMPTY_PUBKEY); + } // If key is 32 byte, parse as normal + else if pk_bytes_len == 32 { + return Self::new(sig_bytes, pk_bytes, engine.taproot_context.annex, ref engine); + // Otherwise, this is an unknown public key, assuming sig is valid + } else { + // However, return an error if the flags preventinf usage of unknown key type is set + if engine.has_flag(ScriptFlags::ScriptVerifyDiscourageUpgradeablePubkeyType) { + return Result::Err(Error::DISCOURAGE_UPGRADABLE_PUBKEY_TYPE); + } + return Result::Ok(Self::empty()); + } + } + + + fn verify(self: TaprootSigVerifier) -> Result<(), felt252> { + let mut opts = TaprootSighashOptionsTrait::new_with_annex(self.annex); + let sig_hash = sighash::calc_taproot_signature_hash::< + T, + >( + self.hashCache, self.hash_type, self.tx, self.inputIndex, self.prevOuts, ref opts, + )?; // on error should return error or false ? + + if !schnorr::verify_schnorr(self.sig, @sig_hash.into(), self.pk_bytes)? { + return Result::Err( + Error::TAPROOT_INVALID_SIG, + ); // should not return error ? VerifyResult ? + } + Result::Ok(()) + } + + fn verify_base(self: TaprootSigVerifier, ref engine: Engine) -> Result<(), felt252> { + if (self.pub_key.is_none()) { + return Result::Ok(()); + } + + let mut opts = TaprootSighashOptionsTrait::new_with_tapscript_version( + engine.taproot_context.code_sep, @engine.taproot_context.tapleaf_hash.into(), + ); + + if engine.taproot_context.annex.len() > 0 { + opts.set_annex(engine.taproot_context.annex); + } + + let sig_hash = sighash::calc_taproot_signature_hash::< + T, + >( + self.hashCache, self.hash_type, self.tx, self.inputIndex, self.prevOuts, ref opts, + )?; // on error should return error or false ? + + if !schnorr::verify_schnorr(self.sig, @sig_hash.into(), self.pk_bytes)? { + return Result::Err( + Error::TAPROOT_INVALID_SIG, + ); // should not return error ? VerifyResult ? + } + Result::Ok(()) + } +} + diff --git a/packages/engine/src/signature/utils.cairo b/packages/engine/src/signature/utils.cairo index d1b962f8..bccc6d9a 100644 --- a/packages/engine/src/signature/utils.cairo +++ b/packages/engine/src/signature/utils.cairo @@ -59,31 +59,31 @@ pub fn transaction_procedure< ) -> EngineTransaction { let hash_type_masked = hash_type & constants::SIG_HASH_MASK; let mut transaction_inputs_clone = array![]; - for input in transaction - .get_transaction_inputs() { - let new_transaction_input = EngineTransactionInput { - previous_outpoint: EngineOutPoint { - txid: input.get_prevout_txid(), vout: input.get_prevout_vout(), - }, - signature_script: input.get_signature_script().clone(), - witness: input.get_witness().into(), - sequence: input.get_sequence(), - }; - transaction_inputs_clone.append(new_transaction_input); + for input in transaction.get_transaction_inputs() { + let new_transaction_input = EngineTransactionInput { + previous_outpoint: EngineOutPoint { + txid: input.get_prevout_txid(), vout: input.get_prevout_vout(), + }, + signature_script: input.get_signature_script().clone(), + witness: input.get_witness().into(), + sequence: input.get_sequence(), }; + transaction_inputs_clone.append(new_transaction_input); + }; let mut transaction_outputs_clone = array![]; - for output in transaction - .get_transaction_outputs() { - let new_transaction_output = EngineTransactionOutput { - value: output.get_value(), publickey_script: output.get_publickey_script().clone(), - }; - transaction_outputs_clone.append(new_transaction_output); + for output in transaction.get_transaction_outputs() { + let new_transaction_output = EngineTransactionOutput { + value: output.get_value(), publickey_script: output.get_publickey_script().clone(), }; + transaction_outputs_clone.append(new_transaction_output); + }; let mut transaction_copy = EngineTransaction { version: transaction.get_version(), transaction_inputs: transaction_inputs_clone, transaction_outputs: transaction_outputs_clone, locktime: transaction.get_locktime(), + utxos: transaction.get_transaction_utxos(), + txid: transaction.get_txid(), }; let mut i: usize = 0; let mut transaction_input: Array = transaction_copy.transaction_inputs; @@ -164,3 +164,14 @@ pub fn is_witness_pub_key_hash(script: @ByteArray) -> bool { } false } + +// Checks if the given script is a Pay-to-Taproot script. +// OP_1 OP_DATA_32 <32-byte-hash> +pub fn is_witness_v1_pub_key_hash(script: @ByteArray) -> bool { + if script.len() == constants::WITNESS_V1_PUB_KEY_HASH_LEN + && script[0] == Opcode::OP_1 + && script[1] == Opcode::OP_DATA_32 { + return true; // remove 2 first bytes ? + } + false +} diff --git a/packages/engine/src/taproot.cairo b/packages/engine/src/taproot.cairo index df71d0f8..5a359465 100644 --- a/packages/engine/src/taproot.cairo +++ b/packages/engine/src/taproot.cairo @@ -2,9 +2,9 @@ use crate::errors::Error; use crate::transaction::{ EngineTransactionTrait, EngineTransactionInputTrait, EngineTransactionOutputTrait, }; +use crate::signature::{schnorr, taproot_signature::{TaprootSigVerifierImpl}}; use crate::engine::Engine; -use crate::signature::schnorr; -use crate::signature::signature::{TaprootSigVerifierImpl}; + use starknet::secp256k1::{Secp256k1Point}; #[derive(Destruct)] @@ -152,8 +152,13 @@ pub impl TaprootContextImpl of TaprootContextTrait { +Drop, +Drop, +Drop, + +Default, >( - ref vm: Engine, witness_program: @ByteArray, raw_sig: @ByteArray, tx: @T, tx_idx: u32, + ref engine: Engine, + witness_program: @ByteArray, + raw_sig: @ByteArray, + tx: @T, + tx_idx: u32, ) -> Result<(), felt252> { let witness: Span = tx.get_transaction_inputs()[tx_idx].get_witness(); let mut annex = @""; @@ -162,12 +167,13 @@ pub impl TaprootContextImpl of TaprootContextTrait { } let mut verifier = TaprootSigVerifierImpl::< - I, O, T, - >::new(ref vm, raw_sig, witness_program, annex)?; - let is_valid = TaprootSigVerifierImpl::::verify(ref verifier); - if !is_valid { + T, + >::new(raw_sig, witness_program, annex, ref engine)?; // mut ? + let is_valid = TaprootSigVerifierImpl::::verify(verifier); + if is_valid.is_err() { return Result::Err(Error::TAPROOT_INVALID_SIG); } + // if verify.sigvalid Ok() else error invalid sig Result::Ok(()) } diff --git a/packages/engine/src/tests/test_schnorr.cairo b/packages/engine/src/tests/test_schnorr.cairo index 708d9e86..3aac90bf 100644 --- a/packages/engine/src/tests/test_schnorr.cairo +++ b/packages/engine/src/tests/test_schnorr.cairo @@ -2,7 +2,6 @@ use crate::signature::schnorr::verify_schnorr; use crate::errors::Error; use starknet::secp256_trait::Signature; use shinigami_utils::byte_array::U256IntoByteArray; - // Test data adapted from: https://github.com/bitcoin/bips/blob/master/bip-0340/test-vectors.csv #[test] @@ -14,7 +13,7 @@ fn test_schnorr_verify_0() { }; let pk: u256 = 0xf9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9; let m: u256 = 0x0; - assert!(verify_schnorr(sig, m.into(), pk.into()).unwrap()); + assert!(verify_schnorr(sig, @m.into(), @pk.into()).unwrap()); } #[test] @@ -26,7 +25,7 @@ fn test_schnorr_verify_1() { }; let pk: u256 = 0xdff1d77f2a671c5f36183726db2341be58feae1da2deced843240f7b502ba659; let m: u256 = 0x243f6a8885a308d313198a2e03707344a4093822299f31d0082efa98ec4e6c89; - assert!(verify_schnorr(sig, m.into(), pk.into()).unwrap()); + assert!(verify_schnorr(sig, @m.into(), @pk.into()).unwrap()); } #[test] @@ -38,7 +37,7 @@ fn test_schnorr_verify_2() { }; let pk: u256 = 0xdd308afec5777e13121fa72b9cc1b7cc0139715309b086c960e18fd969774eb8; let m: u256 = 0x7e2d58d8b3bcdf1abadec7829054f90dda9805aab56c77333024b9d0a508b75c; - assert!(verify_schnorr(sig, m.into(), pk.into()).unwrap()); + assert!(verify_schnorr(sig, @m.into(), @pk.into()).unwrap()); } #[test] @@ -50,7 +49,7 @@ fn test_schnorr_verify_3() { }; let pk: u256 = 0x25d1dff95105f5253c4022f628a996ad3a0d95fbf21d468a1b33f8c160d8f517; let m: u256 = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff; - assert!(verify_schnorr(sig, m.into(), pk.into()).unwrap()); + assert!(verify_schnorr(sig, @m.into(), @pk.into()).unwrap()); } #[test] @@ -62,7 +61,7 @@ fn test_schnorr_verify_4() { }; let pk: u256 = 0xd69c3509bb99e412e68b0fe8544e72837dfa30746d8be2aa65975f29d22dc7b9; let m: u256 = 0x4df3c3f68fcc83b27e9d42c90431a72499f17875c81a599b566c9889b9696703; - assert!(verify_schnorr(sig, m.into(), pk.into()).unwrap()); + assert!(verify_schnorr(sig, @m.into(), @pk.into()).unwrap()); } #[test] @@ -76,7 +75,7 @@ fn test_schnorr_verify_5() { }; let pk: u256 = 0xeefdea4cdb677750a420fee807eacf21eb9898ae79b9768766e4faa04a2d4a34; let m: u256 = 0x243f6a8885a308d313198a2e03707344a4093822299f31d0082efa98ec4e6c89; - verify_schnorr(sig, m.into(), pk.into()).unwrap_err(); + verify_schnorr(sig, @m.into(), @pk.into()).unwrap_err(); } #[test] @@ -89,7 +88,7 @@ fn test_schnorr_verify_6() { }; let pk: u256 = 0xdff1d77f2a671c5f36183726db2341be58feae1da2deced843240f7b502ba659; let m: u256 = 0x243f6a8885a308d313198a2e03707344a4093822299f31d0082efa98ec4e6c89; - assert_eq!(verify_schnorr(sig, m.into(), pk.into()).unwrap(), false); + assert_eq!(verify_schnorr(sig, @m.into(), @pk.into()).unwrap(), false); } #[test] @@ -102,7 +101,7 @@ fn test_schnorr_verify_7() { }; let pk: u256 = 0xdff1d77f2a671c5f36183726db2341be58feae1da2deced843240f7b502ba659; let m: u256 = 0x243f6a8885a308d313198a2e03707344a4093822299f31d0082efa98ec4e6c89; - assert_eq!(verify_schnorr(sig, m.into(), pk.into()).unwrap(), false); + assert_eq!(verify_schnorr(sig, @m.into(), @pk.into()).unwrap(), false); } #[test] @@ -115,7 +114,7 @@ fn test_schnorr_verify_8() { }; let pk: u256 = 0xdff1d77f2a671c5f36183726db2341be58feae1da2deced843240f7b502ba659; let m: u256 = 0x243f6a8885a308d313198a2e03707344a4093822299f31d0082efa98ec4e6c89; - assert_eq!(verify_schnorr(sig, m.into(), pk.into()).unwrap(), false); + assert_eq!(verify_schnorr(sig, @m.into(), @pk.into()).unwrap(), false); } #[test] @@ -129,7 +128,7 @@ fn test_schnorr_verify_9() { }; let pk: u256 = 0xdff1d77f2a671c5f36183726db2341be58feae1da2deced843240f7b502ba659; let m: u256 = 0x243f6a8885a308d313198a2e03707344a4093822299f31d0082efa98ec4e6c89; - assert_eq!(verify_schnorr(sig, m.into(), pk.into()).unwrap(), false); + assert_eq!(verify_schnorr(sig, @m.into(), @pk.into()).unwrap(), false); } #[test] @@ -143,7 +142,7 @@ fn test_schnorr_verify_10() { }; let pk: u256 = 0xdff1d77f2a671c5f36183726db2341be58feae1da2deced843240f7b502ba659; let m: u256 = 0x243f6a8885a308d313198a2e03707344a4093822299f31d0082efa98ec4e6c89; - assert_eq!(verify_schnorr(sig, m.into(), pk.into()).unwrap(), false); + assert_eq!(verify_schnorr(sig, @m.into(), @pk.into()).unwrap(), false); } #[test] @@ -156,7 +155,7 @@ fn test_schnorr_verify_11() { }; let pk: u256 = 0xdff1d77f2a671c5f36183726db2341be58feae1da2deced843240f7b502ba659; let m: u256 = 0x243f6a8885a308d313198a2e03707344a4093822299f31d0082efa98ec4e6c89; - assert_eq!(verify_schnorr(sig, m.into(), pk.into()).unwrap(), false); + assert_eq!(verify_schnorr(sig, @m.into(), @pk.into()).unwrap(), false); } #[test] @@ -169,7 +168,7 @@ fn test_schnorr_verify_12() { }; let pk: u256 = 0xdff1d77f2a671c5f36183726db2341be58feae1da2deced843240f7b502ba659; let m: u256 = 0x243f6a8885a308d313198a2e03707344a4093822299f31d0082efa98ec4e6c89; - let result = verify_schnorr(sig, m.into(), pk.into()).unwrap_err(); + let result = verify_schnorr(sig, @m.into(), @pk.into()).unwrap_err(); let expected = Error::SCHNORR_INVALID_SIG_R_FIELD; assert_eq!(result, expected); } @@ -184,7 +183,7 @@ fn test_schnorr_verify_13() { }; let pk: u256 = 0xdff1d77f2a671c5f36183726db2341be58feae1da2deced843240f7b502ba659; let m: u256 = 0x243f6a8885a308d313198a2e03707344a4093822299f31d0082efa98ec4e6c89; - let result = verify_schnorr(sig, m.into(), pk.into()).unwrap_err(); + let result = verify_schnorr(sig, @m.into(), @pk.into()).unwrap_err(); let expected = Error::SCHNORR_INVALID_SIG_SIZE; assert_eq!(result, expected); } @@ -200,5 +199,5 @@ fn test_schnorr_verify_14() { }; let pk: u256 = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30; let m: u256 = 0x243f6a8885a308d313198a2e03707344a4093822299f31d0082efa98ec4e6c89; - verify_schnorr(sig, m.into(), pk.into()).unwrap_err(); + verify_schnorr(sig, @m.into(), @pk.into()).unwrap_err(); } diff --git a/packages/engine/src/tests/test_taproot_hash.cairo b/packages/engine/src/tests/test_taproot_hash.cairo new file mode 100644 index 00000000..be87fd4e --- /dev/null +++ b/packages/engine/src/tests/test_taproot_hash.cairo @@ -0,0 +1,395 @@ +use crate::signature::sighash::{ + calc_taproot_signature_hash, TaprootSighashOptions, BASE_SIGHASH_EXT_FLAG, + TAPSCRIPT_SIGHASH_EXT_FLAG, +}; + +use crate::transaction::{ + EngineTransactionOutput, EngineTransaction, EngineTransactionInput, EngineOutPoint, + EngineInternalTransactionTrait, UTXO, +}; +use crate::hash_cache::{TxSigHashes, SigHashMidstateTrait}; +use shinigami_engine::utxo::{}; +use shinigami_utils::bytecode::hex_to_bytecode; +use shinigami_utils::byte_array::{U256IntoByteArray}; + + +#[test] +fn test_new_sigHashMidstate() { + // https://github.com/bitcoin/bips/blob/master/bip-0341/wallet-test-vectors.json#l227 + let raw_transaction_hex = + "0x02000000097de20cbff686da83a54981d2b9bab3586f4ca7e48f57f5b55963115f3b334e9c010000000000000000d7b7cab57b1393ace2d064f4d4a2cb8af6def61273e127517d44759b6dafdd990000000000fffffffff8e1f583384333689228c5d28eac13366be082dc57441760d957275419a418420000000000fffffffff0689180aa63b30cb162a73c6d2a38b7eeda2a83ece74310fda0843ad604853b0100000000feffffffaa5202bdf6d8ccd2ee0f0202afbbb7461d9264a25e5bfd3c5a52ee1239e0ba6c0000000000feffffff956149bdc66faa968eb2be2d2faa29718acbfe3941215893a2a3446d32acd050000000000000000000e664b9773b88c09c32cb70a2a3e4da0ced63b7ba3b22f848531bbb1d5d5f4c94010000000000000000e9aa6b8e6c9de67619e6a3924ae25696bb7b694bb677a632a74ef7eadfd4eabf0000000000ffffffffa778eb6a263dc090464cd125c466b5a99667720b1c110468831d058aa1b82af10100000000ffffffff0200ca9a3b000000001976a91406afd46bcdfd22ef94ac122aa11f241244a37ecc88ac807840cb0000000020ac9a87f5594be208f8532db38cff670c450ed2fea8fcdefcc9a663f78bab962b0065cd1d"; + let raw_transaction = hex_to_bytecode(@raw_transaction_hex); + + let utxos = array![ + UTXO { + amount: 420000000, + pubkey_script: hex_to_bytecode( + @"0x512053a1f6e454df1aa2776a2814a721372d6258050de330b3c6d10ee8f4e0dda343", + ), + block_height: Default::default(), + }, + UTXO { + amount: 462000000, + pubkey_script: hex_to_bytecode( + @"0x5120147c9c57132f6e7ecddba9800bb0c4449251c92a1e60371ee77557b6620f3ea3", + ), + block_height: Default::default(), + }, + UTXO { + amount: 294000000, + pubkey_script: hex_to_bytecode(@"0x76a914751e76e8199196d454941c45d1b3a323f1433bd688ac"), + block_height: Default::default(), + }, + UTXO { + amount: 504000000, + pubkey_script: hex_to_bytecode( + @"0x5120e4d810fd50586274face62b8a807eb9719cef49c04177cc6b76a9a4251d5450e", + ), + block_height: Default::default(), + }, + UTXO { + amount: 630000000, + pubkey_script: hex_to_bytecode( + @"0x512091b64d5324723a985170e4dc5a0f84c041804f2cd12660fa5dec09fc21783605", + ), + block_height: Default::default(), + }, + UTXO { + amount: 378000000, + pubkey_script: hex_to_bytecode(@"0x00147dd65592d0ab2fe0d0257d571abf032cd9db93dc"), + block_height: Default::default(), + }, + UTXO { + amount: 672000000, + pubkey_script: hex_to_bytecode( + @"0x512075169f4001aa68f15bbed28b218df1d0a62cbbcf1188c6665110c293c907b831", + ), + block_height: Default::default(), + }, + UTXO { + amount: 546000000, + pubkey_script: hex_to_bytecode( + @"0x5120712447206d7a5238acc7ff53fbe94a3b64539ad291c7cdbc490b7577e4b17df5", + ), + block_height: Default::default(), + }, + UTXO { + amount: 588000000, + pubkey_script: hex_to_bytecode( + @"0x512077e30a5522dd9f894c3f8b8bd4c4b2cf82ca7da8a3ea6a239655c39c050ab220", + ), + block_height: Default::default(), + }, + ]; + + let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction, 0, utxos); + let sig_hash = SigHashMidstateTrait::new(@transaction); + + let expected_hash_prevouts = + 0xe3b33bb4ef3a52ad1fffb555c0d82828eb22737036eaeb02a235d82b909c4c3f_u256; + let expected_hash_sequence = + 0x18959c7221ab5ce9e26c3cd67b22c24f8baa54bac281d8e6b05e400e6c3a957e_u256; + let expected_hash_outputs = + 0xa2e6dab7c1f0dcd297c8d61647fd17d821541ea69c3cc37dcbad7f90d4eb4bc5_u256; + + let exepected_hash_amount = + 0x58a6964a4f5f8f0b642ded0a8a553be7622a719da71d1f5befcefcdee8e0fde6_u256; + let expected_hash_script_pubkeys = + 0x23ad0f61ad2bca5ba6a7693f50fce988e17c3780bf2b1e720cfbb38fbdd52e21_u256; + + assert_eq!(sig_hash.taproot.hash_prevouts_v1, expected_hash_prevouts); + assert_eq!(sig_hash.taproot.hash_sequence_v1, expected_hash_sequence); + assert_eq!(sig_hash.taproot.hash_outputs_v1, expected_hash_outputs); + + assert_eq!(sig_hash.taproot.hash_input_scripts_v1, expected_hash_script_pubkeys); + assert_eq!(sig_hash.taproot.hash_input_amounts_v1, exepected_hash_amount); +} + +#[test] +fn test_calc_taproot_signature_hash_key_path_spend() { + // https://learnmeabitcoin.com/technical/upgrades/taproot/#examples + // txid 091d2aaadc409298fd8353a4cd94c319481a0b4623fb00872fe240448e93fcbe input 0 + let h_type: u32 = 0x01; // SIGHASH_ALL + let transaction = EngineTransaction { + txid: 0, + version: 2, + transaction_inputs: array![ + EngineTransactionInput { + previous_outpoint: EngineOutPoint { + txid: 0xec9016580d98a93909faf9d2f431e74f781b438d81372bb6aab4db67725c11a7_u256, //le + vout: 0, + }, + signature_script: Default::default(), + sequence: 0xffffffff, + witness: array![ + hex_to_bytecode( + @"0xb693a0797b24bae12ed0516a2f5ba765618dca89b75e498ba5b745b71644362298a45ca39230d10a02ee6290a91cebf9839600f7e35158a447ea182ea0e022ae01", + ), + ], + }, + ], + transaction_outputs: array![ + EngineTransactionOutput { + value: 10000, + publickey_script: hex_to_bytecode( + @"0x00144e44ca792ce545acba99d41304460dd1f53be384", + ), + }, + ], + locktime: 0, + utxos: array![ + UTXO { + amount: 20000, + pubkey_script: hex_to_bytecode( + @"0x51200f0c8db753acbd17343a39c2f3f4e35e4be6da749f9e35137ab220e7b238a667", + ), + block_height: 861957, + }, + ], + }; + + let sig_hashes: TxSigHashes = SigHashMidstateTrait::new(@transaction); + let input_idx: u32 = 0; + let prev_output: EngineTransactionOutput = Default::default(); + + let mut opts = TaprootSighashOptions { + ext_flag: BASE_SIGHASH_EXT_FLAG, // key path spend + annex_hash: @"", + tap_leaf_hash: @"", + key_version: 0, + code_sep_pos: 0, + }; + + let result = calc_taproot_signature_hash( + sig_hashes, h_type, @transaction, input_idx, prev_output, ref opts, + ); + let expected_hash = 0xa7b390196945d71549a2454f0185ece1b47c56873cf41789d78926852c355132_u256; + + assert_eq!(result.is_ok(), true); + assert_eq!(result.unwrap(), expected_hash); +} + +// no signature so useless ? +#[test] +fn test_calc_taproot_signature_hash_script_path_spend_simple() { + // txid 5ff05f74d385bd39e344329330461f74b390c1b5ead87c4f51b40c555b75719d input 1 + let h_type: u32 = 0x01; // SIGHASH_ALL + let transaction = EngineTransaction { + txid: 0, + version: 2, + transaction_inputs: array![ + EngineTransactionInput { + previous_outpoint: EngineOutPoint { + txid: 0xC20DA20832C3894854DC63F69CF7FE805323B3D476AAA8E730244B36A575D244_u256, //le + vout: 0, + }, + signature_script: Default::default(), + sequence: 0xffffffff, + witness: array![ + hex_to_bytecode( + @"0x304402200c4c0bfe93f6622fa0790b6d28bf755c1a3f23e8404bb804ca8e2db080b613b102205bcf0a4e4559ba9b40e6b174cf91af061dfa21691923b410e351326708b041a001", + ), + hex_to_bytecode( + @"0x030c7196376bc1df61b6da6ee711868fd30e370dd273332bfb02a2287d11e2e9c5" //verify + ), + ], + }, + EngineTransactionInput { + previous_outpoint: EngineOutPoint { + txid: 0x87ADAA9D7302D05896B0D491A099208C20EA0AC9FA776DDF4B7CAFCAFAF8C48B_u256, //le + vout: 1, + }, + signature_script: Default::default(), + sequence: 0xffffffff, + witness: array![ + hex_to_bytecode(@"0x08"), + hex_to_bytecode(@"0x5887"), + hex_to_bytecode( + @"0xc1924c163b385af7093440184af6fd6244936d1288cbb41cc3812286d3f83a3329", + ), + ], + }, + ], + transaction_outputs: array![ + EngineTransactionOutput { + value: 3599, + publickey_script: hex_to_bytecode( + @"0x001492b8c3a56fac121ddcdffbc85b02fb9ef681038a", + ), + }, + ], + locktime: 0, + utxos: array![ + UTXO { + amount: 999, + pubkey_script: hex_to_bytecode(@"0x001492b8c3a56fac121ddcdffbc85b02fb9ef681038a"), + block_height: 862105, + }, + UTXO { + amount: 20000, + pubkey_script: hex_to_bytecode( + @"0x51201baeaaf9047cc42055a37a3ac981bdf7f5ab96fad0d2d07c54608e8a181b9477", + ), + block_height: 862100, + }, + ], + }; + + let input_idx: u32 = 1; + let sig_hashes: TxSigHashes = SigHashMidstateTrait::new(@transaction); + let prev_output: EngineTransactionOutput = Default::default(); + + let mut opts = TaprootSighashOptions { + ext_flag: TAPSCRIPT_SIGHASH_EXT_FLAG, // script path spend + annex_hash: @"", + tap_leaf_hash: @hex_to_bytecode( + @"0xe4b47d76d2f78791323a035811c350bb7875568006cb60f0e171efb70c11bda4", + ), + key_version: 0, + code_sep_pos: 0, + }; + + let result = calc_taproot_signature_hash( + sig_hashes, h_type, @transaction, input_idx, prev_output, ref opts, + ); + // println!("result: {:x}", result.unwrap()); + + assert_eq!(result.is_ok(), true); +} + +#[test] +fn test_calc_taproot_signature_hash_script_path_spend_signature() { + // txid 797505b104b5fb840931c115ea35d445eb1f64c9279bf23aa5bb4c3d779da0c2 input 0 + let input_idx: u32 = 0; + let h_type: u32 = 0x01; // SIGHASH_ALL + let transaction = EngineTransaction { + txid: 0, + version: 2, + transaction_inputs: array![ + EngineTransactionInput { + previous_outpoint: EngineOutPoint { + txid: 0x3CFE8B95D22502698FD98837F83D8D4BE31EE3EDDD9D1AB1A95654C64604C4D1_u256, //le + vout: 0, + }, + signature_script: Default::default(), + sequence: 0xffffffff, + witness: array![ + hex_to_bytecode( + @"0x01769105cbcbdcaaee5e58cd201ba3152477fda31410df8b91b4aee2c4864c7700615efb425e002f146a39ca0a4f2924566762d9213bd33f825fad83977fba7f01", + ), + hex_to_bytecode( + @"0x206d4ddc0e47d2e8f82cbe2fc2d0d749e7bd3338112cecdc76d8f831ae6620dbe0ac", + ), + hex_to_bytecode( + @"0xc0924c163b385af7093440184af6fd6244936d1288cbb41cc3812286d3f83a3329", + ), + ], + }, + ], + transaction_outputs: array![ + EngineTransactionOutput { + value: 15000, + publickey_script: hex_to_bytecode( + @"0x00140de745dc58d8e62e6f47bde30cd5804a82016f9e", + ), + }, + ], + locktime: 0, + utxos: array![ + UTXO { + amount: 20000, + pubkey_script: hex_to_bytecode( + @"0x5120f3778defe5173a9bf7169575116224f961c03c725c0e98b8da8f15df29194b80", + ), + block_height: 863496, + }, + ], + }; + + let sig_hashes: TxSigHashes = SigHashMidstateTrait::new(@transaction); + let prev_output: EngineTransactionOutput = Default::default(); + + let mut opts = TaprootSighashOptions { + ext_flag: TAPSCRIPT_SIGHASH_EXT_FLAG, + annex_hash: @"", + tap_leaf_hash: @hex_to_bytecode( + @"0x858dfe26a3dd48a2c1fcee1d631f0aadf6a61135fc51f75758e945bca534ef16", + ), + key_version: 0, + code_sep_pos: 0xffffffff, + }; + + let result = calc_taproot_signature_hash( + sig_hashes, h_type, @transaction, input_idx, prev_output, ref opts, + ); + let expected_hash = 0x752453d473e511a0da2097d664d69fe5eb89d8d9d00eab924b42fc0801a980c9_u256; + + assert_eq!(result.is_ok(), true); + assert_eq!(result.unwrap(), expected_hash); +} + +// no signature so useless ? +#[test] +fn test_calc_taproot_signature_hash_script_path_spend_tree() { + // txid 992af7eb67f37a4dfaa64ea6f03a70c35b6063ba5ee3fe41734c3460b4006463 input 0 + let input_idx: u32 = 0; + let h_type: u32 = 0x01; // SIGHASH_ALL + let transaction = EngineTransaction { + txid: 0, + version: 2, + transaction_inputs: array![ + EngineTransactionInput { + previous_outpoint: EngineOutPoint { + txid: 0xD7C0AA93D852C70ED440C5295242C2AC06F41C3A2A174B5A5B112CEBDF0F7BEC_u256, //le + vout: 0, + }, + signature_script: Default::default(), + sequence: 0xffffffff, + witness: array![ + hex_to_bytecode(@"0x03"), + hex_to_bytecode(@"0x5387"), + hex_to_bytecode( + @"0xc0924c163b385af7093440184af6fd6244936d1288cbb41cc3812286d3f83a33291324300a84045033ec539f60c70d582c48b9acf04150da091694d83171b44ec9bf2c4bf1ca72f7b8538e9df9bdfd3ba4c305ad11587f12bbfafa00d58ad6051d54962df196af2827a86f4bde3cf7d7c1a9dcb6e17f660badefbc892309bb145f", + ), + ], + }, + ], + transaction_outputs: array![ + EngineTransactionOutput { + value: 294, + publickey_script: hex_to_bytecode( + @"0x001492b8c3a56fac121ddcdffbc85b02fb9ef681038a", + ), + }, + ], + locktime: 0, + utxos: array![ + UTXO { + amount: 10000, + pubkey_script: hex_to_bytecode( + @"0x5120979cff99636da1b0e49f8711514c642f640d1f64340c3784942296368fadd0a5", + ), + block_height: 863496, + }, + ], + }; + + let sig_hashes: TxSigHashes = SigHashMidstateTrait::new(@transaction); + let prev_output: EngineTransactionOutput = Default::default(); + + let mut opts = TaprootSighashOptions { + ext_flag: TAPSCRIPT_SIGHASH_EXT_FLAG, + annex_hash: @"", + tap_leaf_hash: @hex_to_bytecode( + @"0x160bd30406f8d5333be044e6d2d14624470495da8a3f91242ce338599b233931", + ), + key_version: 0, + code_sep_pos: 0xffffffff, + }; + + let result = calc_taproot_signature_hash( + sig_hashes, h_type, @transaction, input_idx, prev_output, ref opts, + ); + + assert_eq!(result.is_ok(), true); +} diff --git a/packages/engine/src/transaction.cairo b/packages/engine/src/transaction.cairo index 160ec188..e1bb4c92 100644 --- a/packages/engine/src/transaction.cairo +++ b/packages/engine/src/transaction.cairo @@ -4,14 +4,22 @@ use shinigami_utils::bytecode::{bytecode_to_hex, read_var_int, write_var_int}; use shinigami_utils::bit_shifts::shr; use shinigami_utils::hash::double_sha256; +#[derive(Debug, Drop, Clone, Default)] +pub struct UTXO { + pub amount: i64, + pub pubkey_script: ByteArray, + pub block_height: u32, + // TODO: flags? +} + // Tracks previous transaction outputs -#[derive(Drop, Copy)] +#[derive(Drop, Copy, Default)] pub struct EngineOutPoint { pub txid: u256, pub vout: u32, } -#[derive(Drop, Clone)] +#[derive(Drop, Clone, Default)] pub struct EngineTransactionInput { pub previous_outpoint: EngineOutPoint, pub signature_script: ByteArray, @@ -19,7 +27,7 @@ pub struct EngineTransactionInput { pub sequence: u32, } -#[derive(Drop, Clone)] +#[derive(Drop, Clone, Default)] pub struct EngineTransactionOutput { pub value: i64, pub publickey_script: ByteArray, @@ -33,6 +41,9 @@ pub struct EngineTransaction { pub transaction_inputs: Array, pub transaction_outputs: Array, pub locktime: u32, + // TODO replace UTXO by EngineTransactionOutput? + pub txid: u256, + pub utxos: Array, } pub trait EngineInternalTransactionTrait { @@ -41,14 +52,27 @@ pub trait EngineInternalTransactionTrait { transaction_inputs: Array, transaction_outputs: Array, locktime: u32, + txid: u256, + utxos: Array, + ) -> EngineTransaction; + fn new_signed( + script_sig: ByteArray, pubkey_script: ByteArray, txid: u256, utxos: Array, ) -> EngineTransaction; - fn new_signed(script_sig: ByteArray, pubkey_script: ByteArray) -> EngineTransaction; fn new_signed_witness( - script_sig: ByteArray, pubkey_script: ByteArray, witness: Array, value: i64, + script_sig: ByteArray, + pubkey_script: ByteArray, + witness: Array, + value: i64, + txid: u256, + utxos: Array, ) -> EngineTransaction; - fn btc_decode(raw: ByteArray, encoding: u32) -> EngineTransaction; - fn deserialize(raw: ByteArray) -> EngineTransaction; - fn deserialize_no_witness(raw: ByteArray) -> EngineTransaction; + fn btc_decode( + raw: ByteArray, encoding: u32, txid: u256, utxos: Array, + ) -> EngineTransaction; + fn deserialize(raw: ByteArray, txid: u256, utxos: Array) -> EngineTransaction; + fn deserialize_no_witness( + raw: ByteArray, txid: u256, utxos: Array, + ) -> EngineTransaction; //never used ? fn btc_encode(self: EngineTransaction, encoding: u32) -> ByteArray; fn serialize(self: EngineTransaction) -> ByteArray; fn serialize_no_witness(self: EngineTransaction) -> ByteArray; @@ -69,16 +93,22 @@ pub impl EngineInternalTransactionImpl of EngineInternalTransactionTrait { transaction_inputs: Array, transaction_outputs: Array, locktime: u32, + txid: u256, + utxos: Array, ) -> EngineTransaction { EngineTransaction { version: version, transaction_inputs: transaction_inputs, transaction_outputs: transaction_outputs, locktime: locktime, + txid: txid, + utxos: utxos, } } - fn new_signed(script_sig: ByteArray, pubkey_script: ByteArray) -> EngineTransaction { + fn new_signed( + script_sig: ByteArray, pubkey_script: ByteArray, txid: u256, utxos: Array, + ) -> EngineTransaction { let coinbase_tx_inputs = array![ EngineTransactionInput { previous_outpoint: EngineOutPoint { txid: 0x0, vout: 0xffffffff }, @@ -95,6 +125,8 @@ pub impl EngineInternalTransactionImpl of EngineInternalTransactionTrait { transaction_inputs: coinbase_tx_inputs, transaction_outputs: coinbase_tx_outputs, locktime: 0, + txid: 0, + utxos: Default::default(), }; let coinbase_bytes = coinbase_tx.serialize_no_witness(); let coinbase_txid = double_sha256(@coinbase_bytes); @@ -110,6 +142,8 @@ pub impl EngineInternalTransactionImpl of EngineInternalTransactionTrait { ], transaction_outputs: array![EngineTransactionOutput { value: 0, publickey_script: "" }], locktime: 0, + txid: txid, + utxos: utxos, }; // let transaction = EngineTransaction { // version: 1, @@ -128,7 +162,12 @@ pub impl EngineInternalTransactionImpl of EngineInternalTransactionTrait { } fn new_signed_witness( - script_sig: ByteArray, pubkey_script: ByteArray, witness: Array, value: i64, + script_sig: ByteArray, + pubkey_script: ByteArray, + witness: Array, + value: i64, + txid: u256, + utxos: Array, ) -> EngineTransaction { let coinbase_tx_inputs = array![ EngineTransactionInput { @@ -146,6 +185,8 @@ pub impl EngineInternalTransactionImpl of EngineInternalTransactionTrait { transaction_inputs: coinbase_tx_inputs, transaction_outputs: coinbase_tx_outputs, locktime: 0, + txid: 0, + utxos: Default::default(), }; let coinbase_bytes = coinbase_tx.serialize_no_witness(); let coinbase_txid = double_sha256(@coinbase_bytes); @@ -163,12 +204,16 @@ pub impl EngineInternalTransactionImpl of EngineInternalTransactionTrait { EngineTransactionOutput { value: value, publickey_script: "" }, ], locktime: 0, + txid: txid, + utxos: utxos, }; transaction } // Deserialize a transaction from a byte array. - fn btc_decode(raw: ByteArray, encoding: u32) -> EngineTransaction { + fn btc_decode( + raw: ByteArray, encoding: u32, txid: u256, utxos: Array, + ) -> EngineTransaction { let mut offset: usize = 0; let version: i32 = byte_array_value_at_le(@raw, ref offset, 4).try_into().unwrap(); if encoding == WITNESS_ENCODING { @@ -213,7 +258,6 @@ pub impl EngineInternalTransactionImpl of EngineInternalTransactionTrait { }; let mut inputs_with_witness: Array = array![]; - if encoding == WITNESS_ENCODING { // one witness for each input i = 0; @@ -242,31 +286,36 @@ pub impl EngineInternalTransactionImpl of EngineInternalTransactionTrait { version: version, transaction_inputs: inputs_with_witness, transaction_outputs: outputs, - locktime: locktime, + locktime, + txid, + utxos, } } else { EngineTransaction { version: version, transaction_inputs: inputs, transaction_outputs: outputs, - locktime: locktime, + locktime, + txid, + utxos, } } } - fn deserialize(raw: ByteArray) -> EngineTransaction { + fn deserialize(raw: ByteArray, txid: u256, utxos: Array) -> EngineTransaction { let mut offset: usize = 0; let _version: i32 = byte_array_value_at_le(@raw, ref offset, 4).try_into().unwrap(); let flags: u16 = byte_array_value_at_le(@raw, ref offset, 2).try_into().unwrap(); + if flags == 0x100 { - Self::btc_decode(raw, WITNESS_ENCODING) + Self::btc_decode(raw, WITNESS_ENCODING, txid, utxos) } else { - Self::btc_decode(raw, BASE_ENCODING) + Self::btc_decode(raw, BASE_ENCODING, txid, utxos) } } - fn deserialize_no_witness(raw: ByteArray) -> EngineTransaction { - Self::btc_decode(raw, BASE_ENCODING) + fn deserialize_no_witness(raw: ByteArray, txid: u256, utxos: Array) -> EngineTransaction { + Self::btc_decode(raw, BASE_ENCODING, txid, utxos) } // Serialize the transaction data for hashing based on encoding used. @@ -422,6 +471,8 @@ impl TransactionDefault of Default { transaction_inputs: array![default_txin], transaction_outputs: array![], locktime: 0, + txid: 0, + utxos: Default::default(), }; transaction } @@ -476,21 +527,49 @@ pub impl EngineTransactionOutputTraitInternalImpl of EngineTransactionOutputTrai } } +// #[derive(Drop, Copy, Default)] +// pub struct EngineDataFromRaito { +// pub txid: u256, +// } + +// pub trait TransactionDataFromRaitoTrait { +// fn set_data_from_raito(ref self: R, txid: u256); +// fn get_txid(self: @R) -> u256; +// fn get_utxos(self: @R) -> Array; +// } + +// pub impl TransactionDataFromRaitoImpl of TransactionDataFromRaitoTrait { +// fn set_data_from_raito(ref self: EngineDataFromRaito, txid: u256) { +// EngineDataFromRaito { txid: txid }; +// } + +// fn get_txid(self: @EngineDataFromRaito) -> u256 { +// *self.txid +// } +// } + pub trait EngineTransactionTrait< - T, I, O, +EngineTransactionInputTrait, +EngineTransactionOutputTrait, + T, I, O, // R, + +EngineTransactionInputTrait, +EngineTransactionOutputTrait, + // +TransactionDataFromRaitoTrait, > { fn get_version(self: @T) -> i32; fn get_transaction_inputs(self: @T) -> Span; fn get_transaction_outputs(self: @T) -> Span; fn get_locktime(self: @T) -> u32; + fn get_txid(self: @T) -> u256; + fn get_transaction_utxos(self: @T) -> Array; //Span? + fn get_input_utxo(self: @T, input_index: u32) -> UTXO; } pub impl EngineTransactionTraitInternalImpl of EngineTransactionTrait< EngineTransaction, EngineTransactionInput, EngineTransactionOutput, + // EngineDataFromRaito, EngineTransactionInputTraitInternalImpl, EngineTransactionOutputTraitInternalImpl, + // TransactionDataFromRaitoImpl, > { fn get_version(self: @EngineTransaction) -> i32 { *self.version @@ -507,4 +586,16 @@ pub impl EngineTransactionTraitInternalImpl of EngineTransactionTrait< fn get_locktime(self: @EngineTransaction) -> u32 { *self.locktime } + + fn get_txid(self: @EngineTransaction) -> u256 { + *self.txid + } + + fn get_transaction_utxos(self: @EngineTransaction) -> Array { + self.utxos.clone() + } + + fn get_input_utxo(self: @EngineTransaction, input_index: u32) -> UTXO { + self.get_transaction_utxos().at(input_index).clone() + } } diff --git a/packages/tests/src/lib.cairo b/packages/tests/src/lib.cairo index 0cc3c2c1..19bafef1 100644 --- a/packages/tests/src/lib.cairo +++ b/packages/tests/src/lib.cairo @@ -1,5 +1,5 @@ pub mod validate; -pub mod utxo; +// pub mod utxo; pub mod utils; #[cfg(test)] pub mod tests { diff --git a/packages/tests/src/tests/opcodes/test_crypto.cairo b/packages/tests/src/tests/opcodes/test_crypto.cairo index 2c274850..7329f826 100644 --- a/packages/tests/src/tests/opcodes/test_crypto.cairo +++ b/packages/tests/src/tests/opcodes/test_crypto.cairo @@ -468,3 +468,108 @@ fn test_op_checkmultisig_dummy_not_zero() { ); check_dstack_size(ref engine, 0); } +// Taproot test +// #[test] +// fn test_op_checksigadd() { +// let script_sig = +// "OP_DATA_71 +// 0x3044022008f4f37e2d8f74e18c1b8fde2374d5f28402fb8ab7fd1cc5b786aa40851a70cb02201f40afd1627798ee8529095ca4b205498032315240ac322c9d8ff0f205a93a5801 +// OP_DATA_33 0x024aeaf55040fa16de37303d13ca1dde85f4ca9baa36e2963a27a1c0c1165fe2b1"; +// let script_pubkey = "OP_1 OP_CHECKSIGADD OP_CHECKSIG"; +// let mut transaction = mock_transaction_legacy_p2pkh(script_sig); +// let mut engine = test_compile_and_run_with_tx(script_pubkey, transaction); +// check_dstack_size(ref engine, 1); +// let expected_stack = array![ScriptNum::wrap(1)]; +// check_expected_dstack(ref engine, expected_stack.span()); +// } + +// #[test] +// fn test_op_checksigadd_valid() { +// let script_sig = +// "OP_DATA_71 +// 0x3044022008f4f37e2d8f74e18c1b8fde2374d5f28402fb8ab7fd1cc5b786aa40851a70cb02201f40afd1627798ee8529095ca4b205498032315240ac322c9d8ff0f205a93a5801 +// OP_DATA_33 0x024aeaf55040fa16de37303d13ca1dde85f4ca9baa36e2963a27a1c0c1165fe2b1"; +// let script_pubkey = "OP_1 OP_SWAP OP_CHECKSIGADD"; +// let mut transaction = utils::mock_transaction(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(2)]; +// utils::check_expected_dstack(ref engine, expected_stack.span()); +// } + +// #[test] +// fn test_op_checksigadd_invalid_script_num() { +// let script_sig = +// "OP_DATA_71 +// 0x3044022008f4f37e2d8f74e18c1b8fde2374d5f28402fb8ab7fd1cc5b786aa40851a70cb02201f40afd1627798ee8529095ca4b205498032315240ac322c9d8ff0f205a93a5801 +// OP_DATA_33 0x024aeaf55040fa16de37303d13ca1dde85f4ca9baa36e2963a27a1c0c1165fe2b1"; +// let script_pubkey = "OP_DATA_5 0x0102030405 OP_SWAP OP_CHECKSIGADD"; +// let mut transaction = utils::mock_transaction(script_sig); +// let mut engine = utils::test_compile_and_run_with_tx_err( +// script_pubkey, transaction, 'InvalidScriptNum', +// ); +// utils::check_dstack_size(ref engine, 0); +// } + +// #[test] +// fn test_op_checksigadd_invalid_signature() { +// let script_sig = +// "OP_DATA_71 +// 0x3044022008f4f37e2d8f74f18c1b8fde2374d5f28402fb8ab7fd1cc5b786aa40851a70cb02201f40afd1627798ee8529095ca4b205498032315240ac322c9d8ff0f205a93a5801 +// OP_DATA_33 0x024aeaf55040fa16de37303d13ca1dde85f4ca9baa36e2963a27a1c0c1165fe2b1"; +// let script_pubkey = "OP_1 OP_SWAP OP_CHECKSIGADD"; +// let mut transaction = utils::mock_transaction(script_sig); +// let mut engine = utils::test_compile_and_run_with_tx_err( +// script_pubkey, transaction, 'InvalidSignature', +// ); +// 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_checksigadd_invalid_hash_type() { +// let script_sig = +// "OP_DATA_71 +// 0x3044022008f4f37e2d8f74e18c1b8fde2374d5f28402fb8ab7fd1cc5b786aa40851a70cb02201f40afd1627798ee8529095ca4b205498032315240ac322c9d8ff0f205a93a5807 +// OP_DATA_33 0x024aeaf55040fa16de37303d13ca1dde85f4ca9baa36e2963a27a1c0c1165fe2b1"; +// let script_pubkey = "OP_1 OP_SWAP OP_CHECKSIGADD"; +// let mut transaction = utils::mock_transaction(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(1)]; +// utils::check_expected_dstack(ref engine, expected_stack.span()); +// } + +// #[test] +// fn test_op_checksigadd_empty_signature() { +// let script_sig = +// "OP_0 OP_DATA_33 0x024aeaf55040fa16de37303d13ca1dde85f4ca9baa36e2963a27a1c0c1165fe2b1"; +// let script_pubkey = "OP_1 OP_SWAP OP_CHECKSIGADD"; +// let mut transaction = utils::mock_transaction(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(1)]; +// utils::check_expected_dstack(ref engine, expected_stack.span()); +// } + +// #[test] +// fn test_op_checksigadd_too_short_signature() { +// let script_sig = +// "OP_DATA_1 0x01 OP_DATA_33 +// 0x024aeaf55040fa16de37303d13ca1dde85f4ca9baa36e2963a27a1c0c1165fe2b1"; +// let script_pubkey = "OP_1 OP_SWAP OP_CHECKSIGADD"; +// let mut transaction = utils::mock_transaction(script_sig); +// let mut engine = utils::test_compile_and_run_with_tx_err( +// script_pubkey, transaction, 'invalid sig fmt: too short', +// ); +// utils::check_dstack_size(ref engine, 0); +// let expected_stack = array![]; +// utils::check_expected_dstack(ref engine, expected_stack.span()); +// } + + diff --git a/packages/tests/src/tests/test_coinbase.cairo b/packages/tests/src/tests/test_coinbase.cairo index 1eb8dc03..0533d8cc 100644 --- a/packages/tests/src/tests/test_coinbase.cairo +++ b/packages/tests/src/tests/test_coinbase.cairo @@ -27,7 +27,7 @@ fn test_validate_coinbase_block_0() { let raw_transaction_hex = "0x01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000"; let raw_transaction = hex_to_bytecode(@raw_transaction_hex); - let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction); + let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction, 0, array![]); assert!( transaction.validate_coinbase(0, 5000000000).is_ok(), "Genesis block coinbase transaction invalid", @@ -40,7 +40,7 @@ fn test_validate_coinbase_block_1() { let raw_transaction_hex = "0x01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0704ffff001d0104ffffffff0100f2052a0100000043410496b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52da7589379515d4e0a604f8141781e62294721166bf621e73a82cbf2342c858eeac00000000"; let raw_transaction = hex_to_bytecode(@raw_transaction_hex); - let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction); + let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction, 0, array![]); assert!( transaction.validate_coinbase(1, 5000000000).is_ok(), "Block 1 coinbase transaction invalid", @@ -53,7 +53,7 @@ fn test_validate_coinbase_block_150007() { let raw_transaction_hex = "0x01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0804233fa04e028b12ffffffff0130490b2a010000004341047eda6bd04fb27cab6e7c28c99b94977f073e912f25d1ff7165d9c95cd9bbe6da7e7ad7f2acb09e0ced91705f7616af53bee51a238b7dc527f2be0aa60469d140ac00000000"; let raw_transaction = hex_to_bytecode(@raw_transaction_hex); - let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction); + let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction, 0, array![]); assert!( transaction.validate_coinbase(150007, 350000).is_ok(), "Block 150007 coinbase transaction invalid", @@ -66,7 +66,7 @@ fn test_validate_coinbase_block_227835() { let raw_transaction_hex = "0x01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0f0479204f51024f09062f503253482fffffffff01da495f9500000000232103ddcdae35e28aca364daa1397612d2dafd891ee136d2ca5ab83faff6bc12ed67eac00000000"; let raw_transaction = hex_to_bytecode(@raw_transaction_hex); - let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction); + let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction, 0, array![]); assert!( transaction.validate_coinbase(227835, 6050010).is_ok(), "Block 227835 coinbase transaction invalid", @@ -79,7 +79,7 @@ fn test_validate_coinbase_block_227836() { let raw_transaction_hex = "0x01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff2703fc7903062f503253482f04ac204f510858029a11000003550d3363646164312f736c7573682f0000000001207e6295000000001976a914e285a29e0704004d4e95dbb7c57a98563d9fb2eb88ac00000000"; let raw_transaction = hex_to_bytecode(@raw_transaction_hex); - let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction); + let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction, 0, array![]); assert!( transaction.validate_coinbase(227836, 6260000).is_ok(), "Block 227836 coinbase transaction invalid", @@ -92,7 +92,7 @@ fn test_validate_coinbase_block_400021() { let raw_transaction_hex = "0x01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff1b03951a0604f15ccf5609013803062b9b5a0100072f425443432f200000000001ebc31495000000001976a9142c30a6aaac6d96687291475d7d52f4b469f665a688ac00000000"; let raw_transaction = hex_to_bytecode(@raw_transaction_hex); - let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction); + let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction, 0, array![]); assert!( transaction.validate_coinbase(400021, 1166059).is_ok(), "Block 400021 coinbase transaction invalid", @@ -105,7 +105,7 @@ fn test_validate_coinbase_block_481823() { let raw_transaction_hex = "0x01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4e031f5a070473319e592f4254432e434f4d2f4e59412ffabe6d6dcceb2a9d0444c51cabc4ee97a1a000036ca0cb48d25b94b78c8367d8b868454b0100000000000000c0309b21000008c5f8f80000ffffffff0291920b5d0000000017a914e083685a1097ce1ea9e91987ab9e94eae33d8a13870000000000000000266a24aa21a9ede6c99265a6b9e1d36c962fda0516b35709c49dc3b8176fa7e5d5f1f6197884b400000000"; let raw_transaction = hex_to_bytecode(@raw_transaction_hex); - let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction); + let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction, 0, array![]); assert!( transaction.validate_coinbase(481823, 311039505).is_ok(), "Block 481823 coinbase transaction invalid", @@ -113,13 +113,12 @@ fn test_validate_coinbase_block_481823() { } #[test] -#[ignore] fn test_validate_coinbase_block_481824() { // Test the first block after BIP141 segwit let raw_transaction_hex = "0x010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff6403205a07f4d3f9da09acf878c2c9c96c410d69758f0eae0e479184e0564589052e832c42899c867100010000000000000000db9901006052ce25d80acfde2f425443432f20537570706f7274202f4e59412f00000000000000000000000000000000000000000000025d322c57000000001976a9142c30a6aaac6d96687291475d7d52f4b469f665a688ac0000000000000000266a24aa21a9ed6c3c4dff76b5760d58694147264d208689ee07823e5694c4872f856eacf5a5d80120000000000000000000000000000000000000000000000000000000000000000000000000"; let raw_transaction = hex_to_bytecode(@raw_transaction_hex); - let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction); + let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction, 0, array![]); assert!( transaction.validate_coinbase(481824, 212514269).is_ok(), "Block 481824 coinbase transaction invalid", @@ -127,13 +126,12 @@ fn test_validate_coinbase_block_481824() { } #[test] -#[ignore] fn test_validate_coinbase_block_538403() { // Test random block from learnmebitcoin let raw_transaction_hex = "0x010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff2503233708184d696e656420627920416e74506f6f6c373946205b8160a4256c0000946e0100ffffffff02f595814a000000001976a914edf10a7fac6b32e24daa5305c723f3de58db1bc888ac0000000000000000266a24aa21a9edfaa194df59043645ba0f58aad74bfd5693fa497093174d12a4bb3b0574a878db0120000000000000000000000000000000000000000000000000000000000000000000000000"; let raw_transaction = hex_to_bytecode(@raw_transaction_hex); - let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction); + let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction, 0, array![]); assert!( transaction.validate_coinbase(538403, 6517).is_ok(), "Block 538403 coinbase transaction invalid", diff --git a/packages/tests/src/tests/test_p2ms.cairo b/packages/tests/src/tests/test_p2ms.cairo index 36dcef96..eaf8aa61 100644 --- a/packages/tests/src/tests/test_p2ms.cairo +++ b/packages/tests/src/tests/test_p2ms.cairo @@ -1,7 +1,6 @@ -use shinigami_engine::transaction::EngineInternalTransactionTrait; -use crate::utxo::UTXO; -use crate::validate; +use shinigami_engine::transaction::{UTXO, EngineInternalTransactionTrait}; use shinigami_utils::bytecode::hex_to_bytecode; +use crate::validate; #[test] fn test_p2ms_1_of_2() { @@ -16,7 +15,7 @@ fn test_p2ms_1_of_2() { let raw_transaction_hex = "0x0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba26000000000490047304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000"; let raw_transaction = hex_to_bytecode(@raw_transaction_hex); - let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction); + let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction, 0, array![]); let utxo_hints = array![prev_out]; let res = validate::validate_p2ms(@transaction, 0, utxo_hints); @@ -39,7 +38,7 @@ fn test_p2ms_2_of_3() { let raw_transaction_hex = "0x010000000139c92b102879eb95f14e7344e4dd7d481e1238b1bfb1fa0f735068d2927b231400000000910047304402208fc06d216ebb4b6a3a3e0f906e1512c372fa8a9c2a92505d04e9b451ea7acd0c0220764303bb7e514ddd77855949d941c934e9cbda8e3c3827bfdb5777477e73885b014730440220569ec6d2e81625dd18c73920e0079cdb4c1d67d3d7616759eb0c18cf566b3d3402201c60318f0a62e3ba85ca0f158d4dfe63c0779269eb6765b6fc939fc51e7a8ea901ffffffff0140787d01000000001976a914641ad5051edd97029a003fe9efb29359fcee409d88ac00000000"; let raw_transaction = hex_to_bytecode(@raw_transaction_hex); - let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction); + let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction, 0, array![]); let utxo_hints = array![prev_out]; let res = validate::validate_p2ms(@transaction, 0, utxo_hints); @@ -59,7 +58,7 @@ fn test_p2ms_3_of_3() { let raw_transaction_hex = "0x0100000001269ca5990a6bdd62b12dc7c03d05edbb98b94742c075c44686a911df75a7ae2d00000000d9004730440220c0949354ad3a8b7162360a3b513683c417b38ea237805580d75e14950f3a4fed02206f95bc753511e96d82592b01eea4ce0f05b76d24c19e6b707a6468f1f7943a18014730440220a5f9c09fb40a6b02a7d20fcd246ba72995f34613b5afe18bd1b8b197b756aea402200511aecc66f7d7738baca0515c18444ba024d30ef304cdcf375fa163d4217b34014730440220938b9fd2b543e544eeb09abe519a1dbe900ec2761eff7277d8fea2e8397b6687022002886dd0e36aeb18c0c8752303f6898776552f7f877ff1d900d9078b26314aba01ffffffff0180f0fa02000000001976a914641ad5051edd97029a003fe9efb29359fcee409d88ac00000000"; let raw_transaction = hex_to_bytecode(@raw_transaction_hex); - let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction); + let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction, 0, array![]); let utxo_hints = array![prev_out]; let res = validate::validate_p2ms(@transaction, 0, utxo_hints); @@ -81,7 +80,7 @@ fn test_p2ms_1of2_invalid_pubkey() { let raw_transaction_hex = "0x01000000013de6aff69d5ebeca70a84d1dcef768bbcadbad210084012f8cda24233c8db278000000004b00493046022100a41a9015c847f404a14fcc81bf711ee2ce57583987948d54ebe540aafca97e0d022100d4e30d1ca42f77df8290b8975aa8fc0733d7c0cfdd5067ca516bac6c4012b47a01ffffffff01607d860500000000475121037953dbf08030f67352134992643d033417eaa6fcfb770c038f364ff40d7615882100dd28dfb81abe444429c466a1e3ab7c22365c48f234ef0f8d40397202969d4e9552ae00000000"; let raw_transaction = hex_to_bytecode(@raw_transaction_hex); - let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction); + let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction, 0, array![]); let utxo_hints = array![prev_out_1of2_invalid]; let res = validate::validate_p2ms(@transaction, 0, utxo_hints); @@ -101,7 +100,7 @@ fn test_p2ms_2_of_3_random() { let raw_transaction_hex = "0x010000000110a5fee9786a9d2d72c25525e52dd70cbd9035d5152fac83b62d3aa7e2301d58000000009300483045022100af204ef91b8dba5884df50f87219ccef22014c21dd05aa44470d4ed800b7f6e40220428fe058684db1bb2bfb6061bff67048592c574effc217f0d150daedcf36787601483045022100e8547aa2c2a2761a5a28806d3ae0d1bbf0aeff782f9081dfea67b86cacb321340220771a166929469c34959daf726a2ac0c253f9aff391e58a3c7cb46d8b7e0fdc4801ffffffff0180a21900000000001976a914971802edf585cdbc4e57017d6e5142515c1e502888ac00000000"; let raw_transaction = hex_to_bytecode(@raw_transaction_hex); - let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction); + let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction, 0, array![]); let utxo_hints = array![prev_out]; let res = validate::validate_p2ms(@transaction, 0, utxo_hints); @@ -121,7 +120,7 @@ fn test_p2ms_1_of_1() { let raw_transaction_hex = "0x01000000160bc5e16112acf01a64ab1013c7e9b34f76e2949c441ab46a086f66e9fd7a14930100000069463043021f6f7901a657289e36c25f5697a943e5013cb212ad10a2507dcace9e0e4c79230220480a8b35a574a7ba9c6073ada81c74e8e3aa63be92783211cd4dcb7d0d058346012103a2d725439d181166ac95e210f46f66c3f1088066bbfd4f534dd59488a02b4c3afffffffff3ed1ba6b8573906c810041d3418258492b1f7404656f4beb9a5adc304a5c0eb000000004746304302202ea812a99f9f8749bcd86e4e6dd0dba57fed2d7710152eec2a7688705f91cdd7021f176a029635ac01704dc425a2ba3f52e2f2290ab259a915c563be9c78083e3d01ffffffff8d68f286af7ec5a9450130f3e3c603bef4a95eaf9629b904c917d3b8f8e5486a00000000894630430220564f7beba5e28cd71fab7d3e8f359af94718938e451a653a5753896b50f87837021f32395bf3973c4b3f10ea86c7b81e38dcc2349a355ea3710d2c3bb57e8af2fb014104ab24a3241d2ab64955b9c7891fa7e7f0f5272c19db9034fdb02ef0414ab2583ea0588dabd07956b5f455e58fbf1da0c554362fc6600c3761b71aa933fd078496ffffffff1628a0ef1d36fdb5e4d16ca3768bce09328a6b8bc51c4ff37a629417454cc9560000000089463043021f0383c75fbdffb8ebc153577b4504cefe30f17969935e6cc4983adde4e069a5022070ece7763f97010534edcb6f80e1cfee88c1e30d0672cf47750c4f4e81771a2301410413c550cdb368790ba8ff0b70ac219b3016e9dcb11d8c7d62f77f94d133700e1e99f5ddfbdb8a2a2748ab2d93e1ec35c475f75e1a3a1f02bedb95ae3bdf757ed0ffffffff0bc5e16112acf01a64ab1013c7e9b34f76e2949c441ab46a086f66e9fd7a149300000000480046304302206b77a5f4395be4702a6fdf9d8233fc43ebcbb46010cba836be326cf548b9f993021f3267322bda863128d87be3cd4f3f1d3d1b4593113469bda89bd2b525b296a101ffffffffe75eb07f3d1bb6f1513555f04c58e3a41bfbac3408bdfa52ea32af01d79e1dac000000004800463043021f0ce39527434f3486c4f936f6fd8c3a3a5d7c889ee26b53be4f1302d87a82ca02202e543448f986aedc8ae72834e9ffab070267b022fada952bdcd25f2e459dbd1601ffffffffe75eb07f3d1bb6f1513555f04c58e3a41bfbac3408bdfa52ea32af01d79e1dac0100000069463043022020a90bb3d2a2056784ba36ba8582919695fc38b8a4c4809cb8d8c9b80027acff021f59c9d5a20f41debd5ba18ba1e95e458205b2b0db47a5010ae4e8b41989851101210281feb90c058c3436f8bc361930ae99fcfb530a699cdad141d7244bfcad521a1fffffffff4f29f13df6ddd7958770536d0edb19f4be03649ffa16b829f443e6536dfb333800000000474630430220345e21582d8a72ea3a4c390cfb021750147d5e490359fbcc7ef2345ee6284e90021f566b1fc655a0551383c36a5a94ad1fd44354ccbd48f1971895b6b422ba0e7301ffffffff1e563aa032652ef37a62964bda78ffa0ca97c9e644703f4494fc653f09c151e901000000480046304302203a0f649a666d4d3d5dec37cad6ff5fafea7c8767c54f6b715725bebc353176e9021f065a848bb213d0f4d0f69d156f284d25585d21bc28a288e54f15f36bb6b3bd01ffffffff1e563aa032652ef37a62964bda78ffa0ca97c9e644703f4494fc653f09c151e9020000006946304302207c4c5f02f2d8975418062107ee53d7fa9b7156437b4042deee63b38ab872a1c0021f30bf4f46ceec9c11bf72fae4fda65e57815742185264d6de872a9b6061326c01210281feb90c058c3436f8bc361930ae99fcfb530a699cdad141d7244bfcad521a1fffffffff3b8e11f993e5d512fbe70f6bd69df5510d87af895405f7bb07a3c3e5346813e6010000004800463043021f270dca121a53d5fd4a3fde799c0d24433c6893c48dae8ebfcd3675b66f06e10220183b3b56de5389fd6b5a7a71973df1a22da4c992ecc3bd32947be98b9ff9a5e601ffffffff3b8e11f993e5d512fbe70f6bd69df5510d87af895405f7bb07a3c3e5346813e60200000069463043021f34293f84b57d2805e6304d658c3764bc4da25685b0496d9c380115fcfb454b02206ae45d29bf23350371592f1bfba867b6fb2e594a4127d5f4e629ad7d4d018a4c01210281feb90c058c3436f8bc361930ae99fcfb530a699cdad141d7244bfcad521a1fffffffff288de338771ab35855db114211dc0fbbe772ad0ffba74e61ee21b5b53f8dac49010000004800463043021f6a4e7fdb0caac5b8864d0ca6b554c8bc1218df0fcefbd290e35a5659d82f85022075a2ea7b65c9eb69271179b4bf3c30a4b38b1867d1aa69059ca6073fe4317f0101ffffffff288de338771ab35855db114211dc0fbbe772ad0ffba74e61ee21b5b53f8dac4902000000694630430220478ef2590f1234e3daf0c9427ebc323d04f9ad25ddd8ea97eb51c66fa9c87e2e021f42500217ac76ce8368560e8521e58b050d62d9fd5bd40f4e8cf3acc286ebcc01210281feb90c058c3436f8bc361930ae99fcfb530a699cdad141d7244bfcad521a1fffffffff8ef083f541c2de13ae0233a35313b5c0fa936ae0f370c45077553d9508b0c71f01000000480046304302207a227400e6d30d1066a4659e5c655fc5870a12b229271bcb92c8a0fa90701604021f1e9783d80b8ef66f7a046c26022320259f1725aa9dba3549b9ecc2a3da627301ffffffff8ef083f541c2de13ae0233a35313b5c0fa936ae0f370c45077553d9508b0c71f02000000694630430220196990be449654b189ecd889240fe82d985af2cd42b690237415f3af72f507e6021f540f28bf9d553ce3d34c3e423241b498c5bfd91c69430d585811526d52570a012102c9c4ae505aeb0a50f1aa138e206c565ce6c49e0bf53d65fa1b2a8c80fd67f76cffffffff38e8175faf2547691f8fc068b7f0f55ce935f0ca5c2bb0cdb4c963c75ae7f5d60000000069463043021f432f653385412babd4a2cd040224c86b33eaaa8a0f269db23f431b3635e99102204df783d7a847551f5fd4d035e1d6fbb4f805d02e185fc0652e9948f203ce87e301210281feb90c058c3436f8bc361930ae99fcfb530a699cdad141d7244bfcad521a1fffffffff0a0ffeda5bd362eff2b94acbf57e122517d4f646fbc2905b57111c5eae41427f0000000089463043021f7bb3bc842a55b53b82390910bd1841c82dc81aec59c802f76c339bdf009e2f022042dc6eb57e126ed43d5b80645fc4403d2f139e5778e11628c65c124f8fae724b0141043f4a57ae0ab1537a4989c8364aba45f809950d233c8d517f08b040a5e9e3f5cd72c408045d551e74b9d03ec2747374c9adbf70edbdae19b97b50f08f6940b75bffffffff3087672cbdd861bd4ebad66b6d1d2cf247ba70aa11eb2042cc65f67c00ca5df500000000894630430220660ebc07d3f0e526b3ffd315009f80b3dc795bf17e95110b00250187a5aeafc0021f2e2cc2144b22cbffe429ea739a7887aa8e57fa29be7b860e8c239427563c500141043f4a57ae0ab1537a4989c8364aba45f809950d233c8d517f08b040a5e9e3f5cd72c408045d551e74b9d03ec2747374c9adbf70edbdae19b97b50f08f6940b75bffffffff68559862052c2544cda16af407017b091fd9b378c9ebeb58dd7957b6ba007aa602000000894630430220758a9dc0ff31e2876451a036491361cbc0e35b8a5239248ec163a79e7dc215c6021f0370556d36e1dc82d35aff0ea725687441d31d037d7d1573e4e01c3164ee650141043f4a57ae0ab1537a4989c8364aba45f809950d233c8d517f08b040a5e9e3f5cd72c408045d551e74b9d03ec2747374c9adbf70edbdae19b97b50f08f6940b75bffffffff04f2365e9cc619ac1b1d75b94cee48137dd559427b888d81d86fac8a5a15d0d30000000089463043021f206e176f5625b4677d616cb8a0150ca581aa368830452b2f6d09a2ce46d9cb022009bf3af7a5286bc8d93326f1de80e207021868a5acd72f24bf5ad2a3a15053e70141043f4a57ae0ab1537a4989c8364aba45f809950d233c8d517f08b040a5e9e3f5cd72c408045d551e74b9d03ec2747374c9adbf70edbdae19b97b50f08f6940b75bffffffffc7d8b5acf21826368095a66aa016566571c5c6b36ffda3d919a7c4aa756a1d8e0000000069463043021f5d87c2d418050a0f6c3c82d9924d6fe30de08577dcdfea9a0e2090cfb6001e02203d852275d29b51ef676e4ff3012a012c62f3c082aa4b842d85725f33d516ebaa012103a2d725439d181166ac95e210f46f66c3f1088066bbfd4f534dd59488a02b4c3affffffff0280c3c901000000001976a914161d7a3d0ee15c793ab300433192f949d8f3566588ac581d0909000000001976a9141c7260b625f21287e7c9c1147f2fd73ed69025a288ac00000000"; let raw_transaction = hex_to_bytecode(@raw_transaction_hex); - let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction); + let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction, 0, array![]); let res = validate::validate_transaction_at(@transaction, 0, prev_out, 5); assert!(res.is_ok(), "P2MS 1-of-1 transaction validation failed"); @@ -141,9 +140,11 @@ fn test_p2ms_20_of_20() { let raw_transaction_hex = "0x0100000001bbb397fdf39cf8b14a49148861c751543172a6f6500e679e079a7aecfbf7aac400000000fdb50500483045022100e222a0a6816475d85ad28fbeb66e97c931081076dc9655da3afc6c1d81b43f9802204681f9ea9d52a31c9c47cf78b71410ecae6188d7c31495f5f1adfe0df5864a7401483045022100e222a0a6816475d85ad28fbeb66e97c931081076dc9655da3afc6c1d81b43f9802204681f9ea9d52a31c9c47cf78b71410ecae6188d7c31495f5f1adfe0df5864a7401483045022100e222a0a6816475d85ad28fbeb66e97c931081076dc9655da3afc6c1d81b43f9802204681f9ea9d52a31c9c47cf78b71410ecae6188d7c31495f5f1adfe0df5864a7401483045022100e222a0a6816475d85ad28fbeb66e97c931081076dc9655da3afc6c1d81b43f9802204681f9ea9d52a31c9c47cf78b71410ecae6188d7c31495f5f1adfe0df5864a7401483045022100e222a0a6816475d85ad28fbeb66e97c931081076dc9655da3afc6c1d81b43f9802204681f9ea9d52a31c9c47cf78b71410ecae6188d7c31495f5f1adfe0df5864a7401483045022100e222a0a6816475d85ad28fbeb66e97c931081076dc9655da3afc6c1d81b43f9802204681f9ea9d52a31c9c47cf78b71410ecae6188d7c31495f5f1adfe0df5864a7401483045022100e222a0a6816475d85ad28fbeb66e97c931081076dc9655da3afc6c1d81b43f9802204681f9ea9d52a31c9c47cf78b71410ecae6188d7c31495f5f1adfe0df5864a7401483045022100e222a0a6816475d85ad28fbeb66e97c931081076dc9655da3afc6c1d81b43f9802204681f9ea9d52a31c9c47cf78b71410ecae6188d7c31495f5f1adfe0df5864a7401483045022100e222a0a6816475d85ad28fbeb66e97c931081076dc9655da3afc6c1d81b43f9802204681f9ea9d52a31c9c47cf78b71410ecae6188d7c31495f5f1adfe0df5864a7401483045022100e222a0a6816475d85ad28fbeb66e97c931081076dc9655da3afc6c1d81b43f9802204681f9ea9d52a31c9c47cf78b71410ecae6188d7c31495f5f1adfe0df5864a7401483045022100e222a0a6816475d85ad28fbeb66e97c931081076dc9655da3afc6c1d81b43f9802204681f9ea9d52a31c9c47cf78b71410ecae6188d7c31495f5f1adfe0df5864a7401483045022100e222a0a6816475d85ad28fbeb66e97c931081076dc9655da3afc6c1d81b43f9802204681f9ea9d52a31c9c47cf78b71410ecae6188d7c31495f5f1adfe0df5864a7401483045022100e222a0a6816475d85ad28fbeb66e97c931081076dc9655da3afc6c1d81b43f9802204681f9ea9d52a31c9c47cf78b71410ecae6188d7c31495f5f1adfe0df5864a7401483045022100e222a0a6816475d85ad28fbeb66e97c931081076dc9655da3afc6c1d81b43f9802204681f9ea9d52a31c9c47cf78b71410ecae6188d7c31495f5f1adfe0df5864a7401483045022100e222a0a6816475d85ad28fbeb66e97c931081076dc9655da3afc6c1d81b43f9802204681f9ea9d52a31c9c47cf78b71410ecae6188d7c31495f5f1adfe0df5864a7401483045022100e222a0a6816475d85ad28fbeb66e97c931081076dc9655da3afc6c1d81b43f9802204681f9ea9d52a31c9c47cf78b71410ecae6188d7c31495f5f1adfe0df5864a7401483045022100e222a0a6816475d85ad28fbeb66e97c931081076dc9655da3afc6c1d81b43f9802204681f9ea9d52a31c9c47cf78b71410ecae6188d7c31495f5f1adfe0df5864a7401483045022100e222a0a6816475d85ad28fbeb66e97c931081076dc9655da3afc6c1d81b43f9802204681f9ea9d52a31c9c47cf78b71410ecae6188d7c31495f5f1adfe0df5864a7401483045022100e222a0a6816475d85ad28fbeb66e97c931081076dc9655da3afc6c1d81b43f9802204681f9ea9d52a31c9c47cf78b71410ecae6188d7c31495f5f1adfe0df5864a7401483045022100e222a0a6816475d85ad28fbeb66e97c931081076dc9655da3afc6c1d81b43f9802204681f9ea9d52a31c9c47cf78b71410ecae6188d7c31495f5f1adfe0df5864a7401ffffffff0180841e00000000001976a9144663e5aab48b092c7478620d867ef2976bce149a88ac00000000"; let raw_transaction = hex_to_bytecode(@raw_transaction_hex); - let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction); - let utxo_hints = array![prev_out]; - let res = validate::validate_transaction(@transaction, 0, utxo_hints); + let transaction = EngineInternalTransactionTrait::deserialize( + raw_transaction, 0, array![prev_out], + ); + + let res = validate::validate_transaction(@transaction, 0); assert!(res.is_ok(), "P2MS 20-of-20 transaction validation failed"); } diff --git a/packages/tests/src/tests/test_p2pk.cairo b/packages/tests/src/tests/test_p2pk.cairo index 641c298c..0bf35f93 100644 --- a/packages/tests/src/tests/test_p2pk.cairo +++ b/packages/tests/src/tests/test_p2pk.cairo @@ -1,7 +1,6 @@ use shinigami_engine::engine::{EngineImpl}; -use shinigami_engine::transaction::EngineInternalTransactionTrait; +use shinigami_engine::transaction::{UTXO, EngineInternalTransactionTrait}; use crate::validate; -use crate::utxo::UTXO; use shinigami_utils::bytecode::hex_to_bytecode; // https://learnmeabitcoin.com/explorer/tx/cda1f7d88232ff7e4fc1fcbf8a66b2cc3b8e19b5bc0ad22618453b8b75156740 @@ -15,10 +14,10 @@ fn test_compressed_pubkey() { let raw_transaction_hex = "0x0100000001475948774538830c533ad43cd5cd9a241a72569679e5f0474e670f466c81b83d00000000494830450221009c31a8561a3e422211e72242170d63d5d420398629475b48925720b4ba5b064202201b72633207c17b6129b9fee23247f48a11eb656c1ca4c50c828e4fdbf881be6301ffffffff0160090000000000001976a91474d03dbb59f75ff54ee97dadf221d74a48c3b52288ac00000000"; let raw_transaction = hex_to_bytecode(@raw_transaction_hex); - let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction); let utxo_hints = array![prev_out]; + let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction, 0, utxo_hints); - let res = validate::validate_transaction(@transaction, 0, utxo_hints); + let res = validate::validate_transaction(@transaction, 0); assert!(res.is_ok(), "Transaction validation failed"); } @@ -33,10 +32,10 @@ fn test_block_181_tx_mainnet() { let raw_transaction_hex = "0x0100000001169e1e83e930853391bc6f35f605c6754cfead57cf8387639d3b4096c54f18f40100000048473044022027542a94d6646c51240f23a76d33088d3dd8815b25e9ea18cac67d1171a3212e02203baf203c6e7b80ebd3e588628466ea28be572fe1aaa3f30947da4763dd3b3d2b01ffffffff0200ca9a3b00000000434104b5abd412d4341b45056d3e376cd446eca43fa871b51961330deebd84423e740daa520690e1d9e074654c59ff87b408db903649623e86f1ca5412786f61ade2bfac005ed0b20000000043410411db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3ac00000000"; let raw_transaction = hex_to_bytecode(@raw_transaction_hex); - let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction); let utxo_hints = array![prev_out]; + let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction, 0, utxo_hints); - let res = validate::validate_transaction(@transaction, 0, utxo_hints); + let res = validate::validate_transaction(@transaction, 0); assert!(res.is_ok(), "Transaction validation failed"); } @@ -51,10 +50,10 @@ fn test_block_182_tx_mainnet() { let raw_transaction_hex = "0x0100000001be141eb442fbc446218b708f40caeb7507affe8acff58ed992eb5ddde43c6fa1010000004847304402201f27e51caeb9a0988a1e50799ff0af94a3902403c3ad4068b063e7b4d1b0a76702206713f69bd344058b0dee55a9798759092d0916dbbc3e592fee43060005ddc17401ffffffff0200e1f5050000000043410401518fa1d1e1e3e162852d68d9be1c0abad5e3d6297ec95f1f91b909dc1afe616d6876f92918451ca387c4387609ae1a895007096195a824baf9c38ea98c09c3ac007ddaac0000000043410411db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3ac00000000"; let raw_transaction = hex_to_bytecode(@raw_transaction_hex); - let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction); let utxo_hints = array![prev_out]; + let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction, 0, utxo_hints); - let res = validate::validate_transaction(@transaction, 0, utxo_hints); + let res = validate::validate_transaction(@transaction, 0); assert!(res.is_ok(), "Transaction validation failed"); } @@ -80,9 +79,9 @@ fn test_block_496_tx_mainnet() { let raw_transaction_hex = "0x010000000321f75f3139a013f50f315b23b0c9a2b6eac31e2bec98e5891c924664889942260000000049483045022100cb2c6b346a978ab8c61b18b5e9397755cbd17d6eb2fe0083ef32e067fa6c785a02206ce44e613f31d9a6b0517e46f3db1576e9812cc98d159bfdaf759a5014081b5c01ffffffff79cda0945903627c3da1f85fc95d0b8ee3e76ae0cfdc9a65d09744b1f8fc85430000000049483045022047957cdd957cfd0becd642f6b84d82f49b6cb4c51a91f49246908af7c3cfdf4a022100e96b46621f1bffcf5ea5982f88cef651e9354f5791602369bf5a82a6cd61a62501fffffffffe09f5fe3ffbf5ee97a54eb5e5069e9da6b4856ee86fc52938c2f979b0f38e82000000004847304402204165be9a4cbab8049e1af9723b96199bfd3e85f44c6b4c0177e3962686b26073022028f638da23fc003760861ad481ead4099312c60030d4cb57820ce4d33812a5ce01ffffffff01009d966b01000000434104ea1feff861b51fe3f5f8a3b12d0f4712db80e919548a80839fc47c6a21e66d957e9c5d8cd108c7a2d2324bad71f9904ac0ae7336507d785b17a2c115e427a32fac00000000"; let raw_transaction = hex_to_bytecode(@raw_transaction_hex); - let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction); let utxo_hints = array![prev_out, prev_out2, prev_out3]; + let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction, 0, utxo_hints); - let res = validate::validate_transaction(@transaction, 0, utxo_hints); + let res = validate::validate_transaction(@transaction, 0); assert!(res.is_ok(), "Transaction validation failed"); } diff --git a/packages/tests/src/tests/test_p2pkh.cairo b/packages/tests/src/tests/test_p2pkh.cairo index 6b7fe6b9..1568c792 100644 --- a/packages/tests/src/tests/test_p2pkh.cairo +++ b/packages/tests/src/tests/test_p2pkh.cairo @@ -1,7 +1,6 @@ -use shinigami_engine::transaction::EngineInternalTransactionTrait; -use crate::utxo::UTXO; -use crate::validate; +use shinigami_engine::transaction::{UTXO, EngineInternalTransactionTrait}; use shinigami_utils::bytecode::hex_to_bytecode; +use crate::validate; #[test] fn test_p2pkh_transaction() { @@ -10,7 +9,6 @@ fn test_p2pkh_transaction() { let raw_transaction_hex = "0x0100000002f60b5e96f09422354ab150b0e506c4bffedaf20216d30059cc5a3061b4c83dff000000004a493046022100e26d9ff76a07d68369e5782be3f8532d25ecc8add58ee256da6c550b52e8006b022100b4431f5a9a4dcb51cbdcaae935218c0ae4cfc8aa903fe4e5bac4c208290b7d5d01fffffffff7272ef43189f5553c2baea50f59cde99b3220fd518884d932016d055895b62d000000004a493046022100a2ab7cdc5b67aca032899ea1b262f6e8181060f5a34ee667a82dac9c7b7db4c3022100911bc945c4b435df8227466433e56899fbb65833e4853683ecaa12ee840d16bf01ffffffff0100e40b54020000001976a91412ab8dc588ca9d5787dde7eb29569da63c3a238c88ac00000000"; let raw_transaction = hex_to_bytecode(@raw_transaction_hex); - let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction); let prevout_pk_script_1 = "0x4104c9560dc538db21476083a5c65a34c7cc219960b1e6f27a87571cd91edfd00dac16dca4b4a7c4ab536f85bc263b3035b762c5576dc6772492b8fb54af23abff6dac"; @@ -25,8 +23,9 @@ fn test_p2pkh_transaction() { block_height: 357, }; let utxo_hints = array![prevout_1, prevout_2]; + let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction, 0, utxo_hints); - let res = validate::validate_transaction(@transaction, 0, utxo_hints); + let res = validate::validate_transaction(@transaction, 0); assert!(res.is_ok(), "Transaction validation failed"); } @@ -37,7 +36,6 @@ fn test_p2pkh_transaction_spend() { let raw_transaction_hex = "0x01000000030dd7891efbf67da47c651531db8aab3144ed7a524e4ae1e30b773525e27ddd7b000000004948304502206f6a68710a51f77e5a1fa4d1037a23a76723724a51fd54710949e0189ee02dfa022100dad3454ade12fe84f3818e14c41ec2e02bbb154dd3136a094cdf86f67ebbe0b601ffffffff16851666962e37a75a246101f2e340c628b1db3c045d4d3cfb2d1c0f58f97c6f000000008b48304502203f004eeed0cef2715643e2f25a27a28f3c578e94c7f0f6a4df104e7d163f7f8f022100b8b248c1cfd8f77a0365107a9511d759b7544d979dd152a955c867afac0ef7860141044d05240cfbd8a2786eda9dadd520c1609b8593ff8641018d57703d02ba687cf2f187f0cee2221c3afb1b5ff7888caced2423916b61444666ca1216f26181398cffffffffffda5d38e91fd9a0d92872d51f83cb746fc7bf5d3ff13402f8d0d5ed60ddc79c0000000049483045022100b6fd43f2fa16e092678283f64d2e08fb2070b4af2b3ddfb9ca3c5e238288acaa02200c5a28e0a4fc1a540f6eeb30ccc4788050eae46964fe33ccb4500c3de1320c2501ffffffff02c0c62d00000000001976a91417194e1bd175fb5b1b2a1f9d221f6f5c29e1928388ac00c817a8040000001976a91465bda9b05f7e9a8f96a7f4ba0996a877708ef90888ac00000000"; let raw_transaction = hex_to_bytecode(@raw_transaction_hex); - let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction); let prevout_pk_script_0 = "0x4104889fcdfd7c5430d13f1eb5f508e2e87f38d2406fad8425a824e032ccb371ef62465331e1a6334d7c3770a2ad2a958e740130343399d01dbd87426db850f9faf9ac"; @@ -64,9 +62,10 @@ fn test_p2pkh_transaction_spend() { }; let utxo_hints = array![prev_out0, prev_out1, prev_out2]; + let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction, 0, utxo_hints); // Run Shinigami and validate the transaction execution - let res = validate::validate_transaction(@transaction, 0, utxo_hints); + let res = validate::validate_transaction(@transaction, 0); assert!(res.is_ok(), "Transaction validation failed"); } @@ -83,7 +82,6 @@ fn test_block_770000_p2pkh_transaction() { let raw_transaction_hex = "0x0200000001c3cbbd0f9ac1f59225df1381c10c4b104ed7d78beef73e89cbd163c8d98b729e000000006a47304402202acb8afaa5745d1fd99dab6e74d89ee679daca1973796f61916e6e27905cd01b022067120362c1145cdd2a2de182435618b3c356a72849718997e5546d62ea9925fb012102e34755efb7b73a51f0a2facc10c9aab73b99a3f676c60fe5a7e865c75d61cce3feffffff02acf15608020000001976a9149ee1cd0c085b88bd7b22e44abe52734e0a61c94288ac404b4c00000000001976a914a7e9478b4f77c490c32472cfe8ad672d24fc77a888accfbf0b00"; let raw_transaction = hex_to_bytecode(@raw_transaction_hex); - let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction); let prevout_pk_script = "0x76a9140900bb14c7cb6a52fd8a22fd68a5986eb193c9f588ac"; let prevout = UTXO { @@ -92,8 +90,9 @@ fn test_block_770000_p2pkh_transaction() { block_height: 769998, }; let utxo_hints = array![prevout]; + let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction, 0, utxo_hints); - let res = validate::validate_transaction(@transaction, 0, utxo_hints); + let res = validate::validate_transaction(@transaction, 0); assert!(res.is_ok(), "Transaction validation failed"); } @@ -103,7 +102,6 @@ fn test_block_770002_p2pkh_transaction() { let raw_transaction_hex = "0x0200000001cc8e9c87148eae3bc918c94b13c0971d7959474e37d040c777aab7d8f621bfa2000000006a47304402204321dc44fa1207aa353ce1b0270887910c10825b284cfc5dc1e5451da43defd002204b0cab71575f765d11f52b1f03bdc3d11b00a6d94e1f2185530c6f82bdae2e7d012102e9698e55e8b3bca5aa108e094e4b0b42ce0dd697268c77ccc14ef54bd336088efeffffff02404b4c00000000001976a9148ca7043a7bf78518c3dfc9c756f3af8fef4ce6db88acc9a30a08020000001976a914b8b5866bc6828bb1e9c9ddc132fe42965355c19b88acd1bf0b00"; let raw_transaction = hex_to_bytecode(@raw_transaction_hex); - let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction); let prevout_pk_script = "0x76a9149ee1cd0c085b88bd7b22e44abe52734e0a61c94288ac"; let prevout = UTXO { @@ -112,7 +110,8 @@ fn test_block_770002_p2pkh_transaction() { block_height: 769998, }; let utxo_hints = array![prevout]; + let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction, 0, utxo_hints); - let res = validate::validate_transaction(@transaction, 0, utxo_hints); + let res = validate::validate_transaction(@transaction, 0); assert!(res.is_ok(), "Transaction validation failed"); } diff --git a/packages/tests/src/tests/test_p2sh.cairo b/packages/tests/src/tests/test_p2sh.cairo index e2f42dd3..1b8c5961 100644 --- a/packages/tests/src/tests/test_p2sh.cairo +++ b/packages/tests/src/tests/test_p2sh.cairo @@ -1,37 +1,37 @@ -use shinigami_engine::transaction::EngineInternalTransactionTrait; +use shinigami_engine::transaction::{UTXO, EngineInternalTransactionTrait}; use shinigami_engine::engine::EngineImpl; use shinigami_engine::hash_cache::HashCacheImpl; use shinigami_engine::flags::ScriptFlags; -use crate::utxo::UTXO; use crate::validate; 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"; + // why this one works 0xa914748284390f9e263a4b766a75d0633c50426eb87588 + let prev_out_1of2_invalid = UTXO { amount: 10000000, pubkey_script: hex_to_bytecode(@prevout_pubkey), block_height: 177625, }; + // set utxo directly in validate_at + let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction, 0, array![]); let flags: u32 = ScriptFlags::ScriptBip16.into(); + let res = validate::validate_transaction_at(@transaction, flags, prev_out_1of2_invalid, 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"; @@ -40,19 +40,19 @@ fn test_p2sh_transaction_2() { }; let utxo_hints = array![prev_out_1of2_invalid]; + let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction, 0, utxo_hints); + let flags: u32 = ScriptFlags::ScriptBip16.into(); - let res = validate::validate_transaction(@transaction, flags, utxo_hints); + let res = validate::validate_transaction(@transaction, flags); 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"; @@ -60,19 +60,19 @@ fn test_p2sh_transaction_3() { amount: 10000000, pubkey_script: hex_to_bytecode(@prevout_pubkey), block_height: 183729, }; + let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction, 0, array![]); let flags: u32 = ScriptFlags::ScriptBip16.into(); + let res = validate::validate_transaction_at(@transaction, flags, prev_out, 11); assert!(res.is_ok(), "P2SH failed!:{:?}", res.unwrap_err()); } - #[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"; @@ -81,8 +81,10 @@ fn test_p2sh_transaction_4() { }; let utxo_hints = array![prev_out]; + let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction, 0, utxo_hints); let flags: u32 = ScriptFlags::ScriptBip16.into(); - let res = validate::validate_transaction(@transaction, flags, utxo_hints); + + let res = validate::validate_transaction(@transaction, flags); assert!(res.is_ok(), "P2SH failed!:{:?}", res.unwrap_err()); } @@ -93,8 +95,6 @@ fn test_p2sh_transaction_5() { "0x01000000017fa897c3556271c34cb28c03c196c2d912093264c9d293cb4980a2635474467d010000000f5355540b6f93598893578893588851ffffffff01501e0000000000001976a914aa2482ce71d219018ef334f6cc551ee88abd920888ac00000000"; let raw_transaction = hex_to_bytecode(@raw_transaction_hex); - let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction); - let prevout_pubkey = "0xa914da5a92e670a66538be1c550af352646000b2367d87"; let prev_out = UTXO { @@ -102,8 +102,12 @@ fn test_p2sh_transaction_5() { }; let utxo_hints = array![prev_out]; + let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction, 0, utxo_hints); + + // let res = validate_p2sh(@transaction, 0, 0); + let flags: u32 = ScriptFlags::ScriptBip16.into(); - let res = validate::validate_transaction(@transaction, flags, utxo_hints); + let res = validate::validate_transaction(@transaction, flags); assert!(res.is_ok(), "P2SH failed!:{:?}", res.unwrap_err()); } @@ -114,14 +118,15 @@ fn test_p2sh_transaction_6() { "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 transaction = EngineInternalTransactionTrait::deserialize(raw_transaction, 0, array![]); let flags: u32 = ScriptFlags::ScriptBip16.into(); let res = validate::validate_transaction_at(@transaction, flags, prev_out, 4); assert!(res.is_ok(), "P2SH failed!:{:?}", res.unwrap_err()); } + diff --git a/packages/tests/src/tests/test_p2wpkh.cairo b/packages/tests/src/tests/test_p2wpkh.cairo index 7638e28c..afc09445 100644 --- a/packages/tests/src/tests/test_p2wpkh.cairo +++ b/packages/tests/src/tests/test_p2wpkh.cairo @@ -1,5 +1,4 @@ -use shinigami_engine::transaction::EngineInternalTransactionTrait; -use crate::utxo::UTXO; +use shinigami_engine::transaction::{UTXO, EngineInternalTransactionTrait}; use crate::validate; use shinigami_utils::bytecode::hex_to_bytecode; use shinigami_engine::flags::ScriptFlags; @@ -10,7 +9,6 @@ fn test_p2wpkh_create_transaction() { let raw_transaction_hex = "0x020000000001016972546966be990440a0665b73d0f4c3c942592d1f64d1033717aaa3e2c2ec913300000000ffffffff024087100000000000160014841b80d2cc75f5345c482af96294d04fdd66b2b760e31600000000001600142e8734f8e263e516d47fcaa2dfe1bd01e0dc935802473044022042e5e3ed2a41214ae864634b6fde33ca2ff312f3d89d6aa3e14c026d50d8ed3202206c38dcd0432a0724490356fbf599cdae40e334c3667a9253f8f4cc57cf3c4480012103f465315805ed271eb972e43d84d2a9e19494d10151d9f6adb32b8534bfd764ab00000000"; let raw_transaction = hex_to_bytecode(@raw_transaction_hex); - let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction); let prevout_script = "0x0014841b80d2cc75f5345c482af96294d04fdd66b2b7"; let prevout = UTXO { @@ -18,8 +16,10 @@ fn test_p2wpkh_create_transaction() { }; let utxo_hints = array![prevout]; + let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction, 0, utxo_hints); let flags: u32 = ScriptFlags::ScriptVerifyWitness.into() | ScriptFlags::ScriptBip16.into(); - let res = validate::validate_transaction(@transaction, flags, utxo_hints); + + let res = validate::validate_transaction(@transaction, flags); assert!(res.is_ok(), "Transaction validation failed"); } @@ -30,7 +30,6 @@ fn test_p2wpkh_unlock_transaction() { let raw_transaction_hex = "0x020000000001013aa815ace3c5751ee6c325d614044ad58c18ed2858a44f9d9f98fbcddad878c10000000000ffffffff01344d10000000000016001430cd68883f558464ec7939d9f960956422018f0702483045022100c7fb3bd38bdceb315a28a0793d85f31e4e1d9983122b4a5de741d6ddca5caf8202207b2821abd7a1a2157a9d5e69d2fdba3502b0a96be809c34981f8445555bdafdb012103f465315805ed271eb972e43d84d2a9e19494d10151d9f6adb32b8534bfd764ab00000000"; let raw_transaction = hex_to_bytecode(@raw_transaction_hex); - let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction); let prevout_script = "0x0014841b80d2cc75f5345c482af96294d04fdd66b2b7"; let prevout = UTXO { @@ -38,8 +37,10 @@ fn test_p2wpkh_unlock_transaction() { }; let utxo_hints = array![prevout]; + let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction, 0, utxo_hints); let flags: u32 = ScriptFlags::ScriptVerifyWitness.into() | ScriptFlags::ScriptBip16.into(); - let res = validate::validate_transaction(@transaction, flags, utxo_hints); + + let res = validate::validate_transaction(@transaction, flags); assert!(res.is_ok(), "Transaction validation failed"); } @@ -50,7 +51,6 @@ fn test_p2wpkh_first_transaction() { let raw_transaction_hex = "0x010000000001017405e391018c5e9dc79f324f9607c9c46d21b02f66dabaa870b4add871d6379f01000000171600148d7a0a3461e3891723e5fdf8129caa0075060cffffffffff01fcf60200000000001600148d7a0a3461e3891723e5fdf8129caa0075060cff0248304502210088025cffdaf69d310c6fed11832edd9c19b6a912c132262701ad0e6133227d9202207d73bbf777abd2aeae995d684e6bb1a048c5ac722e16de48bdd35643df7decf001210283409659355b6d1cc3c32decd5d561abaac86c37a353b52895a5e6c196d6f44800000000"; let raw_transaction = hex_to_bytecode(@raw_transaction_hex); - let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction); let prevout_script = "0xa914811ee2fb2d0c96b478992e1c07320b253ef3ee2687"; let prevout = UTXO { @@ -58,9 +58,10 @@ fn test_p2wpkh_first_transaction() { }; let utxo_hints = array![prevout]; + let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction, 0, utxo_hints); let flags: u32 = ScriptFlags::ScriptVerifyWitness.into() | ScriptFlags::ScriptBip16.into(); - let res = validate::validate_transaction(@transaction, flags, utxo_hints); + let res = validate::validate_transaction(@transaction, flags); assert!(res.is_ok(), "P2WPKH Transaction validation failed"); } @@ -70,7 +71,6 @@ fn test_p2wpkh_first_witness_spend() { let raw_transaction_hex = "0x01000000000101ad2bb91208eef398def3ed3e784d9ee9b7befeb56a3053c3561849b88bc4cedf0000000000ffffffff037a3e0100000000001600148d7a0a3461e3891723e5fdf8129caa0075060cff7a3e0100000000001600148d7a0a3461e3891723e5fdf8129caa0075060cff0000000000000000256a2342697462616e6b20496e632e204a6170616e20737570706f727473205365675769742102483045022100a6e33a7aff720ba9f33a0a8346a16fdd022196862796d511d31978c40c9ad48b02206fb8f67bd699a8c952b3386a81d122c366d2d36cd08e2de21207e6aa6f96ce9501210283409659355b6d1cc3c32decd5d561abaac86c37a353b52895a5e6c196d6f44800000000"; let raw_transaction = hex_to_bytecode(@raw_transaction_hex); - let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction); let prevout_script = "0x00148d7a0a3461e3891723e5fdf8129caa0075060cff"; let prevout = UTXO { @@ -78,8 +78,9 @@ fn test_p2wpkh_first_witness_spend() { }; let utxo_hints = array![prevout]; let flags: u32 = ScriptFlags::ScriptVerifyWitness.into() | ScriptFlags::ScriptBip16.into(); + let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction, 0, utxo_hints); - let res = validate::validate_transaction(@transaction, flags, utxo_hints); + let res = validate::validate_transaction(@transaction, flags); assert!(res.is_ok(), "P2WPKH first follow-up spend witness validation failed"); } @@ -89,7 +90,6 @@ fn test_p2wpkh_uncompressed_key_scriptpubkey_validation() { let raw_transaction_hex = "0x020000000122998d36f7953b150106cfa0a8722b51309c3eca93256e3a2402e14083fe8db2010000006a473044022076a81ac13cf50982401b0ef7fef518d5f72ffa6a7f95175c9bc16b0d57c4cc3e02202dce5bb27c1bc390d6a120806a0bbe2d8dd682918dc050d374ad29f2aba67703012102ee49077747264b56032c80bc588c7fb724f282bf8969e5efef3030770d4aaf2affffffff01905f010000000000160014671041727b982843f7e3db4669c2f542e05096fb00000000"; let raw_transaction = hex_to_bytecode(@raw_transaction_hex); - let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction); let prevout_script = "0x76a9149acd0fbc308ea273b61bad6322a17c8a7694845d88ac"; let prevout = UTXO { @@ -98,8 +98,9 @@ fn test_p2wpkh_uncompressed_key_scriptpubkey_validation() { let utxo_hints = array![prevout]; let flags: u32 = ScriptFlags::ScriptVerifyWitness.into() | ScriptFlags::ScriptBip16.into(); + let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction, 0, utxo_hints); - let res = validate::validate_transaction(@transaction, flags, utxo_hints); + let res = validate::validate_transaction(@transaction, flags); assert!(res.is_ok(), "P2WPKH uncompressed key ScriptPubKey validation failed"); } diff --git a/packages/tests/src/tests/test_p2wsh.cairo b/packages/tests/src/tests/test_p2wsh.cairo index bcda93e7..c4abd116 100644 --- a/packages/tests/src/tests/test_p2wsh.cairo +++ b/packages/tests/src/tests/test_p2wsh.cairo @@ -1,9 +1,8 @@ -use shinigami_engine::transaction::EngineInternalTransactionTrait; +use shinigami_engine::transaction::{UTXO, EngineInternalTransactionTrait}; use shinigami_engine::flags::ScriptFlags; use shinigami_engine::errors::Error; use crate::validate; -use crate::utxo::UTXO; use shinigami_utils::bytecode::hex_to_bytecode; // P2WSH with P2MS @@ -19,11 +18,12 @@ fn test_learnmeabitcoin_usage() { let raw_transaction_hex = "0x010000000001016542b657eea04a75b1582969b5b532b3110b392b4b553297435a11b064e2eb460100000000ffffffff02c454fd000000000017a9145e7be6ec3e2382c669aaf3c71da1056f47b9024d875b07330200000000220020ea166bf0492c6f908e45404932e0f39c0571a71007c22b872548cd20f19a92f504004730440220415899bbee08e42376d06e8f86c92b4987613c2816352fe09cd1479fd639f18c02200db57f508f69e266d76c23891708158bda18690c165a41b0aa88303b97609f780147304402203973de2303e8787767090dd25c8a4dc97ce1aa7eb4c0962f13952ed4e856ff8e02203f1bb425def789eea8be46407d10b3c8730407176aef4dc2c29865eb5e5542bf0169522103848e308569b644372a5eb26665f1a8c34ca393c130b376db2fae75c43500013c2103cec1ee615c17e06d4f4b0a08617dffb8e568936bdff18fb057832a58ad4d1b752103eed7ae80c34d70f5ba93f93965f69f3c691da0f4607f242f4fd6c7a48789233e53aeee9c0900"; let raw_transaction = hex_to_bytecode(@raw_transaction_hex); - let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction); let utxo_hints = array![prev_out]; + let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction, 0, utxo_hints); let flags: u32 = ScriptFlags::ScriptVerifyWitness.into() | ScriptFlags::ScriptBip16.into(); - let res = validate::validate_transaction(@transaction, flags, utxo_hints); + + let res = validate::validate_transaction(@transaction, flags); assert!(res.is_ok(), "Transaction validation failed"); } @@ -41,11 +41,12 @@ fn test_learnmeabitcoin_usage_wrong_hash_in_pubkey_script() { let raw_transaction_hex = "0x010000000001016542b657eea04a75b1582969b5b532b3110b392b4b553297435a11b064e2eb460100000000ffffffff02c454fd000000000017a9145e7be6ec3e2382c669aaf3c71da1056f47b9024d875b07330200000000220020ea166bf0492c6f908e45404932e0f39c0571a71007c22b872548cd20f19a92f504004730440220415899bbee08e42376d06e8f86c92b4987613c2816352fe09cd1479fd639f18c02200db57f508f69e266d76c23891708158bda18690c165a41b0aa88303b97609f780147304402203973de2303e8787767090dd25c8a4dc97ce1aa7eb4c0962f13952ed4e856ff8e02203f1bb425def789eea8be46407d10b3c8730407176aef4dc2c29865eb5e5542bf0169522103848e308569b644372a5eb26665f1a8c34ca393c130b376db2fae75c43500013c2103cec1ee615c17e06d4f4b0a08617dffb8e568936bdff18fb057832a58ad4d1b752103eed7ae80c34d70f5ba93f93965f69f3c691da0f4607f242f4fd6c7a48789233e53aeee9c0900"; let raw_transaction = hex_to_bytecode(@raw_transaction_hex); - let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction); let utxo_hints = array![prev_out]; + let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction, 0, utxo_hints); let flags: u32 = ScriptFlags::ScriptVerifyWitness.into() | ScriptFlags::ScriptBip16.into(); - let res = validate::validate_transaction(@transaction, flags, utxo_hints); + + let res = validate::validate_transaction(@transaction, flags); assert!(res.is_err(), "Transaction validation should fail"); assert!(res.unwrap_err() == Error::WITNESS_PROGRAM_MISMATCH, "Wrong error"); } @@ -66,11 +67,12 @@ fn test_learnmeabitcoin_usage_different_witness_script_from_hash() { let raw_transaction_hex = "0x010000000001016542b657eea04a75b1582969b5b532b3110b392b4b553297435a11b064e2eb460100000000ffffffff02c454fd000000000017a9145e7be6ec3e2382c669aaf3c71da1056f47b9024d875b07330200000000220020ea166bf0492c6f908e45404932e0f39c0571a71007c22b872548cd20f19a92f504004730440220415899bbee08e42376d06e8f86c92b4987613c2816352fe09cd1479fd639f18c02200db57f508f69e266d76c23891708158bda18690c165a41b0aa88303b97609f780147304402203973de2303e8787767090dd25c8a4dc97ce1aa7eb4c0962f13952ed4e856ff8e02203f1bb425def789eea8be46407d10b3c8730407176aef4dc2c29865eb5e5542bf0169522103f48e308569b644372a5eb26665f1a8c34ca393c130b376db2fae75c43500013c2103cec1ee615c17e06d4f4b0a08617dffb8e568936bdff18fb057832a58ad4d1b752103eed7ae80c34d70f5ba93f93965f69f3c691da0f4607f242f4fd6c7a48789233e53aeee9c0900"; let raw_transaction = hex_to_bytecode(@raw_transaction_hex); - let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction); let utxo_hints = array![prev_out]; let flags: u32 = ScriptFlags::ScriptVerifyWitness.into() | ScriptFlags::ScriptBip16.into(); - let res = validate::validate_transaction(@transaction, flags, utxo_hints); + let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction, 0, utxo_hints); + + let res = validate::validate_transaction(@transaction, flags); assert!(res.is_err(), "Transaction validation should fail"); assert!(res.unwrap_err() == Error::WITNESS_PROGRAM_MISMATCH, "Wrong error"); } @@ -88,11 +90,12 @@ fn test_custom_hash_puzzle() { let raw_transaction_hex = "0x020000000001018a39b5cdd48c7d45a31a89cd675a95f5de78aebeeda1e55ac35d7110c3bacfc60000000000ffffffff01204e0000000000001976a914ee63c8c790952de677d1f8019c9474d84098d6e188ac0202123423aa20a23421f2ba909c885a3077bb6f8eb4312487797693bbcfe7e311f797e3c5b8fa8700000000"; let raw_transaction = hex_to_bytecode(@raw_transaction_hex); - let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction); let utxo_hints = array![prev_out]; let flags: u32 = ScriptFlags::ScriptVerifyWitness.into() | ScriptFlags::ScriptBip16.into(); - let res = validate::validate_transaction(@transaction, flags, utxo_hints); + let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction, 0, utxo_hints); + + let res = validate::validate_transaction(@transaction, flags); assert!(res.is_ok(), "Transaction validation failed"); } @@ -109,11 +112,12 @@ fn test_custom_hash_puzzle_invalid_unlock_code() { let raw_transaction_hex = "0x020000000001018a39b5cdd48c7d45a31a89cd675a95f5de78aebeeda1e55ac35d7110c3bacfc60000000000ffffffff01204e0000000000001976a914ee63c8c790952de677d1f8019c9474d84098d6e188ac0202234523aa20a23421f2ba909c885a3077bb6f8eb4312487797693bbcfe7e311f797e3c5b8fa8700000000"; let raw_transaction = hex_to_bytecode(@raw_transaction_hex); - let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction); let utxo_hints = array![prev_out]; let flags: u32 = ScriptFlags::ScriptVerifyWitness.into() | ScriptFlags::ScriptBip16.into(); - let res = validate::validate_transaction(@transaction, flags, utxo_hints); + let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction, 0, utxo_hints); + + let res = validate::validate_transaction(@transaction, flags); assert!(res.is_err(), "Transaction validation should fail"); assert!(res.unwrap_err() == Error::SCRIPT_FAILED, "Wrong error"); } @@ -131,11 +135,12 @@ fn test_custom_hash_puzzle_wrong_hash_script_in_pubkey_script() { let raw_transaction_hex = "0x020000000001018a39b5cdd48c7d45a31a89cd675a95f5de78aebeeda1e55ac35d7110c3bacfc60000000000ffffffff01204e0000000000001976a914ee63c8c790952de677d1f8019c9474d84098d6e188ac0202123423aa20a23421f2ba909c885a3077bb6f8eb4312487797693bbcfe7e311f797e3c5b8fa8700000000"; let raw_transaction = hex_to_bytecode(@raw_transaction_hex); - let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction); let utxo_hints = array![prev_out]; let flags: u32 = ScriptFlags::ScriptVerifyWitness.into() | ScriptFlags::ScriptBip16.into(); - let res = validate::validate_transaction(@transaction, flags, utxo_hints); + let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction, 0, utxo_hints); + + let res = validate::validate_transaction(@transaction, flags); assert!(res.is_err(), "Transaction validation should fail"); assert!(res.unwrap_err() == Error::WITNESS_PROGRAM_MISMATCH, "Wrong error"); } @@ -156,11 +161,12 @@ fn test_custom_hash_puzzle_different_witness_script_from_hash() { let raw_transaction_hex = "0x020000000001018a39b5cdd48c7d45a31a89cd675a95f5de78aebeeda1e55ac35d7110c3bacfc60000000000ffffffff01204e0000000000001976a914ee63c8c790952de677d1f8019c9474d84098d6e188ac0202123423ba20a23421f2ba909c885a3077bb6f8eb4312487797693bbcfe7e311f797e3c5b8fa8700000000"; let raw_transaction = hex_to_bytecode(@raw_transaction_hex); - let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction); let utxo_hints = array![prev_out]; let flags: u32 = ScriptFlags::ScriptVerifyWitness.into() | ScriptFlags::ScriptBip16.into(); - let res = validate::validate_transaction(@transaction, flags, utxo_hints); + let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction, 0, utxo_hints); + + let res = validate::validate_transaction(@transaction, flags); assert!(res.is_err(), "Transaction validation should fail"); assert!(res.unwrap_err() == Error::WITNESS_PROGRAM_MISMATCH, "Wrong error"); } diff --git a/packages/tests/src/tests/test_transactions.cairo b/packages/tests/src/tests/test_transactions.cairo index 29e945af..9f6d5722 100644 --- a/packages/tests/src/tests/test_transactions.cairo +++ b/packages/tests/src/tests/test_transactions.cairo @@ -1,5 +1,5 @@ -use shinigami_engine::transaction::EngineInternalTransactionTrait; -use crate::utxo::UTXO; +use shinigami_engine::transaction::{UTXO, EngineInternalTransactionTrait}; + use crate::validate; use shinigami_utils::byte_array::u256_from_byte_array_with_offset; use shinigami_utils::bytecode::hex_to_bytecode; @@ -13,7 +13,7 @@ fn test_deserialize_transaction() { let raw_transaction_hex = "0x010000000291056d7ab3e99f9506f248783e0801c9039082d7d876dd45a8ab1f0a166226e2000000008c493046022100a3deff7d28eca94e018cfafcf4e705cc6bb56ce1dab83a6377e6e97d28d305d90221008cfc8d40bb8e336f5210a4197760f6b9650ae6ec4682cc1626841d9c87d1b0f20141049305a94c5b8e71d8be2a2d7188d74cb38affc9dc83ab77cc2fedf7c03a82a56175b9c335ce4546a943a2215a9c04757f08c2cc97f731a208ea767119050e0b97ffffffff465345e66a84047bf58a3787456d8023c38e04734c72d7f7039b9220ac503b6e000000008a47304402202ff5fe06ff3ee680e069cd28ff3ed9a60050ba52ed811a739a29b81e3667074602203c0d1b63d0c495ee1b63886e42c2db0c4cb041ce0c957ad7febe0fbcd23498ee014104cc2cb6eb11b7b504e1aa2826cf8ce7568bc757d7f58ab1eaa0b5e6945ccdcc5b111c0c1163a28037b89501e0b83e3fdceb22a2fd80533e5211acac060b17b2a4ffffffff0243190600000000001976a914a2baed4cdeda71053537312ee32cf0ab9f22cf1888acc0451b11000000001976a914f3e0b1ca6d94a95e1f3683ea6f3d2b563ad475e688ac00000000"; let raw_transaction = hex_to_bytecode(@raw_transaction_hex); - let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction); + let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction, 0, array![]); assert_eq!(transaction.version, 1, "Version is not correct"); assert_eq!(transaction.transaction_inputs.len(), 2, "Transaction inputs length is not correct"); @@ -74,7 +74,7 @@ fn test_deserialize_first_p2pkh_transaction() { let raw_transaction_hex = "0x0100000002f60b5e96f09422354ab150b0e506c4bffedaf20216d30059cc5a3061b4c83dff000000004a493046022100e26d9ff76a07d68369e5782be3f8532d25ecc8add58ee256da6c550b52e8006b022100b4431f5a9a4dcb51cbdcaae935218c0ae4cfc8aa903fe4e5bac4c208290b7d5d01fffffffff7272ef43189f5553c2baea50f59cde99b3220fd518884d932016d055895b62d000000004a493046022100a2ab7cdc5b67aca032899ea1b262f6e8181060f5a34ee667a82dac9c7b7db4c3022100911bc945c4b435df8227466433e56899fbb65833e4853683ecaa12ee840d16bf01ffffffff0100e40b54020000001976a91412ab8dc588ca9d5787dde7eb29569da63c3a238c88ac00000000"; let raw_transaction = hex_to_bytecode(@raw_transaction_hex); - let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction); + let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction, 0, array![]); assert_eq!(transaction.version, 1, "Version is not correct"); assert_eq!(transaction.transaction_inputs.len(), 2, "Transaction inputs length is not correct"); @@ -127,7 +127,7 @@ fn test_deserialize_first_p2sh_transaction() { let raw_transaction_hex = "0x01000000014ce7153d92e3b24d9eea31f8cf391c3fb4c39f7742b341b2d36c6367e7546474000000006c493046022100c554360535b2ad3b1cb1b966a87807f7a7e45fa485348d662a1e7413dced8471022100d6bcfc4385b7ac41ca3968a73c4a28e38879192c3db1286b36e59ec9fce52bbd012103c96e3a9e63986801269d5f278246ed7cdc2d392595d0a25b102e04598f4b4fa9ffffffff02cb871a00000000001976a914c02ebae82202119f23f330781ff26b303edb7dbd88ac809698000000000017a914748284390f9e263a4b766a75d0633c50426eb8758700000000"; let raw_transaction = hex_to_bytecode(@raw_transaction_hex); - let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction); + let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction, 0, array![]); 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]; @@ -175,7 +175,7 @@ fn test_deserialize_p2wsh_transaction() { let raw_transaction_hex = "0x020000000001018a39b5cdd48c7d45a31a89cd675a95f5de78aebeeda1e55ac35d7110c3bacfc60000000000ffffffff01204e0000000000001976a914ee63c8c790952de677d1f8019c9474d84098d6e188ac0202123423aa20a23421f2ba909c885a3077bb6f8eb4312487797693bbcfe7e311f797e3c5b8fa8700000000"; let raw_transaction = hex_to_bytecode(@raw_transaction_hex); - let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction); + let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction, 0, array![]); assert_eq!(transaction.version, 2, "Version is not correct"); assert_eq!(transaction.transaction_inputs.len(), 1, "Transaction inputs length is not correct"); @@ -215,7 +215,7 @@ fn test_deserialize_p2wpkh_transaction() { let raw_transaction_hex = "0x020000000001016972546966be990440a0665b73d0f4c3c942592d1f64d1033717aaa3e2c2ec913300000000ffffffff024087100000000000160014841b80d2cc75f5345c482af96294d04fdd66b2b760e31600000000001600142e8734f8e263e516d47fcaa2dfe1bd01e0dc935802473044022042e5e3ed2a41214ae864634b6fde33ca2ff312f3d89d6aa3e14c026d50d8ed3202206c38dcd0432a0724490356fbf599cdae40e334c3667a9253f8f4cc57cf3c4480012103f465315805ed271eb972e43d84d2a9e19494d10151d9f6adb32b8534bfd764ab00000000"; let raw_transaction = hex_to_bytecode(@raw_transaction_hex); - let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction); + let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction, 0, array![]); assert_eq!(transaction.version, 2, "Transaction version is incorrect"); assert_eq!(transaction.transaction_inputs.len(), 1, "Incorrect number of transaction inputs"); @@ -272,7 +272,6 @@ fn test_validate_transaction() { let raw_transaction_hex = "0x0100000001c997a5e56e104102fa209c6a852dd90660a20b2d9c352423edce25857fcd3704000000004847304402204e45e16932b8af514961a1d3a1a25fdf3f4f7732e9d624c6c61548ab5fb8cd410220181522ec8eca07de4860a4acdd12909d831cc56cbbac4622082221a8768d1d0901ffffffff0200ca9a3b00000000434104ae1a62fe09c5f51b13905f07f06b99a2f7159b2225f374cd378d71302fa28414e7aab37397f554a7df5f142c21c1b7303b8a0626f1baded5c72a704f7e6cd84cac00286bee0000000043410411db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3ac00000000"; let raw_transaction = hex_to_bytecode(@raw_transaction_hex); - let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction); // Setup UTXO hints ( previous valid outputs used to execute this transaction ) let prevout_pk_script = @@ -281,8 +280,9 @@ fn test_validate_transaction() { amount: 5000000000, pubkey_script: hex_to_bytecode(@prevout_pk_script), block_height: 9, }; let utxo_hints = array![prev_out]; + let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction, 0, utxo_hints); // Run Shinigami and validate the transaction execution - let res = validate::validate_transaction(@transaction, 0, utxo_hints); + let res = validate::validate_transaction(@transaction, 0); assert!(res.is_ok(), "Transaction validation failed"); } diff --git a/packages/tests/src/utils.cairo b/packages/tests/src/utils.cairo index 65a5247b..c274ba8e 100644 --- a/packages/tests/src/utils.cairo +++ b/packages/tests/src/utils.cairo @@ -155,6 +155,8 @@ pub fn mock_transaction_with( transaction_inputs: tx_inputs, transaction_outputs: tx_outputs, locktime: locktime, + txid: 0, + utxos: array![], } } @@ -225,6 +227,8 @@ pub fn mock_witness_transaction() -> EngineTransaction { transaction_inputs: transaction_inputs, transaction_outputs: transaction_outputs, locktime: 0, + txid: 0, + utxos: array![], } } diff --git a/packages/tests/src/utxo.cairo b/packages/tests/src/utxo.cairo index fb0b3dbe..f88067e4 100644 --- a/packages/tests/src/utxo.cairo +++ b/packages/tests/src/utxo.cairo @@ -1,10 +1,10 @@ -#[derive(Debug, Drop)] -pub struct UTXO { - pub amount: i64, - pub pubkey_script: ByteArray, - pub block_height: i32, - // TODO: flags? -} -// TODO: implement UTXOSet? +// #[derive(Debug, Drop)] +// pub struct UTXO { +// pub amount: i64, +// pub pubkey_script: ByteArray, +// pub block_height: u32, +// // TODO: flags? +// } +// // TODO: implement UTXOSet? diff --git a/packages/tests/src/validate.cairo b/packages/tests/src/validate.cairo index db5283ab..4c665313 100644 --- a/packages/tests/src/validate.cairo +++ b/packages/tests/src/validate.cairo @@ -1,31 +1,29 @@ use shinigami_engine::engine::EngineImpl; use shinigami_engine::hash_cache::HashCacheImpl; -use shinigami_engine::transaction::EngineTransaction; +use shinigami_engine::transaction::{EngineTransaction, UTXO}; use shinigami_engine::opcodes::Opcode; -use crate::utxo::UTXO; // TODO: Move validate coinbase here -// TODO: Remove hints? // utxo_hints: Set of existing utxos that are being spent by this transaction -pub fn validate_transaction( - tx: @EngineTransaction, flags: u32, utxo_hints: Array, -) -> Result<(), felt252> { +pub fn validate_transaction(tx: @EngineTransaction, flags: u32) -> Result<(), felt252> { let input_count = tx.transaction_inputs.len(); - if input_count != utxo_hints.len() { + let utxo_count = tx.utxos.len(); + if input_count != utxo_count { return Result::Err('Invalid number of utxo hints'); } let mut i = 0; let mut err = ''; while i != input_count { - let utxo = utxo_hints[i]; + let utxo = tx.utxos.at(i); let hash_cache = HashCacheImpl::new(tx); // TODO: Error handling let mut engine = EngineImpl::new( utxo.pubkey_script, tx, i, flags, *utxo.amount, @hash_cache, ) .unwrap(); + let res = engine.execute(); if res.is_err() { err = res.unwrap_err(); @@ -49,6 +47,7 @@ pub fn validate_transaction_at( @prevout.pubkey_script, tx, at, flags, prevout.amount, @hash_cache, ) .unwrap(); + let res = engine.execute(); if res.is_err() { return Result::Err(res.unwrap_err()); diff --git a/packages/utils/src/hash.cairo b/packages/utils/src/hash.cairo index dea7c008..557814c1 100644 --- a/packages/utils/src/hash.cairo +++ b/packages/utils/src/hash.cairo @@ -10,10 +10,37 @@ pub fn sha256_byte_array(byte: @ByteArray) -> ByteArray { hash_value } +pub fn sha256_u256(hash: [u32; 8]) -> u256 { + let mut bytes = ""; + for word in hash.span() { + bytes.append_word((*word).into(), 4); + }; + + let msg_hash = compute_sha256_byte_array(@bytes); + let mut hash_value: u256 = 0; + for word in msg_hash.span() { + hash_value *= 0x100000000; + hash_value = hash_value + (*word).into(); + }; + + hash_value +} + pub fn double_sha256_bytearray(byte: @ByteArray) -> ByteArray { return sha256_byte_array(@sha256_byte_array(byte)); } +pub fn simple_sha256(byte: @ByteArray) -> u256 { + let msg_hash = compute_sha256_byte_array(byte); + let mut hash_value: u256 = 0; + for word in msg_hash.span() { + hash_value *= 0x100000000; + hash_value = hash_value + (*word).into(); + }; + + hash_value +} + pub fn double_sha256(byte: @ByteArray) -> u256 { let msg_hash = compute_sha256_byte_array(byte); let mut res_bytes = ""; @@ -22,11 +49,20 @@ pub fn double_sha256(byte: @ByteArray) -> u256 { }; let msg_hash = compute_sha256_byte_array(@res_bytes); let mut hash_value: u256 = 0; - for word in msg_hash - .span() { - hash_value *= 0x100000000; - hash_value = hash_value + (*word).into(); - }; + for word in msg_hash.span() { + hash_value *= 0x100000000; + hash_value = hash_value + (*word).into(); + }; + + hash_value +} + +pub fn hash_to_u256(hash: [u32; 8]) -> u256 { + let mut hash_value: u256 = 0; + for word in hash.span() { + hash_value *= 0x100000000; + hash_value = hash_value + (*word).into(); + }; hash_value } diff --git a/packages/utils/src/hex.cairo b/packages/utils/src/hex.cairo index f6ff239a..48e53048 100644 --- a/packages/utils/src/hex.cairo +++ b/packages/utils/src/hex.cairo @@ -16,3 +16,20 @@ pub fn int_to_hex(value: u8) -> felt252 { upper_half.into() * byte_shift.into() + lower_half.into() } + +/// Converts bytes to hex (base16). +pub fn to_hex(data: @ByteArray) -> ByteArray { + let alphabet: @ByteArray = @"0123456789abcdef"; + let mut result: ByteArray = Default::default(); + + let mut i = 0; + while i != data.len() { + let value: u32 = data[i].into(); + let (l, r) = core::traits::DivRem::div_rem(value, 16); + result.append_byte(alphabet.at(l).unwrap()); + result.append_byte(alphabet.at(r).unwrap()); + i += 1; + }; + + result +}