From d34583c7405963c373ea602caf452428d5b1759e Mon Sep 17 00:00:00 2001 From: Lior Goldberg Date: Sun, 19 Mar 2023 21:53:31 +0200 Subject: [PATCH] Cairo v0.11.0 (pre3). --- src/starkware/cairo/lang/VERSION | 2 +- .../execution/execute_entry_point.py | 2 +- .../execution/os_resources.json | 4 +- .../business_logic/transaction/CMakeLists.txt | 1 + .../business_logic/transaction/objects.py | 17 ++ src/starkware/starknet/cli/starknet_cli.py | 39 ++- .../starknet/compiler/v1/CMakeLists.txt | 5 +- src/starkware/starknet/compiler/v1/compile.py | 67 +++-- .../compiler/v1/mainnet_libfuncs.json | 3 + .../compiler/v1/testnet2_libfuncs.json | 143 +++++++++++ .../compiler/v1/testnet_libfuncs.json | 129 ++++++++++ .../deprecated_execute_syscalls.cairo | 11 +- .../core/os/execution/execute_syscalls.cairo | 237 +++++++++++++++++- .../starknet/core/os/program_hash.json | 2 +- .../starknet/definitions/constants.py | 3 + .../starknet/definitions/error_codes.py | 2 + .../api/contract_class/contract_class.py | 16 +- .../contract_class/contract_class_utils.py | 8 +- .../contracts/test_contract_cairo1.cairo | 34 +++ .../starknet/solidity/StarknetMessaging.sol | 1 + .../testing/mock_starknet_messaging_test.py | 8 +- .../starknet/testing/postman_test.py | 26 +- 22 files changed, 707 insertions(+), 53 deletions(-) create mode 100644 src/starkware/starknet/compiler/v1/mainnet_libfuncs.json create mode 100644 src/starkware/starknet/compiler/v1/testnet2_libfuncs.json create mode 100644 src/starkware/starknet/compiler/v1/testnet_libfuncs.json diff --git a/src/starkware/cairo/lang/VERSION b/src/starkware/cairo/lang/VERSION index e0cbcd58..a09c7eb7 100644 --- a/src/starkware/cairo/lang/VERSION +++ b/src/starkware/cairo/lang/VERSION @@ -1 +1 @@ -0.11.0a1 +0.11.0a2 diff --git a/src/starkware/starknet/business_logic/execution/execute_entry_point.py b/src/starkware/starknet/business_logic/execution/execute_entry_point.py index 69f3505b..98354666 100644 --- a/src/starkware/starknet/business_logic/execution/execute_entry_point.py +++ b/src/starkware/starknet/business_logic/execution/execute_entry_point.py @@ -329,7 +329,7 @@ def _execute( storage=syscall_handler.storage, result=get_call_result(runner=runner, initial_gas=self.initial_gas), events=syscall_handler.events, - l2_to_l1_messages=[], + l2_to_l1_messages=syscall_handler.l2_to_l1_messages, internal_calls=syscall_handler.internal_calls, ) diff --git a/src/starkware/starknet/business_logic/execution/os_resources.json b/src/starkware/starknet/business_logic/execution/os_resources.json index 908475e1..4a2846cc 100644 --- a/src/starkware/starknet/business_logic/execution/os_resources.json +++ b/src/starkware/starknet/business_logic/execution/os_resources.json @@ -27,7 +27,7 @@ "range_check_builtin": 18 }, "n_memory_holes": 0, - "n_steps": 926 + "n_steps": 920 }, "emit_event": { "builtin_instance_counter": {}, @@ -124,7 +124,7 @@ "range_check_builtin": 83 }, "n_memory_holes": 0, - "n_steps": 3577 + "n_steps": 3571 }, "INVOKE_FUNCTION": { "builtin_instance_counter": { diff --git a/src/starkware/starknet/business_logic/transaction/CMakeLists.txt b/src/starkware/starknet/business_logic/transaction/CMakeLists.txt index 5728d753..b88d1e0c 100644 --- a/src/starkware/starknet/business_logic/transaction/CMakeLists.txt +++ b/src/starkware/starknet/business_logic/transaction/CMakeLists.txt @@ -7,6 +7,7 @@ python_lib(starknet_transaction_objects_lib LIBS everest_business_logic_lib everest_business_logic_state_api_lib + everest_definitions_lib everest_internal_transaction_lib everest_transaction_lib starknet_abi_lib diff --git a/src/starkware/starknet/business_logic/transaction/objects.py b/src/starkware/starknet/business_logic/transaction/objects.py index 2a6de3a7..4ac16190 100644 --- a/src/starkware/starknet/business_logic/transaction/objects.py +++ b/src/starkware/starknet/business_logic/transaction/objects.py @@ -11,6 +11,7 @@ from services.everest.api.gateway.transaction import EverestTransaction from services.everest.business_logic.internal_transaction import EverestInternalTransaction from services.everest.business_logic.state_api import StateProxy +from services.everest.definitions.fields import format_felt_list from starkware.python.utils import as_non_optional, from_bytes, to_bytes from starkware.starknet.business_logic.execution.execute_entry_point import ExecuteEntryPoint from starkware.starknet.business_logic.execution.objects import ( @@ -348,6 +349,22 @@ def run_validate_entrypoint( n_steps=general_config.validate_max_n_steps ), ) + + class_hash = state.get_class_hash_at(contract_address=self.sender_address) + compiled_class_hash = state.get_compiled_class_hash(class_hash=class_hash) + if compiled_class_hash != 0: + # The account contract class is a Cairo 1.0 contract; the 'validate' entry point + # should return 'VALID'. + stark_assert( + call_info.retdata == constants.VALIDATE_RETDATA, + code=StarknetErrorCode.INVALID_RETURN_DATA, + message=( + "Invalid 'validate' return values. " + f"Expected: {format_felt_list(constants.VALIDATE_RETDATA)}, " + f"got: {format_felt_list(call_info.retdata)}." + ), + ) + remaining_gas -= call_info.gas_consumed verify_no_calls_to_other_contracts(call_info=call_info, function_name="'validate'") diff --git a/src/starkware/starknet/cli/starknet_cli.py b/src/starkware/starknet/cli/starknet_cli.py index 8a933814..71de6e02 100755 --- a/src/starkware/starknet/cli/starknet_cli.py +++ b/src/starkware/starknet/cli/starknet_cli.py @@ -110,7 +110,12 @@ async def declare(args: argparse.Namespace, command_args: List[str]): await simulate_or_estimate_fee(args=args, tx=declare_tx_for_simulate) return - max_fee = await compute_max_fee(args=args, tx=declare_tx_for_simulate, has_wallet=has_wallet) + max_fee = await compute_max_fee( + args=args, + tx=declare_tx_for_simulate, + has_wallet=has_wallet, + skip_validate=args.skip_validate, + ) tx = await create_declare_tx( args=args, @@ -154,7 +159,12 @@ async def deprecated_declare(args: argparse.Namespace): await simulate_or_estimate_fee(args=args, tx=declare_tx_for_simulate) return - max_fee = await compute_max_fee(args=args, tx=declare_tx_for_simulate, has_wallet=has_wallet) + max_fee = await compute_max_fee( + args=args, + tx=declare_tx_for_simulate, + has_wallet=has_wallet, + skip_validate=args.skip_validate, + ) tx = await create_deprecated_declare_tx( args=args, @@ -234,7 +244,9 @@ async def deploy_with_invoke(args: argparse.Namespace): max_fee=0, call=True, ) - max_fee = await compute_max_fee(args=args, tx=invoke_tx_for_fee_estimation, has_wallet=True) + max_fee = await compute_max_fee( + args=args, tx=invoke_tx_for_fee_estimation, has_wallet=True, skip_validate=False + ) tx, contract_address = await create_invoke_tx_for_deploy( args=args, salt=salt, @@ -289,7 +301,12 @@ async def deploy_account(args: argparse.Namespace, command_args: List[str]): await simulate_or_estimate_fee(args=args, tx=deploy_account_tx_for_simulate) return - max_fee = await compute_max_fee(args=args, tx=deploy_account_tx_for_simulate, has_wallet=True) + max_fee = await compute_max_fee( + args=args, + tx=deploy_account_tx_for_simulate, + has_wallet=True, + skip_validate=args.skip_validate, + ) tx, contract_address = await create_deploy_account_tx( args=args, @@ -360,7 +377,12 @@ async def invoke(args: argparse.Namespace, command_args: List[str]): if args.dry_run: assert has_wallet, "--dry_run can only be used for invocation through an account contract." - max_fee = await compute_max_fee(args=args, tx=invoke_tx_for_simulate, has_wallet=has_wallet) + max_fee = await compute_max_fee( + args=args, + tx=invoke_tx_for_simulate, + has_wallet=has_wallet, + skip_validate=args.skip_validate, + ) tx = await create_invoke_tx( args=args, @@ -847,7 +869,10 @@ def validate_max_fee(max_fee: Optional[int]): async def compute_max_fee( - args: argparse.Namespace, tx: Optional[AccountTransaction], has_wallet: bool + args: argparse.Namespace, + tx: Optional[AccountTransaction], + has_wallet: bool, + skip_validate: bool, ) -> int: """ Returns max_fee argument if passed, and estimates and returns the max fee otherwise. @@ -860,7 +885,7 @@ async def compute_max_fee( max_fee = await compute_max_fee_for_tx( feeder_client=get_feeder_gateway_client(args), tx=as_non_optional(tx), - skip_validate=args.skip_validate, + skip_validate=skip_validate, ) max_fee_eth = float(Web3.fromWei(max_fee, "ether")) diff --git a/src/starkware/starknet/compiler/v1/CMakeLists.txt b/src/starkware/starknet/compiler/v1/CMakeLists.txt index 0709225e..5e89cc83 100644 --- a/src/starkware/starknet/compiler/v1/CMakeLists.txt +++ b/src/starkware/starknet/compiler/v1/CMakeLists.txt @@ -83,7 +83,7 @@ set(CAIRO_COMPILER_ARTIFACTS add_custom_command( OUTPUT "${CAIRO_COMPILER_DUMMY_FILE}" "${CAIRO_COMPILER_FILES}" - COMMAND curl -Lo release-x86_64-unknown-linux-musl.tar.gz https://github.com/starkware-libs/cairo/releases/download/v1.0.0-alpha.5/release-x86_64-unknown-linux-musl.tar.gz + COMMAND curl -Lo release-x86_64-unknown-linux-musl.tar.gz https://github.com/starkware-libs/cairo/releases/download/v1.0.0-alpha.6/release-x86_64-unknown-linux-musl.tar.gz COMMAND tar -xf release-x86_64-unknown-linux-musl.tar.gz COMMAND touch "${CAIRO_COMPILER_DUMMY_FILE}" COMMENT "Downloading cairo compiler." @@ -96,6 +96,9 @@ python_lib(starknet_compile_v1_lib FILES compile.py + mainnet_libfuncs.json + testnet_libfuncs.json + testnet2_libfuncs.json ARTIFACTS "${CAIRO_COMPILER_ARTIFACTS}" diff --git a/src/starkware/starknet/compiler/v1/compile.py b/src/starkware/starknet/compiler/v1/compile.py index 1206f2ef..f10a936e 100644 --- a/src/starkware/starknet/compiler/v1/compile.py +++ b/src/starkware/starknet/compiler/v1/compile.py @@ -11,6 +11,14 @@ DEFAULT_ALLOWED_LIBFUNCS_ARG: List[str] = [] + +def get_allowed_libfuncs_list_file(file_name: str) -> str: + main_dir_path = os.path.dirname(__file__) + file_path = os.path.join(main_dir_path, file_name + ".json") + + return file_path + + if "RUNFILES_DIR" in os.environ: from bazel_tools.tools.python.runfiles import runfiles @@ -25,30 +33,55 @@ STARKNET_COMPILE = os.path.join(os.path.dirname(__file__), "bin", "starknet-compile") +def build_allowed_libfuncs_args( + allowed_libfuncs_list_name: Optional[str] = None, + allowed_libfuncs_list_file: Optional[str] = None, +) -> List[str]: + if allowed_libfuncs_list_name is None and allowed_libfuncs_list_file is None: + return DEFAULT_ALLOWED_LIBFUNCS_ARG + + assert allowed_libfuncs_list_name is None or allowed_libfuncs_list_file is None, ( + "Received too many libfuncs list parameters." + f"allowed_libfuncs_list_name = {allowed_libfuncs_list_name}," + f"allowed_libfuncs_list_file = {allowed_libfuncs_list_file}." + ) + + if allowed_libfuncs_list_name is not None: + return ["--allowed-libfuncs-list-name", allowed_libfuncs_list_name] + + assert allowed_libfuncs_list_file is not None + return [ + "--allowed-libfuncs-list-file", + get_allowed_libfuncs_list_file(allowed_libfuncs_list_file), + ] + + def compile_cairo_to_sierra( - cairo_path: str, allowed_libfuncs_list_name: Optional[str] = None + cairo_path: str, + allowed_libfuncs_list_name: Optional[str] = None, + allowed_libfuncs_list_file: Optional[str] = None, ) -> JsonObject: """ Compiles a Starknet Cairo 1.0 contract; returns the resulting Sierra as json. """ - additional_args = ( - DEFAULT_ALLOWED_LIBFUNCS_ARG - if allowed_libfuncs_list_name is None - else ["--allowed-libfuncs-list-name", allowed_libfuncs_list_name] + additional_args = build_allowed_libfuncs_args( + allowed_libfuncs_list_name=allowed_libfuncs_list_name, + allowed_libfuncs_list_file=allowed_libfuncs_list_file, ) return run_compile_command(command=[STARKNET_COMPILE, cairo_path, *additional_args]) def compile_sierra_to_casm( - sierra_path: str, allowed_libfuncs_list_name: Optional[str] = None + sierra_path: str, + allowed_libfuncs_list_name: Optional[str] = None, + allowed_libfuncs_list_file: Optional[str] = None, ) -> JsonObject: """ Compiles a Starknet Sierra contract; returns the resulting Casm as json. """ - additional_args = ( - DEFAULT_ALLOWED_LIBFUNCS_ARG - if allowed_libfuncs_list_name is None - else ["--allowed-libfuncs-list-name", allowed_libfuncs_list_name] + additional_args = build_allowed_libfuncs_args( + allowed_libfuncs_list_name=allowed_libfuncs_list_name, + allowed_libfuncs_list_file=allowed_libfuncs_list_file, ) return run_compile_command( command=[STARKNET_SIERRA_COMPILE, sierra_path, "--add-pythonic-hints", *additional_args] @@ -56,20 +89,26 @@ def compile_sierra_to_casm( def compile_cairo_to_casm( - cairo_path: str, allowed_libfuncs_list_name: Optional[str] = None + cairo_path: str, + allowed_libfuncs_list_name: Optional[str] = None, + allowed_libfuncs_list_file: Optional[str] = None, ) -> JsonObject: """ Compiles a Starknet Cairo 1.0 contract to Casm; returns the resulting Casm as json. """ raw_sierra = compile_cairo_to_sierra( - cairo_path=cairo_path, allowed_libfuncs_list_name=allowed_libfuncs_list_name + cairo_path=cairo_path, + allowed_libfuncs_list_name=allowed_libfuncs_list_name, + allowed_libfuncs_list_file=allowed_libfuncs_list_file, ) with tempfile.NamedTemporaryFile(mode="w") as sierra_file: json.dump(obj=raw_sierra, fp=sierra_file, indent=2) sierra_file.flush() return compile_sierra_to_casm( - sierra_path=sierra_file.name, allowed_libfuncs_list_name=allowed_libfuncs_list_name + sierra_path=sierra_file.name, + allowed_libfuncs_list_name=allowed_libfuncs_list_name, + allowed_libfuncs_list_file=allowed_libfuncs_list_file, ) @@ -93,7 +132,7 @@ def run_compile_command(command: List[str]) -> JsonObject: if result.returncode != 0: raise StarkException( code=StarknetErrorCode.COMPILATION_FAILED, - message=f"Compilation failed. Error: {result.stderr.decode()}", + message=f"Compilation failed. {result.stderr.decode()}", ) # Read and return the compilation result from the output. diff --git a/src/starkware/starknet/compiler/v1/mainnet_libfuncs.json b/src/starkware/starknet/compiler/v1/mainnet_libfuncs.json new file mode 100644 index 00000000..a189a916 --- /dev/null +++ b/src/starkware/starknet/compiler/v1/mainnet_libfuncs.json @@ -0,0 +1,3 @@ +{ + "allowed_libfuncs": [] +} diff --git a/src/starkware/starknet/compiler/v1/testnet2_libfuncs.json b/src/starkware/starknet/compiler/v1/testnet2_libfuncs.json new file mode 100644 index 00000000..a7829800 --- /dev/null +++ b/src/starkware/starknet/compiler/v1/testnet2_libfuncs.json @@ -0,0 +1,143 @@ +{ + "allowed_libfuncs": [ + "alloc_local", + "array_append", + "array_get", + "array_len", + "array_new", + "array_pop_front", + "array_snapshot_pop_front", + "bitwise", + "bool_and_impl", + "bool_not_impl", + "bool_or_impl", + "bool_to_felt252", + "bool_xor_impl", + "branch_align", + "call_contract_syscall", + "class_hash_const", + "class_hash_to_felt252", + "class_hash_try_from_felt252", + "contract_address_const", + "contract_address_to_felt252", + "contract_address_try_from_felt252", + "deploy_syscall", + "disable_ap_tracking", + "drop", + "dup", + "ec_neg", + "ec_point_from_x_nz", + "ec_point_is_zero", + "ec_point_try_new_nz", + "ec_point_unwrap", + "ec_point_zero", + "ec_state_add", + "ec_state_add_mul", + "ec_state_init", + "ec_state_try_finalize_nz", + "emit_event_syscall", + "enable_ap_tracking", + "enum_init", + "enum_match", + "enum_snapshot_match", + "felt252_add", + "felt252_const", + "felt252_dict_new", + "felt252_dict_read", + "felt252_dict_squash", + "felt252_dict_write", + "felt252_div", + "felt252_is_zero", + "felt252_mul", + "felt252_sub", + "finalize_locals", + "function_call", + "get_builtin_costs", + "get_execution_info_syscall", + "into_box", + "jump", + "library_call_syscall", + "match_nullable", + "null", + "nullable_from_box", + "pedersen", + "redeposit_gas", + "rename", + "replace_class_syscall", + "revoke_ap_tracking", + "send_message_to_l1_syscall", + "snapshot_take", + "storage_address_from_base", + "storage_address_from_base_and_offset", + "storage_address_to_felt252", + "storage_address_try_from_felt252", + "storage_base_address_const", + "storage_base_address_from_felt252", + "storage_read_syscall", + "storage_write_syscall", + "store_local", + "store_temp", + "struct_construct", + "struct_deconstruct", + "struct_snapshot_deconstruct", + "u128_const", + "u128_eq", + "u128_is_zero", + "u128_le", + "u128_lt", + "u128_overflowing_add", + "u128_overflowing_sub", + "u128_safe_divmod", + "u128_to_felt252", + "u128_wide_mul", + "u128s_from_felt252", + "u16_const", + "u16_eq", + "u16_is_zero", + "u16_le", + "u16_lt", + "u16_overflowing_add", + "u16_overflowing_sub", + "u16_safe_divmod", + "u16_to_felt252", + "u16_try_from_felt252", + "u16_wide_mul", + "u32_const", + "u32_eq", + "u32_is_zero", + "u32_le", + "u32_lt", + "u32_overflowing_add", + "u32_overflowing_sub", + "u32_safe_divmod", + "u32_to_felt252", + "u32_try_from_felt252", + "u32_wide_mul", + "u64_const", + "u64_eq", + "u64_is_zero", + "u64_le", + "u64_lt", + "u64_overflowing_add", + "u64_overflowing_sub", + "u64_safe_divmod", + "u64_to_felt252", + "u64_try_from_felt252", + "u64_wide_mul", + "u8_const", + "u8_eq", + "u8_is_zero", + "u8_le", + "u8_lt", + "u8_overflowing_add", + "u8_overflowing_sub", + "u8_safe_divmod", + "u8_to_felt252", + "u8_try_from_felt252", + "u8_wide_mul", + "unbox", + "unwrap_non_zero", + "withdraw_gas", + "withdraw_gas_all" + ] +} diff --git a/src/starkware/starknet/compiler/v1/testnet_libfuncs.json b/src/starkware/starknet/compiler/v1/testnet_libfuncs.json new file mode 100644 index 00000000..e2389d9b --- /dev/null +++ b/src/starkware/starknet/compiler/v1/testnet_libfuncs.json @@ -0,0 +1,129 @@ +{ + "allowed_libfuncs": [ + "alloc_local", + "array_append", + "array_get", + "array_len", + "array_new", + "array_pop_front", + "array_snapshot_pop_front", + "bitwise", + "bool_and_impl", + "bool_not_impl", + "bool_or_impl", + "bool_to_felt252", + "bool_xor_impl", + "branch_align", + "call_contract_syscall", + "class_hash_const", + "class_hash_to_felt252", + "class_hash_try_from_felt252", + "contract_address_const", + "contract_address_to_felt252", + "contract_address_try_from_felt252", + "deploy_syscall", + "disable_ap_tracking", + "drop", + "dup", + "emit_event_syscall", + "enable_ap_tracking", + "enum_init", + "enum_match", + "enum_snapshot_match", + "felt252_add", + "felt252_const", + "felt252_div", + "felt252_is_zero", + "felt252_mul", + "felt252_sub", + "finalize_locals", + "function_call", + "get_builtin_costs", + "get_execution_info_syscall", + "into_box", + "jump", + "library_call_syscall", + "match_nullable", + "null", + "nullable_from_box", + "pedersen", + "redeposit_gas", + "rename", + "replace_class_syscall", + "revoke_ap_tracking", + "send_message_to_l1_syscall", + "snapshot_take", + "storage_address_from_base", + "storage_address_from_base_and_offset", + "storage_address_to_felt252", + "storage_address_try_from_felt252", + "storage_base_address_const", + "storage_base_address_from_felt252", + "storage_read_syscall", + "storage_write_syscall", + "store_local", + "store_temp", + "struct_construct", + "struct_deconstruct", + "struct_snapshot_deconstruct", + "u128_const", + "u128_eq", + "u128_is_zero", + "u128_le", + "u128_lt", + "u128_overflowing_add", + "u128_overflowing_sub", + "u128_safe_divmod", + "u128_to_felt252", + "u128_wide_mul", + "u128s_from_felt252", + "u16_const", + "u16_eq", + "u16_is_zero", + "u16_le", + "u16_lt", + "u16_overflowing_add", + "u16_overflowing_sub", + "u16_safe_divmod", + "u16_to_felt252", + "u16_try_from_felt252", + "u16_wide_mul", + "u32_const", + "u32_eq", + "u32_is_zero", + "u32_le", + "u32_lt", + "u32_overflowing_add", + "u32_overflowing_sub", + "u32_safe_divmod", + "u32_to_felt252", + "u32_try_from_felt252", + "u32_wide_mul", + "u64_const", + "u64_eq", + "u64_is_zero", + "u64_le", + "u64_lt", + "u64_overflowing_add", + "u64_overflowing_sub", + "u64_safe_divmod", + "u64_to_felt252", + "u64_try_from_felt252", + "u64_wide_mul", + "u8_const", + "u8_eq", + "u8_is_zero", + "u8_le", + "u8_lt", + "u8_overflowing_add", + "u8_overflowing_sub", + "u8_safe_divmod", + "u8_to_felt252", + "u8_try_from_felt252", + "u8_wide_mul", + "unbox", + "unwrap_non_zero", + "withdraw_gas", + "withdraw_gas_all" + ] +} diff --git a/src/starkware/starknet/core/os/execution/deprecated_execute_syscalls.cairo b/src/starkware/starknet/core/os/execution/deprecated_execute_syscalls.cairo index 6280e6cb..456dedb2 100644 --- a/src/starkware/starknet/core/os/execution/deprecated_execute_syscalls.cairo +++ b/src/starkware/starknet/core/os/execution/deprecated_execute_syscalls.cairo @@ -654,7 +654,8 @@ func execute_deprecated_syscalls{ ); } -// Deploys a contract. +// Deploys a contract and invokes its constructor. +// Returns the constructor's return data. // // Arguments: // block_context - A global context that is fixed throughout the block. @@ -666,7 +667,9 @@ func deploy_contract{ contract_state_changes: DictAccess*, contract_class_changes: DictAccess*, outputs: OsCarriedOutputs*, -}(block_context: BlockContext*, constructor_execution_context: ExecutionContext*) { +}(block_context: BlockContext*, constructor_execution_context: ExecutionContext*) -> ( + retdata_size: felt, retdata: felt* +) { alloc_locals; local contract_address = constructor_execution_context.execution_info.contract_address; @@ -696,9 +699,7 @@ func deploy_contract{ ); // Invoke the contract constructor. - select_execute_entry_point_func( + return select_execute_entry_point_func( block_context=block_context, execution_context=constructor_execution_context ); - - return (); } diff --git a/src/starkware/starknet/core/os/execution/execute_syscalls.cairo b/src/starkware/starknet/core/os/execution/execute_syscalls.cairo index f3767b8e..3267a6f4 100644 --- a/src/starkware/starknet/core/os/execution/execute_syscalls.cairo +++ b/src/starkware/starknet/core/os/execution/execute_syscalls.cairo @@ -1,23 +1,31 @@ from starkware.cairo.common.dict import dict_read, dict_update from starkware.cairo.common.dict_access import DictAccess from starkware.cairo.common.math import assert_lt, assert_nn, assert_not_zero +from starkware.cairo.common.memcpy import memcpy from starkware.cairo.common.segments import relocate_segment from starkware.starknet.common.new_syscalls import ( CALL_CONTRACT_SELECTOR, + DEPLOY_SELECTOR, EMIT_EVENT_SELECTOR, GET_EXECUTION_INFO_SELECTOR, LIBRARY_CALL_SELECTOR, + REPLACE_CLASS_SELECTOR, + SEND_MESSAGE_TO_L1_SELECTOR, STORAGE_READ_SELECTOR, STORAGE_WRITE_SELECTOR, CallContractRequest, CallContractResponse, + DeployRequest, + DeployResponse, EmitEventRequest, ExecutionInfo, FailureReason, GetExecutionInfoResponse, LibraryCallRequest, + ReplaceClassRequest, RequestHeader, ResponseHeader, + SendMessageToL1Request, StorageReadRequest, StorageReadResponse, StorageWriteRequest, @@ -26,20 +34,31 @@ from starkware.starknet.core.os.block_context import BlockContext from starkware.starknet.core.os.builtins import BuiltinPointers from starkware.starknet.core.os.constants import ( CALL_CONTRACT_GAS_COST, + CONSTRUCTOR_ENTRY_POINT_SELECTOR, + DEPLOY_GAS_COST, EMIT_EVENT_GAS_COST, + ENTRY_POINT_TYPE_CONSTRUCTOR, ENTRY_POINT_TYPE_EXTERNAL, ERROR_OUT_OF_GAS, GET_EXECUTION_INFO_GAS_COST, LIBRARY_CALL_GAS_COST, + REPLACE_CLASS_GAS_COST, + SEND_MESSAGE_TO_L1_GAS_COST, STORAGE_READ_GAS_COST, STORAGE_WRITE_GAS_COST, SYSCALL_BASE_GAS_COST, ) +from starkware.starknet.core.os.contract_address.contract_address import get_contract_address from starkware.starknet.core.os.execution.deprecated_execute_entry_point import ( select_execute_entry_point_func, ) +from starkware.starknet.core.os.execution.deprecated_execute_syscalls import deploy_contract from starkware.starknet.core.os.execution.execute_entry_point import ExecutionContext -from starkware.starknet.core.os.output import OsCarriedOutputs +from starkware.starknet.core.os.output import ( + MessageToL1Header, + OsCarriedOutputs, + os_carried_outputs_new, +) from starkware.starknet.core.os.state import StateEntry // Executes the system calls in syscall_ptr. @@ -111,10 +130,39 @@ func execute_syscalls{ ); } - assert selector = EMIT_EVENT_SELECTOR; - reduce_syscall_gas_and_write_response_header( - total_gas_cost=EMIT_EVENT_GAS_COST, request_struct_size=EmitEventRequest.SIZE - ); + if (selector == EMIT_EVENT_SELECTOR) { + // Skip as long as the block hash is not calculated by the OS. + reduce_syscall_gas_and_write_response_header( + total_gas_cost=EMIT_EVENT_GAS_COST, request_struct_size=EmitEventRequest.SIZE + ); + return execute_syscalls( + block_context=block_context, + execution_context=execution_context, + syscall_ptr_end=syscall_ptr_end, + ); + } + + if (selector == DEPLOY_SELECTOR) { + execute_deploy(block_context=block_context, caller_execution_context=execution_context); + return execute_syscalls( + block_context=block_context, + execution_context=execution_context, + syscall_ptr_end=syscall_ptr_end, + ); + } + + if (selector == REPLACE_CLASS_SELECTOR) { + execute_replace_class(contract_address=execution_context.execution_info.contract_address); + return execute_syscalls( + block_context=block_context, + execution_context=execution_context, + syscall_ptr_end=syscall_ptr_end, + ); + } + + assert selector = SEND_MESSAGE_TO_L1_SELECTOR; + + execute_send_message_to_l1(contract_address=execution_context.execution_info.contract_address); return execute_syscalls( block_context=block_context, execution_context=execution_context, @@ -248,7 +296,7 @@ func contract_call_helper{ ) actual = memory.get_range(addr=ids.retdata, size=ids.retdata_size) - assert expected == actual, f'Return value mismatch expected={expected}, actual={actual}.' + assert expected == actual, f'Return value mismatch; expected={expected}, actual={actual}.' %} // Write the response. @@ -260,9 +308,113 @@ func contract_call_helper{ return (); } +// Deploys a contract and invokes its constructor. +func execute_deploy{ + range_check_ptr, + syscall_ptr: felt*, + builtin_ptrs: BuiltinPointers*, + contract_state_changes: DictAccess*, + contract_class_changes: DictAccess*, + outputs: OsCarriedOutputs*, +}(block_context: BlockContext*, caller_execution_context: ExecutionContext*) { + alloc_locals; + let request = cast(syscall_ptr + RequestHeader.SIZE, DeployRequest*); + let (success, remaining_gas) = reduce_syscall_base_gas( + specific_base_gas_cost=DEPLOY_GAS_COST, request_struct_size=DeployRequest.SIZE + ); + if (success == 0) { + // Not enough gas to execute the syscall. + return (); + } + + local caller_execution_info: ExecutionInfo* = caller_execution_context.execution_info; + local caller_address = caller_execution_info.contract_address; + + // Verify deploy_from_zero is either 0 (FALSE) or 1 (TRUE). + tempvar deploy_from_zero = request.deploy_from_zero; + assert deploy_from_zero * (deploy_from_zero - 1) = 0; + // Set deployer_address to 0 if request.deploy_from_zero is TRUE. + let deployer_address = (1 - deploy_from_zero) * caller_address; + + tempvar constructor_calldata_start = request.constructor_calldata_start; + tempvar constructor_calldata_size = request.constructor_calldata_end - + constructor_calldata_start; + let hash_ptr = builtin_ptrs.pedersen; + with hash_ptr { + let (contract_address) = get_contract_address( + salt=request.contract_address_salt, + class_hash=request.class_hash, + constructor_calldata_size=constructor_calldata_size, + constructor_calldata=constructor_calldata_start, + deployer_address=deployer_address, + ); + } + tempvar builtin_ptrs = new BuiltinPointers( + pedersen=hash_ptr, + range_check=builtin_ptrs.range_check, + ecdsa=builtin_ptrs.ecdsa, + bitwise=builtin_ptrs.bitwise, + ec_op=builtin_ptrs.ec_op, + poseidon=builtin_ptrs.poseidon, + segment_arena=builtin_ptrs.segment_arena, + ); + + tempvar constructor_execution_context = new ExecutionContext( + entry_point_type=ENTRY_POINT_TYPE_CONSTRUCTOR, + class_hash=request.class_hash, + calldata_size=constructor_calldata_size, + calldata=constructor_calldata_start, + execution_info=new ExecutionInfo( + block_info=caller_execution_info.block_info, + tx_info=caller_execution_info.tx_info, + caller_address=caller_address, + contract_address=contract_address, + selector=CONSTRUCTOR_ENTRY_POINT_SELECTOR, + ), + deprecated_tx_info=caller_execution_context.deprecated_tx_info, + ); + + with remaining_gas { + let (retdata_size, retdata) = deploy_contract( + block_context=block_context, constructor_execution_context=constructor_execution_context + ); + } + + let response_header = cast(syscall_ptr, ResponseHeader*); + // Advance syscall pointer to the response body. + let syscall_ptr = syscall_ptr + ResponseHeader.SIZE; + + // Write the response header. + assert [response_header] = ResponseHeader(gas=remaining_gas, failure_flag=0); + + let response = cast(syscall_ptr, DeployResponse*); + // Advance syscall pointer to the next syscall. + let syscall_ptr = syscall_ptr + DeployResponse.SIZE; + + %{ + # Check that the actual return value matches the expected one. + expected = memory.get_range( + addr=ids.response.constructor_retdata_start, + size=ids.response.constructor_retdata_end - ids.response.constructor_retdata_start, + ) + actual = memory.get_range(addr=ids.retdata, size=ids.retdata_size) + assert expected == actual, f'Return value mismatch; expected={expected}, actual={actual}.' + %} + + // Write the response. + relocate_segment(src_ptr=response.constructor_retdata_start, dest_ptr=retdata); + assert [response] = DeployResponse( + contract_address=contract_address, + constructor_retdata_start=retdata, + constructor_retdata_end=retdata + retdata_size, + ); + + return (); +} + // Reads a value from the current contract's storage. func execute_storage_read{range_check_ptr, syscall_ptr: felt*, contract_state_changes: DictAccess*}( - contract_address + contract_address: felt ) { alloc_locals; let request = cast(syscall_ptr + RequestHeader.SIZE, StorageReadRequest*); @@ -312,7 +464,7 @@ func execute_storage_read{range_check_ptr, syscall_ptr: felt*, contract_state_ch // Writes a value to the current contract's storage. func execute_storage_write{ range_check_ptr, syscall_ptr: felt*, contract_state_changes: DictAccess* -}(contract_address) { +}(contract_address: felt) { alloc_locals; let request = cast(syscall_ptr + RequestHeader.SIZE, StorageWriteRequest*); @@ -382,6 +534,75 @@ func execute_get_execution_info{range_check_ptr, syscall_ptr: felt*}( return (); } +// Replaces the class. +func execute_replace_class{ + range_check_ptr, syscall_ptr: felt*, contract_state_changes: DictAccess* +}(contract_address: felt) { + alloc_locals; + let request = cast(syscall_ptr + RequestHeader.SIZE, ReplaceClassRequest*); + + // Reduce gas. + let success = reduce_syscall_gas_and_write_response_header( + total_gas_cost=REPLACE_CLASS_GAS_COST, request_struct_size=ReplaceClassRequest.SIZE + ); + if (success == 0) { + // Not enough gas to execute the syscall. + return (); + } + + let class_hash = request.class_hash; + + local state_entry: StateEntry*; + %{ + # Fetch a state_entry in this hint and validate it in the update at the end + # of this function. + ids.state_entry = __dict_manager.get_dict(ids.contract_state_changes)[ids.contract_address] + %} + + tempvar new_state_entry = new StateEntry( + class_hash=class_hash, storage_ptr=state_entry.storage_ptr, nonce=state_entry.nonce + ); + + dict_update{dict_ptr=contract_state_changes}( + key=contract_address, + prev_value=cast(state_entry, felt), + new_value=cast(new_state_entry, felt), + ); + + return (); +} + +// Sends a message to L1. +func execute_send_message_to_l1{range_check_ptr, syscall_ptr: felt*, outputs: OsCarriedOutputs*}( + contract_address: felt +) { + alloc_locals; + let request = cast(syscall_ptr + RequestHeader.SIZE, SendMessageToL1Request*); + let success = reduce_syscall_gas_and_write_response_header( + total_gas_cost=SEND_MESSAGE_TO_L1_GAS_COST, request_struct_size=SendMessageToL1Request.SIZE + ); + if (success == 0) { + // Not enough gas to execute the syscall. + return (); + } + + tempvar payload_start = request.payload_start; + tempvar payload_size = request.payload_end - payload_start; + + assert [outputs.messages_to_l1] = MessageToL1Header( + from_address=contract_address, to_address=request.to_address, payload_size=payload_size + ); + memcpy( + dst=outputs.messages_to_l1 + MessageToL1Header.SIZE, src=payload_start, len=payload_size + ); + let (outputs) = os_carried_outputs_new( + messages_to_l1=outputs.messages_to_l1 + MessageToL1Header.SIZE + payload_size, + messages_to_l2=outputs.messages_to_l2, + ); + + return (); +} + // Reduces the total amount of gas required for the current syscall and writes the response header. // In case of out-of-gas failure, writes the FailureReason object to syscall_ptr. // Returns 1 if the gas reduction succeeded and 0 otherwise. diff --git a/src/starkware/starknet/core/os/program_hash.json b/src/starkware/starknet/core/os/program_hash.json index e2538571..a7e87edf 100644 --- a/src/starkware/starknet/core/os/program_hash.json +++ b/src/starkware/starknet/core/os/program_hash.json @@ -1,3 +1,3 @@ { - "program_hash": "0x1c3dd8ba84f2895f749e8286cf26d813e7a51d7b65565a75d8a8da33f1aeae" + "program_hash": "0x6c8e862f81a33d0940ae371bbac8d9e8cd1f0128f947f3b1ba031a1c1e9b90a" } diff --git a/src/starkware/starknet/definitions/constants.py b/src/starkware/starknet/definitions/constants.py index 0ac178cf..98eb9b0c 100644 --- a/src/starkware/starknet/definitions/constants.py +++ b/src/starkware/starknet/definitions/constants.py @@ -97,6 +97,9 @@ # The (empirical) L1 gas cost of each Cairo step. N_STEPS_FEE_WEIGHT = 0.01 +# Expected return values of a 'validate' entry point. +VALIDATE_RETDATA = [from_bytes(b"VALID")] + class GasCost(Enum): """ diff --git a/src/starkware/starknet/definitions/error_codes.py b/src/starkware/starknet/definitions/error_codes.py index 9e4c7a60..4f090f8b 100644 --- a/src/starkware/starknet/definitions/error_codes.py +++ b/src/starkware/starknet/definitions/error_codes.py @@ -18,6 +18,7 @@ class StarknetErrorCode(ErrorCode): FEE_TRANSFER_FAILURE = auto() INVALID_BLOCK_NUMBER = auto() INVALID_BLOCK_TIMESTAMP = auto() + INVALID_COMPILED_CLASS = auto() INVALID_COMPILED_CLASS_HASH = auto() INVALID_CONTRACT_CLASS = auto() INVALID_CONTRACT_CLASS_VERSION = auto() @@ -129,6 +130,7 @@ class StarknetErrorCode(ErrorCode): StarknetErrorCode.CONTRACT_ADDRESS_UNAVAILABLE, StarknetErrorCode.ENTRY_POINT_NOT_FOUND_IN_CONTRACT, StarknetErrorCode.FEE_TRANSFER_FAILURE, + StarknetErrorCode.INVALID_COMPILED_CLASS, StarknetErrorCode.INVALID_RETURN_DATA, StarknetErrorCode.INVALID_TRANSACTION_VERSION, StarknetErrorCode.L1_TO_L2_MESSAGE_INSUFFICIENT_FEE, diff --git a/src/starkware/starknet/services/api/contract_class/contract_class.py b/src/starkware/starknet/services/api/contract_class/contract_class.py index 2f1183bf..7706e8ea 100644 --- a/src/starkware/starknet/services/api/contract_class/contract_class.py +++ b/src/starkware/starknet/services/api/contract_class/contract_class.py @@ -209,9 +209,19 @@ def parse_pythonic_hints(self, data: Dict[str, Any], many: bool, **kwargs) -> Di The returned CairoHint object takes empty "accessible_scopes" and "flow_tracking_data" values as these are only relevant to Cairo 0 programs. """ - assert "program" not in data, ( - "Unsupported compiled class format. " - "Cairo 1.0 compiled class must not contain the attribute `program`." + # Invalidate 0.11.0-pre-release compiled classes. + stark_assert( + "program" not in data, + code=StarknetErrorCode.INVALID_COMPILED_CLASS, + message="Unsupported compiled class format. " + "Cairo 1.0 compiled class must not contain the attribute `program`.", + ) + # Invalidate compiled classes without pythonic hints. + stark_assert( + "pythonic_hints" in data, + code=StarknetErrorCode.INVALID_COMPILED_CLASS, + message="Unsupported compiled class format. " + "Cairo 1.0 compiled class must contain the attribute `pythonic_hints`.", ) pythonic_hints = data["pythonic_hints"] diff --git a/src/starkware/starknet/services/api/contract_class/contract_class_utils.py b/src/starkware/starknet/services/api/contract_class/contract_class_utils.py index a25085fd..b389dc26 100644 --- a/src/starkware/starknet/services/api/contract_class/contract_class_utils.py +++ b/src/starkware/starknet/services/api/contract_class/contract_class_utils.py @@ -10,7 +10,9 @@ def compile_contract_class( - contract_class: ContractClass, allowed_libfuncs_list_name: Optional[str] = None + contract_class: ContractClass, + allowed_libfuncs_list_name: Optional[str] = None, + allowed_libfuncs_list_file: Optional[str] = None, ) -> CompiledClass: """ Compiles a contract class to a compiled class. @@ -31,7 +33,9 @@ def compile_contract_class( # Compile the Sierra file. casm_from_compiled_sierra = compile_sierra_to_casm( - sierra_path=temp_sierra_file_name, allowed_libfuncs_list_name=allowed_libfuncs_list_name + sierra_path=temp_sierra_file_name, + allowed_libfuncs_list_name=allowed_libfuncs_list_name, + allowed_libfuncs_list_file=allowed_libfuncs_list_file, ) # Parse the resultant Casm file. diff --git a/src/starkware/starknet/services/api/contract_class/contracts/test_contract_cairo1.cairo b/src/starkware/starknet/services/api/contract_class/contracts/test_contract_cairo1.cairo index d864c1cc..6e3bc98f 100644 --- a/src/starkware/starknet/services/api/contract_class/contracts/test_contract_cairo1.cairo +++ b/src/starkware/starknet/services/api/contract_class/contracts/test_contract_cairo1.cairo @@ -3,6 +3,7 @@ mod TestContract { use starknet::storage_read_syscall; use starknet::storage_write_syscall; use starknet::syscalls::emit_event_syscall; + use starknet::syscalls::send_message_to_l1_syscall; use starknet::StorageAddress; use starknet::ContractAddress; use starknet::storage_access::storage_base_address_from_felt252; @@ -14,6 +15,7 @@ mod TestContract { use array::SpanTrait; use array::ArrayTrait; use box::BoxTrait; + use dict::Felt252DictTrait; const UNEXPECTED_ERROR: felt252 = 'UNEXPECTED ERROR'; @@ -91,6 +93,21 @@ mod TestContract { emit_event_syscall(keys.span(), data.span()).unwrap_syscall(); } + #[external] + fn test_send_message_to_l1(to_address: felt252, payload: Array::) { + send_message_to_l1_syscall(to_address, payload.span()).unwrap_syscall(); + } + + #[external] + fn test_emit_simple_event( + argument: felt252, my_array: Array::, another_argument: felt252 + ) { + simple_event(argument, my_array); + } + + #[event] + fn simple_event(argument: felt252, my_array: Array::) {} + #[external] fn test_call_contract( contract_address: ContractAddress, entry_point_selector: felt252, calldata: Array:: @@ -115,5 +132,22 @@ mod TestContract { 'success' } + /// Tests the segment arena builtin, by creating dictionaries (`felt252_dict_new()` and + /// `squash()` use the segment arena builtin). + /// + /// Expected return value: 200. + #[external] + fn test_segment_arena() -> felt252 { + let mut x = felt252_dict_new::(); + let mut y = felt252_dict_new::(); + x.insert(0, 100); + y.insert(1, 200); + // x.get(1) returns 0 (the default value), y.get(1) returns 200. + let z = x.get(1) + y.get(1); + y.squash(); + x.squash(); + z + } + } diff --git a/src/starkware/starknet/solidity/StarknetMessaging.sol b/src/starkware/starknet/solidity/StarknetMessaging.sol index e7c84351..53414991 100644 --- a/src/starkware/starknet/solidity/StarknetMessaging.sol +++ b/src/starkware/starknet/solidity/StarknetMessaging.sol @@ -112,6 +112,7 @@ contract StarknetMessaging is IStarknetMessaging { uint256 selector, uint256[] calldata payload ) external payable override returns (bytes32, uint256) { + require(msg.value > 0, "L1_MSG_FEE_MUST_BE_GREATER_THAN_0"); require(msg.value <= getMaxL1MsgFee(), "MAX_L1_MSG_FEE_EXCEEDED"); uint256 nonce = l1ToL2MessageNonce(); NamedStorage.setUintValue(L1L2_MESSAGE_NONCE_TAG, nonce + 1); diff --git a/src/starkware/starknet/testing/mock_starknet_messaging_test.py b/src/starkware/starknet/testing/mock_starknet_messaging_test.py index 4ca98ad1..237d22e2 100644 --- a/src/starkware/starknet/testing/mock_starknet_messaging_test.py +++ b/src/starkware/starknet/testing/mock_starknet_messaging_test.py @@ -46,8 +46,12 @@ def test_mock_consume_message_to_l2(eth_test_utils, mock_starknet_contract): msg_hash = msg.get_hash() assert mock_starknet_contract.l1ToL2Messages.call(msg_hash) == 0 - mock_starknet_contract.sendMessageToL2.transact(l2_address, selector, payload) - assert mock_starknet_contract.l1ToL2Messages.call(msg_hash) == 1 + # Set fee as it is mandatory to be greater than 0. + transact_args = {"value": 4} + mock_starknet_contract.sendMessageToL2.transact( + l2_address, selector, payload, transact_args=transact_args + ) + assert mock_starknet_contract.l1ToL2Messages.call(msg_hash) == transact_args["value"] + 1 mock_starknet_contract.mockConsumeMessageToL2.transact( int(l1_address, 16), diff --git a/src/starkware/starknet/testing/postman_test.py b/src/starkware/starknet/testing/postman_test.py index c4f8cb5b..c4a7f38a 100644 --- a/src/starkware/starknet/testing/postman_test.py +++ b/src/starkware/starknet/testing/postman_test.py @@ -36,11 +36,14 @@ async def test_postman_l1_to_l2_positive_flow( nonce = postman.mock_starknet_messaging_contract.l1ToL2MessageNonce.call() + # Set fee as it is mandatory to be greater than 0. + transact_args = {"value": 4} + postman.mock_starknet_messaging_contract.sendMessageToL2.transact( - test_contract.contract_address, selector, payload1 + test_contract.contract_address, selector, payload1, transact_args=transact_args ) postman.mock_starknet_messaging_contract.sendMessageToL2.transact( - test_contract.contract_address, selector, payload1 + test_contract.contract_address, selector, payload1, transact_args=transact_args ) msg_hashes = [ @@ -55,7 +58,10 @@ async def test_postman_l1_to_l2_positive_flow( ] for msg_hash in msg_hashes: - assert postman.mock_starknet_messaging_contract.l1ToL2Messages.call(msg_hash) == 1 + assert ( + postman.mock_starknet_messaging_contract.l1ToL2Messages.call(msg_hash) + == transact_args["value"] + 1 + ) await postman.flush() for msg_hash in msg_hashes: assert postman.mock_starknet_messaging_contract.l1ToL2Messages.call(msg_hash) == 0 @@ -70,7 +76,7 @@ async def test_postman_l1_to_l2_positive_flow( nonce += 2 assert nonce == postman.mock_starknet_messaging_contract.l1ToL2MessageNonce.call() postman.mock_starknet_messaging_contract.sendMessageToL2.transact( - test_contract.contract_address, selector, payload2 + test_contract.contract_address, selector, payload2, transact_args=transact_args ) msg_hash2 = StarknetMessageToL2( @@ -81,7 +87,10 @@ async def test_postman_l1_to_l2_positive_flow( nonce=nonce, ).get_hash() - assert postman.mock_starknet_messaging_contract.l1ToL2Messages.call(msg_hash2) == 1 + assert ( + postman.mock_starknet_messaging_contract.l1ToL2Messages.call(msg_hash2) + == transact_args["value"] + 1 + ) await postman.flush() assert postman.mock_starknet_messaging_contract.l1ToL2Messages.call(msg_hash2) == 0 @@ -93,13 +102,18 @@ async def test_postman_l1_to_l2_positive_flow( async def test_postman_l1_to_l2_another_mock_starknet_messaging_contract( postman: Postman, eth_test_utils: EthTestUtils ): + # Set fee as it is mandatory to be greater than 0. + transact_args = {"value": 4} + other_messaging_contract = eth_test_utils.accounts[0].deploy(MockStarknetMessaging, 0) INVALID_L2_ADDRESS = 0 INVALID_SELECTOR = 2 # This message is sent into another StarknetMessaging contract and therefore shouldn't be # proccessed by postman. If it will be proccessed the test will fail because the address and # selector are invalid. - other_messaging_contract.sendMessageToL2.transact(INVALID_L2_ADDRESS, INVALID_SELECTOR, [3, 4]) + other_messaging_contract.sendMessageToL2.transact( + INVALID_L2_ADDRESS, INVALID_SELECTOR, [3, 4], transact_args=transact_args + ) await postman.flush()