aefa_engine_state.erl
1 %%%------------------------------------------------------------------- 2 %%% @copyright (C) 2019, Aeternity Anstalt 3 %%% @doc 4 %%% ADT for the engine state 5 %%% @end 6 %%%------------------------------------------------------------------- 7 -module(aefa_engine_state). 8 9 -export([ new/7 10 , finalize/1 11 ]). 12 13 %% Getters 14 -export([ accumulator/1 15 , accumulator_stack/1 16 , bbs/1 17 , call_stack/1 18 , call_value/1 19 , caller/1 20 , chain_api/1 21 , code_cache/1 22 , creator_cache/1 23 , current_bb/1 24 , current_contract/1 25 , current_function/1 26 , current_tvars/1 27 , functions/1 28 , gas/1 29 , logs/1 30 , memory/1 31 , stores/1 32 , trace/1 33 , vm_version/1 34 , consensus_version/1 35 ]). 36 37 %% Setters 38 -export([ set_accumulator/2 39 , set_accumulator_stack/2 40 , set_bbs/2 41 , set_call_stack/2 42 , set_call_value/2 43 , set_caller/2 44 , set_chain_api/2 45 , set_code_cache/2 46 , set_creator_cache/2 47 , set_current_bb/2 48 , set_current_contract/2 49 , set_current_function/2 50 , set_current_tvars/2 51 , set_functions/2 52 , set_gas/2 53 , add_log/2 54 , set_memory/2 55 , set_stores/2 56 , set_trace/2 57 ]). 58 59 %% More complex stuff 60 -export([ check_reentrant_remote/2 61 , collect_gas_stores_on_error/1 62 , collect_gas_stores_on_exit/1 63 , collect_gas_stores_on_revert/1 64 , current_bb_instructions/1 65 , dup_accumulator/1 66 , dup_accumulator/2 67 , drop_accumulator/2 68 , contract_fate_bytecode/2 69 , contract_find_final_ref/2 70 , remove_contract/2 71 , in_auth_context/1 72 , is_onchain/1 73 , pop_accumulator/1 74 , pop_call_stack/1 75 , push_accumulator/2 76 , push_arguments/2 77 , push_call_stack/1 78 , push_gas_cap/2 79 , push_continuation/2 80 , push_return_type_check/4 81 , spend_gas/2 82 , spend_gas_for_new_cells/2 83 , spend_gas_for_store_values/2 84 , spend_gas_for_traversal/3 85 , spend_gas_for_traversal/4 86 , update_for_remote_call/5 87 ]). 88 89 -ifdef(TEST). 90 -export([ add_trace/2 91 , gas_traversal/4 92 , cost/1 93 ]). 94 -endif. 95 96 -ifdef(DEBUG_INFO). 97 -export([ debug_info/1 98 , set_debug_info/2 99 ]). 100 -endif. 101 102 -define(FIX_CONTRACT_CHECK_WINDOW_LOWER_LIMIT, 237000). 103 -define(FIX_CONTRACT_CHECK_WINDOW_UPPER_LIMIT, 245000). 104 105 -include_lib("aebytecode/include/aeb_fate_data.hrl"). 106 -include_lib("aecontract/include/aecontract.hrl"). 107 -include_lib("aecontract/include/hard_forks.hrl"). 108 109 -type void_or_fate() :: ?FATE_VOID | aeb_fate_data:fate_type(). 110 -type pubkey() :: <<_:256>>. 111 112 -ifdef(DEBUG_INFO). 113 -type debug_info() :: aefa_debug:info(). 114 -else. 115 -type debug_info() :: disabled. 116 -endif. 117 118 -record(es, { accumulator :: void_or_fate() 119 , accumulator_stack :: [aeb_fate_data:fate_type()] 120 , bbs :: map() 121 , call_stack :: [tuple()] %% TODO: Better type 122 , caller :: aeb_fate_data:fate_address() 123 , call_value :: non_neg_integer() 124 , chain_api :: aefa_chain_api:state() 125 , code_cache :: map() %% Cache for loaded contracts. 126 , creator_cache :: map() %% Cache for creators of contracts 127 , created_cells :: integer() %% Heap memory used 128 , current_bb :: non_neg_integer() 129 , current_contract :: ?FATE_VOID | pubkey() 130 , current_function :: ?FATE_VOID | binary() 131 , current_tvars :: map() %% Instantiations for type variables in the current call (needed when type checking return value) 132 , functions :: map() %% Cache for current contract. 133 , gas :: integer() 134 , logs :: [term()] 135 , memory :: map() %% Environment #{name => val} 136 , seen_contracts :: [pubkey()] 137 %% Call stack of contracts (including tail calls) 138 , stores :: aefa_stores:store() 139 , trace :: list() 140 , vm_version :: non_neg_integer() 141 , debug_info :: debug_info() 142 }). 143 144 -opaque state() :: #es{}. 145 -export_type([ state/0 146 ]). 147 148 -spec new(non_neg_integer(), non_neg_integer(), map(), aefa_stores:store(), aefa_chain_api:state(), map(), non_neg_integer()) -> state(). 149 new(Gas, Value, Spec, Stores, APIState, CodeCache, VMVersion) -> 150 [error({bad_init_arg, X, Y}) || {X, Y} <- [{gas, Gas}, {value, Value}], 151 not (is_integer(Y) andalso Y >= 0)], 152 #es{ accumulator = ?FATE_VOID 153 , accumulator_stack = [] 154 , bbs = #{} 155 , call_stack = [] 156 , caller = aeb_fate_data:make_address(maps:get(caller, Spec)) 157 , call_value = Value 158 , chain_api = APIState 159 , code_cache = CodeCache 160 , creator_cache = #{} 161 , created_cells = 0 162 , current_bb = 0 163 , current_contract = ?FATE_VOID 164 , current_function = ?FATE_VOID 165 , current_tvars = #{} 166 , functions = #{} 167 , gas = Gas 168 , logs = [] 169 , memory = #{} 170 , seen_contracts = [] 171 , stores = Stores 172 , trace = [] 173 , vm_version = VMVersion 174 , debug_info = disabled 175 }. 176 177 aefa_stores(#es{ chain_api = APIState }) -> 178 Protocol = aetx_env:consensus_version(aefa_chain_api:tx_env(APIState)), 179 case Protocol >= ?IRIS_PROTOCOL_VSN of 180 true -> aefa_stores; 181 false -> aefa_stores_lima 182 end. 183 184 -spec finalize(state()) -> {ok, state()} | {error, out_of_gas}. 185 finalize(#es{chain_api = API, stores = Stores} = ES) -> 186 Aefa_stores = aefa_stores(ES), 187 try 188 ES1 = lists:foldl(fun(Val, ES0) -> spend_gas_for_traversal(Val, serial, ES0) end, 189 ES, aefa_stores:terms_to_finalize(Stores)), 190 Gas = gas(ES1), 191 case Aefa_stores:finalize(API, Gas, Stores) of 192 {ok, Stores1, GasLeft} -> 193 {ok, ES1#es{chain_api = Stores1, gas = GasLeft}}; 194 {error, out_of_gas} -> 195 {error, out_of_gas} 196 end 197 catch 198 throw:out_of_gas -> 199 {error, out_of_gas} 200 end. 201 202 %%%=================================================================== 203 %%% API 204 %%%=================================================================== 205 206 -ifdef(TEST). 207 add_trace(I, #es{trace = Trace} = ES) -> 208 ES#es{trace = [{I, erlang:process_info(self(), reductions)}|Trace]}. 209 -endif. 210 211 -spec update_for_remote_call(pubkey(), term(), aect_contracts:vm_version(), aeb_fate_data:fate_address(), state()) -> state(). 212 update_for_remote_call(Contract, ContractCode, VMV, Caller, ES) -> 213 ES#es{ functions = aeb_fate_code:functions(ContractCode) 214 , current_contract = Contract 215 , caller = Caller 216 , vm_version = VMV 217 }. 218 219 -spec check_reentrant_remote(aeb_fate_data:fate_contract(), state()) -> 220 {ok, state()} | error. 221 check_reentrant_remote(?FATE_CONTRACT(Current), #es{current_contract = Current}) -> 222 error; 223 check_reentrant_remote(?FATE_CONTRACT(Pubkey), #es{seen_contracts = Seen} = ES) -> 224 case lists:member(Pubkey, Seen) of 225 true -> 226 error; 227 false -> 228 {ok, ES#es{seen_contracts = [current_contract(ES)|Seen]}} 229 end. 230 231 -spec is_onchain(state()) -> boolean(). 232 is_onchain(#es{chain_api = APIState}) -> 233 aefa_chain_api:is_onchain(APIState). 234 235 -spec in_auth_context(state()) -> boolean(). 236 in_auth_context(#es{chain_api = APIState}) -> 237 undefined =/= aetx_env:ga_tx_hash(aefa_chain_api:tx_env(APIState)). 238 239 240 -spec contract_fate_bytecode(pubkey(), state()) -> 'error' | 241 {'ok', term(), aect_contracts:vm_version(), state()}. 242 contract_fate_bytecode(Pubkey, #es{chain_api = AS0} = ES0) -> 243 CodeCache = code_cache(ES0), 244 case maps:get(Pubkey, CodeCache, void) of 245 void -> 246 ES1 = spend_gas([{?IRIS_PROTOCOL_VSN, 4900}, {?LIMA_PROTOCOL_VSN, 0}], ES0), 247 case aefa_chain_api:contract_fate_bytecode(Pubkey, AS0) of 248 {ok, ContractCode, VMV, AS1} -> 249 CodeCache1 = maps:put(Pubkey, {ContractCode, VMV}, CodeCache), 250 ES2 = set_code_cache(CodeCache1, ES1), 251 ES3 = set_chain_api(AS1, ES2), 252 {ok, ContractCode, VMV, ES3}; 253 error -> 254 error 255 end; 256 {ContractCode, VMV} -> 257 {ok, ContractCode, VMV, ES0} 258 end. 259 260 -spec contract_find_final_ref(aect_contracts:pubkey(), state()) -> 261 'error' | {'ok', aect_contracts:pubkey(), aect_contracts:vm_version()}. 262 contract_find_final_ref(PK, ES) -> 263 %% References are not considered in the cache, therefore 264 %% we need to search in the chain. 265 aefa_chain_api:contract_find_final_ref(PK, chain_api(ES)). 266 267 -spec remove_contract(pubkey(), state()) -> state(). 268 remove_contract(Pubkey, #es{chain_api = AS0} = ES0) -> 269 ES1 = set_chain_api(aefa_chain_api:remove_contract(Pubkey, AS0), ES0), 270 CodeCache = aefa_engine_state:code_cache(ES1), 271 set_code_cache(maps:remove(Pubkey, CodeCache), ES1). 272 273 %%%------------------ 274 %%% Accumulator stack 275 276 -spec push_arguments([aeb_fate_data:fate_type()], state()) -> state(). 277 push_arguments(Args, #es{accumulator_stack = Stack, accumulator = Acc} = ES) -> 278 push_arguments(lists:reverse(Args), Acc, Stack, ES). 279 280 push_arguments([], Acc, Stack, ES) -> 281 ES#es{ accumulator = Acc 282 , accumulator_stack = Stack}; 283 push_arguments([A|As], Acc, Stack, ES) -> 284 push_arguments(As, A, [Acc | Stack], ES). 285 286 -ifdef(DEBUG_INFO). 287 -define(PUSH_DEBUG_CALL_STACK(Info), aefa_debug:push_call_stack(Info)). 288 -define(POP_DEBUG_CALL_STACK(Info), aefa_debug:pop_call_stack(Info)). 289 -else. 290 -define(PUSH_DEBUG_CALL_STACK(Info), Info). 291 -define(POP_DEBUG_CALL_STACK(Info), Info). 292 -endif. 293 294 -spec push_call_stack(state()) -> state(). 295 push_call_stack(#es{ current_bb = BB 296 , current_function = Function 297 , current_contract = Contract 298 , vm_version = VmVersion 299 , current_tvars = TVars 300 , accumulator = Acc 301 , accumulator_stack = AccS 302 , call_stack = Stack 303 , call_value = Value 304 , caller = Caller 305 , memory = Mem 306 , debug_info = DbgInfo} = ES) -> 307 AccS1 = [Acc || Acc /= void] ++ AccS, 308 ES#es{accumulator = void, 309 accumulator_stack = [], 310 call_stack = [{Caller, Contract, VmVersion, Function, TVars, BB + 1, AccS1, Mem, Value}|Stack], 311 debug_info = ?PUSH_DEBUG_CALL_STACK(DbgInfo)}. 312 313 %% TODO: Make better types for all these things 314 -spec pop_call_stack(state()) -> 315 {'empty', state()} | 316 {'modify', fun((state()) -> state()), state()} | 317 {'return_check', map(), protected | unprotected, aeb_fate_data:fate_type_type(), 318 aefa_stores:store(), aefa_chain_api:state(), state()} | 319 {'local', _, map(), non_neg_integer(), state()} | 320 {'remote', aeb_fate_data:fate_address(), aeb_fate_data:fate_contract(), 321 _, map(), non_neg_integer(), state()}. 322 pop_call_stack(#es{accumulator = ReturnValue, 323 call_stack = Stack, 324 debug_info = DbgInfo, 325 current_contract = Current} = ES) -> 326 case Stack of 327 [] -> {empty, ES}; 328 [{modify, Continuation}| Rest] -> 329 {modify, Continuation, ES#es{call_stack = Rest}}; 330 [{return_check, TVars, Protected, Stores, API, ReturnType}| Rest] -> 331 {return_check, TVars, Protected, ReturnType, Stores, API, ES#es{ call_stack = Rest}}; 332 [{gas_store, StoredGas}| Rest] -> 333 ES1 = ES#es{ gas = StoredGas + gas(ES) 334 , call_stack = Rest 335 }, 336 pop_call_stack(ES1); 337 [{_Caller, Current, _VmVersion, Function, TVars, BB, AccS, Mem, Value}| Rest] -> 338 {local, Function, TVars, BB, 339 ES#es{ call_value = Value 340 , accumulator = ReturnValue 341 , accumulator_stack = AccS 342 , memory = Mem 343 , call_stack = Rest 344 , debug_info = ?POP_DEBUG_CALL_STACK(DbgInfo) 345 }}; 346 [{Caller, Pubkey, VmVersion, Function, TVars, BB, AccS, Mem, Value}| Rest] -> 347 Seen = pop_seen_contracts(Pubkey, ES), 348 NewCurrent = 349 case aefa_chain_api:generation(ES#es.chain_api) of 350 Height when Height >= ?FIX_CONTRACT_CHECK_WINDOW_LOWER_LIMIT, 351 Height =< ?FIX_CONTRACT_CHECK_WINDOW_UPPER_LIMIT -> 352 ES#es.current_contract; 353 _Height -> 354 Pubkey 355 end, 356 {remote, Caller, aeb_fate_data:make_contract(Pubkey), Function, TVars, BB, 357 ES#es{ call_value = Value 358 , accumulator = ReturnValue 359 , accumulator_stack = AccS 360 , memory = Mem 361 , call_stack = Rest 362 , seen_contracts = Seen 363 , current_contract = NewCurrent 364 , vm_version = VmVersion 365 , debug_info = ?POP_DEBUG_CALL_STACK(DbgInfo) 366 }} 367 end. 368 369 -spec collect_gas_stores_on_error(state()) -> integer(). 370 collect_gas_stores_on_error(#es{call_stack = Stack}) -> 371 collect_gas_stores(Stack, 0). 372 373 -spec collect_gas_stores_on_exit(state()) -> integer(). 374 collect_gas_stores_on_exit(#es{call_stack = Stack}) -> 375 collect_gas_stores(Stack, 0). 376 377 -spec collect_gas_stores_on_revert(state()) -> integer(). 378 collect_gas_stores_on_revert(#es{call_stack = Stack, gas = Gas}) -> 379 collect_gas_stores(Stack, Gas). 380 381 collect_gas_stores([{gas_store, Gas}|Left], AccGas) -> 382 collect_gas_stores(Left, AccGas + Gas); 383 collect_gas_stores([{modify, _}|Left], AccGas) -> 384 collect_gas_stores(Left, AccGas); 385 collect_gas_stores([{return_check, _, _, _, _, _}|Left], AccGas) -> 386 collect_gas_stores(Left, AccGas); 387 collect_gas_stores([{_, _, _, _, _, _, _, _, _}|Left], AccGas) -> 388 collect_gas_stores(Left, AccGas); 389 collect_gas_stores([], AccGas) -> 390 AccGas. 391 392 pop_seen_contracts(Pubkey, #es{seen_contracts = Seen}) -> 393 %% NOTE: We might have remote tailcalls leaving entries here, 394 %% but not in the actual call stack. Drop until we reach the 395 %% contract we are returning to. 396 [_|Seen1] = lists:dropwhile(fun(X) -> X =/= Pubkey end, Seen), 397 Seen1. 398 399 -spec push_gas_cap(pos_integer(), state()) -> state(). 400 push_gas_cap(GasCap, #es{gas = AvailableGas} = ES) when GasCap >= AvailableGas -> 401 %% Nothing is reserved 402 ES; 403 push_gas_cap(GasCap, #es{ gas = AvailableGas 404 , call_stack = Stack} = ES) when GasCap < AvailableGas -> 405 ES#es{ call_stack = [{gas_store, AvailableGas - GasCap}|Stack] 406 , gas = GasCap 407 }. 408 409 -spec push_continuation(fun((state()) -> state()), state()) -> state(). 410 push_continuation(Cont, #es{ call_stack = Stack } = ES) -> 411 ES#es{ call_stack = [{modify, Cont}|Stack]}. 412 413 -spec push_return_type_check(aeb_fate_data:fate_type_type(), #{}, unprotected | protected, state()) -> state(). 414 push_return_type_check(RetType, TVars, Protected, #es{ call_stack = Stack, stores = Stores, chain_api = API } = ES) -> 415 %% Note that the TVars must correspond to the bindings for the 416 %% return type. Typically, the current_tvars corresponds to the 417 %% next function in the call stack. 418 ES#es{ call_stack = [{return_check, TVars, Protected, Stores, API, RetType}|Stack]}. 419 420 -spec push_accumulator(aeb_fate_data:fate_type(), state()) -> state(). 421 push_accumulator(V, #es{ accumulator = ?FATE_VOID 422 , accumulator_stack = [] } = ES) -> 423 ES1 = ES#es{ accumulator = V 424 , accumulator_stack = []}, 425 spend_gas_for_new_cells(1, ES1); 426 push_accumulator(V, #es{ accumulator = X 427 , accumulator_stack = Stack } = ES) -> 428 ES1 = ES#es{ accumulator = V 429 , accumulator_stack = [X|Stack]}, 430 spend_gas_for_new_cells(1, ES1). 431 432 -spec pop_accumulator(state()) -> {aeb_fate_data:fate_type(), state()}. 433 pop_accumulator(#es{accumulator = ?FATE_VOID, accumulator_stack = [], created_cells = C} = ES) -> 434 case consensus_version(ES) >= ?IRIS_PROTOCOL_VSN of 435 true -> 436 aefa_fate:abort({pop_empty_stack, 1}, ES); 437 false -> 438 {?FATE_VOID, ES#es{created_cells = C - 1}} 439 end; 440 pop_accumulator(#es{accumulator = X, accumulator_stack = [], created_cells = C} = ES) -> 441 {X, ES#es{ accumulator = ?FATE_VOID 442 , created_cells = C - 1}}; 443 pop_accumulator(#es{accumulator = X, 444 accumulator_stack = [V|Stack], 445 created_cells = C} = ES) -> 446 {X, ES#es{ accumulator = V 447 , accumulator_stack = Stack 448 , created_cells = C - 1 449 }}. 450 451 -spec dup_accumulator(state()) -> state(). 452 dup_accumulator(ES) -> 453 dup_accumulator(0, ES). 454 455 -spec dup_accumulator(non_neg_integer(), state()) -> state(). 456 dup_accumulator(N, #es{accumulator = X, accumulator_stack = Stack} = ES) -> 457 Protocol = consensus_version(ES), 458 case is_integer(N) andalso N >= 0 andalso N =< length(Stack) andalso (N > 0 orelse X /= ?FATE_VOID) of 459 false when Protocol >= ?IRIS_PROTOCOL_VSN -> 460 aefa_fate:abort({type_error, dup, N}); 461 _ -> 462 {X1, Stack1} = get_n(N, [X|Stack]), 463 ES1 = ES#es{ accumulator = X1 464 , accumulator_stack = [X|Stack1]}, 465 spend_gas_for_new_cells(1, ES1) 466 end. 467 468 get_n(0, [X|XS]) -> {X, [X|XS]}; 469 get_n(N, [X|XS]) -> 470 {Y, List} = get_n(N-1, XS), 471 {Y, [X|List]}. 472 473 -spec drop_accumulator(non_neg_integer(), state()) -> state(). 474 drop_accumulator(0, ES) -> ES; 475 drop_accumulator(N, #es{accumulator_stack = [V|Stack], 476 created_cells = C 477 } = ES) -> 478 drop_accumulator(N-1, ES#es{ accumulator = V 479 , accumulator_stack = Stack 480 , created_cells = C - 1 481 }); 482 drop_accumulator(N, #es{accumulator_stack = [], 483 created_cells = C 484 } = ES) -> 485 drop_accumulator(N-1, ES#es{accumulator = ?FATE_VOID 486 , accumulator_stack = [] 487 , created_cells = C - 1 488 }). 489 490 %%%------------------ 491 492 -spec current_bb_instructions(state()) -> list(). 493 current_bb_instructions(#es{current_bb = BB, bbs = BBS} = ES) -> 494 case maps:get(BB, BBS, void) of 495 void -> aefa_fate:abort({trying_to_reach_bb, BB}, ES); 496 Instructions -> Instructions 497 end. 498 499 %%%------------------ 500 501 -spec spend_gas(non_neg_integer() | list({non_neg_integer(), non_neg_integer()}), state()) -> state(). 502 spend_gas(X, #es{gas = Gas} = ES) when is_integer(X) -> 503 NewGas = Gas - X, 504 case NewGas < 0 of 505 true -> aefa_fate:abort(out_of_gas, ES); 506 false -> ES#es{gas = NewGas} 507 end; 508 spend_gas(GasOpts, ES) when is_list(GasOpts) -> 509 Protocol = consensus_version(ES), 510 spend_gas(get_gas(Protocol, GasOpts), ES). 511 512 get_gas(_Protocol, [{_, Gas}]) -> Gas; 513 get_gas(Protocol, [{Protocol1, Gas} | _]) when Protocol >= Protocol1 -> Gas; 514 get_gas(Protocol, [_ | Rest]) -> get_gas(Protocol, Rest). 515 516 %% The gas price per cell increases by 1 for each kibiword (each 1024 64-bit word) used. 517 -spec spend_gas_for_new_cells(integer(), state()) -> state(). 518 spend_gas_for_new_cells(NewCells, ES) when NewCells < 0 -> 519 case consensus_version(ES) of 520 P when P < ?IRIS_PROTOCOL_VSN -> spend_gas_for_new_cells1(NewCells, ES); 521 P when P >= ?IRIS_PROTOCOL_VSN -> spend_gas_for_new_cells1(abs(NewCells) + 2, ES) 522 end; 523 spend_gas_for_new_cells(NewCells, ES) -> 524 spend_gas_for_new_cells1(NewCells, ES). 525 526 spend_gas_for_new_cells1(NewCells, #es{ created_cells = Cells } = ES) when NewCells + Cells =< 1024 -> 527 TotalCells = Cells + NewCells, 528 spend_gas(NewCells, ES#es{ created_cells = TotalCells }); 529 spend_gas_for_new_cells1(1, #es{ created_cells = Cells } = ES) -> 530 TotalCells = Cells + 1, 531 CellCost = 1 + (TotalCells bsr 10), 532 spend_gas(CellCost, ES#es{ created_cells = TotalCells }); 533 spend_gas_for_new_cells1(NewCells, #es{ created_cells = Cells } = ES) -> 534 TotalCells = Cells + NewCells, 535 CellCost = 1 + (TotalCells bsr 10), 536 spend_gas(NewCells * CellCost, ES#es{ created_cells = TotalCells }). 537 538 spend_gas_for_store_values(StoreValues, ES) -> 539 case consensus_version(ES) < ?CERES_PROTOCOL_VSN of 540 true -> ES; 541 false -> spend_gas(StoreValues * 5000, ES) 542 end. 543 544 -define(GAS_DENOMINATOR, 1000). 545 546 -record(cost, { node = 0 %% mGas per node 547 , leaf = 0 %% mGas per byte 548 , unfold = 0 %% mGas per unfold 549 }). 550 551 cost(simple) -> 552 #cost{ node = 800, leaf = 0, unfold = 0 }; 553 554 cost(serial) -> 555 #cost{ node = 800, leaf = 300, unfold = 0}; 556 557 cost(unfold) -> 558 #cost{ node = 800, leaf = 0, unfold = 20000000 }; 559 560 cost(unfold_compare) -> 561 #cost{ node = 800, leaf = 4, unfold = 20000000 }; 562 563 cost(unfold_serial) -> 564 #cost{ node = 800, leaf = 300, unfold = 20000000 }; 565 566 cost(final) -> 567 #cost{ node = 800, leaf = 1000, unfold = 0 }. 568 569 -type cost_model() :: simple | serial | unfold | unfold_compare | unfold_serial | final. 570 571 %% As spend_gas_for_traversal/4, but do not look inside store maps. 572 -spec spend_gas_for_traversal(aeb_fate_data:fate_type(), cost_model(), state()) -> state(). 573 spend_gas_for_traversal(Term, CostModel, ES) -> 574 spend_gas_for_traversal(Term, CostModel, {fun(_) -> 0 end, fun(_) -> ?FATE_UNIT end}, ES). 575 576 %% Call this before deep traversals of fate terms to make sure there is enough 577 %% gas. Throws an out of gas exception if there's not. Parameterised by the gas 578 %% cost model. And an unfolding function for store maps. 579 -spec spend_gas_for_traversal(aeb_fate_data:fate_type(), 580 cost_model(), 581 {fun((integer()) -> non_neg_integer()), fun((integer()) -> aeb_fate_data:fate_type())}, 582 state()) -> state(). 583 spend_gas_for_traversal(Term, CostModel, Unfold, ES = #es{gas = Gas, chain_api = APIState}) -> 584 Protocol = aetx_env:consensus_version(aefa_chain_api:tx_env(APIState)), 585 case Protocol < ?IRIS_PROTOCOL_VSN of 586 true -> ES; 587 false -> 588 try 589 case gas_traversal(Gas * ?GAS_DENOMINATOR, cost(CostModel), Unfold, Term) of 590 GasLeft when GasLeft >= 0 -> ES#es{gas = GasLeft div ?GAS_DENOMINATOR}; 591 _ -> aefa_fate:abort(out_of_gas, ES) 592 end 593 catch 594 throw:out_of_gas -> 595 aefa_fate:abort(out_of_gas, ES) 596 end 597 end. 598 599 gas_traversal(Gas, _Cost, _Unfold, _T) when Gas < 0 -> throw(out_of_gas); 600 gas_traversal(Gas, Cost, Unfold, T) -> gas_traversal_t(Gas, Cost, Unfold, T). 601 602 gas_traversal_t(Gas, Cost, _Unfold, ?FATE_MAP_TOMBSTONE= Val) -> Gas - Cost#cost.node - leaf_cost(Cost, Val); 603 gas_traversal_t(Gas, Cost, _Unfold, ?FATE_TRUE = Val) -> Gas - Cost#cost.node - leaf_cost(Cost, Val); 604 gas_traversal_t(Gas, Cost, _Unfold, ?FATE_FALSE = Val) -> Gas - Cost#cost.node - leaf_cost(Cost, Val); 605 gas_traversal_t(Gas, Cost, _Unfold, ?FATE_UNIT = Val) -> Gas - Cost#cost.node - leaf_cost(Cost, Val); 606 gas_traversal_t(Gas, Cost, _Unfold, ?FATE_BITS(_) = Val) -> Gas - Cost#cost.node - leaf_cost(Cost, Val); 607 gas_traversal_t(Gas, Cost, _Unfold, ?FATE_BYTES(_) = Val) -> Gas - Cost#cost.node - leaf_cost(Cost, Val); 608 gas_traversal_t(Gas, Cost, _Unfold, ?FATE_ADDRESS(_) = Val) -> Gas - Cost#cost.node - leaf_cost(Cost, Val); 609 gas_traversal_t(Gas, Cost, _Unfold, ?FATE_CONTRACT(_) = Val) -> Gas - Cost#cost.node - leaf_cost(Cost, Val); 610 gas_traversal_t(Gas, Cost, _Unfold, ?FATE_ORACLE(_) = Val) -> Gas - Cost#cost.node - leaf_cost(Cost, Val); 611 gas_traversal_t(Gas, Cost, _Unfold, ?FATE_ORACLE_Q(_) = Val) -> Gas - Cost#cost.node - leaf_cost(Cost, Val); 612 gas_traversal_t(Gas, Cost, _Unfold, ?FATE_CHANNEL(_) = Val) -> Gas - Cost#cost.node - leaf_cost(Cost, Val); 613 gas_traversal_t(Gas, Cost, _Unfold, ?FATE_TYPEREP(T)) -> gas_traversal_type(Gas - Cost#cost.node, Cost, T); 614 gas_traversal_t(Gas, Cost, _Unfold, Val) when ?IS_FATE_INTEGER(Val) -> Gas - Cost#cost.node - leaf_cost(Cost, Val); 615 gas_traversal_t(Gas, Cost, _Unfold, Val) when ?IS_FATE_STRING(Val) -> Gas - Cost#cost.node - leaf_cost(Cost, Val); 616 gas_traversal_t(Gas, Cost, Unfold, ?FATE_TUPLE(Val)) -> 617 gas_traversal_l(Gas - Cost#cost.node, Cost, Unfold, tuple_to_list(Val)); 618 gas_traversal_t(Gas, Cost, Unfold, Val) when ?IS_FATE_LIST(Val) -> 619 gas_traversal_l(Gas, Cost, Unfold, ?FATE_LIST_VALUE(Val)); 620 gas_traversal_t(Gas, Cost, Unfold, ?FATE_VARIANT(_Arities, _Tag, Vals)) -> 621 gas_traversal_l(Gas - Cost#cost.node, Cost, Unfold, tuple_to_list(Vals)); 622 gas_traversal_t(Gas, Cost, Unfold, Val) when ?IS_FATE_MAP(Val) -> 623 gas_traversal_m(Gas, Cost, Unfold, ?FATE_MAP_VALUE(Val)); 624 gas_traversal_t(Gas, Cost, {MapSize, Unfold}, ?FATE_STORE_MAP(Cache, Id) ) -> 625 Gas1 = Gas - MapSize(Id) * Cost#cost.unfold - Cost#cost.node, 626 case Gas1 < 0 of 627 true -> throw(out_of_gas); 628 false -> 629 Gas2 = gas_traversal(Gas1, Cost, {MapSize, Unfold}, Unfold(Id)), 630 gas_traversal_m(Gas2, Cost, {MapSize, Unfold}, Cache) 631 end. 632 633 gas_traversal_l(Gas, Cost, _Unfold, []) -> Gas - Cost#cost.node; 634 gas_traversal_l(Gas0, Cost, Unfold, [H | T]) -> 635 Gas1 = gas_traversal(Gas0 - Cost#cost.node, Cost, Unfold, H), 636 gas_traversal_l(Gas1, Cost, Unfold, T). 637 638 gas_traversal_m(Gas, Cost, Unfold, Map) -> 639 maps:fold(fun(K, V, Gas1) -> 640 Gas2 = gas_traversal(Gas1 - Cost#cost.node, Cost, Unfold, K), 641 gas_traversal(Gas2, Cost, Unfold, V) 642 end, Gas - Cost#cost.node, Map). 643 644 gas_traversal_type(Gas, _Cost, _T) when Gas < 0 -> throw(out_of_gas); 645 gas_traversal_type(Gas, Cost, {tuple, L}) when is_list(L) -> 646 gas_traversal_type(Gas - Cost#cost.node, Cost, L); 647 gas_traversal_type(Gas, Cost, {variant, L}) when is_list(L) -> 648 gas_traversal_type(Gas - Cost#cost.node, Cost, L); 649 gas_traversal_type(Gas, Cost, {list, T}) -> 650 gas_traversal_type(Gas - Cost#cost.node, Cost, T); 651 gas_traversal_type(Gas0, Cost, {map, K, V}) -> 652 Gas1 = gas_traversal_type(Gas0 - Cost#cost.node, Cost, K), 653 gas_traversal_type(Gas1, Cost, V); 654 gas_traversal_type(Gas, Cost, integer) -> Gas - Cost#cost.leaf; 655 gas_traversal_type(Gas, Cost, boolean) -> Gas - Cost#cost.leaf; 656 gas_traversal_type(Gas, Cost, address) -> Gas - Cost#cost.leaf; 657 gas_traversal_type(Gas, Cost, hash) -> Gas - Cost#cost.leaf; 658 gas_traversal_type(Gas, Cost, signature) -> Gas - Cost#cost.leaf; 659 gas_traversal_type(Gas, Cost, contract) -> Gas - Cost#cost.leaf; 660 gas_traversal_type(Gas, Cost, oracle) -> Gas - Cost#cost.leaf; 661 gas_traversal_type(Gas, Cost, channel) -> Gas - Cost#cost.leaf; 662 gas_traversal_type(Gas, Cost, bits) -> Gas - Cost#cost.leaf; 663 gas_traversal_type(Gas, Cost, string) -> Gas - Cost#cost.leaf; 664 gas_traversal_type(Gas, Cost, contract_bytearray) -> Gas - Cost#cost.leaf; 665 gas_traversal_type(Gas0, Cost, [H|T]) -> 666 Gas1 = gas_traversal_type(Gas0, Cost, H), 667 gas_traversal_type(Gas1, Cost, T); 668 gas_traversal_type(Gas, Cost, []) -> Gas - Cost#cost.leaf. 669 670 leaf_cost(#cost{ leaf = 0 }, _) -> 0; 671 leaf_cost(#cost{ leaf = LeafCost }, Val) -> LeafCost * leaf_size(Val). 672 673 leaf_size(?FATE_MAP_TOMBSTONE) -> 1; 674 leaf_size(?FATE_TRUE ) -> 1; 675 leaf_size(?FATE_FALSE ) -> 1; 676 leaf_size(?FATE_UNIT ) -> 1; 677 leaf_size(?FATE_BITS(B) ) -> int_size(B); 678 leaf_size(?FATE_BYTES(B) ) -> byte_size(B); 679 leaf_size(?FATE_ADDRESS(_) ) -> 32; 680 leaf_size(?FATE_CONTRACT(_) ) -> 32; 681 leaf_size(?FATE_ORACLE(_) ) -> 32; 682 leaf_size(?FATE_ORACLE_Q(_) ) -> 32; 683 leaf_size(?FATE_CHANNEL(_) ) -> 32; 684 leaf_size(Val) when ?IS_FATE_INTEGER(Val) -> int_size(Val); 685 leaf_size(Val) when ?IS_FATE_STRING(Val) -> byte_size(Val); 686 leaf_size(_) -> 0. 687 688 int_size(N) -> int_size(N, 1). 689 690 int_size(N, Sz) when abs(N) < 256 -> Sz; 691 int_size(N, Sz) -> int_size(N div 256, Sz + 1). 692 693 %%%------------------ 694 695 -spec accumulator(state()) -> void_or_fate(). 696 accumulator(#es{accumulator = X}) -> 697 X. 698 699 -spec set_accumulator(void_or_fate(), state()) -> state(). 700 set_accumulator(X, ES) -> 701 ES#es{accumulator = X}. 702 703 %%%------------------ 704 705 -spec accumulator_stack(state()) -> [aeb_fate_data:fate_type()]. 706 accumulator_stack(#es{accumulator_stack = X}) -> 707 X. 708 709 -spec set_accumulator_stack([aeb_fate_data:fate_type()], state()) -> state(). 710 set_accumulator_stack(X, ES) -> 711 ES#es{accumulator_stack = X}. 712 713 %%%------------------ 714 715 -spec bbs(state()) -> map(). 716 bbs(#es{bbs = X}) -> 717 X. 718 719 -spec set_bbs(map(), state()) -> state(). 720 set_bbs(X, ES) -> 721 ES#es{bbs = X}. 722 723 %%%------------------ 724 725 -spec call_stack(state()) -> list(). 726 call_stack(#es{call_stack = X}) -> 727 X. 728 729 -spec set_call_stack(list(), state()) -> state(). 730 set_call_stack(X, ES) -> 731 ES#es{call_stack = X}. 732 733 %%%------------------ 734 735 -spec call_value(state()) -> non_neg_integer(). 736 call_value(#es{call_value = X}) -> 737 X. 738 739 -spec set_call_value(non_neg_integer(), state()) -> state(). 740 set_call_value(X, ES) when is_integer(X), X >= 0 -> 741 ES#es{call_value = X}. 742 743 %%%------------------ 744 745 -spec caller(state()) -> aeb_fate_data:fate_address(). 746 caller(#es{caller = X}) -> 747 X. 748 749 -spec set_caller(aeb_fate_data:fate_address(), state()) -> state(). 750 set_caller(X, ES) -> 751 ES#es{caller = X}. 752 753 %%%------------------ 754 755 -spec chain_api(state()) -> aefa_chain_api:state(). 756 chain_api(#es{chain_api = X}) -> 757 X. 758 759 -spec set_chain_api(aefa_chain_api:state(), state()) -> state(). 760 set_chain_api(X, ES) -> 761 ES#es{chain_api = X}. 762 763 %%%------------------ 764 765 -spec code_cache(state()) -> map(). 766 code_cache(#es{code_cache = X}) -> 767 X. 768 769 -spec set_code_cache(map(), state()) -> state(). 770 set_code_cache(X, ES) -> 771 ES#es{code_cache = X}. 772 773 %%%------------------ 774 775 -spec creator_cache(state()) -> map(). 776 creator_cache(#es{creator_cache = X}) -> 777 X. 778 779 -spec set_creator_cache(map(), state()) -> state(). 780 set_creator_cache(X, ES) -> 781 ES#es{creator_cache = X}. 782 783 %%%------------------ 784 785 -spec current_bb(state()) -> non_neg_integer(). 786 current_bb(#es{current_bb = X}) -> 787 X. 788 789 -spec set_current_bb(non_neg_integer(), state()) -> state(). 790 set_current_bb(X, ES) -> 791 ES#es{current_bb = X}. 792 793 %%%------------------ 794 795 -spec current_contract(state()) -> pubkey(). 796 current_contract(#es{current_contract = X}) -> 797 X. 798 799 -spec set_current_contract(pubkey(), state()) -> state(). 800 set_current_contract(X, ES) -> 801 ES#es{current_contract = X}. 802 803 %%%------------------ 804 805 -spec current_function(state()) -> binary(). 806 current_function(#es{current_function = X}) -> 807 X. 808 809 -spec set_current_function(binary(), state()) -> state(). 810 set_current_function(X, ES) -> 811 ES#es{current_function = X}. 812 813 %%%------------------ 814 815 -spec current_tvars(state()) -> map(). 816 current_tvars(#es{current_tvars = X}) -> 817 X. 818 819 -spec set_current_tvars(map(), state()) -> state(). 820 set_current_tvars(X, ES) -> 821 ES#es{current_tvars = X}. 822 823 %%%------------------ 824 825 -spec functions(state()) -> map(). 826 functions(#es{functions = X}) -> 827 X. 828 829 -spec set_functions(map(), state()) -> state(). 830 set_functions(X, ES) -> 831 ES#es{functions = X}. 832 833 %%%------------------ 834 835 -spec gas(state()) -> integer(). 836 gas(#es{gas = X}) -> 837 X. 838 839 -spec set_gas(integer(), state()) -> state(). 840 set_gas(X, ES) -> 841 ES#es{gas = X}. 842 843 %%%------------------ 844 845 -spec logs(state()) -> list(). 846 logs(#es{logs = X}) -> 847 X. 848 849 -spec add_log(term(), state()) -> state(). 850 add_log(X, ES) -> 851 ES#es{logs = [X | ES#es.logs]}. 852 853 %%%------------------ 854 855 -spec memory(state()) -> map(). 856 memory(#es{memory = X}) -> 857 X. 858 859 -spec set_memory(map(), state()) -> state(). 860 set_memory(X, ES) -> 861 ES#es{memory = X}. 862 863 %%%------------------ 864 865 -spec stores(state()) -> aefa_stores:store(). 866 stores(#es{stores=X}) -> 867 X. 868 869 -spec set_stores(aefa_stores:store(), state()) -> state(). 870 set_stores(X, ES) -> 871 ES#es{stores=X}. 872 873 %%%------------------ 874 875 -spec trace(state()) -> list(). 876 trace(#es{trace = X}) -> 877 X. 878 879 -spec set_trace(list(), state()) -> state(). 880 set_trace(X, ES) -> 881 ES#es{trace = X}. 882 883 %%%------------------ 884 -spec vm_version(state()) -> non_neg_integer(). 885 vm_version(#es{vm_version = X}) -> 886 X. 887 888 %%%------------------ 889 -spec consensus_version(state()) -> non_neg_integer(). 890 consensus_version(#es{chain_api = Api}) -> 891 TxEnv = aefa_chain_api:tx_env(Api), 892 aetx_env:consensus_version(TxEnv). 893 894 %%%------------------ 895 896 -ifdef(DEBUG_INFO). 897 debug_info(#es{debug_info = Info}) -> 898 Info. 899 900 set_debug_info(Info, ES) -> 901 ES#es{debug_info = Info}. 902 -endif.