diff --git a/etherlink/tezt/lib/eth_cli.ml b/etherlink/tezt/lib/eth_cli.ml index 7cafad6ce5036299f530c090f2977dbc2df5b41b..5958ecb8ffbdcf69b9311ef1e2d3874b6810c225 100644 --- a/etherlink/tezt/lib/eth_cli.ml +++ b/etherlink/tezt/lib/eth_cli.ml @@ -99,7 +99,7 @@ let check_abi ~label () = let* abi_list = spawn_command_and_read_string ["abi:list"] in return (String.split_on_char '\n' abi_list |> List.mem label) -let deploy ~source_private_key ~endpoint ~abi ~bin = +let deploy ?args ~source_private_key ~endpoint ~abi ~bin () = let decode json = let open JSON in let contract_address = @@ -109,16 +109,17 @@ let deploy ~source_private_key ~endpoint ~abi ~bin = Some (contract_address, tx_hash) in spawn_command_and_read_json - [ - "contract:deploy"; - "--pk"; - source_private_key; - "--abi"; - abi; - "--network"; - endpoint; - bin; - ] + ([ + "contract:deploy"; + "--pk"; + source_private_key; + "--abi"; + abi; + "--network"; + endpoint; + bin; + ] + @ Cli_arg.optional_arg "args" Fun.id args) decode let contract_send ?(expect_failure = false) ~source_private_key ~endpoint diff --git a/etherlink/tezt/lib/eth_cli.mli b/etherlink/tezt/lib/eth_cli.mli index e026e68ee6e61bc66d7b772e42727468810567f6..f17b7a56a91f8217453f4f0542b9e21086d01792 100644 --- a/etherlink/tezt/lib/eth_cli.mli +++ b/etherlink/tezt/lib/eth_cli.mli @@ -74,7 +74,7 @@ val check_abi : label:string -> unit -> bool Lwt.t (** [show_abi] ~label () returns the ABI registered in the client. *) val show_abi : label:string -> unit -> string Lwt.t -(** [deploy ~source_private_key ~endpoint ~abi ~bin] crafts and sign a +(** [deploy ?args ~source_private_key ~endpoint ~abi ~bin ()] crafts and sign a transaction deploying [bin] whose interface [abi] is registered in the client, and sends the raw transaction to the JSON-API server listening at [endpoint]. [bin] is a path to the binary file, and [abi] is the label used @@ -82,10 +82,12 @@ val show_abi : label:string -> unit -> string Lwt.t Returns a pair [(address, tx_hash)]. *) val deploy : + ?args:string -> source_private_key:string -> endpoint:string -> abi:string -> bin:string -> + unit -> (string * string) Lwt.t (** [contract_send ~source_private_key ~endpoint ~abi_label ~address diff --git a/etherlink/tezt/lib/helpers.ml b/etherlink/tezt/lib/helpers.ml index df788a813105e9e0169c271424f580b0bc1798f8..7786613b6ed0e2a200d9b8d8d60cf1f771ca9146 100644 --- a/etherlink/tezt/lib/helpers.ml +++ b/etherlink/tezt/lib/helpers.ml @@ -41,6 +41,10 @@ let leftPad32 s = let add_0x s = "0x" ^ s +let remove_0x s = + if String.starts_with ~prefix:"0x" s then String.sub s 2 (String.length s - 2) + else s + let mapping_position index map_position = Tezos_crypto.Hacl.Hash.Keccak_256.digest (Hex.to_bytes diff --git a/etherlink/tezt/lib/helpers.mli b/etherlink/tezt/lib/helpers.mli index 2ef6fb2441f7c25f42e15f513808fc0b6c9e37be..9b05c3a922a208468cf9b4336d62ab99c7adf678 100644 --- a/etherlink/tezt/lib/helpers.mli +++ b/etherlink/tezt/lib/helpers.mli @@ -35,6 +35,9 @@ val u16_to_bytes : int -> string (** [add_0x s] will add the hexa prefix `0x` before the given string. *) val add_0x : string -> string +(** [remove_0x s] removes the [0x] prefix in [s] if it is present. *) +val remove_0x : string -> string + (** [mapping_position key map_position] computes the storage position for a value in a mapping given its [key] and the position of the map itself. diff --git a/etherlink/tezt/lib/solidity_contracts.ml b/etherlink/tezt/lib/solidity_contracts.ml index 2331fcc8267d19d079633e7c380b4213b38a3fa4..21c170c1efdf88cac572cf29cb6788ab23470fb6 100644 --- a/etherlink/tezt/lib/solidity_contracts.ml +++ b/etherlink/tezt/lib/solidity_contracts.ml @@ -394,3 +394,11 @@ let batcher () = ~label:"batcher" ~contract:"Batcher" ~evm_version:"shanghai" + +let reentrancy_test () = + compile_contract + ~source: + "etherlink/kernel_evm/evm_execution/tests/contracts/src/ReentrancyTester.sol" + ~label:"reentrancy_tester" + ~contract:"ReentrancyTester" + ~evm_version:"shanghai" diff --git a/etherlink/tezt/tests/eth_call.ml b/etherlink/tezt/tests/eth_call.ml index 0ee6cfb41cb27be0f06043d3ed58047ff810e518..fa7955aa8808b12324beb9dbac860f60af419383 100644 --- a/etherlink/tezt/tests/eth_call.ml +++ b/etherlink/tezt/tests/eth_call.ml @@ -57,12 +57,11 @@ let test_call_state_override_balance = (* Deploy the contract. *) let* contract, _tx_hash = send_transaction_to_sequencer - (fun () -> - Eth_cli.deploy - ~source_private_key:Eth_account.bootstrap_accounts.(0).private_key - ~endpoint:(Evm_node.endpoint sequencer) - ~abi:constant.abi - ~bin:constant.bin) + (Eth_cli.deploy + ~source_private_key:Eth_account.bootstrap_accounts.(0).private_key + ~endpoint:(Evm_node.endpoint sequencer) + ~abi:constant.abi + ~bin:constant.bin) sequencer in let caller_address = "0x0123456789012345678901234567890123456789" in @@ -123,12 +122,11 @@ let test_call_state_override_code = (* Deploy the contract. *) let* contract, _tx_hash = send_transaction_to_sequencer - (fun () -> - Eth_cli.deploy - ~source_private_key:Eth_account.bootstrap_accounts.(0).private_key - ~endpoint:(Evm_node.endpoint sequencer) - ~abi:constant.abi - ~bin:constant.bin) + (Eth_cli.deploy + ~source_private_key:Eth_account.bootstrap_accounts.(0).private_key + ~endpoint:(Evm_node.endpoint sequencer) + ~abi:constant.abi + ~bin:constant.bin) sequencer in let bytecode_accessor = @@ -189,12 +187,11 @@ let test_call_state_override_nonce = (* Deploy the contract. *) let* contract, _tx_hash = send_transaction_to_sequencer - (fun () -> - Eth_cli.deploy - ~source_private_key:Eth_account.bootstrap_accounts.(0).private_key - ~endpoint:(Evm_node.endpoint sequencer) - ~abi:factory.abi - ~bin:factory.bin) + (Eth_cli.deploy + ~source_private_key:Eth_account.bootstrap_accounts.(0).private_key + ~endpoint:(Evm_node.endpoint sequencer) + ~abi:factory.abi + ~bin:factory.bin) sequencer in @@ -254,12 +251,11 @@ let test_call_state_override_state_diff = (* Deploy the contract. *) let* contract, _tx_hash = send_transaction_to_sequencer - (fun () -> - Eth_cli.deploy - ~source_private_key:Eth_account.bootstrap_accounts.(0).private_key - ~endpoint:(Evm_node.endpoint sequencer) - ~abi:constant.abi - ~bin:constant.bin) + (Eth_cli.deploy + ~source_private_key:Eth_account.bootstrap_accounts.(0).private_key + ~endpoint:(Evm_node.endpoint sequencer) + ~abi:constant.abi + ~bin:constant.bin) sequencer in @@ -356,12 +352,11 @@ let test_call_state_override_state = (* Deploy the contract. *) let* contract, _tx_hash = send_transaction_to_sequencer - (fun () -> - Eth_cli.deploy - ~source_private_key:Eth_account.bootstrap_accounts.(0).private_key - ~endpoint:(Evm_node.endpoint sequencer) - ~abi:constant.abi - ~bin:constant.bin) + (Eth_cli.deploy + ~source_private_key:Eth_account.bootstrap_accounts.(0).private_key + ~endpoint:(Evm_node.endpoint sequencer) + ~abi:constant.abi + ~bin:constant.bin) sequencer in @@ -466,12 +461,11 @@ let test_call_state_override_state_empty = (* Deploy the contract. *) let* contract, _tx_hash = send_transaction_to_sequencer - (fun () -> - Eth_cli.deploy - ~source_private_key:Eth_account.bootstrap_accounts.(0).private_key - ~endpoint:(Evm_node.endpoint sequencer) - ~abi:constant.abi - ~bin:constant.bin) + (Eth_cli.deploy + ~source_private_key:Eth_account.bootstrap_accounts.(0).private_key + ~endpoint:(Evm_node.endpoint sequencer) + ~abi:constant.abi + ~bin:constant.bin) sequencer in diff --git a/etherlink/tezt/tests/evm_rollup.ml b/etherlink/tezt/tests/evm_rollup.ml index ae643ab0332cfe7f23e9136c980d14cb103b7718..b22bb2b2b0a54b0a4c16cbf20860907090948e78 100644 --- a/etherlink/tezt/tests/evm_rollup.ml +++ b/etherlink/tezt/tests/evm_rollup.ml @@ -712,7 +712,7 @@ let deploy ~contract ~sender full_evm_setup = Eth_cli.update_abi ~label:contract.label ~abi:contract.abi () else Eth_cli.add_abi ~label:contract.label ~abi:contract.abi () in - let send_deploy () = + let send_deploy = Eth_cli.deploy ~source_private_key:sender.Eth_account.private_key ~endpoint:evm_node_endpoint diff --git a/etherlink/tezt/tests/evm_sequencer.ml b/etherlink/tezt/tests/evm_sequencer.ml index c7c1759a3dbc9951f2a50563acf32d2916b868ab..d8af3dd843c6645f702e4f36e3787371c753ff38 100644 --- a/etherlink/tezt/tests/evm_sequencer.ml +++ b/etherlink/tezt/tests/evm_sequencer.ml @@ -154,12 +154,17 @@ let send_deposit_to_delayed_inbox ~amount ~l1_contracts ~depositor ~receiver let* _ = next_rollup_node_level ~sc_rollup_node ~client in unit -let send_fa_deposit_to_delayed_inbox ~amount ~l1_contracts ~depositor ~receiver - ~sc_rollup_node ~sc_rollup_address client = +let send_fa_deposit_to_delayed_inbox ?(proxy = "") ~amount ~l1_contracts + ~depositor ~receiver ~sc_rollup_node ~sc_rollup_address client = let* () = Client.transfer ~entrypoint:"set" - ~arg:(sf "Pair %S (Pair (Right (Right %s)) 0)" sc_rollup_address receiver) + ~arg: + (sf + "Pair %S (Pair (Right (Right %s%s)) 0)" + sc_rollup_address + receiver + (Helpers.remove_0x proxy)) ~amount:Tez.zero ~giver:depositor.Account.public_key_hash ~receiver:l1_contracts.ticket_router_tester @@ -1746,6 +1751,100 @@ let test_fa_withdrawal_is_included = unit +let test_fa_reentrant_deposit_reverts = + register_all + ~da_fee:Wei.one + ~tags: + [ + "evm"; + "sequencer"; + "delayed_outbox"; + "fa_deposit"; + "enabled"; + "reentrancy"; + ] + ~title:"FA Deposit cannot do reentrancy" + ~enable_fa_bridge:true + ~kernels:[Kernel.Latest] + ~time_between_blocks:Nothing + ~additional_uses:[Constant.octez_codec] + @@ fun { + client; + l1_contracts; + sc_rollup_address; + sc_rollup_node; + sequencer; + proxy; + _; + } + _protocol -> + let* reentrancy = Solidity_contracts.reentrancy_test () in + let* () = Eth_cli.add_abi ~label:reentrancy.label ~abi:reentrancy.abi () in + + let sender = Eth_account.bootstrap_accounts.(0) in + let* reentrancy, _create_tx = + send_transaction_to_sequencer + (Eth_cli.deploy + ~args: + (sf + {|["0xff00000000000000000000000000000000000001", "0x", 0, "0x00000000000000000000000000000000000000000000", "0x", 5]|}) + ~source_private_key:sender.private_key + ~endpoint:(Evm_node.endpoint sequencer) + ~abi:reentrancy.label + ~bin:reentrancy.bin) + sequencer + in + + let amount = 42 in + let depositor = Constant.bootstrap5 in + let receiver = Eth_account.bootstrap_accounts.(0).address in + + let* () = + send_fa_deposit_to_delayed_inbox + ~proxy:reentrancy + ~amount + ~l1_contracts + ~depositor + ~receiver + ~sc_rollup_node + ~sc_rollup_address + client + in + + let* () = + wait_for_delayed_inbox_add_tx_and_injected + ~sequencer + ~sc_rollup_node + ~client + in + let* () = bake_until_sync ~sc_rollup_node ~proxy ~sequencer ~client () in + + (* If the call to the proxy fails, the owner of the ticket is the receiver, + not the proxy. + + As the proxy is doing reeantrancy, the inner call should revert, therefore + the receiver should be the owner. + *) + let* zero_ticket_hash = ticket_hash l1_contracts.ticket_router_tester 0 in + let* receiver_balance = + ticket_balance + ~ticket_hash:zero_ticket_hash + ~account:receiver + (Either.Right sequencer) + in + Check.((receiver_balance > 0) int) + ~error_msg:"As the deposit fails, the receiver should own the ticket" ; + let* proxy_balance = + ticket_balance + ~ticket_hash:zero_ticket_hash + ~account:reentrancy + (Either.Right sequencer) + in + Check.((proxy_balance = 0) int) + ~error_msg:"As the deposit fails, the proxy must not own the ticket" ; + + unit + let test_delayed_deposit_from_init_rollup_node = register_all ~block_storage_sqlite3:false (* The test uses init from rollup node. *) @@ -2333,12 +2432,11 @@ let test_extended_block_param = (* Deploy the contract. *) let* contract, _tx_hash = send_transaction_to_sequencer - (fun () -> - Eth_cli.deploy - ~source_private_key:Eth_account.bootstrap_accounts.(0).private_key - ~endpoint:(Evm_node.endpoint sequencer) - ~abi:counter_resolved.abi - ~bin:counter_resolved.bin) + (Eth_cli.deploy + ~source_private_key:Eth_account.bootstrap_accounts.(0).private_key + ~endpoint:(Evm_node.endpoint sequencer) + ~abi:counter_resolved.abi + ~bin:counter_resolved.bin) sequencer in (* Takes the level where it was originated, the counter value will be 0. *) @@ -4152,12 +4250,11 @@ let test_outbox_size_limit_resilience ~slow = in let* spammer_contract, _tx_hash = send_transaction_to_sequencer - (fun () -> - Eth_cli.deploy - ~source_private_key:receiver.private_key - ~endpoint:(Evm_node.endpoint sequencer) - ~abi:spammer_resolved.abi - ~bin:spammer_resolved.bin) + (Eth_cli.deploy + ~source_private_key:receiver.private_key + ~endpoint:(Evm_node.endpoint sequencer) + ~abi:spammer_resolved.abi + ~bin:spammer_resolved.bin) sequencer in let* _ = @@ -4923,12 +5020,11 @@ let test_overwrite_simulation_tick_limit = (* Deploy the contract. *) let* recursive_address, _tx_hash = send_transaction_to_sequencer - (fun () -> - Eth_cli.deploy - ~source_private_key:Eth_account.bootstrap_accounts.(0).private_key - ~endpoint:(Evm_node.endpoint sequencer) - ~abi:recursive.abi - ~bin:recursive.bin) + (Eth_cli.deploy + ~source_private_key:Eth_account.bootstrap_accounts.(0).private_key + ~endpoint:(Evm_node.endpoint sequencer) + ~abi:recursive.abi + ~bin:recursive.bin) sequencer in @@ -5088,12 +5184,11 @@ let test_trace_transaction_call = in let* contract_address, _tx_deployment = send_transaction_to_sequencer - (fun () -> - Eth_cli.deploy - ~source_private_key:sender.Eth_account.private_key - ~endpoint - ~abi:simple_storage_resolved.label - ~bin:simple_storage_resolved.bin) + (Eth_cli.deploy + ~source_private_key:sender.Eth_account.private_key + ~endpoint + ~abi:simple_storage_resolved.label + ~bin:simple_storage_resolved.bin) sequencer in let* _ = produce_block sequencer in @@ -5182,12 +5277,11 @@ let test_trace_transaction_call_trace = in let* contract_address, _tx_deployment = send_transaction_to_sequencer - (fun () -> - Eth_cli.deploy - ~source_private_key:sender.Eth_account.private_key - ~endpoint - ~abi:simple_storage_resolved.label - ~bin:simple_storage_resolved.bin) + (Eth_cli.deploy + ~source_private_key:sender.Eth_account.private_key + ~endpoint + ~abi:simple_storage_resolved.label + ~bin:simple_storage_resolved.bin) sequencer in let* _ = produce_block sequencer in @@ -5281,12 +5375,11 @@ let test_trace_transaction_calltracer_failed_create = let* () = Eth_cli.add_abi ~label:call_types.label ~abi:call_types.abi () in let* _contract_address, create_tx = send_transaction_to_sequencer - (fun () -> - Eth_cli.deploy - ~source_private_key:sender.Eth_account.private_key - ~endpoint - ~abi:call_types.label - ~bin:call_types.bin) + (Eth_cli.deploy + ~source_private_key:sender.Eth_account.private_key + ~endpoint + ~abi:call_types.label + ~bin:call_types.bin) sequencer in let* _ = produce_block sequencer in @@ -5337,12 +5430,11 @@ let test_trace_transaction_calltracer_all_types = let* () = Eth_cli.add_abi ~label:call_types.label ~abi:call_types.abi () in let* contract_address, _ = send_transaction_to_sequencer - (fun () -> - Eth_cli.deploy - ~source_private_key:sender.Eth_account.private_key - ~endpoint - ~abi:call_types.label - ~bin:call_types.bin) + (Eth_cli.deploy + ~source_private_key:sender.Eth_account.private_key + ~endpoint + ~abi:call_types.label + ~bin:call_types.bin) sequencer in let* _ = produce_block sequencer in @@ -5429,12 +5521,11 @@ let test_trace_transaction_call_tracer_with_logs = in let* contract_address, _ = send_transaction_to_sequencer - (fun () -> - Eth_cli.deploy - ~source_private_key:sender.Eth_account.private_key - ~endpoint - ~abi:simple_logger.label - ~bin:simple_logger.bin) + (Eth_cli.deploy + ~source_private_key:sender.Eth_account.private_key + ~endpoint + ~abi:simple_logger.label + ~bin:simple_logger.bin) sequencer in let* _ = produce_block sequencer in @@ -5489,12 +5580,11 @@ let test_trace_transaction_call_trace_certain_depth = in let* contract_address, _ = send_transaction_to_sequencer - (fun () -> - Eth_cli.deploy - ~source_private_key:sender.Eth_account.private_key - ~endpoint - ~abi:call_tracer_depth.label - ~bin:call_tracer_depth.bin) + (Eth_cli.deploy + ~source_private_key:sender.Eth_account.private_key + ~endpoint + ~abi:call_tracer_depth.label + ~bin:call_tracer_depth.bin) sequencer in let* _ = produce_block sequencer in @@ -5566,12 +5656,11 @@ let test_trace_transaction_call_revert = let* () = Eth_cli.add_abi ~label:call_revert.label ~abi:call_revert.abi () in let* contract_address, _ = send_transaction_to_sequencer - (fun () -> - Eth_cli.deploy - ~source_private_key:sender.Eth_account.private_key - ~endpoint - ~abi:call_revert.label - ~bin:call_revert.bin) + (Eth_cli.deploy + ~source_private_key:sender.Eth_account.private_key + ~endpoint + ~abi:call_revert.label + ~bin:call_revert.bin) sequencer in let* _ = produce_block sequencer in @@ -5626,12 +5715,11 @@ let test_trace_transaction_call_trace_revert = in let* contract_address, _ = send_transaction_to_sequencer - (fun () -> - Eth_cli.deploy - ~source_private_key:sender.Eth_account.private_key - ~endpoint - ~abi:call_tracer_revert.label - ~bin:call_tracer_revert.bin) + (Eth_cli.deploy + ~source_private_key:sender.Eth_account.private_key + ~endpoint + ~abi:call_tracer_revert.label + ~bin:call_tracer_revert.bin) sequencer in let* _ = produce_block sequencer in @@ -5719,12 +5807,11 @@ let test_trace_transaction_calltracer_multiple_txs = let* () = Eth_cli.add_abi ~label:call_types.label ~abi:call_types.abi () in let* address, _ = send_transaction_to_sequencer - (fun () -> - Eth_cli.deploy - ~source_private_key:sender_0.Eth_account.private_key - ~endpoint - ~abi:call_types.label - ~bin:call_types.bin) + (Eth_cli.deploy + ~source_private_key:sender_0.Eth_account.private_key + ~endpoint + ~abi:call_types.label + ~bin:call_types.bin) sequencer in let* raw_tx_0 = @@ -5823,12 +5910,11 @@ let test_trace_transaction_calltracer_precompiles = let* () = Eth_cli.add_abi ~label:precompiles.label ~abi:precompiles.abi () in let* contract_address, _ = send_transaction_to_sequencer - (fun () -> - Eth_cli.deploy - ~source_private_key:sender.Eth_account.private_key - ~endpoint - ~abi:precompiles.label - ~bin:precompiles.bin) + (Eth_cli.deploy + ~source_private_key:sender.Eth_account.private_key + ~endpoint + ~abi:precompiles.label + ~bin:precompiles.bin) sequencer in let* _ = produce_block sequencer in @@ -6015,12 +6101,11 @@ let test_miner = in let* contract, _tx_hash = send_transaction_to_sequencer - (fun () -> - Eth_cli.deploy - ~source_private_key:Eth_account.bootstrap_accounts.(0).private_key - ~endpoint:(Evm_node.endpoint sequencer) - ~abi:coinbase_resolved.abi - ~bin:coinbase_resolved.bin) + (Eth_cli.deploy + ~source_private_key:Eth_account.bootstrap_accounts.(0).private_key + ~endpoint:(Evm_node.endpoint sequencer) + ~abi:coinbase_resolved.abi + ~bin:coinbase_resolved.bin) sequencer in let* storage_coinbase = @@ -6101,12 +6186,11 @@ let test_trace_call = in let* contract_address, _tx_deployment = send_transaction_to_sequencer - (fun () -> - Eth_cli.deploy - ~source_private_key:sender.Eth_account.private_key - ~endpoint - ~abi:simple_storage_resolved.label - ~bin:simple_storage_resolved.bin) + (Eth_cli.deploy + ~source_private_key:sender.Eth_account.private_key + ~endpoint + ~abi:simple_storage_resolved.label + ~bin:simple_storage_resolved.bin) sequencer in let* () = @@ -6510,12 +6594,11 @@ let test_regression_block_hash_gen = let* {abi; bin; _} = Solidity_contracts.block_hash_gen () in let* _contract, _tx_hash = send_transaction_to_sequencer - (fun () -> - Eth_cli.deploy - ~source_private_key:Eth_account.bootstrap_accounts.(0).private_key - ~endpoint:(Evm_node.endpoint sequencer) - ~abi - ~bin) + (Eth_cli.deploy + ~source_private_key:Eth_account.bootstrap_accounts.(0).private_key + ~endpoint:(Evm_node.endpoint sequencer) + ~abi + ~bin) sequencer in unit @@ -6962,12 +7045,11 @@ let test_inconsistent_da_fees = let* () = Eth_cli.add_abi ~label:batcher.label ~abi:batcher.abi () in let* batcher_contract, _tx_hash = send_transaction_to_sequencer - (fun () -> - Eth_cli.deploy - ~source_private_key:Eth_account.bootstrap_accounts.(0).private_key - ~endpoint:(Evm_node.endpoint sequencer) - ~abi:batcher.abi - ~bin:batcher.bin) + (Eth_cli.deploy + ~source_private_key:Eth_account.bootstrap_accounts.(0).private_key + ~endpoint:(Evm_node.endpoint sequencer) + ~abi:batcher.abi + ~bin:batcher.bin) sequencer in let* _tx = @@ -7082,6 +7164,7 @@ let () = test_delayed_deposit_is_included protocols ; test_delayed_fa_deposit_is_included protocols ; test_delayed_fa_deposit_is_ignored_if_feature_disabled protocols ; + test_fa_reentrant_deposit_reverts protocols ; test_delayed_transaction_peeked protocols ; test_invalid_delayed_transaction protocols ; test_fa_withdrawal_is_included protocols ;