diff --git a/contracts/benchmarks/str-repeat/Cargo.toml b/contracts/benchmarks/str-repeat/Cargo.toml index 116327da15..867b7a97fa 100644 --- a/contracts/benchmarks/str-repeat/Cargo.toml +++ b/contracts/benchmarks/str-repeat/Cargo.toml @@ -8,6 +8,9 @@ publish = false [lib] path = "src/str_repeat.rs" +[features] +managed-buffer-builder-cached = ["multiversx-sc/managed-buffer-builder-cached"] + [dependencies.multiversx-sc] version = "0.47.8" path = "../../../framework/base" diff --git a/contracts/benchmarks/str-repeat/sc-config.toml b/contracts/benchmarks/str-repeat/sc-config.toml index a850f32470..964b5500c8 100644 --- a/contracts/benchmarks/str-repeat/sc-config.toml +++ b/contracts/benchmarks/str-repeat/sc-config.toml @@ -1,6 +1,16 @@ [settings] main = "str-repeat" -# the only purpose of this config is to specify the allocator [contracts.str-repeat] allocator = "leaking" + +[contracts.str-repeat-mb-builder-basic] +add-unlabelled = false +add-labels = ["mb-builder"] +add-endpoints = ["init"] + +[contracts.str-repeat-mb-builder-cached] +add-unlabelled = false +add-labels = ["mb-builder"] +add-endpoints = ["init"] +features = ["managed-buffer-builder-cached"] diff --git a/contracts/benchmarks/str-repeat/scenarios/mb_builder_basic.scen.json b/contracts/benchmarks/str-repeat/scenarios/mb_builder_basic.scen.json new file mode 100644 index 0000000000..81086d866e --- /dev/null +++ b/contracts/benchmarks/str-repeat/scenarios/mb_builder_basic.scen.json @@ -0,0 +1,80 @@ +{ + "name": "str-repeat", + "steps": [ + { + "step": "setState", + "accounts": { + "address:owner": { + "nonce": "0", + "balance": "0" + }, + "sc:contract": { + "code": "mxsc:../output/str-repeat-mb-builder-basic.mxsc.json" + } + } + }, + { + "step": "scCall", + "id": "benchmark-mb-builder-basic-10", + "comment": "code is smaller, so basic wins here", + "tx": { + "from": "address:owner", + "to": "sc:contract", + "function": "mb_builder_benchmark", + "arguments": [ + "0x01020304", + "10" + ], + "gasLimit": "10,000,000", + "gasPrice": "0" + }, + "expect": { + "out": "*", + "status": "", + "gas": "8855882" + } + }, + { + "step": "scCall", + "id": "benchmark-mb-builder-basic-tipping-point", + "comment": "the caching optimization starts to compensate the larger code size", + "tx": { + "from": "address:owner", + "to": "sc:contract", + "function": "mb_builder_benchmark", + "arguments": [ + "0x01020304", + "22" + ], + "gasLimit": "10,000,000", + "gasPrice": "0" + }, + "expect": { + "out": "*", + "status": "", + "gas": "8778242" + } + }, + { + "step": "scCall", + "id": "benchmark-mb-builder-basic", + "comment": "for many repeats, the cached version wins", + "tx": { + "from": "address:owner", + "to": "sc:contract", + "function": "mb_builder_benchmark", + "arguments": [ + "0x01020304", + "10000" + ], + "gasLimit": "10,000,000,000", + "gasPrice": "0" + }, + "expect": { + "out": "*", + "status": "", + "gas": "9934220582" + } + } + ] +} diff --git a/contracts/benchmarks/str-repeat/scenarios/mb_builder_cached.scen.json b/contracts/benchmarks/str-repeat/scenarios/mb_builder_cached.scen.json new file mode 100644 index 0000000000..9261fabd37 --- /dev/null +++ b/contracts/benchmarks/str-repeat/scenarios/mb_builder_cached.scen.json @@ -0,0 +1,80 @@ +{ + "name": "str-repeat", + "steps": [ + { + "step": "setState", + "accounts": { + "address:owner": { + "nonce": "0", + "balance": "0" + }, + "sc:contract": { + "code": "mxsc:../output/str-repeat-mb-builder-cached.mxsc.json" + } + } + }, + { + "step": "scCall", + "id": "benchmark-mb-builder-cached-10", + "comment": "code is smaller, so basic wins here", + "tx": { + "from": "address:owner", + "to": "sc:contract", + "function": "mb_builder_benchmark", + "arguments": [ + "0x01020304", + "10" + ], + "gasLimit": "10,000,000", + "gasPrice": "0" + }, + "expect": { + "out": "*", + "status": "", + "gas": "8834532" + } + }, + { + "step": "scCall", + "id": "benchmark-mb-builder-cached-tipping-point", + "comment": "the caching optimization starts to compensate the larger code size", + "tx": { + "from": "address:owner", + "to": "sc:contract", + "function": "mb_builder_benchmark", + "arguments": [ + "0x01020304", + "22" + ], + "gasLimit": "10,000,000", + "gasPrice": "0" + }, + "expect": { + "out": "*", + "status": "", + "gas": "8779332" + } + }, + { + "step": "scCall", + "id": "benchmark-mb-builder-cached", + "comment": "for many repeats, the cached version wins", + "tx": { + "from": "address:owner", + "to": "sc:contract", + "function": "mb_builder_benchmark", + "arguments": [ + "0x01020304", + "10000" + ], + "gasLimit": "10,000,000,000", + "gasPrice": "0" + }, + "expect": { + "out": "*", + "status": "", + "gas": "9938367902" + } + } + ] +} diff --git a/contracts/benchmarks/str-repeat/src/str_repeat.rs b/contracts/benchmarks/str-repeat/src/str_repeat.rs index 0cd6fb0325..81ad7fa874 100644 --- a/contracts/benchmarks/str-repeat/src/str_repeat.rs +++ b/contracts/benchmarks/str-repeat/src/str_repeat.rs @@ -25,4 +25,15 @@ pub trait StrRepeat { #[view(getByteArray)] #[storage_mapper("byteArray")] fn byte_array(&self) -> SingleValueMapper>; + + #[view] + #[label("mb-builder")] + fn mb_builder_benchmark(&self, payload: u32, num_repeats: usize) -> ManagedBuffer { + let mut builder = ManagedBufferBuilder::default(); + let payload_bytes = payload.to_be_bytes(); + for _ in 0..num_repeats { + builder.append_bytes(&payload_bytes); + } + builder.into_managed_buffer() + } } diff --git a/contracts/benchmarks/str-repeat/tests/scenario_go_test.rs b/contracts/benchmarks/str-repeat/tests/scenario_go_test.rs index e1be038d02..325d9ac866 100644 --- a/contracts/benchmarks/str-repeat/tests/scenario_go_test.rs +++ b/contracts/benchmarks/str-repeat/tests/scenario_go_test.rs @@ -4,6 +4,18 @@ fn world() -> ScenarioWorld { ScenarioWorld::vm_go() } +#[test] +#[ignore = "gas benchmark, too brittle to include permanently"] +fn mb_builder_basic_go() { + world().run("scenarios/mb_builder_basic.scen.json"); +} + +#[test] +#[ignore = "gas benchmark, too brittle to include permanently"] +fn mb_builder_cached_go() { + world().run("scenarios/mb_builder_cached.scen.json"); +} + #[test] fn str_repeat_go() { world().run("scenarios/str_repeat.scen.json"); diff --git a/contracts/benchmarks/str-repeat/tests/scenario_rs_test.rs b/contracts/benchmarks/str-repeat/tests/scenario_rs_test.rs index 42f585f314..4fcef41480 100644 --- a/contracts/benchmarks/str-repeat/tests/scenario_rs_test.rs +++ b/contracts/benchmarks/str-repeat/tests/scenario_rs_test.rs @@ -6,9 +6,27 @@ fn world() -> ScenarioWorld { "mxsc:output/str-repeat.mxsc.json", str_repeat::ContractBuilder, ); + blockchain.register_contract( + "mxsc:output/str-repeat-mb-builder-basic.mxsc.json", + str_repeat::ContractBuilder, + ); + blockchain.register_contract( + "mxsc:output/str-repeat-mb-builder-cached.mxsc.json", + str_repeat::ContractBuilder, + ); blockchain } +#[test] +fn mb_builder_basic_rs() { + world().run("scenarios/mb_builder_basic.scen.json"); +} + +#[test] +fn mb_builder_cached_rs() { + world().run("scenarios/mb_builder_cached.scen.json"); +} + #[test] fn str_repeat_rs() { world().run("scenarios/str_repeat.scen.json"); diff --git a/contracts/benchmarks/str-repeat/wasm-str-repeat-mb-builder-basic/Cargo.lock b/contracts/benchmarks/str-repeat/wasm-str-repeat-mb-builder-basic/Cargo.lock new file mode 100644 index 0000000000..7f11ee332c --- /dev/null +++ b/contracts/benchmarks/str-repeat/wasm-str-repeat-mb-builder-basic/Cargo.lock @@ -0,0 +1,170 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + +[[package]] +name = "autocfg" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" + +[[package]] +name = "bitflags" +version = "2.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" + +[[package]] +name = "endian-type" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hex-literal" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" + +[[package]] +name = "multiversx-sc" +version = "0.47.8" +dependencies = [ + "bitflags", + "hex-literal", + "multiversx-sc-codec", + "multiversx-sc-derive", + "num-traits", +] + +[[package]] +name = "multiversx-sc-codec" +version = "0.18.6" +dependencies = [ + "arrayvec", + "multiversx-sc-codec-derive", +] + +[[package]] +name = "multiversx-sc-codec-derive" +version = "0.18.6" +dependencies = [ + "hex", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "multiversx-sc-derive" +version = "0.47.8" +dependencies = [ + "hex", + "proc-macro2", + "quote", + "radix_trie", + "syn", +] + +[[package]] +name = "multiversx-sc-wasm-adapter" +version = "0.47.8" +dependencies = [ + "multiversx-sc", +] + +[[package]] +name = "nibble_vec" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43" +dependencies = [ + "smallvec", +] + +[[package]] +name = "num-traits" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +dependencies = [ + "autocfg", +] + +[[package]] +name = "proc-macro2" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radix_trie" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd" +dependencies = [ + "endian-type", + "nibble_vec", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "str-repeat" +version = "0.0.0" +dependencies = [ + "multiversx-sc", +] + +[[package]] +name = "str-repeat-mb-builder-basic-wasm" +version = "0.0.0" +dependencies = [ + "multiversx-sc-wasm-adapter", + "str-repeat", +] + +[[package]] +name = "syn" +version = "2.0.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" diff --git a/contracts/benchmarks/str-repeat/wasm-str-repeat-mb-builder-basic/Cargo.toml b/contracts/benchmarks/str-repeat/wasm-str-repeat-mb-builder-basic/Cargo.toml new file mode 100644 index 0000000000..2e124ace9c --- /dev/null +++ b/contracts/benchmarks/str-repeat/wasm-str-repeat-mb-builder-basic/Cargo.toml @@ -0,0 +1,32 @@ +# Code generated by the multiversx-sc build system. DO NOT EDIT. + +# ########################################## +# ############## AUTO-GENERATED ############# +# ########################################## + +[package] +name = "str-repeat-mb-builder-basic-wasm" +version = "0.0.0" +edition = "2021" +publish = false + +[lib] +crate-type = ["cdylib"] + +[profile.release] +codegen-units = 1 +opt-level = "z" +lto = true +debug = false +panic = "abort" +overflow-checks = false + +[dependencies.str-repeat] +path = ".." + +[dependencies.multiversx-sc-wasm-adapter] +version = "0.47.8" +path = "../../../../framework/wasm-adapter" + +[workspace] +members = ["."] diff --git a/contracts/benchmarks/str-repeat/wasm-str-repeat-mb-builder-basic/src/lib.rs b/contracts/benchmarks/str-repeat/wasm-str-repeat-mb-builder-basic/src/lib.rs new file mode 100644 index 0000000000..868b673634 --- /dev/null +++ b/contracts/benchmarks/str-repeat/wasm-str-repeat-mb-builder-basic/src/lib.rs @@ -0,0 +1,27 @@ +// Code generated by the multiversx-sc build system. DO NOT EDIT. + +//////////////////////////////////////////////////// +////////////////// AUTO-GENERATED ////////////////// +//////////////////////////////////////////////////// + +// Init: 1 +// Endpoints: 1 +// Async Callback (empty): 1 +// Total number of exported functions: 3 + +#![no_std] +#![allow(internal_features)] +#![feature(lang_items)] + +multiversx_sc_wasm_adapter::allocator!(); +multiversx_sc_wasm_adapter::panic_handler!(); + +multiversx_sc_wasm_adapter::endpoints! { + str_repeat + ( + init => init + mb_builder_benchmark => mb_builder_benchmark + ) +} + +multiversx_sc_wasm_adapter::async_callback_empty! {} diff --git a/contracts/benchmarks/str-repeat/wasm-str-repeat-mb-builder-cached/Cargo.lock b/contracts/benchmarks/str-repeat/wasm-str-repeat-mb-builder-cached/Cargo.lock new file mode 100644 index 0000000000..a866ae3bba --- /dev/null +++ b/contracts/benchmarks/str-repeat/wasm-str-repeat-mb-builder-cached/Cargo.lock @@ -0,0 +1,170 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + +[[package]] +name = "autocfg" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" + +[[package]] +name = "bitflags" +version = "2.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" + +[[package]] +name = "endian-type" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hex-literal" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" + +[[package]] +name = "multiversx-sc" +version = "0.47.8" +dependencies = [ + "bitflags", + "hex-literal", + "multiversx-sc-codec", + "multiversx-sc-derive", + "num-traits", +] + +[[package]] +name = "multiversx-sc-codec" +version = "0.18.6" +dependencies = [ + "arrayvec", + "multiversx-sc-codec-derive", +] + +[[package]] +name = "multiversx-sc-codec-derive" +version = "0.18.6" +dependencies = [ + "hex", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "multiversx-sc-derive" +version = "0.47.8" +dependencies = [ + "hex", + "proc-macro2", + "quote", + "radix_trie", + "syn", +] + +[[package]] +name = "multiversx-sc-wasm-adapter" +version = "0.47.8" +dependencies = [ + "multiversx-sc", +] + +[[package]] +name = "nibble_vec" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43" +dependencies = [ + "smallvec", +] + +[[package]] +name = "num-traits" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +dependencies = [ + "autocfg", +] + +[[package]] +name = "proc-macro2" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radix_trie" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd" +dependencies = [ + "endian-type", + "nibble_vec", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "str-repeat" +version = "0.0.0" +dependencies = [ + "multiversx-sc", +] + +[[package]] +name = "str-repeat-mb-builder-cached-wasm" +version = "0.0.0" +dependencies = [ + "multiversx-sc-wasm-adapter", + "str-repeat", +] + +[[package]] +name = "syn" +version = "2.0.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" diff --git a/contracts/benchmarks/str-repeat/wasm-str-repeat-mb-builder-cached/Cargo.toml b/contracts/benchmarks/str-repeat/wasm-str-repeat-mb-builder-cached/Cargo.toml new file mode 100644 index 0000000000..0ab57dce26 --- /dev/null +++ b/contracts/benchmarks/str-repeat/wasm-str-repeat-mb-builder-cached/Cargo.toml @@ -0,0 +1,33 @@ +# Code generated by the multiversx-sc build system. DO NOT EDIT. + +# ########################################## +# ############## AUTO-GENERATED ############# +# ########################################## + +[package] +name = "str-repeat-mb-builder-cached-wasm" +version = "0.0.0" +edition = "2021" +publish = false + +[lib] +crate-type = ["cdylib"] + +[profile.release] +codegen-units = 1 +opt-level = "z" +lto = true +debug = false +panic = "abort" +overflow-checks = false + +[dependencies.str-repeat] +path = ".." +features = ["managed-buffer-builder-cached"] + +[dependencies.multiversx-sc-wasm-adapter] +version = "0.47.8" +path = "../../../../framework/wasm-adapter" + +[workspace] +members = ["."] diff --git a/contracts/benchmarks/str-repeat/wasm-str-repeat-mb-builder-cached/src/lib.rs b/contracts/benchmarks/str-repeat/wasm-str-repeat-mb-builder-cached/src/lib.rs new file mode 100644 index 0000000000..868b673634 --- /dev/null +++ b/contracts/benchmarks/str-repeat/wasm-str-repeat-mb-builder-cached/src/lib.rs @@ -0,0 +1,27 @@ +// Code generated by the multiversx-sc build system. DO NOT EDIT. + +//////////////////////////////////////////////////// +////////////////// AUTO-GENERATED ////////////////// +//////////////////////////////////////////////////// + +// Init: 1 +// Endpoints: 1 +// Async Callback (empty): 1 +// Total number of exported functions: 3 + +#![no_std] +#![allow(internal_features)] +#![feature(lang_items)] + +multiversx_sc_wasm_adapter::allocator!(); +multiversx_sc_wasm_adapter::panic_handler!(); + +multiversx_sc_wasm_adapter::endpoints! { + str_repeat + ( + init => init + mb_builder_benchmark => mb_builder_benchmark + ) +} + +multiversx_sc_wasm_adapter::async_callback_empty! {} diff --git a/contracts/examples/ping-pong-egld/scenarios/ping-pong-call-pong-all-interrupted-2.scen.json b/contracts/examples/ping-pong-egld/scenarios/ping-pong-call-pong-all-interrupted-2.scen.json index 2b597650b6..9253cff4f8 100644 --- a/contracts/examples/ping-pong-egld/scenarios/ping-pong-call-pong-all-interrupted-2.scen.json +++ b/contracts/examples/ping-pong-egld/scenarios/ping-pong-call-pong-all-interrupted-2.scen.json @@ -21,7 +21,7 @@ "to": "sc:ping-pong", "function": "pongAll", "arguments": [], - "gasLimit": "6,100,000", + "gasLimit": "3,500,000", "gasPrice": "0" }, "expect": { diff --git a/contracts/feature-tests/use-module/scenarios/use_module_ongoing_operation_example.scen.json b/contracts/feature-tests/use-module/scenarios/use_module_ongoing_operation_example.scen.json index 45a659c44e..3e43e9270a 100644 --- a/contracts/feature-tests/use-module/scenarios/use_module_ongoing_operation_example.scen.json +++ b/contracts/feature-tests/use-module/scenarios/use_module_ongoing_operation_example.scen.json @@ -52,7 +52,7 @@ "to": "sc:use_module", "function": "countTo100", "arguments": [], - "gasLimit": "5,350,000", + "gasLimit": "4,500,000", "gasPrice": "0" }, "expect": { diff --git a/framework/base/Cargo.toml b/framework/base/Cargo.toml index 9edc7414d7..961a0c59d5 100644 --- a/framework/base/Cargo.toml +++ b/framework/base/Cargo.toml @@ -19,6 +19,7 @@ all-features = true [features] num-bigint = ["multiversx-sc-codec/num-bigint"] alloc = ["multiversx-sc-codec/alloc"] +managed-buffer-builder-cached = [] esdt-token-payment-legacy-decode = [] [dependencies] diff --git a/framework/base/src/io/finish.rs b/framework/base/src/io/finish.rs index f44cc02c5e..49ec4c8560 100644 --- a/framework/base/src/io/finish.rs +++ b/framework/base/src/io/finish.rs @@ -8,8 +8,8 @@ use crate::{ contract_base::ExitCodecErrorHandler, err_msg, types::{ - BigInt, BigUint, ManagedBuffer, ManagedBufferCachedBuilder, ManagedSCError, ManagedType, - SCError, StaticSCError, + BigInt, BigUint, ManagedBuffer, ManagedBufferBuilder, ManagedSCError, ManagedType, SCError, + StaticSCError, }, }; @@ -47,7 +47,7 @@ impl TopEncodeOutput for ApiOutputAdapter where FA: ManagedTypeApi + EndpointFinishApi, { - type NestedBuffer = ManagedBufferCachedBuilder; + type NestedBuffer = ManagedBufferBuilder; fn set_slice_u8(self, bytes: &[u8]) { FA::finish_api_impl().finish_slice_u8(bytes); @@ -94,7 +94,7 @@ where } fn start_nested_encode(&self) -> Self::NestedBuffer { - ManagedBufferCachedBuilder::new_from_slice(&[]) + ManagedBufferBuilder::new_from_slice(&[]) } fn finalize_nested_encode(self, nb: Self::NestedBuffer) { diff --git a/framework/base/src/macros.rs b/framework/base/src/macros.rs index 1754688d03..86c963a057 100644 --- a/framework/base/src/macros.rs +++ b/framework/base/src/macros.rs @@ -63,7 +63,7 @@ macro_rules! require_old { macro_rules! sc_panic { ($msg:tt, $($arg:expr),+ $(,)?) => {{ let mut ___buffer___ = - multiversx_sc::types::ManagedBufferCachedBuilder::::new_from_slice(&[]); + multiversx_sc::types::ManagedBufferBuilder::::new_from_slice(&[]); multiversx_sc::derive::format_receiver_args!(___buffer___, $msg, $($arg),+); multiversx_sc::contract_base::ErrorHelper::::signal_error_with_message(___buffer___.into_managed_buffer()); }}; @@ -123,7 +123,7 @@ macro_rules! sc_print { macro_rules! sc_format { ($msg:tt, $($arg:expr),+ $(,)?) => {{ let mut ___buffer___ = - multiversx_sc::types::ManagedBufferCachedBuilder::::new_from_slice(&[]); + multiversx_sc::types::ManagedBufferBuilder::::new_from_slice(&[]); multiversx_sc::derive::format_receiver_args!(___buffer___, $msg, $($arg),+); ___buffer___.into_managed_buffer() }}; diff --git a/framework/base/src/storage/storage_set.rs b/framework/base/src/storage/storage_set.rs index e988b47351..1a407617f5 100644 --- a/framework/base/src/storage/storage_set.rs +++ b/framework/base/src/storage/storage_set.rs @@ -6,7 +6,7 @@ use crate::{ codec::*, contract_base::ExitCodecErrorHandler, err_msg, - types::{BigInt, BigUint, ManagedBuffer, ManagedBufferCachedBuilder, ManagedRef, ManagedType}, + types::{BigInt, BigUint, ManagedBuffer, ManagedBufferBuilder, ManagedRef, ManagedType}, }; use super::StorageKey; @@ -39,7 +39,7 @@ impl<'k, A> TopEncodeOutput for StorageSetOutput<'k, A> where A: StorageWriteApi + ManagedTypeApi + ErrorApi + 'static, { - type NestedBuffer = ManagedBufferCachedBuilder; + type NestedBuffer = ManagedBufferBuilder; fn set_slice_u8(self, bytes: &[u8]) { self.set_managed_buffer(&bytes.into()) @@ -71,7 +71,7 @@ where } fn start_nested_encode(&self) -> Self::NestedBuffer { - ManagedBufferCachedBuilder::new_from_slice(&[]) + ManagedBufferBuilder::new_from_slice(&[]) } fn finalize_nested_encode(self, nb: Self::NestedBuffer) { diff --git a/framework/base/src/types/interaction/contract_call_exec.rs b/framework/base/src/types/interaction/contract_call_exec.rs index 260897e1a6..fd9994f2b0 100644 --- a/framework/base/src/types/interaction/contract_call_exec.rs +++ b/framework/base/src/types/interaction/contract_call_exec.rs @@ -9,8 +9,7 @@ use crate::{ formatter::SCLowerHex, io::{ArgErrorHandler, ArgId, ManagedResultArgLoader}, types::{ - BigUint, EsdtTokenPayment, ManagedBuffer, ManagedBufferCachedBuilder, ManagedType, - ManagedVec, + BigUint, EsdtTokenPayment, ManagedBuffer, ManagedBufferBuilder, ManagedType, ManagedVec, }, }; @@ -56,7 +55,7 @@ where } pub fn to_call_data_string(&self) -> ManagedBuffer { - let mut result = ManagedBufferCachedBuilder::default(); + let mut result = ManagedBufferBuilder::default(); result.append_managed_buffer(&self.basic.function_call.function_name); for arg in self.basic.function_call.arg_buffer.raw_arg_iter() { result.append_bytes(b"@"); diff --git a/framework/base/src/types/interaction/function_call.rs b/framework/base/src/types/interaction/function_call.rs index a2140f14fa..94740624f5 100644 --- a/framework/base/src/types/interaction/function_call.rs +++ b/framework/base/src/types/interaction/function_call.rs @@ -11,7 +11,7 @@ use crate::{ }, formatter::SCLowerHex, types::{ - EsdtTokenPayment, ManagedAddress, ManagedBuffer, ManagedBufferCachedBuilder, ManagedVec, + EsdtTokenPayment, ManagedAddress, ManagedBuffer, ManagedBufferBuilder, ManagedVec, MultiValueEncoded, }, }; @@ -64,7 +64,7 @@ where } pub fn to_call_data_string(&self) -> ManagedBuffer { - let mut result = ManagedBufferCachedBuilder::default(); + let mut result = ManagedBufferBuilder::default(); result.append_managed_buffer(&self.function_name); for arg in self.arg_buffer.raw_arg_iter() { result.append_bytes(b"@"); diff --git a/framework/base/src/types/managed/wrapped/builder.rs b/framework/base/src/types/managed/wrapped/builder.rs new file mode 100644 index 0000000000..78f81f6e81 --- /dev/null +++ b/framework/base/src/types/managed/wrapped/builder.rs @@ -0,0 +1,18 @@ +mod managed_buffer_builder; +mod managed_buffer_builder_impl; +mod managed_buffer_builder_impl_basic; +mod managed_buffer_builder_impl_cached; + +pub use managed_buffer_builder::ManagedBufferBuilder; +pub use managed_buffer_builder_impl::ManagedBufferBuilderImpl; +pub use managed_buffer_builder_impl_basic::ManagedBufferBuilderImplBasic; +pub use managed_buffer_builder_impl_cached::ManagedBufferBuilderImplCached; + +#[deprecated(since = "0.48.0", note = "Renamed to ManagedBufferBuilder.")] +pub type ManagedBufferCachedBuilder = ManagedBufferBuilder; + +#[cfg(feature = "managed-buffer-builder-cached")] +pub type ManagedBufferImplDefault = ManagedBufferBuilderImplCached; + +#[cfg(not(feature = "managed-buffer-builder-cached"))] +pub type ManagedBufferImplDefault = ManagedBufferBuilderImplBasic; diff --git a/framework/base/src/types/managed/wrapped/managed_buffer_cached_builder.rs b/framework/base/src/types/managed/wrapped/builder/managed_buffer_builder.rs similarity index 63% rename from framework/base/src/types/managed/wrapped/managed_buffer_cached_builder.rs rename to framework/base/src/types/managed/wrapped/builder/managed_buffer_builder.rs index 85d6c5a264..b65aa8e0e0 100644 --- a/framework/base/src/types/managed/wrapped/managed_buffer_cached_builder.rs +++ b/framework/base/src/types/managed/wrapped/builder/managed_buffer_builder.rs @@ -1,3 +1,5 @@ +use core::marker::PhantomData; + use crate::codec::{EncodeError, EncodeErrorHandler, NestedEncodeOutput, TryStaticCast}; use crate::{ @@ -6,45 +8,41 @@ use crate::{ hex_util::{byte_to_binary_digits, byte_to_hex_digits}, FormatBuffer, FormatByteReceiver, SCBinary, SCCodec, SCDisplay, SCLowerHex, }, - types::{BigInt, BigUint, ManagedBuffer, StaticBufferRef}, + types::ManagedBuffer, }; +use super::{ManagedBufferBuilderImpl, ManagedBufferImplDefault}; + const HEX_CONVERSION_BUFFER_LEN: usize = 32; const BIN_CONVERSION_BUFFER_LEN: usize = 32; -pub struct ManagedBufferCachedBuilder +pub struct ManagedBufferBuilder> where M: ManagedTypeApi, + Impl: ManagedBufferBuilderImpl, { - managed_buffer: ManagedBuffer, - static_cache: Option>, + _phantom: PhantomData, + implementation: Impl, } -impl ManagedBufferCachedBuilder +impl ManagedBufferBuilder where M: ManagedTypeApi, + Impl: ManagedBufferBuilderImpl, { /// Creates instance as lazily as possible. /// If possible, the slice is loaded into the static buffer. /// If not, it is saved into the managed buffer so that the data is not lost. /// Use `flush_to_managed_buffer` after this to ensure that the managed buffer is populated. pub fn new_from_slice(slice: &[u8]) -> Self { - let static_cache = StaticBufferRef::try_new(slice); - if static_cache.is_some() { - ManagedBufferCachedBuilder { - managed_buffer: ManagedBuffer::new(), - static_cache, - } - } else { - ManagedBufferCachedBuilder { - managed_buffer: slice.into(), - static_cache: None, - } + ManagedBufferBuilder { + _phantom: PhantomData, + implementation: Impl::new_from_slice(slice), } } } -impl Default for ManagedBufferCachedBuilder +impl Default for ManagedBufferBuilder> where M: ManagedTypeApi, { @@ -54,48 +52,21 @@ where } } -impl ManagedBufferCachedBuilder +impl ManagedBufferBuilder where M: ManagedTypeApi, + Impl: ManagedBufferBuilderImpl, { - pub fn into_managed_buffer(mut self) -> ManagedBuffer { - self.flush_to_managed_buffer(); - self.managed_buffer - } - - fn flush_to_managed_buffer(&mut self) { - let old_static_cache = core::mem::take(&mut self.static_cache); - if let Some(static_cache) = &old_static_cache { - static_cache.with_buffer_contents(|bytes| { - self.managed_buffer.append_bytes(bytes); - }); - } + pub fn into_managed_buffer(self) -> ManagedBuffer { + self.implementation.into_managed_buffer() } pub fn append_bytes(&mut self, bytes: &[u8]) { - if let Some(static_cache) = &mut self.static_cache { - let success = static_cache.try_extend_from_slice(bytes); - if !success { - self.flush_to_managed_buffer(); - self.managed_buffer.append_bytes(bytes); - } - } else { - self.managed_buffer.append_bytes(bytes); - } + self.implementation.append_bytes(bytes); } pub fn append_managed_buffer(&mut self, item: &ManagedBuffer) { - if let Some(static_cache) = &mut self.static_cache { - let success = static_cache.try_extend_from_copy_bytes(item.len(), |dest_slice| { - let _ = item.load_slice(0, dest_slice); - }); - if !success { - self.flush_to_managed_buffer(); - self.managed_buffer.append(item); - } - } else { - self.managed_buffer.append(item); - } + self.implementation.append_managed_buffer(item); } /// Converts the input to hex and adds it to the current buffer. @@ -125,14 +96,18 @@ where } } -impl NestedEncodeOutput for ManagedBufferCachedBuilder { +impl NestedEncodeOutput for ManagedBufferBuilder +where + M: ManagedTypeApi, + Impl: ManagedBufferBuilderImpl, +{ fn write(&mut self, bytes: &[u8]) { self.append_bytes(bytes); } #[inline] fn supports_specialized_type() -> bool { - T::type_eq::>() || T::type_eq::>() || T::type_eq::>() + T::type_eq::>() } #[inline] @@ -156,9 +131,10 @@ impl NestedEncodeOutput for ManagedBufferCachedBuilder { } } -impl FormatByteReceiver for ManagedBufferCachedBuilder +impl FormatByteReceiver for ManagedBufferBuilder where M: ManagedTypeApi, + Impl: ManagedBufferBuilderImpl, { type Api = M; @@ -179,7 +155,10 @@ where } } -impl FormatBuffer for ManagedBufferCachedBuilder { +impl FormatBuffer for ManagedBufferBuilder> +where + M: ManagedTypeApi, +{ fn append_ascii(&mut self, ascii: &[u8]) { self.append_bytes(ascii) } diff --git a/framework/base/src/types/managed/wrapped/builder/managed_buffer_builder_impl.rs b/framework/base/src/types/managed/wrapped/builder/managed_buffer_builder_impl.rs new file mode 100644 index 0000000000..dfade9b6e3 --- /dev/null +++ b/framework/base/src/types/managed/wrapped/builder/managed_buffer_builder_impl.rs @@ -0,0 +1,14 @@ +use crate::{api::ManagedTypeApi, types::ManagedBuffer}; + +pub trait ManagedBufferBuilderImpl +where + M: ManagedTypeApi, +{ + fn new_from_slice(slice: &[u8]) -> Self; + + fn into_managed_buffer(self) -> ManagedBuffer; + + fn append_bytes(&mut self, bytes: &[u8]); + + fn append_managed_buffer(&mut self, item: &ManagedBuffer); +} diff --git a/framework/base/src/types/managed/wrapped/builder/managed_buffer_builder_impl_basic.rs b/framework/base/src/types/managed/wrapped/builder/managed_buffer_builder_impl_basic.rs new file mode 100644 index 0000000000..3c1e87c7fb --- /dev/null +++ b/framework/base/src/types/managed/wrapped/builder/managed_buffer_builder_impl_basic.rs @@ -0,0 +1,40 @@ +use crate::{api::ManagedTypeApi, types::ManagedBuffer}; + +use super::ManagedBufferBuilderImpl; + +/// Basic implementation of a ManagedBuffer builder, no caching. +/// +/// It is the ManagedBuffer itself, we just append to it each time. +pub struct ManagedBufferBuilderImplBasic +where + M: ManagedTypeApi, +{ + managed_buffer: ManagedBuffer, +} + +impl ManagedBufferBuilderImpl for ManagedBufferBuilderImplBasic +where + M: ManagedTypeApi, +{ + #[inline] + fn new_from_slice(slice: &[u8]) -> Self { + ManagedBufferBuilderImplBasic { + managed_buffer: slice.into(), + } + } + + #[inline] + fn into_managed_buffer(self) -> ManagedBuffer { + self.managed_buffer + } + + #[inline] + fn append_bytes(&mut self, bytes: &[u8]) { + self.managed_buffer.append_bytes(bytes); + } + + #[inline] + fn append_managed_buffer(&mut self, item: &ManagedBuffer) { + self.managed_buffer.append(item); + } +} diff --git a/framework/base/src/types/managed/wrapped/builder/managed_buffer_builder_impl_cached.rs b/framework/base/src/types/managed/wrapped/builder/managed_buffer_builder_impl_cached.rs new file mode 100644 index 0000000000..ead06e151c --- /dev/null +++ b/framework/base/src/types/managed/wrapped/builder/managed_buffer_builder_impl_cached.rs @@ -0,0 +1,84 @@ +use crate::{ + api::ManagedTypeApi, + types::{ManagedBuffer, StaticBufferRef}, +}; + +use super::ManagedBufferBuilderImpl; + +/// A ManagedBuffer builder implementation that caches data to the static cache locally in the contract. +pub struct ManagedBufferBuilderImplCached +where + M: ManagedTypeApi, +{ + managed_buffer: ManagedBuffer, + static_cache: Option>, +} + +impl ManagedBufferBuilderImplCached +where + M: ManagedTypeApi, +{ + fn flush_to_managed_buffer(&mut self) { + let old_static_cache = core::mem::take(&mut self.static_cache); + if let Some(static_cache) = &old_static_cache { + static_cache.with_buffer_contents(|bytes| { + self.managed_buffer.append_bytes(bytes); + }); + } + } +} + +impl ManagedBufferBuilderImpl for ManagedBufferBuilderImplCached +where + M: ManagedTypeApi, +{ + /// Creates instance as lazily as possible. + /// If possible, the slice is loaded into the static buffer. + /// If not, it is saved into the managed buffer so that the data is not lost. + /// Use `flush_to_managed_buffer` after this to ensure that the managed buffer is populated. + fn new_from_slice(slice: &[u8]) -> Self { + let static_cache = StaticBufferRef::try_new(slice); + if static_cache.is_some() { + ManagedBufferBuilderImplCached { + managed_buffer: ManagedBuffer::new(), + static_cache, + } + } else { + ManagedBufferBuilderImplCached { + managed_buffer: slice.into(), + static_cache: None, + } + } + } + + fn into_managed_buffer(mut self) -> ManagedBuffer { + self.flush_to_managed_buffer(); + self.managed_buffer + } + + fn append_bytes(&mut self, bytes: &[u8]) { + if let Some(static_cache) = &mut self.static_cache { + let success = static_cache.try_extend_from_slice(bytes); + if !success { + self.flush_to_managed_buffer(); + self.managed_buffer.append_bytes(bytes); + } + } else { + self.managed_buffer.append_bytes(bytes); + } + } + + fn append_managed_buffer(&mut self, item: &ManagedBuffer) { + if let Some(static_cache) = &mut self.static_cache { + let success = static_cache.try_extend_from_copy_bytes(item.len(), |dest_slice| { + let _ = item.load_slice(0, dest_slice); + }); + if !success { + self.flush_to_managed_buffer(); + self.managed_buffer.append(item); + } + } else { + self.managed_buffer.append(item); + } + } +} diff --git a/framework/base/src/types/managed/wrapped/mod.rs b/framework/base/src/types/managed/wrapped/mod.rs index 7ead89e3a0..e883e67e93 100644 --- a/framework/base/src/types/managed/wrapped/mod.rs +++ b/framework/base/src/types/managed/wrapped/mod.rs @@ -1,3 +1,4 @@ +mod builder; mod egld_or_esdt_token_identifier; mod egld_or_esdt_token_payment; mod egld_or_multi_esdt_payment; @@ -5,7 +6,6 @@ mod encoded_managed_vec_item; mod esdt_token_data; mod esdt_token_payment; mod managed_address; -mod managed_buffer_cached_builder; mod managed_byte_array; mod managed_option; mod managed_ref; @@ -19,6 +19,7 @@ mod randomness_source; mod token_identifier; mod traits; +pub use builder::*; pub use egld_or_esdt_token_identifier::EgldOrEsdtTokenIdentifier; pub use egld_or_esdt_token_payment::EgldOrEsdtTokenPayment; pub use egld_or_multi_esdt_payment::EgldOrMultiEsdtPayment; @@ -26,7 +27,6 @@ pub(crate) use encoded_managed_vec_item::EncodedManagedVecItem; pub use esdt_token_data::EsdtTokenData; pub use esdt_token_payment::{EsdtTokenPayment, MultiEsdtPayment}; pub use managed_address::ManagedAddress; -pub use managed_buffer_cached_builder::ManagedBufferCachedBuilder; pub(crate) use managed_byte_array::ManagedBufferSizeContext; pub use managed_byte_array::ManagedByteArray; pub use managed_option::ManagedOption; diff --git a/framework/derive/src/preprocessing/substitution_list.rs b/framework/derive/src/preprocessing/substitution_list.rs index 8084651a49..62fdb11823 100644 --- a/framework/derive/src/preprocessing/substitution_list.rs +++ b/framework/derive/src/preprocessing/substitution_list.rs @@ -71,7 +71,7 @@ fn add_managed_types(substitutions: &mut SubstitutionsMap) { add_managed_type(substitutions, "e!(EsdtTokenData)); add_managed_type(substitutions, "e!(EsdtTokenPayment)); add_managed_type(substitutions, "e!(ManagedAddress)); - add_managed_type(substitutions, "e!(ManagedBufferCachedBuilder)); + add_managed_type(substitutions, "e!(ManagedBufferBuilder)); add_managed_type_with_generics(substitutions, "e!(ManagedByteArray)); add_managed_type_with_generics(substitutions, "e!(ManagedOption)); add_managed_type_with_generics(substitutions, "e!(ManagedRef)); diff --git a/framework/scenario/src/api/local_api_vh/print_api_vh.rs b/framework/scenario/src/api/local_api_vh/print_api_vh.rs index 6774e9546d..58cdb7fd16 100644 --- a/framework/scenario/src/api/local_api_vh/print_api_vh.rs +++ b/framework/scenario/src/api/local_api_vh/print_api_vh.rs @@ -2,7 +2,7 @@ use std::cell::RefCell; use multiversx_sc::{ api::{PrintApi, PrintApiImpl}, - types::ManagedBufferCachedBuilder, + types::ManagedBufferBuilder, }; use crate::api::{VMHooksApi, VMHooksApiBackend}; @@ -34,7 +34,7 @@ impl PrintApi for VMHooksApi { } impl PrintApiImpl for VMHooksApi { - type Buffer = ManagedBufferCachedBuilder; + type Buffer = ManagedBufferBuilder; fn print_buffer(&self, buffer: Self::Buffer) { let bytes = buffer.into_managed_buffer().to_boxed_bytes();