shrub/app/btc-provider.hoon

335 lines
8.4 KiB
Plaintext
Raw Normal View History

2020-10-20 17:13:52 +03:00
:: btc-provider.hoon
:: Proxy that serves a BTC full node and ElectRS address indexer
::
2020-10-28 16:20:24 +03:00
:: Subscriptions: none
2020-11-07 18:51:31 +03:00
:: To Subscribers: /clients
2020-10-28 16:20:24 +03:00
:: current connection state
:: results/errors of RPC calls
::
:: Scrys
:: x/is-whitelisted/SHIP: bool, whether ship is whitelisted
::
2021-02-21 13:13:50 +03:00
/- *bitcoin, json-rpc, *btc-provider
/+ dbug, default-agent, bl=btc, groupl=group, resource
|%
+$ versioned-state
$% state-0
==
::
+$ state-0 [%0 =host-info =whitelist]
::
+$ card card:agent:gall
::
--
%- agent:dbug
=| state-0
=* state -
^- agent:gall
=<
|_ =bowl:gall
+* this .
def ~(. (default-agent this %|) bowl)
hc ~(. +> bowl)
::
++ on-init
^- (quip card _this)
~& > '%btc-provider initialized successfully'
=| wl=^whitelist
:- ~
%_ this
host-info
['' connected=%.n %main block=0 clients=*(set ship)]
whitelist wl(public %.n, kids %.n)
==
::
++ on-save
^- vase
!>(state)
::
++ on-load
|= old-state=vase
^- (quip card _this)
2020-10-20 17:13:52 +03:00
~& > '%btc-provider recompiled successfully '
`this(state !<(versioned-state old-state))
::
++ on-poke
|= [=mark =vase]
^- (quip card _this)
?> ?|((team:title our.bowl src.bowl) (is-client:hc src.bowl))
=^ cards state
?+ mark (on-poke:def mark vase)
2020-11-07 14:56:11 +03:00
%btc-provider-command
?> (team:title our.bowl src.bowl)
(handle-command:hc !<(command vase))
%btc-provider-action
(handle-action:hc !<(action vase))
==
[cards this]
::
++ on-watch
|= pax=path
^- (quip card _this)
2020-11-07 18:51:31 +03:00
?> ?=([%clients *] pax)
?. (is-whitelisted:hc src.bowl)
~& >>> "btc-provider: blocked client {<src.bowl>}"
[~[[%give %kick ~ ~]] this]
2021-01-28 14:03:53 +03:00
~& > "btc-provider: accepted client {<src.bowl>}"
2020-11-16 20:06:45 +03:00
:- do-ping:hc
this(clients.host-info (~(put in clients.host-info) src.bowl))
::
++ on-arvo
|= [=wire =sign-arvo]
^- (quip card _this)
2020-11-09 13:48:03 +03:00
:: check for connectivity every 30 seconds
2020-11-07 18:51:31 +03:00
::
2020-11-07 14:56:11 +03:00
?: ?=([%ping-timer *] wire)
2020-11-10 16:29:50 +03:00
[do-ping:hc this]
=^ cards state
?+ +<.sign-arvo (on-arvo:def wire sign-arvo)
%http-response
2021-01-26 14:27:32 +03:00
(handle-rpc-response:hc wire client-response.sign-arvo)
==
[cards this]
::
++ on-peek
|= pax=path
^- (unit (unit cage))
?+ pax (on-peek:def pax)
[%x %is-whitelisted @t ~]
``noun+!>((is-whitelisted:hc (ship (slav %p +>-.pax))))
::
[%x %is-client @t ~]
``noun+!>((is-client (ship (slav %p +>-.pax))))
==
::
++ on-leave on-leave:def
++ on-agent on-agent:def
++ on-fail on-fail:def
--
:: helper core
|_ =bowl:gall
2020-11-07 18:51:31 +03:00
++ handle-command
|= comm=command
^- (quip card _state)
?- -.comm
%set-credentials
2020-11-10 16:29:50 +03:00
:- do-ping
2021-01-28 14:03:53 +03:00
state(host-info [api-url.comm connected=%.n network.comm block=0 clients=*(set ship)])
2020-11-07 18:51:31 +03:00
::
%add-whitelist
?- -.wt.comm
%public
`state(public.whitelist %.y)
::
%kids
`state(kids.whitelist %.y)
::
%users
`state(users.whitelist (~(uni in users.whitelist) users.wt.comm))
::
%groups
`state(groups.whitelist (~(uni in groups.whitelist) groups.wt.comm))
==
2021-02-15 11:23:56 +03:00
::
%remove-whitelist
=. state
?- -.wt.comm
%public
state(public.whitelist %.n)
::
%kids
state(kids.whitelist %.n)
::
%users
state(users.whitelist (~(dif in users.whitelist) users.wt.comm))
::
%groups
state(groups.whitelist (~(dif in groups.whitelist) groups.wt.comm))
==
2021-02-15 11:23:56 +03:00
clean-client-list
==
2020-11-09 13:48:03 +03:00
:: if not connected, only %ping action is allowed
2020-11-07 18:51:31 +03:00
::
++ handle-action
|= act=action
2020-11-09 12:53:30 +03:00
^- (quip card _state)
2020-12-23 14:21:48 +03:00
?. ?|(connected.host-info =(-.act %ping))
2020-11-07 14:56:11 +03:00
~& >>> "Not connected to RPC"
2020-11-09 15:29:30 +03:00
[~[(send-update [%| %not-connected 500])] state]
2021-02-15 11:23:56 +03:00
?: ?&(?=(%check-network -.act) ?!(=(network.act network.host-info)))
%- (slog ~[leaf+"network mismatch with client {<src.bowl>}"])
(kick-client src.bowl)
2020-12-13 13:04:47 +03:00
=/ ract=action:rpc-types
2021-02-15 11:23:56 +03:00
?+ -.act ~|("Invalid action" !!)
2020-11-09 12:53:30 +03:00
%address-info
2020-12-23 14:21:48 +03:00
[%get-address-info address.act]
2020-12-06 20:53:02 +03:00
::
%tx-info
2020-12-23 14:21:48 +03:00
[%get-tx-vals txid.act]
2020-11-20 14:58:27 +03:00
::
%raw-tx
2020-12-23 14:21:48 +03:00
[%get-raw-tx txid.act]
2020-12-13 13:04:47 +03:00
::
2020-12-15 19:05:50 +03:00
%broadcast-tx
2020-12-23 14:21:48 +03:00
[%broadcast-tx rawtx.act]
2020-10-20 17:13:52 +03:00
==
2020-11-09 12:53:30 +03:00
[~[(req-card act ract)] state]
2020-11-20 14:58:27 +03:00
::
2020-11-09 12:53:30 +03:00
++ req-card
2020-12-13 13:04:47 +03:00
|= [act=action ract=action:rpc-types]
2020-11-09 12:53:30 +03:00
=| out=outbound-config:iris
=/ req=request:http
2021-02-21 13:13:50 +03:00
(gen-request:bl host-info ract)
2020-11-13 16:39:34 +03:00
[%pass (rpc-wire act) %arvo %i %request req out]
2020-12-23 14:21:48 +03:00
:: wire structure: /action-tas/now
2020-11-09 12:53:30 +03:00
::
2020-11-13 16:39:34 +03:00
++ rpc-wire
2020-11-12 18:20:54 +03:00
|= act=action ^- wire
2020-12-24 12:58:05 +03:00
/[-.act]/[(scot %ux (cut 3 [0 20] eny.bowl))]
2020-11-13 15:47:11 +03:00
::
2021-02-15 11:23:56 +03:00
++ kick-client
|= client=ship
^- (quip card _state)
~& >>> "dropping client {<client>}"
:- ~[[%give %kick ~[/clients] `client]]
2021-02-18 15:13:06 +03:00
state(clients.host-info (~(dif in clients.host-info) (silt ~[client])))
2021-02-15 11:23:56 +03:00
::
2020-11-13 13:14:35 +03:00
:: Handles HTTP responses from RPC servers. Parses for errors, then handles response.
2020-11-09 15:29:30 +03:00
:: For actions that require collating multiple RPC calls, uses req-card to call out
:: to RPC again if more information is required.
2020-11-07 18:51:31 +03:00
::
++ handle-rpc-response
2020-10-20 17:13:52 +03:00
|= [=wire response=client-response:iris]
^- (quip card _state)
?. ?=(%finished -.response) `state
2020-11-07 12:14:34 +03:00
=* status status-code.response-header.response
2020-11-09 12:53:30 +03:00
:: handle error types: connection errors, RPC errors (in order)
2020-11-07 12:14:34 +03:00
::
=^ conn-err state
(connection-error status)
?^ conn-err
2020-11-10 14:09:59 +03:00
:_ state(connected.host-info %.n)
2020-11-16 20:06:45 +03:00
~[(send-status [%disconnected ~]) (send-update [%| u.conn-err])]
2020-11-07 12:14:34 +03:00
::
2020-12-13 23:33:08 +03:00
%+ handle-rpc-result wire
2021-02-21 13:13:50 +03:00
%- parse-result:rpc:bl
(get-rpc-response:bl response)
2020-11-07 12:14:34 +03:00
::
++ connection-error
2020-10-23 20:35:04 +03:00
|= status=@ud
2020-11-07 12:14:34 +03:00
^- [(unit error) _state]
2020-12-13 13:04:47 +03:00
?+ status [`[%rpc-error ~] state]
2020-11-07 12:14:34 +03:00
%200
[~ state]
%400
[`[%bad-request status] state]
2020-11-07 18:51:31 +03:00
%401
[`[%no-auth status] state(connected.host-info %.n)]
2020-11-07 12:14:34 +03:00
%502
[`[%not-connected status] state(connected.host-info %.n)]
2020-11-07 12:14:34 +03:00
%504
[`[%not-connected status] state(connected.host-info %.n)]
==
::
2020-12-13 13:04:47 +03:00
++ handle-rpc-result
|= [=wire r=result:rpc-types]
^- (quip card _state)
2020-12-23 14:21:48 +03:00
?+ -.wire ~|("Unexpected HTTP response" !!)
%address-info
2020-12-13 13:04:47 +03:00
?> ?=([%get-address-info *] r)
:_ state
2020-12-23 14:21:48 +03:00
~[(send-update [%.y %address-info +.r])]
2020-12-13 13:04:47 +03:00
::
2020-12-23 14:21:48 +03:00
%tx-info
2020-12-13 13:04:47 +03:00
?> ?=([%get-tx-vals *] r)
:_ state
2020-12-23 14:21:48 +03:00
~[(send-update [%.y %tx-info +.r])]
2020-12-13 13:04:47 +03:00
::
2020-12-23 14:21:48 +03:00
%raw-tx
2020-12-13 13:04:47 +03:00
?> ?=([%get-raw-tx *] r)
:_ state
2020-12-23 14:21:48 +03:00
~[(send-update [%.y %raw-tx +.r])]
2020-12-13 20:00:52 +03:00
::
2020-12-23 14:21:48 +03:00
%broadcast-tx
2020-12-13 20:00:52 +03:00
?> ?=([%broadcast-tx *] r)
:_ state
2020-12-23 14:21:48 +03:00
~[(send-update [%.y %broadcast-tx +.r])]
2020-12-13 13:04:47 +03:00
::
2020-12-23 14:21:48 +03:00
%ping
2021-01-26 14:27:32 +03:00
?> ?=([%get-block-info *] r)
:_ state(connected.host-info %.y, block.host-info block.r)
?: =(block.host-info block.r)
2021-02-15 11:23:56 +03:00
~[(send-status [%connected network.host-info block.r fee.r])]
~[(send-status [%new-block network.host-info block.r fee.r blockhash.r blockfilter.r])]
2020-12-13 13:04:47 +03:00
==
::
2020-11-10 14:09:59 +03:00
++ send-status
|= =status ^- card
%- ?: ?=(%new-block -.status)
~&(>> "%new-block: {<block.status>}" same)
same
2020-11-10 16:29:50 +03:00
[%give %fact ~[/clients] %btc-provider-status !>(status)]
2020-11-19 12:45:05 +03:00
::
2020-10-23 20:35:04 +03:00
++ send-update
2020-11-09 15:29:30 +03:00
|= =update
^- card
2020-12-09 11:56:38 +03:00
=+ c=[%give %fact ~[/clients] %btc-provider-update !>(update)]
?: ?=(%.y -.update)
:: ~& >> "prov. update: {<p.update>}"
2020-12-09 11:56:38 +03:00
c
2020-12-23 14:21:48 +03:00
~& >> "prov. err: {<p.update>}"
2020-12-09 11:56:38 +03:00
c
2020-11-07 12:14:34 +03:00
::
++ is-whitelisted
|= user=ship ^- ?
|^
?| public.whitelist
=(our.bowl user)
?&(kids.whitelist is-kid)
(~(has in users.whitelist) user)
in-group
==
++ is-kid
=(our.bowl (sein:title our.bowl now.bowl user))
++ in-group
=/ gs ~(tap in groups.whitelist)
|-
?~ gs %.n
2021-02-18 15:59:32 +03:00
?: (~(is-member groupl bowl) user i.gs)
%.y
$(gs t.gs)
:: .^((unit group:g) %gx ;:(weld /=group-store=/groups p /noun))
--
:: +clean-client-list: remove clients who are no longer whitelisted
:: called after a whitelist change
::
++ clean-client-list
2021-02-15 11:23:56 +03:00
^- (quip card _state)
=/ to-kick=(set ship)
2021-02-18 15:13:06 +03:00
%- silt
2021-02-15 11:23:56 +03:00
%+ murn ~(tap in clients.host-info)
|= c=ship ^- (unit ship)
?:((is-whitelisted c) ~ `c)
:_ state(clients.host-info (~(dif in clients.host-info) to-kick))
%+ turn ~(tap in to-kick)
|=(c=ship [%give %kick ~[/clients] `c])
2020-11-10 16:29:50 +03:00
::
++ is-client
|= user=ship ^- ?
2020-10-22 10:36:25 +03:00
(~(has in clients.host-info) user)
2020-11-10 16:29:50 +03:00
::
++ start-ping-timer
|= interval=@dr ^- card
[%pass /ping-timer %arvo %b %wait (add now.bowl interval)]
::
++ do-ping
^- (list card)
2020-12-24 12:58:05 +03:00
=/ act=action [%ping ~]
2020-11-10 16:29:50 +03:00
:~ :* %pass /ping/[(scot %da now.bowl)] %agent
[our.bowl %btc-provider] %poke
2020-12-24 12:58:05 +03:00
%btc-provider-action !>(act)
2020-11-10 16:29:50 +03:00
==
(start-ping-timer ~s30)
==
--