Updates necessary for the nwe poop

This commit is contained in:
Craig Everett 2024-11-12 18:52:08 +09:00
parent 3f80b11d63
commit dbf4f6bdb2
6 changed files with 90 additions and 110 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -6,6 +6,10 @@
%%% @end
-module(hakuzaru).
-vsn("0.4.1").
-author("Craig Everett <ceverett@tsuriai.jp>").
-copyright("Craig Everett <ceverett@tsuriai.jp>").
-license("GPL-3.0-or-later").
-behavior(application).

View File

@ -4,24 +4,21 @@
%%% This module is the high-level interface to the Gajumaru blockchain system.
%%% The interface is split into three main sections:
%%% - Get/Set admin functions
%%% - AE node JSON query interface functions
%%% - AE contract call and serialization interface functions
%%% - Node JSON query interface functions
%%% - Contract call and serialization interface functions
%%%
%%% The get/set admin functions are for setting or checking things like the Gajumaru
%%% "network ID" and list of addresses of AE nodes you want to use for answering
%%% queries to the blockchain (usually you will run these nodes in your own back end).
%%% "network ID" and list of addresses of nodes you want to use for answering
%%% queries to the blockchain.
%%%
%%% The JSON query interface functions are the blockchain query functions themselves
%%% which are translated to network queries and return Erlang messages as responses.
%%%
%%% The contract call and serialization interface are the functions used to convert
%%% a desired call to a smart contract on the chain to call data serialized in a form
%%% that an Gajumaru compatible wallet, SDK or (in the case of a web service) in-page
%%% code based on a JS library such as Sidekick (another component of Hakuzaru) can
%%% use to generate signature requests and signed transaction objects for submission
%%% to an Gajumaru network node for inclusion in the transaction mempool.
%%% that a Gajumaru compatible wallet or library can sign and submit to a Gajumaru node.
%%%
%%% This module also includes the standard OTP "application" interface and start/stop
%%% This module does not implement the OTP application behavior.
%%% helper functions.
%%% @end
@ -37,7 +34,7 @@
tls/0, tls/1,
timeout/0, timeout/1]).
% AE node JSON query interface functions
% Node JSON query interface functions
-export([top_height/0, top_block/0,
kb_current/0, kb_current_hash/0, kb_current_height/0,
kb_pending/0,
@ -53,18 +50,16 @@
post_tx/1,
contract/1, contract_code/1,
contract_poi/1,
% oracle/1, oracle_queries/1, oracle_queries_by_id/2,
name/1,
% channel/1,
peer_pubkey/0,
status/0,
status_chainends/0]).
% AE contract call and serialization interface functions
% Contract call and serialization interface functions
-export([read_aci/1,
min_gas/0,
min_gas_price/0,
min_fee/0,
contract_create/3,
contract_create/8,
prepare_contract/1,
@ -154,7 +149,6 @@
% "call_data" => contract_byte_array(),
% "code" => contract_byte_array(),
% "deposit" => non_neg_integer(),
% "fee" => pos_integer(),
% "gas" => pos_integer(),
% "gas_price" => pos_integer(),
% "nonce" => pos_integer(),
@ -646,7 +640,7 @@ dry_run(TX, Accounts, KBHash) ->
txs => [#{tx => TXB}],
tx_events => true},
JSON = zj:binary_encode(DryData),
request("/v3/dry-run", JSON).
request("/v3/dry_run", JSON).
-spec decode_bytearray_fate(EncodedStr) -> {ok, Result} | {error, Reason}
when EncodedStr :: binary() | string(),
@ -718,15 +712,17 @@ tx_info(ID) ->
result(request(["/v3/transactions/", ID, "/info"])).
-spec post_tx(Data) -> {ok, Result} | {error, Reason}
when Data :: term(), % FIXME
when Data :: string() | binary(),
Result :: term(), % FIXME
Reason :: chain_error() | string().
%% @doc
%% Post a transaction to the chain.
post_tx(Data) ->
post_tx(Data) when is_binary(Data) ->
JSON = zj:binary_encode(#{tx => Data}),
request("/v3/transactions", JSON).
request("/v3/transactions", JSON);
post_tx(Data) when is_list(Data) ->
post_tx(list_to_binary(Data)).
-spec contract(ID) -> {ok, ContractData} | {error, Reason}
@ -864,11 +860,12 @@ contract_create(CreatorID, Path, InitArgs) ->
case next_nonce(CreatorID) of
{ok, Nonce} ->
Amount = 0,
Gas = 100000,
{ok, Height} = top_height(),
TTL = Height + 262980,
Gas = 500000,
GasPrice = min_gas_price(),
Fee = min_fee(),
contract_create(CreatorID, Nonce,
Amount, Gas, GasPrice, Fee,
Amount, TTL, Gas, GasPrice,
Path, InitArgs);
Error ->
Error
@ -876,14 +873,14 @@ contract_create(CreatorID, Path, InitArgs) ->
-spec contract_create(CreatorID, Nonce,
Amount, Gas, GasPrice, Fee,
Amount, TTL, Gas, GasPrice,
Path, InitArgs) -> Result
when CreatorID :: pubkey(),
Nonce :: pos_integer(),
Amount :: non_neg_integer(),
TTL :: pos_integer(),
Gas :: pos_integer(),
GasPrice :: pos_integer(),
Fee :: non_neg_integer(),
Path :: file:filename(),
InitArgs :: [string()],
Result :: {ok, CreateTX} | {error, Reason},
@ -927,6 +924,12 @@ contract_create(CreatorID, Path, InitArgs) ->
%% of course there are very good reasons why it should be set to a non-zero value
%% in the case of calls related to contract-governed payment systems.
%% </li>
%% <b>TTL:</b>
%% This stands for "Time-To-Live", meaning the height beyond which this element is
%% considered to be eligible for garbage collection (and therefore inaccessible!).
%% The TTL can be extended by a "live extension" transaction (basically pay for the
%% data to remain alive longer).
%% </li>
%% <li>
%% <b>Gas:</b>
%% This number sets a limit on the maximum amount of computation the caller is willing
@ -960,13 +963,6 @@ contract_create(CreatorID, Path, InitArgs) ->
%% transaction, thus making miners more likely to prioritize the high value ones.
%% </li>
%% <li>
%% <b>Fee:</b>
%% This value should really be caled `Bribe' or `Tip'.
%% This is a flat fee in aettos that is paid into the block reward, thereby allowing
%% an additional way to prioritize a given transaction above others, even if the
%% transaction will not consume much gas.
%% </li>
%% <li>
%% <b>ACI:</b>
%% This is the compiled contract's metadata. It provides the information necessary
%% for the contract call data to be formed in a way that the Gajumaru runtime will
@ -999,77 +995,81 @@ contract_create(CreatorID, Path, InitArgs) ->
%% if you do not already have a copy, and can check the spec of a function before
%% trying to form a contract call.
contract_create(CreatorID, Nonce,
Amount, Gas, GasPrice, Fee,
Path, InitArgs) ->
case aeso_compiler:file(Path, [{aci, json}]) of
{ok, Compiled} ->
contract_create2(CreatorID, Nonce,
Amount, Gas, GasPrice, Fee,
Compiled, InitArgs);
contract_create(CreatorID, Nonce, Amount, TTL, Gas, GasPrice, Path, InitArgs) ->
case file:read_file(Path) of
{ok, Source} ->
Dir = filename:dirname(Path),
{ok, CWD} = file:get_cwd(),
SrcDir = aeso_utils:canonical_dir(Path),
Options =
[{aci, json},
{src_file, Path},
{src_dir, SrcDir},
{include, {file_system, [CWD, aeso_utils:canonical_dir(Dir)]}}],
contract_create2(CreatorID, Nonce, Amount, TTL, Gas, GasPrice,
Source, Options, InitArgs);
Error ->
Error
end.
contract_create2(CreatorID, Nonce,
Amount, Gas, GasPrice, Fee,
Compiled, InitArgs) ->
contract_create2(CreatorID, Nonce, Amount, TTL, Gas, GasPrice, Source, Options, InitArgs) ->
case aeso_compiler:from_string(Source, Options) of
{ok, Compiled} ->
contract_create3(CreatorID, Nonce, Amount, TTL, Gas, GasPrice,
Source, Compiled, InitArgs);
Error ->
Error
end.
contract_create3(CreatorID, Nonce, Amount, TTL, Gas, GasPrice, Source, Compiled, InitArgs) ->
AACI = prepare_aaci(maps:get(aci, Compiled)),
case encode_call_data(AACI, "init", InitArgs) of
{ok, CallData} ->
contract_create3(CreatorID, Nonce,
Amount, Gas, GasPrice, Fee,
Compiled, CallData);
contract_create4(CreatorID, Nonce, Amount, TTL, Gas, GasPrice,
Source, Compiled, CallData);
Error ->
Error
end.
contract_create3(CreatorID, Nonce,
Amount, Gas, GasPrice, Fee,
Compiled, CallData) ->
contract_create4(CreatorID, Nonce, Amount, TTL, Gas, GasPrice, Source, Compiled, CallData) ->
PK = unicode:characters_to_binary(CreatorID),
try
{account_pubkey, OwnerID} = aeser_api_encoder:decode(PK),
contract_create4(OwnerID, Nonce,
Amount, Gas, GasPrice, Fee,
Compiled, CallData)
contract_create5(OwnerID, Nonce, Amount, TTL, Gas, GasPrice, Source, Compiled, CallData)
catch
Error:Reason -> {Error, Reason}
end.
contract_create4(OwnerID, Nonce,
Amount, Gas, GasPrice, Fee,
Compiled, CallData) ->
contract_create5(OwnerID, Nonce, Amount, TTL, Gas, GasPrice, Source, Compiled, CallData) ->
Code = aeser_contract_code:serialize(Compiled),
VM = 7,
ABI = 3,
VM = 1,
ABI = 1,
<<CTVersion:32>> = <<VM:16, ABI:16>>,
ContractCreateVersion = 1,
TTL = 0,
Type = contract_create_tx,
Fields =
[{owner_id, aeser_id:create(account, OwnerID)},
{nonce, Nonce},
{code, Code},
{source, Source},
{ct_version, CTVersion},
{fee, Fee},
{ttl, TTL},
{deposit, 0},
{amount, Amount},
{gas, Gas},
{gas_price, GasPrice},
{gas, Gas},
{call_data, CallData}],
Template =
[{owner_id, id},
{nonce, int},
{code, binary},
{source, binary},
{ct_version, int},
{fee, int},
{ttl, int},
{deposit, int},
{amount, int},
{gas, int},
{gas_price, int},
{gas, int},
{call_data, binary}],
TXB = aeser_chain_objects:serialize(Type, ContractCreateVersion, Template, Fields),
try
@ -1121,9 +1121,9 @@ read_aci(Path) ->
CallTX :: binary(),
Reason :: term().
%% @doc
%% Form a contract call using hardcoded default values for `Gas', `GasPrice', `Fee',
%% Form a contract call using hardcoded default values for `Gas', `GasPrice',
%% and `Amount' to simplify the call (10 args is a bit much for normal calls!).
%% The values used are 20k for `Gas' and `Fee', the `GasPrice' is fixed at 1b (the
%% The values used are 20k for `Gas', the `GasPrice' is fixed at 1b (the
%% default "miner minimum" defined in default configs), and the `Amount' is 0.
%%
%% For details on the meaning of these and other argument values see the doc comment
@ -1134,10 +1134,11 @@ contract_call(CallerID, AACI, ConID, Fun, Args) ->
{ok, Nonce} ->
Gas = min_gas(),
GasPrice = min_gas_price(),
Fee = min_fee(),
Amount = 0,
{ok, Height} = top_height(),
TTL = Height + 262980,
contract_call(CallerID, Nonce,
Gas, GasPrice, Fee, Amount,
Gas, GasPrice, Amount, TTL,
AACI, ConID, Fun, Args);
Error ->
Error
@ -1165,10 +1166,11 @@ contract_call(CallerID, Gas, AACI, ConID, Fun, Args) ->
case next_nonce(CallerID) of
{ok, Nonce} ->
GasPrice = min_gas_price(),
Fee = min_fee(),
Amount = 0,
{ok, Height} = top_height(),
TTL = Height + 262980,
contract_call(CallerID, Nonce,
Gas, GasPrice, Fee, Amount,
Gas, GasPrice, Amount, TTL,
AACI, ConID, Fun, Args);
Error ->
Error
@ -1176,14 +1178,14 @@ contract_call(CallerID, Gas, AACI, ConID, Fun, Args) ->
-spec contract_call(CallerID, Nonce,
Gas, GasPrice, Fee, Amount,
Gas, GasPrice, Amount, TTL,
AACI, ConID, Fun, Args) -> Result
when CallerID :: unicode:chardata(),
Nonce :: pos_integer(),
Gas :: pos_integer(),
GasPrice :: pos_integer(),
Fee :: non_neg_integer(),
Amount :: non_neg_integer(),
TTL :: pos_integer(),
AACI :: map(),
ConID :: unicode:chardata(),
Fun :: string(),
@ -1250,13 +1252,6 @@ contract_call(CallerID, Gas, AACI, ConID, Fun, Args) ->
%% transaction, thus making miners more likely to prioritize the high value ones.
%% </li>
%% <li>
%% <b>Fee:</b>
%% This value should really be caled `Bribe' or `Tip'.
%% This is a flat fee in aettos that is paid into the block reward, thereby allowing
%% an additional way to prioritize a given transaction above others, even if the
%% transaction will not consume much gas.
%% </li>
%% <li>
%% <b>Amount:</b>
%% All Gajumaru transactions can carry an "amount" spent from the origin account
%% (in this case the `CallerID') to the destination. In a "Spend" transaction this
@ -1300,33 +1295,32 @@ contract_call(CallerID, Gas, AACI, ConID, Fun, Args) ->
%% if you do not already have a copy, and can check the spec of a function before
%% trying to form a contract call.
contract_call(CallerID, Nonce, Gas, GP, Fee, Amount, AACI, ConID, Fun, Args) ->
contract_call(CallerID, Nonce, Gas, GP, Amount, TTL, AACI, ConID, Fun, Args) ->
case encode_call_data(AACI, Fun, Args) of
{ok, CD} -> contract_call2(CallerID, Nonce, Gas, GP, Fee, Amount, ConID, CD);
{ok, CD} -> contract_call2(CallerID, Nonce, Gas, GP, Amount, TTL, ConID, CD);
Error -> Error
end.
contract_call2(CallerID, Nonce, Gas, GasPrice, Fee, Amount, ConID, CallData) ->
contract_call2(CallerID, Nonce, Gas, GasPrice, Amount, TTL, ConID, CallData) ->
CallerBin = unicode:characters_to_binary(CallerID),
try
{account_pubkey, PK} = aeser_api_encoder:decode(CallerBin),
contract_call3(PK, Nonce, Gas, GasPrice, Fee, Amount, ConID, CallData)
contract_call3(PK, Nonce, Gas, GasPrice, Amount, TTL, ConID, CallData)
catch
Error:Reason -> {Error, Reason}
end.
contract_call3(PK, Nonce, Gas, GasPrice, Fee, Amount, ConID, CallData) ->
contract_call3(PK, Nonce, Gas, GasPrice, Amount, TTL, ConID, CallData) ->
ConBin = unicode:characters_to_binary(ConID),
try
{contract_pubkey, CK} = aeser_api_encoder:decode(ConBin),
contract_call4(PK, Nonce, Gas, GasPrice, Fee, Amount, CK, CallData)
contract_call4(PK, Nonce, Gas, GasPrice, Amount, TTL, CK, CallData)
catch
Error:Reason -> {Error, Reason}
end.
contract_call4(PK, Nonce, Gas, GasPrice, Fee, Amount, CK, CallData) ->
ABI = 3,
TTL = 0,
contract_call4(PK, Nonce, Gas, GasPrice, Amount, TTL, CK, CallData) ->
ABI = 1,
CallVersion = 1,
Type = contract_call_tx,
Fields =
@ -1334,22 +1328,20 @@ contract_call4(PK, Nonce, Gas, GasPrice, Fee, Amount, CK, CallData) ->
{nonce, Nonce},
{contract_id, aeser_id:create(contract, CK)},
{abi_version, ABI},
{fee, Fee},
{ttl, TTL},
{amount, Amount},
{gas, Gas},
{gas_price, GasPrice},
{gas, Gas},
{call_data, CallData}],
Template =
[{caller_id, id},
{nonce, int},
{contract_id, id},
{abi_version, int},
{fee, int},
{ttl, int},
{amount, int},
{gas, int},
{gas_price, int},
{gas, int},
{call_data, binary}],
TXB = aeser_chain_objects:serialize(Type, CallVersion, Template, Fields),
try
@ -1988,7 +1980,7 @@ aaci_lookup_spec({aaci, _, FunDefs, _}, Fun) ->
%% contention becomes an issue.
min_gas_price() ->
1000000000.
1_000_000_000.
-spec min_gas() -> integer().
@ -2005,19 +1997,6 @@ min_gas() ->
20000.
-spec min_fee() -> integer().
%% @doc
%% This function always returns 200,000,000,000,000 in the current version.
%%
%% This is the minimum fee amount currently accepted -- it is up to callers whether
%% they want to customize this value higher (or possibly lower, though as things stand
%% that would only work on an independent AE-based network, not the actual Gajumaru
%% mainnet or testnet).
min_fee() ->
200000000000000.
encode_call_data({aaci, _ContractName, FunDefs, _TypeDefs}, Fun, Args) ->
case maps:find(Fun, FunDefs) of
{ok, {ArgDef, _ResultDef}} -> encode_call_data2(ArgDef, Fun, Args);
@ -2052,10 +2031,9 @@ verify_signature(Sig, Message, PubKey) ->
end.
verify_signature2(Sig, Message, PK) ->
% Superhero salts/hashes the message before signing it, in order to protect
% Gajumaru signatures require messages to be salted and hashed, then
% the hash is what gets signed in order to protect
% the user from accidentally signing a transaction disguised as a message.
% In order to verify the signature, we have to duplicate superhero's
% salt/hash procedure here.
%
% Salt the message then hash with blake2b. See:
% 1. Erlang Blake2 blake2b/2 function:
@ -2064,13 +2042,13 @@ verify_signature2(Sig, Message, PK) ->
% https://gitlab.com/ioecs/aepp-sdk-js/blob/370f1e30064ad0239ba59931908d9aba0a2e86b6/src/utils/crypto.ts#L171-L175
% 3. SDK hashing:
% https://gitlab.com/ioecs/aepp-sdk-js/blob/370f1e30064ad0239ba59931908d9aba0a2e86b6/src/utils/crypto.ts#L83-L85
Prefix = <<"aeternity Signed Message:\n">>,
% Prefix = <<"gajumaru Signed Message:\n">>, % TODO: Switch the prefix after we kill Superhero
Prefix = <<"Gajumaru 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)>>,
% Signature = <<(binary_to_integer(Sig, 16)):(64 * 8)>>,
Signature = base64:decode(Sig),
Result = ecu_eddsa:sign_verify_detached(Signature, Hashed, PK),
{ok, Result}.

View File

@ -1,5 +1,3 @@
%%% @private
-module(hz_fetcher).
-vsn("0.4.1").
-author("Craig Everett <ceverett@tsuriai.jp>").