diff --git a/include/aeb_fate_data.hrl b/include/aeb_fate_data.hrl index 8ef98dd..958eae9 100644 --- a/include/aeb_fate_data.hrl +++ b/include/aeb_fate_data.hrl @@ -7,8 +7,7 @@ -define(FATE_MAP_T, #{ fate_type() => fate_type() }). -define(FATE_STRING_T, binary()). -define(FATE_ADDRESS_T, {address, <<_:256>>}). --define(FATE_HASH_T, {hash, binary()}). --define(FATE_SIGNATURE_T, {signature, binary()}). +-define(FATE_BYTES_T(N), {bytes, binary()}). -define(FATE_CONTRACT_T, {contract, <<_:256>>}). -define(FATE_ORACLE_T, {oracle, <<_:256>>}). -define(FATE_ORACLE_Q_T, {oracle_query, <<_:256>>}). @@ -25,8 +24,8 @@ -define(IS_FATE_MAP(X), (is_map(X))). -define(IS_FATE_TUPLE(X), (is_tuple(X) andalso (tuple == element(1, X) andalso is_tuple(element(2, X))))). -define(IS_FATE_ADDRESS(X), (is_tuple(X) andalso (address == element(1, X) andalso is_binary(element(2, X))))). --define(IS_FATE_HASH(X), (is_tuple(X) andalso (hash == element(1, X) andalso is_binary(element(2, X))))). --define(IS_FATE_SIGNATURE(X), (is_tuple(X) andalso (signature == element(1, X) andalso is_binary(element(2, X))))). +-define(IS_FATE_BYTES(X), (is_tuple(X) andalso (bytes == element(1, X) andalso is_binary(element(2, X))))). +-define(IS_FATE_BYTES(N, X), (?IS_FATE_BYTES(X) andalso byte_size(element(2, X)) == (N))). -define(IS_FATE_CONTRACT(X), (is_tuple(X) andalso (contract == element(1, X) andalso is_binary(element(2, X))))). -define(IS_FATE_ORACLE(X), (is_tuple(X) andalso (oracle == element(1, X) andalso is_binary(element(2, X))))). -define(IS_FATE_ORACLE_Q(X), (is_tuple(X) andalso (oracle_query == element(1, X) andalso is_binary(element(2, X))))). @@ -46,8 +45,7 @@ -define(FATE_UNIT, {tuple, {}}). -define(FATE_TUPLE(T), {tuple, T}). -define(FATE_ADDRESS(A), {address, A}). --define(FATE_HASH(X), {hash, X}). --define(FATE_SIGNATURE(S), {signature, S}). +-define(FATE_BYTES(X), {bytes, X}). -define(FATE_CONTRACT(X), {contract, X}). -define(FATE_ORACLE(X), {oracle, X}). -define(FATE_ORACLE_Q(X), {oracle_query, X}). @@ -62,8 +60,7 @@ -define(FATE_TUPLE_ELEMENTS(X), (tuple_to_list(element(2, X)))). -define(FATE_STRING_VALUE(X), (X)). -define(FATE_ADDRESS_VALUE(X), (element(2, X))). --define(FATE_HASH_VALUE(X), (element(2, X))). --define(FATE_SIGNATURE_VALUE(X), (element(2, X))). +-define(FATE_BYTES_VALUE(X), (element(2, X))). -define(FATE_CONTRACT_VALUE(X), (element(2, X))). -define(FATE_ORACLE_VALUE(X), (element(2, X))). -define(FATE_NAME_VALUE(X), (element(2, X))). diff --git a/quickcheck/aefate_code_eqc.erl b/quickcheck/aefate_code_eqc.erl index 29f7821..7938d7b 100644 --- a/quickcheck/aefate_code_eqc.erl +++ b/quickcheck/aefate_code_eqc.erl @@ -27,36 +27,42 @@ prop_serializes() -> in_parallel( ?FORALL(FateCode, fate_code(0), - ?WHENFAIL(eqc:format("Trying to serialize/deserialize ~p failed~n", [FateCode]), - begin - Binary = aeb_fate_code:serialize(FateCode), - ?WHENFAIL(eqc:format("serialized: ~p~n", [Binary]), - begin - Decoded = aeb_fate_code:deserialize(Binary), - measure(binary_size, size(Binary), - equals(Decoded, FateCode)) - end) - end))). + begin + {T0, Binary} = timer:tc(fun() -> aeb_fate_code:serialize(FateCode) end), + ?WHENFAIL(eqc:format("serialized:\n ~120p~n", [Binary]), + begin + {T1, Decoded} = timer:tc(fun() -> aeb_fate_code:deserialize(Binary) end), + measure(binary_size, size(Binary), + measure(serialize, T0 / 1000, + measure(deserialize, T1 / 1000, + conjunction([{equal, equals(Decoded, FateCode)}, + {serialize_time, T0 / 1000 < 500}, + {deserialize_time, T1 / 1000 < 500}])))) + end) + end)). prop_fail_serializes() -> conjunction([{Failure, eqc:counterexample( ?FORALL(FateCode, fate_code(Failure), ?FORALL(Binary, catch aeb_fate_code:serialize(FateCode), - is_binary(aeb_fate_code:serialize(FateCode))))) - =/= true} || Failure <- [1,2,3,4, 5] ]). + is_binary(Binary)))) + =/= true} || Failure <- [1, 2, 3, 4, 5] ]). prop_fuzz() -> in_parallel( ?FORALL(Binary, ?LET(FateCode, fate_code(0), aeb_fate_code:serialize(FateCode)), - ?FORALL(InjectedBin, injection(Binary), - try Org = aeb_fate_code:deserialize(InjectedBin), - NewBin = aeb_fate_code:serialize(Org), - NewOrg = aeb_fate_code:deserialize(NewBin), - ?WHENFAIL(eqc:format("Deserialize ~p gives\n~p\nSerializes to ~p\n", [InjectedBin, Org, NewOrg]), - equals(NewBin, InjectedBin)) - catch _:_ -> - true - end))). + ?FORALL(FuzzedBin, fuzz(Binary), + try aeb_fate_code:deserialize(FuzzedBin) of + Code -> + ?WHENFAIL(eqc:format("Code:\n ~p\n", [Code]), + begin + Bin1 = aeb_fate_code:serialize(Code), + Code1 = aeb_fate_code:deserialize(Bin1), + ?WHENFAIL(eqc:format("Reserialized\n ~120p\n", [Bin1]), + equals(Code, Code1)) + end) + catch _:_ -> true + end))). prop_opcodes() -> ?FORALL(Opcode, choose(0, 16#ff), @@ -80,16 +86,23 @@ fate_code(Failure) -> {non_empty(map(if Failure == 1 -> binary(1); true -> binary(4) end, {{list(aefate_type_eqc:fate_type(Size div 3)), aefate_type_eqc:fate_type(Size div 3)}, bbs_code(Failure)})), - map(small_fate_data_key(5), small_fate_data(4)), - map(small_fate_data_key(5), small_fate_data(4))}, + small_map(small_fate_data_key(5), small_fate_data(4)), + small_map(small_fate_data_key(5), small_fate_data(4))}, aeb_fate_code:update_annotations( aeb_fate_code:update_symbols( aeb_fate_code:update_functions( aeb_fate_code:new(), FMap), SMap), AMap))). +short_list(Max, Gen) -> + ?LET(N, choose(0, Max), eqc_gen:list(N, Gen)). + +small_map(KeyGen, ValGen) -> + ?LET(KeyVals, short_list(6, {KeyGen, ValGen}), + return(maps:from_list(KeyVals))). + bbs_code(Failure) -> frequency([{if Failure == 2 -> 5; true -> 0 end, #{0 => []}}, - {10, ?LET(BBs, list(bb_code(Failure)), + {10, ?LET(BBs, short_list(6, bb_code(Failure)), maps:from_list( lists:zip(lists:seq(0, length(BBs)-1), BBs)))}]). @@ -97,9 +110,9 @@ bb_code(Failure) -> EndBB = [ Op || Op <- valid_opcodes(), aeb_fate_opcodes:end_bb(Op) ], NonEndBB = valid_opcodes() -- EndBB, frequency( - [{if Failure == 3 -> 5; true -> 0 end, ?LET(Ops, non_empty(list(elements(NonEndBB))), bblock(Failure, Ops))}, - {if Failure == 4 -> 5; true -> 0 end, ?LET({Ops, Op}, {list(elements(valid_opcodes())), elements(EndBB)}, bblock(Failure, Ops ++ [Op]))}, - {10, ?LET({Ops, Op}, {list(elements(NonEndBB)), elements(EndBB)}, + [{if Failure == 3 -> 5; true -> 0 end, ?LET(Ops, non_empty(short_list(6, elements(NonEndBB))), bblock(Failure, Ops))}, + {if Failure == 4 -> 5; true -> 0 end, ?LET({Ops, Op}, {short_list(6, elements(valid_opcodes())), elements(EndBB)}, bblock(Failure, Ops ++ [Op]))}, + {10, ?LET({Ops, Op}, {short_list(6, elements(NonEndBB)), elements(EndBB)}, bblock(Failure, Ops ++ [Op]))}]). bblock(Failure, Ops) -> @@ -118,14 +131,34 @@ bblock(Failure, Ops) -> end end || Op <- Ops ]. -injection(Binary) -> - ?LET({N, Inj}, {choose(0, byte_size(Binary) - 1), choose(0,255)}, +fuzz(Binary) -> + ?LET({N, Inj}, {choose(0, byte_size(Binary) - 1), choose(0, 255)}, begin M = N * 8, <> = Binary, <> end). +prop_small() -> + ?FORALL(Value, small_fate_data(4), + begin + Bin = aeb_fate_encoding:serialize(Value), + Size = byte_size(Bin), + measure(size, Size, + ?WHENFAIL(eqc:format("Size: ~p\n", [Size]), + Size < 1000)) + end). + +prop_small_type() -> + ?FORALL(Type, ?SIZED(Size, aefate_type_eqc:fate_type(Size div 3)), + begin + Bin = iolist_to_binary(aeb_fate_encoding:serialize_type(Type)), + Size = byte_size(Bin), + measure(size, Size, + ?WHENFAIL(eqc:format("Size: ~p\n", [Size]), + Size < 1000)) + end). + small_fate_data(N) -> ?SIZED(Size, resize(Size div N, aefate_eqc:fate_data())). diff --git a/quickcheck/aefate_eqc.erl b/quickcheck/aefate_eqc.erl index 3cf183d..4a06401 100644 --- a/quickcheck/aefate_eqc.erl +++ b/quickcheck/aefate_eqc.erl @@ -32,21 +32,15 @@ prop_format_scan() -> end)). prop_serializes() -> - ?FORALL(FateDatas, non_empty(?SIZED(Size, resize(Size div 2, list(fate_data())))), - ?WHENFAIL(eqc:format("Trying to serialize/deserialize ~p failed~n", [FateDatas]), + ?FORALL({Data, Garbage}, {fate_data(), binary()}, + ?WHENFAIL(eqc:format("Trying to serialize/deserialize ~p failed~n", [Data]), begin - {T1, Binary} = - timer:tc( fun() -> - << begin B = aeb_fate_encoding:serialize(Data), - <> end || Data <- FateDatas >> - end), - {T2, {FateData, _}} = - timer:tc(fun() -> aeb_fate_encoding:deserialize_one(Binary) end), + Binary = <<(aeb_fate_encoding:serialize(Data))/binary, Garbage/binary>>, + {FateData, Rest} = aeb_fate_encoding:deserialize_one(Binary), measure(binary_size, size(Binary), - measure(encode, T1, - measure(decode, T2, - conjunction([{equal, equals(hd(FateDatas), FateData)}, - {size, size(Binary) < 500000}])))) + conjunction([{equal, equals(Data, FateData)}, + {rest, equals(Garbage, Rest)}, + {size, size(Binary) < 500000}])) end)). prop_fuzz() -> @@ -98,74 +92,65 @@ fate_data() -> ?SIZED(Size, ?LET(Data, fate_data(Size, [map, variant]), eqc_symbolic:eval(Data))). fate_data_key() -> - ?SIZED(Size, ?LET(Data, fate_data(Size div 4, []), eqc_symbolic:eval(Data))). + ?SIZED(Size, ?LET(Data, fate_data(Size div 4, [variant]), eqc_symbolic:eval(Data))). fate_data(0, _Options) -> ?LAZY( - oneof([fate_integer(), - fate_boolean(), - fate_nil(), - fate_unit(), - fate_string(), - fate_address(), - fate_hash(), - fate_signature(), - fate_contract(), - fate_oracle(), - fate_oracle_q(), - fate_name(), - fate_bits(), - fate_channel()])); + frequency( + [{5, oneof([fate_integer(), fate_boolean(), fate_nil(), fate_unit()])}, + {1, oneof([fate_string(), fate_address(), fate_bytes(), fate_contract(), + fate_oracle(), fate_oracle_q(), fate_name(), fate_bits(), fate_channel()])}])); fate_data(Size, Options) -> - oneof([?LAZY(fate_data(Size - 1, Options)), - ?LAZY(fate_list( fate_data(Size div 5, Options) )), - ?LAZY(fate_tuple( list(fate_data(Size div 5, Options)) ))] ++ - [?LAZY(fate_variant( list(fate_data(Size div 5, Options)))) - || lists:member(variant, Options) - ] ++ - [ - ?LAZY(fate_map( fate_data(Size div 8, Options -- [map, variant]), - fate_data(Size div 5, Options))) - || lists:member(map, Options) - ]). + ?LAZY( + oneof([fate_data(0, Options), + fate_list(Size, Options), + fate_tuple(Size, Options)] ++ + [fate_variant(Size, Options) + || lists:member(variant, Options)] ++ + [fate_map(Size, Options) + || lists:member(map, Options)])). -fate_integer() -> {call, aeb_fate_data, make_integer, [oneof([int(), largeint()])]}. -fate_bits() -> {call, aeb_fate_data, make_bits, [oneof([int(), largeint()])]}. -fate_boolean() -> {call, aeb_fate_data, make_boolean, [elements([true, false])]}. -fate_nil() -> {call, aeb_fate_data, make_list, [[]]}. -fate_unit() -> {call, aeb_fate_data, make_unit, []}. -fate_string() -> {call, aeb_fate_data, make_string, - [frequency([{10, non_quote_string()}, {2, list(non_quote_string())}, - {1, ?LET(N, choose(64-3, 64+3), vector(N, $a))}])]}. -fate_address() -> {call, aeb_fate_data, make_address, [non_zero_binary(256 div 8)]}. -fate_hash() -> {call, aeb_fate_data, make_hash, [non_zero_binary(32)]}. -fate_signature() -> {call, aeb_fate_data, make_signature, [non_zero_binary(64)]}. -fate_contract() -> {call, aeb_fate_data, make_contract, [non_zero_binary(256 div 8)]}. -fate_oracle() -> {call, aeb_fate_data, make_oracle, [non_zero_binary(256 div 8)]}. -fate_oracle_q() -> {call, aeb_fate_data, make_oracle_query, [non_zero_binary(256 div 8)]}. -fate_name() -> {call, aeb_fate_data, make_name, [non_zero_binary(256 div 8)]}. -fate_channel() -> {call, aeb_fate_data, make_channel, [non_zero_binary(256 div 8)]}. +fate_integer() -> ?LET(X, oneof([int(), largeint()]), return(aeb_fate_data:make_integer(X))). +fate_bits() -> ?LET(X, oneof([int(), largeint()]), return(aeb_fate_data:make_bits(X))). +fate_boolean() -> ?LET(X, elements([true, false]), return(aeb_fate_data:make_boolean(X))). +fate_nil() -> aeb_fate_data:make_list([]). +fate_unit() -> aeb_fate_data:make_unit(). +fate_string() -> ?LET(X, frequency([{10, non_quote_string()}, {2, list(non_quote_string())}, + {1, ?LET(N, choose(64-3, 64+3), vector(N, $a))}]), + return(aeb_fate_data:make_string(X))). +fate_address() -> ?LET(X, binary(256 div 8), return(aeb_fate_data:make_address(X))). +fate_bytes() -> ?LET(X, non_empty(binary()), return(aeb_fate_data:make_bytes(X))). +fate_contract() -> ?LET(X, binary(256 div 8), return(aeb_fate_data:make_contract(X))). +fate_oracle() -> ?LET(X, binary(256 div 8), return(aeb_fate_data:make_oracle(X))). +fate_oracle_q() -> ?LET(X, binary(256 div 8), return(aeb_fate_data:make_oracle_query(X))). +fate_name() -> ?LET(X, binary(256 div 8), return(aeb_fate_data:make_name(X))). +fate_channel() -> ?LET(X, binary(256 div 8), return(aeb_fate_data:make_channel(X))). + +fate_values(Size, N, Options) -> + eqc_gen:list(N, fate_data(Size div max(1, N), Options)). %% May shrink to fate_unit -fate_tuple(ListGen) -> - {call, aeb_fate_data, make_tuple, [?LET(Elements, ListGen, list_to_tuple(Elements))]}. +fate_tuple(Size, Options) -> + ?LET(N, choose(0, 6), + ?LETSHRINK(Elements, fate_values(Size, N, Options), + return(aeb_fate_data:make_tuple(list_to_tuple(Elements))))). -fate_variant(ListGen) -> - ?LET({L1, L2, TupleAsList}, {list(choose(0, 255)), list(choose(0,255)), ListGen}, - {call, aeb_fate_data, make_variant, - [L1 ++ [length(TupleAsList)] ++ L2, length(L1), list_to_tuple(TupleAsList)]}). +fate_variant(Size, Options) -> + ?LET({L1, L2, {tuple, Args}}, {list(choose(0, 255)), list(choose(0,255)), fate_tuple(Size, Options)}, + return(aeb_fate_data:make_variant(L1 ++ [tuple_size(Args)] ++ L2, + length(L1), Args))). -fate_list(Gen) -> - {call, aeb_fate_data, make_list, [frequency([{20, list(Gen)}, {1, ?LET(N, choose(64-3, 64+3), vector(N, Gen))}])]}. +fate_list(Size, Options) -> + ?LET(N, frequency([{20, choose(0, 6)}, {1, choose(64 - 3, 64 + 3)}]), + ?LETSHRINK(Vs, fate_values(Size, N, Options), + return(aeb_fate_data:make_list(Vs)))). -fate_map(KeyGen, ValGen) -> - {call, aeb_fate_data, make_map, [map(KeyGen, ValGen)]}. - - -non_zero_binary(N) -> - Bits = N*8, - ?SUCHTHAT(Bin, binary(N), begin <> = Bin, V =/= 0 end). +fate_map(Size, Options) -> + ?LET(N, choose(0, 6), + ?LETSHRINK(Values, fate_values(Size, N, Options), + ?LET(Keys, vector(length(Values), fate_data(Size div max(1, N * 2), Options -- [map])), + return(aeb_fate_data:make_map(maps:from_list(lists:zip(Keys, Values))))))). non_quote_string() -> ?SUCHTHAT(S, utf8(), [ quote || <<34>> <= S ] == []). diff --git a/quickcheck/aefate_type_eqc.erl b/quickcheck/aefate_type_eqc.erl index 7d5596b..d81f6d2 100644 --- a/quickcheck/aefate_type_eqc.erl +++ b/quickcheck/aefate_type_eqc.erl @@ -11,9 +11,12 @@ -compile([export_all, nowarn_export_all]). +kind(X) when is_atom(X) -> X; +kind(T) when is_tuple(T) -> element(1, T). + prop_roundtrip() -> ?FORALL(FateType, fate_type(), - collect(FateType, + collect(kind(FateType), begin Serialized = aeb_fate_encoding:serialize_type(FateType), BinSerialized = list_to_binary(Serialized), @@ -32,8 +35,7 @@ fate_type(0) -> oneof([integer, boolean, address, - hash, - signature, + {bytes, nat()}, contract, oracle, name, @@ -41,9 +43,15 @@ fate_type(0) -> bits, string]); fate_type(Size) -> - oneof([?LAZY(fate_type(Size div 2)), - {list, ?LAZY(fate_type(Size div 2))}, - {tuple, list(?LAZY(fate_type(Size div 2)))}, - {variant, list(?LAZY(fate_type(Size div 2)))}, - ?LETSHRINK([T1, T2], [?LAZY(fate_type(Size div 2)), ?LAZY(fate_type(Size div 2))], - {map, T1, T2})]). + ?LAZY( + oneof([fate_type(0), + {list, fate_type(Size div 2)}, + ?LETSHRINK(Ts, fate_types(Size), {tuple, Ts}), + ?LETSHRINK(Ts, fate_types(Size), {variant, Ts}), + ?LETSHRINK([T1, T2], vector(2, fate_type(Size div 2)), + {map, T1, T2})])). + +fate_types(Size) -> + ?LET(N, choose(0, 6), + eqc_gen:list(N, fate_type(Size div max(2, N)))). + diff --git a/src/aeb_fate_asm.erl b/src/aeb_fate_asm.erl index 68eab63..e2ebca3 100644 --- a/src/aeb_fate_asm.erl +++ b/src/aeb_fate_asm.erl @@ -282,15 +282,10 @@ to_bytecode([{object,_line, {channel, Value}}|Rest], to_bytecode(Rest, Address, Env, [{immediate, aeb_fate_data:make_contract(Value)}|Code], Opts); -to_bytecode([{hash,_line, Value}|Rest], +to_bytecode([{bytes,_line, Value}|Rest], Address, Env, Code, Opts) -> to_bytecode(Rest, Address, Env, - [{immediate, aeb_fate_data:make_hash(Value)}|Code], - Opts); -to_bytecode([{signature,_line, {signature, Value}}|Rest], - Address, Env, Code, Opts) -> - to_bytecode(Rest, Address, Env, - [{immediate, aeb_fate_data:make_signature(Value)}|Code], + [{immediate, aeb_fate_data:make_bytes(Value)}|Code], Opts); to_bytecode([{id,_line, ID}|Rest], Address, Env, Code, Opts) -> {Env2, Id} = insert_symbol(list_to_binary(ID), Env), diff --git a/src/aeb_fate_asm_scan.template b/src/aeb_fate_asm_scan.template index 48639a7..0fadbb2 100644 --- a/src/aeb_fate_asm_scan.template +++ b/src/aeb_fate_asm_scan.template @@ -17,8 +17,7 @@ BASE64 = [A-Za-z0-9+/=] INT = {DIGIT}+ HEX = 0x{HEXDIGIT}+ OBJECT = @[a-z][a-z]_{BASE58}+ -HASH = #{BASE64}+ -SIG = \$sg_{BASE58}+ +BYTES = #{BASE64}+ WS = [\000-\s] ID = {LOWER}[a-zA-Z0-9_]* STRING = "[^"]*" @@ -36,10 +35,8 @@ false : {token, {boolean, TokenLine, false}}. FUNCTION : {token, {function, TokenLine, 'FUNCTION' }}. -{HASH} : - {token, {hash, TokenLine, parse_hash(TokenChars)}}. -{SIG} : - {token, {signature, TokenLine, parse_object(TokenChars)}}. +{BYTES} : + {token, {bytes, TokenLine, parse_hash(TokenChars)}}. {OBJECT} : {token, {object, TokenLine, parse_object(TokenChars)}}. {ID} : diff --git a/src/aeb_fate_code.erl b/src/aeb_fate_code.erl index 5d010bc..73c1ea1 100644 --- a/src/aeb_fate_code.erl +++ b/src/aeb_fate_code.erl @@ -100,6 +100,7 @@ serialize(#fcode{} = F) -> serialize(F, []). serialize(#fcode{} = F, Options) -> + sanity_check(F), serialize(F, serialize_functions(F), Options). serialize(#fcode{} = F, Functions, Options) -> @@ -127,10 +128,8 @@ to_hexstring(ByteList) -> serialize_functions(#fcode{ functions = Functions }) -> %% Sort the functions on name to get a canonical serialisation. iolist_to_binary( - lists:foldr(fun({Id, {Sig, C}}, Acc) when byte_size(Id) == 4 -> - [[?FUNCTION, Id, serialize_signature(Sig), serialize_bbs(C)] | Acc]; - ({Id, _}, _) -> - error({illegal_function_id, Id}) + lists:foldr(fun({Id, {Sig, C}}, Acc) -> + [[?FUNCTION, Id, serialize_signature(Sig), serialize_bbs(C)] | Acc] end, [], lists:sort(maps:to_list(Functions)))). serialize_signature({Args, RetType}) -> @@ -148,46 +147,70 @@ serialize_bbs(#{} = BBs) -> serialize_bbs(BBs, N, Acc) -> case maps:get(N, BBs, none) of - none -> - %% Assert that the BBs were contiguous - Size = maps:size(BBs), - case Size =:= N of - true -> - lists:reverse(Acc); - false -> - error({not_contiguous_labels, lists:sort(maps:keys(BBs))}) - end; - [] -> - error({empty_code_block, N}); - BB -> - serialize_bbs(BBs, N + 1, [serialize_bb(BB, [])|Acc]) + none -> lists:reverse(Acc); + BB -> serialize_bbs(BBs, N + 1, [serialize_bb(BB, [])|Acc]) end. serialize_bb([Op], Acc) -> - lists:reverse([serialize_op(true, Op)|Acc]); + lists:reverse([serialize_op(Op)|Acc]); serialize_bb([Op|Rest], Acc) -> - serialize_bb(Rest, [serialize_op(false, Op)|Acc]). -%% serialize_bb([], Acc) -> -%% lists:reverse(Acc). + serialize_bb(Rest, [serialize_op(Op)|Acc]). -serialize_op(Kind, Op) -> +serialize_op(Op) -> [Mnemonic|Args] = case is_tuple(Op) of true -> tuple_to_list(Op); false -> [Op] end, - safe_serialize(Kind, aeb_fate_opcodes:m_to_op(Mnemonic), Args). + [aeb_fate_opcodes:m_to_op(Mnemonic) | serialize_code(Args)]. -safe_serialize(Last, Op, Args) -> +sanity_check(#fcode{ functions = Funs }) -> + _ = [ case Def of + {_, BBs} when byte_size(Id) == 4 -> sanity_check_bbs(BBs); + _ -> error({illegal_function_id, Id}) + end || {Id, Def} <- maps:to_list(Funs) ], + ok. + +sanity_check_bbs(#{} = BBs) -> + sanity_check_bbs(BBs, 0). + +sanity_check_bbs(BBs, N) -> + case maps:get(N, BBs, none) of + none -> + %% Assert that the BBs were contiguous + case maps:size(BBs) =:= N of + true -> ok; + false -> error({not_contiguous_labels, lists:sort(maps:keys(BBs))}) + end; + [] -> + error({empty_code_block, N}); + BB -> + sanity_check_bb(BB), + sanity_check_bbs(BBs, N + 1) + end. + +sanity_check_bb([Op]) -> + sanity_check_op(true, Op); +sanity_check_bb([Op|Rest]) -> + sanity_check_op(false, Op), + sanity_check_bb(Rest). + +sanity_check_op(IsLast, Op) -> + [Mnemonic|Args] = + case is_tuple(Op) of + true -> tuple_to_list(Op); + false -> [Op] + end, + safe_sanity_check(IsLast, aeb_fate_opcodes:m_to_op(Mnemonic), Args). + +safe_sanity_check(IsLast, Op, Args) -> case length(Args) == aeb_fate_opcodes:args(Op) of true -> - case Last == aeb_fate_opcodes:end_bb(Op) of - true -> [Op|serialize_code(Args)]; - false -> - error({wrong_opcode_in_bb, Op}) + case IsLast == aeb_fate_opcodes:end_bb(Op) of + true -> ok; + false -> error({wrong_opcode_in_bb, Op}) end; - false -> - error({wrong_nr_args_opcode, Op}) + false -> error({wrong_nr_args_opcode, Op}) end. @@ -266,10 +289,13 @@ deserialize(Bytes) -> , functions => #{} , code => #{} }, - #fcode{ functions = deserialize_functions(ByteCode, Env) - , annotations = deserialize_annotations(Annotations) - , symbols = deserialize_symbols(SymbolTable) - }. + Fcode = + #fcode{ functions = deserialize_functions(ByteCode, Env) + , annotations = deserialize_annotations(Annotations) + , symbols = deserialize_symbols(SymbolTable) + }, + sanity_check(Fcode), + Fcode. deserialize_functions(<>, diff --git a/src/aeb_fate_data.erl b/src/aeb_fate_data.erl index 25e7c87..cb16f61 100644 --- a/src/aeb_fate_data.erl +++ b/src/aeb_fate_data.erl @@ -12,12 +12,12 @@ -type fate_map() :: ?FATE_MAP_T. -type fate_string() :: ?FATE_STRING_T. -type fate_address() :: ?FATE_ADDRESS_T. --type fate_hash() :: ?FATE_HASH_T. +-type fate_hash() :: ?FATE_BYTES_T(32). +-type fate_signature() :: ?FATE_BYTES_T(64). -type fate_contract() :: ?FATE_CONTRACT_T. -type fate_oracle() :: ?FATE_ORACLE_T. -type fate_name() :: ?FATE_NAME_T. -type fate_channel() :: ?FATE_CHANNEL_T. --type fate_signature() :: ?FATE_SIGNATURE_T. -type fate_variant() :: ?FATE_VARIANT_T. -type fate_tuple() :: ?FATE_TUPLE_T. -type fate_bits() :: ?FATE_BITS_T. @@ -87,6 +87,7 @@ , make_string/1 , make_map/1 , make_address/1 + , make_bytes/1 , make_hash/1 , make_signature/1 , make_contract/1 @@ -113,8 +114,9 @@ make_unit() -> ?FATE_UNIT. make_tuple(T) -> ?FATE_TUPLE(T). make_map(M) -> ?MAKE_FATE_MAP(M). make_address(X) -> ?FATE_ADDRESS(X). -make_hash(X) -> ?FATE_HASH(X). -make_signature(X) -> ?FATE_SIGNATURE(X). +make_bytes(X) -> ?FATE_BYTES(X). +make_hash(X) -> make_bytes(X). +make_signature(X) -> make_bytes(X). make_contract(X) -> ?FATE_CONTRACT(X). make_oracle(X) -> ?FATE_ORACLE(X). make_oracle_query(X) -> ?FATE_ORACLE_Q(X). @@ -168,11 +170,9 @@ format(?FATE_VARIANT(Arities, Tag, T)) -> " |)"]; format(M) when ?IS_FATE_MAP(M) -> ["{ ", format_kvs(maps:to_list(?FATE_MAP_VALUE(M))), " }"]; -format(?FATE_HASH(X)) -> ["#", base64:encode(X)]; +format(?FATE_BYTES(X)) -> ["#", base64:encode(X)]; format(?FATE_ADDRESS(X)) -> ["@", aeser_api_encoder:encode(account_pubkey, X)]; -format(?FATE_SIGNATURE(X)) -> - ["$", aeser_api_encoder:encode(signature, X)]; format(?FATE_CONTRACT(X)) -> ["@", aeser_api_encoder:encode(contract_pubkey, X)]; format(?FATE_ORACLE(X)) -> @@ -218,8 +218,8 @@ ordinal(T) when ?IS_FATE_CHANNEL(T) -> 3; ordinal(T) when ?IS_FATE_CONTRACT(T) -> 4; ordinal(T) when ?IS_FATE_NAME(T) -> 5; ordinal(T) when ?IS_FATE_ORACLE(T) -> 6; -ordinal(T) when ?IS_FATE_HASH(T) -> 7; -ordinal(T) when ?IS_FATE_SIGNATURE(T) -> 8; +ordinal(T) when ?IS_FATE_BYTES(T) -> 7; +%% 8; ordinal(T) when ?IS_FATE_BITS(T) -> 9; ordinal(T) when ?IS_FATE_STRING(T) -> 10; ordinal(T) when ?IS_FATE_TUPLE(T) -> 11; diff --git a/src/aeb_fate_encoding.erl b/src/aeb_fate_encoding.erl index 48cceb3..8a62178 100644 --- a/src/aeb_fate_encoding.erl +++ b/src/aeb_fate_encoding.erl @@ -73,7 +73,7 @@ -define(TYPE_MAP , 2#01100111). %% 0110 0111 | Type | Type -define(TYPE_STRING , 2#01110111). %% 0111 0111 - string typedef -define(TYPE_VARIANT , 2#10000111). %% 1000 0111 | [Arities] | [Type] - %% 1001 0111 +-define(TYPE_BYTES , 2#10010111). %% 1001 0111 - Bytes typedef %% 1010 0111 %% 1011 0111 %% 1100 0111 @@ -108,13 +108,12 @@ %% Object types -define(OTYPE_ADDRESS, 0). --define(OTYPE_HASH, 1). --define(OTYPE_SIGNATURE, 2). --define(OTYPE_CONTRACT, 3). --define(OTYPE_ORACLE, 4). --define(OTYPE_ORACLE_Q, 5). --define(OTYPE_NAME, 6). --define(OTYPE_CHANNEL, 7). +-define(OTYPE_BYTES, 1). +-define(OTYPE_CONTRACT, 2). +-define(OTYPE_ORACLE, 3). +-define(OTYPE_ORACLE_Q, 4). +-define(OTYPE_NAME, 5). +-define(OTYPE_CHANNEL, 6). -define(IS_TYPE_TAG(X), (X =:= ?TYPE_INTEGER orelse X =:= ?TYPE_BOOLEAN orelse @@ -153,12 +152,10 @@ serialize(String) when ?IS_FATE_STRING(String), <>; +serialize(?FATE_BYTES(Bytes)) when is_binary(Bytes) -> + <>; serialize(?FATE_ADDRESS(Address)) when is_binary(Address) -> <>; -serialize(?FATE_HASH(Address)) when is_binary(Address) -> - <>; -serialize(?FATE_SIGNATURE(Address)) when is_binary(Address) -> - <>; serialize(?FATE_CONTRACT(Address)) when is_binary(Address) -> <>; serialize(?FATE_ORACLE(Address)) when is_binary(Address) -> @@ -233,9 +230,9 @@ serialize_type({tuple, Ts}) -> N when N =< 255 -> [?TYPE_TUPLE, N | [serialize_type(T) || T <- Ts]] end; +serialize_type({bytes, N}) when 0 =< N -> + [?TYPE_BYTES | binary_to_list(serialize_integer(N))]; serialize_type(address) -> [?TYPE_OBJECT, ?OTYPE_ADDRESS]; -serialize_type(hash) -> [?TYPE_OBJECT, ?OTYPE_HASH]; -serialize_type(signature) -> [?TYPE_OBJECT, ?OTYPE_SIGNATURE]; serialize_type(contract) -> [?TYPE_OBJECT, ?OTYPE_CONTRACT]; serialize_type(oracle) -> [?TYPE_OBJECT, ?OTYPE_ORACLE]; serialize_type(oracle_query)-> [?TYPE_OBJECT, ?OTYPE_ORACLE_Q]; @@ -263,11 +260,13 @@ deserialize_type(<>) -> deserialize_type(<>) -> {Ts, Rest2} = deserialize_types(N, Rest, []), {{tuple, Ts}, Rest2}; +deserialize_type(<>) -> + {N, Rest2} = deserialize_one(Rest), + true = is_integer(N) andalso N >= 0, + {{bytes, N}, Rest2}; deserialize_type(<>) -> case ObjectType of ?OTYPE_ADDRESS -> {address, Rest}; - ?OTYPE_HASH -> {hash, Rest}; - ?OTYPE_SIGNATURE -> {signature, Rest}; ?OTYPE_CONTRACT -> {contract, Rest}; ?OTYPE_ORACLE -> {oracle, Rest}; ?OTYPE_ORACLE_Q -> {oracle_query, Rest}; @@ -382,13 +381,15 @@ deserialize2(<>) -> String = binary:part(Rest, 0, S), Rest2 = binary:part(Rest, byte_size(Rest), - (byte_size(Rest) - S)), {?MAKE_FATE_STRING(String), Rest2}; +deserialize2(<>) -> + {String, Rest2} = deserialize_one(Rest), + true = ?IS_FATE_STRING(String), + {?FATE_BYTES(?FATE_STRING_VALUE(String)), Rest2}; deserialize2(<>) -> {A, Rest2} = aeser_rlp:decode_one(Rest), Val = case ObjectType of ?OTYPE_ADDRESS -> ?FATE_ADDRESS(A); - ?OTYPE_HASH -> ?FATE_HASH(A); - ?OTYPE_SIGNATURE -> ?FATE_SIGNATURE(A); ?OTYPE_CONTRACT -> ?FATE_CONTRACT(A); ?OTYPE_ORACLE -> ?FATE_ORACLE(A); ?OTYPE_ORACLE_Q -> ?FATE_ORACLE_Q(A); @@ -485,7 +486,5 @@ sort(KVList) -> valid_key_type(K) when ?IS_FATE_MAP(K) -> error({map_as_key_in_map, K}); -valid_key_type(K) when ?IS_FATE_VARIANT(K) -> - error({variant_as_key_in_map, K}); valid_key_type(_K) -> true. diff --git a/src/aeb_fate_generate_ops.erl b/src/aeb_fate_generate_ops.erl index 5adb95c..e656347 100644 --- a/src/aeb_fate_generate_ops.erl +++ b/src/aeb_fate_generate_ops.erl @@ -161,8 +161,8 @@ ops_defs() -> , { 'MAP_TO_LIST', 16#70, false, 3, [a,a], map_to_list, {map}, list, "Arg0 := The tuple list representation of the map Arg1."} , { 'STR_LENGTH', 16#71, false, 3, [a,a], str_length, {string}, integer, "Arg0 := The length of the string Arg1."} - , { 'ECVERIFY', 16#72, false, 1300, [a,a,a,a], ecverify, {hash, address, signature}, boolean, "Arg0 := ecverify(Hash, PubKey, Signature)"} - , { 'ECVERIFY_SECP256K1', 16#73, false, 1300, [a,a,a,a], ecverify_secp256k1, {hash, signature, signature}, boolean, "Arg0 := ecverify_secp256k1(Hash, PubKey, Signature)"} + , { 'ECVERIFY', 16#72, false, 1300, [a,a,a,a], ecverify, {bytes, address, bytes}, boolean, "Arg0 := ecverify(Hash, PubKey, Signature)"} + , { 'ECVERIFY_SECP256K1', 16#73, false, 1300, [a,a,a,a], ecverify_secp256k1, {bytes, bytes, bytes}, boolean, "Arg0 := ecverify_secp256k1(Hash, PubKey, Signature)"} , { 'CONTRACT_TO_ADDRESS', 16#74, false, 3, [a,a], contract_to_address, {contract}, address, "Arg0 := Arg1 - A no-op type conversion"} , { 'AUTH_TX_HASH', 16#75, false, 3, [a], auth_tx_hash, {}, variant, "If in GA authentication context return Some(TxHash) otherwise None."} diff --git a/test/asm_code/oracles.fate b/test/asm_code/oracles.fate index 81da73a..0b05f6a 100644 --- a/test/asm_code/oracles.fate +++ b/test/asm_code/oracles.fate @@ -1,7 +1,7 @@ ;; CONTRACT oracles FUNCTION register (address, integer, {variant, [{tuple, [integer]}, {tuple, [integer]}]}) : oracle - ORACLE_REGISTER a $sg_11111111111111111111111111111111111111111111111111111111111111116oUsJe arg0 arg1 arg2 'string '{variant, [{tuple, []}, {tuple, [integer]}]} + ORACLE_REGISTER a #AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== arg0 arg1 arg2 'string '{variant, [{tuple, []}, {tuple, [integer]}]} RETURN FUNCTION query (oracle, integer, string) : oracle_query @@ -12,11 +12,11 @@ FUNCTION bogus_query () : oracle_query RETURNR @oq_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv FUNCTION respond (oracle, integer, string) : {tuple, []} - ORACLE_RESPOND $sg_11111111111111111111111111111111111111111111111111111111111111116oUsJe arg0 arg1 arg2 'string '{variant, [{tuple, []}, {tuple, [integer]}]} + ORACLE_RESPOND #AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== arg0 arg1 arg2 'string '{variant, [{tuple, []}, {tuple, [integer]}]} RETURNR {} FUNCTION extend (oracle, {variant, [{tuple, [integer]}, {tuple, [integer]}]}) : {tuple, []} - ORACLE_EXTEND $sg_11111111111111111111111111111111111111111111111111111111111111116oUsJe arg0 arg1 + ORACLE_EXTEND #AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== arg0 arg1 RETURNR {} FUNCTION get_question (oracle, oracle_query) : string