diff --git a/frame/contracts/benchmarks/README.md b/frame/contracts/benchmarks/README.md new file mode 100644 index 0000000000000..a4b15bd840db4 --- /dev/null +++ b/frame/contracts/benchmarks/README.md @@ -0,0 +1,9 @@ +# Benchmarks + +This directory contains real world (ink!, solang) contracts which are used in macro benchmarks. +Those benchmarks are not used to determine weights but rather to compare different contract +languages and execution engines with larger wasm modules. + +Files in this directory are used by `#[extra]` benchmarks in `src/benchmarking`. The json +files are for informational purposes only and are not consumed by the benchmarks. + diff --git a/frame/contracts/benchmarks/ink_erc20.json b/frame/contracts/benchmarks/ink_erc20.json new file mode 100644 index 0000000000000..390dd9b06cd4c --- /dev/null +++ b/frame/contracts/benchmarks/ink_erc20.json @@ -0,0 +1,819 @@ +{ + "metadataVersion": "0.1.0", + "source": { + "hash": "0x6be8492017fe96b7a92bb39b4ede04b96effb8fcaf9237bfdccef7d9e732c760", + "language": "ink! 3.0.0-rc4", + "compiler": "rustc 1.56.0-nightly" + }, + "contract": { + "name": "erc20", + "version": "3.0.0-rc4", + "authors": [ + "Parity Technologies " + ] + }, + "spec": { + "constructors": [ + { + "args": [ + { + "name": "initial_supply", + "type": { + "displayName": [ + "Balance" + ], + "type": 1 + } + } + ], + "docs": [ + "Creates a new ERC-20 contract with the specified initial supply." + ], + "name": [ + "new" + ], + "selector": "0x9bae9d5e" + } + ], + "docs": [], + "events": [ + { + "args": [ + { + "docs": [], + "indexed": true, + "name": "from", + "type": { + "displayName": [ + "Option" + ], + "type": 15 + } + }, + { + "docs": [], + "indexed": true, + "name": "to", + "type": { + "displayName": [ + "Option" + ], + "type": 15 + } + }, + { + "docs": [], + "indexed": false, + "name": "value", + "type": { + "displayName": [ + "Balance" + ], + "type": 1 + } + } + ], + "docs": [ + " Event emitted when a token transfer occurs." + ], + "name": "Transfer" + }, + { + "args": [ + { + "docs": [], + "indexed": true, + "name": "owner", + "type": { + "displayName": [ + "AccountId" + ], + "type": 5 + } + }, + { + "docs": [], + "indexed": true, + "name": "spender", + "type": { + "displayName": [ + "AccountId" + ], + "type": 5 + } + }, + { + "docs": [], + "indexed": false, + "name": "value", + "type": { + "displayName": [ + "Balance" + ], + "type": 1 + } + } + ], + "docs": [ + " Event emitted when an approval occurs that `spender` is allowed to withdraw", + " up to the amount of `value` tokens from `owner`." + ], + "name": "Approval" + } + ], + "messages": [ + { + "args": [], + "docs": [ + " Returns the total token supply." + ], + "mutates": false, + "name": [ + "total_supply" + ], + "payable": false, + "returnType": { + "displayName": [ + "Balance" + ], + "type": 1 + }, + "selector": "0xdb6375a8" + }, + { + "args": [ + { + "name": "owner", + "type": { + "displayName": [ + "AccountId" + ], + "type": 5 + } + } + ], + "docs": [ + " Returns the account balance for the specified `owner`.", + "", + " Returns `0` if the account is non-existent." + ], + "mutates": false, + "name": [ + "balance_of" + ], + "payable": false, + "returnType": { + "displayName": [ + "Balance" + ], + "type": 1 + }, + "selector": "0x0f755a56" + }, + { + "args": [ + { + "name": "owner", + "type": { + "displayName": [ + "AccountId" + ], + "type": 5 + } + }, + { + "name": "spender", + "type": { + "displayName": [ + "AccountId" + ], + "type": 5 + } + } + ], + "docs": [ + " Returns the amount which `spender` is still allowed to withdraw from `owner`.", + "", + " Returns `0` if no allowance has been set `0`." + ], + "mutates": false, + "name": [ + "allowance" + ], + "payable": false, + "returnType": { + "displayName": [ + "Balance" + ], + "type": 1 + }, + "selector": "0x6a00165e" + }, + { + "args": [ + { + "name": "to", + "type": { + "displayName": [ + "AccountId" + ], + "type": 5 + } + }, + { + "name": "value", + "type": { + "displayName": [ + "Balance" + ], + "type": 1 + } + } + ], + "docs": [ + " Transfers `value` amount of tokens from the caller's account to account `to`.", + "", + " On success a `Transfer` event is emitted.", + "", + " # Errors", + "", + " Returns `InsufficientBalance` error if there are not enough tokens on", + " the caller's account balance." + ], + "mutates": true, + "name": [ + "transfer" + ], + "payable": false, + "returnType": { + "displayName": [ + "Result" + ], + "type": 12 + }, + "selector": "0x84a15da1" + }, + { + "args": [ + { + "name": "spender", + "type": { + "displayName": [ + "AccountId" + ], + "type": 5 + } + }, + { + "name": "value", + "type": { + "displayName": [ + "Balance" + ], + "type": 1 + } + } + ], + "docs": [ + " Allows `spender` to withdraw from the caller's account multiple times, up to", + " the `value` amount.", + "", + " If this function is called again it overwrites the current allowance with `value`.", + "", + " An `Approval` event is emitted." + ], + "mutates": true, + "name": [ + "approve" + ], + "payable": false, + "returnType": { + "displayName": [ + "Result" + ], + "type": 12 + }, + "selector": "0x681266a0" + }, + { + "args": [ + { + "name": "from", + "type": { + "displayName": [ + "AccountId" + ], + "type": 5 + } + }, + { + "name": "to", + "type": { + "displayName": [ + "AccountId" + ], + "type": 5 + } + }, + { + "name": "value", + "type": { + "displayName": [ + "Balance" + ], + "type": 1 + } + } + ], + "docs": [ + " Transfers `value` tokens on the behalf of `from` to the account `to`.", + "", + " This can be used to allow a contract to transfer tokens on ones behalf and/or", + " to charge fees in sub-currencies, for example.", + "", + " On success a `Transfer` event is emitted.", + "", + " # Errors", + "", + " Returns `InsufficientAllowance` error if there are not enough tokens allowed", + " for the caller to withdraw from `from`.", + "", + " Returns `InsufficientBalance` error if there are not enough tokens on", + " the account balance of `from`." + ], + "mutates": true, + "name": [ + "transfer_from" + ], + "payable": false, + "returnType": { + "displayName": [ + "Result" + ], + "type": 12 + }, + "selector": "0x0b396f18" + } + ] + }, + "storage": { + "struct": { + "fields": [ + { + "layout": { + "cell": { + "key": "0x0000000000000000000000000000000000000000000000000000000000000000", + "ty": 1 + } + }, + "name": "total_supply" + }, + { + "layout": { + "struct": { + "fields": [ + { + "layout": { + "struct": { + "fields": [ + { + "layout": { + "cell": { + "key": "0x0100000000000000000000000000000000000000000000000000000000000000", + "ty": 2 + } + }, + "name": "header" + }, + { + "layout": { + "struct": { + "fields": [ + { + "layout": { + "cell": { + "key": "0x0200000000000000000000000000000000000000000000000000000000000000", + "ty": 3 + } + }, + "name": "len" + }, + { + "layout": { + "array": { + "cellsPerElem": 1, + "layout": { + "cell": { + "key": "0x0200000001000000000000000000000000000000000000000000000000000000", + "ty": 4 + } + }, + "len": 4294967295, + "offset": "0x0300000000000000000000000000000000000000000000000000000000000000" + } + }, + "name": "elems" + } + ] + } + }, + "name": "entries" + } + ] + } + }, + "name": "keys" + }, + { + "layout": { + "hash": { + "layout": { + "cell": { + "key": "0x0300000001000000000000000000000000000000000000000000000000000000", + "ty": 9 + } + }, + "offset": "0x0200000001000000000000000000000000000000000000000000000000000000", + "strategy": { + "hasher": "Blake2x256", + "postfix": "", + "prefix": "0x696e6b20686173686d6170" + } + } + }, + "name": "values" + } + ] + } + }, + "name": "balances" + }, + { + "layout": { + "struct": { + "fields": [ + { + "layout": { + "struct": { + "fields": [ + { + "layout": { + "cell": { + "key": "0x0300000001000000000000000000000000000000000000000000000000000000", + "ty": 2 + } + }, + "name": "header" + }, + { + "layout": { + "struct": { + "fields": [ + { + "layout": { + "cell": { + "key": "0x0400000001000000000000000000000000000000000000000000000000000000", + "ty": 3 + } + }, + "name": "len" + }, + { + "layout": { + "array": { + "cellsPerElem": 1, + "layout": { + "cell": { + "key": "0x0400000002000000000000000000000000000000000000000000000000000000", + "ty": 10 + } + }, + "len": 4294967295, + "offset": "0x0500000001000000000000000000000000000000000000000000000000000000" + } + }, + "name": "elems" + } + ] + } + }, + "name": "entries" + } + ] + } + }, + "name": "keys" + }, + { + "layout": { + "hash": { + "layout": { + "cell": { + "key": "0x0500000002000000000000000000000000000000000000000000000000000000", + "ty": 9 + } + }, + "offset": "0x0400000002000000000000000000000000000000000000000000000000000000", + "strategy": { + "hasher": "Blake2x256", + "postfix": "", + "prefix": "0x696e6b20686173686d6170" + } + } + }, + "name": "values" + } + ] + } + }, + "name": "allowances" + } + ] + } + }, + "types": [ + { + "def": { + "primitive": "u128" + } + }, + { + "def": { + "composite": { + "fields": [ + { + "name": "last_vacant", + "type": 3, + "typeName": "Index" + }, + { + "name": "len", + "type": 3, + "typeName": "u32" + }, + { + "name": "len_entries", + "type": 3, + "typeName": "u32" + } + ] + } + }, + "path": [ + "ink_storage", + "collections", + "stash", + "Header" + ] + }, + { + "def": { + "primitive": "u32" + } + }, + { + "def": { + "variant": { + "variants": [ + { + "fields": [ + { + "type": 8, + "typeName": "VacantEntry" + } + ], + "name": "Vacant" + }, + { + "fields": [ + { + "type": 5, + "typeName": "T" + } + ], + "name": "Occupied" + } + ] + } + }, + "params": [ + 5 + ], + "path": [ + "ink_storage", + "collections", + "stash", + "Entry" + ] + }, + { + "def": { + "composite": { + "fields": [ + { + "type": 6, + "typeName": "[u8; 32]" + } + ] + } + }, + "path": [ + "ink_env", + "types", + "AccountId" + ] + }, + { + "def": { + "array": { + "len": 32, + "type": 7 + } + } + }, + { + "def": { + "primitive": "u8" + } + }, + { + "def": { + "composite": { + "fields": [ + { + "name": "next", + "type": 3, + "typeName": "Index" + }, + { + "name": "prev", + "type": 3, + "typeName": "Index" + } + ] + } + }, + "path": [ + "ink_storage", + "collections", + "stash", + "VacantEntry" + ] + }, + { + "def": { + "composite": { + "fields": [ + { + "name": "value", + "type": 1, + "typeName": "V" + }, + { + "name": "key_index", + "type": 3, + "typeName": "KeyIndex" + } + ] + } + }, + "params": [ + 1 + ], + "path": [ + "ink_storage", + "collections", + "hashmap", + "ValueEntry" + ] + }, + { + "def": { + "variant": { + "variants": [ + { + "fields": [ + { + "type": 8, + "typeName": "VacantEntry" + } + ], + "name": "Vacant" + }, + { + "fields": [ + { + "type": 11, + "typeName": "T" + } + ], + "name": "Occupied" + } + ] + } + }, + "params": [ + 11 + ], + "path": [ + "ink_storage", + "collections", + "stash", + "Entry" + ] + }, + { + "def": { + "tuple": [ + 5, + 5 + ] + } + }, + { + "def": { + "variant": { + "variants": [ + { + "fields": [ + { + "type": 13, + "typeName": "T" + } + ], + "name": "Ok" + }, + { + "fields": [ + { + "type": 14, + "typeName": "E" + } + ], + "name": "Err" + } + ] + } + }, + "params": [ + 13, + 14 + ], + "path": [ + "Result" + ] + }, + { + "def": { + "tuple": [] + } + }, + { + "def": { + "variant": { + "variants": [ + { + "discriminant": 0, + "name": "InsufficientBalance" + }, + { + "discriminant": 1, + "name": "InsufficientAllowance" + } + ] + } + }, + "path": [ + "erc20", + "erc20", + "Error" + ] + }, + { + "def": { + "variant": { + "variants": [ + { + "name": "None" + }, + { + "fields": [ + { + "type": 5, + "typeName": "T" + } + ], + "name": "Some" + } + ] + } + }, + "params": [ + 5 + ], + "path": [ + "Option" + ] + } + ] +} \ No newline at end of file diff --git a/frame/contracts/benchmarks/ink_erc20.wasm b/frame/contracts/benchmarks/ink_erc20.wasm new file mode 100644 index 0000000000000..ffd522760a02d Binary files /dev/null and b/frame/contracts/benchmarks/ink_erc20.wasm differ diff --git a/frame/contracts/benchmarks/ink_erc20_test.wasm b/frame/contracts/benchmarks/ink_erc20_test.wasm new file mode 100644 index 0000000000000..f5d84552960a3 Binary files /dev/null and b/frame/contracts/benchmarks/ink_erc20_test.wasm differ diff --git a/frame/contracts/benchmarks/solang_erc20.json b/frame/contracts/benchmarks/solang_erc20.json new file mode 100644 index 0000000000000..9d8fd5ce70e70 --- /dev/null +++ b/frame/contracts/benchmarks/solang_erc20.json @@ -0,0 +1,581 @@ +{ + "contract": { + "authors": [ + "unknown" + ], + "name": "ERC20PresetFixedSupply", + "version": "0.0.1" + }, + "metadataVersion": "0.1.0", + "source": { + "compiler": "solang 0.1.7", + "hash": "0x9c55e342566e89c741eb641eec3af796836da750fc930c55bccc0604a47ef700", + "language": "Solidity 0.1.7" + }, + "spec": { + "constructors": [ + { + "args": [ + { + "name": "name", + "type": { + "display_name": [ + "String" + ], + "type": 2 + } + }, + { + "name": "symbol", + "type": { + "display_name": [ + "String" + ], + "type": 2 + } + }, + { + "name": "initialSupply", + "type": { + "display_name": [ + "u256" + ], + "type": 1 + } + }, + { + "name": "owner", + "type": { + "display_name": [ + "AccountId" + ], + "type": 5 + } + } + ], + "docs": [ + "" + ], + "name": "new", + "selector": "0xa6f1f5e1" + } + ], + "events": [ + { + "args": [ + { + "indexed": true, + "name": "owner", + "type": { + "display_name": [ + "AccountId" + ], + "type": 5 + } + }, + { + "indexed": true, + "name": "spender", + "type": { + "display_name": [ + "AccountId" + ], + "type": 5 + } + }, + { + "indexed": false, + "name": "value", + "type": { + "display_name": [ + "u256" + ], + "type": 1 + } + } + ], + "docs": [ + "" + ], + "name": "Approval" + }, + { + "args": [ + { + "indexed": true, + "name": "from", + "type": { + "display_name": [ + "AccountId" + ], + "type": 5 + } + }, + { + "indexed": true, + "name": "to", + "type": { + "display_name": [ + "AccountId" + ], + "type": 5 + } + }, + { + "indexed": false, + "name": "value", + "type": { + "display_name": [ + "u256" + ], + "type": 1 + } + } + ], + "docs": [ + "" + ], + "name": "Transfer" + } + ], + "messages": [ + { + "args": [ + { + "name": "account", + "type": { + "display_name": [ + "AccountId" + ], + "type": 5 + } + }, + { + "name": "amount", + "type": { + "display_name": [ + "u256" + ], + "type": 1 + } + } + ], + "docs": [ + "" + ], + "mutates": true, + "name": "burnFrom", + "payable": false, + "return_type": null, + "selector": "0x0f1354f3" + }, + { + "args": [ + { + "name": "account", + "type": { + "display_name": [ + "AccountId" + ], + "type": 5 + } + } + ], + "docs": [ + "" + ], + "mutates": false, + "name": "balanceOf", + "payable": false, + "return_type": { + "display_name": [ + "u256" + ], + "type": 1 + }, + "selector": "0x6c7f1542" + }, + { + "args": [], + "docs": [ + "" + ], + "mutates": false, + "name": "totalSupply", + "payable": false, + "return_type": { + "display_name": [ + "u256" + ], + "type": 1 + }, + "selector": "0x18160ddd" + }, + { + "args": [], + "docs": [ + "" + ], + "mutates": false, + "name": "decimals", + "payable": false, + "return_type": { + "display_name": [ + "u8" + ], + "type": 3 + }, + "selector": "0x313ce567" + }, + { + "args": [ + { + "name": "owner", + "type": { + "display_name": [ + "AccountId" + ], + "type": 5 + } + }, + { + "name": "spender", + "type": { + "display_name": [ + "AccountId" + ], + "type": 5 + } + } + ], + "docs": [ + "" + ], + "mutates": false, + "name": "allowance", + "payable": false, + "return_type": { + "display_name": [ + "u256" + ], + "type": 1 + }, + "selector": "0xf2a9a8c7" + }, + { + "args": [], + "docs": [ + "" + ], + "mutates": false, + "name": "name", + "payable": false, + "return_type": { + "display_name": [ + "String" + ], + "type": 2 + }, + "selector": "0x06fdde03" + }, + { + "args": [ + { + "name": "spender", + "type": { + "display_name": [ + "AccountId" + ], + "type": 5 + } + }, + { + "name": "subtractedValue", + "type": { + "display_name": [ + "u256" + ], + "type": 1 + } + } + ], + "docs": [ + "" + ], + "mutates": true, + "name": "decreaseAllowance", + "payable": false, + "return_type": { + "display_name": [ + "bool" + ], + "type": 6 + }, + "selector": "0x4b76697b" + }, + { + "args": [ + { + "name": "sender", + "type": { + "display_name": [ + "AccountId" + ], + "type": 5 + } + }, + { + "name": "recipient", + "type": { + "display_name": [ + "AccountId" + ], + "type": 5 + } + }, + { + "name": "amount", + "type": { + "display_name": [ + "u256" + ], + "type": 1 + } + } + ], + "docs": [ + "" + ], + "mutates": true, + "name": "transferFrom", + "payable": false, + "return_type": { + "display_name": [ + "bool" + ], + "type": 6 + }, + "selector": "0x2fb840f5" + }, + { + "args": [], + "docs": [ + "" + ], + "mutates": false, + "name": "symbol", + "payable": false, + "return_type": { + "display_name": [ + "String" + ], + "type": 2 + }, + "selector": "0x95d89b41" + }, + { + "args": [ + { + "name": "spender", + "type": { + "display_name": [ + "AccountId" + ], + "type": 5 + } + }, + { + "name": "addedValue", + "type": { + "display_name": [ + "u256" + ], + "type": 1 + } + } + ], + "docs": [ + "" + ], + "mutates": true, + "name": "increaseAllowance", + "payable": false, + "return_type": { + "display_name": [ + "bool" + ], + "type": 6 + }, + "selector": "0xb936c899" + }, + { + "args": [ + { + "name": "recipient", + "type": { + "display_name": [ + "AccountId" + ], + "type": 5 + } + }, + { + "name": "amount", + "type": { + "display_name": [ + "u256" + ], + "type": 1 + } + } + ], + "docs": [ + "" + ], + "mutates": true, + "name": "transfer", + "payable": false, + "return_type": { + "display_name": [ + "bool" + ], + "type": 6 + }, + "selector": "0x6a467394" + }, + { + "args": [ + { + "name": "spender", + "type": { + "display_name": [ + "AccountId" + ], + "type": 5 + } + }, + { + "name": "amount", + "type": { + "display_name": [ + "u256" + ], + "type": 1 + } + } + ], + "docs": [ + "" + ], + "mutates": true, + "name": "approve", + "payable": false, + "return_type": { + "display_name": [ + "bool" + ], + "type": 6 + }, + "selector": "0x47144421" + }, + { + "args": [ + { + "name": "amount", + "type": { + "display_name": [ + "u256" + ], + "type": 1 + } + } + ], + "docs": [ + "" + ], + "mutates": true, + "name": "burn", + "payable": false, + "return_type": null, + "selector": "0x42966c68" + } + ] + }, + "storage": { + "struct": { + "fields": [ + { + "layout": { + "cell": { + "key": "0x0000000000000000000000000000000000000000000000000000000000000002", + "ty": 1 + } + }, + "name": "_totalSupply" + }, + { + "layout": { + "cell": { + "key": "0x0000000000000000000000000000000000000000000000000000000000000003", + "ty": 2 + } + }, + "name": "_name" + }, + { + "layout": { + "cell": { + "key": "0x0000000000000000000000000000000000000000000000000000000000000004", + "ty": 2 + } + }, + "name": "_symbol" + } + ] + } + }, + "types": [ + { + "def": { + "primitive": "u256" + } + }, + { + "def": { + "primitive": "str" + } + }, + { + "def": { + "primitive": "u8" + } + }, + { + "def": { + "array": { + "len": 32, + "type": 3 + } + } + }, + { + "def": { + "composite": { + "fields": [ + { + "type": 4 + } + ] + } + }, + "path": [ + "AccountId" + ] + }, + { + "def": { + "primitive": "bool" + } + } + ] +} diff --git a/frame/contracts/benchmarks/solang_erc20.wasm b/frame/contracts/benchmarks/solang_erc20.wasm new file mode 100644 index 0000000000000..0796085d33249 Binary files /dev/null and b/frame/contracts/benchmarks/solang_erc20.wasm differ diff --git a/frame/contracts/src/benchmarking/code.rs b/frame/contracts/src/benchmarking/code.rs index 64bdde9b6ea5a..15abd9968cd01 100644 --- a/frame/contracts/src/benchmarking/code.rs +++ b/frame/contracts/src/benchmarking/code.rs @@ -26,14 +26,12 @@ use crate::Config; use frame_support::traits::Get; -use pwasm_utils::{ - parity_wasm::{ - builder, - elements::{ - self, BlockType, CustomSection, FuncBody, Instruction, Instructions, Section, ValueType, - }, +use pwasm_utils::parity_wasm::{ + builder, + elements::{ + self, BlockType, CustomSection, External, FuncBody, Instruction, Instructions, Module, + Section, ValueType, }, - stack_height::inject_limiter, }; use sp_core::crypto::UncheckedFrom; use sp_runtime::traits::Hash; @@ -241,9 +239,8 @@ where let mut code = contract.build(); - // Inject stack height metering if def.inject_stack_metering { - code = inject_limiter(code, T::Schedule::get().limits.stack_height).unwrap(); + code = inject_stack_metering::(code); } let code = code.to_bytes().unwrap(); @@ -257,6 +254,34 @@ where T: Config, T::AccountId: UncheckedFrom + AsRef<[u8]>, { + /// Uses the supplied wasm module and instruments it when requested. + pub fn instrumented(code: &[u8], inject_gas: bool, inject_stack: bool) -> Self { + let module = { + let mut module = Module::from_bytes(code).unwrap(); + if inject_gas { + module = inject_gas_metering::(module); + } + if inject_stack { + module = inject_stack_metering::(module); + } + module + }; + let limits = module + .import_section() + .unwrap() + .entries() + .iter() + .find_map(|e| if let External::Memory(mem) = e.external() { Some(mem) } else { None }) + .unwrap() + .limits() + .clone(); + let code = module.to_bytes().unwrap(); + let hash = T::Hashing::hash(&code); + let memory = + ImportedMemory { min_pages: limits.initial(), max_pages: limits.maximum().unwrap() }; + Self { code, hash, memory: Some(memory) } + } + /// Creates a wasm module with an empty `call` and `deploy` function and nothing else. pub fn dummy() -> Self { ModuleDefinition::default().into() @@ -519,3 +544,14 @@ where { T::Schedule::get().limits.memory_pages } + +fn inject_gas_metering(module: Module) -> Module { + let schedule = T::Schedule::get(); + let gas_rules = schedule.rules(&module); + pwasm_utils::inject_gas_counter(module, &gas_rules, "seal0").unwrap() +} + +fn inject_stack_metering(module: Module) -> Module { + let height = T::Schedule::get().limits.stack_height; + pwasm_utils::stack_height::inject_limiter(module, height).unwrap() +} diff --git a/frame/contracts/src/benchmarking/mod.rs b/frame/contracts/src/benchmarking/mod.rs index 1ffb84dad9aa5..c3757cf705bf4 100644 --- a/frame/contracts/src/benchmarking/mod.rs +++ b/frame/contracts/src/benchmarking/mod.rs @@ -30,7 +30,7 @@ use self::{ sandbox::Sandbox, }; use crate::{ - exec::StorageKey, + exec::{AccountIdOf, StorageKey}, rent::Rent, schedule::{API_BENCHMARK_BATCH_SIZE, INSTR_BENCHMARK_BATCH_SIZE}, storage::Storage, @@ -124,9 +124,9 @@ where .saturating_mul(>::from(storage_size) / 2u32.into()) .saturating_add(T::DepositPerContract::get()); - (storage_size, endowment) + (Some(storage_size), endowment) }, - Endow::Max => (0u32.into(), Endow::max::()), + Endow::Max => (None, Endow::max::()), }; T::Currency::make_free_balance_be(&caller, caller_funding::()); let salt = vec![0xff]; @@ -158,7 +158,9 @@ where }; let mut contract = result.alive_info()?; - contract.storage_size = storage_size; + if let Some(size) = storage_size { + contract.storage_size = size; + } ContractInfoOf::::insert(&result.account_id, ContractInfo::Alive(contract)); Ok(result) @@ -278,6 +280,24 @@ fn caller_funding() -> BalanceOf { BalanceOf::::max_value() / 2u32.into() } +/// Load the specified contract file from disk by including it into the runtime. +/// +/// We need to load a different version of ink! contracts when the benchmark is run as +/// a test. This is because ink! contracts depend on the sizes of types that are defined +/// differently in the test environment. Solang is more lax in that regard. +macro_rules! load_benchmark { + ($name:expr) => {{ + #[cfg(not(test))] + { + include_bytes!(concat!("../../benchmarks/", $name, ".wasm")) + } + #[cfg(test)] + { + include_bytes!(concat!("../../benchmarks/", $name, "_test.wasm")) + } + }}; +} + benchmarks! { where_clause { where T::AccountId: UncheckedFrom, @@ -2536,6 +2556,88 @@ benchmarks! { #[cfg(not(feature = "std"))] return Err("Run this bench with a native runtime in order to see the schedule."); }: {} + + // Execute one erc20 transfer using the ink! erc20 example contract. + // + // `g` is used to enable gas instrumentation to compare the performance impact of + // that instrumentation at runtime. + #[extra] + ink_erc20_transfer { + let g in 0 .. 1; + let gas_metering = if g == 0 { false } else { true }; + let code = load_benchmark!("ink_erc20"); + let data = { + let new: ([u8; 4], BalanceOf) = ([0x9b, 0xae, 0x9d, 0x5e], 1000u32.into()); + new.encode() + }; + let instance = Contract::::new( + WasmModule::instrumented(code, gas_metering, true), data, Endow::Max, + )?; + let data = { + let transfer: ([u8; 4], AccountIdOf, BalanceOf) = ( + [0x84, 0xa1, 0x5d, 0xa1], + account::("receiver", 0, 0), + 1u32.into(), + ); + transfer.encode() + }; + }: { + >::bare_call( + instance.caller, + instance.account_id, + 0u32.into(), + Weight::MAX, + data, + false, + ) + .result?; + } + + // Execute one erc20 transfer using the open zeppelin erc20 contract compiled with solang. + // + // `g` is used to enable gas instrumentation to compare the performance impact of + // that instrumentation at runtime. + #[extra] + solang_erc20_transfer { + let g in 0 .. 1; + let gas_metering = if g == 0 { false } else { true }; + let code = include_bytes!("../../benchmarks/solang_erc20.wasm"); + let caller = account::("instantiator", 0, 0); + let mut balance = [0u8; 32]; + balance[0] = 100; + let data = { + let new: ([u8; 4], &str, &str, [u8; 32], AccountIdOf) = ( + [0xa6, 0xf1, 0xf5, 0xe1], + "KSM", + "K", + balance, + caller.clone(), + ); + new.encode() + }; + let instance = Contract::::with_caller( + caller, WasmModule::instrumented(code, gas_metering, true), data, Endow::Max, + )?; + balance[0] = 1; + let data = { + let transfer: ([u8; 4], AccountIdOf, [u8; 32]) = ( + [0x6a, 0x46, 0x73, 0x94], + account::("receiver", 0, 0), + balance, + ); + transfer.encode() + }; + }: { + >::bare_call( + instance.caller, + instance.account_id, + 0u32.into(), + Weight::MAX, + data, + false, + ) + .result?; + } } impl_benchmark_test_suite!(