diff --git a/bindings/curse_mcms/curse_mcms/curse_mcms.go b/bindings/curse_mcms/curse_mcms/curse_mcms.go index 9ab5620b8..fb423ce9e 100644 --- a/bindings/curse_mcms/curse_mcms/curse_mcms.go +++ b/bindings/curse_mcms/curse_mcms/curse_mcms.go @@ -188,6 +188,8 @@ const ( E_UNKNOWN_TARGET uint64 = 54 E_UNKNOWN_CURSE_MCMS_MODULE_FUNCTION uint64 = 55 E_UNKNOWN_CURSE_MCMS_ACCOUNT_FUNCTION uint64 = 56 + E_INVALID_PREDECESSOR_LEN uint64 = 57 + E_INVALID_SALT_LEN uint64 = 58 ) // Structs diff --git a/bindings/mcms/mcms/mcms.go b/bindings/mcms/mcms/mcms.go index 5ef57da79..38a272d24 100644 --- a/bindings/mcms/mcms/mcms.go +++ b/bindings/mcms/mcms/mcms.go @@ -195,6 +195,8 @@ const ( E_NOT_CANCELLER_ROLE uint64 = 50 E_NOT_TIMELOCK_ROLE uint64 = 51 E_UNKNOWN_MCMS_MODULE uint64 = 52 + E_INVALID_PREDECESSOR_LEN uint64 = 53 + E_INVALID_SALT_LEN uint64 = 54 ) // Structs diff --git a/contracts/mcms/curse_mcms/sources/curse_mcms.move b/contracts/mcms/curse_mcms/sources/curse_mcms.move index b0c77c6c8..ff85d461e 100644 --- a/contracts/mcms/curse_mcms/sources/curse_mcms.move +++ b/contracts/mcms/curse_mcms/sources/curse_mcms.move @@ -199,6 +199,8 @@ module curse_mcms::curse_mcms { const E_UNKNOWN_TARGET: u64 = 54; const E_UNKNOWN_CURSE_MCMS_MODULE_FUNCTION: u64 = 55; const E_UNKNOWN_CURSE_MCMS_ACCOUNT_FUNCTION: u64 = 56; + const E_INVALID_PREDECESSOR_LEN: u64 = 57; + const E_INVALID_SALT_LEN: u64 = 58; fun init_module(publisher: &signer) { let bypasser = create_multisig(publisher, BYPASSER_ROLE); @@ -1535,6 +1537,9 @@ module curse_mcms::curse_mcms { public fun hash_operation_batch( calls: vector, predecessor: vector, salt: vector ): vector { + assert!(predecessor.length() == 32, E_INVALID_PREDECESSOR_LEN); + assert!(salt.length() == 32, E_INVALID_SALT_LEN); + let packed = vector[]; packed.append(bcs::to_bytes(&calls)); packed.append(predecessor); diff --git a/contracts/mcms/curse_mcms/tests/curse_mcms_test.move b/contracts/mcms/curse_mcms/tests/curse_mcms_test.move index 3ff56e3ba..744b28add 100644 --- a/contracts/mcms/curse_mcms/tests/curse_mcms_test.move +++ b/contracts/mcms/curse_mcms/tests/curse_mcms_test.move @@ -1,6 +1,7 @@ #[test_only] module curse_mcms::curse_mcms_test { use std::signer; + use std::string; use std::vector; use aptos_framework::account; use aptos_framework::timestamp; @@ -604,4 +605,41 @@ module curse_mcms::curse_mcms_test { let _config_bypasser = curse_mcms::get_config(curse_mcms::bypasser_role()); let _config_proposer = curse_mcms::get_config(curse_mcms::proposer_role()); } + + #[test] + #[ + expected_failure( + abort_code = curse_mcms::curse_mcms::E_INVALID_PREDECESSOR_LEN, + location = curse_mcms::curse_mcms + ) + ] + fun test_hash_operation_batch__invalid_predecessor_len() { + let calls = + curse_mcms::create_calls( + vector[@curse_mcms], + vector[string::utf8(b"curse_mcms")], + vector[string::utf8(b"timelock_update_min_delay")], + vector[vector[0u8]] + ); + let _ = curse_mcms::hash_operation_batch(calls, x"deadbeef", vector[1u8]); + } + + #[test] + #[ + expected_failure( + abort_code = curse_mcms::curse_mcms::E_INVALID_SALT_LEN, + location = curse_mcms::curse_mcms + ) + ] + fun test_hash_operation_batch__invalid_salt_len() { + let calls = + curse_mcms::create_calls( + vector[@curse_mcms], + vector[string::utf8(b"curse_mcms")], + vector[string::utf8(b"timelock_update_min_delay")], + vector[vector[0u8]] + ); + let _ = + curse_mcms::hash_operation_batch(calls, curse_mcms::zero_hash(), x"deadbeef"); + } } diff --git a/contracts/mcms/mcms/sources/mcms.move b/contracts/mcms/mcms/sources/mcms.move index 979a65764..8333e9280 100644 --- a/contracts/mcms/mcms/sources/mcms.move +++ b/contracts/mcms/mcms/sources/mcms.move @@ -199,6 +199,8 @@ module mcms::mcms { const E_NOT_CANCELLER_ROLE: u64 = 50; const E_NOT_TIMELOCK_ROLE: u64 = 51; const E_UNKNOWN_MCMS_MODULE: u64 = 52; + const E_INVALID_PREDECESSOR_LEN: u64 = 53; + const E_INVALID_SALT_LEN: u64 = 54; fun init_module(publisher: &signer) { let bypasser = create_multisig(publisher, BYPASSER_ROLE); @@ -1614,6 +1616,9 @@ module mcms::mcms { public fun hash_operation_batch( calls: vector, predecessor: vector, salt: vector ): vector { + assert!(predecessor.length() == 32, E_INVALID_PREDECESSOR_LEN); + assert!(salt.length() == 32, E_INVALID_SALT_LEN); + let packed = vector[]; packed.append(bcs::to_bytes(&calls)); packed.append(predecessor); diff --git a/contracts/mcms/mcms/tests/mcms_executor_test.move b/contracts/mcms/mcms/tests/mcms_executor_test.move index aec6852cd..44cc3e315 100644 --- a/contracts/mcms/mcms/tests/mcms_executor_test.move +++ b/contracts/mcms/mcms/tests/mcms_executor_test.move @@ -17,11 +17,11 @@ module mcms::mcms_executor_test { const TEST_CHAIN_ID: u256 = 4; const TEST_NONCE: u64 = 0; - const PROPOSER_ADDR1: vector = x"5916431f0ea809587757df994233861e1271be55"; - const PROPOSER_ADDR2: vector = x"8803c3ed076e57d51e28301933418094bd961cc5"; - const PROPOSER_ADDR3: vector = x"8950e6c6832c9b0591801418684d27b2853b2c74"; + const PROPOSER_ADDR1: vector = x"2b5ad5c4795c026514f8317c7a215e218dccd6cf"; + const PROPOSER_ADDR2: vector = x"6813eb9362372eef6200f3b1dbc3f819671cba69"; + const PROPOSER_ADDR3: vector = x"7e5f4552091a69125d5dfcb7b8c2659029395bdf"; - const ROOT: vector = x"f7a8b0f28b2ae826313604377ecd0dd07dd4107e0777db5d251560aa2dbf760d"; + const ROOT: vector = x"d65e7d9805d6a2943fd5bdaf9f14285788b8ae7fdcb6424f1b0c1fe0e63d90bc"; const TEST_PROOF: vector> = vector[ x"a619565e90c1c564293b59b344ed0e12ed06eafb3c45b70baf6fdf299a046297", diff --git a/contracts/mcms/mcms/tests/mcms_test.move b/contracts/mcms/mcms/tests/mcms_test.move index 2c490903e..553c89138 100644 --- a/contracts/mcms/mcms/tests/mcms_test.move +++ b/contracts/mcms/mcms/tests/mcms_test.move @@ -20,6 +20,9 @@ module mcms::mcms_tests { use mcms::mcms_registry; use std::code::{PackageRegistry}; + // =================== Regenerate test constants =================== + // Run: go test ./relayer/txm -run TestRegenMcmsTestConstants -v + // keccak256("MANY_CHAIN_MULTI_SIG_DOMAIN_SEPARATOR_OP_APTOS") const MANY_CHAIN_MULTI_SIG_DOMAIN_SEPARATOR_OP: vector = x"e5a6d1256b00d7ec22512b6b60a3f4d75c559745d2dbf309f77b8b756caabe14"; @@ -28,13 +31,17 @@ module mcms::mcms_tests { const MIN_DELAY: u64 = 3600; // 1 hour delay const TEST_TARGET_ADDRESS: address = @0xabc; - const TEST_SALT: vector = x"1234567890abcdef"; - const TEST_PREDECESSOR: vector = x""; + const TEST_SALT: vector = x"1234567890abcdef000000000000000000000000000000000000000000000000"; + const SALT_ONE: vector = x"0000000000000000000000000000000000000000000000000000000000000001"; + const SALT_TWO: vector = x"0000000000000000000000000000000000000000000000000000000000000002"; + const SALT_ABCD: vector = x"abcd000000000000000000000000000000000000000000000000000000000000"; + const SALT_EFAB: vector = x"efab000000000000000000000000000000000000000000000000000000000000"; + const TEST_PREDECESSOR: vector = x"0000000000000000000000000000000000000000000000000000000000000000"; // Proposer signers from the logs (already in ascending order) - const PROPOSER_ADDR1: vector = x"5916431f0ea809587757df994233861e1271be55"; - const PROPOSER_ADDR2: vector = x"8803c3ed076e57d51e28301933418094bd961cc5"; - const PROPOSER_ADDR3: vector = x"8950e6c6832c9b0591801418684d27b2853b2c74"; + const PROPOSER_ADDR1: vector = x"2b5ad5c4795c026514f8317c7a215e218dccd6cf"; + const PROPOSER_ADDR2: vector = x"6813eb9362372eef6200f3b1dbc3f819671cba69"; + const PROPOSER_ADDR3: vector = x"7e5f4552091a69125d5dfcb7b8c2659029395bdf"; // test config: 2-of-3 multisig const SIGNER_GROUPS: vector = vector[0, 0, 0]; @@ -45,11 +52,11 @@ module mcms::mcms_tests { const GROUP_PARENTS: vector = vector[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - const ROOT: vector = x"f7a8b0f28b2ae826313604377ecd0dd07dd4107e0777db5d251560aa2dbf760d"; + const ROOT: vector = x"d65e7d9805d6a2943fd5bdaf9f14285788b8ae7fdcb6424f1b0c1fe0e63d90bc"; const POST_OP_COUNT: u64 = 4; const METADATA_PROOF: vector> = vector[ - x"66cf50cb9a50c740313fd0f889b676af60d35ef700711d94df6eeff3f1ba66c2", + x"0fff02db76d3a8c1d76a384c90404554d574239dc4e9d5c37c2b6580b6172842", x"951f1094081a858642cc6635f0885317828a7fddddd00668391c50f1e9e1bb66", x"597116801e22b18150f2abc4ca2ecd63e147bb67e24e4b5f900d49b909e1919f" ]; @@ -99,7 +106,7 @@ module mcms::mcms_tests { // }, const LEAVES: vector> = vector[ x"a619565e90c1c564293b59b344ed0e12ed06eafb3c45b70baf6fdf299a046297", // metadata hash - x"66cf50cb9a50c740313fd0f889b676af60d35ef700711d94df6eeff3f1ba66c2", // op1 hash + x"0fff02db76d3a8c1d76a384c90404554d574239dc4e9d5c37c2b6580b6172842", // op1 hash x"2feec0e3a232c5c847874246203e62c43db473fe85245095122e166be9114e13", // op2 hash x"411a4726f8a920fc0a814bd9897a06f3dd0f1c799a047deaa6469f105f5a6705", // op3 hash x"cb4dffef33843b197cd33346d3339d8432b14789504167c63fb9f74a73baaea5" // op4 hash @@ -109,13 +116,13 @@ module mcms::mcms_tests { const PRE_OP_COUNT: u64 = 0; const SIGNATURES: vector> = vector[ - x"72398e2f325e707217fa8108a08c126f49f4144c30c7e93896d139c9f1d9468c30424b060c19fa5c7820a17b57badb19375207c787878533834618688a4780581c", - x"9bb8ba839f9152cdc61556fcc70b0ebcb4d442654263a3d1c323e1eed85ebc6016e87c8e59f12d850b9d6b789ccafd93f19dcf65eb6fc75fd4351d5970214c1d1c", - x"225739c80de11d50f3dca8fbb8288881abad17439690abd8eee32d48ff2f6dd204c48aff2fac4dfe8ce7176fd12d2633d7892bfb3d3f3cfeb00352773fe55c8c1b" + x"f7fa9f145088415bad762a9b8a34e9219c5a83b5e32fbcd36d54791070b281270d55578ec3754867f6788c1bb98c34f130ad71b3619234019b75a77552e9e0581b", + x"bec2d7b5640661bc84d4c70bb43b9eb3d0b9c16cc67a36429c9393c3406b137619ebcc87dcdfbfa73fa9ba79c0cd10e4add392e2160590d3d2dbbbd2fbddfae51c", + x"04d0b81e83d16a344fb9736c359bdc254b9ce6db2e2185c33327b5091ff1f48d6e92b31c17271439e698ffe992c3483a1b8f44552809c0ac5abc1411ae44a77c1b" ]; const OP1_NONCE: u64 = 0; - const OP1_DATA: vector = x"01a969156fce9a4f08bcdc07b90f338efc630bff8dfa8340500cb6414aca762a4e010c6d636d735f6163636f756e7401106163636570745f6f776e6572736869700100200000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + const OP1_DATA: vector = x"01a969156fce9a4f08bcdc07b90f338efc630bff8dfa8340500cb6414aca762a4e010c6d636d735f6163636f756e7401106163636570745f6f776e65727368697001002000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000100000000000000"; #[test_only] fun setup(deployer: &signer, owner: &signer, framework: &signer): address { @@ -236,7 +243,7 @@ module mcms::mcms_tests { vector[string::utf8(b"accept_ownership")], vector[vector[]], mcms::zero_hash(), - vector[] + mcms::zero_hash() ); // Verify new owner is now `@mcms` @@ -1417,6 +1424,34 @@ module mcms::mcms_tests { assert!(min_delay == 0, 0); } + #[test] + #[expected_failure( + abort_code = mcms::mcms::E_INVALID_PREDECESSOR_LEN, location = mcms::mcms + )] + public fun test_hash_operation_batch__invalid_predecessor_len() { + let calls = + mcms::create_calls( + vector[@mcms], + vector[string::utf8(b"mcms")], + vector[string::utf8(b"timelock_update_min_delay")], + vector[bcs::to_bytes(&MIN_DELAY)] + ); + let _ = mcms::hash_operation_batch(calls, x"deadbeef", TEST_SALT); + } + + #[test] + #[expected_failure(abort_code = mcms::mcms::E_INVALID_SALT_LEN, location = mcms::mcms)] + public fun test_hash_operation_batch__invalid_salt_len() { + let calls = + mcms::create_calls( + vector[@mcms], + vector[string::utf8(b"mcms")], + vector[string::utf8(b"timelock_update_min_delay")], + vector[bcs::to_bytes(&MIN_DELAY)] + ); + let _ = mcms::hash_operation_batch(calls, TEST_PREDECESSOR, x"deadbeef"); + } + #[test(deployer = @mcms, owner = @mcms_owner, framework = @aptos_framework)] public fun test_update_min_delay( deployer: &signer, owner: &signer, framework: &signer @@ -1569,8 +1604,8 @@ module mcms::mcms_tests { vector[string::utf8(b"test_module")], // But only 1 module name vector[string::utf8(b"test_function")], vector[vector[0u8]], - vector[], - vector[], + mcms::zero_hash(), + mcms::zero_hash(), 0 ); } @@ -1591,8 +1626,8 @@ module mcms::mcms_tests { vector[string::utf8(b"test_module")], vector[string::utf8(b"test_function")], vector[vector[0u8]], - vector[], - vector[1u8], + mcms::zero_hash(), + SALT_ONE, MIN_DELAY - 1 ); } @@ -1612,8 +1647,8 @@ module mcms::mcms_tests { let module_names = vector[string::utf8(b"test_module")]; let function_names = vector[string::utf8(b"test_function")]; let datas = vector[vector[0u8]]; - let predecessor = vector[]; - let salt = vector[1u8]; + let predecessor = mcms::zero_hash(); + let salt = SALT_ONE; mcms::test_timelock_schedule_batch( targets, @@ -1655,8 +1690,8 @@ module mcms::mcms_tests { vector[string::utf8(b"test_module")], vector[string::utf8(b"test_function")], vector[vector[0u8]], - vector[], - vector[], + mcms::zero_hash(), + mcms::zero_hash(), 0 ); } @@ -1672,8 +1707,8 @@ module mcms::mcms_tests { let module_names = vector[string::utf8(b"test_module")]; let function_names = vector[string::utf8(b"test_function")]; let datas = vector[vector[0u8]]; - let predecessor = vector[]; - let salt = vector[1u8]; + let predecessor = mcms::zero_hash(); + let salt = SALT_ONE; let delay = 100000; mcms::test_timelock_update_min_delay(delay); @@ -1767,8 +1802,8 @@ module mcms::mcms_tests { let module_names1 = vector[string::utf8(b"test_module")]; let function_names1 = vector[string::utf8(b"test_function1")]; let datas1 = vector[vector[0u8]]; - let predecessor1 = vector[]; - let salt1 = vector[1u8]; + let predecessor1 = mcms::zero_hash(); + let salt1 = SALT_ONE; // First, schedule the first batch mcms::test_timelock_schedule_batch( @@ -1782,8 +1817,9 @@ module mcms::mcms_tests { ); // Generate a unique identifier for the second batch - let salt2 = vector[2u8]; - let predecessor2 = x"deadbeef"; // Use a non-existent operation ID as a predecessor + let salt2 = SALT_TWO; + let predecessor2 = + x"deadbeef00000000000000000000000000000000000000000000000000000000"; // Non-existent operation ID // Schedule second batch with dependency on non-existent operation let targets2 = vector[@mcms]; @@ -1829,7 +1865,7 @@ module mcms::mcms_tests { let function_names = vector[string::utf8(b"nonexistent_function")]; let datas = vector[vector[0u8]]; let predecessor = mcms::zero_hash(); - let salt = vector[1u8]; + let salt = SALT_ONE; // Schedule the batch with the non-existent function mcms::test_timelock_schedule_batch( @@ -1869,7 +1905,7 @@ module mcms::mcms_tests { let function_names = vector[string::utf8(b"timelock_update_min_delay")]; let datas = vector[bcs::to_bytes(&MIN_DELAY)]; let predecessor = mcms::zero_hash(); - let salt = vector[1u8]; + let salt = SALT_ONE; // Try to execute without scheduling first - should fail with E_OPERATION_NOT_READY mcms::timelock_execute_batch( @@ -1897,7 +1933,7 @@ module mcms::mcms_tests { let data = bcs::to_bytes(&MIN_DELAY); let datas = vector[data]; let predecessor = mcms::zero_hash(); - let salt = vector[1u8]; + let salt = SALT_ONE; mcms::test_timelock_schedule_batch( targets, @@ -1977,7 +2013,7 @@ module mcms::mcms_tests { let data = bcs::to_bytes(&MIN_DELAY); let datas_1 = vector[data]; let predecessor_1 = mcms::zero_hash(); - let salt_1 = x"abcd"; + let salt_1 = SALT_ABCD; let delay = 1; // Small delay for testing mcms::test_timelock_schedule_batch( @@ -2008,7 +2044,7 @@ module mcms::mcms_tests { let data = bcs::to_bytes(&new_delay); let datas_2 = vector[data]; let predecessor_2 = id_1; // Use first operation as predecessor - let salt_2 = x"efab"; + let salt_2 = SALT_EFAB; mcms::test_timelock_schedule_batch( targets_2, @@ -2089,7 +2125,7 @@ module mcms::mcms_tests { let function_names = vector[string::utf8(b"timelock_update_min_delay")]; let datas = vector[bcs::to_bytes(&MIN_DELAY)]; let predecessor = mcms::zero_hash(); - let salt = vector[1u8]; + let salt = SALT_ONE; mcms::test_timelock_schedule_batch( targets, @@ -2274,7 +2310,7 @@ module mcms::mcms_tests { let data = bcs::to_bytes(&MIN_DELAY); let datas = vector[data]; let predecessor = mcms::zero_hash(); - let salt = vector[1u8]; + let salt = SALT_ONE; // Use a delay that is greater than the min_delay to avoid E_INSUFFICIENT_DELAY error let delay = 100; @@ -2378,7 +2414,7 @@ module mcms::mcms_tests { let function_names = vector[string::utf8(b"set_config")]; let datas = vector[data]; let predecessor = mcms::zero_hash(); - let salt = vector[1u8]; + let salt = SALT_ONE; let delay = 1; dispatch_timelock_schedule_batch( @@ -2422,7 +2458,7 @@ module mcms::mcms_tests { // test_timelock_cancel // First schedule the operation for `set_config` - let salt = vector[2u8]; + let salt = SALT_TWO; dispatch_timelock_schedule_batch( targets, module_names, @@ -2449,7 +2485,7 @@ module mcms::mcms_tests { vector[string::utf8(b"timelock_update_min_delay")], vector[bcs::to_bytes(&100)], mcms::zero_hash(), // predecessor - vector[1u8], // salt + SALT_ONE, // salt delay // delay ); let new_delay = 2; @@ -2469,7 +2505,7 @@ module mcms::mcms_tests { vector[string::utf8(b"timelock_block_function")], vector[data], mcms::zero_hash(), // predecessor - vector[1u8], // salt + SALT_ONE, // salt new_delay // delay ); mcms::test_timelock_dispatch( @@ -2485,7 +2521,7 @@ module mcms::mcms_tests { vector[string::utf8(b"timelock_unblock_function")], vector[data], mcms::zero_hash(), // predecessor - vector[1u8], // salt + SALT_ONE, // salt new_delay // delay ); mcms::test_timelock_dispatch( diff --git a/relayer/txm/common.go b/relayer/txm/common.go index e53735a48..5ff0e9bef 100644 --- a/relayer/txm/common.go +++ b/relayer/txm/common.go @@ -757,6 +757,13 @@ func ScheduleBatchOperationsAsDeployer( } func HashOperationBatch(targets []aptos.AccountAddress, moduleNames, functionNames []string, datas [][]byte, predecessor, salt []byte) (common.Hash, error) { + if len(predecessor) != 32 { + return common.Hash{}, fmt.Errorf("predecessor must be 32 bytes, got %d", len(predecessor)) + } + if len(salt) != 32 { + return common.Hash{}, fmt.Errorf("salt must be 32 bytes, got %d", len(salt)) + } + // Verify all arrays have the same length if len(targets) != len(moduleNames) || len(targets) != len(functionNames) || len(targets) != len(datas) { return common.Hash{}, fmt.Errorf("mismatched array lengths: targets=%d, moduleNames=%d, functionNames=%d, datas=%d", diff --git a/relayer/txm/mcms_test_constants_gen_test.go b/relayer/txm/mcms_test_constants_gen_test.go new file mode 100644 index 000000000..80650f7b3 --- /dev/null +++ b/relayer/txm/mcms_test_constants_gen_test.go @@ -0,0 +1,109 @@ +package txm + +import ( + "encoding/hex" + "fmt" + "math/big" + "os" + "testing" + + "github.com/aptos-labs/aptos-go-sdk" + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/require" +) + +// Regenerate mcms_test.move e2e constants +// Run: go test ./relayer/txm -run TestRegenMcmsTestConstants -v +func TestRegenMcmsTestConstants(t *testing.T) { + mcmsAccount := aptos.AccountAddress{} + require.NoError(t, mcmsAccount.ParseStringRelaxed("0xa969156fce9a4f08bcdc07b90f338efc630bff8dfa8340500cb6414aca762a4e")) + + chainID := big.NewInt(4) + role := uint8(2) + predecessor := make([]byte, 32) + salt := make([]byte, 32) + delay := uint64(1) + + op1Data, err := SerializeScheduleBatchParams( + []TimelockOperation{{ + Target: mcmsAccount, ModuleName: "mcms_account", + FunctionName: "accept_ownership", Data: []byte{}, + }}, + predecessor, salt, delay, + ) + require.NoError(t, err) + + op1 := Op{ + Role: role, ChainID: chainID, MultiSig: mcmsAccount, Nonce: 0, + To: mcmsAccount, ModuleName: "mcms", FunctionName: "timelock_schedule_batch", + Data: op1Data, + } + op1Leaf, err := HashOp(&op1) + require.NoError(t, err) + + // Unchanged op leaves from mcms_test.move LEAVES (ops 2-4) + op2, _ := hex.DecodeString("2feec0e3a232c5c847874246203e62c43db473fe85245095122e166be9114e13") + op3, _ := hex.DecodeString("411a4726f8a920fc0a814bd9897a06f3dd0f1c799a047deaa6469f105f5a6705") + op4, _ := hex.DecodeString("cb4dffef33843b197cd33346d3339d8432b14789504167c63fb9f74a73baaea5") + metadataLeaf, _ := hex.DecodeString("a619565e90c1c564293b59b344ed0e12ed06eafb3c45b70baf6fdf299a046297") + + leaves := [][32]byte{ + bytes32(metadataLeaf), op1Leaf, + bytes32(op2), bytes32(op3), bytes32(op4), + } + tree, err := NewMerkleTree(leaves) + require.NoError(t, err) + + root := tree.GetRoot() + validUntil := uint64(1744315405) + signedHash := CalculateSignedHash(root, validUntil) + + // Use deterministic signers; addresses must match mcms_test PROPOSER_ADDR* + signers := GenerateDeterministicSigners(t) + signatures := GenerateSignatures(t, signers[:3], signedHash) + + op1Proof := tree.GetProof(1) + metadataProof := tree.GetProof(0) + + var out string + out += fmt.Sprintf("const ROOT: vector = x\"%s\";\n", hex.EncodeToString(root[:])) + out += fmt.Sprintf("const OP1_DATA: vector = x\"%s\";\n", hex.EncodeToString(op1Data)) + out += "const OP1_PROOF: vector> = vector[\n" + for i, p := range op1Proof { + if i < len(op1Proof)-1 { + out += fmt.Sprintf(" x\"%s\",\n", hex.EncodeToString(p[:])) + } else { + out += fmt.Sprintf(" x\"%s\"\n", hex.EncodeToString(p[:])) + } + } + out += "];\n" + out += "const METADATA_PROOF: vector> = vector[\n" + for i, p := range metadataProof { + if i < len(metadataProof)-1 { + out += fmt.Sprintf(" x\"%s\",\n", hex.EncodeToString(p[:])) + } else { + out += fmt.Sprintf(" x\"%s\"\n", hex.EncodeToString(p[:])) + } + } + out += "];\n" + out += "const SIGNATURES: vector> = vector[\n" + for i, sig := range signatures { + if i < len(signatures)-1 { + out += fmt.Sprintf(" x\"%s\",\n", hex.EncodeToString(sig)) + } else { + out += fmt.Sprintf(" x\"%s\"\n", hex.EncodeToString(sig)) + } + } + out += "];\n" + out += fmt.Sprintf("// LEAVES[1] op1: x\"%s\"\n", hex.EncodeToString(op1Leaf[:])) + require.NoError(t, os.WriteFile("/tmp/mcms_test_constants.txt", []byte(out), 0o644)) +} + +func bytes32(b []byte) [32]byte { + var out [32]byte + copy(out[:], b) + return out +} + +// Ensure common.Hash compatible +var _ = common.Hash{} diff --git a/relayer/txm/txm_multisig_deploy_ccip_local_test.go b/relayer/txm/txm_multisig_deploy_ccip_local_test.go index 416264513..6606552eb 100644 --- a/relayer/txm/txm_multisig_deploy_ccip_local_test.go +++ b/relayer/txm/txm_multisig_deploy_ccip_local_test.go @@ -356,7 +356,7 @@ func runDeployMCMSAndCCIPInChunks(t *testing.T, logger logger.Logger, rpcURL str // Schedule and execute operations delay := uint64(TEST_DELAY) - salt := []byte{} // Empty salt for initial deployment + salt := make([]byte, 32) // ZERO_SALT (32 bytes, matches on-chain bytes32 salt) scheduleAndExecuteOperations(t, logger, txm, allOperations, proposerSigners, deployerAddress, deployerPublicKeyHex, role, deployChainIdBig, delay, salt, false) // Verify deployment was successful @@ -441,7 +441,8 @@ func runDeployMCMSAndCCIPInChunks(t *testing.T, logger logger.Logger, rpcURL str allUpgradeOps = append(allUpgradeOps, upgradeOp) // Schedule and execute upgrade operations (with unique salt) - upgradeSalt := []byte("upgrade") // Use a different salt for the upgrade + upgradeSalt := make([]byte, 32) + copy(upgradeSalt, []byte("upgrade")) // distinct from zero salt; must be 32 bytes on-chain scheduleAndExecuteOperations(t, logger, txm, allUpgradeOps, proposerSigners, deployerAddress, deployerPublicKeyHex, role, deployChainIdBig, delay, upgradeSalt, false) diff --git a/relayer/txm/txm_multisig_local_test.go b/relayer/txm/txm_multisig_local_test.go index 659c15639..5a2bd1196 100644 --- a/relayer/txm/txm_multisig_local_test.go +++ b/relayer/txm/txm_multisig_local_test.go @@ -291,7 +291,7 @@ func runMultisigTest(t *testing.T, logger logger.Logger, rpcURL string, keystore } // ZERO_HASH (32 bytes of zeros) predecessor := make([]byte, 32) - salt := []byte{} + salt := make([]byte, 32) delay := uint64(0) rootMetadata := RootMetadata{ @@ -465,7 +465,7 @@ func runMultisigTest(t *testing.T, logger logger.Logger, rpcURL string, keystore } // ZERO_HASH (32 bytes of zeros) predecessor := make([]byte, 32) - salt := []byte{} + salt := make([]byte, 32) delay := TEST_DELAY rootMetadata := RootMetadata{ @@ -635,7 +635,7 @@ func runMultisigTest(t *testing.T, logger logger.Logger, rpcURL string, keystore } // ZERO_HASH (32 bytes of zeros) predecessor := make([]byte, 32) - salt := []byte{} + salt := make([]byte, 32) delay := TEST_DELAY rootMetadata := RootMetadata{