WIP: Hakuzaru fixes and tweaks #33
+131
-34
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user