/ apps / aefate / src / aefa_engine_state.erl
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.