From c00c4a5ac39eb6a5f2d4f9c06c67e05a60920243 Mon Sep 17 00:00:00 2001 From: Robert Virding Date: Wed, 20 Mar 2019 17:20:48 +0100 Subject: [PATCH] First commit of moved functions Most function references to aeso_ have been converted to aeb_. --- .gitignore | 2 + include/aeb_heap.hrl | 15 +++ src/aeb_abi.erl | 209 ++++++++++++++++++++++++++++++ src/aeb_blake2.erl | 149 +++++++++++++++++++++ src/aeb_heap.erl | 301 +++++++++++++++++++++++++++++++++++++++++++ src/aeb_icode.hrl | 68 ++++++++++ src/aeb_memory.erl | 19 +++ 7 files changed, 763 insertions(+) create mode 100644 include/aeb_heap.hrl create mode 100644 src/aeb_abi.erl create mode 100644 src/aeb_blake2.erl create mode 100644 src/aeb_heap.erl create mode 100644 src/aeb_icode.hrl create mode 100644 src/aeb_memory.erl diff --git a/.gitignore b/.gitignore index 1887839..6c6ddc1 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,5 @@ include/aeb_fate_opcodes.hrl src/aeb_fate_code.erl src/aeb_fate_opcodes.erl src/aeb_fate_pp.erl +*.erl~ +*.aes~ diff --git a/include/aeb_heap.hrl b/include/aeb_heap.hrl new file mode 100644 index 0000000..a37d97a --- /dev/null +++ b/include/aeb_heap.hrl @@ -0,0 +1,15 @@ + +-record(pmap, {key_t :: aeso_sophia:type(), + val_t :: aeso_sophia:type(), + parent :: none | non_neg_integer(), + size = 0 :: non_neg_integer(), + data :: #{aeso_heap:binary_value() => aeso_heap:binary_value() | tombstone} + | stored}). + +-record(maps, { maps = #{} :: #{ non_neg_integer() => #pmap{} } + , next_id = 0 :: non_neg_integer() }). + +-record(heap, { maps :: #maps{}, + offset :: aeso_heap:offset(), + heap :: binary() | #{non_neg_integer() => non_neg_integer()} }). + diff --git a/src/aeb_abi.erl b/src/aeb_abi.erl new file mode 100644 index 0000000..360348d --- /dev/null +++ b/src/aeb_abi.erl @@ -0,0 +1,209 @@ +%%%------------------------------------------------------------------- +%%% @copyright (C) 2017, Aeternity Anstalt +%%% @doc +%%% Encode and decode data and function calls according to +%%% Sophia-AEVM-ABI. +%%% @end +%%% Created : 25 Jan 2018 +%%% +%%%------------------------------------------------------------------- +-module(aeb_abi). +-define(HASH_SIZE, 32). + +-export([ old_create_calldata/3 + , create_calldata/4 + , check_calldata/2 + , function_type_info/3 + , function_type_hash/3 + , arg_typerep_from_function/2 + , type_hash_from_function_name/2 + , typereps_from_type_hash/2 + , function_name_from_type_hash/2 + , get_function_hash_from_calldata/1 + ]). + +-type hash() :: <<_:256>>. %% 256 = ?HASH_SIZE * 8. +-type function_name() :: binary(). %% String +-type typerep() :: aeso_sophia:type(). +-type function_type_info() :: { FunctionHash :: hash() + , FunctionName :: function_name() + , ArgType :: binary() %% binary typerep + , OutType :: binary() %% binary typerep + }. +-type type_info() :: [function_type_info()]. + +%%%=================================================================== +%%% API +%%%=================================================================== + +%%%=================================================================== +%%% Handle calldata + +create_calldata(FunName, Args, ArgTypes0, RetType) -> + ArgTypes = {tuple, ArgTypes0}, + <> = + function_type_hash(list_to_binary(FunName), ArgTypes, RetType), + Data = aeb_heap:to_binary({TypeHashInt, list_to_tuple(Args)}), + {ok, Data, {tuple, [word, ArgTypes]}, RetType}. + +get_type_info_and_hash(#{type_info := TypeInfo}, FunName) -> + FunBin = list_to_binary(FunName), + case type_hash_from_function_name(FunBin, TypeInfo) of + {ok, <>} -> {ok, TypeInfo, TypeHashInt}; + {ok, _} -> {error, bad_type_hash}; + {error, _} = Err -> Err + end. + +-spec check_calldata(binary(), type_info()) -> + {'ok', typerep(), typerep()} | {'error', atom()}. +check_calldata(CallData, TypeInfo) -> + %% The first element of the CallData should be the function name + case get_function_hash_from_calldata(CallData) of + {ok, Hash} -> + case typereps_from_type_hash(Hash, TypeInfo) of + {ok, ArgType, OutType} -> + try aeb_heap:from_binary({tuple, [word, ArgType]}, CallData) of + {ok, _Something} -> + {ok, {tuple, [word, ArgType]}, OutType}; + {error, _} -> + {error, bad_call_data} + catch + _T:_E -> + {error, bad_call_data} + end; + {error, _} -> + {error, unknown_function} + end; + {error, _What} -> + {error, bad_call_data} + end. + +-spec get_function_hash_from_calldata(CallData::binary()) -> + {ok, binary()} | {error, term()}. +get_function_hash_from_calldata(CallData) -> + case aeb_heap:from_binary({tuple, [word]}, CallData) of + {ok, {HashInt}} -> {ok, <>}; + {error, _} = Error -> Error + end. + +%%%=================================================================== +%%% Handle type info from contract meta data + +-spec function_type_info(function_name(), [typerep()], typerep()) -> + function_type_info(). +function_type_info(Name, ArgTypes, OutType) -> + ArgType = {tuple, ArgTypes}, + { function_type_hash(Name, ArgType, OutType) + , Name + , aeb_heap:to_binary(ArgType) + , aeb_heap:to_binary(OutType) + }. + +-spec function_type_hash(function_name(), typerep(), typerep()) -> hash(). +function_type_hash(Name, ArgType, OutType) when is_binary(Name) -> + Bin = iolist_to_binary([ Name + , aeb_heap:to_binary(ArgType) + , aeb_heap:to_binary(OutType) + ]), + %% Calculate a 256 bit digest BLAKE2b hash value of a binary + {ok, Hash} = aeb_blake2:blake2b(?HASH_SIZE, Bin), + Hash. + +-spec arg_typerep_from_function(function_name(), type_info()) -> + {'ok', typerep()} | {'error', 'bad_type_data' | 'unknown_function'}. +arg_typerep_from_function(Function, TypeInfo) -> + case lists:keyfind(Function, 2, TypeInfo) of + {_TypeHash, Function, ArgTypeBin,_OutTypeBin} -> + case aeb_heap:from_binary(typerep, ArgTypeBin) of + {ok, ArgType} -> {ok, ArgType}; + {error,_} -> {error, bad_type_data} + end; + false -> + {error, unknown_function} + end. + +-spec typereps_from_type_hash(hash(), type_info()) -> + {'ok', typerep(), typerep()} | {'error', 'bad_type_data' | 'unknown_function'}. +typereps_from_type_hash(TypeHash, TypeInfo) -> + case lists:keyfind(TypeHash, 1, TypeInfo) of + {TypeHash,_Function, ArgTypeBin, OutTypeBin} -> + case {aeb_heap:from_binary(typerep, ArgTypeBin), + aeb_heap:from_binary(typerep, OutTypeBin)} of + {{ok, ArgType}, {ok, OutType}} -> {ok, ArgType, OutType}; + {_, _} -> {error, bad_type_data} + end; + false -> + {error, unknown_function} + end. + +-spec function_name_from_type_hash(hash(), type_info()) -> + {'ok', function_name()} + | {'error', 'unknown_function'}. +function_name_from_type_hash(TypeHash, TypeInfo) -> + case lists:keyfind(TypeHash, 1, TypeInfo) of + {TypeHash, Function,_ArgTypeBin,_OutTypeBin} -> + {ok, Function}; + false -> + {error, unknown_function} + end. + +-spec type_hash_from_function_name(function_name(), type_info()) -> + {'ok', hash()} + | {'error', 'unknown_function'}. +type_hash_from_function_name(Name, TypeInfo) -> + case lists:keyfind(Name, 2, TypeInfo) of + {TypeHash, Name,_ArgTypeBin,_OutTypeBin} -> + {ok, TypeHash}; + false -> + {error, unknown_function} + end. + +%% -- Old calldata creation. Kept for backwards compatibility. --------------- + +old_create_calldata(Contract, Function, Argument) when is_map(Contract) -> + case aeb_constants:string(Argument) of + {ok, {tuple, _, _} = Tuple} -> + old_encode_call(Contract, Function, Tuple); + {ok, {unit, _} = Tuple} -> + old_encode_call(Contract, Function, Tuple); + {ok, ParsedArgument} -> + %% The Sophia compiler does not parse a singleton tuple (42) as a tuple, + %% Wrap it in a tuple. + old_encode_call(Contract, Function, {tuple, [], [ParsedArgument]}); + {error, _} -> + {error, argument_syntax_error} + end. + +%% Call takes one arument. +%% Use a tuple to pass multiple arguments. +old_encode_call(Contract, Function, ArgumentAst) -> + Argument = old_ast_to_erlang(ArgumentAst), + case get_type_info_and_hash(Contract, Function) of + {ok, TypeInfo, TypeHashInt} -> + Data = aeb_heap:to_binary({TypeHashInt, Argument}), + case check_calldata(Data, TypeInfo) of + {ok, CallDataType, OutType} -> + {ok, Data, CallDataType, OutType}; + {error, _} = Err -> + Err + end; + {error, _} = Err -> Err + end. + +old_ast_to_erlang({int, _, N}) -> N; +old_ast_to_erlang({hash, _, <>}) -> N; +old_ast_to_erlang({hash, _, <>}) -> {Hi, Lo}; %% signature +old_ast_to_erlang({bool, _, true}) -> 1; +old_ast_to_erlang({bool, _, false}) -> 0; +old_ast_to_erlang({string, _, Bin}) -> Bin; +old_ast_to_erlang({unit, _}) -> {}; +old_ast_to_erlang({con, _, "None"}) -> none; +old_ast_to_erlang({app, _, {con, _, "Some"}, [A]}) -> {some, old_ast_to_erlang(A)}; +old_ast_to_erlang({tuple, _, Elems}) -> + list_to_tuple(lists:map(fun old_ast_to_erlang/1, Elems)); +old_ast_to_erlang({list, _, Elems}) -> + lists:map(fun old_ast_to_erlang/1, Elems); +old_ast_to_erlang({map, _, Elems}) -> + maps:from_list([ {old_ast_to_erlang(element(1, Elem)), old_ast_to_erlang(element(2, Elem))} + || Elem <- Elems ]). + diff --git a/src/aeb_blake2.erl b/src/aeb_blake2.erl new file mode 100644 index 0000000..c5a849f --- /dev/null +++ b/src/aeb_blake2.erl @@ -0,0 +1,149 @@ +%%%============================================================================= +%%% @copyright (C) 2019, Aeternity Anstalt +%%% @doc +%%% BLAKE2b implementation in Erlang - for details see: https://blake2.net +%%% @end +%%%============================================================================= + +-module(aeb_blake2). + +-export([ blake2b/2 + , blake2b/3 + ]). + +-define(MAX_64BIT, 16#ffffffffffffffff). + +-spec blake2b(HashLen :: integer(), Msg :: binary()) -> {ok, binary()}. +blake2b(HashLen, Msg) -> + blake2b(HashLen, Msg, <<>>). + +-spec blake2b(HashLen :: integer(), Msg :: binary(), Key :: binary()) -> {ok, binary()}. +blake2b(HashLen, Msg0, Key) -> + %% If message should be keyed, prepend message with padded key. + Msg = <<(pad(128, Key))/binary, Msg0/binary>>, + + %% Set up the initial state + Init = (16#01010000 + (byte_size(Key) bsl 8) + HashLen), + <> = blake_iv(), + H = <<(H0 bxor Init):64, H1_7/binary>>, + + %% Perform the compression - message will be chopped into 128-byte chunks. + State = blake2b_compress(H, Msg, 0), + + %% Just return the requested part of the hash + {ok, binary_part(to_little_endian(State), {0, HashLen})}. + +blake2b_compress(H, <>, BCompr) when Rest /= <<>> -> + H1 = blake2b_compress(H, <>, BCompr + 128, false), + blake2b_compress(H1, Rest, BCompr + 128); +blake2b_compress(H, SmallChunk, BCompr) -> + Size = byte_size(SmallChunk), + FillSize = (128 - Size) * 8, + blake2b_compress(H, <>, BCompr + Size, true). + +blake2b_compress(H, Chunk0, BCompr, Last) -> + Chunk = to_big_endian(Chunk0), + <> = <>, + V12_ = V12 bxor (BCompr band ?MAX_64BIT), + V13_ = V13 bxor ((BCompr bsr 64) band ?MAX_64BIT), + V14_ = case Last of + false -> V14; + true -> V14 bxor ?MAX_64BIT + end, + V = <>, + + <> = + lists:foldl(fun(Round, Vx) -> blake2b_mix(Round, Chunk, Vx) end, V, lists:seq(0, 11)), + + <> = H, + <<((HInt bxor VLow) bxor VHigh):(8*64)>>. + +blake2b_mix(Rnd, Chunk, V) -> + <> = V, + <> = Chunk, + Ms = {M0, M1, M2, M3, M4, M5, M6, M7, M8, M9, M10, M11, M12, M13, M14, M15}, + M = fun(Ix) -> element(Ix+1, Ms) end, + + [S0, S1, S2, S3, S4, S5, S6, S7, S8, S9, S10, S11, S12, S13, S14, S15] = sigma(Rnd rem 10), + + {Vx0, Vx4, Vx8, Vx12} = blake2b_mix(V0, V4, V8, V12, M(S0), M(S1)), + {Vx1, Vx5, Vx9, Vx13} = blake2b_mix(V1, V5, V9, V13, M(S2), M(S3)), + {Vx2, Vx6, Vx10, Vx14} = blake2b_mix(V2, V6, V10, V14, M(S4), M(S5)), + {Vx3, Vx7, Vx11, Vx15} = blake2b_mix(V3, V7, V11, V15, M(S6), M(S7)), + + {Vy0, Vy5, Vy10, Vy15} = blake2b_mix(Vx0, Vx5, Vx10, Vx15, M(S8), M(S9)), + {Vy1, Vy6, Vy11, Vy12} = blake2b_mix(Vx1, Vx6, Vx11, Vx12, M(S10), M(S11)), + {Vy2, Vy7, Vy8, Vy13} = blake2b_mix(Vx2, Vx7, Vx8, Vx13, M(S12), M(S13)), + {Vy3, Vy4, Vy9, Vy14} = blake2b_mix(Vx3, Vx4, Vx9, Vx14, M(S14), M(S15)), + + <>. + +blake2b_mix(Va, Vb, Vc, Vd, X, Y) -> + Va1 = (Va + Vb + X) band ?MAX_64BIT, + Vd1 = rotr64(32, Vd bxor Va1), + + Vc1 = (Vc + Vd1) band ?MAX_64BIT, + Vb1 = rotr64(24, Vb bxor Vc1), + + Va2 = (Va1 + Vb1 + Y) band ?MAX_64BIT, + Vd2 = rotr64(16, Va2 bxor Vd1), + + Vc2 = (Vc1 + Vd2) band ?MAX_64BIT, + Vb2 = rotr64(63, Vb1 bxor Vc2), + + {Va2, Vb2, Vc2, Vd2}. + +blake_iv() -> + IV0 = 16#6A09E667F3BCC908, + IV1 = 16#BB67AE8584CAA73B, + IV2 = 16#3C6EF372FE94F82B, + IV3 = 16#A54FF53A5F1D36F1, + IV4 = 16#510E527FADE682D1, + IV5 = 16#9B05688C2B3E6C1F, + IV6 = 16#1F83D9ABFB41BD6B, + IV7 = 16#5BE0CD19137E2179, + <>. + +sigma(N) -> + {_, Row} = lists:keyfind(N, 1, sigma()), Row. + +sigma() -> + [{0, [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]}, + {1, [14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3]}, + {2, [11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4]}, + {3, [ 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8]}, + {4, [ 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13]}, + {5, [ 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9]}, + {6, [12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11]}, + {7, [13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10]}, + {8, [ 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5]}, + {9, [10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0]}]. + +rotr64(N, I64) -> + <> = rotr641(N, <>), + I64rot. + +rotr641(16, <>) -> <>; +rotr641(24, <>) -> <>; +rotr641(32, <>) -> <>; +rotr641(63, <>) -> <>. + +pad(N, Bin) -> + case (N - (byte_size(Bin) rem N)) rem N of + 0 -> Bin; + Pad -> <> + end. + +to_big_endian(Bin) -> to_big_endian(Bin, <<>>). +to_big_endian(<<>>, Acc) -> Acc; +to_big_endian(<>, Acc) -> + to_big_endian(Rest, <>). + +to_little_endian(Bin) -> to_little_endian(Bin, <<>>). +to_little_endian(<<>>, Acc) -> Acc; +to_little_endian(<>, Acc) -> + to_little_endian(Rest, <>). + diff --git a/src/aeb_heap.erl b/src/aeb_heap.erl new file mode 100644 index 0000000..906cfe7 --- /dev/null +++ b/src/aeb_heap.erl @@ -0,0 +1,301 @@ +-module(aeb_heap). + +-export([ to_binary/1 + , to_binary/2 + , from_heap/3 + , from_binary/2 + , from_binary/3 + , maps_with_next_id/1 + , set_next_id/2 + , heap_fragment/3 + , heap_value/3 + , heap_value/4 + , heap_value_pointer/1 + , heap_value_maps/1 + , heap_value_offset/1 + , heap_value_heap/1 + , heap_fragment_maps/1 + , heap_fragment_offset/1 + , heap_fragment_heap/1 + ]). + +-export_type([binary_value/0, heap_value/0, offset/0, heap_fragment/0]). + +-include("aeb_icode.hrl"). +-include_lib("aebytecode/include/aeb_heap.hrl"). + +-type word() :: non_neg_integer(). +-type pointer() :: word(). +-opaque heap_fragment() :: #heap{}. +-type offset() :: non_neg_integer(). +-type binary_value() :: binary(). +-type heap_value() :: {pointer(), heap_fragment()}. + + +-spec maps_with_next_id(heap_fragment()) -> #maps{}. +%% Create just a maps value, don't keep rest of Heap +maps_with_next_id(#heap{maps = #maps{next_id = N}}) -> + #maps{ next_id = N }. + +-spec set_next_id(heap_fragment(), non_neg_integer()) -> heap_fragment(). +set_next_id(Heap, N) -> + Heap#heap{ maps = Heap#heap.maps#maps{ next_id = N } }. + +%% -- data type heap_fragment + +-spec heap_fragment(binary() | #{non_neg_integer() => non_neg_integer()}) -> heap_fragment(). +heap_fragment(Heap) -> + heap_fragment(#maps{ next_id = 0 }, 0, Heap). + +-spec heap_fragment(#maps{}, offset(), + binary() | #{non_neg_integer() => non_neg_integer()}) -> heap_fragment(). +heap_fragment(Maps, Offset, Heap) -> + #heap{maps = Maps, offset = Offset, heap = Heap}. + +-spec heap_fragment_maps(heap_fragment()) -> #maps{}. +heap_fragment_maps(#heap{maps = Maps}) -> + Maps. + +-spec heap_fragment_offset(heap_fragment()) -> offset(). +heap_fragment_offset(#heap{offset = Offs}) -> + Offs. + +-spec heap_fragment_heap(heap_fragment()) -> binary() | #{non_neg_integer() => non_neg_integer()}. +heap_fragment_heap(#heap{heap = Heap}) -> + Heap. + + +%% -- data type heap_value + +-spec heap_value(#maps{}, pointer(), + binary() | #{non_neg_integer() => non_neg_integer()}) -> heap_value(). +heap_value(Maps, Ptr, Heap) -> + heap_value(Maps, Ptr, Heap, 0). + +-spec heap_value(#maps{}, pointer(), + binary() | #{non_neg_integer() => non_neg_integer()}, offset()) -> heap_value(). +heap_value(Maps, Ptr, Heap, Offs) -> + {Ptr, heap_fragment(Maps, Offs, Heap)}. + +-spec heap_value_pointer(heap_value()) -> pointer(). +heap_value_pointer({Ptr, _}) -> Ptr. + +-spec heap_value_maps(heap_value()) -> #maps{}. +heap_value_maps({_, Heap}) -> Heap#heap.maps. + +-spec heap_value_offset(heap_value()) -> offset(). +heap_value_offset({_, Heap}) -> Heap#heap.offset. + +-spec heap_value_heap(heap_value()) -> + binary() | #{non_neg_integer() => non_neg_integer()}. +heap_value_heap({_, Heap}) -> Heap#heap.heap. + +%% -- Value to binary -------------------------------------------------------- + +-spec to_binary(aeso_sophia:data()) -> aeso_sophia:heap(). +%% Encode the data as a heap where the first word is the value (for unboxed +%% types) or a pointer to the value (for boxed types). +to_binary(Data) -> + to_binary(Data, 0). + +to_binary(Data, BaseAddress) -> + {Address, Memory} = to_binary1(Data, BaseAddress + 32), + R = <>, + R. + + +%% Allocate the data in memory, from the given address. Return a pair +%% of memory contents from that address and the value representing the +%% data. +to_binary1(Data,_Address) when is_integer(Data) -> + {Data,<<>>}; +to_binary1(Data, Address) when is_binary(Data) -> + %% a string + Words = aeb_memory:binary_to_words(Data), + {Address,<<(size(Data)):256, << <> || W <- Words>>/binary>>}; +to_binary1(none, Address) -> to_binary1({variant, 0, []}, Address); +to_binary1({some, Value}, Address) -> to_binary1({variant, 1, [Value]}, Address); +to_binary1(word, Address) -> to_binary1({?TYPEREP_WORD_TAG}, Address); +to_binary1(string, Address) -> to_binary1({?TYPEREP_STRING_TAG}, Address); +to_binary1(typerep, Address) -> to_binary1({?TYPEREP_TYPEREP_TAG}, Address); +to_binary1(function, Address) -> to_binary1({?TYPEREP_FUN_TAG}, Address); +to_binary1({list, T}, Address) -> to_binary1({?TYPEREP_LIST_TAG, T}, Address); +to_binary1({option, T}, Address) -> to_binary1({variant, [[], [T]]}, Address); +to_binary1({tuple, Ts}, Address) -> to_binary1({?TYPEREP_TUPLE_TAG, Ts}, Address); +to_binary1({variant, Cons}, Address) -> to_binary1({?TYPEREP_VARIANT_TAG, Cons}, Address); +to_binary1({map, K, V}, Address) -> to_binary1({?TYPEREP_MAP_TAG, K, V}, Address); +to_binary1({variant, Tag, Args}, Address) -> + to_binary1(list_to_tuple([Tag | Args]), Address); +to_binary1(Map, Address) when is_map(Map) -> + Size = maps:size(Map), + %% Sort according to binary ordering + KVs = lists:sort([ {to_binary(K), to_binary(V)} || {K, V} <- maps:to_list(Map) ]), + {Address, <> || {K, V} <- KVs >>/binary >>}; +to_binary1({}, _Address) -> + {0, <<>>}; +to_binary1(Data, Address) when is_tuple(Data) -> + {Elems,Memory} = to_binaries(tuple_to_list(Data),Address+32*size(Data)), + ElemsBin = << <> || W <- Elems>>, + {Address,<< ElemsBin/binary, Memory/binary >>}; +to_binary1([],_Address) -> + <> = <<(-1):256>>, + {Nil,<<>>}; +to_binary1([H|T],Address) -> + to_binary1({H,T},Address). + + +to_binaries([],_Address) -> + {[],<<>>}; +to_binaries([H|T],Address) -> + {HRep,HMem} = to_binary1(H,Address), + {TRep,TMem} = to_binaries(T,Address+size(HMem)), + {[HRep|TRep],<>}. + +%% Interpret a return value (a binary) using a type rep. + +-spec from_heap(Type :: ?Type(), Heap :: binary(), Ptr :: integer()) -> + {ok, term()} | {error, term()}. +from_heap(Type, Heap, Ptr) -> + try {ok, from_binary(#{}, Type, Heap, Ptr)} + catch _:Err -> + %% io:format("** Error: from_heap failed with ~p\n ~p\n", [Err, erlang:get_stacktrace()]), + {error, Err} + end. + +%% Base address is the address of the first word of the given heap. +-spec from_binary(T :: ?Type(), + Heap :: binary(), + BaseAddr :: non_neg_integer()) -> + {ok, term()} | {error, term()}. +from_binary(T, Heap = <>, BaseAddr) -> + from_heap(T, <<0:BaseAddr/unit:8, Heap/binary>>, V); +from_binary(_, Bin, _BaseAddr) -> + {error, {binary_too_short, Bin}}. + +-spec from_binary(?Type(), binary()) -> {ok, term()} | {error, term()}. +from_binary(T, Heap) -> + from_binary(T, Heap, 0). + +from_binary(_, word, _, V) -> + V; +from_binary(_, signed_word, _, V) -> + <> = <>, + N; +from_binary(_, bool, _, V) -> + case V of + 0 -> false; + 1 -> true + end; +from_binary(_, string, Heap, V) -> + StringSize = heap_word(Heap,V), + BitAddr = 8*(V+32), + <<_:BitAddr,Bytes:StringSize/binary,_/binary>> = Heap, + Bytes; +from_binary(_, {tuple, []}, _, _) -> + {}; +from_binary(Visited, {tuple,Cpts}, Heap, V) -> + check_circular_refs(Visited, V), + NewVisited = Visited#{V => true}, + ElementNums = lists:seq(0, length(Cpts)-1), + TypesAndPointers = lists:zip(Cpts, ElementNums), + ElementAddress = fun(Index) -> V + 32 * Index end, + Element = fun(Index) -> + heap_word(Heap, ElementAddress(Index)) + end, + Convert = fun(Type, Index) -> + from_binary(NewVisited, Type, Heap, Element(Index)) + end, + Elements = [Convert(T, I) || {T,I} <- TypesAndPointers], + list_to_tuple(Elements); +from_binary(Visited, {list, Elem}, Heap, V) -> + <> = <<(-1):256>>, + if V==Nil -> + []; + true -> + {H,T} = from_binary(Visited, {tuple,[Elem,{list,Elem}]},Heap,V), + [H|T] + end; +from_binary(Visited, {option, A}, Heap, V) -> + from_binary(Visited, {variant_t, [{none, []}, {some, [A]}]}, Heap, V); +from_binary(Visited, {variant, Cons}, Heap, V) -> + Tag = heap_word(Heap, V), + Args = lists:nth(Tag + 1, Cons), + Visited1 = Visited#{V => true}, + {variant, Tag, tuple_to_list(from_binary(Visited1, {tuple, Args}, Heap, V + 32))}; +from_binary(Visited, {variant_t, TCons}, Heap, V) -> %% Tagged variants + {Tags, Cons} = lists:unzip(TCons), + {variant, I, Args} = from_binary(Visited, {variant, Cons}, Heap, V), + Tag = lists:nth(I + 1, Tags), + case Args of + [] -> Tag; + _ -> list_to_tuple([Tag | Args]) + end; +from_binary(_Visited, {map, A, B}, Heap, Ptr) -> + %% FORMAT: [Size] [KeySize] Key [ValSize] Val .. [KeySize] Key [ValSize] Val + Size = heap_word(Heap, Ptr), + map_binary_to_value(A, B, Size, Heap, Ptr + 32); +from_binary(Visited, typerep, Heap, V) -> + check_circular_refs(Visited, V), + Tag = heap_word(Heap, V), + Arg1 = fun(T, I) -> from_binary(Visited#{V => true}, T, Heap, heap_word(Heap, V + 32 * I)) end, + Arg = fun(T) -> Arg1(T, 1) end, + case Tag of + ?TYPEREP_WORD_TAG -> word; + ?TYPEREP_STRING_TAG -> string; + ?TYPEREP_TYPEREP_TAG -> typerep; + ?TYPEREP_LIST_TAG -> {list, Arg(typerep)}; + ?TYPEREP_TUPLE_TAG -> {tuple, Arg({list, typerep})}; + ?TYPEREP_VARIANT_TAG -> {variant, Arg({list, {list, typerep}})}; + ?TYPEREP_MAP_TAG -> {map, Arg(typerep), Arg1(typerep, 2)}; + ?TYPEREP_FUN_TAG -> function + end. + +map_binary_to_value(KeyType, ValType, N, Bin, Ptr) -> + %% Avoid looping on bogus sizes + MaxN = byte_size(Bin) div 64, + Heap = heap_fragment(Bin), + map_from_binary({value, KeyType, ValType}, min(N, MaxN), Heap, Ptr, #{}). + +map_from_binary(_, 0, _, _, Map) -> Map; +map_from_binary({value, KeyType, ValType} = Output, I, Heap, Ptr, Map) -> + KeySize = get_word(Heap, Ptr), + KeyPtr = Ptr + 32, + KeyBin = get_chunk(Heap, KeyPtr, KeySize), + ValSize = get_word(Heap, KeyPtr + KeySize), + ValPtr = KeyPtr + KeySize + 32, + ValBin = get_chunk(Heap, ValPtr, ValSize), + %% Keys and values are self contained binaries + {ok, Key} = from_binary(KeyType, KeyBin), + {ok, Val} = from_binary(ValType, ValBin), + map_from_binary(Output, I - 1, Heap, ValPtr + ValSize, Map#{Key => Val}). + +check_circular_refs(Visited, V) -> + case maps:is_key(V, Visited) of + true -> exit(circular_references); + false -> ok + end. + +heap_word(Heap, Addr) when is_binary(Heap) -> + BitSize = 8*Addr, + <<_:BitSize,W:256,_/binary>> = Heap, + W; +heap_word(Heap, Addr) when is_map(Heap) -> + 0 = Addr rem 32, %% Check that it's word aligned. + maps:get(Addr, Heap, 0). + +get_word(#heap{offset = Offs, heap = Mem}, Addr) when Addr >= Offs -> + get_word(Mem, Addr - Offs); +get_word(Mem, Addr) when is_binary(Mem) -> + <<_:Addr/unit:8, Word:256, _/binary>> = Mem, + Word. + +get_chunk(#heap{offset = Offs, heap = Mem}, Addr, Bytes) when Addr >= Offs -> + get_chunk(Mem, Addr - Offs, Bytes); +get_chunk(Mem, Addr, Bytes) when is_binary(Mem) -> + <<_:Addr/unit:8, Chunk:Bytes/binary, _/binary>> = Mem, + Chunk. + + + + diff --git a/src/aeb_icode.hrl b/src/aeb_icode.hrl new file mode 100644 index 0000000..56da80a --- /dev/null +++ b/src/aeb_icode.hrl @@ -0,0 +1,68 @@ + +-define(Type(), aeso_sophia:type()). + +-define(TYPEREP_WORD_TAG, 0). +-define(TYPEREP_STRING_TAG, 1). +-define(TYPEREP_LIST_TAG, 2). +-define(TYPEREP_TUPLE_TAG, 3). +-define(TYPEREP_VARIANT_TAG, 4). +-define(TYPEREP_TYPEREP_TAG, 5). +-define(TYPEREP_MAP_TAG, 6). +-define(TYPEREP_FUN_TAG, 7). + +-record(arg, {name::string(), type::?Type()}). + +-type expr() :: term(). +-type arg() :: #arg{name::string(), type::?Type()}. +-type arg_list() :: [arg()]. + +-record(fun_dec, { name :: string() + , args :: arg_list() + , body :: expr()}). + +-record(var_ref, { name :: string() | list(string()) | {builtin, atom() | tuple()}}). + +-record(prim_call_contract, + { gas :: expr() + , address :: expr() + , value :: expr() + , arg :: expr() + , type_hash:: expr() + }). + +-record(prim_balance, { address :: expr() }). +-record(prim_block_hash, { height :: expr() }). +-record(prim_put, { state :: expr() }). + +-record(integer, {value :: integer()}). + +-record(tuple, {cpts :: [expr()]}). + +-record(list, {elems :: [expr()]}). + +-record(unop, { op :: term() + , rand :: expr()}). + +-record(binop, { op :: term() + , left :: expr() + , right :: expr()}). + +-record(ifte, { decision :: expr() + , then :: expr() + , else :: expr()}). + +-record(switch, { expr :: expr() + , cases :: [{expr(),expr()}]}). + +-record(funcall, { function :: expr() + , args :: [expr()]}). + +-record(lambda, { args :: arg_list(), + body :: expr()}). + +-record(missing_field, { format :: string() + , args :: [term()]}). + +-record(seq, {exprs :: [expr()]}). + +-record(event, {topics :: [expr()], payload :: expr()}). diff --git a/src/aeb_memory.erl b/src/aeb_memory.erl new file mode 100644 index 0000000..1e07549 --- /dev/null +++ b/src/aeb_memory.erl @@ -0,0 +1,19 @@ +%%%------------------------------------------------------------------- +%%% @copyright (C) 2018, Aeternity Anstalt +%%% @doc +%%% Memory speifics that compiler and VM need to agree upon +%%% @end +%%% Created : 19 Dec 2018 +%%%------------------------------------------------------------------- + +-module(aeb_memory). + +-export([binary_to_words/1]). + +binary_to_words(<<>>) -> + []; +binary_to_words(<>) -> + [N|binary_to_words(Bin)]; +binary_to_words(Bin) -> + binary_to_words(<>). +