Add signature verification

This commit is contained in:
Craig Everett
2022-12-01 13:18:31 +09:00
parent d4cb2b0107
commit 36ddcfc97f
2 changed files with 82 additions and 7 deletions
+81 -6
View File
@@ -69,7 +69,10 @@
contract_create/8, contract_create/8,
prepare_contract/1, prepare_contract/1,
contract_call/5, contract_call/5,
contract_call/10]). contract_call/6,
contract_call/10,
verify_signature/3]).
% OTP Application Interface % OTP Application Interface
%-export([start/0, stop/0]). %-export([start/0, stop/0]).
@@ -78,7 +81,7 @@
%%% Types %%% Types
-export_type([ae_node/0, network_id/0]). -export_type([ae_node/0, network_id/0, ae_error/0]).
-type ae_node() :: {inet:ip_address(), inet:port_number()}. -type ae_node() :: {inet:ip_address(), inet:port_number()}.
@@ -530,8 +533,14 @@ acc_pending_txs(AccountID) ->
%% Retrieve the next nonce for the given account %% Retrieve the next nonce for the given account
next_nonce(AccountID) -> next_nonce(AccountID) ->
case request(["/v2/accounts/", AccountID, "/next-nonce"]) of % case request(["/v2/accounts/", AccountID, "/next-nonce"]) of
{ok, #{"next_nonce" := Nonce}} -> {ok, Nonce}; % {ok, #{"next_nonce" := Nonce}} -> {ok, Nonce};
% {ok, #{"reason" := "Account not found"}} -> {ok, 1};
% {ok, #{"reason" := Reason}} -> {error, Reason};
% Error -> Error
% end.
case request(["/v2/accounts/", AccountID]) of
{ok, #{"nonce" := Nonce}} -> {ok, Nonce + 1};
{ok, #{"reason" := "Account not found"}} -> {ok, 1}; {ok, #{"reason" := "Account not found"}} -> {ok, 1};
{ok, #{"reason" := Reason}} -> {error, Reason}; {ok, #{"reason" := Reason}} -> {error, Reason};
Error -> Error Error -> Error
@@ -753,7 +762,7 @@ result(Received) -> Received.
%% @doc %% @doc
%% This function reads the source of a Sophia contract (an .aes file) %% This function reads the source of a Sophia contract (an .aes file)
%% and returns the unsigned create contract call data with default values. %% and returns the unsigned create contract call data with default values.
%% For more control over exactly what those values are, use create_contract/9. %% For more control over exactly what those values are, use create_contract/8.
contract_create(CreatorID, Path, InitArgs) -> contract_create(CreatorID, Path, InitArgs) ->
case next_nonce(CreatorID) of case next_nonce(CreatorID) of
@@ -935,7 +944,9 @@ contract_create4(OwnerID, Nonce,
Amount, Gas, GasPrice, Fee, Amount, Gas, GasPrice, Fee,
Compiled, CallData) -> Compiled, CallData) ->
Code = aeser_contract_code:serialize(Compiled), Code = aeser_contract_code:serialize(Compiled),
CTVersion = 2, VM = 7,
ABI = 3,
<<CTVersion:32>> = <<VM:16, ABI:16>>,
ContractCreateVersion = 1, ContractCreateVersion = 1,
TTL = 0, TTL = 0,
Type = contract_create_tx, Type = contract_create_tx,
@@ -1032,6 +1043,33 @@ contract_call(CallerID, AACI, ConID, Fun, Args) ->
AACI, ConID, Fun, Args). AACI, ConID, Fun, Args).
-spec contract_call(CallerID, Gas, AACI, ConID, Fun, Args) -> Result
when CallerID :: unicode:chardata(),
Gas :: pos_integer(),
AACI :: map(),
ConID :: unicode:chardata(),
Fun :: string(),
Args :: [string()],
Result :: {ok, CallTX} | {error, Reason},
CallTX :: binary(),
Reason :: term().
%% @doc
%% Just like contract_call/5, but allows you to specify the amount of gas
%% without getting into a major adventure with the other arguments.
%%
%% For details on the meaning of these and other argument values see the doc comment
%% for contract_call/10.
contract_call(CallerID, Gas, AACI, ConID, Fun, Args) ->
{ok, Nonce} = next_nonce(CallerID),
GasPrice = min_gas_price(),
Fee = min_fee(),
Amount = 0,
contract_call(CallerID, Nonce,
Gas, GasPrice, Fee, Amount,
AACI, ConID, Fun, Args).
-spec contract_call(CallerID, Nonce, -spec contract_call(CallerID, Nonce,
Gas, GasPrice, Fee, Amount, Gas, GasPrice, Fee, Amount,
AACI, ConID, Fun, Args) -> Result AACI, ConID, Fun, Args) -> Result
@@ -1364,6 +1402,43 @@ encode_call_data3(ArgDef, Fun, Args) ->
end. end.
verify_signature(Sig, Message, PubKey) ->
case aeser_api_encoder:decode(PubKey) of
{account_pubkey, PK} -> verify_signature2(Sig, Message, PK);
Other -> {error, {bad_key, Other}}
end.
verify_signature2(Sig, Message, PK) ->
Prefix = <<"aeternity Signed Message:\n">>,
{ok, PSize} = vencode(byte_size(Prefix)),
{ok, MSize} = vencode(byte_size(Message)),
Smashed = iolist_to_binary([PSize, Prefix, MSize, Message]),
{ok, Hashed} = eblake2:blake2b(32, Smashed),
Signature = <<(binary_to_integer(Sig, 16)):(64 * 8)>>,
Result = enacl:sign_verify_detached(Signature, Hashed, PK),
{ok, Result}.
vencode(N) when N < 0 ->
{error, {negative_N, N}};
vencode(N) when N < 16#FD ->
{ok, <<N>>};
vencode(N) when N =< 16#FFFF ->
NBytes = eu(N, 2),
{ok, <<16#FD, NBytes/binary>>};
vencode(N) when N =< 16#FFFF_FFFF ->
NBytes = eu(N, 4),
{ok, <<16#FE, NBytes/binary>>};
vencode(N) when N < (2 bsl 64) ->
NBytes = eu(N, 8),
{ok, <<16#FF, NBytes/binary>>}.
eu(N, Size) ->
Bytes = binary:encode_unsigned(N, little),
NExtraZeros = Size - byte_size(Bytes),
ExtraZeros = << <<0>> || _ <- lists:seq(1, NExtraZeros) >>,
<<Bytes/binary, ExtraZeros/binary>>.
%%% Debug functionality %%% Debug functionality
+1 -1
View File
@@ -37,7 +37,7 @@
-record(fetcher, -record(fetcher,
{pid = none :: none | pid(), {pid = none :: none | pid(),
mon = none :: none | reference(), mon = none :: none | reference(),
time = none :: none | erlang:timestamp(), time = none :: none | integer(), % nanosecond timestamp
node = none :: none | vanilae:ae_node(), node = none :: none | vanilae:ae_node(),
from = none :: none | gen_server:from(), from = none :: none | gen_server:from(),
req = none :: none | binary()}). req = none :: none | binary()}).