From 7704d82c6f8ca8764a8562cb5f46b9f6f9c1d0c3 Mon Sep 17 00:00:00 2001 From: Jarvis Carroll Date: Tue, 23 Sep 2025 12:39:07 +1000 Subject: [PATCH] serialize signatures This took a surprising number of goose chases to work out... I had to find out - what is the gmser prefix for a signature (sg_) - what is the gmb wrapper for a signature (none) - what errors gmser can report when a signature is invalid - what an example of a valid signature is - what that example signature serializes to --- src/hz.erl | 55 ++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 39 insertions(+), 16 deletions(-) diff --git a/src/hz.erl b/src/hz.erl index 38ee508..120d17e 100644 --- a/src/hz.erl +++ b/src/hz.erl @@ -1806,29 +1806,20 @@ coerce({O, N, integer}, S, to_fate) when is_list(S) -> error:badarg -> single_error({invalid, O, N, S}) end; coerce({O, N, address}, S, to_fate) -> - try - case gmser_api_encoder:decode(unicode:characters_to_binary(S)) of - {account_pubkey, Key} -> {ok, {address, Key}}; - _ -> single_error({invalid, O, N, S}) - end - catch - error:_ -> single_error({invalid, O, N, S}) - end; + coerce_chain_object(O, N, address, account_pubkey, S); coerce({_, _, address}, {address, Bin}, from_fate) -> Address = gmser_api_encoder:encode(account_pubkey, Bin), {ok, unicode:characters_to_list(Address)}; coerce({O, N, contract}, S, to_fate) -> - try - case gmser_api_encoder:decode(unicode:characters_to_binary(S)) of - {contract_pubkey, Key} -> {ok, {contract, Key}}; - _ -> single_error({invalid, O, N, S}) - end - catch - error:_ -> single_error({invalid, O, N, S}) - end; + coerce_chain_object(O, N, contract, contract_pubkey, S); coerce({_, _, contract}, {contract, Bin}, from_fate) -> Address = gmser_api_encoder:encode(contract_pubkey, Bin), {ok, unicode:characters_to_list(Address)}; +coerce({O, N, signature}, S, to_fate) -> + coerce_chain_object(O, N, signature, signature, S); +coerce({_, _, signature}, Bin, from_fate) -> + Address = gmser_api_encoder:encode(signature, Bin), + {ok, unicode:characters_to_list(Address)}; coerce({_, _, boolean}, true, _) -> {ok, true}; coerce({_, _, boolean}, false, _) -> @@ -1897,6 +1888,27 @@ coerce({O, N, _}, Data, from_fate) -> {ok, Data}; coerce({O, N, _}, Data, _) -> single_error({invalid, O, N, Data}). +coerce_chain_object(O, N, T, Tag, S) -> + case decode_chain_object(Tag, S) of + {ok, Data} -> {ok, coerce_chain_object2(T, Data)}; + {error, Reason} -> single_error({Reason, O, N, S}) + end. + +coerce_chain_object2(address, Data) -> {address, Data}; +coerce_chain_object2(contract, Data) -> {contract, Data}; +coerce_chain_object2(signature, Data) -> Data. + +decode_chain_object(Tag, S) -> + try + case gmser_api_encoder:decode(unicode:characters_to_binary(S)) of + {Tag, Data} -> {ok, Data}; + {_, _} -> {error, wrong_prefix} + end + catch + error:missing_prefix -> {error, missing_prefix}; + error:incorrect_size -> {error, incorrect_size} + end. + coerce_list(Type, Elements, Direction) -> % 0 index since it represents a sophia list coerce_list(Type, Elements, Direction, 0, [], []). @@ -2427,6 +2439,8 @@ try_coerce(Type, Sophia, Fate) -> _ -> erlang:error({from_fate_failed, Sophia, SophiaActual}) end, + % Finally, check that the FATE result is something that gmb understands. + gmb_fate_encoding:serialize(Fate), ok. coerce_int_test() -> @@ -2449,6 +2463,15 @@ coerce_contract_test() -> 167,208,53,78,40,235,2,163,132,36,47,183,228,151,9, 210,39,214>>}). +coerce_signature_test() -> + {ok, Type} = annotate_type(signature, #{}), + try_coerce(Type, + "sg_XDyF8LJC4tpMyAySvpaG1f5V9F2XxAbRx9iuVjvvdNMwVracLhzAuXhRM5kXAFtpwW1DCHuz5jGehUayCah4jub32Ti2n", + <<231,4,97,129,16,173,37,42,194,249,28,94,134,163,208,84,22,135, + 169,85,212,142,14,12,233,252,97,50,193,158,229,51,123,206,222, + 249,2,3,85,173,106,150,243,253,89,128,248,52,195,140,95,114, + 233,110,119,143,206,137,124,36,63,154,85,7>>). + coerce_bool_test() -> {ok, Type} = annotate_type(boolean, #{}), try_coerce(Type, true, true),