From ddd835c6bf6e31c45112bbb968b283568e4b9f99 Mon Sep 17 00:00:00 2001 From: Maxim Andreev Date: Sun, 29 Dec 2024 11:42:58 +0000 Subject: [PATCH] add experimental storage benchmark --- benchmark/Makefile | 13 +- benchmark/compare.py | 53 ++- benchmark/providers/etherscan/main.py | 98 ++++ benchmark/providers/smlxl/Cargo.lock | 625 ++++++++++++++++++++++++++ benchmark/providers/smlxl/Cargo.toml | 12 + benchmark/providers/smlxl/Dockerfile | 10 + benchmark/providers/smlxl/src/main.rs | 161 +++++++ 7 files changed, 963 insertions(+), 9 deletions(-) create mode 100644 benchmark/providers/smlxl/Cargo.lock create mode 100644 benchmark/providers/smlxl/Cargo.toml create mode 100644 benchmark/providers/smlxl/Dockerfile create mode 100644 benchmark/providers/smlxl/src/main.rs diff --git a/benchmark/Makefile b/benchmark/Makefile index 6a9d4df..28d53c9 100644 --- a/benchmark/Makefile +++ b/benchmark/Makefile @@ -2,9 +2,10 @@ PROVIDER_BASE = etherscan PROVIDERS_SELECTORS ?= simple whatsabi evm-hound-rs sevm evmole-rs evmole-js evmole-py PROVIDERS_ARGUMENTS ?= simple evmole-rs evmole-js evmole-py PROVIDERS_MUTABILITY ?= simple whatsabi sevm evmole-rs evmole-js evmole-py -#heimdall-rs +PROVIDERS_STORAGE ?= evmole-rs smlxl DATASETS ?= largest1k random50k vyper +DATASETS_STORAGE ?= storage3k DOCKER ?= docker DOCKER_CPUS ?= 1 DOCKER_PREFIX ?= evmole-bench @@ -12,27 +13,31 @@ DOCKER_PREFIX ?= evmole-bench PROVIDERS_SELECTORS := $(PROVIDER_BASE) $(PROVIDERS_SELECTORS) PROVIDERS_ARGUMENTS := $(PROVIDER_BASE) $(PROVIDERS_ARGUMENTS) PROVIDERS_MUTABILITY := $(PROVIDER_BASE) $(PROVIDERS_MUTABILITY) -PROVIDERS_UNIQ := $(sort $(PROVIDERS_SELECTORS) $(PROVIDERS_ARGUMENTS) $(PROVIDERS_MUTABILITY)) +PROVIDERS_STORAGE := $(PROVIDER_BASE) $(PROVIDERS_STORAGE) +PROVIDERS_UNIQ := $(sort $(PROVIDERS_SELECTORS) $(PROVIDERS_ARGUMENTS) $(PROVIDERS_MUTABILITY) $(PROVIDERS_STORAGE)) DATASET := $(shell pwd)/datasets RES := $(shell pwd)/results BUILD_TARGETS := $(addsuffix .build, $(PROVIDERS_UNIQ)) -UNPACK_TARGETS := $(foreach d,$(DATASETS),$(addprefix datasets/, $(d))) +UNPACK_TARGETS := $(foreach d,$(DATASETS) $(DATASETS_STORAGE),$(addprefix datasets/, $(d))) RUN_SELECTORS_TARGETS := $(foreach p,$(PROVIDERS_SELECTORS),$(addprefix $(p).selectors/, $(DATASETS))) RUN_ARGUMENTS_TARGETS := $(foreach p,$(PROVIDERS_ARGUMENTS),$(addprefix $(p).arguments/, $(DATASETS))) RUN_MUTABILITY_TARGETS := $(foreach p,$(PROVIDERS_MUTABILITY),$(addprefix $(p).mutability/, $(DATASETS))) +RUN_STORAGE_TARGETS := $(foreach p,$(PROVIDERS_STORAGE),$(addprefix $(p).storage/, $(DATASETS_STORAGE))) -RUN_TARGETS := $(RUN_SELECTORS_TARGETS) $(RUN_ARGUMENTS_TARGETS) $(RUN_MUTABILITY_TARGETS) +RUN_TARGETS := $(RUN_SELECTORS_TARGETS) $(RUN_ARGUMENTS_TARGETS) $(RUN_MUTABILITY_TARGETS) $(RUN_STORAGE_TARGETS) benchmark-selectors: $(addsuffix .build, $(PROVIDERS_SELECTORS)) run-selectors benchmark-arguments: $(addsuffix .build, $(PROVIDERS_ARGUMENTS)) run-arguments benchmark-mutability: $(addsuffix .build, $(PROVIDERS_MUTABILITY)) run-mutability +benchmark-storage: $(addsuffix .build, $(PROVIDERS_STORAGE)) run-storage build: $(BUILD_TARGETS) run-selectors: $(RUN_SELECTORS_TARGETS) run-arguments: $(RUN_ARGUMENTS_TARGETS) run-mutability: $(RUN_MUTABILITY_TARGETS) +run-storage: $(RUN_STORAGE_TARGETS) $(BUILD_TARGETS): $(info [*] Building $(basename $@)...) diff --git a/benchmark/compare.py b/benchmark/compare.py index 271d358..ac1c05d 100644 --- a/benchmark/compare.py +++ b/benchmark/compare.py @@ -229,6 +229,42 @@ def process_mutability(dname: str, providers: list[str], results_dir: str, stric def process_arguments(dname: str, providers: list[str], results_dir: str, normalize_rules: set[str]): return process_functions('arguments', dname, providers, results_dir, lambda x: normalize_args(x, normalize_rules)) +def process_storage(dname: str, providers: list[str], results_dir: str): + pdata, ptimes = load_data('storage', dname, providers, results_dir) + ret = [] + for fname, (_meta, gt) in pdata[0].items(): + func = [] + for gt_slot, gt_type in gt.items(): + data = [] + for i in range(1, len(providers)): # skip ground_truth provider + vtype = pdata[i][fname][1].get(gt_slot) + if vtype == gt_type: + data.append([1]) + else: + data.append([0, vtype]) + func.append({'s': gt_slot, 'gt': gt_type, 'data': data}) + + qwe = set() + for i in range(1, len(providers)): + qwe |= set(pdata[i][fname][1].keys()) + + false_positive_slots = sorted(list(qwe - set(pdata[0][fname][1].keys()))) + for slot in false_positive_slots: + data = [] + for i in range(1, len(providers)): # skip ground_truth provider + vtype = pdata[i][fname][1].get(slot) + if vtype is None: + data.append([1]) + else: + data.append([0, vtype]) + func.append({'s': slot, 'gt': None, 'data': data}) + + ret.append({ + 'addr': fname[2:-5], # '0xFF.json' => 'FF' + 'func': func, + }) + return {'dataset': dname, 'results': ret, 'timings': ptimes[1:]} + def show_arguments_or_mutability(providers: list[str], all_results: list, show_errors: bool): for dataset_result in all_results: cnt_contracts = len(dataset_result['results']) @@ -244,7 +280,7 @@ def show_arguments_or_mutability(providers: list[str], all_results: list, show_e if show_errors is not True: continue print(' errors:') - for x in dataset_result['results']: + for x in sorted(dataset_result['results'], key=lambda x:x['addr']): for y in x['func']: if len(y['data'][provider_idx]) > 1: assert y['data'][provider_idx][0] == 0 @@ -260,13 +296,15 @@ def show_arguments_or_mutability(providers: list[str], all_results: list, show_e if __name__ == '__main__': parser = argparse.ArgumentParser() parser.add_argument('--results-dir', type=str, default=pathlib.Path(__file__).parent / 'results', help='results directory') - parser.add_argument('--mode', choices=['selectors', 'arguments', 'mutability'], default='selectors', help='mode') + parser.add_argument('--mode', choices=['selectors', 'arguments', 'mutability', 'storage'], default='selectors', help='mode') parser.add_argument('--providers', nargs='+', default=None) - parser.add_argument('--datasets', nargs='+', default=['largest1k', 'random50k', 'vyper']) + parser.add_argument('--datasets', nargs='+', default=None) parser.add_argument('--markdown', nargs='?', default=False, const=True, help='show markdown output') parser.add_argument('--show-errors', nargs='?', default=False, const=True, help='show errors') parser.add_argument('--normalize-args', nargs='+', required=False, choices=['fixed-size-array', 'tuples', 'string-bytes'], help='normalize arguments rules') cfg = parser.parse_args() + if cfg.datasets is None: + cfg.datasets = ['storage3k'] if cfg.mode == 'storage' else ['largest1k', 'random50k', 'vyper'] if cfg.providers is None: if cfg.mode == 'selectors': cfg.providers = ['etherscan', 'evmole-rs', 'evmole-js', 'evmole-py', 'whatsabi', 'sevm', 'evm-hound-rs', 'heimdall-rs', 'simple'] @@ -274,8 +312,8 @@ def show_arguments_or_mutability(providers: list[str], all_results: list, show_e cfg.providers = ['etherscan', 'evmole-rs', 'evmole-js', 'evmole-py', 'heimdall-rs', 'simple'] elif cfg.mode == 'mutability': cfg.providers = ['etherscan', 'evmole-rs', 'evmole-js', 'evmole-py', 'whatsabi', 'sevm', 'heimdall-rs', 'simple'] - else: - cfg.providers = [] + elif cfg.mode == 'storage': + cfg.providers = ['etherscan', 'evmole-rs', 'smlxl'] print('Config:') print('\n'.join(f' {field} = {getattr(cfg, field)}' for field in vars(cfg)), '\n') @@ -306,3 +344,8 @@ def show_arguments_or_mutability(providers: list[str], all_results: list, show_e x['dataset'] += '/strict' results.append(x) show_arguments_or_mutability(cfg.providers, results, cfg.show_errors) + + elif cfg.mode == 'storage': + # results = [process_storage(d, cfg.providers, cfg.results_dir) for d in cfg.datasets] + results = [process_storage('storage3k', cfg.providers, cfg.results_dir)] + show_arguments_or_mutability(cfg.providers, results, cfg.show_errors) diff --git a/benchmark/providers/etherscan/main.py b/benchmark/providers/etherscan/main.py index 458edcc..c6f867d 100644 --- a/benchmark/providers/etherscan/main.py +++ b/benchmark/providers/etherscan/main.py @@ -22,7 +22,105 @@ def join_inputs(inputs) -> str: n += ',' return n[:-1] +def process_storage_mapping(types, k, v) -> str: + kt = types[k] + vt = types[v] + if isinstance(vt, str): + return f'mapping({kt} => {vt})' + + if isinstance(vt, dict): + assert len(vt) == 1 + val = process_storage_mapping(types, *list(vt.items())[0]) + return f'mapping({kt} => {val})' + + if isinstance(vt, tuple): + if len(vt) == 1: + # struct with only 1 field: + return process_storage_mapping(types, k, vt[0]['type']) + else: + return f'mapping({kt} => struct_{len(vt)}_fields)' + + if isinstance(vt, list): + val = process_storage_dynarray(types, types[vt[0]]) + return f'mapping({kt} => {val})' + + raise Exception(f'Unsupported map type {kt} / {vt}') + +def process_storage_dynarray(types, base) -> str: + if isinstance(base, str): + return f'{base}[]' + if isinstance(base, tuple): + if len(base) == 1: + return process_storage_dynarray(types, base[0]) + '[]' + else: + return f'struct_{len(base)}_fields[]' + + if isinstance(base, list): + return process_storage_dynarray(types, types[base[0]]) + '[]' + + raise Exception(f'Unsupported dynamic array base type {base}') + +def process_storage_value(types, base_slot: int, offset, value) -> dict[str, str]: + key = f'{base_slot:064x}_{offset}' + if isinstance(value, str): + return {key: value} + elif isinstance(value, tuple): + assert offset == 0 + ret: dict[str, str] = {} + for y in value: + r = process_storage_value(types, base_slot + int(y['slot']), y['offset'], types[ y['type'] ]) + ret.update(r) + return ret + elif isinstance(value, dict): + assert len(value) == 1 + k, v = list(value.items())[0] + v = process_storage_mapping(types, k, v) + return {key: v} + elif isinstance(value, list): + base = types[ value[0] ] + v = process_storage_dynarray(types, base) + return {key: v} + else: + raise Exception(f'Unsupported value type {value}') + +def process_storage(sl): + """ + Experimental code, not 100% accurate benchmark + """ + types = {} + for (tname, tinfo) in (sl['types'] or {}).items(): + tvalue = None + match tinfo['encoding']: + case 'inplace': + if 'members' in tinfo: + assert tinfo['label'].startswith('struct') + tvalue = tuple(tinfo['members']) + else: + tvalue = tinfo['label'] + case 'mapping': + tvalue = {tinfo['key']: tinfo['value']} + case 'bytes': + tvalue = tinfo['label'] + case 'dynamic_array': + tvalue = [ tinfo['base'] ] + case _: + raise Exception(f'Unsupported type {tinfo}') + + if isinstance(tvalue, str): + tvalue = tvalue.replace('address payable', 'address') + tvalue = re.sub(r'contract \w+', 'address', tvalue) + types[tname] = tvalue + + ret = {} + for x in sl['storage']: + r = process_storage_value(types, int(x['slot']), x['offset'], types[ x['type'] ]) + ret.update(r) + + return ret + def process(data, mode): + if mode == 'storage': + return process_storage(data['storageLayout']) ret = {} for x in data['abi']: if x['type'] != 'function': diff --git a/benchmark/providers/smlxl/Cargo.lock b/benchmark/providers/smlxl/Cargo.lock new file mode 100644 index 0000000..6717f87 --- /dev/null +++ b/benchmark/providers/smlxl/Cargo.lock @@ -0,0 +1,625 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "anstream" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" + +[[package]] +name = "anstyle-parse" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" +dependencies = [ + "anstyle", + "windows-sys", +] + +[[package]] +name = "bimap" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "230c5f1ca6a325a32553f8640d31ac9b49f2411e901e427570154868b46da4f7" + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clap" +version = "4.5.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.93", +] + +[[package]] +name = "clap_lex" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" + +[[package]] +name = "colorchoice" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" + +[[package]] +name = "cpufeatures" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "downcast-rs" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "ethnum" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b90ca2580b73ab6a1f724b76ca11ab632df820fd6040c336200d2c1df7b3c82c" + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + +[[package]] +name = "itertools" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" + +[[package]] +name = "keccak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "libc" +version = "0.2.169" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" + +[[package]] +name = "main" +version = "0.1.0" +dependencies = [ + "clap", + "hex", + "serde", + "serde_json", + "storage-layout-extractor", +] + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro2" +version = "1.0.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "serde" +version = "1.0.217" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.217" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.93", +] + +[[package]] +name = "serde_json" +version = "1.0.134" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d00f4175c42ee48b15416f6193a959ba3a0d67fc699a0db9ad12df9f83991c7d" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest", + "keccak", +] + +[[package]] +name = "storage-layout-extractor" +version = "0.5.0" +source = "git+https://github.com/smlxl/storage-layout-extractor?branch=main#b7d7a074f44209e1e12683a0e439b7fca6953872" +dependencies = [ + "bimap", + "bitvec", + "derivative", + "downcast-rs", + "ethnum", + "hex", + "itertools", + "serde", + "sha3", + "thiserror", + "uuid", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c786062daee0d6db1132800e623df74274a0a87322d8e183338e01b3d98d058" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.93", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicode-ident" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "uuid" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" +dependencies = [ + "getrandom", + "rand", + "uuid-macro-internal", +] + +[[package]] +name = "uuid-macro-internal" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b91f57fe13a38d0ce9e28a03463d8d3c2468ed03d75375110ec71d93b449a08" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.93", +] + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.93", +] diff --git a/benchmark/providers/smlxl/Cargo.toml b/benchmark/providers/smlxl/Cargo.toml new file mode 100644 index 0000000..e6f67f9 --- /dev/null +++ b/benchmark/providers/smlxl/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "main" +version = "0.1.0" +edition = "2021" + +[dependencies] +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +hex = "0.4.3" +clap = { version = "4.5.1", features = ["derive"] } + +storage-layout-extractor = { git = "https://github.com/smlxl/storage-layout-extractor", branch = "main" } diff --git a/benchmark/providers/smlxl/Dockerfile b/benchmark/providers/smlxl/Dockerfile new file mode 100644 index 0000000..98d8998 --- /dev/null +++ b/benchmark/providers/smlxl/Dockerfile @@ -0,0 +1,10 @@ +# syntax=docker/dockerfile:1 + +FROM rust:1.83 +WORKDIR /app +COPY . . +RUN --mount=type=cache,target=./.cargo \ + --mount=type=cache,target=./target \ + CARGO_HOME=/app/.cargo \ + cargo install --locked --root=. --path . +ENTRYPOINT ["./bin/main"] diff --git a/benchmark/providers/smlxl/src/main.rs b/benchmark/providers/smlxl/src/main.rs new file mode 100644 index 0000000..4b4abb3 --- /dev/null +++ b/benchmark/providers/smlxl/src/main.rs @@ -0,0 +1,161 @@ +use std::collections::HashMap; +use std::fs; +use std::io::{BufWriter, Write}; +use std::time::{Duration, Instant}; +use clap::{Parser, ValueEnum}; + +use storage_layout_extractor::{ + self, + watchdog::Watchdog, + extractor::{ + chain::{ + version::{ChainVersion, EthereumVersion}, + Chain, + }, + contract::Contract, + }, + tc, + vm, +}; + +#[derive(serde::Deserialize)] +struct Input { + #[serde(rename = "runtimeBytecode")] + code: String, +} + +#[derive(ValueEnum, Clone, PartialEq)] +enum Mode { + Storage, +} + +#[derive(Parser)] +struct Args { + mode: Mode, + + input_dir: String, + + output_file: String, + + selectors_file: Option, + + #[arg(long)] + filter_filename: Option, + + #[arg(long)] + filter_selector: Option, +} + +// TODO: improve this, validate with github upstream (new issue) +fn type_str(tp: &tc::abi::AbiType) -> String { + use tc::abi::AbiType::*; + // eprintln!("{:?}", tp); + match tp { + Any => "uint256".to_string(), + Number { size } | UInt { size } => format!("uint{}", size.unwrap_or(256)), + Int { size } => format!("int{}", size.unwrap_or(256)), + Address => "address".to_string(), + Selector => "err_selector".to_string(), + Function => "err_function".to_string(), + Bool => "bool".to_string(), + Array { size, tp } => format!("{}[{}]", type_str(tp), u16::try_from(size.0).unwrap_or(1)), + Bytes { length } => format!("bytes{}", length.unwrap_or(32)), + Bits { length } => "errbits".to_string(), + DynArray { tp } => format!("{}[]", type_str(tp)), + DynBytes => "bytes".to_string(), + Mapping { key_type, value_type } => format!("mapping({} => {})", type_str(key_type), type_str(value_type)), + Struct { elements } => { + "struct".to_string() + }, + InfiniteType => "uint256".to_string(), + ConflictedType { conflicts, reasons } => "err_conflict".to_string(), + } +} + +#[derive(Debug)] +struct MyWatchDog { + pub end: Instant, +} + +impl MyWatchDog { + fn new(time_limit: Duration) -> Self { + MyWatchDog { + end: Instant::now() + time_limit, + } + } +} + +impl Watchdog for MyWatchDog { + fn should_stop(&self) -> bool { + let now = Instant::now(); + now >= self.end + } + + fn poll_every(&self) -> usize { + storage_layout_extractor::constant::DEFAULT_WATCHDOG_POLL_LOOP_ITERATIONS + } +} + +fn main() -> Result<(), Box> { + let cfg = Args::parse(); + type Meta = u64; // duration in ms + let mut ret_other: HashMap)> = HashMap::new(); + for entry in fs::read_dir(cfg.input_dir)? { + let entry = entry?; + let path = entry.path(); + let fname = entry.file_name().to_str().unwrap().to_string(); + let code = { + let file_content = fs::read_to_string(path)?; + let v: Input = serde_json::from_str(&file_content)?; + hex::decode(v.code.strip_prefix("0x").expect("0x prefix expected"))? + }; + eprintln!("processing {}", fname); + + let contract = Contract::new( + code, + Chain::Ethereum { + version: EthereumVersion::latest(), + }, + ); + + let vm_config = vm::Config::default(); + let unifier_config = tc::Config::default(); + // let watchdog = LazyWatchdog.in_rc(); + let watchdog = std::rc::Rc::new(MyWatchDog::new(Duration::from_secs(3))); + let extractor = storage_layout_extractor::new(contract, vm_config, unifier_config, watchdog); + let now = Instant::now(); + let r = extractor.analyze(); + let dur = now.elapsed().as_millis() as u64; + + ret_other.insert( + fname, + ( + dur, + match r { + Ok(layout) => { + layout.slots().iter().map(|s| + ( + format!( + "{}_{}", + hex::encode(s.index.0.to_be_bytes()), + s.offset / 8, + ), + type_str(&s.typ), + )).collect() + }, + Err(_err) => { + HashMap::new() + // "err".to_string() + }, + } + ) + ); + } + + let file = fs::File::create(cfg.output_file)?; + let mut bw = BufWriter::new(file); + let _ = serde_json::to_writer(&mut bw, &ret_other); + bw.flush()?; + + Ok(()) +}