mirror of
https://github.com/urbit/shrub.git
synced 2024-12-01 14:42:02 +03:00
1728 lines
50 KiB
Plaintext
1728 lines
50 KiB
Plaintext
:: roller: Azimuth L2 roll aggregator
|
|
::
|
|
:: general flow is as described below, to ensure transactions actually go
|
|
:: through once we start sending it out, in the dumbest reasonable way.
|
|
::
|
|
:: periodic timer fires:
|
|
:: if there are no pending l2 txs, do nothing.
|
|
:: else kick off tx submission flow:
|
|
:: "freeze" pending txs, store alongside nonce, then increment nonce,
|
|
:: kick off thread for sending the corresponding l1 tx:
|
|
:: if nonce doesn't match on-chain expected nonce, bail.
|
|
:: if we can't afford the tx fee, bail.
|
|
:: construct, sign, submit the l1 tx.
|
|
:: if thread bailed, retry in five minutes.
|
|
:: if thread succeeded, retry in five minutes with higher gas price.
|
|
:: when retrying, only do so if l2 txs remain in the "frozen" txs group.
|
|
:: on %tx diff from naive, remove the matching tx from the frozen group.
|
|
::
|
|
::TODO questions:
|
|
:: - it's a bit weird how we just assume the raw and tx in raw-tx to match...
|
|
::
|
|
/- *dice
|
|
/+ azimuth,
|
|
naive,
|
|
dice,
|
|
lib=naive-transactions,
|
|
default-agent,
|
|
ethereum,
|
|
dbug,
|
|
verb
|
|
::
|
|
|%
|
|
+$ app-state
|
|
$: %6
|
|
:: pending: the next l2 txs to be sent
|
|
:: sending: l2 txs awaiting l2 confirmation, ordered by nonce
|
|
:: finding: sig+raw-tx hash reverse lookup for txs in sending map
|
|
:: history: status of l2 txs by ethereum address, timestamp sorted
|
|
:: ship-quota: number of txs submited per ship in the current slice
|
|
:: allowances: specific no of allowed transactions per given ship
|
|
:: next-nonce: next l1 nonce to use
|
|
:: next-batch: when then next l2 batch will be sent
|
|
:: next-slice: when the global quota will be reset
|
|
:: pre: predicted l2 state
|
|
:: own: ownership of azimuth points
|
|
:: spo: residents and escapees, per sponsor
|
|
::
|
|
pending=(list pend-tx)
|
|
sending=(tree [l1-tx-pointer send-tx])
|
|
finding=(map keccak ?(%confirmed %failed [=time l1-tx-pointer]))
|
|
history=(map address:ethereum (tree hist-tx))
|
|
ship-quota=(map ship @ud)
|
|
allowances=(map ship (unit @ud))
|
|
next-nonce=(unit @ud)
|
|
next-batch=time
|
|
next-slice=time
|
|
pre=^state:naive
|
|
own=owners
|
|
spo=sponsors
|
|
::
|
|
:: pk: private key to send the roll
|
|
:: quota: max numbers of transactions per unit of time (slice)
|
|
:: slice: unit of time where txs are allowed to be added to pending
|
|
:: derive: defer derivation of predicted/ownership state
|
|
:: frequency: time to wait between sending batches (TODO fancier)
|
|
:: endpoint: ethereum rpc endpoint to use
|
|
:: contract: ethereum contract address
|
|
:: chain-id: mainnet, goerli, local (https://chainid.network/)
|
|
:: resend-time: time to resend a batch with higher gas prie
|
|
:: update-rate: frequency to update the roller's predicted state
|
|
:: fallback-gas-price: default batch gas price
|
|
::
|
|
pk=@
|
|
slice=@dr
|
|
quota=@ud
|
|
derive=?
|
|
frequency=@dr
|
|
endpoint=(unit @t)
|
|
contract=@ux
|
|
chain-id=@
|
|
resend-time=@dr
|
|
update-rate=@dr
|
|
fallback-gas-price=@ud
|
|
==
|
|
::
|
|
+$ action
|
|
$% :: submit: request to add a l2 tx to the pending queue
|
|
::
|
|
[%submit force=? =address:naive sig=@ tx=part-tx]
|
|
:: cancel: cancels a pending transaction
|
|
::
|
|
:: a signed message ("cancel: 0xkeccak") is used as ownership validation
|
|
::
|
|
[%cancel sig=@ keccak=@ =l2-tx =ship]
|
|
:: commit: manually commit a batch of pending txs
|
|
::
|
|
:: TODO: maybe pk=(unit @) later
|
|
::
|
|
[%commit ~]
|
|
:: config: configure the roller
|
|
::
|
|
[%config config]
|
|
:: assign: assign an allowance to a ship for submitting l2 txs
|
|
::
|
|
[%assign =ship quota=(unit @ud)]
|
|
:: refuel: bumps the next-gas-price of a sending tx
|
|
::
|
|
[%refuel nonce=@ address=(unit address:ethereum) gas=@ud]
|
|
==
|
|
::
|
|
+$ card card:agent:gall
|
|
++ lverb &
|
|
--
|
|
:: Helpers
|
|
::
|
|
=> |%
|
|
:: TODO /lib/sys.hoon?
|
|
::
|
|
++ sys
|
|
|%
|
|
++ b
|
|
|%
|
|
++ wait
|
|
|= [=wire =time]
|
|
^- card
|
|
[%pass wire %arvo %b %wait time]
|
|
--
|
|
--
|
|
:: TODO /lib/spider.hoon?
|
|
::
|
|
++ spider
|
|
|%
|
|
++ start-thread
|
|
|= [=bowl:gall =wire thread=term arg=vase]
|
|
^- (list card)
|
|
=/ =beak byk.bowl(r da+now.bowl)
|
|
=/ tid=@ta (rap 3 thread '--' (scot %uv eny.bowl) ~)
|
|
=/ args [~ `tid beak thread arg]
|
|
:~ [%pass wire %agent [our.bowl %spider] %watch /thread-result/[tid]]
|
|
[%pass wire %agent [our.bowl %spider] %poke %spider-start !>(args)]
|
|
==
|
|
::
|
|
++ leave
|
|
|= [agent=@p =path]
|
|
^- card
|
|
[%pass path %agent [agent %spider] %leave ~]
|
|
--
|
|
::
|
|
++ get-l1-address
|
|
|= [=tx:naive nas=^state:naive]
|
|
^- (unit address:ethereum)
|
|
?~ point=(get:orp:dice points.nas ship.from.tx) ~
|
|
=< `address
|
|
(proxy-from-point:naive proxy.from.tx u.point)
|
|
::
|
|
++ timer
|
|
|%
|
|
:: +set-roller: %wait until next whole frequency
|
|
::
|
|
++ set-roller
|
|
|= [frequency=@dr now=@da]
|
|
^- [=card =time]
|
|
=+ time=(mul +((div now frequency)) frequency)
|
|
[(wait:b:sys /timer time) time]
|
|
:: +set-roller: %wait until next whole :slice
|
|
::
|
|
++ set-quota
|
|
|= [slice=@dr now=@da]
|
|
^- [=card =time]
|
|
=+ time=(mul +((div now slice)) slice)
|
|
[(wait:b:sys /quota-timer time) time]
|
|
--
|
|
:: TODO: move address to state?
|
|
::
|
|
++ get-address
|
|
|= pk=@
|
|
^- address:ethereum
|
|
(address-from-prv:key:ethereum pk)
|
|
--
|
|
:: Cards
|
|
::
|
|
=> |%
|
|
++ emit
|
|
|= updates=(list update)
|
|
=| cards=(list card)
|
|
|- ^- (list card)
|
|
?~ updates (flop cards)
|
|
=* up i.updates
|
|
=/ [address=@t last-owner=(unit @t)]
|
|
?- -.up
|
|
%tx
|
|
:_ ~
|
|
(scot %ux address.pend-tx.up)
|
|
::
|
|
%point
|
|
:- (scot %ux address.to.up)
|
|
?~(from.up ~ `(scot %ux address.u.from.up))
|
|
==
|
|
=. cards
|
|
%+ welp
|
|
^- (list card)
|
|
?- -.i.updates
|
|
%tx
|
|
[%give %fact ~[/txs/[address]] tx+!>(up)]~
|
|
::
|
|
%point
|
|
=/ =cage point+!>(up)
|
|
%+ weld
|
|
[%give %fact ~[/points/[address]] cage]~
|
|
?~ last-owner ~
|
|
[%give %fact ~[/points/[u.last-owner]] cage]~
|
|
==
|
|
cards
|
|
::
|
|
$(updates t.updates)
|
|
--
|
|
::
|
|
=| app-state
|
|
=* state -
|
|
::
|
|
%- agent:dbug
|
|
%+ verb |
|
|
^- agent:gall
|
|
::
|
|
=<
|
|
|_ =bowl:gall
|
|
+* this .
|
|
do ~(. +> bowl)
|
|
def ~(. (default-agent this %|) bowl)
|
|
::
|
|
++ on-init
|
|
^- (quip card _this)
|
|
=: frequency ~h1
|
|
quota 25
|
|
slice ~d7
|
|
resend-time ~m5
|
|
update-rate ~m5
|
|
contract naive:local-contracts:azimuth
|
|
chain-id chain-id:local-contracts:azimuth
|
|
fallback-gas-price 10.000.000.000
|
|
==
|
|
=^ card-1 next-batch (set-roller:timer frequency now.bowl)
|
|
=^ card-2 next-slice (set-quota:timer slice now.bowl)
|
|
:_ this
|
|
:~ card-1
|
|
card-2
|
|
[%pass /azimuth-events %agent [our.bowl %azimuth] %watch /event]
|
|
==
|
|
::
|
|
++ on-save !>(state)
|
|
++ on-load
|
|
|= old=vase
|
|
^- (quip card _this)
|
|
=| cards=(list card)
|
|
|^
|
|
=+ !<(old-state=app-states old)
|
|
=? cards ?=(%0 -.old-state)
|
|
[card:(set-quota:timer slice now.bowl)]~
|
|
=? old-state ?=(%0 -.old-state)
|
|
^- state-1
|
|
=| ship-quota=(map ship @ud)
|
|
=/ [slice=@dr quota=@ud resend-time=@dr update-rate=@dr]
|
|
[~d7 7 ~m5 ~m1]
|
|
=, old-state
|
|
:* %1
|
|
pending ^-((tree [l1-tx-pointer old-send-tx-4]) sending)
|
|
finding history ship-quota next-nonce next-batch
|
|
pre own pk slice quota derive
|
|
frequency endpoint contract chain-id
|
|
resend-time update-rate
|
|
==
|
|
=? old-state ?=(%1 -.old-state)
|
|
^- state-2
|
|
=| spo=(map ship [residents=(set ship) requests=(set ship)])
|
|
=, old-state
|
|
:* %2
|
|
pending sending finding history
|
|
ship-quota next-nonce next-batch
|
|
pre own spo pk slice quota derive
|
|
frequency endpoint contract chain-id
|
|
resend-time update-rate
|
|
==
|
|
=? old-state ?=(%2 -.old-state)
|
|
^- state-3
|
|
=, old-state
|
|
=| allowances=(map ship (unit @ud))
|
|
=/ next-slice=time (mul +((div now.bowl slice)) slice)
|
|
:* %3
|
|
pending sending finding history
|
|
ship-quota allowances
|
|
next-nonce next-batch next-slice
|
|
pre own spo pk slice quota derive
|
|
frequency endpoint contract chain-id
|
|
resend-time update-rate
|
|
==
|
|
=? old-state ?=(%3 -.old-state)
|
|
^- state-4
|
|
=, old-state
|
|
=/ fallback-gas-price=@ud 10.000.000.000
|
|
:* %4
|
|
pending sending finding history
|
|
ship-quota allowances
|
|
next-nonce next-batch next-slice
|
|
pre own spo pk slice quota derive
|
|
frequency endpoint contract chain-id
|
|
resend-time update-rate fallback-gas-price
|
|
==
|
|
=? old-state ?=(%4 -.old-state)
|
|
^- state-5
|
|
=/ new-sending=(tree [l1-tx-pointer old-send-tx-5])
|
|
%+ run:ors:dice sending.old-state
|
|
|= old=old-send-tx-4
|
|
^- old-send-tx-5
|
|
old(txs (turn txs.old (lead |)))
|
|
=, old-state
|
|
:* %5
|
|
pending new-sending finding history
|
|
ship-quota allowances
|
|
next-nonce next-batch next-slice
|
|
pre own spo pk slice quota derive
|
|
frequency endpoint contract chain-id
|
|
resend-time update-rate fallback-gas-price
|
|
==
|
|
=? old-state ?=(%5 -.old-state)
|
|
^- app-state
|
|
=/ new-sending=(tree [l1-tx-pointer send-tx])
|
|
%+ run:ors:dice sending.old-state
|
|
|= old=old-send-tx-5
|
|
^- send-tx
|
|
%= old
|
|
txs
|
|
%+ turn txs.old
|
|
|= [force=? =raw-tx:naive]
|
|
=/ sign-address=(unit @ux)
|
|
(extract-address:lib raw-tx pre chain-id)
|
|
:_ [force raw-tx]
|
|
?. ?=(^ sign-address)
|
|
0x0
|
|
u.sign-address
|
|
==
|
|
=, old-state
|
|
:* %6
|
|
pending new-sending finding history
|
|
ship-quota allowances
|
|
next-nonce next-batch next-slice
|
|
pre own spo pk slice quota derive
|
|
frequency endpoint contract chain-id
|
|
resend-time update-rate fallback-gas-price
|
|
==
|
|
?> ?=(%6 -.old-state)
|
|
[cards this(state old-state)]
|
|
::
|
|
++ app-states
|
|
$% state-0
|
|
state-1
|
|
state-2
|
|
state-3
|
|
state-4
|
|
state-5
|
|
app-state
|
|
==
|
|
::
|
|
++ state-0
|
|
$: %0
|
|
pending=(list pend-tx)
|
|
sending=(tree [l1-tx-pointer old-send-tx-4])
|
|
finding=(map keccak ?(%confirmed %failed [=time l1-tx-pointer]))
|
|
history=(map address:ethereum (tree hist-tx))
|
|
next-nonce=(unit @ud)
|
|
next-batch=time
|
|
pre=^state:naive
|
|
own=owners
|
|
derive=?
|
|
pk=@
|
|
frequency=@dr
|
|
endpoint=(unit @t)
|
|
contract=@ux
|
|
chain-id=@
|
|
==
|
|
::
|
|
++ state-1
|
|
$: %1
|
|
pending=(list pend-tx)
|
|
sending=(tree [l1-tx-pointer old-send-tx-4])
|
|
finding=(map keccak ?(%confirmed %failed [=time l1-tx-pointer]))
|
|
history=(map address:ethereum (tree hist-tx))
|
|
ship-quota=(map ship @ud)
|
|
next-nonce=(unit @ud)
|
|
next-batch=time
|
|
pre=^state:naive
|
|
own=owners
|
|
pk=@
|
|
slice=@dr
|
|
quota=@ud
|
|
derive=?
|
|
frequency=@dr
|
|
endpoint=(unit @t)
|
|
contract=@ux
|
|
chain-id=@
|
|
resend-time=@dr
|
|
update-rate=@dr
|
|
==
|
|
::
|
|
++ state-2
|
|
$: %2
|
|
pending=(list pend-tx)
|
|
sending=(tree [l1-tx-pointer old-send-tx-4])
|
|
finding=(map keccak ?(%confirmed %failed [=time l1-tx-pointer]))
|
|
history=(map address:ethereum (tree hist-tx))
|
|
ship-quota=(map ship @ud)
|
|
next-nonce=(unit @ud)
|
|
next-batch=time
|
|
pre=^state:naive
|
|
own=owners
|
|
spo=sponsors
|
|
pk=@
|
|
slice=@dr
|
|
quota=@ud
|
|
derive=?
|
|
frequency=@dr
|
|
endpoint=(unit @t)
|
|
contract=@ux
|
|
chain-id=@
|
|
resend-time=@dr
|
|
update-rate=@dr
|
|
==
|
|
::
|
|
++ state-3
|
|
$: %3
|
|
pending=(list pend-tx)
|
|
sending=(tree [l1-tx-pointer old-send-tx-4])
|
|
finding=(map keccak ?(%confirmed %failed [=time l1-tx-pointer]))
|
|
history=(map address:ethereum (tree hist-tx))
|
|
ship-quota=(map ship @ud)
|
|
allowances=(map ship (unit @ud))
|
|
next-nonce=(unit @ud)
|
|
next-batch=time
|
|
next-slice=time
|
|
pre=^state:naive
|
|
own=owners
|
|
spo=sponsors
|
|
pk=@
|
|
slice=@dr
|
|
quota=@ud
|
|
derive=?
|
|
frequency=@dr
|
|
endpoint=(unit @t)
|
|
contract=@ux
|
|
chain-id=@
|
|
resend-time=@dr
|
|
update-rate=@dr
|
|
==
|
|
::
|
|
+$ old-send-tx-4 [next-gas-price=@ud sent=? txs=(list =raw-tx:naive)]
|
|
::
|
|
++ state-4
|
|
$: %4
|
|
pending=(list pend-tx)
|
|
sending=(tree [l1-tx-pointer old-send-tx-4])
|
|
finding=(map keccak ?(%confirmed %failed [=time l1-tx-pointer]))
|
|
history=(map address:ethereum (tree hist-tx))
|
|
ship-quota=(map ship @ud)
|
|
allowances=(map ship (unit @ud))
|
|
next-nonce=(unit @ud)
|
|
next-batch=time
|
|
next-slice=time
|
|
pre=^state:naive
|
|
own=owners
|
|
spo=sponsors
|
|
pk=@
|
|
slice=@dr
|
|
quota=@ud
|
|
derive=?
|
|
frequency=@dr
|
|
endpoint=(unit @t)
|
|
contract=@ux
|
|
chain-id=@
|
|
resend-time=@dr
|
|
update-rate=@dr
|
|
fallback-gas-price=@ud
|
|
==
|
|
::
|
|
+$ old-send-tx-5
|
|
[next-gas-price=@ud sent=? txs=(list [force=? =raw-tx:naive])]
|
|
::
|
|
++ state-5
|
|
$: %5
|
|
pending=(list pend-tx)
|
|
sending=(tree [l1-tx-pointer old-send-tx-5])
|
|
finding=(map keccak ?(%confirmed %failed [=time l1-tx-pointer]))
|
|
history=(map address:ethereum (tree hist-tx))
|
|
ship-quota=(map ship @ud)
|
|
allowances=(map ship (unit @ud))
|
|
next-nonce=(unit @ud)
|
|
next-batch=time
|
|
next-slice=time
|
|
pre=^state:naive
|
|
own=owners
|
|
spo=sponsors
|
|
pk=@
|
|
slice=@dr
|
|
quota=@ud
|
|
derive=?
|
|
frequency=@dr
|
|
endpoint=(unit @t)
|
|
contract=@ux
|
|
chain-id=@
|
|
resend-time=@dr
|
|
update-rate=@dr
|
|
fallback-gas-price=@ud
|
|
==
|
|
--
|
|
::
|
|
++ on-poke
|
|
|= [=mark =vase]
|
|
^- (quip card _this)
|
|
=^ cards state
|
|
?+ mark (on-poke:def mark vase)
|
|
%roller-action
|
|
=+ !<(poke=action vase)
|
|
(on-action:do poke)
|
|
==
|
|
[cards this]
|
|
:: +on-peek: scry paths
|
|
::
|
|
:: /x/pending -> %noun (list pend-tx)
|
|
:: /x/pending/[~ship] -> %noun (list pend-tx)
|
|
:: /x/pending/[0xadd.ress] -> %noun (list pend-tx)
|
|
:: /x/tx/[0xke.ccak]/status -> %noun tx-status
|
|
:: /x/history/[0xadd.ress] -> %noun (list hist-tx)
|
|
:: /x/nonce/[~ship]/[proxy] -> %noun (unit @)
|
|
:: /x/spawned/[~star] -> %noun (list ship)
|
|
:: /x/unspawned/[~star] -> %noun (list ship)
|
|
:: /x/sponsored/[~point] -> %noun [(list ship) (list ship)]
|
|
:: /x/next-batch -> %atom time
|
|
:: /x/next-slice -> %atom time
|
|
:: /x/point/[~ship] -> %noun point:naive
|
|
:: /x/ships/[0xadd.ress] -> %noun (list ship)
|
|
:: /x/config -> %noun config
|
|
:: /x/chain-id -> %atom @
|
|
:: /x/owned/[0xadd.ress] -> %noun (list ship)
|
|
:: /x/transfers/[0xadd.ress] -> %noun (list ship)
|
|
:: /x/manager/[0xadd.ress] -> %noun (list ship)
|
|
:: /x/voting/[0xadd.ress] -> %noun (list ship)
|
|
:: /x/spawning/[0xadd.ress] -> %noun (list ship)
|
|
:: /x/predicted -> %noun state:naive
|
|
:: /x/quota -> %atom @ud
|
|
:: /x/slice -> %atom @dr
|
|
:: /x/over-quota/[~ship] -> %atom ?
|
|
:: /x/ship-quota/[~ship] -> %atom @ud
|
|
:: /x/allowances -> %noun (map @p (unit @ud))
|
|
:: /x/allowance/[~ship] -> %noun (unit @ud)
|
|
:: /x/ready -> %atom ?
|
|
::
|
|
++ on-peek
|
|
|= =path
|
|
^- (unit (unit cage))
|
|
?+ path ~
|
|
[%x %pending ~] ``noun+!>(pending)
|
|
[%x %pending @ ~] (pending-by:on-peek:do i.t.t.path)
|
|
[%x %tx @ %status ~] (status:on-peek:do i.t.t.path)
|
|
[%x %pending-tx @ ~] (transaction:on-peek:do i.t.t.path)
|
|
[%x %history @ ~] (history:on-peek:do i.t.t.path)
|
|
[%x %nonce @ @ ~] (nonce:on-peek:do i.t.t.path i.t.t.t.path)
|
|
[%x %spawned @ ~] (spawned:on-peek:do i.t.t.path)
|
|
[%x %unspawned @ ~] (unspawned:on-peek:do i.t.t.path)
|
|
[%x %sponsored @ ~] (sponsored:on-peek:do i.t.t.path)
|
|
[%x %next-batch ~] ``atom+!>(next-batch)
|
|
[%x %next-slice ~] ``atom+!>(next-slice)
|
|
[%x %point @ ~] (point:on-peek:do i.t.t.path)
|
|
[%x %ships @ ~] (ships:on-peek:do i.t.t.path)
|
|
[%x %config ~] config:on-peek:do
|
|
[%x %chain-id ~] ``atom+!>(chain-id)
|
|
[%x %owned @ ~] (points-proxy:on-peek:do %own i.t.t.path)
|
|
[%x %transfers @ ~] (points-proxy:on-peek:do %transfer i.t.t.path)
|
|
[%x %manager @ ~] (points-proxy:on-peek:do %manage i.t.t.path)
|
|
[%x %voting @ ~] (points-proxy:on-peek:do %vote i.t.t.path)
|
|
[%x %spawning @ ~] (points-proxy:on-peek:do %spawn i.t.t.path)
|
|
[%x %predicted ~] ``noun+!>(pre)
|
|
[%x %quota ~] ``atom+!>(quota)
|
|
[%x %slice ~] ``atom+!>(slice)
|
|
[%x %over-quota @ ~] (over-quota:on-peek:do i.t.t.path)
|
|
[%x %ship-quota @ ~] (ship-quota:on-peek:do i.t.t.path)
|
|
[%x %allowances ~] ``noun+!>(allowances)
|
|
[%x %allowance @ ~] (allowance:on-peek:do i.t.t.path)
|
|
[%x %ready ~] ``atom+!>(?=(^ points.pre))
|
|
==
|
|
::
|
|
++ on-arvo
|
|
|= [=wire =sign-arvo]
|
|
^- (quip card _this)
|
|
?+ wire (on-arvo:def wire sign-arvo)
|
|
[%timer ~]
|
|
?+ +<.sign-arvo (on-arvo:def wire sign-arvo)
|
|
%wake =^(cards state (on-timer:do &) [cards this])
|
|
==
|
|
[%quota-timer ~]
|
|
?+ +<.sign-arvo (on-arvo:def wire sign-arvo)
|
|
%wake =^(cards state on-quota-timer:do [cards this])
|
|
==
|
|
::
|
|
[%predict ~]
|
|
?+ +<.sign-arvo (on-arvo:def wire sign-arvo)
|
|
%wake
|
|
=^ effects state
|
|
(predicted-state canonical):do
|
|
[(emit effects) this(derive &)]
|
|
==
|
|
::
|
|
[%resend @ @ ~]
|
|
=/ [address=@ux nonce=@ud]
|
|
[(slav %ux i.t.wire) (slav %ud i.t.t.wire)]
|
|
?+ +<.sign-arvo (on-arvo:def wire sign-arvo)
|
|
%wake
|
|
=/ cards=(list card) (send-roll:do address nonce)
|
|
=? sending
|
|
?& ?=(~ cards)
|
|
(has:ors:dice sending [address nonce])
|
|
=(0 (lent txs:(got:ors:dice sending [address nonce])))
|
|
==
|
|
~& > "empty sending, removing {<[nonce address]>}"
|
|
=^ * sending
|
|
(del:ors:dice sending [address nonce])
|
|
sending
|
|
[cards this]
|
|
==
|
|
==
|
|
::
|
|
++ on-fail
|
|
|= [=term =tang]
|
|
::TODO if crashed during timer, set new timer? how to detect?
|
|
(on-fail:def term tang)
|
|
::
|
|
++ on-watch
|
|
|= =path
|
|
^- (quip card _this)
|
|
:_ this
|
|
|^
|
|
?+ path (on-watch:def path)
|
|
[%txs @ ~] ?>(?=(^ (slaw %ux i.t.path)) ~)
|
|
[%points @ ~] ?>(?=(^ (slaw %ux i.t.path)) ~)
|
|
[%connect @ ~] [%give %fact ~ (init i.t.path)]~
|
|
==
|
|
::
|
|
++ init
|
|
|= wat=@t
|
|
^- cage
|
|
:- %roller-data
|
|
!> ^- roller-data
|
|
?~ addr=(slaw %ux wat) !!
|
|
=/ [=owners =sponsors =points:naive]
|
|
(give-points u.addr)
|
|
=/ txs=(tree hist-tx) (give-history u.addr)
|
|
[chain-id points txs owners sponsors]
|
|
::
|
|
++ give-points
|
|
|= =address:ethereum
|
|
^- [owners sponsors points:naive]
|
|
=/ controlled=(list [proxy:naive ship])
|
|
(controlled-ships:dice address own)
|
|
%+ roll controlled
|
|
|= [[=proxy:naive =ship] =owners =sponsors =points:naive]
|
|
=/ sponsoring (~(get by spo.state) ship)
|
|
:+ (~(put ju owners) [proxy address] ship)
|
|
::
|
|
?~ sponsoring sponsors
|
|
(~(put by sponsors) ship u.sponsoring)
|
|
::
|
|
%+ put:orp:dice points
|
|
[ship (need (get:orp:dice points.pre ship))]
|
|
::
|
|
++ give-history
|
|
|= =address:ethereum
|
|
^- (tree hist-tx)
|
|
?~ hist=(~(get by history) address)
|
|
~
|
|
u.hist
|
|
--
|
|
::
|
|
++ on-leave on-leave:def
|
|
::
|
|
++ on-agent
|
|
|= [=wire =sign:agent:gall]
|
|
^- (quip card _this)
|
|
|^
|
|
?+ wire (on-agent:def wire sign)
|
|
[%send @ @ *] (send-batch i.t.wire i.t.t.wire sign)
|
|
[%azimuth-events ~] (azimuth-event sign)
|
|
[%nonce ~] (nonce sign)
|
|
[%refresh-nonce @ ~] (refresh i.t.wire sign)
|
|
==
|
|
::
|
|
++ send-batch
|
|
|= [address=@t nonce=@t =sign:agent:gall]
|
|
^- (quip card _this)
|
|
=/ [address=@ux nonce=@ud]
|
|
[(slav %ux address) (slav %ud nonce)]
|
|
?- -.sign
|
|
%poke-ack
|
|
?~ p.sign
|
|
%- (slog leaf+"Send batch thread started successfully" ~)
|
|
[~ this]
|
|
%- (slog leaf+"{(trip dap.bowl)} couldn't start thread" u.p.sign)
|
|
:_ this
|
|
[(leave:spider:do our.bowl wire)]~
|
|
::
|
|
%watch-ack
|
|
?~ p.sign
|
|
[~ this]
|
|
=/ =tank leaf+"{(trip dap.bowl)} couldn't start listen to thread"
|
|
%- (slog tank u.p.sign)
|
|
[~ this]
|
|
::
|
|
%kick
|
|
[~ this]
|
|
::
|
|
%fact
|
|
?+ p.cage.sign (on-agent:def wire sign)
|
|
%thread-fail
|
|
=+ !<([=term =tang] q.cage.sign)
|
|
%- (slog leaf+"{(trip dap.bowl)} failed" leaf+<term> tang)
|
|
=^ cards state
|
|
(on-batch-result:do address nonce %.n^[%crash 'thread failed'])
|
|
[cards this]
|
|
::
|
|
%thread-done
|
|
=+ !<(result=(each [@ud @ud] [term @t]) q.cage.sign)
|
|
=^ cards state
|
|
(on-batch-result:do address nonce result)
|
|
[cards this]
|
|
==
|
|
==
|
|
::
|
|
++ azimuth-event
|
|
|= =sign:agent:gall
|
|
^- (quip card _this)
|
|
?+ -.sign [~ this]
|
|
%watch-ack
|
|
?~ p.sign [~ this]
|
|
=/ =tank leaf+"{(trip dap.bowl)} couldn't start listen to %azimuth"
|
|
%- (slog tank u.p.sign)
|
|
[~ this]
|
|
::
|
|
%fact
|
|
?+ p.cage.sign (on-agent:def wire sign)
|
|
%naive-diffs
|
|
=+ !<(=diff:naive q.cage.sign)
|
|
=^ cards state
|
|
(on-naive-diff:do diff)
|
|
[cards this]
|
|
::
|
|
%naive-state
|
|
~& > %received-azimuth-state
|
|
=+ !<([nas=^state:naive =indices] q.cage.sign)
|
|
=^ effects state
|
|
(predicted-state:do nas indices)
|
|
[(emit effects) this]
|
|
==
|
|
==
|
|
::
|
|
++ nonce
|
|
|= =sign:agent:gall
|
|
^- (quip card _this)
|
|
?- -.sign
|
|
%poke-ack
|
|
?~ p.sign
|
|
%- (slog leaf+"Nonce thread started successfully" ~)
|
|
[~ this]
|
|
%- (slog leaf+"{(trip dap.bowl)} couldn't start thread" u.p.sign)
|
|
:_ this
|
|
[(leave:spider:do our.bowl wire)]~
|
|
::
|
|
%watch-ack
|
|
?~ p.sign
|
|
[~ this]
|
|
=/ =tank leaf+"{(trip dap.bowl)} couldn't start listen to thread"
|
|
%- (slog tank u.p.sign)
|
|
[~ this]
|
|
::
|
|
%kick
|
|
[~ this]
|
|
::
|
|
%fact
|
|
?+ p.cage.sign (on-agent:def wire sign)
|
|
%thread-fail
|
|
=+ !<([=term =tang] q.cage.sign)
|
|
%- (slog leaf+"{(trip dap.bowl)} failed" leaf+<term> tang)
|
|
[~ this]
|
|
::
|
|
%thread-done
|
|
=+ !<(nonce=@ud q.cage.sign)
|
|
[~ this(next-nonce `nonce)]
|
|
==
|
|
==
|
|
::
|
|
++ refresh
|
|
|= [nonce=@t =sign:agent:gall]
|
|
^- (quip card _this)
|
|
=/ failed-nonce=@ud (slav %ud nonce)
|
|
?- -.sign
|
|
%poke-ack
|
|
?~ p.sign
|
|
%- (slog leaf+"Refresh Nonce thread started successfully" ~)
|
|
[~ this]
|
|
%- (slog leaf+"{(trip dap.bowl)} couldn't start thread" u.p.sign)
|
|
:_ this
|
|
[(leave:spider:do our.bowl wire)]~
|
|
::
|
|
%watch-ack
|
|
?~ p.sign
|
|
[~ this]
|
|
=/ =tank leaf+"{(trip dap.bowl)} couldn't start listen to thread"
|
|
%- (slog tank u.p.sign)
|
|
[~ this]
|
|
::
|
|
%kick
|
|
[~ this]
|
|
::
|
|
%fact
|
|
?+ p.cage.sign (on-agent:def wire sign)
|
|
%thread-fail
|
|
=+ !<([=term =tang] q.cage.sign)
|
|
%- (slog leaf+"{(trip dap.bowl)} failed" leaf+<term> tang)
|
|
[~ this]
|
|
::
|
|
%thread-done
|
|
=+ !<(nonce=@ud q.cage.sign)
|
|
=^ cards state
|
|
(on-out-of-sync:do nonce failed-nonce)
|
|
[cards this]
|
|
==
|
|
==
|
|
--
|
|
--
|
|
::
|
|
|_ =bowl:gall
|
|
:: +canonical: current naive, ownership, and sponsorship state
|
|
::
|
|
++ canonical
|
|
|^ nas^own^spo
|
|
::
|
|
++ nas
|
|
.^ ^state:naive
|
|
%gx
|
|
(scot %p our.bowl)
|
|
%azimuth
|
|
(scot %da now.bowl)
|
|
/nas/noun
|
|
==
|
|
::
|
|
++ own
|
|
.^ owners
|
|
%gx
|
|
(scot %p our.bowl)
|
|
%azimuth
|
|
(scot %da now.bowl)
|
|
/own/noun
|
|
==
|
|
::
|
|
++ spo
|
|
.^ sponsors
|
|
%gx
|
|
(scot %p our.bowl)
|
|
%azimuth
|
|
(scot %da now.bowl)
|
|
/spo/noun
|
|
==
|
|
--
|
|
:: +predicted-state
|
|
::
|
|
:: derives predicted state from applying pending & sending txs to
|
|
:: the provided naive state, discarding invalid txs in the process
|
|
::
|
|
++ predicted-state
|
|
|= [nas=^state:naive =indices]
|
|
^- (quip update _state)
|
|
=: pre nas
|
|
own own.indices
|
|
spo spo.indices
|
|
==
|
|
|^
|
|
=^ [nes=_sending updates-1=(list update)] state
|
|
apply-sending
|
|
=^ [nep=_pending updates-2=(list update)] state
|
|
apply-pending
|
|
:- (welp updates-1 updates-2)
|
|
state(sending nes, pending nep)
|
|
::
|
|
++ apply-pending
|
|
(apply-txs pending %pending next-nonce.state)
|
|
::
|
|
++ apply-sending
|
|
=| ups=(list update)
|
|
=/ valid=_sending ~
|
|
=+ sorted=(tap:ors:dice sending)
|
|
|- ^+ [[valid ups] state]
|
|
?~ sorted [[valid ups] state]
|
|
::
|
|
=* key key.i.sorted
|
|
=* val val.i.sorted
|
|
=/ txs=(list pend-tx)
|
|
%+ turn txs.val
|
|
|=([addr=@ux force=? =raw-tx:naive] force^addr^*time^raw-tx)
|
|
=^ [new-valid=_txs nups=_ups] state
|
|
(apply-txs txs %sending `nonce.key)
|
|
=/ new-sending
|
|
(turn new-valid |=([force=? addr=@ux * =raw-tx:naive] addr^force^raw-tx))
|
|
:: we only hear updates for this nonce if it has been sent
|
|
::
|
|
=. valid ::=? valid sent.val
|
|
(put:ors:dice valid key val(txs new-sending))
|
|
$(sorted t.sorted, ups (welp ups nups))
|
|
::
|
|
++ apply-txs
|
|
|= [txs=(list pend-tx) type=?(%pending %sending) nonce=(unit @ud)]
|
|
=/ valid=_txs ~
|
|
=| updates=(list update)
|
|
|- ^+ [[valid updates] state]
|
|
?~ txs
|
|
:_ state
|
|
[(flop valid) (flop updates)]
|
|
::
|
|
=* tx i.txs
|
|
=* raw-tx raw-tx.i.txs
|
|
=* ship ship.from.tx.raw-tx.i.txs
|
|
=/ =keccak (hash-raw-tx:lib raw-tx)
|
|
=^ [gud=? up-1=_updates] state
|
|
(try-apply pre force.tx raw-tx)
|
|
=/ =roll-tx [ship type keccak (l2-tx +<.tx.raw-tx)]
|
|
=? valid gud [tx valid]
|
|
=^ up-2 history
|
|
?: gud [~ history]
|
|
=. time.tx
|
|
?: ?=(%pending type) time.tx
|
|
?~ wer=(~(get by finding) keccak)
|
|
~& >>> "missing %sending tx in finding"^[ship raw-tx]
|
|
now.bowl
|
|
?@ u.wer
|
|
~& >>> "weird tx in finding gud: {<gud>} {<u.wer>}"^[ship raw-tx]
|
|
now.bowl
|
|
time.u.wer
|
|
~? =(0x0 address.tx) %weird-null-tx-address^'apply-txs'
|
|
(update-history:dice history [tx]~ %failed)
|
|
=? finding !gud (~(put by finding) keccak %failed)
|
|
=. updates :(welp up-2 up-1 updates)
|
|
$(txs t.txs)
|
|
--
|
|
:: +try-apply: maybe apply the given l2 tx to the naive state
|
|
::
|
|
++ try-apply
|
|
|= [nas=^state:naive force=? =raw-tx:naive]
|
|
^- [[? ups=(list update)] _state]
|
|
=/ [success=? updates=(list update) predicted=_nas =indices]
|
|
(apply-raw-tx:dice force chain-id raw-tx nas own spo)
|
|
=: pre predicted
|
|
own own.indices
|
|
spo spo.indices
|
|
==
|
|
[[success updates] state]
|
|
::
|
|
++ on-action
|
|
|= =action
|
|
^- (quip card _state)
|
|
=+ local=(team:title our.bowl src.bowl)
|
|
|^
|
|
?- -.action
|
|
%commit ?>(local (on-timer |))
|
|
%config ?>(local (on-config +.action))
|
|
%assign ?>(local `state(allowances (~(put by allowances) +.action)))
|
|
%refuel ?>(local (refuel-tx +.action))
|
|
%cancel (cancel-tx +.action)
|
|
::
|
|
%submit
|
|
:: TODO: return [~ state] instead of crashing
|
|
:: if naive state hasn't being retrieved yet?
|
|
::
|
|
?> ?=(^ points.pre)
|
|
%- take-tx
|
|
:* force.action
|
|
address.action
|
|
now.bowl
|
|
sig.action
|
|
(part-tx-to-full tx.action)
|
|
==
|
|
==
|
|
::
|
|
++ part-tx-to-full
|
|
|= =part-tx
|
|
^- [octs tx:naive]
|
|
?- -.part-tx
|
|
%raw
|
|
?~ batch=(parse-raw-tx:naive 0 q.raw.part-tx)
|
|
~? lverb [dap.bowl %parse-failed]
|
|
:: TODO: maybe return a unit if parsing fails?
|
|
::
|
|
!!
|
|
[raw tx]:-.u.batch
|
|
::
|
|
%don [(gen-tx-octs:lib +.part-tx) +.part-tx]
|
|
%ful +.part-tx
|
|
==
|
|
--
|
|
::
|
|
++ on-config
|
|
|= =config
|
|
^- (quip card _state)
|
|
?- -.config
|
|
%frequency [~ state(frequency frequency.config)]
|
|
%fallback [~ state(fallback-gas-price gas.config)]
|
|
%resend-time [~ state(resend-time time.config)]
|
|
%update-rate [~ state(update-rate rate.config)]
|
|
%slice [~ state(slice slice.config)]
|
|
%quota [~ state(quota quota.config)]
|
|
::
|
|
%endpoint
|
|
:- ~
|
|
=/ [contract=@ux chain-id=@]
|
|
=< [naive chain-id]
|
|
=, azimuth
|
|
?+ net.config !!
|
|
%mainnet mainnet-contracts
|
|
%goerli goerli-contracts
|
|
%local local-contracts
|
|
%default contracts
|
|
==
|
|
%_ state
|
|
contract contract
|
|
chain-id chain-id
|
|
endpoint `endpoint.config
|
|
==
|
|
::
|
|
%setkey
|
|
=? pk.config =((end [3 2] pk.config) '0x')
|
|
(rsh [3 2] pk.config)
|
|
?~ pk=(de:base16:mimes:html pk.config)
|
|
`state
|
|
[(get-nonce q.u.pk /nonce) state(pk q.u.pk)]
|
|
==
|
|
:: +cancel-tx: cancel a pending transaction
|
|
::
|
|
++ cancel-tx
|
|
|= [sig=@ =keccak =l2-tx =ship]
|
|
^- (quip card _state)
|
|
?^ status=(~(get by finding) keccak)
|
|
~? lverb [dap.bowl %tx-not-pending status+u.status]
|
|
[~ state]
|
|
:: "cancel: 0x1234abcd"
|
|
::
|
|
=/ message=octs
|
|
%: cad:naive 3
|
|
8^'cancel: '
|
|
::
|
|
=; hash=@t
|
|
(met 3 hash)^hash
|
|
(crip "0x{((x-co:co 65) keccak)}")
|
|
::
|
|
~
|
|
==
|
|
?~ addr=(verify-sig:lib sig message)
|
|
~? lverb [dap.bowl %cancel-sig-fail]
|
|
[~ state]
|
|
=^ time pending
|
|
=| nep=(list pend-tx)
|
|
|- ^- [(unit time) _nep]
|
|
?~ pending [~ (flop nep)]
|
|
?: =(keccak (hash-raw-tx:lib raw-tx.i.pending))
|
|
[`time.i.pending (weld (flop nep) t.pending)]
|
|
$(pending t.pending, nep [i.pending nep])
|
|
?~ time
|
|
~? lverb [dap.bowl %weird-tx-not-pending]
|
|
[~ state]
|
|
:- ~
|
|
%_ state
|
|
history
|
|
=+ txs=(~(got by history) u.addr)
|
|
=. txs +:(del:orh:dice txs u.time)
|
|
%+ ~(put by history) u.addr
|
|
%^ put:orh:dice txs
|
|
u.time
|
|
[ship %cancelled keccak l2-tx]
|
|
==
|
|
:: +refuel-tx: bumps the gas price for a sending tx
|
|
::
|
|
++ refuel-tx
|
|
|= [nonce=@ud address=(unit address:ethereum) gas=@ud]
|
|
^- (quip card _state)
|
|
=/ batch=[address:ethereum @ud]
|
|
:_ nonce
|
|
?^(address u.address (get-address pk.state))
|
|
=. sending
|
|
?~ send-tx=(get:ors:dice sending batch)
|
|
sending
|
|
%^ put:ors:dice sending
|
|
batch
|
|
u.send-tx(next-gas-price gas)
|
|
`state
|
|
:: +take-tx: accept submitted l2 tx into the :pending list
|
|
::
|
|
++ take-tx
|
|
|= =pend-tx
|
|
^- (quip card _state)
|
|
=* ship ship.from.tx.raw-tx.pend-tx
|
|
=/ [exceeded=? next-quota=@] (quota-exceeded ship)
|
|
?: exceeded [~ state]
|
|
=/ sign-address=(unit @ux)
|
|
(extract-address:lib raw-tx.pend-tx pre chain-id)
|
|
=? address.pend-tx ?=(^ sign-address)
|
|
(need sign-address)
|
|
=^ [gud=? cards-1=(list update)] state
|
|
(try-apply pre [force raw-tx]:pend-tx)
|
|
=^ cards-2 history
|
|
(update-history:dice history [pend-tx]~ ?:(gud %pending %failed))
|
|
?. gud
|
|
:_ state
|
|
:: %point and (%failed) %tx updates
|
|
::
|
|
(emit cards-1)
|
|
=: pending (snoc pending pend-tx)
|
|
ship-quota (~(put by ship-quota) ship next-quota)
|
|
==
|
|
:: toggle derivation
|
|
::
|
|
:_ state(derive ?:(derive | derive))
|
|
;: welp
|
|
(emit cards-1) :: %point updates
|
|
(emit cards-2) :: %tx updates
|
|
::
|
|
?. derive ~
|
|
:: defer updating predicted state from canonical
|
|
::
|
|
[(wait:b:sys /predict (add update-rate now.bowl))]~
|
|
==
|
|
:: +on-timer: every :frequency, freeze :pending txs roll and start sending it
|
|
::
|
|
++ on-timer
|
|
|= new=?
|
|
^- (quip card _state)
|
|
=^ updates-1 state
|
|
(predicted-state canonical)
|
|
=^ cards state
|
|
?: =(~ pending) [~ state]
|
|
?~ next-nonce
|
|
~? lverb [dap.bowl %missing-roller-nonce] [~ state]
|
|
:: this guarantees that next-nonce is only incremented
|
|
:: when the thread that's sending the previous batch
|
|
:: has come back and confirms that it was sent to L1
|
|
::
|
|
?: pending-batch
|
|
:: this would postpone sending the batch for a whole "frequency"
|
|
:: TODO: set up a timer to retry this in ~mX ?
|
|
::
|
|
~? lverb [dap.bowl %nonce-out-sync] [~ state]
|
|
=/ nonce=@ud u.next-nonce
|
|
=^ updates-2 history (update-history:dice history pending %sending)
|
|
=/ =address:ethereum (get-address pk)
|
|
=: pending ~
|
|
derive &
|
|
next-nonce `+(u.next-nonce)
|
|
::
|
|
sending
|
|
%^ put:ors:dice sending
|
|
[address nonce]
|
|
:+ 0 |
|
|
(turn pending |=([force=? addr=@ux * =raw-tx:naive] addr^force^raw-tx))
|
|
::
|
|
finding
|
|
%- ~(gas by finding)
|
|
%+ turn pending
|
|
|= pend-tx
|
|
(hash-raw-tx:lib raw-tx)^[time address nonce]
|
|
==
|
|
:_ state
|
|
;: welp
|
|
(emit updates-1)
|
|
(emit updates-2)
|
|
(send-roll address nonce)
|
|
==
|
|
?. new cards^state
|
|
=^ card next-batch
|
|
(set-roller:timer frequency now.bowl)
|
|
[card cards]^state
|
|
:: +on-quota-timer: resets tx quota for all ships
|
|
::
|
|
++ on-quota-timer
|
|
^- (quip card _state)
|
|
=^ card next-slice (set-quota:timer slice now.bowl)
|
|
:- [card]~
|
|
state(ship-quota *(map ship @ud))
|
|
:: +get-nonce: retrieves the latest nonce
|
|
::
|
|
++ get-nonce
|
|
|= [pk=@ =wire]
|
|
^- (list card)
|
|
?~ endpoint ~?(lverb [dap.bowl %no-endpoint] ~)
|
|
(start-thread:spider bowl wire [%roller-nonce !>([u.endpoint pk])])
|
|
::
|
|
++ quota-exceeded
|
|
|= =ship
|
|
^- [exceeded=? next-quota=@ud]
|
|
=/ quota=(unit @ud) (~(get by ship-quota) ship)
|
|
=/ allow=(unit (unit @ud)) (~(get by allowances) ship)
|
|
?~ quota
|
|
:_ 1
|
|
?~ allow |
|
|
?~(u.allow | =(u.u.allow 0))
|
|
:_ +(u.quota)
|
|
?~ allow
|
|
(gte u.quota quota.state)
|
|
:: ship has been whitelisted ("?~ u.allow" means no quota restrictions)
|
|
::
|
|
?~(u.allow | (gte u.quota u.u.allow))
|
|
:: +pending-batch: checks if the previous nonce has been sent
|
|
::
|
|
:: If %.y, the roller has been trying to send a batch for a whole frequency.
|
|
::
|
|
:: The cause of not sending the previous batch can happen because
|
|
:: of thread failure or because the private key loaded onto
|
|
:: the roller was used for something other than signing L2 batches right
|
|
:: after the send-batch thread started.
|
|
::
|
|
:: After reaching this state, any subsequents attempts have failed
|
|
:: (prior to updating the sending nonce if we hit the on-out-of-sync case)
|
|
:: which would possibly require a manual intervention (e.g. changing the
|
|
:: ethereum node URL, adding funds to the roller's address, manually bumping
|
|
:: the fall-back-gas-price or refueling the current batch with higher gas)
|
|
::
|
|
++ pending-batch
|
|
^- ?
|
|
?~ newest-batch=(ram:ors:dice sending) |
|
|
!=(sent.val.u.newest-batch &)
|
|
:: +on-out-of-sync: handles a mismatch between current and expected l1 nonce
|
|
::
|
|
++ on-out-of-sync
|
|
|= [nonce=@ud failed-nonce=@ud]
|
|
^- (quip card _state)
|
|
~& > %begin-on-out-of-sync
|
|
=/ =address:ethereum (get-address pk)
|
|
:: we only consider nonces >= than the one that failed
|
|
::
|
|
=/ failed-sending=(list [l1-tx-pointer send-tx])
|
|
%- tap:ors:dice
|
|
:: (range exclusive)
|
|
::
|
|
(lot:ors:dice sending [`[address (dec failed-nonce)] ~])
|
|
=/ confirmed-sending=_sending
|
|
(lot:ors:dice sending [~ `[address failed-nonce]])
|
|
=/ [nes=_sending nif=_finding sih=_history]
|
|
%- tail
|
|
%+ roll failed-sending
|
|
|= $: [p=l1-tx-pointer q=send-tx]
|
|
new-nonce=_nonce
|
|
sending=_confirmed-sending
|
|
finding=_finding
|
|
history=_history
|
|
==
|
|
|^
|
|
=* nonce nonce.p
|
|
=* txs txs.q
|
|
:: TODO: this shouldn't be needed
|
|
?: (lth nonce.p failed-nonce)
|
|
~& >>> [%on-out-of-sync nonce+nonce.p failed+failed-nonce]
|
|
[new-nonce sending finding history]
|
|
:+ +(new-nonce)
|
|
fix-sending
|
|
process-l2-txs
|
|
::
|
|
++ fix-sending
|
|
(put:ors:dice sending [p(nonce new-nonce) q(sent %.n)])
|
|
::
|
|
++ process-l2-txs
|
|
%+ roll txs.q
|
|
|= [[@ @ =raw-tx:naive] nif=_finding sih=_history]
|
|
=/ =keccak (hash-raw-tx:lib raw-tx)
|
|
|^
|
|
?~ val=(~(get by nif) keccak)
|
|
[nif sih]
|
|
?. ?=(^ u.val) [nif sih]
|
|
:- (fix-finding u.val)
|
|
(fix-history time.u.val address.u.val)
|
|
::
|
|
++ fix-finding
|
|
|= val=[time l1-tx-pointer]
|
|
^+ nif
|
|
(~(put by nif) keccak val(nonce.+ new-nonce))
|
|
::
|
|
++ fix-history
|
|
|= [=time =address:ethereum]
|
|
^+ sih
|
|
=* ship ship.from.tx.raw-tx
|
|
=/ l2-tx (l2-tx +<.tx.raw-tx)
|
|
=/ =roll-tx [ship %sending keccak l2-tx]
|
|
=+ txs=(~(got by sih) address)
|
|
=. txs +:(del:orh:dice txs time)
|
|
%+ ~(put by sih) address
|
|
(put:orh:dice txs [time roll-tx])
|
|
--
|
|
--
|
|
=: sending nes
|
|
finding nif
|
|
history sih
|
|
next-nonce `+(nonce)
|
|
==
|
|
~& > %end-on-out-of-sync
|
|
[(send-roll address nonce) state]
|
|
:: +send-roll: start thread to submit roll from :sending to l1
|
|
::
|
|
++ send-roll
|
|
|= [=address:ethereum =nonce:naive]
|
|
^- (list card)
|
|
?~ endpoint
|
|
~? lverb [dap.bowl %no-endpoint]
|
|
~
|
|
:: if this nonce isn't in the sending queue anymore, it's done
|
|
::
|
|
?. (has:ors:dice sending [address nonce])
|
|
~? lverb [dap.bowl %done-sending [address nonce]]
|
|
~
|
|
:: if there are no txs for this nonce, don't send it
|
|
::
|
|
?: =(0 (lent txs:(got:ors:dice sending [address nonce])))
|
|
~& >>> [dap.bowl %empty-nonce]
|
|
~
|
|
:: start the thread, passing in the l2 txs to use
|
|
:: TODO should go ahead and set resend timer in case thread hangs, or nah?
|
|
::
|
|
%^ start-thread:spider
|
|
bowl
|
|
/send/(scot %ux address)/(scot %ud nonce)
|
|
:- %roller-send
|
|
!> ^- rpc-send-roll
|
|
:* u.endpoint
|
|
contract
|
|
chain-id
|
|
pk
|
|
nonce
|
|
fallback-gas-price
|
|
::
|
|
=< [next-gas-price (turn txs (cork tail tail))]
|
|
[. (got:ors:dice sending [address nonce])]
|
|
==
|
|
:: +on-batch-result: await resend after thread success or failure
|
|
::
|
|
++ on-batch-result
|
|
|= [=address:ethereum nonce=@ud result=(each [@ud @ud] [term @t])]
|
|
^- (quip card _state)
|
|
|^
|
|
:: print error if there was one
|
|
::
|
|
~? ?=(%| -.result) [dap.bowl %send-error nonce+nonce +.p.result]
|
|
:: if this nonce was removed from the queue by a
|
|
:: previous resend-with-higher-gas thread, it's done
|
|
::
|
|
?. (has:ors:dice sending [address nonce])
|
|
~? lverb [dap.bowl %done-sending [address nonce]]
|
|
`state
|
|
?: ?=([%| %not-sent %batch-parse-error] result)
|
|
:: if we tried to send a malformed batch, remove it from the queue
|
|
::
|
|
~& >>> [dap.bowl %removing-malformed-batch]
|
|
=^ * sending
|
|
(del:ors:dice sending [address nonce])
|
|
`state
|
|
=/ =send-tx (got:ors:dice sending [address nonce])
|
|
:: if the number of txs sent is less than the ones in sending, we remove
|
|
:: them from the latest sending batch and add them on top of the pending list
|
|
::
|
|
=/ n-txs=@ud ?:(?=(%& -.result) -.p.result (lent txs.send-tx))
|
|
=/ not-sent=(list [=address:naive force=? =raw-tx:naive])
|
|
(slag n-txs txs.send-tx)
|
|
=/ partial-send=? &(?=(%& -.result) (lth n-txs (lent txs.send-tx)))
|
|
=? txs.send-tx partial-send
|
|
(oust [n-txs (lent txs.send-tx)] txs.send-tx)
|
|
=? pending partial-send
|
|
(fix-not-sent-pending not-sent)
|
|
=/ [nif=_finding sih=_history]
|
|
(fix-not-sent-status not-sent)
|
|
=: finding nif
|
|
history sih
|
|
==
|
|
~? partial-send [%extracting-txs-from-batch (lent not-sent)]
|
|
::
|
|
=? sending ?| ?=(%& -.result)
|
|
?=([%| %crash *] result)
|
|
==
|
|
%^ put:ors:dice sending
|
|
[address nonce]
|
|
:: update gas price for this tx in state
|
|
::
|
|
?: ?=(%& -.result)
|
|
send-tx(next-gas-price +.p.result, sent &)
|
|
:: if the thread crashed, we don't know the gas used, so we udpate it
|
|
:: manually, same as the thread would do. this has the problem of causing
|
|
:: the batch to be blocked if the thread keeps crashing, and we don't have
|
|
:: enough funds to pay.
|
|
::
|
|
:: on the other hand if the thread fails because +fetch-gas-price fails
|
|
:: (e.g. API change), and our fallback gas price is too low, the batch will
|
|
:: also be blocked, if we don't increase the next-gas-price, so either way
|
|
:: the batch will be stuck because of another underlying issue.
|
|
::
|
|
%_ send-tx
|
|
next-gas-price
|
|
?: =(0 next-gas-price.send-tx)
|
|
fallback-gas-price
|
|
(add next-gas-price.send-tx 5.000.000.000)
|
|
==
|
|
:_ state
|
|
?: ?& !sent.send-tx
|
|
?=([%| %not-sent %behind-nonce] result)
|
|
==
|
|
:: start out-of-sync flow if our L1 nonce is behind
|
|
:: and this transaction hasn't been sent out yet
|
|
::
|
|
~& > [dap.bowl %start-refresh-nonce-thread]
|
|
(get-nonce pk.state /refresh-nonce/(scot %ud nonce))
|
|
:: resend the l1 tx in five minutes if:
|
|
::
|
|
:: - the thread succeeds and returns the next gas price
|
|
:: - the thread failed because:
|
|
:: - the roll's eth addres doesn't have enough funds to pay
|
|
:: - the thread crashes
|
|
:: - the sending L1 nonce is ahead of the expected one
|
|
:: - a general ethereum error
|
|
::
|
|
:_ ~
|
|
%+ wait:b:sys
|
|
/resend/(scot %ux address)/(scot %ud nonce)
|
|
(add resend-time now.bowl)
|
|
::
|
|
++ fix-not-sent-pending
|
|
|= not-sent=(list [=address:naive force=? =raw-tx:naive])
|
|
=; txs=(list pend-tx)
|
|
(weld txs pending)
|
|
:: TODO: this would not be needed if txs.send-tx was a (list pend-tx)
|
|
::
|
|
%+ murn not-sent
|
|
|= [=address:naive force=? =raw-tx:naive]
|
|
=/ =keccak (hash-raw-tx:lib raw-tx)
|
|
?~ wer=(~(get by finding) keccak)
|
|
~& >>> %missing-tx-in-finding
|
|
~
|
|
?@ u.wer
|
|
~& >>> %missing-tx-in-finding
|
|
~
|
|
`[force address time.u.wer raw-tx]
|
|
::
|
|
++ fix-not-sent-status
|
|
|= not-sent=(list [=address:naive force=? =raw-tx:naive])
|
|
%+ roll not-sent
|
|
|= [[@ @ =raw-tx:naive] nif=_finding sih=_history]
|
|
=/ =keccak (hash-raw-tx:lib raw-tx)
|
|
?~ val=(~(get by nif) keccak)
|
|
[nif sih]
|
|
?. ?=(^ u.val)
|
|
[nif sih]
|
|
=* time time.u.val
|
|
=* address address.u.val
|
|
=* ship ship.from.tx.raw-tx
|
|
=/ l2-tx (l2-tx +<.tx.raw-tx)
|
|
=/ =roll-tx [ship %pending keccak l2-tx]
|
|
=+ txs=(~(got by sih) address)
|
|
=. txs +:(del:orh:dice txs time)
|
|
:- (~(del by nif) keccak)
|
|
%+ ~(put by sih) address
|
|
(put:orh:dice txs [time roll-tx])
|
|
--
|
|
:: +on-naive-diff: process l2 tx confirmations
|
|
::
|
|
++ on-naive-diff
|
|
|= =diff:naive
|
|
^- (quip card _state)
|
|
?. |(?=(%point -.diff) ?=(%tx -.diff))
|
|
[~ state]
|
|
=; [cards=(list card) =_state]
|
|
:_ state(derive ?:(derive | derive))
|
|
%+ weld cards
|
|
?. derive ~
|
|
:: defer updating state from canonical
|
|
::
|
|
[(wait:b:sys /predict (add update-rate now.bowl))]~
|
|
::
|
|
?: ?=(%point -.diff) [~ state]
|
|
?> ?=(%tx -.diff)
|
|
=/ =keccak (hash-raw-tx:lib raw-tx.diff)
|
|
?~ wer=(~(get by finding) keccak)
|
|
:: tx not submitted by this roller
|
|
::
|
|
[~ state]
|
|
?@ u.wer
|
|
~? &(?=(%confirmed u.wer) ?=(~ err.diff))
|
|
[dap.bowl %weird-double-confirm from.tx.raw-tx.diff]
|
|
[~ state]
|
|
=* nonce nonce.u.wer
|
|
=* tx-address address.u.wer
|
|
=* ship ship.from.tx.raw-tx.diff
|
|
=* time time.u.wer
|
|
=* tx tx.raw-tx.diff
|
|
=/ l2-tx (l2-tx +<.tx)
|
|
:: remove the tx from the sending map
|
|
::
|
|
=. sending
|
|
=/ =address:ethereum (get-address pk)
|
|
?~ sen=(get:ors:dice sending [address nonce])
|
|
~? lverb [dap.bowl %weird-double-remove nonce+nonce]
|
|
sending
|
|
?~ nin=(find [raw-tx.diff]~ (turn txs.u.sen (cork tail tail)))
|
|
~? lverb [dap.bowl %weird-unknown nonce+nonce]
|
|
sending
|
|
=. txs.u.sen (oust [u.nin 1] txs.u.sen)
|
|
?~ txs.u.sen
|
|
~? lverb
|
|
[dap.bowl %done-with-nonce [address nonce]]
|
|
=^ * sending
|
|
(del:ors:dice sending [address nonce])
|
|
sending
|
|
^+ sending
|
|
(put:ors:dice sending [address nonce] u.sen)
|
|
:: update the finding map with the new status
|
|
::
|
|
=. finding
|
|
%+ ~(put by finding) keccak
|
|
?~ err.diff %confirmed
|
|
:: if we kept the forced flag around for longer, we could notify of
|
|
:: unexpected tx failures here. would that be useful? probably not?
|
|
:: ~? !forced [dap.bowl %aggregated-tx-failed-anyway err.diff]
|
|
%failed
|
|
::
|
|
~? =(0x0 tx-address) %weird-null-tx-address^'on-naive-diff'
|
|
=^ updates history
|
|
%^ update-history:dice
|
|
history
|
|
[| tx-address time raw-tx.diff]~
|
|
?~(err.diff %confirmed %failed)
|
|
[(emit updates) state]
|
|
::
|
|
++ on-peek
|
|
|%
|
|
++ pending-by
|
|
|= wat=@t
|
|
?~ who=(slaw %p wat)
|
|
:: by-address
|
|
::
|
|
?~ wer=(slaw %ux wat)
|
|
[~ ~]
|
|
=; pending=(list pend-tx)
|
|
``noun+!>(pending)
|
|
%+ skim pending
|
|
|= pend-tx
|
|
:: TODO: use this instead? =(u.wer address)
|
|
::
|
|
?~ addr=(get-l1-address tx.raw-tx pre) |
|
|
=(u.wer u.addr)
|
|
:: by-ship
|
|
::
|
|
=; pending=(list pend-tx)
|
|
``noun+!>(pending)
|
|
%+ skim pending
|
|
|= pend-tx
|
|
=(u.who ship.from.tx.raw-tx)
|
|
::
|
|
++ status
|
|
|= wat=@t
|
|
?~ keccak=(slaw %ux wat)
|
|
[~ ~]
|
|
:+ ~ ~
|
|
:- %noun
|
|
!> ^- tx-status
|
|
?^ status=(~(get by finding) u.keccak)
|
|
?@ u.status [u.status ~]
|
|
[%sending `+.u.status]
|
|
:: TODO: potentially slow!
|
|
=; known=?
|
|
[?:(known %pending %unknown) ~]
|
|
%+ lien pending
|
|
|= pend-tx
|
|
=(u.keccak (hash-raw-tx:lib raw-tx))
|
|
::
|
|
++ transaction
|
|
|= wat=@t
|
|
?~ keccak=(slaw %ux wat)
|
|
[~ ~]
|
|
:+ ~ ~
|
|
:- %noun
|
|
!> ^- (unit pend-tx)
|
|
:: TODO: potentially slow!
|
|
|-
|
|
?~ pending ~
|
|
=* tx i.pending
|
|
?: =(u.keccak (hash-tx:lib raw.raw-tx.tx))
|
|
`tx
|
|
$(pending t.pending)
|
|
::
|
|
++ history
|
|
|= wat=@t
|
|
:+ ~ ~
|
|
:- %noun
|
|
!> ^- (list hist-tx)
|
|
?~ addr=(slaw %ux wat) ~
|
|
?~ hist=(~(get by ^history) u.addr) ~
|
|
(tap:orh:dice u.hist)
|
|
::
|
|
++ nonce
|
|
|= [who=@t proxy=@t]
|
|
?~ who=(slaw %p who)
|
|
[~ ~]
|
|
?. ?=(proxy:naive proxy)
|
|
[~ ~]
|
|
:+ ~ ~
|
|
:- %noun
|
|
!> ^- (unit @)
|
|
?~ point=(get:orp:dice points.pre u.who)
|
|
~
|
|
=< `nonce
|
|
(proxy-from-point:naive proxy u.point)
|
|
::
|
|
++ spawned
|
|
|= wat=@t
|
|
:+ ~ ~
|
|
:- %noun
|
|
!> ^- (list @p)
|
|
?~ star=(slaw %p wat) ~
|
|
=; range
|
|
(turn range head)
|
|
:: range exclusive [star first-moon-last-planet]
|
|
::
|
|
%- tap:orp:dice
|
|
(lot:orp:dice points.pre [`u.star `(cat 3 u.star 0x1.ffff)])
|
|
::
|
|
++ unspawned
|
|
|= wat=@t
|
|
:+ ~ ~
|
|
:- %noun
|
|
!> ^- (list @p)
|
|
?~ star=(slaw %p wat) ~
|
|
=/ spawned=(set @p)
|
|
=; points
|
|
(~(gas in *(set @p)) (turn points head))
|
|
%- tap:orp:dice
|
|
(lot:orp:dice points.pre [`u.star `(cat 3 u.star 0x1.ffff)])
|
|
=/ children=(list @p)
|
|
(turn (gulf 0x1 0xffff) |=(a=@ (cat 3 u.star a)))
|
|
%+ murn children
|
|
|= =ship
|
|
?: (~(has in spawned) ship) ~
|
|
`ship
|
|
::
|
|
++ sponsored
|
|
|= wat=@t
|
|
:+ ~ ~
|
|
:- %noun
|
|
!> ^- [(list ship) (list ship)]
|
|
?~ who=(slaw %p wat) [~ ~]
|
|
?~ sponsor=(~(get by spo) u.who)
|
|
[~ ~]
|
|
:- ~(tap in residents.u.sponsor)
|
|
~(tap in requests.u.sponsor)
|
|
::
|
|
++ point
|
|
|= wat=@t
|
|
?~ ship=(rush wat ;~(pfix sig fed:ag))
|
|
``noun+!>(*(unit point:naive))
|
|
``noun+!>((get:orp:dice points.pre u.ship))
|
|
::
|
|
++ ships
|
|
|= wat=@t
|
|
:+ ~ ~
|
|
:- %noun
|
|
!> ^- (list ship)
|
|
?~ addr=(slaw %ux wat) ~
|
|
(turn (controlled-ships:dice u.addr own) tail)
|
|
::
|
|
++ config
|
|
:+ ~ ~
|
|
:- %noun
|
|
!> ^- roller-config
|
|
:* next-batch
|
|
frequency
|
|
resend-time
|
|
update-rate
|
|
contract
|
|
chain-id
|
|
slice
|
|
quota
|
|
==
|
|
::
|
|
++ points-proxy
|
|
|= [=proxy:naive wat=@t]
|
|
:+ ~ ~
|
|
:- %noun
|
|
!> ^- (list ship)
|
|
?~ addr=(slaw %ux wat)
|
|
~
|
|
~(tap in (~(get ju own) [proxy u.addr]))
|
|
::
|
|
++ over-quota
|
|
|= wat=@t
|
|
?~ who=(slaw %p wat) [~ ~]
|
|
=/ [exceeded=? *] (quota-exceeded u.who)
|
|
``atom+!>(exceeded)
|
|
::
|
|
++ ship-quota
|
|
|= wat=@t
|
|
?~ who=(slaw %p wat) [~ ~]
|
|
=/ [exceeded=? next-quota=@ud] (quota-exceeded u.who)
|
|
=/ allow=(unit (unit @ud)) (~(get by allowances) u.who)
|
|
:+ ~ ~
|
|
:- %atom
|
|
!> ^- @ud
|
|
?: exceeded 0
|
|
=/ max-quota=@ quota.state
|
|
?: &(?=(^ allow) ?=(~ u.allow))
|
|
max-quota
|
|
=? max-quota &(?=(^ allow) ?=(^ u.allow))
|
|
u.u.allow
|
|
(sub max-quota (dec next-quota))
|
|
::
|
|
++ allowance
|
|
|= wat=@t
|
|
?~ who=(slaw %p wat) [~ ~]
|
|
:+ ~ ~
|
|
:- %noun
|
|
!> ^- (unit @ud)
|
|
?^ allow=(~(get by allowances) u.who)
|
|
u.allow
|
|
`quota.state
|
|
--
|
|
--
|