diff --git a/crates/rooch-framework/src/natives/gas_parameter/move_module.rs b/crates/rooch-framework/src/natives/gas_parameter/move_module.rs index fbd839c343..f6756ead71 100644 --- a/crates/rooch-framework/src/natives/gas_parameter/move_module.rs +++ b/crates/rooch-framework/src/natives/gas_parameter/move_module.rs @@ -13,6 +13,12 @@ crate::natives::gas_parameter::native::define_gas_parameters_for_natives!(GasPar [.request_init_functions.per_byte, "request_init_functions.per_byte", (5 + 1) * MUL], [.check_compatibililty_inner.base, "check_compatibililty_inner.base", (5 + 1) * MUL], [.check_compatibililty_inner.per_byte, "check_compatibililty_inner.per_byte", (5 + 1) * MUL], - [.remap_module_addresses_inner.base, "remap_module_addresses_inner.base", (5 + 1) * MUL], - [.remap_module_addresses_inner.per_byte, "remap_module_addresses_inner.per_byte", (5 + 1) * MUL], + [.replace_address_identifiers.base, "replace_address_identifiers.base", (5 + 1) * MUL], + [.replace_address_identifiers.per_byte, "replace_address_identifiers.per_byte", (5 + 1) * MUL], + [.replace_addresses_constant.base, "replace_addresses_constant.base", (5 + 1) * MUL], + [.replace_addresses_constant.per_byte, "replace_addresses_constant.per_byte", (5 + 1) * MUL], + [.replace_identifiers.base, "replace_identifiers.base", (5 + 1) * MUL], + [.replace_identifiers.per_byte, "replace_identifiers.per_byte", (5 + 1) * MUL], + [.replace_bytes_constant.base, "replace_bytes_constant.base", (5 + 1) * MUL], + [.replace_bytes_constant.per_byte, "replace_bytes_constant.per_byte", (5 + 1) * MUL], ]); diff --git a/examples/publish_modules/sources/publish.move b/examples/publish_modules/sources/publish.move index e2ebcc1c8a..a7def98aeb 100644 --- a/examples/publish_modules/sources/publish.move +++ b/examples/publish_modules/sources/publish.move @@ -5,7 +5,7 @@ /// If the entry functions argument type is changed or the bytecode of the module is changed, and the tests are not updated, the tests will fail. /// Please update the tests as follows: /// 1. Compile the example/counter with `./target/debug/rooch move test -p examples/counter -d` -/// 2. Run the following command to get the bytecode of the compiled module: `xxd -c 0 -p examples/counter/build/counter/bytecode_modules/counter.mv` +/// 2. Run the following command to get the bytecode of the compiled module: `xxd -c 99999 -p examples/counter/build/counter/bytecode_modules/counter.mv` /// 3. Copy the bytecode of the compiled module from the output of the above command, and update the `module_bytes` variable in the tests below. module rooch_examples::publish { use std::vector; @@ -26,9 +26,9 @@ module rooch_examples::publish { // with account 0x42 let module_bytes: vector = x"a11ceb0b060000000b010006020608030e26043406053a32076c7d08e9014006a902220acb02050cd002560da6030200000101010200030c00020400000005000100000600010000070201000008030400010907080108010a09010108010b0a0b0108040605060606010708010002070801060c0106080101030107080001080002070801050107090003070801060c090002060801050106090007636f756e7465720f6163636f756e745f73746f7261676507636f6e7465787407436f756e74657207436f6e7465787408696e63726561736509696e6372656173655f04696e69740576616c756511676c6f62616c5f626f72726f775f6d75740e676c6f62616c5f6d6f76655f746f0d676c6f62616c5f626f72726f77000000000000000000000000000000000000000000000000000000000000004200000000000000000000000000000000000000000000000000000000000000020520000000000000000000000000000000000000000000000000000000000000004200020108030001040001030b0011010201010000050d0b00070038000c010a01100014060100000000000000160b010f0015020200000001060b000b0106000000000000000012003801020301000001060b000700380210001402000000"; let modules = vector::singleton(move_module::new(module_bytes)); - let old_addresses = vector::singleton(@0x42); - let new_addresses = vector::singleton(signer::address_of(account)); - let remapped_modules = move_module::remap_module_addresses(modules, old_addresses, new_addresses); + let old_address = @0x42; + let new_address = signer::address_of(account); + let remapped_modules = move_module::binding_module_address(modules, old_address, new_address); account_storage::publish_modules(ctx, account, remapped_modules); } } diff --git a/moveos/moveos-stdlib/moveos-stdlib/doc/move_module.md b/moveos/moveos-stdlib/moveos-stdlib/doc/move_module.md index 75d63747ae..65322e2fca 100644 --- a/moveos/moveos-stdlib/moveos-stdlib/doc/move_module.md +++ b/moveos/moveos-stdlib/moveos-stdlib/doc/move_module.md @@ -12,12 +12,19 @@ - [Function `module_name`](#0x2_move_module_module_name) - [Function `sort_and_verify_modules`](#0x2_move_module_sort_and_verify_modules) - [Function `check_comatibility`](#0x2_move_module_check_comatibility) -- [Function `remap_module_addresses`](#0x2_move_module_remap_module_addresses) +- [Function `binding_module_address`](#0x2_move_module_binding_module_address) +- [Function `replace_module_identiner`](#0x2_move_module_replace_module_identiner) +- [Function `replace_struct_identifier`](#0x2_move_module_replace_struct_identifier) - [Function `request_init_functions`](#0x2_move_module_request_init_functions) +- [Function `replace_address_identifiers`](#0x2_move_module_replace_address_identifiers) +- [Function `replace_identifiers`](#0x2_move_module_replace_identifiers) +- [Function `replace_addresses_constant`](#0x2_move_module_replace_addresses_constant) +- [Function `replace_bytes_constant`](#0x2_move_module_replace_bytes_constant)
use 0x1::error;
 use 0x1::string;
+use 0x1::vector;
 
@@ -207,15 +214,14 @@ Abort if the new module is not compatible with the old module. - + -## Function `remap_module_addresses` +## Function `binding_module_address` -Remap addresses in module binary where the length of -old_addresses must equal to that of new_addresses. +Binding given module's address to the new address -
public fun remap_module_addresses(modules: vector<move_module::MoveModule>, old_addresses: vector<address>, new_addresses: vector<address>): vector<move_module::MoveModule>
+
public fun binding_module_address(modules: vector<move_module::MoveModule>, old_address: address, new_address: address): vector<move_module::MoveModule>
 
@@ -224,13 +230,64 @@ Remap addresses in module binary where the length of Implementation -
public fun remap_module_addresses(
+
public fun binding_module_address(
     modules: vector<MoveModule>,
-    old_addresses: vector<address>,
-    new_addresses: vector<address>,
+    old_address: address,
+    new_address: address,
+): vector<MoveModule> {
+    let bytes_vec = vector::empty<vector<u8>>();
+    let i = 0u64;
+    let len = vector::length(&modules);
+    while (i < len) {
+        vector::push_back(&mut bytes_vec, vector::pop_back(&mut modules).byte_codes);
+        i = i + 1;
+    };
+    let old_addresses = vector::singleton(old_address);
+    let new_addresses = vector::singleton(new_address);
+
+    let rebinded_bytes = replace_address_identifiers(bytes_vec, old_addresses, new_addresses);
+    let rebinded_bytes = replace_addresses_constant(rebinded_bytes, old_addresses, new_addresses);
+    let rebinded_modules = vector::empty<MoveModule>();
+    i = 0u64;
+    let len = vector::length(&rebinded_bytes);
+    while (i < len) {
+        vector::push_back(&mut rebinded_modules, MoveModule {
+            byte_codes: vector::pop_back(&mut rebinded_bytes),
+        });
+        i = i + 1;
+    };
+    vector::destroy_empty(rebinded_bytes);
+    rebinded_modules
+}
+
+ + + + + + + +## Function `replace_module_identiner` + +Replace given module's identifier to the new ones + + +
public fun replace_module_identiner(modules: vector<move_module::MoveModule>, old_names: vector<string::String>, new_names: vector<string::String>): vector<move_module::MoveModule>
+
+ + + +
+Implementation + + +
public fun replace_module_identiner (
+    modules: vector<MoveModule>,
+    old_names: vector<String>,
+    new_names: vector<String>,
 ): vector<MoveModule> {
     assert!(
-        vector::length(&old_addresses) == vector::length(&new_addresses),
+        vector::length(&old_names) == vector::length(&new_names),
         error::invalid_argument(ErrorLengthNotMatch)
     );
     let bytes_vec = vector::empty<vector<u8>>();
@@ -240,19 +297,48 @@ Remap addresses in module binary where the length of
         vector::push_back(&mut bytes_vec, vector::pop_back(&mut modules).byte_codes);
         i = i + 1;
     };
-    let remapped_bytes = remap_module_addresses_inner(bytes_vec, old_addresses, new_addresses);
-    // let remapped_bytes = remap_module_addresses_inner(bytes_vec);
-    let remapped_modules = vector::empty<MoveModule>();
+
+    let rebinded_bytes = replace_identifiers(bytes_vec, old_names, new_names);
+    let rebinded_modules = vector::empty<MoveModule>();
     i = 0u64;
-    let len = vector::length(&remapped_bytes);
+    let len = vector::length(&rebinded_bytes);
     while (i < len) {
-        vector::push_back(&mut remapped_modules, MoveModule {
-            byte_codes: vector::pop_back(&mut remapped_bytes),
+        vector::push_back(&mut rebinded_modules, MoveModule {
+            byte_codes: vector::pop_back(&mut rebinded_bytes),
         });
         i = i + 1;
     };
-    vector::destroy_empty(remapped_bytes);
-    remapped_modules
+    vector::destroy_empty(rebinded_bytes);
+    rebinded_modules
+}
+
+ + + +
+ + + +## Function `replace_struct_identifier` + +Replace given struct's identifier to the new ones + + +
public fun replace_struct_identifier(modules: vector<move_module::MoveModule>, old_names: vector<string::String>, new_names: vector<string::String>): vector<move_module::MoveModule>
+
+ + + +
+Implementation + + +
public fun replace_struct_identifier(
+    modules: vector<MoveModule>,
+    old_names: vector<String>,
+    new_names: vector<String>,
+): vector<MoveModule> {
+    replace_module_identiner(modules, old_names, new_names)
 }
 
@@ -283,4 +369,115 @@ account_address: address of all the modules +
+ + + +## Function `replace_address_identifiers` + +Native function to replace addresses identifier in module binary where the length of +old_addresses must equal to that of new_addresses. + + +
public(friend) fun replace_address_identifiers(bytes: vector<vector<u8>>, old_addresses: vector<address>, new_addresses: vector<address>): vector<vector<u8>>
+
+ + + +
+Implementation + + +
native public(friend) fun replace_address_identifiers(
+    bytes: vector<vector<u8>>,
+    old_addresses: vector<address>,
+    new_addresses: vector<address>,
+): vector<vector<u8>>;
+
+ + + +
+ + + +## Function `replace_identifiers` + +Native function to replace the name identifier old_name to new_name in module binary. + + +
public(friend) fun replace_identifiers(bytes: vector<vector<u8>>, old_idents: vector<string::String>, new_idents: vector<string::String>): vector<vector<u8>>
+
+ + + +
+Implementation + + +
native public(friend) fun replace_identifiers(
+    bytes: vector<vector<u8>>,
+    old_idents: vector<String>,
+    new_idents: vector<String>,
+): vector<vector<u8>>;
+
+ + + +
+ + + +## Function `replace_addresses_constant` + +Native function to replace constant addresses in module binary where the length of +old_addresses must equal to that of new_addresses. + + +
public(friend) fun replace_addresses_constant(bytes: vector<vector<u8>>, old_addresses: vector<address>, new_addresses: vector<address>): vector<vector<u8>>
+
+ + + +
+Implementation + + +
native public(friend) fun replace_addresses_constant(
+    bytes: vector<vector<u8>>,
+    old_addresses: vector<address>,
+    new_addresses: vector<address>,
+): vector<vector<u8>>;
+
+ + + +
+ + + +## Function `replace_bytes_constant` + +Native function to replace constant bytes in module binary where the length of +old_bytes must equal to that of new_bytes. + + +
public(friend) fun replace_bytes_constant(bytes: vector<vector<u8>>, old_bytes: vector<vector<u8>>, new_bytes: vector<vector<u8>>): vector<vector<u8>>
+
+ + + +
+Implementation + + +
native public(friend) fun replace_bytes_constant(
+    bytes: vector<vector<u8>>,
+    old_bytes: vector<vector<u8>>,
+    new_bytes: vector<vector<u8>>,
+): vector<vector<u8>>;
+
+ + +
diff --git a/moveos/moveos-stdlib/moveos-stdlib/sources/move_module.move b/moveos/moveos-stdlib/moveos-stdlib/sources/move_module.move index e805b3c91f..8a789ec3df 100644 --- a/moveos/moveos-stdlib/moveos-stdlib/sources/move_module.move +++ b/moveos/moveos-stdlib/moveos-stdlib/sources/move_module.move @@ -55,15 +55,45 @@ module moveos_std::move_module { check_compatibililty_inner(new_module.byte_codes, old_module.byte_codes); } - /// Remap addresses in module binary where the length of - /// `old_addresses` must equal to that of `new_addresses`. - public fun remap_module_addresses( + /// Binding given module's address to the new address + public fun binding_module_address( modules: vector, - old_addresses: vector
, - new_addresses: vector
, + old_address: address, + new_address: address, + ): vector { + let bytes_vec = vector::empty>(); + let i = 0u64; + let len = vector::length(&modules); + while (i < len) { + vector::push_back(&mut bytes_vec, vector::pop_back(&mut modules).byte_codes); + i = i + 1; + }; + let old_addresses = vector::singleton(old_address); + let new_addresses = vector::singleton(new_address); + + let rebinded_bytes = replace_address_identifiers(bytes_vec, old_addresses, new_addresses); + let rebinded_bytes = replace_addresses_constant(rebinded_bytes, old_addresses, new_addresses); + let rebinded_modules = vector::empty(); + i = 0u64; + let len = vector::length(&rebinded_bytes); + while (i < len) { + vector::push_back(&mut rebinded_modules, MoveModule { + byte_codes: vector::pop_back(&mut rebinded_bytes), + }); + i = i + 1; + }; + vector::destroy_empty(rebinded_bytes); + rebinded_modules + } + + /// Replace given module's identifier to the new ones + public fun replace_module_identiner ( + modules: vector, + old_names: vector, + new_names: vector, ): vector { assert!( - vector::length(&old_addresses) == vector::length(&new_addresses), + vector::length(&old_names) == vector::length(&new_names), error::invalid_argument(ErrorLengthNotMatch) ); let bytes_vec = vector::empty>(); @@ -73,19 +103,28 @@ module moveos_std::move_module { vector::push_back(&mut bytes_vec, vector::pop_back(&mut modules).byte_codes); i = i + 1; }; - let remapped_bytes = remap_module_addresses_inner(bytes_vec, old_addresses, new_addresses); - // let remapped_bytes = remap_module_addresses_inner(bytes_vec); - let remapped_modules = vector::empty(); + + let rebinded_bytes = replace_identifiers(bytes_vec, old_names, new_names); + let rebinded_modules = vector::empty(); i = 0u64; - let len = vector::length(&remapped_bytes); + let len = vector::length(&rebinded_bytes); while (i < len) { - vector::push_back(&mut remapped_modules, MoveModule { - byte_codes: vector::pop_back(&mut remapped_bytes), + vector::push_back(&mut rebinded_modules, MoveModule { + byte_codes: vector::pop_back(&mut rebinded_bytes), }); i = i + 1; }; - vector::destroy_empty(remapped_bytes); - remapped_modules + vector::destroy_empty(rebinded_bytes); + rebinded_modules + } + + /// Replace given struct's identifier to the new ones + public fun replace_struct_identifier( + modules: vector, + old_names: vector, + new_names: vector, + ): vector { + replace_module_identiner(modules, old_names, new_names) } native fun module_name_inner(byte_codes: &vector): String; @@ -101,14 +140,36 @@ module moveos_std::move_module { native fun check_compatibililty_inner(new_bytecodes: vector, old_bytecodes: vector); - /// Native function to remap addresses in module binary where the length of - /// `old_addresses` must equal to that of `new_addresses`. - native fun remap_module_addresses_inner( + /// Native function to replace addresses identifier in module binary where the length of + /// `old_addresses` must equal to that of `new_addresses`. + native public(friend) fun replace_address_identifiers( + bytes: vector>, + old_addresses: vector
, + new_addresses: vector
, + ): vector>; + + /// Native function to replace the name identifier `old_name` to `new_name` in module binary. + native public(friend) fun replace_identifiers( + bytes: vector>, + old_idents: vector, + new_idents: vector, + ): vector>; + + /// Native function to replace constant addresses in module binary where the length of + /// `old_addresses` must equal to that of `new_addresses`. + native public(friend) fun replace_addresses_constant( bytes: vector>, old_addresses: vector
, new_addresses: vector
, ): vector>; + /// Native function to replace constant bytes in module binary where the length of + /// `old_bytes` must equal to that of `new_bytes`. + native public(friend) fun replace_bytes_constant( + bytes: vector>, + old_bytes: vector>, + new_bytes: vector>, + ): vector>; #[test_only] use std::debug; @@ -160,22 +221,94 @@ module moveos_std::move_module { } #[test(account=@0x1314)] - fun test_remap_address(account: &signer) { + fun test_binding_module_address(account: &signer) { let addr = signer::address_of(account); let ctx = context::new_test_context(addr); - // The following is the bytes and hex of the compiled module: example/counter/sources/counter.move - // with account 0x42 + // The following is the bytes and hex of the compiled module: + // example/counter/sources/counter.move with account 0x1314 + let ref_bytes: vector = x"a11ceb0b060000000b010006020608030e26043406053a32076c7d08e9014006a902220acb02050cd002560da6030200000101010200030c00020400000005000100000600010000070201000008030400010907080108010a09010108010b0a0b0108040605060606010708010002070801060c0106080101030107080001080002070801050107090003070801060c090002060801050106090007636f756e7465720f6163636f756e745f73746f7261676507636f6e7465787407436f756e74657207436f6e7465787408696e63726561736509696e6372656173655f04696e69740576616c756511676c6f62616c5f626f72726f775f6d75740e676c6f62616c5f6d6f76655f746f0d676c6f62616c5f626f72726f77000000000000000000000000000000000000000000000000000000000000131400000000000000000000000000000000000000000000000000000000000000020520000000000000000000000000000000000000000000000000000000000000131400020108030001040001030b0011010201010000050d0b00070038000c010a01100014060100000000000000160b010f0015020200000001060b000b0106000000000000000012003801020301000001060b000700380210001402000000"; + + // The following is the bytes and hex of the compiled module: + // example/counter/sources/counter.move with account 0x42 let module_bytes: vector = x"a11ceb0b060000000b010006020608030e26043406053a32076c7d08e9014006a902220acb02050cd002560da6030200000101010200030c00020400000005000100000600010000070201000008030400010907080108010a09010108010b0a0b0108040605060606010708010002070801060c0106080101030107080001080002070801050107090003070801060c090002060801050106090007636f756e7465720f6163636f756e745f73746f7261676507636f6e7465787407436f756e74657207436f6e7465787408696e63726561736509696e6372656173655f04696e69740576616c756511676c6f62616c5f626f72726f775f6d75740e676c6f62616c5f6d6f76655f746f0d676c6f62616c5f626f72726f77000000000000000000000000000000000000000000000000000000000000004200000000000000000000000000000000000000000000000000000000000000020520000000000000000000000000000000000000000000000000000000000000004200020108030001040001030b0011010201010000050d0b00070038000c010a01100014060100000000000000160b010f0015020200000001060b000b0106000000000000000012003801020301000001060b000700380210001402000000"; + let m: MoveModule = Self::new(module_bytes); let modules = vector::singleton(m); - let new_addresses = vector::singleton(addr); - let old_addresses = vector::singleton(@0x42); - let remapped_modules = Self::remap_module_addresses(modules, old_addresses, new_addresses); + let new_address = addr; + let old_address = @0x42; + let remapped_modules = Self::binding_module_address(modules, old_address, new_address); // In `sort_and_verify_modules`, addresses of modules are ensured to be the same with signer address // So if the remapping is failed, the verification will fail let (module_names, _module_names_with_init_fn) = Self::sort_and_verify_modules(&remapped_modules, addr); + + // compare the remapped modules bytes + let modified_bytes = vector::borrow(&remapped_modules, 0).byte_codes; + assert!(std::compare::cmp_bcs_bytes(&modified_bytes, &ref_bytes) == 0u8, 1); debug::print(&module_names); context::drop_test_context(ctx); } + + #[test(account=@0x42)] + fun test_replace_module_and_struct_name(account: &signer) { + let addr = signer::address_of(account); + let ctx = context::new_test_context(addr); + // The following is the bytes and hex of `my_counter` module with account 0x42 + // `my_counter` is from `example/counter/sources/counter.move` with the following modification: + // 1. module name: counter -> my_counter + // 2. struct name: MyCounter -> MyCounter + let ref_bytes: vector = x"a11ceb0b060000000b010006020608030e26043406053a32076c820108ee014006ae02220ad002050cd502560dab030200000101010200030c00020400000005000100000600010000070201000008030400010907080108010a09010108010b0a0b0108040605060606010708010002070801060c0106080101030107080001080002070801050107090003070801060c09000206080105010609000a6d795f636f756e7465720f6163636f756e745f73746f7261676507636f6e74657874094d79436f756e74657207436f6e7465787408696e63726561736509696e6372656173655f04696e69740576616c756511676c6f62616c5f626f72726f775f6d75740e676c6f62616c5f6d6f76655f746f0d676c6f62616c5f626f72726f77000000000000000000000000000000000000000000000000000000000000004200000000000000000000000000000000000000000000000000000000000000020520000000000000000000000000000000000000000000000000000000000000004200020108030001040001030b0011010201010000050d0b00070038000c010a01100014060100000000000000160b010f0015020200000001060b000b0106000000000000000012003801020301000001060b000700380210001402000000"; + + // The following is the bytes and hex of the compiled module: + // example/counter/sources/counter.move with account 0x42 + let module_bytes: vector = x"a11ceb0b060000000b010006020608030e26043406053a32076c7d08e9014006a902220acb02050cd002560da6030200000101010200030c00020400000005000100000600010000070201000008030400010907080108010a09010108010b0a0b0108040605060606010708010002070801060c0106080101030107080001080002070801050107090003070801060c090002060801050106090007636f756e7465720f6163636f756e745f73746f7261676507636f6e7465787407436f756e74657207436f6e7465787408696e63726561736509696e6372656173655f04696e69740576616c756511676c6f62616c5f626f72726f775f6d75740e676c6f62616c5f6d6f76655f746f0d676c6f62616c5f626f72726f77000000000000000000000000000000000000000000000000000000000000004200000000000000000000000000000000000000000000000000000000000000020520000000000000000000000000000000000000000000000000000000000000004200020108030001040001030b0011010201010000050d0b00070038000c010a01100014060100000000000000160b010f0015020200000001060b000b0106000000000000000012003801020301000001060b000700380210001402000000"; + + let modules = vector::singleton(module_bytes); + let new_names = vector::empty(); + vector::push_back(&mut new_names, std::string::utf8(b"my_counter")); + vector::push_back(&mut new_names, std::string::utf8(b"MyCounter")); + let old_names = vector::empty(); + vector::push_back(&mut old_names, std::string::utf8(b"counter")); + vector::push_back(&mut old_names, std::string::utf8(b"Counter")); + + let new_modules = Self::replace_identifiers(modules, old_names, new_names); + + // compare the remapped modules bytes + let modified_bytes = vector::borrow(&new_modules, 0); + assert!(std::compare::cmp_bcs_bytes(modified_bytes, &ref_bytes) == 0u8, 1); + + context::drop_test_context(ctx); + } + + + #[test(account=@0x42)] + fun test_replace_string_constant(account: &signer) { + let addr = signer::address_of(account); + let ctx = context::new_test_context(addr); + // The following is the bytes and hex of module with account 0x42 + // The module is from `examples/coins/sources/fixed_supply_coin.move` with the following modification: + // 1. constance string: "Fixed Supply Coin" -> "My Fixed Supply Coin" + // 2. constance string: "FSC" -> "MFSC" + let ref_bytes: vector = x"a11ceb0b060000000b010012021220033250048201140596017e079402bc0208d004800106d005640ab4060e0cc2067d0dbf070200000101020202030204020503060307030800090c00000a0800030b0000040f0e0100010810080007110001080101170700000c000100000d020100051204050002130708010804140a08010808150c0d010806160e01010c0118101100071912010108051a01130100071b140d0108081c0215010c081616010108021d1701010803060409050b060b080b090b0a0b0b0b0c0b0d0602070802060c000107080202050b0501080001060c010501080102070802050107090001080401070b03010900010800020708040f010b0501090003070802050b05010900030b050108000b030108040c010a02010806040708020806080602010c020708020f010b03010804020708040b0501090003070802060c09001166697865645f737570706c795f636f696e06737472696e670f6163636f756e745f73746f7261676507636f6e746578740a6f626a6563745f726566067369676e6572126163636f756e745f636f696e5f73746f726504636f696e0a636f696e5f73746f72650346534308547265617375727907436f6e746578740666617563657404696e69740b64756d6d795f6669656c64094f626a65637452656609436f696e53746f726504436f696e0a616464726573735f6f6611676c6f62616c5f626f72726f775f6d75740a626f72726f775f6d7574087769746864726177076465706f73697406537472696e6704757466380f72656769737465725f657874656e640d6d6f64756c655f7369676e65720b6d696e745f657874656e64116372656174655f636f696e5f73746f72650e676c6f62616c5f6d6f76655f746f00000000000000000000000000000000000000000000000000000000000000420000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000030f2000b4f9e430000000000000000000000000000000000000000000000000000000052000000000000000000000000000000000000000000000000000000000000000420a0215144d7920466978656420537570706c7920436f696e0a0205044d4653430002010e01010201080b030108040001040003100b0111020c020a00070138000f0038014a102700000000000000000000000000000000000000000000000000000000000038020c030b000b020b03380302010000000f1a0a0007021107070311073101380438050c030a00070038060c010a0038070c020d0238010b0138080b000e030b021201380902010000"; + + // The following is the bytes and hex of the compiled module: + // `examples/coins/sources/fixed_supply_coin.move` with account 0x42 + let module_bytes: vector = x"a11ceb0b060000000b010012021220033250048201140596017e079402bc0208d004800106d005600ab0060e0cbe067d0dbb070200000101020202030204020503060307030800090c00000a0800030b0000040f0e0100010810080007110001080101170700000c000100000d020100051204050002130708010804140a08010808150c0d010806160e01010c0118101100071912010108051a01130100071b140d0108081c0215010c081616010108021d1701010803060409050b060b080b090b0a0b0b0b0c0b0d0602070802060c000107080202050b0501080001060c010501080102070802050107090001080401070b03010900010800020708040f010b0501090003070802050b05010900030b050108000b030108040c010a02010806040708020806080602010c020708020f010b03010804020708040b0501090003070802060c09001166697865645f737570706c795f636f696e06737472696e670f6163636f756e745f73746f7261676507636f6e746578740a6f626a6563745f726566067369676e6572126163636f756e745f636f696e5f73746f726504636f696e0a636f696e5f73746f72650346534308547265617375727907436f6e746578740666617563657404696e69740b64756d6d795f6669656c64094f626a65637452656609436f696e53746f726504436f696e0a616464726573735f6f6611676c6f62616c5f626f72726f775f6d75740a626f72726f775f6d7574087769746864726177076465706f73697406537472696e6704757466380f72656769737465725f657874656e640d6d6f64756c655f7369676e65720b6d696e745f657874656e64116372656174655f636f696e5f73746f72650e676c6f62616c5f6d6f76655f746f00000000000000000000000000000000000000000000000000000000000000420000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000030f2000b4f9e430000000000000000000000000000000000000000000000000000000052000000000000000000000000000000000000000000000000000000000000000420a021211466978656420537570706c7920436f696e0a0204034653430002010e01010201080b030108040001040003100b0111020c020a00070138000f0038014a102700000000000000000000000000000000000000000000000000000000000038020c030b000b020b03380302010000000f1a0a0007021107070311073101380438050c030a00070038060c010a0038070c020d0238010b0138080b000e030b021201380902010000"; + + let modules = vector::singleton(module_bytes); + let new_bytes = vector::empty>(); + vector::push_back(&mut new_bytes, b"My Fixed Supply Coin"); + vector::push_back(&mut new_bytes, b"MFSC"); + let old_bytes = vector::empty>(); + vector::push_back(&mut old_bytes, b"Fixed Supply Coin"); + vector::push_back(&mut old_bytes, b"FSC"); + + let new_modules = Self::replace_bytes_constant(modules, old_bytes, new_bytes); + + // compare the remapped modules bytes + let modified_bytes = vector::borrow(&new_modules, 0); + assert!(std::compare::cmp_bcs_bytes(modified_bytes, &ref_bytes) == 0u8, 1); + + context::drop_test_context(ctx); + } } \ No newline at end of file diff --git a/moveos/moveos-stdlib/src/natives/moveos_stdlib/move_module.rs b/moveos/moveos-stdlib/src/natives/moveos_stdlib/move_module.rs index c89fdc7ef2..b5bfe9b1d4 100644 --- a/moveos/moveos-stdlib/src/natives/moveos_stdlib/move_module.rs +++ b/moveos/moveos-stdlib/src/natives/moveos_stdlib/move_module.rs @@ -204,19 +204,9 @@ fn request_init_functions( let account_address = pop_arg!(args, AccountAddress); let module_context = context.extensions_mut().get_mut::(); for name_str in pop_arg!(args, Vec) { - let mut fields = name_str.value_as::()?.unpack()?; // std::string::String; - let val = fields.next().ok_or_else(|| { - PartialVMError::new(StatusCode::TYPE_RESOLUTION_FAILURE) - .with_message("There must have only one field".to_owned()) - })?; - let name_bytes = val.value_as::>()?; - cost += gas_params.per_byte * NumBytes::new(name_bytes.len() as u64); - let module_id = ModuleId::new( - account_address, - Identifier::from_utf8(name_bytes).map_err(|e| { - PartialVMError::new(StatusCode::TYPE_RESOLUTION_FAILURE).with_message(e.to_string()) - })?, - ); + let name_ident = unpack_string_to_identifier(name_str)?; + cost += gas_params.per_byte * NumBytes::new(1u64); + let module_id = ModuleId::new(account_address, name_ident); module_context.init_functions.insert(module_id); } Ok(NativeResult::ok(cost, smallvec![])) @@ -270,22 +260,80 @@ fn check_compatibililty_inner( } /*************************************************************************************************** - * native fun remap_module_addresses_inner( + * native fun replace_address_identifiers( * bytes: vector>, * old_addresses: vector
, * new_addresses: vector
, - * ): (vector, vector>); + * ): vector>; * Native function to remap addresses in module binary where the length of * `old_addresses` must equal to that of `new_addresses`. **************************************************************************************************/ #[derive(Debug, Clone)] -pub struct RemapAddressesGasParameters { +pub struct ReplaceAddressIdentifierGasParameters { + pub base: InternalGas, + pub per_byte: InternalGasPerByte, +} + +fn replace_address_identifiers( + gas_params: &ReplaceAddressIdentifierGasParameters, + _context: &mut NativeContext, + _ty_args: Vec, + mut args: VecDeque, +) -> PartialVMResult { + debug_assert!(args.len() == 3, "Wrong number of arguments"); + let mut cost = gas_params.base; + let new_address_vec = pop_arg!(args, Vector); + let old_address_vec = pop_arg!(args, Vector); + let num_addresses = new_address_vec.elem_views().len(); + if num_addresses != old_address_vec.elem_views().len() { + return Ok(NativeResult::err( + cost, + moveos_types::move_std::error::invalid_argument(E_LENTH_NOT_MATCH), + )); + }; + let num_addresses = num_addresses as u64; + let new_addresses = new_address_vec.unpack(&Type::Address, num_addresses)?; + let old_addresses = old_address_vec.unpack(&Type::Address, num_addresses)?; + + let address_mapping: HashMap = + zip_eq(old_addresses, new_addresses) + .map(|(a, b)| { + Ok(( + a.value_as::()?, + b.value_as::()?, + )) + }) + .collect::>()?; + + let mut bundle = vec![]; + for module in pop_arg!(args, Vec) { + let byte_codes = module.value_as::>()?; + cost += gas_params.per_byte * NumBytes::new(byte_codes.len() as u64); + bundle.push(byte_codes); + } + let output_modules = modify_modules(bundle, |module| { + module_replace_address_identifiers(module, &address_mapping) + })?; + Ok(NativeResult::ok(cost, smallvec![output_modules])) +} + +/*************************************************************************************************** + * native fun replace_addresses_constant( + * bytes: vector>, + * old_addresses: vector
, + * new_addresses: vector
, + * ): (vector, vector>); + * Native function to replace constant addresses in module binary where the length of + * `old_addresses` must equal to that of `new_addresses`. + **************************************************************************************************/ +#[derive(Debug, Clone)] +pub struct ReplaceAddressConstantGasParameters { pub base: InternalGas, pub per_byte: InternalGasPerByte, } -fn remap_module_addresses_inner( - gas_params: &RemapAddressesGasParameters, +fn replace_addresses_constant( + gas_params: &ReplaceAddressConstantGasParameters, _context: &mut NativeContext, _ty_args: Vec, mut args: VecDeque, @@ -321,39 +369,166 @@ fn remap_module_addresses_inner( cost += gas_params.per_byte * NumBytes::new(byte_codes.len() as u64); bundle.push(byte_codes); } - let mut compiled_modules = bundle + let output_modules = modify_modules(bundle, |module| { + module_replace_constant_addresses(module, &address_mapping) + })?; + Ok(NativeResult::ok(cost, smallvec![output_modules])) +} + +/*************************************************************************************************** + * native fun replace_identifiers( + * bytes: vector>, + * old_idents: vector, + * new_idents: vector, + * ): vector>; + * Native function to replace the name identifier `old_idents` to `new_idents` in module binary. + **************************************************************************************************/ +#[derive(Debug, Clone)] +pub struct ReplaceIdentifierGasParameters { + pub base: InternalGas, + pub per_byte: InternalGasPerByte, +} + +fn replace_identifiers( + gas_params: &ReplaceIdentifierGasParameters, + _context: &mut NativeContext, + _ty_args: Vec, + mut args: VecDeque, +) -> PartialVMResult { + debug_assert!(args.len() == 3, "Wrong number of arguments"); + let mut cost = gas_params.base; + + let new_identifiers = pop_arg!(args, Vec); + let old_identifiers = pop_arg!(args, Vec); + let num_identifiers = new_identifiers.len(); + if num_identifiers != old_identifiers.len() { + return Ok(NativeResult::err( + cost, + moveos_types::move_std::error::invalid_argument(E_LENTH_NOT_MATCH), + )); + }; + + let identifier_mapping: HashMap = + zip_eq(old_identifiers, new_identifiers) + .map(|(a, b)| { + Ok(( + unpack_string_to_identifier(a)?, + unpack_string_to_identifier(b)?, + )) + }) + .collect::>()?; + + let mut bundle = vec![]; + for module in pop_arg!(args, Vec) { + let byte_codes = module.value_as::>()?; + cost += gas_params.per_byte * NumBytes::new(byte_codes.len() as u64); + bundle.push(byte_codes); + } + let output_modules = modify_modules(bundle, |module| { + module_replace_identifiers(module, &identifier_mapping) + })?; + Ok(NativeResult::ok(cost, smallvec![output_modules])) +} + +/*************************************************************************************************** + * native public(friend) fun replace_bytes_constant( + * bytes: vector>, + * old_bytes: vector>, + * new_bytes: vector>, + * ): vector>; + * Native function to replace the name identifier `old_idents` to `new_idents` in module binary. + **************************************************************************************************/ +#[derive(Debug, Clone)] +pub struct ReplaceBytesConstantfierGasParameters { + pub base: InternalGas, + pub per_byte: InternalGasPerByte, +} + +fn replace_bytes_constant( + gas_params: &ReplaceBytesConstantfierGasParameters, + _context: &mut NativeContext, + _ty_args: Vec, + mut args: VecDeque, +) -> PartialVMResult { + debug_assert!(args.len() == 3, "Wrong number of arguments"); + let mut cost = gas_params.base; + + let new_bytes = pop_arg!(args, Vec); + let old_bytes = pop_arg!(args, Vec); + let num = new_bytes.len(); + if num != old_bytes.len() { + return Ok(NativeResult::err( + cost, + moveos_types::move_std::error::invalid_argument(E_LENTH_NOT_MATCH), + )); + }; + + let bytes_mapping: HashMap, Vec> = zip_eq(old_bytes, new_bytes) + .map(|(a, b)| Ok((a.value_as::>()?, b.value_as::>()?))) + .collect::>()?; + + let mut bundle = vec![]; + for module in pop_arg!(args, Vec) { + let byte_codes = module.value_as::>()?; + cost += gas_params.per_byte * NumBytes::new(byte_codes.len() as u64); + bundle.push(byte_codes); + } + let output_modules = modify_modules(bundle, |module| { + module_replace_constant_bytes(module, &bytes_mapping) + })?; + Ok(NativeResult::ok(cost, smallvec![output_modules])) +} + +fn modify_modules( + module_bundles: Vec>, + replace_fn: impl Fn(&mut CompiledModule) -> PartialVMResult<()>, +) -> PartialVMResult { + let mut compiled_modules = module_bundles .into_iter() .map(|b| CompiledModule::deserialize(&b)) .collect::>>()?; let mut remapped_bubdles = vec![]; - for m in compiled_modules.iter_mut() { - // TODO: charge gas - module_remap_addresses(m, &address_mapping)?; + for module in compiled_modules.iter_mut() { + replace_fn(module)?; let mut binary: Vec = vec![]; - m.serialize(&mut binary).map_err(|e| { + module.serialize(&mut binary).map_err(|e| { PartialVMError::new(StatusCode::VALUE_SERIALIZATION_ERROR).with_message(e.to_string()) })?; let value = Value::vector_u8(binary); remapped_bubdles.push(value); } let output_modules = Vector::pack(&Type::Vector(Box::new(Type::U8)), remapped_bubdles)?; - Ok(NativeResult::ok(cost, smallvec![output_modules])) + Ok(output_modules) } -fn module_remap_constant_addresses(value: &mut MoveValue, f: &dyn Fn(&mut AccountAddress)) { +fn movevalue_replace_addresses(value: &mut MoveValue, f: &dyn Fn(&mut AccountAddress)) { match value { MoveValue::Address(addr) => f(addr), MoveValue::Vector(vals) => { vals.iter_mut() - .for_each(|val| module_remap_constant_addresses(val, f)); + .for_each(|val| movevalue_replace_addresses(val, f)); } // TODO: handle constant addresses in Other struct _ => {} } } -fn module_remap_addresses( +fn module_replace_identifiers( + module: &mut CompiledModule, + identifier_mapping: &HashMap, +) -> PartialVMResult<()> { + for i in 0..module.identifiers.len() { + if let Some(new_ident) = identifier_mapping.get(&module.identifiers[i]) { + module.identifiers[i] = Identifier::new(new_ident.to_string()).map_err(|e| { + PartialVMError::new(StatusCode::TYPE_RESOLUTION_FAILURE).with_message(e.to_string()) + })?; + } + } + Ok(()) +} + +fn module_replace_address_identifiers( module: &mut CompiledModule, address_mapping: &HashMap, ) -> PartialVMResult<()> { @@ -363,6 +538,13 @@ fn module_remap_addresses( *addr = *new_addr; } } + Ok(()) +} + +fn module_replace_constant_addresses( + module: &mut CompiledModule, + address_mapping: &HashMap, +) -> PartialVMResult<()> { // replace addresses in constant. for constant in module.constant_pool.iter_mut() { let mut constant_value = constant.deserialize_constant().ok_or_else(|| { @@ -370,7 +552,7 @@ fn module_remap_addresses( .with_message("cannot deserialize constant".to_string()) })?; - module_remap_constant_addresses(&mut constant_value, &|addr| { + movevalue_replace_addresses(&mut constant_value, &|addr| { if let Some(new_addr) = address_mapping.get(addr) { *addr = *new_addr; } @@ -385,6 +567,46 @@ fn module_remap_addresses( Ok(()) } +fn module_replace_constant_bytes( + module: &mut CompiledModule, + bytes_mapping: &HashMap, Vec>, +) -> PartialVMResult<()> { + // replace bytes in constant. + for constant in module.constant_pool.iter_mut() { + let constant_value = constant.deserialize_constant().ok_or_else(|| { + PartialVMError::new(StatusCode::VALUE_DESERIALIZATION_ERROR) + .with_message("cannot deserialize constant".to_string()) + })?; + + if let MoveValue::Vector(vals) = constant_value { + if let Ok(bytes) = MoveValue::vec_to_vec_u8(vals) { + if let Some(new_bytes) = bytes_mapping.get(&bytes) { + constant.data = MoveValue::vector_u8(new_bytes.clone()) + .simple_serialize() + .ok_or_else(|| { + PartialVMError::new(StatusCode::VALUE_SERIALIZATION_ERROR) + .with_message("cannot serialize constant".to_string()) + })?; + } + } + } + } + Ok(()) +} + +/// Unpack input `std::string::String` to identifier. +fn unpack_string_to_identifier(value: Value) -> PartialVMResult { + let mut fields = value.value_as::()?.unpack()?; // std::string::String; + let val = fields.next().ok_or_else(|| { + PartialVMError::new(StatusCode::TYPE_RESOLUTION_FAILURE) + .with_message("There must have only one field".to_owned()) + })?; + let bytes = val.value_as::>()?; + let ident = Identifier::from_utf8(bytes).map_err(|e| { + PartialVMError::new(StatusCode::TYPE_RESOLUTION_FAILURE).with_message(e.to_string()) + })?; + Ok(ident) +} /*************************************************************************************************** * module * @@ -395,7 +617,10 @@ pub struct GasParameters { pub sort_and_verify_modules_inner: VerifyModulesGasParameters, pub request_init_functions: RequestInitFunctionsGasParameters, pub check_compatibililty_inner: CheckCompatibilityInnerGasParameters, - pub remap_module_addresses_inner: RemapAddressesGasParameters, + pub replace_address_identifiers: ReplaceAddressIdentifierGasParameters, + pub replace_addresses_constant: ReplaceAddressConstantGasParameters, + pub replace_identifiers: ReplaceIdentifierGasParameters, + pub replace_bytes_constant: ReplaceBytesConstantfierGasParameters, } impl GasParameters { @@ -417,7 +642,19 @@ impl GasParameters { base: 0.into(), per_byte: 0.into(), }, - remap_module_addresses_inner: RemapAddressesGasParameters { + replace_address_identifiers: ReplaceAddressIdentifierGasParameters { + base: 0.into(), + per_byte: 0.into(), + }, + replace_addresses_constant: ReplaceAddressConstantGasParameters { + base: 0.into(), + per_byte: 0.into(), + }, + replace_identifiers: ReplaceIdentifierGasParameters { + base: 0.into(), + per_byte: 0.into(), + }, + replace_bytes_constant: ReplaceBytesConstantfierGasParameters { base: 0.into(), per_byte: 0.into(), }, @@ -450,12 +687,27 @@ pub fn make_all(gas_params: GasParameters) -> impl Iterator