From a8436074a6478fde2b5945938e4d1b122e7be940 Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Wed, 4 Jan 2023 08:22:41 -0500 Subject: [PATCH] Store mapping from hashes for Ethereum transactions to Filecoin Message Cids --- .circleci/config.yml | 5 + api/api_full.go | 1 + api/mocks/mock_full.go | 15 + api/proxy_gen.go | 13 + build/openrpc/full.json.gz | Bin 32992 -> 33062 bytes build/openrpc/gateway.json.gz | Bin 8495 -> 8497 bytes build/openrpc/miner.json.gz | Bin 16049 -> 16050 bytes build/openrpc/worker.json.gz | Bin 5225 -> 5224 bytes chain/eth_transaction_hash_lookup.go | 162 ++++++++++ chain/events/filter/mempool.go | 7 +- chain/types/ethtypes/eth_transactions.go | 30 ++ chain/types/ethtypes/eth_types.go | 13 +- documentation/en/api-v1-unstable-methods.md | 17 + documentation/en/default-lotus-config.toml | 8 + extern/filecoin-ffi | 2 +- itests/eth_deploy_test.go | 1 + itests/eth_filter_test.go | 6 +- itests/eth_hash_lookup_test.go | 340 ++++++++++++++++++++ itests/kit/node_opts.go | 7 + node/builder_chain.go | 3 +- node/config/def.go | 3 + node/config/doc_gen.go | 14 + node/config/types.go | 18 +- node/impl/full/eth.go | 237 ++++++++++---- node/modules/ethmodule.go | 84 +++++ node/repo/fsrepo.go | 20 ++ node/repo/interface.go | 3 + node/repo/memrepo.go | 8 + 28 files changed, 943 insertions(+), 74 deletions(-) create mode 100644 chain/eth_transaction_hash_lookup.go create mode 100644 itests/eth_hash_lookup_test.go create mode 100644 node/modules/ethmodule.go diff --git a/.circleci/config.yml b/.circleci/config.yml index d3c160cd5..db625b2be 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -740,6 +740,11 @@ workflows: suite: itest-eth_filter target: "./itests/eth_filter_test.go" + - test: + name: test-itest-eth_hash_lookup + suite: itest-eth_hash_lookup + target: "./itests/eth_hash_lookup_test.go" + - test: name: test-itest-eth_transactions suite: itest-eth_transactions diff --git a/api/api_full.go b/api/api_full.go index 41fbe6b3e..c2a494dcf 100644 --- a/api/api_full.go +++ b/api/api_full.go @@ -778,6 +778,7 @@ type FullNode interface { EthGetBlockByHash(ctx context.Context, blkHash ethtypes.EthHash, fullTxInfo bool) (ethtypes.EthBlock, error) //perm:read EthGetBlockByNumber(ctx context.Context, blkNum string, fullTxInfo bool) (ethtypes.EthBlock, error) //perm:read EthGetTransactionByHash(ctx context.Context, txHash *ethtypes.EthHash) (*ethtypes.EthTx, error) //perm:read + EthGetTransactionHashByCid(ctx context.Context, cid cid.Cid) (*ethtypes.EthHash, error) //perm:read EthGetTransactionCount(ctx context.Context, sender ethtypes.EthAddress, blkOpt string) (ethtypes.EthUint64, error) //perm:read EthGetTransactionReceipt(ctx context.Context, txHash ethtypes.EthHash) (*EthTxReceipt, error) //perm:read EthGetTransactionByBlockHashAndIndex(ctx context.Context, blkHash ethtypes.EthHash, txIndex ethtypes.EthUint64) (ethtypes.EthTx, error) //perm:read diff --git a/api/mocks/mock_full.go b/api/mocks/mock_full.go index 10ff250e6..18eb15bae 100644 --- a/api/mocks/mock_full.go +++ b/api/mocks/mock_full.go @@ -1252,6 +1252,21 @@ func (mr *MockFullNodeMockRecorder) EthGetTransactionCount(arg0, arg1, arg2 inte return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EthGetTransactionCount", reflect.TypeOf((*MockFullNode)(nil).EthGetTransactionCount), arg0, arg1, arg2) } +// EthGetTransactionHashByCid mocks base method. +func (m *MockFullNode) EthGetTransactionHashByCid(arg0 context.Context, arg1 cid.Cid) (*ethtypes.EthHash, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "EthGetTransactionHashByCid", arg0, arg1) + ret0, _ := ret[0].(*ethtypes.EthHash) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// EthGetTransactionHashByCid indicates an expected call of EthGetTransactionHashByCid. +func (mr *MockFullNodeMockRecorder) EthGetTransactionHashByCid(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EthGetTransactionHashByCid", reflect.TypeOf((*MockFullNode)(nil).EthGetTransactionHashByCid), arg0, arg1) +} + // EthGetTransactionReceipt mocks base method. func (m *MockFullNode) EthGetTransactionReceipt(arg0 context.Context, arg1 ethtypes.EthHash) (*api.EthTxReceipt, error) { m.ctrl.T.Helper() diff --git a/api/proxy_gen.go b/api/proxy_gen.go index 67249bb5f..d1df2a215 100644 --- a/api/proxy_gen.go +++ b/api/proxy_gen.go @@ -263,6 +263,8 @@ type FullNodeStruct struct { EthGetTransactionCount func(p0 context.Context, p1 ethtypes.EthAddress, p2 string) (ethtypes.EthUint64, error) `perm:"read"` + EthGetTransactionHashByCid func(p0 context.Context, p1 cid.Cid) (*ethtypes.EthHash, error) `perm:"read"` + EthGetTransactionReceipt func(p0 context.Context, p1 ethtypes.EthHash) (*EthTxReceipt, error) `perm:"read"` EthMaxPriorityFeePerGas func(p0 context.Context) (ethtypes.EthBigInt, error) `perm:"read"` @@ -2172,6 +2174,17 @@ func (s *FullNodeStub) EthGetTransactionCount(p0 context.Context, p1 ethtypes.Et return *new(ethtypes.EthUint64), ErrNotSupported } +func (s *FullNodeStruct) EthGetTransactionHashByCid(p0 context.Context, p1 cid.Cid) (*ethtypes.EthHash, error) { + if s.Internal.EthGetTransactionHashByCid == nil { + return nil, ErrNotSupported + } + return s.Internal.EthGetTransactionHashByCid(p0, p1) +} + +func (s *FullNodeStub) EthGetTransactionHashByCid(p0 context.Context, p1 cid.Cid) (*ethtypes.EthHash, error) { + return nil, ErrNotSupported +} + func (s *FullNodeStruct) EthGetTransactionReceipt(p0 context.Context, p1 ethtypes.EthHash) (*EthTxReceipt, error) { if s.Internal.EthGetTransactionReceipt == nil { return nil, ErrNotSupported diff --git a/build/openrpc/full.json.gz b/build/openrpc/full.json.gz index 6b4fc7b4f84dbe70c394294d61c1448f80608da6..b8192f95cfd6868571699ba40cf2f5408b5cc4c3 100644 GIT binary patch delta 32933 zcmZ6Sb8sL**Y1OjZQHhOC!1trn;SdXOl&(FCmU;H+qP}nzWaUeeXDNW|4w(+RL!ZG ze)>75f4%JwRuljhhX;5)qr5FHGIS+8Mr0dCNLL)Ai}IJ{<+<--|2Z0 zBbHdHjasS1iTs9(fPDPvS}^mxlE+AzZR3i_`%&4mXji<}V=B)FCBz5M2K8_5C<#Ka zxt$v_6w4yZ?g-ISutgtM@5i|6^X4iMXp()bliV5%(N7*mX# zNEWKyBv?SmyQkp7$9ch!)W&h>K$gx5r)M zGPxW~T@haccdCgpaR#N%dJ7nDu@5PH-5NKq0kJhqdNHb(6i?7ox8?PN_1T%6VdE@= zu3oCGJ}DvJo5>FhL!=cW3cMD=TH-yW!Recs>Hv!kM6bB5t03)w43{Y(Lrw(AZZWn| zWC!LJ;KGH^|HuWAS9MVLYrAQSGJiNl!lEmBVb$ZzE9vebqi<=&o-8!RH$x&9)&y4ch*Xt{frO(n z;|5b4hxj3^P$Xi|>RchJ#rc9D$WI$)iD@HM!0-mw54FaUUU82rMSZc<_(GX}45z(A0A{$J)M`y|E#Y)%1J$e=@j z1yuv3A^WMF5LdUg<*1nlG)BJ%D&(ZCI5wfl{(&Wd37&_~<4YJbk?=N|#k+b6&W)NW z4eoTK!2a7XBzJxz#3XjN;C7^z8+L#YIsOS4bWCY9{q?DDL?umk|DaFE9jCc}-`|0) zPS-rw$nz46PA`3MX&7u~@5S(*s~#DD^X<{aT`;Imj}!N` z$@1`1hUn8NkSD7Jh%q-|Cqil(^1vQ<4+Qy%blJ$KTM#U#RwgwuU`^iV2{iLn#K99G zqt$k%8$#PKCBap1UV}a~iJ!zmuMX>k(TMF1^adCfb6U#(Z4)*W{~2n?(i;eLyT=b; z{m?g`pVOAKpzDM-r!Qx;4LR+<=aTnXg-?k&`@lFE7;FhD35Kay>N<#du#tyc{la zoAY0RV>dwZ+pR9(*%Ob1(G`wW8sHSFCx1cM8A?($8qOzqmzGheemoe7*NEb}(rOvkV9nDG7fI zhj1_0)JT^J^5!J4?Xz(^AKW7+zQZcHly?;qGAZWvrREWngkC_u9s>fRyNNefnzos= zdZ$GdE4EzLKaw*)&&T7U2Z+ezbGd{lFdHBOvrz75s<0W%Q0ZWLLq+++B$#2$$!ZqW zWFGi9v&o!!n+|>5n8nz>SXwk}+Hu}aLGSswu{LDn1CN;Bc|5fOPc(mXn)0ViOr%X! zWNB~tPvH%12gf74emaFvuR-+U90@g4c)ZJwHC*z)!0XCb;r<}`L zO9oXedq^iiB9cDu$OTgY7nTF=up_5n7tW01^fPf#S#V-gw{ zT`Cv6yH#Z7^Vcv}Q1h>A|3LfrzG%DQ{>ur3aP9V?7;s7GW&bm17IWv7CnO@_q2lhs z&R8kL-GIM-{D$cAI|QvHoP=Sxg-d$XX z-NUq047L5bw}SEP;`MPe2Gjidd~o&x^L+dAZm@lf?$g050D9G>8-P^2v}BPZzn_hqgp)h} za&vU``X$!+O05k`vvKb3|ARSS!&+M( z(u4!EDu5&y-tji+@4^};lg<^;mbF)x*p48C+rs&K-BYanr!vt--N|~+Isdv~GY~zp z?Q8qd!PkZ-Z*iZp5qlo0^ZfV1-S;N1Lo~%ZARh zG~ghF`CY7da3Tc)`;?Zt3zE_iUZOe4 z0o6n%uKty#$cf=AA--a^aqL@Os1N<>r3-TR!7V4`ziUSIf`<|AijrcqJKUJCn&Pg6l>?}7SYx^I;;h5KvDMh z4kJVtm=NDIdgzvxJ4CftMU-t85D1(#fl9dE#>7#>{A`)f%iEj2Qc#-6hw#nQ=F&e1 z5@18hctkKe{x;_q_GfIjXy%7d9 zPG8Pk*$O+iKRNlhpdhw=&67@$yO?seNvUVfF?^JoNh1+{J&eEDx!5$tMn+lzuA+&CANQxScBTnu zFG$`Z;nzXkhzN@|vp71$2#oBTfY`i9J!CUlju%OrcW!-aKI8Y`b8s&Aza=8oIOnUD zU*;o4W$(usXUHp;YG;I1lDKDhpB3^$!%E!Z=m0WXpO(aa6&xA}+H}o5-hqi{^qM8xh9u-Bu7}O`#p(UJomy{- zw2BnfTFRU=2@ZiksvC6q?^kUm+cK3ijK-QEMEQFq4Jb*U5Z-AxqNq|Vx_&SP53vZv z2EQ|!2&<9;Cd5ZECPWQZKrr`PhuJ*?g>?2ivaD;0`cU3tP3|6uq#Uu_4})w`6nT7; zHOqa3#o^$wB4MG_?nh-`=o#+la7F0M4e5__ScQXui$2 zTB`=ss1uGKzw(vZkur}YWMdV`)P@eK|DN!>gz`V)x#(FSKI5VuUHJ$j*TzpIPkm!S z;dBsdo{X516BS)S_tYasqJ#x?BL3Y)BK+BBc~AAcg-05I1Y#H(`hN2j61y^!NA2U` zBG&!Y`Rna*Vy)?`9H1@xsvVP)c)>%!*Kq}ZKWa1m!rxhboBIf?X-bR)NDlkNr*HXYt5zX!Bo0rR*mB270B;wv|z?j*5IR_=wGT* z%spq4_9m5jau|;*mbBzqNyz;{X}%3TcBrIhtsv*;CN`32=?fuBy}=_eoOLvocfSZl zX1M{ojDJ7`GZ!wN4|{X7(n?bLjV36hMJ{5@9j#Onn!^4CxgSAzTR4PY>pmoiM^ZFZ z)!Uf)BOa?Q79fq}$w){Qf=tt)Q;zSC!15$O!xm0viCY-HiB8z#>wtigJ~$?v&_UTH zNiUAuHd>=Y-U_!{h~GewD#u)x35WyvCIE$}_0zN=eql;Z{iz($(KpCa` zOg1YRRRfg{v(AbB40biF10to>T5BD951b#8SuqD`8UTOXt5kIsy%?~yy_Lzp(|T~I z!QN${Xx@21CZE!cgD*oI@)e)9qn1RjU*iUyJgJA8L)TDq3T#SsxePusD0&ZuS4=|f z!~gJwtn=1%tvK(6lkV&GRc?@%iAXijJ!EPvl0~;Yh$xH8fzVdwJiZ0SRx0xy3SpDn zOC8e%0sl>CMdZIFd&H41Urfj_=6l`?*VyA$|9-0(lxN)!X`8;Dicf(MHClFDxeY$W zV*<)Vq@kT39pdoDq_b83)Y%4dai~9kqVW6i*JzynM!14=!@MV8(L}5MMQRwmQ&pkU zm_R|Dq;Wy7J076ISI%Q!(3(CQBogt|2fv_LQ$v3sWeD34_2q4zqg_1U>ew_NnU zibFAe zl1?^#`jHL-nQjZoTDqRhZeTvaT!6~g(i5F?=2261y>$wI!$a95_*GYQCc7}l!$^+j z?MO4^=B;KFiOTp(hjf-gMnni#Tac$Wku7@vWQ3CThf#w0uU}~=Ltw)h#v}Ov8`t*P zyZ$GkDwD}iu91A3zjl0@Ts#c2kgQ&%MyHRwOjZ_FO$M0sM~33dEG`u$ycv4)sRSpl zk*ZSR#;I_PDs$p%E8izrv`KJTFLb7Bz@&sh7?JkHaIP@NAU}C;Ua z$s%Ly_#Jdhw2rP>h_outHib^7kW6sh0HZrtKSY?E046quLkC5hwuMu4)1T z*$q)=+KUHOLUkmtUNavM<3X~gi*pcQcFD$WvjjsFy^-kAqJ$_UU2B|92Bj6{l?C3S z_PLh(wI+3}1SEDfLhWi4mwS8$wd+9Xc_hWC4Zk9#zrJP0Zrtpc2|lE=0!VJwqo5<^ zb!V<~0(8S+>eCtTGbsa2`-2^GC_hddX}8NpI7LnT)enHNA7ffNpLJHBZ81*mFjk#03_ z#%cxTJxQSZM)u@w(C0TtT~fkq!8X49X`%k8`_!1txSLTnbL47F^fI9fUk-E2HFGh; zrR9sPPk*x4yZHMr|Mmi&NWz{McofP%rvxtKdE41x86VgK$i+B4|ZUk@b(wY;!lcU-ALi-RCAlFhp|cR z5($8W(`<4$jS~|YC3wJF-M%+JlHvT@24-N<+&bfGwd`%&xIBeh6?S)!4$UqvhU+1- zJcKdXF?O4$Npu}op$+AWq};sse4(I=c88!Ug(NDcf(N)seC&Hf_?at`uUl_d=hwMU zirzTJl-%v{!AW2weaLyD@FqhNljAvwU=gsSIeq!y_0hEZwbZfc|NPa}qII+y370h& zzJHggcza9aF-K2Za(N7-Gu(E)0!Bpw`a`Q^(;n`>ELte;Q?f3(ky+fUYAaNn@W+&DU-BH~R2>$){Q#i8k~ zo3IdQhpf$xz?-0c5+X-6A^_PQTt<3JTYp)bdZdmcL8e3Gm?}1VE7@the(K35nmNsA zN3O;+T!-=UE|6BRw+Q1`BT<&|vJHCa>bHkW$i zLIZ2o$@ro@HCHID_S$Ni9+F*^wj|M66^8-!Hm=bFNh{;e)cbQ8-3ZAeiPgc!ZMSKA znKt}Jx_#Qo`XlBrD=GBrR$fAICL)k928k(BpWdi4_*-O>i%fP_T1P;t z@w>VOaj+Qh2)1!~^_r}u`dk}%`J^dHIv_q8r1C>_3yuh_5?zUA6ru1(#Fs<>ZI4Ea z#bzX2a?7C(vSDc_)^N?aw*XOxJjO0|Io`6>5n09>d6pqrloP=XNO7{_7~@TMk;NDR{ZJwzi*4oS zS5itEDb_N>L8Ns)3}Ql_=PPEk~UIzxW+jq-Df;^4nFANzB zCb5&O7W)uh_DJ>REDPXg(Jte%m&V$BItADBzuqbCh`AmV&(=Gx^`%8J`|*w1*(j32miP6>lyA_&6I!0d?f$TE(-iAIu#FU4%{q=(w?W0Ci(GX42-Zh z1Tk?@wboV5_fOkq_HR^;>$-%S-ypV{gI#bFfk-l?SUoZALI$0qG?Mt$s{X_zB%Za7llvEpTf4I;2qWf4xu)OR2-V z$n1aKHx>p1XZV@8p)p9Vb8vL=?(d0H)i<#zu)c#2pV&nHap>N=&$f*ZYZ2;z_U|Sq zROPAR?bIRXkrrgJI@Y?v!&2JeW4(~t?2D-UOoM1;uJl}3@#Fhw14rSefZ&ce7vbXbDbV}QjjQ6sIK)wAYOY*PE0)zZ>bOawkuNr}j!$nVxbUx00{H0A@994^c zq(Kwgo2DypV=e*PJP|FnQOQ1Dz2zS|i)D&AE{^&bZ4&AgQ@MM@e4d7Z3C9U?8nDKO zBg0L2qy-SUp?*_fXPC>NyYmkD49thoKkam5SVkt_t{&-}YeEul0KEQ*k5-oOS_*=R z6`XrJl(mFgO>Z;j*$YOafr?9Q4ES}iaq%d{UP=@6DCED?Ia3uKd?XP=Hm?6t{rK70 z`{DfiWwf?7z0xxa3}nTHhi2sby2OLRS=fY$zag!IqaS`g`QfzV?`Lw#9Y?}+n_u#W ziBPqLZ9e77JB_;*H|w`*+oUlBhfEleFK?n}x&C40IM=KZBM-|dZe@C`fd?#u&_w;@ z3cK?S`ov}{`)p!UiPDafw|z+( zR4XMEfbX;4cRc@iA4#~Xb2R@3ACRW*2lS0N9AnkdrO-5VK||HyAG@@e&)p%Gk}w7! z`oho#^hVQL2s~d-9}lNrF^I9im)8v-6{=DEI7)EOjL!r89Ol@5 zFo%QM1nH(0AT(?TZLhFSj_vFzB=Jpi+R-BtDD^cqaL6@wm7Gp>)d9DP^#!kX{K$TK z3Z-a)QCzv7uC)}#{WB#$&oHJOx}1cR&$J5TjFO}y027f6a|%1WD9aXi*zR#-pcEh4 z-$)k3zd?e)0Xv!7mE+C7DQDDpC>y=x>+WUSjw#F`$D=BC2y?R3?`h;Y(L z4S%|gIQFP=Vq8Bt-%MN64~RePE~I7h8Vsmi!lf7o7!O#FCswwh&t+m&55n_};E@+Y zD;2>{*UXpc%CKEqM{E;F3VI_{)p!R3DM|?GR~DCa7?@fUlGFy1S%WoqHm%%lvWjk5 zwI?j}-{e20G#b0 ztdfzvUcPG(KWt|HroLZHZ%MgV=tk8|c_LhDRQ0t;Q@*B z3WC>yfx6(@qmyWrZE0SpDzK3SH~|GB_K+Po_m4@(6P5{7Gvsd+7Qnwp1Vo8yrb@W% z^QCuJdA-Pg;Kt(#lV?AFnw)qdVd&-|IYyL^Ju>8oGrWMH$77q8IFDI9Q_hIX~xXWO|;29+W^E_ec<^@w8wWv%RP zYjXbekz>#0hM6z$hvT>Tx5TCn8$}fMA5fEVpz^i~o_puV zr|`yqc4$$3hw9KD0j*c%{6Ow4hUBHLXj|qK`q%MM@tiOW$gHZ?nyx69LglYs=S{^| z=gaFQ_0liFpinZHvswd~coqhP$c&|?{r~LrM_Zry74NHo4`fhl*YR09H{9{9G)Qf! zl)7FvPj-IL`c%7ep5+ihv-Mgv^jdv)f)%S2L9~uJq)k*N1I19CMp0?cjQh@-VW^$p zpn|(gW2dYkeuJw({-ex3ma%YNW9IilU&qErHizV5)dEp;Q{@g|30z-hH9)zacesny zf7#hDOTBd{BiZSEbO_jhgnF+n60UC%o+G%sO_xb5ImDssy=$E1pPeK;9UN*mG^ls<3kQ;0d(lXMuS=no zuol@(tDK#H_a+v=FLOim^7c91xxv=)?>mey$WeyON_=;2&_sI!J@N?Ax&`I>ZNlrw zYWMI>fEj62RX7VkDSo;tFDRAxw2An9%lSely3d=yk)noir+b|JI@`xbw?^y3Dh5P9 z(F{_Um^(AUIRe*E3dX$nPG6uBaxkbp+SXt~ z#e9}&vPplp5juSyqo?%Rx#|#@@lv~0lVi@6HC37Kw3HUFEr%~2sv@>%&y<_&=p_WY zL%>s`l(2~<(%#cRF;q%tCnS=8^H342pYoub?p0;Yoc_bBdxYMsUYOLVa~zZs=U#)v z8yb`CgdYgJ?|b*gw$5C>7>(@oC)KRKzl*0xJ}3{kWkNR~YS5oZhIu_u#{?CxD{wTn z)J*)_Ky95(GVs>cmk$Ryx&EK2>T%93MkOXx?cAlCb?zciqcQt8EY*1ga7!n{lGcOd zY!dC|lUoysNF1di)wnslX_hpStxy1MKlI6sL;8=QF~aXb+_Xe$CDLKH(Yv~&>b!%j z7&G<J(lRhEfc@4^HF+i6061;5~;_r70nZB$bj1Esud=cCDTB zeGZQY%CC17`P>0ih6P)?mdD68RYYsX68WJr*1?!ks%mBP;WJ4~$PJQSKQSu7IG*@Y zGl=X^+ieqLl5OD}&{W>ZMFdX_imZ+&X0dLGO?iw+A9;MVaNb%HKf*YS>nn)Mnms=h zR976*YK?&SUi)JAaaW7twX1*p&0FB5p+(!;7O-@2h1B-(;p=YS)K<53VSnZJ*0*5? z09|Wa8z+dbd>fyCw+P@vH$EMT+d7!O&fVVx^v#&hAEDMZE=1fdLccDNAU8fA+b$it z+Stx7?Y}%4i;Pc1hb_RWsY`EUd_P~6?RDf(?I%u}t+MOg4`f_PjQ}S_sxcT1#qAmj zI{P};nlfG$MpdReV`s>8jsquk8UA>ColS*^pJW`-^Xw{jEix0n)%^bBQ2i-!scH$e zhKRZ?!`h0Pjr5eMsBtRPv?xby!`3%tMY}cS@n!N9{m1Yj9iN8NXD)pHb|F}!C6bz{uxLWuh!^YphTAN9c`A;Tdbkk~O1th|@b4q{du-2fLTK!PNLYkLgywheV{OMwOoHNnKFhpYEd=E6 z=7;8C@M9^f6oDO!B;t<~CG67l*4sIWQZ0k0A6hrJC2|zJD+Tf6je-x9;dVLPBfs>0 ztqFFKkz8aq7l;yuIykHpM85vA{xTv=9xa{aJy%MsOds`?m=>Ct+Y9(w_u%J!H?V-W z@oNU%g$%ZGOTy?_!7U{P(A`UAQTo=Q7cmM6uTx(Zmr&7lc`$R) z4vOg0br*ol9d6-$w-K%cZGfD|{ZBB3JAe7Zd}4%>Q+9J~WEp#z7fr(S$AC+npW_-Djf9Joggj;lukH#w@t;Yd>mhl$ESAi9te)AYFvB z8>NB4Vepw0N7X*Wmiae0^rvukEX{xMD_KI1p?~nz=>HF2efk^xU-&9Q?7H%P_iY7u zQ82A1n1U+k$_w7U-fSvvyCcEBteu(el3;sMdc97?8$TKUaAwCVc=2c!80<6b#QSFH z?a;LGvQXqedz*_*GzKY-bfrf5DO2$!m)_;->0bi9ARYQF*_`&HLz`83K`UB3im%qm z({fS$r+;!Lo2OB-XZc>6sg+)HK>r6|MYLPs!AvxyeA3|I={H3TXKYW2MM7AwX}LbX zKMrf|5029*M#Q=VXll`ydD2e^H4&22WGp2WjoNz0Xzp1YehJ3? zCc)vDT)2_w~tnDVTxZ`t2TeHg*&%_##eD)P4O#fUkjAm%@V6lgY3s^wpOfAK~t3sN;G~2 zqnmlexif~eb*tLnbG)*pn$E^YuiyC#r^5b~d=57kq*Ftk4k6{}`cYt_ID}I@ALuBI zJ2L{v+epGweeT26soOR)`O<%A*jwNYa+kcPnUuz!a5k74a^;q81Ll2ziNzNahMN+N zgR09>48Z`wT2Kl~*BqIRC0I?a(zn$xHkOF%V7gurPu}jS<4-UYyhIt}DQB7mP01>S zZJouzQ#DhGtCY}wd}JvuaT$4hee^;?@KlwdZc{P>?Igs$+j%D}LbLh&7}yYNQWREl zOgYfX?M?WFWXQ)u@4rp}5EBlsHzL2qI92Qv9QF(53eBlw#B@q^2F5Qz#%iuyn2ecy zEA5aK8B~J4IUC-x}l6oDeJl{>n;fov-i`x8Ekq*&iJ%zzdGNUJ$QTsnhS5 zb37p{F7DMFmCO9E?g-e`8>xv#nihJ%$rcIOBy)k^Sgj?M%5$NUoBULo{UDFo4{0F? z74A%QPy32FtnLBmUQ8VwB&$AP=O|?AQdU&vM+Q>e_qD`Mi;*a&p-h7OAT}O3>s81q zT4FJHO%WTRHYPbP@LdM~?JCV1CvKZ#hF`}|7U{8mSnp)=6tgF{cG-J4=2?NiZp0M+ z93KRSnscX>Lgn-q{>=*XZF0`zU3kHA^!n=L{jI&vQ zkj9{W*nL-v&QW~VbP_&mM33oE5nVC2pchy5tyFrF;7u!!Td*+r zxR#kDRTXErGw->G!x`@#0)%s)6W^36P}zOjBi4O;98? z1#PP={}@3F_$U%LAHN}Gk{3T^S9@k_7VaTKoIo@96fF~a69<1mM(WKVJq`Be>}$hi zI+#kOv!?I4^ias?$*f_>{Y@$*$77Y;3(L&6HnvTBOJOfsHTGl+H-CV@CkZIsAi#F; zde;{=y&JnKl;PVu4FW!1R9rv4$tkFZm?(D-by_dqw;~T#2*gea#B3F7a9+qXO+{ib zc4p8o64J8yBcw0zIJruziLfu)W0VRvJv$_ICn8C6!c!bqQX;AZ^UFyi95oRL>G{4%P_;)|zljgwXpk&+%W!!S zgTK&OK?J^4bl)KxP2zhP6|y{-67W!Fl%q#AwO(L!s5)V)Pmas&6aTAceIPk?XJt@b ziS=s^85u)R2eC@F&-#?i*1Y>K7%pLK;LcQP7L7knGa? zMd#j$9`y4%+f)dcT>LC+Xwq!XG}{cusxIR2!K=xHAzxnC;u&_-O-boj8IA=zFHQGBT0{HXjB;wqn)x;upOw-Kbrwn%FN5=&*$#sI+VipfgY7AMz%l*wdF<89`m z1O$d7lU93oTOvr*g-^pnG&i$Wh;K=>NDV>bEFQa3)HV76Dw|)@+yJlX!R&e zEw}>FSb%BY=HOVHR_I>g+PNx)dK(N3D~Sk#SODsR>h67wX$bn70`?*bDXifzjsBE`jaczY%9Nw{c-#1(ZE_PKSQ>h06l-TE^;MrT)HH) zg!0g8^yFaXa$2BLSWkjllqjGSHd(p?`I;lt%y9~f9Fy9V9ef__Htl;QrL;Hba0LrH zEAfzDWiGY3F00(^w6M6JU7wRE|piP2S zP1>XhcT>Tj`uTw(;3m7YL2y@e_LKs{9Z-_31U@Cou&HN;-l}{onTHo_hu-QKis$~U z?N@hEyN1L~%$-e2hq!1LG^RpMOPo&Yw4*F}Fy!&@G4w=qT9fNEKn(XDyiLDgOuguJs9O`XdG(0MjY9Z>BG3+~1CJCpm60@NTzK$Qpm#B3=c~FdvXM z^?eqBQb%RdLi-mzPtH|EOy(4;j*;4t_`86)>b49Rz$TssL(K+iHi+#@Irx|5C&D31 z+L0skl>W+tmM8)r^dXn$y)a_6c8K+JnucBs7K-MpN;^4SBy*_c^zPTAoUNo|Bn;CU zZss;w9)-?xyR3YN!{YDA-Fl1HwpttiXQ&UqWflsP%&Fxvj2p}ZB_#6?6cCC2qx?X zun@K!VLA@cQ#g*mlqs#0i_{H^0?fbc6u%=_m36jm@C|$2nq0|$6YJ7Xdnxp54IhI| z5H`@;l~Nx2gOd7&Vts#OtmV#c@Q)3PZm-0Kzibg-NnZg=p`3Vdx8=irG5G{8XSea7-x+?idt#h>E#y6w6&BvY7L3e6fJStTF2bZAioe(f{x-M zud2EgeKq8Fp_OQeUBy(Sttk^;uQ9~K*`PoT^qy43h5xMhMyerUo9r(O`Vo?T0fsd~ zFCpu3_U$V26H-xPjrv?y9G(B}*-(Ps{sPfsWD$VJ0W%4*gT@Nu%q3s-sVhQ8`>ZLP z|HF4$EH$_ANl7pe{#*#Jvr?4bDLr!B&Lv-cO`j8pi!9{%wG`lw14K29z z$@+xMhVA~vk)Mr8Ty2snb?ibmalpuUiE%+hG%aV@a*6TYBd6U}AIb_y+le-Th8royZ`R zq~}`VqOtha8^6`h9cA3$8L*;ZvG$C-;J#|Vpc^@BBXD%L(yU^)mcuh5M_9PW+e{UmCwlDfkEW?NWhlU;*>Y5Tc?97JWm zaS^b@2QlTQb{ehGB0iH02{=TYxaGIk?jdWpwHvgHwS#Bwpps-ALXUnq!7V>b6BPMh zje}xQ&P#D}*Z(wX^l&U$!U2chB&As$ns+=FXi6;<7X;xFsphH=QW_pgq?RHFB3e%? z`DyVx_td$H3pGL6fCNTDN3=_u%&ZbHE^@VAI=h%$?-sV*!kjH7040OeGe)b*XHvHL z`UeKlvnnn(CpL*+_yNf=na=FWI@xG4=M%3ppO2(eiAmKRRM(PclzTQtQ=3f^cgb;# z|L$|WR}*qOKbP_+Dp*~$)jNmMQFSSB*X=C%{ATvjP2R{K(l6?nA%(U*wn{hnBxmS3 zVvzA&@YE3{p<4U~h`^G2w;*6{-~R&>#6`T}=K?;f0X>Uw&hdAtTOZsT;ru7Gx4XAl zDH#QJTO4*V&86BxDn#dt#oAb|-hf($1J|V`=9RkfMjRRh#jj0OL~XVCHmnfy;J>cH z)EYH$el3s#xae(*>xVPtJ%Nx&xcrvaxjlgjH-=sGE6h;zz?eACJd$JBdKM4JGW1ge zLDhHc^6&nSWdUfLgPr}zt-*=|Gx*F%s6^U6^*t*xblA++Okz6du#Wjbxuvg6=yd{6 z2Agc%c97gS+opbrpo$sLnUH2OQ6?L}hp=ETRn=;5)R^Bs7{){)?zL z%ZB^*TkK3GkVo-7{ZxHzo1U<;)gHi=fGvR@dH2lTiVF&kDH;yUs@ z2*csHgpG`whYaVyzWO_aK>Y&a?)XItc$S9-lGgG75N@)0vb1|Dx2ZRi=(t}7|0|87 zz>D=hsncODk!S}^>VF|!AOW9_2k!UB8(r?i)Z{4^FR&$86$mx2z$T zE3K+pcZlLqo~UWO)l7~BcAbzKnP>%TTNRT;yL<&+dO7y1y%AK}lK)s(Tbp9!y%kDTL zN^}Rs`dukuJ$&0}kO$%&=T#FYp`pn( zHuw-W_h7fxNcRfESj!}6eJHUF<^)4|N;-QyaKZwz1)ygi;q6rJ(e&@n7cTzTDw4sp zHl2iST&pwbRW8Y?RCF{B{}RVSbcoy|YNOW*)1*po4&I!_Oc5P|b;4j85fZOo3j? zL9`F5-bX>&bMk}9>QdHP)#oPn-XHn^#RyXr$!sS{EvJdKVWn% zOq4J%4FtLO>En?Z9D1Mwrx%{v2AOjiJgfx!O8dkb*j$ox#{Zn zuB*GcYOlT4dhmRfSxnQKP`Tv96jh=rvg#BqU_0bmv^wIo)s547OYA$7{|3b348<|N zTU*J@1DdYl!VF0Xk&(IhqxU?ycD+AvX;5F+OV{0pg%UQ=1yHhAw`Md@9h`~==$H1h ze9YH>ondS%I6(2-8gi!7v>xx|39xMXl*D7i#9J3L*) z4p%;Osi;3%8kI(JJ!q=SM*l|n`KoPvKRwQQ4-f=F-UX^2*@nPou?VS9T24U7wS*i& zV^&@<&c|$%cnzD9N#>LPlwT#Hc(F$8vEZ%-AeE!CVftLgCIsHUxt3^1P_*FsxTbX= zNnI%kP6{*JsW@=Lrj9pdb z0JYX`n<%ojwnDp!aE#(bgv2SZcwNw>l167Ffb=&WC;B9jzlq`h87ImTa3bY%aT2$ z*p{eq4r~l1`eif2j@uya;A@D6z)ZlFpdol5A_O~%CpyWsz1WB^($6&5VEHA$Kx|A@ zW~}vc?bx;c!Q^{VmuMmS2cN56oFOzE?W+-jV-BB@Sq2Bulr7UGl<{x3qeomrtzaZX zZq}2zf?v?q)P?{n_{?NsuPw-`;JO%Y#~QLV+(S*o(h(3=`=IQhKHWwDa0)i_R_r74Ac z8cFS56P$FsQINpL*0;-2uYBEKibasi;Y+@~F49$Ln{QMle6$%J=0r=Yk=sSUFHfu| z7^gO=^Fr5`kBN{j68=9)BT>zauP&}2q1l%8c%x5;=3x{NpYL4?K3RV>q>PE=@4iIh zi_{uFhk>5`Ijw{ytgjpOd0T%f#^n4{;Qc`1ZAJ)Sx-i!#;9BZPt@n51AteH2MS8jf zx7pnNhGq(9U%LqzyVx$Uy8Yr|U74~cNqemaPG!&8tkalZf5cPJw$G$8f?1t9)%Ba* zjM;$x35o~S5sb>pm2*kdRpi3;E?H5%kT?+bd(Kl$TRQ_>Y`jl6Q(SzJr2L31aoE|rlydue=KLpU)k(L#KM}>)(RbD z{K9H#q+=;Nv~z2nRn*SzemAp&^nrkYgCKx-QiB+3CiO~MHh#qcyP{)Rd#Y+25OvnJ zxkny_-)mtsY_+JQTOMgyc<~q0?`ok~Mty-d-$3LlMT8Qo|M(R-_y!lx{}*3j#FE(b zeI_HyHh|1p=T8hM-D9LV%Rf{I>y0LOV$L%5#=h!*)W6CEDKw&zJGI{~b+ENt$Ktei zO`fz5&0dFOB=-x|)FOaqOa2m+*;29SVS~81i$LgJSnE!n zQ-OEWGk;p!C9E-jf&N~e2lkAGZ=H4{zG283pD;OGsT;}TFZ;0v4A31hECXtH&)Dd; zPFr%8Ad*BpPFedA*oI1p;BgGa+m$R~*&(rOV{~@{M{a6y>cUl6hI6uJan=3((5Jfyd6s=oT`VbEAN zLal)t-182_j)4JjX@iR z1Vr;f8vJF>UI>7#olInmLGlzKJyzrdE0W>@P3VT?zoVeCHy81U2=Li`Bis z`8#U_e!^Z!JsKY(@$Q5B2oq^03Qp7RxM|GT(AaOM67-=>0v81T5NjFqo1R^#o`~E+ z;mXxWWSt?-qtS;irR8T1)@a+x%g1eF%d0>5F55FZwVon+HyF7tDEMzJTmh2HO>-6Q zy51Ur-G9g}TwaHy=U@38@%iOjq9NPnV{u70yN1r+89p{6cH5gWa?E^Qw@ofoyxcU3 z`*^c+TrKtjfDxLWFdknp@_ds*hAs)^T)emQYag%B`<(HT%x|hl@Q<&mVbT73um5s_ zh1DIIX>Z*F>Wxl0{d!A>+_*jm?_eF!*mJU^u*Y+nvm@nqrj6;ipRJE?T`U~TL!x{K zllqGWqr$uN?R470%U(&D%2w%DnWl}i7FeiSHQHT~vJ)wS~ zOooNQz0lbjAw{3Dy9dz0O6_0P+p(IA6{*<>CpA!kg8ae^Zw%_xEO_xD%t%hIRM!wS z&d)4A;dl3D^c)N~3BH7)YmI(mYp!8wWnI=c|4KC-6Vdc6R%LEh{Cx=(7tEbyPXRJ` z`(6zoNkS-t8m^bMaocq35gdbJOKs@8I6?NcD0*I7S>3uDCfR!x*%RVj1Nqqs=R7UefR9lomsDb zuNXh5@MRB4HwlfpGtjerpBWa z&yo?G`iV~oofDV}rH-d*Wmp=RI!maIrSFKw=`~hDND1bs>JrlHxgB6xPIIXS11hQ= zd)SvitON~H>Jhg^l7J1}1y#QdlvwIVDO6l0qPzWummGJWC19h0kUHO|t!JTFeYB1m zSj!JmOMosP4t4Fbf-yrzkgp9?>CCOBeptidmQN<{i=04Ia9V;afTG4Pu}!#@5t+!& zLh%M#Y4-Eb5~p1R$ze{QWr0tnuHG7GP3*ELDpwr#;f5*;$__kO0slzAf${vx;R1}6 zzfYULZ_(RS0CwE5{)*xWRWi+YtJ0>Sh;D7 zO~}=}b@9R-&(q{jY#_%h*meeS$1>!6CLJQBOfWms4&ykV5A5g#fl%k44Mwm7r{%qi zCI;c{m31l1s5mLr0t+#q9Lrn&=2wz#Q&jb4#qccKfe7Hp@|L^cK=k4N{O<|)JV)|- zBRcY5;Nm_%`b;D^Y~*d$~=cdV?eu1(;=# z$s$mr-s0EQP{8zTHD0`H7NU7Z~ijE>%>DiAESe35b2~3_-;|!A1ba-fjZ+YITSyf!#`5IOnp2% zIdGwdL>_m1_Msd3UR*3Vo5Njw#y&{FJp#7E!z(>3rnmL7;i>(h9uscrU`I9X2(*+epfI$TQGC2qHdlHV&RZ;&qQfs5EV;n&Jp8wz;cG zZw2)mZ2AEW*?}-YMxw(HwLuu}rffrZbnPV94GP%i17WwE|9n|K1cDWel~PMp$##0D z@{7tZ<3Ez??R^wq$gE*arbX-9Z@LNA1n+fw;1p-T>X&dO^zh;D1A1@A7Xq-embRKK zZS_BqI`zz;NTsb`)CjO4XlfINA79gC3-vo1k*Ql!{p#O`Y&ZcEvR3R+21-4pO|NHR zoBN@A`4O@4b+Y1&ENBf{8f|8lq+su>qgdo)f=74hqy?CMR|I(Z5*sNG{U+J6-b5Ki z;PNtM?4HBoAWl0PSyQ66r4dL!&=YZtcJCG>QKfdUXw(674M`$^ubk;56=g^KTDu_W z#@YFf+)Q93!QQsok0C}$gaB@Kx*#zf^SSA7mbGu(5|-FTJKJMxKfkkdu^2~w%3 z+6s>Ah;Ow@b5#iQ_lbJzkwBX`Ft?KcNssC_G-U7HZt)=D|9CgR+j}u4k=nblKjnvH ztK=w9S#TXDF3Z`U2GZvVa}a*UfU^xw|16SJuwOZ}FaQ4v!PrP>XUWMmRj$i_sZ&vz zEuSPsSua2--M^CTg#QKI-nZ5L@bEh@5ZMj#LjCG(6&dgH8nDjXqVzui6}T3isXpiv z;Nb=P3!ozZ7scpX$+2Y+6Gzr#-?n2QNBqfE`rPt z@@go;o#%TXbSd5MOR>)}DX!3YrNmj~6tgq#B{YY2#00=64)%=T9(vIJ*iUe2S@KkP zfITaMU*}e!=WRCE+q;}_r}8+)>{%4qXl-4Msj*;Rlx0Dd{l_|?QDb7ClmC0g`eBwN zz+YAvY>Y`Ja0kN1n35k5c`$14{vkON54Nx(8qam}O-4Iqi~+iB_`+tSF6SrY#Rz)K zpVFMU%bc8Bw$aejv_;mND_4QBT!wHDZ#wI5-;?d|f`8*IO-jIya<|3(;4bw#7nO2F zuhMt1mh`Jv;eg0Ix3GhyRj)t9X1p&p1Qv{H_xy3`)gu)`I|g4$|J?$}GAS+^s8 ze>}8B%~)ZYp%5#wm0^wAaDm?4Iej)eEyNbZ`Wr^6vlMM4pHNS9n{-(?Gh!7r44C%8 zXBRQm{q>jjBllqUYY~AHkJut3Z>3x3Q}d}Sih&94Az3w$?;!}N{x9Y0J;QpZg#;czP9C2WBW7uctxQ5jJm}1(y%FNYfCb} z8Au!`5&fp7ze#Y9Z^$; zhT&_<4b|n$71m3rwXy;OczNc77a2kmLF75(zNkqM1FRww6n|V0ErtLO;59qx1r5sy4gO1;jBYWH8fuIl+uswy=ltXf~#41CKm9<9>h z=anx{-vL3|iMZ%t6v?{}AcF>to-x<$i@1XsS6o&lpiAc;9-u=FhU0_i3IG{{oik*? zWczrAxN3J*S(tk``SvRjv5eHHAR_hsOU`^XqY}2{{J0(Cza9Sx>w-MS_w*h$H5AOgmm6Tp5S}=R zz~k7qod6zna8XLQN<{8RAP*amC{n9Kbd4ErTUW4mHBlBP!n1W-!5uQ%IYCWV_m)>p zVku`fuIMsM;h8A{6rv=fPDItvTy~D__07i7Y>LdPWAKp!Q^Q>_T8avgO^4ku$S7fp zLNO?xw-)=ZgO!XwhLfrHFiFN&ucDASj`IUsWl;9e$sbbRuCuEuQIP~YhAlZ4LJy^* z)=;DHm=8dSnDZG6EsI3oQ1CSq0#K8vf|UO3<4TZ4uHCc&Sd@Ju`^;#16FHUltrHSQ zVG)l}P?9|b>sh-U2S3>P1Ublw5hiCb-BRlvw_(StU>xJ2DRqU6uV2Vv!vBU1A;ulx zK=a>*u)gGI#u2C*g%u|2pQfRJD#pb`)f2@h4wE`_l>9O2O2w(6y8aLd8veZgJA6od zED~FpU_;mjgonJG(cV}YQIsSYR^i7-GH&UUk&iKZi(tjIp1hQ!mF*Euf#nBMcd6Va zc@s9`5N}T09_C~TeuhPG;u1A``~O)d;+hNs*E4+s+uOn;dS}V@H(#tjgvYK*!oc77 zH3A#im6$9*rKBl7+P-0KY13LP{C%1$CN^4&{g`PAVEs{eb3_o9X*=uG$i^G4bVn}U zq**KZ$j-%Yhy-K|5*=NKbG;dDLfahjGldIG?IOE84-$MZ1^E_3v~#^R6b%xU8rlv9 zegD^;q_dpVPx+B;D-1kq&ODvwE~?aX#b)=CH*wsmQ8yT?$#gH2~A_P zuI`yIU}DM&98cAg%aIb*jnPu|LrN;#*go8^gIx zYo4CH5=^DsaA*BcHU4E>CowW7EC5(lv}Ke&&di!}YY%G5-GJkY{Llz<-`Ep-MTt~J z(PIkoC)m6qf$!8LmUA*D>4H_E;$Q|Mq<^(m1n1>C;ZK(>pH5(AHv4X$BRVp?m4x2I zjv(oYJ5WNX*b^-NWpzN(C0P1N5@n_77F^+|%2=9_gyD*IeSo=;&rhL~eywkZ5Jg*F z5~W=bkR+#}&oNxN+*-uKx7eJeEKAXXLh407)LEyhHVrSUjqwT;j*Mz2OD0?;QJX={oj=(G@URaJi6NK_0oIR zJ|P=df5^Zmgwu?zdC5?< zLr*W`qN7YM^@y?(*XYetmJdXYG^b>rLFwS)ptNfIwz7$(xVROqc<|&O-fPcA^>Hd7 zCPh#F$52%BxPUP(Cp3Fx_b>XGp-+~c?IPj5&!AANOKex>nh)Bk*pdVAjPlO!=jh~# zBeNBzD>E4Wz5ok_=%dBK&32?n+rx>cle^$t<0>sqR<4Ir|0q7q1_|A%#=%{FzvOeK zM{I1u!X@-`S8Ns&q8d!MXlzN?rR|$y!jZWc3dyRO9DGNN4B`nfsnU!*$Tx5pT`djX zO|bMT_Pvo2?AAVFOWB3nRL{{THtN-7f@rZRl1+FOPhDi#o0z^Qdab zK*jpi%?#G$-rsr0MU=0!_N3Ms@_cRBua)lxF1GAyaFv^XVS9lN3Ui-*KfjgXfykc$ z9Rm!QPa00fG0{k*p#Rw%!_`<-R`~NGS5rPv=o8YMXi$^^u*(`Q;e|EZzv>mOLWw5( zg1l$?qpr7w!AgFaUSGP@|sd6 zRQy+JTDJ2DP5F?c^LmascbbVOPk2vCt&g6j zUjLgH%)8$0m96lQCsr@tsEPRK{$6@`PrNyqh5j4xh!zWefYI9-W)I8L3*FQUHWp=| z@3|&?yk=ogac&Vc#_u2S!IsmWA-i{QZ-qBH*N;VC7|%;Im{j<@C&WU%`??k=lven& zTK`YF>+z3=tNAad_Ph^2mFd;#igfnzS`Sx1rn$>j^=R zfiL6)<@cY22-S`LUq%WVTJ{Xg?fVm9?zrCGh`1tZ!$Jz}_NJNZdB_YgrAEXCL|2Oq zXJq5g5{7ky1Z-JTwz#ZS=DMhi+H3kUe9Hq~I@~-w+@XoJ5r5yz)}KB}fp}lheMX>a zInSlx4ZhBH!-4HPNS?@j_&C9@hPusWQ|g)3Jm1=z#Q3YjaJ~2;w(~0d)@ro)L(xmN zU-qarS0r>LkFw3hdymY^CcJ`^C!+~B#*oyE!A!67ClbX(vHq;D%*8|=RcPh8CI*yX zHWjG*uV&m#^&>r>V`6!9bEZzTsV@M>57YDNdQ}Ikkd^PA(vV>lt||NlCIMFo92!!2 z4O8zIcOA9V-^l{b$hD?M=MU`3tWm7c!+)2EDs{`-WlXKtPS_1=#7Q5uhb+NTr;O)v zRP}uygZMQw{0p`#m*?W{J^U)wT8ffpSb3jznPqD!##`a$P|^E57`546%+m*;sw~(K z$yD-lj99xie`qc<{=!$;27ga}i>{>4Q>|8;c`6lC+58b3Ophu-XiO;X zaTYbylzbvc_wD=79&bye796hjvBge7>AIbC0~L}sgH#kTh8-DhIA3*ueSz)3C~8h6 zVgv)cIz4Iq!y0~yHD5~e7>W#9=MUC&G=-&bV_YTl7n`%tY@XO?`}t4mPkCS7Q)$@~ z@S&Pj@iV+io06+y$t|N^8kNcKCtIXg`Ze>C+hvk;->pR{> zXf5VQ<-7fXoA)}?A?(Hm_%mH-a@fu&i;O+-eotteNjpldM<|f-V@J%Mh35KIH!+h~ z)N#8~9or{IH;S;hwNeaV8QiDY0ctBG;IF>OgiyScK z4oY}{>Kxo60cJXd{Z1l!nH%T6mT*3>1rWogSQP>Z;iEX~L0W7KM*2~nvz#+zr~*9U zWCvsVk>m)@0FyM4!o@u}Tk>#!8~j0Ky6|oIQheq97s_-d(vt*tocCinRVE5KYD!aS z?v3kyU1RlVhkXzT1An)ARpNwTfgTh3yITLK>`UHjUOYw2=1@9`sHq2csGB?-#HE!k?yPT}3r43)>ZT)Ce{xc?7oiL-r0m0t@1r#LZB*jpU5nDn zKdr`Ml5NOx%~0QXL}To5G6Tn7MP*mVpTUH*5gt``>EPqN6D@ljnfP;ArE>1fUD2}n zcGf|Vd4#>P%szr%`zw8=xtVn8IJI zisMxFvR-E3J@)joM(z_4G?Pc|lt}dYp>X*6TM|)4Wnq4C)8@$g6On_c>ug%dV`zj3 zm4&G+w#Y02ZR*XH*(IwZ2u5rm63g_DoRBiOwew6Ow{h5A%=LPmj4^=o{t-#p6Rh|HNj`Y8&Wfxhq{Hm(Z*Y}|X`|=EMHVlPrPJK7Y@4;xJERS% zUCY>q$l$Aezer*a9WS#{%}rtme9mE-p5BnoXMfMl3l8ss0a|bbwXNvg{qMUNFg1ML@8p_4h5?q~#d`=EIOU<~MMv z!y|z=CK|z4C_2eLn8*^gU(kPH3ka|EL}e3tQEQ_`ZJhVg5lruu_p_h$SNbbF)TD%m z765*sId>+jtQA^06D?#LNl67?@o9B$l|g01Qu9WKjy)E0y38(_G3iryNOx6Izt~=s zd~q7Q%C*P6jlRr*MGh&`5#;fFoz@vuP2xZCtMjqVvhRQOKE4J8B3 z+cTLmIGFL1NJ;%h8Zkl5vO+`E_j%0dpzNT6Kv&}GL?7`ht8WjCdI{qk+)mX7SAY~Z z|2_VL{h|VY@xM2b9$&sQf-o&$K}8}>AWxV@=(J)QKBVhMr_0}0tP%dvD@{RaEn)>lgL`^YBqJ|gAYPXF5z^d`AA8#V2;F(Q;D zU1WIp23NR6C_JV5XbPe@M66T5vS|68315#;gRAR51!1nG4^RVKxXT@iWEk^qh_UJ2 zw&V%(R3MLy@(6v1AJe3@fyL~r$(Id*?(eNkHu?5K26;v~Vamz+idB-|f}!s$@{p)O z_hyS7EcUpz#2_n%w(tD2cp&08kw@C@pOWY|C_bm1neU{0n3(dYk38jEzUX47X1YA0 zl6uP>&-Zwcu8|Poj6Ll?jWws6d`+vpJIrL!8-Wxk|0ge>m>xU$i+ZIr@GxU+v&s)`dHIXDZ`MLSGniUrt(HAnsB9PcOsI`A#&`Z^U}7l z7NEWn?g07#v_0e}NB+3nz#~-98-~DkA}?F&$K6Pchx0$&_d^}>(?_2UzJE2KFF#XW z-88pr)q9QXJJipk->Evm3eT!Fj-Srn^mBKr?|j@oMAEaruI$3Irz3Q>`D)GDb`^>7 zHOaxvr3-=soQ_X14ENhDuLZ2^7GlDnKs|`K5ZZXslkf)7vor~E}~*d8Zrb&S=e)zhKC`yVr5buGqY0BtK8^okGvsf(yp&k$zz>t zUnc)QQDN8Ww}em40$Ea?c&f>Tgj@w93=(4s%H*??yO6~jPQORJj*)TsMkWhb;I-2| z3td9eFTo*YK!d@ZjggTmH`b78lI#Wx5>*vV4T(qrbPlg5S)(S4s9eFtW_N*-GsCCPWUdS04T6yq1;I%? z_pLlHQFTkCaHoV<_LXLJRGC?e$xNC&%$s93z?V^FFb9l+95vyA>1M7vYB=}}+c{gv z8`gUh{rqPRnvynXdp|hcMCsj_HkVOA#6b_VFhgc=$p!ShR zBsqFOeuSslbf#bZyA|~FD(K-=G$0)F^ZwscId0~|TLtyQ0p=bp-fZ1ar+Q-tWPz~i zC*m6(P2%9IeQ}JP-v+U9I_SUX6|W2Y5-a@D9Az=yxg(JxphpQSi2VBb|FPrONf(k9Tb22l{^}8YRqT zo9`7C;!H8hrY>)&9jYq8^Z6z;T^gw09&WdvYj_#kFkZ$O`%&Kd_yGBclJhYG?I@3Z-(F27-wGT>l{m51Io=W$<(-!T$)J zib?1tdBwJ&$EN#@-V&vyzDvaP!{ULAX|jx(f-ynUjw$PeG}^_J;!RDoSEjUiF_DYh zS|JpCAevj!AD~*jASrPsxI0$7B8d`pXjs7%62w>u3k$-+l%XW|Cq(EUMJQOosB^M7 z^@5^?$jXf=Q*AxC`>l#+FnNE8^U3rpYcfqYRXm zNM1~-`O%|E?Pt4@7N9Yt7{Qq2IiT{Oq;!#P0IY;7JUC&kn+8A;F%I#4MR1ND4lPcY z?b62am3DbV89EG({YCr6s=6&~WGRIca7K@*k=fT{w;2N?WVxG920X*6CfIM2lW^5KyULCC}H{#EjZK?;z^{WS$9w zT{;o5Py8I0t|_CysAXJoyT2oK`@!~kasRf39Kyt~IFwK7QRloRYSB20f0zQ;#9WlZ zs_pHYyL;!ejclC8{vV+H?(oJ{-olp+&cWjPF+{52Hc74yyq)u}J+lVd<*wmh?Z!rH z2!6pwHT5V@wl$wtjN?C9%G+8%9MpP0ml@h#Y0=qMtzA@C&h3w}IiHZ3CC%Yvfix=r zQO-OWZ~Jb=`GE+*QD%s9(`;lc_8c`{jzr|S-9EL;7-X6iwo1b2Rljn8Azkb4C za@Wp8QrtX=CP+&HvhPhx3hA#m$^yq-0p&3pDH?wm$0Z|@sbeoVbb8nQ2h7Q&Fy8M5 zUB-Gcz0imRpkgJ(5c61+dZ5WKhTZ9lQ$|HEWCgB7yS+Kw8EGapE{gq_Bd$czv4|UI`-S6jDyLZl6RjAEt@E)zb zS#JNGi%OtPuWgptn4)D4(fl=v;%Re`F@lG?Oji%djSE?}BJ8qq3{93c!*33?U^=ZKNn@#@Jb#l#@4 zYhHwzO4fa}c|GAt(mItg*BOGVj|?^xQ*4=9e1~>SEb8rZqT7&Ioiv z`ocxB?`uQJ@sagHaK4FU9z$Wx+3-Q5RjgSk`U#Lv_!_+dnThi_8PF8I$X8Q2JPKb_ z-}Xc5%9f)ECFFoOv-JH}#u(BL7F4XyY1y2j266`I0^1L@P6=%Zc#tsBpfbUv`fb_G z>`0#+ITG{&Z%hfM{$+TMTWIX_@osuDPiLb`E4i-B)@wch5p1bTWar>xE%5{ornr?$a>%VE zYSgYP*C!UB+jhdUF6|n8FO|!g7bg+Q30t?qZo8hqSg)zTwoCtQ)X*ecplp|bWqGyi zw=?t~?N$mY3{crd806_@ava;ya;j8IJP=ZT&YzsS1a)TDqcEU25K;(SXpUxLxS%`%Ni&rB>K=|m`G5gM^ejUOp`^9LXK0%5y1w{zGlATMFS3O%ETUZ@f`($@$58WHRss@e8%;if?@MG$qA*9 zxSuVO29n_RAM8IDG0S`ky*)zCCI)+iu+98{BT~h3HybkoE%DE{#bt{Gf~_BN3OwcU z+LP25CW8`gH!4{r49rS>LN)rb&lj)d@##e z>erm*y-8KH4Q>>kYU$RJbwhcw=nV&xTGPAQ*7XY$8r9TsbQl8EsTn6PAe&U-0#u^_ zD3_q7w}e?NVzgQxu4W-lV89HvqOKrR<#%JkaLk&TN=PP9R;1K@?U(^_!GPV*QfkRX zYDB3wmd4xh@`=&@ z824XTo1Kq*u};>eGOJPhp!Oypl9M%Jd~ww{bW`0H?dfRT^WWe*eXXdRS!|tjA%K=5 zr=^9bX3@qEY{*U70-57$cZVGkFKjQ|2qV}iW&c=j$sX+_S$0QXu|l*hLW2ah&o~vx zD4?`t`b<@BMr!QUtgnJe5`!4j7djAdK$w;HA;`Y1C!)k1!x#pR;y_8lR4Pgpf+F(B z_HV$mUV+J3WvhB#r}sLdYHshJWXHJAzrFCqF)%j(c@)gM>$C89Qx~i1qw9kSlvAD_BTl> zN|f8SegI7TDy1k3h_mZYiq-^Qu10PGL_KI)&Wzk}Yb?h(8YnlH- ztiWWm4R9i;Q`;wfGC%pFUQ;rrMJ&XTQz-cys@98J{Wb$JaCWNSuy+ycC2d4{^&@F=$0;?m4vljDSfUvfX5I~Nql90e*cR44P>jIr((j&B}6Nq8^Au7oQlRC^X*TJMmI7P_?iACZ0 z`A$^m9;nk^F=UW=hn4f`PmE0AY2fXnfUQi9h4Gu*HnInQZj|a3pa9W-UP>f@Czl(zSiBuMA`fh$j z=etIR(kw1>_$WZC-LUvaf8uoRWh9M#Y2+VC&t>v*$Oh&$t_1w$24eat6nsia*@Pa^ zUBQvwlGIqp;lcxKq&c+VJ@Oe4-F`NlQCB3Uzuh2zMeO`iMa}AU-{(jdPt~ZS2wz|U zwe$*WxgZ&w*0k@g8?k$$`ESo#w1LPnuJmwFs=at7NkCp2f>`A z?px}t%)T2;LK0CwL=mrGk|kR<(II2Grk8X+pv$fN9uE^n^QgKa)qL)^^T)6Z>7@1i z3E&R-Wt)o*31-4X7bjVdNc&-}TClg}s z7ujA%#DBBBPc;UN&_1{IO>7dWm?Mw@vx-co@`qB8Ea~okNp6`qQPDQp`^uI6U81bhkdwjpmm2XB%r;? zpV51&BSW{1OM6S+t9J({eje@{N<0T8xN+ZCdEK;6K#cATY$w)mo2HrWJ6qlkIPf^< zf&WPmnn$bP!FTuPqLJA@4lT>wuCr@F?jCvFTaUJI!-ZcVf0jOC%Il6S`US;^FIf7~ zq10g1dL`VX)K{ion|SV-ID}4459n!oq4pU@K^Y=h?7bNxVWlwhMww3FRgNC14&#r# zMaamaW7Xa|O zha!^%12|%JZAMiQzJAIUCpZOu)NN4&=Rf$$y=&K9w>=m)_2tlBsw6I1#<+c1@(em(pH96lPtuvSw zN0DZHo~G8{{~8hPHqMqH?_K<-%N-&It+;<+vymCRF)eRqbNS)r!pk5( z_35Kxswry@$<332$wtORbkUk^K3|{jG1IiyBaCb1<$0gU6g0QngfhFyo+2uM5!+`)<4mEplrP>qL$&8U3#@ zCw0{jqXE>A3eY4F5Xk+e1zbr`q~O5lV!N46C&_2UPK_}tURk$c7IT`z&Dda;|A2Rj^Ga2H;-^3aC~{GE{B$C zMh7XMleRanGSb|1dQX4Y-RmWLJRfhxVu8eMHc#Q-Kpqh<3BTI6k2hv*mwsIUdG8@; zF_=*+?2p`qn(yyIVXfkA-H!gV%w5<9-skQ2rRmAay+BO*| z-QNa>JpRMjSF3M`Bs0kD&6kl8kc{|zB|}MBOE{OOSz&)fwwt#lTc@VX!3f|UOgN-1 zl9oJVfa^x)_65FRiNv?erE5OWngs&$&e_lsOCkr%RezC7RiwWr+^v9t5*qQtE=MMY z#39Wy$okjPBDHlF-1ILhcX;&0;W5s%6U!WIi zKP;69Ex%sV_}#|(gdf`*UWFc3JXH52U% zQlOV1H=?%4r3@!`X`*uaEuCF@pAtEY?O+714~E*f@#h`}gB-|Ho#z}MZ;Pc#Vx#`Q fbssTzvF`Z1XxepwPizp7&(AwHeSWiaD3JdHq`d4S delta 32830 zcmYhCV{jlrw}xZe+1R$Z(Z;s9vF%K3+uYdR*xcB*?PSBd-~H-V-CuKh(9=~@-Tj{P zK2s71wi5srj|Ze4NL=XXwC+iu<&6zk^CfM@I8TX?lN-r`>Vh>?OC5G>_Xk~v2}L%T z;x_8AVZpM0BcAy<7caVP6!jGY+djp2^V0dK?vif}8Vh$r0^GQd2+#1{1eFC%cJ|(J zIt9L;yYRJA8>XXa6}i_fdS4c$~&J_kZI! zb>Xw&%;`7W;<>ir+(Phh-Ktu)46=&BEvM^B7t_&*7I@Gs>e74G(5lO% zf)YU*J2Hd7dVsEGb0_(u@tQF8K?uhw0RCiFAkqhgju0{1IAQ!<{$d)Uetiy%f2P8t z!0cnka12@EO2Zx6U|Li49rDa`)82w#jXM4MSLFtyN(EZoTj`FTVDC^Q3_=0UtVN~A zz`q-ie#qP_o4mtUg>3}0mZs`dZD_ix6Q{q8G(Lww3K+pazZHO>QspL?_(Z{Q1A=_z z9H2!?&)#jIOQI@_1EOWbBS{VpN2L9?Fh#@?=Qth2D2;I>{kZ=AB=!&5DY%hp2NvIE zT)_u%oIqpCYy;Skn8A%%Dv?BQ!UDAo*6TtO1DDILr>a964Tux7PZe zY!Gu<{iw=K13|+Ux{*rG6yjVx16<~s`l%LjXRUXh{@8MShWfgvl3fsrl4S`8B1Eiy(^ zU$#D!C4Fja{Z2IS@s^*b$T02Hn&C8}dqe$!hP9j)vUMFohGMp1hAjO-fdAi7RCrn@ zwlhlxI`-W7nNfx94Q^~KmcGk8W?|vuI<~c|M`*qryJI*$F3Fb9k)e)z|2d`Ox_hQQ z+po7fi@o@-jC|nz1`G>CVO56sIdgTR;Av(<}XKkVvlrD7$_Z(z*#WY-)ABZxPnK}|IIWT z_x2p*R|)%$)=hXBT{vAY;d1;<&NU=hKqR`4rCkWAU(ozAVL4dmm&e2H-K7V9KFfim zVpn3+ud|{k8lE4~1Ld_PVc7?19yZz^1c+jeU;_0B6tEmN67dCqJIt5@Ww%MaV})yZ zUV~ZVW|@+j<7*?^55`mgfhC~tJ(+tU1;H&ezCeISdyRYvO$;H#2PMjoH4Eubdzs?# zdc`Al;&dbPZM6?yZdKH&cG-jN=@A?U!hx|PDIH3|=*9W20cyUU-FDQ2J}ZkQPfIi^ zVvuy^myPn2YGD+hJJR&2&79fx`4d8@@eI0p&^LGojr{x_b+2Zl+tlL zyaA(-rZ&umj}2hh=9M?*O(4&#BGZS*{T4y6!lD1ng?!`4^6J$i*8T_>2k0OEL@p zLp(|_1#dtQSsUv3kyc?l_op zeROI5Loys-*Z&85AN*>I6V-;1AShv%Xf!xu@y?$zHY5s!4r&x4C5PCn1UB?}<=CCH z%6UE0z3jrKWnC@ znErJzrV(0L96vW%1)m(4&^6Zq+gl*)pY0*3i{Zz!DsV2bs`y>HxoWbD^EMCPlOC#e z8*uxCd0q+0@cfaKo8k_dLkR#_G8≶9hLM^q{Xv-oi^Y+myCMpO6YnPDYrf!P$oz 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 C=;THK diff --git a/build/openrpc/gateway.json.gz b/build/openrpc/gateway.json.gz index cc3870a4977ca6f01accdda37a04d03256828e51..05e440b6cb09bde25ee8b50a9bab7b234a7a2978 100644 GIT binary patch delta 6570 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 W_2K!A3<2ToO;*(mH=q|0;eP;_0lotO delta 6561 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 T`~Ck5009604EAard%*z!8%V8H diff --git a/build/openrpc/miner.json.gz b/build/openrpc/miner.json.gz index 4f67b5393c45ac4f20fd5d09cab5b6b0e4c22634..f0aad35f18b796370d85e404c74cb3d04651eb7a 100644 GIT binary patch delta 15944 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 delta 15945 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 diff --git a/build/openrpc/worker.json.gz b/build/openrpc/worker.json.gz index 88e4498042fb5a85b47d9ac54f2a3c511dcb3231..a0c2750044512cd7468ce0d396c51a81475e319b 100644 GIT binary patch delta 4004 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 Ku-UCH0RaGX4z6ba delta 4005 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 L^BnfAE&%}m95>Ib diff --git a/chain/eth_transaction_hash_lookup.go b/chain/eth_transaction_hash_lookup.go new file mode 100644 index 000000000..a7debc5a6 --- /dev/null +++ b/chain/eth_transaction_hash_lookup.go @@ -0,0 +1,162 @@ +package chain + +import ( + "database/sql" + "math" + + "github.com/ipfs/go-cid" + _ "github.com/mattn/go-sqlite3" + "golang.org/x/xerrors" + + "github.com/filecoin-project/go-state-types/abi" + + "github.com/filecoin-project/lotus/chain/types/ethtypes" +) + +var pragmas = []string{ + "PRAGMA synchronous = normal", + "PRAGMA temp_store = memory", + "PRAGMA mmap_size = 30000000000", + "PRAGMA page_size = 32768", + "PRAGMA auto_vacuum = NONE", + "PRAGMA automatic_index = OFF", + "PRAGMA journal_mode = WAL", + "PRAGMA read_uncommitted = ON", +} + +var ddls = []string{ + `CREATE TABLE IF NOT EXISTS tx_hash_lookup ( + hash TEXT PRIMARY KEY, + cid TEXT NOT NULL, + epoch INT NOT NULL + )`, + + // metadata containing version of schema + `CREATE TABLE IF NOT EXISTS _meta ( + version UINT64 NOT NULL UNIQUE + )`, + + // version 1. + `INSERT OR IGNORE INTO _meta (version) VALUES (1)`, +} + +const schemaVersion = 1 +const MemPoolEpoch = math.MaxInt64 + +const ( + insertTxHash = `INSERT INTO tx_hash_lookup + (hash, cid, epoch) + VALUES(?, ?, ?) + ON CONFLICT (hash) DO UPDATE SET epoch = EXCLUDED.epoch + WHERE epoch > EXCLUDED.epoch` +) + +type TransactionHashLookup struct { + db *sql.DB +} + +func (ei *TransactionHashLookup) InsertTxHash(txHash ethtypes.EthHash, c cid.Cid, epoch int64) error { + hashEntry, err := ei.db.Prepare(insertTxHash) + if err != nil { + return xerrors.Errorf("prepare insert event: %w", err) + } + + _, err = hashEntry.Exec(txHash.String(), c.String(), epoch) + return err +} + +func (ei *TransactionHashLookup) LookupCidFromTxHash(txHash ethtypes.EthHash) (cid.Cid, error) { + q, err := ei.db.Query("SELECT cid FROM tx_hash_lookup WHERE hash = :hash;", sql.Named("hash", txHash.String())) + if err != nil { + return cid.Undef, err + } + + var c string + if !q.Next() { + return cid.Undef, xerrors.Errorf("transaction hash %s not found", txHash.String()) + } + err = q.Scan(&c) + if err != nil { + return cid.Undef, err + } + return cid.Decode(c) +} + +func (ei *TransactionHashLookup) LookupTxHashFromCid(c cid.Cid) (ethtypes.EthHash, error) { + q, err := ei.db.Query("SELECT hash FROM tx_hash_lookup WHERE cid = :cid;", sql.Named("cid", c.String())) + if err != nil { + return ethtypes.EmptyEthHash, err + } + + var hashString string + if !q.Next() { + return ethtypes.EmptyEthHash, xerrors.Errorf("transaction hash %s not found", c.String()) + } + err = q.Scan(&hashString) + if err != nil { + return ethtypes.EmptyEthHash, err + } + return ethtypes.ParseEthHash(hashString) +} + +func (ei *TransactionHashLookup) RemoveEntriesOlderThan(epoch abi.ChainEpoch) (int64, error) { + res, err := ei.db.Exec("DELETE FROM tx_hash_lookup WHERE epoch < :epoch;", sql.Named("epoch", epoch)) + if err != nil { + return 0, err + } + + return res.RowsAffected() +} + +func NewTransactionHashLookup(path string) (*TransactionHashLookup, error) { + db, err := sql.Open("sqlite3", path+"?mode=rwc") + if err != nil { + return nil, xerrors.Errorf("open sqlite3 database: %w", err) + } + + for _, pragma := range pragmas { + if _, err := db.Exec(pragma); err != nil { + _ = db.Close() + return nil, xerrors.Errorf("exec pragma %q: %w", pragma, err) + } + } + + q, err := db.Query("SELECT name FROM sqlite_master WHERE type='table' AND name='_meta';") + if err == sql.ErrNoRows || !q.Next() { + // empty database, create the schema + for _, ddl := range ddls { + if _, err := db.Exec(ddl); err != nil { + _ = db.Close() + return nil, xerrors.Errorf("exec ddl %q: %w", ddl, err) + } + } + } else if err != nil { + _ = db.Close() + return nil, xerrors.Errorf("looking for _meta table: %w", err) + } else { + // Ensure we don't open a database from a different schema version + + row := db.QueryRow("SELECT max(version) FROM _meta") + var version int + err := row.Scan(&version) + if err != nil { + _ = db.Close() + return nil, xerrors.Errorf("invalid database version: no version found") + } + if version != schemaVersion { + _ = db.Close() + return nil, xerrors.Errorf("invalid database version: got %d, expected %d", version, schemaVersion) + } + } + + return &TransactionHashLookup{ + db: db, + }, nil +} + +func (ei *TransactionHashLookup) Close() error { + if ei.db == nil { + return nil + } + return ei.db.Close() +} diff --git a/chain/events/filter/mempool.go b/chain/events/filter/mempool.go index 250fc5961..39ccf12c2 100644 --- a/chain/events/filter/mempool.go +++ b/chain/events/filter/mempool.go @@ -5,7 +5,6 @@ import ( "sync" "time" - "github.com/ipfs/go-cid" "golang.org/x/xerrors" "github.com/filecoin-project/lotus/api" @@ -18,7 +17,7 @@ type MemPoolFilter struct { ch chan<- interface{} mu sync.Mutex - collected []cid.Cid + collected []*types.SignedMessage lastTaken time.Time } @@ -55,10 +54,10 @@ func (f *MemPoolFilter) CollectMessage(ctx context.Context, msg *types.SignedMes copy(f.collected, f.collected[1:]) f.collected = f.collected[:len(f.collected)-1] } - f.collected = append(f.collected, msg.Cid()) + f.collected = append(f.collected, msg) } -func (f *MemPoolFilter) TakeCollectedMessages(context.Context) []cid.Cid { +func (f *MemPoolFilter) TakeCollectedMessages(context.Context) []*types.SignedMessage { f.mu.Lock() collected := f.collected f.collected = nil diff --git a/chain/types/ethtypes/eth_transactions.go b/chain/types/ethtypes/eth_transactions.go index ba4b14f56..b6ae22169 100644 --- a/chain/types/ethtypes/eth_transactions.go +++ b/chain/types/ethtypes/eth_transactions.go @@ -231,6 +231,36 @@ func (tx *EthTxArgs) ToRlpUnsignedMsg() ([]byte, error) { return append([]byte{0x02}, encoded...), nil } +func (tx *EthTx) ToEthTxArgs() EthTxArgs { + return EthTxArgs{ + ChainID: int(tx.ChainID), + Nonce: int(tx.Nonce), + To: tx.To, + Value: big.Int(tx.Value), + MaxFeePerGas: big.Int(tx.MaxFeePerGas), + MaxPriorityFeePerGas: big.Int(tx.MaxPriorityFeePerGas), + GasLimit: int(tx.Gas), + Input: tx.Input, + V: big.Int(tx.V), + R: big.Int(tx.R), + S: big.Int(tx.S), + } +} + +func (tx *EthTx) TxHash() (EthHash, error) { + ethTxArgs := tx.ToEthTxArgs() + return (ðTxArgs).TxHash() +} + +func (tx *EthTxArgs) TxHash() (EthHash, error) { + rlp, err := tx.ToRlpSignedMsg() + if err != nil { + return EmptyEthHash, err + } + + return EthHashFromTxBytes(rlp), nil +} + func (tx *EthTxArgs) ToRlpSignedMsg() ([]byte, error) { packed1, err := tx.packTxFields() if err != nil { diff --git a/chain/types/ethtypes/eth_types.go b/chain/types/ethtypes/eth_types.go index a79238bf8..ea1322d67 100644 --- a/chain/types/ethtypes/eth_types.go +++ b/chain/types/ethtypes/eth_types.go @@ -392,10 +392,21 @@ func ParseEthHash(s string) (EthHash, error) { return h, nil } +func EthHashFromTxBytes(b []byte) EthHash { + hasher := sha3.NewLegacyKeccak256() + hasher.Write(b) + hash := hasher.Sum(nil) + + var ethHash EthHash + copy(ethHash[:], hash) + return ethHash +} + func (h EthHash) String() string { return "0x" + hex.EncodeToString(h[:]) } +// Should ONLY be used for blocks and Filecoin messages. Eth transactions expect a different hashing scheme. func (h EthHash) ToCid() cid.Cid { // err is always nil mh, _ := multihash.EncodeName(h[:], "blake2b-256") @@ -556,7 +567,7 @@ type EthLog struct { // The index corresponds to the sequence of messages produced by ChainGetParentMessages TransactionIndex EthUint64 `json:"transactionIndex"` - // TransactionHash is the cid of the message that produced the event log. + // TransactionHash is the hash of the RLP message that produced the event log. TransactionHash EthHash `json:"transactionHash"` // BlockHash is the hash of the tipset containing the message that produced the log. diff --git a/documentation/en/api-v1-unstable-methods.md b/documentation/en/api-v1-unstable-methods.md index fe5dd542c..bb64a4313 100644 --- a/documentation/en/api-v1-unstable-methods.md +++ b/documentation/en/api-v1-unstable-methods.md @@ -88,6 +88,7 @@ * [EthGetTransactionByBlockNumberAndIndex](#EthGetTransactionByBlockNumberAndIndex) * [EthGetTransactionByHash](#EthGetTransactionByHash) * [EthGetTransactionCount](#EthGetTransactionCount) + * [EthGetTransactionHashByCid](#EthGetTransactionHashByCid) * [EthGetTransactionReceipt](#EthGetTransactionReceipt) * [EthMaxPriorityFeePerGas](#EthMaxPriorityFeePerGas) * [EthNewBlockFilter](#EthNewBlockFilter) @@ -2778,6 +2779,22 @@ Inputs: Response: `"0x5"` +### EthGetTransactionHashByCid + + +Perms: read + +Inputs: +```json +[ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } +] +``` + +Response: `"0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e"` + ### EthGetTransactionReceipt diff --git a/documentation/en/default-lotus-config.toml b/documentation/en/default-lotus-config.toml index 7a1ab7bc5..4f2148552 100644 --- a/documentation/en/default-lotus-config.toml +++ b/documentation/en/default-lotus-config.toml @@ -343,3 +343,11 @@ #ActorEventDatabasePath = "" +[EthTxHashConfig] + # EnableEthHashToFilecoinCidMapping enables storing a mapping of eth transaction hashes to filecoin message Cids + # + # type: bool + # env var: LOTUS_ETHTXHASHCONFIG_ENABLEETHHASHTOFILECOINCIDMAPPING + #EnableEthHashToFilecoinCidMapping = true + + diff --git a/extern/filecoin-ffi b/extern/filecoin-ffi index 86eac2161..c4adeb453 160000 --- a/extern/filecoin-ffi +++ b/extern/filecoin-ffi @@ -1 +1 @@ -Subproject commit 86eac2161f442945bffee3fbfe7d094c20b48dd3 +Subproject commit c4adeb4532719acf7b1c182cb98a3cca7b955a14 diff --git a/itests/eth_deploy_test.go b/itests/eth_deploy_test.go index 13a68ce46..f73076d02 100644 --- a/itests/eth_deploy_test.go +++ b/itests/eth_deploy_test.go @@ -41,6 +41,7 @@ func TestDeployment(t *testing.T) { cfg.ActorEvent.EnableRealTimeFilterAPI = true return nil }), + kit.EthTxHashLookup(), ) ens.InterconnectAll().BeginMining(blockTime) diff --git a/itests/eth_filter_test.go b/itests/eth_filter_test.go index bee9a8c38..3d2d3064a 100644 --- a/itests/eth_filter_test.go +++ b/itests/eth_filter_test.go @@ -204,7 +204,7 @@ func TestEthNewFilterCatchAll(t *testing.T) { kit.QuietMiningLogs() blockTime := 100 * time.Millisecond - client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC(), kit.RealTimeFilterAPI()) + client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC(), kit.RealTimeFilterAPI(), kit.EthTxHashLookup()) ens.InterconnectAll().BeginMining(blockTime) ctx, cancel := context.WithTimeout(context.Background(), time.Minute) @@ -289,9 +289,9 @@ func TestEthNewFilterCatchAll(t *testing.T) { received := make(map[ethtypes.EthHash]msgInTipset) for m := range msgChan { - eh, err := ethtypes.EthHashFromCid(m.msg.Cid) + eh, err := client.EthGetTransactionHashByCid(ctx, m.msg.Cid) require.NoError(err) - received[eh] = m + received[*eh] = m } require.Equal(iterations, len(received), "all messages on chain") diff --git a/itests/eth_hash_lookup_test.go b/itests/eth_hash_lookup_test.go new file mode 100644 index 000000000..7188a34e4 --- /dev/null +++ b/itests/eth_hash_lookup_test.go @@ -0,0 +1,340 @@ +package itests + +import ( + "context" + "encoding/hex" + "os" + "testing" + "time" + + "github.com/stretchr/testify/require" + + "github.com/filecoin-project/go-state-types/big" + + "github.com/filecoin-project/lotus/api" + "github.com/filecoin-project/lotus/build" + "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/chain/types/ethtypes" + "github.com/filecoin-project/lotus/itests/kit" + "github.com/filecoin-project/lotus/node/config" +) + +// TestTransactionHashLookup tests to see if lotus correctly stores a mapping from ethereum transaction hash to +// Filecoin Message Cid +func TestTransactionHashLookup(t *testing.T) { + kit.QuietMiningLogs() + + blocktime := 1 * time.Second + client, _, ens := kit.EnsembleMinimal( + t, + kit.MockProofs(), + kit.ThroughRPC(), + kit.EthTxHashLookup(), + ) + ens.InterconnectAll().BeginMining(blocktime) + + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + // install contract + contractHex, err := os.ReadFile("./contracts/SimpleCoin.bin") + require.NoError(t, err) + + contract, err := hex.DecodeString(string(contractHex)) + require.NoError(t, err) + + // create a new Ethereum account + key, ethAddr, deployer := client.EVM().NewAccount() + + // send some funds to the f410 address + kit.SendFunds(ctx, t, client, deployer, types.FromFil(10)) + + gaslimit, err := client.EthEstimateGas(ctx, ethtypes.EthCall{ + From: ðAddr, + Data: contract, + }) + require.NoError(t, err) + + maxPriorityFeePerGas, err := client.EthMaxPriorityFeePerGas(ctx) + require.NoError(t, err) + + // now deploy a contract from the embryo, and validate it went well + tx := ethtypes.EthTxArgs{ + ChainID: build.Eip155ChainId, + Value: big.Zero(), + Nonce: 0, + MaxFeePerGas: types.NanoFil, + MaxPriorityFeePerGas: big.Int(maxPriorityFeePerGas), + GasLimit: int(gaslimit), + Input: contract, + V: big.Zero(), + R: big.Zero(), + S: big.Zero(), + } + + client.EVM().SignTransaction(&tx, key.PrivateKey) + + rawTxHash, err := tx.TxHash() + require.NoError(t, err) + + hash := client.EVM().SubmitTransaction(ctx, &tx) + require.Equal(t, rawTxHash, hash) + + mpoolTx, err := client.EthGetTransactionByHash(ctx, &hash) + require.NoError(t, err) + require.Equal(t, hash, mpoolTx.Hash) + + // Wait for message to land on chain + var receipt *api.EthTxReceipt + for i := 0; i < 20; i++ { + receipt, err = client.EthGetTransactionReceipt(ctx, hash) + if err != nil || receipt == nil { + time.Sleep(blocktime) + continue + } + break + } + require.NoError(t, err) + require.NotNil(t, receipt) + + // Verify that the chain transaction now has new fields set. + chainTx, err := client.EthGetTransactionByHash(ctx, &hash) + require.NoError(t, err) + require.Equal(t, hash, chainTx.Hash) + + // require that the hashes are identical + require.Equal(t, hash, chainTx.Hash) + require.NotNil(t, chainTx.BlockNumber) + require.Greater(t, uint64(*chainTx.BlockNumber), uint64(0)) + require.NotNil(t, chainTx.BlockHash) + require.NotEmpty(t, *chainTx.BlockHash) + require.NotNil(t, chainTx.TransactionIndex) + require.Equal(t, uint64(*chainTx.TransactionIndex), uint64(0)) // only transaction +} + +// TestTransactionHashLookupNoDb tests to see if looking up eth transactions by hash breaks without the lookup table +func TestTransactionHashLookupNoDb(t *testing.T) { + kit.QuietMiningLogs() + + blocktime := 1 * time.Second + client, _, ens := kit.EnsembleMinimal( + t, + kit.MockProofs(), + kit.ThroughRPC(), + kit.WithCfgOpt(func(cfg *config.FullNode) error { + cfg.EthTxHashConfig.EnableEthHashToFilecoinCidMapping = false + return nil + }), + ) + ens.InterconnectAll().BeginMining(blocktime) + + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + // install contract + contractHex, err := os.ReadFile("./contracts/SimpleCoin.bin") + require.NoError(t, err) + + contract, err := hex.DecodeString(string(contractHex)) + require.NoError(t, err) + + // create a new Ethereum account + key, ethAddr, deployer := client.EVM().NewAccount() + + // send some funds to the f410 address + kit.SendFunds(ctx, t, client, deployer, types.FromFil(10)) + + gaslimit, err := client.EthEstimateGas(ctx, ethtypes.EthCall{ + From: ðAddr, + Data: contract, + }) + require.NoError(t, err) + + maxPriorityFeePerGas, err := client.EthMaxPriorityFeePerGas(ctx) + require.NoError(t, err) + + // now deploy a contract from the embryo, and validate it went well + tx := ethtypes.EthTxArgs{ + ChainID: build.Eip155ChainId, + Value: big.Zero(), + Nonce: 0, + MaxFeePerGas: types.NanoFil, + MaxPriorityFeePerGas: big.Int(maxPriorityFeePerGas), + GasLimit: int(gaslimit), + Input: contract, + V: big.Zero(), + R: big.Zero(), + S: big.Zero(), + } + + client.EVM().SignTransaction(&tx, key.PrivateKey) + + rawTxHash, err := tx.TxHash() + require.NoError(t, err) + + hash := client.EVM().SubmitTransaction(ctx, &tx) + require.Equal(t, rawTxHash, hash) + + // We shouldn't be able to find the tx + mpoolTx, err := client.EthGetTransactionByHash(ctx, &hash) + require.NoError(t, err) + require.Nil(t, mpoolTx) + + // Wait for message to land on chain, we can't know exactly when because we can't find it. + time.Sleep(20 * blocktime) + receipt, err := client.EthGetTransactionReceipt(ctx, hash) + require.NoError(t, err) + require.Nil(t, receipt) + + // We still shouldn't be able to find the tx + chainTx, err := client.EthGetTransactionByHash(ctx, &hash) + require.NoError(t, err) + require.Nil(t, chainTx) +} + +// TestTransactionHashLookupBlsFilecoinMessage tests to see if lotus can find a BLS Filecoin Message using the transaction hash +func TestTransactionHashLookupBlsFilecoinMessage(t *testing.T) { + kit.QuietMiningLogs() + + blocktime := 1 * time.Second + client, _, ens := kit.EnsembleMinimal( + t, + kit.MockProofs(), + kit.ThroughRPC(), + kit.EthTxHashLookup(), + ) + ens.InterconnectAll().BeginMining(blocktime) + + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + // get the existing balance from the default wallet to then split it. + bal, err := client.WalletBalance(ctx, client.DefaultKey.Address) + require.NoError(t, err) + + // create a new address where to send funds. + addr, err := client.WalletNew(ctx, types.KTBLS) + require.NoError(t, err) + + toSend := big.Div(bal, big.NewInt(2)) + msg := &types.Message{ + From: client.DefaultKey.Address, + To: addr, + Value: toSend, + } + + sm, err := client.MpoolPushMessage(ctx, msg, nil) + require.NoError(t, err) + + hash, err := ethtypes.EthHashFromCid(sm.Message.Cid()) + require.NoError(t, err) + + mpoolTx, err := client.EthGetTransactionByHash(ctx, &hash) + require.NoError(t, err) + require.Equal(t, hash, mpoolTx.Hash) + + // Wait for message to land on chain + var receipt *api.EthTxReceipt + for i := 0; i < 20; i++ { + receipt, err = client.EthGetTransactionReceipt(ctx, hash) + if err != nil || receipt == nil { + time.Sleep(blocktime) + continue + } + break + } + require.NoError(t, err) + require.NotNil(t, receipt) + require.Equal(t, hash, receipt.TransactionHash) + + // Verify that the chain transaction now has new fields set. + chainTx, err := client.EthGetTransactionByHash(ctx, &hash) + require.NoError(t, err) + require.Equal(t, hash, chainTx.Hash) + + // require that the hashes are identical + require.Equal(t, hash, chainTx.Hash) + require.NotNil(t, chainTx.BlockNumber) + require.Greater(t, uint64(*chainTx.BlockNumber), uint64(0)) + require.NotNil(t, chainTx.BlockHash) + require.NotEmpty(t, *chainTx.BlockHash) + require.NotNil(t, chainTx.TransactionIndex) + require.Equal(t, uint64(*chainTx.TransactionIndex), uint64(0)) // only transaction +} + +// TestTransactionHashLookupSecpFilecoinMessage tests to see if lotus can find a Secp Filecoin Message using the transaction hash +func TestTransactionHashLookupSecpFilecoinMessage(t *testing.T) { + kit.QuietMiningLogs() + + blocktime := 1 * time.Second + client, _, ens := kit.EnsembleMinimal( + t, + kit.MockProofs(), + kit.ThroughRPC(), + kit.EthTxHashLookup(), + ) + ens.InterconnectAll().BeginMining(blocktime) + + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + // get the existing balance from the default wallet to then split it. + bal, err := client.WalletBalance(ctx, client.DefaultKey.Address) + require.NoError(t, err) + + // create a new address where to send funds. + addr, err := client.WalletNew(ctx, types.KTSecp256k1) + require.NoError(t, err) + + toSend := big.Div(bal, big.NewInt(2)) + setupMsg := &types.Message{ + From: client.DefaultKey.Address, + To: addr, + Value: toSend, + } + + setupSmsg, err := client.MpoolPushMessage(ctx, setupMsg, nil) + require.NoError(t, err) + + _, err = client.StateWaitMsg(ctx, setupSmsg.Cid(), 3, api.LookbackNoLimit, true) + require.NoError(t, err) + + // Send message for secp account + secpMsg := &types.Message{ + From: addr, + To: client.DefaultKey.Address, + Value: big.Div(toSend, big.NewInt(2)), + } + + secpSmsg, err := client.MpoolPushMessage(ctx, secpMsg, nil) + require.NoError(t, err) + + hash, err := ethtypes.EthHashFromCid(secpSmsg.Cid()) + require.NoError(t, err) + + mpoolTx, err := client.EthGetTransactionByHash(ctx, &hash) + require.NoError(t, err) + require.Equal(t, hash, mpoolTx.Hash) + + _, err = client.StateWaitMsg(ctx, secpSmsg.Cid(), 3, api.LookbackNoLimit, true) + require.NoError(t, err) + + receipt, err := client.EthGetTransactionReceipt(ctx, hash) + require.NoError(t, err) + require.NotNil(t, receipt) + require.Equal(t, hash, receipt.TransactionHash) + + // Verify that the chain transaction now has new fields set. + chainTx, err := client.EthGetTransactionByHash(ctx, &hash) + require.NoError(t, err) + require.Equal(t, hash, chainTx.Hash) + + // require that the hashes are identical + require.Equal(t, hash, chainTx.Hash) + require.NotNil(t, chainTx.BlockNumber) + require.Greater(t, uint64(*chainTx.BlockNumber), uint64(0)) + require.NotNil(t, chainTx.BlockHash) + require.NotEmpty(t, *chainTx.BlockHash) + require.NotNil(t, chainTx.TransactionIndex) + require.Equal(t, uint64(*chainTx.TransactionIndex), uint64(0)) // only transaction +} diff --git a/itests/kit/node_opts.go b/itests/kit/node_opts.go index a220a0d1b..aed6772c5 100644 --- a/itests/kit/node_opts.go +++ b/itests/kit/node_opts.go @@ -296,3 +296,10 @@ func HistoricFilterAPI(dbpath string) NodeOpt { return nil }) } + +func EthTxHashLookup() NodeOpt { + return WithCfgOpt(func(cfg *config.FullNode) error { + cfg.EthTxHashConfig.EnableEthHashToFilecoinCidMapping = true + return nil + }) +} diff --git a/node/builder_chain.go b/node/builder_chain.go index 541b451b7..ae3c326de 100644 --- a/node/builder_chain.go +++ b/node/builder_chain.go @@ -161,7 +161,6 @@ var ChainNode = Options( Override(new(messagepool.Provider), messagepool.NewProvider), Override(new(messagepool.MpoolNonceAPI), From(new(*messagepool.MessagePool))), Override(new(full.ChainModuleAPI), From(new(full.ChainModule))), - Override(new(full.EthModuleAPI), From(new(full.EthModule))), Override(new(full.GasModuleAPI), From(new(full.GasModule))), Override(new(full.MpoolModuleAPI), From(new(full.MpoolModule))), Override(new(full.StateModuleAPI), From(new(full.StateModule))), @@ -261,6 +260,8 @@ func ConfigFullNode(c interface{}) Option { Override(new(events.EventAPI), From(new(modules.EventAPI))), // in lite-mode Eth event api is provided by gateway ApplyIf(isFullNode, Override(new(full.EthEventAPI), modules.EthEventAPI(cfg.ActorEvent))), + + Override(new(full.EthModuleAPI), modules.EthModuleAPI(cfg.EthTxHashConfig)), ) } diff --git a/node/config/def.go b/node/config/def.go index 7540aa3f7..079023417 100644 --- a/node/config/def.go +++ b/node/config/def.go @@ -107,6 +107,9 @@ func DefaultFullNode() *FullNode { MaxFilterResults: 10000, MaxFilterHeightRange: 2880, // conservative limit of one day }, + EthTxHashConfig: EthTxHashConfig{ + EnableEthHashToFilecoinCidMapping: true, + }, } } diff --git a/node/config/doc_gen.go b/node/config/doc_gen.go index 0da9c7853..ed60cea36 100644 --- a/node/config/doc_gen.go +++ b/node/config/doc_gen.go @@ -391,6 +391,14 @@ see https://lotus.filecoin.io/storage-providers/advanced-configurations/market/# Comment: ``, }, }, + "EthTxHashConfig": []DocField{ + { + Name: "EnableEthHashToFilecoinCidMapping", + Type: "bool", + + Comment: `EnableEthHashToFilecoinCidMapping enables storing a mapping of eth transaction hashes to filecoin message Cids`, + }, + }, "FeeConfig": []DocField{ { Name: "DefaultMaxFee", @@ -434,6 +442,12 @@ see https://lotus.filecoin.io/storage-providers/advanced-configurations/market/# Name: "ActorEvent", Type: "ActorEventConfig", + Comment: ``, + }, + { + Name: "EthTxHashConfig", + Type: "EthTxHashConfig", + Comment: ``, }, }, diff --git a/node/config/types.go b/node/config/types.go index b0f9b63c0..9d9634570 100644 --- a/node/config/types.go +++ b/node/config/types.go @@ -22,12 +22,13 @@ type Common struct { // FullNode is a full node config type FullNode struct { Common - Client Client - Wallet Wallet - Fees FeeConfig - Chainstore Chainstore - Cluster UserRaftConfig - ActorEvent ActorEventConfig + Client Client + Wallet Wallet + Fees FeeConfig + Chainstore Chainstore + Cluster UserRaftConfig + ActorEvent ActorEventConfig + EthTxHashConfig EthTxHashConfig } // // Common @@ -692,3 +693,8 @@ type ActorEventConfig struct { // Set a timeout for subscription clients // Set upper bound on index size } + +type EthTxHashConfig struct { + // EnableEthHashToFilecoinCidMapping enables storing a mapping of eth transaction hashes to filecoin message Cids + EnableEthHashToFilecoinCidMapping bool +} diff --git a/node/impl/full/eth.go b/node/impl/full/eth.go index 71770e847..5d696993e 100644 --- a/node/impl/full/eth.go +++ b/node/impl/full/eth.go @@ -21,9 +21,11 @@ import ( builtintypes "github.com/filecoin-project/go-state-types/builtin" "github.com/filecoin-project/go-state-types/builtin/v10/eam" "github.com/filecoin-project/go-state-types/builtin/v10/evm" + "github.com/filecoin-project/go-state-types/crypto" "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/build" + "github.com/filecoin-project/lotus/chain" "github.com/filecoin-project/lotus/chain/actors" builtinactors "github.com/filecoin-project/lotus/chain/actors/builtin" "github.com/filecoin-project/lotus/chain/events/filter" @@ -43,6 +45,7 @@ type EthModuleAPI interface { EthGetBlockByHash(ctx context.Context, blkHash ethtypes.EthHash, fullTxInfo bool) (ethtypes.EthBlock, error) EthGetBlockByNumber(ctx context.Context, blkNum string, fullTxInfo bool) (ethtypes.EthBlock, error) EthGetTransactionByHash(ctx context.Context, txHash *ethtypes.EthHash) (*ethtypes.EthTx, error) + EthGetTransactionHashByCid(ctx context.Context, cid cid.Cid) (*ethtypes.EthHash, error) EthGetTransactionCount(ctx context.Context, sender ethtypes.EthAddress, blkOpt string) (ethtypes.EthUint64, error) EthGetTransactionReceipt(ctx context.Context, txHash ethtypes.EthHash) (*api.EthTxReceipt, error) EthGetTransactionByBlockHashAndIndex(ctx context.Context, blkHash ethtypes.EthHash, txIndex ethtypes.EthUint64) (ethtypes.EthTx, error) @@ -107,11 +110,10 @@ var ( // accepts as the best parent tipset, based on the blocks it is accumulating // within the HEAD tipset. type EthModule struct { - fx.In - - Chain *store.ChainStore - Mpool *messagepool.MessagePool - StateManager *stmgr.StateManager + Chain *store.ChainStore + Mpool *messagepool.MessagePool + StateManager *stmgr.StateManager + EthTxHashManager *EthTxHashManager ChainAPI MpoolAPI @@ -254,10 +256,21 @@ func (a *EthModule) EthGetTransactionByHash(ctx context.Context, txHash *ethtype return nil, nil } - cid := txHash.ToCid() + c := cid.Undef + if a.EthTxHashManager != nil { + var err error + c, err = a.EthTxHashManager.TransactionHashLookup.LookupCidFromTxHash(*txHash) + if err != nil { + log.Debug("could not find transaction hash %s in lookup table", txHash.String()) + } + } + // This isn't an eth transaction we have the mapping for, so let's look it up as a filecoin message + if c == cid.Undef { + c = txHash.ToCid() + } // first, try to get the cid from mined transactions - msgLookup, err := a.StateAPI.StateSearchMsg(ctx, types.EmptyTSK, cid, api.LookbackNoLimit, true) + msgLookup, err := a.StateAPI.StateSearchMsg(ctx, types.EmptyTSK, c, api.LookbackNoLimit, true) if err == nil { tx, err := newEthTxFromFilecoinMessageLookup(ctx, msgLookup, -1, a.Chain, a.StateAPI) if err == nil { @@ -274,8 +287,8 @@ func (a *EthModule) EthGetTransactionByHash(ctx context.Context, txHash *ethtype } for _, p := range pending { - if p.Cid() == cid { - tx, err := newEthTxFromFilecoinMessage(ctx, p, a.StateAPI) + if p.Cid() == c { + tx, err := NewEthTxFromFilecoinMessage(ctx, p, a.StateAPI) if err != nil { return nil, fmt.Errorf("could not convert Filecoin message into tx: %s", err) } @@ -286,6 +299,11 @@ func (a *EthModule) EthGetTransactionByHash(ctx context.Context, txHash *ethtype return nil, nil } +func (a *EthModule) EthGetTransactionHashByCid(ctx context.Context, cid cid.Cid) (*ethtypes.EthHash, error) { + hash, err := EthTxHashFromFilecoinMessageCid(ctx, cid, a.StateAPI) + return &hash, err +} + func (a *EthModule) EthGetTransactionCount(ctx context.Context, sender ethtypes.EthAddress, blkParam string) (ethtypes.EthUint64, error) { addr, err := sender.ToFilecoinAddress() if err != nil { @@ -305,19 +323,30 @@ func (a *EthModule) EthGetTransactionCount(ctx context.Context, sender ethtypes. } func (a *EthModule) EthGetTransactionReceipt(ctx context.Context, txHash ethtypes.EthHash) (*api.EthTxReceipt, error) { - cid := txHash.ToCid() + c := cid.Undef + if a.EthTxHashManager != nil { + var err error + c, err = a.EthTxHashManager.TransactionHashLookup.LookupCidFromTxHash(txHash) + if err != nil { + log.Debug("could not find transaction hash %s in lookup table", txHash.String()) + } + } + // This isn't an eth transaction we have the mapping for, so let's look it up as a filecoin message + if c == cid.Undef { + c = txHash.ToCid() + } - msgLookup, err := a.StateAPI.StateSearchMsg(ctx, types.EmptyTSK, cid, api.LookbackNoLimit, true) - if err != nil { + msgLookup, err := a.StateAPI.StateSearchMsg(ctx, types.EmptyTSK, c, api.LookbackNoLimit, true) + if err != nil || msgLookup == nil { return nil, nil } tx, err := newEthTxFromFilecoinMessageLookup(ctx, msgLookup, -1, a.Chain, a.StateAPI) - if err != nil { + if err != nil && tx.Hash == ethtypes.EmptyEthHash { return nil, nil } - replay, err := a.StateAPI.StateReplay(ctx, types.EmptyTSK, cid) + replay, err := a.StateAPI.StateReplay(ctx, types.EmptyTSK, c) if err != nil { return nil, nil } @@ -640,11 +669,12 @@ func (a *EthModule) EthSendRawTransaction(ctx context.Context, rawTx ethtypes.Et smsg.Message.Method = builtinactors.MethodSend } - cid, err := a.MpoolAPI.MpoolPush(ctx, smsg) + _, err = a.MpoolAPI.MpoolPush(ctx, smsg) if err != nil { return ethtypes.EmptyEthHash, err } - return ethtypes.EthHashFromCid(cid) + + return ethtypes.EthHashFromTxBytes(rawTx), nil } func (a *EthModule) ethCallToFilecoinMessage(ctx context.Context, tx ethtypes.EthCall) (*types.Message, error) { @@ -791,7 +821,7 @@ func (e *EthEvent) EthGetLogs(ctx context.Context, filterSpec *ethtypes.EthFilte _ = e.uninstallFilter(ctx, f) - return ethFilterResultFromEvents(ces) + return ethFilterResultFromEvents(ces, e.SubManager.StateAPI) } func (e *EthEvent) EthGetFilterChanges(ctx context.Context, id ethtypes.EthFilterID) (*ethtypes.EthFilterResult, error) { @@ -806,11 +836,11 @@ func (e *EthEvent) EthGetFilterChanges(ctx context.Context, id ethtypes.EthFilte switch fc := f.(type) { case filterEventCollector: - return ethFilterResultFromEvents(fc.TakeCollectedEvents(ctx)) + return ethFilterResultFromEvents(fc.TakeCollectedEvents(ctx), e.SubManager.StateAPI) case filterTipSetCollector: return ethFilterResultFromTipSets(fc.TakeCollectedTipSets(ctx)) case filterMessageCollector: - return ethFilterResultFromMessages(fc.TakeCollectedMessages(ctx)) + return ethFilterResultFromMessages(fc.TakeCollectedMessages(ctx), e.SubManager.StateAPI) } return nil, xerrors.Errorf("unknown filter type") @@ -828,7 +858,7 @@ func (e *EthEvent) EthGetFilterLogs(ctx context.Context, id ethtypes.EthFilterID switch fc := f.(type) { case filterEventCollector: - return ethFilterResultFromEvents(fc.TakeCollectedEvents(ctx)) + return ethFilterResultFromEvents(fc.TakeCollectedEvents(ctx), e.SubManager.StateAPI) } return nil, xerrors.Errorf("wrong filter type") @@ -1141,14 +1171,14 @@ type filterEventCollector interface { } type filterMessageCollector interface { - TakeCollectedMessages(context.Context) []cid.Cid + TakeCollectedMessages(context.Context) []*types.SignedMessage } type filterTipSetCollector interface { TakeCollectedTipSets(context.Context) []types.TipSetKey } -func ethFilterResultFromEvents(evs []*filter.CollectedEvent) (*ethtypes.EthFilterResult, error) { +func ethFilterResultFromEvents(evs []*filter.CollectedEvent, sa StateAPI) (*ethtypes.EthFilterResult, error) { res := ðtypes.EthFilterResult{} for _, ev := range evs { log := ethtypes.EthLog{ @@ -1174,11 +1204,10 @@ func ethFilterResultFromEvents(evs []*filter.CollectedEvent) (*ethtypes.EthFilte return nil, err } - log.TransactionHash, err = ethtypes.EthHashFromCid(ev.MsgCid) + log.TransactionHash, err = EthTxHashFromFilecoinMessageCid(context.TODO(), ev.MsgCid, sa) if err != nil { return nil, err } - c, err := ev.TipSetKey.Cid() if err != nil { return nil, err @@ -1213,11 +1242,11 @@ func ethFilterResultFromTipSets(tsks []types.TipSetKey) (*ethtypes.EthFilterResu return res, nil } -func ethFilterResultFromMessages(cs []cid.Cid) (*ethtypes.EthFilterResult, error) { +func ethFilterResultFromMessages(cs []*types.SignedMessage, sa StateAPI) (*ethtypes.EthFilterResult, error) { res := ðtypes.EthFilterResult{} for _, c := range cs { - hash, err := ethtypes.EthHashFromCid(c) + hash, err := EthTxHashFromSignedFilecoinMessage(context.TODO(), c, sa) if err != nil { return nil, err } @@ -1316,7 +1345,7 @@ func (e *ethSubscription) start(ctx context.Context) { var err error switch vt := v.(type) { case *filter.CollectedEvent: - resp.Result, err = ethFilterResultFromEvents([]*filter.CollectedEvent{vt}) + resp.Result, err = ethFilterResultFromEvents([]*filter.CollectedEvent{vt}, e.StateAPI) case *types.TipSet: eb, err := newEthBlockFromFilecoinTipSet(ctx, vt, true, e.Chain, e.ChainAPI, e.StateAPI) if err != nil { @@ -1391,18 +1420,15 @@ func newEthBlockFromFilecoinTipSet(ctx context.Context, ts *types.TipSet, fullTx } gasUsed += msgLookup.Receipt.GasUsed + tx, err := newEthTxFromFilecoinMessageLookup(ctx, msgLookup, txIdx, cs, sa) + if err != nil { + return ethtypes.EthBlock{}, nil + } + if fullTxInfo { - tx, err := newEthTxFromFilecoinMessageLookup(ctx, msgLookup, txIdx, cs, sa) - if err != nil { - return ethtypes.EthBlock{}, nil - } block.Transactions = append(block.Transactions, tx) } else { - hash, err := ethtypes.EthHashFromCid(msg.Cid()) - if err != nil { - return ethtypes.EthBlock{}, err - } - block.Transactions = append(block.Transactions, hash.String()) + block.Transactions = append(block.Transactions, tx.Hash.String()) } } @@ -1454,19 +1480,35 @@ func lookupEthAddress(ctx context.Context, addr address.Address, sa StateAPI) (e return ethtypes.EthAddressFromFilecoinAddress(idAddr) } -func newEthTxFromFilecoinMessage(ctx context.Context, smsg *types.SignedMessage, sa StateAPI) (ethtypes.EthTx, error) { - fromEthAddr, err := lookupEthAddress(ctx, smsg.Message.From, sa) - if err != nil { - return ethtypes.EthTx{}, err +func EthTxHashFromFilecoinMessageCid(ctx context.Context, c cid.Cid, sa StateAPI) (ethtypes.EthHash, error) { + smsg, err := sa.Chain.GetSignedMessage(ctx, c) + if err == nil { + return EthTxHashFromSignedFilecoinMessage(ctx, smsg, sa) } - toEthAddr, err := lookupEthAddress(ctx, smsg.Message.To, sa) - if err != nil { - return ethtypes.EthTx{}, err + return ethtypes.EthHashFromCid(c) +} + +func EthTxHashFromSignedFilecoinMessage(ctx context.Context, smsg *types.SignedMessage, sa StateAPI) (ethtypes.EthHash, error) { + if smsg.Signature.Type == crypto.SigTypeDelegated { + ethTx, err := NewEthTxFromFilecoinMessage(ctx, smsg, sa) + if err != nil { + return ethtypes.EmptyEthHash, err + } + return ethTx.Hash, nil } + return ethtypes.EthHashFromCid(smsg.Cid()) +} + +func NewEthTxFromFilecoinMessage(ctx context.Context, smsg *types.SignedMessage, sa StateAPI) (ethtypes.EthTx, error) { + // Ignore errors here so we can still parse non-eth messages + fromEthAddr, _ := lookupEthAddress(ctx, smsg.Message.From, sa) + toEthAddr, _ := lookupEthAddress(ctx, smsg.Message.To, sa) + toAddr := &toEthAddr input := smsg.Message.Params + var err error // Check to see if we need to decode as contract deployment. // We don't need to resolve the to address, because there's only one form (an ID). if smsg.Message.To == builtintypes.EthereumAddressManagerActorAddr { @@ -1505,26 +1547,38 @@ func newEthTxFromFilecoinMessage(ctx context.Context, smsg *types.SignedMessage, r, s, v = ethtypes.EthBigIntZero, ethtypes.EthBigIntZero, ethtypes.EthBigIntZero } - hash, err := ethtypes.EthHashFromCid(smsg.Cid()) - if err != nil { - return ethtypes.EthTx{}, err - } - tx := ethtypes.EthTx{ - Hash: hash, Nonce: ethtypes.EthUint64(smsg.Message.Nonce), ChainID: ethtypes.EthUint64(build.Eip155ChainId), From: fromEthAddr, To: toAddr, Value: ethtypes.EthBigInt(smsg.Message.Value), Type: ethtypes.EthUint64(2), + Input: input, Gas: ethtypes.EthUint64(smsg.Message.GasLimit), MaxFeePerGas: ethtypes.EthBigInt(smsg.Message.GasFeeCap), MaxPriorityFeePerGas: ethtypes.EthBigInt(smsg.Message.GasPremium), V: v, R: r, S: s, - Input: input, + } + + // This is an eth tx + if smsg.Signature.Type == crypto.SigTypeDelegated { + tx.Hash, err = tx.TxHash() + if err != nil { + return tx, err + } + } else if smsg.Signature.Type == crypto.SigTypeUnknown { // BLS Filecoin message + tx.Hash, err = ethtypes.EthHashFromCid(smsg.Message.Cid()) + if err != nil { + return tx, err + } + } else { // Secp Filecoin Message + tx.Hash, err = ethtypes.EthHashFromCid(smsg.Cid()) + if err != nil { + return tx, err + } } return tx, nil @@ -1537,11 +1591,6 @@ func newEthTxFromFilecoinMessageLookup(ctx context.Context, msgLookup *api.MsgLo if msgLookup == nil { return ethtypes.EthTx{}, fmt.Errorf("msg does not exist") } - cid := msgLookup.Message - txHash, err := ethtypes.EthHashFromCid(cid) - if err != nil { - return ethtypes.EthTx{}, err - } ts, err := cs.LoadTipSet(ctx, msgLookup.TipSet) if err != nil { @@ -1583,10 +1632,21 @@ func newEthTxFromFilecoinMessageLookup(ctx context.Context, msgLookup *api.MsgLo smsg, err := cs.GetSignedMessage(ctx, msgLookup.Message) if err != nil { - return ethtypes.EthTx{}, err + // We couldn't find the signed message, it might be a BLS message, so search for a regular message. + msg, err := cs.GetMessage(ctx, msgLookup.Message) + if err != nil { + return ethtypes.EthTx{}, err + } + smsg = &types.SignedMessage{ + Message: *msg, + Signature: crypto.Signature{ + Type: crypto.SigTypeUnknown, + Data: nil, + }, + } } - tx, err := newEthTxFromFilecoinMessage(ctx, smsg, sa) + tx, err := NewEthTxFromFilecoinMessage(ctx, smsg, sa) if err != nil { return ethtypes.EthTx{}, err } @@ -1597,7 +1657,6 @@ func newEthTxFromFilecoinMessageLookup(ctx context.Context, msgLookup *api.MsgLo ) tx.ChainID = ethtypes.EthUint64(build.Eip155ChainId) - tx.Hash = txHash tx.BlockHash = &blkHash tx.BlockNumber = &bn tx.TransactionIndex = &ti @@ -1701,6 +1760,68 @@ func newEthTxReceipt(ctx context.Context, tx ethtypes.EthTx, lookup *api.MsgLook return receipt, nil } +func (m EthTxHashManager) Apply(ctx context.Context, from, to *types.TipSet) error { + for _, blk := range to.Blocks() { + _, smsgs, err := m.StateAPI.Chain.MessagesForBlock(ctx, blk) + if err != nil { + return err + } + + for _, smsg := range smsgs { + if smsg.Signature.Type != crypto.SigTypeDelegated { + continue + } + + hash, err := EthTxHashFromSignedFilecoinMessage(ctx, smsg, m.StateAPI) + if err != nil { + return err + } + + err = m.TransactionHashLookup.InsertTxHash(hash, smsg.Cid(), int64(to.Height())) + if err != nil { + return err + } + } + } + + return nil +} + +type EthTxHashManager struct { + StateAPI StateAPI + TransactionHashLookup *chain.TransactionHashLookup +} + +func (m EthTxHashManager) Revert(ctx context.Context, from, to *types.TipSet) error { + return nil +} + +func WaitForMpoolUpdates(ctx context.Context, ch <-chan api.MpoolUpdate, manager *EthTxHashManager) { + for { + select { + case <-ctx.Done(): + return + case u := <-ch: + if u.Type != api.MpoolAdd { + continue + } + if u.Message.Signature.Type != crypto.SigTypeDelegated { + continue + } + + ethTx, err := NewEthTxFromFilecoinMessage(ctx, u.Message, manager.StateAPI) + if err != nil { + log.Errorf("error converting filecoin message to eth tx: %s", err) + } + + err = manager.TransactionHashLookup.InsertTxHash(ethTx.Hash, u.Message.Cid(), chain.MemPoolEpoch) + if err != nil { + log.Errorf("error inserting tx mapping to db: %s", err) + } + } + } +} + // decodeLogBytes decodes a CBOR-serialized array into its original form. // // This function swallows errors and returns the original array if it failed diff --git a/node/modules/ethmodule.go b/node/modules/ethmodule.go new file mode 100644 index 000000000..a16b7534d --- /dev/null +++ b/node/modules/ethmodule.go @@ -0,0 +1,84 @@ +package modules + +import ( + "context" + "path/filepath" + + "go.uber.org/fx" + + "github.com/filecoin-project/lotus/chain" + "github.com/filecoin-project/lotus/chain/events" + "github.com/filecoin-project/lotus/chain/messagepool" + "github.com/filecoin-project/lotus/chain/stmgr" + "github.com/filecoin-project/lotus/chain/store" + "github.com/filecoin-project/lotus/node/config" + "github.com/filecoin-project/lotus/node/impl/full" + "github.com/filecoin-project/lotus/node/modules/helpers" + "github.com/filecoin-project/lotus/node/repo" +) + +func EthModuleAPI(cfg config.EthTxHashConfig) func(helpers.MetricsCtx, repo.LockedRepo, fx.Lifecycle, *store.ChainStore, *stmgr.StateManager, EventAPI, *messagepool.MessagePool, full.StateAPI, full.ChainAPI, full.MpoolAPI) (*full.EthModule, error) { + return func(mctx helpers.MetricsCtx, r repo.LockedRepo, lc fx.Lifecycle, cs *store.ChainStore, sm *stmgr.StateManager, evapi EventAPI, mp *messagepool.MessagePool, stateapi full.StateAPI, chainapi full.ChainAPI, mpoolapi full.MpoolAPI) (*full.EthModule, error) { + em := &full.EthModule{ + Chain: cs, + Mpool: mp, + StateManager: sm, + ChainAPI: chainapi, + MpoolAPI: mpoolapi, + StateAPI: stateapi, + } + + if !cfg.EnableEthHashToFilecoinCidMapping { + // mapping functionality disabled. Nothing to do here + return em, nil + } + + dbPath, err := r.SqlitePath() + if err != nil { + return nil, err + } + + transactionHashLookup, err := chain.NewTransactionHashLookup(filepath.Join(dbPath + "txHash.db")) + if err != nil { + return nil, err + } + + lc.Append(fx.Hook{ + OnStop: func(ctx context.Context) error { + return transactionHashLookup.Close() + }, + }) + + ethTxHashManager := full.EthTxHashManager{ + StateAPI: stateapi, + TransactionHashLookup: transactionHashLookup, + } + + em.EthTxHashManager = ðTxHashManager + + const ChainHeadConfidence = 1 + + ctx := helpers.LifecycleCtx(mctx, lc) + lc.Append(fx.Hook{ + OnStart: func(context.Context) error { + ev, err := events.NewEventsWithConfidence(ctx, &evapi, ChainHeadConfidence) + if err != nil { + return err + } + + // Tipset listener + _ = ev.Observe(ðTxHashManager) + + ch, err := mp.Updates(ctx) + if err != nil { + return err + } + go full.WaitForMpoolUpdates(ctx, ch, ðTxHashManager) + + return nil + }, + }) + + return em, nil + } +} diff --git a/node/repo/fsrepo.go b/node/repo/fsrepo.go index 68550e389..bd387babf 100644 --- a/node/repo/fsrepo.go +++ b/node/repo/fsrepo.go @@ -37,6 +37,7 @@ const ( fsDatastore = "datastore" fsLock = "repo.lock" fsKeystore = "keystore" + fsSqlite = "sqlite" ) func NewRepoTypeFromString(t string) RepoType { @@ -411,6 +412,10 @@ type fsLockedRepo struct { ssErr error ssOnce sync.Once + sqlPath string + sqlErr error + sqlOnce sync.Once + storageLk sync.Mutex configLk sync.Mutex } @@ -515,6 +520,21 @@ func (fsr *fsLockedRepo) SplitstorePath() (string, error) { return fsr.ssPath, fsr.ssErr } +func (fsr *fsLockedRepo) SqlitePath() (string, error) { + fsr.sqlOnce.Do(func() { + path := fsr.join(fsSqlite) + + if err := os.MkdirAll(path, 0755); err != nil { + fsr.sqlErr = err + return + } + + fsr.sqlPath = path + }) + + return fsr.sqlPath, fsr.sqlErr +} + // join joins path elements with fsr.path func (fsr *fsLockedRepo) join(paths ...string) string { return filepath.Join(append([]string{fsr.path}, paths...)...) diff --git a/node/repo/interface.go b/node/repo/interface.go index dd0839559..328862b92 100644 --- a/node/repo/interface.go +++ b/node/repo/interface.go @@ -69,6 +69,9 @@ type LockedRepo interface { // SplitstorePath returns the path for the SplitStore SplitstorePath() (string, error) + // SqlitePath returns the path for the Sqlite database + SqlitePath() (string, error) + // Returns config in this repo Config() (interface{}, error) SetConfig(func(interface{})) error diff --git a/node/repo/memrepo.go b/node/repo/memrepo.go index 61d960872..7817776a9 100644 --- a/node/repo/memrepo.go +++ b/node/repo/memrepo.go @@ -277,6 +277,14 @@ func (lmem *lockedMemRepo) SplitstorePath() (string, error) { return splitstorePath, nil } +func (lmem *lockedMemRepo) SqlitePath() (string, error) { + sqlitePath := filepath.Join(lmem.Path(), "sqlite") + if err := os.MkdirAll(sqlitePath, 0755); err != nil { + return "", err + } + return sqlitePath, nil +} + func (lmem *lockedMemRepo) ListDatastores(ns string) ([]int64, error) { return nil, nil }