WIP: Hakuzaru fixes and tweaks #33

Draft
spivee wants to merge 5 commits from spivee/fixes into master
Showing only changes of commit 4302ae002c - Show all commits
+131 -34
View File
@@ -46,6 +46,7 @@
acc_pending_txs/1, acc_pending_txs/1,
next_nonce/1, next_nonce/1,
dry_run/1, dry_run/2, dry_run/3, % dry_run_map/1, dry_run/1, dry_run/2, dry_run/3, % dry_run_map/1,
read_contract_getter/4, read_contract_getter/5,
tx/1, tx_info/1, tx/1, tx_info/1,
post_tx/1, post_tx/1,
contract/1, contract_code/1, contract_source/1, contract/1, contract_code/1, contract_source/1,
@@ -71,6 +72,7 @@
contract_call/5, contract_call/5,
contract_call/6, contract_call/6,
contract_call/10, contract_call/10,
parse_tx_info/2,
decode_bytearray/2, decode_bytearray/2,
spend/5, spend/10, spend/5, spend/10,
sign_tx/2, sign_tx/3, sign_tx/2, sign_tx/3,
@@ -627,7 +629,8 @@ dry_run(TX) ->
-spec dry_run(TX, Accounts) -> {ok, Result} | {error, Reason} -spec dry_run(TX, Accounts) -> {ok, Result} | {error, Reason}
when TX :: binary() | string(), when TX :: binary() | string(),
Accounts :: [pubkey()], Accounts :: [Account],
Account :: {pubkey(), integer()} | #{string() => term()},
Result :: term(), % FIXME Result :: term(), % FIXME
Reason :: term(). % FIXME Reason :: term(). % FIXME
%% @doc %% @doc
@@ -643,7 +646,8 @@ dry_run(TX, Accounts) ->
-spec dry_run(TX, Accounts, KBHash) -> {ok, Result} | {error, Reason} -spec dry_run(TX, Accounts, KBHash) -> {ok, Result} | {error, Reason}
when TX :: binary() | string(), when TX :: binary() | string(),
Accounts :: [pubkey()], Accounts :: [Account],
Account :: {pubkey(), integer()} | #{string() => term()},
KBHash :: binary() | string(), KBHash :: binary() | string(),
Result :: term(), % FIXME Result :: term(), % FIXME
Reason :: term(). % FIXME Reason :: term(). % FIXME
@@ -652,21 +656,85 @@ dry_run(TX, Accounts) ->
%% hash provided. %% hash provided.
dry_run(TX, Accounts, KBHash) -> dry_run(TX, Accounts, KBHash) ->
NAccounts = lists:map(fun normalize_account/1, Accounts),
KBB = to_binary(KBHash), KBB = to_binary(KBHash),
TXB = to_binary(TX), TXB = to_binary(TX),
DryData = #{top => KBB, DryData = #{top => KBB,
accounts => Accounts, accounts => NAccounts,
txs => [#{tx => TXB}], txs => [#{tx => TXB}],
tx_events => true}, tx_events => true},
JSON = zj:binary_encode(DryData), JSON = zj:binary_encode(DryData),
request("/v3/dry_run", JSON). request("/v3/dry_run", JSON).
normalize_account({Pubkey, Amount}) ->
PubkeyBin = unicode:characters_to_binary(Pubkey),
#{"pub_key" => PubkeyBin, "amount" => Amount};
normalize_account(Val) ->
Val.
% TODO % TODO
%dry_run_map(Map) -> %dry_run_map(Map) ->
% JSON = zj:binary_encode(Map), % JSON = zj:binary_encode(Map),
% request("/v3/dry_run", JSON). % request("/v3/dry_run", JSON).
parse_tx_info({error, Reason}, _) ->
{error, Reason};
parse_tx_info({ok, Result}, Format) ->
parse_tx_info(Result, Format);
parse_tx_info(#{"call_info" := #{"contract_id" := Contract}}, deploy) ->
% TODO: What happens if a contract deploy goes wrong?
{ok, Contract};
parse_tx_info(#{"call_info" := #{"return_type" := Status,
"return_value" := Value}},
Format) ->
parse_tx_value(Status, Value, Format);
parse_tx_info(#{"reason" := Reason,
"parameter" := Parameter,
"info" := #{"error" := Reason2,
"path" := Path,
"data" := Data}},
_)->
% Overall dry run error. Informative, but annoyingly inconsistent with all
% other cases.
{error, {Reason, Reason2, [Parameter | Path], Data}};
parse_tx_info(#{"results" := Results}, Format) ->
% Dry run result, could be multiple results or one, and each could be a
% success or an error.
parse_tx_info(Results, Format);
parse_tx_info([Next, Then | Rest], Format) ->
case Next of
#{"call_obj" := #{"return_type" := "ok"}} ->
% Success. Assume this transaction was just setting up conditions
% for later transactions, and move on.
parse_tx_info([Then | Rest], Format);
_ ->
% Some error. Stop here and parse the error out.
parse_tx_info(Next, Format)
end;
parse_tx_info([Last], Format) ->
parse_tx_info(Last, Format);
parse_tx_info(#{"reason" := Message}, _) ->
% Dry run error for individual tx.
{error, Message};
parse_tx_info(#{"call_obj" := #{"return_type" := Status,
"return_value" := Value}},
Format) ->
% Dry run result. At this point we can parse it the same way we parse
% tx_info.
parse_tx_value(Status, Value, Format).
parse_tx_value("revert", Value, _) ->
Message = decode_bytearray(Value, fate),
{error, {abort, Message}};
parse_tx_value("error", Value, _) ->
% gmser takes binary inputs and gives binary outputs
EncodedBinary = list_to_binary(Value),
{contract_bytearray, Binary} = gmser_api_encoder:decode(EncodedBinary),
Message = binary_to_list(Binary),
{error, {contract_error, Message}};
parse_tx_value("ok", Value, Format) ->
decode_bytearray(Value, Format).
-spec decode_bytearray_fate(EncodedStr) -> {ok, Result} | {error, Reason} -spec decode_bytearray_fate(EncodedStr) -> {ok, Result} | {error, Reason}
when EncodedStr :: binary() | string(), when EncodedStr :: binary() | string(),
@@ -720,6 +788,32 @@ decode_bytearray2(FATE, sophia) -> hz_sophia:fate_to_list(FATE);
decode_bytearray2(FATE, {sophia, Type}) -> hz_sophia:fate_to_list(Type, FATE); decode_bytearray2(FATE, {sophia, Type}) -> hz_sophia:fate_to_list(Type, FATE);
decode_bytearray2(FATE, {erlang, Type}) -> hz_aaci:fate_to_erlang(Type, FATE). decode_bytearray2(FATE, {erlang, Type}) -> hz_aaci:fate_to_erlang(Type, FATE).
read_contract_getter(AACI, ConID, Fun, Args) ->
case contract(ConID) of
{ok, {}} ->
CallerID = ConID,
read_contract_getter(CallerID, AACI, ConID, Fun, Args);
{error, Reason} ->
{error, Reason}
end.
read_contract_getter(CallerID, AACI, ConID, Fun, Args) ->
case convert_args(AACI, Fun, Args) of
{ok, {ArgsFATE, ReturnFormat}} ->
read_contract_getter2(CallerID, ConID, Fun, ArgsFATE, ReturnFormat);
{error, Reason} ->
{error, Reason}
end.
read_contract_getter2(CallerID, ConID, Fun, Args, ReturnFormat) ->
case contract_call(CallerID, {}, ConID, Fun, {fate, Args}) of
{ok, TX} ->
Result = dry_run(TX, [{CallerID, 1 bsl 80}]),
parse_tx_info(Result, ReturnFormat);
{error, Reason} ->
{error, Reason}
end.
to_binary(S) when is_binary(S) -> S; to_binary(S) when is_binary(S) -> S;
to_binary(S) when is_list(S) -> list_to_binary(S). to_binary(S) when is_list(S) -> list_to_binary(S).
@@ -1614,44 +1708,47 @@ min_gas() ->
200_000. 200_000.
encode_call_data({aaci, _ContractName, FunDefs, _TypeDefs}, Fun, Args) -> encode_call_data(AACI, Fun, Args) ->
case maps:find(Fun, FunDefs) of case convert_args(AACI, Fun, Args) of
{ok, {ArgDef, _ResultDef}} -> encode_call_data2(ArgDef, Fun, Args); {ok, {ArgsFATE, _}} ->
error -> {error, bad_fun_name} gmb_fate_abi:create_calldata(Fun, ArgsFATE);
end; {error, Reason} ->
encode_call_data({aaci, Label}, Fun, Args) -> {error, Reason}
case hz_man:lookup_aaci(Label) of
{ok, AACI} -> encode_call_data(AACI, Fun, Args);
error -> {error, aaci_not_found}
end. end.
encode_call_data2(ArgDef, Fun, {sophia, Args}) -> convert_args(_, _, {fate, Args}) ->
case convert(ArgDef, Args) of {ok, {Args, fate}};
{ok, Converted} -> gmb_fate_abi:create_calldata(Fun, Converted); convert_args(AACI, Fun, Args) ->
Errors -> Errors case aaci_lookup_spec(AACI, Fun) of
end; {ok, {ArgTypes, ReturnType}} ->
encode_call_data2(ArgDef, Fun, {erlang, Args}) -> convert_args2(ArgTypes, Args, ReturnType);
case hz_aaci:erlang_args_to_fate(ArgDef, Args) of {error, Reason} ->
{ok, Coerced} -> gmb_fate_abi:create_calldata(Fun, Coerced); {error, Reason}
Errors -> Errors end.
end;
encode_call_data2(_, Fun, {fate, Args}) ->
% TODO: This should probably be moved back closer to the initiating call.
% 2026-02-13: Craig
gmb_fate_abi:create_calldata(Fun, Args);
encode_call_data2(ArgDef, Fun, Args) ->
encode_call_data2(ArgDef, Fun, {sophia, Args}).
convert(Defs, Args) -> convert(Defs, Args, 1, [], []). convert_args2(ArgTypes, {erlang, Args}, ReturnType) ->
case hz_aaci:erlang_args_to_fate(ArgTypes, Args) of
{ok, Converted} -> {ok, {Converted, {erlang, ReturnType}}};
{error, Reason} -> {error, Reason}
end;
convert_args2(ArgTypes, {sophia, Args}, ReturnType) ->
case sophia_args_to_fate(ArgTypes, Args) of
{ok, Converted} -> {ok, {Converted, {sophia, ReturnType}}};
{error, Reason} -> {error, Reason}
end;
convert_args2(ArgTypes, Args, ReturnType) ->
convert_args2(ArgTypes, {sophia, Args}, ReturnType).
convert([{Name, Def} | Defs], [Arg | Args], Nth, Terms, Errors) -> sophia_args_to_fate(Defs, Args) -> sophia_args_to_fate(Defs, Args, 1, [], []).
sophia_args_to_fate([{Name, Def} | Defs], [Arg | Args], Nth, Terms, Errors) ->
case hz_sophia:parse_literal(Def, Arg) of case hz_sophia:parse_literal(Def, Arg) of
{ok, Term} -> convert(Defs, Args, Nth + 1, [Term | Terms], Errors); {ok, Term} -> sophia_args_to_fate(Defs, Args, Nth + 1, [Term | Terms], Errors);
{error, Reason} -> convert(Defs, Args, Nth + 1, Terms, [{Nth, Name, Reason} | Errors]) {error, Reason} -> sophia_args_to_fate(Defs, Args, Nth + 1, Terms, [{Nth, Name, Reason} | Errors])
end; end;
convert([], [], _, Terms, []) -> sophia_args_to_fate([], [], _, Terms, []) ->
{ok, lists:reverse(Terms)}; {ok, lists:reverse(Terms)};
convert([], [], _, _, Errors) -> sophia_args_to_fate([], [], _, _, Errors) ->
{error, Errors}. {error, Errors}.
-spec sign_tx(Unsigned, SecKey) -> Result -spec sign_tx(Unsigned, SecKey) -> Result