From b482249712f6816dc3a89f79430c7d839bbd0e18 Mon Sep 17 00:00:00 2001 From: Fang Date: Fri, 13 Dec 2019 23:30:52 +0100 Subject: [PATCH 01/39] ethio: implement +request-batch-rpc-loose Produces batch request results regardless of node-side error. Reimplements +request-batch-rpc-strict using it. --- pkg/arvo/lib/ethio.hoon | 51 +++++++++++++++++++++++++++++------------ 1 file changed, 36 insertions(+), 15 deletions(-) diff --git a/pkg/arvo/lib/ethio.hoon b/pkg/arvo/lib/ethio.hoon index 4f60430feb..759ae7fd46 100644 --- a/pkg/arvo/lib/ethio.hoon +++ b/pkg/arvo/lib/ethio.hoon @@ -21,7 +21,7 @@ %+ strand-fail:strandio %unexpected-multiple-results [>(lent res)< ~] -:: +request-batch-rpc-strict: send rpc request, with retry +:: +request-batch-rpc-strict: send rpc requests, with retry :: :: sends a batch request. produces results for all requests in the batch, :: but only if all of them are successful. @@ -32,8 +32,39 @@ `10 attempt-request :: - +$ result [id=@t =json] - +$ results (list result) + +$ results (list [id=@t =json]) + :: + ++ attempt-request + =/ m (strand:strandio ,(unit results)) + ^- form:m + ;< responses=(list response:rpc:jstd) bind:m + (request-batch-rpc-loose url reqs) + =- ?~ err + (pure:m `res) + (pure:m ~) + %+ roll responses + |= $: rpc=response:rpc:jstd + [res=results err=(list [id=@t code=@t message=@t])] + == + ?: ?=(%error -.rpc) + [res [+.rpc err]] + ?. ?=(%result -.rpc) + [res [['' 'ethio-rpc-fail' (crip )] err]] + [[+.rpc res] err] + -- +:: +request-batch-rpc-loose: send rpc requests, with retry +:: +:: sends a batch request. produces results for all requests in the batch, +:: including the ones that are unsuccessful. +:: +++ request-batch-rpc-loose + |= [url=@ta reqs=(list [id=(unit @t) req=request:rpc:ethereum])] + |^ %+ (retry:strandio results) + `10 + attempt-request + :: + +$ result response:rpc:jstd + +$ results (list response:rpc:jstd) :: ++ attempt-request =/ m (strand:strandio ,(unit results)) @@ -71,18 +102,7 @@ ((ar:dejs-soft:format parse-one-response) u.jon) ?~ array (strand-fail:strandio %rpc-result-incomplete-batch >u.jon< ~) - =- ?~ err - (pure:m `res) - (pure:m ~) - %+ roll u.array - |= $: rpc=response:rpc:jstd - [res=results err=(list [id=@t code=@t message=@t])] - == - ?: ?=(%error -.rpc) - [res [+.rpc err]] - ?. ?=(%result -.rpc) - [res [['' 'ethio-rpc-fail' (crip )] err]] - [[+.rpc res] err] + (pure:m array) :: ++ parse-one-response |= =json @@ -98,6 +118,7 @@ =, dejs-soft:format (ot id+so error+(ot code+no message+so ~) ~) -- +:: :: +read-contract: calls a read function on a contract, produces result hex :: ++ read-contract From fc1d852faf2f3e8b2d616a208ab874e03ad10a8e Mon Sep 17 00:00:00 2001 From: Fang Date: Fri, 13 Dec 2019 23:57:48 +0100 Subject: [PATCH 02/39] static gall: update send-txs Renamed to eth-sender. Can still sign eth-txs at multiple gas prices, fan transactions to multiple nodes, wait for confirmation between transaction batches (now of user-specified size). The previous nonce reading implementation was broken beyond belief and has been taken out. Can be reimplemented once RLP decoding is in the stdlib. --- pkg/arvo/app/eth-sender.hoon | 296 +++++++++++++++++++++++++++++++++ pkg/arvo/ted/eth/send-txs.hoon | 110 ++++++++++++ 2 files changed, 406 insertions(+) create mode 100644 pkg/arvo/app/eth-sender.hoon create mode 100644 pkg/arvo/ted/eth/send-txs.hoon diff --git a/pkg/arvo/app/eth-sender.hoon b/pkg/arvo/app/eth-sender.hoon new file mode 100644 index 0000000000..a7e6036beb --- /dev/null +++ b/pkg/arvo/app/eth-sender.hoon @@ -0,0 +1,296 @@ +:: eth-sender: utility for signing & sending eth-txs +:: +:: usage: +:: +:: sign txs for gasses of 2 and 11 gwei; (~ for default gwei set) +:: store at path +:: :eth-sender [%sign %/txs %/txs/eth-txs %/pk/txt ~[2 0]] +:: +:: read nonce range from signed transactions at path +:: :eth-sender [%read %txs/txt] +:: +:: send all but first 50 txs from path to mainnet, +:: waiting for confirms every 4 txs +:: :eth-sender [%send %/txs/txt 4 `index+50 ~] +:: +/+ default-agent, verb +:: +|% +++ state-0 + $: %0 + ~ + == +:: ++$ tx-range + $: how=?(%nonce %index) :: tx nonce / index in file + wat=$@(@ud [start=@ud end=@ud]) :: inclusive. end optional + == +:: ++$ command + $% [%sign out=path in=path key=path gweis=(list @ud)] + [%read =path] + :: + $: %send + txs=path + step-size=@ud + range=(unit tx-range) + nodes=(list [id=@tas url=@t]) + == + == +:: ++$ card card:agent:gall +-- +:: +=| state-0 +=* state - +%+ verb | +=< + |_ =bowl:gall + +* this . + do ~(. +> bowl) + def ~(. (default-agent this %|) bowl) + :: + ++ on-init on-init:def + ++ on-save !>(state) + ++ on-load on-load:def + :: + ++ on-poke + |= [=mark =vase] + ^- (quip card _this) + ?. ?=(%noun mark) [~ this] + =/ =command !<(command vase) + ?- -.command + %read + ~& (read-transactions:do +.command) + [~ this] + :: + %sign + :_ this + (sign-transactions:do +.command) + :: + %send + :_ this + (send-transactions:do +.command) + == + :: + ++ on-agent + |= [=wire =sign:agent:gall] + ^- (quip card _this) + ?. ?=([%send *] wire) + (on-agent:def wire sign) + ?- -.sign + %poke-ack + ?~ p.sign + [~ this] + %- (slog leaf+"{(trip dap.bowl)} couldn't start thread" u.p.sign) + :_ this + [(leave-spider:do wire our.bowl)]~ + :: + %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+ tang) + [~ this] + :: + %thread-done + ~& ['all submitted to' t.wire] + [~ this] + == + == + :: + ++ on-peek on-peek:def + ++ on-watch on-watch:def + ++ on-leave on-leave:def + ++ on-arvo on-arvo:def + ++ on-fail on-fail:def + -- +:: +|_ =bowl:gall +++ poke-spider + |= [=path our=@p =cage] + ^- card + [%pass path %agent [our %spider] %poke cage] +:: +++ watch-spider + |= [=path our=@p =sub=path] + ^- card + [%pass path %agent [our %spider] %watch sub-path] +:: +++ leave-spider + |= [=path our=@p] + ^- card + [%pass path %agent [our %spider] %leave ~] +:: +++ start-txs-send + |= [[node-id=@tas node=@t] step=@ud txs=(list @ux)] + ^- (list card) + =/ tid=@ta + :((cury cat 3) dap.bowl '--' node-id '--' (scot %uv eny.bowl)) + =/ args + :^ ~ `tid %eth-send-txs + !>([node step txs]) + :~ (watch-spider /send/[tid] our.bowl /thread-result/[tid]) + (poke-spider /send/[tid] our.bowl %spider-start !>(args)) + == +:: +:: +++ get-file + |= =path + ~| path + .^ (list cord) + %cx + (scot %p our.bowl) + %home + (scot %da now.bowl) + path + == +:: +++ write-file-wain + |= [=path tox=(list cord)] + ^- card + ?> ?=([@ desk @ *] path) + =- [%pass [%write path] %arvo %c %info -] + :- i.t.path + =- &+[t.t.t.path -]~ + =/ y .^(arch %cy path) + ?~ fil.y + ins+txt+!>(tox) + mut+txt+!>(tox) +:: +++ write-file-transactions + |= [=path tox=(list transaction:rpc:ethereum)] + ^- card + ?> ?=([@ desk @ *] path) + =- [%pass [%write path] %arvo %c %info -] + :- i.t.path + =- &+[t.t.t.path -]~ + =/ y .^(arch %cy path) + ?~ fil.y + ins+eth-txs+!>(tox) + mut+eth-txs+!>(tox) +:: +:: +++ read-transactions + |= =path + ^- tape + =+ tox=.^((list cord) %cx path) + =+ [first last]=(read-nonces tox) + %+ weld + "Found nonces {(scow %ud first)} through {(scow %ud last)}" + " in {(scow %ud (lent tox))} transactions." +:: +++ read-nonces + |= tox=(list cord) + |^ ^- [@ud @ud] + ?: =(~ tox) ::NOTE tmi + [0 0] + :- (read-nonce (snag 0 tox)) + (read-nonce (snag (dec (lent tox)) tox)) + :: + ++ read-nonce + |= tex=cord + ^- @ud + ~| %nonce-reading-unimplemented + !! ::TODO implement rlp decoding, then decode tex to get nonce + -- +:: +:: +++ sign-transactions + |= [out=path in=path key=path gasses=(list @ud)] + ^- (list card) + %+ turn + ?. =(~ gasses) gasses + :: default gwei set + ~[4 8 12 20 32] + |= gas=@ud + %+ write-file-wain + :: add gas amount to path + =+ end=(dec (lent out)) + =- (weld (scag end out) -) + ?: =(0 gas) [(snag end out) /txt] + :_ /txt + (cat 3 (snag end out) (crip '-' ((d-co:co 1) gas))) + :: + %- sign + :+ in key + :: modify tx gas if non-zero gwei specified + ?: =(0 gas) ~ + `(mul gas 1.000.000.000) +:: +++ sign + =, rpc:ethereum + |= [in=path key=path gas=(unit @ud)] + ^- (list cord) + ?> ?=([@ @ @ *] key) + =/ pkf (get-file t.t.t.key) + ?> ?=(^ pkf) + =/ pk (rash i.pkf ;~(pfix (jest '0x') hex)) + =/ txs .^((list transaction) %cx in) + =/ enumerated + =/ n 1 + |- ^- (list [@ud transaction]) + ?~ txs + ~ + [[n i.txs] $(n +(n), txs t.txs)] + %+ turn enumerated + |= [n=@ud tx=transaction] + ~? =(0 (mod n 100)) [%signing n] + =? gas-price.tx ?=(^ gas) u.gas + (crip '0' 'x' ((x-co:co 0) (sign-transaction:key:ethereum tx pk))) +:: +++ send-transactions + |= [=path step=@ud range=(unit tx-range) nodes=(list [id=@tas url=@t])] + ^- (list card) + =? nodes =(~ nodes) + :~ geth+'http://eth-mainnet.urbit.org:8545' + == + ~& 'loading txs...' + =/ tox=(list cord) .^((list cord) %cx path) + =? tox ?=(^ range) + (txs-in-range tox u.range) + =/ txs=(list @ux) + %+ turn tox + (cork trip tape-to-ux) + ~& ['sending txs:' (lent txs)] + %- zing + %+ turn nodes + |= node=[@tas @t] + (start-txs-send node step txs) +:: +++ txs-in-range + |= [tox=(list cord) =tx-range] + ^+ tox + =* ran wat.tx-range + ?- how.tx-range + %index + ?@ ran + (slag ran tox) + %+ slag start.ran + (scag end.ran tox) + :: + %nonce + =+ [first last]=(read-nonces tox) + ?: !=((lent tox) +((sub last first))) + ~| ['probably non-contiguous set of transactions' -] + !! + ?@ ran + (slag (sub ran first) tox) + %+ slag (sub start.ran first) + (scag (sub +(end.ran) first) tox) + == +:: +++ tape-to-ux + |= t=tape + (scan t ;~(pfix (jest '0x') hex)) +-- diff --git a/pkg/arvo/ted/eth/send-txs.hoon b/pkg/arvo/ted/eth/send-txs.hoon new file mode 100644 index 0000000000..f59d95c48a --- /dev/null +++ b/pkg/arvo/ted/eth/send-txs.hoon @@ -0,0 +1,110 @@ +:: eth/send-txs: query ethereum for contract data +:: +:: produces hex string result, for use with +decode-results:rpc:ethereum +:: +/+ ethio, strandio +:: +=> + |% + ++ tape-to-ux + |= t=tape + (scan t ;~(pfix (jest '0x') hex)) + -- +:: +|= args=vase +=+ !<([url=@t step-size=@ud txs=(list @ux)] args) +=/ m (strand:strandio ,vase) +^- form:m +|- +=* submit-loop $ +~& ['remaining txs:' (lent txs)] +?: =(~ txs) (pure:m !>(~)) +:: send a step-size batch of transactions +:: +;< responses=(list response:rpc:jstd) bind:m + %+ request-batch-rpc-loose:ethio url + %+ turn (scag step-size txs) + |= tx=@ux + :- `(scot %ux (end 3 10 tx)) + [%eth-send-raw-transaction tx] +:: parse tx hashes out of responses, bailing on submission failure +:: +=/ pending=(each (set @ux) [term tang]) + =| pending=(list @ux) + |- + ?~ responses &+(sy pending) + =/ res=response:rpc:jstd i.responses ::NOTE =* breaks typechecks + ?+ -.res |+[%unexpected-non-result >res< ~] + %result + %_ $ + responses t.responses + pending [(tape-to-ux (sa:dejs:format res.res)) pending] + == + :: + %error + ?: ?| =('known transaction' (end 3 17 message.res)) + =('Known transaction' (end 3 17 message.res)) + =('Transaction with the same ' (end 3 26 message.res)) + == + ~& [%sent-a-known-transaction--skipping id.res] + $(responses t.responses) + ?: ?| =('nonce too low' message.res) + =('Nonce too low' message.res) + == + ::NOTE this assumes it's an "oops re-sent txs" case + ~& [%nonce-too-low--skipping id.res] + $(responses t.responses) + |+[%transaction-send-failed >+.res< ~] + == +?: ?=(%| -.pending) + (strand-fail:strandio p.pending) +:: wait for the transactions to get confirmed +:: +|- +=* confirm-loop $ +?: =(~ p.pending) + :: all confirmed, continue to next step-size transactions + :: + submit-loop(txs (slag step-size txs)) +~& [~(wyt in p.pending) 'txs awaiting confirmation'] +:: get receipts +:: +;< responses=(list response:rpc:jstd) bind:m + %+ request-batch-rpc-loose:ethio url + %+ turn ~(tap in p.pending) + |= txh=@ux + :- `(crip '0' 'x' ((x-co:co 64) txh)) + [%eth-get-transaction-receipt txh] +:: find transactions that haven't been confirmed yet, bailing on failure +:: +=/ unconfirmed=(each (set @ux) [term tang]) + =| done=(list @ux) + |- + ?~ responses &+(~(dif in p.pending) (sy done)) + =/ res=response:rpc:jstd i.responses ::NOTE =* breaks typechecks + ?. ?=(?(%error %result) -.res) + |+[%unexpected-non-result >res< ~] + =/ txh=@ux (tape-to-ux (trip id.res)) + ?- -.res + %error + ~& :- 'receipt fetch error' + [code.res message.res] + $(responses t.responses) + :: + %result + ?. ?=([%o *] res.res) + $(responses t.responses) + =/ status=@ + %- tape-to-ux + %- sa:dejs:format + (~(got by p.res.res) 'status') + ?. =(1 status) + |+[%transaction-failed >txh< ~] + $(responses t.responses, done [txh done]) + == +?: ?=(%| -.unconfirmed) + (strand-fail:strandio p.unconfirmed) +:: some transactions still pending, wait for a bit, then check again +:: +;< ~ bind:m (sleep:strandio ~s30) +confirm-loop(pending unconfirmed) From 769a1c96afc6ac486f11122bb4ee0cf59619f1f6 Mon Sep 17 00:00:00 2001 From: Philip Monk Date: Sat, 14 Dec 2019 00:43:04 -0800 Subject: [PATCH 03/39] eyre: turn sigpam into flog This error is mostly harmless, but it does indicate we aren't cleaning up our subscriptions properly. This lets you silence with |knob. fixes #2088 --- pkg/arvo/sys/vane/eyre.hoon | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/arvo/sys/vane/eyre.hoon b/pkg/arvo/sys/vane/eyre.hoon index f1553be529..52b09a3bf2 100644 --- a/pkg/arvo/sys/vane/eyre.hoon +++ b/pkg/arvo/sys/vane/eyre.hoon @@ -1590,8 +1590,8 @@ =/ channel=(unit channel) (~(get by session.channel-state.state) channel-id) ?~ channel - ~& [%received-event-for-nonexistent-channel channel-id] - [~ state] + :_ state :_ ~ + [duct %pass /flog %d %flog %crud %eyre-no-channel >id=channel-id< ~] :: =/ event-id next-id.u.channel :: From 5c56cce6ea669106cd9e2d61375ebe4462871fb0 Mon Sep 17 00:00:00 2001 From: Fang Date: Sat, 14 Dec 2019 21:08:24 +0100 Subject: [PATCH 04/39] zuse: lightly restructure encode:rlp:ethereum --- pkg/arvo/sys/zuse.hoon | 48 ++++++++++++++++++++++-------------------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/pkg/arvo/sys/zuse.hoon b/pkg/arvo/sys/zuse.hoon index 9dbe2a31c3..48ca3ed535 100644 --- a/pkg/arvo/sys/zuse.hoon +++ b/pkg/arvo/sys/zuse.hoon @@ -8040,39 +8040,41 @@ $% [%l l=(list item)] [%b b=byts] == + :: +encode-atoms: encode list of atoms as a %l of %b items :: - :: treat atoms as list of items ++ encode-atoms |= l=(list @) + ^- @ %+ encode %l %+ turn l |=(a=@ b+[(met 3 a) a]) :: ++ encode |= in=item - ^- @ - ?- -.in - %b - ?: &(=(1 wid.b.in) (lth dat.b.in 0x80)) - dat.b.in - %^ cat 3 dat.b.in - ::TODO unsure if this should pass wid or (met 3 dat)... - (encode-length wid.b.in 0x80) + |^ ^- @ + ?- -.in + %b + ?: &(=(1 wid.b.in) (lte dat.b.in 0x7f)) + dat.b.in + %^ cat 3 dat.b.in + ::TODO unsure if this should pass wid or (met 3 dat)... + (encode-length wid.b.in 0x80) + :: + %l + =/ out=@ + %+ roll l.in + |= [ni=item en=@] + (cat 3 (encode ni) en) + %^ cat 3 out + (encode-length (met 3 out) 0xc0) + == :: - %l - =/ out=@ - %+ roll l.in - |= [ni=item en=@] - (cat 3 (encode ni) en) - %^ cat 3 out - (encode-length (met 3 out) 0xc0) - == - :: - ++ encode-length - |= [len=@ off=@] - ?: (lth len 56) (add len off) - =- (cat 3 len -) - :(add (met 3 len) off 55) + ++ encode-length + |= [len=@ off=@] + ?: (lth len 56) (add len off) + =- (cat 3 len -) + :(add (met 3 len) off 55) + -- :: ::TODO decode :: From 7755bc97183c131dc4ec9efacd4cb6c107dde860 Mon Sep 17 00:00:00 2001 From: Fang Date: Sat, 14 Dec 2019 21:25:20 +0100 Subject: [PATCH 05/39] zuse: correctly encode leading zeroes in rlp data --- pkg/arvo/sys/zuse.hoon | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pkg/arvo/sys/zuse.hoon b/pkg/arvo/sys/zuse.hoon index 48ca3ed535..c266a114a6 100644 --- a/pkg/arvo/sys/zuse.hoon +++ b/pkg/arvo/sys/zuse.hoon @@ -8056,8 +8056,7 @@ %b ?: &(=(1 wid.b.in) (lte dat.b.in 0x7f)) dat.b.in - %^ cat 3 dat.b.in - ::TODO unsure if this should pass wid or (met 3 dat)... + =- (can 3 ~[b.in [(met 3 -) -]]) (encode-length wid.b.in 0x80) :: %l From 1647d760d10649278950116fb33bf8dc1d9d297c Mon Sep 17 00:00:00 2001 From: Fang Date: Sat, 14 Dec 2019 21:39:54 +0100 Subject: [PATCH 06/39] zuse: implement eth rlp decoding Also updates existing tests to check both en- and decoding. --- bin/solid.pill | 4 +- pkg/arvo/sys/zuse.hoon | 83 +++++++++++++++++++- pkg/arvo/tests/sys/zuse/ethereum/rlp.hoon | 96 ++++++++++++----------- 3 files changed, 134 insertions(+), 49 deletions(-) diff --git a/bin/solid.pill b/bin/solid.pill index a3e9dd86b4..85758a4685 100644 --- a/bin/solid.pill +++ b/bin/solid.pill @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:04735cc4764f9a3e6c4fb5b046a6b9590664fe9f644578c58f3bc6acc911b723 -size 9606039 +oid sha256:9c3a02071d4f701011b2afa9230420f310b7e3689b9e2837c55c871121dca21b +size 16486956 diff --git a/pkg/arvo/sys/zuse.hoon b/pkg/arvo/sys/zuse.hoon index c266a114a6..5b4bdade8e 100644 --- a/pkg/arvo/sys/zuse.hoon +++ b/pkg/arvo/sys/zuse.hoon @@ -8074,9 +8074,90 @@ =- (cat 3 len -) :(add (met 3 len) off 55) -- + :: +decode-atoms: decode expecting a %l of %b items, producing atoms within :: - ::TODO decode + ++ decode-atoms + |= dat=@ + ^- (list @) + =/ i=item (decode dat) + ~| [%unexpected-data i] + ?> ?=(%l -.i) + %+ turn l.i + |= i=item + ~| [%unexpected-list i] + ?> ?=(%b -.i) + dat.b.i :: + ++ decode + |= dat=@ + ^- item + =/ bytes=(list @) (flop (rip 3 dat)) + =? bytes ?=(~ bytes) ~[0] + |^ item:decode-head + :: + ++ decode-head + ^- [done=@ud =item] + ?~ bytes + ~| %rlp-unexpected-end + !! + =* byt i.bytes + :: byte in 0x00-0x79 range encodes itself + :: + ?: (lte byt 0x79) + :- 1 + [%b 1^byt] + :: byte in 0x80-0xb7 range encodes string length + :: + ?: (lte byt 0xb7) + =+ len=(sub byt 0x80) + :- +(len) + :- %b + len^(get-value 1 len) + :: byte in 0xb8-0xbf range encodes string length length + :: + ?: (lte byt 0xbf) + =+ led=(sub byt 0xb7) + =+ len=(get-value 1 led) + :- (add +(led) len) + :- %b + len^(get-value +(led) len) + :: byte in 0xc0-f7 range encodes list length + :: + ?: (lte byt 0xf7) + =+ len=(sub byt 0xc0) + :- +(len) + :- %l + %. len + decode-list(bytes (slag 1 `(list @)`bytes)) + :: byte in 0xf8-ff range encodes list length length + :: + ?: (lte byt 0xff) + =+ led=(sub byt 0xf7) + =+ len=(get-value 1 led) + :- (add +(led) len) + :- %l + %. len + decode-list(bytes (slag +(led) `(list @)`bytes)) + ~| [%rip-not-bloq-3 `@ux`byt] + !! + :: + ++ decode-list + |= rem=@ud + ^- (list item) + ?: =(0 rem) ~ + =+ ^- [don=@ud =item] ::TODO =/ + decode-head + :- item + %= $ + rem (sub rem don) + bytes (slag don bytes) + == + :: + ++ get-value + |= [at=@ud to=@ud] + ^- @ + (rep 3 (flop (swag [at to] bytes))) + -- -- :: :: abi en/decoding diff --git a/pkg/arvo/tests/sys/zuse/ethereum/rlp.hoon b/pkg/arvo/tests/sys/zuse/ethereum/rlp.hoon index 92431fd844..b11c338261 100644 --- a/pkg/arvo/tests/sys/zuse/ethereum/rlp.hoon +++ b/pkg/arvo/tests/sys/zuse/ethereum/rlp.hoon @@ -1,53 +1,57 @@ -/+ *test -|% ::NOTE tests lightly modified from the examples here: :: https://github.com/ethereum/wiki/wiki/RLP :: -++ test-encoding-string - %+ expect-eq - !> %- encode:rlp:ethereum - b+3^0xaa.bbcc - !> 0x83aa.bbcc +/+ *test +=, rlp:ethereum :: -++ test-encoding-list - %+ expect-eq - !> %- encode:rlp:ethereum - l+~[b+3^0xaa.bbcc b+3^0xdd.eeff] - !> 0xc8.83aa.bbcc.83dd.eeff +=/ vectors=(list [nom=tape dat=@ rlp=item]) + :~ :+ "string" + 0x83aa.bbcc + b+3^0xaa.bbcc + :: + :+ "list" + 0xc8.83aa.bbcc.83dd.eeff + l+~[b+3^0xaa.bbcc b+3^0xdd.eeff] + :: + :+ "empty list" + 0xc0 + l+~ + :: + :+ "zero byte" + 0x0 + b+1^0x0 + :: + :+ "empty zero" + 0x80 + b+0^0x0 + :: + :+ "value 15" + 0xf + b+1^0xf + :: + :+ "value 1024" + 0x82.0400 + b+2^0x400 + :: + :+ "set of three" + 0xc7c0.c1c0.c3c0.c1c0 + l+[l+~ l+[l+~ ~] l+[l+~ l+[l+~ ~] ~] ~] + == :: -++ test-encoding-empty-list +|% +++ test-all-vectors + |- ^- tang + ?~ vectors ~ + %+ weld $(vectors t.vectors) + =, i.vectors + %+ category nom + %+ weld + %+ category "encode" + %+ expect-eq + !> dat + !> (encode rlp) + %+ category "decode" %+ expect-eq - !> %- encode:rlp:ethereum - l+~ - !> 0xc0 -:: -++ test-encoding-zero-byte - %+ expect-eq - !> %- encode:rlp:ethereum - b+1^0x0 - !> 0x0 -:: -++ test-encoding-empty-zero - %+ expect-eq - !> %- encode:rlp:ethereum - b+0^0x0 - !> 0x80 -:: -++ test-encoding-15 - %+ expect-eq - !> %- encode:rlp:ethereum - b+1^15 - !> 0xf -:: -++ test-encoding-1024 - %+ expect-eq - !> %- encode:rlp:ethereum - b+2^1.024 - !> 0x82.0400 -:: -++ test-encoding-set-of-three - %+ expect-eq - !> %- encode:rlp:ethereum - l+[l+~ l+[l+~ ~] l+[l+~ l+[l+~ ~] ~] ~] - !> 0xc7c0.c1c0.c3c0.c1c0 + !> rlp + !> (decode dat) -- From bdc7fb81e08bab36f6304a36e99d52ebfc2542f4 Mon Sep 17 00:00:00 2001 From: Fang Date: Sat, 14 Dec 2019 21:50:07 +0100 Subject: [PATCH 07/39] eth-sender: do proper nonce reading --- pkg/arvo/app/eth-sender.hoon | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pkg/arvo/app/eth-sender.hoon b/pkg/arvo/app/eth-sender.hoon index a7e6036beb..27f0ef2c7d 100644 --- a/pkg/arvo/app/eth-sender.hoon +++ b/pkg/arvo/app/eth-sender.hoon @@ -201,8 +201,9 @@ ++ read-nonce |= tex=cord ^- @ud - ~| %nonce-reading-unimplemented - !! ::TODO implement rlp decoding, then decode tex to get nonce + %+ snag 0 + %- decode-atoms:rlp:ethereum + (tape-to-ux (trip tex)) -- :: :: From 249c02fe9fb400521b8f5ab27050c1284b9a9bcc Mon Sep 17 00:00:00 2001 From: Brendan Hay Date: Mon, 16 Dec 2019 09:18:08 +0100 Subject: [PATCH 08/39] build: trimming unnecessary image configuration --- nix/ops/image/default.nix | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/nix/ops/image/default.nix b/nix/ops/image/default.nix index f182dfcf91..5917293847 100644 --- a/nix/ops/image/default.nix +++ b/nix/ops/image/default.nix @@ -8,23 +8,20 @@ pkgs.dockerTools.buildImage { set -euo pipefail - export PATH=/bin:/usr/bin:/sbin:/usr/sbin:$PATH - ${pkgs.dockerTools.shadowSetup} - mkdir -p /bin /share /data /tmp + mkdir -p /share /data /tmp ${pkgs.coreutils}/bin/ln -sf ${pill} /share/urbit.pill - ${pkgs.coreutils}/bin/ln -sf ${entrypoint} /bin/urbit ''; + contents = [ urbit ]; + config = { - Entrypoint = [ "urbit" ]; + Entrypoint = [ urbit.meta.name ]; WorkingDir = "/data"; - Env = [ "PATH=/bin" ]; - Volumes = { "/data" = {}; }; From e88f91f3b2ff15f48c5d8abc213a6dfe8db5d32f Mon Sep 17 00:00:00 2001 From: Philip Monk Date: Fri, 6 Dec 2019 14:22:11 -0800 Subject: [PATCH 09/39] eth-watcher: actually stop pending thread when restarting --- pkg/arvo/app/eth-watcher.hoon | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/arvo/app/eth-watcher.hoon b/pkg/arvo/app/eth-watcher.hoon index d57de6025c..7f1c47ad3f 100644 --- a/pkg/arvo/app/eth-watcher.hoon +++ b/pkg/arvo/app/eth-watcher.hoon @@ -154,7 +154,7 @@ ~ [(wait-shortcut path.poke now.bowl) ~] :: - =/ restart-cards + =/ restart-cards=(list card) =/ dog (~(get by dogs.state) path.poke) ?. ?& restart ?=(^ dog) @@ -162,7 +162,7 @@ == ~ =/ =cage [%spider-stop !>([u.running.u.dog &])] - [%pass [%starting path] %agent [our.bowl %spider] %poke cage] + [%pass [%starting path] %agent [our.bowl %spider] %poke cage]~ =/ new-dog =/ dog=watchdog ?: restart *watchdog @@ -176,7 +176,7 @@ :: %clear =. dogs.state (~(del by dogs.state) path.poke) - [~ this] + [(weld wait-cards restart-cards) this] == :: :: +on-watch: subscribe & get initial subscription data From e5ac690fd3557067489cf639c4cee9572bd3f487 Mon Sep 17 00:00:00 2001 From: Philip Monk Date: Fri, 6 Dec 2019 20:44:36 -0800 Subject: [PATCH 10/39] jael: re-enable ship-to-ship communication Also fix bug in eth-watcher that didn't cancel outstanding threads when config changes. And set default rift for ourselves to 0. --- pkg/arvo/app/eth-watcher.hoon | 9 +++++---- pkg/arvo/sys/vane/jael.hoon | 26 +++++++++++++++++++------- pkg/arvo/ted/ph/all.hoon | 1 + pkg/arvo/ted/ph/breach-hi-cousin.hoon | 4 ++++ pkg/arvo/ted/ph/breach-multiple.hoon | 2 ++ pkg/arvo/ted/ph/breach-sudden.hoon | 4 ++++ pkg/arvo/ted/ph/breach-sync.hoon | 2 ++ 7 files changed, 37 insertions(+), 11 deletions(-) diff --git a/pkg/arvo/app/eth-watcher.hoon b/pkg/arvo/app/eth-watcher.hoon index 7f1c47ad3f..3037b66ccf 100644 --- a/pkg/arvo/app/eth-watcher.hoon +++ b/pkg/arvo/app/eth-watcher.hoon @@ -149,7 +149,7 @@ =/ already (~(has by dogs.state) path.poke) ~? &(already restart) [dap.bowl 'overwriting existing watchdog on' path.poke] - =/ wait-cards + =/ wait-cards=(list card) ?: already ~ [(wait-shortcut path.poke now.bowl) ~] @@ -162,7 +162,8 @@ == ~ =/ =cage [%spider-stop !>([u.running.u.dog &])] - [%pass [%starting path] %agent [our.bowl %spider] %poke cage]~ + :_ ~ + `card`[%pass [%starting path.poke] %agent [our.bowl %spider] %poke cage] =/ new-dog =/ dog=watchdog ?: restart *watchdog @@ -172,11 +173,11 @@ number from.config.poke == =. dogs.state (~(put by dogs.state) path.poke new-dog) - [wait-cards this] + [(weld wait-cards restart-cards) this] :: %clear =. dogs.state (~(del by dogs.state) path.poke) - [(weld wait-cards restart-cards) this] + [~ this] == :: :: +on-watch: subscribe & get initial subscription data diff --git a/pkg/arvo/sys/vane/jael.hoon b/pkg/arvo/sys/vane/jael.hoon index a67884f75c..e2d92e7e26 100644 --- a/pkg/arvo/sys/vane/jael.hoon +++ b/pkg/arvo/sys/vane/jael.hoon @@ -257,14 +257,14 @@ =. sig.own.pki sig.seed.tac :: load our initial public key :: + =/ spon-ship=(unit ship) + =/ flopped-spon (flop spon.tac) + ?~(flopped-spon ~ `ship.i.flopped-spon) =. pos.zim.pki =/ cub (nol:nu:crub:crypto key.seed.tac) %+ ~(put by pos.zim.pki) our - =/ spon-ship - =/ flopped-spon (flop spon.tac) - ?~(flopped-spon ~ `ship.i.flopped-spon) - [1 lyf.seed.tac (my [lyf.seed.tac [1 pub:ex:cub]] ~) spon-ship] + [0 lyf.seed.tac (my [lyf.seed.tac [1 pub:ex:cub]] ~) spon-ship] :: our initial private key :: =. lyf.own.pki lyf.seed.tac @@ -307,8 +307,20 @@ :: =. +>.$ (poke-watch hen %azimuth-tracker nod.own.pki) =. +>.$ - %- curd =< abet - (sources:~(feel su hen our pki etn) ~ [%| %azimuth-tracker]) + ?- (clan:title our) + %czar + %- curd =< abet + (sources:~(feel su hen our pki etn) ~ [%| %azimuth-tracker]) + :: + * + =. +>.$ + %- curd =< abet + %+ sources:~(feel su hen our pki etn) + (silt (turn spon-points head)) + [%| %azimuth-tracker] + %- curd =< abet + (sources:~(feel su hen our pki etn) ~ [%& (need spon-ship)]) + == :: =. moz %+ weld moz @@ -508,7 +520,7 @@ ^+ +> ?- hin [%a %done *] - ?~ error.hin ~&(%ares-fine +>.$) + ?~ error.hin +>.$ ~& [%done-bad tag.u.error.hin] %- (slog tang.u.error.hin) ::TODO fail:et diff --git a/pkg/arvo/ted/ph/all.hoon b/pkg/arvo/ted/ph/all.hoon index d107061437..9fb13636e1 100644 --- a/pkg/arvo/ted/ph/all.hoon +++ b/pkg/arvo/ted/ph/all.hoon @@ -14,6 +14,7 @@ %ph-child-sync %ph-breach-multiple %ph-breach-sudden + %ph-breach-hi-cousin %ph-hi-linnup-az %ph-moon-az == diff --git a/pkg/arvo/ted/ph/breach-hi-cousin.hoon b/pkg/arvo/ted/ph/breach-hi-cousin.hoon index 52e7d2f872..5984913e7b 100644 --- a/pkg/arvo/ted/ph/breach-hi-cousin.hoon +++ b/pkg/arvo/ted/ph/breach-hi-cousin.hoon @@ -1,3 +1,7 @@ +:: This is useful to test that we properly hear about breaches outside +:: our sponsorship tree. We usually hear about these via ship-to-ship +:: communication. +:: /- spider /+ *ph-io =, strand=strand:spider diff --git a/pkg/arvo/ted/ph/breach-multiple.hoon b/pkg/arvo/ted/ph/breach-multiple.hoon index 5d2f2306a1..4c76bc8a6f 100644 --- a/pkg/arvo/ted/ph/breach-multiple.hoon +++ b/pkg/arvo/ted/ph/breach-multiple.hoon @@ -1,3 +1,5 @@ +:: This tests breaches of both parent and child in succession. +:: /- spider /+ *ph-io =, strand=strand:spider diff --git a/pkg/arvo/ted/ph/breach-sudden.hoon b/pkg/arvo/ted/ph/breach-sudden.hoon index 8761e87c63..ae6af62ddc 100644 --- a/pkg/arvo/ted/ph/breach-sudden.hoon +++ b/pkg/arvo/ted/ph/breach-sudden.hoon @@ -1,3 +1,7 @@ +:: This tests whether we can handle the case where our immediate +:: sponsor dies without telling us about its breach, so we must hear +:: about it from somewhere else. +:: /- spider /+ *ph-io =, strand=strand:spider diff --git a/pkg/arvo/ted/ph/breach-sync.hoon b/pkg/arvo/ted/ph/breach-sync.hoon index 494dffe4d9..d8a4ac09fb 100644 --- a/pkg/arvo/ted/ph/breach-sync.hoon +++ b/pkg/arvo/ted/ph/breach-sync.hoon @@ -1,3 +1,5 @@ +:: This tests that syncs are correctly restarted after a breach +:: /- spider /+ *ph-io =, strand=strand:spider From 7ca3d9624edb753bab620e660c054183ccde3584 Mon Sep 17 00:00:00 2001 From: Philip Monk Date: Tue, 17 Dec 2019 19:20:32 -0800 Subject: [PATCH 11/39] ames: handle misordered crashing boons Two bugs fixed here: first, if the %done reentrancy triggered another %boon, that wasn't getting translated to a %lost, even though it could have been the reason the event crashed in the first place. Second, the %done reentrancy needs to happen after we emit our move, so that we don't invert the order of the %boon's we produce. --- pkg/arvo/sys/vane/ames.hoon | 20 ++++++++++++++++---- pkg/arvo/tests/sys/vane/ames.hoon | 2 +- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/pkg/arvo/sys/vane/ames.hoon b/pkg/arvo/sys/vane/ames.hoon index 647fad1ba8..b63a58386c 100644 --- a/pkg/arvo/sys/vane/ames.hoon +++ b/pkg/arvo/sys/vane/ames.hoon @@ -2217,17 +2217,29 @@ ^+ peer-core :: send ack unconditionally :: + =. peer-core (emit (got-duct bone) %give %boon message) =. peer-core (run-message-sink bone %done ok=%.y) :: ?. ?=([%hear * * ok=%.n] task) :: fresh boon; give message to client vane :: - %- (trace msg.veb |.("boon {}")) - (emit (got-duct bone) %give %boon message) + %- (trace msg.veb |.("boon {}")) + peer-core :: we previously crashed on this message; notify client vane :: - %- (trace msg.veb |.("crashed on boon {}")) - (emit (got-duct bone) %give %lost ~) + %- (trace msg.veb |.("crashed on boon {}")) + boon-to-lost + :: +boon-to-lost: convert all boons to losts + :: + ++ boon-to-lost + ^+ peer-core + =. moves + %+ turn moves + |= =move + ?. ?=([* %give %boon *] move) + move + [duct.move %give %lost ~] + peer-core :: +on-sink-nack-trace: handle nack-trace received by |message-sink :: ++ on-sink-nack-trace diff --git a/pkg/arvo/tests/sys/vane/ames.hoon b/pkg/arvo/tests/sys/vane/ames.hoon index ea7563ea7e..f6be625e77 100644 --- a/pkg/arvo/tests/sys/vane/ames.hoon +++ b/pkg/arvo/tests/sys/vane/ames.hoon @@ -205,7 +205,7 @@ :: %+ expect-eq !> [~[/g/talk] %give %boon [%post 'first1!!']] - !> (snag 1 `(list move:ames)`moves6) + !> (snag 0 `(list move:ames)`moves6) == :: ++ test-nack ^- tang From 9fb37543ecfb0294f4acafd2570623495c8beab6 Mon Sep 17 00:00:00 2001 From: Ted Blackman Date: Wed, 18 Dec 2019 00:25:27 -0500 Subject: [PATCH 12/39] ford: clear build results on +load --- bin/brass.pill | 4 ++-- bin/solid.pill | 4 ++-- pkg/arvo/sys/vane/ford.hoon | 16 ++++++++-------- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/bin/brass.pill b/bin/brass.pill index 212ade9876..7221c67262 100644 --- a/bin/brass.pill +++ b/bin/brass.pill @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:18d492d912068e7fefef48006105d39c1c8f56aa756b7aeae48387c2254c1b91 -size 7153239 +oid sha256:407a763f44eb91db0dd4a1ec2dbd12ed4332b48decefd3999c4313844daa2c0b +size 7226043 diff --git a/bin/solid.pill b/bin/solid.pill index a3e9dd86b4..2a2d86b428 100644 --- a/bin/solid.pill +++ b/bin/solid.pill @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:04735cc4764f9a3e6c4fb5b046a6b9590664fe9f644578c58f3bc6acc911b723 -size 9606039 +oid sha256:a1eefcccc270aa4fce80bfe2b8a3edc74b1958326ac4df988b6f1fe47590bcec +size 9609018 diff --git a/pkg/arvo/sys/vane/ford.hoon b/pkg/arvo/sys/vane/ford.hoon index 6825f3d92a..8f1920ec90 100644 --- a/pkg/arvo/sys/vane/ford.hoon +++ b/pkg/arvo/sys/vane/ford.hoon @@ -6108,6 +6108,7 @@ :: and a namespace function :: |= [our=ship now=@da eny=@uvJ scry-gate=sley] +=* ford-gate . :: allow jets to be registered within this core :: ~% %ford ..is ~ @@ -6332,12 +6333,16 @@ -- :: +load: migrate old state to new state (called on vane reload) :: +:: Trim builds completely in case a change to our code invalidated an +:: old build result. +:: ++ load |= old=axle - ^+ ..^$ + ^+ ford-gate :: - ~! %loading - ..^$(ax old) + =. ax old + =. ford-gate +:(call ~[/ford-load-self] *type %trim 0) + ford-gate :: +stay: produce current state :: ++ stay `axle`ax @@ -6346,9 +6351,4 @@ ++ scry |= * [~ ~] -:: %utilities -:: -::+| -:: -++ ford-gate ..$ -- From e2cfcafc925a3e8258018b5516f3fa9d164d8913 Mon Sep 17 00:00:00 2001 From: Matilde Park Date: Wed, 18 Dec 2019 00:10:22 -0500 Subject: [PATCH 13/39] chat: validate chat names Add a regex check for non-lowercase, non-slash, non-hyphen characters, or numerals, and a quick check for starts-with-slashes, double-slashes, to a boolean before creating a chat at that path. --- pkg/arvo/app/chat/js/index.js | 2 +- pkg/arvo/app/chat/js/tile.js | 2 +- pkg/interface/chat/src/js/components/new.js | 12 ++++++++++-- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/pkg/arvo/app/chat/js/index.js b/pkg/arvo/app/chat/js/index.js index f2f093ad58..408a9e37c2 100644 --- a/pkg/arvo/app/chat/js/index.js +++ b/pkg/arvo/app/chat/js/index.js @@ -1 +1 @@ -!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(require("buffer")):"function"==typeof define&&define.amd?define("index",["buffer"],t):t((e=e||self).buffer)}(this,function(e){"use strict";e=e&&e.hasOwnProperty("default")?e.default:e;var t="undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{},n=t.performance||{},r=(n.now||n.mozNow||n.msNow||n.oNow||n.webkitNow,"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{});function i(){throw new Error("Dynamic requires are not currently supported by rollup-plugin-commonjs")}function a(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}function l(e,t){return e(t={exports:{}},t.exports),t.exports}var o=Object.getOwnPropertySymbols,u=Object.prototype.hasOwnProperty,s=Object.prototype.propertyIsEnumerable;var c=function(){try{if(!Object.assign)return!1;var e=new String("abc");if(e[5]="de","5"===Object.getOwnPropertyNames(e)[0])return!1;for(var t={},n=0;n<10;n++)t["_"+String.fromCharCode(n)]=n;if("0123456789"!==Object.getOwnPropertyNames(t).map(function(e){return t[e]}).join(""))return!1;var r={};return"abcdefghijklmnopqrst".split("").forEach(function(e){r[e]=e}),"abcdefghijklmnopqrst"===Object.keys(Object.assign({},r)).join("")}catch(e){return!1}}()?Object.assign:function(e,t){for(var n,r,i=function(e){if(null==e)throw new TypeError("Object.assign cannot be called with null or undefined");return Object(e)}(e),a=1;a2?n-2:0),i=2;i1?t-1:0),r=1;r2?n-2:0),i=2;i8)throw new Error("warningWithoutStack() currently supports at most 8 arguments.");if(!e){if("undefined"!=typeof console){var a=r.map(function(e){return""+e});a.unshift("Warning: "+t),Function.prototype.apply.call(console.error,console,a)}try{var l=0,o="Warning: "+t.replace(/%s/g,function(){return r[l++]});throw new Error(o)}catch(e){}}},T={};function N(e,t){var n=e.constructor,r=n&&(n.displayName||n.name)||"ReactClass",i=r+"."+t;T[i]||(S(!1,"Can't call %s on a component that is not yet mounted. This is a no-op, but it might indicate a bug in your application. Instead, assign to `this.state` directly or define a `state = {};` class property with the desired state in the %s component.",t,r),T[i]=!0)}var P={isMounted:function(e){return!1},enqueueForceUpdate:function(e,t,n){N(e,"forceUpdate")},enqueueReplaceState:function(e,t,n,r){N(e,"replaceState")},enqueueSetState:function(e,t,n,r){N(e,"setState")}},L={};function R(e,t,n){this.props=e,this.context=t,this.refs=L,this.updater=n||P}Object.freeze(L),R.prototype.isReactComponent={},R.prototype.setState=function(e,t){!function(){if("object"!=typeof e&&"function"!=typeof e&&null!=e)throw M(Error("setState(...): takes an object of state variables to update or a function which returns an object of state variables."))}(),this.updater.enqueueSetState(this,e,t,"setState")},R.prototype.forceUpdate=function(e){this.updater.enqueueForceUpdate(this,e,"forceUpdate")};var O={isMounted:["isMounted","Instead, make sure to clean up subscriptions and pending requests in componentWillUnmount to prevent memory leaks."],replaceState:["replaceState","Refactor your code to use setState instead (see https://github.com/facebook/react/issues/3236)."]},D=function(e,t){Object.defineProperty(R.prototype,e,{get:function(){E(!1,"%s(...) is deprecated in plain JavaScript React classes. %s",t[0],t[1])}})};for(var A in O)O.hasOwnProperty(A)&&D(A,O[A]);function I(){}function U(e,t,n){this.props=e,this.context=t,this.refs=L,this.updater=n||P}I.prototype=R.prototype;var j=U.prototype=new I;j.constructor=U,t(j,R.prototype),j.isPureReactComponent=!0;var z={current:null},H={suspense:null},F={current:null},Z=/^(.*)[\\\/]/,V=1;function W(e){if(null==e)return null;if("number"==typeof e.tag&&S(!1,"Received an unexpected object in getComponentName(). This is likely a bug in React. Please file an issue."),"function"==typeof e)return e.displayName||e.name||null;if("string"==typeof e)return e;switch(e){case l:return"Fragment";case a:return"Portal";case u:return"Profiler";case o:return"StrictMode";case p:return"Suspense";case m:return"SuspenseList"}if("object"==typeof e)switch(e.$$typeof){case f:return"Context.Consumer";case s:return"Context.Provider";case h:return r=e,i=e.render,c="ForwardRef",d=i.displayName||i.name||"",r.displayName||(""!==d?c+"("+d+")":c);case v:return W(e.type);case b:var t=(n=e)._status===V?n._result:null;if(t)return W(t)}var n,r,i,c,d;return null}var q={},Y=null;function B(e){Y=e}q.getCurrentStack=null,q.getStackAddendum=function(){var e="";if(Y){var t=W(Y.type),n=Y._owner;e+=function(e,t,n){var r="";if(t){var i=t.fileName,a=i.replace(Z,"");if(/^index\./.test(a)){var l=i.match(Z);if(l){var o=l[1];o&&(a=o.replace(Z,"")+"/"+a)}}r=" (at "+a+":"+t.lineNumber+")"}else n&&(r=" (created by "+n+")");return"\n in "+(e||"Unknown")+r}(t,Y._source,n&&W(n.type))}var r=q.getCurrentStack;return r&&(e+=r()||""),e};var $={ReactCurrentDispatcher:z,ReactCurrentBatchConfig:H,ReactCurrentOwner:F,IsSomeRendererActing:{current:!1},assign:t};t($,{ReactDebugCurrentFrame:q,ReactComponentTreeHook:{}});var Q,G,K=function(e,t){if(!e){for(var n=$.ReactDebugCurrentFrame.getStackAddendum(),r=arguments.length,i=new Array(r>2?r-2:0),a=2;a1){for(var c=Array(s),f=0;f.")}return t}(t);if(!xe[n]){xe[n]=!0;var r="";e&&e._owner&&e._owner!==F.current&&(r=" It was passed a child from "+W(e._owner.type)+"."),B(e),K(!1,'Each child in a list should have a unique "key" prop.%s%s See https://fb.me/react-warning-keys for more information.',n,r),B(null)}}}function Me(e,t){if("object"==typeof e)if(Array.isArray(e))for(var n=0;n",u=" Did you accidentally export a JSX literal instead of a component?"):s=typeof e,K(!1,"React.createElement: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: %s.%s",s,u)}var f=re.apply(this,arguments);if(null==f)return f;if(o)for(var d=2;d is not supported and will be removed in a future major release. Did you mean to render instead?")),n.Provider},set:function(e){n.Provider=e}},_currentValue:{get:function(){return n._currentValue},set:function(e){n._currentValue=e}},_currentValue2:{get:function(){return n._currentValue2},set:function(e){n._currentValue2=e}},_threadCount:{get:function(){return n._threadCount},set:function(e){n._threadCount=e}},Consumer:{get:function(){return r||(r=!0,K(!1,"Rendering is not supported and will be removed in a future major release. Did you mean to render instead?")),n.Consumer}}}),n.Consumer=a,n._currentRenderer=null,n._currentRenderer2=null,n},forwardRef:function(e){return null!=e&&e.$$typeof===v?S(!1,"forwardRef requires a render function but received a `memo` component. Instead of forwardRef(memo(...)), use memo(forwardRef(...))."):"function"!=typeof e?S(!1,"forwardRef requires a render function but was given %s.",null===e?"null":typeof e):0!==e.length&&2!==e.length&&S(!1,"forwardRef render functions accept exactly two parameters: props and ref. %s",1===e.length?"Did you forget to use the ref parameter?":"Any additional parameter will be undefined."),null!=e&&(null!=e.defaultProps||null!=e.propTypes)&&S(!1,"forwardRef render functions do not support propTypes or defaultProps. Did you accidentally pass a React component?"),{$$typeof:h,render:e}},lazy:function(e){var t,n,r={$$typeof:b,_ctor:e,_status:-1,_result:null};return Object.defineProperties(r,{defaultProps:{configurable:!0,get:function(){return t},set:function(e){K(!1,"React.lazy(...): It is not supported to assign `defaultProps` to a lazy component import. Either specify them where the component is defined, or create a wrapping component around it."),t=e,Object.defineProperty(r,"defaultProps",{enumerable:!0})}},propTypes:{configurable:!0,get:function(){return n},set:function(e){K(!1,"React.lazy(...): It is not supported to assign `propTypes` to a lazy component import. Either specify them where the component is defined, or create a wrapping component around it."),n=e,Object.defineProperty(r,"propTypes",{enumerable:!0})}}}),r},memo:function(e,t){return ke(e)||S(!1,"memo: The first argument must be a component. Instead received: %s",null===e?"null":typeof e),{$$typeof:v,type:e,compare:void 0===t?null:t}},useCallback:function(e,t){return we().useCallback(e,t)},useContext:function(e,t){var n=we();if(void 0!==t&&K(!1,"useContext() second argument is reserved for future use in React. Passing it is not supported. You passed: %s.%s",t,"number"==typeof t&&Array.isArray(arguments[2])?"\n\nDid you call array.map(useContext)? Calling Hooks inside a loop is not supported. Learn more at https://fb.me/rules-of-hooks":""),void 0!==e._context){var r=e._context;r.Consumer===e?K(!1,"Calling useContext(Context.Consumer) is not supported, may cause bugs, and will be removed in a future major release. Did you mean to call useContext(Context) instead?"):r.Provider===e&&K(!1,"Calling useContext(Context.Provider) is not supported. Did you mean to call useContext(Context) instead?")}return n.useContext(e,t)},useEffect:function(e,t){return we().useEffect(e,t)},useImperativeHandle:function(e,t,n){return we().useImperativeHandle(e,t,n)},useDebugValue:function(e,t){return we().useDebugValue(e,t)},useLayoutEffect:function(e,t){return we().useLayoutEffect(e,t)},useMemo:function(e,t){return we().useMemo(e,t)},useReducer:function(e,t,n){return we().useReducer(e,t,n)},useRef:function(e){return we().useRef(e)},useState:function(e){return we().useState(e)},Fragment:l,Profiler:u,StrictMode:o,Suspense:p,unstable_SuspenseList:m,createElement:Se,cloneElement:function(e,n,r){for(var i=function(e,n,r){var i;!function(){if(null==e)throw M(Error("React.cloneElement(...): The argument must be a React element, but you passed "+e+"."))}();var a,l=t({},e.props),o=e.key,u=e.ref,s=e._self,c=e._source,f=e._owner;if(null!=n)for(i in ee(n)&&(u=n.ref,f=F.current),te(n)&&(o=""+n.key),e.type&&e.type.defaultProps&&(a=e.type.defaultProps),n)J.call(n,i)&&!X.hasOwnProperty(i)&&(void 0===n[i]&&void 0!==a?l[i]=a[i]:l[i]=n[i]);var d=arguments.length-2;if(1===d)l.children=r;else if(d>1){for(var h=Array(d),p=0;p=_},l=function(){},t.unstable_forceFrameRate=function(e){0>e||125T(l,n))void 0!==u&&0>T(u,l)?(e[r]=u,e[o]=n,r=o):(e[r]=l,e[a]=n,r=a);else{if(!(void 0!==u&&0>T(u,n)))break e;e[r]=u,e[o]=n,r=o}}}return t}return null}function T(e,t){var n=e.sortIndex-t.sortIndex;return 0!==n?n:e.id-t.id}var N=[],P=[],L=1,R=null,O=3,D=!1,A=!1,I=!1;function U(e){for(var t=E(P);null!==t;){if(null===t.callback)S(P);else{if(!(t.startTime<=e))break;S(P),t.sortIndex=t.expirationTime,M(N,t)}t=E(P)}}function j(e){if(I=!1,U(e),!A)if(null!==E(N))A=!0,n(z);else{var t=E(P);null!==t&&r(j,t.startTime-e)}}function z(e,n){A=!1,I&&(I=!1,i()),D=!0;var l=O;try{for(U(n),R=E(N);null!==R&&(!(R.expirationTime>n)||e&&!a());){var o=R.callback;if(null!==o){R.callback=null,O=R.priorityLevel;var u=o(R.expirationTime<=n);n=t.unstable_now(),"function"==typeof u?R.callback=u:R===E(N)&&S(N),U(n)}else S(N);R=E(N)}if(null!==R)var s=!0;else{var c=E(P);null!==c&&r(j,c.startTime-n),s=!1}return s}finally{R=null,O=l,D=!1}}function H(e){switch(e){case 1:return-1;case 2:return 250;case 5:return 1073741823;case 4:return 1e4;default:return 5e3}}var F=l;t.unstable_ImmediatePriority=1,t.unstable_UserBlockingPriority=2,t.unstable_NormalPriority=3,t.unstable_IdlePriority=5,t.unstable_LowPriority=4,t.unstable_runWithPriority=function(e,t){switch(e){case 1:case 2:case 3:case 4:case 5:break;default:e=3}var n=O;O=e;try{return t()}finally{O=n}},t.unstable_next=function(e){switch(O){case 1:case 2:case 3:var t=3;break;default:t=O}var n=O;O=t;try{return e()}finally{O=n}},t.unstable_scheduleCallback=function(e,a,l){var o=t.unstable_now();if("object"==typeof l&&null!==l){var u=l.delay;u="number"==typeof u&&0o?(e.sortIndex=u,M(P,e),null===E(N)&&e===E(P)&&(I?i():I=!0,r(j,u-o))):(e.sortIndex=l,M(N,e),A||D||(A=!0,n(z))),e},t.unstable_cancelCallback=function(e){e.callback=null},t.unstable_wrapCallback=function(e){var t=O;return function(){var n=O;O=t;try{return e.apply(this,arguments)}finally{O=n}}},t.unstable_getCurrentPriorityLevel=function(){return O},t.unstable_shouldYield=function(){var e=t.unstable_now();U(e);var n=E(N);return n!==R&&null!==R&&null!==n&&null!==n.callback&&n.startTime<=e&&n.expirationTime=x},a=function(){},t.unstable_forceFrameRate=function(e){e<0||e>125?console.error("forceFrameRate takes a positive int between 0 and 125, forcing framerates higher than 125 fps is not unsupported"):_=e>0?Math.floor(1e3/e):33.33};var C=new MessageChannel,M=C.port2;C.port1.onmessage=function(){if(null!==k){var e=t.unstable_now();x=e+_;try{k(!0,e)?M.postMessage(null):(g=!1,k=null)}catch(e){throw M.postMessage(null),e}}else g=!1},e=function(e){k=e,g||(g=!0,M.postMessage(null))},n=function(e,n){w=p(function(){e(t.unstable_now())},n)},r=function(){m(w),w=-1}}function E(e,t){var n=e.length;e.push(t),function(e,t,n){var r=n;for(;;){var i=Math.floor((r-1)/2),a=e[i];if(!(void 0!==a&&N(a,t)>0))return;e[i]=t,e[r]=a,r=i}}(e,t,n)}function S(e){var t=e[0];return void 0===t?null:t}function T(e){var t=e[0];if(void 0!==t){var n=e.pop();return n!==t&&(e[0]=n,function(e,t,n){var r=n,i=e.length;for(;rY){if((Y*=2)>q)return console.error("Scheduler Profiling: Event log exceeded maximum size. Don't forget to call `stopLoggingProfilingEvents()`."),void ae();var n=new Int32Array(4*Y);n.set($),B=n.buffer,$=n}$.set(e,t)}}function ae(){var e=B;return Y=0,B=null,$=null,Q=0,e}function le(e,t){z[V]++,null!==$&&ie([G,t,e.id,e.priorityLevel])}function oe(e,t){z[H]=P,z[F]=0,z[V]--,null!==$&&ie([K,t,e.id])}function ue(e,t){z[H]=P,z[F]=0,z[Z]=0,null!==$&&ie([te,t,e.id,I])}var se=-1,ce=250,fe=5e3,de=1e4,he=1073741823,pe=[],me=[],ve=1,be=null,ye=O,ge=!1,ke=!1,we=!1;function _e(e){for(var t=S(me);null!==t;){if(null===t.callback)T(me);else{if(!(t.startTime<=e))return;T(me),t.sortIndex=t.expirationTime,E(pe,t),le(t,e),t.isQueued=!0}t=S(me)}}function xe(t){if(we=!1,_e(t),!ke)if(null!==S(pe))ke=!0,e(Ce);else{var r=S(me);null!==r&&n(xe,r.startTime-t)}}function Ce(e,n){var i;i=n,null!==$&&ie([re,i,U]),ke=!1,we&&(we=!1,r()),ge=!0;var a=ye;try{if(!o)return Me(e,n);try{return Me(e,n)}catch(e){if(null!==be){var l=t.unstable_now();!function(e,t){z[H]=P,z[F]=0,z[V]--,null!==$&&ie([J,t,e.id])}(be,l),be.isQueued=!1}throw e}}finally{be=null,ye=a,ge=!1,function(e){U++,null!==$&&ie([ne,e,U])}(t.unstable_now())}}function Me(e,r){var a,o,u=r;for(_e(u),be=S(pe);null!==be&&!l&&(!(be.expirationTime>u)||e&&!i());){var s=be.callback;if(null!==s){be.callback=null,ye=be.priorityLevel;var c=be.expirationTime<=u;a=be,o=u,I++,z[H]=a.priorityLevel,z[F]=a.id,z[Z]=I,null!==$&&ie([ee,o,a.id,I]);var f=s(c);u=t.unstable_now(),"function"==typeof f?(be.callback=f,ue(be,u)):(oe(be,u),be.isQueued=!1,be===S(pe)&&T(pe)),_e(u)}else T(pe);be=S(pe)}if(null!==be)return!0;var d=S(me);return null!==d&&n(xe,d.startTime-u),!1}function Ee(e){switch(e){case L:return se;case R:return ce;case A:return he;case D:return de;case O:default:return fe}}var Se=a,Te={startLoggingProfilingEvents:function(){Y=W,B=new ArrayBuffer(4*Y),$=new Int32Array(B),Q=0},stopLoggingProfilingEvents:ae,sharedProfilingBuffer:j};t.unstable_ImmediatePriority=L,t.unstable_UserBlockingPriority=R,t.unstable_NormalPriority=O,t.unstable_IdlePriority=A,t.unstable_LowPriority=D,t.unstable_runWithPriority=function(e,t){switch(e){case L:case R:case O:case D:case A:break;default:e=O}var n=ye;ye=e;try{return t()}finally{ye=n}},t.unstable_next=function(e){var t;switch(ye){case L:case R:case O:t=O;break;default:t=ye}var n=ye;ye=t;try{return e()}finally{ye=n}},t.unstable_scheduleCallback=function(i,a,l){var o,u,s=t.unstable_now();if("object"==typeof l&&null!==l){var c=l.delay;o="number"==typeof c&&c>0?s+c:s,u="number"==typeof l.timeout?l.timeout:Ee(i)}else u=Ee(i),o=s;var f=o+u,d={id:ve++,callback:a,priorityLevel:i,startTime:o,expirationTime:f,sortIndex:-1,isQueued:!1};return o>s?(d.sortIndex=o,E(me,d),null===S(pe)&&d===S(me)&&(we?r():we=!0,n(xe,o-s))):(d.sortIndex=f,E(pe,d),le(d,s),d.isQueued=!0,ke||ge||(ke=!0,e(Ce))),d},t.unstable_cancelCallback=function(e){e.isQueued&&(function(e,t){z[V]--,null!==$&&ie([X,t,e.id])}(e,t.unstable_now()),e.isQueued=!1),e.callback=null},t.unstable_wrapCallback=function(e){var t=ye;return function(){var n=ye;ye=t;try{return e.apply(this,arguments)}finally{ye=n}}},t.unstable_getCurrentPriorityLevel=function(){return ye},t.unstable_shouldYield=function(){var e=t.unstable_now();_e(e);var n=S(pe);return n!==be&&null!==be&&null!==n&&null!==n.callback&&n.startTime<=e&&n.expirationTime