diff --git a/NOTES.txt b/NOTES.txt index 9c8f372..fea9542 100644 --- a/NOTES.txt +++ b/NOTES.txt @@ -1,3 +1,9 @@ +2026-02-25(PRH): + grids: instead of spying the chain for a tx, let's use dead drop + + - so for "generate", need to form tx for the user to sign + - make dead drop -> have process that knows each tx and the dead drop location + OPEN LOOPS - 2025-11-12 - websockets - separate websocket handling from websocket parsing/sending diff --git a/priv/static/js/dist/grids-basic.js b/priv/static/js/dist/grids-basic.js index d338d7c..9255f69 100644 --- a/priv/static/js/dist/grids-basic.js +++ b/priv/static/js/dist/grids-basic.js @@ -59,7 +59,7 @@ async function grids_request(net_id, recipient, amount, payload) { 'amount': amount, 'payload': payload }; let obj_text = JSON.stringify(obj, undefined, 4); - let url = '/grids-spend'; + let url = '/grids-mkdd'; let req_options = { method: 'POST', headers: { 'content-type': 'application/json' }, body: obj_text }; diff --git a/priv/static/js/dist/grids-basic.js.map b/priv/static/js/dist/grids-basic.js.map index 84466f6..2153be8 100644 --- a/priv/static/js/dist/grids-basic.js.map +++ b/priv/static/js/dist/grids-basic.js.map @@ -1 +1 @@ -{"version":3,"file":"grids-basic.js","sourceRoot":"","sources":["../ts/grids-basic.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"grids-basic.js","sourceRoot":"","sources":["../ts/grids-basic.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/priv/static/js/ts/grids-basic.ts b/priv/static/js/ts/grids-basic.ts index f1e8390..a81526f 100644 --- a/priv/static/js/ts/grids-basic.ts +++ b/priv/static/js/ts/grids-basic.ts @@ -103,7 +103,7 @@ grids_request let obj_text : string = JSON.stringify(obj, undefined, 4); - let url = '/grids-spend'; + let url = '/grids-mkdd'; let req_options = {method: 'POST', headers: {'content-type': 'application/json'}, body: obj_text}; diff --git a/src/fd_gridsd.erl b/src/fd_gridsd.erl index 275bf30..da1d8b3 100644 --- a/src/fd_gridsd.erl +++ b/src/fd_gridsd.erl @@ -9,7 +9,7 @@ -export([ %% caller context - mk_spend/4, + mkdd/4, %% api start_link/0, @@ -22,16 +22,26 @@ -include("$zx_include/zx_logger.hrl"). %% for craig's autism --record(sp, - {recipient :: string(), - amount :: non_neg_integer(), - payload :: binary()}). +%-type grids_get_response() :: #{"grids" := 1, +% "chain" := "gajumaru", +% "network_id" := "groot.testnet", +% "type" := "tx", +% "public_id" := false, +% "payload" := string()}. +% +% semantic type for hex encoding of a random binary string +-type hex() :: binary(). +-define(SEC, 1). +-define(MIN, 60*SEC). +-define(DEAD_DROP_TTL, 30*MIN). --type search_pattern() :: #sp{}. --type sp() :: search_pattern(). +-record(dd, + {created_at :: integer(), + payload :: string()}). +-type dd() :: #dd{}. -record(s, - {looking_for = [] :: [{sp(), NotifyWhenSeen :: pid()}]}). + {drops = #{} :: #{hex() := dd()}}). -type state() :: #s{}. @@ -40,7 +50,7 @@ %% caller context %%----------------------------------------------------------------------------- --spec mk_spend(NetworkId, Recipient, Amount, Payload) -> Result +-spec mkdd(NetworkId, Recipient, Amount, Payload) -> Result when NetworkId :: string(), Recipient :: string(), Amount :: non_neg_integer(), @@ -50,10 +60,10 @@ URL :: string(), QR_PNG :: binary(). % @doc -% Very important: amount MUST be an integer >= 0 +% make a dead drop -mk_spend(NetworkId, Recipient, Amount, Payload) -> - gen_server:call(?MODULE, {mk_spend, NetworkId, Recipient, Amount, Payload}). +mkdd(NetworkId, Recipient, Amount, Payload) -> + gen_server:call(?MODULE, {mkdd, NetworkId, Recipient, Amount, Payload}). %% gen_server callbacks @@ -73,8 +83,8 @@ init(none) -> {ok, InitState}. -handle_call({mk_spend, NetworkId, Recipient, Amount, Payload}, From, State) -> - case i_mk_spend(NetworkId, Recipient, Amount, Payload, From, State) of +handle_call({mkdd, NetworkId, Recipient, Amount, Payload}, From, State) -> + case do_mkdd(NetworkId, Recipient, Amount, Payload, State) of {ok, URL, PNG, NewState} -> {reply, {ok, URL, PNG}, NewState}; Error -> {reply, Error, State} end; @@ -104,67 +114,46 @@ terminate(_, _) -> %% internals %%----------------------------------------------------------------------------- --spec i_mk_spend(NetworkId, Recipient, Amount, Payload, From, State) -> Result +-spec do_mkdd(NetworkId, Recipient, Amount, Payload, State) -> Result when NetworkId :: string(), Recipient :: string(), - Amount :: non_neg_integer(), + Amount :: pos_integer(), Payload :: binary(), - From :: {pid(), reference()}, State :: state(), Result :: {ok, URL, QR_PNG, NewState} - | {error, string()}, - URL :: string(), - QR_PNG :: binary(), - NewState :: state(). + | {error, Reason}, + URL :: string(), + QR_PNG :: binary(), + NewState :: state(), + Reason :: any(). -i_mk_spend(NetworkId, Recipient, Amount, Payload, {FromPID, _}, State) - when is_integer(Amount), Amount >= 0, - is_binary(Payload) -> - URL = gmgrids:encode({spend, NetworkId, Recipient}, - [{amount, Amount}, - {payload, Payload}]), - URLBin = unicode:characters_to_binary(URL), - PNG = qr:encode_png(URLBin), - case i_register(Recipient, Amount, Payload, FromPID, State) of - {ok, NewState} -> {ok, URL, PNG, NewState}; - Error -> Error - end; -i_mk_spend(_, _, Amount, _, _, _) when (not is_integer(Amount)) -> - {error, "non_integer_amount"}; -i_mk_spend(_, _, Amount, _, _, _) when Amount < 0 -> - {error, "negative_amount"}; -i_mk_spend(_, _, _, _, _, _) -> - {error, "bad_payload"}. +do_mkdd(NetId, Recipient, Amount, Payload, State = #s{drops = Drops}) -> + HexStr = rand_hex(), + CreatedAt = now_sec(), + TxStr = form_txstr(NetId, Recipient, Amount, Payload), + DD = #dd{created_at = CreatedAt, payload = TxStr}, + URL = dd_url(HexStr), + PNG = qr:encode_png(unicode:characters_to_binary(URL)), + NewDrops = maps:put(HexStr, DD, Drops), + NewState = State#s{drops = NewDrops}, + {ok, URL, PNG, NewState}. + +now_sec() -> + calendar:datetime_to_gregorian_seconds(calendar:universal_time()). -i_register(Recipient, Amount, Payload, FromPID, State = #s{looking_for = Patterns}) -> - SP = i_sp(Recipient, Amount, Payload), - case i_lookup(SP, State) of - not_found -> - NewPattern = {SP, FromPID}, - NewPatterns = [NewPattern | Patterns], - NewState = State#s{looking_for = NewPatterns}, - {ok, NewState}; - {found, _} -> - {error, already_registered} - end. +-spec rand_hex() -> hex(). +rand_hex() -> + unicode:characters_to_binary(hexify(rand:bytes(10))). -%% future proofing -i_sp(Recipient, Amount, Payload) -> - {sp, Recipient, Amount, Payload}. +hexify(<>) when B < 16#10 -> ["0", integer_to_list(B, 16), hexify(Rest)]; +hexify(<>) -> [integer_to_list(B, 16), hexify(Rest)]; +hexify(<<>>) -> []. + +dd_url(HexStr) -> + unicode:characters_to_list(["grids://", fewd:host(), "/1/d/", HexStr]). --spec i_lookup(SearchPattern, State) -> Result - when SearchPattern :: sp(), - State :: state(), - Result :: {found, NotifyPID :: pid()} - | not_found. - -% @private look up search pattern and see if we're looking for it - -i_lookup(SearchPattern, State) -> - #s{looking_for = Patterns} = State, - case lists:keyfind(SearchPattern, 1, Patterns) of - false -> not_found; - {_, Notify} -> {found, Notify} - end. +% ref +form_txstr(_NetId, _Recipient, _Amount, _Payload) -> + "foobar". diff --git a/src/fd_httpd_client.erl b/src/fd_httpd_client.erl index 9c0b9c1..6b8426d 100644 --- a/src/fd_httpd_client.erl +++ b/src/fd_httpd_client.erl @@ -238,9 +238,9 @@ route(Sock, get, Route, Request, Received) -> end; route(Sock, post, Route, Request, Received) -> case Route of - <<"/grids-spend">> -> grids_spend(Sock, Request) , Received; - <<"/wfcin">> -> wfcin(Sock, Request) , Received; - _ -> fd_httpd_utils:http_err(Sock, 404) , Received + <<"/grids-mkdd">> -> grids_mkdd(Sock, Request) , Received; + <<"/wfcin">> -> wfcin(Sock, Request) , Received; + _ -> fd_httpd_utils:http_err(Sock, 404) , Received end; route(Sock, _, _, _, Received) -> fd_httpd_utils:http_err(Sock, 404), @@ -324,14 +324,14 @@ ws_echo_loop(Sock, Frames, Received) -> %% grids %% ------------------------------ -grids_spend(Sock, #request{enctype = json, - body = B = #{"network_id" := NetId, - "recipient" := Recipient, - "amount" := Amount, - "payload" := Payload}}) -> - tell("grids_spend good request: ~tp", [B]), +grids_mkdd(Sock, #request{enctype = json, + body = B = #{"network_id" := NetId, + "recipient" := Recipient, + "amount" := Amount, + "payload" := Payload}}) -> + tell("grids_mkdd good request: ~tp", [B]), RespObj = - case fd_gridsd:mk_spend(NetId, Recipient, Amount, unicode:characters_to_binary(Payload)) of + case fd_gridsd:mkdd(NetId, Recipient, Amount, unicode:characters_to_binary(Payload)) of {ok, URL, PNG} -> #{"ok" => true, "result" => #{"url" => URL, @@ -345,8 +345,8 @@ grids_spend(Sock, #request{enctype = json, Response = #response{headers = [{"content-type", "application/json"}], body = Body}, fd_httpd_utils:respond(Sock, Response); -grids_spend(Sock, Request) -> - tell("grids_spend: bad request: ~tp", [Request]), +grids_mkdd(Sock, Request) -> + tell("grids_mkdd: bad request: ~tp", [Request]), fd_httpd_utils:http_err(Sock, 400). diff --git a/src/fewd.erl b/src/fewd.erl index 0647131..4ac3d53 100644 --- a/src/fewd.erl +++ b/src/fewd.erl @@ -9,13 +9,15 @@ -copyright("Peter Harpending "). -license("BSD-2-Clause-FreeBSD"). --export([network_id/0, pubkey/0, akstr/0]). +-export([url/0, host/0, network_id/0, pubkey/0, akstr/0]). -export([listen/1, ignore/0]). -export([start/2, stop/1]). -include("$zx_include/zx_logger.hrl"). +url() -> "http://" ++ host(). +host() -> "localhost:8000". network_id() -> "groot.testnet". pubkey() -> pad32(<<"fewd demo">>). akstr() -> gmgrids:akstr(pubkey()).