diff --git a/src/hz.erl b/src/hz.erl index 255aef8..2eef9c4 100644 --- a/src/hz.erl +++ b/src/hz.erl @@ -29,13 +29,13 @@ -license("GPL-3.0-or-later"). % Get/Set admin functions. --export([network_id/0, network_id/1, - chain_nodes/0, chain_nodes/1, +-export([chain_nodes/0, chain_nodes/1, tls/0, tls/1, timeout/0, timeout/1]). % Node JSON query interface functions --export([top_height/0, top_block/0, +-export([network_id/0, + top_height/0, top_block/0, kb_current/0, kb_current_hash/0, kb_current_height/0, kb_pending/0, kb_by_hash/1, kb_by_height/1, @@ -71,6 +71,8 @@ contract_call/6, contract_call/10, decode_bytearray_fate/1, decode_bytearray/2, + spend/5, spend/10, + sign_tx/2, sign_tx/3, verify_signature/3]). @@ -225,17 +227,10 @@ %% call data or perform other actions on chain that require a signature. network_id() -> - hz_man:network_id(). - - --spec network_id(Identifier) -> ok | {error, Reason} - when Identifier :: string() | none, - Reason :: not_started. -%% @doc -%% Sets the network ID, or returns `not_started' if the service is not yet started. - -network_id(Identifier) -> - hz_man:network_id(Identifier). + case status() of + {ok, #{"network_id" := NetworkID}} -> {ok, NetworkID}; + Error -> Error + end. -spec chain_nodes() -> [chain_node()]. @@ -2110,6 +2105,172 @@ encode_call_data2(ArgDef, Fun, Args) -> end. +sign_tx(Unsigned, SecKey) -> + case network_id() of + {ok, NetworkID} -> sign_tx(Unsigned, SecKey, NetworkID); + Error -> Error + end. + +sign_tx(Unsigned, SecKey, MNetworkID) -> + UnsignedBin = unicode:characters_to_binary(Unsigned), + NetworkID = unicode:characters_to_binary(MNetworkID), + {ok, TX_Data} = gmser_api_encoder:safe_decode(transaction, UnsignedBin), + {ok, Hash} = eblake2:blake2b(32, TX_Data), + NetworkHash = <>, + Signature = ecu_eddsa:sign_detached(NetworkHash, SecKey), + SigTxType = signed_tx, + SigTxVsn = 1, + SigTemplate = + [{signatures, [binary]}, + {transaction, binary}], + TX = + [{signatures, [Signature]}, + {transaction, TX_Data}], + SignedTX = gmser_chain_objects:serialize(SigTxType, SigTxVsn, SigTemplate, TX), + gmser_api_encoder:encode(transaction, SignedTX). + + +spend(SenderID, SecKey, ReceipientID, Amount, Payload) -> + case status() of + {ok, #{"top_block_height" := Height, "network_id" := NetworkID}} -> + spend(SenderID, SecKey, ReceipientID, Amount, Payload, Height, NetworkID); + Error -> + Error + end. + +spend(SenderID, SecKey, RecipientID, Amount, Payload, Height, NetworkID) -> + case next_nonce(SenderID) of + {ok, Nonce} -> + {ok, Height} = top_height(), + TTL = Height + 262980, + Gas = 20000, + GasPrice = min_gas_price(), + spend(SenderID, + SecKey, + RecipientID, + Amount, + GasPrice, + Gas, + TTL, + Nonce, + Payload, + NetworkID); + Error -> + Error + end. + + +spend(SenderID, + SecKey, + RecipientID, + Amount, + GasPrice, + Gas, + TTL, + Nonce, + Payload, + NetworkID) -> + case decode_account_id(unicode:characters_to_binary(SenderID)) of + {ok, DSenderID} -> + spend2(gmser_id:create(account, DSenderID), + SecKey, + RecipientID, + Amount, + GasPrice, + Gas, + TTL, + Nonce, + Payload, + NetworkID); + Error -> + Error + end. + +spend2(DSenderID, + SecKey, + RecipientID, + Amount, + GasPrice, + Gas, + TTL, + Nonce, + Payload, + NetworkID) -> + case decode_account_id(unicode:characters_to_binary(RecipientID)) of + {ok, DRecipientID} -> + spend3(DSenderID, + SecKey, + gmser_id:create(account, DRecipientID), + Amount, + GasPrice, + Gas, + TTL, + Nonce, + Payload, + NetworkID); + Error -> + Error + end. + + +decode_account_id(S) when is_list(S) -> + decode_account_id(list_to_binary(S)); +decode_account_id(B) -> + try + {account_pubkey, PK} = gmser_api_encoder:decode(B), + {ok, PK} + catch + E:R -> {E, R} + end. + + +spend3(DSenderID, + SecKey, + DRecipientID, + Amount, + GasPrice, + Gas, + TTL, + Nonce, + Payload, + MNetworkID) -> + NetworkID = unicode:characters_to_binary(MNetworkID), + Type = spend_tx, + Vsn = 1, + Fields = + [{sender_id, DSenderID}, + {recipient_id, DRecipientID}, + {amount, Amount}, + {gas_price, GasPrice}, + {gas, Gas}, + {ttl, TTL}, + {nonce, Nonce}, + {payload, Payload}], + Template = + [{sender_id, id}, + {recipient_id, id}, + {amount, int}, + {gas_price, int}, + {gas, int}, + {ttl, int}, + {nonce, int}, + {payload, binary}], + BinaryTX = gmser_chain_objects:serialize(Type, Vsn, Template, Fields), + NetworkTX = <>, + Signature = ecu_eddsa:sign_detached(NetworkTX, SecKey), + SigTxType = signed_tx, + SigTxVsn = 1, + SigTemplate = + [{signatures, [binary]}, + {transaction, binary}], + TX_Data = + [{signatures, [Signature]}, + {transaction, BinaryTX}], + SignedTX = gmser_chain_objects:serialize(SigTxType, SigTxVsn, SigTemplate, TX_Data), + Encoded = gmser_api_encoder:encode(transaction, SignedTX), + hz:post_tx(Encoded). + + -spec verify_signature(Sig, Message, PubKey) -> Result when Sig :: binary(), Message :: iodata(), diff --git a/src/hz_man.erl b/src/hz_man.erl index 82aa6f1..16b38d3 100644 --- a/src/hz_man.erl +++ b/src/hz_man.erl @@ -16,8 +16,7 @@ -license("MIT"). %% Admin functions --export([network_id/0, network_id/1, - tls/0, tls/1, +-export([tls/0, tls/1, chain_nodes/0, chain_nodes/1, timeout/0, timeout/1]). @@ -44,8 +43,7 @@ req = none :: none | binary()}). -record(s, - {network_id = "gm_mainnet" :: string(), - tls = false :: boolean(), + {tls = false :: boolean(), chain_nodes = {[], []} :: {[hz:chain_node()], [hz:chain_node()]}, sticky = none :: none | hz:chain_node(), fetchers = [] :: [#fetcher{}], @@ -58,19 +56,6 @@ %%% Service Interface --spec network_id() -> Name - when Name :: hz:network_id(). - -network_id() -> - gen_server:call(?MODULE, network_id). - - --spec network_id(Name) -> ok - when Name :: hz:network_id(). - -network_id(Name) -> - gen_server:cast(?MODULE, {network_id, Name}). - -spec tls() -> boolean(). @@ -163,8 +148,6 @@ init(none) -> handle_call({request, Request}, From, State) -> NewState = do_request(Request, From, State), {noreply, NewState}; -handle_call(network_id, _, State = #s{network_id = Name}) -> - {reply, Name, State}; handle_call(tls, _, State = #s{tls = TLS}) -> {reply, TLS, State}; handle_call(chain_nodes, _, State = #s{chain_nodes = {Wait, Used}}) -> @@ -177,8 +160,6 @@ handle_call(Unexpected, From, State) -> {noreply, State}. -handle_cast({network_id, Name}, State) -> - {noreply, State#s{network_id = Name}}; handle_cast({tls, Boolean}, State) -> NewState = do_tls(Boolean, State), {noreply, NewState};