From 0921c911a346d6c89094ec503a3a3b6809753fc1 Mon Sep 17 00:00:00 2001 From: hunjixin <1084400399@qq.com> Date: Wed, 20 Sep 2023 17:29:17 +0800 Subject: [PATCH 001/394] feat: add confidence for init miner --- cmd/lotus-miner/init.go | 29 ++++++++++++++++++----------- cmd/lotus-miner/init_restore.go | 3 +-- cmd/lotus-miner/init_service.go | 2 +- documentation/en/cli-lotus-miner.md | 1 + 4 files changed, 21 insertions(+), 14 deletions(-) diff --git a/cmd/lotus-miner/init.go b/cmd/lotus-miner/init.go index c109e85b9..8659e3d29 100644 --- a/cmd/lotus-miner/init.go +++ b/cmd/lotus-miner/init.go @@ -120,6 +120,11 @@ var initCmd = &cli.Command{ Name: "from", Usage: "select which address to send actor creation message from", }, + &cli.Uint64Flag{ + Name: "confidence", + Usage: "number of block confirmations to wait for", + Value: build.MessageConfidence, + }, }, Subcommands: []*cli.Command{ restoreCmd, @@ -146,6 +151,8 @@ var initCmd = &cli.Command{ return xerrors.Errorf("failed to parse gas-price flag: %s", err) } + confidence := cctx.Uint64("confidence") + symlink := cctx.Bool("symlink-imported-sectors") if symlink { log.Info("will attempt to symlink to imported sectors") @@ -265,7 +272,7 @@ var initCmd = &cli.Command{ } } - if err := storageMinerInit(ctx, cctx, api, r, ssize, gasPrice); err != nil { + if err := storageMinerInit(ctx, cctx, api, r, ssize, gasPrice, confidence); err != nil { log.Errorf("Failed to initialize lotus-miner: %+v", err) path, err := homedir.Expand(repoPath) if err != nil { @@ -414,7 +421,7 @@ func findMarketDealID(ctx context.Context, api v1api.FullNode, deal markettypes. return 0, xerrors.New("deal not found") } -func storageMinerInit(ctx context.Context, cctx *cli.Context, api v1api.FullNode, r repo.Repo, ssize abi.SectorSize, gasPrice types.BigInt) error { +func storageMinerInit(ctx context.Context, cctx *cli.Context, api v1api.FullNode, r repo.Repo, ssize abi.SectorSize, gasPrice types.BigInt, confidence uint64) error { lr, err := r.Lock(repo.StorageMiner) if err != nil { return err @@ -501,7 +508,7 @@ func storageMinerInit(ctx context.Context, cctx *cli.Context, api v1api.FullNode return xerrors.Errorf("failed to start up genesis miner: %w", err) } - cerr := configureStorageMiner(ctx, api, a, peerid, gasPrice) + cerr := configureStorageMiner(ctx, api, a, peerid, gasPrice, confidence) if err := m.Stop(ctx); err != nil { log.Error("failed to shut down miner: ", err) @@ -541,13 +548,13 @@ func storageMinerInit(ctx context.Context, cctx *cli.Context, api v1api.FullNode } } - if err := configureStorageMiner(ctx, api, a, peerid, gasPrice); err != nil { + if err := configureStorageMiner(ctx, api, a, peerid, gasPrice, confidence); err != nil { return xerrors.Errorf("failed to configure miner: %w", err) } addr = a } else { - a, err := createStorageMiner(ctx, api, ssize, peerid, gasPrice, cctx) + a, err := createStorageMiner(ctx, api, ssize, peerid, gasPrice, confidence, cctx) if err != nil { return xerrors.Errorf("creating miner failed: %w", err) } @@ -589,7 +596,7 @@ func makeHostKey(lr repo.LockedRepo) (crypto.PrivKey, error) { return pk, nil } -func configureStorageMiner(ctx context.Context, api v1api.FullNode, addr address.Address, peerid peer.ID, gasPrice types.BigInt) error { +func configureStorageMiner(ctx context.Context, api v1api.FullNode, addr address.Address, peerid peer.ID, gasPrice types.BigInt, confidence uint64) error { mi, err := api.StateMinerInfo(ctx, addr, types.EmptyTSK) if err != nil { return xerrors.Errorf("getWorkerAddr returned bad address: %w", err) @@ -615,7 +622,7 @@ func configureStorageMiner(ctx context.Context, api v1api.FullNode, addr address } log.Info("Waiting for message: ", smsg.Cid()) - ret, err := api.StateWaitMsg(ctx, smsg.Cid(), build.MessageConfidence, lapi.LookbackNoLimit, true) + ret, err := api.StateWaitMsg(ctx, smsg.Cid(), confidence, lapi.LookbackNoLimit, true) if err != nil { return err } @@ -627,7 +634,7 @@ func configureStorageMiner(ctx context.Context, api v1api.FullNode, addr address return nil } -func createStorageMiner(ctx context.Context, api v1api.FullNode, ssize abi.SectorSize, peerid peer.ID, gasPrice types.BigInt, cctx *cli.Context) (address.Address, error) { +func createStorageMiner(ctx context.Context, api v1api.FullNode, ssize abi.SectorSize, peerid peer.ID, gasPrice types.BigInt, confidence uint64, cctx *cli.Context) (address.Address, error) { var err error var owner address.Address if cctx.String("owner") != "" { @@ -679,7 +686,7 @@ func createStorageMiner(ctx context.Context, api v1api.FullNode, ssize abi.Secto log.Infof("Initializing worker account %s, message: %s", worker, signed.Cid()) log.Infof("Waiting for confirmation") - mw, err := api.StateWaitMsg(ctx, signed.Cid(), build.MessageConfidence, lapi.LookbackNoLimit, true) + mw, err := api.StateWaitMsg(ctx, signed.Cid(), confidence, lapi.LookbackNoLimit, true) if err != nil { return address.Undef, xerrors.Errorf("waiting for worker init: %w", err) } @@ -703,7 +710,7 @@ func createStorageMiner(ctx context.Context, api v1api.FullNode, ssize abi.Secto log.Infof("Initializing owner account %s, message: %s", worker, signed.Cid()) log.Infof("Waiting for confirmation") - mw, err := api.StateWaitMsg(ctx, signed.Cid(), build.MessageConfidence, lapi.LookbackNoLimit, true) + mw, err := api.StateWaitMsg(ctx, signed.Cid(), confidence, lapi.LookbackNoLimit, true) if err != nil { return address.Undef, xerrors.Errorf("waiting for owner init: %w", err) } @@ -752,7 +759,7 @@ func createStorageMiner(ctx context.Context, api v1api.FullNode, ssize abi.Secto log.Infof("Pushed CreateMiner message: %s", signed.Cid()) log.Infof("Waiting for confirmation") - mw, err := api.StateWaitMsg(ctx, signed.Cid(), build.MessageConfidence, lapi.LookbackNoLimit, true) + mw, err := api.StateWaitMsg(ctx, signed.Cid(), confidence, lapi.LookbackNoLimit, true) if err != nil { return address.Undef, xerrors.Errorf("waiting for createMiner message: %w", err) } diff --git a/cmd/lotus-miner/init_restore.go b/cmd/lotus-miner/init_restore.go index 7e28729bb..272754c23 100644 --- a/cmd/lotus-miner/init_restore.go +++ b/cmd/lotus-miner/init_restore.go @@ -80,8 +80,7 @@ var restoreCmd = &cli.Command{ } log.Info("Configuring miner actor") - - if err := configureStorageMiner(ctx, api, maddr, peerid, big.Zero()); err != nil { + if err := configureStorageMiner(ctx, api, maddr, peerid, big.Zero(), cctx.Uint64("confidence")); err != nil { return err } diff --git a/cmd/lotus-miner/init_service.go b/cmd/lotus-miner/init_service.go index 235e4e4c8..876313941 100644 --- a/cmd/lotus-miner/init_service.go +++ b/cmd/lotus-miner/init_service.go @@ -105,7 +105,7 @@ var serviceCmd = &cli.Command{ if es.Contains(MarketsService) { log.Info("Configuring miner actor") - if err := configureStorageMiner(ctx, api, maddr, peerid, big.Zero()); err != nil { + if err := configureStorageMiner(ctx, api, maddr, peerid, big.Zero(), cctx.Uint64("confidence")); err != nil { return err } } diff --git a/documentation/en/cli-lotus-miner.md b/documentation/en/cli-lotus-miner.md index 8406b07cc..ecfea092c 100644 --- a/documentation/en/cli-lotus-miner.md +++ b/documentation/en/cli-lotus-miner.md @@ -66,6 +66,7 @@ OPTIONS: --no-local-storage don't use storageminer repo for sector storage (default: false) --gas-premium value set gas premium for initialization messages in AttoFIL (default: "0") --from value select which address to send actor creation message from + --confidence value number of block confirmations to wait for (default: 5) --help, -h show help ``` From e425322c2bd1fba6e4e37baae4a54d00fffde6d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 29 Nov 2023 17:17:34 +0100 Subject: [PATCH 002/394] misc: Drop the raft-cluster experiment --- .circleci/config.yml | 6 - api/api_full.go | 3 - api/docgen/docgen.go | 4 - api/mocks/mock_full.go | 30 - api/proxy_gen.go | 26 - api/types.go | 63 -- build/openrpc/full.json.gz | Bin 34820 -> 34608 bytes build/openrpc/gateway.json.gz | Bin 11872 -> 11871 bytes build/openrpc/miner.json.gz | Bin 16371 -> 16373 bytes build/openrpc/worker.json.gz | Bin 6051 -> 6051 bytes .../messagesigner/messagesigner_consensus.go | 98 --- documentation/en/api-v1-unstable-methods.md | 30 - documentation/en/default-lotus-config.toml | 62 -- itests/kit/ensemble.go | 4 - itests/raft_messagesigner_test.go | 577 ------------------ lib/consensus/raft/config.go | 135 ---- lib/consensus/raft/consensus.go | 512 ---------------- lib/consensus/raft/interfaces.go | 41 -- lib/consensus/raft/raft.go | 563 ----------------- node/builder.go | 1 - node/builder_chain.go | 13 - node/config/def.go | 23 - node/config/doc_gen.go | 68 --- node/config/types.go | 28 - node/impl/full.go | 9 - node/impl/full/mpool.go | 16 - node/impl/full/raft.go | 50 -- node/modules/rpc.go | 55 -- 28 files changed, 2417 deletions(-) delete mode 100644 chain/messagesigner/messagesigner_consensus.go delete mode 100644 itests/raft_messagesigner_test.go delete mode 100644 lib/consensus/raft/config.go delete mode 100644 lib/consensus/raft/consensus.go delete mode 100644 lib/consensus/raft/interfaces.go delete mode 100644 lib/consensus/raft/raft.go delete mode 100644 node/impl/full/raft.go delete mode 100644 node/modules/rpc.go diff --git a/.circleci/config.yml b/.circleci/config.yml index 20701f7d5..7987060d4 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -858,12 +858,6 @@ workflows: - build suite: itest-pending_deal_allocation target: "./itests/pending_deal_allocation_test.go" - - test: - name: test-itest-raft_messagesigner - requires: - - build - suite: itest-raft_messagesigner - target: "./itests/raft_messagesigner_test.go" - test: name: test-itest-remove_verifreg_datacap requires: diff --git a/api/api_full.go b/api/api_full.go index 4ae2ea531..3dc7f8bb2 100644 --- a/api/api_full.go +++ b/api/api_full.go @@ -879,9 +879,6 @@ type FullNode interface { // LOTUS_BACKUP_BASE_PATH environment variable set to some path, and that // the path specified when calling CreateBackup is within the base path CreateBackup(ctx context.Context, fpath string) error //perm:admin - - RaftState(ctx context.Context) (*RaftStateData, error) //perm:read - RaftLeader(ctx context.Context) (peer.ID, error) //perm:read } // reverse interface to the client, called after EthSubscribe diff --git a/api/docgen/docgen.go b/api/docgen/docgen.go index 018629600..70024d3db 100644 --- a/api/docgen/docgen.go +++ b/api/docgen/docgen.go @@ -355,10 +355,6 @@ func init() { addExample(map[string]bitfield.BitField{ "": bitfield.NewFromSet([]uint64{5, 6, 7, 10}), }) - addExample(&api.RaftStateData{ - NonceMap: make(map[address.Address]uint64), - MsgUuids: make(map[uuid.UUID]*types.SignedMessage), - }) addExample(http.Header{ "Authorization": []string{"Bearer ey.."}, diff --git a/api/mocks/mock_full.go b/api/mocks/mock_full.go index 92b719550..ed9fe740e 100644 --- a/api/mocks/mock_full.go +++ b/api/mocks/mock_full.go @@ -2919,36 +2919,6 @@ func (mr *MockFullNodeMockRecorder) PaychVoucherSubmit(arg0, arg1, arg2, arg3, a return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PaychVoucherSubmit", reflect.TypeOf((*MockFullNode)(nil).PaychVoucherSubmit), arg0, arg1, arg2, arg3, arg4) } -// RaftLeader mocks base method. -func (m *MockFullNode) RaftLeader(arg0 context.Context) (peer.ID, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "RaftLeader", arg0) - ret0, _ := ret[0].(peer.ID) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// RaftLeader indicates an expected call of RaftLeader. -func (mr *MockFullNodeMockRecorder) RaftLeader(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RaftLeader", reflect.TypeOf((*MockFullNode)(nil).RaftLeader), arg0) -} - -// RaftState mocks base method. -func (m *MockFullNode) RaftState(arg0 context.Context) (*api.RaftStateData, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "RaftState", arg0) - ret0, _ := ret[0].(*api.RaftStateData) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// RaftState indicates an expected call of RaftState. -func (mr *MockFullNodeMockRecorder) RaftState(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RaftState", reflect.TypeOf((*MockFullNode)(nil).RaftState), arg0) -} - // Session mocks base method. func (m *MockFullNode) Session(arg0 context.Context) (uuid.UUID, error) { m.ctrl.T.Helper() diff --git a/api/proxy_gen.go b/api/proxy_gen.go index 8adcbc189..589ae8f56 100644 --- a/api/proxy_gen.go +++ b/api/proxy_gen.go @@ -453,10 +453,6 @@ type FullNodeMethods struct { PaychVoucherSubmit func(p0 context.Context, p1 address.Address, p2 *paych.SignedVoucher, p3 []byte, p4 []byte) (cid.Cid, error) `perm:"sign"` - RaftLeader func(p0 context.Context) (peer.ID, error) `perm:"read"` - - RaftState func(p0 context.Context) (*RaftStateData, error) `perm:"read"` - StateAccountKey func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (address.Address, error) `perm:"read"` StateActorCodeCIDs func(p0 context.Context, p1 abinetwork.Version) (map[string]cid.Cid, error) `perm:"read"` @@ -3223,28 +3219,6 @@ func (s *FullNodeStub) PaychVoucherSubmit(p0 context.Context, p1 address.Address return *new(cid.Cid), ErrNotSupported } -func (s *FullNodeStruct) RaftLeader(p0 context.Context) (peer.ID, error) { - if s.Internal.RaftLeader == nil { - return *new(peer.ID), ErrNotSupported - } - return s.Internal.RaftLeader(p0) -} - -func (s *FullNodeStub) RaftLeader(p0 context.Context) (peer.ID, error) { - return *new(peer.ID), ErrNotSupported -} - -func (s *FullNodeStruct) RaftState(p0 context.Context) (*RaftStateData, error) { - if s.Internal.RaftState == nil { - return nil, ErrNotSupported - } - return s.Internal.RaftState(p0) -} - -func (s *FullNodeStub) RaftState(p0 context.Context) (*RaftStateData, error) { - return nil, ErrNotSupported -} - func (s *FullNodeStruct) StateAccountKey(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (address.Address, error) { if s.Internal.StateAccountKey == nil { return *new(address.Address), ErrNotSupported diff --git a/api/types.go b/api/types.go index 93ed4083f..7fd607750 100644 --- a/api/types.go +++ b/api/types.go @@ -69,11 +69,6 @@ type MessageSendSpec struct { MaximizeFeeCap bool } -type MpoolMessageWhole struct { - Msg *types.Message - Spec *MessageSendSpec -} - // GraphSyncDataTransfer provides diagnostics on a data transfer happening over graphsync type GraphSyncDataTransfer struct { // GraphSync request id for this transfer @@ -351,64 +346,6 @@ type ForkUpgradeParams struct { UpgradeWatermelonHeight abi.ChainEpoch } -type NonceMapType map[address.Address]uint64 -type MsgUuidMapType map[uuid.UUID]*types.SignedMessage - -type RaftStateData struct { - NonceMap NonceMapType - MsgUuids MsgUuidMapType -} - -func (n *NonceMapType) MarshalJSON() ([]byte, error) { - marshalled := make(map[string]uint64) - for a, n := range *n { - marshalled[a.String()] = n - } - return json.Marshal(marshalled) -} - -func (n *NonceMapType) UnmarshalJSON(b []byte) error { - unmarshalled := make(map[string]uint64) - err := json.Unmarshal(b, &unmarshalled) - if err != nil { - return err - } - *n = make(map[address.Address]uint64) - for saddr, nonce := range unmarshalled { - a, err := address.NewFromString(saddr) - if err != nil { - return err - } - (*n)[a] = nonce - } - return nil -} - -func (m *MsgUuidMapType) MarshalJSON() ([]byte, error) { - marshalled := make(map[string]*types.SignedMessage) - for u, msg := range *m { - marshalled[u.String()] = msg - } - return json.Marshal(marshalled) -} - -func (m *MsgUuidMapType) UnmarshalJSON(b []byte) error { - unmarshalled := make(map[string]*types.SignedMessage) - err := json.Unmarshal(b, &unmarshalled) - if err != nil { - return err - } - *m = make(map[uuid.UUID]*types.SignedMessage) - for suid, msg := range unmarshalled { - u, err := uuid.Parse(suid) - if err != nil { - return err - } - (*m)[u] = msg - } - return nil -} - // ChainExportConfig holds configuration for chain ranged exports. type ChainExportConfig struct { WriteBufferSize int diff --git a/build/openrpc/full.json.gz b/build/openrpc/full.json.gz index 99426649275b903af8f200301ae779290cfda9cb..3aa6bc16b201c89b6541b5ac3ff4a9c84c666216 100644 GIT binary patch delta 33358 zcmZUaQ*dBWv$kV96HRQ}wr$(CC)v@&=ESybClebJ+nm_RneRJQr~az{W>@cZyU=g< z`*cV=SY0ewJOz+6{O3{bs@wSrKRiS%nmQmXpi7Qs`vU{>DoQA_!5qg`VM>cE6)ODd z)4g!+d9@If#CYc($!A@6u)5ozH*75A5y}6N>qy`X|6V{@z;fsP0;Y?X_wvD)lgjKD zn!2f{?OSt{gHP{kH_je`yY}_Y+YCV@fAwZq7RBJj<@qeDH2*uw2n0`tt7BhR@~oG!KMDt- zhn2vD9Gc zrlZ>G%N!ug3faXPXFL)X5{=bUzDs*ftI^xujWYUs8~NTr0(`z$)^3~Lts;azbq5>Z zI9G^6VP_LFg(C0@{b+H58a4ytDse?>3+v;m@f7#@FaQzdQbp%M9>)5<-HSvuc#D4W zXAl1Ku}3n_{~ay(?Flbz zG=`>~3MK)(-HRnqK(tpT=fU?x!LQf!OvC3IMO>sv`_Vp2=&}bROI>e#VKxPEAy4B zxX#pXlr{fs+`bP=wic$)+3J{dm9pgZjvl?6NB%#Cl)<)iSCF zR`q3Dwt|Lx292=zRE7eN$Dttt${7ht0W2J2mV~2xI6$%tV8p)#)n1JUOF>aS zjWzL6s=fZ+4%iKl|0VzvNJdkkzH>_Qq9P(nnOO3}&V(+=L)bRvf*K%=4D;d< zCr)C49s>(d08v}Ji^S{Z1*TvSeo1mWIfvt2ZG$TWMy)ht(@bJaBndWa} z{5>cl2YN854|{(KtN~b-65IS4Dbc6j$*(YpTeWC#HgLtK)U>Gt?m=^-KR8m`S z84ACinc(NB9>eG45!%|_qi z^;$PndqEKnOGFWfM~!ExzqemQ#6}8&1q3{=G}5G$G1x3(v=1{h1S5z!lhIB)S@s(IPrJ z>l<=FX&df(J7}e(uKe1_Daw!vL^B8Oe!x1H(ZXKD0K}rcE-h?mb7*36KYz@EG!FSf zK<2!y4&qI%h^o|jcj3Rhf+C1|F@`1SKq(r(!rwN4&PA}3@SNdH55wN*7H@39_cnh}t<7&ZrD;|NR+r#w}CXOuT;~#Bs`ko>3ZY^v;P+ z`?nc2KVWG#Zjqzre4LQmBD|uqWqjYRXkH z+pU~MvN_8YYaJ1Nu!6s7rZ93K zb3qbx32P1wqqonDCDqG`@Td+3utH#yFu;KxjFE~&1h@2YDt0^(LlYo?iD`XBD(%q{ z_CtixIiPpN`_Xz1&>s=}J*79nbE1Yc{2lB}WxOJu{~R9V;ue8f^4WMtmq-oLC#MMgJPhpF#Wk`eAt; zN9CmSD?N?vQrnwwUiXXZY5 zhJ~o!GcOClMHw?@$N&1|zRhYdZX__OgmV|>nkzY9_iD58^lI}#|L-*{&Xz(}FYsi+ zDe5Ru`*OL`-5Ki1TlFL>cM0hdbUxYkfE*Fc0l>ZTTO&}f8Xf&Sd4|U`ZWX1lJ^PSi zeD&y4tqv){l(nI6Dy#UD3_B~&*igbRem+-3I0YiHqFZ~}QUkyCgDis*S2GGih%=U; zc`>&=CL^is@)rYn5q+hOhJKDZE1=`_kzF@4^H$ewcqA()z2;#WT?teUhHD|t4jhjSqI0d*~47!j#PR+#EC@5=Sl=g%usX&8s1q3frEDogvaL?FfyXS;-X z@JXV9r({y>j=sIKY{X^fFgWtUzS_X; z;}$)>cAmMEf=9e{x%`eNzs?|7%&$~k>f2M*M&ATs@wl9ndWh2d@=$v*9V5$=U-u@# zS0slgG(0~vE@Y{M<2lw3Gr*`>|8GZ+vsfX6W5cWaoPs7Ga%F;m@jys&A|r?LHJy7Y z<6#%85VwyXPg@X+LS~6U-?yd}kfOh(-azMVPcoSksN0c__2<>S%Fr@mAMw1R&=XpM zCnGyi7Pc!@ec4$wbcCvyx2SzDwzJ$l@?@YIkA!@mS>qhmz1;}`1Bf98C&==q3da4m z)qMP*@#DT~!&$jlWx+A}NO7mQW`8nw?8@2KFgoWxl(?bV!!PxsV*0J89Vwx^V~Cv) z1$t&hb@E%cn_Qyzy>3yw_Wdj>wtd z32~Qqt;TX!uY8bs(-MI$`E6MNZpuT7k6j#;C@E|5Q>y^B7LZ!i#!kUU`<88|pmuAh5 z7S{d=>Sy0dk05PZ8@gWzNuFOI&$-KK_;HDdN%m9;BhIECP|@+y;Npy!_!Ly`~NQ@sUfQxup?I1~Sq6v|}Q5>Im zQiYX)A+H_&Muso6BN{8Bejh28E=`iA>S@9Ryd_|D#+gKM=Z5Tw`UuizbeL2X!WO(^e(YIPh*euXEmv8mFVtZ28Ru`<2I)H>_1^!6qI_X0Bl z65R;%cqbF9ReeP+SMBT~#dUvae>l}+qwyD)MO?9ZuLYTvUO8R-I~b`Z5W=ld=sN}3 zS4J5n6$%dFpYzk*C+|^|24y>9&P9I6IhrNSB4Y!J z>%MR9&>qtR+}7TDk{Z=}HZSubmFE2lZ9D~*pqiCPlx%J&;7Hk!N(S1*&#U=8mA|x2 znyC>+oFa!z3AwY>ot`lwwDGFK2lrRmx^JXhcEbv->T^hum=n^N?vb`yOswa$aMmp{ zTCk?7lascWhbeNWylq)Ema6y6%`brRs8-B|M!Rxc|JZPyq1lQFBL+l%^VW)HNTyvj zzec@Mx%3=N)}AU0zH)2&e^sZ|r#k?Ok*fz>ai zjsSiVG&L0xyyu!Or<`~;rS(PalwJ^awRZ>XF3K1jXKWtHx^7(Zn)J&KTWDVmGoQBS~Zc;kv@5#KmL z&=ux$_9-|QQu0&X3RT_6N`j9^fQc4X61iRGa)5}Y0SMJWs2>Omije<&2o?2u6fCt zA%u#OF1fg;DBOA zzDo=1*)bb;2c6=N-23XRd+~WX9=KXIf2Uf|TVV$i2V`0gJ!*fdLqG7I9PHx~URnyJ zNnO-gru_4iBlCQ4g9}q=3qktduDkXhx1K$PgmT+<%6r&Fxp($=oSG{$C)~(_t9s7* zD)9b(<5dq-7mx@c6yD}8Xk68HT}`O&Bc-c=Ayo?|HPcZJ03R3}!; z;0+IcHyzzIhk?yZCUR3iue32%!Rt(mYB|EU;PEKfl6&0(xp(nzk{@YtyG#KYQB z_rZx|K`&mD(FIP7`!=DDf9S6;+b5ZC%$j6WZZ3f=sTc)Td?PTQE?4pEGX=6cP6O?4 z4OV2j)g}Q!+^6_yD>AVC@lpHra*ca`)9dxu_!=J`_=*!fUf&!Ue8L)xy9n`b>w*^q zI$r(&H`2bj-Xf1D2?gBMaW@N(pZ@Aj`jO%@sL0G6fuGDl!>iUL&n)!n(mhNxY{9$i zd%6V|r}g>K6Ttm!ar{J554IA62+NI8AgxR^u_!|xM47NB#q{{GoB6z5 zYc*RV-;1^Hm%O@FmY&w$LbmZi9_b6{I+JNf#IP`=h$vw;0lZn;N~|=e{{Ev>^XlZe zvcy+nON`E>9-2BB?E zNswyld5z;zpEQX{GdYc&UC$;e5uWT3 zZBIwFOG<4dq=?KtoW$*$tTk;sr#6wM%>I?wO)$*#G0kU7C73aJRPF|B{hZ2k`M7$4 zVmH4eI&eyk{*hBSCNikJboN^#`cxMsH}ML4> z+!FDSd(-T!s94rFfx2vbZRwK1#JO*AEp$B}JUH2N=3D9B zH2D=%93r-X8pjih7j|@C?V9gbrE*A(!>}UyjrSn=7ZptIk8*QpG-g^D9dwY#zH)l~ z3iK&bYMW&5H|noW!@#t%*Oxns@UDpzzPpO=D2a6)WR(KqpxF8l#Bg5H7(F^v8#MP{ zU%Qt@Ehl8UeE_=eO=P&7og+y~rm zN$F~r+F1lbdmbKc0ZV)g+7d=u=t+TId->isaA57SvZhb7Z3rL7nA=3&qWgw@*M>q@ zJ?f~lA7oE)p-B|6P_{dh{=LBbq`MAb>Poa$tK|1|1SZ2K0z7;6*itCzq1M(>)XeUw z@{%$METSNh3q%qdCK2V&?`;2?Lz)KZCSFZm8~C?=49oWPqk2(7vzC^&*J|0#vvS0o z<_bx^m@I(f84BuQ%smA)WV7o!Y+F*e+B}_x-?(!8de0f6IBe$s+|UAq!r&wuS5L zLEUr~DwcA<-7%o#@?kVpDSaq~KZg!UnPjVA6$kO*wl8@RcokHls@EQ>oa5xVB_fu( zj>tlrWL8_3rI){R9RibCT(epY$uqx~tuA|(6SmPFdx}&(mNOvngVAm^+)pkp5s_b^ z?gWHv^v<3L-LAaKhUnF(*?U|V8)nxP{9ermtL?3CKU1WK>Ahvg*VFU6C)nq>`;Kp?qyMnS2&HaFY_ z@(wZ}!~9-{itEa(t9ftL%v|3gHDdv3B0m>j;%&31SuoJVQaFsz8?tr&itv417G>!d zLc{lT1AOQ(TM?!ouiZym+>afrc(=E>Q?O_pp$B>LU3hf&;=?MshzfSdy>4VM_$2M> zwUotqHz=bc^vIOlX1q!iyhp8q+Jz$9y@Eho6!j5Nav$qo%Gq7$hv={K!l-LT`RE{ig3|#5`wea+iq8ywl!m9HvLZzjvN*#s>-HU% zP4a(u9M-PD_**zPcsg?D7-oNmI`-l(=ai*=|1JzrPm-KznqchznrtigF)CQMjgnxi z2BMV0NytQqhPC7ZrS-Hep!C1#a~3OX@HV6zUPCKD=|Tx*gn+GS3E{$9nS$EW<*~DH z%#T_*ny=Ie;bK}LirAv)1kZ*-uKo9S5G+?wYbW%jU832#SHQT$b0O`O6+G2imQS!T5`AcHlSp zRadvp>qQHBR6DHg>-uOhneXLjGS0BBj6deBs=daWHdJKHPYlV*RNT~kj|PDvr4-v9 zaL`m7-w3>kUe3F@O0~I7pW|3x=*~)~fmAbb+7gARsvhB(O2$JX$J0}EvO3oTq95ol zuLp&!;hRrj`Vo|A$)bjQb4A*vHST9MjjFBNyzRmv6JiV1!BQ-GMg=FXRb~gbbplhR z=j-(jna8IKiw(o0vc^)*)w%#pu$|7uC*9$^O`1E)Wim|?_))HaFoC?aS4Iw1refnX z%SQE7I_1LRvY90ZE23+)T6WmnaZ8I^tZuqB>zI+CjAfl;Vz0m--x$<(HMF-qBKcb6 zX>@GuB&L*df!vk_P*!DGo}rGZ?esS|i&6!5x;dMxqgvS}Yh>&i$sFKgZ)t>M=CdRF zqbgw^(e+~e+goO?N$(u7K&ECdDQo}H5jP&C zBlj@>H+jx~y00kYEzs*MHc0T}_cQSK*Nkep;qjicVacjtujJGr83uIe_VkAn^x;=s zwCwEcxRlOMwyap$g*e^9vc=z}IhD!1-H&3c7xsT@rWIwL9|2%Vc!5!gF=j9@hsfD3 zVlf4*bLn#9sGrZA*Oi3&G?C=;m6?59aWF^vn zr(mL~9}vB&XB{=^G&zV=Tg`B^<6YU{xxll6l)9mnHZ2I{q1%QxlB^^T@BXVF<6u*8 zMu*7Ue-B^)hNkB4d^Qu)%|<8EMr{ST*T#uq+QQG)T-bJh;6-o_e(J~ys&~sA3^SV< ziemj7P(cS1;b)Q2!*rj`uTL%TX3m-IHf?SXSEI<~5>TYLi(r7apq7<4G*lce>ttPN zXndjK?&Nu_<>( zM)HrVoVgT_lF~YXYV@ZFZfDjBdh;G!p`u~7sh!Po8za$_ zTNsA}m0C54H%xrhmp>G*y6qD*}F!a9G-R|+zf-h_Hx;19Jz9Jq+30? z=&hH4F??_;Vxb*E)xuAGck}9u=&^6dO9{Fb!z@UCgzcx`xXD9ldQdVb;F97!`luN6 zAfgc1xx9b+vPtK@D?C6_6f{o7Ow?1XmhKv9Wk#{p{JC^I?rnwmE9ig`{= z9QmC^pK+D?lfAyAiw3ss=dTIu>}2`h)YzmufKq*qx2LB_^30#Jjo#(APKdA=8&)U- zDJ*guzf)1Y-pE#iVe;vNc^_DVuE<~&taiS=4*XZX`EQISbwyu76k_ql_BGG+*-drl z_9yRV>3a&Jhl5iy0#EV*(LUE9lU@nykf~F3b~NoLLVc(Yexu6RzRGU%Y4PV9I6f0k z0KXi*c8ucGslMfyuszzIWbU-ZmQAWR?Nf|gxGZCIiG=Odep-$QEdy(zE61fCpLu1g zHSX%LE=NiDCc&JXVnLq0*ZpgVWG(H=YPmcvX%f^+c>N)W;{5LlA;o#o);>zncFQ#6N>*0PL-J#9Mm0X)64fzc>dhwacjw_*N8~lJf3Y-l}n_0)LFn*fC`=};J*Ze zis(c9KEt7JZ%~9;;HOl6u!G689>bN7qgI+%n|Y8 z90G_pM4ij*l4i=kT^5Dlntc+FOLsP*pq{PgF&$OgoEBOO-tM#2@68Pj?vC7mpxp31 z=5kZeDVmX6svcJkYonw962|-s^dOcr6f1_;Al%8PIAmDki@UO382P`Ew9`vdK|h6q zOI-zuTC$cOsG)&Q2JBr|7o*RlM3<9&=y_8Ileu;GZ`QC%jHY+_oySsKd9mSK70m5a z?;|{UPz+}Gd-1KTuj53w_^vjojTzc=IKp+Z3(n`$4hr+QiNBAdP(rMh;Smq^`wu(! zAGjLKC*=}TOLI~k%qJxzt782X0i)nu2S-@~eYLlYtf#l-+`ITBZFJTBsgJO#br&D& zjv?eoqcD^0q`0Q1!_hJcl>`$Y7*LG^=TWXR(1OM5`is)l zAAXazg9eRd^|H}4?7SQq-!^JP!W^=A+APr3m@`?=U&ck?InYh2Wx|Z@^s~&ez+If% z`-uL7sd=v_l#uw&MUO1Whrx(su8#O=^eBTRj=<;x_6ws->O)u#?>#Ag+6P+x-mG`T zdf~h3s2T?F;CUh1i+lgdZ$2Q=t9ROhDfpO4z~t0C(z$MQ`=hTdqkidUl_u!V5HRjc z3W6Q8icB7-VM_l=;xqOep%>M&Y1!rRERUKDC|MsmpJe{(zpl}tr!&O246ji^%3x!bH^m|ZQ%~xzkNCGJHieZd(op1^8_UJL3pqwAY7d~d~oHh(WSXO~g zY_Yi}Gv^|5GvBc_upy~EHW*pRLAk+ej5k9hNoYjdt6JfMaqm7p!H`vbJBKL>(~47>!{=!9c)kADO{J4w zmXcnA&gi@-k64ouBg4hRLM^qv)950@TtjiNp(C?2e>!QgWc%_&A}}lLkpP+v!^Lz{QGKzOW+-^ouwr zCvi=dQgtfb=h-Y%-rPL$C`Hs?+_>8u7lWDAK6mMJ`q4prrSmyDoKS=?F%_P(X4440 zyffCHvlrW>7qagM^Q@1sZGrh{f88b8Xv~*NI|u)wN|L3Ez^7ojlKI`h`|y??>5wG zs^5Z30=*mHxmKcBdNEtdBV%00m`ac%SI7GF;&*YklPvnx_N>QhOY^vco@1Qj|Cd+P zVO}9|-at^;lP_eXJP+aq^}0jPJS36oyNTdiZvm?$v^0~q149A#13RQDEGAgac~TrX zA>bQ_l&PB4a^{^SE{RUaI6!8xHNe;nQmpK3Wz)VSBX-<%o$R^;W z#uFWVv2D{G-28sE?9>|C$-Ys&>wxI_|17-~ z;$AscEJTVOotigr#GYjRJp_ju*JVQ_vU0sB>^V-!fzakUS&Jrxhf%adSDL_`h5beR zd*@sI$yOJpH-ufX$T}6IZ{(pe#OnIlJ=qdpQ`($)R-V9&iDgn1hYbT`WZ_ zop4JfVxRpo6PY%G!Z>(&2Ej;(B3R(CKI^6IUhI4Wm2SX>Qz`eD7`^y=Ff$(&7uj)t z*ZwhfB<+3h>;j(<1&H&D9Uw(cv>pp%Rs~JxkaG*HaPoOa>+(|n;m?3IYm$(A+LFX8 zG9IRi2sKkCLt4$M4=7rd)d2rYzY!{wO`?H7#ZrVay{kp%Bja$rJ|<_Bzr_1Qc|E=E z;s!%w4UVFds4z-g3&SScS$&BOv1c%Oya$uRPhhPpPyN_q9e4?8wEd}bt&I|Q1GF#| zsU9~9K^ciqII+Oz>ztfx`EOp=UWZ?b&RiAKXy{R4if$cQK-)J&zNV^gejW@|4?-2i z2j0pk(?p(p#N5pYN$Fop^kXZ{F0SPB6?#X zw^c|KBe?0Cv;wk_Y6EF=JE6u*3iVhA=l-z~c=xZ${x?TGkP6^*sSN3F{)1A*Z8Oj- zLi%`#DmG4nK+k6GtKQGbnW2k6-KvHqb{>Dsb315PZ7}AcsEal;CdI-54O}|O75*;8 z%PG40Rl|jXgxC2iyChphERU7^*9yYran%8gl2H*W-^~2(sX9^acFjz<^PmM3g}Kgx z4%Vbd=a1r+kHr=PCY@mmBe~s3PlyFpRD1YH=GE$c^@p|ZKN@sp#$Y5p?iL*UYi?xp zRpr|ZGX?1&{gJPc3o$-o+e(MTJ?WQB{R(3mqUjM@I=ghM5YA7T7vs}B=@<70((Aa# z2sYQ4Gz!k~+*+mn8ek@UP>}*ROn*Yn9ibLvv`_=2nU2TCw(cQKS-mv*ym*5>Es0Br zkDi9pW}Q(?bD8!xnIGjhe12tpE&=TB9;KtYvXU37KYf^6GrswYJoWH@CYVj-&ENJW%_Cb}1-z~3ii2oh+e8NZxohw&-=_@*rzoYhE`6np;EY5qYX7-oC z3@@piZY+;+*!o9Wr93L6mV`BX!QI{LaLn+4d)oSGoe$~|F2B|-hcydSJs4Lb+xqb- zpYPk-e@{iAGaw4IUtw{Ay_ zGSsD9ir69ltB3TjM6}L_br_d@>X)OM4XPeYsz}8mPj(S&0kd) z+{V2#G_r-;&5-O&)L4UuDJ1k&%kF%J#Pldbd%7Zw=qzW86hH)KUy) zxp8k}X%1&>h)(unHTFwn;!F>H7dYaENssEaPIzfzR_h3cDXGysZ5lI6p1<_;Pg=T! z77^Lo2v1Y)CC_|f%nE86zeoH+Fgv`Rz11)cOfg^;H%{@j-8(sICo|bJyvuaw5)AKr zGwxagjKee$S&?4&oc-WY(L}-|mkmY7vtI13L}MLi!#2nVUu47P%!2Ur|I!U9>=iS% zziaU&MnScZPE6^UB&(XqUzwFI~78uhdJVH3@{)I)%lK1O&j*Tl(fn3v_ zFs&ODZrBay4VwF5(#CMpMK2y1__{Ivw)l;w!x`;9sP+GUfd5_OOIdpQvB~7S^vBkZ zfmdR7;6r6uA!3~zxPBR+78cO7^frLxWn4lW$iFu@ypMRvJY|c&n3rQ@nr0Jrs zH_Oa>4E=cLvD|0}iVZDF`Q2dTnm73DzRi;Oa529}z;rou#8sZ|$hJ`Quw6sbC$EbuD8*O#B5*yW>i+WV z-We;r?f5STdHFisqX{jAvF=&uF&!3mRx7=S#VFJ*o~|eb75-{40h@2bpBV;|$N9TT zfP?Fj8t#{}duUqJix$C+MD>A;TDwfv`!E$OxaB$CR*v-V@d)(IfD0F5vsho&WXjdw zxU#8>D;1D3BIu=k!~d8(NEQjsIbsi}BZMUCHgQvlY}=t#j08u zY&<>GzdA-IaWEYhc>z_$X(R(nx(!ZBfY2TJ!z2o~7vc{d`HH9v8@fH_9fAWRsP=tO zm@E|6Zzf*i$&<KWla6tv#8nZS|m{SG{iV`=GZSc$#7fVUYy z0RJ@CJV%FLiOPn3V9hop`wWi}>uIRpY)$5$a*m3j*8i(l(4Ymu`o>5Mc)*DqM8zRmg!?XqO?cq!6d-^rgqy*R zJ_zK5XlD~cbxSVLgyCJ&a4R3)V~tkRwtGQr1CLFmdpnkjMzs_75-9{LqWDKr$G~)P zr~+leXG%`(yD=Qkad~9#5MK0B$LT*{NRq{5TG)Bz_30lAE!nFnaJG~~;MGarpo&~> zF;g(i2=#R?2^Gpd%ax|wXXKB|s?CR}BfG3E9c7EE;#dMnNF{h{nzYNv16wJu$*?=h z4fi97jEMnw3n-Ngf(_v5QGcL`M|(m+ImcuM9c35Dokbx!@j87~#LE7YV;r}fgtI)2 zn*0!PXnj7bZq>6GT@^K6A#o&unO9jTc!I4+A^uMPCn>06S%SJ*`G7_{2GyChQ*9yl zy1p9mh6$UpF>id2VJUAdJFwXzo;^5~Gq{TThUy3i8OvX9X5lh{)k8s^=_^^O6{vXO z?WY-N_M3I8flUIb}38+;iDrBR#!2sw`y=zvfomT*c8u|qBl^2Eq& z>8y=bCUn-Me{4ciW5zd>D3Y*>^&=$vmztkJ9%1cpu- zUvaI*s3pb&?GxjDeTh-gKuRlvvs2)6@9c-{UlD%oEa{S2AO=OIbyey$8Ifhp3*|m- zB8S9U>dVXs4(AsU1Rl1Vr-mTwV5sDMp{pG<@1Xy6;v*6X=)!fkCd0||6P9~r9To5_ zd4PBIFwA70VS)pG3(C?Rzn5l7oU!{NC-_VUp6>&_{*e=FByjSU_d|Ajo&0 z`}O{h{^#Z4$1JjN)W(ei$C7s(K;3et5^de+v|QNiJn!7uMrY~D<^ry#974CE4er+K zeLWGcJ+!Vh2jxJon=2c%&xRJ;bOE)cJIg&&8AYpwoPu`F)8nWYjQKgw_ti{dtO;_C z-w>fsltcLp6sj+%IBVs-)BtmK+m=w2_ETG%AP&_-^$O?mpE{r^2_c z-1h%SooLrPlCu770z{*SA@sz|DE{s(2kV(c6AdAOO%}RtLz)&bjf8}P7$m?bIKlCG z1qXzc68ybkzpa-nhd=o*e4hRX3jHsovgF+Gpdct***?3Rq7F1aa8!9Xk!=iZp)>X& zdVVBZ!%p)@{hpi+Yq(k6YZ=8KTfS8_ft8%xuGx5zqT1J*7BPBP^K}H0^q$ zEh=!_5jGpPm&Dl|c#DWFA*pl_Z z=t2v}bSjHoo~B3O$-xU4bpC<*K!14@%m}*P9v$DmaRx|s1XKni3enGlW*XMtW);sl zf5U4N;GH(sFu4xFvJ|YiRDBOnth_g%QSg+pxOaxD!|;)f_Dm#spoxA>`NiVjJi3Eq z+aCPx4hPzOsXhM?Khw#0$9wB)f0=Vi)w@Rqu&YD)Vo0AQFTMsmbDL0g2@fBdl_09n z|AL;$bU6Ojp*5_bu*y{-x*tU*k7}thK8TZzj&wqX_DOI65(>jbqgeZBi!O2+3unLxIfow}jR69ER8)6z9H7H4jhhkBdI!LDY1!aP$@ zP%8*g-J-PJe-O?@{UDKbWElJ&Qd@RZv|=B4T^(9@=; zD;rc4F~agV&sH_GF@ZS0xHBcrn=d(x?8^aaZAHYL5buX z_Xx z_DiB2Mr2|A){^Pl#>%SBH4R?CV5C`4qCnIeJNXF(HTFquGo{Qw!x+IyVE*m+ha*RW=+%gY2Y~CgStd z9-_JYsM1l7LmlMmB2eZ)3Qxs;GZk46Ly+@jk!Gn=f7pTmzyYyYb=#(Z&?)B9xWyFu%lu>5U1En&{$Aq-gpN~!%Mp2h zQLi&+M0vq8!tW*JXjphIZVJ?ld;O`Yp$=NMhyScXJSxq1#(+Lhr4rwuxXB~S`lgV)MthAQ$Ab7zi#p+@*HXN4uN!5KFbsCg9<8_ z_L^h?sX-s$i7dctvYb_CccU73j0nwduk+F1Us703>I4xpzl>O7)(qQ5QVWVZ`CBeP zu)@^!e5k}&OmdWvu27-lH^9TDEfUSuCiyI0b)Kt_aU>F6UTI~a$htu}Sg%ZT!Dd{X zr5{7yefRATS$$}`t%D7EpCE5^zZ;Yi>Jx=6D5dlYxg-N?j>TI=40W2wL!}p!40FmC?AYKy$sm(d>ES8-d>k43&nn`9E@c$pE~j|dc-a{pMIv!_4^os&85*LT^Wlu0u9*hU<&H>EqseX&F zyN;n@>9VXBQKx9Pc$R5xK(Sad?@1}EL=gWa%A}u<8Ic0k^H@iR^Aa=!RvdB^u$IL~ zre+H>p99Kbix@*UT2E9;VrryYZPK_eka^K|XjM0m*W%II+*vKtKr^Mz)x>VoHBm(JA2+Jay?8IQ0U{eMkb4zH9cc~Eg9d#|(^EDf1PK$+i z%dHDD&BDLD*U)g38Qk;^gSFF0=M4%s43Jp#m8xC{5pwOZgC~QOIpfAKDqk~zI*x_t ze$$8@_zV#6V1F5>wMs}3m#__to|kPrVK?ALp+?UoPb?|;6iLs^i8}*&Z1#T$SF);ngoRcUfIrz{EU9PMm8A?^EyZ^1og&dZ zK)q%mY(R6kKBKRSuU_3swuIry52fsuc}oR}8K&eJD&g4MJ_6%TV^Wtn7wgWkqNXrH zeCA*IaFT~K^rMD+fvJi7@JG!bAT052xIbg`g|D7n!(Di2+vF-xiJeI_WL=33H_{?z zm(m15cVXFtAj+En>aMH0!%Mcpn6Q73A`WL1RlQOv=GR?^?ZX~aYL5b={5Z)ZF|XTD zulCO=s%ycy{L4~w^+$9JB?wlcUOum!@w0+rv`x5%ne5#CycPyV=y+m&g@0oM;p0;CqtEa!$;5RjAj)OR~?5 z?M))|KH3iXHz=(u@)Fs63G905_f>1gJCG1ah*zN5UC9XLwMFXD)So`4&QA4H4D;R`>r&1 z=XEJB-zIuUCkgkL&9zdG%84@#XRoRIlYdCLLv>t5y18>wx1iZs%d*Zg&1zT=_kxW^ zrFON2eYN&w_t~StL&|=3c1M$~tfd~SREtHbO?7sqr7{rP%;BpU(^ytqu9TyF^7aJ6 z0-9QMtQ3CTGo(<(h~*qz$`;>2f+e1Wtkd!T`=B zc0kB?LuvSLW=vj9M#nT9ew5<`tNXXhx;KwxG$=Mq1dhol>MtTU@em<5Q1TmxtbJj* zp%D$0)=uF040G3W2JDP|PzRG;d$DQqccJ{lW0h(*j`v`7ibJ(qrDIi3y~ZmKF5v6# zy}={3YmwYt4gSOFNzJu7%Y<{X8{LNkiLs#8Sy zZdk)edWk(6NcO*tP$b@7w4#xocy4)=2SjvrB=>;aKB@|h+Y6U z@C3!NPW9HO^c7 zzuWY`5tAZ#^HIoaJEV;anP_oQoz_N?1P^VErJz!0dn8c4<>BxdFi3QY}29C zIR!obl?wQh*C99zYx6|`IGGkvBa33V_@yHqeHdfOFcY1mCmQxCA*;u&1bYN3k_8yY z@G2y~7u2zh2(sT8ut@_V&`Cz)E7w*x7N(meo_SPxeMCy@(JP`XNB?-odggv>_62N{ zlglnRe%>mzT0GJ73HB=AbU=2_S>toX9rs!Lj|`woJUg~jy2Mc1c|$}5M{>jw_h~k3 zMqj7(-A48>e+_fIP)4szoW%U9mN;{n?HAsI#(; z`G{26@IL^$Kt;b$Dlm4YFiuye2dR^f0jYJnyN10ZZgn3-mS&D5hWT*GBHvC zT6GwF7{zKuuyutfSCf!}xfaD*6uZ{c7SXH4gBA~3JZSNt#e;3};HWBZu>dT~L^(7M zafscW1pwFz_jMhvP#UXHgZAoz6J962#0a`YPQ~#(;2|UoI%2lL>2W6*Fzzqcwk#Ck ze?$dAGxeV-W_t?0yeEL&EO4^G$pR+}oHh|S?NsGMPIUj}9r*~`Bt|YY(G85M+(}kJ z=ZAQDO1KOYEf(5O+I<7Q{}|tMv;->C!|EEGC9_o(Pbx+thOiK~MHDuKZ~b&0!8sE! zZA`hBgI)T3u_ro++;KumAdW(&(iSR5f8Z5SJwbm9T&C`{p%YCQlpE(@j8rX70U~T3 zB5+UVDhZ+@7`>D9JrcuH|1aL|fe=&cbdvFo|~!k%I%AI`^G@Eid^L z!<8eM#IDN7huJVXS5GUaz=6IWd13QI$k8t+L)4K+ZCzAf%_3=wq*vqxU1w^GFMIfD zWn)I_;1L3B*LhC&pCp&PDLG~?V1(J!7ObhZ=`*ZNYHd<$lUkc}6K&GnljlPte^FDz z0wfENEI_gVX%hj`epO!QSkEuxv(L29q02t!o-fbr5Vx_vgnK?Sf{p=~nREWh;jooD ztgDWqS%0+!kxylgi~j0$^Z-Bggb+_*-4^}U)&pz}9BbfM1IHRT9T_-ps*6wwrV0|HyP2e0orZFwfKF0y{~s(u z)qs1DN5T8In0syrZwa6we_#P3{F%DnxxG=!7|i65)|$ewH5H+62Q?Y~KOlal?o?|l zId%2r1m^c2uS9c~$*!Y;*wj7O-(8n$wvEh8Mrb4|BeEh18Qocz+EP{b)(O!$#W#qg zk{~G`$rku3sL z7v|-7rR=i2ViBVm6$aVcyos2$0}U-ZePq#1ud4PJ!zHbCcCU`IJQrIlO^l$YV%co| zJ!5aTcWPDXAE@0$`RUqAC{notv9vX!$XKBb5|7g?Dm@`7ZF9gGofCN{?EBMC%EGu& zr7%fvI>=uR3OU3Ce*`GR)a~ZfFXJ%e00q?XvM&&E(~qs{wW`;2o54<+wFw_akqv9^ zgwO&Plj()yA$RV#_NwKYSt zXUeaZwG^#IXC>q!37J~hg?Y@rGuie#RiP4*pIw=47Xk#Se?9R7xq031{VwAzj&-<& ze%x@+EZX^7G1&^zryE$O>yOfObq3#agEhYbDML*R;00tApZ*5=%B!Q3+Dt+^ z%Up+~_9&p^pn$W?8v%l6@EWPZ2p%L;1SsjJ(QtyrIlUqRo~ssgdQ#DuDxVYsV@()x z`#NW<=8h|ZiHjX5JyOxdNHhe5a@mF$fecdcLc~1kf4iw`k$ifKLga$+J)i_#5$IFV z2wlAR<0ej z_D=c7fA9{kGqq13FTF+i7Uf%%Z&CgxqWry@eCh%vE|F6`ss6ASzb&5cI0t)X_+@Cm z9?93_0xiSwoyLVU%hb6T&Rf840lNk4PZ_XV2y7v+g}@d9Zz2SKQ`-Gxe-_06C)pELhIxy@%ofvbAf{bsYM%g> zwdl~ILyHbAI^0Bb__iY5_!q=KVa5?@lR_(9xda!!1TVyV)?O<@D%mLsYh zrZoUFWm-Xp(PUb<@e;!;v)!Sd2AlrOHU-c_&R+Da%| zf62)AX zAt}@-XG4ZmL0b75y!23rWTrhrIk-n0KynWpI-4QFqvU;<0dX*HJj|ucKzgIC97vmL_BE~O?@te2VzxyETCiWF`Z#vKiY71{01f8f32 zz4Lu4DE#tuS{3O`@Tao2o~j)=st6`c~^(t#7sdMr-}uicCdPeH~HX7ej2N=5=Lr zyVt?)))96WDsA0x&E|w|snO0vj%I7*3cB?~7Kl%tFYq>%ih*ofN7x0SpoYo49S9im zC$CkRF(y;BdXn6z$OSlVe{Bn#a)9NI&`jkyfaqD5n13HaTlE&2$ zZ(j${xkhf>i1CiwzIick*Gp)mH(=+P{`c_rz4iZ}x_Ox@@n!26k4tBx857$bg*5Q&3ke*=iatt)crA;1FU z2u^XM3tQwZWn-E93&|wFQ5rMR5cfhlpL&3j*Nz76j}5YIRJgEnKNV<_v9tQ+b?2#rBYYaDa%^H-wVcN#Qwh z*%bi*z{o=FtA}~zrL`3#iNexnpSP|Ecn!XJhyWUJw)J%~kpUwDD@q7OYREFQbtI!T$0+ zaG;ogF#-V!HGI9r+@o_YwXQ(EFh0r+iCRO^49G6k0zmE-_##+P-ob`$5COMh+;V_a z@`V_6{E^Z_*LOmXeJU~V_&z?}o)F3amB)NCqr8P!<|7p!HlF*Wip?ate$=ipNT@w! zlNPLofFR=be^%!-lFHo$)ms!~QP2`7=xxQx*oZa6T%9B6-nWKBaSK(rC~~QR7X>u5 z7~I6I*ZA!6{=hc4Cq0UlJMgRz>=AunWdgOReAit$ucx@#9#kB)l5F+~vzAtus9Amv z=KGJPLPfMyBTNbnHWw$F+LfGj@|#*8F}+RtQbTLnl^S)+)9U7D=*!Ocq`s5MNuA86Zd3uczSFg( zHYzo^QzEr!aA#kxk-;5@tz>!?A}13OX2_Zs$O#eu2$@%&GGjSL?f2yABzfF0mFOcQ zMB+vF5#g!F6QO~^sh-)AM#LArz*7R1Zu!}}f0V0YSSvLi%2$zmpr4VLdJrj(X zT+Mg2-p|B>8&+gYkYr*-Pg~(G_naP;UO(pXU8~1j%szgsZo+{u`%#Y z`fX=^KV^pBsXO4WO}2M#%c$g}eiP5{t3cGzI%q6FG6Oe@GTT&vQtKNo`_#=Ge@oWIW8mUxBulNI#rj#SpXJ&4S-Ras6N=hQ z_Y~w+Ym=%jK5}iMILq*Lj<_|Yd|eUjZe2ig&B96xD=ndrpsomb30}O)w0SN+p|M9!*6A)FB|kiS)Tk6B zt&gv~&Xio+ODIZXe@eKZ2u{ljf0=;O2+AH z7Aae#Y?1PFL&}?)5PJJfUB*2vBHQPHNHt&{F#Y{U`TnvQU|pChqSRs77RTz5+30E{ z{rDBWy}Y}>wF}Vt02}!LANm-*1vwVvSdjB%LC(fz1sV?OGU`QXQs(0se_jsfs3ukx zNoRT>HABrR&ZXvuYlG z-Z_p?)c-=w6J6)(H{VCU#K8p;Rhy;2&h@*Z1lYyV)yEm;sZuR6c|%1oAN$w=*XTaJ z>w~JhE2j7c5pa4OKcCPLe=y_(a;1-b2B2z^J=5zSax2BugV{-zz0i5}kvpP0BmQqP42C$kyYs>u$Gbs2=^=a=->}Wr-I2r`RwOvq zJkDt-6xh+}ar5*x#U?7{<`@&iZ)teFr8VbE@L~po?@B5CFz{3qe>S+QuYMQ!}l!Y&_vM`+Cc9&r|#ZOrv zsI-WI$3*lOKrTM|S;n^6dR1iBrvf3LaVo&!s3H|$!l0ia!4t&z(dqFL;x5%nBZ=2Y z%%wW%*e6>x5EBWPf2JxYGc>o*8qu0%9+a?2MqBprC;iUf2>UM$&Hz|!q3{s-NjGI zMOPLv6}>Q;J-NKoJ9=vMsQtJ0VL`eTqum`vL9?B!XAp?6Talzv3Z^4ia+bXh72XYtXXxVKJD1f^*=5ZtogaIO)mlDEl?xo#}Uv6Sj!DDf4$ zklcenU>n8-e_rt*nIg7zMXpHFG7JJABh`4+tiAXHhiVG-t&1vvjD~I@oVN8_Q6$&u+8j~xc+V1WO-R$=^J-ni!wcLuoq-ojlKBTks z@6>MpAJCs8z1jZ(vQK!1dGFw@{P7;4BN!wv&q6fAfAd)s8jyq-oasT8#V81~u5ho) zcpSKBdJ~TDjzbx`<=#D;?%rMB1^=Fq;qAovdvA6#^k(#$-;Uk8Yk0TA8pjBzQ8(Rr zPmQ}pRXu+u@-;o7O?xum;cv8e9eu~PTgbHeNc8$^TrFDc53+e|G0{>PyHkp(DC)8= zO$6O%e>N4ycW0M1eP&b9*fWrya;)c${5U6fm>*FW^$rI8-nmq}~|)<@Y{ax ze{EidP>El?%<^j~yVBCj$#8+Fchc3P zmSnOF#jkS&-LJ&Ik9bz@hjhn2?beif7t^d4fd}KD!Y@j6DBS{Xm?Q>_Fd-#f6>shn{yb_x)c&Wt<2*q}bx|JcLRELtl23}2TmchHGZA3V)X}uIA?1YCJt`O_} zu-=cVOQd51x*-?0666_^u~~=Blt45CX6HK{(qhA zV%R8|$BdGx^IqF8^N3$&K}2mz=+s5kW{a7)&G}$^INFPh?CxBw9}(=(8J}{kA3`6> zAJQuqohpc(cE1+H3O6~V8@1gOe~85-Ha>@(Lp5f2D4ObKwU#(OyJ@gyzakaBQolHK z=04<@Oof*BTOL^}G*bm{N=~Vb@zy21MnoNNUyNz;UDX{{%eLJD(3Fg8L3nbJzI1#t zx`Pe}PErOiRftYN^ko3#g~+_ED{?6ijE>fr3zY)N{jG;W6bEO7lv9WLe*kl#kNQ$0 zU3&gVe^^%!rcXM*zMs4$I7w`vowA(``%tVmzQgADl%FtE~K+y-XfQE zx^jbPb-qxE7CYaT4nc1u_hG;-fn}_7QOCY$gPZ z3^Y#1pk)Hwg!CyFW~tyZe;6VV&Iy2`0Xm<0vc?~u!L5*PS{eo+y}>SWfhTiV$RN0H z9wNYjzK`5~=F@>&?E42-ZUfi9tYeuclGR40rAey`d%)snttEp+Csn_eDzpjD6}{ zGaxt~xk5oD!&O}!MaH=kQhmBc3{|wSxbl0UhM?oAl9A?0N{BM=q(V%G9_nUwX%-+y z06#aba0^3Mw-_Szf8$=Ri--jMR1#Y~is)oev>fCNi7$UT$#$IiG9I3KNK}NRSi3Jj z+`ae=qF;iLSRk3N#G-Gimqs)J*&gHR$QD+d6q6$rHR31)3~?q{C|)QrJ;VhLBw&mb zir^G5JQHXn6%?A(!UVW@GC^`kxx-Ryt9oXxi0rRyg47F|Os^U7rC71q3A!S4!m;{X z@lXYai$N$-W#noyF%bvJ$rp5dcuP|b`E zoSjUN!{g?zNRxkC7+(L+u;l&@=yWOJNek%jXuNB+5jsAL4Q zIpe^CH(FlOFhmXqL|9o&t*I$wS44(7FwkjW@RL7Fk_QK-$oLrq=e0i5VR!oMPG844 zK29gxu*E8ST`%%JbTl@##z~I70C)WbGh&CjE31grGGX9D~x3MErsI67j{d*<|KX6 zG5u?*&PUr7m*z=ba;!w*4R%p@M13D}6hi-y&WYRdR99V#brFPT&~=e}hLM9V@ZT!I z-I!9pB6Lu6lW%^ZUazCAM$4D=)m-H-m>9$0!`dOFBdceaNOqaQJK)3x1s`)7!f70y z7hVwu5`Tahn3Bp!x!8{U#z3oDFq84Xtw z;vyEVkp>g3rznUVdv=TdLG@`FgBK>WqL5LxG92%C!aKEj*=!F+U#iyFxz^f2wHc=D zXjpeI)iDxCL;HqW>bj(bre>^ib$yx7^7q6Rb$=ZedSn6}^z)a2ic3i9E=E2#&<4h8 zh91Z{nguF+)0Ho>f#OO*^uZXiB!D$U4i2zHnldUmy2eT>H#vz~ws)w5o}6(Tucp=M z$`zTk>vjR7)tlyeGbNY`^O${Sc%XL} zjE!*CY^p-d@vWI=l`B4kx6x^w-rBf~%kS6Fm|-2D#G2}tow_U6J|JG6Tf|7WM75W@ zjQ3TOyiHM;-5E5gK^P=uiu+GH*dkG=kAG4AgQ??R>Q-Hq6XdyCBo;$AI37}$?B`YU z6h9l1*r%C)%qH(y?wX4=)ndDK*XaqN?EBP#T8_3q3V1HdyOB{|`Y6Euz3bNF!2qF}QY-l!DQE31jWcZ1? zzZ`-<)s+voz`3q128t(FBzl`_NvETk&^d9tjZRqF2uiUE+bV3Uu&u&= zEQP&acb`B;SG3&q%(k(de|iRk@71Q-k8U*gqB{s$5YT)_wJ>We7f~`?rNyG}r;>?ofzyf3Eh@`; zP#K^1h=Vag1Oy><=MHlFaepRLov&5q=uNV@%Ia5Sj2t>c48R+ReK_{fvq~OXvrAgH zzDtXkrO=aEAa;h4TrGx&G`*Epi|2MJOQ=pt@!af#w{;2ZKOlbOLp*CO)@1GWdwjCG z>Gqy1Y$5?=!A@mgG)(jTJVirV?m6;F)h?cTYd7#R>%#8cZLAqm#(&$3yns<56ZMmK z`DGn~kLLgszb`lpdHm!8$vC8gw`=DLY-p+IaXbyHCi5kp9V*xv)McZI)K`0lp{m}s zuTX1+AIrdkX3oKy9$UEt<9RXj25O4S9D$B{m4*}1(evjDA*}jc#tT@p)D3)p^|d$5 z<7f5a^KlOD4C@lTe1Aavi@L37nRA8tR!a1yu!b{bOcY+GLZ|j{p4@shmZd|t8eL!& z-S(suQX|z>YFE^MkHTvcTSC{-3C88J>GgYuK6S2-kq_@LkXW5N!@iIo3=uYGG6m~r zOwQ=7kP|W-Kb~NjCGAp3NS)N|EJR0iHpBeLgT9Z*6v=jD6Mx}74X=M!*Hk4*H}!*y z8NEgy<;_fSdHw1zMCk8|cc)V#=d=9nCp_VFGAV!gjxjVF`}gH<&(UO#{PK5~FsLBs zU-Q|&=aA=KssVg}>=MobOr}W&^K9)S=wpYLw|s^|hGE4xzlM-ZD@Z!>;ha^BBb>85 zt@_m$8s4I5Wq*g4^Ke{2=*Rm2g>Hy%s_5~kLw(3AUVrs22n@>KU0mOX6>mHkURTjG zt(uxo;vvCgS}{m(PFz&c<2T4rI75DJ>HX^WPH?cjyDR;-x{wEW0+Tn15%1CUw*7+@M~D4I0(UvvY&W zdFkAsYEF_isF@S`jcVrPL4&#lf!UyLR^~OSn34MpD(19FgPK{r*q~xYL^r9I68cT5 zrSU|AdTHI>q*_*2G^v?Z!cFR>Rd9oPMa9)Fn|KCImRw^}2?mV}$TEzXx@W~C$-v?Z zPc}JG_kYf)F1y(W#Lppd>5L%8j`0*R{ywBLwLRY2`m1iT!R_Eh530=195-!ZF;){& z<2KE9)5DLBs^ut?~ zKxI8L#;mKb-l@y@RUs8Fv2%@hdtY5mn`C?bEf!-X(F3|*y={B8#~aAdkZpiWkqdwc8q}qvu4`yfvuc! z`+ug5evAlz39>!xVRgqrKD_6*PoKsPA7*LTHWb%hKQbvsi|Rsc20)n1IuFI;Nc zB6dFs-p`1lW`o)=ne(PDzmDJ>eJ8*YQhz6Ep-S$&(gi+4UcPxC@pd77!1o`fH#4V+ zs1-}CZ)VO7)9@T^hAyAdYX-*9xt0fJBS{5)Ke}Lnfm_Ty0KrU56ed%Fu4$;RZjJ;r z<1`3lT05RfJ>bchj_$B_=9?MD8DG*QRd>H9tcBL8yH(|;L5$6>9}F~`fXh@AE{6qhw{mik zr+0yrT)!{Brb2MLF5ie^-*Ycih|lJQU3}}}m{2oU z!Is5TOV!yT;BMNS!TDdxj{NQSjDVx` zmTj#-<0}(yE5uFDR+u?$){H`o$%w`?mH!G3X~MOkQ)be7SRvhuJ%2w)yYhUg#ICC> z<{}9B_U^&n!Jc?7xxk7=@BqJuU|6F7p{@$aLAW zGdM(%>Xn3bQ~tp;Ar%Sp5A;mFjB$W2QLJA|$#1A1(60`a1n-j&8#fmxUOT(U*P9PhVOT@R;{EAM2K!%h?2$E{-kaR(L%+c}^ zb4RW89vN%Zx|A<0>9z1CpRMBes_Blq+QKej%yEF{khHxIQh%n?N)2ptjJF$g(+4{_ z)))Hfq!{K{wGF1tXM3eD&a=wB+V~XJ#^!@_LqSi~vGzFKoj&tON9|`TgAbZ+t*hyv z*K+%0*d;RUQ($`^#;sPn0}8I{kh2NZ=BWN8E`+EFhzsS3yiZEqZvcLoXc~+K{uYAX zEb#bd0Y9sBe}DAvkN*A9|EE6sKNBVMbrbx948DFBaD0c<2WorJFVR`gasSu1-(_^q zmZJ72Q5fiCL|3bxB~}{a-Rs(EVF0Ka0~gJ-%&@JRYMqqnS26_NBxh1l@j?@n)uW|K z*F?sBwx(W1T^+7pH}@;z_8W>ijj*byjxHOAZ^eEfdw)kq8;5VyFBEpF)v8mLbs&;U zET8Ga&a$o_iE3Xp6t~nj?QJ#p;uFCf!{{r}uLda1@@v_6F>i|0RLMWkveN;(#^QVZ=bg?~A*RxIj~f?xCBYtSr<_^lYdtKi zc>eo(>wn1QuiKVHVIs63gMIRH6y~aLa}^V@EeUIuyQz(-m~btg$`QX4MsZQKscqz& zTUNH*ax3fW)uLzpaHVH<6Y5P9e2dbpqKGQC*U)VZjrH`}--nowFp9#hBh256KOFU0 z(|m_^)MB@sRn?T26fFUmZpop6m`EgqDAHQ1mPFSK%wRl4A%GHKK@-77jqMisrPo`Z ztKM2(LuCtVRS|~~-F_g5g(oUH-!`GpsI`6vaRi4ES}&r%iqv|7R6RMu<4=?8YDowC zMO}^d%54seHXdE+Ro6@%pfHGi_(mDn}B~>8gM2g0$bb#I63hAo=*vuQZ4%&9`26hdkN@VQjlqV*G!+xOHb=%+OhUT2q}M#n zg{=b^WO$nlP~6+!d-eA9-kZIHH@ONh=JM;ANUOvBUD;KJ2uqk$ZB8RNXX5Lm9U>tn zV>ymyp;_Z}ga{hp0ITm%_4o$GG7j!P3FT0S4}5=SD71f#_xah?12xUl?PW>Y3W)}PdslG8ho+mh-o z9cU8vp7pcc#i?S;*<7)edc;gWlhet5BcPd5d93g$>t?_@2bM~TK4%y^BJ(mv$89P&_h)1 zjB`8=6k~oXLV`OE0v05%VDZ{nDKxl3PyI}oVh4v^vE!-5{;-f(-)*`@Os4e>en=Rn zPatODOZzm}L86*+&XwL&(FZL27{%vRBlw>X_Nf}XjwdT4;-lUqnDB%B8eQhEZz+?2 zZX#R@RYuhOcrX-&{Vp&Rdf~oa+(o)7DgrO;cTQvYE{f8_xDL9I2CrrD=@km-9TGcy z@y(faPu4uk-=0-Z*St4{ETe(ndEV}$Z{5ORc!T_`!*|7y_c14h-TI)D{%$&d4*%V_ zf{UXiNBXz)wEZW65$4Nx;s48?%EYT2MlI@ngzOxX+%;e?!$q!sm1`m2ArHzWhQ0Th z+#sy)WylZl&)bvdX3~G%h^!~4pRr$_ASt)s)AhH{8j?@Ts9(qB!&>zl#-?ktzL<}) zDG;i*dUJkn&hO3ny*a-(=Rb8-y}38%ucP$5S&8&^scyv8UT5;rb#j!}G#W@6Tkg@< zh1R<^AC&s$Y)B~Z6~HJ+BAHfS=pV*n4H5 zjFSy;Dg#J}?vpxj9~`eTG)^{>U8Ru4=dg^b9fT)54e{{fe7bA5HyY%C5}U%rh~Z(e@^x7S$u3%HLiMqh8QMEcUW zoE<|xCp0eTpIesitb`d;YeND4tSiSQ zl@6d4bX=E@*p^MN0$I>>Tk693KDsKyHhZ>-P77-~O4VLWESN?u(i!41rK2(Uu?}-I z{#IaPnF7*K^f=4IS6M4W9&OSgZrXO2{#ncd^{ z?n;2M_4D<#$kbDXUtV)TW4#1EkC3{joV3SXhp$Z8Q!sd>I{@1PpOiUdgd>h(%Z|d7 z*D8ZfX8^_sU=l=0_;MBgr-tX#O$ZKT}Y(g&+7;-ERpd9-0od%bLb?s@;J2{=iQ(H9i6stde6 zQ(x1lJE)dVpKy+IA4(H&T^71{!^)^>p!M>|dMJ9ddX*Rsa|?e(FOEC?4%#PBj^|z4 z8T&(y`yr=hu9T1DwdEehxwX5~+Pn2c<)jr_-SG)thwM zWOwn+b}t;f9T^9C+%$6^8j8;R!c^+Mmuj5qWuZq5ayI?TM!wuT7axX6N~cT`$23G8 znj$10hyQwG4;z1;d{3u_ea}OmX0xU-^=+Q_W#8AYn}9Lop?}#{=8L+HYNr(V0S>p0 zaVP}+Cvr!FE4ct1B)w>;OEIq*carh4?O6UJbOr6QK#ajL8UV;xGQ(sHFb50BQeTAf zZ4VFHehs|^WaJl3PcubmEmxu@oQriGW3l<^ za+raL(kTi-GFPnQkzpxq28CRiULJ?1C`n*b9*Nv3aPNC(l1 zuAvZE6KMkLoRc_oCRRNRc(l|_{hc5t0Zy!rpnDV~9EEDwm*D*K^^FQqQxq!r!VI9l z6Bvoc(hO{3t{mfV>y~`Qe4-pH!%Fqlh#V0{929VtnleBao zXg1j#kx z2a`N?8-Hk1l^Kdj5oj7(Ar?Y6rM3k=Ns%<7@eGAvxC9WSb`A)HIuU?h)j}{DyPVod z#Bm_rZq8vKq?u_&4AyU2Bun)D31pM4{GqX%&)?P%R;f~2a;U6R=3hFY?MPA9og#V8 z#)6nR&C4*G8*1mxcpuYQE{_J43Zy9bMTi%z-+z99>0HeU|Aa-VQcS@5DN| zqJNZkhS4A!IHpWeJXcb^yKkE^FNgdHGV~syBh^>&XIEL#88$nL#h`FqUls_d6<%$7^=gDo@T|ll%Hw#baI>^>YbD(YD#9)Q2e?= zF#MB5X$BWjuuF`MR&z?dOL^9lU;^W@UVq~kN^~gMqxNudxEjk@7^r`k`iJw4!-MVg zKOusylcCV8QgP9b`;Q)Xl0?yWv1ObJt-nn1Br%@p0tp|ymc+-qtr@9E?^0f>9!%Kd z=bBk3PeBcN1z*9~t!K=!9Ct0}bYHg$Y^@kap_iV|73^%4_?cPM^7b|9WJ}g%V1JBY zaJv1hB}dDANIBu+(q1NRs)7WBu=5=oEfUv<;LGHcSDG#((%gElWM0eB&WXi#Q$G`Z znG`6kh^mT~_Ty#5v^1F(v;MPAAm&%mQqTNWXI`7~ZZ=iCymNt7sCWKBNlMT2t!Jr( zovb0x8PZxHnp4Ze%2N9{6CsjXIc?JFE&Mj}G7B8yEg4)c@pJ}Yz7K@1Bf-tt z_>9tNGGFP@NxA6GsbhD{zM&#GZ1C@7oJ5pP85qJ~3X(bC6huOxDiE?Xzki7GR47QY z%m-v)kztvCKR|e{t}%*A+Cj3IpcolHk+I6(l3Q}|=gsLMke;AQgL}cxqZ|ZfKhG_J zApHa>s%x3zrhJ~3+&VA#T`YtCEjj*l0{8?H?W(by7J5W}32^v}GQkflqE~thBvCQX zpelb$l!?;BEFyFMGY?AqZht+P%Vw4^Zo6#CLsILpk-Z)OeP31f>wjSs(S?bk{@=`v zAu3DZvIWX69yNQU2c$sYmPpbRNb%LXf_hg_?+WT&L0#Oz%WvTi7C|ZRNOb@avj0d3 z7uBgN%pOsZxx#wo-=g>2r{!Q$jh{&4jyR}EA==sc=t?{eHh(_q>pN|*D?g@J ze_ZUCEzLOA*X>V$6^5d0n)yWgWixX9ds{#Ane%NEI<~qyXI*J*dGyRvc>w6M+$HF& zC5Q89zCxGB(o&}#@e@isnIoox#fDt#?kXTrqvy!u77}${YU0Zt{p_*#(jONtU%lGP znptf*7d7oqa)W2tm4E+J7)Q%?YJ#rng!HT%eR0q}Z?>BtjnyaofOsVchQ}_XNj&rx zD9K+7ioWRr07UaHZ>#B#HngSqQh@vLqR72cRo^Xd2-y z3T#>X;0!KhboUSe7z9ZSITCbb$_PQ64M4(Bwax)J1!Kh1ihs8RW$)z1+Hafvw)uj# z%_dNxwIGFhM4_J*QOF z_?V(nzPdgL*d6vd3S=COFbP?jaP7b5=?m$+%^wC{bOza8r+%3_wOQ-U3bj?rMVn5u zQpyW1neg)mqkqmQIR<8$mR_DQYPTtwKy_;9_FIun=%Kfxv@w4RRgTZjx2M7&_-#=+ z&TN}i`o_^{bxTsK6^>+}2ca560grOVD`~e%bE>PXGq>bI%*hmHA)l;f2A9%wmFBfs zOmJke_(|ErM9W*?Muqx8*$>D8!ZI!@t86w{MVx^JijzxwC4b%$gTHN{r)4Z$vB#nr zdu&vUS3V2N|=^N(b=-Nx7meZ0Td0Pfyj#46>g#_G1iUzNVxW78&a+{}3aNj~S*g zmwcM5iZ?1xB7a{)S#iF-Jned~DT~XO^J5&V4DMtsw8&^_6vlR1b^}3o=-d*Cl}d9% zvkeHkr8@rDxy}7u0i@f+b(-OpC>XGNeZ$jei9uEqFj-Z+F<-X|cz5fTNJSSbZCzd! z05MYi1VeqNBOx5m5Ws?u5e$$}QlZj90~&S+u-Pj#?pF#;X_{2T?w3orC@k-B^hMrt z(fjjz9Gzog|2#@!!n2=eG??12t%kFQSBRnb4xL~&Okx90Hj6}FXW6XHGyrPpreKvpp7%4kSo6S?-jGm}Wdfk+fLr?p-11&IqRQ)f0Ak8ms zq4UMPw%2QWPgu0}+Fm!3^-WXKi?0y#)&NSna-A&Appte$f(bv^?+!IdH{G2e&HCbj zm}IzUi+vxKa0<|bR4ygW zGt2z{cDoKt%`CGy#iX2$%~{~c>dyW|b#J9f;7EGaBBzD0G2A(b8`ZE7EzAfTCPYk- zzC__b-hWB+mI{eO$uAtBI~0qRI0bM4mtYjr8Nj@c0(=a!^+kUYm7{X?LrYAjN2MtG z$yfMJ9ZJ=b1m`ertxxE>E7gg{HLZzsv7j@4npd-Fvb>a~w$QMGhfBbgB#^hiJ~M6W zLiapI63$7QFMCzu*{TvRPIBzkT`69YpgQxvi`N_p1&cvQRhPC5xqc zp`qHdUGYH@E5m=a^3rRsg&bHZB>-D8j%I%HpwiM+Kis)jE|x9{frLaY_4_N0rzrk( zqHgLRK_*QM$2iof83!9jQaYMn#=f#Gj_WWUtK+&``D-q%EjtAZk9-l!*!b=3olW`w z&Gz=8{QrM81{aoMA2(Z98F})vr?oe^;jyS>~LLS15!Q!7h}- zJb%rKq1|)<2^b=f2u+Anz~n0c0YQss34$0&FTOH4DWCAxEg^_6#37GQVw#L6%DYQw z*d4xgVJsb`{zn8ub@xv!XEjK)GmIsA9yM#|Nu zk1nR<3^5LWXLGHTh`D}^$F9}i(^wQIFp|y-jX;D5*QroV;`O75W&Q1Lc3h)iKJA{c zr~Z=bTMK?bOz_xYts>8=cKw z_&>w&mv%@GrnCLCCF9v{+>ZHz)O0xtv^ae*f7iDm)>ym|EAk@0UG<8M){prLMW79V zw%EX_^7F!XfK18#ag)n|ArxNpbZu*W=q=V~dv9OWAr>r2@R^c=LJ)VDCM;U!Lu}SI zalg2E`nr=+f-is1MsmEVyNA6b!56&(ZX{A!EAf#h;f@0_q%>OPv(3}47A$I@J>^S@ z>1H8Q;3HceiwTmqjdC}k&e?^lg*JxPtOj#lhQ}%fnw`6d`^D zBS-@OFpXSoM<|Bhz^~dlg~y)=ZxRMM@(s{0Ha#!gmm+`v_3|LkiXGoE)X#KaM-FJg z07IG>kO_ie3Mo11RO5JZ_z6`D)BaF35-xq&0|4aL@c#C8&5aK=H&4(ACK0a#uyI?; zc_P)pR~}GKRYV^M!l{P;rqx{Fs|v0uJHCdhu(`#)kQCMr+UjwKI$9h%HTM(Dr;bpI zFhsku^Y(vMdtSDwt$SQ9aJFjW(v61l9|d!)XSlp$&~`@u^c&I(I~MM5@74tQt7&q2 zFD72gWuhv(Yk@jO=?}DAm8r+3-$^WQf#FrFN4IQm5c3${L5{!_Nx%wgL9MYt{PfEy z|4Hkj(z4esL1{NSwPeI(LJK+U;s9I|$mQenvWS1J8wXJ1-FhaQ>t0RjO0_&b!nW$> zN9YRR=U_TYNp@s0vRPauAw;Xy_M!ZF5Wj5s&>Ln-OKZR8?u?pOpJr=p$g1*+aBD4$ z+nnnZ{RchIrCnIH05vZ$3XlX>gCNx_jB_K1l2E>}_HKRYUsh&7=F-#1U0tuwe(&p1 zc^`jI%Dk$%+MyQD8D?!|jHp^|Ci?4}w?GlZ8pKJ7Vhe$y`E3#Z@O%Mav~{ffmjF z&p*)eMmo*?x7Z7`c+mY-@xE$v6*6bBiBt}MnG#cr&|ISyB?eXDTk`7bXJ-Lb98+eCl zoO8N$ogoCT+FnJ`4h_nh_2$$KR?7^JiM^miM;J($f|V&JZFsH=su8hQeP1^wOQ+&4 zH{jCzDxdKyO1K~MS6p#^vRB)lhgyHIv)vdWB|fQv#%M3sGzA~=rtL<$X67~%%lhOh_Y5CJ@n@jndjVKwIm~9Ci%8Ki-s)ca4Yi>h-p6!i|GuCkK&s#u z35uhz3O+z~hG$seHhh5WJwnHDp1r(`(F`Xu{oq_?myoDEm--V_fmc^1;U9kv=OG&3 z#d~-^pbRbeWXZ;_?x*+jzei+uF$(@UnBDD8W^~FI!|;9z@Auipci#lJvH|P>6aUw~ zHzTcnx2USe&k>6`9Z=tv%-FlMqd&IO9Y6gY+wLROzbAVBc~>)&{o!`425)yx64$NX zVj)ks`4_O92OF8%Sa(%8D!sVw&(tnRrQ}KnEw9GPGe}bxd^mg zY;}Q3-624Io;J>cLIV$zA%`LpoBA%J?nGy&f*%g`-dp(}a7FKvR);|<)-AcQE?|Td zF_~C#0`JTP?w1ll_w>x7jY-OS>t`Po*_HI(tGlLvhI^C2ha4xB4KglkfIC%X?XaXi d8BX)`+&|d({{7qc{~rJV|NlO<+R;w&1^~*i&GP^N delta 33541 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 diff --git a/build/openrpc/gateway.json.gz b/build/openrpc/gateway.json.gz index 2a3e5c939acae4a30127e564ae3f3c9baa2c2339..64765a403bcc1ddc6b5e19235a2d8f88ff13e0c8 100644 GIT binary patch delta 9385 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 R{{a91|Nr`Ma)jeQ0|0m4W<&r0 delta 13660 zcmV-iHKWS)fAfE^4*~-7p_35;rhm(lo|FD4M|xf%@o)SU)fb%sj1;-q1J(e*oA#QoH(c(9i?2{eaCGk&>*;O;M^m;KIk#wI{g*!cZEbgSWBqrr zS)$!?G+yG-b~oK_(Rg?_)*eGr(_uCytM6QD4kzhS^DQE1iFFL^T^q798(garG`pLY zJ`gfE0@8JVj@tP-(G#CA)>j;84%_GFFp+Y6iC!G&2* zszxB>3DQfn!W=(jT+l0aVN<&70c!}p+&GNdcnNjn?mjNj@+a_W1FbK?jzn%MZscO* ziQfo5ITAMqd?hIyA3q;2AJ=|&o9Xkpw=G-!jao2@VWQX6m47cKXTN;Y&QAF=L5kY( zpx^KJ+)-%9bK$C@booi3JbS#k>3PniF9qJDsOwL5QsDy^pOguAy;AP)#{k>nwvQ=J zuF&`@00^m_(27b=S$$2E!%B%Y&u?4C*E|@GxR!(D9}GZU;0tU74Pa9JR!D9OGR~YA zk(D|8IpouHI|h7l&cX3fleh(j0#0VLKn5!T0-fixZ3h+s0<52tg$UsSK1h>j3BP|Y zUx!co8Toh~y_^xuFf{R3NjZ&pJ_f6{X3v1vRLN(fnXT`$P{ICh2S5V0BzRx|hc`LA zCfCIO3<~j}Q{rHUIe;#~5;1+vb(ydg_!s|hfB_ik#i56 z?ZVkP3~hQ~g$S}C<8UAWm|OZ%&s%@U=3RLmh&R`SQLuzMHZg;oMfbO#{cxF7&vrm>&Ou!6EU5fG3Cgx12B=tFZ>goa`__c5!t(#2C%^^+1@jR zyx+ifjQEXh3v)=*K8NS18Cy+lfMXb@rrwMbHMh#1dO$Xp* zK-(Nexe`Dy&=tqzH*^mi{>R}pIb&d9QvKEeTf(pzpP(fs*jWqOQOtd52mIaHpU zJ;x3c1X8cGK@v^iyBhggSW&aT6U= zu}K`kFI()mB1ibR_&k3>*N?QqcZhIvu055ml4G7RIY!BKCD->P*T)(8O&;M~BAwnL zySW1+45+d9A`Gl8*F_A$Vj&$t0y2g+78A+!wtNAg*sMjI7WFoBf}%N`j18UJ;V#(9 zfwN`kslcMgH@u!M5yq*QX0_}4z8QFuk+~93sr3G|!NDO&EwF!Z2wHnQ97VAV6Nk}A zy2>_?Y@uoHq`%5=zmmVCnPs`z1&WWpYd)H0q&%D=XgVj9IM_h;@5`l$3A*%Ld*Wq1 zrYZ!R?p;|>YUb{63gM>nnp}t~ELUa#d#S?5ryw!s?!#i1;dB{d6z{z1`zGF_j7ZhA zMRKXJUD6E$ZI6F&!%+7~wlU-t(LN2)&ND)wqDw^fB5+fnwn4TjVE00|>F6uMeGSu-bB-f~AVVi*28qw}u)TW`X2=-|Rc90dQ5?-OtDKDM&EzjiH z2m(cyxKDpfj0n??y;A!k@9&OV6yay*aDhxYmMaGBSQ6?xVniRfRCHlu9bF*XeW~?B z&`+o-LZ>+<;)O&c_BecI?-t%qc7F~X_SrHxGse&`p|g%Y@}}U#gm|MJ_Z!rF2`^ri z1Y38)t6tiXoS&)?f@La`{^>4x)lTZy;0lpz$@G6zmrN_8m_mY8VT8%BR8nv%M^x=K z%D_3fI8jU`|5TvSBuAC2Y7}SCQeTCbv`qUL2iGYM`trkljwV^*K3q#m9>vC6lj^r- zBeNg@D$7PFQ5QZPV6L%Hrz(6V-g~kdvdWN$-#l5JkDJtbSubdNl-NAHFvgx{g#mG@ zzCwR&PZ117kh7c;Ts0NBA&OwP;+!ZkfW2RV^)xOrwG%#{93ACF`6Owf9anNb+;&-{ z*q_{(L1M@&_INq$G0Tf13R6O>Q5gk*+hUN?!5IxWBprRlA}@wTX8pXVr35LouRzL# zfYfJ?y{eBi)D>&I9M%}*#YKe(q3uYMLePILu}GDGllp9uIRwQjFNalzc@bG5VrVy} zr2ueCY*Ie7r9P8nps!fu<*>*oFXrnCu@=46Y0$E7C-JKf6mmpKr*(BUNW*sTw67e~ zU3N?>nQ=aujWfdSM44t}zafNfiH_cw4O|u(uu>1WG9^R?kf!n>8@F`HkcEaUHR6AS z>Xg}TwIWe?1e&HS>4t#8+c&RL^utl=BqJm%wj!x9MK!3D!zrw$Q+aX_P`mH5>>Cx);O=?x6#0q$7NXjNUbIL}jiW&>;Z^y(cDx2Efem$~dYJ)bBq8 zRY{oh$!wYt>LJ4sPax5ec?uc)WJ7=AEK|s?jF`y+mL+Qp$RKmu9D0q+|NQJ!a2I83WQ$p8O)6zf z|K2Xd8SUjh9G6haMUfANr=wpf{qw`uhp)f>`~U9HFaOJ|57Re}{rO*S^*?_fetG-; zYWR!uj(+&`V1GQn`}Kd>vUaq2?qzSvckpDg?|G3P0UD?%NH^?JL3T!>jx z&?VrXz^k8AzRHV*%k3^*`Vnka34<#dP_~JdkDFKf*Z~GwY*#B$WI0jMP~X}%w=p7u z(gC8J*2;#~+Sw7kHF4NoL6U#Q#c8yX7*a-~GB-9~3L_bll?TFm{Pz|)OoVPf_Pn27 zS-(hi_=Lrm{eFLNApZH(?;neQ z{;l;iH_$x4(K%g7EY9gF*-r@&k2jAukB>bq%r1%CGtQ2DJrr*!-cY;|=Z%WD@u#Vo zdKcG4IHt;_eW&G8Nmr^t~#9!HkW5bDBF(#^jaEPm_pIjWLI@)=*_ zaETEv7aaySR#1Od$^}SXO^ogh98fHDXBbM}4FWJE=3$ z8=?BN;FZ3G=8WPUXXo&aB}qEJ@% z<;oF-M_pBdMkc7HlzSl}Vw90FBYDKFF^QmAyPRekpjK*XrU7mzP$_||s;>FcLmO3f z&CW@taYlcllH}2I>_lQ>nuHl8U}fLH95ix09>o?+0(!yCsEIBrmD|c5(VpsUr68Po zVP;yZQt3|WRQv}z6|Y?$R2?3u!vl4A@MMPvFSeBDe3Fst7*edYa~xwZ8W#ADLEB52 z?Cs@S(G_WeT?`<_oTa?F1NU3o@y%2g3-P+2GwT9(xiJ$4qb z%3BbzK5}h|jE-vB70J=<$->A$JH^__ZmBGfmMjnXykcBI&!5^8Y_vC{Zw%x8mKaW} zKrgln6FcixVR}IvHN8kaQPlk2)pp|OkQ3nrWPJS(h1A?|++Hsowy_4ok?@BxXg~(@ zTcv-Pk?)}s9L#zIo#|Z@D|d+;WZ!WuA&Bl0EwxZO&8M(6WL+h|76%2eAnkCVEfp#FTM)zy;4_%%y~I{a{M+y?8Mf?U@))x00r$B_uQC zwc~kDJ0lCaCC15{%lIPK-+#nJLN#MMV^x3ByobStB>^QxLUuoZCQfJ7eT>j7>(+|N ziL~jB35TlKgefa!h4Ng#QR$b~zzZ2m->YSQr*_6_XZ#)PjAMpc#Wji8PSw73b$=CK zQ%H61m3E>Eywn%2jZzJ*sNGX4SU##$_u*OyLibCbS451Ejwe3-0B(iu;qJINm*4=R+W^WL>LZpGqW!dvv;m8 zs(n>OI;i@$RiwjfOS~np-21&tD&JCFCGosMV3cvOf2ZV$tENSKuCj{w2!`kL=p4Sq zt24rcu@ieR0&sy3Pu367^nG6|@=Vp+UgjAjUBohJe?$pxZ#JLsb+%yljZ+5@V}m`8Vl-@F5R>nA z5K4ude^2}4-0~e)tyTEBDy;A$luBA1n=*=^xWrEljSxe4?*^qR;`7pr_$ZNcKJ8Dk z>T+O$$?7d%{aSLy|2(67n6zO&Ve1O9+~aH?P&>uK!g%ecLRffYNvRThTSlCmV8hXl zIj!|H5pw4L`UThn zFlwVi_oF2gJ+iQmt72fqz+GqHK|dp$`ocuU3Q7B? z9`l9(q%$sD;6u;A%H`b_7mDz09u&S9AL;{n5-HnHHJ}6J=hMt zEVE;9t27My{eDmLYmQVj+JjOLh30Yj4D1(Xzzzl((cyoWNT+wmeh>LV*I}D5fGnTL zmz#h#WR)Kp_J-V#*vL=e-C9`jlJj#eszZ8uA#*p!0HHC5*M3z`~7`5>d>gezUnx>nRBf`@H zZ?!c7KS$fWlfpVP=qTEE(`ci_NQ!fET9Re)*(rZ%(zq7rai(!2ORZ`BBl-V>lz(~u z*D6{<(og66F3ZTvLX&9&AI>De!L+AsFbVyNdkcfHcgB2cez90y+;4= zr`1BzYM(}yJ@IT|){}9}X#%P2kvTPxcHGY_cv^)*4AGod<7vJw+dW&w_5!xQcN>(dbtz>_-lGR<3)khi0bgtyW1e*9Ox{qhCL$ZI^&qytl((EI>L#`BWAnij?_AlQ zy0Gb8)UBKpIOSz|3baZxJ$UE>`2!Mcn()G=bm`O&JWau?9r2$&V3p9JR6%D{K|W8F zOYciwyBJH=Yb&o!#d0d0Rzjx@GgE)R(}i-9E1KxEVKbd3fe7|%Q=<*{rO}2;qbZHn zX^j@*sB%}8yuIfttSg!MO6w`DS4QiNbF=F&5p0}Z@5891;TDvd^);E_XFK{UJOrh+ zl-BBu)_R(&GLpO?_*N0YQ3FagVxx6p=kBHML1oO0Axwtw8w%Ya8DVS&6U!A}xe3`TpJZ5Xi z0JIT61TIY8J0^gwu>&x17$iD6Y9nMBeZW3 zbK=z)p3?)>QH~pBR19$E_RWIY^)8DK11M`N<(Ncaa80eD7;4#o9u&fUxi(MC#nm>V8n18boSEt_|^KA1t)O4mL*sNlRr=VJnYXt_zc4Kk-S_1QV zl~atdHIWS*23dP7`!t#LY5Wmdq6AjacO?v=Y|}-eRL>*PQARt;$W@dEkuX_5TPqtH z_1u_AFj>q77ufAoO5R)*83)>2L8hRY$7l%zbV$I0|Hm|u0Vn~EXnzyh_loT<0+vm6 zH-N39hmWnr>f#XSiwb3>4dh?`={I;6I ztVm2G7JYBBV$dIs>xzq#hwbjoCyljz^AqC;inn73ACJWKJFtaAM1&VIp1F?UqjWL1R_ z%vqTsy6a!HlQ2zJ3pokVZW;Unnyrjt3O!bZ5hlqp*ru~b)n23A?4L?{Te%cBW|EmI zkZLu8sWv5Ps76^hIlNv>Mn6ShQktb4HjNUhC-$<+8GpdMr@7w=>0MENd7qYAE8p{8 zdYxX#)_b0jkm&{rig?!d=GLiqeSQK;`r-4J8{riSgf!&JTp8yQ$3?Rd}3hO3p0R-9hIlG<+5!)tQs9&Pg- zC&0_#V}Iz7e=rWZ+Ri@IBb;ixwdX8C)FO=S3QzG;KuNITYa z$cG4Z!EL}s&;Wd!B7#$#ReN@$497{t)kIwPRTPH=mk66l8K(sqr?rd7_lG~{#oUB6 zhC2z>42@9l?Vf!`&o|Q~=gW#K822+W1jEAM0+S~sTmgfVgd|*ljQdbmvbSfYRf|Q%>~&O zKNsQMn6VXIo=?Zq%t&Qds=aX>yduc{h{V};T{?_GO1IKtjf(BG*atbLTC8OaV=Xy? zJbNvUndi-LcP~#?kntLtCKdF5(%393TE^u2GN(3|E~47; zmq~AM)6?E~2>VaY$Y%%nzkWjxO!zh`9qWXkRy{6syRZ70#IuYCC76P0eY~(krSVZ_ z;IzvN?`_n?B6T5TXIR;otO^yE3#ADUsq;ws+fz}tfgf|P!mKd4F;akk!^FK-{ z)NmI$3N^2vdfGKMwgQ%lpqV@?i0au8uQ`piY2L^at6R{xjGh(xbJQF52Eyr{b71%* ztDt#qFtblE4YiO9BwrCpPe_vatWi1tNYGDfsw%*LyEqOxfrRuaI?;&YBxfChKe1_o zCB!030g@e{A`&^&2CSh2bZVN|5dKC9#Q_{3+@M2ng&0^;TLwXM8sz__cgVJ}fq(;- zi0y#BM*6LDcuoF*CN_Y+hBnk0hqH769RqmqwFllD{JRJG2lF2IPY)b?ziKw$i&IOk z2%EeP$P#bAl$CNhJ(Wb9^mCJGCx!tIlkg`=e=6yQxv_0NeKwC&je}|@STzoHpYf_G zBPb=)ilbHo*;Wv^Dx{nWT2(=-#f&}5jjp>yI>L=SUM_^JE#0LUoO@u87%cQ`x$xR? zYX!*dy|!I^pk?#g7-SrxNVp7@aMbdu-F*`DIdk(t-PCMR(fRnB7EWz$JA2wo*CtCU zf0DCRI`|7q2j|l76V`7L`$JfI$MXIf?_m^T^fny7DSy`D0&#GcEv~nRv`579nhr{r zf}VYKue0iP(_6vdrvZWsSe--VL z4ZNw0_4Oz_yL!5=PN2R;%!p(+7&DcDUi+pAzYzapfeEyC?}$#tzLc|Nkadek|REAN6&I5iscTZ-GFf1-|vyC<|#oWo*0%o>M-36w!?((%E5m!n%YV@5k`({Pg9iM8Lh8&=!Ow+I%b>Dc)6M_Ae zs#DRD=hMkJH@9A1N7CpdjRR<{4840jN=xTxE^0-IOtCvz#8qoD$WUI&A_sp6&KNK# z5}`K?*&9qC6U8PdIndV-f0G`#Ut@g@bV#nr0s(^(H_%7+^U3?@P@FK{VX^{{7=X|K zAb)1!I}|F3ZJI!bTL*zaDgp};XmSzQZq@2;=lm;vKA%n|xe51It=DtPfSASc(b6$R z?4AL<9sbY8yhtEqq2=tUep4P>emBKs6nCCaCr7zu2d>&F3w4!We-^499~RVws=<6= z97}LqfPmA5GEj;qUkXpoa+COlZ!Wltwl+_Oz_ex!hk>@lmQged*)ohk=A}%SfOi{< z?EtmGpAfTC!G+>BH0PR#d`G-mA=|$)4ME%7DZ};0V#9fEH35Hb)PM(l7_Bj&53wEY z3nN&>dO_FzUNf>aHT5=~;1%B%k`-5Xi~<4d6mkG zw4VT1Qsud9SY7RTIH&WGzyO*ia2QH?^@701f!}`{Qw(oOo zNd)xkSrG9M8WmEDOu26`IJnM9UJq%|Vtc z0~>-{hp%1S_fpnB9m~98tlKtnie!m~O&i3q7Y}LY7+Ky+FqiMhAXYq^Qk405o z>m;yLzYDi)-b(!#^fHBazGvQ^=0+SjTsePi8*#UUP~$$HSMCy%B`v}6vYQ;951`fI zbAJ^1l@vc9CXaC>HgpLfSm8V59jAiD4xF9-K)c9}-sBSyL&i3Z#n%u)g3pg*YnpbERF!Y+F%?82eMN?XJ}zd|xIF27ZfXVYfA!99SyM9da8k0^hK zLA}fGTne~EmWg%v*)kwQ?U(9+4tr-O3MlTqhDWrh%^RKJyJ#hul;Sd+)c!dTIeL4okDfBeC7DdxADMr}(1ktYRz$DpX_AmT6Al~QY1?<7~_mX$gp53cSV};;ddwYCK%#GCChXwSw=-7pHGka zxe2z8+_E<^N(6*TaT<{z%tG@)z;DZP@Q}?9$53GwB%JMEGR!lG@>G8eTc2SESxL8& z1>YEktcG2SWuq|iFR#sG%_yA4%$Y!I?{^$M`JJqaw$Glw>mjD=pdd*DoFoY7D zW7%BB2wFq1bhZqehwnwaUJ1e%-Lysa9?tnX$%1U{cv+Nu@6zJ^#3|EKVMnVSO_ziD zY_>Q+%TfPeY#gD3#Rz}t2Xi}4@WDkw{yu@^Djehl z34iT}ynL|f)=CBX#x&{uCz}0gXQgxZDI&X!msO)bJQPB1?}X<)evA`FuF7}*M5Vd8 zgK0~98yazy9=*A*@{M}{lZa3Vt$L|C%Yu@sbR0PMpTz|ckuQHGS?dH^P{(X1;zQqf zH=T=L@2{f2-5bjd@#7484Cui!$kk_cmr|PsxT|Ww&uw%DJSkx5#1Kw$Ph!n-8Sy@e{{_n-;>z z4%{K`;nFD7{sOY~C*e|j_2nkRP%mUxAHQ5~O@&^EXT#GGK84Dt6%ohLtK{O>ap4v| zxL+eXC^e2vB+`LIa*%~|5v?G6uU_Rv$YF6}>;Y;U$d-TJUCTyGWFw-Z!)tOO0b{n7 z1yS#XF(iDq;`jf@dsy0tcL8+-pp9GrHZl&c$#0aQWAJH>9k4-cO%1=|`4XE9*)HY_ z=*SX~feriGw=LT%NuEVx`#S;Ii+3%`4SPd=G#*XnN2A$jI@`-(RFZ>BY8AJ*YP$+0 zYbzU9EA@Z3yg%Naw{F2Dwf%i5+us*vDNV8plE^hUMY;)X_x}4P)=~sevBh+D8ncqb zgHfJMHFi^;i~N-lOj>eV#zz+q!+D|*oRS6Q%E{~6Wf{vU`jWV2*S@Rd$$7GZcP-iw z-;qU^3}|`2wDcd&w=~VFEaSn+*>~!QyyiN zgb9;t5sA(R4n~N|AACz@}CUn+G3C`#XmcoZmmB))kERndc~$gvfAzz#dSCfDR_DUPY`-y^24IlnoG^dU1#JTaV?R&3Ps#{3r$DdX&{jj=Tis3J>e4Hui;(n{LG~CTBBD6CI4W+Whn_( z7>WY$0tt+eA+JWN09SlYC=h{9B>|_x17}N_v|*X(ngD^R zI4^X>v7WzFJ%IR5RGcJwUFeiJSRNr_V&FS$Z291IPkxV`}H_wDx(TC~<)6YgNWzk!^#T!CF=kfx&RZSwuuX z9)Icg`n^G~-|OG>v`YjH?fA>EH{yQ}di|Tns@V)K67?$b$?n@O|FeM)XAG|P`*UyIeIhLsgQ3aXFzc1spjSA38-UQyPobYVsKX8Zg z1Sc7t^3)c{`5ZD)$#g22PDK;G&^lkUQGNn*{d@g_@AGan3S{I>cz=G1bhCei&M3I% zuds_c`Xl$&zgF%!%0W2e%A@=*$_VOGGCI@MDDFv$`Qna;d$}3u$xPUnZ7+`VQyCk_ zv}8rIq%pn}%VpR-JFX?2-`%j8I$ZAJ|1@mwN4*pQ*rC%S+2gJ3ugErq|h!+Em67j-K3gPe+eKf_QlG3V3SG(Yv| z5*ae9rm;m4g_I>pe@?^@mdhuKLT^b%CQyEou=by;d!7cP`5eOj_+U64P7cQ7#o%B8 zjq$<4=pQX6^TlwekM|QJs4`5Rj8l*DGfXZKC+lAjG+CoOlQ5hL*(QJ91Nor@Qig4V z&RS#I2>vP)B@@^1QkA_SltICRZB>HmX(<=@RkZN3E-v$MRi&m-vrw-~W!P$qZT@}96TV34w-Rm}-Ymy!6RlKP!&l>?u1@rlRryrSzLz9OUxuS?-p|iA z`(*E$`r2F&`*ydz;=6y45(3agCtU{0b*V1(f-x!|obtgbA6%dWU7x~pI2yVtat9fi zYkyMv7TG715Y7N~ByI#k?ez5sl%^Z`I7-=uVYFL@Ow$p`T7()1Fv`aw-%xNbnmXX} z;-tm8e=6Di>3Lz85n?Wh;a_x7FSixbO!=@cg`?J9U_7A3mg>f*Rz{$Zv@6Qh4vOif4^x+gujA#^h4#ZE;x#iLVLG$%p z|25|+aV_#|ZhwDVZpohzGwrzVURSLhYs3Dqe=ryv42GZj^W)+0xPSC-t*8A$9p+B^ znmSC|OMFhyNs3FY@|}M2gM9^&52oQLa{lXkin31&TJou({0-9G9Gp+g-?SyQH;|LH z0U0`Ac!Sce+Lla`9i;N3M0U+b8JPf5HeNBvn&>z2eHniR7Z=Ln!(s&|nw8dYsh!l& zaNek%K*gn^T%h8zzG+XzebYoxe?)#uCDGH)R)pH<5;=5h>mj)idh$*Ch&C7Nor85~ zYRAK)@$Bem-k;9L;~sw{Jzc}W_-H&EO~*6w@ai5~+VNmGn~mrF!E83``;U|DPcA+? z+VOPU(>{NQzrJYj!^LMX7>$m!n;0$@br@Ha_m~*;-tw;0@4MpbBic|tX~Vui1U^I` ze7N}hnPC%ug>o$hqRj=gp=lx$JDZe8A1*!%1^gB|D6NMRO7yL5Bf|CcV^15zP??Qp zvuQtyjsL7ggu?wKaO%?hbaH}*F{B3lqtVfLFdKi4BX|v_gZ^|dJQ__~MJ?#9pN`%r zhTdp691Mq(@zJm!#jii=AI*n@$)GyX${}WW%D9RFNp~uf^5j`<}Rn4L2U(t>)Xvf!689$YbpXVp{xWUsga*ta( zog(+go_}Tn4a34nM-9xJe%H*L1_`6Uf52rYp0jBrqQzxf!4_A*^@jg>+yLO zs*l@0ouc}<_0u7$57|C$u*jfY+b8kpxow}a2tASQ(|mq9IYIkTk!4Yg*ZOF6z*gh?-T?@XS{UO(a=VxEYC2gOfI^Now?el-* z9&dH)XuN#u;(vzyaevUCkEfHVivOt-|MP51-2D|+M_@lf{Liy7K-u`8 z=VE}6ab!RSb!?>BMxAoA#f^b(A=mAx>-OBmwQaYs>-33B!j|e3cAZ|>POodHmo;uX zbqcw-HPtEP;$~H+ko&F<**b(=$d-Q+m)oXUQiJ|@HaVKcoMP^?7CmAaPJwP=)3M3v z*z6>I56_BD^3dg3(McY&JSRHcTB~j?Rkv0uZnHcuHgO~6d9g`4d!7@UWYnK}-pw>KxK-;q;Hg6UW7;1K=acbpFpht^q9gv! z_K_=Vjtu+5$-F-s4`;Ed8y$^Dv-xB+K59GuF9*NQE^DV(wd0Gb0+}lvU3G@$nCm&K#U-XW-ZJQF7HY zsI24o$nz&eu-og~_2upQ>b8Gp4mB5WZQDbw$hQ_^dG<*D33pG&$Uklvb&LEXmJz)} zHnL2t!>wgNhE%fG(;7(4=ckhs)ajfcmF(4CcuuE~>-4&IdRbMnmrC~93v%B_vR8^7 zq!PdOX9uaouWsZ-sKl=_D5}ISmH5>eF{cv0RN~h&CVtsyg@_|=|G9rfJ7pp%6R8WX zqE@6wISZ*q4nb+`nxCT8C?mE%>^i-$onF^YFRLSaQU=hEU;u3i|4U_%?alI08D!6%cA+xJn&8*jW$pB; zc6?D)2ARqrYn`aNok4&0$jzQl(7AQAW3n~Pp7xHwg^3L9nAuz8-%tM$yEwQ%;e+`3 z!P?5(==(kGMEouOd)m=6=0;kWKh z`c~uvua|Ec!^}k5u|~F=1+ulC#&%Zd_hV=%-@JDVN=*a_f9Zdt{w?{Tcin#lyndk$ zBXEO4@Em4ivJ&uCgxV-_4Sy1$$m=RIBCh-|Ganz^mYAW9lZvSO0NOU(@!ucO=3>2b zur96P;x0a-&DA}$DmC_(q09&68%O3dhxpo|%NWma!}-6A*tRUHlj6ti2~uAy$FxgbXaxJ}mxn>B~TlZevR>zxJB|0I&lf2!L0f zCtseIXY`zD`{F&idF*L6`g@D*Ksgx5vXKrMeI-Z u=rClzh8BKp+4N!epA{m9EBgP=r}Obxdwjfk{Qm<00RR7B3pd2$KLY>(9a{(h diff --git a/build/openrpc/worker.json.gz b/build/openrpc/worker.json.gz index 2e6e690e5b18e273b6e90e5d303fd222f318b945..ad518528c220e5ed049d7917ed2e6100042f3f5f 100644 GIT binary patch delta 3303 zcmVfpHFQYH8{sIGZy)Tmi13iB`g96v>K2XO+b|j0~%qn^DOaoP?l4I9(!g=Ey zdDq*81fc@$-)_J7>{>_=Sk?V!$ZaeV>Boj+8Oy)^%4>U&d@K(dfQftH6)~LvIZX8N+-q^fN_c2UnfP#=03JUY;X9 ziytSqVSchg#(!~bToFiz#5RSUSDaA$FbXo4SRG1hesk+wH7s&vVRzO>OKBa9Q8G*r z6{EjeH%$^Aq8wdw_a5&}ltP;dZN4?M+0zRk0-?wu-|z+Y9Acp>&qkBG1SJA~XOrRt zB7eZh_x-!@iGg zK-R6bz!?{Aed4k?w!wG-5cnfH|Dgc{PJdk+Tj1Tpj94>3s7=HWMIZCP;lvVek3D4F zVSC++VAc^p)CLHA5v69xpS99UnNP4S^c+nV#`qhuFzP<|_N>3?qHdp$yZ#-Xdw;o) ze|y2goqKqaWGB?9 z!E|z6Z)B(K8J#b8fJ;P_(2Kegcz-fn`Pg+h(Zbi*K8<{T9Z^<;8xl{hP2*OB@m@ic z(w;s}R>9LJnyouZWh2p=XJrCQiibOqSSHObEsrvtt(ck3uFH#2ho7{|?zR%jo3kdP zTZ1w6ay>yhD3seC{`m0W1C-_fqL(ww4Y(CIo3gr>KN3c|C5e}8>ka8VTg zn>zY`EswDW z+DbHE3i+;x-vNCyP4A0%6E2K;(PTOWcNN@Ka96?IJh=OIp<~8v=NM(SL@6_@gbnw$ zkynFpHK?4Nc&MZIqpb%9f1OTWjOhtRz6Smhx6=CJ-s4N|GBm|d+am1i*RNCehE4)% zfp2;Un;F^Xe2i&eBX_<`k^yez4qbB^;!;IC&Tp|rfqlQTQfXILX+?#ntKk8+C2Ld$41OV zl+tw#{uY_V$!Mv;e{<%#lix$79#)J}QOgx7#6*-a?-hyr*mi1A6&k2&)Ejvj&&q41 z%*8^>keO~UVhA8UKww=YA?VKSW*G7Na>KY4h5Wz?BSJi&ZR^-Eymf&xx)||dr#=d$ zg8Cr(?>Oh5)qij=ZC#_465M@WOXukEENZaR*WjGc#IJ4if3q38d=~T8;+iqYk89_M z&-{uJEv%Ch%Z|Xi>)V8xNAWY*Xj;GdXGXB?iGh6X>o7IMOlRx=TB zkDNgG-BUy_f5QdmNSnn^?uy}y-^<%uZRH%UtIe2J&E`$RHl5_Xja*V@-jo! z_X})^C=q$4@7vJ3!vaHRu7!R768nCn)0HefRXN}EgMiqrtE=5mG~Lh+t6#TP&Ca*! z%s2R`qKIq^QQMsZ(GhSb;2|6}@T2+~m|}{VkZ@eke_4Qs8K!Z$8|p><0iPqr{Tq6q zCs_|VBc9}&=eG$iLC*zoA}#Y{atbTm_txg)5!d>ucnh?JYpPbS}6%gmSI{M>-6)p$k1>o2WK{> zoJ>fLf0sKYHejNAA5$(|osXT#aa3k3fRh!sfoz*$-^Vr}>(*M}j0*=jaoHT(V7veb z{DD~%NDw%6ZES&e4>MxT0HHP!Llk|?1BVk!ygl}ib%*VBFFai8LDU8ad=aH)$e*>+ zliVlR7J7~*3uF8ZSr~O6e0$bkbWyj@$6fypI?uho+{eGYVByZazsBQY~mU=e2mK_`>R5mbMz1=yic z6kzA>ebs#As8UflMPm|>JdaQFzRcj-rwLpjajN#iWR?^W)trZ@CTr$!C~VO0n1# z0F&b^Y3&6zUd?QqwSbEHp64rn3mhjW{UdysnCq@nKOE;fH?C2)$hZ=#s+DalQMWpB z70P)|u!Ya)#I1~56J0llTGM~yQuA#xO@@d56I+maIN4-f( zb}sZ~vQrfXRv1`e;6uZ-p_}<(`Vu4OH2ef*)kMog&8i_fLXXI~2(yAI_Wf{oSv(G7 zB(gZU$C(o5c5L6;dyYCeC3e*v=fqH&3i)YnsU2n&{C5Wbtc8@zT>nS-@Zm!U#2E;2s~70dlkL$rt@kNO|qd%%{mud z6gXGl{CfuHo!*mT6E1(xD7Ys|xb<^}_g|6PRIX`J>agvkG)YYc!_>f&>q{yoN@Bw@ z#Ka$9T}iF9sC_d9R1{FD2dH!lF8)e|+kgxVyysXBuilWxH8bECP&U*L8LrI@mOD=)r>&DLW1y zY&x7HcIN{=!z-&mKETnt_R2b1;5U~8K11Rk;sYOZPU!T8&d~yJbcX=~6Z`;tY`N6- z0e69L?`VoR7C%Zj^6!99cRO%uJnA60TFR(|h9*$M>RMKHmY}rF3<~bSxts;ub{{H7 znz&kSs3|s+xYof&vgtkFJbq_nI>ik5alwP5!w;t~!;{$*Gy-~5lMfXj9h&xZ^@8(w zDTW{5784B|>KPMF)v8X)KfY-V?-Y|u6-|GPqSVnyBQCIwEeEkftRme;k<)*llzF&M z_1tnqUh6f*fegtnvh9>&LAj{V>5fJS>E^M~H+sjWIqn>fk!>E2ZGABAjmCyynR`cz zoqH~7S>wfQv|Etu_B-d1hLO|0*V|UO+t@Y~Bs)aI&4MfmA(Lnccn<}SD%PgoQFC>u_nI)J-9LnO_X_qu zS2C&k0l;;=9aD{r;025hEdI3IHRV{h{M>w_xcN%+j^s!R7P;dR;P@uc^)3d{*X9mh z>wabw`LmLp<;%=YR(2({bY)s6Ps@L2rXV{r6yA33K(}A84H_LA4bxjIT2G?p{+V-E z)I+w7?T-tN&!~+b*B4N%-h>@ol5aS?cp~?3KyS}4f+a@KRNh^hN$R=@m4H_=0WV`1 z&tKSHB_kyHeQDj)yF+u|uS`FCw?xK}8ytyI$1t*&M!dPx8rcgN)Mo+Ix3qs$vZ_i} ztqpJv3v!5JTdoK;<68L>wY7UFhI(<49WXC0Q3>5`sA^=Vr-)UX=MBZtZf9ufdq+}9 z6>?ONr1U2u{9ggVyrN33>s3qOSXVoA1uWTrzWQyt8au9a=YxQ9;s8Fw3 l4_H8HnBu9jJSr)bsT**)yj>6ee*gdg|Nj^Y+9+{Y0RTjSV4?s3 delta 3303 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 Date: Wed, 29 Nov 2023 17:56:20 +0100 Subject: [PATCH 003/394] mod tidy --- go.mod | 13 +------------ go.sum | 41 ----------------------------------------- 2 files changed, 1 insertion(+), 53 deletions(-) diff --git a/go.mod b/go.mod index 25dc88655..fa5e9370f 100644 --- a/go.mod +++ b/go.mod @@ -70,12 +70,9 @@ require ( github.com/gregdhill/go-openrpc v0.0.0-20220114144539-ae6f44720487 github.com/hako/durafmt v0.0.0-20200710122514-c0fb7b4da026 github.com/hannahhoward/go-pubsub v0.0.0-20200423002714-8d62886cc36e - github.com/hashicorp/go-hclog v1.3.0 github.com/hashicorp/go-multierror v1.1.1 github.com/hashicorp/golang-lru/arc/v2 v2.0.5 github.com/hashicorp/golang-lru/v2 v2.0.5 - github.com/hashicorp/raft v1.3.10 - github.com/hashicorp/raft-boltdb v0.0.0-20171010151810-6e5ba93211ea github.com/icza/backscanner v0.0.0-20210726202459-ac2ffc679f94 github.com/influxdata/influxdb1-client v0.0.0-20200827194710-b269163b24ab github.com/ipfs/bbloom v0.0.4 @@ -108,11 +105,8 @@ require ( github.com/koalacxr/quantile v0.0.1 github.com/libp2p/go-buffer-pool v0.1.0 github.com/libp2p/go-libp2p v0.31.0 - github.com/libp2p/go-libp2p-consensus v0.0.1 - github.com/libp2p/go-libp2p-gorpc v0.5.0 github.com/libp2p/go-libp2p-kad-dht v0.24.0 github.com/libp2p/go-libp2p-pubsub v0.9.3 - github.com/libp2p/go-libp2p-raft v0.4.0 github.com/libp2p/go-libp2p-record v0.2.0 github.com/libp2p/go-libp2p-routing-helpers v0.7.0 github.com/libp2p/go-maddr-filter v0.1.0 @@ -154,7 +148,6 @@ require ( go.uber.org/multierr v1.11.0 go.uber.org/zap v1.25.0 golang.org/x/crypto v0.12.0 - golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 golang.org/x/net v0.14.0 golang.org/x/sync v0.3.0 golang.org/x/sys v0.11.0 @@ -173,11 +166,9 @@ require ( github.com/StackExchange/wmi v1.2.1 // indirect github.com/akavel/rsrc v0.8.0 // indirect github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect - github.com/armon/go-metrics v0.3.9 // indirect github.com/benbjohnson/clock v1.3.5 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bep/debounce v1.2.1 // indirect - github.com/boltdb/bolt v1.3.1 // indirect github.com/cespare/xxhash v1.1.0 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/cilium/ebpf v0.9.1 // indirect @@ -224,8 +215,6 @@ require ( github.com/google/pprof v0.0.0-20230817174616-7a8ec2ada47b // indirect github.com/hannahhoward/cbor-gen-for v0.0.0-20230214144701-5d17c9d5243c // indirect github.com/hashicorp/errwrap v1.1.0 // indirect - github.com/hashicorp/go-immutable-radix v1.3.1 // indirect - github.com/hashicorp/go-msgpack v0.5.5 // indirect github.com/hashicorp/golang-lru v0.6.0 // indirect github.com/huin/goupnp v1.2.0 // indirect github.com/iancoleman/orderedmap v0.1.0 // indirect @@ -307,7 +296,6 @@ require ( github.com/spaolacci/murmur3 v1.1.0 // indirect github.com/tidwall/gjson v1.14.4 // indirect github.com/twmb/murmur3 v1.1.6 // indirect - github.com/ugorji/go/codec v1.2.6 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasttemplate v1.0.1 // indirect github.com/whyrusleeping/cbor v0.0.0-20171005072247-63513f603b11 // indirect @@ -323,6 +311,7 @@ require ( go.opentelemetry.io/otel/trace v1.16.0 // indirect go.uber.org/dig v1.17.0 // indirect go4.org v0.0.0-20230225012048-214862532bf5 // indirect + golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 // indirect golang.org/x/mod v0.12.0 // indirect golang.org/x/text v0.12.0 // indirect gonum.org/v1/gonum v0.13.0 // indirect diff --git a/go.sum b/go.sum index a96b193a3..797f30619 100644 --- a/go.sum +++ b/go.sum @@ -48,8 +48,6 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/DataDog/datadog-go v2.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= -github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ= github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= @@ -99,9 +97,6 @@ github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= -github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878/go.mod h1:3AMJUQhVx52RsWOnlkpikZr01T/yAVN2gn0861vByNg= -github.com/armon/go-metrics v0.3.9 h1:O2sNqxBdvq8Eq5xmzljcYzAORli6RWCvEym4cJf9m18= -github.com/armon/go-metrics v0.3.9/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= @@ -118,8 +113,6 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY= github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/boltdb/bolt v1.3.1 h1:JQmyP4ZBrce+ZQu0dY660FMfatumYDLun9hBCUVIkF4= -github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= github.com/btcsuite/btcd v0.0.0-20190213025234-306aecffea32/go.mod h1:DrZx5ec/dmnfpw9KyYoQyYo7d0KEvTkk/5M/vbZjAr8= github.com/btcsuite/btcd v0.0.0-20190523000118-16327141da8c/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI= @@ -164,8 +157,6 @@ github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38 github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs= github.com/cilium/ebpf v0.9.1 h1:64sn2K3UKw8NbP/blsixRpF3nXuyhz/VjRlRzvlBRu4= github.com/cilium/ebpf v0.9.1/go.mod h1:+OhNOIXx/Fnu1IE8bJz2dzOA+VSfyTfdNUVdlQnxUFY= -github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= -github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= @@ -588,26 +579,16 @@ github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyN github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-hclog v0.9.1/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= -github.com/hashicorp/go-hclog v1.3.0 h1:G0ACM8Z2WilWgPv3Vdzwm3V0BQu/kSmrkVtpe1fy9do= -github.com/hashicorp/go-hclog v1.3.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= -github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-msgpack v0.5.5 h1:i9R9JSrqIz0QVLz3sz+i3YJdT7TTSLcfLLzJi9aZTuI= -github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= @@ -624,10 +605,6 @@ github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= -github.com/hashicorp/raft v1.3.10 h1:LR5QZX1VQd0DFWZfeCwWawyeKfpS/Tm1yjnJIY5X4Tw= -github.com/hashicorp/raft v1.3.10/go.mod h1:J8naEwc6XaaCfts7+28whSeRvCqTd6e20BlCU3LtEO4= -github.com/hashicorp/raft-boltdb v0.0.0-20171010151810-6e5ba93211ea h1:xykPFhrBAS2J0VBzVa5e80b5ZtYuNQtgXjN40qBZlD4= -github.com/hashicorp/raft-boltdb v0.0.0-20171010151810-6e5ba93211ea/go.mod h1:pNv7Wc3ycL6F5oOWn+tPGo2gWD4a5X+yp/ntwdKLjRk= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= @@ -888,7 +865,6 @@ github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlT github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= @@ -984,8 +960,6 @@ github.com/libp2p/go-libp2p-circuit v0.1.0/go.mod h1:Ahq4cY3V9VJcHcn1SBXjr78AbFk github.com/libp2p/go-libp2p-circuit v0.1.4/go.mod h1:CY67BrEjKNDhdTk8UgBX1Y/H5c3xkAcs3gnksxY7osU= github.com/libp2p/go-libp2p-circuit v0.2.1/go.mod h1:BXPwYDN5A8z4OEY9sOfr2DUQMLQvKt/6oku45YUmjIo= github.com/libp2p/go-libp2p-circuit v0.4.0/go.mod h1:t/ktoFIUzM6uLQ+o1G6NuBl2ANhBKN9Bc8jRIk31MoA= -github.com/libp2p/go-libp2p-consensus v0.0.1 h1:jcVbHRZLwTXU9iT/mPi+Lx4/OrIzq3bU1TbZNhYFCV8= -github.com/libp2p/go-libp2p-consensus v0.0.1/go.mod h1:+9Wrfhc5QOqWB0gXI0m6ARlkHfdJpcFXmRU0WoHz4Mo= github.com/libp2p/go-libp2p-core v0.0.1/go.mod h1:g/VxnTZ/1ygHxH3dKok7Vno1VfpvGcGip57wjTU4fco= github.com/libp2p/go-libp2p-core v0.0.2/go.mod h1:9dAcntw/n46XycV4RnlBq3BpgrmyUi9LuoTNdPrbUco= github.com/libp2p/go-libp2p-core v0.0.3/go.mod h1:j+YQMNz9WNSkNezXOsahp9kwZBKBvxLpKD316QWSJXE= @@ -1014,8 +988,6 @@ github.com/libp2p/go-libp2p-discovery v0.1.0/go.mod h1:4F/x+aldVHjHDHuX85x1zWoFT github.com/libp2p/go-libp2p-discovery v0.2.0/go.mod h1:s4VGaxYMbw4+4+tsoQTqh7wfxg97AEdo4GYBt6BadWg= github.com/libp2p/go-libp2p-discovery v0.3.0/go.mod h1:o03drFnz9BVAZdzC/QUQ+NeQOu38Fu7LJGEOK2gQltw= github.com/libp2p/go-libp2p-discovery v0.5.0/go.mod h1:+srtPIU9gDaBNu//UHvcdliKBIcr4SfDcm0/PfPJLug= -github.com/libp2p/go-libp2p-gorpc v0.5.0 h1:mmxxAPdP3JzpYH4KcDf4csXnqtd1HazLPfdyB2MBRb8= -github.com/libp2p/go-libp2p-gorpc v0.5.0/go.mod h1:GpHuvY3m0YFkd0+inOGo4HDtc4up9OS/mBPXvEpNuRY= github.com/libp2p/go-libp2p-gostream v0.6.0 h1:QfAiWeQRce6pqnYfmIVWJFXNdDyfiR/qkCnjyaZUPYU= github.com/libp2p/go-libp2p-gostream v0.6.0/go.mod h1:Nywu0gYZwfj7Jc91PQvbGU8dIpqbQQkjWgDuOrFaRdA= github.com/libp2p/go-libp2p-kad-dht v0.24.0 h1:nZnFDQEFU4N8GzclnR+IGxIgR7k4PPCDk/GK9A28onk= @@ -1046,8 +1018,6 @@ github.com/libp2p/go-libp2p-pnet v0.2.0/go.mod h1:Qqvq6JH/oMZGwqs3N1Fqhv8NVhrdYc github.com/libp2p/go-libp2p-pubsub v0.9.3 h1:ihcz9oIBMaCK9kcx+yHWm3mLAFBMAUsM4ux42aikDxo= github.com/libp2p/go-libp2p-pubsub v0.9.3/go.mod h1:RYA7aM9jIic5VV47WXu4GkcRxRhrdElWf8xtyli+Dzc= github.com/libp2p/go-libp2p-quic-transport v0.10.0/go.mod h1:RfJbZ8IqXIhxBRm5hqUEJqjiiY8xmEuq3HUDS993MkA= -github.com/libp2p/go-libp2p-raft v0.4.0 h1:2atEs7/FWH35bRiLh8gTejGh5NA9u4eG7BXjpf/B+Z4= -github.com/libp2p/go-libp2p-raft v0.4.0/go.mod h1:qJCYtFBTbip2wngLxFeAb9o52XmAPi2vSIQ4hV7IpSA= github.com/libp2p/go-libp2p-record v0.1.0/go.mod h1:ujNc8iuE5dlKWVy6wuL6dd58t0n7xI4hAIl8pE6wu5Q= github.com/libp2p/go-libp2p-record v0.2.0 h1:oiNUOCWno2BFuxt3my4i1frNrt7PerzB3queqa1NkQ0= github.com/libp2p/go-libp2p-record v0.2.0/go.mod h1:I+3zMkvvg5m2OcSdoL0KPljyJyvNDFGKX7QdlpYUcwk= @@ -1179,7 +1149,6 @@ github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcncea github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= @@ -1372,8 +1341,6 @@ github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnh github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= -github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0= github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= @@ -1404,7 +1371,6 @@ github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4 github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= -github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.10.0/go.mod h1:WJM3cc3yu7XKBKa/I8WeZm+V3eltZnBwfENSU7mdogU= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= @@ -1426,7 +1392,6 @@ github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7q github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= -github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.18.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= @@ -1587,15 +1552,11 @@ github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhV github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= github.com/twmb/murmur3 v1.1.6 h1:mqrRot1BRxm+Yct+vavLMou2/iJt0tNVTTC0QoIjaZg= github.com/twmb/murmur3 v1.1.6/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= github.com/uber/jaeger-client-go v2.30.0+incompatible h1:D6wyKGCecFaSRUpo8lCVbaOOb6ThwMmTEbhRwtKR97o= github.com/uber/jaeger-lib v2.4.1+incompatible h1:td4jdvLcExb4cBISKIpHuGoVXh+dVKhn2Um6rjCsSsg= -github.com/ugorji/go v1.2.6/go.mod h1:anCg0y61KIhDlPZmnH+so+RQbysYVyDko0IMgJv0Nn0= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= -github.com/ugorji/go/codec v1.2.6 h1:7kbGefxLoDBuYXOms4yD7223OpNMMPNPZxXk5TvFcyQ= -github.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxWFFpvxTw= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= @@ -2017,11 +1978,9 @@ golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= From d7ea5561dcf4b189537c77d80f1b01eb896721b1 Mon Sep 17 00:00:00 2001 From: "Andrew Jackson (Ajax)" Date: Mon, 4 Dec 2023 22:30:40 -0600 Subject: [PATCH 004/394] maint: break out lp deps for easy testing --- cmd/lotus-provider/config.go | 47 +--- cmd/lotus-provider/deps/deps.go | 270 +++++++++++++++++++++++ cmd/lotus-provider/main.go | 9 +- cmd/lotus-provider/proving.go | 23 +- cmd/lotus-provider/rpc/rpc.go | 91 +++++++- cmd/lotus-provider/run.go | 343 +----------------------------- cmd/lotus-provider/tasks/tasks.go | 58 +++++ 7 files changed, 450 insertions(+), 391 deletions(-) create mode 100644 cmd/lotus-provider/deps/deps.go create mode 100644 cmd/lotus-provider/tasks/tasks.go diff --git a/cmd/lotus-provider/config.go b/cmd/lotus-provider/config.go index 5bd681429..49eed327a 100644 --- a/cmd/lotus-provider/config.go +++ b/cmd/lotus-provider/config.go @@ -2,7 +2,6 @@ package main import ( "context" - "database/sql" "errors" "fmt" "io" @@ -14,7 +13,7 @@ import ( "github.com/urfave/cli/v2" "golang.org/x/xerrors" - "github.com/filecoin-project/lotus/lib/harmony/harmonydb" + "github.com/filecoin-project/lotus/cmd/lotus-provider/deps" "github.com/filecoin-project/lotus/node/config" ) @@ -77,7 +76,7 @@ var configSetCmd = &cli.Command{ Action: func(cctx *cli.Context) error { args := cctx.Args() - db, err := makeDB(cctx) + db, err := deps.MakeDB(cctx) if err != nil { return err } @@ -131,7 +130,7 @@ var configGetCmd = &cli.Command{ if args.Len() != 1 { return fmt.Errorf("want 1 layer arg, got %d", args.Len()) } - db, err := makeDB(cctx) + db, err := deps.MakeDB(cctx) if err != nil { return err } @@ -153,7 +152,7 @@ var configListCmd = &cli.Command{ Usage: "List config layers you can get.", Flags: []cli.Flag{}, Action: func(cctx *cli.Context) error { - db, err := makeDB(cctx) + db, err := deps.MakeDB(cctx) if err != nil { return err } @@ -180,7 +179,7 @@ var configRmCmd = &cli.Command{ if args.Len() != 1 { return errors.New("must have exactly 1 arg for the layer name") } - db, err := makeDB(cctx) + db, err := deps.MakeDB(cctx) if err != nil { return err } @@ -209,11 +208,11 @@ var configViewCmd = &cli.Command{ }, }, Action: func(cctx *cli.Context) error { - db, err := makeDB(cctx) + db, err := deps.MakeDB(cctx) if err != nil { return err } - lp, err := getConfig(cctx, db) + lp, err := deps.GetConfig(cctx, db) if err != nil { return err } @@ -225,35 +224,3 @@ var configViewCmd = &cli.Command{ return nil }, } - -func getConfig(cctx *cli.Context, db *harmonydb.DB) (*config.LotusProviderConfig, error) { - lp := config.DefaultLotusProvider() - have := []string{} - layers := cctx.StringSlice("layers") - for _, layer := range layers { - text := "" - err := db.QueryRow(cctx.Context, `SELECT config FROM harmony_config WHERE title=$1`, layer).Scan(&text) - if err != nil { - if strings.Contains(err.Error(), sql.ErrNoRows.Error()) { - return nil, fmt.Errorf("missing layer '%s' ", layer) - } - if layer == "base" { - return nil, errors.New(`lotus-provider defaults to a layer named 'base'. - Either use 'migrate' command or edit a base.toml and upload it with: lotus-provider config set base.toml`) - } - return nil, fmt.Errorf("could not read layer '%s': %w", layer, err) - } - meta, err := toml.Decode(text, &lp) - if err != nil { - return nil, fmt.Errorf("could not read layer, bad toml %s: %w", layer, err) - } - for _, k := range meta.Keys() { - have = append(have, strings.Join(k, " ")) - } - } - _ = have // FUTURE: verify that required fields are here. - // If config includes 3rd-party config, consider JSONSchema as a way that - // 3rd-parties can dynamically include config requirements and we can - // validate the config. Because of layering, we must validate @ startup. - return lp, nil -} diff --git a/cmd/lotus-provider/deps/deps.go b/cmd/lotus-provider/deps/deps.go new file mode 100644 index 000000000..f82a1ecd6 --- /dev/null +++ b/cmd/lotus-provider/deps/deps.go @@ -0,0 +1,270 @@ +// Package deps provides the dependencies for the lotus provider node. +package deps + +import ( + "context" + "database/sql" + "encoding/base64" + "errors" + "fmt" + "net" + "net/http" + "strings" + + ds "github.com/ipfs/go-datastore" + dssync "github.com/ipfs/go-datastore/sync" + logging "github.com/ipfs/go-log/v2" + + "github.com/BurntSushi/toml" + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-jsonrpc/auth" + "github.com/filecoin-project/go-statestore" + "github.com/filecoin-project/lotus/api" + cliutil "github.com/filecoin-project/lotus/cli/util" + "github.com/filecoin-project/lotus/journal" + "github.com/filecoin-project/lotus/journal/alerting" + "github.com/filecoin-project/lotus/journal/fsjournal" + "github.com/filecoin-project/lotus/lib/harmony/harmonydb" + "github.com/filecoin-project/lotus/node/config" + "github.com/filecoin-project/lotus/node/modules/dtypes" + "github.com/filecoin-project/lotus/node/repo" + "github.com/filecoin-project/lotus/provider" + "github.com/filecoin-project/lotus/storage/ctladdr" + "github.com/filecoin-project/lotus/storage/paths" + "github.com/filecoin-project/lotus/storage/sealer" + "github.com/filecoin-project/lotus/storage/sealer/ffiwrapper" + "github.com/filecoin-project/lotus/storage/sealer/storiface" + "github.com/gbrlsnchs/jwt/v3" + "github.com/urfave/cli/v2" + "golang.org/x/xerrors" +) + +var log = logging.Logger("lotus-provider/deps") + +func MakeDB(cctx *cli.Context) (*harmonydb.DB, error) { + dbConfig := config.HarmonyDB{ + Username: cctx.String("db-user"), + Password: cctx.String("db-password"), + Hosts: strings.Split(cctx.String("db-host"), ","), + Database: cctx.String("db-name"), + Port: cctx.String("db-port"), + } + return harmonydb.NewFromConfig(dbConfig) +} + +type JwtPayload struct { + Allow []auth.Permission +} + +func StorageAuth(apiKey string) (sealer.StorageAuth, error) { + if apiKey == "" { + return nil, xerrors.Errorf("no api key provided") + } + + rawKey, err := base64.StdEncoding.DecodeString(apiKey) + if err != nil { + return nil, xerrors.Errorf("decoding api key: %w", err) + } + + key := jwt.NewHS256(rawKey) + + p := JwtPayload{ + Allow: []auth.Permission{"admin"}, + } + + token, err := jwt.Sign(&p, key) + if err != nil { + return nil, err + } + + headers := http.Header{} + headers.Add("Authorization", "Bearer "+string(token)) + return sealer.StorageAuth(headers), nil +} + +type Deps struct { + Cfg *config.LotusProviderConfig + DB *harmonydb.DB + Full api.FullNode + Verif storiface.Verifier + LW *sealer.LocalWorker + As *ctladdr.AddressSelector + Maddrs []dtypes.MinerAddress + Stor *paths.Remote + Si *paths.DBIndex + LocalStore *paths.Local + ListenAddr string +} + +const ( + FlagRepoPath = "repo-path" +) + +func (deps *Deps) PopulateRemainingDeps(ctx context.Context, cctx *cli.Context, makeRepo bool) error { + + var err error + if makeRepo { + // Open repo + repoPath := cctx.String(FlagRepoPath) + fmt.Println("repopath", repoPath) + r, err := repo.NewFS(repoPath) + if err != nil { + return err + } + + ok, err := r.Exists() + if err != nil { + return err + } + if !ok { + if err := r.Init(repo.Provider); err != nil { + return err + } + } + } + + if deps.Cfg == nil { + deps.DB, err = MakeDB(cctx) + if err != nil { + return err + } + } + + if deps.Cfg == nil { + // The config feeds into task runners & their helpers + deps.Cfg, err = GetConfig(cctx, deps.DB) + if err != nil { + return err + } + } + + log.Debugw("config", "config", deps.Cfg) + + if deps.Verif == nil { + deps.Verif = ffiwrapper.ProofVerifier + } + + if deps.As == nil { + deps.As, err = provider.AddressSelector(&deps.Cfg.Addresses)() + if err != nil { + return err + } + } + + if deps.Si == nil { + de, err := journal.ParseDisabledEvents(deps.Cfg.Journal.DisabledEvents) + if err != nil { + return err + } + j, err := fsjournal.OpenFSJournalPath(cctx.String("journal"), de) + if err != nil { + return err + } + defer func() { + <-ctx.Done() + _ = j.Close() + }() + + al := alerting.NewAlertingSystem(j) + deps.Si = paths.NewDBIndex(al, deps.DB) + } + + if deps.Full == nil { + var fullCloser func() + deps.Full, fullCloser, err = cliutil.GetFullNodeAPIV1LotusProvider(cctx, deps.Cfg.Apis.ChainApiInfo) + if err != nil { + return err + } + + go func() { + <-ctx.Done() + fullCloser() + }() + } + + bls := &paths.BasicLocalStorage{ + PathToJSON: cctx.String("storage-json"), + } + + if deps.ListenAddr == "" { + listenAddr := cctx.String("listen") + const unspecifiedAddress = "0.0.0.0" + addressSlice := strings.Split(listenAddr, ":") + if ip := net.ParseIP(addressSlice[0]); ip != nil { + if ip.String() == unspecifiedAddress { + rip, err := deps.DB.GetRoutableIP() + if err != nil { + return err + } + deps.ListenAddr = rip + ":" + addressSlice[1] + } + } + } + if deps.LocalStore == nil { + deps.LocalStore, err = paths.NewLocal(ctx, bls, deps.Si, []string{"http://" + deps.ListenAddr + "/remote"}) + if err != nil { + return err + } + } + + sa, err := StorageAuth(deps.Cfg.Apis.StorageRPCSecret) + if err != nil { + return xerrors.Errorf(`'%w' while parsing the config toml's + [Apis] + StorageRPCSecret=%v +Get it with: jq .PrivateKey ~/.lotus-miner/keystore/MF2XI2BNNJ3XILLQOJUXMYLUMU`, err, deps.Cfg.Apis.StorageRPCSecret) + } + if deps.Stor == nil { + deps.Stor = paths.NewRemote(deps.LocalStore, deps.Si, http.Header(sa), 10, &paths.DefaultPartialFileHandler{}) + } + if deps.LW == nil { + wstates := statestore.New(dssync.MutexWrap(ds.NewMapDatastore())) + + // todo localWorker isn't the abstraction layer we want to use here, we probably want to go straight to ffiwrapper + // maybe with a lotus-provider specific abstraction. LocalWorker does persistent call tracking which we probably + // don't need (ehh.. maybe we do, the async callback system may actually work decently well with harmonytask) + deps.LW = sealer.NewLocalWorker(sealer.WorkerConfig{}, deps.Stor, deps.LocalStore, deps.Si, nil, wstates) + } + if len(deps.Maddrs) == 0 { + for _, s := range deps.Cfg.Addresses.MinerAddresses { + addr, err := address.NewFromString(s) + if err != nil { + return err + } + deps.Maddrs = append(deps.Maddrs, dtypes.MinerAddress(addr)) + } + } + return nil +} + +func GetConfig(cctx *cli.Context, db *harmonydb.DB) (*config.LotusProviderConfig, error) { + lp := config.DefaultLotusProvider() + have := []string{} + layers := cctx.StringSlice("layers") + for _, layer := range layers { + text := "" + err := db.QueryRow(cctx.Context, `SELECT config FROM harmony_config WHERE title=$1`, layer).Scan(&text) + if err != nil { + if strings.Contains(err.Error(), sql.ErrNoRows.Error()) { + return nil, fmt.Errorf("missing layer '%s' ", layer) + } + if layer == "base" { + return nil, errors.New(`lotus-provider defaults to a layer named 'base'. + Either use 'migrate' command or edit a base.toml and upload it with: lotus-provider config set base.toml`) + } + return nil, fmt.Errorf("could not read layer '%s': %w", layer, err) + } + meta, err := toml.Decode(text, &lp) + if err != nil { + return nil, fmt.Errorf("could not read layer, bad toml %s: %w", layer, err) + } + for _, k := range meta.Keys() { + have = append(have, strings.Join(k, " ")) + } + } + _ = have // FUTURE: verify that required fields are here. + // If config includes 3rd-party config, consider JSONSchema as a way that + // 3rd-parties can dynamically include config requirements and we can + // validate the config. Because of layering, we must validate @ startup. + return lp, nil +} diff --git a/cmd/lotus-provider/main.go b/cmd/lotus-provider/main.go index 19cc6f5f9..48ff70122 100644 --- a/cmd/lotus-provider/main.go +++ b/cmd/lotus-provider/main.go @@ -15,6 +15,7 @@ import ( "github.com/filecoin-project/lotus/build" lcli "github.com/filecoin-project/lotus/cli" cliutil "github.com/filecoin-project/lotus/cli/util" + "github.com/filecoin-project/lotus/cmd/lotus-provider/deps" "github.com/filecoin-project/lotus/lib/lotuslog" "github.com/filecoin-project/lotus/lib/tracing" "github.com/filecoin-project/lotus/node/repo" @@ -131,7 +132,7 @@ func main() { Value: "base", }, &cli.StringFlag{ - Name: FlagRepoPath, + Name: deps.FlagRepoPath, EnvVars: []string{"LOTUS_REPO_PATH"}, Value: "~/.lotusprovider", }, @@ -144,7 +145,7 @@ func main() { After: func(c *cli.Context) error { if r := recover(); r != nil { // Generate report in LOTUS_PATH and re-raise panic - build.GeneratePanicReport(c.String("panic-reports"), c.String(FlagRepoPath), c.App.Name) + build.GeneratePanicReport(c.String("panic-reports"), c.String(deps.FlagRepoPath), c.App.Name) panic(r) } return nil @@ -154,7 +155,3 @@ func main() { app.Metadata["repoType"] = repo.Provider lcli.RunApp(app) } - -const ( - FlagRepoPath = "repo-path" -) diff --git a/cmd/lotus-provider/proving.go b/cmd/lotus-provider/proving.go index 577b5b5f9..001108712 100644 --- a/cmd/lotus-provider/proving.go +++ b/cmd/lotus-provider/proving.go @@ -15,6 +15,7 @@ import ( "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/dline" + "github.com/filecoin-project/lotus/cmd/lotus-provider/deps" "github.com/filecoin-project/lotus/lib/harmony/harmonydb" "github.com/filecoin-project/lotus/provider" ) @@ -62,18 +63,18 @@ var wdPostTaskCmd = &cli.Command{ Action: func(cctx *cli.Context) error { ctx := context.Background() - deps, err := getDeps(ctx, cctx) + deps, err := deps.GetDeps(ctx, cctx) if err != nil { return err } - ts, err := deps.full.ChainHead(ctx) + ts, err := deps.Full.ChainHead(ctx) if err != nil { return xerrors.Errorf("cannot get chainhead %w", err) } ht := ts.Height() - addr, err := address.NewFromString(deps.cfg.Addresses.MinerAddresses[0]) + addr, err := address.NewFromString(deps.Cfg.Addresses.MinerAddresses[0]) if err != nil { return xerrors.Errorf("cannot get miner address %w", err) } @@ -82,7 +83,7 @@ var wdPostTaskCmd = &cli.Command{ return xerrors.Errorf("cannot get miner id %w", err) } var id int64 - _, err = deps.db.BeginTransaction(ctx, func(tx *harmonydb.Tx) (commit bool, err error) { + _, err = deps.DB.BeginTransaction(ctx, func(tx *harmonydb.Tx) (commit bool, err error) { err = tx.QueryRow(`INSERT INTO harmony_task (name, posted_time, added_by) VALUES ('WdPost', CURRENT_TIMESTAMP, 123) RETURNING id`).Scan(&id) if err != nil { log.Error("inserting harmony_task: ", err) @@ -108,7 +109,7 @@ var wdPostTaskCmd = &cli.Command{ var result sql.NullString for { time.Sleep(time.Second) - err = deps.db.QueryRow(ctx, `SELECT result FROM harmony_test WHERE task_id=$1`, id).Scan(&result) + err = deps.DB.QueryRow(ctx, `SELECT result FROM harmony_test WHERE task_id=$1`, id).Scan(&result) if err != nil { return xerrors.Errorf("reading result from harmony_test: %w", err) } @@ -157,29 +158,29 @@ It will not send any messages to the chain. Since it can compute any deadline, o Action: func(cctx *cli.Context) error { ctx := context.Background() - deps, err := getDeps(ctx, cctx) + deps, err := deps.GetDeps(ctx, cctx) if err != nil { return err } - wdPostTask, wdPoStSubmitTask, derlareRecoverTask, err := provider.WindowPostScheduler(ctx, deps.cfg.Fees, deps.cfg.Proving, deps.full, deps.verif, deps.lw, nil, - deps.as, deps.maddrs, deps.db, deps.stor, deps.si, deps.cfg.Subsystems.WindowPostMaxTasks) + wdPostTask, wdPoStSubmitTask, derlareRecoverTask, err := provider.WindowPostScheduler(ctx, deps.Cfg.Fees, deps.Cfg.Proving, deps.Full, deps.Verif, deps.LW, nil, + deps.As, deps.Maddrs, deps.DB, deps.Stor, deps.Si, deps.Cfg.Subsystems.WindowPostMaxTasks) if err != nil { return err } _, _ = wdPoStSubmitTask, derlareRecoverTask - if len(deps.maddrs) == 0 { + if len(deps.Maddrs) == 0 { return errors.New("no miners to compute WindowPoSt for") } - head, err := deps.full.ChainHead(ctx) + head, err := deps.Full.ChainHead(ctx) if err != nil { return xerrors.Errorf("failed to get chain head: %w", err) } di := dline.NewInfo(head.Height(), cctx.Uint64("deadline"), 0, 0, 0, 10 /*challenge window*/, 0, 0) - for _, maddr := range deps.maddrs { + for _, maddr := range deps.Maddrs { out, err := wdPostTask.DoPartition(ctx, head, address.Address(maddr), di, cctx.Uint64("partition")) if err != nil { fmt.Println("Error computing WindowPoSt for miner", maddr, err) diff --git a/cmd/lotus-provider/rpc/rpc.go b/cmd/lotus-provider/rpc/rpc.go index 3ae3e2a1f..0fe1039d9 100644 --- a/cmd/lotus-provider/rpc/rpc.go +++ b/cmd/lotus-provider/rpc/rpc.go @@ -1,21 +1,32 @@ +// Package rpc provides all direct access to this node. package rpc import ( "context" + "encoding/base64" + "encoding/json" + "net" "net/http" + "time" + "github.com/gbrlsnchs/jwt/v3" "github.com/gorilla/mux" + logging "github.com/ipfs/go-log/v2" + "go.opencensus.io/tag" + "golang.org/x/xerrors" - // logging "github.com/ipfs/go-log/v2" "github.com/filecoin-project/go-jsonrpc" "github.com/filecoin-project/go-jsonrpc/auth" "github.com/filecoin-project/lotus/api" + "github.com/filecoin-project/lotus/cmd/lotus-provider/deps" "github.com/filecoin-project/lotus/lib/rpcenc" + "github.com/filecoin-project/lotus/metrics" "github.com/filecoin-project/lotus/metrics/proxy" + "github.com/filecoin-project/lotus/storage/paths" ) -//var log = logging.Logger("lp/rpc") +var log = logging.Logger("lp/rpc") func LotusProviderHandler( authv func(ctx context.Context, token string) ([]auth.Permission, error), @@ -49,3 +60,79 @@ func LotusProviderHandler( } return ah } + +type ProviderAPI struct { + *deps.Deps + ShutdownChan chan struct{} +} + +func (p *ProviderAPI) Version(context.Context) (api.Version, error) { + return api.ProviderAPIVersion0, nil +} + +// Trigger shutdown +func (p *ProviderAPI) Shutdown(context.Context) error { + close(p.ShutdownChan) + return nil +} + +func ListenAndServe(ctx context.Context, dependencies *deps.Deps, shutdownChan chan struct{}) error { + + fh := &paths.FetchHandler{Local: dependencies.LocalStore, PfHandler: &paths.DefaultPartialFileHandler{}} + remoteHandler := func(w http.ResponseWriter, r *http.Request) { + if !auth.HasPerm(r.Context(), nil, api.PermAdmin) { + w.WriteHeader(401) + _ = json.NewEncoder(w).Encode(struct{ Error string }{"unauthorized: missing admin permission"}) + return + } + + fh.ServeHTTP(w, r) + } + // local APIs + { + // debugging + mux := mux.NewRouter() + mux.PathPrefix("/").Handler(http.DefaultServeMux) // pprof + mux.PathPrefix("/remote").HandlerFunc(remoteHandler) + } + + var authVerify func(context.Context, string) ([]auth.Permission, error) + { + privateKey, err := base64.StdEncoding.DecodeString(dependencies.Cfg.Apis.StorageRPCSecret) + if err != nil { + return xerrors.Errorf("decoding storage rpc secret: %w", err) + } + authVerify = func(ctx context.Context, token string) ([]auth.Permission, error) { + var payload deps.JwtPayload + if _, err := jwt.Verify([]byte(token), jwt.NewHS256(privateKey), &payload); err != nil { + return nil, xerrors.Errorf("JWT Verification failed: %w", err) + } + + return payload.Allow, nil + } + } + // Serve the RPC. + srv := &http.Server{ + Handler: LotusProviderHandler( + authVerify, + remoteHandler, + &ProviderAPI{dependencies, shutdownChan}, + true), + ReadHeaderTimeout: time.Minute * 3, + BaseContext: func(listener net.Listener) context.Context { + ctx, _ := tag.New(context.Background(), tag.Upsert(metrics.APIInterface, "lotus-worker")) + return ctx + }, + } + + go func() { + <-ctx.Done() + log.Warn("Shutting down...") + if err := srv.Shutdown(context.TODO()); err != nil { + log.Errorf("shutting down RPC server failed: %s", err) + } + log.Warn("Graceful shutdown successful") + }() + + return nil +} diff --git a/cmd/lotus-provider/run.go b/cmd/lotus-provider/run.go index bf19ee537..87d073963 100644 --- a/cmd/lotus-provider/run.go +++ b/cmd/lotus-provider/run.go @@ -2,54 +2,23 @@ package main import ( "context" - "encoding/base64" - "encoding/json" "fmt" - "net" - "net/http" "os" - "strings" "time" - "github.com/gbrlsnchs/jwt/v3" - "github.com/gorilla/mux" - ds "github.com/ipfs/go-datastore" - dssync "github.com/ipfs/go-datastore/sync" "github.com/pkg/errors" - "github.com/samber/lo" "github.com/urfave/cli/v2" "go.opencensus.io/stats" "go.opencensus.io/tag" - "golang.org/x/xerrors" - "github.com/filecoin-project/go-address" - "github.com/filecoin-project/go-jsonrpc/auth" - "github.com/filecoin-project/go-statestore" - - "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/build" lcli "github.com/filecoin-project/lotus/cli" - cliutil "github.com/filecoin-project/lotus/cli/util" + "github.com/filecoin-project/lotus/cmd/lotus-provider/deps" "github.com/filecoin-project/lotus/cmd/lotus-provider/rpc" - "github.com/filecoin-project/lotus/journal" - "github.com/filecoin-project/lotus/journal/alerting" - "github.com/filecoin-project/lotus/journal/fsjournal" - "github.com/filecoin-project/lotus/lib/harmony/harmonydb" - "github.com/filecoin-project/lotus/lib/harmony/harmonytask" + "github.com/filecoin-project/lotus/cmd/lotus-provider/tasks" "github.com/filecoin-project/lotus/lib/ulimit" "github.com/filecoin-project/lotus/metrics" "github.com/filecoin-project/lotus/node" - "github.com/filecoin-project/lotus/node/config" - "github.com/filecoin-project/lotus/node/modules/dtypes" - "github.com/filecoin-project/lotus/node/repo" - "github.com/filecoin-project/lotus/provider" - "github.com/filecoin-project/lotus/provider/lpmessage" - "github.com/filecoin-project/lotus/provider/lpwinning" - "github.com/filecoin-project/lotus/storage/ctladdr" - "github.com/filecoin-project/lotus/storage/paths" - "github.com/filecoin-project/lotus/storage/sealer" - "github.com/filecoin-project/lotus/storage/sealer/ffiwrapper" - "github.com/filecoin-project/lotus/storage/sealer/storiface" ) type stackTracer interface { @@ -144,112 +113,22 @@ var runCmd = &cli.Command{ } } - deps, err := getDeps(ctx, cctx) - - if err != nil { - return err - } - cfg, db, full, verif, lw, as, maddrs, stor, si, localStore := deps.cfg, deps.db, deps.full, deps.verif, deps.lw, deps.as, deps.maddrs, deps.stor, deps.si, deps.localStore - - var activeTasks []harmonytask.TaskInterface - - sender, sendTask := lpmessage.NewSender(full, full, db) - activeTasks = append(activeTasks, sendTask) - - /////////////////////////////////////////////////////////////////////// - ///// Task Selection - /////////////////////////////////////////////////////////////////////// - { - - if cfg.Subsystems.EnableWindowPost { - wdPostTask, wdPoStSubmitTask, derlareRecoverTask, err := provider.WindowPostScheduler(ctx, cfg.Fees, cfg.Proving, full, verif, lw, sender, - as, maddrs, db, stor, si, cfg.Subsystems.WindowPostMaxTasks) - if err != nil { - return err - } - activeTasks = append(activeTasks, wdPostTask, wdPoStSubmitTask, derlareRecoverTask) - } - - if cfg.Subsystems.EnableWinningPost { - winPoStTask := lpwinning.NewWinPostTask(cfg.Subsystems.WinningPostMaxTasks, db, lw, verif, full, maddrs) - activeTasks = append(activeTasks, winPoStTask) - } - } - log.Infow("This lotus_provider instance handles", - "miner_addresses", maddrs, - "tasks", lo.Map(activeTasks, func(t harmonytask.TaskInterface, _ int) string { return t.TypeDetails().Name })) - - taskEngine, err := harmonytask.New(db, activeTasks, deps.listenAddr) + var dependencies *deps.Deps + err = dependencies.PopulateRemainingDeps(ctx, cctx, true) if err != nil { return err } + taskEngine, err := tasks.StartTasks(ctx, dependencies) + if err != nil { + return nil + } defer taskEngine.GracefullyTerminate(time.Hour) - fh := &paths.FetchHandler{Local: localStore, PfHandler: &paths.DefaultPartialFileHandler{}} - remoteHandler := func(w http.ResponseWriter, r *http.Request) { - if !auth.HasPerm(r.Context(), nil, api.PermAdmin) { - w.WriteHeader(401) - _ = json.NewEncoder(w).Encode(struct{ Error string }{"unauthorized: missing admin permission"}) - return - } - - fh.ServeHTTP(w, r) + err = rpc.ListenAndServe(ctx, dependencies, shutdownChan) // Monitor for shutdown. + if err != nil { + return err } - // local APIs - { - // debugging - mux := mux.NewRouter() - mux.PathPrefix("/").Handler(http.DefaultServeMux) // pprof - mux.PathPrefix("/remote").HandlerFunc(remoteHandler) - - /*ah := &auth.Handler{ - Verify: authv, - Next: mux.ServeHTTP, - }*/ // todo - - } - - var authVerify func(context.Context, string) ([]auth.Permission, error) - { - privateKey, err := base64.StdEncoding.DecodeString(deps.cfg.Apis.StorageRPCSecret) - if err != nil { - return xerrors.Errorf("decoding storage rpc secret: %w", err) - } - authVerify = func(ctx context.Context, token string) ([]auth.Permission, error) { - var payload jwtPayload - if _, err := jwt.Verify([]byte(token), jwt.NewHS256(privateKey), &payload); err != nil { - return nil, xerrors.Errorf("JWT Verification failed: %w", err) - } - - return payload.Allow, nil - } - } - // Serve the RPC. - srv := &http.Server{ - Handler: rpc.LotusProviderHandler( - authVerify, - remoteHandler, - &ProviderAPI{deps, shutdownChan}, - true), - ReadHeaderTimeout: time.Minute * 3, - BaseContext: func(listener net.Listener) context.Context { - ctx, _ := tag.New(context.Background(), tag.Upsert(metrics.APIInterface, "lotus-worker")) - return ctx - }, - } - - go func() { - <-ctx.Done() - log.Warn("Shutting down...") - if err := srv.Shutdown(context.TODO()); err != nil { - log.Errorf("shutting down RPC server failed: %s", err) - } - log.Warn("Graceful shutdown successful") - }() - - // Monitor for shutdown. - // TODO provide a graceful shutdown API on shutdownChan finishCh := node.MonitorShutdown(shutdownChan) //node.ShutdownHandler{Component: "rpc server", StopFunc: rpcStopper}, //node.ShutdownHandler{Component: "provider", StopFunc: stop}, @@ -257,203 +136,3 @@ var runCmd = &cli.Command{ return nil }, } - -func makeDB(cctx *cli.Context) (*harmonydb.DB, error) { - dbConfig := config.HarmonyDB{ - Username: cctx.String("db-user"), - Password: cctx.String("db-password"), - Hosts: strings.Split(cctx.String("db-host"), ","), - Database: cctx.String("db-name"), - Port: cctx.String("db-port"), - } - return harmonydb.NewFromConfig(dbConfig) -} - -type jwtPayload struct { - Allow []auth.Permission -} - -func StorageAuth(apiKey string) (sealer.StorageAuth, error) { - if apiKey == "" { - return nil, xerrors.Errorf("no api key provided") - } - - rawKey, err := base64.StdEncoding.DecodeString(apiKey) - if err != nil { - return nil, xerrors.Errorf("decoding api key: %w", err) - } - - key := jwt.NewHS256(rawKey) - - p := jwtPayload{ - Allow: []auth.Permission{"admin"}, - } - - token, err := jwt.Sign(&p, key) - if err != nil { - return nil, err - } - - headers := http.Header{} - headers.Add("Authorization", "Bearer "+string(token)) - return sealer.StorageAuth(headers), nil -} - -type Deps struct { - cfg *config.LotusProviderConfig - db *harmonydb.DB - full api.FullNode - verif storiface.Verifier - lw *sealer.LocalWorker - as *ctladdr.AddressSelector - maddrs []dtypes.MinerAddress - stor *paths.Remote - si *paths.DBIndex - localStore *paths.Local - listenAddr string -} - -func getDeps(ctx context.Context, cctx *cli.Context) (*Deps, error) { - // Open repo - - repoPath := cctx.String(FlagRepoPath) - fmt.Println("repopath", repoPath) - r, err := repo.NewFS(repoPath) - if err != nil { - return nil, err - } - - ok, err := r.Exists() - if err != nil { - return nil, err - } - if !ok { - if err := r.Init(repo.Provider); err != nil { - return nil, err - } - } - - db, err := makeDB(cctx) - if err != nil { - return nil, err - } - - /////////////////////////////////////////////////////////////////////// - ///// Dependency Setup - /////////////////////////////////////////////////////////////////////// - - // The config feeds into task runners & their helpers - cfg, err := getConfig(cctx, db) - if err != nil { - return nil, err - } - - log.Debugw("config", "config", cfg) - - var verif storiface.Verifier = ffiwrapper.ProofVerifier - - as, err := provider.AddressSelector(&cfg.Addresses)() - if err != nil { - return nil, err - } - - de, err := journal.ParseDisabledEvents(cfg.Journal.DisabledEvents) - if err != nil { - return nil, err - } - j, err := fsjournal.OpenFSJournalPath(cctx.String("journal"), de) - if err != nil { - return nil, err - } - - full, fullCloser, err := cliutil.GetFullNodeAPIV1LotusProvider(cctx, cfg.Apis.ChainApiInfo) - if err != nil { - return nil, err - } - - go func() { - select { - case <-ctx.Done(): - fullCloser() - _ = j.Close() - } - }() - sa, err := StorageAuth(cfg.Apis.StorageRPCSecret) - if err != nil { - return nil, xerrors.Errorf(`'%w' while parsing the config toml's - [Apis] - StorageRPCSecret=%v -Get it with: jq .PrivateKey ~/.lotus-miner/keystore/MF2XI2BNNJ3XILLQOJUXMYLUMU`, err, cfg.Apis.StorageRPCSecret) - } - - al := alerting.NewAlertingSystem(j) - si := paths.NewDBIndex(al, db) - bls := &paths.BasicLocalStorage{ - PathToJSON: cctx.String("storage-json"), - } - - listenAddr := cctx.String("listen") - const unspecifiedAddress = "0.0.0.0" - addressSlice := strings.Split(listenAddr, ":") - if ip := net.ParseIP(addressSlice[0]); ip != nil { - if ip.String() == unspecifiedAddress { - rip, err := db.GetRoutableIP() - if err != nil { - return nil, err - } - listenAddr = rip + ":" + addressSlice[1] - } - } - localStore, err := paths.NewLocal(ctx, bls, si, []string{"http://" + listenAddr + "/remote"}) - if err != nil { - return nil, err - } - - stor := paths.NewRemote(localStore, si, http.Header(sa), 10, &paths.DefaultPartialFileHandler{}) - - wstates := statestore.New(dssync.MutexWrap(ds.NewMapDatastore())) - - // todo localWorker isn't the abstraction layer we want to use here, we probably want to go straight to ffiwrapper - // maybe with a lotus-provider specific abstraction. LocalWorker does persistent call tracking which we probably - // don't need (ehh.. maybe we do, the async callback system may actually work decently well with harmonytask) - lw := sealer.NewLocalWorker(sealer.WorkerConfig{}, stor, localStore, si, nil, wstates) - - var maddrs []dtypes.MinerAddress - for _, s := range cfg.Addresses.MinerAddresses { - addr, err := address.NewFromString(s) - if err != nil { - return nil, err - } - maddrs = append(maddrs, dtypes.MinerAddress(addr)) - } - - return &Deps{ // lint: intentionally not-named so it will fail if one is forgotten - cfg, - db, - full, - verif, - lw, - as, - maddrs, - stor, - si, - localStore, - listenAddr, - }, nil - -} - -type ProviderAPI struct { - *Deps - ShutdownChan chan struct{} -} - -func (p *ProviderAPI) Version(context.Context) (api.Version, error) { - return api.ProviderAPIVersion0, nil -} - -// Trigger shutdown -func (p *ProviderAPI) Shutdown(context.Context) error { - close(p.ShutdownChan) - return nil -} diff --git a/cmd/lotus-provider/tasks/tasks.go b/cmd/lotus-provider/tasks/tasks.go new file mode 100644 index 000000000..18c56a744 --- /dev/null +++ b/cmd/lotus-provider/tasks/tasks.go @@ -0,0 +1,58 @@ +// Package tasks contains tasks that can be run by the lotus-provider command. +package tasks + +import ( + "context" + + logging "github.com/ipfs/go-log/v2" + + "github.com/filecoin-project/lotus/cmd/lotus-provider/deps" + "github.com/filecoin-project/lotus/lib/harmony/harmonytask" + "github.com/filecoin-project/lotus/provider" + "github.com/filecoin-project/lotus/provider/lpmessage" + "github.com/filecoin-project/lotus/provider/lpwinning" + "github.com/samber/lo" +) + +var log = logging.Logger("lotus-provider/deps") + +func StartTasks(ctx context.Context, dependencies *deps.Deps) (*harmonytask.TaskEngine, error) { + cfg := dependencies.Cfg + db := dependencies.DB + full := dependencies.Full + verif := dependencies.Verif + lw := dependencies.LW + as := dependencies.As + maddrs := dependencies.Maddrs + stor := dependencies.Stor + si := dependencies.Si + var activeTasks []harmonytask.TaskInterface + + sender, sendTask := lpmessage.NewSender(full, full, db) + activeTasks = append(activeTasks, sendTask) + + /////////////////////////////////////////////////////////////////////// + ///// Task Selection + /////////////////////////////////////////////////////////////////////// + { + + if cfg.Subsystems.EnableWindowPost { + wdPostTask, wdPoStSubmitTask, derlareRecoverTask, err := provider.WindowPostScheduler(ctx, cfg.Fees, cfg.Proving, full, verif, lw, sender, + as, maddrs, db, stor, si, cfg.Subsystems.WindowPostMaxTasks) + if err != nil { + return nil, err + } + activeTasks = append(activeTasks, wdPostTask, wdPoStSubmitTask, derlareRecoverTask) + } + + if cfg.Subsystems.EnableWinningPost { + winPoStTask := lpwinning.NewWinPostTask(cfg.Subsystems.WinningPostMaxTasks, db, lw, verif, full, maddrs) + activeTasks = append(activeTasks, winPoStTask) + } + } + log.Infow("This lotus_provider instance handles", + "miner_addresses", maddrs, + "tasks", lo.Map(activeTasks, func(t harmonytask.TaskInterface, _ int) string { return t.TypeDetails().Name })) + + return harmonytask.New(db, activeTasks, dependencies.ListenAddr) +} From 8db6e73c9074ad41af0940a7ad68e0e5134ff6cc Mon Sep 17 00:00:00 2001 From: Phi Date: Mon, 4 Dec 2023 10:55:26 +0100 Subject: [PATCH 005/394] Bump version in master Bump version in master --- build/openrpc/full.json.gz | Bin 34818 -> 34820 bytes build/openrpc/gateway.json.gz | Bin 11875 -> 11872 bytes build/openrpc/miner.json.gz | Bin 16370 -> 16371 bytes build/openrpc/worker.json.gz | Bin 6052 -> 6051 bytes build/version.go | 2 +- documentation/en/cli-lotus-miner.md | 2 +- documentation/en/cli-lotus-worker.md | 2 +- documentation/en/cli-lotus.md | 2 +- 8 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/openrpc/full.json.gz b/build/openrpc/full.json.gz index 4e3f31fbac65da324b9d4afcfa0df76515c27435..45489699a662d670aa38c7821e2bd49ceb88fb6d 100644 GIT binary patch delta 29452 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 Do?x+@ diff --git a/build/openrpc/gateway.json.gz b/build/openrpc/gateway.json.gz index fe52522fdc80bd103c059f6980f4e7d8ed94f483..52715057145366a37930c2c9c54ad529f7c593a2 100644 GIT binary patch delta 8869 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 delta 8845 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 literal 6052 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 diff --git a/build/version.go b/build/version.go index 0758686f6..215940e7a 100644 --- a/build/version.go +++ b/build/version.go @@ -37,7 +37,7 @@ func BuildTypeString() string { } // BuildVersion is the local build version -const BuildVersion = "1.25.1-dev" +const BuildVersion = "1.25.2-dev" func UserVersion() string { if os.Getenv("LOTUS_VERSION_IGNORE_COMMIT") == "1" { diff --git a/documentation/en/cli-lotus-miner.md b/documentation/en/cli-lotus-miner.md index f3fb8a9e1..82b6b300a 100644 --- a/documentation/en/cli-lotus-miner.md +++ b/documentation/en/cli-lotus-miner.md @@ -7,7 +7,7 @@ USAGE: lotus-miner [global options] command [command options] [arguments...] VERSION: - 1.25.1-dev + 1.25.2-dev COMMANDS: init Initialize a lotus miner repo diff --git a/documentation/en/cli-lotus-worker.md b/documentation/en/cli-lotus-worker.md index cf0b9fd36..73774503c 100644 --- a/documentation/en/cli-lotus-worker.md +++ b/documentation/en/cli-lotus-worker.md @@ -7,7 +7,7 @@ USAGE: lotus-worker [global options] command [command options] [arguments...] VERSION: - 1.25.1-dev + 1.25.2-dev COMMANDS: run Start lotus worker diff --git a/documentation/en/cli-lotus.md b/documentation/en/cli-lotus.md index a6a6ea30a..959aaefac 100644 --- a/documentation/en/cli-lotus.md +++ b/documentation/en/cli-lotus.md @@ -7,7 +7,7 @@ USAGE: lotus [global options] command [command options] [arguments...] VERSION: - 1.25.1-dev + 1.25.2-dev COMMANDS: daemon Start a lotus daemon process From c9f13253fd1952db0469fd1369c19299b9b477d9 Mon Sep 17 00:00:00 2001 From: Phi Date: Tue, 5 Dec 2023 08:13:25 +0100 Subject: [PATCH 006/394] Make gen Make gen --- build/openrpc/full.json.gz | Bin 34820 -> 34817 bytes build/openrpc/gateway.json.gz | Bin 11872 -> 11875 bytes build/openrpc/miner.json.gz | Bin 16371 -> 16370 bytes build/openrpc/worker.json.gz | Bin 6051 -> 6052 bytes 4 files changed, 0 insertions(+), 0 deletions(-) diff --git a/build/openrpc/full.json.gz b/build/openrpc/full.json.gz index 45489699a662d670aa38c7821e2bd49ceb88fb6d..ad2116cb9187f20cea37fef3a37566fd3a2730cf 100644 GIT binary patch delta 30260 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 diff --git a/build/openrpc/gateway.json.gz b/build/openrpc/gateway.json.gz index 52715057145366a37930c2c9c54ad529f7c593a2..48ad91135e370665117694d7515928b8773c1ad8 100644 GIT binary patch delta 8840 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 CGd(c? delta 8864 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 iK;e{Bd|_osyX`#_^YQWf?_`$% diff --git a/build/openrpc/miner.json.gz b/build/openrpc/miner.json.gz index 120a1c457ff1b645d72a48626162ac17610296df..a16ed4f260fa20727e54ba0b3e2a51c8aa09f1ec 100644 GIT binary patch delta 15993 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 literal 6051 zcmai&RZtwV6YH0xHDLAcYfTR;5sm9upmLgAPMd=xchAG z-rcQz*sATP?(@)9N9xq~HA5^G(tifx2o&I6($x(%BHRY15Mx1u;#vzc`S61pXT$g~@;Zjeal+}``{bq{) z0QFgC;eLFU?J>#bK33O~q$n`K{pLMtCSL~K*9scHfl;)(T7faXk{?Eig#LF(Z(WPu zh~!$(^J1@lcuKN6QAU*!xXBskL7j4^8fmL+CPfU+x%~;+JFwih%(N5@IV+**>?$(r z4GgEg3caUoM3?-k6-sYuOMlL9E=RvL)VTM+v>osy&dgJvN~DD!+YUXirOF2s@!t{s zl)_YU%nu+=>>-Syl04Y&tm|J3xi@^G<`VLmH1WQ8{627I1?ie&Y&uTj-Od$Q7>Fa8 znM4zlo-fSsX|eB+3(t&-*O-kW#Jfleo;%%%7~C7@UeH`VH(T7~m8!(XGpYXw0of;Q z^rC)mz|$1PX&{UoX$Xh-5aT;lIc|Tp+SV(4D6{ffnZdif(GLyVGUVg`q6r|ZL~x4 zdNB6FSOSN_0bFMG(T(GKwKrZeziqzj7mHqxwz`Qvb8Ix5<5K2BX;CAmB`W)a#m>+s zMQ@){y};Duc19;%QqkH2jUy(q21`U(rBYtYuHXd!1S1;X(qq0`*Yh>D^4h9LnF36| z1;S5bmIM}t6@ztHQkx%vKT5EGl#xYIkcIMhgVtIcxXFoh!g&g6M4)?-78d1mQ0K{) zQoTCk4yT1+n0Ex zq2$D9`kn|XH=0;9-?;Z0oO7i*#KsvGI&5);X~7EcXmgH33k7)Q>G9`4=JYW_O$9m| zuETG!Q?CdpGVnpUiOKT&u#uyDL&$L?-Gd{-r`S90#C2ppTdJ}jG4x}2B9c|mCAVxE zNZ@MWzsJUG-(6qn3HKstY?QkWS_f7FlYiai5{Z+E-(b3-*v{dOZ%pcQRSrs7;vA}W)}PP_8MJlX&2qmR)Oo~l!=4sm$J%(jo6u=x&bDv-2KigLqcYq_w z9HRIoHl?TlZwumcn>vj_1nK66Xjon4LZuURcRV6!F=y>s)Ril3+bQPBYC(+lQ1_Ss z8FWnHFi@Bx2!ry=VVWG;66h!#PX~8-)rb_;k(7}XtD^B|wFQ9c_bVZL-aL~9n%{O{ z#l3O5bz&mstKt*1d|U%*FFK8uGodz3z?zv-+T41?V5-9`WX`?+zC zo}lwvxnv0pbHtb@zW^{kvT&>$gGYKDi zr!7)O`tR>uCWh2O6YUh4N0%vBVN2!FnSfe!;8+}T#<^j}Kw&}}jwnxfDHu-_h<#bZ zMZgClwdPKv_M#h2C_9G@9=JMx&Zxq+Vp{ivd`RiRoM*_EdKy_w4!nJuIlSGvU7<- z=kkL9BIgioo@}*>^|}x!_6a*A2h)3v+hYQ9Oj|PX&)d%t=?xpJ!WZ_ubr|WXqy(N4 z_dfe^8p|d7(kmBr`EjRKq&COC~MCvuO1Kn3+s zVtee9ff6vaWR6b82F?)_xmn);+m`oy{Z(N9<>l}1tQ%jYW~T>3Im{}pp0+NKeXAI9 z+AH=90wev#apsoyg=Q&?D6+TQA-*8th0$61VRMEAhVZDP|byhV@T zcJ}jA_DpYoYeukS-2s?G0e;M9EpaiXF|JSdK+fJU@CBr!C(P7|u@xD&Oe8pY8>0c1au-D` zu7~s3i2G`l+04+#3(;Z1xy)ResU;7)YBRbN0KviBkJT@BqL=A9KgQQs_fD%dug7Du#}jN>f|7`(x%b+zXg_c9I- zSJ~eA4&w+uj}NE&faW#$x2J-Diy%|=!3$MG$FI*=_cHa?NS`l*OvjOk*MBI|8Vybk zDC%%EJBA{6M5X3jb#V0UJ5;#l9gmpET$gNB*f-lO(sc&| z4065@6J_-2q@F<)#gwZ{at2lldt2X<%Y1VM)|hL*dKS^C%Jd6T;e z`Kx@mlAEr;z#kngrf!nLFRFDDzqB@h2KQvQr+hn+C3769$nRzR>NalRbw0+u@~}R= zfnVn&sT&*g?R7?l?DZ7qt#bLxQlB#SwdQNUEq(L8!?dhVY_0gwbraHo%Zu_TW7QN| z)qv@B*`6{m7v`4!vF5NLL;PqC%XMwUqMnwv6hBZXG8ER&BQ~i}8EW=fSr@bSZ)LZf zD}GmoAK1Ow&l6FR`LIEea&jgo%Y>e6WyoGV1WuDz2IszhtG==UwFR31=4t=?&h*hE zvQ63THQ)}Sb#u9vb1tlw9|p?^wJg(>kr~m0Ns)Ar{wLX2LVAZx7F0!I75|#SGvsJ4 z5mZTH6*krtgra$Q_CnQ$mI~V$)KiUQo_W^sk9rV_@<_!62Nn4-r z{GydcPnTt6d(MQ{ppoObnk)6=aiE3%s2I7b1v0;c9(M$q*hYeF3dSX$wU)6b5yCX0 z3rCeWVK0!o=p6MgrADn)ot|bKSU$gh!45CcyveB1>T!91zeKrD?EA*J*WONbRWpWj zt+2Btq_NBF;EL8}mie?a=B5mN82-?$VGAm&*0Y#Wq+0nGL1W5vm`Zj27e0rvxRRUW z{aO69C}$%1Y6v>vTQ2+bRuEmRz=VUeGd>`eh=-WrDgDfd7Xx<+l<-x4pAKf;N9%)P zlM8-%50%1;ZQ=A@%_qb(<}MnccSi>~j_iD0qn9{=NGVB=)Wl2vy2*Tm+?|rn&cmJy zra}|!qWGlxe=PKwZR&qCTL$}&f+ET(?Ty0%o*z69SEt{<<2swfJVki?rnAn-MJJa* ziOxQiq%-8+xl3->C_y;%!I27}acW87#u*zg2=sj0J^}tIbfI1zNq@9eN7KAFBVv!G z$P=0434OD8-5XXYPxDmqkgQU=e=CsA|5Bjy|4V@`pgw;UNXrsQNyNifzBlk0TWBBi zQjECsRS|~N;IyQrR-c?qx9E+Dj0eWI1!>6+)A4?|6(oP4<=DyJA!1M2mN-zseX3tO z$EIL&F)Axc=U?{ordgEgzvKs82^)yfX8*V4@b7lNab;N2z^j%+)r@*r%nt1U@b<_)Z9Xr*(lDjZeWumhg=BeP8jkrHNF*t>S@(E({&*9&D1 z;vxpl`SLxkKh4swZ@E$!j{smI7$oj-ezsUbcdRV4RFSOZMg3EC;kgYZb82YI09L}l zThf_vmwvHd*5|2E%e&3niyM7ou}HYRK)GJC?`(rQtFJR4BP~mWc{u6POX$xtit!s2 zh5Um@;j11a^Zkj|hEp@kwM383l%Wdty_`ToLa3)_D@{x}%^!Y zY8YaRc!jwEnC_(1r=*0gI zS>q67_z|`uqnIgFQC-;7UNPOiY75UV8U0QfBu}omg1MG|O#y-(U%D>z4!EE4+@3r0 z7((1n>(Bi|C{pXhUay$X5TJaazGYFfETq)e`$x^-y`s)&rV2m*d&OGrs=2cEwobvL z4B)_TBvUdx8le&kJfCib6-d1dA#*~goc|UBZf1N2>VzV`Ik~)Up%L+tLE$|nCK9$_ z5N3*Yp^^URm6t+6*E+~42>m5T{2_>2W|H$ENZ_fSfWh4F5g>n^zl4GP%yEU7pGMdw zCBE=9geE7}fB(R>SQquwZf^j59$lHoQK-;MEpO{|#_qFGs!FL~5-^}b0xRZ>TjM>s z^Plrkr0}cZnWi|4K7bJ`&w8<+7wNBWiytr8`>K&1hf!*Z2x3u=mb>e-6KVC>u`6uV zHj`X#4&GuX-p^&P^T%!;vp}R+%%{<30Bbnn{s;+D>PyFGnv7h`zEq05ADs;EQS++)-iM9Kf$Q&* zd2)Df@jwjN6MRDZJf|?O@U!~0xLo3o4y@)7wwq@cZzD&oT})o$)3D15H;ax<0go^vkiHHtOM}pg3~|MD|W|g_qNST1=v5k z$+I~+ZaZGlGt+JPnU%A+VoB=SfXTYg%P;MT)tib`eu`6oNJS0SpTHP<)_YN23Yzg{H3BWJ!uQri{O0?Tz!!SgvKSxs&F_(Js|N!-c?@L z3qDzePHp2muk2KOb?xhakdvgY@Mm@EAkjA`VTh-jY46-P!R*+Wq$FQ<@s7Tq5`;yJ zc{NuXqEU|2!H+~e8tm4mLowNOylABtJw%m`CNcdZ4D`SH2bXvsr+c>aIdlJA7?c^r zl-|vqKETT>L0{j;M@3_~L%v&dt2#zKLD^WHA$8>pblF}*E;{jAhxx!{U0Tz$U*V}2 zM$EO=9CFIqwahj!UN9FWEf+E(zZsUxI-$=RG32kDsBYmP{ z0PjisaD+t`SSdCS%x!P9HT0#$Ea~V*;^I^q7;8qxC7|6*7-q5k_ss7tL}qLVa2)w& zR*T3pS!#W;{-LsdFB*|N+q1Bw4_R6iCS{UP7{Ej~B9>#BAwHwWrq`=c?=9Iq)H)Ti zP=N+v9n+H^1=U&UT%!iV{Upk%srqp!=?tIjIvmCto$fpC6LoJLSwn7X`O zza?Mc=d-|GU1j7Knk5Db+5&AFnL3_A!|qoS5T-R3pL2zijo{UMGc?t>!m2K)U@FX$ zgdMrw3dMAoH@~-`TSZa%;_+8!m3QQBk6BjWjZ<{oYY|c6k^~DYo_HVk3ZW(n-pVPI zN@u$&E4}oZj}+^I*CDjUi~n$h{DkPgC^#O|#}t{GSYb}Ys_M#l(^OOcK47z)+x3-q zZCRqqeppS`)d@c$Wnz1d#zGjpV^{>R_+-Kb47!v ucn6U9aBb6S4p7OpsLOq-ITP(wKI3k<`9hx%e<$(?#1)fixvL>E(tiM!QSAKy From 538039fc8d19b173b1b775f2477ea51dfe94da67 Mon Sep 17 00:00:00 2001 From: "Andrew Jackson (Ajax)" Date: Tue, 5 Dec 2023 10:34:17 -0600 Subject: [PATCH 007/394] fix sender --- provider/lpmessage/sender.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/provider/lpmessage/sender.go b/provider/lpmessage/sender.go index 8d6cd4027..cb43b7401 100644 --- a/provider/lpmessage/sender.go +++ b/provider/lpmessage/sender.go @@ -76,7 +76,7 @@ func (s *SendTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (done b SignedData []byte `db:"signed_data"` } - err = s.db.QueryRow(ctx, `select from_key, nonce, to_addr, unsigned_data, unsigned_cid from message_sends where id = $1`, taskID).Scan(&dbMsg) + err = s.db.QueryRow(ctx, `select from_key, nonce, to_addr, unsigned_data, unsigned_cid from message_sends where send_task_id = $1`, taskID).Scan(&dbMsg) if err != nil { return false, xerrors.Errorf("getting message from db: %w", err) } From 2618b963e744babd4debbaae3486f7a68392e19c Mon Sep 17 00:00:00 2001 From: "Andrew Jackson (Ajax)" Date: Tue, 5 Dec 2023 10:41:32 -0600 Subject: [PATCH 008/394] getdeps --- cmd/lotus-provider/deps/deps.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cmd/lotus-provider/deps/deps.go b/cmd/lotus-provider/deps/deps.go index f82a1ecd6..54305f6a1 100644 --- a/cmd/lotus-provider/deps/deps.go +++ b/cmd/lotus-provider/deps/deps.go @@ -82,6 +82,11 @@ func StorageAuth(apiKey string) (sealer.StorageAuth, error) { return sealer.StorageAuth(headers), nil } +func GetDeps(ctx context.Context, cctx *cli.Context) (*Deps, error) { + var deps *Deps + return deps, deps.PopulateRemainingDeps(ctx, cctx, true) +} + type Deps struct { Cfg *config.LotusProviderConfig DB *harmonydb.DB From 943c223dad598a253636dcc1a2934ed315058366 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 5 Dec 2023 17:51:42 +0100 Subject: [PATCH 009/394] fix: lotus-provider: Fix winning PoSt --- provider/lpwinning/winning_task.go | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/provider/lpwinning/winning_task.go b/provider/lpwinning/winning_task.go index f02ffa1ae..907b594fd 100644 --- a/provider/lpwinning/winning_task.go +++ b/provider/lpwinning/winning_task.go @@ -4,10 +4,8 @@ import ( "bytes" "context" "crypto/rand" - "database/sql" "encoding/binary" "encoding/json" - "errors" "time" "github.com/ipfs/go-cid" @@ -579,12 +577,13 @@ func (t *WinPostTask) mineBasic(ctx context.Context) { taskFn(func(id harmonytask.TaskID, tx *harmonydb.Tx) (shouldCommit bool, seriousError error) { // First we check if the mining base includes blocks we may have mined previously to avoid getting slashed // select mining_tasks where epoch==base_epoch if win=true to maybe get base block cid which has to be included in our tipset - var baseBlockCid string - err := tx.QueryRow(`SELECT mined_cid FROM mining_tasks WHERE epoch = $1 AND sp_id = $2 AND won = true`, baseEpoch, spID).Scan(&baseBlockCid) - if err != nil && !errors.Is(err, sql.ErrNoRows) { + var baseBlockCids []string + err := tx.Select(&baseBlockCids, `SELECT mined_cid FROM mining_tasks WHERE epoch = $1 AND sp_id = $2 AND won = true`, baseEpoch, spID) + if err != nil { return false, xerrors.Errorf("querying mining_tasks: %w", err) } - if baseBlockCid != "" { + if len(baseBlockCids) >= 1 { + baseBlockCid := baseBlockCids[0] c, err := cid.Parse(baseBlockCid) if err != nil { return false, xerrors.Errorf("parsing mined_cid: %w", err) From 0490359ac6b3ab7692b50ef71f5f9cbce497c177 Mon Sep 17 00:00:00 2001 From: "Andrew Jackson (Ajax)" Date: Tue, 5 Dec 2023 13:36:47 -0600 Subject: [PATCH 010/394] in progress --- itests/kit/ensemble_presets.go | 15 +++++++++++++++ itests/kit/node_full.go | 12 ++++++++++++ 2 files changed, 27 insertions(+) diff --git a/itests/kit/ensemble_presets.go b/itests/kit/ensemble_presets.go index 3ec39cf90..88f07b684 100644 --- a/itests/kit/ensemble_presets.go +++ b/itests/kit/ensemble_presets.go @@ -101,6 +101,21 @@ func EnsembleOneTwo(t *testing.T, opts ...interface{}) (*TestFullNode, *TestMine return &full, &one, &two, ens } +// EnsembleProvider creates and starts an Ensemble with a single full node and a single provider. +// It does not interconnect nodes nor does it begin mining. +func EnsembleProvider(t *testing.T, opts ...interface{}) (*TestFullNode, *TestProviderNode, *Ensemble) { + opts = append(opts, WithAllSubsystems()) + + eopts, nopts := siftOptions(t, opts) + + var ( + full TestFullNode + provider TestProviderNode + ) + ens := NewEnsemble(t, eopts...).FullNode(&full, nopts...).Provider(&provider, &full, nopts...).Start() + return &full, &provider, ens +} + func siftOptions(t *testing.T, opts []interface{}) (eopts []EnsembleOpt, nopts []NodeOpt) { for _, v := range opts { switch o := v.(type) { diff --git a/itests/kit/node_full.go b/itests/kit/node_full.go index 3e80ed688..697c59aed 100644 --- a/itests/kit/node_full.go +++ b/itests/kit/node_full.go @@ -22,6 +22,7 @@ import ( "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/wallet/key" cliutil "github.com/filecoin-project/lotus/cli/util" + "github.com/filecoin-project/lotus/cmd/lotus-provider/deps" "github.com/filecoin-project/lotus/gateway" "github.com/filecoin-project/lotus/node" ) @@ -54,6 +55,17 @@ type TestFullNode struct { options nodeOpts } +// TestProviderNode represents a Provider node enrolled in an Ensemble. +type TestProviderNode struct { + v1api.LotusProviderStruct + + t *testing.T + + *deps.Deps + + options nodeOpts +} + func MergeFullNodes(fullNodes []*TestFullNode) *TestFullNode { var wrappedFullNode TestFullNode var fns api.FullNodeStruct From 685934a644a0f6ad16d7ed959bf2c285a1718a32 Mon Sep 17 00:00:00 2001 From: "Andrew Jackson (Ajax)" Date: Tue, 5 Dec 2023 13:45:08 -0600 Subject: [PATCH 011/394] fix: sql Scan cannot write to an object --- provider/lpmessage/sender.go | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/provider/lpmessage/sender.go b/provider/lpmessage/sender.go index cb43b7401..bee22ff69 100644 --- a/provider/lpmessage/sender.go +++ b/provider/lpmessage/sender.go @@ -76,7 +76,11 @@ func (s *SendTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (done b SignedData []byte `db:"signed_data"` } - err = s.db.QueryRow(ctx, `select from_key, nonce, to_addr, unsigned_data, unsigned_cid from message_sends where send_task_id = $1`, taskID).Scan(&dbMsg) + err = s.db.QueryRow(ctx, ` + SELECT from_key, nonce, to_addr, unsigned_data, unsigned_cid + FROM message_sends + WHERE send_task_id = $1`, taskID).Scan( + &dbMsg.FromKey, &dbMsg.Nonce, &dbMsg.ToAddr, &dbMsg.UnsignedData, &dbMsg.UnsignedCid) if err != nil { return false, xerrors.Errorf("getting message from db: %w", err) } @@ -96,8 +100,11 @@ func (s *SendTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (done b } // try to acquire lock - cn, err := s.db.Exec(ctx, `INSERT INTO message_send_locks (from_key, task_id, claimed_at) VALUES ($1, $2, CURRENT_TIMESTAMP) - ON CONFLICT (from_key) DO UPDATE SET task_id = EXCLUDED.task_id, claimed_at = CURRENT_TIMESTAMP WHERE message_send_locks.task_id = $2;`, dbMsg.FromKey, taskID) + cn, err := s.db.Exec(ctx, ` + INSERT INTO message_send_locks (from_key, task_id, claimed_at) + VALUES ($1, $2, CURRENT_TIMESTAMP) ON CONFLICT (from_key) DO UPDATE + SET task_id = EXCLUDED.task_id, claimed_at = CURRENT_TIMESTAMP + WHERE message_send_locks.task_id = $2;`, dbMsg.FromKey, taskID) if err != nil { return false, xerrors.Errorf("acquiring send lock: %w", err) } @@ -114,7 +121,8 @@ func (s *SendTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (done b // defer release db send lock defer func() { - _, err2 := s.db.Exec(ctx, `delete from message_send_locks where from_key = $1 and task_id = $2`, dbMsg.FromKey, taskID) + _, err2 := s.db.Exec(ctx, ` + DELETE from message_send_locks WHERE from_key = $1 AND task_id = $2`, dbMsg.FromKey, taskID) if err2 != nil { log.Errorw("releasing send lock", "task_id", taskID, "from", dbMsg.FromKey, "error", err2) @@ -135,7 +143,8 @@ func (s *SendTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (done b // get nonce from db var dbNonce *uint64 - r := s.db.QueryRow(ctx, `select max(nonce) from message_sends where from_key = $1 and send_success = true`, msg.From.String()) + r := s.db.QueryRow(ctx, ` + SELECT MAX(nonce) FROM message_sends WHERE from_key = $1 AND send_success = true`, msg.From.String()) if err := r.Scan(&dbNonce); err != nil { return false, xerrors.Errorf("getting nonce from db: %w", err) } @@ -164,7 +173,9 @@ func (s *SendTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (done b // write to db - n, err := s.db.Exec(ctx, `update message_sends set nonce = $1, signed_data = $2, signed_json = $3, signed_cid = $4 where send_task_id = $5`, + n, err := s.db.Exec(ctx, ` + UPDATE message_sends SET nonce = $1, signed_data = $2, signed_json = $3, signed_cid = $4 + WHERE send_task_id = $5`, msg.Nonce, data, string(jsonBytes), sigMsg.Cid().String(), taskID) if err != nil { return false, xerrors.Errorf("updating db record: %w", err) @@ -198,7 +209,9 @@ func (s *SendTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (done b sendError = err.Error() } - _, err = s.db.Exec(ctx, `update message_sends set send_success = $1, send_error = $2, send_time = CURRENT_TIMESTAMP where send_task_id = $3`, sendSuccess, sendError, taskID) + _, err = s.db.Exec(ctx, ` + UPDATE message_sends SET send_success = $1, send_error = $2, send_time = CURRENT_TIMESTAMP + WHERE send_task_id = $3`, sendSuccess, sendError, taskID) if err != nil { return false, xerrors.Errorf("updating db record: %w", err) } From 91b0032ee4f0b79c7f4e51ed568239b5f15b14a8 Mon Sep 17 00:00:00 2001 From: "Andrew Jackson (Ajax)" Date: Tue, 5 Dec 2023 13:58:15 -0600 Subject: [PATCH 012/394] do not suggest the default layer --- cmd/lotus-provider/migrate.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/cmd/lotus-provider/migrate.go b/cmd/lotus-provider/migrate.go index 819499402..3869c7dfb 100644 --- a/cmd/lotus-provider/migrate.go +++ b/cmd/lotus-provider/migrate.go @@ -231,12 +231,17 @@ func fromMiner(cctx *cli.Context) (err error) { dbSettings += ` --db-name="` + smCfg.HarmonyDB.Database + `"` } + var layerMaybe string + if name != "base" { + layerMaybe = "--layer=" + name + } + msg += ` To work with the config: ` + cliCommandColor(`lotus-provider `+dbSettings+` config help `) msg += ` To run Lotus Provider: in its own machine or cgroup without other files, use the command: -` + cliCommandColor(`lotus-provider `+dbSettings+` run --layers="`+name+`"`) +` + cliCommandColor(`lotus-provider `+dbSettings+` run `+layerMaybe) fmt.Println(msg) return nil } From e570693b3ef93c78c031e539d86bcf33ad0b982e Mon Sep 17 00:00:00 2001 From: "Andrew Jackson (Ajax)" Date: Tue, 5 Dec 2023 17:38:07 -0600 Subject: [PATCH 013/394] lp docsgen --- Makefile | 4 +++- api/docgen/docgen.go | 4 ++++ documentation/en/api-v0-methods-provider.md | 25 +++++++++++++++++++++ 3 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 documentation/en/api-v0-methods-provider.md diff --git a/Makefile b/Makefile index 68d97227b..c3c46f71e 100644 --- a/Makefile +++ b/Makefile @@ -356,7 +356,7 @@ docsgen-md-bin: api-gen actors-gen docsgen-openrpc-bin: api-gen actors-gen $(GOCC) build $(GOFLAGS) -o docgen-openrpc ./api/docgen-openrpc/cmd -docsgen-md: docsgen-md-full docsgen-md-storage docsgen-md-worker +docsgen-md: docsgen-md-full docsgen-md-storage docsgen-md-worker docsgen-md-provider docsgen-md-full: docsgen-md-bin ./docgen-md "api/api_full.go" "FullNode" "api" "./api" > documentation/en/api-v1-unstable-methods.md @@ -365,6 +365,8 @@ docsgen-md-storage: docsgen-md-bin ./docgen-md "api/api_storage.go" "StorageMiner" "api" "./api" > documentation/en/api-v0-methods-miner.md docsgen-md-worker: docsgen-md-bin ./docgen-md "api/api_worker.go" "Worker" "api" "./api" > documentation/en/api-v0-methods-worker.md +docsgen-md-provider: docsgen-md-bin + ./docgen-md "api/api_lp.go" "Provider" "api" "./api" > documentation/en/api-v0-methods-provider.md docsgen-openrpc: docsgen-openrpc-full docsgen-openrpc-storage docsgen-openrpc-worker docsgen-openrpc-gateway diff --git a/api/docgen/docgen.go b/api/docgen/docgen.go index 018629600..5a05c8d0e 100644 --- a/api/docgen/docgen.go +++ b/api/docgen/docgen.go @@ -432,6 +432,10 @@ func GetAPIType(name, pkg string) (i interface{}, t reflect.Type, permStruct []r i = &api.GatewayStruct{} t = reflect.TypeOf(new(struct{ api.Gateway })).Elem() permStruct = append(permStruct, reflect.TypeOf(api.GatewayStruct{}.Internal)) + case "Provider": + i = &api.LotusProviderStruct{} + t = reflect.TypeOf(new(struct{ api.LotusProvider })).Elem() + permStruct = append(permStruct, reflect.TypeOf(api.LotusProviderStruct{}.Internal)) default: panic("unknown type") } diff --git a/documentation/en/api-v0-methods-provider.md b/documentation/en/api-v0-methods-provider.md new file mode 100644 index 000000000..fc4a2daf7 --- /dev/null +++ b/documentation/en/api-v0-methods-provider.md @@ -0,0 +1,25 @@ +# Groups +* [](#) + * [Shutdown](#Shutdown) + * [Version](#Version) +## + + +### Shutdown + + +Perms: admin + +Inputs: `null` + +Response: `{}` + +### Version + + +Perms: admin + +Inputs: `null` + +Response: `131840` + From 3d30dedc39867adc3bfa01e7c95ff07d38c29e8b Mon Sep 17 00:00:00 2001 From: Phi Date: Wed, 6 Dec 2023 12:52:26 +0100 Subject: [PATCH 014/394] Actually show miner-addrs in info-log Actually show miner-addrs in lotus-provider info-log --- cmd/lotus-provider/run.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/cmd/lotus-provider/run.go b/cmd/lotus-provider/run.go index bf19ee537..de97aa766 100644 --- a/cmd/lotus-provider/run.go +++ b/cmd/lotus-provider/run.go @@ -176,7 +176,7 @@ var runCmd = &cli.Command{ } } log.Infow("This lotus_provider instance handles", - "miner_addresses", maddrs, + "miner_addresses", minerAddressesToStrings(maddrs), "tasks", lo.Map(activeTasks, func(t harmonytask.TaskInterface, _ int) string { return t.TypeDetails().Name })) taskEngine, err := harmonytask.New(db, activeTasks, deps.listenAddr) @@ -457,3 +457,11 @@ func (p *ProviderAPI) Shutdown(context.Context) error { close(p.ShutdownChan) return nil } + +func minerAddressesToStrings(maddrs []dtypes.MinerAddress) []string { + strs := make([]string, len(maddrs)) + for i, addr := range maddrs { + strs[i] = address.Address(addr).String() + } + return strs +} From 0ceef47a459c82dafc40ac7275105682f50e1845 Mon Sep 17 00:00:00 2001 From: GlacierWalrus <114941647+GlacierWalrus@users.noreply.github.com> Date: Wed, 6 Dec 2023 12:47:08 +0000 Subject: [PATCH 015/394] Remove PL operated bootstrap nodes from mainnet.pi --- build/bootstrap/mainnet.pi | 6 ------ 1 file changed, 6 deletions(-) diff --git a/build/bootstrap/mainnet.pi b/build/bootstrap/mainnet.pi index 7838158de..0c72f12a4 100644 --- a/build/bootstrap/mainnet.pi +++ b/build/bootstrap/mainnet.pi @@ -1,9 +1,3 @@ -/dns4/bootstrap-0.mainnet.filops.net/tcp/1347/p2p/12D3KooWCVe8MmsEMes2FzgTpt9fXtmCY7wrq91GRiaC8PHSCCBj -/dns4/bootstrap-1.mainnet.filops.net/tcp/1347/p2p/12D3KooWCwevHg1yLCvktf2nvLu7L9894mcrJR4MsBCcm4syShVc -/dns4/bootstrap-2.mainnet.filops.net/tcp/1347/p2p/12D3KooWEWVwHGn2yR36gKLozmb4YjDJGerotAPGxmdWZx2nxMC4 -/dns4/bootstrap-6.mainnet.filops.net/tcp/1347/p2p/12D3KooWP5MwCiqdMETF9ub1P3MbCvQCcfconnYHbWg6sUJcDRQQ -/dns4/bootstrap-7.mainnet.filops.net/tcp/1347/p2p/12D3KooWRs3aY1p3juFjPy8gPN95PEQChm2QKGUCAdcDCC4EBMKf -/dns4/bootstrap-8.mainnet.filops.net/tcp/1347/p2p/12D3KooWScFR7385LTyR4zU1bYdzSiiAb5rnNABfVahPvVSzyTkR /dns4/lotus-bootstrap.ipfsforce.com/tcp/41778/p2p/12D3KooWGhufNmZHF3sv48aQeS13ng5XVJZ9E6qy2Ms4VzqeUsHk /dns4/bootstrap-0.starpool.in/tcp/12757/p2p/12D3KooWGHpBMeZbestVEWkfdnC9u7p6uFHXL1n7m1ZBqsEmiUzz /dns4/bootstrap-1.starpool.in/tcp/12757/p2p/12D3KooWQZrGH1PxSNZPum99M1zNvjNFM33d1AAu5DcvdHptuU7u From 4b1445e3b2ad863a5f10342a7c55c611ee258c24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 6 Dec 2023 20:53:50 +0100 Subject: [PATCH 016/394] fix: lotus-provider: Wait for the correct taskID --- provider/lpmessage/sender.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/provider/lpmessage/sender.go b/provider/lpmessage/sender.go index bee22ff69..5d03cfd02 100644 --- a/provider/lpmessage/sender.go +++ b/provider/lpmessage/sender.go @@ -324,6 +324,7 @@ func (s *Sender) Send(ctx context.Context, msg *types.Message, mss *api.MessageS return cid.Undef, xerrors.Errorf("marshaling message: %w", err) } + var sendTaskID *harmonytask.TaskID taskAdder(func(id harmonytask.TaskID, tx *harmonydb.Tx) (shouldCommit bool, seriousError error) { _, err := tx.Exec(`insert into message_sends (from_key, to_addr, send_reason, unsigned_data, unsigned_cid, send_task_id) values ($1, $2, $3, $4, $5, $6)`, msg.From.String(), msg.To.String(), reason, unsBytes.Bytes(), msg.Cid().String(), id) @@ -331,9 +332,15 @@ func (s *Sender) Send(ctx context.Context, msg *types.Message, mss *api.MessageS return false, xerrors.Errorf("inserting message into db: %w", err) } + sendTaskID = &id + return true, nil }) + if sendTaskID == nil { + return cid.Undef, xerrors.Errorf("failed to add task") + } + // wait for exec var ( pollInterval = 50 * time.Millisecond @@ -350,7 +357,7 @@ func (s *Sender) Send(ctx context.Context, msg *types.Message, mss *api.MessageS var sigCidStr, sendError string var sendSuccess *bool - err = s.db.QueryRow(ctx, `select signed_cid, send_success, send_error from message_sends where send_task_id = $1`, taskAdder).Scan(&sigCidStr, &sendSuccess, &sendError) + err = s.db.QueryRow(ctx, `select signed_cid, send_success, send_error from message_sends where send_task_id = $1`, &sendTaskID).Scan(&sigCidStr, &sendSuccess, &sendError) if err != nil { return cid.Undef, xerrors.Errorf("getting cid for task: %w", err) } From 93fd40826740b7caa122144f6d0e535345c8e2e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 6 Dec 2023 22:54:46 +0100 Subject: [PATCH 017/394] lotus-provider: additional fixes to make recover work --- lib/harmony/harmonytask/harmonytask.go | 5 +++++ provider/lpmessage/sender.go | 7 ++++++- provider/lpwindow/recover_task.go | 2 +- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/lib/harmony/harmonytask/harmonytask.go b/lib/harmony/harmonytask/harmonytask.go index 595e5b63a..31b632975 100644 --- a/lib/harmony/harmonytask/harmonytask.go +++ b/lib/harmony/harmonytask/harmonytask.go @@ -146,6 +146,11 @@ func New( TaskTypeDetails: c.TypeDetails(), TaskEngine: e, } + + if len(h.Name) > 16 { + return nil, fmt.Errorf("task name too long: %s, max 16 characters", h.Name) + } + e.handlers = append(e.handlers, &h) e.taskMap[h.TaskTypeDetails.Name] = &h } diff --git a/provider/lpmessage/sender.go b/provider/lpmessage/sender.go index 5d03cfd02..5467e5d2f 100644 --- a/provider/lpmessage/sender.go +++ b/provider/lpmessage/sender.go @@ -354,7 +354,7 @@ func (s *Sender) Send(ctx context.Context, msg *types.Message, mss *api.MessageS for { var err error - var sigCidStr, sendError string + var sigCidStr, sendError *string var sendSuccess *bool err = s.db.QueryRow(ctx, `select signed_cid, send_success, send_error from message_sends where send_task_id = $1`, &sendTaskID).Scan(&sigCidStr, &sendSuccess, &sendError) @@ -373,6 +373,11 @@ func (s *Sender) Send(ctx context.Context, msg *types.Message, mss *api.MessageS continue } + if sigCidStr == nil || sendError == nil { + // should never happen because sendSuccess is already not null here + return cid.Undef, xerrors.Errorf("got null values for sigCidStr or sendError, this should never happen") + } + if !*sendSuccess { sendErr = xerrors.Errorf("send error: %s", sendError) } else { diff --git a/provider/lpwindow/recover_task.go b/provider/lpwindow/recover_task.go index 6006f3c35..12f8522b5 100644 --- a/provider/lpwindow/recover_task.go +++ b/provider/lpwindow/recover_task.go @@ -217,7 +217,7 @@ func (w *WdPostRecoverDeclareTask) CanAccept(ids []harmonytask.TaskID, engine *h func (w *WdPostRecoverDeclareTask) TypeDetails() harmonytask.TaskTypeDetails { return harmonytask.TaskTypeDetails{ Max: 128, - Name: "WdPostRecoverDeclare", + Name: "WdPostRecover", Cost: resources.Resources{ Cpu: 1, Gpu: 0, From bf5132e50bedd405948c85d51365d29c0956c9c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 6 Dec 2023 23:00:53 +0100 Subject: [PATCH 018/394] more sender fixes --- provider/lpmessage/sender.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/provider/lpmessage/sender.go b/provider/lpmessage/sender.go index 5467e5d2f..0db0c0b51 100644 --- a/provider/lpmessage/sender.go +++ b/provider/lpmessage/sender.go @@ -379,9 +379,9 @@ func (s *Sender) Send(ctx context.Context, msg *types.Message, mss *api.MessageS } if !*sendSuccess { - sendErr = xerrors.Errorf("send error: %s", sendError) + sendErr = xerrors.Errorf("send error: %s", *sendError) } else { - sigCid, err = cid.Parse(sigCidStr) + sigCid, err = cid.Parse(*sigCidStr) if err != nil { return cid.Undef, xerrors.Errorf("parsing signed cid: %w", err) } From efb4a09cf8330acd64b38714c6800bd85ca21a1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 7 Dec 2023 12:17:53 +0100 Subject: [PATCH 019/394] harmony: Fix task reclaim on restart --- lib/harmony/harmonytask/harmonytask.go | 4 +-- lib/harmony/harmonytask/task_type_handler.go | 38 ++++++++++++-------- 2 files changed, 25 insertions(+), 17 deletions(-) diff --git a/lib/harmony/harmonytask/harmonytask.go b/lib/harmony/harmonytask/harmonytask.go index 31b632975..7577c5cf5 100644 --- a/lib/harmony/harmonytask/harmonytask.go +++ b/lib/harmony/harmonytask/harmonytask.go @@ -176,7 +176,7 @@ func New( continue // not really fatal, but not great } } - if !h.considerWork("recovered", []TaskID{TaskID(w.ID)}) { + if !h.considerWork(workSourceRecover, []TaskID{TaskID(w.ID)}) { log.Error("Strange: Unable to accept previously owned task: ", w.ID, w.Name) } } @@ -285,7 +285,7 @@ func (e *TaskEngine) pollerTryAllWork() { continue } if len(unownedTasks) > 0 { - accepted := v.considerWork("poller", unownedTasks) + accepted := v.considerWork(workSourcePoller, unownedTasks) if accepted { return // accept new work slowly and in priority order } diff --git a/lib/harmony/harmonytask/task_type_handler.go b/lib/harmony/harmonytask/task_type_handler.go index 79a156fef..34f7a5c3e 100644 --- a/lib/harmony/harmonytask/task_type_handler.go +++ b/lib/harmony/harmonytask/task_type_handler.go @@ -49,6 +49,11 @@ func (h *taskTypeHandler) AddTask(extra func(TaskID, *harmonydb.Tx) (bool, error } } +const ( + workSourcePoller = "poller" + workSourceRecover = "recovered" +) + // considerWork is called to attempt to start work on a task-id of this task type. // It presumes single-threaded calling, so there should not be a multi-threaded re-entry. // The only caller should be the one work poller thread. This does spin off other threads, @@ -87,22 +92,25 @@ top: return false } - // 4. Can we claim the work for our hostname? - ct, err := h.TaskEngine.db.Exec(h.TaskEngine.ctx, "UPDATE harmony_task SET owner_id=$1 WHERE id=$2 AND owner_id IS NULL", h.TaskEngine.ownerID, *tID) - if err != nil { - log.Error(err) - return false - } - if ct == 0 { - log.Infow("did not accept task", "task_id", strconv.Itoa(int(*tID)), "reason", "already Taken", "name", h.Name) - var tryAgain = make([]TaskID, 0, len(ids)-1) - for _, id := range ids { - if id != *tID { - tryAgain = append(tryAgain, id) - } + // if recovering we don't need to try to claim anything because those tasks are already claimed by us + if from != workSourceRecover { + // 4. Can we claim the work for our hostname? + ct, err := h.TaskEngine.db.Exec(h.TaskEngine.ctx, "UPDATE harmony_task SET owner_id=$1 WHERE id=$2 AND owner_id IS NULL", h.TaskEngine.ownerID, *tID) + if err != nil { + log.Error(err) + return false + } + if ct == 0 { + log.Infow("did not accept task", "task_id", strconv.Itoa(int(*tID)), "reason", "already Taken", "name", h.Name) + var tryAgain = make([]TaskID, 0, len(ids)-1) + for _, id := range ids { + if id != *tID { + tryAgain = append(tryAgain, id) + } + } + ids = tryAgain + goto top } - ids = tryAgain - goto top } h.Count.Add(1) From 0e49673c4991a0a2ad74d2f8d19a4b154002625f Mon Sep 17 00:00:00 2001 From: "Andrew Jackson (Ajax)" Date: Thu, 7 Dec 2023 15:32:35 -0600 Subject: [PATCH 020/394] fix: db serialize txn - retry --- cmd/lotus-provider/proving.go | 4 ++ lib/harmony/harmonydb/userfuncs.go | 45 +++++++++++++++++++- lib/harmony/harmonytask/task_type_handler.go | 9 +++- storage/paths/db_index.go | 35 +++++++++------ 4 files changed, 79 insertions(+), 14 deletions(-) diff --git a/cmd/lotus-provider/proving.go b/cmd/lotus-provider/proving.go index 577b5b5f9..621896c91 100644 --- a/cmd/lotus-provider/proving.go +++ b/cmd/lotus-provider/proving.go @@ -82,6 +82,7 @@ var wdPostTaskCmd = &cli.Command{ return xerrors.Errorf("cannot get miner id %w", err) } var id int64 + retryAddTask: _, err = deps.db.BeginTransaction(ctx, func(tx *harmonydb.Tx) (commit bool, err error) { err = tx.QueryRow(`INSERT INTO harmony_task (name, posted_time, added_by) VALUES ('WdPost', CURRENT_TIMESTAMP, 123) RETURNING id`).Scan(&id) if err != nil { @@ -102,6 +103,9 @@ var wdPostTaskCmd = &cli.Command{ return true, nil }) if err != nil { + if harmonydb.IsErrSerialization(err) { + goto retryAddTask + } return xerrors.Errorf("writing SQL transaction: %w", err) } fmt.Printf("Inserted task %v. Waiting for success ", id) diff --git a/lib/harmony/harmonydb/userfuncs.go b/lib/harmony/harmonydb/userfuncs.go index 788ca4a34..add50c269 100644 --- a/lib/harmony/harmonydb/userfuncs.go +++ b/lib/harmony/harmonydb/userfuncs.go @@ -3,6 +3,7 @@ package harmonydb import ( "context" "errors" + "runtime" "github.com/georgysavva/scany/v2/pgxscan" "github.com/jackc/pgerrcode" @@ -10,6 +11,8 @@ import ( "github.com/jackc/pgx/v5/pgconn" ) +var inTxErr = errors.New("Cannot use a non-transaction func in a transaction") + // rawStringOnly is _intentionally_private_ to force only basic strings in SQL queries. // In any package, raw strings will satisfy compilation. Ex: // @@ -22,6 +25,9 @@ type rawStringOnly string // Note, for CREATE & DROP please keep these permanent and express // them in the ./sql/ files (next number). func (db *DB) Exec(ctx context.Context, sql rawStringOnly, arguments ...any) (count int, err error) { + if usedInTransaction() { + return 0, inTxErr + } res, err := db.pgx.Exec(ctx, string(sql), arguments...) return int(res.RowsAffected()), err } @@ -55,6 +61,9 @@ type Query struct { // fmt.Println(id, name) // } func (db *DB) Query(ctx context.Context, sql rawStringOnly, arguments ...any) (*Query, error) { + if usedInTransaction() { + return &Query{}, inTxErr + } q, err := db.pgx.Query(ctx, string(sql), arguments...) return &Query{q}, err } @@ -66,6 +75,9 @@ type Row interface { Scan(...any) error } +type rowErr struct{} +func (rowErr) Scan(..any) error { return inTxErr } + // QueryRow gets 1 row using column order matching. // This is a timesaver for the special case of wanting the first row returned only. // EX: @@ -74,7 +86,10 @@ type Row interface { // var ID = 123 // err := db.QueryRow(ctx, "SELECT name, pet FROM users WHERE ID=?", ID).Scan(&name, &pet) func (db *DB) QueryRow(ctx context.Context, sql rawStringOnly, arguments ...any) Row { - return db.pgx.QueryRow(ctx, string(sql), arguments...) + if usedInTransaction() { + return rowErr{} + } + return db.pgx.QueryRow(ctx, string(sql), arguments...) } /* @@ -92,6 +107,9 @@ Ex: err := db.Select(ctx, &users, "SELECT name, id, tel_no FROM customers WHERE pet=?", pet) */ func (db *DB) Select(ctx context.Context, sliceOfStructPtr any, sql rawStringOnly, arguments ...any) error { + if usedInTransaction() { + return inTxErr + } return pgxscan.Select(ctx, db.pgx, sliceOfStructPtr, string(sql), arguments...) } @@ -100,10 +118,30 @@ type Tx struct { ctx context.Context } +// usedInTransaction is a helper to prevent nesting transactions +// & non-transaction calls in transactions. In the case of a stack read error, +// it will return false, so only use true for a course of action. +func usedInTransaction() bool { + ok := true + fn := "" + for v:=2; ok; v++ { + _,_,fn,ok = runtime.Caller(v) + if strings.Contains(fn, "BeginTransaction") { + return true + } + } + return false +} + // BeginTransaction is how you can access transactions using this library. // The entire transaction happens in the function passed in. // The return must be true or a rollback will occur. +// Be sure to test the error for IsErrSerialization() if you want to retry +// when there is a DB serialization error. func (db *DB) BeginTransaction(ctx context.Context, f func(*Tx) (commit bool, err error)) (didCommit bool, retErr error) { + if usedInTransaction() { + return 0, inTxErr + } tx, err := db.pgx.BeginTx(ctx, pgx.TxOptions{}) if err != nil { return false, err @@ -156,3 +194,8 @@ func IsErrUniqueContraint(err error) bool { var e2 *pgconn.PgError return errors.As(err, &e2) && e2.Code == pgerrcode.UniqueViolation } + +func IsErrSerialization(err error) bool { + var e2 *pgconn.PgError + return errors.As(err, &e2) && e2.Code == pgerrcode.SerializationFailure +} diff --git a/lib/harmony/harmonytask/task_type_handler.go b/lib/harmony/harmonytask/task_type_handler.go index 34f7a5c3e..6091cb61e 100644 --- a/lib/harmony/harmonytask/task_type_handler.go +++ b/lib/harmony/harmonytask/task_type_handler.go @@ -25,6 +25,7 @@ type taskTypeHandler struct { func (h *taskTypeHandler) AddTask(extra func(TaskID, *harmonydb.Tx) (bool, error)) { var tID TaskID +retryAddTask: _, err := h.TaskEngine.db.BeginTransaction(h.TaskEngine.ctx, func(tx *harmonydb.Tx) (bool, error) { // create taskID (from DB) _, err := tx.Exec(`INSERT INTO harmony_task (name, added_by, posted_time) @@ -44,6 +45,9 @@ func (h *taskTypeHandler) AddTask(extra func(TaskID, *harmonydb.Tx) (bool, error log.Debugf("addtask(%s) saw unique constraint, so it's added already.", h.Name) return } + if harmonydb.IsErrSerialization(err) { + goto retryAddTask + } log.Error("Could not add task. AddTasFunc failed: %v", err) return } @@ -161,7 +165,7 @@ top: func (h *taskTypeHandler) recordCompletion(tID TaskID, workStart time.Time, done bool, doErr error) { workEnd := time.Now() - +retryRecordCompletion: cm, err := h.TaskEngine.db.BeginTransaction(h.TaskEngine.ctx, func(tx *harmonydb.Tx) (bool, error) { var postedTime time.Time err := tx.QueryRow(`SELECT posted_time FROM harmony_task WHERE id=$1`, tID).Scan(&postedTime) @@ -214,6 +218,9 @@ VALUES ($1, $2, $3, $4, $5, $6, $7, $8)`, tID, h.Name, postedTime, workStart, wo return true, nil }) if err != nil { + if harmonydb.IsErrSerialization(err) { + goto retryRecordCompletion + } log.Error("Could not record transaction: ", err) return } diff --git a/storage/paths/db_index.go b/storage/paths/db_index.go index 1e4abfab1..69a03198d 100644 --- a/storage/paths/db_index.go +++ b/storage/paths/db_index.go @@ -180,12 +180,12 @@ func (dbi *DBIndex) StorageAttach(ctx context.Context, si storiface.StorageInfo, } } +retryAttachStorage: // Single transaction to attach storage which is not present in the DB _, err := dbi.harmonyDB.BeginTransaction(ctx, func(tx *harmonydb.Tx) (commit bool, err error) { - var urls sql.NullString var storageId sql.NullString - err = dbi.harmonyDB.QueryRow(ctx, + err = tx.QueryRow( "Select storage_id, urls FROM storage_path WHERE storage_id = $1", string(si.ID)).Scan(&storageId, &urls) if err != nil && !strings.Contains(err.Error(), "no rows in result set") { return false, xerrors.Errorf("storage attach select fails: %v", err) @@ -200,7 +200,7 @@ func (dbi *DBIndex) StorageAttach(ctx context.Context, si storiface.StorageInfo, } currUrls = union(currUrls, si.URLs) - _, err = dbi.harmonyDB.Exec(ctx, + _, err = tx.Exec( "UPDATE storage_path set urls=$1, weight=$2, max_storage=$3, can_seal=$4, can_store=$5, groups=$6, allow_to=$7, allow_types=$8, deny_types=$9 WHERE storage_id=$10", strings.Join(currUrls, ","), si.Weight, @@ -220,7 +220,7 @@ func (dbi *DBIndex) StorageAttach(ctx context.Context, si storiface.StorageInfo, } // Insert storage id - _, err = dbi.harmonyDB.Exec(ctx, + _, err = tx.Exec( "INSERT INTO storage_path "+ "Values($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16)", si.ID, @@ -245,6 +245,9 @@ func (dbi *DBIndex) StorageAttach(ctx context.Context, si storiface.StorageInfo, return true, nil }) if err != nil { + if harmonydb.IsErrSerialization(err) { + goto retryAttachStorage + } return err } @@ -284,22 +287,26 @@ func (dbi *DBIndex) StorageDetach(ctx context.Context, id storiface.ID, url stri log.Warnw("Dropping sector path endpoint", "path", id, "url", url) } else { + retryDropPath: // Single transaction to drop storage path and sector decls which have this as a storage path _, err := dbi.harmonyDB.BeginTransaction(ctx, func(tx *harmonydb.Tx) (commit bool, err error) { // Drop storage path completely - _, err = dbi.harmonyDB.Exec(ctx, "DELETE FROM storage_path WHERE storage_id=$1", id) + _, err = tx.Exec("DELETE FROM storage_path WHERE storage_id=$1", id) if err != nil { return false, err } // Drop all sectors entries which use this storage path - _, err = dbi.harmonyDB.Exec(ctx, "DELETE FROM sector_location WHERE storage_id=$1", id) + _, err = tx.Exec("DELETE FROM sector_location WHERE storage_id=$1", id) if err != nil { return false, err } return true, nil }) if err != nil { + if harmonydb.IsErrSerialization(err) { + goto retryDropPath + } return err } log.Warnw("Dropping sector storage", "path", id) @@ -373,9 +380,10 @@ func (dbi *DBIndex) StorageDeclareSector(ctx context.Context, storageID storifac return xerrors.Errorf("invalid filetype") } +retryStorageDeclareSector: _, err := dbi.harmonyDB.BeginTransaction(ctx, func(tx *harmonydb.Tx) (commit bool, err error) { var currPrimary sql.NullBool - err = dbi.harmonyDB.QueryRow(ctx, + err = tx.QueryRow( "SELECT is_primary FROM sector_location WHERE miner_id=$1 and sector_num=$2 and sector_filetype=$3 and storage_id=$4", uint64(s.Miner), uint64(s.Number), int(ft), string(storageID)).Scan(&currPrimary) if err != nil && !strings.Contains(err.Error(), "no rows in result set") { @@ -385,7 +393,7 @@ func (dbi *DBIndex) StorageDeclareSector(ctx context.Context, storageID storifac // If storage id already exists for this sector, update primary if need be if currPrimary.Valid { if !currPrimary.Bool && primary { - _, err = dbi.harmonyDB.Exec(ctx, + _, err = tx.Exec( "UPDATE sector_location set is_primary = TRUE WHERE miner_id=$1 and sector_num=$2 and sector_filetype=$3 and storage_id=$4", s.Miner, s.Number, ft, storageID) if err != nil { @@ -395,7 +403,7 @@ func (dbi *DBIndex) StorageDeclareSector(ctx context.Context, storageID storifac log.Warnf("sector %v redeclared in %s", s, storageID) } } else { - _, err = dbi.harmonyDB.Exec(ctx, + _, err = tx.Exec( "INSERT INTO sector_location "+ "values($1, $2, $3, $4, $5)", s.Miner, s.Number, ft, storageID, primary) @@ -407,6 +415,9 @@ func (dbi *DBIndex) StorageDeclareSector(ctx context.Context, storageID storifac return true, nil }) if err != nil { + if harmonydb.IsErrSerialization(err) { + goto retryStorageDeclareSector + } return err } @@ -750,7 +761,7 @@ func (dbi *DBIndex) lock(ctx context.Context, sector abi.SectorID, read storifac _, err := dbi.harmonyDB.BeginTransaction(ctx, func(tx *harmonydb.Tx) (commit bool, err error) { fts := (read | write).AllSet() - err = dbi.harmonyDB.Select(ctx, &rows, + err = tx.Select(&rows, `SELECT sector_filetype, read_ts, read_refs, write_ts FROM sector_location WHERE miner_id=$1 @@ -792,7 +803,7 @@ func (dbi *DBIndex) lock(ctx context.Context, sector abi.SectorID, read storifac } // Acquire write locks - _, err = dbi.harmonyDB.Exec(ctx, + _, err = tx.Exec( `UPDATE sector_location SET write_ts = NOW(), write_lock_owner = $1 WHERE miner_id=$2 @@ -807,7 +818,7 @@ func (dbi *DBIndex) lock(ctx context.Context, sector abi.SectorID, read storifac } // Acquire read locks - _, err = dbi.harmonyDB.Exec(ctx, + _, err = tx.Exec( `UPDATE sector_location SET read_ts = NOW(), read_refs = read_refs + 1 WHERE miner_id=$1 From 1e09e1e9661bd67d46f62b050fd1afd622d97223 Mon Sep 17 00:00:00 2001 From: "Andrew Jackson (Ajax)" Date: Thu, 7 Dec 2023 16:01:28 -0600 Subject: [PATCH 021/394] detect unsafe code uses --- lib/harmony/harmonydb/harmonydb.go | 3 ++ lib/harmony/harmonydb/userfuncs.go | 48 ++++++++++++++++-------------- 2 files changed, 29 insertions(+), 22 deletions(-) diff --git a/lib/harmony/harmonydb/harmonydb.go b/lib/harmony/harmonydb/harmonydb.go index 0fed176d2..caa5d1daa 100644 --- a/lib/harmony/harmonydb/harmonydb.go +++ b/lib/harmony/harmonydb/harmonydb.go @@ -10,6 +10,7 @@ import ( "sort" "strconv" "strings" + "sync" "time" logging "github.com/ipfs/go-log/v2" @@ -33,6 +34,8 @@ type DB struct { cfg *pgxpool.Config schema string hostnames []string + BTFPOnce sync.Once + BTFP uintptr } var logger = logging.Logger("harmonydb") diff --git a/lib/harmony/harmonydb/userfuncs.go b/lib/harmony/harmonydb/userfuncs.go index add50c269..8abce73d0 100644 --- a/lib/harmony/harmonydb/userfuncs.go +++ b/lib/harmony/harmonydb/userfuncs.go @@ -9,6 +9,7 @@ import ( "github.com/jackc/pgerrcode" "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgconn" + "github.com/samber/lo" ) var inTxErr = errors.New("Cannot use a non-transaction func in a transaction") @@ -25,7 +26,7 @@ type rawStringOnly string // Note, for CREATE & DROP please keep these permanent and express // them in the ./sql/ files (next number). func (db *DB) Exec(ctx context.Context, sql rawStringOnly, arguments ...any) (count int, err error) { - if usedInTransaction() { + if db.usedInTransaction() { return 0, inTxErr } res, err := db.pgx.Exec(ctx, string(sql), arguments...) @@ -61,7 +62,7 @@ type Query struct { // fmt.Println(id, name) // } func (db *DB) Query(ctx context.Context, sql rawStringOnly, arguments ...any) (*Query, error) { - if usedInTransaction() { + if db.usedInTransaction() { return &Query{}, inTxErr } q, err := db.pgx.Query(ctx, string(sql), arguments...) @@ -76,7 +77,8 @@ type Row interface { } type rowErr struct{} -func (rowErr) Scan(..any) error { return inTxErr } + +func (rowErr) Scan(_ ...any) error { return inTxErr } // QueryRow gets 1 row using column order matching. // This is a timesaver for the special case of wanting the first row returned only. @@ -86,10 +88,10 @@ func (rowErr) Scan(..any) error { return inTxErr } // var ID = 123 // err := db.QueryRow(ctx, "SELECT name, pet FROM users WHERE ID=?", ID).Scan(&name, &pet) func (db *DB) QueryRow(ctx context.Context, sql rawStringOnly, arguments ...any) Row { - if usedInTransaction() { + if db.usedInTransaction() { return rowErr{} } - return db.pgx.QueryRow(ctx, string(sql), arguments...) + return db.pgx.QueryRow(ctx, string(sql), arguments...) } /* @@ -107,7 +109,7 @@ Ex: err := db.Select(ctx, &users, "SELECT name, id, tel_no FROM customers WHERE pet=?", pet) */ func (db *DB) Select(ctx context.Context, sliceOfStructPtr any, sql rawStringOnly, arguments ...any) error { - if usedInTransaction() { + if db.usedInTransaction() { return inTxErr } return pgxscan.Select(ctx, db.pgx, sliceOfStructPtr, string(sql), arguments...) @@ -118,29 +120,31 @@ type Tx struct { ctx context.Context } -// usedInTransaction is a helper to prevent nesting transactions -// & non-transaction calls in transactions. In the case of a stack read error, -// it will return false, so only use true for a course of action. -func usedInTransaction() bool { - ok := true - fn := "" - for v:=2; ok; v++ { - _,_,fn,ok = runtime.Caller(v) - if strings.Contains(fn, "BeginTransaction") { - return true - } - } - return false +// usedInTransaction is a helper to prevent nesting transactions +// & non-transaction calls in transactions. It only checks 20 frames. +// Fast: This memory should all be in CPU Caches. +func (db *DB) usedInTransaction() bool { + var framePtrs = (&[20]uintptr{})[:] + runtime.Callers(3, framePtrs) + return lo.Contains(framePtrs, db.BTFP) } // BeginTransaction is how you can access transactions using this library. // The entire transaction happens in the function passed in. // The return must be true or a rollback will occur. // Be sure to test the error for IsErrSerialization() if you want to retry -// when there is a DB serialization error. +// +// when there is a DB serialization error. +// +//go:noinline func (db *DB) BeginTransaction(ctx context.Context, f func(*Tx) (commit bool, err error)) (didCommit bool, retErr error) { - if usedInTransaction() { - return 0, inTxErr + db.BTFPOnce.Do(func() { + fp := make([]uintptr, 20) + runtime.Callers(1, fp) + db.BTFP = fp[0] + }) + if db.usedInTransaction() { + return false, inTxErr } tx, err := db.pgx.BeginTx(ctx, pgx.TxOptions{}) if err != nil { From 4ab92cae70784e1bf139e2e2c2c39a635be60a77 Mon Sep 17 00:00:00 2001 From: Phi Date: Fri, 8 Dec 2023 09:21:07 +0100 Subject: [PATCH 022/394] Fix log output format in wdPostTaskCmd Fix log output format in wdPostTaskCmd --- cmd/lotus-provider/proving.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/lotus-provider/proving.go b/cmd/lotus-provider/proving.go index 577b5b5f9..a3211b176 100644 --- a/cmd/lotus-provider/proving.go +++ b/cmd/lotus-provider/proving.go @@ -117,7 +117,7 @@ var wdPostTaskCmd = &cli.Command{ } fmt.Print(".") } - log.Infof("Result:", result.String) + log.Infof("Result: %s", result.String) return nil }, } From 986f240d914915cd8390ae810890f4ce22c0758b Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Sat, 9 Dec 2023 18:08:32 +0900 Subject: [PATCH 023/394] fix: exchange: allow up to 10k messages per block Also explicitly limit how many bytes we're willing to read in one go such that we're capable of reading a worst-case tipset (like, really, never going to happen worst-case). Previously, this wasn't an issue. However, we've bumped the max number of messages from 8,192 to 150,000 and need to limit allocations somewhere else. --- chain/exchange/cbor_gen.go | 140 +++++----------------------- chain/exchange/client.go | 10 +- chain/exchange/protocol.go | 2 + chain/exchange/protocol_encoding.go | 125 +++++++++++++++++++++++++ gen/main.go | 2 +- 5 files changed, 161 insertions(+), 118 deletions(-) create mode 100644 chain/exchange/protocol_encoding.go diff --git a/chain/exchange/cbor_gen.go b/chain/exchange/cbor_gen.go index e66b6d798..71c75869d 100644 --- a/chain/exchange/cbor_gen.go +++ b/chain/exchange/cbor_gen.go @@ -306,9 +306,9 @@ func (t *Response) UnmarshalCBOR(r io.Reader) (err error) { return nil } -var lengthBufCompactedMessages = []byte{132} +var lengthBufCompactedMessagesCBOR = []byte{132} -func (t *CompactedMessages) MarshalCBOR(w io.Writer) error { +func (t *CompactedMessagesCBOR) MarshalCBOR(w io.Writer) error { if t == nil { _, err := w.Write(cbg.CborNull) return err @@ -316,12 +316,12 @@ func (t *CompactedMessages) MarshalCBOR(w io.Writer) error { cw := cbg.NewCborWriter(w) - if _, err := cw.Write(lengthBufCompactedMessages); err != nil { + if _, err := cw.Write(lengthBufCompactedMessagesCBOR); err != nil { return err } // t.Bls ([]*types.Message) (slice) - if len(t.Bls) > cbg.MaxLength { + if len(t.Bls) > 150000 { return xerrors.Errorf("Slice value in field t.Bls was too long") } @@ -334,7 +334,7 @@ func (t *CompactedMessages) MarshalCBOR(w io.Writer) error { } } - // t.BlsIncludes ([][]uint64) (slice) + // t.BlsIncludes ([]exchange.messageIndices) (slice) if len(t.BlsIncludes) > cbg.MaxLength { return xerrors.Errorf("Slice value in field t.BlsIncludes was too long") } @@ -343,24 +343,13 @@ func (t *CompactedMessages) MarshalCBOR(w io.Writer) error { return err } for _, v := range t.BlsIncludes { - if len(v) > cbg.MaxLength { - return xerrors.Errorf("Slice value in field v was too long") - } - - if err := cw.WriteMajorTypeHeader(cbg.MajArray, uint64(len(v))); err != nil { + if err := v.MarshalCBOR(cw); err != nil { return err } - for _, v := range v { - - if err := cw.WriteMajorTypeHeader(cbg.MajUnsignedInt, uint64(v)); err != nil { - return err - } - - } } // t.Secpk ([]*types.SignedMessage) (slice) - if len(t.Secpk) > cbg.MaxLength { + if len(t.Secpk) > 150000 { return xerrors.Errorf("Slice value in field t.Secpk was too long") } @@ -373,7 +362,7 @@ func (t *CompactedMessages) MarshalCBOR(w io.Writer) error { } } - // t.SecpkIncludes ([][]uint64) (slice) + // t.SecpkIncludes ([]exchange.messageIndices) (slice) if len(t.SecpkIncludes) > cbg.MaxLength { return xerrors.Errorf("Slice value in field t.SecpkIncludes was too long") } @@ -382,26 +371,15 @@ func (t *CompactedMessages) MarshalCBOR(w io.Writer) error { return err } for _, v := range t.SecpkIncludes { - if len(v) > cbg.MaxLength { - return xerrors.Errorf("Slice value in field v was too long") - } - - if err := cw.WriteMajorTypeHeader(cbg.MajArray, uint64(len(v))); err != nil { + if err := v.MarshalCBOR(cw); err != nil { return err } - for _, v := range v { - - if err := cw.WriteMajorTypeHeader(cbg.MajUnsignedInt, uint64(v)); err != nil { - return err - } - - } } return nil } -func (t *CompactedMessages) UnmarshalCBOR(r io.Reader) (err error) { - *t = CompactedMessages{} +func (t *CompactedMessagesCBOR) UnmarshalCBOR(r io.Reader) (err error) { + *t = CompactedMessagesCBOR{} cr := cbg.NewCborReader(r) @@ -430,7 +408,7 @@ func (t *CompactedMessages) UnmarshalCBOR(r io.Reader) (err error) { return err } - if extra > cbg.MaxLength { + if extra > 150000 { return fmt.Errorf("t.Bls: array too large (%d)", extra) } @@ -471,7 +449,7 @@ func (t *CompactedMessages) UnmarshalCBOR(r io.Reader) (err error) { } } - // t.BlsIncludes ([][]uint64) (slice) + // t.BlsIncludes ([]exchange.messageIndices) (slice) maj, extra, err = cr.ReadHeader() if err != nil { @@ -487,7 +465,7 @@ func (t *CompactedMessages) UnmarshalCBOR(r io.Reader) (err error) { } if extra > 0 { - t.BlsIncludes = make([][]uint64, extra) + t.BlsIncludes = make([]messageIndices, extra) } for i := 0; i < int(extra); i++ { @@ -499,47 +477,13 @@ func (t *CompactedMessages) UnmarshalCBOR(r io.Reader) (err error) { _ = extra _ = err - maj, extra, err = cr.ReadHeader() - if err != nil { - return err - } + { - if extra > cbg.MaxLength { - return fmt.Errorf("t.BlsIncludes[i]: array too large (%d)", extra) - } - - if maj != cbg.MajArray { - return fmt.Errorf("expected cbor array") - } - - if extra > 0 { - t.BlsIncludes[i] = make([]uint64, extra) - } - - for j := 0; j < int(extra); j++ { - { - var maj byte - var extra uint64 - var err error - _ = maj - _ = extra - _ = err - - { - - maj, extra, err = cr.ReadHeader() - if err != nil { - return err - } - if maj != cbg.MajUnsignedInt { - return fmt.Errorf("wrong type for uint64 field") - } - t.BlsIncludes[i][j] = uint64(extra) - - } + if err := t.BlsIncludes[i].UnmarshalCBOR(cr); err != nil { + return xerrors.Errorf("unmarshaling t.BlsIncludes[i]: %w", err) } - } + } } } @@ -550,7 +494,7 @@ func (t *CompactedMessages) UnmarshalCBOR(r io.Reader) (err error) { return err } - if extra > cbg.MaxLength { + if extra > 150000 { return fmt.Errorf("t.Secpk: array too large (%d)", extra) } @@ -591,7 +535,7 @@ func (t *CompactedMessages) UnmarshalCBOR(r io.Reader) (err error) { } } - // t.SecpkIncludes ([][]uint64) (slice) + // t.SecpkIncludes ([]exchange.messageIndices) (slice) maj, extra, err = cr.ReadHeader() if err != nil { @@ -607,7 +551,7 @@ func (t *CompactedMessages) UnmarshalCBOR(r io.Reader) (err error) { } if extra > 0 { - t.SecpkIncludes = make([][]uint64, extra) + t.SecpkIncludes = make([]messageIndices, extra) } for i := 0; i < int(extra); i++ { @@ -619,47 +563,13 @@ func (t *CompactedMessages) UnmarshalCBOR(r io.Reader) (err error) { _ = extra _ = err - maj, extra, err = cr.ReadHeader() - if err != nil { - return err - } + { - if extra > cbg.MaxLength { - return fmt.Errorf("t.SecpkIncludes[i]: array too large (%d)", extra) - } - - if maj != cbg.MajArray { - return fmt.Errorf("expected cbor array") - } - - if extra > 0 { - t.SecpkIncludes[i] = make([]uint64, extra) - } - - for j := 0; j < int(extra); j++ { - { - var maj byte - var extra uint64 - var err error - _ = maj - _ = extra - _ = err - - { - - maj, extra, err = cr.ReadHeader() - if err != nil { - return err - } - if maj != cbg.MajUnsignedInt { - return fmt.Errorf("wrong type for uint64 field") - } - t.SecpkIncludes[i][j] = uint64(extra) - - } + if err := t.SecpkIncludes[i].UnmarshalCBOR(cr); err != nil { + return xerrors.Errorf("unmarshaling t.SecpkIncludes[i]: %w", err) } - } + } } } diff --git a/chain/exchange/client.go b/chain/exchange/client.go index db39628be..fca8249ce 100644 --- a/chain/exchange/client.go +++ b/chain/exchange/client.go @@ -4,6 +4,7 @@ import ( "bufio" "context" "fmt" + "io" "math/rand" "time" @@ -23,6 +24,10 @@ import ( "github.com/filecoin-project/lotus/lib/peermgr" ) +// Set the max exchange message size to 120MiB. Purely based on gas numbers, we can include ~8MiB of +// messages per block, so I've set this to 120MiB to be _very_ safe. +const maxExchangeMessageSize = (15 * 8) << 20 + // client implements exchange.Client, using the libp2p ChainExchange protocol // as the fetching mechanism. type client struct { @@ -434,10 +439,11 @@ func (c *client) sendRequestToPeer(ctx context.Context, peer peer.ID, req *Reque log.Warnw("CloseWrite err", "error", err) } - // Read response. + // Read response, limiting the size of the response to maxExchangeMessageSize as we allow a + // lot of messages (10k+) but they'll mostly be quite small. var res Response err = cborutil.ReadCborRPC( - bufio.NewReader(incrt.New(stream, ReadResMinSpeed, ReadResDeadline)), + bufio.NewReader(io.LimitReader(incrt.New(stream, ReadResMinSpeed, ReadResDeadline), maxExchangeMessageSize)), &res) if err != nil { c.peerTracker.logFailure(peer, build.Clock.Since(connectionStart), req.Length) diff --git a/chain/exchange/protocol.go b/chain/exchange/protocol.go index 5e12d31cc..cd25f4a43 100644 --- a/chain/exchange/protocol.go +++ b/chain/exchange/protocol.go @@ -154,6 +154,8 @@ type BSTipSet struct { // FIXME: The logic to decompress this structure should belong // // to itself, not to the consumer. +// +// NOTE: Max messages is: BlockMessageLimit (10k) * MaxTipsetSize (15) = 150k type CompactedMessages struct { Bls []*types.Message BlsIncludes [][]uint64 diff --git a/chain/exchange/protocol_encoding.go b/chain/exchange/protocol_encoding.go new file mode 100644 index 000000000..7df00a639 --- /dev/null +++ b/chain/exchange/protocol_encoding.go @@ -0,0 +1,125 @@ +package exchange + +import ( + "fmt" + "io" + + cbg "github.com/whyrusleeping/cbor-gen" + xerrors "golang.org/x/xerrors" + + "github.com/filecoin-project/lotus/build" + types "github.com/filecoin-project/lotus/chain/types" +) + +// Type used for encoding/decoding compacted messages. This is a ustom type as we need custom limits. +// - Max messages is 150,000 as that's 15 times the max block size (in messages). It needs to be +// large enough to cover a full tipset full of full blocks. +type CompactedMessagesCBOR struct { + Bls []*types.Message `cborgen:"maxlen=150000"` + BlsIncludes []messageIndices + + Secpk []*types.SignedMessage `cborgen:"maxlen=150000"` + SecpkIncludes []messageIndices +} + +// Unmarshal into the "decoding" struct, then copy into the actual struct. +func (t *CompactedMessages) UnmarshalCBOR(r io.Reader) (err error) { + var c CompactedMessagesCBOR + if err := c.UnmarshalCBOR(r); err != nil { + return err + } + t.Bls = c.Bls + t.BlsIncludes = make([][]uint64, len(c.BlsIncludes)) + for i, v := range c.BlsIncludes { + t.BlsIncludes[i] = v.v + } + t.Secpk = c.Secpk + t.SecpkIncludes = make([][]uint64, len(c.SecpkIncludes)) + for i, v := range c.SecpkIncludes { + t.SecpkIncludes[i] = v.v + } + return nil +} + +// Copy into the encoding struct, then marshal. +func (t *CompactedMessages) MarshalCBOR(w io.Writer) error { + if t == nil { + _, err := w.Write(cbg.CborNull) + return err + } + + var c CompactedMessagesCBOR + c.Bls = t.Bls + c.BlsIncludes = make([]messageIndices, len(t.BlsIncludes)) + for i, v := range t.BlsIncludes { + c.BlsIncludes[i].v = v + } + c.Secpk = t.Secpk + c.SecpkIncludes = make([]messageIndices, len(t.SecpkIncludes)) + for i, v := range t.SecpkIncludes { + c.SecpkIncludes[i].v = v + } + return c.MarshalCBOR(w) +} + +// this needs to be a struct or cborgen will peak into it and ignore the Unmarshal/Marshal functions +type messageIndices struct { + v []uint64 +} + +func (t *messageIndices) UnmarshalCBOR(r io.Reader) (err error) { + cr := cbg.NewCborReader(r) + + maj, extra, err := cr.ReadHeader() + if err != nil { + return err + } + + if maj != cbg.MajArray { + return fmt.Errorf("cbor input should be of type array") + } + + if extra > build.BlockMessageLimit { + return fmt.Errorf("cbor input had wrong number of fields") + } + + if extra > 0 { + t.v = make([]uint64, extra) + } + + for i := 0; i < int(extra); i++ { + maj, extra, err := cr.ReadHeader() + if err != nil { + return err + } + if maj != cbg.MajUnsignedInt { + return fmt.Errorf("wrong type for uint64 field") + } + t.v[i] = uint64(extra) + + } + return nil +} + +func (t *messageIndices) MarshalCBOR(w io.Writer) error { + if t == nil { + _, err := w.Write(cbg.CborNull) + return err + } + + cw := cbg.NewCborWriter(w) + + if len(t.v) > build.BlockMessageLimit { + return xerrors.Errorf("Slice value in field v was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajArray, uint64(len(t.v))); err != nil { + return err + } + for _, v := range t.v { + if err := cw.WriteMajorTypeHeader(cbg.MajUnsignedInt, uint64(v)); err != nil { + return err + } + } + return nil +} diff --git a/gen/main.go b/gen/main.go index 0cd3999c3..942b3ac2c 100644 --- a/gen/main.go +++ b/gen/main.go @@ -92,7 +92,7 @@ func main() { err = gen.WriteTupleEncodersToFile("./chain/exchange/cbor_gen.go", "exchange", exchange.Request{}, exchange.Response{}, - exchange.CompactedMessages{}, + exchange.CompactedMessagesCBOR{}, exchange.BSTipSet{}, ) if err != nil { From 88e90aaa44fbcca7c2505bade05db3f1035492bc Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Sat, 9 Dec 2023 18:21:22 +0900 Subject: [PATCH 024/394] fix lints --- chain/exchange/protocol_encoding.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/chain/exchange/protocol_encoding.go b/chain/exchange/protocol_encoding.go index 7df00a639..ca15831d6 100644 --- a/chain/exchange/protocol_encoding.go +++ b/chain/exchange/protocol_encoding.go @@ -95,7 +95,7 @@ func (t *messageIndices) UnmarshalCBOR(r io.Reader) (err error) { if maj != cbg.MajUnsignedInt { return fmt.Errorf("wrong type for uint64 field") } - t.v[i] = uint64(extra) + t.v[i] = extra } return nil @@ -117,7 +117,7 @@ func (t *messageIndices) MarshalCBOR(w io.Writer) error { return err } for _, v := range t.v { - if err := cw.WriteMajorTypeHeader(cbg.MajUnsignedInt, uint64(v)); err != nil { + if err := cw.WriteMajorTypeHeader(cbg.MajUnsignedInt, v); err != nil { return err } } From f2a4891ada91ff7cfb6b4fd7b1ee02426593ebf1 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Sat, 9 Dec 2023 23:24:43 +0900 Subject: [PATCH 025/394] fix: exchange: explicitly cast the block message limit const (#11511) It's not a const for the testground build, and needs to be an int 99% of the time. So we might as well just cast here. --- chain/exchange/protocol_encoding.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chain/exchange/protocol_encoding.go b/chain/exchange/protocol_encoding.go index ca15831d6..040dd0d40 100644 --- a/chain/exchange/protocol_encoding.go +++ b/chain/exchange/protocol_encoding.go @@ -79,7 +79,7 @@ func (t *messageIndices) UnmarshalCBOR(r io.Reader) (err error) { return fmt.Errorf("cbor input should be of type array") } - if extra > build.BlockMessageLimit { + if extra > uint64(build.BlockMessageLimit) { return fmt.Errorf("cbor input had wrong number of fields") } From 4a5c24176275dea965c6ed44bb42cb1c518a07c5 Mon Sep 17 00:00:00 2001 From: jennijuju Date: Sat, 9 Dec 2023 23:47:21 +0800 Subject: [PATCH 026/394] bump version --- build/openrpc/full.json.gz | Bin 34817 -> 34817 bytes build/openrpc/gateway.json.gz | Bin 11875 -> 11875 bytes build/openrpc/miner.json.gz | Bin 16370 -> 16370 bytes build/openrpc/worker.json.gz | Bin 6052 -> 6052 bytes build/version.go | 2 +- documentation/en/cli-lotus-miner.md | 2 +- documentation/en/cli-lotus-worker.md | 2 +- documentation/en/cli-lotus.md | 2 +- 8 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/openrpc/full.json.gz b/build/openrpc/full.json.gz index ad2116cb9187f20cea37fef3a37566fd3a2730cf..c40fbf2ccb25a5a4f9eebabe81c044faf4f0f2dc 100644 GIT binary patch delta 23 fcmZpiz|=T_X+j6%-i@6F?Hn&UKmVBWi;V#QelH6q delta 23 fcmZpiz|=T_X+j6%zKxv)?Hp?jf^Db#Vq*XRa*qi( diff --git a/build/openrpc/gateway.json.gz b/build/openrpc/gateway.json.gz index 48ad91135e370665117694d7515928b8773c1ad8..ead9a4e655ce0c24ba7d60320fce3a5ec0c92da1 100644 GIT binary patch delta 21 ccmaDH^EhTgGh@}pmg~A48j??1vU`~r0C83b@c;k- delta 21 dcmaDH^EhTgGvls}E!TB97}Rb5XZJEO0044z2L~AKE^3RkL{Tm0BvRm`v3p{ delta 21 dcmexV|EYdL3uF7n)>M0rAjbFCAKNoC004Ob37-G} diff --git a/build/openrpc/worker.json.gz b/build/openrpc/worker.json.gz index 8e9d63d795a477a58214191b82d59ea52770cd47..078e8f95c7f0c4af2487dfe8f2798551d7b43122 100644 GIT binary patch delta 21 ccmZ3YzeImRBjbyWO;X|<^7C8L3nLgA09~mEfB*mh delta 21 ccmZ3YzeImRBV)$KCMj_a$+8 Date: Sat, 9 Dec 2023 11:20:41 -0600 Subject: [PATCH 027/394] fix: lint --- lib/harmony/harmonydb/userfuncs.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/harmony/harmonydb/userfuncs.go b/lib/harmony/harmonydb/userfuncs.go index 8abce73d0..eb17efd46 100644 --- a/lib/harmony/harmonydb/userfuncs.go +++ b/lib/harmony/harmonydb/userfuncs.go @@ -12,7 +12,7 @@ import ( "github.com/samber/lo" ) -var inTxErr = errors.New("Cannot use a non-transaction func in a transaction") +var errTx = errors.New("Cannot use a non-transaction func in a transaction") // rawStringOnly is _intentionally_private_ to force only basic strings in SQL queries. // In any package, raw strings will satisfy compilation. Ex: @@ -27,7 +27,7 @@ type rawStringOnly string // them in the ./sql/ files (next number). func (db *DB) Exec(ctx context.Context, sql rawStringOnly, arguments ...any) (count int, err error) { if db.usedInTransaction() { - return 0, inTxErr + return 0, errTx } res, err := db.pgx.Exec(ctx, string(sql), arguments...) return int(res.RowsAffected()), err @@ -63,7 +63,7 @@ type Query struct { // } func (db *DB) Query(ctx context.Context, sql rawStringOnly, arguments ...any) (*Query, error) { if db.usedInTransaction() { - return &Query{}, inTxErr + return &Query{}, errTx } q, err := db.pgx.Query(ctx, string(sql), arguments...) return &Query{q}, err @@ -78,7 +78,7 @@ type Row interface { type rowErr struct{} -func (rowErr) Scan(_ ...any) error { return inTxErr } +func (rowErr) Scan(_ ...any) error { return errTx } // QueryRow gets 1 row using column order matching. // This is a timesaver for the special case of wanting the first row returned only. @@ -110,7 +110,7 @@ Ex: */ func (db *DB) Select(ctx context.Context, sliceOfStructPtr any, sql rawStringOnly, arguments ...any) error { if db.usedInTransaction() { - return inTxErr + return errTx } return pgxscan.Select(ctx, db.pgx, sliceOfStructPtr, string(sql), arguments...) } @@ -124,9 +124,9 @@ type Tx struct { // & non-transaction calls in transactions. It only checks 20 frames. // Fast: This memory should all be in CPU Caches. func (db *DB) usedInTransaction() bool { - var framePtrs = (&[20]uintptr{})[:] - runtime.Callers(3, framePtrs) - return lo.Contains(framePtrs, db.BTFP) + var framePtrs = (&[20]uintptr{})[:] // 20 can be stack-local (no alloc) + runtime.Callers(3, framePtrs) // skip past our caller. + return lo.Contains(framePtrs, db.BTFP) // Unsafe read @ beginTx overlap, but 'return false' is correct there. } // BeginTransaction is how you can access transactions using this library. @@ -144,7 +144,7 @@ func (db *DB) BeginTransaction(ctx context.Context, f func(*Tx) (commit bool, er db.BTFP = fp[0] }) if db.usedInTransaction() { - return false, inTxErr + return false, errTx } tx, err := db.pgx.BeginTx(ctx, pgx.TxOptions{}) if err != nil { From 96353e63ea062212b0227cb69155252764538856 Mon Sep 17 00:00:00 2001 From: "Andrew Jackson (Ajax)" Date: Mon, 11 Dec 2023 10:50:49 -0600 Subject: [PATCH 028/394] exp backoff, short stack err --- cmd/lotus-provider/proving.go | 3 +++ lib/harmony/harmonydb/userfuncs.go | 6 +++--- lib/harmony/harmonytask/task_type_handler.go | 6 ++++++ storage/paths/db_index.go | 9 +++++++++ 4 files changed, 21 insertions(+), 3 deletions(-) diff --git a/cmd/lotus-provider/proving.go b/cmd/lotus-provider/proving.go index 621896c91..025950586 100644 --- a/cmd/lotus-provider/proving.go +++ b/cmd/lotus-provider/proving.go @@ -82,6 +82,7 @@ var wdPostTaskCmd = &cli.Command{ return xerrors.Errorf("cannot get miner id %w", err) } var id int64 + retryDelay := time.Millisecond * 10 retryAddTask: _, err = deps.db.BeginTransaction(ctx, func(tx *harmonydb.Tx) (commit bool, err error) { err = tx.QueryRow(`INSERT INTO harmony_task (name, posted_time, added_by) VALUES ('WdPost', CURRENT_TIMESTAMP, 123) RETURNING id`).Scan(&id) @@ -104,6 +105,8 @@ var wdPostTaskCmd = &cli.Command{ }) if err != nil { if harmonydb.IsErrSerialization(err) { + time.Sleep(retryDelay) + retryDelay *= 2 goto retryAddTask } return xerrors.Errorf("writing SQL transaction: %w", err) diff --git a/lib/harmony/harmonydb/userfuncs.go b/lib/harmony/harmonydb/userfuncs.go index eb17efd46..9e2b5a1b6 100644 --- a/lib/harmony/harmonydb/userfuncs.go +++ b/lib/harmony/harmonydb/userfuncs.go @@ -124,9 +124,9 @@ type Tx struct { // & non-transaction calls in transactions. It only checks 20 frames. // Fast: This memory should all be in CPU Caches. func (db *DB) usedInTransaction() bool { - var framePtrs = (&[20]uintptr{})[:] // 20 can be stack-local (no alloc) - runtime.Callers(3, framePtrs) // skip past our caller. - return lo.Contains(framePtrs, db.BTFP) // Unsafe read @ beginTx overlap, but 'return false' is correct there. + var framePtrs = (&[20]uintptr{})[:] // 20 can be stack-local (no alloc) + framePtrs = framePtrs[:runtime.Callers(3, framePtrs)] // skip past our caller. + return lo.Contains(framePtrs, db.BTFP) // Unsafe read @ beginTx overlap, but 'return false' is correct there. } // BeginTransaction is how you can access transactions using this library. diff --git a/lib/harmony/harmonytask/task_type_handler.go b/lib/harmony/harmonytask/task_type_handler.go index 6091cb61e..ccfcc1f6f 100644 --- a/lib/harmony/harmonytask/task_type_handler.go +++ b/lib/harmony/harmonytask/task_type_handler.go @@ -25,6 +25,7 @@ type taskTypeHandler struct { func (h *taskTypeHandler) AddTask(extra func(TaskID, *harmonydb.Tx) (bool, error)) { var tID TaskID + retryWait := time.Millisecond * 100 retryAddTask: _, err := h.TaskEngine.db.BeginTransaction(h.TaskEngine.ctx, func(tx *harmonydb.Tx) (bool, error) { // create taskID (from DB) @@ -46,6 +47,8 @@ retryAddTask: return } if harmonydb.IsErrSerialization(err) { + time.Sleep(retryWait) + retryWait *= 2 goto retryAddTask } log.Error("Could not add task. AddTasFunc failed: %v", err) @@ -165,6 +168,7 @@ top: func (h *taskTypeHandler) recordCompletion(tID TaskID, workStart time.Time, done bool, doErr error) { workEnd := time.Now() + retryWait := time.Millisecond * 100 retryRecordCompletion: cm, err := h.TaskEngine.db.BeginTransaction(h.TaskEngine.ctx, func(tx *harmonydb.Tx) (bool, error) { var postedTime time.Time @@ -219,6 +223,8 @@ VALUES ($1, $2, $3, $4, $5, $6, $7, $8)`, tID, h.Name, postedTime, workStart, wo }) if err != nil { if harmonydb.IsErrSerialization(err) { + time.Sleep(retryWait) + retryWait *= 2 goto retryRecordCompletion } log.Error("Could not record transaction: ", err) diff --git a/storage/paths/db_index.go b/storage/paths/db_index.go index 69a03198d..e6bf3e5da 100644 --- a/storage/paths/db_index.go +++ b/storage/paths/db_index.go @@ -180,6 +180,7 @@ func (dbi *DBIndex) StorageAttach(ctx context.Context, si storiface.StorageInfo, } } + retryWait := time.Millisecond * 100 retryAttachStorage: // Single transaction to attach storage which is not present in the DB _, err := dbi.harmonyDB.BeginTransaction(ctx, func(tx *harmonydb.Tx) (commit bool, err error) { @@ -246,6 +247,8 @@ retryAttachStorage: }) if err != nil { if harmonydb.IsErrSerialization(err) { + time.Sleep(retryWait) + retryWait *= 2 goto retryAttachStorage } return err @@ -287,6 +290,7 @@ func (dbi *DBIndex) StorageDetach(ctx context.Context, id storiface.ID, url stri log.Warnw("Dropping sector path endpoint", "path", id, "url", url) } else { + retryWait := time.Millisecond * 100 retryDropPath: // Single transaction to drop storage path and sector decls which have this as a storage path _, err := dbi.harmonyDB.BeginTransaction(ctx, func(tx *harmonydb.Tx) (commit bool, err error) { @@ -305,6 +309,8 @@ func (dbi *DBIndex) StorageDetach(ctx context.Context, id storiface.ID, url stri }) if err != nil { if harmonydb.IsErrSerialization(err) { + time.Sleep(retryWait) + retryWait *= 2 goto retryDropPath } return err @@ -380,6 +386,7 @@ func (dbi *DBIndex) StorageDeclareSector(ctx context.Context, storageID storifac return xerrors.Errorf("invalid filetype") } + retryWait := time.Millisecond * 100 retryStorageDeclareSector: _, err := dbi.harmonyDB.BeginTransaction(ctx, func(tx *harmonydb.Tx) (commit bool, err error) { var currPrimary sql.NullBool @@ -416,6 +423,8 @@ retryStorageDeclareSector: }) if err != nil { if harmonydb.IsErrSerialization(err) { + time.Sleep(retryWait) + retryWait *= 2 goto retryStorageDeclareSector } return err From de38e77cfc11d471d13eb00c6ae209e6bf8abb03 Mon Sep 17 00:00:00 2001 From: "Andrew Jackson (Ajax)" Date: Mon, 11 Dec 2023 11:31:38 -0600 Subject: [PATCH 029/394] tx-detector use atomic --- lib/harmony/harmonydb/harmonydb.go | 3 ++- lib/harmony/harmonydb/userfuncs.go | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/harmony/harmonydb/harmonydb.go b/lib/harmony/harmonydb/harmonydb.go index caa5d1daa..5ec9f5a25 100644 --- a/lib/harmony/harmonydb/harmonydb.go +++ b/lib/harmony/harmonydb/harmonydb.go @@ -11,6 +11,7 @@ import ( "strconv" "strings" "sync" + "sync/atomic" "time" logging "github.com/ipfs/go-log/v2" @@ -35,7 +36,7 @@ type DB struct { schema string hostnames []string BTFPOnce sync.Once - BTFP uintptr + BTFP atomic.Uintptr } var logger = logging.Logger("harmonydb") diff --git a/lib/harmony/harmonydb/userfuncs.go b/lib/harmony/harmonydb/userfuncs.go index 9e2b5a1b6..7fcf76dcd 100644 --- a/lib/harmony/harmonydb/userfuncs.go +++ b/lib/harmony/harmonydb/userfuncs.go @@ -126,7 +126,7 @@ type Tx struct { func (db *DB) usedInTransaction() bool { var framePtrs = (&[20]uintptr{})[:] // 20 can be stack-local (no alloc) framePtrs = framePtrs[:runtime.Callers(3, framePtrs)] // skip past our caller. - return lo.Contains(framePtrs, db.BTFP) // Unsafe read @ beginTx overlap, but 'return false' is correct there. + return lo.Contains(framePtrs, db.BTFP.Load()) // Unsafe read @ beginTx overlap, but 'return false' is correct there. } // BeginTransaction is how you can access transactions using this library. @@ -141,7 +141,7 @@ func (db *DB) BeginTransaction(ctx context.Context, f func(*Tx) (commit bool, er db.BTFPOnce.Do(func() { fp := make([]uintptr, 20) runtime.Callers(1, fp) - db.BTFP = fp[0] + db.BTFP.Store(fp[0]) }) if db.usedInTransaction() { return false, errTx From becfc470a291c84881699584356ea5cacb6fadbf Mon Sep 17 00:00:00 2001 From: "Andrew Jackson (Ajax)" Date: Mon, 11 Dec 2023 17:47:29 -0600 Subject: [PATCH 030/394] basic ensemble provider --- itests/kit/ensemble.go | 56 +++++++++++++++++++++++++++++----- itests/kit/ensemble_presets.go | 2 +- 2 files changed, 50 insertions(+), 8 deletions(-) diff --git a/itests/kit/ensemble.go b/itests/kit/ensemble.go index 3c83ba896..12e2ce8aa 100644 --- a/itests/kit/ensemble.go +++ b/itests/kit/ensemble.go @@ -20,6 +20,7 @@ import ( "github.com/libp2p/go-libp2p/core/peer" mocknet "github.com/libp2p/go-libp2p/p2p/net/mock" "github.com/stretchr/testify/require" + "github.com/urfave/cli/v2" "github.com/filecoin-project/go-address" cborutil "github.com/filecoin-project/go-cbor-util" @@ -45,6 +46,9 @@ import ( "github.com/filecoin-project/lotus/chain/stmgr" "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/wallet/key" + "github.com/filecoin-project/lotus/cmd/lotus-provider/deps" + "github.com/filecoin-project/lotus/cmd/lotus-provider/rpc" + "github.com/filecoin-project/lotus/cmd/lotus-provider/tasks" "github.com/filecoin-project/lotus/cmd/lotus-seed/seed" "github.com/filecoin-project/lotus/cmd/lotus-worker/sealworker" "github.com/filecoin-project/lotus/gateway" @@ -120,15 +124,17 @@ type Ensemble struct { options *ensembleOpts inactive struct { - fullnodes []*TestFullNode - miners []*TestMiner - workers []*TestWorker + fullnodes []*TestFullNode + providernodes []*TestProviderNode + miners []*TestMiner + workers []*TestWorker } active struct { - fullnodes []*TestFullNode - miners []*TestMiner - workers []*TestWorker - bms map[*TestMiner]*BlockMiner + fullnodes []*TestFullNode + providernodes []*TestProviderNode + miners []*TestMiner + workers []*TestWorker + bms map[*TestMiner]*BlockMiner } genesis struct { version network.Version @@ -221,6 +227,20 @@ func (n *Ensemble) FullNode(full *TestFullNode, opts ...NodeOpt) *Ensemble { return n } +// FullNode enrolls a new Provider node. +func (n *Ensemble) Provider(lp *TestProviderNode, opts ...NodeOpt) *Ensemble { + options := DefaultNodeOpts + for _, o := range opts { + err := o(&options) + require.NoError(n.t, err) + } + + *lp = TestProviderNode{t: n.t, options: options, Deps: &deps.Deps{}} + + n.inactive.providernodes = append(n.inactive.providernodes, lp) + return n +} + // Miner enrolls a new miner, using the provided full node for chain // interactions. func (n *Ensemble) MinerEnroll(minerNode *TestMiner, full *TestFullNode, opts ...NodeOpt) *Ensemble { @@ -886,6 +906,28 @@ func (n *Ensemble) Start() *Ensemble { // to active, so clear the slice. n.inactive.workers = n.inactive.workers[:0] + for _, p := range n.inactive.providernodes { + + // TODO setup config with options + err := p.Deps.PopulateRemainingDeps(context.Background(), &cli.Context{}, false) + require.NoError(n.t, err) + + shutdownChan := make(chan struct{}) + taskEngine, err := tasks.StartTasks(ctx, p.Deps) + if err != nil { + return nil + } + defer taskEngine.GracefullyTerminate(time.Hour) + + err = rpc.ListenAndServe(ctx, p.Deps, shutdownChan) // Monitor for shutdown. + require.NoError(n.t, err) + finishCh := node.MonitorShutdown(shutdownChan) //node.ShutdownHandler{Component: "rpc server", StopFunc: rpcStopper}, + //node.ShutdownHandler{Component: "provider", StopFunc: stop}, + + <-finishCh + + n.active.providernodes = append(n.active.providernodes, p) + } // --------------------- // MISC // --------------------- diff --git a/itests/kit/ensemble_presets.go b/itests/kit/ensemble_presets.go index 88f07b684..68b85fde0 100644 --- a/itests/kit/ensemble_presets.go +++ b/itests/kit/ensemble_presets.go @@ -112,7 +112,7 @@ func EnsembleProvider(t *testing.T, opts ...interface{}) (*TestFullNode, *TestPr full TestFullNode provider TestProviderNode ) - ens := NewEnsemble(t, eopts...).FullNode(&full, nopts...).Provider(&provider, &full, nopts...).Start() + ens := NewEnsemble(t, eopts...).FullNode(&full, nopts...).Provider(&provider, nopts...).Start() return &full, &provider, ens } From cdcb96567c8c598ada8c6aa60f8366f83b5d2cf7 Mon Sep 17 00:00:00 2001 From: "Andrew Jackson (Ajax)" Date: Mon, 11 Dec 2023 18:12:05 -0600 Subject: [PATCH 031/394] listenAndServe hooked up --- cmd/lotus-provider/rpc/rpc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/lotus-provider/rpc/rpc.go b/cmd/lotus-provider/rpc/rpc.go index 0fe1039d9..b724d8d4d 100644 --- a/cmd/lotus-provider/rpc/rpc.go +++ b/cmd/lotus-provider/rpc/rpc.go @@ -134,5 +134,5 @@ func ListenAndServe(ctx context.Context, dependencies *deps.Deps, shutdownChan c log.Warn("Graceful shutdown successful") }() - return nil + return srv.ListenAndServe() } From 0bad4d910dd055deb20440144509f60873511687 Mon Sep 17 00:00:00 2001 From: "Andrew Jackson (Ajax)" Date: Mon, 11 Dec 2023 18:30:39 -0600 Subject: [PATCH 032/394] fix the listener --- cmd/lotus-provider/proving.go | 2 +- cmd/lotus-provider/rpc/rpc.go | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/cmd/lotus-provider/proving.go b/cmd/lotus-provider/proving.go index 1602739ed..379bfdf85 100644 --- a/cmd/lotus-provider/proving.go +++ b/cmd/lotus-provider/proving.go @@ -86,7 +86,7 @@ var wdPostTaskCmd = &cli.Command{ retryDelay := time.Millisecond * 10 retryAddTask: - _, err = deps.db.BeginTransaction(ctx, func(tx *harmonydb.Tx) (commit bool, err error) { + _, err = deps.DB.BeginTransaction(ctx, func(tx *harmonydb.Tx) (commit bool, err error) { err = tx.QueryRow(`INSERT INTO harmony_task (name, posted_time, added_by) VALUES ('WdPost', CURRENT_TIMESTAMP, 123) RETURNING id`).Scan(&id) if err != nil { log.Error("inserting harmony_task: ", err) diff --git a/cmd/lotus-provider/rpc/rpc.go b/cmd/lotus-provider/rpc/rpc.go index b724d8d4d..cc0ecbb3b 100644 --- a/cmd/lotus-provider/rpc/rpc.go +++ b/cmd/lotus-provider/rpc/rpc.go @@ -123,6 +123,7 @@ func ListenAndServe(ctx context.Context, dependencies *deps.Deps, shutdownChan c ctx, _ := tag.New(context.Background(), tag.Upsert(metrics.APIInterface, "lotus-worker")) return ctx }, + Addr: dependencies.ListenAddr, } go func() { From 0175faf5c2a3b7695e7d863714e3becd0d028fdf Mon Sep 17 00:00:00 2001 From: "Andrew Jackson (Ajax)" Date: Mon, 11 Dec 2023 21:10:15 -0600 Subject: [PATCH 033/394] deps fix --- cmd/lotus-provider/deps/deps.go | 7 +++++-- cmd/lotus-provider/rpc/rpc.go | 1 + cmd/lotus-provider/run.go | 7 ++++--- ~/.lotus-provider/journal/lotus-journal.ndjson | 0 4 files changed, 10 insertions(+), 5 deletions(-) create mode 100644 ~/.lotus-provider/journal/lotus-journal.ndjson diff --git a/cmd/lotus-provider/deps/deps.go b/cmd/lotus-provider/deps/deps.go index 54305f6a1..13b83b83d 100644 --- a/cmd/lotus-provider/deps/deps.go +++ b/cmd/lotus-provider/deps/deps.go @@ -83,8 +83,8 @@ func StorageAuth(apiKey string) (sealer.StorageAuth, error) { } func GetDeps(ctx context.Context, cctx *cli.Context) (*Deps, error) { - var deps *Deps - return deps, deps.PopulateRemainingDeps(ctx, cctx, true) + var deps Deps + return &deps, deps.PopulateRemainingDeps(ctx, cctx, true) } type Deps struct { @@ -107,6 +107,9 @@ const ( func (deps *Deps) PopulateRemainingDeps(ctx context.Context, cctx *cli.Context, makeRepo bool) error { + if deps == nil { + *deps = Deps{} + } var err error if makeRepo { // Open repo diff --git a/cmd/lotus-provider/rpc/rpc.go b/cmd/lotus-provider/rpc/rpc.go index cc0ecbb3b..1f075f79a 100644 --- a/cmd/lotus-provider/rpc/rpc.go +++ b/cmd/lotus-provider/rpc/rpc.go @@ -135,5 +135,6 @@ func ListenAndServe(ctx context.Context, dependencies *deps.Deps, shutdownChan c log.Warn("Graceful shutdown successful") }() + log.Infof("Setting up RPC server at %s", dependencies.ListenAddr) return srv.ListenAndServe() } diff --git a/cmd/lotus-provider/run.go b/cmd/lotus-provider/run.go index 975762f0a..f58d0c939 100644 --- a/cmd/lotus-provider/run.go +++ b/cmd/lotus-provider/run.go @@ -113,11 +113,12 @@ var runCmd = &cli.Command{ } } - var dependencies *deps.Deps - err = dependencies.PopulateRemainingDeps(ctx, cctx, true) + var deps deps.Deps + err = deps.PopulateRemainingDeps(ctx, cctx, true) if err != nil { return err } + dependencies := &deps taskEngine, err := tasks.StartTasks(ctx, dependencies) @@ -136,4 +137,4 @@ var runCmd = &cli.Command{ <-finishCh return nil }, -} \ No newline at end of file +} diff --git a/~/.lotus-provider/journal/lotus-journal.ndjson b/~/.lotus-provider/journal/lotus-journal.ndjson new file mode 100644 index 000000000..e69de29bb From a478b734fd7d93ea30a1de2210c3160e185a06f7 Mon Sep 17 00:00:00 2001 From: "Andrew Jackson (Ajax)" Date: Mon, 11 Dec 2023 23:15:41 -0600 Subject: [PATCH 034/394] build-env: unable to exit from deps --- cmd/lotus-provider/deps/deps.go | 5 +---- cmd/lotus-provider/main.go | 6 +++--- cmd/lotus-provider/run.go | 10 +++++++--- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/cmd/lotus-provider/deps/deps.go b/cmd/lotus-provider/deps/deps.go index 13b83b83d..9a0a3691f 100644 --- a/cmd/lotus-provider/deps/deps.go +++ b/cmd/lotus-provider/deps/deps.go @@ -106,10 +106,6 @@ const ( ) func (deps *Deps) PopulateRemainingDeps(ctx context.Context, cctx *cli.Context, makeRepo bool) error { - - if deps == nil { - *deps = Deps{} - } var err error if makeRepo { // Open repo @@ -242,6 +238,7 @@ Get it with: jq .PrivateKey ~/.lotus-miner/keystore/MF2XI2BNNJ3XILLQOJUXMYLUMU`, deps.Maddrs = append(deps.Maddrs, dtypes.MinerAddress(addr)) } } + fmt.Println("last line of populate") return nil } diff --git a/cmd/lotus-provider/main.go b/cmd/lotus-provider/main.go index 48ff70122..18b2e87f3 100644 --- a/cmd/lotus-provider/main.go +++ b/cmd/lotus-provider/main.go @@ -5,7 +5,7 @@ import ( "fmt" "os" "os/signal" - "runtime/debug" + "runtime/pprof" "syscall" "github.com/fatih/color" @@ -29,8 +29,8 @@ func SetupCloseHandler() { go func() { <-c fmt.Println("\r- Ctrl+C pressed in Terminal") - debug.PrintStack() - os.Exit(1) + pprof.Lookup("goroutine").WriteTo(os.Stdout, 1) + panic(1) }() } diff --git a/cmd/lotus-provider/run.go b/cmd/lotus-provider/run.go index f58d0c939..8e7d5ad79 100644 --- a/cmd/lotus-provider/run.go +++ b/cmd/lotus-provider/run.go @@ -113,14 +113,18 @@ var runCmd = &cli.Command{ } } - var deps deps.Deps - err = deps.PopulateRemainingDeps(ctx, cctx, true) + fmt.Println("before populateRemainingDeps") + dependencies := &deps.Deps{} + err = dependencies.PopulateRemainingDeps(ctx, cctx, true) + fmt.Println("after popdeps") if err != nil { + fmt.Println("err", err) return err } - dependencies := &deps + fmt.Println("ef") taskEngine, err := tasks.StartTasks(ctx, dependencies) + fmt.Println("gh") if err != nil { return nil From 4161c270d73f7ac706f9b2d2d8314378d2fc5609 Mon Sep 17 00:00:00 2001 From: "Andrew Jackson (Ajax)" Date: Mon, 11 Dec 2023 23:16:57 -0600 Subject: [PATCH 035/394] debugWeb 1 --- cmd/lotus-provider/rpc/rpc.go | 18 +- cmd/lotus-provider/web/api/debug/debug.go | 95 +++++++++ cmd/lotus-provider/web/api/routes.go | 12 ++ cmd/lotus-provider/web/srv.go | 40 ++++ cmd/lotus-provider/web/static/index.html | 181 ++++++++++++++++++ .../web/static/modules/chain-connectivity.js | 73 +++++++ node/config/def.go | 4 +- node/config/types.go | 3 + 8 files changed, 423 insertions(+), 3 deletions(-) create mode 100644 cmd/lotus-provider/web/api/debug/debug.go create mode 100644 cmd/lotus-provider/web/api/routes.go create mode 100644 cmd/lotus-provider/web/srv.go create mode 100644 cmd/lotus-provider/web/static/index.html create mode 100644 cmd/lotus-provider/web/static/modules/chain-connectivity.js diff --git a/cmd/lotus-provider/rpc/rpc.go b/cmd/lotus-provider/rpc/rpc.go index 1f075f79a..4b4a77cf9 100644 --- a/cmd/lotus-provider/rpc/rpc.go +++ b/cmd/lotus-provider/rpc/rpc.go @@ -13,6 +13,7 @@ import ( "github.com/gorilla/mux" logging "github.com/ipfs/go-log/v2" "go.opencensus.io/tag" + "golang.org/x/sync/errgroup" "golang.org/x/xerrors" "github.com/filecoin-project/go-jsonrpc" @@ -20,6 +21,7 @@ import ( "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/cmd/lotus-provider/deps" + "github.com/filecoin-project/lotus/cmd/lotus-provider/web" "github.com/filecoin-project/lotus/lib/rpcenc" "github.com/filecoin-project/lotus/metrics" "github.com/filecoin-project/lotus/metrics/proxy" @@ -126,15 +128,27 @@ func ListenAndServe(ctx context.Context, dependencies *deps.Deps, shutdownChan c Addr: dependencies.ListenAddr, } + log.Infof("Setting up RPC server at %s", dependencies.ListenAddr) + + web, err := web.GetSrv(ctx, dependencies) + if err != nil { + return err + } + go func() { <-ctx.Done() log.Warn("Shutting down...") if err := srv.Shutdown(context.TODO()); err != nil { log.Errorf("shutting down RPC server failed: %s", err) } + if err := web.Shutdown(context.Background()); err != nil { + log.Errorf("shutting down web server failed: %s", err) + } log.Warn("Graceful shutdown successful") }() - log.Infof("Setting up RPC server at %s", dependencies.ListenAddr) - return srv.ListenAndServe() + eg := errgroup.Group{} + eg.Go(srv.ListenAndServe) + eg.Go(web.ListenAndServe) + return eg.Wait() } diff --git a/cmd/lotus-provider/web/api/debug/debug.go b/cmd/lotus-provider/web/api/debug/debug.go new file mode 100644 index 000000000..1dcd7c5a3 --- /dev/null +++ b/cmd/lotus-provider/web/api/debug/debug.go @@ -0,0 +1,95 @@ +// Package debug provides the API for various debug endpoints in lotus-provider. +package debug + +import ( + "encoding/json" + "fmt" + "net/http" + "time" + + "github.com/gorilla/mux" + logging "github.com/ipfs/go-log/v2" + + "github.com/filecoin-project/lotus/build" + cliutil "github.com/filecoin-project/lotus/cli/util" + "github.com/filecoin-project/lotus/cmd/lotus-provider/deps" +) + +var log = logging.Logger("lp/web/debug") + +type debug struct { + *deps.Deps +} + +func Routes(r *mux.Router, deps *deps.Deps) { + d := debug{deps} + r.Methods("GET").Path("chain-state-sse").HandlerFunc(d.chainStateSSE) +} + +type rpcInfo struct { + Address string + CLayers []string + Reachable bool + SyncState string + Version string +} + +func (d *debug) chainStateSSE(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Access-Control-Allow-Origin", "*") + w.Header().Set("Access-Control-Allow-Headers", "Content-Type") + w.Header().Set("Content-Type", "text/event-stream") + w.Header().Set("Cache-Control", "no-cache") + w.Header().Set("Connection", "keep-alive") + + v1api := d.Deps.Full + ctx := r.Context() + + ai := cliutil.ParseApiInfo(d.Deps.Cfg.Apis.ChainApiInfo[0]) + ver, err := v1api.Version(ctx) + if err != nil { + log.Warnw("Version", "error", err) + return + } + +sse: + for { + head, err := v1api.ChainHead(ctx) + if err != nil { + log.Warnw("ChainHead", "error", err) + return + } + + var syncState string + switch { + case time.Now().Unix()-int64(head.MinTimestamp()) < int64(build.BlockDelaySecs*3/2): // within 1.5 epochs + syncState = "ok" + case time.Now().Unix()-int64(head.MinTimestamp()) < int64(build.BlockDelaySecs*5): // within 5 epochs + syncState = fmt.Sprintf("slow (%s behind)", time.Since(time.Unix(int64(head.MinTimestamp()), 0)).Truncate(time.Second)) + default: + syncState = fmt.Sprintf("behind (%s behind)", time.Since(time.Unix(int64(head.MinTimestamp()), 0)).Truncate(time.Second)) + } + + select { + case <-ctx.Done(): + break sse + default: + } + + fmt.Fprintf(w, "data: ") + err = json.NewEncoder(w).Encode(rpcInfo{ + Address: ai.Addr, + CLayers: []string{}, + Reachable: true, + Version: ver.Version, + SyncState: syncState, + }) + if err != nil { + log.Warnw("json encode", "error", err) + return + } + fmt.Fprintf(w, "\n\n") + if f, ok := w.(http.Flusher); ok { + f.Flush() + } + } +} diff --git a/cmd/lotus-provider/web/api/routes.go b/cmd/lotus-provider/web/api/routes.go new file mode 100644 index 000000000..91e317722 --- /dev/null +++ b/cmd/lotus-provider/web/api/routes.go @@ -0,0 +1,12 @@ +// Package api provides the HTTP API for the lotus provider web gui. +package api + +import ( + "github.com/filecoin-project/lotus/cmd/lotus-provider/deps" + "github.com/filecoin-project/lotus/cmd/lotus-provider/web/api/debug" + "github.com/gorilla/mux" +) + +func Routes(r *mux.Router, deps *deps.Deps) { + debug.Routes(r.PathPrefix("/debug").Subrouter(), deps) +} diff --git a/cmd/lotus-provider/web/srv.go b/cmd/lotus-provider/web/srv.go new file mode 100644 index 000000000..f59a8d837 --- /dev/null +++ b/cmd/lotus-provider/web/srv.go @@ -0,0 +1,40 @@ +// Package web defines the HTTP web server for static files and endpoints. +package web + +import ( + "context" + "embed" + "net" + "net/http" + "strings" + + "github.com/filecoin-project/lotus/cmd/lotus-provider/deps" + "github.com/filecoin-project/lotus/cmd/lotus-provider/web/api" + "github.com/filecoin-project/lotus/metrics" + "github.com/gorilla/mux" + "go.opencensus.io/tag" +) + +// go:embed static +var static embed.FS + +func GetSrv(ctx context.Context, deps *deps.Deps) (*http.Server, error) { + mux := mux.NewRouter() + api.Routes(mux.PathPrefix("/api").Subrouter(), deps) + mux.NotFoundHandler = http.FileServer(http.FS(static)) + + return &http.Server{ + Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if strings.HasSuffix(r.URL.Path, "/") { + r.URL.Path = r.URL.Path + "index.html" + return + } + mux.ServeHTTP(w, r) + }), + BaseContext: func(listener net.Listener) context.Context { + ctx, _ := tag.New(context.Background(), tag.Upsert(metrics.APIInterface, "lotus-provider")) + return ctx + }, + Addr: deps.Cfg.Subsystems.GuiAddress, + }, nil +} diff --git a/cmd/lotus-provider/web/static/index.html b/cmd/lotus-provider/web/static/index.html new file mode 100644 index 000000000..731ee799b --- /dev/null +++ b/cmd/lotus-provider/web/static/index.html @@ -0,0 +1,181 @@ + + + + Lotus Provider Cluster Overview + + + + + +

+
+

Lotus Provider Cluster

+
+
+ version [todo] +
+
+
+
+
+

Chain Connectivity

+ +
+
+
+
+

Actor Summary

+ + + + + + + + + + + + + + + + + +
AddressConfig LayersQaPDeadlines
f01234mig023TiB +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + + + \ No newline at end of file diff --git a/cmd/lotus-provider/web/static/modules/chain-connectivity.js b/cmd/lotus-provider/web/static/modules/chain-connectivity.js new file mode 100644 index 000000000..871f40094 --- /dev/null +++ b/cmd/lotus-provider/web/static/modules/chain-connectivity.js @@ -0,0 +1,73 @@ +import { LitElement, html, css } from 'https://cdn.jsdelivr.net/npm/lit-html@3.1.0/lit-html.min.js'; +window.customElements.define('chain-connectivity', class MyElement extends LitElement { + constructor() { + super(); + this.data = []; + this.loadData(); + } + loadData() { + const eventSource = new EventSource('/api/debug/chain-state-sse'); + eventSource.onmessage = (event) => { + this.data.push(JSON.parse(event.data)); + }; + eventSource.onerror = (error) => { + console.error('Error:', error); + loadData(); + }; + }; + + static get styles() { + return [css` + :host { + box-sizing: border-box; /* Don't forgert this to include padding/border inside width calculation */ + } + table { + border-collapse: collapse; + } + + table td, table th { + border-left: 1px solid #f0f0f0; + padding: 1px 5px; + } + + table tr td:first-child, table tr th:first-child { + border-left: none; + } + + .success { + color: green; + } + .warning { + color: yellow; + } + .error { + color: red; + } + `]; + } + render = () => html` + + + + + + + + + + + ${this.data.map(item => html` + + + + + + + + `)} + + + + +
RPC AddressReachabilitySync StatusVersion
{{.Address}}${item.Address}${item.Reachable ? html`ok` : html`FAIL`}${item.SyncState === "ok" ? html`ok` : html`${item.SyncState}`}${item.Version}
Data incoming...
` +}); diff --git a/node/config/def.go b/node/config/def.go index dc358b140..e8f315add 100644 --- a/node/config/def.go +++ b/node/config/def.go @@ -351,7 +351,9 @@ func DefaultUserRaftConfig() *UserRaftConfig { func DefaultLotusProvider() *LotusProviderConfig { return &LotusProviderConfig{ - Subsystems: ProviderSubsystemsConfig{}, + Subsystems: ProviderSubsystemsConfig{ + GuiAddress: ":4701", + }, Fees: LotusProviderFees{ DefaultMaxFee: DefaultDefaultMaxFee, MaxPreCommitGasFee: types.MustParseFIL("0.025"), diff --git a/node/config/types.go b/node/config/types.go index 2152e0795..6233191f4 100644 --- a/node/config/types.go +++ b/node/config/types.go @@ -96,6 +96,9 @@ type ProviderSubsystemsConfig struct { WindowPostMaxTasks int EnableWinningPost bool WinningPostMaxTasks int + + // The address that should listen for Web GUI requests. + GuiAddress string } type DAGStoreConfig struct { From 17e17eaac3fe6542a26ac2ac16749b8f807c15fd Mon Sep 17 00:00:00 2001 From: "Andrew Jackson (Ajax)" Date: Mon, 11 Dec 2023 23:36:04 -0600 Subject: [PATCH 036/394] move --- cmd/lotus-provider/web/static/{modules => }/chain-connectivity.js | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename cmd/lotus-provider/web/static/{modules => }/chain-connectivity.js (100%) diff --git a/cmd/lotus-provider/web/static/modules/chain-connectivity.js b/cmd/lotus-provider/web/static/chain-connectivity.js similarity index 100% rename from cmd/lotus-provider/web/static/modules/chain-connectivity.js rename to cmd/lotus-provider/web/static/chain-connectivity.js From 8070e3424f8f5a761f05eec2c92ddbe642c5bdac Mon Sep 17 00:00:00 2001 From: "Andrew Jackson (Ajax)" Date: Mon, 11 Dec 2023 23:47:31 -0600 Subject: [PATCH 037/394] fix deps --- cmd/lotus-provider/web/static/chain-connectivity.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/lotus-provider/web/static/chain-connectivity.js b/cmd/lotus-provider/web/static/chain-connectivity.js index 871f40094..2381a2f22 100644 --- a/cmd/lotus-provider/web/static/chain-connectivity.js +++ b/cmd/lotus-provider/web/static/chain-connectivity.js @@ -1,4 +1,4 @@ -import { LitElement, html, css } from 'https://cdn.jsdelivr.net/npm/lit-html@3.1.0/lit-html.min.js'; +import { LitElement, html, css } from 'https://cdn.jsdelivr.net/gh/lit/dist@3/core/lit-all.min.js'; window.customElements.define('chain-connectivity', class MyElement extends LitElement { constructor() { super(); From 5717ec8c0af286e1d3832ace7c561ca2bbdf7f07 Mon Sep 17 00:00:00 2001 From: "Andrew Jackson (Ajax)" Date: Mon, 11 Dec 2023 23:48:07 -0600 Subject: [PATCH 038/394] fix deps2 --- cmd/lotus-provider/web/static/chain-connectivity.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/lotus-provider/web/static/chain-connectivity.js b/cmd/lotus-provider/web/static/chain-connectivity.js index 2381a2f22..6fc63234f 100644 --- a/cmd/lotus-provider/web/static/chain-connectivity.js +++ b/cmd/lotus-provider/web/static/chain-connectivity.js @@ -1,4 +1,4 @@ -import { LitElement, html, css } from 'https://cdn.jsdelivr.net/gh/lit/dist@3/core/lit-all.min.js'; +import { LitElement, html, css } from 'https://cdn.jsdelivr.net/gh/lit/dist@3/all/lit-all.min.js'; window.customElements.define('chain-connectivity', class MyElement extends LitElement { constructor() { super(); From bc76004c1564da48ecda173b92d65c038e50e30a Mon Sep 17 00:00:00 2001 From: "Andrew Jackson (Ajax)" Date: Tue, 12 Dec 2023 08:07:38 -0600 Subject: [PATCH 039/394] module --- cmd/lotus-provider/web/static/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/lotus-provider/web/static/index.html b/cmd/lotus-provider/web/static/index.html index 731ee799b..00b48b6fd 100644 --- a/cmd/lotus-provider/web/static/index.html +++ b/cmd/lotus-provider/web/static/index.html @@ -2,7 +2,7 @@ Lotus Provider Cluster Overview - + - -
-
-

Lotus Provider Cluster

-
-
- version [todo] -
+
+
+

Lotus Provider Cluster

-
-
-
-

Chain Connectivity

- -
+
+ version [todo] +
+
+
+
+
+

Chain Connectivity

+

Actor Summary

- - - - - - + + + + + + - - - - - - - +
AddressConfig LayersQaPDeadlines
AddressConfig LayersQaPDeadlines
f01234mig023TiB -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+

Cluster Machines

+ + + + + + + + + + + +
HostIDConfig LayersLast Contact
+
+
+

Recently Finished Tasks

+ + + + + + + + + + + + + + + +
NameIDExecutorPostedStartEndOutcomeMessage
+
+
+
+

Cluster Tasks

+ + + + + + + + + + + +
TaskIDPostedOwner
+
+
- \ No newline at end of file diff --git a/lib/harmony/harmonytask/task_type_handler.go b/lib/harmony/harmonytask/task_type_handler.go index ccfcc1f6f..a6e8933d2 100644 --- a/lib/harmony/harmonytask/task_type_handler.go +++ b/lib/harmony/harmonytask/task_type_handler.go @@ -184,6 +184,9 @@ retryRecordCompletion: return false, fmt.Errorf("could not log completion: %w", err) } result = "" + if doErr != nil { + result = "non-failing error: " + doErr.Error() + } } else { if doErr != nil { result = "error: " + doErr.Error() diff --git a/provider/lpwinning/winning_task.go b/provider/lpwinning/winning_task.go index 907b594fd..6e5e1cc5a 100644 --- a/provider/lpwinning/winning_task.go +++ b/provider/lpwinning/winning_task.go @@ -156,13 +156,18 @@ func (t *WinPostTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (don ComputeTime: details.CompTime, } - persistNoWin := func() error { - _, err := t.db.Exec(ctx, `UPDATE mining_base_block SET no_win = true WHERE task_id = $1`, taskID) + persistNoWin := func() (bool, error) { + n, err := t.db.Exec(ctx, `UPDATE mining_base_block SET no_win = true WHERE task_id = $1`, taskID) if err != nil { - return xerrors.Errorf("marking base as not-won: %w", err) + return false, xerrors.Errorf("marking base as not-won: %w", err) + } + log.Debugw("persisted no-win", "rows", n) + + if n == 0 { + return false, xerrors.Errorf("persist no win: no rows updated") } - return nil + return false, nil } // ensure we have a beacon entry for the epoch we're mining on @@ -182,13 +187,13 @@ func (t *WinPostTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (don if mbi == nil { // not eligible to mine on this base, we're done here log.Debugw("WinPoSt not eligible to mine on this base", "tipset", types.LogCids(base.TipSet.Cids())) - return true, persistNoWin() + return persistNoWin() } if !mbi.EligibleForMining { // slashed or just have no power yet, we're done here log.Debugw("WinPoSt not eligible for mining", "tipset", types.LogCids(base.TipSet.Cids())) - return true, persistNoWin() + return persistNoWin() } if len(mbi.Sectors) == 0 { @@ -217,7 +222,7 @@ func (t *WinPostTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (don if eproof == nil { // not a winner, we're done here log.Debugw("WinPoSt not a winner", "tipset", types.LogCids(base.TipSet.Cids())) - return true, persistNoWin() + return persistNoWin() } } From 25b228c2f60cf9c0021544f06d11d766e64dc01a Mon Sep 17 00:00:00 2001 From: "Andrew Jackson (Ajax)" Date: Tue, 19 Dec 2023 09:12:45 -0600 Subject: [PATCH 051/394] fix: lint --- cmd/lotus-provider/web/api/routes.go | 3 ++- cmd/lotus-provider/web/hapi/routes.go | 4 ++-- cmd/lotus-provider/web/srv.go | 5 +++-- documentation/en/cli-lotus-provider.md | 18 ++++++++++++++++++ .../en/default-lotus-provider-config.toml | 8 ++++++++ node/config/doc_gen.go | 12 ++++++++++++ 6 files changed, 45 insertions(+), 5 deletions(-) diff --git a/cmd/lotus-provider/web/api/routes.go b/cmd/lotus-provider/web/api/routes.go index 91e317722..9bb6fb67c 100644 --- a/cmd/lotus-provider/web/api/routes.go +++ b/cmd/lotus-provider/web/api/routes.go @@ -2,9 +2,10 @@ package api import ( + "github.com/gorilla/mux" + "github.com/filecoin-project/lotus/cmd/lotus-provider/deps" "github.com/filecoin-project/lotus/cmd/lotus-provider/web/api/debug" - "github.com/gorilla/mux" ) func Routes(r *mux.Router, deps *deps.Deps) { diff --git a/cmd/lotus-provider/web/hapi/routes.go b/cmd/lotus-provider/web/hapi/routes.go index b56c8c7c0..d327a76a8 100644 --- a/cmd/lotus-provider/web/hapi/routes.go +++ b/cmd/lotus-provider/web/hapi/routes.go @@ -4,11 +4,11 @@ import ( "embed" "html/template" + "github.com/gorilla/mux" logging "github.com/ipfs/go-log/v2" + "golang.org/x/xerrors" "github.com/filecoin-project/lotus/cmd/lotus-provider/deps" - "github.com/gorilla/mux" - "golang.org/x/xerrors" ) //go:embed web/* diff --git a/cmd/lotus-provider/web/srv.go b/cmd/lotus-provider/web/srv.go index 869280005..f41501fef 100644 --- a/cmd/lotus-provider/web/srv.go +++ b/cmd/lotus-provider/web/srv.go @@ -8,12 +8,13 @@ import ( "net/http" "strings" + "github.com/gorilla/mux" + "go.opencensus.io/tag" + "github.com/filecoin-project/lotus/cmd/lotus-provider/deps" "github.com/filecoin-project/lotus/cmd/lotus-provider/web/api" "github.com/filecoin-project/lotus/cmd/lotus-provider/web/hapi" "github.com/filecoin-project/lotus/metrics" - "github.com/gorilla/mux" - "go.opencensus.io/tag" ) // go:embed static diff --git a/documentation/en/cli-lotus-provider.md b/documentation/en/cli-lotus-provider.md index de77697e3..a01cd0856 100644 --- a/documentation/en/cli-lotus-provider.md +++ b/documentation/en/cli-lotus-provider.md @@ -14,6 +14,7 @@ COMMANDS: stop Stop a running lotus provider config Manage node config by layers. The layer 'base' will always be applied. test Utility functions for testing + web Start lotus provider web interface version Print version help, h Shows a list of commands or help for one command DEVELOPER: @@ -247,6 +248,23 @@ OPTIONS: --help, -h show help ``` +## lotus-provider web +``` +NAME: + lotus-provider web - Start lotus provider web interface + +USAGE: + lotus-provider web [command options] [arguments...] + +DESCRIPTION: + Start an instance of lotus provider web interface. + This creates the 'web' layer if it does not exist, then calls run with that layer. + +OPTIONS: + --listen value Address to listen on (default: "127.0.0.1:4701") + --help, -h show help +``` + ## lotus-provider version ``` NAME: diff --git a/documentation/en/default-lotus-provider-config.toml b/documentation/en/default-lotus-provider-config.toml index 91606e503..8573fbda1 100644 --- a/documentation/en/default-lotus-provider-config.toml +++ b/documentation/en/default-lotus-provider-config.toml @@ -11,6 +11,14 @@ # type: int #WinningPostMaxTasks = 0 + # type: bool + #EnableWebGui = false + + # The address that should listen for Web GUI requests. + # + # type: string + #GuiAddress = ":4701" + [Fees] # type: types.FIL diff --git a/node/config/doc_gen.go b/node/config/doc_gen.go index 13ecb2706..012214770 100644 --- a/node/config/doc_gen.go +++ b/node/config/doc_gen.go @@ -1013,6 +1013,18 @@ block rewards will be missed!`, Comment: ``, }, + { + Name: "EnableWebGui", + Type: "bool", + + Comment: ``, + }, + { + Name: "GuiAddress", + Type: "string", + + Comment: `The address that should listen for Web GUI requests.`, + }, }, "ProvingConfig": { { From bfc26cdc184c9c1f4dd8d9c7d8a8dea912aeb03b Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Tue, 19 Dec 2023 16:39:52 -0500 Subject: [PATCH 052/394] fix: sync: do not include incoming in return of syncFork (#11541) --- chain/sync.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/chain/sync.go b/chain/sync.go index c61c9f310..4dccc2036 100644 --- a/chain/sync.go +++ b/chain/sync.go @@ -896,7 +896,7 @@ func (syncer *Syncer) syncFork(ctx context.Context, incoming *types.TipSet, know if commonParent { // known contains at least one of incoming's Parents => the common ancestor is known's Parents (incoming's Grandparents) - // in this case, we need to return {incoming, incoming.Parents()} + // in this case, we need to return {incoming.Parents()} incomingParents, err := syncer.store.LoadTipSet(ctx, incomingParentsTsk) if err != nil { // fallback onto the network @@ -912,7 +912,7 @@ func (syncer *Syncer) syncFork(ctx context.Context, incoming *types.TipSet, know incomingParents = tips[0] } - return []*types.TipSet{incoming, incomingParents}, nil + return []*types.TipSet{incomingParents}, nil } // TODO: Does this mean we always ask for ForkLengthThreshold blocks from the network, even if we just need, like, 2? Yes. From b0bc4a96320942c0673a3cab08314074edf4b4f9 Mon Sep 17 00:00:00 2001 From: Rob Quist Date: Wed, 20 Dec 2023 01:18:20 +0100 Subject: [PATCH 053/394] Fix 2 typo's (#11542) * Fix typo in miner * Fix typo in sectors.go --- cmd/lotus-miner/sectors.go | 2 +- cmd/lotus-shed/miner.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/lotus-miner/sectors.go b/cmd/lotus-miner/sectors.go index 07cc2e795..3e4439eb8 100644 --- a/cmd/lotus-miner/sectors.go +++ b/cmd/lotus-miner/sectors.go @@ -2290,7 +2290,7 @@ var sectorsCompactPartitionsCmd = &cli.Command{ if len(parts) <= 0 { return fmt.Errorf("must include at least one partition to compact") } - fmt.Printf("compacting %d paritions\n", len(parts)) + fmt.Printf("compacting %d partitions\n", len(parts)) var makeMsgForPartitions func(partitionsBf bitfield.BitField) ([]*types.Message, error) makeMsgForPartitions = func(partitionsBf bitfield.BitField) ([]*types.Message, error) { diff --git a/cmd/lotus-shed/miner.go b/cmd/lotus-shed/miner.go index a8bb93744..2f9b4ecf1 100644 --- a/cmd/lotus-shed/miner.go +++ b/cmd/lotus-shed/miner.go @@ -553,7 +553,7 @@ var sendInvalidWindowPoStCmd = &cli.Command{ return xerrors.Errorf("serializing params: %w", err) } - fmt.Printf("submitting bad PoST for %d paritions\n", len(partitionIndices)) + fmt.Printf("submitting bad PoST for %d partitions\n", len(partitionIndices)) smsg, err := api.MpoolPushMessage(ctx, &types.Message{ From: minfo.Worker, To: maddr, From 8dffab5d3964487448f232ab4ff7a3fe46283598 Mon Sep 17 00:00:00 2001 From: "Andrew Jackson (Ajax)" Date: Tue, 19 Dec 2023 18:58:55 -0600 Subject: [PATCH 054/394] fix: lp lint and dev simplicities --- cmd/lotus-provider/run.go | 8 ++++++-- cmd/lotus-provider/web/hapi/simpleinfo.go | 2 +- cmd/lotus-provider/web/srv.go | 13 ++++++++++++- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/cmd/lotus-provider/run.go b/cmd/lotus-provider/run.go index 00e3079d9..a47cb6ec4 100644 --- a/cmd/lotus-provider/run.go +++ b/cmd/lotus-provider/run.go @@ -167,12 +167,16 @@ var webCmd = &cli.Command{ cfg := config.DefaultLotusProvider() cfg.Subsystems.EnableWebGui = true var b bytes.Buffer - toml.NewEncoder(&b).Encode(cfg) + if err = toml.NewEncoder(&b).Encode(cfg); err != nil { + return err + } if err = setConfig(db, "web", b.String()); err != nil { return err } } - cctx.Set("layers", "web") + if err = cctx.Set("layers", "web"); err != nil { + return err + } return runCmd.Action(cctx) }, } diff --git a/cmd/lotus-provider/web/hapi/simpleinfo.go b/cmd/lotus-provider/web/hapi/simpleinfo.go index 470739518..a14735a84 100644 --- a/cmd/lotus-provider/web/hapi/simpleinfo.go +++ b/cmd/lotus-provider/web/hapi/simpleinfo.go @@ -77,7 +77,7 @@ func (a *app) indexTasksHistory(w http.ResponseWriter, r *http.Request) { a.executeTemplate(w, "cluster_task_history", s) } -var templateDev = os.Getenv("LOTUS_HAPI_TEMPLATE_DEV") == "1" +var templateDev = os.Getenv("LOTUS_WEB_DEV") == "1" func (a *app) executeTemplate(w http.ResponseWriter, name string, data interface{}) { if templateDev { diff --git a/cmd/lotus-provider/web/srv.go b/cmd/lotus-provider/web/srv.go index f41501fef..c618d0baa 100644 --- a/cmd/lotus-provider/web/srv.go +++ b/cmd/lotus-provider/web/srv.go @@ -6,7 +6,9 @@ import ( "embed" "net" "net/http" + "os" "strings" + "time" "github.com/gorilla/mux" "go.opencensus.io/tag" @@ -20,6 +22,10 @@ import ( // go:embed static var static embed.FS +// An dev mode hack for no-restart changes to static and templates. +// You still need to recomplie the binary for changes to go code. +var webDev = os.Getenv("LOTUS_WEB_DEV") == "1" + func GetSrv(ctx context.Context, deps *deps.Deps) (*http.Server, error) { mux := mux.NewRouter() api.Routes(mux.PathPrefix("/api").Subrouter(), deps) @@ -28,6 +34,9 @@ func GetSrv(ctx context.Context, deps *deps.Deps) (*http.Server, error) { return nil, err } mux.NotFoundHandler = http.FileServer(http.FS(static)) + if webDev { + mux.NotFoundHandler = http.FileServer(http.Dir("cmd/lotus-provider/web/static")) + } return &http.Server{ Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -41,6 +50,8 @@ func GetSrv(ctx context.Context, deps *deps.Deps) (*http.Server, error) { ctx, _ := tag.New(context.Background(), tag.Upsert(metrics.APIInterface, "lotus-provider")) return ctx }, - Addr: deps.Cfg.Subsystems.GuiAddress, + Addr: deps.Cfg.Subsystems.GuiAddress, + ReadTimeout: time.Minute * 3, + ReadHeaderTimeout: time.Minute * 3, // lint }, nil } From d9d1b4370ce149b1809c0d26f15014db3fc1df89 Mon Sep 17 00:00:00 2001 From: Rob Quist Date: Thu, 21 Dec 2023 18:14:00 +0100 Subject: [PATCH 055/394] Remove error log as its no longer an error since snap --- storage/pipeline/input.go | 1 - 1 file changed, 1 deletion(-) diff --git a/storage/pipeline/input.go b/storage/pipeline/input.go index 6fdae03d9..b595f533d 100644 --- a/storage/pipeline/input.go +++ b/storage/pipeline/input.go @@ -600,7 +600,6 @@ func (m *Sealing) updateInput(ctx context.Context, sp abi.RegisteredSealProof) e log.Debugw("updateInput matching done", "matches", len(matches), "toAssign", len(toAssign), "assigned", assigned, "openSectors", len(m.openSectors), "pieces", len(m.pendingPieces)) if len(toAssign) > 0 { - log.Errorf("we are trying to create a new sector with open sectors %v", m.openSectors) if err := m.tryGetDealSector(ctx, sp, getExpirationCached); err != nil { log.Errorw("Failed to create a new sector for deals", "error", err) } From 464e492fd20ac28da2030a2c5df66fb6d964038e Mon Sep 17 00:00:00 2001 From: "Andrew Jackson (Ajax)" Date: Sat, 30 Dec 2023 09:37:04 -0600 Subject: [PATCH 056/394] srv cleanups --- cli/util/api.go | 2 +- cmd/lotus-provider/deps/deps.go | 8 +++- cmd/lotus-provider/run.go | 14 +++++- cmd/lotus-provider/web/api/debug/debug.go | 31 ++++++------- cmd/lotus-provider/web/hapi/routes.go | 4 +- cmd/lotus-provider/web/srv.go | 53 +++++++++++++++++------ 6 files changed, 75 insertions(+), 37 deletions(-) diff --git a/cli/util/api.go b/cli/util/api.go index 3602b752d..fe1ac1536 100644 --- a/cli/util/api.go +++ b/cli/util/api.go @@ -445,7 +445,7 @@ func GetFullNodeAPIV1LotusProvider(ctx *cli.Context, ainfoCfg []string, opts ... for _, head := range heads { v1api, closer, err := client.NewFullNodeRPCV1(ctx.Context, head.addr, head.header, rpcOpts...) if err != nil { - log.Warnf("Not able to establish connection to node with addr: %s", head.addr) + log.Warnf("Not able to establish connection to node with addr: %s, Reason: %s", head.addr, err.Error()) continue } fullNodes = append(fullNodes, v1api) diff --git a/cmd/lotus-provider/deps/deps.go b/cmd/lotus-provider/deps/deps.go index 499cc7bec..7a8db855f 100644 --- a/cmd/lotus-provider/deps/deps.go +++ b/cmd/lotus-provider/deps/deps.go @@ -9,6 +9,7 @@ import ( "fmt" "net" "net/http" + "os" "strings" "github.com/BurntSushi/toml" @@ -176,7 +177,11 @@ func (deps *Deps) PopulateRemainingDeps(ctx context.Context, cctx *cli.Context, if deps.Full == nil { var fullCloser func() - deps.Full, fullCloser, err = cliutil.GetFullNodeAPIV1LotusProvider(cctx, deps.Cfg.Apis.ChainApiInfo) + cfgApiInfo := deps.Cfg.Apis.ChainApiInfo + if v := os.Getenv("FULLNODE_API_INFO"); v != "" { + cfgApiInfo = []string{v} + } + deps.Full, fullCloser, err = cliutil.GetFullNodeAPIV1LotusProvider(cctx, cfgApiInfo) if err != nil { return err } @@ -267,6 +272,7 @@ func GetConfig(cctx *cli.Context, db *harmonydb.DB) (*config.LotusProviderConfig for _, k := range meta.Keys() { have = append(have, strings.Join(k, " ")) } + log.Infow("Using layer", "layer", layer, "config", lp) } _ = have // FUTURE: verify that required fields are here. // If config includes 3rd-party config, consider JSONSchema as a way that diff --git a/cmd/lotus-provider/run.go b/cmd/lotus-provider/run.go index a47cb6ec4..0f18f2843 100644 --- a/cmd/lotus-provider/run.go +++ b/cmd/lotus-provider/run.go @@ -5,6 +5,7 @@ import ( "context" "fmt" "os" + "strings" "time" "github.com/BurntSushi/toml" @@ -155,6 +156,15 @@ var webCmd = &cli.Command{ Usage: "Address to listen on", Value: "127.0.0.1:4701", }, + &cli.StringSliceFlag{ + Name: "layers", + Usage: "list of layers to be interpreted (atop defaults). Default: base. Web will be added", + Value: cli.NewStringSlice("base"), + }, + &cli.BoolFlag{ + Name: "nosync", + Usage: "don't check full-node sync status", + }, }, Action: func(cctx *cli.Context) error { db, err := deps.MakeDB(cctx) @@ -174,7 +184,9 @@ var webCmd = &cli.Command{ return err } } - if err = cctx.Set("layers", "web"); err != nil { + layers := append([]string{"web"}, cctx.StringSlice("layers")...) + err = cctx.Set("layers", strings.Join(layers, ",")) + if err != nil { return err } return runCmd.Action(cctx) diff --git a/cmd/lotus-provider/web/api/debug/debug.go b/cmd/lotus-provider/web/api/debug/debug.go index 7382ca3f8..845684519 100644 --- a/cmd/lotus-provider/web/api/debug/debug.go +++ b/cmd/lotus-provider/web/api/debug/debug.go @@ -29,7 +29,7 @@ type debug struct { func Routes(r *mux.Router, deps *deps.Deps) { d := debug{deps} - r.Methods("GET").Path("chain-state-sse").HandlerFunc(d.chainStateSSE) + r.HandleFunc("/chain-state-sse", d.chainStateSSE) } type rpcInfo struct { @@ -79,29 +79,24 @@ func (d *debug) chainStateSSE(w http.ResponseWriter, r *http.Request) { return } - apiInfos := map[string][]byte{} // api address -> token - // for dedup by address - for _, info := range rpcInfos { - ai := cliutil.ParseApiInfo(info.Apis.ChainApiInfo[0]) - apiInfos[ai.Addr] = ai.Token - } + dedup := map[string]bool{} // for dedup by address infos := map[string]rpcInfo{} // api address -> rpc info var infosLk sync.Mutex var wg sync.WaitGroup - wg.Add(len(rpcInfos)) - for addr, token := range apiInfos { - addr := addr - ai := cliutil.APIInfo{ - Addr: addr, - Token: token, + for _, info := range rpcInfos { + ai := cliutil.ParseApiInfo(info.Apis.ChainApiInfo[0]) + if dedup[ai.Addr] { + continue } - go func(info string) { + dedup[ai.Addr] = true + wg.Add(1) + go func() { defer wg.Done() var clayers []string for layer, a := range confNameToAddr { - if a == addr { + if a == ai.Addr { clayers = append(clayers, layer) } } @@ -113,8 +108,8 @@ func (d *debug) chainStateSSE(w http.ResponseWriter, r *http.Request) { } defer func() { infosLk.Lock() + defer infosLk.Unlock() infos[ai.Addr] = myinfo - infosLk.Unlock() }() da, err := ai.DialArgs("v1") if err != nil { @@ -126,7 +121,7 @@ func (d *debug) chainStateSSE(w http.ResponseWriter, r *http.Request) { v1api, closer, err := client.NewFullNodeRPCV1(ctx, da, ah) if err != nil { - log.Warnf("Not able to establish connection to node with addr: %s", addr) + log.Warnf("Not able to establish connection to node with addr: %s", ai.Addr) return } defer closer() @@ -160,7 +155,7 @@ func (d *debug) chainStateSSE(w http.ResponseWriter, r *http.Request) { Version: ver.Version, SyncState: syncState, } - }(addr) + }() } wg.Wait() diff --git a/cmd/lotus-provider/web/hapi/routes.go b/cmd/lotus-provider/web/hapi/routes.go index d327a76a8..b07ab60a5 100644 --- a/cmd/lotus-provider/web/hapi/routes.go +++ b/cmd/lotus-provider/web/hapi/routes.go @@ -15,9 +15,7 @@ import ( var templateFS embed.FS func Routes(r *mux.Router, deps *deps.Deps) error { - - t := new(template.Template) - t, err := t.ParseFS(templateFS, "web/*") + t, err := template.ParseFS(templateFS, "web/*") if err != nil { return xerrors.Errorf("parse templates: %w", err) } diff --git a/cmd/lotus-provider/web/srv.go b/cmd/lotus-provider/web/srv.go index c618d0baa..d409eb868 100644 --- a/cmd/lotus-provider/web/srv.go +++ b/cmd/lotus-provider/web/srv.go @@ -4,9 +4,12 @@ package web import ( "context" "embed" + "io" + "io/fs" "net" "net/http" "os" + "path" "strings" "time" @@ -19,33 +22,57 @@ import ( "github.com/filecoin-project/lotus/metrics" ) -// go:embed static +//go:embed static var static embed.FS +var basePath = "/static/" + // An dev mode hack for no-restart changes to static and templates. // You still need to recomplie the binary for changes to go code. var webDev = os.Getenv("LOTUS_WEB_DEV") == "1" func GetSrv(ctx context.Context, deps *deps.Deps) (*http.Server, error) { - mux := mux.NewRouter() - api.Routes(mux.PathPrefix("/api").Subrouter(), deps) - err := hapi.Routes(mux.PathPrefix("/hapi").Subrouter(), deps) + mx := mux.NewRouter() + err := hapi.Routes(mx.PathPrefix("/hapi").Subrouter(), deps) if err != nil { return nil, err } - mux.NotFoundHandler = http.FileServer(http.FS(static)) + api.Routes(mx.PathPrefix("/api").Subrouter(), deps) + + basePath := basePath + + var static fs.FS = static if webDev { - mux.NotFoundHandler = http.FileServer(http.Dir("cmd/lotus-provider/web/static")) + basePath = "cmd/lotus-provider/web/static" + static = os.DirFS(basePath) } + mx.NotFoundHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // If the request is for a directory, redirect to the index file. + if strings.HasSuffix(r.URL.Path, "/") { + r.URL.Path += "index.html" + } + + file, err := static.Open(path.Join(basePath, r.URL.Path)[1:]) + if err != nil { + w.WriteHeader(http.StatusNotFound) + w.Write([]byte("404 Not Found")) + return + } + defer file.Close() + + fileInfo, err := file.Stat() + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte("500 Internal Server Error")) + return + } + + http.ServeContent(w, r, fileInfo.Name(), fileInfo.ModTime(), file.(io.ReadSeeker)) + }) + return &http.Server{ - Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if strings.HasSuffix(r.URL.Path, "/") { - r.URL.Path = r.URL.Path + "index.html" - return - } - mux.ServeHTTP(w, r) - }), + Handler: http.HandlerFunc(mx.ServeHTTP), BaseContext: func(listener net.Listener) context.Context { ctx, _ := tag.New(context.Background(), tag.Upsert(metrics.APIInterface, "lotus-provider")) return ctx From 56bbeb01af7d55489ea69f83847d39a47ba087bb Mon Sep 17 00:00:00 2001 From: "Andrew Jackson (Ajax)" Date: Tue, 2 Jan 2024 11:29:55 -0600 Subject: [PATCH 057/394] fix: lint --- cmd/lotus-provider/web/srv.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/lotus-provider/web/srv.go b/cmd/lotus-provider/web/srv.go index d409eb868..55d20cc9a 100644 --- a/cmd/lotus-provider/web/srv.go +++ b/cmd/lotus-provider/web/srv.go @@ -56,15 +56,15 @@ func GetSrv(ctx context.Context, deps *deps.Deps) (*http.Server, error) { file, err := static.Open(path.Join(basePath, r.URL.Path)[1:]) if err != nil { w.WriteHeader(http.StatusNotFound) - w.Write([]byte("404 Not Found")) + _, _ = w.Write([]byte("404 Not Found")) return } - defer file.Close() + defer func() { _ = file.Close() }() fileInfo, err := file.Stat() if err != nil { w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte("500 Internal Server Error")) + _, _ = w.Write([]byte("500 Internal Server Error")) return } From 15128b69a7a93a0a294d05b91b53164d7a90e060 Mon Sep 17 00:00:00 2001 From: "Andrew Jackson (Ajax)" Date: Wed, 3 Jan 2024 10:11:10 -0600 Subject: [PATCH 058/394] Update winning_task.go last return should be true --- provider/lpwinning/winning_task.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/provider/lpwinning/winning_task.go b/provider/lpwinning/winning_task.go index 6e5e1cc5a..bf4f2fe71 100644 --- a/provider/lpwinning/winning_task.go +++ b/provider/lpwinning/winning_task.go @@ -167,7 +167,7 @@ func (t *WinPostTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (don return false, xerrors.Errorf("persist no win: no rows updated") } - return false, nil + return true, nil } // ensure we have a beacon entry for the epoch we're mining on From ad8e90d31cfecf14daa09b603409734280bc3e29 Mon Sep 17 00:00:00 2001 From: "Andrew Jackson (Ajax)" Date: Wed, 3 Jan 2024 16:18:23 -0600 Subject: [PATCH 059/394] lp debugWeb ui bug --- cmd/lotus-provider/web/static/chain-connectivity.js | 1 - 1 file changed, 1 deletion(-) diff --git a/cmd/lotus-provider/web/static/chain-connectivity.js b/cmd/lotus-provider/web/static/chain-connectivity.js index 1b37666bb..ea7349c41 100644 --- a/cmd/lotus-provider/web/static/chain-connectivity.js +++ b/cmd/lotus-provider/web/static/chain-connectivity.js @@ -59,7 +59,6 @@ window.customElements.define('chain-connectivity', class MyElement extends LitEl ${this.data.map(item => html` - {{.Address}} ${item.Address} ${item.Reachable ? html`ok` : html`FAIL`} ${item.SyncState === "ok" ? html`ok` : html`${item.SyncState}`} From fafaf0d7af0c14a74a83da65b539ac5810bc8346 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 4 Jan 2024 12:47:30 +0100 Subject: [PATCH 060/394] fix: wdpost: fix vanilla proof indexes --- storage/sealer/worker_local.go | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/storage/sealer/worker_local.go b/storage/sealer/worker_local.go index 9f2f2efdc..7fc494955 100644 --- a/storage/sealer/worker_local.go +++ b/storage/sealer/worker_local.go @@ -664,7 +664,7 @@ func (l *LocalWorker) GenerateWindowPoStAdv(ctx context.Context, ppt abi.Registe var wg sync.WaitGroup wg.Add(len(sectors)) - vproofs := make([][]byte, 0, len(sectors)) + vproofs := make([][]byte, len(sectors)) for i, s := range sectors { if l.challengeThrottle != nil { @@ -702,8 +702,7 @@ func (l *LocalWorker) GenerateWindowPoStAdv(ctx context.Context, ppt abi.Registe return } - //vproofs[i] = vanilla // todo substitutes?? - vproofs = append(vproofs, vanilla) + vproofs[i] = vanilla }(i, s) } wg.Wait() @@ -717,6 +716,22 @@ func (l *LocalWorker) GenerateWindowPoStAdv(ctx context.Context, ppt abi.Registe return storiface.WindowPoStResult{Skipped: skipped}, nil } + // compact skipped sectors + var skippedSoFar int + for i := range vproofs { + if len(vproofs[i]) == 0 { + skippedSoFar++ + continue + } + + if skippedSoFar > 0 { + vproofs[i-skippedSoFar] = vproofs[i] + } + } + + vproofs = vproofs[:len(vproofs)-skippedSoFar] + + // compute the PoSt! res, err := sb.GenerateWindowPoStWithVanilla(ctx, ppt, mid, randomness, vproofs, partitionIdx) r := storiface.WindowPoStResult{ PoStProofs: res, From fc8e4c54f8479e15e5fed784c1db18a525a5cf30 Mon Sep 17 00:00:00 2001 From: "Andrew Jackson (Ajax)" Date: Thu, 4 Jan 2024 09:47:56 -0600 Subject: [PATCH 061/394] docsgen --- documentation/en/cli-lotus-provider.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/documentation/en/cli-lotus-provider.md b/documentation/en/cli-lotus-provider.md index a01cd0856..5e5864107 100644 --- a/documentation/en/cli-lotus-provider.md +++ b/documentation/en/cli-lotus-provider.md @@ -261,8 +261,10 @@ DESCRIPTION: This creates the 'web' layer if it does not exist, then calls run with that layer. OPTIONS: - --listen value Address to listen on (default: "127.0.0.1:4701") - --help, -h show help + --listen value Address to listen on (default: "127.0.0.1:4701") + --layers value [ --layers value ] list of layers to be interpreted (atop defaults). Default: base. Web will be added (default: "base") + --nosync don't check full-node sync status (default: false) + --help, -h show help ``` ## lotus-provider version From 156a3a400cbff6cd2cf95e0a0c71038241a04e59 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Sat, 6 Jan 2024 15:42:52 -0800 Subject: [PATCH 062/394] fix: Eth API: accept input data in call arguments under field 'input' (#11505) The correct name for this field is 'input' according to the Ethereum specs [0]. However, for the longest time, clients have been using 'data' and servers have been lenient to accept both, preferring 'input' over 'data' when both appear. Our lack of support for 'input' had gone unnoticed until go-ethereum decided to adjust their ethclient implementation to issue eth_call and eth_estimateGas requests with the 'input' field instead of 'data' [1]. This suddenly broke apps using this client against Lotus' Eth API. [0]: https://github.com/ethereum/execution-apis/blob/main/src/schemas/transaction.yaml#L33-L35 [1]: ethereum/go-ethereum#28078 Co-authored-by: raulk --- chain/types/ethtypes/eth_types.go | 18 ++++++++++++--- chain/types/ethtypes/eth_types_test.go | 31 +++++++++++++++++++++++++- 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/chain/types/ethtypes/eth_types.go b/chain/types/ethtypes/eth_types.go index b933329f4..bff15ed24 100644 --- a/chain/types/ethtypes/eth_types.go +++ b/chain/types/ethtypes/eth_types.go @@ -229,13 +229,25 @@ type EthCall struct { } func (c *EthCall) UnmarshalJSON(b []byte) error { - type TempEthCall EthCall - var params TempEthCall + type EthCallRaw EthCall // Avoid a recursive call. + type EthCallDecode struct { + // The field should be "input" by spec, but many clients use "data" so we support + // both, but prefer "input". + Input *EthBytes `json:"input"` + EthCallRaw + } + var params EthCallDecode if err := json.Unmarshal(b, ¶ms); err != nil { return err } - *c = EthCall(params) + + // If input is specified, prefer it. + if params.Input != nil { + params.Data = *params.Input + } + + *c = EthCall(params.EthCallRaw) return nil } diff --git a/chain/types/ethtypes/eth_types_test.go b/chain/types/ethtypes/eth_types_test.go index 4a73184c2..2a1b2df55 100644 --- a/chain/types/ethtypes/eth_types_test.go +++ b/chain/types/ethtypes/eth_types_test.go @@ -194,11 +194,40 @@ func TestMaskedIDInF4(t *testing.T) { } func TestUnmarshalEthCall(t *testing.T) { - data := `{"from":"0x4D6D86b31a112a05A473c4aE84afaF873f632325","to":"0xFe01CC39f5Ae8553D6914DBb9dC27D219fa22D7f","gas":"0x5","gasPrice":"0x6","value":"0x123","data":""}` + data := `{"from":"0x4D6D86b31a112a05A473c4aE84afaF873f632325","to":"0xFe01CC39f5Ae8553D6914DBb9dC27D219fa22D7f","gas":"0x5","gasPrice":"0x6","value":"0x123","data":"0xFF"}` var c EthCall err := c.UnmarshalJSON([]byte(data)) require.Nil(t, err) + require.EqualValues(t, []byte{0xff}, c.Data) +} + +func TestUnmarshalEthCallInput(t *testing.T) { + data := `{"from":"0x4D6D86b31a112a05A473c4aE84afaF873f632325","to":"0xFe01CC39f5Ae8553D6914DBb9dC27D219fa22D7f","gas":"0x5","gasPrice":"0x6","value":"0x123","input":"0xFF"}` + + var c EthCall + err := c.UnmarshalJSON([]byte(data)) + require.Nil(t, err) + require.EqualValues(t, []byte{0xff}, c.Data) +} + +func TestUnmarshalEthCallInputAndData(t *testing.T) { + data := `{"from":"0x4D6D86b31a112a05A473c4aE84afaF873f632325","to":"0xFe01CC39f5Ae8553D6914DBb9dC27D219fa22D7f","gas":"0x5","gasPrice":"0x6","value":"0x123","data":"0xFE","input":"0xFF"}` + + var c EthCall + err := c.UnmarshalJSON([]byte(data)) + require.Nil(t, err) + require.EqualValues(t, []byte{0xff}, c.Data) +} + +func TestUnmarshalEthCallInputAndDataEmpty(t *testing.T) { + // Even if the input is empty, it should be used when specified. + data := `{"from":"0x4D6D86b31a112a05A473c4aE84afaF873f632325","to":"0xFe01CC39f5Ae8553D6914DBb9dC27D219fa22D7f","gas":"0x5","gasPrice":"0x6","value":"0x123","data":"0xFE","input":""}` + + var c EthCall + err := c.UnmarshalJSON([]byte(data)) + require.Nil(t, err) + require.EqualValues(t, []byte{}, c.Data) } func TestUnmarshalEthBytes(t *testing.T) { From b93e89c401f83544e930ae40143b9ddcd3fc4874 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 8 Jan 2024 14:55:15 -0800 Subject: [PATCH 063/394] chore: mod: use upstream poseidon The upstream PR was merged https://github.com/triplewz/poseidon/pull/1. --- go.mod | 4 +--- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index 11a4a5d22..457968e84 100644 --- a/go.mod +++ b/go.mod @@ -142,7 +142,7 @@ require ( github.com/samber/lo v1.38.1 github.com/stretchr/testify v1.8.4 github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 - github.com/triplewz/poseidon v0.0.0-20220525065023-a7cdb0e183e7 + github.com/triplewz/poseidon v0.0.0-20230828015038-79d8165c88ed github.com/urfave/cli/v2 v2.25.5 github.com/whyrusleeping/bencher v0.0.0-20190829221104-bb6607aa8bba github.com/whyrusleeping/cbor-gen v0.0.0-20230923211252-36a87e1ba72f @@ -346,5 +346,3 @@ require ( replace github.com/filecoin-project/filecoin-ffi => ./extern/filecoin-ffi replace github.com/filecoin-project/test-vectors => ./extern/test-vectors - -replace github.com/triplewz/poseidon => github.com/magik6k/poseidon v0.0.0-neptune diff --git a/go.sum b/go.sum index b0f13f5bb..97abc94c9 100644 --- a/go.sum +++ b/go.sum @@ -1178,8 +1178,6 @@ github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0Q github.com/magefile/mage v1.9.0 h1:t3AU2wNwehMCW97vuqQLtw6puppWXHO+O2MHo5a50XE= github.com/magefile/mage v1.9.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/magik6k/poseidon v0.0.0-neptune h1:Dfz15iiYGGE9Esvn8pZFlbiiCrHuyZDxm6LGXQfaf9c= -github.com/magik6k/poseidon v0.0.0-neptune/go.mod h1:QYG1d0B4YZD7TgF6qZndTTu4rxUGFCCZAQRDanDj+9c= github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= @@ -1608,6 +1606,8 @@ github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhV github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/triplewz/poseidon v0.0.0-20230828015038-79d8165c88ed h1:C8H2ql+vCBhEi7d3vMBBbdCAKv9s/thfPyLEuSvFpMU= +github.com/triplewz/poseidon v0.0.0-20230828015038-79d8165c88ed/go.mod h1:QYG1d0B4YZD7TgF6qZndTTu4rxUGFCCZAQRDanDj+9c= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= github.com/twmb/murmur3 v1.1.6 h1:mqrRot1BRxm+Yct+vavLMou2/iJt0tNVTTC0QoIjaZg= github.com/twmb/murmur3 v1.1.6/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= From 5621c1a193cc1598ad59181a9aaa3a706b671956 Mon Sep 17 00:00:00 2001 From: jennijuju Date: Tue, 9 Jan 2024 19:27:39 +0800 Subject: [PATCH 064/394] update go multi address --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 11a4a5d22..2200716e8 100644 --- a/go.mod +++ b/go.mod @@ -125,7 +125,7 @@ require ( github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 github.com/mitchellh/go-homedir v1.1.0 github.com/multiformats/go-base32 v0.1.0 - github.com/multiformats/go-multiaddr v0.11.0 + github.com/multiformats/go-multiaddr v0.12.0 github.com/multiformats/go-multiaddr-dns v0.3.1 github.com/multiformats/go-multibase v0.2.0 github.com/multiformats/go-multicodec v0.9.0 diff --git a/go.sum b/go.sum index b0f13f5bb..d69c610b3 100644 --- a/go.sum +++ b/go.sum @@ -1282,8 +1282,8 @@ github.com/multiformats/go-multiaddr v0.2.2/go.mod h1:NtfXiOtHvghW9KojvtySjH5y0u github.com/multiformats/go-multiaddr v0.3.0/go.mod h1:dF9kph9wfJ+3VLAaeBqo9Of8x4fJxp6ggJGteB8HQTI= github.com/multiformats/go-multiaddr v0.3.1/go.mod h1:uPbspcUPd5AfaP6ql3ujFY+QWzmBD8uLLL4bXW0XfGc= github.com/multiformats/go-multiaddr v0.3.3/go.mod h1:lCKNGP1EQ1eZ35Za2wlqnabm9xQkib3fyB+nZXHLag0= -github.com/multiformats/go-multiaddr v0.11.0 h1:XqGyJ8ufbCE0HmTDwx2kPdsrQ36AGPZNZX6s6xfJH10= -github.com/multiformats/go-multiaddr v0.11.0/go.mod h1:gWUm0QLR4thQ6+ZF6SXUw8YjtwQSPapICM+NmCkxHSM= +github.com/multiformats/go-multiaddr v0.12.0 h1:1QlibTFkoXJuDjjYsMHhE73TnzJQl8FSWatk/0gxGzE= +github.com/multiformats/go-multiaddr v0.12.0/go.mod h1:WmZXgObOQOYp9r3cslLlppkrz1FYSHmE834dfz/lWu8= github.com/multiformats/go-multiaddr-dns v0.0.1/go.mod h1:9kWcqw/Pj6FwxAwW38n/9403szc57zJPs45fmnznu3Q= github.com/multiformats/go-multiaddr-dns v0.0.2/go.mod h1:9kWcqw/Pj6FwxAwW38n/9403szc57zJPs45fmnznu3Q= github.com/multiformats/go-multiaddr-dns v0.2.0/go.mod h1:TJ5pr5bBO7Y1B18djPuRsVkduhQH2YqYSbxWJzYGdK0= From 06f0e6beb8e7b552da646f3d40cec6e767fd3200 Mon Sep 17 00:00:00 2001 From: "Andrew Jackson (Ajax)" Date: Tue, 9 Jan 2024 10:28:57 -0600 Subject: [PATCH 065/394] fix: docker --- Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Makefile b/Makefile index c3c46f71e..a17b50d09 100644 --- a/Makefile +++ b/Makefile @@ -100,6 +100,8 @@ BINS+=lotus-miner lotus-provider: $(BUILD_DEPS) rm -f lotus-provider $(GOCC) build $(GOFLAGS) -o lotus-provider ./cmd/lotus-provider +.PHONY: lotus-provider +BINS+=lotus-provider lp2k: GOFLAGS+=-tags=2k lp2k: lotus-provider From aeed4cba6bb041c286627d0a58d713c80f94310e Mon Sep 17 00:00:00 2001 From: jennijuju Date: Wed, 10 Jan 2024 21:15:45 +0800 Subject: [PATCH 066/394] update to the latest go multiaddress release --- go.mod | 10 +++++----- go.sum | 20 ++++++++++---------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/go.mod b/go.mod index b30d87f62..c1b353fab 100644 --- a/go.mod +++ b/go.mod @@ -119,7 +119,7 @@ require ( github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 github.com/mitchellh/go-homedir v1.1.0 github.com/multiformats/go-base32 v0.1.0 - github.com/multiformats/go-multiaddr v0.12.0 + github.com/multiformats/go-multiaddr v0.12.1 github.com/multiformats/go-multiaddr-dns v0.3.1 github.com/multiformats/go-multibase v0.2.0 github.com/multiformats/go-multicodec v0.9.0 @@ -154,11 +154,11 @@ require ( go.uber.org/fx v1.20.0 go.uber.org/multierr v1.11.0 go.uber.org/zap v1.25.0 - golang.org/x/crypto v0.12.0 + golang.org/x/crypto v0.17.0 golang.org/x/net v0.14.0 golang.org/x/sync v0.3.0 - golang.org/x/sys v0.11.0 - golang.org/x/term v0.11.0 + golang.org/x/sys v0.15.0 + golang.org/x/term v0.15.0 golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846 golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 @@ -321,7 +321,7 @@ require ( go4.org v0.0.0-20230225012048-214862532bf5 // indirect golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 // indirect golang.org/x/mod v0.12.0 // indirect - golang.org/x/text v0.12.0 // indirect + golang.org/x/text v0.14.0 // indirect gonum.org/v1/gonum v0.13.0 // indirect google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 // indirect google.golang.org/grpc v1.55.0 // indirect diff --git a/go.sum b/go.sum index e1958bc8f..a7983d80c 100644 --- a/go.sum +++ b/go.sum @@ -1249,8 +1249,8 @@ github.com/multiformats/go-multiaddr v0.2.2/go.mod h1:NtfXiOtHvghW9KojvtySjH5y0u github.com/multiformats/go-multiaddr v0.3.0/go.mod h1:dF9kph9wfJ+3VLAaeBqo9Of8x4fJxp6ggJGteB8HQTI= github.com/multiformats/go-multiaddr v0.3.1/go.mod h1:uPbspcUPd5AfaP6ql3ujFY+QWzmBD8uLLL4bXW0XfGc= github.com/multiformats/go-multiaddr v0.3.3/go.mod h1:lCKNGP1EQ1eZ35Za2wlqnabm9xQkib3fyB+nZXHLag0= -github.com/multiformats/go-multiaddr v0.12.0 h1:1QlibTFkoXJuDjjYsMHhE73TnzJQl8FSWatk/0gxGzE= -github.com/multiformats/go-multiaddr v0.12.0/go.mod h1:WmZXgObOQOYp9r3cslLlppkrz1FYSHmE834dfz/lWu8= +github.com/multiformats/go-multiaddr v0.12.1 h1:vm+BA/WZA8QZDp1pF1FWhi5CT3g1tbi5GJmqpb6wnlk= +github.com/multiformats/go-multiaddr v0.12.1/go.mod h1:7mPkiBMmLeFipt+nNSq9pHZUeJSt8lHBgH6yhj0YQzE= github.com/multiformats/go-multiaddr-dns v0.0.1/go.mod h1:9kWcqw/Pj6FwxAwW38n/9403szc57zJPs45fmnznu3Q= github.com/multiformats/go-multiaddr-dns v0.0.2/go.mod h1:9kWcqw/Pj6FwxAwW38n/9403szc57zJPs45fmnznu3Q= github.com/multiformats/go-multiaddr-dns v0.2.0/go.mod h1:TJ5pr5bBO7Y1B18djPuRsVkduhQH2YqYSbxWJzYGdK0= @@ -1779,8 +1779,8 @@ golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= -golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk= -golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= +golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= +golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -2014,16 +2014,16 @@ golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= -golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.11.0 h1:F9tnn/DA/Im8nCwm+fX+1/eBwi4qFjRT++MhtVC4ZX0= -golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= +golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= +golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -2033,8 +2033,8 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc= -golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= From 170b9844efc6d6a2f1fa4c1e6f2fa751a081b594 Mon Sep 17 00:00:00 2001 From: Aayush Date: Mon, 8 Jan 2024 15:42:10 -0500 Subject: [PATCH 067/394] feat: exchange: change GetBlocks to always fetch the requested number of tipsets --- chain/exchange/client.go | 56 +++++++++++++++++++++++++----------- chain/exchange/interfaces.go | 4 +-- 2 files changed, 42 insertions(+), 18 deletions(-) diff --git a/chain/exchange/client.go b/chain/exchange/client.go index 769c375ca..9cbb44955 100644 --- a/chain/exchange/client.go +++ b/chain/exchange/client.go @@ -284,16 +284,18 @@ func (c *client) validateCompressedIndices(chain []*BSTipSet) error { len(msgs.SecpkIncludes), blocksNum) } + blsLen := uint64(len(msgs.Bls)) + secpLen := uint64(len(msgs.Secpk)) for blockIdx := 0; blockIdx < blocksNum; blockIdx++ { for _, mi := range msgs.BlsIncludes[blockIdx] { - if int(mi) >= len(msgs.Bls) { + if mi >= blsLen { return xerrors.Errorf("index in BlsIncludes (%d) exceeds number of messages (%d)", mi, len(msgs.Bls)) } } for _, mi := range msgs.SecpkIncludes[blockIdx] { - if int(mi) >= len(msgs.Secpk) { + if mi >= secpLen { return xerrors.Errorf("index in SecpkIncludes (%d) exceeds number of messages (%d)", mi, len(msgs.Secpk)) } @@ -315,18 +317,36 @@ func (c *client) GetBlocks(ctx context.Context, tsk types.TipSetKey, count int) ) } - req := &Request{ - Head: tsk.Cids(), - Length: uint64(count), - Options: Headers, + var ret []*types.TipSet + start := tsk.Cids() + for len(ret) < count { + req := &Request{ + Head: start, + Length: uint64(count - len(ret)), + Options: Headers, + } + + validRes, err := c.doRequest(ctx, req, nil, nil) + if err != nil { + return nil, xerrors.Errorf("failed to doRequest: %w", err) + } + + if len(validRes.tipsets) == 0 { + return nil, xerrors.Errorf("doRequest fetched zero tipsets: %w", err) + } + + ret = append(ret, validRes.tipsets...) + + last := validRes.tipsets[len(validRes.tipsets)-1] + if last.Height() <= 1 { + // we've walked all the way up to genesis, return + break + } + + start = last.Parents().Cids() } - validRes, err := c.doRequest(ctx, req, nil, nil) - if err != nil { - return nil, err - } - - return validRes.tipsets, nil + return ret, nil } // GetFullTipSet implements Client.GetFullTipSet(). Refer to the godocs there. @@ -341,12 +361,16 @@ func (c *client) GetFullTipSet(ctx context.Context, peer peer.ID, tsk types.TipS validRes, err := c.doRequest(ctx, req, &peer, nil) if err != nil { - return nil, err + return nil, xerrors.Errorf("failed to doRequest: %w", err) } - return validRes.toFullTipSets()[0], nil - // If `doRequest` didn't fail we are guaranteed to have at least - // *one* tipset here, so it's safe to index directly. + fullTipsets := validRes.toFullTipSets() + + if len(fullTipsets) == 0 { + return nil, xerrors.New("unexpectedly got no tipsets in exchange") + } + + return fullTipsets[0], nil } // GetChainMessages implements Client.GetChainMessages(). Refer to the godocs there. diff --git a/chain/exchange/interfaces.go b/chain/exchange/interfaces.go index c95127929..ff11b63eb 100644 --- a/chain/exchange/interfaces.go +++ b/chain/exchange/interfaces.go @@ -28,8 +28,8 @@ type Server interface { // used by the Syncer. type Client interface { // GetBlocks fetches block headers from the network, from the provided - // tipset *backwards*, returning as many tipsets as the count parameter, - // or less. + // tipset *backwards*, returning as many tipsets as the count parameter. + // The ONLY case in which we return fewer than `count` tipsets is if we hit genesis. GetBlocks(ctx context.Context, tsk types.TipSetKey, count int) ([]*types.TipSet, error) // GetChainMessages fetches messages from the network, starting from the first provided tipset From c86f5a1b2b3122c7c8d67775e9b2b3127c84661e Mon Sep 17 00:00:00 2001 From: Aayush Date: Wed, 10 Jan 2024 17:50:33 -0500 Subject: [PATCH 068/394] fix: chain: use latest go-state-types types for miner UI --- api/docgen/docgen.go | 2 ++ api/mocks/mock_full.go | 21 ++++++++++--------- api/v0api/v0mocks/mock_full.go | 21 ++++++++++--------- build/openrpc/full.json.gz | Bin 34608 -> 34601 bytes build/openrpc/gateway.json.gz | Bin 11872 -> 11870 bytes chain/actors/builtin/miner/actor.go.template | 3 ++- chain/actors/builtin/miner/miner.go | 3 ++- documentation/en/api-v0-methods.md | 12 +++++------ documentation/en/api-v1-unstable-methods.md | 12 +++++------ storage/pipeline/mocks/api.go | 17 ++++++++------- storage/wdpost/wdpost_run_test.go | 7 ++++--- 11 files changed, 53 insertions(+), 45 deletions(-) diff --git a/api/docgen/docgen.go b/api/docgen/docgen.go index b31c25b86..181efeb30 100644 --- a/api/docgen/docgen.go +++ b/api/docgen/docgen.go @@ -32,6 +32,7 @@ import ( "github.com/filecoin-project/go-fil-markets/retrievalmarket" "github.com/filecoin-project/go-jsonrpc/auth" "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/go-state-types/builtin/v12/miner" "github.com/filecoin-project/go-state-types/builtin/v9/verifreg" "github.com/filecoin-project/go-state-types/crypto" "github.com/filecoin-project/go-state-types/exitcode" @@ -145,6 +146,7 @@ func init() { allocationId := verifreg.AllocationId(0) addExample(allocationId) addExample(&allocationId) + addExample(miner.SectorOnChainInfoFlags(0)) addExample(map[verifreg.AllocationId]verifreg.Allocation{}) claimId := verifreg.ClaimId(0) addExample(claimId) diff --git a/api/mocks/mock_full.go b/api/mocks/mock_full.go index ed9fe740e..f4a9832bb 100644 --- a/api/mocks/mock_full.go +++ b/api/mocks/mock_full.go @@ -27,8 +27,9 @@ import ( auth "github.com/filecoin-project/go-jsonrpc/auth" abi "github.com/filecoin-project/go-state-types/abi" big "github.com/filecoin-project/go-state-types/big" + miner "github.com/filecoin-project/go-state-types/builtin/v12/miner" paych "github.com/filecoin-project/go-state-types/builtin/v8/paych" - miner "github.com/filecoin-project/go-state-types/builtin/v9/miner" + miner0 "github.com/filecoin-project/go-state-types/builtin/v9/miner" verifreg "github.com/filecoin-project/go-state-types/builtin/v9/verifreg" crypto "github.com/filecoin-project/go-state-types/crypto" dline "github.com/filecoin-project/go-state-types/dline" @@ -36,7 +37,7 @@ import ( api "github.com/filecoin-project/lotus/api" apitypes "github.com/filecoin-project/lotus/api/types" - miner0 "github.com/filecoin-project/lotus/chain/actors/builtin/miner" + miner1 "github.com/filecoin-project/lotus/chain/actors/builtin/miner" types "github.com/filecoin-project/lotus/chain/types" ethtypes "github.com/filecoin-project/lotus/chain/types/ethtypes" alerting "github.com/filecoin-project/lotus/journal/alerting" @@ -3549,7 +3550,7 @@ func (mr *MockFullNodeMockRecorder) StateMinerInfo(arg0, arg1, arg2 interface{}) } // StateMinerInitialPledgeCollateral mocks base method. -func (m *MockFullNode) StateMinerInitialPledgeCollateral(arg0 context.Context, arg1 address.Address, arg2 miner.SectorPreCommitInfo, arg3 types.TipSetKey) (big.Int, error) { +func (m *MockFullNode) StateMinerInitialPledgeCollateral(arg0 context.Context, arg1 address.Address, arg2 miner0.SectorPreCommitInfo, arg3 types.TipSetKey) (big.Int, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "StateMinerInitialPledgeCollateral", arg0, arg1, arg2, arg3) ret0, _ := ret[0].(big.Int) @@ -3594,7 +3595,7 @@ func (mr *MockFullNodeMockRecorder) StateMinerPower(arg0, arg1, arg2 interface{} } // StateMinerPreCommitDepositForPower mocks base method. -func (m *MockFullNode) StateMinerPreCommitDepositForPower(arg0 context.Context, arg1 address.Address, arg2 miner.SectorPreCommitInfo, arg3 types.TipSetKey) (big.Int, error) { +func (m *MockFullNode) StateMinerPreCommitDepositForPower(arg0 context.Context, arg1 address.Address, arg2 miner0.SectorPreCommitInfo, arg3 types.TipSetKey) (big.Int, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "StateMinerPreCommitDepositForPower", arg0, arg1, arg2, arg3) ret0, _ := ret[0].(big.Int) @@ -3759,10 +3760,10 @@ func (mr *MockFullNodeMockRecorder) StateSearchMsg(arg0, arg1, arg2, arg3, arg4 } // StateSectorExpiration mocks base method. -func (m *MockFullNode) StateSectorExpiration(arg0 context.Context, arg1 address.Address, arg2 abi.SectorNumber, arg3 types.TipSetKey) (*miner0.SectorExpiration, error) { +func (m *MockFullNode) StateSectorExpiration(arg0 context.Context, arg1 address.Address, arg2 abi.SectorNumber, arg3 types.TipSetKey) (*miner1.SectorExpiration, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "StateSectorExpiration", arg0, arg1, arg2, arg3) - ret0, _ := ret[0].(*miner0.SectorExpiration) + ret0, _ := ret[0].(*miner1.SectorExpiration) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -3789,10 +3790,10 @@ func (mr *MockFullNodeMockRecorder) StateSectorGetInfo(arg0, arg1, arg2, arg3 in } // StateSectorPartition mocks base method. -func (m *MockFullNode) StateSectorPartition(arg0 context.Context, arg1 address.Address, arg2 abi.SectorNumber, arg3 types.TipSetKey) (*miner0.SectorLocation, error) { +func (m *MockFullNode) StateSectorPartition(arg0 context.Context, arg1 address.Address, arg2 abi.SectorNumber, arg3 types.TipSetKey) (*miner1.SectorLocation, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "StateSectorPartition", arg0, arg1, arg2, arg3) - ret0, _ := ret[0].(*miner0.SectorLocation) + ret0, _ := ret[0].(*miner1.SectorLocation) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -3804,10 +3805,10 @@ func (mr *MockFullNodeMockRecorder) StateSectorPartition(arg0, arg1, arg2, arg3 } // StateSectorPreCommitInfo mocks base method. -func (m *MockFullNode) StateSectorPreCommitInfo(arg0 context.Context, arg1 address.Address, arg2 abi.SectorNumber, arg3 types.TipSetKey) (*miner.SectorPreCommitOnChainInfo, error) { +func (m *MockFullNode) StateSectorPreCommitInfo(arg0 context.Context, arg1 address.Address, arg2 abi.SectorNumber, arg3 types.TipSetKey) (*miner0.SectorPreCommitOnChainInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "StateSectorPreCommitInfo", arg0, arg1, arg2, arg3) - ret0, _ := ret[0].(*miner.SectorPreCommitOnChainInfo) + ret0, _ := ret[0].(*miner0.SectorPreCommitOnChainInfo) ret1, _ := ret[1].(error) return ret0, ret1 } diff --git a/api/v0api/v0mocks/mock_full.go b/api/v0api/v0mocks/mock_full.go index 7a722ed25..151b1dbc1 100644 --- a/api/v0api/v0mocks/mock_full.go +++ b/api/v0api/v0mocks/mock_full.go @@ -26,8 +26,9 @@ import ( auth "github.com/filecoin-project/go-jsonrpc/auth" abi "github.com/filecoin-project/go-state-types/abi" big "github.com/filecoin-project/go-state-types/big" + miner "github.com/filecoin-project/go-state-types/builtin/v12/miner" paych "github.com/filecoin-project/go-state-types/builtin/v8/paych" - miner "github.com/filecoin-project/go-state-types/builtin/v9/miner" + miner0 "github.com/filecoin-project/go-state-types/builtin/v9/miner" verifreg "github.com/filecoin-project/go-state-types/builtin/v9/verifreg" crypto "github.com/filecoin-project/go-state-types/crypto" dline "github.com/filecoin-project/go-state-types/dline" @@ -36,7 +37,7 @@ import ( api "github.com/filecoin-project/lotus/api" apitypes "github.com/filecoin-project/lotus/api/types" v0api "github.com/filecoin-project/lotus/api/v0api" - miner0 "github.com/filecoin-project/lotus/chain/actors/builtin/miner" + miner1 "github.com/filecoin-project/lotus/chain/actors/builtin/miner" types "github.com/filecoin-project/lotus/chain/types" alerting "github.com/filecoin-project/lotus/journal/alerting" marketevents "github.com/filecoin-project/lotus/markets/loggers" @@ -2669,7 +2670,7 @@ func (mr *MockFullNodeMockRecorder) StateMinerInfo(arg0, arg1, arg2 interface{}) } // StateMinerInitialPledgeCollateral mocks base method. -func (m *MockFullNode) StateMinerInitialPledgeCollateral(arg0 context.Context, arg1 address.Address, arg2 miner.SectorPreCommitInfo, arg3 types.TipSetKey) (big.Int, error) { +func (m *MockFullNode) StateMinerInitialPledgeCollateral(arg0 context.Context, arg1 address.Address, arg2 miner0.SectorPreCommitInfo, arg3 types.TipSetKey) (big.Int, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "StateMinerInitialPledgeCollateral", arg0, arg1, arg2, arg3) ret0, _ := ret[0].(big.Int) @@ -2714,7 +2715,7 @@ func (mr *MockFullNodeMockRecorder) StateMinerPower(arg0, arg1, arg2 interface{} } // StateMinerPreCommitDepositForPower mocks base method. -func (m *MockFullNode) StateMinerPreCommitDepositForPower(arg0 context.Context, arg1 address.Address, arg2 miner.SectorPreCommitInfo, arg3 types.TipSetKey) (big.Int, error) { +func (m *MockFullNode) StateMinerPreCommitDepositForPower(arg0 context.Context, arg1 address.Address, arg2 miner0.SectorPreCommitInfo, arg3 types.TipSetKey) (big.Int, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "StateMinerPreCommitDepositForPower", arg0, arg1, arg2, arg3) ret0, _ := ret[0].(big.Int) @@ -2894,10 +2895,10 @@ func (mr *MockFullNodeMockRecorder) StateSearchMsgLimited(arg0, arg1, arg2 inter } // StateSectorExpiration mocks base method. -func (m *MockFullNode) StateSectorExpiration(arg0 context.Context, arg1 address.Address, arg2 abi.SectorNumber, arg3 types.TipSetKey) (*miner0.SectorExpiration, error) { +func (m *MockFullNode) StateSectorExpiration(arg0 context.Context, arg1 address.Address, arg2 abi.SectorNumber, arg3 types.TipSetKey) (*miner1.SectorExpiration, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "StateSectorExpiration", arg0, arg1, arg2, arg3) - ret0, _ := ret[0].(*miner0.SectorExpiration) + ret0, _ := ret[0].(*miner1.SectorExpiration) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -2924,10 +2925,10 @@ func (mr *MockFullNodeMockRecorder) StateSectorGetInfo(arg0, arg1, arg2, arg3 in } // StateSectorPartition mocks base method. -func (m *MockFullNode) StateSectorPartition(arg0 context.Context, arg1 address.Address, arg2 abi.SectorNumber, arg3 types.TipSetKey) (*miner0.SectorLocation, error) { +func (m *MockFullNode) StateSectorPartition(arg0 context.Context, arg1 address.Address, arg2 abi.SectorNumber, arg3 types.TipSetKey) (*miner1.SectorLocation, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "StateSectorPartition", arg0, arg1, arg2, arg3) - ret0, _ := ret[0].(*miner0.SectorLocation) + ret0, _ := ret[0].(*miner1.SectorLocation) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -2939,10 +2940,10 @@ func (mr *MockFullNodeMockRecorder) StateSectorPartition(arg0, arg1, arg2, arg3 } // StateSectorPreCommitInfo mocks base method. -func (m *MockFullNode) StateSectorPreCommitInfo(arg0 context.Context, arg1 address.Address, arg2 abi.SectorNumber, arg3 types.TipSetKey) (miner.SectorPreCommitOnChainInfo, error) { +func (m *MockFullNode) StateSectorPreCommitInfo(arg0 context.Context, arg1 address.Address, arg2 abi.SectorNumber, arg3 types.TipSetKey) (miner0.SectorPreCommitOnChainInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "StateSectorPreCommitInfo", arg0, arg1, arg2, arg3) - ret0, _ := ret[0].(miner.SectorPreCommitOnChainInfo) + ret0, _ := ret[0].(miner0.SectorPreCommitOnChainInfo) ret1, _ := ret[1].(error) return ret0, ret1 } diff --git a/build/openrpc/full.json.gz b/build/openrpc/full.json.gz index 3c30e81a9427867a286d476b5eb52110c995bcae..0b0acf6dae7a7a7e8d64017a070186a9a62d8b6b 100644 GIT binary patch delta 7650 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 Q2LJ&7|3;wZ-81n90P!6V7XSbN delta 7657 zcmV#BbZA+ps5n7PJK6yC` zbJe%Giiy~kgf+|E)W%dyxE4?4h~EjLxTxCHHuB9aD_d^4mG$*%(X)QI(lfgWb6Y9tdLLX^PJM zttJ#2wbt(-j^Hpt>qYceky=lXswYQy{E2^XM-f_RzriYC6K`t=DEHP`z7`qH-Jznf zbX3t1fJ$!u!c0_vvJC`~fFY8gHDL@F1kf0;IU;?Ke=k~BiB@~!*9>I((&3wLy{L;( z`#Vh_A#!P-AQ(oNw1xG`(^%KSY6!WB&$QV_YpN;LQfk_La}}2vV>Wn~l$FXB3c`Q1 z#E8b$l_)7bd#Tt<#g&FqGsh|nqt+{@5xk^~Unj#EW(-pz&69)eZ-b2!%;pIf+rBh+ z9MgpGjl;d2_9nP$ica3sB9Fb%p&;sao8VWZSb7hWsCC$EMqoW5-_qKknr;yECAux; zecDG?VXrx>Fg)mZEs4)KL0}{bE>nN6S?(%qFZX-7?;`hC+ZkG#8HB`tQJV2y6Htp> z*p+TacVQ{2uL%`k$he6}QL+glz?5kz7xX~t|M}y8{_h?7_}_f~VgDVAKmVVj;OqUzqxaW4AK5AWaC0A@pWXfO-+Z(&*pQf} zVq(|k$oPs$NEes%ny0z2bpV45Z<7IvyL-E@58mv)-Q9nis{mszzn+P-I^5flU1f-{ zgh|!rG=g&`zE0X95@Irz<7j^tnl(;Gh@cS;u=);Fk8e;cSa|GRZy|Ws)`lYxo?jP%im=- zWddaVNnI&9y#u)|sqWH&CSmVcKigfLDz==>6%6+UI%3|QyD zQc2P03}Z)RUdHH{&Srm@s}sz8w-c9X4U-S+IjtWV$NV0RtlPd+tjd77EChnAl<^a= zISOP1R_bb12BjBzh^n1&j>myw%x^_VaK}Nwg5(t}UOOv=23P2*p9xd!;IJ!pJhj*# z782{bO}B{2w7$U)3FGt$#4LPipXNG9R8!8m(wi#!fQ28U`0Rf-g8vC&pQ^Fzc(O7g zKI%<^3E$tV(PjSnmJ)SSWilQb;}_CmEtQvCSAFyqi$6PzVV*rU`4Ll<#Dq1xP-R5T zj|W3h*y{pAp%?D!#a*PUq9X9Ze&;lX@1iI@jO(BaY4BPWpI)JW-XXEW7vG#&_hikp z{Owuwbj^EH$TEK#_?_qNKKj-z42Czz&pLcp40#`OQrNBEG$OXiO+;QLT4SRY_1C=M zk`Z#;m}SzgZf~`iez>|%6%Y9^Yw<)!jr1PWe&_N<|EXV&`FAd66RX1EzZ+L@akS(} z|CXM%|0FQNeEBZ?f7w%+c$LGbMZJ%Zonw-_2JB_H$kl(ZaxLUL$V!~#E)lW=cCf2olhT;~cE1K|cfCTD<4Q8h!H?x@;R%_&5(=1cq6HB-pB zW@*N{*UZo87Z;oPW@{z7mvJ4f1*(t{d6WycS@S>O(rvD@`Vu6y6K!roRXbF^|59b zxeF#Jm@J#(b`-0U8ED*?vV&)3r;Q%@CsdCdil^%D3zLh7D!(jIpmzA|M`!QheZ0BjR{ zQs$5mjyQ@fI|@@?s|-4w0T?5INf0IB%T@TF8lHEr-S;VofBFFO4N3RV3#@N1}44y)Ws9os6~pkkK^`>WP@?i;je zo28slMLf{%L!!#4ziEhsC}v9%pb&8Cr5jgiBjxUvK3HWD2hCZ|qYZ1?>t*|M&-?u* z;3PdpUr@}de=hL)OnptG?x0#eeZo1;eJD-9by?`*4J)Ijf!50->!Ik;>Q!Pm%q<>Bahn$+ZQa+N`mU|fI*3Nco@75EQM{k<&Os)_NRl4(5qA0Dt zW~lQ!od%t+-lWqeyNhqOd*R^i$T-O3rkVTDP;}-Of2LCRy;S2=FAF_lkhAGuHuB}( zx%eCH8Z}Yq_`@VVG1dJgM{mZs8 zU(|I}JEg!6aJYGlLm}utkvke($pzpb=|w|bih0erlZ=;b$MPScD`=MmVhoPa06@l) z875qq{mii);Z+jTgU|PneQtIaBEx9G1MgZ#HV2YMPACaE#5QPKj2ZyTdK(wJ- zm5va~!H6a#1eAba0x=of64A;G@d*vV0!QZFFMvdiFko=0f>5>cYv?T?Bfn^RnkhnS zxe_(uT&(LDi_K4$!wf`}PEiPwxndoU3`=PT}x$5qTX90{N6oUmMoB>V& zA}on@nn-jH10F4PQ-3FjNq`fpBj_Fl2}hwC_9Zy~e0`%r)D(pZzAyvm?*vAou`~ml zf0!%BINZD?UooF3#~5as(vb2=fd?_LG{$30V3aa6gG=#A5R3t2w}d4@fEXJkQM61M zO$T}a784vyWF_&%K!m3VKoHVo7@=2ejz}oy=DX^t2dyu~C!!)AjZiG4@$sh<@#&X< zj=%@Vz#WVd#N=$aC0Q|s#{>!mXaI%@f7fK92v9yjvHDJktBlAzn26?oWi*Kc^j{%j z9203*9o!N^Nq`2b0viCtgU#RiL)-%^E{)ormhh`jWtIDPo|afLLR27z#0rIgp=k6< zl^Kdj5oj7(Ar?Y6rM3w^Ns%<7@eGAvxC9WSb`A)HIuU?h)j}{DyPVod#Bm_rf4-W- zKu9ywiWsclv`Cid`xD3}oB2ayH=n<)A*@oRwB%4(r_8@}Lfes|tUE>WoQ(xBbDEc7 zHaFDHoAExTvs@kxDiugk@QV;HTEG1O*%_W;E`66DAbXF{F`Q>FFJm;r$xJ^um%HS~ z;r3wTQVWA}LX*KprTcC;oQG(9e;4oKy?`>b;FBdAzrLT|&;K5goy92lXMc9LGnvsT zUkt&;W zQLsyljaG9?y-Ru4lVAelv0mdBN^~gMqxNudxEjk@7^r`k`iJw4!~L!FKOusylcCV8 zQgP9b`;Q)Xl0?yWv1ObJf33ev@gy;x=>iEKyq3hryR8|iNbgc!svb<(OV&lT)!mH3%i)bjQ<>10dRWnhe8aJv1hB}dDA zNIBu+(q1NRs)7WBu=5=oEfUv<;LGHcSDG#((%gElWM0eB&WXi#e^WmbeVG&}t%$0M zmiFUi#I!V-7PJ1dP9Wx2(NfR+R%c$D@@_U&yu5RPRj7CVK}kx_^Q~v8gq^G*&l%EM zAevLl!^%?oIOIPK-Q}=8s{H_w{ljGJAUmqIlU3OP+H)1R66CofqhEu5n}8+O9(c!i z_VBE44w~RuuMv!ce~A!Dt(-P#^%i~`d6@+c@umzemv}k@Fy8}0*OB1nYhV9$XMlX$t}6~^XBvrNKa6u!M)(;e^CyCvY+RcK#+cd6xFp% z@s)g@mfSoq_+2c6{w+EFbOQJU678z7oECaSehF~+i!#9vETUI>3?xx8&!8%QOO%Py z#4I9n{xc6s{BAy&%Vw4^Zo6#CLsILpk-Z)OeP31f>wjSs(S?bk{@=`vAu3DZvI)vA z9yNQU2c$sYf0jtn6iD&ayMlUGQ11%rT|r&k!OL&q4;Dcw??`n35wiD42p83X51+TWu0+^6MWQjMQT+V`shkL4mLjO>pN|*D?g@Je_ZUCEzLOA z*X>V$6^5d0n)yWgWixX9ds{#8ne**d6FRoKJ7-;Ke{6a5%u{&)=(F4<=&U7&^Ju<8 zm&ejlrycPVN<5h(rh>(WTv+7cO7D+RK_*Z8;Y; z?N4%pXW5niQy53fc4~sI>V)*H8+~!mK5w_1AdS^0{D62R2!_Wlq)9yV7UTo$lVhyw z4Jhlc57+KvxUugyrSaMl%V$;clP-BK0m_qIc_4pKp|v1|dPJe06;a3^*|3KZ>S2U> z7@?;IBlI($0(P1Z`E^cC7<0U?H7X_Y&?kX28u*x^Qog!A2-qF=ItpYQjW7vWnsDvE z=IIORyv-j5UUUZ8UZ;MUI<;Br%?h(;0m3pa zDywWZSVf$H1&R>}=xh$f>0^+_Am({kz{-D&IRFzt<4}dH#VAYy1Q`A&1sF^V-V%eq zZJ?)REL^e2q8WQ^RE$?X3;jd}=FNqCc&QLcy`Jzg^@J)R+^jRy)Ns)sWYNQaOGdZ~ z8(qg%xk5^O%jlM~)m0eg3d!m_ibzTa@dQb^qAhIcW1CM;)yfRApEmYm3}U{fq!xb` z8S5+m5F?I{8Ky9oe449@H!4peUqe}OzP&u{`gKzlmoMkXI93_l$yjKS(b6c4?X>I$ zg6`0{B@!!@=7we)5Ohm*{IPSJ`?~^2w~6aC!!1!TVE6ilr_mCFtR`Txs(53*ZWZwE z<}HzmE>_yQyea@ z&ZG+zuipA9c<9bxRMSHTOwUTbY;#cvIRpXBE>Wt@%8XOqI;0Fju}0+ti12u6E=Q(> zI2=aEkh>)rr59=N=r>poi$QVOl`RAnB^ z*ATr0VjlT9it^Y|&K4SMO3;6iZ=uEEqPCB#4i+`M+TvGpXU-F*3NUqkVIZbdA`|yI z|6`==C~Y=ReKUHZGU;toMh-pg;|{dI_)+!0_<=OPxP{Ia_u5{s?LA@9+G~5=NY;a< zq!(Wy=B)vgbmcl(nn5M)fCLl1ztd}pQbfX^Ks7E*I(T#d^BYhk^*s4R+U6SBfXo^QL z^gj+-l~)`Ge*?d&&)a`^#EpYd)tZa2IRd3g>SO!IcrQgvnkkJ1E&`EcihPRHgf zaAb97f1kXZq{eUH*Wc6ks}3KsP&eKsi=}#@q1v-u@j((R!?p6#Yp;bI zSScj{TQZJje)6Ew(p5j)xmPZhE(w8zL@o9ED~+cp{&b>l>K{QSO$^64)TtQ<8%I() znqS7gvL%k|FdnPpx?A~cF0C!w1q+XS5zE;4t*z}>^8bIEt*t}(|Nm?ZHcnOY)asY1 z5ht&ui}m~E{M~#WVX>sVaR+ka)Gl448B9+QC6z%;h*~JdB!nEj;&_It%tCFgU8Ngp z96Dnksc+@VVKztnh9R4m-6WW_=E94+yLPZ&cXv$)k7PK*TrS#n!aS>ArwIP8PA9U= zIUBD~2rYktT_}Zl{+bm-yXgQDFhn2`nh>Xe$yWdZf)>#d1Tm6cd}VS{KH<$RG#O8ncbCwxJACWHSUO7mj|hh9?y1a>n9FXAgQ;4eU#{NkE3{?rgb+*-3Ir4B zHQTS49MgoTB<3qBA0Ze}a!PoNHA`1i=1LU1&Iy0yu(IB_foSBCUUN^N?T{W!XZvSM#2efkar$8Xu5Ycecq3NiMSi>L z6&tM|^A(Cf8v<>yfm7w@h3^2FlKbP9WYvEnvosr*MUz*4+lliW0S5#{OQG(p+xgE? znG`>-G`;BQ+SdBeTddD^58l)v7A#5dnap53eHTS-9bPtpO8a%AVZ*N7g=M3_O}$wo z*YG@v$4Gj*wDBTzyKNpLU|v$^$4L``nG?`V_ZRbn&Mz@Ypdq+@K+c`h#$cSlE6PqBUjrI zis3i#t9DM|@h8HYgh7sc1N4hc&kOgZ$bY>&$g^U{cMSD29oUfrS}?$nCI)1JV3m7U-ke1#WlQLbK^tJ%@Z_&NyO^_Y}}S|o=A1@l?Q*6Qx(w% zf^e$gziBlW_^N_y%8sw0Dr|1CFC>NagSL9yp^g^EPR;!U^Qj}$A`H>4?7Y3zo|kQE z>mHX2oUPipbfcmCN5LHH87}V_w4KpE{f6|yj)i+$yEQ@nYMPwhi;35AnW)O{TA+?m z`U5RjW$Ll%cM{86V0hK)(Jg=58^k=ucaS46MG~;WT2O0j5I_BL%74u9BQZ0{b= zEkMmni~=OV)gVaq3gg@eq9l}Wti4-b`j?d%kh%0Ua#z>uv)}uARNlvvGH+_GcBsX3 zhFMz~BdS)LiT?WLEl>op260lN*g~Laew##z-z-v%Z~>My6TKx==(TYIgFIr_QRz}uRbANAEQ(4yrMxIl|$|K}fQc_W?X{#)z? zT0H3fs(4?uxe6}SRVJpcHdP;Xvt;Q?>1B8<`n`Ee-rYeQNdReaUY4VB2!;_G0EUA3 z&g=c@w)N+$2!&%5-xA#q2~A3p8H#ZLBU61Fys~TWfLGw?>{@?l*xB^!*!!wKq}w(l zHtjAW82Y`iBROzT6H}$;`gQ6TXekad0$o{*Gm70+eCloO8N)ogoCT+FnJ` z4h_nh_2$$KR?B}3kBPmYL`N7%n1YolCvAAH3#t*ZSAAbMCQGN{E;r!P{3@UED@wQ@ z^H*GPezI5Fo`+hnz1%lhOh z_Y5CJ@n@jndjVKwIm~9Ci%8Ki-s)ca4Yi>h-p6!i|Gs~qBtWX*7YT}^unImvc7|tI z;Wm7L>^(xqaGt%qjL{4yGyULPW|xqtJ(v0uRDoAlCgC3r=OG&3#k+VfpbRbeWXZ;_ z@2B_kzei+eF$(_KpWW?DW^~FI!|;9z@Aufoci#lJve&*hBdvb7sH(@$5sNt;P~Vo! z*t@i&Kem6<9Y6gY+wLROzbAVBc~>)&{o!`425)yx64$NXVj)ks`4_O92OF8%b=MH`cp_14cmDzYo-y;pZl z0S!fWk0Xo-H~O}>2rp8oj$T@w+NeaRoBT#q{8dZjD;s27)&O^^%GzN`eKMTp>G}F# X)T0^DG=CM_1PCG$ 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 JJQe}&zW_~9Bg+5) delta 6726 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 =yF@E9ai(tIvQ6y2uqW@+mSp z64_#9-f#x9Y1n@lx`6JbCH#D#vKa_2FDJh08lXI1{A)3Q|n1gZVH8 zN@|K_O)9XRrsQ+lq>9;a<(oLTKJz9kGs~hv5ny2`OK!3C4ZDEVlP5%kmd@3f{0-#-RCru4lh(B@L$qJuC(I}W5NP{-p zdJxBJUR3gFUazWs0^^v^SrVjC1e;TSVX^^~t*BKnc86JOw^JZ`#>s-a?NXG`~u1|ZfL6p6J7(oN`yOUiD@ zFNxboT~^&)eUsH5hVMRDQP#LYm&Mc?G;vPsFFrytuOn?8ib*=AtDvi_b)V)9@yts? zUI1`+4s==qE;Ulb=2kK<8ovH!gqRo=Z>TDJ^Gj;CQdbZz$jPUZmB8@nYwVl4GlgS! ze&`h7@xO7y1~`IUBw9b^R6BRG@fI^y8ONtpXecdDZrDYEMi+!Qcr8LAl`6~)gfG#N zH}j%6R7VUbt_@E}G7^{j@sB99LEfsq-jg|($vX42tjPm83$7M>|MF+uBKBNMGJw}q zME=NN!NUH?zz}z}y7QK63Rj5aUiOUNxl(+2I+=nG;HIU+f%@>r9d`MhS<#2RO+}yV zAv~K{JnHO;AyH_Gxvk#&#XPKq<0mz!;8OarbIEq{0sJE%a=tO4q}PUvKru1(zSAU^ zqd}BNAOq7#h3Cv5JLlN9T1{iFJDfl66E;dY%~(`hbYsH%^55l$C0nfC&}oyWEy6vf zOrL^0z!;L}7SwaMvL&WJ!NKRM;V1f27+2bl!xB?=cfRf{hEB~G3nIt(7d<^Xkls)a zUu34PrCo^g>ifJsbG-H*awwYM{h+&LPN6KW`R#sh73nV5AXvl*w|SI@l^e{7b41k8 z9fQ~3#y?#eITS)o_}5-G-~C507pFu}MzoSC@PwP);=c3(OM0;gF@Ml@BF^D^ZFdph zey)myPUoqUBYM2u2k{L(eqc60OS`?pvMN)9+2iX!1<=NZR?~?5Bw>F`d5-=A&U>Dk z)IFt>u7-%KieGonEBRVN^n)(!yweZvZa{lfb|MnVg-bm(?@q^w-0Tnu{!vOcg>BFT zu;7QKv0qMA7Ij?5pNJRqIm`?!YWKgK!vFm} zWH_tmFDnQ|^cXABI|QL?CXI1xR23HSDw!h968T?8WncLQK|X|a*;9%BGyBMt#rm{q zC@gY_2dhYb`)~c~anI5C8)2$$!`%H|6NqU`*|4hbkttPe-(K%mh{$dGv^D0qDPx78 zMVy_KsR)Qam(PE2DY&nSYdYz&{GFa)>HMsk#f7Ir!Kb zA+wbad%%U?BwS&^7g1%$QDxq@ZI2?e6r?9`*W$d}ZE%#53IiShBoOHNIoNp-;gXCD F`Y+k+C_DfF diff --git a/chain/actors/builtin/miner/actor.go.template b/chain/actors/builtin/miner/actor.go.template index b4ad3a4b5..ead254847 100644 --- a/chain/actors/builtin/miner/actor.go.template +++ b/chain/actors/builtin/miner/actor.go.template @@ -17,6 +17,7 @@ import ( "github.com/filecoin-project/lotus/chain/actors/adt" "github.com/filecoin-project/lotus/chain/types" + miner12 "github.com/filecoin-project/go-state-types/builtin/v12/miner" minertypes "github.com/filecoin-project/go-state-types/builtin/v9/miner" "github.com/filecoin-project/go-state-types/manifest" @@ -152,7 +153,7 @@ type Partition interface { UnprovenSectors() (bitfield.BitField, error) } -type SectorOnChainInfo = minertypes.SectorOnChainInfo +type SectorOnChainInfo = miner12.SectorOnChainInfo func PreferredSealProofTypeFromWindowPoStType(nver network.Version, proof abi.RegisteredPoStProof, configWantSynthetic bool) (abi.RegisteredSealProof, error) { // We added support for the new proofs in network version 7, and removed support for the old diff --git a/chain/actors/builtin/miner/miner.go b/chain/actors/builtin/miner/miner.go index 1637cdade..151688d04 100644 --- a/chain/actors/builtin/miner/miner.go +++ b/chain/actors/builtin/miner/miner.go @@ -9,6 +9,7 @@ import ( "github.com/filecoin-project/go-state-types/abi" actorstypes "github.com/filecoin-project/go-state-types/actors" "github.com/filecoin-project/go-state-types/big" + miner12 "github.com/filecoin-project/go-state-types/builtin/v12/miner" minertypes "github.com/filecoin-project/go-state-types/builtin/v9/miner" "github.com/filecoin-project/go-state-types/cbor" "github.com/filecoin-project/go-state-types/dline" @@ -212,7 +213,7 @@ type Partition interface { UnprovenSectors() (bitfield.BitField, error) } -type SectorOnChainInfo = minertypes.SectorOnChainInfo +type SectorOnChainInfo = miner12.SectorOnChainInfo func PreferredSealProofTypeFromWindowPoStType(nver network.Version, proof abi.RegisteredPoStProof, configWantSynthetic bool) (abi.RegisteredSealProof, error) { // We added support for the new proofs in network version 7, and removed support for the old diff --git a/documentation/en/api-v0-methods.md b/documentation/en/api-v0-methods.md index 9110c1c3b..2eb683f2c 100644 --- a/documentation/en/api-v0-methods.md +++ b/documentation/en/api-v0-methods.md @@ -5875,12 +5875,12 @@ Response: "InitialPledge": "0", "ExpectedDayReward": "0", "ExpectedStoragePledge": "0", - "ReplacedSectorAge": 10101, + "PowerBaseEpoch": 10101, "ReplacedDayReward": "0", "SectorKeyCID": { "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" }, - "SimpleQAPower": true + "Flags": 0 } ] ``` @@ -6355,12 +6355,12 @@ Response: "InitialPledge": "0", "ExpectedDayReward": "0", "ExpectedStoragePledge": "0", - "ReplacedSectorAge": 10101, + "PowerBaseEpoch": 10101, "ReplacedDayReward": "0", "SectorKeyCID": { "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" }, - "SimpleQAPower": true + "Flags": 0 } ] ``` @@ -6760,12 +6760,12 @@ Response: "InitialPledge": "0", "ExpectedDayReward": "0", "ExpectedStoragePledge": "0", - "ReplacedSectorAge": 10101, + "PowerBaseEpoch": 10101, "ReplacedDayReward": "0", "SectorKeyCID": { "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" }, - "SimpleQAPower": true + "Flags": 0 } ``` diff --git a/documentation/en/api-v1-unstable-methods.md b/documentation/en/api-v1-unstable-methods.md index 53b5ddbe6..6c315a92d 100644 --- a/documentation/en/api-v1-unstable-methods.md +++ b/documentation/en/api-v1-unstable-methods.md @@ -7473,12 +7473,12 @@ Response: "InitialPledge": "0", "ExpectedDayReward": "0", "ExpectedStoragePledge": "0", - "ReplacedSectorAge": 10101, + "PowerBaseEpoch": 10101, "ReplacedDayReward": "0", "SectorKeyCID": { "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" }, - "SimpleQAPower": true + "Flags": 0 } ] ``` @@ -7981,12 +7981,12 @@ Response: "InitialPledge": "0", "ExpectedDayReward": "0", "ExpectedStoragePledge": "0", - "ReplacedSectorAge": 10101, + "PowerBaseEpoch": 10101, "ReplacedDayReward": "0", "SectorKeyCID": { "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" }, - "SimpleQAPower": true + "Flags": 0 } ] ``` @@ -8340,12 +8340,12 @@ Response: "InitialPledge": "0", "ExpectedDayReward": "0", "ExpectedStoragePledge": "0", - "ReplacedSectorAge": 10101, + "PowerBaseEpoch": 10101, "ReplacedDayReward": "0", "SectorKeyCID": { "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" }, - "SimpleQAPower": true + "Flags": 0 } ``` diff --git a/storage/pipeline/mocks/api.go b/storage/pipeline/mocks/api.go index 5c67a1c42..126dbba7d 100644 --- a/storage/pipeline/mocks/api.go +++ b/storage/pipeline/mocks/api.go @@ -15,14 +15,15 @@ import ( bitfield "github.com/filecoin-project/go-bitfield" abi "github.com/filecoin-project/go-state-types/abi" big "github.com/filecoin-project/go-state-types/big" - miner "github.com/filecoin-project/go-state-types/builtin/v9/miner" + miner "github.com/filecoin-project/go-state-types/builtin/v12/miner" + miner0 "github.com/filecoin-project/go-state-types/builtin/v9/miner" verifreg "github.com/filecoin-project/go-state-types/builtin/v9/verifreg" crypto "github.com/filecoin-project/go-state-types/crypto" dline "github.com/filecoin-project/go-state-types/dline" network "github.com/filecoin-project/go-state-types/network" api "github.com/filecoin-project/lotus/api" - miner0 "github.com/filecoin-project/lotus/chain/actors/builtin/miner" + miner1 "github.com/filecoin-project/lotus/chain/actors/builtin/miner" types "github.com/filecoin-project/lotus/chain/types" ) @@ -290,7 +291,7 @@ func (mr *MockSealingAPIMockRecorder) StateMinerInfo(arg0, arg1, arg2 interface{ } // StateMinerInitialPledgeCollateral mocks base method. -func (m *MockSealingAPI) StateMinerInitialPledgeCollateral(arg0 context.Context, arg1 address.Address, arg2 miner.SectorPreCommitInfo, arg3 types.TipSetKey) (big.Int, error) { +func (m *MockSealingAPI) StateMinerInitialPledgeCollateral(arg0 context.Context, arg1 address.Address, arg2 miner0.SectorPreCommitInfo, arg3 types.TipSetKey) (big.Int, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "StateMinerInitialPledgeCollateral", arg0, arg1, arg2, arg3) ret0, _ := ret[0].(big.Int) @@ -320,7 +321,7 @@ func (mr *MockSealingAPIMockRecorder) StateMinerPartitions(arg0, arg1, arg2, arg } // StateMinerPreCommitDepositForPower mocks base method. -func (m *MockSealingAPI) StateMinerPreCommitDepositForPower(arg0 context.Context, arg1 address.Address, arg2 miner.SectorPreCommitInfo, arg3 types.TipSetKey) (big.Int, error) { +func (m *MockSealingAPI) StateMinerPreCommitDepositForPower(arg0 context.Context, arg1 address.Address, arg2 miner0.SectorPreCommitInfo, arg3 types.TipSetKey) (big.Int, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "StateMinerPreCommitDepositForPower", arg0, arg1, arg2, arg3) ret0, _ := ret[0].(big.Int) @@ -410,10 +411,10 @@ func (mr *MockSealingAPIMockRecorder) StateSectorGetInfo(arg0, arg1, arg2, arg3 } // StateSectorPartition mocks base method. -func (m *MockSealingAPI) StateSectorPartition(arg0 context.Context, arg1 address.Address, arg2 abi.SectorNumber, arg3 types.TipSetKey) (*miner0.SectorLocation, error) { +func (m *MockSealingAPI) StateSectorPartition(arg0 context.Context, arg1 address.Address, arg2 abi.SectorNumber, arg3 types.TipSetKey) (*miner1.SectorLocation, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "StateSectorPartition", arg0, arg1, arg2, arg3) - ret0, _ := ret[0].(*miner0.SectorLocation) + ret0, _ := ret[0].(*miner1.SectorLocation) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -425,10 +426,10 @@ func (mr *MockSealingAPIMockRecorder) StateSectorPartition(arg0, arg1, arg2, arg } // StateSectorPreCommitInfo mocks base method. -func (m *MockSealingAPI) StateSectorPreCommitInfo(arg0 context.Context, arg1 address.Address, arg2 abi.SectorNumber, arg3 types.TipSetKey) (*miner.SectorPreCommitOnChainInfo, error) { +func (m *MockSealingAPI) StateSectorPreCommitInfo(arg0 context.Context, arg1 address.Address, arg2 abi.SectorNumber, arg3 types.TipSetKey) (*miner0.SectorPreCommitOnChainInfo, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "StateSectorPreCommitInfo", arg0, arg1, arg2, arg3) - ret0, _ := ret[0].(*miner.SectorPreCommitOnChainInfo) + ret0, _ := ret[0].(*miner0.SectorPreCommitOnChainInfo) ret1, _ := ret[1].(error) return ret0, ret1 } diff --git a/storage/wdpost/wdpost_run_test.go b/storage/wdpost/wdpost_run_test.go index a3847a6f6..a6b37fd54 100644 --- a/storage/wdpost/wdpost_run_test.go +++ b/storage/wdpost/wdpost_run_test.go @@ -16,6 +16,7 @@ import ( actorstypes "github.com/filecoin-project/go-state-types/actors" "github.com/filecoin-project/go-state-types/big" "github.com/filecoin-project/go-state-types/builtin" + miner12 "github.com/filecoin-project/go-state-types/builtin/v12/miner" minertypes "github.com/filecoin-project/go-state-types/builtin/v9/miner" "github.com/filecoin-project/go-state-types/crypto" "github.com/filecoin-project/go-state-types/dline" @@ -73,13 +74,13 @@ func (m *mockStorageMinerAPI) StateMinerPartitions(ctx context.Context, a addres return m.partitions, nil } -func (m *mockStorageMinerAPI) StateMinerSectors(ctx context.Context, address address.Address, snos *bitfield.BitField, key types.TipSetKey) ([]*minertypes.SectorOnChainInfo, error) { - var sis []*minertypes.SectorOnChainInfo +func (m *mockStorageMinerAPI) StateMinerSectors(ctx context.Context, address address.Address, snos *bitfield.BitField, key types.TipSetKey) ([]*miner12.SectorOnChainInfo, error) { + var sis []*miner12.SectorOnChainInfo if snos == nil { panic("unsupported") } _ = snos.ForEach(func(i uint64) error { - sis = append(sis, &minertypes.SectorOnChainInfo{ + sis = append(sis, &miner12.SectorOnChainInfo{ SectorNumber: abi.SectorNumber(i), }) return nil From 32559aa463f6e771d1a58aa97922f769c2572027 Mon Sep 17 00:00:00 2001 From: "Andrew Jackson (Ajax)" Date: Sat, 13 Jan 2024 06:47:22 -0600 Subject: [PATCH 069/394] rearrange --- cmd/lotus-provider/rpc/rpc.go | 5 ++--- cmd/lotus-provider/run.go | 15 +++++---------- .../web => provider/lpweb}/api/debug/debug.go | 0 .../web => provider/lpweb}/api/routes.go | 2 +- .../web => provider/lpweb}/hapi/routes.go | 0 .../web => provider/lpweb}/hapi/simpleinfo.go | 2 +- .../lpweb}/hapi/web/actor_summary.gohtml | 0 .../lpweb}/hapi/web/chain_rpcs.gohtml | 0 .../lpweb}/hapi/web/cluster_machines.gohtml | 0 .../lpweb}/hapi/web/cluster_task_history.gohtml | 0 .../lpweb}/hapi/web/cluster_tasks.gohtml | 0 {cmd/lotus-provider/web => provider/lpweb}/srv.go | 8 ++++---- .../lpweb}/static/chain-connectivity.js | 0 .../web => provider/lpweb}/static/index.html | 0 14 files changed, 13 insertions(+), 19 deletions(-) rename {cmd/lotus-provider/web => provider/lpweb}/api/debug/debug.go (100%) rename {cmd/lotus-provider/web => provider/lpweb}/api/routes.go (80%) rename {cmd/lotus-provider/web => provider/lpweb}/hapi/routes.go (100%) rename {cmd/lotus-provider/web => provider/lpweb}/hapi/simpleinfo.go (98%) rename {cmd/lotus-provider/web => provider/lpweb}/hapi/web/actor_summary.gohtml (100%) rename {cmd/lotus-provider/web => provider/lpweb}/hapi/web/chain_rpcs.gohtml (100%) rename {cmd/lotus-provider/web => provider/lpweb}/hapi/web/cluster_machines.gohtml (100%) rename {cmd/lotus-provider/web => provider/lpweb}/hapi/web/cluster_task_history.gohtml (100%) rename {cmd/lotus-provider/web => provider/lpweb}/hapi/web/cluster_tasks.gohtml (100%) rename {cmd/lotus-provider/web => provider/lpweb}/srv.go (90%) rename {cmd/lotus-provider/web => provider/lpweb}/static/chain-connectivity.js (100%) rename {cmd/lotus-provider/web => provider/lpweb}/static/index.html (100%) diff --git a/cmd/lotus-provider/rpc/rpc.go b/cmd/lotus-provider/rpc/rpc.go index d77d8c81e..e2897030f 100644 --- a/cmd/lotus-provider/rpc/rpc.go +++ b/cmd/lotus-provider/rpc/rpc.go @@ -21,10 +21,10 @@ import ( "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/cmd/lotus-provider/deps" - "github.com/filecoin-project/lotus/cmd/lotus-provider/web" "github.com/filecoin-project/lotus/lib/rpcenc" "github.com/filecoin-project/lotus/metrics" "github.com/filecoin-project/lotus/metrics/proxy" + "github.com/filecoin-project/lotus/provider/lpweb" "github.com/filecoin-project/lotus/storage/paths" ) @@ -79,7 +79,6 @@ func (p *ProviderAPI) Shutdown(context.Context) error { } func ListenAndServe(ctx context.Context, dependencies *deps.Deps, shutdownChan chan struct{}) error { - fh := &paths.FetchHandler{Local: dependencies.LocalStore, PfHandler: &paths.DefaultPartialFileHandler{}} remoteHandler := func(w http.ResponseWriter, r *http.Request) { if !auth.HasPerm(r.Context(), nil, api.PermAdmin) { @@ -133,7 +132,7 @@ func ListenAndServe(ctx context.Context, dependencies *deps.Deps, shutdownChan c eg.Go(srv.ListenAndServe) if dependencies.Cfg.Subsystems.EnableWebGui { - web, err := web.GetSrv(ctx, dependencies) + web, err := lpweb.GetSrv(ctx, dependencies) if err != nil { return err } diff --git a/cmd/lotus-provider/run.go b/cmd/lotus-provider/run.go index 0f18f2843..b1a4ff828 100644 --- a/cmd/lotus-provider/run.go +++ b/cmd/lotus-provider/run.go @@ -1,14 +1,12 @@ package main import ( - "bytes" "context" "fmt" "os" "strings" "time" - "github.com/BurntSushi/toml" "github.com/pkg/errors" "github.com/urfave/cli/v2" "go.opencensus.io/stats" @@ -22,7 +20,6 @@ import ( "github.com/filecoin-project/lotus/lib/ulimit" "github.com/filecoin-project/lotus/metrics" "github.com/filecoin-project/lotus/node" - "github.com/filecoin-project/lotus/node/config" ) type stackTracer interface { @@ -174,13 +171,11 @@ var webCmd = &cli.Command{ webtxt, err := getConfig(db, "web") if err != nil || webtxt == "" { - cfg := config.DefaultLotusProvider() - cfg.Subsystems.EnableWebGui = true - var b bytes.Buffer - if err = toml.NewEncoder(&b).Encode(cfg); err != nil { - return err - } - if err = setConfig(db, "web", b.String()); err != nil { + + s := `[Susbystems] + EnableWebGui = true + ` + if err = setConfig(db, "web", s); err != nil { return err } } diff --git a/cmd/lotus-provider/web/api/debug/debug.go b/provider/lpweb/api/debug/debug.go similarity index 100% rename from cmd/lotus-provider/web/api/debug/debug.go rename to provider/lpweb/api/debug/debug.go diff --git a/cmd/lotus-provider/web/api/routes.go b/provider/lpweb/api/routes.go similarity index 80% rename from cmd/lotus-provider/web/api/routes.go rename to provider/lpweb/api/routes.go index 9bb6fb67c..85b17486f 100644 --- a/cmd/lotus-provider/web/api/routes.go +++ b/provider/lpweb/api/routes.go @@ -5,7 +5,7 @@ import ( "github.com/gorilla/mux" "github.com/filecoin-project/lotus/cmd/lotus-provider/deps" - "github.com/filecoin-project/lotus/cmd/lotus-provider/web/api/debug" + "github.com/filecoin-project/lotus/provider/lpweb/api/debug" ) func Routes(r *mux.Router, deps *deps.Deps) { diff --git a/cmd/lotus-provider/web/hapi/routes.go b/provider/lpweb/hapi/routes.go similarity index 100% rename from cmd/lotus-provider/web/hapi/routes.go rename to provider/lpweb/hapi/routes.go diff --git a/cmd/lotus-provider/web/hapi/simpleinfo.go b/provider/lpweb/hapi/simpleinfo.go similarity index 98% rename from cmd/lotus-provider/web/hapi/simpleinfo.go rename to provider/lpweb/hapi/simpleinfo.go index a14735a84..ee36a1e17 100644 --- a/cmd/lotus-provider/web/hapi/simpleinfo.go +++ b/provider/lpweb/hapi/simpleinfo.go @@ -82,7 +82,7 @@ var templateDev = os.Getenv("LOTUS_WEB_DEV") == "1" func (a *app) executeTemplate(w http.ResponseWriter, name string, data interface{}) { if templateDev { fs := os.DirFS("./cmd/lotus-provider/web/hapi/web") - a.t = template.Must(template.ParseFS(fs, "web/*")) + a.t = template.Must(template.ParseFS(fs, "*")) } if err := a.t.ExecuteTemplate(w, name, data); err != nil { log.Errorf("execute template %s: %v", name, err) diff --git a/cmd/lotus-provider/web/hapi/web/actor_summary.gohtml b/provider/lpweb/hapi/web/actor_summary.gohtml similarity index 100% rename from cmd/lotus-provider/web/hapi/web/actor_summary.gohtml rename to provider/lpweb/hapi/web/actor_summary.gohtml diff --git a/cmd/lotus-provider/web/hapi/web/chain_rpcs.gohtml b/provider/lpweb/hapi/web/chain_rpcs.gohtml similarity index 100% rename from cmd/lotus-provider/web/hapi/web/chain_rpcs.gohtml rename to provider/lpweb/hapi/web/chain_rpcs.gohtml diff --git a/cmd/lotus-provider/web/hapi/web/cluster_machines.gohtml b/provider/lpweb/hapi/web/cluster_machines.gohtml similarity index 100% rename from cmd/lotus-provider/web/hapi/web/cluster_machines.gohtml rename to provider/lpweb/hapi/web/cluster_machines.gohtml diff --git a/cmd/lotus-provider/web/hapi/web/cluster_task_history.gohtml b/provider/lpweb/hapi/web/cluster_task_history.gohtml similarity index 100% rename from cmd/lotus-provider/web/hapi/web/cluster_task_history.gohtml rename to provider/lpweb/hapi/web/cluster_task_history.gohtml diff --git a/cmd/lotus-provider/web/hapi/web/cluster_tasks.gohtml b/provider/lpweb/hapi/web/cluster_tasks.gohtml similarity index 100% rename from cmd/lotus-provider/web/hapi/web/cluster_tasks.gohtml rename to provider/lpweb/hapi/web/cluster_tasks.gohtml diff --git a/cmd/lotus-provider/web/srv.go b/provider/lpweb/srv.go similarity index 90% rename from cmd/lotus-provider/web/srv.go rename to provider/lpweb/srv.go index 55d20cc9a..f6bcfcf85 100644 --- a/cmd/lotus-provider/web/srv.go +++ b/provider/lpweb/srv.go @@ -1,5 +1,5 @@ -// Package web defines the HTTP web server for static files and endpoints. -package web +// Package lpweb defines the HTTP web server for static files and endpoints. +package lpweb import ( "context" @@ -17,9 +17,9 @@ import ( "go.opencensus.io/tag" "github.com/filecoin-project/lotus/cmd/lotus-provider/deps" - "github.com/filecoin-project/lotus/cmd/lotus-provider/web/api" - "github.com/filecoin-project/lotus/cmd/lotus-provider/web/hapi" "github.com/filecoin-project/lotus/metrics" + "github.com/filecoin-project/lotus/provider/lpweb/api" + "github.com/filecoin-project/lotus/provider/lpweb/hapi" ) //go:embed static diff --git a/cmd/lotus-provider/web/static/chain-connectivity.js b/provider/lpweb/static/chain-connectivity.js similarity index 100% rename from cmd/lotus-provider/web/static/chain-connectivity.js rename to provider/lpweb/static/chain-connectivity.js diff --git a/cmd/lotus-provider/web/static/index.html b/provider/lpweb/static/index.html similarity index 100% rename from cmd/lotus-provider/web/static/index.html rename to provider/lpweb/static/index.html From 73d830e2fb89c4b0f077771b465cf959dfd2d105 Mon Sep 17 00:00:00 2001 From: Phi Date: Wed, 17 Jan 2024 13:27:09 +0100 Subject: [PATCH 070/394] Remove merge conflict artefact Remove merge conflict artefact --- CHANGELOG.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5da284702..9cf98e889 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -196,8 +196,6 @@ This patch release allows for up to 10k messages per block. Additionally, it int ## Improvements - fix: exchange: allow up to 10k messages per block ([filecoin-project/lotus#11506](https://github.com/filecoin-project/lotus/pull/11506)) ->>>>>>> releases - # v 1.25.0 / 2023-11-22 This is a highly recommended feature release of Lotus. This optional release supports the Filecoin network version 21 upgrade, codenamed Watermelon 🍉, in addition to the numerous improvements and enhancements for node operators, ETH RPC-providers and storage providers. From 61cfb51674e9a6b92cfa80c86746e568e241714c Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 17 Jan 2024 16:15:58 -0800 Subject: [PATCH 071/394] fix: fill in power-base & replaced-day-reward in state getter see #11586 --- chain/actors/builtin/miner/state.go.template | 6 +++++- chain/actors/builtin/miner/v12.go | 3 +++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/chain/actors/builtin/miner/state.go.template b/chain/actors/builtin/miner/state.go.template index 3c7f05d9a..ac3326828 100644 --- a/chain/actors/builtin/miner/state.go.template +++ b/chain/actors/builtin/miner/state.go.template @@ -583,7 +583,11 @@ func fromV{{.v}}SectorOnChainInfo(v{{.v}} miner{{.v}}.SectorOnChainInfo) SectorO ExpectedStoragePledge: v{{.v}}.ExpectedStoragePledge, {{if (ge .v 7)}} SectorKeyCID: v{{.v}}.SectorKeyCID, - {{end}} + {{end}} + {{if (ge .v 12)}} + PowerBaseEpoch: v{{.v}}.PowerBaseEpoch, + ReplacedDayReward: v{{.v}}.ReplacedDayReward, + {{end}} } return info } diff --git a/chain/actors/builtin/miner/v12.go b/chain/actors/builtin/miner/v12.go index 90ecc97fd..f06d2c431 100644 --- a/chain/actors/builtin/miner/v12.go +++ b/chain/actors/builtin/miner/v12.go @@ -545,6 +545,9 @@ func fromV12SectorOnChainInfo(v12 miner12.SectorOnChainInfo) SectorOnChainInfo { ExpectedStoragePledge: v12.ExpectedStoragePledge, SectorKeyCID: v12.SectorKeyCID, + + PowerBaseEpoch: v12.PowerBaseEpoch, + ReplacedDayReward: v12.ReplacedDayReward, } return info } From 3685cb5e12ade81390c3489539ac063cb5624460 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 18 Jan 2024 13:23:49 -0800 Subject: [PATCH 072/394] fix: daemon: avoid prompting to remove chain when noninteractive (#11582) 1. Let the user specify `--remove-existing-chain=false` to avoid the prompt and fail if there's an existing chain. 2. Only prompt when interactive. Otherwise, _don't_ remove the chain by-default. --- cmd/lotus/daemon.go | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/cmd/lotus/daemon.go b/cmd/lotus/daemon.go index 5d8096d1f..aeb703432 100644 --- a/cmd/lotus/daemon.go +++ b/cmd/lotus/daemon.go @@ -178,6 +178,8 @@ var DaemonCmd = &cli.Command{ return xerrors.Errorf("enabling runtime metrics: %w", err) } + interactive := cctx.Bool("interactive") + if cctx.Bool("manage-fdlimit") { if _, _, err := ulimit.ManageFdLimit(); err != nil { log.Errorf("setting file descriptor limit: %s", err) @@ -299,8 +301,8 @@ var DaemonCmd = &cli.Command{ willImportChain = true } - willRemoveChain := cctx.Bool("remove-existing-chain") - if willImportChain && !willRemoveChain { + var willRemoveChain bool + if interactive && willImportChain && !cctx.IsSet("remove-existing-chain") { // Confirm with the user about the intention to remove chain data. reader := bufio.NewReader(os.Stdin) fmt.Print("Importing chain or snapshot will by default delete existing local chain data. Do you want to proceed and delete? (yes/no): ") @@ -309,14 +311,16 @@ var DaemonCmd = &cli.Command{ return xerrors.Errorf("reading user input: %w", err) } userInput = strings.ToLower(strings.TrimSpace(userInput)) - - if userInput == "yes" { + switch userInput { + case "yes": willRemoveChain = true - } else if userInput == "no" { + case "no": willRemoveChain = false - } else { + default: return fmt.Errorf("invalid input. please answer with 'yes' or 'no'") } + } else { + willRemoveChain = cctx.Bool("remove-existing-chain") } if willRemoveChain { From e430d336b22d8eaba31e9e3f64926b079180d89b Mon Sep 17 00:00:00 2001 From: Vid Bregar <38587571+vidbregar@users.noreply.github.com> Date: Fri, 19 Jan 2024 16:56:12 +0100 Subject: [PATCH 073/394] fix: Dockerfile non-interactive snapshot import (#11579) --- Dockerfile | 2 +- scripts/docker-lotus-entrypoint.sh | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Dockerfile b/Dockerfile index c9750a71f..ea26b6184 100644 --- a/Dockerfile +++ b/Dockerfile @@ -73,7 +73,7 @@ COPY --from=lotus-builder /opt/filecoin/lotus /usr/local/bin/ COPY --from=lotus-builder /opt/filecoin/lotus-shed /usr/local/bin/ COPY scripts/docker-lotus-entrypoint.sh / -ARG DOCKER_LOTUS_IMPORT_SNAPSHOT https://snapshots.mainnet.filops.net/minimal/latest +ARG DOCKER_LOTUS_IMPORT_SNAPSHOT=https://forest-archive.chainsafe.dev/latest/mainnet/ ENV DOCKER_LOTUS_IMPORT_SNAPSHOT ${DOCKER_LOTUS_IMPORT_SNAPSHOT} ENV FILECOIN_PARAMETER_CACHE /var/tmp/filecoin-proof-parameters ENV LOTUS_PATH /var/lib/lotus diff --git a/scripts/docker-lotus-entrypoint.sh b/scripts/docker-lotus-entrypoint.sh index de1dfbcc8..342121d97 100755 --- a/scripts/docker-lotus-entrypoint.sh +++ b/scripts/docker-lotus-entrypoint.sh @@ -1,18 +1,21 @@ #!/usr/bin/env bash -if [ ! -z $DOCKER_LOTUS_IMPORT_SNAPSHOT ]; then +if [ ! -z "$DOCKER_LOTUS_IMPORT_SNAPSHOT" ]; then GATE="$LOTUS_PATH"/date_initialized # Don't init if already initialized. if [ ! -f "$GATE" ]; then echo importing minimal snapshot - /usr/local/bin/lotus daemon --import-snapshot "$DOCKER_LOTUS_IMPORT_SNAPSHOT" --halt-after-import + /usr/local/bin/lotus daemon \ + --import-snapshot "$DOCKER_LOTUS_IMPORT_SNAPSHOT" \ + --remove-existing-chain=false \ + --halt-after-import # Block future inits date > "$GATE" fi fi # import wallet, if provided -if [ ! -z $DOCKER_LOTUS_IMPORT_WALLET ]; then +if [ ! -z "$DOCKER_LOTUS_IMPORT_WALLET" ]; then /usr/local/bin/lotus-shed keyinfo import "$DOCKER_LOTUS_IMPORT_WALLET" fi From 0006bc3d77e787fd76ae7354dc0d52cab1a28ba7 Mon Sep 17 00:00:00 2001 From: "Andrew Jackson (Ajax)" Date: Fri, 19 Jan 2024 18:48:29 -0600 Subject: [PATCH 074/394] feat: multiaddr lp --- api/types.go | 1 + cmd/lotus-provider/deps/deps.go | 24 ++++++---- cmd/lotus-provider/migrate.go | 4 +- cmd/lotus-provider/proving.go | 3 +- node/config/def.go | 4 +- node/config/types.go | 6 ++- node/impl/full/state.go | 1 + provider/address.go | 73 +++++++++++++++++------------ provider/builder.go | 2 +- provider/lpwindow/recover_task.go | 4 +- provider/lpwindow/submit_task.go | 6 +-- storage/ctladdr/multiaddresses.go | 76 +++++++++++++++++++++++++++++++ 12 files changed, 154 insertions(+), 50 deletions(-) create mode 100644 storage/ctladdr/multiaddresses.go diff --git a/api/types.go b/api/types.go index 7fd607750..e29b7c6f7 100644 --- a/api/types.go +++ b/api/types.go @@ -291,6 +291,7 @@ type ExportRef struct { } type MinerInfo struct { + MinerID address.Address Owner address.Address // Must be an ID-address. Worker address.Address // Must be an ID-address. NewWorker address.Address // Must be an ID-address. diff --git a/cmd/lotus-provider/deps/deps.go b/cmd/lotus-provider/deps/deps.go index 7a8db855f..34db9f461 100644 --- a/cmd/lotus-provider/deps/deps.go +++ b/cmd/lotus-provider/deps/deps.go @@ -10,6 +10,7 @@ import ( "net" "net/http" "os" + "regexp" "strings" "github.com/BurntSushi/toml" @@ -95,7 +96,7 @@ type Deps struct { Full api.FullNode Verif storiface.Verifier LW *sealer.LocalWorker - As *ctladdr.AddressSelector + As *ctladdr.MultiAddressSelector Maddrs []dtypes.MinerAddress Stor *paths.Remote Si *paths.DBIndex @@ -151,7 +152,7 @@ func (deps *Deps) PopulateRemainingDeps(ctx context.Context, cctx *cli.Context, } if deps.As == nil { - deps.As, err = provider.AddressSelector(&deps.Cfg.Addresses)() + deps.As, err = provider.AddressSelector(deps.Cfg.Addresses)() if err != nil { return err } @@ -236,18 +237,22 @@ Get it with: jq .PrivateKey ~/.lotus-miner/keystore/MF2XI2BNNJ3XILLQOJUXMYLUMU`, deps.LW = sealer.NewLocalWorker(sealer.WorkerConfig{}, deps.Stor, deps.LocalStore, deps.Si, nil, wstates) } if len(deps.Maddrs) == 0 { - for _, s := range deps.Cfg.Addresses.MinerAddresses { - addr, err := address.NewFromString(s) - if err != nil { - return err + for _, s := range deps.Cfg.Addresses { + for _, s := range s.MinerAddresses { + addr, err := address.NewFromString(s) + if err != nil { + return err + } + deps.Maddrs = append(deps.Maddrs, dtypes.MinerAddress(addr)) } - deps.Maddrs = append(deps.Maddrs, dtypes.MinerAddress(addr)) } } fmt.Println("last line of populate") return nil } +var oldAddresses = regexp.MustCompile("(?i)^[addresses]$") + func GetConfig(cctx *cli.Context, db *harmonydb.DB) (*config.LotusProviderConfig, error) { lp := config.DefaultLotusProvider() have := []string{} @@ -265,7 +270,10 @@ func GetConfig(cctx *cli.Context, db *harmonydb.DB) (*config.LotusProviderConfig } return nil, fmt.Errorf("could not read layer '%s': %w", layer, err) } - meta, err := toml.Decode(text, &lp) + + // allow migration from old config format that was limited to 1 wallet setup. + newText := oldAddresses.ReplaceAllString(text, "[[addresses]]") + meta, err := toml.Decode(newText, &lp) if err != nil { return nil, fmt.Errorf("could not read layer, bad toml %s: %w", layer, err) } diff --git a/cmd/lotus-provider/migrate.go b/cmd/lotus-provider/migrate.go index 3869c7dfb..315691788 100644 --- a/cmd/lotus-provider/migrate.go +++ b/cmd/lotus-provider/migrate.go @@ -148,7 +148,9 @@ func fromMiner(cctx *cli.Context) (err error) { return xerrors.Errorf("parsing miner actor address: %w", err) } - lpCfg.Addresses.MinerAddresses = []string{addr.String()} + lpCfg.Addresses = []config.LotusProviderAddresses{{ + MinerAddresses: []string{addr.String()}, + }} ks, err := lr.KeyStore() if err != nil { diff --git a/cmd/lotus-provider/proving.go b/cmd/lotus-provider/proving.go index 379bfdf85..175372249 100644 --- a/cmd/lotus-provider/proving.go +++ b/cmd/lotus-provider/proving.go @@ -74,7 +74,8 @@ var wdPostTaskCmd = &cli.Command{ } ht := ts.Height() - addr, err := address.NewFromString(deps.Cfg.Addresses.MinerAddresses[0]) + // It's not important to be super-accurate as it's only for basic testing. + addr, err := address.NewFromString(deps.Cfg.Addresses[0].MinerAddresses[0]) if err != nil { return xerrors.Errorf("cannot get miner address %w", err) } diff --git a/node/config/def.go b/node/config/def.go index 192ab4f5e..48b877b3b 100644 --- a/node/config/def.go +++ b/node/config/def.go @@ -349,11 +349,11 @@ func DefaultLotusProvider() *LotusProviderConfig { MaxWindowPoStGasFee: types.MustParseFIL("5"), MaxPublishDealsFee: types.MustParseFIL("0.05"), }, - Addresses: LotusProviderAddresses{ + Addresses: []LotusProviderAddresses{{ PreCommitControl: []string{}, CommitControl: []string{}, TerminateControl: []string{}, - }, + }}, Proving: ProvingConfig{ ParallelCheckLimit: 32, PartitionCheckTimeout: Duration(20 * time.Minute), diff --git a/node/config/types.go b/node/config/types.go index 8661ce190..208d85939 100644 --- a/node/config/types.go +++ b/node/config/types.go @@ -68,8 +68,10 @@ type StorageMiner struct { type LotusProviderConfig struct { Subsystems ProviderSubsystemsConfig - Fees LotusProviderFees - Addresses LotusProviderAddresses + Fees LotusProviderFees + + // Addresses of wallets per MinerAddress (one of the fields). + Addresses []LotusProviderAddresses Proving ProvingConfig Journal JournalConfig Apis ApisConfig diff --git a/node/impl/full/state.go b/node/impl/full/state.go index 0e92c8e5b..d294307c1 100644 --- a/node/impl/full/state.go +++ b/node/impl/full/state.go @@ -169,6 +169,7 @@ func (m *StateModule) StateMinerInfo(ctx context.Context, actor address.Address, } ret := api.MinerInfo{ + MinerID: actor, Owner: info.Owner, Worker: info.Worker, ControlAddresses: info.ControlAddresses, diff --git a/provider/address.go b/provider/address.go index f69ca3fac..298fe8784 100644 --- a/provider/address.go +++ b/provider/address.go @@ -5,47 +5,60 @@ import ( "github.com/filecoin-project/go-address" + "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/node/config" "github.com/filecoin-project/lotus/storage/ctladdr" ) -func AddressSelector(addrConf *config.LotusProviderAddresses) func() (*ctladdr.AddressSelector, error) { - return func() (*ctladdr.AddressSelector, error) { - as := &ctladdr.AddressSelector{} +func AddressSelector(addrConf []config.LotusProviderAddresses) func() (*ctladdr.MultiAddressSelector, error) { + return func() (*ctladdr.MultiAddressSelector, error) { + as := &ctladdr.MultiAddressSelector{ + MinerMap: make(map[address.Address]api.AddressConfig), + } if addrConf == nil { return as, nil } - as.DisableOwnerFallback = addrConf.DisableOwnerFallback - as.DisableWorkerFallback = addrConf.DisableWorkerFallback + for _, addrConf := range addrConf { + for _, minerID := range addrConf.MinerAddresses { + tmp := api.AddressConfig{ + DisableOwnerFallback: addrConf.DisableOwnerFallback, + DisableWorkerFallback: addrConf.DisableWorkerFallback, + } - for _, s := range addrConf.PreCommitControl { - addr, err := address.NewFromString(s) - if err != nil { - return nil, xerrors.Errorf("parsing precommit control address: %w", err) + for _, s := range addrConf.PreCommitControl { + addr, err := address.NewFromString(s) + if err != nil { + return nil, xerrors.Errorf("parsing precommit control address: %w", err) + } + + tmp.PreCommitControl = append(tmp.PreCommitControl, addr) + } + + for _, s := range addrConf.CommitControl { + addr, err := address.NewFromString(s) + if err != nil { + return nil, xerrors.Errorf("parsing commit control address: %w", err) + } + + tmp.CommitControl = append(tmp.CommitControl, addr) + } + + for _, s := range addrConf.TerminateControl { + addr, err := address.NewFromString(s) + if err != nil { + return nil, xerrors.Errorf("parsing terminate control address: %w", err) + } + + tmp.TerminateControl = append(tmp.TerminateControl, addr) + } + a, err := address.NewFromString(minerID) + if err != nil { + return nil, xerrors.Errorf("parsing miner address %s: %w", minerID, err) + } + as.MinerMap[a] = tmp } - - as.PreCommitControl = append(as.PreCommitControl, addr) } - - for _, s := range addrConf.CommitControl { - addr, err := address.NewFromString(s) - if err != nil { - return nil, xerrors.Errorf("parsing commit control address: %w", err) - } - - as.CommitControl = append(as.CommitControl, addr) - } - - for _, s := range addrConf.TerminateControl { - addr, err := address.NewFromString(s) - if err != nil { - return nil, xerrors.Errorf("parsing terminate control address: %w", err) - } - - as.TerminateControl = append(as.TerminateControl, addr) - } - return as, nil } } diff --git a/provider/builder.go b/provider/builder.go index 81a1a7a0a..82781d211 100644 --- a/provider/builder.go +++ b/provider/builder.go @@ -21,7 +21,7 @@ import ( func WindowPostScheduler(ctx context.Context, fc config.LotusProviderFees, pc config.ProvingConfig, api api.FullNode, verif storiface.Verifier, lw *sealer.LocalWorker, sender *lpmessage.Sender, - as *ctladdr.AddressSelector, addresses []dtypes.MinerAddress, db *harmonydb.DB, + as *ctladdr.MultiAddressSelector, addresses []dtypes.MinerAddress, db *harmonydb.DB, stor paths.Store, idx paths.SectorIndex, max int) (*lpwindow.WdPostTask, *lpwindow.WdPostSubmitTask, *lpwindow.WdPostRecoverDeclareTask, error) { chainSched := chainsched.New(api) diff --git a/provider/lpwindow/recover_task.go b/provider/lpwindow/recover_task.go index 12f8522b5..bd88db4dc 100644 --- a/provider/lpwindow/recover_task.go +++ b/provider/lpwindow/recover_task.go @@ -34,7 +34,7 @@ type WdPostRecoverDeclareTask struct { faultTracker sealer.FaultTracker maxDeclareRecoveriesGasFee types.FIL - as *ctladdr.AddressSelector + as *ctladdr.MultiAddressSelector actors []dtypes.MinerAddress startCheckTF promise.Promise[harmonytask.AddTaskFunc] @@ -61,7 +61,7 @@ func NewWdPostRecoverDeclareTask(sender *lpmessage.Sender, db *harmonydb.DB, api WdPostRecoverDeclareTaskApi, faultTracker sealer.FaultTracker, - as *ctladdr.AddressSelector, + as *ctladdr.MultiAddressSelector, pcs *chainsched.ProviderChainSched, maxDeclareRecoveriesGasFee types.FIL, diff --git a/provider/lpwindow/submit_task.go b/provider/lpwindow/submit_task.go index 72f2499f6..6bb92ea42 100644 --- a/provider/lpwindow/submit_task.go +++ b/provider/lpwindow/submit_task.go @@ -47,12 +47,12 @@ type WdPostSubmitTask struct { api WdPoStSubmitTaskApi maxWindowPoStGasFee types.FIL - as *ctladdr.AddressSelector + as *ctladdr.MultiAddressSelector submitPoStTF promise.Promise[harmonytask.AddTaskFunc] } -func NewWdPostSubmitTask(pcs *chainsched.ProviderChainSched, send *lpmessage.Sender, db *harmonydb.DB, api WdPoStSubmitTaskApi, maxWindowPoStGasFee types.FIL, as *ctladdr.AddressSelector) (*WdPostSubmitTask, error) { +func NewWdPostSubmitTask(pcs *chainsched.ProviderChainSched, send *lpmessage.Sender, db *harmonydb.DB, api WdPoStSubmitTaskApi, maxWindowPoStGasFee types.FIL, as *ctladdr.MultiAddressSelector) (*WdPostSubmitTask, error) { res := &WdPostSubmitTask{ sender: send, db: db, @@ -248,7 +248,7 @@ type MsgPrepAPI interface { StateLookupID(context.Context, address.Address, types.TipSetKey) (address.Address, error) } -func preparePoStMessage(w MsgPrepAPI, as *ctladdr.AddressSelector, maddr address.Address, msg *types.Message, maxFee abi.TokenAmount) (*types.Message, *api.MessageSendSpec, error) { +func preparePoStMessage(w MsgPrepAPI, as *ctladdr.MultiAddressSelector, maddr address.Address, msg *types.Message, maxFee abi.TokenAmount) (*types.Message, *api.MessageSendSpec, error) { mi, err := w.StateMinerInfo(context.Background(), maddr, types.EmptyTSK) if err != nil { return nil, nil, xerrors.Errorf("error getting miner info: %w", err) diff --git a/storage/ctladdr/multiaddresses.go b/storage/ctladdr/multiaddresses.go new file mode 100644 index 000000000..54a3433e0 --- /dev/null +++ b/storage/ctladdr/multiaddresses.go @@ -0,0 +1,76 @@ +package ctladdr + +import ( + "context" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/go-state-types/big" + + "github.com/filecoin-project/lotus/api" + "github.com/filecoin-project/lotus/chain/types" +) + +type MultiAddressSelector struct { + MinerMap map[address.Address]api.AddressConfig +} + +func (as *MultiAddressSelector) AddressFor(ctx context.Context, a NodeApi, mi api.MinerInfo, use api.AddrUse, goodFunds, minFunds abi.TokenAmount) (address.Address, abi.TokenAmount, error) { + if as == nil { + // should only happen in some tests + log.Warnw("smart address selection disabled, using worker address") + return mi.Worker, big.Zero(), nil + } + + tmp := as.MinerMap[mi.MinerID] + + var addrs []address.Address + switch use { + case api.PreCommitAddr: + addrs = append(addrs, tmp.PreCommitControl...) + case api.CommitAddr: + addrs = append(addrs, tmp.CommitControl...) + case api.TerminateSectorsAddr: + addrs = append(addrs, tmp.TerminateControl...) + case api.DealPublishAddr: + addrs = append(addrs, tmp.DealPublishControl...) + default: + defaultCtl := map[address.Address]struct{}{} + for _, a := range mi.ControlAddresses { + defaultCtl[a] = struct{}{} + } + delete(defaultCtl, mi.Owner) + delete(defaultCtl, mi.Worker) + + configCtl := append([]address.Address{}, tmp.PreCommitControl...) + configCtl = append(configCtl, tmp.CommitControl...) + configCtl = append(configCtl, tmp.TerminateControl...) + configCtl = append(configCtl, tmp.DealPublishControl...) + + for _, addr := range configCtl { + if addr.Protocol() != address.ID { + var err error + addr, err = a.StateLookupID(ctx, addr, types.EmptyTSK) + if err != nil { + log.Warnw("looking up control address", "address", addr, "error", err) + continue + } + } + + delete(defaultCtl, addr) + } + + for a := range defaultCtl { + addrs = append(addrs, a) + } + } + + if len(addrs) == 0 || !tmp.DisableWorkerFallback { + addrs = append(addrs, mi.Worker) + } + if !tmp.DisableOwnerFallback { + addrs = append(addrs, mi.Owner) + } + + return pickAddress(ctx, a, mi, goodFunds, minFunds, addrs) +} From 6a6b03ef4071eebf0e777d3ba6ae0cfb34dc7d99 Mon Sep 17 00:00:00 2001 From: Aayush Date: Wed, 24 Jan 2024 18:35:46 -0500 Subject: [PATCH 075/394] feat: drand: refactor round verification --- chain/beacon/beacon.go | 17 +++++++++++++---- chain/beacon/drand/drand.go | 7 ++----- chain/sync.go | 10 +++++----- 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/chain/beacon/beacon.go b/chain/beacon/beacon.go index aa76bcffe..ff75b8579 100644 --- a/chain/beacon/beacon.go +++ b/chain/beacon/beacon.go @@ -79,11 +79,21 @@ func ValidateBlockValues(bSchedule Schedule, nv network.Version, h *types.BlockH return xerrors.Errorf("expected to have beacon entries in this block, but didn't find any") } + // Verify that the last beacon entry's round corresponds to the round we expect last := h.BeaconEntries[len(h.BeaconEntries)-1] if last.Round != maxRound { return xerrors.Errorf("expected final beacon entry in block to be at round %d, got %d", maxRound, last.Round) } + // Verify that all other entries' rounds are as expected for the epochs in between parentEpoch and h.Height + for i, e := range h.BeaconEntries { + correctRound := b.MaxBeaconRoundForEpoch(nv, parentEpoch+abi.ChainEpoch(i)+1) + if e.Round != correctRound { + return xerrors.Errorf("unexpected beacon round %d, expected %d for epoch %d", e.Round, correctRound, parentEpoch+abi.ChainEpoch(i)) + } + } + + // Verify the beacon entries themselves for i, e := range h.BeaconEntries { if err := b.VerifyEntry(e, prevEntry); err != nil { return xerrors.Errorf("beacon entry %d (%d - %x (%d)) was invalid: %w", i, e.Round, e.Data, len(e.Data), err) @@ -132,10 +142,10 @@ func BeaconEntriesForBlock(ctx context.Context, bSchedule Schedule, nv network.V prev.Round = maxRound - 1 } - cur := maxRound var out []types.BeaconEntry - for cur > prev.Round { - rch := beacon.Entry(ctx, cur) + for currEpoch := epoch; currEpoch > parentEpoch; currEpoch-- { + currRound := beacon.MaxBeaconRoundForEpoch(nv, currEpoch) + rch := beacon.Entry(ctx, currRound) select { case resp := <-rch: if resp.Err != nil { @@ -143,7 +153,6 @@ func BeaconEntriesForBlock(ctx context.Context, bSchedule Schedule, nv network.V } out = append(out, resp.Entry) - cur = resp.Entry.Round - 1 case <-ctx.Done(): return nil, xerrors.Errorf("context timed out waiting on beacon entry to come back for epoch %d: %w", epoch, ctx.Err()) } diff --git a/chain/beacon/drand/drand.go b/chain/beacon/drand/drand.go index 5825fa691..1b78daadd 100644 --- a/chain/beacon/drand/drand.go +++ b/chain/beacon/drand/drand.go @@ -170,10 +170,6 @@ func (db *DrandBeacon) VerifyEntry(curr types.BeaconEntry, prev types.BeaconEntr return nil } - if curr.Round != prev.Round+1 { - return xerrors.Errorf("invalid beacon entry: cur (%d) != prev (%d) + 1", curr.Round, prev.Round) - } - if be := db.getCachedValue(curr.Round); be != nil { if !bytes.Equal(curr.Data, be.Data) { return xerrors.New("invalid beacon value, does not match cached good value") @@ -190,7 +186,8 @@ func (db *DrandBeacon) VerifyEntry(curr types.BeaconEntry, prev types.BeaconEntr if err == nil { db.cacheValue(curr) } - return err + + return nil } func (db *DrandBeacon) MaxBeaconRoundForEpoch(nv network.Version, filEpoch abi.ChainEpoch) uint64 { diff --git a/chain/sync.go b/chain/sync.go index 4dccc2036..ded6ab1b5 100644 --- a/chain/sync.go +++ b/chain/sync.go @@ -704,25 +704,25 @@ func (syncer *Syncer) collectHeaders(ctx context.Context, incoming *types.TipSet } { - // ensure consistency of beacon entires + // ensure consistency of beacon entries targetBE := incoming.Blocks()[0].BeaconEntries sorted := sort.SliceIsSorted(targetBE, func(i, j int) bool { return targetBE[i].Round < targetBE[j].Round }) if !sorted { - syncer.bad.Add(incoming.Cids()[0], NewBadBlockReason(incoming.Cids(), "wrong order of beacon entires")) - return nil, xerrors.Errorf("wrong order of beacon entires") + syncer.bad.Add(incoming.Cids()[0], NewBadBlockReason(incoming.Cids(), "wrong order of beacon entries")) + return nil, xerrors.Errorf("wrong order of beacon entries") } for _, bh := range incoming.Blocks()[1:] { if len(targetBE) != len(bh.BeaconEntries) { // cannot mark bad, I think @Kubuxu - return nil, xerrors.Errorf("tipset contained different number for beacon entires") + return nil, xerrors.Errorf("tipset contained different number for beacon entries") } for i, be := range bh.BeaconEntries { if targetBE[i].Round != be.Round || !bytes.Equal(targetBE[i].Data, be.Data) { // cannot mark bad, I think @Kubuxu - return nil, xerrors.Errorf("tipset contained different beacon entires") + return nil, xerrors.Errorf("tipset contained different beacon entries") } } From 6cbeb9aad69a0f9776ac79f8ba42aea09dc61f23 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 26 Jan 2024 08:50:32 -0800 Subject: [PATCH 076/394] fix: sync: atomically switch chains when checkpointing (#11595) We can now atomically switch chains when checkpointing. Previously, we'd call `SetHead` followed by `SetCheckpoint`. Unfortunately, that's not atomic and the "head" could have reverted before we called `SetCheckpoint` (causing the latter to fail). Now, we just call `SetCheckpoint` and let `SetCheckpoint` adjust our head. This changes the behavior of `ChainStore.SetCheckpoint`, but `Syncer.SyncCheckpoint` is the only caller anyways. --- chain/checkpoint.go | 32 +++++++++----------------------- chain/store/checkpoint_test.go | 21 ++++++--------------- chain/store/store.go | 15 ++++++++------- 3 files changed, 23 insertions(+), 45 deletions(-) diff --git a/chain/checkpoint.go b/chain/checkpoint.go index f9b0bb4eb..2810b1e4a 100644 --- a/chain/checkpoint.go +++ b/chain/checkpoint.go @@ -24,8 +24,15 @@ func (syncer *Syncer) SyncCheckpoint(ctx context.Context, tsk types.TipSetKey) e ts = tss[0] } - if err := syncer.switchChain(ctx, ts); err != nil { - return xerrors.Errorf("failed to switch chain when syncing checkpoint: %w", err) + hts := syncer.ChainStore().GetHeaviestTipSet() + if hts.Equals(ts) { + // Current head, no need to switch. + } else if anc, err := syncer.store.IsAncestorOf(ctx, ts, hts); err != nil { + return xerrors.Errorf("failed to walk the chain when checkpointing: %w", err) + } else if anc { + // New checkpoint is on the current chain, we definitely have the tipsets. + } else if err := syncer.collectChain(ctx, ts, hts, true); err != nil { + return xerrors.Errorf("failed to collect chain for checkpoint: %w", err) } if err := syncer.ChainStore().SetCheckpoint(ctx, ts); err != nil { @@ -34,24 +41,3 @@ func (syncer *Syncer) SyncCheckpoint(ctx context.Context, tsk types.TipSetKey) e return nil } - -func (syncer *Syncer) switchChain(ctx context.Context, ts *types.TipSet) error { - hts := syncer.ChainStore().GetHeaviestTipSet() - if hts.Equals(ts) { - return nil - } - - if anc, err := syncer.store.IsAncestorOf(ctx, ts, hts); err == nil && anc { - return nil - } - - // Otherwise, sync the chain and set the head. - if err := syncer.collectChain(ctx, ts, hts, true); err != nil { - return xerrors.Errorf("failed to collect chain for checkpoint: %w", err) - } - - if err := syncer.ChainStore().SetHead(ctx, ts); err != nil { - return xerrors.Errorf("failed to set the chain head: %w", err) - } - return nil -} diff --git a/chain/store/checkpoint_test.go b/chain/store/checkpoint_test.go index c5dff94a8..a30c4d860 100644 --- a/chain/store/checkpoint_test.go +++ b/chain/store/checkpoint_test.go @@ -44,22 +44,14 @@ func TestChainCheckpoint(t *testing.T) { head := cs.GetHeaviestTipSet() require.True(t, head.Equals(checkpointParents)) - // Try to set the checkpoint in the future, it should fail. + // Checkpoint into the future. err = cs.SetCheckpoint(ctx, checkpoint) - require.Error(t, err) - - // Then move the head back. - err = cs.SetHead(ctx, checkpoint) require.NoError(t, err) - // Verify it worked. + // And verify that it worked. head = cs.GetHeaviestTipSet() require.True(t, head.Equals(checkpoint)) - // And checkpoint it. - err = cs.SetCheckpoint(ctx, checkpoint) - require.NoError(t, err) - // Let the second miner miner mine a fork last = checkpointParents for i := 0; i < 4; i++ { @@ -85,11 +77,10 @@ func TestChainCheckpoint(t *testing.T) { head = cs.GetHeaviestTipSet() require.True(t, head.Equals(last)) - // Setting a checkpoint on the other fork should fail. + // We should switch back if we checkpoint again. err = cs.SetCheckpoint(ctx, checkpoint) - require.Error(t, err) - - // Setting a checkpoint on this fork should succeed. - err = cs.SetCheckpoint(ctx, checkpointParents) require.NoError(t, err) + + head = cs.GetHeaviestTipSet() + require.True(t, head.Equals(checkpoint)) } diff --git a/chain/store/store.go b/chain/store/store.go index f2826fc2f..b1431c2ee 100644 --- a/chain/store/store.go +++ b/chain/store/store.go @@ -793,9 +793,12 @@ func (cs *ChainStore) removeCheckpoint(ctx context.Context) error { return nil } -// SetCheckpoint will set a checkpoint past which the chainstore will not allow forks. +// SetCheckpoint will set a checkpoint past which the chainstore will not allow forks. If the new +// checkpoint is not an ancestor of the current head, head will be set to the new checkpoint. // -// NOTE: Checkpoints cannot be set beyond ForkLengthThreshold epochs in the past. +// NOTE: Checkpoints cannot be set beyond ForkLengthThreshold epochs in the past, but can be set +// arbitrarily far into the future. +// NOTE: The new checkpoint must already be synced. func (cs *ChainStore) SetCheckpoint(ctx context.Context, ts *types.TipSet) error { tskBytes, err := json.Marshal(ts.Key()) if err != nil { @@ -805,10 +808,6 @@ func (cs *ChainStore) SetCheckpoint(ctx context.Context, ts *types.TipSet) error cs.heaviestLk.Lock() defer cs.heaviestLk.Unlock() - if ts.Height() > cs.heaviest.Height() { - return xerrors.Errorf("cannot set a checkpoint in the future") - } - // Otherwise, this operation could get _very_ expensive. if cs.heaviest.Height()-ts.Height() > build.ForkLengthThreshold { return xerrors.Errorf("cannot set a checkpoint before the fork threshold") @@ -821,7 +820,9 @@ func (cs *ChainStore) SetCheckpoint(ctx context.Context, ts *types.TipSet) error } if !anc { - return xerrors.Errorf("cannot mark tipset as checkpoint, since it isn't in the main-chain: %w", err) + if err := cs.takeHeaviestTipSet(ctx, ts); err != nil { + return xerrors.Errorf("failed to switch chains when setting checkpoint: %w", err) + } } } err = cs.metadataDs.Put(ctx, checkpointKey, tskBytes) From 4d73febaf7ffa6458a48742a553260c0aadc5220 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 29 Jan 2024 11:17:05 -0800 Subject: [PATCH 077/394] chore: sync: cleanup sync serve and reduce log noise (#11543) * chore: cleanup sync serve and reduce log noise 1. Demote a noisy blocksync request error to debug. All this warning means is that someone is requesting a tipset we don't have. 2. Add a separate warning if we fail to collect a chain. If we have the tipsets but fail to collect the chain, something is actually wrong. 3. Fix a TODO and return a single CompactedMessages rather than 4 separate values. * generally reduce the warning to info It turns out we do fail to gather messages frequently as well, likely because we have written the tipsets but haven't fetched the messages... --- chain/exchange/server.go | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/chain/exchange/server.go b/chain/exchange/server.go index 03dcf0ed7..ac3454c90 100644 --- a/chain/exchange/server.go +++ b/chain/exchange/server.go @@ -137,7 +137,7 @@ func (s *server) serviceRequest(ctx context.Context, req *validatedRequest) (*Re chain, err := collectChainSegment(ctx, s.cs, req) if err != nil { - log.Warn("block sync request: collectChainSegment failed: ", err) + log.Info("block sync request: collectChainSegment failed: ", err) return &Response{ Status: InternalError, ErrorMessage: err.Error(), @@ -171,17 +171,11 @@ func collectChainSegment(ctx context.Context, cs *store.ChainStore, req *validat } if req.options.IncludeMessages { - bmsgs, bmincl, smsgs, smincl, err := gatherMessages(ctx, cs, ts) + bst.Messages, err = gatherMessages(ctx, cs, ts) if err != nil { return nil, xerrors.Errorf("gather messages failed: %w", err) } - // FIXME: Pass the response to `gatherMessages()` and set all this there. - bst.Messages = &CompactedMessages{} - bst.Messages.Bls = bmsgs - bst.Messages.BlsIncludes = bmincl - bst.Messages.Secpk = smsgs - bst.Messages.SecpkIncludes = smincl } bstips = append(bstips, &bst) @@ -196,16 +190,16 @@ func collectChainSegment(ctx context.Context, cs *store.ChainStore, req *validat } } -func gatherMessages(ctx context.Context, cs *store.ChainStore, ts *types.TipSet) ([]*types.Message, [][]uint64, []*types.SignedMessage, [][]uint64, error) { +func gatherMessages(ctx context.Context, cs *store.ChainStore, ts *types.TipSet) (*CompactedMessages, error) { + msgs := new(CompactedMessages) blsmsgmap := make(map[cid.Cid]uint64) secpkmsgmap := make(map[cid.Cid]uint64) - var secpkincl, blsincl [][]uint64 var blscids, secpkcids []cid.Cid for _, block := range ts.Blocks() { bc, sc, err := cs.ReadMsgMetaCids(ctx, block.Messages) if err != nil { - return nil, nil, nil, nil, err + return nil, err } // FIXME: DRY. Use `chain.Message` interface. @@ -220,7 +214,7 @@ func gatherMessages(ctx context.Context, cs *store.ChainStore, ts *types.TipSet) bmi = append(bmi, i) } - blsincl = append(blsincl, bmi) + msgs.BlsIncludes = append(msgs.BlsIncludes, bmi) smi := make([]uint64, 0, len(sc)) for _, m := range sc { @@ -233,18 +227,19 @@ func gatherMessages(ctx context.Context, cs *store.ChainStore, ts *types.TipSet) smi = append(smi, i) } - secpkincl = append(secpkincl, smi) + msgs.SecpkIncludes = append(msgs.SecpkIncludes, smi) } - blsmsgs, err := cs.LoadMessagesFromCids(ctx, blscids) + var err error + msgs.Bls, err = cs.LoadMessagesFromCids(ctx, blscids) if err != nil { - return nil, nil, nil, nil, err + return nil, err } - secpkmsgs, err := cs.LoadSignedMessagesFromCids(ctx, secpkcids) + msgs.Secpk, err = cs.LoadSignedMessagesFromCids(ctx, secpkcids) if err != nil { - return nil, nil, nil, nil, err + return nil, err } - return blsmsgs, blsincl, secpkmsgs, secpkincl, nil + return msgs, nil } From 1f3abf499519dfe3243a20b85fc946c35eedfe62 Mon Sep 17 00:00:00 2001 From: "Andrew Jackson (Ajax)" Date: Mon, 29 Jan 2024 18:22:50 -0600 Subject: [PATCH 078/394] also-sql naming --- build/openrpc/full.json.gz | Bin 34608 -> 34617 bytes build/openrpc/gateway.json.gz | Bin 11872 -> 11884 bytes documentation/en/api-v0-methods.md | 1 + documentation/en/api-v1-unstable-methods.md | 1 + .../en/default-lotus-provider-config.toml | 19 +----------------- go.mod | 7 +++++++ go.sum | 8 ++++++++ ...0230706.sql => 20230706-itest_scratch.sql} | 0 ...20230712.sql => 20230712-sector_index.sql} | 0 .../{20230719.sql => 20230719-harmony.sql} | 0 .../sql/{20230823.sql => 20230823-wdpost.sql} | 0 .../sql/{20230919.sql => 20230919-config.sql} | 0 ...{20231103.sql => 20231103-chain_sends.sql} | 0 ...20231110.sql => 20231110-mining_tasks.sql} | 0 ... => 20231113-harmony_taskhistory_oops.sql} | 0 node/config/doc_gen.go | 4 ++-- 16 files changed, 20 insertions(+), 20 deletions(-) rename lib/harmony/harmonydb/sql/{20230706.sql => 20230706-itest_scratch.sql} (100%) rename lib/harmony/harmonydb/sql/{20230712.sql => 20230712-sector_index.sql} (100%) rename lib/harmony/harmonydb/sql/{20230719.sql => 20230719-harmony.sql} (100%) rename lib/harmony/harmonydb/sql/{20230823.sql => 20230823-wdpost.sql} (100%) rename lib/harmony/harmonydb/sql/{20230919.sql => 20230919-config.sql} (100%) rename lib/harmony/harmonydb/sql/{20231103.sql => 20231103-chain_sends.sql} (100%) rename lib/harmony/harmonydb/sql/{20231110.sql => 20231110-mining_tasks.sql} (100%) rename lib/harmony/harmonydb/sql/{20231113.sql => 20231113-harmony_taskhistory_oops.sql} (100%) diff --git a/build/openrpc/full.json.gz b/build/openrpc/full.json.gz index 3c30e81a9427867a286d476b5eb52110c995bcae..a08ffb3f05df7d68dcc3304ffc5c2912d69cf863 100644 GIT binary patch delta 8033 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 delta 8075 zcmV;6A9Uckjsmcb0)l^PVAGTWm|tM7z8x2jyDYaDo-a2j|WO8WB~Etw_fV@ zQn#17KSt{AHDOnr!+46gN!8a%-sVWGCuwt}R?692=>`d#E9Wm?i~Vzuta;9q%{rM& z(Rs>(m@cGE=8Q^XjZSDE5TKS;ase5u5)t1RXs2>I5| z{_g&+crLm^8I0uL7zOB9;l6YODJy>|+PJb`P>e?yg_2o4E(Yl?^BHHzblI~rI7E@^ zm4tOu{=qaM6$$eX^h~~taeyvStY1pWZ>S&BD1sctf)n{dF;aN^eV~?<7!-wpSJC_# zWc>3y6yLVMMg-X;|Hur1LKQsR`u@E;&_MfZw!#|p%yV5DFmJq}^t-vhLgf*gABfY7jS%s)CX#N&@a(h&vF0Px8G%S&!&@pXik5J2Vy^v zy`!Uz!?)@e3Om(m)hWw55XmK$&-7tuS=Wz5wXYhATk4zkwwinKiC~UlbQz&=jP$e9 z`#B0Y3Qyqj3N2t97M`1(-mY_n<`E1~sD|j>7^S?JH$`fy405h@xA`@PUor+ zQ|Y_Mjf=dJU=Oxa&Mfn_9u|LAJpX;Yb>#BbZA+ps5n7PJK6yC`bJe%Giiy~kgf+|E z)W%dyxE4?4h~EjLxTxCHHuB9aD_d^4mG$*%(X)QI(lfgWb6Y9tdLLX^PJMttJ#2wbt(-j^Hpt z>qYceky=lXswYQy{E2W!5n5=!!75-AZ)*oA_tsgy78%Xmp`x*LRM8QDN^btbOjLlf z4Fr&YA(EgqVGI`p&=`NPIU;?Ke=k~BiB@~!*9>I((&3wLy{L;(`#Vh_A#!P-AQ(oN zw1xG`(^%KSY6!WB&$QV_YpN;LQfk_La}}2vV>Wn~l$FXB3c|F+h{o2HC@DUBsn|=! zm4;F?$0`h?)+?tGyrhg@C&L+L3{xV_lY{MVgN+l+<_QFg)mZ zEs4)KL0}{bE>o{r?ka3A_j|eTBKKF@8Csedgv5VQn(WpHm2O9OVJWJw2^C<- zxQR$nvI!!`Qv~7?;ZO1 z-+caI{~e1z|DU7a>;1>0_t!fg*(v>Sb043d-Tm?3e6)Wt*pQf}Vq(|k$oPs$NEes% zny0z2bpV45Z<7IvyL-E@58mv)-Q9nis{mszzn+P-I^5flU1f-{gh|!rG=g&`zE0X9 z5@Irz<7gI|HBLu}pb-wR`VLi(Z%{1b;Qo_P4t4mzXNE%icPR?ud)jaXr36%6+UI%3|QyDQc2P03}Z)RUdHH{ z&Ssda6U=S|R6r5AdL zs-1svj>myw%x^_VaK}Nwg5(t}UOOv=23P2*p9xd!;IJ!pJhj*#782{bO}B{2w7$U) z3FGt$#4LPipXNG9R8!8m(wi#!fQ28U`0O`={|RBAsMvN9q*>P> z5_MB$G9DV^7t&%am6u#see@NJKRb+Jo;`my`4Ll<#Dq1xP-R5Tj|W3h*y{pAp%?D! z#a*PUq9X9Ze&;lX@1iI@jO(BaY4BPWpI)JW-XXEW7vG#&_hikp{Owuwbj^EH$TAxE zo#*X7`qnKBhBwI1I(%0Qc^`99*sb3*BDTp*L|!FYW1|-J*Sz485pvv^Wzw#0Z?%7z zez>|%6%Y9^Yw<)!jr1PWe&_N<|EXV&`FAd66RX1EzZ+L@akS(}|CXM%|0FQNeEBZ? zf7w%+c$LGbMZJ%Zonw-_2JB_H$knfME#y1oLAk`R_db&wg!R1)`62#!d-B{&`fnSN z_2l$3_RA9_<@S5J{`Ofz@@X0M>$rb>SgU@+*mP~y7xPgz1wz$UZ_e+{`Mo*6H|O`} z{HLxr_vZX{l%9i?NN<e0VHIoR8L@fTXBwJOv76ONtjb>OyyTGili~Z5XY3T9`8!67p^Of z64@#%8zpk@T$I1#B#sf`#rSJ6cO}SY`s@=4(Y=aw{&S8pjqEt0EQ2~bp1>$V!~#E) z=IG;u)6pn>_Em<)$wsoP)RU5LDSxz9jgM2&+_!M4ksDm+3Kaw420kWdfJ;#|L!9oY z+EdLbM6%{f``0y7$hl@|#=6(c&*&EyoB3vICAyb!9jyhbkP&&53%6PGKj6}BuCK0| zjm6>W%eQgr&Fe4V_8Lon0r&C6==MsaFOAFD0aRjvl8-=zL%eBQd}@r|Lj+14QqYq= za2kJ+69KNoPJF4SzTz}_upqF5J_hWuw*X4Z*KG8|)uSd_vOb}4LI2#ce0L?xm|7bO z@Mm2)E~#_?t)Sz&e8jeFdKJimrrS~%&iBz(8MfK8O>|mV(^0DSVq(EGYLU(mmnj{M z!H;#Aqw(jG$`dh_-D%>$Id+J+ z`8fR78++LB#*!H(V}LnWK$iLxBm|UzU;;52 z+!E2s4DksK!2*9r=H4%WM2#?DaH)b&weoA|Eg&PmXnL9{LTkAaHQ`*W>llm8PnW|C zM3hcZ2$H#C9ghr4X)|C9*{c}MV;mS#l)RGsXT=2TL!`$Xh2RyyL{O6H8yV6;w4!S$ z1lB~_JZIx$totqoXa;#Ok;TPhxw-1@iDvZu2xIssbAT z#DmS>`a|3UD=v-No|f>dPi2++cb=A5GD1`!hQxmgg@B=G^h%W(ib)Y@8d@P1LN}$h z2|h`YG@|hggh3_ts$&ZrL^QwS*OgubVA#aqO3bb@|=wYF>{)iVKz6^&YST*rn6ig4Js8# zQSg6@5HDK4{Q%h+o?$M1mmeT|kI*rkXD=^fG{ebEKRB1WL%#}vFHQ2b4TUj*&A0YcI(@h)FAR9QQOj0~oQoXxxn=&tl z{0K7i9-$-ESMlV}I~1QXj%QMyTQAgYVML$|gvuV>;Cbqj9VNt2mM}xA7Yd^3RBm~g zlA9Q+!-k$_#krK9Y4&t-oFM9*lqPCQX46poxY){}o= z0^_k>;}=SFDA}X-aB;XA%UKwxf0+7*^Nqv(t@J-3g07RH(5zB%(U1F&9(R&N(RZYKOz|W!p6LP!AH0^t$Gfc=sYvfqUaB5U*yHD#Stn0H4S5A$!Pu>5%&{DIE$4J! zw+d{n7)PO(p3fERY?b(#S=93OHR*q3OV(vzj9_rO{j4QN%X~;V;o{O>CT^;N1cb2j z9UCnY*N5QC#dcFa6MdN!D6NR9ik9}{WyG{JnHIDDvrZu9 zSJ6_>{8ndPoAPcpRlK}&fmNt?{y|Af&-1Nksf3-ZAB9B>LEAy9u62w9q6M0qL{Bw6MIvarao%)cKXJXhBkMJ4SZSxiuj zjGxF@#>o&9sqq`Rrc$DVHDAYiK71B%#9%`OX0E!$}S!?d!z@XK;V{0(iBMX z)w_avS5WT?>Rmxy+`-Fl;SUx;Dep*i01>kHNC+3zsVmGLQIWaAdgbIw>)PL<_uQxD zU{Z~rNaKz;s7WE(+4_IzN<0oWKI-c`ZLljprdNMl?3gXhIM&zgPkywz@lKU1@B2^vqLv0O+&aCFrarhx2H@LYK$VQl}m96G}XpBc_7I zhFt6JDj-p#=g8w05_Mi`;>#ZW?6LRK9~UlPz1qu~S#3ENHSK>-a)W2tmH$&1N6U6< zg0AX>^sF0wanL?*x0@i1)hGObcqIsi$1bEvJoFai1MHJytm_RZ>#x`DW4N*JH>L5~ z63b^*@~BO4rR$O;iU1~A2)Ev{BpGZ6peuW58sRMpY+C!^3@&AK_YeUX1W61z5_DzC z2tk|;K*CV9&H;Zo1!Kh1inj!1@8rhXZ=3zL`GU61CQzZZAccBFp`R5|$RF9ThY{*w zgnAgErv@YRGoJ!>nh^PQPEHteyskAWCGyZGfioKTn4(g?x;_Zl9rij3WE_n!30azO z?Z4*f3+cSg9|m4@2H9SxewjM8S?kRTwN=VRn@+P*$_sxkneg)mqs}Nf24( zuPK>8b!zDLTaiuZp|_*7F@FnHj?d1wr@|okZBaSSY@1d3#?feXOH!*9j%1(*p&CO0 zk8;KN|=^N(b=-Nx6TbEo|vyn@>;G$_%ofHuhr-V!o!N z78V)nEB_E9j*l6pFqeFqtBN-&Pa;{7F(77cNE0yMkW*ZQ6OLhFQbDR6S0!X)s>omhHQ7~Zl`i7^`5`(NJV6v)sW4>+` z@a})+Es=^YR@%C}Dga`n`U!^mPDesGo*{q*A0rqbp`=2kg$6Y25MZ-cXxtT=(ln`v z-7lALQCQyN=!?APqW9rfm5k`cIM@h>RW>u8d#AcSq zRPD~B3ly*3`YL$n&R|s2LkCRHO1^A!Q3yE%0n9E@s?ExbQ{FnH3_`I+zV2l*}kCd)izNLVY5e^Xv5D3wnPd0mSr5;?V2UmLH z;7UDC(9^&PDl2Yk|9Y@!q`jR72}kPDje2yW9^I%%H|o)idUPXw96Z>nL)3p=lHgcq zibpW?KMq=zR~!d_1HY=z+jz#a=vANbZZ~c>ybgJJ4jWAKcGFUIW&n@U1cv!=@gWRN zem{+7J23V%mG3V`kWtc|xvSR@pIA~cO`tfo>0<|bR4ygWGt2ycuw4hHW|rBUVp2}W z<}7ezb!UH~y0_9Ka3sBIk<))d*ck2{#EoiLh!$pq4HF_JNME9GAn(7Vc}s=Fq2w11 z&>f1!N}K|?fJ-on=?q}rM*%*D+4>@h%2B!cp(Uo%qf!+8Czl+?EH7lgXZ{XM8)Ay?mAF@z4-X)8rdZD4(vt98)5-Y>C^3rRs zg&bHZB>-D8j%I%HpwiM+Kis)jE|x9{frLaY_4_N0rzrk(qHgLRK_*QM$2iof83!9j zQaYMn#=f#8j_WWUtK)yVTls4)tu5OH3y*ve%h>p>t?gIx|C_C?L;3&zYz#I|Rr1v8 zm#GmauceFi`{n%Id>&!3q`Ywla^uu4U85OHPY@-QK}?8RD8?j&9KGUrhN{d$ZLM9U z8)_UnV;`w+<;r0;NBo8%o0r`rn6&1?i@UpauwQp~O$d)z~4 zC$h{r8?R6ZErMMrg?av(6+^q}01_}nAQ74nr+~><00M#*(GmnPl3sjea#B9w&09hc zUx-5%v$%O8t)rhU)IA%#fJNZj6JeTA*L9-s>y0W$%O#Oc4qM z6X`YEub3Rugs6Wc<|`^6AsA3{N_dPlOIK9pN))@!3FNS{-nW5h_W8 z2OILQqa{a`sh0(TODHz!PW4GeGKaNT*w9o7xi5F=!!d-lDA#TX)*X3GxI>CZXV>q> z;~0&FigNhx#*LJ#O&?uM$r)lC&gNPv5p(?-k6o+3r?G!1PGBUR7aD;G5w26An#Aiz z5zG49-R!tV!F<|1VNd-f*SXr;`ajF5(9c;`-1{@>=QDtXiR*)sAm?V=6FJEf-} zW!tH(YjLlodN(?oz3_jA;VU_5;nMQt5kHi1g}b)#X!uHA)YqrgqQ zStHl*Jc-9hdb+gnB7ngJ4ZsXe#rnY<%qe4d7)gJ~8fnMoRNXg__=tCyCM;U!Lu}SI zalg2E`nK+dcly27o1|;|Wm7WUO;s1)Y}RZ->in~nGj%ATm~aTqeF){K1#k>;@00C) zvd>0x9Ms*zUXtL8UI8}}sjQXw$dho#ff!O6t@7FCX;%vtHPD{&rNnfzkSXwyEswp>eWP)Ir zLP}0L)i|CUenQp4v_DjhgiBxc006}`yj_2D<3r8O6EuQJ#OnZT+?H~lNOka)2b5D4 z(FcNXs^PzBH5d4*f@{i-uc0bzZm};Uh4q8BdfcIo7ROG_{RH!=Bh(@c(XQ;gz15zV zZEEWtmkXS&+PHM1q5Ma|9P1e_?-;b5(Leo$^umsXdt19TLH=r*oZgFx*K(Pt%I<$! zppH@c11(o&>apo}63bg)c-89BE!!K!JjQpBBQQl0u)X$?6pf! z+Ko;v88MmALJqq)0M`U^`S`pnV(Z2M)Ofd^$>zFOle$tZkB_jey7>{h0{A(Yj#82x zS&VEJS4jxbYPEeRe;&jyTR!xLnbLpK+ON4gqvqA8*%}+Ns=OlHS_|Vg=Q>6ILCu-lD0Vrt+jhsP4ItGmr9?Z zs)U2*C!OWr1|~@AFQ>3TYr;+mp=>DpBU$wakF4R>frmi+sA9k~3=}PHkcr5z8c}w2iK^#c{X>nebqjCs_5gP!8 zg89zt{pq&#=c@>XV-(*K-4A~WO-hm(ig5rVQ+*q}vTN^vSK#RET4>nW^y}FBsz0RL zHX}CeE+ZKFy|5!Wa8MIdrRMr|>KAA!4l)8=S&TD^-B^ut9IjgXR^uwrs=~=B|Cea- zx+EnXrmlN(6T3GgpY3gL)x;O+S%b%}X%x0g9NYOFg>R$T|wV@o|$8=`@zMv#Ps^Aw1ileX!K0tPc zXISAje1PmdLdS5Py}XRk3@0=F;9O>xkf=SE`V&-vS63$C9}eds8sEjccrTy~E%;=~ z#;@ja}i#Z)o z-NqHOW|bka$en+=hM9%SK;t)12#p$AEkpqgRSY%+3lv4K^j${XiOx<1KOE}4xAH&W zir#IE2bZZ+{c~a4mHlm$GHYVVM&umXrkd?ExGn98ohz|%5i1@zoaUm)2LRfM+2p-y zIz!5!sbg#^qvO&_>AlI#TXJJvzz8W~GO^+W-kA&BFC|-o?&+CD85oaX8I Z`e5Vx_ix|-e*gdg|Ns3dZ5B@P1_0r8#q|IH diff --git a/build/openrpc/gateway.json.gz b/build/openrpc/gateway.json.gz index 82d638d7e432e35816cbd7fe44b39594f9c6a5f9..39055fc6affd76e88bfc885a3f1708e95c5656e3 100644 GIT binary patch delta 2623 zcmV-F3c&T?Tr`g87K!1UP90krww`GmW8-~vdO@S$YbOZdQpy@U@r$Sw2zZ039S&^}tG zpiq=eUS4`r%+y2C&&34(2y>?FBQxPGDe5)>4fex6W8FGR`B zf;~ts5{cM}bp2G0h`HG)6?o51isM$&_YzQEC~XxmmR4|COgW9pRE{d9$D$iW3aGQF zS(ZV_t~kRtgev~WoM(M%C}g?Pb{HoPL>5pbB_#VNVo86fqarpJ$l%u&F1_3CR+Z1R%+M(+iS-oSU%p^-8HtSYKqP zxA^(4k=OfQfXTYAjHe66A*&j2Qc#w`AFM!Ff32+?fj*}1 z-e?p*`$9}3GDt*5%>VX<8BHOD(FW;GF??!bmArravTJ+$tz<>Sf7 z6>1Y6W^ z8*3P+r46LMPdPev+0XHcss1BC~^&F4p=HO`>=nj zpkhfly@l+ZV`PUM{@lIytZRb*0xUips80UQCf-EmuGyu6jA4kiS_v%V8fjR^$Ts|N z_<($7UoFdhq~m*^QX`@hD!q&u>l2)kcnBwn}-Rq zCI)hC6fQ9wV>)**vck9TX280Gc07MdxLI!v;MxW-NHFUJ1e2Y#j#|&>S2eH;1sO-* zc}bRjDHLxLjKl9+oA|m2#v!oCcp!eiTaS-l|KNMmR6KifTkBuInMYFf396FMxj!c(M$} zyNkQU?B9{weHd9k_NMpU$<({U4?}x#2NwN+2(yb|d~IPGkB%^vqwSV?O_*cx1kOP4 znS^~Gw#?J}kPfar57)K*k7R&jFCY*vU=Vr8q;`m z5=0RCj&Ku-k_j4yzd{4%57&t^DZ+*|l8+ir(RcBj84^r@KV~$3Nm&QwJ;puRm6nOe z1Pd+tS3v*5*c|M&WLct`Ab10@4rI?GzcnP~His%iQopkO#Wr^zt;U;`aDqSvOX=1CV zJWp}AQ*2QU^%K^YaDjguMHF@9v>z$g38%>@VEyMnzNS@+SI^|AGp$WY4;w39(y4U? z>Ya2XH`DWE`*A8^Ei@#FA<7IfvRXFFOL=oh9u39$Ff-M*z_Pc-V_S)-s>Z7l16q=b zl^A)-WR$%)PDTiDY;D65sbL1!=I{Dd1Wxv#60$pV`*K{J_fg#;sCb-C(Jr>a~v%?mh# zSh>hx3W7{{TPUk&XDV*WE-juS7y7K_A1;w@LOI8&iPfpurq`>lFs?(T%p$|aRF^}2 zd+}4p@|6&DUhOJxD58X=&?!`cZXj8hH)6F2fdlPY3w3|f!tq>@V#0 zh3OgO(3P(goO*HYB8-4@xSK+@N@n7UcG(`zr#ZJSVo_SJD(I_AOZ)4nd3hG+ez2@&VN!>5Sg#9 z07k0BTn2wZ<&PI~*ry7DLv6A0*D-GpZ5}fZ$;jIbC8_j|n3~v4sVN#OIuR}6H?Lb~ z?p77$PKuZ#=;H9vT3)G@oU4JTpBUN%LnOhUettE@d?Q!ar zh(v#hL(8)ndmN-er`I{?w_E0s<5_nn&;d#n5;=e(^v(|^c<*KmyoC5)2;uFQ)JFUQ z9H;y){uShx>MZD(dTYF)eu^VBK6fjNd8EKs5GW5%ibS*XZ(2RL#<`!0I19h~B`&G> zrS_j>>GGhYs^b?p?9tO9MbVf8P%I=VzpFQ-!YrU-O8jPiGo@&+V17*yluQ6*cRPM8 hfWj%M_`=GNb`E>y&xQf(TAJv$KcdEyIuGf zpYDJEKRhy9=EdB>C`9biQfFU~YkLn@-VMI;J#TbN_WU50C`5lJ==FQMhx@&Q-rhl~ z1Mo5VdbDjoi@qFtZUEh29((|E2cJ4%Mv5253nEFVWFK<{VOr)9bm0hDh<=Bn$G6ZY zqUX=KhXGSsKL^m}f9JF7;(-ewUBZWwVK3nW5B3s1URB|ur4v?k9uQ^5r<<)kK4?Wvfoif#cZAhM!nSvr?pafbYbs`SSU zWPPMA7Es0PCHp7RMW~}9HW!G}*B0Wu+wOl>mDHklmUs^ZzE5GRgmhoC z6l4NO%rGHMYjkPz>(J(iHb=JUy)Gxg^gF=-IppPN>YY%jsJZV$ z7iax%KJx5z*6O)y!v*zq{V{`yBk0)i0+{@o0LOvuIC(;x5yx}vjYjdaFT^w=gG6M+ z+-P5z(G*e`ZIJF1!>1-z$;(fkcQ*vqU3wBH&~kqOwPREBkxj!*mMG*}q5C7$H1#b+ zdZad2xI@84i1cL`Akvg#h|r-bk7`0Da>5*QF+Nw^e@OcI3p(zy0sRn@3Vh#x%vj$C-J!mzpi1-$yL z@y4c<$+cU_iMh5FOgIl%o)xEOzisYYM4S0l+`entI!6HHgLA+Tul%^?_5p26ESbn zH^J3=eO(k~FCA33-D}}iBm?lbk-pf2x5KI`AlGo_fTbd{54#E~mW0z=$lf_dcF2F> z&)s{^x+eH9z~aM!>g4Zi;!R}knq4Z$7=~D@mB2!-k%o1QY{L(S56EW*MgX1BJKAQ5 z+%d&=7e~4bv<(zP+cC~@s7sDKe+q45xHNzf8&JRqnV4m~d6*z;Vj$N>;S$3!rgH}) zD}4KI2CO@1$D@Rs_0|BcZ2*G=vrcG0Fxg4#sP%k)RRghGyz zN6A~(kW5HcWMd-6AQVi(IEP|-QHr^UjF+&RqKMv**-7WyQrl0L^-PXB)7q5uu(9HQC7oJV zpx#MGax*MD33oLtUJhqjXs%pF{F`y-> zSc#FROh(zu-eiOT$JRD1FH}wVr-+Tl7>$}BQ$Om#Wb zHx55_EMEyh=hd$Ah9XK>3Y|hF=mwI7c_UU*5IE4TwYWAd_RiI1Cy7t#0(}KF`@(Kt zn4UonUHR(1sTb!i!U#BjR}QqtFextxEe!1wu)Ky3z_&Bcqk_YH9}?(gL7Q&TdoC?J zdXt4lTmBB0wQkrA3P0DM@73$K@H;GY_hj^mxbsN?1|o1!m^tp_znye_?))cJ1(Ess z3Sgv4%w-T%{&*pWeX1Zh)D|mQ9rFg!<}ve-jJ(ZIl1lH0sfpcxZknR8q7%^~e)GC@ z=5AFX?WFKHf-Vjp&DCxqEdsP=f(X&07|jb+vn!fIeadpl3Y!>T{JivKt#C>xoLQpC zYnlZu1ipUzHQgV$+v!ym@?@0mug?zsCuM~GhFGAlnV)8Un)y9j=GU*P()a}+tQrEq z`j)Y~qB>a=w7Za?v)hQR4iOoUaab^@#g*2xj-$d)%B+mI8r62lDRdZ+*-1T+#aW1 ziAa<ZdqD<8!yNm`4hH1%dMLq)0S7|EAT0Yn=P3h_mp!U*eLA zUuyqJmM#xUsycpw!yY{yQWT9j0L4O*^1DhZ%mON=#Bb&|Q;PNq=GO#4$pk=lx8uhG iD4ddtFRTm~Y3HzKK0bbb{C@!e0RR7cYS-^;jRF7%3JfFw diff --git a/documentation/en/api-v0-methods.md b/documentation/en/api-v0-methods.md index 9110c1c3b..04e26493c 100644 --- a/documentation/en/api-v0-methods.md +++ b/documentation/en/api-v0-methods.md @@ -5995,6 +5995,7 @@ Inputs: Response: ```json { + "MinerID": "f01234", "Owner": "f01234", "Worker": "f01234", "NewWorker": "f01234", diff --git a/documentation/en/api-v1-unstable-methods.md b/documentation/en/api-v1-unstable-methods.md index 53b5ddbe6..e3107fc6c 100644 --- a/documentation/en/api-v1-unstable-methods.md +++ b/documentation/en/api-v1-unstable-methods.md @@ -7621,6 +7621,7 @@ Inputs: Response: ```json { + "MinerID": "f01234", "Owner": "f01234", "Worker": "f01234", "NewWorker": "f01234", diff --git a/documentation/en/default-lotus-provider-config.toml b/documentation/en/default-lotus-provider-config.toml index 8573fbda1..1daee17d0 100644 --- a/documentation/en/default-lotus-provider-config.toml +++ b/documentation/en/default-lotus-provider-config.toml @@ -56,32 +56,15 @@ #PerSector = "0.03 FIL" -[Addresses] - # Addresses to send PreCommit messages from - # - # type: []string +[[Addresses]] #PreCommitControl = [] - # Addresses to send Commit messages from - # - # type: []string #CommitControl = [] - # type: []string #TerminateControl = [] - # DisableOwnerFallback disables usage of the owner address for messages - # sent automatically - # - # type: bool #DisableOwnerFallback = false - # DisableWorkerFallback disables usage of the worker address for messages - # sent automatically, if control addresses are configured. - # A control address that doesn't have enough funds will still be chosen - # over the worker address if this flag is set. - # - # type: bool #DisableWorkerFallback = false diff --git a/go.mod b/go.mod index c1b353fab..1acbfa7e8 100644 --- a/go.mod +++ b/go.mod @@ -166,6 +166,12 @@ require ( gotest.tools v2.2.0+incompatible ) +require ( + github.com/bahlo/generic-list-go v0.2.0 // indirect + github.com/buger/jsonparser v1.1.1 // indirect + github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect +) + require ( github.com/GeertJohan/go.incremental v1.0.0 // indirect github.com/PuerkitoBio/purell v1.1.1 // indirect @@ -225,6 +231,7 @@ require ( github.com/hashicorp/golang-lru v0.6.0 // indirect github.com/huin/goupnp v1.2.0 // indirect github.com/iancoleman/orderedmap v0.1.0 // indirect + github.com/invopop/jsonschema v0.12.0 github.com/ipfs/go-bitfield v1.1.0 // indirect github.com/ipfs/go-blockservice v0.5.1 // indirect github.com/ipfs/go-ipfs-blockstore v1.3.0 // indirect diff --git a/go.sum b/go.sum index a7983d80c..8cc338de2 100644 --- a/go.sum +++ b/go.sum @@ -102,6 +102,8 @@ github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6l github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= +github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk= +github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o= @@ -135,6 +137,8 @@ github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46f github.com/buger/goterm v1.0.3 h1:7V/HeAQHrzPk/U4BvyH2g9u+xbUW9nr4yRPyG59W4fM= github.com/buger/goterm v1.0.3/go.mod h1:HiFWV3xnkolgrBV3mY8m0X0Pumt4zg4QhbdOzQtB8tE= github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= +github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs= +github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -630,6 +634,8 @@ github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANyt github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= github.com/influxdata/influxdb1-client v0.0.0-20200827194710-b269163b24ab h1:HqW4xhhynfjrtEiiSGcQUd6vrK23iMam1FO8rI7mwig= github.com/influxdata/influxdb1-client v0.0.0-20200827194710-b269163b24ab/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= +github.com/invopop/jsonschema v0.12.0 h1:6ovsNSuvn9wEQVOyc72aycBMVQFKz7cPdMJn10CvzRI= +github.com/invopop/jsonschema v0.12.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0= github.com/ipfs/bbloom v0.0.1/go.mod h1:oqo8CVWsJFMOZqTglBG4wydCE4IQA/G2/SEofB0rjUI= github.com/ipfs/bbloom v0.0.4 h1:Gi+8EGJ2y5qiD5FbsbpX/TMNcJw8gSqr7eyjHa4Fhvs= github.com/ipfs/bbloom v0.0.4/go.mod h1:cS9YprKXpoZ9lT0n/Mw/a6/aFV6DTjTLYHeA+gyqMG0= @@ -1635,6 +1641,8 @@ github.com/whyrusleeping/mdns v0.0.0-20180901202407-ef14215e6b30/go.mod h1:j4l84 github.com/whyrusleeping/mdns v0.0.0-20190826153040-b9b60ed33aa9/go.mod h1:j4l84WPFclQPj320J9gp0XwNKBb3U0zt5CBqjPp22G4= github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7 h1:E9S12nwJwEOXe2d6gT6qxdvqMnNq+VnSsKPgm2ZZNds= github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7/go.mod h1:X2c0RVCI1eSUFI8eLcY3c0423ykwiUdxLJtkDvruhjI= +github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc= +github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw= github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7VPsoEPHyzalCE06qoARUCeBBE= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= diff --git a/lib/harmony/harmonydb/sql/20230706.sql b/lib/harmony/harmonydb/sql/20230706-itest_scratch.sql similarity index 100% rename from lib/harmony/harmonydb/sql/20230706.sql rename to lib/harmony/harmonydb/sql/20230706-itest_scratch.sql diff --git a/lib/harmony/harmonydb/sql/20230712.sql b/lib/harmony/harmonydb/sql/20230712-sector_index.sql similarity index 100% rename from lib/harmony/harmonydb/sql/20230712.sql rename to lib/harmony/harmonydb/sql/20230712-sector_index.sql diff --git a/lib/harmony/harmonydb/sql/20230719.sql b/lib/harmony/harmonydb/sql/20230719-harmony.sql similarity index 100% rename from lib/harmony/harmonydb/sql/20230719.sql rename to lib/harmony/harmonydb/sql/20230719-harmony.sql diff --git a/lib/harmony/harmonydb/sql/20230823.sql b/lib/harmony/harmonydb/sql/20230823-wdpost.sql similarity index 100% rename from lib/harmony/harmonydb/sql/20230823.sql rename to lib/harmony/harmonydb/sql/20230823-wdpost.sql diff --git a/lib/harmony/harmonydb/sql/20230919.sql b/lib/harmony/harmonydb/sql/20230919-config.sql similarity index 100% rename from lib/harmony/harmonydb/sql/20230919.sql rename to lib/harmony/harmonydb/sql/20230919-config.sql diff --git a/lib/harmony/harmonydb/sql/20231103.sql b/lib/harmony/harmonydb/sql/20231103-chain_sends.sql similarity index 100% rename from lib/harmony/harmonydb/sql/20231103.sql rename to lib/harmony/harmonydb/sql/20231103-chain_sends.sql diff --git a/lib/harmony/harmonydb/sql/20231110.sql b/lib/harmony/harmonydb/sql/20231110-mining_tasks.sql similarity index 100% rename from lib/harmony/harmonydb/sql/20231110.sql rename to lib/harmony/harmonydb/sql/20231110-mining_tasks.sql diff --git a/lib/harmony/harmonydb/sql/20231113.sql b/lib/harmony/harmonydb/sql/20231113-harmony_taskhistory_oops.sql similarity index 100% rename from lib/harmony/harmonydb/sql/20231113.sql rename to lib/harmony/harmonydb/sql/20231113-harmony_taskhistory_oops.sql diff --git a/node/config/doc_gen.go b/node/config/doc_gen.go index 8ea61c782..a63e40a42 100644 --- a/node/config/doc_gen.go +++ b/node/config/doc_gen.go @@ -740,9 +740,9 @@ over the worker address if this flag is set.`, }, { Name: "Addresses", - Type: "LotusProviderAddresses", + Type: "[]LotusProviderAddresses", - Comment: ``, + Comment: `Addresses of wallets per MinerAddress (one of the fields).`, }, { Name: "Proving", From efb9422c012292c33ec5c0c9d8ac924c6ca15b65 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 30 Jan 2024 10:29:54 -0800 Subject: [PATCH 079/394] fix: eth: re-execute tipsets on missing events (#11588) This will re-execute tipsets to forcibly re-compute and store events when they're missing. This is effectively lazy backfilling of events. NOTE: This _won't_ backfill the index itself, it'll just give us the events. fixes #11335 --- chain/stmgr/execute.go | 16 ++++++++++++++-- node/impl/full/eth.go | 10 +--------- node/impl/full/eth_utils.go | 24 +++++++++++++++++++++--- 3 files changed, 36 insertions(+), 14 deletions(-) diff --git a/chain/stmgr/execute.go b/chain/stmgr/execute.go index bed857833..e716ed198 100644 --- a/chain/stmgr/execute.go +++ b/chain/stmgr/execute.go @@ -13,6 +13,16 @@ import ( ) func (sm *StateManager) TipSetState(ctx context.Context, ts *types.TipSet) (st cid.Cid, rec cid.Cid, err error) { + return sm.tipSetState(ctx, ts, false) +} + +// Recompute the tipset state without trying to lookup a pre-computed result in the chainstore. +// Useful if we know that our local chain-state isn't complete (e.g., we've discarded the events). +func (sm *StateManager) RecomputeTipSetState(ctx context.Context, ts *types.TipSet) (st cid.Cid, rec cid.Cid, err error) { + return sm.tipSetState(ctx, ts, true) +} + +func (sm *StateManager) tipSetState(ctx context.Context, ts *types.TipSet, recompute bool) (st cid.Cid, rec cid.Cid, err error) { ctx, span := trace.StartSpan(ctx, "tipSetState") defer span.End() if span.IsRecordingEvents() { @@ -65,8 +75,10 @@ func (sm *StateManager) TipSetState(ctx context.Context, ts *types.TipSet) (st c // First, try to find the tipset in the current chain. If found, we can avoid re-executing // it. - if st, rec, found := tryLookupTipsetState(ctx, sm.cs, ts); found { - return st, rec, nil + if !recompute { + if st, rec, found := tryLookupTipsetState(ctx, sm.cs, ts); found { + return st, rec, nil + } } st, rec, err = sm.tsExec.ExecuteTipSet(ctx, sm, ts, sm.tsExecMonitor, false) diff --git a/node/impl/full/eth.go b/node/impl/full/eth.go index d4298492f..cbe491afe 100644 --- a/node/impl/full/eth.go +++ b/node/impl/full/eth.go @@ -422,15 +422,7 @@ func (a *EthModule) EthGetTransactionReceiptLimited(ctx context.Context, txHash return nil, xerrors.Errorf("failed to convert %s into an Eth Txn: %w", txHash, err) } - var events []types.Event - if rct := msgLookup.Receipt; rct.EventsRoot != nil { - events, err = a.ChainAPI.ChainGetEvents(ctx, *rct.EventsRoot) - if err != nil { - return nil, xerrors.Errorf("failed get events for %s", txHash) - } - } - - receipt, err := newEthTxReceipt(ctx, tx, msgLookup, events, a.Chain, a.StateAPI) + receipt, err := newEthTxReceipt(ctx, tx, msgLookup, a.ChainAPI, a.StateAPI) if err != nil { return nil, xerrors.Errorf("failed to convert %s into an Eth Receipt: %w", txHash, err) } diff --git a/node/impl/full/eth_utils.go b/node/impl/full/eth_utils.go index 5f22cea82..90e6d054a 100644 --- a/node/impl/full/eth_utils.go +++ b/node/impl/full/eth_utils.go @@ -654,7 +654,7 @@ func newEthTxFromMessageLookup(ctx context.Context, msgLookup *api.MsgLookup, tx return tx, nil } -func newEthTxReceipt(ctx context.Context, tx ethtypes.EthTx, lookup *api.MsgLookup, events []types.Event, cs *store.ChainStore, sa StateAPI) (api.EthTxReceipt, error) { +func newEthTxReceipt(ctx context.Context, tx ethtypes.EthTx, lookup *api.MsgLookup, ca ChainAPI, sa StateAPI) (api.EthTxReceipt, error) { var ( transactionIndex ethtypes.EthUint64 blockHash ethtypes.EthHash @@ -695,7 +695,7 @@ func newEthTxReceipt(ctx context.Context, tx ethtypes.EthTx, lookup *api.MsgLook receipt.CumulativeGasUsed = ethtypes.EmptyEthInt // TODO: avoid loading the tipset twice (once here, once when we convert the message to a txn) - ts, err := cs.GetTipSetFromKey(ctx, lookup.TipSet) + ts, err := ca.Chain.GetTipSetFromKey(ctx, lookup.TipSet) if err != nil { return api.EthTxReceipt{}, xerrors.Errorf("failed to lookup tipset %s when constructing the eth txn receipt: %w", lookup.TipSet, err) } @@ -706,7 +706,7 @@ func newEthTxReceipt(ctx context.Context, tx ethtypes.EthTx, lookup *api.MsgLook } // The tx is located in the parent tipset - parentTs, err := cs.LoadTipSet(ctx, ts.Parents()) + parentTs, err := ca.Chain.LoadTipSet(ctx, ts.Parents()) if err != nil { return api.EthTxReceipt{}, xerrors.Errorf("failed to lookup tipset %s when constructing the eth txn receipt: %w", ts.Parents(), err) } @@ -731,6 +731,24 @@ func newEthTxReceipt(ctx context.Context, tx ethtypes.EthTx, lookup *api.MsgLook receipt.ContractAddress = &addr } + var events []types.Event + if rct := lookup.Receipt; rct.EventsRoot != nil { + events, err = ca.ChainGetEvents(ctx, *rct.EventsRoot) + if err != nil { + // Fore-recompute, we must have enabled the Event APIs after computing this + // tipset. + if _, _, err := sa.StateManager.RecomputeTipSetState(ctx, ts); err != nil { + + return api.EthTxReceipt{}, xerrors.Errorf("failed get events: %w", err) + } + // Try again + events, err = ca.ChainGetEvents(ctx, *rct.EventsRoot) + if err != nil { + return api.EthTxReceipt{}, xerrors.Errorf("failed get events: %w", err) + } + } + } + if len(events) > 0 { receipt.Logs = make([]ethtypes.EthLog, 0, len(events)) for i, evt := range events { From a0867a2a60f7f39fcf58825a77c1f6729ccf35c6 Mon Sep 17 00:00:00 2001 From: "Andrew Jackson (Ajax)" Date: Tue, 30 Jan 2024 16:44:08 -0600 Subject: [PATCH 080/394] fix: lp multiaddr, minerID rm, cfg upgrade --- api/types.go | 1 - cmd/lotus-provider/config.go | 3 +-- cmd/lotus-provider/deps/deps.go | 12 ++++++++---- cmd/lotus-provider/migrate.go | 3 ++- node/impl/full/state.go | 1 - provider/lpwindow/submit_task.go | 2 +- storage/ctladdr/multiaddresses.go | 4 ++-- 7 files changed, 14 insertions(+), 12 deletions(-) diff --git a/api/types.go b/api/types.go index e29b7c6f7..7fd607750 100644 --- a/api/types.go +++ b/api/types.go @@ -291,7 +291,6 @@ type ExportRef struct { } type MinerInfo struct { - MinerID address.Address Owner address.Address // Must be an ID-address. Worker address.Address // Must be an ID-address. NewWorker address.Address // Must be an ID-address. diff --git a/cmd/lotus-provider/config.go b/cmd/lotus-provider/config.go index 44ba49beb..13156833c 100644 --- a/cmd/lotus-provider/config.go +++ b/cmd/lotus-provider/config.go @@ -9,7 +9,6 @@ import ( "path" "strings" - "github.com/BurntSushi/toml" "github.com/urfave/cli/v2" "golang.org/x/xerrors" @@ -103,7 +102,7 @@ var configSetCmd = &cli.Command{ } lp := config.DefaultLotusProvider() // ensure it's toml - _, err = toml.Decode(string(bytes), lp) + _, err = deps.LoadConfigWithUpgrades(string(bytes), lp) if err != nil { return fmt.Errorf("cannot decode file: %w", err) } diff --git a/cmd/lotus-provider/deps/deps.go b/cmd/lotus-provider/deps/deps.go index 34db9f461..d5ccd30c3 100644 --- a/cmd/lotus-provider/deps/deps.go +++ b/cmd/lotus-provider/deps/deps.go @@ -253,6 +253,12 @@ Get it with: jq .PrivateKey ~/.lotus-miner/keystore/MF2XI2BNNJ3XILLQOJUXMYLUMU`, var oldAddresses = regexp.MustCompile("(?i)^[addresses]$") +func LoadConfigWithUpgrades(text string, lp *config.LotusProviderConfig) (toml.MetaData, error) { + // allow migration from old config format that was limited to 1 wallet setup. + newText := oldAddresses.ReplaceAllString(text, "[[addresses]]") + meta, err := toml.Decode(newText, &lp) + return meta, err +} func GetConfig(cctx *cli.Context, db *harmonydb.DB) (*config.LotusProviderConfig, error) { lp := config.DefaultLotusProvider() have := []string{} @@ -271,11 +277,9 @@ func GetConfig(cctx *cli.Context, db *harmonydb.DB) (*config.LotusProviderConfig return nil, fmt.Errorf("could not read layer '%s': %w", layer, err) } - // allow migration from old config format that was limited to 1 wallet setup. - newText := oldAddresses.ReplaceAllString(text, "[[addresses]]") - meta, err := toml.Decode(newText, &lp) + meta, err := LoadConfigWithUpgrades(text, lp) if err != nil { - return nil, fmt.Errorf("could not read layer, bad toml %s: %w", layer, err) + return lp, fmt.Errorf("could not read layer, bad toml %s: %w", layer, err) } for _, k := range meta.Keys() { have = append(have, strings.Join(k, " ")) diff --git a/cmd/lotus-provider/migrate.go b/cmd/lotus-provider/migrate.go index 315691788..805c404bd 100644 --- a/cmd/lotus-provider/migrate.go +++ b/cmd/lotus-provider/migrate.go @@ -20,6 +20,7 @@ import ( "github.com/filecoin-project/go-address" cliutil "github.com/filecoin-project/lotus/cli/util" + "github.com/filecoin-project/lotus/cmd/lotus-provider/deps" "github.com/filecoin-project/lotus/lib/harmony/harmonydb" "github.com/filecoin-project/lotus/node/config" "github.com/filecoin-project/lotus/node/modules" @@ -124,7 +125,7 @@ func fromMiner(cctx *cli.Context) (err error) { return fmt.Errorf("could not read config.toml: %w", err) } var lpCfg config.LotusProviderConfig - _, err = toml.Decode(string(buf), &lpCfg) + _, err = deps.LoadConfigWithUpgrades(string(buf), &lpCfg) if err != nil { return fmt.Errorf("could not decode toml: %w", err) } diff --git a/node/impl/full/state.go b/node/impl/full/state.go index d294307c1..0e92c8e5b 100644 --- a/node/impl/full/state.go +++ b/node/impl/full/state.go @@ -169,7 +169,6 @@ func (m *StateModule) StateMinerInfo(ctx context.Context, actor address.Address, } ret := api.MinerInfo{ - MinerID: actor, Owner: info.Owner, Worker: info.Worker, ControlAddresses: info.ControlAddresses, diff --git a/provider/lpwindow/submit_task.go b/provider/lpwindow/submit_task.go index 6bb92ea42..6283c7f74 100644 --- a/provider/lpwindow/submit_task.go +++ b/provider/lpwindow/submit_task.go @@ -292,7 +292,7 @@ func preparePoStMessage(w MsgPrepAPI, as *ctladdr.MultiAddressSelector, maddr ad goodFunds := big.Add(minGasFeeMsg.RequiredFunds(), minGasFeeMsg.Value) minFunds := big.Min(big.Add(minGasFeeMsg.RequiredFunds(), minGasFeeMsg.Value), goodFunds) - from, _, err := as.AddressFor(context.Background(), w, mi, api.PoStAddr, goodFunds, minFunds) + from, _, err := as.AddressFor(context.Background(), w, maddr, mi, api.PoStAddr, goodFunds, minFunds) if err != nil { return nil, nil, xerrors.Errorf("error getting address: %w", err) } diff --git a/storage/ctladdr/multiaddresses.go b/storage/ctladdr/multiaddresses.go index 54a3433e0..fbb153e84 100644 --- a/storage/ctladdr/multiaddresses.go +++ b/storage/ctladdr/multiaddresses.go @@ -15,14 +15,14 @@ type MultiAddressSelector struct { MinerMap map[address.Address]api.AddressConfig } -func (as *MultiAddressSelector) AddressFor(ctx context.Context, a NodeApi, mi api.MinerInfo, use api.AddrUse, goodFunds, minFunds abi.TokenAmount) (address.Address, abi.TokenAmount, error) { +func (as *MultiAddressSelector) AddressFor(ctx context.Context, a NodeApi, minerID address.Address, mi api.MinerInfo, use api.AddrUse, goodFunds, minFunds abi.TokenAmount) (address.Address, abi.TokenAmount, error) { if as == nil { // should only happen in some tests log.Warnw("smart address selection disabled, using worker address") return mi.Worker, big.Zero(), nil } - tmp := as.MinerMap[mi.MinerID] + tmp := as.MinerMap[minerID] var addrs []address.Address switch use { From 95fb198597b8d996b396281da8c975f5c9c858b6 Mon Sep 17 00:00:00 2001 From: Ian Norden Date: Wed, 31 Jan 2024 13:49:07 -0500 Subject: [PATCH 081/394] perf: api: add indexes to event topics and emitter addr (#11477) * add index to event_entry.key * add index to event.emitter_addr --- chain/events/filter/index.go | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/chain/events/filter/index.go b/chain/events/filter/index.go index 2b1890c73..0f4a54c5b 100644 --- a/chain/events/filter/index.go +++ b/chain/events/filter/index.go @@ -46,6 +46,7 @@ var ddls = []string{ )`, `CREATE INDEX IF NOT EXISTS height_tipset_key ON event (height,tipset_key)`, + `CREATE INDEX IF NOT EXISTS event_emitter_addr ON event (emitter_addr)`, `CREATE TABLE IF NOT EXISTS event_entry ( event_id INTEGER, @@ -56,6 +57,8 @@ var ddls = []string{ value BLOB NOT NULL )`, + `CREATE INDEX IF NOT EXISTS event_entry_key_index ON event_entry (key)`, + // metadata containing version of schema `CREATE TABLE IF NOT EXISTS _meta ( version UINT64 NOT NULL UNIQUE @@ -63,6 +66,7 @@ var ddls = []string{ `INSERT OR IGNORE INTO _meta (version) VALUES (1)`, `INSERT OR IGNORE INTO _meta (version) VALUES (2)`, + `INSERT OR IGNORE INTO _meta (version) VALUES (3)`, } var ( @@ -70,7 +74,7 @@ var ( ) const ( - schemaVersion = 2 + schemaVersion = 3 eventExists = `SELECT MAX(id) FROM event WHERE height=? AND tipset_key=? AND tipset_key_cid=? AND emitter_addr=? AND event_index=? AND message_cid=? AND message_index=?` insertEvent = `INSERT OR IGNORE INTO event(height, tipset_key, tipset_key_cid, emitter_addr, event_index, message_cid, message_index, reverted) VALUES(?, ?, ?, ?, ?, ?, ?, ?)` @@ -321,6 +325,22 @@ func NewEventIndex(ctx context.Context, path string, chainStore *store.ChainStor version = 2 } + if version == 2 { + log.Infof("upgrading event index from version 2 to version 3") + + // to upgrade to version 3 we only need to create an index on the event_entry.key column + // and on the event.emitter_addr column + // which means we can just reapply the schema (it will not have any effect on existing data) + for _, ddl := range ddls { + if _, err := db.Exec(ddl); err != nil { + _ = db.Close() + return nil, xerrors.Errorf("could not upgrade index to version 3, exec ddl %q: %w", ddl, err) + } + } + + version = 3 + } + if version != schemaVersion { _ = db.Close() return nil, xerrors.Errorf("invalid database version: got %d, expected %d", version, schemaVersion) From ca877940a0cd0b53bea246cb20b4780d977c45c0 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 6 Feb 2024 17:22:14 -0800 Subject: [PATCH 082/394] feat: fvm: update the FVM/FFI to v4.1 (#11608) This: 1. Adds nv22 support. 2. Updates the message tracing format. --- CHANGELOG.md | 46 +++++++ api/docgen/docgen.go | 8 +- build/openrpc/full.json.gz | Bin 34601 -> 34712 bytes build/openrpc/gateway.json.gz | Bin 11870 -> 11931 bytes chain/types/cbor_gen.go | 127 ++++++++++++++++---- chain/types/execresult.go | 17 +-- documentation/en/api-v0-methods.md | 114 ++++++++++++++---- documentation/en/api-v1-unstable-methods.md | 114 ++++++++++++++---- extern/filecoin-ffi | 2 +- gen/main.go | 1 + go.sum | 8 ++ node/impl/full/eth_trace.go | 11 +- 12 files changed, 362 insertions(+), 86 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9cf98e889..c18f2f6a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,52 @@ ## Improvements +### Tracing API + +Replace the `CodeCid` field in the message trace (added in 1.23.4) with an `InvokedActor` field. + +**Before:** + +```javascript +{ + "Msg": { + "From": ..., + "To": ..., + ... + "CodeCid": ... // The actor's code CID. + } + "MsgRct": ..., + "GasCharges": [], + "Subcalls": [], +} +``` + +**After:** + +```javascript +{ + "Msg": { + "From": ..., + "To": ... + } + "InvokedActor": { // The invoked actor (ommitted if the actor wasn't invoked). + "Id": 1234, // The ID of the actor. + "State": { // The actor's state object (may change between network versions). + "Code": ..., // The actor's code CID. + "Head": ..., // The actor's state-root (when invoked). + "CallSeqNum": ..., // The actor's nonce. + "Balance": ..., // The actor's balance (when invoked). + "Address": ..., // Delegated address (FEVM only). + } + } + "MsgRct": ..., + "GasCharges": [], + "Subcalls": [], +} +``` + +This means the trace now contains an accurate "snapshot" of the actor at the time of the call, information that may not be present in the final state-tree (e.g., due to reverts). This will hopefully improve the performance and accuracy of indexing services. + # v1.25.2 / 2024-01-11 This is an optional but **highly recommended feature release** of Lotus, as it includes fixes for synchronizations issues that users have experienced. The feature release also introduces `Lotus-Provider` in its alpha testing phase, as well as the ability to call external PC2-binaries during the sealing process. diff --git a/api/docgen/docgen.go b/api/docgen/docgen.go index 181efeb30..6028a40f2 100644 --- a/api/docgen/docgen.go +++ b/api/docgen/docgen.go @@ -154,13 +154,14 @@ func init() { addExample(map[verifreg.ClaimId]verifreg.Claim{}) addExample(map[string]int{"name": 42}) addExample(map[string]time.Time{"name": time.Unix(1615243938, 0).UTC()}) + addExample(abi.ActorID(1000)) + addExample(map[string]types.Actor{ + "t01236": ExampleValue("init", reflect.TypeOf(types.Actor{}), nil).(types.Actor), + }) addExample(&types.ExecutionTrace{ Msg: ExampleValue("init", reflect.TypeOf(types.MessageTrace{}), nil).(types.MessageTrace), MsgRct: ExampleValue("init", reflect.TypeOf(types.ReturnTrace{}), nil).(types.ReturnTrace), }) - addExample(map[string]types.Actor{ - "t01236": ExampleValue("init", reflect.TypeOf(types.Actor{}), nil).(types.Actor), - }) addExample(map[string]api.MarketDeal{ "t026363": ExampleValue("init", reflect.TypeOf(api.MarketDeal{}), nil).(api.MarketDeal), }) @@ -209,7 +210,6 @@ func init() { si := uint64(12) addExample(&si) addExample(retrievalmarket.DealID(5)) - addExample(abi.ActorID(1000)) addExample(map[string]cid.Cid{}) addExample(map[string][]api.SealedRef{ "98000": { diff --git a/build/openrpc/full.json.gz b/build/openrpc/full.json.gz index 0b0acf6dae7a7a7e8d64017a070186a9a62d8b6b..dc9bc44ec13c8002b79e97069b6d6e6e6ed4ece5 100644 GIT binary patch delta 32201 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ν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 W{4U`)0|?0Xw*#MSs8K8!$o~Q?m__LT diff --git a/chain/types/cbor_gen.go b/chain/types/cbor_gen.go index fe8e7e3fe..74a278ce4 100644 --- a/chain/types/cbor_gen.go +++ b/chain/types/cbor_gen.go @@ -2395,7 +2395,83 @@ func (t *GasTrace) UnmarshalCBOR(r io.Reader) (err error) { return nil } -var lengthBufMessageTrace = []byte{137} +var lengthBufActorTrace = []byte{130} + +func (t *ActorTrace) MarshalCBOR(w io.Writer) error { + if t == nil { + _, err := w.Write(cbg.CborNull) + return err + } + + cw := cbg.NewCborWriter(w) + + if _, err := cw.Write(lengthBufActorTrace); err != nil { + return err + } + + // t.Id (abi.ActorID) (uint64) + + if err := cw.WriteMajorTypeHeader(cbg.MajUnsignedInt, uint64(t.Id)); err != nil { + return err + } + + // t.State (types.ActorV5) (struct) + if err := t.State.MarshalCBOR(cw); err != nil { + return err + } + return nil +} + +func (t *ActorTrace) UnmarshalCBOR(r io.Reader) (err error) { + *t = ActorTrace{} + + cr := cbg.NewCborReader(r) + + maj, extra, err := cr.ReadHeader() + if err != nil { + return err + } + defer func() { + if err == io.EOF { + err = io.ErrUnexpectedEOF + } + }() + + if maj != cbg.MajArray { + return fmt.Errorf("cbor input should be of type array") + } + + if extra != 2 { + return fmt.Errorf("cbor input had wrong number of fields") + } + + // t.Id (abi.ActorID) (uint64) + + { + + maj, extra, err = cr.ReadHeader() + if err != nil { + return err + } + if maj != cbg.MajUnsignedInt { + return fmt.Errorf("wrong type for uint64 field") + } + t.Id = abi.ActorID(extra) + + } + // t.State (types.ActorV5) (struct) + + { + + if err := t.State.UnmarshalCBOR(cr); err != nil { + return xerrors.Errorf("unmarshaling t.State: %w", err) + } + + } + return nil +} + +var lengthBufMessageTrace = []byte{136} func (t *MessageTrace) MarshalCBOR(w io.Writer) error { if t == nil { @@ -2459,13 +2535,6 @@ func (t *MessageTrace) MarshalCBOR(w io.Writer) error { if err := cbg.WriteBool(w, t.ReadOnly); err != nil { return err } - - // t.CodeCid (cid.Cid) (struct) - - if err := cbg.WriteCid(cw, t.CodeCid); err != nil { - return xerrors.Errorf("failed to write cid field t.CodeCid: %w", err) - } - return nil } @@ -2488,7 +2557,7 @@ func (t *MessageTrace) UnmarshalCBOR(r io.Reader) (err error) { return fmt.Errorf("cbor input should be of type array") } - if extra != 9 { + if extra != 8 { return fmt.Errorf("cbor input had wrong number of fields") } @@ -2599,18 +2668,6 @@ func (t *MessageTrace) UnmarshalCBOR(r io.Reader) (err error) { default: return fmt.Errorf("booleans are either major type 7, value 20 or 21 (got %d)", extra) } - // t.CodeCid (cid.Cid) (struct) - - { - - c, err := cbg.ReadCid(cr) - if err != nil { - return xerrors.Errorf("failed to read cid field t.CodeCid: %w", err) - } - - t.CodeCid = c - - } return nil } @@ -2747,7 +2804,7 @@ func (t *ReturnTrace) UnmarshalCBOR(r io.Reader) (err error) { return nil } -var lengthBufExecutionTrace = []byte{132} +var lengthBufExecutionTrace = []byte{133} func (t *ExecutionTrace) MarshalCBOR(w io.Writer) error { if t == nil { @@ -2771,6 +2828,11 @@ func (t *ExecutionTrace) MarshalCBOR(w io.Writer) error { return err } + // t.InvokedActor (types.ActorTrace) (struct) + if err := t.InvokedActor.MarshalCBOR(cw); err != nil { + return err + } + // t.GasCharges ([]*types.GasTrace) (slice) if len(t.GasCharges) > 1000000000 { return xerrors.Errorf("Slice value in field t.GasCharges was too long") @@ -2820,7 +2882,7 @@ func (t *ExecutionTrace) UnmarshalCBOR(r io.Reader) (err error) { return fmt.Errorf("cbor input should be of type array") } - if extra != 4 { + if extra != 5 { return fmt.Errorf("cbor input had wrong number of fields") } @@ -2841,6 +2903,25 @@ func (t *ExecutionTrace) UnmarshalCBOR(r io.Reader) (err error) { return xerrors.Errorf("unmarshaling t.MsgRct: %w", err) } + } + // t.InvokedActor (types.ActorTrace) (struct) + + { + + b, err := cr.ReadByte() + if err != nil { + return err + } + if b != cbg.CborNull[0] { + if err := cr.UnreadByte(); err != nil { + return err + } + t.InvokedActor = new(ActorTrace) + if err := t.InvokedActor.UnmarshalCBOR(cr); err != nil { + return xerrors.Errorf("unmarshaling t.InvokedActor pointer: %w", err) + } + } + } // t.GasCharges ([]*types.GasTrace) (slice) diff --git a/chain/types/execresult.go b/chain/types/execresult.go index 4556f7b88..99bbb6ece 100644 --- a/chain/types/execresult.go +++ b/chain/types/execresult.go @@ -4,8 +4,6 @@ import ( "encoding/json" "time" - "github.com/ipfs/go-cid" - "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/exitcode" @@ -28,7 +26,11 @@ type MessageTrace struct { ParamsCodec uint64 GasLimit uint64 ReadOnly bool - CodeCid cid.Cid +} + +type ActorTrace struct { + Id abi.ActorID + State Actor } type ReturnTrace struct { @@ -38,10 +40,11 @@ type ReturnTrace struct { } type ExecutionTrace struct { - Msg MessageTrace - MsgRct ReturnTrace - GasCharges []*GasTrace `cborgen:"maxlen=1000000000"` - Subcalls []ExecutionTrace `cborgen:"maxlen=1000000000"` + Msg MessageTrace + MsgRct ReturnTrace + InvokedActor *ActorTrace `json:",omitempty"` + GasCharges []*GasTrace `cborgen:"maxlen=1000000000"` + Subcalls []ExecutionTrace `cborgen:"maxlen=1000000000"` } func (et ExecutionTrace) SumGas() GasTrace { diff --git a/documentation/en/api-v0-methods.md b/documentation/en/api-v0-methods.md index 2eb683f2c..411190339 100644 --- a/documentation/en/api-v0-methods.md +++ b/documentation/en/api-v0-methods.md @@ -4878,16 +4878,27 @@ Response: "Params": "Ynl0ZSBhcnJheQ==", "ParamsCodec": 42, "GasLimit": 42, - "ReadOnly": true, - "CodeCid": { - "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" - } + "ReadOnly": true }, "MsgRct": { "ExitCode": 0, "Return": "Ynl0ZSBhcnJheQ==", "ReturnCodec": 42 }, + "InvokedActor": { + "Id": 1000, + "State": { + "Code": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Head": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Nonce": 42, + "Balance": "0", + "Address": "f01234" + } + }, "GasCharges": [ { "Name": "string value", @@ -4907,16 +4918,27 @@ Response: "Params": "Ynl0ZSBhcnJheQ==", "ParamsCodec": 42, "GasLimit": 42, - "ReadOnly": true, - "CodeCid": { - "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" - } + "ReadOnly": true }, "MsgRct": { "ExitCode": 0, "Return": "Ynl0ZSBhcnJheQ==", "ReturnCodec": 42 }, + "InvokedActor": { + "Id": 1000, + "State": { + "Code": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Head": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Nonce": 42, + "Balance": "0", + "Address": "f01234" + } + }, "GasCharges": [ { "Name": "string value", @@ -5118,16 +5140,27 @@ Response: "Params": "Ynl0ZSBhcnJheQ==", "ParamsCodec": 42, "GasLimit": 42, - "ReadOnly": true, - "CodeCid": { - "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" - } + "ReadOnly": true }, "MsgRct": { "ExitCode": 0, "Return": "Ynl0ZSBhcnJheQ==", "ReturnCodec": 42 }, + "InvokedActor": { + "Id": 1000, + "State": { + "Code": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Head": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Nonce": 42, + "Balance": "0", + "Address": "f01234" + } + }, "GasCharges": [ { "Name": "string value", @@ -5147,16 +5180,27 @@ Response: "Params": "Ynl0ZSBhcnJheQ==", "ParamsCodec": 42, "GasLimit": 42, - "ReadOnly": true, - "CodeCid": { - "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" - } + "ReadOnly": true }, "MsgRct": { "ExitCode": 0, "Return": "Ynl0ZSBhcnJheQ==", "ReturnCodec": 42 }, + "InvokedActor": { + "Id": 1000, + "State": { + "Code": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Head": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Nonce": 42, + "Balance": "0", + "Address": "f01234" + } + }, "GasCharges": [ { "Name": "string value", @@ -6518,16 +6562,27 @@ Response: "Params": "Ynl0ZSBhcnJheQ==", "ParamsCodec": 42, "GasLimit": 42, - "ReadOnly": true, - "CodeCid": { - "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" - } + "ReadOnly": true }, "MsgRct": { "ExitCode": 0, "Return": "Ynl0ZSBhcnJheQ==", "ReturnCodec": 42 }, + "InvokedActor": { + "Id": 1000, + "State": { + "Code": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Head": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Nonce": 42, + "Balance": "0", + "Address": "f01234" + } + }, "GasCharges": [ { "Name": "string value", @@ -6547,16 +6602,27 @@ Response: "Params": "Ynl0ZSBhcnJheQ==", "ParamsCodec": 42, "GasLimit": 42, - "ReadOnly": true, - "CodeCid": { - "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" - } + "ReadOnly": true }, "MsgRct": { "ExitCode": 0, "Return": "Ynl0ZSBhcnJheQ==", "ReturnCodec": 42 }, + "InvokedActor": { + "Id": 1000, + "State": { + "Code": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Head": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Nonce": 42, + "Balance": "0", + "Address": "f01234" + } + }, "GasCharges": [ { "Name": "string value", diff --git a/documentation/en/api-v1-unstable-methods.md b/documentation/en/api-v1-unstable-methods.md index 6c315a92d..c24cdc6dd 100644 --- a/documentation/en/api-v1-unstable-methods.md +++ b/documentation/en/api-v1-unstable-methods.md @@ -6375,16 +6375,27 @@ Response: "Params": "Ynl0ZSBhcnJheQ==", "ParamsCodec": 42, "GasLimit": 42, - "ReadOnly": true, - "CodeCid": { - "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" - } + "ReadOnly": true }, "MsgRct": { "ExitCode": 0, "Return": "Ynl0ZSBhcnJheQ==", "ReturnCodec": 42 }, + "InvokedActor": { + "Id": 1000, + "State": { + "Code": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Head": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Nonce": 42, + "Balance": "0", + "Address": "f01234" + } + }, "GasCharges": [ { "Name": "string value", @@ -6404,16 +6415,27 @@ Response: "Params": "Ynl0ZSBhcnJheQ==", "ParamsCodec": 42, "GasLimit": 42, - "ReadOnly": true, - "CodeCid": { - "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" - } + "ReadOnly": true }, "MsgRct": { "ExitCode": 0, "Return": "Ynl0ZSBhcnJheQ==", "ReturnCodec": 42 }, + "InvokedActor": { + "Id": 1000, + "State": { + "Code": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Head": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Nonce": 42, + "Balance": "0", + "Address": "f01234" + } + }, "GasCharges": [ { "Name": "string value", @@ -6615,16 +6637,27 @@ Response: "Params": "Ynl0ZSBhcnJheQ==", "ParamsCodec": 42, "GasLimit": 42, - "ReadOnly": true, - "CodeCid": { - "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" - } + "ReadOnly": true }, "MsgRct": { "ExitCode": 0, "Return": "Ynl0ZSBhcnJheQ==", "ReturnCodec": 42 }, + "InvokedActor": { + "Id": 1000, + "State": { + "Code": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Head": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Nonce": 42, + "Balance": "0", + "Address": "f01234" + } + }, "GasCharges": [ { "Name": "string value", @@ -6644,16 +6677,27 @@ Response: "Params": "Ynl0ZSBhcnJheQ==", "ParamsCodec": 42, "GasLimit": 42, - "ReadOnly": true, - "CodeCid": { - "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" - } + "ReadOnly": true }, "MsgRct": { "ExitCode": 0, "Return": "Ynl0ZSBhcnJheQ==", "ReturnCodec": 42 }, + "InvokedActor": { + "Id": 1000, + "State": { + "Code": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Head": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Nonce": 42, + "Balance": "0", + "Address": "f01234" + } + }, "GasCharges": [ { "Name": "string value", @@ -8144,16 +8188,27 @@ Response: "Params": "Ynl0ZSBhcnJheQ==", "ParamsCodec": 42, "GasLimit": 42, - "ReadOnly": true, - "CodeCid": { - "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" - } + "ReadOnly": true }, "MsgRct": { "ExitCode": 0, "Return": "Ynl0ZSBhcnJheQ==", "ReturnCodec": 42 }, + "InvokedActor": { + "Id": 1000, + "State": { + "Code": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Head": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Nonce": 42, + "Balance": "0", + "Address": "f01234" + } + }, "GasCharges": [ { "Name": "string value", @@ -8173,16 +8228,27 @@ Response: "Params": "Ynl0ZSBhcnJheQ==", "ParamsCodec": 42, "GasLimit": 42, - "ReadOnly": true, - "CodeCid": { - "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" - } + "ReadOnly": true }, "MsgRct": { "ExitCode": 0, "Return": "Ynl0ZSBhcnJheQ==", "ReturnCodec": 42 }, + "InvokedActor": { + "Id": 1000, + "State": { + "Code": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Head": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Nonce": 42, + "Balance": "0", + "Address": "f01234" + } + }, "GasCharges": [ { "Name": "string value", diff --git a/extern/filecoin-ffi b/extern/filecoin-ffi index 4176a1e66..b715c9403 160000 --- a/extern/filecoin-ffi +++ b/extern/filecoin-ffi @@ -1 +1 @@ -Subproject commit 4176a1e662e865834bfdc5861da921dc2d272b45 +Subproject commit b715c9403faf919e95fdc702cd651e842f18d890 diff --git a/gen/main.go b/gen/main.go index 942b3ac2c..d84343739 100644 --- a/gen/main.go +++ b/gen/main.go @@ -38,6 +38,7 @@ func main() { types.EventEntry{}, // Tracing types.GasTrace{}, + types.ActorTrace{}, types.MessageTrace{}, types.ReturnTrace{}, types.ExecutionTrace{}, diff --git a/go.sum b/go.sum index a7983d80c..57eb9bcd1 100644 --- a/go.sum +++ b/go.sum @@ -1821,6 +1821,7 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1883,7 +1884,9 @@ golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14= golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -1909,6 +1912,7 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20180202135801-37707fdb30a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -2014,6 +2018,7 @@ golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= @@ -2022,6 +2027,7 @@ golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9sn golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -2033,6 +2039,7 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -2101,6 +2108,7 @@ golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846 h1:Vve/L0v7CXXuxUmaMGIEK/dEeq7uiqb5qBgQrZzIE7E= golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/node/impl/full/eth_trace.go b/node/impl/full/eth_trace.go index 9d24394d7..123d96fe1 100644 --- a/node/impl/full/eth_trace.go +++ b/node/impl/full/eth_trace.go @@ -56,6 +56,11 @@ func buildTraces(traces *[]*ethtypes.EthTrace, parent *ethtypes.EthTrace, addr [ return nil } + // Skip the trace if we never reached the point where we invoked this actor. + if et.InvokedActor == nil { + return nil + } + trace := ðtypes.EthTrace{ Action: ethtypes.EthTraceAction{ From: from, @@ -67,7 +72,7 @@ func buildTraces(traces *[]*ethtypes.EthTrace, parent *ethtypes.EthTrace, addr [ FilecoinFrom: et.Msg.From, FilecoinTo: et.Msg.To, FilecoinMethod: et.Msg.Method, - FilecoinCodeCid: et.Msg.CodeCid, + FilecoinCodeCid: et.InvokedActor.State.Code, }, Result: ethtypes.EthTraceResult{ GasUsed: ethtypes.EthUint64(et.SumGas().TotalGas), @@ -137,7 +142,7 @@ func buildTraces(traces *[]*ethtypes.EthTrace, parent *ethtypes.EthTrace, addr [ if parent.Action.FilecoinTo == builtin.InitActorAddr && parent.Action.FilecoinMethod == builtin.MethodsInit.Exec && et.Msg.Method == builtin.MethodConstructor { - log.Debugf("COND3 Native actor creation! method:%d, code:%s, height:%d", et.Msg.Method, et.Msg.CodeCid.String(), height) + log.Debugf("COND3 Native actor creation! method:%d, code:%s, height:%d", et.Msg.Method, et.InvokedActor.State.Code.String(), height) parent.SetCallType("create") parent.Action.To = to parent.Action.Input = []byte{0xFE} @@ -165,7 +170,7 @@ func buildTraces(traces *[]*ethtypes.EthTrace, parent *ethtypes.EthTrace, addr [ // TODO: We need to handle failures in contract creations and support resurrections on an existing but dead EVM actor) if calledCreateOnEAM && eamCalledInitOnExec4 && initCreatedActor { - log.Debugf("COND4 EVM contract creation method:%d, code:%s, height:%d", et.Msg.Method, et.Msg.CodeCid.String(), height) + log.Debugf("COND4 EVM contract creation method:%d, code:%s, height:%d", et.Msg.Method, et.InvokedActor.State.Code.String(), height) if parent.Parent.Action.FilecoinMethod == builtin.MethodsEAM.Create { parent.Parent.SetCallType("create") From 16b3f12c8eb2ed4fd344d7c6b2ccd7d57589b5c7 Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Mon, 12 Feb 2024 22:44:11 +1100 Subject: [PATCH 083/394] Add lotus-provider to build to match install --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index a17b50d09..236d2d98e 100644 --- a/Makefile +++ b/Makefile @@ -124,7 +124,7 @@ lotus-gateway: $(BUILD_DEPS) .PHONY: lotus-gateway BINS+=lotus-gateway -build: lotus lotus-miner lotus-worker +build: lotus lotus-miner lotus-worker lotus-provider @[[ $$(type -P "lotus") ]] && echo "Caution: you have \ an existing lotus binary in your PATH. This may cause problems if you don't run 'sudo make install'" || true From 425e672ef6deaefa05b5906c82dab892df758577 Mon Sep 17 00:00:00 2001 From: "Andrew Jackson (Ajax)" Date: Mon, 12 Feb 2024 19:03:45 -0600 Subject: [PATCH 084/394] lp:feat multiaddr, comments --- cmd/lotus-provider/deps/deps.go | 8 ++++---- cmd/lotus-provider/proving.go | 2 +- go.mod | 7 ------- go.sum | 8 -------- provider/address.go | 8 ++++---- provider/builder.go | 4 ++-- provider/lpwindow/compute_task.go | 7 +++---- provider/lpwindow/faults_simple.go | 2 +- provider/lpwindow/recover_task.go | 12 ++++++------ provider/lpwindow/submit_task.go | 8 ++++---- provider/lpwinning/winning_task.go | 6 +++--- .../multictladdr}/multiaddresses.go | 11 ++++++++--- storage/ctladdr/addresses.go | 4 ++-- 13 files changed, 38 insertions(+), 49 deletions(-) rename {storage/ctladdr => provider/multictladdr}/multiaddresses.go (83%) diff --git a/cmd/lotus-provider/deps/deps.go b/cmd/lotus-provider/deps/deps.go index d5ccd30c3..1e6180d71 100644 --- a/cmd/lotus-provider/deps/deps.go +++ b/cmd/lotus-provider/deps/deps.go @@ -35,7 +35,7 @@ import ( "github.com/filecoin-project/lotus/node/modules/dtypes" "github.com/filecoin-project/lotus/node/repo" "github.com/filecoin-project/lotus/provider" - "github.com/filecoin-project/lotus/storage/ctladdr" + "github.com/filecoin-project/lotus/provider/multictladdr" "github.com/filecoin-project/lotus/storage/paths" "github.com/filecoin-project/lotus/storage/sealer" "github.com/filecoin-project/lotus/storage/sealer/ffiwrapper" @@ -96,8 +96,8 @@ type Deps struct { Full api.FullNode Verif storiface.Verifier LW *sealer.LocalWorker - As *ctladdr.MultiAddressSelector - Maddrs []dtypes.MinerAddress + As *multictladdr.MultiAddressSelector + Maddrs map[dtypes.MinerAddress]bool Stor *paths.Remote Si *paths.DBIndex LocalStore *paths.Local @@ -243,7 +243,7 @@ Get it with: jq .PrivateKey ~/.lotus-miner/keystore/MF2XI2BNNJ3XILLQOJUXMYLUMU`, if err != nil { return err } - deps.Maddrs = append(deps.Maddrs, dtypes.MinerAddress(addr)) + deps.Maddrs[dtypes.MinerAddress(addr)] = true } } } diff --git a/cmd/lotus-provider/proving.go b/cmd/lotus-provider/proving.go index 175372249..eaef45db7 100644 --- a/cmd/lotus-provider/proving.go +++ b/cmd/lotus-provider/proving.go @@ -189,7 +189,7 @@ It will not send any messages to the chain. Since it can compute any deadline, o di := dline.NewInfo(head.Height(), cctx.Uint64("deadline"), 0, 0, 0, 10 /*challenge window*/, 0, 0) - for _, maddr := range deps.Maddrs { + for maddr := range deps.Maddrs { out, err := wdPostTask.DoPartition(ctx, head, address.Address(maddr), di, cctx.Uint64("partition")) if err != nil { fmt.Println("Error computing WindowPoSt for miner", maddr, err) diff --git a/go.mod b/go.mod index 1acbfa7e8..c1b353fab 100644 --- a/go.mod +++ b/go.mod @@ -166,12 +166,6 @@ require ( gotest.tools v2.2.0+incompatible ) -require ( - github.com/bahlo/generic-list-go v0.2.0 // indirect - github.com/buger/jsonparser v1.1.1 // indirect - github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect -) - require ( github.com/GeertJohan/go.incremental v1.0.0 // indirect github.com/PuerkitoBio/purell v1.1.1 // indirect @@ -231,7 +225,6 @@ require ( github.com/hashicorp/golang-lru v0.6.0 // indirect github.com/huin/goupnp v1.2.0 // indirect github.com/iancoleman/orderedmap v0.1.0 // indirect - github.com/invopop/jsonschema v0.12.0 github.com/ipfs/go-bitfield v1.1.0 // indirect github.com/ipfs/go-blockservice v0.5.1 // indirect github.com/ipfs/go-ipfs-blockstore v1.3.0 // indirect diff --git a/go.sum b/go.sum index 8cc338de2..a7983d80c 100644 --- a/go.sum +++ b/go.sum @@ -102,8 +102,6 @@ github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6l github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= -github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk= -github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o= @@ -137,8 +135,6 @@ github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46f github.com/buger/goterm v1.0.3 h1:7V/HeAQHrzPk/U4BvyH2g9u+xbUW9nr4yRPyG59W4fM= github.com/buger/goterm v1.0.3/go.mod h1:HiFWV3xnkolgrBV3mY8m0X0Pumt4zg4QhbdOzQtB8tE= github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= -github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs= -github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -634,8 +630,6 @@ github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANyt github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= github.com/influxdata/influxdb1-client v0.0.0-20200827194710-b269163b24ab h1:HqW4xhhynfjrtEiiSGcQUd6vrK23iMam1FO8rI7mwig= github.com/influxdata/influxdb1-client v0.0.0-20200827194710-b269163b24ab/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= -github.com/invopop/jsonschema v0.12.0 h1:6ovsNSuvn9wEQVOyc72aycBMVQFKz7cPdMJn10CvzRI= -github.com/invopop/jsonschema v0.12.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0= github.com/ipfs/bbloom v0.0.1/go.mod h1:oqo8CVWsJFMOZqTglBG4wydCE4IQA/G2/SEofB0rjUI= github.com/ipfs/bbloom v0.0.4 h1:Gi+8EGJ2y5qiD5FbsbpX/TMNcJw8gSqr7eyjHa4Fhvs= github.com/ipfs/bbloom v0.0.4/go.mod h1:cS9YprKXpoZ9lT0n/Mw/a6/aFV6DTjTLYHeA+gyqMG0= @@ -1641,8 +1635,6 @@ github.com/whyrusleeping/mdns v0.0.0-20180901202407-ef14215e6b30/go.mod h1:j4l84 github.com/whyrusleeping/mdns v0.0.0-20190826153040-b9b60ed33aa9/go.mod h1:j4l84WPFclQPj320J9gp0XwNKBb3U0zt5CBqjPp22G4= github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7 h1:E9S12nwJwEOXe2d6gT6qxdvqMnNq+VnSsKPgm2ZZNds= github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7/go.mod h1:X2c0RVCI1eSUFI8eLcY3c0423ykwiUdxLJtkDvruhjI= -github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc= -github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw= github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7VPsoEPHyzalCE06qoARUCeBBE= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= diff --git a/provider/address.go b/provider/address.go index 298fe8784..84a10a5d7 100644 --- a/provider/address.go +++ b/provider/address.go @@ -7,12 +7,12 @@ import ( "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/node/config" - "github.com/filecoin-project/lotus/storage/ctladdr" + "github.com/filecoin-project/lotus/provider/multictladdr" ) -func AddressSelector(addrConf []config.LotusProviderAddresses) func() (*ctladdr.MultiAddressSelector, error) { - return func() (*ctladdr.MultiAddressSelector, error) { - as := &ctladdr.MultiAddressSelector{ +func AddressSelector(addrConf []config.LotusProviderAddresses) func() (*multictladdr.MultiAddressSelector, error) { + return func() (*multictladdr.MultiAddressSelector, error) { + as := &multictladdr.MultiAddressSelector{ MinerMap: make(map[address.Address]api.AddressConfig), } if addrConf == nil { diff --git a/provider/builder.go b/provider/builder.go index 82781d211..cff387970 100644 --- a/provider/builder.go +++ b/provider/builder.go @@ -11,7 +11,7 @@ import ( "github.com/filecoin-project/lotus/provider/chainsched" "github.com/filecoin-project/lotus/provider/lpmessage" "github.com/filecoin-project/lotus/provider/lpwindow" - "github.com/filecoin-project/lotus/storage/ctladdr" + "github.com/filecoin-project/lotus/provider/multictladdr" "github.com/filecoin-project/lotus/storage/paths" "github.com/filecoin-project/lotus/storage/sealer" "github.com/filecoin-project/lotus/storage/sealer/storiface" @@ -21,7 +21,7 @@ import ( func WindowPostScheduler(ctx context.Context, fc config.LotusProviderFees, pc config.ProvingConfig, api api.FullNode, verif storiface.Verifier, lw *sealer.LocalWorker, sender *lpmessage.Sender, - as *ctladdr.MultiAddressSelector, addresses []dtypes.MinerAddress, db *harmonydb.DB, + as *multictladdr.MultiAddressSelector, addresses map[dtypes.MinerAddress]bool, db *harmonydb.DB, stor paths.Store, idx paths.SectorIndex, max int) (*lpwindow.WdPostTask, *lpwindow.WdPostSubmitTask, *lpwindow.WdPostRecoverDeclareTask, error) { chainSched := chainsched.New(api) diff --git a/provider/lpwindow/compute_task.go b/provider/lpwindow/compute_task.go index e9d582704..9fc4afe4f 100644 --- a/provider/lpwindow/compute_task.go +++ b/provider/lpwindow/compute_task.go @@ -70,7 +70,7 @@ type WdPostTask struct { windowPoStTF promise.Promise[harmonytask.AddTaskFunc] - actors []dtypes.MinerAddress + actors map[dtypes.MinerAddress]bool max int } @@ -86,9 +86,8 @@ func NewWdPostTask(db *harmonydb.DB, faultTracker sealer.FaultTracker, prover ProverPoSt, verifier storiface.Verifier, - pcs *chainsched.ProviderChainSched, - actors []dtypes.MinerAddress, + actors map[dtypes.MinerAddress]bool, max int, ) (*WdPostTask, error) { t := &WdPostTask{ @@ -356,7 +355,7 @@ func (t *WdPostTask) Adder(taskFunc harmonytask.AddTaskFunc) { } func (t *WdPostTask) processHeadChange(ctx context.Context, revert, apply *types.TipSet) error { - for _, act := range t.actors { + for act := range t.actors { maddr := address.Address(act) aid, err := address.IDFromAddress(maddr) diff --git a/provider/lpwindow/faults_simple.go b/provider/lpwindow/faults_simple.go index d43e8ee19..b596fb5a7 100644 --- a/provider/lpwindow/faults_simple.go +++ b/provider/lpwindow/faults_simple.go @@ -110,7 +110,7 @@ func (m *SimpleFaultTracker) CheckProvable(ctx context.Context, pp abi.Registere if !locked { log.Warnw("CheckProvable Sector FAULT: can't acquire read lock", "sector", sector) - addBad(sector.ID, fmt.Sprint("can't acquire read lock")) + addBad(sector.ID, "can't acquire read lock") return } diff --git a/provider/lpwindow/recover_task.go b/provider/lpwindow/recover_task.go index bd88db4dc..076ed51c1 100644 --- a/provider/lpwindow/recover_task.go +++ b/provider/lpwindow/recover_task.go @@ -22,7 +22,7 @@ import ( "github.com/filecoin-project/lotus/node/modules/dtypes" "github.com/filecoin-project/lotus/provider/chainsched" "github.com/filecoin-project/lotus/provider/lpmessage" - "github.com/filecoin-project/lotus/storage/ctladdr" + "github.com/filecoin-project/lotus/provider/multictladdr" "github.com/filecoin-project/lotus/storage/sealer" "github.com/filecoin-project/lotus/storage/wdpost" ) @@ -34,8 +34,8 @@ type WdPostRecoverDeclareTask struct { faultTracker sealer.FaultTracker maxDeclareRecoveriesGasFee types.FIL - as *ctladdr.MultiAddressSelector - actors []dtypes.MinerAddress + as *multictladdr.MultiAddressSelector + actors map[dtypes.MinerAddress]bool startCheckTF promise.Promise[harmonytask.AddTaskFunc] } @@ -61,11 +61,11 @@ func NewWdPostRecoverDeclareTask(sender *lpmessage.Sender, db *harmonydb.DB, api WdPostRecoverDeclareTaskApi, faultTracker sealer.FaultTracker, - as *ctladdr.MultiAddressSelector, + as *multictladdr.MultiAddressSelector, pcs *chainsched.ProviderChainSched, maxDeclareRecoveriesGasFee types.FIL, - actors []dtypes.MinerAddress) (*WdPostRecoverDeclareTask, error) { + actors map[dtypes.MinerAddress]bool) (*WdPostRecoverDeclareTask, error) { t := &WdPostRecoverDeclareTask{ sender: sender, db: db, @@ -235,7 +235,7 @@ func (w *WdPostRecoverDeclareTask) Adder(taskFunc harmonytask.AddTaskFunc) { func (w *WdPostRecoverDeclareTask) processHeadChange(ctx context.Context, revert, apply *types.TipSet) error { tf := w.startCheckTF.Val(ctx) - for _, act := range w.actors { + for act := range w.actors { maddr := address.Address(act) aid, err := address.IDFromAddress(maddr) diff --git a/provider/lpwindow/submit_task.go b/provider/lpwindow/submit_task.go index 6283c7f74..d6937354b 100644 --- a/provider/lpwindow/submit_task.go +++ b/provider/lpwindow/submit_task.go @@ -21,7 +21,7 @@ import ( "github.com/filecoin-project/lotus/lib/promise" "github.com/filecoin-project/lotus/provider/chainsched" "github.com/filecoin-project/lotus/provider/lpmessage" - "github.com/filecoin-project/lotus/storage/ctladdr" + "github.com/filecoin-project/lotus/provider/multictladdr" "github.com/filecoin-project/lotus/storage/wdpost" ) @@ -47,12 +47,12 @@ type WdPostSubmitTask struct { api WdPoStSubmitTaskApi maxWindowPoStGasFee types.FIL - as *ctladdr.MultiAddressSelector + as *multictladdr.MultiAddressSelector submitPoStTF promise.Promise[harmonytask.AddTaskFunc] } -func NewWdPostSubmitTask(pcs *chainsched.ProviderChainSched, send *lpmessage.Sender, db *harmonydb.DB, api WdPoStSubmitTaskApi, maxWindowPoStGasFee types.FIL, as *ctladdr.MultiAddressSelector) (*WdPostSubmitTask, error) { +func NewWdPostSubmitTask(pcs *chainsched.ProviderChainSched, send *lpmessage.Sender, db *harmonydb.DB, api WdPoStSubmitTaskApi, maxWindowPoStGasFee types.FIL, as *multictladdr.MultiAddressSelector) (*WdPostSubmitTask, error) { res := &WdPostSubmitTask{ sender: send, db: db, @@ -248,7 +248,7 @@ type MsgPrepAPI interface { StateLookupID(context.Context, address.Address, types.TipSetKey) (address.Address, error) } -func preparePoStMessage(w MsgPrepAPI, as *ctladdr.MultiAddressSelector, maddr address.Address, msg *types.Message, maxFee abi.TokenAmount) (*types.Message, *api.MessageSendSpec, error) { +func preparePoStMessage(w MsgPrepAPI, as *multictladdr.MultiAddressSelector, maddr address.Address, msg *types.Message, maxFee abi.TokenAmount) (*types.Message, *api.MessageSendSpec, error) { mi, err := w.StateMinerInfo(context.Background(), maddr, types.EmptyTSK) if err != nil { return nil, nil, xerrors.Errorf("error getting miner info: %w", err) diff --git a/provider/lpwinning/winning_task.go b/provider/lpwinning/winning_task.go index bf4f2fe71..8b289a8de 100644 --- a/provider/lpwinning/winning_task.go +++ b/provider/lpwinning/winning_task.go @@ -42,7 +42,7 @@ type WinPostTask struct { verifier storiface.Verifier api WinPostAPI - actors []dtypes.MinerAddress + actors map[dtypes.MinerAddress]bool mineTF promise.Promise[harmonytask.AddTaskFunc] } @@ -70,7 +70,7 @@ type ProverWinningPoSt interface { GenerateWinningPoSt(ctx context.Context, ppt abi.RegisteredPoStProof, minerID abi.ActorID, sectorInfo []storiface.PostSectorChallenge, randomness abi.PoStRandomness) ([]prooftypes.PoStProof, error) } -func NewWinPostTask(max int, db *harmonydb.DB, prover ProverWinningPoSt, verifier storiface.Verifier, api WinPostAPI, actors []dtypes.MinerAddress) *WinPostTask { +func NewWinPostTask(max int, db *harmonydb.DB, prover ProverWinningPoSt, verifier storiface.Verifier, api WinPostAPI, actors map[dtypes.MinerAddress]bool) *WinPostTask { t := &WinPostTask{ max: max, db: db, @@ -572,7 +572,7 @@ func (t *WinPostTask) mineBasic(ctx context.Context) { baseEpoch := workBase.TipSet.Height() - for _, act := range t.actors { + for act := range t.actors { spID, err := address.IDFromAddress(address.Address(act)) if err != nil { log.Errorf("failed to get spID from address %s: %s", act, err) diff --git a/storage/ctladdr/multiaddresses.go b/provider/multictladdr/multiaddresses.go similarity index 83% rename from storage/ctladdr/multiaddresses.go rename to provider/multictladdr/multiaddresses.go index fbb153e84..4f1947729 100644 --- a/storage/ctladdr/multiaddresses.go +++ b/provider/multictladdr/multiaddresses.go @@ -1,21 +1,26 @@ -package ctladdr +package multictladdr import ( "context" + logging "github.com/ipfs/go-log/v2" + "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/big" "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/storage/ctladdr" ) +var log = logging.Logger("multictladdr") + type MultiAddressSelector struct { MinerMap map[address.Address]api.AddressConfig } -func (as *MultiAddressSelector) AddressFor(ctx context.Context, a NodeApi, minerID address.Address, mi api.MinerInfo, use api.AddrUse, goodFunds, minFunds abi.TokenAmount) (address.Address, abi.TokenAmount, error) { +func (as *MultiAddressSelector) AddressFor(ctx context.Context, a ctladdr.NodeApi, minerID address.Address, mi api.MinerInfo, use api.AddrUse, goodFunds, minFunds abi.TokenAmount) (address.Address, abi.TokenAmount, error) { if as == nil { // should only happen in some tests log.Warnw("smart address selection disabled, using worker address") @@ -72,5 +77,5 @@ func (as *MultiAddressSelector) AddressFor(ctx context.Context, a NodeApi, miner addrs = append(addrs, mi.Owner) } - return pickAddress(ctx, a, mi, goodFunds, minFunds, addrs) + return ctladdr.PickAddress(ctx, a, mi, goodFunds, minFunds, addrs) } diff --git a/storage/ctladdr/addresses.go b/storage/ctladdr/addresses.go index ee778cb38..3ffa4f41e 100644 --- a/storage/ctladdr/addresses.go +++ b/storage/ctladdr/addresses.go @@ -82,10 +82,10 @@ func (as *AddressSelector) AddressFor(ctx context.Context, a NodeApi, mi api.Min addrs = append(addrs, mi.Owner) } - return pickAddress(ctx, a, mi, goodFunds, minFunds, addrs) + return PickAddress(ctx, a, mi, goodFunds, minFunds, addrs) } -func pickAddress(ctx context.Context, a NodeApi, mi api.MinerInfo, goodFunds, minFunds abi.TokenAmount, addrs []address.Address) (address.Address, abi.TokenAmount, error) { +func PickAddress(ctx context.Context, a NodeApi, mi api.MinerInfo, goodFunds, minFunds abi.TokenAmount, addrs []address.Address) (address.Address, abi.TokenAmount, error) { leastBad := mi.Worker bestAvail := minFunds From 846daa1788f5b275c07b9d2fd7067ff08fa0c560 Mon Sep 17 00:00:00 2001 From: "Andrew Jackson (Ajax)" Date: Mon, 12 Feb 2024 19:27:55 -0600 Subject: [PATCH 085/394] docsgen --- build/openrpc/full.json.gz | Bin 34617 -> 34608 bytes build/openrpc/gateway.json.gz | Bin 11884 -> 11872 bytes documentation/en/api-v0-methods.md | 1 - documentation/en/api-v1-unstable-methods.md | 1 - 4 files changed, 2 deletions(-) diff --git a/build/openrpc/full.json.gz b/build/openrpc/full.json.gz index a08ffb3f05df7d68dcc3304ffc5c2912d69cf863..3c30e81a9427867a286d476b5eb52110c995bcae 100644 GIT binary patch delta 8075 zcmV;6A9Uckjsmcb0)l^PVAGTWm|tM7z8x2jyDYaDo-a2j|WO8WB~Etw_fV@ zQn#17KSt{AHDOnr!+46gN!8a%-sVWGCuwt}R?692=>`d#E9Wm?i~Vzuta;9q%{rM& z(Rs>(m@cGE=8Q^XjZSDE5TKS;ase5u5)t1RXs2>I5| z{_g&+crLm^8I0uL7zOB9;l6YODJy>|+PJb`P>e?yg_2o4E(Yl?^BHHzblI~rI7E@^ zm4tOu{=qaM6$$eX^h~~taeyvStY1pWZ>S&BD1sctf)n{dF;aN^eV~?<7!-wpSJC_# zWc>3y6yLVMMg-X;|Hur1LKQsR`u@E;&_MfZw!#|p%yV5DFmJq}^t-vhLgf*gABfY7jS%s)CX#N&@a(h&vF0Px8G%S&!&@pXik5J2Vy^v zy`!Uz!?)@e3Om(m)hWw55XmK$&-7tuS=Wz5wXYhATk4zkwwinKiC~UlbQz&=jP$e9 z`#B0Y3Qyqj3N2t97M`1(-mY_n<`E1~sD|j>7^S?JH$`fy405h@xA`@PUor+ zQ|Y_Mjf=dJU=Oxa&Mfn_9u|LAJpX;Yb>#BbZA+ps5n7PJK6yC`bJe%Giiy~kgf+|E z)W%dyxE4?4h~EjLxTxCHHuB9aD_d^4mG$*%(X)QI(lfgWb6Y9tdLLX^PJMttJ#2wbt(-j^Hpt z>qYceky=lXswYQy{E2W!5n5=!!75-AZ)*oA_tsgy78%Xmp`x*LRM8QDN^btbOjLlf z4Fr&YA(EgqVGI`p&=`NPIU;?Ke=k~BiB@~!*9>I((&3wLy{L;(`#Vh_A#!P-AQ(oN zw1xG`(^%KSY6!WB&$QV_YpN;LQfk_La}}2vV>Wn~l$FXB3c|F+h{o2HC@DUBsn|=! zm4;F?$0`h?)+?tGyrhg@C&L+L3{xV_lY{MVgN+l+<_QFg)mZ zEs4)KL0}{bE>o{r?ka3A_j|eTBKKF@8Csedgv5VQn(WpHm2O9OVJWJw2^C<- zxQR$nvI!!`Qv~7?;ZO1 z-+caI{~e1z|DU7a>;1>0_t!fg*(v>Sb043d-Tm?3e6)Wt*pQf}Vq(|k$oPs$NEes% zny0z2bpV45Z<7IvyL-E@58mv)-Q9nis{mszzn+P-I^5flU1f-{gh|!rG=g&`zE0X9 z5@Irz<7gI|HBLu}pb-wR`VLi(Z%{1b;Qo_P4t4mzXNE%icPR?ud)jaXr36%6+UI%3|QyDQc2P03}Z)RUdHH{ z&Ssda6U=S|R6r5AdL zs-1svj>myw%x^_VaK}Nwg5(t}UOOv=23P2*p9xd!;IJ!pJhj*#782{bO}B{2w7$U) z3FGt$#4LPipXNG9R8!8m(wi#!fQ28U`0O`={|RBAsMvN9q*>P> z5_MB$G9DV^7t&%am6u#see@NJKRb+Jo;`my`4Ll<#Dq1xP-R5Tj|W3h*y{pAp%?D! z#a*PUq9X9Ze&;lX@1iI@jO(BaY4BPWpI)JW-XXEW7vG#&_hikp{Owuwbj^EH$TAxE zo#*X7`qnKBhBwI1I(%0Qc^`99*sb3*BDTp*L|!FYW1|-J*Sz485pvv^Wzw#0Z?%7z zez>|%6%Y9^Yw<)!jr1PWe&_N<|EXV&`FAd66RX1EzZ+L@akS(}|CXM%|0FQNeEBZ? zf7w%+c$LGbMZJ%Zonw-_2JB_H$knfME#y1oLAk`R_db&wg!R1)`62#!d-B{&`fnSN z_2l$3_RA9_<@S5J{`Ofz@@X0M>$rb>SgU@+*mP~y7xPgz1wz$UZ_e+{`Mo*6H|O`} z{HLxr_vZX{l%9i?NN<e0VHIoR8L@fTXBwJOv76ONtjb>OyyTGili~Z5XY3T9`8!67p^Of z64@#%8zpk@T$I1#B#sf`#rSJ6cO}SY`s@=4(Y=aw{&S8pjqEt0EQ2~bp1>$V!~#E) z=IG;u)6pn>_Em<)$wsoP)RU5LDSxz9jgM2&+_!M4ksDm+3Kaw420kWdfJ;#|L!9oY z+EdLbM6%{f``0y7$hl@|#=6(c&*&EyoB3vICAyb!9jyhbkP&&53%6PGKj6}BuCK0| zjm6>W%eQgr&Fe4V_8Lon0r&C6==MsaFOAFD0aRjvl8-=zL%eBQd}@r|Lj+14QqYq= za2kJ+69KNoPJF4SzTz}_upqF5J_hWuw*X4Z*KG8|)uSd_vOb}4LI2#ce0L?xm|7bO z@Mm2)E~#_?t)Sz&e8jeFdKJimrrS~%&iBz(8MfK8O>|mV(^0DSVq(EGYLU(mmnj{M z!H;#Aqw(jG$`dh_-D%>$Id+J+ z`8fR78++LB#*!H(V}LnWK$iLxBm|UzU;;52 z+!E2s4DksK!2*9r=H4%WM2#?DaH)b&weoA|Eg&PmXnL9{LTkAaHQ`*W>llm8PnW|C zM3hcZ2$H#C9ghr4X)|C9*{c}MV;mS#l)RGsXT=2TL!`$Xh2RyyL{O6H8yV6;w4!S$ z1lB~_JZIx$totqoXa;#Ok;TPhxw-1@iDvZu2xIssbAT z#DmS>`a|3UD=v-No|f>dPi2++cb=A5GD1`!hQxmgg@B=G^h%W(ib)Y@8d@P1LN}$h z2|h`YG@|hggh3_ts$&ZrL^QwS*OgubVA#aqO3bb@|=wYF>{)iVKz6^&YST*rn6ig4Js8# zQSg6@5HDK4{Q%h+o?$M1mmeT|kI*rkXD=^fG{ebEKRB1WL%#}vFHQ2b4TUj*&A0YcI(@h)FAR9QQOj0~oQoXxxn=&tl z{0K7i9-$-ESMlV}I~1QXj%QMyTQAgYVML$|gvuV>;Cbqj9VNt2mM}xA7Yd^3RBm~g zlA9Q+!-k$_#krK9Y4&t-oFM9*lqPCQX46poxY){}o= z0^_k>;}=SFDA}X-aB;XA%UKwxf0+7*^Nqv(t@J-3g07RH(5zB%(U1F&9(R&N(RZYKOz|W!p6LP!AH0^t$Gfc=sYvfqUaB5U*yHD#Stn0H4S5A$!Pu>5%&{DIE$4J! zw+d{n7)PO(p3fERY?b(#S=93OHR*q3OV(vzj9_rO{j4QN%X~;V;o{O>CT^;N1cb2j z9UCnY*N5QC#dcFa6MdN!D6NR9ik9}{WyG{JnHIDDvrZu9 zSJ6_>{8ndPoAPcpRlK}&fmNt?{y|Af&-1Nksf3-ZAB9B>LEAy9u62w9q6M0qL{Bw6MIvarao%)cKXJXhBkMJ4SZSxiuj zjGxF@#>o&9sqq`Rrc$DVHDAYiK71B%#9%`OX0E!$}S!?d!z@XK;V{0(iBMX z)w_avS5WT?>Rmxy+`-Fl;SUx;Dep*i01>kHNC+3zsVmGLQIWaAdgbIw>)PL<_uQxD zU{Z~rNaKz;s7WE(+4_IzN<0oWKI-c`ZLljprdNMl?3gXhIM&zgPkywz@lKU1@B2^vqLv0O+&aCFrarhx2H@LYK$VQl}m96G}XpBc_7I zhFt6JDj-p#=g8w05_Mi`;>#ZW?6LRK9~UlPz1qu~S#3ENHSK>-a)W2tmH$&1N6U6< zg0AX>^sF0wanL?*x0@i1)hGObcqIsi$1bEvJoFai1MHJytm_RZ>#x`DW4N*JH>L5~ z63b^*@~BO4rR$O;iU1~A2)Ev{BpGZ6peuW58sRMpY+C!^3@&AK_YeUX1W61z5_DzC z2tk|;K*CV9&H;Zo1!Kh1inj!1@8rhXZ=3zL`GU61CQzZZAccBFp`R5|$RF9ThY{*w zgnAgErv@YRGoJ!>nh^PQPEHteyskAWCGyZGfioKTn4(g?x;_Zl9rij3WE_n!30azO z?Z4*f3+cSg9|m4@2H9SxewjM8S?kRTwN=VRn@+P*$_sxkneg)mqs}Nf24( zuPK>8b!zDLTaiuZp|_*7F@FnHj?d1wr@|okZBaSSY@1d3#?feXOH!*9j%1(*p&CO0 zk8;KN|=^N(b=-Nx6TbEo|vyn@>;G$_%ofHuhr-V!o!N z78V)nEB_E9j*l6pFqeFqtBN-&Pa;{7F(77cNE0yMkW*ZQ6OLhFQbDR6S0!X)s>omhHQ7~Zl`i7^`5`(NJV6v)sW4>+` z@a})+Es=^YR@%C}Dga`n`U!^mPDesGo*{q*A0rqbp`=2kg$6Y25MZ-cXxtT=(ln`v z-7lALQCQyN=!?APqW9rfm5k`cIM@h>RW>u8d#AcSq zRPD~B3ly*3`YL$n&R|s2LkCRHO1^A!Q3yE%0n9E@s?ExbQ{FnH3_`I+zV2l*}kCd)izNLVY5e^Xv5D3wnPd0mSr5;?V2UmLH z;7UDC(9^&PDl2Yk|9Y@!q`jR72}kPDje2yW9^I%%H|o)idUPXw96Z>nL)3p=lHgcq zibpW?KMq=zR~!d_1HY=z+jz#a=vANbZZ~c>ybgJJ4jWAKcGFUIW&n@U1cv!=@gWRN zem{+7J23V%mG3V`kWtc|xvSR@pIA~cO`tfo>0<|bR4ygWGt2ycuw4hHW|rBUVp2}W z<}7ezb!UH~y0_9Ka3sBIk<))d*ck2{#EoiLh!$pq4HF_JNME9GAn(7Vc}s=Fq2w11 z&>f1!N}K|?fJ-on=?q}rM*%*D+4>@h%2B!cp(Uo%qf!+8Czl+?EH7lgXZ{XM8)Ay?mAF@z4-X)8rdZD4(vt98)5-Y>C^3rRs zg&bHZB>-D8j%I%HpwiM+Kis)jE|x9{frLaY_4_N0rzrk(qHgLRK_*QM$2iof83!9j zQaYMn#=f#8j_WWUtK)yVTls4)tu5OH3y*ve%h>p>t?gIx|C_C?L;3&zYz#I|Rr1v8 zm#GmauceFi`{n%Id>&!3q`Ywla^uu4U85OHPY@-QK}?8RD8?j&9KGUrhN{d$ZLM9U z8)_UnV;`w+<;r0;NBo8%o0r`rn6&1?i@UpauwQp~O$d)z~4 zC$h{r8?R6ZErMMrg?av(6+^q}01_}nAQ74nr+~><00M#*(GmnPl3sjea#B9w&09hc zUx-5%v$%O8t)rhU)IA%#fJNZj6JeTA*L9-s>y0W$%O#Oc4qM z6X`YEub3Rugs6Wc<|`^6AsA3{N_dPlOIK9pN))@!3FNS{-nW5h_W8 z2OILQqa{a`sh0(TODHz!PW4GeGKaNT*w9o7xi5F=!!d-lDA#TX)*X3GxI>CZXV>q> z;~0&FigNhx#*LJ#O&?uM$r)lC&gNPv5p(?-k6o+3r?G!1PGBUR7aD;G5w26An#Aiz z5zG49-R!tV!F<|1VNd-f*SXr;`ajF5(9c;`-1{@>=QDtXiR*)sAm?V=6FJEf-} zW!tH(YjLlodN(?oz3_jA;VU_5;nMQt5kHi1g}b)#X!uHA)YqrgqQ zStHl*Jc-9hdb+gnB7ngJ4ZsXe#rnY<%qe4d7)gJ~8fnMoRNXg__=tCyCM;U!Lu}SI zalg2E`nK+dcly27o1|;|Wm7WUO;s1)Y}RZ->in~nGj%ATm~aTqeF){K1#k>;@00C) zvd>0x9Ms*zUXtL8UI8}}sjQXw$dho#ff!O6t@7FCX;%vtHPD{&rNnfzkSXwyEswp>eWP)Ir zLP}0L)i|CUenQp4v_DjhgiBxc006}`yj_2D<3r8O6EuQJ#OnZT+?H~lNOka)2b5D4 z(FcNXs^PzBH5d4*f@{i-uc0bzZm};Uh4q8BdfcIo7ROG_{RH!=Bh(@c(XQ;gz15zV zZEEWtmkXS&+PHM1q5Ma|9P1e_?-;b5(Leo$^umsXdt19TLH=r*oZgFx*K(Pt%I<$! zppH@c11(o&>apo}63bg)c-89BE!!K!JjQpBBQQl0u)X$?6pf! z+Ko;v88MmALJqq)0M`U^`S`pnV(Z2M)Ofd^$>zFOle$tZkB_jey7>{h0{A(Yj#82x zS&VEJS4jxbYPEeRe;&jyTR!xLnbLpK+ON4gqvqA8*%}+Ns=OlHS_|Vg=Q>6ILCu-lD0Vrt+jhsP4ItGmr9?Z zs)U2*C!OWr1|~@AFQ>3TYr;+mp=>DpBU$wakF4R>frmi+sA9k~3=}PHkcr5z8c}w2iK^#c{X>nebqjCs_5gP!8 zg89zt{pq&#=c@>XV-(*K-4A~WO-hm(ig5rVQ+*q}vTN^vSK#RET4>nW^y}FBsz0RL zHX}CeE+ZKFy|5!Wa8MIdrRMr|>KAA!4l)8=S&TD^-B^ut9IjgXR^uwrs=~=B|Cea- zx+EnXrmlN(6T3GgpY3gL)x;O+S%b%}X%x0g9NYOFg>R$T|wV@o|$8=`@zMv#Ps^Aw1ileX!K0tPc zXISAje1PmdLdS5Py}XRk3@0=F;9O>xkf=SE`V&-vS63$C9}eds8sEjccrTy~E%;=~ z#;@ja}i#Z)o z-NqHOW|bka$en+=hM9%SK;t)12#p$AEkpqgRSY%+3lv4K^j${XiOx<1KOE}4xAH&W zir#IE2bZZ+{c~a4mHlm$GHYVVM&umXrkd?ExGn98ohz|%5i1@zoaUm)2LRfM+2p-y zIz!5!sbg#^qvO&_>AlI#TXJJvzz8W~GO^+W-kA&BFC|-o?&+CD85oaX8I Z`e5Vx_ix|-e*gdg|Ns3dZ5B@P1_0r8#q|IH delta 8033 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 diff --git a/build/openrpc/gateway.json.gz b/build/openrpc/gateway.json.gz index 39055fc6affd76e88bfc885a3f1708e95c5656e3..82d638d7e432e35816cbd7fe44b39594f9c6a5f9 100644 GIT binary patch delta 2572 zcmV+n3iI{sT;N==NhE)wD*o}{rd6^po`08qatR;u4@0u`J%^!I7IkR*;|XxbFs&$E zLFk{`=D_T9PkO(5-j}l&xQf(TAJv$KcdEyIuGf zpYDJEKRhy9=EdB>C`9biQfFU~YkLn@-VMI;J#TbN_WU50C`5lJ==FQMhx@&Q-rhl~ z1Mo5VdbDjoi@qFtZUEh29((|E2cJ4%Mv5253nEFVWFK<{VOr)9bm0hDh<=Bn$G6ZY zqUX=KhXGSsKL^m}f9JF7;(-ewUBZWwVK3nW5B3s1URB|ur4v?k9uQ^5r<<)kK4?Wvfoif#cZAhM!nSvr?pafbYbs`SSU zWPPMA7Es0PCHp7RMW~}9HW!G}*B0Wu+wOl>mDHklmUs^ZzE5GRgmhoC z6l4NO%rGHMYjkPz>(J(iHb=JUy)Gxg^gF=-IppPN>YY%jsJZV$ z7iax%KJx5z*6O)y!v*zq{V{`yBk0)i0+{@o0LOvuIC(;x5yx}vjYjdaFT^w=gG6M+ z+-P5z(G*e`ZIJF1!>1-z$;(fkcQ*vqU3wBH&~kqOwPREBkxj!*mMG*}q5C7$H1#b+ zdZad2xI@84i1cL`Akvg#h|r-bk7`0Da>5*QF+Nw^e@OcI3p(zy0sRn@3Vh#x%vj$C-J!mzpi1-$yL z@y4c<$+cU_iMh5FOgIl%o)xEOzisYYM4S0l+`entI!6HHgLA+Tul%^?_5p26ESbn zH^J3=eO(k~FCA33-D}}iBm?lbk-pf2x5KI`AlGo_fTbd{54#E~mW0z=$lf_dcF2F> z&)s{^x+eH9z~aM!>g4Zi;!R}knq4Z$7=~D@mB2!-k%o1QY{L(S56EW*MgX1BJKAQ5 z+%d&=7e~4bv<(zP+cC~@s7sDKe+q45xHNzf8&JRqnV4m~d6*z;Vj$N>;S$3!rgH}) zD}4KI2CO@1$D@Rs_0|BcZ2*G=vrcG0Fxg4#sP%k)RRghGyz zN6A~(kW5HcWMd-6AQVi(IEP|-QHr^UjF+&RqKMv**-7WyQrl0L^-PXB)7q5uu(9HQC7oJV zpx#MGax*MD33oLtUJhqjXs%pF{F`y-> zSc#FROh(zu-eiOT$JRD1FH}wVr-+Tl7>$}BQ$Om#Wb zHx55_EMEyh=hd$Ah9XK>3Y|hF=mwI7c_UU*5IE4TwYWAd_RiI1Cy7t#0(}KF`@(Kt zn4UonUHR(1sTb!i!U#BjR}QqtFextxEe!1wu)Ky3z_&Bcqk_YH9}?(gL7Q&TdoC?J zdXt4lTmBB0wQkrA3P0DM@73$K@H;GY_hj^mxbsN?1|o1!m^tp_znye_?))cJ1(Ess z3Sgv4%w-T%{&*pWeX1Zh)D|mQ9rFg!<}ve-jJ(ZIl1lH0sfpcxZknR8q7%^~e)GC@ z=5AFX?WFKHf-Vjp&DCxqEdsP=f(X&07|jb+vn!fIeadpl3Y!>T{JivKt#C>xoLQpC zYnlZu1ipUzHQgV$+v!ym@?@0mug?zsCuM~GhFGAlnV)8Un)y9j=GU*P()a}+tQrEq z`j)Y~qB>a=w7Za?v)hQR4iOoUaab^@#g*2xj-$d)%B+mI8r62lDRdZ+*-1T+#aW1 ziAa<ZdqD<8!yNm`4hH1%dMLq)0S7|EAT0Yn=P3h_mp!U*eLA zUuyqJmM#xUsycpw!yY{yQWT9j0L4O*^1DhZ%mON=#Bb&|Q;PNq=GO#4$pk=lx8uhG iD4ddtFRTm~Y3HzKK0bbb{C@!e0RR7cYS-^;jRF7%3JfFw delta 2623 zcmV-F3c&T?Tr`g87K!1UP90krww`GmW8-~vdO@S$YbOZdQpy@U@r$Sw2zZ039S&^}tG zpiq=eUS4`r%+y2C&&34(2y>?FBQxPGDe5)>4fex6W8FGR`B zf;~ts5{cM}bp2G0h`HG)6?o51isM$&_YzQEC~XxmmR4|COgW9pRE{d9$D$iW3aGQF zS(ZV_t~kRtgev~WoM(M%C}g?Pb{HoPL>5pbB_#VNVo86fqarpJ$l%u&F1_3CR+Z1R%+M(+iS-oSU%p^-8HtSYKqP zxA^(4k=OfQfXTYAjHe66A*&j2Qc#w`AFM!Ff32+?fj*}1 z-e?p*`$9}3GDt*5%>VX<8BHOD(FW;GF??!bmArravTJ+$tz<>Sf7 z6>1Y6W^ z8*3P+r46LMPdPev+0XHcss1BC~^&F4p=HO`>=nj zpkhfly@l+ZV`PUM{@lIytZRb*0xUips80UQCf-EmuGyu6jA4kiS_v%V8fjR^$Ts|N z_<($7UoFdhq~m*^QX`@hD!q&u>l2)kcnBwn}-Rq zCI)hC6fQ9wV>)**vck9TX280Gc07MdxLI!v;MxW-NHFUJ1e2Y#j#|&>S2eH;1sO-* zc}bRjDHLxLjKl9+oA|m2#v!oCcp!eiTaS-l|KNMmR6KifTkBuInMYFf396FMxj!c(M$} zyNkQU?B9{weHd9k_NMpU$<({U4?}x#2NwN+2(yb|d~IPGkB%^vqwSV?O_*cx1kOP4 znS^~Gw#?J}kPfar57)K*k7R&jFCY*vU=Vr8q;`m z5=0RCj&Ku-k_j4yzd{4%57&t^DZ+*|l8+ir(RcBj84^r@KV~$3Nm&QwJ;puRm6nOe z1Pd+tS3v*5*c|M&WLct`Ab10@4rI?GzcnP~His%iQopkO#Wr^zt;U;`aDqSvOX=1CV zJWp}AQ*2QU^%K^YaDjguMHF@9v>z$g38%>@VEyMnzNS@+SI^|AGp$WY4;w39(y4U? z>Ya2XH`DWE`*A8^Ei@#FA<7IfvRXFFOL=oh9u39$Ff-M*z_Pc-V_S)-s>Z7l16q=b zl^A)-WR$%)PDTiDY;D65sbL1!=I{Dd1Wxv#60$pV`*K{J_fg#;sCb-C(Jr>a~v%?mh# zSh>hx3W7{{TPUk&XDV*WE-juS7y7K_A1;w@LOI8&iPfpurq`>lFs?(T%p$|aRF^}2 zd+}4p@|6&DUhOJxD58X=&?!`cZXj8hH)6F2fdlPY3w3|f!tq>@V#0 zh3OgO(3P(goO*HYB8-4@xSK+@N@n7UcG(`zr#ZJSVo_SJD(I_AOZ)4nd3hG+ez2@&VN!>5Sg#9 z07k0BTn2wZ<&PI~*ry7DLv6A0*D-GpZ5}fZ$;jIbC8_j|n3~v4sVN#OIuR}6H?Lb~ z?p77$PKuZ#=;H9vT3)G@oU4JTpBUN%LnOhUettE@d?Q!ar zh(v#hL(8)ndmN-er`I{?w_E0s<5_nn&;d#n5;=e(^v(|^c<*KmyoC5)2;uFQ)JFUQ z9H;y){uShx>MZD(dTYF)eu^VBK6fjNd8EKs5GW5%ibS*XZ(2RL#<`!0I19h~B`&G> zrS_j>>GGhYs^b?p?9tO9MbVf8P%I=VzpFQ-!YrU-O8jPiGo@&+V17*yluQ6*cRPM8 hfWj%M_`=GNb`E>y Date: Wed, 14 Feb 2024 04:36:50 +1100 Subject: [PATCH 086/394] Clean up warning message (#11619) --- chain/consensus/filcns/filecoin.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chain/consensus/filcns/filecoin.go b/chain/consensus/filcns/filecoin.go index bb70d5d11..486a04b5c 100644 --- a/chain/consensus/filcns/filecoin.go +++ b/chain/consensus/filcns/filecoin.go @@ -149,7 +149,7 @@ func (filec *FilecoinEC) ValidateBlock(ctx context.Context, b *types.FullBlock) return xerrors.Errorf("block was from the future (now=%d, blk=%d): %w", now, h.Timestamp, consensus.ErrTemporal) } if h.Timestamp > now { - log.Warn("Got block from the future, but within threshold", h.Timestamp, build.Clock.Now().Unix()) + log.Warnf("Got block from the future, but within threshold (%d > %d)", h.Timestamp, now) } minerCheck := async.Err(func() error { From 5bd7f2ca4ddbd3edcf2b509628eb496fece874eb Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Thu, 15 Feb 2024 08:52:26 +1100 Subject: [PATCH 087/394] Don't gzip openrpc files for friendlier git wrangling the .gz files are impossible to rebase/merge/etc. but the files are needed in the repo for `go build` because they are embeds; so we just don't gzip them --- .circleci/config.yml | 8 +- .circleci/template.yml | 8 +- Makefile | 8 +- build/openrpc.go | 29 +- build/openrpc/full.json | 26558 ++++++++++++++++++++++++++++++++ build/openrpc/full.json.gz | Bin 34712 -> 0 bytes build/openrpc/gateway.json | 9909 ++++++++++++ build/openrpc/gateway.json.gz | Bin 11931 -> 0 bytes build/openrpc/miner.json | 12498 +++++++++++++++ build/openrpc/miner.json.gz | Bin 16371 -> 0 bytes build/openrpc/worker.json | 5536 +++++++ build/openrpc/worker.json.gz | Bin 6050 -> 0 bytes 12 files changed, 54517 insertions(+), 37 deletions(-) create mode 100644 build/openrpc/full.json delete mode 100644 build/openrpc/full.json.gz create mode 100644 build/openrpc/gateway.json delete mode 100644 build/openrpc/gateway.json.gz create mode 100644 build/openrpc/miner.json delete mode 100644 build/openrpc/miner.json.gz create mode 100644 build/openrpc/worker.json delete mode 100644 build/openrpc/worker.json.gz diff --git a/.circleci/config.yml b/.circleci/config.yml index 90db3a626..81cc6adcf 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -392,14 +392,8 @@ jobs: - attach_workspace: at: ~/ - run: go install golang.org/x/tools/cmd/goimports - - run: zcat build/openrpc/full.json.gz | jq > ../pre-openrpc-full - - run: zcat build/openrpc/miner.json.gz | jq > ../pre-openrpc-miner - - run: zcat build/openrpc/worker.json.gz | jq > ../pre-openrpc-worker - run: make docsgen - - run: zcat build/openrpc/full.json.gz | jq > ../post-openrpc-full - - run: zcat build/openrpc/miner.json.gz | jq > ../post-openrpc-miner - - run: zcat build/openrpc/worker.json.gz | jq > ../post-openrpc-worker - - run: diff ../pre-openrpc-full ../post-openrpc-full && diff ../pre-openrpc-miner ../post-openrpc-miner && diff ../pre-openrpc-worker ../post-openrpc-worker && git --no-pager diff && git --no-pager diff --quiet + - run: git --no-pager diff && git --no-pager diff --quiet lint-all: description: | diff --git a/.circleci/template.yml b/.circleci/template.yml index 9011f1a86..eaaa83e87 100644 --- a/.circleci/template.yml +++ b/.circleci/template.yml @@ -392,14 +392,8 @@ jobs: - attach_workspace: at: ~/ - run: go install golang.org/x/tools/cmd/goimports - - run: zcat build/openrpc/full.json.gz | jq > ../pre-openrpc-full - - run: zcat build/openrpc/miner.json.gz | jq > ../pre-openrpc-miner - - run: zcat build/openrpc/worker.json.gz | jq > ../pre-openrpc-worker - run: make docsgen - - run: zcat build/openrpc/full.json.gz | jq > ../post-openrpc-full - - run: zcat build/openrpc/miner.json.gz | jq > ../post-openrpc-miner - - run: zcat build/openrpc/worker.json.gz | jq > ../post-openrpc-worker - - run: diff ../pre-openrpc-full ../post-openrpc-full && diff ../pre-openrpc-miner ../post-openrpc-miner && diff ../pre-openrpc-worker ../post-openrpc-worker && git --no-pager diff && git --no-pager diff --quiet + - run: git --no-pager diff && git --no-pager diff --quiet lint-all: description: | diff --git a/Makefile b/Makefile index a17b50d09..841c715e1 100644 --- a/Makefile +++ b/Makefile @@ -373,13 +373,13 @@ docsgen-md-provider: docsgen-md-bin docsgen-openrpc: docsgen-openrpc-full docsgen-openrpc-storage docsgen-openrpc-worker docsgen-openrpc-gateway docsgen-openrpc-full: docsgen-openrpc-bin - ./docgen-openrpc "api/api_full.go" "FullNode" "api" "./api" -gzip > build/openrpc/full.json.gz + ./docgen-openrpc "api/api_full.go" "FullNode" "api" "./api" > build/openrpc/full.json docsgen-openrpc-storage: docsgen-openrpc-bin - ./docgen-openrpc "api/api_storage.go" "StorageMiner" "api" "./api" -gzip > build/openrpc/miner.json.gz + ./docgen-openrpc "api/api_storage.go" "StorageMiner" "api" "./api" > build/openrpc/miner.json docsgen-openrpc-worker: docsgen-openrpc-bin - ./docgen-openrpc "api/api_worker.go" "Worker" "api" "./api" -gzip > build/openrpc/worker.json.gz + ./docgen-openrpc "api/api_worker.go" "Worker" "api" "./api" > build/openrpc/worker.json docsgen-openrpc-gateway: docsgen-openrpc-bin - ./docgen-openrpc "api/api_gateway.go" "Gateway" "api" "./api" -gzip > build/openrpc/gateway.json.gz + ./docgen-openrpc "api/api_gateway.go" "Gateway" "api" "./api" > build/openrpc/gateway.json .PHONY: docsgen docsgen-md-bin docsgen-openrpc-bin diff --git a/build/openrpc.go b/build/openrpc.go index a50d6f51e..1c644bf96 100644 --- a/build/openrpc.go +++ b/build/openrpc.go @@ -2,7 +2,6 @@ package build import ( "bytes" - "compress/gzip" "embed" "encoding/json" @@ -12,17 +11,9 @@ import ( //go:embed openrpc var openrpcfs embed.FS -func mustReadGzippedOpenRPCDocument(data []byte) apitypes.OpenRPCDocument { - zr, err := gzip.NewReader(bytes.NewBuffer(data)) - if err != nil { - log.Fatal(err) - } +func mustReadOpenRPCDocument(data []byte) apitypes.OpenRPCDocument { m := apitypes.OpenRPCDocument{} - err = json.NewDecoder(zr).Decode(&m) - if err != nil { - log.Fatal(err) - } - err = zr.Close() + err := json.NewDecoder(bytes.NewBuffer(data)).Decode(&m) if err != nil { log.Fatal(err) } @@ -30,33 +21,33 @@ func mustReadGzippedOpenRPCDocument(data []byte) apitypes.OpenRPCDocument { } func OpenRPCDiscoverJSON_Full() apitypes.OpenRPCDocument { - data, err := openrpcfs.ReadFile("openrpc/full.json.gz") + data, err := openrpcfs.ReadFile("openrpc/full.json") if err != nil { panic(err) } - return mustReadGzippedOpenRPCDocument(data) + return mustReadOpenRPCDocument(data) } func OpenRPCDiscoverJSON_Miner() apitypes.OpenRPCDocument { - data, err := openrpcfs.ReadFile("openrpc/miner.json.gz") + data, err := openrpcfs.ReadFile("openrpc/miner.json") if err != nil { panic(err) } - return mustReadGzippedOpenRPCDocument(data) + return mustReadOpenRPCDocument(data) } func OpenRPCDiscoverJSON_Worker() apitypes.OpenRPCDocument { - data, err := openrpcfs.ReadFile("openrpc/worker.json.gz") + data, err := openrpcfs.ReadFile("openrpc/worker.json") if err != nil { panic(err) } - return mustReadGzippedOpenRPCDocument(data) + return mustReadOpenRPCDocument(data) } func OpenRPCDiscoverJSON_Gateway() apitypes.OpenRPCDocument { - data, err := openrpcfs.ReadFile("openrpc/gateway.json.gz") + data, err := openrpcfs.ReadFile("openrpc/gateway.json") if err != nil { panic(err) } - return mustReadGzippedOpenRPCDocument(data) + return mustReadOpenRPCDocument(data) } diff --git a/build/openrpc/full.json b/build/openrpc/full.json new file mode 100644 index 000000000..5f70ce52a --- /dev/null +++ b/build/openrpc/full.json @@ -0,0 +1,26558 @@ +{ + "openrpc": "1.2.6", + "info": { + "title": "Lotus RPC API", + "version": "1.25.3-dev" + }, + "methods": [ + { + "name": "Filecoin.ChainBlockstoreInfo", + "description": "```go\nfunc (s *FullNodeStruct) ChainBlockstoreInfo(p0 context.Context) (map[string]interface{}, error) {\n\tif s.Internal.ChainBlockstoreInfo == nil {\n\t\treturn *new(map[string]interface{}), ErrNotSupported\n\t}\n\treturn s.Internal.ChainBlockstoreInfo(p0)\n}\n```", + "summary": "ChainBlockstoreInfo returns some basic information about the blockstore\n", + "paramStructure": "by-position", + "params": [], + "result": { + "name": "map[string]interface{}", + "description": "map[string]interface{}", + "summary": "", + "schema": { + "examples": [ + { + "abc": 123 + } + ], + "patternProperties": { + ".*": { + "additionalProperties": true, + "type": "object" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1464" + } + }, + { + "name": "Filecoin.ChainCheckBlockstore", + "description": "```go\nfunc (s *FullNodeStruct) ChainCheckBlockstore(p0 context.Context) error {\n\tif s.Internal.ChainCheckBlockstore == nil {\n\t\treturn ErrNotSupported\n\t}\n\treturn s.Internal.ChainCheckBlockstore(p0)\n}\n```", + "summary": "ChainCheckBlockstore performs an (asynchronous) health check on the chain/state blockstore\nif supported by the underlying implementation.\n", + "paramStructure": "by-position", + "params": [], + "result": { + "name": "Null", + "description": "Null", + "schema": { + "type": [ + "null" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1475" + } + }, + { + "name": "Filecoin.ChainDeleteObj", + "description": "```go\nfunc (s *FullNodeStruct) ChainDeleteObj(p0 context.Context, p1 cid.Cid) error {\n\tif s.Internal.ChainDeleteObj == nil {\n\t\treturn ErrNotSupported\n\t}\n\treturn s.Internal.ChainDeleteObj(p0, p1)\n}\n```", + "summary": "ChainDeleteObj deletes node referenced by the given CID\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "cid.Cid", + "summary": "", + "schema": { + "title": "Content Identifier", + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "examples": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "Null", + "description": "Null", + "schema": { + "type": [ + "null" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1486" + } + }, + { + "name": "Filecoin.ChainExportRangeInternal", + "description": "```go\nfunc (s *FullNodeStruct) ChainExportRangeInternal(p0 context.Context, p1 types.TipSetKey, p2 types.TipSetKey, p3 ChainExportConfig) error {\n\tif s.Internal.ChainExportRangeInternal == nil {\n\t\treturn ErrNotSupported\n\t}\n\treturn s.Internal.ChainExportRangeInternal(p0, p1, p2, p3)\n}\n```", + "summary": "ChainExportRangeInternal triggers the export of a chain\nCAR-snapshot directly to disk. It is similar to ChainExport,\nexcept, depending on options, the snapshot can include receipts,\nmessages and stateroots for the length between the specified head\nand tail, thus producing \"archival-grade\" snapshots that include\nall the on-chain data. The header chain is included back to\ngenesis and these snapshots can be used to initialize Filecoin\nnodes.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p3", + "description": "ChainExportConfig", + "summary": "", + "schema": { + "examples": [ + { + "WriteBufferSize": 123, + "NumWorkers": 123, + "IncludeMessages": true, + "IncludeReceipts": true, + "IncludeStateRoots": true + } + ], + "additionalProperties": false, + "properties": { + "IncludeMessages": { + "type": "boolean" + }, + "IncludeReceipts": { + "type": "boolean" + }, + "IncludeStateRoots": { + "type": "boolean" + }, + "NumWorkers": { + "title": "number", + "type": "number" + }, + "WriteBufferSize": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "Null", + "description": "Null", + "schema": { + "type": [ + "null" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1508" + } + }, + { + "name": "Filecoin.ChainGetBlock", + "description": "```go\nfunc (s *FullNodeStruct) ChainGetBlock(p0 context.Context, p1 cid.Cid) (*types.BlockHeader, error) {\n\tif s.Internal.ChainGetBlock == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.ChainGetBlock(p0, p1)\n}\n```", + "summary": "ChainGetBlock returns the block specified by the given CID.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "cid.Cid", + "summary": "", + "schema": { + "title": "Content Identifier", + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "examples": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*types.BlockHeader", + "description": "*types.BlockHeader", + "summary": "", + "schema": { + "examples": [ + { + "Miner": "f01234", + "Ticket": { + "VRFProof": "Ynl0ZSBhcnJheQ==" + }, + "ElectionProof": { + "WinCount": 9, + "VRFProof": "Ynl0ZSBhcnJheQ==" + }, + "BeaconEntries": [ + { + "Round": 42, + "Data": "Ynl0ZSBhcnJheQ==" + } + ], + "WinPoStProof": [ + { + "PoStProof": 8, + "ProofBytes": "Ynl0ZSBhcnJheQ==" + } + ], + "Parents": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ], + "ParentWeight": "0", + "Height": 10101, + "ParentStateRoot": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "ParentMessageReceipts": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Messages": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "BLSAggregate": { + "Type": 2, + "Data": "Ynl0ZSBhcnJheQ==" + }, + "Timestamp": 42, + "BlockSig": { + "Type": 2, + "Data": "Ynl0ZSBhcnJheQ==" + }, + "ForkSignaling": 42, + "ParentBaseFee": "0" + } + ], + "additionalProperties": false, + "properties": { + "BLSAggregate": { + "additionalProperties": false, + "properties": { + "Data": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "Type": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "BeaconEntries": { + "items": { + "additionalProperties": false, + "properties": { + "Data": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "Round": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "type": "array" + }, + "BlockSig": { + "additionalProperties": false, + "properties": { + "Data": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "Type": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "ElectionProof": { + "additionalProperties": false, + "properties": { + "VRFProof": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "WinCount": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "ForkSignaling": { + "title": "number", + "type": "number" + }, + "Height": { + "title": "number", + "type": "number" + }, + "Messages": { + "title": "Content Identifier", + "type": "string" + }, + "Miner": { + "additionalProperties": false, + "type": "object" + }, + "ParentBaseFee": { + "additionalProperties": false, + "type": "object" + }, + "ParentMessageReceipts": { + "title": "Content Identifier", + "type": "string" + }, + "ParentStateRoot": { + "title": "Content Identifier", + "type": "string" + }, + "ParentWeight": { + "additionalProperties": false, + "type": "object" + }, + "Parents": { + "items": { + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "title": "Content Identifier", + "type": "string" + }, + "type": "array" + }, + "Ticket": { + "additionalProperties": false, + "properties": { + "VRFProof": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + } + }, + "type": "object" + }, + "Timestamp": { + "title": "number", + "type": "number" + }, + "WinPoStProof": { + "items": { + "additionalProperties": false, + "properties": { + "PoStProof": { + "title": "number", + "type": "number" + }, + "ProofBytes": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + } + }, + "type": "object" + }, + "type": "array" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1519" + } + }, + { + "name": "Filecoin.ChainGetBlockMessages", + "description": "```go\nfunc (s *FullNodeStruct) ChainGetBlockMessages(p0 context.Context, p1 cid.Cid) (*BlockMessages, error) {\n\tif s.Internal.ChainGetBlockMessages == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.ChainGetBlockMessages(p0, p1)\n}\n```", + "summary": "ChainGetBlockMessages returns messages stored in the specified block.\n\nNote: If there are multiple blocks in a tipset, it's likely that some\nmessages will be duplicated. It's also possible for blocks in a tipset to have\ndifferent messages from the same sender at the same nonce. When that happens,\nonly the first message (in a block with lowest ticket) will be considered\nfor execution\n\nNOTE: THIS METHOD SHOULD ONLY BE USED FOR GETTING MESSAGES IN A SPECIFIC BLOCK\n\nDO NOT USE THIS METHOD TO GET MESSAGES INCLUDED IN A TIPSET\nUse ChainGetParentMessages, which will perform correct message deduplication\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "cid.Cid", + "summary": "", + "schema": { + "title": "Content Identifier", + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "examples": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*BlockMessages", + "description": "*BlockMessages", + "summary": "", + "schema": { + "examples": [ + { + "BlsMessages": [ + { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + } + ], + "SecpkMessages": [ + { + "Message": { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + }, + "Signature": { + "Type": 2, + "Data": "Ynl0ZSBhcnJheQ==" + }, + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + } + ], + "Cids": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ] + } + ], + "additionalProperties": false, + "properties": { + "BlsMessages": { + "items": { + "additionalProperties": false, + "properties": { + "From": { + "additionalProperties": false, + "type": "object" + }, + "GasFeeCap": { + "additionalProperties": false, + "type": "object" + }, + "GasLimit": { + "title": "number", + "type": "number" + }, + "GasPremium": { + "additionalProperties": false, + "type": "object" + }, + "Method": { + "title": "number", + "type": "number" + }, + "Nonce": { + "title": "number", + "type": "number" + }, + "Params": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "To": { + "additionalProperties": false, + "type": "object" + }, + "Value": { + "additionalProperties": false, + "type": "object" + }, + "Version": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "type": "array" + }, + "Cids": { + "items": { + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "title": "Content Identifier", + "type": "string" + }, + "type": "array" + }, + "SecpkMessages": { + "items": { + "additionalProperties": false, + "properties": { + "Message": { + "additionalProperties": false, + "properties": { + "From": { + "additionalProperties": false, + "type": "object" + }, + "GasFeeCap": { + "additionalProperties": false, + "type": "object" + }, + "GasLimit": { + "title": "number", + "type": "number" + }, + "GasPremium": { + "additionalProperties": false, + "type": "object" + }, + "Method": { + "title": "number", + "type": "number" + }, + "Nonce": { + "title": "number", + "type": "number" + }, + "Params": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "To": { + "additionalProperties": false, + "type": "object" + }, + "Value": { + "additionalProperties": false, + "type": "object" + }, + "Version": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "Signature": { + "additionalProperties": false, + "properties": { + "Data": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "Type": { + "title": "number", + "type": "number" + } + }, + "type": "object" + } + }, + "type": "object" + }, + "type": "array" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1530" + } + }, + { + "name": "Filecoin.ChainGetEvents", + "description": "```go\nfunc (s *FullNodeStruct) ChainGetEvents(p0 context.Context, p1 cid.Cid) ([]types.Event, error) {\n\tif s.Internal.ChainGetEvents == nil {\n\t\treturn *new([]types.Event), ErrNotSupported\n\t}\n\treturn s.Internal.ChainGetEvents(p0, p1)\n}\n```", + "summary": "ChainGetEvents returns the events under an event AMT root CID.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "cid.Cid", + "summary": "", + "schema": { + "title": "Content Identifier", + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "examples": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "[]types.Event", + "description": "[]types.Event", + "summary": "", + "schema": { + "examples": [ + [ + { + "Emitter": 1000, + "Entries": [ + { + "Flags": 7, + "Key": "string value", + "Codec": 42, + "Value": "Ynl0ZSBhcnJheQ==" + } + ] + } + ] + ], + "items": [ + { + "additionalProperties": false, + "properties": { + "Emitter": { + "title": "number", + "type": "number" + }, + "Entries": { + "items": { + "additionalProperties": false, + "properties": { + "Codec": { + "title": "number", + "type": "number" + }, + "Flags": { + "title": "number", + "type": "number" + }, + "Key": { + "type": "string" + }, + "Value": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + } + }, + "type": "object" + }, + "type": "array" + } + }, + "type": [ + "object" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1541" + } + }, + { + "name": "Filecoin.ChainGetGenesis", + "description": "```go\nfunc (s *FullNodeStruct) ChainGetGenesis(p0 context.Context) (*types.TipSet, error) {\n\tif s.Internal.ChainGetGenesis == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.ChainGetGenesis(p0)\n}\n```", + "summary": "ChainGetGenesis returns the genesis tipset.\n", + "paramStructure": "by-position", + "params": [], + "result": { + "name": "*types.TipSet", + "description": "*types.TipSet", + "summary": "", + "schema": { + "examples": [ + { + "Cids": null, + "Blocks": null, + "Height": 0 + } + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1552" + } + }, + { + "name": "Filecoin.ChainGetMessage", + "description": "```go\nfunc (s *FullNodeStruct) ChainGetMessage(p0 context.Context, p1 cid.Cid) (*types.Message, error) {\n\tif s.Internal.ChainGetMessage == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.ChainGetMessage(p0, p1)\n}\n```", + "summary": "ChainGetMessage reads a message referenced by the specified CID from the\nchain blockstore.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "cid.Cid", + "summary": "", + "schema": { + "title": "Content Identifier", + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "examples": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*types.Message", + "description": "*types.Message", + "summary": "", + "schema": { + "examples": [ + { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + } + ], + "additionalProperties": false, + "properties": { + "From": { + "additionalProperties": false, + "type": "object" + }, + "GasFeeCap": { + "additionalProperties": false, + "type": "object" + }, + "GasLimit": { + "title": "number", + "type": "number" + }, + "GasPremium": { + "additionalProperties": false, + "type": "object" + }, + "Method": { + "title": "number", + "type": "number" + }, + "Nonce": { + "title": "number", + "type": "number" + }, + "Params": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "To": { + "additionalProperties": false, + "type": "object" + }, + "Value": { + "additionalProperties": false, + "type": "object" + }, + "Version": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1563" + } + }, + { + "name": "Filecoin.ChainGetMessagesInTipset", + "description": "```go\nfunc (s *FullNodeStruct) ChainGetMessagesInTipset(p0 context.Context, p1 types.TipSetKey) ([]Message, error) {\n\tif s.Internal.ChainGetMessagesInTipset == nil {\n\t\treturn *new([]Message), ErrNotSupported\n\t}\n\treturn s.Internal.ChainGetMessagesInTipset(p0, p1)\n}\n```", + "summary": "ChainGetMessagesInTipset returns message stores in current tipset\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "[]Message", + "description": "[]Message", + "summary": "", + "schema": { + "examples": [ + [ + { + "Cid": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Message": { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + } + } + ] + ], + "items": [ + { + "additionalProperties": false, + "properties": { + "Cid": { + "title": "Content Identifier", + "type": "string" + }, + "Message": { + "additionalProperties": false, + "properties": { + "Cid": { + "title": "Content Identifier", + "type": "string" + }, + "Message": {} + }, + "type": "object" + } + }, + "type": [ + "object" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1574" + } + }, + { + "name": "Filecoin.ChainGetNode", + "description": "```go\nfunc (s *FullNodeStruct) ChainGetNode(p0 context.Context, p1 string) (*IpldObject, error) {\n\tif s.Internal.ChainGetNode == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.ChainGetNode(p0, p1)\n}\n```", + "summary": "", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "string", + "summary": "", + "schema": { + "examples": [ + "string value" + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*IpldObject", + "description": "*IpldObject", + "summary": "", + "schema": { + "examples": [ + { + "Cid": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Obj": {} + } + ], + "additionalProperties": false, + "properties": { + "Cid": { + "title": "Content Identifier", + "type": "string" + }, + "Obj": { + "additionalProperties": true, + "type": "object" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1585" + } + }, + { + "name": "Filecoin.ChainGetParentMessages", + "description": "```go\nfunc (s *FullNodeStruct) ChainGetParentMessages(p0 context.Context, p1 cid.Cid) ([]Message, error) {\n\tif s.Internal.ChainGetParentMessages == nil {\n\t\treturn *new([]Message), ErrNotSupported\n\t}\n\treturn s.Internal.ChainGetParentMessages(p0, p1)\n}\n```", + "summary": "ChainGetParentMessages returns messages stored in parent tipset of the\nspecified block.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "cid.Cid", + "summary": "", + "schema": { + "title": "Content Identifier", + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "examples": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "[]Message", + "description": "[]Message", + "summary": "", + "schema": { + "examples": [ + [ + { + "Cid": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Message": { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + } + } + ] + ], + "items": [ + { + "additionalProperties": false, + "properties": { + "Cid": { + "title": "Content Identifier", + "type": "string" + }, + "Message": { + "additionalProperties": false, + "properties": { + "Cid": { + "title": "Content Identifier", + "type": "string" + }, + "Message": {} + }, + "type": "object" + } + }, + "type": [ + "object" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1596" + } + }, + { + "name": "Filecoin.ChainGetParentReceipts", + "description": "```go\nfunc (s *FullNodeStruct) ChainGetParentReceipts(p0 context.Context, p1 cid.Cid) ([]*types.MessageReceipt, error) {\n\tif s.Internal.ChainGetParentReceipts == nil {\n\t\treturn *new([]*types.MessageReceipt), ErrNotSupported\n\t}\n\treturn s.Internal.ChainGetParentReceipts(p0, p1)\n}\n```", + "summary": "ChainGetParentReceipts returns receipts for messages in parent tipset of\nthe specified block. The receipts in the list returned is one-to-one with the\nmessages returned by a call to ChainGetParentMessages with the same blockCid.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "cid.Cid", + "summary": "", + "schema": { + "title": "Content Identifier", + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "examples": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "[]*types.MessageReceipt", + "description": "[]*types.MessageReceipt", + "summary": "", + "schema": { + "examples": [ + [ + { + "ExitCode": 0, + "Return": "Ynl0ZSBhcnJheQ==", + "GasUsed": 9, + "EventsRoot": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + } + ] + ], + "items": [ + { + "additionalProperties": false, + "properties": { + "EventsRoot": { + "title": "Content Identifier", + "type": "string" + }, + "ExitCode": { + "title": "number", + "type": "number" + }, + "GasUsed": { + "title": "number", + "type": "number" + }, + "Return": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + } + }, + "type": [ + "object" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1607" + } + }, + { + "name": "Filecoin.ChainGetPath", + "description": "```go\nfunc (s *FullNodeStruct) ChainGetPath(p0 context.Context, p1 types.TipSetKey, p2 types.TipSetKey) ([]*HeadChange, error) {\n\tif s.Internal.ChainGetPath == nil {\n\t\treturn *new([]*HeadChange), ErrNotSupported\n\t}\n\treturn s.Internal.ChainGetPath(p0, p1, p2)\n}\n```", + "summary": "ChainGetPath returns a set of revert/apply operations needed to get from\none tipset to another, for example:\n```\n to\n ^\nfrom tAA\n ^ ^\ntBA tAB\n ^---*--^\n ^\n tRR\n```\nWould return `[revert(tBA), apply(tAB), apply(tAA)]`\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "[]*HeadChange", + "description": "[]*HeadChange", + "summary": "", + "schema": { + "examples": [ + [ + { + "Type": "string value", + "Val": { + "Cids": null, + "Blocks": null, + "Height": 0 + } + } + ] + ], + "items": [ + { + "additionalProperties": false, + "properties": { + "Type": { + "type": "string" + }, + "Val": { + "additionalProperties": false, + "type": "object" + } + }, + "type": [ + "object" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1618" + } + }, + { + "name": "Filecoin.ChainGetTipSet", + "description": "```go\nfunc (s *FullNodeStruct) ChainGetTipSet(p0 context.Context, p1 types.TipSetKey) (*types.TipSet, error) {\n\tif s.Internal.ChainGetTipSet == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.ChainGetTipSet(p0, p1)\n}\n```", + "summary": "ChainGetTipSet returns the tipset specified by the given TipSetKey.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*types.TipSet", + "description": "*types.TipSet", + "summary": "", + "schema": { + "examples": [ + { + "Cids": null, + "Blocks": null, + "Height": 0 + } + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1629" + } + }, + { + "name": "Filecoin.ChainGetTipSetAfterHeight", + "description": "```go\nfunc (s *FullNodeStruct) ChainGetTipSetAfterHeight(p0 context.Context, p1 abi.ChainEpoch, p2 types.TipSetKey) (*types.TipSet, error) {\n\tif s.Internal.ChainGetTipSetAfterHeight == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.ChainGetTipSetAfterHeight(p0, p1, p2)\n}\n```", + "summary": "ChainGetTipSetAfterHeight looks back for a tipset at the specified epoch.\nIf there are no blocks at the specified epoch, the first non-nil tipset at a later epoch\nwill be returned.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "abi.ChainEpoch", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 10101 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*types.TipSet", + "description": "*types.TipSet", + "summary": "", + "schema": { + "examples": [ + { + "Cids": null, + "Blocks": null, + "Height": 0 + } + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1640" + } + }, + { + "name": "Filecoin.ChainGetTipSetByHeight", + "description": "```go\nfunc (s *FullNodeStruct) ChainGetTipSetByHeight(p0 context.Context, p1 abi.ChainEpoch, p2 types.TipSetKey) (*types.TipSet, error) {\n\tif s.Internal.ChainGetTipSetByHeight == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.ChainGetTipSetByHeight(p0, p1, p2)\n}\n```", + "summary": "ChainGetTipSetByHeight looks back for a tipset at the specified epoch.\nIf there are no blocks at the specified epoch, a tipset at an earlier epoch\nwill be returned.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "abi.ChainEpoch", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 10101 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*types.TipSet", + "description": "*types.TipSet", + "summary": "", + "schema": { + "examples": [ + { + "Cids": null, + "Blocks": null, + "Height": 0 + } + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1651" + } + }, + { + "name": "Filecoin.ChainHasObj", + "description": "```go\nfunc (s *FullNodeStruct) ChainHasObj(p0 context.Context, p1 cid.Cid) (bool, error) {\n\tif s.Internal.ChainHasObj == nil {\n\t\treturn false, ErrNotSupported\n\t}\n\treturn s.Internal.ChainHasObj(p0, p1)\n}\n```", + "summary": "ChainHasObj checks if a given CID exists in the chain blockstore.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "cid.Cid", + "summary": "", + "schema": { + "title": "Content Identifier", + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "examples": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "bool", + "description": "bool", + "summary": "", + "schema": { + "examples": [ + true + ], + "type": [ + "boolean" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1662" + } + }, + { + "name": "Filecoin.ChainHead", + "description": "```go\nfunc (s *FullNodeStruct) ChainHead(p0 context.Context) (*types.TipSet, error) {\n\tif s.Internal.ChainHead == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.ChainHead(p0)\n}\n```", + "summary": "ChainHead returns the current head of the chain.\n", + "paramStructure": "by-position", + "params": [], + "result": { + "name": "*types.TipSet", + "description": "*types.TipSet", + "summary": "", + "schema": { + "examples": [ + { + "Cids": null, + "Blocks": null, + "Height": 0 + } + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1673" + } + }, + { + "name": "Filecoin.ChainHotGC", + "description": "```go\nfunc (s *FullNodeStruct) ChainHotGC(p0 context.Context, p1 HotGCOpts) error {\n\tif s.Internal.ChainHotGC == nil {\n\t\treturn ErrNotSupported\n\t}\n\treturn s.Internal.ChainHotGC(p0, p1)\n}\n```", + "summary": "ChainHotGC does online (badger) GC on the hot store; only supported if you are using\nthe splitstore\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "HotGCOpts", + "summary": "", + "schema": { + "examples": [ + { + "Threshold": 12.3, + "Periodic": true, + "Moving": true + } + ], + "additionalProperties": false, + "properties": { + "Moving": { + "type": "boolean" + }, + "Periodic": { + "type": "boolean" + }, + "Threshold": { + "type": "number" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "Null", + "description": "Null", + "schema": { + "type": [ + "null" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1684" + } + }, + { + "name": "Filecoin.ChainPrune", + "description": "```go\nfunc (s *FullNodeStruct) ChainPrune(p0 context.Context, p1 PruneOpts) error {\n\tif s.Internal.ChainPrune == nil {\n\t\treturn ErrNotSupported\n\t}\n\treturn s.Internal.ChainPrune(p0, p1)\n}\n```", + "summary": "ChainPrune forces compaction on cold store and garbage collects; only supported if you\nare using the splitstore\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "PruneOpts", + "summary": "", + "schema": { + "examples": [ + { + "MovingGC": true, + "RetainState": 9 + } + ], + "additionalProperties": false, + "properties": { + "MovingGC": { + "type": "boolean" + }, + "RetainState": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "Null", + "description": "Null", + "schema": { + "type": [ + "null" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1706" + } + }, + { + "name": "Filecoin.ChainPutObj", + "description": "```go\nfunc (s *FullNodeStruct) ChainPutObj(p0 context.Context, p1 blocks.Block) error {\n\tif s.Internal.ChainPutObj == nil {\n\t\treturn ErrNotSupported\n\t}\n\treturn s.Internal.ChainPutObj(p0, p1)\n}\n```", + "summary": "ChainPutObj puts a given object into the block store\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "blocks.Block", + "summary": "", + "schema": { + "examples": [ + {} + ], + "additionalProperties": true + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "Null", + "description": "Null", + "schema": { + "type": [ + "null" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1717" + } + }, + { + "name": "Filecoin.ChainReadObj", + "description": "```go\nfunc (s *FullNodeStruct) ChainReadObj(p0 context.Context, p1 cid.Cid) ([]byte, error) {\n\tif s.Internal.ChainReadObj == nil {\n\t\treturn *new([]byte), ErrNotSupported\n\t}\n\treturn s.Internal.ChainReadObj(p0, p1)\n}\n```", + "summary": "ChainReadObj reads ipld nodes referenced by the specified CID from chain\nblockstore and returns raw bytes.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "cid.Cid", + "summary": "", + "schema": { + "title": "Content Identifier", + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "examples": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "[]byte", + "description": "[]byte", + "summary": "", + "schema": { + "examples": [ + "Ynl0ZSBhcnJheQ==" + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1728" + } + }, + { + "name": "Filecoin.ChainSetHead", + "description": "```go\nfunc (s *FullNodeStruct) ChainSetHead(p0 context.Context, p1 types.TipSetKey) error {\n\tif s.Internal.ChainSetHead == nil {\n\t\treturn ErrNotSupported\n\t}\n\treturn s.Internal.ChainSetHead(p0, p1)\n}\n```", + "summary": "ChainSetHead forcefully sets current chain head. Use with caution.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "Null", + "description": "Null", + "schema": { + "type": [ + "null" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1739" + } + }, + { + "name": "Filecoin.ChainStatObj", + "description": "```go\nfunc (s *FullNodeStruct) ChainStatObj(p0 context.Context, p1 cid.Cid, p2 cid.Cid) (ObjStat, error) {\n\tif s.Internal.ChainStatObj == nil {\n\t\treturn *new(ObjStat), ErrNotSupported\n\t}\n\treturn s.Internal.ChainStatObj(p0, p1, p2)\n}\n```", + "summary": "ChainStatObj returns statistics about the graph referenced by 'obj'.\nIf 'base' is also specified, then the returned stat will be a diff\nbetween the two objects.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "cid.Cid", + "summary": "", + "schema": { + "title": "Content Identifier", + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "examples": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "cid.Cid", + "summary": "", + "schema": { + "title": "Content Identifier", + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "examples": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "ObjStat", + "description": "ObjStat", + "summary": "", + "schema": { + "examples": [ + { + "Size": 42, + "Links": 42 + } + ], + "additionalProperties": false, + "properties": { + "Links": { + "title": "number", + "type": "number" + }, + "Size": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1750" + } + }, + { + "name": "Filecoin.ChainTipSetWeight", + "description": "```go\nfunc (s *FullNodeStruct) ChainTipSetWeight(p0 context.Context, p1 types.TipSetKey) (types.BigInt, error) {\n\tif s.Internal.ChainTipSetWeight == nil {\n\t\treturn *new(types.BigInt), ErrNotSupported\n\t}\n\treturn s.Internal.ChainTipSetWeight(p0, p1)\n}\n```", + "summary": "ChainTipSetWeight computes weight for the specified tipset.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "types.BigInt", + "description": "types.BigInt", + "summary": "", + "schema": { + "examples": [ + "0" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1761" + } + }, + { + "name": "Filecoin.ClientCalcCommP", + "description": "```go\nfunc (s *FullNodeStruct) ClientCalcCommP(p0 context.Context, p1 string) (*CommPRet, error) {\n\tif s.Internal.ClientCalcCommP == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.ClientCalcCommP(p0, p1)\n}\n```", + "summary": "ClientCalcCommP calculates the CommP for a specified file\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "string", + "summary": "", + "schema": { + "examples": [ + "string value" + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*CommPRet", + "description": "*CommPRet", + "summary": "", + "schema": { + "examples": [ + { + "Root": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Size": 1024 + } + ], + "additionalProperties": false, + "properties": { + "Root": { + "title": "Content Identifier", + "type": "string" + }, + "Size": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1772" + } + }, + { + "name": "Filecoin.ClientCancelDataTransfer", + "description": "```go\nfunc (s *FullNodeStruct) ClientCancelDataTransfer(p0 context.Context, p1 datatransfer.TransferID, p2 peer.ID, p3 bool) error {\n\tif s.Internal.ClientCancelDataTransfer == nil {\n\t\treturn ErrNotSupported\n\t}\n\treturn s.Internal.ClientCancelDataTransfer(p0, p1, p2, p3)\n}\n```", + "summary": "ClientCancelDataTransfer cancels a data transfer with the given transfer ID and other peer\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "datatransfer.TransferID", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 3 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "peer.ID", + "summary": "", + "schema": { + "examples": [ + "12D3KooWGzxzKZYveHXtpG6AsrUJBcWxHBFS2HsEoGTxrMLvKXtf" + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p3", + "description": "bool", + "summary": "", + "schema": { + "examples": [ + true + ], + "type": [ + "boolean" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "Null", + "description": "Null", + "schema": { + "type": [ + "null" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1783" + } + }, + { + "name": "Filecoin.ClientCancelRetrievalDeal", + "description": "```go\nfunc (s *FullNodeStruct) ClientCancelRetrievalDeal(p0 context.Context, p1 retrievalmarket.DealID) error {\n\tif s.Internal.ClientCancelRetrievalDeal == nil {\n\t\treturn ErrNotSupported\n\t}\n\treturn s.Internal.ClientCancelRetrievalDeal(p0, p1)\n}\n```", + "summary": "ClientCancelRetrievalDeal cancels an ongoing retrieval deal based on DealID\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "retrievalmarket.DealID", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 5 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "Null", + "description": "Null", + "schema": { + "type": [ + "null" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1794" + } + }, + { + "name": "Filecoin.ClientDealPieceCID", + "description": "```go\nfunc (s *FullNodeStruct) ClientDealPieceCID(p0 context.Context, p1 cid.Cid) (DataCIDSize, error) {\n\tif s.Internal.ClientDealPieceCID == nil {\n\t\treturn *new(DataCIDSize), ErrNotSupported\n\t}\n\treturn s.Internal.ClientDealPieceCID(p0, p1)\n}\n```", + "summary": "ClientCalcCommP calculates the CommP and data size of the specified CID\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "cid.Cid", + "summary": "", + "schema": { + "title": "Content Identifier", + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "examples": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "DataCIDSize", + "description": "DataCIDSize", + "summary": "", + "schema": { + "examples": [ + { + "PayloadSize": 9, + "PieceSize": 1032, + "PieceCID": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + } + ], + "additionalProperties": false, + "properties": { + "PayloadSize": { + "title": "number", + "type": "number" + }, + "PieceCID": { + "title": "Content Identifier", + "type": "string" + }, + "PieceSize": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1816" + } + }, + { + "name": "Filecoin.ClientDealSize", + "description": "```go\nfunc (s *FullNodeStruct) ClientDealSize(p0 context.Context, p1 cid.Cid) (DataSize, error) {\n\tif s.Internal.ClientDealSize == nil {\n\t\treturn *new(DataSize), ErrNotSupported\n\t}\n\treturn s.Internal.ClientDealSize(p0, p1)\n}\n```", + "summary": "ClientDealSize calculates real deal data size\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "cid.Cid", + "summary": "", + "schema": { + "title": "Content Identifier", + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "examples": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "DataSize", + "description": "DataSize", + "summary": "", + "schema": { + "examples": [ + { + "PayloadSize": 9, + "PieceSize": 1032 + } + ], + "additionalProperties": false, + "properties": { + "PayloadSize": { + "title": "number", + "type": "number" + }, + "PieceSize": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1827" + } + }, + { + "name": "Filecoin.ClientExport", + "description": "```go\nfunc (s *FullNodeStruct) ClientExport(p0 context.Context, p1 ExportRef, p2 FileRef) error {\n\tif s.Internal.ClientExport == nil {\n\t\treturn ErrNotSupported\n\t}\n\treturn s.Internal.ClientExport(p0, p1, p2)\n}\n```", + "summary": "ClientExport exports a file stored in the local filestore to a system file\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "ExportRef", + "summary": "", + "schema": { + "examples": [ + { + "Root": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "DAGs": [ + { + "DataSelector": "Links/21/Hash/Links/42/Hash", + "ExportMerkleProof": true + } + ], + "FromLocalCAR": "string value", + "DealID": 5 + } + ], + "additionalProperties": false, + "properties": { + "DAGs": { + "items": { + "additionalProperties": false, + "properties": { + "DataSelector": { + "type": "string" + }, + "ExportMerkleProof": { + "type": "boolean" + } + }, + "type": "object" + }, + "type": "array" + }, + "DealID": { + "title": "number", + "type": "number" + }, + "FromLocalCAR": { + "type": "string" + }, + "Root": { + "title": "Content Identifier", + "type": "string" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "FileRef", + "summary": "", + "schema": { + "examples": [ + { + "Path": "string value", + "IsCAR": true + } + ], + "additionalProperties": false, + "properties": { + "IsCAR": { + "type": "boolean" + }, + "Path": { + "type": "string" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "Null", + "description": "Null", + "schema": { + "type": [ + "null" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1838" + } + }, + { + "name": "Filecoin.ClientFindData", + "description": "```go\nfunc (s *FullNodeStruct) ClientFindData(p0 context.Context, p1 cid.Cid, p2 *cid.Cid) ([]QueryOffer, error) {\n\tif s.Internal.ClientFindData == nil {\n\t\treturn *new([]QueryOffer), ErrNotSupported\n\t}\n\treturn s.Internal.ClientFindData(p0, p1, p2)\n}\n```", + "summary": "ClientFindData identifies peers that have a certain file, and returns QueryOffers (one per peer).\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "cid.Cid", + "summary": "", + "schema": { + "title": "Content Identifier", + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "examples": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "*cid.Cid", + "summary": "", + "schema": { + "title": "Content Identifier", + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "examples": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "[]QueryOffer", + "description": "[]QueryOffer", + "summary": "", + "schema": { + "examples": [ + [ + { + "Err": "string value", + "Root": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Piece": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Size": 42, + "MinPrice": "0", + "UnsealPrice": "0", + "PricePerByte": "0", + "PaymentInterval": 42, + "PaymentIntervalIncrease": 42, + "Miner": "f01234", + "MinerPeer": { + "Address": "f01234", + "ID": "12D3KooWGzxzKZYveHXtpG6AsrUJBcWxHBFS2HsEoGTxrMLvKXtf", + "PieceCID": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + } + } + ] + ], + "items": [ + { + "additionalProperties": false, + "properties": { + "Err": { + "type": "string" + }, + "MinPrice": { + "additionalProperties": false, + "type": "object" + }, + "Miner": { + "additionalProperties": false, + "type": "object" + }, + "MinerPeer": { + "additionalProperties": false, + "properties": { + "Address": { + "additionalProperties": false, + "type": "object" + }, + "ID": { + "type": "string" + }, + "PieceCID": { + "title": "Content Identifier", + "type": "string" + } + }, + "type": "object" + }, + "PaymentInterval": { + "title": "number", + "type": "number" + }, + "PaymentIntervalIncrease": { + "title": "number", + "type": "number" + }, + "Piece": { + "title": "Content Identifier", + "type": "string" + }, + "PricePerByte": { + "additionalProperties": false, + "type": "object" + }, + "Root": { + "title": "Content Identifier", + "type": "string" + }, + "Size": { + "title": "number", + "type": "number" + }, + "UnsealPrice": { + "additionalProperties": false, + "type": "object" + } + }, + "type": [ + "object" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1849" + } + }, + { + "name": "Filecoin.ClientGenCar", + "description": "```go\nfunc (s *FullNodeStruct) ClientGenCar(p0 context.Context, p1 FileRef, p2 string) error {\n\tif s.Internal.ClientGenCar == nil {\n\t\treturn ErrNotSupported\n\t}\n\treturn s.Internal.ClientGenCar(p0, p1, p2)\n}\n```", + "summary": "ClientGenCar generates a CAR file for the specified file.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "FileRef", + "summary": "", + "schema": { + "examples": [ + { + "Path": "string value", + "IsCAR": true + } + ], + "additionalProperties": false, + "properties": { + "IsCAR": { + "type": "boolean" + }, + "Path": { + "type": "string" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "string", + "summary": "", + "schema": { + "examples": [ + "string value" + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "Null", + "description": "Null", + "schema": { + "type": [ + "null" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1860" + } + }, + { + "name": "Filecoin.ClientGetDealInfo", + "description": "```go\nfunc (s *FullNodeStruct) ClientGetDealInfo(p0 context.Context, p1 cid.Cid) (*DealInfo, error) {\n\tif s.Internal.ClientGetDealInfo == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.ClientGetDealInfo(p0, p1)\n}\n```", + "summary": "ClientGetDealInfo returns the latest information about a given deal.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "cid.Cid", + "summary": "", + "schema": { + "title": "Content Identifier", + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "examples": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*DealInfo", + "description": "*DealInfo", + "summary": "", + "schema": { + "examples": [ + { + "ProposalCid": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "State": 42, + "Message": "string value", + "DealStages": { + "Stages": [ + { + "Name": "string value", + "Description": "string value", + "ExpectedDuration": "string value", + "CreatedTime": "0001-01-01T00:00:00Z", + "UpdatedTime": "0001-01-01T00:00:00Z", + "Logs": [ + { + "Log": "string value", + "UpdatedTime": "0001-01-01T00:00:00Z" + } + ] + } + ] + }, + "Provider": "f01234", + "DataRef": { + "TransferType": "string value", + "Root": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "PieceCid": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "PieceSize": 1024, + "RawBlockSize": 42 + }, + "PieceCID": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Size": 42, + "PricePerEpoch": "0", + "Duration": 42, + "DealID": 5432, + "CreationTime": "0001-01-01T00:00:00Z", + "Verified": true, + "TransferChannelID": { + "Initiator": "12D3KooWGzxzKZYveHXtpG6AsrUJBcWxHBFS2HsEoGTxrMLvKXtf", + "Responder": "12D3KooWGzxzKZYveHXtpG6AsrUJBcWxHBFS2HsEoGTxrMLvKXtf", + "ID": 3 + }, + "DataTransfer": { + "TransferID": 3, + "Status": 1, + "BaseCID": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "IsInitiator": true, + "IsSender": true, + "Voucher": "string value", + "Message": "string value", + "OtherPeer": "12D3KooWGzxzKZYveHXtpG6AsrUJBcWxHBFS2HsEoGTxrMLvKXtf", + "Transferred": 42, + "Stages": { + "Stages": [ + { + "Name": "string value", + "Description": "string value", + "CreatedTime": "0001-01-01T00:00:00Z", + "UpdatedTime": "0001-01-01T00:00:00Z", + "Logs": [ + { + "Log": "string value", + "UpdatedTime": "0001-01-01T00:00:00Z" + } + ] + } + ] + } + } + } + ], + "additionalProperties": false, + "properties": { + "CreationTime": { + "format": "date-time", + "type": "string" + }, + "DataRef": { + "additionalProperties": false, + "properties": { + "PieceCid": { + "title": "Content Identifier", + "type": "string" + }, + "PieceSize": { + "title": "number", + "type": "number" + }, + "RawBlockSize": { + "title": "number", + "type": "number" + }, + "Root": { + "title": "Content Identifier", + "type": "string" + }, + "TransferType": { + "type": "string" + } + }, + "type": "object" + }, + "DataTransfer": { + "additionalProperties": false, + "properties": { + "BaseCID": { + "title": "Content Identifier", + "type": "string" + }, + "IsInitiator": { + "type": "boolean" + }, + "IsSender": { + "type": "boolean" + }, + "Message": { + "type": "string" + }, + "OtherPeer": { + "type": "string" + }, + "Stages": { + "additionalProperties": false, + "properties": { + "Stages": { + "items": { + "additionalProperties": false, + "properties": { + "CreatedTime": { + "additionalProperties": false, + "type": "object" + }, + "Description": { + "type": "string" + }, + "Logs": { + "items": { + "additionalProperties": false, + "properties": { + "Log": { + "type": "string" + }, + "UpdatedTime": { + "additionalProperties": false, + "type": "object" + } + }, + "type": "object" + }, + "type": "array" + }, + "Name": { + "type": "string" + }, + "UpdatedTime": { + "additionalProperties": false, + "type": "object" + } + }, + "type": "object" + }, + "type": "array" + } + }, + "type": "object" + }, + "Status": { + "title": "number", + "type": "number" + }, + "TransferID": { + "title": "number", + "type": "number" + }, + "Transferred": { + "title": "number", + "type": "number" + }, + "Voucher": { + "type": "string" + } + }, + "type": "object" + }, + "DealID": { + "title": "number", + "type": "number" + }, + "DealStages": { + "additionalProperties": false, + "properties": { + "Stages": { + "items": { + "additionalProperties": false, + "properties": { + "CreatedTime": { + "additionalProperties": false, + "type": "object" + }, + "Description": { + "type": "string" + }, + "ExpectedDuration": { + "type": "string" + }, + "Logs": { + "items": { + "additionalProperties": false, + "properties": { + "Log": { + "type": "string" + }, + "UpdatedTime": { + "additionalProperties": false, + "type": "object" + } + }, + "type": "object" + }, + "type": "array" + }, + "Name": { + "type": "string" + }, + "UpdatedTime": { + "additionalProperties": false, + "type": "object" + } + }, + "type": "object" + }, + "type": "array" + } + }, + "type": "object" + }, + "Duration": { + "title": "number", + "type": "number" + }, + "Message": { + "type": "string" + }, + "PieceCID": { + "title": "Content Identifier", + "type": "string" + }, + "PricePerEpoch": { + "additionalProperties": false, + "type": "object" + }, + "ProposalCid": { + "title": "Content Identifier", + "type": "string" + }, + "Provider": { + "additionalProperties": false, + "type": "object" + }, + "Size": { + "title": "number", + "type": "number" + }, + "State": { + "title": "number", + "type": "number" + }, + "TransferChannelID": { + "additionalProperties": false, + "properties": { + "ID": { + "title": "number", + "type": "number" + }, + "Initiator": { + "type": "string" + }, + "Responder": { + "type": "string" + } + }, + "type": "object" + }, + "Verified": { + "type": "boolean" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1871" + } + }, + { + "name": "Filecoin.ClientGetDealStatus", + "description": "```go\nfunc (s *FullNodeStruct) ClientGetDealStatus(p0 context.Context, p1 uint64) (string, error) {\n\tif s.Internal.ClientGetDealStatus == nil {\n\t\treturn \"\", ErrNotSupported\n\t}\n\treturn s.Internal.ClientGetDealStatus(p0, p1)\n}\n```", + "summary": "ClientGetDealStatus returns status given a code\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "uint64", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 42 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "string", + "description": "string", + "summary": "", + "schema": { + "examples": [ + "string value" + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1882" + } + }, + { + "name": "Filecoin.ClientHasLocal", + "description": "```go\nfunc (s *FullNodeStruct) ClientHasLocal(p0 context.Context, p1 cid.Cid) (bool, error) {\n\tif s.Internal.ClientHasLocal == nil {\n\t\treturn false, ErrNotSupported\n\t}\n\treturn s.Internal.ClientHasLocal(p0, p1)\n}\n```", + "summary": "ClientHasLocal indicates whether a certain CID is locally stored.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "cid.Cid", + "summary": "", + "schema": { + "title": "Content Identifier", + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "examples": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "bool", + "description": "bool", + "summary": "", + "schema": { + "examples": [ + true + ], + "type": [ + "boolean" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1915" + } + }, + { + "name": "Filecoin.ClientImport", + "description": "```go\nfunc (s *FullNodeStruct) ClientImport(p0 context.Context, p1 FileRef) (*ImportRes, error) {\n\tif s.Internal.ClientImport == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.ClientImport(p0, p1)\n}\n```", + "summary": "ClientImport imports file under the specified path into filestore.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "FileRef", + "summary": "", + "schema": { + "examples": [ + { + "Path": "string value", + "IsCAR": true + } + ], + "additionalProperties": false, + "properties": { + "IsCAR": { + "type": "boolean" + }, + "Path": { + "type": "string" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*ImportRes", + "description": "*ImportRes", + "summary": "", + "schema": { + "examples": [ + { + "Root": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "ImportID": 50 + } + ], + "additionalProperties": false, + "properties": { + "ImportID": { + "title": "number", + "type": "number" + }, + "Root": { + "title": "Content Identifier", + "type": "string" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1926" + } + }, + { + "name": "Filecoin.ClientListDataTransfers", + "description": "```go\nfunc (s *FullNodeStruct) ClientListDataTransfers(p0 context.Context) ([]DataTransferChannel, error) {\n\tif s.Internal.ClientListDataTransfers == nil {\n\t\treturn *new([]DataTransferChannel), ErrNotSupported\n\t}\n\treturn s.Internal.ClientListDataTransfers(p0)\n}\n```", + "summary": "ClientListTransfers returns the status of all ongoing transfers of data\n", + "paramStructure": "by-position", + "params": [], + "result": { + "name": "[]DataTransferChannel", + "description": "[]DataTransferChannel", + "summary": "", + "schema": { + "examples": [ + [ + { + "TransferID": 3, + "Status": 1, + "BaseCID": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "IsInitiator": true, + "IsSender": true, + "Voucher": "string value", + "Message": "string value", + "OtherPeer": "12D3KooWGzxzKZYveHXtpG6AsrUJBcWxHBFS2HsEoGTxrMLvKXtf", + "Transferred": 42, + "Stages": { + "Stages": [ + { + "Name": "string value", + "Description": "string value", + "CreatedTime": "0001-01-01T00:00:00Z", + "UpdatedTime": "0001-01-01T00:00:00Z", + "Logs": [ + { + "Log": "string value", + "UpdatedTime": "0001-01-01T00:00:00Z" + } + ] + } + ] + } + } + ] + ], + "items": [ + { + "additionalProperties": false, + "properties": { + "BaseCID": { + "title": "Content Identifier", + "type": "string" + }, + "IsInitiator": { + "type": "boolean" + }, + "IsSender": { + "type": "boolean" + }, + "Message": { + "type": "string" + }, + "OtherPeer": { + "type": "string" + }, + "Stages": { + "additionalProperties": false, + "properties": { + "Stages": { + "items": { + "additionalProperties": false, + "properties": { + "CreatedTime": { + "additionalProperties": false, + "type": "object" + }, + "Description": { + "type": "string" + }, + "Logs": { + "items": { + "additionalProperties": false, + "properties": { + "Log": { + "type": "string" + }, + "UpdatedTime": { + "additionalProperties": false, + "type": "object" + } + }, + "type": "object" + }, + "type": "array" + }, + "Name": { + "type": "string" + }, + "UpdatedTime": { + "additionalProperties": false, + "type": "object" + } + }, + "type": "object" + }, + "type": "array" + } + }, + "type": "object" + }, + "Status": { + "title": "number", + "type": "number" + }, + "TransferID": { + "title": "number", + "type": "number" + }, + "Transferred": { + "title": "number", + "type": "number" + }, + "Voucher": { + "type": "string" + } + }, + "type": [ + "object" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1937" + } + }, + { + "name": "Filecoin.ClientListDeals", + "description": "```go\nfunc (s *FullNodeStruct) ClientListDeals(p0 context.Context) ([]DealInfo, error) {\n\tif s.Internal.ClientListDeals == nil {\n\t\treturn *new([]DealInfo), ErrNotSupported\n\t}\n\treturn s.Internal.ClientListDeals(p0)\n}\n```", + "summary": "ClientListDeals returns information about the deals made by the local client.\n", + "paramStructure": "by-position", + "params": [], + "result": { + "name": "[]DealInfo", + "description": "[]DealInfo", + "summary": "", + "schema": { + "examples": [ + [ + { + "ProposalCid": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "State": 42, + "Message": "string value", + "DealStages": { + "Stages": [ + { + "Name": "string value", + "Description": "string value", + "ExpectedDuration": "string value", + "CreatedTime": "0001-01-01T00:00:00Z", + "UpdatedTime": "0001-01-01T00:00:00Z", + "Logs": [ + { + "Log": "string value", + "UpdatedTime": "0001-01-01T00:00:00Z" + } + ] + } + ] + }, + "Provider": "f01234", + "DataRef": { + "TransferType": "string value", + "Root": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "PieceCid": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "PieceSize": 1024, + "RawBlockSize": 42 + }, + "PieceCID": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Size": 42, + "PricePerEpoch": "0", + "Duration": 42, + "DealID": 5432, + "CreationTime": "0001-01-01T00:00:00Z", + "Verified": true, + "TransferChannelID": { + "Initiator": "12D3KooWGzxzKZYveHXtpG6AsrUJBcWxHBFS2HsEoGTxrMLvKXtf", + "Responder": "12D3KooWGzxzKZYveHXtpG6AsrUJBcWxHBFS2HsEoGTxrMLvKXtf", + "ID": 3 + }, + "DataTransfer": { + "TransferID": 3, + "Status": 1, + "BaseCID": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "IsInitiator": true, + "IsSender": true, + "Voucher": "string value", + "Message": "string value", + "OtherPeer": "12D3KooWGzxzKZYveHXtpG6AsrUJBcWxHBFS2HsEoGTxrMLvKXtf", + "Transferred": 42, + "Stages": { + "Stages": [ + { + "Name": "string value", + "Description": "string value", + "CreatedTime": "0001-01-01T00:00:00Z", + "UpdatedTime": "0001-01-01T00:00:00Z", + "Logs": [ + { + "Log": "string value", + "UpdatedTime": "0001-01-01T00:00:00Z" + } + ] + } + ] + } + } + } + ] + ], + "items": [ + { + "additionalProperties": false, + "properties": { + "CreationTime": { + "format": "date-time", + "type": "string" + }, + "DataRef": { + "additionalProperties": false, + "properties": { + "PieceCid": { + "title": "Content Identifier", + "type": "string" + }, + "PieceSize": { + "title": "number", + "type": "number" + }, + "RawBlockSize": { + "title": "number", + "type": "number" + }, + "Root": { + "title": "Content Identifier", + "type": "string" + }, + "TransferType": { + "type": "string" + } + }, + "type": "object" + }, + "DataTransfer": { + "additionalProperties": false, + "properties": { + "BaseCID": { + "title": "Content Identifier", + "type": "string" + }, + "IsInitiator": { + "type": "boolean" + }, + "IsSender": { + "type": "boolean" + }, + "Message": { + "type": "string" + }, + "OtherPeer": { + "type": "string" + }, + "Stages": { + "additionalProperties": false, + "properties": { + "Stages": { + "items": { + "additionalProperties": false, + "properties": { + "CreatedTime": { + "additionalProperties": false, + "type": "object" + }, + "Description": { + "type": "string" + }, + "Logs": { + "items": { + "additionalProperties": false, + "properties": { + "Log": { + "type": "string" + }, + "UpdatedTime": { + "additionalProperties": false, + "type": "object" + } + }, + "type": "object" + }, + "type": "array" + }, + "Name": { + "type": "string" + }, + "UpdatedTime": { + "additionalProperties": false, + "type": "object" + } + }, + "type": "object" + }, + "type": "array" + } + }, + "type": "object" + }, + "Status": { + "title": "number", + "type": "number" + }, + "TransferID": { + "title": "number", + "type": "number" + }, + "Transferred": { + "title": "number", + "type": "number" + }, + "Voucher": { + "type": "string" + } + }, + "type": "object" + }, + "DealID": { + "title": "number", + "type": "number" + }, + "DealStages": { + "additionalProperties": false, + "properties": { + "Stages": { + "items": { + "additionalProperties": false, + "properties": { + "CreatedTime": { + "additionalProperties": false, + "type": "object" + }, + "Description": { + "type": "string" + }, + "ExpectedDuration": { + "type": "string" + }, + "Logs": { + "items": { + "additionalProperties": false, + "properties": { + "Log": { + "type": "string" + }, + "UpdatedTime": { + "additionalProperties": false, + "type": "object" + } + }, + "type": "object" + }, + "type": "array" + }, + "Name": { + "type": "string" + }, + "UpdatedTime": { + "additionalProperties": false, + "type": "object" + } + }, + "type": "object" + }, + "type": "array" + } + }, + "type": "object" + }, + "Duration": { + "title": "number", + "type": "number" + }, + "Message": { + "type": "string" + }, + "PieceCID": { + "title": "Content Identifier", + "type": "string" + }, + "PricePerEpoch": { + "additionalProperties": false, + "type": "object" + }, + "ProposalCid": { + "title": "Content Identifier", + "type": "string" + }, + "Provider": { + "additionalProperties": false, + "type": "object" + }, + "Size": { + "title": "number", + "type": "number" + }, + "State": { + "title": "number", + "type": "number" + }, + "TransferChannelID": { + "additionalProperties": false, + "properties": { + "ID": { + "title": "number", + "type": "number" + }, + "Initiator": { + "type": "string" + }, + "Responder": { + "type": "string" + } + }, + "type": "object" + }, + "Verified": { + "type": "boolean" + } + }, + "type": [ + "object" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1948" + } + }, + { + "name": "Filecoin.ClientListImports", + "description": "```go\nfunc (s *FullNodeStruct) ClientListImports(p0 context.Context) ([]Import, error) {\n\tif s.Internal.ClientListImports == nil {\n\t\treturn *new([]Import), ErrNotSupported\n\t}\n\treturn s.Internal.ClientListImports(p0)\n}\n```", + "summary": "ClientListImports lists imported files and their root CIDs\n", + "paramStructure": "by-position", + "params": [], + "result": { + "name": "[]Import", + "description": "[]Import", + "summary": "", + "schema": { + "examples": [ + [ + { + "Key": 50, + "Err": "string value", + "Root": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Source": "string value", + "FilePath": "string value", + "CARPath": "string value" + } + ] + ], + "items": [ + { + "additionalProperties": false, + "properties": { + "CARPath": { + "type": "string" + }, + "Err": { + "type": "string" + }, + "FilePath": { + "type": "string" + }, + "Key": { + "title": "number", + "type": "number" + }, + "Root": { + "title": "Content Identifier", + "type": "string" + }, + "Source": { + "type": "string" + } + }, + "type": [ + "object" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1959" + } + }, + { + "name": "Filecoin.ClientListRetrievals", + "description": "```go\nfunc (s *FullNodeStruct) ClientListRetrievals(p0 context.Context) ([]RetrievalInfo, error) {\n\tif s.Internal.ClientListRetrievals == nil {\n\t\treturn *new([]RetrievalInfo), ErrNotSupported\n\t}\n\treturn s.Internal.ClientListRetrievals(p0)\n}\n```", + "summary": "ClientListRetrievals returns information about retrievals made by the local client\n", + "paramStructure": "by-position", + "params": [], + "result": { + "name": "[]RetrievalInfo", + "description": "[]RetrievalInfo", + "summary": "", + "schema": { + "examples": [ + [ + { + "PayloadCID": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "ID": 5, + "PieceCID": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "PricePerByte": "0", + "UnsealPrice": "0", + "Status": 0, + "Message": "string value", + "Provider": "12D3KooWGzxzKZYveHXtpG6AsrUJBcWxHBFS2HsEoGTxrMLvKXtf", + "BytesReceived": 42, + "BytesPaidFor": 42, + "TotalPaid": "0", + "TransferChannelID": { + "Initiator": "12D3KooWGzxzKZYveHXtpG6AsrUJBcWxHBFS2HsEoGTxrMLvKXtf", + "Responder": "12D3KooWGzxzKZYveHXtpG6AsrUJBcWxHBFS2HsEoGTxrMLvKXtf", + "ID": 3 + }, + "DataTransfer": { + "TransferID": 3, + "Status": 1, + "BaseCID": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "IsInitiator": true, + "IsSender": true, + "Voucher": "string value", + "Message": "string value", + "OtherPeer": "12D3KooWGzxzKZYveHXtpG6AsrUJBcWxHBFS2HsEoGTxrMLvKXtf", + "Transferred": 42, + "Stages": { + "Stages": [ + { + "Name": "string value", + "Description": "string value", + "CreatedTime": "0001-01-01T00:00:00Z", + "UpdatedTime": "0001-01-01T00:00:00Z", + "Logs": [ + { + "Log": "string value", + "UpdatedTime": "0001-01-01T00:00:00Z" + } + ] + } + ] + } + }, + "Event": 5 + } + ] + ], + "items": [ + { + "additionalProperties": false, + "properties": { + "BytesPaidFor": { + "title": "number", + "type": "number" + }, + "BytesReceived": { + "title": "number", + "type": "number" + }, + "DataTransfer": { + "additionalProperties": false, + "properties": { + "BaseCID": { + "title": "Content Identifier", + "type": "string" + }, + "IsInitiator": { + "type": "boolean" + }, + "IsSender": { + "type": "boolean" + }, + "Message": { + "type": "string" + }, + "OtherPeer": { + "type": "string" + }, + "Stages": { + "additionalProperties": false, + "properties": { + "Stages": { + "items": { + "additionalProperties": false, + "properties": { + "CreatedTime": { + "additionalProperties": false, + "type": "object" + }, + "Description": { + "type": "string" + }, + "Logs": { + "items": { + "additionalProperties": false, + "properties": { + "Log": { + "type": "string" + }, + "UpdatedTime": { + "additionalProperties": false, + "type": "object" + } + }, + "type": "object" + }, + "type": "array" + }, + "Name": { + "type": "string" + }, + "UpdatedTime": { + "additionalProperties": false, + "type": "object" + } + }, + "type": "object" + }, + "type": "array" + } + }, + "type": "object" + }, + "Status": { + "title": "number", + "type": "number" + }, + "TransferID": { + "title": "number", + "type": "number" + }, + "Transferred": { + "title": "number", + "type": "number" + }, + "Voucher": { + "type": "string" + } + }, + "type": "object" + }, + "Event": { + "title": "number", + "type": "number" + }, + "ID": { + "title": "number", + "type": "number" + }, + "Message": { + "type": "string" + }, + "PayloadCID": { + "title": "Content Identifier", + "type": "string" + }, + "PieceCID": { + "title": "Content Identifier", + "type": "string" + }, + "PricePerByte": { + "additionalProperties": false, + "type": "object" + }, + "Provider": { + "type": "string" + }, + "Status": { + "title": "number", + "type": "number" + }, + "TotalPaid": { + "additionalProperties": false, + "type": "object" + }, + "TransferChannelID": { + "additionalProperties": false, + "properties": { + "ID": { + "title": "number", + "type": "number" + }, + "Initiator": { + "type": "string" + }, + "Responder": { + "type": "string" + } + }, + "type": "object" + }, + "UnsealPrice": { + "additionalProperties": false, + "type": "object" + } + }, + "type": [ + "object" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1970" + } + }, + { + "name": "Filecoin.ClientMinerQueryOffer", + "description": "```go\nfunc (s *FullNodeStruct) ClientMinerQueryOffer(p0 context.Context, p1 address.Address, p2 cid.Cid, p3 *cid.Cid) (QueryOffer, error) {\n\tif s.Internal.ClientMinerQueryOffer == nil {\n\t\treturn *new(QueryOffer), ErrNotSupported\n\t}\n\treturn s.Internal.ClientMinerQueryOffer(p0, p1, p2, p3)\n}\n```", + "summary": "ClientMinerQueryOffer returns a QueryOffer for the specific miner and file.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "cid.Cid", + "summary": "", + "schema": { + "title": "Content Identifier", + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "examples": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p3", + "description": "*cid.Cid", + "summary": "", + "schema": { + "title": "Content Identifier", + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "examples": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "QueryOffer", + "description": "QueryOffer", + "summary": "", + "schema": { + "examples": [ + { + "Err": "string value", + "Root": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Piece": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Size": 42, + "MinPrice": "0", + "UnsealPrice": "0", + "PricePerByte": "0", + "PaymentInterval": 42, + "PaymentIntervalIncrease": 42, + "Miner": "f01234", + "MinerPeer": { + "Address": "f01234", + "ID": "12D3KooWGzxzKZYveHXtpG6AsrUJBcWxHBFS2HsEoGTxrMLvKXtf", + "PieceCID": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + } + } + ], + "additionalProperties": false, + "properties": { + "Err": { + "type": "string" + }, + "MinPrice": { + "additionalProperties": false, + "type": "object" + }, + "Miner": { + "additionalProperties": false, + "type": "object" + }, + "MinerPeer": { + "additionalProperties": false, + "properties": { + "Address": { + "additionalProperties": false, + "type": "object" + }, + "ID": { + "type": "string" + }, + "PieceCID": { + "title": "Content Identifier", + "type": "string" + } + }, + "type": "object" + }, + "PaymentInterval": { + "title": "number", + "type": "number" + }, + "PaymentIntervalIncrease": { + "title": "number", + "type": "number" + }, + "Piece": { + "title": "Content Identifier", + "type": "string" + }, + "PricePerByte": { + "additionalProperties": false, + "type": "object" + }, + "Root": { + "title": "Content Identifier", + "type": "string" + }, + "Size": { + "title": "number", + "type": "number" + }, + "UnsealPrice": { + "additionalProperties": false, + "type": "object" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1981" + } + }, + { + "name": "Filecoin.ClientQueryAsk", + "description": "```go\nfunc (s *FullNodeStruct) ClientQueryAsk(p0 context.Context, p1 peer.ID, p2 address.Address) (*StorageAsk, error) {\n\tif s.Internal.ClientQueryAsk == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.ClientQueryAsk(p0, p1, p2)\n}\n```", + "summary": "ClientQueryAsk returns a signed StorageAsk from the specified miner.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "peer.ID", + "summary": "", + "schema": { + "examples": [ + "12D3KooWGzxzKZYveHXtpG6AsrUJBcWxHBFS2HsEoGTxrMLvKXtf" + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*StorageAsk", + "description": "*StorageAsk", + "summary": "", + "schema": { + "examples": [ + { + "Response": { + "Price": "0", + "VerifiedPrice": "0", + "MinPieceSize": 1032, + "MaxPieceSize": 1032, + "Miner": "f01234", + "Timestamp": 10101, + "Expiry": 10101, + "SeqNo": 42 + }, + "DealProtocols": [ + "string value" + ] + } + ], + "additionalProperties": false, + "properties": { + "DealProtocols": { + "items": { + "type": "string" + }, + "type": "array" + }, + "Response": {} + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1992" + } + }, + { + "name": "Filecoin.ClientRemoveImport", + "description": "```go\nfunc (s *FullNodeStruct) ClientRemoveImport(p0 context.Context, p1 imports.ID) error {\n\tif s.Internal.ClientRemoveImport == nil {\n\t\treturn ErrNotSupported\n\t}\n\treturn s.Internal.ClientRemoveImport(p0, p1)\n}\n```", + "summary": "ClientRemoveImport removes file import\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "imports.ID", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 50 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "Null", + "description": "Null", + "schema": { + "type": [ + "null" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2003" + } + }, + { + "name": "Filecoin.ClientRestartDataTransfer", + "description": "```go\nfunc (s *FullNodeStruct) ClientRestartDataTransfer(p0 context.Context, p1 datatransfer.TransferID, p2 peer.ID, p3 bool) error {\n\tif s.Internal.ClientRestartDataTransfer == nil {\n\t\treturn ErrNotSupported\n\t}\n\treturn s.Internal.ClientRestartDataTransfer(p0, p1, p2, p3)\n}\n```", + "summary": "ClientRestartDataTransfer attempts to restart a data transfer with the given transfer ID and other peer\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "datatransfer.TransferID", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 3 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "peer.ID", + "summary": "", + "schema": { + "examples": [ + "12D3KooWGzxzKZYveHXtpG6AsrUJBcWxHBFS2HsEoGTxrMLvKXtf" + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p3", + "description": "bool", + "summary": "", + "schema": { + "examples": [ + true + ], + "type": [ + "boolean" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "Null", + "description": "Null", + "schema": { + "type": [ + "null" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2014" + } + }, + { + "name": "Filecoin.ClientRetrieve", + "description": "```go\nfunc (s *FullNodeStruct) ClientRetrieve(p0 context.Context, p1 RetrievalOrder) (*RestrievalRes, error) {\n\tif s.Internal.ClientRetrieve == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.ClientRetrieve(p0, p1)\n}\n```", + "summary": "ClientRetrieve initiates the retrieval of a file, as specified in the order.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "RetrievalOrder", + "summary": "", + "schema": { + "examples": [ + { + "Root": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Piece": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "DataSelector": "Links/21/Hash/Links/42/Hash", + "Size": 42, + "Total": "0", + "UnsealPrice": "0", + "PaymentInterval": 42, + "PaymentIntervalIncrease": 42, + "Client": "f01234", + "Miner": "f01234", + "MinerPeer": { + "Address": "f01234", + "ID": "12D3KooWGzxzKZYveHXtpG6AsrUJBcWxHBFS2HsEoGTxrMLvKXtf", + "PieceCID": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + }, + "RemoteStore": "00000000-0000-0000-0000-000000000000" + } + ], + "additionalProperties": false, + "properties": { + "Client": { + "additionalProperties": false, + "type": "object" + }, + "DataSelector": { + "type": "string" + }, + "Miner": { + "additionalProperties": false, + "type": "object" + }, + "MinerPeer": { + "additionalProperties": false, + "properties": { + "Address": { + "additionalProperties": false, + "type": "object" + }, + "ID": { + "type": "string" + }, + "PieceCID": { + "title": "Content Identifier", + "type": "string" + } + }, + "type": "object" + }, + "PaymentInterval": { + "title": "number", + "type": "number" + }, + "PaymentIntervalIncrease": { + "title": "number", + "type": "number" + }, + "Piece": { + "title": "Content Identifier", + "type": "string" + }, + "RemoteStore": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 16, + "minItems": 16, + "type": "array" + }, + "Root": { + "title": "Content Identifier", + "type": "string" + }, + "Size": { + "title": "number", + "type": "number" + }, + "Total": { + "additionalProperties": false, + "type": "object" + }, + "UnsealPrice": { + "additionalProperties": false, + "type": "object" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*RestrievalRes", + "description": "*RestrievalRes", + "summary": "", + "schema": { + "examples": [ + { + "DealID": 5 + } + ], + "additionalProperties": false, + "properties": { + "DealID": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2025" + } + }, + { + "name": "Filecoin.ClientRetrieveTryRestartInsufficientFunds", + "description": "```go\nfunc (s *FullNodeStruct) ClientRetrieveTryRestartInsufficientFunds(p0 context.Context, p1 address.Address) error {\n\tif s.Internal.ClientRetrieveTryRestartInsufficientFunds == nil {\n\t\treturn ErrNotSupported\n\t}\n\treturn s.Internal.ClientRetrieveTryRestartInsufficientFunds(p0, p1)\n}\n```", + "summary": "ClientRetrieveTryRestartInsufficientFunds attempts to restart stalled retrievals on a given payment channel\nwhich are stuck due to insufficient funds\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "Null", + "description": "Null", + "schema": { + "type": [ + "null" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2036" + } + }, + { + "name": "Filecoin.ClientRetrieveWait", + "description": "```go\nfunc (s *FullNodeStruct) ClientRetrieveWait(p0 context.Context, p1 retrievalmarket.DealID) error {\n\tif s.Internal.ClientRetrieveWait == nil {\n\t\treturn ErrNotSupported\n\t}\n\treturn s.Internal.ClientRetrieveWait(p0, p1)\n}\n```", + "summary": "ClientRetrieveWait waits for retrieval to be complete\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "retrievalmarket.DealID", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 5 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "Null", + "description": "Null", + "schema": { + "type": [ + "null" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2047" + } + }, + { + "name": "Filecoin.ClientStartDeal", + "description": "```go\nfunc (s *FullNodeStruct) ClientStartDeal(p0 context.Context, p1 *StartDealParams) (*cid.Cid, error) {\n\tif s.Internal.ClientStartDeal == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.ClientStartDeal(p0, p1)\n}\n```", + "summary": "ClientStartDeal proposes a deal with a miner.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "*StartDealParams", + "summary": "", + "schema": { + "examples": [ + { + "Data": { + "TransferType": "string value", + "Root": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "PieceCid": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "PieceSize": 1024, + "RawBlockSize": 42 + }, + "Wallet": "f01234", + "Miner": "f01234", + "EpochPrice": "0", + "MinBlocksDuration": 42, + "ProviderCollateral": "0", + "DealStartEpoch": 10101, + "FastRetrieval": true, + "VerifiedDeal": true + } + ], + "additionalProperties": false, + "properties": { + "Data": { + "additionalProperties": false, + "properties": { + "PieceCid": { + "title": "Content Identifier", + "type": "string" + }, + "PieceSize": { + "title": "number", + "type": "number" + }, + "RawBlockSize": { + "title": "number", + "type": "number" + }, + "Root": { + "title": "Content Identifier", + "type": "string" + }, + "TransferType": { + "type": "string" + } + }, + "type": "object" + }, + "DealStartEpoch": { + "title": "number", + "type": "number" + }, + "EpochPrice": { + "additionalProperties": false, + "type": "object" + }, + "FastRetrieval": { + "type": "boolean" + }, + "MinBlocksDuration": { + "title": "number", + "type": "number" + }, + "Miner": { + "additionalProperties": false, + "type": "object" + }, + "ProviderCollateral": { + "additionalProperties": false, + "type": "object" + }, + "VerifiedDeal": { + "type": "boolean" + }, + "Wallet": { + "additionalProperties": false, + "type": "object" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*cid.Cid", + "description": "*cid.Cid", + "summary": "", + "schema": { + "title": "Content Identifier", + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "examples": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2058" + } + }, + { + "name": "Filecoin.ClientStatelessDeal", + "description": "```go\nfunc (s *FullNodeStruct) ClientStatelessDeal(p0 context.Context, p1 *StartDealParams) (*cid.Cid, error) {\n\tif s.Internal.ClientStatelessDeal == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.ClientStatelessDeal(p0, p1)\n}\n```", + "summary": "ClientStatelessDeal fire-and-forget-proposes an offline deal to a miner without subsequent tracking.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "*StartDealParams", + "summary": "", + "schema": { + "examples": [ + { + "Data": { + "TransferType": "string value", + "Root": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "PieceCid": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "PieceSize": 1024, + "RawBlockSize": 42 + }, + "Wallet": "f01234", + "Miner": "f01234", + "EpochPrice": "0", + "MinBlocksDuration": 42, + "ProviderCollateral": "0", + "DealStartEpoch": 10101, + "FastRetrieval": true, + "VerifiedDeal": true + } + ], + "additionalProperties": false, + "properties": { + "Data": { + "additionalProperties": false, + "properties": { + "PieceCid": { + "title": "Content Identifier", + "type": "string" + }, + "PieceSize": { + "title": "number", + "type": "number" + }, + "RawBlockSize": { + "title": "number", + "type": "number" + }, + "Root": { + "title": "Content Identifier", + "type": "string" + }, + "TransferType": { + "type": "string" + } + }, + "type": "object" + }, + "DealStartEpoch": { + "title": "number", + "type": "number" + }, + "EpochPrice": { + "additionalProperties": false, + "type": "object" + }, + "FastRetrieval": { + "type": "boolean" + }, + "MinBlocksDuration": { + "title": "number", + "type": "number" + }, + "Miner": { + "additionalProperties": false, + "type": "object" + }, + "ProviderCollateral": { + "additionalProperties": false, + "type": "object" + }, + "VerifiedDeal": { + "type": "boolean" + }, + "Wallet": { + "additionalProperties": false, + "type": "object" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*cid.Cid", + "description": "*cid.Cid", + "summary": "", + "schema": { + "title": "Content Identifier", + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "examples": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2069" + } + }, + { + "name": "Filecoin.CreateBackup", + "description": "```go\nfunc (s *FullNodeStruct) CreateBackup(p0 context.Context, p1 string) error {\n\tif s.Internal.CreateBackup == nil {\n\t\treturn ErrNotSupported\n\t}\n\treturn s.Internal.CreateBackup(p0, p1)\n}\n```", + "summary": "CreateBackup creates node backup onder the specified file name. The\nmethod requires that the lotus daemon is running with the\nLOTUS_BACKUP_BASE_PATH environment variable set to some path, and that\nthe path specified when calling CreateBackup is within the base path\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "string", + "summary": "", + "schema": { + "examples": [ + "string value" + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "Null", + "description": "Null", + "schema": { + "type": [ + "null" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2080" + } + }, + { + "name": "Filecoin.EthAccounts", + "description": "```go\nfunc (s *FullNodeStruct) EthAccounts(p0 context.Context) ([]ethtypes.EthAddress, error) {\n\tif s.Internal.EthAccounts == nil {\n\t\treturn *new([]ethtypes.EthAddress), ErrNotSupported\n\t}\n\treturn s.Internal.EthAccounts(p0)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [], + "result": { + "name": "[]ethtypes.EthAddress", + "description": "[]ethtypes.EthAddress", + "summary": "", + "schema": { + "examples": [ + [ + "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031" + ] + ], + "items": [ + { + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "maxItems": 20, + "minItems": 20, + "type": [ + "array" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2091" + } + }, + { + "name": "Filecoin.EthAddressToFilecoinAddress", + "description": "```go\nfunc (s *FullNodeStruct) EthAddressToFilecoinAddress(p0 context.Context, p1 ethtypes.EthAddress) (address.Address, error) {\n\tif s.Internal.EthAddressToFilecoinAddress == nil {\n\t\treturn *new(address.Address), ErrNotSupported\n\t}\n\treturn s.Internal.EthAddressToFilecoinAddress(p0, p1)\n}\n```", + "summary": "EthAddressToFilecoinAddress converts an EthAddress into an f410 Filecoin Address\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "ethtypes.EthAddress", + "summary": "", + "schema": { + "examples": [ + "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031" + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "maxItems": 20, + "minItems": 20, + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "address.Address", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2102" + } + }, + { + "name": "Filecoin.EthBlockNumber", + "description": "```go\nfunc (s *FullNodeStruct) EthBlockNumber(p0 context.Context) (ethtypes.EthUint64, error) {\n\tif s.Internal.EthBlockNumber == nil {\n\t\treturn *new(ethtypes.EthUint64), ErrNotSupported\n\t}\n\treturn s.Internal.EthBlockNumber(p0)\n}\n```", + "summary": "EthBlockNumber returns the height of the latest (heaviest) TipSet\n", + "paramStructure": "by-position", + "params": [], + "result": { + "name": "ethtypes.EthUint64", + "description": "ethtypes.EthUint64", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + "0x5" + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2113" + } + }, + { + "name": "Filecoin.EthCall", + "description": "```go\nfunc (s *FullNodeStruct) EthCall(p0 context.Context, p1 ethtypes.EthCall, p2 ethtypes.EthBlockNumberOrHash) (ethtypes.EthBytes, error) {\n\tif s.Internal.EthCall == nil {\n\t\treturn *new(ethtypes.EthBytes), ErrNotSupported\n\t}\n\treturn s.Internal.EthCall(p0, p1, p2)\n}\n```", + "summary": "", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "ethtypes.EthCall", + "summary": "", + "schema": { + "examples": [ + { + "from": "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031", + "to": "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031", + "gas": "0x5", + "gasPrice": "0x0", + "value": "0x0", + "data": "0x07" + } + ], + "additionalProperties": false, + "properties": { + "data": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "type": "array" + }, + "from": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 20, + "minItems": 20, + "type": "array" + }, + "gas": { + "title": "number", + "type": "number" + }, + "gasPrice": { + "additionalProperties": false, + "type": "object" + }, + "to": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 20, + "minItems": 20, + "type": "array" + }, + "value": { + "additionalProperties": false, + "type": "object" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "ethtypes.EthBlockNumberOrHash", + "summary": "", + "schema": { + "examples": [ + "string value" + ], + "additionalProperties": false, + "properties": { + "blockHash": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "blockNumber": { + "title": "number", + "type": "number" + }, + "requireCanonical": { + "type": "boolean" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "ethtypes.EthBytes", + "description": "ethtypes.EthBytes", + "summary": "", + "schema": { + "examples": [ + "0x07" + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2124" + } + }, + { + "name": "Filecoin.EthChainId", + "description": "```go\nfunc (s *FullNodeStruct) EthChainId(p0 context.Context) (ethtypes.EthUint64, error) {\n\tif s.Internal.EthChainId == nil {\n\t\treturn *new(ethtypes.EthUint64), ErrNotSupported\n\t}\n\treturn s.Internal.EthChainId(p0)\n}\n```", + "summary": "", + "paramStructure": "by-position", + "params": [], + "result": { + "name": "ethtypes.EthUint64", + "description": "ethtypes.EthUint64", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + "0x5" + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2135" + } + }, + { + "name": "Filecoin.EthEstimateGas", + "description": "```go\nfunc (s *FullNodeStruct) EthEstimateGas(p0 context.Context, p1 jsonrpc.RawParams) (ethtypes.EthUint64, error) {\n\tif s.Internal.EthEstimateGas == nil {\n\t\treturn *new(ethtypes.EthUint64), ErrNotSupported\n\t}\n\treturn s.Internal.EthEstimateGas(p0, p1)\n}\n```", + "summary": "", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "jsonrpc.RawParams", + "summary": "", + "schema": { + "examples": [ + "Bw==" + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "ethtypes.EthUint64", + "description": "ethtypes.EthUint64", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + "0x5" + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2146" + } + }, + { + "name": "Filecoin.EthFeeHistory", + "description": "```go\nfunc (s *FullNodeStruct) EthFeeHistory(p0 context.Context, p1 jsonrpc.RawParams) (ethtypes.EthFeeHistory, error) {\n\tif s.Internal.EthFeeHistory == nil {\n\t\treturn *new(ethtypes.EthFeeHistory), ErrNotSupported\n\t}\n\treturn s.Internal.EthFeeHistory(p0, p1)\n}\n```", + "summary": "", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "jsonrpc.RawParams", + "summary": "", + "schema": { + "examples": [ + "Bw==" + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "ethtypes.EthFeeHistory", + "description": "ethtypes.EthFeeHistory", + "summary": "", + "schema": { + "examples": [ + { + "oldestBlock": "0x5", + "baseFeePerGas": [ + "0x0" + ], + "gasUsedRatio": [ + 12.3 + ], + "reward": [] + } + ], + "additionalProperties": false, + "properties": { + "baseFeePerGas": { + "items": { + "additionalProperties": false, + "type": "object" + }, + "type": "array" + }, + "gasUsedRatio": { + "items": { + "type": "number" + }, + "type": "array" + }, + "oldestBlock": { + "title": "number", + "type": "number" + }, + "reward": { + "items": { + "items": { + "additionalProperties": false, + "type": "object" + }, + "type": "array" + }, + "type": "array" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2157" + } + }, + { + "name": "Filecoin.EthGasPrice", + "description": "```go\nfunc (s *FullNodeStruct) EthGasPrice(p0 context.Context) (ethtypes.EthBigInt, error) {\n\tif s.Internal.EthGasPrice == nil {\n\t\treturn *new(ethtypes.EthBigInt), ErrNotSupported\n\t}\n\treturn s.Internal.EthGasPrice(p0)\n}\n```", + "summary": "", + "paramStructure": "by-position", + "params": [], + "result": { + "name": "ethtypes.EthBigInt", + "description": "ethtypes.EthBigInt", + "summary": "", + "schema": { + "examples": [ + "0x0" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2168" + } + }, + { + "name": "Filecoin.EthGetBalance", + "description": "```go\nfunc (s *FullNodeStruct) EthGetBalance(p0 context.Context, p1 ethtypes.EthAddress, p2 ethtypes.EthBlockNumberOrHash) (ethtypes.EthBigInt, error) {\n\tif s.Internal.EthGetBalance == nil {\n\t\treturn *new(ethtypes.EthBigInt), ErrNotSupported\n\t}\n\treturn s.Internal.EthGetBalance(p0, p1, p2)\n}\n```", + "summary": "", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "ethtypes.EthAddress", + "summary": "", + "schema": { + "examples": [ + "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031" + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "maxItems": 20, + "minItems": 20, + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "ethtypes.EthBlockNumberOrHash", + "summary": "", + "schema": { + "examples": [ + "string value" + ], + "additionalProperties": false, + "properties": { + "blockHash": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "blockNumber": { + "title": "number", + "type": "number" + }, + "requireCanonical": { + "type": "boolean" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "ethtypes.EthBigInt", + "description": "ethtypes.EthBigInt", + "summary": "", + "schema": { + "examples": [ + "0x0" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2179" + } + }, + { + "name": "Filecoin.EthGetBlockByHash", + "description": "```go\nfunc (s *FullNodeStruct) EthGetBlockByHash(p0 context.Context, p1 ethtypes.EthHash, p2 bool) (ethtypes.EthBlock, error) {\n\tif s.Internal.EthGetBlockByHash == nil {\n\t\treturn *new(ethtypes.EthBlock), ErrNotSupported\n\t}\n\treturn s.Internal.EthGetBlockByHash(p0, p1, p2)\n}\n```", + "summary": "", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "ethtypes.EthHash", + "summary": "", + "schema": { + "examples": [ + "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e" + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "maxItems": 32, + "minItems": 32, + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "bool", + "summary": "", + "schema": { + "examples": [ + true + ], + "type": [ + "boolean" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "ethtypes.EthBlock", + "description": "ethtypes.EthBlock", + "summary": "", + "schema": { + "examples": [ + { + "hash": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", + "parentHash": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", + "sha3Uncles": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", + "miner": "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031", + "stateRoot": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", + "transactionsRoot": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", + "receiptsRoot": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", + "logsBloom": "0x07", + "difficulty": "0x5", + "totalDifficulty": "0x5", + "number": "0x5", + "gasLimit": "0x5", + "gasUsed": "0x5", + "timestamp": "0x5", + "extraData": "0x07", + "mixHash": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", + "nonce": "0x0707070707070707", + "baseFeePerGas": "0x0", + "size": "0x5", + "transactions": [ + {} + ], + "uncles": [ + "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e" + ] + } + ], + "additionalProperties": false, + "properties": { + "baseFeePerGas": { + "additionalProperties": false, + "type": "object" + }, + "difficulty": { + "title": "number", + "type": "number" + }, + "extraData": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "type": "array" + }, + "gasLimit": { + "title": "number", + "type": "number" + }, + "gasUsed": { + "title": "number", + "type": "number" + }, + "hash": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "logsBloom": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "type": "array" + }, + "miner": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 20, + "minItems": 20, + "type": "array" + }, + "mixHash": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "nonce": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 8, + "minItems": 8, + "type": "array" + }, + "number": { + "title": "number", + "type": "number" + }, + "parentHash": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "receiptsRoot": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "sha3Uncles": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "size": { + "title": "number", + "type": "number" + }, + "stateRoot": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "timestamp": { + "title": "number", + "type": "number" + }, + "totalDifficulty": { + "title": "number", + "type": "number" + }, + "transactions": { + "items": { + "additionalProperties": true, + "type": "object" + }, + "type": "array" + }, + "transactionsRoot": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "uncles": { + "items": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "type": "array" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2190" + } + }, + { + "name": "Filecoin.EthGetBlockByNumber", + "description": "```go\nfunc (s *FullNodeStruct) EthGetBlockByNumber(p0 context.Context, p1 string, p2 bool) (ethtypes.EthBlock, error) {\n\tif s.Internal.EthGetBlockByNumber == nil {\n\t\treturn *new(ethtypes.EthBlock), ErrNotSupported\n\t}\n\treturn s.Internal.EthGetBlockByNumber(p0, p1, p2)\n}\n```", + "summary": "", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "string", + "summary": "", + "schema": { + "examples": [ + "string value" + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "bool", + "summary": "", + "schema": { + "examples": [ + true + ], + "type": [ + "boolean" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "ethtypes.EthBlock", + "description": "ethtypes.EthBlock", + "summary": "", + "schema": { + "examples": [ + { + "hash": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", + "parentHash": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", + "sha3Uncles": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", + "miner": "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031", + "stateRoot": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", + "transactionsRoot": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", + "receiptsRoot": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", + "logsBloom": "0x07", + "difficulty": "0x5", + "totalDifficulty": "0x5", + "number": "0x5", + "gasLimit": "0x5", + "gasUsed": "0x5", + "timestamp": "0x5", + "extraData": "0x07", + "mixHash": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", + "nonce": "0x0707070707070707", + "baseFeePerGas": "0x0", + "size": "0x5", + "transactions": [ + {} + ], + "uncles": [ + "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e" + ] + } + ], + "additionalProperties": false, + "properties": { + "baseFeePerGas": { + "additionalProperties": false, + "type": "object" + }, + "difficulty": { + "title": "number", + "type": "number" + }, + "extraData": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "type": "array" + }, + "gasLimit": { + "title": "number", + "type": "number" + }, + "gasUsed": { + "title": "number", + "type": "number" + }, + "hash": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "logsBloom": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "type": "array" + }, + "miner": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 20, + "minItems": 20, + "type": "array" + }, + "mixHash": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "nonce": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 8, + "minItems": 8, + "type": "array" + }, + "number": { + "title": "number", + "type": "number" + }, + "parentHash": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "receiptsRoot": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "sha3Uncles": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "size": { + "title": "number", + "type": "number" + }, + "stateRoot": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "timestamp": { + "title": "number", + "type": "number" + }, + "totalDifficulty": { + "title": "number", + "type": "number" + }, + "transactions": { + "items": { + "additionalProperties": true, + "type": "object" + }, + "type": "array" + }, + "transactionsRoot": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "uncles": { + "items": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "type": "array" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2201" + } + }, + { + "name": "Filecoin.EthGetBlockTransactionCountByHash", + "description": "```go\nfunc (s *FullNodeStruct) EthGetBlockTransactionCountByHash(p0 context.Context, p1 ethtypes.EthHash) (ethtypes.EthUint64, error) {\n\tif s.Internal.EthGetBlockTransactionCountByHash == nil {\n\t\treturn *new(ethtypes.EthUint64), ErrNotSupported\n\t}\n\treturn s.Internal.EthGetBlockTransactionCountByHash(p0, p1)\n}\n```", + "summary": "EthGetBlockTransactionCountByHash returns the number of messages in the TipSet\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "ethtypes.EthHash", + "summary": "", + "schema": { + "examples": [ + "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e" + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "maxItems": 32, + "minItems": 32, + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "ethtypes.EthUint64", + "description": "ethtypes.EthUint64", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + "0x5" + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2212" + } + }, + { + "name": "Filecoin.EthGetBlockTransactionCountByNumber", + "description": "```go\nfunc (s *FullNodeStruct) EthGetBlockTransactionCountByNumber(p0 context.Context, p1 ethtypes.EthUint64) (ethtypes.EthUint64, error) {\n\tif s.Internal.EthGetBlockTransactionCountByNumber == nil {\n\t\treturn *new(ethtypes.EthUint64), ErrNotSupported\n\t}\n\treturn s.Internal.EthGetBlockTransactionCountByNumber(p0, p1)\n}\n```", + "summary": "EthGetBlockTransactionCountByNumber returns the number of messages in the TipSet\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "ethtypes.EthUint64", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + "0x5" + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "ethtypes.EthUint64", + "description": "ethtypes.EthUint64", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + "0x5" + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2223" + } + }, + { + "name": "Filecoin.EthGetCode", + "description": "```go\nfunc (s *FullNodeStruct) EthGetCode(p0 context.Context, p1 ethtypes.EthAddress, p2 ethtypes.EthBlockNumberOrHash) (ethtypes.EthBytes, error) {\n\tif s.Internal.EthGetCode == nil {\n\t\treturn *new(ethtypes.EthBytes), ErrNotSupported\n\t}\n\treturn s.Internal.EthGetCode(p0, p1, p2)\n}\n```", + "summary": "", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "ethtypes.EthAddress", + "summary": "", + "schema": { + "examples": [ + "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031" + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "maxItems": 20, + "minItems": 20, + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "ethtypes.EthBlockNumberOrHash", + "summary": "", + "schema": { + "examples": [ + "string value" + ], + "additionalProperties": false, + "properties": { + "blockHash": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "blockNumber": { + "title": "number", + "type": "number" + }, + "requireCanonical": { + "type": "boolean" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "ethtypes.EthBytes", + "description": "ethtypes.EthBytes", + "summary": "", + "schema": { + "examples": [ + "0x07" + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2234" + } + }, + { + "name": "Filecoin.EthGetFilterChanges", + "description": "```go\nfunc (s *FullNodeStruct) EthGetFilterChanges(p0 context.Context, p1 ethtypes.EthFilterID) (*ethtypes.EthFilterResult, error) {\n\tif s.Internal.EthGetFilterChanges == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.EthGetFilterChanges(p0, p1)\n}\n```", + "summary": "Polling method for a filter, returns event logs which occurred since last poll.\n(requires write perm since timestamp of last filter execution will be written)\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "ethtypes.EthFilterID", + "summary": "", + "schema": { + "examples": [ + "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e" + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "maxItems": 32, + "minItems": 32, + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*ethtypes.EthFilterResult", + "description": "*ethtypes.EthFilterResult", + "summary": "", + "schema": { + "examples": [ + [ + {} + ] + ], + "additionalProperties": false, + "properties": { + "Results": { + "items": { + "additionalProperties": true, + "type": "object" + }, + "type": "array" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2245" + } + }, + { + "name": "Filecoin.EthGetFilterLogs", + "description": "```go\nfunc (s *FullNodeStruct) EthGetFilterLogs(p0 context.Context, p1 ethtypes.EthFilterID) (*ethtypes.EthFilterResult, error) {\n\tif s.Internal.EthGetFilterLogs == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.EthGetFilterLogs(p0, p1)\n}\n```", + "summary": "Returns event logs matching filter with given id.\n(requires write perm since timestamp of last filter execution will be written)\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "ethtypes.EthFilterID", + "summary": "", + "schema": { + "examples": [ + "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e" + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "maxItems": 32, + "minItems": 32, + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*ethtypes.EthFilterResult", + "description": "*ethtypes.EthFilterResult", + "summary": "", + "schema": { + "examples": [ + [ + {} + ] + ], + "additionalProperties": false, + "properties": { + "Results": { + "items": { + "additionalProperties": true, + "type": "object" + }, + "type": "array" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2256" + } + }, + { + "name": "Filecoin.EthGetLogs", + "description": "```go\nfunc (s *FullNodeStruct) EthGetLogs(p0 context.Context, p1 *ethtypes.EthFilterSpec) (*ethtypes.EthFilterResult, error) {\n\tif s.Internal.EthGetLogs == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.EthGetLogs(p0, p1)\n}\n```", + "summary": "Returns event logs matching given filter spec.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "*ethtypes.EthFilterSpec", + "summary": "", + "schema": { + "examples": [ + { + "fromBlock": "2301220", + "address": [ + "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031" + ], + "topics": null + } + ], + "additionalProperties": false, + "properties": { + "address": { + "items": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 20, + "minItems": 20, + "type": "array" + }, + "type": "array" + }, + "blockHash": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "fromBlock": { + "type": "string" + }, + "toBlock": { + "type": "string" + }, + "topics": { + "items": { + "items": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "type": "array" + }, + "type": "array" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*ethtypes.EthFilterResult", + "description": "*ethtypes.EthFilterResult", + "summary": "", + "schema": { + "examples": [ + [ + {} + ] + ], + "additionalProperties": false, + "properties": { + "Results": { + "items": { + "additionalProperties": true, + "type": "object" + }, + "type": "array" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2267" + } + }, + { + "name": "Filecoin.EthGetMessageCidByTransactionHash", + "description": "```go\nfunc (s *FullNodeStruct) EthGetMessageCidByTransactionHash(p0 context.Context, p1 *ethtypes.EthHash) (*cid.Cid, error) {\n\tif s.Internal.EthGetMessageCidByTransactionHash == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.EthGetMessageCidByTransactionHash(p0, p1)\n}\n```", + "summary": "", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "*ethtypes.EthHash", + "summary": "", + "schema": { + "examples": [ + "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e" + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "maxItems": 32, + "minItems": 32, + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*cid.Cid", + "description": "*cid.Cid", + "summary": "", + "schema": { + "title": "Content Identifier", + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "examples": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2278" + } + }, + { + "name": "Filecoin.EthGetStorageAt", + "description": "```go\nfunc (s *FullNodeStruct) EthGetStorageAt(p0 context.Context, p1 ethtypes.EthAddress, p2 ethtypes.EthBytes, p3 ethtypes.EthBlockNumberOrHash) (ethtypes.EthBytes, error) {\n\tif s.Internal.EthGetStorageAt == nil {\n\t\treturn *new(ethtypes.EthBytes), ErrNotSupported\n\t}\n\treturn s.Internal.EthGetStorageAt(p0, p1, p2, p3)\n}\n```", + "summary": "", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "ethtypes.EthAddress", + "summary": "", + "schema": { + "examples": [ + "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031" + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "maxItems": 20, + "minItems": 20, + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "ethtypes.EthBytes", + "summary": "", + "schema": { + "examples": [ + "0x07" + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p3", + "description": "ethtypes.EthBlockNumberOrHash", + "summary": "", + "schema": { + "examples": [ + "string value" + ], + "additionalProperties": false, + "properties": { + "blockHash": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "blockNumber": { + "title": "number", + "type": "number" + }, + "requireCanonical": { + "type": "boolean" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "ethtypes.EthBytes", + "description": "ethtypes.EthBytes", + "summary": "", + "schema": { + "examples": [ + "0x07" + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2289" + } + }, + { + "name": "Filecoin.EthGetTransactionByBlockHashAndIndex", + "description": "```go\nfunc (s *FullNodeStruct) EthGetTransactionByBlockHashAndIndex(p0 context.Context, p1 ethtypes.EthHash, p2 ethtypes.EthUint64) (ethtypes.EthTx, error) {\n\tif s.Internal.EthGetTransactionByBlockHashAndIndex == nil {\n\t\treturn *new(ethtypes.EthTx), ErrNotSupported\n\t}\n\treturn s.Internal.EthGetTransactionByBlockHashAndIndex(p0, p1, p2)\n}\n```", + "summary": "", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "ethtypes.EthHash", + "summary": "", + "schema": { + "examples": [ + "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e" + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "maxItems": 32, + "minItems": 32, + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "ethtypes.EthUint64", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + "0x5" + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "ethtypes.EthTx", + "description": "ethtypes.EthTx", + "summary": "", + "schema": { + "examples": [ + { + "chainId": "0x5", + "nonce": "0x5", + "hash": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", + "blockHash": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", + "blockNumber": "0x5", + "transactionIndex": "0x5", + "from": "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031", + "to": "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031", + "value": "0x0", + "type": "0x5", + "input": "0x07", + "gas": "0x5", + "maxFeePerGas": "0x0", + "maxPriorityFeePerGas": "0x0", + "accessList": [ + "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e" + ], + "v": "0x0", + "r": "0x0", + "s": "0x0" + } + ], + "additionalProperties": false, + "properties": { + "accessList": { + "items": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "type": "array" + }, + "blockHash": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "blockNumber": { + "title": "number", + "type": "number" + }, + "chainId": { + "title": "number", + "type": "number" + }, + "from": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 20, + "minItems": 20, + "type": "array" + }, + "gas": { + "title": "number", + "type": "number" + }, + "hash": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "input": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "type": "array" + }, + "maxFeePerGas": { + "additionalProperties": false, + "type": "object" + }, + "maxPriorityFeePerGas": { + "additionalProperties": false, + "type": "object" + }, + "nonce": { + "title": "number", + "type": "number" + }, + "r": { + "additionalProperties": false, + "type": "object" + }, + "s": { + "additionalProperties": false, + "type": "object" + }, + "to": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 20, + "minItems": 20, + "type": "array" + }, + "transactionIndex": { + "title": "number", + "type": "number" + }, + "type": { + "title": "number", + "type": "number" + }, + "v": { + "additionalProperties": false, + "type": "object" + }, + "value": { + "additionalProperties": false, + "type": "object" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2300" + } + }, + { + "name": "Filecoin.EthGetTransactionByBlockNumberAndIndex", + "description": "```go\nfunc (s *FullNodeStruct) EthGetTransactionByBlockNumberAndIndex(p0 context.Context, p1 ethtypes.EthUint64, p2 ethtypes.EthUint64) (ethtypes.EthTx, error) {\n\tif s.Internal.EthGetTransactionByBlockNumberAndIndex == nil {\n\t\treturn *new(ethtypes.EthTx), ErrNotSupported\n\t}\n\treturn s.Internal.EthGetTransactionByBlockNumberAndIndex(p0, p1, p2)\n}\n```", + "summary": "", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "ethtypes.EthUint64", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + "0x5" + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "ethtypes.EthUint64", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + "0x5" + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "ethtypes.EthTx", + "description": "ethtypes.EthTx", + "summary": "", + "schema": { + "examples": [ + { + "chainId": "0x5", + "nonce": "0x5", + "hash": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", + "blockHash": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", + "blockNumber": "0x5", + "transactionIndex": "0x5", + "from": "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031", + "to": "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031", + "value": "0x0", + "type": "0x5", + "input": "0x07", + "gas": "0x5", + "maxFeePerGas": "0x0", + "maxPriorityFeePerGas": "0x0", + "accessList": [ + "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e" + ], + "v": "0x0", + "r": "0x0", + "s": "0x0" + } + ], + "additionalProperties": false, + "properties": { + "accessList": { + "items": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "type": "array" + }, + "blockHash": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "blockNumber": { + "title": "number", + "type": "number" + }, + "chainId": { + "title": "number", + "type": "number" + }, + "from": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 20, + "minItems": 20, + "type": "array" + }, + "gas": { + "title": "number", + "type": "number" + }, + "hash": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "input": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "type": "array" + }, + "maxFeePerGas": { + "additionalProperties": false, + "type": "object" + }, + "maxPriorityFeePerGas": { + "additionalProperties": false, + "type": "object" + }, + "nonce": { + "title": "number", + "type": "number" + }, + "r": { + "additionalProperties": false, + "type": "object" + }, + "s": { + "additionalProperties": false, + "type": "object" + }, + "to": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 20, + "minItems": 20, + "type": "array" + }, + "transactionIndex": { + "title": "number", + "type": "number" + }, + "type": { + "title": "number", + "type": "number" + }, + "v": { + "additionalProperties": false, + "type": "object" + }, + "value": { + "additionalProperties": false, + "type": "object" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2311" + } + }, + { + "name": "Filecoin.EthGetTransactionByHash", + "description": "```go\nfunc (s *FullNodeStruct) EthGetTransactionByHash(p0 context.Context, p1 *ethtypes.EthHash) (*ethtypes.EthTx, error) {\n\tif s.Internal.EthGetTransactionByHash == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.EthGetTransactionByHash(p0, p1)\n}\n```", + "summary": "", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "*ethtypes.EthHash", + "summary": "", + "schema": { + "examples": [ + "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e" + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "maxItems": 32, + "minItems": 32, + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*ethtypes.EthTx", + "description": "*ethtypes.EthTx", + "summary": "", + "schema": { + "examples": [ + { + "chainId": "0x5", + "nonce": "0x5", + "hash": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", + "blockHash": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", + "blockNumber": "0x5", + "transactionIndex": "0x5", + "from": "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031", + "to": "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031", + "value": "0x0", + "type": "0x5", + "input": "0x07", + "gas": "0x5", + "maxFeePerGas": "0x0", + "maxPriorityFeePerGas": "0x0", + "accessList": [ + "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e" + ], + "v": "0x0", + "r": "0x0", + "s": "0x0" + } + ], + "additionalProperties": false, + "properties": { + "accessList": { + "items": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "type": "array" + }, + "blockHash": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "blockNumber": { + "title": "number", + "type": "number" + }, + "chainId": { + "title": "number", + "type": "number" + }, + "from": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 20, + "minItems": 20, + "type": "array" + }, + "gas": { + "title": "number", + "type": "number" + }, + "hash": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "input": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "type": "array" + }, + "maxFeePerGas": { + "additionalProperties": false, + "type": "object" + }, + "maxPriorityFeePerGas": { + "additionalProperties": false, + "type": "object" + }, + "nonce": { + "title": "number", + "type": "number" + }, + "r": { + "additionalProperties": false, + "type": "object" + }, + "s": { + "additionalProperties": false, + "type": "object" + }, + "to": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 20, + "minItems": 20, + "type": "array" + }, + "transactionIndex": { + "title": "number", + "type": "number" + }, + "type": { + "title": "number", + "type": "number" + }, + "v": { + "additionalProperties": false, + "type": "object" + }, + "value": { + "additionalProperties": false, + "type": "object" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2322" + } + }, + { + "name": "Filecoin.EthGetTransactionByHashLimited", + "description": "```go\nfunc (s *FullNodeStruct) EthGetTransactionByHashLimited(p0 context.Context, p1 *ethtypes.EthHash, p2 abi.ChainEpoch) (*ethtypes.EthTx, error) {\n\tif s.Internal.EthGetTransactionByHashLimited == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.EthGetTransactionByHashLimited(p0, p1, p2)\n}\n```", + "summary": "", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "*ethtypes.EthHash", + "summary": "", + "schema": { + "examples": [ + "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e" + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "maxItems": 32, + "minItems": 32, + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "abi.ChainEpoch", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 10101 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*ethtypes.EthTx", + "description": "*ethtypes.EthTx", + "summary": "", + "schema": { + "examples": [ + { + "chainId": "0x5", + "nonce": "0x5", + "hash": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", + "blockHash": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", + "blockNumber": "0x5", + "transactionIndex": "0x5", + "from": "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031", + "to": "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031", + "value": "0x0", + "type": "0x5", + "input": "0x07", + "gas": "0x5", + "maxFeePerGas": "0x0", + "maxPriorityFeePerGas": "0x0", + "accessList": [ + "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e" + ], + "v": "0x0", + "r": "0x0", + "s": "0x0" + } + ], + "additionalProperties": false, + "properties": { + "accessList": { + "items": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "type": "array" + }, + "blockHash": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "blockNumber": { + "title": "number", + "type": "number" + }, + "chainId": { + "title": "number", + "type": "number" + }, + "from": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 20, + "minItems": 20, + "type": "array" + }, + "gas": { + "title": "number", + "type": "number" + }, + "hash": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "input": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "type": "array" + }, + "maxFeePerGas": { + "additionalProperties": false, + "type": "object" + }, + "maxPriorityFeePerGas": { + "additionalProperties": false, + "type": "object" + }, + "nonce": { + "title": "number", + "type": "number" + }, + "r": { + "additionalProperties": false, + "type": "object" + }, + "s": { + "additionalProperties": false, + "type": "object" + }, + "to": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 20, + "minItems": 20, + "type": "array" + }, + "transactionIndex": { + "title": "number", + "type": "number" + }, + "type": { + "title": "number", + "type": "number" + }, + "v": { + "additionalProperties": false, + "type": "object" + }, + "value": { + "additionalProperties": false, + "type": "object" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2333" + } + }, + { + "name": "Filecoin.EthGetTransactionCount", + "description": "```go\nfunc (s *FullNodeStruct) EthGetTransactionCount(p0 context.Context, p1 ethtypes.EthAddress, p2 ethtypes.EthBlockNumberOrHash) (ethtypes.EthUint64, error) {\n\tif s.Internal.EthGetTransactionCount == nil {\n\t\treturn *new(ethtypes.EthUint64), ErrNotSupported\n\t}\n\treturn s.Internal.EthGetTransactionCount(p0, p1, p2)\n}\n```", + "summary": "", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "ethtypes.EthAddress", + "summary": "", + "schema": { + "examples": [ + "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031" + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "maxItems": 20, + "minItems": 20, + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "ethtypes.EthBlockNumberOrHash", + "summary": "", + "schema": { + "examples": [ + "string value" + ], + "additionalProperties": false, + "properties": { + "blockHash": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "blockNumber": { + "title": "number", + "type": "number" + }, + "requireCanonical": { + "type": "boolean" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "ethtypes.EthUint64", + "description": "ethtypes.EthUint64", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + "0x5" + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2344" + } + }, + { + "name": "Filecoin.EthGetTransactionHashByCid", + "description": "```go\nfunc (s *FullNodeStruct) EthGetTransactionHashByCid(p0 context.Context, p1 cid.Cid) (*ethtypes.EthHash, error) {\n\tif s.Internal.EthGetTransactionHashByCid == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.EthGetTransactionHashByCid(p0, p1)\n}\n```", + "summary": "", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "cid.Cid", + "summary": "", + "schema": { + "title": "Content Identifier", + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "examples": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*ethtypes.EthHash", + "description": "*ethtypes.EthHash", + "summary": "", + "schema": { + "examples": [ + "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e" + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "maxItems": 32, + "minItems": 32, + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2355" + } + }, + { + "name": "Filecoin.EthGetTransactionReceipt", + "description": "```go\nfunc (s *FullNodeStruct) EthGetTransactionReceipt(p0 context.Context, p1 ethtypes.EthHash) (*EthTxReceipt, error) {\n\tif s.Internal.EthGetTransactionReceipt == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.EthGetTransactionReceipt(p0, p1)\n}\n```", + "summary": "", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "ethtypes.EthHash", + "summary": "", + "schema": { + "examples": [ + "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e" + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "maxItems": 32, + "minItems": 32, + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*EthTxReceipt", + "description": "*EthTxReceipt", + "summary": "", + "schema": { + "examples": [ + { + "transactionHash": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", + "transactionIndex": "0x5", + "blockHash": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", + "blockNumber": "0x5", + "from": "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031", + "to": "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031", + "root": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", + "status": "0x5", + "contractAddress": "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031", + "cumulativeGasUsed": "0x5", + "gasUsed": "0x5", + "effectiveGasPrice": "0x0", + "logsBloom": "0x07", + "logs": [ + { + "address": "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031", + "data": "0x07", + "topics": [ + "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e" + ], + "removed": true, + "logIndex": "0x5", + "transactionIndex": "0x5", + "transactionHash": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", + "blockHash": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", + "blockNumber": "0x5" + } + ], + "type": "0x5" + } + ], + "additionalProperties": false, + "properties": { + "blockHash": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "blockNumber": { + "title": "number", + "type": "number" + }, + "contractAddress": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 20, + "minItems": 20, + "type": "array" + }, + "cumulativeGasUsed": { + "title": "number", + "type": "number" + }, + "effectiveGasPrice": { + "additionalProperties": false, + "type": "object" + }, + "from": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 20, + "minItems": 20, + "type": "array" + }, + "gasUsed": { + "title": "number", + "type": "number" + }, + "logs": { + "items": { + "additionalProperties": false, + "properties": { + "address": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 20, + "minItems": 20, + "type": "array" + }, + "blockHash": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "blockNumber": { + "title": "number", + "type": "number" + }, + "data": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "type": "array" + }, + "logIndex": { + "title": "number", + "type": "number" + }, + "removed": { + "type": "boolean" + }, + "topics": { + "items": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "type": "array" + }, + "transactionHash": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "transactionIndex": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "type": "array" + }, + "logsBloom": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "type": "array" + }, + "root": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "status": { + "title": "number", + "type": "number" + }, + "to": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 20, + "minItems": 20, + "type": "array" + }, + "transactionHash": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "transactionIndex": { + "title": "number", + "type": "number" + }, + "type": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2366" + } + }, + { + "name": "Filecoin.EthGetTransactionReceiptLimited", + "description": "```go\nfunc (s *FullNodeStruct) EthGetTransactionReceiptLimited(p0 context.Context, p1 ethtypes.EthHash, p2 abi.ChainEpoch) (*EthTxReceipt, error) {\n\tif s.Internal.EthGetTransactionReceiptLimited == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.EthGetTransactionReceiptLimited(p0, p1, p2)\n}\n```", + "summary": "", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "ethtypes.EthHash", + "summary": "", + "schema": { + "examples": [ + "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e" + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "maxItems": 32, + "minItems": 32, + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "abi.ChainEpoch", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 10101 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*EthTxReceipt", + "description": "*EthTxReceipt", + "summary": "", + "schema": { + "examples": [ + { + "transactionHash": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", + "transactionIndex": "0x5", + "blockHash": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", + "blockNumber": "0x5", + "from": "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031", + "to": "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031", + "root": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", + "status": "0x5", + "contractAddress": "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031", + "cumulativeGasUsed": "0x5", + "gasUsed": "0x5", + "effectiveGasPrice": "0x0", + "logsBloom": "0x07", + "logs": [ + { + "address": "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031", + "data": "0x07", + "topics": [ + "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e" + ], + "removed": true, + "logIndex": "0x5", + "transactionIndex": "0x5", + "transactionHash": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", + "blockHash": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", + "blockNumber": "0x5" + } + ], + "type": "0x5" + } + ], + "additionalProperties": false, + "properties": { + "blockHash": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "blockNumber": { + "title": "number", + "type": "number" + }, + "contractAddress": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 20, + "minItems": 20, + "type": "array" + }, + "cumulativeGasUsed": { + "title": "number", + "type": "number" + }, + "effectiveGasPrice": { + "additionalProperties": false, + "type": "object" + }, + "from": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 20, + "minItems": 20, + "type": "array" + }, + "gasUsed": { + "title": "number", + "type": "number" + }, + "logs": { + "items": { + "additionalProperties": false, + "properties": { + "address": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 20, + "minItems": 20, + "type": "array" + }, + "blockHash": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "blockNumber": { + "title": "number", + "type": "number" + }, + "data": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "type": "array" + }, + "logIndex": { + "title": "number", + "type": "number" + }, + "removed": { + "type": "boolean" + }, + "topics": { + "items": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "type": "array" + }, + "transactionHash": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "transactionIndex": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "type": "array" + }, + "logsBloom": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "type": "array" + }, + "root": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "status": { + "title": "number", + "type": "number" + }, + "to": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 20, + "minItems": 20, + "type": "array" + }, + "transactionHash": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "transactionIndex": { + "title": "number", + "type": "number" + }, + "type": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2377" + } + }, + { + "name": "Filecoin.EthMaxPriorityFeePerGas", + "description": "```go\nfunc (s *FullNodeStruct) EthMaxPriorityFeePerGas(p0 context.Context) (ethtypes.EthBigInt, error) {\n\tif s.Internal.EthMaxPriorityFeePerGas == nil {\n\t\treturn *new(ethtypes.EthBigInt), ErrNotSupported\n\t}\n\treturn s.Internal.EthMaxPriorityFeePerGas(p0)\n}\n```", + "summary": "", + "paramStructure": "by-position", + "params": [], + "result": { + "name": "ethtypes.EthBigInt", + "description": "ethtypes.EthBigInt", + "summary": "", + "schema": { + "examples": [ + "0x0" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2388" + } + }, + { + "name": "Filecoin.EthNewBlockFilter", + "description": "```go\nfunc (s *FullNodeStruct) EthNewBlockFilter(p0 context.Context) (ethtypes.EthFilterID, error) {\n\tif s.Internal.EthNewBlockFilter == nil {\n\t\treturn *new(ethtypes.EthFilterID), ErrNotSupported\n\t}\n\treturn s.Internal.EthNewBlockFilter(p0)\n}\n```", + "summary": "Installs a persistent filter to notify when a new block arrives.\n", + "paramStructure": "by-position", + "params": [], + "result": { + "name": "ethtypes.EthFilterID", + "description": "ethtypes.EthFilterID", + "summary": "", + "schema": { + "examples": [ + "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e" + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "maxItems": 32, + "minItems": 32, + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2399" + } + }, + { + "name": "Filecoin.EthNewFilter", + "description": "```go\nfunc (s *FullNodeStruct) EthNewFilter(p0 context.Context, p1 *ethtypes.EthFilterSpec) (ethtypes.EthFilterID, error) {\n\tif s.Internal.EthNewFilter == nil {\n\t\treturn *new(ethtypes.EthFilterID), ErrNotSupported\n\t}\n\treturn s.Internal.EthNewFilter(p0, p1)\n}\n```", + "summary": "Installs a persistent filter based on given filter spec.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "*ethtypes.EthFilterSpec", + "summary": "", + "schema": { + "examples": [ + { + "fromBlock": "2301220", + "address": [ + "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031" + ], + "topics": null + } + ], + "additionalProperties": false, + "properties": { + "address": { + "items": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 20, + "minItems": 20, + "type": "array" + }, + "type": "array" + }, + "blockHash": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "fromBlock": { + "type": "string" + }, + "toBlock": { + "type": "string" + }, + "topics": { + "items": { + "items": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "type": "array" + }, + "type": "array" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "ethtypes.EthFilterID", + "description": "ethtypes.EthFilterID", + "summary": "", + "schema": { + "examples": [ + "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e" + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "maxItems": 32, + "minItems": 32, + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2410" + } + }, + { + "name": "Filecoin.EthNewPendingTransactionFilter", + "description": "```go\nfunc (s *FullNodeStruct) EthNewPendingTransactionFilter(p0 context.Context) (ethtypes.EthFilterID, error) {\n\tif s.Internal.EthNewPendingTransactionFilter == nil {\n\t\treturn *new(ethtypes.EthFilterID), ErrNotSupported\n\t}\n\treturn s.Internal.EthNewPendingTransactionFilter(p0)\n}\n```", + "summary": "Installs a persistent filter to notify when new messages arrive in the message pool.\n", + "paramStructure": "by-position", + "params": [], + "result": { + "name": "ethtypes.EthFilterID", + "description": "ethtypes.EthFilterID", + "summary": "", + "schema": { + "examples": [ + "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e" + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "maxItems": 32, + "minItems": 32, + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2421" + } + }, + { + "name": "Filecoin.EthProtocolVersion", + "description": "```go\nfunc (s *FullNodeStruct) EthProtocolVersion(p0 context.Context) (ethtypes.EthUint64, error) {\n\tif s.Internal.EthProtocolVersion == nil {\n\t\treturn *new(ethtypes.EthUint64), ErrNotSupported\n\t}\n\treturn s.Internal.EthProtocolVersion(p0)\n}\n```", + "summary": "", + "paramStructure": "by-position", + "params": [], + "result": { + "name": "ethtypes.EthUint64", + "description": "ethtypes.EthUint64", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + "0x5" + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2432" + } + }, + { + "name": "Filecoin.EthSendRawTransaction", + "description": "```go\nfunc (s *FullNodeStruct) EthSendRawTransaction(p0 context.Context, p1 ethtypes.EthBytes) (ethtypes.EthHash, error) {\n\tif s.Internal.EthSendRawTransaction == nil {\n\t\treturn *new(ethtypes.EthHash), ErrNotSupported\n\t}\n\treturn s.Internal.EthSendRawTransaction(p0, p1)\n}\n```", + "summary": "", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "ethtypes.EthBytes", + "summary": "", + "schema": { + "examples": [ + "0x07" + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "ethtypes.EthHash", + "description": "ethtypes.EthHash", + "summary": "", + "schema": { + "examples": [ + "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e" + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "maxItems": 32, + "minItems": 32, + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2443" + } + }, + { + "name": "Filecoin.EthSubscribe", + "description": "```go\nfunc (s *FullNodeStruct) EthSubscribe(p0 context.Context, p1 jsonrpc.RawParams) (ethtypes.EthSubscriptionID, error) {\n\tif s.Internal.EthSubscribe == nil {\n\t\treturn *new(ethtypes.EthSubscriptionID), ErrNotSupported\n\t}\n\treturn s.Internal.EthSubscribe(p0, p1)\n}\n```", + "summary": "Subscribe to different event types using websockets\neventTypes is one or more of:\n - newHeads: notify when new blocks arrive.\n - pendingTransactions: notify when new messages arrive in the message pool.\n - logs: notify new event logs that match a criteria\nparams contains additional parameters used with the log event type\nThe client will receive a stream of EthSubscriptionResponse values until EthUnsubscribe is called.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "jsonrpc.RawParams", + "summary": "", + "schema": { + "examples": [ + "Bw==" + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "ethtypes.EthSubscriptionID", + "description": "ethtypes.EthSubscriptionID", + "summary": "", + "schema": { + "examples": [ + "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e" + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "maxItems": 32, + "minItems": 32, + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2454" + } + }, + { + "name": "Filecoin.EthSyncing", + "description": "```go\nfunc (s *FullNodeStruct) EthSyncing(p0 context.Context) (ethtypes.EthSyncingResult, error) {\n\tif s.Internal.EthSyncing == nil {\n\t\treturn *new(ethtypes.EthSyncingResult), ErrNotSupported\n\t}\n\treturn s.Internal.EthSyncing(p0)\n}\n```", + "summary": "", + "paramStructure": "by-position", + "params": [], + "result": { + "name": "ethtypes.EthSyncingResult", + "description": "ethtypes.EthSyncingResult", + "summary": "", + "schema": { + "examples": [ + false + ], + "additionalProperties": false, + "properties": { + "CurrentBlock": { + "title": "number", + "type": "number" + }, + "DoneSync": { + "type": "boolean" + }, + "HighestBlock": { + "title": "number", + "type": "number" + }, + "StartingBlock": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2465" + } + }, + { + "name": "Filecoin.EthTraceBlock", + "description": "```go\nfunc (s *FullNodeStruct) EthTraceBlock(p0 context.Context, p1 string) ([]*ethtypes.EthTraceBlock, error) {\n\tif s.Internal.EthTraceBlock == nil {\n\t\treturn *new([]*ethtypes.EthTraceBlock), ErrNotSupported\n\t}\n\treturn s.Internal.EthTraceBlock(p0, p1)\n}\n```", + "summary": "TraceAPI related methods\n\nReturns traces created at given block\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "string", + "summary": "", + "schema": { + "examples": [ + "string value" + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "[]*ethtypes.EthTraceBlock", + "description": "[]*ethtypes.EthTraceBlock", + "summary": "", + "schema": { + "examples": [ + [ + { + "action": { + "callType": "string value", + "from": "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031", + "to": "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031", + "gas": "0x5", + "input": "0x07", + "value": "0x0" + }, + "result": { + "gasUsed": "0x5", + "output": "0x07" + }, + "subtraces": 123, + "traceAddress": [ + 123 + ], + "Type": "string value", + "blockHash": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", + "blockNumber": 9, + "transactionHash": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", + "transactionPosition": 123 + } + ] + ], + "items": [ + { + "additionalProperties": false, + "properties": { + "Type": { + "type": "string" + }, + "action": { + "additionalProperties": false, + "properties": { + "callType": { + "type": "string" + }, + "from": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 20, + "minItems": 20, + "type": "array" + }, + "gas": { + "title": "number", + "type": "number" + }, + "input": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "type": "array" + }, + "to": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 20, + "minItems": 20, + "type": "array" + }, + "value": { + "additionalProperties": false, + "type": "object" + } + }, + "type": "object" + }, + "blockHash": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "blockNumber": { + "title": "number", + "type": "number" + }, + "result": { + "additionalProperties": false, + "properties": { + "gasUsed": { + "title": "number", + "type": "number" + }, + "output": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "type": "array" + } + }, + "type": "object" + }, + "subtraces": { + "title": "number", + "type": "number" + }, + "traceAddress": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "type": "array" + }, + "transactionHash": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "transactionPosition": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2476" + } + }, + { + "name": "Filecoin.EthTraceReplayBlockTransactions", + "description": "```go\nfunc (s *FullNodeStruct) EthTraceReplayBlockTransactions(p0 context.Context, p1 string, p2 []string) ([]*ethtypes.EthTraceReplayBlockTransaction, error) {\n\tif s.Internal.EthTraceReplayBlockTransactions == nil {\n\t\treturn *new([]*ethtypes.EthTraceReplayBlockTransaction), ErrNotSupported\n\t}\n\treturn s.Internal.EthTraceReplayBlockTransactions(p0, p1, p2)\n}\n```", + "summary": "Replays all transactions in a block returning the requested traces for each transaction\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "string", + "summary": "", + "schema": { + "examples": [ + "string value" + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "[]string", + "summary": "", + "schema": { + "examples": [ + [ + "string value" + ] + ], + "items": [ + { + "type": [ + "string" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "[]*ethtypes.EthTraceReplayBlockTransaction", + "description": "[]*ethtypes.EthTraceReplayBlockTransaction", + "summary": "", + "schema": { + "examples": [ + [ + { + "output": "0x07", + "stateDiff": "string value", + "trace": [ + { + "action": { + "callType": "string value", + "from": "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031", + "to": "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031", + "gas": "0x5", + "input": "0x07", + "value": "0x0" + }, + "result": { + "gasUsed": "0x5", + "output": "0x07" + }, + "subtraces": 123, + "traceAddress": [ + 123 + ], + "Type": "string value" + } + ], + "transactionHash": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", + "vmTrace": "string value" + } + ] + ], + "items": [ + { + "additionalProperties": false, + "properties": { + "output": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "type": "array" + }, + "stateDiff": { + "type": "string" + }, + "trace": { + "items": { + "additionalProperties": false, + "properties": { + "Type": { + "type": "string" + }, + "action": { + "additionalProperties": false, + "properties": { + "callType": { + "type": "string" + }, + "from": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 20, + "minItems": 20, + "type": "array" + }, + "gas": { + "title": "number", + "type": "number" + }, + "input": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "type": "array" + }, + "to": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 20, + "minItems": 20, + "type": "array" + }, + "value": { + "additionalProperties": false, + "type": "object" + } + }, + "type": "object" + }, + "result": { + "additionalProperties": false, + "properties": { + "gasUsed": { + "title": "number", + "type": "number" + }, + "output": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "type": "array" + } + }, + "type": "object" + }, + "subtraces": { + "title": "number", + "type": "number" + }, + "traceAddress": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "type": "array" + } + }, + "type": "object" + }, + "type": "array" + }, + "transactionHash": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "vmTrace": { + "type": "string" + } + }, + "type": [ + "object" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2487" + } + }, + { + "name": "Filecoin.EthUninstallFilter", + "description": "```go\nfunc (s *FullNodeStruct) EthUninstallFilter(p0 context.Context, p1 ethtypes.EthFilterID) (bool, error) {\n\tif s.Internal.EthUninstallFilter == nil {\n\t\treturn false, ErrNotSupported\n\t}\n\treturn s.Internal.EthUninstallFilter(p0, p1)\n}\n```", + "summary": "Uninstalls a filter with given id.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "ethtypes.EthFilterID", + "summary": "", + "schema": { + "examples": [ + "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e" + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "maxItems": 32, + "minItems": 32, + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "bool", + "description": "bool", + "summary": "", + "schema": { + "examples": [ + true + ], + "type": [ + "boolean" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2498" + } + }, + { + "name": "Filecoin.EthUnsubscribe", + "description": "```go\nfunc (s *FullNodeStruct) EthUnsubscribe(p0 context.Context, p1 ethtypes.EthSubscriptionID) (bool, error) {\n\tif s.Internal.EthUnsubscribe == nil {\n\t\treturn false, ErrNotSupported\n\t}\n\treturn s.Internal.EthUnsubscribe(p0, p1)\n}\n```", + "summary": "Unsubscribe from a websocket subscription\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "ethtypes.EthSubscriptionID", + "summary": "", + "schema": { + "examples": [ + "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e" + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "maxItems": 32, + "minItems": 32, + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "bool", + "description": "bool", + "summary": "", + "schema": { + "examples": [ + true + ], + "type": [ + "boolean" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2509" + } + }, + { + "name": "Filecoin.FilecoinAddressToEthAddress", + "description": "```go\nfunc (s *FullNodeStruct) FilecoinAddressToEthAddress(p0 context.Context, p1 address.Address) (ethtypes.EthAddress, error) {\n\tif s.Internal.FilecoinAddressToEthAddress == nil {\n\t\treturn *new(ethtypes.EthAddress), ErrNotSupported\n\t}\n\treturn s.Internal.FilecoinAddressToEthAddress(p0, p1)\n}\n```", + "summary": "FilecoinAddressToEthAddress converts an f410 or f0 Filecoin Address to an EthAddress\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "ethtypes.EthAddress", + "description": "ethtypes.EthAddress", + "summary": "", + "schema": { + "examples": [ + "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031" + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "maxItems": 20, + "minItems": 20, + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2520" + } + }, + { + "name": "Filecoin.GasEstimateFeeCap", + "description": "```go\nfunc (s *FullNodeStruct) GasEstimateFeeCap(p0 context.Context, p1 *types.Message, p2 int64, p3 types.TipSetKey) (types.BigInt, error) {\n\tif s.Internal.GasEstimateFeeCap == nil {\n\t\treturn *new(types.BigInt), ErrNotSupported\n\t}\n\treturn s.Internal.GasEstimateFeeCap(p0, p1, p2, p3)\n}\n```", + "summary": "GasEstimateFeeCap estimates gas fee cap\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "*types.Message", + "summary": "", + "schema": { + "examples": [ + { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + } + ], + "additionalProperties": false, + "properties": { + "From": { + "additionalProperties": false, + "type": "object" + }, + "GasFeeCap": { + "additionalProperties": false, + "type": "object" + }, + "GasLimit": { + "title": "number", + "type": "number" + }, + "GasPremium": { + "additionalProperties": false, + "type": "object" + }, + "Method": { + "title": "number", + "type": "number" + }, + "Nonce": { + "title": "number", + "type": "number" + }, + "Params": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "To": { + "additionalProperties": false, + "type": "object" + }, + "Value": { + "additionalProperties": false, + "type": "object" + }, + "Version": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "int64", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 9 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p3", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "types.BigInt", + "description": "types.BigInt", + "summary": "", + "schema": { + "examples": [ + "0" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2531" + } + }, + { + "name": "Filecoin.GasEstimateGasLimit", + "description": "```go\nfunc (s *FullNodeStruct) GasEstimateGasLimit(p0 context.Context, p1 *types.Message, p2 types.TipSetKey) (int64, error) {\n\tif s.Internal.GasEstimateGasLimit == nil {\n\t\treturn 0, ErrNotSupported\n\t}\n\treturn s.Internal.GasEstimateGasLimit(p0, p1, p2)\n}\n```", + "summary": "GasEstimateGasLimit estimates gas used by the message and returns it.\nIt fails if message fails to execute.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "*types.Message", + "summary": "", + "schema": { + "examples": [ + { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + } + ], + "additionalProperties": false, + "properties": { + "From": { + "additionalProperties": false, + "type": "object" + }, + "GasFeeCap": { + "additionalProperties": false, + "type": "object" + }, + "GasLimit": { + "title": "number", + "type": "number" + }, + "GasPremium": { + "additionalProperties": false, + "type": "object" + }, + "Method": { + "title": "number", + "type": "number" + }, + "Nonce": { + "title": "number", + "type": "number" + }, + "Params": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "To": { + "additionalProperties": false, + "type": "object" + }, + "Value": { + "additionalProperties": false, + "type": "object" + }, + "Version": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "int64", + "description": "int64", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 9 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2542" + } + }, + { + "name": "Filecoin.GasEstimateGasPremium", + "description": "```go\nfunc (s *FullNodeStruct) GasEstimateGasPremium(p0 context.Context, p1 uint64, p2 address.Address, p3 int64, p4 types.TipSetKey) (types.BigInt, error) {\n\tif s.Internal.GasEstimateGasPremium == nil {\n\t\treturn *new(types.BigInt), ErrNotSupported\n\t}\n\treturn s.Internal.GasEstimateGasPremium(p0, p1, p2, p3, p4)\n}\n```", + "summary": "GasEstimateGasPremium estimates what gas price should be used for a\nmessage to have high likelihood of inclusion in `nblocksincl` epochs.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "uint64", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 42 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p3", + "description": "int64", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 9 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p4", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "types.BigInt", + "description": "types.BigInt", + "summary": "", + "schema": { + "examples": [ + "0" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2553" + } + }, + { + "name": "Filecoin.GasEstimateMessageGas", + "description": "```go\nfunc (s *FullNodeStruct) GasEstimateMessageGas(p0 context.Context, p1 *types.Message, p2 *MessageSendSpec, p3 types.TipSetKey) (*types.Message, error) {\n\tif s.Internal.GasEstimateMessageGas == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.GasEstimateMessageGas(p0, p1, p2, p3)\n}\n```", + "summary": "GasEstimateMessageGas estimates gas values for unset message gas fields\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "*types.Message", + "summary": "", + "schema": { + "examples": [ + { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + } + ], + "additionalProperties": false, + "properties": { + "From": { + "additionalProperties": false, + "type": "object" + }, + "GasFeeCap": { + "additionalProperties": false, + "type": "object" + }, + "GasLimit": { + "title": "number", + "type": "number" + }, + "GasPremium": { + "additionalProperties": false, + "type": "object" + }, + "Method": { + "title": "number", + "type": "number" + }, + "Nonce": { + "title": "number", + "type": "number" + }, + "Params": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "To": { + "additionalProperties": false, + "type": "object" + }, + "Value": { + "additionalProperties": false, + "type": "object" + }, + "Version": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "*MessageSendSpec", + "summary": "", + "schema": { + "examples": [ + { + "MaxFee": "0", + "MsgUuid": "07070707-0707-0707-0707-070707070707", + "MaximizeFeeCap": true + } + ], + "additionalProperties": false, + "properties": { + "MaxFee": { + "additionalProperties": false, + "type": "object" + }, + "MaximizeFeeCap": { + "type": "boolean" + }, + "MsgUuid": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 16, + "minItems": 16, + "type": "array" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p3", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*types.Message", + "description": "*types.Message", + "summary": "", + "schema": { + "examples": [ + { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + } + ], + "additionalProperties": false, + "properties": { + "From": { + "additionalProperties": false, + "type": "object" + }, + "GasFeeCap": { + "additionalProperties": false, + "type": "object" + }, + "GasLimit": { + "title": "number", + "type": "number" + }, + "GasPremium": { + "additionalProperties": false, + "type": "object" + }, + "Method": { + "title": "number", + "type": "number" + }, + "Nonce": { + "title": "number", + "type": "number" + }, + "Params": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "To": { + "additionalProperties": false, + "type": "object" + }, + "Value": { + "additionalProperties": false, + "type": "object" + }, + "Version": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2564" + } + }, + { + "name": "Filecoin.MarketAddBalance", + "description": "```go\nfunc (s *FullNodeStruct) MarketAddBalance(p0 context.Context, p1 address.Address, p2 address.Address, p3 types.BigInt) (cid.Cid, error) {\n\tif s.Internal.MarketAddBalance == nil {\n\t\treturn *new(cid.Cid), ErrNotSupported\n\t}\n\treturn s.Internal.MarketAddBalance(p0, p1, p2, p3)\n}\n```", + "summary": "MarketAddBalance adds funds to the market actor\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p3", + "description": "types.BigInt", + "summary": "", + "schema": { + "examples": [ + "0" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "cid.Cid", + "description": "cid.Cid", + "summary": "", + "schema": { + "title": "Content Identifier", + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "examples": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2575" + } + }, + { + "name": "Filecoin.MarketGetReserved", + "description": "```go\nfunc (s *FullNodeStruct) MarketGetReserved(p0 context.Context, p1 address.Address) (types.BigInt, error) {\n\tif s.Internal.MarketGetReserved == nil {\n\t\treturn *new(types.BigInt), ErrNotSupported\n\t}\n\treturn s.Internal.MarketGetReserved(p0, p1)\n}\n```", + "summary": "MarketGetReserved gets the amount of funds that are currently reserved for the address\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "types.BigInt", + "description": "types.BigInt", + "summary": "", + "schema": { + "examples": [ + "0" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2586" + } + }, + { + "name": "Filecoin.MarketReleaseFunds", + "description": "```go\nfunc (s *FullNodeStruct) MarketReleaseFunds(p0 context.Context, p1 address.Address, p2 types.BigInt) error {\n\tif s.Internal.MarketReleaseFunds == nil {\n\t\treturn ErrNotSupported\n\t}\n\treturn s.Internal.MarketReleaseFunds(p0, p1, p2)\n}\n```", + "summary": "MarketReleaseFunds releases funds reserved by MarketReserveFunds\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "types.BigInt", + "summary": "", + "schema": { + "examples": [ + "0" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "Null", + "description": "Null", + "schema": { + "type": [ + "null" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2597" + } + }, + { + "name": "Filecoin.MarketReserveFunds", + "description": "```go\nfunc (s *FullNodeStruct) MarketReserveFunds(p0 context.Context, p1 address.Address, p2 address.Address, p3 types.BigInt) (cid.Cid, error) {\n\tif s.Internal.MarketReserveFunds == nil {\n\t\treturn *new(cid.Cid), ErrNotSupported\n\t}\n\treturn s.Internal.MarketReserveFunds(p0, p1, p2, p3)\n}\n```", + "summary": "MarketReserveFunds reserves funds for a deal\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p3", + "description": "types.BigInt", + "summary": "", + "schema": { + "examples": [ + "0" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "cid.Cid", + "description": "cid.Cid", + "summary": "", + "schema": { + "title": "Content Identifier", + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "examples": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2608" + } + }, + { + "name": "Filecoin.MarketWithdraw", + "description": "```go\nfunc (s *FullNodeStruct) MarketWithdraw(p0 context.Context, p1 address.Address, p2 address.Address, p3 types.BigInt) (cid.Cid, error) {\n\tif s.Internal.MarketWithdraw == nil {\n\t\treturn *new(cid.Cid), ErrNotSupported\n\t}\n\treturn s.Internal.MarketWithdraw(p0, p1, p2, p3)\n}\n```", + "summary": "MarketWithdraw withdraws unlocked funds from the market actor\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p3", + "description": "types.BigInt", + "summary": "", + "schema": { + "examples": [ + "0" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "cid.Cid", + "description": "cid.Cid", + "summary": "", + "schema": { + "title": "Content Identifier", + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "examples": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2619" + } + }, + { + "name": "Filecoin.MinerCreateBlock", + "description": "```go\nfunc (s *FullNodeStruct) MinerCreateBlock(p0 context.Context, p1 *BlockTemplate) (*types.BlockMsg, error) {\n\tif s.Internal.MinerCreateBlock == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.MinerCreateBlock(p0, p1)\n}\n```", + "summary": "", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "*BlockTemplate", + "summary": "", + "schema": { + "examples": [ + { + "Miner": "f01234", + "Parents": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ], + "Ticket": { + "VRFProof": "Ynl0ZSBhcnJheQ==" + }, + "Eproof": { + "WinCount": 9, + "VRFProof": "Ynl0ZSBhcnJheQ==" + }, + "BeaconValues": [ + { + "Round": 42, + "Data": "Ynl0ZSBhcnJheQ==" + } + ], + "Messages": [ + { + "Message": { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + }, + "Signature": { + "Type": 2, + "Data": "Ynl0ZSBhcnJheQ==" + }, + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + } + ], + "Epoch": 10101, + "Timestamp": 42, + "WinningPoStProof": [ + { + "PoStProof": 8, + "ProofBytes": "Ynl0ZSBhcnJheQ==" + } + ] + } + ], + "additionalProperties": false, + "properties": { + "BeaconValues": { + "items": { + "additionalProperties": false, + "properties": { + "Data": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "Round": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "type": "array" + }, + "Epoch": { + "title": "number", + "type": "number" + }, + "Eproof": { + "additionalProperties": false, + "properties": { + "VRFProof": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "WinCount": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "Messages": { + "items": { + "additionalProperties": false, + "properties": { + "Message": { + "additionalProperties": false, + "properties": { + "From": { + "additionalProperties": false, + "type": "object" + }, + "GasFeeCap": { + "additionalProperties": false, + "type": "object" + }, + "GasLimit": { + "title": "number", + "type": "number" + }, + "GasPremium": { + "additionalProperties": false, + "type": "object" + }, + "Method": { + "title": "number", + "type": "number" + }, + "Nonce": { + "title": "number", + "type": "number" + }, + "Params": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "To": { + "additionalProperties": false, + "type": "object" + }, + "Value": { + "additionalProperties": false, + "type": "object" + }, + "Version": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "Signature": { + "additionalProperties": false, + "properties": { + "Data": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "Type": { + "title": "number", + "type": "number" + } + }, + "type": "object" + } + }, + "type": "object" + }, + "type": "array" + }, + "Miner": { + "additionalProperties": false, + "type": "object" + }, + "Parents": { + "additionalProperties": false, + "type": "object" + }, + "Ticket": { + "additionalProperties": false, + "properties": { + "VRFProof": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + } + }, + "type": "object" + }, + "Timestamp": { + "title": "number", + "type": "number" + }, + "WinningPoStProof": { + "items": { + "additionalProperties": false, + "properties": { + "PoStProof": { + "title": "number", + "type": "number" + }, + "ProofBytes": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + } + }, + "type": "object" + }, + "type": "array" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*types.BlockMsg", + "description": "*types.BlockMsg", + "summary": "", + "schema": { + "examples": [ + { + "Header": { + "Miner": "f01234", + "Ticket": { + "VRFProof": "Ynl0ZSBhcnJheQ==" + }, + "ElectionProof": { + "WinCount": 9, + "VRFProof": "Ynl0ZSBhcnJheQ==" + }, + "BeaconEntries": [ + { + "Round": 42, + "Data": "Ynl0ZSBhcnJheQ==" + } + ], + "WinPoStProof": [ + { + "PoStProof": 8, + "ProofBytes": "Ynl0ZSBhcnJheQ==" + } + ], + "Parents": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ], + "ParentWeight": "0", + "Height": 10101, + "ParentStateRoot": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "ParentMessageReceipts": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Messages": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "BLSAggregate": { + "Type": 2, + "Data": "Ynl0ZSBhcnJheQ==" + }, + "Timestamp": 42, + "BlockSig": { + "Type": 2, + "Data": "Ynl0ZSBhcnJheQ==" + }, + "ForkSignaling": 42, + "ParentBaseFee": "0" + }, + "BlsMessages": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ], + "SecpkMessages": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ] + } + ], + "additionalProperties": false, + "properties": { + "BlsMessages": { + "items": { + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "title": "Content Identifier", + "type": "string" + }, + "type": "array" + }, + "Header": { + "additionalProperties": false, + "properties": { + "BLSAggregate": { + "additionalProperties": false, + "properties": { + "Data": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "Type": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "BeaconEntries": { + "items": { + "additionalProperties": false, + "properties": { + "Data": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "Round": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "type": "array" + }, + "BlockSig": { + "additionalProperties": false, + "properties": { + "Data": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "Type": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "ElectionProof": { + "additionalProperties": false, + "properties": { + "VRFProof": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "WinCount": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "ForkSignaling": { + "title": "number", + "type": "number" + }, + "Height": { + "title": "number", + "type": "number" + }, + "Messages": { + "title": "Content Identifier", + "type": "string" + }, + "Miner": { + "additionalProperties": false, + "type": "object" + }, + "ParentBaseFee": { + "additionalProperties": false, + "type": "object" + }, + "ParentMessageReceipts": { + "title": "Content Identifier", + "type": "string" + }, + "ParentStateRoot": { + "title": "Content Identifier", + "type": "string" + }, + "ParentWeight": { + "additionalProperties": false, + "type": "object" + }, + "Parents": { + "items": { + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "title": "Content Identifier", + "type": "string" + }, + "type": "array" + }, + "Ticket": { + "additionalProperties": false, + "properties": { + "VRFProof": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + } + }, + "type": "object" + }, + "Timestamp": { + "title": "number", + "type": "number" + }, + "WinPoStProof": { + "items": { + "additionalProperties": false, + "properties": { + "PoStProof": { + "title": "number", + "type": "number" + }, + "ProofBytes": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + } + }, + "type": "object" + }, + "type": "array" + } + }, + "type": "object" + }, + "SecpkMessages": { + "items": { + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "title": "Content Identifier", + "type": "string" + }, + "type": "array" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2630" + } + }, + { + "name": "Filecoin.MinerGetBaseInfo", + "description": "```go\nfunc (s *FullNodeStruct) MinerGetBaseInfo(p0 context.Context, p1 address.Address, p2 abi.ChainEpoch, p3 types.TipSetKey) (*MiningBaseInfo, error) {\n\tif s.Internal.MinerGetBaseInfo == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.MinerGetBaseInfo(p0, p1, p2, p3)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "abi.ChainEpoch", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 10101 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p3", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*MiningBaseInfo", + "description": "*MiningBaseInfo", + "summary": "", + "schema": { + "examples": [ + { + "MinerPower": "0", + "NetworkPower": "0", + "Sectors": [ + { + "SealProof": 8, + "SectorNumber": 9, + "SectorKey": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "SealedCID": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + } + ], + "WorkerKey": "f01234", + "SectorSize": 34359738368, + "PrevBeaconEntry": { + "Round": 42, + "Data": "Ynl0ZSBhcnJheQ==" + }, + "BeaconEntries": [ + { + "Round": 42, + "Data": "Ynl0ZSBhcnJheQ==" + } + ], + "EligibleForMining": true + } + ], + "additionalProperties": false, + "properties": { + "BeaconEntries": { + "items": { + "additionalProperties": false, + "properties": { + "Data": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "Round": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "type": "array" + }, + "EligibleForMining": { + "type": "boolean" + }, + "MinerPower": { + "additionalProperties": false, + "type": "object" + }, + "NetworkPower": { + "additionalProperties": false, + "type": "object" + }, + "PrevBeaconEntry": { + "additionalProperties": false, + "properties": { + "Data": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "Round": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "SectorSize": { + "title": "number", + "type": "number" + }, + "Sectors": { + "items": { + "additionalProperties": false, + "properties": { + "SealProof": { + "title": "number", + "type": "number" + }, + "SealedCID": { + "title": "Content Identifier", + "type": "string" + }, + "SectorKey": { + "title": "Content Identifier", + "type": "string" + }, + "SectorNumber": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "type": "array" + }, + "WorkerKey": { + "additionalProperties": false, + "type": "object" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2641" + } + }, + { + "name": "Filecoin.MpoolBatchPush", + "description": "```go\nfunc (s *FullNodeStruct) MpoolBatchPush(p0 context.Context, p1 []*types.SignedMessage) ([]cid.Cid, error) {\n\tif s.Internal.MpoolBatchPush == nil {\n\t\treturn *new([]cid.Cid), ErrNotSupported\n\t}\n\treturn s.Internal.MpoolBatchPush(p0, p1)\n}\n```", + "summary": "MpoolBatchPush batch pushes a signed message to mempool.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "[]*types.SignedMessage", + "summary": "", + "schema": { + "examples": [ + [ + { + "Message": { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + }, + "Signature": { + "Type": 2, + "Data": "Ynl0ZSBhcnJheQ==" + }, + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + } + ] + ], + "items": [ + { + "additionalProperties": false, + "properties": { + "Message": { + "additionalProperties": false, + "properties": { + "From": { + "additionalProperties": false, + "type": "object" + }, + "GasFeeCap": { + "additionalProperties": false, + "type": "object" + }, + "GasLimit": { + "title": "number", + "type": "number" + }, + "GasPremium": { + "additionalProperties": false, + "type": "object" + }, + "Method": { + "title": "number", + "type": "number" + }, + "Nonce": { + "title": "number", + "type": "number" + }, + "Params": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "To": { + "additionalProperties": false, + "type": "object" + }, + "Value": { + "additionalProperties": false, + "type": "object" + }, + "Version": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "Signature": { + "additionalProperties": false, + "properties": { + "Data": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "Type": { + "title": "number", + "type": "number" + } + }, + "type": "object" + } + }, + "type": [ + "object" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "[]cid.Cid", + "description": "[]cid.Cid", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ] + ], + "items": [ + { + "title": "Content Identifier", + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "type": [ + "string" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2652" + } + }, + { + "name": "Filecoin.MpoolBatchPushMessage", + "description": "```go\nfunc (s *FullNodeStruct) MpoolBatchPushMessage(p0 context.Context, p1 []*types.Message, p2 *MessageSendSpec) ([]*types.SignedMessage, error) {\n\tif s.Internal.MpoolBatchPushMessage == nil {\n\t\treturn *new([]*types.SignedMessage), ErrNotSupported\n\t}\n\treturn s.Internal.MpoolBatchPushMessage(p0, p1, p2)\n}\n```", + "summary": "MpoolBatchPushMessage batch pushes a unsigned message to mempool.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "[]*types.Message", + "summary": "", + "schema": { + "examples": [ + [ + { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + } + ] + ], + "items": [ + { + "additionalProperties": false, + "properties": { + "From": { + "additionalProperties": false, + "type": "object" + }, + "GasFeeCap": { + "additionalProperties": false, + "type": "object" + }, + "GasLimit": { + "title": "number", + "type": "number" + }, + "GasPremium": { + "additionalProperties": false, + "type": "object" + }, + "Method": { + "title": "number", + "type": "number" + }, + "Nonce": { + "title": "number", + "type": "number" + }, + "Params": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "To": { + "additionalProperties": false, + "type": "object" + }, + "Value": { + "additionalProperties": false, + "type": "object" + }, + "Version": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "*MessageSendSpec", + "summary": "", + "schema": { + "examples": [ + { + "MaxFee": "0", + "MsgUuid": "07070707-0707-0707-0707-070707070707", + "MaximizeFeeCap": true + } + ], + "additionalProperties": false, + "properties": { + "MaxFee": { + "additionalProperties": false, + "type": "object" + }, + "MaximizeFeeCap": { + "type": "boolean" + }, + "MsgUuid": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 16, + "minItems": 16, + "type": "array" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "[]*types.SignedMessage", + "description": "[]*types.SignedMessage", + "summary": "", + "schema": { + "examples": [ + [ + { + "Message": { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + }, + "Signature": { + "Type": 2, + "Data": "Ynl0ZSBhcnJheQ==" + }, + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + } + ] + ], + "items": [ + { + "additionalProperties": false, + "properties": { + "Message": { + "additionalProperties": false, + "properties": { + "From": { + "additionalProperties": false, + "type": "object" + }, + "GasFeeCap": { + "additionalProperties": false, + "type": "object" + }, + "GasLimit": { + "title": "number", + "type": "number" + }, + "GasPremium": { + "additionalProperties": false, + "type": "object" + }, + "Method": { + "title": "number", + "type": "number" + }, + "Nonce": { + "title": "number", + "type": "number" + }, + "Params": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "To": { + "additionalProperties": false, + "type": "object" + }, + "Value": { + "additionalProperties": false, + "type": "object" + }, + "Version": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "Signature": { + "additionalProperties": false, + "properties": { + "Data": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "Type": { + "title": "number", + "type": "number" + } + }, + "type": "object" + } + }, + "type": [ + "object" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2663" + } + }, + { + "name": "Filecoin.MpoolBatchPushUntrusted", + "description": "```go\nfunc (s *FullNodeStruct) MpoolBatchPushUntrusted(p0 context.Context, p1 []*types.SignedMessage) ([]cid.Cid, error) {\n\tif s.Internal.MpoolBatchPushUntrusted == nil {\n\t\treturn *new([]cid.Cid), ErrNotSupported\n\t}\n\treturn s.Internal.MpoolBatchPushUntrusted(p0, p1)\n}\n```", + "summary": "MpoolBatchPushUntrusted batch pushes a signed message to mempool from untrusted sources.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "[]*types.SignedMessage", + "summary": "", + "schema": { + "examples": [ + [ + { + "Message": { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + }, + "Signature": { + "Type": 2, + "Data": "Ynl0ZSBhcnJheQ==" + }, + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + } + ] + ], + "items": [ + { + "additionalProperties": false, + "properties": { + "Message": { + "additionalProperties": false, + "properties": { + "From": { + "additionalProperties": false, + "type": "object" + }, + "GasFeeCap": { + "additionalProperties": false, + "type": "object" + }, + "GasLimit": { + "title": "number", + "type": "number" + }, + "GasPremium": { + "additionalProperties": false, + "type": "object" + }, + "Method": { + "title": "number", + "type": "number" + }, + "Nonce": { + "title": "number", + "type": "number" + }, + "Params": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "To": { + "additionalProperties": false, + "type": "object" + }, + "Value": { + "additionalProperties": false, + "type": "object" + }, + "Version": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "Signature": { + "additionalProperties": false, + "properties": { + "Data": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "Type": { + "title": "number", + "type": "number" + } + }, + "type": "object" + } + }, + "type": [ + "object" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "[]cid.Cid", + "description": "[]cid.Cid", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ] + ], + "items": [ + { + "title": "Content Identifier", + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "type": [ + "string" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2674" + } + }, + { + "name": "Filecoin.MpoolCheckMessages", + "description": "```go\nfunc (s *FullNodeStruct) MpoolCheckMessages(p0 context.Context, p1 []*MessagePrototype) ([][]MessageCheckStatus, error) {\n\tif s.Internal.MpoolCheckMessages == nil {\n\t\treturn *new([][]MessageCheckStatus), ErrNotSupported\n\t}\n\treturn s.Internal.MpoolCheckMessages(p0, p1)\n}\n```", + "summary": "MpoolCheckMessages performs logical checks on a batch of messages\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "[]*MessagePrototype", + "summary": "", + "schema": { + "examples": [ + [ + { + "Message": { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + }, + "ValidNonce": true + } + ] + ], + "items": [ + { + "additionalProperties": false, + "properties": { + "Message": { + "additionalProperties": false, + "properties": { + "From": { + "additionalProperties": false, + "type": "object" + }, + "GasFeeCap": { + "additionalProperties": false, + "type": "object" + }, + "GasLimit": { + "title": "number", + "type": "number" + }, + "GasPremium": { + "additionalProperties": false, + "type": "object" + }, + "Method": { + "title": "number", + "type": "number" + }, + "Nonce": { + "title": "number", + "type": "number" + }, + "Params": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "To": { + "additionalProperties": false, + "type": "object" + }, + "Value": { + "additionalProperties": false, + "type": "object" + }, + "Version": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "ValidNonce": { + "type": "boolean" + } + }, + "type": [ + "object" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "[][]MessageCheckStatus", + "description": "[][]MessageCheckStatus", + "summary": "", + "schema": { + "examples": [ + [ + [ + { + "Cid": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Code": 0, + "OK": true, + "Err": "string value", + "Hint": { + "abc": 123 + } + } + ] + ] + ], + "items": [ + { + "items": [ + { + "additionalProperties": false, + "properties": { + "Cid": { + "title": "Content Identifier", + "type": "string" + }, + "Code": { + "title": "number", + "type": "number" + }, + "Err": { + "type": "string" + }, + "Hint": { + "patternProperties": { + ".*": { + "additionalProperties": true, + "type": "object" + } + }, + "type": "object" + }, + "OK": { + "type": "boolean" + } + }, + "type": [ + "object" + ] + } + ], + "type": [ + "array" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2685" + } + }, + { + "name": "Filecoin.MpoolCheckPendingMessages", + "description": "```go\nfunc (s *FullNodeStruct) MpoolCheckPendingMessages(p0 context.Context, p1 address.Address) ([][]MessageCheckStatus, error) {\n\tif s.Internal.MpoolCheckPendingMessages == nil {\n\t\treturn *new([][]MessageCheckStatus), ErrNotSupported\n\t}\n\treturn s.Internal.MpoolCheckPendingMessages(p0, p1)\n}\n```", + "summary": "MpoolCheckPendingMessages performs logical checks for all pending messages from a given address\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "[][]MessageCheckStatus", + "description": "[][]MessageCheckStatus", + "summary": "", + "schema": { + "examples": [ + [ + [ + { + "Cid": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Code": 0, + "OK": true, + "Err": "string value", + "Hint": { + "abc": 123 + } + } + ] + ] + ], + "items": [ + { + "items": [ + { + "additionalProperties": false, + "properties": { + "Cid": { + "title": "Content Identifier", + "type": "string" + }, + "Code": { + "title": "number", + "type": "number" + }, + "Err": { + "type": "string" + }, + "Hint": { + "patternProperties": { + ".*": { + "additionalProperties": true, + "type": "object" + } + }, + "type": "object" + }, + "OK": { + "type": "boolean" + } + }, + "type": [ + "object" + ] + } + ], + "type": [ + "array" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2696" + } + }, + { + "name": "Filecoin.MpoolCheckReplaceMessages", + "description": "```go\nfunc (s *FullNodeStruct) MpoolCheckReplaceMessages(p0 context.Context, p1 []*types.Message) ([][]MessageCheckStatus, error) {\n\tif s.Internal.MpoolCheckReplaceMessages == nil {\n\t\treturn *new([][]MessageCheckStatus), ErrNotSupported\n\t}\n\treturn s.Internal.MpoolCheckReplaceMessages(p0, p1)\n}\n```", + "summary": "MpoolCheckReplaceMessages performs logical checks on pending messages with replacement\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "[]*types.Message", + "summary": "", + "schema": { + "examples": [ + [ + { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + } + ] + ], + "items": [ + { + "additionalProperties": false, + "properties": { + "From": { + "additionalProperties": false, + "type": "object" + }, + "GasFeeCap": { + "additionalProperties": false, + "type": "object" + }, + "GasLimit": { + "title": "number", + "type": "number" + }, + "GasPremium": { + "additionalProperties": false, + "type": "object" + }, + "Method": { + "title": "number", + "type": "number" + }, + "Nonce": { + "title": "number", + "type": "number" + }, + "Params": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "To": { + "additionalProperties": false, + "type": "object" + }, + "Value": { + "additionalProperties": false, + "type": "object" + }, + "Version": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "[][]MessageCheckStatus", + "description": "[][]MessageCheckStatus", + "summary": "", + "schema": { + "examples": [ + [ + [ + { + "Cid": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Code": 0, + "OK": true, + "Err": "string value", + "Hint": { + "abc": 123 + } + } + ] + ] + ], + "items": [ + { + "items": [ + { + "additionalProperties": false, + "properties": { + "Cid": { + "title": "Content Identifier", + "type": "string" + }, + "Code": { + "title": "number", + "type": "number" + }, + "Err": { + "type": "string" + }, + "Hint": { + "patternProperties": { + ".*": { + "additionalProperties": true, + "type": "object" + } + }, + "type": "object" + }, + "OK": { + "type": "boolean" + } + }, + "type": [ + "object" + ] + } + ], + "type": [ + "array" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2707" + } + }, + { + "name": "Filecoin.MpoolClear", + "description": "```go\nfunc (s *FullNodeStruct) MpoolClear(p0 context.Context, p1 bool) error {\n\tif s.Internal.MpoolClear == nil {\n\t\treturn ErrNotSupported\n\t}\n\treturn s.Internal.MpoolClear(p0, p1)\n}\n```", + "summary": "MpoolClear clears pending messages from the mpool.\nIf clearLocal is true, ALL messages will be cleared.\nIf clearLocal is false, local messages will be protected, all others will be cleared.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "bool", + "summary": "", + "schema": { + "examples": [ + true + ], + "type": [ + "boolean" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "Null", + "description": "Null", + "schema": { + "type": [ + "null" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2718" + } + }, + { + "name": "Filecoin.MpoolGetConfig", + "description": "```go\nfunc (s *FullNodeStruct) MpoolGetConfig(p0 context.Context) (*types.MpoolConfig, error) {\n\tif s.Internal.MpoolGetConfig == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.MpoolGetConfig(p0)\n}\n```", + "summary": "MpoolGetConfig returns (a copy of) the current mpool config\n", + "paramStructure": "by-position", + "params": [], + "result": { + "name": "*types.MpoolConfig", + "description": "*types.MpoolConfig", + "summary": "", + "schema": { + "examples": [ + { + "PriorityAddrs": [ + "f01234" + ], + "SizeLimitHigh": 123, + "SizeLimitLow": 123, + "ReplaceByFeeRatio": 1.23, + "PruneCooldown": 60000000000, + "GasLimitOverestimation": 12.3 + } + ], + "additionalProperties": false, + "properties": { + "GasLimitOverestimation": { + "type": "number" + }, + "PriorityAddrs": { + "items": { + "additionalProperties": false, + "type": "object" + }, + "type": "array" + }, + "PruneCooldown": { + "title": "number", + "type": "number" + }, + "ReplaceByFeeRatio": { + "title": "number", + "type": "number" + }, + "SizeLimitHigh": { + "title": "number", + "type": "number" + }, + "SizeLimitLow": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2729" + } + }, + { + "name": "Filecoin.MpoolGetNonce", + "description": "```go\nfunc (s *FullNodeStruct) MpoolGetNonce(p0 context.Context, p1 address.Address) (uint64, error) {\n\tif s.Internal.MpoolGetNonce == nil {\n\t\treturn 0, ErrNotSupported\n\t}\n\treturn s.Internal.MpoolGetNonce(p0, p1)\n}\n```", + "summary": "MpoolGetNonce gets next nonce for the specified sender.\nNote that this method may not be atomic. Use MpoolPushMessage instead.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "uint64", + "description": "uint64", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 42 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2740" + } + }, + { + "name": "Filecoin.MpoolPending", + "description": "```go\nfunc (s *FullNodeStruct) MpoolPending(p0 context.Context, p1 types.TipSetKey) ([]*types.SignedMessage, error) {\n\tif s.Internal.MpoolPending == nil {\n\t\treturn *new([]*types.SignedMessage), ErrNotSupported\n\t}\n\treturn s.Internal.MpoolPending(p0, p1)\n}\n```", + "summary": "MpoolPending returns pending mempool messages.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "[]*types.SignedMessage", + "description": "[]*types.SignedMessage", + "summary": "", + "schema": { + "examples": [ + [ + { + "Message": { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + }, + "Signature": { + "Type": 2, + "Data": "Ynl0ZSBhcnJheQ==" + }, + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + } + ] + ], + "items": [ + { + "additionalProperties": false, + "properties": { + "Message": { + "additionalProperties": false, + "properties": { + "From": { + "additionalProperties": false, + "type": "object" + }, + "GasFeeCap": { + "additionalProperties": false, + "type": "object" + }, + "GasLimit": { + "title": "number", + "type": "number" + }, + "GasPremium": { + "additionalProperties": false, + "type": "object" + }, + "Method": { + "title": "number", + "type": "number" + }, + "Nonce": { + "title": "number", + "type": "number" + }, + "Params": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "To": { + "additionalProperties": false, + "type": "object" + }, + "Value": { + "additionalProperties": false, + "type": "object" + }, + "Version": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "Signature": { + "additionalProperties": false, + "properties": { + "Data": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "Type": { + "title": "number", + "type": "number" + } + }, + "type": "object" + } + }, + "type": [ + "object" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2751" + } + }, + { + "name": "Filecoin.MpoolPush", + "description": "```go\nfunc (s *FullNodeStruct) MpoolPush(p0 context.Context, p1 *types.SignedMessage) (cid.Cid, error) {\n\tif s.Internal.MpoolPush == nil {\n\t\treturn *new(cid.Cid), ErrNotSupported\n\t}\n\treturn s.Internal.MpoolPush(p0, p1)\n}\n```", + "summary": "MpoolPush pushes a signed message to mempool.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "*types.SignedMessage", + "summary": "", + "schema": { + "examples": [ + { + "Message": { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + }, + "Signature": { + "Type": 2, + "Data": "Ynl0ZSBhcnJheQ==" + }, + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + } + ], + "additionalProperties": false, + "properties": { + "Message": { + "additionalProperties": false, + "properties": { + "From": { + "additionalProperties": false, + "type": "object" + }, + "GasFeeCap": { + "additionalProperties": false, + "type": "object" + }, + "GasLimit": { + "title": "number", + "type": "number" + }, + "GasPremium": { + "additionalProperties": false, + "type": "object" + }, + "Method": { + "title": "number", + "type": "number" + }, + "Nonce": { + "title": "number", + "type": "number" + }, + "Params": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "To": { + "additionalProperties": false, + "type": "object" + }, + "Value": { + "additionalProperties": false, + "type": "object" + }, + "Version": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "Signature": { + "additionalProperties": false, + "properties": { + "Data": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "Type": { + "title": "number", + "type": "number" + } + }, + "type": "object" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "cid.Cid", + "description": "cid.Cid", + "summary": "", + "schema": { + "title": "Content Identifier", + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "examples": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2762" + } + }, + { + "name": "Filecoin.MpoolPushMessage", + "description": "```go\nfunc (s *FullNodeStruct) MpoolPushMessage(p0 context.Context, p1 *types.Message, p2 *MessageSendSpec) (*types.SignedMessage, error) {\n\tif s.Internal.MpoolPushMessage == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.MpoolPushMessage(p0, p1, p2)\n}\n```", + "summary": "MpoolPushMessage atomically assigns a nonce, signs, and pushes a message\nto mempool.\nmaxFee is only used when GasFeeCap/GasPremium fields aren't specified\n\nWhen maxFee is set to 0, MpoolPushMessage will guess appropriate fee\nbased on current chain conditions\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "*types.Message", + "summary": "", + "schema": { + "examples": [ + { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + } + ], + "additionalProperties": false, + "properties": { + "From": { + "additionalProperties": false, + "type": "object" + }, + "GasFeeCap": { + "additionalProperties": false, + "type": "object" + }, + "GasLimit": { + "title": "number", + "type": "number" + }, + "GasPremium": { + "additionalProperties": false, + "type": "object" + }, + "Method": { + "title": "number", + "type": "number" + }, + "Nonce": { + "title": "number", + "type": "number" + }, + "Params": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "To": { + "additionalProperties": false, + "type": "object" + }, + "Value": { + "additionalProperties": false, + "type": "object" + }, + "Version": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "*MessageSendSpec", + "summary": "", + "schema": { + "examples": [ + { + "MaxFee": "0", + "MsgUuid": "07070707-0707-0707-0707-070707070707", + "MaximizeFeeCap": true + } + ], + "additionalProperties": false, + "properties": { + "MaxFee": { + "additionalProperties": false, + "type": "object" + }, + "MaximizeFeeCap": { + "type": "boolean" + }, + "MsgUuid": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 16, + "minItems": 16, + "type": "array" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*types.SignedMessage", + "description": "*types.SignedMessage", + "summary": "", + "schema": { + "examples": [ + { + "Message": { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + }, + "Signature": { + "Type": 2, + "Data": "Ynl0ZSBhcnJheQ==" + }, + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + } + ], + "additionalProperties": false, + "properties": { + "Message": { + "additionalProperties": false, + "properties": { + "From": { + "additionalProperties": false, + "type": "object" + }, + "GasFeeCap": { + "additionalProperties": false, + "type": "object" + }, + "GasLimit": { + "title": "number", + "type": "number" + }, + "GasPremium": { + "additionalProperties": false, + "type": "object" + }, + "Method": { + "title": "number", + "type": "number" + }, + "Nonce": { + "title": "number", + "type": "number" + }, + "Params": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "To": { + "additionalProperties": false, + "type": "object" + }, + "Value": { + "additionalProperties": false, + "type": "object" + }, + "Version": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "Signature": { + "additionalProperties": false, + "properties": { + "Data": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "Type": { + "title": "number", + "type": "number" + } + }, + "type": "object" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2773" + } + }, + { + "name": "Filecoin.MpoolPushUntrusted", + "description": "```go\nfunc (s *FullNodeStruct) MpoolPushUntrusted(p0 context.Context, p1 *types.SignedMessage) (cid.Cid, error) {\n\tif s.Internal.MpoolPushUntrusted == nil {\n\t\treturn *new(cid.Cid), ErrNotSupported\n\t}\n\treturn s.Internal.MpoolPushUntrusted(p0, p1)\n}\n```", + "summary": "MpoolPushUntrusted pushes a signed message to mempool from untrusted sources.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "*types.SignedMessage", + "summary": "", + "schema": { + "examples": [ + { + "Message": { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + }, + "Signature": { + "Type": 2, + "Data": "Ynl0ZSBhcnJheQ==" + }, + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + } + ], + "additionalProperties": false, + "properties": { + "Message": { + "additionalProperties": false, + "properties": { + "From": { + "additionalProperties": false, + "type": "object" + }, + "GasFeeCap": { + "additionalProperties": false, + "type": "object" + }, + "GasLimit": { + "title": "number", + "type": "number" + }, + "GasPremium": { + "additionalProperties": false, + "type": "object" + }, + "Method": { + "title": "number", + "type": "number" + }, + "Nonce": { + "title": "number", + "type": "number" + }, + "Params": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "To": { + "additionalProperties": false, + "type": "object" + }, + "Value": { + "additionalProperties": false, + "type": "object" + }, + "Version": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "Signature": { + "additionalProperties": false, + "properties": { + "Data": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "Type": { + "title": "number", + "type": "number" + } + }, + "type": "object" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "cid.Cid", + "description": "cid.Cid", + "summary": "", + "schema": { + "title": "Content Identifier", + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "examples": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2784" + } + }, + { + "name": "Filecoin.MpoolSelect", + "description": "```go\nfunc (s *FullNodeStruct) MpoolSelect(p0 context.Context, p1 types.TipSetKey, p2 float64) ([]*types.SignedMessage, error) {\n\tif s.Internal.MpoolSelect == nil {\n\t\treturn *new([]*types.SignedMessage), ErrNotSupported\n\t}\n\treturn s.Internal.MpoolSelect(p0, p1, p2)\n}\n```", + "summary": "MpoolSelect returns a list of pending messages for inclusion in the next block\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "float64", + "summary": "", + "schema": { + "examples": [ + 12.3 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "[]*types.SignedMessage", + "description": "[]*types.SignedMessage", + "summary": "", + "schema": { + "examples": [ + [ + { + "Message": { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + }, + "Signature": { + "Type": 2, + "Data": "Ynl0ZSBhcnJheQ==" + }, + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + } + ] + ], + "items": [ + { + "additionalProperties": false, + "properties": { + "Message": { + "additionalProperties": false, + "properties": { + "From": { + "additionalProperties": false, + "type": "object" + }, + "GasFeeCap": { + "additionalProperties": false, + "type": "object" + }, + "GasLimit": { + "title": "number", + "type": "number" + }, + "GasPremium": { + "additionalProperties": false, + "type": "object" + }, + "Method": { + "title": "number", + "type": "number" + }, + "Nonce": { + "title": "number", + "type": "number" + }, + "Params": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "To": { + "additionalProperties": false, + "type": "object" + }, + "Value": { + "additionalProperties": false, + "type": "object" + }, + "Version": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "Signature": { + "additionalProperties": false, + "properties": { + "Data": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "Type": { + "title": "number", + "type": "number" + } + }, + "type": "object" + } + }, + "type": [ + "object" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2795" + } + }, + { + "name": "Filecoin.MpoolSetConfig", + "description": "```go\nfunc (s *FullNodeStruct) MpoolSetConfig(p0 context.Context, p1 *types.MpoolConfig) error {\n\tif s.Internal.MpoolSetConfig == nil {\n\t\treturn ErrNotSupported\n\t}\n\treturn s.Internal.MpoolSetConfig(p0, p1)\n}\n```", + "summary": "MpoolSetConfig sets the mpool config to (a copy of) the supplied config\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "*types.MpoolConfig", + "summary": "", + "schema": { + "examples": [ + { + "PriorityAddrs": [ + "f01234" + ], + "SizeLimitHigh": 123, + "SizeLimitLow": 123, + "ReplaceByFeeRatio": 1.23, + "PruneCooldown": 60000000000, + "GasLimitOverestimation": 12.3 + } + ], + "additionalProperties": false, + "properties": { + "GasLimitOverestimation": { + "type": "number" + }, + "PriorityAddrs": { + "items": { + "additionalProperties": false, + "type": "object" + }, + "type": "array" + }, + "PruneCooldown": { + "title": "number", + "type": "number" + }, + "ReplaceByFeeRatio": { + "title": "number", + "type": "number" + }, + "SizeLimitHigh": { + "title": "number", + "type": "number" + }, + "SizeLimitLow": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "Null", + "description": "Null", + "schema": { + "type": [ + "null" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2806" + } + }, + { + "name": "Filecoin.MsigAddApprove", + "description": "```go\nfunc (s *FullNodeStruct) MsigAddApprove(p0 context.Context, p1 address.Address, p2 address.Address, p3 uint64, p4 address.Address, p5 address.Address, p6 bool) (*MessagePrototype, error) {\n\tif s.Internal.MsigAddApprove == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.MsigAddApprove(p0, p1, p2, p3, p4, p5, p6)\n}\n```", + "summary": "MsigAddApprove approves a previously proposed AddSigner message\nIt takes the following params: \u003cmultisig address\u003e, \u003csender address of the approve msg\u003e, \u003cproposed message ID\u003e,\n\u003cproposer address\u003e, \u003cnew signer\u003e, \u003cwhether the number of required signers should be increased\u003e\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p3", + "description": "uint64", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 42 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p4", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p5", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p6", + "description": "bool", + "summary": "", + "schema": { + "examples": [ + true + ], + "type": [ + "boolean" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*MessagePrototype", + "description": "*MessagePrototype", + "summary": "", + "schema": { + "examples": [ + { + "Message": { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + }, + "ValidNonce": true + } + ], + "additionalProperties": false, + "properties": { + "Message": { + "additionalProperties": false, + "properties": { + "From": { + "additionalProperties": false, + "type": "object" + }, + "GasFeeCap": { + "additionalProperties": false, + "type": "object" + }, + "GasLimit": { + "title": "number", + "type": "number" + }, + "GasPremium": { + "additionalProperties": false, + "type": "object" + }, + "Method": { + "title": "number", + "type": "number" + }, + "Nonce": { + "title": "number", + "type": "number" + }, + "Params": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "To": { + "additionalProperties": false, + "type": "object" + }, + "Value": { + "additionalProperties": false, + "type": "object" + }, + "Version": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "ValidNonce": { + "type": "boolean" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2828" + } + }, + { + "name": "Filecoin.MsigAddCancel", + "description": "```go\nfunc (s *FullNodeStruct) MsigAddCancel(p0 context.Context, p1 address.Address, p2 address.Address, p3 uint64, p4 address.Address, p5 bool) (*MessagePrototype, error) {\n\tif s.Internal.MsigAddCancel == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.MsigAddCancel(p0, p1, p2, p3, p4, p5)\n}\n```", + "summary": "MsigAddCancel cancels a previously proposed AddSigner message\nIt takes the following params: \u003cmultisig address\u003e, \u003csender address of the cancel msg\u003e, \u003cproposed message ID\u003e,\n\u003cnew signer\u003e, \u003cwhether the number of required signers should be increased\u003e\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p3", + "description": "uint64", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 42 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p4", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p5", + "description": "bool", + "summary": "", + "schema": { + "examples": [ + true + ], + "type": [ + "boolean" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*MessagePrototype", + "description": "*MessagePrototype", + "summary": "", + "schema": { + "examples": [ + { + "Message": { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + }, + "ValidNonce": true + } + ], + "additionalProperties": false, + "properties": { + "Message": { + "additionalProperties": false, + "properties": { + "From": { + "additionalProperties": false, + "type": "object" + }, + "GasFeeCap": { + "additionalProperties": false, + "type": "object" + }, + "GasLimit": { + "title": "number", + "type": "number" + }, + "GasPremium": { + "additionalProperties": false, + "type": "object" + }, + "Method": { + "title": "number", + "type": "number" + }, + "Nonce": { + "title": "number", + "type": "number" + }, + "Params": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "To": { + "additionalProperties": false, + "type": "object" + }, + "Value": { + "additionalProperties": false, + "type": "object" + }, + "Version": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "ValidNonce": { + "type": "boolean" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2839" + } + }, + { + "name": "Filecoin.MsigAddPropose", + "description": "```go\nfunc (s *FullNodeStruct) MsigAddPropose(p0 context.Context, p1 address.Address, p2 address.Address, p3 address.Address, p4 bool) (*MessagePrototype, error) {\n\tif s.Internal.MsigAddPropose == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.MsigAddPropose(p0, p1, p2, p3, p4)\n}\n```", + "summary": "MsigAddPropose proposes adding a signer in the multisig\nIt takes the following params: \u003cmultisig address\u003e, \u003csender address of the propose msg\u003e,\n\u003cnew signer\u003e, \u003cwhether the number of required signers should be increased\u003e\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p3", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p4", + "description": "bool", + "summary": "", + "schema": { + "examples": [ + true + ], + "type": [ + "boolean" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*MessagePrototype", + "description": "*MessagePrototype", + "summary": "", + "schema": { + "examples": [ + { + "Message": { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + }, + "ValidNonce": true + } + ], + "additionalProperties": false, + "properties": { + "Message": { + "additionalProperties": false, + "properties": { + "From": { + "additionalProperties": false, + "type": "object" + }, + "GasFeeCap": { + "additionalProperties": false, + "type": "object" + }, + "GasLimit": { + "title": "number", + "type": "number" + }, + "GasPremium": { + "additionalProperties": false, + "type": "object" + }, + "Method": { + "title": "number", + "type": "number" + }, + "Nonce": { + "title": "number", + "type": "number" + }, + "Params": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "To": { + "additionalProperties": false, + "type": "object" + }, + "Value": { + "additionalProperties": false, + "type": "object" + }, + "Version": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "ValidNonce": { + "type": "boolean" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2850" + } + }, + { + "name": "Filecoin.MsigApprove", + "description": "```go\nfunc (s *FullNodeStruct) MsigApprove(p0 context.Context, p1 address.Address, p2 uint64, p3 address.Address) (*MessagePrototype, error) {\n\tif s.Internal.MsigApprove == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.MsigApprove(p0, p1, p2, p3)\n}\n```", + "summary": "MsigApprove approves a previously-proposed multisig message by transaction ID\nIt takes the following params: \u003cmultisig address\u003e, \u003cproposed transaction ID\u003e \u003csigner address\u003e\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "uint64", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 42 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p3", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*MessagePrototype", + "description": "*MessagePrototype", + "summary": "", + "schema": { + "examples": [ + { + "Message": { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + }, + "ValidNonce": true + } + ], + "additionalProperties": false, + "properties": { + "Message": { + "additionalProperties": false, + "properties": { + "From": { + "additionalProperties": false, + "type": "object" + }, + "GasFeeCap": { + "additionalProperties": false, + "type": "object" + }, + "GasLimit": { + "title": "number", + "type": "number" + }, + "GasPremium": { + "additionalProperties": false, + "type": "object" + }, + "Method": { + "title": "number", + "type": "number" + }, + "Nonce": { + "title": "number", + "type": "number" + }, + "Params": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "To": { + "additionalProperties": false, + "type": "object" + }, + "Value": { + "additionalProperties": false, + "type": "object" + }, + "Version": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "ValidNonce": { + "type": "boolean" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2861" + } + }, + { + "name": "Filecoin.MsigApproveTxnHash", + "description": "```go\nfunc (s *FullNodeStruct) MsigApproveTxnHash(p0 context.Context, p1 address.Address, p2 uint64, p3 address.Address, p4 address.Address, p5 types.BigInt, p6 address.Address, p7 uint64, p8 []byte) (*MessagePrototype, error) {\n\tif s.Internal.MsigApproveTxnHash == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.MsigApproveTxnHash(p0, p1, p2, p3, p4, p5, p6, p7, p8)\n}\n```", + "summary": "MsigApproveTxnHash approves a previously-proposed multisig message, specified\nusing both transaction ID and a hash of the parameters used in the\nproposal. This method of approval can be used to ensure you only approve\nexactly the transaction you think you are.\nIt takes the following params: \u003cmultisig address\u003e, \u003cproposed message ID\u003e, \u003cproposer address\u003e, \u003crecipient address\u003e, \u003cvalue to transfer\u003e,\n\u003csender address of the approve msg\u003e, \u003cmethod to call in the proposed message\u003e, \u003cparams to include in the proposed message\u003e\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "uint64", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 42 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p3", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p4", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p5", + "description": "types.BigInt", + "summary": "", + "schema": { + "examples": [ + "0" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p6", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p7", + "description": "uint64", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 42 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p8", + "description": "[]byte", + "summary": "", + "schema": { + "examples": [ + "Ynl0ZSBhcnJheQ==" + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*MessagePrototype", + "description": "*MessagePrototype", + "summary": "", + "schema": { + "examples": [ + { + "Message": { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + }, + "ValidNonce": true + } + ], + "additionalProperties": false, + "properties": { + "Message": { + "additionalProperties": false, + "properties": { + "From": { + "additionalProperties": false, + "type": "object" + }, + "GasFeeCap": { + "additionalProperties": false, + "type": "object" + }, + "GasLimit": { + "title": "number", + "type": "number" + }, + "GasPremium": { + "additionalProperties": false, + "type": "object" + }, + "Method": { + "title": "number", + "type": "number" + }, + "Nonce": { + "title": "number", + "type": "number" + }, + "Params": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "To": { + "additionalProperties": false, + "type": "object" + }, + "Value": { + "additionalProperties": false, + "type": "object" + }, + "Version": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "ValidNonce": { + "type": "boolean" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2872" + } + }, + { + "name": "Filecoin.MsigCancel", + "description": "```go\nfunc (s *FullNodeStruct) MsigCancel(p0 context.Context, p1 address.Address, p2 uint64, p3 address.Address) (*MessagePrototype, error) {\n\tif s.Internal.MsigCancel == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.MsigCancel(p0, p1, p2, p3)\n}\n```", + "summary": "MsigCancel cancels a previously-proposed multisig message\nIt takes the following params: \u003cmultisig address\u003e, \u003cproposed transaction ID\u003e \u003csigner address\u003e\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "uint64", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 42 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p3", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*MessagePrototype", + "description": "*MessagePrototype", + "summary": "", + "schema": { + "examples": [ + { + "Message": { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + }, + "ValidNonce": true + } + ], + "additionalProperties": false, + "properties": { + "Message": { + "additionalProperties": false, + "properties": { + "From": { + "additionalProperties": false, + "type": "object" + }, + "GasFeeCap": { + "additionalProperties": false, + "type": "object" + }, + "GasLimit": { + "title": "number", + "type": "number" + }, + "GasPremium": { + "additionalProperties": false, + "type": "object" + }, + "Method": { + "title": "number", + "type": "number" + }, + "Nonce": { + "title": "number", + "type": "number" + }, + "Params": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "To": { + "additionalProperties": false, + "type": "object" + }, + "Value": { + "additionalProperties": false, + "type": "object" + }, + "Version": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "ValidNonce": { + "type": "boolean" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2883" + } + }, + { + "name": "Filecoin.MsigCancelTxnHash", + "description": "```go\nfunc (s *FullNodeStruct) MsigCancelTxnHash(p0 context.Context, p1 address.Address, p2 uint64, p3 address.Address, p4 types.BigInt, p5 address.Address, p6 uint64, p7 []byte) (*MessagePrototype, error) {\n\tif s.Internal.MsigCancelTxnHash == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.MsigCancelTxnHash(p0, p1, p2, p3, p4, p5, p6, p7)\n}\n```", + "summary": "MsigCancel cancels a previously-proposed multisig message\nIt takes the following params: \u003cmultisig address\u003e, \u003cproposed transaction ID\u003e, \u003crecipient address\u003e, \u003cvalue to transfer\u003e,\n\u003csender address of the cancel msg\u003e, \u003cmethod to call in the proposed message\u003e, \u003cparams to include in the proposed message\u003e\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "uint64", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 42 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p3", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p4", + "description": "types.BigInt", + "summary": "", + "schema": { + "examples": [ + "0" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p5", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p6", + "description": "uint64", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 42 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p7", + "description": "[]byte", + "summary": "", + "schema": { + "examples": [ + "Ynl0ZSBhcnJheQ==" + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*MessagePrototype", + "description": "*MessagePrototype", + "summary": "", + "schema": { + "examples": [ + { + "Message": { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + }, + "ValidNonce": true + } + ], + "additionalProperties": false, + "properties": { + "Message": { + "additionalProperties": false, + "properties": { + "From": { + "additionalProperties": false, + "type": "object" + }, + "GasFeeCap": { + "additionalProperties": false, + "type": "object" + }, + "GasLimit": { + "title": "number", + "type": "number" + }, + "GasPremium": { + "additionalProperties": false, + "type": "object" + }, + "Method": { + "title": "number", + "type": "number" + }, + "Nonce": { + "title": "number", + "type": "number" + }, + "Params": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "To": { + "additionalProperties": false, + "type": "object" + }, + "Value": { + "additionalProperties": false, + "type": "object" + }, + "Version": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "ValidNonce": { + "type": "boolean" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2894" + } + }, + { + "name": "Filecoin.MsigCreate", + "description": "```go\nfunc (s *FullNodeStruct) MsigCreate(p0 context.Context, p1 uint64, p2 []address.Address, p3 abi.ChainEpoch, p4 types.BigInt, p5 address.Address, p6 types.BigInt) (*MessagePrototype, error) {\n\tif s.Internal.MsigCreate == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.MsigCreate(p0, p1, p2, p3, p4, p5, p6)\n}\n```", + "summary": "MsigCreate creates a multisig wallet\nIt takes the following params: \u003crequired number of senders\u003e, \u003capproving addresses\u003e, \u003cunlock duration\u003e\n\u003cinitial balance\u003e, \u003csender address of the create msg\u003e, \u003cgas price\u003e\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "uint64", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 42 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "[]address.Address", + "summary": "", + "schema": { + "examples": [ + [ + "f01234" + ] + ], + "items": [ + { + "additionalProperties": false, + "type": [ + "object" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p3", + "description": "abi.ChainEpoch", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 10101 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p4", + "description": "types.BigInt", + "summary": "", + "schema": { + "examples": [ + "0" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p5", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p6", + "description": "types.BigInt", + "summary": "", + "schema": { + "examples": [ + "0" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*MessagePrototype", + "description": "*MessagePrototype", + "summary": "", + "schema": { + "examples": [ + { + "Message": { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + }, + "ValidNonce": true + } + ], + "additionalProperties": false, + "properties": { + "Message": { + "additionalProperties": false, + "properties": { + "From": { + "additionalProperties": false, + "type": "object" + }, + "GasFeeCap": { + "additionalProperties": false, + "type": "object" + }, + "GasLimit": { + "title": "number", + "type": "number" + }, + "GasPremium": { + "additionalProperties": false, + "type": "object" + }, + "Method": { + "title": "number", + "type": "number" + }, + "Nonce": { + "title": "number", + "type": "number" + }, + "Params": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "To": { + "additionalProperties": false, + "type": "object" + }, + "Value": { + "additionalProperties": false, + "type": "object" + }, + "Version": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "ValidNonce": { + "type": "boolean" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2905" + } + }, + { + "name": "Filecoin.MsigGetAvailableBalance", + "description": "```go\nfunc (s *FullNodeStruct) MsigGetAvailableBalance(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (types.BigInt, error) {\n\tif s.Internal.MsigGetAvailableBalance == nil {\n\t\treturn *new(types.BigInt), ErrNotSupported\n\t}\n\treturn s.Internal.MsigGetAvailableBalance(p0, p1, p2)\n}\n```", + "summary": "MsigGetAvailableBalance returns the portion of a multisig's balance that can be withdrawn or spent\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "types.BigInt", + "description": "types.BigInt", + "summary": "", + "schema": { + "examples": [ + "0" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2916" + } + }, + { + "name": "Filecoin.MsigGetPending", + "description": "```go\nfunc (s *FullNodeStruct) MsigGetPending(p0 context.Context, p1 address.Address, p2 types.TipSetKey) ([]*MsigTransaction, error) {\n\tif s.Internal.MsigGetPending == nil {\n\t\treturn *new([]*MsigTransaction), ErrNotSupported\n\t}\n\treturn s.Internal.MsigGetPending(p0, p1, p2)\n}\n```", + "summary": "MsigGetPending returns pending transactions for the given multisig\nwallet. Once pending transactions are fully approved, they will no longer\nappear here.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "[]*MsigTransaction", + "description": "[]*MsigTransaction", + "summary": "", + "schema": { + "examples": [ + [ + { + "ID": 9, + "To": "f01234", + "Value": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "Approved": [ + "f01234" + ] + } + ] + ], + "items": [ + { + "additionalProperties": false, + "properties": { + "Approved": { + "items": { + "additionalProperties": false, + "type": "object" + }, + "type": "array" + }, + "ID": { + "title": "number", + "type": "number" + }, + "Method": { + "title": "number", + "type": "number" + }, + "Params": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "To": { + "additionalProperties": false, + "type": "object" + }, + "Value": { + "additionalProperties": false, + "type": "object" + } + }, + "type": [ + "object" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2927" + } + }, + { + "name": "Filecoin.MsigGetVested", + "description": "```go\nfunc (s *FullNodeStruct) MsigGetVested(p0 context.Context, p1 address.Address, p2 types.TipSetKey, p3 types.TipSetKey) (types.BigInt, error) {\n\tif s.Internal.MsigGetVested == nil {\n\t\treturn *new(types.BigInt), ErrNotSupported\n\t}\n\treturn s.Internal.MsigGetVested(p0, p1, p2, p3)\n}\n```", + "summary": "MsigGetVested returns the amount of FIL that vested in a multisig in a certain period.\nIt takes the following params: \u003cmultisig address\u003e, \u003cstart epoch\u003e, \u003cend epoch\u003e\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p3", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "types.BigInt", + "description": "types.BigInt", + "summary": "", + "schema": { + "examples": [ + "0" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2938" + } + }, + { + "name": "Filecoin.MsigGetVestingSchedule", + "description": "```go\nfunc (s *FullNodeStruct) MsigGetVestingSchedule(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (MsigVesting, error) {\n\tif s.Internal.MsigGetVestingSchedule == nil {\n\t\treturn *new(MsigVesting), ErrNotSupported\n\t}\n\treturn s.Internal.MsigGetVestingSchedule(p0, p1, p2)\n}\n```", + "summary": "MsigGetVestingSchedule returns the vesting details of a given multisig.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "MsigVesting", + "description": "MsigVesting", + "summary": "", + "schema": { + "examples": [ + { + "InitialBalance": "0", + "StartEpoch": 10101, + "UnlockDuration": 10101 + } + ], + "additionalProperties": false, + "properties": { + "InitialBalance": { + "additionalProperties": false, + "type": "object" + }, + "StartEpoch": { + "title": "number", + "type": "number" + }, + "UnlockDuration": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2949" + } + }, + { + "name": "Filecoin.MsigPropose", + "description": "```go\nfunc (s *FullNodeStruct) MsigPropose(p0 context.Context, p1 address.Address, p2 address.Address, p3 types.BigInt, p4 address.Address, p5 uint64, p6 []byte) (*MessagePrototype, error) {\n\tif s.Internal.MsigPropose == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.MsigPropose(p0, p1, p2, p3, p4, p5, p6)\n}\n```", + "summary": "MsigPropose proposes a multisig message\nIt takes the following params: \u003cmultisig address\u003e, \u003crecipient address\u003e, \u003cvalue to transfer\u003e,\n\u003csender address of the propose msg\u003e, \u003cmethod to call in the proposed message\u003e, \u003cparams to include in the proposed message\u003e\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p3", + "description": "types.BigInt", + "summary": "", + "schema": { + "examples": [ + "0" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p4", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p5", + "description": "uint64", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 42 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p6", + "description": "[]byte", + "summary": "", + "schema": { + "examples": [ + "Ynl0ZSBhcnJheQ==" + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*MessagePrototype", + "description": "*MessagePrototype", + "summary": "", + "schema": { + "examples": [ + { + "Message": { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + }, + "ValidNonce": true + } + ], + "additionalProperties": false, + "properties": { + "Message": { + "additionalProperties": false, + "properties": { + "From": { + "additionalProperties": false, + "type": "object" + }, + "GasFeeCap": { + "additionalProperties": false, + "type": "object" + }, + "GasLimit": { + "title": "number", + "type": "number" + }, + "GasPremium": { + "additionalProperties": false, + "type": "object" + }, + "Method": { + "title": "number", + "type": "number" + }, + "Nonce": { + "title": "number", + "type": "number" + }, + "Params": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "To": { + "additionalProperties": false, + "type": "object" + }, + "Value": { + "additionalProperties": false, + "type": "object" + }, + "Version": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "ValidNonce": { + "type": "boolean" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2960" + } + }, + { + "name": "Filecoin.MsigRemoveSigner", + "description": "```go\nfunc (s *FullNodeStruct) MsigRemoveSigner(p0 context.Context, p1 address.Address, p2 address.Address, p3 address.Address, p4 bool) (*MessagePrototype, error) {\n\tif s.Internal.MsigRemoveSigner == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.MsigRemoveSigner(p0, p1, p2, p3, p4)\n}\n```", + "summary": "MsigRemoveSigner proposes the removal of a signer from the multisig.\nIt accepts the multisig to make the change on, the proposer address to\nsend the message from, the address to be removed, and a boolean\nindicating whether or not the signing threshold should be lowered by one\nalong with the address removal.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p3", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p4", + "description": "bool", + "summary": "", + "schema": { + "examples": [ + true + ], + "type": [ + "boolean" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*MessagePrototype", + "description": "*MessagePrototype", + "summary": "", + "schema": { + "examples": [ + { + "Message": { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + }, + "ValidNonce": true + } + ], + "additionalProperties": false, + "properties": { + "Message": { + "additionalProperties": false, + "properties": { + "From": { + "additionalProperties": false, + "type": "object" + }, + "GasFeeCap": { + "additionalProperties": false, + "type": "object" + }, + "GasLimit": { + "title": "number", + "type": "number" + }, + "GasPremium": { + "additionalProperties": false, + "type": "object" + }, + "Method": { + "title": "number", + "type": "number" + }, + "Nonce": { + "title": "number", + "type": "number" + }, + "Params": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "To": { + "additionalProperties": false, + "type": "object" + }, + "Value": { + "additionalProperties": false, + "type": "object" + }, + "Version": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "ValidNonce": { + "type": "boolean" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2971" + } + }, + { + "name": "Filecoin.MsigSwapApprove", + "description": "```go\nfunc (s *FullNodeStruct) MsigSwapApprove(p0 context.Context, p1 address.Address, p2 address.Address, p3 uint64, p4 address.Address, p5 address.Address, p6 address.Address) (*MessagePrototype, error) {\n\tif s.Internal.MsigSwapApprove == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.MsigSwapApprove(p0, p1, p2, p3, p4, p5, p6)\n}\n```", + "summary": "MsigSwapApprove approves a previously proposed SwapSigner\nIt takes the following params: \u003cmultisig address\u003e, \u003csender address of the approve msg\u003e, \u003cproposed message ID\u003e,\n\u003cproposer address\u003e, \u003cold signer\u003e, \u003cnew signer\u003e\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p3", + "description": "uint64", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 42 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p4", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p5", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p6", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*MessagePrototype", + "description": "*MessagePrototype", + "summary": "", + "schema": { + "examples": [ + { + "Message": { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + }, + "ValidNonce": true + } + ], + "additionalProperties": false, + "properties": { + "Message": { + "additionalProperties": false, + "properties": { + "From": { + "additionalProperties": false, + "type": "object" + }, + "GasFeeCap": { + "additionalProperties": false, + "type": "object" + }, + "GasLimit": { + "title": "number", + "type": "number" + }, + "GasPremium": { + "additionalProperties": false, + "type": "object" + }, + "Method": { + "title": "number", + "type": "number" + }, + "Nonce": { + "title": "number", + "type": "number" + }, + "Params": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "To": { + "additionalProperties": false, + "type": "object" + }, + "Value": { + "additionalProperties": false, + "type": "object" + }, + "Version": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "ValidNonce": { + "type": "boolean" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2982" + } + }, + { + "name": "Filecoin.MsigSwapCancel", + "description": "```go\nfunc (s *FullNodeStruct) MsigSwapCancel(p0 context.Context, p1 address.Address, p2 address.Address, p3 uint64, p4 address.Address, p5 address.Address) (*MessagePrototype, error) {\n\tif s.Internal.MsigSwapCancel == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.MsigSwapCancel(p0, p1, p2, p3, p4, p5)\n}\n```", + "summary": "MsigSwapCancel cancels a previously proposed SwapSigner message\nIt takes the following params: \u003cmultisig address\u003e, \u003csender address of the cancel msg\u003e, \u003cproposed message ID\u003e,\n\u003cold signer\u003e, \u003cnew signer\u003e\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p3", + "description": "uint64", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 42 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p4", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p5", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*MessagePrototype", + "description": "*MessagePrototype", + "summary": "", + "schema": { + "examples": [ + { + "Message": { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + }, + "ValidNonce": true + } + ], + "additionalProperties": false, + "properties": { + "Message": { + "additionalProperties": false, + "properties": { + "From": { + "additionalProperties": false, + "type": "object" + }, + "GasFeeCap": { + "additionalProperties": false, + "type": "object" + }, + "GasLimit": { + "title": "number", + "type": "number" + }, + "GasPremium": { + "additionalProperties": false, + "type": "object" + }, + "Method": { + "title": "number", + "type": "number" + }, + "Nonce": { + "title": "number", + "type": "number" + }, + "Params": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "To": { + "additionalProperties": false, + "type": "object" + }, + "Value": { + "additionalProperties": false, + "type": "object" + }, + "Version": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "ValidNonce": { + "type": "boolean" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2993" + } + }, + { + "name": "Filecoin.MsigSwapPropose", + "description": "```go\nfunc (s *FullNodeStruct) MsigSwapPropose(p0 context.Context, p1 address.Address, p2 address.Address, p3 address.Address, p4 address.Address) (*MessagePrototype, error) {\n\tif s.Internal.MsigSwapPropose == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.MsigSwapPropose(p0, p1, p2, p3, p4)\n}\n```", + "summary": "MsigSwapPropose proposes swapping 2 signers in the multisig\nIt takes the following params: \u003cmultisig address\u003e, \u003csender address of the propose msg\u003e,\n\u003cold signer\u003e, \u003cnew signer\u003e\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p3", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p4", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*MessagePrototype", + "description": "*MessagePrototype", + "summary": "", + "schema": { + "examples": [ + { + "Message": { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + }, + "ValidNonce": true + } + ], + "additionalProperties": false, + "properties": { + "Message": { + "additionalProperties": false, + "properties": { + "From": { + "additionalProperties": false, + "type": "object" + }, + "GasFeeCap": { + "additionalProperties": false, + "type": "object" + }, + "GasLimit": { + "title": "number", + "type": "number" + }, + "GasPremium": { + "additionalProperties": false, + "type": "object" + }, + "Method": { + "title": "number", + "type": "number" + }, + "Nonce": { + "title": "number", + "type": "number" + }, + "Params": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "To": { + "additionalProperties": false, + "type": "object" + }, + "Value": { + "additionalProperties": false, + "type": "object" + }, + "Version": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "ValidNonce": { + "type": "boolean" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3004" + } + }, + { + "name": "Filecoin.NetListening", + "description": "```go\nfunc (s *FullNodeStruct) NetListening(p0 context.Context) (bool, error) {\n\tif s.Internal.NetListening == nil {\n\t\treturn false, ErrNotSupported\n\t}\n\treturn s.Internal.NetListening(p0)\n}\n```", + "summary": "", + "paramStructure": "by-position", + "params": [], + "result": { + "name": "bool", + "description": "bool", + "summary": "", + "schema": { + "examples": [ + true + ], + "type": [ + "boolean" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3015" + } + }, + { + "name": "Filecoin.NetVersion", + "description": "```go\nfunc (s *FullNodeStruct) NetVersion(p0 context.Context) (string, error) {\n\tif s.Internal.NetVersion == nil {\n\t\treturn \"\", ErrNotSupported\n\t}\n\treturn s.Internal.NetVersion(p0)\n}\n```", + "summary": "", + "paramStructure": "by-position", + "params": [], + "result": { + "name": "string", + "description": "string", + "summary": "", + "schema": { + "examples": [ + "string value" + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3026" + } + }, + { + "name": "Filecoin.NodeStatus", + "description": "```go\nfunc (s *FullNodeStruct) NodeStatus(p0 context.Context, p1 bool) (NodeStatus, error) {\n\tif s.Internal.NodeStatus == nil {\n\t\treturn *new(NodeStatus), ErrNotSupported\n\t}\n\treturn s.Internal.NodeStatus(p0, p1)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "bool", + "summary": "", + "schema": { + "examples": [ + true + ], + "type": [ + "boolean" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "NodeStatus", + "description": "NodeStatus", + "summary": "", + "schema": { + "examples": [ + { + "SyncStatus": { + "Epoch": 42, + "Behind": 42 + }, + "PeerStatus": { + "PeersToPublishMsgs": 123, + "PeersToPublishBlocks": 123 + }, + "ChainStatus": { + "BlocksPerTipsetLast100": 12.3, + "BlocksPerTipsetLastFinality": 12.3 + } + } + ], + "additionalProperties": false, + "properties": { + "ChainStatus": { + "additionalProperties": false, + "properties": { + "BlocksPerTipsetLast100": { + "type": "number" + }, + "BlocksPerTipsetLastFinality": { + "type": "number" + } + }, + "type": "object" + }, + "PeerStatus": { + "additionalProperties": false, + "properties": { + "PeersToPublishBlocks": { + "title": "number", + "type": "number" + }, + "PeersToPublishMsgs": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "SyncStatus": { + "additionalProperties": false, + "properties": { + "Behind": { + "title": "number", + "type": "number" + }, + "Epoch": { + "title": "number", + "type": "number" + } + }, + "type": "object" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3037" + } + }, + { + "name": "Filecoin.PaychAllocateLane", + "description": "```go\nfunc (s *FullNodeStruct) PaychAllocateLane(p0 context.Context, p1 address.Address) (uint64, error) {\n\tif s.Internal.PaychAllocateLane == nil {\n\t\treturn 0, ErrNotSupported\n\t}\n\treturn s.Internal.PaychAllocateLane(p0, p1)\n}\n```", + "summary": "", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "uint64", + "description": "uint64", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 42 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3048" + } + }, + { + "name": "Filecoin.PaychAvailableFunds", + "description": "```go\nfunc (s *FullNodeStruct) PaychAvailableFunds(p0 context.Context, p1 address.Address) (*ChannelAvailableFunds, error) {\n\tif s.Internal.PaychAvailableFunds == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.PaychAvailableFunds(p0, p1)\n}\n```", + "summary": "", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*ChannelAvailableFunds", + "description": "*ChannelAvailableFunds", + "summary": "", + "schema": { + "examples": [ + { + "Channel": "f01234", + "From": "f01234", + "To": "f01234", + "ConfirmedAmt": "0", + "PendingAmt": "0", + "NonReservedAmt": "0", + "PendingAvailableAmt": "0", + "PendingWaitSentinel": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "QueuedAmt": "0", + "VoucherReedeemedAmt": "0" + } + ], + "additionalProperties": false, + "properties": { + "Channel": { + "additionalProperties": false, + "type": "object" + }, + "ConfirmedAmt": { + "additionalProperties": false, + "type": "object" + }, + "From": { + "additionalProperties": false, + "type": "object" + }, + "NonReservedAmt": { + "additionalProperties": false, + "type": "object" + }, + "PendingAmt": { + "additionalProperties": false, + "type": "object" + }, + "PendingAvailableAmt": { + "additionalProperties": false, + "type": "object" + }, + "PendingWaitSentinel": { + "title": "Content Identifier", + "type": "string" + }, + "QueuedAmt": { + "additionalProperties": false, + "type": "object" + }, + "To": { + "additionalProperties": false, + "type": "object" + }, + "VoucherReedeemedAmt": { + "additionalProperties": false, + "type": "object" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3059" + } + }, + { + "name": "Filecoin.PaychAvailableFundsByFromTo", + "description": "```go\nfunc (s *FullNodeStruct) PaychAvailableFundsByFromTo(p0 context.Context, p1 address.Address, p2 address.Address) (*ChannelAvailableFunds, error) {\n\tif s.Internal.PaychAvailableFundsByFromTo == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.PaychAvailableFundsByFromTo(p0, p1, p2)\n}\n```", + "summary": "", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*ChannelAvailableFunds", + "description": "*ChannelAvailableFunds", + "summary": "", + "schema": { + "examples": [ + { + "Channel": "f01234", + "From": "f01234", + "To": "f01234", + "ConfirmedAmt": "0", + "PendingAmt": "0", + "NonReservedAmt": "0", + "PendingAvailableAmt": "0", + "PendingWaitSentinel": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "QueuedAmt": "0", + "VoucherReedeemedAmt": "0" + } + ], + "additionalProperties": false, + "properties": { + "Channel": { + "additionalProperties": false, + "type": "object" + }, + "ConfirmedAmt": { + "additionalProperties": false, + "type": "object" + }, + "From": { + "additionalProperties": false, + "type": "object" + }, + "NonReservedAmt": { + "additionalProperties": false, + "type": "object" + }, + "PendingAmt": { + "additionalProperties": false, + "type": "object" + }, + "PendingAvailableAmt": { + "additionalProperties": false, + "type": "object" + }, + "PendingWaitSentinel": { + "title": "Content Identifier", + "type": "string" + }, + "QueuedAmt": { + "additionalProperties": false, + "type": "object" + }, + "To": { + "additionalProperties": false, + "type": "object" + }, + "VoucherReedeemedAmt": { + "additionalProperties": false, + "type": "object" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3070" + } + }, + { + "name": "Filecoin.PaychCollect", + "description": "```go\nfunc (s *FullNodeStruct) PaychCollect(p0 context.Context, p1 address.Address) (cid.Cid, error) {\n\tif s.Internal.PaychCollect == nil {\n\t\treturn *new(cid.Cid), ErrNotSupported\n\t}\n\treturn s.Internal.PaychCollect(p0, p1)\n}\n```", + "summary": "", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "cid.Cid", + "description": "cid.Cid", + "summary": "", + "schema": { + "title": "Content Identifier", + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "examples": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3081" + } + }, + { + "name": "Filecoin.PaychFund", + "description": "```go\nfunc (s *FullNodeStruct) PaychFund(p0 context.Context, p1 address.Address, p2 address.Address, p3 types.BigInt) (*ChannelInfo, error) {\n\tif s.Internal.PaychFund == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.PaychFund(p0, p1, p2, p3)\n}\n```", + "summary": "PaychFund gets or creates a payment channel between address pair.\nThe specified amount will be added to the channel through on-chain send for future use\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p3", + "description": "types.BigInt", + "summary": "", + "schema": { + "examples": [ + "0" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*ChannelInfo", + "description": "*ChannelInfo", + "summary": "", + "schema": { + "examples": [ + { + "Channel": "f01234", + "WaitSentinel": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + } + ], + "additionalProperties": false, + "properties": { + "Channel": { + "additionalProperties": false, + "type": "object" + }, + "WaitSentinel": { + "title": "Content Identifier", + "type": "string" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3092" + } + }, + { + "name": "Filecoin.PaychGet", + "description": "```go\nfunc (s *FullNodeStruct) PaychGet(p0 context.Context, p1 address.Address, p2 address.Address, p3 types.BigInt, p4 PaychGetOpts) (*ChannelInfo, error) {\n\tif s.Internal.PaychGet == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.PaychGet(p0, p1, p2, p3, p4)\n}\n```", + "summary": "PaychGet gets or creates a payment channel between address pair\n The specified amount will be reserved for use. If there aren't enough non-reserved funds\n available, funds will be added through an on-chain message.\n - When opts.OffChain is true, this call will not cause any messages to be sent to the chain (no automatic\n channel creation/funds adding). If the operation can't be performed without sending a message an error will be\n returned. Note that even when this option is specified, this call can be blocked by previous operations on the\n channel waiting for on-chain operations.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p3", + "description": "types.BigInt", + "summary": "", + "schema": { + "examples": [ + "0" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p4", + "description": "PaychGetOpts", + "summary": "", + "schema": { + "examples": [ + { + "OffChain": true + } + ], + "additionalProperties": false, + "properties": { + "OffChain": { + "type": "boolean" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*ChannelInfo", + "description": "*ChannelInfo", + "summary": "", + "schema": { + "examples": [ + { + "Channel": "f01234", + "WaitSentinel": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + } + ], + "additionalProperties": false, + "properties": { + "Channel": { + "additionalProperties": false, + "type": "object" + }, + "WaitSentinel": { + "title": "Content Identifier", + "type": "string" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3103" + } + }, + { + "name": "Filecoin.PaychGetWaitReady", + "description": "```go\nfunc (s *FullNodeStruct) PaychGetWaitReady(p0 context.Context, p1 cid.Cid) (address.Address, error) {\n\tif s.Internal.PaychGetWaitReady == nil {\n\t\treturn *new(address.Address), ErrNotSupported\n\t}\n\treturn s.Internal.PaychGetWaitReady(p0, p1)\n}\n```", + "summary": "", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "cid.Cid", + "summary": "", + "schema": { + "title": "Content Identifier", + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "examples": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "address.Address", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3114" + } + }, + { + "name": "Filecoin.PaychList", + "description": "```go\nfunc (s *FullNodeStruct) PaychList(p0 context.Context) ([]address.Address, error) {\n\tif s.Internal.PaychList == nil {\n\t\treturn *new([]address.Address), ErrNotSupported\n\t}\n\treturn s.Internal.PaychList(p0)\n}\n```", + "summary": "", + "paramStructure": "by-position", + "params": [], + "result": { + "name": "[]address.Address", + "description": "[]address.Address", + "summary": "", + "schema": { + "examples": [ + [ + "f01234" + ] + ], + "items": [ + { + "additionalProperties": false, + "type": [ + "object" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3125" + } + }, + { + "name": "Filecoin.PaychNewPayment", + "description": "```go\nfunc (s *FullNodeStruct) PaychNewPayment(p0 context.Context, p1 address.Address, p2 address.Address, p3 []VoucherSpec) (*PaymentInfo, error) {\n\tif s.Internal.PaychNewPayment == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.PaychNewPayment(p0, p1, p2, p3)\n}\n```", + "summary": "", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p3", + "description": "[]VoucherSpec", + "summary": "", + "schema": { + "examples": [ + [ + { + "Amount": "0", + "TimeLockMin": 10101, + "TimeLockMax": 10101, + "MinSettle": 10101, + "Extra": { + "Actor": "f01234", + "Method": 1, + "Data": "Ynl0ZSBhcnJheQ==" + } + } + ] + ], + "items": [ + { + "additionalProperties": false, + "properties": { + "Amount": { + "additionalProperties": false, + "type": "object" + }, + "Extra": { + "additionalProperties": false, + "properties": { + "Actor": { + "additionalProperties": false, + "type": "object" + }, + "Data": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "Method": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "MinSettle": { + "title": "number", + "type": "number" + }, + "TimeLockMax": { + "title": "number", + "type": "number" + }, + "TimeLockMin": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*PaymentInfo", + "description": "*PaymentInfo", + "summary": "", + "schema": { + "examples": [ + { + "Channel": "f01234", + "WaitSentinel": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Vouchers": [ + { + "ChannelAddr": "f01234", + "TimeLockMin": 10101, + "TimeLockMax": 10101, + "SecretHash": "Ynl0ZSBhcnJheQ==", + "Extra": { + "Actor": "f01234", + "Method": 1, + "Data": "Ynl0ZSBhcnJheQ==" + }, + "Lane": 42, + "Nonce": 42, + "Amount": "0", + "MinSettleHeight": 10101, + "Merges": [ + { + "Lane": 42, + "Nonce": 42 + } + ], + "Signature": { + "Type": 2, + "Data": "Ynl0ZSBhcnJheQ==" + } + } + ] + } + ], + "additionalProperties": false, + "properties": { + "Channel": { + "additionalProperties": false, + "type": "object" + }, + "Vouchers": { + "items": { + "additionalProperties": false, + "properties": { + "Amount": { + "additionalProperties": false, + "type": "object" + }, + "ChannelAddr": { + "additionalProperties": false, + "type": "object" + }, + "Extra": { + "additionalProperties": false, + "properties": { + "Actor": { + "additionalProperties": false, + "type": "object" + }, + "Data": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "Method": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "Lane": { + "title": "number", + "type": "number" + }, + "Merges": { + "items": { + "additionalProperties": false, + "properties": { + "Lane": { + "title": "number", + "type": "number" + }, + "Nonce": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "type": "array" + }, + "MinSettleHeight": { + "title": "number", + "type": "number" + }, + "Nonce": { + "title": "number", + "type": "number" + }, + "SecretHash": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "Signature": { + "additionalProperties": false, + "properties": { + "Data": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "Type": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "TimeLockMax": { + "title": "number", + "type": "number" + }, + "TimeLockMin": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "type": "array" + }, + "WaitSentinel": { + "title": "Content Identifier", + "type": "string" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3136" + } + }, + { + "name": "Filecoin.PaychSettle", + "description": "```go\nfunc (s *FullNodeStruct) PaychSettle(p0 context.Context, p1 address.Address) (cid.Cid, error) {\n\tif s.Internal.PaychSettle == nil {\n\t\treturn *new(cid.Cid), ErrNotSupported\n\t}\n\treturn s.Internal.PaychSettle(p0, p1)\n}\n```", + "summary": "", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "cid.Cid", + "description": "cid.Cid", + "summary": "", + "schema": { + "title": "Content Identifier", + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "examples": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3147" + } + }, + { + "name": "Filecoin.PaychStatus", + "description": "```go\nfunc (s *FullNodeStruct) PaychStatus(p0 context.Context, p1 address.Address) (*PaychStatus, error) {\n\tif s.Internal.PaychStatus == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.PaychStatus(p0, p1)\n}\n```", + "summary": "", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*PaychStatus", + "description": "*PaychStatus", + "summary": "", + "schema": { + "examples": [ + { + "ControlAddr": "f01234", + "Direction": 1 + } + ], + "additionalProperties": false, + "properties": { + "ControlAddr": { + "additionalProperties": false, + "type": "object" + }, + "Direction": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3158" + } + }, + { + "name": "Filecoin.PaychVoucherAdd", + "description": "```go\nfunc (s *FullNodeStruct) PaychVoucherAdd(p0 context.Context, p1 address.Address, p2 *paych.SignedVoucher, p3 []byte, p4 types.BigInt) (types.BigInt, error) {\n\tif s.Internal.PaychVoucherAdd == nil {\n\t\treturn *new(types.BigInt), ErrNotSupported\n\t}\n\treturn s.Internal.PaychVoucherAdd(p0, p1, p2, p3, p4)\n}\n```", + "summary": "", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "*paych.SignedVoucher", + "summary": "", + "schema": { + "examples": [ + { + "ChannelAddr": "f01234", + "TimeLockMin": 10101, + "TimeLockMax": 10101, + "SecretHash": "Ynl0ZSBhcnJheQ==", + "Extra": { + "Actor": "f01234", + "Method": 1, + "Data": "Ynl0ZSBhcnJheQ==" + }, + "Lane": 42, + "Nonce": 42, + "Amount": "0", + "MinSettleHeight": 10101, + "Merges": [ + { + "Lane": 42, + "Nonce": 42 + } + ], + "Signature": { + "Type": 2, + "Data": "Ynl0ZSBhcnJheQ==" + } + } + ], + "additionalProperties": false, + "properties": { + "Amount": { + "additionalProperties": false, + "type": "object" + }, + "ChannelAddr": { + "additionalProperties": false, + "type": "object" + }, + "Extra": { + "additionalProperties": false, + "properties": { + "Actor": { + "additionalProperties": false, + "type": "object" + }, + "Data": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "Method": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "Lane": { + "title": "number", + "type": "number" + }, + "Merges": { + "items": { + "additionalProperties": false, + "properties": { + "Lane": { + "title": "number", + "type": "number" + }, + "Nonce": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "type": "array" + }, + "MinSettleHeight": { + "title": "number", + "type": "number" + }, + "Nonce": { + "title": "number", + "type": "number" + }, + "SecretHash": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "Signature": { + "additionalProperties": false, + "properties": { + "Data": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "Type": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "TimeLockMax": { + "title": "number", + "type": "number" + }, + "TimeLockMin": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p3", + "description": "[]byte", + "summary": "", + "schema": { + "examples": [ + "Ynl0ZSBhcnJheQ==" + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p4", + "description": "types.BigInt", + "summary": "", + "schema": { + "examples": [ + "0" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "types.BigInt", + "description": "types.BigInt", + "summary": "", + "schema": { + "examples": [ + "0" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3169" + } + }, + { + "name": "Filecoin.PaychVoucherCheckSpendable", + "description": "```go\nfunc (s *FullNodeStruct) PaychVoucherCheckSpendable(p0 context.Context, p1 address.Address, p2 *paych.SignedVoucher, p3 []byte, p4 []byte) (bool, error) {\n\tif s.Internal.PaychVoucherCheckSpendable == nil {\n\t\treturn false, ErrNotSupported\n\t}\n\treturn s.Internal.PaychVoucherCheckSpendable(p0, p1, p2, p3, p4)\n}\n```", + "summary": "", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "*paych.SignedVoucher", + "summary": "", + "schema": { + "examples": [ + { + "ChannelAddr": "f01234", + "TimeLockMin": 10101, + "TimeLockMax": 10101, + "SecretHash": "Ynl0ZSBhcnJheQ==", + "Extra": { + "Actor": "f01234", + "Method": 1, + "Data": "Ynl0ZSBhcnJheQ==" + }, + "Lane": 42, + "Nonce": 42, + "Amount": "0", + "MinSettleHeight": 10101, + "Merges": [ + { + "Lane": 42, + "Nonce": 42 + } + ], + "Signature": { + "Type": 2, + "Data": "Ynl0ZSBhcnJheQ==" + } + } + ], + "additionalProperties": false, + "properties": { + "Amount": { + "additionalProperties": false, + "type": "object" + }, + "ChannelAddr": { + "additionalProperties": false, + "type": "object" + }, + "Extra": { + "additionalProperties": false, + "properties": { + "Actor": { + "additionalProperties": false, + "type": "object" + }, + "Data": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "Method": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "Lane": { + "title": "number", + "type": "number" + }, + "Merges": { + "items": { + "additionalProperties": false, + "properties": { + "Lane": { + "title": "number", + "type": "number" + }, + "Nonce": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "type": "array" + }, + "MinSettleHeight": { + "title": "number", + "type": "number" + }, + "Nonce": { + "title": "number", + "type": "number" + }, + "SecretHash": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "Signature": { + "additionalProperties": false, + "properties": { + "Data": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "Type": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "TimeLockMax": { + "title": "number", + "type": "number" + }, + "TimeLockMin": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p3", + "description": "[]byte", + "summary": "", + "schema": { + "examples": [ + "Ynl0ZSBhcnJheQ==" + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p4", + "description": "[]byte", + "summary": "", + "schema": { + "examples": [ + "Ynl0ZSBhcnJheQ==" + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "bool", + "description": "bool", + "summary": "", + "schema": { + "examples": [ + true + ], + "type": [ + "boolean" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3180" + } + }, + { + "name": "Filecoin.PaychVoucherCheckValid", + "description": "```go\nfunc (s *FullNodeStruct) PaychVoucherCheckValid(p0 context.Context, p1 address.Address, p2 *paych.SignedVoucher) error {\n\tif s.Internal.PaychVoucherCheckValid == nil {\n\t\treturn ErrNotSupported\n\t}\n\treturn s.Internal.PaychVoucherCheckValid(p0, p1, p2)\n}\n```", + "summary": "", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "*paych.SignedVoucher", + "summary": "", + "schema": { + "examples": [ + { + "ChannelAddr": "f01234", + "TimeLockMin": 10101, + "TimeLockMax": 10101, + "SecretHash": "Ynl0ZSBhcnJheQ==", + "Extra": { + "Actor": "f01234", + "Method": 1, + "Data": "Ynl0ZSBhcnJheQ==" + }, + "Lane": 42, + "Nonce": 42, + "Amount": "0", + "MinSettleHeight": 10101, + "Merges": [ + { + "Lane": 42, + "Nonce": 42 + } + ], + "Signature": { + "Type": 2, + "Data": "Ynl0ZSBhcnJheQ==" + } + } + ], + "additionalProperties": false, + "properties": { + "Amount": { + "additionalProperties": false, + "type": "object" + }, + "ChannelAddr": { + "additionalProperties": false, + "type": "object" + }, + "Extra": { + "additionalProperties": false, + "properties": { + "Actor": { + "additionalProperties": false, + "type": "object" + }, + "Data": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "Method": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "Lane": { + "title": "number", + "type": "number" + }, + "Merges": { + "items": { + "additionalProperties": false, + "properties": { + "Lane": { + "title": "number", + "type": "number" + }, + "Nonce": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "type": "array" + }, + "MinSettleHeight": { + "title": "number", + "type": "number" + }, + "Nonce": { + "title": "number", + "type": "number" + }, + "SecretHash": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "Signature": { + "additionalProperties": false, + "properties": { + "Data": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "Type": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "TimeLockMax": { + "title": "number", + "type": "number" + }, + "TimeLockMin": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "Null", + "description": "Null", + "schema": { + "type": [ + "null" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3191" + } + }, + { + "name": "Filecoin.PaychVoucherCreate", + "description": "```go\nfunc (s *FullNodeStruct) PaychVoucherCreate(p0 context.Context, p1 address.Address, p2 types.BigInt, p3 uint64) (*VoucherCreateResult, error) {\n\tif s.Internal.PaychVoucherCreate == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.PaychVoucherCreate(p0, p1, p2, p3)\n}\n```", + "summary": "", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "types.BigInt", + "summary": "", + "schema": { + "examples": [ + "0" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p3", + "description": "uint64", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 42 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*VoucherCreateResult", + "description": "*VoucherCreateResult", + "summary": "", + "schema": { + "examples": [ + { + "Voucher": { + "ChannelAddr": "f01234", + "TimeLockMin": 10101, + "TimeLockMax": 10101, + "SecretHash": "Ynl0ZSBhcnJheQ==", + "Extra": { + "Actor": "f01234", + "Method": 1, + "Data": "Ynl0ZSBhcnJheQ==" + }, + "Lane": 42, + "Nonce": 42, + "Amount": "0", + "MinSettleHeight": 10101, + "Merges": [ + { + "Lane": 42, + "Nonce": 42 + } + ], + "Signature": { + "Type": 2, + "Data": "Ynl0ZSBhcnJheQ==" + } + }, + "Shortfall": "0" + } + ], + "additionalProperties": false, + "properties": { + "Shortfall": { + "additionalProperties": false, + "type": "object" + }, + "Voucher": { + "additionalProperties": false, + "properties": { + "Amount": { + "additionalProperties": false, + "type": "object" + }, + "ChannelAddr": { + "additionalProperties": false, + "type": "object" + }, + "Extra": { + "additionalProperties": false, + "properties": { + "Actor": { + "additionalProperties": false, + "type": "object" + }, + "Data": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "Method": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "Lane": { + "title": "number", + "type": "number" + }, + "Merges": { + "items": { + "additionalProperties": false, + "properties": { + "Lane": { + "title": "number", + "type": "number" + }, + "Nonce": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "type": "array" + }, + "MinSettleHeight": { + "title": "number", + "type": "number" + }, + "Nonce": { + "title": "number", + "type": "number" + }, + "SecretHash": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "Signature": { + "additionalProperties": false, + "properties": { + "Data": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "Type": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "TimeLockMax": { + "title": "number", + "type": "number" + }, + "TimeLockMin": { + "title": "number", + "type": "number" + } + }, + "type": "object" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3202" + } + }, + { + "name": "Filecoin.PaychVoucherList", + "description": "```go\nfunc (s *FullNodeStruct) PaychVoucherList(p0 context.Context, p1 address.Address) ([]*paych.SignedVoucher, error) {\n\tif s.Internal.PaychVoucherList == nil {\n\t\treturn *new([]*paych.SignedVoucher), ErrNotSupported\n\t}\n\treturn s.Internal.PaychVoucherList(p0, p1)\n}\n```", + "summary": "", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "[]*paych.SignedVoucher", + "description": "[]*paych.SignedVoucher", + "summary": "", + "schema": { + "examples": [ + [ + { + "ChannelAddr": "f01234", + "TimeLockMin": 10101, + "TimeLockMax": 10101, + "SecretHash": "Ynl0ZSBhcnJheQ==", + "Extra": { + "Actor": "f01234", + "Method": 1, + "Data": "Ynl0ZSBhcnJheQ==" + }, + "Lane": 42, + "Nonce": 42, + "Amount": "0", + "MinSettleHeight": 10101, + "Merges": [ + { + "Lane": 42, + "Nonce": 42 + } + ], + "Signature": { + "Type": 2, + "Data": "Ynl0ZSBhcnJheQ==" + } + } + ] + ], + "items": [ + { + "additionalProperties": false, + "properties": { + "Amount": { + "additionalProperties": false, + "type": "object" + }, + "ChannelAddr": { + "additionalProperties": false, + "type": "object" + }, + "Extra": { + "additionalProperties": false, + "properties": { + "Actor": { + "additionalProperties": false, + "type": "object" + }, + "Data": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "Method": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "Lane": { + "title": "number", + "type": "number" + }, + "Merges": { + "items": { + "additionalProperties": false, + "properties": { + "Lane": { + "title": "number", + "type": "number" + }, + "Nonce": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "type": "array" + }, + "MinSettleHeight": { + "title": "number", + "type": "number" + }, + "Nonce": { + "title": "number", + "type": "number" + }, + "SecretHash": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "Signature": { + "additionalProperties": false, + "properties": { + "Data": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "Type": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "TimeLockMax": { + "title": "number", + "type": "number" + }, + "TimeLockMin": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3213" + } + }, + { + "name": "Filecoin.PaychVoucherSubmit", + "description": "```go\nfunc (s *FullNodeStruct) PaychVoucherSubmit(p0 context.Context, p1 address.Address, p2 *paych.SignedVoucher, p3 []byte, p4 []byte) (cid.Cid, error) {\n\tif s.Internal.PaychVoucherSubmit == nil {\n\t\treturn *new(cid.Cid), ErrNotSupported\n\t}\n\treturn s.Internal.PaychVoucherSubmit(p0, p1, p2, p3, p4)\n}\n```", + "summary": "", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "*paych.SignedVoucher", + "summary": "", + "schema": { + "examples": [ + { + "ChannelAddr": "f01234", + "TimeLockMin": 10101, + "TimeLockMax": 10101, + "SecretHash": "Ynl0ZSBhcnJheQ==", + "Extra": { + "Actor": "f01234", + "Method": 1, + "Data": "Ynl0ZSBhcnJheQ==" + }, + "Lane": 42, + "Nonce": 42, + "Amount": "0", + "MinSettleHeight": 10101, + "Merges": [ + { + "Lane": 42, + "Nonce": 42 + } + ], + "Signature": { + "Type": 2, + "Data": "Ynl0ZSBhcnJheQ==" + } + } + ], + "additionalProperties": false, + "properties": { + "Amount": { + "additionalProperties": false, + "type": "object" + }, + "ChannelAddr": { + "additionalProperties": false, + "type": "object" + }, + "Extra": { + "additionalProperties": false, + "properties": { + "Actor": { + "additionalProperties": false, + "type": "object" + }, + "Data": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "Method": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "Lane": { + "title": "number", + "type": "number" + }, + "Merges": { + "items": { + "additionalProperties": false, + "properties": { + "Lane": { + "title": "number", + "type": "number" + }, + "Nonce": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "type": "array" + }, + "MinSettleHeight": { + "title": "number", + "type": "number" + }, + "Nonce": { + "title": "number", + "type": "number" + }, + "SecretHash": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "Signature": { + "additionalProperties": false, + "properties": { + "Data": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "Type": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "TimeLockMax": { + "title": "number", + "type": "number" + }, + "TimeLockMin": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p3", + "description": "[]byte", + "summary": "", + "schema": { + "examples": [ + "Ynl0ZSBhcnJheQ==" + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p4", + "description": "[]byte", + "summary": "", + "schema": { + "examples": [ + "Ynl0ZSBhcnJheQ==" + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "cid.Cid", + "description": "cid.Cid", + "summary": "", + "schema": { + "title": "Content Identifier", + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "examples": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3224" + } + }, + { + "name": "Filecoin.StateAccountKey", + "description": "```go\nfunc (s *FullNodeStruct) StateAccountKey(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (address.Address, error) {\n\tif s.Internal.StateAccountKey == nil {\n\t\treturn *new(address.Address), ErrNotSupported\n\t}\n\treturn s.Internal.StateAccountKey(p0, p1, p2)\n}\n```", + "summary": "StateAccountKey returns the public key address of the given ID address for secp and bls accounts\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "address.Address", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3235" + } + }, + { + "name": "Filecoin.StateActorCodeCIDs", + "description": "```go\nfunc (s *FullNodeStruct) StateActorCodeCIDs(p0 context.Context, p1 abinetwork.Version) (map[string]cid.Cid, error) {\n\tif s.Internal.StateActorCodeCIDs == nil {\n\t\treturn *new(map[string]cid.Cid), ErrNotSupported\n\t}\n\treturn s.Internal.StateActorCodeCIDs(p0, p1)\n}\n```", + "summary": "StateActorCodeCIDs returns the CIDs of all the builtin actors for the given network version\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "abinetwork.Version", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 21 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "map[string]cid.Cid", + "description": "map[string]cid.Cid", + "summary": "", + "schema": { + "examples": [ + {} + ], + "patternProperties": { + ".*": { + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "title": "Content Identifier", + "type": "string" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3246" + } + }, + { + "name": "Filecoin.StateActorManifestCID", + "description": "```go\nfunc (s *FullNodeStruct) StateActorManifestCID(p0 context.Context, p1 abinetwork.Version) (cid.Cid, error) {\n\tif s.Internal.StateActorManifestCID == nil {\n\t\treturn *new(cid.Cid), ErrNotSupported\n\t}\n\treturn s.Internal.StateActorManifestCID(p0, p1)\n}\n```", + "summary": "StateActorManifestCID returns the CID of the builtin actors manifest for the given network version\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "abinetwork.Version", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 21 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "cid.Cid", + "description": "cid.Cid", + "summary": "", + "schema": { + "title": "Content Identifier", + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "examples": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3257" + } + }, + { + "name": "Filecoin.StateAllMinerFaults", + "description": "```go\nfunc (s *FullNodeStruct) StateAllMinerFaults(p0 context.Context, p1 abi.ChainEpoch, p2 types.TipSetKey) ([]*Fault, error) {\n\tif s.Internal.StateAllMinerFaults == nil {\n\t\treturn *new([]*Fault), ErrNotSupported\n\t}\n\treturn s.Internal.StateAllMinerFaults(p0, p1, p2)\n}\n```", + "summary": "StateAllMinerFaults returns all non-expired Faults that occur within lookback epochs of the given tipset\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "abi.ChainEpoch", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 10101 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "[]*Fault", + "description": "[]*Fault", + "summary": "", + "schema": { + "examples": [ + [ + { + "Miner": "f01234", + "Epoch": 10101 + } + ] + ], + "items": [ + { + "additionalProperties": false, + "properties": { + "Epoch": { + "title": "number", + "type": "number" + }, + "Miner": { + "additionalProperties": false, + "type": "object" + } + }, + "type": [ + "object" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3268" + } + }, + { + "name": "Filecoin.StateCall", + "description": "```go\nfunc (s *FullNodeStruct) StateCall(p0 context.Context, p1 *types.Message, p2 types.TipSetKey) (*InvocResult, error) {\n\tif s.Internal.StateCall == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.StateCall(p0, p1, p2)\n}\n```", + "summary": "StateCall runs the given message and returns its result without any persisted changes.\n\nStateCall applies the message to the tipset's parent state. The\nmessage is not applied on-top-of the messages in the passed-in\ntipset.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "*types.Message", + "summary": "", + "schema": { + "examples": [ + { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + } + ], + "additionalProperties": false, + "properties": { + "From": { + "additionalProperties": false, + "type": "object" + }, + "GasFeeCap": { + "additionalProperties": false, + "type": "object" + }, + "GasLimit": { + "title": "number", + "type": "number" + }, + "GasPremium": { + "additionalProperties": false, + "type": "object" + }, + "Method": { + "title": "number", + "type": "number" + }, + "Nonce": { + "title": "number", + "type": "number" + }, + "Params": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "To": { + "additionalProperties": false, + "type": "object" + }, + "Value": { + "additionalProperties": false, + "type": "object" + }, + "Version": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*InvocResult", + "description": "*InvocResult", + "summary": "", + "schema": { + "examples": [ + { + "MsgCid": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Msg": { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + }, + "MsgRct": { + "ExitCode": 0, + "Return": "Ynl0ZSBhcnJheQ==", + "GasUsed": 9, + "EventsRoot": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + }, + "GasCost": { + "Message": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "GasUsed": "0", + "BaseFeeBurn": "0", + "OverEstimationBurn": "0", + "MinerPenalty": "0", + "MinerTip": "0", + "Refund": "0", + "TotalCost": "0" + }, + "ExecutionTrace": { + "Msg": { + "From": "f01234", + "To": "f01234", + "Value": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "ParamsCodec": 42, + "GasLimit": 42, + "ReadOnly": true + }, + "MsgRct": { + "ExitCode": 0, + "Return": "Ynl0ZSBhcnJheQ==", + "ReturnCodec": 42 + }, + "InvokedActor": { + "Id": 1000, + "State": { + "Code": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Head": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Nonce": 42, + "Balance": "0", + "Address": "f01234" + } + }, + "GasCharges": [ + { + "Name": "string value", + "tg": 9, + "cg": 9, + "sg": 9, + "tt": 60000000000 + } + ], + "Subcalls": [ + { + "Msg": { + "From": "f01234", + "To": "f01234", + "Value": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "ParamsCodec": 42, + "GasLimit": 42, + "ReadOnly": true + }, + "MsgRct": { + "ExitCode": 0, + "Return": "Ynl0ZSBhcnJheQ==", + "ReturnCodec": 42 + }, + "InvokedActor": { + "Id": 1000, + "State": { + "Code": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Head": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Nonce": 42, + "Balance": "0", + "Address": "f01234" + } + }, + "GasCharges": [ + { + "Name": "string value", + "tg": 9, + "cg": 9, + "sg": 9, + "tt": 60000000000 + } + ], + "Subcalls": null + } + ] + }, + "Error": "string value", + "Duration": 60000000000 + } + ], + "additionalProperties": false, + "properties": { + "Duration": { + "title": "number", + "type": "number" + }, + "Error": { + "type": "string" + }, + "ExecutionTrace": { + "additionalProperties": false, + "properties": { + "GasCharges": { + "items": { + "additionalProperties": false, + "properties": { + "Name": { + "type": "string" + }, + "cg": { + "title": "number", + "type": "number" + }, + "sg": { + "title": "number", + "type": "number" + }, + "tg": { + "title": "number", + "type": "number" + }, + "tt": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "type": "array" + }, + "InvokedActor": { + "additionalProperties": false, + "properties": { + "Id": { + "title": "number", + "type": "number" + }, + "State": { + "additionalProperties": false, + "properties": { + "Address": { + "additionalProperties": false, + "type": "object" + }, + "Balance": { + "additionalProperties": false, + "type": "object" + }, + "Code": { + "title": "Content Identifier", + "type": "string" + }, + "Head": { + "title": "Content Identifier", + "type": "string" + }, + "Nonce": { + "title": "number", + "type": "number" + } + }, + "type": "object" + } + }, + "type": "object" + }, + "Msg": { + "additionalProperties": false, + "properties": { + "From": { + "additionalProperties": false, + "type": "object" + }, + "GasLimit": { + "title": "number", + "type": "number" + }, + "Method": { + "title": "number", + "type": "number" + }, + "Params": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "ParamsCodec": { + "title": "number", + "type": "number" + }, + "ReadOnly": { + "type": "boolean" + }, + "To": { + "additionalProperties": false, + "type": "object" + }, + "Value": { + "additionalProperties": false, + "type": "object" + } + }, + "type": "object" + }, + "MsgRct": { + "additionalProperties": false, + "properties": { + "ExitCode": { + "title": "number", + "type": "number" + }, + "Return": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "ReturnCodec": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "Subcalls": { + "items": {}, + "type": "array" + } + }, + "type": "object" + }, + "GasCost": { + "additionalProperties": false, + "properties": { + "BaseFeeBurn": { + "additionalProperties": false, + "type": "object" + }, + "GasUsed": { + "additionalProperties": false, + "type": "object" + }, + "Message": { + "title": "Content Identifier", + "type": "string" + }, + "MinerPenalty": { + "additionalProperties": false, + "type": "object" + }, + "MinerTip": { + "additionalProperties": false, + "type": "object" + }, + "OverEstimationBurn": { + "additionalProperties": false, + "type": "object" + }, + "Refund": { + "additionalProperties": false, + "type": "object" + }, + "TotalCost": { + "additionalProperties": false, + "type": "object" + } + }, + "type": "object" + }, + "Msg": { + "additionalProperties": false, + "properties": { + "From": { + "additionalProperties": false, + "type": "object" + }, + "GasFeeCap": { + "additionalProperties": false, + "type": "object" + }, + "GasLimit": { + "title": "number", + "type": "number" + }, + "GasPremium": { + "additionalProperties": false, + "type": "object" + }, + "Method": { + "title": "number", + "type": "number" + }, + "Nonce": { + "title": "number", + "type": "number" + }, + "Params": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "To": { + "additionalProperties": false, + "type": "object" + }, + "Value": { + "additionalProperties": false, + "type": "object" + }, + "Version": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "MsgCid": { + "title": "Content Identifier", + "type": "string" + }, + "MsgRct": { + "additionalProperties": false, + "properties": { + "EventsRoot": { + "title": "Content Identifier", + "type": "string" + }, + "ExitCode": { + "title": "number", + "type": "number" + }, + "GasUsed": { + "title": "number", + "type": "number" + }, + "Return": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + } + }, + "type": "object" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3279" + } + }, + { + "name": "Filecoin.StateChangedActors", + "description": "```go\nfunc (s *FullNodeStruct) StateChangedActors(p0 context.Context, p1 cid.Cid, p2 cid.Cid) (map[string]types.Actor, error) {\n\tif s.Internal.StateChangedActors == nil {\n\t\treturn *new(map[string]types.Actor), ErrNotSupported\n\t}\n\treturn s.Internal.StateChangedActors(p0, p1, p2)\n}\n```", + "summary": "StateChangedActors returns all the actors whose states change between the two given state CIDs\nTODO: Should this take tipset keys instead?\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "cid.Cid", + "summary": "", + "schema": { + "title": "Content Identifier", + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "examples": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "cid.Cid", + "summary": "", + "schema": { + "title": "Content Identifier", + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "examples": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "map[string]types.Actor", + "description": "map[string]types.Actor", + "summary": "", + "schema": { + "examples": [ + { + "t01236": { + "Code": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Head": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Nonce": 42, + "Balance": "0", + "Address": "f01234" + } + } + ], + "patternProperties": { + ".*": { + "additionalProperties": false, + "properties": { + "Address": { + "additionalProperties": false, + "type": "object" + }, + "Balance": { + "additionalProperties": false, + "type": "object" + }, + "Code": { + "title": "Content Identifier", + "type": "string" + }, + "Head": { + "title": "Content Identifier", + "type": "string" + }, + "Nonce": { + "title": "number", + "type": "number" + } + }, + "type": "object" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3290" + } + }, + { + "name": "Filecoin.StateCirculatingSupply", + "description": "```go\nfunc (s *FullNodeStruct) StateCirculatingSupply(p0 context.Context, p1 types.TipSetKey) (abi.TokenAmount, error) {\n\tif s.Internal.StateCirculatingSupply == nil {\n\t\treturn *new(abi.TokenAmount), ErrNotSupported\n\t}\n\treturn s.Internal.StateCirculatingSupply(p0, p1)\n}\n```", + "summary": "StateCirculatingSupply returns the exact circulating supply of Filecoin at the given tipset.\nThis is not used anywhere in the protocol itself, and is only for external consumption.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "abi.TokenAmount", + "description": "abi.TokenAmount", + "summary": "", + "schema": { + "examples": [ + "0" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3301" + } + }, + { + "name": "Filecoin.StateCompute", + "description": "```go\nfunc (s *FullNodeStruct) StateCompute(p0 context.Context, p1 abi.ChainEpoch, p2 []*types.Message, p3 types.TipSetKey) (*ComputeStateOutput, error) {\n\tif s.Internal.StateCompute == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.StateCompute(p0, p1, p2, p3)\n}\n```", + "summary": "StateCompute is a flexible command that applies the given messages on the given tipset.\nThe messages are run as though the VM were at the provided height.\n\nWhen called, StateCompute will:\n- Load the provided tipset, or use the current chain head if not provided\n- Compute the tipset state of the provided tipset on top of the parent state\n - (note that this step runs before vmheight is applied to the execution)\n - Execute state upgrade if any were scheduled at the epoch, or in null\n blocks preceding the tipset\n - Call the cron actor on null blocks preceding the tipset\n - For each block in the tipset\n - Apply messages in blocks in the specified\n - Award block reward by calling the reward actor\n - Call the cron actor for the current epoch\n- If the specified vmheight is higher than the current epoch, apply any\n needed state upgrades to the state\n- Apply the specified messages to the state\n\nThe vmheight parameter sets VM execution epoch, and can be used to simulate\nmessage execution in different network versions. If the specified vmheight\nepoch is higher than the epoch of the specified tipset, any state upgrades\nuntil the vmheight will be executed on the state before applying messages\nspecified by the user.\n\nNote that the initial tipset state computation is not affected by the\nvmheight parameter - only the messages in the `apply` set are\n\nIf the caller wants to simply compute the state, vmheight should be set to\nthe epoch of the specified tipset.\n\nMessages in the `apply` parameter must have the correct nonces, and gas\nvalues set.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "abi.ChainEpoch", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 10101 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "[]*types.Message", + "summary": "", + "schema": { + "examples": [ + [ + { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + } + ] + ], + "items": [ + { + "additionalProperties": false, + "properties": { + "From": { + "additionalProperties": false, + "type": "object" + }, + "GasFeeCap": { + "additionalProperties": false, + "type": "object" + }, + "GasLimit": { + "title": "number", + "type": "number" + }, + "GasPremium": { + "additionalProperties": false, + "type": "object" + }, + "Method": { + "title": "number", + "type": "number" + }, + "Nonce": { + "title": "number", + "type": "number" + }, + "Params": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "To": { + "additionalProperties": false, + "type": "object" + }, + "Value": { + "additionalProperties": false, + "type": "object" + }, + "Version": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p3", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*ComputeStateOutput", + "description": "*ComputeStateOutput", + "summary": "", + "schema": { + "examples": [ + { + "Root": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Trace": [ + { + "MsgCid": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Msg": { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + }, + "MsgRct": { + "ExitCode": 0, + "Return": "Ynl0ZSBhcnJheQ==", + "GasUsed": 9, + "EventsRoot": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + }, + "GasCost": { + "Message": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "GasUsed": "0", + "BaseFeeBurn": "0", + "OverEstimationBurn": "0", + "MinerPenalty": "0", + "MinerTip": "0", + "Refund": "0", + "TotalCost": "0" + }, + "ExecutionTrace": { + "Msg": { + "From": "f01234", + "To": "f01234", + "Value": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "ParamsCodec": 42, + "GasLimit": 42, + "ReadOnly": true + }, + "MsgRct": { + "ExitCode": 0, + "Return": "Ynl0ZSBhcnJheQ==", + "ReturnCodec": 42 + }, + "InvokedActor": { + "Id": 1000, + "State": { + "Code": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Head": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Nonce": 42, + "Balance": "0", + "Address": "f01234" + } + }, + "GasCharges": [ + { + "Name": "string value", + "tg": 9, + "cg": 9, + "sg": 9, + "tt": 60000000000 + } + ], + "Subcalls": [ + { + "Msg": { + "From": "f01234", + "To": "f01234", + "Value": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "ParamsCodec": 42, + "GasLimit": 42, + "ReadOnly": true + }, + "MsgRct": { + "ExitCode": 0, + "Return": "Ynl0ZSBhcnJheQ==", + "ReturnCodec": 42 + }, + "InvokedActor": { + "Id": 1000, + "State": { + "Code": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Head": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Nonce": 42, + "Balance": "0", + "Address": "f01234" + } + }, + "GasCharges": [ + { + "Name": "string value", + "tg": 9, + "cg": 9, + "sg": 9, + "tt": 60000000000 + } + ], + "Subcalls": null + } + ] + }, + "Error": "string value", + "Duration": 60000000000 + } + ] + } + ], + "additionalProperties": false, + "properties": { + "Root": { + "title": "Content Identifier", + "type": "string" + }, + "Trace": { + "items": { + "additionalProperties": false, + "properties": { + "Duration": { + "title": "number", + "type": "number" + }, + "Error": { + "type": "string" + }, + "ExecutionTrace": { + "additionalProperties": false, + "properties": { + "GasCharges": { + "items": { + "additionalProperties": false, + "properties": { + "Name": { + "type": "string" + }, + "cg": { + "title": "number", + "type": "number" + }, + "sg": { + "title": "number", + "type": "number" + }, + "tg": { + "title": "number", + "type": "number" + }, + "tt": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "type": "array" + }, + "InvokedActor": { + "additionalProperties": false, + "properties": { + "Id": { + "title": "number", + "type": "number" + }, + "State": { + "additionalProperties": false, + "properties": { + "Address": { + "additionalProperties": false, + "type": "object" + }, + "Balance": { + "additionalProperties": false, + "type": "object" + }, + "Code": { + "title": "Content Identifier", + "type": "string" + }, + "Head": { + "title": "Content Identifier", + "type": "string" + }, + "Nonce": { + "title": "number", + "type": "number" + } + }, + "type": "object" + } + }, + "type": "object" + }, + "Msg": { + "additionalProperties": false, + "properties": { + "From": { + "additionalProperties": false, + "type": "object" + }, + "GasLimit": { + "title": "number", + "type": "number" + }, + "Method": { + "title": "number", + "type": "number" + }, + "Params": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "ParamsCodec": { + "title": "number", + "type": "number" + }, + "ReadOnly": { + "type": "boolean" + }, + "To": { + "additionalProperties": false, + "type": "object" + }, + "Value": { + "additionalProperties": false, + "type": "object" + } + }, + "type": "object" + }, + "MsgRct": { + "additionalProperties": false, + "properties": { + "ExitCode": { + "title": "number", + "type": "number" + }, + "Return": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "ReturnCodec": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "Subcalls": { + "items": {}, + "type": "array" + } + }, + "type": "object" + }, + "GasCost": { + "additionalProperties": false, + "properties": { + "BaseFeeBurn": { + "additionalProperties": false, + "type": "object" + }, + "GasUsed": { + "additionalProperties": false, + "type": "object" + }, + "Message": { + "title": "Content Identifier", + "type": "string" + }, + "MinerPenalty": { + "additionalProperties": false, + "type": "object" + }, + "MinerTip": { + "additionalProperties": false, + "type": "object" + }, + "OverEstimationBurn": { + "additionalProperties": false, + "type": "object" + }, + "Refund": { + "additionalProperties": false, + "type": "object" + }, + "TotalCost": { + "additionalProperties": false, + "type": "object" + } + }, + "type": "object" + }, + "Msg": { + "additionalProperties": false, + "properties": { + "From": { + "additionalProperties": false, + "type": "object" + }, + "GasFeeCap": { + "additionalProperties": false, + "type": "object" + }, + "GasLimit": { + "title": "number", + "type": "number" + }, + "GasPremium": { + "additionalProperties": false, + "type": "object" + }, + "Method": { + "title": "number", + "type": "number" + }, + "Nonce": { + "title": "number", + "type": "number" + }, + "Params": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "To": { + "additionalProperties": false, + "type": "object" + }, + "Value": { + "additionalProperties": false, + "type": "object" + }, + "Version": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "MsgCid": { + "title": "Content Identifier", + "type": "string" + }, + "MsgRct": { + "additionalProperties": false, + "properties": { + "EventsRoot": { + "title": "Content Identifier", + "type": "string" + }, + "ExitCode": { + "title": "number", + "type": "number" + }, + "GasUsed": { + "title": "number", + "type": "number" + }, + "Return": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object" + }, + "type": "array" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3312" + } + }, + { + "name": "Filecoin.StateComputeDataCID", + "description": "```go\nfunc (s *FullNodeStruct) StateComputeDataCID(p0 context.Context, p1 address.Address, p2 abi.RegisteredSealProof, p3 []abi.DealID, p4 types.TipSetKey) (cid.Cid, error) {\n\tif s.Internal.StateComputeDataCID == nil {\n\t\treturn *new(cid.Cid), ErrNotSupported\n\t}\n\treturn s.Internal.StateComputeDataCID(p0, p1, p2, p3, p4)\n}\n```", + "summary": "StateComputeDataCID computes DataCID from a set of on-chain deals\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "abi.RegisteredSealProof", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 8 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p3", + "description": "[]abi.DealID", + "summary": "", + "schema": { + "examples": [ + [ + 5432 + ] + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p4", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "cid.Cid", + "description": "cid.Cid", + "summary": "", + "schema": { + "title": "Content Identifier", + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "examples": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3323" + } + }, + { + "name": "Filecoin.StateDealProviderCollateralBounds", + "description": "```go\nfunc (s *FullNodeStruct) StateDealProviderCollateralBounds(p0 context.Context, p1 abi.PaddedPieceSize, p2 bool, p3 types.TipSetKey) (DealCollateralBounds, error) {\n\tif s.Internal.StateDealProviderCollateralBounds == nil {\n\t\treturn *new(DealCollateralBounds), ErrNotSupported\n\t}\n\treturn s.Internal.StateDealProviderCollateralBounds(p0, p1, p2, p3)\n}\n```", + "summary": "StateDealProviderCollateralBounds returns the min and max collateral a storage provider\ncan issue. It takes the deal size and verified status as parameters.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "abi.PaddedPieceSize", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 1032 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "bool", + "summary": "", + "schema": { + "examples": [ + true + ], + "type": [ + "boolean" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p3", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "DealCollateralBounds", + "description": "DealCollateralBounds", + "summary": "", + "schema": { + "examples": [ + { + "Min": "0", + "Max": "0" + } + ], + "additionalProperties": false, + "properties": { + "Max": { + "additionalProperties": false, + "type": "object" + }, + "Min": { + "additionalProperties": false, + "type": "object" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3334" + } + }, + { + "name": "Filecoin.StateDecodeParams", + "description": "```go\nfunc (s *FullNodeStruct) StateDecodeParams(p0 context.Context, p1 address.Address, p2 abi.MethodNum, p3 []byte, p4 types.TipSetKey) (interface{}, error) {\n\tif s.Internal.StateDecodeParams == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.StateDecodeParams(p0, p1, p2, p3, p4)\n}\n```", + "summary": "StateDecodeParams attempts to decode the provided params, based on the recipient actor address and method number.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "abi.MethodNum", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 1 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p3", + "description": "[]byte", + "summary": "", + "schema": { + "examples": [ + "Ynl0ZSBhcnJheQ==" + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p4", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "interface{}", + "description": "interface{}", + "summary": "", + "schema": { + "examples": [ + {} + ], + "additionalProperties": true, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3345" + } + }, + { + "name": "Filecoin.StateEncodeParams", + "description": "```go\nfunc (s *FullNodeStruct) StateEncodeParams(p0 context.Context, p1 cid.Cid, p2 abi.MethodNum, p3 json.RawMessage) ([]byte, error) {\n\tif s.Internal.StateEncodeParams == nil {\n\t\treturn *new([]byte), ErrNotSupported\n\t}\n\treturn s.Internal.StateEncodeParams(p0, p1, p2, p3)\n}\n```", + "summary": "StateEncodeParams attempts to encode the provided json params to the binary from\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "cid.Cid", + "summary": "", + "schema": { + "title": "Content Identifier", + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "examples": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "abi.MethodNum", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 1 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p3", + "description": "json.RawMessage", + "summary": "", + "schema": { + "examples": [ + "json raw message" + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "[]byte", + "description": "[]byte", + "summary": "", + "schema": { + "examples": [ + "Ynl0ZSBhcnJheQ==" + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3356" + } + }, + { + "name": "Filecoin.StateGetActor", + "description": "```go\nfunc (s *FullNodeStruct) StateGetActor(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (*types.Actor, error) {\n\tif s.Internal.StateGetActor == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.StateGetActor(p0, p1, p2)\n}\n```", + "summary": "StateGetActor returns the indicated actor's nonce and balance.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*types.Actor", + "description": "*types.Actor", + "summary": "", + "schema": { + "examples": [ + { + "Code": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Head": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Nonce": 42, + "Balance": "0", + "Address": "f01234" + } + ], + "additionalProperties": false, + "properties": { + "Address": { + "additionalProperties": false, + "type": "object" + }, + "Balance": { + "additionalProperties": false, + "type": "object" + }, + "Code": { + "title": "Content Identifier", + "type": "string" + }, + "Head": { + "title": "Content Identifier", + "type": "string" + }, + "Nonce": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3367" + } + }, + { + "name": "Filecoin.StateGetAllocation", + "description": "```go\nfunc (s *FullNodeStruct) StateGetAllocation(p0 context.Context, p1 address.Address, p2 verifregtypes.AllocationId, p3 types.TipSetKey) (*verifregtypes.Allocation, error) {\n\tif s.Internal.StateGetAllocation == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.StateGetAllocation(p0, p1, p2, p3)\n}\n```", + "summary": "StateGetAllocation returns the allocation for a given address and allocation ID.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "verifregtypes.AllocationId", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 0 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p3", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*verifregtypes.Allocation", + "description": "*verifregtypes.Allocation", + "summary": "", + "schema": { + "examples": [ + { + "Client": 1000, + "Provider": 1000, + "Data": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Size": 1032, + "TermMin": 10101, + "TermMax": 10101, + "Expiration": 10101 + } + ], + "additionalProperties": false, + "properties": { + "Client": { + "title": "number", + "type": "number" + }, + "Data": { + "title": "Content Identifier", + "type": "string" + }, + "Expiration": { + "title": "number", + "type": "number" + }, + "Provider": { + "title": "number", + "type": "number" + }, + "Size": { + "title": "number", + "type": "number" + }, + "TermMax": { + "title": "number", + "type": "number" + }, + "TermMin": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3378" + } + }, + { + "name": "Filecoin.StateGetAllocationForPendingDeal", + "description": "```go\nfunc (s *FullNodeStruct) StateGetAllocationForPendingDeal(p0 context.Context, p1 abi.DealID, p2 types.TipSetKey) (*verifregtypes.Allocation, error) {\n\tif s.Internal.StateGetAllocationForPendingDeal == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.StateGetAllocationForPendingDeal(p0, p1, p2)\n}\n```", + "summary": "StateGetAllocationForPendingDeal returns the allocation for a given deal ID of a pending deal. Returns nil if\npending allocation is not found.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "abi.DealID", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 5432 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*verifregtypes.Allocation", + "description": "*verifregtypes.Allocation", + "summary": "", + "schema": { + "examples": [ + { + "Client": 1000, + "Provider": 1000, + "Data": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Size": 1032, + "TermMin": 10101, + "TermMax": 10101, + "Expiration": 10101 + } + ], + "additionalProperties": false, + "properties": { + "Client": { + "title": "number", + "type": "number" + }, + "Data": { + "title": "Content Identifier", + "type": "string" + }, + "Expiration": { + "title": "number", + "type": "number" + }, + "Provider": { + "title": "number", + "type": "number" + }, + "Size": { + "title": "number", + "type": "number" + }, + "TermMax": { + "title": "number", + "type": "number" + }, + "TermMin": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3389" + } + }, + { + "name": "Filecoin.StateGetAllocations", + "description": "```go\nfunc (s *FullNodeStruct) StateGetAllocations(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (map[verifregtypes.AllocationId]verifregtypes.Allocation, error) {\n\tif s.Internal.StateGetAllocations == nil {\n\t\treturn *new(map[verifregtypes.AllocationId]verifregtypes.Allocation), ErrNotSupported\n\t}\n\treturn s.Internal.StateGetAllocations(p0, p1, p2)\n}\n```", + "summary": "StateGetAllocations returns the all the allocations for a given client.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "map[verifregtypes.AllocationId]verifregtypes.Allocation", + "description": "map[verifregtypes.AllocationId]verifregtypes.Allocation", + "summary": "", + "schema": { + "examples": [ + {} + ], + "patternProperties": { + ".*": { + "additionalProperties": false, + "properties": { + "Client": { + "title": "number", + "type": "number" + }, + "Data": { + "title": "Content Identifier", + "type": "string" + }, + "Expiration": { + "title": "number", + "type": "number" + }, + "Provider": { + "title": "number", + "type": "number" + }, + "Size": { + "title": "number", + "type": "number" + }, + "TermMax": { + "title": "number", + "type": "number" + }, + "TermMin": { + "title": "number", + "type": "number" + } + }, + "type": "object" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3400" + } + }, + { + "name": "Filecoin.StateGetBeaconEntry", + "description": "```go\nfunc (s *FullNodeStruct) StateGetBeaconEntry(p0 context.Context, p1 abi.ChainEpoch) (*types.BeaconEntry, error) {\n\tif s.Internal.StateGetBeaconEntry == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.StateGetBeaconEntry(p0, p1)\n}\n```", + "summary": "StateGetBeaconEntry returns the beacon entry for the given filecoin epoch. If\nthe entry has not yet been produced, the call will block until the entry\nbecomes available\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "abi.ChainEpoch", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 10101 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*types.BeaconEntry", + "description": "*types.BeaconEntry", + "summary": "", + "schema": { + "examples": [ + { + "Round": 42, + "Data": "Ynl0ZSBhcnJheQ==" + } + ], + "additionalProperties": false, + "properties": { + "Data": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "Round": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3411" + } + }, + { + "name": "Filecoin.StateGetClaim", + "description": "```go\nfunc (s *FullNodeStruct) StateGetClaim(p0 context.Context, p1 address.Address, p2 verifregtypes.ClaimId, p3 types.TipSetKey) (*verifregtypes.Claim, error) {\n\tif s.Internal.StateGetClaim == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.StateGetClaim(p0, p1, p2, p3)\n}\n```", + "summary": "StateGetClaim returns the claim for a given address and claim ID.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "verifregtypes.ClaimId", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 0 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p3", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*verifregtypes.Claim", + "description": "*verifregtypes.Claim", + "summary": "", + "schema": { + "examples": [ + { + "Provider": 1000, + "Client": 1000, + "Data": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Size": 1032, + "TermMin": 10101, + "TermMax": 10101, + "TermStart": 10101, + "Sector": 9 + } + ], + "additionalProperties": false, + "properties": { + "Client": { + "title": "number", + "type": "number" + }, + "Data": { + "title": "Content Identifier", + "type": "string" + }, + "Provider": { + "title": "number", + "type": "number" + }, + "Sector": { + "title": "number", + "type": "number" + }, + "Size": { + "title": "number", + "type": "number" + }, + "TermMax": { + "title": "number", + "type": "number" + }, + "TermMin": { + "title": "number", + "type": "number" + }, + "TermStart": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3422" + } + }, + { + "name": "Filecoin.StateGetClaims", + "description": "```go\nfunc (s *FullNodeStruct) StateGetClaims(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (map[verifregtypes.ClaimId]verifregtypes.Claim, error) {\n\tif s.Internal.StateGetClaims == nil {\n\t\treturn *new(map[verifregtypes.ClaimId]verifregtypes.Claim), ErrNotSupported\n\t}\n\treturn s.Internal.StateGetClaims(p0, p1, p2)\n}\n```", + "summary": "StateGetClaims returns the all the claims for a given provider.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "map[verifregtypes.ClaimId]verifregtypes.Claim", + "description": "map[verifregtypes.ClaimId]verifregtypes.Claim", + "summary": "", + "schema": { + "examples": [ + {} + ], + "patternProperties": { + ".*": { + "additionalProperties": false, + "properties": { + "Client": { + "title": "number", + "type": "number" + }, + "Data": { + "title": "Content Identifier", + "type": "string" + }, + "Provider": { + "title": "number", + "type": "number" + }, + "Sector": { + "title": "number", + "type": "number" + }, + "Size": { + "title": "number", + "type": "number" + }, + "TermMax": { + "title": "number", + "type": "number" + }, + "TermMin": { + "title": "number", + "type": "number" + }, + "TermStart": { + "title": "number", + "type": "number" + } + }, + "type": "object" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3433" + } + }, + { + "name": "Filecoin.StateGetNetworkParams", + "description": "```go\nfunc (s *FullNodeStruct) StateGetNetworkParams(p0 context.Context) (*NetworkParams, error) {\n\tif s.Internal.StateGetNetworkParams == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.StateGetNetworkParams(p0)\n}\n```", + "summary": "StateGetNetworkParams return current network params\n", + "paramStructure": "by-position", + "params": [], + "result": { + "name": "*NetworkParams", + "description": "*NetworkParams", + "summary": "", + "schema": { + "examples": [ + { + "NetworkName": "lotus", + "BlockDelaySecs": 42, + "ConsensusMinerMinPower": "0", + "SupportedProofTypes": [ + 8 + ], + "PreCommitChallengeDelay": 10101, + "ForkUpgradeParams": { + "UpgradeSmokeHeight": 10101, + "UpgradeBreezeHeight": 10101, + "UpgradeIgnitionHeight": 10101, + "UpgradeLiftoffHeight": 10101, + "UpgradeAssemblyHeight": 10101, + "UpgradeRefuelHeight": 10101, + "UpgradeTapeHeight": 10101, + "UpgradeKumquatHeight": 10101, + "BreezeGasTampingDuration": 10101, + "UpgradeCalicoHeight": 10101, + "UpgradePersianHeight": 10101, + "UpgradeOrangeHeight": 10101, + "UpgradeClausHeight": 10101, + "UpgradeTrustHeight": 10101, + "UpgradeNorwegianHeight": 10101, + "UpgradeTurboHeight": 10101, + "UpgradeHyperdriveHeight": 10101, + "UpgradeChocolateHeight": 10101, + "UpgradeOhSnapHeight": 10101, + "UpgradeSkyrHeight": 10101, + "UpgradeSharkHeight": 10101, + "UpgradeHyggeHeight": 10101, + "UpgradeLightningHeight": 10101, + "UpgradeThunderHeight": 10101, + "UpgradeWatermelonHeight": 10101 + }, + "Eip155ChainID": 123 + } + ], + "additionalProperties": false, + "properties": { + "BlockDelaySecs": { + "title": "number", + "type": "number" + }, + "ConsensusMinerMinPower": { + "additionalProperties": false, + "type": "object" + }, + "Eip155ChainID": { + "title": "number", + "type": "number" + }, + "ForkUpgradeParams": { + "additionalProperties": false, + "properties": { + "BreezeGasTampingDuration": { + "title": "number", + "type": "number" + }, + "UpgradeAssemblyHeight": { + "title": "number", + "type": "number" + }, + "UpgradeBreezeHeight": { + "title": "number", + "type": "number" + }, + "UpgradeCalicoHeight": { + "title": "number", + "type": "number" + }, + "UpgradeChocolateHeight": { + "title": "number", + "type": "number" + }, + "UpgradeClausHeight": { + "title": "number", + "type": "number" + }, + "UpgradeHyggeHeight": { + "title": "number", + "type": "number" + }, + "UpgradeHyperdriveHeight": { + "title": "number", + "type": "number" + }, + "UpgradeIgnitionHeight": { + "title": "number", + "type": "number" + }, + "UpgradeKumquatHeight": { + "title": "number", + "type": "number" + }, + "UpgradeLiftoffHeight": { + "title": "number", + "type": "number" + }, + "UpgradeLightningHeight": { + "title": "number", + "type": "number" + }, + "UpgradeNorwegianHeight": { + "title": "number", + "type": "number" + }, + "UpgradeOhSnapHeight": { + "title": "number", + "type": "number" + }, + "UpgradeOrangeHeight": { + "title": "number", + "type": "number" + }, + "UpgradePersianHeight": { + "title": "number", + "type": "number" + }, + "UpgradeRefuelHeight": { + "title": "number", + "type": "number" + }, + "UpgradeSharkHeight": { + "title": "number", + "type": "number" + }, + "UpgradeSkyrHeight": { + "title": "number", + "type": "number" + }, + "UpgradeSmokeHeight": { + "title": "number", + "type": "number" + }, + "UpgradeTapeHeight": { + "title": "number", + "type": "number" + }, + "UpgradeThunderHeight": { + "title": "number", + "type": "number" + }, + "UpgradeTrustHeight": { + "title": "number", + "type": "number" + }, + "UpgradeTurboHeight": { + "title": "number", + "type": "number" + }, + "UpgradeWatermelonHeight": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "NetworkName": { + "type": "string" + }, + "PreCommitChallengeDelay": { + "title": "number", + "type": "number" + }, + "SupportedProofTypes": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "type": "array" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3444" + } + }, + { + "name": "Filecoin.StateGetRandomnessDigestFromBeacon", + "description": "```go\nfunc (s *FullNodeStruct) StateGetRandomnessDigestFromBeacon(p0 context.Context, p1 abi.ChainEpoch, p2 types.TipSetKey) (abi.Randomness, error) {\n\tif s.Internal.StateGetRandomnessDigestFromBeacon == nil {\n\t\treturn *new(abi.Randomness), ErrNotSupported\n\t}\n\treturn s.Internal.StateGetRandomnessDigestFromBeacon(p0, p1, p2)\n}\n```", + "summary": "StateGetRandomnessDigestFromBeacon is used to sample the beacon for randomness.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "abi.ChainEpoch", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 10101 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "abi.Randomness", + "description": "abi.Randomness", + "summary": "", + "schema": { + "examples": [ + "Bw==" + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3455" + } + }, + { + "name": "Filecoin.StateGetRandomnessDigestFromTickets", + "description": "```go\nfunc (s *FullNodeStruct) StateGetRandomnessDigestFromTickets(p0 context.Context, p1 abi.ChainEpoch, p2 types.TipSetKey) (abi.Randomness, error) {\n\tif s.Internal.StateGetRandomnessDigestFromTickets == nil {\n\t\treturn *new(abi.Randomness), ErrNotSupported\n\t}\n\treturn s.Internal.StateGetRandomnessDigestFromTickets(p0, p1, p2)\n}\n```", + "summary": "StateGetRandomnessDigestFromTickets. is used to sample the chain for randomness.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "abi.ChainEpoch", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 10101 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "abi.Randomness", + "description": "abi.Randomness", + "summary": "", + "schema": { + "examples": [ + "Bw==" + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3466" + } + }, + { + "name": "Filecoin.StateGetRandomnessFromBeacon", + "description": "```go\nfunc (s *FullNodeStruct) StateGetRandomnessFromBeacon(p0 context.Context, p1 crypto.DomainSeparationTag, p2 abi.ChainEpoch, p3 []byte, p4 types.TipSetKey) (abi.Randomness, error) {\n\tif s.Internal.StateGetRandomnessFromBeacon == nil {\n\t\treturn *new(abi.Randomness), ErrNotSupported\n\t}\n\treturn s.Internal.StateGetRandomnessFromBeacon(p0, p1, p2, p3, p4)\n}\n```", + "summary": "StateGetRandomnessFromBeacon is used to sample the beacon for randomness.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "crypto.DomainSeparationTag", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 2 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "abi.ChainEpoch", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 10101 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p3", + "description": "[]byte", + "summary": "", + "schema": { + "examples": [ + "Ynl0ZSBhcnJheQ==" + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p4", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "abi.Randomness", + "description": "abi.Randomness", + "summary": "", + "schema": { + "examples": [ + "Bw==" + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3477" + } + }, + { + "name": "Filecoin.StateGetRandomnessFromTickets", + "description": "```go\nfunc (s *FullNodeStruct) StateGetRandomnessFromTickets(p0 context.Context, p1 crypto.DomainSeparationTag, p2 abi.ChainEpoch, p3 []byte, p4 types.TipSetKey) (abi.Randomness, error) {\n\tif s.Internal.StateGetRandomnessFromTickets == nil {\n\t\treturn *new(abi.Randomness), ErrNotSupported\n\t}\n\treturn s.Internal.StateGetRandomnessFromTickets(p0, p1, p2, p3, p4)\n}\n```", + "summary": "StateGetRandomnessFromTickets is used to sample the chain for randomness.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "crypto.DomainSeparationTag", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 2 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "abi.ChainEpoch", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 10101 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p3", + "description": "[]byte", + "summary": "", + "schema": { + "examples": [ + "Ynl0ZSBhcnJheQ==" + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p4", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "abi.Randomness", + "description": "abi.Randomness", + "summary": "", + "schema": { + "examples": [ + "Bw==" + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3488" + } + }, + { + "name": "Filecoin.StateListActors", + "description": "```go\nfunc (s *FullNodeStruct) StateListActors(p0 context.Context, p1 types.TipSetKey) ([]address.Address, error) {\n\tif s.Internal.StateListActors == nil {\n\t\treturn *new([]address.Address), ErrNotSupported\n\t}\n\treturn s.Internal.StateListActors(p0, p1)\n}\n```", + "summary": "StateListActors returns the addresses of every actor in the state\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "[]address.Address", + "description": "[]address.Address", + "summary": "", + "schema": { + "examples": [ + [ + "f01234" + ] + ], + "items": [ + { + "additionalProperties": false, + "type": [ + "object" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3499" + } + }, + { + "name": "Filecoin.StateListMessages", + "description": "```go\nfunc (s *FullNodeStruct) StateListMessages(p0 context.Context, p1 *MessageMatch, p2 types.TipSetKey, p3 abi.ChainEpoch) ([]cid.Cid, error) {\n\tif s.Internal.StateListMessages == nil {\n\t\treturn *new([]cid.Cid), ErrNotSupported\n\t}\n\treturn s.Internal.StateListMessages(p0, p1, p2, p3)\n}\n```", + "summary": "StateListMessages looks back and returns all messages with a matching to or from address, stopping at the given height.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "*MessageMatch", + "summary": "", + "schema": { + "examples": [ + { + "To": "f01234", + "From": "f01234" + } + ], + "additionalProperties": false, + "properties": { + "From": { + "additionalProperties": false, + "type": "object" + }, + "To": { + "additionalProperties": false, + "type": "object" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p3", + "description": "abi.ChainEpoch", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 10101 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "[]cid.Cid", + "description": "[]cid.Cid", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ] + ], + "items": [ + { + "title": "Content Identifier", + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "type": [ + "string" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3510" + } + }, + { + "name": "Filecoin.StateListMiners", + "description": "```go\nfunc (s *FullNodeStruct) StateListMiners(p0 context.Context, p1 types.TipSetKey) ([]address.Address, error) {\n\tif s.Internal.StateListMiners == nil {\n\t\treturn *new([]address.Address), ErrNotSupported\n\t}\n\treturn s.Internal.StateListMiners(p0, p1)\n}\n```", + "summary": "StateListMiners returns the addresses of every miner that has claimed power in the Power Actor\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "[]address.Address", + "description": "[]address.Address", + "summary": "", + "schema": { + "examples": [ + [ + "f01234" + ] + ], + "items": [ + { + "additionalProperties": false, + "type": [ + "object" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3521" + } + }, + { + "name": "Filecoin.StateLookupID", + "description": "```go\nfunc (s *FullNodeStruct) StateLookupID(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (address.Address, error) {\n\tif s.Internal.StateLookupID == nil {\n\t\treturn *new(address.Address), ErrNotSupported\n\t}\n\treturn s.Internal.StateLookupID(p0, p1, p2)\n}\n```", + "summary": "StateLookupID retrieves the ID address of the given address\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "address.Address", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3532" + } + }, + { + "name": "Filecoin.StateLookupRobustAddress", + "description": "```go\nfunc (s *FullNodeStruct) StateLookupRobustAddress(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (address.Address, error) {\n\tif s.Internal.StateLookupRobustAddress == nil {\n\t\treturn *new(address.Address), ErrNotSupported\n\t}\n\treturn s.Internal.StateLookupRobustAddress(p0, p1, p2)\n}\n```", + "summary": "StateLookupRobustAddress returns the public key address of the given ID address for non-account addresses (multisig, miners etc)\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "address.Address", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3543" + } + }, + { + "name": "Filecoin.StateMarketBalance", + "description": "```go\nfunc (s *FullNodeStruct) StateMarketBalance(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (MarketBalance, error) {\n\tif s.Internal.StateMarketBalance == nil {\n\t\treturn *new(MarketBalance), ErrNotSupported\n\t}\n\treturn s.Internal.StateMarketBalance(p0, p1, p2)\n}\n```", + "summary": "StateMarketBalance looks up the Escrow and Locked balances of the given address in the Storage Market\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "MarketBalance", + "description": "MarketBalance", + "summary": "", + "schema": { + "examples": [ + { + "Escrow": "0", + "Locked": "0" + } + ], + "additionalProperties": false, + "properties": { + "Escrow": { + "additionalProperties": false, + "type": "object" + }, + "Locked": { + "additionalProperties": false, + "type": "object" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3554" + } + }, + { + "name": "Filecoin.StateMarketDeals", + "description": "```go\nfunc (s *FullNodeStruct) StateMarketDeals(p0 context.Context, p1 types.TipSetKey) (map[string]*MarketDeal, error) {\n\tif s.Internal.StateMarketDeals == nil {\n\t\treturn *new(map[string]*MarketDeal), ErrNotSupported\n\t}\n\treturn s.Internal.StateMarketDeals(p0, p1)\n}\n```", + "summary": "StateMarketDeals returns information about every deal in the Storage Market\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "map[string]*MarketDeal", + "description": "map[string]*MarketDeal", + "summary": "", + "schema": { + "examples": [ + { + "t026363": { + "Proposal": { + "PieceCID": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "PieceSize": 1032, + "VerifiedDeal": true, + "Client": "f01234", + "Provider": "f01234", + "Label": "", + "StartEpoch": 10101, + "EndEpoch": 10101, + "StoragePricePerEpoch": "0", + "ProviderCollateral": "0", + "ClientCollateral": "0" + }, + "State": { + "SectorStartEpoch": 10101, + "LastUpdatedEpoch": 10101, + "SlashEpoch": 10101, + "VerifiedClaim": 0 + } + } + } + ], + "patternProperties": { + ".*": { + "additionalProperties": false, + "properties": { + "Proposal": { + "additionalProperties": false, + "properties": { + "Client": { + "additionalProperties": false, + "type": "object" + }, + "ClientCollateral": { + "additionalProperties": false, + "type": "object" + }, + "EndEpoch": { + "title": "number", + "type": "number" + }, + "Label": { + "additionalProperties": false, + "type": "object" + }, + "PieceCID": { + "title": "Content Identifier", + "type": "string" + }, + "PieceSize": { + "title": "number", + "type": "number" + }, + "Provider": { + "additionalProperties": false, + "type": "object" + }, + "ProviderCollateral": { + "additionalProperties": false, + "type": "object" + }, + "StartEpoch": { + "title": "number", + "type": "number" + }, + "StoragePricePerEpoch": { + "additionalProperties": false, + "type": "object" + }, + "VerifiedDeal": { + "type": "boolean" + } + }, + "type": "object" + }, + "State": { + "additionalProperties": false, + "properties": { + "LastUpdatedEpoch": { + "title": "number", + "type": "number" + }, + "SectorStartEpoch": { + "title": "number", + "type": "number" + }, + "SlashEpoch": { + "title": "number", + "type": "number" + }, + "VerifiedClaim": { + "title": "number", + "type": "number" + } + }, + "type": "object" + } + }, + "type": "object" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3565" + } + }, + { + "name": "Filecoin.StateMarketParticipants", + "description": "```go\nfunc (s *FullNodeStruct) StateMarketParticipants(p0 context.Context, p1 types.TipSetKey) (map[string]MarketBalance, error) {\n\tif s.Internal.StateMarketParticipants == nil {\n\t\treturn *new(map[string]MarketBalance), ErrNotSupported\n\t}\n\treturn s.Internal.StateMarketParticipants(p0, p1)\n}\n```", + "summary": "StateMarketParticipants returns the Escrow and Locked balances of every participant in the Storage Market\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "map[string]MarketBalance", + "description": "map[string]MarketBalance", + "summary": "", + "schema": { + "examples": [ + { + "t026363": { + "Escrow": "0", + "Locked": "0" + } + } + ], + "patternProperties": { + ".*": { + "additionalProperties": false, + "properties": { + "Escrow": { + "additionalProperties": false, + "type": "object" + }, + "Locked": { + "additionalProperties": false, + "type": "object" + } + }, + "type": "object" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3576" + } + }, + { + "name": "Filecoin.StateMarketStorageDeal", + "description": "```go\nfunc (s *FullNodeStruct) StateMarketStorageDeal(p0 context.Context, p1 abi.DealID, p2 types.TipSetKey) (*MarketDeal, error) {\n\tif s.Internal.StateMarketStorageDeal == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.StateMarketStorageDeal(p0, p1, p2)\n}\n```", + "summary": "StateMarketStorageDeal returns information about the indicated deal\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "abi.DealID", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 5432 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*MarketDeal", + "description": "*MarketDeal", + "summary": "", + "schema": { + "examples": [ + { + "Proposal": { + "PieceCID": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "PieceSize": 1032, + "VerifiedDeal": true, + "Client": "f01234", + "Provider": "f01234", + "Label": "", + "StartEpoch": 10101, + "EndEpoch": 10101, + "StoragePricePerEpoch": "0", + "ProviderCollateral": "0", + "ClientCollateral": "0" + }, + "State": { + "SectorStartEpoch": 10101, + "LastUpdatedEpoch": 10101, + "SlashEpoch": 10101, + "VerifiedClaim": 0 + } + } + ], + "additionalProperties": false, + "properties": { + "Proposal": { + "additionalProperties": false, + "properties": { + "Client": { + "additionalProperties": false, + "type": "object" + }, + "ClientCollateral": { + "additionalProperties": false, + "type": "object" + }, + "EndEpoch": { + "title": "number", + "type": "number" + }, + "Label": { + "additionalProperties": false, + "type": "object" + }, + "PieceCID": { + "title": "Content Identifier", + "type": "string" + }, + "PieceSize": { + "title": "number", + "type": "number" + }, + "Provider": { + "additionalProperties": false, + "type": "object" + }, + "ProviderCollateral": { + "additionalProperties": false, + "type": "object" + }, + "StartEpoch": { + "title": "number", + "type": "number" + }, + "StoragePricePerEpoch": { + "additionalProperties": false, + "type": "object" + }, + "VerifiedDeal": { + "type": "boolean" + } + }, + "type": "object" + }, + "State": { + "additionalProperties": false, + "properties": { + "LastUpdatedEpoch": { + "title": "number", + "type": "number" + }, + "SectorStartEpoch": { + "title": "number", + "type": "number" + }, + "SlashEpoch": { + "title": "number", + "type": "number" + }, + "VerifiedClaim": { + "title": "number", + "type": "number" + } + }, + "type": "object" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3587" + } + }, + { + "name": "Filecoin.StateMinerActiveSectors", + "description": "```go\nfunc (s *FullNodeStruct) StateMinerActiveSectors(p0 context.Context, p1 address.Address, p2 types.TipSetKey) ([]*miner.SectorOnChainInfo, error) {\n\tif s.Internal.StateMinerActiveSectors == nil {\n\t\treturn *new([]*miner.SectorOnChainInfo), ErrNotSupported\n\t}\n\treturn s.Internal.StateMinerActiveSectors(p0, p1, p2)\n}\n```", + "summary": "StateMinerActiveSectors returns info about sectors that a given miner is actively proving.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "[]*miner.SectorOnChainInfo", + "description": "[]*miner.SectorOnChainInfo", + "summary": "", + "schema": { + "examples": [ + [ + { + "SectorNumber": 9, + "SealProof": 8, + "SealedCID": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "DealIDs": [ + 5432 + ], + "Activation": 10101, + "Expiration": 10101, + "DealWeight": "0", + "VerifiedDealWeight": "0", + "InitialPledge": "0", + "ExpectedDayReward": "0", + "ExpectedStoragePledge": "0", + "PowerBaseEpoch": 10101, + "ReplacedDayReward": "0", + "SectorKeyCID": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Flags": 0 + } + ] + ], + "items": [ + { + "additionalProperties": false, + "properties": { + "Activation": { + "title": "number", + "type": "number" + }, + "DealIDs": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "type": "array" + }, + "DealWeight": { + "additionalProperties": false, + "type": "object" + }, + "ExpectedDayReward": { + "additionalProperties": false, + "type": "object" + }, + "ExpectedStoragePledge": { + "additionalProperties": false, + "type": "object" + }, + "Expiration": { + "title": "number", + "type": "number" + }, + "Flags": { + "title": "number", + "type": "number" + }, + "InitialPledge": { + "additionalProperties": false, + "type": "object" + }, + "PowerBaseEpoch": { + "title": "number", + "type": "number" + }, + "ReplacedDayReward": { + "additionalProperties": false, + "type": "object" + }, + "SealProof": { + "title": "number", + "type": "number" + }, + "SealedCID": { + "title": "Content Identifier", + "type": "string" + }, + "SectorKeyCID": { + "title": "Content Identifier", + "type": "string" + }, + "SectorNumber": { + "title": "number", + "type": "number" + }, + "VerifiedDealWeight": { + "additionalProperties": false, + "type": "object" + } + }, + "type": [ + "object" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3598" + } + }, + { + "name": "Filecoin.StateMinerAllocated", + "description": "```go\nfunc (s *FullNodeStruct) StateMinerAllocated(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (*bitfield.BitField, error) {\n\tif s.Internal.StateMinerAllocated == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.StateMinerAllocated(p0, p1, p2)\n}\n```", + "summary": "StateMinerAllocated returns a bitfield containing all sector numbers marked as allocated in miner state\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*bitfield.BitField", + "description": "*bitfield.BitField", + "summary": "", + "schema": { + "examples": [ + [ + 0 + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3609" + } + }, + { + "name": "Filecoin.StateMinerAvailableBalance", + "description": "```go\nfunc (s *FullNodeStruct) StateMinerAvailableBalance(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (types.BigInt, error) {\n\tif s.Internal.StateMinerAvailableBalance == nil {\n\t\treturn *new(types.BigInt), ErrNotSupported\n\t}\n\treturn s.Internal.StateMinerAvailableBalance(p0, p1, p2)\n}\n```", + "summary": "StateMinerAvailableBalance returns the portion of a miner's balance that can be withdrawn or spent\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "types.BigInt", + "description": "types.BigInt", + "summary": "", + "schema": { + "examples": [ + "0" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3620" + } + }, + { + "name": "Filecoin.StateMinerDeadlines", + "description": "```go\nfunc (s *FullNodeStruct) StateMinerDeadlines(p0 context.Context, p1 address.Address, p2 types.TipSetKey) ([]Deadline, error) {\n\tif s.Internal.StateMinerDeadlines == nil {\n\t\treturn *new([]Deadline), ErrNotSupported\n\t}\n\treturn s.Internal.StateMinerDeadlines(p0, p1, p2)\n}\n```", + "summary": "StateMinerDeadlines returns all the proving deadlines for the given miner\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "[]Deadline", + "description": "[]Deadline", + "summary": "", + "schema": { + "examples": [ + [ + { + "PostSubmissions": [ + 5, + 1 + ], + "DisputableProofCount": 42 + } + ] + ], + "items": [ + { + "additionalProperties": false, + "properties": { + "DisputableProofCount": { + "title": "number", + "type": "number" + }, + "PostSubmissions": { + "additionalProperties": false, + "type": "object" + } + }, + "type": [ + "object" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3631" + } + }, + { + "name": "Filecoin.StateMinerFaults", + "description": "```go\nfunc (s *FullNodeStruct) StateMinerFaults(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (bitfield.BitField, error) {\n\tif s.Internal.StateMinerFaults == nil {\n\t\treturn *new(bitfield.BitField), ErrNotSupported\n\t}\n\treturn s.Internal.StateMinerFaults(p0, p1, p2)\n}\n```", + "summary": "StateMinerFaults returns a bitfield indicating the faulty sectors of the given miner\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "bitfield.BitField", + "description": "bitfield.BitField", + "summary": "", + "schema": { + "examples": [ + [ + 5, + 1 + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3642" + } + }, + { + "name": "Filecoin.StateMinerInfo", + "description": "```go\nfunc (s *FullNodeStruct) StateMinerInfo(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (MinerInfo, error) {\n\tif s.Internal.StateMinerInfo == nil {\n\t\treturn *new(MinerInfo), ErrNotSupported\n\t}\n\treturn s.Internal.StateMinerInfo(p0, p1, p2)\n}\n```", + "summary": "StateMinerInfo returns info about the indicated miner\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "MinerInfo", + "description": "MinerInfo", + "summary": "", + "schema": { + "examples": [ + { + "Owner": "f01234", + "Worker": "f01234", + "NewWorker": "f01234", + "ControlAddresses": [ + "f01234" + ], + "WorkerChangeEpoch": 10101, + "PeerId": "12D3KooWGzxzKZYveHXtpG6AsrUJBcWxHBFS2HsEoGTxrMLvKXtf", + "Multiaddrs": [ + "Ynl0ZSBhcnJheQ==" + ], + "WindowPoStProofType": 8, + "SectorSize": 34359738368, + "WindowPoStPartitionSectors": 42, + "ConsensusFaultElapsed": 10101, + "PendingOwnerAddress": "f01234", + "Beneficiary": "f01234", + "BeneficiaryTerm": { + "Quota": "0", + "UsedQuota": "0", + "Expiration": 10101 + }, + "PendingBeneficiaryTerm": { + "NewBeneficiary": "f01234", + "NewQuota": "0", + "NewExpiration": 10101, + "ApprovedByBeneficiary": true, + "ApprovedByNominee": true + } + } + ], + "additionalProperties": false, + "properties": { + "Beneficiary": { + "additionalProperties": false, + "type": "object" + }, + "BeneficiaryTerm": { + "additionalProperties": false, + "properties": { + "Expiration": { + "title": "number", + "type": "number" + }, + "Quota": { + "additionalProperties": false, + "type": "object" + }, + "UsedQuota": { + "additionalProperties": false, + "type": "object" + } + }, + "type": "object" + }, + "ConsensusFaultElapsed": { + "title": "number", + "type": "number" + }, + "ControlAddresses": { + "items": { + "additionalProperties": false, + "type": "object" + }, + "type": "array" + }, + "Multiaddrs": { + "items": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "type": "array" + }, + "NewWorker": { + "additionalProperties": false, + "type": "object" + }, + "Owner": { + "additionalProperties": false, + "type": "object" + }, + "PeerId": { + "type": "string" + }, + "PendingBeneficiaryTerm": { + "additionalProperties": false, + "properties": { + "ApprovedByBeneficiary": { + "type": "boolean" + }, + "ApprovedByNominee": { + "type": "boolean" + }, + "NewBeneficiary": { + "additionalProperties": false, + "type": "object" + }, + "NewExpiration": { + "title": "number", + "type": "number" + }, + "NewQuota": { + "additionalProperties": false, + "type": "object" + } + }, + "type": "object" + }, + "PendingOwnerAddress": { + "additionalProperties": false, + "type": "object" + }, + "SectorSize": { + "title": "number", + "type": "number" + }, + "WindowPoStPartitionSectors": { + "title": "number", + "type": "number" + }, + "WindowPoStProofType": { + "title": "number", + "type": "number" + }, + "Worker": { + "additionalProperties": false, + "type": "object" + }, + "WorkerChangeEpoch": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3653" + } + }, + { + "name": "Filecoin.StateMinerInitialPledgeCollateral", + "description": "```go\nfunc (s *FullNodeStruct) StateMinerInitialPledgeCollateral(p0 context.Context, p1 address.Address, p2 miner.SectorPreCommitInfo, p3 types.TipSetKey) (types.BigInt, error) {\n\tif s.Internal.StateMinerInitialPledgeCollateral == nil {\n\t\treturn *new(types.BigInt), ErrNotSupported\n\t}\n\treturn s.Internal.StateMinerInitialPledgeCollateral(p0, p1, p2, p3)\n}\n```", + "summary": "StateMinerInitialPledgeCollateral returns the initial pledge collateral for the specified miner's sector\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "miner.SectorPreCommitInfo", + "summary": "", + "schema": { + "examples": [ + { + "SealProof": 8, + "SectorNumber": 9, + "SealedCID": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "SealRandEpoch": 10101, + "DealIDs": [ + 5432 + ], + "Expiration": 10101, + "UnsealedCid": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + } + ], + "additionalProperties": false, + "properties": { + "DealIDs": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "type": "array" + }, + "Expiration": { + "title": "number", + "type": "number" + }, + "SealProof": { + "title": "number", + "type": "number" + }, + "SealRandEpoch": { + "title": "number", + "type": "number" + }, + "SealedCID": { + "title": "Content Identifier", + "type": "string" + }, + "SectorNumber": { + "title": "number", + "type": "number" + }, + "UnsealedCid": { + "title": "Content Identifier", + "type": "string" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p3", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "types.BigInt", + "description": "types.BigInt", + "summary": "", + "schema": { + "examples": [ + "0" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3664" + } + }, + { + "name": "Filecoin.StateMinerPartitions", + "description": "```go\nfunc (s *FullNodeStruct) StateMinerPartitions(p0 context.Context, p1 address.Address, p2 uint64, p3 types.TipSetKey) ([]Partition, error) {\n\tif s.Internal.StateMinerPartitions == nil {\n\t\treturn *new([]Partition), ErrNotSupported\n\t}\n\treturn s.Internal.StateMinerPartitions(p0, p1, p2, p3)\n}\n```", + "summary": "StateMinerPartitions returns all partitions in the specified deadline\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "uint64", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 42 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p3", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "[]Partition", + "description": "[]Partition", + "summary": "", + "schema": { + "examples": [ + [ + { + "AllSectors": [ + 5, + 1 + ], + "FaultySectors": [ + 5, + 1 + ], + "RecoveringSectors": [ + 5, + 1 + ], + "LiveSectors": [ + 5, + 1 + ], + "ActiveSectors": [ + 5, + 1 + ] + } + ] + ], + "items": [ + { + "additionalProperties": false, + "properties": { + "ActiveSectors": { + "additionalProperties": false, + "type": "object" + }, + "AllSectors": { + "additionalProperties": false, + "type": "object" + }, + "FaultySectors": { + "additionalProperties": false, + "type": "object" + }, + "LiveSectors": { + "additionalProperties": false, + "type": "object" + }, + "RecoveringSectors": { + "additionalProperties": false, + "type": "object" + } + }, + "type": [ + "object" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3675" + } + }, + { + "name": "Filecoin.StateMinerPower", + "description": "```go\nfunc (s *FullNodeStruct) StateMinerPower(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (*MinerPower, error) {\n\tif s.Internal.StateMinerPower == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.StateMinerPower(p0, p1, p2)\n}\n```", + "summary": "StateMinerPower returns the power of the indicated miner\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*MinerPower", + "description": "*MinerPower", + "summary": "", + "schema": { + "examples": [ + { + "MinerPower": { + "RawBytePower": "0", + "QualityAdjPower": "0" + }, + "TotalPower": { + "RawBytePower": "0", + "QualityAdjPower": "0" + }, + "HasMinPower": true + } + ], + "additionalProperties": false, + "properties": { + "HasMinPower": { + "type": "boolean" + }, + "MinerPower": { + "additionalProperties": false, + "properties": { + "QualityAdjPower": { + "additionalProperties": false, + "type": "object" + }, + "RawBytePower": { + "additionalProperties": false, + "type": "object" + } + }, + "type": "object" + }, + "TotalPower": { + "additionalProperties": false, + "properties": { + "QualityAdjPower": { + "additionalProperties": false, + "type": "object" + }, + "RawBytePower": { + "additionalProperties": false, + "type": "object" + } + }, + "type": "object" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3686" + } + }, + { + "name": "Filecoin.StateMinerPreCommitDepositForPower", + "description": "```go\nfunc (s *FullNodeStruct) StateMinerPreCommitDepositForPower(p0 context.Context, p1 address.Address, p2 miner.SectorPreCommitInfo, p3 types.TipSetKey) (types.BigInt, error) {\n\tif s.Internal.StateMinerPreCommitDepositForPower == nil {\n\t\treturn *new(types.BigInt), ErrNotSupported\n\t}\n\treturn s.Internal.StateMinerPreCommitDepositForPower(p0, p1, p2, p3)\n}\n```", + "summary": "StateMinerInitialPledgeCollateral returns the precommit deposit for the specified miner's sector\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "miner.SectorPreCommitInfo", + "summary": "", + "schema": { + "examples": [ + { + "SealProof": 8, + "SectorNumber": 9, + "SealedCID": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "SealRandEpoch": 10101, + "DealIDs": [ + 5432 + ], + "Expiration": 10101, + "UnsealedCid": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + } + ], + "additionalProperties": false, + "properties": { + "DealIDs": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "type": "array" + }, + "Expiration": { + "title": "number", + "type": "number" + }, + "SealProof": { + "title": "number", + "type": "number" + }, + "SealRandEpoch": { + "title": "number", + "type": "number" + }, + "SealedCID": { + "title": "Content Identifier", + "type": "string" + }, + "SectorNumber": { + "title": "number", + "type": "number" + }, + "UnsealedCid": { + "title": "Content Identifier", + "type": "string" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p3", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "types.BigInt", + "description": "types.BigInt", + "summary": "", + "schema": { + "examples": [ + "0" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3697" + } + }, + { + "name": "Filecoin.StateMinerProvingDeadline", + "description": "```go\nfunc (s *FullNodeStruct) StateMinerProvingDeadline(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (*dline.Info, error) {\n\tif s.Internal.StateMinerProvingDeadline == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.StateMinerProvingDeadline(p0, p1, p2)\n}\n```", + "summary": "StateMinerProvingDeadline calculates the deadline at some epoch for a proving period\nand returns the deadline-related calculations.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*dline.Info", + "description": "*dline.Info", + "summary": "", + "schema": { + "examples": [ + { + "CurrentEpoch": 10101, + "PeriodStart": 10101, + "Index": 42, + "Open": 10101, + "Close": 10101, + "Challenge": 10101, + "FaultCutoff": 10101, + "WPoStPeriodDeadlines": 42, + "WPoStProvingPeriod": 10101, + "WPoStChallengeWindow": 10101, + "WPoStChallengeLookback": 10101, + "FaultDeclarationCutoff": 10101 + } + ], + "additionalProperties": false, + "properties": { + "Challenge": { + "title": "number", + "type": "number" + }, + "Close": { + "title": "number", + "type": "number" + }, + "CurrentEpoch": { + "title": "number", + "type": "number" + }, + "FaultCutoff": { + "title": "number", + "type": "number" + }, + "FaultDeclarationCutoff": { + "title": "number", + "type": "number" + }, + "Index": { + "title": "number", + "type": "number" + }, + "Open": { + "title": "number", + "type": "number" + }, + "PeriodStart": { + "title": "number", + "type": "number" + }, + "WPoStChallengeLookback": { + "title": "number", + "type": "number" + }, + "WPoStChallengeWindow": { + "title": "number", + "type": "number" + }, + "WPoStPeriodDeadlines": { + "title": "number", + "type": "number" + }, + "WPoStProvingPeriod": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3708" + } + }, + { + "name": "Filecoin.StateMinerRecoveries", + "description": "```go\nfunc (s *FullNodeStruct) StateMinerRecoveries(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (bitfield.BitField, error) {\n\tif s.Internal.StateMinerRecoveries == nil {\n\t\treturn *new(bitfield.BitField), ErrNotSupported\n\t}\n\treturn s.Internal.StateMinerRecoveries(p0, p1, p2)\n}\n```", + "summary": "StateMinerRecoveries returns a bitfield indicating the recovering sectors of the given miner\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "bitfield.BitField", + "description": "bitfield.BitField", + "summary": "", + "schema": { + "examples": [ + [ + 5, + 1 + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3719" + } + }, + { + "name": "Filecoin.StateMinerSectorAllocated", + "description": "```go\nfunc (s *FullNodeStruct) StateMinerSectorAllocated(p0 context.Context, p1 address.Address, p2 abi.SectorNumber, p3 types.TipSetKey) (bool, error) {\n\tif s.Internal.StateMinerSectorAllocated == nil {\n\t\treturn false, ErrNotSupported\n\t}\n\treturn s.Internal.StateMinerSectorAllocated(p0, p1, p2, p3)\n}\n```", + "summary": "StateMinerSectorAllocated checks if a sector number is marked as allocated.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "abi.SectorNumber", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 9 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p3", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "bool", + "description": "bool", + "summary": "", + "schema": { + "examples": [ + true + ], + "type": [ + "boolean" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3730" + } + }, + { + "name": "Filecoin.StateMinerSectorCount", + "description": "```go\nfunc (s *FullNodeStruct) StateMinerSectorCount(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (MinerSectors, error) {\n\tif s.Internal.StateMinerSectorCount == nil {\n\t\treturn *new(MinerSectors), ErrNotSupported\n\t}\n\treturn s.Internal.StateMinerSectorCount(p0, p1, p2)\n}\n```", + "summary": "StateMinerSectorCount returns the number of sectors in a miner's sector set and proving set\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "MinerSectors", + "description": "MinerSectors", + "summary": "", + "schema": { + "examples": [ + { + "Live": 42, + "Active": 42, + "Faulty": 42 + } + ], + "additionalProperties": false, + "properties": { + "Active": { + "title": "number", + "type": "number" + }, + "Faulty": { + "title": "number", + "type": "number" + }, + "Live": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3741" + } + }, + { + "name": "Filecoin.StateMinerSectors", + "description": "```go\nfunc (s *FullNodeStruct) StateMinerSectors(p0 context.Context, p1 address.Address, p2 *bitfield.BitField, p3 types.TipSetKey) ([]*miner.SectorOnChainInfo, error) {\n\tif s.Internal.StateMinerSectors == nil {\n\t\treturn *new([]*miner.SectorOnChainInfo), ErrNotSupported\n\t}\n\treturn s.Internal.StateMinerSectors(p0, p1, p2, p3)\n}\n```", + "summary": "StateMinerSectors returns info about the given miner's sectors. If the filter bitfield is nil, all sectors are included.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "*bitfield.BitField", + "summary": "", + "schema": { + "examples": [ + [ + 0 + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p3", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "[]*miner.SectorOnChainInfo", + "description": "[]*miner.SectorOnChainInfo", + "summary": "", + "schema": { + "examples": [ + [ + { + "SectorNumber": 9, + "SealProof": 8, + "SealedCID": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "DealIDs": [ + 5432 + ], + "Activation": 10101, + "Expiration": 10101, + "DealWeight": "0", + "VerifiedDealWeight": "0", + "InitialPledge": "0", + "ExpectedDayReward": "0", + "ExpectedStoragePledge": "0", + "PowerBaseEpoch": 10101, + "ReplacedDayReward": "0", + "SectorKeyCID": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Flags": 0 + } + ] + ], + "items": [ + { + "additionalProperties": false, + "properties": { + "Activation": { + "title": "number", + "type": "number" + }, + "DealIDs": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "type": "array" + }, + "DealWeight": { + "additionalProperties": false, + "type": "object" + }, + "ExpectedDayReward": { + "additionalProperties": false, + "type": "object" + }, + "ExpectedStoragePledge": { + "additionalProperties": false, + "type": "object" + }, + "Expiration": { + "title": "number", + "type": "number" + }, + "Flags": { + "title": "number", + "type": "number" + }, + "InitialPledge": { + "additionalProperties": false, + "type": "object" + }, + "PowerBaseEpoch": { + "title": "number", + "type": "number" + }, + "ReplacedDayReward": { + "additionalProperties": false, + "type": "object" + }, + "SealProof": { + "title": "number", + "type": "number" + }, + "SealedCID": { + "title": "Content Identifier", + "type": "string" + }, + "SectorKeyCID": { + "title": "Content Identifier", + "type": "string" + }, + "SectorNumber": { + "title": "number", + "type": "number" + }, + "VerifiedDealWeight": { + "additionalProperties": false, + "type": "object" + } + }, + "type": [ + "object" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3752" + } + }, + { + "name": "Filecoin.StateNetworkName", + "description": "```go\nfunc (s *FullNodeStruct) StateNetworkName(p0 context.Context) (dtypes.NetworkName, error) {\n\tif s.Internal.StateNetworkName == nil {\n\t\treturn *new(dtypes.NetworkName), ErrNotSupported\n\t}\n\treturn s.Internal.StateNetworkName(p0)\n}\n```", + "summary": "StateNetworkName returns the name of the network the node is synced to\n", + "paramStructure": "by-position", + "params": [], + "result": { + "name": "dtypes.NetworkName", + "description": "dtypes.NetworkName", + "summary": "", + "schema": { + "examples": [ + "lotus" + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3763" + } + }, + { + "name": "Filecoin.StateNetworkVersion", + "description": "```go\nfunc (s *FullNodeStruct) StateNetworkVersion(p0 context.Context, p1 types.TipSetKey) (apitypes.NetworkVersion, error) {\n\tif s.Internal.StateNetworkVersion == nil {\n\t\treturn *new(apitypes.NetworkVersion), ErrNotSupported\n\t}\n\treturn s.Internal.StateNetworkVersion(p0, p1)\n}\n```", + "summary": "StateNetworkVersion returns the network version at the given tipset\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "apitypes.NetworkVersion", + "description": "apitypes.NetworkVersion", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 21 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3774" + } + }, + { + "name": "Filecoin.StateReadState", + "description": "```go\nfunc (s *FullNodeStruct) StateReadState(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (*ActorState, error) {\n\tif s.Internal.StateReadState == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.StateReadState(p0, p1, p2)\n}\n```", + "summary": "StateReadState returns the indicated actor's state.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*ActorState", + "description": "*ActorState", + "summary": "", + "schema": { + "examples": [ + { + "Balance": "0", + "Code": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "State": {} + } + ], + "additionalProperties": false, + "properties": { + "Balance": { + "additionalProperties": false, + "type": "object" + }, + "Code": { + "title": "Content Identifier", + "type": "string" + }, + "State": { + "additionalProperties": true, + "type": "object" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3785" + } + }, + { + "name": "Filecoin.StateReplay", + "description": "```go\nfunc (s *FullNodeStruct) StateReplay(p0 context.Context, p1 types.TipSetKey, p2 cid.Cid) (*InvocResult, error) {\n\tif s.Internal.StateReplay == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.StateReplay(p0, p1, p2)\n}\n```", + "summary": "StateReplay replays a given message, assuming it was included in a block in the specified tipset.\n\nIf a tipset key is provided, and a replacing message is not found on chain,\nthe method will return an error saying that the message wasn't found\n\nIf no tipset key is provided, the appropriate tipset is looked up, and if\nthe message was gas-repriced, the on-chain message will be replayed - in\nthat case the returned InvocResult.MsgCid will not match the Cid param\n\nIf the caller wants to ensure that exactly the requested message was executed,\nthey MUST check that InvocResult.MsgCid is equal to the provided Cid.\nWithout this check both the requested and original message may appear as\nsuccessfully executed on-chain, which may look like a double-spend.\n\nA replacing message is a message with a different CID, any of Gas values, and\ndifferent signature, but with all other parameters matching (source/destination,\nnonce, params, etc.)\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "cid.Cid", + "summary": "", + "schema": { + "title": "Content Identifier", + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "examples": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*InvocResult", + "description": "*InvocResult", + "summary": "", + "schema": { + "examples": [ + { + "MsgCid": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Msg": { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + }, + "MsgRct": { + "ExitCode": 0, + "Return": "Ynl0ZSBhcnJheQ==", + "GasUsed": 9, + "EventsRoot": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + }, + "GasCost": { + "Message": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "GasUsed": "0", + "BaseFeeBurn": "0", + "OverEstimationBurn": "0", + "MinerPenalty": "0", + "MinerTip": "0", + "Refund": "0", + "TotalCost": "0" + }, + "ExecutionTrace": { + "Msg": { + "From": "f01234", + "To": "f01234", + "Value": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "ParamsCodec": 42, + "GasLimit": 42, + "ReadOnly": true + }, + "MsgRct": { + "ExitCode": 0, + "Return": "Ynl0ZSBhcnJheQ==", + "ReturnCodec": 42 + }, + "InvokedActor": { + "Id": 1000, + "State": { + "Code": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Head": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Nonce": 42, + "Balance": "0", + "Address": "f01234" + } + }, + "GasCharges": [ + { + "Name": "string value", + "tg": 9, + "cg": 9, + "sg": 9, + "tt": 60000000000 + } + ], + "Subcalls": [ + { + "Msg": { + "From": "f01234", + "To": "f01234", + "Value": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "ParamsCodec": 42, + "GasLimit": 42, + "ReadOnly": true + }, + "MsgRct": { + "ExitCode": 0, + "Return": "Ynl0ZSBhcnJheQ==", + "ReturnCodec": 42 + }, + "InvokedActor": { + "Id": 1000, + "State": { + "Code": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Head": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Nonce": 42, + "Balance": "0", + "Address": "f01234" + } + }, + "GasCharges": [ + { + "Name": "string value", + "tg": 9, + "cg": 9, + "sg": 9, + "tt": 60000000000 + } + ], + "Subcalls": null + } + ] + }, + "Error": "string value", + "Duration": 60000000000 + } + ], + "additionalProperties": false, + "properties": { + "Duration": { + "title": "number", + "type": "number" + }, + "Error": { + "type": "string" + }, + "ExecutionTrace": { + "additionalProperties": false, + "properties": { + "GasCharges": { + "items": { + "additionalProperties": false, + "properties": { + "Name": { + "type": "string" + }, + "cg": { + "title": "number", + "type": "number" + }, + "sg": { + "title": "number", + "type": "number" + }, + "tg": { + "title": "number", + "type": "number" + }, + "tt": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "type": "array" + }, + "InvokedActor": { + "additionalProperties": false, + "properties": { + "Id": { + "title": "number", + "type": "number" + }, + "State": { + "additionalProperties": false, + "properties": { + "Address": { + "additionalProperties": false, + "type": "object" + }, + "Balance": { + "additionalProperties": false, + "type": "object" + }, + "Code": { + "title": "Content Identifier", + "type": "string" + }, + "Head": { + "title": "Content Identifier", + "type": "string" + }, + "Nonce": { + "title": "number", + "type": "number" + } + }, + "type": "object" + } + }, + "type": "object" + }, + "Msg": { + "additionalProperties": false, + "properties": { + "From": { + "additionalProperties": false, + "type": "object" + }, + "GasLimit": { + "title": "number", + "type": "number" + }, + "Method": { + "title": "number", + "type": "number" + }, + "Params": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "ParamsCodec": { + "title": "number", + "type": "number" + }, + "ReadOnly": { + "type": "boolean" + }, + "To": { + "additionalProperties": false, + "type": "object" + }, + "Value": { + "additionalProperties": false, + "type": "object" + } + }, + "type": "object" + }, + "MsgRct": { + "additionalProperties": false, + "properties": { + "ExitCode": { + "title": "number", + "type": "number" + }, + "Return": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "ReturnCodec": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "Subcalls": { + "items": {}, + "type": "array" + } + }, + "type": "object" + }, + "GasCost": { + "additionalProperties": false, + "properties": { + "BaseFeeBurn": { + "additionalProperties": false, + "type": "object" + }, + "GasUsed": { + "additionalProperties": false, + "type": "object" + }, + "Message": { + "title": "Content Identifier", + "type": "string" + }, + "MinerPenalty": { + "additionalProperties": false, + "type": "object" + }, + "MinerTip": { + "additionalProperties": false, + "type": "object" + }, + "OverEstimationBurn": { + "additionalProperties": false, + "type": "object" + }, + "Refund": { + "additionalProperties": false, + "type": "object" + }, + "TotalCost": { + "additionalProperties": false, + "type": "object" + } + }, + "type": "object" + }, + "Msg": { + "additionalProperties": false, + "properties": { + "From": { + "additionalProperties": false, + "type": "object" + }, + "GasFeeCap": { + "additionalProperties": false, + "type": "object" + }, + "GasLimit": { + "title": "number", + "type": "number" + }, + "GasPremium": { + "additionalProperties": false, + "type": "object" + }, + "Method": { + "title": "number", + "type": "number" + }, + "Nonce": { + "title": "number", + "type": "number" + }, + "Params": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "To": { + "additionalProperties": false, + "type": "object" + }, + "Value": { + "additionalProperties": false, + "type": "object" + }, + "Version": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "MsgCid": { + "title": "Content Identifier", + "type": "string" + }, + "MsgRct": { + "additionalProperties": false, + "properties": { + "EventsRoot": { + "title": "Content Identifier", + "type": "string" + }, + "ExitCode": { + "title": "number", + "type": "number" + }, + "GasUsed": { + "title": "number", + "type": "number" + }, + "Return": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + } + }, + "type": "object" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3796" + } + }, + { + "name": "Filecoin.StateSearchMsg", + "description": "```go\nfunc (s *FullNodeStruct) StateSearchMsg(p0 context.Context, p1 types.TipSetKey, p2 cid.Cid, p3 abi.ChainEpoch, p4 bool) (*MsgLookup, error) {\n\tif s.Internal.StateSearchMsg == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.StateSearchMsg(p0, p1, p2, p3, p4)\n}\n```", + "summary": "StateSearchMsg looks back up to limit epochs in the chain for a message, and returns its receipt and the tipset where it was executed\n\nNOTE: If a replacing message is found on chain, this method will return\na MsgLookup for the replacing message - the MsgLookup.Message will be a different\nCID than the one provided in the 'cid' param, MsgLookup.Receipt will contain the\nresult of the execution of the replacing message.\n\nIf the caller wants to ensure that exactly the requested message was executed,\nthey must check that MsgLookup.Message is equal to the provided 'cid', or set the\n`allowReplaced` parameter to false. Without this check, and with `allowReplaced`\nset to true, both the requested and original message may appear as\nsuccessfully executed on-chain, which may look like a double-spend.\n\nA replacing message is a message with a different CID, any of Gas values, and\ndifferent signature, but with all other parameters matching (source/destination,\nnonce, params, etc.)\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "cid.Cid", + "summary": "", + "schema": { + "title": "Content Identifier", + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "examples": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p3", + "description": "abi.ChainEpoch", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 10101 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p4", + "description": "bool", + "summary": "", + "schema": { + "examples": [ + true + ], + "type": [ + "boolean" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*MsgLookup", + "description": "*MsgLookup", + "summary": "", + "schema": { + "examples": [ + { + "Message": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Receipt": { + "ExitCode": 0, + "Return": "Ynl0ZSBhcnJheQ==", + "GasUsed": 9, + "EventsRoot": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + }, + "ReturnDec": {}, + "TipSet": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ], + "Height": 10101 + } + ], + "additionalProperties": false, + "properties": { + "Height": { + "title": "number", + "type": "number" + }, + "Message": { + "title": "Content Identifier", + "type": "string" + }, + "Receipt": { + "additionalProperties": false, + "properties": { + "EventsRoot": { + "title": "Content Identifier", + "type": "string" + }, + "ExitCode": { + "title": "number", + "type": "number" + }, + "GasUsed": { + "title": "number", + "type": "number" + }, + "Return": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + } + }, + "type": "object" + }, + "ReturnDec": { + "additionalProperties": true, + "type": "object" + }, + "TipSet": { + "additionalProperties": false, + "type": "object" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3807" + } + }, + { + "name": "Filecoin.StateSectorExpiration", + "description": "```go\nfunc (s *FullNodeStruct) StateSectorExpiration(p0 context.Context, p1 address.Address, p2 abi.SectorNumber, p3 types.TipSetKey) (*miner.SectorExpiration, error) {\n\tif s.Internal.StateSectorExpiration == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.StateSectorExpiration(p0, p1, p2, p3)\n}\n```", + "summary": "StateSectorExpiration returns epoch at which given sector will expire\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "abi.SectorNumber", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 9 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p3", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*miner.SectorExpiration", + "description": "*miner.SectorExpiration", + "summary": "", + "schema": { + "examples": [ + { + "OnTime": 10101, + "Early": 10101 + } + ], + "additionalProperties": false, + "properties": { + "Early": { + "title": "number", + "type": "number" + }, + "OnTime": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3818" + } + }, + { + "name": "Filecoin.StateSectorGetInfo", + "description": "```go\nfunc (s *FullNodeStruct) StateSectorGetInfo(p0 context.Context, p1 address.Address, p2 abi.SectorNumber, p3 types.TipSetKey) (*miner.SectorOnChainInfo, error) {\n\tif s.Internal.StateSectorGetInfo == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.StateSectorGetInfo(p0, p1, p2, p3)\n}\n```", + "summary": "StateSectorGetInfo returns the on-chain info for the specified miner's sector. Returns null in case the sector info isn't found\nNOTE: returned info.Expiration may not be accurate in some cases, use StateSectorExpiration to get accurate\nexpiration epoch\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "abi.SectorNumber", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 9 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p3", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*miner.SectorOnChainInfo", + "description": "*miner.SectorOnChainInfo", + "summary": "", + "schema": { + "examples": [ + { + "SectorNumber": 9, + "SealProof": 8, + "SealedCID": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "DealIDs": [ + 5432 + ], + "Activation": 10101, + "Expiration": 10101, + "DealWeight": "0", + "VerifiedDealWeight": "0", + "InitialPledge": "0", + "ExpectedDayReward": "0", + "ExpectedStoragePledge": "0", + "PowerBaseEpoch": 10101, + "ReplacedDayReward": "0", + "SectorKeyCID": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Flags": 0 + } + ], + "additionalProperties": false, + "properties": { + "Activation": { + "title": "number", + "type": "number" + }, + "DealIDs": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "type": "array" + }, + "DealWeight": { + "additionalProperties": false, + "type": "object" + }, + "ExpectedDayReward": { + "additionalProperties": false, + "type": "object" + }, + "ExpectedStoragePledge": { + "additionalProperties": false, + "type": "object" + }, + "Expiration": { + "title": "number", + "type": "number" + }, + "Flags": { + "title": "number", + "type": "number" + }, + "InitialPledge": { + "additionalProperties": false, + "type": "object" + }, + "PowerBaseEpoch": { + "title": "number", + "type": "number" + }, + "ReplacedDayReward": { + "additionalProperties": false, + "type": "object" + }, + "SealProof": { + "title": "number", + "type": "number" + }, + "SealedCID": { + "title": "Content Identifier", + "type": "string" + }, + "SectorKeyCID": { + "title": "Content Identifier", + "type": "string" + }, + "SectorNumber": { + "title": "number", + "type": "number" + }, + "VerifiedDealWeight": { + "additionalProperties": false, + "type": "object" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3829" + } + }, + { + "name": "Filecoin.StateSectorPartition", + "description": "```go\nfunc (s *FullNodeStruct) StateSectorPartition(p0 context.Context, p1 address.Address, p2 abi.SectorNumber, p3 types.TipSetKey) (*miner.SectorLocation, error) {\n\tif s.Internal.StateSectorPartition == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.StateSectorPartition(p0, p1, p2, p3)\n}\n```", + "summary": "StateSectorPartition finds deadline/partition with the specified sector\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "abi.SectorNumber", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 9 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p3", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*miner.SectorLocation", + "description": "*miner.SectorLocation", + "summary": "", + "schema": { + "examples": [ + { + "Deadline": 42, + "Partition": 42 + } + ], + "additionalProperties": false, + "properties": { + "Deadline": { + "title": "number", + "type": "number" + }, + "Partition": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3840" + } + }, + { + "name": "Filecoin.StateSectorPreCommitInfo", + "description": "```go\nfunc (s *FullNodeStruct) StateSectorPreCommitInfo(p0 context.Context, p1 address.Address, p2 abi.SectorNumber, p3 types.TipSetKey) (*miner.SectorPreCommitOnChainInfo, error) {\n\tif s.Internal.StateSectorPreCommitInfo == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.StateSectorPreCommitInfo(p0, p1, p2, p3)\n}\n```", + "summary": "StateSectorPreCommitInfo returns the PreCommit info for the specified miner's sector.\nReturns nil and no error if the sector isn't precommitted.\n\nNote that the sector number may be allocated while PreCommitInfo is nil. This means that either allocated sector\nnumbers were compacted, and the sector number was marked as allocated in order to reduce size of the allocated\nsectors bitfield, or that the sector was precommitted, but the precommit has expired.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "abi.SectorNumber", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 9 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p3", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*miner.SectorPreCommitOnChainInfo", + "description": "*miner.SectorPreCommitOnChainInfo", + "summary": "", + "schema": { + "examples": [ + { + "Info": { + "SealProof": 8, + "SectorNumber": 9, + "SealedCID": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "SealRandEpoch": 10101, + "DealIDs": [ + 5432 + ], + "Expiration": 10101, + "UnsealedCid": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + }, + "PreCommitDeposit": "0", + "PreCommitEpoch": 10101 + } + ], + "additionalProperties": false, + "properties": { + "Info": { + "additionalProperties": false, + "properties": { + "DealIDs": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "type": "array" + }, + "Expiration": { + "title": "number", + "type": "number" + }, + "SealProof": { + "title": "number", + "type": "number" + }, + "SealRandEpoch": { + "title": "number", + "type": "number" + }, + "SealedCID": { + "title": "Content Identifier", + "type": "string" + }, + "SectorNumber": { + "title": "number", + "type": "number" + }, + "UnsealedCid": { + "title": "Content Identifier", + "type": "string" + } + }, + "type": "object" + }, + "PreCommitDeposit": { + "additionalProperties": false, + "type": "object" + }, + "PreCommitEpoch": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3851" + } + }, + { + "name": "Filecoin.StateVMCirculatingSupplyInternal", + "description": "```go\nfunc (s *FullNodeStruct) StateVMCirculatingSupplyInternal(p0 context.Context, p1 types.TipSetKey) (CirculatingSupply, error) {\n\tif s.Internal.StateVMCirculatingSupplyInternal == nil {\n\t\treturn *new(CirculatingSupply), ErrNotSupported\n\t}\n\treturn s.Internal.StateVMCirculatingSupplyInternal(p0, p1)\n}\n```", + "summary": "StateVMCirculatingSupplyInternal returns an approximation of the circulating supply of Filecoin at the given tipset.\nThis is the value reported by the runtime interface to actors code.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "CirculatingSupply", + "description": "CirculatingSupply", + "summary": "", + "schema": { + "examples": [ + { + "FilVested": "0", + "FilMined": "0", + "FilBurnt": "0", + "FilLocked": "0", + "FilCirculating": "0", + "FilReserveDisbursed": "0" + } + ], + "additionalProperties": false, + "properties": { + "FilBurnt": { + "additionalProperties": false, + "type": "object" + }, + "FilCirculating": { + "additionalProperties": false, + "type": "object" + }, + "FilLocked": { + "additionalProperties": false, + "type": "object" + }, + "FilMined": { + "additionalProperties": false, + "type": "object" + }, + "FilReserveDisbursed": { + "additionalProperties": false, + "type": "object" + }, + "FilVested": { + "additionalProperties": false, + "type": "object" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3862" + } + }, + { + "name": "Filecoin.StateVerifiedClientStatus", + "description": "```go\nfunc (s *FullNodeStruct) StateVerifiedClientStatus(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (*abi.StoragePower, error) {\n\tif s.Internal.StateVerifiedClientStatus == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.StateVerifiedClientStatus(p0, p1, p2)\n}\n```", + "summary": "StateVerifiedClientStatus returns the data cap for the given address.\nReturns nil if there is no entry in the data cap table for the\naddress.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*abi.StoragePower", + "description": "*abi.StoragePower", + "summary": "", + "schema": { + "examples": [ + "0" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3873" + } + }, + { + "name": "Filecoin.StateVerifiedRegistryRootKey", + "description": "```go\nfunc (s *FullNodeStruct) StateVerifiedRegistryRootKey(p0 context.Context, p1 types.TipSetKey) (address.Address, error) {\n\tif s.Internal.StateVerifiedRegistryRootKey == nil {\n\t\treturn *new(address.Address), ErrNotSupported\n\t}\n\treturn s.Internal.StateVerifiedRegistryRootKey(p0, p1)\n}\n```", + "summary": "StateVerifiedRegistryRootKey returns the address of the Verified Registry's root key\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "address.Address", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3884" + } + }, + { + "name": "Filecoin.StateVerifierStatus", + "description": "```go\nfunc (s *FullNodeStruct) StateVerifierStatus(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (*abi.StoragePower, error) {\n\tif s.Internal.StateVerifierStatus == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.StateVerifierStatus(p0, p1, p2)\n}\n```", + "summary": "StateVerifierStatus returns the data cap for the given address.\nReturns nil if there is no entry in the data cap table for the\naddress.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*abi.StoragePower", + "description": "*abi.StoragePower", + "summary": "", + "schema": { + "examples": [ + "0" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3895" + } + }, + { + "name": "Filecoin.StateWaitMsg", + "description": "```go\nfunc (s *FullNodeStruct) StateWaitMsg(p0 context.Context, p1 cid.Cid, p2 uint64, p3 abi.ChainEpoch, p4 bool) (*MsgLookup, error) {\n\tif s.Internal.StateWaitMsg == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.StateWaitMsg(p0, p1, p2, p3, p4)\n}\n```", + "summary": "StateWaitMsg looks back up to limit epochs in the chain for a message.\nIf not found, it blocks until the message arrives on chain, and gets to the\nindicated confidence depth.\n\nNOTE: If a replacing message is found on chain, this method will return\na MsgLookup for the replacing message - the MsgLookup.Message will be a different\nCID than the one provided in the 'cid' param, MsgLookup.Receipt will contain the\nresult of the execution of the replacing message.\n\nIf the caller wants to ensure that exactly the requested message was executed,\nthey must check that MsgLookup.Message is equal to the provided 'cid', or set the\n`allowReplaced` parameter to false. Without this check, and with `allowReplaced`\nset to true, both the requested and original message may appear as\nsuccessfully executed on-chain, which may look like a double-spend.\n\nA replacing message is a message with a different CID, any of Gas values, and\ndifferent signature, but with all other parameters matching (source/destination,\nnonce, params, etc.)\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "cid.Cid", + "summary": "", + "schema": { + "title": "Content Identifier", + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "examples": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "uint64", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 42 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p3", + "description": "abi.ChainEpoch", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 10101 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p4", + "description": "bool", + "summary": "", + "schema": { + "examples": [ + true + ], + "type": [ + "boolean" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*MsgLookup", + "description": "*MsgLookup", + "summary": "", + "schema": { + "examples": [ + { + "Message": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Receipt": { + "ExitCode": 0, + "Return": "Ynl0ZSBhcnJheQ==", + "GasUsed": 9, + "EventsRoot": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + }, + "ReturnDec": {}, + "TipSet": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ], + "Height": 10101 + } + ], + "additionalProperties": false, + "properties": { + "Height": { + "title": "number", + "type": "number" + }, + "Message": { + "title": "Content Identifier", + "type": "string" + }, + "Receipt": { + "additionalProperties": false, + "properties": { + "EventsRoot": { + "title": "Content Identifier", + "type": "string" + }, + "ExitCode": { + "title": "number", + "type": "number" + }, + "GasUsed": { + "title": "number", + "type": "number" + }, + "Return": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + } + }, + "type": "object" + }, + "ReturnDec": { + "additionalProperties": true, + "type": "object" + }, + "TipSet": { + "additionalProperties": false, + "type": "object" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3906" + } + }, + { + "name": "Filecoin.SyncCheckBad", + "description": "```go\nfunc (s *FullNodeStruct) SyncCheckBad(p0 context.Context, p1 cid.Cid) (string, error) {\n\tif s.Internal.SyncCheckBad == nil {\n\t\treturn \"\", ErrNotSupported\n\t}\n\treturn s.Internal.SyncCheckBad(p0, p1)\n}\n```", + "summary": "SyncCheckBad checks if a block was marked as bad, and if it was, returns\nthe reason.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "cid.Cid", + "summary": "", + "schema": { + "title": "Content Identifier", + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "examples": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "string", + "description": "string", + "summary": "", + "schema": { + "examples": [ + "string value" + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3917" + } + }, + { + "name": "Filecoin.SyncCheckpoint", + "description": "```go\nfunc (s *FullNodeStruct) SyncCheckpoint(p0 context.Context, p1 types.TipSetKey) error {\n\tif s.Internal.SyncCheckpoint == nil {\n\t\treturn ErrNotSupported\n\t}\n\treturn s.Internal.SyncCheckpoint(p0, p1)\n}\n```", + "summary": "SyncCheckpoint marks a blocks as checkpointed, meaning that it won't ever fork away from it.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "Null", + "description": "Null", + "schema": { + "type": [ + "null" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3928" + } + }, + { + "name": "Filecoin.SyncMarkBad", + "description": "```go\nfunc (s *FullNodeStruct) SyncMarkBad(p0 context.Context, p1 cid.Cid) error {\n\tif s.Internal.SyncMarkBad == nil {\n\t\treturn ErrNotSupported\n\t}\n\treturn s.Internal.SyncMarkBad(p0, p1)\n}\n```", + "summary": "SyncMarkBad marks a blocks as bad, meaning that it won't ever by synced.\nUse with extreme caution.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "cid.Cid", + "summary": "", + "schema": { + "title": "Content Identifier", + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "examples": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "Null", + "description": "Null", + "schema": { + "type": [ + "null" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3950" + } + }, + { + "name": "Filecoin.SyncState", + "description": "```go\nfunc (s *FullNodeStruct) SyncState(p0 context.Context) (*SyncState, error) {\n\tif s.Internal.SyncState == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.SyncState(p0)\n}\n```", + "summary": "SyncState returns the current status of the lotus sync system.\n", + "paramStructure": "by-position", + "params": [], + "result": { + "name": "*SyncState", + "description": "*SyncState", + "summary": "", + "schema": { + "examples": [ + { + "ActiveSyncs": [ + { + "WorkerID": 42, + "Base": { + "Cids": null, + "Blocks": null, + "Height": 0 + }, + "Target": { + "Cids": null, + "Blocks": null, + "Height": 0 + }, + "Stage": 1, + "Height": 10101, + "Start": "0001-01-01T00:00:00Z", + "End": "0001-01-01T00:00:00Z", + "Message": "string value" + } + ], + "VMApplied": 42 + } + ], + "additionalProperties": false, + "properties": { + "ActiveSyncs": { + "items": { + "additionalProperties": false, + "properties": { + "Base": { + "additionalProperties": false, + "type": "object" + }, + "End": { + "format": "date-time", + "type": "string" + }, + "Height": { + "title": "number", + "type": "number" + }, + "Message": { + "type": "string" + }, + "Stage": { + "title": "number", + "type": "number" + }, + "Start": { + "format": "date-time", + "type": "string" + }, + "Target": { + "additionalProperties": false, + "type": "object" + }, + "WorkerID": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "type": "array" + }, + "VMApplied": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3961" + } + }, + { + "name": "Filecoin.SyncSubmitBlock", + "description": "```go\nfunc (s *FullNodeStruct) SyncSubmitBlock(p0 context.Context, p1 *types.BlockMsg) error {\n\tif s.Internal.SyncSubmitBlock == nil {\n\t\treturn ErrNotSupported\n\t}\n\treturn s.Internal.SyncSubmitBlock(p0, p1)\n}\n```", + "summary": "SyncSubmitBlock can be used to submit a newly created block to the.\nnetwork through this node\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "*types.BlockMsg", + "summary": "", + "schema": { + "examples": [ + { + "Header": { + "Miner": "f01234", + "Ticket": { + "VRFProof": "Ynl0ZSBhcnJheQ==" + }, + "ElectionProof": { + "WinCount": 9, + "VRFProof": "Ynl0ZSBhcnJheQ==" + }, + "BeaconEntries": [ + { + "Round": 42, + "Data": "Ynl0ZSBhcnJheQ==" + } + ], + "WinPoStProof": [ + { + "PoStProof": 8, + "ProofBytes": "Ynl0ZSBhcnJheQ==" + } + ], + "Parents": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ], + "ParentWeight": "0", + "Height": 10101, + "ParentStateRoot": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "ParentMessageReceipts": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Messages": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "BLSAggregate": { + "Type": 2, + "Data": "Ynl0ZSBhcnJheQ==" + }, + "Timestamp": 42, + "BlockSig": { + "Type": 2, + "Data": "Ynl0ZSBhcnJheQ==" + }, + "ForkSignaling": 42, + "ParentBaseFee": "0" + }, + "BlsMessages": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ], + "SecpkMessages": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ] + } + ], + "additionalProperties": false, + "properties": { + "BlsMessages": { + "items": { + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "title": "Content Identifier", + "type": "string" + }, + "type": "array" + }, + "Header": { + "additionalProperties": false, + "properties": { + "BLSAggregate": { + "additionalProperties": false, + "properties": { + "Data": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "Type": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "BeaconEntries": { + "items": { + "additionalProperties": false, + "properties": { + "Data": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "Round": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "type": "array" + }, + "BlockSig": { + "additionalProperties": false, + "properties": { + "Data": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "Type": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "ElectionProof": { + "additionalProperties": false, + "properties": { + "VRFProof": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "WinCount": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "ForkSignaling": { + "title": "number", + "type": "number" + }, + "Height": { + "title": "number", + "type": "number" + }, + "Messages": { + "title": "Content Identifier", + "type": "string" + }, + "Miner": { + "additionalProperties": false, + "type": "object" + }, + "ParentBaseFee": { + "additionalProperties": false, + "type": "object" + }, + "ParentMessageReceipts": { + "title": "Content Identifier", + "type": "string" + }, + "ParentStateRoot": { + "title": "Content Identifier", + "type": "string" + }, + "ParentWeight": { + "additionalProperties": false, + "type": "object" + }, + "Parents": { + "items": { + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "title": "Content Identifier", + "type": "string" + }, + "type": "array" + }, + "Ticket": { + "additionalProperties": false, + "properties": { + "VRFProof": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + } + }, + "type": "object" + }, + "Timestamp": { + "title": "number", + "type": "number" + }, + "WinPoStProof": { + "items": { + "additionalProperties": false, + "properties": { + "PoStProof": { + "title": "number", + "type": "number" + }, + "ProofBytes": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + } + }, + "type": "object" + }, + "type": "array" + } + }, + "type": "object" + }, + "SecpkMessages": { + "items": { + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "title": "Content Identifier", + "type": "string" + }, + "type": "array" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "Null", + "description": "Null", + "schema": { + "type": [ + "null" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3972" + } + }, + { + "name": "Filecoin.SyncUnmarkAllBad", + "description": "```go\nfunc (s *FullNodeStruct) SyncUnmarkAllBad(p0 context.Context) error {\n\tif s.Internal.SyncUnmarkAllBad == nil {\n\t\treturn ErrNotSupported\n\t}\n\treturn s.Internal.SyncUnmarkAllBad(p0)\n}\n```", + "summary": "SyncUnmarkAllBad purges bad block cache, making it possible to sync to chains previously marked as bad\n", + "paramStructure": "by-position", + "params": [], + "result": { + "name": "Null", + "description": "Null", + "schema": { + "type": [ + "null" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3983" + } + }, + { + "name": "Filecoin.SyncUnmarkBad", + "description": "```go\nfunc (s *FullNodeStruct) SyncUnmarkBad(p0 context.Context, p1 cid.Cid) error {\n\tif s.Internal.SyncUnmarkBad == nil {\n\t\treturn ErrNotSupported\n\t}\n\treturn s.Internal.SyncUnmarkBad(p0, p1)\n}\n```", + "summary": "SyncUnmarkBad unmarks a blocks as bad, making it possible to be validated and synced again.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "cid.Cid", + "summary": "", + "schema": { + "title": "Content Identifier", + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "examples": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "Null", + "description": "Null", + "schema": { + "type": [ + "null" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3994" + } + }, + { + "name": "Filecoin.SyncValidateTipset", + "description": "```go\nfunc (s *FullNodeStruct) SyncValidateTipset(p0 context.Context, p1 types.TipSetKey) (bool, error) {\n\tif s.Internal.SyncValidateTipset == nil {\n\t\treturn false, ErrNotSupported\n\t}\n\treturn s.Internal.SyncValidateTipset(p0, p1)\n}\n```", + "summary": "SyncValidateTipset indicates whether the provided tipset is valid or not\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "bool", + "description": "bool", + "summary": "", + "schema": { + "examples": [ + true + ], + "type": [ + "boolean" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4005" + } + }, + { + "name": "Filecoin.WalletBalance", + "description": "```go\nfunc (s *FullNodeStruct) WalletBalance(p0 context.Context, p1 address.Address) (types.BigInt, error) {\n\tif s.Internal.WalletBalance == nil {\n\t\treturn *new(types.BigInt), ErrNotSupported\n\t}\n\treturn s.Internal.WalletBalance(p0, p1)\n}\n```", + "summary": "WalletBalance returns the balance of the given address at the current head of the chain.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "types.BigInt", + "description": "types.BigInt", + "summary": "", + "schema": { + "examples": [ + "0" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4016" + } + }, + { + "name": "Filecoin.WalletDefaultAddress", + "description": "```go\nfunc (s *FullNodeStruct) WalletDefaultAddress(p0 context.Context) (address.Address, error) {\n\tif s.Internal.WalletDefaultAddress == nil {\n\t\treturn *new(address.Address), ErrNotSupported\n\t}\n\treturn s.Internal.WalletDefaultAddress(p0)\n}\n```", + "summary": "WalletDefaultAddress returns the address marked as default in the wallet.\n", + "paramStructure": "by-position", + "params": [], + "result": { + "name": "address.Address", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4027" + } + }, + { + "name": "Filecoin.WalletDelete", + "description": "```go\nfunc (s *FullNodeStruct) WalletDelete(p0 context.Context, p1 address.Address) error {\n\tif s.Internal.WalletDelete == nil {\n\t\treturn ErrNotSupported\n\t}\n\treturn s.Internal.WalletDelete(p0, p1)\n}\n```", + "summary": "WalletDelete deletes an address from the wallet.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "Null", + "description": "Null", + "schema": { + "type": [ + "null" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4038" + } + }, + { + "name": "Filecoin.WalletExport", + "description": "```go\nfunc (s *FullNodeStruct) WalletExport(p0 context.Context, p1 address.Address) (*types.KeyInfo, error) {\n\tif s.Internal.WalletExport == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.WalletExport(p0, p1)\n}\n```", + "summary": "WalletExport returns the private key of an address in the wallet.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*types.KeyInfo", + "description": "*types.KeyInfo", + "summary": "", + "schema": { + "examples": [ + { + "Type": "bls", + "PrivateKey": "Ynl0ZSBhcnJheQ==" + } + ], + "additionalProperties": false, + "properties": { + "PrivateKey": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "Type": { + "type": "string" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4049" + } + }, + { + "name": "Filecoin.WalletHas", + "description": "```go\nfunc (s *FullNodeStruct) WalletHas(p0 context.Context, p1 address.Address) (bool, error) {\n\tif s.Internal.WalletHas == nil {\n\t\treturn false, ErrNotSupported\n\t}\n\treturn s.Internal.WalletHas(p0, p1)\n}\n```", + "summary": "WalletHas indicates whether the given address is in the wallet.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "bool", + "description": "bool", + "summary": "", + "schema": { + "examples": [ + true + ], + "type": [ + "boolean" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4060" + } + }, + { + "name": "Filecoin.WalletImport", + "description": "```go\nfunc (s *FullNodeStruct) WalletImport(p0 context.Context, p1 *types.KeyInfo) (address.Address, error) {\n\tif s.Internal.WalletImport == nil {\n\t\treturn *new(address.Address), ErrNotSupported\n\t}\n\treturn s.Internal.WalletImport(p0, p1)\n}\n```", + "summary": "WalletImport receives a KeyInfo, which includes a private key, and imports it into the wallet.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "*types.KeyInfo", + "summary": "", + "schema": { + "examples": [ + { + "Type": "bls", + "PrivateKey": "Ynl0ZSBhcnJheQ==" + } + ], + "additionalProperties": false, + "properties": { + "PrivateKey": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "Type": { + "type": "string" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "address.Address", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4071" + } + }, + { + "name": "Filecoin.WalletList", + "description": "```go\nfunc (s *FullNodeStruct) WalletList(p0 context.Context) ([]address.Address, error) {\n\tif s.Internal.WalletList == nil {\n\t\treturn *new([]address.Address), ErrNotSupported\n\t}\n\treturn s.Internal.WalletList(p0)\n}\n```", + "summary": "WalletList lists all the addresses in the wallet.\n", + "paramStructure": "by-position", + "params": [], + "result": { + "name": "[]address.Address", + "description": "[]address.Address", + "summary": "", + "schema": { + "examples": [ + [ + "f01234" + ] + ], + "items": [ + { + "additionalProperties": false, + "type": [ + "object" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4082" + } + }, + { + "name": "Filecoin.WalletNew", + "description": "```go\nfunc (s *FullNodeStruct) WalletNew(p0 context.Context, p1 types.KeyType) (address.Address, error) {\n\tif s.Internal.WalletNew == nil {\n\t\treturn *new(address.Address), ErrNotSupported\n\t}\n\treturn s.Internal.WalletNew(p0, p1)\n}\n```", + "summary": "WalletNew creates a new address in the wallet with the given sigType.\nAvailable key types: bls, secp256k1, secp256k1-ledger\nSupport for numerical types: 1 - secp256k1, 2 - BLS is deprecated\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "types.KeyType", + "summary": "", + "schema": { + "examples": [ + "bls" + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "address.Address", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4093" + } + }, + { + "name": "Filecoin.WalletSetDefault", + "description": "```go\nfunc (s *FullNodeStruct) WalletSetDefault(p0 context.Context, p1 address.Address) error {\n\tif s.Internal.WalletSetDefault == nil {\n\t\treturn ErrNotSupported\n\t}\n\treturn s.Internal.WalletSetDefault(p0, p1)\n}\n```", + "summary": "WalletSetDefault marks the given address as as the default one.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "Null", + "description": "Null", + "schema": { + "type": [ + "null" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4104" + } + }, + { + "name": "Filecoin.WalletSign", + "description": "```go\nfunc (s *FullNodeStruct) WalletSign(p0 context.Context, p1 address.Address, p2 []byte) (*crypto.Signature, error) {\n\tif s.Internal.WalletSign == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.WalletSign(p0, p1, p2)\n}\n```", + "summary": "WalletSign signs the given bytes using the given address.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "[]byte", + "summary": "", + "schema": { + "examples": [ + "Ynl0ZSBhcnJheQ==" + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*crypto.Signature", + "description": "*crypto.Signature", + "summary": "", + "schema": { + "examples": [ + { + "Type": 2, + "Data": "Ynl0ZSBhcnJheQ==" + } + ], + "additionalProperties": false, + "properties": { + "Data": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "Type": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4115" + } + }, + { + "name": "Filecoin.WalletSignMessage", + "description": "```go\nfunc (s *FullNodeStruct) WalletSignMessage(p0 context.Context, p1 address.Address, p2 *types.Message) (*types.SignedMessage, error) {\n\tif s.Internal.WalletSignMessage == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.WalletSignMessage(p0, p1, p2)\n}\n```", + "summary": "WalletSignMessage signs the given message using the given address.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "*types.Message", + "summary": "", + "schema": { + "examples": [ + { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + } + ], + "additionalProperties": false, + "properties": { + "From": { + "additionalProperties": false, + "type": "object" + }, + "GasFeeCap": { + "additionalProperties": false, + "type": "object" + }, + "GasLimit": { + "title": "number", + "type": "number" + }, + "GasPremium": { + "additionalProperties": false, + "type": "object" + }, + "Method": { + "title": "number", + "type": "number" + }, + "Nonce": { + "title": "number", + "type": "number" + }, + "Params": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "To": { + "additionalProperties": false, + "type": "object" + }, + "Value": { + "additionalProperties": false, + "type": "object" + }, + "Version": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*types.SignedMessage", + "description": "*types.SignedMessage", + "summary": "", + "schema": { + "examples": [ + { + "Message": { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + }, + "Signature": { + "Type": 2, + "Data": "Ynl0ZSBhcnJheQ==" + }, + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + } + ], + "additionalProperties": false, + "properties": { + "Message": { + "additionalProperties": false, + "properties": { + "From": { + "additionalProperties": false, + "type": "object" + }, + "GasFeeCap": { + "additionalProperties": false, + "type": "object" + }, + "GasLimit": { + "title": "number", + "type": "number" + }, + "GasPremium": { + "additionalProperties": false, + "type": "object" + }, + "Method": { + "title": "number", + "type": "number" + }, + "Nonce": { + "title": "number", + "type": "number" + }, + "Params": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "To": { + "additionalProperties": false, + "type": "object" + }, + "Value": { + "additionalProperties": false, + "type": "object" + }, + "Version": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "Signature": { + "additionalProperties": false, + "properties": { + "Data": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "Type": { + "title": "number", + "type": "number" + } + }, + "type": "object" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4126" + } + }, + { + "name": "Filecoin.WalletValidateAddress", + "description": "```go\nfunc (s *FullNodeStruct) WalletValidateAddress(p0 context.Context, p1 string) (address.Address, error) {\n\tif s.Internal.WalletValidateAddress == nil {\n\t\treturn *new(address.Address), ErrNotSupported\n\t}\n\treturn s.Internal.WalletValidateAddress(p0, p1)\n}\n```", + "summary": "WalletValidateAddress validates whether a given string can be decoded as a well-formed address\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "string", + "summary": "", + "schema": { + "examples": [ + "string value" + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "address.Address", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4137" + } + }, + { + "name": "Filecoin.WalletVerify", + "description": "```go\nfunc (s *FullNodeStruct) WalletVerify(p0 context.Context, p1 address.Address, p2 []byte, p3 *crypto.Signature) (bool, error) {\n\tif s.Internal.WalletVerify == nil {\n\t\treturn false, ErrNotSupported\n\t}\n\treturn s.Internal.WalletVerify(p0, p1, p2, p3)\n}\n```", + "summary": "WalletVerify takes an address, a signature, and some bytes, and indicates whether the signature is valid.\nThe address does not have to be in the wallet.\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "[]byte", + "summary": "", + "schema": { + "examples": [ + "Ynl0ZSBhcnJheQ==" + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p3", + "description": "*crypto.Signature", + "summary": "", + "schema": { + "examples": [ + { + "Type": 2, + "Data": "Ynl0ZSBhcnJheQ==" + } + ], + "additionalProperties": false, + "properties": { + "Data": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "Type": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "bool", + "description": "bool", + "summary": "", + "schema": { + "examples": [ + true + ], + "type": [ + "boolean" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4148" + } + }, + { + "name": "Filecoin.Web3ClientVersion", + "description": "```go\nfunc (s *FullNodeStruct) Web3ClientVersion(p0 context.Context) (string, error) {\n\tif s.Internal.Web3ClientVersion == nil {\n\t\treturn \"\", ErrNotSupported\n\t}\n\treturn s.Internal.Web3ClientVersion(p0)\n}\n```", + "summary": "Returns the client version\n", + "paramStructure": "by-position", + "params": [], + "result": { + "name": "string", + "description": "string", + "summary": "", + "schema": { + "examples": [ + "string value" + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4159" + } + } + ] +} \ No newline at end of file diff --git a/build/openrpc/full.json.gz b/build/openrpc/full.json.gz deleted file mode 100644 index dc9bc44ec13c8002b79e97069b6d6e6e6ed4ece5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 34712 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 bo}RDXZhZg#?fd@^00960^n4>NeE9|deTSL` diff --git a/build/openrpc/gateway.json b/build/openrpc/gateway.json new file mode 100644 index 000000000..42521f0c8 --- /dev/null +++ b/build/openrpc/gateway.json @@ -0,0 +1,9909 @@ +{ + "openrpc": "1.2.6", + "info": { + "title": "Lotus RPC API", + "version": "1.25.3-dev" + }, + "methods": [ + { + "name": "Filecoin.ChainGetBlock", + "description": "```go\nfunc (s *GatewayStruct) ChainGetBlock(p0 context.Context, p1 cid.Cid) (*types.BlockHeader, error) {\n\tif s.Internal.ChainGetBlock == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.ChainGetBlock(p0, p1)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "cid.Cid", + "summary": "", + "schema": { + "title": "Content Identifier", + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "examples": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*types.BlockHeader", + "description": "*types.BlockHeader", + "summary": "", + "schema": { + "examples": [ + { + "Miner": "f01234", + "Ticket": { + "VRFProof": "Ynl0ZSBhcnJheQ==" + }, + "ElectionProof": { + "WinCount": 9, + "VRFProof": "Ynl0ZSBhcnJheQ==" + }, + "BeaconEntries": [ + { + "Round": 42, + "Data": "Ynl0ZSBhcnJheQ==" + } + ], + "WinPoStProof": [ + { + "PoStProof": 8, + "ProofBytes": "Ynl0ZSBhcnJheQ==" + } + ], + "Parents": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ], + "ParentWeight": "0", + "Height": 10101, + "ParentStateRoot": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "ParentMessageReceipts": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Messages": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "BLSAggregate": { + "Type": 2, + "Data": "Ynl0ZSBhcnJheQ==" + }, + "Timestamp": 42, + "BlockSig": { + "Type": 2, + "Data": "Ynl0ZSBhcnJheQ==" + }, + "ForkSignaling": 42, + "ParentBaseFee": "0" + } + ], + "additionalProperties": false, + "properties": { + "BLSAggregate": { + "additionalProperties": false, + "properties": { + "Data": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "Type": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "BeaconEntries": { + "items": { + "additionalProperties": false, + "properties": { + "Data": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "Round": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "type": "array" + }, + "BlockSig": { + "additionalProperties": false, + "properties": { + "Data": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "Type": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "ElectionProof": { + "additionalProperties": false, + "properties": { + "VRFProof": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "WinCount": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "ForkSignaling": { + "title": "number", + "type": "number" + }, + "Height": { + "title": "number", + "type": "number" + }, + "Messages": { + "title": "Content Identifier", + "type": "string" + }, + "Miner": { + "additionalProperties": false, + "type": "object" + }, + "ParentBaseFee": { + "additionalProperties": false, + "type": "object" + }, + "ParentMessageReceipts": { + "title": "Content Identifier", + "type": "string" + }, + "ParentStateRoot": { + "title": "Content Identifier", + "type": "string" + }, + "ParentWeight": { + "additionalProperties": false, + "type": "object" + }, + "Parents": { + "items": { + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "title": "Content Identifier", + "type": "string" + }, + "type": "array" + }, + "Ticket": { + "additionalProperties": false, + "properties": { + "VRFProof": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + } + }, + "type": "object" + }, + "Timestamp": { + "title": "number", + "type": "number" + }, + "WinPoStProof": { + "items": { + "additionalProperties": false, + "properties": { + "PoStProof": { + "title": "number", + "type": "number" + }, + "ProofBytes": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + } + }, + "type": "object" + }, + "type": "array" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4170" + } + }, + { + "name": "Filecoin.ChainGetBlockMessages", + "description": "```go\nfunc (s *GatewayStruct) ChainGetBlockMessages(p0 context.Context, p1 cid.Cid) (*BlockMessages, error) {\n\tif s.Internal.ChainGetBlockMessages == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.ChainGetBlockMessages(p0, p1)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "cid.Cid", + "summary": "", + "schema": { + "title": "Content Identifier", + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "examples": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*BlockMessages", + "description": "*BlockMessages", + "summary": "", + "schema": { + "examples": [ + { + "BlsMessages": [ + { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + } + ], + "SecpkMessages": [ + { + "Message": { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + }, + "Signature": { + "Type": 2, + "Data": "Ynl0ZSBhcnJheQ==" + }, + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + } + ], + "Cids": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ] + } + ], + "additionalProperties": false, + "properties": { + "BlsMessages": { + "items": { + "additionalProperties": false, + "properties": { + "From": { + "additionalProperties": false, + "type": "object" + }, + "GasFeeCap": { + "additionalProperties": false, + "type": "object" + }, + "GasLimit": { + "title": "number", + "type": "number" + }, + "GasPremium": { + "additionalProperties": false, + "type": "object" + }, + "Method": { + "title": "number", + "type": "number" + }, + "Nonce": { + "title": "number", + "type": "number" + }, + "Params": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "To": { + "additionalProperties": false, + "type": "object" + }, + "Value": { + "additionalProperties": false, + "type": "object" + }, + "Version": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "type": "array" + }, + "Cids": { + "items": { + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "title": "Content Identifier", + "type": "string" + }, + "type": "array" + }, + "SecpkMessages": { + "items": { + "additionalProperties": false, + "properties": { + "Message": { + "additionalProperties": false, + "properties": { + "From": { + "additionalProperties": false, + "type": "object" + }, + "GasFeeCap": { + "additionalProperties": false, + "type": "object" + }, + "GasLimit": { + "title": "number", + "type": "number" + }, + "GasPremium": { + "additionalProperties": false, + "type": "object" + }, + "Method": { + "title": "number", + "type": "number" + }, + "Nonce": { + "title": "number", + "type": "number" + }, + "Params": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "To": { + "additionalProperties": false, + "type": "object" + }, + "Value": { + "additionalProperties": false, + "type": "object" + }, + "Version": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "Signature": { + "additionalProperties": false, + "properties": { + "Data": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "Type": { + "title": "number", + "type": "number" + } + }, + "type": "object" + } + }, + "type": "object" + }, + "type": "array" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4181" + } + }, + { + "name": "Filecoin.ChainGetGenesis", + "description": "```go\nfunc (s *GatewayStruct) ChainGetGenesis(p0 context.Context) (*types.TipSet, error) {\n\tif s.Internal.ChainGetGenesis == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.ChainGetGenesis(p0)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [], + "result": { + "name": "*types.TipSet", + "description": "*types.TipSet", + "summary": "", + "schema": { + "examples": [ + { + "Cids": null, + "Blocks": null, + "Height": 0 + } + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4192" + } + }, + { + "name": "Filecoin.ChainGetMessage", + "description": "```go\nfunc (s *GatewayStruct) ChainGetMessage(p0 context.Context, p1 cid.Cid) (*types.Message, error) {\n\tif s.Internal.ChainGetMessage == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.ChainGetMessage(p0, p1)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "cid.Cid", + "summary": "", + "schema": { + "title": "Content Identifier", + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "examples": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*types.Message", + "description": "*types.Message", + "summary": "", + "schema": { + "examples": [ + { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + } + ], + "additionalProperties": false, + "properties": { + "From": { + "additionalProperties": false, + "type": "object" + }, + "GasFeeCap": { + "additionalProperties": false, + "type": "object" + }, + "GasLimit": { + "title": "number", + "type": "number" + }, + "GasPremium": { + "additionalProperties": false, + "type": "object" + }, + "Method": { + "title": "number", + "type": "number" + }, + "Nonce": { + "title": "number", + "type": "number" + }, + "Params": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "To": { + "additionalProperties": false, + "type": "object" + }, + "Value": { + "additionalProperties": false, + "type": "object" + }, + "Version": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4203" + } + }, + { + "name": "Filecoin.ChainGetParentMessages", + "description": "```go\nfunc (s *GatewayStruct) ChainGetParentMessages(p0 context.Context, p1 cid.Cid) ([]Message, error) {\n\tif s.Internal.ChainGetParentMessages == nil {\n\t\treturn *new([]Message), ErrNotSupported\n\t}\n\treturn s.Internal.ChainGetParentMessages(p0, p1)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "cid.Cid", + "summary": "", + "schema": { + "title": "Content Identifier", + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "examples": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "[]Message", + "description": "[]Message", + "summary": "", + "schema": { + "examples": [ + [ + { + "Cid": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Message": { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + } + } + ] + ], + "items": [ + { + "additionalProperties": false, + "properties": { + "Cid": { + "title": "Content Identifier", + "type": "string" + }, + "Message": { + "additionalProperties": false, + "properties": { + "Cid": { + "title": "Content Identifier", + "type": "string" + }, + "Message": {} + }, + "type": "object" + } + }, + "type": [ + "object" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4214" + } + }, + { + "name": "Filecoin.ChainGetParentReceipts", + "description": "```go\nfunc (s *GatewayStruct) ChainGetParentReceipts(p0 context.Context, p1 cid.Cid) ([]*types.MessageReceipt, error) {\n\tif s.Internal.ChainGetParentReceipts == nil {\n\t\treturn *new([]*types.MessageReceipt), ErrNotSupported\n\t}\n\treturn s.Internal.ChainGetParentReceipts(p0, p1)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "cid.Cid", + "summary": "", + "schema": { + "title": "Content Identifier", + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "examples": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "[]*types.MessageReceipt", + "description": "[]*types.MessageReceipt", + "summary": "", + "schema": { + "examples": [ + [ + { + "ExitCode": 0, + "Return": "Ynl0ZSBhcnJheQ==", + "GasUsed": 9, + "EventsRoot": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + } + ] + ], + "items": [ + { + "additionalProperties": false, + "properties": { + "EventsRoot": { + "title": "Content Identifier", + "type": "string" + }, + "ExitCode": { + "title": "number", + "type": "number" + }, + "GasUsed": { + "title": "number", + "type": "number" + }, + "Return": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + } + }, + "type": [ + "object" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4225" + } + }, + { + "name": "Filecoin.ChainGetPath", + "description": "```go\nfunc (s *GatewayStruct) ChainGetPath(p0 context.Context, p1 types.TipSetKey, p2 types.TipSetKey) ([]*HeadChange, error) {\n\tif s.Internal.ChainGetPath == nil {\n\t\treturn *new([]*HeadChange), ErrNotSupported\n\t}\n\treturn s.Internal.ChainGetPath(p0, p1, p2)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "[]*HeadChange", + "description": "[]*HeadChange", + "summary": "", + "schema": { + "examples": [ + [ + { + "Type": "string value", + "Val": { + "Cids": null, + "Blocks": null, + "Height": 0 + } + } + ] + ], + "items": [ + { + "additionalProperties": false, + "properties": { + "Type": { + "type": "string" + }, + "Val": { + "additionalProperties": false, + "type": "object" + } + }, + "type": [ + "object" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4236" + } + }, + { + "name": "Filecoin.ChainGetTipSet", + "description": "```go\nfunc (s *GatewayStruct) ChainGetTipSet(p0 context.Context, p1 types.TipSetKey) (*types.TipSet, error) {\n\tif s.Internal.ChainGetTipSet == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.ChainGetTipSet(p0, p1)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*types.TipSet", + "description": "*types.TipSet", + "summary": "", + "schema": { + "examples": [ + { + "Cids": null, + "Blocks": null, + "Height": 0 + } + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4247" + } + }, + { + "name": "Filecoin.ChainGetTipSetAfterHeight", + "description": "```go\nfunc (s *GatewayStruct) ChainGetTipSetAfterHeight(p0 context.Context, p1 abi.ChainEpoch, p2 types.TipSetKey) (*types.TipSet, error) {\n\tif s.Internal.ChainGetTipSetAfterHeight == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.ChainGetTipSetAfterHeight(p0, p1, p2)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "abi.ChainEpoch", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 10101 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*types.TipSet", + "description": "*types.TipSet", + "summary": "", + "schema": { + "examples": [ + { + "Cids": null, + "Blocks": null, + "Height": 0 + } + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4258" + } + }, + { + "name": "Filecoin.ChainGetTipSetByHeight", + "description": "```go\nfunc (s *GatewayStruct) ChainGetTipSetByHeight(p0 context.Context, p1 abi.ChainEpoch, p2 types.TipSetKey) (*types.TipSet, error) {\n\tif s.Internal.ChainGetTipSetByHeight == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.ChainGetTipSetByHeight(p0, p1, p2)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "abi.ChainEpoch", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 10101 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*types.TipSet", + "description": "*types.TipSet", + "summary": "", + "schema": { + "examples": [ + { + "Cids": null, + "Blocks": null, + "Height": 0 + } + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4269" + } + }, + { + "name": "Filecoin.ChainHasObj", + "description": "```go\nfunc (s *GatewayStruct) ChainHasObj(p0 context.Context, p1 cid.Cid) (bool, error) {\n\tif s.Internal.ChainHasObj == nil {\n\t\treturn false, ErrNotSupported\n\t}\n\treturn s.Internal.ChainHasObj(p0, p1)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "cid.Cid", + "summary": "", + "schema": { + "title": "Content Identifier", + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "examples": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "bool", + "description": "bool", + "summary": "", + "schema": { + "examples": [ + true + ], + "type": [ + "boolean" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4280" + } + }, + { + "name": "Filecoin.ChainHead", + "description": "```go\nfunc (s *GatewayStruct) ChainHead(p0 context.Context) (*types.TipSet, error) {\n\tif s.Internal.ChainHead == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.ChainHead(p0)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [], + "result": { + "name": "*types.TipSet", + "description": "*types.TipSet", + "summary": "", + "schema": { + "examples": [ + { + "Cids": null, + "Blocks": null, + "Height": 0 + } + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4291" + } + }, + { + "name": "Filecoin.ChainPutObj", + "description": "```go\nfunc (s *GatewayStruct) ChainPutObj(p0 context.Context, p1 blocks.Block) error {\n\tif s.Internal.ChainPutObj == nil {\n\t\treturn ErrNotSupported\n\t}\n\treturn s.Internal.ChainPutObj(p0, p1)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "blocks.Block", + "summary": "", + "schema": { + "examples": [ + {} + ], + "additionalProperties": true + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "Null", + "description": "Null", + "schema": { + "type": [ + "null" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4313" + } + }, + { + "name": "Filecoin.ChainReadObj", + "description": "```go\nfunc (s *GatewayStruct) ChainReadObj(p0 context.Context, p1 cid.Cid) ([]byte, error) {\n\tif s.Internal.ChainReadObj == nil {\n\t\treturn *new([]byte), ErrNotSupported\n\t}\n\treturn s.Internal.ChainReadObj(p0, p1)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "cid.Cid", + "summary": "", + "schema": { + "title": "Content Identifier", + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "examples": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "[]byte", + "description": "[]byte", + "summary": "", + "schema": { + "examples": [ + "Ynl0ZSBhcnJheQ==" + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4324" + } + }, + { + "name": "Filecoin.Discover", + "description": "```go\nfunc (s *GatewayStruct) Discover(p0 context.Context) (apitypes.OpenRPCDocument, error) {\n\tif s.Internal.Discover == nil {\n\t\treturn *new(apitypes.OpenRPCDocument), ErrNotSupported\n\t}\n\treturn s.Internal.Discover(p0)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [], + "result": { + "name": "apitypes.OpenRPCDocument", + "description": "apitypes.OpenRPCDocument", + "summary": "", + "schema": { + "examples": [ + { + "info": { + "title": "Lotus RPC API", + "version": "1.2.1/generated=2020-11-22T08:22:42-06:00" + }, + "methods": [], + "openrpc": "1.2.6" + } + ], + "patternProperties": { + ".*": { + "additionalProperties": true, + "type": "object" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4335" + } + }, + { + "name": "Filecoin.EthAccounts", + "description": "```go\nfunc (s *GatewayStruct) EthAccounts(p0 context.Context) ([]ethtypes.EthAddress, error) {\n\tif s.Internal.EthAccounts == nil {\n\t\treturn *new([]ethtypes.EthAddress), ErrNotSupported\n\t}\n\treturn s.Internal.EthAccounts(p0)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [], + "result": { + "name": "[]ethtypes.EthAddress", + "description": "[]ethtypes.EthAddress", + "summary": "", + "schema": { + "examples": [ + [ + "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031" + ] + ], + "items": [ + { + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "maxItems": 20, + "minItems": 20, + "type": [ + "array" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4346" + } + }, + { + "name": "Filecoin.EthBlockNumber", + "description": "```go\nfunc (s *GatewayStruct) EthBlockNumber(p0 context.Context) (ethtypes.EthUint64, error) {\n\tif s.Internal.EthBlockNumber == nil {\n\t\treturn *new(ethtypes.EthUint64), ErrNotSupported\n\t}\n\treturn s.Internal.EthBlockNumber(p0)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [], + "result": { + "name": "ethtypes.EthUint64", + "description": "ethtypes.EthUint64", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + "0x5" + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4357" + } + }, + { + "name": "Filecoin.EthCall", + "description": "```go\nfunc (s *GatewayStruct) EthCall(p0 context.Context, p1 ethtypes.EthCall, p2 ethtypes.EthBlockNumberOrHash) (ethtypes.EthBytes, error) {\n\tif s.Internal.EthCall == nil {\n\t\treturn *new(ethtypes.EthBytes), ErrNotSupported\n\t}\n\treturn s.Internal.EthCall(p0, p1, p2)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "ethtypes.EthCall", + "summary": "", + "schema": { + "examples": [ + { + "from": "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031", + "to": "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031", + "gas": "0x5", + "gasPrice": "0x0", + "value": "0x0", + "data": "0x07" + } + ], + "additionalProperties": false, + "properties": { + "data": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "type": "array" + }, + "from": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 20, + "minItems": 20, + "type": "array" + }, + "gas": { + "title": "number", + "type": "number" + }, + "gasPrice": { + "additionalProperties": false, + "type": "object" + }, + "to": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 20, + "minItems": 20, + "type": "array" + }, + "value": { + "additionalProperties": false, + "type": "object" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "ethtypes.EthBlockNumberOrHash", + "summary": "", + "schema": { + "examples": [ + "string value" + ], + "additionalProperties": false, + "properties": { + "blockHash": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "blockNumber": { + "title": "number", + "type": "number" + }, + "requireCanonical": { + "type": "boolean" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "ethtypes.EthBytes", + "description": "ethtypes.EthBytes", + "summary": "", + "schema": { + "examples": [ + "0x07" + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4368" + } + }, + { + "name": "Filecoin.EthChainId", + "description": "```go\nfunc (s *GatewayStruct) EthChainId(p0 context.Context) (ethtypes.EthUint64, error) {\n\tif s.Internal.EthChainId == nil {\n\t\treturn *new(ethtypes.EthUint64), ErrNotSupported\n\t}\n\treturn s.Internal.EthChainId(p0)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [], + "result": { + "name": "ethtypes.EthUint64", + "description": "ethtypes.EthUint64", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + "0x5" + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4379" + } + }, + { + "name": "Filecoin.EthEstimateGas", + "description": "```go\nfunc (s *GatewayStruct) EthEstimateGas(p0 context.Context, p1 jsonrpc.RawParams) (ethtypes.EthUint64, error) {\n\tif s.Internal.EthEstimateGas == nil {\n\t\treturn *new(ethtypes.EthUint64), ErrNotSupported\n\t}\n\treturn s.Internal.EthEstimateGas(p0, p1)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "jsonrpc.RawParams", + "summary": "", + "schema": { + "examples": [ + "Bw==" + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "ethtypes.EthUint64", + "description": "ethtypes.EthUint64", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + "0x5" + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4390" + } + }, + { + "name": "Filecoin.EthFeeHistory", + "description": "```go\nfunc (s *GatewayStruct) EthFeeHistory(p0 context.Context, p1 jsonrpc.RawParams) (ethtypes.EthFeeHistory, error) {\n\tif s.Internal.EthFeeHistory == nil {\n\t\treturn *new(ethtypes.EthFeeHistory), ErrNotSupported\n\t}\n\treturn s.Internal.EthFeeHistory(p0, p1)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "jsonrpc.RawParams", + "summary": "", + "schema": { + "examples": [ + "Bw==" + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "ethtypes.EthFeeHistory", + "description": "ethtypes.EthFeeHistory", + "summary": "", + "schema": { + "examples": [ + { + "oldestBlock": "0x5", + "baseFeePerGas": [ + "0x0" + ], + "gasUsedRatio": [ + 12.3 + ], + "reward": [] + } + ], + "additionalProperties": false, + "properties": { + "baseFeePerGas": { + "items": { + "additionalProperties": false, + "type": "object" + }, + "type": "array" + }, + "gasUsedRatio": { + "items": { + "type": "number" + }, + "type": "array" + }, + "oldestBlock": { + "title": "number", + "type": "number" + }, + "reward": { + "items": { + "items": { + "additionalProperties": false, + "type": "object" + }, + "type": "array" + }, + "type": "array" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4401" + } + }, + { + "name": "Filecoin.EthGasPrice", + "description": "```go\nfunc (s *GatewayStruct) EthGasPrice(p0 context.Context) (ethtypes.EthBigInt, error) {\n\tif s.Internal.EthGasPrice == nil {\n\t\treturn *new(ethtypes.EthBigInt), ErrNotSupported\n\t}\n\treturn s.Internal.EthGasPrice(p0)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [], + "result": { + "name": "ethtypes.EthBigInt", + "description": "ethtypes.EthBigInt", + "summary": "", + "schema": { + "examples": [ + "0x0" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4412" + } + }, + { + "name": "Filecoin.EthGetBalance", + "description": "```go\nfunc (s *GatewayStruct) EthGetBalance(p0 context.Context, p1 ethtypes.EthAddress, p2 ethtypes.EthBlockNumberOrHash) (ethtypes.EthBigInt, error) {\n\tif s.Internal.EthGetBalance == nil {\n\t\treturn *new(ethtypes.EthBigInt), ErrNotSupported\n\t}\n\treturn s.Internal.EthGetBalance(p0, p1, p2)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "ethtypes.EthAddress", + "summary": "", + "schema": { + "examples": [ + "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031" + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "maxItems": 20, + "minItems": 20, + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "ethtypes.EthBlockNumberOrHash", + "summary": "", + "schema": { + "examples": [ + "string value" + ], + "additionalProperties": false, + "properties": { + "blockHash": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "blockNumber": { + "title": "number", + "type": "number" + }, + "requireCanonical": { + "type": "boolean" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "ethtypes.EthBigInt", + "description": "ethtypes.EthBigInt", + "summary": "", + "schema": { + "examples": [ + "0x0" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4423" + } + }, + { + "name": "Filecoin.EthGetBlockByHash", + "description": "```go\nfunc (s *GatewayStruct) EthGetBlockByHash(p0 context.Context, p1 ethtypes.EthHash, p2 bool) (ethtypes.EthBlock, error) {\n\tif s.Internal.EthGetBlockByHash == nil {\n\t\treturn *new(ethtypes.EthBlock), ErrNotSupported\n\t}\n\treturn s.Internal.EthGetBlockByHash(p0, p1, p2)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "ethtypes.EthHash", + "summary": "", + "schema": { + "examples": [ + "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e" + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "maxItems": 32, + "minItems": 32, + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "bool", + "summary": "", + "schema": { + "examples": [ + true + ], + "type": [ + "boolean" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "ethtypes.EthBlock", + "description": "ethtypes.EthBlock", + "summary": "", + "schema": { + "examples": [ + { + "hash": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", + "parentHash": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", + "sha3Uncles": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", + "miner": "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031", + "stateRoot": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", + "transactionsRoot": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", + "receiptsRoot": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", + "logsBloom": "0x07", + "difficulty": "0x5", + "totalDifficulty": "0x5", + "number": "0x5", + "gasLimit": "0x5", + "gasUsed": "0x5", + "timestamp": "0x5", + "extraData": "0x07", + "mixHash": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", + "nonce": "0x0707070707070707", + "baseFeePerGas": "0x0", + "size": "0x5", + "transactions": [ + {} + ], + "uncles": [ + "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e" + ] + } + ], + "additionalProperties": false, + "properties": { + "baseFeePerGas": { + "additionalProperties": false, + "type": "object" + }, + "difficulty": { + "title": "number", + "type": "number" + }, + "extraData": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "type": "array" + }, + "gasLimit": { + "title": "number", + "type": "number" + }, + "gasUsed": { + "title": "number", + "type": "number" + }, + "hash": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "logsBloom": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "type": "array" + }, + "miner": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 20, + "minItems": 20, + "type": "array" + }, + "mixHash": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "nonce": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 8, + "minItems": 8, + "type": "array" + }, + "number": { + "title": "number", + "type": "number" + }, + "parentHash": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "receiptsRoot": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "sha3Uncles": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "size": { + "title": "number", + "type": "number" + }, + "stateRoot": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "timestamp": { + "title": "number", + "type": "number" + }, + "totalDifficulty": { + "title": "number", + "type": "number" + }, + "transactions": { + "items": { + "additionalProperties": true, + "type": "object" + }, + "type": "array" + }, + "transactionsRoot": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "uncles": { + "items": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "type": "array" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4434" + } + }, + { + "name": "Filecoin.EthGetBlockByNumber", + "description": "```go\nfunc (s *GatewayStruct) EthGetBlockByNumber(p0 context.Context, p1 string, p2 bool) (ethtypes.EthBlock, error) {\n\tif s.Internal.EthGetBlockByNumber == nil {\n\t\treturn *new(ethtypes.EthBlock), ErrNotSupported\n\t}\n\treturn s.Internal.EthGetBlockByNumber(p0, p1, p2)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "string", + "summary": "", + "schema": { + "examples": [ + "string value" + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "bool", + "summary": "", + "schema": { + "examples": [ + true + ], + "type": [ + "boolean" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "ethtypes.EthBlock", + "description": "ethtypes.EthBlock", + "summary": "", + "schema": { + "examples": [ + { + "hash": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", + "parentHash": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", + "sha3Uncles": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", + "miner": "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031", + "stateRoot": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", + "transactionsRoot": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", + "receiptsRoot": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", + "logsBloom": "0x07", + "difficulty": "0x5", + "totalDifficulty": "0x5", + "number": "0x5", + "gasLimit": "0x5", + "gasUsed": "0x5", + "timestamp": "0x5", + "extraData": "0x07", + "mixHash": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", + "nonce": "0x0707070707070707", + "baseFeePerGas": "0x0", + "size": "0x5", + "transactions": [ + {} + ], + "uncles": [ + "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e" + ] + } + ], + "additionalProperties": false, + "properties": { + "baseFeePerGas": { + "additionalProperties": false, + "type": "object" + }, + "difficulty": { + "title": "number", + "type": "number" + }, + "extraData": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "type": "array" + }, + "gasLimit": { + "title": "number", + "type": "number" + }, + "gasUsed": { + "title": "number", + "type": "number" + }, + "hash": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "logsBloom": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "type": "array" + }, + "miner": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 20, + "minItems": 20, + "type": "array" + }, + "mixHash": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "nonce": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 8, + "minItems": 8, + "type": "array" + }, + "number": { + "title": "number", + "type": "number" + }, + "parentHash": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "receiptsRoot": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "sha3Uncles": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "size": { + "title": "number", + "type": "number" + }, + "stateRoot": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "timestamp": { + "title": "number", + "type": "number" + }, + "totalDifficulty": { + "title": "number", + "type": "number" + }, + "transactions": { + "items": { + "additionalProperties": true, + "type": "object" + }, + "type": "array" + }, + "transactionsRoot": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "uncles": { + "items": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "type": "array" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4445" + } + }, + { + "name": "Filecoin.EthGetBlockTransactionCountByHash", + "description": "```go\nfunc (s *GatewayStruct) EthGetBlockTransactionCountByHash(p0 context.Context, p1 ethtypes.EthHash) (ethtypes.EthUint64, error) {\n\tif s.Internal.EthGetBlockTransactionCountByHash == nil {\n\t\treturn *new(ethtypes.EthUint64), ErrNotSupported\n\t}\n\treturn s.Internal.EthGetBlockTransactionCountByHash(p0, p1)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "ethtypes.EthHash", + "summary": "", + "schema": { + "examples": [ + "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e" + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "maxItems": 32, + "minItems": 32, + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "ethtypes.EthUint64", + "description": "ethtypes.EthUint64", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + "0x5" + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4456" + } + }, + { + "name": "Filecoin.EthGetBlockTransactionCountByNumber", + "description": "```go\nfunc (s *GatewayStruct) EthGetBlockTransactionCountByNumber(p0 context.Context, p1 ethtypes.EthUint64) (ethtypes.EthUint64, error) {\n\tif s.Internal.EthGetBlockTransactionCountByNumber == nil {\n\t\treturn *new(ethtypes.EthUint64), ErrNotSupported\n\t}\n\treturn s.Internal.EthGetBlockTransactionCountByNumber(p0, p1)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "ethtypes.EthUint64", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + "0x5" + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "ethtypes.EthUint64", + "description": "ethtypes.EthUint64", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + "0x5" + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4467" + } + }, + { + "name": "Filecoin.EthGetCode", + "description": "```go\nfunc (s *GatewayStruct) EthGetCode(p0 context.Context, p1 ethtypes.EthAddress, p2 ethtypes.EthBlockNumberOrHash) (ethtypes.EthBytes, error) {\n\tif s.Internal.EthGetCode == nil {\n\t\treturn *new(ethtypes.EthBytes), ErrNotSupported\n\t}\n\treturn s.Internal.EthGetCode(p0, p1, p2)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "ethtypes.EthAddress", + "summary": "", + "schema": { + "examples": [ + "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031" + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "maxItems": 20, + "minItems": 20, + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "ethtypes.EthBlockNumberOrHash", + "summary": "", + "schema": { + "examples": [ + "string value" + ], + "additionalProperties": false, + "properties": { + "blockHash": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "blockNumber": { + "title": "number", + "type": "number" + }, + "requireCanonical": { + "type": "boolean" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "ethtypes.EthBytes", + "description": "ethtypes.EthBytes", + "summary": "", + "schema": { + "examples": [ + "0x07" + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4478" + } + }, + { + "name": "Filecoin.EthGetFilterChanges", + "description": "```go\nfunc (s *GatewayStruct) EthGetFilterChanges(p0 context.Context, p1 ethtypes.EthFilterID) (*ethtypes.EthFilterResult, error) {\n\tif s.Internal.EthGetFilterChanges == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.EthGetFilterChanges(p0, p1)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "ethtypes.EthFilterID", + "summary": "", + "schema": { + "examples": [ + "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e" + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "maxItems": 32, + "minItems": 32, + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*ethtypes.EthFilterResult", + "description": "*ethtypes.EthFilterResult", + "summary": "", + "schema": { + "examples": [ + [ + {} + ] + ], + "additionalProperties": false, + "properties": { + "Results": { + "items": { + "additionalProperties": true, + "type": "object" + }, + "type": "array" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4489" + } + }, + { + "name": "Filecoin.EthGetFilterLogs", + "description": "```go\nfunc (s *GatewayStruct) EthGetFilterLogs(p0 context.Context, p1 ethtypes.EthFilterID) (*ethtypes.EthFilterResult, error) {\n\tif s.Internal.EthGetFilterLogs == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.EthGetFilterLogs(p0, p1)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "ethtypes.EthFilterID", + "summary": "", + "schema": { + "examples": [ + "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e" + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "maxItems": 32, + "minItems": 32, + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*ethtypes.EthFilterResult", + "description": "*ethtypes.EthFilterResult", + "summary": "", + "schema": { + "examples": [ + [ + {} + ] + ], + "additionalProperties": false, + "properties": { + "Results": { + "items": { + "additionalProperties": true, + "type": "object" + }, + "type": "array" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4500" + } + }, + { + "name": "Filecoin.EthGetLogs", + "description": "```go\nfunc (s *GatewayStruct) EthGetLogs(p0 context.Context, p1 *ethtypes.EthFilterSpec) (*ethtypes.EthFilterResult, error) {\n\tif s.Internal.EthGetLogs == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.EthGetLogs(p0, p1)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "*ethtypes.EthFilterSpec", + "summary": "", + "schema": { + "examples": [ + { + "fromBlock": "2301220", + "address": [ + "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031" + ], + "topics": null + } + ], + "additionalProperties": false, + "properties": { + "address": { + "items": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 20, + "minItems": 20, + "type": "array" + }, + "type": "array" + }, + "blockHash": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "fromBlock": { + "type": "string" + }, + "toBlock": { + "type": "string" + }, + "topics": { + "items": { + "items": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "type": "array" + }, + "type": "array" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*ethtypes.EthFilterResult", + "description": "*ethtypes.EthFilterResult", + "summary": "", + "schema": { + "examples": [ + [ + {} + ] + ], + "additionalProperties": false, + "properties": { + "Results": { + "items": { + "additionalProperties": true, + "type": "object" + }, + "type": "array" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4511" + } + }, + { + "name": "Filecoin.EthGetMessageCidByTransactionHash", + "description": "```go\nfunc (s *GatewayStruct) EthGetMessageCidByTransactionHash(p0 context.Context, p1 *ethtypes.EthHash) (*cid.Cid, error) {\n\tif s.Internal.EthGetMessageCidByTransactionHash == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.EthGetMessageCidByTransactionHash(p0, p1)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "*ethtypes.EthHash", + "summary": "", + "schema": { + "examples": [ + "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e" + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "maxItems": 32, + "minItems": 32, + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*cid.Cid", + "description": "*cid.Cid", + "summary": "", + "schema": { + "title": "Content Identifier", + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "examples": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4522" + } + }, + { + "name": "Filecoin.EthGetStorageAt", + "description": "```go\nfunc (s *GatewayStruct) EthGetStorageAt(p0 context.Context, p1 ethtypes.EthAddress, p2 ethtypes.EthBytes, p3 ethtypes.EthBlockNumberOrHash) (ethtypes.EthBytes, error) {\n\tif s.Internal.EthGetStorageAt == nil {\n\t\treturn *new(ethtypes.EthBytes), ErrNotSupported\n\t}\n\treturn s.Internal.EthGetStorageAt(p0, p1, p2, p3)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "ethtypes.EthAddress", + "summary": "", + "schema": { + "examples": [ + "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031" + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "maxItems": 20, + "minItems": 20, + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "ethtypes.EthBytes", + "summary": "", + "schema": { + "examples": [ + "0x07" + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p3", + "description": "ethtypes.EthBlockNumberOrHash", + "summary": "", + "schema": { + "examples": [ + "string value" + ], + "additionalProperties": false, + "properties": { + "blockHash": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "blockNumber": { + "title": "number", + "type": "number" + }, + "requireCanonical": { + "type": "boolean" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "ethtypes.EthBytes", + "description": "ethtypes.EthBytes", + "summary": "", + "schema": { + "examples": [ + "0x07" + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4533" + } + }, + { + "name": "Filecoin.EthGetTransactionByHash", + "description": "```go\nfunc (s *GatewayStruct) EthGetTransactionByHash(p0 context.Context, p1 *ethtypes.EthHash) (*ethtypes.EthTx, error) {\n\tif s.Internal.EthGetTransactionByHash == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.EthGetTransactionByHash(p0, p1)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "*ethtypes.EthHash", + "summary": "", + "schema": { + "examples": [ + "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e" + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "maxItems": 32, + "minItems": 32, + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*ethtypes.EthTx", + "description": "*ethtypes.EthTx", + "summary": "", + "schema": { + "examples": [ + { + "chainId": "0x5", + "nonce": "0x5", + "hash": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", + "blockHash": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", + "blockNumber": "0x5", + "transactionIndex": "0x5", + "from": "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031", + "to": "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031", + "value": "0x0", + "type": "0x5", + "input": "0x07", + "gas": "0x5", + "maxFeePerGas": "0x0", + "maxPriorityFeePerGas": "0x0", + "accessList": [ + "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e" + ], + "v": "0x0", + "r": "0x0", + "s": "0x0" + } + ], + "additionalProperties": false, + "properties": { + "accessList": { + "items": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "type": "array" + }, + "blockHash": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "blockNumber": { + "title": "number", + "type": "number" + }, + "chainId": { + "title": "number", + "type": "number" + }, + "from": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 20, + "minItems": 20, + "type": "array" + }, + "gas": { + "title": "number", + "type": "number" + }, + "hash": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "input": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "type": "array" + }, + "maxFeePerGas": { + "additionalProperties": false, + "type": "object" + }, + "maxPriorityFeePerGas": { + "additionalProperties": false, + "type": "object" + }, + "nonce": { + "title": "number", + "type": "number" + }, + "r": { + "additionalProperties": false, + "type": "object" + }, + "s": { + "additionalProperties": false, + "type": "object" + }, + "to": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 20, + "minItems": 20, + "type": "array" + }, + "transactionIndex": { + "title": "number", + "type": "number" + }, + "type": { + "title": "number", + "type": "number" + }, + "v": { + "additionalProperties": false, + "type": "object" + }, + "value": { + "additionalProperties": false, + "type": "object" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4544" + } + }, + { + "name": "Filecoin.EthGetTransactionByHashLimited", + "description": "```go\nfunc (s *GatewayStruct) EthGetTransactionByHashLimited(p0 context.Context, p1 *ethtypes.EthHash, p2 abi.ChainEpoch) (*ethtypes.EthTx, error) {\n\tif s.Internal.EthGetTransactionByHashLimited == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.EthGetTransactionByHashLimited(p0, p1, p2)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "*ethtypes.EthHash", + "summary": "", + "schema": { + "examples": [ + "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e" + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "maxItems": 32, + "minItems": 32, + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "abi.ChainEpoch", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 10101 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*ethtypes.EthTx", + "description": "*ethtypes.EthTx", + "summary": "", + "schema": { + "examples": [ + { + "chainId": "0x5", + "nonce": "0x5", + "hash": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", + "blockHash": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", + "blockNumber": "0x5", + "transactionIndex": "0x5", + "from": "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031", + "to": "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031", + "value": "0x0", + "type": "0x5", + "input": "0x07", + "gas": "0x5", + "maxFeePerGas": "0x0", + "maxPriorityFeePerGas": "0x0", + "accessList": [ + "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e" + ], + "v": "0x0", + "r": "0x0", + "s": "0x0" + } + ], + "additionalProperties": false, + "properties": { + "accessList": { + "items": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "type": "array" + }, + "blockHash": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "blockNumber": { + "title": "number", + "type": "number" + }, + "chainId": { + "title": "number", + "type": "number" + }, + "from": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 20, + "minItems": 20, + "type": "array" + }, + "gas": { + "title": "number", + "type": "number" + }, + "hash": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "input": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "type": "array" + }, + "maxFeePerGas": { + "additionalProperties": false, + "type": "object" + }, + "maxPriorityFeePerGas": { + "additionalProperties": false, + "type": "object" + }, + "nonce": { + "title": "number", + "type": "number" + }, + "r": { + "additionalProperties": false, + "type": "object" + }, + "s": { + "additionalProperties": false, + "type": "object" + }, + "to": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 20, + "minItems": 20, + "type": "array" + }, + "transactionIndex": { + "title": "number", + "type": "number" + }, + "type": { + "title": "number", + "type": "number" + }, + "v": { + "additionalProperties": false, + "type": "object" + }, + "value": { + "additionalProperties": false, + "type": "object" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4555" + } + }, + { + "name": "Filecoin.EthGetTransactionCount", + "description": "```go\nfunc (s *GatewayStruct) EthGetTransactionCount(p0 context.Context, p1 ethtypes.EthAddress, p2 ethtypes.EthBlockNumberOrHash) (ethtypes.EthUint64, error) {\n\tif s.Internal.EthGetTransactionCount == nil {\n\t\treturn *new(ethtypes.EthUint64), ErrNotSupported\n\t}\n\treturn s.Internal.EthGetTransactionCount(p0, p1, p2)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "ethtypes.EthAddress", + "summary": "", + "schema": { + "examples": [ + "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031" + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "maxItems": 20, + "minItems": 20, + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "ethtypes.EthBlockNumberOrHash", + "summary": "", + "schema": { + "examples": [ + "string value" + ], + "additionalProperties": false, + "properties": { + "blockHash": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "blockNumber": { + "title": "number", + "type": "number" + }, + "requireCanonical": { + "type": "boolean" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "ethtypes.EthUint64", + "description": "ethtypes.EthUint64", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + "0x5" + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4566" + } + }, + { + "name": "Filecoin.EthGetTransactionHashByCid", + "description": "```go\nfunc (s *GatewayStruct) EthGetTransactionHashByCid(p0 context.Context, p1 cid.Cid) (*ethtypes.EthHash, error) {\n\tif s.Internal.EthGetTransactionHashByCid == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.EthGetTransactionHashByCid(p0, p1)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "cid.Cid", + "summary": "", + "schema": { + "title": "Content Identifier", + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "examples": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*ethtypes.EthHash", + "description": "*ethtypes.EthHash", + "summary": "", + "schema": { + "examples": [ + "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e" + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "maxItems": 32, + "minItems": 32, + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4577" + } + }, + { + "name": "Filecoin.EthGetTransactionReceipt", + "description": "```go\nfunc (s *GatewayStruct) EthGetTransactionReceipt(p0 context.Context, p1 ethtypes.EthHash) (*EthTxReceipt, error) {\n\tif s.Internal.EthGetTransactionReceipt == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.EthGetTransactionReceipt(p0, p1)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "ethtypes.EthHash", + "summary": "", + "schema": { + "examples": [ + "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e" + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "maxItems": 32, + "minItems": 32, + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*EthTxReceipt", + "description": "*EthTxReceipt", + "summary": "", + "schema": { + "examples": [ + { + "transactionHash": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", + "transactionIndex": "0x5", + "blockHash": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", + "blockNumber": "0x5", + "from": "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031", + "to": "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031", + "root": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", + "status": "0x5", + "contractAddress": "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031", + "cumulativeGasUsed": "0x5", + "gasUsed": "0x5", + "effectiveGasPrice": "0x0", + "logsBloom": "0x07", + "logs": [ + { + "address": "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031", + "data": "0x07", + "topics": [ + "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e" + ], + "removed": true, + "logIndex": "0x5", + "transactionIndex": "0x5", + "transactionHash": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", + "blockHash": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", + "blockNumber": "0x5" + } + ], + "type": "0x5" + } + ], + "additionalProperties": false, + "properties": { + "blockHash": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "blockNumber": { + "title": "number", + "type": "number" + }, + "contractAddress": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 20, + "minItems": 20, + "type": "array" + }, + "cumulativeGasUsed": { + "title": "number", + "type": "number" + }, + "effectiveGasPrice": { + "additionalProperties": false, + "type": "object" + }, + "from": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 20, + "minItems": 20, + "type": "array" + }, + "gasUsed": { + "title": "number", + "type": "number" + }, + "logs": { + "items": { + "additionalProperties": false, + "properties": { + "address": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 20, + "minItems": 20, + "type": "array" + }, + "blockHash": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "blockNumber": { + "title": "number", + "type": "number" + }, + "data": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "type": "array" + }, + "logIndex": { + "title": "number", + "type": "number" + }, + "removed": { + "type": "boolean" + }, + "topics": { + "items": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "type": "array" + }, + "transactionHash": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "transactionIndex": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "type": "array" + }, + "logsBloom": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "type": "array" + }, + "root": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "status": { + "title": "number", + "type": "number" + }, + "to": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 20, + "minItems": 20, + "type": "array" + }, + "transactionHash": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "transactionIndex": { + "title": "number", + "type": "number" + }, + "type": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4588" + } + }, + { + "name": "Filecoin.EthGetTransactionReceiptLimited", + "description": "```go\nfunc (s *GatewayStruct) EthGetTransactionReceiptLimited(p0 context.Context, p1 ethtypes.EthHash, p2 abi.ChainEpoch) (*EthTxReceipt, error) {\n\tif s.Internal.EthGetTransactionReceiptLimited == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.EthGetTransactionReceiptLimited(p0, p1, p2)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "ethtypes.EthHash", + "summary": "", + "schema": { + "examples": [ + "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e" + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "maxItems": 32, + "minItems": 32, + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "abi.ChainEpoch", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 10101 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*EthTxReceipt", + "description": "*EthTxReceipt", + "summary": "", + "schema": { + "examples": [ + { + "transactionHash": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", + "transactionIndex": "0x5", + "blockHash": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", + "blockNumber": "0x5", + "from": "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031", + "to": "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031", + "root": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", + "status": "0x5", + "contractAddress": "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031", + "cumulativeGasUsed": "0x5", + "gasUsed": "0x5", + "effectiveGasPrice": "0x0", + "logsBloom": "0x07", + "logs": [ + { + "address": "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031", + "data": "0x07", + "topics": [ + "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e" + ], + "removed": true, + "logIndex": "0x5", + "transactionIndex": "0x5", + "transactionHash": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", + "blockHash": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", + "blockNumber": "0x5" + } + ], + "type": "0x5" + } + ], + "additionalProperties": false, + "properties": { + "blockHash": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "blockNumber": { + "title": "number", + "type": "number" + }, + "contractAddress": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 20, + "minItems": 20, + "type": "array" + }, + "cumulativeGasUsed": { + "title": "number", + "type": "number" + }, + "effectiveGasPrice": { + "additionalProperties": false, + "type": "object" + }, + "from": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 20, + "minItems": 20, + "type": "array" + }, + "gasUsed": { + "title": "number", + "type": "number" + }, + "logs": { + "items": { + "additionalProperties": false, + "properties": { + "address": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 20, + "minItems": 20, + "type": "array" + }, + "blockHash": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "blockNumber": { + "title": "number", + "type": "number" + }, + "data": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "type": "array" + }, + "logIndex": { + "title": "number", + "type": "number" + }, + "removed": { + "type": "boolean" + }, + "topics": { + "items": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "type": "array" + }, + "transactionHash": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "transactionIndex": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "type": "array" + }, + "logsBloom": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "type": "array" + }, + "root": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "status": { + "title": "number", + "type": "number" + }, + "to": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 20, + "minItems": 20, + "type": "array" + }, + "transactionHash": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "transactionIndex": { + "title": "number", + "type": "number" + }, + "type": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4599" + } + }, + { + "name": "Filecoin.EthMaxPriorityFeePerGas", + "description": "```go\nfunc (s *GatewayStruct) EthMaxPriorityFeePerGas(p0 context.Context) (ethtypes.EthBigInt, error) {\n\tif s.Internal.EthMaxPriorityFeePerGas == nil {\n\t\treturn *new(ethtypes.EthBigInt), ErrNotSupported\n\t}\n\treturn s.Internal.EthMaxPriorityFeePerGas(p0)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [], + "result": { + "name": "ethtypes.EthBigInt", + "description": "ethtypes.EthBigInt", + "summary": "", + "schema": { + "examples": [ + "0x0" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4610" + } + }, + { + "name": "Filecoin.EthNewBlockFilter", + "description": "```go\nfunc (s *GatewayStruct) EthNewBlockFilter(p0 context.Context) (ethtypes.EthFilterID, error) {\n\tif s.Internal.EthNewBlockFilter == nil {\n\t\treturn *new(ethtypes.EthFilterID), ErrNotSupported\n\t}\n\treturn s.Internal.EthNewBlockFilter(p0)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [], + "result": { + "name": "ethtypes.EthFilterID", + "description": "ethtypes.EthFilterID", + "summary": "", + "schema": { + "examples": [ + "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e" + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "maxItems": 32, + "minItems": 32, + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4621" + } + }, + { + "name": "Filecoin.EthNewFilter", + "description": "```go\nfunc (s *GatewayStruct) EthNewFilter(p0 context.Context, p1 *ethtypes.EthFilterSpec) (ethtypes.EthFilterID, error) {\n\tif s.Internal.EthNewFilter == nil {\n\t\treturn *new(ethtypes.EthFilterID), ErrNotSupported\n\t}\n\treturn s.Internal.EthNewFilter(p0, p1)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "*ethtypes.EthFilterSpec", + "summary": "", + "schema": { + "examples": [ + { + "fromBlock": "2301220", + "address": [ + "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031" + ], + "topics": null + } + ], + "additionalProperties": false, + "properties": { + "address": { + "items": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 20, + "minItems": 20, + "type": "array" + }, + "type": "array" + }, + "blockHash": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "fromBlock": { + "type": "string" + }, + "toBlock": { + "type": "string" + }, + "topics": { + "items": { + "items": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "type": "array" + }, + "type": "array" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "ethtypes.EthFilterID", + "description": "ethtypes.EthFilterID", + "summary": "", + "schema": { + "examples": [ + "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e" + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "maxItems": 32, + "minItems": 32, + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4632" + } + }, + { + "name": "Filecoin.EthNewPendingTransactionFilter", + "description": "```go\nfunc (s *GatewayStruct) EthNewPendingTransactionFilter(p0 context.Context) (ethtypes.EthFilterID, error) {\n\tif s.Internal.EthNewPendingTransactionFilter == nil {\n\t\treturn *new(ethtypes.EthFilterID), ErrNotSupported\n\t}\n\treturn s.Internal.EthNewPendingTransactionFilter(p0)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [], + "result": { + "name": "ethtypes.EthFilterID", + "description": "ethtypes.EthFilterID", + "summary": "", + "schema": { + "examples": [ + "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e" + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "maxItems": 32, + "minItems": 32, + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4643" + } + }, + { + "name": "Filecoin.EthProtocolVersion", + "description": "```go\nfunc (s *GatewayStruct) EthProtocolVersion(p0 context.Context) (ethtypes.EthUint64, error) {\n\tif s.Internal.EthProtocolVersion == nil {\n\t\treturn *new(ethtypes.EthUint64), ErrNotSupported\n\t}\n\treturn s.Internal.EthProtocolVersion(p0)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [], + "result": { + "name": "ethtypes.EthUint64", + "description": "ethtypes.EthUint64", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + "0x5" + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4654" + } + }, + { + "name": "Filecoin.EthSendRawTransaction", + "description": "```go\nfunc (s *GatewayStruct) EthSendRawTransaction(p0 context.Context, p1 ethtypes.EthBytes) (ethtypes.EthHash, error) {\n\tif s.Internal.EthSendRawTransaction == nil {\n\t\treturn *new(ethtypes.EthHash), ErrNotSupported\n\t}\n\treturn s.Internal.EthSendRawTransaction(p0, p1)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "ethtypes.EthBytes", + "summary": "", + "schema": { + "examples": [ + "0x07" + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "ethtypes.EthHash", + "description": "ethtypes.EthHash", + "summary": "", + "schema": { + "examples": [ + "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e" + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "maxItems": 32, + "minItems": 32, + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4665" + } + }, + { + "name": "Filecoin.EthSubscribe", + "description": "```go\nfunc (s *GatewayStruct) EthSubscribe(p0 context.Context, p1 jsonrpc.RawParams) (ethtypes.EthSubscriptionID, error) {\n\tif s.Internal.EthSubscribe == nil {\n\t\treturn *new(ethtypes.EthSubscriptionID), ErrNotSupported\n\t}\n\treturn s.Internal.EthSubscribe(p0, p1)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "jsonrpc.RawParams", + "summary": "", + "schema": { + "examples": [ + "Bw==" + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "ethtypes.EthSubscriptionID", + "description": "ethtypes.EthSubscriptionID", + "summary": "", + "schema": { + "examples": [ + "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e" + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "maxItems": 32, + "minItems": 32, + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4676" + } + }, + { + "name": "Filecoin.EthSyncing", + "description": "```go\nfunc (s *GatewayStruct) EthSyncing(p0 context.Context) (ethtypes.EthSyncingResult, error) {\n\tif s.Internal.EthSyncing == nil {\n\t\treturn *new(ethtypes.EthSyncingResult), ErrNotSupported\n\t}\n\treturn s.Internal.EthSyncing(p0)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [], + "result": { + "name": "ethtypes.EthSyncingResult", + "description": "ethtypes.EthSyncingResult", + "summary": "", + "schema": { + "examples": [ + false + ], + "additionalProperties": false, + "properties": { + "CurrentBlock": { + "title": "number", + "type": "number" + }, + "DoneSync": { + "type": "boolean" + }, + "HighestBlock": { + "title": "number", + "type": "number" + }, + "StartingBlock": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4687" + } + }, + { + "name": "Filecoin.EthTraceBlock", + "description": "```go\nfunc (s *GatewayStruct) EthTraceBlock(p0 context.Context, p1 string) ([]*ethtypes.EthTraceBlock, error) {\n\tif s.Internal.EthTraceBlock == nil {\n\t\treturn *new([]*ethtypes.EthTraceBlock), ErrNotSupported\n\t}\n\treturn s.Internal.EthTraceBlock(p0, p1)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "string", + "summary": "", + "schema": { + "examples": [ + "string value" + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "[]*ethtypes.EthTraceBlock", + "description": "[]*ethtypes.EthTraceBlock", + "summary": "", + "schema": { + "examples": [ + [ + { + "action": { + "callType": "string value", + "from": "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031", + "to": "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031", + "gas": "0x5", + "input": "0x07", + "value": "0x0" + }, + "result": { + "gasUsed": "0x5", + "output": "0x07" + }, + "subtraces": 123, + "traceAddress": [ + 123 + ], + "Type": "string value", + "blockHash": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", + "blockNumber": 9, + "transactionHash": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", + "transactionPosition": 123 + } + ] + ], + "items": [ + { + "additionalProperties": false, + "properties": { + "Type": { + "type": "string" + }, + "action": { + "additionalProperties": false, + "properties": { + "callType": { + "type": "string" + }, + "from": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 20, + "minItems": 20, + "type": "array" + }, + "gas": { + "title": "number", + "type": "number" + }, + "input": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "type": "array" + }, + "to": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 20, + "minItems": 20, + "type": "array" + }, + "value": { + "additionalProperties": false, + "type": "object" + } + }, + "type": "object" + }, + "blockHash": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "blockNumber": { + "title": "number", + "type": "number" + }, + "result": { + "additionalProperties": false, + "properties": { + "gasUsed": { + "title": "number", + "type": "number" + }, + "output": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "type": "array" + } + }, + "type": "object" + }, + "subtraces": { + "title": "number", + "type": "number" + }, + "traceAddress": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "type": "array" + }, + "transactionHash": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "transactionPosition": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4698" + } + }, + { + "name": "Filecoin.EthTraceReplayBlockTransactions", + "description": "```go\nfunc (s *GatewayStruct) EthTraceReplayBlockTransactions(p0 context.Context, p1 string, p2 []string) ([]*ethtypes.EthTraceReplayBlockTransaction, error) {\n\tif s.Internal.EthTraceReplayBlockTransactions == nil {\n\t\treturn *new([]*ethtypes.EthTraceReplayBlockTransaction), ErrNotSupported\n\t}\n\treturn s.Internal.EthTraceReplayBlockTransactions(p0, p1, p2)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "string", + "summary": "", + "schema": { + "examples": [ + "string value" + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "[]string", + "summary": "", + "schema": { + "examples": [ + [ + "string value" + ] + ], + "items": [ + { + "type": [ + "string" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "[]*ethtypes.EthTraceReplayBlockTransaction", + "description": "[]*ethtypes.EthTraceReplayBlockTransaction", + "summary": "", + "schema": { + "examples": [ + [ + { + "output": "0x07", + "stateDiff": "string value", + "trace": [ + { + "action": { + "callType": "string value", + "from": "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031", + "to": "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031", + "gas": "0x5", + "input": "0x07", + "value": "0x0" + }, + "result": { + "gasUsed": "0x5", + "output": "0x07" + }, + "subtraces": 123, + "traceAddress": [ + 123 + ], + "Type": "string value" + } + ], + "transactionHash": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", + "vmTrace": "string value" + } + ] + ], + "items": [ + { + "additionalProperties": false, + "properties": { + "output": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "type": "array" + }, + "stateDiff": { + "type": "string" + }, + "trace": { + "items": { + "additionalProperties": false, + "properties": { + "Type": { + "type": "string" + }, + "action": { + "additionalProperties": false, + "properties": { + "callType": { + "type": "string" + }, + "from": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 20, + "minItems": 20, + "type": "array" + }, + "gas": { + "title": "number", + "type": "number" + }, + "input": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "type": "array" + }, + "to": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 20, + "minItems": 20, + "type": "array" + }, + "value": { + "additionalProperties": false, + "type": "object" + } + }, + "type": "object" + }, + "result": { + "additionalProperties": false, + "properties": { + "gasUsed": { + "title": "number", + "type": "number" + }, + "output": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "type": "array" + } + }, + "type": "object" + }, + "subtraces": { + "title": "number", + "type": "number" + }, + "traceAddress": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "type": "array" + } + }, + "type": "object" + }, + "type": "array" + }, + "transactionHash": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 32, + "minItems": 32, + "type": "array" + }, + "vmTrace": { + "type": "string" + } + }, + "type": [ + "object" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4709" + } + }, + { + "name": "Filecoin.EthUninstallFilter", + "description": "```go\nfunc (s *GatewayStruct) EthUninstallFilter(p0 context.Context, p1 ethtypes.EthFilterID) (bool, error) {\n\tif s.Internal.EthUninstallFilter == nil {\n\t\treturn false, ErrNotSupported\n\t}\n\treturn s.Internal.EthUninstallFilter(p0, p1)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "ethtypes.EthFilterID", + "summary": "", + "schema": { + "examples": [ + "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e" + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "maxItems": 32, + "minItems": 32, + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "bool", + "description": "bool", + "summary": "", + "schema": { + "examples": [ + true + ], + "type": [ + "boolean" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4720" + } + }, + { + "name": "Filecoin.EthUnsubscribe", + "description": "```go\nfunc (s *GatewayStruct) EthUnsubscribe(p0 context.Context, p1 ethtypes.EthSubscriptionID) (bool, error) {\n\tif s.Internal.EthUnsubscribe == nil {\n\t\treturn false, ErrNotSupported\n\t}\n\treturn s.Internal.EthUnsubscribe(p0, p1)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "ethtypes.EthSubscriptionID", + "summary": "", + "schema": { + "examples": [ + "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e" + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "maxItems": 32, + "minItems": 32, + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "bool", + "description": "bool", + "summary": "", + "schema": { + "examples": [ + true + ], + "type": [ + "boolean" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4731" + } + }, + { + "name": "Filecoin.GasEstimateGasPremium", + "description": "```go\nfunc (s *GatewayStruct) GasEstimateGasPremium(p0 context.Context, p1 uint64, p2 address.Address, p3 int64, p4 types.TipSetKey) (types.BigInt, error) {\n\tif s.Internal.GasEstimateGasPremium == nil {\n\t\treturn *new(types.BigInt), ErrNotSupported\n\t}\n\treturn s.Internal.GasEstimateGasPremium(p0, p1, p2, p3, p4)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "uint64", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 42 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p3", + "description": "int64", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 9 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p4", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "types.BigInt", + "description": "types.BigInt", + "summary": "", + "schema": { + "examples": [ + "0" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4742" + } + }, + { + "name": "Filecoin.GasEstimateMessageGas", + "description": "```go\nfunc (s *GatewayStruct) GasEstimateMessageGas(p0 context.Context, p1 *types.Message, p2 *MessageSendSpec, p3 types.TipSetKey) (*types.Message, error) {\n\tif s.Internal.GasEstimateMessageGas == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.GasEstimateMessageGas(p0, p1, p2, p3)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "*types.Message", + "summary": "", + "schema": { + "examples": [ + { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + } + ], + "additionalProperties": false, + "properties": { + "From": { + "additionalProperties": false, + "type": "object" + }, + "GasFeeCap": { + "additionalProperties": false, + "type": "object" + }, + "GasLimit": { + "title": "number", + "type": "number" + }, + "GasPremium": { + "additionalProperties": false, + "type": "object" + }, + "Method": { + "title": "number", + "type": "number" + }, + "Nonce": { + "title": "number", + "type": "number" + }, + "Params": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "To": { + "additionalProperties": false, + "type": "object" + }, + "Value": { + "additionalProperties": false, + "type": "object" + }, + "Version": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "*MessageSendSpec", + "summary": "", + "schema": { + "examples": [ + { + "MaxFee": "0", + "MsgUuid": "07070707-0707-0707-0707-070707070707", + "MaximizeFeeCap": true + } + ], + "additionalProperties": false, + "properties": { + "MaxFee": { + "additionalProperties": false, + "type": "object" + }, + "MaximizeFeeCap": { + "type": "boolean" + }, + "MsgUuid": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 16, + "minItems": 16, + "type": "array" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p3", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*types.Message", + "description": "*types.Message", + "summary": "", + "schema": { + "examples": [ + { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + } + ], + "additionalProperties": false, + "properties": { + "From": { + "additionalProperties": false, + "type": "object" + }, + "GasFeeCap": { + "additionalProperties": false, + "type": "object" + }, + "GasLimit": { + "title": "number", + "type": "number" + }, + "GasPremium": { + "additionalProperties": false, + "type": "object" + }, + "Method": { + "title": "number", + "type": "number" + }, + "Nonce": { + "title": "number", + "type": "number" + }, + "Params": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "To": { + "additionalProperties": false, + "type": "object" + }, + "Value": { + "additionalProperties": false, + "type": "object" + }, + "Version": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4753" + } + }, + { + "name": "Filecoin.MinerGetBaseInfo", + "description": "```go\nfunc (s *GatewayStruct) MinerGetBaseInfo(p0 context.Context, p1 address.Address, p2 abi.ChainEpoch, p3 types.TipSetKey) (*MiningBaseInfo, error) {\n\tif s.Internal.MinerGetBaseInfo == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.MinerGetBaseInfo(p0, p1, p2, p3)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "abi.ChainEpoch", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 10101 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p3", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*MiningBaseInfo", + "description": "*MiningBaseInfo", + "summary": "", + "schema": { + "examples": [ + { + "MinerPower": "0", + "NetworkPower": "0", + "Sectors": [ + { + "SealProof": 8, + "SectorNumber": 9, + "SectorKey": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "SealedCID": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + } + ], + "WorkerKey": "f01234", + "SectorSize": 34359738368, + "PrevBeaconEntry": { + "Round": 42, + "Data": "Ynl0ZSBhcnJheQ==" + }, + "BeaconEntries": [ + { + "Round": 42, + "Data": "Ynl0ZSBhcnJheQ==" + } + ], + "EligibleForMining": true + } + ], + "additionalProperties": false, + "properties": { + "BeaconEntries": { + "items": { + "additionalProperties": false, + "properties": { + "Data": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "Round": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "type": "array" + }, + "EligibleForMining": { + "type": "boolean" + }, + "MinerPower": { + "additionalProperties": false, + "type": "object" + }, + "NetworkPower": { + "additionalProperties": false, + "type": "object" + }, + "PrevBeaconEntry": { + "additionalProperties": false, + "properties": { + "Data": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "Round": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "SectorSize": { + "title": "number", + "type": "number" + }, + "Sectors": { + "items": { + "additionalProperties": false, + "properties": { + "SealProof": { + "title": "number", + "type": "number" + }, + "SealedCID": { + "title": "Content Identifier", + "type": "string" + }, + "SectorKey": { + "title": "Content Identifier", + "type": "string" + }, + "SectorNumber": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "type": "array" + }, + "WorkerKey": { + "additionalProperties": false, + "type": "object" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4764" + } + }, + { + "name": "Filecoin.MpoolGetNonce", + "description": "```go\nfunc (s *GatewayStruct) MpoolGetNonce(p0 context.Context, p1 address.Address) (uint64, error) {\n\tif s.Internal.MpoolGetNonce == nil {\n\t\treturn 0, ErrNotSupported\n\t}\n\treturn s.Internal.MpoolGetNonce(p0, p1)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "uint64", + "description": "uint64", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 42 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4775" + } + }, + { + "name": "Filecoin.MpoolPending", + "description": "```go\nfunc (s *GatewayStruct) MpoolPending(p0 context.Context, p1 types.TipSetKey) ([]*types.SignedMessage, error) {\n\tif s.Internal.MpoolPending == nil {\n\t\treturn *new([]*types.SignedMessage), ErrNotSupported\n\t}\n\treturn s.Internal.MpoolPending(p0, p1)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "[]*types.SignedMessage", + "description": "[]*types.SignedMessage", + "summary": "", + "schema": { + "examples": [ + [ + { + "Message": { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + }, + "Signature": { + "Type": 2, + "Data": "Ynl0ZSBhcnJheQ==" + }, + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + } + ] + ], + "items": [ + { + "additionalProperties": false, + "properties": { + "Message": { + "additionalProperties": false, + "properties": { + "From": { + "additionalProperties": false, + "type": "object" + }, + "GasFeeCap": { + "additionalProperties": false, + "type": "object" + }, + "GasLimit": { + "title": "number", + "type": "number" + }, + "GasPremium": { + "additionalProperties": false, + "type": "object" + }, + "Method": { + "title": "number", + "type": "number" + }, + "Nonce": { + "title": "number", + "type": "number" + }, + "Params": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "To": { + "additionalProperties": false, + "type": "object" + }, + "Value": { + "additionalProperties": false, + "type": "object" + }, + "Version": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "Signature": { + "additionalProperties": false, + "properties": { + "Data": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "Type": { + "title": "number", + "type": "number" + } + }, + "type": "object" + } + }, + "type": [ + "object" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4786" + } + }, + { + "name": "Filecoin.MpoolPush", + "description": "```go\nfunc (s *GatewayStruct) MpoolPush(p0 context.Context, p1 *types.SignedMessage) (cid.Cid, error) {\n\tif s.Internal.MpoolPush == nil {\n\t\treturn *new(cid.Cid), ErrNotSupported\n\t}\n\treturn s.Internal.MpoolPush(p0, p1)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "*types.SignedMessage", + "summary": "", + "schema": { + "examples": [ + { + "Message": { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + }, + "Signature": { + "Type": 2, + "Data": "Ynl0ZSBhcnJheQ==" + }, + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + } + ], + "additionalProperties": false, + "properties": { + "Message": { + "additionalProperties": false, + "properties": { + "From": { + "additionalProperties": false, + "type": "object" + }, + "GasFeeCap": { + "additionalProperties": false, + "type": "object" + }, + "GasLimit": { + "title": "number", + "type": "number" + }, + "GasPremium": { + "additionalProperties": false, + "type": "object" + }, + "Method": { + "title": "number", + "type": "number" + }, + "Nonce": { + "title": "number", + "type": "number" + }, + "Params": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "To": { + "additionalProperties": false, + "type": "object" + }, + "Value": { + "additionalProperties": false, + "type": "object" + }, + "Version": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "Signature": { + "additionalProperties": false, + "properties": { + "Data": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "Type": { + "title": "number", + "type": "number" + } + }, + "type": "object" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "cid.Cid", + "description": "cid.Cid", + "summary": "", + "schema": { + "title": "Content Identifier", + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "examples": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4797" + } + }, + { + "name": "Filecoin.MsigGetAvailableBalance", + "description": "```go\nfunc (s *GatewayStruct) MsigGetAvailableBalance(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (types.BigInt, error) {\n\tif s.Internal.MsigGetAvailableBalance == nil {\n\t\treturn *new(types.BigInt), ErrNotSupported\n\t}\n\treturn s.Internal.MsigGetAvailableBalance(p0, p1, p2)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "types.BigInt", + "description": "types.BigInt", + "summary": "", + "schema": { + "examples": [ + "0" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4808" + } + }, + { + "name": "Filecoin.MsigGetPending", + "description": "```go\nfunc (s *GatewayStruct) MsigGetPending(p0 context.Context, p1 address.Address, p2 types.TipSetKey) ([]*MsigTransaction, error) {\n\tif s.Internal.MsigGetPending == nil {\n\t\treturn *new([]*MsigTransaction), ErrNotSupported\n\t}\n\treturn s.Internal.MsigGetPending(p0, p1, p2)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "[]*MsigTransaction", + "description": "[]*MsigTransaction", + "summary": "", + "schema": { + "examples": [ + [ + { + "ID": 9, + "To": "f01234", + "Value": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "Approved": [ + "f01234" + ] + } + ] + ], + "items": [ + { + "additionalProperties": false, + "properties": { + "Approved": { + "items": { + "additionalProperties": false, + "type": "object" + }, + "type": "array" + }, + "ID": { + "title": "number", + "type": "number" + }, + "Method": { + "title": "number", + "type": "number" + }, + "Params": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "To": { + "additionalProperties": false, + "type": "object" + }, + "Value": { + "additionalProperties": false, + "type": "object" + } + }, + "type": [ + "object" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4819" + } + }, + { + "name": "Filecoin.MsigGetVested", + "description": "```go\nfunc (s *GatewayStruct) MsigGetVested(p0 context.Context, p1 address.Address, p2 types.TipSetKey, p3 types.TipSetKey) (types.BigInt, error) {\n\tif s.Internal.MsigGetVested == nil {\n\t\treturn *new(types.BigInt), ErrNotSupported\n\t}\n\treturn s.Internal.MsigGetVested(p0, p1, p2, p3)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p3", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "types.BigInt", + "description": "types.BigInt", + "summary": "", + "schema": { + "examples": [ + "0" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4830" + } + }, + { + "name": "Filecoin.MsigGetVestingSchedule", + "description": "```go\nfunc (s *GatewayStruct) MsigGetVestingSchedule(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (MsigVesting, error) {\n\tif s.Internal.MsigGetVestingSchedule == nil {\n\t\treturn *new(MsigVesting), ErrNotSupported\n\t}\n\treturn s.Internal.MsigGetVestingSchedule(p0, p1, p2)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "MsigVesting", + "description": "MsigVesting", + "summary": "", + "schema": { + "examples": [ + { + "InitialBalance": "0", + "StartEpoch": 10101, + "UnlockDuration": 10101 + } + ], + "additionalProperties": false, + "properties": { + "InitialBalance": { + "additionalProperties": false, + "type": "object" + }, + "StartEpoch": { + "title": "number", + "type": "number" + }, + "UnlockDuration": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4841" + } + }, + { + "name": "Filecoin.NetListening", + "description": "```go\nfunc (s *GatewayStruct) NetListening(p0 context.Context) (bool, error) {\n\tif s.Internal.NetListening == nil {\n\t\treturn false, ErrNotSupported\n\t}\n\treturn s.Internal.NetListening(p0)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [], + "result": { + "name": "bool", + "description": "bool", + "summary": "", + "schema": { + "examples": [ + true + ], + "type": [ + "boolean" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4852" + } + }, + { + "name": "Filecoin.NetVersion", + "description": "```go\nfunc (s *GatewayStruct) NetVersion(p0 context.Context) (string, error) {\n\tif s.Internal.NetVersion == nil {\n\t\treturn \"\", ErrNotSupported\n\t}\n\treturn s.Internal.NetVersion(p0)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [], + "result": { + "name": "string", + "description": "string", + "summary": "", + "schema": { + "examples": [ + "string value" + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4863" + } + }, + { + "name": "Filecoin.StateAccountKey", + "description": "```go\nfunc (s *GatewayStruct) StateAccountKey(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (address.Address, error) {\n\tif s.Internal.StateAccountKey == nil {\n\t\treturn *new(address.Address), ErrNotSupported\n\t}\n\treturn s.Internal.StateAccountKey(p0, p1, p2)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "address.Address", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4874" + } + }, + { + "name": "Filecoin.StateCall", + "description": "```go\nfunc (s *GatewayStruct) StateCall(p0 context.Context, p1 *types.Message, p2 types.TipSetKey) (*InvocResult, error) {\n\tif s.Internal.StateCall == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.StateCall(p0, p1, p2)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "*types.Message", + "summary": "", + "schema": { + "examples": [ + { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + } + ], + "additionalProperties": false, + "properties": { + "From": { + "additionalProperties": false, + "type": "object" + }, + "GasFeeCap": { + "additionalProperties": false, + "type": "object" + }, + "GasLimit": { + "title": "number", + "type": "number" + }, + "GasPremium": { + "additionalProperties": false, + "type": "object" + }, + "Method": { + "title": "number", + "type": "number" + }, + "Nonce": { + "title": "number", + "type": "number" + }, + "Params": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "To": { + "additionalProperties": false, + "type": "object" + }, + "Value": { + "additionalProperties": false, + "type": "object" + }, + "Version": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*InvocResult", + "description": "*InvocResult", + "summary": "", + "schema": { + "examples": [ + { + "MsgCid": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Msg": { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + }, + "MsgRct": { + "ExitCode": 0, + "Return": "Ynl0ZSBhcnJheQ==", + "GasUsed": 9, + "EventsRoot": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + }, + "GasCost": { + "Message": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "GasUsed": "0", + "BaseFeeBurn": "0", + "OverEstimationBurn": "0", + "MinerPenalty": "0", + "MinerTip": "0", + "Refund": "0", + "TotalCost": "0" + }, + "ExecutionTrace": { + "Msg": { + "From": "f01234", + "To": "f01234", + "Value": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "ParamsCodec": 42, + "GasLimit": 42, + "ReadOnly": true + }, + "MsgRct": { + "ExitCode": 0, + "Return": "Ynl0ZSBhcnJheQ==", + "ReturnCodec": 42 + }, + "InvokedActor": { + "Id": 1000, + "State": { + "Code": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Head": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Nonce": 42, + "Balance": "0", + "Address": "f01234" + } + }, + "GasCharges": [ + { + "Name": "string value", + "tg": 9, + "cg": 9, + "sg": 9, + "tt": 60000000000 + } + ], + "Subcalls": [ + { + "Msg": { + "From": "f01234", + "To": "f01234", + "Value": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "ParamsCodec": 42, + "GasLimit": 42, + "ReadOnly": true + }, + "MsgRct": { + "ExitCode": 0, + "Return": "Ynl0ZSBhcnJheQ==", + "ReturnCodec": 42 + }, + "InvokedActor": { + "Id": 1000, + "State": { + "Code": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Head": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Nonce": 42, + "Balance": "0", + "Address": "f01234" + } + }, + "GasCharges": [ + { + "Name": "string value", + "tg": 9, + "cg": 9, + "sg": 9, + "tt": 60000000000 + } + ], + "Subcalls": null + } + ] + }, + "Error": "string value", + "Duration": 60000000000 + } + ], + "additionalProperties": false, + "properties": { + "Duration": { + "title": "number", + "type": "number" + }, + "Error": { + "type": "string" + }, + "ExecutionTrace": { + "additionalProperties": false, + "properties": { + "GasCharges": { + "items": { + "additionalProperties": false, + "properties": { + "Name": { + "type": "string" + }, + "cg": { + "title": "number", + "type": "number" + }, + "sg": { + "title": "number", + "type": "number" + }, + "tg": { + "title": "number", + "type": "number" + }, + "tt": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "type": "array" + }, + "InvokedActor": { + "additionalProperties": false, + "properties": { + "Id": { + "title": "number", + "type": "number" + }, + "State": { + "additionalProperties": false, + "properties": { + "Address": { + "additionalProperties": false, + "type": "object" + }, + "Balance": { + "additionalProperties": false, + "type": "object" + }, + "Code": { + "title": "Content Identifier", + "type": "string" + }, + "Head": { + "title": "Content Identifier", + "type": "string" + }, + "Nonce": { + "title": "number", + "type": "number" + } + }, + "type": "object" + } + }, + "type": "object" + }, + "Msg": { + "additionalProperties": false, + "properties": { + "From": { + "additionalProperties": false, + "type": "object" + }, + "GasLimit": { + "title": "number", + "type": "number" + }, + "Method": { + "title": "number", + "type": "number" + }, + "Params": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "ParamsCodec": { + "title": "number", + "type": "number" + }, + "ReadOnly": { + "type": "boolean" + }, + "To": { + "additionalProperties": false, + "type": "object" + }, + "Value": { + "additionalProperties": false, + "type": "object" + } + }, + "type": "object" + }, + "MsgRct": { + "additionalProperties": false, + "properties": { + "ExitCode": { + "title": "number", + "type": "number" + }, + "Return": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "ReturnCodec": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "Subcalls": { + "items": {}, + "type": "array" + } + }, + "type": "object" + }, + "GasCost": { + "additionalProperties": false, + "properties": { + "BaseFeeBurn": { + "additionalProperties": false, + "type": "object" + }, + "GasUsed": { + "additionalProperties": false, + "type": "object" + }, + "Message": { + "title": "Content Identifier", + "type": "string" + }, + "MinerPenalty": { + "additionalProperties": false, + "type": "object" + }, + "MinerTip": { + "additionalProperties": false, + "type": "object" + }, + "OverEstimationBurn": { + "additionalProperties": false, + "type": "object" + }, + "Refund": { + "additionalProperties": false, + "type": "object" + }, + "TotalCost": { + "additionalProperties": false, + "type": "object" + } + }, + "type": "object" + }, + "Msg": { + "additionalProperties": false, + "properties": { + "From": { + "additionalProperties": false, + "type": "object" + }, + "GasFeeCap": { + "additionalProperties": false, + "type": "object" + }, + "GasLimit": { + "title": "number", + "type": "number" + }, + "GasPremium": { + "additionalProperties": false, + "type": "object" + }, + "Method": { + "title": "number", + "type": "number" + }, + "Nonce": { + "title": "number", + "type": "number" + }, + "Params": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "To": { + "additionalProperties": false, + "type": "object" + }, + "Value": { + "additionalProperties": false, + "type": "object" + }, + "Version": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "MsgCid": { + "title": "Content Identifier", + "type": "string" + }, + "MsgRct": { + "additionalProperties": false, + "properties": { + "EventsRoot": { + "title": "Content Identifier", + "type": "string" + }, + "ExitCode": { + "title": "number", + "type": "number" + }, + "GasUsed": { + "title": "number", + "type": "number" + }, + "Return": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + } + }, + "type": "object" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4885" + } + }, + { + "name": "Filecoin.StateDealProviderCollateralBounds", + "description": "```go\nfunc (s *GatewayStruct) StateDealProviderCollateralBounds(p0 context.Context, p1 abi.PaddedPieceSize, p2 bool, p3 types.TipSetKey) (DealCollateralBounds, error) {\n\tif s.Internal.StateDealProviderCollateralBounds == nil {\n\t\treturn *new(DealCollateralBounds), ErrNotSupported\n\t}\n\treturn s.Internal.StateDealProviderCollateralBounds(p0, p1, p2, p3)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "abi.PaddedPieceSize", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 1032 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "bool", + "summary": "", + "schema": { + "examples": [ + true + ], + "type": [ + "boolean" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p3", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "DealCollateralBounds", + "description": "DealCollateralBounds", + "summary": "", + "schema": { + "examples": [ + { + "Min": "0", + "Max": "0" + } + ], + "additionalProperties": false, + "properties": { + "Max": { + "additionalProperties": false, + "type": "object" + }, + "Min": { + "additionalProperties": false, + "type": "object" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4896" + } + }, + { + "name": "Filecoin.StateDecodeParams", + "description": "```go\nfunc (s *GatewayStruct) StateDecodeParams(p0 context.Context, p1 address.Address, p2 abi.MethodNum, p3 []byte, p4 types.TipSetKey) (interface{}, error) {\n\tif s.Internal.StateDecodeParams == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.StateDecodeParams(p0, p1, p2, p3, p4)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "abi.MethodNum", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 1 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p3", + "description": "[]byte", + "summary": "", + "schema": { + "examples": [ + "Ynl0ZSBhcnJheQ==" + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p4", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "interface{}", + "description": "interface{}", + "summary": "", + "schema": { + "examples": [ + {} + ], + "additionalProperties": true, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4907" + } + }, + { + "name": "Filecoin.StateGetActor", + "description": "```go\nfunc (s *GatewayStruct) StateGetActor(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (*types.Actor, error) {\n\tif s.Internal.StateGetActor == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.StateGetActor(p0, p1, p2)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*types.Actor", + "description": "*types.Actor", + "summary": "", + "schema": { + "examples": [ + { + "Code": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Head": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Nonce": 42, + "Balance": "0", + "Address": "f01234" + } + ], + "additionalProperties": false, + "properties": { + "Address": { + "additionalProperties": false, + "type": "object" + }, + "Balance": { + "additionalProperties": false, + "type": "object" + }, + "Code": { + "title": "Content Identifier", + "type": "string" + }, + "Head": { + "title": "Content Identifier", + "type": "string" + }, + "Nonce": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4918" + } + }, + { + "name": "Filecoin.StateGetAllocation", + "description": "```go\nfunc (s *GatewayStruct) StateGetAllocation(p0 context.Context, p1 address.Address, p2 verifregtypes.AllocationId, p3 types.TipSetKey) (*verifregtypes.Allocation, error) {\n\tif s.Internal.StateGetAllocation == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.StateGetAllocation(p0, p1, p2, p3)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "verifregtypes.AllocationId", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 0 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p3", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*verifregtypes.Allocation", + "description": "*verifregtypes.Allocation", + "summary": "", + "schema": { + "examples": [ + { + "Client": 1000, + "Provider": 1000, + "Data": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Size": 1032, + "TermMin": 10101, + "TermMax": 10101, + "Expiration": 10101 + } + ], + "additionalProperties": false, + "properties": { + "Client": { + "title": "number", + "type": "number" + }, + "Data": { + "title": "Content Identifier", + "type": "string" + }, + "Expiration": { + "title": "number", + "type": "number" + }, + "Provider": { + "title": "number", + "type": "number" + }, + "Size": { + "title": "number", + "type": "number" + }, + "TermMax": { + "title": "number", + "type": "number" + }, + "TermMin": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4929" + } + }, + { + "name": "Filecoin.StateGetAllocationForPendingDeal", + "description": "```go\nfunc (s *GatewayStruct) StateGetAllocationForPendingDeal(p0 context.Context, p1 abi.DealID, p2 types.TipSetKey) (*verifregtypes.Allocation, error) {\n\tif s.Internal.StateGetAllocationForPendingDeal == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.StateGetAllocationForPendingDeal(p0, p1, p2)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "abi.DealID", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 5432 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*verifregtypes.Allocation", + "description": "*verifregtypes.Allocation", + "summary": "", + "schema": { + "examples": [ + { + "Client": 1000, + "Provider": 1000, + "Data": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Size": 1032, + "TermMin": 10101, + "TermMax": 10101, + "Expiration": 10101 + } + ], + "additionalProperties": false, + "properties": { + "Client": { + "title": "number", + "type": "number" + }, + "Data": { + "title": "Content Identifier", + "type": "string" + }, + "Expiration": { + "title": "number", + "type": "number" + }, + "Provider": { + "title": "number", + "type": "number" + }, + "Size": { + "title": "number", + "type": "number" + }, + "TermMax": { + "title": "number", + "type": "number" + }, + "TermMin": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4940" + } + }, + { + "name": "Filecoin.StateGetAllocations", + "description": "```go\nfunc (s *GatewayStruct) StateGetAllocations(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (map[verifregtypes.AllocationId]verifregtypes.Allocation, error) {\n\tif s.Internal.StateGetAllocations == nil {\n\t\treturn *new(map[verifregtypes.AllocationId]verifregtypes.Allocation), ErrNotSupported\n\t}\n\treturn s.Internal.StateGetAllocations(p0, p1, p2)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "map[verifregtypes.AllocationId]verifregtypes.Allocation", + "description": "map[verifregtypes.AllocationId]verifregtypes.Allocation", + "summary": "", + "schema": { + "examples": [ + {} + ], + "patternProperties": { + ".*": { + "additionalProperties": false, + "properties": { + "Client": { + "title": "number", + "type": "number" + }, + "Data": { + "title": "Content Identifier", + "type": "string" + }, + "Expiration": { + "title": "number", + "type": "number" + }, + "Provider": { + "title": "number", + "type": "number" + }, + "Size": { + "title": "number", + "type": "number" + }, + "TermMax": { + "title": "number", + "type": "number" + }, + "TermMin": { + "title": "number", + "type": "number" + } + }, + "type": "object" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4951" + } + }, + { + "name": "Filecoin.StateGetClaim", + "description": "```go\nfunc (s *GatewayStruct) StateGetClaim(p0 context.Context, p1 address.Address, p2 verifregtypes.ClaimId, p3 types.TipSetKey) (*verifregtypes.Claim, error) {\n\tif s.Internal.StateGetClaim == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.StateGetClaim(p0, p1, p2, p3)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "verifregtypes.ClaimId", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 0 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p3", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*verifregtypes.Claim", + "description": "*verifregtypes.Claim", + "summary": "", + "schema": { + "examples": [ + { + "Provider": 1000, + "Client": 1000, + "Data": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Size": 1032, + "TermMin": 10101, + "TermMax": 10101, + "TermStart": 10101, + "Sector": 9 + } + ], + "additionalProperties": false, + "properties": { + "Client": { + "title": "number", + "type": "number" + }, + "Data": { + "title": "Content Identifier", + "type": "string" + }, + "Provider": { + "title": "number", + "type": "number" + }, + "Sector": { + "title": "number", + "type": "number" + }, + "Size": { + "title": "number", + "type": "number" + }, + "TermMax": { + "title": "number", + "type": "number" + }, + "TermMin": { + "title": "number", + "type": "number" + }, + "TermStart": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4962" + } + }, + { + "name": "Filecoin.StateGetClaims", + "description": "```go\nfunc (s *GatewayStruct) StateGetClaims(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (map[verifregtypes.ClaimId]verifregtypes.Claim, error) {\n\tif s.Internal.StateGetClaims == nil {\n\t\treturn *new(map[verifregtypes.ClaimId]verifregtypes.Claim), ErrNotSupported\n\t}\n\treturn s.Internal.StateGetClaims(p0, p1, p2)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "map[verifregtypes.ClaimId]verifregtypes.Claim", + "description": "map[verifregtypes.ClaimId]verifregtypes.Claim", + "summary": "", + "schema": { + "examples": [ + {} + ], + "patternProperties": { + ".*": { + "additionalProperties": false, + "properties": { + "Client": { + "title": "number", + "type": "number" + }, + "Data": { + "title": "Content Identifier", + "type": "string" + }, + "Provider": { + "title": "number", + "type": "number" + }, + "Sector": { + "title": "number", + "type": "number" + }, + "Size": { + "title": "number", + "type": "number" + }, + "TermMax": { + "title": "number", + "type": "number" + }, + "TermMin": { + "title": "number", + "type": "number" + }, + "TermStart": { + "title": "number", + "type": "number" + } + }, + "type": "object" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4973" + } + }, + { + "name": "Filecoin.StateListMiners", + "description": "```go\nfunc (s *GatewayStruct) StateListMiners(p0 context.Context, p1 types.TipSetKey) ([]address.Address, error) {\n\tif s.Internal.StateListMiners == nil {\n\t\treturn *new([]address.Address), ErrNotSupported\n\t}\n\treturn s.Internal.StateListMiners(p0, p1)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "[]address.Address", + "description": "[]address.Address", + "summary": "", + "schema": { + "examples": [ + [ + "f01234" + ] + ], + "items": [ + { + "additionalProperties": false, + "type": [ + "object" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4984" + } + }, + { + "name": "Filecoin.StateLookupID", + "description": "```go\nfunc (s *GatewayStruct) StateLookupID(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (address.Address, error) {\n\tif s.Internal.StateLookupID == nil {\n\t\treturn *new(address.Address), ErrNotSupported\n\t}\n\treturn s.Internal.StateLookupID(p0, p1, p2)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "address.Address", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4995" + } + }, + { + "name": "Filecoin.StateMarketBalance", + "description": "```go\nfunc (s *GatewayStruct) StateMarketBalance(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (MarketBalance, error) {\n\tif s.Internal.StateMarketBalance == nil {\n\t\treturn *new(MarketBalance), ErrNotSupported\n\t}\n\treturn s.Internal.StateMarketBalance(p0, p1, p2)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "MarketBalance", + "description": "MarketBalance", + "summary": "", + "schema": { + "examples": [ + { + "Escrow": "0", + "Locked": "0" + } + ], + "additionalProperties": false, + "properties": { + "Escrow": { + "additionalProperties": false, + "type": "object" + }, + "Locked": { + "additionalProperties": false, + "type": "object" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5006" + } + }, + { + "name": "Filecoin.StateMarketStorageDeal", + "description": "```go\nfunc (s *GatewayStruct) StateMarketStorageDeal(p0 context.Context, p1 abi.DealID, p2 types.TipSetKey) (*MarketDeal, error) {\n\tif s.Internal.StateMarketStorageDeal == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.StateMarketStorageDeal(p0, p1, p2)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "abi.DealID", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 5432 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*MarketDeal", + "description": "*MarketDeal", + "summary": "", + "schema": { + "examples": [ + { + "Proposal": { + "PieceCID": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "PieceSize": 1032, + "VerifiedDeal": true, + "Client": "f01234", + "Provider": "f01234", + "Label": "", + "StartEpoch": 10101, + "EndEpoch": 10101, + "StoragePricePerEpoch": "0", + "ProviderCollateral": "0", + "ClientCollateral": "0" + }, + "State": { + "SectorStartEpoch": 10101, + "LastUpdatedEpoch": 10101, + "SlashEpoch": 10101, + "VerifiedClaim": 0 + } + } + ], + "additionalProperties": false, + "properties": { + "Proposal": { + "additionalProperties": false, + "properties": { + "Client": { + "additionalProperties": false, + "type": "object" + }, + "ClientCollateral": { + "additionalProperties": false, + "type": "object" + }, + "EndEpoch": { + "title": "number", + "type": "number" + }, + "Label": { + "additionalProperties": false, + "type": "object" + }, + "PieceCID": { + "title": "Content Identifier", + "type": "string" + }, + "PieceSize": { + "title": "number", + "type": "number" + }, + "Provider": { + "additionalProperties": false, + "type": "object" + }, + "ProviderCollateral": { + "additionalProperties": false, + "type": "object" + }, + "StartEpoch": { + "title": "number", + "type": "number" + }, + "StoragePricePerEpoch": { + "additionalProperties": false, + "type": "object" + }, + "VerifiedDeal": { + "type": "boolean" + } + }, + "type": "object" + }, + "State": { + "additionalProperties": false, + "properties": { + "LastUpdatedEpoch": { + "title": "number", + "type": "number" + }, + "SectorStartEpoch": { + "title": "number", + "type": "number" + }, + "SlashEpoch": { + "title": "number", + "type": "number" + }, + "VerifiedClaim": { + "title": "number", + "type": "number" + } + }, + "type": "object" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5017" + } + }, + { + "name": "Filecoin.StateMinerInfo", + "description": "```go\nfunc (s *GatewayStruct) StateMinerInfo(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (MinerInfo, error) {\n\tif s.Internal.StateMinerInfo == nil {\n\t\treturn *new(MinerInfo), ErrNotSupported\n\t}\n\treturn s.Internal.StateMinerInfo(p0, p1, p2)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "MinerInfo", + "description": "MinerInfo", + "summary": "", + "schema": { + "examples": [ + { + "Owner": "f01234", + "Worker": "f01234", + "NewWorker": "f01234", + "ControlAddresses": [ + "f01234" + ], + "WorkerChangeEpoch": 10101, + "PeerId": "12D3KooWGzxzKZYveHXtpG6AsrUJBcWxHBFS2HsEoGTxrMLvKXtf", + "Multiaddrs": [ + "Ynl0ZSBhcnJheQ==" + ], + "WindowPoStProofType": 8, + "SectorSize": 34359738368, + "WindowPoStPartitionSectors": 42, + "ConsensusFaultElapsed": 10101, + "PendingOwnerAddress": "f01234", + "Beneficiary": "f01234", + "BeneficiaryTerm": { + "Quota": "0", + "UsedQuota": "0", + "Expiration": 10101 + }, + "PendingBeneficiaryTerm": { + "NewBeneficiary": "f01234", + "NewQuota": "0", + "NewExpiration": 10101, + "ApprovedByBeneficiary": true, + "ApprovedByNominee": true + } + } + ], + "additionalProperties": false, + "properties": { + "Beneficiary": { + "additionalProperties": false, + "type": "object" + }, + "BeneficiaryTerm": { + "additionalProperties": false, + "properties": { + "Expiration": { + "title": "number", + "type": "number" + }, + "Quota": { + "additionalProperties": false, + "type": "object" + }, + "UsedQuota": { + "additionalProperties": false, + "type": "object" + } + }, + "type": "object" + }, + "ConsensusFaultElapsed": { + "title": "number", + "type": "number" + }, + "ControlAddresses": { + "items": { + "additionalProperties": false, + "type": "object" + }, + "type": "array" + }, + "Multiaddrs": { + "items": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "type": "array" + }, + "NewWorker": { + "additionalProperties": false, + "type": "object" + }, + "Owner": { + "additionalProperties": false, + "type": "object" + }, + "PeerId": { + "type": "string" + }, + "PendingBeneficiaryTerm": { + "additionalProperties": false, + "properties": { + "ApprovedByBeneficiary": { + "type": "boolean" + }, + "ApprovedByNominee": { + "type": "boolean" + }, + "NewBeneficiary": { + "additionalProperties": false, + "type": "object" + }, + "NewExpiration": { + "title": "number", + "type": "number" + }, + "NewQuota": { + "additionalProperties": false, + "type": "object" + } + }, + "type": "object" + }, + "PendingOwnerAddress": { + "additionalProperties": false, + "type": "object" + }, + "SectorSize": { + "title": "number", + "type": "number" + }, + "WindowPoStPartitionSectors": { + "title": "number", + "type": "number" + }, + "WindowPoStProofType": { + "title": "number", + "type": "number" + }, + "Worker": { + "additionalProperties": false, + "type": "object" + }, + "WorkerChangeEpoch": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5028" + } + }, + { + "name": "Filecoin.StateMinerPower", + "description": "```go\nfunc (s *GatewayStruct) StateMinerPower(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (*MinerPower, error) {\n\tif s.Internal.StateMinerPower == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.StateMinerPower(p0, p1, p2)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*MinerPower", + "description": "*MinerPower", + "summary": "", + "schema": { + "examples": [ + { + "MinerPower": { + "RawBytePower": "0", + "QualityAdjPower": "0" + }, + "TotalPower": { + "RawBytePower": "0", + "QualityAdjPower": "0" + }, + "HasMinPower": true + } + ], + "additionalProperties": false, + "properties": { + "HasMinPower": { + "type": "boolean" + }, + "MinerPower": { + "additionalProperties": false, + "properties": { + "QualityAdjPower": { + "additionalProperties": false, + "type": "object" + }, + "RawBytePower": { + "additionalProperties": false, + "type": "object" + } + }, + "type": "object" + }, + "TotalPower": { + "additionalProperties": false, + "properties": { + "QualityAdjPower": { + "additionalProperties": false, + "type": "object" + }, + "RawBytePower": { + "additionalProperties": false, + "type": "object" + } + }, + "type": "object" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5039" + } + }, + { + "name": "Filecoin.StateMinerProvingDeadline", + "description": "```go\nfunc (s *GatewayStruct) StateMinerProvingDeadline(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (*dline.Info, error) {\n\tif s.Internal.StateMinerProvingDeadline == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.StateMinerProvingDeadline(p0, p1, p2)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*dline.Info", + "description": "*dline.Info", + "summary": "", + "schema": { + "examples": [ + { + "CurrentEpoch": 10101, + "PeriodStart": 10101, + "Index": 42, + "Open": 10101, + "Close": 10101, + "Challenge": 10101, + "FaultCutoff": 10101, + "WPoStPeriodDeadlines": 42, + "WPoStProvingPeriod": 10101, + "WPoStChallengeWindow": 10101, + "WPoStChallengeLookback": 10101, + "FaultDeclarationCutoff": 10101 + } + ], + "additionalProperties": false, + "properties": { + "Challenge": { + "title": "number", + "type": "number" + }, + "Close": { + "title": "number", + "type": "number" + }, + "CurrentEpoch": { + "title": "number", + "type": "number" + }, + "FaultCutoff": { + "title": "number", + "type": "number" + }, + "FaultDeclarationCutoff": { + "title": "number", + "type": "number" + }, + "Index": { + "title": "number", + "type": "number" + }, + "Open": { + "title": "number", + "type": "number" + }, + "PeriodStart": { + "title": "number", + "type": "number" + }, + "WPoStChallengeLookback": { + "title": "number", + "type": "number" + }, + "WPoStChallengeWindow": { + "title": "number", + "type": "number" + }, + "WPoStPeriodDeadlines": { + "title": "number", + "type": "number" + }, + "WPoStProvingPeriod": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5050" + } + }, + { + "name": "Filecoin.StateMinerSectorCount", + "description": "```go\nfunc (s *GatewayStruct) StateMinerSectorCount(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (MinerSectors, error) {\n\tif s.Internal.StateMinerSectorCount == nil {\n\t\treturn *new(MinerSectors), ErrNotSupported\n\t}\n\treturn s.Internal.StateMinerSectorCount(p0, p1, p2)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "MinerSectors", + "description": "MinerSectors", + "summary": "", + "schema": { + "examples": [ + { + "Live": 42, + "Active": 42, + "Faulty": 42 + } + ], + "additionalProperties": false, + "properties": { + "Active": { + "title": "number", + "type": "number" + }, + "Faulty": { + "title": "number", + "type": "number" + }, + "Live": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5061" + } + }, + { + "name": "Filecoin.StateNetworkName", + "description": "```go\nfunc (s *GatewayStruct) StateNetworkName(p0 context.Context) (dtypes.NetworkName, error) {\n\tif s.Internal.StateNetworkName == nil {\n\t\treturn *new(dtypes.NetworkName), ErrNotSupported\n\t}\n\treturn s.Internal.StateNetworkName(p0)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [], + "result": { + "name": "dtypes.NetworkName", + "description": "dtypes.NetworkName", + "summary": "", + "schema": { + "examples": [ + "lotus" + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5072" + } + }, + { + "name": "Filecoin.StateNetworkVersion", + "description": "```go\nfunc (s *GatewayStruct) StateNetworkVersion(p0 context.Context, p1 types.TipSetKey) (apitypes.NetworkVersion, error) {\n\tif s.Internal.StateNetworkVersion == nil {\n\t\treturn *new(apitypes.NetworkVersion), ErrNotSupported\n\t}\n\treturn s.Internal.StateNetworkVersion(p0, p1)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "apitypes.NetworkVersion", + "description": "apitypes.NetworkVersion", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 21 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5083" + } + }, + { + "name": "Filecoin.StateReadState", + "description": "```go\nfunc (s *GatewayStruct) StateReadState(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (*ActorState, error) {\n\tif s.Internal.StateReadState == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.StateReadState(p0, p1, p2)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*ActorState", + "description": "*ActorState", + "summary": "", + "schema": { + "examples": [ + { + "Balance": "0", + "Code": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "State": {} + } + ], + "additionalProperties": false, + "properties": { + "Balance": { + "additionalProperties": false, + "type": "object" + }, + "Code": { + "title": "Content Identifier", + "type": "string" + }, + "State": { + "additionalProperties": true, + "type": "object" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5094" + } + }, + { + "name": "Filecoin.StateReplay", + "description": "```go\nfunc (s *GatewayStruct) StateReplay(p0 context.Context, p1 types.TipSetKey, p2 cid.Cid) (*InvocResult, error) {\n\tif s.Internal.StateReplay == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.StateReplay(p0, p1, p2)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "cid.Cid", + "summary": "", + "schema": { + "title": "Content Identifier", + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "examples": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*InvocResult", + "description": "*InvocResult", + "summary": "", + "schema": { + "examples": [ + { + "MsgCid": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Msg": { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + }, + "MsgRct": { + "ExitCode": 0, + "Return": "Ynl0ZSBhcnJheQ==", + "GasUsed": 9, + "EventsRoot": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + }, + "GasCost": { + "Message": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "GasUsed": "0", + "BaseFeeBurn": "0", + "OverEstimationBurn": "0", + "MinerPenalty": "0", + "MinerTip": "0", + "Refund": "0", + "TotalCost": "0" + }, + "ExecutionTrace": { + "Msg": { + "From": "f01234", + "To": "f01234", + "Value": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "ParamsCodec": 42, + "GasLimit": 42, + "ReadOnly": true + }, + "MsgRct": { + "ExitCode": 0, + "Return": "Ynl0ZSBhcnJheQ==", + "ReturnCodec": 42 + }, + "InvokedActor": { + "Id": 1000, + "State": { + "Code": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Head": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Nonce": 42, + "Balance": "0", + "Address": "f01234" + } + }, + "GasCharges": [ + { + "Name": "string value", + "tg": 9, + "cg": 9, + "sg": 9, + "tt": 60000000000 + } + ], + "Subcalls": [ + { + "Msg": { + "From": "f01234", + "To": "f01234", + "Value": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "ParamsCodec": 42, + "GasLimit": 42, + "ReadOnly": true + }, + "MsgRct": { + "ExitCode": 0, + "Return": "Ynl0ZSBhcnJheQ==", + "ReturnCodec": 42 + }, + "InvokedActor": { + "Id": 1000, + "State": { + "Code": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Head": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Nonce": 42, + "Balance": "0", + "Address": "f01234" + } + }, + "GasCharges": [ + { + "Name": "string value", + "tg": 9, + "cg": 9, + "sg": 9, + "tt": 60000000000 + } + ], + "Subcalls": null + } + ] + }, + "Error": "string value", + "Duration": 60000000000 + } + ], + "additionalProperties": false, + "properties": { + "Duration": { + "title": "number", + "type": "number" + }, + "Error": { + "type": "string" + }, + "ExecutionTrace": { + "additionalProperties": false, + "properties": { + "GasCharges": { + "items": { + "additionalProperties": false, + "properties": { + "Name": { + "type": "string" + }, + "cg": { + "title": "number", + "type": "number" + }, + "sg": { + "title": "number", + "type": "number" + }, + "tg": { + "title": "number", + "type": "number" + }, + "tt": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "type": "array" + }, + "InvokedActor": { + "additionalProperties": false, + "properties": { + "Id": { + "title": "number", + "type": "number" + }, + "State": { + "additionalProperties": false, + "properties": { + "Address": { + "additionalProperties": false, + "type": "object" + }, + "Balance": { + "additionalProperties": false, + "type": "object" + }, + "Code": { + "title": "Content Identifier", + "type": "string" + }, + "Head": { + "title": "Content Identifier", + "type": "string" + }, + "Nonce": { + "title": "number", + "type": "number" + } + }, + "type": "object" + } + }, + "type": "object" + }, + "Msg": { + "additionalProperties": false, + "properties": { + "From": { + "additionalProperties": false, + "type": "object" + }, + "GasLimit": { + "title": "number", + "type": "number" + }, + "Method": { + "title": "number", + "type": "number" + }, + "Params": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "ParamsCodec": { + "title": "number", + "type": "number" + }, + "ReadOnly": { + "type": "boolean" + }, + "To": { + "additionalProperties": false, + "type": "object" + }, + "Value": { + "additionalProperties": false, + "type": "object" + } + }, + "type": "object" + }, + "MsgRct": { + "additionalProperties": false, + "properties": { + "ExitCode": { + "title": "number", + "type": "number" + }, + "Return": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "ReturnCodec": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "Subcalls": { + "items": {}, + "type": "array" + } + }, + "type": "object" + }, + "GasCost": { + "additionalProperties": false, + "properties": { + "BaseFeeBurn": { + "additionalProperties": false, + "type": "object" + }, + "GasUsed": { + "additionalProperties": false, + "type": "object" + }, + "Message": { + "title": "Content Identifier", + "type": "string" + }, + "MinerPenalty": { + "additionalProperties": false, + "type": "object" + }, + "MinerTip": { + "additionalProperties": false, + "type": "object" + }, + "OverEstimationBurn": { + "additionalProperties": false, + "type": "object" + }, + "Refund": { + "additionalProperties": false, + "type": "object" + }, + "TotalCost": { + "additionalProperties": false, + "type": "object" + } + }, + "type": "object" + }, + "Msg": { + "additionalProperties": false, + "properties": { + "From": { + "additionalProperties": false, + "type": "object" + }, + "GasFeeCap": { + "additionalProperties": false, + "type": "object" + }, + "GasLimit": { + "title": "number", + "type": "number" + }, + "GasPremium": { + "additionalProperties": false, + "type": "object" + }, + "Method": { + "title": "number", + "type": "number" + }, + "Nonce": { + "title": "number", + "type": "number" + }, + "Params": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + }, + "To": { + "additionalProperties": false, + "type": "object" + }, + "Value": { + "additionalProperties": false, + "type": "object" + }, + "Version": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "MsgCid": { + "title": "Content Identifier", + "type": "string" + }, + "MsgRct": { + "additionalProperties": false, + "properties": { + "EventsRoot": { + "title": "Content Identifier", + "type": "string" + }, + "ExitCode": { + "title": "number", + "type": "number" + }, + "GasUsed": { + "title": "number", + "type": "number" + }, + "Return": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + } + }, + "type": "object" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5105" + } + }, + { + "name": "Filecoin.StateSearchMsg", + "description": "```go\nfunc (s *GatewayStruct) StateSearchMsg(p0 context.Context, p1 types.TipSetKey, p2 cid.Cid, p3 abi.ChainEpoch, p4 bool) (*MsgLookup, error) {\n\tif s.Internal.StateSearchMsg == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.StateSearchMsg(p0, p1, p2, p3, p4)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "cid.Cid", + "summary": "", + "schema": { + "title": "Content Identifier", + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "examples": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p3", + "description": "abi.ChainEpoch", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 10101 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p4", + "description": "bool", + "summary": "", + "schema": { + "examples": [ + true + ], + "type": [ + "boolean" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*MsgLookup", + "description": "*MsgLookup", + "summary": "", + "schema": { + "examples": [ + { + "Message": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Receipt": { + "ExitCode": 0, + "Return": "Ynl0ZSBhcnJheQ==", + "GasUsed": 9, + "EventsRoot": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + }, + "ReturnDec": {}, + "TipSet": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ], + "Height": 10101 + } + ], + "additionalProperties": false, + "properties": { + "Height": { + "title": "number", + "type": "number" + }, + "Message": { + "title": "Content Identifier", + "type": "string" + }, + "Receipt": { + "additionalProperties": false, + "properties": { + "EventsRoot": { + "title": "Content Identifier", + "type": "string" + }, + "ExitCode": { + "title": "number", + "type": "number" + }, + "GasUsed": { + "title": "number", + "type": "number" + }, + "Return": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + } + }, + "type": "object" + }, + "ReturnDec": { + "additionalProperties": true, + "type": "object" + }, + "TipSet": { + "additionalProperties": false, + "type": "object" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5116" + } + }, + { + "name": "Filecoin.StateSectorGetInfo", + "description": "```go\nfunc (s *GatewayStruct) StateSectorGetInfo(p0 context.Context, p1 address.Address, p2 abi.SectorNumber, p3 types.TipSetKey) (*miner.SectorOnChainInfo, error) {\n\tif s.Internal.StateSectorGetInfo == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.StateSectorGetInfo(p0, p1, p2, p3)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "abi.SectorNumber", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 9 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p3", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*miner.SectorOnChainInfo", + "description": "*miner.SectorOnChainInfo", + "summary": "", + "schema": { + "examples": [ + { + "SectorNumber": 9, + "SealProof": 8, + "SealedCID": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "DealIDs": [ + 5432 + ], + "Activation": 10101, + "Expiration": 10101, + "DealWeight": "0", + "VerifiedDealWeight": "0", + "InitialPledge": "0", + "ExpectedDayReward": "0", + "ExpectedStoragePledge": "0", + "PowerBaseEpoch": 10101, + "ReplacedDayReward": "0", + "SectorKeyCID": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Flags": 0 + } + ], + "additionalProperties": false, + "properties": { + "Activation": { + "title": "number", + "type": "number" + }, + "DealIDs": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "type": "array" + }, + "DealWeight": { + "additionalProperties": false, + "type": "object" + }, + "ExpectedDayReward": { + "additionalProperties": false, + "type": "object" + }, + "ExpectedStoragePledge": { + "additionalProperties": false, + "type": "object" + }, + "Expiration": { + "title": "number", + "type": "number" + }, + "Flags": { + "title": "number", + "type": "number" + }, + "InitialPledge": { + "additionalProperties": false, + "type": "object" + }, + "PowerBaseEpoch": { + "title": "number", + "type": "number" + }, + "ReplacedDayReward": { + "additionalProperties": false, + "type": "object" + }, + "SealProof": { + "title": "number", + "type": "number" + }, + "SealedCID": { + "title": "Content Identifier", + "type": "string" + }, + "SectorKeyCID": { + "title": "Content Identifier", + "type": "string" + }, + "SectorNumber": { + "title": "number", + "type": "number" + }, + "VerifiedDealWeight": { + "additionalProperties": false, + "type": "object" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5127" + } + }, + { + "name": "Filecoin.StateVerifiedClientStatus", + "description": "```go\nfunc (s *GatewayStruct) StateVerifiedClientStatus(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (*abi.StoragePower, error) {\n\tif s.Internal.StateVerifiedClientStatus == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.StateVerifiedClientStatus(p0, p1, p2)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*abi.StoragePower", + "description": "*abi.StoragePower", + "summary": "", + "schema": { + "examples": [ + "0" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5138" + } + }, + { + "name": "Filecoin.StateVerifierStatus", + "description": "```go\nfunc (s *GatewayStruct) StateVerifierStatus(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (*abi.StoragePower, error) {\n\tif s.Internal.StateVerifierStatus == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.StateVerifierStatus(p0, p1, p2)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*abi.StoragePower", + "description": "*abi.StoragePower", + "summary": "", + "schema": { + "examples": [ + "0" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5149" + } + }, + { + "name": "Filecoin.StateWaitMsg", + "description": "```go\nfunc (s *GatewayStruct) StateWaitMsg(p0 context.Context, p1 cid.Cid, p2 uint64, p3 abi.ChainEpoch, p4 bool) (*MsgLookup, error) {\n\tif s.Internal.StateWaitMsg == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\treturn s.Internal.StateWaitMsg(p0, p1, p2, p3, p4)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "cid.Cid", + "summary": "", + "schema": { + "title": "Content Identifier", + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "examples": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "uint64", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 42 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p3", + "description": "abi.ChainEpoch", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 10101 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p4", + "description": "bool", + "summary": "", + "schema": { + "examples": [ + true + ], + "type": [ + "boolean" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "*MsgLookup", + "description": "*MsgLookup", + "summary": "", + "schema": { + "examples": [ + { + "Message": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Receipt": { + "ExitCode": 0, + "Return": "Ynl0ZSBhcnJheQ==", + "GasUsed": 9, + "EventsRoot": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + }, + "ReturnDec": {}, + "TipSet": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ], + "Height": 10101 + } + ], + "additionalProperties": false, + "properties": { + "Height": { + "title": "number", + "type": "number" + }, + "Message": { + "title": "Content Identifier", + "type": "string" + }, + "Receipt": { + "additionalProperties": false, + "properties": { + "EventsRoot": { + "title": "Content Identifier", + "type": "string" + }, + "ExitCode": { + "title": "number", + "type": "number" + }, + "GasUsed": { + "title": "number", + "type": "number" + }, + "Return": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + } + }, + "type": "object" + }, + "ReturnDec": { + "additionalProperties": true, + "type": "object" + }, + "TipSet": { + "additionalProperties": false, + "type": "object" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5160" + } + }, + { + "name": "Filecoin.Version", + "description": "```go\nfunc (s *GatewayStruct) Version(p0 context.Context) (APIVersion, error) {\n\tif s.Internal.Version == nil {\n\t\treturn *new(APIVersion), ErrNotSupported\n\t}\n\treturn s.Internal.Version(p0)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [], + "result": { + "name": "APIVersion", + "description": "APIVersion", + "summary": "", + "schema": { + "examples": [ + { + "Version": "string value", + "APIVersion": 131840, + "BlockDelay": 42 + } + ], + "additionalProperties": false, + "properties": { + "APIVersion": { + "title": "number", + "type": "number" + }, + "BlockDelay": { + "title": "number", + "type": "number" + }, + "Version": { + "type": "string" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5171" + } + }, + { + "name": "Filecoin.WalletBalance", + "description": "```go\nfunc (s *GatewayStruct) WalletBalance(p0 context.Context, p1 address.Address) (types.BigInt, error) {\n\tif s.Internal.WalletBalance == nil {\n\t\treturn *new(types.BigInt), ErrNotSupported\n\t}\n\treturn s.Internal.WalletBalance(p0, p1)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "types.BigInt", + "description": "types.BigInt", + "summary": "", + "schema": { + "examples": [ + "0" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5182" + } + }, + { + "name": "Filecoin.Web3ClientVersion", + "description": "```go\nfunc (s *GatewayStruct) Web3ClientVersion(p0 context.Context) (string, error) {\n\tif s.Internal.Web3ClientVersion == nil {\n\t\treturn \"\", ErrNotSupported\n\t}\n\treturn s.Internal.Web3ClientVersion(p0)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [], + "result": { + "name": "string", + "description": "string", + "summary": "", + "schema": { + "examples": [ + "string value" + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5193" + } + } + ] +} \ No newline at end of file diff --git a/build/openrpc/gateway.json.gz b/build/openrpc/gateway.json.gz deleted file mode 100644 index f4b89fe2f11fc456ab1f242144135b3d89477d62..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11931 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ν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 diff --git a/build/openrpc/worker.json b/build/openrpc/worker.json new file mode 100644 index 000000000..257fa1bad --- /dev/null +++ b/build/openrpc/worker.json @@ -0,0 +1,5536 @@ +{ + "openrpc": "1.2.6", + "info": { + "title": "Lotus RPC API", + "version": "1.25.3-dev" + }, + "methods": [ + { + "name": "Filecoin.AddPiece", + "description": "```go\nfunc (s *WorkerStruct) AddPiece(p0 context.Context, p1 storiface.SectorRef, p2 []abi.UnpaddedPieceSize, p3 abi.UnpaddedPieceSize, p4 storiface.Data) (storiface.CallID, error) {\n\tif s.Internal.AddPiece == nil {\n\t\treturn *new(storiface.CallID), ErrNotSupported\n\t}\n\treturn s.Internal.AddPiece(p0, p1, p2, p3, p4)\n}\n```", + "summary": "", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "storiface.SectorRef", + "summary": "", + "schema": { + "examples": [ + { + "ID": { + "Miner": 1000, + "Number": 9 + }, + "ProofType": 8 + } + ], + "additionalProperties": false, + "properties": { + "ID": { + "additionalProperties": false, + "properties": { + "Miner": { + "title": "number", + "type": "number" + }, + "Number": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "ProofType": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "[]abi.UnpaddedPieceSize", + "summary": "", + "schema": { + "examples": [ + [ + 1024 + ] + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p3", + "description": "abi.UnpaddedPieceSize", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 1024 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p4", + "description": "storiface.Data", + "summary": "", + "schema": { + "examples": [ + {} + ], + "additionalProperties": true + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "storiface.CallID", + "description": "storiface.CallID", + "summary": "", + "schema": { + "examples": [ + { + "Sector": { + "Miner": 1000, + "Number": 9 + }, + "ID": "07070707-0707-0707-0707-070707070707" + } + ], + "additionalProperties": false, + "properties": { + "ID": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 16, + "minItems": 16, + "type": "array" + }, + "Sector": { + "additionalProperties": false, + "properties": { + "Miner": { + "title": "number", + "type": "number" + }, + "Number": { + "title": "number", + "type": "number" + } + }, + "type": "object" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7041" + } + }, + { + "name": "Filecoin.DataCid", + "description": "```go\nfunc (s *WorkerStruct) DataCid(p0 context.Context, p1 abi.UnpaddedPieceSize, p2 storiface.Data) (storiface.CallID, error) {\n\tif s.Internal.DataCid == nil {\n\t\treturn *new(storiface.CallID), ErrNotSupported\n\t}\n\treturn s.Internal.DataCid(p0, p1, p2)\n}\n```", + "summary": "storiface.WorkerCalls\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "abi.UnpaddedPieceSize", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 1024 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "storiface.Data", + "summary": "", + "schema": { + "examples": [ + {} + ], + "additionalProperties": true + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "storiface.CallID", + "description": "storiface.CallID", + "summary": "", + "schema": { + "examples": [ + { + "Sector": { + "Miner": 1000, + "Number": 9 + }, + "ID": "07070707-0707-0707-0707-070707070707" + } + ], + "additionalProperties": false, + "properties": { + "ID": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 16, + "minItems": 16, + "type": "array" + }, + "Sector": { + "additionalProperties": false, + "properties": { + "Miner": { + "title": "number", + "type": "number" + }, + "Number": { + "title": "number", + "type": "number" + } + }, + "type": "object" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7052" + } + }, + { + "name": "Filecoin.DownloadSectorData", + "description": "```go\nfunc (s *WorkerStruct) DownloadSectorData(p0 context.Context, p1 storiface.SectorRef, p2 bool, p3 map[storiface.SectorFileType]storiface.SectorLocation) (storiface.CallID, error) {\n\tif s.Internal.DownloadSectorData == nil {\n\t\treturn *new(storiface.CallID), ErrNotSupported\n\t}\n\treturn s.Internal.DownloadSectorData(p0, p1, p2, p3)\n}\n```", + "summary": "", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "storiface.SectorRef", + "summary": "", + "schema": { + "examples": [ + { + "ID": { + "Miner": 1000, + "Number": 9 + }, + "ProofType": 8 + } + ], + "additionalProperties": false, + "properties": { + "ID": { + "additionalProperties": false, + "properties": { + "Miner": { + "title": "number", + "type": "number" + }, + "Number": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "ProofType": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "bool", + "summary": "", + "schema": { + "examples": [ + true + ], + "type": [ + "boolean" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p3", + "description": "map[storiface.SectorFileType]storiface.SectorLocation", + "summary": "", + "schema": { + "examples": [ + { + "2": { + "Local": false, + "URL": "https://example.com/sealingservice/sectors/s-f0123-12345", + "Headers": null + } + } + ], + "patternProperties": { + ".*": { + "additionalProperties": false, + "properties": { + "Headers": { + "items": { + "additionalProperties": false, + "properties": { + "Key": { + "type": "string" + }, + "Value": { + "type": "string" + } + }, + "type": "object" + }, + "type": "array" + }, + "Local": { + "type": "boolean" + }, + "URL": { + "type": "string" + } + }, + "type": "object" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "storiface.CallID", + "description": "storiface.CallID", + "summary": "", + "schema": { + "examples": [ + { + "Sector": { + "Miner": 1000, + "Number": 9 + }, + "ID": "07070707-0707-0707-0707-070707070707" + } + ], + "additionalProperties": false, + "properties": { + "ID": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 16, + "minItems": 16, + "type": "array" + }, + "Sector": { + "additionalProperties": false, + "properties": { + "Miner": { + "title": "number", + "type": "number" + }, + "Number": { + "title": "number", + "type": "number" + } + }, + "type": "object" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7063" + } + }, + { + "name": "Filecoin.Enabled", + "description": "```go\nfunc (s *WorkerStruct) Enabled(p0 context.Context) (bool, error) {\n\tif s.Internal.Enabled == nil {\n\t\treturn false, ErrNotSupported\n\t}\n\treturn s.Internal.Enabled(p0)\n}\n```", + "summary": "", + "paramStructure": "by-position", + "params": [], + "result": { + "name": "bool", + "description": "bool", + "summary": "", + "schema": { + "examples": [ + true + ], + "type": [ + "boolean" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7074" + } + }, + { + "name": "Filecoin.Fetch", + "description": "```go\nfunc (s *WorkerStruct) Fetch(p0 context.Context, p1 storiface.SectorRef, p2 storiface.SectorFileType, p3 storiface.PathType, p4 storiface.AcquireMode) (storiface.CallID, error) {\n\tif s.Internal.Fetch == nil {\n\t\treturn *new(storiface.CallID), ErrNotSupported\n\t}\n\treturn s.Internal.Fetch(p0, p1, p2, p3, p4)\n}\n```", + "summary": "", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "storiface.SectorRef", + "summary": "", + "schema": { + "examples": [ + { + "ID": { + "Miner": 1000, + "Number": 9 + }, + "ProofType": 8 + } + ], + "additionalProperties": false, + "properties": { + "ID": { + "additionalProperties": false, + "properties": { + "Miner": { + "title": "number", + "type": "number" + }, + "Number": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "ProofType": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "storiface.SectorFileType", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 1 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p3", + "description": "storiface.PathType", + "summary": "", + "schema": { + "examples": [ + "sealing" + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p4", + "description": "storiface.AcquireMode", + "summary": "", + "schema": { + "examples": [ + "move" + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "storiface.CallID", + "description": "storiface.CallID", + "summary": "", + "schema": { + "examples": [ + { + "Sector": { + "Miner": 1000, + "Number": 9 + }, + "ID": "07070707-0707-0707-0707-070707070707" + } + ], + "additionalProperties": false, + "properties": { + "ID": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 16, + "minItems": 16, + "type": "array" + }, + "Sector": { + "additionalProperties": false, + "properties": { + "Miner": { + "title": "number", + "type": "number" + }, + "Number": { + "title": "number", + "type": "number" + } + }, + "type": "object" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7085" + } + }, + { + "name": "Filecoin.FinalizeReplicaUpdate", + "description": "```go\nfunc (s *WorkerStruct) FinalizeReplicaUpdate(p0 context.Context, p1 storiface.SectorRef) (storiface.CallID, error) {\n\tif s.Internal.FinalizeReplicaUpdate == nil {\n\t\treturn *new(storiface.CallID), ErrNotSupported\n\t}\n\treturn s.Internal.FinalizeReplicaUpdate(p0, p1)\n}\n```", + "summary": "", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "storiface.SectorRef", + "summary": "", + "schema": { + "examples": [ + { + "ID": { + "Miner": 1000, + "Number": 9 + }, + "ProofType": 8 + } + ], + "additionalProperties": false, + "properties": { + "ID": { + "additionalProperties": false, + "properties": { + "Miner": { + "title": "number", + "type": "number" + }, + "Number": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "ProofType": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "storiface.CallID", + "description": "storiface.CallID", + "summary": "", + "schema": { + "examples": [ + { + "Sector": { + "Miner": 1000, + "Number": 9 + }, + "ID": "07070707-0707-0707-0707-070707070707" + } + ], + "additionalProperties": false, + "properties": { + "ID": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 16, + "minItems": 16, + "type": "array" + }, + "Sector": { + "additionalProperties": false, + "properties": { + "Miner": { + "title": "number", + "type": "number" + }, + "Number": { + "title": "number", + "type": "number" + } + }, + "type": "object" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7096" + } + }, + { + "name": "Filecoin.FinalizeSector", + "description": "```go\nfunc (s *WorkerStruct) FinalizeSector(p0 context.Context, p1 storiface.SectorRef) (storiface.CallID, error) {\n\tif s.Internal.FinalizeSector == nil {\n\t\treturn *new(storiface.CallID), ErrNotSupported\n\t}\n\treturn s.Internal.FinalizeSector(p0, p1)\n}\n```", + "summary": "", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "storiface.SectorRef", + "summary": "", + "schema": { + "examples": [ + { + "ID": { + "Miner": 1000, + "Number": 9 + }, + "ProofType": 8 + } + ], + "additionalProperties": false, + "properties": { + "ID": { + "additionalProperties": false, + "properties": { + "Miner": { + "title": "number", + "type": "number" + }, + "Number": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "ProofType": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "storiface.CallID", + "description": "storiface.CallID", + "summary": "", + "schema": { + "examples": [ + { + "Sector": { + "Miner": 1000, + "Number": 9 + }, + "ID": "07070707-0707-0707-0707-070707070707" + } + ], + "additionalProperties": false, + "properties": { + "ID": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 16, + "minItems": 16, + "type": "array" + }, + "Sector": { + "additionalProperties": false, + "properties": { + "Miner": { + "title": "number", + "type": "number" + }, + "Number": { + "title": "number", + "type": "number" + } + }, + "type": "object" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7107" + } + }, + { + "name": "Filecoin.GenerateSectorKeyFromData", + "description": "```go\nfunc (s *WorkerStruct) GenerateSectorKeyFromData(p0 context.Context, p1 storiface.SectorRef, p2 cid.Cid) (storiface.CallID, error) {\n\tif s.Internal.GenerateSectorKeyFromData == nil {\n\t\treturn *new(storiface.CallID), ErrNotSupported\n\t}\n\treturn s.Internal.GenerateSectorKeyFromData(p0, p1, p2)\n}\n```", + "summary": "", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "storiface.SectorRef", + "summary": "", + "schema": { + "examples": [ + { + "ID": { + "Miner": 1000, + "Number": 9 + }, + "ProofType": 8 + } + ], + "additionalProperties": false, + "properties": { + "ID": { + "additionalProperties": false, + "properties": { + "Miner": { + "title": "number", + "type": "number" + }, + "Number": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "ProofType": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "cid.Cid", + "summary": "", + "schema": { + "title": "Content Identifier", + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "examples": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "storiface.CallID", + "description": "storiface.CallID", + "summary": "", + "schema": { + "examples": [ + { + "Sector": { + "Miner": 1000, + "Number": 9 + }, + "ID": "07070707-0707-0707-0707-070707070707" + } + ], + "additionalProperties": false, + "properties": { + "ID": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 16, + "minItems": 16, + "type": "array" + }, + "Sector": { + "additionalProperties": false, + "properties": { + "Miner": { + "title": "number", + "type": "number" + }, + "Number": { + "title": "number", + "type": "number" + } + }, + "type": "object" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7118" + } + }, + { + "name": "Filecoin.GenerateWindowPoSt", + "description": "```go\nfunc (s *WorkerStruct) GenerateWindowPoSt(p0 context.Context, p1 abi.RegisteredPoStProof, p2 abi.ActorID, p3 []storiface.PostSectorChallenge, p4 int, p5 abi.PoStRandomness) (storiface.WindowPoStResult, error) {\n\tif s.Internal.GenerateWindowPoSt == nil {\n\t\treturn *new(storiface.WindowPoStResult), ErrNotSupported\n\t}\n\treturn s.Internal.GenerateWindowPoSt(p0, p1, p2, p3, p4, p5)\n}\n```", + "summary": "", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "abi.RegisteredPoStProof", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 8 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "abi.ActorID", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 1000 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p3", + "description": "[]storiface.PostSectorChallenge", + "summary": "", + "schema": { + "examples": [ + [ + { + "SealProof": 8, + "SectorNumber": 9, + "SealedCID": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Challenge": [ + 42 + ], + "Update": true + } + ] + ], + "items": [ + { + "additionalProperties": false, + "properties": { + "Challenge": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "type": "array" + }, + "SealProof": { + "title": "number", + "type": "number" + }, + "SealedCID": { + "title": "Content Identifier", + "type": "string" + }, + "SectorNumber": { + "title": "number", + "type": "number" + }, + "Update": { + "type": "boolean" + } + }, + "type": [ + "object" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p4", + "description": "int", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 123 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p5", + "description": "abi.PoStRandomness", + "summary": "", + "schema": { + "examples": [ + "Bw==" + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "storiface.WindowPoStResult", + "description": "storiface.WindowPoStResult", + "summary": "", + "schema": { + "examples": [ + { + "PoStProofs": { + "PoStProof": 8, + "ProofBytes": "Ynl0ZSBhcnJheQ==" + }, + "Skipped": [ + { + "Miner": 1000, + "Number": 9 + } + ] + } + ], + "additionalProperties": false, + "properties": { + "PoStProofs": { + "additionalProperties": false, + "properties": { + "PoStProof": { + "title": "number", + "type": "number" + }, + "ProofBytes": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + } + }, + "type": "object" + }, + "Skipped": { + "items": { + "additionalProperties": false, + "properties": { + "Miner": { + "title": "number", + "type": "number" + }, + "Number": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "type": "array" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7129" + } + }, + { + "name": "Filecoin.GenerateWinningPoSt", + "description": "```go\nfunc (s *WorkerStruct) GenerateWinningPoSt(p0 context.Context, p1 abi.RegisteredPoStProof, p2 abi.ActorID, p3 []storiface.PostSectorChallenge, p4 abi.PoStRandomness) ([]proof.PoStProof, error) {\n\tif s.Internal.GenerateWinningPoSt == nil {\n\t\treturn *new([]proof.PoStProof), ErrNotSupported\n\t}\n\treturn s.Internal.GenerateWinningPoSt(p0, p1, p2, p3, p4)\n}\n```", + "summary": "", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "abi.RegisteredPoStProof", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 8 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "abi.ActorID", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 1000 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p3", + "description": "[]storiface.PostSectorChallenge", + "summary": "", + "schema": { + "examples": [ + [ + { + "SealProof": 8, + "SectorNumber": 9, + "SealedCID": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Challenge": [ + 42 + ], + "Update": true + } + ] + ], + "items": [ + { + "additionalProperties": false, + "properties": { + "Challenge": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "type": "array" + }, + "SealProof": { + "title": "number", + "type": "number" + }, + "SealedCID": { + "title": "Content Identifier", + "type": "string" + }, + "SectorNumber": { + "title": "number", + "type": "number" + }, + "Update": { + "type": "boolean" + } + }, + "type": [ + "object" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p4", + "description": "abi.PoStRandomness", + "summary": "", + "schema": { + "examples": [ + "Bw==" + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "[]proof.PoStProof", + "description": "[]proof.PoStProof", + "summary": "", + "schema": { + "examples": [ + [ + { + "PoStProof": 8, + "ProofBytes": "Ynl0ZSBhcnJheQ==" + } + ] + ], + "items": [ + { + "additionalProperties": false, + "properties": { + "PoStProof": { + "title": "number", + "type": "number" + }, + "ProofBytes": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + } + }, + "type": [ + "object" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7140" + } + }, + { + "name": "Filecoin.Info", + "description": "```go\nfunc (s *WorkerStruct) Info(p0 context.Context) (storiface.WorkerInfo, error) {\n\tif s.Internal.Info == nil {\n\t\treturn *new(storiface.WorkerInfo), ErrNotSupported\n\t}\n\treturn s.Internal.Info(p0)\n}\n```", + "summary": "", + "paramStructure": "by-position", + "params": [], + "result": { + "name": "storiface.WorkerInfo", + "description": "storiface.WorkerInfo", + "summary": "", + "schema": { + "examples": [ + { + "Hostname": "string value", + "IgnoreResources": true, + "Resources": { + "MemPhysical": 42, + "MemUsed": 42, + "MemSwap": 42, + "MemSwapUsed": 42, + "CPUs": 42, + "GPUs": [ + "string value" + ], + "Resources": { + "post/v0/windowproof": { + "0": { + "MinMemory": 2048, + "MaxMemory": 2048, + "GPUUtilization": 1, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 2048, + "MaxConcurrent": 0 + }, + "1": { + "MinMemory": 8388608, + "MaxMemory": 8388608, + "GPUUtilization": 1, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 8388608, + "MaxConcurrent": 0 + }, + "10": { + "MinMemory": 2048, + "MaxMemory": 2048, + "GPUUtilization": 1, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 2048, + "MaxConcurrent": 0 + }, + "11": { + "MinMemory": 8388608, + "MaxMemory": 8388608, + "GPUUtilization": 1, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 8388608, + "MaxConcurrent": 0 + }, + "12": { + "MinMemory": 1073741824, + "MaxMemory": 1610612736, + "GPUUtilization": 1, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 10737418240, + "MaxConcurrent": 0 + }, + "13": { + "MinMemory": 32212254720, + "MaxMemory": 103079215104, + "GPUUtilization": 1, + "MaxParallelism": -1, + "MaxParallelismGPU": 6, + "BaseMinMemory": 34359738368, + "MaxConcurrent": 0 + }, + "14": { + "MinMemory": 64424509440, + "MaxMemory": 128849018880, + "GPUUtilization": 1, + "MaxParallelism": -1, + "MaxParallelismGPU": 6, + "BaseMinMemory": 68719476736, + "MaxConcurrent": 0 + }, + "2": { + "MinMemory": 1073741824, + "MaxMemory": 1610612736, + "GPUUtilization": 1, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 10737418240, + "MaxConcurrent": 0 + }, + "3": { + "MinMemory": 32212254720, + "MaxMemory": 103079215104, + "GPUUtilization": 1, + "MaxParallelism": -1, + "MaxParallelismGPU": 6, + "BaseMinMemory": 34359738368, + "MaxConcurrent": 0 + }, + "4": { + "MinMemory": 64424509440, + "MaxMemory": 128849018880, + "GPUUtilization": 1, + "MaxParallelism": -1, + "MaxParallelismGPU": 6, + "BaseMinMemory": 68719476736, + "MaxConcurrent": 0 + }, + "5": { + "MinMemory": 2048, + "MaxMemory": 2048, + "GPUUtilization": 1, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 2048, + "MaxConcurrent": 0 + }, + "6": { + "MinMemory": 8388608, + "MaxMemory": 8388608, + "GPUUtilization": 1, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 8388608, + "MaxConcurrent": 0 + }, + "7": { + "MinMemory": 1073741824, + "MaxMemory": 1610612736, + "GPUUtilization": 1, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 10737418240, + "MaxConcurrent": 0 + }, + "8": { + "MinMemory": 32212254720, + "MaxMemory": 103079215104, + "GPUUtilization": 1, + "MaxParallelism": -1, + "MaxParallelismGPU": 6, + "BaseMinMemory": 34359738368, + "MaxConcurrent": 0 + }, + "9": { + "MinMemory": 64424509440, + "MaxMemory": 128849018880, + "GPUUtilization": 1, + "MaxParallelism": -1, + "MaxParallelismGPU": 6, + "BaseMinMemory": 68719476736, + "MaxConcurrent": 0 + } + }, + "post/v0/winningproof": { + "0": { + "MinMemory": 2048, + "MaxMemory": 2048, + "GPUUtilization": 1, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 2048, + "MaxConcurrent": 0 + }, + "1": { + "MinMemory": 8388608, + "MaxMemory": 8388608, + "GPUUtilization": 1, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 8388608, + "MaxConcurrent": 0 + }, + "10": { + "MinMemory": 2048, + "MaxMemory": 2048, + "GPUUtilization": 1, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 2048, + "MaxConcurrent": 0 + }, + "11": { + "MinMemory": 8388608, + "MaxMemory": 8388608, + "GPUUtilization": 1, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 8388608, + "MaxConcurrent": 0 + }, + "12": { + "MinMemory": 2048, + "MaxMemory": 2048, + "GPUUtilization": 1, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 10737418240, + "MaxConcurrent": 0 + }, + "13": { + "MinMemory": 1073741824, + "MaxMemory": 1073741824, + "GPUUtilization": 1, + "MaxParallelism": -1, + "MaxParallelismGPU": 6, + "BaseMinMemory": 34359738368, + "MaxConcurrent": 0 + }, + "14": { + "MinMemory": 1073741824, + "MaxMemory": 1073741824, + "GPUUtilization": 1, + "MaxParallelism": -1, + "MaxParallelismGPU": 6, + "BaseMinMemory": 68719476736, + "MaxConcurrent": 0 + }, + "2": { + "MinMemory": 2048, + "MaxMemory": 2048, + "GPUUtilization": 1, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 10737418240, + "MaxConcurrent": 0 + }, + "3": { + "MinMemory": 1073741824, + "MaxMemory": 1073741824, + "GPUUtilization": 1, + "MaxParallelism": -1, + "MaxParallelismGPU": 6, + "BaseMinMemory": 34359738368, + "MaxConcurrent": 0 + }, + "4": { + "MinMemory": 1073741824, + "MaxMemory": 1073741824, + "GPUUtilization": 1, + "MaxParallelism": -1, + "MaxParallelismGPU": 6, + "BaseMinMemory": 68719476736, + "MaxConcurrent": 0 + }, + "5": { + "MinMemory": 2048, + "MaxMemory": 2048, + "GPUUtilization": 1, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 2048, + "MaxConcurrent": 0 + }, + "6": { + "MinMemory": 8388608, + "MaxMemory": 8388608, + "GPUUtilization": 1, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 8388608, + "MaxConcurrent": 0 + }, + "7": { + "MinMemory": 2048, + "MaxMemory": 2048, + "GPUUtilization": 1, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 10737418240, + "MaxConcurrent": 0 + }, + "8": { + "MinMemory": 1073741824, + "MaxMemory": 1073741824, + "GPUUtilization": 1, + "MaxParallelism": -1, + "MaxParallelismGPU": 6, + "BaseMinMemory": 34359738368, + "MaxConcurrent": 0 + }, + "9": { + "MinMemory": 1073741824, + "MaxMemory": 1073741824, + "GPUUtilization": 1, + "MaxParallelism": -1, + "MaxParallelismGPU": 6, + "BaseMinMemory": 68719476736, + "MaxConcurrent": 0 + } + }, + "seal/v0/addpiece": { + "0": { + "MinMemory": 2048, + "MaxMemory": 2048, + "GPUUtilization": 0, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 2048, + "MaxConcurrent": 0 + }, + "1": { + "MinMemory": 8388608, + "MaxMemory": 8388608, + "GPUUtilization": 0, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 8388608, + "MaxConcurrent": 0 + }, + "10": { + "MinMemory": 2048, + "MaxMemory": 2048, + "GPUUtilization": 0, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 2048, + "MaxConcurrent": 0 + }, + "11": { + "MinMemory": 8388608, + "MaxMemory": 8388608, + "GPUUtilization": 0, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 8388608, + "MaxConcurrent": 0 + }, + "12": { + "MinMemory": 1073741824, + "MaxMemory": 1073741824, + "GPUUtilization": 0, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 1073741824, + "MaxConcurrent": 0 + }, + "13": { + "MinMemory": 4294967296, + "MaxMemory": 4294967296, + "GPUUtilization": 0, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 1073741824, + "MaxConcurrent": 0 + }, + "14": { + "MinMemory": 8589934592, + "MaxMemory": 8589934592, + "GPUUtilization": 0, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 1073741824, + "MaxConcurrent": 0 + }, + "2": { + "MinMemory": 1073741824, + "MaxMemory": 1073741824, + "GPUUtilization": 0, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 1073741824, + "MaxConcurrent": 0 + }, + "3": { + "MinMemory": 4294967296, + "MaxMemory": 4294967296, + "GPUUtilization": 0, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 1073741824, + "MaxConcurrent": 0 + }, + "4": { + "MinMemory": 8589934592, + "MaxMemory": 8589934592, + "GPUUtilization": 0, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 1073741824, + "MaxConcurrent": 0 + }, + "5": { + "MinMemory": 2048, + "MaxMemory": 2048, + "GPUUtilization": 0, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 2048, + "MaxConcurrent": 0 + }, + "6": { + "MinMemory": 8388608, + "MaxMemory": 8388608, + "GPUUtilization": 0, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 8388608, + "MaxConcurrent": 0 + }, + "7": { + "MinMemory": 1073741824, + "MaxMemory": 1073741824, + "GPUUtilization": 0, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 1073741824, + "MaxConcurrent": 0 + }, + "8": { + "MinMemory": 4294967296, + "MaxMemory": 4294967296, + "GPUUtilization": 0, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 1073741824, + "MaxConcurrent": 0 + }, + "9": { + "MinMemory": 8589934592, + "MaxMemory": 8589934592, + "GPUUtilization": 0, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 1073741824, + "MaxConcurrent": 0 + } + }, + "seal/v0/commit/1": { + "0": { + "MinMemory": 2048, + "MaxMemory": 2048, + "GPUUtilization": 0, + "MaxParallelism": 0, + "MaxParallelismGPU": 0, + "BaseMinMemory": 2048, + "MaxConcurrent": 0 + }, + "1": { + "MinMemory": 8388608, + "MaxMemory": 8388608, + "GPUUtilization": 0, + "MaxParallelism": 0, + "MaxParallelismGPU": 0, + "BaseMinMemory": 8388608, + "MaxConcurrent": 0 + }, + "10": { + "MinMemory": 2048, + "MaxMemory": 2048, + "GPUUtilization": 0, + "MaxParallelism": 0, + "MaxParallelismGPU": 0, + "BaseMinMemory": 2048, + "MaxConcurrent": 0 + }, + "11": { + "MinMemory": 8388608, + "MaxMemory": 8388608, + "GPUUtilization": 0, + "MaxParallelism": 0, + "MaxParallelismGPU": 0, + "BaseMinMemory": 8388608, + "MaxConcurrent": 0 + }, + "12": { + "MinMemory": 1073741824, + "MaxMemory": 1073741824, + "GPUUtilization": 0, + "MaxParallelism": 0, + "MaxParallelismGPU": 0, + "BaseMinMemory": 1073741824, + "MaxConcurrent": 0 + }, + "13": { + "MinMemory": 1073741824, + "MaxMemory": 1073741824, + "GPUUtilization": 0, + "MaxParallelism": 0, + "MaxParallelismGPU": 0, + "BaseMinMemory": 1073741824, + "MaxConcurrent": 0 + }, + "14": { + "MinMemory": 1073741824, + "MaxMemory": 1073741824, + "GPUUtilization": 0, + "MaxParallelism": 0, + "MaxParallelismGPU": 0, + "BaseMinMemory": 1073741824, + "MaxConcurrent": 0 + }, + "2": { + "MinMemory": 1073741824, + "MaxMemory": 1073741824, + "GPUUtilization": 0, + "MaxParallelism": 0, + "MaxParallelismGPU": 0, + "BaseMinMemory": 1073741824, + "MaxConcurrent": 0 + }, + "3": { + "MinMemory": 1073741824, + "MaxMemory": 1073741824, + "GPUUtilization": 0, + "MaxParallelism": 0, + "MaxParallelismGPU": 0, + "BaseMinMemory": 1073741824, + "MaxConcurrent": 0 + }, + "4": { + "MinMemory": 1073741824, + "MaxMemory": 1073741824, + "GPUUtilization": 0, + "MaxParallelism": 0, + "MaxParallelismGPU": 0, + "BaseMinMemory": 1073741824, + "MaxConcurrent": 0 + }, + "5": { + "MinMemory": 2048, + "MaxMemory": 2048, + "GPUUtilization": 0, + "MaxParallelism": 0, + "MaxParallelismGPU": 0, + "BaseMinMemory": 2048, + "MaxConcurrent": 0 + }, + "6": { + "MinMemory": 8388608, + "MaxMemory": 8388608, + "GPUUtilization": 0, + "MaxParallelism": 0, + "MaxParallelismGPU": 0, + "BaseMinMemory": 8388608, + "MaxConcurrent": 0 + }, + "7": { + "MinMemory": 1073741824, + "MaxMemory": 1073741824, + "GPUUtilization": 0, + "MaxParallelism": 0, + "MaxParallelismGPU": 0, + "BaseMinMemory": 1073741824, + "MaxConcurrent": 0 + }, + "8": { + "MinMemory": 1073741824, + "MaxMemory": 1073741824, + "GPUUtilization": 0, + "MaxParallelism": 0, + "MaxParallelismGPU": 0, + "BaseMinMemory": 1073741824, + "MaxConcurrent": 0 + }, + "9": { + "MinMemory": 1073741824, + "MaxMemory": 1073741824, + "GPUUtilization": 0, + "MaxParallelism": 0, + "MaxParallelismGPU": 0, + "BaseMinMemory": 1073741824, + "MaxConcurrent": 0 + } + }, + "seal/v0/commit/2": { + "0": { + "MinMemory": 2048, + "MaxMemory": 2048, + "GPUUtilization": 1, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 2048, + "MaxConcurrent": 0 + }, + "1": { + "MinMemory": 8388608, + "MaxMemory": 8388608, + "GPUUtilization": 1, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 8388608, + "MaxConcurrent": 0 + }, + "10": { + "MinMemory": 2048, + "MaxMemory": 2048, + "GPUUtilization": 1, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 2048, + "MaxConcurrent": 0 + }, + "11": { + "MinMemory": 8388608, + "MaxMemory": 8388608, + "GPUUtilization": 1, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 8388608, + "MaxConcurrent": 0 + }, + "12": { + "MinMemory": 1073741824, + "MaxMemory": 1610612736, + "GPUUtilization": 1, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 10737418240, + "MaxConcurrent": 0 + }, + "13": { + "MinMemory": 32212254720, + "MaxMemory": 161061273600, + "GPUUtilization": 1, + "MaxParallelism": -1, + "MaxParallelismGPU": 6, + "BaseMinMemory": 34359738368, + "MaxConcurrent": 0 + }, + "14": { + "MinMemory": 64424509440, + "MaxMemory": 204010946560, + "GPUUtilization": 1, + "MaxParallelism": -1, + "MaxParallelismGPU": 6, + "BaseMinMemory": 68719476736, + "MaxConcurrent": 0 + }, + "2": { + "MinMemory": 1073741824, + "MaxMemory": 1610612736, + "GPUUtilization": 1, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 10737418240, + "MaxConcurrent": 0 + }, + "3": { + "MinMemory": 32212254720, + "MaxMemory": 161061273600, + "GPUUtilization": 1, + "MaxParallelism": -1, + "MaxParallelismGPU": 6, + "BaseMinMemory": 34359738368, + "MaxConcurrent": 0 + }, + "4": { + "MinMemory": 64424509440, + "MaxMemory": 204010946560, + "GPUUtilization": 1, + "MaxParallelism": -1, + "MaxParallelismGPU": 6, + "BaseMinMemory": 68719476736, + "MaxConcurrent": 0 + }, + "5": { + "MinMemory": 2048, + "MaxMemory": 2048, + "GPUUtilization": 1, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 2048, + "MaxConcurrent": 0 + }, + "6": { + "MinMemory": 8388608, + "MaxMemory": 8388608, + "GPUUtilization": 1, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 8388608, + "MaxConcurrent": 0 + }, + "7": { + "MinMemory": 1073741824, + "MaxMemory": 1610612736, + "GPUUtilization": 1, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 10737418240, + "MaxConcurrent": 0 + }, + "8": { + "MinMemory": 32212254720, + "MaxMemory": 161061273600, + "GPUUtilization": 1, + "MaxParallelism": -1, + "MaxParallelismGPU": 6, + "BaseMinMemory": 34359738368, + "MaxConcurrent": 0 + }, + "9": { + "MinMemory": 64424509440, + "MaxMemory": 204010946560, + "GPUUtilization": 1, + "MaxParallelism": -1, + "MaxParallelismGPU": 6, + "BaseMinMemory": 68719476736, + "MaxConcurrent": 0 + } + }, + "seal/v0/datacid": { + "0": { + "MinMemory": 4294967296, + "MaxMemory": 4294967296, + "GPUUtilization": 0, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 1073741824, + "MaxConcurrent": 0 + }, + "1": { + "MinMemory": 4294967296, + "MaxMemory": 4294967296, + "GPUUtilization": 0, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 1073741824, + "MaxConcurrent": 0 + }, + "10": { + "MinMemory": 4294967296, + "MaxMemory": 4294967296, + "GPUUtilization": 0, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 1073741824, + "MaxConcurrent": 0 + }, + "11": { + "MinMemory": 4294967296, + "MaxMemory": 4294967296, + "GPUUtilization": 0, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 1073741824, + "MaxConcurrent": 0 + }, + "12": { + "MinMemory": 4294967296, + "MaxMemory": 4294967296, + "GPUUtilization": 0, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 1073741824, + "MaxConcurrent": 0 + }, + "13": { + "MinMemory": 4294967296, + "MaxMemory": 4294967296, + "GPUUtilization": 0, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 1073741824, + "MaxConcurrent": 0 + }, + "14": { + "MinMemory": 4294967296, + "MaxMemory": 4294967296, + "GPUUtilization": 0, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 1073741824, + "MaxConcurrent": 0 + }, + "2": { + "MinMemory": 4294967296, + "MaxMemory": 4294967296, + "GPUUtilization": 0, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 1073741824, + "MaxConcurrent": 0 + }, + "3": { + "MinMemory": 4294967296, + "MaxMemory": 4294967296, + "GPUUtilization": 0, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 1073741824, + "MaxConcurrent": 0 + }, + "4": { + "MinMemory": 4294967296, + "MaxMemory": 4294967296, + "GPUUtilization": 0, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 1073741824, + "MaxConcurrent": 0 + }, + "5": { + "MinMemory": 4294967296, + "MaxMemory": 4294967296, + "GPUUtilization": 0, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 1073741824, + "MaxConcurrent": 0 + }, + "6": { + "MinMemory": 4294967296, + "MaxMemory": 4294967296, + "GPUUtilization": 0, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 1073741824, + "MaxConcurrent": 0 + }, + "7": { + "MinMemory": 4294967296, + "MaxMemory": 4294967296, + "GPUUtilization": 0, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 1073741824, + "MaxConcurrent": 0 + }, + "8": { + "MinMemory": 4294967296, + "MaxMemory": 4294967296, + "GPUUtilization": 0, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 1073741824, + "MaxConcurrent": 0 + }, + "9": { + "MinMemory": 4294967296, + "MaxMemory": 4294967296, + "GPUUtilization": 0, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 1073741824, + "MaxConcurrent": 0 + } + }, + "seal/v0/fetch": { + "0": { + "MinMemory": 1048576, + "MaxMemory": 1048576, + "GPUUtilization": 0, + "MaxParallelism": 0, + "MaxParallelismGPU": 0, + "BaseMinMemory": 0, + "MaxConcurrent": 0 + }, + "1": { + "MinMemory": 1048576, + "MaxMemory": 1048576, + "GPUUtilization": 0, + "MaxParallelism": 0, + "MaxParallelismGPU": 0, + "BaseMinMemory": 0, + "MaxConcurrent": 0 + }, + "10": { + "MinMemory": 1048576, + "MaxMemory": 1048576, + "GPUUtilization": 0, + "MaxParallelism": 0, + "MaxParallelismGPU": 0, + "BaseMinMemory": 0, + "MaxConcurrent": 0 + }, + "11": { + "MinMemory": 1048576, + "MaxMemory": 1048576, + "GPUUtilization": 0, + "MaxParallelism": 0, + "MaxParallelismGPU": 0, + "BaseMinMemory": 0, + "MaxConcurrent": 0 + }, + "12": { + "MinMemory": 1048576, + "MaxMemory": 1048576, + "GPUUtilization": 0, + "MaxParallelism": 0, + "MaxParallelismGPU": 0, + "BaseMinMemory": 0, + "MaxConcurrent": 0 + }, + "13": { + "MinMemory": 1048576, + "MaxMemory": 1048576, + "GPUUtilization": 0, + "MaxParallelism": 0, + "MaxParallelismGPU": 0, + "BaseMinMemory": 0, + "MaxConcurrent": 0 + }, + "14": { + "MinMemory": 1048576, + "MaxMemory": 1048576, + "GPUUtilization": 0, + "MaxParallelism": 0, + "MaxParallelismGPU": 0, + "BaseMinMemory": 0, + "MaxConcurrent": 0 + }, + "2": { + "MinMemory": 1048576, + "MaxMemory": 1048576, + "GPUUtilization": 0, + "MaxParallelism": 0, + "MaxParallelismGPU": 0, + "BaseMinMemory": 0, + "MaxConcurrent": 0 + }, + "3": { + "MinMemory": 1048576, + "MaxMemory": 1048576, + "GPUUtilization": 0, + "MaxParallelism": 0, + "MaxParallelismGPU": 0, + "BaseMinMemory": 0, + "MaxConcurrent": 0 + }, + "4": { + "MinMemory": 1048576, + "MaxMemory": 1048576, + "GPUUtilization": 0, + "MaxParallelism": 0, + "MaxParallelismGPU": 0, + "BaseMinMemory": 0, + "MaxConcurrent": 0 + }, + "5": { + "MinMemory": 1048576, + "MaxMemory": 1048576, + "GPUUtilization": 0, + "MaxParallelism": 0, + "MaxParallelismGPU": 0, + "BaseMinMemory": 0, + "MaxConcurrent": 0 + }, + "6": { + "MinMemory": 1048576, + "MaxMemory": 1048576, + "GPUUtilization": 0, + "MaxParallelism": 0, + "MaxParallelismGPU": 0, + "BaseMinMemory": 0, + "MaxConcurrent": 0 + }, + "7": { + "MinMemory": 1048576, + "MaxMemory": 1048576, + "GPUUtilization": 0, + "MaxParallelism": 0, + "MaxParallelismGPU": 0, + "BaseMinMemory": 0, + "MaxConcurrent": 0 + }, + "8": { + "MinMemory": 1048576, + "MaxMemory": 1048576, + "GPUUtilization": 0, + "MaxParallelism": 0, + "MaxParallelismGPU": 0, + "BaseMinMemory": 0, + "MaxConcurrent": 0 + }, + "9": { + "MinMemory": 1048576, + "MaxMemory": 1048576, + "GPUUtilization": 0, + "MaxParallelism": 0, + "MaxParallelismGPU": 0, + "BaseMinMemory": 0, + "MaxConcurrent": 0 + } + }, + "seal/v0/precommit/1": { + "0": { + "MinMemory": 2048, + "MaxMemory": 2048, + "GPUUtilization": 0, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 2048, + "MaxConcurrent": 0 + }, + "1": { + "MinMemory": 8388608, + "MaxMemory": 8388608, + "GPUUtilization": 0, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 8388608, + "MaxConcurrent": 0 + }, + "10": { + "MinMemory": 2048, + "MaxMemory": 2048, + "GPUUtilization": 0, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 2048, + "MaxConcurrent": 0 + }, + "11": { + "MinMemory": 8388608, + "MaxMemory": 8388608, + "GPUUtilization": 0, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 8388608, + "MaxConcurrent": 0 + }, + "12": { + "MinMemory": 805306368, + "MaxMemory": 1073741824, + "GPUUtilization": 0, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 1048576, + "MaxConcurrent": 0 + }, + "13": { + "MinMemory": 60129542144, + "MaxMemory": 68719476736, + "GPUUtilization": 0, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 10485760, + "MaxConcurrent": 0 + }, + "14": { + "MinMemory": 120259084288, + "MaxMemory": 137438953472, + "GPUUtilization": 0, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 10485760, + "MaxConcurrent": 0 + }, + "2": { + "MinMemory": 805306368, + "MaxMemory": 1073741824, + "GPUUtilization": 0, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 1048576, + "MaxConcurrent": 0 + }, + "3": { + "MinMemory": 60129542144, + "MaxMemory": 68719476736, + "GPUUtilization": 0, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 10485760, + "MaxConcurrent": 0 + }, + "4": { + "MinMemory": 120259084288, + "MaxMemory": 137438953472, + "GPUUtilization": 0, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 10485760, + "MaxConcurrent": 0 + }, + "5": { + "MinMemory": 2048, + "MaxMemory": 2048, + "GPUUtilization": 0, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 2048, + "MaxConcurrent": 0 + }, + "6": { + "MinMemory": 8388608, + "MaxMemory": 8388608, + "GPUUtilization": 0, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 8388608, + "MaxConcurrent": 0 + }, + "7": { + "MinMemory": 805306368, + "MaxMemory": 1073741824, + "GPUUtilization": 0, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 1048576, + "MaxConcurrent": 0 + }, + "8": { + "MinMemory": 60129542144, + "MaxMemory": 68719476736, + "GPUUtilization": 0, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 10485760, + "MaxConcurrent": 0 + }, + "9": { + "MinMemory": 120259084288, + "MaxMemory": 137438953472, + "GPUUtilization": 0, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 10485760, + "MaxConcurrent": 0 + } + }, + "seal/v0/precommit/2": { + "0": { + "MinMemory": 2048, + "MaxMemory": 2048, + "GPUUtilization": 0, + "MaxParallelism": -1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 2048, + "MaxConcurrent": 0 + }, + "1": { + "MinMemory": 8388608, + "MaxMemory": 8388608, + "GPUUtilization": 0, + "MaxParallelism": -1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 8388608, + "MaxConcurrent": 0 + }, + "10": { + "MinMemory": 2048, + "MaxMemory": 2048, + "GPUUtilization": 0, + "MaxParallelism": -1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 2048, + "MaxConcurrent": 0 + }, + "11": { + "MinMemory": 8388608, + "MaxMemory": 8388608, + "GPUUtilization": 0, + "MaxParallelism": -1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 8388608, + "MaxConcurrent": 0 + }, + "12": { + "MinMemory": 1073741824, + "MaxMemory": 1610612736, + "GPUUtilization": 0, + "MaxParallelism": -1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 1073741824, + "MaxConcurrent": 0 + }, + "13": { + "MinMemory": 16106127360, + "MaxMemory": 16106127360, + "GPUUtilization": 1, + "MaxParallelism": -1, + "MaxParallelismGPU": 6, + "BaseMinMemory": 1073741824, + "MaxConcurrent": 0 + }, + "14": { + "MinMemory": 32212254720, + "MaxMemory": 32212254720, + "GPUUtilization": 1, + "MaxParallelism": -1, + "MaxParallelismGPU": 6, + "BaseMinMemory": 1073741824, + "MaxConcurrent": 0 + }, + "2": { + "MinMemory": 1073741824, + "MaxMemory": 1610612736, + "GPUUtilization": 0, + "MaxParallelism": -1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 1073741824, + "MaxConcurrent": 0 + }, + "3": { + "MinMemory": 16106127360, + "MaxMemory": 16106127360, + "GPUUtilization": 1, + "MaxParallelism": -1, + "MaxParallelismGPU": 6, + "BaseMinMemory": 1073741824, + "MaxConcurrent": 0 + }, + "4": { + "MinMemory": 32212254720, + "MaxMemory": 32212254720, + "GPUUtilization": 1, + "MaxParallelism": -1, + "MaxParallelismGPU": 6, + "BaseMinMemory": 1073741824, + "MaxConcurrent": 0 + }, + "5": { + "MinMemory": 2048, + "MaxMemory": 2048, + "GPUUtilization": 0, + "MaxParallelism": -1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 2048, + "MaxConcurrent": 0 + }, + "6": { + "MinMemory": 8388608, + "MaxMemory": 8388608, + "GPUUtilization": 0, + "MaxParallelism": -1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 8388608, + "MaxConcurrent": 0 + }, + "7": { + "MinMemory": 1073741824, + "MaxMemory": 1610612736, + "GPUUtilization": 0, + "MaxParallelism": -1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 1073741824, + "MaxConcurrent": 0 + }, + "8": { + "MinMemory": 16106127360, + "MaxMemory": 16106127360, + "GPUUtilization": 1, + "MaxParallelism": -1, + "MaxParallelismGPU": 6, + "BaseMinMemory": 1073741824, + "MaxConcurrent": 0 + }, + "9": { + "MinMemory": 32212254720, + "MaxMemory": 32212254720, + "GPUUtilization": 1, + "MaxParallelism": -1, + "MaxParallelismGPU": 6, + "BaseMinMemory": 1073741824, + "MaxConcurrent": 0 + } + }, + "seal/v0/provereplicaupdate/1": { + "0": { + "MinMemory": 2048, + "MaxMemory": 2048, + "GPUUtilization": 0, + "MaxParallelism": 0, + "MaxParallelismGPU": 0, + "BaseMinMemory": 2048, + "MaxConcurrent": 0 + }, + "1": { + "MinMemory": 8388608, + "MaxMemory": 8388608, + "GPUUtilization": 0, + "MaxParallelism": 0, + "MaxParallelismGPU": 0, + "BaseMinMemory": 8388608, + "MaxConcurrent": 0 + }, + "10": { + "MinMemory": 2048, + "MaxMemory": 2048, + "GPUUtilization": 0, + "MaxParallelism": 0, + "MaxParallelismGPU": 0, + "BaseMinMemory": 2048, + "MaxConcurrent": 0 + }, + "11": { + "MinMemory": 8388608, + "MaxMemory": 8388608, + "GPUUtilization": 0, + "MaxParallelism": 0, + "MaxParallelismGPU": 0, + "BaseMinMemory": 8388608, + "MaxConcurrent": 0 + }, + "12": { + "MinMemory": 1073741824, + "MaxMemory": 1073741824, + "GPUUtilization": 0, + "MaxParallelism": 0, + "MaxParallelismGPU": 0, + "BaseMinMemory": 1073741824, + "MaxConcurrent": 0 + }, + "13": { + "MinMemory": 1073741824, + "MaxMemory": 1073741824, + "GPUUtilization": 0, + "MaxParallelism": 0, + "MaxParallelismGPU": 0, + "BaseMinMemory": 1073741824, + "MaxConcurrent": 0 + }, + "14": { + "MinMemory": 1073741824, + "MaxMemory": 1073741824, + "GPUUtilization": 0, + "MaxParallelism": 0, + "MaxParallelismGPU": 0, + "BaseMinMemory": 1073741824, + "MaxConcurrent": 0 + }, + "2": { + "MinMemory": 1073741824, + "MaxMemory": 1073741824, + "GPUUtilization": 0, + "MaxParallelism": 0, + "MaxParallelismGPU": 0, + "BaseMinMemory": 1073741824, + "MaxConcurrent": 0 + }, + "3": { + "MinMemory": 1073741824, + "MaxMemory": 1073741824, + "GPUUtilization": 0, + "MaxParallelism": 0, + "MaxParallelismGPU": 0, + "BaseMinMemory": 1073741824, + "MaxConcurrent": 0 + }, + "4": { + "MinMemory": 1073741824, + "MaxMemory": 1073741824, + "GPUUtilization": 0, + "MaxParallelism": 0, + "MaxParallelismGPU": 0, + "BaseMinMemory": 1073741824, + "MaxConcurrent": 0 + }, + "5": { + "MinMemory": 2048, + "MaxMemory": 2048, + "GPUUtilization": 0, + "MaxParallelism": 0, + "MaxParallelismGPU": 0, + "BaseMinMemory": 2048, + "MaxConcurrent": 0 + }, + "6": { + "MinMemory": 8388608, + "MaxMemory": 8388608, + "GPUUtilization": 0, + "MaxParallelism": 0, + "MaxParallelismGPU": 0, + "BaseMinMemory": 8388608, + "MaxConcurrent": 0 + }, + "7": { + "MinMemory": 1073741824, + "MaxMemory": 1073741824, + "GPUUtilization": 0, + "MaxParallelism": 0, + "MaxParallelismGPU": 0, + "BaseMinMemory": 1073741824, + "MaxConcurrent": 0 + }, + "8": { + "MinMemory": 1073741824, + "MaxMemory": 1073741824, + "GPUUtilization": 0, + "MaxParallelism": 0, + "MaxParallelismGPU": 0, + "BaseMinMemory": 1073741824, + "MaxConcurrent": 0 + }, + "9": { + "MinMemory": 1073741824, + "MaxMemory": 1073741824, + "GPUUtilization": 0, + "MaxParallelism": 0, + "MaxParallelismGPU": 0, + "BaseMinMemory": 1073741824, + "MaxConcurrent": 0 + } + }, + "seal/v0/provereplicaupdate/2": { + "0": { + "MinMemory": 2048, + "MaxMemory": 2048, + "GPUUtilization": 1, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 2048, + "MaxConcurrent": 0 + }, + "1": { + "MinMemory": 8388608, + "MaxMemory": 8388608, + "GPUUtilization": 1, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 8388608, + "MaxConcurrent": 0 + }, + "10": { + "MinMemory": 2048, + "MaxMemory": 2048, + "GPUUtilization": 1, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 2048, + "MaxConcurrent": 0 + }, + "11": { + "MinMemory": 8388608, + "MaxMemory": 8388608, + "GPUUtilization": 1, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 8388608, + "MaxConcurrent": 0 + }, + "12": { + "MinMemory": 1073741824, + "MaxMemory": 1610612736, + "GPUUtilization": 1, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 10737418240, + "MaxConcurrent": 0 + }, + "13": { + "MinMemory": 32212254720, + "MaxMemory": 161061273600, + "GPUUtilization": 1, + "MaxParallelism": -1, + "MaxParallelismGPU": 6, + "BaseMinMemory": 34359738368, + "MaxConcurrent": 0 + }, + "14": { + "MinMemory": 64424509440, + "MaxMemory": 204010946560, + "GPUUtilization": 1, + "MaxParallelism": -1, + "MaxParallelismGPU": 6, + "BaseMinMemory": 68719476736, + "MaxConcurrent": 0 + }, + "2": { + "MinMemory": 1073741824, + "MaxMemory": 1610612736, + "GPUUtilization": 1, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 10737418240, + "MaxConcurrent": 0 + }, + "3": { + "MinMemory": 32212254720, + "MaxMemory": 161061273600, + "GPUUtilization": 1, + "MaxParallelism": -1, + "MaxParallelismGPU": 6, + "BaseMinMemory": 34359738368, + "MaxConcurrent": 0 + }, + "4": { + "MinMemory": 64424509440, + "MaxMemory": 204010946560, + "GPUUtilization": 1, + "MaxParallelism": -1, + "MaxParallelismGPU": 6, + "BaseMinMemory": 68719476736, + "MaxConcurrent": 0 + }, + "5": { + "MinMemory": 2048, + "MaxMemory": 2048, + "GPUUtilization": 1, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 2048, + "MaxConcurrent": 0 + }, + "6": { + "MinMemory": 8388608, + "MaxMemory": 8388608, + "GPUUtilization": 1, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 8388608, + "MaxConcurrent": 0 + }, + "7": { + "MinMemory": 1073741824, + "MaxMemory": 1610612736, + "GPUUtilization": 1, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 10737418240, + "MaxConcurrent": 0 + }, + "8": { + "MinMemory": 32212254720, + "MaxMemory": 161061273600, + "GPUUtilization": 1, + "MaxParallelism": -1, + "MaxParallelismGPU": 6, + "BaseMinMemory": 34359738368, + "MaxConcurrent": 0 + }, + "9": { + "MinMemory": 64424509440, + "MaxMemory": 204010946560, + "GPUUtilization": 1, + "MaxParallelism": -1, + "MaxParallelismGPU": 6, + "BaseMinMemory": 68719476736, + "MaxConcurrent": 0 + } + }, + "seal/v0/regensectorkey": { + "0": { + "MinMemory": 2048, + "MaxMemory": 2048, + "GPUUtilization": 1, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 2048, + "MaxConcurrent": 0 + }, + "1": { + "MinMemory": 8388608, + "MaxMemory": 8388608, + "GPUUtilization": 1, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 8388608, + "MaxConcurrent": 0 + }, + "10": { + "MinMemory": 2048, + "MaxMemory": 2048, + "GPUUtilization": 1, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 2048, + "MaxConcurrent": 0 + }, + "11": { + "MinMemory": 8388608, + "MaxMemory": 8388608, + "GPUUtilization": 1, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 8388608, + "MaxConcurrent": 0 + }, + "12": { + "MinMemory": 1073741824, + "MaxMemory": 1073741824, + "GPUUtilization": 1, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 1073741824, + "MaxConcurrent": 0 + }, + "13": { + "MinMemory": 4294967296, + "MaxMemory": 4294967296, + "GPUUtilization": 1, + "MaxParallelism": 1, + "MaxParallelismGPU": 6, + "BaseMinMemory": 1073741824, + "MaxConcurrent": 0 + }, + "14": { + "MinMemory": 8589934592, + "MaxMemory": 8589934592, + "GPUUtilization": 1, + "MaxParallelism": 1, + "MaxParallelismGPU": 6, + "BaseMinMemory": 1073741824, + "MaxConcurrent": 0 + }, + "2": { + "MinMemory": 1073741824, + "MaxMemory": 1073741824, + "GPUUtilization": 1, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 1073741824, + "MaxConcurrent": 0 + }, + "3": { + "MinMemory": 4294967296, + "MaxMemory": 4294967296, + "GPUUtilization": 1, + "MaxParallelism": 1, + "MaxParallelismGPU": 6, + "BaseMinMemory": 1073741824, + "MaxConcurrent": 0 + }, + "4": { + "MinMemory": 8589934592, + "MaxMemory": 8589934592, + "GPUUtilization": 1, + "MaxParallelism": 1, + "MaxParallelismGPU": 6, + "BaseMinMemory": 1073741824, + "MaxConcurrent": 0 + }, + "5": { + "MinMemory": 2048, + "MaxMemory": 2048, + "GPUUtilization": 1, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 2048, + "MaxConcurrent": 0 + }, + "6": { + "MinMemory": 8388608, + "MaxMemory": 8388608, + "GPUUtilization": 1, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 8388608, + "MaxConcurrent": 0 + }, + "7": { + "MinMemory": 1073741824, + "MaxMemory": 1073741824, + "GPUUtilization": 1, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 1073741824, + "MaxConcurrent": 0 + }, + "8": { + "MinMemory": 4294967296, + "MaxMemory": 4294967296, + "GPUUtilization": 1, + "MaxParallelism": 1, + "MaxParallelismGPU": 6, + "BaseMinMemory": 1073741824, + "MaxConcurrent": 0 + }, + "9": { + "MinMemory": 8589934592, + "MaxMemory": 8589934592, + "GPUUtilization": 1, + "MaxParallelism": 1, + "MaxParallelismGPU": 6, + "BaseMinMemory": 1073741824, + "MaxConcurrent": 0 + } + }, + "seal/v0/replicaupdate": { + "0": { + "MinMemory": 2048, + "MaxMemory": 2048, + "GPUUtilization": 1, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 2048, + "MaxConcurrent": 0 + }, + "1": { + "MinMemory": 8388608, + "MaxMemory": 8388608, + "GPUUtilization": 1, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 8388608, + "MaxConcurrent": 0 + }, + "10": { + "MinMemory": 2048, + "MaxMemory": 2048, + "GPUUtilization": 1, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 2048, + "MaxConcurrent": 0 + }, + "11": { + "MinMemory": 8388608, + "MaxMemory": 8388608, + "GPUUtilization": 1, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 8388608, + "MaxConcurrent": 0 + }, + "12": { + "MinMemory": 1073741824, + "MaxMemory": 1073741824, + "GPUUtilization": 1, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 1073741824, + "MaxConcurrent": 0 + }, + "13": { + "MinMemory": 4294967296, + "MaxMemory": 4294967296, + "GPUUtilization": 1, + "MaxParallelism": 1, + "MaxParallelismGPU": 6, + "BaseMinMemory": 1073741824, + "MaxConcurrent": 0 + }, + "14": { + "MinMemory": 8589934592, + "MaxMemory": 8589934592, + "GPUUtilization": 1, + "MaxParallelism": 1, + "MaxParallelismGPU": 6, + "BaseMinMemory": 1073741824, + "MaxConcurrent": 0 + }, + "2": { + "MinMemory": 1073741824, + "MaxMemory": 1073741824, + "GPUUtilization": 1, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 1073741824, + "MaxConcurrent": 0 + }, + "3": { + "MinMemory": 4294967296, + "MaxMemory": 4294967296, + "GPUUtilization": 1, + "MaxParallelism": 1, + "MaxParallelismGPU": 6, + "BaseMinMemory": 1073741824, + "MaxConcurrent": 0 + }, + "4": { + "MinMemory": 8589934592, + "MaxMemory": 8589934592, + "GPUUtilization": 1, + "MaxParallelism": 1, + "MaxParallelismGPU": 6, + "BaseMinMemory": 1073741824, + "MaxConcurrent": 0 + }, + "5": { + "MinMemory": 2048, + "MaxMemory": 2048, + "GPUUtilization": 1, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 2048, + "MaxConcurrent": 0 + }, + "6": { + "MinMemory": 8388608, + "MaxMemory": 8388608, + "GPUUtilization": 1, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 8388608, + "MaxConcurrent": 0 + }, + "7": { + "MinMemory": 1073741824, + "MaxMemory": 1073741824, + "GPUUtilization": 1, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 1073741824, + "MaxConcurrent": 0 + }, + "8": { + "MinMemory": 4294967296, + "MaxMemory": 4294967296, + "GPUUtilization": 1, + "MaxParallelism": 1, + "MaxParallelismGPU": 6, + "BaseMinMemory": 1073741824, + "MaxConcurrent": 0 + }, + "9": { + "MinMemory": 8589934592, + "MaxMemory": 8589934592, + "GPUUtilization": 1, + "MaxParallelism": 1, + "MaxParallelismGPU": 6, + "BaseMinMemory": 1073741824, + "MaxConcurrent": 0 + } + }, + "seal/v0/unseal": { + "0": { + "MinMemory": 2048, + "MaxMemory": 2048, + "GPUUtilization": 0, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 2048, + "MaxConcurrent": 0 + }, + "1": { + "MinMemory": 8388608, + "MaxMemory": 8388608, + "GPUUtilization": 0, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 8388608, + "MaxConcurrent": 0 + }, + "10": { + "MinMemory": 2048, + "MaxMemory": 2048, + "GPUUtilization": 0, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 2048, + "MaxConcurrent": 0 + }, + "11": { + "MinMemory": 8388608, + "MaxMemory": 8388608, + "GPUUtilization": 0, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 8388608, + "MaxConcurrent": 0 + }, + "12": { + "MinMemory": 805306368, + "MaxMemory": 1073741824, + "GPUUtilization": 0, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 1048576, + "MaxConcurrent": 0 + }, + "13": { + "MinMemory": 60129542144, + "MaxMemory": 68719476736, + "GPUUtilization": 0, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 10485760, + "MaxConcurrent": 0 + }, + "14": { + "MinMemory": 120259084288, + "MaxMemory": 137438953472, + "GPUUtilization": 0, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 10485760, + "MaxConcurrent": 0 + }, + "2": { + "MinMemory": 805306368, + "MaxMemory": 1073741824, + "GPUUtilization": 0, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 1048576, + "MaxConcurrent": 0 + }, + "3": { + "MinMemory": 60129542144, + "MaxMemory": 68719476736, + "GPUUtilization": 0, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 10485760, + "MaxConcurrent": 0 + }, + "4": { + "MinMemory": 120259084288, + "MaxMemory": 137438953472, + "GPUUtilization": 0, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 10485760, + "MaxConcurrent": 0 + }, + "5": { + "MinMemory": 2048, + "MaxMemory": 2048, + "GPUUtilization": 0, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 2048, + "MaxConcurrent": 0 + }, + "6": { + "MinMemory": 8388608, + "MaxMemory": 8388608, + "GPUUtilization": 0, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 8388608, + "MaxConcurrent": 0 + }, + "7": { + "MinMemory": 805306368, + "MaxMemory": 1073741824, + "GPUUtilization": 0, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 1048576, + "MaxConcurrent": 0 + }, + "8": { + "MinMemory": 60129542144, + "MaxMemory": 68719476736, + "GPUUtilization": 0, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 10485760, + "MaxConcurrent": 0 + }, + "9": { + "MinMemory": 120259084288, + "MaxMemory": 137438953472, + "GPUUtilization": 0, + "MaxParallelism": 1, + "MaxParallelismGPU": 0, + "BaseMinMemory": 10485760, + "MaxConcurrent": 0 + } + } + } + } + } + ], + "additionalProperties": false, + "properties": { + "Hostname": { + "type": "string" + }, + "IgnoreResources": { + "type": "boolean" + }, + "Resources": { + "additionalProperties": false, + "properties": { + "CPUs": { + "title": "number", + "type": "number" + }, + "GPUs": { + "items": { + "type": "string" + }, + "type": "array" + }, + "MemPhysical": { + "title": "number", + "type": "number" + }, + "MemSwap": { + "title": "number", + "type": "number" + }, + "MemSwapUsed": { + "title": "number", + "type": "number" + }, + "MemUsed": { + "title": "number", + "type": "number" + }, + "Resources": { + "patternProperties": { + ".*": { + "patternProperties": { + ".*": { + "additionalProperties": false, + "properties": { + "BaseMinMemory": { + "title": "number", + "type": "number" + }, + "GPUUtilization": { + "type": "number" + }, + "MaxConcurrent": { + "title": "number", + "type": "number" + }, + "MaxMemory": { + "title": "number", + "type": "number" + }, + "MaxParallelism": { + "title": "number", + "type": "number" + }, + "MaxParallelismGPU": { + "title": "number", + "type": "number" + }, + "MinMemory": { + "title": "number", + "type": "number" + } + }, + "type": "object" + } + }, + "type": "object" + } + }, + "type": "object" + } + }, + "type": "object" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7151" + } + }, + { + "name": "Filecoin.MoveStorage", + "description": "```go\nfunc (s *WorkerStruct) MoveStorage(p0 context.Context, p1 storiface.SectorRef, p2 storiface.SectorFileType) (storiface.CallID, error) {\n\tif s.Internal.MoveStorage == nil {\n\t\treturn *new(storiface.CallID), ErrNotSupported\n\t}\n\treturn s.Internal.MoveStorage(p0, p1, p2)\n}\n```", + "summary": "", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "storiface.SectorRef", + "summary": "", + "schema": { + "examples": [ + { + "ID": { + "Miner": 1000, + "Number": 9 + }, + "ProofType": 8 + } + ], + "additionalProperties": false, + "properties": { + "ID": { + "additionalProperties": false, + "properties": { + "Miner": { + "title": "number", + "type": "number" + }, + "Number": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "ProofType": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "storiface.SectorFileType", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 1 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "storiface.CallID", + "description": "storiface.CallID", + "summary": "", + "schema": { + "examples": [ + { + "Sector": { + "Miner": 1000, + "Number": 9 + }, + "ID": "07070707-0707-0707-0707-070707070707" + } + ], + "additionalProperties": false, + "properties": { + "ID": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 16, + "minItems": 16, + "type": "array" + }, + "Sector": { + "additionalProperties": false, + "properties": { + "Miner": { + "title": "number", + "type": "number" + }, + "Number": { + "title": "number", + "type": "number" + } + }, + "type": "object" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7162" + } + }, + { + "name": "Filecoin.Paths", + "description": "```go\nfunc (s *WorkerStruct) Paths(p0 context.Context) ([]storiface.StoragePath, error) {\n\tif s.Internal.Paths == nil {\n\t\treturn *new([]storiface.StoragePath), ErrNotSupported\n\t}\n\treturn s.Internal.Paths(p0)\n}\n```", + "summary": "", + "paramStructure": "by-position", + "params": [], + "result": { + "name": "[]storiface.StoragePath", + "description": "[]storiface.StoragePath", + "summary": "", + "schema": { + "examples": [ + [ + { + "ID": "76f1988b-ef30-4d7e-b3ec-9a627f4ba5a8", + "Weight": 42, + "LocalPath": "string value", + "CanSeal": true, + "CanStore": true + } + ] + ], + "items": [ + { + "additionalProperties": false, + "properties": { + "CanSeal": { + "type": "boolean" + }, + "CanStore": { + "type": "boolean" + }, + "ID": { + "type": "string" + }, + "LocalPath": { + "type": "string" + }, + "Weight": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7173" + } + }, + { + "name": "Filecoin.ProcessSession", + "description": "```go\nfunc (s *WorkerStruct) ProcessSession(p0 context.Context) (uuid.UUID, error) {\n\tif s.Internal.ProcessSession == nil {\n\t\treturn *new(uuid.UUID), ErrNotSupported\n\t}\n\treturn s.Internal.ProcessSession(p0)\n}\n```", + "summary": "returns a random UUID of worker session, generated randomly when worker\nprocess starts\n", + "paramStructure": "by-position", + "params": [], + "result": { + "name": "uuid.UUID", + "description": "uuid.UUID", + "summary": "", + "schema": { + "examples": [ + "07070707-0707-0707-0707-070707070707" + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "maxItems": 16, + "minItems": 16, + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7184" + } + }, + { + "name": "Filecoin.ProveReplicaUpdate1", + "description": "```go\nfunc (s *WorkerStruct) ProveReplicaUpdate1(p0 context.Context, p1 storiface.SectorRef, p2 cid.Cid, p3 cid.Cid, p4 cid.Cid) (storiface.CallID, error) {\n\tif s.Internal.ProveReplicaUpdate1 == nil {\n\t\treturn *new(storiface.CallID), ErrNotSupported\n\t}\n\treturn s.Internal.ProveReplicaUpdate1(p0, p1, p2, p3, p4)\n}\n```", + "summary": "", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "storiface.SectorRef", + "summary": "", + "schema": { + "examples": [ + { + "ID": { + "Miner": 1000, + "Number": 9 + }, + "ProofType": 8 + } + ], + "additionalProperties": false, + "properties": { + "ID": { + "additionalProperties": false, + "properties": { + "Miner": { + "title": "number", + "type": "number" + }, + "Number": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "ProofType": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "cid.Cid", + "summary": "", + "schema": { + "title": "Content Identifier", + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "examples": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p3", + "description": "cid.Cid", + "summary": "", + "schema": { + "title": "Content Identifier", + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "examples": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p4", + "description": "cid.Cid", + "summary": "", + "schema": { + "title": "Content Identifier", + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "examples": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "storiface.CallID", + "description": "storiface.CallID", + "summary": "", + "schema": { + "examples": [ + { + "Sector": { + "Miner": 1000, + "Number": 9 + }, + "ID": "07070707-0707-0707-0707-070707070707" + } + ], + "additionalProperties": false, + "properties": { + "ID": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 16, + "minItems": 16, + "type": "array" + }, + "Sector": { + "additionalProperties": false, + "properties": { + "Miner": { + "title": "number", + "type": "number" + }, + "Number": { + "title": "number", + "type": "number" + } + }, + "type": "object" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7195" + } + }, + { + "name": "Filecoin.ProveReplicaUpdate2", + "description": "```go\nfunc (s *WorkerStruct) ProveReplicaUpdate2(p0 context.Context, p1 storiface.SectorRef, p2 cid.Cid, p3 cid.Cid, p4 cid.Cid, p5 storiface.ReplicaVanillaProofs) (storiface.CallID, error) {\n\tif s.Internal.ProveReplicaUpdate2 == nil {\n\t\treturn *new(storiface.CallID), ErrNotSupported\n\t}\n\treturn s.Internal.ProveReplicaUpdate2(p0, p1, p2, p3, p4, p5)\n}\n```", + "summary": "", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "storiface.SectorRef", + "summary": "", + "schema": { + "examples": [ + { + "ID": { + "Miner": 1000, + "Number": 9 + }, + "ProofType": 8 + } + ], + "additionalProperties": false, + "properties": { + "ID": { + "additionalProperties": false, + "properties": { + "Miner": { + "title": "number", + "type": "number" + }, + "Number": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "ProofType": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "cid.Cid", + "summary": "", + "schema": { + "title": "Content Identifier", + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "examples": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p3", + "description": "cid.Cid", + "summary": "", + "schema": { + "title": "Content Identifier", + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "examples": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p4", + "description": "cid.Cid", + "summary": "", + "schema": { + "title": "Content Identifier", + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "examples": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p5", + "description": "storiface.ReplicaVanillaProofs", + "summary": "", + "schema": { + "examples": [ + [ + "Ynl0ZSBhcnJheQ==" + ] + ], + "items": [ + { + "type": [ + "string" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "storiface.CallID", + "description": "storiface.CallID", + "summary": "", + "schema": { + "examples": [ + { + "Sector": { + "Miner": 1000, + "Number": 9 + }, + "ID": "07070707-0707-0707-0707-070707070707" + } + ], + "additionalProperties": false, + "properties": { + "ID": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 16, + "minItems": 16, + "type": "array" + }, + "Sector": { + "additionalProperties": false, + "properties": { + "Miner": { + "title": "number", + "type": "number" + }, + "Number": { + "title": "number", + "type": "number" + } + }, + "type": "object" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7206" + } + }, + { + "name": "Filecoin.ReleaseUnsealed", + "description": "```go\nfunc (s *WorkerStruct) ReleaseUnsealed(p0 context.Context, p1 storiface.SectorRef, p2 []storiface.Range) (storiface.CallID, error) {\n\tif s.Internal.ReleaseUnsealed == nil {\n\t\treturn *new(storiface.CallID), ErrNotSupported\n\t}\n\treturn s.Internal.ReleaseUnsealed(p0, p1, p2)\n}\n```", + "summary": "", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "storiface.SectorRef", + "summary": "", + "schema": { + "examples": [ + { + "ID": { + "Miner": 1000, + "Number": 9 + }, + "ProofType": 8 + } + ], + "additionalProperties": false, + "properties": { + "ID": { + "additionalProperties": false, + "properties": { + "Miner": { + "title": "number", + "type": "number" + }, + "Number": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "ProofType": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "[]storiface.Range", + "summary": "", + "schema": { + "examples": [ + [ + { + "Offset": 1024, + "Size": 1024 + } + ] + ], + "items": [ + { + "additionalProperties": false, + "properties": { + "Offset": { + "title": "number", + "type": "number" + }, + "Size": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "storiface.CallID", + "description": "storiface.CallID", + "summary": "", + "schema": { + "examples": [ + { + "Sector": { + "Miner": 1000, + "Number": 9 + }, + "ID": "07070707-0707-0707-0707-070707070707" + } + ], + "additionalProperties": false, + "properties": { + "ID": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 16, + "minItems": 16, + "type": "array" + }, + "Sector": { + "additionalProperties": false, + "properties": { + "Miner": { + "title": "number", + "type": "number" + }, + "Number": { + "title": "number", + "type": "number" + } + }, + "type": "object" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7217" + } + }, + { + "name": "Filecoin.Remove", + "description": "```go\nfunc (s *WorkerStruct) Remove(p0 context.Context, p1 abi.SectorID) error {\n\tif s.Internal.Remove == nil {\n\t\treturn ErrNotSupported\n\t}\n\treturn s.Internal.Remove(p0, p1)\n}\n```", + "summary": "Storage / Other\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "abi.SectorID", + "summary": "", + "schema": { + "examples": [ + { + "Miner": 1000, + "Number": 9 + } + ], + "additionalProperties": false, + "properties": { + "Miner": { + "title": "number", + "type": "number" + }, + "Number": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "Null", + "description": "Null", + "schema": { + "type": [ + "null" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7228" + } + }, + { + "name": "Filecoin.ReplicaUpdate", + "description": "```go\nfunc (s *WorkerStruct) ReplicaUpdate(p0 context.Context, p1 storiface.SectorRef, p2 []abi.PieceInfo) (storiface.CallID, error) {\n\tif s.Internal.ReplicaUpdate == nil {\n\t\treturn *new(storiface.CallID), ErrNotSupported\n\t}\n\treturn s.Internal.ReplicaUpdate(p0, p1, p2)\n}\n```", + "summary": "", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "storiface.SectorRef", + "summary": "", + "schema": { + "examples": [ + { + "ID": { + "Miner": 1000, + "Number": 9 + }, + "ProofType": 8 + } + ], + "additionalProperties": false, + "properties": { + "ID": { + "additionalProperties": false, + "properties": { + "Miner": { + "title": "number", + "type": "number" + }, + "Number": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "ProofType": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "[]abi.PieceInfo", + "summary": "", + "schema": { + "examples": [ + [ + { + "Size": 1032, + "PieceCID": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + } + ] + ], + "items": [ + { + "additionalProperties": false, + "properties": { + "PieceCID": { + "title": "Content Identifier", + "type": "string" + }, + "Size": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "storiface.CallID", + "description": "storiface.CallID", + "summary": "", + "schema": { + "examples": [ + { + "Sector": { + "Miner": 1000, + "Number": 9 + }, + "ID": "07070707-0707-0707-0707-070707070707" + } + ], + "additionalProperties": false, + "properties": { + "ID": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 16, + "minItems": 16, + "type": "array" + }, + "Sector": { + "additionalProperties": false, + "properties": { + "Miner": { + "title": "number", + "type": "number" + }, + "Number": { + "title": "number", + "type": "number" + } + }, + "type": "object" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7239" + } + }, + { + "name": "Filecoin.SealCommit1", + "description": "```go\nfunc (s *WorkerStruct) SealCommit1(p0 context.Context, p1 storiface.SectorRef, p2 abi.SealRandomness, p3 abi.InteractiveSealRandomness, p4 []abi.PieceInfo, p5 storiface.SectorCids) (storiface.CallID, error) {\n\tif s.Internal.SealCommit1 == nil {\n\t\treturn *new(storiface.CallID), ErrNotSupported\n\t}\n\treturn s.Internal.SealCommit1(p0, p1, p2, p3, p4, p5)\n}\n```", + "summary": "", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "storiface.SectorRef", + "summary": "", + "schema": { + "examples": [ + { + "ID": { + "Miner": 1000, + "Number": 9 + }, + "ProofType": 8 + } + ], + "additionalProperties": false, + "properties": { + "ID": { + "additionalProperties": false, + "properties": { + "Miner": { + "title": "number", + "type": "number" + }, + "Number": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "ProofType": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "abi.SealRandomness", + "summary": "", + "schema": { + "examples": [ + "Bw==" + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p3", + "description": "abi.InteractiveSealRandomness", + "summary": "", + "schema": { + "examples": [ + "Bw==" + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p4", + "description": "[]abi.PieceInfo", + "summary": "", + "schema": { + "examples": [ + [ + { + "Size": 1032, + "PieceCID": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + } + ] + ], + "items": [ + { + "additionalProperties": false, + "properties": { + "PieceCID": { + "title": "Content Identifier", + "type": "string" + }, + "Size": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p5", + "description": "storiface.SectorCids", + "summary": "", + "schema": { + "examples": [ + { + "Unsealed": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Sealed": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + } + ], + "additionalProperties": false, + "properties": { + "Sealed": { + "title": "Content Identifier", + "type": "string" + }, + "Unsealed": { + "title": "Content Identifier", + "type": "string" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "storiface.CallID", + "description": "storiface.CallID", + "summary": "", + "schema": { + "examples": [ + { + "Sector": { + "Miner": 1000, + "Number": 9 + }, + "ID": "07070707-0707-0707-0707-070707070707" + } + ], + "additionalProperties": false, + "properties": { + "ID": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 16, + "minItems": 16, + "type": "array" + }, + "Sector": { + "additionalProperties": false, + "properties": { + "Miner": { + "title": "number", + "type": "number" + }, + "Number": { + "title": "number", + "type": "number" + } + }, + "type": "object" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7250" + } + }, + { + "name": "Filecoin.SealCommit2", + "description": "```go\nfunc (s *WorkerStruct) SealCommit2(p0 context.Context, p1 storiface.SectorRef, p2 storiface.Commit1Out) (storiface.CallID, error) {\n\tif s.Internal.SealCommit2 == nil {\n\t\treturn *new(storiface.CallID), ErrNotSupported\n\t}\n\treturn s.Internal.SealCommit2(p0, p1, p2)\n}\n```", + "summary": "", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "storiface.SectorRef", + "summary": "", + "schema": { + "examples": [ + { + "ID": { + "Miner": 1000, + "Number": 9 + }, + "ProofType": 8 + } + ], + "additionalProperties": false, + "properties": { + "ID": { + "additionalProperties": false, + "properties": { + "Miner": { + "title": "number", + "type": "number" + }, + "Number": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "ProofType": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "storiface.Commit1Out", + "summary": "", + "schema": { + "examples": [ + "Bw==" + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "storiface.CallID", + "description": "storiface.CallID", + "summary": "", + "schema": { + "examples": [ + { + "Sector": { + "Miner": 1000, + "Number": 9 + }, + "ID": "07070707-0707-0707-0707-070707070707" + } + ], + "additionalProperties": false, + "properties": { + "ID": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 16, + "minItems": 16, + "type": "array" + }, + "Sector": { + "additionalProperties": false, + "properties": { + "Miner": { + "title": "number", + "type": "number" + }, + "Number": { + "title": "number", + "type": "number" + } + }, + "type": "object" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7261" + } + }, + { + "name": "Filecoin.SealPreCommit1", + "description": "```go\nfunc (s *WorkerStruct) SealPreCommit1(p0 context.Context, p1 storiface.SectorRef, p2 abi.SealRandomness, p3 []abi.PieceInfo) (storiface.CallID, error) {\n\tif s.Internal.SealPreCommit1 == nil {\n\t\treturn *new(storiface.CallID), ErrNotSupported\n\t}\n\treturn s.Internal.SealPreCommit1(p0, p1, p2, p3)\n}\n```", + "summary": "", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "storiface.SectorRef", + "summary": "", + "schema": { + "examples": [ + { + "ID": { + "Miner": 1000, + "Number": 9 + }, + "ProofType": 8 + } + ], + "additionalProperties": false, + "properties": { + "ID": { + "additionalProperties": false, + "properties": { + "Miner": { + "title": "number", + "type": "number" + }, + "Number": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "ProofType": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "abi.SealRandomness", + "summary": "", + "schema": { + "examples": [ + "Bw==" + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p3", + "description": "[]abi.PieceInfo", + "summary": "", + "schema": { + "examples": [ + [ + { + "Size": 1032, + "PieceCID": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + } + ] + ], + "items": [ + { + "additionalProperties": false, + "properties": { + "PieceCID": { + "title": "Content Identifier", + "type": "string" + }, + "Size": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "storiface.CallID", + "description": "storiface.CallID", + "summary": "", + "schema": { + "examples": [ + { + "Sector": { + "Miner": 1000, + "Number": 9 + }, + "ID": "07070707-0707-0707-0707-070707070707" + } + ], + "additionalProperties": false, + "properties": { + "ID": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 16, + "minItems": 16, + "type": "array" + }, + "Sector": { + "additionalProperties": false, + "properties": { + "Miner": { + "title": "number", + "type": "number" + }, + "Number": { + "title": "number", + "type": "number" + } + }, + "type": "object" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7272" + } + }, + { + "name": "Filecoin.SealPreCommit2", + "description": "```go\nfunc (s *WorkerStruct) SealPreCommit2(p0 context.Context, p1 storiface.SectorRef, p2 storiface.PreCommit1Out) (storiface.CallID, error) {\n\tif s.Internal.SealPreCommit2 == nil {\n\t\treturn *new(storiface.CallID), ErrNotSupported\n\t}\n\treturn s.Internal.SealPreCommit2(p0, p1, p2)\n}\n```", + "summary": "", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "storiface.SectorRef", + "summary": "", + "schema": { + "examples": [ + { + "ID": { + "Miner": 1000, + "Number": 9 + }, + "ProofType": 8 + } + ], + "additionalProperties": false, + "properties": { + "ID": { + "additionalProperties": false, + "properties": { + "Miner": { + "title": "number", + "type": "number" + }, + "Number": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "ProofType": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "storiface.PreCommit1Out", + "summary": "", + "schema": { + "examples": [ + "Bw==" + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "storiface.CallID", + "description": "storiface.CallID", + "summary": "", + "schema": { + "examples": [ + { + "Sector": { + "Miner": 1000, + "Number": 9 + }, + "ID": "07070707-0707-0707-0707-070707070707" + } + ], + "additionalProperties": false, + "properties": { + "ID": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 16, + "minItems": 16, + "type": "array" + }, + "Sector": { + "additionalProperties": false, + "properties": { + "Miner": { + "title": "number", + "type": "number" + }, + "Number": { + "title": "number", + "type": "number" + } + }, + "type": "object" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7283" + } + }, + { + "name": "Filecoin.Session", + "description": "```go\nfunc (s *WorkerStruct) Session(p0 context.Context) (uuid.UUID, error) {\n\tif s.Internal.Session == nil {\n\t\treturn *new(uuid.UUID), ErrNotSupported\n\t}\n\treturn s.Internal.Session(p0)\n}\n```", + "summary": "Like ProcessSession, but returns an error when worker is disabled\n", + "paramStructure": "by-position", + "params": [], + "result": { + "name": "uuid.UUID", + "description": "uuid.UUID", + "summary": "", + "schema": { + "examples": [ + "07070707-0707-0707-0707-070707070707" + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "maxItems": 16, + "minItems": 16, + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7294" + } + }, + { + "name": "Filecoin.SetEnabled", + "description": "```go\nfunc (s *WorkerStruct) SetEnabled(p0 context.Context, p1 bool) error {\n\tif s.Internal.SetEnabled == nil {\n\t\treturn ErrNotSupported\n\t}\n\treturn s.Internal.SetEnabled(p0, p1)\n}\n```", + "summary": "SetEnabled marks the worker as enabled/disabled. Not that this setting\nmay take a few seconds to propagate to task scheduler\n", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "bool", + "summary": "", + "schema": { + "examples": [ + true + ], + "type": [ + "boolean" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "Null", + "description": "Null", + "schema": { + "type": [ + "null" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7305" + } + }, + { + "name": "Filecoin.Shutdown", + "description": "```go\nfunc (s *WorkerStruct) Shutdown(p0 context.Context) error {\n\tif s.Internal.Shutdown == nil {\n\t\treturn ErrNotSupported\n\t}\n\treturn s.Internal.Shutdown(p0)\n}\n```", + "summary": "Trigger shutdown\n", + "paramStructure": "by-position", + "params": [], + "result": { + "name": "Null", + "description": "Null", + "schema": { + "type": [ + "null" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7316" + } + }, + { + "name": "Filecoin.StorageAddLocal", + "description": "```go\nfunc (s *WorkerStruct) StorageAddLocal(p0 context.Context, p1 string) error {\n\tif s.Internal.StorageAddLocal == nil {\n\t\treturn ErrNotSupported\n\t}\n\treturn s.Internal.StorageAddLocal(p0, p1)\n}\n```", + "summary": "", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "string", + "summary": "", + "schema": { + "examples": [ + "string value" + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "Null", + "description": "Null", + "schema": { + "type": [ + "null" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7327" + } + }, + { + "name": "Filecoin.StorageDetachAll", + "description": "```go\nfunc (s *WorkerStruct) StorageDetachAll(p0 context.Context) error {\n\tif s.Internal.StorageDetachAll == nil {\n\t\treturn ErrNotSupported\n\t}\n\treturn s.Internal.StorageDetachAll(p0)\n}\n```", + "summary": "", + "paramStructure": "by-position", + "params": [], + "result": { + "name": "Null", + "description": "Null", + "schema": { + "type": [ + "null" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7338" + } + }, + { + "name": "Filecoin.StorageDetachLocal", + "description": "```go\nfunc (s *WorkerStruct) StorageDetachLocal(p0 context.Context, p1 string) error {\n\tif s.Internal.StorageDetachLocal == nil {\n\t\treturn ErrNotSupported\n\t}\n\treturn s.Internal.StorageDetachLocal(p0, p1)\n}\n```", + "summary": "", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "string", + "summary": "", + "schema": { + "examples": [ + "string value" + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "Null", + "description": "Null", + "schema": { + "type": [ + "null" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7349" + } + }, + { + "name": "Filecoin.StorageLocal", + "description": "```go\nfunc (s *WorkerStruct) StorageLocal(p0 context.Context) (map[storiface.ID]string, error) {\n\tif s.Internal.StorageLocal == nil {\n\t\treturn *new(map[storiface.ID]string), ErrNotSupported\n\t}\n\treturn s.Internal.StorageLocal(p0)\n}\n```", + "summary": "", + "paramStructure": "by-position", + "params": [], + "result": { + "name": "map[storiface.ID]string", + "description": "map[storiface.ID]string", + "summary": "", + "schema": { + "examples": [ + { + "76f1988b-ef30-4d7e-b3ec-9a627f4ba5a8": "/data/path" + } + ], + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7360" + } + }, + { + "name": "Filecoin.StorageRedeclareLocal", + "description": "```go\nfunc (s *WorkerStruct) StorageRedeclareLocal(p0 context.Context, p1 *storiface.ID, p2 bool) error {\n\tif s.Internal.StorageRedeclareLocal == nil {\n\t\treturn ErrNotSupported\n\t}\n\treturn s.Internal.StorageRedeclareLocal(p0, p1, p2)\n}\n```", + "summary": "", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "*storiface.ID", + "summary": "", + "schema": { + "examples": [ + "1399aa04-2625-44b1-bad4-bd07b59b22c4" + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "bool", + "summary": "", + "schema": { + "examples": [ + true + ], + "type": [ + "boolean" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "Null", + "description": "Null", + "schema": { + "type": [ + "null" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7371" + } + }, + { + "name": "Filecoin.TaskDisable", + "description": "```go\nfunc (s *WorkerStruct) TaskDisable(p0 context.Context, p1 sealtasks.TaskType) error {\n\tif s.Internal.TaskDisable == nil {\n\t\treturn ErrNotSupported\n\t}\n\treturn s.Internal.TaskDisable(p0, p1)\n}\n```", + "summary": "", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "sealtasks.TaskType", + "summary": "", + "schema": { + "examples": [ + "seal/v0/commit/2" + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "Null", + "description": "Null", + "schema": { + "type": [ + "null" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7382" + } + }, + { + "name": "Filecoin.TaskEnable", + "description": "```go\nfunc (s *WorkerStruct) TaskEnable(p0 context.Context, p1 sealtasks.TaskType) error {\n\tif s.Internal.TaskEnable == nil {\n\t\treturn ErrNotSupported\n\t}\n\treturn s.Internal.TaskEnable(p0, p1)\n}\n```", + "summary": "", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "sealtasks.TaskType", + "summary": "", + "schema": { + "examples": [ + "seal/v0/commit/2" + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "Null", + "description": "Null", + "schema": { + "type": [ + "null" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7393" + } + }, + { + "name": "Filecoin.TaskTypes", + "description": "```go\nfunc (s *WorkerStruct) TaskTypes(p0 context.Context) (map[sealtasks.TaskType]struct{}, error) {\n\tif s.Internal.TaskTypes == nil {\n\t\treturn *new(map[sealtasks.TaskType]struct{}), ErrNotSupported\n\t}\n\treturn s.Internal.TaskTypes(p0)\n}\n```", + "summary": "TaskType -\u003e Weight\n", + "paramStructure": "by-position", + "params": [], + "result": { + "name": "map[sealtasks.TaskType]struct{}", + "description": "map[sealtasks.TaskType]struct{}", + "summary": "", + "schema": { + "examples": [ + { + "seal/v0/precommit/2": {} + } + ], + "patternProperties": { + ".*": { + "additionalProperties": false, + "type": "object" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7404" + } + }, + { + "name": "Filecoin.UnsealPiece", + "description": "```go\nfunc (s *WorkerStruct) UnsealPiece(p0 context.Context, p1 storiface.SectorRef, p2 storiface.UnpaddedByteIndex, p3 abi.UnpaddedPieceSize, p4 abi.SealRandomness, p5 cid.Cid) (storiface.CallID, error) {\n\tif s.Internal.UnsealPiece == nil {\n\t\treturn *new(storiface.CallID), ErrNotSupported\n\t}\n\treturn s.Internal.UnsealPiece(p0, p1, p2, p3, p4, p5)\n}\n```", + "summary": "", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "storiface.SectorRef", + "summary": "", + "schema": { + "examples": [ + { + "ID": { + "Miner": 1000, + "Number": 9 + }, + "ProofType": 8 + } + ], + "additionalProperties": false, + "properties": { + "ID": { + "additionalProperties": false, + "properties": { + "Miner": { + "title": "number", + "type": "number" + }, + "Number": { + "title": "number", + "type": "number" + } + }, + "type": "object" + }, + "ProofType": { + "title": "number", + "type": "number" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "storiface.UnpaddedByteIndex", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 1040384 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p3", + "description": "abi.UnpaddedPieceSize", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 1024 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p4", + "description": "abi.SealRandomness", + "summary": "", + "schema": { + "examples": [ + "Bw==" + ], + "items": [ + { + "title": "number", + "description": "Number is a number", + "type": [ + "number" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p5", + "description": "cid.Cid", + "summary": "", + "schema": { + "title": "Content Identifier", + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "examples": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "storiface.CallID", + "description": "storiface.CallID", + "summary": "", + "schema": { + "examples": [ + { + "Sector": { + "Miner": 1000, + "Number": 9 + }, + "ID": "07070707-0707-0707-0707-070707070707" + } + ], + "additionalProperties": false, + "properties": { + "ID": { + "items": { + "description": "Number is a number", + "title": "number", + "type": "number" + }, + "maxItems": 16, + "minItems": 16, + "type": "array" + }, + "Sector": { + "additionalProperties": false, + "properties": { + "Miner": { + "title": "number", + "type": "number" + }, + "Number": { + "title": "number", + "type": "number" + } + }, + "type": "object" + } + }, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7415" + } + }, + { + "name": "Filecoin.Version", + "description": "```go\nfunc (s *WorkerStruct) Version(p0 context.Context) (Version, error) {\n\tif s.Internal.Version == nil {\n\t\treturn *new(Version), ErrNotSupported\n\t}\n\treturn s.Internal.Version(p0)\n}\n```", + "summary": "", + "paramStructure": "by-position", + "params": [], + "result": { + "name": "Version", + "description": "Version", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 131840 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7426" + } + }, + { + "name": "Filecoin.WaitQuiet", + "description": "```go\nfunc (s *WorkerStruct) WaitQuiet(p0 context.Context) error {\n\tif s.Internal.WaitQuiet == nil {\n\t\treturn ErrNotSupported\n\t}\n\treturn s.Internal.WaitQuiet(p0)\n}\n```", + "summary": "WaitQuiet blocks until there are no tasks running\n", + "paramStructure": "by-position", + "params": [], + "result": { + "name": "Null", + "description": "Null", + "schema": { + "type": [ + "null" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7437" + } + } + ] +} \ No newline at end of file diff --git a/build/openrpc/worker.json.gz b/build/openrpc/worker.json.gz deleted file mode 100644 index 9b4f0243d684cfa2d3e19daa1481dd88845da7bf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6050 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 z
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 Date: Sun, 17 Dec 2023 12:41:45 +0100 Subject: [PATCH 088/394] begin drafting lotus-provider SDR pipeline --- lib/harmony/harmonydb/sql/20231217.sql | 86 ++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 lib/harmony/harmonydb/sql/20231217.sql diff --git a/lib/harmony/harmonydb/sql/20231217.sql b/lib/harmony/harmonydb/sql/20231217.sql new file mode 100644 index 000000000..d744fbaf5 --- /dev/null +++ b/lib/harmony/harmonydb/sql/20231217.sql @@ -0,0 +1,86 @@ +-- NOTE: task_ids can be the same between different task types and between different sectors +-- e.g. SN-supraseal doing 128 sdr/TreeC/TreeR with the same task_id + +create table sectors_sdr_pipeline ( + sp_id bigint not null, + sector_number bigint not null, + + -- at request time + create_time timestamp not null, + reg_seal_proof int not null, + + -- sdr + ticket_epoch bigint, + ticket_value bytea, + + task_id_sdr bigint, + after_sdr bool not null default false, + + -- tree D + tree_d_cid text, -- commd from treeD compute + + task_id_tree_d bigint, + after_tree_d bool not null default false, + + -- tree C + task_id_tree_c bigint, + after_tree_c bool not null default false, + + -- tree R + tree_r_cid text, -- commr from treeR compute + + task_id_tree_r bigint, + after_tree_r bool not null default false, + + -- precommit message sending + precommit_msg_cid text, + + task_id_precommit_msg bigint, + after_precommit_msg bool not null default false, + + -- precommit message wait + seed_epoch bigint, + precommit_msg_tsk bytea, + + task_id_precommit_msg_wait bigint, + after_precommit_msg_success bool not null default false, + + -- seed + seed_value bytea, + + -- Commit (PoRep snark) + task_id_porep bigint, + porep_proof bytea, + + -- Commit message sending + commit_msg_cid text, + + task_id_commit_msg bigint, + after_commit_msg bool not null default false, + + -- Commit message wait + commit_msg_tsk bytea, + + task_id_commit_msg_wait bigint, + after_commit_msg_success bool not null default false, + + -- Failure handling + failed bool not null default false, + failed_at timestamp, + failed_reason varchar(20), + failed_reason_msg text, + + -- constraints + primary key (sp_id, sector_number) +); + +create table sectors_sdr_initial_pieces ( + sp_id bigint not null, + sector_number bigint not null, + + piece_index bigint not null, + piece_cid text not null, + piece_size bigint not null, + + primary key (sp_id, sector_number, piece_index) +); From e89fff2308f799e7916b431df633369c69615103 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 18 Dec 2023 12:02:48 +0100 Subject: [PATCH 089/394] lpseal: Sector number allocator, pipeline start cli --- cmd/lotus-provider/deps/deps.go | 29 ++++ cmd/lotus-provider/main.go | 1 + cmd/lotus-provider/pipeline.go | 144 ++++++++++++++++++ ...20231217.sql => 20231217-sdr-pipeline.sql} | 10 +- provider/lpseal/sector_num_alloc.go | 119 +++++++++++++++ 5 files changed, 301 insertions(+), 2 deletions(-) create mode 100644 cmd/lotus-provider/pipeline.go rename lib/harmony/harmonydb/sql/{20231217.sql => 20231217-sdr-pipeline.sql} (87%) create mode 100644 provider/lpseal/sector_num_alloc.go diff --git a/cmd/lotus-provider/deps/deps.go b/cmd/lotus-provider/deps/deps.go index 1e6180d71..da8eeb8aa 100644 --- a/cmd/lotus-provider/deps/deps.go +++ b/cmd/lotus-provider/deps/deps.go @@ -292,3 +292,32 @@ func GetConfig(cctx *cli.Context, db *harmonydb.DB) (*config.LotusProviderConfig // validate the config. Because of layering, we must validate @ startup. return lp, nil } + +func GetDepsCLI(ctx context.Context, cctx *cli.Context) (*Deps, error) { + db, err := MakeDB(cctx) + if err != nil { + return nil, err + } + + cfg, err := GetConfig(cctx, db) + if err != nil { + return nil, err + } + + full, fullCloser, err := cliutil.GetFullNodeAPIV1LotusProvider(cctx, cfg.Apis.ChainApiInfo) + if err != nil { + return nil, err + } + go func() { + select { + case <-ctx.Done(): + fullCloser() + } + }() + + return &Deps{ + Cfg: cfg, + DB: db, + Full: full, + }, nil +} diff --git a/cmd/lotus-provider/main.go b/cmd/lotus-provider/main.go index 1b025303c..d6185cc3c 100644 --- a/cmd/lotus-provider/main.go +++ b/cmd/lotus-provider/main.go @@ -47,6 +47,7 @@ func main() { configCmd, testCmd, webCmd, + pipelineCmd, //backupCmd, //lcli.WithCategory("chain", actorCmd), //lcli.WithCategory("storage", sectorsCmd), diff --git a/cmd/lotus-provider/pipeline.go b/cmd/lotus-provider/pipeline.go new file mode 100644 index 000000000..2ac46af2c --- /dev/null +++ b/cmd/lotus-provider/pipeline.go @@ -0,0 +1,144 @@ +package main + +import ( + "fmt" + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-commp-utils/zerocomm" + "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/lotus/chain/actors/builtin/miner" + "github.com/filecoin-project/lotus/chain/types" + lcli "github.com/filecoin-project/lotus/cli" + "github.com/filecoin-project/lotus/cmd/lotus-provider/deps" + "github.com/filecoin-project/lotus/lib/harmony/harmonydb" + "github.com/filecoin-project/lotus/provider/lpseal" + "github.com/ipfs/go-cid" + "github.com/urfave/cli/v2" + "golang.org/x/xerrors" +) + +var pipelineCmd = &cli.Command{ + Name: "pipeline", + Usage: "Manage the sealing pipeline", + Subcommands: []*cli.Command{ + pipelineStartCmd, + }, +} + +var pipelineStartCmd = &cli.Command{ + Name: "start", + Usage: "Start new sealing operations manually", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "actor", + Usage: "Specify actor address to start sealing sectors for", + Required: true, + }, + &cli.BoolFlag{ + Name: "now", + Usage: "Start sealing sectors for all actors now (not on schedule)", + }, + &cli.BoolFlag{ + Name: "cc", + Usage: "Start sealing new CC sectors", + }, + &cli.IntFlag{ + Name: "count", + Usage: "Number of sectors to start", + Value: 1, + }, + &cli.BoolFlag{ + Name: "synthetic", + Usage: "Use synthetic PoRep", + Value: true, + }, + &cli.StringSliceFlag{ // todo consider moving layers top level + Name: "layers", + Usage: "list of layers to be interpreted (atop defaults). Default: base", + Value: cli.NewStringSlice("base"), + }, + }, + Action: func(cctx *cli.Context) error { + if !cctx.Bool("now") { + return xerrors.Errorf("schedule not implemented, use --now") + } + if !cctx.IsSet("actor") { + return cli.ShowCommandHelp(cctx, "start") + } + + act, err := address.NewFromString(cctx.String("actor")) + if err != nil { + return xerrors.Errorf("parsing --actor: %w", err) + } + + ctx := lcli.ReqContext(cctx) + dep, err := deps.GetDepsCLI(ctx, cctx) + if err != nil { + return err + } + + /* + create table sectors_sdr_pipeline ( + sp_id bigint not null, + sector_number bigint not null, + + -- at request time + create_time timestamp not null, + reg_seal_proof int not null, + comm_d_cid text not null, + + [... other not relevant fields] + */ + + mid, err := address.IDFromAddress(act) + if err != nil { + return xerrors.Errorf("getting miner id: %w", err) + } + + mi, err := dep.Full.StateMinerInfo(ctx, act, types.EmptyTSK) + if err != nil { + return xerrors.Errorf("getting miner info: %w", err) + } + + nv, err := dep.Full.StateNetworkVersion(ctx, types.EmptyTSK) + if err != nil { + return xerrors.Errorf("getting network version: %w", err) + } + + wpt := mi.WindowPoStProofType + spt, err := miner.PreferredSealProofTypeFromWindowPoStType(nv, wpt, cctx.Bool("synthetic")) + if err != nil { + return xerrors.Errorf("getting seal proof type: %w", err) + } + + ssize, err := spt.SectorSize() + if err != nil { + return xerrors.Errorf("getting sector size: %w", err) + } + + var commd cid.Cid + if cctx.Bool("cc") { + commd = zerocomm.ZeroPieceCommitment(abi.PaddedPieceSize(ssize).Unpadded()) + } else { + return xerrors.Errorf("only CC sectors supported for now") + } + + num, err := lpseal.AllocateSectorNumbers(ctx, dep.Full, dep.DB, act, cctx.Int("count"), func(tx *harmonydb.Tx, numbers []abi.SectorNumber) (bool, error) { + for _, n := range numbers { + _, err := tx.Exec("insert into sectors_sdr_pipeline (sp_id, sector_number, reg_seal_proof, comm_d_cid) values ($1, $2, $3, $4)", mid, n, spt, commd.String()) + if err != nil { + return false, xerrors.Errorf("inserting into sectors_sdr_pipeline: %w", err) + } + } + return true, nil + }) + if err != nil { + return xerrors.Errorf("allocating sector numbers: %w", err) + } + + for _, number := range num { + fmt.Println(number) + } + + return nil + }, +} diff --git a/lib/harmony/harmonydb/sql/20231217.sql b/lib/harmony/harmonydb/sql/20231217-sdr-pipeline.sql similarity index 87% rename from lib/harmony/harmonydb/sql/20231217.sql rename to lib/harmony/harmonydb/sql/20231217-sdr-pipeline.sql index d744fbaf5..864c51ec8 100644 --- a/lib/harmony/harmonydb/sql/20231217.sql +++ b/lib/harmony/harmonydb/sql/20231217-sdr-pipeline.sql @@ -6,8 +6,9 @@ create table sectors_sdr_pipeline ( sector_number bigint not null, -- at request time - create_time timestamp not null, + create_time timestamp not null default current_timestamp, reg_seal_proof int not null, + comm_d_cid text not null, -- sdr ticket_epoch bigint, @@ -17,7 +18,7 @@ create table sectors_sdr_pipeline ( after_sdr bool not null default false, -- tree D - tree_d_cid text, -- commd from treeD compute + tree_d_cid text, -- commd from treeD compute, should match comm_d_cid task_id_tree_d bigint, after_tree_d bool not null default false, @@ -84,3 +85,8 @@ create table sectors_sdr_initial_pieces ( primary key (sp_id, sector_number, piece_index) ); + +create table sectors_allocated_numbers ( + sp_id bigint not null primary key, + allocated jsonb not null +); diff --git a/provider/lpseal/sector_num_alloc.go b/provider/lpseal/sector_num_alloc.go new file mode 100644 index 000000000..23a97cd96 --- /dev/null +++ b/provider/lpseal/sector_num_alloc.go @@ -0,0 +1,119 @@ +package lpseal + +import ( + "context" + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-bitfield" + rlepluslazy "github.com/filecoin-project/go-bitfield/rle" + "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/lib/harmony/harmonydb" + "golang.org/x/xerrors" +) + +type AllocAPI interface { + StateMinerAllocated(context.Context, address.Address, types.TipSetKey) (*bitfield.BitField, error) +} + +func AllocateSectorNumbers(ctx context.Context, a AllocAPI, db *harmonydb.DB, maddr address.Address, count int, txcb ...func(*harmonydb.Tx, []abi.SectorNumber) (bool, error)) ([]abi.SectorNumber, error) { + chainAlloc, err := a.StateMinerAllocated(ctx, maddr, types.EmptyTSK) + if err != nil { + return nil, xerrors.Errorf("getting on-chain allocated sector numbers: %w", err) + } + + mid, err := address.IDFromAddress(maddr) + if err != nil { + return nil, xerrors.Errorf("getting miner id: %w", err) + } + + var res []abi.SectorNumber + + comm, err := db.BeginTransaction(ctx, func(tx *harmonydb.Tx) (commit bool, err error) { + // query from db, if exists unmarsal to bitfield + var dbAllocated bitfield.BitField + var rawJson []byte + + err = tx.QueryRow("select COALESCE(allocated, '[0]') from sectors_allocated_numbers sa FULL OUTER JOIN (SELECT 1) AS d ON true where sp_id = $1 or sp_id is null", mid).Scan(&rawJson) + if err != nil { + return false, xerrors.Errorf("querying allocated sector numbers: %w", err) + } + + if rawJson != nil { + err = dbAllocated.UnmarshalJSON(rawJson) + if err != nil { + return false, xerrors.Errorf("unmarshaling allocated sector numbers: %w", err) + } + } + + if err := dbAllocated.UnmarshalJSON(rawJson); err != nil { + return false, xerrors.Errorf("unmarshaling allocated sector numbers: %w", err) + } + + merged, err := bitfield.MergeBitFields(*chainAlloc, dbAllocated) + if err != nil { + return false, xerrors.Errorf("merging allocated sector numbers: %w", err) + } + + allAssignable, err := bitfield.NewFromIter(&rlepluslazy.RunSliceIterator{Runs: []rlepluslazy.Run{ + { + Val: true, + Len: abi.MaxSectorNumber, + }, + }}) + + inverted, err := bitfield.SubtractBitField(allAssignable, merged) + if err != nil { + return false, xerrors.Errorf("subtracting allocated sector numbers: %w", err) + } + + toAlloc, err := inverted.Slice(0, uint64(count)) + if err != nil { + return false, xerrors.Errorf("getting slice of allocated sector numbers: %w", err) + } + + err = toAlloc.ForEach(func(u uint64) error { + res = append(res, abi.SectorNumber(u)) + return nil + }) + if err != nil { + return false, xerrors.Errorf("iterating allocated sector numbers: %w", err) + } + + toPersist, err := bitfield.MergeBitFields(merged, toAlloc) + if err != nil { + return false, xerrors.Errorf("merging allocated sector numbers: %w", err) + } + + rawJson, err = toPersist.MarshalJSON() + if err != nil { + return false, xerrors.Errorf("marshaling allocated sector numbers: %w", err) + } + + _, err = tx.Exec("insert into sectors_allocated_numbers(sp_id, allocated) values($1, $2) on conflict(sp_id) do update set allocated = $2", mid, rawJson) + if err != nil { + return false, xerrors.Errorf("persisting allocated sector numbers: %w", err) + } + + for i, f := range txcb { + commit, err = f(tx, res) + if err != nil { + return false, xerrors.Errorf("executing tx callback %d: %w", i, err) + } + + if !commit { + return false, nil + } + } + + return true, nil + }) + + if err != nil { + return nil, xerrors.Errorf("allocating sector numbers: %w", err) + } + if !comm { + return nil, xerrors.Errorf("allocating sector numbers: commit failed") + } + + return res, nil +} From bb42a70143e5dee5968869872bae640ba4be19d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 18 Dec 2023 18:29:10 +0100 Subject: [PATCH 090/394] scayyold the SDR task --- provider/lpseal/task_sdr.go | 44 +++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 provider/lpseal/task_sdr.go diff --git a/provider/lpseal/task_sdr.go b/provider/lpseal/task_sdr.go new file mode 100644 index 000000000..2f3cd8dd3 --- /dev/null +++ b/provider/lpseal/task_sdr.go @@ -0,0 +1,44 @@ +package lpseal + +import ( + "github.com/filecoin-project/lotus/lib/harmony/harmonydb" + "github.com/filecoin-project/lotus/lib/harmony/harmonytask" + "github.com/filecoin-project/lotus/lib/harmony/resources" +) + +type SDRAPI interface { +} + +type SDRTask struct { + api SDRAPI + db *harmonydb.DB + + maxSDR int +} + +func (s *SDRTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (done bool, err error) { + //TODO implement me + panic("implement me") +} + +func (s *SDRTask) CanAccept(ids []harmonytask.TaskID, engine *harmonytask.TaskEngine) (*harmonytask.TaskID, error) { + //TODO implement me + panic("implement me") +} + +func (s *SDRTask) TypeDetails() harmonytask.TaskTypeDetails { + return harmonytask.TaskTypeDetails{ + Max: s.maxSDR, + Name: "SDR", + Cost: resources.Resources{}, + MaxFailures: 0, + Follows: nil, + } +} + +func (s *SDRTask) Adder(taskFunc harmonytask.AddTaskFunc) { + //TODO implement me + panic("implement me") +} + +var _ harmonytask.TaskInterface = &SDRTask{} From 44b441d7adb70b48a64ff65ea8680923195b30a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 18 Dec 2023 23:48:49 +0100 Subject: [PATCH 091/394] lpseal: Implement SDR for CC sectors --- cmd/lotus-provider/pipeline.go | 19 +-- .../harmonydb/sql/20231217-sdr-pipeline.sql | 4 +- provider/lpffi/sdr_funcs.go | 56 ++++++++ provider/lpseal/poller.go | 129 +++++++++++++++++ provider/lpseal/task_sdr.go | 134 ++++++++++++++++-- storage/sealer/ffiwrapper/sealer_cgo.go | 2 +- 6 files changed, 318 insertions(+), 26 deletions(-) create mode 100644 provider/lpffi/sdr_funcs.go create mode 100644 provider/lpseal/poller.go diff --git a/cmd/lotus-provider/pipeline.go b/cmd/lotus-provider/pipeline.go index 2ac46af2c..8e01044e2 100644 --- a/cmd/lotus-provider/pipeline.go +++ b/cmd/lotus-provider/pipeline.go @@ -3,7 +3,6 @@ package main import ( "fmt" "github.com/filecoin-project/go-address" - "github.com/filecoin-project/go-commp-utils/zerocomm" "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/lotus/chain/actors/builtin/miner" "github.com/filecoin-project/lotus/chain/types" @@ -11,7 +10,6 @@ import ( "github.com/filecoin-project/lotus/cmd/lotus-provider/deps" "github.com/filecoin-project/lotus/lib/harmony/harmonydb" "github.com/filecoin-project/lotus/provider/lpseal" - "github.com/ipfs/go-cid" "github.com/urfave/cli/v2" "golang.org/x/xerrors" ) @@ -64,6 +62,9 @@ var pipelineStartCmd = &cli.Command{ if !cctx.IsSet("actor") { return cli.ShowCommandHelp(cctx, "start") } + if !cctx.Bool("cc") { + return xerrors.Errorf("only CC sectors supported for now") + } act, err := address.NewFromString(cctx.String("actor")) if err != nil { @@ -110,21 +111,9 @@ var pipelineStartCmd = &cli.Command{ return xerrors.Errorf("getting seal proof type: %w", err) } - ssize, err := spt.SectorSize() - if err != nil { - return xerrors.Errorf("getting sector size: %w", err) - } - - var commd cid.Cid - if cctx.Bool("cc") { - commd = zerocomm.ZeroPieceCommitment(abi.PaddedPieceSize(ssize).Unpadded()) - } else { - return xerrors.Errorf("only CC sectors supported for now") - } - num, err := lpseal.AllocateSectorNumbers(ctx, dep.Full, dep.DB, act, cctx.Int("count"), func(tx *harmonydb.Tx, numbers []abi.SectorNumber) (bool, error) { for _, n := range numbers { - _, err := tx.Exec("insert into sectors_sdr_pipeline (sp_id, sector_number, reg_seal_proof, comm_d_cid) values ($1, $2, $3, $4)", mid, n, spt, commd.String()) + _, err := tx.Exec("insert into sectors_sdr_pipeline (sp_id, sector_number, reg_seal_proof) values ($1, $2, $3)", mid, n, spt) if err != nil { return false, xerrors.Errorf("inserting into sectors_sdr_pipeline: %w", err) } diff --git a/lib/harmony/harmonydb/sql/20231217-sdr-pipeline.sql b/lib/harmony/harmonydb/sql/20231217-sdr-pipeline.sql index 864c51ec8..e41e865d0 100644 --- a/lib/harmony/harmonydb/sql/20231217-sdr-pipeline.sql +++ b/lib/harmony/harmonydb/sql/20231217-sdr-pipeline.sql @@ -8,7 +8,6 @@ create table sectors_sdr_pipeline ( -- at request time create_time timestamp not null default current_timestamp, reg_seal_proof int not null, - comm_d_cid text not null, -- sdr ticket_epoch bigint, @@ -83,6 +82,9 @@ create table sectors_sdr_initial_pieces ( piece_cid text not null, piece_size bigint not null, + -- foreign key + foreign key (sp_id, sector_number) references sectors_sdr_pipeline (sp_id, sector_number) on delete cascade, + primary key (sp_id, sector_number, piece_index) ); diff --git a/provider/lpffi/sdr_funcs.go b/provider/lpffi/sdr_funcs.go new file mode 100644 index 000000000..c7ff7064a --- /dev/null +++ b/provider/lpffi/sdr_funcs.go @@ -0,0 +1,56 @@ +package lpffi + +import ( + "context" + ffi "github.com/filecoin-project/filecoin-ffi" + commcid "github.com/filecoin-project/go-fil-commcid" + "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/lotus/storage/sealer/ffiwrapper" + "github.com/filecoin-project/lotus/storage/sealer/storiface" + "github.com/ipfs/go-cid" + "golang.org/x/xerrors" +) + +type ExternPrecommit2 func(ctx context.Context, sector storiface.SectorRef, cache, sealed string, pc1out storiface.PreCommit1Out) (sealedCID cid.Cid, unsealedCID cid.Cid, err error) + +type ExternalSealer struct { + PreCommit2 ExternPrecommit2 +} + +type SealCalls struct { + sectors ffiwrapper.SectorProvider + + // externCalls cointain overrides for calling alternative sealing logic + externCalls ExternalSealer +} + +func (sb *SealCalls) GenerateSDR(ctx context.Context, sector storiface.SectorRef, ticket abi.SealRandomness, commKcid cid.Cid) error { + paths, releaseSector, err := sb.sectors.AcquireSector(ctx, sector, storiface.FTCache, storiface.FTNone, storiface.PathSealing) + if err != nil { + return xerrors.Errorf("acquiring sector paths: %w", err) + } + defer releaseSector() + + // prepare SDR params + commp, err := commcid.CIDToDataCommitmentV1(commKcid) + if err != nil { + return xerrors.Errorf("computing commK: %w", err) + } + + replicaID, err := sector.ProofType.ReplicaId(sector.ID.Miner, sector.ID.Number, ticket, commp) + if err != nil { + return xerrors.Errorf("computing replica id: %w", err) + } + + // generate new sector key + err = ffi.GenerateSDR( + sector.ProofType, + paths.Cache, + replicaID, + ) + if err != nil { + return xerrors.Errorf("generating SDR %d (%s): %w", sector.ID.Number, paths.Unsealed, err) + } + + return nil +} diff --git a/provider/lpseal/poller.go b/provider/lpseal/poller.go new file mode 100644 index 000000000..d299832ec --- /dev/null +++ b/provider/lpseal/poller.go @@ -0,0 +1,129 @@ +package lpseal + +import ( + "context" + "github.com/filecoin-project/lotus/lib/harmony/harmonydb" + "github.com/filecoin-project/lotus/lib/harmony/harmonytask" + "github.com/filecoin-project/lotus/lib/promise" + logging "github.com/ipfs/go-log/v2" + "golang.org/x/xerrors" + "time" +) + +var log = logging.Logger("lpseal") + +const ( + pollerSDR = iota + + numPollers +) + +const sealPollerInterval = 10 * time.Second + +type SealPoller struct { + db *harmonydb.DB + + pollers [numPollers]promise.Promise[harmonytask.AddTaskFunc] +} + +func (s *SealPoller) RunPoller(ctx context.Context) error { + ticker := time.NewTicker(sealPollerInterval) + defer ticker.Stop() + + for { + select { + case <-ctx.Done(): + return nil + case <-ticker.C: + s.poll(ctx) + } + } +} + +func (s *SealPoller) poll(ctx context.Context) error { + var tasks []struct { + SpID int64 `db:"sp_id"` + SectorNumber int64 `db:"sector_number"` + + TaskSDR *int64 `db:"task_id_sdr"` + AfterSDR bool `db:"after_sdr"` + + TaskTreeD *int64 `db:"task_id_tree_d"` + AfterTreeD bool `db:"after_tree_d"` + + TaskTreeC *int64 `db:"task_id_tree_c"` + AfterTreeC bool `db:"after_tree_c"` + + TaskTreeR *int64 `db:"task_id_tree_r"` + AfterTreeR bool `db:"after_tree_r"` + + TaskPrecommitMsg *int64 `db:"task_id_precommit_msg"` + AfterPrecommitMsg bool `db:"after_precommit_msg"` + + TaskPrecommitMsgWait *int64 `db:"task_id_precommit_msg_wait"` + AfterPrecommitMsgSuccess bool `db:"after_precommit_msg_success"` + + TaskPoRep *int64 `db:"task_id_porep"` + PoRepProof []byte `db:"porep_proof"` + + TaskCommitMsg *int64 `db:"task_id_commit_msg"` + AfterCommitMsg bool `db:"after_commit_msg"` + + TaskCommitMsgWait *int64 `db:"task_id_commit_msg_wait"` + AfterCommitMsgSuccess bool `db:"after_commit_msg_success"` + + Failed bool `db:"failed"` + FailedReason string `db:"failed_reason"` + } + + err := s.db.Select(ctx, &tasks, `SELECT * FROM sectors_sdr_pipeline WHERE after_commit_msg_success != true`) + if err != nil { + return err + } + + for _, task := range tasks { + if task.Failed { + continue + } + + if task.TaskSDR == nil { + s.pollers[pollerSDR].Val(ctx)(func(id harmonytask.TaskID, tx *harmonydb.Tx) (shouldCommit bool, seriousError error) { + n, err := tx.Exec(`UPDATE sectors_sdr_pipeline SET task_id_sdr = $1 WHERE sp_id = $2 AND sector_number = $3`, id, task.SpID, task.SectorNumber) + if err != nil { + return false, xerrors.Errorf("update sectors_sdr_pipeline: %w", err) + } + if n != 1 { + return false, xerrors.Errorf("expected to update 1 row, updated %d", n) + } + + return true, nil + }) + } + if task.TaskTreeD == nil { + // todo start tree d task + } + + // todo those two are really one pc2 + if task.TaskTreeC == nil && task.AfterSDR { + // todo start tree c task + } + if task.TaskTreeR == nil && task.AfterTreeC { + // todo start tree r task + } + + if task.TaskPrecommitMsg == nil && task.AfterTreeR && task.AfterTreeD { + // todo start precommit msg task + } + + if task.TaskPrecommitMsgWait == nil && task.AfterPrecommitMsg { + // todo start precommit msg wait task + } + + todoWaitSeed := false + if task.TaskPoRep == nil && todoWaitSeed { + // todo start porep task + } + } + + return nil +} diff --git a/provider/lpseal/task_sdr.go b/provider/lpseal/task_sdr.go index 2f3cd8dd3..fe7762140 100644 --- a/provider/lpseal/task_sdr.go +++ b/provider/lpseal/task_sdr.go @@ -1,44 +1,160 @@ package lpseal import ( + "bytes" + "context" + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-commp-utils/zerocomm" + "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/go-state-types/crypto" + "github.com/filecoin-project/lotus/chain/actors/policy" + "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/lib/harmony/harmonydb" "github.com/filecoin-project/lotus/lib/harmony/harmonytask" "github.com/filecoin-project/lotus/lib/harmony/resources" + "github.com/filecoin-project/lotus/provider/lpffi" + "github.com/filecoin-project/lotus/storage/sealer/storiface" + "golang.org/x/xerrors" ) type SDRAPI interface { + ChainHead(context.Context) (*types.TipSet, error) + StateGetRandomnessFromTickets(context.Context, crypto.DomainSeparationTag, abi.ChainEpoch, []byte, types.TipSetKey) (abi.Randomness, error) } type SDRTask struct { api SDRAPI db *harmonydb.DB + sp *SealPoller + + sc *lpffi.SealCalls maxSDR int } func (s *SDRTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (done bool, err error) { - //TODO implement me - panic("implement me") + ctx := context.Background() + + var sectorParams struct { + SpID int64 `db:"sp_id"` + SectorNumber int64 `db:"sector_number"` + RegSealProof abi.RegisteredSealProof `db:"reg_seal_proof"` + } + + err = s.db.Select(ctx, §orParams, ` + SELECT sp_id, sector_number, reg_seal_proof + FROM sectors_sdr_pipeline`) + if err != nil { + return false, xerrors.Errorf("getting sector params: %w", err) + } + + var pieces []struct { + PieceIndex int64 `db:"piece_index"` + PieceCID string `db:"piece_cid"` + PieceSize int64 `db:"piece_size"` + } + + err = s.db.Select(ctx, &pieces, ` + SELECT piece_index, piece_cid, piece_size + FROM sectors_sdr_initial_pieces + WHERE sp_id = $1 AND sector_number = $2`, sectorParams.SpID, sectorParams.SectorNumber) + if err != nil { + return false, xerrors.Errorf("getting pieces: %w", err) + } + + if len(pieces) > 0 { + // todo sdr with data + return false, xerrors.Errorf("todo sdr with data") + } + + ssize, err := sectorParams.RegSealProof.SectorSize() + if err != nil { + return false, xerrors.Errorf("getting sector size: %w", err) + } + + commd := zerocomm.ZeroPieceCommitment(abi.PaddedPieceSize(ssize).Unpadded()) + + sref := storiface.SectorRef{ + ID: abi.SectorID{ + Miner: abi.ActorID(sectorParams.SpID), + Number: abi.SectorNumber(sectorParams.SectorNumber), + }, + ProofType: sectorParams.RegSealProof, + } + + // get ticket + maddr, err := address.NewIDAddress(uint64(sectorParams.SpID)) + if err != nil { + return false, xerrors.Errorf("getting miner address: %w", err) + } + + ticket, err := s.getTicket(ctx, maddr) + if err != nil { + return false, xerrors.Errorf("getting ticket: %w", err) + } + + // do the SDR!! + + err = s.sc.GenerateSDR(ctx, sref, ticket, commd) + if err != nil { + return false, xerrors.Errorf("generating sdr: %w", err) + } + + // store success! + n, err := s.db.Exec(ctx, `UPDATE sectors_sdr_pipeline SET after_sdr = true WHERE sp_id = $1 AND sector_number = $2`, sectorParams.SpID, sectorParams.SectorNumber) + if err != nil { + return false, xerrors.Errorf("store sdr success: updating pipeline: %w", err) + } + if n != 1 { + return false, xerrors.Errorf("store sdr success: updated %d rows", n) + } + + return true, nil +} + +func (s *SDRTask) getTicket(ctx context.Context, maddr address.Address) (abi.SealRandomness, error) { + ts, err := s.api.ChainHead(ctx) + if err != nil { + return nil, xerrors.Errorf("getting chain head: %w", err) + } + + ticketEpoch := ts.Height() - policy.SealRandomnessLookback + buf := new(bytes.Buffer) + if err := maddr.MarshalCBOR(buf); err != nil { + return nil, xerrors.Errorf("marshaling miner address: %w", err) + } + + rand, err := s.api.StateGetRandomnessFromTickets(ctx, crypto.DomainSeparationTag_SealRandomness, ticketEpoch, buf.Bytes(), ts.Key()) + if err != nil { + return nil, xerrors.Errorf("getting randomness from tickets: %w", err) + } + + return abi.SealRandomness(rand), nil } func (s *SDRTask) CanAccept(ids []harmonytask.TaskID, engine *harmonytask.TaskEngine) (*harmonytask.TaskID, error) { - //TODO implement me - panic("implement me") + // todo check storage (reserve too?) + + id := ids[0] + return &id, nil } func (s *SDRTask) TypeDetails() harmonytask.TaskTypeDetails { return harmonytask.TaskTypeDetails{ - Max: s.maxSDR, - Name: "SDR", - Cost: resources.Resources{}, + Max: s.maxSDR, + Name: "SDR", + Cost: resources.Resources{ // todo offset for prefetch? + Cpu: 4, // todo multicore sdr + Gpu: 0, + Ram: 54 << 30, // todo measure; lower on 2k devnetn + }, MaxFailures: 0, Follows: nil, } } func (s *SDRTask) Adder(taskFunc harmonytask.AddTaskFunc) { - //TODO implement me - panic("implement me") + s.sp.pollers[pollerSDR].Set(taskFunc) } var _ harmonytask.TaskInterface = &SDRTask{} diff --git a/storage/sealer/ffiwrapper/sealer_cgo.go b/storage/sealer/ffiwrapper/sealer_cgo.go index 04e891665..f16130331 100644 --- a/storage/sealer/ffiwrapper/sealer_cgo.go +++ b/storage/sealer/ffiwrapper/sealer_cgo.go @@ -520,7 +520,7 @@ func (sb *Sealer) regenerateSectorKey(ctx context.Context, sector storiface.Sect // prepare SDR params commp, err := commcid.CIDToDataCommitmentV1(keyDataCid) if err != nil { - return xerrors.Errorf("computing commP: %w", err) + return xerrors.Errorf("computing commK: %w", err) } replicaID, err := sector.ProofType.ReplicaId(sector.ID.Miner, sector.ID.Number, ticket, commp) From 4eb7bc91d9ebf05163f280d29cb22f3565b0e3af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 19 Dec 2023 11:20:18 +0100 Subject: [PATCH 092/394] lpseal: Fix sdr do query --- provider/lpseal/task_sdr.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/provider/lpseal/task_sdr.go b/provider/lpseal/task_sdr.go index fe7762140..1b5268cab 100644 --- a/provider/lpseal/task_sdr.go +++ b/provider/lpseal/task_sdr.go @@ -43,7 +43,8 @@ func (s *SDRTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (done bo err = s.db.Select(ctx, §orParams, ` SELECT sp_id, sector_number, reg_seal_proof - FROM sectors_sdr_pipeline`) + FROM sectors_sdr_pipeline + WHERE task_id_sdr = $1`, taskID) if err != nil { return false, xerrors.Errorf("getting sector params: %w", err) } From 5d26c3a9dcc77c6a263bbdd7b9193b98aab9bafe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 19 Dec 2023 12:16:38 +0100 Subject: [PATCH 093/394] lpseal: Wire up sdr task --- cmd/lotus-provider/tasks/tasks.go | 23 +++++++++++ lib/promise/promise.go | 6 +++ node/config/types.go | 5 +++ provider/lpffi/sdr_funcs.go | 63 +++++++++++++++++++++++++++---- provider/lpseal/poller.go | 16 ++++++-- provider/lpseal/task_sdr.go | 10 +++++ storage/sealer/worker_local.go | 12 +++--- 7 files changed, 117 insertions(+), 18 deletions(-) diff --git a/cmd/lotus-provider/tasks/tasks.go b/cmd/lotus-provider/tasks/tasks.go index 2c4cd58bf..418031157 100644 --- a/cmd/lotus-provider/tasks/tasks.go +++ b/cmd/lotus-provider/tasks/tasks.go @@ -3,6 +3,8 @@ package tasks import ( "context" + "github.com/filecoin-project/lotus/provider/lpffi" + "github.com/filecoin-project/lotus/provider/lpseal" logging "github.com/ipfs/go-log/v2" "github.com/samber/lo" @@ -25,6 +27,7 @@ func StartTasks(ctx context.Context, dependencies *deps.Deps) (*harmonytask.Task as := dependencies.As maddrs := dependencies.Maddrs stor := dependencies.Stor + lstor := dependencies.LocalStore si := dependencies.Si var activeTasks []harmonytask.TaskInterface @@ -35,6 +38,7 @@ func StartTasks(ctx context.Context, dependencies *deps.Deps) (*harmonytask.Task ///// Task Selection /////////////////////////////////////////////////////////////////////// { + // PoSt if cfg.Subsystems.EnableWindowPost { wdPostTask, wdPoStSubmitTask, derlareRecoverTask, err := provider.WindowPostScheduler(ctx, cfg.Fees, cfg.Proving, full, verif, lw, sender, @@ -50,6 +54,25 @@ func StartTasks(ctx context.Context, dependencies *deps.Deps) (*harmonytask.Task activeTasks = append(activeTasks, winPoStTask) } } + + { + // Sealing + hasAnySealingTask := cfg.Subsystems.EnableSealSDR + + var sp *lpseal.SealPoller + var slr *lpffi.SealCalls + if hasAnySealingTask { + sp = lpseal.NewPoller(db) + go sp.RunPoller(ctx) + + slr = lpffi.NewSealCalls(stor, lstor, si) + } + + if cfg.Subsystems.EnableSealSDR { + sdrTask := lpseal.NewSDRTask(full, db, sp, slr, cfg.Subsystems.SealSDRMaxTasks) + activeTasks = append(activeTasks, sdrTask) + } + } log.Infow("This lotus_provider instance handles", "miner_addresses", maddrs, "tasks", lo.Map(activeTasks, func(t harmonytask.TaskInterface, _ int) string { return t.TypeDetails().Name })) diff --git a/lib/promise/promise.go b/lib/promise/promise.go index 9b6a5e2b0..02e917ca1 100644 --- a/lib/promise/promise.go +++ b/lib/promise/promise.go @@ -45,3 +45,9 @@ func (p *Promise[T]) Val(ctx context.Context) T { return val } } + +func (p *Promise[T]) IsSet() bool { + p.mu.Lock() + defer p.mu.Unlock() + return p.done != nil +} diff --git a/node/config/types.go b/node/config/types.go index 208d85939..65fa4da02 100644 --- a/node/config/types.go +++ b/node/config/types.go @@ -101,6 +101,11 @@ type ProviderSubsystemsConfig struct { EnableWebGui bool // The address that should listen for Web GUI requests. GuiAddress string + + // EnableSealSDR enables SDR tasks to run. SDR is the long sequential computation + // creating layers. In lotus-miner this was run as part of PreCommit1. + EnableSealSDR bool + SealSDRMaxTasks int } type DAGStoreConfig struct { diff --git a/provider/lpffi/sdr_funcs.go b/provider/lpffi/sdr_funcs.go index c7ff7064a..fc1748213 100644 --- a/provider/lpffi/sdr_funcs.go +++ b/provider/lpffi/sdr_funcs.go @@ -5,23 +5,72 @@ import ( ffi "github.com/filecoin-project/filecoin-ffi" commcid "github.com/filecoin-project/go-fil-commcid" "github.com/filecoin-project/go-state-types/abi" - "github.com/filecoin-project/lotus/storage/sealer/ffiwrapper" + "github.com/filecoin-project/lotus/storage/paths" "github.com/filecoin-project/lotus/storage/sealer/storiface" "github.com/ipfs/go-cid" + logging "github.com/ipfs/go-log/v2" "golang.org/x/xerrors" ) +var log = logging.Logger("lpffi") + +/* type ExternPrecommit2 func(ctx context.Context, sector storiface.SectorRef, cache, sealed string, pc1out storiface.PreCommit1Out) (sealedCID cid.Cid, unsealedCID cid.Cid, err error) -type ExternalSealer struct { - PreCommit2 ExternPrecommit2 + type ExternalSealer struct { + PreCommit2 ExternPrecommit2 + } +*/ +type SealCalls struct { + sectors *storageProvider + + /*// externCalls cointain overrides for calling alternative sealing logic + externCalls ExternalSealer*/ } -type SealCalls struct { - sectors ffiwrapper.SectorProvider +func NewSealCalls(st paths.Store, ls *paths.Local, si paths.SectorIndex) *SealCalls { + return &SealCalls{ + sectors: &storageProvider{ + storage: st, + localStore: ls, + sindex: si, + }, + } +} - // externCalls cointain overrides for calling alternative sealing logic - externCalls ExternalSealer +type storageProvider struct { + storage paths.Store + localStore *paths.Local + sindex paths.SectorIndex +} + +func (l *storageProvider) AcquireSector(ctx context.Context, sector storiface.SectorRef, existing storiface.SectorFileType, allocate storiface.SectorFileType, sealing storiface.PathType) (storiface.SectorPaths, func(), error) { + paths, storageIDs, err := l.storage.AcquireSector(ctx, sector, existing, allocate, sealing, storiface.AcquireMove) + if err != nil { + return storiface.SectorPaths{}, nil, err + } + + releaseStorage, err := l.localStore.Reserve(ctx, sector, allocate, storageIDs, storiface.FSOverheadSeal) + if err != nil { + return storiface.SectorPaths{}, nil, xerrors.Errorf("reserving storage space: %w", err) + } + + log.Debugf("acquired sector %d (e:%d; a:%d): %v", sector, existing, allocate, paths) + + return paths, func() { + releaseStorage() + + for _, fileType := range storiface.PathTypes { + if fileType&allocate == 0 { + continue + } + + sid := storiface.PathByType(storageIDs, fileType) + if err := l.sindex.StorageDeclareSector(ctx, storiface.ID(sid), sector.ID, fileType, true); err != nil { + log.Errorf("declare sector error: %+v", err) + } + } + }, nil } func (sb *SealCalls) GenerateSDR(ctx context.Context, sector storiface.SectorRef, ticket abi.SealRandomness, commKcid cid.Cid) error { diff --git a/provider/lpseal/poller.go b/provider/lpseal/poller.go index d299832ec..4747eb332 100644 --- a/provider/lpseal/poller.go +++ b/provider/lpseal/poller.go @@ -26,16 +26,24 @@ type SealPoller struct { pollers [numPollers]promise.Promise[harmonytask.AddTaskFunc] } -func (s *SealPoller) RunPoller(ctx context.Context) error { +func NewPoller(db *harmonydb.DB) *SealPoller { + return &SealPoller{ + db: db, + } +} + +func (s *SealPoller) RunPoller(ctx context.Context) { ticker := time.NewTicker(sealPollerInterval) defer ticker.Stop() for { select { case <-ctx.Done(): - return nil + return case <-ticker.C: - s.poll(ctx) + if err := s.poll(ctx); err != nil { + log.Errorf("polling sdr sector pipeline: %w", err) + } } } } @@ -86,7 +94,7 @@ func (s *SealPoller) poll(ctx context.Context) error { continue } - if task.TaskSDR == nil { + if task.TaskSDR == nil && s.pollers[pollerSDR].IsSet() { s.pollers[pollerSDR].Val(ctx)(func(id harmonytask.TaskID, tx *harmonydb.Tx) (shouldCommit bool, seriousError error) { n, err := tx.Exec(`UPDATE sectors_sdr_pipeline SET task_id_sdr = $1 WHERE sp_id = $2 AND sector_number = $3`, id, task.SpID, task.SectorNumber) if err != nil { diff --git a/provider/lpseal/task_sdr.go b/provider/lpseal/task_sdr.go index 1b5268cab..928344069 100644 --- a/provider/lpseal/task_sdr.go +++ b/provider/lpseal/task_sdr.go @@ -32,6 +32,16 @@ type SDRTask struct { maxSDR int } +func NewSDRTask(api SDRAPI, db *harmonydb.DB, sp *SealPoller, sc *lpffi.SealCalls, maxSDR int) *SDRTask { + return &SDRTask{ + api: api, + db: db, + sp: sp, + sc: sc, + maxSDR: maxSDR, + } +} + func (s *SDRTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (done bool, err error) { ctx := context.Background() diff --git a/storage/sealer/worker_local.go b/storage/sealer/worker_local.go index 7fc494955..417a15e62 100644 --- a/storage/sealer/worker_local.go +++ b/storage/sealer/worker_local.go @@ -28,8 +28,6 @@ import ( "github.com/filecoin-project/lotus/storage/sealer/storiface" ) -var pathTypes = []storiface.SectorFileType{storiface.FTUnsealed, storiface.FTSealed, storiface.FTCache, storiface.FTUpdate, storiface.FTUpdateCache} - type WorkerConfig struct { TaskTypes []sealtasks.TaskType NoSwap bool @@ -167,7 +165,7 @@ func (l *localWorkerPathProvider) AcquireSector(ctx context.Context, sector stor return paths, func() { releaseStorage() - for _, fileType := range pathTypes { + for _, fileType := range storiface.PathTypes { if fileType&allocate == 0 { continue } @@ -180,16 +178,16 @@ func (l *localWorkerPathProvider) AcquireSector(ctx context.Context, sector stor }, nil } +func (l *localWorkerPathProvider) AcquireSectorCopy(ctx context.Context, id storiface.SectorRef, existing storiface.SectorFileType, allocate storiface.SectorFileType, ptype storiface.PathType) (storiface.SectorPaths, func(), error) { + return (&localWorkerPathProvider{w: l.w, op: storiface.AcquireCopy}).AcquireSector(ctx, id, existing, allocate, ptype) +} + func FFIExec(opts ...ffiwrapper.FFIWrapperOpt) func(l *LocalWorker) (storiface.Storage, error) { return func(l *LocalWorker) (storiface.Storage, error) { return ffiwrapper.New(&localWorkerPathProvider{w: l}, opts...) } } -func (l *localWorkerPathProvider) AcquireSectorCopy(ctx context.Context, id storiface.SectorRef, existing storiface.SectorFileType, allocate storiface.SectorFileType, ptype storiface.PathType) (storiface.SectorPaths, func(), error) { - return (&localWorkerPathProvider{w: l.w, op: storiface.AcquireCopy}).AcquireSector(ctx, id, existing, allocate, ptype) -} - type ReturnType string const ( From 04f5a595e45019a8cd52e48719e25cc6405b4a48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 19 Dec 2023 12:39:25 +0100 Subject: [PATCH 094/394] lpseal: Fix poller --- .../harmonydb/sql/20231217-sdr-pipeline.sql | 4 ++-- provider/lpseal/poller.go | 16 ++++++++++++++-- provider/lpwinning/winning_task.go | 6 +++--- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/lib/harmony/harmonydb/sql/20231217-sdr-pipeline.sql b/lib/harmony/harmonydb/sql/20231217-sdr-pipeline.sql index e41e865d0..c0540148f 100644 --- a/lib/harmony/harmonydb/sql/20231217-sdr-pipeline.sql +++ b/lib/harmony/harmonydb/sql/20231217-sdr-pipeline.sql @@ -67,8 +67,8 @@ create table sectors_sdr_pipeline ( -- Failure handling failed bool not null default false, failed_at timestamp, - failed_reason varchar(20), - failed_reason_msg text, + failed_reason varchar(20) not null default '', + failed_reason_msg text not null default '', -- constraints primary key (sp_id, sector_number) diff --git a/provider/lpseal/poller.go b/provider/lpseal/poller.go index 4747eb332..c3222016d 100644 --- a/provider/lpseal/poller.go +++ b/provider/lpseal/poller.go @@ -42,7 +42,7 @@ func (s *SealPoller) RunPoller(ctx context.Context) { return case <-ticker.C: if err := s.poll(ctx); err != nil { - log.Errorf("polling sdr sector pipeline: %w", err) + log.Errorw("polling failed", "error", err) } } } @@ -84,7 +84,19 @@ func (s *SealPoller) poll(ctx context.Context) error { FailedReason string `db:"failed_reason"` } - err := s.db.Select(ctx, &tasks, `SELECT * FROM sectors_sdr_pipeline WHERE after_commit_msg_success != true`) + err := s.db.Select(ctx, &tasks, `SELECT + sp_id, sector_number, + task_id_sdr, after_sdr, + task_id_tree_d, after_tree_d, + task_id_tree_c, after_tree_c, + task_id_tree_r, after_tree_r, + task_id_precommit_msg, after_precommit_msg, + task_id_precommit_msg_wait, after_precommit_msg_success, + task_id_porep, porep_proof, + task_id_commit_msg, after_commit_msg, + task_id_commit_msg_wait, after_commit_msg_success, + failed, failed_reason + FROM sectors_sdr_pipeline WHERE after_commit_msg_success != true`) if err != nil { return err } diff --git a/provider/lpwinning/winning_task.go b/provider/lpwinning/winning_task.go index 8b289a8de..b795ade4f 100644 --- a/provider/lpwinning/winning_task.go +++ b/provider/lpwinning/winning_task.go @@ -107,13 +107,13 @@ func (t *WinPostTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (don // First query to fetch from mining_tasks err = t.db.QueryRow(ctx, `SELECT sp_id, epoch, base_compute_time FROM mining_tasks WHERE task_id = $1`, taskID).Scan(&details.SpID, &details.Epoch, &details.CompTime) if err != nil { - return false, err + return false, xerrors.Errorf("query mining base info fail: %w", err) } // Second query to fetch from mining_base_block rows, err := t.db.Query(ctx, `SELECT block_cid FROM mining_base_block WHERE task_id = $1`, taskID) if err != nil { - return false, err + return false, xerrors.Errorf("query mining base blocks fail: %w", err) } defer rows.Close() @@ -126,7 +126,7 @@ func (t *WinPostTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (don } if err := rows.Err(); err != nil { - return false, err + return false, xerrors.Errorf("query mining base blocks fail (rows.Err): %w", err) } // construct base From e19b5333c3912042201e003dafd6110f6d5a02f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 19 Dec 2023 12:50:27 +0100 Subject: [PATCH 095/394] lpseal: Working SDR! --- provider/lpffi/sdr_funcs.go | 2 +- provider/lpseal/task_sdr.go | 20 +++++++++++++++++--- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/provider/lpffi/sdr_funcs.go b/provider/lpffi/sdr_funcs.go index fc1748213..1dae5fa71 100644 --- a/provider/lpffi/sdr_funcs.go +++ b/provider/lpffi/sdr_funcs.go @@ -74,7 +74,7 @@ func (l *storageProvider) AcquireSector(ctx context.Context, sector storiface.Se } func (sb *SealCalls) GenerateSDR(ctx context.Context, sector storiface.SectorRef, ticket abi.SealRandomness, commKcid cid.Cid) error { - paths, releaseSector, err := sb.sectors.AcquireSector(ctx, sector, storiface.FTCache, storiface.FTNone, storiface.PathSealing) + paths, releaseSector, err := sb.sectors.AcquireSector(ctx, sector, storiface.FTNone, storiface.FTCache, storiface.PathSealing) if err != nil { return xerrors.Errorf("acquiring sector paths: %w", err) } diff --git a/provider/lpseal/task_sdr.go b/provider/lpseal/task_sdr.go index 928344069..82908d40f 100644 --- a/provider/lpseal/task_sdr.go +++ b/provider/lpseal/task_sdr.go @@ -7,6 +7,7 @@ import ( "github.com/filecoin-project/go-commp-utils/zerocomm" "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/crypto" + "github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/chain/actors/policy" "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/lib/harmony/harmonydb" @@ -17,6 +18,8 @@ import ( "golang.org/x/xerrors" ) +var isDevnet = build.BlockDelaySecs < 30 + type SDRAPI interface { ChainHead(context.Context) (*types.TipSet, error) StateGetRandomnessFromTickets(context.Context, crypto.DomainSeparationTag, abi.ChainEpoch, []byte, types.TipSetKey) (abi.Randomness, error) @@ -45,13 +48,13 @@ func NewSDRTask(api SDRAPI, db *harmonydb.DB, sp *SealPoller, sc *lpffi.SealCall func (s *SDRTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (done bool, err error) { ctx := context.Background() - var sectorParams struct { + var sectorParamsArr []struct { SpID int64 `db:"sp_id"` SectorNumber int64 `db:"sector_number"` RegSealProof abi.RegisteredSealProof `db:"reg_seal_proof"` } - err = s.db.Select(ctx, §orParams, ` + err = s.db.Select(ctx, §orParamsArr, ` SELECT sp_id, sector_number, reg_seal_proof FROM sectors_sdr_pipeline WHERE task_id_sdr = $1`, taskID) @@ -59,6 +62,11 @@ func (s *SDRTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (done bo return false, xerrors.Errorf("getting sector params: %w", err) } + if len(sectorParamsArr) != 1 { + return false, xerrors.Errorf("expected 1 sector params, got %d", len(sectorParamsArr)) + } + sectorParams := sectorParamsArr[0] + var pieces []struct { PieceIndex int64 `db:"piece_index"` PieceCID string `db:"piece_cid"` @@ -151,7 +159,7 @@ func (s *SDRTask) CanAccept(ids []harmonytask.TaskID, engine *harmonytask.TaskEn } func (s *SDRTask) TypeDetails() harmonytask.TaskTypeDetails { - return harmonytask.TaskTypeDetails{ + res := harmonytask.TaskTypeDetails{ Max: s.maxSDR, Name: "SDR", Cost: resources.Resources{ // todo offset for prefetch? @@ -162,6 +170,12 @@ func (s *SDRTask) TypeDetails() harmonytask.TaskTypeDetails { MaxFailures: 0, Follows: nil, } + + if isDevnet { + res.Cost.Ram = 1 << 30 + } + + return res } func (s *SDRTask) Adder(taskFunc harmonytask.AddTaskFunc) { From 5eccc03fee665552460caabfd70154b5fe8bb386 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 20 Dec 2023 10:41:00 +0100 Subject: [PATCH 096/394] lpseal: store ticket after sdr --- provider/lpseal/task_sdr.go | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/provider/lpseal/task_sdr.go b/provider/lpseal/task_sdr.go index 82908d40f..0afdc34d4 100644 --- a/provider/lpseal/task_sdr.go +++ b/provider/lpseal/task_sdr.go @@ -107,7 +107,7 @@ func (s *SDRTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (done bo return false, xerrors.Errorf("getting miner address: %w", err) } - ticket, err := s.getTicket(ctx, maddr) + ticket, ticketEpoch, err := s.getTicket(ctx, maddr) if err != nil { return false, xerrors.Errorf("getting ticket: %w", err) } @@ -120,7 +120,10 @@ func (s *SDRTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (done bo } // store success! - n, err := s.db.Exec(ctx, `UPDATE sectors_sdr_pipeline SET after_sdr = true WHERE sp_id = $1 AND sector_number = $2`, sectorParams.SpID, sectorParams.SectorNumber) + n, err := s.db.Exec(ctx, `UPDATE sectors_sdr_pipeline + SET after_sdr = true, ticket_epoch = $3, ticket_value = $4 + WHERE sp_id = $1 AND sector_number = $2`, + sectorParams.SpID, sectorParams.SectorNumber, ticketEpoch, ticket) if err != nil { return false, xerrors.Errorf("store sdr success: updating pipeline: %w", err) } @@ -131,24 +134,24 @@ func (s *SDRTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (done bo return true, nil } -func (s *SDRTask) getTicket(ctx context.Context, maddr address.Address) (abi.SealRandomness, error) { +func (s *SDRTask) getTicket(ctx context.Context, maddr address.Address) (abi.SealRandomness, abi.ChainEpoch, error) { ts, err := s.api.ChainHead(ctx) if err != nil { - return nil, xerrors.Errorf("getting chain head: %w", err) + return nil, 0, xerrors.Errorf("getting chain head: %w", err) } ticketEpoch := ts.Height() - policy.SealRandomnessLookback buf := new(bytes.Buffer) if err := maddr.MarshalCBOR(buf); err != nil { - return nil, xerrors.Errorf("marshaling miner address: %w", err) + return nil, 0, xerrors.Errorf("marshaling miner address: %w", err) } rand, err := s.api.StateGetRandomnessFromTickets(ctx, crypto.DomainSeparationTag_SealRandomness, ticketEpoch, buf.Bytes(), ts.Key()) if err != nil { - return nil, xerrors.Errorf("getting randomness from tickets: %w", err) + return nil, 0, xerrors.Errorf("getting randomness from tickets: %w", err) } - return abi.SealRandomness(rand), nil + return abi.SealRandomness(rand), ticketEpoch, nil } func (s *SDRTask) CanAccept(ids []harmonytask.TaskID, engine *harmonytask.TaskEngine) (*harmonytask.TaskID, error) { From d79bc0ba132074d079a16f754d02982755c6dd2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 20 Dec 2023 11:28:06 +0100 Subject: [PATCH 097/394] lpseal: scaffold sdr-trees task --- provider/lpffi/sdr_funcs.go | 4 ++ provider/lpseal/poller.go | 1 + provider/lpseal/task_sdr.go | 16 ++--- provider/lpseal/task_trees.go | 107 ++++++++++++++++++++++++++++++++++ 4 files changed, 120 insertions(+), 8 deletions(-) create mode 100644 provider/lpseal/task_trees.go diff --git a/provider/lpffi/sdr_funcs.go b/provider/lpffi/sdr_funcs.go index 1dae5fa71..39b2dffde 100644 --- a/provider/lpffi/sdr_funcs.go +++ b/provider/lpffi/sdr_funcs.go @@ -103,3 +103,7 @@ func (sb *SealCalls) GenerateSDR(ctx context.Context, sector storiface.SectorRef return nil } + +func (sb *SealCalls) TreeRC() { + ffi.SealPreCommitPhase2() +} diff --git a/provider/lpseal/poller.go b/provider/lpseal/poller.go index c3222016d..a9f7b3ade 100644 --- a/provider/lpseal/poller.go +++ b/provider/lpseal/poller.go @@ -14,6 +14,7 @@ var log = logging.Logger("lpseal") const ( pollerSDR = iota + pollerTrees numPollers ) diff --git a/provider/lpseal/task_sdr.go b/provider/lpseal/task_sdr.go index 0afdc34d4..86efedafb 100644 --- a/provider/lpseal/task_sdr.go +++ b/provider/lpseal/task_sdr.go @@ -32,16 +32,16 @@ type SDRTask struct { sc *lpffi.SealCalls - maxSDR int + max int } func NewSDRTask(api SDRAPI, db *harmonydb.DB, sp *SealPoller, sc *lpffi.SealCalls, maxSDR int) *SDRTask { return &SDRTask{ - api: api, - db: db, - sp: sp, - sc: sc, - maxSDR: maxSDR, + api: api, + db: db, + sp: sp, + sc: sc, + max: maxSDR, } } @@ -163,14 +163,14 @@ func (s *SDRTask) CanAccept(ids []harmonytask.TaskID, engine *harmonytask.TaskEn func (s *SDRTask) TypeDetails() harmonytask.TaskTypeDetails { res := harmonytask.TaskTypeDetails{ - Max: s.maxSDR, + Max: s.max, Name: "SDR", Cost: resources.Resources{ // todo offset for prefetch? Cpu: 4, // todo multicore sdr Gpu: 0, Ram: 54 << 30, // todo measure; lower on 2k devnetn }, - MaxFailures: 0, + MaxFailures: 2, Follows: nil, } diff --git a/provider/lpseal/task_trees.go b/provider/lpseal/task_trees.go new file mode 100644 index 000000000..612794b99 --- /dev/null +++ b/provider/lpseal/task_trees.go @@ -0,0 +1,107 @@ +package lpseal + +import ( + "context" + "github.com/filecoin-project/go-commp-utils/zerocomm" + "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/lotus/lib/harmony/harmonydb" + "github.com/filecoin-project/lotus/lib/harmony/harmonytask" + "github.com/filecoin-project/lotus/lib/harmony/resources" + "github.com/filecoin-project/lotus/provider/lpffi" + "github.com/filecoin-project/lotus/storage/sealer/storiface" + "golang.org/x/xerrors" +) + +type TreesTask struct { + sp *SealPoller + db *harmonydb.DB + sc *lpffi.SealCalls + + max int +} + +func (t *TreesTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (done bool, err error) { + ctx := context.Background() + + var sectorParamsArr []struct { + SpID int64 `db:"sp_id"` + SectorNumber int64 `db:"sector_number"` + RegSealProof abi.RegisteredSealProof `db:"reg_seal_proof"` + } + + err = t.db.Select(ctx, §orParamsArr, ` + SELECT sp_id, sector_number, reg_seal_proof + FROM sectors_sdr_pipeline + WHERE task_id_tree_r = $1 and task_id_tree_c = $1 and task_id_tree_d = $1`, taskID) + if err != nil { + return false, xerrors.Errorf("getting sector params: %w", err) + } + + if len(sectorParamsArr) != 1 { + return false, xerrors.Errorf("expected 1 sector params, got %d", len(sectorParamsArr)) + } + sectorParams := sectorParamsArr[0] + + var pieces []struct { + PieceIndex int64 `db:"piece_index"` + PieceCID string `db:"piece_cid"` + PieceSize int64 `db:"piece_size"` + } + + err = t.db.Select(ctx, &pieces, ` + SELECT piece_index, piece_cid, piece_size + FROM sectors_sdr_initial_pieces + WHERE sp_id = $1 AND sector_number = $2`, sectorParams.SpID, sectorParams.SectorNumber) + if err != nil { + return false, xerrors.Errorf("getting pieces: %w", err) + } + + if len(pieces) > 0 { + // todo sectors with data + return false, xerrors.Errorf("todo sectors with data") + } + + ssize, err := sectorParams.RegSealProof.SectorSize() + if err != nil { + return false, xerrors.Errorf("getting sector size: %w", err) + } + + commd := zerocomm.ZeroPieceCommitment(abi.PaddedPieceSize(ssize).Unpadded()) + + sref := storiface.SectorRef{ + ID: abi.SectorID{ + Miner: abi.ActorID(sectorParams.SpID), + Number: abi.SectorNumber(sectorParams.SectorNumber), + }, + ProofType: sectorParams.RegSealProof, + } + + t.sc. +} + +func (t *TreesTask) CanAccept(ids []harmonytask.TaskID, engine *harmonytask.TaskEngine) (*harmonytask.TaskID, error) { + // todo reserve storage + + id := ids[0] + return &id, nil +} + +func (t *TreesTask) TypeDetails() harmonytask.TaskTypeDetails { + return harmonytask.TaskTypeDetails{ + Max: t.max, + Name: "SDRTrees", + Cost: resources.Resources{ + Cpu: 1, + Gpu: 1, + Ram: 8000, // todo + }, + MaxFailures: 3, + Follows: nil, + } +} + +func (t *TreesTask) Adder(taskFunc harmonytask.AddTaskFunc) { + t.sp.pollers[pollerTrees].Set(taskFunc) +} + +var _ harmonytask.TaskInterface = &TreesTask{} From f74c3ccd2d2b5081be1ada6561388cd00b5b039b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 20 Dec 2023 14:45:19 +0100 Subject: [PATCH 098/394] lpseal: Implement SDR-Trees --- cmd/lotus-provider/tasks/tasks.go | 4 ++ lib/harmony/resources/getGPU.go | 1 + node/config/types.go | 10 +++- provider/lpffi/sdr_funcs.go | 95 ++++++++++++++++++++++++++++++- provider/lpseal/poller.go | 23 ++++---- provider/lpseal/task_trees.go | 32 ++++++++++- 6 files changed, 150 insertions(+), 15 deletions(-) diff --git a/cmd/lotus-provider/tasks/tasks.go b/cmd/lotus-provider/tasks/tasks.go index 418031157..b81814423 100644 --- a/cmd/lotus-provider/tasks/tasks.go +++ b/cmd/lotus-provider/tasks/tasks.go @@ -72,6 +72,10 @@ func StartTasks(ctx context.Context, dependencies *deps.Deps) (*harmonytask.Task sdrTask := lpseal.NewSDRTask(full, db, sp, slr, cfg.Subsystems.SealSDRMaxTasks) activeTasks = append(activeTasks, sdrTask) } + if cfg.Subsystems.EnableSealSDRTrees { + treesTask := lpseal.NewTreesTask(sp, db, slr, cfg.Subsystems.SealSDRTreesMaxTasks) + activeTasks = append(activeTasks, treesTask) + } } log.Infow("This lotus_provider instance handles", "miner_addresses", maddrs, diff --git a/lib/harmony/resources/getGPU.go b/lib/harmony/resources/getGPU.go index 9a73bcd0d..3489e7491 100644 --- a/lib/harmony/resources/getGPU.go +++ b/lib/harmony/resources/getGPU.go @@ -11,6 +11,7 @@ import ( func getGPUDevices() float64 { // GPU boolean gpus, err := ffi.GetGPUDevices() + logger.Infow("GPUs", "list", gpus) if err != nil { logger.Errorf("getting gpu devices failed: %+v", err) } diff --git a/node/config/types.go b/node/config/types.go index 65fa4da02..e91d1dcf5 100644 --- a/node/config/types.go +++ b/node/config/types.go @@ -103,9 +103,17 @@ type ProviderSubsystemsConfig struct { GuiAddress string // EnableSealSDR enables SDR tasks to run. SDR is the long sequential computation - // creating layers. In lotus-miner this was run as part of PreCommit1. + // creating layers. + // In lotus-miner this was run as part of PreCommit1. EnableSealSDR bool SealSDRMaxTasks int + + // EnableSealSDRTrees enables the SDR pipeline tree-building task to run. + // This task handles encoding of unsealed data into last sdr layer and building + // of TreeR, TreeC and TreeD. + // In lotus-miner this was run as part of PreCommit2 (TreeD was run in PreCommit1). + EnableSealSDRTrees bool + SealSDRTreesMaxTasks int } type DAGStoreConfig struct { diff --git a/provider/lpffi/sdr_funcs.go b/provider/lpffi/sdr_funcs.go index 39b2dffde..09099af84 100644 --- a/provider/lpffi/sdr_funcs.go +++ b/provider/lpffi/sdr_funcs.go @@ -2,14 +2,19 @@ package lpffi import ( "context" + "encoding/json" + "fmt" ffi "github.com/filecoin-project/filecoin-ffi" commcid "github.com/filecoin-project/go-fil-commcid" "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/lotus/storage/paths" + "github.com/filecoin-project/lotus/storage/pipeline/lib/nullreader" "github.com/filecoin-project/lotus/storage/sealer/storiface" "github.com/ipfs/go-cid" logging "github.com/ipfs/go-log/v2" "golang.org/x/xerrors" + "io" + "os" ) var log = logging.Logger("lpffi") @@ -104,6 +109,92 @@ func (sb *SealCalls) GenerateSDR(ctx context.Context, sector storiface.SectorRef return nil } -func (sb *SealCalls) TreeRC() { - ffi.SealPreCommitPhase2() +func (sb *SealCalls) TreeRC(ctx context.Context, sector storiface.SectorRef, unsealed cid.Cid) (cid.Cid, cid.Cid, error) { + p1o, err := sb.makePhase1Out(unsealed, sector.ProofType) + if err != nil { + return cid.Undef, cid.Undef, xerrors.Errorf("make phase1 output: %w", err) + } + + paths, releaseSector, err := sb.sectors.AcquireSector(ctx, sector, storiface.FTCache, storiface.FTSealed, storiface.PathSealing) + if err != nil { + return cid.Undef, cid.Undef, xerrors.Errorf("acquiring sector paths: %w", err) + } + defer releaseSector() + + { + // create sector-sized file at paths.Sealed; PC2 transforms it into a sealed sector in-place + ssize, err := sector.ProofType.SectorSize() + if err != nil { + return cid.Undef, cid.Undef, xerrors.Errorf("getting sector size: %w", err) + } + + // paths.Sealed is a string filepath + f, err := os.Create(paths.Sealed) + if err != nil { + return cid.Undef, cid.Undef, xerrors.Errorf("creating sealed sector file: %w", err) + } + if err := f.Truncate(int64(ssize)); err != nil { + return cid.Undef, cid.Undef, xerrors.Errorf("truncating sealed sector file: %w", err) + } + + if os.Getenv("SEAL_WRITE_UNSEALED") == "1" { + // expliticly write zeros to unsealed sector + _, err := io.CopyN(f, nullreader.NewNullReader(abi.UnpaddedPieceSize(ssize)), int64(ssize)) + if err != nil { + return cid.Undef, cid.Undef, xerrors.Errorf("writing zeros to sealed sector file: %w", err) + } + } + } + + return ffi.SealPreCommitPhase2(p1o, paths.Cache, paths.Sealed) +} + +func (sb *SealCalls) makePhase1Out(unsCid cid.Cid, spt abi.RegisteredSealProof) ([]byte, error) { + commd, err := commcid.CIDToDataCommitmentV1(unsCid) + if err != nil { + return nil, xerrors.Errorf("make uns cid: %w", err) + } + + type Config struct { + ID string `json:"id"` + Path string `json:"path"` + RowsToDiscard int `json:"rows_to_discard"` + Size int `json:"size"` + } + + type Labels struct { + Labels []Config `json:"labels"` + } + + var phase1Output struct { + CommD [32]byte `json:"comm_d"` + Config Config `json:"config"` // TreeD + Labels map[string]*Labels `json:"labels"` + RegisteredProof string `json:"registered_proof"` + } + + copy(phase1Output.CommD[:], commd) + + phase1Output.Config.ID = "tree-d" + phase1Output.Config.Path = "/placeholder" + phase1Output.Labels = map[string]*Labels{} + + switch spt { + case abi.RegisteredSealProof_StackedDrg2KiBV1_1, abi.RegisteredSealProof_StackedDrg2KiBV1_1_Feat_SyntheticPoRep: + phase1Output.Config.RowsToDiscard = 0 + phase1Output.Config.Size = 127 + phase1Output.Labels["StackedDrg2KiBV1"].Labels = make([]Config, 2) + phase1Output.RegisteredProof = "StackedDrg2KiBV1_1" + + for i, l := range phase1Output.Labels["StackedDrg2KiBV1"].Labels { + l.ID = fmt.Sprintf("layer-%d", i+1) + l.Path = "/placeholder" + l.RowsToDiscard = 0 + l.Size = 64 + } + default: + panic("todo") + } + + return json.Marshal(phase1Output) } diff --git a/provider/lpseal/poller.go b/provider/lpseal/poller.go index a9f7b3ade..d3fae5990 100644 --- a/provider/lpseal/poller.go +++ b/provider/lpseal/poller.go @@ -109,7 +109,7 @@ func (s *SealPoller) poll(ctx context.Context) error { if task.TaskSDR == nil && s.pollers[pollerSDR].IsSet() { s.pollers[pollerSDR].Val(ctx)(func(id harmonytask.TaskID, tx *harmonydb.Tx) (shouldCommit bool, seriousError error) { - n, err := tx.Exec(`UPDATE sectors_sdr_pipeline SET task_id_sdr = $1 WHERE sp_id = $2 AND sector_number = $3`, id, task.SpID, task.SectorNumber) + n, err := tx.Exec(`UPDATE sectors_sdr_pipeline SET task_id_sdr = $1 WHERE sp_id = $2 AND sector_number = $3 and task_id_sdr is null`, id, task.SpID, task.SectorNumber) if err != nil { return false, xerrors.Errorf("update sectors_sdr_pipeline: %w", err) } @@ -120,16 +120,19 @@ func (s *SealPoller) poll(ctx context.Context) error { return true, nil }) } - if task.TaskTreeD == nil { - // todo start tree d task - } + if task.TaskTreeD == nil && task.TaskTreeC == nil && task.TaskTreeR == nil && s.pollers[pollerTrees].IsSet() && task.AfterSDR { + s.pollers[pollerTrees].Val(ctx)(func(id harmonytask.TaskID, tx *harmonydb.Tx) (shouldCommit bool, seriousError error) { + n, err := tx.Exec(`UPDATE sectors_sdr_pipeline SET task_id_tree_d = $1, task_id_tree_c = $1, task_id_tree_r = $1 + WHERE sp_id = $2 AND sector_number = $3 and task_id_tree_d is null and task_id_tree_c is null and task_id_tree_r is null`, id, task.SpID, task.SectorNumber) + if err != nil { + return false, xerrors.Errorf("update sectors_sdr_pipeline: %w", err) + } + if n != 1 { + return false, xerrors.Errorf("expected to update 1 row, updated %d", n) + } - // todo those two are really one pc2 - if task.TaskTreeC == nil && task.AfterSDR { - // todo start tree c task - } - if task.TaskTreeR == nil && task.AfterTreeC { - // todo start tree r task + return true, nil + }) } if task.TaskPrecommitMsg == nil && task.AfterTreeR && task.AfterTreeD { diff --git a/provider/lpseal/task_trees.go b/provider/lpseal/task_trees.go index 612794b99..610f280f0 100644 --- a/provider/lpseal/task_trees.go +++ b/provider/lpseal/task_trees.go @@ -20,6 +20,16 @@ type TreesTask struct { max int } +func NewTreesTask(sp *SealPoller, db *harmonydb.DB, sc *lpffi.SealCalls, maxTrees int) *TreesTask { + return &TreesTask{ + sp: sp, + db: db, + sc: sc, + + max: maxTrees, + } +} + func (t *TreesTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (done bool, err error) { ctx := context.Background() @@ -76,7 +86,25 @@ func (t *TreesTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (done ProofType: sectorParams.RegSealProof, } - t.sc. + sealed, unsealed, err := t.sc.TreeRC(ctx, sref, commd) + if err != nil { + return false, xerrors.Errorf("computing tree r and c: %w", err) + } + + // todo tree d!! (?) + + n, err := t.db.Exec(ctx, `UPDATE sectors_sdr_pipeline + SET after_tree_r = true, after_tree_c = true, after_tree_d = true, tree_r_cid = $1, tree_d_cid = $3 + WHERE sp_id = $1 AND sector_number = $2`, + sectorParams.SpID, sectorParams.SectorNumber, sealed, unsealed) + if err != nil { + return false, xerrors.Errorf("store sdr-trees success: updating pipeline: %w", err) + } + if n != 1 { + return false, xerrors.Errorf("store sdr-trees success: updated %d rows", n) + } + + return true, nil } func (t *TreesTask) CanAccept(ids []harmonytask.TaskID, engine *harmonytask.TaskEngine) (*harmonytask.TaskID, error) { @@ -93,7 +121,7 @@ func (t *TreesTask) TypeDetails() harmonytask.TaskTypeDetails { Cost: resources.Resources{ Cpu: 1, Gpu: 1, - Ram: 8000, // todo + Ram: 8000 << 20, // todo }, MaxFailures: 3, Follows: nil, From 47672e0933bed356a74ddc3779c18d1e13131304 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 20 Dec 2023 18:03:47 +0100 Subject: [PATCH 099/394] storage: Fix dbindex heartbeat --- storage/paths/db_index.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/paths/db_index.go b/storage/paths/db_index.go index e6bf3e5da..018ea84fd 100644 --- a/storage/paths/db_index.go +++ b/storage/paths/db_index.go @@ -337,7 +337,7 @@ func (dbi *DBIndex) StorageReportHealth(ctx context.Context, id storiface.ID, re report.Stat.FSAvailable, report.Stat.Reserved, report.Stat.Used, - time.Now()) + time.Now().UTC()) if err != nil { return xerrors.Errorf("updating storage health in DB fails with err: %v", err) } From c63ecba668c05699f4cee3489f86210bbc59af4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 20 Dec 2023 19:22:52 +0100 Subject: [PATCH 100/394] lpseal: Fix makePhase1Out properly --- provider/lpffi/sdr_funcs.go | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/provider/lpffi/sdr_funcs.go b/provider/lpffi/sdr_funcs.go index 09099af84..d5c2f03fb 100644 --- a/provider/lpffi/sdr_funcs.go +++ b/provider/lpffi/sdr_funcs.go @@ -115,6 +115,8 @@ func (sb *SealCalls) TreeRC(ctx context.Context, sector storiface.SectorRef, uns return cid.Undef, cid.Undef, xerrors.Errorf("make phase1 output: %w", err) } + log.Errorw("phase1 output", "p1o", p1o) + paths, releaseSector, err := sb.sectors.AcquireSector(ctx, sector, storiface.FTCache, storiface.FTSealed, storiface.PathSealing) if err != nil { return cid.Undef, cid.Undef, xerrors.Errorf("acquiring sector paths: %w", err) @@ -163,6 +165,7 @@ func (sb *SealCalls) makePhase1Out(unsCid cid.Cid, spt abi.RegisteredSealProof) } type Labels struct { + H *string `json:"_h"` // proofs want this.. Labels []Config `json:"labels"` } @@ -183,14 +186,16 @@ func (sb *SealCalls) makePhase1Out(unsCid cid.Cid, spt abi.RegisteredSealProof) case abi.RegisteredSealProof_StackedDrg2KiBV1_1, abi.RegisteredSealProof_StackedDrg2KiBV1_1_Feat_SyntheticPoRep: phase1Output.Config.RowsToDiscard = 0 phase1Output.Config.Size = 127 - phase1Output.Labels["StackedDrg2KiBV1"].Labels = make([]Config, 2) + phase1Output.Labels["StackedDrg2KiBV1"] = &Labels{} phase1Output.RegisteredProof = "StackedDrg2KiBV1_1" - for i, l := range phase1Output.Labels["StackedDrg2KiBV1"].Labels { - l.ID = fmt.Sprintf("layer-%d", i+1) - l.Path = "/placeholder" - l.RowsToDiscard = 0 - l.Size = 64 + for i := 0; i < 2; i++ { + phase1Output.Labels["StackedDrg2KiBV1"].Labels = append(phase1Output.Labels["StackedDrg2KiBV1"].Labels, Config{ + ID: fmt.Sprintf("layer-%d", i+1), + Path: "/placeholder", + RowsToDiscard: 0, + Size: 64, + }) } default: panic("todo") From 3f9aae7031fa1244141abe84fdedd7393cfbd1b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 21 Dec 2023 17:28:26 +0100 Subject: [PATCH 101/394] lpseal: Implement TreeD build function in Go --- provider/lpproof/treed_build.go | 241 +++++++++++++++++++++++++++ provider/lpproof/treed_build_test.go | 116 +++++++++++++ 2 files changed, 357 insertions(+) create mode 100644 provider/lpproof/treed_build.go create mode 100644 provider/lpproof/treed_build_test.go diff --git a/provider/lpproof/treed_build.go b/provider/lpproof/treed_build.go new file mode 100644 index 000000000..ff52968e5 --- /dev/null +++ b/provider/lpproof/treed_build.go @@ -0,0 +1,241 @@ +package lpproof + +import ( + commcid "github.com/filecoin-project/go-fil-commcid" + "github.com/filecoin-project/go-state-types/abi" + "github.com/hashicorp/go-multierror" + "github.com/ipfs/go-cid" + "github.com/minio/sha256-simd" + "golang.org/x/xerrors" + "io" + "math/bits" + "os" + "runtime" + "sync" + "time" +) + +const nodeSize = 32 +const threadChunkSize = 1 << 20 +const nodesPerChunk = threadChunkSize / nodeSize + +func hashChunk(data [][]byte) { + l1Nodes := len(data[0]) / nodeSize / 2 + + d := sha256.New() + + for i := 0; i < l1Nodes; i++ { + levels := bits.TrailingZeros(^uint(i)) + 1 + + inNode := i * 2 // at level 0 + outNode := i + + for l := 0; l < levels; l++ { + d.Reset() + inNodeData := data[l][inNode*nodeSize : (inNode+2)*nodeSize] + d.Write(inNodeData) + copy(data[l+1][outNode*nodeSize:(outNode+1)*nodeSize], d.Sum(nil)) + // set top bits to 00 + data[l+1][outNode*nodeSize+nodeSize-1] &= 0x3f + + inNode-- + inNode >>= 1 + outNode >>= 1 + } + } +} + +func BuildTreeD(data io.Reader, outPath string, size abi.PaddedPieceSize) (cid.Cid, error) { + out, err := os.Create(outPath) + if err != nil { + return cid.Undef, err + } + + outSize := treeSize(size) + + // allocate space for the tree + err = out.Truncate(int64(outSize)) + if err != nil { + return cid.Undef, err + } + + // setup buffers + maxThreads := int64(size) / threadChunkSize + if maxThreads > int64(runtime.NumCPU()) { + maxThreads = int64(runtime.NumCPU()) + } + if maxThreads < 1 { + maxThreads = 1 + } + + // allocate buffers + var bufLk sync.Mutex + workerBuffers := make([][][]byte, maxThreads) // [worker][level][levelSize] + + for i := range workerBuffers { + workerBuffer := make([][]byte, 1) + + bottomBufSize := int64(threadChunkSize) + if bottomBufSize > int64(size) { + bottomBufSize = int64(size) + } + workerBuffer[0] = make([]byte, bottomBufSize) + + // append levels until we get to a 32 byte level + for len(workerBuffer[len(workerBuffer)-1]) > 32 { + newLevel := make([]byte, len(workerBuffer[len(workerBuffer)-1])/2) + workerBuffer = append(workerBuffer, newLevel) + } + workerBuffers[i] = workerBuffer + } + + // prepare apex buffer + apexBottomSize := uint64(size) / uint64(len(workerBuffers[0][0])) + var apexBuf [][]byte + threadLayers := 1 + + if apexBottomSize > 1 { + apexBuf = make([][]byte, 1) + apexBuf[0] = make([]byte, apexBottomSize) + for len(apexBuf[len(apexBuf)-1]) > 32 { + newLevel := make([]byte, len(apexBuf[len(apexBuf)-1])/2) + apexBuf = append(apexBuf, newLevel) + threadLayers++ + } + } + + // start processing + var processed uint64 + var workWg sync.WaitGroup + var errLock sync.Mutex + var oerr error + + for processed < uint64(size) { + // get a buffer + bufLk.Lock() + if len(workerBuffers) == 0 { + bufLk.Unlock() + time.Sleep(50 * time.Microsecond) + continue + } + + // pop last + workBuffer := workerBuffers[len(workerBuffers)-1] + workerBuffers = workerBuffers[:len(workerBuffers)-1] + + bufLk.Unlock() + + // before reading check that we didn't get a write error + errLock.Lock() + if oerr != nil { + errLock.Unlock() + return cid.Undef, oerr + } + errLock.Unlock() + + // read data into the bottom level + // note: the bottom level will never be too big; data is power of two + // size, and if it's smaller than a single buffer, we only have one + // smaller buffer + + _, err := io.ReadFull(data, workBuffer[0]) + if err != nil && err != io.EOF { + return cid.Undef, err + } + + // start processing + workWg.Add(1) + go func(startOffset uint64) { + hashChunk(workBuffer) + + // persist apex if needed + if len(apexBuf) > 0 { + apexHash := workBuffer[len(workBuffer)-1] + hashPos := startOffset >> threadLayers + + copy(apexBuf[0][hashPos:hashPos+nodeSize], apexHash) + } + + // write results + offsetInLayer := startOffset + for layer, layerData := range workBuffer { + + // layerOff is outSize:bits[most significant bit - layer] + layerOff := layerOffset(uint64(size), layer) + dataOff := offsetInLayer + layerOff + offsetInLayer /= 2 + + _, werr := out.WriteAt(layerData, int64(dataOff)) + if werr != nil { + errLock.Lock() + oerr = multierror.Append(oerr, werr) + errLock.Unlock() + return + } + } + + // return buffer + bufLk.Lock() + workerBuffers = append(workerBuffers, workBuffer) + bufLk.Unlock() + + workWg.Done() + }(processed) + + processed += uint64(len(workBuffer[0])) + } + + workWg.Wait() + + if oerr != nil { + return cid.Undef, oerr + } + + if len(apexBuf) > 0 { + // hash the apex + hashChunk(apexBuf) + + // write apex + for apexLayer, layerData := range apexBuf { + layer := apexLayer + threadLayers + + layerOff := layerOffset(uint64(size), layer) + _, werr := out.WriteAt(layerData, int64(layerOff)) + if werr != nil { + return cid.Undef, xerrors.Errorf("write apex: %w", werr) + } + } + } + + var commp [32]byte + if len(workerBuffers) == 1 { + copy(commp[:], workerBuffers[0][0]) + } else { + copy(commp[:], apexBuf[0]) + } + + commCid, err := commcid.DataCommitmentV1ToCID(commp[:]) + if err != nil { + return cid.Undef, err + } + + return commCid, nil +} + +func treeSize(data abi.PaddedPieceSize) uint64 { + bytesToAlloc := uint64(data) + + // append bytes until we get to nodeSize + for todo := bytesToAlloc; todo > nodeSize; todo /= 2 { + bytesToAlloc += todo / 2 + } + + return bytesToAlloc +} + +func layerOffset(size uint64, layer int) uint64 { + layerBits := uint64(1) << uint64(layer) + layerBits-- + layerOff := (size * layerBits) >> uint64(layer-1) + return layerOff +} diff --git a/provider/lpproof/treed_build_test.go b/provider/lpproof/treed_build_test.go new file mode 100644 index 000000000..f1834de3e --- /dev/null +++ b/provider/lpproof/treed_build_test.go @@ -0,0 +1,116 @@ +package lpproof + +import ( + "crypto/rand" + "github.com/filecoin-project/go-state-types/abi" + "github.com/stretchr/testify/require" + "testing" +) + +func TestTreeSize(t *testing.T) { + require.Equal(t, uint64(32), treeSize(abi.PaddedPieceSize(32))) + require.Equal(t, uint64(64+32), treeSize(abi.PaddedPieceSize(64))) + require.Equal(t, uint64(128+64+32), treeSize(abi.PaddedPieceSize(128))) + require.Equal(t, uint64(256+128+64+32), treeSize(abi.PaddedPieceSize(256))) +} + +func TestTreeLayerOffset(t *testing.T) { + require.Equal(t, uint64(0), layerOffset(128, 0)) + require.Equal(t, uint64(128), layerOffset(128, 1)) + require.Equal(t, uint64(128+64), layerOffset(128, 2)) + require.Equal(t, uint64(128+64+32), layerOffset(128, 3)) +} + +func TestHashChunk(t *testing.T) { + chunk := make([]byte, 64) + chunk[0] = 0x01 + + out := make([]byte, 32) + + data := [][]byte{chunk, out} + hashChunk(data) + + // 16 ab ab 34 1f b7 f3 70 e2 7e 4d ad cf 81 76 6d + // d0 df d0 ae 64 46 94 77 bb 2c f6 61 49 38 b2 2f + expect := []byte{ + 0x16, 0xab, 0xab, 0x34, 0x1f, 0xb7, 0xf3, 0x70, + 0xe2, 0x7e, 0x4d, 0xad, 0xcf, 0x81, 0x76, 0x6d, + 0xd0, 0xdf, 0xd0, 0xae, 0x64, 0x46, 0x94, 0x77, + 0xbb, 0x2c, 0xf6, 0x61, 0x49, 0x38, 0xb2, 0x2f, + } + + require.Equal(t, expect, out) +} + +func TestHashChunk2L(t *testing.T) { + data0 := make([]byte, 128) + data0[0] = 0x01 + + l1 := make([]byte, 64) + l2 := make([]byte, 32) + + data := [][]byte{data0, l1, l2} + hashChunk(data) + + // 16 ab ab 34 1f b7 f3 70 e2 7e 4d ad cf 81 76 6d + // d0 df d0 ae 64 46 94 77 bb 2c f6 61 49 38 b2 2f + expectL1Left := []byte{ + 0x16, 0xab, 0xab, 0x34, 0x1f, 0xb7, 0xf3, 0x70, + 0xe2, 0x7e, 0x4d, 0xad, 0xcf, 0x81, 0x76, 0x6d, + 0xd0, 0xdf, 0xd0, 0xae, 0x64, 0x46, 0x94, 0x77, + 0xbb, 0x2c, 0xf6, 0x61, 0x49, 0x38, 0xb2, 0x2f, + } + + // f5 a5 fd 42 d1 6a 20 30 27 98 ef 6e d3 09 97 9b + // 43 00 3d 23 20 d9 f0 e8 ea 98 31 a9 27 59 fb 0b + expectL1Rest := []byte{ + 0xf5, 0xa5, 0xfd, 0x42, 0xd1, 0x6a, 0x20, 0x30, + 0x27, 0x98, 0xef, 0x6e, 0xd3, 0x09, 0x97, 0x9b, + 0x43, 0x00, 0x3d, 0x23, 0x20, 0xd9, 0xf0, 0xe8, + 0xea, 0x98, 0x31, 0xa9, 0x27, 0x59, 0xfb, 0x0b, + } + + require.Equal(t, expectL1Left, l1[:32]) + require.Equal(t, expectL1Rest, l1[32:]) + + // 0d d6 da e4 1c 2f 75 55 01 29 59 4f b6 44 e4 a8 + // 42 cf af b3 16 a2 d5 93 21 e3 88 fe 84 a1 ec 2f + expectL2 := []byte{ + 0x0d, 0xd6, 0xda, 0xe4, 0x1c, 0x2f, 0x75, 0x55, + 0x01, 0x29, 0x59, 0x4f, 0xb6, 0x44, 0xe4, 0xa8, + 0x42, 0xcf, 0xaf, 0xb3, 0x16, 0xa2, 0xd5, 0x93, + 0x21, 0xe3, 0x88, 0xfe, 0x84, 0xa1, 0xec, 0x2f, + } + + require.Equal(t, expectL2, l2) +} + +func BenchmarkHashChunk(b *testing.B) { + const benchSize = 1024 * 1024 + + // Generate 1 MiB of random data + randomData := make([]byte, benchSize) + if _, err := rand.Read(randomData); err != nil { + b.Fatalf("Failed to generate random data: %v", err) + } + + // Prepare data structure for hashChunk + data := make([][]byte, 1) + data[0] = randomData + + // append levels until we get to a 32 byte level + for len(data[len(data)-1]) > 32 { + newLevel := make([]byte, len(data[len(data)-1])/2) + data = append(data, newLevel) + } + + b.SetBytes(benchSize) // Set the number of bytes for the benchmark + + b.ResetTimer() // Start the timer after setup + + for i := 0; i < b.N; i++ { + hashChunk(data) + // Use the result in some way to avoid compiler optimization + _ = data[1] + } +} From 0d4953c8da50896dca9c0b9c11c96bc466070bc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sat, 23 Dec 2023 15:14:41 +0100 Subject: [PATCH 102/394] lpseal: Make TreeD correct --- provider/lpproof/treed_build.go | 30 +++--- provider/lpproof/treed_build_test.go | 148 +++++++++++++++++++++++++++ 2 files changed, 164 insertions(+), 14 deletions(-) diff --git a/provider/lpproof/treed_build.go b/provider/lpproof/treed_build.go index ff52968e5..8064d5045 100644 --- a/provider/lpproof/treed_build.go +++ b/provider/lpproof/treed_build.go @@ -90,17 +90,18 @@ func BuildTreeD(data io.Reader, outPath string, size abi.PaddedPieceSize) (cid.C } // prepare apex buffer - apexBottomSize := uint64(size) / uint64(len(workerBuffers[0][0])) var apexBuf [][]byte - threadLayers := 1 + { + apexBottomSize := uint64(size) / uint64(len(workerBuffers[0][0])) + if apexBottomSize == 0 { + apexBottomSize = 1 + } - if apexBottomSize > 1 { apexBuf = make([][]byte, 1) - apexBuf[0] = make([]byte, apexBottomSize) + apexBuf[0] = make([]byte, apexBottomSize*nodeSize) for len(apexBuf[len(apexBuf)-1]) > 32 { newLevel := make([]byte, len(apexBuf[len(apexBuf)-1])/2) apexBuf = append(apexBuf, newLevel) - threadLayers++ } } @@ -148,10 +149,10 @@ func BuildTreeD(data io.Reader, outPath string, size abi.PaddedPieceSize) (cid.C go func(startOffset uint64) { hashChunk(workBuffer) - // persist apex if needed - if len(apexBuf) > 0 { + // persist apex + { apexHash := workBuffer[len(workBuffer)-1] - hashPos := startOffset >> threadLayers + hashPos := startOffset / uint64(len(workBuffer[0])) * nodeSize copy(apexBuf[0][hashPos:hashPos+nodeSize], apexHash) } @@ -191,13 +192,18 @@ func BuildTreeD(data io.Reader, outPath string, size abi.PaddedPieceSize) (cid.C return cid.Undef, oerr } + threadLayers := bits.Len(uint(len(workerBuffers[0][0])) / nodeSize) + if len(apexBuf) > 0 { // hash the apex hashChunk(apexBuf) // write apex for apexLayer, layerData := range apexBuf { - layer := apexLayer + threadLayers + if apexLayer == 0 { + continue + } + layer := apexLayer + threadLayers - 1 layerOff := layerOffset(uint64(size), layer) _, werr := out.WriteAt(layerData, int64(layerOff)) @@ -208,11 +214,7 @@ func BuildTreeD(data io.Reader, outPath string, size abi.PaddedPieceSize) (cid.C } var commp [32]byte - if len(workerBuffers) == 1 { - copy(commp[:], workerBuffers[0][0]) - } else { - copy(commp[:], apexBuf[0]) - } + copy(commp[:], apexBuf[len(apexBuf)-1]) commCid, err := commcid.DataCommitmentV1ToCID(commp[:]) if err != nil { diff --git a/provider/lpproof/treed_build_test.go b/provider/lpproof/treed_build_test.go index f1834de3e..fe020ebb7 100644 --- a/provider/lpproof/treed_build_test.go +++ b/provider/lpproof/treed_build_test.go @@ -1,9 +1,13 @@ package lpproof import ( + "bytes" "crypto/rand" + "fmt" "github.com/filecoin-project/go-state-types/abi" "github.com/stretchr/testify/require" + "os" + "path/filepath" "testing" ) @@ -85,6 +89,150 @@ func TestHashChunk2L(t *testing.T) { require.Equal(t, expectL2, l2) } +func Test2K(t *testing.T) { + data := make([]byte, 2048) + data[0] = 0x01 + + tempFile := filepath.Join(t.TempDir(), "tree.dat") + + commd, err := BuildTreeD(bytes.NewReader(data), tempFile, 2048) + require.NoError(t, err) + fmt.Println(commd) + + // dump tree.dat + dat, err := os.ReadFile(tempFile) + require.NoError(t, err) + + for i, b := range dat { + // 32 values per line + if i%32 == 0 { + fmt.Println() + + // line offset hexdump style + fmt.Printf("%04x: ", i) + } + fmt.Printf("%02x ", b) + } + fmt.Println() + + require.Equal(t, "baga6ea4seaqovgk4kr4eoifujh6jfmdqvw3m6zrvyjqzu6s6abkketui6jjoydi", commd.String()) + +} + +func Test8MiB(t *testing.T) { + data := make([]byte, 8<<20) + data[0] = 0x01 + + tempFile := filepath.Join(t.TempDir(), "tree.dat") + + commd, err := BuildTreeD(bytes.NewReader(data), tempFile, 8<<20) + require.NoError(t, err) + fmt.Println(commd) + + // dump tree.dat + dat, err := os.ReadFile(tempFile) + require.NoError(t, err) + + actualD := hexPrint32LDedup(dat) + + expectD := `00000000: 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00000020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +* +00800000: 16 ab ab 34 1f b7 f3 70 e2 7e 4d ad cf 81 76 6d d0 df d0 ae 64 46 94 77 bb 2c f6 61 49 38 b2 2f +00800020: f5 a5 fd 42 d1 6a 20 30 27 98 ef 6e d3 09 97 9b 43 00 3d 23 20 d9 f0 e8 ea 98 31 a9 27 59 fb 0b +* +00c00000: 0d d6 da e4 1c 2f 75 55 01 29 59 4f b6 44 e4 a8 42 cf af b3 16 a2 d5 93 21 e3 88 fe 84 a1 ec 2f +00c00020: 37 31 bb 99 ac 68 9f 66 ee f5 97 3e 4a 94 da 18 8f 4d dc ae 58 07 24 fc 6f 3f d6 0d fd 48 83 33 +* +00e00000: 11 b1 c4 80 05 21 d5 e5 83 4a de b3 70 7c 74 15 9f f3 37 b0 96 16 3c 94 31 16 73 40 e7 b1 17 1d +00e00020: 64 2a 60 7e f8 86 b0 04 bf 2c 19 78 46 3a e1 d4 69 3a c0 f4 10 eb 2d 1b 7a 47 fe 20 5e 5e 75 0f +* +00f00000: ec 69 25 55 9b cc 52 84 0a 22 38 5b 2b 6b 35 b4 50 14 50 04 28 f4 59 fe c1 23 01 0f e7 ef 18 1c +00f00020: 57 a2 38 1a 28 65 2b f4 7f 6b ef 7a ca 67 9b e4 ae de 58 71 ab 5c f3 eb 2c 08 11 44 88 cb 85 26 +* +00f80000: 3d d2 eb 19 3e e2 f0 47 34 87 bf 4b 83 aa 3a bd a9 c8 4e fa e5 52 6d 8a fd 61 2d 5d 9e 3d 79 34 +00f80020: 1f 7a c9 59 55 10 e0 9e a4 1c 46 0b 17 64 30 bb 32 2c d6 fb 41 2e c5 7c b1 7d 98 9a 43 10 37 2f +* +00fc0000: ea 99 5c 54 78 47 20 b4 49 fc 92 b0 70 ad b6 cf 66 35 c2 61 9a 7a 5e 00 54 a2 4e 88 f2 52 ec 0d +00fc0020: fc 7e 92 82 96 e5 16 fa ad e9 86 b2 8f 92 d4 4a 4f 24 b9 35 48 52 23 37 6a 79 90 27 bc 18 f8 33 +* +00fe0000: b9 97 02 8b 06 d7 2e 96 07 86 79 58 e1 5f 8d 07 b7 ae 37 ab 29 ab 3f a9 de fe c9 8e aa 37 6e 28 +00fe0020: 08 c4 7b 38 ee 13 bc 43 f4 1b 91 5c 0e ed 99 11 a2 60 86 b3 ed 62 40 1b f9 d5 8b 8d 19 df f6 24 +* +00ff0000: a0 c4 4f 7b a4 4c d2 3c 2e bf 75 98 7b e8 98 a5 63 80 73 b2 f9 11 cf ee ce 14 5a 77 58 0c 6c 12 +00ff0020: b2 e4 7b fb 11 fa cd 94 1f 62 af 5c 75 0f 3e a5 cc 4d f5 17 d5 c4 f1 6d b2 b4 d7 7b ae c1 a3 2f +* +00ff8000: 89 2d 2b 00 a5 c1 54 10 94 ca 65 de 21 3b bd 45 90 14 15 ed d1 10 17 cd 29 f3 ed 75 73 02 a0 3f +00ff8020: f9 22 61 60 c8 f9 27 bf dc c4 18 cd f2 03 49 31 46 00 8e ae fb 7d 02 19 4d 5e 54 81 89 00 51 08 +* +00ffc000: 22 48 54 8b ba a5 8f e2 db 0b 07 18 c1 d7 20 1f ed 64 c7 8d 7d 22 88 36 b2 a1 b2 f9 42 0b ef 3c +00ffc020: 2c 1a 96 4b b9 0b 59 eb fe 0f 6d a2 9a d6 5a e3 e4 17 72 4a 8f 7c 11 74 5a 40 ca c1 e5 e7 40 11 +* +00ffe000: 1c 6a 48 08 3e 17 49 90 ef c0 56 ec b1 44 75 1d e2 76 d8 a5 1c 3d 93 d7 4c 81 92 48 ab 78 cc 30 +00ffe020: fe e3 78 ce f1 64 04 b1 99 ed e0 b1 3e 11 b6 24 ff 9d 78 4f bb ed 87 8d 83 29 7e 79 5e 02 4f 02 +* +00fff000: 0a b4 26 38 1b 72 cd 3b b3 e3 c7 82 18 fe 1f 18 3b 3a 19 db c4 d9 26 94 30 03 cd 01 b6 d1 8d 0b +00fff020: 8e 9e 24 03 fa 88 4c f6 23 7f 60 df 25 f8 3e e4 0d ca 9e d8 79 eb 6f 63 52 d1 50 84 f5 ad 0d 3f +* +00fff800: 16 0d 87 17 1b e7 ae e4 20 a3 54 24 cf df 4f fe a2 fd 7b 94 58 89 58 f3 45 11 57 fc 39 8f 34 26 +00fff820: 75 2d 96 93 fa 16 75 24 39 54 76 e3 17 a9 85 80 f0 09 47 af b7 a3 05 40 d6 25 a9 29 1c c1 2a 07 +* +00fffc00: 1f 40 60 11 da 08 f8 09 80 63 97 dc 1c 57 b9 87 83 37 5a 59 5d d6 81 42 6c 1e cd d4 3c ab e3 3c +00fffc20: 70 22 f6 0f 7e f6 ad fa 17 11 7a 52 61 9e 30 ce a8 2c 68 07 5a df 1c 66 77 86 ec 50 6e ef 2d 19 +* +00fffe00: 51 4e dd 2f 6f 8f 6d fd 54 b0 d1 20 7b b7 06 df 85 c5 a3 19 0e af 38 72 37 20 c5 07 56 67 7f 14 +00fffe20: d9 98 87 b9 73 57 3a 96 e1 13 93 64 52 36 c1 7b 1f 4c 70 34 d7 23 c7 a9 9f 70 9b b4 da 61 16 2b +* +00ffff00: 5a 1d 84 74 85 a3 4b 28 08 93 a9 cf b2 8b 54 44 67 12 8b eb c0 22 bd de c1 04 be ca b4 f4 81 31 +00ffff20: d0 b5 30 db b0 b4 f2 5c 5d 2f 2a 28 df ee 80 8b 53 41 2a 02 93 1f 18 c4 99 f5 a2 54 08 6b 13 26 +* +00ffff80: c5 fb f3 f9 4c c2 2b 3c 51 ad c1 ea af e9 4b a0 9f b2 73 f3 73 d2 10 1f 12 0b 11 c6 85 21 66 2f +00ffffa0: 84 c0 42 1b a0 68 5a 01 bf 79 5a 23 44 06 4f e4 24 bd 52 a9 d2 43 77 b3 94 ff 4c 4b 45 68 e8 11 +00ffffc0: 23 40 4a 88 80 f9 cb c7 20 39 cb 86 14 35 9c 28 34 84 55 70 fe 95 19 0b bd 4d 93 41 42 e8 25 2c +` + + fmt.Println(actualD) + + require.EqualValues(t, expectD, actualD) + + require.Equal(t, "baga6ea4seaqcgqckrcapts6hea44xbqugwocqneekvyp5fizbo6u3e2biluckla", commd.String()) +} + +func hexPrint32LDedup(b []byte) string { + var prevLine string + var currentLine string + var isDuplicate bool + + var outStr string + + for i, v := range b { + // Build the current line + currentLine += fmt.Sprintf("%02x ", v) + + if (i+1)%32 == 0 || i == len(b)-1 { + // Check for duplicate line + wasDuplicate := isDuplicate + isDuplicate = currentLine == prevLine + + if !isDuplicate { + if wasDuplicate { + //fmt.Println("*") + outStr += fmt.Sprintln("*") + } + + //fmt.Printf("%08x: %s\n", i-31, currentLine) + outStr += fmt.Sprintf("%08x: %s\n", i-31, currentLine) + } + + // Update the previous line and reset current line + prevLine = currentLine + currentLine = "" + } + } + + return outStr +} + func BenchmarkHashChunk(b *testing.B) { const benchSize = 1024 * 1024 From 6af3757136eaf25caeafc7d9f74e0ed51834422b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sat, 23 Dec 2023 15:18:48 +0100 Subject: [PATCH 103/394] treed: 512M bench --- provider/lpproof/treed_build_test.go | 35 ++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/provider/lpproof/treed_build_test.go b/provider/lpproof/treed_build_test.go index fe020ebb7..5fd28bbd3 100644 --- a/provider/lpproof/treed_build_test.go +++ b/provider/lpproof/treed_build_test.go @@ -6,6 +6,7 @@ import ( "fmt" "github.com/filecoin-project/go-state-types/abi" "github.com/stretchr/testify/require" + "io/ioutil" "os" "path/filepath" "testing" @@ -262,3 +263,37 @@ func BenchmarkHashChunk(b *testing.B) { _ = data[1] } } + +func BenchmarkBuildTreeD512M(b *testing.B) { + const dataSize = 512 * 1024 * 1024 // 512 MiB + + // Generate 512 MiB of random data + data := make([]byte, dataSize) + if _, err := rand.Read(data); err != nil { + b.Fatalf("Failed to generate random data: %v", err) + } + + b.SetBytes(int64(dataSize)) // Set the number of bytes for the benchmark + + for i := 0; i < b.N; i++ { + // Create a temporary file for each iteration + tempFile, err := ioutil.TempFile("", "tree.dat") + if err != nil { + b.Fatalf("Failed to create temporary file: %v", err) + } + tempFilePath := tempFile.Name() + tempFile.Close() + + b.StartTimer() // Start the timer for the BuildTreeD operation + + _, err = BuildTreeD(bytes.NewReader(data), tempFilePath, dataSize) + if err != nil { + b.Fatalf("BuildTreeD failed: %v", err) + } + + b.StopTimer() // Stop the timer after BuildTreeD completes + + // Clean up the temporary file + os.Remove(tempFilePath) + } +} From a2c906fe4f8f94bbcefb45ffb3c87bc924ec67e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sat, 23 Dec 2023 15:42:24 +0100 Subject: [PATCH 104/394] lpseal: Use pooled buffers for TreeD --- provider/lpproof/treed_build.go | 29 +++++++++++++++++++++------- provider/lpproof/treed_build_test.go | 22 +++++++++++++++++++++ 2 files changed, 44 insertions(+), 7 deletions(-) diff --git a/provider/lpproof/treed_build.go b/provider/lpproof/treed_build.go index 8064d5045..a59ef6624 100644 --- a/provider/lpproof/treed_build.go +++ b/provider/lpproof/treed_build.go @@ -5,6 +5,7 @@ import ( "github.com/filecoin-project/go-state-types/abi" "github.com/hashicorp/go-multierror" "github.com/ipfs/go-cid" + pool "github.com/libp2p/go-buffer-pool" "github.com/minio/sha256-simd" "golang.org/x/xerrors" "io" @@ -24,6 +25,8 @@ func hashChunk(data [][]byte) { d := sha256.New() + sumBuf := make([]byte, nodeSize) + for i := 0; i < l1Nodes; i++ { levels := bits.TrailingZeros(^uint(i)) + 1 @@ -34,7 +37,7 @@ func hashChunk(data [][]byte) { d.Reset() inNodeData := data[l][inNode*nodeSize : (inNode+2)*nodeSize] d.Write(inNodeData) - copy(data[l+1][outNode*nodeSize:(outNode+1)*nodeSize], d.Sum(nil)) + copy(data[l+1][outNode*nodeSize:(outNode+1)*nodeSize], d.Sum(sumBuf[:0])) // set top bits to 00 data[l+1][outNode*nodeSize+nodeSize-1] &= 0x3f @@ -61,8 +64,8 @@ func BuildTreeD(data io.Reader, outPath string, size abi.PaddedPieceSize) (cid.C // setup buffers maxThreads := int64(size) / threadChunkSize - if maxThreads > int64(runtime.NumCPU()) { - maxThreads = int64(runtime.NumCPU()) + if maxThreads > int64(runtime.NumCPU())*15/10 { + maxThreads = int64(runtime.NumCPU()) * 15 / 10 } if maxThreads < 1 { maxThreads = 1 @@ -79,11 +82,11 @@ func BuildTreeD(data io.Reader, outPath string, size abi.PaddedPieceSize) (cid.C if bottomBufSize > int64(size) { bottomBufSize = int64(size) } - workerBuffer[0] = make([]byte, bottomBufSize) + workerBuffer[0] = pool.Get(int(bottomBufSize)) // append levels until we get to a 32 byte level for len(workerBuffer[len(workerBuffer)-1]) > 32 { - newLevel := make([]byte, len(workerBuffer[len(workerBuffer)-1])/2) + newLevel := pool.Get(len(workerBuffer[len(workerBuffer)-1]) / 2) workerBuffer = append(workerBuffer, newLevel) } workerBuffers[i] = workerBuffer @@ -98,13 +101,25 @@ func BuildTreeD(data io.Reader, outPath string, size abi.PaddedPieceSize) (cid.C } apexBuf = make([][]byte, 1) - apexBuf[0] = make([]byte, apexBottomSize*nodeSize) + apexBuf[0] = pool.Get(int(apexBottomSize * nodeSize)) for len(apexBuf[len(apexBuf)-1]) > 32 { - newLevel := make([]byte, len(apexBuf[len(apexBuf)-1])/2) + newLevel := pool.Get(len(apexBuf[len(apexBuf)-1]) / 2) apexBuf = append(apexBuf, newLevel) } } + // defer free pool buffers + defer func() { + for _, workerBuffer := range workerBuffers { + for _, level := range workerBuffer { + pool.Put(level) + } + } + for _, level := range apexBuf { + pool.Put(level) + } + }() + // start processing var processed uint64 var workWg sync.WaitGroup diff --git a/provider/lpproof/treed_build_test.go b/provider/lpproof/treed_build_test.go index 5fd28bbd3..6c69c4ed1 100644 --- a/provider/lpproof/treed_build_test.go +++ b/provider/lpproof/treed_build_test.go @@ -5,10 +5,12 @@ import ( "crypto/rand" "fmt" "github.com/filecoin-project/go-state-types/abi" + pool "github.com/libp2p/go-buffer-pool" "github.com/stretchr/testify/require" "io/ioutil" "os" "path/filepath" + "runtime" "testing" ) @@ -273,6 +275,26 @@ func BenchmarkBuildTreeD512M(b *testing.B) { b.Fatalf("Failed to generate random data: %v", err) } + // preallocate NumCPU+1 1MiB/512k/256k/... + // with Pool.Get / Pool.Put, so that they are in the pool + { + nc := runtime.NumCPU() + bufs := [][]byte{} + for i := 0; i < nc+1; i++ { + for sz := 1 << 20; sz > 32; sz >>= 1 { + b := pool.Get(sz) + bufs = append(bufs, b) + } + } + for _, b := range bufs { + pool.Put(b) + } + } + + if b.N == 1 { + b.N = 10 + } + b.SetBytes(int64(dataSize)) // Set the number of bytes for the benchmark for i := 0; i < b.N; i++ { From 56240f50779488c7ed23ab08778cff1f2164bdb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sat, 23 Dec 2023 18:49:11 +0100 Subject: [PATCH 105/394] lpseal: Wire up TreeD to TreesTask --- provider/lpffi/sdr_funcs.go | 18 ++++++++++++++++++ provider/lpseal/task_sdr.go | 2 +- provider/lpseal/task_trees.go | 12 +++++++++++- storage/sealer/proofpaths/cachefiles.go | 3 ++- 4 files changed, 32 insertions(+), 3 deletions(-) diff --git a/provider/lpffi/sdr_funcs.go b/provider/lpffi/sdr_funcs.go index d5c2f03fb..955f88a33 100644 --- a/provider/lpffi/sdr_funcs.go +++ b/provider/lpffi/sdr_funcs.go @@ -7,14 +7,17 @@ import ( ffi "github.com/filecoin-project/filecoin-ffi" commcid "github.com/filecoin-project/go-fil-commcid" "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/lotus/provider/lpproof" "github.com/filecoin-project/lotus/storage/paths" "github.com/filecoin-project/lotus/storage/pipeline/lib/nullreader" + "github.com/filecoin-project/lotus/storage/sealer/proofpaths" "github.com/filecoin-project/lotus/storage/sealer/storiface" "github.com/ipfs/go-cid" logging "github.com/ipfs/go-log/v2" "golang.org/x/xerrors" "io" "os" + "path/filepath" ) var log = logging.Logger("lpffi") @@ -109,6 +112,21 @@ func (sb *SealCalls) GenerateSDR(ctx context.Context, sector storiface.SectorRef return nil } +func (sb *SealCalls) TreeD(ctx context.Context, sector storiface.SectorRef, size abi.PaddedPieceSize, data io.Reader) (cid.Cid, error) { + maybeUns := storiface.FTNone + // todo sectors with data + + paths, releaseSector, err := sb.sectors.AcquireSector(ctx, sector, maybeUns, storiface.FTCache, storiface.PathSealing) + if err != nil { + return cid.Undef, xerrors.Errorf("acquiring sector paths: %w", err) + } + defer releaseSector() + + log.Errorw("oest.idos.hbisor.bpisro.pisro.bpisro.bxsrobpyxsrbpoyxsrgbopyx treed", "paths", paths.Cache) + + return lpproof.BuildTreeD(data, filepath.Join(paths.Cache, proofpaths.TreeDName), size) +} + func (sb *SealCalls) TreeRC(ctx context.Context, sector storiface.SectorRef, unsealed cid.Cid) (cid.Cid, cid.Cid, error) { p1o, err := sb.makePhase1Out(unsealed, sector.ProofType) if err != nil { diff --git a/provider/lpseal/task_sdr.go b/provider/lpseal/task_sdr.go index 86efedafb..f5a92a7e1 100644 --- a/provider/lpseal/task_sdr.go +++ b/provider/lpseal/task_sdr.go @@ -123,7 +123,7 @@ func (s *SDRTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (done bo n, err := s.db.Exec(ctx, `UPDATE sectors_sdr_pipeline SET after_sdr = true, ticket_epoch = $3, ticket_value = $4 WHERE sp_id = $1 AND sector_number = $2`, - sectorParams.SpID, sectorParams.SectorNumber, ticketEpoch, ticket) + sectorParams.SpID, sectorParams.SectorNumber, ticketEpoch, []byte(ticket)) if err != nil { return false, xerrors.Errorf("store sdr success: updating pipeline: %w", err) } diff --git a/provider/lpseal/task_trees.go b/provider/lpseal/task_trees.go index 610f280f0..a0d27a6ec 100644 --- a/provider/lpseal/task_trees.go +++ b/provider/lpseal/task_trees.go @@ -8,6 +8,7 @@ import ( "github.com/filecoin-project/lotus/lib/harmony/harmonytask" "github.com/filecoin-project/lotus/lib/harmony/resources" "github.com/filecoin-project/lotus/provider/lpffi" + "github.com/filecoin-project/lotus/storage/pipeline/lib/nullreader" "github.com/filecoin-project/lotus/storage/sealer/storiface" "golang.org/x/xerrors" ) @@ -86,12 +87,21 @@ func (t *TreesTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (done ProofType: sectorParams.RegSealProof, } + // D + treeUnsealed, err := t.sc.TreeD(ctx, sref, abi.PaddedPieceSize(ssize), nullreader.NewNullReader(abi.PaddedPieceSize(ssize).Unpadded())) + if err != nil { + return false, xerrors.Errorf("computing tree d: %w", err) + } + + // R / C sealed, unsealed, err := t.sc.TreeRC(ctx, sref, commd) if err != nil { return false, xerrors.Errorf("computing tree r and c: %w", err) } - // todo tree d!! (?) + if unsealed != treeUnsealed { + return false, xerrors.Errorf("tree-d and tree-r/c unsealed CIDs disagree") + } n, err := t.db.Exec(ctx, `UPDATE sectors_sdr_pipeline SET after_tree_r = true, after_tree_c = true, after_tree_d = true, tree_r_cid = $1, tree_d_cid = $3 diff --git a/storage/sealer/proofpaths/cachefiles.go b/storage/sealer/proofpaths/cachefiles.go index 24b29e9f5..d624e0777 100644 --- a/storage/sealer/proofpaths/cachefiles.go +++ b/storage/sealer/proofpaths/cachefiles.go @@ -6,7 +6,8 @@ import ( "github.com/filecoin-project/go-state-types/abi" ) -var dataFilePrefix = "sc-02-data-" +const dataFilePrefix = "sc-02-data-" +const TreeDName = dataFilePrefix + "tree-d.dat" func LayerFileName(layer int) string { return fmt.Sprintf("%slayer-%d.dat", dataFilePrefix, layer) From a98e72507e7c6cacffac8dc047d405b6107d210a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sat, 23 Dec 2023 19:46:57 +0100 Subject: [PATCH 106/394] harmony: Use correct free memory --- lib/harmony/resources/resources.go | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/lib/harmony/resources/resources.go b/lib/harmony/resources/resources.go index b129496d8..31060084c 100644 --- a/lib/harmony/resources/resources.go +++ b/lib/harmony/resources/resources.go @@ -3,6 +3,7 @@ package resources import ( "bytes" "context" + "github.com/elastic/go-sysinfo" "os/exec" "regexp" "runtime" @@ -10,7 +11,6 @@ import ( "time" logging "github.com/ipfs/go-log/v2" - "github.com/pbnjay/memory" "golang.org/x/sys/unix" "golang.org/x/xerrors" @@ -122,9 +122,19 @@ func getResources() (res Resources, err error) { } } + h, err := sysinfo.Host() + if err != nil { + return Resources{}, err + } + + mem, err := h.Memory() + if err != nil { + return Resources{}, err + } + res = Resources{ Cpu: runtime.NumCPU(), - Ram: memory.FreeMemory(), + Ram: mem.Available, Gpu: getGPUDevices(), } From 1684f275b0061797e93b9f3176057cd9d787597d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sat, 23 Dec 2023 20:31:01 +0100 Subject: [PATCH 107/394] harmony: Use correct free memory --- provider/lpffi/sdr_funcs.go | 2 +- provider/lpseal/task_trees.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/provider/lpffi/sdr_funcs.go b/provider/lpffi/sdr_funcs.go index 955f88a33..7cd903d19 100644 --- a/provider/lpffi/sdr_funcs.go +++ b/provider/lpffi/sdr_funcs.go @@ -116,7 +116,7 @@ func (sb *SealCalls) TreeD(ctx context.Context, sector storiface.SectorRef, size maybeUns := storiface.FTNone // todo sectors with data - paths, releaseSector, err := sb.sectors.AcquireSector(ctx, sector, maybeUns, storiface.FTCache, storiface.PathSealing) + paths, releaseSector, err := sb.sectors.AcquireSector(ctx, sector, storiface.FTCache, maybeUns, storiface.PathSealing) if err != nil { return cid.Undef, xerrors.Errorf("acquiring sector paths: %w", err) } diff --git a/provider/lpseal/task_trees.go b/provider/lpseal/task_trees.go index a0d27a6ec..d764af3ef 100644 --- a/provider/lpseal/task_trees.go +++ b/provider/lpseal/task_trees.go @@ -88,7 +88,7 @@ func (t *TreesTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (done } // D - treeUnsealed, err := t.sc.TreeD(ctx, sref, abi.PaddedPieceSize(ssize), nullreader.NewNullReader(abi.PaddedPieceSize(ssize).Unpadded())) + treeUnsealed, err := t.sc.TreeD(ctx, sref, abi.PaddedPieceSize(ssize), nullreader.NewNullReader(abi.UnpaddedPieceSize(ssize))) if err != nil { return false, xerrors.Errorf("computing tree d: %w", err) } @@ -104,7 +104,7 @@ func (t *TreesTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (done } n, err := t.db.Exec(ctx, `UPDATE sectors_sdr_pipeline - SET after_tree_r = true, after_tree_c = true, after_tree_d = true, tree_r_cid = $1, tree_d_cid = $3 + SET after_tree_r = true, after_tree_c = true, after_tree_d = true, tree_r_cid = $3, tree_d_cid = $4 WHERE sp_id = $1 AND sector_number = $2`, sectorParams.SpID, sectorParams.SectorNumber, sealed, unsealed) if err != nil { From 84c2d46829fa7c880600702e290df8eaf478379c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sun, 24 Dec 2023 22:06:36 +0100 Subject: [PATCH 108/394] lpffi: Remove debug log from TreeD --- provider/lpffi/sdr_funcs.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/provider/lpffi/sdr_funcs.go b/provider/lpffi/sdr_funcs.go index 7cd903d19..178e39583 100644 --- a/provider/lpffi/sdr_funcs.go +++ b/provider/lpffi/sdr_funcs.go @@ -122,8 +122,6 @@ func (sb *SealCalls) TreeD(ctx context.Context, sector storiface.SectorRef, size } defer releaseSector() - log.Errorw("oest.idos.hbisor.bpisro.pisro.bpisro.bxsrobpyxsrbpoyxsrgbopyx treed", "paths", paths.Cache) - return lpproof.BuildTreeD(data, filepath.Join(paths.Cache, proofpaths.TreeDName), size) } From ff79ede52f6c8a6c3019a4756245e3589d6d2762 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 25 Dec 2023 18:35:25 +0100 Subject: [PATCH 109/394] lpseal: Submit precommit task --- provider/lpseal/poller.go | 15 ++- provider/lpseal/task_submit_precommit.go | 148 +++++++++++++++++++++++ 2 files changed, 161 insertions(+), 2 deletions(-) create mode 100644 provider/lpseal/task_submit_precommit.go diff --git a/provider/lpseal/poller.go b/provider/lpseal/poller.go index d3fae5990..2d2b035a1 100644 --- a/provider/lpseal/poller.go +++ b/provider/lpseal/poller.go @@ -15,6 +15,7 @@ var log = logging.Logger("lpseal") const ( pollerSDR = iota pollerTrees + pollerPrecommitMsg numPollers ) @@ -123,7 +124,7 @@ func (s *SealPoller) poll(ctx context.Context) error { if task.TaskTreeD == nil && task.TaskTreeC == nil && task.TaskTreeR == nil && s.pollers[pollerTrees].IsSet() && task.AfterSDR { s.pollers[pollerTrees].Val(ctx)(func(id harmonytask.TaskID, tx *harmonydb.Tx) (shouldCommit bool, seriousError error) { n, err := tx.Exec(`UPDATE sectors_sdr_pipeline SET task_id_tree_d = $1, task_id_tree_c = $1, task_id_tree_r = $1 - WHERE sp_id = $2 AND sector_number = $3 and task_id_tree_d is null and task_id_tree_c is null and task_id_tree_r is null`, id, task.SpID, task.SectorNumber) + WHERE sp_id = $2 AND sector_number = $3 and after_sdr = true and task_id_tree_d is null and task_id_tree_c is null and task_id_tree_r is null`, id, task.SpID, task.SectorNumber) if err != nil { return false, xerrors.Errorf("update sectors_sdr_pipeline: %w", err) } @@ -136,7 +137,17 @@ func (s *SealPoller) poll(ctx context.Context) error { } if task.TaskPrecommitMsg == nil && task.AfterTreeR && task.AfterTreeD { - // todo start precommit msg task + s.pollers[pollerPrecommitMsg].Val(ctx)(func(id harmonytask.TaskID, tx *harmonydb.Tx) (shouldCommit bool, seriousError error) { + n, err := tx.Exec(`UPDATE sectors_sdr_pipeline SET task_id_precommit_msg = $1 WHERE sp_id = $2 AND sector_number = $3 and task_id_precommit_msg is null and after_tree_r = true and after_tree_d = true`, id, task.SpID, task.SectorNumber) + if err != nil { + return false, xerrors.Errorf("update sectors_sdr_pipeline: %w", err) + } + if n != 1 { + return false, xerrors.Errorf("expected to update 1 row, updated %d", n) + } + + return true, nil + }) } if task.TaskPrecommitMsgWait == nil && task.AfterPrecommitMsg { diff --git a/provider/lpseal/task_submit_precommit.go b/provider/lpseal/task_submit_precommit.go new file mode 100644 index 000000000..cfd8a31fb --- /dev/null +++ b/provider/lpseal/task_submit_precommit.go @@ -0,0 +1,148 @@ +package lpseal + +import ( + "bytes" + "context" + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/go-state-types/big" + "github.com/filecoin-project/go-state-types/builtin" + miner12 "github.com/filecoin-project/go-state-types/builtin/v12/miner" + "github.com/filecoin-project/lotus/api" + "github.com/filecoin-project/lotus/chain/actors/builtin/miner" + "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/lib/harmony/harmonydb" + "github.com/filecoin-project/lotus/lib/harmony/harmonytask" + "github.com/filecoin-project/lotus/lib/harmony/resources" + "github.com/filecoin-project/lotus/provider/lpmessage" + "github.com/ipfs/go-cid" + "golang.org/x/xerrors" +) + +type SubmitPrecommitTaskApi interface { + StateMinerPreCommitDepositForPower(context.Context, address.Address, miner.SectorPreCommitInfo, types.TipSetKey) (big.Int, error) +} + +type SubmitPrecommitTask struct { + sp *SealPoller + db *harmonydb.DB + api SubmitPrecommitTaskApi + sender *lpmessage.Sender + + maxFee types.FIL +} + +func (s *SubmitPrecommitTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (done bool, err error) { + ctx := context.Background() + + var sectorParamsArr []struct { + SpID int64 `db:"sp_id"` + SectorNumber int64 `db:"sector_number"` + RegSealProof abi.RegisteredSealProof `db:"reg_seal_proof"` + TicketEpoch abi.ChainEpoch `db:"ticket_epoch"` + SealedCID string `db:"sealed_cid"` + UnsealedCID string `db:"unsealed_cid"` + } + + err = s.db.Select(ctx, §orParamsArr, ` + SELECT sp_id, sector_number, reg_seal_proof, ticket_epoch, tree_r_cid, tree_d_cid + FROM sectors_sdr_pipeline + WHERE task_id_precommit_msg = $1`, taskID) + if err != nil { + return false, xerrors.Errorf("getting sector params: %w", err) + } + + if len(sectorParamsArr) != 1 { + return false, xerrors.Errorf("expected 1 sector params, got %d", len(sectorParamsArr)) + } + sectorParams := sectorParamsArr[0] + + maddr, err := address.NewIDAddress(uint64(sectorParams.SpID)) + if err != nil { + return false, xerrors.Errorf("getting miner address: %w", err) + } + + sealedCID, err := cid.Parse(sectorParams.SealedCID) + if err != nil { + return false, xerrors.Errorf("parsing sealed CID: %w", err) + } + + unsealedCID, err := cid.Parse(sectorParams.UnsealedCID) + if err != nil { + return false, xerrors.Errorf("parsing unsealed CID: %w", err) + } + _ = unsealedCID + + params := miner.PreCommitSectorBatchParams2{} + + expiration := sectorParams.TicketEpoch + miner12.MaxSectorExpirationExtension + + params.Sectors = append(params.Sectors, miner.SectorPreCommitInfo{ + SealProof: sectorParams.RegSealProof, + SectorNumber: abi.SectorNumber(sectorParams.SectorNumber), + SealedCID: sealedCID, + SealRandEpoch: sectorParams.TicketEpoch, + DealIDs: nil, // todo + Expiration: expiration, + //UnsealedCid: unsealedCID, // todo with deals + }) + var pbuf bytes.Buffer + if err := params.MarshalCBOR(&pbuf); err != nil { + return false, xerrors.Errorf("serializing params: %w", err) + } + + collateral, err := s.api.StateMinerPreCommitDepositForPower(ctx, maddr, params.Sectors[0], types.EmptyTSK) + if err != nil { + return false, xerrors.Errorf("getting precommit deposit: %w", err) + } + + msg := &types.Message{ + To: maddr, + Method: builtin.MethodsMiner.PreCommitSectorBatch2, + Params: pbuf.Bytes(), + Value: collateral, // todo config for pulling from miner balance!! + } + + mss := &api.MessageSendSpec{ + MaxFee: abi.TokenAmount(s.maxFee), + } + + mcid, err := s.sender.Send(ctx, msg, mss, "precommit") + if err != nil { + return false, xerrors.Errorf("sending message: %w", err) + } + + // set precommit_msg_cid + _, err = s.db.Exec(ctx, `UPDATE sectors_sdr_pipeline + SET precommit_msg_cid = $1 + WHERE task_id_precommit_msg = $2`, mcid, taskID) + if err != nil { + return false, xerrors.Errorf("updating precommit_msg_cid: %w", err) + } + + return true, nil +} + +func (s *SubmitPrecommitTask) CanAccept(ids []harmonytask.TaskID, engine *harmonytask.TaskEngine) (*harmonytask.TaskID, error) { + id := ids[0] + return &id, nil +} + +func (s *SubmitPrecommitTask) TypeDetails() harmonytask.TaskTypeDetails { + return harmonytask.TaskTypeDetails{ + Max: 1024, + Name: "PreCommitSubmit", + Cost: resources.Resources{ + Cpu: 0, + Gpu: 0, + Ram: 1 << 20, + }, + MaxFailures: 16, + } +} + +func (s *SubmitPrecommitTask) Adder(taskFunc harmonytask.AddTaskFunc) { + s.sp.pollers[pollerPrecommitMsg].Set(taskFunc) +} + +var _ harmonytask.TaskInterface = &SubmitPrecommitTask{} From e743db5feee0e4c1c5dbe4f87281051f3535fcd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 26 Dec 2023 10:57:58 +0100 Subject: [PATCH 110/394] lpmessage: Scaffold waiter --- cmd/lotus-provider/run.go | 3 - .../harmonydb/sql/20231225-message-waits.sql | 13 ++ provider/lpmessage/watch.go | 145 ++++++++++++++++++ 3 files changed, 158 insertions(+), 3 deletions(-) create mode 100644 lib/harmony/harmonydb/sql/20231225-message-waits.sql create mode 100644 provider/lpmessage/watch.go diff --git a/cmd/lotus-provider/run.go b/cmd/lotus-provider/run.go index b1a4ff828..b5a286ba7 100644 --- a/cmd/lotus-provider/run.go +++ b/cmd/lotus-provider/run.go @@ -117,13 +117,10 @@ var runCmd = &cli.Command{ dependencies := &deps.Deps{} err = dependencies.PopulateRemainingDeps(ctx, cctx, true) if err != nil { - fmt.Println("err", err) return err } - fmt.Println("ef") taskEngine, err := tasks.StartTasks(ctx, dependencies) - fmt.Println("gh") if err != nil { return nil diff --git a/lib/harmony/harmonydb/sql/20231225-message-waits.sql b/lib/harmony/harmonydb/sql/20231225-message-waits.sql new file mode 100644 index 000000000..f3cfc1b6d --- /dev/null +++ b/lib/harmony/harmonydb/sql/20231225-message-waits.sql @@ -0,0 +1,13 @@ +create table message_waits ( + signed_message_cid text primary key references message_sends (signed_cid), + waiter_machine_id int references harmony_machines (id) on delete set null, + + executed_tsk_cid text, + executed_tsk_epoch bigint, + executed_msg_cid text, + executed_msg_data jsonb, + + executed_rcpt_exitcode bigint, + executed_rcpt_return bytea, + executed_rcpt_gas_used bigint +) diff --git a/provider/lpmessage/watch.go b/provider/lpmessage/watch.go new file mode 100644 index 000000000..3f18e5add --- /dev/null +++ b/provider/lpmessage/watch.go @@ -0,0 +1,145 @@ +package lpmessage + +import ( + "context" + "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/lib/harmony/harmonydb" + "github.com/filecoin-project/lotus/lib/harmony/harmonytask" + "github.com/filecoin-project/lotus/provider/chainsched" + "sync/atomic" +) + +/* +create table message_waits ( + + signed_message_cid text primary key references message_sends (signed_cid), + waiter_machine_id int references harmony_machines (id) on delete set null, + + executed_tsk_cid text, + executed_tsk_epoch bigint, + executed_msg_cid text, + executed_msg_data jsonb, + + executed_rcpt_exitcode bigint, + executed_rcpt_return bytea, + executed_rcpt_gas_used bigint + +) + +create table message_sends +( + + from_key text not null, + to_addr text not null, + send_reason text not null, + send_task_id bigint not null, + + unsigned_data bytea not null, + unsigned_cid text not null, + + nonce bigint, + signed_data bytea, + signed_json jsonb, + signed_cid text, + + send_time timestamp default null, + send_success boolean default null, + send_error text, + + constraint message_sends_pk + primary key (send_task_id, from_key) + +); +*/ +type MessageWaiter struct { + db *harmonydb.DB + ht *harmonytask.TaskEngine + + stopping, stopped chan struct{} + + updateCh chan struct{} + bestTs atomic.Pointer[types.TipSetKey] +} + +func NewMessageWaiter(db *harmonydb.DB, ht *harmonytask.TaskEngine, pcs *chainsched.ProviderChainSched) (*MessageWaiter, error) { + mw := &MessageWaiter{ + db: db, + ht: ht, + stopping: make(chan struct{}), + stopped: make(chan struct{}), + updateCh: make(chan struct{}), + } + mw.run() + if err := pcs.AddHandler(mw.processHeadChange); err != nil { + return nil, err + } + return mw, nil +} + +func (mw *MessageWaiter) run() { + defer close(mw.stopped) + + for { + select { + case <-mw.stopping: + // todo cleanup assignments + return + case <-mw.updateCh: + mw.update() + } + } +} + +func (mw *MessageWaiter) update() { + ctx := context.Background() + + tsk := *mw.bestTs.Load() + + machineID := mw.ht.ResourcesAvailable().MachineID + + // first if we see pending messages with null owner, assign them to ourselves + { + n, err := mw.db.Exec(ctx, `UPDATE message_waits SET owner_machine_id = $1 WHERE owner_machine_id IS NULL AND executed_tsk_cid IS NULL`, machineID) + if err != nil { + log.Errorf("failed to assign pending messages: %+v", err) + return + } + if n > 0 { + log.Debugw("assigned pending messages to ourselves", "assigned", n) + } + } + + // get messages assigned to us + var msgs []struct { + Cid string + Nonce uint64 + } + + // really large limit in case of things getting stuck and backlogging severely + err := mw.db.Select(ctx, &msgs, `SELECT signed_message_cid FROM message_waits WHERE owner_machine_id = $1 LIMIT 10000`, machineID) + if err != nil { + log.Errorf("failed to get assigned messages: %+v", err) + return + } +} + +func (mw *MessageWaiter) Stop(ctx context.Context) error { + close(mw.stopping) + select { + case <-mw.stopped: + case <-ctx.Done(): + return ctx.Err() + } + + return nil +} + +func (mw *MessageWaiter) processHeadChange(ctx context.Context, revert *types.TipSet, apply *types.TipSet) error { + best := apply.Key() + mw.bestTs.Store(&best) + select { + case mw.updateCh <- struct{}{}: + default: + } + return nil +} From 77bb8061dffdc0fbdf36414c6fd5ddf338081306 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 4 Jan 2024 12:58:44 +0100 Subject: [PATCH 111/394] lpmessage: in-progress message waiter --- provider/lpmessage/watch.go | 75 ++++++++++++++++++++++++++++++++++--- 1 file changed, 70 insertions(+), 5 deletions(-) diff --git a/provider/lpmessage/watch.go b/provider/lpmessage/watch.go index 3f18e5add..a97c76e48 100644 --- a/provider/lpmessage/watch.go +++ b/provider/lpmessage/watch.go @@ -2,13 +2,19 @@ package lpmessage import ( "context" + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/lib/harmony/harmonydb" "github.com/filecoin-project/lotus/lib/harmony/harmonytask" "github.com/filecoin-project/lotus/provider/chainsched" + "github.com/ipfs/go-cid" "sync/atomic" ) +const MinConfidence = 6 + /* create table message_waits ( @@ -51,9 +57,17 @@ create table message_sends ); */ +type MessageWaiterApi interface { + StateGetActor(ctx context.Context, actor address.Address, tsk types.TipSetKey) (*types.Actor, error) + ChainGetTipSetByHeight(context.Context, abi.ChainEpoch, types.TipSetKey) (*types.TipSet, error) + ChainGetTipSet(context.Context, types.TipSetKey) (*types.TipSet, error) + StateSearchMsg(ctx context.Context, from types.TipSetKey, msg cid.Cid, limit abi.ChainEpoch, allowReplaced bool) (*api.MsgLookup, error) +} + type MessageWaiter struct { - db *harmonydb.DB - ht *harmonytask.TaskEngine + db *harmonydb.DB + ht *harmonytask.TaskEngine + api MessageWaiterApi stopping, stopped chan struct{} @@ -65,6 +79,7 @@ func NewMessageWaiter(db *harmonydb.DB, ht *harmonytask.TaskEngine, pcs *chainsc mw := &MessageWaiter{ db: db, ht: ht, + api: api, stopping: make(chan struct{}), stopped: make(chan struct{}), updateCh: make(chan struct{}), @@ -95,6 +110,19 @@ func (mw *MessageWaiter) update() { tsk := *mw.bestTs.Load() + ts, err := mw.api.ChainGetTipSet(ctx, tsk) + if err != nil { + log.Errorf("failed to get tipset: %+v", err) + return + } + + lbts, err := mw.api.ChainGetTipSetByHeight(ctx, ts.Height()-MinConfidence, tsk) + if err != nil { + log.Errorf("failed to get tipset: %+v", err) + return + } + lbtsk := lbts.Key() + machineID := mw.ht.ResourcesAvailable().MachineID // first if we see pending messages with null owner, assign them to ourselves @@ -111,16 +139,53 @@ func (mw *MessageWaiter) update() { // get messages assigned to us var msgs []struct { - Cid string - Nonce uint64 + Cid string `db:"signed_message_cid"` + From string `db:"from_key"` + Nonce uint64 `db:"nonce"` + + FromAddr address.Address `db:"-"` } // really large limit in case of things getting stuck and backlogging severely - err := mw.db.Select(ctx, &msgs, `SELECT signed_message_cid FROM message_waits WHERE owner_machine_id = $1 LIMIT 10000`, machineID) + err = mw.db.Select(ctx, &msgs, `SELECT signed_message_cid, from_key, nonce FROM message_wait + JOIN message_sends ON signed_message_cid = signed_cid + WHERE owner_machine_id = $1 LIMIT 10000`, machineID) if err != nil { log.Errorf("failed to get assigned messages: %+v", err) return } + + // get address/nonce set to check + toCheck := make(map[address.Address]uint64) + + for i := range msgs { + msgs[i].FromAddr, err = address.NewFromString(msgs[i].From) + if err != nil { + log.Errorf("failed to parse from address: %+v", err) + return + } + toCheck[msgs[i].FromAddr] = 0 + } + + // get the nonce for each address + for addr := range toCheck { + act, err := mw.api.StateGetActor(ctx, addr, lbtsk) + if err != nil { + log.Errorf("failed to get actor: %+v", err) + return + } + + toCheck[addr] = act.Nonce + } + + // check if any of the messages we have assigned to us are now on chain, and have been for MinConfidence epochs + for _, msg := range msgs { + look, err := mw.api.StateSearchMsg(ctx, lbtsk, cid.MustParse(msg.Cid), api.LookbackNoLimit, false) + if err != nil { + log.Errorf("failed to search for message: %+v", err) + return + } + } } func (mw *MessageWaiter) Stop(ctx context.Context) error { From 2c7041b1672f6df4134b1ce0a3ec35f287f28e6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 4 Jan 2024 15:40:31 +0100 Subject: [PATCH 112/394] lpmessage: Finish basic message waiter --- provider/lpmessage/watch.go | 91 +++++++++++++++++++------------------ 1 file changed, 46 insertions(+), 45 deletions(-) diff --git a/provider/lpmessage/watch.go b/provider/lpmessage/watch.go index a97c76e48..5f98a9a0c 100644 --- a/provider/lpmessage/watch.go +++ b/provider/lpmessage/watch.go @@ -2,6 +2,7 @@ package lpmessage import ( "context" + "encoding/json" "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/lotus/api" @@ -15,53 +16,12 @@ import ( const MinConfidence = 6 -/* -create table message_waits ( - - signed_message_cid text primary key references message_sends (signed_cid), - waiter_machine_id int references harmony_machines (id) on delete set null, - - executed_tsk_cid text, - executed_tsk_epoch bigint, - executed_msg_cid text, - executed_msg_data jsonb, - - executed_rcpt_exitcode bigint, - executed_rcpt_return bytea, - executed_rcpt_gas_used bigint - -) - -create table message_sends -( - - from_key text not null, - to_addr text not null, - send_reason text not null, - send_task_id bigint not null, - - unsigned_data bytea not null, - unsigned_cid text not null, - - nonce bigint, - signed_data bytea, - signed_json jsonb, - signed_cid text, - - send_time timestamp default null, - send_success boolean default null, - send_error text, - - constraint message_sends_pk - primary key (send_task_id, from_key) - -); -*/ type MessageWaiterApi interface { StateGetActor(ctx context.Context, actor address.Address, tsk types.TipSetKey) (*types.Actor, error) ChainGetTipSetByHeight(context.Context, abi.ChainEpoch, types.TipSetKey) (*types.TipSet, error) ChainGetTipSet(context.Context, types.TipSetKey) (*types.TipSet, error) StateSearchMsg(ctx context.Context, from types.TipSetKey, msg cid.Cid, limit abi.ChainEpoch, allowReplaced bool) (*api.MsgLookup, error) + ChainGetMessage(ctx context.Context, mc cid.Cid) (*types.Message, error) } type MessageWaiter struct { @@ -75,7 +35,7 @@ type MessageWaiter struct { bestTs atomic.Pointer[types.TipSetKey] } -func NewMessageWaiter(db *harmonydb.DB, ht *harmonytask.TaskEngine, pcs *chainsched.ProviderChainSched) (*MessageWaiter, error) { +func NewMessageWaiter(db *harmonydb.DB, ht *harmonytask.TaskEngine, pcs *chainsched.ProviderChainSched, api MessageWaiterApi) (*MessageWaiter, error) { mw := &MessageWaiter{ db: db, ht: ht, @@ -127,7 +87,7 @@ func (mw *MessageWaiter) update() { // first if we see pending messages with null owner, assign them to ourselves { - n, err := mw.db.Exec(ctx, `UPDATE message_waits SET owner_machine_id = $1 WHERE owner_machine_id IS NULL AND executed_tsk_cid IS NULL`, machineID) + n, err := mw.db.Exec(ctx, `UPDATE message_waits SET waiter_machine_id = $1 WHERE waiter_machine_id IS NULL AND executed_tsk_cid IS NULL`, machineID) if err != nil { log.Errorf("failed to assign pending messages: %+v", err) return @@ -149,7 +109,7 @@ func (mw *MessageWaiter) update() { // really large limit in case of things getting stuck and backlogging severely err = mw.db.Select(ctx, &msgs, `SELECT signed_message_cid, from_key, nonce FROM message_wait JOIN message_sends ON signed_message_cid = signed_cid - WHERE owner_machine_id = $1 LIMIT 10000`, machineID) + WHERE waiter_machine_id = $1 LIMIT 10000`, machineID) if err != nil { log.Errorf("failed to get assigned messages: %+v", err) return @@ -180,11 +140,52 @@ func (mw *MessageWaiter) update() { // check if any of the messages we have assigned to us are now on chain, and have been for MinConfidence epochs for _, msg := range msgs { + if msg.Nonce > toCheck[msg.FromAddr] { + continue // definitely not on chain yet + } + look, err := mw.api.StateSearchMsg(ctx, lbtsk, cid.MustParse(msg.Cid), api.LookbackNoLimit, false) if err != nil { log.Errorf("failed to search for message: %+v", err) return } + + if look == nil { + continue // not on chain yet (or not executed yet) + } + + tskCid, err := look.TipSet.Cid() + if err != nil { + log.Errorf("failed to get tipset cid: %+v", err) + return + } + + emsg, err := mw.api.ChainGetMessage(ctx, look.Message) + if err != nil { + log.Errorf("failed to get message: %+v", err) + return + } + + execMsg, err := json.Marshal(emsg) + if err != nil { + log.Errorf("failed to marshal message: %+v", err) + return + } + + // record in db + _, err = mw.db.Exec(ctx, `UPDATE message_waits SET + waiter_machine_id = NULL, + executed_tsk_cid = $1, executed_tsk_epoch = $2, + executed_msg_cid = $3, executed_msg_data = $4, + executed_rcpt_exitcode = $5, executed_rcpt_return = $6, executed_rcpt_gas_used = $7 + WHERE signed_message_cid = $8`, tskCid, look.Height, + look.Message, execMsg, + look.Receipt.ExitCode, look.Receipt.Return, look.Receipt.GasUsed, + msg.Cid) + if err != nil { + log.Errorf("failed to update message wait: %+v", err) + return + } } } From 2f45646a92f3f3d903809e7d994b094443c6ddf1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 4 Jan 2024 16:57:31 +0100 Subject: [PATCH 113/394] lpseal: write precommit message to message_waits --- lib/harmony/harmonydb/sql/20231217-sdr-pipeline.sql | 2 -- provider/lpseal/task_submit_precommit.go | 5 +++++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/harmony/harmonydb/sql/20231217-sdr-pipeline.sql b/lib/harmony/harmonydb/sql/20231217-sdr-pipeline.sql index c0540148f..c30a17e7c 100644 --- a/lib/harmony/harmonydb/sql/20231217-sdr-pipeline.sql +++ b/lib/harmony/harmonydb/sql/20231217-sdr-pipeline.sql @@ -41,8 +41,6 @@ create table sectors_sdr_pipeline ( -- precommit message wait seed_epoch bigint, precommit_msg_tsk bytea, - - task_id_precommit_msg_wait bigint, after_precommit_msg_success bool not null default false, -- seed diff --git a/provider/lpseal/task_submit_precommit.go b/provider/lpseal/task_submit_precommit.go index cfd8a31fb..198d53e43 100644 --- a/provider/lpseal/task_submit_precommit.go +++ b/provider/lpseal/task_submit_precommit.go @@ -120,6 +120,11 @@ func (s *SubmitPrecommitTask) Do(taskID harmonytask.TaskID, stillOwned func() bo return false, xerrors.Errorf("updating precommit_msg_cid: %w", err) } + _, err = s.db.Exec(ctx, `INSERT INTO message_waits (signed_message_cid) VALUES ($1)`, mcid) + if err != nil { + return false, xerrors.Errorf("inserting into message_waits: %w", err) + } + return true, nil } From 0244c7df50e5db05f5118a5c7d833072b35c18fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 5 Jan 2024 16:10:34 +0100 Subject: [PATCH 114/394] lpseal: Wire up SubmitPrecommit --- cmd/lotus-provider/proving.go | 2 +- cmd/lotus-provider/tasks/tasks.go | 30 +++++++++++++++++-- .../harmonydb/sql/20231225-message-waits.sql | 2 +- node/config/types.go | 4 +++ provider/builder.go | 6 +--- provider/lpmessage/watch.go | 14 ++++----- provider/lpseal/poller.go | 11 ++++--- provider/lpseal/task_submit_precommit.go | 11 +++++++ provider/lpwindow/compute_task.go | 6 ++-- provider/lpwindow/recover_task.go | 6 ++-- provider/lpwindow/submit_task.go | 6 ++-- 11 files changed, 71 insertions(+), 27 deletions(-) diff --git a/cmd/lotus-provider/proving.go b/cmd/lotus-provider/proving.go index eaef45db7..cd955011b 100644 --- a/cmd/lotus-provider/proving.go +++ b/cmd/lotus-provider/proving.go @@ -172,7 +172,7 @@ It will not send any messages to the chain. Since it can compute any deadline, o return err } - wdPostTask, wdPoStSubmitTask, derlareRecoverTask, err := provider.WindowPostScheduler(ctx, deps.Cfg.Fees, deps.Cfg.Proving, deps.Full, deps.Verif, deps.LW, nil, + wdPostTask, wdPoStSubmitTask, derlareRecoverTask, err := provider.WindowPostScheduler(ctx, deps.Cfg.Fees, deps.Cfg.Proving, deps.Full, deps.Verif, deps.LW, nil, nil, deps.As, deps.Maddrs, deps.DB, deps.Stor, deps.Si, deps.Cfg.Subsystems.WindowPostMaxTasks) if err != nil { return err diff --git a/cmd/lotus-provider/tasks/tasks.go b/cmd/lotus-provider/tasks/tasks.go index b81814423..1bf96431c 100644 --- a/cmd/lotus-provider/tasks/tasks.go +++ b/cmd/lotus-provider/tasks/tasks.go @@ -3,6 +3,7 @@ package tasks import ( "context" + "github.com/filecoin-project/lotus/provider/chainsched" "github.com/filecoin-project/lotus/provider/lpffi" "github.com/filecoin-project/lotus/provider/lpseal" @@ -34,6 +35,8 @@ func StartTasks(ctx context.Context, dependencies *deps.Deps) (*harmonytask.Task sender, sendTask := lpmessage.NewSender(full, full, db) activeTasks = append(activeTasks, sendTask) + chainSched := chainsched.New(full) + /////////////////////////////////////////////////////////////////////// ///// Task Selection /////////////////////////////////////////////////////////////////////// @@ -42,7 +45,7 @@ func StartTasks(ctx context.Context, dependencies *deps.Deps) (*harmonytask.Task if cfg.Subsystems.EnableWindowPost { wdPostTask, wdPoStSubmitTask, derlareRecoverTask, err := provider.WindowPostScheduler(ctx, cfg.Fees, cfg.Proving, full, verif, lw, sender, - as, maddrs, db, stor, si, cfg.Subsystems.WindowPostMaxTasks) + chainSched, as, maddrs, db, stor, si, cfg.Subsystems.WindowPostMaxTasks) if err != nil { return nil, err } @@ -55,9 +58,9 @@ func StartTasks(ctx context.Context, dependencies *deps.Deps) (*harmonytask.Task } } + hasAnySealingTask := cfg.Subsystems.EnableSealSDR { // Sealing - hasAnySealingTask := cfg.Subsystems.EnableSealSDR var sp *lpseal.SealPoller var slr *lpffi.SealCalls @@ -76,10 +79,31 @@ func StartTasks(ctx context.Context, dependencies *deps.Deps) (*harmonytask.Task treesTask := lpseal.NewTreesTask(sp, db, slr, cfg.Subsystems.SealSDRTreesMaxTasks) activeTasks = append(activeTasks, treesTask) } + if cfg.Subsystems.EnableSendPrecommitMsg { + precommitTask := lpseal.NewSubmitPrecommitTask(sp, db, full, sender, cfg.Fees.MaxPreCommitGasFee) + activeTasks = append(activeTasks, precommitTask) + } } log.Infow("This lotus_provider instance handles", "miner_addresses", maddrs, "tasks", lo.Map(activeTasks, func(t harmonytask.TaskInterface, _ int) string { return t.TypeDetails().Name })) - return harmonytask.New(db, activeTasks, dependencies.ListenAddr) + ht, err := harmonytask.New(db, activeTasks, dependencies.ListenAddr) + if err != nil { + return nil, err + } + + if hasAnySealingTask { + watcher, err := lpmessage.NewMessageWatcher(db, ht, chainSched, full) + if err != nil { + return nil, err + } + _ = watcher + } + + if cfg.Subsystems.EnableWindowPost || hasAnySealingTask { + go chainSched.Run(ctx) + } + + return ht, nil } diff --git a/lib/harmony/harmonydb/sql/20231225-message-waits.sql b/lib/harmony/harmonydb/sql/20231225-message-waits.sql index f3cfc1b6d..4143f3a56 100644 --- a/lib/harmony/harmonydb/sql/20231225-message-waits.sql +++ b/lib/harmony/harmonydb/sql/20231225-message-waits.sql @@ -1,5 +1,5 @@ create table message_waits ( - signed_message_cid text primary key references message_sends (signed_cid), + signed_message_cid text primary key, waiter_machine_id int references harmony_machines (id) on delete set null, executed_tsk_cid text, diff --git a/node/config/types.go b/node/config/types.go index e91d1dcf5..e2924de89 100644 --- a/node/config/types.go +++ b/node/config/types.go @@ -114,6 +114,10 @@ type ProviderSubsystemsConfig struct { // In lotus-miner this was run as part of PreCommit2 (TreeD was run in PreCommit1). EnableSealSDRTrees bool SealSDRTreesMaxTasks int + + // EnableSendPrecommitMsg enables the sending of precommit messages to the chain + // from this lotus-provider instance. + EnableSendPrecommitMsg bool } type DAGStoreConfig struct { diff --git a/provider/builder.go b/provider/builder.go index cff387970..d49333965 100644 --- a/provider/builder.go +++ b/provider/builder.go @@ -20,12 +20,10 @@ import ( //var log = logging.Logger("provider") func WindowPostScheduler(ctx context.Context, fc config.LotusProviderFees, pc config.ProvingConfig, - api api.FullNode, verif storiface.Verifier, lw *sealer.LocalWorker, sender *lpmessage.Sender, + api api.FullNode, verif storiface.Verifier, lw *sealer.LocalWorker, sender *lpmessage.Sender, chainSched *chainsched.ProviderChainSched, as *multictladdr.MultiAddressSelector, addresses map[dtypes.MinerAddress]bool, db *harmonydb.DB, stor paths.Store, idx paths.SectorIndex, max int) (*lpwindow.WdPostTask, *lpwindow.WdPostSubmitTask, *lpwindow.WdPostRecoverDeclareTask, error) { - chainSched := chainsched.New(api) - // todo config ft := lpwindow.NewSimpleFaultTracker(stor, idx, 32, 5*time.Second, 300*time.Second) @@ -44,7 +42,5 @@ func WindowPostScheduler(ctx context.Context, fc config.LotusProviderFees, pc co return nil, nil, nil, err } - go chainSched.Run(ctx) - return computeTask, submitTask, recoverTask, nil } diff --git a/provider/lpmessage/watch.go b/provider/lpmessage/watch.go index 5f98a9a0c..ee8d33237 100644 --- a/provider/lpmessage/watch.go +++ b/provider/lpmessage/watch.go @@ -24,7 +24,7 @@ type MessageWaiterApi interface { ChainGetMessage(ctx context.Context, mc cid.Cid) (*types.Message, error) } -type MessageWaiter struct { +type MessageWatcher struct { db *harmonydb.DB ht *harmonytask.TaskEngine api MessageWaiterApi @@ -35,8 +35,8 @@ type MessageWaiter struct { bestTs atomic.Pointer[types.TipSetKey] } -func NewMessageWaiter(db *harmonydb.DB, ht *harmonytask.TaskEngine, pcs *chainsched.ProviderChainSched, api MessageWaiterApi) (*MessageWaiter, error) { - mw := &MessageWaiter{ +func NewMessageWatcher(db *harmonydb.DB, ht *harmonytask.TaskEngine, pcs *chainsched.ProviderChainSched, api MessageWaiterApi) (*MessageWatcher, error) { + mw := &MessageWatcher{ db: db, ht: ht, api: api, @@ -51,7 +51,7 @@ func NewMessageWaiter(db *harmonydb.DB, ht *harmonytask.TaskEngine, pcs *chainsc return mw, nil } -func (mw *MessageWaiter) run() { +func (mw *MessageWatcher) run() { defer close(mw.stopped) for { @@ -65,7 +65,7 @@ func (mw *MessageWaiter) run() { } } -func (mw *MessageWaiter) update() { +func (mw *MessageWatcher) update() { ctx := context.Background() tsk := *mw.bestTs.Load() @@ -189,7 +189,7 @@ func (mw *MessageWaiter) update() { } } -func (mw *MessageWaiter) Stop(ctx context.Context) error { +func (mw *MessageWatcher) Stop(ctx context.Context) error { close(mw.stopping) select { case <-mw.stopped: @@ -200,7 +200,7 @@ func (mw *MessageWaiter) Stop(ctx context.Context) error { return nil } -func (mw *MessageWaiter) processHeadChange(ctx context.Context, revert *types.TipSet, apply *types.TipSet) error { +func (mw *MessageWatcher) processHeadChange(ctx context.Context, revert *types.TipSet, apply *types.TipSet) error { best := apply.Key() mw.bestTs.Store(&best) select { diff --git a/provider/lpseal/poller.go b/provider/lpseal/poller.go index 2d2b035a1..f1ba84728 100644 --- a/provider/lpseal/poller.go +++ b/provider/lpseal/poller.go @@ -70,8 +70,7 @@ func (s *SealPoller) poll(ctx context.Context) error { TaskPrecommitMsg *int64 `db:"task_id_precommit_msg"` AfterPrecommitMsg bool `db:"after_precommit_msg"` - TaskPrecommitMsgWait *int64 `db:"task_id_precommit_msg_wait"` - AfterPrecommitMsgSuccess bool `db:"after_precommit_msg_success"` + AfterPrecommitMsgSuccess bool `db:"after_precommit_msg_success"` TaskPoRep *int64 `db:"task_id_porep"` PoRepProof []byte `db:"porep_proof"` @@ -93,7 +92,7 @@ func (s *SealPoller) poll(ctx context.Context) error { task_id_tree_c, after_tree_c, task_id_tree_r, after_tree_r, task_id_precommit_msg, after_precommit_msg, - task_id_precommit_msg_wait, after_precommit_msg_success, + after_precommit_msg_success, task_id_porep, porep_proof, task_id_commit_msg, after_commit_msg, task_id_commit_msg_wait, after_commit_msg_success, @@ -150,7 +149,11 @@ func (s *SealPoller) poll(ctx context.Context) error { }) } - if task.TaskPrecommitMsgWait == nil && task.AfterPrecommitMsg { + if task.TaskPrecommitMsg != nil && !task.AfterPrecommitMsg { + + } + + if task.AfterPrecommitMsg { // todo start precommit msg wait task } diff --git a/provider/lpseal/task_submit_precommit.go b/provider/lpseal/task_submit_precommit.go index 198d53e43..6b926f8f5 100644 --- a/provider/lpseal/task_submit_precommit.go +++ b/provider/lpseal/task_submit_precommit.go @@ -32,6 +32,17 @@ type SubmitPrecommitTask struct { maxFee types.FIL } +func NewSubmitPrecommitTask(sp *SealPoller, db *harmonydb.DB, api SubmitPrecommitTaskApi, sender *lpmessage.Sender, maxFee types.FIL) *SubmitPrecommitTask { + return &SubmitPrecommitTask{ + sp: sp, + db: db, + api: api, + sender: sender, + + maxFee: maxFee, + } +} + func (s *SubmitPrecommitTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (done bool, err error) { ctx := context.Background() diff --git a/provider/lpwindow/compute_task.go b/provider/lpwindow/compute_task.go index 9fc4afe4f..08f99e3e9 100644 --- a/provider/lpwindow/compute_task.go +++ b/provider/lpwindow/compute_task.go @@ -102,8 +102,10 @@ func NewWdPostTask(db *harmonydb.DB, max: max, } - if err := pcs.AddHandler(t.processHeadChange); err != nil { - return nil, err + if pcs != nil { + if err := pcs.AddHandler(t.processHeadChange); err != nil { + return nil, err + } } return t, nil diff --git a/provider/lpwindow/recover_task.go b/provider/lpwindow/recover_task.go index 076ed51c1..27c501dde 100644 --- a/provider/lpwindow/recover_task.go +++ b/provider/lpwindow/recover_task.go @@ -77,8 +77,10 @@ func NewWdPostRecoverDeclareTask(sender *lpmessage.Sender, actors: actors, } - if err := pcs.AddHandler(t.processHeadChange); err != nil { - return nil, err + if pcs != nil { + if err := pcs.AddHandler(t.processHeadChange); err != nil { + return nil, err + } } return t, nil diff --git a/provider/lpwindow/submit_task.go b/provider/lpwindow/submit_task.go index d6937354b..8d39c40ab 100644 --- a/provider/lpwindow/submit_task.go +++ b/provider/lpwindow/submit_task.go @@ -62,8 +62,10 @@ func NewWdPostSubmitTask(pcs *chainsched.ProviderChainSched, send *lpmessage.Sen as: as, } - if err := pcs.AddHandler(res.processHeadChange); err != nil { - return nil, err + if pcs != nil { + if err := pcs.AddHandler(res.processHeadChange); err != nil { + return nil, err + } } return res, nil From 725460bc450bb74225a8d6f649595537c9ce2c17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 5 Jan 2024 16:49:56 +0100 Subject: [PATCH 115/394] dbindex: Fix heartbeat checks --- storage/paths/db_index.go | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/storage/paths/db_index.go b/storage/paths/db_index.go index 018ea84fd..32afdea1e 100644 --- a/storage/paths/db_index.go +++ b/storage/paths/db_index.go @@ -187,7 +187,7 @@ retryAttachStorage: var urls sql.NullString var storageId sql.NullString err = tx.QueryRow( - "Select storage_id, urls FROM storage_path WHERE storage_id = $1", string(si.ID)).Scan(&storageId, &urls) + "SELECT storage_id, urls FROM storage_path WHERE storage_id = $1", string(si.ID)).Scan(&storageId, &urls) if err != nil && !strings.Contains(err.Error(), "no rows in result set") { return false, xerrors.Errorf("storage attach select fails: %v", err) } @@ -202,7 +202,7 @@ retryAttachStorage: currUrls = union(currUrls, si.URLs) _, err = tx.Exec( - "UPDATE storage_path set urls=$1, weight=$2, max_storage=$3, can_seal=$4, can_store=$5, groups=$6, allow_to=$7, allow_types=$8, deny_types=$9 WHERE storage_id=$10", + "UPDATE storage_path set urls=$1, weight=$2, max_storage=$3, can_seal=$4, can_store=$5, groups=$6, allow_to=$7, allow_types=$8, deny_types=$9, last_heartbeat=NOW() WHERE storage_id=$10", strings.Join(currUrls, ","), si.Weight, si.MaxStorage, @@ -223,7 +223,7 @@ retryAttachStorage: // Insert storage id _, err = tx.Exec( "INSERT INTO storage_path "+ - "Values($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16)", + "Values($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, NOW())", si.ID, strings.Join(si.URLs, ","), si.Weight, @@ -238,8 +238,7 @@ retryAttachStorage: st.Available, st.FSAvailable, st.Reserved, - st.Used, - time.Now()) + st.Used) if err != nil { return false, xerrors.Errorf("StorageAttach insert fails: %v", err) } @@ -331,13 +330,12 @@ func (dbi *DBIndex) StorageReportHealth(ctx context.Context, id storiface.ID, re } _, err = dbi.harmonyDB.Exec(ctx, - "UPDATE storage_path set capacity=$1, available=$2, fs_available=$3, reserved=$4, used=$5, last_heartbeat=$6", + "UPDATE storage_path set capacity=$1, available=$2, fs_available=$3, reserved=$4, used=$5, last_heartbeat=NOW()", report.Stat.Capacity, report.Stat.Available, report.Stat.FSAvailable, report.Stat.Reserved, - report.Stat.Used, - time.Now().UTC()) + report.Stat.Used) if err != nil { return xerrors.Errorf("updating storage health in DB fails with err: %v", err) } @@ -574,9 +572,9 @@ func (dbi *DBIndex) StorageFindSector(ctx context.Context, s abi.SectorID, ft st FROM storage_path WHERE can_seal=true and available >= $1 - and NOW()-last_heartbeat < $2 + and NOW()-($2 * INTERVAL '1 second') < last_heartbeat and heartbeat_err is null`, - spaceReq, SkippedHeartbeatThresh) + spaceReq, SkippedHeartbeatThresh.Seconds()) if err != nil { return nil, xerrors.Errorf("Selecting allowfetch storage paths from DB fails err: %v", err) } @@ -713,12 +711,12 @@ func (dbi *DBIndex) StorageBestAlloc(ctx context.Context, allocate storiface.Sec deny_types FROM storage_path WHERE available >= $1 - and NOW()-last_heartbeat < $2 + and NOW()-($2 * INTERVAL '1 second') < last_heartbeat and heartbeat_err is null - and ($3 and can_seal = TRUE or $4 and can_store = TRUE) + and (($3 and can_seal = TRUE) or ($4 and can_store = TRUE)) order by (available::numeric * weight) desc`, spaceReq, - SkippedHeartbeatThresh, + SkippedHeartbeatThresh.Seconds(), pathType == storiface.PathSealing, pathType == storiface.PathStorage, ) From 9560c3efaff250ae3bc0053201c373f5b03ffb0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 5 Jan 2024 16:50:24 +0100 Subject: [PATCH 116/394] storage config: Expand paths --- node/config/storage.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/node/config/storage.go b/node/config/storage.go index ac5d57de8..8494fb71a 100644 --- a/node/config/storage.go +++ b/node/config/storage.go @@ -3,6 +3,7 @@ package config import ( "encoding/json" "errors" + "github.com/mitchellh/go-homedir" "io" "io/fs" "os" @@ -14,6 +15,11 @@ import ( ) func StorageFromFile(path string, def *storiface.StorageConfig) (*storiface.StorageConfig, error) { + path, err := homedir.Expand(path) + if err != nil { + return nil, xerrors.Errorf("expanding storage config path: %w", err) + } + file, err := os.Open(path) switch { case os.IsNotExist(err): @@ -40,6 +46,11 @@ func StorageFromReader(reader io.Reader) (*storiface.StorageConfig, error) { } func WriteStorageFile(filePath string, config storiface.StorageConfig) error { + filePath, err := homedir.Expand(filePath) + if err != nil { + return xerrors.Errorf("expanding storage config path: %w", err) + } + b, err := json.MarshalIndent(config, "", " ") if err != nil { return xerrors.Errorf("marshaling storage config: %w", err) From dd6a0095b0939fc261747db27511714b54f6bf72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 5 Jan 2024 16:58:32 +0100 Subject: [PATCH 117/394] lpseal: Make precommit sending work --- cmd/lotus-provider/tasks/tasks.go | 2 +- provider/lpseal/task_submit_precommit.go | 23 ++++++++++++++++++++--- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/cmd/lotus-provider/tasks/tasks.go b/cmd/lotus-provider/tasks/tasks.go index 1bf96431c..b12f9691a 100644 --- a/cmd/lotus-provider/tasks/tasks.go +++ b/cmd/lotus-provider/tasks/tasks.go @@ -80,7 +80,7 @@ func StartTasks(ctx context.Context, dependencies *deps.Deps) (*harmonytask.Task activeTasks = append(activeTasks, treesTask) } if cfg.Subsystems.EnableSendPrecommitMsg { - precommitTask := lpseal.NewSubmitPrecommitTask(sp, db, full, sender, cfg.Fees.MaxPreCommitGasFee) + precommitTask := lpseal.NewSubmitPrecommitTask(sp, db, full, sender, as, cfg.Fees.MaxPreCommitGasFee) activeTasks = append(activeTasks, precommitTask) } } diff --git a/provider/lpseal/task_submit_precommit.go b/provider/lpseal/task_submit_precommit.go index 6b926f8f5..0adc9f5ab 100644 --- a/provider/lpseal/task_submit_precommit.go +++ b/provider/lpseal/task_submit_precommit.go @@ -15,12 +15,16 @@ import ( "github.com/filecoin-project/lotus/lib/harmony/harmonytask" "github.com/filecoin-project/lotus/lib/harmony/resources" "github.com/filecoin-project/lotus/provider/lpmessage" + "github.com/filecoin-project/lotus/storage/ctladdr" + sealing "github.com/filecoin-project/lotus/storage/pipeline" "github.com/ipfs/go-cid" "golang.org/x/xerrors" ) type SubmitPrecommitTaskApi interface { StateMinerPreCommitDepositForPower(context.Context, address.Address, miner.SectorPreCommitInfo, types.TipSetKey) (big.Int, error) + StateMinerInfo(context.Context, address.Address, types.TipSetKey) (api.MinerInfo, error) + ctladdr.NodeApi } type SubmitPrecommitTask struct { @@ -28,16 +32,18 @@ type SubmitPrecommitTask struct { db *harmonydb.DB api SubmitPrecommitTaskApi sender *lpmessage.Sender + as sealing.AddressSelector maxFee types.FIL } -func NewSubmitPrecommitTask(sp *SealPoller, db *harmonydb.DB, api SubmitPrecommitTaskApi, sender *lpmessage.Sender, maxFee types.FIL) *SubmitPrecommitTask { +func NewSubmitPrecommitTask(sp *SealPoller, db *harmonydb.DB, api SubmitPrecommitTaskApi, sender *lpmessage.Sender, as sealing.AddressSelector, maxFee types.FIL) *SubmitPrecommitTask { return &SubmitPrecommitTask{ sp: sp, db: db, api: api, sender: sender, + as: as, maxFee: maxFee, } @@ -51,8 +57,8 @@ func (s *SubmitPrecommitTask) Do(taskID harmonytask.TaskID, stillOwned func() bo SectorNumber int64 `db:"sector_number"` RegSealProof abi.RegisteredSealProof `db:"reg_seal_proof"` TicketEpoch abi.ChainEpoch `db:"ticket_epoch"` - SealedCID string `db:"sealed_cid"` - UnsealedCID string `db:"unsealed_cid"` + SealedCID string `db:"tree_r_cid"` + UnsealedCID string `db:"tree_d_cid"` } err = s.db.Select(ctx, §orParamsArr, ` @@ -107,8 +113,19 @@ func (s *SubmitPrecommitTask) Do(taskID harmonytask.TaskID, stillOwned func() bo return false, xerrors.Errorf("getting precommit deposit: %w", err) } + mi, err := s.api.StateMinerInfo(ctx, maddr, types.EmptyTSK) + if err != nil { + return false, xerrors.Errorf("getting miner info: %w", err) + } + + a, _, err := s.as.AddressFor(ctx, s.api, mi, api.PreCommitAddr, collateral, big.Zero()) + if err != nil { + return false, xerrors.Errorf("getting address for precommit: %w", err) + } + msg := &types.Message{ To: maddr, + From: a, Method: builtin.MethodsMiner.PreCommitSectorBatch2, Params: pbuf.Bytes(), Value: collateral, // todo config for pulling from miner balance!! From 902a0fc524d11c1468609e3670e9546e290aa774 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 5 Jan 2024 17:07:55 +0100 Subject: [PATCH 118/394] lpmessage: Fix watcher --- provider/chainsched/chain_sched.go | 6 +++--- provider/lpmessage/watch.go | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/provider/chainsched/chain_sched.go b/provider/chainsched/chain_sched.go index 559a0274f..238baac54 100644 --- a/provider/chainsched/chain_sched.go +++ b/provider/chainsched/chain_sched.go @@ -66,13 +66,13 @@ func (s *ProviderChainSched) Run(ctx context.Context) { } gotCur = false - log.Info("restarting window post scheduler") + log.Info("restarting chain scheduler") } select { case changes, ok := <-notifs: if !ok { - log.Warn("window post scheduler notifs channel closed") + log.Warn("chain notifs channel closed") notifs = nil continue } @@ -124,7 +124,7 @@ func (s *ProviderChainSched) Run(ctx context.Context) { func (s *ProviderChainSched) update(ctx context.Context, revert, apply *types.TipSet) { if apply == nil { - log.Error("no new tipset in window post ProviderChainSched.update") + log.Error("no new tipset in ProviderChainSched.update") return } diff --git a/provider/lpmessage/watch.go b/provider/lpmessage/watch.go index ee8d33237..7c7478645 100644 --- a/provider/lpmessage/watch.go +++ b/provider/lpmessage/watch.go @@ -44,7 +44,7 @@ func NewMessageWatcher(db *harmonydb.DB, ht *harmonytask.TaskEngine, pcs *chains stopped: make(chan struct{}), updateCh: make(chan struct{}), } - mw.run() + go mw.run() if err := pcs.AddHandler(mw.processHeadChange); err != nil { return nil, err } @@ -107,7 +107,7 @@ func (mw *MessageWatcher) update() { } // really large limit in case of things getting stuck and backlogging severely - err = mw.db.Select(ctx, &msgs, `SELECT signed_message_cid, from_key, nonce FROM message_wait + err = mw.db.Select(ctx, &msgs, `SELECT signed_message_cid, from_key, nonce FROM message_waits JOIN message_sends ON signed_message_cid = signed_cid WHERE waiter_machine_id = $1 LIMIT 10000`, machineID) if err != nil { From d9e903fa8236b99b34378b1ea640f63ae028ba00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 5 Jan 2024 22:05:50 +0100 Subject: [PATCH 119/394] lpseal: Get seed epoch in poller --- cmd/lotus-provider/tasks/tasks.go | 2 +- provider/lpseal/poller.go | 63 +++++++++++++++++++++--- provider/lpseal/task_submit_precommit.go | 2 +- 3 files changed, 58 insertions(+), 9 deletions(-) diff --git a/cmd/lotus-provider/tasks/tasks.go b/cmd/lotus-provider/tasks/tasks.go index b12f9691a..dd2b9ef83 100644 --- a/cmd/lotus-provider/tasks/tasks.go +++ b/cmd/lotus-provider/tasks/tasks.go @@ -65,7 +65,7 @@ func StartTasks(ctx context.Context, dependencies *deps.Deps) (*harmonytask.Task var sp *lpseal.SealPoller var slr *lpffi.SealCalls if hasAnySealingTask { - sp = lpseal.NewPoller(db) + sp = lpseal.NewPoller(db, full) go sp.RunPoller(ctx) slr = lpffi.NewSealCalls(stor, lstor, si) diff --git a/provider/lpseal/poller.go b/provider/lpseal/poller.go index f1ba84728..343a1e7ae 100644 --- a/provider/lpseal/poller.go +++ b/provider/lpseal/poller.go @@ -2,6 +2,11 @@ package lpseal import ( "context" + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/lotus/chain/actors/builtin/miner" + "github.com/filecoin-project/lotus/chain/actors/policy" + "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/lib/harmony/harmonydb" "github.com/filecoin-project/lotus/lib/harmony/harmonytask" "github.com/filecoin-project/lotus/lib/promise" @@ -22,15 +27,21 @@ const ( const sealPollerInterval = 10 * time.Second +type SealPollerAPI interface { + StateSectorPreCommitInfo(context.Context, address.Address, abi.SectorNumber, types.TipSetKey) (*miner.SectorPreCommitOnChainInfo, error) +} + type SealPoller struct { - db *harmonydb.DB + db *harmonydb.DB + api SealPollerAPI pollers [numPollers]promise.Promise[harmonytask.AddTaskFunc] } -func NewPoller(db *harmonydb.DB) *SealPoller { +func NewPoller(db *harmonydb.DB, api SealPollerAPI) *SealPoller { return &SealPoller{ - db: db, + db: db, + api: api, } } @@ -149,12 +160,50 @@ func (s *SealPoller) poll(ctx context.Context) error { }) } - if task.TaskPrecommitMsg != nil && !task.AfterPrecommitMsg { + if task.TaskPrecommitMsg != nil && !task.AfterPrecommitMsgSuccess { + // join pipeline precommit_msg_cid with message_waits signed_message_cid, if executed_tsk_epoch is not null, return result - } + var execResult []struct { + ExecutedTskCID string `db:"executed_tsk_cid"` + ExecutedTskEpoch int64 `db:"executed_tsk_epoch"` + ExecutedMsgCID string `db:"executed_msg_cid"` - if task.AfterPrecommitMsg { - // todo start precommit msg wait task + ExecutedRcptExitCode int64 `db:"executed_rcpt_exitcode"` + ExecutedRcptGasUsed int64 `db:"executed_rcpt_gas_used"` + } + + err := s.db.Select(ctx, &execResult, `SELECT executed_tsk_cid, executed_tsk_epoch, executed_msg_cid, executed_rcpt_exitcode, executed_rcpt_gas_used + FROM sectors_sdr_pipeline + JOIN message_waits ON sectors_sdr_pipeline.precommit_msg_cid = message_waits.signed_message_cid + WHERE sp_id = $1 AND sector_number = $2 AND executed_tsk_epoch is not null`, task.SpID, task.SectorNumber) + if err != nil { + log.Errorw("failed to query message_waits", "error", err) + } + + if len(execResult) > 0 { + maddr, err := address.NewIDAddress(uint64(task.SpID)) + if err != nil { + return err + } + + pci, err := s.api.StateSectorPreCommitInfo(ctx, maddr, abi.SectorNumber(task.SectorNumber), types.EmptyTSK) + if err != nil { + return xerrors.Errorf("get precommit info: %w", err) + } + + if pci != nil { + randHeight := pci.PreCommitEpoch + policy.GetPreCommitChallengeDelay() + + _, err := s.db.Exec(ctx, `UPDATE sectors_sdr_pipeline SET + seed_epoch = $1, precommit_msg_tsk = $2, after_precommit_msg_success = true + WHERE sp_id = $3 AND sector_number = $4 and seed_epoch is NULL`, + randHeight, execResult[0].ExecutedTskCID, task.SpID, task.SectorNumber) + if err != nil { + return xerrors.Errorf("update sectors_sdr_pipeline: %w", err) + } + } // todo handle missing precommit info (eg expired precommit) + + } } todoWaitSeed := false diff --git a/provider/lpseal/task_submit_precommit.go b/provider/lpseal/task_submit_precommit.go index 0adc9f5ab..99b1a0655 100644 --- a/provider/lpseal/task_submit_precommit.go +++ b/provider/lpseal/task_submit_precommit.go @@ -142,7 +142,7 @@ func (s *SubmitPrecommitTask) Do(taskID harmonytask.TaskID, stillOwned func() bo // set precommit_msg_cid _, err = s.db.Exec(ctx, `UPDATE sectors_sdr_pipeline - SET precommit_msg_cid = $1 + SET precommit_msg_cid = $1, after_precommit_msg = true WHERE task_id_precommit_msg = $2`, mcid, taskID) if err != nil { return false, xerrors.Errorf("updating precommit_msg_cid: %w", err) From fe7558368a25d1c63c5b8374a8ad56b670f213e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 11 Jan 2024 16:46:47 +0100 Subject: [PATCH 120/394] storage: Support C1 directly from sector storage --- storage/paths/http_handler.go | 27 ++++++++++++ storage/paths/interface.go | 2 + storage/paths/local.go | 24 +++++++++++ storage/paths/remote.go | 81 ++++++++++++++++++++++++++++++++++- 4 files changed, 133 insertions(+), 1 deletion(-) diff --git a/storage/paths/http_handler.go b/storage/paths/http_handler.go index 4d0539079..ec0453ed8 100644 --- a/storage/paths/http_handler.go +++ b/storage/paths/http_handler.go @@ -3,6 +3,7 @@ package paths import ( "bytes" "encoding/json" + "github.com/ipfs/go-cid" "io" "net/http" "os" @@ -55,6 +56,7 @@ func (handler *FetchHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { mux.HandleFunc("/remote/stat/{id}", handler.remoteStatFs).Methods("GET") mux.HandleFunc("/remote/vanilla/single", handler.generateSingleVanillaProof).Methods("POST") + mux.HandleFunc("/remote/vanilla/porep", handler.generatePoRepVanillaProof).Methods("POST") mux.HandleFunc("/remote/{type}/{id}/{spt}/allocated/{offset}/{size}", handler.remoteGetAllocated).Methods("GET") mux.HandleFunc("/remote/{type}/{id}", handler.remoteGetSector).Methods("GET") mux.HandleFunc("/remote/{type}/{id}", handler.remoteDeleteSector).Methods("DELETE") @@ -312,6 +314,31 @@ func (handler *FetchHandler) generateSingleVanillaProof(w http.ResponseWriter, r http.ServeContent(w, r, "", time.Time{}, bytes.NewReader(vanilla)) } +type PoRepVanillaParams struct { + Sector storiface.SectorRef + Sealed cid.Cid + Unsealed cid.Cid + Ticket abi.SealRandomness + Seed abi.InteractiveSealRandomness +} + +func (handler *FetchHandler) generatePoRepVanillaProof(w http.ResponseWriter, r *http.Request) { + var params PoRepVanillaParams + if err := json.NewDecoder(r.Body).Decode(¶ms); err != nil { + http.Error(w, err.Error(), 500) + return + } + + vanilla, err := handler.Local.GenetartePoRepVanillaProof(r.Context(), params.Sector, params.Sealed, params.Unsealed, params.Ticket, params.Seed) + if err != nil { + http.Error(w, err.Error(), 500) + return + } + + w.Header().Set("Content-Type", "application/octet-stream") + http.ServeContent(w, r, "", time.Time{}, bytes.NewReader(vanilla)) +} + func FileTypeFromString(t string) (storiface.SectorFileType, error) { switch t { case storiface.FTUnsealed.String(): diff --git a/storage/paths/interface.go b/storage/paths/interface.go index d96135de8..f34c4c907 100644 --- a/storage/paths/interface.go +++ b/storage/paths/interface.go @@ -2,6 +2,7 @@ package paths import ( "context" + "github.com/ipfs/go-cid" "io" "github.com/filecoin-project/go-state-types/abi" @@ -48,4 +49,5 @@ type Store interface { Reserve(ctx context.Context, sid storiface.SectorRef, ft storiface.SectorFileType, storageIDs storiface.SectorPaths, overheadTab map[storiface.SectorFileType]int) (func(), error) GenerateSingleVanillaProof(ctx context.Context, minerID abi.ActorID, si storiface.PostSectorChallenge, ppt abi.RegisteredPoStProof) ([]byte, error) + GenetartePoRepVanillaProof(ctx context.Context, sr storiface.SectorRef, sealed, unsealed cid.Cid, ticket abi.SealRandomness, seed abi.InteractiveSealRandomness) ([]byte, error) } diff --git a/storage/paths/local.go b/storage/paths/local.go index 577d4dbe0..a7f81f2eb 100644 --- a/storage/paths/local.go +++ b/storage/paths/local.go @@ -3,6 +3,7 @@ package paths import ( "context" "encoding/json" + "github.com/ipfs/go-cid" "math/bits" "math/rand" "os" @@ -809,4 +810,27 @@ func (st *Local) GenerateSingleVanillaProof(ctx context.Context, minerID abi.Act } } +func (st *Local) GenetartePoRepVanillaProof(ctx context.Context, sr storiface.SectorRef, sealed, unsealed cid.Cid, ticket abi.SealRandomness, seed abi.InteractiveSealRandomness) ([]byte, error) { + src, _, err := st.AcquireSector(ctx, sr, storiface.FTSealed|storiface.FTCache, storiface.FTNone, storiface.PathStorage, storiface.AcquireMove) + if err != nil { + return nil, xerrors.Errorf("acquire sector: %w", err) + } + + if src.Sealed == "" || src.Cache == "" { + return nil, errPathNotFound + } + + ssize, err := sr.ProofType.SectorSize() + if err != nil { + return nil, xerrors.Errorf("getting sector size: %w", err) + } + + secPiece := []abi.PieceInfo{{ + Size: abi.PaddedPieceSize(ssize), + PieceCID: unsealed, + }} + + return ffi.SealCommitPhase1(sr.ProofType, sealed, unsealed, src.Cache, src.Sealed, sr.ID.Number, sr.ID.Miner, ticket, seed, secPiece) +} + var _ Store = &Local{} diff --git a/storage/paths/remote.go b/storage/paths/remote.go index 0b7563bb2..d9c232abe 100644 --- a/storage/paths/remote.go +++ b/storage/paths/remote.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "fmt" + "github.com/ipfs/go-cid" "io" "math/bits" "net/http" @@ -827,6 +828,8 @@ func (r *Remote) GenerateSingleVanillaProof(ctx context.Context, minerID abi.Act return nil, xerrors.Errorf("resp.Body ReadAll: %w", err) } + _ = resp.Body.Close() + return body, nil } } @@ -834,7 +837,83 @@ func (r *Remote) GenerateSingleVanillaProof(ctx context.Context, minerID abi.Act return nil, xerrors.Errorf("sector not found") } -var _ Store = &Remote{} +func (r *Remote) GenetartePoRepVanillaProof(ctx context.Context, sr storiface.SectorRef, sealed, unsealed cid.Cid, ticket abi.SealRandomness, seed abi.InteractiveSealRandomness) ([]byte, error) { + p, err := r.local.GenetartePoRepVanillaProof(ctx, sr, sealed, unsealed, ticket, seed) + if err != errPathNotFound { + return p, err + } + + ft := storiface.FTSealed | storiface.FTCache + si, err := r.index.StorageFindSector(ctx, sr.ID, ft, 0, false) + if err != nil { + return nil, xerrors.Errorf("finding sector %d failed: %w", sr.ID, err) + } + + requestParams := PoRepVanillaParams{ + Sector: sr, + Sealed: sealed, + Unsealed: unsealed, + Ticket: ticket, + Seed: seed, + } + jreq, err := json.Marshal(requestParams) + if err != nil { + return nil, err + } + + for _, info := range si { + for _, u := range info.BaseURLs { + url := fmt.Sprintf("%s/vanilla/porep", u) + + req, err := http.NewRequest("POST", url, strings.NewReader(string(jreq))) + if err != nil { + return nil, xerrors.Errorf("request: %w", err) + } + + if r.auth != nil { + req.Header = r.auth.Clone() + } + req = req.WithContext(ctx) + + resp, err := http.DefaultClient.Do(req) + if err != nil { + return nil, xerrors.Errorf("do request: %w", err) + } + + if resp.StatusCode != http.StatusOK { + if resp.StatusCode == http.StatusNotFound { + log.Debugw("reading vanilla proof from remote not-found response", "url", url, "store", info.ID) + continue + } + body, err := io.ReadAll(resp.Body) + if err != nil { + return nil, xerrors.Errorf("resp.Body ReadAll: %w", err) + } + + if err := resp.Body.Close(); err != nil { + log.Error("response close: ", err) + } + + return nil, xerrors.Errorf("non-200 code from %s: '%s'", url, strings.TrimSpace(string(body))) + } + + body, err := io.ReadAll(resp.Body) + if err != nil { + if err := resp.Body.Close(); err != nil { + log.Error("response close: ", err) + } + + return nil, xerrors.Errorf("resp.Body ReadAll: %w", err) + } + + _ = resp.Body.Close() + + return body, nil + } + } + + return nil, xerrors.Errorf("sector not found") +} type funcCloser func() error From 1e7218f8de4dfcd212c887b3e482f62ad0bbf295 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 11 Jan 2024 16:47:07 +0100 Subject: [PATCH 121/394] lpseal: PoRep proof (commit) support --- cmd/lotus-provider/tasks/tasks.go | 4 + node/config/types.go | 5 + provider/lpffi/sdr_funcs.go | 9 ++ provider/lpseal/poller.go | 3 +- provider/lpseal/task_porep.go | 161 ++++++++++++++++++++++++++++++ provider/lpseal/task_sdr.go | 2 +- 6 files changed, 182 insertions(+), 2 deletions(-) create mode 100644 provider/lpseal/task_porep.go diff --git a/cmd/lotus-provider/tasks/tasks.go b/cmd/lotus-provider/tasks/tasks.go index dd2b9ef83..2570f68e8 100644 --- a/cmd/lotus-provider/tasks/tasks.go +++ b/cmd/lotus-provider/tasks/tasks.go @@ -83,6 +83,10 @@ func StartTasks(ctx context.Context, dependencies *deps.Deps) (*harmonytask.Task precommitTask := lpseal.NewSubmitPrecommitTask(sp, db, full, sender, as, cfg.Fees.MaxPreCommitGasFee) activeTasks = append(activeTasks, precommitTask) } + if cfg.Subsystems.EnablePoRepProof { + porepTask := lpseal.NewPoRepTask(db, full, sp, slr, cfg.Subsystems.PoRepProofMaxTasks) + activeTasks = append(activeTasks, porepTask) + } } log.Infow("This lotus_provider instance handles", "miner_addresses", maddrs, diff --git a/node/config/types.go b/node/config/types.go index e2924de89..430074097 100644 --- a/node/config/types.go +++ b/node/config/types.go @@ -118,6 +118,11 @@ type ProviderSubsystemsConfig struct { // EnableSendPrecommitMsg enables the sending of precommit messages to the chain // from this lotus-provider instance. EnableSendPrecommitMsg bool + + // EnableSendCommitMsg enables the computation of the porep proof + // In lotus-miner this was Commit1 / Commit2 + EnablePoRepProof bool + PoRepProofMaxTasks int } type DAGStoreConfig struct { diff --git a/provider/lpffi/sdr_funcs.go b/provider/lpffi/sdr_funcs.go index 178e39583..5353ff87e 100644 --- a/provider/lpffi/sdr_funcs.go +++ b/provider/lpffi/sdr_funcs.go @@ -167,6 +167,15 @@ func (sb *SealCalls) TreeRC(ctx context.Context, sector storiface.SectorRef, uns return ffi.SealPreCommitPhase2(p1o, paths.Cache, paths.Sealed) } +func (sb *SealCalls) PoRepSnark(ctx context.Context, sn storiface.SectorRef, sealed, unsealed cid.Cid, ticket abi.SealRandomness, seed abi.InteractiveSealRandomness) ([]byte, error) { + vproof, err := sb.sectors.storage.GenetartePoRepVanillaProof(ctx, sn, sealed, unsealed, ticket, seed) + if err != nil { + return nil, xerrors.Errorf("failed to generate vanilla proof: %w", err) + } + + return ffi.SealCommitPhase2(vproof, sn.ID.Number, sn.ID.Miner) +} + func (sb *SealCalls) makePhase1Out(unsCid cid.Cid, spt abi.RegisteredSealProof) ([]byte, error) { commd, err := commcid.CIDToDataCommitmentV1(unsCid) if err != nil { diff --git a/provider/lpseal/poller.go b/provider/lpseal/poller.go index 343a1e7ae..f097b27f5 100644 --- a/provider/lpseal/poller.go +++ b/provider/lpseal/poller.go @@ -21,6 +21,7 @@ const ( pollerSDR = iota pollerTrees pollerPrecommitMsg + pollerPoRep numPollers ) @@ -207,7 +208,7 @@ func (s *SealPoller) poll(ctx context.Context) error { } todoWaitSeed := false - if task.TaskPoRep == nil && todoWaitSeed { + if task.TaskPoRep != nil && todoWaitSeed { // todo start porep task } } diff --git a/provider/lpseal/task_porep.go b/provider/lpseal/task_porep.go new file mode 100644 index 000000000..8a428d20c --- /dev/null +++ b/provider/lpseal/task_porep.go @@ -0,0 +1,161 @@ +package lpseal + +import ( + "bytes" + "context" + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/go-state-types/crypto" + "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/lib/harmony/harmonydb" + "github.com/filecoin-project/lotus/lib/harmony/harmonytask" + "github.com/filecoin-project/lotus/lib/harmony/resources" + "github.com/filecoin-project/lotus/provider/lpffi" + "github.com/filecoin-project/lotus/storage/sealer/storiface" + "github.com/ipfs/go-cid" + "golang.org/x/xerrors" +) + +type PoRepAPI interface { + ChainHead(context.Context) (*types.TipSet, error) + StateGetRandomnessFromBeacon(context.Context, crypto.DomainSeparationTag, abi.ChainEpoch, []byte, types.TipSetKey) (abi.Randomness, error) +} + +type PoRepTask struct { + db *harmonydb.DB + api PoRepAPI + sp *SealPoller + sc *lpffi.SealCalls + + max int +} + +func NewPoRepTask(db *harmonydb.DB, api PoRepAPI, sp *SealPoller, sc *lpffi.SealCalls, maxPoRep int) *PoRepTask { + return &PoRepTask{ + db: db, + api: api, + sp: sp, + sc: sc, + max: maxPoRep, + } +} + +func (p *PoRepTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (done bool, err error) { + ctx := context.Background() + + var sectorParamsArr []struct { + SpID int64 `db:"sp_id"` + SectorNumber int64 `db:"sector_number"` + RegSealProof abi.RegisteredSealProof `db:"reg_seal_proof"` + TicketEpoch abi.ChainEpoch `db:"ticket_epoch"` + TicketValue []byte `db:"ticket_value"` + SeedEpoch abi.ChainEpoch `db:"seed_epoch"` + SealedCID string `db:"tree_r_cid"` + UnsealedCID string `db:"tree_d_cid"` + } + + err = p.db.Select(ctx, §orParamsArr, ` + SELECT sp_id, sector_number, reg_seal_proof, ticket_epoch, ticket_value, seed_epoch, tree_r_cid, tree_d_cid + FROM sectors_sdr_pipeline + WHERE task_id_porep = $1`, taskID) + if err != nil { + return false, err + } + if len(sectorParamsArr) != 1 { + return false, xerrors.Errorf("expected 1 sector params, got %d", len(sectorParamsArr)) + } + sectorParams := sectorParamsArr[0] + + sealed, err := cid.Parse(sectorParams.SealedCID) + if err != nil { + return false, xerrors.Errorf("failed to parse sealed cid: %w", err) + } + + unsealed, err := cid.Parse(sectorParams.UnsealedCID) + if err != nil { + return false, xerrors.Errorf("failed to parse unsealed cid: %w", err) + } + + ts, err := p.api.ChainHead(ctx) + if err != nil { + return false, xerrors.Errorf("failed to get chain head: %w", err) + } + + maddr, err := address.NewIDAddress(uint64(sectorParams.SpID)) + if err != nil { + return false, xerrors.Errorf("failed to create miner address: %w", err) + } + + buf := new(bytes.Buffer) + if err := maddr.MarshalCBOR(buf); err != nil { + return false, xerrors.Errorf("failed to marshal miner address: %w", err) + } + + rand, err := p.api.StateGetRandomnessFromBeacon(ctx, crypto.DomainSeparationTag_InteractiveSealChallengeSeed, sectorParams.SeedEpoch, buf.Bytes(), ts.Key()) + if err != nil { + return false, xerrors.Errorf("failed to get randomness for computing seal proof: %w", err) + } + + sr := storiface.SectorRef{ + ID: abi.SectorID{ + Miner: abi.ActorID(sectorParams.SpID), + Number: abi.SectorNumber(sectorParams.SectorNumber), + }, + ProofType: sectorParams.RegSealProof, + } + + // COMPUTE THE PROOF! + + proof, err := p.sc.PoRepSnark(ctx, sr, sealed, unsealed, sectorParams.TicketValue, abi.InteractiveSealRandomness(rand)) + if err != nil { + return false, xerrors.Errorf("failed to compute seal proof: %w", err) + } + + // store success! + n, err := p.db.Exec(ctx, `UPDATE sectors_sdr_pipeline + SET after_sdr = true, seed_value = $3, porep_proof = $4 + WHERE sp_id = $1 AND sector_number = $2`, + sectorParams.SpID, sectorParams.SectorNumber, []byte(rand), proof) + if err != nil { + return false, xerrors.Errorf("store sdr success: updating pipeline: %w", err) + } + if n != 1 { + return false, xerrors.Errorf("store sdr success: updated %d rows", n) + } + + return true, nil +} + +func (p *PoRepTask) CanAccept(ids []harmonytask.TaskID, engine *harmonytask.TaskEngine) (*harmonytask.TaskID, error) { + // todo sort by priority + + id := ids[0] + return &id, nil +} + +func (p *PoRepTask) TypeDetails() harmonytask.TaskTypeDetails { + res := harmonytask.TaskTypeDetails{ + Max: p.max, + Name: "PoRep", + Cost: resources.Resources{ + Cpu: 1, + Gpu: 1, + Ram: 50 << 30, // todo correct value + MachineID: 0, + }, + MaxFailures: 5, + Follows: nil, + } + + if isDevnet { + res.Cost.Ram = 1 << 30 + } + + return res +} + +func (p *PoRepTask) Adder(taskFunc harmonytask.AddTaskFunc) { + p.sp.pollers[pollerPoRep].Set(taskFunc) +} + +var _ harmonytask.TaskInterface = &PoRepTask{} diff --git a/provider/lpseal/task_sdr.go b/provider/lpseal/task_sdr.go index f5a92a7e1..0c6c3e5e5 100644 --- a/provider/lpseal/task_sdr.go +++ b/provider/lpseal/task_sdr.go @@ -168,7 +168,7 @@ func (s *SDRTask) TypeDetails() harmonytask.TaskTypeDetails { Cost: resources.Resources{ // todo offset for prefetch? Cpu: 4, // todo multicore sdr Gpu: 0, - Ram: 54 << 30, // todo measure; lower on 2k devnetn + Ram: 54 << 30, }, MaxFailures: 2, Follows: nil, From 41f80a66f9a234ebec2b0a4982b77091d00cb717 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 11 Jan 2024 19:24:28 +0100 Subject: [PATCH 122/394] lpseal: Commit submit task --- cmd/lotus-provider/tasks/tasks.go | 4 + node/config/types.go | 4 + provider/lpseal/poller.go | 1 + provider/lpseal/task_submit_commit.go | 175 ++++++++++++++++++++++++++ 4 files changed, 184 insertions(+) create mode 100644 provider/lpseal/task_submit_commit.go diff --git a/cmd/lotus-provider/tasks/tasks.go b/cmd/lotus-provider/tasks/tasks.go index 2570f68e8..4ae7a242f 100644 --- a/cmd/lotus-provider/tasks/tasks.go +++ b/cmd/lotus-provider/tasks/tasks.go @@ -87,6 +87,10 @@ func StartTasks(ctx context.Context, dependencies *deps.Deps) (*harmonytask.Task porepTask := lpseal.NewPoRepTask(db, full, sp, slr, cfg.Subsystems.PoRepProofMaxTasks) activeTasks = append(activeTasks, porepTask) } + if cfg.Subsystems.EnableSendCommitMsg { + commitTask := lpseal.NewSubmitCommitTask(sp, db, full, sender, as, cfg.Fees.MaxCommitGasFee) + activeTasks = append(activeTasks, commitTask) + } } log.Infow("This lotus_provider instance handles", "miner_addresses", maddrs, diff --git a/node/config/types.go b/node/config/types.go index 430074097..8caf75664 100644 --- a/node/config/types.go +++ b/node/config/types.go @@ -123,6 +123,10 @@ type ProviderSubsystemsConfig struct { // In lotus-miner this was Commit1 / Commit2 EnablePoRepProof bool PoRepProofMaxTasks int + + // EnableSendCommitMsg enables the sending of commit messages to the chain + // from this lotus-provider instance. + EnableSendCommitMsg bool } type DAGStoreConfig struct { diff --git a/provider/lpseal/poller.go b/provider/lpseal/poller.go index f097b27f5..2605a5a51 100644 --- a/provider/lpseal/poller.go +++ b/provider/lpseal/poller.go @@ -22,6 +22,7 @@ const ( pollerTrees pollerPrecommitMsg pollerPoRep + pollerCommitMsg numPollers ) diff --git a/provider/lpseal/task_submit_commit.go b/provider/lpseal/task_submit_commit.go new file mode 100644 index 000000000..4967098ad --- /dev/null +++ b/provider/lpseal/task_submit_commit.go @@ -0,0 +1,175 @@ +package lpseal + +import ( + "bytes" + "context" + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/go-state-types/big" + "github.com/filecoin-project/go-state-types/builtin" + "github.com/filecoin-project/lotus/api" + "github.com/filecoin-project/lotus/chain/actors/builtin/miner" + "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/lib/harmony/harmonydb" + "github.com/filecoin-project/lotus/lib/harmony/harmonytask" + "github.com/filecoin-project/lotus/lib/harmony/resources" + "github.com/filecoin-project/lotus/provider/lpmessage" + "github.com/filecoin-project/lotus/storage/ctladdr" + sealing "github.com/filecoin-project/lotus/storage/pipeline" + "golang.org/x/xerrors" +) + +type SubmitCommitAPI interface { + ChainHead(context.Context) (*types.TipSet, error) + StateMinerInfo(context.Context, address.Address, types.TipSetKey) (api.MinerInfo, error) + StateMinerInitialPledgeCollateral(context.Context, address.Address, miner.SectorPreCommitInfo, types.TipSetKey) (big.Int, error) + StateSectorPreCommitInfo(context.Context, address.Address, abi.SectorNumber, types.TipSetKey) (*miner.SectorPreCommitOnChainInfo, error) + ctladdr.NodeApi +} + +type SubmitCommitTask struct { + sp *SealPoller + db *harmonydb.DB + api SubmitCommitAPI + + sender *lpmessage.Sender + as sealing.AddressSelector + + maxFee types.FIL +} + +func NewSubmitCommitTask(sp *SealPoller, db *harmonydb.DB, api SubmitCommitAPI, sender *lpmessage.Sender, as sealing.AddressSelector, maxFee types.FIL) *SubmitCommitTask { + return &SubmitCommitTask{ + sp: sp, + db: db, + api: api, + sender: sender, + as: as, + + maxFee: maxFee, + } +} + +func (s *SubmitCommitTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (done bool, err error) { + ctx := context.Background() + + var sectorParamsArr []struct { + SpID int64 `db:"sp_id"` + SectorNumber int64 `db:"sector_number"` + Proof []byte `db:"porep_proof"` + } + + err = s.db.Select(ctx, §orParamsArr, ` + SELECT sp_id, sector_number, porep_proof + FROM sectors_sdr_pipeline + WHERE task_id_commit_msg = $1`, taskID) + if err != nil { + return false, xerrors.Errorf("getting sector params: %w", err) + } + + if len(sectorParamsArr) != 1 { + return false, xerrors.Errorf("expected 1 sector params, got %d", len(sectorParamsArr)) + } + sectorParams := sectorParamsArr[0] + + maddr, err := address.NewIDAddress(uint64(sectorParams.SpID)) + if err != nil { + return false, xerrors.Errorf("getting miner address: %w", err) + } + + params := miner.ProveCommitSectorParams{ + SectorNumber: abi.SectorNumber(sectorParams.SectorNumber), + Proof: sectorParams.Proof, + } + + enc := new(bytes.Buffer) + if err := params.MarshalCBOR(enc); err != nil { + return false, xerrors.Errorf("could not serialize commit params: %w", err) + } + + ts, err := s.api.ChainHead(ctx) + if err != nil { + return false, xerrors.Errorf("getting chain head: %w", err) + } + + mi, err := s.api.StateMinerInfo(ctx, maddr, types.EmptyTSK) + if err != nil { + return false, xerrors.Errorf("getting miner info: %w", err) + } + + pci, err := s.api.StateSectorPreCommitInfo(ctx, maddr, abi.SectorNumber(sectorParams.SectorNumber), ts.Key()) + if err != nil { + return false, xerrors.Errorf("getting precommit info: %w", err) + } + if pci == nil { + return false, xerrors.Errorf("precommit info not found on chain") + } + + collateral, err := s.api.StateMinerInitialPledgeCollateral(ctx, maddr, pci.Info, ts.Key()) + if err != nil { + return false, xerrors.Errorf("getting initial pledge collateral: %w", err) + } + + collateral = big.Sub(collateral, pci.PreCommitDeposit) + if collateral.LessThan(big.Zero()) { + collateral = big.Zero() + } + + a, _, err := s.as.AddressFor(ctx, s.api, mi, api.CommitAddr, collateral, big.Zero()) + if err != nil { + return false, xerrors.Errorf("getting address for precommit: %w", err) + } + + msg := &types.Message{ + To: maddr, + From: a, + Method: builtin.MethodsMiner.ProveCommitSector, // todo ddo provecommit3 + Params: enc.Bytes(), + Value: collateral, // todo config for pulling from miner balance!! + } + + mss := &api.MessageSendSpec{ + MaxFee: abi.TokenAmount(s.maxFee), + } + + mcid, err := s.sender.Send(ctx, msg, mss, "commit") + if err != nil { + return false, xerrors.Errorf("pushing message to mpool: %w", err) + } + + _, err = s.db.Exec(ctx, `UPDATE sectors_sdr_pipeline SET commit_msg_cid = $1, after_commit_msg = true WHERE sp_id = $2 AND sector_number = $3`, mcid, sectorParams.SpID, sectorParams.SectorNumber) + if err != nil { + return false, xerrors.Errorf("updating commit_msg_cid: %w", err) + } + + _, err = s.db.Exec(ctx, `INSERT INTO message_waits (signed_message_cid) VALUES ($1)`, mcid) + if err != nil { + return false, xerrors.Errorf("inserting into message_waits: %w", err) + } + + return true, nil +} + +func (s *SubmitCommitTask) CanAccept(ids []harmonytask.TaskID, engine *harmonytask.TaskEngine) (*harmonytask.TaskID, error) { + id := ids[0] + return &id, nil +} + +func (s *SubmitCommitTask) TypeDetails() harmonytask.TaskTypeDetails { + return harmonytask.TaskTypeDetails{ + Max: 128, + Name: "CommitSubmit", + Cost: resources.Resources{ + Cpu: 0, + Gpu: 0, + Ram: 1 << 20, + }, + MaxFailures: 16, + } +} + +func (s *SubmitCommitTask) Adder(taskFunc harmonytask.AddTaskFunc) { + s.sp.pollers[pollerCommitMsg].Set(taskFunc) +} + +var _ harmonytask.TaskInterface = &SubmitCommitTask{} From f2e1b5a35cef5170d7c57ed6e6c747a9c9b242f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 11 Jan 2024 22:39:42 +0100 Subject: [PATCH 123/394] lpseal: Poller for PoRep --- .../harmonydb/sql/20231217-sdr-pipeline.sql | 3 +- provider/lpseal/poller.go | 97 ++++++++++++++++--- provider/lpseal/task_porep.go | 2 +- 3 files changed, 88 insertions(+), 14 deletions(-) diff --git a/lib/harmony/harmonydb/sql/20231217-sdr-pipeline.sql b/lib/harmony/harmonydb/sql/20231217-sdr-pipeline.sql index c30a17e7c..a239cc8a7 100644 --- a/lib/harmony/harmonydb/sql/20231217-sdr-pipeline.sql +++ b/lib/harmony/harmonydb/sql/20231217-sdr-pipeline.sql @@ -49,6 +49,7 @@ create table sectors_sdr_pipeline ( -- Commit (PoRep snark) task_id_porep bigint, porep_proof bytea, + after_porep bool not null default false, -- Commit message sending commit_msg_cid text, @@ -58,8 +59,6 @@ create table sectors_sdr_pipeline ( -- Commit message wait commit_msg_tsk bytea, - - task_id_commit_msg_wait bigint, after_commit_msg_success bool not null default false, -- Failure handling diff --git a/provider/lpseal/poller.go b/provider/lpseal/poller.go index 2605a5a51..47e60ea6d 100644 --- a/provider/lpseal/poller.go +++ b/provider/lpseal/poller.go @@ -28,9 +28,12 @@ const ( ) const sealPollerInterval = 10 * time.Second +const seedEpochConfidence = 3 type SealPollerAPI interface { StateSectorPreCommitInfo(context.Context, address.Address, abi.SectorNumber, types.TipSetKey) (*miner.SectorPreCommitOnChainInfo, error) + StateSectorGetInfo(ctx context.Context, maddr address.Address, sectorNumber abi.SectorNumber, tsk types.TipSetKey) (*miner.SectorOnChainInfo, error) + ChainHead(context.Context) (*types.TipSet, error) } type SealPoller struct { @@ -83,16 +86,17 @@ func (s *SealPoller) poll(ctx context.Context) error { TaskPrecommitMsg *int64 `db:"task_id_precommit_msg"` AfterPrecommitMsg bool `db:"after_precommit_msg"` - AfterPrecommitMsgSuccess bool `db:"after_precommit_msg_success"` + AfterPrecommitMsgSuccess bool `db:"after_precommit_msg_success"` + SeedEpoch *int64 `db:"seed_epoch"` TaskPoRep *int64 `db:"task_id_porep"` PoRepProof []byte `db:"porep_proof"` + AfterPoRep bool `db:"after_porep"` TaskCommitMsg *int64 `db:"task_id_commit_msg"` AfterCommitMsg bool `db:"after_commit_msg"` - TaskCommitMsgWait *int64 `db:"task_id_commit_msg_wait"` - AfterCommitMsgSuccess bool `db:"after_commit_msg_success"` + AfterCommitMsgSuccess bool `db:"after_commit_msg_success"` Failed bool `db:"failed"` FailedReason string `db:"failed_reason"` @@ -105,10 +109,10 @@ func (s *SealPoller) poll(ctx context.Context) error { task_id_tree_c, after_tree_c, task_id_tree_r, after_tree_r, task_id_precommit_msg, after_precommit_msg, - after_precommit_msg_success, - task_id_porep, porep_proof, + after_precommit_msg_success, seed_epoch, + task_id_porep, porep_proof, after_porep, task_id_commit_msg, after_commit_msg, - task_id_commit_msg_wait, after_commit_msg_success, + after_commit_msg_success, failed, failed_reason FROM sectors_sdr_pipeline WHERE after_commit_msg_success != true`) if err != nil { @@ -163,8 +167,6 @@ func (s *SealPoller) poll(ctx context.Context) error { } if task.TaskPrecommitMsg != nil && !task.AfterPrecommitMsgSuccess { - // join pipeline precommit_msg_cid with message_waits signed_message_cid, if executed_tsk_epoch is not null, return result - var execResult []struct { ExecutedTskCID string `db:"executed_tsk_cid"` ExecutedTskEpoch int64 `db:"executed_tsk_epoch"` @@ -208,9 +210,82 @@ func (s *SealPoller) poll(ctx context.Context) error { } } - todoWaitSeed := false - if task.TaskPoRep != nil && todoWaitSeed { - // todo start porep task + ts, err := s.api.ChainHead(ctx) + if err != nil { + return xerrors.Errorf("getting chain head: %w", err) + } + + if s.pollers[pollerPoRep].IsSet() && task.AfterPrecommitMsgSuccess && task.SeedEpoch != nil && task.TaskPoRep == nil && ts.Height() >= abi.ChainEpoch(*task.SeedEpoch+seedEpochConfidence) { + s.pollers[pollerPoRep].Val(ctx)(func(id harmonytask.TaskID, tx *harmonydb.Tx) (shouldCommit bool, seriousError error) { + n, err := tx.Exec(`UPDATE sectors_sdr_pipeline SET task_id_porep = $1 WHERE sp_id = $2 AND sector_number = $3 and task_id_porep is null`, id, task.SpID, task.SectorNumber) + if err != nil { + return false, xerrors.Errorf("update sectors_sdr_pipeline: %w", err) + } + if n != 1 { + return false, xerrors.Errorf("expected to update 1 row, updated %d", n) + } + + return true, nil + }) + } + + if task.AfterPoRep && len(task.PoRepProof) > 0 && task.TaskPrecommitMsg == nil { + s.pollers[pollerCommitMsg].Val(ctx)(func(id harmonytask.TaskID, tx *harmonydb.Tx) (shouldCommit bool, seriousError error) { + n, err := tx.Exec(`UPDATE sectors_sdr_pipeline SET task_id_commit_msg = $1 WHERE sp_id = $2 AND sector_number = $3 and task_id_commit_msg is null`, id, task.SpID, task.SectorNumber) + if err != nil { + return false, xerrors.Errorf("update sectors_sdr_pipeline: %w", err) + } + if n != 1 { + return false, xerrors.Errorf("expected to update 1 row, updated %d", n) + } + + return true, nil + }) + } + + if task.AfterCommitMsg && !task.AfterCommitMsgSuccess { + var execResult []struct { + ExecutedTskCID string `db:"executed_tsk_cid"` + ExecutedTskEpoch int64 `db:"executed_tsk_epoch"` + ExecutedMsgCID string `db:"executed_msg_cid"` + + ExecutedRcptExitCode int64 `db:"executed_rcpt_exitcode"` + ExecutedRcptGasUsed int64 `db:"executed_rcpt_gas_used"` + } + + err := s.db.Select(ctx, &execResult, `SELECT executed_tsk_cid, executed_tsk_epoch, executed_msg_cid, executed_rcpt_exitcode, executed_rcpt_gas_used + FROM sectors_sdr_pipeline + JOIN message_waits ON sectors_sdr_pipeline.commit_msg_cid = message_waits.signed_message_cid + WHERE sp_id = $1 AND sector_number = $2 AND executed_tsk_epoch is not null`, task.SpID, task.SectorNumber) + if err != nil { + log.Errorw("failed to query message_waits", "error", err) + } + + if len(execResult) > 0 { + maddr, err := address.NewIDAddress(uint64(task.SpID)) + if err != nil { + return err + } + + si, err := s.api.StateSectorGetInfo(ctx, maddr, abi.SectorNumber(task.SectorNumber), types.EmptyTSK) + if err != nil { + return xerrors.Errorf("get sector info: %w", err) + } + + if si != nil { + // todo handdle missing sector info (not found after cron) + } else { + // yay! + + _, err := s.db.Exec(ctx, `UPDATE sectors_sdr_pipeline SET + after_commit_msg_success = true, commit_msg_tsk = $1 + WHERE sp_id = $2 AND sector_number = $3 and after_commit_msg_success is NULL`, + execResult[0].ExecutedTskCID, task.SpID, task.SectorNumber) + if err != nil { + return xerrors.Errorf("update sectors_sdr_pipeline: %w", err) + } + } + } } } diff --git a/provider/lpseal/task_porep.go b/provider/lpseal/task_porep.go index 8a428d20c..96d0dc56c 100644 --- a/provider/lpseal/task_porep.go +++ b/provider/lpseal/task_porep.go @@ -113,7 +113,7 @@ func (p *PoRepTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (done // store success! n, err := p.db.Exec(ctx, `UPDATE sectors_sdr_pipeline - SET after_sdr = true, seed_value = $3, porep_proof = $4 + SET after_porep = true, seed_value = $3, porep_proof = $4 WHERE sp_id = $1 AND sector_number = $2`, sectorParams.SpID, sectorParams.SectorNumber, []byte(rand), proof) if err != nil { From 3eea16c5286f76400fbac57efca4e33f5a0fbf46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 11 Jan 2024 23:55:26 +0100 Subject: [PATCH 124/394] lpseal: WORKING SDR PIPELINE --- cmd/lotus-provider/pipeline.go | 2 +- provider/lpffi/sdr_funcs.go | 4 ++++ provider/lpseal/poller.go | 9 +++++---- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/cmd/lotus-provider/pipeline.go b/cmd/lotus-provider/pipeline.go index 8e01044e2..20eb93451 100644 --- a/cmd/lotus-provider/pipeline.go +++ b/cmd/lotus-provider/pipeline.go @@ -47,7 +47,7 @@ var pipelineStartCmd = &cli.Command{ &cli.BoolFlag{ Name: "synthetic", Usage: "Use synthetic PoRep", - Value: true, + Value: false, // todo implement synthetic }, &cli.StringSliceFlag{ // todo consider moving layers top level Name: "layers", diff --git a/provider/lpffi/sdr_funcs.go b/provider/lpffi/sdr_funcs.go index 5353ff87e..801b2e7ab 100644 --- a/provider/lpffi/sdr_funcs.go +++ b/provider/lpffi/sdr_funcs.go @@ -167,6 +167,10 @@ func (sb *SealCalls) TreeRC(ctx context.Context, sector storiface.SectorRef, uns return ffi.SealPreCommitPhase2(p1o, paths.Cache, paths.Sealed) } +func (sb *SealCalls) GenerateSynthPoRep() { + panic("todo") +} + func (sb *SealCalls) PoRepSnark(ctx context.Context, sn storiface.SectorRef, sealed, unsealed cid.Cid, ticket abi.SealRandomness, seed abi.InteractiveSealRandomness) ([]byte, error) { vproof, err := sb.sectors.storage.GenetartePoRepVanillaProof(ctx, sn, sealed, unsealed, ticket, seed) if err != nil { diff --git a/provider/lpseal/poller.go b/provider/lpseal/poller.go index 47e60ea6d..f2703067d 100644 --- a/provider/lpseal/poller.go +++ b/provider/lpseal/poller.go @@ -229,7 +229,7 @@ func (s *SealPoller) poll(ctx context.Context) error { }) } - if task.AfterPoRep && len(task.PoRepProof) > 0 && task.TaskPrecommitMsg == nil { + if task.AfterPoRep && len(task.PoRepProof) > 0 && task.TaskCommitMsg == nil && s.pollers[pollerCommitMsg].IsSet() { s.pollers[pollerCommitMsg].Val(ctx)(func(id harmonytask.TaskID, tx *harmonydb.Tx) (shouldCommit bool, seriousError error) { n, err := tx.Exec(`UPDATE sectors_sdr_pipeline SET task_id_commit_msg = $1 WHERE sp_id = $2 AND sector_number = $3 and task_id_commit_msg is null`, id, task.SpID, task.SectorNumber) if err != nil { @@ -243,7 +243,7 @@ func (s *SealPoller) poll(ctx context.Context) error { }) } - if task.AfterCommitMsg && !task.AfterCommitMsgSuccess { + if task.AfterCommitMsg && !task.AfterCommitMsgSuccess && s.pollers[pollerCommitMsg].IsSet() { var execResult []struct { ExecutedTskCID string `db:"executed_tsk_cid"` ExecutedTskEpoch int64 `db:"executed_tsk_epoch"` @@ -272,14 +272,15 @@ func (s *SealPoller) poll(ctx context.Context) error { return xerrors.Errorf("get sector info: %w", err) } - if si != nil { + if si == nil { + log.Errorw("todo handle missing sector info (not found after cron)", "sp", task.SpID, "sector", task.SectorNumber, "exec_epoch", execResult[0].ExecutedTskEpoch, "exec_tskcid", execResult[0].ExecutedTskCID, "msg_cid", execResult[0].ExecutedMsgCID) // todo handdle missing sector info (not found after cron) } else { // yay! _, err := s.db.Exec(ctx, `UPDATE sectors_sdr_pipeline SET after_commit_msg_success = true, commit_msg_tsk = $1 - WHERE sp_id = $2 AND sector_number = $3 and after_commit_msg_success is NULL`, + WHERE sp_id = $2 AND sector_number = $3 and after_commit_msg_success = false`, execResult[0].ExecutedTskCID, task.SpID, task.SectorNumber) if err != nil { return xerrors.Errorf("update sectors_sdr_pipeline: %w", err) From 4c2235a686de50b0b09ce2d32473b0a54adb39cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 12 Jan 2024 11:03:37 +0100 Subject: [PATCH 125/394] make gen, mod tidy --- .circleci/config.yml | 2 +- cmd/lotus-provider/pipeline.go | 7 ++- cmd/lotus-provider/tasks/tasks.go | 6 +- documentation/en/cli-lotus-provider.md | 49 +++++++++++++--- .../en/default-lotus-provider-config.toml | 42 ++++++++++++++ go.mod | 4 +- lib/harmony/resources/resources.go | 2 +- node/config/doc_gen.go | 56 +++++++++++++++++++ node/config/storage.go | 2 +- node/config/types.go | 8 +-- provider/lpffi/sdr_funcs.go | 15 +++-- provider/lpmessage/watch.go | 7 ++- provider/lpproof/treed_build.go | 16 +++--- provider/lpproof/treed_build_test.go | 8 ++- provider/lpseal/poller.go | 9 ++- provider/lpseal/sector_num_alloc.go | 5 +- provider/lpseal/task_porep.go | 7 ++- provider/lpseal/task_sdr.go | 5 +- provider/lpseal/task_submit_commit.go | 5 +- provider/lpseal/task_submit_precommit.go | 7 ++- provider/lpseal/task_trees.go | 5 +- storage/paths/http_handler.go | 2 +- storage/paths/interface.go | 3 +- storage/paths/local.go | 2 +- storage/paths/mocks/store.go | 16 ++++++ storage/paths/remote.go | 2 +- 26 files changed, 238 insertions(+), 54 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 81cc6adcf..4410dfc60 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1018,7 +1018,7 @@ workflows: requires: - build suite: utest-unit-rest - target: "./blockstore/... ./build/... ./chain/... ./conformance/... ./gateway/... ./journal/... ./lib/... ./markets/... ./paychmgr/... ./tools/..." + target: "./blockstore/... ./build/... ./chain/... ./conformance/... ./gateway/... ./journal/... ./lib/... ./markets/... ./paychmgr/... ./provider/... ./tools/..." resource_class: 2xlarge - test: name: test-unit-storage diff --git a/cmd/lotus-provider/pipeline.go b/cmd/lotus-provider/pipeline.go index 20eb93451..5447779a7 100644 --- a/cmd/lotus-provider/pipeline.go +++ b/cmd/lotus-provider/pipeline.go @@ -2,16 +2,19 @@ package main import ( "fmt" + + "github.com/urfave/cli/v2" + "golang.org/x/xerrors" + "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/lotus/chain/actors/builtin/miner" "github.com/filecoin-project/lotus/chain/types" lcli "github.com/filecoin-project/lotus/cli" "github.com/filecoin-project/lotus/cmd/lotus-provider/deps" "github.com/filecoin-project/lotus/lib/harmony/harmonydb" "github.com/filecoin-project/lotus/provider/lpseal" - "github.com/urfave/cli/v2" - "golang.org/x/xerrors" ) var pipelineCmd = &cli.Command{ diff --git a/cmd/lotus-provider/tasks/tasks.go b/cmd/lotus-provider/tasks/tasks.go index 4ae7a242f..d4332843f 100644 --- a/cmd/lotus-provider/tasks/tasks.go +++ b/cmd/lotus-provider/tasks/tasks.go @@ -3,9 +3,6 @@ package tasks import ( "context" - "github.com/filecoin-project/lotus/provider/chainsched" - "github.com/filecoin-project/lotus/provider/lpffi" - "github.com/filecoin-project/lotus/provider/lpseal" logging "github.com/ipfs/go-log/v2" "github.com/samber/lo" @@ -13,7 +10,10 @@ import ( "github.com/filecoin-project/lotus/cmd/lotus-provider/deps" "github.com/filecoin-project/lotus/lib/harmony/harmonytask" "github.com/filecoin-project/lotus/provider" + "github.com/filecoin-project/lotus/provider/chainsched" + "github.com/filecoin-project/lotus/provider/lpffi" "github.com/filecoin-project/lotus/provider/lpmessage" + "github.com/filecoin-project/lotus/provider/lpseal" "github.com/filecoin-project/lotus/provider/lpwinning" ) diff --git a/documentation/en/cli-lotus-provider.md b/documentation/en/cli-lotus-provider.md index 5e5864107..f9ef85af8 100644 --- a/documentation/en/cli-lotus-provider.md +++ b/documentation/en/cli-lotus-provider.md @@ -10,13 +10,14 @@ VERSION: 1.25.3-dev COMMANDS: - run Start a lotus provider process - stop Stop a running lotus provider - config Manage node config by layers. The layer 'base' will always be applied. - test Utility functions for testing - web Start lotus provider web interface - version Print version - help, h Shows a list of commands or help for one command + run Start a lotus provider process + stop Stop a running lotus provider + config Manage node config by layers. The layer 'base' will always be applied. + test Utility functions for testing + web Start lotus provider web interface + pipeline Manage the sealing pipeline + version Print version + help, h Shows a list of commands or help for one command DEVELOPER: auth Manage RPC permissions log Manage logging @@ -267,6 +268,40 @@ OPTIONS: --help, -h show help ``` +## lotus-provider pipeline +``` +NAME: + lotus-provider pipeline - Manage the sealing pipeline + +USAGE: + lotus-provider pipeline command [command options] [arguments...] + +COMMANDS: + start Start new sealing operations manually + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help +``` + +### lotus-provider pipeline start +``` +NAME: + lotus-provider pipeline start - Start new sealing operations manually + +USAGE: + lotus-provider pipeline start [command options] [arguments...] + +OPTIONS: + --actor value Specify actor address to start sealing sectors for + --now Start sealing sectors for all actors now (not on schedule) (default: false) + --cc Start sealing new CC sectors (default: false) + --count value Number of sectors to start (default: 1) + --synthetic Use synthetic PoRep (default: false) + --layers value [ --layers value ] list of layers to be interpreted (atop defaults). Default: base (default: "base") + --help, -h show help +``` + ## lotus-provider version ``` NAME: diff --git a/documentation/en/default-lotus-provider-config.toml b/documentation/en/default-lotus-provider-config.toml index 1daee17d0..de3c68de7 100644 --- a/documentation/en/default-lotus-provider-config.toml +++ b/documentation/en/default-lotus-provider-config.toml @@ -11,6 +11,48 @@ # type: int #WinningPostMaxTasks = 0 + # EnableSealSDR enables SDR tasks to run. SDR is the long sequential computation + # creating layers. + # In lotus-miner this was run as part of PreCommit1. + # + # type: bool + #EnableSealSDR = false + + # type: int + #SealSDRMaxTasks = 0 + + # EnableSealSDRTrees enables the SDR pipeline tree-building task to run. + # This task handles encoding of unsealed data into last sdr layer and building + # of TreeR, TreeC and TreeD. + # In lotus-miner this was run as part of PreCommit2 (TreeD was run in PreCommit1). + # + # type: bool + #EnableSealSDRTrees = false + + # type: int + #SealSDRTreesMaxTasks = 0 + + # EnableSendPrecommitMsg enables the sending of precommit messages to the chain + # from this lotus-provider instance. + # + # type: bool + #EnableSendPrecommitMsg = false + + # EnableSendCommitMsg enables the computation of the porep proof + # In lotus-miner this was Commit1 / Commit2 + # + # type: bool + #EnablePoRepProof = false + + # type: int + #PoRepProofMaxTasks = 0 + + # EnableSendCommitMsg enables the sending of commit messages to the chain + # from this lotus-provider instance. + # + # type: bool + #EnableSendCommitMsg = false + # type: bool #EnableWebGui = false diff --git a/go.mod b/go.mod index c1b353fab..24366a162 100644 --- a/go.mod +++ b/go.mod @@ -117,6 +117,7 @@ require ( github.com/mattn/go-isatty v0.0.19 github.com/mattn/go-sqlite3 v1.14.16 github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 + github.com/minio/sha256-simd v1.0.1 github.com/mitchellh/go-homedir v1.1.0 github.com/multiformats/go-base32 v0.1.0 github.com/multiformats/go-multiaddr v0.12.1 @@ -126,7 +127,6 @@ require ( github.com/multiformats/go-multihash v0.2.3 github.com/multiformats/go-varint v0.0.7 github.com/open-rpc/meta-schema v0.0.0-20201029221707-1b72ef2ea333 - github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 github.com/pkg/errors v0.9.1 github.com/polydawn/refmt v0.89.0 github.com/prometheus/client_golang v1.16.0 @@ -276,7 +276,6 @@ require ( github.com/miekg/dns v1.1.55 // indirect github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b // indirect github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc // indirect - github.com/minio/sha256-simd v1.0.1 // indirect github.com/mr-tron/base58 v1.2.0 // indirect github.com/multiformats/go-base36 v0.2.0 // indirect github.com/multiformats/go-multiaddr-fmt v0.1.0 // indirect @@ -286,6 +285,7 @@ require ( github.com/onsi/ginkgo/v2 v2.11.0 // indirect github.com/opencontainers/runtime-spec v1.1.0 // indirect github.com/opentracing/opentracing-go v1.2.0 // indirect + github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect github.com/petar/GoLLRB v0.0.0-20210522233825-ae3b015fd3e9 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_model v0.4.0 // indirect diff --git a/lib/harmony/resources/resources.go b/lib/harmony/resources/resources.go index 31060084c..7e42b595c 100644 --- a/lib/harmony/resources/resources.go +++ b/lib/harmony/resources/resources.go @@ -3,13 +3,13 @@ package resources import ( "bytes" "context" - "github.com/elastic/go-sysinfo" "os/exec" "regexp" "runtime" "sync/atomic" "time" + "github.com/elastic/go-sysinfo" logging "github.com/ipfs/go-log/v2" "golang.org/x/sys/unix" "golang.org/x/xerrors" diff --git a/node/config/doc_gen.go b/node/config/doc_gen.go index a63e40a42..326be057c 100644 --- a/node/config/doc_gen.go +++ b/node/config/doc_gen.go @@ -1007,6 +1007,62 @@ block rewards will be missed!`, Comment: ``, }, + { + Name: "EnableSealSDR", + Type: "bool", + + Comment: `EnableSealSDR enables SDR tasks to run. SDR is the long sequential computation +creating layers. +In lotus-miner this was run as part of PreCommit1.`, + }, + { + Name: "SealSDRMaxTasks", + Type: "int", + + Comment: ``, + }, + { + Name: "EnableSealSDRTrees", + Type: "bool", + + Comment: `EnableSealSDRTrees enables the SDR pipeline tree-building task to run. +This task handles encoding of unsealed data into last sdr layer and building +of TreeR, TreeC and TreeD. +In lotus-miner this was run as part of PreCommit2 (TreeD was run in PreCommit1).`, + }, + { + Name: "SealSDRTreesMaxTasks", + Type: "int", + + Comment: ``, + }, + { + Name: "EnableSendPrecommitMsg", + Type: "bool", + + Comment: `EnableSendPrecommitMsg enables the sending of precommit messages to the chain +from this lotus-provider instance.`, + }, + { + Name: "EnablePoRepProof", + Type: "bool", + + Comment: `EnableSendCommitMsg enables the computation of the porep proof +In lotus-miner this was Commit1 / Commit2`, + }, + { + Name: "PoRepProofMaxTasks", + Type: "int", + + Comment: ``, + }, + { + Name: "EnableSendCommitMsg", + Type: "bool", + + Comment: `EnableSendCommitMsg enables the sending of commit messages to the chain +from this lotus-provider instance.`, + }, { Name: "EnableWebGui", Type: "bool", diff --git a/node/config/storage.go b/node/config/storage.go index 8494fb71a..a6a8868dd 100644 --- a/node/config/storage.go +++ b/node/config/storage.go @@ -3,12 +3,12 @@ package config import ( "encoding/json" "errors" - "github.com/mitchellh/go-homedir" "io" "io/fs" "os" "path" + "github.com/mitchellh/go-homedir" "golang.org/x/xerrors" "github.com/filecoin-project/lotus/storage/sealer/storiface" diff --git a/node/config/types.go b/node/config/types.go index 8caf75664..05ed8786f 100644 --- a/node/config/types.go +++ b/node/config/types.go @@ -98,10 +98,6 @@ type ProviderSubsystemsConfig struct { EnableWinningPost bool WinningPostMaxTasks int - EnableWebGui bool - // The address that should listen for Web GUI requests. - GuiAddress string - // EnableSealSDR enables SDR tasks to run. SDR is the long sequential computation // creating layers. // In lotus-miner this was run as part of PreCommit1. @@ -127,6 +123,10 @@ type ProviderSubsystemsConfig struct { // EnableSendCommitMsg enables the sending of commit messages to the chain // from this lotus-provider instance. EnableSendCommitMsg bool + + EnableWebGui bool + // The address that should listen for Web GUI requests. + GuiAddress string } type DAGStoreConfig struct { diff --git a/provider/lpffi/sdr_funcs.go b/provider/lpffi/sdr_funcs.go index 801b2e7ab..7ac01ed08 100644 --- a/provider/lpffi/sdr_funcs.go +++ b/provider/lpffi/sdr_funcs.go @@ -4,20 +4,23 @@ import ( "context" "encoding/json" "fmt" + "io" + "os" + "path/filepath" + + "github.com/ipfs/go-cid" + logging "github.com/ipfs/go-log/v2" + "golang.org/x/xerrors" + ffi "github.com/filecoin-project/filecoin-ffi" commcid "github.com/filecoin-project/go-fil-commcid" "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/lotus/provider/lpproof" "github.com/filecoin-project/lotus/storage/paths" "github.com/filecoin-project/lotus/storage/pipeline/lib/nullreader" "github.com/filecoin-project/lotus/storage/sealer/proofpaths" "github.com/filecoin-project/lotus/storage/sealer/storiface" - "github.com/ipfs/go-cid" - logging "github.com/ipfs/go-log/v2" - "golang.org/x/xerrors" - "io" - "os" - "path/filepath" ) var log = logging.Logger("lpffi") diff --git a/provider/lpmessage/watch.go b/provider/lpmessage/watch.go index 7c7478645..1a04a67a1 100644 --- a/provider/lpmessage/watch.go +++ b/provider/lpmessage/watch.go @@ -3,15 +3,18 @@ package lpmessage import ( "context" "encoding/json" + "sync/atomic" + + "github.com/ipfs/go-cid" + "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/lib/harmony/harmonydb" "github.com/filecoin-project/lotus/lib/harmony/harmonytask" "github.com/filecoin-project/lotus/provider/chainsched" - "github.com/ipfs/go-cid" - "sync/atomic" ) const MinConfidence = 6 diff --git a/provider/lpproof/treed_build.go b/provider/lpproof/treed_build.go index a59ef6624..a7cc83d4b 100644 --- a/provider/lpproof/treed_build.go +++ b/provider/lpproof/treed_build.go @@ -1,19 +1,21 @@ package lpproof import ( - commcid "github.com/filecoin-project/go-fil-commcid" - "github.com/filecoin-project/go-state-types/abi" - "github.com/hashicorp/go-multierror" - "github.com/ipfs/go-cid" - pool "github.com/libp2p/go-buffer-pool" - "github.com/minio/sha256-simd" - "golang.org/x/xerrors" "io" "math/bits" "os" "runtime" "sync" "time" + + "github.com/hashicorp/go-multierror" + "github.com/ipfs/go-cid" + pool "github.com/libp2p/go-buffer-pool" + "github.com/minio/sha256-simd" + "golang.org/x/xerrors" + + commcid "github.com/filecoin-project/go-fil-commcid" + "github.com/filecoin-project/go-state-types/abi" ) const nodeSize = 32 diff --git a/provider/lpproof/treed_build_test.go b/provider/lpproof/treed_build_test.go index 6c69c4ed1..6d9d2c67d 100644 --- a/provider/lpproof/treed_build_test.go +++ b/provider/lpproof/treed_build_test.go @@ -4,14 +4,16 @@ import ( "bytes" "crypto/rand" "fmt" - "github.com/filecoin-project/go-state-types/abi" - pool "github.com/libp2p/go-buffer-pool" - "github.com/stretchr/testify/require" "io/ioutil" "os" "path/filepath" "runtime" "testing" + + pool "github.com/libp2p/go-buffer-pool" + "github.com/stretchr/testify/require" + + "github.com/filecoin-project/go-state-types/abi" ) func TestTreeSize(t *testing.T) { diff --git a/provider/lpseal/poller.go b/provider/lpseal/poller.go index f2703067d..1def0d061 100644 --- a/provider/lpseal/poller.go +++ b/provider/lpseal/poller.go @@ -2,17 +2,20 @@ package lpseal import ( "context" + "time" + + logging "github.com/ipfs/go-log/v2" + "golang.org/x/xerrors" + "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/lotus/chain/actors/builtin/miner" "github.com/filecoin-project/lotus/chain/actors/policy" "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/lib/harmony/harmonydb" "github.com/filecoin-project/lotus/lib/harmony/harmonytask" "github.com/filecoin-project/lotus/lib/promise" - logging "github.com/ipfs/go-log/v2" - "golang.org/x/xerrors" - "time" ) var log = logging.Logger("lpseal") diff --git a/provider/lpseal/sector_num_alloc.go b/provider/lpseal/sector_num_alloc.go index 23a97cd96..4b9ca1ad3 100644 --- a/provider/lpseal/sector_num_alloc.go +++ b/provider/lpseal/sector_num_alloc.go @@ -2,13 +2,16 @@ package lpseal import ( "context" + + "golang.org/x/xerrors" + "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-bitfield" rlepluslazy "github.com/filecoin-project/go-bitfield/rle" "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/lib/harmony/harmonydb" - "golang.org/x/xerrors" ) type AllocAPI interface { diff --git a/provider/lpseal/task_porep.go b/provider/lpseal/task_porep.go index 96d0dc56c..f13acab3d 100644 --- a/provider/lpseal/task_porep.go +++ b/provider/lpseal/task_porep.go @@ -3,17 +3,20 @@ package lpseal import ( "bytes" "context" + + "github.com/ipfs/go-cid" + "golang.org/x/xerrors" + "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/crypto" + "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/lib/harmony/harmonydb" "github.com/filecoin-project/lotus/lib/harmony/harmonytask" "github.com/filecoin-project/lotus/lib/harmony/resources" "github.com/filecoin-project/lotus/provider/lpffi" "github.com/filecoin-project/lotus/storage/sealer/storiface" - "github.com/ipfs/go-cid" - "golang.org/x/xerrors" ) type PoRepAPI interface { diff --git a/provider/lpseal/task_sdr.go b/provider/lpseal/task_sdr.go index 0c6c3e5e5..4a59d205e 100644 --- a/provider/lpseal/task_sdr.go +++ b/provider/lpseal/task_sdr.go @@ -3,10 +3,14 @@ package lpseal import ( "bytes" "context" + + "golang.org/x/xerrors" + "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-commp-utils/zerocomm" "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/crypto" + "github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/chain/actors/policy" "github.com/filecoin-project/lotus/chain/types" @@ -15,7 +19,6 @@ import ( "github.com/filecoin-project/lotus/lib/harmony/resources" "github.com/filecoin-project/lotus/provider/lpffi" "github.com/filecoin-project/lotus/storage/sealer/storiface" - "golang.org/x/xerrors" ) var isDevnet = build.BlockDelaySecs < 30 diff --git a/provider/lpseal/task_submit_commit.go b/provider/lpseal/task_submit_commit.go index 4967098ad..793b15618 100644 --- a/provider/lpseal/task_submit_commit.go +++ b/provider/lpseal/task_submit_commit.go @@ -3,10 +3,14 @@ package lpseal import ( "bytes" "context" + + "golang.org/x/xerrors" + "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/big" "github.com/filecoin-project/go-state-types/builtin" + "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/chain/actors/builtin/miner" "github.com/filecoin-project/lotus/chain/types" @@ -16,7 +20,6 @@ import ( "github.com/filecoin-project/lotus/provider/lpmessage" "github.com/filecoin-project/lotus/storage/ctladdr" sealing "github.com/filecoin-project/lotus/storage/pipeline" - "golang.org/x/xerrors" ) type SubmitCommitAPI interface { diff --git a/provider/lpseal/task_submit_precommit.go b/provider/lpseal/task_submit_precommit.go index 99b1a0655..e1d98df50 100644 --- a/provider/lpseal/task_submit_precommit.go +++ b/provider/lpseal/task_submit_precommit.go @@ -3,11 +3,16 @@ package lpseal import ( "bytes" "context" + + "github.com/ipfs/go-cid" + "golang.org/x/xerrors" + "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/big" "github.com/filecoin-project/go-state-types/builtin" miner12 "github.com/filecoin-project/go-state-types/builtin/v12/miner" + "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/chain/actors/builtin/miner" "github.com/filecoin-project/lotus/chain/types" @@ -17,8 +22,6 @@ import ( "github.com/filecoin-project/lotus/provider/lpmessage" "github.com/filecoin-project/lotus/storage/ctladdr" sealing "github.com/filecoin-project/lotus/storage/pipeline" - "github.com/ipfs/go-cid" - "golang.org/x/xerrors" ) type SubmitPrecommitTaskApi interface { diff --git a/provider/lpseal/task_trees.go b/provider/lpseal/task_trees.go index d764af3ef..67fe86d7f 100644 --- a/provider/lpseal/task_trees.go +++ b/provider/lpseal/task_trees.go @@ -2,15 +2,18 @@ package lpseal import ( "context" + + "golang.org/x/xerrors" + "github.com/filecoin-project/go-commp-utils/zerocomm" "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/lotus/lib/harmony/harmonydb" "github.com/filecoin-project/lotus/lib/harmony/harmonytask" "github.com/filecoin-project/lotus/lib/harmony/resources" "github.com/filecoin-project/lotus/provider/lpffi" "github.com/filecoin-project/lotus/storage/pipeline/lib/nullreader" "github.com/filecoin-project/lotus/storage/sealer/storiface" - "golang.org/x/xerrors" ) type TreesTask struct { diff --git a/storage/paths/http_handler.go b/storage/paths/http_handler.go index ec0453ed8..058dee71f 100644 --- a/storage/paths/http_handler.go +++ b/storage/paths/http_handler.go @@ -3,7 +3,6 @@ package paths import ( "bytes" "encoding/json" - "github.com/ipfs/go-cid" "io" "net/http" "os" @@ -11,6 +10,7 @@ import ( "time" "github.com/gorilla/mux" + "github.com/ipfs/go-cid" logging "github.com/ipfs/go-log/v2" "golang.org/x/xerrors" diff --git a/storage/paths/interface.go b/storage/paths/interface.go index f34c4c907..a16821bd9 100644 --- a/storage/paths/interface.go +++ b/storage/paths/interface.go @@ -2,9 +2,10 @@ package paths import ( "context" - "github.com/ipfs/go-cid" "io" + "github.com/ipfs/go-cid" + "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/lotus/storage/sealer/fsutil" diff --git a/storage/paths/local.go b/storage/paths/local.go index a7f81f2eb..28e620a99 100644 --- a/storage/paths/local.go +++ b/storage/paths/local.go @@ -3,7 +3,6 @@ package paths import ( "context" "encoding/json" - "github.com/ipfs/go-cid" "math/bits" "math/rand" "os" @@ -11,6 +10,7 @@ import ( "sync" "time" + "github.com/ipfs/go-cid" "golang.org/x/xerrors" ffi "github.com/filecoin-project/filecoin-ffi" diff --git a/storage/paths/mocks/store.go b/storage/paths/mocks/store.go index 72be48323..19eebaab9 100644 --- a/storage/paths/mocks/store.go +++ b/storage/paths/mocks/store.go @@ -9,6 +9,7 @@ import ( reflect "reflect" gomock "github.com/golang/mock/gomock" + cid "github.com/ipfs/go-cid" abi "github.com/filecoin-project/go-state-types/abi" @@ -85,6 +86,21 @@ func (mr *MockStoreMockRecorder) GenerateSingleVanillaProof(arg0, arg1, arg2, ar return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GenerateSingleVanillaProof", reflect.TypeOf((*MockStore)(nil).GenerateSingleVanillaProof), arg0, arg1, arg2, arg3) } +// GenetartePoRepVanillaProof mocks base method. +func (m *MockStore) GenetartePoRepVanillaProof(arg0 context.Context, arg1 storiface.SectorRef, arg2, arg3 cid.Cid, arg4 abi.SealRandomness, arg5 abi.InteractiveSealRandomness) ([]byte, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GenetartePoRepVanillaProof", arg0, arg1, arg2, arg3, arg4, arg5) + ret0, _ := ret[0].([]byte) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GenetartePoRepVanillaProof indicates an expected call of GenetartePoRepVanillaProof. +func (mr *MockStoreMockRecorder) GenetartePoRepVanillaProof(arg0, arg1, arg2, arg3, arg4, arg5 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GenetartePoRepVanillaProof", reflect.TypeOf((*MockStore)(nil).GenetartePoRepVanillaProof), arg0, arg1, arg2, arg3, arg4, arg5) +} + // MoveStorage mocks base method. func (m *MockStore) MoveStorage(arg0 context.Context, arg1 storiface.SectorRef, arg2 storiface.SectorFileType) error { m.ctrl.T.Helper() diff --git a/storage/paths/remote.go b/storage/paths/remote.go index d9c232abe..f72400c5e 100644 --- a/storage/paths/remote.go +++ b/storage/paths/remote.go @@ -4,7 +4,6 @@ import ( "context" "encoding/json" "fmt" - "github.com/ipfs/go-cid" "io" "math/bits" "net/http" @@ -18,6 +17,7 @@ import ( "time" "github.com/hashicorp/go-multierror" + "github.com/ipfs/go-cid" "golang.org/x/xerrors" "github.com/filecoin-project/go-state-types/abi" From 7798916a9ab12d2270b2eb13f34766f8310d3b1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 12 Jan 2024 13:38:43 +0100 Subject: [PATCH 126/394] lpweb: Fix dev mode --- provider/lpweb/hapi/web/cluster_task_history.gohtml | 6 +++++- provider/lpweb/srv.go | 5 +---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/provider/lpweb/hapi/web/cluster_task_history.gohtml b/provider/lpweb/hapi/web/cluster_task_history.gohtml index 8f04ef5c5..c9b36ce3a 100644 --- a/provider/lpweb/hapi/web/cluster_task_history.gohtml +++ b/provider/lpweb/hapi/web/cluster_task_history.gohtml @@ -8,7 +8,11 @@ {{.Start}} {{.End}} {{if .Result}}success{{else}}error{{end}} - {{.Err}} + +

+ {{.Err}} +
+ {{end}} {{end}} diff --git a/provider/lpweb/srv.go b/provider/lpweb/srv.go index f6bcfcf85..fcb167309 100644 --- a/provider/lpweb/srv.go +++ b/provider/lpweb/srv.go @@ -39,12 +39,9 @@ func GetSrv(ctx context.Context, deps *deps.Deps) (*http.Server, error) { } api.Routes(mx.PathPrefix("/api").Subrouter(), deps) - basePath := basePath - var static fs.FS = static if webDev { - basePath = "cmd/lotus-provider/web/static" - static = os.DirFS(basePath) + static = os.DirFS("./cmd/lotus-provider/web") } mx.NotFoundHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { From 6c8605c077c559b531ca3fb246538f7a57567acb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 12 Jan 2024 14:09:12 +0100 Subject: [PATCH 127/394] lpweb: Fix actor display --- provider/lpweb/hapi/robust_rpc.go | 89 ++++++++++++++ provider/lpweb/hapi/routes.go | 3 + provider/lpweb/hapi/simpleinfo.go | 4 + provider/lpweb/hapi/watch_actor.go | 181 +++++++++++++++++++++++++++++ provider/lpweb/static/index.html | 4 +- 5 files changed, 279 insertions(+), 2 deletions(-) create mode 100644 provider/lpweb/hapi/robust_rpc.go create mode 100644 provider/lpweb/hapi/watch_actor.go diff --git a/provider/lpweb/hapi/robust_rpc.go b/provider/lpweb/hapi/robust_rpc.go new file mode 100644 index 000000000..24274f329 --- /dev/null +++ b/provider/lpweb/hapi/robust_rpc.go @@ -0,0 +1,89 @@ +package hapi + +import ( + "context" + "github.com/filecoin-project/lotus/api/client" + cliutil "github.com/filecoin-project/lotus/cli/util" + "time" +) + +func (a *app) watchRpc() { + ticker := time.NewTicker(watchInterval) + for { + err := a.updateRpc(context.TODO()) + if err != nil { + log.Errorw("updating rpc info", "error", err) + } + select { + case <-ticker.C: + } + } +} + +type minimalApiInfo struct { + Apis struct { + ChainApiInfo []string + } +} + +func (a *app) updateRpc(ctx context.Context) error { + rpcInfos := map[string]minimalApiInfo{} // config name -> api info + confNameToAddr := map[string]string{} // config name -> api address + + err := forEachConfig[minimalApiInfo](a, func(name string, info minimalApiInfo) error { + if len(info.Apis.ChainApiInfo) == 0 { + return nil + } + + rpcInfos[name] = info + + for _, addr := range info.Apis.ChainApiInfo { + ai := cliutil.ParseApiInfo(addr) + confNameToAddr[name] = ai.Addr + } + + return nil + }) + if err != nil { + return err + } + + apiInfos := map[string][]byte{} // api address -> token + + // for dedup by address + for _, info := range rpcInfos { + ai := cliutil.ParseApiInfo(info.Apis.ChainApiInfo[0]) + apiInfos[ai.Addr] = ai.Token + } + + a.rpcInfoLk.Lock() + + // todo improve this shared rpc logic + if a.workingApi == nil { + for addr, token := range apiInfos { + ai := cliutil.APIInfo{ + Addr: addr, + Token: token, + } + + da, err := ai.DialArgs("v1") + if err != nil { + continue + } + + ah := ai.AuthHeader() + + v1api, closer, err := client.NewFullNodeRPCV1(ctx, da, ah) + if err != nil { + continue + } + _ = closer // todo + + a.workingApi = v1api + } + } + + a.rpcInfoLk.Unlock() + + return nil +} diff --git a/provider/lpweb/hapi/routes.go b/provider/lpweb/hapi/routes.go index b07ab60a5..7a2e3d9be 100644 --- a/provider/lpweb/hapi/routes.go +++ b/provider/lpweb/hapi/routes.go @@ -25,6 +25,9 @@ func Routes(r *mux.Router, deps *deps.Deps) error { t: t, } + go a.watchRpc() + go a.watchActor() + r.HandleFunc("/simpleinfo/actorsummary", a.actorSummary) r.HandleFunc("/simpleinfo/machines", a.indexMachines) r.HandleFunc("/simpleinfo/tasks", a.indexTasks) diff --git a/provider/lpweb/hapi/simpleinfo.go b/provider/lpweb/hapi/simpleinfo.go index ee36a1e17..40b4b3ed6 100644 --- a/provider/lpweb/hapi/simpleinfo.go +++ b/provider/lpweb/hapi/simpleinfo.go @@ -2,6 +2,7 @@ package hapi import ( "context" + "github.com/filecoin-project/lotus/api/v1api" "html/template" "net/http" "os" @@ -15,6 +16,9 @@ type app struct { db *harmonydb.DB t *template.Template + rpcInfoLk sync.Mutex + workingApi v1api.FullNode + actorInfoLk sync.Mutex actorInfos []actorInfo } diff --git a/provider/lpweb/hapi/watch_actor.go b/provider/lpweb/hapi/watch_actor.go new file mode 100644 index 000000000..31cf9ac1f --- /dev/null +++ b/provider/lpweb/hapi/watch_actor.go @@ -0,0 +1,181 @@ +package hapi + +import ( + "context" + "github.com/BurntSushi/toml" + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/lotus/chain/types" + "golang.org/x/xerrors" + "sort" + "time" +) + +const watchInterval = time.Second * 10 + +func (a *app) watchActor() { + ticker := time.NewTicker(watchInterval) + for { + err := a.updateActor(context.TODO()) + if err != nil { + log.Errorw("updating rpc info", "error", err) + } + select { + case <-ticker.C: + } + } +} + +type minimalActorInfo struct { + Addresses struct { + MinerAddresses []string + } +} + +func (a *app) updateActor(ctx context.Context) error { + a.rpcInfoLk.Lock() + api := a.workingApi + a.rpcInfoLk.Unlock() + + if api == nil { + log.Warnw("no working api yet") + return nil + } + + var actorInfos []actorInfo + + confNameToAddr := map[address.Address][]string{} // address -> config names + + err := forEachConfig[minimalActorInfo](a, func(name string, info minimalActorInfo) error { + for _, addr := range info.Addresses.MinerAddresses { + a, err := address.NewFromString(addr) + if err != nil { + return xerrors.Errorf("parsing address: %w", err) + } + confNameToAddr[a] = append(confNameToAddr[a], name) + } + + return nil + }) + if err != nil { + return err + } + + for addr, cnames := range confNameToAddr { + p, err := api.StateMinerPower(ctx, addr, types.EmptyTSK) + if err != nil { + return xerrors.Errorf("getting miner power: %w", err) + } + + dls, err := api.StateMinerDeadlines(ctx, addr, types.EmptyTSK) + if err != nil { + return xerrors.Errorf("getting deadlines: %w", err) + } + + outDls := []actorDeadline{} + + for dlidx := range dls { + p, err := api.StateMinerPartitions(ctx, addr, uint64(dlidx), types.EmptyTSK) + if err != nil { + return xerrors.Errorf("getting partition: %w", err) + } + + dl := actorDeadline{ + Empty: false, + Current: false, // todo + Proven: false, + PartFaulty: false, + Faulty: false, + } + + var live, faulty uint64 + + for _, part := range p { + l, err := part.LiveSectors.Count() + if err != nil { + return xerrors.Errorf("getting live sectors: %w", err) + } + live += l + + f, err := part.FaultySectors.Count() + if err != nil { + return xerrors.Errorf("getting faulty sectors: %w", err) + } + faulty += f + } + + dl.Empty = live == 0 + dl.Proven = live > 0 && faulty == 0 + dl.PartFaulty = faulty > 0 + dl.Faulty = faulty > 0 && faulty == live + + outDls = append(outDls, dl) + } + + pd, err := api.StateMinerProvingDeadline(ctx, addr, types.EmptyTSK) + if err != nil { + return xerrors.Errorf("getting proving deadline: %w", err) + } + + if len(outDls) != 48 { + return xerrors.Errorf("expected 48 deadlines, got %d", len(outDls)) + } + + outDls[pd.Index].Current = true + + actorInfos = append(actorInfos, actorInfo{ + Address: addr.String(), + CLayers: cnames, + QualityAdjustedPower: types.DeciStr(p.MinerPower.QualityAdjPower), + RawBytePower: types.DeciStr(p.MinerPower.RawBytePower), + Deadlines: outDls, + }) + } + + sort.Slice(actorInfos, func(i, j int) bool { + return actorInfos[i].Address < actorInfos[j].Address + }) + + a.actorInfoLk.Lock() + a.actorInfos = actorInfos + a.actorInfoLk.Unlock() + + return nil +} + +func (a *app) loadConfigs(ctx context.Context) (map[string]string, error) { + rows, err := a.db.Query(ctx, `SELECT title, config FROM harmony_config`) + if err != nil { + return nil, xerrors.Errorf("getting db configs: %w", err) + } + + configs := make(map[string]string) + for rows.Next() { + var title, config string + if err := rows.Scan(&title, &config); err != nil { + return nil, xerrors.Errorf("scanning db configs: %w", err) + } + configs[title] = config + } + + return configs, nil +} + +func forEachConfig[T any](a *app, cb func(name string, v T) error) error { + confs, err := a.loadConfigs(context.Background()) + if err != nil { + return err + } + + for name, tomlStr := range confs { + var info T + if err := toml.Unmarshal([]byte(tomlStr), &info); err != nil { + return xerrors.Errorf("unmarshaling %s config: %w", name, err) + } + + if err := cb(name, info); err != nil { + return xerrors.Errorf("cb: %w", err) + } + } + + return nil +} diff --git a/provider/lpweb/static/index.html b/provider/lpweb/static/index.html index 98f7336ad..d0ca8eb16 100644 --- a/provider/lpweb/static/index.html +++ b/provider/lpweb/static/index.html @@ -168,7 +168,7 @@ Message - +
@@ -184,7 +184,7 @@ Owner - +
From afc124ab2ad4865586e4f2fd647680a08146244c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 12 Jan 2024 14:14:40 +0100 Subject: [PATCH 128/394] harmony: utc times in task_history queries --- lib/harmony/harmonytask/harmonytask.go | 2 +- lib/harmony/harmonytask/task_type_handler.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/harmony/harmonytask/harmonytask.go b/lib/harmony/harmonytask/harmonytask.go index 7577c5cf5..c56b83b95 100644 --- a/lib/harmony/harmonytask/harmonytask.go +++ b/lib/harmony/harmonytask/harmonytask.go @@ -233,7 +233,7 @@ func (e *TaskEngine) followWorkInDB() { for fromName, srcs := range e.follows { var cList []int // Which work is done (that we follow) since we last checked? err := e.db.Select(e.ctx, &cList, `SELECT h.task_id FROM harmony_task_history - WHERE h.work_end>$1 AND h.name=$2`, lastFollowTime, fromName) + WHERE h.work_end>$1 AND h.name=$2`, lastFollowTime.UTC(), fromName) if err != nil { log.Error("Could not query DB: ", err) return diff --git a/lib/harmony/harmonytask/task_type_handler.go b/lib/harmony/harmonytask/task_type_handler.go index a6e8933d2..a4eb004c2 100644 --- a/lib/harmony/harmonytask/task_type_handler.go +++ b/lib/harmony/harmonytask/task_type_handler.go @@ -218,7 +218,7 @@ retryRecordCompletion: } _, err = tx.Exec(`INSERT INTO harmony_task_history (task_id, name, posted, work_start, work_end, result, completed_by_host_and_port, err) -VALUES ($1, $2, $3, $4, $5, $6, $7, $8)`, tID, h.Name, postedTime, workStart, workEnd, done, h.TaskEngine.hostAndPort, result) +VALUES ($1, $2, $3, $4, $5, $6, $7, $8)`, tID, h.Name, postedTime.UTC(), workStart.UTC(), workEnd.UTC(), done, h.TaskEngine.hostAndPort, result) if err != nil { return false, fmt.Errorf("could not write history: %w", err) } From 0bd4d7dd8b66ff5304aa2cf1583c8b300564b0be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 12 Jan 2024 16:16:35 +0100 Subject: [PATCH 129/394] lpweb: Pipeline view --- lib/harmony/harmonytask/task_type_handler.go | 1 + provider/lpweb/hapi/routes.go | 1 + provider/lpweb/hapi/simpleinfo.go | 76 ++++++++++++++++++- .../hapi/web/cluster_task_history.gohtml | 3 +- provider/lpweb/hapi/web/pipline_porep.gohtml | 15 ++++ provider/lpweb/static/index.html | 61 ++++++++++----- 6 files changed, 133 insertions(+), 24 deletions(-) create mode 100644 provider/lpweb/hapi/web/pipline_porep.gohtml diff --git a/lib/harmony/harmonytask/task_type_handler.go b/lib/harmony/harmonytask/task_type_handler.go index a4eb004c2..0a25fc507 100644 --- a/lib/harmony/harmonytask/task_type_handler.go +++ b/lib/harmony/harmonytask/task_type_handler.go @@ -173,6 +173,7 @@ retryRecordCompletion: cm, err := h.TaskEngine.db.BeginTransaction(h.TaskEngine.ctx, func(tx *harmonydb.Tx) (bool, error) { var postedTime time.Time err := tx.QueryRow(`SELECT posted_time FROM harmony_task WHERE id=$1`, tID).Scan(&postedTime) + if err != nil { return false, fmt.Errorf("could not log completion: %w ", err) } diff --git a/provider/lpweb/hapi/routes.go b/provider/lpweb/hapi/routes.go index 7a2e3d9be..47f924fab 100644 --- a/provider/lpweb/hapi/routes.go +++ b/provider/lpweb/hapi/routes.go @@ -32,6 +32,7 @@ func Routes(r *mux.Router, deps *deps.Deps) error { r.HandleFunc("/simpleinfo/machines", a.indexMachines) r.HandleFunc("/simpleinfo/tasks", a.indexTasks) r.HandleFunc("/simpleinfo/taskhistory", a.indexTasksHistory) + r.HandleFunc("/simpleinfo/pipeline-porep", a.indexPipelinePorep) return nil } diff --git a/provider/lpweb/hapi/simpleinfo.go b/provider/lpweb/hapi/simpleinfo.go index 40b4b3ed6..c08dcee08 100644 --- a/provider/lpweb/hapi/simpleinfo.go +++ b/provider/lpweb/hapi/simpleinfo.go @@ -3,6 +3,7 @@ package hapi import ( "context" "github.com/filecoin-project/lotus/api/v1api" + "golang.org/x/xerrors" "html/template" "net/http" "os" @@ -81,6 +82,17 @@ func (a *app) indexTasksHistory(w http.ResponseWriter, r *http.Request) { a.executeTemplate(w, "cluster_task_history", s) } +func (a *app) indexPipelinePorep(w http.ResponseWriter, r *http.Request) { + s, err := a.porepPipelineSummary(r.Context()) + if err != nil { + log.Errorf("porep pipeline summary: %v", err) + http.Error(w, "internal server error", http.StatusInternalServerError) + return + } + + a.executeTemplate(w, "pipeline_porep", s) +} + var templateDev = os.Getenv("LOTUS_WEB_DEV") == "1" func (a *app) executeTemplate(w http.ResponseWriter, name string, data interface{}) { @@ -111,7 +123,7 @@ type taskHistorySummary struct { Name string TaskID int64 - Posted, Start, End string + Posted, Start, Queued, Took string Result bool Err string @@ -181,11 +193,67 @@ func (a *app) clusterTaskHistorySummary(ctx context.Context) ([]taskHistorySumma return nil, err // Handle error } - t.Posted = posted.Round(time.Second).Format("02 Jan 06 15:04") - t.Start = start.Round(time.Second).Format("02 Jan 06 15:04") - t.End = end.Round(time.Second).Format("02 Jan 06 15:04") + t.Posted = posted.Local().Round(time.Second).Format("02 Jan 06 15:04") + t.Start = start.Local().Round(time.Second).Format("02 Jan 06 15:04") + //t.End = end.Local().Round(time.Second).Format("02 Jan 06 15:04") + + t.Queued = start.Sub(posted).Round(time.Second).String() + if t.Queued == "0s" { + t.Queued = start.Sub(posted).Round(time.Millisecond).String() + } + + t.Took = end.Sub(start).Round(time.Second).String() + if t.Took == "0s" { + t.Took = end.Sub(start).Round(time.Millisecond).String() + } summaries = append(summaries, t) } return summaries, nil } + +type porepPipelineSummary struct { + Actor string + + CountSDR int + CountTrees int + CountPrecommitMsg int + CountWaitSeed int + CountPoRep int + CountCommitMsg int + CountDone int + CountFailed int +} + +func (a *app) porepPipelineSummary(ctx context.Context) ([]porepPipelineSummary, error) { + rows, err := a.db.Query(ctx, ` + SELECT + sp_id, + COUNT(*) FILTER (WHERE after_sdr = true AND after_tree_d = false) as CountSDR, + COUNT(*) FILTER (WHERE (after_tree_d = true OR after_tree_c = true OR after_tree_r = true) AND after_precommit_msg = false) as CountTrees, + COUNT(*) FILTER (WHERE after_precommit_msg = true AND after_precommit_msg_success = false) as CountPrecommitMsg, + COUNT(*) FILTER (WHERE after_precommit_msg_success = true AND after_porep = false) as CountWaitSeed, + COUNT(*) FILTER (WHERE after_porep = true AND after_commit_msg = false) as CountPoRep, + COUNT(*) FILTER (WHERE after_commit_msg = true AND after_commit_msg_success = false) as CountCommitMsg, + COUNT(*) FILTER (WHERE after_commit_msg_success = true) as CountDone, + COUNT(*) FILTER (WHERE failed = true) as CountFailed + FROM + sectors_sdr_pipeline + GROUP BY sp_id`) + if err != nil { + return nil, xerrors.Errorf("query: %w", err) + } + defer rows.Close() + + var summaries []porepPipelineSummary + for rows.Next() { + var summary porepPipelineSummary + if err := rows.Scan(&summary.Actor, &summary.CountSDR, &summary.CountTrees, &summary.CountPrecommitMsg, &summary.CountWaitSeed, &summary.CountPoRep, &summary.CountCommitMsg, &summary.CountDone, &summary.CountFailed); err != nil { + return nil, xerrors.Errorf("scan: %w", err) + } + summary.Actor = "f0" + summary.Actor + + summaries = append(summaries, summary) + } + return summaries, nil +} diff --git a/provider/lpweb/hapi/web/cluster_task_history.gohtml b/provider/lpweb/hapi/web/cluster_task_history.gohtml index c9b36ce3a..f95dbb2b2 100644 --- a/provider/lpweb/hapi/web/cluster_task_history.gohtml +++ b/provider/lpweb/hapi/web/cluster_task_history.gohtml @@ -6,7 +6,8 @@ {{.CompletedBy}} {{.Posted}} {{.Start}} - {{.End}} + {{.Queued}} + {{.Took}} {{if .Result}}success{{else}}error{{end}}
diff --git a/provider/lpweb/hapi/web/pipline_porep.gohtml b/provider/lpweb/hapi/web/pipline_porep.gohtml new file mode 100644 index 000000000..5e7c7f7c6 --- /dev/null +++ b/provider/lpweb/hapi/web/pipline_porep.gohtml @@ -0,0 +1,15 @@ +{{define "pipeline_porep"}} + {{range .}} + + {{.Actor}} + {{.CountSDR}} + {{.CountTrees}} + {{.CountPrecommitMsg}} + {{.CountWaitSeed}} + {{.CountPoRep}} + {{.CountCommitMsg}} + {{.CountDone}} + {{.CountFailed}} + + {{end}} +{{end}} diff --git a/provider/lpweb/static/index.html b/provider/lpweb/static/index.html index d0ca8eb16..80680f1b5 100644 --- a/provider/lpweb/static/index.html +++ b/provider/lpweb/static/index.html @@ -3,6 +3,7 @@ Lotus Provider Cluster Overview + + + +
+
+

Lotus Provider PoRep Pipeline

+
+
+
+
+
+

Sectors

+ + + + + + + + + + +
Sector IDCreate TimeState
+
+
+ + \ No newline at end of file From 684e7d77c6f5d45fceadeea3cf73d3285f5fa7f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sun, 11 Feb 2024 22:47:08 +0100 Subject: [PATCH 169/394] lpseal: Fix finalize with cc sectors --- provider/lpseal/task_finalize.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/provider/lpseal/task_finalize.go b/provider/lpseal/task_finalize.go index f06b307bf..92183655a 100644 --- a/provider/lpseal/task_finalize.go +++ b/provider/lpseal/task_finalize.go @@ -52,7 +52,7 @@ func (f *FinalizeTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (do var keepUnsealed bool - if err := f.db.QueryRow(ctx, `select bool_or(not data_delete_on_finalize) from sectors_sdr_initial_pieces where sp_id=$1 and sector_number=$2`, task.SpID, task.SectorNumber).Scan(&keepUnsealed); err != nil { + if err := f.db.QueryRow(ctx, `select coalesce(bool_or(not data_delete_on_finalize), false) from sectors_sdr_initial_pieces where sp_id=$1 and sector_number=$2`, task.SpID, task.SectorNumber).Scan(&keepUnsealed); err != nil { return false, err } From 67a369d8306e2141b05ff77c02fcff6c336aab71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 12 Feb 2024 13:45:13 +0100 Subject: [PATCH 170/394] lpseal: Handle unlealed in MoveStorage --- provider/lpffi/sdr_funcs.go | 46 ++++++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/provider/lpffi/sdr_funcs.go b/provider/lpffi/sdr_funcs.go index 2ca025f47..6b59d9268 100644 --- a/provider/lpffi/sdr_funcs.go +++ b/provider/lpffi/sdr_funcs.go @@ -328,5 +328,49 @@ func (sb *SealCalls) FinalizeSector(ctx context.Context, sector storiface.Sector } func (sb *SealCalls) MoveStorage(ctx context.Context, sector storiface.SectorRef) error { - return sb.sectors.storage.MoveStorage(ctx, sector, storiface.FTCache|storiface.FTSealed) + // only move the unsealed file if it still exists and needs moving + moveUnsealed := storiface.FTUnsealed + { + found, unsealedPathType, err := sb.sectorStorageType(ctx, sector, storiface.FTUnsealed) + if err != nil { + return xerrors.Errorf("checking cache storage type: %w", err) + } + + if !found || unsealedPathType == storiface.PathStorage { + moveUnsealed = storiface.FTNone + } + } + + toMove := storiface.FTCache | storiface.FTSealed | moveUnsealed + + err := sb.sectors.storage.MoveStorage(ctx, sector, toMove) + if err != nil { + return xerrors.Errorf("moving storage: %w", err) + } + + for _, fileType := range toMove.AllSet() { + if err := sb.sectors.storage.RemoveCopies(ctx, sector.ID, fileType); err != nil { + return xerrors.Errorf("rm copies (t:%s, s:%v): %w", fileType, sector, err) + } + } + + return nil +} + +func (sb *SealCalls) sectorStorageType(ctx context.Context, sector storiface.SectorRef, ft storiface.SectorFileType) (sectorFound bool, ptype storiface.PathType, err error) { + stores, err := sb.sectors.sindex.StorageFindSector(ctx, sector.ID, ft, 0, false) + if err != nil { + return false, "", xerrors.Errorf("finding sector: %w", err) + } + if len(stores) == 0 { + return false, "", nil + } + + for _, store := range stores { + if store.CanSeal { + return true, storiface.PathSealing, nil + } + } + + return true, storiface.PathStorage, nil } From 3c4c286eeb2c8d85c46064a44bd21e5da8717980 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 12 Feb 2024 14:08:38 +0100 Subject: [PATCH 171/394] lpseal: Correct task definition order --- cmd/lotus-provider/tasks/tasks.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cmd/lotus-provider/tasks/tasks.go b/cmd/lotus-provider/tasks/tasks.go index 216251179..eab551551 100644 --- a/cmd/lotus-provider/tasks/tasks.go +++ b/cmd/lotus-provider/tasks/tasks.go @@ -71,6 +71,7 @@ func StartTasks(ctx context.Context, dependencies *deps.Deps) (*harmonytask.Task slr = lpffi.NewSealCalls(stor, lstor, si) } + // NOTE: Tasks with the LEAST priority are at the top if cfg.Subsystems.EnableSealSDR { sdrTask := lpseal.NewSDRTask(full, db, sp, slr, cfg.Subsystems.SealSDRMaxTasks) activeTasks = append(activeTasks, sdrTask) @@ -101,6 +102,11 @@ func StartTasks(ctx context.Context, dependencies *deps.Deps) (*harmonytask.Task "miner_addresses", maddrs, "tasks", lo.Map(activeTasks, func(t harmonytask.TaskInterface, _ int) string { return t.TypeDetails().Name })) + // harmony treats the first task as highest priority, so reverse the order + // (we could have just appended to this list in the reverse order, but defining + // tasks in pipeline order is more intuitive) + activeTasks = lo.Reverse(activeTasks) + ht, err := harmonytask.New(db, activeTasks, dependencies.ListenAddr) if err != nil { return nil, err From f2f1cc30eccc6b6c98fe6922486fc788e00c45aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 12 Feb 2024 15:14:40 +0100 Subject: [PATCH 172/394] lpseal: Appease the linter --- cmd/lotus-shed/lpdeal.go | 10 +++++++++- provider/lpproof/treed_build.go | 1 - provider/lpseal/poller_commit_msg.go | 2 +- provider/lpseal/task_movestorage.go | 3 +++ provider/lpseal/task_trees_test.go | 7 ++++++- 5 files changed, 19 insertions(+), 4 deletions(-) diff --git a/cmd/lotus-shed/lpdeal.go b/cmd/lotus-shed/lpdeal.go index 28b631fda..43d23f597 100644 --- a/cmd/lotus-shed/lpdeal.go +++ b/cmd/lotus-shed/lpdeal.go @@ -559,6 +559,14 @@ var lpBoostProxyCmd = &cli.Command{ fmt.Printf("Token: %s:%s\n", tok, ma) } - return http.ListenAndServe(cctx.String("listen"), mux) + server := &http.Server{ + Addr: cctx.String("listen"), + Handler: mux, + ReadTimeout: 15 * time.Second, + WriteTimeout: 48 * time.Hour, // really high because we block until TreeD + IdleTimeout: 60 * time.Second, + } + + return server.ListenAndServe() }, } diff --git a/provider/lpproof/treed_build.go b/provider/lpproof/treed_build.go index 00d13114c..bf91da8c8 100644 --- a/provider/lpproof/treed_build.go +++ b/provider/lpproof/treed_build.go @@ -22,7 +22,6 @@ import ( const nodeSize = 32 const threadChunkSize = 1 << 20 -const nodesPerChunk = threadChunkSize / nodeSize func hashChunk(data [][]byte) { l1Nodes := len(data[0]) / nodeSize / 2 diff --git a/provider/lpseal/poller_commit_msg.go b/provider/lpseal/poller_commit_msg.go index bbdc85039..a70b709bc 100644 --- a/provider/lpseal/poller_commit_msg.go +++ b/provider/lpseal/poller_commit_msg.go @@ -83,7 +83,7 @@ func (s *SealPoller) pollCommitMsgFail(ctx context.Context, task pollTask, execR fallthrough case exitcode.SysErrOutOfGas: // just retry - return s.pollRetryPrecommitMsgSend(ctx, task, execResult) + return s.pollRetryCommitMsgSend(ctx, task, execResult) default: return xerrors.Errorf("commit message failed with exit code %s", exitcode.ExitCode(execResult.ExecutedRcptExitCode)) } diff --git a/provider/lpseal/task_movestorage.go b/provider/lpseal/task_movestorage.go index d168e49ee..aa16d08e9 100644 --- a/provider/lpseal/task_movestorage.go +++ b/provider/lpseal/task_movestorage.go @@ -64,6 +64,9 @@ func (m *MoveStorageTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) } _, err = m.db.Exec(ctx, `update sectors_sdr_pipeline set after_move_storage=true where task_id_move_storage=$1`, taskID) + if err != nil { + return false, xerrors.Errorf("updating task: %w", err) + } return true, nil } diff --git a/provider/lpseal/task_trees_test.go b/provider/lpseal/task_trees_test.go index d9fd6c41e..204fa57fc 100644 --- a/provider/lpseal/task_trees_test.go +++ b/provider/lpseal/task_trees_test.go @@ -5,6 +5,8 @@ import ( "net/http" "net/http/httptest" "testing" + + "github.com/stretchr/testify/require" ) // TestUrlPieceReader_Read tests various scenarios of reading data from UrlPieceReader @@ -12,7 +14,8 @@ func TestUrlPieceReader_Read(t *testing.T) { // Create a test server testData := "This is a test string." ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - io.WriteString(w, testData) + _, err := io.WriteString(w, testData) + require.NoError(t, err) })) defer ts.Close() @@ -29,6 +32,8 @@ func TestUrlPieceReader_Read(t *testing.T) { } for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { reader := UrlPieceReader{ Url: ts.URL, From ee31932731682c9693b4de49b1a2660b5dc6b98e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 12 Feb 2024 18:03:47 +0100 Subject: [PATCH 173/394] lotus-provider: Move --layers to top-level --- cmd/lotus-provider/main.go | 10 +++++----- cmd/lotus-provider/pipeline.go | 13 ++++--------- cmd/lotus-provider/run.go | 10 ---------- 3 files changed, 9 insertions(+), 24 deletions(-) diff --git a/cmd/lotus-provider/main.go b/cmd/lotus-provider/main.go index a5cd3f452..16834f898 100644 --- a/cmd/lotus-provider/main.go +++ b/cmd/lotus-provider/main.go @@ -48,7 +48,7 @@ func main() { configCmd, testCmd, webCmd, - pipelineCmd, + sealCmd, } jaeger := tracing.SetupJaegerTracing("lotus") @@ -124,10 +124,10 @@ func main() { Hidden: true, Value: "5433", }, - &cli.StringFlag{ - Name: "layers", - EnvVars: []string{"LOTUS_LAYERS", "LOTUS_CONFIG_LAYERS"}, - Value: "base", + &cli.StringSliceFlag{ + Name: "layers", + Usage: "list of layers to be interpreted (atop defaults). Default: base", + Value: cli.NewStringSlice("base"), }, &cli.StringFlag{ Name: deps.FlagRepoPath, diff --git a/cmd/lotus-provider/pipeline.go b/cmd/lotus-provider/pipeline.go index 5447779a7..45a1c3a17 100644 --- a/cmd/lotus-provider/pipeline.go +++ b/cmd/lotus-provider/pipeline.go @@ -17,15 +17,15 @@ import ( "github.com/filecoin-project/lotus/provider/lpseal" ) -var pipelineCmd = &cli.Command{ - Name: "pipeline", +var sealCmd = &cli.Command{ + Name: "seal", Usage: "Manage the sealing pipeline", Subcommands: []*cli.Command{ - pipelineStartCmd, + sealStartCmd, }, } -var pipelineStartCmd = &cli.Command{ +var sealStartCmd = &cli.Command{ Name: "start", Usage: "Start new sealing operations manually", Flags: []cli.Flag{ @@ -52,11 +52,6 @@ var pipelineStartCmd = &cli.Command{ Usage: "Use synthetic PoRep", Value: false, // todo implement synthetic }, - &cli.StringSliceFlag{ // todo consider moving layers top level - Name: "layers", - Usage: "list of layers to be interpreted (atop defaults). Default: base", - Value: cli.NewStringSlice("base"), - }, }, Action: func(cctx *cli.Context) error { if !cctx.Bool("now") { diff --git a/cmd/lotus-provider/run.go b/cmd/lotus-provider/run.go index b5a286ba7..2b6f4ef3b 100644 --- a/cmd/lotus-provider/run.go +++ b/cmd/lotus-provider/run.go @@ -50,11 +50,6 @@ var runCmd = &cli.Command{ Usage: "manage open file limit", Value: true, }, - &cli.StringSliceFlag{ - Name: "layers", - Usage: "list of layers to be interpreted (atop defaults). Default: base", - Value: cli.NewStringSlice("base"), - }, &cli.StringFlag{ Name: "storage-json", Usage: "path to json file containing storage config", @@ -150,11 +145,6 @@ var webCmd = &cli.Command{ Usage: "Address to listen on", Value: "127.0.0.1:4701", }, - &cli.StringSliceFlag{ - Name: "layers", - Usage: "list of layers to be interpreted (atop defaults). Default: base. Web will be added", - Value: cli.NewStringSlice("base"), - }, &cli.BoolFlag{ Name: "nosync", Usage: "don't check full-node sync status", From 9ddfe23702ea99424a77d1d7725a61ab86584d66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 14 Feb 2024 11:09:10 +0100 Subject: [PATCH 174/394] lp: Small migration improvements --- chain/types/fil.go | 4 ++++ cmd/lotus-provider/config_migrate.go | 14 ++++++++----- storage/paths/db_index.go | 31 +++++++++++++++++----------- 3 files changed, 32 insertions(+), 17 deletions(-) diff --git a/chain/types/fil.go b/chain/types/fil.go index 2a0ccb460..d20b3c021 100644 --- a/chain/types/fil.go +++ b/chain/types/fil.go @@ -77,6 +77,10 @@ func (f FIL) MarshalText() (text []byte, err error) { } func (f FIL) UnmarshalText(text []byte) error { + if f.Int == nil { + return fmt.Errorf("cannot unmarshal into nil BigInt (text:%s)", string(text)) + } + p, err := ParseFIL(string(text)) if err != nil { return err diff --git a/cmd/lotus-provider/config_migrate.go b/cmd/lotus-provider/config_migrate.go index 805c404bd..7b395d5a9 100644 --- a/cmd/lotus-provider/config_migrate.go +++ b/cmd/lotus-provider/config_migrate.go @@ -37,7 +37,7 @@ var configMigrateCmd = &cli.Command{ Aliases: []string{FlagMinerRepoDeprecation}, EnvVars: []string{"LOTUS_MINER_PATH", "LOTUS_STORAGE_PATH"}, Value: "~/.lotusminer", - Usage: fmt.Sprintf("Specify miner repo path. flag(%s) and env(LOTUS_STORAGE_PATH) are DEPRECATION, will REMOVE SOON", FlagMinerRepoDeprecation), + Usage: "Miner repo path", }, &cli.StringFlag{ Name: "repo", @@ -124,7 +124,7 @@ func fromMiner(cctx *cli.Context) (err error) { if err != nil { return fmt.Errorf("could not read config.toml: %w", err) } - var lpCfg config.LotusProviderConfig + lpCfg := config.DefaultLotusProvider() _, err = deps.LoadConfigWithUpgrades(string(buf), &lpCfg) if err != nil { return fmt.Errorf("could not decode toml: %w", err) @@ -177,12 +177,16 @@ func fromMiner(cctx *cli.Context) (err error) { } lpCfg.Apis.ChainApiInfo = []string{header.Get("Authorization")[7:] + ":" + ainfo.Addr} - // Enable WindowPoSt - lpCfg.Subsystems.EnableWindowPost = true - msg += "\nBefore running lotus-provider, ensure any miner/worker answering of WindowPost is disabled by " + + // WindowPoSt message + msg += "\n!! Before running lotus-provider with Window PoSt enabled, ensure any miner/worker answering of WindowPost is disabled by " + "(on Miner) " + configColor("DisableBuiltinWindowPoSt=true") + " and (on Workers) not enabling windowpost on CLI or via " + "environment variable " + configColor("LOTUS_WORKER_WINDOWPOST") + "." + // WinningPoSt message + msg += "\n!! Before running lotus-provider with Winning PoSt enabled, ensure any miner/worker answering of WinningPost is disabled by " + + "(on Miner) " + configColor("DisableBuiltinWinningPoSt=true") + " and (on Workers) not enabling winningpost on CLI or via " + + "environment variable " + configColor("LOTUS_WORKER_WINNINGPOST") + "." + // Express as configTOML configTOML := &bytes.Buffer{} if err = toml.NewEncoder(configTOML).Encode(lpCfg); err != nil { diff --git a/storage/paths/db_index.go b/storage/paths/db_index.go index 32afdea1e..1472997b6 100644 --- a/storage/paths/db_index.go +++ b/storage/paths/db_index.go @@ -180,7 +180,7 @@ func (dbi *DBIndex) StorageAttach(ctx context.Context, si storiface.StorageInfo, } } - retryWait := time.Millisecond * 100 + retryWait := time.Millisecond * 20 retryAttachStorage: // Single transaction to attach storage which is not present in the DB _, err := dbi.harmonyDB.BeginTransaction(ctx, func(tx *harmonydb.Tx) (commit bool, err error) { @@ -289,7 +289,7 @@ func (dbi *DBIndex) StorageDetach(ctx context.Context, id storiface.ID, url stri log.Warnw("Dropping sector path endpoint", "path", id, "url", url) } else { - retryWait := time.Millisecond * 100 + retryWait := time.Millisecond * 20 retryDropPath: // Single transaction to drop storage path and sector decls which have this as a storage path _, err := dbi.harmonyDB.BeginTransaction(ctx, func(tx *harmonydb.Tx) (commit bool, err error) { @@ -321,15 +321,9 @@ func (dbi *DBIndex) StorageDetach(ctx context.Context, id storiface.ID, url stri } func (dbi *DBIndex) StorageReportHealth(ctx context.Context, id storiface.ID, report storiface.HealthReport) error { - - var canSeal, canStore bool - err := dbi.harmonyDB.QueryRow(ctx, - "SELECT can_seal, can_store FROM storage_path WHERE storage_id=$1", id).Scan(&canSeal, &canStore) - if err != nil { - return xerrors.Errorf("Querying for storage id %s fails with err %v", id, err) - } - - _, err = dbi.harmonyDB.Exec(ctx, + retryWait := time.Millisecond * 20 +retryReportHealth: + _, err := dbi.harmonyDB.Exec(ctx, "UPDATE storage_path set capacity=$1, available=$2, fs_available=$3, reserved=$4, used=$5, last_heartbeat=NOW()", report.Stat.Capacity, report.Stat.Available, @@ -337,7 +331,20 @@ func (dbi *DBIndex) StorageReportHealth(ctx context.Context, id storiface.ID, re report.Stat.Reserved, report.Stat.Used) if err != nil { - return xerrors.Errorf("updating storage health in DB fails with err: %v", err) + //return xerrors.Errorf("updating storage health in DB fails with err: %v", err) + if harmonydb.IsErrSerialization(err) { + time.Sleep(retryWait) + retryWait *= 2 + goto retryReportHealth + } + return err + } + + var canSeal, canStore bool + err = dbi.harmonyDB.QueryRow(ctx, + "SELECT can_seal, can_store FROM storage_path WHERE storage_id=$1", id).Scan(&canSeal, &canStore) + if err != nil { + return xerrors.Errorf("Querying for storage id %s fails with err %v", id, err) } if report.Stat.Capacity > 0 { From 626d87f5aa54c78aed862e9127472b7266f8fab7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 14 Feb 2024 14:26:55 +0100 Subject: [PATCH 175/394] lpseal: Fix hasAnySealingTask --- cmd/lotus-provider/tasks/tasks.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/cmd/lotus-provider/tasks/tasks.go b/cmd/lotus-provider/tasks/tasks.go index eab551551..00cb6480f 100644 --- a/cmd/lotus-provider/tasks/tasks.go +++ b/cmd/lotus-provider/tasks/tasks.go @@ -58,7 +58,12 @@ func StartTasks(ctx context.Context, dependencies *deps.Deps) (*harmonytask.Task } } - hasAnySealingTask := cfg.Subsystems.EnableSealSDR + hasAnySealingTask := cfg.Subsystems.EnableSealSDR || + cfg.Subsystems.EnableSealSDRTrees || + cfg.Subsystems.EnableSendPrecommitMsg || + cfg.Subsystems.EnablePoRepProof || + cfg.Subsystems.EnableMoveStorage || + cfg.Subsystems.EnableSendCommitMsg { // Sealing From 55a50727fae17691709409621b22d654ee990492 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 14 Feb 2024 15:27:34 +0100 Subject: [PATCH 176/394] lp rpc: Fix StorageStat --- cmd/lotus-provider/rpc/rpc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/lotus-provider/rpc/rpc.go b/cmd/lotus-provider/rpc/rpc.go index b5d7d8dce..54eec45e8 100644 --- a/cmd/lotus-provider/rpc/rpc.go +++ b/cmd/lotus-provider/rpc/rpc.go @@ -145,7 +145,7 @@ func (p *ProviderAPI) StorageLocal(ctx context.Context) (map[storiface.ID]string } func (p *ProviderAPI) StorageStat(ctx context.Context, id storiface.ID) (fsutil.FsStat, error) { - return p.LocalStore.FsStat(ctx, id) + return p.Stor.FsStat(ctx, id) } func (p *ProviderAPI) Version(context.Context) (api.Version, error) { From f6dc31354e31fa3aa66316c992f90f7488699609 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 16 Feb 2024 14:31:13 +0100 Subject: [PATCH 177/394] lotus-provider: Ensure tempdir exists on startup --- cmd/lotus-provider/run.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cmd/lotus-provider/run.go b/cmd/lotus-provider/run.go index 2b6f4ef3b..5135f8a0b 100644 --- a/cmd/lotus-provider/run.go +++ b/cmd/lotus-provider/run.go @@ -78,6 +78,10 @@ var runCmd = &cli.Command{ } } + if err := os.MkdirAll(os.TempDir(), 0755); err != nil { + log.Errorf("ensuring tempdir exists: %s", err) + } + ctx, _ := tag.New(lcli.DaemonContext(cctx), tag.Insert(metrics.Version, build.BuildVersion), tag.Insert(metrics.Commit, build.CurrentCommit), From bc8517baa02b90c6c1171d1b206aef8c8188551c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 16 Feb 2024 20:24:23 +0100 Subject: [PATCH 178/394] paths: Support multiple endpoints in FsStat --- storage/paths/remote.go | 71 +++++++++++++++++++++-------------------- 1 file changed, 36 insertions(+), 35 deletions(-) diff --git a/storage/paths/remote.go b/storage/paths/remote.go index f72400c5e..6433260b1 100644 --- a/storage/paths/remote.go +++ b/storage/paths/remote.go @@ -398,46 +398,47 @@ func (r *Remote) FsStat(ctx context.Context, id storiface.ID) (fsutil.FsStat, er return fsutil.FsStat{}, xerrors.Errorf("no known URLs for remote storage %s", id) } - rl, err := url.Parse(si.URLs[0]) - if err != nil { - return fsutil.FsStat{}, xerrors.Errorf("failed to parse url: %w", err) - } - - rl.Path = gopath.Join(rl.Path, "stat", string(id)) - - req, err := http.NewRequest("GET", rl.String(), nil) - if err != nil { - return fsutil.FsStat{}, xerrors.Errorf("request: %w", err) - } - req.Header = r.auth - req = req.WithContext(ctx) - - resp, err := http.DefaultClient.Do(req) - if err != nil { - return fsutil.FsStat{}, xerrors.Errorf("do request: %w", err) - } - switch resp.StatusCode { - case 200: - break - case 404: - return fsutil.FsStat{}, errPathNotFound - case 500: - b, err := io.ReadAll(resp.Body) + for _, urlStr := range si.URLs { + rl, err := url.Parse(urlStr) if err != nil { - return fsutil.FsStat{}, xerrors.Errorf("fsstat: got http 500, then failed to read the error: %w", err) + log.Warnw("failed to parse URL", "url", urlStr, "error", err) + continue // Try the next URL } - return fsutil.FsStat{}, xerrors.Errorf("fsstat: got http 500: %s", string(b)) + rl.Path = gopath.Join(rl.Path, "stat", string(id)) + + req, err := http.NewRequest("GET", rl.String(), nil) + if err != nil { + log.Warnw("creating request failed", "url", rl.String(), "error", err) + continue // Try the next URL + } + req.Header = r.auth + req = req.WithContext(ctx) + + resp, err := http.DefaultClient.Do(req) + if err != nil { + log.Warnw("request failed", "url", rl.String(), "error", err) + continue // Try the next URL + } + + if resp.StatusCode == 200 { + var out fsutil.FsStat + if err := json.NewDecoder(resp.Body).Decode(&out); err != nil { + _ = resp.Body.Close() + log.Warnw("decoding response failed", "url", rl.String(), "error", err) + continue // Try the next URL + } + _ = resp.Body.Close() + return out, nil // Successfully decoded, return the result + } else { + b, _ := io.ReadAll(resp.Body) // Best-effort read the body for logging + log.Warnw("request to endpoint failed", "url", rl.String(), "statusCode", resp.StatusCode, "response", string(b)) + _ = resp.Body.Close() + // Continue to try the next URL, don't return here as we want to try all URLs + } } - var out fsutil.FsStat - if err := json.NewDecoder(resp.Body).Decode(&out); err != nil { - return fsutil.FsStat{}, xerrors.Errorf("decoding fsstat: %w", err) - } - - defer resp.Body.Close() // nolint - - return out, nil + return fsutil.FsStat{}, xerrors.Errorf("all endpoints failed for remote storage %s", id) } func (r *Remote) readRemote(ctx context.Context, url string, offset, size abi.PaddedPieceSize) (io.ReadCloser, error) { From caf62d937011ef01384e8c74724637cf7c4bd97a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 16 Feb 2024 21:27:15 +0100 Subject: [PATCH 179/394] fix addresssel post-rebase --- cmd/lotus-provider/config_migrate.go | 2 +- cmd/lotus-provider/config_new.go | 9 ++++++++- provider/lpseal/task_submit_commit.go | 8 ++++---- provider/lpseal/task_submit_precommit.go | 8 ++++---- 4 files changed, 17 insertions(+), 10 deletions(-) diff --git a/cmd/lotus-provider/config_migrate.go b/cmd/lotus-provider/config_migrate.go index 7b395d5a9..572a798a5 100644 --- a/cmd/lotus-provider/config_migrate.go +++ b/cmd/lotus-provider/config_migrate.go @@ -125,7 +125,7 @@ func fromMiner(cctx *cli.Context) (err error) { return fmt.Errorf("could not read config.toml: %w", err) } lpCfg := config.DefaultLotusProvider() - _, err = deps.LoadConfigWithUpgrades(string(buf), &lpCfg) + _, err = deps.LoadConfigWithUpgrades(string(buf), lpCfg) if err != nil { return fmt.Errorf("could not decode toml: %w", err) } diff --git a/cmd/lotus-provider/config_new.go b/cmd/lotus-provider/config_new.go index da194d223..38949959a 100644 --- a/cmd/lotus-provider/config_new.go +++ b/cmd/lotus-provider/config_new.go @@ -86,7 +86,14 @@ var configNewCmd = &cli.Command{ return xerrors.Errorf("Failed to get miner info: %w", err) } - lpCfg.Addresses.MinerAddresses = append(lpCfg.Addresses.MinerAddresses, addr) + lpCfg.Addresses = append(lpCfg.Addresses, config.LotusProviderAddresses{ + PreCommitControl: nil, + CommitControl: nil, + TerminateControl: nil, + DisableOwnerFallback: false, + DisableWorkerFallback: false, + MinerAddresses: []string{addr}, + }) } { diff --git a/provider/lpseal/task_submit_commit.go b/provider/lpseal/task_submit_commit.go index 793b15618..789f8833e 100644 --- a/provider/lpseal/task_submit_commit.go +++ b/provider/lpseal/task_submit_commit.go @@ -3,6 +3,7 @@ package lpseal import ( "bytes" "context" + "github.com/filecoin-project/lotus/provider/multictladdr" "golang.org/x/xerrors" @@ -19,7 +20,6 @@ import ( "github.com/filecoin-project/lotus/lib/harmony/resources" "github.com/filecoin-project/lotus/provider/lpmessage" "github.com/filecoin-project/lotus/storage/ctladdr" - sealing "github.com/filecoin-project/lotus/storage/pipeline" ) type SubmitCommitAPI interface { @@ -36,12 +36,12 @@ type SubmitCommitTask struct { api SubmitCommitAPI sender *lpmessage.Sender - as sealing.AddressSelector + as *multictladdr.MultiAddressSelector maxFee types.FIL } -func NewSubmitCommitTask(sp *SealPoller, db *harmonydb.DB, api SubmitCommitAPI, sender *lpmessage.Sender, as sealing.AddressSelector, maxFee types.FIL) *SubmitCommitTask { +func NewSubmitCommitTask(sp *SealPoller, db *harmonydb.DB, api SubmitCommitAPI, sender *lpmessage.Sender, as *multictladdr.MultiAddressSelector, maxFee types.FIL) *SubmitCommitTask { return &SubmitCommitTask{ sp: sp, db: db, @@ -118,7 +118,7 @@ func (s *SubmitCommitTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) collateral = big.Zero() } - a, _, err := s.as.AddressFor(ctx, s.api, mi, api.CommitAddr, collateral, big.Zero()) + a, _, err := s.as.AddressFor(ctx, s.api, maddr, mi, api.CommitAddr, collateral, big.Zero()) if err != nil { return false, xerrors.Errorf("getting address for precommit: %w", err) } diff --git a/provider/lpseal/task_submit_precommit.go b/provider/lpseal/task_submit_precommit.go index 09c9379ac..e6306f2b8 100644 --- a/provider/lpseal/task_submit_precommit.go +++ b/provider/lpseal/task_submit_precommit.go @@ -3,6 +3,7 @@ package lpseal import ( "bytes" "context" + "github.com/filecoin-project/lotus/provider/multictladdr" "github.com/ipfs/go-cid" "golang.org/x/xerrors" @@ -21,7 +22,6 @@ import ( "github.com/filecoin-project/lotus/lib/harmony/resources" "github.com/filecoin-project/lotus/provider/lpmessage" "github.com/filecoin-project/lotus/storage/ctladdr" - sealing "github.com/filecoin-project/lotus/storage/pipeline" ) type SubmitPrecommitTaskApi interface { @@ -35,12 +35,12 @@ type SubmitPrecommitTask struct { db *harmonydb.DB api SubmitPrecommitTaskApi sender *lpmessage.Sender - as sealing.AddressSelector + as *multictladdr.MultiAddressSelector maxFee types.FIL } -func NewSubmitPrecommitTask(sp *SealPoller, db *harmonydb.DB, api SubmitPrecommitTaskApi, sender *lpmessage.Sender, as sealing.AddressSelector, maxFee types.FIL) *SubmitPrecommitTask { +func NewSubmitPrecommitTask(sp *SealPoller, db *harmonydb.DB, api SubmitPrecommitTaskApi, sender *lpmessage.Sender, as *multictladdr.MultiAddressSelector, maxFee types.FIL) *SubmitPrecommitTask { return &SubmitPrecommitTask{ sp: sp, db: db, @@ -146,7 +146,7 @@ func (s *SubmitPrecommitTask) Do(taskID harmonytask.TaskID, stillOwned func() bo return false, xerrors.Errorf("getting miner info: %w", err) } - a, _, err := s.as.AddressFor(ctx, s.api, mi, api.PreCommitAddr, collateral, big.Zero()) + a, _, err := s.as.AddressFor(ctx, s.api, maddr, mi, api.PreCommitAddr, collateral, big.Zero()) if err != nil { return false, xerrors.Errorf("getting address for precommit: %w", err) } From 06a2f8c670938a558ff92c24fe7840153b5c3102 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sat, 17 Feb 2024 00:57:27 +0100 Subject: [PATCH 180/394] paths: Fix dbindex heartbeat --- storage/paths/db_index.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/storage/paths/db_index.go b/storage/paths/db_index.go index 1472997b6..9d12eb70f 100644 --- a/storage/paths/db_index.go +++ b/storage/paths/db_index.go @@ -324,12 +324,13 @@ func (dbi *DBIndex) StorageReportHealth(ctx context.Context, id storiface.ID, re retryWait := time.Millisecond * 20 retryReportHealth: _, err := dbi.harmonyDB.Exec(ctx, - "UPDATE storage_path set capacity=$1, available=$2, fs_available=$3, reserved=$4, used=$5, last_heartbeat=NOW()", + "UPDATE storage_path set capacity=$1, available=$2, fs_available=$3, reserved=$4, used=$5, last_heartbeat=NOW() where storage_id=$6", report.Stat.Capacity, report.Stat.Available, report.Stat.FSAvailable, report.Stat.Reserved, - report.Stat.Used) + report.Stat.Used, + id) if err != nil { //return xerrors.Errorf("updating storage health in DB fails with err: %v", err) if harmonydb.IsErrSerialization(err) { From a003b6ccda18142310c280444de339fdec0c8a25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sat, 17 Feb 2024 00:43:42 +0100 Subject: [PATCH 181/394] make gen --- build/openrpc/full.json | 478 +++++++++++------------ build/openrpc/gateway.json | 186 ++++----- build/openrpc/miner.json | 260 ++++++------ build/openrpc/worker.json | 74 ++-- provider/lpseal/task_submit_commit.go | 2 +- provider/lpseal/task_submit_precommit.go | 2 +- 6 files changed, 501 insertions(+), 501 deletions(-) diff --git a/build/openrpc/full.json b/build/openrpc/full.json index 5f70ce52a..e10ef64e6 100644 --- a/build/openrpc/full.json +++ b/build/openrpc/full.json @@ -37,7 +37,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1464" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1480" } }, { @@ -60,7 +60,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1475" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1491" } }, { @@ -103,7 +103,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1486" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1502" } }, { @@ -214,7 +214,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1508" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1524" } }, { @@ -454,7 +454,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1519" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1535" } }, { @@ -685,7 +685,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1530" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1546" } }, { @@ -784,7 +784,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1541" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1557" } }, { @@ -816,7 +816,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1552" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1568" } }, { @@ -922,7 +922,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1563" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1579" } }, { @@ -1019,7 +1019,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1574" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1590" } }, { @@ -1078,7 +1078,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1585" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1601" } }, { @@ -1171,7 +1171,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1596" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1612" } }, { @@ -1255,7 +1255,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1607" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1623" } }, { @@ -1355,7 +1355,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1618" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1634" } }, { @@ -1411,7 +1411,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1629" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1645" } }, { @@ -1484,7 +1484,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1640" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1656" } }, { @@ -1557,7 +1557,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1651" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1667" } }, { @@ -1604,7 +1604,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1662" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1678" } }, { @@ -1636,7 +1636,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1673" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1689" } }, { @@ -1691,7 +1691,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1684" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1700" } }, { @@ -1743,7 +1743,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1706" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1722" } }, { @@ -1780,7 +1780,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1717" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1733" } }, { @@ -1827,7 +1827,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1728" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1744" } }, { @@ -1874,7 +1874,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1739" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1755" } }, { @@ -1954,7 +1954,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1750" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1766" } }, { @@ -2006,7 +2006,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1761" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1777" } }, { @@ -2065,7 +2065,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1772" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1788" } }, { @@ -2136,7 +2136,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1783" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1799" } }, { @@ -2177,7 +2177,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1794" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1810" } }, { @@ -2245,7 +2245,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1816" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1832" } }, { @@ -2306,7 +2306,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1827" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1843" } }, { @@ -2413,7 +2413,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1838" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1854" } }, { @@ -2569,7 +2569,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1849" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1865" } }, { @@ -2635,7 +2635,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1860" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1876" } }, { @@ -2976,7 +2976,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1871" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1887" } }, { @@ -3021,7 +3021,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1882" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1898" } }, { @@ -3068,7 +3068,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1915" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1931" } }, { @@ -3139,7 +3139,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1926" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1942" } }, { @@ -3282,7 +3282,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1937" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1953" } }, { @@ -3612,7 +3612,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1948" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1964" } }, { @@ -3680,7 +3680,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1959" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1975" } }, { @@ -3914,7 +3914,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1970" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1986" } }, { @@ -4077,7 +4077,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1981" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1997" } }, { @@ -4160,7 +4160,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1992" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2008" } }, { @@ -4201,7 +4201,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2003" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2019" } }, { @@ -4272,7 +4272,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2014" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2030" } }, { @@ -4416,7 +4416,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2025" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2041" } }, { @@ -4456,7 +4456,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2036" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2052" } }, { @@ -4497,7 +4497,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2047" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2063" } }, { @@ -4622,7 +4622,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2058" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2074" } }, { @@ -4747,7 +4747,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2069" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2085" } }, { @@ -4786,7 +4786,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2080" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2096" } }, { @@ -4833,7 +4833,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2091" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2107" } }, { @@ -4888,7 +4888,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2102" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2118" } }, { @@ -4917,7 +4917,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2113" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2129" } }, { @@ -5054,7 +5054,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2124" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2140" } }, { @@ -5083,7 +5083,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2135" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2151" } }, { @@ -5137,7 +5137,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2146" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2162" } }, { @@ -5228,7 +5228,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2157" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2173" } }, { @@ -5256,7 +5256,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2168" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2184" } }, { @@ -5346,7 +5346,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2179" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2195" } }, { @@ -5602,7 +5602,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2190" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2206" } }, { @@ -5847,7 +5847,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2201" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2217" } }, { @@ -5903,7 +5903,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2212" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2228" } }, { @@ -5950,7 +5950,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2223" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2239" } }, { @@ -6048,7 +6048,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2234" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2250" } }, { @@ -6114,7 +6114,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2245" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2261" } }, { @@ -6180,7 +6180,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2256" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2272" } }, { @@ -6289,7 +6289,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2267" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2283" } }, { @@ -6347,7 +6347,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2278" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2294" } }, { @@ -6469,7 +6469,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2289" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2305" } }, { @@ -6673,7 +6673,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2300" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2316" } }, { @@ -6868,7 +6868,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2311" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2327" } }, { @@ -7055,7 +7055,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2322" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2338" } }, { @@ -7259,7 +7259,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2333" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2349" } }, { @@ -7350,7 +7350,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2344" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2360" } }, { @@ -7408,7 +7408,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2355" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2371" } }, { @@ -7666,7 +7666,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2366" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2382" } }, { @@ -7941,7 +7941,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2377" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2393" } }, { @@ -7969,7 +7969,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2388" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2404" } }, { @@ -8007,7 +8007,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2399" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2415" } }, { @@ -8115,7 +8115,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2410" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2426" } }, { @@ -8153,7 +8153,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2421" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2437" } }, { @@ -8182,7 +8182,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2432" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2448" } }, { @@ -8245,7 +8245,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2443" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2459" } }, { @@ -8308,7 +8308,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2454" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2470" } }, { @@ -8353,7 +8353,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2465" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2481" } }, { @@ -8536,7 +8536,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2476" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2492" } }, { @@ -8752,7 +8752,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2487" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2503" } }, { @@ -8806,7 +8806,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2498" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2514" } }, { @@ -8860,7 +8860,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2509" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2525" } }, { @@ -8915,7 +8915,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2520" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2536" } }, { @@ -9058,7 +9058,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2531" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2547" } }, { @@ -9185,7 +9185,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2542" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2558" } }, { @@ -9287,7 +9287,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2553" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2569" } }, { @@ -9510,7 +9510,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2564" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2580" } }, { @@ -9590,7 +9590,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2575" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2591" } }, { @@ -9635,7 +9635,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2586" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2602" } }, { @@ -9691,7 +9691,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2597" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2613" } }, { @@ -9771,7 +9771,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2608" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2624" } }, { @@ -9851,7 +9851,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2619" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2635" } }, { @@ -10336,7 +10336,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2630" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2646" } }, { @@ -10530,7 +10530,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2641" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2657" } }, { @@ -10685,7 +10685,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2652" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2668" } }, { @@ -10934,7 +10934,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2663" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2679" } }, { @@ -11089,7 +11089,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2674" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2690" } }, { @@ -11266,7 +11266,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2685" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2701" } }, { @@ -11364,7 +11364,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2696" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2712" } }, { @@ -11529,7 +11529,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2707" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2723" } }, { @@ -11568,7 +11568,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2718" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2734" } }, { @@ -11633,7 +11633,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2729" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2745" } }, { @@ -11679,7 +11679,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2740" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2756" } }, { @@ -11829,7 +11829,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2751" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2767" } }, { @@ -11966,7 +11966,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2762" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2778" } }, { @@ -12197,7 +12197,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2773" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2789" } }, { @@ -12334,7 +12334,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2784" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2800" } }, { @@ -12499,7 +12499,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2795" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2811" } }, { @@ -12576,7 +12576,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2806" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2822" } }, { @@ -12771,7 +12771,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2828" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2844" } }, { @@ -12950,7 +12950,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2839" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2855" } }, { @@ -13112,7 +13112,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2850" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2866" } }, { @@ -13260,7 +13260,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2861" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2877" } }, { @@ -13488,7 +13488,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2872" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2888" } }, { @@ -13636,7 +13636,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2883" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2899" } }, { @@ -13848,7 +13848,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2894" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2910" } }, { @@ -14054,7 +14054,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2905" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2921" } }, { @@ -14122,7 +14122,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2916" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2932" } }, { @@ -14239,7 +14239,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2927" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2943" } }, { @@ -14330,7 +14330,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2938" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2954" } }, { @@ -14416,7 +14416,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2949" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2965" } }, { @@ -14611,7 +14611,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2960" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2976" } }, { @@ -14773,7 +14773,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2971" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2987" } }, { @@ -14969,7 +14969,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2982" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2998" } }, { @@ -15149,7 +15149,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2993" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3009" } }, { @@ -15312,7 +15312,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3004" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3020" } }, { @@ -15339,7 +15339,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3015" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3031" } }, { @@ -15366,7 +15366,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3026" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3042" } }, { @@ -15465,7 +15465,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3037" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3053" } }, { @@ -15511,7 +15511,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3048" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3064" } }, { @@ -15611,7 +15611,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3059" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3075" } }, { @@ -15727,7 +15727,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3070" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3086" } }, { @@ -15775,7 +15775,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3081" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3097" } }, { @@ -15867,7 +15867,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3092" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3108" } }, { @@ -15982,7 +15982,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3103" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3119" } }, { @@ -16030,7 +16030,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3114" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3130" } }, { @@ -16067,7 +16067,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3125" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3141" } }, { @@ -16339,7 +16339,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3136" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3152" } }, { @@ -16387,7 +16387,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3147" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3163" } }, { @@ -16445,7 +16445,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3158" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3174" } }, { @@ -16650,7 +16650,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3169" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3185" } }, { @@ -16853,7 +16853,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3180" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3196" } }, { @@ -17022,7 +17022,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3191" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3207" } }, { @@ -17226,7 +17226,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3202" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3218" } }, { @@ -17393,7 +17393,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3213" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3229" } }, { @@ -17600,7 +17600,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3224" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3240" } }, { @@ -17668,7 +17668,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3235" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3251" } }, { @@ -17720,7 +17720,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3246" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3262" } }, { @@ -17769,7 +17769,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3257" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3273" } }, { @@ -17860,7 +17860,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3268" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3284" } }, { @@ -18366,7 +18366,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3279" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3295" } }, { @@ -18472,7 +18472,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3290" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3306" } }, { @@ -18524,7 +18524,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3301" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3317" } }, { @@ -19076,7 +19076,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3312" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3328" } }, { @@ -19190,7 +19190,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3323" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3339" } }, { @@ -19287,7 +19287,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3334" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3350" } }, { @@ -19387,7 +19387,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3345" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3361" } }, { @@ -19475,7 +19475,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3356" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3372" } }, { @@ -19575,7 +19575,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3367" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3383" } }, { @@ -19700,7 +19700,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3378" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3394" } }, { @@ -19809,7 +19809,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3389" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3405" } }, { @@ -19912,7 +19912,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3400" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3416" } }, { @@ -19973,7 +19973,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3411" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3427" } }, { @@ -20103,7 +20103,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3422" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3438" } }, { @@ -20210,7 +20210,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3433" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3449" } }, { @@ -20409,7 +20409,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3444" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3460" } }, { @@ -20486,7 +20486,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3455" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3471" } }, { @@ -20563,7 +20563,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3466" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3482" } }, { @@ -20672,7 +20672,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3477" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3493" } }, { @@ -20781,7 +20781,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3488" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3504" } }, { @@ -20842,7 +20842,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3499" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3515" } }, { @@ -20952,7 +20952,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3510" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3526" } }, { @@ -21013,7 +21013,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3521" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3537" } }, { @@ -21081,7 +21081,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3532" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3548" } }, { @@ -21149,7 +21149,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3543" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3559" } }, { @@ -21230,7 +21230,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3554" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3570" } }, { @@ -21384,7 +21384,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3565" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3581" } }, { @@ -21456,7 +21456,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3576" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3592" } }, { @@ -21620,7 +21620,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3587" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3603" } }, { @@ -21785,7 +21785,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3598" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3614" } }, { @@ -21855,7 +21855,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3609" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3625" } }, { @@ -21923,7 +21923,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3620" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3636" } }, { @@ -22016,7 +22016,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3631" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3647" } }, { @@ -22087,7 +22087,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3642" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3658" } }, { @@ -22288,7 +22288,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3653" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3669" } }, { @@ -22420,7 +22420,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3664" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3680" } }, { @@ -22557,7 +22557,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3675" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3691" } }, { @@ -22668,7 +22668,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3686" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3702" } }, { @@ -22800,7 +22800,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3697" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3713" } }, { @@ -22931,7 +22931,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3708" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3724" } }, { @@ -23002,7 +23002,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3719" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3735" } }, { @@ -23086,7 +23086,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3730" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3746" } }, { @@ -23172,7 +23172,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3741" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3757" } }, { @@ -23355,7 +23355,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3752" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3768" } }, { @@ -23382,7 +23382,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3763" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3779" } }, { @@ -23435,7 +23435,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3774" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3790" } }, { @@ -23523,7 +23523,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3785" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3801" } }, { @@ -23974,7 +23974,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3796" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3812" } }, { @@ -24141,7 +24141,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3807" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3823" } }, { @@ -24239,7 +24239,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3818" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3834" } }, { @@ -24412,7 +24412,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3829" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3845" } }, { @@ -24510,7 +24510,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3840" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3856" } }, { @@ -24661,7 +24661,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3851" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3867" } }, { @@ -24746,7 +24746,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3862" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3878" } }, { @@ -24814,7 +24814,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3873" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3889" } }, { @@ -24866,7 +24866,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3884" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3900" } }, { @@ -24934,7 +24934,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3895" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3911" } }, { @@ -25095,7 +25095,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3906" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3922" } }, { @@ -25142,7 +25142,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3917" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3933" } }, { @@ -25189,7 +25189,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3928" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3944" } }, { @@ -25232,7 +25232,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3950" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3966" } }, { @@ -25328,7 +25328,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3961" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3977" } }, { @@ -25594,7 +25594,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3972" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3988" } }, { @@ -25617,7 +25617,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3983" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3999" } }, { @@ -25660,7 +25660,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3994" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4010" } }, { @@ -25711,7 +25711,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4005" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4021" } }, { @@ -25756,7 +25756,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4016" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4032" } }, { @@ -25784,7 +25784,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4027" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4043" } }, { @@ -25824,7 +25824,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4038" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4054" } }, { @@ -25883,7 +25883,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4049" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4065" } }, { @@ -25927,7 +25927,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4060" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4076" } }, { @@ -25986,7 +25986,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4071" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4087" } }, { @@ -26023,7 +26023,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4082" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4098" } }, { @@ -26067,7 +26067,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4093" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4109" } }, { @@ -26107,7 +26107,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4104" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4120" } }, { @@ -26182,7 +26182,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4115" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4131" } }, { @@ -26390,7 +26390,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4126" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4142" } }, { @@ -26434,7 +26434,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4137" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4153" } }, { @@ -26524,7 +26524,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4148" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4164" } }, { @@ -26551,7 +26551,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4159" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4175" } } ] diff --git a/build/openrpc/gateway.json b/build/openrpc/gateway.json index 42521f0c8..28de99b79 100644 --- a/build/openrpc/gateway.json +++ b/build/openrpc/gateway.json @@ -242,7 +242,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4170" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4186" } }, { @@ -473,7 +473,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4181" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4197" } }, { @@ -505,7 +505,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4192" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4208" } }, { @@ -611,7 +611,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4203" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4219" } }, { @@ -704,7 +704,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4214" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4230" } }, { @@ -788,7 +788,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4225" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4241" } }, { @@ -888,7 +888,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4236" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4252" } }, { @@ -944,7 +944,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4247" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4263" } }, { @@ -1017,7 +1017,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4258" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4274" } }, { @@ -1090,7 +1090,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4269" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4285" } }, { @@ -1137,7 +1137,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4280" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4296" } }, { @@ -1169,7 +1169,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4291" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4307" } }, { @@ -1206,7 +1206,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4313" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4329" } }, { @@ -1253,7 +1253,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4324" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4340" } }, { @@ -1293,7 +1293,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4335" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4351" } }, { @@ -1340,7 +1340,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4346" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4362" } }, { @@ -1369,7 +1369,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4357" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4373" } }, { @@ -1506,7 +1506,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4368" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4384" } }, { @@ -1535,7 +1535,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4379" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4395" } }, { @@ -1589,7 +1589,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4390" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4406" } }, { @@ -1680,7 +1680,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4401" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4417" } }, { @@ -1708,7 +1708,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4412" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4428" } }, { @@ -1798,7 +1798,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4423" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4439" } }, { @@ -2054,7 +2054,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4434" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4450" } }, { @@ -2299,7 +2299,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4445" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4461" } }, { @@ -2355,7 +2355,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4456" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4472" } }, { @@ -2402,7 +2402,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4467" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4483" } }, { @@ -2500,7 +2500,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4478" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4494" } }, { @@ -2566,7 +2566,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4489" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4505" } }, { @@ -2632,7 +2632,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4500" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4516" } }, { @@ -2741,7 +2741,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4511" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4527" } }, { @@ -2799,7 +2799,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4522" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4538" } }, { @@ -2921,7 +2921,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4533" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4549" } }, { @@ -3108,7 +3108,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4544" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4560" } }, { @@ -3312,7 +3312,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4555" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4571" } }, { @@ -3403,7 +3403,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4566" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4582" } }, { @@ -3461,7 +3461,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4577" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4593" } }, { @@ -3719,7 +3719,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4588" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4604" } }, { @@ -3994,7 +3994,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4599" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4615" } }, { @@ -4022,7 +4022,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4610" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4626" } }, { @@ -4060,7 +4060,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4621" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4637" } }, { @@ -4168,7 +4168,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4632" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4648" } }, { @@ -4206,7 +4206,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4643" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4659" } }, { @@ -4235,7 +4235,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4654" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4670" } }, { @@ -4298,7 +4298,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4665" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4681" } }, { @@ -4361,7 +4361,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4676" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4692" } }, { @@ -4406,7 +4406,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4687" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4703" } }, { @@ -4589,7 +4589,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4698" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4714" } }, { @@ -4805,7 +4805,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4709" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4725" } }, { @@ -4859,7 +4859,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4720" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4736" } }, { @@ -4913,7 +4913,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4731" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4747" } }, { @@ -5015,7 +5015,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4742" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4758" } }, { @@ -5238,7 +5238,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4753" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4769" } }, { @@ -5432,7 +5432,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4764" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4780" } }, { @@ -5478,7 +5478,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4775" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4791" } }, { @@ -5628,7 +5628,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4786" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4802" } }, { @@ -5765,7 +5765,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4797" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4813" } }, { @@ -5833,7 +5833,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4808" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4824" } }, { @@ -5950,7 +5950,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4819" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4835" } }, { @@ -6041,7 +6041,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4830" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4846" } }, { @@ -6127,7 +6127,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4841" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4857" } }, { @@ -6154,7 +6154,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4852" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4868" } }, { @@ -6181,7 +6181,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4863" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4879" } }, { @@ -6249,7 +6249,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4874" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4890" } }, { @@ -6755,7 +6755,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4885" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4901" } }, { @@ -6852,7 +6852,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4896" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4912" } }, { @@ -6952,7 +6952,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4907" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4923" } }, { @@ -7052,7 +7052,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4918" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4934" } }, { @@ -7177,7 +7177,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4929" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4945" } }, { @@ -7286,7 +7286,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4940" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4956" } }, { @@ -7389,7 +7389,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4951" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4967" } }, { @@ -7519,7 +7519,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4962" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4978" } }, { @@ -7626,7 +7626,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4973" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4989" } }, { @@ -7687,7 +7687,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4984" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5000" } }, { @@ -7755,7 +7755,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4995" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5011" } }, { @@ -7836,7 +7836,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5006" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5022" } }, { @@ -8000,7 +8000,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5017" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5033" } }, { @@ -8201,7 +8201,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5028" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5044" } }, { @@ -8312,7 +8312,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5039" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5055" } }, { @@ -8443,7 +8443,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5050" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5066" } }, { @@ -8529,7 +8529,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5061" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5077" } }, { @@ -8556,7 +8556,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5072" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5088" } }, { @@ -8609,7 +8609,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5083" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5099" } }, { @@ -8697,7 +8697,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5094" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5110" } }, { @@ -9148,7 +9148,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5105" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5121" } }, { @@ -9315,7 +9315,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5116" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5132" } }, { @@ -9488,7 +9488,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5127" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5143" } }, { @@ -9556,7 +9556,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5138" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5154" } }, { @@ -9624,7 +9624,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5149" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5165" } }, { @@ -9785,7 +9785,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5160" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5176" } }, { @@ -9830,7 +9830,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5171" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5187" } }, { @@ -9875,7 +9875,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5182" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5198" } }, { @@ -9902,7 +9902,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5193" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5209" } } ] diff --git a/build/openrpc/miner.json b/build/openrpc/miner.json index 5c3aeac21..ae37f6ee0 100644 --- a/build/openrpc/miner.json +++ b/build/openrpc/miner.json @@ -30,7 +30,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5501" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5594" } }, { @@ -109,7 +109,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5512" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5605" } }, { @@ -155,7 +155,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5523" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5616" } }, { @@ -203,7 +203,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5534" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5627" } }, { @@ -251,7 +251,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5545" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5638" } }, { @@ -354,7 +354,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5556" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5649" } }, { @@ -428,7 +428,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5567" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5660" } }, { @@ -591,7 +591,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5578" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5671" } }, { @@ -742,7 +742,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5589" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5682" } }, { @@ -781,7 +781,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5600" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5693" } }, { @@ -833,7 +833,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5611" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5704" } }, { @@ -872,7 +872,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5633" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5726" } }, { @@ -924,7 +924,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5644" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5737" } }, { @@ -996,7 +996,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5655" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5748" } }, { @@ -1035,7 +1035,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5666" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5759" } }, { @@ -1074,7 +1074,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5677" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5770" } }, { @@ -1101,7 +1101,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5688" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5781" } }, { @@ -1128,7 +1128,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5699" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5792" } }, { @@ -1155,7 +1155,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5710" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5803" } }, { @@ -1182,7 +1182,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5721" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5814" } }, { @@ -1209,7 +1209,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5732" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5825" } }, { @@ -1236,7 +1236,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5743" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5836" } }, { @@ -1294,7 +1294,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5754" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5847" } }, { @@ -1426,7 +1426,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5765" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5858" } }, { @@ -1466,7 +1466,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5776" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5869" } }, { @@ -1505,7 +1505,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5787" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5880" } }, { @@ -1544,7 +1544,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5798" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5891" } }, { @@ -1583,7 +1583,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5809" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5902" } }, { @@ -1622,7 +1622,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5820" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5913" } }, { @@ -1661,7 +1661,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5831" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5924" } }, { @@ -1700,7 +1700,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5842" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5935" } }, { @@ -1752,7 +1752,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5853" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5946" } }, { @@ -1775,7 +1775,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5864" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5957" } }, { @@ -1818,7 +1818,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5875" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5968" } }, { @@ -1889,7 +1889,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5886" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5979" } }, { @@ -2270,7 +2270,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5897" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5990" } }, { @@ -2369,7 +2369,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5919" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6012" } }, { @@ -2420,7 +2420,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5941" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6034" } }, { @@ -2478,7 +2478,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5952" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6045" } }, { @@ -2621,7 +2621,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5963" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6056" } }, { @@ -2753,7 +2753,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5974" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6067" } }, { @@ -3017,7 +3017,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5985" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6078" } }, { @@ -3054,7 +3054,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5996" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6089" } }, { @@ -3192,7 +3192,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6007" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6100" } }, { @@ -3215,7 +3215,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6018" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6111" } }, { @@ -3286,7 +3286,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6029" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6122" } }, { @@ -3329,7 +3329,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6040" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6133" } }, { @@ -3436,7 +3436,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6051" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6144" } }, { @@ -3499,7 +3499,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6062" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6155" } }, { @@ -3531,7 +3531,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6073" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6166" } }, { @@ -3619,7 +3619,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6084" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6177" } }, { @@ -3710,7 +3710,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6095" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6188" } }, { @@ -3750,7 +3750,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6106" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6199" } }, { @@ -3790,7 +3790,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6117" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6210" } }, { @@ -3831,7 +3831,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6128" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6221" } }, { @@ -3899,7 +3899,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6139" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6232" } }, { @@ -4030,7 +4030,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6150" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6243" } }, { @@ -4161,7 +4161,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6161" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6254" } }, { @@ -4261,7 +4261,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6172" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6265" } }, { @@ -4361,7 +4361,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6183" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6276" } }, { @@ -4461,7 +4461,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6194" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6287" } }, { @@ -4561,7 +4561,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6205" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6298" } }, { @@ -4661,7 +4661,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6216" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6309" } }, { @@ -4761,7 +4761,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6227" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6320" } }, { @@ -4885,7 +4885,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6238" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6331" } }, { @@ -5009,7 +5009,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6249" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6342" } }, { @@ -5124,7 +5124,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6260" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6353" } }, { @@ -5224,7 +5224,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6271" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6364" } }, { @@ -5357,7 +5357,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6282" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6375" } }, { @@ -5481,7 +5481,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6293" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6386" } }, { @@ -5605,7 +5605,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6304" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6397" } }, { @@ -5729,7 +5729,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6315" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6408" } }, { @@ -5862,7 +5862,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6326" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6419" } }, { @@ -5962,7 +5962,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6337" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6430" } }, { @@ -6003,7 +6003,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6348" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6441" } }, { @@ -6075,7 +6075,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6359" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6452" } }, { @@ -6125,7 +6125,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6370" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6463" } }, { @@ -6169,7 +6169,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6381" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6474" } }, { @@ -6210,7 +6210,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6392" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6485" } }, { @@ -6399,7 +6399,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6403" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6496" } }, { @@ -6473,7 +6473,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6414" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6507" } }, { @@ -6523,7 +6523,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6425" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6518" } }, { @@ -6552,7 +6552,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6436" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6529" } }, { @@ -6581,7 +6581,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6447" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6540" } }, { @@ -6637,7 +6637,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6458" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6551" } }, { @@ -6660,7 +6660,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6469" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6562" } }, { @@ -6720,7 +6720,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6480" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6573" } }, { @@ -6759,7 +6759,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6491" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6584" } }, { @@ -6799,7 +6799,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6502" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6595" } }, { @@ -6872,7 +6872,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6513" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6606" } }, { @@ -6936,7 +6936,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6524" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6617" } }, { @@ -6999,7 +6999,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6535" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6628" } }, { @@ -7049,7 +7049,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6546" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6639" } }, { @@ -7553,7 +7553,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6557" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6650" } }, { @@ -7594,7 +7594,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6568" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6661" } }, { @@ -7635,7 +7635,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6579" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6672" } }, { @@ -7676,7 +7676,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6590" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6683" } }, { @@ -7717,7 +7717,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6601" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6694" } }, { @@ -7758,7 +7758,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6612" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6705" } }, { @@ -7789,7 +7789,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6623" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6716" } }, { @@ -7839,7 +7839,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6634" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6727" } }, { @@ -7880,7 +7880,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6645" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6738" } }, { @@ -7919,7 +7919,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6656" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6749" } }, { @@ -7983,7 +7983,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6667" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6760" } }, { @@ -8041,7 +8041,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6678" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6771" } }, { @@ -8433,7 +8433,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6689" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6782" } }, { @@ -8469,7 +8469,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6700" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6793" } }, { @@ -8612,7 +8612,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6711" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6804" } }, { @@ -8668,7 +8668,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6722" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6815" } }, { @@ -8707,7 +8707,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6733" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6826" } }, { @@ -8866,7 +8866,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6744" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6837" } }, { @@ -8918,7 +8918,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6755" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6848" } }, { @@ -9075,7 +9075,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6766" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6859" } }, { @@ -9175,7 +9175,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6777" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6870" } }, { @@ -9229,7 +9229,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6788" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6881" } }, { @@ -9268,7 +9268,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6799" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6892" } }, { @@ -9353,7 +9353,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6810" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6903" } }, { @@ -9529,7 +9529,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6821" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6914" } }, { @@ -9625,7 +9625,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6832" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6925" } }, { @@ -9739,7 +9739,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6843" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6936" } }, { @@ -9793,7 +9793,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6854" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6947" } }, { @@ -9827,7 +9827,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6865" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6958" } }, { @@ -9914,7 +9914,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6876" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6969" } }, { @@ -9968,7 +9968,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6887" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6980" } }, { @@ -10068,7 +10068,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6898" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6991" } }, { @@ -10145,7 +10145,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6909" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7002" } }, { @@ -10236,7 +10236,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6920" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7013" } }, { @@ -10275,7 +10275,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6931" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7024" } }, { @@ -10391,7 +10391,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6942" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7035" } }, { @@ -12491,7 +12491,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6953" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7046" } } ] diff --git a/build/openrpc/worker.json b/build/openrpc/worker.json index 257fa1bad..66fee236c 100644 --- a/build/openrpc/worker.json +++ b/build/openrpc/worker.json @@ -161,7 +161,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7041" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7134" } }, { @@ -252,7 +252,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7052" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7145" } }, { @@ -420,7 +420,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7063" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7156" } }, { @@ -447,7 +447,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7074" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7167" } }, { @@ -597,7 +597,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7085" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7178" } }, { @@ -700,7 +700,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7096" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7189" } }, { @@ -803,7 +803,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7107" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7200" } }, { @@ -925,7 +925,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7118" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7211" } }, { @@ -1135,7 +1135,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7129" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7222" } }, { @@ -1306,7 +1306,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7140" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7233" } }, { @@ -3350,7 +3350,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7151" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7244" } }, { @@ -3470,7 +3470,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7162" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7255" } }, { @@ -3531,7 +3531,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7173" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7266" } }, { @@ -3569,7 +3569,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7184" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7277" } }, { @@ -3729,7 +3729,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7195" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7288" } }, { @@ -3913,7 +3913,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7206" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7299" } }, { @@ -4054,7 +4054,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7217" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7310" } }, { @@ -4107,7 +4107,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7228" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7321" } }, { @@ -4250,7 +4250,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7239" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7332" } }, { @@ -4474,7 +4474,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7250" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7343" } }, { @@ -4601,7 +4601,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7261" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7354" } }, { @@ -4768,7 +4768,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7272" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7365" } }, { @@ -4895,7 +4895,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7283" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7376" } }, { @@ -4933,7 +4933,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7294" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7387" } }, { @@ -4972,7 +4972,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7305" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7398" } }, { @@ -4995,7 +4995,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7316" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7409" } }, { @@ -5034,7 +5034,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7327" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7420" } }, { @@ -5057,7 +5057,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7338" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7431" } }, { @@ -5096,7 +5096,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7349" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7442" } }, { @@ -5130,7 +5130,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7360" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7453" } }, { @@ -5184,7 +5184,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7371" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7464" } }, { @@ -5223,7 +5223,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7382" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7475" } }, { @@ -5262,7 +5262,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7393" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7486" } }, { @@ -5297,7 +5297,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7404" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7497" } }, { @@ -5477,7 +5477,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7415" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7508" } }, { @@ -5506,7 +5506,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7426" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7519" } }, { @@ -5529,7 +5529,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7437" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7530" } } ] diff --git a/provider/lpseal/task_submit_commit.go b/provider/lpseal/task_submit_commit.go index 789f8833e..0c3a9ffe7 100644 --- a/provider/lpseal/task_submit_commit.go +++ b/provider/lpseal/task_submit_commit.go @@ -3,7 +3,6 @@ package lpseal import ( "bytes" "context" - "github.com/filecoin-project/lotus/provider/multictladdr" "golang.org/x/xerrors" @@ -19,6 +18,7 @@ import ( "github.com/filecoin-project/lotus/lib/harmony/harmonytask" "github.com/filecoin-project/lotus/lib/harmony/resources" "github.com/filecoin-project/lotus/provider/lpmessage" + "github.com/filecoin-project/lotus/provider/multictladdr" "github.com/filecoin-project/lotus/storage/ctladdr" ) diff --git a/provider/lpseal/task_submit_precommit.go b/provider/lpseal/task_submit_precommit.go index e6306f2b8..4200c2d3e 100644 --- a/provider/lpseal/task_submit_precommit.go +++ b/provider/lpseal/task_submit_precommit.go @@ -3,7 +3,6 @@ package lpseal import ( "bytes" "context" - "github.com/filecoin-project/lotus/provider/multictladdr" "github.com/ipfs/go-cid" "golang.org/x/xerrors" @@ -21,6 +20,7 @@ import ( "github.com/filecoin-project/lotus/lib/harmony/harmonytask" "github.com/filecoin-project/lotus/lib/harmony/resources" "github.com/filecoin-project/lotus/provider/lpmessage" + "github.com/filecoin-project/lotus/provider/multictladdr" "github.com/filecoin-project/lotus/storage/ctladdr" ) From 1d71299212380fd4b4e58f60b5744fb675595708 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sat, 17 Feb 2024 01:13:18 +0100 Subject: [PATCH 182/394] lp: Allocate maddrs map --- cmd/lotus-provider/deps/deps.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cmd/lotus-provider/deps/deps.go b/cmd/lotus-provider/deps/deps.go index 3a26c1468..c58c7ea7b 100644 --- a/cmd/lotus-provider/deps/deps.go +++ b/cmd/lotus-provider/deps/deps.go @@ -237,6 +237,9 @@ Get it with: jq .PrivateKey ~/.lotus-miner/keystore/MF2XI2BNNJ3XILLQOJUXMYLUMU`, // don't need (ehh.. maybe we do, the async callback system may actually work decently well with harmonytask) deps.LW = sealer.NewLocalWorker(sealer.WorkerConfig{}, deps.Stor, deps.LocalStore, deps.Si, nil, wstates) } + if deps.Maddrs == nil { + deps.Maddrs = map[dtypes.MinerAddress]bool{} + } if len(deps.Maddrs) == 0 { for _, s := range deps.Cfg.Addresses { for _, s := range s.MinerAddresses { From e22ab4c8fe0388224b8c532a308f8a25d994f819 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sat, 17 Feb 2024 01:40:01 +0100 Subject: [PATCH 183/394] lp: makePhase1Out for large sectors --- provider/lpffi/sdr_funcs.go | 47 ++++++++++++++++++++++++++++++++++++- storage/paths/db_index.go | 26 ++++++++++++++++++++ 2 files changed, 72 insertions(+), 1 deletion(-) diff --git a/provider/lpffi/sdr_funcs.go b/provider/lpffi/sdr_funcs.go index 6b59d9268..ae402a036 100644 --- a/provider/lpffi/sdr_funcs.go +++ b/provider/lpffi/sdr_funcs.go @@ -268,8 +268,53 @@ func (sb *SealCalls) makePhase1Out(unsCid cid.Cid, spt abi.RegisteredSealProof) Size: 64, }) } + case abi.RegisteredSealProof_StackedDrg512MiBV1_1: + phase1Output.Config.RowsToDiscard = 0 + phase1Output.Config.Size = 33554431 + phase1Output.Labels["StackedDrg512MiBV1"] = &Labels{} + phase1Output.RegisteredProof = "StackedDrg512MiBV1_1" + + for i := 0; i < 2; i++ { + phase1Output.Labels["StackedDrg512MiBV1"].Labels = append(phase1Output.Labels["StackedDrg512MiBV1"].Labels, Config{ + ID: fmt.Sprintf("layer-%d", i+1), + Path: "placeholder", + RowsToDiscard: 0, + Size: 16777216, + }) + } + + case abi.RegisteredSealProof_StackedDrg32GiBV1_1: + phase1Output.Config.RowsToDiscard = 0 + phase1Output.Config.Size = 2147483647 + phase1Output.Labels["StackedDrg32GiBV1"] = &Labels{} + phase1Output.RegisteredProof = "StackedDrg32GiBV1_1" + + for i := 0; i < 11; i++ { + phase1Output.Labels["StackedDrg32GiBV1"].Labels = append(phase1Output.Labels["StackedDrg32GiBV1"].Labels, Config{ + ID: fmt.Sprintf("layer-%d", i+1), + Path: "/placeholder", + RowsToDiscard: 0, + Size: 1073741824, + }) + } + + case abi.RegisteredSealProof_StackedDrg64GiBV1_1: + phase1Output.Config.RowsToDiscard = 0 + phase1Output.Config.Size = 4294967295 + phase1Output.Labels["StackedDrg64GiBV1"] = &Labels{} + phase1Output.RegisteredProof = "StackedDrg64GiBV1_1" + + for i := 0; i < 11; i++ { + phase1Output.Labels["StackedDrg64GiBV1"].Labels = append(phase1Output.Labels["StackedDrg64GiBV1"].Labels, Config{ + ID: fmt.Sprintf("layer-%d", i+1), + Path: "/placeholder", + RowsToDiscard: 0, + Size: 2147483648, + }) + } + default: - panic("todo") + panic("proof type not handled") } return json.Marshal(phase1Output) diff --git a/storage/paths/db_index.go b/storage/paths/db_index.go index 9d12eb70f..142a32e83 100644 --- a/storage/paths/db_index.go +++ b/storage/paths/db_index.go @@ -751,6 +751,32 @@ func (dbi *DBIndex) StorageBestAlloc(ctx context.Context, allocate storiface.Sec return result, nil } +/* +2024-02-17T01:24:10.243+0100 WARN stores paths/remote.go:258 acquireFromRemote encountered errors when fetching sector from remote {"errors": "1 error occurred:\n\t* fetch error http://10.99.16.4:3456/remote/cache/s-t02620-1 (storage 0bd90137-1219-4fb6-82d4-138a72c692b7) -> /data/3/store/cache/fetching/s-t02620-1: do request: Get \"http://10.99.16.4:3456/remote/cache/s-t02620-1\": dial tcp 10.99.16.4:3456: connect: connection refused\n\n"} +2024-02-17T01:24:10.248+0100 INFO stores paths/remote.go:359 Delete http://10.99.16.4:12300/remote/cache/s-t02620-1?keep=0ff4da63-7e0d-477e-a2ee-814c9fd7ef71 + +==> /var/log/lotus/provider1.log <== +2024-02-17T01:25:32.820+0100 INFO harmonytask harmonytask/task_type_handler.go:121 Beginning work on Task {"id": 7815, "from": "poller", "name": "WinPost"} + +==> /var/log/lotus/provider4.log <== +2024-02-17T01:25:45.058+0100 ERROR harmonytask harmonytask/task_type_handler.go:131 Recovered from a serious error while processing SDRTrees task 7702: todo Stack: goroutine 334 [running]: +github.com/filecoin-project/lotus/lib/harmony/harmonytask.(*taskTypeHandler).considerWork.func1.1() + /home/magik6k/github.com/filecoin-project/go-lotus/lib/harmony/harmonytask/task_type_handler.go:130 +0xc5 +panic({0x2210100?, 0x4557680?}) + /usr/lib/go/src/runtime/panic.go:770 +0x132 +github.com/filecoin-project/lotus/provider/lpffi.(*SealCalls).makePhase1Out(0x10?, {{0xc0009ba120?, 0xf?}}, 0x8) + /home/magik6k/github.com/filecoin-project/go-lotus/provider/lpffi/sdr_funcs.go:272 +0x445 +github.com/filecoin-project/lotus/provider/lpffi.(*SealCalls).TreeRC(0xc000884118, {0x457c9b8, 0xa4c7d40}, {{0xa3c, 0x1}, 0x8}, {{0xc0009ba120?, 0x800000000?}}) + /home/magik6k/github.com/filecoin-project/go-lotus/provider/lpffi/sdr_funcs.go:133 +0x95 +github.com/filecoin-project/lotus/provider/lpseal.(*TreesTask).Do(0xc00017c5e0, 0x1e16, 0x6?) + /home/magik6k/github.com/filecoin-project/go-lotus/provider/lpseal/task_trees.go:149 +0x5ca +github.com/filecoin-project/lotus/lib/harmony/harmonytask.(*taskTypeHandler).considerWork.func1() + /home/magik6k/github.com/filecoin-project/go-lotus/lib/harmony/harmonytask/task_type_handler.go:147 +0x2a6 +created by github.com/filecoin-project/lotus/lib/harmony/harmonytask.(*taskTypeHandler).considerWork in goroutine 230 + /home/magik6k/github.com/filecoin-project/go-lotus/lib/harmony/harmonytask/task_type_handler.go:120 +0x465 + +*/ + // timeout after which we consider a lock to be stale const LockTimeOut = 300 * time.Second From c293bcebb6bd6660d0e7966beec633eab0f60961 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sat, 17 Feb 2024 11:20:49 +0100 Subject: [PATCH 184/394] storage: Make remote Generate Vanilla more robust --- provider/lpffi/sdr_funcs.go | 2 +- storage/paths/http_handler.go | 2 +- storage/paths/interface.go | 2 +- storage/paths/local.go | 2 +- storage/paths/mocks/store.go | 6 +-- storage/paths/remote.go | 76 +++++++++++++++++++++++------------ 6 files changed, 57 insertions(+), 33 deletions(-) diff --git a/provider/lpffi/sdr_funcs.go b/provider/lpffi/sdr_funcs.go index ae402a036..daf7d5381 100644 --- a/provider/lpffi/sdr_funcs.go +++ b/provider/lpffi/sdr_funcs.go @@ -192,7 +192,7 @@ func (sb *SealCalls) GenerateSynthPoRep() { } func (sb *SealCalls) PoRepSnark(ctx context.Context, sn storiface.SectorRef, sealed, unsealed cid.Cid, ticket abi.SealRandomness, seed abi.InteractiveSealRandomness) ([]byte, error) { - vproof, err := sb.sectors.storage.GenetartePoRepVanillaProof(ctx, sn, sealed, unsealed, ticket, seed) + vproof, err := sb.sectors.storage.GeneratePoRepVanillaProof(ctx, sn, sealed, unsealed, ticket, seed) if err != nil { return nil, xerrors.Errorf("failed to generate vanilla proof: %w", err) } diff --git a/storage/paths/http_handler.go b/storage/paths/http_handler.go index 058dee71f..57de578a6 100644 --- a/storage/paths/http_handler.go +++ b/storage/paths/http_handler.go @@ -329,7 +329,7 @@ func (handler *FetchHandler) generatePoRepVanillaProof(w http.ResponseWriter, r return } - vanilla, err := handler.Local.GenetartePoRepVanillaProof(r.Context(), params.Sector, params.Sealed, params.Unsealed, params.Ticket, params.Seed) + vanilla, err := handler.Local.GeneratePoRepVanillaProof(r.Context(), params.Sector, params.Sealed, params.Unsealed, params.Ticket, params.Seed) if err != nil { http.Error(w, err.Error(), 500) return diff --git a/storage/paths/interface.go b/storage/paths/interface.go index a16821bd9..4ff206c6d 100644 --- a/storage/paths/interface.go +++ b/storage/paths/interface.go @@ -50,5 +50,5 @@ type Store interface { Reserve(ctx context.Context, sid storiface.SectorRef, ft storiface.SectorFileType, storageIDs storiface.SectorPaths, overheadTab map[storiface.SectorFileType]int) (func(), error) GenerateSingleVanillaProof(ctx context.Context, minerID abi.ActorID, si storiface.PostSectorChallenge, ppt abi.RegisteredPoStProof) ([]byte, error) - GenetartePoRepVanillaProof(ctx context.Context, sr storiface.SectorRef, sealed, unsealed cid.Cid, ticket abi.SealRandomness, seed abi.InteractiveSealRandomness) ([]byte, error) + GeneratePoRepVanillaProof(ctx context.Context, sr storiface.SectorRef, sealed, unsealed cid.Cid, ticket abi.SealRandomness, seed abi.InteractiveSealRandomness) ([]byte, error) } diff --git a/storage/paths/local.go b/storage/paths/local.go index 28e620a99..71de0becf 100644 --- a/storage/paths/local.go +++ b/storage/paths/local.go @@ -810,7 +810,7 @@ func (st *Local) GenerateSingleVanillaProof(ctx context.Context, minerID abi.Act } } -func (st *Local) GenetartePoRepVanillaProof(ctx context.Context, sr storiface.SectorRef, sealed, unsealed cid.Cid, ticket abi.SealRandomness, seed abi.InteractiveSealRandomness) ([]byte, error) { +func (st *Local) GeneratePoRepVanillaProof(ctx context.Context, sr storiface.SectorRef, sealed, unsealed cid.Cid, ticket abi.SealRandomness, seed abi.InteractiveSealRandomness) ([]byte, error) { src, _, err := st.AcquireSector(ctx, sr, storiface.FTSealed|storiface.FTCache, storiface.FTNone, storiface.PathStorage, storiface.AcquireMove) if err != nil { return nil, xerrors.Errorf("acquire sector: %w", err) diff --git a/storage/paths/mocks/store.go b/storage/paths/mocks/store.go index 19eebaab9..b7d46acbc 100644 --- a/storage/paths/mocks/store.go +++ b/storage/paths/mocks/store.go @@ -87,9 +87,9 @@ func (mr *MockStoreMockRecorder) GenerateSingleVanillaProof(arg0, arg1, arg2, ar } // GenetartePoRepVanillaProof mocks base method. -func (m *MockStore) GenetartePoRepVanillaProof(arg0 context.Context, arg1 storiface.SectorRef, arg2, arg3 cid.Cid, arg4 abi.SealRandomness, arg5 abi.InteractiveSealRandomness) ([]byte, error) { +func (m *MockStore) GeneratePoRepVanillaProof(arg0 context.Context, arg1 storiface.SectorRef, arg2, arg3 cid.Cid, arg4 abi.SealRandomness, arg5 abi.InteractiveSealRandomness) ([]byte, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GenetartePoRepVanillaProof", arg0, arg1, arg2, arg3, arg4, arg5) + ret := m.ctrl.Call(m, "GeneratePoRepVanillaProof", arg0, arg1, arg2, arg3, arg4, arg5) ret0, _ := ret[0].([]byte) ret1, _ := ret[1].(error) return ret0, ret1 @@ -98,7 +98,7 @@ func (m *MockStore) GenetartePoRepVanillaProof(arg0 context.Context, arg1 storif // GenetartePoRepVanillaProof indicates an expected call of GenetartePoRepVanillaProof. func (mr *MockStoreMockRecorder) GenetartePoRepVanillaProof(arg0, arg1, arg2, arg3, arg4, arg5 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GenetartePoRepVanillaProof", reflect.TypeOf((*MockStore)(nil).GenetartePoRepVanillaProof), arg0, arg1, arg2, arg3, arg4, arg5) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GeneratePoRepVanillaProof", reflect.TypeOf((*MockStore)(nil).GeneratePoRepVanillaProof), arg0, arg1, arg2, arg3, arg4, arg5) } // MoveStorage mocks base method. diff --git a/storage/paths/remote.go b/storage/paths/remote.go index 6433260b1..afa589683 100644 --- a/storage/paths/remote.go +++ b/storage/paths/remote.go @@ -784,13 +784,17 @@ func (r *Remote) GenerateSingleVanillaProof(ctx context.Context, minerID abi.Act return nil, err } + merr := xerrors.Errorf("sector not found") + for _, info := range si { for _, u := range info.BaseURLs { url := fmt.Sprintf("%s/vanilla/single", u) req, err := http.NewRequest("POST", url, strings.NewReader(string(jreq))) if err != nil { - return nil, xerrors.Errorf("request: %w", err) + merr = multierror.Append(merr, xerrors.Errorf("request: %w", err)) + log.Warnw("GenerateSingleVanillaProof request failed", "url", url, "error", err) + continue } if r.auth != nil { @@ -800,7 +804,9 @@ func (r *Remote) GenerateSingleVanillaProof(ctx context.Context, minerID abi.Act resp, err := http.DefaultClient.Do(req) if err != nil { - return nil, xerrors.Errorf("do request: %w", err) + merr = multierror.Append(merr, xerrors.Errorf("do request: %w", err)) + log.Warnw("GenerateSingleVanillaProof do request failed", "url", url, "error", err) + continue } if resp.StatusCode != http.StatusOK { @@ -810,14 +816,18 @@ func (r *Remote) GenerateSingleVanillaProof(ctx context.Context, minerID abi.Act } body, err := io.ReadAll(resp.Body) if err != nil { - return nil, xerrors.Errorf("resp.Body ReadAll: %w", err) + merr = multierror.Append(merr, xerrors.Errorf("resp.Body ReadAll: %w", err)) + log.Warnw("GenerateSingleVanillaProof read response body failed", "url", url, "error", err) + continue } if err := resp.Body.Close(); err != nil { log.Error("response close: ", err) } - return nil, xerrors.Errorf("non-200 code from %s: '%s'", url, strings.TrimSpace(string(body))) + merr = multierror.Append(merr, xerrors.Errorf("non-200 code from %s: '%s'", url, strings.TrimSpace(string(body)))) + log.Warnw("GenerateSingleVanillaProof non-200 code from remote", "code", resp.StatusCode, "url", url, "body", string(body)) + continue } body, err := io.ReadAll(resp.Body) @@ -826,7 +836,9 @@ func (r *Remote) GenerateSingleVanillaProof(ctx context.Context, minerID abi.Act log.Error("response close: ", err) } - return nil, xerrors.Errorf("resp.Body ReadAll: %w", err) + merr = multierror.Append(merr, xerrors.Errorf("resp.Body ReadAll: %w", err)) + log.Warnw("GenerateSingleVanillaProof read response body failed", "url", url, "error", err) + continue } _ = resp.Body.Close() @@ -835,21 +847,26 @@ func (r *Remote) GenerateSingleVanillaProof(ctx context.Context, minerID abi.Act } } - return nil, xerrors.Errorf("sector not found") + return nil, merr } -func (r *Remote) GenetartePoRepVanillaProof(ctx context.Context, sr storiface.SectorRef, sealed, unsealed cid.Cid, ticket abi.SealRandomness, seed abi.InteractiveSealRandomness) ([]byte, error) { - p, err := r.local.GenetartePoRepVanillaProof(ctx, sr, sealed, unsealed, ticket, seed) +func (r *Remote) GeneratePoRepVanillaProof(ctx context.Context, sr storiface.SectorRef, sealed, unsealed cid.Cid, ticket abi.SealRandomness, seed abi.InteractiveSealRandomness) ([]byte, error) { + // Attempt to generate the proof locally first + p, err := r.local.GeneratePoRepVanillaProof(ctx, sr, sealed, unsealed, ticket, seed) if err != errPathNotFound { return p, err } + // Define the file types to look for based on the sector's state ft := storiface.FTSealed | storiface.FTCache + + // Find sector information si, err := r.index.StorageFindSector(ctx, sr.ID, ft, 0, false) if err != nil { return nil, xerrors.Errorf("finding sector %d failed: %w", sr.ID, err) } + // Prepare request parameters requestParams := PoRepVanillaParams{ Sector: sr, Sealed: sealed, @@ -862,58 +879,65 @@ func (r *Remote) GenetartePoRepVanillaProof(ctx context.Context, sr storiface.Se return nil, err } + merr := xerrors.Errorf("sector not found") + + // Iterate over all found sector locations for _, info := range si { for _, u := range info.BaseURLs { url := fmt.Sprintf("%s/vanilla/porep", u) + // Create and send the request req, err := http.NewRequest("POST", url, strings.NewReader(string(jreq))) if err != nil { - return nil, xerrors.Errorf("request: %w", err) + merr = multierror.Append(merr, xerrors.Errorf("request: %w", err)) + log.Warnw("GeneratePoRepVanillaProof request failed", "url", url, "error", err) + continue } + // Set auth headers if available if r.auth != nil { req.Header = r.auth.Clone() } req = req.WithContext(ctx) + // Execute the request resp, err := http.DefaultClient.Do(req) if err != nil { - return nil, xerrors.Errorf("do request: %w", err) + merr = multierror.Append(merr, xerrors.Errorf("do request: %w", err)) + log.Warnw("GeneratePoRepVanillaProof do request failed", "url", url, "error", err) + continue } + // Handle non-OK status codes if resp.StatusCode != http.StatusOK { + body, _ := io.ReadAll(resp.Body) + _ = resp.Body.Close() + if resp.StatusCode == http.StatusNotFound { log.Debugw("reading vanilla proof from remote not-found response", "url", url, "store", info.ID) continue } - body, err := io.ReadAll(resp.Body) - if err != nil { - return nil, xerrors.Errorf("resp.Body ReadAll: %w", err) - } - if err := resp.Body.Close(); err != nil { - log.Error("response close: ", err) - } - - return nil, xerrors.Errorf("non-200 code from %s: '%s'", url, strings.TrimSpace(string(body))) + merr = multierror.Append(merr, xerrors.Errorf("non-200 code from %s: '%s'", url, strings.TrimSpace(string(body)))) + log.Warnw("GeneratePoRepVanillaProof non-200 code from remote", "code", resp.StatusCode, "url", url, "body", string(body)) + continue } + // Read the response body body, err := io.ReadAll(resp.Body) if err != nil { - if err := resp.Body.Close(); err != nil { - log.Error("response close: ", err) - } - - return nil, xerrors.Errorf("resp.Body ReadAll: %w", err) + merr = multierror.Append(merr, xerrors.Errorf("resp.Body ReadAll: %w", err)) + log.Warnw("GeneratePoRepVanillaProof read response body failed", "url", url, "error", err) } - _ = resp.Body.Close() + // Return the proof if successful return body, nil } } - return nil, xerrors.Errorf("sector not found") + // Return the accumulated error if the proof was not generated + return nil, merr } type funcCloser func() error From e93bcb4bc49cedcb79b068fbb01d171f365bf774 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sat, 17 Feb 2024 13:01:19 +0100 Subject: [PATCH 185/394] fix addr config unmarshal --- cmd/lotus-provider/deps/deps.go | 10 +++++++--- cmd/lotus-provider/proving.go | 19 +++++++------------ node/config/def.go | 1 + 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/cmd/lotus-provider/deps/deps.go b/cmd/lotus-provider/deps/deps.go index c58c7ea7b..74e7c91a4 100644 --- a/cmd/lotus-provider/deps/deps.go +++ b/cmd/lotus-provider/deps/deps.go @@ -142,7 +142,7 @@ func (deps *Deps) PopulateRemainingDeps(ctx context.Context, cctx *cli.Context, // The config feeds into task runners & their helpers deps.Cfg, err = GetConfig(cctx, deps.DB) if err != nil { - return err + return xerrors.Errorf("populate config: %w", err) } } @@ -251,15 +251,19 @@ Get it with: jq .PrivateKey ~/.lotus-miner/keystore/MF2XI2BNNJ3XILLQOJUXMYLUMU`, } } } - fmt.Println("last line of populate") return nil } -var oldAddresses = regexp.MustCompile("(?i)^[addresses]$") +var oldAddresses = regexp.MustCompile("(?i)^\\[addresses\\]$") func LoadConfigWithUpgrades(text string, lp *config.LotusProviderConfig) (toml.MetaData, error) { // allow migration from old config format that was limited to 1 wallet setup. newText := oldAddresses.ReplaceAllString(text, "[[addresses]]") + + if text != newText { + log.Warnw("Upgraded config!", "old", text, "new", newText) + } + meta, err := toml.Decode(newText, &lp) return meta, err } diff --git a/cmd/lotus-provider/proving.go b/cmd/lotus-provider/proving.go index cd955011b..337a8cb22 100644 --- a/cmd/lotus-provider/proving.go +++ b/cmd/lotus-provider/proving.go @@ -54,18 +54,13 @@ var wdPostTaskCmd = &cli.Command{ Usage: "deadline to compute WindowPoSt for ", Value: 0, }, - &cli.StringSliceFlag{ - Name: "layers", - Usage: "list of layers to be interpreted (atop defaults). Default: base", - Value: cli.NewStringSlice("base"), - }, }, Action: func(cctx *cli.Context) error { ctx := context.Background() deps, err := deps.GetDeps(ctx, cctx) if err != nil { - return err + return xerrors.Errorf("get config: %w", err) } ts, err := deps.Full.ChainHead(ctx) @@ -83,24 +78,24 @@ var wdPostTaskCmd = &cli.Command{ if err != nil { return xerrors.Errorf("cannot get miner id %w", err) } - var id int64 + var taskId int64 retryDelay := time.Millisecond * 10 retryAddTask: _, err = deps.DB.BeginTransaction(ctx, func(tx *harmonydb.Tx) (commit bool, err error) { - err = tx.QueryRow(`INSERT INTO harmony_task (name, posted_time, added_by) VALUES ('WdPost', CURRENT_TIMESTAMP, 123) RETURNING id`).Scan(&id) + err = tx.QueryRow(`INSERT INTO harmony_task (name, posted_time, added_by) VALUES ('WdPost', CURRENT_TIMESTAMP, 123) RETURNING id`).Scan(&taskId) if err != nil { log.Error("inserting harmony_task: ", err) return false, xerrors.Errorf("inserting harmony_task: %w", err) } _, err = tx.Exec(`INSERT INTO wdpost_partition_tasks (task_id, sp_id, proving_period_start, deadline_index, partition_index) VALUES ($1, $2, $3, $4, $5)`, - id, maddr, ht, cctx.Uint64("deadline"), 0) + taskId, maddr, ht, cctx.Uint64("deadline"), 0) if err != nil { log.Error("inserting wdpost_partition_tasks: ", err) return false, xerrors.Errorf("inserting wdpost_partition_tasks: %w", err) } - _, err = tx.Exec("INSERT INTO harmony_test (task_id) VALUES ($1)", id) + _, err = tx.Exec("INSERT INTO harmony_test (task_id) VALUES ($1)", taskId) if err != nil { return false, xerrors.Errorf("inserting into harmony_tests: %w", err) } @@ -114,11 +109,11 @@ var wdPostTaskCmd = &cli.Command{ } return xerrors.Errorf("writing SQL transaction: %w", err) } - fmt.Printf("Inserted task %v. Waiting for success ", id) + fmt.Printf("Inserted task %v. Waiting for success ", taskId) var result sql.NullString for { time.Sleep(time.Second) - err = deps.DB.QueryRow(ctx, `SELECT result FROM harmony_test WHERE task_id=$1`, id).Scan(&result) + err = deps.DB.QueryRow(ctx, `SELECT result FROM harmony_test WHERE task_id=$1`, taskId).Scan(&result) if err != nil { return xerrors.Errorf("reading result from harmony_test: %w", err) } diff --git a/node/config/def.go b/node/config/def.go index 48b877b3b..c660fdb60 100644 --- a/node/config/def.go +++ b/node/config/def.go @@ -353,6 +353,7 @@ func DefaultLotusProvider() *LotusProviderConfig { PreCommitControl: []string{}, CommitControl: []string{}, TerminateControl: []string{}, + MinerAddresses: []string{}, }}, Proving: ProvingConfig{ ParallelCheckLimit: 32, From 847b5898220479e7cef17e29274125255dc75f44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sat, 17 Feb 2024 14:51:02 +0100 Subject: [PATCH 186/394] treed: Close out file --- provider/lpproof/treed_build.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/provider/lpproof/treed_build.go b/provider/lpproof/treed_build.go index bf91da8c8..e144f695b 100644 --- a/provider/lpproof/treed_build.go +++ b/provider/lpproof/treed_build.go @@ -51,11 +51,17 @@ func hashChunk(data [][]byte) { } } -func BuildTreeD(data io.Reader, unpaddedData bool, outPath string, size abi.PaddedPieceSize) (cid.Cid, error) { +func BuildTreeD(data io.Reader, unpaddedData bool, outPath string, size abi.PaddedPieceSize) (_ cid.Cid, err error) { out, err := os.Create(outPath) if err != nil { return cid.Undef, err } + defer func() { + cerr := out.Close() + if cerr != nil { + err = multierror.Append(err, cerr) + } + }() outSize := treeSize(size) From 1f5013b607bb77480e383cf7f34638f4f6e97632 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sat, 17 Feb 2024 15:01:59 +0100 Subject: [PATCH 187/394] lpseal: Allow broken dep chains in poller --- provider/lpseal/poller.go | 30 ++++++++++++++++++++++--- provider/lpseal/poller_commit_msg.go | 2 +- provider/lpseal/poller_precommit_msg.go | 2 +- 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/provider/lpseal/poller.go b/provider/lpseal/poller.go index d42189cbe..2cf3c72a6 100644 --- a/provider/lpseal/poller.go +++ b/provider/lpseal/poller.go @@ -183,6 +183,10 @@ func (s *SealPoller) pollStartSDR(ctx context.Context, task pollTask) { } } +func (t pollTask) afterSDR() bool { + return t.AfterSDR +} + func (s *SealPoller) pollStartSDRTrees(ctx context.Context, task pollTask) { if !task.AfterTreeD && !task.AfterTreeC && !task.AfterTreeR && task.TaskTreeD == nil && task.TaskTreeC == nil && task.TaskTreeR == nil && @@ -203,8 +207,20 @@ func (s *SealPoller) pollStartSDRTrees(ctx context.Context, task pollTask) { } } +func (t pollTask) afterTrees() bool { + return t.AfterTreeD && t.AfterTreeC && t.AfterTreeR && t.afterSDR() +} + +func (t pollTask) afterPrecommitMsg() bool { + return t.AfterPrecommitMsg && t.afterTrees() +} + +func (t pollTask) afterPrecommitMsgSuccess() bool { + return t.AfterPrecommitMsgSuccess && t.afterPrecommitMsg() +} + func (s *SealPoller) pollStartPoRep(ctx context.Context, task pollTask, ts *types.TipSet) { - if s.pollers[pollerPoRep].IsSet() && task.AfterPrecommitMsgSuccess && task.SeedEpoch != nil && + if s.pollers[pollerPoRep].IsSet() && task.afterPrecommitMsgSuccess() && task.SeedEpoch != nil && task.TaskPoRep == nil && !task.AfterPoRep && ts.Height() >= abi.ChainEpoch(*task.SeedEpoch+seedEpochConfidence) { @@ -222,8 +238,12 @@ func (s *SealPoller) pollStartPoRep(ctx context.Context, task pollTask, ts *type } } +func (t pollTask) afterPoRep() bool { + return t.AfterPoRep && t.afterPrecommitMsgSuccess() +} + func (s *SealPoller) pollStartFinalize(ctx context.Context, task pollTask, ts *types.TipSet) { - if s.pollers[pollerFinalize].IsSet() && task.AfterPoRep && !task.AfterFinalize && task.TaskFinalize == nil { + if s.pollers[pollerFinalize].IsSet() && task.afterPoRep() && !task.AfterFinalize && task.TaskFinalize == nil { s.pollers[pollerFinalize].Val(ctx)(func(id harmonytask.TaskID, tx *harmonydb.Tx) (shouldCommit bool, seriousError error) { n, err := tx.Exec(`UPDATE sectors_sdr_pipeline SET task_id_finalize = $1 WHERE sp_id = $2 AND sector_number = $3 and task_id_finalize is null`, id, task.SpID, task.SectorNumber) if err != nil { @@ -238,8 +258,12 @@ func (s *SealPoller) pollStartFinalize(ctx context.Context, task pollTask, ts *t } } +func (t pollTask) afterFinalize() bool { + return t.AfterFinalize && t.afterPoRep() +} + func (s *SealPoller) pollStartMoveStorage(ctx context.Context, task pollTask) { - if s.pollers[pollerMoveStorage].IsSet() && task.AfterFinalize && !task.AfterMoveStorage && task.TaskMoveStorage == nil { + if s.pollers[pollerMoveStorage].IsSet() && task.afterFinalize() && !task.AfterMoveStorage && task.TaskMoveStorage == nil { s.pollers[pollerMoveStorage].Val(ctx)(func(id harmonytask.TaskID, tx *harmonydb.Tx) (shouldCommit bool, seriousError error) { n, err := tx.Exec(`UPDATE sectors_sdr_pipeline SET task_id_move_storage = $1 WHERE sp_id = $2 AND sector_number = $3 and task_id_move_storage is null`, id, task.SpID, task.SectorNumber) if err != nil { diff --git a/provider/lpseal/poller_commit_msg.go b/provider/lpseal/poller_commit_msg.go index a70b709bc..0350200ef 100644 --- a/provider/lpseal/poller_commit_msg.go +++ b/provider/lpseal/poller_commit_msg.go @@ -15,7 +15,7 @@ import ( ) func (s *SealPoller) pollStartCommitMsg(ctx context.Context, task pollTask) { - if task.AfterPoRep && len(task.PoRepProof) > 0 && task.TaskCommitMsg == nil && !task.AfterCommitMsg && s.pollers[pollerCommitMsg].IsSet() { + if task.afterPoRep() && len(task.PoRepProof) > 0 && task.TaskCommitMsg == nil && !task.AfterCommitMsg && s.pollers[pollerCommitMsg].IsSet() { s.pollers[pollerCommitMsg].Val(ctx)(func(id harmonytask.TaskID, tx *harmonydb.Tx) (shouldCommit bool, seriousError error) { n, err := tx.Exec(`UPDATE sectors_sdr_pipeline SET task_id_commit_msg = $1 WHERE sp_id = $2 AND sector_number = $3 and task_id_commit_msg is null`, id, task.SpID, task.SectorNumber) if err != nil { diff --git a/provider/lpseal/poller_precommit_msg.go b/provider/lpseal/poller_precommit_msg.go index 759308bf4..c57d8d46b 100644 --- a/provider/lpseal/poller_precommit_msg.go +++ b/provider/lpseal/poller_precommit_msg.go @@ -16,7 +16,7 @@ import ( ) func (s *SealPoller) pollStartPrecommitMsg(ctx context.Context, task pollTask) { - if task.TaskPrecommitMsg == nil && !task.AfterPrecommitMsg && task.AfterTreeR && task.AfterTreeD { + if task.TaskPrecommitMsg == nil && !task.AfterPrecommitMsg && task.afterTrees() { s.pollers[pollerPrecommitMsg].Val(ctx)(func(id harmonytask.TaskID, tx *harmonydb.Tx) (shouldCommit bool, seriousError error) { n, err := tx.Exec(`UPDATE sectors_sdr_pipeline SET task_id_precommit_msg = $1 WHERE sp_id = $2 AND sector_number = $3 and task_id_precommit_msg is null and after_tree_r = true and after_tree_d = true`, id, task.SpID, task.SectorNumber) if err != nil { From bc39e876094479ad02b29ee56709ed46ab9cd284 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sat, 17 Feb 2024 18:05:54 +0100 Subject: [PATCH 188/394] lpproof: Fix TreeD for large sectors --- provider/lpproof/treed_build.go | 13 +- provider/lpproof/treed_build_test.go | 222 +++++++++++++++++++++++---- provider/lpweb/hapi/watch_actor.go | 14 +- 3 files changed, 211 insertions(+), 38 deletions(-) diff --git a/provider/lpproof/treed_build.go b/provider/lpproof/treed_build.go index e144f695b..1fe0d2f0d 100644 --- a/provider/lpproof/treed_build.go +++ b/provider/lpproof/treed_build.go @@ -271,8 +271,13 @@ func treeSize(data abi.PaddedPieceSize) uint64 { } func layerOffset(size uint64, layer int) uint64 { - layerBits := uint64(1) << uint64(layer) - layerBits-- - layerOff := (size * layerBits) >> uint64(layer-1) - return layerOff + allOnes := uint64(0xffff_ffff_ffff_ffff) + + // get 'layer' bits set to 1 + layerOnes := allOnes >> uint64(64-layer) + + // shift layerOnes to the left such that the highest bit is at the same position as the highest bit in size (which is power-of-two) + sizeBitPos := bits.Len64(size) - 1 + layerOnes <<= sizeBitPos - (layer - 1) + return layerOnes } diff --git a/provider/lpproof/treed_build_test.go b/provider/lpproof/treed_build_test.go index daf742a35..634354cf5 100644 --- a/provider/lpproof/treed_build_test.go +++ b/provider/lpproof/treed_build_test.go @@ -1,9 +1,12 @@ package lpproof import ( + "bufio" "bytes" "crypto/rand" "fmt" + "github.com/filecoin-project/lotus/storage/pipeline/lib/nullreader" + "io" "os" "path/filepath" "runtime" @@ -193,7 +196,7 @@ func Test8MiB(t *testing.T) { dat, err := os.ReadFile(tempFile) require.NoError(t, err) - actualD := hexPrint32LDedup(dat) + actualD := hexPrint32LDedup(bytes.NewReader(dat)) fmt.Println(actualD) require.EqualValues(t, expectD8M, actualD) @@ -214,48 +217,168 @@ func Test8MiBUnpad(t *testing.T) { dat, err := os.ReadFile(tempFile) require.NoError(t, err) - actualD := hexPrint32LDedup(dat) + actualD := hexPrint32LDedup(bytes.NewReader(dat)) fmt.Println(actualD) require.EqualValues(t, expectD8M, actualD) require.Equal(t, "baga6ea4seaqcgqckrcapts6hea44xbqugwocqneekvyp5fizbo6u3e2biluckla", commd.String()) } -func hexPrint32LDedup(b []byte) string { - var prevLine string - var currentLine string - var isDuplicate bool +/*func Test32Golden(t *testing.T) { + datFile, err := os.Open("../../seal/cac/sc-02-data-tree-d.dat") + require.NoError(t, err) + bufReader := bufio.NewReaderSize(datFile, 1<<20) + + actualD := hexPrint32LDedup(bufReader) + fmt.Println(actualD) +} +*/ + +var expect32Null = `00000000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +* +800000000: f5 a5 fd 42 d1 6a 20 30 27 98 ef 6e d3 09 97 9b 43 00 3d 23 20 d9 f0 e8 ea 98 31 a9 27 59 fb 0b +* +c00000000: 37 31 bb 99 ac 68 9f 66 ee f5 97 3e 4a 94 da 18 8f 4d dc ae 58 07 24 fc 6f 3f d6 0d fd 48 83 33 +* +e00000000: 64 2a 60 7e f8 86 b0 04 bf 2c 19 78 46 3a e1 d4 69 3a c0 f4 10 eb 2d 1b 7a 47 fe 20 5e 5e 75 0f +* +f00000000: 57 a2 38 1a 28 65 2b f4 7f 6b ef 7a ca 67 9b e4 ae de 58 71 ab 5c f3 eb 2c 08 11 44 88 cb 85 26 +* +f80000000: 1f 7a c9 59 55 10 e0 9e a4 1c 46 0b 17 64 30 bb 32 2c d6 fb 41 2e c5 7c b1 7d 98 9a 43 10 37 2f +* +fc0000000: fc 7e 92 82 96 e5 16 fa ad e9 86 b2 8f 92 d4 4a 4f 24 b9 35 48 52 23 37 6a 79 90 27 bc 18 f8 33 +* +fe0000000: 08 c4 7b 38 ee 13 bc 43 f4 1b 91 5c 0e ed 99 11 a2 60 86 b3 ed 62 40 1b f9 d5 8b 8d 19 df f6 24 +* +ff0000000: b2 e4 7b fb 11 fa cd 94 1f 62 af 5c 75 0f 3e a5 cc 4d f5 17 d5 c4 f1 6d b2 b4 d7 7b ae c1 a3 2f +* +ff8000000: f9 22 61 60 c8 f9 27 bf dc c4 18 cd f2 03 49 31 46 00 8e ae fb 7d 02 19 4d 5e 54 81 89 00 51 08 +* +ffc000000: 2c 1a 96 4b b9 0b 59 eb fe 0f 6d a2 9a d6 5a e3 e4 17 72 4a 8f 7c 11 74 5a 40 ca c1 e5 e7 40 11 +* +ffe000000: fe e3 78 ce f1 64 04 b1 99 ed e0 b1 3e 11 b6 24 ff 9d 78 4f bb ed 87 8d 83 29 7e 79 5e 02 4f 02 +* +fff000000: 8e 9e 24 03 fa 88 4c f6 23 7f 60 df 25 f8 3e e4 0d ca 9e d8 79 eb 6f 63 52 d1 50 84 f5 ad 0d 3f +* +fff800000: 75 2d 96 93 fa 16 75 24 39 54 76 e3 17 a9 85 80 f0 09 47 af b7 a3 05 40 d6 25 a9 29 1c c1 2a 07 +* +fffc00000: 70 22 f6 0f 7e f6 ad fa 17 11 7a 52 61 9e 30 ce a8 2c 68 07 5a df 1c 66 77 86 ec 50 6e ef 2d 19 +* +fffe00000: d9 98 87 b9 73 57 3a 96 e1 13 93 64 52 36 c1 7b 1f 4c 70 34 d7 23 c7 a9 9f 70 9b b4 da 61 16 2b +* +ffff00000: d0 b5 30 db b0 b4 f2 5c 5d 2f 2a 28 df ee 80 8b 53 41 2a 02 93 1f 18 c4 99 f5 a2 54 08 6b 13 26 +* +ffff80000: 84 c0 42 1b a0 68 5a 01 bf 79 5a 23 44 06 4f e4 24 bd 52 a9 d2 43 77 b3 94 ff 4c 4b 45 68 e8 11 +* +ffffc0000: 65 f2 9e 5d 98 d2 46 c3 8b 38 8c fc 06 db 1f 6b 02 13 03 c5 a2 89 00 0b dc e8 32 a9 c3 ec 42 1c +* +ffffe0000: a2 24 75 08 28 58 50 96 5b 7e 33 4b 31 27 b0 c0 42 b1 d0 46 dc 54 40 21 37 62 7c d8 79 9c e1 3a +* +fffff0000: da fd ab 6d a9 36 44 53 c2 6d 33 72 6b 9f ef e3 43 be 8f 81 64 9e c0 09 aa d3 fa ff 50 61 75 08 +* +fffff8000: d9 41 d5 e0 d6 31 4a 99 5c 33 ff bd 4f be 69 11 8d 73 d4 e5 fd 2c d3 1f 0f 7c 86 eb dd 14 e7 06 +* +fffffc000: 51 4c 43 5c 3d 04 d3 49 a5 36 5f bd 59 ff c7 13 62 91 11 78 59 91 c1 a3 c5 3a f2 20 79 74 1a 2f +* +fffffe000: ad 06 85 39 69 d3 7d 34 ff 08 e0 9f 56 93 0a 4a d1 9a 89 de f6 0c bf ee 7e 1d 33 81 c1 e7 1c 37 +* +ffffff000: 39 56 0e 7b 13 a9 3b 07 a2 43 fd 27 20 ff a7 cb 3e 1d 2e 50 5a b3 62 9e 79 f4 63 13 51 2c da 06 +* +ffffff800: cc c3 c0 12 f5 b0 5e 81 1a 2b bf dd 0f 68 33 b8 42 75 b4 7b f2 29 c0 05 2a 82 48 4f 3c 1a 5b 3d +* +ffffffc00: 7d f2 9b 69 77 31 99 e8 f2 b4 0b 77 91 9d 04 85 09 ee d7 68 e2 c7 29 7b 1f 14 37 03 4f c3 c6 2c +* +ffffffe00: 66 ce 05 a3 66 75 52 cf 45 c0 2b cc 4e 83 92 91 9b de ac 35 de 2f f5 62 71 84 8e 9f 7b 67 51 07 +* +fffffff00: d8 61 02 18 42 5a b5 e9 5b 1c a6 23 9d 29 a2 e4 20 d7 06 a9 6f 37 3e 2f 9c 9a 91 d7 59 d1 9b 01 +* +fffffff80: 6d 36 4b 1e f8 46 44 1a 5a 4a 68 86 23 14 ac c0 a4 6f 01 67 17 e5 34 43 e8 39 ee df 83 c2 85 3c +* +fffffffc0: 07 7e 5f de 35 c5 0a 93 03 a5 50 09 e3 49 8a 4e be df f3 9c 42 b7 10 b7 30 d8 ec 7a c7 af a6 3e +` + +func Test32G(t *testing.T) { + if os.Getenv("LOTUS_TEST_LARGE_SECTORS") != "1" { + t.Skip("skipping large sector test without env LOTUS_TEST_LARGE_SECTORS=1") + } + + data := nullreader.NewNullReader(abi.PaddedPieceSize(32 << 30).Unpadded()) + + tempFile := filepath.Join(t.TempDir(), "tree.dat") + + commd, err := BuildTreeD(data, true, tempFile, 32<<30) + require.NoError(t, err) + fmt.Println(commd) + + // dump tree.dat + datFile, err := os.Open(tempFile) + require.NoError(t, err) + defer datFile.Close() + + actualD := hexPrint32LDedup(bufio.NewReaderSize(datFile, 1<<20)) + fmt.Println(actualD) + + require.EqualValues(t, expect32Null, actualD) + require.Equal(t, "baga6ea4seaqao7s73y24kcutaosvacpdjgfe5pw76ooefnyqw4ynr3d2y6x2mpq", commd.String()) +} + +func hexPrint32LDedup(r io.Reader) string { + var prevLine []byte var outStr string + var duplicateLine bool + buffer := make([]byte, 32) + offset := 0 - for i, v := range b { - // Build the current line - currentLine += fmt.Sprintf("%02x ", v) - - if (i+1)%32 == 0 || i == len(b)-1 { - // Check for duplicate line - wasDuplicate := isDuplicate - isDuplicate = currentLine == prevLine - - if !isDuplicate { - if wasDuplicate { - //fmt.Println("*") - outStr += fmt.Sprintln("*") - } - - //fmt.Printf("%08x: %s\n", i-31, currentLine) - outStr += fmt.Sprintf("%08x: %s\n", i-31, currentLine) - } - - // Update the previous line and reset current line - prevLine = currentLine - currentLine = "" + for { + n, err := r.Read(buffer) + if err == io.EOF { + break } + if err != nil { + // Handle the error according to your application's requirements + fmt.Println("Error reading:", err) + break + } + + if string(prevLine) == string(buffer) { + // Mark as duplicate and skip processing + duplicateLine = true + } else { + if duplicateLine { + // Output a marker for the previous duplicate line + outStr += "*\n" + duplicateLine = false + } + // Convert to hex and output + outStr += fmt.Sprintf("%08x: %s\n", offset, toHex(buffer)) + + // Update prevLine + if len(prevLine) != 32 { + prevLine = make([]byte, 32) + } + copy(prevLine, buffer) + } + + offset += n + } + + // If the last line was a duplicate, ensure we mark it + if duplicateLine { + outStr += "*\n" } return outStr } +func toHex(data []byte) string { + var hexStr string + for _, b := range data { + hexStr += fmt.Sprintf("%02x ", b) + } + return hexStr +} + func BenchmarkHashChunk(b *testing.B) { const benchSize = 1024 * 1024 @@ -345,3 +468,46 @@ func BenchmarkBuildTreeD512M(b *testing.B) { } } } + +func TestLayerOffset(t *testing.T) { + { + size := uint64(2048) + + require.Equal(t, uint64(0), layerOffset(size, 0)) + require.Equal(t, size, layerOffset(size, 1)) + require.Equal(t, size+(size/2), layerOffset(size, 2)) + require.Equal(t, size+(size/2)+(size/4), layerOffset(size, 3)) + require.Equal(t, size+(size/2)+(size/4)+(size/8), layerOffset(size, 4)) + require.Equal(t, size+(size/2)+(size/4)+(size/8)+(size/16), layerOffset(size, 5)) + } + + { + size := uint64(32 << 30) + maxLayers := 30 + + for i := 0; i <= maxLayers; i++ { + var expect uint64 + for j := 0; j < i; j++ { + expect += size >> uint64(j) + } + + fmt.Printf("layer %d: %d\n", i, expect) + require.Equal(t, expect, layerOffset(size, i)) + } + } + + { + size := uint64(64 << 30) + maxLayers := 31 + + for i := 0; i <= maxLayers; i++ { + var expect uint64 + for j := 0; j < i; j++ { + expect += size >> uint64(j) + } + + fmt.Printf("layer %d: %d\n", i, expect) + require.Equal(t, expect, layerOffset(size, i)) + } + } +} diff --git a/provider/lpweb/hapi/watch_actor.go b/provider/lpweb/hapi/watch_actor.go index 9505b6195..e785a415b 100644 --- a/provider/lpweb/hapi/watch_actor.go +++ b/provider/lpweb/hapi/watch_actor.go @@ -29,7 +29,7 @@ func (a *app) watchActor() { } type minimalActorInfo struct { - Addresses struct { + Addresses []struct { MinerAddresses []string } } @@ -49,12 +49,14 @@ func (a *app) updateActor(ctx context.Context) error { confNameToAddr := map[address.Address][]string{} // address -> config names err := forEachConfig[minimalActorInfo](a, func(name string, info minimalActorInfo) error { - for _, addr := range info.Addresses.MinerAddresses { - a, err := address.NewFromString(addr) - if err != nil { - return xerrors.Errorf("parsing address: %w", err) + for _, aset := range info.Addresses { + for _, addr := range aset.MinerAddresses { + a, err := address.NewFromString(addr) + if err != nil { + return xerrors.Errorf("parsing address: %w", err) + } + confNameToAddr[a] = append(confNameToAddr[a], name) } - confNameToAddr[a] = append(confNameToAddr[a], name) } return nil From 9f9e2f7fe46bd8bd076b295f94d572e888aafcf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sat, 17 Feb 2024 18:13:30 +0100 Subject: [PATCH 189/394] make gen --- provider/lpproof/treed_build_test.go | 3 ++- storage/paths/mocks/store.go | 30 ++++++++++++++-------------- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/provider/lpproof/treed_build_test.go b/provider/lpproof/treed_build_test.go index 634354cf5..c97184fb2 100644 --- a/provider/lpproof/treed_build_test.go +++ b/provider/lpproof/treed_build_test.go @@ -5,7 +5,6 @@ import ( "bytes" "crypto/rand" "fmt" - "github.com/filecoin-project/lotus/storage/pipeline/lib/nullreader" "io" "os" "path/filepath" @@ -16,6 +15,8 @@ import ( "github.com/stretchr/testify/require" "github.com/filecoin-project/go-state-types/abi" + + "github.com/filecoin-project/lotus/storage/pipeline/lib/nullreader" ) func TestTreeSize(t *testing.T) { diff --git a/storage/paths/mocks/store.go b/storage/paths/mocks/store.go index b7d46acbc..244b4fc91 100644 --- a/storage/paths/mocks/store.go +++ b/storage/paths/mocks/store.go @@ -71,6 +71,21 @@ func (mr *MockStoreMockRecorder) FsStat(arg0, arg1 interface{}) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FsStat", reflect.TypeOf((*MockStore)(nil).FsStat), arg0, arg1) } +// GeneratePoRepVanillaProof mocks base method. +func (m *MockStore) GeneratePoRepVanillaProof(arg0 context.Context, arg1 storiface.SectorRef, arg2, arg3 cid.Cid, arg4 abi.SealRandomness, arg5 abi.InteractiveSealRandomness) ([]byte, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GeneratePoRepVanillaProof", arg0, arg1, arg2, arg3, arg4, arg5) + ret0, _ := ret[0].([]byte) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GeneratePoRepVanillaProof indicates an expected call of GeneratePoRepVanillaProof. +func (mr *MockStoreMockRecorder) GeneratePoRepVanillaProof(arg0, arg1, arg2, arg3, arg4, arg5 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GeneratePoRepVanillaProof", reflect.TypeOf((*MockStore)(nil).GeneratePoRepVanillaProof), arg0, arg1, arg2, arg3, arg4, arg5) +} + // GenerateSingleVanillaProof mocks base method. func (m *MockStore) GenerateSingleVanillaProof(arg0 context.Context, arg1 abi.ActorID, arg2 storiface.PostSectorChallenge, arg3 abi.RegisteredPoStProof) ([]byte, error) { m.ctrl.T.Helper() @@ -86,21 +101,6 @@ func (mr *MockStoreMockRecorder) GenerateSingleVanillaProof(arg0, arg1, arg2, ar return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GenerateSingleVanillaProof", reflect.TypeOf((*MockStore)(nil).GenerateSingleVanillaProof), arg0, arg1, arg2, arg3) } -// GenetartePoRepVanillaProof mocks base method. -func (m *MockStore) GeneratePoRepVanillaProof(arg0 context.Context, arg1 storiface.SectorRef, arg2, arg3 cid.Cid, arg4 abi.SealRandomness, arg5 abi.InteractiveSealRandomness) ([]byte, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GeneratePoRepVanillaProof", arg0, arg1, arg2, arg3, arg4, arg5) - ret0, _ := ret[0].([]byte) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GenetartePoRepVanillaProof indicates an expected call of GenetartePoRepVanillaProof. -func (mr *MockStoreMockRecorder) GenetartePoRepVanillaProof(arg0, arg1, arg2, arg3, arg4, arg5 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GeneratePoRepVanillaProof", reflect.TypeOf((*MockStore)(nil).GeneratePoRepVanillaProof), arg0, arg1, arg2, arg3, arg4, arg5) -} - // MoveStorage mocks base method. func (m *MockStore) MoveStorage(arg0 context.Context, arg1 storiface.SectorRef, arg2 storiface.SectorFileType) error { m.ctrl.T.Helper() From bcc1d09c848af1345d059d027862f929c0c602e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sat, 17 Feb 2024 23:12:11 +0100 Subject: [PATCH 190/394] harmony: use where when updating machine timestamp --- lib/harmony/resources/resources.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/harmony/resources/resources.go b/lib/harmony/resources/resources.go index 7e42b595c..406e71da3 100644 --- a/lib/harmony/resources/resources.go +++ b/lib/harmony/resources/resources.go @@ -82,7 +82,7 @@ func Register(db *harmonydb.DB, hostnameAndPort string) (*Reg, error) { if reg.shutdown.Load() { return } - _, err := db.Exec(ctx, `UPDATE harmony_machines SET last_contact=CURRENT_TIMESTAMP`) + _, err := db.Exec(ctx, `UPDATE harmony_machines SET last_contact=CURRENT_TIMESTAMP where id=$1`, reg.MachineID) if err != nil { logger.Error("Cannot keepalive ", err) } From 9ec2a65d9bc26b5c1b32211254d6d9d820f3f8a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sun, 18 Feb 2024 00:14:55 +0100 Subject: [PATCH 191/394] state: Ignore decode errors in compute-state --html --- cli/state.go | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/cli/state.go b/cli/state.go index 31666a21c..f7d7e7127 100644 --- a/cli/state.go +++ b/cli/state.go @@ -1388,15 +1388,19 @@ func JsonParams(code cid.Cid, method abi.MethodNum, params []byte) (string, erro p, err := stmgr.GetParamType(ar, code, method) // todo use api for correct actor registry if err != nil { - return "", err + return fmt.Sprintf("raw:%x; DECODE ERR: %s", params, err.Error()), nil } if err := p.UnmarshalCBOR(bytes.NewReader(params)); err != nil { - return "", err + return fmt.Sprintf("raw:%x; DECODE cbor ERR: %s", params, err.Error()), nil } b, err := json.MarshalIndent(p, "", " ") - return string(b), err + if err != nil { + return "", err + } + + return string(b), nil } func JsonReturn(code cid.Cid, method abi.MethodNum, ret []byte) (string, error) { @@ -1407,7 +1411,7 @@ func JsonReturn(code cid.Cid, method abi.MethodNum, ret []byte) (string, error) re := reflect.New(methodMeta.Ret.Elem()) p := re.Interface().(cbg.CBORUnmarshaler) if err := p.UnmarshalCBOR(bytes.NewReader(ret)); err != nil { - return "", err + return fmt.Sprintf("raw:%x; DECODE ERR: %s", ret, err.Error()), nil } b, err := json.MarshalIndent(p, "", " ") From b52b1591763b95e86cc6012dc353f7fa95b23f49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sun, 18 Feb 2024 20:04:02 +0100 Subject: [PATCH 192/394] lotus-provider: Apply the Proving.ParallelCheckLimit config to WdPost executor --- cmd/lotus-provider/deps/deps.go | 15 ++++++++------- provider/lpseal/task_submit_precommit.go | 1 - 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/cmd/lotus-provider/deps/deps.go b/cmd/lotus-provider/deps/deps.go index 74e7c91a4..43f95625e 100644 --- a/cmd/lotus-provider/deps/deps.go +++ b/cmd/lotus-provider/deps/deps.go @@ -7,12 +7,6 @@ import ( "encoding/base64" "errors" "fmt" - "net" - "net/http" - "os" - "regexp" - "strings" - "github.com/BurntSushi/toml" "github.com/gbrlsnchs/jwt/v3" ds "github.com/ipfs/go-datastore" @@ -20,6 +14,11 @@ import ( logging "github.com/ipfs/go-log/v2" "github.com/urfave/cli/v2" "golang.org/x/xerrors" + "net" + "net/http" + "os" + "regexp" + "strings" "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-jsonrpc/auth" @@ -235,7 +234,9 @@ Get it with: jq .PrivateKey ~/.lotus-miner/keystore/MF2XI2BNNJ3XILLQOJUXMYLUMU`, // todo localWorker isn't the abstraction layer we want to use here, we probably want to go straight to ffiwrapper // maybe with a lotus-provider specific abstraction. LocalWorker does persistent call tracking which we probably // don't need (ehh.. maybe we do, the async callback system may actually work decently well with harmonytask) - deps.LW = sealer.NewLocalWorker(sealer.WorkerConfig{}, deps.Stor, deps.LocalStore, deps.Si, nil, wstates) + deps.LW = sealer.NewLocalWorker(sealer.WorkerConfig{ + MaxParallelChallengeReads: deps.Cfg.Proving.ParallelCheckLimit, + }, deps.Stor, deps.LocalStore, deps.Si, nil, wstates) } if deps.Maddrs == nil { deps.Maddrs = map[dtypes.MinerAddress]bool{} diff --git a/provider/lpseal/task_submit_precommit.go b/provider/lpseal/task_submit_precommit.go index 4200c2d3e..3c6ce7b6f 100644 --- a/provider/lpseal/task_submit_precommit.go +++ b/provider/lpseal/task_submit_precommit.go @@ -91,7 +91,6 @@ func (s *SubmitPrecommitTask) Do(taskID harmonytask.TaskID, stillOwned func() bo if err != nil { return false, xerrors.Errorf("parsing unsealed CID: %w", err) } - _ = unsealedCID params := miner.PreCommitSectorBatchParams2{} From e0f84087aca5dd7d1ac3b701502824d4c2894faa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 19 Feb 2024 08:47:30 +0100 Subject: [PATCH 193/394] lotus-provider: Use timeouts from config --- provider/builder.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/provider/builder.go b/provider/builder.go index d49333965..08f18a5d8 100644 --- a/provider/builder.go +++ b/provider/builder.go @@ -25,7 +25,7 @@ func WindowPostScheduler(ctx context.Context, fc config.LotusProviderFees, pc co stor paths.Store, idx paths.SectorIndex, max int) (*lpwindow.WdPostTask, *lpwindow.WdPostSubmitTask, *lpwindow.WdPostRecoverDeclareTask, error) { // todo config - ft := lpwindow.NewSimpleFaultTracker(stor, idx, 32, 5*time.Second, 300*time.Second) + ft := lpwindow.NewSimpleFaultTracker(stor, idx, pc.ParallelCheckLimit, time.Duration(pc.SingleCheckTimeout), time.Duration(pc.PartitionCheckTimeout)) computeTask, err := lpwindow.NewWdPostTask(db, api, ft, lw, verif, chainSched, addresses, max) if err != nil { From 648ac7adaf5daf046682147715b28c8d95111d4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 19 Feb 2024 18:08:08 +0100 Subject: [PATCH 194/394] lpdeal: Make booster- retrievals work --- cmd/lotus-shed/lpdeal.go | 5 +++- metrics/proxy/proxy.go | 40 ++++++++++++++++++++++++++++++ provider/lpmarket/fakelm/lmimpl.go | 11 ++++++-- 3 files changed, 53 insertions(+), 3 deletions(-) diff --git a/cmd/lotus-shed/lpdeal.go b/cmd/lotus-shed/lpdeal.go index 43d23f597..6fc843069 100644 --- a/cmd/lotus-shed/lpdeal.go +++ b/cmd/lotus-shed/lpdeal.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "fmt" + "github.com/filecoin-project/lotus/metrics/proxy" "io" "net" "net/http" @@ -535,7 +536,9 @@ var lpBoostProxyCmd = &cli.Command{ color.Green("%s served %.3f MiB in %s (%.2f MiB/s)", pieceCid, float64(n)/(1024*1024), took, mbps) } - mh, err := node.MinerHandler(&ast, false) // todo permissioned + finalApi := proxy.LoggingAPI[api.StorageMiner, api.StorageMinerStruct](&ast) + + mh, err := node.MinerHandler(finalApi, false) // todo permissioned if err != nil { return err } diff --git a/metrics/proxy/proxy.go b/metrics/proxy/proxy.go index 6885adfe9..1f0bd9672 100644 --- a/metrics/proxy/proxy.go +++ b/metrics/proxy/proxy.go @@ -2,8 +2,10 @@ package proxy import ( "context" + "fmt" "reflect" + logging "github.com/ipfs/go-log/v2" "go.opencensus.io/tag" "github.com/filecoin-project/lotus/api" @@ -69,3 +71,41 @@ func proxy(in interface{}, outstr interface{}) { } } } + +var log = logging.Logger("api_proxy") + +func LoggingAPI[T, P any](a T) *P { + var out P + logProxy(a, &out) + return &out +} + +func logProxy(in interface{}, outstr interface{}) { + outs := api.GetInternalStructs(outstr) + for _, out := range outs { + rint := reflect.ValueOf(out).Elem() + ra := reflect.ValueOf(in) + + for f := 0; f < rint.NumField(); f++ { + field := rint.Type().Field(f) + fn := ra.MethodByName(field.Name) + + rint.Field(f).Set(reflect.MakeFunc(field.Type, func(args []reflect.Value) (results []reflect.Value) { + var wargs []interface{} + wargs = append(wargs, "method", field.Name) + + for i := 1; i < len(args); i++ { + wargs = append(wargs, fmt.Sprintf("arg%d", i), args[i].Interface()) + } + + res := fn.Call(args) + for i, r := range res { + wargs = append(wargs, fmt.Sprintf("ret%d", i), r.Interface()) + } + + log.Debugw("APICALL", wargs...) + return res + })) + } + } +} diff --git a/provider/lpmarket/fakelm/lmimpl.go b/provider/lpmarket/fakelm/lmimpl.go index 980f1eeab..377a6225f 100644 --- a/provider/lpmarket/fakelm/lmimpl.go +++ b/provider/lpmarket/fakelm/lmimpl.go @@ -3,6 +3,8 @@ package fakelm import ( "context" "encoding/base64" + "github.com/filecoin-project/go-state-types/network" + "github.com/filecoin-project/lotus/chain/actors/builtin/miner" "net/http" "net/url" @@ -96,6 +98,11 @@ func (l *LMRPCProvider) SectorsStatus(ctx context.Context, sid abi.SectorNumber, } } + spt, err := miner.SealProofTypeFromSectorSize(l.ssize, network.Version20, false) // good enough, just need this for ssize anyways + if err != nil { + return api.SectorInfo{}, err + } + if len(si) == 0 { state := api.SectorState(sealing.UndefinedSectorState) if len(ssip) > 0 { @@ -119,7 +126,7 @@ func (l *LMRPCProvider) SectorsStatus(ctx context.Context, sid abi.SectorNumber, ReplicaUpdateMessage: nil, LastErr: "", Log: nil, - SealProof: 0, + SealProof: spt, Activation: 0, Expiration: 0, DealWeight: big.Zero(), @@ -154,7 +161,7 @@ func (l *LMRPCProvider) SectorsStatus(ctx context.Context, sid abi.SectorNumber, LastErr: "", Log: nil, - SealProof: 0, + SealProof: spt, Activation: 0, Expiration: 0, DealWeight: big.Zero(), From 94a31c6db639fe455cd17b17d2399ef548ed9ca7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 20 Feb 2024 13:14:58 +0100 Subject: [PATCH 195/394] harmony: BeginTransaction Retry Serialization option --- lib/harmony/harmonydb/harmonydb.go | 2 +- lib/harmony/harmonydb/userfuncs.go | 44 +++++++++++++++++++++++++++++- 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/lib/harmony/harmonydb/harmonydb.go b/lib/harmony/harmonydb/harmonydb.go index 5ec9f5a25..a043e903f 100644 --- a/lib/harmony/harmonydb/harmonydb.go +++ b/lib/harmony/harmonydb/harmonydb.go @@ -36,7 +36,7 @@ type DB struct { schema string hostnames []string BTFPOnce sync.Once - BTFP atomic.Uintptr + BTFP atomic.Uintptr // BeginTransactionFramePointer } var logger = logging.Logger("harmonydb") diff --git a/lib/harmony/harmonydb/userfuncs.go b/lib/harmony/harmonydb/userfuncs.go index 7fcf76dcd..6dd066d25 100644 --- a/lib/harmony/harmonydb/userfuncs.go +++ b/lib/harmony/harmonydb/userfuncs.go @@ -4,6 +4,7 @@ import ( "context" "errors" "runtime" + "time" "github.com/georgysavva/scany/v2/pgxscan" "github.com/jackc/pgerrcode" @@ -129,6 +130,25 @@ func (db *DB) usedInTransaction() bool { return lo.Contains(framePtrs, db.BTFP.Load()) // Unsafe read @ beginTx overlap, but 'return false' is correct there. } +type TransactionOptions struct { + RetrySerializationError bool + InitialSerializationErrorRetryWait time.Duration +} + +type TransactionOption func(*TransactionOptions) + +func RetrySerializationErr() TransactionOption { + return func(o *TransactionOptions) { + o.RetrySerializationError = true + } +} + +func InitialSerializationErrorRetryWait(d time.Duration) TransactionOption { + return func(o *TransactionOptions) { + o.InitialSerializationErrorRetryWait = d + } +} + // BeginTransaction is how you can access transactions using this library. // The entire transaction happens in the function passed in. // The return must be true or a rollback will occur. @@ -137,7 +157,7 @@ func (db *DB) usedInTransaction() bool { // when there is a DB serialization error. // //go:noinline -func (db *DB) BeginTransaction(ctx context.Context, f func(*Tx) (commit bool, err error)) (didCommit bool, retErr error) { +func (db *DB) BeginTransaction(ctx context.Context, f func(*Tx) (commit bool, err error), opt ...TransactionOption) (didCommit bool, retErr error) { db.BTFPOnce.Do(func() { fp := make([]uintptr, 20) runtime.Callers(1, fp) @@ -146,6 +166,28 @@ func (db *DB) BeginTransaction(ctx context.Context, f func(*Tx) (commit bool, er if db.usedInTransaction() { return false, errTx } + + opts := TransactionOptions{ + RetrySerializationError: false, + InitialSerializationErrorRetryWait: 10 * time.Millisecond, + } + + for _, o := range opt { + o(&opts) + } + +retry: + comm, err := db.transactionInner(ctx, f) + if err != nil && opts.RetrySerializationError && IsErrSerialization(err) { + time.Sleep(opts.InitialSerializationErrorRetryWait) + opts.InitialSerializationErrorRetryWait *= 2 + goto retry + } + + return comm, err +} + +func (db *DB) transactionInner(ctx context.Context, f func(*Tx) (commit bool, err error)) (didCommit bool, retErr error) { tx, err := db.pgx.BeginTx(ctx, pgx.TxOptions{}) if err != nil { return false, err From f2ebde4d106955e65ce70a8904484b399b869179 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 20 Feb 2024 13:17:29 +0100 Subject: [PATCH 196/394] lpdeal: Use serialization retry option --- cmd/lotus-shed/lpdeal.go | 3 +-- provider/lpseal/sector_num_alloc.go | 2 +- provider/lpseal/task_trees.go | 1 + 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/lotus-shed/lpdeal.go b/cmd/lotus-shed/lpdeal.go index 6fc843069..5934436f4 100644 --- a/cmd/lotus-shed/lpdeal.go +++ b/cmd/lotus-shed/lpdeal.go @@ -565,9 +565,8 @@ var lpBoostProxyCmd = &cli.Command{ server := &http.Server{ Addr: cctx.String("listen"), Handler: mux, - ReadTimeout: 15 * time.Second, + ReadTimeout: 48 * time.Hour, WriteTimeout: 48 * time.Hour, // really high because we block until TreeD - IdleTimeout: 60 * time.Second, } return server.ListenAndServe() diff --git a/provider/lpseal/sector_num_alloc.go b/provider/lpseal/sector_num_alloc.go index fd7112380..1f6a760d1 100644 --- a/provider/lpseal/sector_num_alloc.go +++ b/provider/lpseal/sector_num_alloc.go @@ -112,7 +112,7 @@ func AllocateSectorNumbers(ctx context.Context, a AllocAPI, db *harmonydb.DB, ma } return true, nil - }) + }, harmonydb.RetrySerializationErr()) if err != nil { return nil, xerrors.Errorf("allocating sector numbers: %w", err) diff --git a/provider/lpseal/task_trees.go b/provider/lpseal/task_trees.go index 322e872bb..dbef9ecde 100644 --- a/provider/lpseal/task_trees.go +++ b/provider/lpseal/task_trees.go @@ -248,6 +248,7 @@ func (u *UrlPieceReader) Read(p []byte) (n int, err error) { // if we're below the RawSize, return an unexpected EOF error if u.readSoFar < u.RawSize { + log.Errorw("unexpected EOF", "readSoFar", u.readSoFar, "rawSize", u.RawSize, "url", u.Url) return n, io.ErrUnexpectedEOF } } From 534db8d458588008e28b80fcef40f8dcd2ad21c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 20 Feb 2024 14:08:09 +0100 Subject: [PATCH 197/394] lpweb: Cache api blockstore in sectors page --- blockstore/cached.go | 112 ++++++++++++++++++ .../lpweb/hapi/simpleinfo_pipeline_porep.go | 6 +- 2 files changed, 117 insertions(+), 1 deletion(-) create mode 100644 blockstore/cached.go diff --git a/blockstore/cached.go b/blockstore/cached.go new file mode 100644 index 000000000..f71799225 --- /dev/null +++ b/blockstore/cached.go @@ -0,0 +1,112 @@ +package blockstore + +import ( + "context" + blocks "github.com/ipfs/go-block-format" + "github.com/ipfs/go-cid" +) + +// BlockstoreCache is a cache for blocks, compatible with lru.Cache; Must be safe for concurrent access +type BlockstoreCache interface { + Remove(mhString MhString) bool + Contains(mhString MhString) bool + Get(mhString MhString) (blocks.Block, bool) + Add(mhString MhString, block blocks.Block) (evicted bool) +} + +type ReadCachedBlockstore struct { + top Blockstore + cache BlockstoreCache +} + +type MhString string + +func NewReadCachedBlockstore(top Blockstore, cache BlockstoreCache) *ReadCachedBlockstore { + return &ReadCachedBlockstore{ + top: top, + cache: cache, + } +} + +func (c *ReadCachedBlockstore) DeleteBlock(ctx context.Context, cid cid.Cid) error { + c.cache.Remove(MhString(cid.Hash())) + return c.top.DeleteBlock(ctx, cid) +} + +func (c *ReadCachedBlockstore) Has(ctx context.Context, cid cid.Cid) (bool, error) { + if c.cache.Contains(MhString(cid.Hash())) { + return true, nil + } + + return c.top.Has(ctx, cid) +} + +func (c *ReadCachedBlockstore) Get(ctx context.Context, cid cid.Cid) (blocks.Block, error) { + if out, ok := c.cache.Get(MhString(cid.Hash())); ok { + return out, nil + } + + out, err := c.top.Get(ctx, cid) + if err != nil { + return nil, err + } + + c.cache.Add(MhString(cid.Hash()), out) + return out, nil +} + +func (c *ReadCachedBlockstore) GetSize(ctx context.Context, cid cid.Cid) (int, error) { + if b, ok := c.cache.Get(MhString(cid.Hash())); ok { + return len(b.RawData()), nil + } + + return c.top.GetSize(ctx, cid) +} + +func (c *ReadCachedBlockstore) Put(ctx context.Context, block blocks.Block) error { + c.cache.Add(MhString(block.Cid().Hash()), block) + return c.top.Put(ctx, block) +} + +func (c *ReadCachedBlockstore) PutMany(ctx context.Context, blocks []blocks.Block) error { + for _, b := range blocks { + c.cache.Add(MhString(b.Cid().Hash()), b) + } + + return c.top.PutMany(ctx, blocks) +} + +func (c *ReadCachedBlockstore) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { + return c.top.AllKeysChan(ctx) +} + +func (c *ReadCachedBlockstore) HashOnRead(enabled bool) { + c.top.HashOnRead(enabled) +} + +func (c *ReadCachedBlockstore) View(ctx context.Context, cid cid.Cid, callback func([]byte) error) error { + return c.top.View(ctx, cid, func(bb []byte) error { + blk, err := blocks.NewBlockWithCid(bb, cid) + if err != nil { + return err + } + + c.cache.Add(MhString(cid.Hash()), blk) + + return callback(bb) + }) +} + +func (c *ReadCachedBlockstore) DeleteMany(ctx context.Context, cids []cid.Cid) error { + for _, ci := range cids { + c.cache.Remove(MhString(ci.Hash())) + } + + return c.top.DeleteMany(ctx, cids) +} + +func (c *ReadCachedBlockstore) Flush(ctx context.Context) error { + return c.top.Flush(ctx) +} + +var _ Blockstore = (*ReadCachedBlockstore)(nil) diff --git a/provider/lpweb/hapi/simpleinfo_pipeline_porep.go b/provider/lpweb/hapi/simpleinfo_pipeline_porep.go index 36bd3221b..8bd6cf4b2 100644 --- a/provider/lpweb/hapi/simpleinfo_pipeline_porep.go +++ b/provider/lpweb/hapi/simpleinfo_pipeline_porep.go @@ -1,6 +1,8 @@ package hapi import ( + lru "github.com/hashicorp/golang-lru/v2" + blocks "github.com/ipfs/go-block-format" "net/http" "time" @@ -16,6 +18,8 @@ import ( "github.com/filecoin-project/lotus/lib/must" ) +var ChainBlockCache = must.One(lru.New[blockstore.MhString, blocks.Block](4096)) + func (a *app) pipelinePorepSectors(w http.ResponseWriter, r *http.Request) { ctx := r.Context() @@ -102,7 +106,7 @@ func (a *app) pipelinePorepSectors(w http.ResponseWriter, r *http.Request) { } epoch := head.Height() - stor := store.ActorStore(ctx, blockstore.NewAPIBlockstore(a.workingApi)) + stor := store.ActorStore(ctx, blockstore.NewReadCachedBlockstore(blockstore.NewAPIBlockstore(a.workingApi), ChainBlockCache)) type minerBitfields struct { alloc, sectorSet, active, unproven, faulty bitfield.BitField From 4808b49090074be9e6397b12ac582a7088973306 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 20 Feb 2024 14:14:26 +0100 Subject: [PATCH 198/394] lpmarket: Fix result output in case of tx retry --- provider/lpseal/sector_num_alloc.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/provider/lpseal/sector_num_alloc.go b/provider/lpseal/sector_num_alloc.go index 1f6a760d1..15f6b3435 100644 --- a/provider/lpseal/sector_num_alloc.go +++ b/provider/lpseal/sector_num_alloc.go @@ -32,6 +32,8 @@ func AllocateSectorNumbers(ctx context.Context, a AllocAPI, db *harmonydb.DB, ma var res []abi.SectorNumber comm, err := db.BeginTransaction(ctx, func(tx *harmonydb.Tx) (commit bool, err error) { + res = nil // reset result in case of retry + // query from db, if exists unmarsal to bitfield var dbAllocated bitfield.BitField var rawJson []byte From 8f2931072e5fbbd4b8c9054fed6572dbdd0745d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 20 Feb 2024 17:02:46 +0100 Subject: [PATCH 199/394] lotus-provider: EnvVar for layers --- cmd/lotus-provider/main.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/cmd/lotus-provider/main.go b/cmd/lotus-provider/main.go index 16834f898..bfb17f200 100644 --- a/cmd/lotus-provider/main.go +++ b/cmd/lotus-provider/main.go @@ -125,9 +125,10 @@ func main() { Value: "5433", }, &cli.StringSliceFlag{ - Name: "layers", - Usage: "list of layers to be interpreted (atop defaults). Default: base", - Value: cli.NewStringSlice("base"), + Name: "layers", + EnvVars: []string{"CURIO_LAYERS"}, + Usage: "list of layers to be interpreted (atop defaults). Default: base", + Value: cli.NewStringSlice("base"), }, &cli.StringFlag{ Name: deps.FlagRepoPath, From df5ac9dc2b86bf94cdbdc4ed653809baff302970 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 20 Feb 2024 17:06:54 +0100 Subject: [PATCH 200/394] fix imports --- blockstore/cached.go | 1 + cmd/lotus-provider/deps/deps.go | 11 ++++++----- cmd/lotus-shed/lpdeal.go | 2 +- provider/lpmarket/fakelm/lmimpl.go | 4 ++-- provider/lpweb/hapi/simpleinfo_pipeline_porep.go | 4 ++-- 5 files changed, 12 insertions(+), 10 deletions(-) diff --git a/blockstore/cached.go b/blockstore/cached.go index f71799225..113ec5990 100644 --- a/blockstore/cached.go +++ b/blockstore/cached.go @@ -2,6 +2,7 @@ package blockstore import ( "context" + blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" ) diff --git a/cmd/lotus-provider/deps/deps.go b/cmd/lotus-provider/deps/deps.go index 43f95625e..31e3058c1 100644 --- a/cmd/lotus-provider/deps/deps.go +++ b/cmd/lotus-provider/deps/deps.go @@ -7,6 +7,12 @@ import ( "encoding/base64" "errors" "fmt" + "net" + "net/http" + "os" + "regexp" + "strings" + "github.com/BurntSushi/toml" "github.com/gbrlsnchs/jwt/v3" ds "github.com/ipfs/go-datastore" @@ -14,11 +20,6 @@ import ( logging "github.com/ipfs/go-log/v2" "github.com/urfave/cli/v2" "golang.org/x/xerrors" - "net" - "net/http" - "os" - "regexp" - "strings" "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-jsonrpc/auth" diff --git a/cmd/lotus-shed/lpdeal.go b/cmd/lotus-shed/lpdeal.go index 5934436f4..01f886015 100644 --- a/cmd/lotus-shed/lpdeal.go +++ b/cmd/lotus-shed/lpdeal.go @@ -4,7 +4,6 @@ import ( "bytes" "context" "fmt" - "github.com/filecoin-project/lotus/metrics/proxy" "io" "net" "net/http" @@ -38,6 +37,7 @@ import ( "github.com/filecoin-project/lotus/cmd/lotus-provider/deps" "github.com/filecoin-project/lotus/lib/must" "github.com/filecoin-project/lotus/lib/nullreader" + "github.com/filecoin-project/lotus/metrics/proxy" "github.com/filecoin-project/lotus/node" "github.com/filecoin-project/lotus/provider/lpmarket" "github.com/filecoin-project/lotus/provider/lpmarket/fakelm" diff --git a/provider/lpmarket/fakelm/lmimpl.go b/provider/lpmarket/fakelm/lmimpl.go index 377a6225f..fff0a3685 100644 --- a/provider/lpmarket/fakelm/lmimpl.go +++ b/provider/lpmarket/fakelm/lmimpl.go @@ -3,8 +3,6 @@ package fakelm import ( "context" "encoding/base64" - "github.com/filecoin-project/go-state-types/network" - "github.com/filecoin-project/lotus/chain/actors/builtin/miner" "net/http" "net/url" @@ -17,8 +15,10 @@ import ( "github.com/filecoin-project/go-jsonrpc/auth" "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/big" + "github.com/filecoin-project/go-state-types/network" "github.com/filecoin-project/lotus/api" + "github.com/filecoin-project/lotus/chain/actors/builtin/miner" "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/lib/harmony/harmonydb" "github.com/filecoin-project/lotus/node/config" diff --git a/provider/lpweb/hapi/simpleinfo_pipeline_porep.go b/provider/lpweb/hapi/simpleinfo_pipeline_porep.go index 8bd6cf4b2..236f87cc2 100644 --- a/provider/lpweb/hapi/simpleinfo_pipeline_porep.go +++ b/provider/lpweb/hapi/simpleinfo_pipeline_porep.go @@ -1,11 +1,11 @@ package hapi import ( - lru "github.com/hashicorp/golang-lru/v2" - blocks "github.com/ipfs/go-block-format" "net/http" "time" + lru "github.com/hashicorp/golang-lru/v2" + blocks "github.com/ipfs/go-block-format" "golang.org/x/xerrors" "github.com/filecoin-project/go-address" From 859dbf5603020d813756c89c5864a944b504258b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 20 Feb 2024 17:35:52 +0100 Subject: [PATCH 201/394] lpwindow: Fix the test command --- cmd/lotus-provider/proving.go | 10 ++------ provider/lpwindow/compute_task.go | 38 +++++++++++++++++++++---------- 2 files changed, 28 insertions(+), 20 deletions(-) diff --git a/cmd/lotus-provider/proving.go b/cmd/lotus-provider/proving.go index 337a8cb22..51f302bcc 100644 --- a/cmd/lotus-provider/proving.go +++ b/cmd/lotus-provider/proving.go @@ -80,8 +80,6 @@ var wdPostTaskCmd = &cli.Command{ } var taskId int64 - retryDelay := time.Millisecond * 10 - retryAddTask: _, err = deps.DB.BeginTransaction(ctx, func(tx *harmonydb.Tx) (commit bool, err error) { err = tx.QueryRow(`INSERT INTO harmony_task (name, posted_time, added_by) VALUES ('WdPost', CURRENT_TIMESTAMP, 123) RETURNING id`).Scan(&taskId) if err != nil { @@ -100,13 +98,8 @@ var wdPostTaskCmd = &cli.Command{ return false, xerrors.Errorf("inserting into harmony_tests: %w", err) } return true, nil - }) + }, harmonydb.RetrySerializationErr()) if err != nil { - if harmonydb.IsErrSerialization(err) { - time.Sleep(retryDelay) - retryDelay *= 2 - goto retryAddTask - } return xerrors.Errorf("writing SQL transaction: %w", err) } fmt.Printf("Inserted task %v. Waiting for success ", taskId) @@ -122,6 +115,7 @@ var wdPostTaskCmd = &cli.Command{ } fmt.Print(".") } + fmt.Println() log.Infof("Result: %s", result.String) return nil }, diff --git a/provider/lpwindow/compute_task.go b/provider/lpwindow/compute_task.go index 08f99e3e9..65102d0f0 100644 --- a/provider/lpwindow/compute_task.go +++ b/provider/lpwindow/compute_task.go @@ -135,11 +135,34 @@ func (t *WdPostTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (done deadline := wdpost.NewDeadlineInfo(abi.ChainEpoch(pps), dlIdx, head.Height()) - if deadline.PeriodElapsed() { + var testTask *int + isTestTask := func() bool { + if testTask != nil { + return *testTask > 0 + } + + testTask = new(int) + err := t.db.QueryRow(context.Background(), `SELECT COUNT(*) FROM harmony_test WHERE task_id = $1`, taskID).Scan(testTask) + if err != nil { + log.Errorf("WdPostTask.Do() failed to queryRow: %v", err) + return false + } + + return *testTask > 0 + } + + if deadline.PeriodElapsed() && !isTestTask() { log.Errorf("WdPost removed stale task: %v %v", taskID, deadline) return true, nil } + if deadline.Challenge > head.Height() { + if isTestTask() { + deadline = wdpost.NewDeadlineInfo(abi.ChainEpoch(pps)-deadline.WPoStProvingPeriod, dlIdx, head.Height()-deadline.WPoStProvingPeriod) + log.Warnw("Test task is in the future, adjusting to past", "taskID", taskID, "deadline", deadline) + } + } + maddr, err := address.NewIDAddress(spID) if err != nil { log.Errorf("WdPostTask.Do() failed to NewIDAddress: %v", err) @@ -163,11 +186,7 @@ func (t *WdPostTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (done return false, xerrors.Errorf("marshaling PoSt: %w", err) } - testTaskIDCt := 0 - if err = t.db.QueryRow(context.Background(), `SELECT COUNT(*) FROM harmony_test WHERE task_id = $1`, taskID).Scan(&testTaskIDCt); err != nil { - return false, xerrors.Errorf("querying for test task: %w", err) - } - if testTaskIDCt == 1 { + if isTestTask() { // Do not send test tasks to the chain but to harmony_test & stdout. data, err := json.MarshalIndent(map[string]any{ @@ -243,7 +262,6 @@ func (t *WdPostTask) CanAccept(ids []harmonytask.TaskID, te *harmonytask.TaskEng PartitionIndex uint64 dlInfo *dline.Info `pgx:"-"` - openTs *types.TipSet } var tasks []wdTaskDef @@ -265,13 +283,9 @@ func (t *WdPostTask) CanAccept(ids []harmonytask.TaskID, te *harmonytask.TaskEng tasks[i].dlInfo = wdpost.NewDeadlineInfo(tasks[i].ProvingPeriodStart, tasks[i].DeadlineIndex, ts.Height()) if tasks[i].dlInfo.PeriodElapsed() { + // note: Those may be test tasks return &tasks[i].TaskID, nil } - - tasks[i].openTs, err = t.api.ChainGetTipSetAfterHeight(context.Background(), tasks[i].dlInfo.Open, ts.Key()) - if err != nil { - return nil, xerrors.Errorf("getting task open tipset: %w", err) - } } // todo fix the block below From f62dedc0207b6eceb0693aa2b1763138623f577a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 20 Feb 2024 19:33:06 +0100 Subject: [PATCH 202/394] lpseal: Correctly set expiration epochs when deals are present --- provider/lpseal/task_submit_precommit.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/provider/lpseal/task_submit_precommit.go b/provider/lpseal/task_submit_precommit.go index 3c6ce7b6f..616336103 100644 --- a/provider/lpseal/task_submit_precommit.go +++ b/provider/lpseal/task_submit_precommit.go @@ -110,19 +110,25 @@ func (s *SubmitPrecommitTask) Do(taskID harmonytask.TaskID, stillOwned func() bo PieceCID string `db:"piece_cid"` PieceSize int64 `db:"piece_size"` - F05DealID int64 `db:"f05_deal_id"` + F05DealID int64 `db:"f05_deal_id"` + F05DealEndEpoch int64 `db:"f05_deal_end_epoch"` } err = s.db.Select(ctx, &pieces, ` - SELECT piece_index, piece_cid, piece_size, f05_deal_id + SELECT piece_index, piece_cid, piece_size, f05_deal_id, f05_deal_end_epoch FROM sectors_sdr_initial_pieces WHERE sp_id = $1 AND sector_number = $2 ORDER BY piece_index asc`, sectorParams.SpID, sectorParams.SectorNumber) if err != nil { return false, xerrors.Errorf("getting pieces: %w", err) } + if len(pieces) > 1 { + return false, xerrors.Errorf("too many pieces") // todo support multiple pieces + } + if len(pieces) > 0 { params.Sectors[0].UnsealedCid = &unsealedCID + params.Sectors[0].Expiration = abi.ChainEpoch(pieces[0].F05DealEndEpoch) for _, p := range pieces { params.Sectors[0].DealIDs = append(params.Sectors[0].DealIDs, abi.DealID(p.F05DealID)) From 91fd0ec101f71560ca941abee2766efa8b466cf2 Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Thu, 15 Feb 2024 08:29:39 +1100 Subject: [PATCH 203/394] No sed backup file --- scripts/fiximports | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/scripts/fiximports b/scripts/fiximports index 360051676..140cebbce 100755 --- a/scripts/fiximports +++ b/scripts/fiximports @@ -1,17 +1,22 @@ #!/usr/bin/env bash +sed_replace='/import (/ { + :1 + $!N + s/\n\n/\'$'\n''/ + /)/!b1 +}' + go_files() { find . -type f -name \*.go -not -name \*_cbor_gen.go | grep -v './extern/filecoin-ffi' | grep -v './extern/test-vectors' } -go_files | xargs -I '{}' sed -i.ifixbak -e '/import (/ { - :1 - $!N - s/\n\n/\'$'\n''/ - /)/!b1 -}' '{}' - -find . -type f -name \*.go.ifixbak | xargs rm +# Because -i works differently on macOS, we need to use a different sed command +if [[ "$OSTYPE" == "darwin"* ]]; then + go_files | xargs -I '{}' sed -i '' -e "$sed_replace" '{}' +else + go_files | xargs -I '{}' sed -i -e "$sed_replace" '{}' +fi go_files | xargs -I '{}' goimports -w -local "github.com/filecoin-project" '{}' go_files | xargs -I '{}' goimports -w -local "github.com/filecoin-project/lotus" '{}' From edbff6b2e915c339701e4dbf8b98fb5de6f8fcee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 21 Feb 2024 10:46:02 +0100 Subject: [PATCH 204/394] lpseal: Fix precommit poller hang --- provider/lpseal/poller_precommit_msg.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/provider/lpseal/poller_precommit_msg.go b/provider/lpseal/poller_precommit_msg.go index c57d8d46b..be80ec1df 100644 --- a/provider/lpseal/poller_precommit_msg.go +++ b/provider/lpseal/poller_precommit_msg.go @@ -16,7 +16,7 @@ import ( ) func (s *SealPoller) pollStartPrecommitMsg(ctx context.Context, task pollTask) { - if task.TaskPrecommitMsg == nil && !task.AfterPrecommitMsg && task.afterTrees() { + if task.TaskPrecommitMsg == nil && !task.AfterPrecommitMsg && task.afterTrees() && s.pollers[pollerPrecommitMsg].IsSet() { s.pollers[pollerPrecommitMsg].Val(ctx)(func(id harmonytask.TaskID, tx *harmonydb.Tx) (shouldCommit bool, seriousError error) { n, err := tx.Exec(`UPDATE sectors_sdr_pipeline SET task_id_precommit_msg = $1 WHERE sp_id = $2 AND sector_number = $3 and task_id_precommit_msg is null and after_tree_r = true and after_tree_d = true`, id, task.SpID, task.SectorNumber) if err != nil { From 91d3edecfd96a14b95037b4bf798563142e6edff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 21 Feb 2024 13:28:49 +0100 Subject: [PATCH 205/394] lp: Address review --- cmd/lotus-provider/cli.go | 2 +- provider/lpseal/task_trees.go | 3 --- provider/lpweb/hapi/robust_rpc.go | 5 +++- storage/paths/db_index.go | 41 ++++++------------------------- 4 files changed, 12 insertions(+), 39 deletions(-) diff --git a/cmd/lotus-provider/cli.go b/cmd/lotus-provider/cli.go index 86e01ab35..dc1bdde16 100644 --- a/cmd/lotus-provider/cli.go +++ b/cmd/lotus-provider/cli.go @@ -30,7 +30,7 @@ var cliCmd = &cli.Command{ Flags: []cli.Flag{ &cli.StringFlag{ Name: "machine", - Usage: "machine host:port", + Usage: "machine host:port (lotus-provider run --listen address)", }, }, Before: func(cctx *cli.Context) error { diff --git a/provider/lpseal/task_trees.go b/provider/lpseal/task_trees.go index dbef9ecde..3f9b000cd 100644 --- a/provider/lpseal/task_trees.go +++ b/provider/lpseal/task_trees.go @@ -142,9 +142,6 @@ func (t *TreesTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (done return false, xerrors.Errorf("computing tree d: %w", err) } - // todo: Sooo tree-d contains exactly the unsealed data in the prefix - // when we finalize we can totally just truncate that file and move it to unsealed !! - // R / C sealed, unsealed, err := t.sc.TreeRC(ctx, sref, commd) if err != nil { diff --git a/provider/lpweb/hapi/robust_rpc.go b/provider/lpweb/hapi/robust_rpc.go index 8f0780b8d..e9914bfa6 100644 --- a/provider/lpweb/hapi/robust_rpc.go +++ b/provider/lpweb/hapi/robust_rpc.go @@ -78,7 +78,10 @@ func (a *app) updateRpc(ctx context.Context) error { if err != nil { continue } - _ = closer // todo + go func() { + <-ctx.Done() + closer() + }() a.workingApi = v1api } diff --git a/storage/paths/db_index.go b/storage/paths/db_index.go index 142a32e83..4c935762f 100644 --- a/storage/paths/db_index.go +++ b/storage/paths/db_index.go @@ -180,8 +180,6 @@ func (dbi *DBIndex) StorageAttach(ctx context.Context, si storiface.StorageInfo, } } - retryWait := time.Millisecond * 20 -retryAttachStorage: // Single transaction to attach storage which is not present in the DB _, err := dbi.harmonyDB.BeginTransaction(ctx, func(tx *harmonydb.Tx) (commit bool, err error) { var urls sql.NullString @@ -243,17 +241,9 @@ retryAttachStorage: return false, xerrors.Errorf("StorageAttach insert fails: %v", err) } return true, nil - }) - if err != nil { - if harmonydb.IsErrSerialization(err) { - time.Sleep(retryWait) - retryWait *= 2 - goto retryAttachStorage - } - return err - } + }, harmonydb.RetrySerializationErr()) - return nil + return err } func (dbi *DBIndex) StorageDetach(ctx context.Context, id storiface.ID, url string) error { @@ -289,8 +279,6 @@ func (dbi *DBIndex) StorageDetach(ctx context.Context, id storiface.ID, url stri log.Warnw("Dropping sector path endpoint", "path", id, "url", url) } else { - retryWait := time.Millisecond * 20 - retryDropPath: // Single transaction to drop storage path and sector decls which have this as a storage path _, err := dbi.harmonyDB.BeginTransaction(ctx, func(tx *harmonydb.Tx) (commit bool, err error) { // Drop storage path completely @@ -305,19 +293,14 @@ func (dbi *DBIndex) StorageDetach(ctx context.Context, id storiface.ID, url stri return false, err } return true, nil - }) + }, harmonydb.RetrySerializationErr()) if err != nil { - if harmonydb.IsErrSerialization(err) { - time.Sleep(retryWait) - retryWait *= 2 - goto retryDropPath - } return err } log.Warnw("Dropping sector storage", "path", id) } - return nil + return err } func (dbi *DBIndex) StorageReportHealth(ctx context.Context, id storiface.ID, report storiface.HealthReport) error { @@ -392,8 +375,6 @@ func (dbi *DBIndex) StorageDeclareSector(ctx context.Context, storageID storifac return xerrors.Errorf("invalid filetype") } - retryWait := time.Millisecond * 100 -retryStorageDeclareSector: _, err := dbi.harmonyDB.BeginTransaction(ctx, func(tx *harmonydb.Tx) (commit bool, err error) { var currPrimary sql.NullBool err = tx.QueryRow( @@ -426,17 +407,9 @@ retryStorageDeclareSector: } return true, nil - }) - if err != nil { - if harmonydb.IsErrSerialization(err) { - time.Sleep(retryWait) - retryWait *= 2 - goto retryStorageDeclareSector - } - return err - } + }, harmonydb.RetrySerializationErr()) - return nil + return err } func (dbi *DBIndex) StorageDropSector(ctx context.Context, storageID storiface.ID, s abi.SectorID, ft storiface.SectorFileType) error { @@ -873,7 +846,7 @@ func (dbi *DBIndex) lock(ctx context.Context, sector abi.SectorID, read storifac } return true, nil - }) + }, harmonydb.RetrySerializationErr()) if err != nil { return false, err } From 3b5165358af3f258430854da2f6a49663c35da52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 21 Feb 2024 13:33:49 +0100 Subject: [PATCH 206/394] lpproof treed: defer workWg.Done --- provider/lpproof/treed_build.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/provider/lpproof/treed_build.go b/provider/lpproof/treed_build.go index 1fe0d2f0d..12d0ee93e 100644 --- a/provider/lpproof/treed_build.go +++ b/provider/lpproof/treed_build.go @@ -176,6 +176,8 @@ func BuildTreeD(data io.Reader, unpaddedData bool, outPath string, size abi.Padd // start processing workWg.Add(1) go func(startOffset uint64) { + defer workWg.Done() + if unpaddedData { paddedBuf := pool.Get(int(abi.UnpaddedPieceSize(len(workBuffer[0])).Padded())) fr32.PadSingle(workBuffer[0], paddedBuf) @@ -214,8 +216,6 @@ func BuildTreeD(data io.Reader, unpaddedData bool, outPath string, size abi.Padd bufLk.Lock() workerBuffers = append(workerBuffers, workBuffer) bufLk.Unlock() - - workWg.Done() }(processed) processed += processedSize From 4ae73f059c0fbf6d3b79171be2ffdb7dea62096b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 21 Feb 2024 14:19:48 +0100 Subject: [PATCH 207/394] provider: Fix sql query capitalization --- cmd/lotus-provider/proving.go | 2 +- lib/harmony/harmonydb/userfuncs.go | 4 ++-- provider/lpmarket/deal_ingest.go | 2 +- provider/lpmarket/fakelm/lmimpl.go | 2 +- provider/lpseal/poller.go | 12 ++++++------ provider/lpseal/poller_commit_msg.go | 12 ++++++------ provider/lpseal/poller_precommit_msg.go | 12 ++++++------ provider/lpseal/sector_num_alloc.go | 6 +++--- provider/lpseal/task_finalize.go | 11 ++++++----- provider/lpseal/task_movestorage.go | 4 ++-- provider/lpseal/task_porep.go | 2 +- provider/lpseal/task_sdr.go | 2 +- provider/lpseal/task_submit_commit.go | 2 +- provider/lpseal/task_submit_precommit.go | 4 ++-- provider/lpseal/task_trees.go | 4 ++-- storage/paths/db_index.go | 8 ++++---- 16 files changed, 45 insertions(+), 44 deletions(-) diff --git a/cmd/lotus-provider/proving.go b/cmd/lotus-provider/proving.go index 51f302bcc..cfe7e921a 100644 --- a/cmd/lotus-provider/proving.go +++ b/cmd/lotus-provider/proving.go @@ -98,7 +98,7 @@ var wdPostTaskCmd = &cli.Command{ return false, xerrors.Errorf("inserting into harmony_tests: %w", err) } return true, nil - }, harmonydb.RetrySerializationErr()) + }, harmonydb.OptionRetry()) if err != nil { return xerrors.Errorf("writing SQL transaction: %w", err) } diff --git a/lib/harmony/harmonydb/userfuncs.go b/lib/harmony/harmonydb/userfuncs.go index 6dd066d25..759cbd322 100644 --- a/lib/harmony/harmonydb/userfuncs.go +++ b/lib/harmony/harmonydb/userfuncs.go @@ -137,13 +137,13 @@ type TransactionOptions struct { type TransactionOption func(*TransactionOptions) -func RetrySerializationErr() TransactionOption { +func OptionRetry() TransactionOption { return func(o *TransactionOptions) { o.RetrySerializationError = true } } -func InitialSerializationErrorRetryWait(d time.Duration) TransactionOption { +func OptionSerialRetryTime(d time.Duration) TransactionOption { return func(o *TransactionOptions) { o.InitialSerializationErrorRetryWait = d } diff --git a/provider/lpmarket/deal_ingest.go b/provider/lpmarket/deal_ingest.go index bfab45e34..0858cf801 100644 --- a/provider/lpmarket/deal_ingest.go +++ b/provider/lpmarket/deal_ingest.go @@ -78,7 +78,7 @@ func (p *PieceIngester) AllocatePieceToSector(ctx context.Context, maddr address } n := numbers[0] - _, err := tx.Exec("insert into sectors_sdr_pipeline (sp_id, sector_number, reg_seal_proof) values ($1, $2, $3)", mid, n, spt) + _, err := tx.Exec("INSERT INTO sectors_sdr_pipeline (sp_id, sector_number, reg_seal_proof) VALUES ($1, $2, $3)", mid, n, spt) if err != nil { return false, xerrors.Errorf("inserting into sectors_sdr_pipeline: %w", err) } diff --git a/provider/lpmarket/fakelm/lmimpl.go b/provider/lpmarket/fakelm/lmimpl.go index fff0a3685..abbc4d04d 100644 --- a/provider/lpmarket/fakelm/lmimpl.go +++ b/provider/lpmarket/fakelm/lmimpl.go @@ -75,7 +75,7 @@ func (l *LMRPCProvider) SectorsStatus(ctx context.Context, sid abi.SectorNumber, DealID *int64 `db:"f05_deal_id"` } - err = l.db.Select(ctx, &ssip, "select ssip.piece_cid, ssip.f05_deal_id from sectors_sdr_pipeline p left join sectors_sdr_initial_pieces ssip on p.sp_id = ssip.sp_id and p.sector_number = ssip.sector_number where p.sp_id = $1 and p.sector_number = $2", l.minerID, sid) + err = l.db.Select(ctx, &ssip, "SELECT ssip.piece_cid, ssip.f05_deal_id FROM sectors_sdr_pipeline p LEFT JOIN sectors_sdr_initial_pieces ssip ON p.sp_id = ssip.sp_id AND p.sector_number = ssip.sector_number WHERE p.sp_id = $1 AND p.sector_number = $2", l.minerID, sid) if err != nil { return api.SectorInfo{}, err } diff --git a/provider/lpseal/poller.go b/provider/lpseal/poller.go index 2cf3c72a6..cbdcb3fd9 100644 --- a/provider/lpseal/poller.go +++ b/provider/lpseal/poller.go @@ -137,7 +137,7 @@ func (s *SealPoller) poll(ctx context.Context) error { task_id_commit_msg, after_commit_msg, after_commit_msg_success, failed, failed_reason - FROM sectors_sdr_pipeline WHERE after_commit_msg_success != true or after_move_storage != true`) + FROM sectors_sdr_pipeline WHERE after_commit_msg_success != TRUE OR after_move_storage != TRUE`) if err != nil { return err } @@ -170,7 +170,7 @@ func (s *SealPoller) poll(ctx context.Context) error { func (s *SealPoller) pollStartSDR(ctx context.Context, task pollTask) { if !task.AfterSDR && task.TaskSDR == nil && s.pollers[pollerSDR].IsSet() { s.pollers[pollerSDR].Val(ctx)(func(id harmonytask.TaskID, tx *harmonydb.Tx) (shouldCommit bool, seriousError error) { - n, err := tx.Exec(`UPDATE sectors_sdr_pipeline SET task_id_sdr = $1 WHERE sp_id = $2 AND sector_number = $3 and task_id_sdr is null`, id, task.SpID, task.SectorNumber) + n, err := tx.Exec(`UPDATE sectors_sdr_pipeline SET task_id_sdr = $1 WHERE sp_id = $2 AND sector_number = $3 AND task_id_sdr IS NULL`, id, task.SpID, task.SectorNumber) if err != nil { return false, xerrors.Errorf("update sectors_sdr_pipeline: %w", err) } @@ -194,7 +194,7 @@ func (s *SealPoller) pollStartSDRTrees(ctx context.Context, task pollTask) { s.pollers[pollerTrees].Val(ctx)(func(id harmonytask.TaskID, tx *harmonydb.Tx) (shouldCommit bool, seriousError error) { n, err := tx.Exec(`UPDATE sectors_sdr_pipeline SET task_id_tree_d = $1, task_id_tree_c = $1, task_id_tree_r = $1 - WHERE sp_id = $2 AND sector_number = $3 and after_sdr = true and task_id_tree_d is null and task_id_tree_c is null and task_id_tree_r is null`, id, task.SpID, task.SectorNumber) + WHERE sp_id = $2 AND sector_number = $3 AND after_sdr = TRUE AND task_id_tree_d IS NULL AND task_id_tree_c IS NULL AND task_id_tree_r IS NULL`, id, task.SpID, task.SectorNumber) if err != nil { return false, xerrors.Errorf("update sectors_sdr_pipeline: %w", err) } @@ -225,7 +225,7 @@ func (s *SealPoller) pollStartPoRep(ctx context.Context, task pollTask, ts *type ts.Height() >= abi.ChainEpoch(*task.SeedEpoch+seedEpochConfidence) { s.pollers[pollerPoRep].Val(ctx)(func(id harmonytask.TaskID, tx *harmonydb.Tx) (shouldCommit bool, seriousError error) { - n, err := tx.Exec(`UPDATE sectors_sdr_pipeline SET task_id_porep = $1 WHERE sp_id = $2 AND sector_number = $3 and task_id_porep is null`, id, task.SpID, task.SectorNumber) + n, err := tx.Exec(`UPDATE sectors_sdr_pipeline SET task_id_porep = $1 WHERE sp_id = $2 AND sector_number = $3 AND task_id_porep IS NULL`, id, task.SpID, task.SectorNumber) if err != nil { return false, xerrors.Errorf("update sectors_sdr_pipeline: %w", err) } @@ -245,7 +245,7 @@ func (t pollTask) afterPoRep() bool { func (s *SealPoller) pollStartFinalize(ctx context.Context, task pollTask, ts *types.TipSet) { if s.pollers[pollerFinalize].IsSet() && task.afterPoRep() && !task.AfterFinalize && task.TaskFinalize == nil { s.pollers[pollerFinalize].Val(ctx)(func(id harmonytask.TaskID, tx *harmonydb.Tx) (shouldCommit bool, seriousError error) { - n, err := tx.Exec(`UPDATE sectors_sdr_pipeline SET task_id_finalize = $1 WHERE sp_id = $2 AND sector_number = $3 and task_id_finalize is null`, id, task.SpID, task.SectorNumber) + n, err := tx.Exec(`UPDATE sectors_sdr_pipeline SET task_id_finalize = $1 WHERE sp_id = $2 AND sector_number = $3 AND task_id_finalize IS NULL`, id, task.SpID, task.SectorNumber) if err != nil { return false, xerrors.Errorf("update sectors_sdr_pipeline: %w", err) } @@ -265,7 +265,7 @@ func (t pollTask) afterFinalize() bool { func (s *SealPoller) pollStartMoveStorage(ctx context.Context, task pollTask) { if s.pollers[pollerMoveStorage].IsSet() && task.afterFinalize() && !task.AfterMoveStorage && task.TaskMoveStorage == nil { s.pollers[pollerMoveStorage].Val(ctx)(func(id harmonytask.TaskID, tx *harmonydb.Tx) (shouldCommit bool, seriousError error) { - n, err := tx.Exec(`UPDATE sectors_sdr_pipeline SET task_id_move_storage = $1 WHERE sp_id = $2 AND sector_number = $3 and task_id_move_storage is null`, id, task.SpID, task.SectorNumber) + n, err := tx.Exec(`UPDATE sectors_sdr_pipeline SET task_id_move_storage = $1 WHERE sp_id = $2 AND sector_number = $3 AND task_id_move_storage IS NULL`, id, task.SpID, task.SectorNumber) if err != nil { return false, xerrors.Errorf("update sectors_sdr_pipeline: %w", err) } diff --git a/provider/lpseal/poller_commit_msg.go b/provider/lpseal/poller_commit_msg.go index 0350200ef..d17ccd906 100644 --- a/provider/lpseal/poller_commit_msg.go +++ b/provider/lpseal/poller_commit_msg.go @@ -17,7 +17,7 @@ import ( func (s *SealPoller) pollStartCommitMsg(ctx context.Context, task pollTask) { if task.afterPoRep() && len(task.PoRepProof) > 0 && task.TaskCommitMsg == nil && !task.AfterCommitMsg && s.pollers[pollerCommitMsg].IsSet() { s.pollers[pollerCommitMsg].Val(ctx)(func(id harmonytask.TaskID, tx *harmonydb.Tx) (shouldCommit bool, seriousError error) { - n, err := tx.Exec(`UPDATE sectors_sdr_pipeline SET task_id_commit_msg = $1 WHERE sp_id = $2 AND sector_number = $3 and task_id_commit_msg is null`, id, task.SpID, task.SectorNumber) + n, err := tx.Exec(`UPDATE sectors_sdr_pipeline SET task_id_commit_msg = $1 WHERE sp_id = $2 AND sector_number = $3 AND task_id_commit_msg IS NULL`, id, task.SpID, task.SectorNumber) if err != nil { return false, xerrors.Errorf("update sectors_sdr_pipeline: %w", err) } @@ -37,7 +37,7 @@ func (s *SealPoller) pollCommitMsgLanded(ctx context.Context, task pollTask) err err := s.db.Select(ctx, &execResult, `SELECT spipeline.precommit_msg_cid, spipeline.commit_msg_cid, executed_tsk_cid, executed_tsk_epoch, executed_msg_cid, executed_rcpt_exitcode, executed_rcpt_gas_used FROM sectors_sdr_pipeline spipeline JOIN message_waits ON spipeline.commit_msg_cid = message_waits.signed_message_cid - WHERE sp_id = $1 AND sector_number = $2 AND executed_tsk_epoch is not null`, task.SpID, task.SectorNumber) + WHERE sp_id = $1 AND sector_number = $2 AND executed_tsk_epoch IS NOT NULL`, task.SpID, task.SectorNumber) if err != nil { log.Errorw("failed to query message_waits", "error", err) } @@ -64,8 +64,8 @@ func (s *SealPoller) pollCommitMsgLanded(ctx context.Context, task pollTask) err // yay! _, err := s.db.Exec(ctx, `UPDATE sectors_sdr_pipeline SET - after_commit_msg_success = true, commit_msg_tsk = $1 - WHERE sp_id = $2 AND sector_number = $3 and after_commit_msg_success = false`, + after_commit_msg_success = TRUE, commit_msg_tsk = $1 + WHERE sp_id = $2 AND sector_number = $3 AND after_commit_msg_success = FALSE`, execResult[0].ExecutedTskCID, task.SpID, task.SectorNumber) if err != nil { return xerrors.Errorf("update sectors_sdr_pipeline: %w", err) @@ -97,8 +97,8 @@ func (s *SealPoller) pollRetryCommitMsgSend(ctx context.Context, task pollTask, // make the pipeline entry seem like precommit send didn't happen, next poll loop will retry _, err := s.db.Exec(ctx, `UPDATE sectors_sdr_pipeline SET - commit_msg_cid = null, task_id_commit_msg = null - WHERE commit_msg_cid = $1 AND sp_id = $2 AND sector_number = $3 AND after_commit_msg_success = false`, + commit_msg_cid = NULL, task_id_commit_msg = NULL + WHERE commit_msg_cid = $1 AND sp_id = $2 AND sector_number = $3 AND after_commit_msg_success = FALSE`, *execResult.CommitMsgCID, task.SpID, task.SectorNumber) if err != nil { return xerrors.Errorf("update sectors_sdr_pipeline to retry precommit msg send: %w", err) diff --git a/provider/lpseal/poller_precommit_msg.go b/provider/lpseal/poller_precommit_msg.go index be80ec1df..5f6958b53 100644 --- a/provider/lpseal/poller_precommit_msg.go +++ b/provider/lpseal/poller_precommit_msg.go @@ -18,7 +18,7 @@ import ( func (s *SealPoller) pollStartPrecommitMsg(ctx context.Context, task pollTask) { if task.TaskPrecommitMsg == nil && !task.AfterPrecommitMsg && task.afterTrees() && s.pollers[pollerPrecommitMsg].IsSet() { s.pollers[pollerPrecommitMsg].Val(ctx)(func(id harmonytask.TaskID, tx *harmonydb.Tx) (shouldCommit bool, seriousError error) { - n, err := tx.Exec(`UPDATE sectors_sdr_pipeline SET task_id_precommit_msg = $1 WHERE sp_id = $2 AND sector_number = $3 and task_id_precommit_msg is null and after_tree_r = true and after_tree_d = true`, id, task.SpID, task.SectorNumber) + n, err := tx.Exec(`UPDATE sectors_sdr_pipeline SET task_id_precommit_msg = $1 WHERE sp_id = $2 AND sector_number = $3 AND task_id_precommit_msg IS NULL AND after_tree_r = TRUE AND after_tree_d = TRUE`, id, task.SpID, task.SectorNumber) if err != nil { return false, xerrors.Errorf("update sectors_sdr_pipeline: %w", err) } @@ -50,7 +50,7 @@ func (s *SealPoller) pollPrecommitMsgLanded(ctx context.Context, task pollTask) err := s.db.Select(ctx, &execResult, `SELECT spipeline.precommit_msg_cid, spipeline.commit_msg_cid, executed_tsk_cid, executed_tsk_epoch, executed_msg_cid, executed_rcpt_exitcode, executed_rcpt_gas_used FROM sectors_sdr_pipeline spipeline JOIN message_waits ON spipeline.precommit_msg_cid = message_waits.signed_message_cid - WHERE sp_id = $1 AND sector_number = $2 AND executed_tsk_epoch is not null`, task.SpID, task.SectorNumber) + WHERE sp_id = $1 AND sector_number = $2 AND executed_tsk_epoch IS NOT NULL`, task.SpID, task.SectorNumber) if err != nil { log.Errorw("failed to query message_waits", "error", err) } @@ -74,8 +74,8 @@ func (s *SealPoller) pollPrecommitMsgLanded(ctx context.Context, task pollTask) randHeight := pci.PreCommitEpoch + policy.GetPreCommitChallengeDelay() _, err := s.db.Exec(ctx, `UPDATE sectors_sdr_pipeline SET - seed_epoch = $1, precommit_msg_tsk = $2, after_precommit_msg_success = true - WHERE sp_id = $3 AND sector_number = $4 and seed_epoch is NULL`, + seed_epoch = $1, precommit_msg_tsk = $2, after_precommit_msg_success = TRUE + WHERE sp_id = $3 AND sector_number = $4 AND seed_epoch IS NULL`, randHeight, execResult[0].ExecutedTskCID, task.SpID, task.SectorNumber) if err != nil { return xerrors.Errorf("update sectors_sdr_pipeline: %w", err) @@ -108,8 +108,8 @@ func (s *SealPoller) pollRetryPrecommitMsgSend(ctx context.Context, task pollTas // make the pipeline entry seem like precommit send didn't happen, next poll loop will retry _, err := s.db.Exec(ctx, `UPDATE sectors_sdr_pipeline SET - precommit_msg_cid = null, task_id_precommit_msg = null - WHERE precommit_msg_cid = $1 AND sp_id = $2 AND sector_number = $3 AND after_precommit_msg_success = false`, + precommit_msg_cid = NULL, task_id_precommit_msg = NULL + WHERE precommit_msg_cid = $1 AND sp_id = $2 AND sector_number = $3 AND after_precommit_msg_success = FALSE`, *execResult.PrecommitMsgCID, task.SpID, task.SectorNumber) if err != nil { return xerrors.Errorf("update sectors_sdr_pipeline to retry precommit msg send: %w", err) diff --git a/provider/lpseal/sector_num_alloc.go b/provider/lpseal/sector_num_alloc.go index 15f6b3435..6958a5e6b 100644 --- a/provider/lpseal/sector_num_alloc.go +++ b/provider/lpseal/sector_num_alloc.go @@ -38,7 +38,7 @@ func AllocateSectorNumbers(ctx context.Context, a AllocAPI, db *harmonydb.DB, ma var dbAllocated bitfield.BitField var rawJson []byte - err = tx.QueryRow("select COALESCE(allocated, '[0]') from sectors_allocated_numbers sa FULL OUTER JOIN (SELECT 1) AS d ON true where sp_id = $1 or sp_id is null", mid).Scan(&rawJson) + err = tx.QueryRow("SELECT COALESCE(allocated, '[0]') from sectors_allocated_numbers sa FULL OUTER JOIN (SELECT 1) AS d ON TRUE WHERE sp_id = $1 OR sp_id IS NULL", mid).Scan(&rawJson) if err != nil { return false, xerrors.Errorf("querying allocated sector numbers: %w", err) } @@ -97,7 +97,7 @@ func AllocateSectorNumbers(ctx context.Context, a AllocAPI, db *harmonydb.DB, ma return false, xerrors.Errorf("marshaling allocated sector numbers: %w", err) } - _, err = tx.Exec("insert into sectors_allocated_numbers(sp_id, allocated) values($1, $2) on conflict(sp_id) do update set allocated = $2", mid, rawJson) + _, err = tx.Exec("INSERT INTO sectors_allocated_numbers(sp_id, allocated) VALUES($1, $2) ON CONFLICT(sp_id) DO UPDATE SET allocated = $2", mid, rawJson) if err != nil { return false, xerrors.Errorf("persisting allocated sector numbers: %w", err) } @@ -114,7 +114,7 @@ func AllocateSectorNumbers(ctx context.Context, a AllocAPI, db *harmonydb.DB, ma } return true, nil - }, harmonydb.RetrySerializationErr()) + }, harmonydb.OptionRetry()) if err != nil { return nil, xerrors.Errorf("allocating sector numbers: %w", err) diff --git a/provider/lpseal/task_finalize.go b/provider/lpseal/task_finalize.go index 92183655a..8d425f76a 100644 --- a/provider/lpseal/task_finalize.go +++ b/provider/lpseal/task_finalize.go @@ -40,7 +40,7 @@ func (f *FinalizeTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (do ctx := context.Background() err = f.db.Select(ctx, &tasks, ` - select sp_id, sector_number, reg_seal_proof from sectors_sdr_pipeline where task_id_finalize=$1`, taskID) + SELECT sp_id, sector_number, reg_seal_proof FROM sectors_sdr_pipeline WHERE task_id_finalize = $1`, taskID) if err != nil { return false, xerrors.Errorf("getting task: %w", err) } @@ -52,7 +52,7 @@ func (f *FinalizeTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (do var keepUnsealed bool - if err := f.db.QueryRow(ctx, `select coalesce(bool_or(not data_delete_on_finalize), false) from sectors_sdr_initial_pieces where sp_id=$1 and sector_number=$2`, task.SpID, task.SectorNumber).Scan(&keepUnsealed); err != nil { + if err := f.db.QueryRow(ctx, `SELECT COALESCE(BOOL_OR(NOT data_delete_on_finalize), FALSE) FROM sectors_sdr_initial_pieces WHERE sp_id = $1 AND sector_number = $2`, task.SpID, task.SectorNumber).Scan(&keepUnsealed); err != nil { return false, err } @@ -98,9 +98,10 @@ func (f *FinalizeTask) CanAccept(ids []harmonytask.TaskID, engine *harmonytask.T } err := f.db.Select(ctx, &tasks, ` - select p.task_id_finalize, p.sp_id, p.sector_number, l.storage_id from sectors_sdr_pipeline p - inner join sector_location l on p.sp_id=l.miner_id and p.sector_number=l.sector_num - where task_id_finalize = ANY ($1) and l.sector_filetype=4`, indIDs) + SELECT p.task_id_finalize, p.sp_id, p.sector_number, l.storage_id FROM sectors_sdr_pipeline p + INNER JOIN sector_location l ON p.sp_id = l.miner_id AND p.sector_number = l.sector_num + WHERE task_id_finalize = ANY ($1) AND l.sector_filetype = 4 +`, indIDs) if err != nil { return nil, xerrors.Errorf("getting tasks: %w", err) } diff --git a/provider/lpseal/task_movestorage.go b/provider/lpseal/task_movestorage.go index aa16d08e9..2085214b7 100644 --- a/provider/lpseal/task_movestorage.go +++ b/provider/lpseal/task_movestorage.go @@ -41,7 +41,7 @@ func (m *MoveStorageTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) ctx := context.Background() err = m.db.Select(ctx, &tasks, ` - select sp_id, sector_number, reg_seal_proof from sectors_sdr_pipeline where task_id_move_storage=$1`, taskID) + SELECT sp_id, sector_number, reg_seal_proof FROM sectors_sdr_pipeline WHERE task_id_move_storage = $1`, taskID) if err != nil { return false, xerrors.Errorf("getting task: %w", err) } @@ -63,7 +63,7 @@ func (m *MoveStorageTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) return false, xerrors.Errorf("moving storage: %w", err) } - _, err = m.db.Exec(ctx, `update sectors_sdr_pipeline set after_move_storage=true where task_id_move_storage=$1`, taskID) + _, err = m.db.Exec(ctx, `UPDATE sectors_sdr_pipeline SET after_move_storage = true WHERE task_id_move_storage = $1`, taskID) if err != nil { return false, xerrors.Errorf("updating task: %w", err) } diff --git a/provider/lpseal/task_porep.go b/provider/lpseal/task_porep.go index f13acab3d..ba0283e0c 100644 --- a/provider/lpseal/task_porep.go +++ b/provider/lpseal/task_porep.go @@ -116,7 +116,7 @@ func (p *PoRepTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (done // store success! n, err := p.db.Exec(ctx, `UPDATE sectors_sdr_pipeline - SET after_porep = true, seed_value = $3, porep_proof = $4 + SET after_porep = TRUE, seed_value = $3, porep_proof = $4 WHERE sp_id = $1 AND sector_number = $2`, sectorParams.SpID, sectorParams.SectorNumber, []byte(rand), proof) if err != nil { diff --git a/provider/lpseal/task_sdr.go b/provider/lpseal/task_sdr.go index d8bf907f8..68dca5bde 100644 --- a/provider/lpseal/task_sdr.go +++ b/provider/lpseal/task_sdr.go @@ -81,7 +81,7 @@ func (s *SDRTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (done bo err = s.db.Select(ctx, &pieces, ` SELECT piece_index, piece_cid, piece_size FROM sectors_sdr_initial_pieces - WHERE sp_id = $1 AND sector_number = $2 ORDER BY piece_index asc`, sectorParams.SpID, sectorParams.SectorNumber) + WHERE sp_id = $1 AND sector_number = $2 ORDER BY piece_index ASC`, sectorParams.SpID, sectorParams.SectorNumber) if err != nil { return false, xerrors.Errorf("getting pieces: %w", err) } diff --git a/provider/lpseal/task_submit_commit.go b/provider/lpseal/task_submit_commit.go index 0c3a9ffe7..5b46b7e00 100644 --- a/provider/lpseal/task_submit_commit.go +++ b/provider/lpseal/task_submit_commit.go @@ -140,7 +140,7 @@ func (s *SubmitCommitTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) return false, xerrors.Errorf("pushing message to mpool: %w", err) } - _, err = s.db.Exec(ctx, `UPDATE sectors_sdr_pipeline SET commit_msg_cid = $1, after_commit_msg = true WHERE sp_id = $2 AND sector_number = $3`, mcid, sectorParams.SpID, sectorParams.SectorNumber) + _, err = s.db.Exec(ctx, `UPDATE sectors_sdr_pipeline SET commit_msg_cid = $1, after_commit_msg = TRUE WHERE sp_id = $2 AND sector_number = $3`, mcid, sectorParams.SpID, sectorParams.SectorNumber) if err != nil { return false, xerrors.Errorf("updating commit_msg_cid: %w", err) } diff --git a/provider/lpseal/task_submit_precommit.go b/provider/lpseal/task_submit_precommit.go index 616336103..9f6233f39 100644 --- a/provider/lpseal/task_submit_precommit.go +++ b/provider/lpseal/task_submit_precommit.go @@ -117,7 +117,7 @@ func (s *SubmitPrecommitTask) Do(taskID harmonytask.TaskID, stillOwned func() bo err = s.db.Select(ctx, &pieces, ` SELECT piece_index, piece_cid, piece_size, f05_deal_id, f05_deal_end_epoch FROM sectors_sdr_initial_pieces - WHERE sp_id = $1 AND sector_number = $2 ORDER BY piece_index asc`, sectorParams.SpID, sectorParams.SectorNumber) + WHERE sp_id = $1 AND sector_number = $2 ORDER BY piece_index ASC`, sectorParams.SpID, sectorParams.SectorNumber) if err != nil { return false, xerrors.Errorf("getting pieces: %w", err) } @@ -175,7 +175,7 @@ func (s *SubmitPrecommitTask) Do(taskID harmonytask.TaskID, stillOwned func() bo // set precommit_msg_cid _, err = s.db.Exec(ctx, `UPDATE sectors_sdr_pipeline - SET precommit_msg_cid = $1, after_precommit_msg = true + SET precommit_msg_cid = $1, after_precommit_msg = TRUE WHERE task_id_precommit_msg = $2`, mcid, taskID) if err != nil { return false, xerrors.Errorf("updating precommit_msg_cid: %w", err) diff --git a/provider/lpseal/task_trees.go b/provider/lpseal/task_trees.go index 3f9b000cd..da0fcf1e9 100644 --- a/provider/lpseal/task_trees.go +++ b/provider/lpseal/task_trees.go @@ -51,7 +51,7 @@ func (t *TreesTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (done err = t.db.Select(ctx, §orParamsArr, ` SELECT sp_id, sector_number, reg_seal_proof FROM sectors_sdr_pipeline - WHERE task_id_tree_r = $1 and task_id_tree_c = $1 and task_id_tree_d = $1`, taskID) + WHERE task_id_tree_r = $1 AND task_id_tree_c = $1 AND task_id_tree_d = $1`, taskID) if err != nil { return false, xerrors.Errorf("getting sector params: %w", err) } @@ -74,7 +74,7 @@ func (t *TreesTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (done err = t.db.Select(ctx, &pieces, ` SELECT piece_index, piece_cid, piece_size, data_url, data_headers, data_raw_size FROM sectors_sdr_initial_pieces - WHERE sp_id = $1 AND sector_number = $2 ORDER BY piece_index asc`, sectorParams.SpID, sectorParams.SectorNumber) + WHERE sp_id = $1 AND sector_number = $2 ORDER BY piece_index ASC`, sectorParams.SpID, sectorParams.SectorNumber) if err != nil { return false, xerrors.Errorf("getting pieces: %w", err) } diff --git a/storage/paths/db_index.go b/storage/paths/db_index.go index 4c935762f..a867da3f9 100644 --- a/storage/paths/db_index.go +++ b/storage/paths/db_index.go @@ -241,7 +241,7 @@ func (dbi *DBIndex) StorageAttach(ctx context.Context, si storiface.StorageInfo, return false, xerrors.Errorf("StorageAttach insert fails: %v", err) } return true, nil - }, harmonydb.RetrySerializationErr()) + }, harmonydb.OptionRetry()) return err } @@ -293,7 +293,7 @@ func (dbi *DBIndex) StorageDetach(ctx context.Context, id storiface.ID, url stri return false, err } return true, nil - }, harmonydb.RetrySerializationErr()) + }, harmonydb.OptionRetry()) if err != nil { return err } @@ -407,7 +407,7 @@ func (dbi *DBIndex) StorageDeclareSector(ctx context.Context, storageID storifac } return true, nil - }, harmonydb.RetrySerializationErr()) + }, harmonydb.OptionRetry()) return err } @@ -846,7 +846,7 @@ func (dbi *DBIndex) lock(ctx context.Context, sector abi.SectorID, read storifac } return true, nil - }, harmonydb.RetrySerializationErr()) + }, harmonydb.OptionRetry()) if err != nil { return false, err } From 2eaf34bc9285a8426f1418c497bf17f12a6e2186 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 21 Feb 2024 16:51:29 +0100 Subject: [PATCH 208/394] provider: Expand provider subsystems config docs --- node/config/types.go | 85 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 73 insertions(+), 12 deletions(-) diff --git a/node/config/types.go b/node/config/types.go index e3766e797..6c2812086 100644 --- a/node/config/types.go +++ b/node/config/types.go @@ -93,47 +93,108 @@ type JournalConfig struct { } type ProviderSubsystemsConfig struct { - EnableWindowPost bool - WindowPostMaxTasks int + // EnableWindowPost enables window post to be executed on this lotus-provider instance. Each machine in the cluster + // with WindowPoSt enabled will also participate in the window post scheduler. It is possible to have multiple + // machines with WindowPoSt enabled which will provide redundancy, and in case of multiple partitions per deadline, + // will allow for parallel processing of partitions. + // + // It is possible to have instances handling both WindowPoSt and WinningPoSt, which can provide redundancy without + // the need for additional machines. In setups like this it is generally recommended to run + // partitionsPerDeadline+1 machines. + EnableWindowPost bool + WindowPostMaxTasks int + + // EnableWinningPost enables winning post to be executed on this lotus-provider instance. + // Each machine in the cluster with WinningPoSt enabled will also participate in the winning post scheduler. + // It is possible to mix machines with WindowPoSt and WinningPoSt enabled, for details see the EnableWindowPost + // documentation. EnableWinningPost bool WinningPostMaxTasks int // EnableSealSDR enables SDR tasks to run. SDR is the long sequential computation - // creating layers. + // creating 11 layer files in sector cache directory. + // + // SDR is the first task in the sealing pipeline. It's inputs are just the hash of the + // unsealed data (CommD), sector number, miner id, and the seal proof type. + // It's outputs are the 11 layer files in the sector cache directory. + // // In lotus-miner this was run as part of PreCommit1. - EnableSealSDR bool + EnableSealSDR bool + + // The maximum amount of SDR tasks that can run simultaneously. Note that the maximum number of tasks will + // also be bounded by resources available on the machine. SealSDRMaxTasks int // EnableSealSDRTrees enables the SDR pipeline tree-building task to run. // This task handles encoding of unsealed data into last sdr layer and building // of TreeR, TreeC and TreeD. + // + // This task runs after SDR + // TreeD is first computed with optional input of unsealed data + // TreeR is computed from replica, which is first computed as field + // addition of the last SDR layer and the bottom layer of TreeD (which is the unsealed data) + // TreeC is computed from the 11 SDR layers + // The 3 trees will later be used to compute the PoRep proof. + // + // In case of SyntheticPoRep challenges for PoRep will be pre-generated at this step, and trees and layers + // will be dropped. SyntheticPoRep works by pre-generating a very large set of challenges (~30GiB on disk) + // then using a small subset of them for the actual PoRep computation. This allows for significant scratch space + // saving between PreCommit and PoRep generation at the expense of more computation (generating challenges in this step) + // // In lotus-miner this was run as part of PreCommit2 (TreeD was run in PreCommit1). // Note that nodes with SDRTrees enabled will also answer to Finalize tasks, // which just remove unneeded tree data after PoRep is computed. - EnableSealSDRTrees bool + EnableSealSDRTrees bool + + // The maximum amount of SealSDRTrees tasks that can run simultaneously. Note that the maximum number of tasks will + // also be bounded by resources available on the machine. SealSDRTreesMaxTasks int - FinalizeMaxTasks int + + // FinalizeMaxTasks is the maximum amount of finalize tasks that can run simultaneously. + // The finalize task is enabled on all machines which also handle SDRTrees tasks. Finalize ALWAYS runs on whichever + // machine holds sector cache files, as it removes unneeded tree data after PoRep is computed. + // Finalize will run in parallel with the SubmitCommitMsg task. + FinalizeMaxTasks int // EnableSendPrecommitMsg enables the sending of precommit messages to the chain // from this lotus-provider instance. + // This runs after SDRTrees and uses the output CommD / CommR (roots of TreeD / TreeR) for the message EnableSendPrecommitMsg bool - // EnableSendCommitMsg enables the computation of the porep proof + // EnablePoRepProof enables the computation of the porep proof + // + // This task runs after interactive-porep seed becomes available, which happens 150 epochs (75min) after the + // precommit message lands on chain. This task should run on a machine with a GPU. Vanilla PoRep proofs are + // requested from the machine which holds sector cache files which most likely is the machine which ran the SDRTrees + // task. + // // In lotus-miner this was Commit1 / Commit2 - EnablePoRepProof bool + EnablePoRepProof bool + + // The maximum amount of PoRepProof tasks that can run simultaneously. Note that the maximum number of tasks will + // also be bounded by resources available on the machine. PoRepProofMaxTasks int // EnableSendCommitMsg enables the sending of commit messages to the chain // from this lotus-provider instance. EnableSendCommitMsg bool - // EnableMoveStorage enables the move-into-long-term-storage task to run - // on this lotus-provider instance. This tasks should only be enabled on - // nodes with long-term storage. - EnableMoveStorage bool + // EnableMoveStorage enables the move-into-long-term-storage task to run on this lotus-provider instance. + // This tasks should only be enabled on nodes with long-term storage. + // + // The MoveStorage task is the last task in the sealing pipeline. It moves the sealed sector data from the + // SDRTrees machine into long-term storage. This task runs after the Finalize task. + EnableMoveStorage bool + + // The maximum amount of MoveStorage tasks that can run simultaneously. Note that the maximum number of tasks will + // also be bounded by resources available on the machine. It is recommended that this value is set to a number which + // uses all available network (or disk) bandwidth on the machine without causing bottlenecks. MoveStorageMaxTasks int + // EnableWebGui enables the web GUI on this lotus-provider instance. The UI has minimal local overhead, but it should + // only need to be run on a single machine in the cluster. EnableWebGui bool + // The address that should listen for Web GUI requests. GuiAddress string } From a586982c2ae839fb9e4a085cc4191d87e3b5a39f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 21 Feb 2024 17:00:30 +0100 Subject: [PATCH 209/394] update docsgen --- documentation/en/cli-lotus-provider.md | 92 +++++++++---------- .../en/default-lotus-provider-config.toml | 74 ++++++++++++++- node/config/doc_gen.go | 74 ++++++++++++--- 3 files changed, 173 insertions(+), 67 deletions(-) diff --git a/documentation/en/cli-lotus-provider.md b/documentation/en/cli-lotus-provider.md index 5fb6f7d76..e424bce92 100644 --- a/documentation/en/cli-lotus-provider.md +++ b/documentation/en/cli-lotus-provider.md @@ -10,15 +10,15 @@ VERSION: 1.25.3-dev COMMANDS: - cli Execute cli commands - run Start a lotus provider process - stop Stop a running lotus provider - config Manage node config by layers. The layer 'base' will always be applied. - test Utility functions for testing - web Start lotus provider web interface - pipeline Manage the sealing pipeline - version Print version - help, h Shows a list of commands or help for one command + cli Execute cli commands + run Start a lotus provider process + stop Stop a running lotus provider + config Manage node config by layers. The layer 'base' will always be applied. + test Utility functions for testing + web Start lotus provider web interface + seal Manage the sealing pipeline + version Print version + help, h Shows a list of commands or help for one command DEVELOPER: auth Manage RPC permissions log Manage logging @@ -26,16 +26,16 @@ COMMANDS: fetch-params Fetch proving parameters GLOBAL OPTIONS: - --color use color in display output (default: depends on output being a TTY) - --db-host value Command separated list of hostnames for yugabyte cluster (default: "yugabyte") [$LOTUS_DB_HOST] - --db-name value (default: "yugabyte") [$LOTUS_DB_NAME, $LOTUS_HARMONYDB_HOSTS] - --db-user value (default: "yugabyte") [$LOTUS_DB_USER, $LOTUS_HARMONYDB_USERNAME] - --db-password value (default: "yugabyte") [$LOTUS_DB_PASSWORD, $LOTUS_HARMONYDB_PASSWORD] - --layers value (default: "base") [$LOTUS_LAYERS, $LOTUS_CONFIG_LAYERS] - --repo-path value (default: "~/.lotusprovider") [$LOTUS_REPO_PATH] - --vv enables very verbose mode, useful for debugging the CLI (default: false) - --help, -h show help - --version, -v print the version + --color use color in display output (default: depends on output being a TTY) + --db-host value Command separated list of hostnames for yugabyte cluster (default: "yugabyte") [$LOTUS_DB_HOST] + --db-name value (default: "yugabyte") [$LOTUS_DB_NAME, $LOTUS_HARMONYDB_HOSTS] + --db-user value (default: "yugabyte") [$LOTUS_DB_USER, $LOTUS_HARMONYDB_USERNAME] + --db-password value (default: "yugabyte") [$LOTUS_DB_PASSWORD, $LOTUS_HARMONYDB_PASSWORD] + --layers value [ --layers value ] list of layers to be interpreted (atop defaults). Default: base (default: "base") [$CURIO_LAYERS] + --repo-path value (default: "~/.lotusprovider") [$LOTUS_REPO_PATH] + --vv enables very verbose mode, useful for debugging the CLI (default: false) + --help, -h show help + --version, -v print the version ``` ## lotus-provider cli @@ -51,7 +51,7 @@ COMMANDS: help, h Shows a list of commands or help for one command OPTIONS: - --machine value machine host:port + --machine value machine host:port (lotus-provider run --listen address) --help, -h show help ``` @@ -68,13 +68,12 @@ USAGE: lotus-provider run [command options] [arguments...] OPTIONS: - --listen value host address and port the worker api will listen on (default: "0.0.0.0:12300") [$LOTUS_WORKER_LISTEN] - --nosync don't check full-node sync status (default: false) - --manage-fdlimit manage open file limit (default: true) - --layers value [ --layers value ] list of layers to be interpreted (atop defaults). Default: base (default: "base") - --storage-json value path to json file containing storage config (default: "~/.lotus-provider/storage.json") - --journal value path to journal files (default: "~/.lotus-provider/") - --help, -h show help + --listen value host address and port the worker api will listen on (default: "0.0.0.0:12300") [$LOTUS_WORKER_LISTEN] + --nosync don't check full-node sync status (default: false) + --manage-fdlimit manage open file limit (default: true) + --storage-json value path to json file containing storage config (default: "~/.lotus-provider/storage.json") + --journal value path to journal files (default: "~/.lotus-provider/") + --help, -h show help ``` ## lotus-provider stop @@ -217,7 +216,7 @@ DESCRIPTION: Express a database config (for lotus-provider) from an existing miner. OPTIONS: - --miner-repo value, --storagerepo value Specify miner repo path. flag(storagerepo) and env(LOTUS_STORAGE_PATH) are DEPRECATION, will REMOVE SOON (default: "~/.lotusminer") [$LOTUS_MINER_PATH, $LOTUS_STORAGE_PATH] + --miner-repo value, --storagerepo value Miner repo path (default: "~/.lotusminer") [$LOTUS_MINER_PATH, $LOTUS_STORAGE_PATH] --to-layer value, -t value The layer name for this data push. 'base' is recommended for single-miner setup. --overwrite, -o Use this with --to-layer to replace an existing layer (default: false) --help, -h show help @@ -297,9 +296,8 @@ USAGE: lotus-provider test window-post task [command options] [arguments...] OPTIONS: - --deadline value deadline to compute WindowPoSt for (default: 0) - --layers value [ --layers value ] list of layers to be interpreted (atop defaults). Default: base (default: "base") - --help, -h show help + --deadline value deadline to compute WindowPoSt for (default: 0) + --help, -h show help ``` ## lotus-provider web @@ -315,19 +313,18 @@ DESCRIPTION: This creates the 'web' layer if it does not exist, then calls run with that layer. OPTIONS: - --listen value Address to listen on (default: "127.0.0.1:4701") - --layers value [ --layers value ] list of layers to be interpreted (atop defaults). Default: base. Web will be added (default: "base") - --nosync don't check full-node sync status (default: false) - --help, -h show help + --listen value Address to listen on (default: "127.0.0.1:4701") + --nosync don't check full-node sync status (default: false) + --help, -h show help ``` -## lotus-provider pipeline +## lotus-provider seal ``` NAME: - lotus-provider pipeline - Manage the sealing pipeline + lotus-provider seal - Manage the sealing pipeline USAGE: - lotus-provider pipeline command [command options] [arguments...] + lotus-provider seal command [command options] [arguments...] COMMANDS: start Start new sealing operations manually @@ -337,22 +334,21 @@ OPTIONS: --help, -h show help ``` -### lotus-provider pipeline start +### lotus-provider seal start ``` NAME: - lotus-provider pipeline start - Start new sealing operations manually + lotus-provider seal start - Start new sealing operations manually USAGE: - lotus-provider pipeline start [command options] [arguments...] + lotus-provider seal start [command options] [arguments...] OPTIONS: - --actor value Specify actor address to start sealing sectors for - --now Start sealing sectors for all actors now (not on schedule) (default: false) - --cc Start sealing new CC sectors (default: false) - --count value Number of sectors to start (default: 1) - --synthetic Use synthetic PoRep (default: false) - --layers value [ --layers value ] list of layers to be interpreted (atop defaults). Default: base (default: "base") - --help, -h show help + --actor value Specify actor address to start sealing sectors for + --now Start sealing sectors for all actors now (not on schedule) (default: false) + --cc Start sealing new CC sectors (default: false) + --count value Number of sectors to start (default: 1) + --synthetic Use synthetic PoRep (default: false) + --help, -h show help ``` ## lotus-provider version diff --git a/documentation/en/default-lotus-provider-config.toml b/documentation/en/default-lotus-provider-config.toml index 4c398bb2d..dd9921769 100644 --- a/documentation/en/default-lotus-provider-config.toml +++ b/documentation/en/default-lotus-provider-config.toml @@ -1,10 +1,24 @@ [Subsystems] + # EnableWindowPost enables window post to be executed on this lotus-provider instance. Each machine in the cluster + # with WindowPoSt enabled will also participate in the window post scheduler. It is possible to have multiple + # machines with WindowPoSt enabled which will provide redundancy, and in case of multiple partitions per deadline, + # will allow for parallel processing of partitions. + # + # It is possible to have instances handling both WindowPoSt and WinningPoSt, which can provide redundancy without + # the need for additional machines. In setups like this it is generally recommended to run + # partitionsPerDeadline+1 machines. + # # type: bool #EnableWindowPost = false # type: int #WindowPostMaxTasks = 0 + # EnableWinningPost enables winning post to be executed on this lotus-provider instance. + # Each machine in the cluster with WinningPoSt enabled will also participate in the winning post scheduler. + # It is possible to mix machines with WindowPoSt and WinningPoSt enabled, for details see the EnableWindowPost + # documentation. + # # type: bool #EnableWinningPost = false @@ -12,18 +26,39 @@ #WinningPostMaxTasks = 0 # EnableSealSDR enables SDR tasks to run. SDR is the long sequential computation - # creating layers. + # creating 11 layer files in sector cache directory. + # + # SDR is the first task in the sealing pipeline. It's inputs are just the hash of the + # unsealed data (CommD), sector number, miner id, and the seal proof type. + # It's outputs are the 11 layer files in the sector cache directory. + # # In lotus-miner this was run as part of PreCommit1. # # type: bool #EnableSealSDR = false + # The maximum amount of SDR tasks that can run simultaneously. Note that the maximum number of tasks will + # also be bounded by resources available on the machine. + # # type: int #SealSDRMaxTasks = 0 # EnableSealSDRTrees enables the SDR pipeline tree-building task to run. # This task handles encoding of unsealed data into last sdr layer and building # of TreeR, TreeC and TreeD. + # + # This task runs after SDR + # TreeD is first computed with optional input of unsealed data + # TreeR is computed from replica, which is first computed as field + # addition of the last SDR layer and the bottom layer of TreeD (which is the unsealed data) + # TreeC is computed from the 11 SDR layers + # The 3 trees will later be used to compute the PoRep proof. + # + # In case of SyntheticPoRep challenges for PoRep will be pre-generated at this step, and trees and layers + # will be dropped. SyntheticPoRep works by pre-generating a very large set of challenges (~30GiB on disk) + # then using a small subset of them for the actual PoRep computation. This allows for significant scratch space + # saving between PreCommit and PoRep generation at the expense of more computation (generating challenges in this step) + # # In lotus-miner this was run as part of PreCommit2 (TreeD was run in PreCommit1). # Note that nodes with SDRTrees enabled will also answer to Finalize tasks, # which just remove unneeded tree data after PoRep is computed. @@ -31,24 +66,42 @@ # type: bool #EnableSealSDRTrees = false + # The maximum amount of SealSDRTrees tasks that can run simultaneously. Note that the maximum number of tasks will + # also be bounded by resources available on the machine. + # # type: int #SealSDRTreesMaxTasks = 0 + # FinalizeMaxTasks is the maximum amount of finalize tasks that can run simultaneously. + # The finalize task is enabled on all machines which also handle SDRTrees tasks. Finalize ALWAYS runs on whichever + # machine holds sector cache files, as it removes unneeded tree data after PoRep is computed. + # Finalize will run in parallel with the SubmitCommitMsg task. + # # type: int #FinalizeMaxTasks = 0 # EnableSendPrecommitMsg enables the sending of precommit messages to the chain # from this lotus-provider instance. + # This runs after SDRTrees and uses the output CommD / CommR (roots of TreeD / TreeR) for the message # # type: bool #EnableSendPrecommitMsg = false - # EnableSendCommitMsg enables the computation of the porep proof + # EnablePoRepProof enables the computation of the porep proof + # + # This task runs after interactive-porep seed becomes available, which happens 150 epochs (75min) after the + # precommit message lands on chain. This task should run on a machine with a GPU. Vanilla PoRep proofs are + # requested from the machine which holds sector cache files which most likely is the machine which ran the SDRTrees + # task. + # # In lotus-miner this was Commit1 / Commit2 # # type: bool #EnablePoRepProof = false + # The maximum amount of PoRepProof tasks that can run simultaneously. Note that the maximum number of tasks will + # also be bounded by resources available on the machine. + # # type: int #PoRepProofMaxTasks = 0 @@ -58,16 +111,25 @@ # type: bool #EnableSendCommitMsg = false - # EnableMoveStorage enables the move-into-long-term-storage task to run - # on this lotus-provider instance. This tasks should only be enabled on - # nodes with long-term storage. + # EnableMoveStorage enables the move-into-long-term-storage task to run on this lotus-provider instance. + # This tasks should only be enabled on nodes with long-term storage. + # + # The MoveStorage task is the last task in the sealing pipeline. It moves the sealed sector data from the + # SDRTrees machine into long-term storage. This task runs after the Finalize task. # # type: bool #EnableMoveStorage = false + # The maximum amount of MoveStorage tasks that can run simultaneously. Note that the maximum number of tasks will + # also be bounded by resources available on the machine. It is recommended that this value is set to a number which + # uses all available network (or disk) bandwidth on the machine without causing bottlenecks. + # # type: int #MoveStorageMaxTasks = 0 + # EnableWebGui enables the web GUI on this lotus-provider instance. The UI has minimal local overhead, but it should + # only need to be run on a single machine in the cluster. + # # type: bool #EnableWebGui = false @@ -124,6 +186,8 @@ #DisableWorkerFallback = false + #MinerAddresses = [] + [Proving] # Maximum number of sector checks to run in parallel. (0 = unlimited) diff --git a/node/config/doc_gen.go b/node/config/doc_gen.go index bf8725ae6..6cb93a50e 100644 --- a/node/config/doc_gen.go +++ b/node/config/doc_gen.go @@ -987,7 +987,14 @@ block rewards will be missed!`, Name: "EnableWindowPost", Type: "bool", - Comment: ``, + Comment: `EnableWindowPost enables window post to be executed on this lotus-provider instance. Each machine in the cluster +with WindowPoSt enabled will also participate in the window post scheduler. It is possible to have multiple +machines with WindowPoSt enabled which will provide redundancy, and in case of multiple partitions per deadline, +will allow for parallel processing of partitions. + +It is possible to have instances handling both WindowPoSt and WinningPoSt, which can provide redundancy without +the need for additional machines. In setups like this it is generally recommended to run +partitionsPerDeadline+1 machines.`, }, { Name: "WindowPostMaxTasks", @@ -999,7 +1006,10 @@ block rewards will be missed!`, Name: "EnableWinningPost", Type: "bool", - Comment: ``, + Comment: `EnableWinningPost enables winning post to be executed on this lotus-provider instance. +Each machine in the cluster with WinningPoSt enabled will also participate in the winning post scheduler. +It is possible to mix machines with WindowPoSt and WinningPoSt enabled, for details see the EnableWindowPost +documentation.`, }, { Name: "WinningPostMaxTasks", @@ -1012,14 +1022,20 @@ block rewards will be missed!`, Type: "bool", Comment: `EnableSealSDR enables SDR tasks to run. SDR is the long sequential computation -creating layers. +creating 11 layer files in sector cache directory. + +SDR is the first task in the sealing pipeline. It's inputs are just the hash of the +unsealed data (CommD), sector number, miner id, and the seal proof type. +It's outputs are the 11 layer files in the sector cache directory. + In lotus-miner this was run as part of PreCommit1.`, }, { Name: "SealSDRMaxTasks", Type: "int", - Comment: ``, + Comment: `The maximum amount of SDR tasks that can run simultaneously. Note that the maximum number of tasks will +also be bounded by resources available on the machine.`, }, { Name: "EnableSealSDRTrees", @@ -1028,6 +1044,19 @@ In lotus-miner this was run as part of PreCommit1.`, Comment: `EnableSealSDRTrees enables the SDR pipeline tree-building task to run. This task handles encoding of unsealed data into last sdr layer and building of TreeR, TreeC and TreeD. + +This task runs after SDR +TreeD is first computed with optional input of unsealed data +TreeR is computed from replica, which is first computed as field +addition of the last SDR layer and the bottom layer of TreeD (which is the unsealed data) +TreeC is computed from the 11 SDR layers +The 3 trees will later be used to compute the PoRep proof. + +In case of SyntheticPoRep challenges for PoRep will be pre-generated at this step, and trees and layers +will be dropped. SyntheticPoRep works by pre-generating a very large set of challenges (~30GiB on disk) +then using a small subset of them for the actual PoRep computation. This allows for significant scratch space +saving between PreCommit and PoRep generation at the expense of more computation (generating challenges in this step) + In lotus-miner this was run as part of PreCommit2 (TreeD was run in PreCommit1). Note that nodes with SDRTrees enabled will also answer to Finalize tasks, which just remove unneeded tree data after PoRep is computed.`, @@ -1036,33 +1065,45 @@ which just remove unneeded tree data after PoRep is computed.`, Name: "SealSDRTreesMaxTasks", Type: "int", - Comment: ``, + Comment: `The maximum amount of SealSDRTrees tasks that can run simultaneously. Note that the maximum number of tasks will +also be bounded by resources available on the machine.`, }, { Name: "FinalizeMaxTasks", Type: "int", - Comment: ``, + Comment: `FinalizeMaxTasks is the maximum amount of finalize tasks that can run simultaneously. +The finalize task is enabled on all machines which also handle SDRTrees tasks. Finalize ALWAYS runs on whichever +machine holds sector cache files, as it removes unneeded tree data after PoRep is computed. +Finalize will run in parallel with the SubmitCommitMsg task.`, }, { Name: "EnableSendPrecommitMsg", Type: "bool", Comment: `EnableSendPrecommitMsg enables the sending of precommit messages to the chain -from this lotus-provider instance.`, +from this lotus-provider instance. +This runs after SDRTrees and uses the output CommD / CommR (roots of TreeD / TreeR) for the message`, }, { Name: "EnablePoRepProof", Type: "bool", - Comment: `EnableSendCommitMsg enables the computation of the porep proof + Comment: `EnablePoRepProof enables the computation of the porep proof + +This task runs after interactive-porep seed becomes available, which happens 150 epochs (75min) after the +precommit message lands on chain. This task should run on a machine with a GPU. Vanilla PoRep proofs are +requested from the machine which holds sector cache files which most likely is the machine which ran the SDRTrees +task. + In lotus-miner this was Commit1 / Commit2`, }, { Name: "PoRepProofMaxTasks", Type: "int", - Comment: ``, + Comment: `The maximum amount of PoRepProof tasks that can run simultaneously. Note that the maximum number of tasks will +also be bounded by resources available on the machine.`, }, { Name: "EnableSendCommitMsg", @@ -1075,21 +1116,26 @@ from this lotus-provider instance.`, Name: "EnableMoveStorage", Type: "bool", - Comment: `EnableMoveStorage enables the move-into-long-term-storage task to run -on this lotus-provider instance. This tasks should only be enabled on -nodes with long-term storage.`, + Comment: `EnableMoveStorage enables the move-into-long-term-storage task to run on this lotus-provider instance. +This tasks should only be enabled on nodes with long-term storage. + +The MoveStorage task is the last task in the sealing pipeline. It moves the sealed sector data from the +SDRTrees machine into long-term storage. This task runs after the Finalize task.`, }, { Name: "MoveStorageMaxTasks", Type: "int", - Comment: ``, + Comment: `The maximum amount of MoveStorage tasks that can run simultaneously. Note that the maximum number of tasks will +also be bounded by resources available on the machine. It is recommended that this value is set to a number which +uses all available network (or disk) bandwidth on the machine without causing bottlenecks.`, }, { Name: "EnableWebGui", Type: "bool", - Comment: ``, + Comment: `EnableWebGui enables the web GUI on this lotus-provider instance. The UI has minimal local overhead, but it should +only need to be run on a single machine in the cluster.`, }, { Name: "GuiAddress", From edb2b9584b1929e806825baef0df85536e84e4ec Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 21 Feb 2024 10:32:22 -0800 Subject: [PATCH 210/394] fix: stmgr: remove watermelon migration hack (#11623) We did this to avoid using cached migrations in the borked watermelon upgrade on calibrationnet, but we don't need to keep this in the code (even for users syncing from scratch). --- chain/stmgr/forks.go | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/chain/stmgr/forks.go b/chain/stmgr/forks.go index 9dd66ee8b..2f18bde82 100644 --- a/chain/stmgr/forks.go +++ b/chain/stmgr/forks.go @@ -178,18 +178,16 @@ func (sm *StateManager) HandleStateForks(ctx context.Context, root cid.Cid, heig retCid := root u := sm.stateMigrations[height] if u != nil && u.upgrade != nil { - if height != build.UpgradeWatermelonFixHeight { - migCid, ok, err := u.migrationResultCache.Get(ctx, root) - if err == nil { - if ok { - log.Infow("CACHED migration", "height", height, "from", root, "to", migCid) - return migCid, nil - } - } else if !errors.Is(err, datastore.ErrNotFound) { - log.Errorw("failed to lookup previous migration result", "err", err) - } else { - log.Debug("no cached migration found, migrating from scratch") + migCid, ok, err := u.migrationResultCache.Get(ctx, root) + if err == nil { + if ok { + log.Infow("CACHED migration", "height", height, "from", root, "to", migCid) + return migCid, nil } + } else if !errors.Is(err, datastore.ErrNotFound) { + log.Errorw("failed to lookup previous migration result", "err", err) + } else { + log.Debug("no cached migration found, migrating from scratch") } startTime := time.Now() @@ -197,7 +195,6 @@ func (sm *StateManager) HandleStateForks(ctx context.Context, root cid.Cid, heig // Yes, we clone the cache, even for the final upgrade epoch. Why? Reverts. We may // have to migrate multiple times. tmpCache := u.cache.Clone() - var err error retCid, err = u.upgrade(ctx, sm, tmpCache, cb, root, height, ts) if err != nil { log.Errorw("FAILED migration", "height", height, "from", root, "error", err) From 01cfe8fc722e5ce5514a82ff83e8e9f338c18ac6 Mon Sep 17 00:00:00 2001 From: Samuel Arogbonlo <47984109+samuelarogbonlo@users.noreply.github.com> Date: Wed, 21 Feb 2024 21:06:25 +0100 Subject: [PATCH 211/394] new: add forest bootstrap nodes (#11636) Signed-off-by: samuelarogbonlo --- build/bootstrap/calibnet.pi | 3 +++ build/bootstrap/mainnet.pi | 3 +++ 2 files changed, 6 insertions(+) diff --git a/build/bootstrap/calibnet.pi b/build/bootstrap/calibnet.pi index 4586d93b1..e41ae94c6 100644 --- a/build/bootstrap/calibnet.pi +++ b/build/bootstrap/calibnet.pi @@ -3,3 +3,6 @@ /dns4/bootstrap-2.calibration.fildev.network/tcp/1347/p2p/12D3KooWNRxTHUn8bf7jz1KEUPMc2dMgGfa4f8ZJTsquVSn3vHCG /dns4/bootstrap-3.calibration.fildev.network/tcp/1347/p2p/12D3KooWFWUqE9jgXvcKHWieYs9nhyp6NF4ftwLGAHm4sCv73jjK /dns4/calibration.node.glif.io/tcp/1237/p2p/12D3KooWQPYouEAsUQKzvFUA9sQ8tz4rfpqtTzh2eL6USd9bwg7x +/dns4/bootstrap-calibnet-0.chainsafe-fil.io/tcp/34000/p2p/12D3KooWABQ5gTDHPWyvhJM7jPhtNwNJruzTEo32Lo4gcS5ABAMm +/dns4/bootstrap-calibnet-1.chainsafe-fil.io/tcp/34000/p2p/12D3KooWS3ZRhMYL67b4bD5XQ6fcpTyVQXnDe8H89LvwrDqaSbiT +/dns4/bootstrap-calibnet-2.chainsafe-fil.io/tcp/34000/p2p/12D3KooWEiBN8jBX8EBoM3M47pVRLRWV812gDRUJhMxgyVkUoR48 diff --git a/build/bootstrap/mainnet.pi b/build/bootstrap/mainnet.pi index 7838158de..5fa3f58d2 100644 --- a/build/bootstrap/mainnet.pi +++ b/build/bootstrap/mainnet.pi @@ -10,3 +10,6 @@ /dns4/node.glif.io/tcp/1235/p2p/12D3KooWBF8cpp65hp2u9LK5mh19x67ftAam84z9LsfaquTDSBpt /dns4/bootstarp-0.1475.io/tcp/61256/p2p/12D3KooWRzCVDwHUkgdK7eRgnoXbjDAELhxPErjHzbRLguSV1aRt /dns4/bootstrap-venus.mainnet.filincubator.com/tcp/8888/p2p/QmQu8C6deXwKvJP2D8B6QGyhngc3ZiDnFzEHBDx8yeBXST +/dns4/bootstrap-mainnet-0.chainsafe-fil.io/tcp/34000/p2p/12D3KooWKKkCZbcigsWTEu1cgNetNbZJqeNtysRtFpq7DTqw3eqH +/dns4/bootstrap-mainnet-1.chainsafe-fil.io/tcp/34000/p2p/12D3KooWGnkd9GQKo3apkShQDaq1d6cKJJmsVe6KiQkacUk1T8oZ +/dns4/bootstrap-mainnet-2.chainsafe-fil.io/tcp/34000/p2p/12D3KooWHQRSDFv4FvAjtU32shQ7znz7oRbLBryXzZ9NMK2feyyH From 8ba491b6b475f34caa141b5c6edebd508be11a8a Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 21 Feb 2024 12:20:00 -0800 Subject: [PATCH 212/394] feat: api: improve the correctness of Eth's trace_block (#11609) * Improve the correctness of Eth's trace_block - Improve encoding/decoding of parameters and return values: - Encode "native" parameters and return values with Solidity ABI. - Correctly decode parameters to "create" calls. - Use the correct (ish) output for "create" calls. - Handle all forms of "create". - Make robust with respect to reverts: - Use the actor ID/address from the trace instead of looking it up in the state-tree (may not exist in the state-tree due to a revert). - Gracefully handle failed actor/contract creation. - Improve performance: - We avoid looking anything up in the state-tree when translating the trace, which should significantly improve performance. - Improve code readability: - Remove all "backtracking" logic. - Use an "environment" struct to store temporary state instead of attaching it to the trace. - Fix random bugs: - Fix an allocation bug in the "address" logic (need to set the capacity before modifying the slice). - Improved error checking/handling. - Use correct types for `trace_block` action/results (create, call, etc.). - And use the correct types for Result/Action structs instead of reusing the same "Call" action every time. - Improve error messages. --- CHANGELOG.md | 17 + api/api_full.go | 19 +- build/openrpc/full.json | 172 +---- build/openrpc/full.json.gz | Bin 0 -> 34654 bytes build/openrpc/gateway.json | 170 +---- build/openrpc/gateway.json.gz | Bin 0 -> 11862 bytes chain/actors/builtin/evm/actor.go.template | 14 + chain/actors/builtin/evm/evm.go | 13 + chain/types/ethtypes/eth_types.go | 58 +- documentation/en/api-v1-unstable-methods.md | 52 +- node/impl/full/eth.go | 61 +- node/impl/full/eth_test.go | 40 + node/impl/full/eth_trace.go | 761 ++++++++++++++------ node/impl/full/eth_utils.go | 33 +- 14 files changed, 809 insertions(+), 601 deletions(-) create mode 100644 build/openrpc/full.json.gz create mode 100644 build/openrpc/gateway.json.gz diff --git a/CHANGELOG.md b/CHANGELOG.md index c18f2f6a4..21943bc6a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -50,6 +50,23 @@ Replace the `CodeCid` field in the message trace (added in 1.23.4) with an `Invo This means the trace now contains an accurate "snapshot" of the actor at the time of the call, information that may not be present in the final state-tree (e.g., due to reverts). This will hopefully improve the performance and accuracy of indexing services. +### Ethereum Tracing API (`trace_block` and `trace_replayBlockTransactions`) + +For those with the Ethereum JSON-RPC API enabled, the experimental Ethereum Tracing API has been improved significantly and should be considered "functional". However, it's still new and should be tested extensively before relying on it. This API translates FVM traces to Ethereum-style traces, implementing the OpenEthereum `trace_block` and `trace_replayBlockTransactions` APIs. + +This release fixes numerous bugs with this API and now ABI-encodes non-EVM inputs/outputs as if they were explicit EVM calls to [`handle_filecoin_method`][handlefilecoinmethod] for better block explorer compatibility. + +However, there are some _significant_ limitations: + +1. The Geth APIs are not implemented, only the OpenEthereum (Erigon, etc.) APIs. +2. Block rewards are not (yet) included in the trace. +3. Selfdestruct operations are not included in the trace. +4. EVM smart contract "create" events always specify `0xfe` as the "code" for newly created EVM smart contracts. + +Additionally, Filecoin is not Ethereum no matter how much we try to provide API/tooling compatibility. This API attempts to translate Filecoin semantics into Ethereum semantics as accurately as possible, but it's hardly the best source of data unless you _need_ Filecoin to look like an Ethereum compatible chain. If you're trying to build a new integration with Filecoin, please use the native `StateCompute` method instead. + +[handlefilecoinmethod]: https://fips.filecoin.io/FIPS/fip-0054.html#handlefilecoinmethod-general-handler-for-method-numbers--1024 + # v1.25.2 / 2024-01-11 This is an optional but **highly recommended feature release** of Lotus, as it includes fixes for synchronizations issues that users have experienced. The feature release also introduces `Lotus-Provider` in its alpha testing phase, as well as the ability to call external PC2-binaries during the sealing process. diff --git a/api/api_full.go b/api/api_full.go index 3dc7f8bb2..a9adb1c4b 100644 --- a/api/api_full.go +++ b/api/api_full.go @@ -868,9 +868,26 @@ type FullNode interface { Web3ClientVersion(ctx context.Context) (string, error) //perm:read // TraceAPI related methods + + // Returns an OpenEthereum-compatible trace of the given block (implementing `trace_block`), + // translating Filecoin semantics into Ethereum semantics and tracing both EVM and FVM calls. // - // Returns traces created at given block + // Features: + // + // - FVM actor create events, calls, etc. show up as if they were EVM smart contract events. + // - Native FVM call inputs are ABI-encoded (Solidity ABI) as if they were calls to a + // `handle_filecoin_method(uint64 method, uint64 codec, bytes params)` function + // (where `codec` is the IPLD codec of `params`). + // - Native FVM call outputs (return values) are ABI-encoded as `(uint32 exit_code, uint64 + // codec, bytes output)` where `codec` is the IPLD codec of `output`. + // + // Limitations (for now): + // + // 1. Block rewards are not included in the trace. + // 2. SELFDESTRUCT operations are not included in the trace. + // 3. EVM smart contract "create" events always specify `0xfe` as the "code" for newly created EVM smart contracts. EthTraceBlock(ctx context.Context, blkNum string) ([]*ethtypes.EthTraceBlock, error) //perm:read + // Replays all transactions in a block returning the requested traces for each transaction EthTraceReplayBlockTransactions(ctx context.Context, blkNum string, traceTypes []string) ([]*ethtypes.EthTraceReplayBlockTransaction, error) //perm:read diff --git a/build/openrpc/full.json b/build/openrpc/full.json index 5f70ce52a..54d241a48 100644 --- a/build/openrpc/full.json +++ b/build/openrpc/full.json @@ -8359,7 +8359,7 @@ { "name": "Filecoin.EthTraceBlock", "description": "```go\nfunc (s *FullNodeStruct) EthTraceBlock(p0 context.Context, p1 string) ([]*ethtypes.EthTraceBlock, error) {\n\tif s.Internal.EthTraceBlock == nil {\n\t\treturn *new([]*ethtypes.EthTraceBlock), ErrNotSupported\n\t}\n\treturn s.Internal.EthTraceBlock(p0, p1)\n}\n```", - "summary": "TraceAPI related methods\n\nReturns traces created at given block\n", + "summary": "Returns an OpenEthereum-compatible trace of the given block (implementing `trace_block`),\ntranslating Filecoin semantics into Ethereum semantics and tracing both EVM and FVM calls.\n\nFeatures:\n\n- FVM actor create events, calls, etc. show up as if they were EVM smart contract events.\n- Native FVM call inputs are ABI-encoded (Solidity ABI) as if they were calls to a\n `handle_filecoin_method(uint64 method, uint64 codec, bytes params)` function\n (where `codec` is the IPLD codec of `params`).\n- Native FVM call outputs (return values) are ABI-encoded as `(uint32 exit_code, uint64\n codec, bytes output)` where `codec` is the IPLD codec of `output`.\n\nLimitations (for now):\n\n1. Block rewards are not included in the trace.\n2. SELFDESTRUCT operations are not included in the trace.\n3. EVM smart contract \"create\" events always specify `0xfe` as the \"code\" for newly created EVM smart contracts.\n", "paramStructure": "by-position", "params": [ { @@ -8386,23 +8386,14 @@ "examples": [ [ { - "action": { - "callType": "string value", - "from": "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031", - "to": "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031", - "gas": "0x5", - "input": "0x07", - "value": "0x0" - }, - "result": { - "gasUsed": "0x5", - "output": "0x07" - }, + "type": "string value", + "error": "string value", "subtraces": 123, "traceAddress": [ 123 ], - "Type": "string value", + "action": {}, + "result": {}, "blockHash": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", "blockNumber": 9, "transactionHash": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", @@ -8414,52 +8405,8 @@ { "additionalProperties": false, "properties": { - "Type": { - "type": "string" - }, "action": { - "additionalProperties": false, - "properties": { - "callType": { - "type": "string" - }, - "from": { - "items": { - "description": "Number is a number", - "title": "number", - "type": "number" - }, - "maxItems": 20, - "minItems": 20, - "type": "array" - }, - "gas": { - "title": "number", - "type": "number" - }, - "input": { - "items": { - "description": "Number is a number", - "title": "number", - "type": "number" - }, - "type": "array" - }, - "to": { - "items": { - "description": "Number is a number", - "title": "number", - "type": "number" - }, - "maxItems": 20, - "minItems": 20, - "type": "array" - }, - "value": { - "additionalProperties": false, - "type": "object" - } - }, + "additionalProperties": true, "type": "object" }, "blockHash": { @@ -8476,22 +8423,11 @@ "title": "number", "type": "number" }, + "error": { + "type": "string" + }, "result": { - "additionalProperties": false, - "properties": { - "gasUsed": { - "title": "number", - "type": "number" - }, - "output": { - "items": { - "description": "Number is a number", - "title": "number", - "type": "number" - }, - "type": "array" - } - }, + "additionalProperties": true, "type": "object" }, "subtraces": { @@ -8519,6 +8455,9 @@ "transactionPosition": { "title": "number", "type": "number" + }, + "type": { + "type": "string" } }, "type": [ @@ -8597,23 +8536,14 @@ "stateDiff": "string value", "trace": [ { - "action": { - "callType": "string value", - "from": "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031", - "to": "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031", - "gas": "0x5", - "input": "0x07", - "value": "0x0" - }, - "result": { - "gasUsed": "0x5", - "output": "0x07" - }, + "type": "string value", + "error": "string value", "subtraces": 123, "traceAddress": [ 123 ], - "Type": "string value" + "action": {}, + "result": {} } ], "transactionHash": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", @@ -8640,70 +8570,15 @@ "items": { "additionalProperties": false, "properties": { - "Type": { - "type": "string" - }, "action": { - "additionalProperties": false, - "properties": { - "callType": { - "type": "string" - }, - "from": { - "items": { - "description": "Number is a number", - "title": "number", - "type": "number" - }, - "maxItems": 20, - "minItems": 20, - "type": "array" - }, - "gas": { - "title": "number", - "type": "number" - }, - "input": { - "items": { - "description": "Number is a number", - "title": "number", - "type": "number" - }, - "type": "array" - }, - "to": { - "items": { - "description": "Number is a number", - "title": "number", - "type": "number" - }, - "maxItems": 20, - "minItems": 20, - "type": "array" - }, - "value": { - "additionalProperties": false, - "type": "object" - } - }, + "additionalProperties": true, "type": "object" }, + "error": { + "type": "string" + }, "result": { - "additionalProperties": false, - "properties": { - "gasUsed": { - "title": "number", - "type": "number" - }, - "output": { - "items": { - "description": "Number is a number", - "title": "number", - "type": "number" - }, - "type": "array" - } - }, + "additionalProperties": true, "type": "object" }, "subtraces": { @@ -8717,6 +8592,9 @@ "type": "number" }, "type": "array" + }, + "type": { + "type": "string" } }, "type": "object" diff --git a/build/openrpc/full.json.gz b/build/openrpc/full.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..09793ae3323bbc5d6740bb52e230c346936074de GIT binary patch literal 34654 zcma%?Q*b6x+pd#LY)vw;ZQHi3iEZ1qZF^$AG2Ymo*tYf0_x)A7_Q|f=ebBX5AM`=5 z)lWV5y04oc8XDxk*Y^d$(|M!45$a7%(aS%FwA907OlD7`Tz^;wbvt=@ z5h5Bx6%ZcKEla)q`4i(RS}>~76w6tDN|Q7VGUDpXwRrAjtr(QpaOVNhYeQ$S7NFl7 zJ{IzX==a2V#D9kOz^}w_zVmSb-Oa;u`RL6-X_A4eX6$bLur9^{F!aliRA zkMGN@c*B1O7G~Jsc_Jr^e@D%gYOBDG2Xj-{$c(s8XeBTnPkKtS*{t+{c{7WPZkC0!7GuVh36nB9O z>aWOe+?6h3fFDBuQBEaHZulNt?6m<@is38FgY_+h|ARM?k#l}@Xn}y)>mRuMhDJ?h z;@iR7S@@{sIlLSQe&ut>xZJJ&Cx?ggaKfAfBp=9s&&1<;@h#FEk*VdE;{_@7ZQ@ zC1qPn^zZwd9WNANmO`>)?6i6Qd{ns2bK;o;b+Oa7UDgOp6EW|VW3}%_6sA6To5J*Y z+E^{$-!@BdME`>P@u7n}-}U~TP29*<`aM&AnG3*UDuYGjxl3@)rA>9CO|vdZ86~Ar zb5gDgbyBwi(u!4CwUa3b1_<}#!V7}w3%V)bGL(c&ugUBXoj4GRKmvzr;EM zo8U>qu;<`Lo*OT6+9@BdnKHp>mv7-w=rbLxEyLcKpFamE7Lm}tBGoxi81kOhx3+7w zHb#LGxW0JbOLl6bOESyO1mGKe{a4Ek@W^Mx$VD)) zD2_=#xrR9+0*V**#o-w99)EW+_Ip`XnR- z_#2qtt!_FJ6pgzH>9UE;6s8-nzuekK1(gBXKWWv6{86|gNg(JTs&^28*@o}N8xR>i z42$VIdBi^&=kd-RFwN$)uMhs*4W7^r5;Rkk0+rdebZ)*hZ*R15g#vcfjt$}d$QRZ{K}UL;U8;`<|2S~DSKn#*UOP#oVzCNv&=oHF4Znj@Km^SJ7+Ju0{n%UMM7u2%YCz=vOew1i zG9u@>K_ZqBygbA^8m6QkzVdkKMZ%-6XxZN%#zXcf%3k>pKoqBupA~%o;v(|!RZP5T zENJ1gX*{6`HKee(aQlL9FKl5xot>PdW<7_Jlz?5PCjRZ=u30 zMkbujB)E~;4#*t+L@MG4LpI1OqA=yp2*-Go3^r&SImc_)fzzI2#A9p$tT--SdPc zP4=wa;zSEb@;}Rcmzjx)l?C`$lu4gogrO6ii9{Ep4Wi4)fEjh6{3HmQI4Wl(l3u;i zloeDdW#u)RUW+i|B=>8Yd(3)5-(O1%)0=zJwzbx9R{z!bZw<>Q>Ngw~=57d?@%0XsMal@q3>kmGhD6s?<6X|wwl@|irSwv5FTZQ z-K4W63Yy*>78?2!qF3_2n$x^+{!HKC9BXpzpVKP-8{g~7E+1xW8ixaLMi@5R@Pxu==rM(59;U5(tm*hbWBsXPw62|MKgqJ<9hiKCJr63Q|T&+*+mW^Uigt5+gLzj2T zUJQ&1Du>z5n$p%!B275n)ohFzQ}&t$rc4PIy6y2U^zG3v+*CCgnhT7EU7B9o^|CPr z7{9iq5aG5M{YEw)$H5s?3cZTmLq820ag>!YAsy{;=t#T-%XxN}f%7_#(oJMYDgNQW zjRGMI{yQRN7Usi(L3c4MU_XFp3o7F~7?i+(m4207Tc17XXs4Kri6O2Z>40`}KAD%jfg%w6}j3=H+wm;8*vz7b1U- zu6~~1D+qtSu5S0cgA=HHZ-D#G-o?Sm!{;dq=f}h2i9ylue*MnGii{G&!o{J+oN(vU zT={mqLYx{)dbv3!MY_g;dzg_r@WgBxO1dAYbczhKGs`F{&rC)i)susp_gHLGK~So{ zjG>?XqJZd|7O?VfpZbdi1`kYjmsrk>zb8!3_x3_Bt3iAReBoNI^g{vpcd^v%Qjih= z5+~4*V-rotQ#U?k1U_k?k$@kECJE7v7U0j`66K>}IDWi{x~u>g36If2_)VwlJURF4 zy6Yf+1>lX_-UooA3(JeeYs!A~YLl&d!w_I#WW|txrfWV@>n~~2{Hij0EoCe@2Fb!8 zI%=>3-G#@jrE)FdHP`kd&x(w-MN`?jP)rR_CL3VqmB@BUr5#WHX#vSNu;pdj=BhV) zL^8*_)~-zo(&_k6QTjD$yO=X%JL*D3WUIB*cy?ElH5YV#9pdY^_3zKv`uAx`_yTM< z5v$&e{0E;2to)VJI2<`D!mOvuO|)(NXvyT>jlzS=$N_lqi^Uzxz{od*E#Dq+qSC*1 zK>KE*Of8>Ks_9;~s1Tk^!)c{eo>hE!$WMJQ^A7-0d5!y@?dwmt&Cw*}zmk#L*JD~U z>xyJJ+sr+3#$ng7Ca$uXkw-dC?n?2)B@}!#IBLwt&YHS0qNAtgevI5m)b~Y z87+O2t9P$1zOSFQSBMy1i0rWPmx$+>J|@3g^lmzP97T)i?Hk`*=j1i~g|Cd@e?Afr zpGeChe^2LMO1s&_$;a>GCD0VbA(NW_r0ZMP3`o`6Qfs93v?ZR*3)Jb%#9VoEtuZi< z+($UCDt;Ch=g!Jal7Zg3ohxjy~EU@%qIg>7uAQOO&J!Z!=T?Vq;-S~o50hRNp}P$~1u<9G%y`2{!fbfDsb*znk3rj? zY(L0Diz{{MMa_(V?rr_{PZU1bLbXcpmMJk)jk+ZXx=MtKtV96g%?6)u?sk)71i`l$ApZFZudb5nB~LqeR8LVXM3 zSfoLn2)lZT1wRKZKPXRJ_O?KVGHNZcv&sXXV0Dhmnokd>^ z0s<>^{>9GQOM@W(ei{e8JK@**Pl32Ya9>x9kI!A{0$GS8U%aUB-R&mYgB9jt^ieMvO*E)$ao2f&@ zu@uH9o>gF^p~>op(MjkeZq_j)~}_ z6E+D{@C6f^K&!(Sv$5M;h*9~a3;Zp2;-O+Qe32jt-+Dox@~|aQw^8(3ZHIE45OBg@ zZ>!^0G}W~=e%g&*ZCA+05Z8WWg4i*g-<9Wihlif< zJ1_xK^S%LBj54kAmoP2nO$}5SwuWYpgTqFW4=F|DoihiPa7lp9!3F{p7o8{+@J)L8 zfM~bQV9Ts3?hc|i8J_2&dxkuZ!XUKNp+6G6`F&?w^*riNiFZg6bm?ss+NGq26ZN3x z(RPc;5Ih|s3`ORN>M3}g=uCo<@i1TvBeS_hH)Jmk=$lOEN_9of-v)%qbL5y4dobo?%q%vgSAS%?qR~RdKaQJPb`2$M z(Wt7lP2*G-l_z$wrJ1^E`}On6f4w1~lIM>z#CWLk9xzCsMT_YoM9@Y|77@XP0(Py(@OGkxhNu?m~+S&jLf?Q?RE9Nkyqhhn=pw0Z+5ER7nG{+!yZ>vl!$1T{`3g zcMrCPN3hyVOi8-gq(2;=t2+)(M3MkH-*f7OFjpZX%!syU_|x$bP@~6nt+wSzFL_EG z!hj-qvV*)yU(iAn^M-m!&lEoJO}ZPN1tj z7bv{%N2C_I?|3GY69T9Db#vB0VrDPtj`clbkD|{PL<;DC8eKP$X|GK({h&=X8w4ZM z29milmi>=`*%V_j22bdiTwb2>1_aqnp=++oU)R@a5bCeLQ2fAsk0Ykd%%@BxTEF&E92!?&gODR_ ziZ{Wf(V=Tm5IVl_PI#ORgh7aq7DaxJW&t5@4Y|?a&l8pC-_(Z*;}&ty0%lG1gP}of zkivKnl*GGIAZLC$Txf(UT3!(%-o+DzrxTIdCX#c_TeMM{Yr8ZUDeEkx4**gj9|9!?o{$Hj=3bI7*sKWX{QSIVW05m; z1*fIWnJbOnG%z0cwWa#H^eN4XT)jPb-!MzJ`9FN0pL@TKd+ldOzxVpX0q(RPp8|-v z_U;5o+XnCgjc9MXck~}0r$+|g!xWdkM=)!}5>ZFZ{38k*@Ag$e=gPERaUKzK2oLcn zwA1ov{ppsK(s!Xsg;?}@RBL<>6w&7?|S6NAE<#0-2iH4L`FwbNNr&87{d31q@9iO^Ad(EHF zG+G}A@)5CV0}5(&sJ>^HTv?I~nJ3S#6@H|rASM5W0G4y*s#asJ2-9xr!&S<8y4?cmKYC&9$EtFX%ppfodHT1=To zv+3>S%GgN^{r&W$3XGa1{PD~tRi_`1VbvA?zgMeFYox);vVT??p4BhCvrU^_OgfS4P*V#x0nrvqDME0wvUZAmxCr-puv6JTA%cnwBV-Dom7reWG$>gQQH1^%$%vI zx-^95TD1)poYJ;LKUL4Omat5Z<RqsQbTuNiToC1`PJ0D1w0E<=@0c^Zoxjg)VX_Sg-xiU^8|3|k0PX_$@~U|T zqsZcfNsk^-Ct9I``=6{|zbE>41i!xhL$om4qv3puRK$)cxappZXNT$udi^BZW$gcC zw>-xYg$v6Nd|Zez^&h&E*Y??5xqzCqiiGogZgF*|QUwzyziB)}TCD5G+rXqs>8|aM z?IDxx(Pib%DKa3*N`01p{|H6R7VF0LEha%i4SL7}QuX7Cv;Y?8Sp_6$3q)&U)8wGF z#d{Z{qGr%b(o%iuf4NQ{kj;lDxW2Q=Ygth*hU7NFm5h}7eG47Og-ibxPF3Egc_xMf zTrP^L5Qf%i!sW!FjdWs zCnuz?EIrANnuQc1%k8BRVYO4_~ag$wTs%^+hN#ENn5v~cD%xD$5 z5y9BA8|h>aww=J;WR@^9!>nuIj_eloaK#Yd&=ch1YEx;JEe0 zhK*(h#|T})1cuYXlJ>&3;5k_djsr(IUo18 zw{phu36X^iXf**;ctN$vnXaFo4OH?bMtjg*2{4WThL_ZjVyfJd#$;nu;R4E3^?&dQ{3zb6%Bl zUK7@SUBY2qULlZ<@;b-}1@G0HAbGpSm~<&e7W&V#T?IhJ)*=1q9uL4t1zSTeb;4z^ zCZTO`U8EzQao^J4UPq>6R{j;jYumpB&T_%E0iC8<-@CJ*BpPEBeq3CY;#%}Iq{l@u zgkAFlTyQ&{rQoKWHW!H2BeOlG!F`v6e0rxW-Z1Z$eT8Ma&=Qy3+9eo&iO?2zW!?bXc+B=es#I)%?A053tguZ9-S_5YCZIi>yrzm*;S9OmCST}dN8%oK6)rr z{AM|)zklSG1dKW0ToK_0B`72QTMjL#xZ0>+@r!UI<;%UZzSqCqXZ<5SL@-_Oxyj0k zG%XH=kR-0BC-i9zYsC=Ks7|AJd0;%r7!E&brv;|?Wjtb9~e+osXD9p?hTbI#@V(5K_+4VqH%}Y*{>&RRVQ`;=TSiL zrW&WAOk>ecm2xrl?She|bf<*2hX)wcRW2vwo=6|xyE&|(EAPO%p=6oyLWVqx`5KiK zZr7Ey%8l#Xzk~y(MW@R`q*(NfN)KG>%ub$~`9}+nmzr%;_YamMbg4q@ zbZ-7>PL?eb+?j6Dsbiq`3i*WbWvsk%3Mo_NTIN_+D@T*bmR8n{EZLc19qLungD1{9 zI^3hRlg*hXe+WuhRyoED^3MSy5xUfozK#iHD^SNUF*Q;da!86=Zlun@87^ zz7S0+<=kl&Z0`3fWb4gP@GB%Uez#}F+2;P6(|*gq+c2&t+o;beg@)}DU%bdJfnFR9>TXm?G~L z)rbm%*h50d8UC;2nfmP#GaEX*?thbKdR+dNY++n)ao}>Q0yo>ucDhglC8R;D1xxxg^VaK@04#|gL(Be2EHi&!#KQpsR8@LV!UFEaJcnQ-C7 zAG-%R;aMW_w3`$QOR43Zh{BaR!)r}g#Ei1Qw`2M;l(z#`U_af4olJi)yUL^`LU2hm z1+er{K`(iOEZIKIxdiA;@Ho;EyWC}sxA$|{@F>fW-ckIWReUg*Mw<$;IM zyuIR+duIF$$qx&@%=HqY4DMF6J}s}(i0tMJyRbefQq;5(7OapEkDMq%vXSS$4neQKpQ(rKL@f(0ytVVFc*Z_+_sYm3mErvuIM3eYJD1}Iek-HJ8sqp#AGhABn#OX~Sca^l zK3<2ZI_>`Kb7D6CA0a89G1CHYCI{7njh{+$V2q*xw;j$1f4NX=cU2936LW9;ZO64v z^2J6JZ|Tr19)3aCd-B^XiKI@8Te*M_TZyoz^0b*;D>}8)(nPZ!uTBckiRg%#lUg&$ z31R|6{CS?~?oJ{4>Qg)YX$|O=aafhN9qEHREfTF|@QpavxLvMpAIj9#X6TRBa8x`a zPM6Sa6Umpz+7p9MdmsRYKr`1&{p>AG#&825gln({feF34fd{tWD}nG20*Z zB*HkJhR!WoO!Au^7F|lGw9+t&m(V8^*-vJ-B*?RT3Eg6d%uJExJJ}Z<= z<+vK9)Rca1Hro<#9&wpgh;4^rvy#N0QLrMqPm`~hOyg;+Pn#)FXJ=kS!z8R-0>U3C z)(`SbxDr|Yk^_oaghdtvI>1E~q*YYVOLKR6et3=f?f!CgV$hraxxL#Jo&3$??f!~F z=oBm>eLz2s0MCT`4$U|Ov1Q~Ec2FDWo4bQ_M?>tD%mG8Ly5$uC0`=B&!o(s!NeaQO zZVsPWwSgt+eS_msd;YxOa8pLi(GT;y#f6xgjB(1M;#%#rNOTn`Wzv{Wi7C{wlx)?& zE~F>b6gv-ZLd9VIC4&?yX(xj^-p{uOlH|QJyCzG`l`0CtQt;w!eLCb!W=t(5prJob z2%~53&~_e!;zVx0-B}#TgD)4>Wx?uk&N&Fk9dj^e#7BC1a)T_s)MuwtyVJ;?${w(r zig!5~cSu0gL+QPP5n`v706N^X?zR8E<6^LU_+#}mNA0aGrm!zs7$ebZy7r_g zEcqT3UCy)JD`iO<5T_iTgoEyMIwm<@3Fkosnw)zH8gT~l*~&7!n%08j==Pns)=@h^ zA7)eIDLI-2TW~2feAJHz*Tv6=s*bQ^B)blzNAf{ti-Smk!)Bib0U@T zEM1jMPVO047V$W96r_M%hNB4##>5Ze_{H>vs=K!XMwg%6$~mo#f{LjnM;eyE4Ok9$ zUrym_rHLEOb-0F3(}aiAB|6Kr&{LAn^OO;gt!=O0{U_vbF~>^s;4&mxZGe1@9c7cn zP@4V&{t(p4{>W%h-=rl?TS91jYuAqJ%_Hc{YGR*w@3YsC06&GyKXVKkowdX9pJh<+ zx|g?C>>0cSjCE!L3N#k_=$X1*z9 zc$M$HO#a)GBo8Po@`LZ}yt#{Z6Z2AVaI4%@7QiK`(6XFdtW?tr`d?jUnQ9m;Oxu;$ z#3QA8s5q;}z*+B*X+=ub)PX-nWn~GU(+S>)TxEdZ>wkxYiKOosVRaz52(_2G9Azt( z=yFpod`urox z%S)c-$5frm@cGxwRJ1aSJP#8Llr-(v#Kk6MH;!9+n>0|K==n+Z!e_%v4hLebIaWpO zuIAXaADF1B^`1^q1ls-Iwd_H4><`U6X=)V>q${p9h7PTW$kGOO0s5b{;NJ5=kq(%L zB_~*cXC@#7pQn1D&v%YT|}3LQ=nZ;`uY*P^Ku@}3efYfOlC##y@Z%&loNbRCuBD# zmo%>^%;F*KI#N>#`3!Z@YhV?UMun};eAS;!&IzH)=4D4)$otCms|JN0do8(GfFjhCH2e8ZN=;a0j+T(dBH^bZ?i(*sQQl^NmoL`9EJ0k_sZteVV0u0M#Mi%A zWe_YM%^CG{yye@I*gWBjZ(Aw{N0aINgbDP)c`Og?x@BbL=KoU?PlUe|?16>&!bYO2Xkw!| zfDc7unx>8ECME3nTkx@~9v(PI_D$}*JqHA8=GQL)FWwi<==h!qvNHTJnXtfS!y-7< z(1rbfq*(Rdy>mwZM)ZelTkc;JW&Rv1Mh|f{Y|GqJFJTeaPaX+F0r9{SNEAdLZd1WS zr}8)!B#{HKu*l#5BrGV<$In`tJ7W1XUmT-2!GM8GdW`oY@U#?HRSA$yB6Ip4%=Y0P zq!G#G!-#HOnAVt{T(&GseIdn`=4f}+#S~Vc!^izfyjhkE+g9eg>w-@d&ytpr(d|v7 zd^%csQDAslYSGLzL@B;xOA0Ro>;Tq590h54nj!OjZzma6mpY<0m&M@@Hn4y5Y>1lx z*+3Xjkboa=U_VMj;$fQqR+6ay|{VVBL}(0PSW%O=Izj1$`iDLhzTV4Pv= zZ(E;b!08LQw!=&tbRNQ^+s=?!+wx@La0J;{mtvyR~ z8nfP-#Umt{!0o~JK?|{7k}E35IX#(2&25WfT4T82OZsaJnn5ow zc(#K>J-Ekq2XY(vMu|4|nA8()s7*T5?sPGI`fm2y1n#%i`reYc;0~=) zOnSJ-_@cGplb8EH8p!2tQr#`jqr9sY>QB~^hvv@$?Bo4OwVIB{<4Mdl0jHjiPMNdK zsV|g&0xjpdAKBeec<(lS#Yzob&Q$mrsXP~ar177)`yZQK#Dmx2#lYQ>=}JPm((Wte+FNbM6ZUn_>Ew8sh;Ud zOpE}sf|V((eXkB%kWr6wN$At$0PameU%63g6o2MLs5R6-JH4OsqHck;Hkd~Lfpo5% zFbA|5mc1I5qg(WA9*t@-tpS?nruVh!O14=GJ|kO)wHRxY)C>7pWh&DP&1dlAWX`|Q zinFk2*_qC=giA+xaV!?}>qQAJ7Rd#s$a*=u8Dtt}H7SPV)J`FCn~1I}l)q{qgZQs3 z*zBqvIMsVNZ0|LBjqsyGdy3~X5YCl+l87DK*t;yFzGs#~?Iz$vHx+~56ZNJ1OX^l6 z$ z+K`EeTs%xZ*-9oeXO8#}ULbveddxY!F2Cf+fpZWu!WX3aZ%GsJ!z3LRzj+$}PO6P4 zg^kp4YFngahZOp7Pt|pglXVbdcm$;81r)vtsCME z`HjeUF^)c1r&Hf|ihZjt_V|I12a)Y;_rjK1dM^A+19g0-rY*i&MB4F zfeMxN`okCIn%@HDNmK*vrJ|NcY)DBf{}BhZkz3->z{^&w_K|&+y~JCPooRb>=UM#} z*i7pm4@-#^Xa>!{Yi0=Pu0F0VgQ0rAL|&8l$znvdG52@`i3VlGdCsEcxFABDcsrol z@QH;7iggGBi2@8G?2)bAE>DTeB=tshr!bzTkIIM2?W?ctUW4q?jpn4UbR8fZd8U0S z%VH(e@NzXLnv8)7aFh)i=o>UqJb~L!QG4`pkXlQvmQ}XqUEEsVm@eL75z%9;E#6%z zjn!6lG7j2Hqi3o5QzvQRwP4NDi?(C3`|1HOJNQxf!2!zK!des8Eg$?f^6}mn%JO(; z=PYKn9=t0hDm}bOha@`wgZHa9G;Y(h|qv2 zPM8S^2+@||-DsRIuhQfqM$ZS1OArm!w)!j-flGQ45^#&$k5zz{@LqNt0g=L3a#zd9 zNP+uaG*T0_ zD_d6k5Lnf@nlTSCa|o+T`lP0%QkZYo(J&c*YS2NITG*5aa3aHWPS-V?(!H0sSEQ^P z>gb+@@{xr>ElGv)_8n3|Y9PPVbNJ;XTQ;J`PMc(6Ue7%WG=;`1Auof_AaAMuowY9F z0i*BpohWCIyQ14yEY*+AUTf1$m2C#88arju;BH<{DBAO~`um@L)pHZ819sS?gVK*P z?u1CD1?XPyOJyu!8O?gL);a-i>ypsan_lRs@9q;m?F7eA4z^#W0M6hJL@Y+ z(WMHN6$~UxJ$lNk&k`@bgOj(4swjFr!Uk@|(~w95ie&a6AUR@6({zb&`%bFXmuGuAoM8 zzr0pNK7u2eDQH?Xbv9uImuVJvO-4j6?vZ3ElrP|8Ny5MnA$qR!>V8p^W!RgAP!$fdgoT^`lv$=NwDT_tBytJ(myNst*7Vn8Lo+;kET{di>{&tLEcM>SiAFilAa z*ndEW3Wb-nqwj&8@4C-->zj#^6+u1TOq1CA0^7!LnC{FON9l$mFUz{OygNV4DSV$v zI@X^>3-y5qx0RX3FQ=Mbo(j$y^v^=j1G1^VIt#Lhi_L0e=nqV(bb7RHu2=@rs&v1s z-dO(*$8_2ebDc?O(}|x|xKefq5t)OaGOn%RgJ1`Z5$35Pr|zq!NK(6zJ34I-$WF>a zZVb*%TOpp%udmctvv0;i2M(vXk5b_w}&Japmqi_ca8KbCL6yCv?Y7^zF$)>rC zo)z5xE(scIrFV1)eCeJ2l(`Y+)AE__o%N4JmTp^n`ODl^AgPyj30Ja(m5iHZ7ra6Mqu7u5SRZLY|ny z6GcF{07vELiBW`=H}eoYsy>-(I`g0aXUb zApiedcd_0DQgG@#Y?%=;4w;dsx}~O@#82}*WiQsWRGGe%idaa&QfLK?ymXq=Ke(Cj z+F*%`jAv~w6O?0MC|KS6Ju^d-B70}C`jCv1qeaY22HcrhL~Z9qxOOiliSXsezs63o}W;WO>cClloO!fo6P)y%J<%P(oIP0z6%=x_-x?A7#uU_tBvC!^r=~ zioUs7{K4NN&i_Q|3YrTdi|bF{g!mimi}b9Zz~hBN5Y)9hY;6CmVAQ9@r-S99PnM?R zpEck!)*^n`8PyYVRMg%IJA*xdRD9}2Bi~#eQp+B*PP$2FTN=ppA^qJhI0S%BP}%L9 zSz%Rng%Qft^-^V96z_RQm(yy}%AdrLwvqp}*Pun4`~K_I&Rr*T8Nc=EPfPg98vb;& zm9sShJ@>lCxA%Weo&Afzx(8W4+?zdVsO*7gD4?;Jx^?G`!SlBs z>X;tgfb^|4%w7))F3!t5NeRV21syd;O;JhD$YNSrD@PpPv+Z|U#4}~uW za<&CNKf>mcQt^9Zo)Nko{&cy9Y;qSGt2LPWhQ$>E$xk}rWsinZvwfVr z6)`|(cUk}b*<=gZgioGaXaSISkq3_J`NY}DuZSUpv4Ml5v zd&NX9p~xEh3~xJVdmXFbiZ^4+po;HQi@?PlSvEo0ncA=5K(5x_6w(~+4UvRYOt#NQrkpi|e_g!%-i9=^ z!Rw>qniONK6G})~+u_V_%Jzn)mMt|+(O&n+Tx(L(MS|n&8DVJ()9laQH$bu=;A8DQ zYz!qA?WgRYg601t8k}e}W505yBdfvC|Dge;OMVaD+=9Xi<#B189vFqlpIZ=ls1<1! zGh3x8maYp+o}?pEf_U}r|I`t_P4rLZ|7H`8?r_M#lTZJJAl?T3zt?0wy%w&=dKdMq zO7q(cnLU98%i9D6yS75I6zk-EyY9OI1yxL%7moW&}x^P8le+#J4F9y0!(+3GzG z`9wkX)tuko1CEe#{FT{<;;%x(ENaJoB8lUaJ7RnrOUsk67Il6J#DPFmqD|KPFu2WT zkC)TEO&9EkvWQAb?wR0z$iudjy`4WR0E5cR z0lFL)q)S^B#GuXS7X%3@bLA$&8<)AI*cRx@Od~oH_(P(d0o8YsJ|p1CmWy8le68qo zo0koyQ0<&Gd-EbhNhtRrP7d*$0SEY;F6_y)#YRU=3+YdGd^3zulPd;V%!H>1e=;2j z-qAhMqGcK>4z_XZY4YcEPUUbO-T({{+w(WvpC@2kIbl^F5J|<0el(Bdu6T`Jg#4!F zb(nJ`PBLvI8*Egcj{hP5AU7Us?GZvGEu%@mu?C*(}hhpVz5eljm` z3O>BU#sq-!!nc}QR27>HB0XlRQYsc@na;}6M7_`0%alECPgJyA{TK2RZVc#4byexG zjK?%okSSzKaom4fSjHqXKqsTaqx_uX4wo^Bxy^wr1qV&K0j4Mm_@Ob6hFNU7)moE{4v|3#U z;IR!6=fs0%^u&0UJ>vWwiEhA5;{gOEdUOV8zI?+E-<^mRctx;+5nI+CQ9u`XHyN6p zk|r1I`;WKPr30G!0x@w6tXc?WX@;qy6^}!~5?P-#$*d?N$bqAG1yHo8?!W8<+B-p8 z{RP|aZ}k>66o$Y2eOzVC>$l${mO5lP-Ms!`!-;>wm}R%4J|7jc#LTIaVo~u!4Xu8X z(uoGc5MpYd(~2xCSD%ShlUKlQmZcSDOf65?5AG~xS7l2WE%}E)ev0tw)L=P-5;Mo@^5D#?)Zg?LC-> zZ85F#?dohiTuJ~>z~GE(+bRpygPvhipxq`*Ps<|61Y z#4Lh4q)cWhfrVdiSgMNoHM#+xso>fSm&Ew8ic?D$)LX~akNj@A$bIZbMTf@1CdmF4 z#QH=->w)m@d8+_%FwsGP5WRV>k(srxAJZwv{ zdpXyN-nUjealOpnqNAtY7By&HCb5D+>TK<+Iz=0ueep@8($=!vs6NL3FqNs^q&;QD zW^FxXb*bs)i1>Ra zQsDJp;?BNEc^+FtSFly6_k=BNV|szh2)<8HauQi2t`EGBh8VmEjxy4H9~a4pn7=it zb8}du4Z7DGL$Mxfxv^rbsLYfd-?pmFJoju0h+|jlVqL1^Yy247GQYC+vQa;=zbpjH zQG8*G*WN3a)Jk^<^{;%<$;@DvSTt3wRvA@C)l#=st#VJ+4*FP5K9tb|)* zRP_d5Bu**vU(oLWxlauVRTEIqp)sRoI}9tLV*LroFB$*rU9#gw3us2ikB;Bq0g><- zX-td8|DyNo+D^lBNiBJT0M1x)Ywvb8D;z3Y5%zot&Q_vq3<{kb8xeLH&+qCO8gpj1Y_K=@x6%PJND!J|L8nkG*M{sV^ zN*CG8lS@(}W@yT43x;FZi5=L;|4?;KL82^Co9)xKZQHhO+qP}nwr$(C&C|BcGkyP= ziI{oI*UE~j*!kt!Yd<`~XY?6r8}b_h1pbAr$SDD@{0EuD4!!_W>U6RPM4&@1MC*Rq zxflzPRNkNj%ib+2xhhwFyQF2nPlezEPmZNHF8 z3NpRUti@eQx_QRVvkFoxUMj4$*P$dFq*2=I8e?KXh^ zM>-o`AXIy0_9WM}mSu1)V{-wCGbTq~hg+z3)~WlmxO_*es94yfjUqX$lcmdU|NT8C za*bv^M)tDbgBwitkqb4KaS%`S7*lEe0UQww#RgZ%zs|A_eUC2S7{y0N@ZEjbM7$Su z#7YzUJ3E)dn84Ky6+oYAd(EX?(4txpX0r-P);;G9 zk?0JS17+;hKP;a9EPMmBY%E8=zr9a;t5H4}2?cm&*{?MHn&Ryt7r?cte*%yUDx2DV zQ}}PQS_&M;!gre65s`HgddIvl8-1lCnFdG>-oZJ=KBIKAx;dZ4E6m%Ek@wsJV!1S2 zm6d(!w5kes1$qSfpx`#D!a98K^O6VfsdRrc!?7-`>-pnI?is%07z9NCJ7k8WRylTn zn(#C5H;sKp=TfLFvw^&Ab`XPN3p+@7y+?RIPfitaxF^7`141C|^R=;!nWK>SmA)g@ zZvfuLC5j!Y>$snImqi#B*8XDK?WLvrNC{dk))=vik_gXq=uDfZCL0j!!}lm;297|i zFB@7x47bHa!G|TPm?lv-0nIt5g63wwEF}h`B1jCpUgwl!$1hR)rSvGh!ymy^Nvpne zvvjVWwW39$M#j=+QKnhqwaRgoiCc#-zr%yssjc=awkSnPD5VN!F!Sxml53vxx~Ae@ zSOx|vo|?ZAU;?mM23Xv#@_}>A(-$Mpr!)js=Y`|d+ru=hJ?5Ka`!NH-8M=Ks`%w-y ztY+Cj#|3%jqlzZno6<@9Nv*C3?5%<<*d6k+^{lNGSKsQdI?xy<`)dFBc)y2MO0W*V z8N9$QZKjq=4WotOk`Z|CKXRTqw*RS=kBIYQ&1{#7p;{Qth&OgN7#66R=Tyrm9FF`B zu)u>y9H4yg9E7+?c&*d2`;c+)}9P2wNw4-$T7cHw*aB1&OIpCqJd1v#{;5^TF>{PxfPEu&MZrsimQ zB@HQk&#xPCMu)U37SvGbRol-^r*?ytkqM(9p*(s#lGxbUOvh46Wk-ul_!M~$KT$HU z*qd1EZ&iF&35!8^zwr&(Y0h@9`8n>k;T~8pU|u2xWF0s(Ah6J10*~2=Np&hCG@bOc z!<5FUhe(lVQ)P5xyGK&PO<3mUtEYrt$x7v>9&}ps>P8ReSZoT7x3^fJ1dE!$R`uP><<8W{uZwm4YOMd|ixNw7}qsUI$=~kDYs;I-^?Hm-bNK?fbbJ zy>+E0Qkm39P*ie{7q!?rYCiQ!Nz^_W(&soO5_2Q+DW|THK}|2I>}ZQFpwAai=TSlx zksQlvP8UWbV4RT}Yu6F5S$;k=3?C=P*q)FBfg}8sUaW(lG8_36Sj;9trJRC~UMv+i za|_YC_%0*HRN{HI`Z=|Hl1nQX-g7XkUy#MnEDZi=@haXQSZ7pqv9gv-5a-lRIEkA@ z{;@%Tvy67Y;JE`<#jr%~@zMzS#d)Gb5U8h5k>KvNn)BxpB4(A|iqO$42IJS4*)^G5 zwcw<*uK7>mB4|Vlzfh_PmLV{s_y82bWBF}qt;oYU4P?TvC&eG}SBD`dLX_^6aq}F_ zt(LdSsr0!Fr@v*DM4j5KzGx^BMY3f$@tt|~53P+}{p6xp`5R>qU(fZVHOM=r;*T>Z zM^n<`wpDg%1#_9-0W*pSim=Z9b5=^l8QLDBULL!i;Y{Kd*JGQfrh13#@+Q|Y-Vj&! z>@sga_Y>-|R*}4K!eYei$ZIBRMGhL~cFfv=+2!Yq~2C!}aeI1J_rJgrVYSI-4)- zpA-x&=*uVAenB1lBK6_T5(O!>+JR7dfoO)jm-$+qA&%RnrS*Rtrw~+NmoWV^7;Ex} z9&m|N?R3!AT}29~=)%RKHw`_`E03e3ByvEeq}t?r+iN}5dP1$J3d~8Uq@)(VltdxT^<(l``G!>G%@9+JW&i-FBIlMtM`%id} z^!5Y0fJAu6B*g=?u41#thPaH^3>l4EeJ}QlQ(f$jY;sw@L!{Wa62h{7^LLo&vwSx=`xZ zGDyy>TgwJ2gDiqEu<~K3q~m4MNx1v!L%B6zHEGR-g3-P_8MRo`mxt8GK}cmWl)bNu z;}H*kWOhks+An=C`vN~1X0Rdup(53%xX_`gG7UU=-sy6)T;22>o(KVJHtxfAxT*3h z#vp_1m3RCv37wP_QAXYM4ocU^!yM5L%%!lfisBd5?&;rV27t*ljJ z*V4Q4p?Yc8yt-F6u1vhLhVbmeFsy2+tH#>qg>z+#W8hY>xC*moEqj)Vc`bcp-YQy~ zuO(+3@OJbLXs1+xKHlmQtOE~enZCUh;7{Q9pqYYU#Hdn5HW3r~e^o>3H{dhMxkTVk zpl1KCYAAn+oWw#K=l;b7Ajj%xv?sj7tAdH%2epKIyt}5FsTxb9-GVFYH~Uo)pG5>H z&%s7u>5|sVZbX%x2&P45EBtpu4ak!@!d6G@BZpw-A6kL61LV$!WVNC&1n{4uhtOf( z8o;65<-dzwb${#u3R`D`sO}PLY9~K}3}ALiBT~!*UGY;TIQfUodwkM|NwJ=wH4D!3G#Z#v6?9rSlUQn?3O1PB-WqmPnvz4XWyDNh{?hto zZUW!syEXr&QpyvcO8zDr=4j094THG$S69xHNPkTnBugkY%XkhUt-s2+r}7OvM|jxy zgwHSqM=px)vf8d9008uj@}Cz5x*irbAQFZ3PaCH-RDilccDRK8pxHJ)Inu`c;r)?` zV9H(JCaQe?bZWW;t>MVsRv_nB^Qn8A$plB0(;b z&Zo?e`0M+;;jqa`^`n1{Rxo;Y46kR|QZ-cZbkT8+LWQgH%s`D&EdcRdAidzI$!_%p zSi=1^=xx~eDc%I4IDW-X^Upc+MtPFZ+`VyfZDhfW{KpJ=)8uvpsqf+=VKJw?6^2X^ z|7{sGqNUtMF-R2wet3J&3P((_qnp4pDu@R1GA@V>9Hs22Xf?sf1-8IzFAH|0R{5Yv zf)q?Qd49pjUsBtiQM0sHu+{;dbA{^d05c!`xHBLsq;iqtuA4~@nUNXS5e2FV(>!`B z3zvw@<5T_Kf990R!dfhY#q;`34uusu6BrqQ9wclJ%^Ecmjx2o%J@t-k7o&FzFZBoN zX%%cv7w z(};|Tq`V%t#iAl07TSAgpTgN^GwFj%(L=L4THhCpB&cDMj|Dl|8+URozf*h6Qk&#u zg`Zz@(1yT|ULWRoQ}sJS!rt;n__@tu8m8t_?Q*$Rt|}X!ke+KV*>jGx?1eF}OA%!I zb51Qyi$cXf4q}B!5Z9+;4D=1VHOW#HJV!=2z0hhmzijy1FO2+ip9zIRlTL+?PTg;Q4&DqA_ zN>40%tDb#HQMbzpjRKjKn0+lwdMY}1z{o3I)e!Q~m8^%5Y}^krJ5ASAqOX>5YV9&S zNL1Pe-glL2MjDGr5u~u%Vg(*jPKgC{SA4#=rHHcP5pfp-U0kDuj>=WrvM@=`QL%t- zi}zelUX@JWb~=H6HjTYbTcagqFWBzCdLe~(XgpMctFq>A_U?Ja7~`qYsW zD=p{6N`poLiJ9!1$eUzP$Xj~%8|8MU8;o_hL1}YE8<#&cs`ExZG;(wDO15N}3(9Al z}< zmsudIamcZ_19&(()BveZL*2rlBqRk@Ra5=_$iU=Wh197JJmYCd86fs1HXKb33%;jD z$_qsRAVbxNgPu6c95A?%Yit0#{P$N7h*rmFlRG76ewC2q(g`I#0XK)sLJ1;qc}P{q zM6&jR7`eEfhe%5vxbz@M`jAZQL1lyz*r8T-)wpR`fu``!@=a(ooMLM4fRvDZ=(aBCAqw9~K!FkwXl~NNJ^;u2 zg*nF7nbj)7CSDZq_h_X*ux^x!e@LM}Fc7%~qVaNY=?HEVYnT(=V}`XWlVW`uRe%^0 z>@6xC1!)HZINn*8SgP}zT=8gg~^Hzr^XaKkuiUeheK4&0_Sw(blSGfksk{$)>S9NV_P5gPC z>;#MpDjrOv*X5D0e5NbV0L>@NDP{qsN7i=wt?6v z{ATA_=k68)V^=o2@70t|$WFBG{d&EpE7Xmv+9BE>(`6~=b-7(jdF9cJP;cp%E+Kow z`4RP6TX3`P3X#r=6RztUT`3&-!Ot2)MWRV*;3`RvETsKh6rT4nRWk5)bJbWfV?~MK z0eYtx^Sl|4b8-xOM{rTN)t|r3k!sPB?FSq8H9LUnyMC3mh2Ou*R@_~Aq7rZy&ah@! zbkFhGN_Xct{vY`qPGv(RnVy}z`yhMngw~xB9q|W`5b(zr@Any^7~T`M%n7mOd!pl6 zmBJG!r7ARs)53`fc*@(#6m?%}BNa_gHZ)qeCz%e9r3C4e{1>O#{I}$nmrs*t6-oZ? zh|br=EAW~w5nHS+7m711Xi#sU2Tum*-u0c3uW-FksvN`JFAVWTaLT>e70oBn%+G@k z#_g+MD09wZPt>R0H&&J?g#CBrO^#xb* zAiQWbj2wD^r<_c)ED%f6+YSLESql21?&~THv)~i*_+1}}tt6+-A4|`OjzUj4EIEqy z?-7f#%t^qQ9xu;RhHErRP&*BS%Pe?K%f$?}v~-&{24#+in6{*bGt%}{sL<4f)vIBl z=g2^hk<<)C!!n-JEl4sbi9w*_Ew)#`rEBuB-nWcaE?YlIuC-=gM|0Yeg&@)-gC7Oj zQ&yz|-occT0gY-^dxl(QdC3|=6=k@OV*R{`lLKpQl8MIC&P-AIyTcq7AP%pEtct4Iue}T)qyfPA(9x}Fv72Y*z#W5DM{!QN< z|KE~JE~~H9iy8q9QPE4?gsHkQJ-Gf5vD03t_ArdUcbuDRKx=2Yf27fWU+q86t?;G* z-8hUhm9{@debYp~86X1t6a`9X=(#kejgLdW*Le%Oq#THsLpwEgr9yktq5YX{C~X|% z&rw2VH$t%!%H|$@#}!k8Oe%M!NBPI-b~=kz!pfncBk81jDmxxi)>||HG865n!s}?~ zQeo>wTxa%wiA?~MLe7AN^+5S`!D)9o3DW=v&isoyp&;Q*qhrkGIr5R3HdkOUe82ubg*J76Pu`Mi<=E} zi*UX;AA|mR-19>wq!lwkNb5A&m|Ek2I{|%=RXL}BVZ_}z;J(6uG$wu@7ISmIBKoi4 z4jdhWDi5}3de|KDT#0sC@jiiG7(W{cKm^E{_t`jxk2gq*V5gZGTF^5{=Lk@))F8nj zdyiY#AIJx<<9oUc=$A)@s~xwxix6bf(p#7ZV@5Vq?sYmTfWCJgRs+Eer0qwdFfY<9 ztSG-GW>?P2;@o9g3&Q4rIcTdOU!(%;x4H|I-^j$qH}!WwE|*_p$ef1{$jZR1%J#r9 ztPE08XioW1Y$<6~ZHa+C8DI{kMGW#k(v>NS$GF)>^AwUHSFB1474(bmxQ$8$K>u9s zFy*)Bb_@xW46W+qjEr_7EB^3LN3S$Da5#Pygb^51A|aYdNrpGsl?X|X&0U$fSfu8U z1@N`i-v4dpvSkpy80*nSLxA|GAa3ti;pTphF9(-saMkl| z_qxXr?gJ)h;Ahl}5ZP0vi!ldm7C!H6ktmUW(fv~?f4m^r?JpEo*eV*1v*wEVmu0UhfV>*y(Fl<6{7C8wvdhR$V zfP_U0k}sb8dueP)2%DHhhbqAAqegsgUwz{K+nH7}k%r6#DIdZS>YKWRLjjndiGlOU zlYt*LHlve}7j-Z^qn%?Xs@U!j<_Os5=^1D zhCXq^-a`m=sula?1fnqUi%yOH5^l}i#RlQnu;KNM>6%JPn~(}*pzUv z(h2uW;dC}W)Dt&P8#%qPOY5s8zD=z*L1*8E2};lhbTmc>3)hUv-Fr&`;V;qtJVg3) z;Op*pFyUZrX24gOXJ^s;LrQ?xgE@v0tE7c{xYG4tNLK^EUi1V1|MF&%Y`?Y{3<>v0 zUyy`;ryjj;!F432_B=iO_xN&|+_Ax$sa^A5F9o(tLq1Lins}`u=K@-8MB5nnN7>lJ zW|QG~v_`I1*Vv@jegLCyrdBkUQl{DY=Q;ja#5?zu1|(xs{K zdyft0>N|A@Be~kXNvCKhXMw(pX46zZB!Rk*O10Z}{3dc^dS32BeX_Ii(O_hyL@^DU zO2Iuy2N_pkFAO_mp0C*Hpp`paea%n@{tClc=P1|UC?D#pO_GVTbh$3v7iH%tORs$G z(!6Tn!Ni9}?H!7Al-{@175seY50f-;*YsZ;`-8y1I1ZS7=2&`|x^N(f{^*$mV=dS^ zm%AE%X#f2_6~|>P&kYyQs)$8Is5SARh*c$I;aDCl zlZX6|=I8+^Bu#~Jax>ysw(I?zO+ULnm-m>-LNd(XZe+*!vMJCi_3m7zHY}kBBWdb_-xV9Y<}tGgZr|)QlHvw^-U$?J_TClJzO4 zjb3v0kFAc4sS;Tw8LpRg2FY<&q$o1`x>vA*9#(Nn3Q@`Po!45} zUcy&1wG=uMFf?T_3KuoO6ADogh1M#`Iq^$a;fu{yOx-rX&1Om|m$COUEmS)Wgxs-k z$S-SRrrN(!iDEET|43F>_-@p6y*&UO9G^QxJ4-2hbxTw%01T&R**Ra6=je;zFEQ1(B~!@yKfvsk!* zCyo6A;N?lp#XT++gk@uMDr~BBIVl>LK`H8(suOJT4=Q>+<@Z*Sm=%6_M1w^}HVd*2 zPoLp7fhUQJ+IK@0VJUBdg)w(JwjXdi6dQGR*bAncraC3Xom#rs7L8N&XeMb?;! zneyhy%Gh%?3j>54YNRBG8BDa%YF`|ucEXuWB}tBg zh2XE8n3dB+0pKKYQ*+e1mXLge(z>d?X0R3VvjO=ftCNa>v9s{F)~q$=_-pC+NK(hT z;_F=0fu>Wm5a@rxRT-jTRk@*|n{k@VIc4r5HW_ki`K^VseI*-)iY@PXF4W};U8LIC zAv8caJ&Sx&I@)vteNn6T+llyp1j#hFy5p?|smYDS#8l1Y@w zO59Cf24-D%aG>$v`5p*zTTrbUT|aqvHFli6!VgyNnRNTJNbU5$s_A(Rar>&$@e-Z>EvH?W0vqlthiXgZP)oNf5w?T0 zVpkd!-Z!CiwuC#Ks8KW0;B>B9z_DHkF_9!-QPe4M75RQ8?`CZvamLBH)S=DN?Q=U* zI{iR@zxx4U~F#qIV%V2!`$tAyPFKX+wgSC1z#?orTSqrsKHJ zKEtIGtl3x*c9-rx++3D46P=a#+S3=To)mOxQPbAyV1Zy>L$xyO_l zxiPwG^rIE{L~~#<5#FrE2>T$%)KaCMPr*J-rmCwu2izD+ux~e<{8{K*X(VnO=W3tk8com2-DGN>TaFyEil+t zSJz^kujlr!us0S{bJ!!6dBIf$d+X^)GS)q*R;r_O!k(&QsQ4Aq)K;E_N*7aky3r~r z(gW38k!@xc=E7t>U!jMai>*96Z5F2TgxDe|lcQj-+*B5qxm*iA9p=K+L^mlCqhl}M zP&SLXJYyvahSF5nImwfvV6RvcQ;xYjZS`8F(!@n1Dbs>vyI5BfgsognS^K&%!ff7n zB8D+Ve}3GbdMLeu%1$~Fn!4A^GzrP&_G;OL?bH8_gP1l4VR)S(mvD41%(=96OiRme zMTWKgZ||8v%6rCPsSNd~rJ&_@>4vT5yNDH8|FoK>5U1$`&*oya)L4PEIrw0EZ4c5$ zr;=8?QWLd&E;-NlW`B&Ydlh1Nwb*F73(9Jj3q~Jh@gRw7ErQ)`Rh5Mk%y3KT(f+Xu zBgi#KtF2L$pm7xyTJ>-%;+Eu98D+yO>e0NUX^)bA54s}PzEA)8DnpKdG;KUQMq!&2 zdrT6CsqkCi)8Lf&V_4WuiT@Tpf&)BX_di_;tF5dbSCELB`g*b%$ID^0H-{#LnD{-oPyc*wcrL-I?WYfyL`>B`= z6?!E=QI=Pqae~yJfHJ91idmz4Yt~NBDHt<>L!A_vj0Gp0RXjk>fb%erx6e8Z>N*5N z!aK9cNhtg>jxA_Jk5Flut>HUzfv-%3Gt(A?mLrOnSs6}4%-mvG1yf~DnE?#WwZ{P);)()$akT3Ce}73o zetIONkO}2{E6j_kETTU%>@a{CtFdK@aPEwyMO@W6@1~&+qiM_%D?7IkRs}8>wkjPK z@J5eR?^psyXq&4P>+#p%y+G#2~UD>}3DOCQK?k!9wb-6yLZEElCPOa1l5eg)! zIR=)%ie5`hzNbb{41C;Zzvw(OO+A8XA@X0`BSxb*?(#@&_<1elELg^IT=A?SO!!s* zbz>kyH}y3CQ>2o$NB$EYTcTL!>tonWHdMLJHO|w|#X<^<3;|{ZX@&4#8Dvba#rG4t zY=aOPMRgtCrqP3{EbYS+ZG7V8>+za0cO^zqR?(ua`%5ZL zY4o~F3pkH-q9^o1hOz!3sR=``91zo7Ijx#3vLSFV{m9f6#O1_lB1|B`2~udlO!YA= zP~Vg?q|h;-5+p`{_d6{*7q5yfsU18bm)ER%s#ybWk?rCOD-t^=;?<;P4n?YC>zhkZ zh^)_9yF%w#kd6XBywDgPj0Ee2Z(luQ>r)d_Z@ zc=*J-9=t1tYpcGdMc^NIy%Y#@|jo$VXfIMS(Yn|BX8b2%>nmeSiNttO8!lTL&?prk!*y z=8Q_J?nv`$4&3IQ$8>BA(&$gv`<=u$cflx@EJ{FI zIYzfqI?sc0urHx(Toj?5sy9KGPMUfa(M~n?b#1&OEsY)rI`)O+RvKQ|cXD_i#l^18 zsDkv^b~M$N1nQ1oChDb(H_Q58c%!viT)~jFSMbe{DrXeFCA7g6kl@iKkv#H}JNE#- z7#En)p}uHsMHBgcS5K}O9yZ7DjLB>Kb@SYT0vX8{6MXZHOS>QN0}M!>wPQF@-J1GtbxNt!OuTSV z#JPA1b~WwXxi%8Wp}im&S4j~bH%%Uh(&s3nRb2rAZf1&f%NmlaKYa13|`$RS!`>3;Bj?3E3r zAFwlJBc>cy3if7;oPsi}FvS6;rdBJa( zQGhJ>twX<#mf44var2uC+h`V?4#DX_6^rkTB8Uu;Ct?EeOtS*YBCEG7Me#5Y@Eo#-?~T_k;qbb`iL#D zsWaf!WJg+>SjsCffxW_QCEapcXOvegW+87-Znl!(mg)8mV1>wx+&yZ@R|6 zS6o9=O+g9n^^#4y_k!mL(OgT~7If4j53C4*(HU(C6}>ltq4j$$D9~NJv_}e$N(oi* z@%lEWdUS6%cJAtQ;$N`R>{s~Ei1AQAQbw1)lAxN&`H-jBN*BC*^-%-+ocdKxi-{3Q`v<%cz>PU}t;r8n;Y9%^|9>a#pm|_ST-)^UL#YR!u8QFB9@+pVR={-clqLxlX=_ObeR1K~fKYRAhbRtCIt@*#17Fn8?iB)iDUl zB4hH-F2 zgY`2!5;~onIRA)n3SeqMkWZBIi6`A+Yj7-GmS(ZXn5g-La!NvcrC>iNSgK?suNgWQ{3D~Nm^15;c8>muS5CXMRl2wOoQ2OEd+D>e@y<($ z;WJG7R2+Z2kI5IXbu1u8KuV`(iokgYZlR@F_Mt;Q`h^w(#J0m#9li%hRNIUs!T^fr zh1no77yDHtjvUQP*vQC-?Gg0tqbFTr?&uWjuIEe9eAbSL zd6AIf$MtfNcNV&K<6vwy(~PRBEiIZ-oZ1_u5h31bP3{E+q8_l8y$){yzl$`ZpPZr#2H#Oy_+Tz{^QXbL(_*3CUY{XCGT;FcW1a%XNnj1~nfr zK1(ecdedSWDBvnOJsf>(r>`epyV-kLk9nQD+gXom3ZA>Q!0uhjYzLNgOz&r$9FkU; zFvKx5uV=u`pCoy)1`Dza7GPa@j#+x;P?bWC7yswTaix4Mwq*Z z;)B_mMv!)+Tjv3O})l?4rSFhjk;{DeJlCO-hJ` zUMQDer`RT#8w$c0lfE+g$OMeDx#CVw3WMk7$-JMGLWjmA8!DC(A62lKNK)cH@pQ?H zY6Pwo&8p9FOJyAm_JANwTx%WtB4X_WkrNny zA1X2lWZjgnD^8FJHJBEEOnz0$v>KTWHzbctA0s2Cy}P;*HzwL*Rsod8!nntSHmcun z?aoyPzWRt+40KK-x{CQqb z`7PcvV|?2?*MwzO;W5#k=4hr~RR^OhS_%OCR#OfWCBfz)IculHtfwl~0;tV9XvMc*kpvwnw9vN*h2xnW1OSam~VwLlU)hV2I*vS{#q&l`D-}8IHM0|*T%Lox4F=!zP_VTz>!;+g zlTSP1+Nl3JZzl4*cJY|^NzQpc0u5DN?AFXKtT0_pmmUg`W*uzvng6Vr6!6)pW3Zq! z>*8uaiA1T^Ar2YX7MzVZlVz0Qf*g=1VY4HNTs*K6W$#;)n0vpF;jC%wsyFRIft8Jo4&v6%>;Q!kG4eH_G}@DD zk$7&LCwgMf`Z3wky~&BX96Tyi8Vx7HEQGa7m*Cy~zJ%ehc7Naz!hGS8bIzQ}oLpY# zzUAtz+hg807+?BsteHkL!C8@|xC6qr#o3n!|4?!i3!>@(z$We~FPCDd!*ENyZ@f|e zA#w(}uHcUzV88?zS7#hDhNcID8dpRMcC>&a!jzthSC-t_?j&hDat6--fhjeoctSY8 zgH>RUzp*R7fEC8+R6YNfj~#6;D98^1P70qiK!eaS4WBm;Pi=2p`w1wn3BB4yn1MG6@%G4-wt9h&b8Bprj4}%4 zf1#NCe`NKEDW#C=B03y{hibGQP#z;5!VF3&U?}pC-QjVhjJQXEMJa@s+L46<^`Y^* zNT?wIhu6s?3QZ=>Hy42F)BoxdLPa0|A>dpLNsWd_ z2`6X}B+w_vw5hZUHB?cTz$*0wZ|pEY7!FL&OC344T(xjo?N#*+_4^=>piRC`_eeBUE#>_P|m zhdq7cO2))_9A)6n7xMZkU72!f?Ts=%z|k?LZPC0H6hqaKqQZ2C&^X69HM})2I+3Fe zrk4CLn~z}(B9<%>c`8}y#@0&l*f<48AcP0csceHH_tPzO4{_*Ddff3WsDmg9Jo#33 zwE)8kq6O8}HaWF~GfBlp$9 zL;oXAklkLig)DcoKv{%xCwrpyz(+2YJtmpGwz=$Hy0*AQ3dEuHu=;ZvS6(vBV;U%{ z&%OF^Dj2~+f|lY-0PAbiAqe;kr}WRh_x?to>su~-?9!OLxmP*)*{LuOX(Jn70@(*m z>pFs+CWQi*Y_vuXe;>#$=`9%4T$HkLKUpLj49WFh2PxsClDv-YfEvB46^7GSY?tq; zb593kRxNt4qP7I?Z*`vzYZ;djSw zbVDa}=L%2u_2n6Ki7`pd7S0jsk)cZw1+iou$>&q_Hv2M2%=EKJEs&!rrL#r%x`oDz zs|}|uNpOQU8R96>zLL_M;!fAqT?)gF;gaui+07TE1}QYpeH;(GyGmvbc30zyQkMKD zednV5Kx>wB#kn!mH<_Oq>^3Z=IWEIza%>AW*ux^j_50oAeIv5f`qUJl!(gBTj+yf>m_5qCtjbNw%T)#IS zxQ^K1L(@G*$&zvJB1MMdLXO^|ZMsjJr2YEe!fEyh@*GC`-~nV}r+$j?V1AW54+~T5 zV{0XS&kU?3!G)ph0CW)WxfKv?1@X@}R3v1g!wcrv5-mq5+MjL?53pjUrbRPldlgDN zwG9;yDSYuT{1GBrrS0UoCAzj*s9K>ZG8d^|yy{OLe_>Bd>YmK&INcvuKLy?50Ffuj z*8pP56Ab0O--#tAYzA%P*dsGRh<#dcCob6}(ObG>j%mxBI3`tK{Cmx**B@}vgq`#V z|Nc#II~3L<(`w~kl_)ofao$4$+eQ$}1R(46ur9zk9=Sq5;ZUnqlimGWs}t%u=cCE1sPf*xd=nm&iCmad6IVNYsDGH$8?XxM z@~q_PH8T>su^4R>W9tW4O^bmqz6w#cO*WWG$spV$MO#@u*?Cnha3$r1F?gc+?|MN*<<~* zc?7Y^lCf8}3NUz9SLcI6P340$gY#FBDg^7l_*1{F?t>0cou~1>XCp~akTgD+e&`Y- zPmvJD3y{6gKL(icu+s8l$F_b69Phw9NNj8o+W#-JjIMZ&)eqEtfg;XJ<-??M(X@u` za$!}CR?<0BR7Ll8A~=uF(^GVU0e5Nn>A_n5Q)Yv%(kY{DFVb2Nt_PBl||ga17!l}>0siWYTc!>4G7YsDMKH?p)b z_pbJMSQ_Nb$|VGyl3Y3B;0&jb36W_?@Eg6`LstOY%mF8q;E(XMor9+hMd$L{&}3I4q?NR#cg>Tpcki# zXub<4CoeUAel~}kQe#rc{u^DrD`ri@_!MAiR1~}EGgSdmyB)-eGsBXaDJ(#I(aIeC z@;XQ2;6n^R^*e^ZKZuf)%9`f_9Xd*9etP~oz zt!Tp27rZxeV}eS#aYqrHkqv~~-IJTAJJduhWRx8|q*r)lI{EJ}YU%>RZlFb3Xq@?r zYL2E@(L&;d;QOy`iZ`pJ`HpLZCh?K2xHbI2$|Bb*EzmL#3_qMOSc-q`TM6K#>U5YmL+DNWFpcw9AvPq!TCY4|aF4BtA z?}L5r1dyCpb|DpOXmR@|LZjIjB(^%6BKu640nn>q=%R~*k15cWK}H*Shoy&j084<6 ziWmHdpJ$ptLXl(a!zTa)z|XuJSB)uOFr;`&mypF)EDr(kz_{l(9+bY+fklaK?)k*q z7vnCMgRxu)#CXaV{m*i7Y@lt+g}+k@5gS709zOJEOrYv6^6;D>Ih?1ct z;7Fe=Ue|iD<3zxgRo%Su_Kk`md8R3ASq2I+L}yg?#`Y#Yg&i#mMD|48mT?OLYiKvv z15p)fVJN1-PzVI1V!GdA`&Dl-x=Zjt{s9W?40gnjMD~w%KMDGXGbsIS5dhwhwNHZZ zak=*QJmS*10!5_n14^$Jm~0F207taeB~s^=q)VZLj1{2{^;vY@$%=I)HJwQX4RiJ1 zeZ+bah@|iB<#A2T4Xwje8$a|1gu8mkmWa+uMe*e z-<5qK-{ly$3Bw9q^YRaCHShT0N&A}ar%fvYE>%g0{?zEAj6Cl0X6F?*x0{rx5~%}x z0tLzMAIU?CNVzx}LfO)X&;xn0F>GsW$#ZjGjL*qd_Szx$>ex%$Jv}E2n+*ze_oFEF z(NR0QTkFYxcCBrD6aW6X8Pz)!F8-+YNyo5z(CAd}(VgC&K7vP)9LI3QUzWK-Fv*yC z5>iwQ8wf57E|3u%nI|1$RL*v<&u*%87K}o<){hFOGPs@6XA$}C#LrCGDtc==?T_r} zczMaYxygXX8M6;*({8#Ba#QJ*f&bLXl09TOvFaAUYXW!4Lq2}7P=&g#{2|s4=O<() zko}WX2LKFAQ%C}CNZj)_c~tB@sMQvX-wPkc%^P9Cm`i?3TVUW;zZ%l(ocxgo552OZ zl!4BW_K69m)WYAV?K7`tKD7%$kWPpUPP%B-s>i@l2%&KPs)o!fOrL)ge1~q8T3ari zK<=9Ji!PaT*a0a_ko25nzUck4Rqxw{WXxk!pMhBa)cFQ}k9 z>fJ7Z8-rgh)b1iscROO)ho!*H)#<~FGh$vnkFzV*w!w5}ATbMfV9&BD5uSdw;&!F^ z2^c+a0wg-`o(C5Wx6k>bvQvoUtg*U#V>-qHd9ri^;dMdRF}kw5`cFeP#CNvwU^h>K zFAtVlS{pAJn1gNW1ULn5lY$3?VoTOWC(4UvrE6j1D&!xNv!6xSlRoDbpB7{8_2_jv zow!U|5r5PY*rS_|Rm=!h10mhR7E0Z^`FDDqJn$yGITU<0B_EwP*m&|bUK?>0YDURM zJ9TFF!g%?gz{z`77Ui_IW097i0#D;W=G_y1h?qoQb`QjA81l@08@L)Rk#yrq+nhk(nhn7q;%DdriB<+Uqo#31>DH9q%TM7Kn<+Io1Ca zn+0V0&eWlVV!|Ob_aT&{7QivYy-&9H$vzv&@wV@x9 zl&2{U_P4idE)Rc&QH1y*j35d8!!&ZG9ibS01HWqL6ds=v-XsiiSJ?@eIY5VAGFou z4t2CRc5Ch@n4ykPi!emHu=Dm-dtSDwt$SQ9aJFjW(v^nt9|d!)XSlp$&~`@u^c&I( zI~MM5@6`nPt7&p_FD72gWugkZYk@jO=?}DAm8r+3-$^WQf#FrFN7rm`5c3${L5{!_ zNx%wgL9MYt{PfEyKcsb0Y1wO+pfu%b-xx+rCbW>mt!J{i z?$@NQRLkQNY^!d5g02954yL1&WJeYwo5fWULeyGqAIhHx@ynJEyWO zm!3xM>Uw?ldtZ;r`*>33bS|NB5-M)57t?U2x({AYprts-2y|sJ&M0hqQ&bkDd{kE-IJTxy&?H*e`mWUzDUm+JSJ_|4;~YrtPX#D z8!oxnTz?PZ<($*4>kJ`y)%Ger?a-jCS#M6=VAag- zGq;%_heG|>P0|^V`fdeY)+SfEXZR?JKLZWl3&1MNVK)0*M2e2_R`=R(s14=tA*M6? z_XQ;ZQXhVipg0Pv;3H&bc!m{j!$-(IAan%h*~`lq&2Tc)56)$F35nWssfVBnyt*<8 z|8O`D(fBUj!}|ecXu&5-Hhy(Ky`TR*BD;%G@Xx{QZg(=HQ@$96_fvSk&o;jMCb*TY z_PrTtwYx=8-G7c)%xQ=E)@1tLr5-)lPIvtDIJVtKsDDfJ{PQkmCi}zfS`FUroFuMW zy~U3_;pSh!avp4CYLg>`$Ky#8YiN2I)XSijGRVH!Y|s5CtLi^fF#Y|V-Nw*ra}j90 z*y;k6xcLIn@%I4Cz}l_9anow)T#ctua-mkl+fQEZV5k`a?ecM}v7b#RnFRe~(R3g+(exoY>swMK34KglE mfIC%W?XaXi8BX)`eD!wY`}c3(|9=1g0RR6g57qP<^9BF{rfrP? literal 0 HcmV?d00001 diff --git a/build/openrpc/gateway.json b/build/openrpc/gateway.json index 42521f0c8..269566dd9 100644 --- a/build/openrpc/gateway.json +++ b/build/openrpc/gateway.json @@ -4439,23 +4439,14 @@ "examples": [ [ { - "action": { - "callType": "string value", - "from": "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031", - "to": "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031", - "gas": "0x5", - "input": "0x07", - "value": "0x0" - }, - "result": { - "gasUsed": "0x5", - "output": "0x07" - }, + "type": "string value", + "error": "string value", "subtraces": 123, "traceAddress": [ 123 ], - "Type": "string value", + "action": {}, + "result": {}, "blockHash": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", "blockNumber": 9, "transactionHash": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", @@ -4467,52 +4458,8 @@ { "additionalProperties": false, "properties": { - "Type": { - "type": "string" - }, "action": { - "additionalProperties": false, - "properties": { - "callType": { - "type": "string" - }, - "from": { - "items": { - "description": "Number is a number", - "title": "number", - "type": "number" - }, - "maxItems": 20, - "minItems": 20, - "type": "array" - }, - "gas": { - "title": "number", - "type": "number" - }, - "input": { - "items": { - "description": "Number is a number", - "title": "number", - "type": "number" - }, - "type": "array" - }, - "to": { - "items": { - "description": "Number is a number", - "title": "number", - "type": "number" - }, - "maxItems": 20, - "minItems": 20, - "type": "array" - }, - "value": { - "additionalProperties": false, - "type": "object" - } - }, + "additionalProperties": true, "type": "object" }, "blockHash": { @@ -4529,22 +4476,11 @@ "title": "number", "type": "number" }, + "error": { + "type": "string" + }, "result": { - "additionalProperties": false, - "properties": { - "gasUsed": { - "title": "number", - "type": "number" - }, - "output": { - "items": { - "description": "Number is a number", - "title": "number", - "type": "number" - }, - "type": "array" - } - }, + "additionalProperties": true, "type": "object" }, "subtraces": { @@ -4572,6 +4508,9 @@ "transactionPosition": { "title": "number", "type": "number" + }, + "type": { + "type": "string" } }, "type": [ @@ -4650,23 +4589,14 @@ "stateDiff": "string value", "trace": [ { - "action": { - "callType": "string value", - "from": "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031", - "to": "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031", - "gas": "0x5", - "input": "0x07", - "value": "0x0" - }, - "result": { - "gasUsed": "0x5", - "output": "0x07" - }, + "type": "string value", + "error": "string value", "subtraces": 123, "traceAddress": [ 123 ], - "Type": "string value" + "action": {}, + "result": {} } ], "transactionHash": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", @@ -4693,70 +4623,15 @@ "items": { "additionalProperties": false, "properties": { - "Type": { - "type": "string" - }, "action": { - "additionalProperties": false, - "properties": { - "callType": { - "type": "string" - }, - "from": { - "items": { - "description": "Number is a number", - "title": "number", - "type": "number" - }, - "maxItems": 20, - "minItems": 20, - "type": "array" - }, - "gas": { - "title": "number", - "type": "number" - }, - "input": { - "items": { - "description": "Number is a number", - "title": "number", - "type": "number" - }, - "type": "array" - }, - "to": { - "items": { - "description": "Number is a number", - "title": "number", - "type": "number" - }, - "maxItems": 20, - "minItems": 20, - "type": "array" - }, - "value": { - "additionalProperties": false, - "type": "object" - } - }, + "additionalProperties": true, "type": "object" }, + "error": { + "type": "string" + }, "result": { - "additionalProperties": false, - "properties": { - "gasUsed": { - "title": "number", - "type": "number" - }, - "output": { - "items": { - "description": "Number is a number", - "title": "number", - "type": "number" - }, - "type": "array" - } - }, + "additionalProperties": true, "type": "object" }, "subtraces": { @@ -4770,6 +4645,9 @@ "type": "number" }, "type": "array" + }, + "type": { + "type": "string" } }, "type": "object" diff --git a/build/openrpc/gateway.json.gz b/build/openrpc/gateway.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..bc012704306a49b7ceaa35f33cc4559eaca6a432 GIT binary patch literal 11862 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 0 { + switch r := env.traces[0].Result.(type) { + case *ethtypes.EthCallTraceResult: + output = r.Output + case *ethtypes.EthCreateTraceResult: + output = r.Code } - } else { - output = encodeFilecoinReturnAsABI(ir.ExecutionTrace.MsgRct.ExitCode, ir.ExecutionTrace.MsgRct.ReturnCodec, ir.ExecutionTrace.MsgRct.Return) } - t := ethtypes.EthTraceReplayBlockTransaction{ + allTraces = append(allTraces, ðtypes.EthTraceReplayBlockTransaction{ Output: output, TransactionHash: *txHash, + Trace: env.traces, StateDiff: nil, VmTrace: nil, - } - - err = buildTraces(&t.Trace, nil, []int{}, ir.ExecutionTrace, int64(ts.Height()), st) - if err != nil { - return nil, xerrors.Errorf("failed building traces: %w", err) - } - - allTraces = append(allTraces, &t) + }) } return allTraces, nil diff --git a/node/impl/full/eth_test.go b/node/impl/full/eth_test.go index c364a4873..05c3f2575 100644 --- a/node/impl/full/eth_test.go +++ b/node/impl/full/eth_test.go @@ -1,11 +1,14 @@ package full import ( + "bytes" "encoding/hex" "testing" "github.com/ipfs/go-cid" + "github.com/multiformats/go-multicodec" "github.com/stretchr/testify/require" + cbg "github.com/whyrusleeping/cbor-gen" "github.com/filecoin-project/go-state-types/big" @@ -177,3 +180,40 @@ func TestABIEncoding(t *testing.T) { require.Equal(t, expectedBytes, encodeAsABIHelper(22, 81, dataBytes)) } + +func TestDecodePayload(t *testing.T) { + // "empty" + b, err := decodePayload(nil, 0) + require.NoError(t, err) + require.Empty(t, b) + + // raw empty + _, err = decodePayload(nil, uint64(multicodec.Raw)) + require.NoError(t, err) + require.Empty(t, b) + + // raw non-empty + b, err = decodePayload([]byte{1}, uint64(multicodec.Raw)) + require.NoError(t, err) + require.EqualValues(t, b, []byte{1}) + + // Invalid cbor bytes + _, err = decodePayload(nil, uint64(multicodec.DagCbor)) + require.Error(t, err) + + // valid cbor bytes + var w bytes.Buffer + require.NoError(t, cbg.WriteByteArray(&w, []byte{1})) + b, err = decodePayload(w.Bytes(), uint64(multicodec.DagCbor)) + require.NoError(t, err) + require.EqualValues(t, b, []byte{1}) + + // regular cbor also works. + b, err = decodePayload(w.Bytes(), uint64(multicodec.Cbor)) + require.NoError(t, err) + require.EqualValues(t, b, []byte{1}) + + // random codec should fail + _, err = decodePayload(w.Bytes(), 42) + require.Error(t, err) +} diff --git a/node/impl/full/eth_trace.go b/node/impl/full/eth_trace.go index 123d96fe1..e4e5d794d 100644 --- a/node/impl/full/eth_trace.go +++ b/node/impl/full/eth_trace.go @@ -2,15 +2,22 @@ package full import ( "bytes" + "fmt" "github.com/multiformats/go-multicodec" cbg "github.com/whyrusleeping/cbor-gen" "golang.org/x/xerrors" + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/builtin" - "github.com/filecoin-project/go-state-types/builtin/v10/evm" + eam12 "github.com/filecoin-project/go-state-types/builtin/v12/eam" + evm12 "github.com/filecoin-project/go-state-types/builtin/v12/evm" + init12 "github.com/filecoin-project/go-state-types/builtin/v12/init" + "github.com/filecoin-project/go-state-types/exitcode" builtinactors "github.com/filecoin-project/lotus/chain/actors/builtin" + "github.com/filecoin-project/lotus/chain/actors/builtin/evm" "github.com/filecoin-project/lotus/chain/state" "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/types/ethtypes" @@ -18,10 +25,6 @@ import ( // decodePayload is a utility function which decodes the payload using the given codec func decodePayload(payload []byte, codec uint64) (ethtypes.EthBytes, error) { - if len(payload) == 0 { - return nil, nil - } - switch multicodec.Code(codec) { case multicodec.Identity: return nil, nil @@ -38,217 +41,565 @@ func decodePayload(payload []byte, codec uint64) (ethtypes.EthBytes, error) { return nil, xerrors.Errorf("decodePayload: unsupported codec: %d", codec) } -// buildTraces recursively builds the traces for a given ExecutionTrace by walking the subcalls -func buildTraces(traces *[]*ethtypes.EthTrace, parent *ethtypes.EthTrace, addr []int, et types.ExecutionTrace, height int64, st *state.StateTree) error { - // lookup the eth address from the from/to addresses. Note that this may fail but to support - // this we need to include the ActorID in the trace. For now, just log a warning and skip - // this trace. - // - // TODO: Add ActorID in trace, see https://github.com/filecoin-project/lotus/pull/11100#discussion_r1302442288 - from, err := lookupEthAddress(et.Msg.From, st) - if err != nil { - log.Warnf("buildTraces: failed to lookup from address %s: %v", et.Msg.From, err) - return nil - } - to, err := lookupEthAddress(et.Msg.To, st) - if err != nil { - log.Warnf("buildTraces: failed to lookup to address %s: %w", et.Msg.To, err) - return nil +func decodeParams[P any, T interface { + *P + cbg.CBORUnmarshaler +}](msg *types.MessageTrace) (T, error) { + var params T = new(P) + switch msg.ParamsCodec { + case uint64(multicodec.DagCbor), uint64(multicodec.Cbor): + default: + return nil, xerrors.Errorf("Method called with unexpected codec %d", msg.ParamsCodec) } - // Skip the trace if we never reached the point where we invoked this actor. + if err := params.UnmarshalCBOR(bytes.NewReader(msg.Params)); err != nil { + return nil, xerrors.Errorf("failed to decode params: %w", err) + } + + return params, nil +} + +func decodeReturn[R any, T interface { + *R + cbg.CBORUnmarshaler +}](ret *types.ReturnTrace) (T, error) { + var retval T = new(R) + switch ret.ReturnCodec { + case uint64(multicodec.DagCbor), uint64(multicodec.Cbor): + default: + return nil, xerrors.Errorf("Method returned an unexpected codec %d", ret.ReturnCodec) + } + + if err := retval.UnmarshalCBOR(bytes.NewReader(ret.Return)); err != nil { + return nil, xerrors.Errorf("failed to decode return value: %w", err) + } + + return retval, nil +} + +func find[T any](values []T, cb func(t *T) *T) *T { + for i := range values { + if o := cb(&values[i]); o != nil { + return o + } + } + return nil +} + +type environment struct { + caller ethtypes.EthAddress + isEVM bool + subtraceCount int + traces []*ethtypes.EthTrace + lastByteCode *ethtypes.EthAddress +} + +func baseEnvironment(st *state.StateTree, from address.Address) (*environment, error) { + sender, err := lookupEthAddress(from, st) + if err != nil { + return nil, xerrors.Errorf("top-level message sender %s s could not be found: %w", from, err) + } + return &environment{caller: sender}, nil +} + +func traceToAddress(act *types.ActorTrace) ethtypes.EthAddress { + if act.State.Address != nil { + if addr, err := ethtypes.EthAddressFromFilecoinAddress(*act.State.Address); err == nil { + return addr + } + } + return ethtypes.EthAddressFromActorID(act.Id) +} + +// traceIsEVMOrEAM returns true if the trace is a call to an EVM or EAM actor. +func traceIsEVMOrEAM(et *types.ExecutionTrace) bool { if et.InvokedActor == nil { + return false + } + return builtinactors.IsEvmActor(et.InvokedActor.State.Code) || + et.InvokedActor.Id != abi.ActorID(builtin.EthereumAddressManagerActorID) +} + +func traceErrMsg(et *types.ExecutionTrace) string { + code := et.MsgRct.ExitCode + + if code.IsSuccess() { + return "" + } + + // EVM tools often expect this literal string. + if code == exitcode.SysErrOutOfGas { + return "out of gas" + } + + // indicate when we have a "system" error. + if code < exitcode.FirstActorErrorCode { + return fmt.Sprintf("vm error: %s", code) + } + + // handle special exit codes from the EVM/EAM. + if traceIsEVMOrEAM(et) { + switch code { + case evm.ErrReverted: + return "Reverted" // capitalized for compatibility + case evm.ErrInvalidInstruction: + return "invalid instruction" + case evm.ErrUndefinedInstruction: + return "undefined instruction" + case evm.ErrStackUnderflow: + return "stack underflow" + case evm.ErrStackOverflow: + return "stack overflow" + case evm.ErrIllegalMemoryAccess: + return "illegal memory access" + case evm.ErrBadJumpdest: + return "invalid jump destination" + case evm.ErrSelfdestructFailed: + return "self destruct failed" + } + } + // everything else... + return fmt.Sprintf("actor error: %s", code.Error()) +} + +// buildTraces recursively builds the traces for a given ExecutionTrace by walking the subcalls +func buildTraces(env *environment, addr []int, et *types.ExecutionTrace) error { + trace, recurseInto, err := buildTrace(env, addr, et) + if err != nil { + return xerrors.Errorf("at trace %v: %w", addr, err) + } + + if trace != nil { + env.traces = append(env.traces, trace) + env.subtraceCount++ + } + + // Skip if there's nothing more to do and/or `buildTrace` told us to skip this one. + if recurseInto == nil || recurseInto.InvokedActor == nil || len(recurseInto.Subcalls) == 0 { return nil } - trace := ðtypes.EthTrace{ - Action: ethtypes.EthTraceAction{ - From: from, - To: to, - Gas: ethtypes.EthUint64(et.Msg.GasLimit), - Input: nil, - Value: ethtypes.EthBigInt(et.Msg.Value), - - FilecoinFrom: et.Msg.From, - FilecoinTo: et.Msg.To, - FilecoinMethod: et.Msg.Method, - FilecoinCodeCid: et.InvokedActor.State.Code, - }, - Result: ethtypes.EthTraceResult{ - GasUsed: ethtypes.EthUint64(et.SumGas().TotalGas), - Output: nil, - }, - Subtraces: 0, // will be updated by the children once they are added to the trace - TraceAddress: addr, - - Parent: parent, - LastByteCode: nil, + subEnv := &environment{ + caller: traceToAddress(recurseInto.InvokedActor), + isEVM: builtinactors.IsEvmActor(recurseInto.InvokedActor.State.Code), + traces: env.traces, } - - trace.SetCallType("call") - - if et.Msg.Method == builtin.MethodsEVM.InvokeContract { - log.Debugf("COND1 found InvokeContract call at height: %d", height) - - // TODO: ignore return errors since actors can send gibberish and we don't want - // to fail the whole trace in that case - trace.Action.Input, err = decodePayload(et.Msg.Params, et.Msg.ParamsCodec) - if err != nil { - return xerrors.Errorf("buildTraces: %w", err) - } - trace.Result.Output, err = decodePayload(et.MsgRct.Return, et.MsgRct.ReturnCodec) - if err != nil { - return xerrors.Errorf("buildTraces: %w", err) - } - } else if et.Msg.To == builtin.EthereumAddressManagerActorAddr && - et.Msg.Method == builtin.MethodsEAM.CreateExternal { - log.Debugf("COND2 found CreateExternal call at height: %d", height) - trace.Action.Input, err = decodePayload(et.Msg.Params, et.Msg.ParamsCodec) - if err != nil { - return xerrors.Errorf("buildTraces: %w", err) - } - - if et.MsgRct.ExitCode.IsSuccess() { - // ignore return value - trace.Result.Output = nil - } else { - // return value is the error message - trace.Result.Output, err = decodePayload(et.MsgRct.Return, et.MsgRct.ReturnCodec) - if err != nil { - return xerrors.Errorf("buildTraces: %w", err) - } - } - - // treat this as a contract creation - trace.SetCallType("create") - } else { - // we are going to assume a native method, but we may change it in one of the edge cases below - // TODO: only do this if we know it's a native method (optimization) - trace.Action.Input = encodeFilecoinParamsAsABI(et.Msg.Method, et.Msg.ParamsCodec, et.Msg.Params) - trace.Result.Output = encodeFilecoinReturnAsABI(et.MsgRct.ExitCode, et.MsgRct.ReturnCodec, et.MsgRct.Return) - } - - // TODO: is it OK to check this here or is this only specific to certain edge case (evm to evm)? - if et.Msg.ReadOnly { - trace.SetCallType("staticcall") - } - - // there are several edge cases that require special handling when displaying the traces. Note that while iterating over - // the traces we update the trace backwards (through the parent pointer) - if parent != nil { - // Handle Native actor creation - // - // Actor A calls to the init actor on method 2 and The init actor creates the target actor B then calls it on method 1 - if parent.Action.FilecoinTo == builtin.InitActorAddr && - parent.Action.FilecoinMethod == builtin.MethodsInit.Exec && - et.Msg.Method == builtin.MethodConstructor { - log.Debugf("COND3 Native actor creation! method:%d, code:%s, height:%d", et.Msg.Method, et.InvokedActor.State.Code.String(), height) - parent.SetCallType("create") - parent.Action.To = to - parent.Action.Input = []byte{0xFE} - parent.Result.Output = nil - - // there should never be any subcalls when creating a native actor - // - // TODO: add support for native actors calling another when created - return nil - } - - // Handle EVM contract creation - // - // To detect EVM contract creation we need to check for the following sequence of events: - // - // 1) EVM contract A calls the EAM (Ethereum Address Manager) on method 2 (create) or 3 (create2). - // 2) The EAM calls the init actor on method 3 (Exec4). - // 3) The init actor creates the target actor B then calls it on method 1. - if parent.Parent != nil { - calledCreateOnEAM := parent.Parent.Action.FilecoinTo == builtin.EthereumAddressManagerActorAddr && - (parent.Parent.Action.FilecoinMethod == builtin.MethodsEAM.Create || parent.Parent.Action.FilecoinMethod == builtin.MethodsEAM.Create2) - eamCalledInitOnExec4 := parent.Action.FilecoinTo == builtin.InitActorAddr && - parent.Action.FilecoinMethod == builtin.MethodsInit.Exec4 - initCreatedActor := trace.Action.FilecoinMethod == builtin.MethodConstructor - - // TODO: We need to handle failures in contract creations and support resurrections on an existing but dead EVM actor) - if calledCreateOnEAM && eamCalledInitOnExec4 && initCreatedActor { - log.Debugf("COND4 EVM contract creation method:%d, code:%s, height:%d", et.Msg.Method, et.InvokedActor.State.Code.String(), height) - - if parent.Parent.Action.FilecoinMethod == builtin.MethodsEAM.Create { - parent.Parent.SetCallType("create") - } else { - parent.Parent.SetCallType("create2") - } - - // update the parent.parent to make this - parent.Parent.Action.To = trace.Action.To - parent.Parent.Subtraces = 0 - - // delete the parent (the EAM) and skip the current trace (init) - *traces = (*traces)[:len(*traces)-1] - - return nil - } - } - - if builtinactors.IsEvmActor(parent.Action.FilecoinCodeCid) { - // Handle delegate calls - // - // 1) Look for trace from an EVM actor to itself on InvokeContractDelegate, method 6. - // 2) Check that the previous trace calls another actor on method 3 (GetByteCode) and they are at the same level (same parent) - // 3) Treat this as a delegate call to actor A. - if parent.LastByteCode != nil && trace.Action.From == trace.Action.To && - trace.Action.FilecoinMethod == builtin.MethodsEVM.InvokeContractDelegate { - log.Debugf("COND7 found delegate call, height: %d", height) - prev := parent.LastByteCode - if prev.Action.From == trace.Action.From && prev.Action.FilecoinMethod == builtin.MethodsEVM.GetBytecode && prev.Parent == trace.Parent { - trace.SetCallType("delegatecall") - trace.Action.To = prev.Action.To - - var dp evm.DelegateCallParams - err := dp.UnmarshalCBOR(bytes.NewReader(et.Msg.Params)) - if err != nil { - return xerrors.Errorf("failed UnmarshalCBOR: %w", err) - } - trace.Action.Input = dp.Input - - trace.Result.Output, err = decodePayload(et.MsgRct.Return, et.MsgRct.ReturnCodec) - if err != nil { - return xerrors.Errorf("failed decodePayload: %w", err) - } - } - } else { - // Handle EVM call special casing - // - // Any outbound call from an EVM actor on methods 1-1023 are side-effects from EVM instructions - // and should be dropped from the trace. - if et.Msg.Method > 0 && - et.Msg.Method <= 1023 { - log.Debugf("Infof found outbound call from an EVM actor on method 1-1023 method:%d, code:%s, height:%d", et.Msg.Method, parent.Action.FilecoinCodeCid.String(), height) - - if et.Msg.Method == builtin.MethodsEVM.GetBytecode { - // save the last bytecode trace to handle delegate calls - parent.LastByteCode = trace - } - - return nil - } - } - } - - } - - // we are adding trace to the traces so update the parent subtraces count as it was originally set to zero - if parent != nil { - parent.Subtraces++ - } - - *traces = append(*traces, trace) - - for i, call := range et.Subcalls { - err := buildTraces(traces, trace, append(addr, i), call, height, st) + // Set capacity to the length so each `append` below creates a new slice. Otherwise, we'll + // end up repeatedly mutating previous paths. + addr = addr[:len(addr):len(addr)] + for i := range recurseInto.Subcalls { + err := buildTraces(subEnv, append(addr, subEnv.subtraceCount), &recurseInto.Subcalls[i]) if err != nil { return err } } + trace.Subtraces = subEnv.subtraceCount + env.traces = subEnv.traces return nil } + +// buildTrace processes the passed execution trace and updates the environment, if necessary. +// +// On success, it returns a trace to add (or nil to skip) and the trace recurse into (or nil to skip). +func buildTrace(env *environment, addr []int, et *types.ExecutionTrace) (*ethtypes.EthTrace, *types.ExecutionTrace, error) { + // This function first assumes that the call is a "native" call, then handles all the "not + // native" cases. If we get any unexpected results in any of these special cases, we just + // keep the "native" interpretation and move on. + // + // 1. If we're invoking a contract (even if the caller is a native account/actor), we + // attempt to decode the params/return value as a contract invocation. + // 2. If we're calling the EAM and/or init actor, we try to treat the call as a CREATE. + // 3. Finally, if the caller is an EVM smart contract and it's calling a "private" (1-1023) + // method, we know something special is going on. We look for calls related to + // DELEGATECALL and drop everything else (everything else includes calls triggered by, + // e.g., EXTCODEHASH). + + // If we don't have sufficient funds, or we have a fatal error, or we have some + // other syscall error: skip the entire trace to mimic Ethereum (Ethereum records + // traces _after_ checking things like this). + // + // NOTE: The FFI currently folds all unknown syscall errors into "sys assertion + // failed" which is turned into SysErrFatal. + if len(addr) > 0 { + switch et.MsgRct.ExitCode { + case exitcode.SysErrInsufficientFunds, exitcode.SysErrFatal: + return nil, nil, nil + } + } + + // We may fail before we can even invoke the actor. In that case, we have no 100% reliable + // way of getting its address (e.g., due to reverts) so we're just going to drop the entire + // trace. This is OK (ish) because the call never really "happened". + if et.InvokedActor == nil { + return nil, nil, nil + } + + // Step 2: Decode as a contract invocation + // + // Normal EVM calls. We don't care if the caller/receiver are actually EVM actors, we only + // care if the call _looks_ like an EVM call. If we fail to decode it as an EVM call, we + // fallback on interpreting it as a native call. + if et.Msg.Method == builtin.MethodsEVM.InvokeContract { + return traceEVMCall(env, addr, et) + } + + // Step 3: Decode as a contract deployment + switch et.Msg.To { + // NOTE: this will only catch _direct_ calls to the init actor. Calls through the EAM will + // be caught and _skipped_ below in the next case. + case builtin.InitActorAddr: + switch et.Msg.Method { + case builtin.MethodsInit.Exec, builtin.MethodsInit.Exec4: + return traceNativeCreate(env, addr, et) + } + case builtin.EthereumAddressManagerActorAddr: + switch et.Msg.Method { + case builtin.MethodsEAM.Create, builtin.MethodsEAM.Create2, builtin.MethodsEAM.CreateExternal: + return traceEthCreate(env, addr, et) + } + } + + // Step 4: Handle DELEGATECALL + // + // EVM contracts cannot call methods in the range 1-1023, only the EVM itself can. So, if we + // see a call in this range, we know it's an implementation detail of the EVM and not an + // explicit call by the user. + // + // While the EVM calls several methods in this range (some we've already handled above with + // respect to the EAM), we only care about the ones relevant DELEGATECALL and can _ignore_ + // all the others. + if env.isEVM && et.Msg.Method > 0 && et.Msg.Method < 1024 { + return traceEVMPrivate(env, addr, et) + } + + return traceNativeCall(env, addr, et), et, nil +} + +// Build an EthTrace for a "call" with the given input & output. +func traceCall(env *environment, addr []int, et *types.ExecutionTrace, input, output ethtypes.EthBytes) *ethtypes.EthTrace { + to := traceToAddress(et.InvokedActor) + callType := "call" + if et.Msg.ReadOnly { + callType = "staticcall" + } + return ðtypes.EthTrace{ + Type: "call", + Action: ðtypes.EthCallTraceAction{ + CallType: callType, + From: env.caller, + To: to, + Gas: ethtypes.EthUint64(et.Msg.GasLimit), + Value: ethtypes.EthBigInt(et.Msg.Value), + Input: input, + }, + Result: ðtypes.EthCallTraceResult{ + GasUsed: ethtypes.EthUint64(et.SumGas().TotalGas), + Output: output, + }, + TraceAddress: addr, + Error: traceErrMsg(et), + } +} + +// Build an EthTrace for a "call", parsing the inputs & outputs as a "native" FVM call. +func traceNativeCall(env *environment, addr []int, et *types.ExecutionTrace) *ethtypes.EthTrace { + return traceCall(env, addr, et, + encodeFilecoinParamsAsABI(et.Msg.Method, et.Msg.ParamsCodec, et.Msg.Params), + encodeFilecoinReturnAsABI(et.MsgRct.ExitCode, et.MsgRct.ReturnCodec, et.MsgRct.Return), + ) +} + +// Build an EthTrace for a "call", parsing the inputs & outputs as an EVM call (falling back on +// treating it as a native call). +func traceEVMCall(env *environment, addr []int, et *types.ExecutionTrace) (*ethtypes.EthTrace, *types.ExecutionTrace, error) { + input, err := decodePayload(et.Msg.Params, et.Msg.ParamsCodec) + if err != nil { + log.Debugf("failed to decode contract invocation payload: %w", err) + return traceNativeCall(env, addr, et), et, nil + } + output, err := decodePayload(et.MsgRct.Return, et.MsgRct.ReturnCodec) + if err != nil { + log.Debugf("failed to decode contract invocation return: %w", err) + return traceNativeCall(env, addr, et), et, nil + } + return traceCall(env, addr, et, input, output), et, nil +} + +// Build an EthTrace for a native "create" operation. This should only be called with an +// ExecutionTrace is an Exec or Exec4 method invocation on the Init actor. +func traceNativeCreate(env *environment, addr []int, et *types.ExecutionTrace) (*ethtypes.EthTrace, *types.ExecutionTrace, error) { + if et.Msg.ReadOnly { + // "create" isn't valid in a staticcall, so we just skip this trace + // (couldn't have created an actor anyways). + // This mimic's the EVM: it doesn't trace CREATE calls when in + // read-only mode. + return nil, nil, nil + } + + subTrace := find(et.Subcalls, func(c *types.ExecutionTrace) *types.ExecutionTrace { + if c.Msg.Method == builtin.MethodConstructor { + return c + } + return nil + }) + if subTrace == nil { + // If we succeed in calling Exec/Exec4 but don't even try to construct + // something, we have a bug in our tracing logic or a mismatch between our + // tracing logic and the actors. + if et.MsgRct.ExitCode.IsSuccess() { + return nil, nil, xerrors.Errorf("successful Exec/Exec4 call failed to call a constructor") + } + // Otherwise, this can happen if creation fails early (bad params, + // out of gas, contract already exists, etc.). The EVM wouldn't + // trace such cases, so we don't either. + // + // NOTE: It's actually impossible to run out of gas before calling + // initcode in the EVM (without running out of gas in the calling + // contract), but this is an equivalent edge-case to InvokedActor + // being nil, so we treat it the same way and skip the entire + // operation. + return nil, nil, nil + } + + // Native actors that aren't the EAM can attempt to call Exec4, but such + // call should fail immediately without ever attempting to construct an + // actor. I'm catching this here because it likely means that there's a bug + // in our trace-conversion logic. + if et.Msg.Method == builtin.MethodsInit.Exec4 { + return nil, nil, xerrors.Errorf("direct call to Exec4 successfully called a constructor!") + } + + var output ethtypes.EthBytes + var createdAddr *ethtypes.EthAddress + if et.MsgRct.ExitCode.IsSuccess() { + // We're supposed to put the "installed bytecode" here. But this + // isn't an EVM actor, so we just put some invalid bytecode (this is + // the answer you'd get if you called EXTCODECOPY on a native + // non-account actor, anyways). + output = []byte{0xFE} + + // Extract the address of the created actor from the return value. + initReturn, err := decodeReturn[init12.ExecReturn](&et.MsgRct) + if err != nil { + return nil, nil, xerrors.Errorf("failed to decode init params after a successful Init.Exec call: %w", err) + } + actorId, err := address.IDFromAddress(initReturn.IDAddress) + if err != nil { + return nil, nil, xerrors.Errorf("failed to extract created actor ID from address: %w", err) + } + ethAddr := ethtypes.EthAddressFromActorID(abi.ActorID(actorId)) + createdAddr = ðAddr + } + + return ðtypes.EthTrace{ + Type: "create", + Action: ðtypes.EthCreateTraceAction{ + From: env.caller, + Gas: ethtypes.EthUint64(et.Msg.GasLimit), + Value: ethtypes.EthBigInt(et.Msg.Value), + // If we get here, this isn't a native EVM create. Those always go through + // the EAM. So we have no "real" initcode and must use the sentinel value + // for "invalid" initcode. + Init: []byte{0xFE}, + }, + Result: ðtypes.EthCreateTraceResult{ + GasUsed: ethtypes.EthUint64(et.SumGas().TotalGas), + Address: createdAddr, + Code: output, + }, + TraceAddress: addr, + Error: traceErrMsg(et), + }, subTrace, nil +} + +// Assert that these are all identical so we can simplify the below code and decode once. +var _ *eam12.Return = (*eam12.Return)((*eam12.CreateReturn)(nil)) +var _ *eam12.Return = (*eam12.Return)((*eam12.Create2Return)(nil)) +var _ *eam12.Return = (*eam12.Return)((*eam12.CreateExternalReturn)(nil)) + +// Decode the parameters and return value of an EVM smart contract creation through the EAM. This +// should only be called with an ExecutionTrace for a Create, Create2, or CreateExternal method +// invocation on the EAM. +func decodeCreateViaEAM(et *types.ExecutionTrace) (initcode []byte, addr *ethtypes.EthAddress, err error) { + switch et.Msg.Method { + case builtin.MethodsEAM.Create: + params, err := decodeParams[eam12.CreateParams](&et.Msg) + if err != nil { + return nil, nil, err + } + initcode = params.Initcode + case builtin.MethodsEAM.Create2: + params, err := decodeParams[eam12.Create2Params](&et.Msg) + if err != nil { + return nil, nil, err + } + initcode = params.Initcode + case builtin.MethodsEAM.CreateExternal: + input, err := decodePayload(et.Msg.Params, et.Msg.ParamsCodec) + if err != nil { + return nil, nil, err + } + initcode = input + default: + return nil, nil, xerrors.Errorf("unexpected CREATE method %d", et.Msg.Method) + } + ret, err := decodeReturn[eam12.CreateReturn](&et.MsgRct) + if err != nil { + return nil, (*ethtypes.EthAddress)(&ret.EthAddress), err + } + return initcode, (*ethtypes.EthAddress)(&ret.EthAddress), nil +} + +// Build an EthTrace for an EVM "create" operation. This should only be called with an +// ExecutionTrace for a Create, Create2, or CreateExternal method invocation on the EAM. +func traceEthCreate(env *environment, addr []int, et *types.ExecutionTrace) (*ethtypes.EthTrace, *types.ExecutionTrace, error) { + // Same as the Init actor case above, see the comment there. + if et.Msg.ReadOnly { + return nil, nil, nil + } + + // Look for a call to either a constructor or the EVM's resurrect method. + subTrace := find(et.Subcalls, func(et *types.ExecutionTrace) *types.ExecutionTrace { + if et.Msg.To == builtinactors.InitActorAddr { + return find(et.Subcalls, func(et *types.ExecutionTrace) *types.ExecutionTrace { + if et.Msg.Method == builtinactors.MethodConstructor { + return et + } + return nil + }) + } + if et.Msg.Method == builtin.MethodsEVM.Resurrect { + return et + } + return nil + }) + + // Same as the Init actor case above, see the comment there. + if subTrace == nil { + if et.MsgRct.ExitCode.IsSuccess() { + return nil, nil, xerrors.Errorf("successful Create/Create2 call failed to call a constructor") + } + return nil, nil, nil + } + + // Decode inputs & determine create type. + initcode, createdAddr, err := decodeCreateViaEAM(et) + if err != nil { + return nil, nil, xerrors.Errorf("EAM called with invalid params or returned an invalid result, but it still tried to construct the contract: %w", err) + } + + var output ethtypes.EthBytes + // Handle the output. + switch et.MsgRct.ExitCode { + case 0: // success + // We're _supposed_ to include the contracts bytecode here, but we + // can't do that reliably (e.g., if some part of the trace reverts). + // So we don't try and include a sentinel "impossible bytecode" + // value (the value specified by EIP-3541). + output = []byte{0xFE} + case 33: // Reverted, parse the revert message. + // If we managed to call the constructor, parse/return its revert message. If we + // fail, we just return no output. + output, _ = decodePayload(subTrace.MsgRct.Return, subTrace.MsgRct.ReturnCodec) + } + + return ðtypes.EthTrace{ + Type: "create", + Action: ðtypes.EthCreateTraceAction{ + From: env.caller, + Gas: ethtypes.EthUint64(et.Msg.GasLimit), + Value: ethtypes.EthBigInt(et.Msg.Value), + Init: initcode, + }, + Result: ðtypes.EthCreateTraceResult{ + GasUsed: ethtypes.EthUint64(et.SumGas().TotalGas), + Address: createdAddr, + Code: output, + }, + TraceAddress: addr, + Error: traceErrMsg(et), + }, subTrace, nil +} + +// Build an EthTrace for a "private" method invocation from the EVM. This should only be called with +// an ExecutionTrace from an EVM instance and on a method between 1 and 1023 inclusive. +func traceEVMPrivate(env *environment, addr []int, et *types.ExecutionTrace) (*ethtypes.EthTrace, *types.ExecutionTrace, error) { + // The EVM actor implements DELEGATECALL by: + // + // 1. Asking the callee for its bytecode by calling it on the GetBytecode method. + // 2. Recursively invoking the currently executing contract on the + // InvokeContractDelegate method. + // + // The code below "reconstructs" that delegate call by: + // + // 1. Remembering the last contract on which we called GetBytecode. + // 2. Treating the contract invoked in step 1 as the DELEGATECALL receiver. + // + // Note, however: GetBytecode will be called, e.g., if the user invokes the + // EXTCODECOPY instruction. It's not an error to see multiple GetBytecode calls + // before we see an InvokeContractDelegate. + switch et.Msg.Method { + case builtin.MethodsEVM.GetBytecode: + // NOTE: I'm not checking anything about the receiver here. The EVM won't + // DELEGATECALL any non-EVM actor, but there's no need to encode that fact + // here in case we decide to loosen this up in the future. + if et.MsgRct.ExitCode.IsSuccess() { + to := traceToAddress(et.InvokedActor) + env.lastByteCode = &to + } else { + env.lastByteCode = nil + } + return nil, nil, nil + case builtin.MethodsEVM.InvokeContractDelegate: + // NOTE: We return errors in all the failure cases below instead of trying + // to continue because the caller is an EVM actor. If something goes wrong + // here, there's a bug in our EVM implementation. + + // Handle delegate calls + // + // 1) Look for trace from an EVM actor to itself on InvokeContractDelegate, + // method 6. + // 2) Check that the previous trace calls another actor on method 3 + // (GetByteCode) and they are at the same level (same parent) + // 3) Treat this as a delegate call to actor A. + if env.lastByteCode == nil { + return nil, nil, xerrors.Errorf("unknown bytecode for delegate call") + } + + if to := traceToAddress(et.InvokedActor); env.caller != to { + return nil, nil, xerrors.Errorf("delegate-call not from & to self: %s != %s", env.caller, to) + } + + dp, err := decodeParams[evm12.DelegateCallParams](&et.Msg) + if err != nil { + return nil, nil, xerrors.Errorf("failed to decode delegate-call params: %w", err) + } + + output, err := decodePayload(et.MsgRct.Return, et.MsgRct.ReturnCodec) + if err != nil { + return nil, nil, xerrors.Errorf("failed to decode delegate-call return: %w", err) + } + + return ðtypes.EthTrace{ + Type: "call", + Action: ðtypes.EthCallTraceAction{ + CallType: "delegatecall", + From: env.caller, + To: *env.lastByteCode, + Gas: ethtypes.EthUint64(et.Msg.GasLimit), + Value: ethtypes.EthBigInt(et.Msg.Value), + Input: dp.Input, + }, + Result: ðtypes.EthCallTraceResult{ + GasUsed: ethtypes.EthUint64(et.SumGas().TotalGas), + Output: output, + }, + TraceAddress: addr, + Error: traceErrMsg(et), + }, et, nil + } + // We drop all other "private" calls from FEVM. We _forbid_ explicit calls between 0 and + // 1024 (exclusive), so any calls in this range must be implementation details. + return nil, nil, nil +} diff --git a/node/impl/full/eth_utils.go b/node/impl/full/eth_utils.go index 90e6d054a..6186c2645 100644 --- a/node/impl/full/eth_utils.go +++ b/node/impl/full/eth_utils.go @@ -382,34 +382,35 @@ func parseEthRevert(ret []byte) string { // 3. Otherwise, we fall back to returning a masked ID Ethereum address. If the supplied address is an f0 address, we // use that ID to form the masked ID address. // 4. Otherwise, we fetch the actor's ID from the state tree and form the masked ID with it. +// +// If the actor doesn't exist in the state-tree but we have its ID, we use a masked ID address. It could have been deleted. func lookupEthAddress(addr address.Address, st *state.StateTree) (ethtypes.EthAddress, error) { - // BLOCK A: We are trying to get an actual Ethereum address from an f410 address. // Attempt to convert directly, if it's an f4 address. ethAddr, err := ethtypes.EthAddressFromFilecoinAddress(addr) if err == nil && !ethAddr.IsMaskedID() { return ethAddr, nil } - // Lookup on the target actor and try to get an f410 address. - if actor, err := st.GetActor(addr); err != nil { - return ethtypes.EthAddress{}, err - } else if actor.Address != nil { - if ethAddr, err := ethtypes.EthAddressFromFilecoinAddress(*actor.Address); err == nil && !ethAddr.IsMaskedID() { - return ethAddr, nil - } - } - - // BLOCK B: We gave up on getting an actual Ethereum address and are falling back to a Masked ID address. - // Check if we already have an ID addr, and use it if possible. - if err == nil && ethAddr.IsMaskedID() { - return ethAddr, nil - } - // Otherwise, resolve the ID addr. idAddr, err := st.LookupID(addr) if err != nil { return ethtypes.EthAddress{}, err } + + // Lookup on the target actor and try to get an f410 address. + if actor, err := st.GetActor(idAddr); errors.Is(err, types.ErrActorNotFound) { + // Not found -> use a masked ID address + } else if err != nil { + // Any other error -> fail. + return ethtypes.EthAddress{}, err + } else if actor.Address == nil { + // No delegated address -> use masked ID address. + } else if ethAddr, err := ethtypes.EthAddressFromFilecoinAddress(*actor.Address); err == nil && !ethAddr.IsMaskedID() { + // Conversable into an eth address, use it. + return ethAddr, nil + } + + // Otherwise, use the masked address. return ethtypes.EthAddressFromFilecoinAddress(idAddr) } From 0634dfce07cbd3046e1c6737ab87448a52de0c95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 22 Feb 2024 11:39:17 +0100 Subject: [PATCH 213/394] Fix lint --- provider/lpproof/treed_build_test.go | 4 +++- storage/paths/remote.go | 11 ++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/provider/lpproof/treed_build_test.go b/provider/lpproof/treed_build_test.go index c97184fb2..f9bbde20a 100644 --- a/provider/lpproof/treed_build_test.go +++ b/provider/lpproof/treed_build_test.go @@ -315,7 +315,9 @@ func Test32G(t *testing.T) { // dump tree.dat datFile, err := os.Open(tempFile) require.NoError(t, err) - defer datFile.Close() + defer func() { + require.NoError(t, datFile.Close()) + }() actualD := hexPrint32LDedup(bufio.NewReaderSize(datFile, 1<<20)) fmt.Println(actualD) diff --git a/storage/paths/remote.go b/storage/paths/remote.go index afa589683..882c98c4a 100644 --- a/storage/paths/remote.go +++ b/storage/paths/remote.go @@ -430,12 +430,13 @@ func (r *Remote) FsStat(ctx context.Context, id storiface.ID) (fsutil.FsStat, er } _ = resp.Body.Close() return out, nil // Successfully decoded, return the result - } else { - b, _ := io.ReadAll(resp.Body) // Best-effort read the body for logging - log.Warnw("request to endpoint failed", "url", rl.String(), "statusCode", resp.StatusCode, "response", string(b)) - _ = resp.Body.Close() - // Continue to try the next URL, don't return here as we want to try all URLs } + + // non-200 status code + b, _ := io.ReadAll(resp.Body) // Best-effort read the body for logging + log.Warnw("request to endpoint failed", "url", rl.String(), "statusCode", resp.StatusCode, "response", string(b)) + _ = resp.Body.Close() + // Continue to try the next URL, don't return here as we want to try all URLs } return fsutil.FsStat{}, xerrors.Errorf("all endpoints failed for remote storage %s", id) From 5ab75d35f979eb9a3dbb8fa0cb735f502caf1563 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 22 Feb 2024 14:00:47 +0100 Subject: [PATCH 214/394] fix mac build --- go.mod | 4 ++++ go.sum | 4 ++-- storage/paths/db_index.go | 26 -------------------------- 3 files changed, 6 insertions(+), 28 deletions(-) diff --git a/go.mod b/go.mod index 8bc99cefb..0b580e68b 100644 --- a/go.mod +++ b/go.mod @@ -333,6 +333,10 @@ require ( lukechampine.com/blake3 v1.2.1 // indirect ) +// https://github.com/magik6k/reflink/commit/cff5a40f3eeca17f44fc95a57ff3878e5ac761dc +// https://github.com/KarpelesLab/reflink/pull/2 +replace github.com/KarpelesLab/reflink => github.com/magik6k/reflink v1.0.2-patch1 + replace github.com/filecoin-project/filecoin-ffi => ./extern/filecoin-ffi replace github.com/filecoin-project/test-vectors => ./extern/test-vectors diff --git a/go.sum b/go.sum index 26cd5e8a3..9f0039a7d 100644 --- a/go.sum +++ b/go.sum @@ -57,8 +57,6 @@ github.com/GeertJohan/go.rice v1.0.3 h1:k5viR+xGtIhF61125vCE1cmJ5957RQGXG6dmbaWZ github.com/GeertJohan/go.rice v1.0.3/go.mod h1:XVdrU4pW00M4ikZed5q56tPf1v2KwnIKeIdc9CBYNt4= github.com/Gurpartap/async v0.0.0-20180927173644-4f7f499dd9ee h1:8doiS7ib3zi6/K172oDhSKU0dJ/miJramo9NITOMyZQ= github.com/Gurpartap/async v0.0.0-20180927173644-4f7f499dd9ee/go.mod h1:W0GbEAA4uFNYOGG2cJpmFJ04E6SD1NLELPYZB57/7AY= -github.com/KarpelesLab/reflink v1.0.1 h1:d+tdjliwOCqvub9bl0Y02GxahWkNqejNb3TZTTUcQWA= -github.com/KarpelesLab/reflink v1.0.1/go.mod h1:WGkTOKNjd1FsJKBw3mu4JvrPEDJyJJ+JPtxBkbPoCok= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/Kubuxu/go-os-helper v0.0.1/go.mod h1:N8B+I7vPCT80IcP58r50u4+gEEcsZETFUpAzWW2ep1Y= github.com/Kubuxu/imtui v0.0.0-20210401140320-41663d68d0fa h1:1PPxEyGdIGVkX/kqMvLJ95a1dGS1Sz7tpNEgehEYYt0= @@ -1150,6 +1148,8 @@ github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0Q github.com/magefile/mage v1.9.0 h1:t3AU2wNwehMCW97vuqQLtw6puppWXHO+O2MHo5a50XE= github.com/magefile/mage v1.9.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/magik6k/reflink v1.0.2-patch1 h1:NXSgQugcESI8Z/jBtuAI83YsZuRauY9i9WOyOnJ7Vns= +github.com/magik6k/reflink v1.0.2-patch1/go.mod h1:WGkTOKNjd1FsJKBw3mu4JvrPEDJyJJ+JPtxBkbPoCok= github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= diff --git a/storage/paths/db_index.go b/storage/paths/db_index.go index a867da3f9..063415712 100644 --- a/storage/paths/db_index.go +++ b/storage/paths/db_index.go @@ -724,32 +724,6 @@ func (dbi *DBIndex) StorageBestAlloc(ctx context.Context, allocate storiface.Sec return result, nil } -/* -2024-02-17T01:24:10.243+0100 WARN stores paths/remote.go:258 acquireFromRemote encountered errors when fetching sector from remote {"errors": "1 error occurred:\n\t* fetch error http://10.99.16.4:3456/remote/cache/s-t02620-1 (storage 0bd90137-1219-4fb6-82d4-138a72c692b7) -> /data/3/store/cache/fetching/s-t02620-1: do request: Get \"http://10.99.16.4:3456/remote/cache/s-t02620-1\": dial tcp 10.99.16.4:3456: connect: connection refused\n\n"} -2024-02-17T01:24:10.248+0100 INFO stores paths/remote.go:359 Delete http://10.99.16.4:12300/remote/cache/s-t02620-1?keep=0ff4da63-7e0d-477e-a2ee-814c9fd7ef71 - -==> /var/log/lotus/provider1.log <== -2024-02-17T01:25:32.820+0100 INFO harmonytask harmonytask/task_type_handler.go:121 Beginning work on Task {"id": 7815, "from": "poller", "name": "WinPost"} - -==> /var/log/lotus/provider4.log <== -2024-02-17T01:25:45.058+0100 ERROR harmonytask harmonytask/task_type_handler.go:131 Recovered from a serious error while processing SDRTrees task 7702: todo Stack: goroutine 334 [running]: -github.com/filecoin-project/lotus/lib/harmony/harmonytask.(*taskTypeHandler).considerWork.func1.1() - /home/magik6k/github.com/filecoin-project/go-lotus/lib/harmony/harmonytask/task_type_handler.go:130 +0xc5 -panic({0x2210100?, 0x4557680?}) - /usr/lib/go/src/runtime/panic.go:770 +0x132 -github.com/filecoin-project/lotus/provider/lpffi.(*SealCalls).makePhase1Out(0x10?, {{0xc0009ba120?, 0xf?}}, 0x8) - /home/magik6k/github.com/filecoin-project/go-lotus/provider/lpffi/sdr_funcs.go:272 +0x445 -github.com/filecoin-project/lotus/provider/lpffi.(*SealCalls).TreeRC(0xc000884118, {0x457c9b8, 0xa4c7d40}, {{0xa3c, 0x1}, 0x8}, {{0xc0009ba120?, 0x800000000?}}) - /home/magik6k/github.com/filecoin-project/go-lotus/provider/lpffi/sdr_funcs.go:133 +0x95 -github.com/filecoin-project/lotus/provider/lpseal.(*TreesTask).Do(0xc00017c5e0, 0x1e16, 0x6?) - /home/magik6k/github.com/filecoin-project/go-lotus/provider/lpseal/task_trees.go:149 +0x5ca -github.com/filecoin-project/lotus/lib/harmony/harmonytask.(*taskTypeHandler).considerWork.func1() - /home/magik6k/github.com/filecoin-project/go-lotus/lib/harmony/harmonytask/task_type_handler.go:147 +0x2a6 -created by github.com/filecoin-project/lotus/lib/harmony/harmonytask.(*taskTypeHandler).considerWork in goroutine 230 - /home/magik6k/github.com/filecoin-project/go-lotus/lib/harmony/harmonytask/task_type_handler.go:120 +0x465 - -*/ - // timeout after which we consider a lock to be stale const LockTimeOut = 300 * time.Second From 3a87b3e9e54cf042236b0144898425dca6e974af Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Fri, 23 Feb 2024 05:33:06 +1100 Subject: [PATCH 215/394] Add systemd memory note on install and in config (#11641) * Add systemd memory note on install * Add memory usage note to system config for daemon * Remove limits by default --- Makefile | 8 ++++++-- scripts/lotus-daemon.service | 10 +++++++--- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index 841c715e1..cf7d5eecc 100644 --- a/Makefile +++ b/Makefile @@ -246,7 +246,9 @@ install-daemon-service: install-daemon install -C -m 0644 ./scripts/lotus-daemon.service /etc/systemd/system/lotus-daemon.service systemctl daemon-reload @echo - @echo "lotus-daemon service installed. Don't forget to run 'sudo systemctl start lotus-daemon' to start it and 'sudo systemctl enable lotus-daemon' for it to be enabled on startup." + @echo "lotus-daemon service installed." + @echo "To start the service, run: 'sudo systemctl start lotus-daemon'" + @echo "To enable the service on startup, run: 'sudo systemctl enable lotus-daemon'" install-miner-service: install-miner install-daemon-service mkdir -p /etc/systemd/system @@ -254,7 +256,9 @@ install-miner-service: install-miner install-daemon-service install -C -m 0644 ./scripts/lotus-miner.service /etc/systemd/system/lotus-miner.service systemctl daemon-reload @echo - @echo "lotus-miner service installed. Don't forget to run 'sudo systemctl start lotus-miner' to start it and 'sudo systemctl enable lotus-miner' for it to be enabled on startup." + @echo "lotus-miner service installed." + @echo "To start the service, run: 'sudo systemctl start lotus-miner'" + @echo "To enable the service on startup, run: 'sudo systemctl enable lotus-miner'" install-provider-service: install-provider install-daemon-service mkdir -p /etc/systemd/system diff --git a/scripts/lotus-daemon.service b/scripts/lotus-daemon.service index af35a027c..3f8ea014f 100644 --- a/scripts/lotus-daemon.service +++ b/scripts/lotus-daemon.service @@ -10,9 +10,13 @@ ExecStart=/usr/local/bin/lotus daemon Restart=always RestartSec=10 -MemoryAccounting=true -MemoryHigh=8G -MemoryMax=10G +# If running a node in a shared, memory constrained environment, enable MemoryAccounting to put +# pressure on allocations. This will slow the process down but keep it approximately within the +# limit specified. +#MemoryAccounting=true +#MemoryHigh=64G +#MemoryMax=96G + LimitNOFILE=8192:10240 [Install] From b691adc4874e5e28353f036c077c08ef00ec3b2b Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Fri, 23 Feb 2024 05:34:49 +1100 Subject: [PATCH 216/394] Add `unittests` make target (#11628) --- Makefile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Makefile b/Makefile index cf7d5eecc..1e6ff93aa 100644 --- a/Makefile +++ b/Makefile @@ -307,6 +307,10 @@ install-completions: install -C ./scripts/bash-completion/lotus /usr/share/bash-completion/completions/lotus install -C ./scripts/zsh-completion/lotus /usr/local/share/zsh/site-functions/_lotus +unittests: + @$(GOCC) test $(shell go list ./... | grep -v /lotus/itests) +.PHONY: unittests + clean: rm -rf $(CLEAN) $(BINS) -$(MAKE) -C $(FFI_PATH) clean From a0fb6eccf3644e0a62955f23d32cd911775518b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sat, 24 Feb 2024 11:11:57 +0100 Subject: [PATCH 217/394] feat: lotus-provider: storage find command --- api/api_lp.go | 14 +- api/proxy_gen.go | 13 + build/openrpc/full.json | 478 ++++++++++---------- build/openrpc/gateway.json | 186 ++++---- build/openrpc/miner.json | 260 +++++------ build/openrpc/worker.json | 74 +-- cmd/lotus-provider/storage.go | 115 ++++- documentation/en/api-v0-methods-provider.md | 44 ++ 8 files changed, 678 insertions(+), 506 deletions(-) diff --git a/api/api_lp.go b/api/api_lp.go index fbdd5a803..15e219fa5 100644 --- a/api/api_lp.go +++ b/api/api_lp.go @@ -6,6 +6,7 @@ import ( "net/url" "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/lotus/storage/sealer/fsutil" "github.com/filecoin-project/lotus/storage/sealer/storiface" @@ -16,12 +17,13 @@ type LotusProvider interface { AllocatePieceToSector(ctx context.Context, maddr address.Address, piece PieceDealInfo, rawSize int64, source url.URL, header http.Header) (SectorOffset, error) //perm:write - StorageAddLocal(ctx context.Context, path string) error //perm:admin - StorageDetachLocal(ctx context.Context, path string) error //perm:admin - StorageList(ctx context.Context) (map[storiface.ID][]storiface.Decl, error) //perm:admin - StorageLocal(ctx context.Context) (map[storiface.ID]string, error) //perm:admin - StorageStat(ctx context.Context, id storiface.ID) (fsutil.FsStat, error) //perm:admin - StorageInfo(context.Context, storiface.ID) (storiface.StorageInfo, error) //perm:admin + StorageAddLocal(ctx context.Context, path string) error //perm:admin + StorageDetachLocal(ctx context.Context, path string) error //perm:admin + StorageList(ctx context.Context) (map[storiface.ID][]storiface.Decl, error) //perm:admin + StorageLocal(ctx context.Context) (map[storiface.ID]string, error) //perm:admin + StorageStat(ctx context.Context, id storiface.ID) (fsutil.FsStat, error) //perm:admin + StorageInfo(context.Context, storiface.ID) (storiface.StorageInfo, error) //perm:admin + StorageFindSector(ctx context.Context, sector abi.SectorID, ft storiface.SectorFileType, ssize abi.SectorSize, allowFetch bool) ([]storiface.SectorStorageInfo, error) //perm:admin // Trigger shutdown Shutdown(context.Context) error //perm:admin diff --git a/api/proxy_gen.go b/api/proxy_gen.go index 4ac8ed6c2..97e441bbe 100644 --- a/api/proxy_gen.go +++ b/api/proxy_gen.go @@ -842,6 +842,8 @@ type LotusProviderMethods struct { StorageDetachLocal func(p0 context.Context, p1 string) error `perm:"admin"` + StorageFindSector func(p0 context.Context, p1 abi.SectorID, p2 storiface.SectorFileType, p3 abi.SectorSize, p4 bool) ([]storiface.SectorStorageInfo, error) `perm:"admin"` + StorageInfo func(p0 context.Context, p1 storiface.ID) (storiface.StorageInfo, error) `perm:"admin"` StorageList func(p0 context.Context) (map[storiface.ID][]storiface.Decl, error) `perm:"admin"` @@ -5261,6 +5263,17 @@ func (s *LotusProviderStub) StorageDetachLocal(p0 context.Context, p1 string) er return ErrNotSupported } +func (s *LotusProviderStruct) StorageFindSector(p0 context.Context, p1 abi.SectorID, p2 storiface.SectorFileType, p3 abi.SectorSize, p4 bool) ([]storiface.SectorStorageInfo, error) { + if s.Internal.StorageFindSector == nil { + return *new([]storiface.SectorStorageInfo), ErrNotSupported + } + return s.Internal.StorageFindSector(p0, p1, p2, p3, p4) +} + +func (s *LotusProviderStub) StorageFindSector(p0 context.Context, p1 abi.SectorID, p2 storiface.SectorFileType, p3 abi.SectorSize, p4 bool) ([]storiface.SectorStorageInfo, error) { + return *new([]storiface.SectorStorageInfo), ErrNotSupported +} + func (s *LotusProviderStruct) StorageInfo(p0 context.Context, p1 storiface.ID) (storiface.StorageInfo, error) { if s.Internal.StorageInfo == nil { return *new(storiface.StorageInfo), ErrNotSupported diff --git a/build/openrpc/full.json b/build/openrpc/full.json index d641d251e..8b4eb15b9 100644 --- a/build/openrpc/full.json +++ b/build/openrpc/full.json @@ -37,7 +37,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1480" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1482" } }, { @@ -60,7 +60,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1491" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1493" } }, { @@ -103,7 +103,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1502" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1504" } }, { @@ -214,7 +214,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1524" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1526" } }, { @@ -454,7 +454,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1535" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1537" } }, { @@ -685,7 +685,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1546" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1548" } }, { @@ -784,7 +784,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1557" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1559" } }, { @@ -816,7 +816,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1568" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1570" } }, { @@ -922,7 +922,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1579" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1581" } }, { @@ -1019,7 +1019,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1590" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1592" } }, { @@ -1078,7 +1078,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1601" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1603" } }, { @@ -1171,7 +1171,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1612" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1614" } }, { @@ -1255,7 +1255,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1623" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1625" } }, { @@ -1355,7 +1355,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1634" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1636" } }, { @@ -1411,7 +1411,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1645" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1647" } }, { @@ -1484,7 +1484,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1656" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1658" } }, { @@ -1557,7 +1557,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1667" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1669" } }, { @@ -1604,7 +1604,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1678" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1680" } }, { @@ -1636,7 +1636,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1689" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1691" } }, { @@ -1691,7 +1691,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1700" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1702" } }, { @@ -1743,7 +1743,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1722" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1724" } }, { @@ -1780,7 +1780,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1733" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1735" } }, { @@ -1827,7 +1827,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1744" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1746" } }, { @@ -1874,7 +1874,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1755" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1757" } }, { @@ -1954,7 +1954,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1766" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1768" } }, { @@ -2006,7 +2006,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1777" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1779" } }, { @@ -2065,7 +2065,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1788" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1790" } }, { @@ -2136,7 +2136,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1799" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1801" } }, { @@ -2177,7 +2177,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1810" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1812" } }, { @@ -2245,7 +2245,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1832" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1834" } }, { @@ -2306,7 +2306,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1843" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1845" } }, { @@ -2413,7 +2413,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1854" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1856" } }, { @@ -2569,7 +2569,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1865" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1867" } }, { @@ -2635,7 +2635,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1876" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1878" } }, { @@ -2976,7 +2976,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1887" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1889" } }, { @@ -3021,7 +3021,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1898" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1900" } }, { @@ -3068,7 +3068,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1931" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1933" } }, { @@ -3139,7 +3139,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1942" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1944" } }, { @@ -3282,7 +3282,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1953" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1955" } }, { @@ -3612,7 +3612,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1964" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1966" } }, { @@ -3680,7 +3680,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1975" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1977" } }, { @@ -3914,7 +3914,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1986" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1988" } }, { @@ -4077,7 +4077,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1997" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1999" } }, { @@ -4160,7 +4160,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2008" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2010" } }, { @@ -4201,7 +4201,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2019" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2021" } }, { @@ -4272,7 +4272,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2030" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2032" } }, { @@ -4416,7 +4416,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2041" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2043" } }, { @@ -4456,7 +4456,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2052" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2054" } }, { @@ -4497,7 +4497,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2063" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2065" } }, { @@ -4622,7 +4622,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2074" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2076" } }, { @@ -4747,7 +4747,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2085" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2087" } }, { @@ -4786,7 +4786,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2096" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2098" } }, { @@ -4833,7 +4833,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2107" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2109" } }, { @@ -4888,7 +4888,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2118" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2120" } }, { @@ -4917,7 +4917,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2129" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2131" } }, { @@ -5054,7 +5054,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2140" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2142" } }, { @@ -5083,7 +5083,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2151" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2153" } }, { @@ -5137,7 +5137,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2162" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2164" } }, { @@ -5228,7 +5228,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2173" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2175" } }, { @@ -5256,7 +5256,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2184" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2186" } }, { @@ -5346,7 +5346,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2195" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2197" } }, { @@ -5602,7 +5602,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2206" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2208" } }, { @@ -5847,7 +5847,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2217" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2219" } }, { @@ -5903,7 +5903,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2228" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2230" } }, { @@ -5950,7 +5950,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2239" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2241" } }, { @@ -6048,7 +6048,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2250" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2252" } }, { @@ -6114,7 +6114,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2261" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2263" } }, { @@ -6180,7 +6180,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2272" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2274" } }, { @@ -6289,7 +6289,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2283" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2285" } }, { @@ -6347,7 +6347,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2294" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2296" } }, { @@ -6469,7 +6469,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2305" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2307" } }, { @@ -6673,7 +6673,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2316" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2318" } }, { @@ -6868,7 +6868,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2327" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2329" } }, { @@ -7055,7 +7055,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2338" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2340" } }, { @@ -7259,7 +7259,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2349" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2351" } }, { @@ -7350,7 +7350,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2360" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2362" } }, { @@ -7408,7 +7408,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2371" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2373" } }, { @@ -7666,7 +7666,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2382" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2384" } }, { @@ -7941,7 +7941,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2393" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2395" } }, { @@ -7969,7 +7969,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2404" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2406" } }, { @@ -8007,7 +8007,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2415" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2417" } }, { @@ -8115,7 +8115,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2426" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2428" } }, { @@ -8153,7 +8153,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2437" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2439" } }, { @@ -8182,7 +8182,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2448" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2450" } }, { @@ -8245,7 +8245,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2459" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2461" } }, { @@ -8308,7 +8308,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2470" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2472" } }, { @@ -8353,7 +8353,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2481" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2483" } }, { @@ -8475,7 +8475,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2492" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2494" } }, { @@ -8630,7 +8630,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2503" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2505" } }, { @@ -8684,7 +8684,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2514" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2516" } }, { @@ -8738,7 +8738,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2525" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2527" } }, { @@ -8793,7 +8793,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2536" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2538" } }, { @@ -8936,7 +8936,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2547" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2549" } }, { @@ -9063,7 +9063,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2558" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2560" } }, { @@ -9165,7 +9165,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2569" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2571" } }, { @@ -9388,7 +9388,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2580" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2582" } }, { @@ -9468,7 +9468,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2591" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2593" } }, { @@ -9513,7 +9513,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2602" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2604" } }, { @@ -9569,7 +9569,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2613" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2615" } }, { @@ -9649,7 +9649,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2624" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2626" } }, { @@ -9729,7 +9729,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2635" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2637" } }, { @@ -10214,7 +10214,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2646" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2648" } }, { @@ -10408,7 +10408,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2657" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2659" } }, { @@ -10563,7 +10563,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2668" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2670" } }, { @@ -10812,7 +10812,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2679" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2681" } }, { @@ -10967,7 +10967,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2690" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2692" } }, { @@ -11144,7 +11144,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2701" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2703" } }, { @@ -11242,7 +11242,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2712" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2714" } }, { @@ -11407,7 +11407,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2723" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2725" } }, { @@ -11446,7 +11446,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2734" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2736" } }, { @@ -11511,7 +11511,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2745" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2747" } }, { @@ -11557,7 +11557,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2756" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2758" } }, { @@ -11707,7 +11707,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2767" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2769" } }, { @@ -11844,7 +11844,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2778" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2780" } }, { @@ -12075,7 +12075,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2789" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2791" } }, { @@ -12212,7 +12212,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2800" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2802" } }, { @@ -12377,7 +12377,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2811" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2813" } }, { @@ -12454,7 +12454,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2822" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2824" } }, { @@ -12649,7 +12649,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2844" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2846" } }, { @@ -12828,7 +12828,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2855" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2857" } }, { @@ -12990,7 +12990,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2866" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2868" } }, { @@ -13138,7 +13138,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2877" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2879" } }, { @@ -13366,7 +13366,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2888" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2890" } }, { @@ -13514,7 +13514,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2899" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2901" } }, { @@ -13726,7 +13726,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2910" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2912" } }, { @@ -13932,7 +13932,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2921" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2923" } }, { @@ -14000,7 +14000,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2932" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2934" } }, { @@ -14117,7 +14117,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2943" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2945" } }, { @@ -14208,7 +14208,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2954" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2956" } }, { @@ -14294,7 +14294,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2965" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2967" } }, { @@ -14489,7 +14489,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2976" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2978" } }, { @@ -14651,7 +14651,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2987" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2989" } }, { @@ -14847,7 +14847,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2998" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3000" } }, { @@ -15027,7 +15027,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3009" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3011" } }, { @@ -15190,7 +15190,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3020" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3022" } }, { @@ -15217,7 +15217,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3031" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3033" } }, { @@ -15244,7 +15244,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3042" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3044" } }, { @@ -15343,7 +15343,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3053" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3055" } }, { @@ -15389,7 +15389,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3064" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3066" } }, { @@ -15489,7 +15489,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3075" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3077" } }, { @@ -15605,7 +15605,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3086" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3088" } }, { @@ -15653,7 +15653,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3097" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3099" } }, { @@ -15745,7 +15745,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3108" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3110" } }, { @@ -15860,7 +15860,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3119" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3121" } }, { @@ -15908,7 +15908,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3130" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3132" } }, { @@ -15945,7 +15945,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3141" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3143" } }, { @@ -16217,7 +16217,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3152" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3154" } }, { @@ -16265,7 +16265,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3163" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3165" } }, { @@ -16323,7 +16323,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3174" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3176" } }, { @@ -16528,7 +16528,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3185" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3187" } }, { @@ -16731,7 +16731,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3196" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3198" } }, { @@ -16900,7 +16900,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3207" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3209" } }, { @@ -17104,7 +17104,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3218" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3220" } }, { @@ -17271,7 +17271,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3229" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3231" } }, { @@ -17478,7 +17478,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3240" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3242" } }, { @@ -17546,7 +17546,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3251" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3253" } }, { @@ -17598,7 +17598,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3262" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3264" } }, { @@ -17647,7 +17647,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3273" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3275" } }, { @@ -17738,7 +17738,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3284" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3286" } }, { @@ -18244,7 +18244,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3295" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3297" } }, { @@ -18350,7 +18350,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3306" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3308" } }, { @@ -18402,7 +18402,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3317" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3319" } }, { @@ -18954,7 +18954,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3328" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3330" } }, { @@ -19068,7 +19068,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3339" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3341" } }, { @@ -19165,7 +19165,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3350" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3352" } }, { @@ -19265,7 +19265,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3361" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3363" } }, { @@ -19353,7 +19353,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3372" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3374" } }, { @@ -19453,7 +19453,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3383" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3385" } }, { @@ -19578,7 +19578,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3394" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3396" } }, { @@ -19687,7 +19687,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3405" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3407" } }, { @@ -19790,7 +19790,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3416" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3418" } }, { @@ -19851,7 +19851,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3427" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3429" } }, { @@ -19981,7 +19981,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3438" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3440" } }, { @@ -20088,7 +20088,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3449" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3451" } }, { @@ -20287,7 +20287,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3460" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3462" } }, { @@ -20364,7 +20364,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3471" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3473" } }, { @@ -20441,7 +20441,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3482" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3484" } }, { @@ -20550,7 +20550,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3493" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3495" } }, { @@ -20659,7 +20659,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3504" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3506" } }, { @@ -20720,7 +20720,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3515" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3517" } }, { @@ -20830,7 +20830,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3526" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3528" } }, { @@ -20891,7 +20891,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3537" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3539" } }, { @@ -20959,7 +20959,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3548" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3550" } }, { @@ -21027,7 +21027,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3559" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3561" } }, { @@ -21108,7 +21108,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3570" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3572" } }, { @@ -21262,7 +21262,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3581" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3583" } }, { @@ -21334,7 +21334,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3592" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3594" } }, { @@ -21498,7 +21498,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3603" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3605" } }, { @@ -21663,7 +21663,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3614" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3616" } }, { @@ -21733,7 +21733,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3625" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3627" } }, { @@ -21801,7 +21801,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3636" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3638" } }, { @@ -21894,7 +21894,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3647" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3649" } }, { @@ -21965,7 +21965,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3658" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3660" } }, { @@ -22166,7 +22166,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3669" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3671" } }, { @@ -22298,7 +22298,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3680" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3682" } }, { @@ -22435,7 +22435,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3691" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3693" } }, { @@ -22546,7 +22546,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3702" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3704" } }, { @@ -22678,7 +22678,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3713" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3715" } }, { @@ -22809,7 +22809,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3724" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3726" } }, { @@ -22880,7 +22880,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3735" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3737" } }, { @@ -22964,7 +22964,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3746" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3748" } }, { @@ -23050,7 +23050,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3757" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3759" } }, { @@ -23233,7 +23233,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3768" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3770" } }, { @@ -23260,7 +23260,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3779" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3781" } }, { @@ -23313,7 +23313,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3790" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3792" } }, { @@ -23401,7 +23401,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3801" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3803" } }, { @@ -23852,7 +23852,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3812" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3814" } }, { @@ -24019,7 +24019,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3823" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3825" } }, { @@ -24117,7 +24117,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3834" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3836" } }, { @@ -24290,7 +24290,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3845" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3847" } }, { @@ -24388,7 +24388,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3856" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3858" } }, { @@ -24539,7 +24539,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3867" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3869" } }, { @@ -24624,7 +24624,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3878" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3880" } }, { @@ -24692,7 +24692,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3889" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3891" } }, { @@ -24744,7 +24744,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3900" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3902" } }, { @@ -24812,7 +24812,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3911" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3913" } }, { @@ -24973,7 +24973,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3922" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3924" } }, { @@ -25020,7 +25020,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3933" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3935" } }, { @@ -25067,7 +25067,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3944" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3946" } }, { @@ -25110,7 +25110,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3966" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3968" } }, { @@ -25206,7 +25206,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3977" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3979" } }, { @@ -25472,7 +25472,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3988" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3990" } }, { @@ -25495,7 +25495,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3999" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4001" } }, { @@ -25538,7 +25538,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4010" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4012" } }, { @@ -25589,7 +25589,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4021" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4023" } }, { @@ -25634,7 +25634,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4032" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4034" } }, { @@ -25662,7 +25662,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4043" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4045" } }, { @@ -25702,7 +25702,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4054" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4056" } }, { @@ -25761,7 +25761,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4065" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4067" } }, { @@ -25805,7 +25805,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4076" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4078" } }, { @@ -25864,7 +25864,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4087" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4089" } }, { @@ -25901,7 +25901,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4098" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4100" } }, { @@ -25945,7 +25945,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4109" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4111" } }, { @@ -25985,7 +25985,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4120" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4122" } }, { @@ -26060,7 +26060,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4131" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4133" } }, { @@ -26268,7 +26268,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4142" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4144" } }, { @@ -26312,7 +26312,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4153" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4155" } }, { @@ -26402,7 +26402,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4164" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4166" } }, { @@ -26429,7 +26429,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4175" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4177" } } ] diff --git a/build/openrpc/gateway.json b/build/openrpc/gateway.json index 288da2d21..3bfa391dc 100644 --- a/build/openrpc/gateway.json +++ b/build/openrpc/gateway.json @@ -242,7 +242,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4186" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4188" } }, { @@ -473,7 +473,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4197" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4199" } }, { @@ -505,7 +505,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4208" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4210" } }, { @@ -611,7 +611,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4219" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4221" } }, { @@ -704,7 +704,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4230" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4232" } }, { @@ -788,7 +788,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4241" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4243" } }, { @@ -888,7 +888,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4252" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4254" } }, { @@ -944,7 +944,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4263" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4265" } }, { @@ -1017,7 +1017,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4274" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4276" } }, { @@ -1090,7 +1090,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4285" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4287" } }, { @@ -1137,7 +1137,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4296" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4298" } }, { @@ -1169,7 +1169,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4307" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4309" } }, { @@ -1206,7 +1206,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4329" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4331" } }, { @@ -1253,7 +1253,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4340" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4342" } }, { @@ -1293,7 +1293,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4351" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4353" } }, { @@ -1340,7 +1340,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4362" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4364" } }, { @@ -1369,7 +1369,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4373" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4375" } }, { @@ -1506,7 +1506,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4384" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4386" } }, { @@ -1535,7 +1535,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4395" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4397" } }, { @@ -1589,7 +1589,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4406" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4408" } }, { @@ -1680,7 +1680,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4417" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4419" } }, { @@ -1708,7 +1708,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4428" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4430" } }, { @@ -1798,7 +1798,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4439" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4441" } }, { @@ -2054,7 +2054,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4450" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4452" } }, { @@ -2299,7 +2299,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4461" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4463" } }, { @@ -2355,7 +2355,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4472" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4474" } }, { @@ -2402,7 +2402,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4483" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4485" } }, { @@ -2500,7 +2500,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4494" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4496" } }, { @@ -2566,7 +2566,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4505" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4507" } }, { @@ -2632,7 +2632,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4516" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4518" } }, { @@ -2741,7 +2741,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4527" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4529" } }, { @@ -2799,7 +2799,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4538" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4540" } }, { @@ -2921,7 +2921,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4549" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4551" } }, { @@ -3108,7 +3108,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4560" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4562" } }, { @@ -3312,7 +3312,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4571" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4573" } }, { @@ -3403,7 +3403,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4582" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4584" } }, { @@ -3461,7 +3461,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4593" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4595" } }, { @@ -3719,7 +3719,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4604" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4606" } }, { @@ -3994,7 +3994,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4615" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4617" } }, { @@ -4022,7 +4022,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4626" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4628" } }, { @@ -4060,7 +4060,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4637" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4639" } }, { @@ -4168,7 +4168,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4648" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4650" } }, { @@ -4206,7 +4206,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4659" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4661" } }, { @@ -4235,7 +4235,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4670" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4672" } }, { @@ -4298,7 +4298,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4681" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4683" } }, { @@ -4361,7 +4361,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4692" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4694" } }, { @@ -4406,7 +4406,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4703" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4705" } }, { @@ -4528,7 +4528,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4714" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4716" } }, { @@ -4683,7 +4683,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4725" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4727" } }, { @@ -4737,7 +4737,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4736" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4738" } }, { @@ -4791,7 +4791,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4747" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4749" } }, { @@ -4893,7 +4893,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4758" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4760" } }, { @@ -5116,7 +5116,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4769" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4771" } }, { @@ -5310,7 +5310,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4780" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4782" } }, { @@ -5356,7 +5356,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4791" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4793" } }, { @@ -5506,7 +5506,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4802" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4804" } }, { @@ -5643,7 +5643,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4813" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4815" } }, { @@ -5711,7 +5711,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4824" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4826" } }, { @@ -5828,7 +5828,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4835" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4837" } }, { @@ -5919,7 +5919,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4846" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4848" } }, { @@ -6005,7 +6005,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4857" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4859" } }, { @@ -6032,7 +6032,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4868" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4870" } }, { @@ -6059,7 +6059,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4879" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4881" } }, { @@ -6127,7 +6127,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4890" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4892" } }, { @@ -6633,7 +6633,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4901" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4903" } }, { @@ -6730,7 +6730,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4912" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4914" } }, { @@ -6830,7 +6830,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4923" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4925" } }, { @@ -6930,7 +6930,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4934" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4936" } }, { @@ -7055,7 +7055,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4945" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4947" } }, { @@ -7164,7 +7164,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4956" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4958" } }, { @@ -7267,7 +7267,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4967" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4969" } }, { @@ -7397,7 +7397,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4978" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4980" } }, { @@ -7504,7 +7504,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4989" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4991" } }, { @@ -7565,7 +7565,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5000" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5002" } }, { @@ -7633,7 +7633,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5011" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5013" } }, { @@ -7714,7 +7714,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5022" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5024" } }, { @@ -7878,7 +7878,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5033" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5035" } }, { @@ -8079,7 +8079,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5044" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5046" } }, { @@ -8190,7 +8190,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5055" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5057" } }, { @@ -8321,7 +8321,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5066" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5068" } }, { @@ -8407,7 +8407,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5077" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5079" } }, { @@ -8434,7 +8434,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5088" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5090" } }, { @@ -8487,7 +8487,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5099" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5101" } }, { @@ -8575,7 +8575,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5110" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5112" } }, { @@ -9026,7 +9026,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5121" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5123" } }, { @@ -9193,7 +9193,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5132" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5134" } }, { @@ -9366,7 +9366,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5143" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5145" } }, { @@ -9434,7 +9434,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5154" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5156" } }, { @@ -9502,7 +9502,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5165" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5167" } }, { @@ -9663,7 +9663,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5176" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5178" } }, { @@ -9708,7 +9708,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5187" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5189" } }, { @@ -9753,7 +9753,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5198" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5200" } }, { @@ -9780,7 +9780,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5209" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5211" } } ] diff --git a/build/openrpc/miner.json b/build/openrpc/miner.json index ae37f6ee0..58807933b 100644 --- a/build/openrpc/miner.json +++ b/build/openrpc/miner.json @@ -30,7 +30,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5594" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5607" } }, { @@ -109,7 +109,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5605" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5618" } }, { @@ -155,7 +155,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5616" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5629" } }, { @@ -203,7 +203,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5627" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5640" } }, { @@ -251,7 +251,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5638" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5651" } }, { @@ -354,7 +354,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5649" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5662" } }, { @@ -428,7 +428,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5660" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5673" } }, { @@ -591,7 +591,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5671" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5684" } }, { @@ -742,7 +742,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5682" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5695" } }, { @@ -781,7 +781,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5693" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5706" } }, { @@ -833,7 +833,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5704" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5717" } }, { @@ -872,7 +872,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5726" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5739" } }, { @@ -924,7 +924,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5737" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5750" } }, { @@ -996,7 +996,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5748" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5761" } }, { @@ -1035,7 +1035,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5759" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5772" } }, { @@ -1074,7 +1074,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5770" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5783" } }, { @@ -1101,7 +1101,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5781" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5794" } }, { @@ -1128,7 +1128,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5792" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5805" } }, { @@ -1155,7 +1155,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5803" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5816" } }, { @@ -1182,7 +1182,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5814" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5827" } }, { @@ -1209,7 +1209,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5825" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5838" } }, { @@ -1236,7 +1236,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5836" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5849" } }, { @@ -1294,7 +1294,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5847" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5860" } }, { @@ -1426,7 +1426,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5858" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5871" } }, { @@ -1466,7 +1466,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5869" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5882" } }, { @@ -1505,7 +1505,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5880" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5893" } }, { @@ -1544,7 +1544,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5891" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5904" } }, { @@ -1583,7 +1583,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5902" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5915" } }, { @@ -1622,7 +1622,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5913" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5926" } }, { @@ -1661,7 +1661,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5924" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5937" } }, { @@ -1700,7 +1700,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5935" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5948" } }, { @@ -1752,7 +1752,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5946" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5959" } }, { @@ -1775,7 +1775,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5957" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5970" } }, { @@ -1818,7 +1818,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5968" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5981" } }, { @@ -1889,7 +1889,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5979" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5992" } }, { @@ -2270,7 +2270,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5990" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6003" } }, { @@ -2369,7 +2369,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6012" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6025" } }, { @@ -2420,7 +2420,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6034" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6047" } }, { @@ -2478,7 +2478,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6045" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6058" } }, { @@ -2621,7 +2621,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6056" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6069" } }, { @@ -2753,7 +2753,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6067" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6080" } }, { @@ -3017,7 +3017,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6078" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6091" } }, { @@ -3054,7 +3054,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6089" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6102" } }, { @@ -3192,7 +3192,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6100" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6113" } }, { @@ -3215,7 +3215,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6111" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6124" } }, { @@ -3286,7 +3286,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6122" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6135" } }, { @@ -3329,7 +3329,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6133" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6146" } }, { @@ -3436,7 +3436,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6144" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6157" } }, { @@ -3499,7 +3499,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6155" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6168" } }, { @@ -3531,7 +3531,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6166" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6179" } }, { @@ -3619,7 +3619,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6177" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6190" } }, { @@ -3710,7 +3710,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6188" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6201" } }, { @@ -3750,7 +3750,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6199" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6212" } }, { @@ -3790,7 +3790,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6210" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6223" } }, { @@ -3831,7 +3831,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6221" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6234" } }, { @@ -3899,7 +3899,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6232" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6245" } }, { @@ -4030,7 +4030,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6243" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6256" } }, { @@ -4161,7 +4161,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6254" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6267" } }, { @@ -4261,7 +4261,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6265" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6278" } }, { @@ -4361,7 +4361,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6276" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6289" } }, { @@ -4461,7 +4461,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6287" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6300" } }, { @@ -4561,7 +4561,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6298" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6311" } }, { @@ -4661,7 +4661,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6309" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6322" } }, { @@ -4761,7 +4761,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6320" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6333" } }, { @@ -4885,7 +4885,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6331" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6344" } }, { @@ -5009,7 +5009,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6342" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6355" } }, { @@ -5124,7 +5124,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6353" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6366" } }, { @@ -5224,7 +5224,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6364" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6377" } }, { @@ -5357,7 +5357,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6375" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6388" } }, { @@ -5481,7 +5481,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6386" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6399" } }, { @@ -5605,7 +5605,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6397" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6410" } }, { @@ -5729,7 +5729,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6408" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6421" } }, { @@ -5862,7 +5862,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6419" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6432" } }, { @@ -5962,7 +5962,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6430" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6443" } }, { @@ -6003,7 +6003,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6441" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6454" } }, { @@ -6075,7 +6075,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6452" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6465" } }, { @@ -6125,7 +6125,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6463" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6476" } }, { @@ -6169,7 +6169,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6474" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6487" } }, { @@ -6210,7 +6210,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6485" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6498" } }, { @@ -6399,7 +6399,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6496" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6509" } }, { @@ -6473,7 +6473,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6507" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6520" } }, { @@ -6523,7 +6523,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6518" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6531" } }, { @@ -6552,7 +6552,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6529" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6542" } }, { @@ -6581,7 +6581,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6540" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6553" } }, { @@ -6637,7 +6637,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6551" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6564" } }, { @@ -6660,7 +6660,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6562" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6575" } }, { @@ -6720,7 +6720,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6573" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6586" } }, { @@ -6759,7 +6759,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6584" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6597" } }, { @@ -6799,7 +6799,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6595" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6608" } }, { @@ -6872,7 +6872,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6606" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6619" } }, { @@ -6936,7 +6936,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6617" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6630" } }, { @@ -6999,7 +6999,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6628" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6641" } }, { @@ -7049,7 +7049,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6639" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6652" } }, { @@ -7553,7 +7553,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6650" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6663" } }, { @@ -7594,7 +7594,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6661" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6674" } }, { @@ -7635,7 +7635,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6672" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6685" } }, { @@ -7676,7 +7676,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6683" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6696" } }, { @@ -7717,7 +7717,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6694" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6707" } }, { @@ -7758,7 +7758,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6705" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6718" } }, { @@ -7789,7 +7789,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6716" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6729" } }, { @@ -7839,7 +7839,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6727" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6740" } }, { @@ -7880,7 +7880,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6738" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6751" } }, { @@ -7919,7 +7919,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6749" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6762" } }, { @@ -7983,7 +7983,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6760" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6773" } }, { @@ -8041,7 +8041,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6771" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6784" } }, { @@ -8433,7 +8433,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6782" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6795" } }, { @@ -8469,7 +8469,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6793" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6806" } }, { @@ -8612,7 +8612,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6804" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6817" } }, { @@ -8668,7 +8668,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6815" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6828" } }, { @@ -8707,7 +8707,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6826" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6839" } }, { @@ -8866,7 +8866,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6837" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6850" } }, { @@ -8918,7 +8918,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6848" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6861" } }, { @@ -9075,7 +9075,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6859" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6872" } }, { @@ -9175,7 +9175,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6870" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6883" } }, { @@ -9229,7 +9229,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6881" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6894" } }, { @@ -9268,7 +9268,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6892" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6905" } }, { @@ -9353,7 +9353,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6903" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6916" } }, { @@ -9529,7 +9529,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6914" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6927" } }, { @@ -9625,7 +9625,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6925" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6938" } }, { @@ -9739,7 +9739,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6936" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6949" } }, { @@ -9793,7 +9793,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6947" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6960" } }, { @@ -9827,7 +9827,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6958" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6971" } }, { @@ -9914,7 +9914,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6969" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6982" } }, { @@ -9968,7 +9968,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6980" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6993" } }, { @@ -10068,7 +10068,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6991" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7004" } }, { @@ -10145,7 +10145,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7002" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7015" } }, { @@ -10236,7 +10236,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7013" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7026" } }, { @@ -10275,7 +10275,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7024" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7037" } }, { @@ -10391,7 +10391,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7035" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7048" } }, { @@ -12491,7 +12491,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7046" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7059" } } ] diff --git a/build/openrpc/worker.json b/build/openrpc/worker.json index 66fee236c..fb0eb9946 100644 --- a/build/openrpc/worker.json +++ b/build/openrpc/worker.json @@ -161,7 +161,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7134" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7147" } }, { @@ -252,7 +252,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7145" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7158" } }, { @@ -420,7 +420,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7156" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7169" } }, { @@ -447,7 +447,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7167" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7180" } }, { @@ -597,7 +597,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7178" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7191" } }, { @@ -700,7 +700,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7189" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7202" } }, { @@ -803,7 +803,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7200" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7213" } }, { @@ -925,7 +925,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7211" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7224" } }, { @@ -1135,7 +1135,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7222" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7235" } }, { @@ -1306,7 +1306,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7233" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7246" } }, { @@ -3350,7 +3350,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7244" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7257" } }, { @@ -3470,7 +3470,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7255" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7268" } }, { @@ -3531,7 +3531,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7266" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7279" } }, { @@ -3569,7 +3569,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7277" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7290" } }, { @@ -3729,7 +3729,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7288" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7301" } }, { @@ -3913,7 +3913,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7299" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7312" } }, { @@ -4054,7 +4054,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7310" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7323" } }, { @@ -4107,7 +4107,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7321" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7334" } }, { @@ -4250,7 +4250,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7332" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7345" } }, { @@ -4474,7 +4474,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7343" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7356" } }, { @@ -4601,7 +4601,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7354" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7367" } }, { @@ -4768,7 +4768,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7365" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7378" } }, { @@ -4895,7 +4895,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7376" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7389" } }, { @@ -4933,7 +4933,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7387" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7400" } }, { @@ -4972,7 +4972,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7398" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7411" } }, { @@ -4995,7 +4995,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7409" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7422" } }, { @@ -5034,7 +5034,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7420" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7433" } }, { @@ -5057,7 +5057,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7431" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7444" } }, { @@ -5096,7 +5096,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7442" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7455" } }, { @@ -5130,7 +5130,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7453" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7466" } }, { @@ -5184,7 +5184,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7464" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7477" } }, { @@ -5223,7 +5223,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7475" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7488" } }, { @@ -5262,7 +5262,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7486" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7499" } }, { @@ -5297,7 +5297,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7497" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7510" } }, { @@ -5477,7 +5477,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7508" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7521" } }, { @@ -5506,7 +5506,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7519" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7532" } }, { @@ -5529,7 +5529,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7530" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7543" } } ] diff --git a/cmd/lotus-provider/storage.go b/cmd/lotus-provider/storage.go index a93ff3e9c..987501816 100644 --- a/cmd/lotus-provider/storage.go +++ b/cmd/lotus-provider/storage.go @@ -7,6 +7,7 @@ import ( "os" "path/filepath" "sort" + "strconv" "strings" "time" @@ -17,6 +18,9 @@ import ( "github.com/urfave/cli/v2" "golang.org/x/xerrors" + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/lotus/chain/types" lcli "github.com/filecoin-project/lotus/cli" "github.com/filecoin-project/lotus/cmd/lotus-provider/rpc" @@ -37,9 +41,9 @@ stored while moving through the sealing pipeline (references as 'seal').`, storageAttachCmd, storageDetachCmd, storageListCmd, + storageFindCmd, /*storageDetachCmd, storageRedeclareCmd, - storageFindCmd, storageCleanupCmd, storageLocks,*/ }, @@ -396,3 +400,112 @@ var storageListCmd = &cli.Command{ return nil }, } + +type storedSector struct { + id storiface.ID + store storiface.SectorStorageInfo + types map[storiface.SectorFileType]bool +} + +var storageFindCmd = &cli.Command{ + Name: "find", + Usage: "find sector in the storage system", + ArgsUsage: "[miner address] [sector number]", + Action: func(cctx *cli.Context) error { + minerApi, closer, err := rpc.GetProviderAPI(cctx) + if err != nil { + return err + } + defer closer() + ctx := lcli.ReqContext(cctx) + + if cctx.NArg() != 2 { + return lcli.IncorrectNumArgs(cctx) + } + + maddr := cctx.Args().First() + ma, err := address.NewFromString(maddr) + if err != nil { + return xerrors.Errorf("parsing miner address: %w", err) + } + + mid, err := address.IDFromAddress(ma) + if err != nil { + return err + } + + if !cctx.Args().Present() { + return xerrors.New("Usage: lotus-miner storage find [sector number]") + } + + snum, err := strconv.ParseUint(cctx.Args().Get(1), 10, 64) + if err != nil { + return err + } + + sid := abi.SectorID{ + Miner: abi.ActorID(mid), + Number: abi.SectorNumber(snum), + } + + sectorTypes := []storiface.SectorFileType{ + storiface.FTUnsealed, storiface.FTSealed, storiface.FTCache, storiface.FTUpdate, storiface.FTUpdateCache, + } + + byId := make(map[storiface.ID]*storedSector) + for _, sectorType := range sectorTypes { + infos, err := minerApi.StorageFindSector(ctx, sid, sectorType, 0, false) + if err != nil { + return xerrors.Errorf("finding sector type %d: %w", sectorType, err) + } + + for _, info := range infos { + sts, ok := byId[info.ID] + if !ok { + sts = &storedSector{ + id: info.ID, + store: info, + types: make(map[storiface.SectorFileType]bool), + } + byId[info.ID] = sts + } + sts.types[sectorType] = true + } + } + + local, err := minerApi.StorageLocal(ctx) + if err != nil { + return err + } + + var out []*storedSector + for _, sector := range byId { + out = append(out, sector) + } + sort.Slice(out, func(i, j int) bool { + return out[i].id < out[j].id + }) + + for _, info := range out { + var types []string + for sectorType, present := range info.types { + if present { + types = append(types, sectorType.String()) + } + } + sort.Strings(types) // Optional: Sort types for consistent output + fmt.Printf("In %s (%s)\n", info.id, strings.Join(types, ", ")) + fmt.Printf("\tSealing: %t; Storage: %t\n", info.store.CanSeal, info.store.CanStore) + if localPath, ok := local[info.id]; ok { + fmt.Printf("\tLocal (%s)\n", localPath) + } else { + fmt.Printf("\tRemote\n") + } + for _, l := range info.store.URLs { + fmt.Printf("\tURL: %s\n", l) + } + } + + return nil + }, +} diff --git a/documentation/en/api-v0-methods-provider.md b/documentation/en/api-v0-methods-provider.md index af38c2821..43846035a 100644 --- a/documentation/en/api-v0-methods-provider.md +++ b/documentation/en/api-v0-methods-provider.md @@ -7,6 +7,7 @@ * [Storage](#Storage) * [StorageAddLocal](#StorageAddLocal) * [StorageDetachLocal](#StorageDetachLocal) + * [StorageFindSector](#StorageFindSector) * [StorageInfo](#StorageInfo) * [StorageList](#StorageList) * [StorageLocal](#StorageLocal) @@ -131,6 +132,49 @@ Inputs: Response: `{}` +### StorageFindSector + + +Perms: admin + +Inputs: +```json +[ + { + "Miner": 1000, + "Number": 9 + }, + 1, + 34359738368, + true +] +``` + +Response: +```json +[ + { + "ID": "76f1988b-ef30-4d7e-b3ec-9a627f4ba5a8", + "URLs": [ + "string value" + ], + "BaseURLs": [ + "string value" + ], + "Weight": 42, + "CanSeal": true, + "CanStore": true, + "Primary": true, + "AllowTypes": [ + "string value" + ], + "DenyTypes": [ + "string value" + ] + } +] +``` + ### StorageInfo From d1f7eb1c14be044768f5ba2ca974d611235fe4d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sat, 24 Feb 2024 11:52:23 +0100 Subject: [PATCH 218/394] fix: lpwinning: Fix MiningBase.afterPropDelay --- provider/lpwinning/winning_task.go | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/provider/lpwinning/winning_task.go b/provider/lpwinning/winning_task.go index b795ade4f..7f3c97616 100644 --- a/provider/lpwinning/winning_task.go +++ b/provider/lpwinning/winning_task.go @@ -178,7 +178,7 @@ func (t *WinPostTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (don }) // MAKE A MINING ATTEMPT!! - log.Debugw("attempting to mine a block", "tipset", types.LogCids(base.TipSet.Cids())) + log.Debugw("attempting to mine a block", "tipset", types.LogCids(base.TipSet.Cids()), "null-rounds", base.AddRounds) mbi, err := t.api.MinerGetBaseInfo(ctx, maddr, round, base.TipSet.Key()) if err != nil { @@ -226,6 +226,8 @@ func (t *WinPostTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (don } } + log.Infow("WinPostTask won election", "tipset", types.LogCids(base.TipSet.Cids()), "miner", maddr, "round", round, "eproof", eproof) + // winning PoSt var wpostProof []prooftypes.PoStProof { @@ -277,6 +279,8 @@ func (t *WinPostTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (don } } + log.Infow("WinPostTask winning PoSt computed", "tipset", types.LogCids(base.TipSet.Cids()), "miner", maddr, "round", round, "proofs", wpostProof) + ticket, err := t.computeTicket(ctx, maddr, &rbase, round, base.TipSet.MinTicket(), mbi) if err != nil { return false, xerrors.Errorf("scratching ticket failed: %w", err) @@ -288,6 +292,8 @@ func (t *WinPostTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (don return false, xerrors.Errorf("failed to select messages for block: %w", err) } + log.Infow("WinPostTask selected messages", "tipset", types.LogCids(base.TipSet.Cids()), "miner", maddr, "round", round, "messages", len(msgs)) + // equivocation handling { // This next block exists to "catch" equivocating miners, @@ -358,6 +364,8 @@ func (t *WinPostTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (don } } + log.Infow("WinPostTask base ready", "tipset", types.LogCids(base.TipSet.Cids()), "miner", maddr, "round", round, "ticket", ticket) + // block construction var blockMsg *types.BlockMsg { @@ -379,6 +387,8 @@ func (t *WinPostTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (don } } + log.Infow("WinPostTask block ready", "tipset", types.LogCids(base.TipSet.Cids()), "miner", maddr, "round", round, "block", blockMsg.Header.Cid(), "timestamp", blockMsg.Header.Timestamp) + // persist in db { bhjson, err := json.Marshal(blockMsg.Header) @@ -396,11 +406,13 @@ func (t *WinPostTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (don // wait until block timestamp { + log.Infow("WinPostTask waiting for block timestamp", "tipset", types.LogCids(base.TipSet.Cids()), "miner", maddr, "round", round, "block", blockMsg.Header.Cid(), "until", time.Unix(int64(blockMsg.Header.Timestamp), 0)) time.Sleep(time.Until(time.Unix(int64(blockMsg.Header.Timestamp), 0))) } // submit block!! { + log.Infow("WinPostTask submitting block", "tipset", types.LogCids(base.TipSet.Cids()), "miner", maddr, "round", round, "block", blockMsg.Header.Cid()) if err := t.api.SyncSubmitBlock(ctx, blockMsg); err != nil { return false, xerrors.Errorf("failed to submit block: %w", err) } @@ -489,7 +501,7 @@ func (mb MiningBase) baseTime() time.Time { } func (mb MiningBase) afterPropDelay() time.Time { - return mb.baseTime().Add(randTimeOffset(time.Second)) + return mb.baseTime().Add(time.Duration(build.PropagationDelaySecs) * time.Second).Add(randTimeOffset(time.Second)) } func (t *WinPostTask) mineBasic(ctx context.Context) { From f9e381cb2a063f12c17cc328608cb8b6b9ffe3c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 23 Feb 2024 11:53:49 +0100 Subject: [PATCH 219/394] lotus-provider: Fetch params on startup when needed --- cmd/lotus-provider/deps/deps.go | 15 +++++++++++++++ cmd/lotus-provider/tasks/tasks.go | 16 ++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/cmd/lotus-provider/deps/deps.go b/cmd/lotus-provider/deps/deps.go index 31e3058c1..9ac775a7a 100644 --- a/cmd/lotus-provider/deps/deps.go +++ b/cmd/lotus-provider/deps/deps.go @@ -23,6 +23,7 @@ import ( "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-jsonrpc/auth" + "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-statestore" "github.com/filecoin-project/lotus/api" @@ -32,6 +33,7 @@ import ( "github.com/filecoin-project/lotus/journal/fsjournal" "github.com/filecoin-project/lotus/lib/harmony/harmonydb" "github.com/filecoin-project/lotus/node/config" + "github.com/filecoin-project/lotus/node/modules" "github.com/filecoin-project/lotus/node/modules/dtypes" "github.com/filecoin-project/lotus/node/repo" "github.com/filecoin-project/lotus/provider" @@ -98,6 +100,7 @@ type Deps struct { LW *sealer.LocalWorker As *multictladdr.MultiAddressSelector Maddrs map[dtypes.MinerAddress]bool + ProofTypes map[abi.RegisteredSealProof]bool Stor *paths.Remote Si *paths.DBIndex LocalStore *paths.Local @@ -253,6 +256,18 @@ Get it with: jq .PrivateKey ~/.lotus-miner/keystore/MF2XI2BNNJ3XILLQOJUXMYLUMU`, } } } + if deps.ProofTypes == nil { + deps.ProofTypes = map[abi.RegisteredSealProof]bool{} + } + if len(deps.ProofTypes) == 0 { + for maddr := range deps.Maddrs { + spt, err := modules.SealProofType(maddr, deps.Full) + if err != nil { + return err + } + deps.ProofTypes[spt] = true + } + } return nil } diff --git a/cmd/lotus-provider/tasks/tasks.go b/cmd/lotus-provider/tasks/tasks.go index 00cb6480f..6ecd680d0 100644 --- a/cmd/lotus-provider/tasks/tasks.go +++ b/cmd/lotus-provider/tasks/tasks.go @@ -6,9 +6,11 @@ import ( logging "github.com/ipfs/go-log/v2" "github.com/samber/lo" + "golang.org/x/xerrors" "github.com/filecoin-project/lotus/cmd/lotus-provider/deps" "github.com/filecoin-project/lotus/lib/harmony/harmonytask" + "github.com/filecoin-project/lotus/node/modules" "github.com/filecoin-project/lotus/provider" "github.com/filecoin-project/lotus/provider/chainsched" "github.com/filecoin-project/lotus/provider/lpffi" @@ -37,6 +39,8 @@ func StartTasks(ctx context.Context, dependencies *deps.Deps) (*harmonytask.Task chainSched := chainsched.New(full) + var needProofParams bool + /////////////////////////////////////////////////////////////////////// ///// Task Selection /////////////////////////////////////////////////////////////////////// @@ -50,11 +54,13 @@ func StartTasks(ctx context.Context, dependencies *deps.Deps) (*harmonytask.Task return nil, err } activeTasks = append(activeTasks, wdPostTask, wdPoStSubmitTask, derlareRecoverTask) + needProofParams = true } if cfg.Subsystems.EnableWinningPost { winPoStTask := lpwinning.NewWinPostTask(cfg.Subsystems.WinningPostMaxTasks, db, lw, verif, full, maddrs) activeTasks = append(activeTasks, winPoStTask) + needProofParams = true } } @@ -93,6 +99,7 @@ func StartTasks(ctx context.Context, dependencies *deps.Deps) (*harmonytask.Task if cfg.Subsystems.EnablePoRepProof { porepTask := lpseal.NewPoRepTask(db, full, sp, slr, cfg.Subsystems.PoRepProofMaxTasks) activeTasks = append(activeTasks, porepTask) + needProofParams = true } if cfg.Subsystems.EnableMoveStorage { moveStorageTask := lpseal.NewMoveStorageTask(sp, slr, db, cfg.Subsystems.MoveStorageMaxTasks) @@ -103,6 +110,15 @@ func StartTasks(ctx context.Context, dependencies *deps.Deps) (*harmonytask.Task activeTasks = append(activeTasks, commitTask) } } + + if needProofParams { + for spt := range dependencies.ProofTypes { + if err := modules.GetParams(true)(spt); err != nil { + return nil, xerrors.Errorf("getting params: %w", err) + } + } + } + log.Infow("This lotus_provider instance handles", "miner_addresses", maddrs, "tasks", lo.Map(activeTasks, func(t harmonytask.TaskInterface, _ int) string { return t.TypeDetails().Name })) From cf6d3729028b66c05b6d592cc4aacc7b3f4ad9a7 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 26 Feb 2024 06:57:23 -0800 Subject: [PATCH 220/394] chore: build: update minimum go version to 1.21.7 (#11652) Now that 1.22 is out. Libp2p will also be dropping support for 1.20 soon (if it hasn't already?) so it can _finally_ stop supporting specific compiler versions (we can stop caring so much about the "maximum" supported go version). --- .circleci/config.yml | 10 +++++----- .circleci/template.yml | 4 ++-- Dockerfile | 2 +- GO_VERSION_MIN | 2 +- README.md | 4 ++-- go.mod | 2 +- go.sum | 41 +++++++++++++++++++++++++++++++++++++++++ 7 files changed, 53 insertions(+), 12 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 4410dfc60..6ca0353c5 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -7,12 +7,12 @@ executors: golang: docker: # Must match GO_VERSION_MIN in project root - - image: cimg/go:1.20.7 + - image: cimg/go:1.21.7 resource_class: medium+ golang-2xl: docker: # Must match GO_VERSION_MIN in project root - - image: cimg/go:1.20.7 + - image: cimg/go:1.21.7 resource_class: 2xlarge ubuntu: docker: @@ -164,7 +164,7 @@ jobs: default: unit description: Test suite name to report to CircleCI. docker: - - image: cimg/go:1.20 + - image: cimg/go:1.21 environment: LOTUS_HARMONYDB_HOSTS: yugabyte - image: yugabytedb/yugabyte:2.18.0.0-b65 @@ -218,7 +218,7 @@ jobs: test with. If empty (the default) the commit defined by the git submodule is used. docker: - - image: cimg/go:1.20 + - image: cimg/go:1.21 resource_class: << parameters.resource_class >> steps: - install-ubuntu-deps @@ -406,7 +406,7 @@ jobs: description: | Arguments to pass to golangci-lint docker: - - image: cimg/go:1.20 + - image: cimg/go:1.21 resource_class: medium+ steps: - install-ubuntu-deps diff --git a/.circleci/template.yml b/.circleci/template.yml index eaaa83e87..883e43a8d 100644 --- a/.circleci/template.yml +++ b/.circleci/template.yml @@ -7,12 +7,12 @@ executors: golang: docker: # Must match GO_VERSION_MIN in project root - - image: cimg/go:1.20.7 + - image: cimg/go:1.21.7 resource_class: medium+ golang-2xl: docker: # Must match GO_VERSION_MIN in project root - - image: cimg/go:1.20.7 + - image: cimg/go:1.21.7 resource_class: 2xlarge ubuntu: docker: diff --git a/Dockerfile b/Dockerfile index ea26b6184..14469533b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ ##################################### -FROM golang:1.20.7-bullseye AS lotus-builder +FROM golang:1.21.7-bullseye AS lotus-builder MAINTAINER Lotus Development Team RUN apt-get update && apt-get install -y ca-certificates build-essential clang ocl-icd-opencl-dev ocl-icd-libopencl1 jq libhwloc-dev diff --git a/GO_VERSION_MIN b/GO_VERSION_MIN index 8909929f6..8819d012c 100644 --- a/GO_VERSION_MIN +++ b/GO_VERSION_MIN @@ -1 +1 @@ -1.20.7 +1.21.7 diff --git a/README.md b/README.md index c944d41e6..dd4ff3b54 100644 --- a/README.md +++ b/README.md @@ -71,10 +71,10 @@ For other distributions you can find the required dependencies [here.](https://l #### Go -To build Lotus, you need a working installation of [Go 1.20.10 or higher](https://golang.org/dl/): +To build Lotus, you need a working installation of [Go 1.21.7 or higher](https://golang.org/dl/): ```bash -wget -c https://golang.org/dl/go1.20.10.linux-amd64.tar.gz -O - | sudo tar -xz -C /usr/local +wget -c https://golang.org/dl/go1.21.7.linux-amd64.tar.gz -O - | sudo tar -xz -C /usr/local ``` **TIP:** diff --git a/go.mod b/go.mod index 0b580e68b..b4b03db3a 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/filecoin-project/lotus -go 1.20 +go 1.21 retract v1.14.0 // Accidentally force-pushed tag, use v1.14.1+ instead. diff --git a/go.sum b/go.sum index 9f0039a7d..32af7dec1 100644 --- a/go.sum +++ b/go.sum @@ -44,6 +44,7 @@ git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGy github.com/AndreasBriese/bbloom v0.0.0-20180913140656-343706a395b7/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 h1:cTp8I5+VIoKjsnZuH8vjyaysT/ses3EvZeaV/1UkF2M= +github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= @@ -121,6 +122,7 @@ github.com/btcsuite/btcd v0.0.0-20190824003749-130ea5bddde3/go.mod h1:3J08xEfcug github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= github.com/btcsuite/btcd v0.21.0-beta/go.mod h1:ZSWyehm27aAuS9bvkATT+Xte3hjHZ+MRgMY/8NJ7K94= github.com/btcsuite/btcd v0.22.1 h1:CnwP9LM/M9xuRrGSCGeMVs9iv09uMqwsVX7EeIpgV2c= +github.com/btcsuite/btcd v0.22.1/go.mod h1:wqgTSL29+50LRkmOVknEdmt8ZojIzhuWvgu/iptuN7Y= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= github.com/btcsuite/btcutil v0.0.0-20190207003914-4c204d697803/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= @@ -161,6 +163,7 @@ github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4 github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cockroachdb/cockroach-go/v2 v2.2.0 h1:/5znzg5n373N/3ESjHF5SMLxiW4RKB05Ql//KWfeTFs= +github.com/cockroachdb/cockroach-go/v2 v2.2.0/go.mod h1:u3MiKYGupPPjkn3ozknpMUpxPaNLTFWAya419/zv6eI= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/codegangsta/cli v1.20.0/go.mod h1:/qJNoX69yVSKu5o4jLyXAENLRyk1uhi7zkbQ3slBdOA= @@ -200,6 +203,7 @@ github.com/davidlazar/go-crypto v0.0.0-20170701192655-dcfb0a7ac018/go.mod h1:rQY github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c h1:pFUpOrbxDR6AkioZ1ySsx5yxlDQZ8stG2b88gTPxgJU= github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c/go.mod h1:6UhI8N9EjYm1c2odKpFpAYeR8dsBeM7PtzQhRgxRr9U= github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= +github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= @@ -210,6 +214,7 @@ github.com/dgraph-io/badger v1.6.0-rc1/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhY github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4= github.com/dgraph-io/badger v1.6.1/go.mod h1:FRmFw3uxvcpa8zG3Rxs0th+hCLIuaQg8HlNV5bjgnuU= github.com/dgraph-io/badger v1.6.2 h1:mNw0qs90GVgGGWylh0umH5iag1j6n/PeJtNvL6KY/x8= +github.com/dgraph-io/badger v1.6.2/go.mod h1:JW2yswe3V058sS0kZ2h/AXeDSqFjxnZcRrVH//y2UQE= github.com/dgraph-io/badger/v2 v2.2007.3/go.mod h1:26P/7fbL4kUZVEVKLAKXkBXKOydDmM2p1e+NhhnBCAE= github.com/dgraph-io/badger/v2 v2.2007.4 h1:TRWBQg8UrlUhaFdco01nO2uXwzKS7zd+HVdwV/GHc4o= github.com/dgraph-io/badger/v2 v2.2007.4/go.mod h1:vSw/ax2qojzbN6eXHIx6KPKtCSHJN/Uz0X0VPruTIhk= @@ -269,6 +274,7 @@ github.com/fatih/color v1.8.0/go.mod h1:3l45GVGkyrnYNl9HoIjnp2NnNWvh6hLAqD8yTfGj github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= +github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/filecoin-project/dagstore v0.5.2 h1:Nd6oXdnolbbVhpMpkYT5PJHOjQp4OBSntHpMV5pxj3c= github.com/filecoin-project/dagstore v0.5.2/go.mod h1:mdqKzYrRBHf1pRMthYfMv3n37oOw0Tkx7+TxPt240M0= github.com/filecoin-project/go-address v0.0.3/go.mod h1:jr8JxKsYx+lQlQZmF5i2U0Z+cGQ59wMIps/8YW/lDj8= @@ -323,6 +329,7 @@ github.com/filecoin-project/go-padreader v0.0.1/go.mod h1:VYVPJqwpsfmtoHnAmPx6MU github.com/filecoin-project/go-paramfetch v0.0.4 h1:H+Me8EL8T5+79z/KHYQQcT8NVOzYVqXIi7nhb48tdm8= github.com/filecoin-project/go-paramfetch v0.0.4/go.mod h1:1FH85P8U+DUEmWk1Jkw3Bw7FrwTVUNHk/95PSPG+dts= github.com/filecoin-project/go-retrieval-types v1.2.0 h1:fz6DauLVP3GRg7UuW7HZ6sE+GTmaUW70DTXBF1r9cK0= +github.com/filecoin-project/go-retrieval-types v1.2.0/go.mod h1:ojW6wSw2GPyoRDBGqw1K6JxUcbfa5NOSIiyQEeh7KK0= github.com/filecoin-project/go-state-types v0.0.0-20200903145444-247639ffa6ad/go.mod h1:IQ0MBPnonv35CJHtWSN3YY1Hz2gkPru1Q9qoaYLxx9I= github.com/filecoin-project/go-state-types v0.0.0-20200928172055-2df22083d8ab/go.mod h1:ezYnPf0bNkTsDibL/psSz5dy4B5awOJ/E7P2Saeep8g= github.com/filecoin-project/go-state-types v0.0.0-20201102161440-c8033295a1fc/go.mod h1:ezYnPf0bNkTsDibL/psSz5dy4B5awOJ/E7P2Saeep8g= @@ -375,6 +382,7 @@ github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM github.com/frankban/quicktest v1.14.0/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og= github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= +github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= @@ -391,6 +399,7 @@ github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeME github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= github.com/go-chi/chi v1.5.4 h1:QHdzF2szwjqVV4wmByUnTcsbIg7UGaQ0tPF2t5GcAIs= +github.com/go-chi/chi v1.5.4/go.mod h1:uaf8YgoFazUOkPBG7fxPftUylNumIev9awIWOENIuEg= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -445,8 +454,10 @@ github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5x github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= +github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/googleapis v1.4.1 h1:1Yx4Myt7BxzvUr5ldGSbwYiZG6t9wGBZ+8/fX3Wvtq0= +github.com/gogo/googleapis v1.4.1/go.mod h1:2lpHqI5OcWCtVElxXnPt+s8oJvMpySlOyM6xDCrzib4= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= @@ -455,6 +466,7 @@ github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXP github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/gogo/status v1.1.1 h1:DuHXlSFHNKqTQ+/ACf5Vs6r4X/dH2EgIzR9Vr+H65kg= +github.com/gogo/status v1.1.1/go.mod h1:jpG3dM5QPcqu19Hg8lkUhBFBa3TcLs1DG7+2Jqci7oU= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE= github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ= @@ -549,6 +561,7 @@ github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORR github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20190812055157-5d271430af9f/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g= +github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= @@ -565,11 +578,13 @@ github.com/gregdhill/go-openrpc v0.0.0-20220114144539-ae6f44720487/go.mod h1:a1e github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw= +github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/gxed/hashland/keccakpg v0.0.1/go.mod h1:kRzw3HkwxFU1mpmPP8v1WyQzwdGfmKFJ6tItnhQ67kU= github.com/gxed/hashland/murmur3 v0.0.1/go.mod h1:KjXop02n4/ckmZSnY2+HKcLud/tcmvhST0bie/0lS48= github.com/hako/durafmt v0.0.0-20200710122514-c0fb7b4da026 h1:BpJ2o0OR5FV7vrkDYfXYVJQeMNWa8RhklZOpW2ITAIQ= @@ -641,6 +656,7 @@ github.com/ipfs/go-bitswap v0.1.0/go.mod h1:FFJEf18E9izuCqUtHxbWEvq+reg7o4CW5wSA github.com/ipfs/go-bitswap v0.1.2/go.mod h1:qxSWS4NXGs7jQ6zQvoPY3+NmOfHHG47mhkiLzBpJQIs= github.com/ipfs/go-bitswap v0.5.1/go.mod h1:P+ckC87ri1xFLvk74NlXdP0Kj9RmWAh4+H78sC6Qopo= github.com/ipfs/go-bitswap v0.11.0 h1:j1WVvhDX1yhG32NTC9xfxnqycqYIlhzEzLXG/cU1HyQ= +github.com/ipfs/go-bitswap v0.11.0/go.mod h1:05aE8H3XOU+LXpTedeAS0OZpcO1WFsj5niYQH9a1Tmk= github.com/ipfs/go-block-format v0.0.1/go.mod h1:DK/YYcsSUIVAFNwo/KZCdIIbpN0ROH/baNLgayt4pFc= github.com/ipfs/go-block-format v0.0.2/go.mod h1:AWR46JfpcObNfg3ok2JHDUfdiHRgWhJgCQF+KIgOPJY= github.com/ipfs/go-block-format v0.0.3/go.mod h1:4LmD4ZUw0mhO+JSKdpWwrzATiEfM7WWgQ8H5l6P8MVk= @@ -686,6 +702,7 @@ github.com/ipfs/go-ds-badger v0.0.5/go.mod h1:g5AuuCGmr7efyzQhLL8MzwqcauPojGPUaH github.com/ipfs/go-ds-badger v0.2.1/go.mod h1:Tx7l3aTph3FMFrRS838dcSJh+jjA7cX9DrGVwx/NOwE= github.com/ipfs/go-ds-badger v0.2.3/go.mod h1:pEYw0rgg3FIrywKKnL+Snr+w/LjJZVMTBRn4FS6UHUk= github.com/ipfs/go-ds-badger v0.3.0 h1:xREL3V0EH9S219kFFueOYJJTcjgNSZ2HY1iSvN7U1Ro= +github.com/ipfs/go-ds-badger v0.3.0/go.mod h1:1ke6mXNqeV8K3y5Ak2bAA0osoTfmxUdupVCGm4QUIek= github.com/ipfs/go-ds-badger2 v0.1.3 h1:Zo9JicXJ1DmXTN4KOw7oPXkspZ0AWHcAFCP1tQKnegg= github.com/ipfs/go-ds-badger2 v0.1.3/go.mod h1:TPhhljfrgewjbtuL/tczP8dNrBYwwk+SdPYbms/NO9w= github.com/ipfs/go-ds-leveldb v0.0.1/go.mod h1:feO8V3kubwsEF22n0YRQCffeb79OOYIykR4L04tMOYc= @@ -711,6 +728,7 @@ github.com/ipfs/go-ipfs-blocksutil v0.0.1 h1:Eh/H4pc1hsvhzsQoMEP3Bke/aW5P5rVM1IW github.com/ipfs/go-ipfs-blocksutil v0.0.1/go.mod h1:Yq4M86uIOmxmGPUHv/uI7uKqZNtLb449gwKqXjIsnRk= github.com/ipfs/go-ipfs-chunker v0.0.1/go.mod h1:tWewYK0we3+rMbOh7pPFGDyypCtvGcBFymgY4rSDLAw= github.com/ipfs/go-ipfs-chunker v0.0.5 h1:ojCf7HV/m+uS2vhUGWcogIIxiO5ubl5O57Q7NapWLY8= +github.com/ipfs/go-ipfs-chunker v0.0.5/go.mod h1:jhgdF8vxRHycr00k13FM8Y0E+6BoalYeobXmUyTreP8= github.com/ipfs/go-ipfs-cmds v0.9.0 h1:K0VcXg1l1k6aY6sHnoxYcyimyJQbcV1ueXuWgThmK9Q= github.com/ipfs/go-ipfs-cmds v0.9.0/go.mod h1:SBFHK8WNwC416QWH9Vz1Ql42SSMAOqKpaHUMBu3jpLo= github.com/ipfs/go-ipfs-delay v0.0.0-20181109222059-70721b86a9a8/go.mod h1:8SP1YXK1M1kXuc4KJZINY3TQQ03J2rwBG9QfXmbRPrw= @@ -731,6 +749,7 @@ github.com/ipfs/go-ipfs-exchange-offline v0.3.0/go.mod h1:MOdJ9DChbb5u37M1IcbrRB github.com/ipfs/go-ipfs-files v0.0.3/go.mod h1:INEFm0LL2LWXBhNJ2PMIIb2w45hpXgPjNoE7yA8Y1d4= github.com/ipfs/go-ipfs-files v0.0.4/go.mod h1:INEFm0LL2LWXBhNJ2PMIIb2w45hpXgPjNoE7yA8Y1d4= github.com/ipfs/go-ipfs-files v0.3.0 h1:fallckyc5PYjuMEitPNrjRfpwl7YFt69heCOUhsbGxQ= +github.com/ipfs/go-ipfs-files v0.3.0/go.mod h1:xAUtYMwB+iu/dtf6+muHNSFQCJG2dSiStR2P6sn9tIM= github.com/ipfs/go-ipfs-posinfo v0.0.1 h1:Esoxj+1JgSjX0+ylc0hUmJCOv6V2vFoZiETLR6OtpRs= github.com/ipfs/go-ipfs-posinfo v0.0.1/go.mod h1:SwyeVP+jCwiDu0C313l/8jg6ZxM0qqtlt2a0vILTc1A= github.com/ipfs/go-ipfs-pq v0.0.1/go.mod h1:LWIqQpqfRG3fNc5XsnIhz/wQ2XXGyugQwls7BgUmUfY= @@ -740,6 +759,7 @@ github.com/ipfs/go-ipfs-pq v0.0.3/go.mod h1:btNw5hsHBpRcSSgZtiNm/SLj5gYIZ18AKtv3 github.com/ipfs/go-ipfs-routing v0.1.0/go.mod h1:hYoUkJLyAUKhF58tysKpids8RNDPO42BVMgK5dNsoqY= github.com/ipfs/go-ipfs-routing v0.2.1/go.mod h1:xiNNiwgjmLqPS1cimvAw6EyB9rkVDbiocA4yY+wRNLM= github.com/ipfs/go-ipfs-routing v0.3.0 h1:9W/W3N+g+y4ZDeffSgqhgo7BsBSJwPMcyssET9OWevc= +github.com/ipfs/go-ipfs-routing v0.3.0/go.mod h1:dKqtTFIql7e1zYsEuWLyuOU+E0WJWW8JjbTPLParDWo= github.com/ipfs/go-ipfs-util v0.0.1/go.mod h1:spsl5z8KUnrve+73pOhSVZND1SIxPW5RyBCNzQxlJBc= github.com/ipfs/go-ipfs-util v0.0.2/go.mod h1:CbPtkWJzjLdEcezDns2XYaehFVNXG9zrdrtMecczcsQ= github.com/ipfs/go-ipfs-util v0.0.3 h1:2RFdGez6bu2ZlZdI+rWfIdbQb1KudQp3VGwPtdNCmE0= @@ -799,6 +819,7 @@ github.com/ipfs/go-peertaskqueue v0.8.1 h1:YhxAs1+wxb5jk7RvS0LHdyiILpNmRIRnZVzte github.com/ipfs/go-peertaskqueue v0.8.1/go.mod h1:Oxxd3eaK279FxeydSPPVGHzbwVeHjatZ2GA8XD+KbPU= github.com/ipfs/go-unixfs v0.2.2-0.20190827150610-868af2e9e5cb/go.mod h1:IwAAgul1UQIcNZzKPYZWOCijryFBeCV79cNubPzol+k= github.com/ipfs/go-unixfs v0.4.5 h1:wj8JhxvV1G6CD7swACwSKYa+NgtdWC1RUit+gFnymDU= +github.com/ipfs/go-unixfs v0.4.5/go.mod h1:BIznJNvt/gEx/ooRMI4Us9K8+qeGO7vx1ohnbk8gjFg= github.com/ipfs/go-unixfsnode v1.7.1 h1:RRxO2b6CSr5UQ/kxnGzaChTjp5LWTdf3Y4n8ANZgB/s= github.com/ipfs/go-unixfsnode v1.7.1/go.mod h1:PVfoyZkX1B34qzT3vJO4nsLUpRCyhnMuHBznRcXirlk= github.com/ipfs/go-verifcid v0.0.1/go.mod h1:5Hrva5KBeIog4A+UpqlaIU+DEstipcJYQQZc0g37pY0= @@ -828,6 +849,7 @@ github.com/ipld/go-ipld-prime v0.20.0/go.mod h1:PzqZ/ZR981eKbgdr3y2DJYeD/8bgMawd github.com/ipld/go-ipld-prime-proto v0.0.0-20191113031812-e32bd156a1e5/go.mod h1:gcvzoEDBjwycpXt3LBE061wT9f46szXGHAmj9uoP6fU= github.com/ipld/go-ipld-prime/storage/bsadapter v0.0.0-20211210234204-ce2a1c70cd73/go.mod h1:2PJ0JgxyB08t0b2WKrcuqI3di0V+5n6RS/LTUJhkoxY= github.com/ipld/go-ipld-prime/storage/bsadapter v0.0.0-20230102063945-1a409dc236dd h1:gMlw/MhNr2Wtp5RwGdsW23cs+yCuj9k2ON7i9MiJlRo= +github.com/ipld/go-ipld-prime/storage/bsadapter v0.0.0-20230102063945-1a409dc236dd/go.mod h1:wZ8hH8UxeryOs4kJEJaiui/s00hDSbE37OKsL47g+Sw= github.com/ipld/go-ipld-selector-text-lite v0.0.1 h1:lNqFsQpBHc3p5xHob2KvEg/iM5dIFn6iw4L/Hh+kS1Y= github.com/ipld/go-ipld-selector-text-lite v0.0.1/go.mod h1:U2CQmFb+uWzfIEF3I1arrDa5rwtj00PrpiwwCO+k1RM= github.com/ipni/go-libipni v0.0.8 h1:0wLfZRSBG84swmZwmaLKul/iB/FlBkkl9ZcR1ub+Z+w= @@ -871,6 +893,7 @@ github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901/go.mod h1:Z86h9 github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jonboulle/clockwork v0.3.0 h1:9BSCMi8C+0qdApAp4auwX0RkLGUjs956h0EkuQymUhg= +github.com/jonboulle/clockwork v0.3.0/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= @@ -927,6 +950,7 @@ github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfn github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= @@ -936,6 +960,7 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= github.com/lib/pq v1.10.0 h1:Zx5DJFEYQXio93kgXnQ09fXNiUKsqv4OUEu2UtGcB1E= +github.com/lib/pq v1.10.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/libp2p/go-addr-util v0.0.1/go.mod h1:4ac6O7n9rIAKB1dnd+s8IbbMXkt+oBpzX4/+RACcnlQ= github.com/libp2p/go-addr-util v0.0.2/go.mod h1:Ecd6Fb3yIuLzq4bD7VcywcVSBtefcAwnUISBM3WG15E= github.com/libp2p/go-buffer-pool v0.0.1/go.mod h1:xtyIz9PMobb13WaxR6Zo1Pd1zXJKYg0a8KiIvDp3TzQ= @@ -1059,6 +1084,7 @@ github.com/libp2p/go-libp2p-testing v0.1.2-0.20200422005655-8775583591d8/go.mod github.com/libp2p/go-libp2p-testing v0.3.0/go.mod h1:efZkql4UZ7OVsEfaxNHZPzIehtsBXMrXnCfJIgDti5g= github.com/libp2p/go-libp2p-testing v0.4.0/go.mod h1:Q+PFXYoiYFN5CAEG2w3gLPEzotlKsNSbKQ/lImlOWF0= github.com/libp2p/go-libp2p-testing v0.12.0 h1:EPvBb4kKMWO29qP4mZGyhVzUyR25dvfUIK5WDu6iPUA= +github.com/libp2p/go-libp2p-testing v0.12.0/go.mod h1:KcGDRXyN7sQCllucn1cOOS+Dmm7ujhfEyXQL5lvkcPg= github.com/libp2p/go-libp2p-tls v0.1.3/go.mod h1:wZfuewxOndz5RTnCAxFliGjvYSDA40sKitV4c50uI1M= github.com/libp2p/go-libp2p-transport-upgrader v0.1.1/go.mod h1:IEtA6or8JUbsV07qPW4r01GnTenLW4oi3lOPbUMGJJA= github.com/libp2p/go-libp2p-transport-upgrader v0.2.0/go.mod h1:mQcrHj4asu6ArfSoMuyojOdjx73Q47cYD7s5+gZOlns= @@ -1320,6 +1346,7 @@ github.com/nkovacs/streamquote v1.0.0 h1:PmVIV08Zlx2lZK5fFZlMZ04eHcDTIFJCv/5/0tw github.com/nkovacs/streamquote v1.0.0/go.mod h1:BN+NaZ2CmdKqUuTUXUEm9j95B2TRbpOWpxbJYzzgUsc= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= @@ -1330,6 +1357,7 @@ github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0 github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= github.com/onsi/ginkgo/v2 v2.11.0 h1:WgqUCUt/lT6yXoQ8Wef0fsNn5cAuMK7+KT9UFRz2tcU= github.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7kR0iZvM= github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= @@ -1339,6 +1367,7 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.27.8 h1:gegWiwZjBsf2DgiSbf5hpokZ98JVDMcWkUiigk6/KXc= +github.com/onsi/gomega v1.27.8/go.mod h1:2J8vzI/s+2shY9XHRApDkdgPo1TKT7P2u6fXeJKFnNQ= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/open-rpc/meta-schema v0.0.0-20201029221707-1b72ef2ea333 h1:CznVS40zms0Dj5he4ERo+fRPtO0qxUk8lA8Xu3ddet0= github.com/open-rpc/meta-schema v0.0.0-20201029221707-1b72ef2ea333/go.mod h1:Ag6rSXkHIckQmjFBCweJEEt1mrTPBv8b9W4aU/NQWfI= @@ -1346,8 +1375,10 @@ github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/ github.com/opencontainers/runtime-spec v1.1.0 h1:HHUyrt9mwHUjtasSbXSMvs4cyFxh+Bll4AjJ9odEGpg= github.com/opencontainers/runtime-spec v1.1.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opentracing-contrib/go-grpc v0.0.0-20210225150812-73cb765af46e h1:4cPxUYdgaGzZIT5/j0IfqOrrXmq6bG8AwvwisMXpdrg= +github.com/opentracing-contrib/go-grpc v0.0.0-20210225150812-73cb765af46e/go.mod h1:DYR5Eij8rJl8h7gblRrOZ8g0kW1umSpKqYIBTgeDtLo= github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= github.com/opentracing-contrib/go-stdlib v1.0.0 h1:TBS7YuVotp8myLon4Pv7BtCBzOTo1DeZCld0Z63mW2w= +github.com/opentracing-contrib/go-stdlib v1.0.0/go.mod h1:qtI1ogk+2JhVPIXVc6q+NHziSmy2W5GbdQZFUHADCBU= github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= @@ -1456,6 +1487,7 @@ github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= @@ -1471,6 +1503,7 @@ github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXn github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/sercand/kuberesolver v2.4.0+incompatible h1:WE2OlRf6wjLxHwNkkFLQGaZcVLEXjMjBPjjEU5vksH8= +github.com/sercand/kuberesolver v2.4.0+incompatible/go.mod h1:lWF3GL0xptCB/vCiJPl/ZshwPsX/n4Y7u0CW9E7aQIQ= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/shirou/gopsutil v2.18.12+incompatible h1:1eaJvGomDnH74/5cF4CTmTbLHAriGFsTZppLXDX93OM= github.com/shirou/gopsutil v2.18.12+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= @@ -1509,6 +1542,7 @@ github.com/smartystreets/assertions v1.0.0/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUr github.com/smartystreets/assertions v1.0.1/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM= github.com/smartystreets/assertions v1.2.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= github.com/smartystreets/assertions v1.13.0 h1:Dx1kYM01xsSqKPno3aqLnrwac2LetPvN23diwyr69Qs= +github.com/smartystreets/assertions v1.13.0/go.mod h1:wDmR7qL282YbGsPy6H/yAsesrxfxaaSlJazyFLYVFx8= github.com/smartystreets/goconvey v0.0.0-20190222223459-a17d461953aa/go.mod h1:2RVY1rIf+2J2o/IM9+vPq9RzmHDSseB7FoXiSNIUsoU= github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= @@ -1578,7 +1612,9 @@ github.com/triplewz/poseidon v0.0.0-20230828015038-79d8165c88ed/go.mod h1:QYG1d0 github.com/twmb/murmur3 v1.1.6 h1:mqrRot1BRxm+Yct+vavLMou2/iJt0tNVTTC0QoIjaZg= github.com/twmb/murmur3 v1.1.6/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= github.com/uber/jaeger-client-go v2.30.0+incompatible h1:D6wyKGCecFaSRUpo8lCVbaOOb6ThwMmTEbhRwtKR97o= +github.com/uber/jaeger-client-go v2.30.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= github.com/uber/jaeger-lib v2.4.1+incompatible h1:td4jdvLcExb4cBISKIpHuGoVXh+dVKhn2Um6rjCsSsg= +github.com/uber/jaeger-lib v2.4.1+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= @@ -1596,13 +1632,16 @@ github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMI github.com/warpfork/go-testmark v0.3.0/go.mod h1:jhEf8FVxd+F17juRubpmut64NEG6I2rgkUhlcqqXwE0= github.com/warpfork/go-testmark v0.10.0/go.mod h1:jhEf8FVxd+F17juRubpmut64NEG6I2rgkUhlcqqXwE0= github.com/warpfork/go-testmark v0.11.0 h1:J6LnV8KpceDvo7spaNU4+DauH2n1x+6RaO2rJrmpQ9U= +github.com/warpfork/go-testmark v0.11.0/go.mod h1:jhEf8FVxd+F17juRubpmut64NEG6I2rgkUhlcqqXwE0= github.com/warpfork/go-wish v0.0.0-20180510122957-5ad1f5abf436/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw= github.com/warpfork/go-wish v0.0.0-20190328234359-8b3e70f8e830/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw= github.com/warpfork/go-wish v0.0.0-20200122115046-b9ea61034e4a/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw= github.com/warpfork/go-wish v0.0.0-20220906213052-39a1cc7a02d0 h1:GDDkbFiaK8jsSDJfjId/PEGEShv6ugrt4kYsC5UIDaQ= github.com/warpfork/go-wish v0.0.0-20220906213052-39a1cc7a02d0/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw= github.com/weaveworks/common v0.0.0-20220810113439-c65105d60b18 h1:JN4YR/TNWiZEAHHImrVA2u4DPI+aqPOar23ICUnYZTQ= +github.com/weaveworks/common v0.0.0-20220810113439-c65105d60b18/go.mod h1:YfOOLoW1Q/jIIu0WLeSwgStmrKjuJEZSKTAUc+0KFvE= github.com/weaveworks/promrus v1.2.0 h1:jOLf6pe6/vss4qGHjXmGz4oDJQA+AOCqEL3FvvZGz7M= +github.com/weaveworks/promrus v1.2.0/go.mod h1:SaE82+OJ91yqjrE1rsvBWVzNZKcHYFtMUyS1+Ogs/KA= github.com/whyrusleeping/bencher v0.0.0-20190829221104-bb6607aa8bba h1:X4n8JG2e2biEZZXdBKt9HX7DN3bYGFUqljqqy0DqgnY= github.com/whyrusleeping/bencher v0.0.0-20190829221104-bb6607aa8bba/go.mod h1:CHQnYnQUEPydYCwuy8lmTHfGmdw9TKrhWV0xLx8l0oM= github.com/whyrusleeping/cbor v0.0.0-20171005072247-63513f603b11 h1:5HZfQkwe0mIfyDmc1Em5GqlNRzcdtlv4HTNmdpt7XH0= @@ -1674,6 +1713,7 @@ go.dedis.ch/protobuf v1.0.11 h1:FTYVIEzY/bfl37lu3pR4lIj+F9Vp1jE8oh91VmxKgLo= go.dedis.ch/protobuf v1.0.11/go.mod h1:97QR256dnkimeNdfmURz0wAMNVbd1VmLXhG1CrTYrJ4= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU= +go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= @@ -1720,6 +1760,7 @@ go.uber.org/fx v1.20.0/go.mod h1:qCUj0btiR3/JnanEr1TYEePfSw6o/4qYJscgvzQ5Ub0= go.uber.org/goleak v1.0.0/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= +go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.4.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= From 0eddfee5d62adf4db1a0810b62ecbf75281aaf86 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 26 Feb 2024 07:54:32 -0800 Subject: [PATCH 221/394] chore: switch back to upstream ledger library (#11651) Upstream has merged our code to expose a `SignatureBytes` function, so we can now switch back to using it directly. This also brings bug fixes. --- chain/wallet/ledger/ledger.go | 2 +- cmd/lotus-shed/ledger.go | 2 +- go.mod | 6 +++--- go.sum | 32 ++++++++++++++++++++++---------- 4 files changed, 27 insertions(+), 15 deletions(-) diff --git a/chain/wallet/ledger/ledger.go b/chain/wallet/ledger/ledger.go index 5279389de..5bd218b50 100644 --- a/chain/wallet/ledger/ledger.go +++ b/chain/wallet/ledger/ledger.go @@ -10,7 +10,7 @@ import ( "github.com/ipfs/go-datastore" "github.com/ipfs/go-datastore/query" logging "github.com/ipfs/go-log/v2" - ledgerfil "github.com/whyrusleeping/ledger-filecoin-go" + ledgerfil "github.com/zondax/ledger-filecoin-go" "golang.org/x/xerrors" "github.com/filecoin-project/go-address" diff --git a/cmd/lotus-shed/ledger.go b/cmd/lotus-shed/ledger.go index d9a888d20..0cc668985 100644 --- a/cmd/lotus-shed/ledger.go +++ b/cmd/lotus-shed/ledger.go @@ -7,7 +7,7 @@ import ( "strings" "github.com/urfave/cli/v2" - ledgerfil "github.com/whyrusleeping/ledger-filecoin-go" + ledgerfil "github.com/zondax/ledger-filecoin-go" "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/big" diff --git a/go.mod b/go.mod index b4b03db3a..583a1760e 100644 --- a/go.mod +++ b/go.mod @@ -142,10 +142,10 @@ require ( github.com/urfave/cli/v2 v2.25.5 github.com/whyrusleeping/bencher v0.0.0-20190829221104-bb6607aa8bba github.com/whyrusleeping/cbor-gen v0.0.0-20230923211252-36a87e1ba72f - github.com/whyrusleeping/ledger-filecoin-go v0.9.1-0.20201010031517-c3dcc1bddce4 github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7 github.com/xeipuuv/gojsonschema v1.2.0 github.com/xorcare/golden v0.6.1-0.20191112154924-b87f686d7542 + github.com/zondax/ledger-filecoin-go v0.11.1 github.com/zyedidia/generic v1.2.1 go.opencensus.io v0.24.0 go.opentelemetry.io/otel v1.16.0 @@ -313,8 +313,8 @@ require ( github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect - github.com/zondax/hid v0.9.1 // indirect - github.com/zondax/ledger-go v0.12.1 // indirect + github.com/zondax/hid v0.9.2 // indirect + github.com/zondax/ledger-go v0.14.3 // indirect go.opentelemetry.io/otel/metric v1.16.0 // indirect go.opentelemetry.io/otel/sdk/metric v0.39.0 // indirect go.opentelemetry.io/otel/trace v1.16.0 // indirect diff --git a/go.sum b/go.sum index 32af7dec1..566ce37c6 100644 --- a/go.sum +++ b/go.sum @@ -79,6 +79,7 @@ github.com/Stebalien/go-bitfield v0.0.1/go.mod h1:GNjFpasyUVkHMsfEOk8EFLJ9syQ6SI github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo= +github.com/aead/siphash v1.0.1 h1:FwHfE/T45KPKYuuSAKyyvE+oPWcaQ+CUmFW0bPlM+kg= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= github.com/akavel/rsrc v0.8.0 h1:zjWn7ukO9Kc5Q62DOJCcxGpXC18RawVtYAGdz2aLlfw= @@ -121,18 +122,28 @@ github.com/btcsuite/btcd v0.0.0-20190605094302-a0d1e3e36d50/go.mod h1:3J08xEfcug github.com/btcsuite/btcd v0.0.0-20190824003749-130ea5bddde3/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI= github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= github.com/btcsuite/btcd v0.21.0-beta/go.mod h1:ZSWyehm27aAuS9bvkATT+Xte3hjHZ+MRgMY/8NJ7K94= -github.com/btcsuite/btcd v0.22.1 h1:CnwP9LM/M9xuRrGSCGeMVs9iv09uMqwsVX7EeIpgV2c= -github.com/btcsuite/btcd v0.22.1/go.mod h1:wqgTSL29+50LRkmOVknEdmt8ZojIzhuWvgu/iptuN7Y= +github.com/btcsuite/btcd v0.24.0 h1:gL3uHE/IaFj6fcZSu03SvqPMSx7s/dPzfpG/atRwWdo= +github.com/btcsuite/btcd v0.24.0/go.mod h1:K4IDc1593s8jKXIF7yS7yCTSxrknB9z0STzc2j6XgE4= +github.com/btcsuite/btcd/btcec/v2 v2.1.3 h1:xM/n3yIhHAhHy04z4i43C8p4ehixJZMsnrVJkgl+MTE= +github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE= +github.com/btcsuite/btcd/btcutil v1.1.5 h1:+wER79R5670vs/ZusMTF1yTcRYE5GUsFbdjdisflzM8= +github.com/btcsuite/btcd/btcutil v1.1.5/go.mod h1:PSZZ4UitpLBWzxGd5VGOrLnmOjtPP/a6HaFo12zMs00= +github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 h1:59Kx4K6lzOW5w6nFlA0v5+lk/6sjybR934QNHSJZPTQ= +github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= +github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f h1:bAs4lUbRJpnnkd9VhRV3jjAVU7DJVjMaK+IsvSeZvFo= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= github.com/btcsuite/btcutil v0.0.0-20190207003914-4c204d697803/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= github.com/btcsuite/btcutil v1.0.2/go.mod h1:j9HUFwoQRsZL3V4n+qG+CUnEGHOarIxfC3Le2Yhbcts= +github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd h1:R/opQEbFEy9JGkIguV40SvRY1uliPX8ifOvi6ICsFCw= github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= github.com/btcsuite/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I= github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= +github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792 h1:R8vQdOQdZ9Y3SkEwmHoWBmX1DNXhXZqlTpq6s4tyJGc= github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= +github.com/btcsuite/winsvc v1.0.0 h1:J9B4L7e3oqhXOcm+2IuNApwzQec85lE+QaikUcCs+dk= github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= github.com/buger/goterm v1.0.3 h1:7V/HeAQHrzPk/U4BvyH2g9u+xbUW9nr4yRPyG59W4fM= github.com/buger/goterm v1.0.3/go.mod h1:HiFWV3xnkolgrBV3mY8m0X0Pumt4zg4QhbdOzQtB8tE= @@ -206,6 +217,7 @@ github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5il github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= +github.com/decred/dcrd/lru v1.0.0 h1:Kbsb1SFDsIlaupWPwsPp+dkxiBY1frcS07PCPgotKz8= github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= github.com/detailyang/go-fallocate v0.0.0-20180908115635-432fa640bd2e h1:lj77EKYUpYXTd8CD/+QMIf8b6OIOTsfEBSXiAzuEHTU= github.com/detailyang/go-fallocate v0.0.0-20180908115635-432fa640bd2e/go.mod h1:3ZQK6DMPSz/QZ73jlWxBtUhNA8xZx7LzUFSq/OfP8vk= @@ -898,6 +910,7 @@ github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8Hm github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= +github.com/jrick/logrotate v1.0.0 h1:lQ1bL/n9mBNeIXoTUoYRlK4dHuNJVofX9oWqBtPnSzI= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -925,6 +938,7 @@ github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvW github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23 h1:FOOIBWrEkLgmlgGfMuZT83xIwfPDxEI2OHu6xUmJMFE= github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I= @@ -1669,8 +1683,6 @@ github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1/go.mod h github.com/whyrusleeping/go-logging v0.0.0-20170515211332-0457bb6b88fc/go.mod h1:bopw91TMyo8J3tvftk8xmU2kPmlrt4nScJQZU2hE5EM= github.com/whyrusleeping/go-logging v0.0.1/go.mod h1:lDPYj54zutzG1XYfHAhcc7oNXEburHQBn+Iqd4yS4vE= github.com/whyrusleeping/go-notifier v0.0.0-20170827234753-097c5d47330f/go.mod h1:cZNvX9cFybI01GriPRMXDtczuvUhgbcYr9iCGaNlRv8= -github.com/whyrusleeping/ledger-filecoin-go v0.9.1-0.20201010031517-c3dcc1bddce4 h1:NwiwjQDB3CzQ5XH0rdMh1oQqzJH7O2PSLWxif/w3zsY= -github.com/whyrusleeping/ledger-filecoin-go v0.9.1-0.20201010031517-c3dcc1bddce4/go.mod h1:K+EVq8d5QcQ2At5VECsA+SNZvWefyBXh8TnIsxo1OvQ= github.com/whyrusleeping/mafmt v1.2.8/go.mod h1:faQJFPbLSxzD9xpA02ttW/tS9vZykNvXwGvqIpk20FA= github.com/whyrusleeping/mdns v0.0.0-20180901202407-ef14215e6b30/go.mod h1:j4l84WPFclQPj320J9gp0XwNKBb3U0zt5CBqjPp22G4= github.com/whyrusleeping/mdns v0.0.0-20190826153040-b9b60ed33aa9/go.mod h1:j4l84WPFclQPj320J9gp0XwNKBb3U0zt5CBqjPp22G4= @@ -1696,11 +1708,12 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/zondax/hid v0.9.0/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM= -github.com/zondax/hid v0.9.1 h1:gQe66rtmyZ8VeGFcOpbuH3r7erYtNEAezCAYu8LdkJo= -github.com/zondax/hid v0.9.1/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM= -github.com/zondax/ledger-go v0.12.1 h1:hYRcyznPRJp+5mzF2sazTLP2nGvGjYDD2VzhHhFomLU= -github.com/zondax/ledger-go v0.12.1/go.mod h1:KatxXrVDzgWwbssUWsF5+cOJHXPvzQ09YSlzGNuhOEo= +github.com/zondax/hid v0.9.2 h1:WCJFnEDMiqGF64nlZz28E9qLVZ0KSJ7xpc5DLEyma2U= +github.com/zondax/hid v0.9.2/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM= +github.com/zondax/ledger-filecoin-go v0.11.1 h1:sNfst9L7ntZN5LfLL+rtScKWVBmLiask33QIne+WcxE= +github.com/zondax/ledger-filecoin-go v0.11.1/go.mod h1:pr9sUKUUyJ8epZ/np3vlixq6J8aawlWRwzUwsTsfPNw= +github.com/zondax/ledger-go v0.14.3 h1:wEpJt2CEcBJ428md/5MgSLsXLBos98sBOyxNmCjfUCw= +github.com/zondax/ledger-go v0.14.3/go.mod h1:IKKaoxupuB43g4NxeQmbLXv7T9AlQyie1UpHb342ycI= github.com/zyedidia/generic v1.2.1 h1:Zv5KS/N2m0XZZiuLS82qheRG4X1o5gsWreGb0hR7XDc= github.com/zyedidia/generic v1.2.1/go.mod h1:ly2RBz4mnz1yeuVbQA/VFwGjK3mnHGRj1JuoG336Bis= go.dedis.ch/fixbuf v1.0.3 h1:hGcV9Cd/znUxlusJ64eAlExS+5cJDIyTyEG+otu5wQs= @@ -1804,7 +1817,6 @@ golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190927123631-a832865fa7ad/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= From 066a9fd41a854a68c40dc99ee5369074df1e28cf Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Wed, 28 Feb 2024 05:45:33 +1100 Subject: [PATCH 222/394] Remove singapore drand testnet host from config (#11657) --- build/drand.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/build/drand.go b/build/drand.go index 3b976ac92..abf1a2846 100644 --- a/build/drand.go +++ b/build/drand.go @@ -48,12 +48,10 @@ var DrandConfigs = map[DrandEnum]dtypes.DrandConfig{ Servers: []string{ "https://pl-eu.testnet.drand.sh", "https://pl-us.testnet.drand.sh", - "https://pl-sin.testnet.drand.sh", }, Relays: []string{ "/dnsaddr/pl-eu.testnet.drand.sh/", "/dnsaddr/pl-us.testnet.drand.sh/", - "/dnsaddr/pl-sin.testnet.drand.sh/", }, ChainInfoJSON: `{"public_key":"922a2e93828ff83345bae533f5172669a26c02dc76d6bf59c80892e12ab1455c229211886f35bb56af6d5bea981024df","period":25,"genesis_time":1590445175,"hash":"84b2234fb34e835dccd048255d7ad3194b81af7d978c3bf157e3469592ae4e02","groupHash":"4dd408e5fdff9323c76a9b6f087ba8fdc5a6da907bd9217d9d10f2287d081957"}`, }, From d3ca54d617f4783a1a492993f06e737ea87a5834 Mon Sep 17 00:00:00 2001 From: "Andrew Jackson (Ajax)" Date: Tue, 27 Feb 2024 10:47:58 -0800 Subject: [PATCH 223/394] harmony storage (#11647) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * harmony storage * clean-up in lotus-provider * lints * Do() api change * rm Do() chg and storagemgr * harmony: Address storage iface review --------- Co-authored-by: Łukasz Magiera --- cmd/lotus-provider/deps/deps.go | 1 + itests/harmonytask_test.go | 8 +++---- lib/harmony/harmonytask/task_type_handler.go | 25 ++++++++++++++++++++ lib/harmony/resources/resources.go | 13 ++++++++++ 4 files changed, 43 insertions(+), 4 deletions(-) diff --git a/cmd/lotus-provider/deps/deps.go b/cmd/lotus-provider/deps/deps.go index 31e3058c1..cad7602bb 100644 --- a/cmd/lotus-provider/deps/deps.go +++ b/cmd/lotus-provider/deps/deps.go @@ -253,6 +253,7 @@ Get it with: jq .PrivateKey ~/.lotus-miner/keystore/MF2XI2BNNJ3XILLQOJUXMYLUMU`, } } } + return nil } diff --git a/itests/harmonytask_test.go b/itests/harmonytask_test.go index ab54cbef4..463f131d8 100644 --- a/itests/harmonytask_test.go +++ b/itests/harmonytask_test.go @@ -37,13 +37,13 @@ func withDbSetup(t *testing.T, f func(*kit.TestMiner)) { f(miner) } -func (t *task1) Do(tID harmonytask.TaskID, stillOwned func() bool) (done bool, err error) { +func (t *task1) Do(taskID harmonytask.TaskID, stillOwned func() bool) (done bool, err error) { if !stillOwned() { return false, errors.New("Why not still owned?") } t.myPersonalTableLock.Lock() defer t.myPersonalTableLock.Unlock() - t.WorkCompleted = append(t.WorkCompleted, fmt.Sprintf("taskResult%d", t.myPersonalTable[tID])) + t.WorkCompleted = append(t.WorkCompleted, fmt.Sprintf("taskResult%d", t.myPersonalTable[taskID])) return true, nil } func (t *task1) CanAccept(list []harmonytask.TaskID, e *harmonytask.TaskEngine) (*harmonytask.TaskID, error) { @@ -104,8 +104,8 @@ type passthru struct { adder func(add harmonytask.AddTaskFunc) } -func (t *passthru) Do(tID harmonytask.TaskID, stillOwned func() bool) (done bool, err error) { - return t.do(tID, stillOwned) +func (t *passthru) Do(taskID harmonytask.TaskID, stillOwned func() bool) (done bool, err error) { + return t.do(taskID, stillOwned) } func (t *passthru) CanAccept(list []harmonytask.TaskID, e *harmonytask.TaskEngine) (*harmonytask.TaskID, error) { return t.canAccept(list, e) diff --git a/lib/harmony/harmonytask/task_type_handler.go b/lib/harmony/harmonytask/task_type_handler.go index 5702b3142..2b2521949 100644 --- a/lib/harmony/harmonytask/task_type_handler.go +++ b/lib/harmony/harmonytask/task_type_handler.go @@ -95,16 +95,34 @@ top: return false } + releaseStorage := func() { + } + if h.TaskTypeDetails.Cost.Storage != nil { + if err = h.TaskTypeDetails.Cost.Storage.Claim(int(*tID)); err != nil { + log.Infow("did not accept task", "task_id", strconv.Itoa(int(*tID)), "reason", "storage claim failed", "name", h.Name, "error", err) + return false + } + releaseStorage = func() { + if err := h.TaskTypeDetails.Cost.Storage.MarkComplete(int(*tID)); err != nil { + log.Errorw("Could not release storage", "error", err) + } + } + } + // if recovering we don't need to try to claim anything because those tasks are already claimed by us if from != workSourceRecover { // 4. Can we claim the work for our hostname? ct, err := h.TaskEngine.db.Exec(h.TaskEngine.ctx, "UPDATE harmony_task SET owner_id=$1 WHERE id=$2 AND owner_id IS NULL", h.TaskEngine.ownerID, *tID) if err != nil { log.Error(err) + + releaseStorage() return false } if ct == 0 { log.Infow("did not accept task", "task_id", strconv.Itoa(int(*tID)), "reason", "already Taken", "name", h.Name) + releaseStorage() + var tryAgain = make([]TaskID, 0, len(ids)-1) for _, id := range ids { if id != *tID { @@ -134,6 +152,7 @@ top: } h.Count.Add(-1) + releaseStorage() h.recordCompletion(*tID, workStart, done, doErr) if done { for _, fs := range h.TaskEngine.follows[h.Name] { // Do we know of any follows for this task type? @@ -247,5 +266,11 @@ func (h *taskTypeHandler) AssertMachineHasCapacity() error { if r.Gpu-h.Cost.Gpu < 0 { return errors.New("Did not accept " + h.Name + " task: out of available GPU") } + + if h.TaskTypeDetails.Cost.Storage != nil { + if !h.TaskTypeDetails.Cost.Storage.HasCapacity() { + return errors.New("Did not accept " + h.Name + " task: out of available Storage") + } + } return nil } diff --git a/lib/harmony/resources/resources.go b/lib/harmony/resources/resources.go index 406e71da3..4f67a26af 100644 --- a/lib/harmony/resources/resources.go +++ b/lib/harmony/resources/resources.go @@ -24,6 +24,19 @@ type Resources struct { Gpu float64 Ram uint64 MachineID int + Storage +} + +// Optional Storage management. +type Storage interface { + HasCapacity() bool + + // This allows some other system to claim space for this task. + Claim(taskID int) error + + // This allows some other system to consider the task done. + // It's up to the caller to remove the data, if that applies. + MarkComplete(taskID int) error } type Reg struct { Resources From 5f3675a53673889fec4daac2b0c844bc9d444880 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 27 Feb 2024 20:23:42 +0100 Subject: [PATCH 224/394] lpseal: SDR Storage revervations --- provider/lpffi/sdr_funcs.go | 63 ++++++--- provider/lpffi/task_storage.go | 241 +++++++++++++++++++++++++++++++++ provider/lpseal/task_sdr.go | 29 +++- 3 files changed, 310 insertions(+), 23 deletions(-) create mode 100644 provider/lpffi/task_storage.go diff --git a/provider/lpffi/sdr_funcs.go b/provider/lpffi/sdr_funcs.go index daf7d5381..05a7bbd80 100644 --- a/provider/lpffi/sdr_funcs.go +++ b/provider/lpffi/sdr_funcs.go @@ -11,6 +11,7 @@ import ( "github.com/KarpelesLab/reflink" "github.com/ipfs/go-cid" logging "github.com/ipfs/go-log/v2" + "github.com/puzpuzpuz/xsync/v2" "golang.org/x/xerrors" ffi "github.com/filecoin-project/filecoin-ffi" @@ -18,6 +19,7 @@ import ( "github.com/filecoin-project/go-state-types/abi" proof2 "github.com/filecoin-project/go-state-types/proof" + "github.com/filecoin-project/lotus/lib/harmony/harmonytask" "github.com/filecoin-project/lotus/provider/lpproof" "github.com/filecoin-project/lotus/storage/paths" "github.com/filecoin-project/lotus/storage/sealer/proofpaths" @@ -43,28 +45,51 @@ type SealCalls struct { func NewSealCalls(st paths.Store, ls *paths.Local, si paths.SectorIndex) *SealCalls { return &SealCalls{ sectors: &storageProvider{ - storage: st, - localStore: ls, - sindex: si, + storage: st, + localStore: ls, + sindex: si, + storageReservations: xsync.NewIntegerMapOf[harmonytask.TaskID, *StorageReservation](), }, } } type storageProvider struct { - storage paths.Store - localStore *paths.Local - sindex paths.SectorIndex + storage paths.Store + localStore *paths.Local + sindex paths.SectorIndex + storageReservations *xsync.MapOf[harmonytask.TaskID, *StorageReservation] } -func (l *storageProvider) AcquireSector(ctx context.Context, sector storiface.SectorRef, existing storiface.SectorFileType, allocate storiface.SectorFileType, sealing storiface.PathType) (storiface.SectorPaths, func(), error) { - paths, storageIDs, err := l.storage.AcquireSector(ctx, sector, existing, allocate, sealing, storiface.AcquireMove) - if err != nil { - return storiface.SectorPaths{}, nil, err - } +func (l *storageProvider) AcquireSector(ctx context.Context, taskID *harmonytask.TaskID, sector storiface.SectorRef, existing, allocate storiface.SectorFileType, sealing storiface.PathType) (storiface.SectorPaths, func(), error) { + var paths, storageIDs storiface.SectorPaths + var releaseStorage func() - releaseStorage, err := l.localStore.Reserve(ctx, sector, allocate, storageIDs, storiface.FSOverheadSeal) - if err != nil { - return storiface.SectorPaths{}, nil, xerrors.Errorf("reserving storage space: %w", err) + var ok bool + var resv *StorageReservation + if taskID != nil { + resv, ok = l.storageReservations.Load(*taskID) + } + if ok { + if resv.Alloc != allocate || resv.Existing != existing { + // this should never happen, only when task definition is wrong + return storiface.SectorPaths{}, nil, xerrors.Errorf("storage reservation type mismatch") + } + + log.Debugw("using existing storage reservation", "task", taskID, "sector", sector, "existing", existing, "allocate", allocate) + + paths = resv.Paths + releaseStorage = resv.Release + } else { + var err error + paths, storageIDs, err = l.storage.AcquireSector(ctx, sector, existing, allocate, sealing, storiface.AcquireMove) + if err != nil { + return storiface.SectorPaths{}, nil, err + } + + releaseStorage, err = l.localStore.Reserve(ctx, sector, allocate, storageIDs, storiface.FSOverheadSeal) + if err != nil { + return storiface.SectorPaths{}, nil, xerrors.Errorf("reserving storage space: %w", err) + } } log.Debugf("acquired sector %d (e:%d; a:%d): %v", sector, existing, allocate, paths) @@ -85,8 +110,8 @@ func (l *storageProvider) AcquireSector(ctx context.Context, sector storiface.Se }, nil } -func (sb *SealCalls) GenerateSDR(ctx context.Context, sector storiface.SectorRef, ticket abi.SealRandomness, commKcid cid.Cid) error { - paths, releaseSector, err := sb.sectors.AcquireSector(ctx, sector, storiface.FTNone, storiface.FTCache, storiface.PathSealing) +func (sb *SealCalls) GenerateSDR(ctx context.Context, taskID harmonytask.TaskID, sector storiface.SectorRef, ticket abi.SealRandomness, commKcid cid.Cid) error { + paths, releaseSector, err := sb.sectors.AcquireSector(ctx, &taskID, sector, storiface.FTNone, storiface.FTCache, storiface.PathSealing) if err != nil { return xerrors.Errorf("acquiring sector paths: %w", err) } @@ -120,7 +145,7 @@ func (sb *SealCalls) TreeD(ctx context.Context, sector storiface.SectorRef, size maybeUns := storiface.FTNone // todo sectors with data - paths, releaseSector, err := sb.sectors.AcquireSector(ctx, sector, storiface.FTCache, maybeUns, storiface.PathSealing) + paths, releaseSector, err := sb.sectors.AcquireSector(ctx, nil, sector, storiface.FTCache, maybeUns, storiface.PathSealing) if err != nil { return cid.Undef, xerrors.Errorf("acquiring sector paths: %w", err) } @@ -135,7 +160,7 @@ func (sb *SealCalls) TreeRC(ctx context.Context, sector storiface.SectorRef, uns return cid.Undef, cid.Undef, xerrors.Errorf("make phase1 output: %w", err) } - paths, releaseSector, err := sb.sectors.AcquireSector(ctx, sector, storiface.FTCache, storiface.FTSealed, storiface.PathSealing) + paths, releaseSector, err := sb.sectors.AcquireSector(ctx, nil, sector, storiface.FTCache, storiface.FTSealed, storiface.PathSealing) if err != nil { return cid.Undef, cid.Undef, xerrors.Errorf("acquiring sector paths: %w", err) } @@ -331,7 +356,7 @@ func (sb *SealCalls) FinalizeSector(ctx context.Context, sector storiface.Sector alloc = storiface.FTUnsealed } - sectorPaths, releaseSector, err := sb.sectors.AcquireSector(ctx, sector, storiface.FTCache, alloc, storiface.PathSealing) + sectorPaths, releaseSector, err := sb.sectors.AcquireSector(ctx, nil, sector, storiface.FTCache, alloc, storiface.PathSealing) if err != nil { return xerrors.Errorf("acquiring sector paths: %w", err) } diff --git a/provider/lpffi/task_storage.go b/provider/lpffi/task_storage.go new file mode 100644 index 000000000..01d6671c7 --- /dev/null +++ b/provider/lpffi/task_storage.go @@ -0,0 +1,241 @@ +package lpffi + +import ( + "context" + "time" + + "golang.org/x/xerrors" + + "github.com/filecoin-project/go-state-types/abi" + + "github.com/filecoin-project/lotus/lib/harmony/harmonytask" + "github.com/filecoin-project/lotus/lib/harmony/resources" + "github.com/filecoin-project/lotus/lib/must" + "github.com/filecoin-project/lotus/storage/sealer/storiface" +) + +type SectorRef struct { + SpID int64 `db:"sp_id"` + SectorNumber int64 `db:"sector_number"` + RegSealProof abi.RegisteredSealProof `db:"reg_seal_proof"` +} + +func (sr SectorRef) ID() abi.SectorID { + return abi.SectorID{ + Miner: abi.ActorID(sr.SpID), + Number: abi.SectorNumber(sr.SectorNumber), + } +} + +func (sr SectorRef) Ref() storiface.SectorRef { + return storiface.SectorRef{ + ID: sr.ID(), + ProofType: sr.RegSealProof, + } +} + +type TaskStorage struct { + sc *SealCalls + + alloc, existing storiface.SectorFileType + ssize abi.SectorSize + pathType storiface.PathType + + taskToSectorRef func(taskID harmonytask.TaskID) (SectorRef, error) +} + +type ReleaseStorageFunc func() // free storage reservation + +type StorageReservation struct { + SectorRef SectorRef + Release ReleaseStorageFunc + Paths storiface.SectorPaths + PathIDs storiface.SectorPaths + + Alloc, Existing storiface.SectorFileType +} + +func (sb *SealCalls) Storage(taskToSectorRef func(taskID harmonytask.TaskID) (SectorRef, error), alloc, existing storiface.SectorFileType, ssize abi.SectorSize, pathType storiface.PathType) *TaskStorage { + return &TaskStorage{ + sc: sb, + alloc: alloc, + existing: existing, + ssize: ssize, + pathType: pathType, + taskToSectorRef: taskToSectorRef, + } +} + +func (t *TaskStorage) HasCapacity() bool { + ctx := context.Background() + + paths, err := t.sc.sectors.sindex.StorageBestAlloc(ctx, t.alloc, t.ssize, t.pathType) + if err != nil { + log.Errorf("finding best alloc in HasCapacity: %+v", err) + return false + } + + local, err := t.sc.sectors.localStore.Local(ctx) + if err != nil { + log.Errorf("getting local storage: %+v", err) + return false + } + + for _, path := range paths { + if t.pathType == storiface.PathStorage && !path.CanStore { + continue // we want to store, and this isn't a store path + } + if t.pathType == storiface.PathSealing && !path.CanSeal { + continue // we want to seal, and this isn't a seal path + } + + // check if this path is on this node + var found bool + for _, storagePath := range local { + if storagePath.ID == path.ID { + found = true + break + } + } + if !found { + // this path isn't on this node + continue + } + + // StorageBestAlloc already checks that there is enough space; Not atomic like reserving space, but it's + // good enough for HasCapacity + return true + } + + return false // no path found +} + +func (t *TaskStorage) Claim(taskID int) error { + ctx := context.Background() + + sectorRef, err := t.taskToSectorRef(harmonytask.TaskID(taskID)) + if err != nil { + return xerrors.Errorf("getting sector ref: %w", err) + } + + // storage writelock sector + lkctx, cancel := context.WithCancel(ctx) + + allocate := storiface.FTCache + + lockAcquireTimuout := time.Second * 10 + lockAcquireTimer := time.NewTimer(lockAcquireTimuout) + + go func() { + defer cancel() + + select { + case <-lockAcquireTimer.C: + case <-ctx.Done(): + } + }() + + if err := t.sc.sectors.sindex.StorageLock(lkctx, sectorRef.ID(), storiface.FTNone, allocate); err != nil { + // timer will expire + return xerrors.Errorf("claim StorageLock: %w", err) + } + + if !lockAcquireTimer.Stop() { + // timer expired, so lkctx is done, and that means the lock was acquired and dropped.. + return xerrors.Errorf("failed to acquire lock") + } + defer func() { + // make sure we release the sector lock + lockAcquireTimer.Reset(0) + }() + + // find anywhere + // if found return nil, for now + s, err := t.sc.sectors.sindex.StorageFindSector(ctx, sectorRef.ID(), allocate, must.One(sectorRef.RegSealProof.SectorSize()), false) + if err != nil { + return xerrors.Errorf("claim StorageFindSector: %w", err) + } + + lp, err := t.sc.sectors.localStore.Local(ctx) + if err != nil { + return err + } + + // see if there are any non-local sector files in storage + for _, info := range s { + for _, l := range lp { + if l.ID == info.ID { + continue + } + + // TODO: Create reservation for fetching; This will require quite a bit more refactoring, but for now we'll + // only care about new allocations + return nil + } + } + + // acquire a path to make a reservation in + pathsFs, pathIDs, err := t.sc.sectors.localStore.AcquireSector(ctx, sectorRef.Ref(), storiface.FTNone, allocate, storiface.PathSealing, storiface.AcquireMove) + if err != nil { + return err + } + + // reserve the space + release, err := t.sc.sectors.localStore.Reserve(ctx, sectorRef.Ref(), allocate, pathIDs, storiface.FSOverheadSeal) + if err != nil { + return err + } + + sres := &StorageReservation{ + SectorRef: sectorRef, + Release: release, + Paths: pathsFs, + PathIDs: pathIDs, + + Alloc: t.alloc, + Existing: t.existing, + } + + t.sc.sectors.storageReservations.Store(harmonytask.TaskID(taskID), sres) + + log.Debugw("claimed storage", "task_id", taskID, "sector", sectorRef.ID(), "paths", pathsFs) + + // note: we drop the sector writelock on return; THAT IS INTENTIONAL, this code runs in CanAccept, which doesn't + // guarantee that the work for this sector will happen on this node; SDR CanAccept just ensures that the node can + // run the job, harmonytask is what ensures that only one SDR runs at a time + return nil +} + +func (t *TaskStorage) MarkComplete(taskID int) error { + // MarkComplete is ALWAYS called after the task is done or not scheduled + // If Claim is called and returns without errors, MarkComplete with the same + // taskID is guaranteed to eventually be called + + sectorRef, err := t.taskToSectorRef(harmonytask.TaskID(taskID)) + if err != nil { + return xerrors.Errorf("getting sector ref: %w", err) + } + + sres, ok := t.sc.sectors.storageReservations.Load(harmonytask.TaskID(taskID)) + if !ok { + return xerrors.Errorf("no reservation found for task %d", taskID) + } + + if sectorRef != sres.SectorRef { + return xerrors.Errorf("reservation sector ref doesn't match task sector ref: %+v != %+v", sectorRef, sres.SectorRef) + } + + log.Debugw("marking storage complete", "task_id", taskID, "sector", sectorRef.ID(), "paths", sres.Paths) + + // remove the reservation + t.sc.sectors.storageReservations.Delete(harmonytask.TaskID(taskID)) + + // release the reservation + sres.Release() + + // note: this only frees the reservation, allocated sectors are declared in AcquireSector which is aware of + // the reservation + return nil +} + +var _ resources.Storage = &TaskStorage{} diff --git a/provider/lpseal/task_sdr.go b/provider/lpseal/task_sdr.go index 68dca5bde..694ff6f46 100644 --- a/provider/lpseal/task_sdr.go +++ b/provider/lpseal/task_sdr.go @@ -146,7 +146,7 @@ func (s *SDRTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (done bo // Trees; After one retry, it should return the sector to the // SDR stage; max number of retries should be configurable - err = s.sc.GenerateSDR(ctx, sref, ticket, commd) + err = s.sc.GenerateSDR(ctx, taskID, sref, ticket, commd) if err != nil { return false, xerrors.Errorf("generating sdr: %w", err) } @@ -194,13 +194,19 @@ func (s *SDRTask) CanAccept(ids []harmonytask.TaskID, engine *harmonytask.TaskEn } func (s *SDRTask) TypeDetails() harmonytask.TaskTypeDetails { + ssize := abi.SectorSize(32 << 30) // todo task details needs taskID to get correct sector size + if isDevnet { + ssize = abi.SectorSize(2 << 20) + } + res := harmonytask.TaskTypeDetails{ Max: s.max, Name: "SDR", Cost: resources.Resources{ // todo offset for prefetch? - Cpu: 4, // todo multicore sdr - Gpu: 0, - Ram: 54 << 30, + Cpu: 4, // todo multicore sdr + Gpu: 0, + Ram: 54 << 30, + Storage: s.sc.Storage(s.taskToSector, storiface.FTCache, storiface.FTNone, ssize, storiface.PathSealing), }, MaxFailures: 2, Follows: nil, @@ -217,4 +223,19 @@ func (s *SDRTask) Adder(taskFunc harmonytask.AddTaskFunc) { s.sp.pollers[pollerSDR].Set(taskFunc) } +func (s *SDRTask) taskToSector(id harmonytask.TaskID) (lpffi.SectorRef, error) { + var refs []lpffi.SectorRef + + err := s.db.Select(context.Background(), &refs, `SELECT sp_id, sector_number, reg_seal_proof FROM sectors_sdr_pipeline WHERE task_id_sdr = $1`, id) + if err != nil { + return lpffi.SectorRef{}, xerrors.Errorf("getting sector ref: %w", err) + } + + if len(refs) != 1 { + return lpffi.SectorRef{}, xerrors.Errorf("expected 1 sector ref, got %d", len(refs)) + } + + return refs[0], nil +} + var _ harmonytask.TaskInterface = &SDRTask{} From eff815dc9e72b40c31d8d83475c762f9dc1a4d27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 27 Feb 2024 23:16:57 +0100 Subject: [PATCH 225/394] lpseal: Fix message retry --- provider/lpseal/poller_commit_msg.go | 2 +- provider/lpseal/poller_precommit_msg.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/provider/lpseal/poller_commit_msg.go b/provider/lpseal/poller_commit_msg.go index d17ccd906..19784e719 100644 --- a/provider/lpseal/poller_commit_msg.go +++ b/provider/lpseal/poller_commit_msg.go @@ -97,7 +97,7 @@ func (s *SealPoller) pollRetryCommitMsgSend(ctx context.Context, task pollTask, // make the pipeline entry seem like precommit send didn't happen, next poll loop will retry _, err := s.db.Exec(ctx, `UPDATE sectors_sdr_pipeline SET - commit_msg_cid = NULL, task_id_commit_msg = NULL + commit_msg_cid = NULL, task_id_commit_msg = NULL, after_commit_msg = FALSE WHERE commit_msg_cid = $1 AND sp_id = $2 AND sector_number = $3 AND after_commit_msg_success = FALSE`, *execResult.CommitMsgCID, task.SpID, task.SectorNumber) if err != nil { diff --git a/provider/lpseal/poller_precommit_msg.go b/provider/lpseal/poller_precommit_msg.go index 5f6958b53..22deadc0e 100644 --- a/provider/lpseal/poller_precommit_msg.go +++ b/provider/lpseal/poller_precommit_msg.go @@ -108,7 +108,7 @@ func (s *SealPoller) pollRetryPrecommitMsgSend(ctx context.Context, task pollTas // make the pipeline entry seem like precommit send didn't happen, next poll loop will retry _, err := s.db.Exec(ctx, `UPDATE sectors_sdr_pipeline SET - precommit_msg_cid = NULL, task_id_precommit_msg = NULL + precommit_msg_cid = NULL, task_id_precommit_msg = NULL, after_precommit_msg = FALSE WHERE precommit_msg_cid = $1 AND sp_id = $2 AND sector_number = $3 AND after_precommit_msg_success = FALSE`, *execResult.PrecommitMsgCID, task.SpID, task.SectorNumber) if err != nil { From 7b911594ac8dd4e39d061745f74112aeeb589fa1 Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Sat, 2 Mar 2024 03:32:14 +1100 Subject: [PATCH 226/394] Use existing trusted info for drand client test (#11666) Ref: https://github.com/drand/drand/pull/1317 --- chain/beacon/drand/drand_test.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/chain/beacon/drand/drand_test.go b/chain/beacon/drand/drand_test.go index 7434241a5..355d6937f 100644 --- a/chain/beacon/drand/drand_test.go +++ b/chain/beacon/drand/drand_test.go @@ -3,6 +3,7 @@ package drand import ( + "bytes" "context" "os" "testing" @@ -18,7 +19,12 @@ import ( func TestPrintGroupInfo(t *testing.T) { server := build.DrandConfigs[build.DrandTestnet].Servers[0] - c, err := hclient.New(server, nil, nil) + chainInfo := build.DrandConfigs[build.DrandTestnet].ChainInfoJSON + + drandChain, err := dchain.InfoFromJSON(bytes.NewReader([]byte(chainInfo))) + assert.NoError(t, err) + c, err := hclient.NewWithInfo(server, drandChain, nil) + assert.NoError(t, err) cg := c.(interface { FetchChainInfo(ctx context.Context, groupHash []byte) (*dchain.Info, error) From 9c32c3321c12aa8eff5b0a070214b6e9a75786cc Mon Sep 17 00:00:00 2001 From: Phi Date: Mon, 4 Mar 2024 07:43:59 +0100 Subject: [PATCH 227/394] Update calibnet bootstrap peers Update calibnet bootstrap peers --- build/bootstrap/calibnet.pi | 4 ---- 1 file changed, 4 deletions(-) diff --git a/build/bootstrap/calibnet.pi b/build/bootstrap/calibnet.pi index e41ae94c6..0a14d24ba 100644 --- a/build/bootstrap/calibnet.pi +++ b/build/bootstrap/calibnet.pi @@ -1,7 +1,3 @@ -/dns4/bootstrap-0.calibration.fildev.network/tcp/1347/p2p/12D3KooWCi2w8U4DDB9xqrejb5KYHaQv2iA2AJJ6uzG3iQxNLBMy -/dns4/bootstrap-1.calibration.fildev.network/tcp/1347/p2p/12D3KooWDTayrBojBn9jWNNUih4nNQQBGJD7Zo3gQCKgBkUsS6dp -/dns4/bootstrap-2.calibration.fildev.network/tcp/1347/p2p/12D3KooWNRxTHUn8bf7jz1KEUPMc2dMgGfa4f8ZJTsquVSn3vHCG -/dns4/bootstrap-3.calibration.fildev.network/tcp/1347/p2p/12D3KooWFWUqE9jgXvcKHWieYs9nhyp6NF4ftwLGAHm4sCv73jjK /dns4/calibration.node.glif.io/tcp/1237/p2p/12D3KooWQPYouEAsUQKzvFUA9sQ8tz4rfpqtTzh2eL6USd9bwg7x /dns4/bootstrap-calibnet-0.chainsafe-fil.io/tcp/34000/p2p/12D3KooWABQ5gTDHPWyvhJM7jPhtNwNJruzTEo32Lo4gcS5ABAMm /dns4/bootstrap-calibnet-1.chainsafe-fil.io/tcp/34000/p2p/12D3KooWS3ZRhMYL67b4bD5XQ6fcpTyVQXnDe8H89LvwrDqaSbiT From 53401129223afd024645032646b6d5a0ba0578de Mon Sep 17 00:00:00 2001 From: Jiaying Wang <42981373+jennijuju@users.noreply.github.com> Date: Mon, 4 Mar 2024 18:22:54 +0800 Subject: [PATCH 228/394] Update CODEOWNERS These are the folks who are actively contributing to lotus maintenance, issue triaging, PR reviews and development. Made this change to decouple lotus ownership with other filecoin core repo's ownership and maintainership that comes with lotus-maintainer perm --- .github/CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index b8ec66f00..460f07f7b 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -3,4 +3,4 @@ # Global owners # Ensure maintainers team is a requested reviewer for non-draft PRs -* @filecoin-project/lotus-maintainers +* @jennijuju @Stebalien @snadrus @aarshkshah1992 @magik6k @zengroud0 @arajasek @rjan90 @masih @rvagg From 712e9fd062edaf6347e9d074203fc145bdca9e42 Mon Sep 17 00:00:00 2001 From: Jiaying Wang <42981373+jennijuju@users.noreply.github.com> Date: Mon, 4 Mar 2024 18:24:00 +0800 Subject: [PATCH 229/394] Update CODEOWNERS --- .github/CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 460f07f7b..c6570734a 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -3,4 +3,4 @@ # Global owners # Ensure maintainers team is a requested reviewer for non-draft PRs -* @jennijuju @Stebalien @snadrus @aarshkshah1992 @magik6k @zengroud0 @arajasek @rjan90 @masih @rvagg +* @jennijuju @Stebalien @snadrus @aarshkshah1992 @magik6k @zenground0 @arajasek @rjan90 @masih @rvagg From e84a61cc7dc76aadac557c53ad777674d2a5bb73 Mon Sep 17 00:00:00 2001 From: LexLuthr <88259624+LexLuthr@users.noreply.github.com> Date: Tue, 5 Mar 2024 01:09:23 +0400 Subject: [PATCH 230/394] feat: CLI: add expiration to claim list (#11671) * add expiration to claim list * fix expired threshold --- cli/filplus.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/filplus.go b/cli/filplus.go index 9fbd2a489..569229439 100644 --- a/cli/filplus.go +++ b/cli/filplus.go @@ -379,7 +379,7 @@ var filplusListClaimsCmd = &cli.Command{ ) for claimId, claim := range claimsMap { - if ts.Height() > claim.TermMax || !cctx.IsSet("expired") { + if ts.Height() > claim.TermMax+claim.TermStart || !cctx.IsSet("expired") { tw.Write(map[string]interface{}{ "ID": claimId, "Provider": claim.Provider, From 83cecdbd57ab39a527eceba1fd036b9d0e7e3871 Mon Sep 17 00:00:00 2001 From: tom123222 <160735201+tom123222@users.noreply.github.com> Date: Tue, 5 Mar 2024 06:07:14 +0800 Subject: [PATCH 231/394] chore: fix two typo (#11670) --- chain/consensus/filcns/upgrades.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/chain/consensus/filcns/upgrades.go b/chain/consensus/filcns/upgrades.go index d7e0914fe..552d2089f 100644 --- a/chain/consensus/filcns/upgrades.go +++ b/chain/consensus/filcns/upgrades.go @@ -1695,14 +1695,14 @@ func upgradeActorsV10Common( if stateRoot.Version != types.StateTreeVersion4 { return cid.Undef, xerrors.Errorf( - "expected state root version 4 for actors v9 upgrade, got %d", + "expected state root version 4 for actors v10 upgrade, got %d", stateRoot.Version, ) } manifest, ok := actors.GetManifest(actorstypes.Version10) if !ok { - return cid.Undef, xerrors.Errorf("no manifest CID for v9 upgrade") + return cid.Undef, xerrors.Errorf("no manifest CID for v10 upgrade") } // Perform the migration From d0753d1bdb44d6eca31e2de941ec8a76f8a1d2e4 Mon Sep 17 00:00:00 2001 From: LexLuthr Date: Tue, 5 Mar 2024 13:03:49 +0400 Subject: [PATCH 232/394] fix lint error --- cmd/lotus-provider/deps/deps.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/lotus-provider/deps/deps.go b/cmd/lotus-provider/deps/deps.go index 58d671a10..d38110dd1 100644 --- a/cmd/lotus-provider/deps/deps.go +++ b/cmd/lotus-provider/deps/deps.go @@ -256,7 +256,7 @@ Get it with: jq .PrivateKey ~/.lotus-miner/keystore/MF2XI2BNNJ3XILLQOJUXMYLUMU`, } } } - + if deps.ProofTypes == nil { deps.ProofTypes = map[abi.RegisteredSealProof]bool{} } From 40f3071364cde56aba5bb7865a79c6c0ac930678 Mon Sep 17 00:00:00 2001 From: LexLuthr <88259624+LexLuthr@users.noreply.github.com> Date: Wed, 6 Mar 2024 13:41:37 +0400 Subject: [PATCH 233/394] fix: curio: base config by default (#11676) * base config by default * remove global --layers flag --- cmd/lotus-provider/config.go | 9 ++-- cmd/lotus-provider/config_new.go | 2 +- cmd/lotus-provider/deps/deps.go | 2 +- cmd/lotus-provider/main.go | 6 --- cmd/lotus-provider/pipeline.go | 4 ++ cmd/lotus-provider/proving.go | 5 +- cmd/lotus-provider/run.go | 8 +++ documentation/en/cli-lotus-provider.md | 75 +++++++++++++------------- 8 files changed, 61 insertions(+), 50 deletions(-) diff --git a/cmd/lotus-provider/config.go b/cmd/lotus-provider/config.go index 670e05b9d..1f155f543 100644 --- a/cmd/lotus-provider/config.go +++ b/cmd/lotus-provider/config.go @@ -22,7 +22,7 @@ import ( var configCmd = &cli.Command{ Name: "config", - Usage: "Manage node config by layers. The layer 'base' will always be applied. ", + Usage: "Manage node config by layers. The layer 'base' will always be applied at Curio start-up.", Subcommands: []*cli.Command{ configDefaultCmd, configSetCmd, @@ -168,7 +168,7 @@ func getConfig(db *harmonydb.DB, layer string) (string, error) { var configListCmd = &cli.Command{ Name: "list", Aliases: []string{"ls"}, - Usage: "List config layers you can get.", + Usage: "List config layers present in the DB.", Flags: []cli.Flag{}, Action: func(cctx *cli.Context) error { db, err := deps.MakeDB(cctx) @@ -221,8 +221,7 @@ var configViewCmd = &cli.Command{ Flags: []cli.Flag{ &cli.StringSliceFlag{ Name: "layers", - Usage: "comma or space separated list of layers to be interpreted", - Value: cli.NewStringSlice("base"), + Usage: "comma or space separated list of layers to be interpreted (base is always applied)", Required: true, }, }, @@ -261,7 +260,7 @@ var configEditCmd = &cli.Command{ DefaultText: "", }, &cli.BoolFlag{ - Name: "allow-owerwrite", + Name: "allow-overwrite", Usage: "allow overwrite of existing layer if source is a different layer", }, &cli.BoolFlag{ diff --git a/cmd/lotus-provider/config_new.go b/cmd/lotus-provider/config_new.go index 38949959a..e9d8382ac 100644 --- a/cmd/lotus-provider/config_new.go +++ b/cmd/lotus-provider/config_new.go @@ -25,7 +25,7 @@ import ( var configNewCmd = &cli.Command{ Name: "new-cluster", - Usage: "Create new coniguration for a new cluster", + Usage: "Create new configuration for a new cluster", ArgsUsage: "[SP actor address...]", Flags: []cli.Flag{ &cli.StringFlag{ diff --git a/cmd/lotus-provider/deps/deps.go b/cmd/lotus-provider/deps/deps.go index d38110dd1..e03e7dd24 100644 --- a/cmd/lotus-provider/deps/deps.go +++ b/cmd/lotus-provider/deps/deps.go @@ -289,7 +289,7 @@ func LoadConfigWithUpgrades(text string, lp *config.LotusProviderConfig) (toml.M func GetConfig(cctx *cli.Context, db *harmonydb.DB) (*config.LotusProviderConfig, error) { lp := config.DefaultLotusProvider() have := []string{} - layers := cctx.StringSlice("layers") + layers := append([]string{"base"}, cctx.StringSlice("layers")...) // Always stack on top of "base" layer for _, layer := range layers { text := "" err := db.QueryRow(cctx.Context, `SELECT config FROM harmony_config WHERE title=$1`, layer).Scan(&text) diff --git a/cmd/lotus-provider/main.go b/cmd/lotus-provider/main.go index bfb17f200..b4342d49a 100644 --- a/cmd/lotus-provider/main.go +++ b/cmd/lotus-provider/main.go @@ -124,12 +124,6 @@ func main() { Hidden: true, Value: "5433", }, - &cli.StringSliceFlag{ - Name: "layers", - EnvVars: []string{"CURIO_LAYERS"}, - Usage: "list of layers to be interpreted (atop defaults). Default: base", - Value: cli.NewStringSlice("base"), - }, &cli.StringFlag{ Name: deps.FlagRepoPath, EnvVars: []string{"LOTUS_REPO_PATH"}, diff --git a/cmd/lotus-provider/pipeline.go b/cmd/lotus-provider/pipeline.go index 45a1c3a17..74c49b16a 100644 --- a/cmd/lotus-provider/pipeline.go +++ b/cmd/lotus-provider/pipeline.go @@ -52,6 +52,10 @@ var sealStartCmd = &cli.Command{ Usage: "Use synthetic PoRep", Value: false, // todo implement synthetic }, + &cli.StringSliceFlag{ + Name: "layers", + Usage: "list of layers to be interpreted (atop defaults). Default: base", + }, }, Action: func(cctx *cli.Context) error { if !cctx.Bool("now") { diff --git a/cmd/lotus-provider/proving.go b/cmd/lotus-provider/proving.go index cfe7e921a..c003c907f 100644 --- a/cmd/lotus-provider/proving.go +++ b/cmd/lotus-provider/proving.go @@ -54,6 +54,10 @@ var wdPostTaskCmd = &cli.Command{ Usage: "deadline to compute WindowPoSt for ", Value: 0, }, + &cli.StringSliceFlag{ + Name: "layers", + Usage: "list of layers to be interpreted (atop defaults). Default: base", + }, }, Action: func(cctx *cli.Context) error { ctx := context.Background() @@ -140,7 +144,6 @@ It will not send any messages to the chain. Since it can compute any deadline, o &cli.StringSliceFlag{ Name: "layers", Usage: "list of layers to be interpreted (atop defaults). Default: base", - Value: cli.NewStringSlice("base"), }, &cli.StringFlag{ Name: "storage-json", diff --git a/cmd/lotus-provider/run.go b/cmd/lotus-provider/run.go index 5135f8a0b..018f746f0 100644 --- a/cmd/lotus-provider/run.go +++ b/cmd/lotus-provider/run.go @@ -60,6 +60,10 @@ var runCmd = &cli.Command{ Usage: "path to journal files", Value: "~/.lotus-provider/", }, + &cli.StringSliceFlag{ + Name: "layers", + Usage: "list of layers to be interpreted (atop defaults). Default: base", + }, }, Action: func(cctx *cli.Context) (err error) { defer func() { @@ -153,6 +157,10 @@ var webCmd = &cli.Command{ Name: "nosync", Usage: "don't check full-node sync status", }, + &cli.StringSliceFlag{ + Name: "layers", + Usage: "list of layers to be interpreted (atop defaults). Default: base", + }, }, Action: func(cctx *cli.Context) error { db, err := deps.MakeDB(cctx) diff --git a/documentation/en/cli-lotus-provider.md b/documentation/en/cli-lotus-provider.md index e424bce92..984d1a2d5 100644 --- a/documentation/en/cli-lotus-provider.md +++ b/documentation/en/cli-lotus-provider.md @@ -13,7 +13,7 @@ COMMANDS: cli Execute cli commands run Start a lotus provider process stop Stop a running lotus provider - config Manage node config by layers. The layer 'base' will always be applied. + config Manage node config by layers. The layer 'base' will always be applied at Curio start-up. test Utility functions for testing web Start lotus provider web interface seal Manage the sealing pipeline @@ -26,16 +26,15 @@ COMMANDS: fetch-params Fetch proving parameters GLOBAL OPTIONS: - --color use color in display output (default: depends on output being a TTY) - --db-host value Command separated list of hostnames for yugabyte cluster (default: "yugabyte") [$LOTUS_DB_HOST] - --db-name value (default: "yugabyte") [$LOTUS_DB_NAME, $LOTUS_HARMONYDB_HOSTS] - --db-user value (default: "yugabyte") [$LOTUS_DB_USER, $LOTUS_HARMONYDB_USERNAME] - --db-password value (default: "yugabyte") [$LOTUS_DB_PASSWORD, $LOTUS_HARMONYDB_PASSWORD] - --layers value [ --layers value ] list of layers to be interpreted (atop defaults). Default: base (default: "base") [$CURIO_LAYERS] - --repo-path value (default: "~/.lotusprovider") [$LOTUS_REPO_PATH] - --vv enables very verbose mode, useful for debugging the CLI (default: false) - --help, -h show help - --version, -v print the version + --color use color in display output (default: depends on output being a TTY) + --db-host value Command separated list of hostnames for yugabyte cluster (default: "yugabyte") [$LOTUS_DB_HOST] + --db-name value (default: "yugabyte") [$LOTUS_DB_NAME, $LOTUS_HARMONYDB_HOSTS] + --db-user value (default: "yugabyte") [$LOTUS_DB_USER, $LOTUS_HARMONYDB_USERNAME] + --db-password value (default: "yugabyte") [$LOTUS_DB_PASSWORD, $LOTUS_HARMONYDB_PASSWORD] + --repo-path value (default: "~/.lotusprovider") [$LOTUS_REPO_PATH] + --vv enables very verbose mode, useful for debugging the CLI (default: false) + --help, -h show help + --version, -v print the version ``` ## lotus-provider cli @@ -68,12 +67,13 @@ USAGE: lotus-provider run [command options] [arguments...] OPTIONS: - --listen value host address and port the worker api will listen on (default: "0.0.0.0:12300") [$LOTUS_WORKER_LISTEN] - --nosync don't check full-node sync status (default: false) - --manage-fdlimit manage open file limit (default: true) - --storage-json value path to json file containing storage config (default: "~/.lotus-provider/storage.json") - --journal value path to journal files (default: "~/.lotus-provider/") - --help, -h show help + --listen value host address and port the worker api will listen on (default: "0.0.0.0:12300") [$LOTUS_WORKER_LISTEN] + --nosync don't check full-node sync status (default: false) + --manage-fdlimit manage open file limit (default: true) + --storage-json value path to json file containing storage config (default: "~/.lotus-provider/storage.json") + --journal value path to journal files (default: "~/.lotus-provider/") + --layers value [ --layers value ] list of layers to be interpreted (atop defaults). Default: base + --help, -h show help ``` ## lotus-provider stop @@ -91,7 +91,7 @@ OPTIONS: ## lotus-provider config ``` NAME: - lotus-provider config - Manage node config by layers. The layer 'base' will always be applied. + lotus-provider config - Manage node config by layers. The layer 'base' will always be applied at Curio start-up. USAGE: lotus-provider config command [command options] [arguments...] @@ -100,12 +100,12 @@ COMMANDS: default, defaults Print default node config set, add, update, create Set a config layer or the base by providing a filename or stdin. get, cat, show Get a config layer by name. You may want to pipe the output to a file, or use 'less' - list, ls List config layers you can get. + list, ls List config layers present in the DB. interpret, view, stacked, stack Interpret stacked config layers by this version of lotus-provider, with system-generated comments. remove, rm, del, delete Remove a named config layer. edit edit a config layer from-miner Express a database config (for lotus-provider) from an existing miner. - new-cluster Create new coniguration for a new cluster + new-cluster Create new configuration for a new cluster help, h Shows a list of commands or help for one command OPTIONS: @@ -153,7 +153,7 @@ OPTIONS: ### lotus-provider config list ``` NAME: - lotus-provider config list - List config layers you can get. + lotus-provider config list - List config layers present in the DB. USAGE: lotus-provider config list [command options] [arguments...] @@ -171,7 +171,7 @@ USAGE: lotus-provider config interpret [command options] a list of layers to be interpreted as the final config OPTIONS: - --layers value [ --layers value ] comma or space separated list of layers to be interpreted (default: "base") + --layers value [ --layers value ] comma or space separated list of layers to be interpreted (base is always applied) --help, -h show help ``` @@ -198,7 +198,7 @@ USAGE: OPTIONS: --editor value editor to use (default: "vim") [$EDITOR] --source value source config layer (default: ) - --allow-owerwrite allow overwrite of existing layer if source is a different layer (default: false) + --allow-overwrite allow overwrite of existing layer if source is a different layer (default: false) --no-source-diff save the whole config into the layer, not just the diff (default: false) --no-interpret-source do not interpret source layer (default: true if --source is set) --help, -h show help @@ -225,7 +225,7 @@ OPTIONS: ### lotus-provider config new-cluster ``` NAME: - lotus-provider config new-cluster - Create new coniguration for a new cluster + lotus-provider config new-cluster - Create new configuration for a new cluster USAGE: lotus-provider config new-cluster [command options] [SP actor address...] @@ -281,7 +281,7 @@ DESCRIPTION: OPTIONS: --deadline value deadline to compute WindowPoSt for (default: 0) - --layers value [ --layers value ] list of layers to be interpreted (atop defaults). Default: base (default: "base") + --layers value [ --layers value ] list of layers to be interpreted (atop defaults). Default: base --storage-json value path to json file containing storage config (default: "~/.lotus-provider/storage.json") --partition value partition to compute WindowPoSt for (default: 0) --help, -h show help @@ -296,8 +296,9 @@ USAGE: lotus-provider test window-post task [command options] [arguments...] OPTIONS: - --deadline value deadline to compute WindowPoSt for (default: 0) - --help, -h show help + --deadline value deadline to compute WindowPoSt for (default: 0) + --layers value [ --layers value ] list of layers to be interpreted (atop defaults). Default: base + --help, -h show help ``` ## lotus-provider web @@ -313,9 +314,10 @@ DESCRIPTION: This creates the 'web' layer if it does not exist, then calls run with that layer. OPTIONS: - --listen value Address to listen on (default: "127.0.0.1:4701") - --nosync don't check full-node sync status (default: false) - --help, -h show help + --listen value Address to listen on (default: "127.0.0.1:4701") + --nosync don't check full-node sync status (default: false) + --layers value [ --layers value ] list of layers to be interpreted (atop defaults). Default: base + --help, -h show help ``` ## lotus-provider seal @@ -343,12 +345,13 @@ USAGE: lotus-provider seal start [command options] [arguments...] OPTIONS: - --actor value Specify actor address to start sealing sectors for - --now Start sealing sectors for all actors now (not on schedule) (default: false) - --cc Start sealing new CC sectors (default: false) - --count value Number of sectors to start (default: 1) - --synthetic Use synthetic PoRep (default: false) - --help, -h show help + --actor value Specify actor address to start sealing sectors for + --now Start sealing sectors for all actors now (not on schedule) (default: false) + --cc Start sealing new CC sectors (default: false) + --count value Number of sectors to start (default: 1) + --synthetic Use synthetic PoRep (default: false) + --layers value [ --layers value ] list of layers to be interpreted (atop defaults). Default: base + --help, -h show help ``` ## lotus-provider version From 1d4f5397a200e7b0c0bdb17803313dc53fe28b8b Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Wed, 6 Mar 2024 11:38:06 +0000 Subject: [PATCH 234/394] Add tests to sanity-check embedded built-in actors metadata (#11684) Add tests that assert the embedded built-in actors metadata is correct: * the corresponding CAR file is present in built-in actors released assets as a CAR file. * manifest CID is the only root CID in the corresponding CAR file. * actor CIDs are present in the corresponding CAR file. Fixes #11683 --- .github/workflows/builtin-actor-tests.yml | 18 ++++ build/builtin_actors_gen_test.go | 107 ++++++++++++++++++++++ 2 files changed, 125 insertions(+) create mode 100644 .github/workflows/builtin-actor-tests.yml create mode 100644 build/builtin_actors_gen_test.go diff --git a/.github/workflows/builtin-actor-tests.yml b/.github/workflows/builtin-actor-tests.yml new file mode 100644 index 000000000..6cc49cc46 --- /dev/null +++ b/.github/workflows/builtin-actor-tests.yml @@ -0,0 +1,18 @@ +name: Built-in Actors +on: + push: + paths: + - build/actors + - build/builtin_actors_gen.go + branches: + - release/* +jobs: + release: + name: Release Tests + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-go@v3 + with: + go-version: 1.21 + - run: go test -tags=release ./build \ No newline at end of file diff --git a/build/builtin_actors_gen_test.go b/build/builtin_actors_gen_test.go new file mode 100644 index 000000000..1338097f7 --- /dev/null +++ b/build/builtin_actors_gen_test.go @@ -0,0 +1,107 @@ +//go:build release +// +build release + +package build_test + +import ( + "archive/tar" + "crypto/sha256" + "encoding/hex" + "errors" + "fmt" + "io" + "net/http" + "os" + "strings" + "testing" + + "github.com/DataDog/zstd" + "github.com/ipfs/go-cid" + "github.com/ipld/go-car/v2" + "github.com/stretchr/testify/require" + + actorstypes "github.com/filecoin-project/go-state-types/actors" + + "github.com/filecoin-project/lotus/build" +) + +func TestEmbeddedBuiltinActorsMetadata(t *testing.T) { + subjectsByVersionByNetworks := make(map[actorstypes.Version]map[string]*build.BuiltinActorsMetadata) + for _, subject := range build.EmbeddedBuiltinActorsMetadata { + if subject.BundleGitTag == "" { + // BundleGitTag is required to verify the SHA-256 checksum. + // The pack script only includes this for the latest network version, and it is good enough to only + // check the latest network version metadata. Hence the skip. + continue + } + v, ok := subjectsByVersionByNetworks[subject.Version] + if !ok { + v = make(map[string]*build.BuiltinActorsMetadata) + } + v[subject.Network] = subject + subjectsByVersionByNetworks[subject.Version] = v + } + + for version, networks := range subjectsByVersionByNetworks { + cachedCar, err := os.Open(fmt.Sprintf("./actors/v%v.tar.zst", version)) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, cachedCar.Close()) }) + tarReader := tar.NewReader(zstd.NewReader(cachedCar)) + for { + header, err := tarReader.Next() + if errors.Is(err, io.EOF) { + break + } + require.NoError(t, err) + + network := strings.TrimSuffix(strings.TrimPrefix(header.Name, "builtin-actors-"), ".car") + subject, found := networks[network] + if !found { + continue + } + + shaURL := fmt.Sprintf("https://github.com/filecoin-project/builtin-actors/releases/download/%s/builtin-actors-%s.sha256", subject.BundleGitTag, subject.Network) + resp, err := http.Get(shaURL) + require.NoError(t, err, "failed to retrieve CAR SHA") + require.Equal(t, http.StatusOK, resp.StatusCode, "unexpected response status code while retrieving CAR SHA") + + respBody, err := io.ReadAll(resp.Body) + require.NoError(t, resp.Body.Close()) + require.NoError(t, err) + fields := strings.Fields(string(respBody)) + require.Len(t, fields, 2) + wantShaHex := fields[0] + + hasher := sha256.New() + reader, err := car.NewBlockReader(io.TeeReader(tarReader, hasher)) + require.NoError(t, err) + + require.EqualValues(t, 1, reader.Version) + require.Len(t, reader.Roots, 1, "expected exactly one root CID for builtin actors bundle network %s, version %v", subject.Network, subject.Version) + require.True(t, reader.Roots[0].Equals(subject.ManifestCid), "manifest CID does not match") + + subjectActorsByCid := make(map[cid.Cid]string) + for name, c := range subject.Actors { + subjectActorsByCid[c] = name + } + for { + next, err := reader.Next() + if errors.Is(err, io.EOF) { + break + } + require.NoError(t, err) + name, found := subjectActorsByCid[next.Cid()] + if found { + t.Logf("OK: %sv%v/%s -> %s", subject.Network, subject.Version, name, next.Cid()) + delete(subjectActorsByCid, next.Cid()) + } + } + require.Empty(t, subjectActorsByCid, "ZST CAR bundle did not contain CIDs for all actors; missing: %v", subjectActorsByCid) + + gotShaHex := hex.EncodeToString(hasher.Sum(nil)) + require.Equal(t, wantShaHex, gotShaHex, "SHA-256 digest of ZST CAR bundle does not match builtin-actors release") + delete(networks, network) + } + require.Empty(t, networks, "CAR bundle did not contain CIDs for network; missing: %v", networks) + } +} From b190c4ccf02926f7d6722d6b5095877ae10a17a0 Mon Sep 17 00:00:00 2001 From: tom123222 <160735201+tom123222@users.noreply.github.com> Date: Thu, 7 Mar 2024 11:21:02 +0800 Subject: [PATCH 235/394] opt(cli): ouput deadline open time (#11678) * opt(cli): ouput deadline open time * chore: use a const for the format string --- cmd/lotus-miner/proving.go | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/cmd/lotus-miner/proving.go b/cmd/lotus-miner/proving.go index 2fc1427b5..575dded5a 100644 --- a/cmd/lotus-miner/proving.go +++ b/cmd/lotus-miner/proving.go @@ -19,6 +19,7 @@ import ( "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-bitfield" "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/go-state-types/dline" "github.com/filecoin-project/go-state-types/proof" "github.com/filecoin-project/lotus/blockstore" @@ -237,10 +238,15 @@ var provingDeadlinesCmd = &cli.Command{ return xerrors.Errorf("getting deadlines: %w", err) } + head, err := api.ChainHead(ctx) + if err != nil { + return err + } + fmt.Printf("Miner: %s\n", color.BlueString("%s", maddr)) tw := tabwriter.NewWriter(os.Stdout, 2, 4, 2, ' ', 0) - _, _ = fmt.Fprintln(tw, "deadline\tpartitions\tsectors (faults)\tproven partitions") + _, _ = fmt.Fprintln(tw, "deadline\topen\tpartitions\tsectors (faults)\tproven partitions") for dlIdx, deadline := range deadlines { partitions, err := api.StateMinerPartitions(ctx, maddr, uint64(dlIdx), types.EmptyTSK) @@ -291,13 +297,25 @@ var provingDeadlinesCmd = &cli.Command{ if di.Index == uint64(dlIdx) { cur += "\t(current)" } - _, _ = fmt.Fprintf(tw, "%d\t%d\t%d (%d)\t%d%s\n", dlIdx, partitionCount, sectors, faults, provenPartitions, cur) + + _, _ = fmt.Fprintf(tw, "%d\t%s\t%d\t%d (%d)\t%d%s\n", dlIdx, deadlineOpenTime(head, uint64(dlIdx), di), + partitionCount, sectors, faults, provenPartitions, cur) } return tw.Flush() }, } +func deadlineOpenTime(ts *types.TipSet, dlIdx uint64, di *dline.Info) string { + gapIdx := dlIdx - di.Index + gapHeight := uint64(di.WPoStProvingPeriod) / di.WPoStPeriodDeadlines * gapIdx + + openHeight := di.Open + abi.ChainEpoch(gapHeight) + genesisBlockTimestamp := ts.MinTimestamp() - uint64(ts.Height())*build.BlockDelaySecs + + return time.Unix(int64(genesisBlockTimestamp+build.BlockDelaySecs*uint64(openHeight)), 0).Format(time.TimeOnly) +} + var provingDeadlineInfoCmd = &cli.Command{ Name: "deadline", Usage: "View the current proving period deadline information by its index", @@ -353,12 +371,18 @@ var provingDeadlineInfoCmd = &cli.Command{ return xerrors.Errorf("getting partitions for deadline %d: %w", dlIdx, err) } + head, err := api.ChainHead(ctx) + if err != nil { + return err + } + provenPartitions, err := deadlines[dlIdx].PostSubmissions.Count() if err != nil { return err } fmt.Printf("Deadline Index: %d\n", dlIdx) + fmt.Printf("Deadline Open: %s\n", deadlineOpenTime(head, dlIdx, di)) fmt.Printf("Partitions: %d\n", len(partitions)) fmt.Printf("Proven Partitions: %d\n", provenPartitions) fmt.Printf("Current: %t\n\n", di.Index == dlIdx) From d9a00405730eaf8b8bbf687e6f99f27b5317c357 Mon Sep 17 00:00:00 2001 From: qwdsds <50311944+qwdsds@users.noreply.github.com> Date: Fri, 8 Mar 2024 16:04:25 +0800 Subject: [PATCH 236/394] chore: fix typo (#11697) --- chain/consensus/filcns/filecoin.go | 2 +- chain/consensus/filcns/upgrades.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/chain/consensus/filcns/filecoin.go b/chain/consensus/filcns/filecoin.go index 486a04b5c..a6883a41a 100644 --- a/chain/consensus/filcns/filecoin.go +++ b/chain/consensus/filcns/filecoin.go @@ -165,7 +165,7 @@ func (filec *FilecoinEC) ValidateBlock(ctx context.Context, b *types.FullBlock) } if types.BigCmp(pweight, b.Header.ParentWeight) != 0 { - return xerrors.Errorf("parrent weight different: %s (header) != %s (computed)", + return xerrors.Errorf("parent weight different: %s (header) != %s (computed)", b.Header.ParentWeight, pweight) } diff --git a/chain/consensus/filcns/upgrades.go b/chain/consensus/filcns/upgrades.go index 552d2089f..f411c40b9 100644 --- a/chain/consensus/filcns/upgrades.go +++ b/chain/consensus/filcns/upgrades.go @@ -78,7 +78,7 @@ func init() { return } // use value from environment - log.Infof("migration worker cound set from %s (%d)", EnvMigrationMaxWorkerCount, mwc) + log.Infof("migration worker count set from %s (%d)", EnvMigrationMaxWorkerCount, mwc) MigrationMaxWorkerCount = int(mwc) return } From 7516aff2202d005309f7e97449cfe601b434d227 Mon Sep 17 00:00:00 2001 From: parthshah1 Date: Sun, 10 Mar 2024 18:43:45 -0700 Subject: [PATCH 237/394] fix: api: Length check the array sent to eth_feeHistory RPC (#11696) Co-authored-by: Rod Vagg Co-authored-by: Steven Allen --- node/impl/full/eth.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/node/impl/full/eth.go b/node/impl/full/eth.go index 5c3ab3165..031a83605 100644 --- a/node/impl/full/eth.go +++ b/node/impl/full/eth.go @@ -41,6 +41,8 @@ import ( var ErrUnsupported = errors.New("unsupported method") +const maxEthFeeHistoryRewardPercentiles = 100 + type EthModuleAPI interface { EthBlockNumber(ctx context.Context) (ethtypes.EthUint64, error) EthAccounts(ctx context.Context) ([]ethtypes.EthAddress, error) @@ -689,6 +691,9 @@ func (a *EthModule) EthFeeHistory(ctx context.Context, p jsonrpc.RawParams) (eth } rewardPercentiles := make([]float64, 0) if params.RewardPercentiles != nil { + if len(*params.RewardPercentiles) > maxEthFeeHistoryRewardPercentiles { + return ethtypes.EthFeeHistory{}, errors.New("length of the reward percentile array cannot be greater than 100") + } rewardPercentiles = append(rewardPercentiles, *params.RewardPercentiles...) } for i, rp := range rewardPercentiles { From 6f7498b6228211fc3c5770001af1bb707fcc4372 Mon Sep 17 00:00:00 2001 From: Phi-rjan Date: Tue, 12 Mar 2024 10:33:58 +0100 Subject: [PATCH 238/394] chore: Merge nv22 into master (#11699) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [WIP] feat: Add nv22 skeleton Addition of Network Version 22 skeleton * update FFI * feat: drand: refactor round verification * feat: sealing: Support nv22 DDO features in the sealing pipeline (#11226) * Initial work supporting DDO pieces in lotus-miner * sealing: Update pipeline input to operate on UniversalPiece * sealing: Update pipeline checks/sealing states to operate on UniversalPiece * sealing: Make pipeline build with UniversalPiece * move PieceDealInfo out of api * make gen * make sealing pipeline unit tests pass * fix itest ensemble build * don't panic in SectorsStatus with deals * stop linter from complaining about checkPieces * fix sector import tests * mod tidy * sealing: Add logic for (pre)committing DDO sectors * sealing: state-types with method defs * DDO non-snap pipeline works(?), DDO Itests * DDO support in snapdeals pipeline * make gen * update actor bundles * update the gst market fix * fix: chain: use PreCommitSectorsBatch2 when setting up genesis * some bug fixes * integration working changes * update actor bundles * Make TestOnboardRawPieceSnap pass * Appease the linter * Make deadlines test pass with v12 actors * Update go-state-types, abstract market DealState * make gen * mod tidy, lint fixes * Fix some more tests * Bump version in master Bump version in master * Make gen Make gen * fix sender * fix: lotus-provider: Fix winning PoSt * fix: sql Scan cannot write to an object * Actually show miner-addrs in info-log Actually show miner-addrs in lotus-provider info-log * [WIP] feat: Add nv22 skeleton Addition of Network Version 22 skeleton * update FFI * ddo is now nv22 * make gen * temp actor bundle with ddo * use working go-state-types * gst with v13 market migration * update bundle, builtin.MethodsMiner.ProveCommitSectors2 -> 3 * actually working v13 migration, v13 migration itest * Address review * sealing: Correct DDO snap pledge math * itests: Mixed ddo itest * pipeline: Fix sectorWeight * sealing: convert market deals into PAMs in mixed sectors * sealing: make market to ddo conversion work * fix lint * update gst * Update actors and GST to lastest integ branch * commit batcher: Update ProveCommitSectors3Params builder logic * make gen * use builtin-actors master * ddo: address review * itests: Add commd assertions to ddo tests * make gen * gst with fixed types * config knobs for RequireActivationSuccess * storage: Drop obsolete flaky tasts --------- Co-authored-by: Jennifer Wang Co-authored-by: Aayush Co-authored-by: Shrenuj Bansal Co-authored-by: Phi Co-authored-by: Andrew Jackson (Ajax) Co-authored-by: TippyFlits * feat: implement FIP-0063 * chore: deps: update to go-multiaddr v0.12.2 (#11602) * feat: fvm: update the FVM/FFI to v4.1 (#11608) (#11612) This: 1. Adds nv22 support. 2. Updates the message tracing format. Co-authored-by: Steven Allen * AggregateProofType nil when doing batch updates Use latest nv22 go-state-types version with matching update * Update to v13.0.0-rc.2 bundle * chore: Upgrade heights and codename Update upgrade heights Co-Authored-By: Steven Allen * Update epoch after nv22 DRAND switch Update epoch after nv22 DRAND switch * Update Mango codename to Phoneix Make the codename for the Drand-change inline with Dragon style. * Add UpgradePhoenixHeight to API params * set UpgradePhoenixHeight to be one hour after Dragon * Make gen Make gen and UpgradePhoenixHeight in butterfly and local devnet to be in line with Calibration and Mainnet * Update epoch heights (#11637) Update epoch heights * new: add forest bootstrap nodes (#11636) Signed-off-by: samuelarogbonlo * Merge pull request #11491 from filecoin-project/fix/remove-decommissioned-pl-bootstrap-nodes Remove PL operated bootstrap nodes from mainnet.pi * feat: api: new verified registry methods to get all allocations and claims (#11631) * new verireg methods * update changelog and add itest * update itest and cli * update new method's support till v9 * remove gateway APIs * fix cli internal var names * chore:: backport #11609 to the feat/nv22 branch (#11644) * feat: api: improve the correctness of Eth's trace_block (#11609) * Improve the correctness of Eth's trace_block - Improve encoding/decoding of parameters and return values: - Encode "native" parameters and return values with Solidity ABI. - Correctly decode parameters to "create" calls. - Use the correct (ish) output for "create" calls. - Handle all forms of "create". - Make robust with respect to reverts: - Use the actor ID/address from the trace instead of looking it up in the state-tree (may not exist in the state-tree due to a revert). - Gracefully handle failed actor/contract creation. - Improve performance: - We avoid looking anything up in the state-tree when translating the trace, which should significantly improve performance. - Improve code readability: - Remove all "backtracking" logic. - Use an "environment" struct to store temporary state instead of attaching it to the trace. - Fix random bugs: - Fix an allocation bug in the "address" logic (need to set the capacity before modifying the slice). - Improved error checking/handling. - Use correct types for `trace_block` action/results (create, call, etc.). - And use the correct types for Result/Action structs instead of reusing the same "Call" action every time. - Improve error messages. * Make gen Make gen --------- Co-authored-by: Steven Allen * fix: add UpgradePhoenixHeight to StateGetNetworkParams (#11648) * chore: deps: update to go-state-types v13.0.0-rc.1 * do NOT update the cache when running the real migration * Merge pull request #11632 from hanabi1224/hm/drand-test feat: drand quicknet: allow scheduling drand quicknet upgrade before nv22 on 2k devnet * chore: deps: update to go-state-types v13.0.0-rc.2 chore: deps: update to go-state-types v13.0.0-rc.2 * feat: set migration config UpgradeEpoch for v13 actors upgrade * Built-in actor events first draft * itest for DDO non-market verified data w/ builtin actor events * Tests for builtin actor events API * Clean up DDO+Events tests, add lots of explainer comments * Minor tweaks to events types * Avoid duplicate messages when looking for receipts * Rename internal events modules for clarity * Adjust actor event API after review * s/ActorEvents/Events/g in global config * Manage event sending rate for SubscribeActorEvents * Terminate SubscribeActorEvents chan when at max height * Document future API changes * More clarity in actor event API docs * More post-review changes, lots of tests for SubscribeActorEvents Use BlockDelay as the window for receiving events on the SubscribeActorEvents channel. We expect the user to have received the initial batch of historical events (if any) in one block's time. For real-time events we expect them to not fall behind by roughly one block's time. * Remove duplicate code from actor event type marshalling tests Reduce verbosity and remove duplicate test logic from actor event types JSON marshalling tests. * Rename actor events test to follow go convention Add missing `s` to `actor_events` test file to follow golang convention used across the repo. * Run actor events table tests in deterministic order Refactor `map` usage for actor event table tests to ensure deterministic test execution order, making debugging potential issues easier. If non-determinism is a target, leverage Go's built-in parallel testing capabilities. * Reduce scope for filter removal failure when getting actor events Use a fresh context to remove the temporary filter installed solely to get the actor events. This should reduce chances of failure in a case where the original context may be expired/cancelled. Refactor removal into a `defer` statement for a more readable, concise return statement. * Use fixed RNG seed for actor event tests Improve determinism in actor event tests by using a fixed RNG seed. This makes up a more reproducible test suit. * Use provided libraries to assert eventual conditions Use the functionalities already provided by `testify` to assert eventual conditions, and remove the use of `time.Sleep`. Remove duplicate code in utility functions that are already defined. Refactor assertion helper functions to use consistent terminology: "require" implies fatal error, whereas "assert" implies error where the test may proceed executing. * Update changelog for actor events APIs * Fix concerns and docs identified by review * Update actor bundle to v13.0.0-rc3 Update actor bundle to v13.0.0-rc3 * Prep Lotus v1.26.0-rc1 - For sanity reverting the mainnet upgrade epoch to 99999999, and then only set it when cutting the final release -Update Calibnet CIDs to v13.0.0-rc3 - Add GetActorEvents, SubscribeActorEvents, GetAllClaims and GetAllAllocations methods to the changelog Co-Authored-By: Jiaying Wang <42981373+jennijuju@users.noreply.github.com> * Update CHANGELOG.md Co-authored-by: Masih H. Derkani * Make gen Make gen * fix: beacon: validate drand change at nv16 correctly * bump to v1.26.0-rc2 * test: cleanup ddo verified itest, extract steps to functions also add allocation-removed event case * test: extract verified DDO test to separate file, add more checks * test: add additional actor events checks * Add verification for "deal-activated" actor event * docs(drand): document the meaning of "IsChained" (#11692) * Resolve conflicts I encountered multiple issues when trying to run make gen. And these changes fixed a couple of them: - go mod tidy - Remove RaftState/RaftLeader - Revert `if ts.Height() > claim.TermMax+claim.TermStart || !cctx.IsSet("expired")` to the what is in the release/v1.26.0: `if tsHeight > val.TermMax || !expired` * fixup imports, make jen * Update version Update version in master to v1.27.0-dev * Update node/impl/full/dummy.go Co-authored-by: Łukasz Magiera * Adjust ListClaimsCmd Adjust ListClaimsCmd according to review --------- Signed-off-by: samuelarogbonlo Co-authored-by: TippyFlits Co-authored-by: Aayush Co-authored-by: Łukasz Magiera Co-authored-by: Jennifer Wang Co-authored-by: Shrenuj Bansal Co-authored-by: Andrew Jackson (Ajax) Co-authored-by: Steven Allen Co-authored-by: Rod Vagg Co-authored-by: Samuel Arogbonlo <47984109+samuelarogbonlo@users.noreply.github.com> Co-authored-by: LexLuthr <88259624+LexLuthr@users.noreply.github.com> Co-authored-by: tom123222 <160735201+tom123222@users.noreply.github.com> Co-authored-by: Aarsh Shah Co-authored-by: Masih H. Derkani Co-authored-by: Jiaying Wang <42981373+jennijuju@users.noreply.github.com> --- .circleci/config.yml | 12 + CHANGELOG.md | 132 ++- api/api_full.go | 76 +- api/api_gateway.go | 3 + api/api_storage.go | 40 +- api/cbor_gen.go | 472 +-------- api/docgen/docgen.go | 33 +- api/mocks/mock_full.go | 77 +- api/proxy_gen.go | 99 +- api/types.go | 2 + api/v0api/full.go | 4 + api/v0api/proxy_gen.go | 26 + api/v0api/v0mocks/mock_full.go | 32 +- blockstore/cbor_gen.go | 35 +- build/actors/v13.tar.zst | Bin 0 -> 1646614 bytes build/builtin_actors_gen.go | 217 +++- build/drand.go | 26 +- build/openrpc/full.json | 943 +++++++++++++----- build/openrpc/gateway.json | 380 +++++-- build/openrpc/miner.json | 447 ++++++--- build/openrpc/worker.json | 76 +- build/params_2k.go | 34 +- build/params_butterfly.go | 10 +- build/params_calibnet.go | 9 +- build/params_interop.go | 8 +- build/params_mainnet.go | 19 +- build/params_shared_vals.go | 2 +- build/params_testground.go | 5 +- build/version.go | 2 +- chain/actors/builtin/account/account.go | 11 +- chain/actors/builtin/account/v13.go | 62 ++ chain/actors/builtin/cron/cron.go | 13 +- chain/actors/builtin/cron/v13.go | 57 ++ chain/actors/builtin/datacap/datacap.go | 13 +- chain/actors/builtin/datacap/v13.go | 82 ++ chain/actors/builtin/evm/evm.go | 10 +- chain/actors/builtin/evm/v13.go | 72 ++ chain/actors/builtin/init/init.go | 13 +- chain/actors/builtin/init/v13.go | 147 +++ chain/actors/builtin/market/actor.go.template | 58 +- chain/actors/builtin/market/diff.go | 6 +- chain/actors/builtin/market/market.go | 67 +- chain/actors/builtin/market/state.go.template | 56 +- chain/actors/builtin/market/v0.go | 51 +- chain/actors/builtin/market/v10.go | 51 +- chain/actors/builtin/market/v11.go | 51 +- chain/actors/builtin/market/v12.go | 51 +- chain/actors/builtin/market/v13.go | 404 ++++++++ chain/actors/builtin/market/v2.go | 51 +- chain/actors/builtin/market/v3.go | 51 +- chain/actors/builtin/market/v4.go | 51 +- chain/actors/builtin/market/v5.go | 51 +- chain/actors/builtin/market/v6.go | 51 +- chain/actors/builtin/market/v7.go | 51 +- chain/actors/builtin/market/v8.go | 51 +- chain/actors/builtin/market/v9.go | 51 +- chain/actors/builtin/miner/actor.go.template | 14 +- chain/actors/builtin/miner/miner.go | 20 +- chain/actors/builtin/miner/v13.go | 594 +++++++++++ chain/actors/builtin/multisig/message10.go | 4 +- chain/actors/builtin/multisig/message11.go | 4 +- chain/actors/builtin/multisig/message12.go | 4 +- chain/actors/builtin/multisig/message13.go | 77 ++ chain/actors/builtin/multisig/message8.go | 4 +- chain/actors/builtin/multisig/message9.go | 4 +- chain/actors/builtin/multisig/multisig.go | 24 +- chain/actors/builtin/multisig/v13.go | 138 +++ chain/actors/builtin/paych/message13.go | 109 ++ chain/actors/builtin/paych/paych.go | 7 + chain/actors/builtin/paych/v13.go | 135 +++ chain/actors/builtin/power/power.go | 13 +- chain/actors/builtin/power/v13.go | 207 ++++ chain/actors/builtin/registry.go | 120 +++ chain/actors/builtin/reward/reward.go | 13 +- chain/actors/builtin/reward/v13.go | 120 +++ chain/actors/builtin/system/system.go | 11 +- chain/actors/builtin/system/v13.go | 72 ++ .../actors/builtin/verifreg/actor.go.template | 2 + .../actors/builtin/verifreg/state.go.template | 31 + chain/actors/builtin/verifreg/v0.go | 12 + chain/actors/builtin/verifreg/v10.go | 26 + chain/actors/builtin/verifreg/v11.go | 26 + chain/actors/builtin/verifreg/v12.go | 26 + chain/actors/builtin/verifreg/v13.go | 196 ++++ chain/actors/builtin/verifreg/v2.go | 12 + chain/actors/builtin/verifreg/v3.go | 12 + chain/actors/builtin/verifreg/v4.go | 12 + chain/actors/builtin/verifreg/v5.go | 12 + chain/actors/builtin/verifreg/v6.go | 12 + chain/actors/builtin/verifreg/v7.go | 12 + chain/actors/builtin/verifreg/v8.go | 12 + chain/actors/builtin/verifreg/v9.go | 26 + chain/actors/builtin/verifreg/verifreg.go | 15 +- chain/actors/policy/policy.go | 73 +- chain/actors/version.go | 5 +- chain/beacon/beacon.go | 104 +- chain/beacon/drand/drand.go | 50 +- chain/beacon/drand/drand_test.go | 8 + chain/beacon/mock.go | 6 +- chain/consensus/compute_state.go | 1 + chain/consensus/filcns/filecoin.go | 1 + chain/consensus/filcns/upgrades.go | 125 ++- chain/events/cache.go | 2 +- chain/events/events.go | 6 +- chain/events/events_called.go | 12 +- chain/events/events_height.go | 4 +- chain/events/events_test.go | 2 +- chain/events/filter/event.go | 83 +- chain/events/filter/event_test.go | 75 +- chain/events/filter/index.go | 10 +- chain/events/filter/index_test.go | 218 ++-- chain/events/message_cache.go | 4 +- chain/events/observer.go | 2 +- chain/events/state/predicates.go | 2 +- chain/events/state/predicates_test.go | 14 +- chain/exchange/cbor_gen.go | 44 +- chain/gen/genesis/miners.go | 36 +- chain/state/statetree.go | 2 +- chain/stmgr/actors.go | 2 +- chain/stmgr/forks.go | 4 - chain/types/actor_event.go | 67 ++ chain/types/actor_event_test.go | 125 +++ chain/types/cbor_gen.go | 155 +-- chain/types/ethtypes/eth_types.go | 2 +- chain/types/event.go | 2 +- cli/client.go | 20 +- cli/filplus.go | 266 +++-- cli/util.go | 12 + cmd/lotus-bench/amt_internal.go | 312 ------ cmd/lotus-bench/main.go | 218 ---- cmd/lotus-miner/init.go | 11 +- cmd/lotus-shed/fip-0036.go | 554 ---------- cmd/lotus-shed/indexes.go | 14 +- cmd/lotus-shed/main.go | 1 - cmd/lotus-shed/market.go | 2 +- cmd/lotus-shed/shedgen/cbor_gen.go | 9 +- conformance/chaos/cbor_gen.go | 63 +- documentation/en/api-v0-methods-miner.md | 32 +- documentation/en/api-v0-methods-provider.md | 8 + documentation/en/api-v0-methods.md | 62 +- documentation/en/api-v1-unstable-methods.md | 227 ++++- documentation/en/cli-lotus-miner.md | 2 +- documentation/en/cli-lotus-provider.md | 2 +- documentation/en/cli-lotus-worker.md | 2 +- documentation/en/cli-lotus.md | 11 +- documentation/en/default-lotus-config.toml | 16 +- .../en/default-lotus-miner-config.toml | 24 + gateway/node.go | 3 + gateway/proxy_fil.go | 14 + gen/inlinegen-data.json | 8 +- gen/main.go | 12 +- go.mod | 38 +- go.sum | 133 ++- itests/deadlines_test.go | 38 +- itests/direct_data_onboard_test.go | 408 ++++++++ itests/direct_data_onboard_verified_test.go | 713 +++++++++++++ itests/kit/ensemble.go | 6 +- itests/kit/ensemble_opts_nv.go | 6 +- itests/kit/log.go | 1 + itests/kit/node_opts.go | 4 + itests/pending_deal_allocation_test.go | 4 - itests/sector_terminate_test.go | 33 + itests/verifreg_test.go | 149 +++ lib/result/result.go | 12 + .../ondealsectorcommitted_test.go | 12 +- markets/storageadapter/provider.go | 5 +- node/builder_chain.go | 16 +- node/config/def.go | 3 + node/config/doc_gen.go | 48 +- node/config/types.go | 24 +- node/hello/cbor_gen.go | 13 +- node/impl/full.go | 1 + node/impl/full/actor_events.go | 376 +++++++ node/impl/full/actor_events_test.go | 780 +++++++++++++++ node/impl/full/dummy.go | 15 + node/impl/full/eth.go | 167 ++-- .../impl/full/{eth_event.go => eth_events.go} | 0 node/impl/full/eth_test.go | 76 ++ node/impl/full/state.go | 57 +- node/impl/storminer.go | 5 +- node/modules/actorevent.go | 98 +- node/modules/dtypes/beacon.go | 1 + node/modules/ethmodule.go | 4 +- node/modules/storageminer.go | 10 + paychmgr/cbor_gen.go | 76 +- storage/pipeline/cbor_gen.go | 215 ++-- storage/pipeline/checks.go | 125 ++- storage/pipeline/commit_batch.go | 250 ++++- storage/pipeline/commit_batch_test.go | 498 --------- storage/pipeline/currentdealinfo_test.go | 6 +- storage/pipeline/fsm_events.go | 13 +- storage/pipeline/input.go | 209 ++-- storage/pipeline/mocks/api.go | 91 +- .../pipeline/mocks/mock_precommit_batcher.go | 15 + storage/pipeline/piece/cbor_gen.go | 451 +++++++++ storage/pipeline/piece/piece_info.go | 186 ++++ storage/pipeline/pledge.go | 114 +++ storage/pipeline/precommit_batch.go | 20 +- storage/pipeline/precommit_batch_test.go | 291 ------ storage/pipeline/precommit_policy.go | 18 +- storage/pipeline/precommit_policy_test.go | 94 +- storage/pipeline/receive.go | 24 +- storage/pipeline/sealiface/config.go | 5 + storage/pipeline/sealing.go | 49 +- storage/pipeline/states_failed.go | 124 ++- storage/pipeline/states_failed_test.go | 9 +- storage/pipeline/states_replica_update.go | 127 ++- storage/pipeline/states_sealing.go | 170 +++- storage/pipeline/types.go | 160 ++- storage/pipeline/types_test.go | 13 +- storage/pipeline/upgrade_queue.go | 2 +- storage/sealer/cbor_gen.go | 56 +- storage/sealer/storiface/cbor_gen.go | 44 +- storage/sectorblocks/blocks.go | 5 +- storage/wdpost/wdpost_run_test.go | 8 +- 215 files changed, 12395 insertions(+), 4522 deletions(-) create mode 100644 build/actors/v13.tar.zst create mode 100644 chain/actors/builtin/account/v13.go create mode 100644 chain/actors/builtin/cron/v13.go create mode 100644 chain/actors/builtin/datacap/v13.go create mode 100644 chain/actors/builtin/evm/v13.go create mode 100644 chain/actors/builtin/init/v13.go create mode 100644 chain/actors/builtin/market/v13.go create mode 100644 chain/actors/builtin/miner/v13.go create mode 100644 chain/actors/builtin/multisig/message13.go create mode 100644 chain/actors/builtin/multisig/v13.go create mode 100644 chain/actors/builtin/paych/message13.go create mode 100644 chain/actors/builtin/paych/v13.go create mode 100644 chain/actors/builtin/power/v13.go create mode 100644 chain/actors/builtin/reward/v13.go create mode 100644 chain/actors/builtin/system/v13.go create mode 100644 chain/actors/builtin/verifreg/v13.go create mode 100644 chain/types/actor_event.go create mode 100644 chain/types/actor_event_test.go delete mode 100644 cmd/lotus-bench/amt_internal.go delete mode 100644 cmd/lotus-shed/fip-0036.go create mode 100644 itests/direct_data_onboard_test.go create mode 100644 itests/direct_data_onboard_verified_test.go create mode 100644 node/impl/full/actor_events.go create mode 100644 node/impl/full/actor_events_test.go rename node/impl/full/{eth_event.go => eth_events.go} (100%) delete mode 100644 storage/pipeline/commit_batch_test.go create mode 100644 storage/pipeline/piece/cbor_gen.go create mode 100644 storage/pipeline/piece/piece_info.go create mode 100644 storage/pipeline/pledge.go delete mode 100644 storage/pipeline/precommit_batch_test.go diff --git a/.circleci/config.yml b/.circleci/config.yml index 6ca0353c5..7b67d4a7f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -657,6 +657,18 @@ workflows: - build suite: itest-decode_params target: "./itests/decode_params_test.go" + - test: + name: test-itest-direct_data_onboard + requires: + - build + suite: itest-direct_data_onboard + target: "./itests/direct_data_onboard_test.go" + - test: + name: test-itest-direct_data_onboard_verified + requires: + - build + suite: itest-direct_data_onboard_verified + target: "./itests/direct_data_onboard_verified_test.go" - test: name: test-itest-dup_mpool_messages requires: diff --git a/CHANGELOG.md b/CHANGELOG.md index 21943bc6a..eca451ecd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,9 +2,74 @@ # UNRELEASED +## New features + ## Improvements -### Tracing API +# v1.26.0-rc2 / 2024-03-0y + +This is a release candidate of the upcoming MANDATORY Lotus v1.26.0 release, which will deliver the Filecoin network version 22, codenamed Dragon 🐉. + +**This release candidate sets the calibration network to upgrade at epoch 1427974, which is 2024-03-11T14:00:00Z** +This release does NOT set the mainnet upgrade epoch yet, in which will be updated in the final release. +The Filecoin network version 22 delivers the following FIPs: + +- [FIP-0063: Switching to new Drand mainnet network](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0063.md) +- [FIP-0074: Remove cron-based automatic deal settlement](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0074.md) +- [FIP-0076: Direct data onboarding](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0076.md) +- [FIP-0083: Add built-in Actor events in the Verified Registry, Miner and Market Actors](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0083.md) + +## v13 Builtin Actor Bundle + +The actor bundles for the **calibration network** can be checked as follows: + +``` +lotus state actor-cids --network-version=22 +Network Version: 22 +Actor Version: 13 +Manifest CID: bafy2bzacea4firkyvt2zzdwqjrws5pyeluaesh6uaid246tommayr4337xpmi + +Actor CID +account bafk2bzaceb3j36ri5y5mfklgp5emlvrms6g4733ss2j3l7jismrxq6ng3tcc6 +cron bafk2bzaceaz6rocamdxehgpwcbku6wlapwpgzyyvkrploj66mlqptsulf52bs +datacap bafk2bzacea22nv5g3yngpxvonqfj4r2nkfk64y6yw2malicm7odk77x7zuads +eam bafk2bzaceatqtjzj7623i426noaslouvluhz6e3md3vvquqzku5qj3532uaxg +ethaccount bafk2bzacean3hs7ga5csw6g3uu7watxfnqv5uvxviebn3ba6vg4sagwdur5pu +evm bafk2bzacec5ibmbtzuzjgwjmksm2n6zfq3gkicxqywwu7tsscqgdzajpfctxk +init bafk2bzaced5sq72oemz6qwi6yssxwlos2g54zfprslrx5qfhhx2vlgsbvdpcs +multisig bafk2bzacedbgei6jkx36fwdgvoohce4aghvpohqdhoco7p4thszgssms7olv2 +paymentchannel bafk2bzaceasmgmfsi4mjanxlowsub65fmevhzky4toeqbtw4kp6tmu4kxjpgq +placeholder bafk2bzacedfvut2myeleyq67fljcrw4kkmn5pb5dpyozovj7jpoez5irnc3ro +reward bafk2bzacedjyp6ll5ez27dfgldjj4tntxfvyp4pa5zkk7s5uhipzqjyx2gmuc +storagemarket bafk2bzaceabolct6qdnefwcrtati2us3sxtxfghyqk6aamfhl6byyefmtssqi +storageminer bafk2bzaceckzw3v7wqliyggvjvihz4wywchnnsie4frfvkm3fm5znb64mofri +storagepower bafk2bzacea7t4wynzjajl442mpdqbnh3wusjusqtnzgpvefvweh4n2tgzgqhu +system bafk2bzacedjnrb5glewazsxpcx6rwiuhl4kwrfcqolyprn6rrjtlzmthlhdq6 +verifiedregistry bafk2bzacednskl3bykz5qpo54z2j2p4q44t5of4ktd6vs6ymmg2zebsbxazkm +``` + +## Migration + +We are expecting a bit heavier than normal state migration for this upgrade due to the amount of state changes introduced with Direct Data Onboarding. + +All node operators, including storage providers, should be aware that ONE pre-migration is being scheduled 120 epochs before the upgrade. It will take around 10-20 minutes for the pre-migration and less than 30 seconds for the final migration, depending on the amount of historical state in the node blockstore and the hardware specs the node is running on. During this time, expect slower block validation times, increased CPU and memory usage, and longer delays for API queries + +We recommend node operators (who haven't enabled splitstore discard mode) that do not care about historical chain states, to prune the chain blockstore by syncing from a snapshot 1-2 days before the upgrade. + +You can test out the migration by running running the [`benchmarking a network migration` tutorial.](https://lotus.filecoin.io/kb/test-migration/) + +For certain node operators, such as full archival nodes or systems that need to keep large amounts of state (RPC providers), completing the pre-migration in time before the network upgrade might not be achievable. For those node operators, it is recommended to skip the pre-migration and run the non-cached migration (i.e., just running the migration at the exact upgrade epoch), and schedule for some downtime during the upgrade epoch. Operators of such nodes can read the [`How to disable premigration in network upgrade` tutorial.](https://lotus.filecoin.io/kb/disable-premigration/) + +## New features +- feat: api: new verified registry methods to get all allocations and claims (#11631) ([filecoin-project/lotus#11631](https://github.com/filecoin-project/lotus/pull/11631)) +- new: add forest bootstrap nodes (#11636) ([filecoin-project/lotus#11636](https://github.com/filecoin-project/lotus/pull/11636)) +- feat: sealing: Support nv22 DDO features in the sealing pipeline (#11226) ([filecoin-project/lotus#11226](https://github.com/filecoin-project/lotus/pull/11226)) +- feat: implement FIP-0063 ([filecoin-project/lotus#11572](https://github.com/filecoin-project/lotus/pull/11572)) +- feat: events: Add Lotus APIs to consume smart contract and built-in actor events ([filecoin-project/lotus#11618](https://github.com/filecoin-project/lotus/pull/11618)) + +## Improvements + +## Tracing API Replace the `CodeCid` field in the message trace (added in 1.23.4) with an `InvokedActor` field. @@ -67,6 +132,71 @@ Additionally, Filecoin is not Ethereum no matter how much we try to provide API/ [handlefilecoinmethod]: https://fips.filecoin.io/FIPS/fip-0054.html#handlefilecoinmethod-general-handler-for-method-numbers--1024 +### GetActorEvents and SubscribeActorEvents + +[FIP-0049](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0049.md) introduced _Actor Events_ that can be emitted by user programmed actors. [FIP-0083](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0083.md) introduces new events emitted by the builtin Verified Registry, Miner and Market Actors. These new events for builtin actors are being activated with network version 22 to coincide with _Direct Data Onboarding_ as defined in [FIP-0076](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0076.md) which introduces additional flexibility for data onboarding. Sector, Deal and DataCap lifecycles can be tracked with these events, providing visibility and options for programmatic responses to changes in state. + +Actor events are available on message receipts, but can now be retrieved from a node using the new `GetActorEvents` and `SubscribeActorEvents` methods. These methods allow for querying and subscribing to actor events, respectively. They depend on the Lotus node both collecting events (with `Fevm.Events.RealTimeFilterAPI` and `Fevm.Events.HistoricFilterAPI`) and being enabled with the new configuration option `Events.EnableActorEventsAPI`. Note that a Lotus node can only respond to requests for historic events that it retains in its event store. + +Both `GetActorEvents` and `SubscribeActorEvents` take a filter parameter which can optionally filter events on: + +* `Addresses` of the actor(s) emitting the event +* Specific `Fields` within the event +* `FromHeight` and `ToHeight` to filter events by block height +* `TipSetKey` to restrict events contained within a specific tipset + +`GetActorEvents` provides a one-time query for actor events, while `SubscribeActorEvents` provides a long-lived connection (via websockets) to the Lotus node, allowing for real-time updates on actor events. The subscription can be cancelled by the client at any time. + +### GetAllClaims and GetAllAlocations +Additionally the methods `GetAllAllocations` and `GetAllClaims` has been added to the Lotus API. These methods lists all the available allocations and claims available in the actor state. + +### Lotus CLI + +The `filplus` commands used for listing allocations and claims have been updated. If no argument is provided to the either command, they will list out all the allocations and claims in the verified registry actor. +The output list columns have been modified to `AllocationID` and `ClaimID` instead of ID. + +```shell +lotus filplus list-allocations --help +NAME: + lotus filplus list-allocations - List allocations available in verified registry actor or made by a client if specified + +USAGE: + lotus filplus list-allocations [command options] clientAddress + +OPTIONS: + --expired list only expired allocations (default: false) + --json output results in json format (default: false) + --help, -h show help + + +lotus filplus list-claims --help +NAME: + lotus filplus list-claims - List claims available in verified registry actor or made by provider if specified + +USAGE: + lotus filplus list-claims [command options] providerAddress + +OPTIONS: + --expired list only expired claims (default: false) + --help, -h show help +``` + +## Dependencies +- github.com/filecoin-project/go-state-types (v0.12.8 -> v0.13.0-rc.2) +- chore: deps: update to go-state-types v13.0.0-rc.1 ([filecoin-project/lotus#11662](https://github.com/filecoin-project/lotus/pull/11662)) +- chore: deps: update to go-state-types v13.0.0-rc.2 ([filecoin-project/lotus#11675](https://github.com/filecoin-project/lotus/pull/11675)) +- chore: deps: update to go-multiaddr v0.12.2 (#11602) ([filecoin-project/lotus#11602](https://github.com/filecoin-project/lotus/pull/11602)) +- feat: fvm: update the FVM/FFI to v4.1 (#11608) (#11612) ([filecoin-project/lotus#11612](https://github.com/filecoin-project/lotus/pull/11612)) + +## Others +- Remove PL operated bootstrap nodes from mainnet.pi ([filecoin-project/lotus#11491](https://github.com/filecoin-project/lotus/pull/11491)) +- Update epoch heights (#11637) ([filecoin-project/lotus#11637](https://github.com/filecoin-project/lotus/pull/11637)) +- chore: Set upgrade heights and change codename ([filecoin-project/lotus#11599](https://github.com/filecoin-project/lotus/pull/11599)) +- chore:: backport #11609 to the feat/nv22 branch (#11644) ([filecoin-project/lotus#11644](https://github.com/filecoin-project/lotus/pull/11644)) +- fix: add UpgradePhoenixHeight to StateGetNetworkParams (#11648) ([filecoin-project/lotus#11648](https://github.com/filecoin-project/lotus/pull/11648)) +- feat: drand quicknet: allow scheduling drand quicknet upgrade before nv22 on 2k devnet ([filecoin-project/lotus#11667]https://github.com/filecoin-project/lotus/pull/11667) +- chore: backport #11632 to release/v1.26.0 ([filecoin-project/lotus#11667](https://github.com/filecoin-project/lotus/pull/11667)) + # v1.25.2 / 2024-01-11 This is an optional but **highly recommended feature release** of Lotus, as it includes fixes for synchronizations issues that users have experienced. The feature release also introduces `Lotus-Provider` in its alpha testing phase, as well as the ability to call external PC2-binaries during the sealing process. diff --git a/api/api_full.go b/api/api_full.go index a9adb1c4b..e61bb0ded 100644 --- a/api/api_full.go +++ b/api/api_full.go @@ -20,7 +20,6 @@ import ( "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/big" "github.com/filecoin-project/go-state-types/builtin/v8/paych" - "github.com/filecoin-project/go-state-types/builtin/v9/market" verifregtypes "github.com/filecoin-project/go-state-types/builtin/v9/verifreg" "github.com/filecoin-project/go-state-types/crypto" "github.com/filecoin-project/go-state-types/dline" @@ -28,8 +27,10 @@ import ( apitypes "github.com/filecoin-project/lotus/api/types" "github.com/filecoin-project/lotus/chain/actors/builtin" + "github.com/filecoin-project/lotus/chain/actors/builtin/market" "github.com/filecoin-project/lotus/chain/actors/builtin/miner" "github.com/filecoin-project/lotus/chain/actors/builtin/power" + "github.com/filecoin-project/lotus/chain/actors/builtin/verifreg" "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/types/ethtypes" "github.com/filecoin-project/lotus/node/modules/dtypes" @@ -552,14 +553,20 @@ type FullNode interface { // StateGetAllocationForPendingDeal returns the allocation for a given deal ID of a pending deal. Returns nil if // pending allocation is not found. StateGetAllocationForPendingDeal(ctx context.Context, dealId abi.DealID, tsk types.TipSetKey) (*verifregtypes.Allocation, error) //perm:read + // StateGetAllocationIdForPendingDeal is like StateGetAllocationForPendingDeal except it returns the allocation ID + StateGetAllocationIdForPendingDeal(ctx context.Context, dealId abi.DealID, tsk types.TipSetKey) (verifreg.AllocationId, error) //perm:read // StateGetAllocation returns the allocation for a given address and allocation ID. StateGetAllocation(ctx context.Context, clientAddr address.Address, allocationId verifregtypes.AllocationId, tsk types.TipSetKey) (*verifregtypes.Allocation, error) //perm:read // StateGetAllocations returns the all the allocations for a given client. StateGetAllocations(ctx context.Context, clientAddr address.Address, tsk types.TipSetKey) (map[verifregtypes.AllocationId]verifregtypes.Allocation, error) //perm:read + // StateGetAllAllocations returns the all the allocations available in verified registry actor. + StateGetAllAllocations(ctx context.Context, tsk types.TipSetKey) (map[verifregtypes.AllocationId]verifregtypes.Allocation, error) //perm:read // StateGetClaim returns the claim for a given address and claim ID. StateGetClaim(ctx context.Context, providerAddr address.Address, claimId verifregtypes.ClaimId, tsk types.TipSetKey) (*verifregtypes.Claim, error) //perm:read // StateGetClaims returns the all the claims for a given provider. StateGetClaims(ctx context.Context, providerAddr address.Address, tsk types.TipSetKey) (map[verifregtypes.ClaimId]verifregtypes.Claim, error) //perm:read + // StateGetAllClaims returns the all the claims available in verified registry actor. + StateGetAllClaims(ctx context.Context, tsk types.TipSetKey) (map[verifregtypes.ClaimId]verifregtypes.Claim, error) //perm:read // StateComputeDataCID computes DataCID from a set of on-chain deals StateComputeDataCID(ctx context.Context, maddr address.Address, sectorType abi.RegisteredSealProof, deals []abi.DealID, tsk types.TipSetKey) (cid.Cid, error) //perm:read // StateLookupID retrieves the ID address of the given address @@ -896,6 +903,33 @@ type FullNode interface { // LOTUS_BACKUP_BASE_PATH environment variable set to some path, and that // the path specified when calling CreateBackup is within the base path CreateBackup(ctx context.Context, fpath string) error //perm:admin + + // Actor events + + // GetActorEvents returns all user-programmed and built-in actor events that match the given + // filter. + // This is a request/response API. + // Results available from this API may be limited by the MaxFilterResults and MaxFilterHeightRange + // configuration options and also the amount of historical data available in the node. + // + // This is an EXPERIMENTAL API and may be subject to change. + GetActorEvents(ctx context.Context, filter *types.ActorEventFilter) ([]*types.ActorEvent, error) //perm:read + + // SubscribeActorEvents returns a long-lived stream of all user-programmed and built-in actor + // events that match the given filter. + // Events that match the given filter are written to the stream in real-time as they are emitted + // from the FVM. + // The response stream is closed when the client disconnects, when a ToHeight is specified and is + // reached, or if there is an error while writing an event to the stream. + // This API also allows clients to read all historical events matching the given filter before any + // real-time events are written to the response stream if the filter specifies an earlier + // FromHeight. + // Results available from this API may be limited by the MaxFilterResults and MaxFilterHeightRange + // configuration options and also the amount of historical data available in the node. + // + // Note: this API is only available via websocket connections. + // This is an EXPERIMENTAL API and may be subject to change. + SubscribeActorEvents(ctx context.Context, filter *types.ActorEventFilter) (<-chan *types.ActorEvent, error) //perm:read } // reverse interface to the client, called after EthSubscribe @@ -1131,9 +1165,47 @@ type MarketBalance struct { Locked big.Int } +type MarketDealState struct { + SectorStartEpoch abi.ChainEpoch // -1 if not yet included in proven sector + LastUpdatedEpoch abi.ChainEpoch // -1 if deal state never updated + SlashEpoch abi.ChainEpoch // -1 if deal never slashed +} + +func MakeDealState(mds market.DealState) MarketDealState { + return MarketDealState{ + SectorStartEpoch: mds.SectorStartEpoch(), + LastUpdatedEpoch: mds.LastUpdatedEpoch(), + SlashEpoch: mds.SlashEpoch(), + } +} + +type mstate struct { + s MarketDealState +} + +func (m mstate) SectorStartEpoch() abi.ChainEpoch { + return m.s.SectorStartEpoch +} + +func (m mstate) LastUpdatedEpoch() abi.ChainEpoch { + return m.s.LastUpdatedEpoch +} + +func (m mstate) SlashEpoch() abi.ChainEpoch { + return m.s.SlashEpoch +} + +func (m mstate) Equals(o market.DealState) bool { + return market.DealStatesEqual(m, o) +} + +func (m MarketDealState) Iface() market.DealState { + return mstate{m} +} + type MarketDeal struct { Proposal market.DealProposal - State market.DealState + State MarketDealState } type RetrievalOrder struct { diff --git a/api/api_gateway.go b/api/api_gateway.go index 238bf43ab..e71a8b712 100644 --- a/api/api_gateway.go +++ b/api/api_gateway.go @@ -129,4 +129,7 @@ type Gateway interface { Web3ClientVersion(ctx context.Context) (string, error) EthTraceBlock(ctx context.Context, blkNum string) ([]*ethtypes.EthTraceBlock, error) EthTraceReplayBlockTransactions(ctx context.Context, blkNum string, traceTypes []string) ([]*ethtypes.EthTraceReplayBlockTransaction, error) + + GetActorEvents(ctx context.Context, filter *types.ActorEventFilter) ([]*types.ActorEvent, error) + SubscribeActorEvents(ctx context.Context, filter *types.ActorEventFilter) (<-chan *types.ActorEvent, error) } diff --git a/api/api_storage.go b/api/api_storage.go index d5b3d5c1d..b24ee2af3 100644 --- a/api/api_storage.go +++ b/api/api_storage.go @@ -24,6 +24,7 @@ import ( builtinactors "github.com/filecoin-project/lotus/chain/actors/builtin" "github.com/filecoin-project/lotus/chain/actors/builtin/miner" "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/storage/pipeline/piece" "github.com/filecoin-project/lotus/storage/pipeline/sealiface" "github.com/filecoin-project/lotus/storage/sealer/fsutil" "github.com/filecoin-project/lotus/storage/sealer/storiface" @@ -75,7 +76,7 @@ type StorageMiner interface { // Add piece to an open sector. If no sectors with enough space are open, // either a new sector will be created, or this call will block until more // sectors can be created. - SectorAddPieceToAny(ctx context.Context, size abi.UnpaddedPieceSize, r storiface.Data, d PieceDealInfo) (SectorOffset, error) //perm:admin + SectorAddPieceToAny(ctx context.Context, size abi.UnpaddedPieceSize, r storiface.Data, d piece.PieceDealInfo) (SectorOffset, error) //perm:admin SectorsUnsealPiece(ctx context.Context, sector storiface.SectorRef, offset storiface.UnpaddedByteIndex, size abi.UnpaddedPieceSize, randomness abi.SealRandomness, commd *cid.Cid) error //perm:admin @@ -353,10 +354,21 @@ type SectorLog struct { } type SectorPiece struct { - Piece abi.PieceInfo - DealInfo *PieceDealInfo // nil for pieces which do not appear in deals (e.g. filler pieces) + Piece abi.PieceInfo + + // DealInfo is nil for pieces which do not appear in deals (e.g. filler pieces) + // NOTE: DDO pieces which aren't associated with a market deal and have no + // verified allocation will still have a non-nil DealInfo. + // nil DealInfo indicates that the piece is a filler, and has zero piece commitment. + DealInfo *piece.PieceDealInfo } +// DEPRECATED: Use piece.PieceDealInfo instead +type PieceDealInfo = piece.PieceDealInfo + +// DEPRECATED: Use piece.DealSchedule instead +type DealSchedule = piece.DealSchedule + type SectorInfo struct { SectorID abi.SectorNumber State SectorState @@ -459,28 +471,6 @@ type SectorOffset struct { Offset abi.PaddedPieceSize } -// DealInfo is a tuple of deal identity and its schedule -type PieceDealInfo struct { - // "Old" builtin-market deal info - PublishCid *cid.Cid - DealID abi.DealID - DealProposal *market.DealProposal - - // Common deal info - DealSchedule DealSchedule - - // Best-effort deal asks - KeepUnsealed bool -} - -// DealSchedule communicates the time interval of a storage deal. The deal must -// appear in a sealed (proven) sector no later than StartEpoch, otherwise it -// is invalid. -type DealSchedule struct { - StartEpoch abi.ChainEpoch - EndEpoch abi.ChainEpoch -} - // DagstoreShardInfo is the serialized form of dagstore.DagstoreShardInfo that // we expose through JSON-RPC to avoid clients having to depend on the // dagstore lib. diff --git a/api/cbor_gen.go b/api/cbor_gen.go index fd2cb30b4..7a3f97e59 100644 --- a/api/cbor_gen.go +++ b/api/cbor_gen.go @@ -14,7 +14,8 @@ import ( abi "github.com/filecoin-project/go-state-types/abi" paych "github.com/filecoin-project/go-state-types/builtin/v8/paych" - market "github.com/filecoin-project/go-state-types/builtin/v9/market" + + piece "github.com/filecoin-project/lotus/storage/pipeline/piece" ) var _ = xerrors.Errorf @@ -35,7 +36,7 @@ func (t *PaymentInfo) MarshalCBOR(w io.Writer) error { } // t.Channel (address.Address) (struct) - if len("Channel") > cbg.MaxLength { + if len("Channel") > 8192 { return xerrors.Errorf("Value in field \"Channel\" was too long") } @@ -51,7 +52,7 @@ func (t *PaymentInfo) MarshalCBOR(w io.Writer) error { } // t.Vouchers ([]*paych.SignedVoucher) (slice) - if len("Vouchers") > cbg.MaxLength { + if len("Vouchers") > 8192 { return xerrors.Errorf("Value in field \"Vouchers\" was too long") } @@ -62,7 +63,7 @@ func (t *PaymentInfo) MarshalCBOR(w io.Writer) error { return err } - if len(t.Vouchers) > cbg.MaxLength { + if len(t.Vouchers) > 8192 { return xerrors.Errorf("Slice value in field t.Vouchers was too long") } @@ -73,10 +74,11 @@ func (t *PaymentInfo) MarshalCBOR(w io.Writer) error { if err := v.MarshalCBOR(cw); err != nil { return err } + } // t.WaitSentinel (cid.Cid) (struct) - if len("WaitSentinel") > cbg.MaxLength { + if len("WaitSentinel") > 8192 { return xerrors.Errorf("Value in field \"WaitSentinel\" was too long") } @@ -123,7 +125,7 @@ func (t *PaymentInfo) UnmarshalCBOR(r io.Reader) (err error) { for i := uint64(0); i < n; i++ { { - sval, err := cbg.ReadString(cr) + sval, err := cbg.ReadStringWithMax(cr, 8192) if err != nil { return err } @@ -150,7 +152,7 @@ func (t *PaymentInfo) UnmarshalCBOR(r io.Reader) (err error) { return err } - if extra > cbg.MaxLength { + if extra > 8192 { return fmt.Errorf("t.Vouchers: array too large (%d)", extra) } @@ -188,9 +190,9 @@ func (t *PaymentInfo) UnmarshalCBOR(r io.Reader) (err error) { } } + } } - // t.WaitSentinel (cid.Cid) (struct) case "WaitSentinel": @@ -226,7 +228,7 @@ func (t *SealedRef) MarshalCBOR(w io.Writer) error { } // t.Size (abi.UnpaddedPieceSize) (uint64) - if len("Size") > cbg.MaxLength { + if len("Size") > 8192 { return xerrors.Errorf("Value in field \"Size\" was too long") } @@ -242,7 +244,7 @@ func (t *SealedRef) MarshalCBOR(w io.Writer) error { } // t.Offset (abi.PaddedPieceSize) (uint64) - if len("Offset") > cbg.MaxLength { + if len("Offset") > 8192 { return xerrors.Errorf("Value in field \"Offset\" was too long") } @@ -258,7 +260,7 @@ func (t *SealedRef) MarshalCBOR(w io.Writer) error { } // t.SectorID (abi.SectorNumber) (uint64) - if len("SectorID") > cbg.MaxLength { + if len("SectorID") > 8192 { return xerrors.Errorf("Value in field \"SectorID\" was too long") } @@ -305,7 +307,7 @@ func (t *SealedRef) UnmarshalCBOR(r io.Reader) (err error) { for i := uint64(0); i < n; i++ { { - sval, err := cbg.ReadString(cr) + sval, err := cbg.ReadStringWithMax(cr, 8192) if err != nil { return err } @@ -381,7 +383,7 @@ func (t *SealedRefs) MarshalCBOR(w io.Writer) error { } // t.Refs ([]api.SealedRef) (slice) - if len("Refs") > cbg.MaxLength { + if len("Refs") > 8192 { return xerrors.Errorf("Value in field \"Refs\" was too long") } @@ -392,7 +394,7 @@ func (t *SealedRefs) MarshalCBOR(w io.Writer) error { return err } - if len(t.Refs) > cbg.MaxLength { + if len(t.Refs) > 8192 { return xerrors.Errorf("Slice value in field t.Refs was too long") } @@ -403,6 +405,7 @@ func (t *SealedRefs) MarshalCBOR(w io.Writer) error { if err := v.MarshalCBOR(cw); err != nil { return err } + } return nil } @@ -436,7 +439,7 @@ func (t *SealedRefs) UnmarshalCBOR(r io.Reader) (err error) { for i := uint64(0); i < n; i++ { { - sval, err := cbg.ReadString(cr) + sval, err := cbg.ReadStringWithMax(cr, 8192) if err != nil { return err } @@ -453,7 +456,7 @@ func (t *SealedRefs) UnmarshalCBOR(r io.Reader) (err error) { return err } - if extra > cbg.MaxLength { + if extra > 8192 { return fmt.Errorf("t.Refs: array too large (%d)", extra) } @@ -481,6 +484,7 @@ func (t *SealedRefs) UnmarshalCBOR(r io.Reader) (err error) { } } + } } @@ -505,7 +509,7 @@ func (t *SealTicket) MarshalCBOR(w io.Writer) error { } // t.Epoch (abi.ChainEpoch) (int64) - if len("Epoch") > cbg.MaxLength { + if len("Epoch") > 8192 { return xerrors.Errorf("Value in field \"Epoch\" was too long") } @@ -527,7 +531,7 @@ func (t *SealTicket) MarshalCBOR(w io.Writer) error { } // t.Value (abi.SealRandomness) (slice) - if len("Value") > cbg.MaxLength { + if len("Value") > 8192 { return xerrors.Errorf("Value in field \"Value\" was too long") } @@ -538,7 +542,7 @@ func (t *SealTicket) MarshalCBOR(w io.Writer) error { return err } - if len(t.Value) > cbg.ByteArrayMaxLen { + if len(t.Value) > 2097152 { return xerrors.Errorf("Byte array in field t.Value was too long") } @@ -546,9 +550,10 @@ func (t *SealTicket) MarshalCBOR(w io.Writer) error { return err } - if _, err := cw.Write(t.Value[:]); err != nil { + if _, err := cw.Write(t.Value); err != nil { return err } + return nil } @@ -581,7 +586,7 @@ func (t *SealTicket) UnmarshalCBOR(r io.Reader) (err error) { for i := uint64(0); i < n; i++ { { - sval, err := cbg.ReadString(cr) + sval, err := cbg.ReadStringWithMax(cr, 8192) if err != nil { return err } @@ -594,10 +599,10 @@ func (t *SealTicket) UnmarshalCBOR(r io.Reader) (err error) { case "Epoch": { maj, extra, err := cr.ReadHeader() - var extraI int64 if err != nil { return err } + var extraI int64 switch maj { case cbg.MajUnsignedInt: extraI = int64(extra) @@ -624,7 +629,7 @@ func (t *SealTicket) UnmarshalCBOR(r io.Reader) (err error) { return err } - if extra > cbg.ByteArrayMaxLen { + if extra > 2097152 { return fmt.Errorf("t.Value: byte array too large (%d)", extra) } if maj != cbg.MajByteString { @@ -635,7 +640,7 @@ func (t *SealTicket) UnmarshalCBOR(r io.Reader) (err error) { t.Value = make([]uint8, extra) } - if _, err := io.ReadFull(cr, t.Value[:]); err != nil { + if _, err := io.ReadFull(cr, t.Value); err != nil { return err } @@ -660,7 +665,7 @@ func (t *SealSeed) MarshalCBOR(w io.Writer) error { } // t.Epoch (abi.ChainEpoch) (int64) - if len("Epoch") > cbg.MaxLength { + if len("Epoch") > 8192 { return xerrors.Errorf("Value in field \"Epoch\" was too long") } @@ -682,7 +687,7 @@ func (t *SealSeed) MarshalCBOR(w io.Writer) error { } // t.Value (abi.InteractiveSealRandomness) (slice) - if len("Value") > cbg.MaxLength { + if len("Value") > 8192 { return xerrors.Errorf("Value in field \"Value\" was too long") } @@ -693,7 +698,7 @@ func (t *SealSeed) MarshalCBOR(w io.Writer) error { return err } - if len(t.Value) > cbg.ByteArrayMaxLen { + if len(t.Value) > 2097152 { return xerrors.Errorf("Byte array in field t.Value was too long") } @@ -701,9 +706,10 @@ func (t *SealSeed) MarshalCBOR(w io.Writer) error { return err } - if _, err := cw.Write(t.Value[:]); err != nil { + if _, err := cw.Write(t.Value); err != nil { return err } + return nil } @@ -736,7 +742,7 @@ func (t *SealSeed) UnmarshalCBOR(r io.Reader) (err error) { for i := uint64(0); i < n; i++ { { - sval, err := cbg.ReadString(cr) + sval, err := cbg.ReadStringWithMax(cr, 8192) if err != nil { return err } @@ -749,10 +755,10 @@ func (t *SealSeed) UnmarshalCBOR(r io.Reader) (err error) { case "Epoch": { maj, extra, err := cr.ReadHeader() - var extraI int64 if err != nil { return err } + var extraI int64 switch maj { case cbg.MajUnsignedInt: extraI = int64(extra) @@ -779,7 +785,7 @@ func (t *SealSeed) UnmarshalCBOR(r io.Reader) (err error) { return err } - if extra > cbg.ByteArrayMaxLen { + if extra > 2097152 { return fmt.Errorf("t.Value: byte array too large (%d)", extra) } if maj != cbg.MajByteString { @@ -790,7 +796,7 @@ func (t *SealSeed) UnmarshalCBOR(r io.Reader) (err error) { t.Value = make([]uint8, extra) } - if _, err := io.ReadFull(cr, t.Value[:]); err != nil { + if _, err := io.ReadFull(cr, t.Value); err != nil { return err } @@ -802,239 +808,6 @@ func (t *SealSeed) UnmarshalCBOR(r io.Reader) (err error) { return nil } -func (t *PieceDealInfo) MarshalCBOR(w io.Writer) error { - if t == nil { - _, err := w.Write(cbg.CborNull) - return err - } - - cw := cbg.NewCborWriter(w) - - if _, err := cw.Write([]byte{165}); err != nil { - return err - } - - // t.DealID (abi.DealID) (uint64) - if len("DealID") > cbg.MaxLength { - return xerrors.Errorf("Value in field \"DealID\" was too long") - } - - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("DealID"))); err != nil { - return err - } - if _, err := cw.WriteString(string("DealID")); err != nil { - return err - } - - if err := cw.WriteMajorTypeHeader(cbg.MajUnsignedInt, uint64(t.DealID)); err != nil { - return err - } - - // t.PublishCid (cid.Cid) (struct) - if len("PublishCid") > cbg.MaxLength { - return xerrors.Errorf("Value in field \"PublishCid\" was too long") - } - - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("PublishCid"))); err != nil { - return err - } - if _, err := cw.WriteString(string("PublishCid")); err != nil { - return err - } - - if t.PublishCid == nil { - if _, err := cw.Write(cbg.CborNull); err != nil { - return err - } - } else { - if err := cbg.WriteCid(cw, *t.PublishCid); err != nil { - return xerrors.Errorf("failed to write cid field t.PublishCid: %w", err) - } - } - - // t.DealProposal (market.DealProposal) (struct) - if len("DealProposal") > cbg.MaxLength { - return xerrors.Errorf("Value in field \"DealProposal\" was too long") - } - - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("DealProposal"))); err != nil { - return err - } - if _, err := cw.WriteString(string("DealProposal")); err != nil { - return err - } - - if err := t.DealProposal.MarshalCBOR(cw); err != nil { - return err - } - - // t.DealSchedule (api.DealSchedule) (struct) - if len("DealSchedule") > cbg.MaxLength { - return xerrors.Errorf("Value in field \"DealSchedule\" was too long") - } - - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("DealSchedule"))); err != nil { - return err - } - if _, err := cw.WriteString(string("DealSchedule")); err != nil { - return err - } - - if err := t.DealSchedule.MarshalCBOR(cw); err != nil { - return err - } - - // t.KeepUnsealed (bool) (bool) - if len("KeepUnsealed") > cbg.MaxLength { - return xerrors.Errorf("Value in field \"KeepUnsealed\" was too long") - } - - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("KeepUnsealed"))); err != nil { - return err - } - if _, err := cw.WriteString(string("KeepUnsealed")); err != nil { - return err - } - - if err := cbg.WriteBool(w, t.KeepUnsealed); err != nil { - return err - } - return nil -} - -func (t *PieceDealInfo) UnmarshalCBOR(r io.Reader) (err error) { - *t = PieceDealInfo{} - - cr := cbg.NewCborReader(r) - - maj, extra, err := cr.ReadHeader() - if err != nil { - return err - } - defer func() { - if err == io.EOF { - err = io.ErrUnexpectedEOF - } - }() - - if maj != cbg.MajMap { - return fmt.Errorf("cbor input should be of type map") - } - - if extra > cbg.MaxLength { - return fmt.Errorf("PieceDealInfo: map struct too large (%d)", extra) - } - - var name string - n := extra - - for i := uint64(0); i < n; i++ { - - { - sval, err := cbg.ReadString(cr) - if err != nil { - return err - } - - name = string(sval) - } - - switch name { - // t.DealID (abi.DealID) (uint64) - case "DealID": - - { - - maj, extra, err = cr.ReadHeader() - if err != nil { - return err - } - if maj != cbg.MajUnsignedInt { - return fmt.Errorf("wrong type for uint64 field") - } - t.DealID = abi.DealID(extra) - - } - // t.PublishCid (cid.Cid) (struct) - case "PublishCid": - - { - - b, err := cr.ReadByte() - if err != nil { - return err - } - if b != cbg.CborNull[0] { - if err := cr.UnreadByte(); err != nil { - return err - } - - c, err := cbg.ReadCid(cr) - if err != nil { - return xerrors.Errorf("failed to read cid field t.PublishCid: %w", err) - } - - t.PublishCid = &c - } - - } - // t.DealProposal (market.DealProposal) (struct) - case "DealProposal": - - { - - b, err := cr.ReadByte() - if err != nil { - return err - } - if b != cbg.CborNull[0] { - if err := cr.UnreadByte(); err != nil { - return err - } - t.DealProposal = new(market.DealProposal) - if err := t.DealProposal.UnmarshalCBOR(cr); err != nil { - return xerrors.Errorf("unmarshaling t.DealProposal pointer: %w", err) - } - } - - } - // t.DealSchedule (api.DealSchedule) (struct) - case "DealSchedule": - - { - - if err := t.DealSchedule.UnmarshalCBOR(cr); err != nil { - return xerrors.Errorf("unmarshaling t.DealSchedule: %w", err) - } - - } - // t.KeepUnsealed (bool) (bool) - case "KeepUnsealed": - - maj, extra, err = cr.ReadHeader() - if err != nil { - return err - } - if maj != cbg.MajOther { - return fmt.Errorf("booleans must be major type 7") - } - switch extra { - case 20: - t.KeepUnsealed = false - case 21: - t.KeepUnsealed = true - default: - return fmt.Errorf("booleans are either major type 7, value 20 or 21 (got %d)", extra) - } - - default: - // Field doesn't exist on this type, so ignore it - cbg.ScanForLinks(r, func(cid.Cid) {}) - } - } - - return nil -} func (t *SectorPiece) MarshalCBOR(w io.Writer) error { if t == nil { _, err := w.Write(cbg.CborNull) @@ -1048,7 +821,7 @@ func (t *SectorPiece) MarshalCBOR(w io.Writer) error { } // t.Piece (abi.PieceInfo) (struct) - if len("Piece") > cbg.MaxLength { + if len("Piece") > 8192 { return xerrors.Errorf("Value in field \"Piece\" was too long") } @@ -1063,8 +836,8 @@ func (t *SectorPiece) MarshalCBOR(w io.Writer) error { return err } - // t.DealInfo (api.PieceDealInfo) (struct) - if len("DealInfo") > cbg.MaxLength { + // t.DealInfo (piece.PieceDealInfo) (struct) + if len("DealInfo") > 8192 { return xerrors.Errorf("Value in field \"DealInfo\" was too long") } @@ -1110,7 +883,7 @@ func (t *SectorPiece) UnmarshalCBOR(r io.Reader) (err error) { for i := uint64(0); i < n; i++ { { - sval, err := cbg.ReadString(cr) + sval, err := cbg.ReadStringWithMax(cr, 8192) if err != nil { return err } @@ -1129,7 +902,7 @@ func (t *SectorPiece) UnmarshalCBOR(r io.Reader) (err error) { } } - // t.DealInfo (api.PieceDealInfo) (struct) + // t.DealInfo (piece.PieceDealInfo) (struct) case "DealInfo": { @@ -1142,7 +915,7 @@ func (t *SectorPiece) UnmarshalCBOR(r io.Reader) (err error) { if err := cr.UnreadByte(); err != nil { return err } - t.DealInfo = new(PieceDealInfo) + t.DealInfo = new(piece.PieceDealInfo) if err := t.DealInfo.UnmarshalCBOR(cr); err != nil { return xerrors.Errorf("unmarshaling t.DealInfo pointer: %w", err) } @@ -1158,160 +931,3 @@ func (t *SectorPiece) UnmarshalCBOR(r io.Reader) (err error) { return nil } -func (t *DealSchedule) MarshalCBOR(w io.Writer) error { - if t == nil { - _, err := w.Write(cbg.CborNull) - return err - } - - cw := cbg.NewCborWriter(w) - - if _, err := cw.Write([]byte{162}); err != nil { - return err - } - - // t.EndEpoch (abi.ChainEpoch) (int64) - if len("EndEpoch") > cbg.MaxLength { - return xerrors.Errorf("Value in field \"EndEpoch\" was too long") - } - - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("EndEpoch"))); err != nil { - return err - } - if _, err := cw.WriteString(string("EndEpoch")); err != nil { - return err - } - - if t.EndEpoch >= 0 { - if err := cw.WriteMajorTypeHeader(cbg.MajUnsignedInt, uint64(t.EndEpoch)); err != nil { - return err - } - } else { - if err := cw.WriteMajorTypeHeader(cbg.MajNegativeInt, uint64(-t.EndEpoch-1)); err != nil { - return err - } - } - - // t.StartEpoch (abi.ChainEpoch) (int64) - if len("StartEpoch") > cbg.MaxLength { - return xerrors.Errorf("Value in field \"StartEpoch\" was too long") - } - - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("StartEpoch"))); err != nil { - return err - } - if _, err := cw.WriteString(string("StartEpoch")); err != nil { - return err - } - - if t.StartEpoch >= 0 { - if err := cw.WriteMajorTypeHeader(cbg.MajUnsignedInt, uint64(t.StartEpoch)); err != nil { - return err - } - } else { - if err := cw.WriteMajorTypeHeader(cbg.MajNegativeInt, uint64(-t.StartEpoch-1)); err != nil { - return err - } - } - return nil -} - -func (t *DealSchedule) UnmarshalCBOR(r io.Reader) (err error) { - *t = DealSchedule{} - - cr := cbg.NewCborReader(r) - - maj, extra, err := cr.ReadHeader() - if err != nil { - return err - } - defer func() { - if err == io.EOF { - err = io.ErrUnexpectedEOF - } - }() - - if maj != cbg.MajMap { - return fmt.Errorf("cbor input should be of type map") - } - - if extra > cbg.MaxLength { - return fmt.Errorf("DealSchedule: map struct too large (%d)", extra) - } - - var name string - n := extra - - for i := uint64(0); i < n; i++ { - - { - sval, err := cbg.ReadString(cr) - if err != nil { - return err - } - - name = string(sval) - } - - switch name { - // t.EndEpoch (abi.ChainEpoch) (int64) - case "EndEpoch": - { - maj, extra, err := cr.ReadHeader() - var extraI int64 - if err != nil { - return err - } - switch maj { - case cbg.MajUnsignedInt: - extraI = int64(extra) - if extraI < 0 { - return fmt.Errorf("int64 positive overflow") - } - case cbg.MajNegativeInt: - extraI = int64(extra) - if extraI < 0 { - return fmt.Errorf("int64 negative overflow") - } - extraI = -1 - extraI - default: - return fmt.Errorf("wrong type for int64 field: %d", maj) - } - - t.EndEpoch = abi.ChainEpoch(extraI) - } - // t.StartEpoch (abi.ChainEpoch) (int64) - case "StartEpoch": - { - maj, extra, err := cr.ReadHeader() - var extraI int64 - if err != nil { - return err - } - switch maj { - case cbg.MajUnsignedInt: - extraI = int64(extra) - if extraI < 0 { - return fmt.Errorf("int64 positive overflow") - } - case cbg.MajNegativeInt: - extraI = int64(extra) - if extraI < 0 { - return fmt.Errorf("int64 negative overflow") - } - extraI = -1 - extraI - default: - return fmt.Errorf("wrong type for int64 field: %d", maj) - } - - t.StartEpoch = abi.ChainEpoch(extraI) - } - - default: - // Field doesn't exist on this type, so ignore it - cbg.ScanForLinks(r, func(cid.Cid) {}) - } - } - - return nil -} diff --git a/api/docgen/docgen.go b/api/docgen/docgen.go index 6028a40f2..29ac1d3e8 100644 --- a/api/docgen/docgen.go +++ b/api/docgen/docgen.go @@ -32,7 +32,6 @@ import ( "github.com/filecoin-project/go-fil-markets/retrievalmarket" "github.com/filecoin-project/go-jsonrpc/auth" "github.com/filecoin-project/go-state-types/abi" - "github.com/filecoin-project/go-state-types/builtin/v12/miner" "github.com/filecoin-project/go-state-types/builtin/v9/verifreg" "github.com/filecoin-project/go-state-types/crypto" "github.com/filecoin-project/go-state-types/exitcode" @@ -41,6 +40,7 @@ import ( apitypes "github.com/filecoin-project/lotus/api/types" "github.com/filecoin-project/lotus/api/v0api" "github.com/filecoin-project/lotus/build" + "github.com/filecoin-project/lotus/chain/actors/builtin/miner" "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/types/ethtypes" "github.com/filecoin-project/lotus/node/modules/dtypes" @@ -404,6 +404,32 @@ func init() { percent := types.Percent(123) addExample(percent) addExample(&percent) + + addExample(&miner.PieceActivationManifest{ + CID: c, + Size: 2032, + VerifiedAllocationKey: nil, + Notify: nil, + }) + + addExample(&types.ActorEventBlock{ + Codec: 0x51, + Value: []byte("ddata"), + }) + + addExample(&types.ActorEventFilter{ + Addresses: []address.Address{addr}, + Fields: map[string][]types.ActorEventBlock{ + "abc": { + { + Codec: 0x51, + Value: []byte("ddata"), + }, + }, + }, + FromHeight: epochPtr(1010), + ToHeight: epochPtr(1020), + }) } func GetAPIType(name, pkg string) (i interface{}, t reflect.Type, permStruct []reflect.Type) { @@ -509,6 +535,11 @@ func exampleStruct(method string, t, parent reflect.Type) interface{} { return ns.Interface() } +func epochPtr(ei int64) *abi.ChainEpoch { + ep := abi.ChainEpoch(ei) + return &ep +} + type Visitor struct { Root string Methods map[string]ast.Node diff --git a/api/mocks/mock_full.go b/api/mocks/mock_full.go index f4a9832bb..215ccdfeb 100644 --- a/api/mocks/mock_full.go +++ b/api/mocks/mock_full.go @@ -27,7 +27,7 @@ import ( auth "github.com/filecoin-project/go-jsonrpc/auth" abi "github.com/filecoin-project/go-state-types/abi" big "github.com/filecoin-project/go-state-types/big" - miner "github.com/filecoin-project/go-state-types/builtin/v12/miner" + miner "github.com/filecoin-project/go-state-types/builtin/v13/miner" paych "github.com/filecoin-project/go-state-types/builtin/v8/paych" miner0 "github.com/filecoin-project/go-state-types/builtin/v9/miner" verifreg "github.com/filecoin-project/go-state-types/builtin/v9/verifreg" @@ -1627,6 +1627,21 @@ func (mr *MockFullNodeMockRecorder) GasEstimateMessageGas(arg0, arg1, arg2, arg3 return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GasEstimateMessageGas", reflect.TypeOf((*MockFullNode)(nil).GasEstimateMessageGas), arg0, arg1, arg2, arg3) } +// GetActorEvents mocks base method. +func (m *MockFullNode) GetActorEvents(arg0 context.Context, arg1 *types.ActorEventFilter) ([]*types.ActorEvent, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetActorEvents", arg0, arg1) + ret0, _ := ret[0].([]*types.ActorEvent) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetActorEvents indicates an expected call of GetActorEvents. +func (mr *MockFullNodeMockRecorder) GetActorEvents(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetActorEvents", reflect.TypeOf((*MockFullNode)(nil).GetActorEvents), arg0, arg1) +} + // ID mocks base method. func (m *MockFullNode) ID(arg0 context.Context) (peer.ID, error) { m.ctrl.T.Helper() @@ -3159,6 +3174,36 @@ func (mr *MockFullNodeMockRecorder) StateGetActor(arg0, arg1, arg2 interface{}) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StateGetActor", reflect.TypeOf((*MockFullNode)(nil).StateGetActor), arg0, arg1, arg2) } +// StateGetAllAllocations mocks base method. +func (m *MockFullNode) StateGetAllAllocations(arg0 context.Context, arg1 types.TipSetKey) (map[verifreg.AllocationId]verifreg.Allocation, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "StateGetAllAllocations", arg0, arg1) + ret0, _ := ret[0].(map[verifreg.AllocationId]verifreg.Allocation) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// StateGetAllAllocations indicates an expected call of StateGetAllAllocations. +func (mr *MockFullNodeMockRecorder) StateGetAllAllocations(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StateGetAllAllocations", reflect.TypeOf((*MockFullNode)(nil).StateGetAllAllocations), arg0, arg1) +} + +// StateGetAllClaims mocks base method. +func (m *MockFullNode) StateGetAllClaims(arg0 context.Context, arg1 types.TipSetKey) (map[verifreg.ClaimId]verifreg.Claim, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "StateGetAllClaims", arg0, arg1) + ret0, _ := ret[0].(map[verifreg.ClaimId]verifreg.Claim) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// StateGetAllClaims indicates an expected call of StateGetAllClaims. +func (mr *MockFullNodeMockRecorder) StateGetAllClaims(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StateGetAllClaims", reflect.TypeOf((*MockFullNode)(nil).StateGetAllClaims), arg0, arg1) +} + // StateGetAllocation mocks base method. func (m *MockFullNode) StateGetAllocation(arg0 context.Context, arg1 address.Address, arg2 verifreg.AllocationId, arg3 types.TipSetKey) (*verifreg.Allocation, error) { m.ctrl.T.Helper() @@ -3189,6 +3234,21 @@ func (mr *MockFullNodeMockRecorder) StateGetAllocationForPendingDeal(arg0, arg1, return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StateGetAllocationForPendingDeal", reflect.TypeOf((*MockFullNode)(nil).StateGetAllocationForPendingDeal), arg0, arg1, arg2) } +// StateGetAllocationIdForPendingDeal mocks base method. +func (m *MockFullNode) StateGetAllocationIdForPendingDeal(arg0 context.Context, arg1 abi.DealID, arg2 types.TipSetKey) (verifreg.AllocationId, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "StateGetAllocationIdForPendingDeal", arg0, arg1, arg2) + ret0, _ := ret[0].(verifreg.AllocationId) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// StateGetAllocationIdForPendingDeal indicates an expected call of StateGetAllocationIdForPendingDeal. +func (mr *MockFullNodeMockRecorder) StateGetAllocationIdForPendingDeal(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StateGetAllocationIdForPendingDeal", reflect.TypeOf((*MockFullNode)(nil).StateGetAllocationIdForPendingDeal), arg0, arg1, arg2) +} + // StateGetAllocations mocks base method. func (m *MockFullNode) StateGetAllocations(arg0 context.Context, arg1 address.Address, arg2 types.TipSetKey) (map[verifreg.AllocationId]verifreg.Allocation, error) { m.ctrl.T.Helper() @@ -3894,6 +3954,21 @@ func (mr *MockFullNodeMockRecorder) StateWaitMsg(arg0, arg1, arg2, arg3, arg4 in return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StateWaitMsg", reflect.TypeOf((*MockFullNode)(nil).StateWaitMsg), arg0, arg1, arg2, arg3, arg4) } +// SubscribeActorEvents mocks base method. +func (m *MockFullNode) SubscribeActorEvents(arg0 context.Context, arg1 *types.ActorEventFilter) (<-chan *types.ActorEvent, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SubscribeActorEvents", arg0, arg1) + ret0, _ := ret[0].(<-chan *types.ActorEvent) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// SubscribeActorEvents indicates an expected call of SubscribeActorEvents. +func (mr *MockFullNodeMockRecorder) SubscribeActorEvents(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubscribeActorEvents", reflect.TypeOf((*MockFullNode)(nil).SubscribeActorEvents), arg0, arg1) +} + // SyncCheckBad mocks base method. func (m *MockFullNode) SyncCheckBad(arg0 context.Context, arg1 cid.Cid) (string, error) { m.ctrl.T.Helper() diff --git a/api/proxy_gen.go b/api/proxy_gen.go index 97e441bbe..bd33ae445 100644 --- a/api/proxy_gen.go +++ b/api/proxy_gen.go @@ -37,11 +37,13 @@ import ( apitypes "github.com/filecoin-project/lotus/api/types" builtinactors "github.com/filecoin-project/lotus/chain/actors/builtin" "github.com/filecoin-project/lotus/chain/actors/builtin/miner" + "github.com/filecoin-project/lotus/chain/actors/builtin/verifreg" "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/types/ethtypes" "github.com/filecoin-project/lotus/journal/alerting" "github.com/filecoin-project/lotus/node/modules/dtypes" "github.com/filecoin-project/lotus/node/repo/imports" + "github.com/filecoin-project/lotus/storage/pipeline/piece" "github.com/filecoin-project/lotus/storage/pipeline/sealiface" "github.com/filecoin-project/lotus/storage/sealer/fsutil" "github.com/filecoin-project/lotus/storage/sealer/sealtasks" @@ -335,6 +337,8 @@ type FullNodeMethods struct { GasEstimateMessageGas func(p0 context.Context, p1 *types.Message, p2 *MessageSendSpec, p3 types.TipSetKey) (*types.Message, error) `perm:"read"` + GetActorEvents func(p0 context.Context, p1 *types.ActorEventFilter) ([]*types.ActorEvent, error) `perm:"read"` + MarketAddBalance func(p0 context.Context, p1 address.Address, p2 address.Address, p3 types.BigInt) (cid.Cid, error) `perm:"sign"` MarketGetReserved func(p0 context.Context, p1 address.Address) (types.BigInt, error) `perm:"sign"` @@ -481,10 +485,16 @@ type FullNodeMethods struct { StateGetActor func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (*types.Actor, error) `perm:"read"` + StateGetAllAllocations func(p0 context.Context, p1 types.TipSetKey) (map[verifregtypes.AllocationId]verifregtypes.Allocation, error) `perm:"read"` + + StateGetAllClaims func(p0 context.Context, p1 types.TipSetKey) (map[verifregtypes.ClaimId]verifregtypes.Claim, error) `perm:"read"` + StateGetAllocation func(p0 context.Context, p1 address.Address, p2 verifregtypes.AllocationId, p3 types.TipSetKey) (*verifregtypes.Allocation, error) `perm:"read"` StateGetAllocationForPendingDeal func(p0 context.Context, p1 abi.DealID, p2 types.TipSetKey) (*verifregtypes.Allocation, error) `perm:"read"` + StateGetAllocationIdForPendingDeal func(p0 context.Context, p1 abi.DealID, p2 types.TipSetKey) (verifreg.AllocationId, error) `perm:"read"` + StateGetAllocations func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (map[verifregtypes.AllocationId]verifregtypes.Allocation, error) `perm:"read"` StateGetBeaconEntry func(p0 context.Context, p1 abi.ChainEpoch) (*types.BeaconEntry, error) `perm:"read"` @@ -579,6 +589,8 @@ type FullNodeMethods struct { StateWaitMsg func(p0 context.Context, p1 cid.Cid, p2 uint64, p3 abi.ChainEpoch, p4 bool) (*MsgLookup, error) `perm:"read"` + SubscribeActorEvents func(p0 context.Context, p1 *types.ActorEventFilter) (<-chan *types.ActorEvent, error) `perm:"read"` + SyncCheckBad func(p0 context.Context, p1 cid.Cid) (string, error) `perm:"read"` SyncCheckpoint func(p0 context.Context, p1 types.TipSetKey) error `perm:"admin"` @@ -745,6 +757,8 @@ type GatewayMethods struct { GasEstimateMessageGas func(p0 context.Context, p1 *types.Message, p2 *MessageSendSpec, p3 types.TipSetKey) (*types.Message, error) `` + GetActorEvents func(p0 context.Context, p1 *types.ActorEventFilter) ([]*types.ActorEvent, error) `` + MinerGetBaseInfo func(p0 context.Context, p1 address.Address, p2 abi.ChainEpoch, p3 types.TipSetKey) (*MiningBaseInfo, error) `` MpoolGetNonce func(p0 context.Context, p1 address.Address) (uint64, error) `` @@ -819,6 +833,8 @@ type GatewayMethods struct { StateWaitMsg func(p0 context.Context, p1 cid.Cid, p2 uint64, p3 abi.ChainEpoch, p4 bool) (*MsgLookup, error) `` + SubscribeActorEvents func(p0 context.Context, p1 *types.ActorEventFilter) (<-chan *types.ActorEvent, error) `` + Version func(p0 context.Context) (APIVersion, error) `` WalletBalance func(p0 context.Context, p1 address.Address) (types.BigInt, error) `` @@ -1099,7 +1115,7 @@ type StorageMinerMethods struct { SectorAbortUpgrade func(p0 context.Context, p1 abi.SectorNumber) error `perm:"admin"` - SectorAddPieceToAny func(p0 context.Context, p1 abi.UnpaddedPieceSize, p2 storiface.Data, p3 PieceDealInfo) (SectorOffset, error) `perm:"admin"` + SectorAddPieceToAny func(p0 context.Context, p1 abi.UnpaddedPieceSize, p2 storiface.Data, p3 piece.PieceDealInfo) (SectorOffset, error) `perm:"admin"` SectorCommitFlush func(p0 context.Context) ([]sealiface.CommitBatchRes, error) `perm:"admin"` @@ -2590,6 +2606,17 @@ func (s *FullNodeStub) GasEstimateMessageGas(p0 context.Context, p1 *types.Messa return nil, ErrNotSupported } +func (s *FullNodeStruct) GetActorEvents(p0 context.Context, p1 *types.ActorEventFilter) ([]*types.ActorEvent, error) { + if s.Internal.GetActorEvents == nil { + return *new([]*types.ActorEvent), ErrNotSupported + } + return s.Internal.GetActorEvents(p0, p1) +} + +func (s *FullNodeStub) GetActorEvents(p0 context.Context, p1 *types.ActorEventFilter) ([]*types.ActorEvent, error) { + return *new([]*types.ActorEvent), ErrNotSupported +} + func (s *FullNodeStruct) MarketAddBalance(p0 context.Context, p1 address.Address, p2 address.Address, p3 types.BigInt) (cid.Cid, error) { if s.Internal.MarketAddBalance == nil { return *new(cid.Cid), ErrNotSupported @@ -3393,6 +3420,28 @@ func (s *FullNodeStub) StateGetActor(p0 context.Context, p1 address.Address, p2 return nil, ErrNotSupported } +func (s *FullNodeStruct) StateGetAllAllocations(p0 context.Context, p1 types.TipSetKey) (map[verifregtypes.AllocationId]verifregtypes.Allocation, error) { + if s.Internal.StateGetAllAllocations == nil { + return *new(map[verifregtypes.AllocationId]verifregtypes.Allocation), ErrNotSupported + } + return s.Internal.StateGetAllAllocations(p0, p1) +} + +func (s *FullNodeStub) StateGetAllAllocations(p0 context.Context, p1 types.TipSetKey) (map[verifregtypes.AllocationId]verifregtypes.Allocation, error) { + return *new(map[verifregtypes.AllocationId]verifregtypes.Allocation), ErrNotSupported +} + +func (s *FullNodeStruct) StateGetAllClaims(p0 context.Context, p1 types.TipSetKey) (map[verifregtypes.ClaimId]verifregtypes.Claim, error) { + if s.Internal.StateGetAllClaims == nil { + return *new(map[verifregtypes.ClaimId]verifregtypes.Claim), ErrNotSupported + } + return s.Internal.StateGetAllClaims(p0, p1) +} + +func (s *FullNodeStub) StateGetAllClaims(p0 context.Context, p1 types.TipSetKey) (map[verifregtypes.ClaimId]verifregtypes.Claim, error) { + return *new(map[verifregtypes.ClaimId]verifregtypes.Claim), ErrNotSupported +} + func (s *FullNodeStruct) StateGetAllocation(p0 context.Context, p1 address.Address, p2 verifregtypes.AllocationId, p3 types.TipSetKey) (*verifregtypes.Allocation, error) { if s.Internal.StateGetAllocation == nil { return nil, ErrNotSupported @@ -3415,6 +3464,17 @@ func (s *FullNodeStub) StateGetAllocationForPendingDeal(p0 context.Context, p1 a return nil, ErrNotSupported } +func (s *FullNodeStruct) StateGetAllocationIdForPendingDeal(p0 context.Context, p1 abi.DealID, p2 types.TipSetKey) (verifreg.AllocationId, error) { + if s.Internal.StateGetAllocationIdForPendingDeal == nil { + return *new(verifreg.AllocationId), ErrNotSupported + } + return s.Internal.StateGetAllocationIdForPendingDeal(p0, p1, p2) +} + +func (s *FullNodeStub) StateGetAllocationIdForPendingDeal(p0 context.Context, p1 abi.DealID, p2 types.TipSetKey) (verifreg.AllocationId, error) { + return *new(verifreg.AllocationId), ErrNotSupported +} + func (s *FullNodeStruct) StateGetAllocations(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (map[verifregtypes.AllocationId]verifregtypes.Allocation, error) { if s.Internal.StateGetAllocations == nil { return *new(map[verifregtypes.AllocationId]verifregtypes.Allocation), ErrNotSupported @@ -3932,6 +3992,17 @@ func (s *FullNodeStub) StateWaitMsg(p0 context.Context, p1 cid.Cid, p2 uint64, p return nil, ErrNotSupported } +func (s *FullNodeStruct) SubscribeActorEvents(p0 context.Context, p1 *types.ActorEventFilter) (<-chan *types.ActorEvent, error) { + if s.Internal.SubscribeActorEvents == nil { + return nil, ErrNotSupported + } + return s.Internal.SubscribeActorEvents(p0, p1) +} + +func (s *FullNodeStub) SubscribeActorEvents(p0 context.Context, p1 *types.ActorEventFilter) (<-chan *types.ActorEvent, error) { + return nil, ErrNotSupported +} + func (s *FullNodeStruct) SyncCheckBad(p0 context.Context, p1 cid.Cid) (string, error) { if s.Internal.SyncCheckBad == nil { return "", ErrNotSupported @@ -4779,6 +4850,17 @@ func (s *GatewayStub) GasEstimateMessageGas(p0 context.Context, p1 *types.Messag return nil, ErrNotSupported } +func (s *GatewayStruct) GetActorEvents(p0 context.Context, p1 *types.ActorEventFilter) ([]*types.ActorEvent, error) { + if s.Internal.GetActorEvents == nil { + return *new([]*types.ActorEvent), ErrNotSupported + } + return s.Internal.GetActorEvents(p0, p1) +} + +func (s *GatewayStub) GetActorEvents(p0 context.Context, p1 *types.ActorEventFilter) ([]*types.ActorEvent, error) { + return *new([]*types.ActorEvent), ErrNotSupported +} + func (s *GatewayStruct) MinerGetBaseInfo(p0 context.Context, p1 address.Address, p2 abi.ChainEpoch, p3 types.TipSetKey) (*MiningBaseInfo, error) { if s.Internal.MinerGetBaseInfo == nil { return nil, ErrNotSupported @@ -5186,6 +5268,17 @@ func (s *GatewayStub) StateWaitMsg(p0 context.Context, p1 cid.Cid, p2 uint64, p3 return nil, ErrNotSupported } +func (s *GatewayStruct) SubscribeActorEvents(p0 context.Context, p1 *types.ActorEventFilter) (<-chan *types.ActorEvent, error) { + if s.Internal.SubscribeActorEvents == nil { + return nil, ErrNotSupported + } + return s.Internal.SubscribeActorEvents(p0, p1) +} + +func (s *GatewayStub) SubscribeActorEvents(p0 context.Context, p1 *types.ActorEventFilter) (<-chan *types.ActorEvent, error) { + return nil, ErrNotSupported +} + func (s *GatewayStruct) Version(p0 context.Context) (APIVersion, error) { if s.Internal.Version == nil { return *new(APIVersion), ErrNotSupported @@ -6506,14 +6599,14 @@ func (s *StorageMinerStub) SectorAbortUpgrade(p0 context.Context, p1 abi.SectorN return ErrNotSupported } -func (s *StorageMinerStruct) SectorAddPieceToAny(p0 context.Context, p1 abi.UnpaddedPieceSize, p2 storiface.Data, p3 PieceDealInfo) (SectorOffset, error) { +func (s *StorageMinerStruct) SectorAddPieceToAny(p0 context.Context, p1 abi.UnpaddedPieceSize, p2 storiface.Data, p3 piece.PieceDealInfo) (SectorOffset, error) { if s.Internal.SectorAddPieceToAny == nil { return *new(SectorOffset), ErrNotSupported } return s.Internal.SectorAddPieceToAny(p0, p1, p2, p3) } -func (s *StorageMinerStub) SectorAddPieceToAny(p0 context.Context, p1 abi.UnpaddedPieceSize, p2 storiface.Data, p3 PieceDealInfo) (SectorOffset, error) { +func (s *StorageMinerStub) SectorAddPieceToAny(p0 context.Context, p1 abi.UnpaddedPieceSize, p2 storiface.Data, p3 piece.PieceDealInfo) (SectorOffset, error) { return *new(SectorOffset), ErrNotSupported } diff --git a/api/types.go b/api/types.go index 7fd607750..b7dbe7b36 100644 --- a/api/types.go +++ b/api/types.go @@ -344,6 +344,8 @@ type ForkUpgradeParams struct { UpgradeLightningHeight abi.ChainEpoch UpgradeThunderHeight abi.ChainEpoch UpgradeWatermelonHeight abi.ChainEpoch + UpgradeDragonHeight abi.ChainEpoch + UpgradePhoenixHeight abi.ChainEpoch } // ChainExportConfig holds configuration for chain ranged exports. diff --git a/api/v0api/full.go b/api/v0api/full.go index d92d5a95c..db84ddc87 100644 --- a/api/v0api/full.go +++ b/api/v0api/full.go @@ -537,10 +537,14 @@ type FullNode interface { StateGetAllocation(ctx context.Context, clientAddr address.Address, allocationId verifregtypes.AllocationId, tsk types.TipSetKey) (*verifregtypes.Allocation, error) //perm:read // StateGetAllocations returns the all the allocations for a given client. StateGetAllocations(ctx context.Context, clientAddr address.Address, tsk types.TipSetKey) (map[verifregtypes.AllocationId]verifregtypes.Allocation, error) //perm:read + // StateGetAllAllocations returns the all the allocations available in verified registry actor. + StateGetAllAllocations(ctx context.Context, tsk types.TipSetKey) (map[verifregtypes.AllocationId]verifregtypes.Allocation, error) //perm:read // StateGetClaim returns the claim for a given address and claim ID. StateGetClaim(ctx context.Context, providerAddr address.Address, claimId verifregtypes.ClaimId, tsk types.TipSetKey) (*verifregtypes.Claim, error) //perm:read // StateGetClaims returns the all the claims for a given provider. StateGetClaims(ctx context.Context, providerAddr address.Address, tsk types.TipSetKey) (map[verifregtypes.ClaimId]verifregtypes.Claim, error) //perm:read + // StateGetAllClaims returns the all the claims available in verified registry actor. + StateGetAllClaims(ctx context.Context, tsk types.TipSetKey) (map[verifregtypes.ClaimId]verifregtypes.Claim, error) //perm:read // StateLookupID retrieves the ID address of the given address StateLookupID(context.Context, address.Address, types.TipSetKey) (address.Address, error) //perm:read // StateAccountKey returns the public key address of the given ID address diff --git a/api/v0api/proxy_gen.go b/api/v0api/proxy_gen.go index bd37f6429..90c25d4a7 100644 --- a/api/v0api/proxy_gen.go +++ b/api/v0api/proxy_gen.go @@ -280,6 +280,10 @@ type FullNodeMethods struct { StateGetActor func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (*types.Actor, error) `perm:"read"` + StateGetAllAllocations func(p0 context.Context, p1 types.TipSetKey) (map[verifregtypes.AllocationId]verifregtypes.Allocation, error) `perm:"read"` + + StateGetAllClaims func(p0 context.Context, p1 types.TipSetKey) (map[verifregtypes.ClaimId]verifregtypes.Claim, error) `perm:"read"` + StateGetAllocation func(p0 context.Context, p1 address.Address, p2 verifregtypes.AllocationId, p3 types.TipSetKey) (*verifregtypes.Allocation, error) `perm:"read"` StateGetAllocationForPendingDeal func(p0 context.Context, p1 abi.DealID, p2 types.TipSetKey) (*verifregtypes.Allocation, error) `perm:"read"` @@ -1837,6 +1841,28 @@ func (s *FullNodeStub) StateGetActor(p0 context.Context, p1 address.Address, p2 return nil, ErrNotSupported } +func (s *FullNodeStruct) StateGetAllAllocations(p0 context.Context, p1 types.TipSetKey) (map[verifregtypes.AllocationId]verifregtypes.Allocation, error) { + if s.Internal.StateGetAllAllocations == nil { + return *new(map[verifregtypes.AllocationId]verifregtypes.Allocation), ErrNotSupported + } + return s.Internal.StateGetAllAllocations(p0, p1) +} + +func (s *FullNodeStub) StateGetAllAllocations(p0 context.Context, p1 types.TipSetKey) (map[verifregtypes.AllocationId]verifregtypes.Allocation, error) { + return *new(map[verifregtypes.AllocationId]verifregtypes.Allocation), ErrNotSupported +} + +func (s *FullNodeStruct) StateGetAllClaims(p0 context.Context, p1 types.TipSetKey) (map[verifregtypes.ClaimId]verifregtypes.Claim, error) { + if s.Internal.StateGetAllClaims == nil { + return *new(map[verifregtypes.ClaimId]verifregtypes.Claim), ErrNotSupported + } + return s.Internal.StateGetAllClaims(p0, p1) +} + +func (s *FullNodeStub) StateGetAllClaims(p0 context.Context, p1 types.TipSetKey) (map[verifregtypes.ClaimId]verifregtypes.Claim, error) { + return *new(map[verifregtypes.ClaimId]verifregtypes.Claim), ErrNotSupported +} + func (s *FullNodeStruct) StateGetAllocation(p0 context.Context, p1 address.Address, p2 verifregtypes.AllocationId, p3 types.TipSetKey) (*verifregtypes.Allocation, error) { if s.Internal.StateGetAllocation == nil { return nil, ErrNotSupported diff --git a/api/v0api/v0mocks/mock_full.go b/api/v0api/v0mocks/mock_full.go index 151b1dbc1..df67d0876 100644 --- a/api/v0api/v0mocks/mock_full.go +++ b/api/v0api/v0mocks/mock_full.go @@ -26,7 +26,7 @@ import ( auth "github.com/filecoin-project/go-jsonrpc/auth" abi "github.com/filecoin-project/go-state-types/abi" big "github.com/filecoin-project/go-state-types/big" - miner "github.com/filecoin-project/go-state-types/builtin/v12/miner" + miner "github.com/filecoin-project/go-state-types/builtin/v13/miner" paych "github.com/filecoin-project/go-state-types/builtin/v8/paych" miner0 "github.com/filecoin-project/go-state-types/builtin/v9/miner" verifreg "github.com/filecoin-project/go-state-types/builtin/v9/verifreg" @@ -2339,6 +2339,36 @@ func (mr *MockFullNodeMockRecorder) StateGetActor(arg0, arg1, arg2 interface{}) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StateGetActor", reflect.TypeOf((*MockFullNode)(nil).StateGetActor), arg0, arg1, arg2) } +// StateGetAllAllocations mocks base method. +func (m *MockFullNode) StateGetAllAllocations(arg0 context.Context, arg1 types.TipSetKey) (map[verifreg.AllocationId]verifreg.Allocation, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "StateGetAllAllocations", arg0, arg1) + ret0, _ := ret[0].(map[verifreg.AllocationId]verifreg.Allocation) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// StateGetAllAllocations indicates an expected call of StateGetAllAllocations. +func (mr *MockFullNodeMockRecorder) StateGetAllAllocations(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StateGetAllAllocations", reflect.TypeOf((*MockFullNode)(nil).StateGetAllAllocations), arg0, arg1) +} + +// StateGetAllClaims mocks base method. +func (m *MockFullNode) StateGetAllClaims(arg0 context.Context, arg1 types.TipSetKey) (map[verifreg.ClaimId]verifreg.Claim, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "StateGetAllClaims", arg0, arg1) + ret0, _ := ret[0].(map[verifreg.ClaimId]verifreg.Claim) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// StateGetAllClaims indicates an expected call of StateGetAllClaims. +func (mr *MockFullNodeMockRecorder) StateGetAllClaims(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StateGetAllClaims", reflect.TypeOf((*MockFullNode)(nil).StateGetAllClaims), arg0, arg1) +} + // StateGetAllocation mocks base method. func (m *MockFullNode) StateGetAllocation(arg0 context.Context, arg1 address.Address, arg2 verifreg.AllocationId, arg3 types.TipSetKey) (*verifreg.Allocation, error) { m.ctrl.T.Helper() diff --git a/blockstore/cbor_gen.go b/blockstore/cbor_gen.go index 221f13676..c53e9e850 100644 --- a/blockstore/cbor_gen.go +++ b/blockstore/cbor_gen.go @@ -44,7 +44,7 @@ func (t *NetRpcReq) MarshalCBOR(w io.Writer) error { } // t.Cid ([]cid.Cid) (slice) - if len(t.Cid) > cbg.MaxLength { + if len(t.Cid) > 8192 { return xerrors.Errorf("Slice value in field t.Cid was too long") } @@ -60,7 +60,7 @@ func (t *NetRpcReq) MarshalCBOR(w io.Writer) error { } // t.Data ([][]uint8) (slice) - if len(t.Data) > cbg.MaxLength { + if len(t.Data) > 8192 { return xerrors.Errorf("Slice value in field t.Data was too long") } @@ -68,7 +68,7 @@ func (t *NetRpcReq) MarshalCBOR(w io.Writer) error { return err } for _, v := range t.Data { - if len(v) > cbg.ByteArrayMaxLen { + if len(v) > 2097152 { return xerrors.Errorf("Byte array in field v was too long") } @@ -76,9 +76,10 @@ func (t *NetRpcReq) MarshalCBOR(w io.Writer) error { return err } - if _, err := cw.Write(v[:]); err != nil { + if _, err := cw.Write(v); err != nil { return err } + } return nil } @@ -140,7 +141,7 @@ func (t *NetRpcReq) UnmarshalCBOR(r io.Reader) (err error) { return err } - if extra > cbg.MaxLength { + if extra > 8192 { return fmt.Errorf("t.Cid: array too large (%d)", extra) } @@ -171,9 +172,9 @@ func (t *NetRpcReq) UnmarshalCBOR(r io.Reader) (err error) { t.Cid[i] = c } + } } - // t.Data ([][]uint8) (slice) maj, extra, err = cr.ReadHeader() @@ -181,7 +182,7 @@ func (t *NetRpcReq) UnmarshalCBOR(r io.Reader) (err error) { return err } - if extra > cbg.MaxLength { + if extra > 8192 { return fmt.Errorf("t.Data: array too large (%d)", extra) } @@ -207,7 +208,7 @@ func (t *NetRpcReq) UnmarshalCBOR(r io.Reader) (err error) { return err } - if extra > cbg.ByteArrayMaxLen { + if extra > 2097152 { return fmt.Errorf("t.Data[i]: byte array too large (%d)", extra) } if maj != cbg.MajByteString { @@ -218,12 +219,12 @@ func (t *NetRpcReq) UnmarshalCBOR(r io.Reader) (err error) { t.Data[i] = make([]uint8, extra) } - if _, err := io.ReadFull(cr, t.Data[i][:]); err != nil { + if _, err := io.ReadFull(cr, t.Data[i]); err != nil { return err } + } } - return nil } @@ -253,7 +254,7 @@ func (t *NetRpcResp) MarshalCBOR(w io.Writer) error { } // t.Data ([]uint8) (slice) - if len(t.Data) > cbg.ByteArrayMaxLen { + if len(t.Data) > 2097152 { return xerrors.Errorf("Byte array in field t.Data was too long") } @@ -261,9 +262,10 @@ func (t *NetRpcResp) MarshalCBOR(w io.Writer) error { return err } - if _, err := cw.Write(t.Data[:]); err != nil { + if _, err := cw.Write(t.Data); err != nil { return err } + return nil } @@ -324,7 +326,7 @@ func (t *NetRpcResp) UnmarshalCBOR(r io.Reader) (err error) { return err } - if extra > cbg.ByteArrayMaxLen { + if extra > 2097152 { return fmt.Errorf("t.Data: byte array too large (%d)", extra) } if maj != cbg.MajByteString { @@ -335,9 +337,10 @@ func (t *NetRpcResp) UnmarshalCBOR(r io.Reader) (err error) { t.Data = make([]uint8, extra) } - if _, err := io.ReadFull(cr, t.Data[:]); err != nil { + if _, err := io.ReadFull(cr, t.Data); err != nil { return err } + return nil } @@ -361,7 +364,7 @@ func (t *NetRpcErr) MarshalCBOR(w io.Writer) error { } // t.Msg (string) (string) - if len(t.Msg) > cbg.MaxLength { + if len(t.Msg) > 8192 { return xerrors.Errorf("Value in field t.Msg was too long") } @@ -426,7 +429,7 @@ func (t *NetRpcErr) UnmarshalCBOR(r io.Reader) (err error) { // t.Msg (string) (string) { - sval, err := cbg.ReadString(cr) + sval, err := cbg.ReadStringWithMax(cr, 8192) if err != nil { return err } diff --git a/build/actors/v13.tar.zst b/build/actors/v13.tar.zst new file mode 100644 index 0000000000000000000000000000000000000000..77565abc97bbc3ad6a3c69e801b2f147a2c777cc GIT binary patch literal 1646614 zcmV)EK)}B!wJ-eyXuK{2x@c>)Q4laJD;@oJdo)HpI&X14#^{ZcBhunNBJ?FZYyC@i zp`)!UV-57!N1kk*VEJ_mZr~2D-P?1TelIN4Zr8D2?USlVnq%!IWN%5flv2u+k__Gp z1`D(caB#4?JBZ*nBJ!mRDk9oJ0v&{{gm;W6kRRjqmoY|WG?=&MnDu^!wzr^_*T&7g ze&;xT?mtC)JGXatw|}=YGqXQ=F{Hq{L4e*1&F}n8aO0TUV7)m}4i?*F4&A3THwi3k zTdp^!U#DPEVBi6yUsrR1Z9fGep$*n%EIPnZ`Osm{Ix=7$m~Rg zf+cDM`hwN*`)~+ffM4y6A?GtE9CqzozZ zS%rjxg6fVPRELh4BZmY6ia2oi&rh8`K6QHa5#oMGf71F;R=+C0IAPSZSZky-&KaeQ zF-G^;9hs*mLO=J*pVEjC3~pnfRErcjLWvL&GKUPYKnf8;6oUlG;YLOm-1ZBk8^C{h zK;)OkzYbi`+LZyZ%Cf?`L$03d1Ar}k;XO5hDO>uUid-yButN$ zd50(H_(Hx}KJ8rAH}=_ny37vG?ec@eiG%()>v-0c(aB;!9o3b82WJ9tItaY7pqMic z-8cIWR(@};{rm`f%~z$d)=kDm3ieI?Jg3*xU3v0r&v{MJv#_s=^(x2Ims;1RHfHjx zh5(nmUPfR0=mol$*QBMtep{L;h5gTSFfLXrS1T8*u%9pr^DK@Z?kTO6N-F8LHT8N@ zo*yew5Ky;O`t++@t&~#ARm)jRIgWZ9SD#i|X|$Z3oAVXdwsB83`ZnOX-mHxd0(h>_ z4O>_HSI_>Q?8Pec*R4&*MfKBo<&Ci^_wLC!zD+CqsJ%jH)}EjH_=&DxHf+*Dx6rTM z|Ki@~EoajJy~W|>B}0w}`qu_IzFGG`Ulr^m^Jn-aAjX$BZ|vLBlmh*~f7_#cX`CbW%QZ8f#PYo-~fR$vk;F-}d5d4$5nbFZs$w(!N&u z)uT^6Chrfb@Af%2h4<{)^TyB=_bz>#zSk$~$Q%?1NBf?fARee6cc^W$FukXqt$9!L zp-k|cd^mA%85Uv9bK*UhKajfgtMsb}ArVg|HL2(I#&0d;nCM4uBmZPkw}+ra=A2%r zqMCwJ`svriKR>6aD|6|krQF7%C)nuX+<0M?#iy~_8o$PGs;|ZR$zERM8)d%MNb{4? zm(73K>5b{=&u{uo^Q#(M;sp2H`SmUJUUzyNOJl`>219@pOsR6*5xQ+crnMHx*NZ4> zS`M!jM!mk2TG8ss=2ZaCmgIy>o_KcWmqt>U1ecb?q!$8=;>J$4mpbXPpgz8&b? z_oU8)w0}&C33T;aa&@q}FiN}~EFz}sh8$rL8PvMm&g@1Bx>y^@-!}Key1}$dmYk9^^@YOZpQ4kvIv61fda~ z3!!|B1>s8%afexa{ zG!H$2#z33tANox9&{%8+-*F5u3HS{Df<3@<_yfGfTd)O21q_d1xZRCd+}Zs>%nJy5HS`2U@UinN5@R?8p{`QlhN9*aH(>F2`p5( zg2eK24e_wf(XL*FHm<2{9KQJm zx!X0m)vhq8*4mto4&t^!aEq}m*L4S1+nIIbh%wd-fq-yytYd9$g@S+u+wrm%t*PoaequAm?rUSR0Q34cX;`p$JOF$DvRzIv@fx052QUe1`@_2xR5M&u)Mh`Y2m`O2$%L!dWpwYyZ zH4+CHApogjORP!>p00p0b2n9Ad|^QtT`N?PLFIxcgap}AaO5ZpEhaal0HlpBSFBL& zXab8*8XwS^!odM}sKCn*Gddj6BL&$DO`C%;Yt<!T_iV84PVPOkZ6&S2hrUO%Dq|lr33Q_^9B4ih~_!EY+~5$%g@FTsmuErDOBO2h3W|gNNA~=Vi8#JL=_~Ap|r#iT1F(KAO#aXZh({qz(zO&1YT@u zVFfEzs-!?=Mi-D3DGFf54lmo9o%C@K=f_ zu3S)J28Ao$fKdTJiw?A|z*yQ7b7EEi4GeaO2T;ET=}MOr%2=|3%$hMVDw^IH z1B*uqGbu)#As`5WdNlJp`$RA~(0TK<8AkiR$BuGh*1PRj7(Gh&AVg&=JD_daD z;((_sP*5$LP=w3V3Q$(66xosmN=ud&P6+m8VQJcfOJ0~40H^DSZjD59D->k}V6*5% zan((zxkNM1;yZStW4W97d7GETcdR#0#AjE-UVDBf`kDA`O(Updbgrg53-*9XO8L?H zH;+0UU$*li=q6`b`50yb-3PdYr^4vEtL^x#@xbXE&#*EA`4(l2?=eUaBcls000QL4 zkD?+AEbwoBaeC8>bAI%KbRBF`)x?;#aVm-K=*Cz!fxu{Qj?Q&TK|@f8xM-TpxepWq zqb96C&>^|~?v)0vLL-)CY((c!i~GdRyhF77U5pMBW%_5;rIaxiV;IAj<9OTHT7JD) z)3|bF%9J^FkMR~PB1lbp!D1Iu2T&YwRiN$xRFD?vC%O^^^NA_`*Uy-?86(b2W- zn@^`*h84(x?h##;FerzZQqxXQ=@5$m6mF>D(sTj9vBjGhJ%eGu1Rj0N$S!McSU_8r za&;;U4K0>|JufJ7cxjbR8NC@=*btMIMfp{mI3D<*64MP$5m8`^$cU@UR&3Xdv4VH3!mPQ{($KxW zuv-J{velRFWo%WiIs?~jExVhUv4IdW!9ho6NPDBy;xpdOZ^1RZv6 zD;dOdH|8n>(i%iRpbm#Fja6N$n&FqCLnIu~h{6rzw*w|&CLJ5;|LG&PH*Xr+`;EO= zT}#B-x~`r(tGZU)C(2f!ksFW+6*jU5E;*qBdHzzlp{re8U0sajJjC9*Ekkb@%-i!B z$H-PTMsFT5>oQ{N)~FSMZj?WJgUS^kYE5i%|Muid`bJN_=wo{qeSR{331i*r3|(yR z8d)l~#9_;-!6S{BY5BNgE|!@ke`1jjrkgM4%gAju!gfjP2T|%W@1TTCB+t!@UX7Es z?JDbnv{~ttQ=|y9vh2cMA-b;54ReRUx<*4-S#F0MVeN72X*ZGWz$F){#q@XE(Dfq? z-92?KN0G*A(?HJU_#T=6OSie9#>`HgJ3M16BD%zIm^xRt%^EvEXD-kB;hZTr2G3%1 zRNc(#fpsqQG}VgnuoSnCp;Cj0tSbi%9jq|%p_LvNWnS()2;{CS&@nGXk7Lt$X{qi; zf=yKsa5#wBtue@7Y)InHy#R@-+Z+=IyRKn>8$$sf2s#O{j-3RVbny#e&7S;>l$6|$ zlt3mypphZ|XYc@l=SB;)q|MvX>|MvX>|MvYFd zHq7(tfA!&`_UKV7sGt%N)uExvgGUt-3JUTW=MEh@VvZaU2q@yf5r2N7k5BBn7>h+1 z%s=L^NF<6;5Rniwp&_}&A*TMO`cRA;KYsoGdNr8kq;kMP-C%V5+l$Iw#J&bVoVT;DNY<%Yu5$+yE}X-PMRY?!}fu>@UH^p7|;Y{Opl5@BPMAN za?O@qFn|FLg1Yqv?QK+@e7=x&ut}970&FrLZtxT2^8mo{vK&o--ecd{-?V=*Jc2_5 z8C2uPgPcSLPf*eDVWW#p!MGZZtPV0Cr&!=Rg;qoUytjIM*+|q2{7D z#2*Qrq(7f^k-fN-=0!?zo zViv@t>^h<@Mor71`Rifxo!|R%tg1f)UHSs|EU}T1jE?P(cHs}ukIxpp`r|-2xQ+V! zhU07U`F=JZ*xdZbCkB(;!+gFb?%QCo=eh{{WU=jQigBG5bB}Sb8S7q~ePf@jgMCr7 zpIR4#aT}Es-j{1%Q~ai+v3k|3$HsYnP4bHZZ~WM8(_cI6nP{?}t4m5@rC%wfv}#nV zRI600RI600RI600RI3`T(_PA!b#*y&V3cE^Bp3smnQ|lpbUBC_TEz_UY}j`cHrVI;Esi>X4Pb(M>9S_~_B2f(nR;hK6|X z5E2TCK{D=$J7lC$N8I5fJ`OC_^pMB7Vw^WuQ{uj^nhgpzX+m_R0OIOK(S8J(FlUD3 zxkfeyIUc&o-5F^)fpP8!^(tlkAO(slqYS_Sl5NfHCc60{Owi49ZEP0BOwNTDh&ea< z7Qf!^x)*eVm?v!4Ty^a9of90bDN`nlo;zjUOqkRnS8Wrszp#*F7sh!*wFVhFmr0A& zB`AZXu|U^Pm$vBm#;)@fGNQh*G8)!t?pc>7$suNVJn2=19Z%{gra&-<3kwS`Ou=3O zoU0wicoNW=D%2!_<%0|6>OzaRs;p*IJZXoUHf_U^k|*iNaw5f(jzsXJCM0+g%<7jg znyKlhnk~WZpl$?R)|Y1s1s&|1BNbaiLcK%RvhKSrCcK9@W8B;cbV_5G2+Os?*T4qR zARRzIm8fC239%LE!dx)h7c^b;q0Gjd!Wd(80Y(>H)ByuR<|43rpz{e$Ai_3NSz~0N zH)<(ZXp(t%!QoXNqf3O(vgNvj%~rG`%VbPW)NL+!k|Lf5FC`LN{G+?ram zDjM#TZpS0_VwpH5Ud1pSt(`2oIoejm%Bosha%vnbv4?J~&s^4&l z_h!ADpPr*|uQ^|@m)%~kDAW4J{_zImqHr8j&~;)bX977e-J|s7ua~8izTQ3CQ}{Mb z`Y-8uGMUs1%S{_NU!LYk?#T;&<)O|g|Cnl4+Dxmm6^+YH>ee}x&Qh(FORIx3%sS%f zI%k%m9RkEI4nVFxNUCEgiJ|4qaR6cyrF&lHT+y zPjc%Xg+tHGI@;t?L**E&d&Om>7_agfCj#W)N3&*ktYnz0Mmlmij+{!Ts}pqRlk&hI zMtZJn9=TrC+!X(OrfJ&bY9v`Fjwi)hyUxBg|Cbrv%yO#Rh)?L$4H6Fw{dIpJ3M$cR|I+?T@VX5mJxALSkI`^2bg1m=8AiyRM6%*YTJeSw# zCI_x|Mdgy#LyyX*gHfh7uvt5oIsUSU0)xe&0o)yLa+m@A0)ULfR*)eADKc_sAOj2* zM06>!1UY-RP(lK6?69#$6iaZc6S=l(71wt0M2tmQAt2=z%$xKU@D}jO`ZE8FBc_Zp zQ+CoIV+1}%9Wm;N!NrvZ7#;!;(D4s^;ek16cwml8fCE%NOr=4l3of?U(t=DDT4aF* z4`r~p!U`=7R6zxtDW*~YX9^v%exL!03}B$&JmJI>PBhU3TcDZXHcK=s!AW2tfrZ2s zuL(0Nb3_|Ps7X^q86t`xLWmxI5TcDB&X@t>hcaY*XoHLoIq(3)3_Gkb0WmCrH8=vS zLk|3$L$AR&cyk!Ofo*WEfvtd8gK6Ly90&elgPApc;HfuwCgnwjAEIQ4f`JSWFud`C z3td`(;_~G5#*@N>=Oehbm=AnK0TZq^q*mL&l|LOO6rW*Td z48}2a|FjU>&&LM#G1=Gmd6%X6dFFh6`}w^1d~D!%@5DQg4fk+p3SZb)uZe?sTevR$ znQt{rz4={Sm7tTdvsC-$$FKDpq7!mX zI)|>M7niclR%%%I+|Wtq5rV@dCyO>~Yz<(F2TW3;d=Lf7lMXxpOTKgoz`+Ip=q3Va z!eogOrKCkhT{jePxr}SY>TQmSr-9Z2x(xP7dBh^*~nsWgWLLxRNssH zwYIS>u1Qt+$>5h5e;2E6j5A0C&xLWp?NQDNlS##?j~}OcJYmn=+0EhtYl}QE_BZ74 zBQp+EGdI@QBi~3tcN^YpNJ^<19i`uv`4n$6kI}ABcXi*=A9@ba zz1yR6Q-Q{|%{k|c&U4$gjZP`$CAthPrCzbS0i~(3UsW|jjrzV9eCdc3dwl{Ayt4MT z!35R58rm0Zv#+S_H8;C$FBjat=9Ke;ECte9`30%S{R9fSrdsE$b#{?4FPjXS_OGZJ zgZFhd^W~f_J5G;FwR@?4Z1_$&qx9=NQ9w^Xslz}&Ez;Vr+MIJv?bX(cTw-Ho{`L*+ ztKI8PI%k#9JBn^SmZV-^)%Lx-0Lc)Gdc|O={n8|%uDvA7NWXxeEJ`}1q!$@Hs(JH) z(&uZv0^{PwF_M*wKMa8t|t2O1PlSyi_EP=LxJ9)aM z`BA3LXAXq;Q^wGr78z@0uAK(<9ATdqmqEq5#r0fiYkm6Kc4rJ`IWOCME4970C@qG1 zWw0-5=k^u``SKp{^m?wJpt@_H`=ILO?S*Jt&|WUw=~t_o=ijASdRx4(l-4wfE;x@S zw$)~KE2VQv8KZkkz8_zHP|p2!R+efnE<=oxG0X>ap%;cL={KL{M|kBEDQAzfeQDac z3>`T*$N>a|LkW(bA0G{Tx}z>8lLG`TRRv#SIoJ8 zh1T`5EdBajzbKw9(GS8q#dV7W>5sD@2;NcpW%B0-;rEo^5L0|^N^f~-DcQYdYbHXS z1ue<1nxR{oUo}HzE|lOcxC^+10C1^3Z$T~XUsv`_tJ$#p;Sz*=Wj&s~JjP-T_%)BK z$84y_Yo^B)kRBrekMKv2)pQbmAJbl>yA~UDb~T^sNnPhjG1z+&_rBTpmlbogEchR< zKGg9)-29jaCT6|w<-Oi#=)w4O9+>WZJj??F>#=!YhKh!G@DKt-=zj zLyUUxQ2MWeZ^(m*ZxmJZLw>{_U&KX59}gZypN}FQNIZ&M@c%|WgA9Ir-y8A%hzJM} zq8>ys7Kt%P`G~xc_dFk;6d)Nm0rLM*L`sx= zyphp=QASAKN00aMUXS-a3WE>wQHdXxq%R67$S3)ZJd*etd{G%}FZIL{W^BzV;)pDs zSc+0)sJ_ra3Jxc@@^#yH&*KX^GQ#6A_@au~Z_>n@5+ISM z3p^B9;^89-HJE_niy2a2#u6AJ&*P(rd{INbw;%f-pV3DZeJ=?4SP1gfz)N6$ywMj` z^cQ?J94&$Q9>^ET^Q$r+(y^etsUxPmbr|1-+2BBdG51WMJb|KQ4UfbCFuo;lvP@9BJQ>gxmqtQpF5~BP3MnLU^YarE@HKAQ2-D&S5MhP`OjbzyFH* z@tCngQk;A+JK!>+U$-iM&JKun(UVT>Q$~-djo=s2Igvtz924+l*TVKw@doBi+sB@- zs5oS4sfkt440(0sX6IWtYvHohB>yD1RNhf^=ofNG;04gGK_r7>^??XN*%&VcFQKH2qLS7oIp<6HmBx?J-J{1Cqq}dBV>ihnEM#V9 z3jO61RO}*Q^qhijQ@^C}ty7cLXc_W9k@@}ZH&|>Si#V{3nVH!e%8?uB!m^^D>$zu& z7=fdppr1Jr_9_sTC`1Qwghd7(*_8!<2+FW#iOrX^M2uE(X@uBP8V}MYsRA}e*mWc| z=X_cG#2CSd7g6kqu}KSL0-+HZ7Z+*^{nE(bMSe4x<_dsWTrI)B;Ch!~8A1q!n4z<* z6S_WKWiH$rLU$M=gbtx6LFe4IQa(dLdHt8w(%G|YXkCulZ_k}Q_O@}{_iBef<$*{a_`wCtL583Z=&-1t0n<4gCPV4ce>??_L2adYrbanE+| zagWkv=gKZDb)$RUZ76%+;JO@pX6tbd(QRFgtM__oL)V9+if+%#+_V{R5finx;Lo=68Ezn>i)C1lPtB zz&tRI3%x1CB)UjPGCOl@=4t`V{?ToZ>e^SeFY8;~Uf$Ez{adyDuiEulejkkMRVTY! zMsC?JrPSb(C(cP?u~;k?i^cX>Ypb=^S~MVp(Bro0lyX!I_anB8yiOmlQ^&d2JTCC^ zatuANw40mxA%qYT@2*p_r*>=xQ z7Aa*~Yj;=nEA#f}c$+kyY;bpekC0KOXRyp)7<=4V>t@f5bA8v|r&g!WclVe7q=SgE z=W`rlzN9mH)x+5S$UxW_^JnIM5G=ynQJ|D+YJcNkFb|5925Z{v$E(>|YkX^cL0I9} zYCQ|z+6`K-LT}sN{%h;XFLo z>{Hkd7U9mQ63Hs!x^+3KN*x*acBXtdB64MrlyV4oWJ)3)t}MuR2&yk5PqIfyl;9HU zbuX{V`m$?>{OMw-zSgO8;}HEjL_hju>T6X_>0nsucEnsL?9z)b=@c8CWG=Mf1{djR zsrOcIN}cbM&@;k=4aOI$7#P8LgV^+@ypt0X6DzdYyNW0HUD;}SVYPp{+xzfDasSh5p#?k zLX7gYgnFRhx!QQj=`KnjW*RioD+x-XIAZgz5I^f^*y7MQ417^TMd z_|jhSRpU#0MeS?+*pn~p_)qccDpL(u;0qJ|AKksvdZO0B0$tZ8Miuu57H(3`gX>Nt zpCmAz1@1+2qv{Omn! z--j32C)P&s=!KoyzAU{%P<-4jLO<3C{>wf!@s;X^==`9Q1%f&d!q^)a**&1@vU_sB zx^Z3kos?7h&JSMMWPq%i3v0dYNi_?ggBuKEy%=+$!)@~<`TThYX`XdX6hHU2$Zd3> z%L}VJ&s3G|donVstVebT)B~ z?-da#6Qx`gJ*Rw*?%z2xbF4qDj=&o68lnR;A|>F!oW6N3zPS=Ifi@5dBjbYPSbw&Pd!~rx{_7u+-88rpyEP+Jl31|c% z%i+2*A9CoxB}O!KVsf|~2-6V=sx=&$eXubeZCL7paMg6)#ND1p2c!F#pP7l7z4$pr zx%^=CA*SffyvDbN!tf_sHJ00aOPQwQnwB!jnq=F{?7uWwrN7yiRy`WS=(_NYv3gJG zVJVJ%W@D5ysP<LO4shb%D*2bU>>Yl`}3%cTB4|PEIKcd(lzdz51 ze5s}_~etXzkr0#2q#V(B7toHO@^c!MoqE9_pmfNI#-?g^s zxV6pho=naUboIaRKnEoj2b+UJr-MFq2FX7)NMA%#+27WF2yeY@1cFXhJ0RH4564W) z(M~VUmov^O<4of)zQ+$?!Z?TI=j8tWxn<9tYJ7_R485VDC{FQqso+uy-rqRJplLCBzpgw(qgHnHXh2U`4Jps3eP=>|%yS0000$009(3 zg#aWPj|W7OBIhye0pw&2jF!bh;VjQ_AO$g!G0Grghyefq0006rz%T#|UjGW#3I5oF z`=e?4vHP<1<34yn;18jA;RXz|#>(WE2o;2{y7CIwmj{CyqWYmU3CVE)AxHo;+fP~! ztOe29bHxeqrN_VR%MDulGN3z(!EvmvUCC`P8AJtjf1{yl7$BJFuZDf4KZ3e1rDFK| z=MBv*jRt!}EpAYhcCM=**ToFWwm^P|aW&Fb>Z%QVw@U+aZN{7HMGYHvOwb_6c zz88EWCRk<oAo!v!BDEp-Nj zK^VxscN^&5Q+4Zb*$j3i#KK8PvM*~*h;0^#4=>KqZAT8wuHOPm4EO-)Gn>1}Jk|6= zBz$`>!X(dMYGIVY&|0oV!l%Ao_?GM)Qj??k4Fp=hK^A|OL!K0sO$bjZm8UUU94}3O zv|g*=`s*_Y9T)SEBlfGFIDS9oCv>rU<*6~`ed;FA$Po_{j*P-QBN-9;Z65Wrcr3L~ zyshD2_hqlPJ7wyoZRSv#^ub8$AP9u@wp2zWK#g_?tqmi4+=PzETI4J2F6 zdoI>AeQX$9cH>z(rWX8w_Tk5NZ(aS0BK`wuxggVVfIhy@mPFI-kKg18QrMge&;QzumzA^7U6j_sAUPu1z#lgvi19AVj}p{7j6; z_-P{O*I2!66&m01D@jfAUb&nfJUbGQ#n3+>Cz7n9^#t(9(egj21>4UEDb?b6KQ&jt zc^#4@seyf^U2km`FMpa7Dd~f1s5N)hn&f1t*ldQr(ewPFa*;-Y=45;U#~708v4n9Q zydIoUgcyS;>KV`pK_*~nx5KAFOi)kukHvZ+cc4;54-NFB-BG1p@y?iD<`Hz--nxrm z{@%9&%ZUZm4KplC(4jAMQ#R{Y9>6g+B*ZrO)_rjKUuqpc!{gJQ5zAhW^762O40#n~ zW(8eE@|Hj}$UP{f*&>r#c$63c$Qhc;J_Yv;Dz0An!?fN8hBJR5&+F?Ig>QWNC{E;X zQ2><%o@|vx?@Icedr@~0?@SrNc1DCZ@L&XS(_X^uF&);z>aA25waO1+%d@}9EJTsj zG1`CDyhrJ+Erh-j?L~vROTq8`2GOUuRhT~mpm9apW9!OFAQH*<9@5NxzzHHNuYgXX zy=6q$*@l_d7B-0L1(n@!mT=wL+Gc}4!GO*C9E8Vn9CDRZ^BqmECV;hnd%?g6R6;VA z>;JHo0brQbelqdrggM$L(7#woggG{KOI!-1X_BbW2)JM+FIYAorCo!p;wERgPk+gq z4{+d`R!BcYZ9Z0PFw43zxH4jIEznKJD4i?Atj?!d5z^5F2Ee6+C{l(SYk-yFrUXe_ zK7r5jwCJ_(e~Kp)MWu7Zx#4&>If-c^$)na$w5|35P0)ZuEHtbjX9sCqdY3-F}kE?~=z>yGpVSU`Fu6EO5l?a%PW04&8uC8q=H+Ao8^ z_~m?r<=%Ilc@TLK21&S^5;?TXg>B-3nrP$w%0dcS zS#!&8NQg|^kCb&h%oS%?2$$c)@N;C*C1l=`aZ|vd<8YcJicp!cErp@Dic+RBZt8dt zBEXx8z+(k>{NMhxJgj+4=OqrI+z0wjA#YYYe^)^k za$h&)FNb2S=|W3M^C3@XG^)fRVgRdCG(c!M^lQ>(chR1Fv~f|tp~ ze2gO5J3thnjB_A=fR&6sb4=vNR)PA%H9_z1{pn@T*Ww#5Cxu$ic-s@Lh z;s4wZkg-=(O8KVz9{7#c`TH%QD1A$|&VArQ!{Qa%;}Tb?uvgceFo41tEnJ1_{z3Ir zHMk%w-fnbO@%>*Bj(90_&xTPLnW63!D_WV?i3QDV6wz*KX3lh33G;X;0x4^O*5T>ja^|IxZqN9cUussXMg%~V$WgV7 z{3{TIu{#^)7leGRV!4bVE|xRojr_wYx4pkh6VJpe>REXMa(?K{k;Gs^oXM~^&aAdT zDnB$b{YOEX8Dy@9rE+5lax}&H;oO)Tu9AmqByl8V3eoa|1`qCnJ3xf*0NoE)Z8h2i z^4&)c*IKYH_c)(Eny+l#rMDoFlNTtnBz8xoJUw#)Jbml1wIg_zK21Jj$v_C!}BP}=s0i2ewTXAN8;C!(?TCU;;?tqCS- zrb~V`TbBl6D~kY8K`XV?&vpYsu~K-Kg}HyX0%f@_#=gQ%VR$L6HWHyvPOUcB?!Eoh zC^HDH4H$(TW6k1KCaN%RhEzgz-QG|%Un&{IhaXC!izj_qun><3`{vwH%hU>0Cc6y= zsC;^kT}}GLjMH`Ub@#QyPQub5yH^-+Z8%5U&2fshSguF~74!D&*kifw{P^9_f3p%) zg4$)1=$oJ{t`mPP@LQzzs)5U8<|#d(AOWg3chlEFecFEGq{wgpk6z(*nOgLMXR$mR zO!^b9$=bm_v1plcDo)Ds19ve@wJ2 z1}JmdJ)=pEaiq`m_t-?Fpqtb3aE84bP zm&l^)IO|@_O&PFPt~ScZrr>3?4%i3pL=2L=7eP2Zu4^k& zKhnpj#s%8@pMJ91(o_AUGpcA~K&8s6?v8eI-@@fO2T#qf!x`VH&LNPm#!L~=(lh?Y z+%r&TGUJ&ur;v={IB#a~JR(BLvcf~45uqw-Fr*ak02}=E2{5VorKcO*hezbzD&c51 zGjVL0%o$kN!1|#@BA^b-h4e1Wkt|7GV)kD)NSr_0=1hxbe*?sJ&b^(>! z@)&;sK{#SdLyxwDDOmGb;$A23COQ{UD^B9(8))~nwNm(HMvC$*-357V#IVEcFPw!Z zk51w)m22J38JPttX-TmEXc~?^yPhlTL*5;%BUHk^N;2%<+!{+^)dQ804l;dS>9)iK;XEn&tx>sF!ZMT+y{5f~z0#}xGr=srdy zrS{P9y>*5bLi6Bbq{%QqU=Mg04hYH3_s;v_@hmwjo2%V>Yfl zl*hktcnZM{lf08)l5Ur|rL*QsWT<}o0T*`=KJXjwY)u@*dc2Pqps(uH!jdZ3%v4 zAq`rIJWBtF6HHw#z;h5ZmqVpn)FTpfqFy}_v(wm>ujaec|-{Sg`nG=R0mgAbU zzRZ^l3k;-&(<3NN9rKf$@pCY`RQ;W~d=aN#^Aj)S6wo(sv999wrZyv{Eep~c;L7S6JM5RemzWj1*}x$<|9 zX3bcWDFXL3XCy6>1x)6xQ4D!eL@qp$&-kwxjGoG^PM^U>+x=*%1r6E>+_aBmgqemg zo4?hwkTk98o1aG4k)_X4K(~}Fm{8okY%$$MU}~8E{JzHNXf!^3xlW!TvP*7Xy<9w2 z%k{W6`Uquhud|eQ*-*V7%6If7x~(|A9+rh8o_xX-3-k{pA`^K4?L{-(=Z{-Bpwxf* z-zz3Hm{39a`LA&$w4DDNpmEGJ$&<_c+K#sz&$1-<0bXpe7wru#pqefLdPjZPSC@$* zAN2-4aY^%vmR&ryKZ@aeds@=GU?L)>qZ1ikmV;YgL^|qDZbT9TpZQ|i=}Zj@-&+iP z34br#RN$k!#O|{TpaXY~dWcip)C~+9Aiqk6Kqk#X{))#sg#}RSDxbI$r*Xea2c=OXsaEoKN5pIhy1pA zBy11^s5)y_g}Og#R|b<`@I0HQO#t&qjWW^U8Vw`##9($#Xja03>Ffvu4uAGN35+_S z4P)!y9vx0c5Wc;)q8vRVC-Lf_X{J-*;X_N*F_hBhLlh1Mk!4+C15hE@)9GeIkC@hJ)`~8q7Un8AT6WQo4ZT^7&O!yq>)_w%(|US&HsUX+ekGF-?%_J6|z)+cFQ7^EjkMrNkod* z1$i6G6;DY6OJHk(!xL_s&lmF#YlTCT7n2|QD2T8? zbjkEu*Xv`_#~Ymxo(?b`IShT6M%?@IxNWQcr8UlZjc=CEEOa*tQfX7dP*g&%gpx_1;W2UEw zlqd}}iunHiU&o?2qf|-WHd5*Ys;fB_?~?STB0CEa?%Soci7T|J>JrJv^Z+$>P|hx+ zA+|AKOd6ss(Kc2JpOG?d6*#$QS`uR7RqT(znDmjS97#ElYexWXAq6~By12$DD>X9idZe4; zsnmx~PM7E~n-;@Nd>-|xb9O9D6+F}Od_X2yBxz!xe6D??T4#{(+eHFzh&DJJeF43h z?%_WU3~tW-;ly-+vUvhMaJ0o(={0hdHu_V&K;baBZ`P8}$F%$^q+n>wEBcaSK*&`1 zb#|eo6+Yq`IzN=VM=NflkT*EEKq|-1xJ5>CR6z5w#K^Y?60_x}Ml;zi3owz$v9QL3 zp?NSgK$PGVTH)lfA&Ooi6?BYfE+p(}8zhRN$kRTffijCwgdDD!0cjI~MaH!WX^eb+ zthM}E=?)%h$!R15K&jW^aoCqYzFYk^OJnH0N2`Qnh%uGU?YFB+2RAIONIo#go>lRoiHs^mR^U|%g1P# z1@=4xdN2dyo34i9cEuf6&suV~L#OVk;ZU*=-pf=%t`NFv2S5ce#&3|w2dB9{ z+ig z_cgG}U2#QZ=jO9U63lguH{Ly?!wouE3}U;Ie9OCMMy=qdUN#8NAH-AHs6qhJcqarV zvM~>oa!2vtNhimXLNDhN@(O+!pDiToJw`|%)1mM;LB6!xhmzNV zW#cB0aGD}HPo3di@emIg0|^^+N}9Ig8dMStevKvKffFb^FKa?TBOv)t@Xw?(fA&k^ zbIF~c?Nw|ZcbgMNu~!+MpEXxPw<9y>L+^5PW00C)D)sMSp+fvI#OGb~t>?gqHaZVH z`P?OEh7y!^I?G%pZdCP%NX9sJloadmF!#}i0B0P^=Cboyr&CMf5yH+Lx3di-$ zYO0*>;8W}6DNOuMXb+KY;GpmezO^CI-w*+0BS3N`&0@T?6)~>47skYuDcTrL#zOJ> ztoV(_Hc?o@r^pu1e)3iNGSYKO>%d z&?rs}Co#(lzfLTdrwutKK@-;54G90Tihc32C2vmLg^lY2oRg|54 zYVEh30rL=I;R>cX0;7rq)l!*%)?2T&*eVlnmXy6XXY-Z9HYpCPH38{0j)|E7(3Tu_ z)Mc`_soy5A)8MRKc^hD;`$>(Q(+oAGyQQ!kvaSE;LOkJiHyOWfJ%O3-f~7) z8$tX^$qT?J{ct&g**$aM&ai)D$zUSCT>^!$h{E$;dI_^}K&Op<&3rKE2W(un{a4b= z%6PFUzVh@t<_fNvyeC_M{{_ilC%ixYHNt{j*#jf7S7x!FsA%1UWQ2r)0U<#HJDr{; zl-%=uRKaog2lol*Qm>bbR#&#Vf~=JZ3?Ql_T#tq4q)87V355}9Ev>oTN9Q6(6cP3e zB&liliTRGwozKSyuRXCyWTW7?eqnsr8SaIFSXM%&j|c#dM5gp9%z-j$F+yBXQ&0XH ziTpZi#uLD24DVBv-p&vH#_GLvmR93Cz84P=FrQM?;C>LPd1Q5gcdNN!(~ITYZm*Ux zguz4vP_|><>x~gZ!QZ|c)&hk8i3v4CKrGxzN|B*63yRqOE}AupPofgwXvMgRXn>ExfA|dh^3vTyT;Lr5a#-v z6!~8w+<0gPK{RE8KjK~8T6$Av$h_qf$Zt9!Khg<#^iuy^(>|9lz;B?-g1M#od)Z7C ziI#T&tQQn-KZSaK**9xV&zdEPIL_?3RA6x@QogNA-Uadzyr*cwxzTvEt7S0Wj^xbZ z;D5Z@L6dmMP*2tnHEX^`Kd&HYQ`@g@i9w5ow3m)T7z2g5;I?|Hvpl=t^+~veTkgEVAfgar87cz8h!0z)xKA%icP|(2=0V3YGyWZKd9478oKaUFKv0O$*C_}wkBrPO#sBNbK@atRL8}U77hP{b z49%#3AFIPxd-aEL<;PVz_dpS&9rbCMXHg={wdauJA?kkm^dSPA_x817G{Z_$#j-e5 zTp$6a&G>&Eu6w|;OnTW)OKk%TfETHpYxVe4O8A$-B_6qr##);?OSKgdlfyMWUtO-J z)p0XoR<(&DhvJ?tE0k;+=NrS7an{UOs2Oqm=_QLSF95<6!snL!w<#t`M)uQyT)7smY+6+35ex!S)78WA zmV6HR3T85z>*&%crr&%s@fa{3xGBpBK~8dzw+bVwA)0ZNX`+O?o!r7+8FlxsEy(e9 zpPpqRKzSXPZi4B(mbI3JDd2$gXAx$B_mbAJ1X+sJn+Ry&%0#XQpq&rqI;*_vGP8_w zt*ktnQ^re<`4!AMmEsdNQ%t(~V)&>OKs;`Q_h>f)^3Yj2Gt}QK`t6AeOcEvD?8YAU ztl{td)epqrb>7k?2n;ymUqHVpoiA?!vQR%Z7IUmk33uWJb1(D!s07n)U{LR=z=+FR z>3M)z7PS?f^)+ZtK8htq_-cFnF;6ZwV~^~8$B(sSk5Y=R z%RKI6A;pipLo#*at+K--=p-KB41c-~zIvSQWD)4VVz8 zIc>0FU<5;DqvvGidyl5GM#1Rkk-n>HUE=H4(dcP_q@iBZZn%40a$VyRhjYf^oWf3F zhU20K2XqZay18QT85014;HGKJbCPg5lA!3s=yazWo&q?dx) zEKI$+bJ%ae%5UnD>=>3qv-O;4P2d@#G9W>A?r6k0dc+1Iz$2?8_aU+@?G4xOimaIa zNl9pDaD{QeWic^jlpD6iuS%9GcfrMawGjsIAR<`EGIJTx1yn$8Tal~lhSwoWBopNc zm>IxCFYj{CEtaxV?)2g!^-|mcV^?zP=hnQE2P0i*4})nzE1V*@tPTiA=jOl1v(O_S1zFwbARR7e8OJd_cL)<2S%c2ANx=9) zz4`q$zNF3%AG0T5mJN|RS1|mP+y{VOW6nYO61;|7C{WUdc6p25%Oa6o&T(m26`O8Wuu`>X?PXya>Hy4K+q(4m8B7*8gLq(rVM6(CY4Un_#KvKhQNd^%-)R9bUtCR ziNLLg&@=J=RAenbxn|B(gCKjXc(_c4LllwMaB@K@m1G+_vrwE3@%h~;gaa}`xo9vm z?#z%hU-QG!lz}kH-Pc7n`8hlG7|t|-38 zPJ*4lx$^WbMSF|C!_u&(knVg=Wh(Ia3MNZZAyo$_CNFCL|iHFk^ab(TEO<#`;uFEwJCxJg!Ot_~#2EexUhKuM)baslP z{C)0lLQ4e-`*C-34!Tw~KB10*G~dk(uDt|fGtq0gqCX|T_GZM>S4zTcMZ8IBt!Xpp z1T~3@=nZjaBy5y2>Y1pUTCj2=bfnC}v)AM23-+fSn-jR5NR&z_%dUPY5q6DV0wDxZ z$DbjD7GSfwGx0!xmyKjMgYz>p(!+6kfoE3Hp)b}8QkJW2QUb>vrg^--uwQ6kD;~Y? z(2HCf1^ddHX+G+cLlewYd9GdL3Y3sl7&E6 zjYAGc@^6as0;)+|KbS&}1iFxE$nl!MSqLx2Vg z*rK-*{XQJ$*JDJOKR5jaK@mIA??7HyIU9F^xOn87$CM*C4HVVxHHaYKjsOTQ5J}f9 z#Q!(LdabMb(s@9~`J3|dTY@`F5KLPTU-`kmh413d)VXyhHJL0k$M_k?-*ifTOAmba zs^D1YG690W86AGBU@sy=Zb6>gC~MMZ{tbSKP90H|ihaw-L9{VNQ<7SG&`TgjTCly5 zBB&zxf`I2Z?Cp^GvpOyqAPmrha^PzAD_r@2CAHup!6238<56wsqhT4-O4;*BZ#4Q` z5HP-eqD^M{3*Dd?ejME1tdS7R{`lO7x9Axo^mAr3=es}J-d?ie!x#u7HHuH`oLpgv zPe{u>@vd)0QI2h_o{#bOi-~y}xxS@dIx>pF!Pyz>zMM$Q{QC(bNXHu%sp$hj!7Gwv z2bjJGt{0s^s!2$>TeMF-pX2js38hAMZfG!>TFb0UUe%Gt%;kYgO#!2$TBe@UOMG8F zJ{x+dBdv|WlbOvlcE`r_KdTe?i}^Oc8k5JeND7lsJP$#vWt3RhCN06}9;LI)G%gfM zno6_4Pr>q-;+g!sFu21xIgjY*(Cqtd43dS6fxqN=!3VAB1&F>ra+43IT@l<~uMf`v zSg~K}07+eF|C>~MLn{^!Kr?Z()%l>l96gZPBp{3anSGq4t{sb=?ypz9)u!4We&pB%zm-NncR;CFKEDxu+J}i4hB-Zt&8n%1Uqip-5tl z1e;ZUL>{Q3qtfg(0vG?B3;-Ezc|{)+wX>#AGwqP%`naPdBLU5g!iupB?9s~BMSvdp&3j|FaC(pB%#n7b)G-_#sWHg4OD1l4BOxVA}FK^i697g ztFX(D>m_QTc9KMl@Rn-J}NI2INesTPijVf+y)kj{%A291(fWIJqlSEs8q?Kylz_qqK;=eB4tl9mTS&_A@ z&4rd9J_@Z&*c#dSrZ+H|fsq#&i_apbR>^)Eqavy(GVZ4s(dqLMF`N zutr?~f}(b&jPEHs{4Hrg!d^T(f@y6s$Esv!Fm{W_k;lzQsEyxT{+G};ZVrQinc;yE z0h-8yt=)a@iq!CW+6BMXBBI2ri9Sipr&uR=RcX#$v+(HP-nV1Gts2i9yG2jKKLhb_ddb(Wc?Yz0~7 z=y=r^G9kIcbkP2hWUOSbVZGn(;q9pF%i5EE5*n-Of!@g<754PnlQoLmC8|ia8b3_U z%Pc1|edLXzZn*ce0K#IA?7DnEu3If7j47x$G+>%lWt1x^7cAydX=*0FR%CVNhJ3W0 z;ok65wH=M3n2p(6iH8OGWwkcA+W5OgH)SVXCoVAhlEanY>aRl)K2NItx8UL5W7&SV zphAqobnYQbVutfbO?$$L3nXNPeXPbz^XeWYUjrBF!33*ni$V?zkD#ePI}Qhb1egIy zhQD>*3wJ6}v@O6SY9AL>Wgg*=@=j#1++%d({r&Rvb z4K0Jo?Q)vlGL@{KE%15X$^;Tv#^1fr1Yc1~^eBa^6z#qcpn=c;3arsIkZ4~IFl>xO z|H?Qh4Oa$3drtSpSDY1&DkSi)c0#|*{ECV0cH9M5sU43A5wWe4sY^Xq912^-1(mqP0)bWCK&%o9a>)I%lrzztz zf@oaK%DV&P%nEZ#*kvv5;#Y#{KV=^61nSFlsRmDrq}V7D@K zV8V(qe9mhyCbx3!*7o9tIW_?Txm53~j?CfK&xl;GvD?_IHbyLUh~!3n%wQAJ5Q_+r zyR|y=o)YxCcyik_qHgMag_}9L^%)Vj>UHJM6x==;lWX;|{$>uHQa$`4&Lx-B+3J}o z;C3!w86|E|=g_XuhIQl+@s0~_+t{S;Q}0~1GQ%R5rn$Of6BaI|Zu*&G7i%Xtv5I0r zP(5$z3N5>2=b0xPfbQdZ$sBp)Ss_?^GRbXEqb6d50U);Kd@UMg8>JUog1J@C5ZbSZ zlp@HIL=DEUr#&4do=of%3p-mq|0z9i8+LYQ|SF?#|!dVs{UH?Z-XM{KpHljV5b-)2!S!ai-J#AegOY{0h z(OCR+m_UW{cm^vLds-}&e#P>-a9{bRe)ed#RBXl+9ZHFiC$N5qWG@o2;5hz~j6>|X zjxUBkR$l93Ep%RkvaRXBgkhvAVGGuGm19q!-1t-*!-V5|o=}Gji8qhVKfIsw1ixZR zl%#AGVytg&BK^td_M_;Vt%C(VUMAPy;eSB+%f0r7U~TLP~ zh&2B1dWuVvw5@6}1s3XUi5Ts*#bVy4h*_5d?UkbW;#%b7;?!`m^rPzsw>_ zaIhVkD~Ga$?mnip!s=zkhOSNzveyZS_RRu>b!k;KG6o;OwaSt6kWjS#0&j)}Rd|3$ z1R=tp7nk$(k1r#X6Eo~5kM)6E*R8#JZ+$xsC(pIJIyz{93W|Ep0m({*E$7dPqJPw;pzNb5S|UaS?vlYZua}qM%92-Mlp$4D|}_R)a|MgVa+~ z9lftOhc||Xx$DB6x}nhR$paMMaj++xn-pNxlVCKPTT4P3(>f#`ILhm}a~puGX?zLN zqG-?*u|3fN$f$!BWCH(g)*9y<$Tehr-xlHkB1WT&fkiRu`#ND|sFHEl8}b^0j|i?7 zN9zp0Y`}=~i1{yyop60Z(XfWlHi)OLaWs<3VcH!lsYw_*8u0(rz6aWt* ztOn)jeid=}!XgMIAjQ&h=<0AFkc|IQJSGH65UhX*qRLT5`dmDjBC+6)zf7^;Di_nM z2;r)BYaYIDUDQQc#z)S~NF+vBfA<6m*`wk#KBg%~bh}SkAk#b#PtijeNk^pWKlAfA z2K6DX!CZ8@{RWMyLJqmbIRXY990bxh@6T>R)CXf`Kc1~HJLZpi+Qj9Fqcuow>A;2< zJKs?P+2zKPp&@eS^y`BVr^l@q;~g3S*+ZD%+=}LMiZgrbOGH}6v#OaWXkDS-MyD$EK6_PAM}l*2bGmE*#76YZ?|%(!gIqSRdG%zTK%TF z#U@jN4I7iv?MG3f@;8%RveaM5cxzN~L0@BJE>yiT(X5QLHlsfrwqisz#8g8!M+HVx z!CU&p=!b_K9HX0ZASk5bxB6tx$*D30ybw3V=&VMs;k+xZ@n{eW&Zcp)2(be}X+}y` z=%2gDmE!-bjF2SK2i^lU{~Q^b#O6*6S=q`2hbZW4OPO8$cmmxf$PN}1p!j1rV{H^!HPP&Razq})JT9<(crv@^pp`Ay6CT{R=*PHx|)A&^D|ih`0hS=A!#Zm-v=zA&#^;m4Sz-cloi3H4E%> zH>%_Z+Y-UAt7UM>yXvyLY4y~v`%BmNVAq z&%g?W7TI^*&FI=1VhdQX`yorsNylr!&PWe|*03raFuM@BhzRswx$r?jZ4SC=9`eNu zg&YDoe-`Lkx6s$@M`55uO#Zw@YtdE5Jtj(%-Yrf@=@f34A0&8$LOkQ?NHl&6(mxK} zfT~kgW4ggYxj^ls^10PAhCh8EFPkBGrEyVk)(^HT1NhD3qJ5MMfZu9;IijBdZL)eU zf*Sn|4DV2c6gx%~LY@5IOdaE@RIIgx$!-!74C)!)-UetPH9aqXxPS!1Dy!iXPYGug z{V&qP8`o0_rF59hd5R3<5yj~sqR|aAeT7u&nXkBBXnZ&!bvP+4%>vwsh}PKXW)?tUUB(|?Lx|Koi)VFa+e_sIx=@FV8@)$}4WSTp zT5wyYB-E8aK;zz!S1;~ffTnsW1OuO?ewU6-h>>Tgd$>zj6`uwY_>=e-=_+)9bOsxj z)}QpPmdQE@g{CB;4~=h0`~B>2a<|hdQCuLA%sJ^*LTt7=tS|UMi^m4cHK&F5+=bjA zpv_Lo2TpU^1993e1a>hncn)vj*4vfItc5>5`&ugL%ap2I@`h1L7E3#OVO$jZ6Ql$mdw6KEj^VifW>2Cm%qX7`6rEDBzXK4?KMNR17vNZdf#$31kv z5#5e5z{g3fAK8po{P>W_@1F^5xhKBzQ&|ki?(qS#7MfI0vwIQNs|C?4{^R$YvVPyh z4eS4JoC=CjKcW-4aL$(8ZM04P4;Ka8Os={?UY?!{qLUu+E{`&dO3`a?Ik)Az)cr@Z zS}~Vc7I5>A@)t~YxPbu8MFC6H zrcHDAy-|pzfN14Wm}`cP|2+G?5wW-_ea7}MBaytaA`cC#HMZ_$r=NcITlj?CuS_C*_eL@}lU zw>9^G7Cx3u>^|WR?TkE5zS!dyZo}1WNN}PNi4Q#A?NKNEgv7-1bM-^s7_OykK?Gyt z@)5K7V){lpWg5|gbPN9gjdK-8yLK9tddm=+L<3M+k7Kd)K+`7l3n)M6Pt$w3`I}B& zWzTbu3H9b0?TCy=**g#p?yQH|gKY>bX!*nF&NwU0_BD zZacmr4cE$N6>L+TH5pIVa&Y+?NlCSYVTDO=oPfPd(b77{t8E%K-mKfUw~@MzHuuAD zcfAcT8fMl!Qkr!iZa6d7SLVEa0x6(us^=KiihumWZ!}nCDhDt zDzjZrC&EqjpEi1qAx4T$Db93P{B?$%yJ9Tpd*AUX@u8O`C35)M zZ4lS@(GS&wGx9NqXkk$*OqAxQ?tmU=dCQ-wJhW1a-84;1<^*-B)$|$9n2JnR^;nUug$d#ZvXl}S0 zJ~JyEmBu+)SD)7jUO`uB;$2atPDL*}pR4?_et0k$P?3u z7_$d#Uof-^gfT$_^b0nZDqQ_TyS(akcWMeZh-N#CEkK9w6U#1IddND*-w5%Y=( ztl}G&%z?swhix;Rn-W*N!~m`+rwYl!sY~xyflaK?&Qdspy>Wio1()zS{%Rw(GpLB)~FYLx6lB+qM-aja`T#}+T_1COL#uoN|cK9ok4DrxhQ z2ol_nr$rG-WaIe2hircf*L}+@p4}XrV)fk=f|yyyax6q7Sht#aTb5w_u1S?#F2fc? zf=~n0Phlv1gX-$u?anU>=y#p_xi4pX^f91Ea}tnW?s@F03^tWtdRH8Gja}71a`ZN8n0vnkXif~>TxGN*5SLh z9<1`oMb=D#0z0?Rb_HjW1)~8CX6D>PSYTzHNAo4iv!Eh!vYRkNP#@%ejb6eAgg9QS zBUY|JmO(f+Dwd3(Acz3T3>^d!0Tu~K7R(bAJ|q!>Y*>M;Cncz}jGK$H__K9t&3XnF zE>4(J&G133S`}gpy>qC6IYLThF~ML&PmeOO?|g#2qs}=yd2~G z#Y?ISy*xD3%`<0j4<(vW*2pU#lr0ivT^#@lo7IM_OCp{k!OP^DV7L<;h=;qf+mhh4 z!iv^*juppOVMf8Z^?ELLPh-z+NeGMR1Gg9v$;$27MVrGo&1m+d2_6}BLz(AYk;)>7U?h^gq~^OC9UFN z@)Y(UKM73*(TF38a0HV_PzlrRDJdFn8=`1+$yH!9L zERjONl3e29y0JN=#LYKTqd2ZGA{joSVmD6K#CQELfCO52DSEifFFmJEfdrd9q(oOc zE*rjVzdyYU_=^|>h6JRK3#!f}C3aGMrjfeDEIbS|>7nALY6FBjUj!8GDI>t_ridN3 zJrlVD3qRv>g5Aa|nOFr0Y+6d+iw0vDCK!D+M>UK@UJ5zs5zn0+gy@nT7c?ETZzNzt z>I~B>!%)bNB2bUutkD4W_*yZ!?~?sAoI{iMq<-*wJCyM7IpvF41ki*v$6sqi6dA&I zSrS>s)zBi1luPe}=O^fzu-0rSjlQSk)9w8|_sa z?duFztgm6o$AB??=?NnyuxQt^lq&%x3j9ZOiq-Y?F=<@{nP!1#!Qh#3lo1q9vgTC$ z$@FZmfQ;;;%$f^q(m%bi`(_}$A9ykKK2N5z{E3cqk``;fBMZu4y7M2oMLndF^8f}6 zTjWwk_Cfu-FvFoAP!<=$!`>w=2d9Lg5%e1zMrnqpSh^*3%cJ36dZ# z;9)~`AM*YoVC3Z0!^RN)1igR~S1rl)NpM7Tx=D+AqJiv=6;QJp1C zOo;Q6!r;uCBuibj_9b0BT7lfoM@V=ym~B*T&j~Oa?P^;}m}|@2kCG@DQ<2)2gvmoY ziW?aEL((Hbzhhxqu6gfsl!m7#m5X49R=?B;OvK<>2?PnQAs?la9(ff+B`M|oFZvSs zKwiwptdI*-#HOkjC(F0Hd?CeIwN2~|`mjV|Iwz*pMD*a4w66Fm03Y|1MOue&*8Yo6 zRN0g{y0aff`QoH(bnYkQuU2wNhQR7qAe2J@VrU(^r7(q%gQV|Sbb4D!NpG=A%j&IYeM2MvOXH9ricHSANCY#fSdS zsn-J+-HR&8?AJNzX-**_S*urTs>XH|AhSjQQK_h6Xk{mfvQmn9hZp9F`D|1rYh-@!l1vsL zr&Jsvz;;eajA#Gd&Jw3-n9I^hYv8EPw90h0B<`yyP{4Q-X~uirph zek;9)zv#Kw6UQj@mcJKTJ)D7q)tEIl+%D2JEMM@Nx_FXirm?YV7YT>Bz=^#%Gnl&*(9^Rl(na%?RKk$BM# z01NNGu_*pZXZV`E1vntdiiXDkHq{CR0k8pO=v?7V@et@r`*so8_O&?)!5D2c8#OHc z3}nBPon~!1ZsQ#ZGecLhH9Av+*L(A01)st*G$4HE?T3apBkX2ilN}@l(mD~CNdE3` zwR6bf6Tg1dDr*7)XZ_1Q6{rUJxWy|JWsjQ;>_GS>%dX6KPPxQ{+r5Cs5S($@vZVeb z_H@$Jg_vmyyi><-;Sl7rByp`1T%?MOp}0dk!beC@<2`8fA%peW{RM=)9$%4;$hm9a zbq-}Eorf_J!ax26(tNnk4e3)=*aU?YKm}`TUKJ3sO#P z2jcBR+G_yGS=v!y+-Om;kTfnv)xEv}ncd_`nG?t`1(j5V=v9SB*$v5m>e(GD^4IF< z6(nN>ZHe!Ws&DqHQO2L+8GT@Z8+t?zeou>YzHHepl-3WdDiqJDsDw^b*l84< zIfUJ@3K|h%3C4>aZI!JEOkZkh2h+w)BeUB8g06cB2BQY2(UnY52{^oNHaa<|>h|H{sevszgD}?fSqxz064=^*1zatobPRrU~ ztPs8LsiukTXLiYrT0!`VwI1hVe8tW(N5Vz)Wp$18ft`<8pa*^7-16f-MTtd)WC>>- zS7P1RKzHQ$DHp*32?5yCvn08)=8oa3*@-gzb!8%>o&&^Ypy!e~yd4p0p;1<~(i-%m z+ld+U9v}iC>t+ID0NA>O2g?~a#MXYcqCer9T?tW$yPqbm2xSr~b8nH^8D1)a+Hmt9q1XSY*M9wB)d^;5eelk5@h6lV)MPjwLWS zm@1c54wkgGdl`ijh0;#NaO2Eot5{rTA_mFjnL|61$5q@ANr2-QJPscS$$jLa4-aGE zE%|=mRk>GPi8*|y^XFH>4DoKpO6;qcC2TUF-%}w~yvsY0UlVq!gu-v4pz|lKxqpl7 zyQ}{3E9(&gGbOQO%h`RE{6`is&6G@7a50irjhP3IJ>IDOIc_zaM$I-}518xF2+ zsGTim3VZv_GetGd-c{xQ@lBOuJR%roK_=^|CJq=V*hf`_Bj^mf%*%kA!BhHxTZK*OW=C9jL16<- zkOCL1yyk(XRZnW`ocWsT;`{+Og?d#6h=PVTaQuRABaF+3KLQ6uUqd4ysbzXF3ByBq7@4J_&(PTSBkGhFK`?bo`*5 z<$)^xVlLYQ8W(qqGOqIH-0W7s(~xEFiH^OM0`Benjm&k6?ujJ$Zv5b}Pif`LOUGMb z(Aro!T(k;Bq+}_4FI0qCs0&oy8hpOp4Ta~^vRA9%N;QAG;4S!I6b6N@GBzoE(Yf*o zRRF+;2q^o2 zY5|*4c8jPVxG7MKgiJL-j;>QCN(V(e`CALlnN#AlvBQ1>kpN*Tu*?wX zRuJx#G<=6kHlUD!H5`9V^!BI!=6|R}{4#eQM=$B&Vdz`&|3cLLrP<_Xp2H^GtUJg?iGH>*^~)(t zL2oZ%e>PNXdcG!#@!L^1RCz zp^zz*U67aJP~Z=6f*iTBGy!t^dU5g2{Wlt%+^>Sfr<9|_l{H;rKE>pw5mkF5fhJuU zI)J-L2CdHmyd${h`#ho=W%2&w^9$!YRj*B;bV z3^VlR34jDD8;{w6ZgRKp@(9ytD}V<+BaPf8*a1bGCKfm-zw1&4T0~n`!2od_4BV#y zO0?L)fMKP5^V};Ta|H%yJ3#~|DPgBWd*(`mc9O1)_@Sh4`v-u{rjo9j7CQt&q2 zbQr=RzlQ$n=J-bpU~3HWBMf4SL(BDekF5K2w!2oqa@;LQ0&JcS#%oX^sAR^_)5xcK zenT?n{-OMd^Z)`yy!`W*m)Q96G6y!K6MT3FVv0u)9hB+e#|O}rbtq^`dUo&YGcmZL zM?9IS{A%VBZY@eDii&_AO5K+*IU)WhQfy zKO=Z#X5Ro6FD8hd=ny^7XLJC1aa!r8ej>weu!<|J=_iieehk{*s^5kvCt_x35f+#E zx{i(+3M(jsM=9+7-Oj72@^+3+`+wqbhr`E>p1f~{D6jasG2x{E8#I!t`MDew zUdx72f9WVTu=5`PIpPoO*}$a71l`f`CfPP_1S1SLc(n1-^Zdipsi{qjGPJ{VpeKkb znMN#F86nC0dld~n%qQc;x^9o<9vIdW_%xc|2B2F&s2fxeoFh3jIU<;+>OqdNxU?{j zA)y>M2>^_id{(Oc!>qJ$Z(;KHr^G7g47t(PBAq5#K9GiFn|J9elBI1;Nfcv8 z>e8IMJBu|=*gB8;jfCM}@P&QFb1uKv)NTV*9%4UsTY|Of=xQddvZKGqT-G`m9R*EV zvTQ&xj2;SGGxQ#d&2My)!U!IOPyv@%ZsQ7`*s&f0Y4QyLD-zK$dPW!M0X>F3o>&T> za5uiJ{cK&!WDP{+=+@~Mf;pjw+=-cZ`b1Fy&ctEi??456bmG*KN&+7~=P4i4KZp>{ ze&)m6A@32uUWl`~5vzmT{hc8&Uq)0^MRWp&Iqxo1CHg1TT13E^TLXW{9dkiX2eLoj z-{@!Rd9EpPszWS9(Xe2nv=(;IDkuN*sT~0LbtB{ZM> z``Gg+tiZX%cgBb%$}$1a*<}(v^J0}qO*{pj`{JhAYACt2Fx0Wr9=xKPdV&-#MMpUh zy5dc`qgYp6LBRQ*b|Jigl4b>>$G{3^yT_&E&F1E~2(H&B6M9! z1@7@~GD!4+rMSjEj%XUvjz{b~={xr?}LY&geEZONlKA!@3E zrN{xzm;9pfRzzl(QM#LCkXDLfg$hi&WK8^CQ}65 zC|xuOlaCDlOY4AmDgiX*%<)lIMAic+eg7kp9{w2Vek`viY>=KL1vVY}DJiWtdWAD; zAN94zXm9%v5uilbj**$sKZ8vV7+FBEeq~6jBrQe0ug4fA`F$MtN682!T}#oYp>@LN z4dB@f4n?eib}Z;AiUs#lBE);TSJ@ryainPg{sxi*o%=0)Q*JC zfbwZe1=E_z*b$o-PQmvdxFJj@-IJb9vs8jZC%T~wU#0#&iHP6vrO!waI`HX=&pLiL zX=U&ZtqBVa4YJ-;e5V`FKKo;(rpPkb?pPZLmsUudm=7x(#Bv~5ZV z+t{Cks;TWuCzAP>+Hqr<0 zL4JXVNr(dD;5U8l^xEHQ^nu#UcjNR;nKQ?y&j0HQoWR4k$N3517w&0+K`AKQ&3u2H(flJ<%PS(>m(gwFNPu#uR6Zg z$3Y&#hZUo=erK$`)Ni(K- zGIKn6s9wu)jFn_+mOa=gBTrNvXD`+LHba)b*=z1!SF>$!v+duuoYv6Hyoe_fOijqd zkghRk-Ri4Hjw4E@JYHF@F|r2>>f80b>Uz>E->W4nupHWBf3>ov#0~HxqP@1TK;%}O0AjJwQG9Wrk0Kwx?V&sY=vJXheaMLR9Lszdm z8wH7B^4mLUH@%UAS+!I5zVq^M$eJ~kYTbP2{DY}a+ZWtYQv}Yw_wq~>mnVD&WzEa( ze}g$D#0ZNh8D6zdfB(f&ty-{t*;JT!-rshar+N3&YE$pTUmDG2->;K)PF>hnH@j_6 zyKajA>+@-LRoy=6y!N1ILjJkiDettG-M7tgqS4}2yS}&W`*W{n&3kq`&wuCaHOr=I z03sA%gYoySps=UXfj zxT7Q?rled_(v65HR|J6rqF`#Rbl99RWSX3?|l}OE1bks^B@9@y9Jg`V}V*kEu&v}Li96y!6 zI@8#cd(GqkEY9?xe7AqqKy~x9%{fDTO}EQQpwD~$-?)887R}5S~nphh)4~A5(4J|XtW;+7)eQB0}7JQ*V&vq ziYW@h!jvc|kN`yjCNN5RpbXW`_I8#47GcKGHM-6q;QE_TM@71y0&qftyf6tubhEwG zT%6Fk>dU*D^^ds$$m$2in&*{$`|oE}3v&c27Fktfzn}hm_`VTNrF_6WIyp6PZft63 zW<(q}dU$qhFkzj9ulxOxw(}s6f&KIK^s4gGA_yG7L}W=xmt+9akx|i*0)zx1y4h+& zLNi$mhMs0g(wJ%%z~ahtjiUeuF5kY*BVFZF&`cJCp=L>ru9pjoG=PGDYy|k}@smZ1 z$GAg>PYxd%0&af|NE-fk=(eWn7HZqq|94;eK)#EHJT=n6loJ*GUDUq4ep@o?<=i*t zbH4j@2OMmmK?Y7l3@)$$2LVDnMJE;o5Pj({>z|v4z0FZeIaOBo!wezz$W1Ds0RP;O z304xInCx;c0tg;pV!{ePHPBeiXaKlm|K75l7aZ!m5)ur{3l^H?gbpQ0R6pqSxc%6) zaCpFEnHxQ+mhlT4D+ajj_~7T;er8Eev)S$KACeeegk^@07}$#)PF!VBP?9AhBtw8d zC6`D|9pVx7W-&Zv1}y5pfwjtQzKv2xF;mbS;X+FbJxwiAZgQk08MFrXZ{n32n6fHx$NPE_dvi#W(|smWmy zfE3g6!dl6|$P`fp7JI^g$>oq$*yMy5uy})q8)A))&WsQ)hr1OK2@&Cl+GgHZKXM6!wG5I zs#mSm9o+2yy{+!g->y~vYIf!6Pu;!ttC>Zzz5eUwrCztepZm@YE+Td^Dg}&@whC!9 z&@Q|?BZ$=wJD&Ws^8eX~&#Bl`@nV$pPYcM8%y~FDP@TUOaJgJY1W@8ehcxTH!jyp1 z1b^|ja_s{{^L`QUX z%FfscJJItz&9gkob3A9J%#4{Z6E#oMG)t2-M{`!n%2)|2(efEldX(vPkv9jiFZE2%6+?#2tu#DQeMX~*bAnq7al1u9)@X~s#@6OlzI?8 z@E}J=4>XG*qg_v8K?hM{$YBX6s%U{lATU(oj+2NFL=+@RK{AX1lVmU` zA`i(U!lGYD=uxrRF{?#!R0GIi{aKW~tK`%7Oi%B59zscJW zBIjtF4pK7Yzi%>Ge&;x~P7eSxNgwo&U{v(CtMIPgAJTy4pH%cj!5QCam&R;u<9n-y zh{n5atdR5AA4ngN{?uAK5;D6ap_(V4aI!Op$qS1&zw5X4x?N>b&XQ`X2bwOFDP$bK zg-T95)P!SIg5uyCPe!BAh36LhJ~3164a^%=$Mm)bWvnKD2Q|J#5KI+w@9GDQJF3HS zKN6UMlfVFD6F6{hF;bxFs^sE*O%Cc-byi&ELQrzFzv&!&Wl6$Zat;*7Zh4Hle)EM;y z61{{{(?jY^<<&zp*EfI#|<kFIY|KkVL$X~MmF3bKg_>kl*IzZVP7q@Z?qv)6+4H&XNcMFWaF3YI8z94s0r zQNY0kfm#uNL;6*#;AxZ+fVkoKNZ^=(=>c}Cc#oBF7XP((UX9FIUM*Ur0jdobo-*W9 zs;z>WOjEX_>xESwAQ#|aR0I;W^(8zUv3;Q*m}9a?+iSc4q+8GJ+=TtH(aW0qh$I9h zAO8LKf;u4u5FI8-PNFYq810p=+$oPHlL>aP4149X{WZ?xaGq*bUia*P*(1f+s?fCp zp=E?r$v#K~CdTtaxSgiM`j{`|+)D`e_Q+o2WVcyWx$HCf9R%TyI(3Tq@^>}{hZpT3 z0G1_l`N<0s?T}i$%q|$Yt8A^ND@xAU?}ytij=& z!F~%b!KCs&Rv$AKXd+DWbF}IV2t^&d+h2IDUYKXZE#ZHd8OV92lW%4pknLRG*-R1x z=ECpy2k#!b$>q74e!2I8O&Hsx{@d#p&9go04lN#VzRMUHBCjww$CG7EdJQqyj;>^% zwfm2fJG3Nh3mTN^L&}kgmz@N%(LBx+>>KdIRma3Y5ACBesimNIFTUkVe4xN1K0ET9 zfl?G59<-TX=q(?(=U~9RW_u>fSXA@rW(Of@Y-&Ns=`Fu$xMC)g02LWt1)5E)TAti12xN^Kg7t8h*{J`df?IG?gp_)! zv_>kNFgKK+G{%fah2us6Ha0f(1jWOQiF@!upP1LV#_+E+_L@%ACNU*SFJir+6#y^qgsB5BeU8!b#eZ!N1g{gUfvJ&aZc89N3$Qb*%N20Iiyv7{K6 z+iE`p5DD|SSk-57v;C`}`E*D&erFZ=Yx%<3fZb9wtnrtT#9=|~L&vHP`&K1z&3fmE zm1Na5nMR^K)+)2N#QCK3cA#Tb#|m=3Gm_+168s-+ZQI(=Zr2+u?W+;+l-=Balv{bK zvy7^U$}Y7E9;1g0F0Cu4ey*e=Tf$GOu61QHJzlfNI>H{KWP`KD7hI#i#0X@l?WtTp+mk00J} zWmpg5o;C=8jt=e#8^Pc&(4Dzls+gXduOHNJV)`~q{X6;0WT@WcPZsr4+P5BPXoMSP z$NR50;%Y7y#!9B|IM~vo&6w(KzEO@fTmfVkSHyf}HV{?x3>$jmb*JurmIzHAzd6wS zRUN+!-|q#)JcfMnL@o}<{0;HmNGw2xaP#P*CsBTiv%p*(wmK4m+Uk9-2FICz=9phD zb`;jLaS>-3mpp*oUq8H49{EX|^X{MxxxO*5(Si4aTYVjkXJZd>!1amOWc~!Sh6eOK z$?so5IiUaZ(!EO$zDloRk%?OdK$xbY(-qC`90)|?ZJ2b+$XkgDbHoj+G*78|Kt`C! z`-DJ4?2WQJ!5NCRCSezVZ7Varjhc#zWA-1FDK)MCbP?MVs?TGBEXFsJ)~!2AdY=o|~9zt{X` z$#PVeJF9k%a@H*A_a8YFj}!>akR=@AoSWH;BQD%8X@_dI=ANOqk`?2I0RGE=QFKf? zZ+jHDjE0~MI0esurGw$t&b2+OXI7VdXtJ$Nnu?&Z31w)i8ya_3)w+B;o?Rom<<6!L z5jqBMCj{SJiw1qusC>s~oX}us95gUs(BlvsjhY;jS7*;wmj-r%jd(;_2Mz~pUPr%D zEcvCwL-|18K43BAoDXd1eKK(H#s#?eq^kfn)|H;s93}b z8vG|dfRVVv-#dQkHe0;zyPbcML^^2g(-Zy33mjgA$Oe;)D~SeD$T}?0m|`imCX!d; zkr%soxY~W9O)zgrw8-vYWxcaG%B*^a{9mH-_LU4X5G#RbWGjj}nwNa_QHsANPf&f+ z7SVsKmfe2p*uBi>*Lb)U=_5iKKM5WKu{f@sz!J>N> zTQZa)^dA=P0Wfa!z_Von&l3hYLWF50Xs+#&w7ZQ7sisi}ob$Mh%3spCe^%k|eF9HFH73s+od*1d&BKe$3b=nP z5JZRuo?Xp9y!E`s${&HW70@~T7@c5-nbEK?ZI>kNKWp}OE7EH<>;q{1r@2(9E@XTk zJ60(mEJEC66c{kL-#~jT;epHapfYsNS2hxwj*m;|5MJ+Xv3(|OuKPcW2xmF*iXI@) z%OPzKRNDYakeR;dsF}@ZtBB)saV`+N@T|cK0qH#KEF2)r+Y<3KLv_M>_5$c2-VJvJ8+9%?CiqM=17D>p&N-kWT)_ugB|l+q4#0(}A@0-5N;b}{S0 zta^%~exBL8%~IID@%dhi#LPnW`6%gPd2?P*t3OG+2ZoteD_zVYFIG#7>apqWP2cc~ zj|%cISM&`1V5(o%q~JM-zm*yT9y1{_!cWZVWQOt!Y>5chL^0hn!NGW~QrjptfkV%rRU z#_Rw4pO1d39`TU0#-h$@2yTAU|Zly*MDnnu#;1D5fHsGeEw0`9VP()Ui&76dTBd z1r&G#C%pg(4hBVgTZ*+bz&d^+q=AA4=no*AH+_6qtQZM$ z$7m5BJ$-y=#NfSjb7>`p!bOD$1^Rolg$WY6xi}ecFx=8O426Jo{-i`7BX>Gj%c5KqS74+m&^NPpeb#s1DN;HBUFqoWIMkr=uE%+;Kxkk9rzn zlyaF#iNtqBI4rg@LVImzUE)kUb;F$ZOj$GxYGsjJqe8Jh;I%d5@B=UWA?|aR_nIl* z5<-3O@~SS@IlRRt-Gec=z-@wH^M+6(F}!oEVl@sgyeZga*-3UL8%mia9X~zSj-C>c zwe-BEDaD4j)DjTnO(cc9PN|y&XG=TK22@Bd2p*{kxhC$JzA)#&M2;j;%$iW$c1Rkk zl4k9}Lt%JgHay2L3OX2I>@;L7^-Q%g;c(VDW>;pX0QMwB~Y z0ByTCe%a|1mQ>F%Q$^VqOEd9M>KUY0J=273D4MavnVO=lZF3;Xyi9m2Of|_g2u(Fu zf=ULKS4GjHXc&3(ySYsjrkcylED;0*d!$z=nyH>nLK15!lhV=ZshEXop;%#+RMD=p zgRp~G8r!+z^C^>kSi0gXq}iFCD)!1K@CUQb#-GZ?uN1BEVwfB@0*!dO)#o z6|OS5yjd(Jk+pJET7{1-*hAU9U0(e*o(q5WK_$ZizB(OWyx36#<_i}qMSxHMK?6B# z(AAK0(V~q_?F1UxhYna)8vsCJzc+!34jCFDVguEa)2Sx{oh(>Xlo&wWY%bqAgI83p8FRO4JuuH*BKhh7A#A>Zygv60y`zpDkL*#=@AC zf+~6*jbz5a(aoh;o05cx5TYOf(tPp41&Esqv9Km3e8-L>7a~H0rKcID0yAQ&U|@j} zA5^AHQIcX!MA_BakzNLe0WU%(auu*4ACv|4ALrwHQax)PzvF<8#>4y<&w+7tRZF2z z`6zU9CSPi$p@!}l|96``o37iXH_>fk>$>f_MYnXfWR+YEF0tKq=yq!-<6&7g3(Gkj zYpElsmbi|`jtCg2VWu|Xme6t~p83?^i9G-K3DA6||D!B_07*?;{@8LvL_}o{xf?7? z4s;-(RxWzw%2R<8htTV}JZw7`n{6}zELOe~9CbR1W?V9PSE&Mz=(23h&e=oRXl_by zdT~XcW!JlDVyVc+OZt&_TIQ$2fwpj?*@Bx)Xq)^e+r{oB^jnJgaqU=5J^0!p(9TsK zqEE0_IkaKz1A_(6p3TcEjUNktnJ?%Q+I2foqn?%BJG9s8W&wTGU*8pULakazeDWuq z-Kk4wbEMk1^c!Z0lxrkkPPOUM%+LR9VLudilny8=dfZpuX z@jK8|-h->tpf`H9n?*U)ciW~AyN}y!Y@^ev4(4XX+URL#F!iQ^uBW$(M!RvN`IsL) z=x2VcwXCCD{ja=H5@PqL{3qTs=`NhU}&+6vS)g{!SY=b()d| zVHl$t0Nn0$?Jj)<(b~J4t31mcQboK!?B2Mh6gjXy(mX$Gmc+ldre+rGuSR-gM?LLQ)-6|>KVf1 z0;hx_CzV}tk)pWdwD^`FEOlGRuPiOSEk#mt$W;o-L@WcU|JWJ6|3~s@$^(?1|f1B-?RIIHG1fIZqxTOUWAKC>c-A%Nb-Lc@K7k0bxZrQ4Rq& z$`xe0oWzoH5c~)GJ}Yj;rxF zp2poc8}D;IU*m9mieGUnzJcet9+jTtuP6Vm@wA%9b1mJd8TF#((|cM^kI;GAuiLbr z{?lvvO|xk@?WX%$OoPyJI!@1_(;{@5M$>%#L6`9=O{PQWF+Qn1IKah=7B=8uBp@S3 zYtR`ql(wKR=qR1nPufXi&@Tmg4z%3_J*D>^01`nbC1^_Qz4xo+ZyvYHxkuLgyWnM=cn+^@q2 z@fbYtwHSTKBkC(CPBwT;oQMb6lC04ieZ61~mY?W5;(by;tgEvKA#ET>r4TcWd2ME86|nwV{ul498?Z|pkmwsl)) zq1@dxHal$liFN93V%=qOU3V70mqbrpXJUzMfp4f0L5Glth=_=Yf+Q(OI)Mm6Oj^f= zW|DN0`kF@up^-pDL_`!MDJdvamNDe8ljrX5N`+FPXAFn*$P_A$#fzylzxy^umNHsE z!ji^lu>?9vw(#7D%8EFq_ld?vo{W^u^JU_G~ zBE>U>(QsN#;AtVXIj_p4z zb)1BRBjfysl=52*y8vb`bI`@Am~)Hj%?(0eupWpEN>+oovkBI7gYoVA_dccp|qxe>OA$<2+r5v zN(v1PB+}r`9u3^UQy*U0=Yg*LjM^|=y@|% zLtK4&6=B4DPS)M>zkD=hK3_xcnr#9CY691#l_QEvEhC^6JK|X>_FW-gI1J;5T=;C8 z&Ik%6W0tAwhr{WL#==Nzvc@T>ou|^IZYe&`xtNzQ&iv}3qzXBztlXPk3O!Ya0M*gm9sgZWl((q@Thjp>RCHYz(pW+g z6ghoBkdIFXvMeb-1O!41ljDQ#auV)*4|cW9qCjn?XK$HIG9V{49yc$)K^Xz2m7_)I?mCsrIDpgliuPSpnrbm{?y}Y~SzP6?| z97}Yy&!~5V5gr#akEB>4F-JFdH)ATsYLC<44Av;l6`ytMRM#fO%$nEBB?;L6>H&do zsF7V)oHhfY2vLYB2Bne6b^`+PJXI=1A|eV33JS_7DbyI&ryE>%&?5&L=4xGEE|1Y`idfz2L#@`!76JT;e3RW5`ap) zbA<7n(JTi(z1{RQeNqsIHm`k8c)TG{EoLou;dT~7sPAXt5aF4n5s<3tM>HFf>#c(# z0m%*v_MVYSAv2L>`N|y7E~eJ=)|cn}NrT_vf-NtyB<_k_(ADxOr0PQzmlwX0W$b1^ zueT@H3<0bO-{ts{!%8vOhu2FzOa4fUGI#RnBA;1(>O(cBIQRiVo5LZce)Ah@#hd|P zM33X*Udabo&RegbjK+tcGAPB1F#x_p-_79KG4VIt02*IPX5#0!hk!3EmV-Xplfx87 zMjEXwjs}i0Ip7Qe7A+(QG#|5&*YRFX0=p-r<%GKSa+%AI;bEM0H{4L3XyIdpDK)Iy z42?hv!xcA_mM^%n7&O}EdIs)&I*8oob(>In1G2uEi@Ld(o;3aX4`|qA%xKz|iOkS8 zXTUt4xB~VTYeA~-JQg4#l>@+k$CoS`z*8V;QJ{wvf)8fKpL0fZ=2;}6PivSP4i(9L zN>aD|_M*=gh-acKtlrm5saAX5eR~*p`DMc_ZPiXjTE!vfedOk%7LkZe1Y>LKeae zu)booz&W<)hbp;9(|eMq6cEr!k0hRhXBCI-J`9@^-cj8mGXb~RNjA$2rjOQ&(v3fR zCQCiasEj(P*-~|xV_hcwao45=Pd;6*N!L@>H!Y39?5p(_$yt^#4v|@hF#!M+uFFK@ zA%SF&?o4zOrG-2^`6uPL7h^e7KEw<3#h4nFb5}-Djf_=`ZDh04j{7Tz)z_4@F$PahpoG(jXRE!Z4DOjv~I-# zpIl1}KmhirhC(o}(gB2N1}PeeR|ISa`f+oXyX${`Tj^d2Cb?EYhF#no;UgmCF>rzj zW=vC46j|5*fAU9$_T(pfOS#jQNv4!`b^w6@Yycfre@09YXaVFAjR#@aG-sG2{J6ni zP9gDzoESCv-rF9;o;E(8ZhlSsY;bd24Lss=gtX@F?#SIe!Xar=p@agQ-8QpiveQgn zMFWAS_U~oSKy%4FuKqmyWbZZ0c;nMP-#M*-s~DdA8h#%FCdke$d=}>xD;TSHM7J* z1%%FAA>#>(cv(_@#I`KU0wGSJI6w=IJdmNx9%p^0S~>Nk!fJZ#*X|}M*Oe8^K(Ei2 zbGO3S4K=bvl>+PLZYegqA+4?nm1|sycj_!E=+}~&D)3747HX&BL;j3+C^x(%7Ca(kz z5-H~ub63pF?5>|VZcAcIxXrh%+12-xl6+@ENSz{%MhRS!WdH|Twh=_=YNFX92B9elnBpD7su7*ZnX}8oRXh@b3O2Wd zP1MFpy}SNBtav~gJo<0XftGLN&*kw{@?bLG>fvzG7gp%%8pqU7>k%%1DH9x-K7v1C zD2fwPiY~eYEYnaF4%m7Fd+SK|EQYxC;ZPgm!k(U?mfbfI;R0jOEz z-}d0_O5BK%Z6*v#Ve3DSXacFAa=nBidg;G9-QduUS2gV{f9nJ)dGr)`_{p*y4TBO? zbYZrZw$HZn(2gp`ECG~pH4u_#mgTPW9U77NLtaI4@;W&KPe-84L>EJ0XHz*C)~WIC zetZC6o<3WXfo0cs|0*_3!JCA14a95JNX;TE3IND(5(CPxR*`7e zS+QEQT!(a6H3oJ8T^kL{ps9c|bWB~aMz(5nL2gY3GPfwoS|g`&*Z)8bn0Wcwmt5fL z0aug}6(_};cVO{xSw1U&Oa60^{{~s41}NG*XJ&4+u3{&Rn=HjnzCHj&-In8F<$k_K zjhJnLHuzG!RE0?ebJ2>5?!BqAhqqS;#ez5uJ$haym~OPCRm$@LF!u6&E;j-l;ZSIZ zX^~Xf_~*}II3UD?U9A{86z38S2h9tN3Nj9_1sxKmILNT5e;_N<5k)RD%mmKhn)BJpeYcFf;sq%iL$u5dzWzrdATiS^j@t^pH zrw8(HAI@k3wUf08a2bO##$Yv!0X{N}W(0sm`On&tZk)oHicM$NsOc9O4A}-2o7S(V(uuZ-d@?9zMNb9(VE;Z>Fzp<8Tu#`G#-gesBnE zZ#ZNbk#$mClmTaJxUQ}`d&9fjYKDi@&Huhmo+vh;vdcJ%=@2_|bD0fstF0z#IVqu8^xjJyK`zhr;=8@ z%V?o!)t{IePfYE|`gpV8J*@ekzgkgDt?Vj6rdapcSW(J2Ae22p+6fZMAQDly|AIB< z<_{F-$gqDU?;UP$|B?UiEJK@DB*x8Nsy$^Ee@CetBR|%2)3w)@_|N1yw?0G_k)2%( z&&@}`K_$8;SA=7f^j+V}lC@wTu}G@vLI`6M zym+9vQ;~(Ac~}RCu@(d>JpQx@ikTphU;yT_Ge6)QL_GNZWaY zP+tG$6%ICI;Xn!5EXT|pfG0KsgmPb2nR(hLXl1hk|SqHoE@r?f0pdOMZf`E!tq0E(3m8?93NDro||P%4rHRjv7-^=Pue zItJmVv`4S4Z|iNx^*fKz0eBu-&-=!Dm5Y^}_5iw)bRAA`-c34cn~?@ClYH2gkpWcZ zSE_X2i}3~77j%MkOy@`QABap!YRBZwkVwpuxKx>ZPd}X@<)>thj7Q#+`P9Z}mJqr3 zmU2rerA#R$8L|P|0k#2*#IF($Vo;)k9WBZ94wUdjScii+_Mt=K8_5PpI_J};n%$vV z&hJ@G$Xk%CI48<|FKrAFFxjy|?RJy)NQFM(4?>fPO(w*bu5KR`Dop*EQ%{ z%G>LHd4sjtVZJa0(nI%m_ zs8=j?Z>FQ{l$!fQ9It-D!|@p`Qbn|uiYrvSzL7c31NCbqsphN7YOI3f^TZoPW|X9% zV#YbHvJ$&xZjd?u?thc#KgI&g&b{NCSM#!&(fjeP2K&sWM-|rQdCfg?d3F>%tyz0- zn})sY*FAG*|5K%0<)?}X4aTGB>Q)!8U$Zdr^}#9(Yov0bLB#jN1{_l>At~#;SeTW% zB7JzOQY}`AE>u3uR;S=vD<13=D!*k+CEBD8LqOWA^w6qWmWw?(yimnS7$`k#JmDFF zx+6n^^CG6_2guGKC4m-(zQr*8A+cT_{lnvr_Gm*2A%d)mf6o6M^UPfT^KzOf|UQUTm(19zzqFTGx&*Y{=a`-F;GGg=O+RQ}~!h z_;E|DnAmV*+O)$G5Y8x*2F;eVp@u7>{rrjt&q+?n=t`%#bLLW{TkVb4g+cuHbPezm z93oB#HNutwj#BAd^_Jvy7?8T9MD1WRakz2@EV3X3PhbQ1#KYy;mryd@FlO{Z*4o4; zJ_&&tA}V4skG9Fwk%N4|K6oBc;%Mld!AACCi*JQt)LOps+LSys97G7qYjxu7y2aSt zks}X^6H9(WLZM1Q@%wMd#^T4##H-@2zx^}TY zeF)3g7A6Ist)ec^ssg8+ovBs`pf~cqDd*RkVp9+{$ta^&1aHx%BUg0IN`w)lMPicx zq#U2q{Ia z;!6RLQ4|F#J1UiZ7C!j!Nyx-sdH`}$uoETfC~63mqrRRkw1&!pst@-xlAjwDUn)SH z>0ph_3!=;xz|0@ESf&@rq@)kAqMcb3b*q`<@6KO)sZBo1l2%1stGX6tWwOdVz^aZ) zN}l>nTq2MYm_&beGQ{xYGdvY>Oa*xQ$Z9?+cc zO^G(@jw%W$E!1T?sqD@oc!{7)_4Fz~LRjBF`>fRY3NBw(M&Hi3+uPe6jCbbDQgBt_ zeIMt~2;Voh0=?tZWY=I9|Dpqha&&yiy=ZO7i*w<5_Q4bM!mtYxwxP5o z#OK3>9xZ-=`sbW6Mr$qn`goF{lSF4Ksa8&^tXi$h>oqN_npX76ingt3Y1=s8^+-L- zi_bO6lY^BQs8pfhoBiHg$HS%vwEZpPDF6}i9Cc4-^=%zDnL(yN~^B z6I8Kh8_d`Dlz6`aj<`2=EZzMymFsK*(H*T3m&{Dr1OJE}(>uhLTquih8zE@vuAahG zab`16C?|-C85yTCZ)ax!PA|8dM}cMMtQuv~QCDK09Tq!_D~V?}=VH3vgZKtOP(qYD zMM67@_}(Q`QJLj-9iPvN$bH6ARV2!7VVIPJRD#RRW^!-(5Jk-4 zS1gQ2auk7j+;K+bSruB2;!iITHU6xRD``;_fgPoffY)C}N1M+lFJ z=VKb@vEZ}Iwr|oV`+B+RVy*+F5P{Xs?}C1caODn0_RoU`N8hB~RoDb5;1B2zYY;~l z5)b%v9sH{&baLz^dC|qjZS5K#>-dIhJ&OPko;W4UR|L=GgIpvYqck1m<~74y{B006 zcWN?kkU|LcTd+J`CXVUqqh)dn{0hR~**YmWbBri|ep zX`4{)HFP^>0PQ;*tL7UjAOC>KSbp$Mned)2BF`=!g_c;n!`uGAXg@iEBHVBAyuj%} zBQ>L;U#Q8D!AGV)*D?xi3puQrL|ypgTgoEAH3A%@Hy@QK9{I7?QU>@ItCKkz@H&Br z8V+Y9+7e>(Ao&)M4J|}_T|hrs@Msd?U^2XYVIrMB6~K2=1ip^4!gK+Z`Y7qTf>H~A z=`~>><%&EEnK$Q09ca-O6L!xbVO_0)CF!ji9#$T=GNI7emmfIpw*to;k~tJ`dW7ta zuIi=)p+*|j7h=it;U0EEP+BDsNk;SZfEg}qkEk&Ek8)KY^rGEKcNY`UMWZX$17i}1 zJLmY<^;C0M)Z3wi0niqBw-}$sZ+hECmnSc6*_gN#J8ixb$Kx)@mmp~$YNe7e5VZMlogQN@aFYU|7}6FI80m_}HoZGP*RILRX0g=Ewo)1kg3}v2 zFjx|}^)9}NpTo|`LfU>qLa=iugP|}{PylCWT)+F$ALU-41f}Wz30_W5+=+ceH$A#t za`Y=$jb-e8Z2=cy+;5{z@IDy3#HKF+jq-*Sc}mO&FoZ?k-uq7)I)$_yT^UX|+6zFH zCJY*%yqTYTyCM_QVeuNdJPDeBwJq6XZxe_1W>b$RWdGMuhi^pxF=!Vy^MkjJGyVsq z>=E&RDiTeJ7*jwOSj-QKL`nbIqhRO5aHn*M_+Ly?s{?=}$TOa_{nHAP?FPXEP1(30 zJVTXaac?)BifQI5^E?65HW;h-vg!dIrNaX+lT|&PixLz3j{#Ut5}SX~oh+RUpp!9iR~})#sdB#h11CWkvNH1ay6GQZ#jZs2-cGCC(3-pe>o7_( zIPjPGp687zk)m@I2M334=lVr=yCQwN@JlZ8G}vi3nc!Rd$zu;Jq{0zOiC%OyJrzIa zFXCIBs2@~HjxY(xBG-5j%=HLb@41}=BW7K#bTZIW^(>x@b-mhOx&QBpuUwA1^9*k` zA9!v8HG?mNeTzxF4^05L2UDP~g7^Ftlp<1oHlcDBmMb4Hq0q@EzNBB!?_f$wp{;O* z78kZR{A$-?W!B|zMIda`Uxu zI*_%93yS=H;?e)AxJ)MS-HF=Y^rZ9P#bAodoG(Y{UNs_W0vUhyOvwLF17>Gv{7DIj zx>18*Upfboln30@kPWO}0Q@xh>J9+!%9VS{ki z@hWE6@Rfk~d7~gOMu-{m7sHpXYUTf@U$C^1SIr0`>=>gQ$M>D7gRP5e@p7Ji{Xc&H z(F1RnPBroUjEtKWcA>jvN$3p#NkF#00{TxswEW@OsGRos>BG8S!QJL&Uv_t(w~l@p z@s3gFZKWdWG6Wy?Hfwj2h{Pp%6dP>+dy~$PhI?qQ*`Gq#i|5hjRNT(_ql8b0RUgD^R+nT!kSiL5^Je5e(D9&E zFX5b2@on6T1jGW#zj@8-~i*L6^B*X3EId$9t0vrb z3A19Mw|bdB6=gY`=I8qD4f~TF&BNg02UM`xkfH1IVU(@orvYz%0FwSE3G4xEur<z zFz9p`Vf&=tkV<&1ZfmMe4mX1*pUX0m3EJ)Eep1hUR;1h~V2Pll7}n~45aCzbDQL)C z2>C(%q(2_+bXML@3ezs=-!|9HfAQ=I!A&A-FsK|rr6MRH_*pa&%0qA%_G+8Qp z7c&hqCALj20qDSRqk^LgIAH_m6NzmhcJzWQc_KtA!roA{yYoyW-;Vy9!&ZB8$A)J2xQr~vYlB-*7W*Bq~j`$OC%Y~1JA zU4rSR)r^;(&3j>*1vKtHC2rYRG$G1@2A6I9aQlH^Z>PdBra&!mL0#zhJTh_5E~QbG z%S~kqHvslL@f#bCP6rJ!K$-^oE%};N67^9%sS|C6;)hU;AXAbeX}spkV6~>$9JO^G zDb~*CD>Cy3H3WSIqlQWPR(o}`5PE>gr)zfk_CVSh_Hz$Px!io^(B-cexf zh5y95iT+xIfgM7#a^ANq&_Nb^C((A}Y_&w`#TMHh?bwPI$gChLbVH1|(6P2lP}HNF zMqGWAf|N_RDm#?0(BX3IO!_K=ucb-1^9Ldk-DlR^2fN5BGbV0r0wOaVYdv2(qSuAR zoQipUmCmRV#YarV409&Ci`RGszHmnod2Fy@Ml$MlW|_-49D z3p|2-5NP@42;e(1cC}TH3U#V7FaKfxfT{8x`NyWz19#m$r;ln~OSw0@%KkK^SA*j_ zoDnD4=HtO9jHf*R1SN_LG#yx+i8sg9K$E}joVI|PJ*3eDL*@O(TXhSCHoxkpPsV)} zi2EAqSIs&2?D|4nCBSIEW4Gj6oSf)?5l3H}~NfxDY;QO(NPmmB#(llouj9 zSBoZ0!jEaseeNW1Wb-O}-#(~B{WZ?{29Xrlj;98ae@3aWyMCQHrwE%12nv3wPL}Bz z6SDIv9bi_gpis^z!s@tXSMj(iuko}|#3T?@3SVZHi;Ck~f@K5I-Duh{ZTFKPM+nVA z&{!zNPpJAV&|HYRD;*jZyj;moVB`jmcBHZ#!K*jJ0{PN!-H__k9=wF(N%RGHGt1Rm zR2@@5F#VK2axHCZazmjf&e-Q?hKn4A@Eu!why&i2NyC2sIk0fl3 zIffQa4!20stHHr8%eIeXi-SeJe(l`M)N%43qh^>9D75Jq=08!ccGQzxk6t33S`Bo+ zb)io`Ua5Yj`|BvQ!-=bL1}(oCc`-8~mmPs`4_*WW5G`lo#Aq9AG6w?VD^WtQH0;Zf z^QU8{w5pIl3yS#CBRTBz$f_N}#tka~JrTExwUe%Eqea>@J_LPJnwQ{<85D`Jx?R^; z_K}ZiKa@4UU?4It0<|&1k`i@k)|8g4j|5#JP@{?bEX%g!O)xC-z5{=<1nwl6zjg6A zeichJ@_;QiFe<)qI$?u?ULvDZ%`Y{Iapn#g6wEhxW&{C26KmZMtq7C0n}G7TdBr3o z(&^z|1B>__2pRo!iCBi2URN$F2^qX6QfWbuW8{9wTLZQFLKWC-6TDrKeka7 zJ1Ep2v&dUh!D3Sbp6Dv9Kr=lAeCOwwpsqgqXlZ3{98n4IsuiL<;9K43h=n-pYMmXr zIYAijDho1($pLcxtJORb-M4yk`V)v0|>MqLHh z!m^q#*ggolS{AJrUZQQ;LS84;=JC0w!o%9?Yg^C*^BK^|S-;0Aj$Y&smA;idTDvK_ z>)@(d2SX~Bj!Oaz4-2AvV3!zU1aBdWZf8uB#9SIw4BX@sFhx>Ibo8B00PAdl&&k&y zfLS;9p0UH6Arf^bVG|{v)gUve-*$+4(SpZ`P4yH@tk}62U_I( z3BYKlKV!RetizAkrW4*~VvdIu;4zS5gg=(b z*eIN^{&Bgg>%T^(DZ7z8Km(*{ZZ=0t09vNqhDB^CX<5Ggw!+iS<6MH$1-`P{Ul?P@ z!g&dN_oGbXd7zhG{SB4u*cnuDAM$6Rr*gc_)e~>R>=2QkvD}w_xeKarkNUfun(}Yc z9}qHp&}mj$D4K=>5gYLFobI@Qi;`M)IV)0Z{1=ww@5VQM}Ntnap9&ogEbpNl3OQE$1Vs=Z{R1+CZ(i%5T?6 zPqZLV=x5e;Ur{Xlt2SlNn2MK5V>qZ6d#KTlKwWVg=}@W@Uw*aeN?cdZZW<>J2uk$? zrZ?!%hD1YlIwNmv1tAuJ6|+y;n79c-$V=ByB$Kx9)k_Ys1tB02Z) z6Z|1hc?a)XNU=<4kjtdsRPh0gfcaZ-$LB((RR`M@!TRrAx;l4rQ}5?*63}3p<}3h% zj?oaYYCy4pG=Z5*h%guIA()_M?_AR0)2mnd*eYdvH=YS>OO4(}kDx8Msqj9?Z@!wW z(5{&i6Iwi-gT`~1l%TniFQKduH(b=-?&lxbNA$idl&46dA}=vB7?hM-_En zsY~WZ5tD#LpK2t<7gFkG;etF2h?9T36@zopSE0<<`g#x`s~41dD5oaJW}-~pD0;(R zu2hng8W}t32JEw699>WSrr`_k|7L+Ta7y#;ga;`A5BBbf;qCOt0au zTN|-sni3}zoZ^U`Z|5{9L_E=x-XLoXavqnfGyuaI^I=cyiTDZxsiyQDl!JMh0oU@7hi8w=$ z{Z@}wVhtLHs+S@SQCkQFWBr)7H-;pVmz|04WU)b0{sa5J>RK9HE3)kXP2Z~5>&($Y zngrlZht072fX~IUiTz?l^a<5ngo|%(aS{j#MLxlzNF^!U{Y`%BfXvP_WRi3gYku*kQ7(!!9lLHF#f@X+fwQxYuAs}qq2_4Dpw|mgUMtcq&f+`;b zp@{6FWiZf*iLgR?%nri_)LMAx3=225)dlE)(xOF}aIOJWe3t?W#lJQ~Z2+d+sy}V4 z5v{(RlaPrefxM?ul-DGw0hKf=V?tv#g;op;^v@Unv8w}n&w;>f8o|RveFL6Yxu0{< zaW1cUFkMu`FAmoc!u?$!H{za&C(%P~SQkGT``*B?4pGvEv`9$;vbTy*oNerI%ocn# zR^^0WJUTXkSsN!G=-R>NQN7V;TRoE>)!xizJT9B4hy+)_#@WlBi~Rs&N5TLW+<0T71IN#l%vM$X+4HfqSSh8wXuj*s3g zB@S6@?U*}u+<>3_dW5*55qV%1 zjhJc3VvMGi6T}!}j_v{%(MoZ*4%a5!@iY83TmZ{J>=|NqbNdgCnHJhM!MJD-`6w%YAlnOTax z#a_P3<;x1Cyt#6%Sw+h%qll6Dse08o@WY{TG?DMtTlt?MQr2v1)_D_uw*$cZ217OS z96-_GAn&Z1LZ%F;%hQR9r2LBU?P8!x(9stmaD|Sf3|QvObMzKw)|8gc)*8R`BxmTB zIA|G%nJY~u1g*K@-s)JiU7fj^&Kj!+NKh#c^-}Oo-ZwR@&U;g{$}H=gG?kk&({yPH zydr8kIK87$P%3CE2N+V(Q+e&oOO%wIrO0VXCB3L#VNP)cT&z}mqcBT|M`O}GK_+=_j`A3FBI_rN>x%L70fD6S`!m%1L75w1frTyZL|6Q5($#rR z5(hN!$=9K&q@;xw*@YHR27$7<(82`(%>L}NjJd%`iN4X1+EQO^{N_8H&YJRY^?;<; zY`Cylp^2#Q=~UMOmjSR*oEbn_>Od41IJ(LunY!RmC}}=7O+ZRH^prxeb|}w8(b%AR zlxk*;L_tBJq!w0MsGve_(E>`05BHg+2}2TP5sj&tquMcnj?(1O`1(dZO`j$$X@xrN)bF+LAdtROiARA$S+o3Fj45I7xvs*-GdQAIRauW#|ME zFcpHO%u;5I<3E{xIN9PbK9V`7Hs_r2mDO5ntu>1s@q)6D;#a( z!-=44%(mM`xBm=w-BKJU#a$(DbhufEfA4wzUOz>-s&DgWwnd4F{Wr0jEwR!{l9+)T zvs}H_>;WD><4W-sv&?FdY)!X5Y3|O+QY6Kaw<3(MoCRIy#xrTkGF7{a=WdtJ{G7Y} z{XFnJ8A)@hSfv$^mYnILC_^fF^?ng>cWH9b2b3WrNoHY!Kjimuub(^RO(tN@r3C5~%Xf3<#zi7pDex$Coei*BCt{b|SDSF9COg+eNz7xna> z;@G;dUXXpS^fIKX)?2EU%aLUgs!xKR_~Z{qlF838 zyJcNBHWMRinl1|@RePLO7G$ikTO`$)=}atjBuPTaxILM!uhRfNu;R1JDC5iS7aE$H zDa1-hmm>^1O9KKHnV7%-{~w>^F5)gFzli@ae)JxeAQ_1lgrk&`?8&2E>7`9xO`exT3p@;PHd!L}2KGqm8V=+Y8IkAB6l+f*k|b zfv5;)}juu|BAL2VeM9B*gOl*KfOiWBn42uw@6}arM zViCfM8J%D>BlGe@^a2EsAE?oem{AHwGb0^FGc$;ZiJ=0<7Y2~ECu+oOD=}erS^nS>v)JAdyZ2&!J!TSf&yHcxt(6$ofS9o;Pd52E z@X4#pZrilSjUFpt!vX=IE3gqPBXuq#9A89Mms)wS0qO|N zf4OTql`xYgfqSAou!g1Vk(cGMGDgbHa>KBl?}{o5vR125Nae*1K$p{HE}kRZXjcja zRtH05qx$Tx&9STzN<-ngBG4SE4j7zGPgVC|3|CZJsF8myfxi6HG}0$>(LNok7p>(; zrjfJ~PwT*=Ios%l?QD0mnWkm;W`P;GE<-bE9M+S4JvuCU${lNgMA@g~f(*{SM+eM>!-MTO*7`4{r(z6xB}9|tqr&qCCDmCn$tAhOp#V&5$(5PUk5puZp$e1C z&z7M4CV*c>nN0vwcBsMWy635OmE6V$>~er$Iy#zwYg^B{ULqM>Q{oy%kro0!sOVkP zO(XnAZA zOGW(LK1|rnv{Tc>XqZm363Z9Mm(Jj+JqUm+)d+-P;-nB zBT{lSFQmAn+|Cg*m(KfQt~@Ov8a`-9)q^;u9KY&%@YG_CIj~k2Qz{zk%1-fEtuW+y zaPjf-eB^Zhf(nXMB5Ys6uZp4>p%+q)ygr_o--Ynibo-(U_y7J^gs*V;M6sEy8Ki*<|lhdF1-GqbXv}?qXH)ITB{cek5LdK z*2s#Hk(-TxTSmz>ms?=*`GnMcpmV_OR5D14ZiQ;?LD@xp5bJB8VuCvrnwn~ZZ$LOX zCuNF$sC2hYbaqyFAPm^#g9%Oo6Hch!1MV^tQCRU<>F}i*AQmQM;`<2X5+xlz7*W%@ zfhl(GDrh%wNdgHPBt%Etu&~1fWXBPxCY)%(+88n728Bluj(`~rF`{W<8TnudA}^1K z5jQ$FJ0p}6PN+yWsT2-OHY`>XPq3a?Vl;$_RL&Bk8K_JnAxteRXi-56OiK?MtDA-l z41g(}Mpz(R4z^nwa`krg;iq9E1FqahJm%t~*p8R}GWA zG8YgC-?r)Qw&_*pbn7K`e8Bnx2LWNCZqF)GuWS|YcW4$U3v87z1l2XgbnNfUpdkYd zD_F~B8r|uvdc6Vz52_X0w;t!W&zXVbe*U}7xN|Z%3>qs4cpktQR-S~`Ek>xC5o*tR zl(wr~wNf8w44t7v&?0Qkwe2F+h(5@h{%s!hLSlxMl$20X#h@&yX3|Xgh3#Oj=wVp& z-g;kpAR)`L@BW^*H$C^Mw-^q#`p@Q<-t_+;gw=W>Q#@+U+4D_$?>Fb|q4WD*p(cI4 zTLo^=2-)sV(Zr>$EW?be!J8kL-R_GX%9!XFaNWJ^LHX@H&(3Y}ow?J!Q@Lr{ap1qe%k%ah(m z>%S5clM;+2=TF2=jMd%tIBxIMjmE zH%3zsApZaVe}?X@xat+LPqf-)uPS<2w%7xSo*#g|$pW!hw@kfw_ommhOGWe|*7|(% zf*^>1DSpyp_JIKQ~7VgUQ>xZpsMM%~KI$_gSDk5W}mhh-_54Wu*0F#6O7#s|T;~_=R z>suX)PNlv)xoC{wqs|-0x zq+h`IXhxf9dL~Ff?+)fXjD9G!kcZK5$Qt^C0mm)(olmSSJ3M`ab~R?O!x0yI>MA4Z zh`B?V1bpy*upzR$`W|l0eXYnWpw%fsL&HB0$dmawCY`3K8J+T{rZcGliUz1MvwWGg~j`f9G;kY-j5SH(cr8v0e*l?y)yDho*c8}o7$@e zoeMd%6x?Q;&grqtMfSH{7c zVvMPYQi0F(vQ=8EQPzoFN6YGR+LW_l$xdRI<`bg`_SBs`pYV3vCdQKh$m zrxfe1rF8!d3<|e;3vuse*7s}mz-*muer|jE_+K@$rSDjWg=^C1d^k>23@(K}gw2ay z(S1}ugUiY*gq_F=Aic5nQ5U^tVLmEnoi#Y`^rq&AHe|6@mfqdYBOvOBAh(O?wEhtP zI0z@bBE#Y;U_eAsgsPFXp!=+6CxuT6U)SBzz&951AL2ADLSNY-jiTM|2?e*%tO`B( z>G;rl=-Ej_YGQ+)Wrz=|KOs*)&0iZ%Q2b-#)?S<1{pnHZiR;H_opp`8T{!AJ6M_%l z#p-}|JMVx6xG6VW5`Xz%1NyGw^J!V4+&D~TFBNr2hZW%ZVcwaVpTtR3Ju7G&FP!b5 z@9;1OZfCqB__O3hM{39)MAjU5q5_{Q_1jZyuB|_W(QP{aB)n#!*^B)eGu{a=3`BqI zxBm3RF^obmf_}FHgsz8MR6L{P)9C6aLvw5(%Ul4}>Cy6XJP*^;wb55OH+P{HVSBB6 z6mFzR4QY*w(0Gl}|7TusLz9|jkCK;CV^>ybv!V=TZ|!0ZT-$TVvPHt?-XMYt${8#K z=mm{P(~4dH!WJ7eam?K!CB|&jCB)T)0HvxJplvrScsL8J#$GQo?;^|nv@I<%@P`5g zJHD6Qz$I!WPUBuf#L{3pJa1mZ`Ah>(m6#NE8Q z#8*hRD=QU186i!3>-$4jiAANw%<|-RC&xi~$dX|7utv){rWL4ZAYy5-!u18C{c6ru zXl8m=)Q1)g`P0-ZUTn6mM+~~sDPW?i-aIbLWOME#RXKQ{uNc>4UEc#op;_hwWxRhpOSMi>>o#LaU z+V>=!-m6!@rxOsRGuV{H#cW9$k~4_*yf!JIuuh*+ek;l}}%YHWWgiYVX| zU?qt2M#3s6`HzPg3o!fmR>8(t?^8bp<{uFcK$UX-j^gi^V46DdWJV}q&^5{HGc~Ts zB1N-jf9qHX@|f0X_%0v-Bhj%(Wmm1KDTC73KS&1$PPf^Q{a9q{uIEQI5}=y>*2C(> zr20S6hXYIPZiB|=A3@T@BO)E)z+y7EUwM07W#KGTd3QU>Lp5a*p%)cctCF|mAg57JOyFjCi3b78_= zf{jzA&)QZWYi(O4wrfcTK}@W2#=7+?X+*1vG!>+WQ0vE*BEE47@9iIle;G zqidvoDt%M0>8nAn^k*6)O(FrucySY-)40Ag=fQIX6?&2AtQTI-twoAdjjF*}SJcl* z?Iqw4OG-yCB00t$>2Pt;Sg6G>-EQ6;;snkmapeWjH*2Y%S5j__qcNWjEhELsdSI$n z&&)9O$a0{bxFOf{sC`?TO8;X;r${A~cSwc+1qeMFK#0L^_F7{_StJS=(t6ltRblyH zGb(_UOzcf5fM1*0`xW%^mxa%0?@F?J(Mu@cAE;&MR|IfH!I&rBZrOn3C;@0XcU$Vq z_b8~prW2$!`6s~ z4DpwcZllz>C*o=)luMr#wPnjWP?Bh-HZnAq;BA!OmDZJ}bg0?L1SjPKfY%_idb^xZ zR*w<1+GJQdXm!QN;37qPwY_{MjOK&-cZiPQ6U%@CC1^@$NLZO%u>Kj^5mTz>6Db0Q zkqHQRxj&dvQB~PbEF5rBw5Cax@+Vd>5QIp@;6atMyvG;>sS7NN$hvcGq5hfJR>jmx=-`II`B93bcIEIsAb-5`fcMG^vgY zdp;j<4S5@oc0@U#M>sOGL!smWrC)fEEqM>p-=*pi;wR_j7cfXX9LwP*783N-5<{}y zP)&{To!tT<5{e3tmYLteca8?qReockGlhZF5rQmfqDlZd7e!S=O%!A;y-P1murul_ z_8t^-Z^gP0fw&F?7R4C92{PcRq*lj0%>jpW3)_?qT-LcmYkQQ1WZHFym4MI%$s3g=BKR?9C zy1%FIRJ9xDBT%j-s$D%yMqEH$^zsU!?> z&<1q~)&J_u%ufz=`H4DpQmC9uYU@g!x|qcvPc>03*yL?c^X0L!hX$zcb@D=opnT`z zYjbc+ONbgd0{WR^b)-OR5cqjxUa+pAB2qv>sJ&wH6@7ET$yE_q2mLPS{QAV1_m&G1K_GMI^-L~dO*fyZ*<5+}(Dqxq$jXRN^lMASIVG{04iXbXh7KshvHjT@_c#5BE%zA<`|?rqNTRR?;< z+btz@XI{))O~ZRx(a#`!PsR0vtv)ialC%#CM6NOaUy?d2{Vx!4-DU%kb%r59C-euo zoMe043QZ0B^;*!|qn4Y%dh~6=U~wJzYf0XUw8M*?^N#ERJpy@P-#I$H{6dmYEn#4O z38g6fVODP`%(pYh5yCE^`5N1nwX+y-pfPb0EI&fq?u{vxmW?9P6xn$6RKCLjOF%D- z+=QS^TJVWZ36l0Mqk3}SiYOwotj%*<9k4aOWiu3nyHwk_6P6=*6Cq~tW(l;Lz{)fC zEmz}TONe1u`L2Z$B=2LFS1K8F6-{eVwzc0vY>d!GN>q!4M#vYm+$Fo4G9B#>u?wW{ zNT|j1&PM>k0>*~`3qk3FJb!w>bafZrq_0~*PD>Sh$W=Ro){1lp^Qmh744FJ`7jDcU zxOc{4FMDzM=av{n#;#FE`kSj7J(<=N{R(C?PV=qs6%_eMn#()hGspew5(gp-KA_KB#}RKISr)GCV*4d$lj3(^Ty0+FA+vVf z;JXMvV=ni&3ja%{S&_Z@|Qr%gfAJG70;Mw0g?YlHWKr= zOF!oZgo9%GhL*;B4#!+3R)Y9sL2_aZ*y;;vHFh#-?Yo>j&s8jkS5Q5s(%M9>@^Jsw zk-56_BXye5_aeTFnv%*5HNJ3GdpLSH#?T*a1P?-Go)YFWH+Rpth#GO(w7*VyE1dM2 zg*_-1(=2s5ghoF~s?t{_c`PIj#{O$@Nsre+XSN85#xQ(B04;;$h?X&7n(V$`i51x0 zqG3OhBWlMZ$PbUxnr{lCv5B(O54l)iM`ymt=;Pv7?@2eE@X>rwf5Q1=+#wD)XPO+OblTVK0leS>AEG&fzW}Gy> z5n7`~smyAZi}v7$vo2zmIMjGg&v<)Z>?l`W!fowwJ=U;NRZNX}cCiN9&3qd`aj*~s z|LV2Z2n~ItY2E0KBN32TlNtl{RCyU`rTN}G;WK#0C0WS91Eo4RnH)T)5Zty|P^=Es z6e5Qb7G+Z|gYdsvwCtu`UD03NUW%pTf#^9_0 zPTkuQ<{pDFZihHD3AIe0Xt<6PaXZv~=_+YScGD-7i&IU4mqXfVA8q$Y>4?N>(5I)D z$yQ91IvP536Q;Flp%Y-5Cvf7w>kDtQq%MHlXp#R2%wS#Wi6k5zUwr~?h2f_)6@kvY zWtrC-uULC0+IQI`Zr->~QxkZuqL5T`vrhA6Xb&9ioQu;7P$@cYM{boZN9U}1AcDuM z2IFbdGJk|#D*c~a>5#i+1f(Z47<8f@auSs-RSSY$e<~7CfB3t^n)CdvqL-@ya(N}9 zJ$2X+uqb_lPD|7@-5pK6fYDf>;@JxMXsqqBa-S>JqX*5PwW9}@wYi4Il_&QvRkU5D zW~OwsP*ka%vVRY@^t*u&-d%=s}yKgmIZ+YAdLY*gEkDr zd`AnJX3OWkK*S+EN7H-mqI`h%6Cu8aEtTw%MP(-; zDM5wC_TxNR-GsWh<7g42IJB~gTVeD{E9+uxZE(hk_0eDcLPYq^YrFmK*&xfn75$Z& zrOWM*({QXuu(gSmD=P3z4g-2+$-%JGvI|UTGk(PLXD>Iyv#^F>gou24J#>f{T}6r6 zPoHg-qIcRU`A=eM)L^rU*dx28-17e#7fJoV@4Jf!*=q~eCi9D2^tO@<&|8W<2e8^t zYYCYr#R@BzKvUF>VoR%F@@O>!$6` z%Ny6nxW!u|KtPnC|;?O(4}?#ODX+wdV?j=ug z;%vk1N+!&&^r4bxmV~-e&MX5OixRju-p*HwXYx8JCyJ-O))5ypX3)TZgc};rlY{Oq z0^ZqWFgxKpaOVz}fYP6{WnshFn(ALUC5qt##YI%&;tmf%guLElkWqf$jUG_Q;6r2Y zT0b+jE&+TASX$xi#(B z+(-fR!Rpc-Ih`GkuamEwK}ZtqY|}G&hptaDXi{-y0>6ENXYi!h+_$}ESXA*dNdOx&4fAz_=D4d?9C zdk=hoO2G$MNsPu0zR*_A=}fwmc)^wRSdG@Q4jz}O;0Whm{x)Y3`(Ouh!II4C!x=QW zk77zyOE}y#ywHQRr+0c7bZ`I_7755p!QQoSDQ;X*5TNfc8wL@oG2p9`AHqXU6U4o0%fl_RE2|`xHRQ_wP4lCF2C=)&L<60&rdmpKVcnnGaC!p z*bxTvJ9%JItHV7WDfRC)CR~Z2Ltw$Q@s0tSBS?1NLZLQd84?|s*7gSPvNmQ+oy1S< zd0|+aH+3Nq)*aTqWJsl%Svqc|frj0pQUEC7f+j+*^+kPJE3>29zFVj4=E6Eb8=171 zg~Ar`)3s#DD{xI!w!Ub!y<-_nU|E*Z%omfeIYCij=P1f`?NB|$An)P<1UZ!5xm5~W zDs?1^?nq+No@-!d-FSOUr33-Mo_}2~kftFyVDVns; z2-Kh0Nf;Y>vTBb%*(QVgu~U0J07~9>s)>|@pvrh2>}8V8IB(ZX3U`77YWV0i@7H+v z_Vw9C<$mBctwVgS%~RDw6+)(`u_5~AiYd4#r#U`{q6droNBqH@X(uN2;G~rwe$!cf zjYA7klfw~P;DBCDV#7T~iVO2Jgm$?XtC#_q5kZQEVlTQkYWR$gTKzCN1Jn{V{S^{C zqxHwI5zMD>6D4WG)z4zGQIrR#?2vguVe%?MUF8fh>64|U-EF)gLRhLkKKwZQ&Z|o* zWy$W`Pj}M@~6(%jXG|?p$IYD zWvoB=0Jk09T0CouVgp-F7DPpWfG=az?YtRG)TjqaA#6f0HUxGSr09qQ;>VPT#*(sc z$TID{MrdZjvlbvS@x>HbN>IRI)=2Cj(%0NPPn!?agqco(On=1qwxmTcp`|BLXVTEH zagXHJ6ZRLn4j0@?a8Bg9aWNozEr%Fx*p$5rJivHACtDwP{Ih%{mH9yhLqJ`X{~GzD z3Lp;pM?{!>H!`G&s>{rgpBV>`0USiaE^TUaE)8sJSc9A#hA|Ta9*q^9luF8e*V4@G ze+&$wtl?nColXpilQwx2+VHan1wPP;whx`Z4X77MnW%1w(lte;&f_;H)qtDNnhfYL z{2FdW#JQhj91rUWY$|C!BeO4%8?JDK2F*~hIcu_jLp02%v?>gpz|9lqPv*4Ptx1!% zEw#|^`8)yWe5lHde#VI4_PT6WJ@3L-MJ!zOR1QndgRIW|Zt18;0T3$AMDSjh36e)V zVm9lu+{aEo;%CB3HV|6)jG5#Y#S z92Y$0^G{dECxi}=#|8ueeL%Xz_G>ySII7qL7!mDR^BV=YEV>LAQ(iFQMPb`pVfR&b z?-Sh(B+K3r3U#BQU#X;XX7ndYs~mDSqa0!Oz(0Ub+4H}m@A3{lUsS&v-~@VY=; z;%Z)oU;U^~kr!l;TBc|=;jfTajv|f-(+}aHLUvxnV0d{L3c?>qoiqoD;mNfBmG(9K zWwCg}-@>i~bmIruthS2QU#$H3URgH}!KBiG$!99D&j6s0TwW;5SO0H9xL!X>60u2Hlx`0Ru1jo0?@uMK zKaZ92x(U}*GK)z;2o)Rd90wd|X8(-KBKWCOqY`CInJ?t`VUC%P!FGsI{B zwy|lcnYK{7$p)RoKP?wW&`BaeFiKB<@kZz>@G*1{D>RihmSH~dPo_9ada1W{OuQYD z3$4%O<}HGK4p2-CIt|!hhr8e49gAoEI4SoxNond2geVuz%8f`sx;+pW)M`Zbx8_4O z*v;*xm!?=)1c;K{Fv50-Y2PT7)2zo^af)K#q$am(Sl9%A z%Bd{yKdr__V95-9(y}Wt1ze>SPF{Md$gTnF#cGRTz12nKuYGlxVt);K=iuY zMAsaOyB>64>bjxl;>4T*!hKP-*I7Km%dd~cbBuKPzbsZvD6O~s2BFXUqifgEF-Q_HMrJ)K8bzkj; zw}ZA6*H%L6CoMk!W=UZ=YWLWftwA3I7W9x`s1RY7W-H;8EQ5Z0^j}l^uf1|>$jxt8YXiuV%WGEqS#N{*eodxg4Zw_R@e49kQ2kbO9#mqzeDOFd5uhk?_e3n*UT8;teE?Usb|K4ED73GTm6JFI_M zgjRrb61v{Er$ z)Stu&Z;Q@^UF$!zUn_tSOQ(cwjYuMba@2P0d*HEDqF8k>ydbNZuo|JB5+{Yti8CzH zCX(!q19#*fMvWa-Mp%~bX;Bex)1fO9_(?$k5q1p_DtJ2_AW}!F37nHS`$y_#p2mRTNlp|P5~^31nq;+$sy$4lW-SNK+)l%ePhx!5Lq13T#0&{9UNmZ@ zJ(k#aw2I108ppy*kxFsGS*5p!_}*?1f4ebwqMGX@2QYC>VH1`bqs}R}u1h{U#_R2k zr7W9zSu*SZZwS#Tq{dDnE+|H)fZ3(n%J_8lyVb*6S!=qS-b(}X6PLr+)e6QWYHKkH zpHr$L1~UbpKe3Rr3p~`b1T~UJw!ODl0V1yfSJVL@n4Y-fPmtM{jR&O4a7!a_q(TKT zA6q&HQ3yp!0(ya&WL{vuBYn7B2 z^IWGMU111*OCFU&@hc!Voa0m^TN%r8pF63!r+x)6`(ZLd2<7-89Mgof2=ppBOtW1z z_C&;INS50--76fCW=Gx!R%5uw!=LDFreoOYLvwamEAGwQj?bhqsM?+5*Z~$ayw<5O z5$fHN6qpoHGvm5>gJ&)%IC+ZKS|N_UHl2R2;ZzbJf4uPZtifC=;z&nWdm`DO_|ov; zoj13|Ky)YcM<8#Ao^+2qtG!rHnlK3t@JAMX+5d~ttKb~8%790J9yHEzcE#&hK?A3Y ziU)OXCOWv;KyUYZiP5CKYS1`QQ0XiG&q+v~>uDkm-h@u$rCWMiWkF`X!cJjUH@go- zra#nQ_`bi4-W(|37E+$7&F-VD*v$zAjZzV~7jM=lIYz%yP^p2Is-ZIA$=|fWMU$?1 z?f^TPc#C7$^auVU0Jku_%FhXA*p(*l6qu6!EX>}dR$*A^Z(QQC8r3N#eH+OSMi@Qi zk(Gj6%0~lEB~dN;tqyJ~Z2mBRB}}LSFDT9lR~m>;Oi-R@!?Mre^|usS+0S0={w8g& zdE@dl-;l_Ks9bu2z@tMIftm}}2ul=@w|7`0!@T7p^l&;v#dFc$lh<)%b-7qO@~M9x z)wEKoYlgR1;?x7;KAX;=SeApQMKHP0&Jc>wHQdO$LLgEdO>g9y8yBttU%BC z=;wt$%>kI$dU9Sr4%%RfS>>KWoV^03g!PLVGM5c}i3c)+V+Br={Z&)JC-cFr?-P8s zUuG@+i=7i};`jB55*IT5sji%iL2Ve>eJf=a)pFa>4VoZy#vyj{>`e3#*-gG}S@(P1 zyU?)(>lvR(ja3k(;O9~^<(uz$5;ElZqDLW(F{7|XO&xRu8&QWd1HFz8E$>Zoy(my^ zOk_SRjv?-YWs)-ih%trbF|w2$zy>FfM&{rd#`sVUw3Y)Q#g6nT*|TVQM4F{h0{~Y* zsJ|aSX$ydi;FSg_gd5CDaAbI#K*S{{gA}P%9av0^js^qa=v5)OuM8{1G4kx~+%E6N z1l%BJ>8z{6OOm4uCW7<#v|e!+l9v&v#jqRV8H3`r317Z1SW^Boi$jOj8o(s*PfGPO zDWe2iDU|?n#2hcEnA;jUX8}iJZdHet_5Kee3Xth z(-T5VnC!Qa(9IxeXJ@W6m~Auhgcd8DhCo5p(;t!|+6gp1#WCth^dKXnTSgI=G2kHo z@m(H86B(wMuogQb$TQji^X5d?R>^>+d-6!aWb*!KvLPc-P%ahb>bcMoCp=#L{I8=U z$fFx|<{k(j`b7gxwKCLd7QkdYuNMCFyW5uzicv{@!Xf=8a!$&-8yM$CznrY=@$B9) zy0)Nwg_)5C3l-CeiNvf3X02N1(-1|OLB*DpM+E8_)bINeb8O!NE2hN1htSXf?knC& zenrmaxp9xDkii+faQD8%ug^Lx*C&?;%!Y^&=!;RfjzYs1m4zU&eEX{MLAA-wj;$0_ z9q!mz(LF|@Qot_GalbOAtgCtP{1I82MxrDK@0#L#ryK;gd`AS_3w;=k?+bD!J$b$C z{=#Z^B+nlP0cDKqi9c$#W<%2J)%HiyTo)-;k4XYd`yYLao9c&9+nNJ_AfYViQa=TKVL-)8Aq`B}Ac)0oVQbdtd_&j~=l=KijtzbxzmSWlZ<(MXUv%{Zc3uF2Nx z*lUP@_Z1#&aDDT0oi!QN5BYN!8YuOZfnCdt3Y@LB9SmK_I%`g4&A2IB&VC-Vn>Kg! zQjs&~i7q{&gb|T3phP(yT#zowwnG#A5q&h~*K9=dgC~>W460kGH2Rj^KQZg<{cl44 z%v?50Bh3&#I}ql?gcC*g3geQO?rNm*-Bx!Li!ajJOym#hEeyf(R(_eMUF6!gw<8(& zQv8OCtX}k9qFS%Q@_35&CmHu(2PMzWz^Q;O9!%}uH#;JgXj9D9fAU&4vTP26ZxTd` zqw%E=_7KtqCpY&HKv5O2IySjOS}>_G7196)M!j6ouK&Zkes4ed+MTFd@Y4x0{y}Ny z;p$o-a+S&b??@X_5)jm8{6zSk%iO1>dMFCL3{=v05~t8egWh;!-BB(iS#y@z=)v>ZUb~WkV}WZ%EwyC@_e$jU zru{U&dDjekd~gBrWj9!=J$@7kyt{&tCR=RIAE=M76IHW-@chPaB6J?5nS=it_xQEF znj?ZeuOYsa>BohS?*%hU5i!Ro*^%ICj;jyAA)SS%kg+F?@~-1&@?jKW4u0DMN(Q={f^ro%zgbO zeK3Prc`f6M0$I#s2>kH0mF^n1VInr{+&Po-$@HD3P%#|oGJThPiBW8p1UurmKL5p~ zWKh%QT+QTfGA8x?tPog%5+Nm)_C=*_AjNqRO zj~H4u(l%zutWh9bDPMbK3&KREjtJ|~0JDj%%H+j)3S)&&Kk-CZj3{h&#ViV!GJF}E z-9c7_xK<{5#Lw-@_B6+bVa(Nn3PmmLz-`UiDSPSe{WRZYwz+Jt=_`8&Yzo zy&Ltd5*zI{-$XX+N8QWfwevb%!A7UlY`O{))!_Xcg-6spigjBL}yFjsO(VWBJexL`_tS zhdF```|?&15k+r>2r?&x9~lTF$(lZ}C7|UAcHLu}aK(cu?hX&*Xt$`;!o6dGou=e@ zzrSja04m3IK;5ZkGDeg4OLdmvZ9g^+>Q?DX;1UA**ZW$-xWQaXgnVepp7~(whvJMk zLV1lkF%i(iAu^m29UcnD29g$z7@od033LIwi=ye{`IV+0g4E)yG z&u$9Sj5{qcc3HlmRV{khe6Mul{6y%QsB^>}dWS^hwuS+GMn_}u{C zGlY4MIkfN=Gpbk4aiR8L;Pouk6^Y1>=~pcS#l>wiW$t!5sDmiaE&~{3Q+kR1*XCkt z=bHE>qWm**oKQvO0J5HnARR_f#B_QDtNAO*RTLEN@kKT$kin~r-b|*{l3p+tgC0ug z+`^KHkc92=i%&iuBKg;bW&e(*Hz8PRR#JSDR*cnxj4d)k*2@Ygg$gL`^ben|#1&`e z;Hu806|}o;fSExUV0z?KMAKdK1JoDeGt&9JxU3hN-Fzx5<8(~`Ytl{vSd6&tY30{S zbzJ$5p``Fqfz3@|4c`9!-p{HQSs4icag3iwrY;gs8StpVL~v}DwMp^Ob>Sg_Ed>fm z=|Ipc$+vzTP~icwvK03T^uVa=iyV`#uz}|<{}=<**5a&{SVuohJ@EZb20(6w`y=lx zgz_jidE#>|!ywwZz8GhalfRn(4;ekviD}Ed#}8OY%*4Dg{)fLakQy-QefEh5^9FpI z$;!ui6m(v>n>Q-VN&@pfc~z;@%xf)C-J#*~`uqCxVDeRZC*AjPH>AL+WS%mL^-DMx z>P--xA?|;cczJ+=>z?JK7iFM+NdF9Lr?Y~b!#Dqo`|1UN7f(+HPji}z&K!&8mnjZ< zVk@u4h4!;It;uGi34+HhAA|5lo-*ve3HPm%-U-)wG82jnab^)=vhbO8WY2o1Cr%<;J@0Z2K}-=#1FLj{b4SG% z@0FdJa1FlVK5R()DTM0*3+Y&-yh|YlGElBTUl2|-=+Kjau89Soq8DMjwZBWQM9yeH za{mKU_t7-Q$5o<2OpoYl($hVj6q}0y#K-|k{VX_AKv)(x5nr<2#6kwYF}#ziO`2tf zMP~8Lv$WwaIDn|<`}5(VQr)b<@eRcmUg|H%zE`lb+sk!j@I4AM^@q#KFywOVDc+aX zE4#b2vUf@;lMI9dfCFj+Yl5wt<1Ji@o>ON~C}%3D@L7>mH6wK& zbKW}ZJ&t16T5C<_+Lm1F5&)a4V6Djw&du$Ls#fKSq#Bi@wR>{!z4_i`C&%6Pajasg zO3B#!=Dc^lH*Yf1%RW;Kv(~yj9C9=YBQ>{;r;5)0j@zJ; z%#e0R8r>mEDXg%4*Sj~Tx!PIl3icMc=0~Ey^%@Xqpa|-GZ#U@2%cF(-;`|NIp85vY z{7DyDuvMgDag|i1b zV=ND(7fPfT+NmKK$d%wsAuCb+UhV*#L%amV5fgUwIl5}}niM^a2u7ctCV49xw-vjd z)9gqmOgh){B>^Y`AjN9Yb%QX-!H8l5Ojz;V<-CBAEjDB37dcWxQmaKN0V`3&O)%Id z3XW=GcZP;ig>}bNQfF2MVwvy-XKz{h?UtUEO_OD^SeDi1AW_oPsp(an+~W8_AV;)O zfGCS)24ool0`d$wLT~_~{=-UYnYoOod83=BHOe1RMk%8$KumdAzGb=hCbiUP7=m&_ z=li_@dif${#t0M?R2iZ&Lu97RkO>l@VL{|3>Sphc8JXN4>ON4Cms!-F$ji@he^Q1# z9P@jB_;AB!?ei^)F-v25VuEdA@BzC}dAa$7!!xjwrW@}K(-Jc-2CA3cciMk@7t{0M zTYF5ObcL{RNISXcfPh5Oc zQfF#5)Y`;dU?^O6P&-(uiw}kZM}dL3f|uB^>5|(5WW#1l4=w_WBtEP}vx=<*bvUCT zsG1h5_>7d)6)nyQumF2hH6>=W;FP#UX|V;7=+&Mek*k@}UM6bM^--&$2~Q2wk?m-R zfR@mKwz2&PIy6Ay2c=N$w}-Jalv>IYQ?4kBEelW#WcC;?rfR40l_j6ceWWQ^jMSMQ zJf;aa3NtVnCg3N+z)?=XXv)CADWMW@5DIt+FmMp2l7h9sX?ib~j(kT)M^U(+4$4lK z8{VMIc)6aLU>GH$#*hKRV9NZ=EIARvVtie=B;loeQnf=6{(?kwM1=Hr7&YvZ?%;LRJn}CyE2pN4F4Vu2ZO-s^v+6au=}DkYHyw+|(9hr7VjMv+PrfuCqz5 z2nQQ%#Y)Ho=!2PnW`IUQ-EYg(lHn{S;@waL?&&v2OO~DZNn9n4c}w=r0~~GCDTyy@ z0f0%~yeD^W(7E!SV{FWOk}rzXtTMc(**kAjB}!hNwI2F2kZ~NxaZJ1NhcS{N9+Eh= z#k?f(ly3!Wq-U|17KDukMErwlnv%3Xh>v30Qe5#$A9y>Ah;v`-Tz#y#{C);G+X8#RsxjvN10k-R_EH)^0c{-WbQdv-UfG ziD^xEG+-@FO^s-yWMMa;enc!X2Yij#0W1J99@scp1SY*^=$p_SS|O!H^tc0-OR-2Crd(4zcH54%MW9w-{w2S_dM- zJ#^zkzg{oJp?78*x*A1XE|X8?3_{Zui^+@xy#YTYne_)Ap5Lm>MVm+2hQA6YH@~+Sngn`j~WzhNzF=)>4$tO$wfNJ=6xiq=;e~gRN}_RnkqWKn=1p7 zpePce)P^LLsJGS-WLfq|7FAj2cn#GXn<$>yR5@=s-|Z6>P0a`#)NYFeJ3BF2QmE68 zyPP&v*;a%B!+pKjFF7=CG5wBDWRZ*ZF!xN;&PvUo zXl`4ye4x8Px))-hs0Yd{G^goKO;#)9&B5}Shmf3omaXf$u4iuf>|$3xd=EHccZBY} zVI_QR=#0P_@vGh#zZXjn3(`x0>s2AB71Tn7Ak^ddAecv4E#GQ+m@BtJthrVoQ&o2l zQyyl~5^Q#PJKZ%9&NNGRk6EEbU8H z$wJ9->?}W`zT~X(EYGu}-aO4r<`W4;_qqzH+h_4qTO~9_OIF`)%PPHYdydZAbB|4{ z=yQ5QTK(l2$t3Q0&cm200{8N$jg~?Ph;f<$@y$=3IZkf@NN>gNXjwkMcW;6A7Ih6C zzP$%{MV&pl#Az2SisLxO#j%yRij5bv*?tjkOck$8k9XrdBJaAp+R1jmZ9~>=)>;3E z#}IM#!_km-+sBuO3vX6=yb#pi{Dwf2Lc|ToBf|ERpdoP?V)wUvFCU56x6?(z#XO$W zm*Nf4L<8z#D^HjV?q(7WOQ}u5@iywZxA9S=FQ`2q+#+^9epPM&3D6e^^L?;XG5A*VTuz}ILMA;-c@Teg1 zHfPn3;q(5L{H1&=zliVTGyN)`%C}|zk$%qUPx(Lmqz^?bNuV$2d-^@?pmt0>CS8** zx0J&r;fjumZbmm7k!>~v&#d5YHM|-MtAtWiQd2-+(C7O-JwfeuJtn3moo*?YOTyub zj*4zZHye>{HdLEg)xOp6YACD{N>NEoX&bh$+n>mn@~5!>3DnOw`&WL`xAOlGPgPe| zp9q%f+rE>(!{I#)>i{lO<-5xx|jDQdb)|NoLkY9ngT%qn;q-R%GNOgy#E(`%sDzyR^ zy_~>uLDIb@h`!hB?l~xJsjI~5VQwG<=+s<@ErDeAhv<@|kC)|=IS9|L=OIUu3$@5r z4^2Jw?J~Mr(1Mrh^yih)bLxAfW}BXUpJNs?ilS&nJF0uvI+XP~zp0O*syxEmoARhx zwyLV=PF7KtWm}gI;;oBk9jmgc+EP)?a-o4dZTlkIwk=~W51;X+$pUbjRc9 z)|YLL+Vb$2>#BNH_3gU))3y4QHCrCluRPj>@p#UoswO{k8)HY^X=B8A!og^_mD<8^ zUVQd$tJLk$g4ct$aCtnIWm(ORCd;Db=mHtfD{h`0Wm(Odrb)rsYPDLeY^J8^&DGV) zG8;8!SbxLeEZ2WNA1hH-dWVl zncbJ2+(1e{mgMMzA+!mAg}kKaJbNBO`$yL$3)$su?_#=Ryl7r7du2uf6a_#8z|Ci< z$L9Wa=7I41*jXWl4;>S7N`NuK3lU-0jG@R zWBO8ujE%6)a&&V}W(s&8dj_(XrxlD{^xZ78_*YL)&+h4QdwP0$T-SA7_vN~-`+B`} ztldtT88~k{-se{f!-}UT+^c_H*A5SljF;tqUX;hTa@Gpy%xZc3j;8>1xmqbJW3Z+e zxLhb}HNDUA-t<0g>svnFd)4>!yw)}6c6dDLy595P;mNOZU0<*E^?J4KwtD&g%JFSG zx>(zGZQHh8+19r@-g_QC>)0a!Y~L25DGvU2A~*#01hw!F~bZ~s*oc9(2&SC zPl)*v+p|%98wsLU(d3*imE|%Gb1GNZj%p1-*^D6dF5nLNcj!_U|5XPF3mnBMBj5HKC9;zI{wNsV^OENiVhN7y&B69@ze z^$_LuD3ZBh*rO;NWNs!X^*E*3xsp6{2aqwk1;nCgR8PE(i?rQqS6XH&w0EgvwOPuR zL+R6wbd;k|G=LV!82O8HwPaaM%taJUgaWv$ik_k85v;hdGB(=%>4tVqQ3U#cjDF

ow&vPzM%`N3UB*{G~1na$ADW^^C0XsSyROc=@eI4v_(a(*|-I$xarwp92fQ5F)UBA4}_AFAeuM6UW04S~N`BYEZ zL;@x@)x-jgRBcqV3?hB?jL845{5(x^2kR;8*B`=$beu%6ixt&pUB73sK3PpoJ$x7h z{xgr&=H+UPw`4=+$U(HJQ~)qIkMTdV@}k0R2NQ?0X`U9?QecRW@^Y+;zxFIMZ^s}C zUP(ABvw{oiv74vsfXj7_NSYfAqJ~muN7%x7(l$%!xo(jM8bPuT!?t~285feNL}H#L zah9*#GL4n4FHW0E$*woK1J(ZnYLB1GyfuCCmNJv&NYm+pgtcTQrl2vMe{$ z7Bw<4J&sZOWGxoqr|1-MsKW*Nw>nW1iu5X`joJ|J(GuLs>ZZ zkh%O+rLi0bL%Fo&0|%lzbkM*sh!ylGXoX`^(Y34irXF@LFZPH;_|7EW_E3=!lXbF( zpg)_HQ|Ti^XML#*2w{0Ps zuvs@Hi1c|@>B4xNck$17-W#oiOwPQ35pQy;&G+(G0w~Q zi;E&UErc!$+mLu&iblM)SuB5Y7lVH|d45v~N6VNsXQN`WgvbH7`$YU*NBHCgG#UPd zWmHhl0e)j2j<%z_x)>wJ^c^K*SOo`8>MD7^z1w^~zUe2olg@tp%Xi>x7viO`Lr8d1 zAa)UccLb}XikGZYhU~oK!z@B%*jvefp?Dd49UtE_eV1XJURU}ck^mnBp#h@uXI z{H2Zp(9<&buTR-TU?38$4>@^2dB_c@Li?Lwg$nl!S$G-0K?7RR!x+mWcfJmkMG-kL z{g~zey)|`;>KgxYOS`>|%8A2F+jlO1q=#}9Cax;r?h*V82rB_Sl)H!&t|IoF@F;Jh z^aN9t+DWI?2Z9S@FQFZTB|@g~<{jN~4v+E=y=PUG#OstoA&RpG^@)X?w_X_q=@&d2 z>_=4zwkShuk+C;06-%6*d$+8BT=#OS!8Uk<(twdc!*kGZ7B&2-hGW-urY_OikZe`| z5`Bq(KliKCkjY)MVpW8P2&*1sku) zLSO)BfC>c+(zjNxLCb}Kv7q>(n~t%J^Q)S#PRULv`fR2OqGNCGHP`8{K(n1VOM73E z&oHhP!3%NJy|Yn>unODgn8SKVN}^=d4ZpxYbVQp&N6)o<+7%E{K0;^D zjR!vLJpRxPTM}p?B*)Xwn#UIPa+qM}X)8Xu*it6E(eaG)$_s8eU_2-L12te>0oR|> zI5wOtMO1%J6*#Ve@2|ROKkc{Sj}Ee=@uZYCL>uLRNq_HCiPK75dkt;eR>{C*08~2L zn*k=7={^f&t&C1Zfxex-k+5IjUz;%tg3}jZcKz=AiQ2>?)#eDf;yj0o`@z+y`zP=hp$cMb9?t2o)gker*C%8u0fxEuHE> zapXkmAAA>;gxlQ)|MVlk3YS65JCDc>0~7gsa>R!$?eHdF*hYH_Y8_v@Z?&H`l}`yN zOb~J+A-^iGrqo_G7a0AvD)(y4fMJhFh2G6cE^)qDBN!|@=JmBs1FiO5Uu;#}VUi~k zxO@NtU;M-dZwB+3uypF@B7<*|)>MJpAtL)9o>-T!Yt+^oqpqe3E=SLvd^7&;qx-#1UUJ?p1c8}rl=_;ypvL;A5M2yBzT0@c?04DxGs5x?&|I4!y{<@E z7sw&B(m84Vbt#Txw=Y~Z77;WwII10?Kj0~%1lt?du@t2a^Kf*Z`7dHRVQXwT%3%6e zeiOQlrVSi*w9Hcc^wVlR=64UmKQR6}Q75Gg&6ekPaGP!DE#R7Kwf?Dp z)+zL-VY$c5JQWtqies;)f{WCsx8&i~52L8?T6V$a^o^5J z3Fih^96<_aj;^1$#GlJ69;7CY2YjLldE%ESW5z^r{){-qxa{TCNqjHG8ZZ|tv4RHT zN#VAbvP6NqcS8vkRV|bHOKxP;G5{!4W6t+5^=^aolxT~$Q}N08(G*YW#eoo*^BMv# zKUbqI79EhqlYuG6rq1bibEon#oskv2gg~3(c8%--aw-xKD5m${YY8O{dzPuR|rvg za}}zfjFv>NsJRI!M8GaY(Ua^IN}zXbhv3~RBc(hPS8lnzih1=%TpaM@Jt2-T+m79g zR)buVpB;i_t)^|2h+}ap9ldsuA&}7KfE6M)Urz5@W@#f4bxlJnW$g2s$pH^y4?apX zdN1w&79Z+^*XTr9UHCzxrdrMeBAR1BC_>Yxn#hWPTog@yj?rYnwBrWBj@`v>)TwcS zQ(YN>&H1Jh*T&D3a>r7$ID_X8OqR{_eu+0@ps{=kLL6I4H^~?df1tfKPH#i{kxtSf zgt7&*fK1=kYL~7O#S6 zJ(N^I#H+68Hxce##By(3cp(NuS$8@GWUbx?UaqP7B$&e4MdvU&4a-C0xHeQXR_CFrg%W|}tq4iQIb0QxUg} zP{8j*p14$CRl)qZ1+u^17r~FbrH>43Dox+`Ib!^h(#rZ&rNYDHpmbi3KdiCDSNL}D z76A*@Kxl)}-6gT0I&T};S0Q;)fU)DDZqPeUyLa#uz;q8arUk*BB+`WqfbKWq#vrg+ zT~`H6H#9zK46?AJ(S9Gw8v>QrW%lb)P+h$X0ObHnjRN->GyrD{VqIa?sBf7amqFKT z!adNv1Cy3KDuHeRvd>xshlXD#7%k<7=o5*OkzZJF3abWI>7i;{*|a z-B_G#a4)Zb$bLifl%%)L%BB}*a5oC?B%uFyzmK}k%fBFC+CaS>*ZoTU*GN1G;rVd& zHr&!gQejP_SKKO$AsO8&K55%^-Q{;^N4>K06&^GkZoZlpIn3G)lHaiU)~s6Pq40=* zSgsJjeo$p~1vU(5@iB+=raY*yG}IOKB{T5>k5GbAM(=(`+sM+Y|^ zpg9=$I0Q+dUD;*joOV)+t`C46ic^ zMVf%z*1{94T@E?=MLSk^30?-#8Wlq~atk%jrD24cx9c(Aia_h})Ahp%8cXb0r;6|m z^~1lb=w%1BiSL5NAfsBOlBb=Uas$tN%+UvRL(cT@lxZjs*#jTfXAu9AQ9{us zy=>eD9=&np-zEVaN`aS+T!ekwq%>g;W(|{+HmrX5m>*R|7ozEzS%&$jFwc z-}q={Z@Z}`-^!05CY}%|kE?=L%FieDZ<*|>p?!Q53i#bFU_Mk|^5FhA@MDvZV8Ft@ z<8ixmdEEL#oIR;$MSmni%v$s2GDy*EK5Hs+p1_q0)+RVX-6}czrT-O%kZ>m$_%;~P zrttgI9`8I<2G_D<-_Y+qbd5g^26g-4oZY7;iNU^D_aIsuAwT}w7a34F*Zf_mSbmuO z+CvF%!Og%sNJDfz3G(dW8c?bnlQ7|RT~Cpf8KyNMOy(iM1yar3IDbd1G0KSMWFd>Bl#*0nlBMhgVX#f~i&$*t^i~*_ zQZD6~U11f_8h7r?2+YqoGtOU723+<~XO;V)0Ll@thS+FrAui0U5u!zTwgRjn?vu>s z+BZxPO4;Dhe!LZ=C1i4A7M(_Z0JcGO!f%ZsD^}c+BbYLG$@sS9jJod$$z*}~x5pR*Ca!^jYd z*nqctl?s8VTO)Xg((uk1MC1GuC`!cS%P2fl1YF@^St=%LiOTA*YzZ=}!p^Sn>;x~D z+ZM4ICupOr=ZY+2>((c9%ISEn!QEGqyHW~kEL(?UNw8TJI!l31mNYgK$r@HBZ!BOF zF3?sP&y`q&t*u`0lp%*Nwy+D90^qTDlz%Ss*3DM!u}s#Hb%L{;>{tIFghKoT2$sN z1Vy=f;J{VdcHp43c6+fS)iY&L&XKtZV)H({IAKU~Z}nTW!6=rcJMYYmoP1J?Wgk{d z?3sZx=p8Fi^7&|sAavCGi-+y7s22>|W2de{eN1OoUMPe>Dn_qMycdGio#P&P+*s{C zZgBlK4XYZZ_lT*CAYXwHI$Twoo?ae%oEy`I#_DWz%1hY_JkB4zj4`hQsCS9@;}G6% zTUnwExHOJU6o$koO!pUFs3RK^%mW?>K-Ie%*oIv9r=?d|2vQ;Tq;I`HSEwknS!xjY zRI2jL&jCUdpWi=1P6D_Y(ZWKqMgQ4npg(OCxI99h*X@5!_0cUq4G^=GK`RiP!|G1B zNY~pp+XLJev{wytfiTX0A@IFYvB=rs1yA~MMYDMlqfj27C}+NJG$ziLp_`Z;ocTjj zp+ih7vs@Q*J|1pe4n%ja%*sHwqz%PCZ{}KVqnK^0*9%YAX{XMoPR*5 z%G~v|H;u8gQ<|OJvOs&|3ODuwr0b_YbZ?|m4*GQzt)qmZq5*8U747%|21Oqg3PDrJ z9UmCWV#34<8D5#kasCwhT0jtBJSTTs^*vPwM_dZ>5T^Nh5QNP?6yoOejrZ-1OQy?~ zo-_ZyMCV{&3U{|*GAc(CI_uh?03zH8`g)M9RoVF5%fh^6-2K5|33)V_?AW+c9x8j_b5mkeoa!$X~dEO4!vY$=SPUIK|+{-_hyWX}2^<$|CR zBld>qXZLZ(M$47CMfp!K;w@GRzmB1}0H@&W(poG#YO7?_j5E#aD2wXFDo0^rLu3ti zy^;9j+(&OProxYe5~L{A zWr(c5&mm~o%sNTFN#&jqrdm9wnR1Pn2a#d9zSr5@SYk{%^=mzd$}Q#FwTU0i1{#h; z_f53HuUkYejSe!Y*0(f)yf#Q9D|IFN@yQL5vz%m@#0j*x7JC|%z(fUjrbrekB<7hGN7`}LEzpgxg zi7_{qVS02?xD+TSN}KUK-X(Du<-4M3vwx`ub%kTacHth=E{%P)C7Fuv5Sw1R{?T&A zv%PyPim(9P2)|e=LIGMKe0NzCp?#y>3vvySk3PR-^vJA=iv*o@)DISPz;M`U4sdxy zG42YcLUPVT0mRa+NEp%L>Ixpfb39PCScSaU!xm$toK+FR~VJlSC*JTK#t?6v4&2pn;_bY@Vzfg1opLN>odQ=YR zh%fN+^a>y28Y`8`wK!3p!cQ7lIW>X~MULYzyKBS_wyBREx+#jI))?dh@m&;nqTb?1 ze-5zT2^Gc-3^trnwa@qgqM&>i$9hbj5j!G@j5qGa8$pmhp9_MN5A*3iW8zu(l(s<< z*c2Wnz3TZ?rMST^sV#ddiU~o)%vIWvwN;Rt!CKD92gzjI3xN^IUiC=c>NyJ3~+ zwKThoz$mDOM*!~1sObwedQ|E3=u=Uq5IGHuA)lqoSVa5!rorhd9`hq#a9N%Gl`>QhBpms)HuKZj%O>tq zC}L`XGC|?yb#C7vPULFGWty6W&MI$#nMVPmVmCJ@4)T_CPJ$3T+H!2-Ch}VZ%%UVC zcA+H{+Baeaa**_8sg<_<0!P%oroK1VHB6J(Kl?M zn-T*->>IrC(4FB)nDA=3IOq$ zM2eK(DZ;be4K=w4SC>%L-Q`87Sklm(&0CK0{4CF;WU0tAm+W<D*@RpeoN zLpfUtT0%Ua@)(O&I`hWDXp!YL=x+}`)bM%2?Uj(uYl6r`Q?LDz9p&?(8e!wcoX9HA z>k{5k_`#RIzX-RP1R)u!69Et^=I}{97({cGGZgfN2DkVTDqliDeKh)QzRq&9?ZYM@ zRc&VFghQCZClhQ_3SL6F!atS$-_`}GEmIluEIWhLL8vJ?@!)Mt9jWoU)LiS1bYkXS zY_i>Rf2QX~+XGAydl{jKu5^7CyCX?<&-jlk1PnO-o)keR6=n4Vge?oy>^BEnd<@)i zZ1k9WDKK?g9L-uaJVhHDC%j`R6F*b5UkVm8P|s62LV$#pQG*kDMw3861L=ZxkUk)X zp)!;Z)I;U38e4J>!jbKDdfHP3>ic^G8L{_*u$A&)K5`zjlku#K$8ODr_l?oNAI+*1 z7KEOum+aD#w}?+{1H2DXKRTV=Jiiw#lcahD47-`Gk<9k%A4Hl3R82AV5`_>fFkIy@ znG_YfP>Ln=vK;~~2dPM>8_RapR(YA?4QXsab-tpiGz+6rB&-_ebJz@E3ixe(e)fFX zkJIS{Zg2&);CGQ@W6Zp_2-G{_gg>+x@aqC`y{osYW)uU)Z9QS#h8@J<3EsGistY&- z^K-d$4z8kP4VvEuSx03kTC&FA+*G_-?OQrpMjtsGoFiNhUyaUKAJ7@lod;dK1c;Tz z*R~Q##(i&$ytNSga%fz3Gh}&MOosGLlim=p4CWzXSbAeHJzBZxU^#8@oc)w;BX%IS zG2GqwXh8?NR{WL7|AQe972>`JH*==ksRHK1yLAo#T9wx2QH-#ZV12VgEAnIV3ay9dU@l{-7I% z2Ikk*bNOoHcZ1eC+qFE}K>%V1zt8tCg>vnLv5YG8c{#|gO9d)ap^8;p(F!WK!U|Qi zq6$^4Vij&>SPo}XZC(UD`vbBI(^^b+w9lhlFda#$kc9Y#VfhaP#)T2sf-=NUUm1zX z^LEd^e<%6_2=Xi`H;UhezpsVD(5f!QCyG8)M}b?tsDz!vmhY+mPUnG83E&cj76@qN z4QkUBt@nGl=z^SUDKmL-*BcPA!BSrmO8oWy&M`Tsm|D=GVL5s4zoYUdaeE zW|GRJ5vSqYW{Fv2=VRy~a-$ZUM-apDpl+z>Rpp#q2C!`S93Z)_V{187DiBw^6kkiD z=FD@(_t^f^Bn8~HVV5CA%~KF~3_91r#5HevT;GwTCAVsW`0_enqdImp6fp>(i=*i4 z1`)GzrxMq_8|Y2+qy&UqD?s5>|JVO#?{!D#=DKbuM-C!d^4B z<3P1HKnaxgfT4+2T!|q;qXdV83X0m4QHLn<{Wc7lg#eNJJJa|ePX`jKMe zuic4r(X;KnxLU2K9(Sd&66U zbCTkX$L7{EYU*}4WPcH}T_W7>Fve4V294o0SomN$*`C46*}ePY5uP|qRT)PFy*~`g z7%{W>Jvf#pLcQbu`x|G3$Nh-C>MnPY0I=YtKZPAV2r2gz8IYau2q)8uP|kLE+^?Qn zCgQePs`fv!7crePY$o)yULKm9q>w96W&2`RsdT{@YApk}uKGY!m%<#P19IJmuF~*m z0I^$uYpQg%6Zgcp{$VKRLCkLSMaCmD%;t=R%T-hvj`1GClQAm-z6Ocl(7|6D8&Fyj zP2@!WiLIi=3>K}XLux}|ZCtP$5+m6j!oRBCCiV~>Jgp74%SXHRNAF()$G{2vBLVo+ zq?gEmAP0~5&3CaN(u>;|@Z(iq&vrr>e`JkT@19pAn9c$cm@k(uy^ zmOs&SU9fbzq>t#(wenU*aL5ok6T3!CrXJ3j=(}+$v5icK+)b;i9ej*w@YUYrTfhzw z8_0n215WmlH+0W|ya2>wSPUFb!F49p-R`4?>k@$A)#JfQ5VekitkIHLoBM@Hnc>5u z+=>Oo?}UDD^=i=31K$Y9svp&DL^4CMUP=xnFg`kyB+W5YF91RNFgs!8H6YRp;{ho4 z=fHUexe6xvH4c!Ksg_VQB+QSMlKdI)k@tgA%d1k0N@%9&dmR2~oNH)g#?t~g8Zbm} z;omnjgOkG>f2bn}h_xcJ1$GE=Fyg8x`UFRu zKIOgSPA52NP8X%6=V*+#q}*JnM(Cl-qydR%K;0nqRPqQiA?mjuOjuBKE&=h^5^scX zH+Lr%!j{x(<Sl^3a-K13}Qdr^+P07n8k2~Erofg4lG>w8m`mA->W?1?CW z8kkg_14p*V7$vv&x0KjSwJVkhT1Oolq%PaxY-yjs zevNViAEFX|1kiajQmMZNphoi2!A`VTa6KqWiwX&E^YVGn80op*_@e=rh9$-Uv!{7e z1WpECxMr3*6=ueBAB>f3az@7Qt2|^v21RUOC`%zr;+UKHP&fXqyR_cp5nC`fggV}8 z=QUvoZhvhvgJO>yBV-M^;PMcZqQy2|#Ut?`;A-`u!lmlMa0{;MmwAn5lk>PF+`~s3 za~>ZvwifPu<;7~y#SvdGzrvQ*bvWn}P`xLJPrDu#Pzc0ISqVn+DF35C!w`m9T^~KV zrlW~4{9_t3QYNb~8~{z=$c1ktmWF06xJdY5<|}OGZ(Ai*2`jdDt|A>uWKb60p@UXu zD0sMTi}fh{CdRB`*TX`CM>ky8wIMd)SU?~;I#|F&QSSINoz#45o&pr*GgTD3pgNE^ zb?Ls|=hv`7mSse)5EVPs)FV_sG7tFFGHi)%4vqlF590)*TuoIwK;f&z3M3-!Py0f! z4`+^VxVYCAiioAR6$9}c5zXku;c~tS^2yjDrX&bb73+dhEouFTVdy6q-`ze->ig{3 zZJ>c;3*#PGsqw9-1H@jX?v8LOy=Bm_EEZ~K14)V=X8Uzzb$Gx~fINDa84=?tN+GiG zCV;lSv)Qbo&xQTY8^Z#WcivYTe?EpbO3#ir&KUkku5>y-DH;iwMM;K9g!SbSlLB@eLJ0i(VD59R(dA{(Vt9u*Y&YP+J`p;!(E z(0JMTw9oZ;lt;8L;+-(dw|*RjubQXsQKDLZfe0_#w0I{q3bZ4q3Qr|z%oV2|m>8N6 z@`&l73E5hx0nFJ@%sRa7qphOl+%ef@XEWaqvf%&J7ok*!vUY&Ysw|3wwTmo*BT=@V zowglSg5&35TC^isyfrEnmO09dcM8>L)*=*En%)wlN5`0AxvznlMpk zX$M6*wEo!IFx#j)sbO5P)1md63`<&)98$+7N9N-C?$kileqCq(p z2r`dlR4ekWTo3$hI3r^lrlf_)4|MK*MJl;zmIWNf59qAICeMuS;;=b|=}!6h-m(Er ze#5~mtQG0l;-NVje?hES(GccR-i`G{+0$7qP!;UD3e~|K2jorR-$Ey(I#Q_pry7?U zXLB$p-RZw@_tE?epUql14~g3X|GL+VjJyl?O4*Cl0peI~L<%-}h%sW00vIx$l{Ztt zBzKwG9&PYtY71P=%@PbBJM}<|wSG^NmPWoQw`4Y>bRkqv z{g#Du1I3k&ZK#eYyzXf%9nPt|O$W^NQXtHkvX;kvG5tW0il4b5bB;KrEJjsgyb(P# zym5{*Sfiwsy5$CUmm_OMYWDUVCAxQnFbeVjfCG~2u~USv+?^srq3G0OcOJywpV_Eq z`Vp~PbN4gFlh>*-s;Q@mEZH{OWo035zl8v=@m3*Ei522= zRUu-+ifXQiFGV=A8ffYw7afIQaL}C9Ph16ga9Ls7)|NUmbPiNf#tBnfqX}Y1f#JLn z5h36i_o5xJhr#H-UkX3>aCnhgzh@JuyGMSrr z!?K~!m;GPyEGY-8ruwSf6E1e5PqQw?OqIP2vcnV;lCfkwWL(OL5l4-T*yFpHvg}=o zQQ`-;HPZ1$TjyjT=fyoMK@Pl?>zz+1+{zvm zWANB+i3eZwTkMS~YTh%_3>}ffMpy)$4F{>v3uWBsyfTV;Vr~Y1jbMCu^q-iZrop{I z&{Swv(|5GrOsnS8czc=$*ir#O~Rii zC2IJ{>HR>y%lkQcNCw(lp6bHVYWc~CW4iy)%g8)M{Hy+xjOJVLr>L-MY;MO|4H8~( zS>QErwrO{%YJ)xW&3YtS00;ohb%^31nQlcOQDVDxs{NC|!N{)eD0KW?UszP z&RMg`bvAX#8g@C0xq|X(2x!(yGcD&5&1%>!{FSJGQwQqIQEC~K4kCH#Nzye6U5w}l zSXN$+c*{*MOR@DIdG<>W###kg#*V*SMT(0|$u?R87H@6^oUQ;GY1|e-U~cDtqF55M zMJ1Q>Kt=rU#)wm?BBN9#2vJ-U627qqUnnmeUt_o^>_lIH+jcdR5(A;SfSM2~rA#uV zlu}9oRsm4~X#qBPcNb||GGChvv#gn*j9^ z7CgQ5&T#jpF80cOo`!BV&+~ZTh@NxY0f^=~gN7)cSKONbnzTp}B2#S(SJq6^?|x+Q;5W-@7OO*hP%D-+bqtrFlQY7w7JGRwkUBoCJ7Ia__`X(ty`ixm<~LUN>OC z9j%7ifxPn-H=R40K8AKS7s6Oq zS1PF1*Frarv)>6wap*2vNsNXETf<8GEn%@2o-yh<<-8XnGXv%+TlYEOq&eQCHrz;J z^#0=bNk!&}lXh*hwn0&eIfWuodMFd6D$$|7Dc8EVZV9(_)vhI~xc-v4J&1St_O6zD z_Y!MdYV-Q>>Wl5QSlc#wv96g$^3v8y6z>~hXRR~$di12tP;R_;QR95xo&bCIULfyc ze5KevV|~J~$u+-it??hu`4^n0o;c?>&WGvK0?&ucLFRlzxi{ZD|9)2yKE3pMA-6JX zt*_Ym-hFSp_c+>KZ>=@P!#rZbMZZy^ujqfE`bZ7_f{?r#RoSxsx^M}5y+>PW@Qb4_ zMFk31Jv{X%3~Th)d_n&Qo3#Jse?#jYLbmL1FF9s01n2(Z>8AUzf`QWzlRgire99q0CwIXv*cn z`YapBqZ{2$G}6l@WoslXPTgF9tfMb6PQ-8lmOOohNE9DX$UNu;z^U9@z+IoCy4!OM zJhxG)v1=dwXaj~Qj}N}&EYnovZYC$o4Y?ndGm~+na~vd=?VBG2A_h|&~bUU*EM#n^71@Ym6iR~ zhS!nsUIYsqNO|q8s;E35>s3OxCbMSpND-rkj?3dp67=9bXyk^*v*B>eb9f!)oQ3yx zFD3oU*Sm1q`Jq@YU?^y?z=ewv)(pF(r^M&hmfE|1OP~0BdTzH9)m=62x<_-|li81+ zU!QmUUIG35K9{QOCz=Vrlw`;?4tqc{D<6%Bz8V~xB+2HSbKW!5ar7LqKa|pJNQ1D~ zW&bn!;{xu$)YO!ciF;l`by4lRDtwC9mu{-6Tkj&8l}`Q(OY|au^^rEzMe6!`a%JwOP|zYwe9&ml-Coc2-QEuhVIp{8+?2z~sFacb8Cgt@vMeyIrX0={e8 zw_F`L2k{pY1Nw~6oecSw!FeSm&!_2opY{Fg8mJU_I+AuAKCrjsq<6q^E&P9Q)tbfh z62gKD{vD=mA`NFVe37#Y)<|`J#g3bS1Z_aUkdozI=4AUajwDEHC~Wczp83sZ%-hPe*wr z?ls&EOhZw=Ul`1`8mC?W7eVNjm&nt0gKd2oR|GNv&vq$AeH>D!YMONgE4?9v_i6Wi zm0~x#Qfr&@c5uOu6WAvZ?_g%aI#9(qyz-+WT&(mJU>OC-1_5Do)o7zcB_d79Sv8bn z)e#HBc=7@5wyT9j2k*h2Bd2X~T!dGcr(aANOyAZ|8tSHfIirl#JL>*QfTje%25l!^ zj*w~7Kva^MZVJ)@bq-97jc{eqeK1A9ar!tLo-|$yx_??f9n&9QCk{_0oXBKx9b^ya zqy|$n^s=$ldVDVJ&V9Y=SH5`D&-J(u6xn0+44q}9?Z$1L1(!3WtlmhhJ(D8m%7zfbWhNn$Nf&lhNxzCPTmC zft_V+ZCL5R(zZC#E~Famukww&N&Jj0F6U6wM_GMraah}_0ek-p;Bpa0w*LH?Fvu#fVk?n|aTu=t3@-I_+n6~_;p#HWT=nHn0o93Zvr zGoFnH7rX(HwvrlkuL^wjMZqH8k>IQd`R#{^fbRbuzvc{Op&q;riRwGh`3#&c-~Zqjrsg0$Mfj>cj8#FW#+(zr^6}R z#1dt>twqEMs&?AlWdja=PB0&+c6UQ&OA%w15K;D`W_v=Z^i&Jm7h7Xd187xrlMYBi zK$v1y!YNS{oqqvq%vbSFcFdSZ>8hc?_-LKpQpdKzxPAOrt(`)hyg@rQCx|_G72ylK zbU+3G+Yhy{rtL-nD9?>wg%27cNIRw>e$03!Y8Dv4^%-y0p=8}pZ_-;z$CT~KaH;qa z$g(t4dX+4ATeB;Q3I;gq423|BP6}^hS(bR17S@)7VZ|528pk)Iik<@QyzJZnkbpCEh@YSxZMaV(*x*bF`YHcTEU2K{NZw4E6Ly=0B)cmdzZ07lqLE8I z>lEDO^c?L~A#}r+dcOp!)5Lx4BqSCi;)~pO1 z{ToYB(TSKKt5OsBem)f7M4&9S%#@lO2<^!!x>+sj5&D z%=$K_=>8-#XLm4jBQRGNQw7@pgf@UhY}OVc+g=9Y9#}adrO2nnBOMmK3!=mMMi6?M z5vKQ^e0Wuy0pqh9KMUZ=t&)<}jxGe|y9L78ZdIY%Z#>By-<3t2fG2G+A_AWh^@X7z z+0FSM)GlD+yWeak*BQF=565xu+sBM1vTJ*|1z!wB|2spaGret~MrWB7WBY%i^s#2L2^&qfwegkj&P5 zbDgi5)J&}jZ3Vb5fXD#w1NsQGmA{lr;8rOH=285{eWcr-AYDH1B_bmftX;Jy`vr%- zy7>;p)<#;zVR@ys20?dBLsoEJ$#?h-3#sW!S?n!VaqVW}Fk}eV(bl4J{J<-#5hg$C zd55bJ*B2Q|KCyDZI#msa^L5>=4}@8DE_N#db0GOZ+NJKIR*s;CJViTIAQY5`+<9G* z4{XNfdFse_t0CsV-;k!671Ed&Ld2*V{nCYMlW*&JA?zjc$|=SVPfP({-VS*nL0Gj= zRrs|)0H=Rb$08k?)|5(`5=hXUy4Fzl5CHnB_IP%DxKRxd8dk(Y<-V9NvrTHJ!=?GR z(F?IpbB2l+e4NliL`E7O?3sKsu#xau90F87N7h0z%aMj3Sgl+ttPX~jI6bFHnX2z_*QwNPQ zuRPLhX$=p5qzMfYMJ^@;we05Nl}t?imsoIOr0`-^SfZIv9#m(H^j-I3 z5*=o2@|p>8*0e|zQcbdq^16qPHn9A+!|n44il)7NN2;6%JTaQV0HHi~lCTGmR(zsd zBda>fHXpwCHQx&T0H7>uG-;csm2E0RK54LCHwX)p_4W~;xn1%eJYJ+0eWpaez@whZL+n3DfO6bSCQ~D!dajo{{MmPoRPHwkN+Q;i#u<4#*&{2 zY`R)KG}OfY1?5V$97dz!{bY{;;FUBG#TYJ*xKRj8tu@OfLV~<%9fl^K8kF*1<><|(-52zf+$2$v8+%>$&>+1UC|^% ztlL+@1wgLs*M^D0g*XKh$;|yPx7s0<=rmb^-Nw`8Y$&(0Vi_*IcVK?%M8}F(#K+U< z2{Vk3J${1qF?h;hc>ZW|i)bp3<(vYttQ=PtmwjO|8G7cT#=`Siw3-!y&g;+;$rTqC z$%$VI)C%2(LO_~FZ1L@g&%JB$ej^ly4zZ2%s$n2ppm>*+B}9E$zq?tB?fWM4hctgg9N5D? z@T@hg`EwghQqHC>=o2DXodcJlXp18*84=1t8#XpsL=-e@C9W5a5H);&sBEZ$C?;MY z)(BxPb*y6(IxHMfwn->6(I*sGCQ;7}+ROpDW^8vu^b6F|wU2+TsVOFMM?4B%6z-L( z7OZ6wbL^HmH}xUN1j|?YIB<+!Ibkm4noV8Vs*2JEs{9zb&GpLbj4$CAu;PRV;8N45DW9ug=z>H9z zHE%mVzcIC@QWnU;c*PK1O&f?iw9&z3=5Pwf!87fj3{yx#za17ZlOCueMu*LFk?_xwosOu^zE3bq*TIqx4l(oem!KfQ0!s67vog>TU~8<%Rb zpTevZU(WD34ENY7g-wD*p*}|B3rOrm=&U0nxx7}CT*G@m2TTfAdjvH+Cu_5ehxlq~ z^AW`AMUl+yT*KAy#1o>jSr%jN^?|-dD5z+GmOS3<^Q}&1ie7}8yBov+o5@D+c41&> zlPI32#5^(sZXgx9lp%;IWk>Q;J&>8zth-T&$ABd^P~NE@1MkroAg1z@XdkpB8ax~pFK||JBgCQ zd^N#LJNe+@Kc9LGw3`x83p*dKIS=aVz1H4T6l6qt7gxZmt@x=Yjw=>%&a(b+XltKj zPq4~0FVmS`6|~xr^VN-#1LqfvJ+4vTfneE@HRxLE`B3Bao5VqemcrLU4DwqLodnex z)cR3SB9&Z26fp=tqn!Rv=r46Ppn1lMpEsDN3zyG}FQjISaPu^S;`hd{Y=_!pOx(0RN-$H~szTU_;;6*rb;Z3%~{>Rv~}wC3-b=N)L?804l3kDo7A8s?`BP4E}Mzr{W&oS^xkVz>nY?egf3ShFypj_ghYE ziHX68o4Kv(Z$*==_dR3vPR^VG(B0&g<}4aC2oeB`9wPbNO{Q`Ji2|wuUovOTJ?GV# z)5J?sw_uUPm-IH&hJ=WuU2%W`;0rK1I66YZZpIPaQFslkqJR`Z;3HcJ=0?oNm{sQg zXYkm0yytqJk9D(ATv1Vxn#>sp@W6M z;IVIx2jO1d4az}WBJ!IS!FjI%3k#@f^#a|;`yYq%G7Ehkr->K)W zChmV6v(!$LPxFGmeWL%Vd3FQ#-EkSt(y|9y&XG$s9vxF?IGlU8JaOKC^S%Ry&A-a7 zzjvt6b)D+{GMDo)9nVQxIP~9XgOUZR59j*V9O65^@+upb;EK0peJg7&bFHvd zAO^_GbP!$Duh|Q1Cg)ciJshi67KaU2*AJ|!sHUXuqM)o#PGSKxC7aXxW=do3AH7-h zG#%J9VIl>)=`g07anUgqg3M(+4@dEicQzjiDhEgNF+9(6r7~C+%ZjRt%IaG`ntd2U zr3vkNCNtQ$Znd0Jo4!jLz8IV})Mk=sprZ`RX#Vv<38IsZ## zI=3rCTAoz~d#!l<5;rX;`(&?vmFb%aQ-Xlz`S>lh(0S`}!!{lF$S#dm#a!@Kw`#jp zJpPVZ+MvZhztgi^{y8}Ngy+NLvpEUu#lYr2rgxEAPi;EeFeQCiK4yQ3>hoe^zsx;* z=H5L&o5;&~_h(@9UyN-3Vp>j4VpqrT)R2p#(J`o86yzKZ6Jd9BY@qVK>jC}_Qzv_f zpiy1hU4_53aC;U6Mj;`MlN7*}7+4K~xjiNxfRM%CA-*h;2wJeh{b_7EURYV&k7|&c|S{7bpMjnfR=KXLKx@H_7?O|DK!3(|(SB9O^BTiOXCa z-^XV!G?YRvCipwM_P3V|;h^9Kf+uWt

aeW%I}N0n{`QjH|Peca{2G75iU1i6%%) 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�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 }z3VOKEGA`k(z&yedK=8yB#AT|(9i}|m< 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

dgjBEK4-H3WXlo*oafUXh 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�&4M@ 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

)K&5te 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 z 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 bjexrV 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�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�@YWXv*u>;u zPIa3im_IAJC(=)K`oc`;QxUfwbnnp}_m^9R(Br&J8Kk8AQdiZ52JOK_NMPBbxG)O9 z0uBD(?xbj9bfiNbj<-8it7nmFt+3hb)V9h>?QqJEZdRAD>;0_0TJoBmJ!7T*?6aDF zC-2$4Y3-=EvB5RsF|siV@=T>3e;+yb@J?&7ab7p%$fWo66DVpeEnaCs-wZpoNOUDG z>JEwrIFTq7^%#(-ha-taQxCXni;8{|TdA)U%3wvAiae^}Xhjtjk?EX&N2vI#9XtfL zNE%+Uisu7aZ5`ZhFdt)-kog3(@Bggi?67+EBWr>x7+OEcHrUlb<%Z=dpZ)#l37AK% z9HK8#i_p=ga}t&AlkQ_m!FsxsrSG2qZKgpQ7K^v~{*mfP<2YgEG!x!$JSkQA_x9dz zvyYB1;{`TH?i!yf6CeKF=Pu)i41=Zc!Mj|b=vxuw$%pXhfjlnm7In|A^v+FU*UhH4 z&-chhnY`*5Yn|~`eDj2yjoEBBvL-^}2D$NIm!6eiE! z$?qECSY>wph^frdfA_caw`BDF&JD_>@XdD)gjE z$*Me!-O6Clm@#0>iYJw@}s#Ju**8(RFKE1}VdW#47IN^iqu&1>C=6MxDv+A?XI$tX*{hialw6a8Xfq>g^Y$6Nu! z%yIS-3F9<_eSpF!i`Qi3`~UShKr0zl}x4wA_Zc#jft5G`}E<}tF>dFaX)QbQQ?)zp!RcL^@49? z42!{-%9xJREi%QpXseU*X6W>KDCf+RKj|e#rR#1bQ!#;c!&}va!zQw8r-xLSdzW5Xos2o;s8BP75`XJ*Y+e6EW zvmtlqr7{a-eU%*B*bA|UEJh5L<=B=(^%uFJTPBUS;f3qKiJABlGX*t9pFB@JHtJ&z zFVS%$JcDLMf?IQr`UpGynXKJmOea-snNB-PF!TG}SrsqKuIPiEYlH@Z zcGtoGCQbPv}CbnUs$N2!F=MUL$_Xakt-G*Inl4+k>j0rl)^Dx zd1voBP(8)zbzV%&Z5wcPXOC`Yx*2fjGg8pju%aBzdkyyU;;yKVNh)`Tl&yUEROn*_ zTU4B?ig}A{mE?|}`r0{ye;@%3+-8@bUF+CbY)1j32%O9oo!~^(08PN>Y#)@fB}z2V zxpiVdrU!@yljeI^5ug>A`mbk_i~~m1^XXr=Rw@>?tUhs+$TAzkwDc9}{-W4kgr6Ac z$b794zD+9->^^k3Tq-L6Auo__Nw=-*VnD5M3!<|0eZ zf=`I!X~C*JBjP=ng}nU1x# z<~~jwPK&zGTB0`buM0cuoXqhiM%NH^w;(gt?HIgqRL?rHQwUuteHtfZg}PjW#WuzQmpygAc6C{*ScmpW zvSt(7$9&#fS@5CFE86%p-md2N)oooq*NT0Wwij^5bKmO5mhDY}r!8eU)c13n!xK?a zeL*Ej84lW9fJdfm%{XQ{!BL3!b)MD^{R)PuQ|sXg#e1R>MtcNjaf^X0?tb{Dbi1Ri zB0NIgHenjsiIwO= zEJs!!RdhAXG2{H=tuaLhoC=k-EqGy1^Tg3jq4MV9d^hAB{8LYBIb+|!W)DMcNC+E` z+io?m8#-{Nw!8wbGDy+Q&n@KDr<$9?DrmyumxrMsq#rsIW9h-pcwS5hmsQ_&K|*S4y=Ws<{aG3 zbCX9)xIY06uXJkOq(J$RpQ_Qd>&WPj_O__C%PSM$z#;F^CXPrvD2PaLvAS!w8`1zH z41M8FVYv~zi);Ih2#>VO{y>8pHs%)XMw(@a(1WsKXBR?`e-k*u*vC6@k71F;DAB+i zSfrP^XILAfjkO_B;E~R~_DB}k(+Z(D!zT8`viujbkBy?j;X_pQc+neAv?^W2k@D<4 zBQnDV0~-RX3}bi}7YJ0?rsfgpqe};zqF7K$(m%Gcfll@l&u5NfFP~FLgCjO^K}%1y zHNa97dr9ucCvIGZzNL`GiyPmmdIDER;4C~5F4+fpEG&7m!xbv^C&l&Zs#SakmrG70 zrDIVyAk*4WfLaU0?%QM}Vu5W@Bqa=C;7mZq1xi0dA&2yA4~UX(JYUYvOEM4)kyVo@ z{1D!XED*W)^4ua0y1!ax+U7}uDp`SKc3FSoTqZQau-69caYAe*_O*Wdo3w+#f3wA0 zUNYIc@hiKNv583{y8~Y1wdEIBUP6h;S@(1KXH8iZamD-k(fVgDmD8wORYvr(`7E4k zOTcM=r+Tp%ob>jDwC7Y$b}RnSq1*uBPYPmKCLvO$8i-&CY|WT$xG=|wPpejFuLvm; z62zdVgcfXnX@vRQ*{oyivtY(&=xod(+$E zi8eD^Nmz*s!%GaNJqxa9lhe!5(CyY}{3F(&a;tL$T4HbD;+OkBe0W4;6$o40n_)~U zmKL)Nc<+mZm~%{J$UB%r9BgVhIh0B4t9WxnKRc~e&JNIt0LQ!CZY8lc^?3HP`rl&# z!_~$Of{EBlfUbPiq-EhlU92`erl$6gV7QD=%eDj9>|n3?(lTye0+(^l$0MDKxJs)n z$Oj?nBemrxD}Tob&Pn(IE{!lGDu9?HJWq49*)}C$fpN0>|GRpsb94FF~l?DaCy-{n7|4VwS>q-crhs@mlq5Gl|`H!p$(wumx-vX+WT3$#i6 zr$;)t!8>vNy279$5x3GAIdggaV`cYG52}V?S}~`pMlQ^7Q~3%|Ys}(>^ILBUI48}ORQGJytq85zd$crj+x?{-{N{hp^Vpuzsd8EMrKgIRj(a2N~8f0PbkC201$ z1p5j$TVB+Ktybpk@|=(DUr5ZUsNeAcuYN9;1~}t`o|EC z_*)^2by|+NUx%OA%Cbtc#b~qw4 zziB10%|%ucTlg2a=+iNmC{1+b$H2|$0f}A(dUJ$V>LyGS7D8|7SGsDLnWrCQh2XrT z!4)ad1)1hBu4TySJU2}Kyipe*#%uhas!5;yM;$n|#gx$M&nPkK!^INTEp|pp2$-Rx zJcbUoJzV^_snS-5zx}wc4KU-Ow#FH?LGzL3a0K)H1Hh_Mb>h0Iq+4ABhwrt2Cz#J$ zA3W$@Go~03Wg%2&h%XzB^WKL~v4olD%hwQh5T0zg34l*;XMP26GeMp7ZOz$)vxP+B zLB6fEZzmR*%VC7KgLxVvY_*?$)VAx^tp2I?JKRV^bbcA!N|{@h^btB~tq4}{P#y!@ zmQI<`7c6FgZ1T&8q^YJ=w9gzoA`;j!R?Ew5HO1ugnj_Gdl|iheEICb{#68)#u;Ceu zC!GfAL)J9uD@PF&3h!K-niq!E1PuX$2C{`S^^5lP!8474V^wjLD;oAe$GTL7UGX!B z;I-)|$CZ^D-)9TQnA{+cE#T(uH(jydh0fZw>J~QHHR%q>BQa)BAHvl~Iyw)EGZ}i}dKp}iXuUmxF+Eb+tX9jG#y}V8OFDEUjqzJulJ@)SF0p1x zT|ISnsE~NFE*a2Y;n_A%pjSYbaRD_s_-C}eA!o~J9Y!F(4z^6u7*iTE8$2oBjtZZn zpetJ*ts&^=(bv-r1fB!sWm2Ty=p!wHT|xxQ?%aefM`DU8Eera(bZTObX<>9Sv1_R| zYKBQFO1rQU%FhDEa#*o|j$(&4lw==N^dvQ@N);)bxZ-NH7!~94)uiV(nSZ?dOykge z>vdA1YNqMS|C9^1N(Puft?0i!h>c%c#`>{#K!ak|_&12uLW9V>p`MFT_kgpCnu2AAW*@vUxc7BKo`$ z_-o`axmOV?wpe)*+TEF@<`eo8=Y)i~9VLXf@h_h5`H{!@K{u2)l-Ji{V*184+Kmv- z9rbnfiaDAOCR%6uFZz`_F9dS#G|JTtXR@tlIEx!PFZwUJmLV|W6(V)h0}HN@-qlp? zmVIL+AKelL<&k3J81((wO%)N(J323ZFV+S;iZ!V#*3lGfhfG$sFMc*^Hr4KR3pP** zSuHz$8#iS92V~y%=p%5*OsG+RJ-R;FcVK7bMA+(2?>u;OvlMG?OiBqVgzA;&wu)V8 zJKHTSb4QytusKc+uTsV3guDsjMxB$&kgh&>IlGf~6nH}dwSo#alerpO10KmF$pAJwUR`>^ZPxp=X+?^dhYhr$Dg>K9V=p&KixgAi$8Y+)6n&V!vvZ6u$} zrjN5|h>p4rNtr|z1wQ<%d;6k84J5M$AqMoYwps@mx2xeU!DwH(wEDojx%}9ULPAoA zw|hG8|9D-i_p#;PCY6x*Q1UkCp^LAbdES~^w*93kV15zw{@3a6;3)fv)2T=eH!Tuj zQe0>@is{J$mN26*GXdsoWY`po)=1OU*}q~ElNMKrVW`PBMd>luAVr5}-9HW;wnJ&G-r1mJIQU9&d)yHp_HADEFcb+EU!pkYaVZVODil9N4_CxY};UqB1kimt! z<{zru{N3(smvEKUj_FBJeg@yqS5w8y%GJy!DzeqG0#Dl5nz6NZWw(1$p@y%hJxR5- z?*d-4_YH+u?8es(MaPR>1AdUn+;_eltK7Et6@}Q>mwm%&#d0p?>M68K&-0uRCNoUO8ITVh?ptB{)#rafZ8ODt-)!IXPc6AQ}n`GN=qJFaSC@2m}Ob z5QGT}>6=mk1z^MBfeRPiTFSD_EVbYO;IR<-LKr)}qcws8; zh^>E3V*Dx;bqwb_O}_vPBZ+ru{#hw_@nn^aRdUl0&H%YMXBMB4mDQ<@*w9Gr$EBWR zE&NMxDWx9P3WPSoicAz4IrkP>K{J2p4JoV2&n2K-rj^vumX9$?6GW@_?|Bew%jLV&7ByEFnY6rkFSy3;Gp(PY~rV=$C)&} z6TC@_$GyZ#i!alWp@3Tjwi`!=!zhKbMoxxEe*Bzb%l9m5pH_&PhX14SP6rOY25?+r zvj0OKr_QrK>hpUWiS8qxm592xbo|~Uo751r>G-&&qPNVg&zT`UdL|@PjJcw-larpj zObZxj4yJ&kq|6dnOC5?ko2F-QO%;eu9INq|3Hc8&YVev+;F?N7j(8@Fzv}qOB8&zG zEmNlJzfX7_FTM-ujkMkZ%M(;vMz ze!$$csx}$SN+H=;cahx>OM!4xY5GHXa;8{-Ok#A_V6PaxDc|O-^K{=fu`IkQ+d!yE zx2wyC75ebO$$1zRhU_12A4LoNzh_)X6En%X1M^PhLpjxGh%E;10%dNQiPgmSJKus|ld$u-JS_`@brQnnb4_!qxYrS*{XLUNaU2N8j=+KEXtw1U%Q zUzAR|B5fw?pO}z~rZ8ZZH6pf;^94k?oIr_ zmZ=4g9h&qj!m-XyXyuxP&8s~gJ>8kGG$=;YpMpx#hU;KM6xXn$h=*g=*0n6Lwsz>^ ze1bUDo>PVATJ{T>h=ZzkmZCbeHTosQYI+7YfJQ)n<+EHU;$VFJqw{fTP*YCXL=xTp zl2&GPC{eKbTKxh(!qF)~w7SgGLS zdbgCKaFlY9fdPzU-aKU;$f7Kq>QzH-FfREBZgTZtW0g>E(jCJ z;Nb~r>cphZKCU)VxY_eV?NCrd;}^_|6{nYw5*zy?yce{&wP6@2vOMTDrMLSbWve;V z&u!0-AE)74*deu)(%~ij*%NWN>Av>hz;>nBkXJ7-hB{a)1w>wlnMCsVy|-457E+1P z*!%p}ZgJS3OeRzbqa-EzfU5W(t1Ujq`HTk;97dp>c(=s^D?cIp!uwz9a5YM`Ja(++ z(QO2<4Zl3Xw~y9l*4hXx&;z@OS!$dipHSX;{ZB2DL$;=b^xy>S>WaP`W^h%%T97Do zktZG7@+nbS9I6mgxjL>!NA9Ls*3_4mA6Y-%yvmABQ*ARp3u4PViRQ(p*f5zCqGOp5 zb3ziw1@-tk+uRn>P%KJd*TqjLi%=rhMhJwqUad49W=V+{PqtwaCGZ*FywEfEjp<*l zLh0uiMP$89L=lMOGW;$4b!gx4ix3IH%8fk1@4+>b!;ZIGjrj?lOf+!VXm3l=eEAFP$nsMc`<{{fxiVgNUoLG*Ra z#MTXXIz$abwFn@f7Ni`L6()Tg2KOqWd(apld-^K49?;USx`m4a)~#_pybP@+u#dgc zI8K84dUV1oX#z1oKM>D5+;A7teyPJfCs+No5Pes}409Q(lb7Sm^1Rmcggzn>#i?Wi zNPjVC7*a!0d66Ab!-;=2Re- z&*u*+*5KwV1Vx3JvbsmYcV|*?J^Q3>(UMA0OmohU`bTqLU)`58U$LM_j_aUH^?}GW zrx0tlh#PGmA;;!%^6;tM&^eUx!Jj>KR@i0-5KI@YyV3;No_uDm(5@Z!(To&LKR9cP zY;UtMuC$+I>0~w>$5E4H$YXW@mNQe=ZL_5eeJpbSa-S4m^t^NDB1mQ%+>+o(a_8R3 zddJ}iRKeDWDf5LcY$e72=<12S895b1O?>(CUH%bsWde~B(hPW!>;Yiu>n2y*XWM)h zhOn=_TD)K4^+Wij#OFU?PJ>`-C=;+l?|)q$GY2@oN>)5DfLh~VcqF_V>#Q?~;D8gq zrC~9aL-@6g@K?Ah;}J0p6*NeQ?j&m4=_I)PvP17mUAvV9t>Jtk9IJ5r zwL*T1WzRj6HGiN>;7n_@J6y7>yP80%F;_NTU1W~foOJnembFyAZ12IWK|0v$9I-P2 z7N*4kO2~Sr7!e`ukAG_AyCKafNrL+J>@*@&$wA27b)dhoB`4S99@niBYK+Vy>vYqNgW0p8d{i5h=wTTcx%xX(Wt#Rjg?c)amJd^T-7s#}s9$ zd3oo7BZ7BGWh_x>?1>0RE_i95O%HO`%k#Zu0f1k*Ph}gaT+J^L(NOg*|&s z+`G4c9%oD>N4ob<&pkeIP%yQdnJ)IUKvP-AOW}{b0IQjCT_;L@B&0Ua80>txm%auk zFT)47JI}c!+k^2f)%j+0>(jGOF;IwBFlcP>mOi6rD+ND z(iGf*DC&Tg^Xziy%t)=tu@#e3%$;8#F)9gw-Va(}IGN6~b4p6Xb4%pdE*^GU!=vZ$xL;Hd zP-v0bc72)GK>T}N^SpD1z-a%xx~;K|{}h|WsHf)+-@$LdcT^iS({b^;H}+3$XVTA` z)9FTeYrLZ)75k6(^V8nE>oM2r7vo3d6$4(E&SvYDdmweqMJm@2(e-Whdezj7)oeGo zB19jYBy`&pBwkxNju*^QY{x`C!#7yzVPRn>HyWC(GD}-9Orq_zw+8z9`s#kle8Wyh zTTN{_lY@eSBD6Hd%0ORVU%jbWyV`tlMH?F%J3~jNXx@@WJ5zRY#gJX`7*BR8nMhWO z@yXm;@4#ZR4Z)TX_ad=nnYwB#Axm2@k&Bac_o|eO6L)ev7m8d$7(u~$NrI2#yg09P zjbuVbH2YG@Zo!zxRyj-{hmZeBy-52X)sPtc=i#XeC9-1!{mSsi@($0xWqohT_f-Qz zPxsR|pT}h}1@^T)wQap6iH(HNTzonzQ>K7PWO7XUa0OLKJIR-ojc%^yE92%>bciD zJWed`2W^iI6z^z%De~Gbx)1g252$LC1Em$AI|#_jyRzL}`J^Em@AI5rLs9vR$|4r4 zte3GTb5d*@H>zm=>PHt=O~aM7IuPq3ZdN&1gYC#a_;mL}^UN>$1hIxH^q_YJXHl-E ztt2UYEWZBk{Pp^WNZ@JJD2WZhX+@!wquFDHZ!S+f`*NJ$bzTkL&EvkRZ0GI#R3^>e zMp=-ZLgkTv2Z$+*;rQlooa&h>Wr}=A{u7=d+;Bi|eJqQ97{d|{0$s_m-yYA)I8eBN z3WsJwoGILor0_I?iYqjhG8C2#hU6fE1q2;gMt%kv1?A8fi$G6PtZ+7HPHoGE7(b;( z;*Tx_fmZzfJf$)+wI{jbal5?Tv7~xVr7#cvHz?s_HnosFR#nV_wG(Q;Z>w1o2dGZa zWcy!Tx$|L)snd}<@i30oh*)Jan;w$;7Bop@>Jl#t!TQycx$25@x3M_KcpDQE=UK+R zSvVug9bgPZS<{#ztyjzp(9AIN`+nF)f_rASih1BYE*dEM_-6TxuPF3SG!J9 zAR*oKrY;E_Sesl7VPv3km(3g3oo292qBXKJdlcj%`lsi7d@v6kAs!4{h2Vsl;R1<(!9;L@#3M*SNXp^F zBbWJYyEO23;l!YW+Q)mH`;*c`TO8gU<46%HiJ4iZw`;+c?7?V{NYgbg!Pdv(<8;>v z-)bZg3W)Wjv(gr%GvI-wId#Ekw*@(()KbQq?ca<}B4gW>iMve4O2>c7ySi$^TWLR> zM(RWr;UslBU}HZPX9Xj$UOj{~&`E6GFY+s%8y(VX4;oURIV<4IMdvGkUIp){$7*20%0=g53+fp(J4hc$W&=}Kn}`o@%=;|_QPPB7Sd zS!jr2{rKe9B-BsIYh`zILZ~f-7!134+YnYkq8HWenKT=9sT-N-8nyf&rOHh?_s2|5GxLcy=R!E%7a;UeRhyB4hbjrEWtl6Bjv(!G==K$ z^&qu=MT{@|eH}isZ;z0hSRSbfa}ek{h73H&M>q7}_dZF^Nz9mFIVwQbl`N6{1cmWK z84)C{k|1Q(hn(esjlTU0nuKimS+sd0BnnAg1{+Wx`CB#0{rawqoR@wmX0yo%sY0et ztlfh!%dI|hI?|un`*pm1+{?@3fMN!#*j}HVO+^6guoLOFjRJEr-uYVPW$dd3fsy{# zJ}O9@{lTBsjMvO+o5Q4TN&2WHN;-@=MfaKEakhh6o;Qjx#?8M&C5{EI`|#ZO4IUzK z0=@jB2`(tr21)f^3r#6R6sK@{D~+B?MHDr^luX|B#?B7i?NT?Z$#y`@1KU4BDYMTwmhg zi54XO^dN^e<{Z^hG^m7}yPxz)vL;3g@N_AQeyWuwh7hRxWj3#lgPh&ha(R{!Nk_gR0 zrl5Qny~=Ol2h=6g9h6g|8&b+52|DF^RyYr7jFm~MJ1#S(>%xgp;mWt)$5in+HM8R+ z)mmRS1ab$<*xEi1JVkgfi%Wx_m_qn;x3f6ytps`6ZLR6MPu;ad}*TS6aG|Ha>i|9YH27QR+qs z`q9+uK?4KzGs<^bxKTD-&?&?6pV`DXsk|lyhFz1Ux(hV45Fp9k{i0-&wc}dD!W5%g zyLgYYr4hHkX5u}shX9YNMa;++^&vZU4X+FN2reqd6r4YHb7R4)2cryIZ68Qh$O7yRdT&|*Z&{_(RB zqbRr6fFR=!Pgh)fmSgh9{&Nr<7WpL)-qndh;1CXChrxrHi%3ZsGIV>CuZSm)hZ8wC zE=A1D%S}yZj3w_H)9e_Ew(-!N9sq(TzyU@|2@h^`S)7k1-J0WUc$Tk-28Rz92Z~zWa#zU=?4XzD3nOS6z`bY&RZP30|6|wLS#hJ#l8%JN z?H|DuH z6+{TRzCa>H#XlA~X(su;s!ICacpt8Q2!atPIxgT9iXqFKxADV&SlreO-o z*L!VWN3k zv+wTKoczup5w%a@kdmdZDGzPHg;U4<2M^HVeB zrT@isMf7XP{|-w_AO3@?`|r3ER-Ka_nv)Z{SDD{F_Y|@c8aq1^8ylOqly_dV()1is z_dFFk-PJyoot>YaAN}yJ@8P~}B6e-5cyR4GWNP|3Wa$1Vz2>aOf2lny>!|)dD=Rmz z?LMpg{$ImqNcC$-`%cqm$W!&uYsgz~^mnbD{Zm+eV4N{FQXr$0pZV~uc0rYPw%0f>9RnOQZgV&ei;@RKnE-y3;@m# zt_bc6UJPCb4h{hU0U3=R#?pmdOg{wm3PlC10=7Vd!Jxws!|=k$!#KdC!}KkI!^+aZ z${NDT2E#VN&fy`!<%2rFdBYXM5deWeSs(yG5FrGCkcgOM4nY!;l#Bw4at@JJ6$y(j z7KlvWk3ublDvLISwt@DFhQ+|hh2B7l##GMC%1p(I# zk+P8Tl3sz55s~%C-Xf6?u0B$s$}&;7P()MYO#P8v2B#V{p(^va#G)3ZcA(Dg3k?I% zghzYOM#m?>(bb!G55SK`CnhB&r>3Q6WNu|;XB99oFhW=|FERVDfU=UYvayM>$+1O8 zTNV~am$b1Ru^qFWvYoR5;Mkc6*?s7vOUufIIM6upICQy;;>Up8O(ay@%{48p|Cayh zPPv7*5rqqCE;%d(FJ&(^Eln*QE&W?U6C@H86zmWZ>IC!(_xAM<3Q>@Xo>M+nonM$=(wPD44A$02}}TfP?~G`n>oc`I7sJ`r7)I`$9nj zU|`|k5fG68$iBAP(myECfp6YOfaCR<_TrQ<7kP{iFC@Up#{6h&ewe4NrXo8fCdlRf zWKU!MFMoXnK?Zze;Oj3Wz}4Z(RBvNpe4wqNnw%&%119X3-|^~X*Y8}}gwOyN6w@Hd zAQZ}_pnpL+!S=!Y!2-c`!BoL07$}%1SU*F4hOCAD|DYHf9Uq_YKO`oSfRKVD{tpRB zBp~FJN#96LN={DsM(Tf&mX?-~o}QTgpOlf2k@%hRi1PACKp4g>H@8v4v*WYBzpD;Y zuVX+&vVjGP#3tgBWORp+iwQ()g212wDXeHwBygkeg;tQjg@IVX!bqV)@y7B(A?Ggo zd*COfg3|4kuw&sRF55vaJVsMtc<#FjUq5UWQSY>V12=HcZe{}HDNn7bKj8!pk`WVR z1vP<@CHkN^r!7!SnsPAW6lB;q8!VH;iifc~F_8*h7LiM%H*ok={A&c0S>a?Fa<_|1 z7O$LO*gA^U0e{ywf{|al!GAjw*>hZl!t)i~f9?7H5nWqCw&+Mrqn#vMT4#gSPu%xS zy?g!H;0wxkK^zOFt(0sR)YLLEMicZ0q(Wxiv&s@9sq_}7bOyn{5Fi`z3(dpr63k9P{FlhnUt?=Z6EN|jEY_H&S@g=f2?6eoODfbiO;#G~-{rbWt;Ri?KNs`2JACK)VE8vT}koemNQ^#`gLOTv`Tbi)hp2H zdy3=0i$D_eCpk!HCS9_!o&{5;XS1Nd!fy%X(beOG^1ux*Kex-kjOW$)@8CT<;Lu$l z_>tao?B$fLI5!(Z53Gv0i_B zt5a=s$;|H0XB&IYY{1;d4oe|y62z?s}+!z_E z)+HS$YFdaSi7D=*7^@o==@}Y2U%t%YS?mShAi<%1p)IDxFED#CF9^9JQ!>c_r`_gX zG-2Lg>UtADmzK*;y+QCs@rE8zzFjUPDq&Rl^FB{bK9CCUD_X1dA#$+h{n=^@+hd{9o0FE>v}ux^DM6F zcW;C&CQ~7U3D1tZ2Xeg~ZIfS8CFLNszWlvuwmj_aD1hx$=(w46T>tV|t_#Vo{D-QL z)|M%jf=Yeg+h4yE;KN?7@WS6N7gl`_^{KNZsk9<{tG-dr`H6_8vfdb`J>l3Cg8>uw zmaRhV&aXAT{_o<-IH^jrO+#nr_s#Tb!k|z3x%-j#pW>DUM)uC}o1%lL8; z$`YrQqIYsS%T|))fECV;841~-Ye7_A3LVWxKgxMq!O3`e(U%~8yh3u=RFZ$Czp{xp z?~=E7@*W>0vZgeKBYuX^>sLRJ?`!h4;XpPldl3IL1mgkzq8Ty`Sa39jCXvmvJKPT; z3U*;O68UM{m8Bf4HW7mJOMI}38#9^iB-nYpoSOP&2JN3y;Xql_HD_hT2GyLn&@em4 zXvQSBuSkl1OzscKAKx;HNS&ENw-#$19r=)PLH8s|0u*GYbQRvwipEU?*7E0kbHH@A zl60uJJ08z2!`fd;R)N@kIs1<~cqLv!)q+&mCfD0gJ$t5-Xjn;(#9vyWcArC(AAQaZ z&R4aXq@iY4$eFwvr@>}=6gaLf4!1Tanx~*E%bC7jCdN%$MO7@g2%RPG)s0W?jNCe~ zSraQ$3vvB`KL#Z#a4Ko|Xp+4_1W?bs9LziVU0VTiASFiG?BcOr=VU zO3}R+pzCXu?}wZLA8&OF1`foiJ1?2EQpdR+_#w^YmU28b{~aYDcu&UAVxgo$h0OQ` z4XMXVTH?TG=nlX`3^yA0a1M9yK1ZQ}(Jr!1Ko5f;Xh79hVTDq!GosW9i8xQ#HX#}$ zttePrQuKV3Zs!tfW8V;ruNh~-2Od=6zJ0yR%VyjU<3G=@{LKNn2BYD1Nt4t~Vn7G)REs#p#Gi$vY1=^WKUDxf@>{w<;Yf5-;{&_LpQQkp@}9~}k1KRO0}f7fgb{`!4=_+2vBc0V5!`S~nF+25B-_@0U& z-tW7AJ`^C%$9k!X@N^Uqeo%!vv5Bxp7T_LJz3c>ib-!f2*jW0l5#4iEX}oN+i0P|@{!egNiJLzs$@{ShJ5EaZUu;jtowMV}hBP1s+COKD+=Ad6Vi`hGjvN zl+cwle$M={+DhdSLJ2KGm0Z#SUcxd7C=R|@xX4Ko!eoCpGzxy)!`LPq2eV^r#Z(Mo zZAG#shT2aDeIi>&NzZoORunc;RRnv7?`|s}-f*wiXlQ*QwWXX*ftGizkWSeF({@;dkp6;fwruELwx9;8~k=y+g z#bV~1>GvIsKpK1l&meL4gn0-j3%;g=I<)Jts6#+BmEP|iGQ<4&ezDpzkENX-ahFFB z^h`%ltTT#iv_EiYo6t35Q15~pcM6QcVr<~4WhXW_JqLcrSMMJ=mk6TMWA8(^vLU&Q zuTC$7KqbgYTzwlJKo_ye>2 z+f;3NCDm}yR?nchs&WEFB9b~+^ zmUTj2y#3qW%+MbI$dre64%g(Y6iSVBd6EAeY-n_@i%pll*)o3$Jw2gG!Aq=DklAcp zPr@0~D4awqUjK*N7!MBLoQ<&W6034%nCcKgH5kX&<2 zRJAh#CS;3+9 z3-bbDzOh(`&(w578Gc7wHlPVHQFG$jWsyP)nW-|=oZN)4d(2KBD>5cMM`DF&H%I8) zXO^qD!3P)QA4VM`i@F*nxHL#f-@t{?;EgMeP}-#T(V2Ekxr7vaa5cfF9?ue&A=R>Q zrAsCbI>fjwD%S&{7wIi;H(_K$DPs5v7HrBX8qMM%#+OXh0jWj$;KG`xDX7y7A4Bv) z-j|_0s`uNf;Wq$Du#R~oW!zXUXJu9>pSURoU#hlIcYn>zn}#So>A%6MZr}@EJUxf* zHW*9_$RE2YBa@4kRFwDUs8JRrmyGLo#07UhFbnPgPx_O-sBgK@8pAb?E>k5LVbP@U~#EzWxNcg;HtL(<{55t z=7IOH9>cIOLJvV>zn3o!C$sT5;%mvD9Fm?CEm&m8ZxZF89^}okCLqy9zHbU~%NXf2 zh+6k+W`DNB=I~7i!dl3!a9K$nnsy^$81sD^3HlG+HzL~3nF3R_6SWtpu6}@CZ$MNJ zfEV1h5%HdQqe@hR|I|_d$R&7yh3|lt0hy5BuoD&`q$QUg`V*MvF2_w^Ai>e?%%KPW zXpJW`Tt`QdXj%{=qjyAz0iSC1q*s`$fRqkRir6xVq>R_%7dwMFPNvMNZ5ua6REixn zNrm7dm>(p=g4jT7{5=jaRmW&%HvSFt_UMxVmsek z^$=~zz7!@Dl5v_(*j%m1{8W3giF3#K@&}6sMDN&sahCgvprFlY#RjQ|WSfIC4@0<$ z;sNkIgx@fMhqPPa)3kjQ<5qA%#EWw zZBB3L)1Je)3Tb2z?{7h{9$#_k`twd3;<0|ljs3fY59j~KRhu?l2!Y*mpj*!txa?lYyT#8-QohtQ2I>?A6&xe{w_xm|9t4td<=kv2 z<+L&b_R_##K%=E#u~wmcvVgW$u{v}STcqW5Px+Die|SQ3Mm(wA;y}~+nx#yP_5|5D zogS_>zw9#FNUKpti<)gKQw@bi+!LdtojQ$>Rf}Aggw8bFitxFu3@@NDk}AACAg%Dn zW!@*%S{cR_^LeXPiL`RbNN#gnelp)WlEGHAtkO)%)Ot&TQ ze1=1nqf-$w>v@W4iTVIOS}h`4^u?n>Xa?>SGQ_J)6C+2rWx#Y!IiL?-gE66MSprY(OG3eR$A_LxExsU zk~VRRl4btmrnJ8OBotd=PvkW^2`wS60R%n(_V~3!Fl(F+fS)>feBgk|!5h@8LUbZW zP>=E-k%%!(LAt~za9V#TYbpUgMN9BUnSt_e0LSK3@@KcuT(T^hEaC)TJP`Bw0y7@A@WdQrGG z?TAx^)gngTA}TD^!+Y+TlPhhG?m!sh%9S@>oGdj-NTzE*ZI@zd%WWw zb54+FTv=lw*;3+r@#1XK+FS&!&BdDxl=|Yuix)56l#$%VGOlcK(KKsw6IY1{TX4M? zpM|+v)M^kAl9b#^>GqPLn4rK^GL=h(8LMa$41xsVJ^X1hX}~toxY9m~(yVz0*ZB}) z^E+uDMfsiGeH2&PN8NJ^w@_&xbM1CtQ*3!YtJ@9=wN*H_GZZ zA<$D1B15Ey43#_{sD82#hceIybU_oK>+^jid@_7Eh%(WRE~8915k|r*+eoB}!RpFv zBZJN;Wsn&m-~_0F8c6y=zVhSvKHv6LjLfTE^)`%1x9K$9*m>R9ojO&Afs=3;PJ*l8 zI8MY>;5KyJVI2aCv6|KFwI!=r&uSL4AY~(VWlI~6axRC& z0dc&E4Cmr(osb2*-EDPoFSLuzMRs8eUo4u%k_m$$xRUbx0U|I65kU|NMi2rBK@fyu z5ClOG1VIplLJT2<5TqDnPP5g^S{*o*{m{Plw4}P@F)WDJaJ=R0qr21#IjnYdJhd7;|RLv zg+senW&RROQ3(@Uf~gc_q?STo#K@LlDg~K-!NAO7iHS}2;mi}aB!W@1XT#WvW2>6; zQ82Tq`94W^AYO179}-mmPUF$=?g3%FA)T?~$H&Hv54}@MN#TfA1VsV(by}%lf;A2 zM}|dVT2eC){esGuRkKb#og#~2+9yU)AkDw9Y=)lHQAkbb$e+};eFFp<*Vz`7Qsfo; z1!JWg{>5oTUb~*x+!=k85gNZlrq&BeaSurCR1ObrJgk*QXYpic0KUeh;n}-1`a3O~ zLGcqW`D|Czjtz5hSI7uWdnbh5yDBQFv-9ow%cBZyBjb7**O3wlr%918CZ(X7ma17ojw4 zzX=2B2#v@)1aNqNd$yOCf~?+M>4KxbKSrAOXj|IVqo2eOnGqjN)v;)ZS!UGTR6FD- z&ZYkWeB6q+rTmmMc5sv)(z4$ z?ihU%rpI<_O%P_UFj@pLIvPT6r#XFLN=_lo;H0O*S6lN7^s@{PCrBEXKzn9s=z`h^~ zm0sJ$&1Q}*Sx{s@JFgo|qY6-bx3i#CqX}g=LdgoyUl&FnnYeC{&5<}+i$z7V+NmY5 zFG?Q+m{=}s4wY~m<%EGd>iDHz(6)1RO&CR@N#h)bZkr3Hb?lNR2~KbO-x`Hh#dUqL zFMVYC<5K2OijPOTpPx7Xg`8FAAP8-f5;L%VXDl!dp-UVluxQgd77OeEx6{5}1WsM1 zCF@NufWYohUCMZY0O&&m35pWcCjlWODgW@u4~cWHkULZjwAuOPdZrX7Ub! zen-dJk2C$&$c}Few3=j-F+>FeX;!=B_-k9zRj=j(&7559Z%;OjfzPEQ+tKJ2pLwadrH z#}`i@*6{MN;WZ1=c&x`-%0}5h$r4CF0#FbzVSoez5hjuW2NW!*o~JV>6B7vKL0O4u z0)fCmJ(L4Or&Dc{2n$PrC|kiwR#*iwc_bzbk{J*#>D=pQW6yn_`+R-fzBPQX;e)TA zujd;3t>J0I2Oo9qTElA%A9i|r`uh0zt>NYACjbKlFlvH=6`Ul~Bl3tKkwbEHL_-aT z0m0bwm;1wQ-9|f9RvBm1Fl>UNDkzzz$m0QtBFW*1=zd$b(GHbW#u=a1*oQs5yc*Y8 zje2N!;LdiSvMGy@MPv{cSwSmc1z)5~cadB=7b)X1n#IhLQ5xeCbwnZK(^ErQvMS3q zp2pLWj#Q}1RGBPDS0bybFsurjE%l=#4e6<+jx>CHynOxa-hFG>@bndPTwDXL#7ea) zOJs3i`PEiueYp0DF3AN^Oeiu?WY=2V1-Swjl*VC-GFOEP6NhetCNvq7frKp*GuH*Z z;5ZCUb0RHjeL-<3XX0=hv{jol8BAz0Hr$48A}{Y|;@wcZyu6!e(;J3{)6mSt%NH;X zUS3{)z{|_a%d3f+NfYrhq@j3ud3iN$K`o+%!vc~~Qc{wQOqZ#di(_thA z!%zW(A*kY$eiAN$AXGFmn-qpQA}9!$Ll_2#5DY^MA&3wnAcP1AA&d}02qDB6L{>$O z0YLaGRBuW*lMSJDO^aG6^7{LRnP4XknwA~Npu4vFAI%|+nd84P2N_U1*h#~)FlheN z;>L?B?RIrI1QV{DXuG^*#b!^?EUwOAV3C(-0N+$*3}5z;6ch0{$U8rh9wl%7e5X|( z7TsG|GFR|;t5^fNnd~5xOoqRv0CnahTnPjf32m8}x2DaGB+u##o}ibadB31)sH$F` zHUKsWg~6rboNZ2qey~Mt)Jkt!p+Zv-!k196w!Wjn$}Ct~FjS^xJ6PLMl2thdH_B{) zJU`B&CWX};chuY~)-euiMw$UV#^eu-17Y!SWFR?bDtMLrU)9t$t;9(Fqev!~eu#uz z`Ofie_bt2&6>pkTJm!j-Jt8@Z$~U#kRP265ccK84U*|2K|v)buq0s z`Y?ZD2?ZnR6M}Nwac0VYm(f(aLNF#2^L^Dx1*8fta7u|soU_e!z8*ovW5w(D{1WWJ zkhtRCX+i;=gG#7ukR)Rb^{lYrLG?MP;YJgt1a?p&iiCV)fic8`;}Ezc}!`QY1YpA`uS4B|N+O00Z_SKqCTns?13?vsMn` z*O+oFO&uYM3z74W$5Qv_oWFDoaUIIesFGQL@Tv3c-iG%)`jn;|Lliz^+Lpa#FA4N1 z%_(s#h0CN1H!0t2gj$`j2EkjK&xd8iKh11@Ag+x4_0x!-YB>#i7@E;&TkiObQ z*cqtBiPh(O-GdBgyG76V`@hyIYV4qU#T9RIWT42d&!KIbLLt;Sl0}L^ZV)8NWP)I9 z=k)T96yT2rPVUc_B?JjkidQriwfN!c*)2%0b_4eE?p5S6K`yqz4F&k%W;Ur&T-;Ge z;*NHw%E3(HOT)7!4*k$UCv?3{rw!JOm2&?%`&T40@6xlRFv{@jFob8>Y9)n?+G{L*3d+u@BbA^6BEZC^b5)1EtY*Z z30Jb_h%ZA8{u_bQhJO=jdpPuO7#!QFn$8?c%5kT z)a;-zQQ^5u==SGfie}JzZ|BBORhgT5l01-{!YC?SGXU1AsZ6kx*#_g$O9-V3DFC8` z;J`o%#8$HlZhksEcQemPNLx*4M`q?di-Idhpv+8ttq_W++!YlvMiQ0 z`n`ahEo~<@PDzFUlmMInFVfqAc0A78@mgLRouf6PzbLR5{r9%Np>%WyErJ$7TWH&P zZ~IT@_wRDo^|t?Xe*eyI=^gF77yb83{LwqTxBcyrp56bR@{`AN=+j^5>^W(t(AoOh zFY!mu^t5N;``=LeXxFw3B(mM5=27*^tyeVl z0u8}oki`wMz=FZvyK2^r31B&LtmLe)eh5y~6{=0mpk7cbs4q2j6cYYs0A2WicrEwo zG2!DcINXGl;3W8nxBdMl-ym=N`gn)Bu^0XK(%1LGh#FrVo&O%6{=!dx`fYpBf493y zFszsN1%UK-CQC*KtJ(#FvO(=L&^3oGwicKI3du<18Z46HB>1O8&OT1l z%ifFN7%g%-OmMCyCzDgbpUFlKR{|Hqv2X(Z4-_e%6B@pexY2H~+svuRNy&azc_MOB zvP10bvN86CWwECdF`fc(9LI4SlZe^r*?}3CG!iUuK%wn$AQ?eIMIlD)DJsQ)>M~&i z2%Z$Z7&nCKqY)B2fXaapH#MD_OAV!z90Czy1b;|StLLr0@A|QkiLIi{mLx>@S7=sKl2sees_yM36^HXuTXp^K_1aE0A zM-MMaGW3lpcI3gFp`%XONF{tBK4|p^h<`7MJ(7j-qq~nC!9c$U{;oZiM1LUgDR@a2 z61(QGl-qpue;IIKoQ^!&-tQymX4<+K0KuchLGcjI>$XD#aWxId9M9o7qS?Bsj4-{e z7IsH9`d}C2oIVlFau(fOgYK|S1JZB~N~^$t0UDX&)$9wavD3G@JPSjN(%mpIL%yjD z3iN*;YLM5zkpo0uq+S7jj4PCkd8~jxMpyyu6zZ@WGG2mi8fxecATJ^J828iUDqan9 z^b?r)u`zg}9NPJM4gd<#>3$?QgCfhn%{U7o7(U>{oU{QarvC{9Vwi5u*F3Z!BZ51j zgMj>)%EvoBRKD$d?)*doat}VUTzP?LDws0*2Rn=hcC7w@Rn!#n?^`{b^VqK;+F%5S zaTeLlI%J8~!`)0+(&GEu6zOoc^rKb1eN+-X$9$QJhtM%17>f)tBXNnC$N2gg{1KevY61NiS4d;-=OYUKX!h@i z4I6F^UrOD)$ z7wbKOX)8>zK_67^#9fAtc&ppavT8U462qDu1ss;LFLTaZnZsgavKWkAMJ>cC6ZAlV@NxktHX;eO{X=SE)EWB8 z5dsJ_P`9853#f3ILP_S56K}=rJe%Od?c5~yXrgIipL=;7t5Mv+2H3`{F4Bcg;>}kE z)R1Z9qJ;E}7MCUhN`|amy7G^BB5&e^b!`aF=@|OizK9VfA0KSRE|K>ARYV3CFiD`+Bt2e+iLo z>a-{r4?~lKHXDy>=gjl(Z7zP$;hf|ZnbOL|Ck3pO{270QhOB`Hll6Xd;^!DEW!t7H zjp@0$JQr5`q|-LSn@j)z2tfe={Qo%+0!#n^2mk>9{Qo%+0`y@3N=tAMOW@Er2ipNh z5oCj3yMeCA6&fDUltbHDmorL)&UFs00Hd)?*M7eXZ(7=)dkm&hB(BzX*PA@x2XYXg$NVOXU1xu|Q`R-|?!*<5vf_ZM^`&7550#&h=#z0M()>#)Uf*5o*A za-20e&YB!&O(DyP#KW`TiG(6Kdp#1I9Xa|T0P-;aK>-J@KOhiLJv=@JFz~40Vc@y z3$v0dxsofnk}J8AE4h-e+!^-m&E*YdxD)QYt9B1Q_~3&Nj(G6F2NhiOrmkWdB}+z~ zM6PkkVqw@9*oL*DGS0@?IL^iyie&>@!DUAER8mkYQZ++KPc-asnM*kn5sR=wSXJZE z!B~Y+a80UW7mR~XU>0}4Fx#U$qJMp#N7+TYSZ=p@ySJvSsr9sq>$i*cQOpXaciOwXdlS=US(P(;wOQM> zUz=sOU~;e=$zJW1c9AYus|OofyPK9S`d(6bKelMCRO^I|x}=I~OKi#gZK<`SE4+Wa zKZrUOn@Q}V7j=CfboD`3U&K`uT0TitUq<=aqK8!2+v65trKP&sw#(g$7)`7586w9sXl zie-}+%?O%&a+WowS@R*y+qWX}NlFIOpnX?}#LPuM=Xq3BeID~oS6zJ_9o(YC1>V6U z?os&T`tJCg&%q$_IbQUpu0O|BY?|n08QVo&yLR<+?wV_Ot}p89*Ia)sEO`~KUAqqb zOV`Y1&UiPeoAjoJB6+X`%a{#Ecfw^(0hCe1Fb>`ckN#%-SgROKMw7WRjpu8y3fIU` zxW)y#!zyiTCTw=Lv2i-_vCO=KL*x@0g%0<_BQt3kg-rH3mTj=I8pfQ8w%A z4$a6e>MGDx-J$&)Ok9>>U05z_?-ByxGQ-TPDtXq2ywF~!mnibr1;}Oh{~{anODsff zbnV&$gx;t#H?uzzn*<1pkr+crAu}?;WH*xlQBXM$3Ww1^Kv)=tFdV`#2!mh4N-@mb)f5^7XZ1|{AwI21P^zKqh8^58bjz9y+!yxb*_IBN>OAz*jr^fGBET5* z@~p`6^5XJiqjp5`Ez?q2fPkZRN`h4xoRrV>F{q@NxzPplDo5Ttv@*RUj`NaUu0FEB zd7Stn(~RsDh~^y7n**9AmEPy2-T$KgYXh=@X6q&z7!kLdATQ-R4VF*2+sX|i4RaE@ zg!*4djMAfC+s;iHs7Kolq@K_7w#s8~b(^PI&qZS=xERRBmF_RyCOBz#|+x z+W_So24Ra3)4m+T|IxgvE~nbezR7JAp@AXmfGf$6C;|sZSp3pIy0&L#zF9i7j23k> z*X*uY##wfF=#yZoR5wi{#fISr8iX3ScSLw|nM(ywVpS$$bXFH1~quVQpL{nQ} zElVZ{RAJXOkl|jz%q54A6jHUyiMk_)9KyR+%(x8d2!oMy2iDcm22aM#{whh$^-vmn zU@CNb9V6KjyoPmGPtq_5FS@&w&-Mu;or*Fv`T|e)c>^twKth0W`WQSrX#@z3i;7gF*l8E0iXx7fb{8vj--meU>yK(C(a53gAsNrbr3Px>98tjqSC!C|qEK z7Ydccx*z37A~LtCN!As}U0ezq8p67%>4JrpQ$StZ@XKNAaksXip-Pkm$A4?BNp{4u0V57lplWSqefaq#@g-nO?lb4D85BwU;&rux5c4ihk zfiO69*vo)|Q_elqEX445(eNawo{i>xav&db!Vr#N5Un1<`Ou-lYnk*X3?Et2lyq5- zO1C%YNa_GajVVeuX!ui^3T>L?0Bli>8#y_Cb2HB?P_hQqfs)@iP>>=shant63N@6Z zbtLXMp0%`Jm6i+VIfj*c5=@yxcH0svqsTjfEYXm(;SPdsY=zL~^d|gtSx?1}MP9K5 zI~GB90?e}4e%mU`1udYOy?Bn|Y%~VxVfP}8D5CNpu`S4pGI>K4-a($i8vUmBmH7Gd zsz^-u?9&)2^{o4yN_FZ*;e%2R6hI=!PAoh4{X<+~-!RzMxjQ;xC}AEGWIQy?;=MLI z7a<%dNux1TEqTlMvt(5b{i06G9y-Wf#iY{JOM$gJU=l5HMp+*)E5`bHr(wlR1L+J+&g7-=_9W= zoq%C0Vs5(X{%q)!C+X!fno4kv;exv{S=8)oZh^I(?tNf$=B*9(4^J=d1@E%F2)%~wcwqzC zi9P$4xtO7>9OD1k!{zHs^ZD=S9oe6?JW+pE$3ZH79->o9OR}7tDAx6~sRooH?}w*b zjO~mU;TY#3tcB3^1T(~#H=s1`NrhyLW!1Kq7l|6a+-mjZV8FM*ODe9v)qf{@RS?E2 zZ5u1Y)x*<4H;<0;Q-(4+0vGYKksQy6C&j%e>rrUz!5}8w?$84IU#~{!m%xI^-e#`d{~*zf!;y(G>Iv@Z#@-343lnIPSJxpWGP||gfy*M`bWuVN?f+-U z+yfjdWQ8vhDxZz7e6$SZS1pvivbk-FY$;gZB*bTo@1~@niV8Mfl69Z{e3`*qCJsck z;%*-Y0aEfml&HQI7ybYV_fM4eD#)Z+k2m}$0EhBrJR;i|X4`;ZR(}nhf`jEor6QaIaik$yBc_EUM-Dlh5THFJukT858nQ`V#-<8?Z$e~nNDN!kZX;u4OiEFq6$<f%1IP~lkf$eA?iQ&_VjPx;jBS+I1;#i3|D&)@+jzw*PF8Uxza# zyeSZwy)a8;N{Z~pIuN+#--!Z{cvfx)aSm; z(xs>l)p-JjVHk#y7N>fuD{i@i69$I}DTT;7KsG=gfGijQD#1!K5Qu{y=qRm}P)a2G z5;WrQjY2NuRv|a7V}xPjp_=gjppIQ&U8o$gK?BW@A;Sm9Cw#JXJV<=9X`xVQ3yp+C zQZ}>+9vl)lghe>DX%RvtpGyNQQvAOsfQLmEpMv5kC^*J391e%W;go!8$;L^;B39qZ z#On&& zRaF%{cv2rKjs1M?uea}2@Gx8&o`);H__cKDK3A?j#r@{}mc3cJ-?Dva`%Go8KhnVA`&#x{e20N zA<1pZR2eZ;5K;(fIEn6g5g}P1<0GA;on&Y3IJJxceva_!Ah$r&k$kbJJU-<|#Ty=(Wm zl>{df)jx^!N9><8!_J?(nNCx?--k-yMFH_*`$_XLt77-{0Tg??5s^Rj($fI{W|U zkYzkljEj9b@DLPl0XqrLUP%PYTWJ@P;n0T{!7;PkS(yzQLtd*S z5mso$F2urZJx^x^5_>pw{OB!ycwoS!-)7+CKdwQ8ZvWTmUr9ZwIBiFd_u7IR5FuZo&X)e zWEn0}2TuhGeKZ00dcHBs@u9NTOUf!*`P>#5`;YeS;p83{29pt#raTK5!* z%5bG_xibHzStui(C5m6ue-ljogRH*;|JOWcIMWSJl$}ULgAS+b5%orLE4K=SH`r~i zMn#DbRA54paPy?%lS4Z1{{J^|tfH?dWm&Ke@Kd!XUG!_C+J}+Ryz@|5@YmVem6AS$ z34NZcyquur?;!T(4xm+?3q${l(CSz)sWo0y`*9ST#lrK5W6sHF~b(xpIk@gZ}?5V<_4E=wir2ag$?Lb(s7=|P9-J}JcpDDYiha~Ej&~HTYzLOx1?>mbt>HY z#D}?qm0iX(ZdZzFUr9%jj(il;Mmx7WMTuIJsG`Kx(;lJSqz)O)mn2rozmY#hlAu_` z6Y6FcgqpQ0(&g|u1@+Mny=S@9zdIb;!B@PYMo}ojBV>$0N0D6Ap-NR$U5|p$iSkhM zoKoVj@nWN)iTu~#fGsUhkTm>fLO& z%+y;{a25%dBA;1^Ma5BJD5FP)WKe96A8G z#P4PxlpH4uH>%(Rp^zOYAh^YGNGD7r^7{(vxm#7}`LJ*+9f1s=8Th;|2Xcpp1n^E2 zv)U9xw|JG6JZgCl+7mVMhECkK0_$-eB|hl=Hw!eDJ%m#5GVjD#$9`#u6Xa`5(pyPv z;m}v-0r2P)L|nY@;TmMwj}?}5;q&v6%{re(@pN8V7Uhb6Au7z$Zxk}_tslxD8|0i= z6Q^eD|EXjmr+b6jB#>kLQVu^Zjx&{uG4KM`@`92!FWPNQ?}@NqoT=Z>$@;CZ>nuZj z0O8-hX0H1_dGc0VDgdZ9uQpW~rH&x0cLbj|$)8CvG!y zOF*;wKmb^aQlecGDQj5SbOb4~kc2||k0O}HpVFY1Q3j#Vm(qmvz=KjX?`;0vjUJ|x z1zAF^mq%i6WjD6*)kTFTs3EG~Rqf6LaRvE}X*8sI6Jr=sOCmdKE_{Z}ZFMu*7RN-0 zN>)hoDQ_l`RWU#v6-+IWb793)_L$w>B~LMdm)~wyxnP9(uL5{WjliRLA(LN?^LxI6 znEhp$gzy(=fZs|wn>ersoCB*vVKoHM;e>s}tdI=N+Bb`@*V{}ZcfKQv*G?D5fH-gm zWYv;&H`CW0z{>Ad7zQYAzo^+VNtCYiJpmRS%{e()44W42Mmy7te_c;i?Nv zjIjXPX0W3sSewCCO@ZXYoR02B0JIGvM~aBa^Qlx`i%^#P<<8iubVr^L>uR-l76b%P z0P0G#4oL8>IE?{frK~MaOPy?|Ov%xXa}Ydr3+f*jMu+!ekb>z0uq_WK0ts|L24Vjtc zPjR!elQox|Tk{Z?F1-T3Eq8a{R(*15$x&AJg)wY$x8&~bs!w(|ec^7C-Eeo;3}LH2 z*&8%B%BQ_eD_e3G{8skr?u|?4PHkLr>eH-QYrI27oMpC}0PK)g_>Q)!gdQkcKp*A)TQ$6o$f37z#s43PWq? z(xpq6uAnt^q$3^aNLkR4)=-g(RHPy;1K>Ak%{RRIbjl>aFZ@E+-pazSFmG0BtMa%C`BjLX&XeJh^VQmHx=Dm5}<8jU(c zIyTYv34J;;noK90$#`a_%*G^7a?1^6;^*#8^xUmla?QCdXRznpmRs(*yF2L98`j+O zlexRQw>ivBG8ekSm9Qmrh2JM)XYSROd)^&h>AIsLs2>oD1=2V#UeCtk0hNZDLhayJ{ZLO1ObtFNSO`3vmwHOU(vYDD)r4X+sGQijMABGD zutHjue8uKw9a%_7o0t_A4@>A7k+P1PmE5c(icUlm*|aF6C~QiXH(_)+VUti($yaP{ z*0GM2tm8Bz*fgWjG$T*QIZa48GYS?@i0IM8+X&HAvQky4ARaSREg2&dPqmeN#C*Iy zzQ>FYRL0|nmLWPbehsT+tddo>OC^5|Ga&MKxGGu6U1FXHun4FKn_t6X;E6|%(j(;| z`y~bwJK^FXs^l)mHemuH0?|M=A1*#h5)jiVJUU7cMe@Maac#10?@89(kzT`VN4JS3E$^0xilBOwIMX4zP25I&m){Y zj|^JfowGHPQwZd0jG%D~& z0vrXF9_eNL0jFR4l4^M@14^y*YfxO{sef_t2zxM-1#EASf4)TOb0kAKkuaE4DPi|$ z8mNTzIn#+jm5As^ufLjnxc!{FsQq|mSx4-Iv+)_oH25{}&je2Ijg3;A{kPjj27dB^ z^Q-M0YIfP1WLJ4<)Y_9NVs1%$fHy25MrW}96#CW=axAw6JWg%i9 zv3V%q-8JqUq&kXY5`$WhO-&aOGW9C{!bwTS24IG9hpTe0JEJ%eM(@uqCry|7MauTz ziwn)kf>^IttPn30+qF7DP5^FY*Um1EsB*$2BEXvR0W;S0Q@9i8Y)!)eYzMXU1&~P) zkxNHrL|X`i@1Y$>gG?GcM?+ynUn2{}$*9#L>4|uq0%PSZoUhSUCR&7er1Z;+4c@C9 zLx-g|okTP=Ci&5QU&YZUR>@1BnJ_5C+BC$ZGhE`11 z0$nV#q(hV_5MSLqhJ@;(>Ev82(;q+MxK!>k#H1T4w`>8F_s#DWm2VY``zHs+H>37o zo~_{vRHzDEc>sEB-4~F+R)$8Oeg!6=;(eDr6t!b4|9Y0YYGXKikn}vq2vN)xKOAV% z$}<)2pZOZ<_?SWO4ZdMO0c|zY#+9k7%+47ovixJ2*DMBB8>SPCFB+!KfV$H{R@IP) zGV8>85pc08q{krFs6}{!hr1N&T83=)yVhmqiJZ5(i<#)$@9NhXFtdKClL-D&F9>@t z%ESm0?h%rSeUUl0EE0WTzb{^4l*@cqU(J-x|Au*q89A5VZeJz5FAg%=ofo#Y>8@~FOUi-X!5Z_Q4m_2!PX_#Jm<*t ziMVSS0-ukYZXZ~8x9sTWdPm`cz@uC;rd4MKg9x+iCTfr+Ca4>^=zZu!K*Q}617@Tm zV5H$$B=q`G&ETWO8KF^$AcLGG4={g{t3sclEphk@M!GA&)kUK(dsl?nBq@}~)j%L~a&LBCzx8lu~n zh~zcKfZdWaO0aj+gU@A46b(!dT|EH&kFZ|esc2G18}H~t0e@0Hudq%>r(!CprQmjZ zWnWCE@}@;7oB=6KF%EnmG>y-I*5PzGq5o;Q3>3*ccS{`D{R!=t!J%W`fkTJVav4~X zdG3~Iw9XZV5DwR{VQf19mUF*MpjO!&2r7Mn;0-=?fe+i@h9BK7K$afyQ8Yz|cvROB zEKg8BH->DwKQBl!w+t@xzx7}zZg|bj zJoaO|-gumQY3ek!2OJFIkv{x)l_>r4HX>wOFJ22GPfhHl6(D7e{2fDQmGB ztFG#+5h5aT*cMYuWwx3RZB-*gJ83amO4HGRI#x5fBs2J=0XbZY)l#AlYdSJ*Yh`Kv z#bl>!)zivveop}~?*9Kd_ha7i;!cON&mC|FJi1(d+?B2xFVZ`|`K8IjJj_G(#lF~= z!)@FaVP7%*8mC#U(>L^uj;Y0ZYoF%n9sP^xt$muOw?_SmFL5D<^^oR?X`klltxYche;J@zFwO)PnS8LQpCjbIkIR$YwQ+l-o0Ekc*OSH!ay7}{8RmU3(v@-J z*2zKAwPj%u`!XY=V@Ae{^k!dKn1yb+n!jOs;-1&x-%N!Se>7Q$ICqRh)qNTqk z5S!C1OMlHDL9;AzS|9A9^h}=V#>~sxjTzYP_-J)r+TYH=HpsE%(Jjyj(T!UoXX|W- zl$n>e8+Xi&n}+-zxei&)-vMW;CAA{Ol4_7zkW!FJfCOc6!NP(C1uW*}GG(w88b`!W6>9fH)cv=n15C}d3lUZUQ5wsP|!2}=@?7^H-1A>L!DSl!>n!cm- zTz5M*y$18Rxd{4uFn4U$gE`Y=L5!w?&lp~9FWLzs3-~=h_#5g%w>Q-5AD$E%Pj#VVF0s0Ouvr8NPsGZ zHBL+*GVUv7>VZfRUHtatw0;5ax9{_yiILj?_>O@In@;Xw)gucJh_Xj>>Gj30gMN9| z_WsK^j56{=msWQ0jf0LUWye>G^naD9x|?0f9{}UucloxAtM;of zYH|G3)&m)(97|l`m)#>q@bQ8B)q@w`hK>oco1D~md0_v? z{tFEROuS9RAhO+cI;^uiph+wu*5d&FjUb|2@sJD_e%&| zLZd-vX^i}AE%G-&YVYlG`Kdrp;qSvG>)uj?0Yy0Gu9#W#7zfmEMY_~sUV``l%oLYn z!fM~;;;)H=7V66-!SELnlB%cuT_Gd&ylwkTo_Ptj_)dW$^gz&N89;5%VYcRwCN?zLU21&htE>{Xornj+LCFgB2P|$Y ztpzB|l4Dsv1sDs!RIP2*^>bM6Wxr;-gh8x`j7LT04(B)%N}JRz7H7Rb0k!E=hAgCt z5D(HD|3GROQq>r$$9aB?7%%dA`9c)1tl>AqO?Eh-^;~Z90m}m$;H#R?^@f!BvO=>Mpbtq?KnL zVi6K9;QiT=7#7#6WvVS|XvG^JT@U+-Hr)_Tq<~#~zEoTi%O>6qhZfmFubbXlTH1g4 zx$w^;&N1rqG%_b3jGPUHhJWxy6=A2NQ2BwpqwUAgXfDsllAYQ&r)8$?pXc_E>p67R zN+E6E<)!&QBDhgsabJ3b{GVtc4k1(+VASibz1K&3Bahj;xUsh|ppua^IZx2exsQxv z@k1^av28H5_Fe99K}6Vryu<{2I1-tl_$Q4o^)roTs*<9C}oFPavGB%PANLwR2oA3>(T5WiWN;=nl$Hs zRCh{04yB646)7lTTHonzLagZ+WpY`MVVW9y}Oz^61e1zZZ zz_-w}&vite!;>*61NNMQyi;7o#ou&hXFo(3Nfd?IV4b^56`woyIdr5w=$^(J-v3rX zop*K5ItBsg^bqvuu(yqwC8%+sHw3&tsMiH+_7{vC;!{tzXC7Fr*~W{hr->&$Gxi?7 zGrc;BduDkGyQ%A~u~X`+w87zSrFt@>daeOJmVv$21ivO|mk4OUVO}La7UUJSl$$F&KIFcZ?U$#BYob{!m;j=FZWCCLBjo6bNhirppf6hD|l(jR|pNU1;Ly|P-v zn0>7H#9u7B#}fO&GQ|6xf~6*ZZ4{Vz0qg+)xFE>S$j`*jxX--L#LvLb$j`XXywKp8 z6!1mRj-o|x;(OhpSnRJNd_A@Ui#a)*tx8!8ypga$ja?UK0^~ISq#pssg2#fZf~$qE zudlDKxQLjjg|M)&u&}VOu&}V$(8$l&==g}xn5ekO--F+T(XOK*#e$rGncahe z@X`2#ywItP(xlSR($u*VowK`S|(Q_k!1g_=3RJ!0g$;zL5OYse^;u+V|J@)Pnnj)6~F_ z)z^a8*Vosu->BcogP*X2_JY-$pNEmXgN%ou)#!%lh7$gRorkHYrQ(8vhtqjna>>jl*61SG8LjROQAiuCD=Bp?I=sOgRY-~f++>5tQb z)`EoDgM#OTp^fc=xe4Itz>qW;FbBku2BeZ8EJ#c+2#N^o=Rg=NFeC&7la!ML12EX; zG{`st7>kJJEXV)>xaEt=gPn)fpNE^Bhp4Fnf-$J#s`&xpBLsmMigOGRLkJ;->~3;E z43dK|4rU+*!;~S05JCuHgoucUh=>SDNs@L0ahmF^TrfdkRP|7-gD~ZbA0sN(bVIA; z+1HGsLpuW}Md(D7Yg#hXSVRF8e!9W#ECO6Is?frxM~PavPAGZR{`ZEJ-Bh3L&>@!u z2INR0T-)BQp!DkuByn79qGk?3in{U;-?f8|*S75qCGpq>zpgAjIGY#Jzg$@8?Tn>3 zYEKqdHh0*Q+>kyFnbRE{`)rbSM^!Auc{z^NWzA&TpX|scy`SieFI@(YF>+G_vG za~jglu*)xr9s=!nD}R4r!xj)DlOnXOS0%&sXj(!{sta5Rj~&6IC1blz6BXFAB^8Sd z8o4xK{tc3q(o(|Lzw#h}6E(Hw(#4Qd*Fp);$;*`iOe;v4idS;%VOF{J+cnaC|u4v2X&#CH&=e8Lpd&}d>S|=7@F@7H>6qh z6;Q>BN4z22bl($Z@z2G*FAPt1u`1Cgfe*Iqg}|2xQK;G}HMzY;-;_n;BoE(7fPiak zMKr}*XksFEF9lu8?~iSuLDIrFLfuZQH%#2@j~kfJB&|MY%?Tv0Y{Gf}#8T9)E@y6a zL>5QWsdZQ;9!xlUs^=^N?mqc63I}KAw>%ipLoj>H%w_>nx0y?zy9bU_HYi7r%nzg^ zXL4cQ`VdKWfT(N#aV6WxEm|`XxLh#2DGrU>7Whm*)yT;%zo!1*Xsd# zQBT>#@?0=jY<#KjMY;iXSraZ<6B_NtCQ+pC!W;=~3ZU2ZL|+{R(4RVOQ%u!xqJg2} zgcCY2YP~nU*u=cEDTexW3f)-Pgx_{KZK}#KG!RaqMD(?Vniq7P57g%C^-69Z4FP3m zR-Y}((dVK-Q>a;~#8<~Gi>QE_IS9uxxH-$5a|Qs|(N^P7^Gb|hfb zzb^VDPUj(w7EViGM1V$blbmhlBdjmfPChrLaxg- zuBC~7bIC0&Sh3)ZTB5MgG|S`(5F*)VYxosmPpsIczu>I#pWBH5wS+TCf$)l28)6xA zo`ZHLLBibN3L|q16MVIzm&``GePaWzNFTS*J26#Mxr`hR0B3t|v(^lyKQx`BSWM-9 zaW>oln9t2X>AZpeqSV(!;91s9*e+$t5m0V!2IsqQhVi8oVeqGB3=w~_d|RnbB+mef zkhC9IpiY_T%6lWpzQdd#F-o!q3K9w(gKbCfF=G-a>DcLxUgSr`oYP=&k|fNvwA;<_ z+bt!z9r>adFRTTxgOwu=Ua>CCu<=j-bP>z0SDP0;Zf(BEzW0|M{?xB)S>A5vXB z)NZM}RZ~$J|2fODG9*}FAe1p?tzf_5%fxBAd4>=OnPj6%;Y7VAtEh7BZD;374gB@jUw^6uIuV6QirVF$l)zM zFar0DzDn zO8^nUG6amIxiY+!7*H94rI=V>JRB83}-b6$TGpjKm8;jf6lD5gZ@Rma=j!R|(7rbg^{T;@F}l#RZ=Pw;%~QnWhlLIRJU|G>mA- zAmU-B%nlBv0VEQL$)S7zgGyzhOr}%HEqNvpj}%Jvp(=ftN+yeSL8Vf8uv7?K@E;$m z(&=E0vThJ6RA)~=Y+ev z^Qf4eoTNEw$xQPTjCZx7i_Y%&Lsxv==rT3mnb1Nwr$o36yHge%AdF0U?sH42y-1Of zxOs5*>^NjZ52bigbhYIsNF0WQzdoXOo%voL(P!i<`iOi*A>O_6`d;6Q9pyyEe!Voi z59914>=;XN$70wq#yDu7Xa_o3PuOkJb06PtwqLprug_&MdKpMqD*She3fh&fTfa&4ZT8Qji54 zex#8O3@5YP$-rCJedXNUwQF}9oBVFe0p_%18*GEKYpik3UW0LtCCBo&zx^$JTef9wf@WGba6u_< zubV!eiW^Wwl?F(&Lv!P&Y^w;WsJdTN6!mQwWTx#-@LY`Ue(JykVcwGz5Eumo<-~&z zK0r%s{{h;gqA%L!r%|4LLWa73@+v7Pi5GjV7mh&QQ@A zH1L^&MhhqlB}XcnJW@@NEfb?)%cK=6_KLUSY?rXMOlG;63AbIsd4W|bl~M|zI4DgG zoFC?w50n)+@Ie4cgr$baiO4V_-I&s=OJxaxl_vE_x&v`F+0p?TG>nX7$RL7Y0TW4^ z2Z)HIfWRQK;rC}R=)$yaFqK}E{us%k*jmK8ig-meHcDM1M)4SdjC((xL zB6<;dU?GYj3gJgRiH6jZ=%}7CPW6oO0Xuf}93EBlgrRk4Hsl*Jf%t)#iJRzxC?iIZ zJA4(gsp%G~sETeObf}_RXbY((*i{lLx`nn-jj88;_bbHigxnWw#>k=lA>=uQ?o;<8YbwKAg7LbqL{eroF#U%W2P(F)q_`#yKAdzrRoGfv~&3 zPkw)&+>h`hgs~CMD0Arc}gHafUattYi5Fx6&0W^U_f*6T|1qPdg1OtbMVL*5o5Q70> za3Bl}0>@zBAQ*^4ahgbZaFzgpePK0xkFbAJp(wiDs9$c#_XNQIjb%;Bn^9SoIwYrx zOY+d3pXy&DDJRFpoS4^HR+YkQLYTtic?~Rg|MP%@Rl4_P+>P95NRhfRdQgc={&#)h zfnNljzmWS41debU%4OpnQu`ZcUjtE}vbTq4M!T2n@Et!QTk5%6!X3bz-?JcEuSvha zb9CDq^JTExd-sC{j0957J2{t1ZV}gu5U(7)uJ%)jIpW=l)U=p20qjwH1|-{CPe4mz zt>=+dNG)V@k?jmgikKlsg@1f2d(ak22zJDG8P)UGQ2>L{*sG$cbQHHS^xEQyk~-WJ ze}l6zN2J#RCD29@NCOi%vNz_?RRCg525|@=#(wd^qtAIA8k={Mhfd%%61Dtk##W*{teWdxRZAHY&!3; zw1u6U-r)0ZbMqC5+;zxr(xsv~_u=kl=bTa7PV1+2H z%&sh>Q@g&Ce}3?_#x&l$L9=QCmoh~e7~!~vgiVzFF$W3u^jd(WEz;>$EVlk@zlp2C zcSl*7RqPe=8IdmkZVmVonb8iS!y{<$thnfC?Y#C$#?3v_Bz2Ma^OTuu0dc%l76div zVLulX0MVQI50FdSTHsj(sq*==MY_PxiY)#P)QQ$ff1ea^f}YH0VQlZ}+=-0jCK!9+_Zg$QEM=BK9wf{gNT2n20m=b@b%jlfbTZ;~sdpjD z#f_ZQy4c$Z6L)?yNl@&iP@^b9`{axaH!^0>P_ECaX2y7cBB`XhDABgg=S?`9=k<{S z3olP}w^0`Qj$ESUJ9o`A11nR%q#$CBa!`o+F5oYGckx;z$fY#a%8})eh2{^0ePcsx z9g0-(x8aPgjR!b}bYL%tCy;F!vPv&gx*afd$T$!c!BYQ>J!^ z%eiM0DXp*UL3r|rDj5bo`W}r}tV7$=z_(*9B8c6+p@R)8nt?IF%B`P3EbmBs;rm72Mx?d-q#$FiFP*w-X>r$-*qI;XcP_jdlCY8gQp) zz?K1l7(v+|K=;x@F^r~Rmw?aMAY-bXQOBTouDsULxVbN$X8e!Ijj1aJ#n~K-DuF!N zf}(|&Ft~AfrTx=bNC$zU4-cpX93&V(E}07@xc6n%!Je_fSkF&m5SebS00X-XZ^JSY z)ujd43btI-oAonkiqb=F_-UkDL`QaQB7M+bPN5`0S~SbrN2SXIveGdKd4c$RA@}-g|zVwtEIaET>KM$MKcLUOKupz z+u0;AXTIo4#V+0(@z@s*cY|&>sC+Rjnr#7SZhOzve-R7C0P08K$I3@}9ZHMO>>Lc$ zYo6UFkKv@!IpjMm6}th+JP7GqX*#xEM>|pPJi`fOv<(c-y34PF2$?bfs^UjDB>P&+ z3VH;xaQ;@=fzw{~$Mn-u29%I=b?9UtRt8F`-))L-|HEh(K_X(iqPwL0HCT*uivH*B zdfhF@YFiSe`=EucIRWq|m5V&J#NZZwnb?`iz5*wgV*b-c|4HD%C=!&+jgt>6!!Eg# zrVpfI&|IsIiJ+@331mlZJ149z^uzzrXBHK?b76emflKE6nNRQz{K&xi_QWK3&=1yw zc4XV{&Yzgo_Rl!h8RMFQG0<;@%Se3SLWzcxtJLG5%kJvA+@D_xz~+Ctvf&dbXd^p7 znM&D?#|$ZL(fWANQp+|?@G*D2Y!^-gy9KtSZ$A& z1kSHR(-1Dp?rw^%d(;&Rn<36c$7Q%MveGLiBl2v5*_loP+v~X3B z314Q@Ps1*QGi}Im>q6_87~c_LW!4fgqnySS*b-w5O{hcjZ|}RA>Up$+hpM7GA~*Ll z^dh-=`!#QqC09T&Z8#0TMtZWajWaX-Iz&b0w-fbD2qeq#=F_DO4{TEK!LNKAqCF*X&_cW>hEJbQ~=Clb%k_`NT@}>VF?35|Z*hF^c40|psO%^rQ zT&aFeR*&qa)Tv@3sGiB%`5LwHln$oj7>{jmjd0n8x|oS-Fc-55 z5B7091?VG-D0(AL9-VP4!S|(FK)vO4fMGhm}4I)@J*iqfxcmNCJkw4 zYM~Qc2BMTVjX+Xz)nObvSeOJ{JaAX-+og%_6M_5#aKcxu5x^a3c;>~gKJEaOxk|Ed z62`E&Z9hFZ4k{hxLwpW98j=O%*n8meN#%wyHa|C6)#^%*S~`_?JLsdiF_Ck}?|>fQ z2Ul0^RU&6p3+a-cOF4k;n=nL?FAyKEtVIl2s|*kcQFt0cF%~(S2ISDl!ifI244L0H zvP%&-w9jV4h$9l-rtTCAPcPfDJh4bBC#ehx>C$-SVK_qEDrQXsL|@f;o)BDV)GtE= zKCg9P!fStliG-eA)BiAf?XPH+cdgD^84vlVTIbWv{H5SyS*U>yURC9Tn9u%!ivDE+ zqklyDUY>ChK{SphbwAa5tpj>(^;P5Lm|P%+FMnvA05+D`P1H#;uyMYH(H<1Kn)#p%qpTA)12uZPr>@(Z|udd_D5*_2J}p{T+5(A z;N+HYkJBHPPM^x3FAJ@@?py@(8CGu}XH;7(4;>hZDxAx0=??bq4#>(g&ZwKERNi-- z9+dCrQ6T>`_b1;lt);=Tr|eF8LkYrFR$LlTY>MgQymk_`PEbHI9Lf9(n7LiNNR?06 z0pD&_@l|@!u(KHcXWk60xyuw`7I7etB7vq#{9=jM)bMs*_9ISx14!gJTL0@PWvQkz zTFJRxc7Dq^2GqR|^BU!H2?`S$v}DCp1m1={k2Na8L$`Qie<-ijty0cqUvTF)ZlbLi zH1IKLy%anyk=+HKjh=+w#@g|VqP6okMO?<8^?$fce45t*O?cIG=tHd3dy7_Ryk z)S>qmh#P`&7lAh9HlasdZr2A18`dceT$4{@Y?4$&ylxo+0x%G!qF{nl?Remkpq1ATffHTZ z`O+GV0*mx=nuHgL6IQHA##{~hqh3ZEVFxk}FB-HH)>3+l|9STfU!GDYVO51=&xLPe z>a?X3mi`=bov@|)jJFYeI$=wr&7!FG>CLeE(2(%sOp7bZ*by-l{z^A01;c$_3r!$L z0+jYt*> zh?M=BG_((Q#O%SMj&iLeVEXz5^s{{YPNq zl1zeQQLb*txCa~;C4}9f17{$^GSNI z?G*?wx41P~gb3}*xYO;EJ8dF_NHeui$*0LM9V(m-BdY`$8t3K5!x_A=*Q^4WM>BoZ zp3a@h3nyWHy>Jnv&RCGoTz>*FrjCGJL-Xtq0Ky(~em>3d9#%ewbg60X7w)*FGjs6i~Pk9WhJICB2In3OD7Z z%iWQU`Rxic&6J}>hO&vSdj-&6x=b|UV)GNH;lw+~e!DUwru>(%y$NZizVsDCi5R-2 z4(AB&y{_>8&hoTl)l&!T^uZNr=&|cvKnYnAWKLZP9oIJve?UHSg(nszLq4N}h;K|l z`@b14gF~?h$ZkFcMfsfwOGD4_K3<=)w-EfuxFYA`167EB@UHodlu3a{#Lc`^7vR6H zu8ED*Xthg;Rd&tieOttlH-?36MXBY)oDRgB1ie=sxsElta`u^Fg>>Dt9tFCoT5n;y zy2b(9wzo&~*Qu=_rCC#pY)u7vhi+ooMV#47+NCa34|jh&;-?4K6;q&2)raxt)ZfoE zu3(DM7EHjIu7=a$u5q}X&NgjoAI&aWuKH2!cGy{C9i>x*n7M&c9GkRg2>)AwX$Yk= z3}qJCPFI!6VHST0@th4rIpUKw;f`W&up)T^2VcVz3_#PQv+3fm_lp3*iFtWtKCwZi z+jr;pO;IvN;_zSFQT^f!Yl#7z2M~Hma$fV+w3rW=B!xpi!0o<9nX$_e>pdOE+A8{8 z%@f7$V?ey(Cn3hv!R!-1n3Y@rn;CFr7!ym;GC4}({hYv1xyl}6L=*f`cbqA8E{K%P zk_NiWt@WF!s)mO^I{H1N+(dTvP&iL~VubL(^D-%2 z5Rpx_%9%8+9%b>nOsljTZ`D z8Dj}5FctgN+^>#A@y+dA-@ry2s6rkcY5n%sxr^vlP}PZ!5{zGjd$Q_i`N4zHRS3xE zinHD%99W!*f1viLfO`@U^naKlLE0f!#AHy*As;k=wVXjxvjDgN z6@TGM7-kiRajxe+RgIo2h5Ovqb=Y^txvT5A3HJ`z$a8f+-6yhA8JNdP%OL;w_W2OQ`abcBS6D^w5&1meh@PN&n6J6R+WiBQ>O zGMPAJQzl80%#c5y&u2saMjpjMq)Q5|;8tY9kSO{7m*5|JU#sMDcRR}h6Ll2dXt9}Q*$ zBW6TA;tQU4ds{uNy2fHC(i8+mUH>f>ORH3?(+NeAoRXvYXfPWXF(cv;Uk}jZnp^eo zwyDNEtZJ^0`OMhou*r{~$-zxeM0-@YjteS4~>>M%*fcX+P;!ee;fxldJs zBd6*x{Ke;)>R&S^i3I*pX1mMY14?=b7lQk+tx_$ppV)Z!ZZEbFZC9MNz?DEiLSkJT zmu|Z%ZHRjtvn4p2I><)MA};TwNm4D!Ez=^|sCnK5&*POe&qqAQVK2V*5tO&SKm53X zMin^F=w&p~_ujte+c)LK41COEhha48ufI;oE)FzmqGnn-SC4V7m2=f-rD72+X3SxK zCH~6({+(#2>aWKbjZV(}H-p>oTMWQJfmn(nMM2PaJ*&l1Y1L_UA_E{AAB~L0RWE;0 zT_)r*m$}SkE=6p4OKI7#WOA9y%BI2@Io_s%Dhxpo1UD$1}_^MxPz1q;vd`7o)YtwABP29wd#?n*_+jSG%2M$E3r5b9}E6wuP zTEAZT>sHBu5B%ffJmv6|gUeh*=dP~9zEAZ^I*dEt=YH?4ySfhh9)Em{v1Q;Jf<`r( z`76`@zQ^GV^iA;(PXmOQtfThr2Xl-B2r}pt(aa>3bTIeuWPgGjqj%`nxwuYHwmtDAljcGk1>`58bC5uWSR8l`eP38ct`-Nl?eI$hmTu2U zH0h@CG@Jb`VNAJHK_Cd({BbA{Og^5~P&P#5mxNIcm+|J8Kzev9@|0vLG9{4jd2=Yd zIp2DgJ~typxJije`NFe#K%rN0T6v+p!~{qK#1-1bF>r!0)efu+FFJi(tLzSO(;9#_ z2F>6tZh*uem=QyD!zwfVi;pc<)f_xp4T-9G6f~RC`rg4)&gs%A?EHNS&3}~d_fDUI zeDKXfZ^e>hlgL&CzjA#pRVt9!q0HIWw>ac#_;z#C3^fcPW?gTXK_uBt4a;GmZ&7`r zeebVqib{xJiS**{dapUfY2|O7dLZJMdxlda_DVTO&Qfy@)>~Y7ljt{;{ zGLVUiWkz1lc`?e7Y|YO=SWun(@4-@Ci&trFHqyQktcnmSB!1W(X2_b`04x+ea^S=HT0sNdzs8^< z@2<+e{`J>;_pA_5!Y+QMC4)jvT{%yfnihh2I|=_n%C+c z=90JV5o#un&M?Mz{f3c(3aS~P$%VfWu>q+t8|~?t$3v)zQW-TV{0;TmB_ea+5FXe1 z+BQeMc`B2m`Z@K=sZ5T_=hQc+VmYdxXTo9zq)HSiT#pOm9e*m!YL)wUewO-V)GSZ! zS?ZIew!EVLSxjK$;3x@3RroG&Vx3S%5N|b5cMp{fLqrMbFC-7{j@^$LHC^1Pvw>oV zG$y=-Pkoow5xFpP%Xh+aKs>^JeYPdGYC6d!(S9ss@wgfdPqV^n- zTZ4@D?c%Wic`ABy)&9F6%Iz0tOvG*7em8ylrkLfmn{kk+)04Ox7{rqjph(CD=x#^? zM=LdOA(KevzL2ralF?_;b=Jq*(`hMe7i3xzQ#%4JX`{kNB%9cob6MC};Jy_Y&L)b5 zCdC!_jK0D-!v1|JazP+gHrZi{$K_dsq>X-|sgy)t)3|n^e!ns1;A@au6D_xC0%xxe z{($=lGd5m~A#)0Vu0NBz74$ED>XyZA3ntT=kMBG5GN_`Kk=zF~z0eevl3RG5I;#_F zO9b*Qd3(AFvzpA_lgDtml=F)}^T|O|KC55uT%WIB`+|(3gAODzBRUH(w}`z2^8L$h z^taD~S;s`;6QwsU@SB@-xhTO(^F=Guqb~4k${>2>kFN^~!*qS&Zi{27>gpzrodUTE}S)YC3j6P7Ae=;ZJ~6Bro`3 z3cp+QtqFY_6MC=uWpPU`nFM?P6$fRr^ut#y5B~|j$JL`h(PfNxw4i{0N1ul})&y78 zi4GP0$7(*5>Yoc#{TnlH72J#9n9>UG4+yB=;8P zyy@-sbqa}`_BZHy;(G)7h0uTNpW==0EBT6u^~6Een|358MS7gx-fzjy&uLj4oJ{6$ zXP1scZKspi=R>N@-{E~((A~z%PvyX#->y`%!Uf1=lO71Z2MW?`avI5AEwcwE8%UIi z!Lwd`>jxv(eZ5Zp8D4Q3D)Rgj4;n5;yw?_M_L9esbZ#+r3Uu^P9lO9I_MfA0c2akv zGwO|0kSDifW8d!qEH3~iaIs9#(l`e~;UYRo`FsUvxvs)J!!jKpPxo*7X*3qI5W>(W zp>jkN1ZEqw@0Sf&V)0F3~n0GmgK zPajNUvGTPShE#P(+h|^Wc~oR7$JNW3D8+H%W~1pcU8s?lraG>kmMR8`syLK0rtpl5 zg|cicm9=YuGFZiqwQ8ZQn3k=jj;dBq91?>>RkVqyh(CY+EXI8PEUrF({w#vwE>uHX zh|ah^e-Iho=g*%%$LG(VKYtE!7!!h@Ux%yDpFe;8{CP)%(qLY*yYb`4j~_pN{P%;?U1M{h8k7UMb|E(^cYHgLYwuCyI$vEicWzD6v@u~|8a`jC z*ZE?-K+g{3+H-#T4i~yvovU&!b0zKuE=VZS2a*R;2NDO;m+P@niM)uE5NT%BNyLC1ke%!w>2}z!3u$T#Czr3@I)o3JPTc3IQCr zlajU8T5GMf7DZ9?kL4?UAwf4z zQ_5t*)Y5~Q4!}$SG>il!*+7z@h(v@}M5M_P5hD~v8jOUfI(v`3QmR)9Q38QL1MUTa zOsE3V{^Fj9W+q9pvj!p{1W*b=7>+>@5JCtL3PBJAK@b9jK^TN6VhAxzDI_hU#$+Ox zV1u32&&aJ2ZEtxHLyr$eH_l?p%9(*Qfb16JnFFar-Hz7p<@ydtlGBLz;jZ=#EPpxH zuixoW^>aT8J6K+l$nd~NyIvo8I~S=_*csJ$ynJ;C|0b%&E@{{{LfFsbt4S^0#4M z2e*a$cQfozogz`wNkDg0V19hE<(zbOM=ayHj->)zhz8n;V`E##$T^zP0UgSb*i2)R zcP0skO%JFBs-#Q!gx;b#H7>9xmVphAO#-17QMCuAN|tCK11R9F?t%;1MwEG&o`{G5wN(=bK%22<*ZS%|DV#MqY;lKB=}o8y7V^3Zx}R*dg4F1n$(0QreId{Bs^6%tzPxhVFKiENhdZd?*DO7rjY z0=o635Mv^z_;#~BficLk(J~(w5OZTR6f^%2C$P-nkqqXZR6|Tq#Xl9GrxPzA5yq4c zVw7FZ0Gr8RxU;13{BzK7<)}vuSzr;X3LiQHM|z3_WB9EEZ61}sXys}MaKF(9cCcb# z+E*%*(~gM5E+fuE5TjeK&zoCgK1z6fO+Vf~MWXx#jjnPm*h4_I<{=*$yi$u(E zyNYnt{NQ>-zj`!xsf1;Vqe!)kyril=HMEY~Lxye;eWEIl$f0(nVcf8UV-RG7V^n1 z0QK$U+0M7CpN)lgYg|b_>@g-G6oLeY=yZpi`wxiT-%yCi-VKR(itzC8@bD~@^f;A2 zl~|SZh_JEL^fQ)BmR^=>mXP$c371%xh?me9m>8H;nf@@ELYj1%l&Z+xKb3+^s4`T2 z4dOl#sw@Yh2_hkaAfkX6Lj;5n0U-iHh)75zA|fI)BSz^e#56(E(Z&Hb)Os-LW+L)$ z`0Av;BM#Es(Rn)JOMM&Y;~AmZhQ&$XuOKI#)fQwU+AxPeiCFGQc$ET#H#bk>K{uDs)=`29aY{sPnmp6zn}%zJJ; z{~Pl+aMl&!{IXH;GCF&^k6?zhf6ft)ZMu&&T}J1`ew*RCU*^onQZv3zdfyq@txrNS zi})iA?=_?6{g^F3I>E?x8?UjR%$czb)yZLggI7-hDi|>-jQp9A3yNEhaX-q<^oUj* zIp;ro97J6Ie4avO_scQBJ$Y0i7;y53#pnM(mvjH*rxE_}1Ds~U;ufB%u)xmlmC`+3 zU!lS=jqf^9_a#xMzVRX~ryzrlbQ1?9VdZej=$RXK|FJ&TSvA1!-qPb@hFsLs_tgCw z`!~~cBmlgVp-%Ybi;8XO%x|%29I~~@z!aJvFLJr30J+$!1>)LsVPiz-kRM|F$JQ!uAl9lQYRyIM^gg=xbnhamt@arN$(+H>zSqH#}< z-Rs8PCte`O;{YaZ!<+()001Z`^!fDp1JD5f#<_{I3G+R4000;Q@W2AVC3hfK8li&_ z6jK0Z|KdvEvN#7IFb-fy;tdXL7!7j4F@c9#75c0Ww0kYcL%2i_5wgvYtwIo3FiR?w zQqs1wh%v?tGt4l<2!bFr7zkt3yvGG%6-TuHI@18t0M7t1<+uBWz?{A%6y`zv5JFrNSBSM<0iE|p7lKk+1UYJr~bw*az%`PAc>uy9dZYKzsiPA#W8 z;{xkTopFJ+rM{|5b*V2Ob*6O&1=UvTtgb7=B^4B8ces4jR&71(S%QY5DEguGf-u2zO|&^^QX)tL znppDdVdB`5-;SBFSF994&nAvhj$^FY{eHks*gk>!b{JBW5~RRIU@>%oz!hSFLSeR? zENMy47Es5i&z!-}WeiEih$|dmQq@muW5eg1t~LATNBG*73~H z%*oPRRc6;f<{UYAAY!1PfoOzaL4$vhp=lY=^9K|YqP${kF}qf%1G53)Wy!`H>G*_E zv*go!jbq1-zvfN6iI)YL!Q(>nl)ALKtkrB>md=41cV|`a?C6~xUE`|g8g|D>d)a-- zVdbQZNF*d9qzW&>+~h7jZ*rIE&i<@amULXRDf>hF5IX0xE7}J0D-ZX)ED;o8HkJAh zpAZ-XBw(nq#jc7J6cwlq-;dcjJAwLkH6cpP3kODnXG?l^OyGqW4tX|l5S||=gXfR& zH!em{MhZb4iex5vL(#HQ0BKYjjfjX)M!6SI@;ZO=C#g=uS}qOKjy>}rNzXdRo^_5L zJ2rQmq3(3~=vuB-WooCM|O1jE(xkClXARl%7sAEfdex2jjIku$d=i%6~sWMgH z^F8;(#|J2dR9a9%4k7uVN=QaXMj|4nk}1FlfKtf>B-gVtnM@`U5~)N2A}V=*<7?QJ zp7D8vG!B;AqH!9haT=#_S4HDA&hwWjQOTil;$@Aul1?s1^n6w7o*^`2=xOS#hK{SO z#wzS=Pp!kv%xj2mO$U5*fbk{Ijd+=0?}C%b985W!d>+`T>H(bjGz zTM7+)azOpoms9(S03f6C64KI_3u2D;HD>MWHX!)|2oT8HYaQF=WFgqH%)$B6EkJ)4 z>g=rk+mqOs98?Zd`R?yEZ7kfN3~2VjFaycY!_5O;r)m$4$)Wjt%gNWUQFsEH3VJ;g zOT?aHb&u>Q!HOg$YeE-@mH9+A-84#ep^k&M4XD0HrgW|kkBbU2?nihD+hj$WJq1gI zJwCiqIw01Gy!~f>|38tyW^)yJq!@J(KT7v$4(iA#2xRKb31-jm2g)D!ow_r;SL#iaV``r zLBJ|GF^sZ{&>2CqIW!lKM*ZZ);dctWSF1bbXPfL1Bc zr@cSh%1L^{dk%0QSD)`yy$Knup0hXoF%E#Y=qY5W5%qci%lfZ>{~ED&b8+wlIPGn3 z1KR^U{KyI{Cq}eG2_k<_17LWY$nvC6j`qdqr>94@9Cq>6WdgK)DLH;AdTMhIx%zNM z;Opm(wVz_6G8qsc5NV{fTtkjGx`!Li;TTLY%o9t-1^~Pj1Minuq*Sius)Dh$Gkiwnt|L- zhfzu~(x9vWkXI=L93>3kk9z_=c(vKu7+--!N~C}c{Ct0jvG(_%N=mW8hHoZdg3>Y7q7tkuG2b$ma)~biq`xqG?#v$o!5m)6 z291u5pCl?Z(|Nlc4@m&&I3g*0EY`@lfDdc*FgrP|CD>_ieJ|hz3clD;+0YFkOdACR z#q(i7VZsl058QOStzgmx5;7&=<6u~@P~bn~J%K^hMIn*8gg*U_+~K;Ln^Oj4Btbjz zQ@B;8Unt{cCx2=B!qcA_9$o=_Uj)P-!nQ!G8Mw;20;qn$)wW@LiLa3Xa;?1FcsSN( zivL*OS8@ED*hkDu^g^Jbj51!zH+Ps*N+@8|8}xjcn+V>Uy^* zHv%Jq3-$if@TGO9-LN%OYxeWij0f!AXV6)*`qqeeaYvvh%(Fdhoe_NLN0%=WA#VKT zdgs*yfy2BSwmc+gqC0hALr_!d1J4bcaRmx@4<(0{&h6ac^}f0I9;Zc#7|nG!6uHh$ zuIyIp_}F_28~e#hV1mR9Il6jP_l13R9(D(KR)+3pM6)Md57vwSEi^Uz>_oI`=<@0E zY_XVFn z1LdasiVNbAmUC`bwnS0F^_czM6+@0V1t>J=F5>jbB^K6*iIb}|Q}%y^(|cd!6YiZ6 zh=00J=tWGSu$4!W&Ua%2^A9C?|0V&&^@kTvvkN;lJCnkHly1VGD(%`c`sMS&UZP8e zD5jpF{H!1KY0?r;=OVRT=n}f(f|$pP_oHiVjw!ml(XomgdC@orwB_|3vbTC zLG^BfH0J0YaSv;?H33|T09sUp5=^j^bq)hI;Fh$g^NDGY`dhyR9wHE4@9w?!&m*4p z6#*(mVG9UWB|;{XH9VUkQs#9b{SW)YJ~@Lox~$Ru5dZ$HMv!H-g!D>HkU9$i83F$Q zEfmT;TQD}Tk}0r~A+VAeu#y?E6TmU& z(vb~U=2QIqU59)<5XT2GL!2u!m@UgT11l>#e`DIM!;BV@f6D zF-^1e^0Fa^YRI)TykLyt1`pJgBuAX4C<=;EP;(qvX$mT(RM7+?OczuIven>RrTD~W zic=~V3>UD9{|g?+6tX1G5}&8JB1sdEBX2xM3MM{F6@{`qvFVS`^W3d&iB5{ef@$J2 zJyYX21|2|Pwwy390#MTf2n1=mN>5H;Rw#;^Da$lj2Ub*AM$>Y~mM{mC1OXD>bt^pyvwPGK{9oh;fU_!f?T)f=dMxTLgTv zVAPZgYIHCGpB&N=pD7E~p%fy;l6EXCtp+VJ&hFjs$KQ1~uE|-QjcU3Xd*i(IcXAtl z+;BVVZrt9#9@XWnxBgCUqq>~+_xE?&-ugS~R9Ag_>+huN@#So9{kN0b`1||&`{(R* zewz1_xiz6%3;O%}`}2Ssw|&n3dj3Q8l_aRXg8u&A?smTY^VWh$NdBs;_vG)dy3`V? ztJm{?dh&mI`u&cMip65FSoCS0PewEw>izU=yxvR^Ot|Z9|8;xozwQ<1>`6;X!HCJ1 zN`-=gaSKji6bhf90Fz`fT%nS`q zhr~5P!b8Yg<9TZ|BuTF$p>dn-@2v;7nND$|-E4SpZa8iWbZa*rO$NeM3~6FU7?og` z>h>}ciS4CWC2I#tuIL+@kC2@#$ra1-3EIIO5C$YH1OqQAesxy4*fd+A~_9Y$6ZVbt((V}WzA)~v(VkfzyqG>)ddhGrT^(`a5p zYP$}JDu8Y(azdK+v+A2$Y-<|YrM8;(D)RHfX&ETZEd`7i?75ou+M2dC3m!K8LTfQz zM0C!qhQAJ&a7NQ$J(<4Qw-hiNOz4Dd=tJbQ)Kk(?f-Mo1eB9cSP!SAh$!AGt3AWS% zDG4bNiDpVYX9I>jsIzw6Y1lI}m2h6bMywOiaedSE;`sz9$~7VqpPn4)p#We6m&tX7Z77# zEOyS(PL{g}r=Ne&pO|6OgI8P_oyS=Fjd`&uuD$}JNXk`gEDmv0lT8<&V&YbIbKK<^ zZkg%Go^njJgA^R-MgXPLVoRFQ&agMl`Z<78=1edQwf_D?q6$c@Cos?;kg8{3fI%R= z9>Ac%L8_j>K!ZVAJ%NpNBI#VQ8dvlHIDC8l;oT^BIN{Vud$#b|Q>0T)xZfgkohK%w zNcFprWTqojL9}Bs7~1mc%2^@Ba++I5s7TV+3lSRyM(KfcCEpdO=>+$@7G2f=?qF3% z7K+_Z(I@rea64tG!jCI67Vs}Rh-2Y}jaMh@63fN@z|4k=)&%I{jzb53qV2An0B|u{ z%K2VMlb^-MJB1r7eLt5yJJK@(aCzBFpf7?pM?BpxoiOPU@8vZvc(z*|wJKpN zD<6|Ln|H(AE@W@D*R{^OBlFS4;rl=Av zcd}+-gD1nc4r#_8JPL0Y)-x3LgloRe>4B7dId9Au0?FWjbV|=cuBt2)?GGK)r*uE) z;vyeouuL*b9SYD@ivny(^COP}NgsR2Lt1Ef7rYM}l}7kSH`m9g*4_siiZB3ue7X>G zWnjWRAb2N^*~Yd&H`ycVaUpXt37sP071Q#9R2c?;}_i^|G&Q_fYmp5l*KLY0PV-Mg7&rh1vrw_1lh5=Fu z45W5`zq2J5qZZK@O-5cY(Ra|*%(FK&S{V0ra^H=uOH+RNR5gGnib0+B0`%{6Me70hhK_=%>^EF4h+Ro19AmC+6!ebalv%uxbR@4->0HT6{gxVL`N++_pK%Z6)U9`Au?hI&NhoV+I zrxr?IPGc|G1sEKYiFec-LP?~pKMrP?frYKy8rv5JvmomrY`oW=!*9(zXp+QS7{V<- zJQ&Y@#C|N~^X`b1E=ch=p&?PmXWJn{@_gd)P(<8w}c^&bPu>2qICiuF0Tu03IhpCrLd-7_c-)D{m7AjmtSh&`2sFlP2ix5+4+ znl-~kx7oP%c7`pVxf;BIdEU*&s5m<7gx0Ftgc_U&XrASyEd>NoFW4mvPyRWs$5hZN zfKKoxyU0)H6-F9nfh1Yepbqk267bSsh6oD6#uSwHr(wxh>02KF{O16I=_~h}P)VBT z+)Q$q=y(D!)w#K60>JYO37`X7;^7YetpTN0&+19(9oJWVXI_|ybXyL%39dAelb`hW zEN~qWafjL6Me}IyjNT*f&;B>wWiw*gPhHH|wGLv>3B0bAnJW)e6sJwF2*E@@@slTT zO2DgoVtgn`84=s}AI~(SwC2q+Z(DS24QxOgCp3<)xCySpz^u;bqXu0k-H=kR@p>YP zlideO++jqd^2jM~kF;!~B21csg_M9Ip-3Zh7BSi5q*Q5(&zv$5gdJiC-XIJ!-@!pk z05wgCKif@_UEXvXpX~AhlEV`_IBgxvkjxV=p|UgjCfIN@a@~L!>Se!=FPym{Ss@!g zhW|eq#k$8RzfkDM852R!Cw=cvHK-5dIs?N+DkYs!KJb4$argG7B6Wb0tbzsbj*3?H z(82Pq2+`hk<;87+M_VM<;^OpMs@@hkHl){mht=c{UTW}THpZD@r-Qgqx!yRTp_deC z!ctI)`h_>g+FXoVvox~sw=xOk)* z=$8?ch7JS?(ji${IBkz}ruF&C1`6m!rQYI_Q2J3YOGS3AYS5bcMV&tLMLl3JQeuRY z7`$+Ld@R6V-rJ9q<8Fal8paI;lGYMi7lCpU2xRitSZn?Wv{MT$|25)JohDO&ywJga z2NU#^>dAp%VT+cz5kfG6_q zt;9v>qceTNh>As&J%FM>g#ZWR{D+W_597)3D>lY{g>y+0N*T6TU7>H1Z3V+aIhoj~ z6omj!5t2>tqO$jkH_|TVSvN9^2IB!Vzmm zV~PyyA3M$Oh`d89-*`)*E*Puy+HOx~M#JY>=V&iotUerQyS~h%+$CysEUSH~2ySY* z5|e@PZvX4zbGa+mrc%QrF=Ih?ceJ2(%&GQW&|B86qh znkX-LN+pBQhPTg&b*ul#OZ2KO`T9`5VvFlzY~G;B0!cu zC%6gf%?Wv0P2ZWphom0HQ<|a%iwN=@E~>FwT15X|;MJB_8PVynCSU-1f?Sj~xrpGv zsxyi{DxHMZNRC7*5+@@hDmW~&whU4-J{N>uM)SZl4mY+9^_v3skvT5`W`}1O`Cy`S8zO?&i<}qPF zd3FBhj)Oxur9$I1KT`S3MXkO9Bsn^1&7AS*E~~X=(`+9AA?#x0q zT@vbBsgzyR^8%96c#{pdS;2oeNz2u_;@pNg%OOx*2tJyA)pD?{5kK}6mk{AV3z~9~ z<`05AU1)-mX%0OAf!tWBO41M?PhR z2=!DLj`M-K$EEDA<+$T8)+%Y?{I-SyZs7}S%W_EePeZY0J|qMkER5%pIQrww<~?_+ zXwSVm$8eJ?G(OC%;ii~P=pLGLy03D~Ljw0Rc$wfU&uRdTxC+jDB2v1JThd`=n{k2S z)zS|bk~FU)AqaKi9uS~c%&RXC!lri+&ngYjy)8^yM50&yXL zye(^)a2&kFP@*09;T2@+Eh33~LywRPq(5op@u97c1<<~MH#0;>t_5%OhJdF1qbxgi zl5F8@20XB{Kw_rGo(K$zMkbux0U1tt8h%kXWGOr=eyU87xthI2sLiGwP@XC40gb}T zPpMa1F5--hVld?cW(mj`%ne~b+hT$%)wn3LXf)0HnmhP$^Rt+v(IkL5siLjXk11uQ zrE_^WPz1^bKy(6A8^>aBr?0}*M(3j`cYSlyfHqX{kFhMuasYXsY!=-=!5Kb2s9UiI z=*X-pK}kE5LU4c--2#|HJ;62y?s#C)T@DT%YHi_c7>YzNYHvjI+)Ip2d?;{~qHa^u zO`Jh|TA-v^&a2MijM_Kb=}M&+87%fx1&+C^E!KqeGw0(4n_&U2JvsXfovX`iy0sEk z3N9SW_mw=R7Uvz)wA`F#{gNkg-G%qpEXfN6GI6|Sk+CvR!h68yHWvWa90Eqz;VYw( z6O8Tr7~^;fIEv=4CQpWzT=}0q$#Tzn6#cklo=zEJseTpLXhMv)uqgLJPQv-TC^hzJ zhw$;`98k&;@>&(9)8VtjtBe=b1|7p*3}XN~N?;F3@UA!=602q}25yn(YmburRXo=d zY~2M>xKQTCqtAdvb}g7>_QS|z(Y*p-4~IEG_5Mnw$MFb&f%&B`jPtg^~9 zm20?$Yq(|wx46YEZn29N!x+XehOyw3Q%*T$S;{j!!!tZ%!!eF=jAJcg{NfkC`1OKi zScYX-hG#PTVi+lpR~!R$vr169LXZ&Vfhh%2=9wO;dYsF7AkQPRQeUkYjHYHrCXG=` z(2WZQec@1?;3~{mF$*4uXQ2b($UrKHs|<0;QX#33>&>{{O(^}{)8#|3-B&v7@#9mT zp+x`&3V0u)j}FvzJ{_(-&DCmsYqaLjW;e5xMaoiHE7wYes!FpQUB-|MKk)-SPxSEt z>bg&-85vh%gZ+Jq3xP%moCK~?}FeZKe1hl{hj0D zOHTg#$yILZ@T7}3`FGP*+ONmfJL%$4_q=rS;88beu*3e?uEC#fdMTf9fA3!>sgaY- zgXOT?H})7r3wNF^QrfwqE4rd;xhl&tEWNU8 zSx7jk(AjKV&vRwfl1o>sxw>Gq^IXvtUC|X)OAZ|ePL&!{91V3riDalxi_T{2dYAxfs&cXudv*e~sO1M`6&d z1HG%CrB0e!0goPL>)>n^UjAAktsKS6NxJCIpLmH&V)9I#i5qE*v@2o?pMtbtM(v8I zh$s04w!>bQGisFej7D3>XmoVz_>5HOL^cYBJ*bm7^AD4Y1Plq1(lJIfNs{4H&_Hlf z3=W76K^#dj2m&Jrf*=TkAPm9~V~8=tD5Q{TwgGu-Y%&pKaP>=atcZ5)k3SkW8MFU; zW88H>HEy*$&$MYln?%_J9m)`;Au9N=_Qp}U1 z9&qq`0kXzbfV_;W!PboZx}8FU0gbxsd3lN}HQLx{Ov`x(;_cXB$Fw}K+tmD7m-*fv z;IYUaD8xSo2Ktx>o#Cc6vm=*vzGzLR--lbUv1i!bGYYv);+%)Lys`^)nICLGFd|!| zY>a_u;eOwPz5oRoTtv7^A{4t0+E@Maz*0V<`6F2~*|%4b%>s^GTTO9i0qEnlvvNFm zi5D(5GE zf>RZv>T#%=@}-l;1{z$!K^VXDj?$^2TQxHt0|B=xas4ad^aK!t&LbYG{M(W-r( zhKu4Zx0zt8r&huvhYv}(3ueT@O+d3n2Bj!E9?zy`vYpVQpBefP=B(LlN7(*%IPqf{ znGYG~Gc_cM_4`2v7e9qbBA%yTSH-%l#=!D4cG<3!{aFms+b-SaD0tRvNL)zx9Eywl zX3W_QxKMpC$ju)K;#-iv;U3A)7{9oFA5xO>=+ot4!j4iH8J}N;;=g9_+Mi6~sbtwm zQru+lmonp!@)Tug*k1{0Nx-JhXkOdGAAPw&-)V)dhNVrHU;XI*ow=IRb+;7vX$aQX zZUJmf^yx%DLZSvR0#${4jC|`zMB;NV%E5q{`e7bz3VJCp0O(e0NhE^zg3rH{?0)!|1-`^{?LI7 zuA(3qWnE!(LV>K`GJ_Cu+lG~Tmj3wT%NN;h&N&$)LNQE}10-V9nCeHNZb!}%5?Qdm zbp4gAh}EN^teig=ZZyl_+X;w=yJ7CHA9jMKH4xbhOBzJmI!$@h=s;8AqtffhROAu} z2glpZ*yx(M5%!$O_1|AoHReO1CHR7pHzrzJ9fKs0w^acg_^ikDJ^P- ztjryiZ(8K}QC0YM0dOnp0N@cGvq7ow)oV2UYjqH)RIy@s7PZu_iQj-!5}>_Y7`$`n z5`{~@Zw1ez=)wCu7^vxPt*}_!^BR+nLeMp)l-donm>wrqr!Xat!EqU8r+0N?5gVH* zsE+uvyW)vugJ24cHDGGS!-Gl9L)>|zI(gzNmcXh+)Bvf%!|)T0nh>Ve!ZsEccs7m9 z;fF@$)Ko_=#!dH_!mx@N=rDjZGmvI#u!0FCi+x{VDJEE923AZ!jp51uu6@m^%)JI< zRCqBix2vVj57L*8PWOY$ z0;fCYC8_ddLLmrlW^>9TSAeShX1PtSxWJUY(E+RiQFJ|@RB$_4E=AOC=TLT_9ZB&5 z0bn)82B!HsHvkTYIvXJ{0xn{Z2c+X=n-N|5SAvWNs@!82V+LYTK_rRCTDV;Xmds}$ z36g2qjD^C|O(xJsofctU(f3eMVCIYpsJ;sroJSFIK*2qpcVL?M^>c*ZPjbRAc+o8j z9SSTv{oGJQs6eSDYTcb*PEWn-2ET>pdKA2h+6&BBLdzzcToy|q62)4Dk(+VEYg6yr`u#(P0h!2czmCsI{w4FbEaVxmCJS3rBxW2b3>D&Btpsjt!lw4bJBmJJ&f z#9^TO(yB!slh3$ibhxGtR^LqfEsIQ|^bhi2r2n*KCYX`z9d+`RXtd15h9o0PA=}(| zX*ASOLnSBK66TG|@=6cd`2ppdR8{iu_ZNX0&PELuZ)%W;eiwcX0rh6Rvz9|vLv#O% zplC*SKT8Se9(IIVf$NBnXzR{cj3`yJ8wErjjokTUTJ!mGc>{(qmiH<|;;cZadRYtc z=5e!UFGp>*oB{=iuh`{KJS)&Iq|3LD6mQt11XvLp4u{ZgLW03iS<@c&tXDaI7Fcre zsL!4SmM&;>KsD ztKYs7JVSEW6C}q@;{YRGtJltE><$mOhh%kdqQ+u%HK=&2*eZ4uoV>jo59EzBm0Jx` zez1$L_7{jS^?pJf-Te5xbI4i|&;JcrgTr@ImLyZ*so0XJ9K%x`bfi8uj(u{zPRLfN zNFTzS!*j-#^Id)wunT&xS#DI=h#BgMJnZd~L$?|r4upXgi}uDm<1*b=aB2FGN0k#_ zS(pfek{s@1D`J>F-tGTKHm9kU@A8^h5YH{#M&0gVBg0bd@y z;yPzfKHmMqy#4mg|Hj8N!;>9ln9Xc-ZkeCI9ow9<2PwB?G6IpTH2*$IMj-7igljzu z*pnn}t<_UQ+|2GDV7)oBv7x^}LLSB&n)5qv+-I6EsV;=DHeLkL?e?H!_Kle!b zaG&U;KkN~E!vCgU>TB~?Uw_4E$**3O%=9-Mr%&pajgViluJ7rWRsMHJ<6EE9*I#iX zT3>&~{r=Va<{nA^e_bE$3;W+M>62h7)Vn+uHX=6WHLdz(BOOoH%ly5+_xJwZ-|-E9 z!+IILOaIo*IR0<`WARCdNRrrS+PWjv*v zSWYZVJPbccUFc#nXdJ{BluxNsYSw$Uy*_OvC5^~tv)P=#s)0qP)WDk>*@~bvLO&4R zWI1Udw3ip#b-H|)AtR>E_GNM%spU|~4!pQCvlC@JK4G6DA8T2&2SB73*%(Yz!gbtQ3fTySK0Qo$ z8-TH{E}bbI20B31G-y~73m!;wx|VP>j&^`wm2h?>_Z19kd!;H;u?gBvGd3j!bxo?B z=~V5E4NR?s^n)AgcDt_xZkl)>CG@gVVMJeCgKU@-Hm%tB)JWD4ddT+t0D-yCt`O~6 zFqL|4_#5J5qWbAD01Zmie4YznZQV{q=kB~d)x2bDWKZmS@ofodvx7gM&*u&P#GgV_ zA?SDJI9{bvuT-#`bo!LyZBa0FIt7EFZ&uIg6r5l9Pazx;QMakv&QyxQN87f5RX4ib zLZdd(D7_W+cl=j2yvuJR?;-g+un_y7 zM}SA%h{YnN#NrU$tUc>1%PMOJ3yjr;1X01Qc!dpjdAiLFjHH4a7~hmVxg4c>6(3jw55t_@S%a01Bj^W)Np5vGpD0uPTEi+ z3(1J=Oo4O~BU07uh{)(2md*O4y-t!6?jqQk$r@#$ITA$8C2(qw!V*TVeUZt@2?T@d zl~I>##XxB5VZoJ2t2%W)q0{Ko#(5*1Myt+g^$CqSYi^o0PaNm8#tEf9rBEE4s)KVA zrY51fBvcdyqiC`bQqjTdT5Vtj5n$*;b;zni0y(P@gaHe%L`THZ%mTO|;&z{|K6JTJ zP*PP<+$MEE8`VP*%4Ksm3?swyXGW|>ye6O{MBPE68`afWO|$sB=G@5LgYY&Pc! zmI;~=)Fu(63P#`KuX7U1Kox;qffxdh90PI3GhSt7Wu}fTcqf%N$S;Dg%mibv_PeGVnv+X4!|N&`p7S1 znm7U_3|tasY+t#&KfxqXAj6=^aiEFIbt1;jSHua=e&atyO;}>${k_K z;h1d59XFHn_P@W_y}Q;9wIA&~Gq8Mo|Lbc%_5*#Z?#;(yR$BtH$ApB#^v2niwW7F3){nf*f;DgEUOH*h5YhndhK37 zV-wnQ2`svS>8eZH@N!&}&TE`#lfy|RtJ zt6)J@c^&Y?>aJu6k%P+$i;$IcKzU11wc1ZH)Kvx*0HBVfbmTe zdy*RKeX1(rX3?8EIMfE)=bGfGru^YI3-@OE;${7k9!0`)x4!~%=5eHU3LJpuuQl2Z zJlWHF*e9;d=Y3+*+4sEr4sTT9`A0ANJ&X?bUgf_&eUqcSzG0{x-mA&|yQ7s+f)J@b}hbVsTr?0`r57IHwr^xELX} z^Ul_y*nW%((NjZOOIur3EDW!bFvsZZ{dUooO? zh=j(Zyc^;(Z_jr81Yp#gdP>vk&XrrtwZ{oxy8j5mg-Yh{zfdE{1uCWzK*suP>%1Fw zc?%T%`A0Vs_i>liOQRX#oSr!H9X&N2WiHr90EuMGq78iN<{b#suC78!bKUm^*qEO# z37}Q}?M4I-&aYHzacnB?d4g5aeCBYHspJ>7)uH?Ve+N+vx%@%rsueCYK59|xO5=xG zzHT+b9aTJRx4Cx5Y;2!!8=T+-i<)T*eNP2Y8sIeNDIWArA!V3q0^eC-@^LWN-KSIs9i+j~V&>gRu_HVD#DLoi6P~KaH2iq;{B`vUxzg zBKOGfg99DT`bdatcT|C~d!(2%#Gz zbZN?mDM=l&!JXV_uMz0-0~}*^HbO-Z>)HG)I{u&PUp!Kb*rEFHZAM&MGc@8dV_<+d zj`xe?fh>&M-u+y3)Jc|M!vq84#3;6tb{VbP##r{YnDMPlo%bXWs2LOw*dx52xSRh=v}#Btk`_2g(=7NL&>nv( zx&>mqtVVvN1tu}3o5;`iVCO!yC22KJs^K8pz6+Npy)Aw{Me!4RF4XYO{l>!O|H*+0 zA(QHidE_oVt{FxWf{FK9IL20csnkReU0~Kt1xNv>7?I|1@XycDFVm}KaTA#Z73~*0 z*N|%2uGZ?g0QmSNF@X5OaNNt%Kc_a%oXRwAb}ISb3@ZhIrp0}WAxTnrJwJcdNN`B` z8w<-xv;6dPIvm9W-SHNo>Anq)TVsa*pCs}FP4k{2ACx17-C*PCgC{2)5q(lY?zDrJ ze9?2T+AABGq}OWKE6 zfltP8zF>!}fQi|w*e#Rcyh>jlyA<>@saw2x6+egq*h@1LAyzn=GJ;`QS)Bn&N7WVtCLxVQHuRDeTeip%lpYN>9aK@&4m(TQI zI$3SsL;hGSjxs#er}6vUc0t}j1#UOxWawYNY()mLOhpMIV5?Ac47Plz1|LGX^btNp zWBqpD1rT|9IMd2MOe3#jKslDbC!J~nIhJ*x$c>0-lGXcnq)?mYV7tIFBh;UmZ{Yr- z@;1k_$aW!4nOVN5f+Cf0>yUBy<($mR;vARJmQG8r2`-!p$X1e0;VO|eq zk~7gjnGUt3*at873c zKfB9zjcaDq^Qv`eEo&|Ri!%HMdK-J@2+IYCcI4qJ$`%s$&gfdNc2@E(xk26|75k2J~|M7%} zTzpbS9|H1)mvO-dJ*TP?hdpK)fY@JnqV{MUeZgORh};b2><+w&bF708SG8X;=VwF< zXe;;j)K;vP+UgW)*tV`RwkCnC zyR-SYn=&8l;IsRX3^uFM8OOp~7wTu`L>=1GZSdB`o}GljTb*SS80X&O9^0=`51*D# zDQTMCrqE;pW}#a9A%V?)BQSDz*Mu=~jkkMazbF#r!E=sT#?Ogg@c9Q!cMl8WQGK_a zX9pNH;8jXZG&jV0xCB0oYc7KEPOcOo83xyo5LwSlmkajxSg1b3u!Z^Y<$1{q4R+n- z9}uu0bnBj(`rD-oTh5|kwHndyOGLKPf7c&g-%<5;iBKfSnufQt(kv=Ua+xCS_X-5Z^?jAD! zW$^uHT49|`cB|`5=%2U&Q`F}wY$5}L|L2-742+p!F>w@+wO>!OU=)$27&xzCO|qu^ zzlX5m*6DDuT^>exn6=%!^nA;*mI4K_xd$MqQ3Yx+9wqpT7OB`hN`{T>Rwr5`e8`rt z*6Ux64Yx;;3A)|il+I=2@=?Btf>5I4b2e|0^|SuvymYETjrWziu$EHt--8EcK&xDE ziv~;zK9U|wya7I#3+^USipuVv+rMkbg3sy0oO2;IfA+H-Z}4fvfY>@s&PN{FM#^*Q zbmsB&Ne(`ElCG@y-r&;^Zs3w5S#5SP-)rHRv423zmd%pWR&U*gnYVS(BOi57F^bVn zz$6&R!k_tgTvx*~Oxik=IEj(rRi356oi%p|UE%k3xhT{@J{^jHoB}%I1RGsHjC!(v zQH7=j-JO&P*Dt{kM7-_Tdh4=s=cm>t!7!l2zd6x&Q;L)$#>=1?^FUJ{QS9pPDki3U zpa&uC;8tnu+xN#9-A{9~iZnDq$1eh1I!N&bG8*#$s|M@RG%q3Dpedl=*k=_`iZ`W@ z160_KmcS8Mr|aD(5ii|-9Q{lV0crizZdy*@eRDt~QV2*p4w|EsAmu~$PXS6}7M}15 zP@x!j4!5GAz5c^g`2rkB2ov13E=^ooK)5#{<$}sp9p|v@ ziyna2cUcUbzWv};eEa$`ZX;z)3ylRw2nBmRkgLTQP1dZcgaV*)4q&U7g8Amlrj$0;;&UQXPZwYGS~i zK|oYnR+9xy7168H9t-I|2UUULwGYb%?jU;20M+JrIv*#SDoy^tUbcs7W0yJ(`Twps zkp6(ly*o{0tW(oF@Jx|vz8toDvLLEV&xr=CuS06HT8EE;5(OW1Cob^MI={X5nuSUM9U)Cy=~f@xZF93>eMkexaq9`q-QNN zfQv`%(YDjR5keH($!mZV-O4v2dDP|Gt-z)|!<{mCR+58U=)5^BbT1=f%jU7a3OOV@_iY zqJQoDPS@a~E}P3aBLIFU#3JQ03)NS|6_pVUXNwH9=2(LGAKrd6qTub2zc3S_SQ4{B zl9DmDE^3D2450yd;^sUQS4xuuhOf{d8vWA5ijRzmJ?$xdGVN=SW@aY2)lPF^n?N{E zgkrIAwFU%y07_pp6i5JZtOJPaWH96-MJSMGGXsl;`87pkQ>Weq#Q@Fd0<2X9gI(-n zzK1O$7D8*3X(WoUCuuOi|NsC0|NsC0zpJV;bX{V7SPeA+IRO~~orONpLMwF_t`28q zFO^FDeM|NV>ni{TB%yDMsI>cmFbyPz8rSWrfz&K}cHK4vjAWv5FF);SXD5;N5Gw6%4SU6nt+MBRqqAAc`TI7joOf%( zl;5X;BCkx7X*k-)gXnplx4m6sd*%kMq07*@WET=Lk2aBs;2nENr}6zS-Cj@=>=Ok5 zRBUej5!}FJShS8{6bgkxp-?Cksx*R|Kb=meQ>jI_Y>wTaK;{|#v)WIwL{`Y+tjydp z0s~AC02=Ti;7JWQuo6{VDQzZVGYHv8e#-^os&}hy<|*lna2Cy>N<3?oS*1)7-{eRiSF^FN}HP;F!KL zj8F`hX^X!X=E>G-CGDcin6=ZlVKBJYhV7S>D)pw+e(D>hepJ-kPa$hEVTw(%33lQq z2PH3-6wV|TwHNWnX1vBOVwBAEA)1&(CZ-RrtCh4#k2~$EtyW4^8jO?o#iqj@)=WCw zVeRg2GM|Vg4z6{u{e*TklT4(yn+C2{5Z(Dj?=$+{rc@S!)BFB70dCPdRdrNTeYvKIwUnAS= zg|aZKwrXFynVRT-{P6ehpMLr(DxxDALc@PQ{q`a zJef!PsG#rje%?>_>AcxSumkXYI6f;LfX~c3wIzpx38yUYwW1dh-Z+OwyxjE3a32qNl?M z^T+C`JQ_cKB7m7pgbjuc27j#G+J(9g1BS4ZKeUF^Xbs2bV`Mwxj@d(daF1|}{p{tr;Yq2V+-BWK{uO7hH@d6H+q%mxF)$QU>iL2|N0(jCc3PNF0yImtbO3@KTXwd7ZW*l!v2ihgbi z^;a|tkDOt7o~We3{QTc9ldBRN68udWEwRWfZQoup?YXj!_s0Wua4La0C6|#0ZvJx z5qZgct)T{BG^K0oF>{W12Qu%8*0&kL%rXDWTM49TR(%#Is#ra$TAa( zM8`6~-_pmVYwKm5eDZQIz`wr$&XHnwfs+}IoM z#2divm(r=!8qK-~l3+{X7D^OwD@M|P?jiI`|s zaaNwX1COMDG3dj=>cR#5U`Lub{r#!N(ff_))fEXb2{S_2+U~GSdfcb@lwISs^Jw@{ z$6*eV*lI_&IA9*8Wd`=$^wivc(QUyM-8zambi!NAvAr7sBjX&{$j-wC%hL7(@hptu z9&Xk&{EGQa|1TI{E*M;*iu4?UAC~14lj%WE0H|tSBJ@62;R)cGVY6l3#aHnk%_M*5 znVO_Fk#dVzcv5zQDbK6(3f?ecgQ zq4?N)jDL5>{PG2^FxN3ytd5-|bA5G(ID#6x9sa`4*8bzP?I69>dFbeJ6s}vt)n+>4 z#ddVX>8V?eZk>w5v5~EGQ^WG*jca5*IQGJh8GY3k7v`&clY(a#;DfOxs4777FK}nZ z0TEg9+4*~6G4hI)uN@eKvjH7MQp2M(vjhhHIiyuhv$YV4BD3q|EeT$>F0qJQu&azr z&Txk!)L2~@QL7Vj7h+BTT6&&t-z0Vo^Qk?a{?{6b5CS^8VM! z2&{XgNF6jU)M;+W#4@T2dcBmRFo4u6bI92T3!ksH6|b=+i;gw=vEiQ``Hw74v2%Q8#Iinfk<;AhsqCdfO*$PcqxXiH&08l;_+ zH6k{!r*uaCl4=X2xRxD%PPjjALFpGorMneM#r4zF6x~5x*>o>CV7GyVdRS*RZOMDl z6K8+b;#_6+4jiLD+TU@$Z;hE#0{k5yEW11>CrDL@oJqvZNQeh6# z2Yc2J2r*5$XBVn3lSeqyJ%-=@Ty~nQ)+1A-P!L^{NWTE29l*;rj47KFuKcvg-z}bD z3*nuOk$7qn;Q>=m$O26IpBBP+1{CIf8R5jppx{Q*vl0}>O9QX(?36RL1^p`5hWrah z<9PSrI~;b;NoR*F2acEhP1|f}OP~L6VN8I-U_GOsz)xevK!7M`>Qze%$`Uh-yg#tN zZKo5lIX;9fZIPxR)w+5^S%N*Hci=*8z5V=PFK8dl+A`ErswRh4uV5&un+kzN8xrl+C;R-^FJOL zCPE7IC{6|$bWqTd7<(rOt4B|)U?K9Mjb-Fk26?6Pz4@h!eSM{z_WdMOiEiw6>Cma2 z9>lH1OVaR*oobhz%Lmve3=Cm(nZ|d@geF#)XRwhdgpyuBPKMfHp-_qY&ok5ug7*l{+2-+i1JPFj+!ksT{-A| zP1COZ76W$Teat7z?6Bv@A~}59n>(yd6bM2aM@AnSz2Az~ZhAw$^%7|fDM3P8vI;}- z7-D!x)h0q|*VPr5WOr`ACXmIJ;VZp0rmpg7qq2%g;BQRI0u5enrbtGMh2m!CWzHH3 zZ#r@$Ncb-IMU)6Lv5cM{(_HdftaElbH9Y{9;;g^l=()&}7-K29pnKHBzqKlHK~A9m z2+<&^-;4zln@^ZSX?}KL71S(EMo_eJv13&mffp>1V4;&yv@P>{z^PeBlvO{B3g@oZ z2Ozb0>HyTxLpRr|7t}i;2p%N(qkjALM;4E}ord6B4sY7~@{c>SBze1s_B>aR%x?Fc zxp#J;4s3MpFnjrfLK4a5c+kMyy%&nd32Cz@Azk}{KvE!Pf2F<%g~@Q7WGd|vxhQvg zucWqK2z)yW)+CXV3$j0Xi{oSoON2Y3noPn(@-<$Z#o=7Ug9M>(x(l357w}Nl5KBy` z@7nr#E;ZW6E#N10w1(X(Iw_x@KfJ~6-x1S5P%A#is027FM(vb778k*%hk5(L;GWk$ zWVim^_8qYuI}89dz<~r#B%_$r8oK9W24Uw{9ITi|&|RqG8#>T8AV~5Wzub!mHX`h| zkhnc^Q7~}82X3D>X#D!5*bWe?*c>O5i&Yy8-3`i~TZ;AaN^s#P`9?%e7#A;YY0EaA;`m6{p7 z$O;(iCxZ!Xu3pqe!=9A6w$Ol$vtKLxYkI4a>9FZh$_n`PNk;bTH!EjCwX^IH4 z#k>kP9(3aJp@ z6cCx9>vG!ZO9OiAP+YV_HHtYkiZO}hf1FjA(M^)|d*sOIRRqo^YOZ!Zw5!{_qw88U z-0I3I%rJ%gGPxcJ+facd&gD^4B(WW@cYG;9>>D-maucvrFXwS`|Abp_MI;2jBApjR z2OvcSaBfWH3KDJ-QlkL2rY-LTLbwBbma=4E`|>GtKU>5_u+}}BW<*U#+r);Mcb6uw zBj(%SA)Z5h1ypUZw2K;Zp_bfmS4PPH$+F}BqgT4ajPA5auB9+YDyCBs|7dc?&x#}3DI<2(r#$@LP{A?PONz7aV6LVwVw%lH-u8`o* z#@o{%Fi=ujqGBDb!)@7*NTnt+#N6@zC}gg#_`!fTfmMqv_9bCwsm_ACUYPeVY%m6U zMWvZJQfJZvXa`gS$c;7!gj7G8_#>#Os3HJgqD(K~2s9%TG!qk1uBqs8;&5qq+{D3g z5Qu0V`jA470E5Bq@`c5%UQWKW-mOaLKEL#*8i)-u zxTpW0u1)&w$2c!rdl6mx7Dw}$)+31@Io6MNYn6Np4_?Jme+jZ%9DlTUxwVPfy4rJw z9ydIjiHnb1|IO=G?KoGUuqh>Ab-hq-){e8{G#3-2XQ)6i7Y}&ZteX#L{~{kRRCK3E zl{juk=1#GcWlL@UTjSKlkel>*u-MA>kAk2}<<*V>e%@9<|F#o4I;jb(36>?dhDP}~ zAG0IBjtQ3G{WkfR9$v=9EireUnJE^!1|MJ~59OUo{FEFBRL}naP=l@ph zZKrtKCD)5-?90-6Q^hpX)$9uxST2+{8@Sk+0g51W6tvB81}C-n06ehA@({v!#%ioi%z$tmn}%GE~v<3$U@& zBud|&cB&y-ehm58Sf^x}WlNW5SthHa#;NXbbWfBtnyC}FoJQpmb3DXRC^MhBhjio4 zXG9^}apgQTDNeO@`{wI}b4gc#x|$>3oFWxUrMK!yw{F_l^aTlVlC;RZa+8oK!H8++ zt=2zAqpKvADyhz=n$bc5^I zK{i1bOxd+emfVm$er2>pZhSyL7tPVtCQ^9;^Ix#Px&*aK8y^+^gPwGMv|#)gK{e)d zoqLDuUHS_Z{of4fvAzDHN#4`fUZW-7>}?2QITg@I44c9zEBDUej&jdefEIR!PYBiT zl+=K_E!!rR>W4XE+p>C+JXM5zJoT`P@XInvd1o`?5CvvqaR=Kn!Jjj&MAnqNV!$j5 z$u)07vW%z>m3Kmz8onIDj0EwpZA14Xl8*p z1@w=YNMHyhW$170&a1tqjOgZD9zf>vx4ZndI&TwM+b-8@{GyQC?E<2oF+lD1PjmAE z;Bd+atXX;M;=5S{a?XY{QBeJ4eBxK4@JqjGczgoz!1m=VCTGFLF$Lw548MJ^&lZFV zv!b~RC#fMOJZlG%7KQGuEM&n8AmL$<`B|9BgA~C^Mj-hSa8P$!f=N(X6(J>HZmlPr zI6z~0P9*ahyU%uih9_$y*E*Crm=?#KNw5YBE-`Z+ksrgcG4H?ei*`uuVXC16IPkyaBA~wOm6>VS<-3RTBg^vna zi8MBrAhqhx&O|NDSagduCgsd-9RRj(6x%Op7>#!q5z^X*8ndob-a_|fx1e8oy?XvU z!SiW^iKF~U{$flTOR+17Mn=H{c}@Q|J=xnM5kM=T-6S{Ni~IW)K2=~?F@o7_;MHUO zC?nF=np%3Oco>4rvrw|Z&MVauD?#Iyu7wdSLRFX(1&oNJ0aHm7O+-!NFqQ1ZQ+{~1 zojllkg)ZogC|*Q#AJw22nc4RsdaPQA&a#VTEJDy=AQ}FdKU*gwPS=OpB_0SbZQ-g< zKDiZWy@+hVntlHO)9^b7AWaGyOlL2EP9V{s|fm@B(-Oc0XU^0}c8vFG?&M^)D}qV)FTYAs=KL-@8w{8X`2F zkl>mtDQaS;@RC6R%0UqDi9z6mV8M}af1AwWAhpEtAaPZeNFj))*lAjlG0%fF5~B^e zW&3tzM?lr<_QNq8Rn&)MxMAq>q&4ZxtA*04wzF&0h|poCAdW=DmE@6fF`B4l;EnqI zUhgX`=YWsycjjQGLI>j=u00KZ_z(^nvzMx}$z?*Mh?U8Uk=}$7+#+NbDl*6MJp{+{ zeEaJk*yDy9YnN^QpJ&hsuvMn9Ri{lMhZ6{0?vB3W*86{)tv&oM*oE>Cg5RzX`1*j^ zE8XxT{E*7X?0Ez28%f zfZyrq)iy1>F=HGAIXg+O1lJgBLrjWI$W6S6GmWR|uc-5XzApUvF2L{W3~sw`_w(#v zu7c?E&$GGVZOE!9(!0;8@lN{?ik!E14&in=mAn@^0c^KCS~83jX-c8~PobjXn#6)p z(%}%0<6A0{gE1VzfcS|s`{uMfE!c>Cr42K>4Wp_$y~`1m+7b2ZjYIkHIEalWhrBpb zybjwk`&|jyx-h&eu#0)=_Q<;RO8e%OCrs^>60i*P~Tq= z(A)q}N)TbGC3txi?Xf1DMw`;i-oNV28h%L=;&n-$tlcix1YG0Codg!20^uhz8iXt_ zRxI9VX1sw*Yx+;Po!b^n)y-blf_EkMh#ex^u!haU937j-zRPOl6f`6)N$ZD5F{ciw z|1kUs}TQJ%A0p5f=Exi#KeHH$5)@lWIDe7ws5~S%5R2^ z#Jm>5qNzRpF18c1$;Mj0nDQK)LlIJC^{5%1&JI4Y^n+dr(HZGH%Ml6o??!|TC}7n@ z-yJ&Xg;TX>Z8sSvPqaxqchd1I&-{ezPZnU!_3qupuR>&suauWV9S+3Ub+wu9da6;dzsg@>T}NEL{#$JldB-GTipJ{=HJ^ zpZTc#pl#d(qAH=$DZfMfKnA5{A6`5KIBiwQ5t<~lCQr$8k#!nCIaS|Hi;Uk;6QF%& zQe3?;DqGA;YtpdcgfmCOAe8hS;M+zFBCEkk!tKGrCPbFbcm$AXvL$kMGhy>1PsTsO zfLj1oP@+Mg7SJI-^p;UPBnRG0(dydxLhS*lX_E?o?rJD|JBIvVX0Rg&12`eQ6z&B% zoDuA0ELiMYSANbl4ZMs4CDh4{yo@)^5SuvcBHp z;7}dD=Jm9^7R)`Di9TsvbxG{@%JW?cfo1{f=YK>N2YQ~U`;|Hr!H>fwD(0&-r)`O!t5CI6`=Gz6= zK-n3YR&zn!={5s^8=3UpN2JZV9ulxmi!%&f@_#fnutdMu#v8|dS6j3ZsS2?26_7mp z1UOVjV%>d7_$cwHX)C{Z?gn|9s663Jeu<2R_s$#TgU1PS|G-*N5V zuN@>uRQjY}DHkrOu*Y{-q~Ly-#GylZ$gdIv0J?~#ZpgA?7TKf91AzT&j^BZVfSEEE z}OkT={~CM?e_*L0kP?XFs5 zqGjq3uasWF_)SX${T*^H5sx&0qzp=6&t^6J-v3e0=;FZZ1g;aW>bJ6Xv2NMuU1f zTpnnxD$0xtu{Bf`=Ak1({=6?dT3?#5BUd8dWT9ZDFrtX0h@z;Sen+RIq-3Sk@}vCq zZKOrbN3B2|L|xea_l%i>g@%tti_(t9hbDlgad{h_mYeqDW8#Dvk)4Z;lZ%Cun3;of z6u)T1QSCIL_A&Ic%K{0D6af&r@1G$MYYmihOk;Ss&%xq-D~(-NO}WbG|fP|E?)XbnC1{H-^Cf|A-Ps=om2iR z$>79_;9^>e`~KG?A@t}-*M35aSuxDQfJzW4ZQ)kZ3}DO;L^?`X)HdO+DD@txIPTau z+U=sD5U%YZP|b0FMj9%uF(c1lOmkCeJGtRX=1<4wX-04Ql}W(x`Ft@zD7J~*O6z^* z3*^;)M=m*C1L91(khAAOGMKNyw;bMozk+q8g=esl;z3Y9LyvV?HT~ga(VV%Q72BUN z4IC&KHRq!YjplozE&3Y~L_pzvM!-M0hpw^4mXR8pC+?^{k1Q059rhrMk1@K#pqyVL zT-{SC2(+}&7~}Ww&wt`;I0~3U*K!&s2w0u`g$;_wzk~&@iP)qFpcnlQH-10;c+mra z3agZ#yF_`<-^7BxkF40dc%!ot?PuRwB7tEb*e()2f*|cI{c)b+A~+(SKDYwU5m!tt z$Sel{KnsHaK7c&Pp#R|`|Cd(-001C>PvEzd8H2_C_l5@m`u}4uNdb^9jZ(J?6=3kk z@q7^}d4z{Z`!nFeZ%C5}BIz8KKOcG64cftuyysPHXB=&zhL$rC+2CV_{}Yx1F(x@b zNnQ(~COzn4gSCvD3M&tg14swp@lbjX^0FYKvDg;8M8tSR;ko@Lt%EzZDiu%}z@0WN zNzd=mknS{-YZ8kqf@J_(&%8Eec-6yXY_b-c8VYOr@3lwaGxP@oTsitz0yWz)MW|IXq_oToQ9#3 zq1PC{#C9@*0B0uNJ&GYw3M%sIxLP3KCmD6imXmI&;+6ev`e|}(q3R+0Z9WfaBOLC(4U1>*aU=rx=9nq>(;i<~XAhH`?%t6JL$U6v)af;o2d zYUbQ7az=Y2dKJm{0KfhY@@ar%b&PX!h<$kvuiO}C_!cqYBUs;QI#`?;5oxFMrzQUI z-f|u8dNLa)Bvey8jvXB4{??aM7uQ|aVegfD-&2xb)7(EIrU0z%1AU@<;<-17ejmuM zLuJGx>Apj{%`@FDSSJ0+i;h8a{4r~R?iow`$w3W8_cY1dG+XYlYww@3xAR;d;xU`S zf~V^WTYu&9iZve>>m#yPMCvXIInRqJ7sTW_>+OB)a%Zix`#9{&&AXSmDC8H(7#155 zt()ZEO7%Y%8X(VL<4`feYa5Yk=_x5$($7_=OweU8sHiB>rJTX1KnRx5+TzK0#!L=j z?uJW3PPHC8vFMQU!*=|pjm$~bSL{(UIfB~p)(_g1K73#pDjtCis0!Yv9h zO9i4&eYa2I0-l8h_k9)jWAdN79p8ew3Ku%XgjJLjl!X^TniT#6^+7r%{i9O~=5LN~ zv$xbpa=Kuoxp=j>Sw25ZM)!*{D|1a%tO%hhpxp};Ll#&#=vry47L8F|UO>;cbjv@X zsxfoO393;X`-!E?zYo=q&{!rDMVR9D6($Yc!P+d()tGfg;O;0~UUphSvJE;|Vc;dVWR42R4|FGsFAlrJww)bhC5xu8$ zFy&mRi8%Sp%^}bP4rnw7c?ub2`OE{4eu0BBSr4h!=V*9O_3g4nz?2Sc`8MtxtSqHr z=7J!6s&%aya(myUVy&_LtRkg`+MN^OGhfnSU>;M7`?yV2j~0RPvH{aUbrTDDM&QL~ z^;WvJQzv|juKS^|#fJx~rw{1XRznd7G;rJQe73dLFrR`aui@nQ?gisBE8#=n^p;hG zW9BtFYm8|g9Fg($lx(L49wKpeLUE>i{lA-z{k3-ha1b<*g8T3My?^CiHtyBeb)^Q1CXa7hke7CIDf@L`1RYbs$IU%s1n@9o{YbEv37ruB zsR8SLV|GO*4ub}cd&+LT6JwPfx zX$<$qtsOM4Zm_+ug=H1iZoL;gSE68%hYY~#Vv93JkGVYZ5i`5X)}-| zt5TNHW>(f=GS79^@S5p)n{gyg(HOtgT5&&w6TvLEQaI;{oYCsfSMGhE>$dNnK_`%W z5m<0PQGY=#l_)sQTC6K?6}P58|8QHr;F@sO<#au?@#2eho_|&OrJ&k&SrqBn z-lG~zYwfW;e9a+$%e(7oy^6GdVO?1~VC9g)0sRSi3P> zUdZU%oz_BoT|$K5;JZ>FFRYM&Ae2^X!lcLpMz^B)3lV(D5^EOw64mBvWwP3~pKZ&H z8VYs)gQG_4b!)g>$l}A&%`QO^n!IF(xvVp+W2Tl-fvSnQV#=9VvA0Nn zJ=C*MO7hx@25n`@fmo5_mbCkO(SjFmeM$!w!zjNQcVg47%ojA`Q&7_)cNYEW+lI8i zA!9!u8MG2Dl;$)nWY{?=ewbSRXVCT?n>81g`weXN|CtVpEtG%o>PB7(=Vlbg%C1oN zmJOucsf_g3`K#3%v%}8#RKnk&$6};Om2jiAWy`b5ZZlQdIZrqiA$&*v<>QSJ^63*QRp4kw zdk;KEdG|^Jhcpf7AV;G4_YQDJ2hN+AHlRRYU@l;Ay(%huK$Fz-5(w0l6-UJ2QF~NM z5*0_n;8(K~5=X<3S7TRR5c@v{d`fFs<;RuRBndBv?H_^R{O{6^!;DkP=4fsnzK3{{ z3V14#_f!ukWfHf15Z|MCE52dW|XNz z*wIO_Oyw*C%K-U_TlvQs_`sMH87q}zc7hf(AT%h1Y&2j*3=aOpJ{RJ+30WJRJjKLtj_UoAA>_$O4Bh64ZNU{3GkE4++0n9;8H5t3Y*pXZ;1G3D;k=ZR_kda2$q0aT{uS0yrZIc0 zdO`&pKkgdSZ?9StE+&CXlTq^wR;wBvH13N6|W+WcU#2eaecDEnlQ z*6-c-N3WS{>0{<0?=<-6`Q_E?!Mg%NbMqye~Mk=}Taue`BkVW}rzT6uzf*!MoY@GSAHboF+DFKmOo z7+aQ^T*WsV)VqZQ*IHrscVjUNqMTSJHN^de8EL(ncTM)N%jHh%Tn5w_ABG9%W7yAe z%0;(6D0vgP`5AIrN68Dh$jg7H5}MY&@KwQ27=Ktw=>dA((0RqK4$MMBmYCwH@Obmw z5#7+u`xvJvwxCG(FHno+{@k8~W}+m9`$vn^viukja0a-`xo`k-POuh}R2Tt6x5+QL zn^%gOvOwF?r&>faQwxH$GDWl*G0KdZpRRTklf{bd)RdON8GY|iE+Awg7)Mr;1Z2y8!+H!72Pyw=|kyvWQ_8g~OLeB~|{J6b@A(H^kbEV=#29t-8V$%1Jjw-g{D zB<1b=;@Ge-hXq|u>Pd1e@IFcPI8NaEnJgkol*TXA$ryFgY@vw*j{mgS+x+f`@Rfmv z1|R%#yrmG0TD#7oN#^-2vGA`IV@=YAlj{63l*yP<`o*5t+abmJGv!zBFYP4RUf885uviqWu_GJ5qUQqbhvT41=Lo1mZn3+DhqEr&IWYIGL~Yq zv}eUiDp^kgRnlLwl*QY@#@nX7m+c6crcjd`6U}@F;Ks=&@U^ehpit^iwfomT6+S^m zx6JRlc0QRIpvt>o$?KcAQA5jYLpy3})V?RLv@l_UJm$qfaN?@F$u0L54Z7wy2=Vkw zN{JIMPTo7^c_hXW-#h`vN_QW7E+dYDc>I}c3jF+NYG{3~P`BRFrhW(iu;^|x*<)5> zD8ns2_`-&5GEu*5Lz5@94Zunv7`vRh5NqC2_7oXZvDv*RXwgXUAY~*D^*D06k~9h9 zS2b1>pgaTdJnBBS$sU?xoyv?=wXH7 zSGW3O^}g=&H6pG4?0=C+f8k&0HY{d4Ypsl}j&krvO@1CpxOEA_IMX8Q%+p?xd$n-Z zsJmj*XFPCP4@yMOcbs^j>9_QXwNOj1>qX>eWBxUogeTACwjyf)F?e*H^)98Ey!3qM zP=QcX`~KjzoeWI6G`^q$X!Gef=^P|1sD$&({2)4_dcIgvm2teX)eTr|)Dw_a8-;`C zRKSd#fQ3lsOVIWLv|!6C%14U3yFajvOb^DT@!p_u>bfwu6AWZM6G%pxoSzVW2q}c- zvnE@LHWcO(sY`)Yvj)9hoAG&|IH%tu7}2j4SDZhYdT3eJ)Y8smd`(lE2*a3-o1Q3>osL`99EbqR`@q zNAOzYQ9EMcVSIQDxxtBJM>Ukq&Dq029s&lwHF+es2Q_$CtrjS~854Mt1;t+_{RxlT;U;u@XV<}%*%F~S2^aX_b8O^;q zJfw8DqF$q&!*nRfGv($`<+o;JXlr7eN%CfJ9Z`0qJ;D^_jDoZN*YHdrOd!G_2c?iL zsovgszPiK63#QLz)>mjhPiWr7zBW4Y={fexP1?I!yVTNth8VTj+{oqX=}9O{=IGck zsHfR6+S1mJR?7f&ZChqoAMUqfOncZxczCHfJJc}fsuz#gMW~tZTp00ePfOk4l2w9> z2kCDj^57~vl#u{qmy;}`#Yp1pqM zH?3U!^IE=l;3G^J&lx}X7yHeeh>B+(5xk1bKEwb>#iBofuD7H|TK1k_Fj79~iU(3$ z&h9Mr%&GpQ^ntbQz8F)~=OSxVCjYH*9G>O9w7k~RYpw0*l$qM}{akRVw$5^&TCbj% zTQ=UXHpr4kpxxpS^|{?go9AH^Umj=dK5eS<->k+an~M%zn=94)oDj*ekZ7!!G*~PY z{;~UrX2lWu2DU^qTuUO$VwEK8U!M>UAB_r0oe z1)fi9=6Cx@s{7bT+j@G){16px)Nb?SrNYWk{(H1~#o){Yv%xQ}`0jtM5XrF+d4?v z0|cPB`Ve2k0`rS5(O7<8z>{ZvPXf5pQq04R&KXC;7*1QoD{bq-kj0_@oezhu#Ao@w z0BQR6e;a{=z0{qNr|i!%!`hQt5I|rKql+C7$ohrzjC2jegDx#qMq_e!!hihY$0ZaiMarffq9xvn(eKdrmVz3v z=#tLp+P&JUrku9tojEdKI444*5MUI6wDTPO%ij}CBT7-iR}X>|m7 z$^#G}WCTEpstsP~{7?CR5|AGj zbXOaeP{$t9uxBhmaWc7`;ILL5+00`wxvLM|8s*q3RfGsh3RhI*=6$h)X7Vi*RmWcDDCAOnhjy zL@KP>0C7nl2zut|*cz8Q{j6cesZr9I%ICZw!YBpTrgcx(|Gn5c_;Aq$5b3 zolW_WYbT+Xca%b2sid8TiYm%*MO4)1o9C3I*G{7A2TStET6dIie6s%Hq zEjEn*+M_IkRQ+373`b{EDV*Fc=%8(`h`rvd926WBVe&fO%qFFt3oDd|O`aPc43ZW( z`M{!y;=rhqpaUnq$u4^}E_w!eQ^s(0pRo|{bLHtMjVBawu5(^f5`BRY=*?W|*19&E z=nzfXeoOK^(d@FbCT2gi5E}BwTPmA^_Sx{!JHJr6RmT`Lc(_Ja#iQ^dEFq2}zv z#g>ce5(0|!EG(@BJt$XOG@W^d_aYkjXX|M(21==VKg&V8TQds5!W8!nYcHa&M$GkO z#l>N=#`C7|P_VZMA`qnSr!p7{6g?$CNZZdJlt{9hENtYwTR0AIWz=I%%)ado->kDOJuwK8%EXu(>`(`2J!(f z&$P-`=c|u4dU#hPIq-EI9Z9txQ}B_gfP{%G$TyO$&OT^zq-S4KUv0;R4=~_5PI6D< zDLGZa=L>^VQ&CITNK*=jo?RAmmv@2*j&+1Y5n@^uri&S(BkmyIoE0iEQl1WHwO-tq~$26$cmin$l7h`zg`ZZ zC~9XgnM-6i$l>PIDp`LV$cpASX&FA{b_Qt+hLMB62Ow3`A4LE)*ba5VUT#y}^;1tI z@;eHR|B3V99ng4=L*~1z%)Te!x~ahz+6SNXh_LR_cvAgBmWLe+d~aB`%_!icII2$< zb!8RLckzUo9#8`KNnp0m!7^-rG>ZEXsOtl@l%Zh;ZjRH7N2cSvW0;vxM6xo-eT&%@ zkf|dQQmvq+iijO2sNlG^{V&XIlX@@FVreJ;!LA^~x0yyA3!!&yu35eMNkONBr8}FG z6oX)Zbp6!Soxl#6z_;Is4U-Z}m5L^zq3t$NOi+pPZl9Y(AC7HF^M%u z$})1|`j)Z|#o<^iO0}jl91)n( zjs~{;K1apm5JPcD4pea=rN%8hUpGl5k~W|_^%HbYHKt*i0TEr3vWN(-7=3-bA6Eca z@VfdO6r-!Tccwf7-UO~XnbdFh_;j$~2@&e&EV(7*{`}?G; zx@iksXJq4i!{M)>nWGf$aRTOX=xuUf(RP^isO^fSD)#ZcGPx3i$U&H(wFo+|yjJ}` zCX;ol*yv);db5-&*s$`miVRxjeywr$s@!}sPkT~Lw79I0=#`Fh#(PbZg?H=E92Id6g;qnPz;SG z>_uJlq?{ASV=pT~_x9>(SAx4-YP2vFBe|jx$gwhtW;qPGs;ZO%eS2OxGo?A?(~!~a zZY7EKG`Kx(Z(6MAIl$xvg{n~!JVLk|8(gnHrIV9&__rUzZ&b^(vK136YD>W31BLa5 z14XPCq#-$`$B8Pa+!LPju7@9=x(lCmAU7n?*Tas{jngWFhrnOYFXm_cK0Rv%=%K^| zWF-ja?|jG^i@8IZU_RV`z3W~f9=9ORU*qp-tq*Q3Uj%$;vU(7JmIWXneWd z3U698QmafojTteOaETYOFmG%$Xh-|RDEs%;LQkNoW_*&(o_<$9bcf;RlD6MqzrIi( zW56S+f;J_F-_ax90LPl`{8MMiWvG-U)Yb4>7TzYXO$ZYYER0dD+(Df_+*Q!hChdNlfm5+WcTf^~zkhkbV%vbLrak*mr_>zss$~Oc z1Nrb|=iz@q<&=bBB|<7wf&^Hr;-~h9R^J?grt8Lw&gnqcp>cjX5uoQqLDaLo6QOaU zpN)d~-2n{w2C$Goz&)LsamgB?V})3LG*{^tfwX~?g8Vpl7{8wP0A73aY+YmIYh_8D zd9#5ZS5oqc>=4g>M_XlTvOy=@92mf!oJJr`uou>(kt)VR*@;wB|1YJ1Z-ms}#y#JE zlZXlDpCcz4W+4>ju}&+$1aXTb8LVH?X5uv6QX(kJ;@Hs+`0VgJsNlvkNXGFf?+uQT zMw1v#U)Z(YT0mffi`(UIAaDgDblxNeBKj_+B92C4CwWEFPd1463+R3Xyzmp(p6|&4 zRFy@!Ko`h372dq}9nq0e4#n_OBt>PwXnThol=|@~NUeSVyoHLjUyGdzdyn7y9IyNh zUt|GMkBmNwnL)RL!4SgbCwT2~T~ zvSP!)sUkR*zx7T;55R`^@6B+OBzAdYW^B8kLq_ND;ZXyNIN4tiDLAlb!DVdhzp!2~l`ydZF#a8B5BVgTO zdAFb3oW?BT`JA@XxRTPFv2)y`;qCx~O9>B`E(x9=g^-XCAA^C|5E5z>3ZEKcm^grl zj)vWb2!U4(uL=T*1p%=`B(Lo3>|_QZHe$85vdUy-#AF_rsASHP!^NgVK!_)y6AF?J zk|qNib~A=`+AFpSgZu3aVy^I1TcklniXLsl5+{VL*qjo6A3lJR;@M0&Ps3NGMUiF= zK6IMOVoHX8e%2SsX!J^zk}^%uEsB_7Iw9;9QU9LK9k@!M8?DVM%Ovk;--{4rPjw`19KJ;CfPH(6D0bOSrt0=Exf87N*K&0jBis6fIa8 zs`J8)5TFF7i^qAY!;s<|NXgdaNy)ZmGanRg(@ptzIf&hkdpX32{TT}(^j-ERxu+&% zhHp)Ib-;fUgSz{IRV!v(x_ai8LV3tRTW4irA*X}Jv&vCOHo@1HY+BXe$ZnHbc!W4q z5L|3542|PdS$O%QlP63%!S#JUt=iNX#n>-)Lzhi^gP_gfQl2QS4|i0ArH>;fX3Jzl zZo?Fhbn1acnaC{mh%ADy(jvWtWNCYq{27autagnMyf#{{iju6s>8$q6DM176NpuYd zJD!~DvA<0WoH=#w*4BfDHHpF}Fm&ESGEvaiVkn#DUFhhJMTIX>`WQDb6RL!xZ zzuB^WBXBrRR@!xl+TKr2UWrMBq=W|j%jt!Qho|Zx;nAh@)QA&Dcgdnfb);>F9qV{S zc2z_}lq+KqaHUq@+dlTqT3Ie@b-ZtODFnq}5(l zKS0K|y*5@5t5cU(YwcL_eOkeJ#^x7<$6{civi_w|#GRd?Q}2esMXC&lME>_#seG z@ZTf!#jj8BEwB^|mb?U=J9i(bcOCWSo*1jr8!x~+L=!;p$-AydK=6!{Yk`{u6g0Q9 z)2cy600k|UDXRQac#UqcX=%aA6Z|urgLJ=-HIyTVWvn)HnKwN(OttnOaDnIu=gF5! zGg_aKfwmV42rPIoZ32yq`R+PSAX*%#QKHDi^t6;T^g4*59kC6&R-i;rDEJ_@(S5Aa z0;G?}Z}Un=b4HL|&-U1*#%)$4QPZ?{TDT)dI0k>NZM3@uP%?MG#1APS&E0LM%+VVF zAW7J$6DATW9b8rj8NN@{RAO^liP$=o#ehjt*lwYYW&Rr5f}qyZgZc1Szjo9ITx<>1 zR+|uYEen9x8ut`zWjE%*$IyYmd@oUM6{EZtyK7sW;oL?XAAXE_;C%3D259bApExe; z65|F>{F@$U^U31H&e}!HqP-D72iDn!=f+|qqy~#qoZq&@21#=*&3~`ju2&|(4+aM% zF)nRP>qzYme|^W3Ab%=)!u76oH!lDs?uWbMur3|6|4z3=+AlICE@J!BSTqF0OTB%G zEG%2R#e#y{Y!K#nfv7$b9z;>C93yE;8C1=JpY~%?qUibUa8(Z7inl89Z(aC(kF~51 z`!k+0|J-7`mK+9DrHkKkt84NAQsX*_J0OxS4V$4AdCi3sVxBSdlr&#+XPsf-9df$2 zgrBisu(1Rk99cz6CDA#O(b@aF+}^4!al#>*y+zW-a=~&1%c(bTdC67O-UlJ_vJ{g9 zvN+RrhOWXniaI&yotjBO_F)mZRlef00M5RN`>}U*q!&zxq&H*eFC#=(atp+!_thE> zN$}m+xGKjW30bwYiNWe1+n(%d@0`74x9mHgeK7VH~QZL2 zX9v3qyowta^4mXBxn~H-I?ATevE4ikdV9rxc-k;Bi6r|;8HnkeTl zCj|Gw-QC^Y-Q6KTa1HJjc=P@Dt@ZBPQ&Z=R^z`W?y{4TsOsr<{&@6B_GB}Xw&%YfnpN^bgRFN|^?7-H>J$#| z23Eq5AVD#v6t8hN^=dU%H#S_}r+kXib(#5MG9hP?!Zw4mBlXo1h>MqluaifM3TPZ}%{3ccULB}_N4h?^XQ;B)AGW$E> zkfPWF8KE`6_;d>287=()K@LR|@u1lS$sKVnIJ^cW&u)u8-0Xeah)8mLWkGAU0XdpL z>E3228G$gCZXfC`A5MY`X=MzdtT)AdMM3JO>#4oB>TBb>G}!%u@e9@=vT4miH6z>2 zWSTs_()ADl+B%#OSko@*xR?Zg?Z!@onS5Ydh(SNNbMAP|J#q_gy!kP}e!`VVEGREV z9-$WrLSd?qIbXXO>CB;UUnFW-)>Q(7dxHl|_!Ye10Dr-@U$q5*R~qfgd+kR~MhHsF zU?9Jj)e|#66b^Tt)W8_a{mU?1--nlGSK}C?^7PF!?3FKdi~ft{ zXPietoFU5WYo!BSE7tOErf7Yy>*$wA9jOkTDZ9r%ANd9BqER_7$_$Lhnh&eP{#5)% zw@lfs8L;G4q<_3gz2s;y*p5%_F|Hy{y17AUceuFjcv>`_fH~gGaf60~!mXx&4wxyO zQ7X4F_8Yv*SmOZt0b4fkhtU>4$m3*V&7T+>a0x_%b?I2*oJ<_yQ6druJT0s{1%Y!j zvb8xiFLlg!%N#$VC1j_v3=8RYGSY(B-U8n9N2N)#c*TDB%UJ!H3T;rVzd{1V6+)4T zB^5}K7hrO#%mxnpO>N$c6K7RnNT~EEaim~RsuvC&CL}S5^nuk!YUpYbp(20wUMX!} zcksW|UepDgx+D@EKLYF}lU_{+%Tyt?QPXzv3wM$C#1u+^>ZCzvH-=cy_!c4Q>3G0z z%<}Qg)Jtka*dQ+B%^!G_!m?w(Tg3s`Dkp(ykoh~#Tcj6YUa;kHQV^vQkWm4_f5>{+ z*-mR$k`l-Wt;_z3tvYcyL`2I`_Y{PUoL*0(!z|OTp^h*Z?9dTa{5V=zmW65-T)64r zM>IDoIT$z>6)K|2fCn=gkOwbGH{;#HGm9V$1E83qKO0UP^c7(P~gik5hP$^H!r zX9+{Y&%R*=!45-qw|ohwSI8ryO5S*UBBprW>MMl_xgFUC%j zZwstz)2qGmrpTF9Q2jRqc>y$^{mY&WZVp0A8~!ypUT+ko**z)ZW$^Y7B#qN<(4MhU z;PXb&d7THhp+WG#_yGG_v%nGB{zjj*+K?#e`EVl`n4}Ku(znGrD!c-$<0S#Z|P zE=HjM?iQljR;A%X|5ZqtT&{Y_6x+Y?lsLKR2NGP<@~Inx#pqj}vmVD&zMie`6kgutcS2xw?|XRlIT?ps zv{btB{G9eF7Z14Pf=3ErY07^XFV`(T^GTEJBr5hM+iXggkWFJE9IVD-8jIpb4!~%! zts>sSO4gd5LeiVf_#3B}nh4QW$k}Xc%2LWsP_kxF&RW^rb;8^L2yNv82Ap`z;k7Y$ z(9ONmJ5FAUXu<;2B(k)Jc?b$5VhXTV@%}jtC`9WJ7uhV!?4GUEFCL$kFK2WsBrU+< zBoz-5VwWcqDdzoKjpbC_l5T?V&9d9yEKbUB254==vzkL*!tpo%KL~0G)S3dR*C|tx{C0MY zJB+^-21p25@W~BOYrz=Db`4DRTa#eUFz^?!kVcM8OA`H{iDmiNAHUd)0L#9Wl*(^? zBz#>AI4n8Sb;&I~ z1Wvhi#JZvgAnZ(21n|2>6s-0AiGd(8tQ#{+A0Yqrla)2voHnIpX!e9_9*XLogjMB( zWqKC@BOx401h?9vQiUX3wY-d3x3!&(GnhjbhJ)7s-i-WicU~Aci{nNIOAVA|se(<0 zW@m3=XP2#|hcAR`rZri%KW~D!+6{r18F3NI!8lVAbzc~4N+c%Eqb-|eKxFQgx0F%( znR=Y$k{TAcg%MM1?5MW0t%Mx6^Cq+}_$kb@8W@568T)nmw1!LsRrginZ*`7&{e}ws zK@$uXwpM#|R$ri)eu%A8k>$xO+Dm7TE)5t*HH$O1{ru5^@GSTTb5Cx!_) zi0PWL+(w^_Zlll4A(Vs@lrUAXc#_7%5AVN}a0ExIM^B%A+*JFtwk7a8-8B!eA)a`uzX(8VU;kQCFs!Z9GB!=-X6hWl%7QT}k5eL%KT#Y2KNw0i- z`I~7z*vOmu`-$=@WL67rdOb($*5}v504vqu#u+onCGi|n7hb(IU+C`xty2pd1C-ts z+q|IYhyN1*A*5TEQpeP^U3gHJH1hR8b(y(eEwaChFMVew z{=-j=vL##2pzr3C)AQtdl+$p$5+V{FK)UI5QflNzafQM-)FfGRwifaaD%A=N&@Jlz zufVS&&ku@d=4cs8j0z3Ge6>AZOUziPwuztzAu^m z)4m0NO3L@H_pWC*X$hu@&#Rti51&(`+duC+-(IhU7i-&}{~+J_obrEmkvtFd6`iy{ z$AZ4&c;hGY5$ny}KG62l>4DHQ7|`t*{4Bm>@*7=|goGs1V?=+;+jHa}=ojpw-TcHq zu7}pg{(~(%N(1fcd90ozL2d^^pT&0ti;mM3+Ptk z#?i4nkO$~79WgJSatNR32k2if2~h)cgK_?$w+PS&NukcMp{!OW(}U@u4af||5NiL* zoisQrkupNSy&3Bvs^Rcpi_t{9@B>KQ3sv8sxcPMp^7Hc#*;3?6#@RGWi;Ii9lNmw4 zmP>UZ977lD(5unHoYd@SY;43Ruy4_;No~|@)L~4OKXd6Yz>w3fC@d@#E6}LWsDv%4 z)SzoaNO^;AaB{)T4v-U2=|%f@g$^|YYN|&5(u)n zyb%7pThM3J=YPv-NuOK>W2go7X)SWpr>G1%`sR-cJ0S z+x`MIUjt?<+uTiJ^qXvCZ`Zti+I|RW6z0D0y8)I#RqBe3K!OTod<-&bl=p1}CJwfU z2<7jm@Ug$~r93>@NNE`k=>zM=Mh{VTk^?P#E!9fJw-OMbsfhe%p$9;C+dW3)d-}(F z#CyYA`WZBnb5Mm3{#>Jd0se7!dIk$zlYNG^5Bzg*3I=TLz*3Mm66(hOI$my_si>(e ztc(Q7AFW^EL)(sRo7*~1rDlq|?M!4#RGRFWdFc zLAI+9X0XYLi{LEpN%q`2o71eQ!7gGfvk}Ahl{Mr()g=za|CQnaD!$7any!_Zl_1z& zJt4?_7qyARqK({NU8nEbp3T4Z(9_6n)CM_C%c-|(a9OBxYHH@_%zC-B=VU|nzU)Ua zw~7P?iQA9VFlj5L9%OCW0|;h2S{FF=ruuEIZEd+`6r?QIcI~;cN;77dE~TP2*c>wJ z0geYwEChJ7mQ6X+P_>piEmx;Ab=h04Muz9DIezC{ZcJx`Y?7drvZQB+xw<5%xXF=Zxu}o{|Mx8S zV#Ldw);5sMtAOGKK7_wH_8%fe*liu26w<+c(zBoi_nrRY@eMG2 zLZeH<)&vz{Rh|srfokReksq%H1j2wJfN}H>51^)oK||xggoZ_O?Y$N;=ar?bOe0 zi0>+xf4kEEc%UlG=}LJW{2;5o@{CMDm}Id=Cujo>{%+1K_4{8IR-g~f#|9?0T-R*z+(MD=s*Xrt6k3>>%~e6E5cvKhpcb1UG}c{$ln zrqRaZ3#b(BsEIgme0~lI7Y+paHDLdpAafY}P{n|6f~g;^=On8F!gpr2#jrhU5S2=H zE}M-9dvMYJ{kT4y6iZu?8J-YawFv(FUL2EM3Y>oOj~?jyV8%p$5!-QbA52jFXgWI{ zn{o)}6D>$AtJAxQ;w_~PAlMj~5GS7i8<3?X-xW|7#h6Ww(1*c?)aRL~wIM{*s7It% z{!FdOnK`B1F8~HdhNhI%dz!ySl}5MH3X^VB7Y*JvKkAYlT4E#?!hFBB>l3HL<5H`8 z*l#tFM$94Gcds9BuU`~1lE!$nB7WzC(bp6D=e9Y+gtxB-i>2)pBk>R7Y1sqnoS@Q6 zeP{Cd&_Z4|uel)v=JBZ{mGrU1(7}>wQbC-zyA8#^y<66p$`jg-P;e`Z$O4tELy~C>IpUHVqqpb% z)JtG|`Ig&UChL6XzEdRp-8BWwgeX%uqAO`ALGWtZUMYSQ@Kal^y3z93@1>$n$zChv|Yfq&(-sJ>c81cy^ZVGUD0QF15chnrPk6cL}e7d{fT2^k z7;-q}?{&R-{~(HSls~b6ii*fe>PYw22RYiq28lbZ+A0jQBNsONxJISJ9BZFcCi3So zfXGv%6QAM26~%QsHO#{$`~D2sL`}+cMrHse135}anEvPou2JVU+dffvX*;76rm~Uc ziH$XDYkq!3vz3RX*a?}z`|V$1R7(>imCukUTs1CKI=3;c#S2}$(I=Ktmb0?iW8*VR zOnRT*{Gz6>-8fu8Ka$On%V&p}tC>^+iyX`g%T-f<_P(VCyHvh!usGyl|_s=xkk`ZluWX1p&NF@{z z+~Xvfjlzvkcc!HhHveEO`a#h$Q`HZcIx46!Fw)6XGMnn&N>;$d;%CjXcw5KH@X4si zNStnX?m5e)g9FGNXNpt+!{g`pY>ZG&QL%IsBYC(aIAN*uepX7}#p=ZpTUL!}s9dF~ z5X(B*vR)UCq5n9b8CRZAHNpFxG;OtjT#>{G}MwQ7gHX!H}$mOMpkUdUm@dNs#YGF}ZWn$tdDa9=}-y*U2 z%Vx7g84}^+fuH28OEq!v%}?$-2^)c&mH6|ZIuvXDM6=2myJ!1X-@wGeA#FYua>bC= z_9e*#(QKWDU%5~;3J>3pw1?9Wk_%G%o*YTpbtKAzyJsXUiXIJyg2Xqeq&gk*G-TQ zWj$iWRl;#l`mUzbH2LQ0_ICi!sIF_kaY#)FXjF1t&hO-~=wZw|4!4S54|M*0Q_l&6 zpYqpaLW@R^!;HoTJ<*vDkOCX*m!{IJo~GtTC#mF z!>7fu?JM;VPpj0GQ0L%fj(k%ReoaG;MN5|EiQnG$WZ$m@7*tWj%G; zX=2{o@AN0$(*zU;Z zgyk0%)oz*!(CKaN+`6n=xU9y$1yQUOL${vf|A6pL6U}RRhZo;kKshcEttGuD-^FUo zSn3$jrS_pSof$ym7}TqFdda1|HZA?5VcwxJRj64rZ4*{L9psG~>%KdnB0*{YmT&8B zEjD1ENlt0gK-fa|$Z!A=bj5h{(}%Ah0(LTR1YEMIoLuSea*#ho8?grInlZ6NLUK4` zg#12ku%4RfoIEm=o(P^Ojjs)kj{`^IE4es8I^)MqyZ-K$*=m6F3)8}7W?`AHbC`1X z$lnQ>iM`rVHdXVR)9O$4aXtbuwVlbf%6?oc^3C&s+-2&nwP9CreN6KMTN1NBW=kGU z6pVo>PM7Knlv(9JjvTp7+||M1R?k=dIJQAiZf5(5b&bRMQ4b$YPk(pd`d$vn7gJ0W4mi-~tUD&30o&!yl)262_M_+x=4L&B3;XZsmK;)g6 z8J$jCdG%L{7e{)>0u<&xfVsdbtV2n7V}%m4kgo(dazSCb)N2_izhhrK$XISOs#9<% zTAo4#xbRwMkU52T4xg7-5`y)vx}ZCWW_csc9t>JZddCwohSE)g!0z2+sy0w^%WEh zJOdRcJ!cauLt((`^NI8RmGhyer;F{4r|O;M>Kzc)m!FclxTUpDParo}acs zpN9m825&4?fp)O@yuJMi!tm(G(8Sj#2P=ROKm;HLkN`*lWB_ss00%226*Ubl9X$hp z5x@dq1+W3wIXJnvd3Xf`i8%nmqG&i`yy6m)QnGULrwWRQO3G?#>Kd9_+B&*A06l#J zLj!=(cViP{Q!{f5OA9M&8(SMYdk4oKj!sU_NuhvbKnfrgkOoKxWB@V&S%7RlQsVS< z5gmFN5SGDcIIJUO##i|?-O|VF*RklS=tW%0%j?M*6`VZM-tAJG522VCt>c?d8J+Or z$1kB+>Lpn0>$|r(SdF9Gsc6_3;n<_2l{A#q<0YrzI3)sJtTDJbc=PjRW^exg024qH zz!%^U2>Bno&=o6({R>U@Fr$L-o^XiB+11%Ko@lgawmlq zUf*`#Ki-u=Q2GCG2LXNu39h87s;Z`jOrwSZ3yzAWt^w*yO>Oib!Ro}oKJY<7F;a4L z^n~OEw?Tv{a~Jk#w63*{IBp3FVo9KAxMau{t58A!-3r>ZM99!!JSfxvkpp~)-asse zVF^S=q%E`tzKfab7gAo97=97kTl%w_1MyFQc4v>Pos{-(g4O_HdqnRMgLGy~jJ@Yz zJ7!%DRme(<$YRZMeLn@$j-mK`zMMT{h3&9`y0yl8bhoaQihE)M&Us~3eN&T6h|v&0;GjF(?sET;Ialwh)*m0k$lK6(v_`=cEd z3WLP&aBJhA(F_h=lvADxx+7%RAK1o%1S#BKcnJ@Gc9mK_P@qNN#!~-^UKbC@O_I(* zXa!j)K*-dqQLE4}8m@-^`(wM>{EiLfz-*WKh0`aiy8sdm_bWk|q>L4KL=llHUIYwcD2xC&Ljs-2mw{Yl1F@VZA>00s$1OwV9cd zm70xsgX?uM#YmO0a|khZghLbAnH)3i_&M|Eg7P~x0Bm`WlNruDPZUzG&OjQC&cELa z<`ON-3gM}&kY-!EzDZ;4x8^R=Z!*hVjrAUcj5Cj@n0Sl!w#YE&KGyphg|s)VD?2A= z)D(uUs{$*(LkON>Wara*-%Q+Wm4RA>pf|16o%jjm+;KH~&q?(=k17&;p|yEr{?K(t zj;>I$DHX|cH1tdGg-8-z(uVexjJMulXCMXGejHsP=}RcLS@x6nZX5zxd5C}wu#R1e zwGr+W@Nk@*l~TlKXcQywEbg3P#Iwtua|H-6S+nH z&f+}FWvzH3<{deWNZr(QCuUTf%!lovBg$_P{cYYdhR78)UJ2-Zv2nh5xC~Jv!l}5_tT5HcIK_tG1~F2P{q0*(s;UeG zawz#c+GJzlen64Sf7Uf*JjPuoEGG=oT?1S=D2S3`9ato+b+mP)^#FUusmkif|3qk| z?9E3*0Pa63l)3|u3JDJj8@(5pyB9zmIUF7ik%h+~8gn+}4l0(ASPLZu7aepa9P>S$ zw%-Ifo*iHP-(Whpx&o0{{J&%ut9ttXry3J+J0FOD24$g}`k%lDk)A@DMYE3L{ijUu z!u}`!5e$HY97zG@9=CP0z2Deq6tp8}Lh1UEDLB^w4y0)vAPX9lD4MyhYDX`~A@{oL zKOZyDEdkI%mfI8Ed;R*Fy#Mbo!2i30A?8&~$is(m9wJsSn?#Xwg1iV~sXlYdRia6n zFlW02ZT7!y|5N{Wlm8O&+uC!q;%C`pmfB=mDvoQZQ&5afOC?gu{wE;Qr0ak7$^TR% zQjk!&+6`*q_xR>7n0<7JFe$i1DAD#1wEr=dkwKWsP?8V8EdXg_z@bh=Vi{$}V~hX3 zS}n&bigjw3q+J#~xJX6hKmmClRIJhR;ITgeS4eq*>?I22f@jdR$cm9%p6um-!R0q}VdmuX$;e+)wIx&J!%JgL&2C=-F|lzme@F zl2+X?@jVkk7_K^p2~iP-!wm|g$r3xbNx{3JGtC@d-Tz&86+F-cJF(;Up@yv{&ab9Ko82! z2gWuk4&Kzs$6<_FWVD-8*^>(4^g-lq{x5wpy_u^}kWVbBO!)A|B^V)`^zPGXf?Hh* z%|2+9G~ks^H*id;s`LykOvDg`rzB=Vq!8s7C1i?-`9yi5b_)X?*pTio)tmKp$oC5a=xIVQDFQuV7S&9Y{{9Fc z5D52T*2uN{WMW)kk7_|^eo8yrCuR2G=_=z-dp7b7g%1PYQ`*-Z(%?+C$;NI4_$Ti7 zjv9;>m;{&rcg&-wgbNH0=NBw#$B3{Wzl#EDW2$&2mWO+Djv|Io-DZ9=o z(Ai~?Vk|W=$SkR0dtI%wcGJypZNts5?aa-?iEvDk>A$(oWjnvllZ|K+VpjT~52wtk z*coZQ5uDl_ei`i38&~Z+L1>IDg=OlJhxDN8ExOE&OB?Y1w=2&`lTNLK`lsg6q)W>Wt*_W2INq%hNq){@fHH+_!awduqdBl3QO^Ob8j_Y zKf`y-+VgQRny`)4;<4UCU`wt&*D8`iGBptc_1p4~2&T0DAm-KS7Y)J=*q>GEh1e{3 z6MwxKagRV&Ot8zQC2RD)dD=vaJ-7Mqd$B3%`09Q$0S>>F)q)2Smz!8fCsKwYWq~{6 zi^-5xqRXl!yEQgo9gOPX45$)N#Kl`Q8_MU zUbn{@3iO?A9Mj}Opjhb?KMEv<`3yVSuj|2}PllDWwc5Bf(E<_js?v)8u&+3u5ft=U z@2jh97pSOSxv*xgmboc@M?+>xqX15s@HwqlBSiAalNn3`9($CRkyg(pBWoF8%;}L( zRsdCZV2mRI{DvJuXpw0?Zbu#hc-HxyrR5b(eyKNH-Zoq#XK?0mqEb>7upO=Bdhklx zOphZNQCTgJn6*~cuO$Mj&r_yKg?|9gI?}~bv~tI>g4q>Sj4qvx9;m^K0ktbTd2M3| z_!L4Ehi*qMr`fe1@a-+`W^v%#fBL%~PImRn4kw72Ctuglt=3+&7#=Ue3Qti2TqiX1 zXZU|C1H0XZ+4_^TA?p}n_HiIKIm&ZEEvO~Taeh$#IhSj@574sZ)An+MCc==YUGY>2 z$6m=vl1x9HPOWnLl@;=QQqX#Ji6)(hpKq*e{1(r;mpm|z>Psw&O^WNJ@Zp$UYan|% zIxM@z4#~ckAp=Fn`;Lq0zd-K@Z5N`eXV}r=Gzi_I!W>|_kEBR2wIC>2mi$oA~^3V`iL_vJ0 znJ$_FIHe^iUOoa(=B=@2fatnds1jr>BP$raoIRS{JWB;6Md}M=>K!4$Bwk9*^uR@v z4qPO-C4YVwUClQW^{r*&<<3Qt;?HtW;Nr_ZxL^Cg1*VHT9P>|YhL?S^LI!;~I*UX$ z^^hm9W3U@Ag_M%apQ+3%DVh76_J|~RRp*7_r}Bti$*9+G0eJ2!@F_vc9Z0yH7=C+N znnCifr9>!?qTG9;!e`;ad!-4Z?HpV&$}JGYopvDv6|y$VQh#_5YYKRt2gI%?n7;6Z ze_qUP@49GQCvuY13Hvb^h!qcvhG(%(hMb{Q!*zC7)f??=d&5FUW zi07`}Y;Ui6Wm1;4l(QtNgB11) zZ{PdY8Zn5J-5dGGduE>(;Qj~do^tN9pzvJ-_iN6{YrLmd^4ja4V4sk+CV>n6-a}!( zBlX-vs&46+$^kyj3i_Dv*R(XvYQ2y2G%xL$w^%*RIwpA{gsS8|Df(X;{zgFrudp8k z4Ws+YuL#dh7)*!cxoj{$;$gjj{?Y0ONYb~l=%)-tPSb@t8Zue5%UIiwl2Of(pQq~B zx`q7+YKCqyN#z>I1=`Dz^r|$Vd?iE$y3H^ag{sMuLs^w_F}qCJlT|)a-zvU+1_k|G zh(M{P!49I}P36~2Cb-XBx>3VVFS?5$Ob%s>vL}xkCw0-(N>Dw-&X6#gBy&NpnBv!^ zxj#hdps72=NlevrVU!+c%m`@~S&heFLH};TU@)wzg8=D_^f4rxE^B5&=@Sw&PWZHk z?*net&u3stATWeiyX@Q9&(J*t7-nh8+FLb1YU}3c+5dfYS!e*q?V-~BJ!eX83-P~$D&%IHo%DhdnQRb;xadq_EJ{G!Bcy;W_KJKW2 z?E%z@R@J?JpKn4vfyS?|_8qqpI=y6$mya`fn28ntA%vz)2kc5IC~$lz?U zTy+GDf!;xuO54Piv(L6kZAZrBwXwKNcJA7+x18aaQNw8z_n`AWGHOo?$5k0;nrbX| zICartLu3YUOq;Wc?@d=UȆ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 to type DealStateChange struct { ID abi.DealID - From *DealState - To *DealState + From DealState + To DealState } type DealProposalChanges struct { @@ -172,12 +182,36 @@ type ProposalIDState struct { Proposal markettypes.DealProposal } -func EmptyDealState() *DealState { - return &DealState{ - SectorStartEpoch: -1, - SlashEpoch: -1, - LastUpdatedEpoch: -1, + +type emptyDealState struct{} + +func (e *emptyDealState) SectorStartEpoch() abi.ChainEpoch { + return -1 +} + +func (e *emptyDealState) LastUpdatedEpoch() abi.ChainEpoch { + return -1 +} + +func (e *emptyDealState) SlashEpoch() abi.ChainEpoch { + return -1 +} + +func (e *emptyDealState) Equals(other DealState) bool { + if e.SectorStartEpoch() != other.SectorStartEpoch() { + return false } + if e.LastUpdatedEpoch() != other.LastUpdatedEpoch() { + return false + } + if e.SlashEpoch() != other.SlashEpoch() { + return false + } + return true +} + +func EmptyDealState() DealState { + return &emptyDealState{} } // returns the earned fees and pending fees for a given deal @@ -196,8 +230,8 @@ func GetDealFees(deal markettypes.DealProposal, height abi.ChainEpoch) (abi.Toke return ef, big.Sub(tf, ef) } -func IsDealActive(state markettypes.DealState) bool { - return state.SectorStartEpoch > -1 && state.SlashEpoch == -1 +func IsDealActive(state DealState) bool { + return state.SectorStartEpoch() > -1 && state.SlashEpoch() == -1 } func labelFromGoString(s string) (markettypes.DealLabel, error) { diff --git a/chain/actors/builtin/market/diff.go b/chain/actors/builtin/market/diff.go index ef3c2c28d..292299790 100644 --- a/chain/actors/builtin/market/diff.go +++ b/chain/actors/builtin/market/diff.go @@ -64,7 +64,7 @@ func (d *marketStatesDiffer) Add(key uint64, val *cbg.Deferred) error { if err != nil { return err } - d.Results.Added = append(d.Results.Added, DealIDState{abi.DealID(key), *ds}) + d.Results.Added = append(d.Results.Added, DealIDState{abi.DealID(key), ds}) return nil } @@ -77,7 +77,7 @@ func (d *marketStatesDiffer) Modify(key uint64, from, to *cbg.Deferred) error { if err != nil { return err } - if *dsFrom != *dsTo { + if !dsFrom.Equals(dsTo) { d.Results.Modified = append(d.Results.Modified, DealStateChange{abi.DealID(key), dsFrom, dsTo}) } return nil @@ -88,6 +88,6 @@ func (d *marketStatesDiffer) Remove(key uint64, val *cbg.Deferred) error { if err != nil { return err } - d.Results.Removed = append(d.Results.Removed, DealIDState{abi.DealID(key), *ds}) + d.Results.Removed = append(d.Results.Removed, DealIDState{abi.DealID(key), ds}) return nil } diff --git a/chain/actors/builtin/market/market.go b/chain/actors/builtin/market/market.go index 39473d560..13c09f91b 100644 --- a/chain/actors/builtin/market/market.go +++ b/chain/actors/builtin/market/market.go @@ -58,6 +58,9 @@ func Load(store adt.Store, act *types.Actor) (State, error) { case actorstypes.Version12: return load12(store, act.Head) + case actorstypes.Version13: + return load13(store, act.Head) + } } @@ -128,6 +131,9 @@ func MakeState(store adt.Store, av actorstypes.Version) (State, error) { case actorstypes.Version12: return make12(store) + case actorstypes.Version13: + return make13(store) + } return nil, xerrors.Errorf("unknown actor version %d", av) } @@ -162,10 +168,10 @@ type BalanceTable interface { type DealStates interface { ForEach(cb func(id abi.DealID, ds DealState) error) error - Get(id abi.DealID) (*DealState, bool, error) + Get(id abi.DealID) (DealState, bool, error) array() adt.Array - decode(*cbg.Deferred) (*DealState, error) + decode(*cbg.Deferred) (DealState, error) } type DealProposals interface { @@ -226,6 +232,9 @@ func DecodePublishStorageDealsReturn(b []byte, nv network.Version) (PublishStora case actorstypes.Version12: return decodePublishStorageDealsReturn12(b) + case actorstypes.Version13: + return decodePublishStorageDealsReturn13(b) + } return nil, xerrors.Errorf("unknown actor version %d", av) } @@ -233,7 +242,17 @@ func DecodePublishStorageDealsReturn(b []byte, nv network.Version) (PublishStora type DealProposal = markettypes.DealProposal type DealLabel = markettypes.DealLabel -type DealState = markettypes.DealState +type DealState interface { + SectorStartEpoch() abi.ChainEpoch // -1 if not yet included in proven sector + LastUpdatedEpoch() abi.ChainEpoch // -1 if deal state never updated + SlashEpoch() abi.ChainEpoch // -1 if deal never slashed + + Equals(other DealState) bool +} + +func DealStatesEqual(a, b DealState) bool { + return DealStatesEqual(a, b) +} type DealStateChanges struct { Added []DealIDState @@ -249,8 +268,8 @@ type DealIDState struct { // DealStateChange is a change in deal state from -> to type DealStateChange struct { ID abi.DealID - From *DealState - To *DealState + From DealState + To DealState } type DealProposalChanges struct { @@ -263,12 +282,35 @@ type ProposalIDState struct { Proposal markettypes.DealProposal } -func EmptyDealState() *DealState { - return &DealState{ - SectorStartEpoch: -1, - SlashEpoch: -1, - LastUpdatedEpoch: -1, +type emptyDealState struct{} + +func (e *emptyDealState) SectorStartEpoch() abi.ChainEpoch { + return -1 +} + +func (e *emptyDealState) LastUpdatedEpoch() abi.ChainEpoch { + return -1 +} + +func (e *emptyDealState) SlashEpoch() abi.ChainEpoch { + return -1 +} + +func (e *emptyDealState) Equals(other DealState) bool { + if e.SectorStartEpoch() != other.SectorStartEpoch() { + return false } + if e.LastUpdatedEpoch() != other.LastUpdatedEpoch() { + return false + } + if e.SlashEpoch() != other.SlashEpoch() { + return false + } + return true +} + +func EmptyDealState() DealState { + return &emptyDealState{} } // returns the earned fees and pending fees for a given deal @@ -287,8 +329,8 @@ func GetDealFees(deal markettypes.DealProposal, height abi.ChainEpoch) (abi.Toke return ef, big.Sub(tf, ef) } -func IsDealActive(state markettypes.DealState) bool { - return state.SectorStartEpoch > -1 && state.SlashEpoch == -1 +func IsDealActive(state DealState) bool { + return state.SectorStartEpoch() > -1 && state.SlashEpoch() == -1 } func labelFromGoString(s string) (markettypes.DealLabel, error) { @@ -313,5 +355,6 @@ func AllCodes() []cid.Cid { (&state10{}).Code(), (&state11{}).Code(), (&state12{}).Code(), + (&state13{}).Code(), } } diff --git a/chain/actors/builtin/market/state.go.template b/chain/actors/builtin/market/state.go.template index 1eab9d743..467057660 100644 --- a/chain/actors/builtin/market/state.go.template +++ b/chain/actors/builtin/market/state.go.template @@ -175,7 +175,7 @@ type dealStates{{.v}} struct { adt.Array } -func (s *dealStates{{.v}}) Get(dealID abi.DealID) (*DealState, bool, error) { +func (s *dealStates{{.v}}) Get(dealID abi.DealID) (DealState, bool, error) { var deal{{.v}} market{{.v}}.DealState found, err := s.Array.Get(uint64(dealID), &deal{{.v}}) if err != nil { @@ -185,7 +185,7 @@ func (s *dealStates{{.v}}) Get(dealID abi.DealID) (*DealState, bool, error) { return nil, false, nil } deal := fromV{{.v}}DealState(deal{{.v}}) - return &deal, true, nil + return deal, true, nil } func (s *dealStates{{.v}}) ForEach(cb func(dealID abi.DealID, ds DealState) error) error { @@ -195,31 +195,57 @@ func (s *dealStates{{.v}}) ForEach(cb func(dealID abi.DealID, ds DealState) erro }) } -func (s *dealStates{{.v}}) decode(val *cbg.Deferred) (*DealState, error) { +func (s *dealStates{{.v}}) decode(val *cbg.Deferred) (DealState, error) { var ds{{.v}} market{{.v}}.DealState if err := ds{{.v}}.UnmarshalCBOR(bytes.NewReader(val.Raw)); err != nil { return nil, err } ds := fromV{{.v}}DealState(ds{{.v}}) - return &ds, nil + return ds, nil } func (s *dealStates{{.v}}) array() adt.Array { return s.Array } -func fromV{{.v}}DealState(v{{.v}} market{{.v}}.DealState) DealState { - ret := DealState{ - SectorStartEpoch: v{{.v}}.SectorStartEpoch, - LastUpdatedEpoch: v{{.v}}.LastUpdatedEpoch, - SlashEpoch: v{{.v}}.SlashEpoch, - VerifiedClaim: 0, - } - {{if (ge .v 9)}} - ret.VerifiedClaim = verifregtypes.AllocationId(v{{.v}}.VerifiedClaim) - {{end}} +type dealStateV{{.v}} struct { + ds{{.v}} market{{.v}}.DealState +} - return ret +func (d dealStateV{{.v}}) SectorStartEpoch() abi.ChainEpoch { + return d.ds{{.v}}.SectorStartEpoch +} + +func (d dealStateV{{.v}}) LastUpdatedEpoch() abi.ChainEpoch { + return d.ds{{.v}}.LastUpdatedEpoch +} + +func (d dealStateV{{.v}}) SlashEpoch() abi.ChainEpoch { + return d.ds{{.v}}.SlashEpoch +} + +func (d dealStateV{{.v}}) Equals(other DealState) bool { + if ov{{.v}}, ok := other.(dealStateV{{.v}}); ok { + return d.ds{{.v}} == ov{{.v}}.ds{{.v}} + } + + if d.SectorStartEpoch() != other.SectorStartEpoch() { + return false + } + if d.LastUpdatedEpoch() != other.LastUpdatedEpoch() { + return false + } + if d.SlashEpoch() != other.SlashEpoch() { + return false + } + + return true +} + +var _ DealState = (*dealStateV{{.v}})(nil) + +func fromV{{.v}}DealState(v{{.v}} market{{.v}}.DealState) DealState { + return dealStateV{{.v}}{v{{.v}}} } type dealProposals{{.v}} struct { diff --git a/chain/actors/builtin/market/v0.go b/chain/actors/builtin/market/v0.go index ca6970dfa..d797d53f8 100644 --- a/chain/actors/builtin/market/v0.go +++ b/chain/actors/builtin/market/v0.go @@ -154,7 +154,7 @@ type dealStates0 struct { adt.Array } -func (s *dealStates0) Get(dealID abi.DealID) (*DealState, bool, error) { +func (s *dealStates0) Get(dealID abi.DealID) (DealState, bool, error) { var deal0 market0.DealState found, err := s.Array.Get(uint64(dealID), &deal0) if err != nil { @@ -164,7 +164,7 @@ func (s *dealStates0) Get(dealID abi.DealID) (*DealState, bool, error) { return nil, false, nil } deal := fromV0DealState(deal0) - return &deal, true, nil + return deal, true, nil } func (s *dealStates0) ForEach(cb func(dealID abi.DealID, ds DealState) error) error { @@ -174,28 +174,57 @@ func (s *dealStates0) ForEach(cb func(dealID abi.DealID, ds DealState) error) er }) } -func (s *dealStates0) decode(val *cbg.Deferred) (*DealState, error) { +func (s *dealStates0) decode(val *cbg.Deferred) (DealState, error) { var ds0 market0.DealState if err := ds0.UnmarshalCBOR(bytes.NewReader(val.Raw)); err != nil { return nil, err } ds := fromV0DealState(ds0) - return &ds, nil + return ds, nil } func (s *dealStates0) array() adt.Array { return s.Array } -func fromV0DealState(v0 market0.DealState) DealState { - ret := DealState{ - SectorStartEpoch: v0.SectorStartEpoch, - LastUpdatedEpoch: v0.LastUpdatedEpoch, - SlashEpoch: v0.SlashEpoch, - VerifiedClaim: 0, +type dealStateV0 struct { + ds0 market0.DealState +} + +func (d dealStateV0) SectorStartEpoch() abi.ChainEpoch { + return d.ds0.SectorStartEpoch +} + +func (d dealStateV0) LastUpdatedEpoch() abi.ChainEpoch { + return d.ds0.LastUpdatedEpoch +} + +func (d dealStateV0) SlashEpoch() abi.ChainEpoch { + return d.ds0.SlashEpoch +} + +func (d dealStateV0) Equals(other DealState) bool { + if ov0, ok := other.(dealStateV0); ok { + return d.ds0 == ov0.ds0 } - return ret + if d.SectorStartEpoch() != other.SectorStartEpoch() { + return false + } + if d.LastUpdatedEpoch() != other.LastUpdatedEpoch() { + return false + } + if d.SlashEpoch() != other.SlashEpoch() { + return false + } + + return true +} + +var _ DealState = (*dealStateV0)(nil) + +func fromV0DealState(v0 market0.DealState) DealState { + return dealStateV0{v0} } type dealProposals0 struct { diff --git a/chain/actors/builtin/market/v10.go b/chain/actors/builtin/market/v10.go index 878f0d465..290c17d09 100644 --- a/chain/actors/builtin/market/v10.go +++ b/chain/actors/builtin/market/v10.go @@ -153,7 +153,7 @@ type dealStates10 struct { adt.Array } -func (s *dealStates10) Get(dealID abi.DealID) (*DealState, bool, error) { +func (s *dealStates10) Get(dealID abi.DealID) (DealState, bool, error) { var deal10 market10.DealState found, err := s.Array.Get(uint64(dealID), &deal10) if err != nil { @@ -163,7 +163,7 @@ func (s *dealStates10) Get(dealID abi.DealID) (*DealState, bool, error) { return nil, false, nil } deal := fromV10DealState(deal10) - return &deal, true, nil + return deal, true, nil } func (s *dealStates10) ForEach(cb func(dealID abi.DealID, ds DealState) error) error { @@ -173,30 +173,57 @@ func (s *dealStates10) ForEach(cb func(dealID abi.DealID, ds DealState) error) e }) } -func (s *dealStates10) decode(val *cbg.Deferred) (*DealState, error) { +func (s *dealStates10) decode(val *cbg.Deferred) (DealState, error) { var ds10 market10.DealState if err := ds10.UnmarshalCBOR(bytes.NewReader(val.Raw)); err != nil { return nil, err } ds := fromV10DealState(ds10) - return &ds, nil + return ds, nil } func (s *dealStates10) array() adt.Array { return s.Array } -func fromV10DealState(v10 market10.DealState) DealState { - ret := DealState{ - SectorStartEpoch: v10.SectorStartEpoch, - LastUpdatedEpoch: v10.LastUpdatedEpoch, - SlashEpoch: v10.SlashEpoch, - VerifiedClaim: 0, +type dealStateV10 struct { + ds10 market10.DealState +} + +func (d dealStateV10) SectorStartEpoch() abi.ChainEpoch { + return d.ds10.SectorStartEpoch +} + +func (d dealStateV10) LastUpdatedEpoch() abi.ChainEpoch { + return d.ds10.LastUpdatedEpoch +} + +func (d dealStateV10) SlashEpoch() abi.ChainEpoch { + return d.ds10.SlashEpoch +} + +func (d dealStateV10) Equals(other DealState) bool { + if ov10, ok := other.(dealStateV10); ok { + return d.ds10 == ov10.ds10 } - ret.VerifiedClaim = verifregtypes.AllocationId(v10.VerifiedClaim) + if d.SectorStartEpoch() != other.SectorStartEpoch() { + return false + } + if d.LastUpdatedEpoch() != other.LastUpdatedEpoch() { + return false + } + if d.SlashEpoch() != other.SlashEpoch() { + return false + } - return ret + return true +} + +var _ DealState = (*dealStateV10)(nil) + +func fromV10DealState(v10 market10.DealState) DealState { + return dealStateV10{v10} } type dealProposals10 struct { diff --git a/chain/actors/builtin/market/v11.go b/chain/actors/builtin/market/v11.go index a64272209..56a4c6038 100644 --- a/chain/actors/builtin/market/v11.go +++ b/chain/actors/builtin/market/v11.go @@ -153,7 +153,7 @@ type dealStates11 struct { adt.Array } -func (s *dealStates11) Get(dealID abi.DealID) (*DealState, bool, error) { +func (s *dealStates11) Get(dealID abi.DealID) (DealState, bool, error) { var deal11 market11.DealState found, err := s.Array.Get(uint64(dealID), &deal11) if err != nil { @@ -163,7 +163,7 @@ func (s *dealStates11) Get(dealID abi.DealID) (*DealState, bool, error) { return nil, false, nil } deal := fromV11DealState(deal11) - return &deal, true, nil + return deal, true, nil } func (s *dealStates11) ForEach(cb func(dealID abi.DealID, ds DealState) error) error { @@ -173,30 +173,57 @@ func (s *dealStates11) ForEach(cb func(dealID abi.DealID, ds DealState) error) e }) } -func (s *dealStates11) decode(val *cbg.Deferred) (*DealState, error) { +func (s *dealStates11) decode(val *cbg.Deferred) (DealState, error) { var ds11 market11.DealState if err := ds11.UnmarshalCBOR(bytes.NewReader(val.Raw)); err != nil { return nil, err } ds := fromV11DealState(ds11) - return &ds, nil + return ds, nil } func (s *dealStates11) array() adt.Array { return s.Array } -func fromV11DealState(v11 market11.DealState) DealState { - ret := DealState{ - SectorStartEpoch: v11.SectorStartEpoch, - LastUpdatedEpoch: v11.LastUpdatedEpoch, - SlashEpoch: v11.SlashEpoch, - VerifiedClaim: 0, +type dealStateV11 struct { + ds11 market11.DealState +} + +func (d dealStateV11) SectorStartEpoch() abi.ChainEpoch { + return d.ds11.SectorStartEpoch +} + +func (d dealStateV11) LastUpdatedEpoch() abi.ChainEpoch { + return d.ds11.LastUpdatedEpoch +} + +func (d dealStateV11) SlashEpoch() abi.ChainEpoch { + return d.ds11.SlashEpoch +} + +func (d dealStateV11) Equals(other DealState) bool { + if ov11, ok := other.(dealStateV11); ok { + return d.ds11 == ov11.ds11 } - ret.VerifiedClaim = verifregtypes.AllocationId(v11.VerifiedClaim) + if d.SectorStartEpoch() != other.SectorStartEpoch() { + return false + } + if d.LastUpdatedEpoch() != other.LastUpdatedEpoch() { + return false + } + if d.SlashEpoch() != other.SlashEpoch() { + return false + } - return ret + return true +} + +var _ DealState = (*dealStateV11)(nil) + +func fromV11DealState(v11 market11.DealState) DealState { + return dealStateV11{v11} } type dealProposals11 struct { diff --git a/chain/actors/builtin/market/v12.go b/chain/actors/builtin/market/v12.go index 56e651a9b..cf7687203 100644 --- a/chain/actors/builtin/market/v12.go +++ b/chain/actors/builtin/market/v12.go @@ -153,7 +153,7 @@ type dealStates12 struct { adt.Array } -func (s *dealStates12) Get(dealID abi.DealID) (*DealState, bool, error) { +func (s *dealStates12) Get(dealID abi.DealID) (DealState, bool, error) { var deal12 market12.DealState found, err := s.Array.Get(uint64(dealID), &deal12) if err != nil { @@ -163,7 +163,7 @@ func (s *dealStates12) Get(dealID abi.DealID) (*DealState, bool, error) { return nil, false, nil } deal := fromV12DealState(deal12) - return &deal, true, nil + return deal, true, nil } func (s *dealStates12) ForEach(cb func(dealID abi.DealID, ds DealState) error) error { @@ -173,30 +173,57 @@ func (s *dealStates12) ForEach(cb func(dealID abi.DealID, ds DealState) error) e }) } -func (s *dealStates12) decode(val *cbg.Deferred) (*DealState, error) { +func (s *dealStates12) decode(val *cbg.Deferred) (DealState, error) { var ds12 market12.DealState if err := ds12.UnmarshalCBOR(bytes.NewReader(val.Raw)); err != nil { return nil, err } ds := fromV12DealState(ds12) - return &ds, nil + return ds, nil } func (s *dealStates12) array() adt.Array { return s.Array } -func fromV12DealState(v12 market12.DealState) DealState { - ret := DealState{ - SectorStartEpoch: v12.SectorStartEpoch, - LastUpdatedEpoch: v12.LastUpdatedEpoch, - SlashEpoch: v12.SlashEpoch, - VerifiedClaim: 0, +type dealStateV12 struct { + ds12 market12.DealState +} + +func (d dealStateV12) SectorStartEpoch() abi.ChainEpoch { + return d.ds12.SectorStartEpoch +} + +func (d dealStateV12) LastUpdatedEpoch() abi.ChainEpoch { + return d.ds12.LastUpdatedEpoch +} + +func (d dealStateV12) SlashEpoch() abi.ChainEpoch { + return d.ds12.SlashEpoch +} + +func (d dealStateV12) Equals(other DealState) bool { + if ov12, ok := other.(dealStateV12); ok { + return d.ds12 == ov12.ds12 } - ret.VerifiedClaim = verifregtypes.AllocationId(v12.VerifiedClaim) + if d.SectorStartEpoch() != other.SectorStartEpoch() { + return false + } + if d.LastUpdatedEpoch() != other.LastUpdatedEpoch() { + return false + } + if d.SlashEpoch() != other.SlashEpoch() { + return false + } - return ret + return true +} + +var _ DealState = (*dealStateV12)(nil) + +func fromV12DealState(v12 market12.DealState) DealState { + return dealStateV12{v12} } type dealProposals12 struct { diff --git a/chain/actors/builtin/market/v13.go b/chain/actors/builtin/market/v13.go new file mode 100644 index 000000000..d270319ce --- /dev/null +++ b/chain/actors/builtin/market/v13.go @@ -0,0 +1,404 @@ +package market + +import ( + "bytes" + "fmt" + + "github.com/ipfs/go-cid" + cbg "github.com/whyrusleeping/cbor-gen" + "golang.org/x/xerrors" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-bitfield" + rlepluslazy "github.com/filecoin-project/go-bitfield/rle" + "github.com/filecoin-project/go-state-types/abi" + actorstypes "github.com/filecoin-project/go-state-types/actors" + "github.com/filecoin-project/go-state-types/builtin" + market13 "github.com/filecoin-project/go-state-types/builtin/v13/market" + adt13 "github.com/filecoin-project/go-state-types/builtin/v13/util/adt" + markettypes "github.com/filecoin-project/go-state-types/builtin/v9/market" + "github.com/filecoin-project/go-state-types/manifest" + + "github.com/filecoin-project/lotus/chain/actors" + "github.com/filecoin-project/lotus/chain/actors/adt" + verifregtypes "github.com/filecoin-project/lotus/chain/actors/builtin/verifreg" + "github.com/filecoin-project/lotus/chain/types" +) + +var _ State = (*state13)(nil) + +func load13(store adt.Store, root cid.Cid) (State, error) { + out := state13{store: store} + err := store.Get(store.Context(), root, &out) + if err != nil { + return nil, err + } + return &out, nil +} + +func make13(store adt.Store) (State, error) { + out := state13{store: store} + + s, err := market13.ConstructState(store) + if err != nil { + return nil, err + } + + out.State = *s + + return &out, nil +} + +type state13 struct { + market13.State + store adt.Store +} + +func (s *state13) TotalLocked() (abi.TokenAmount, error) { + fml := types.BigAdd(s.TotalClientLockedCollateral, s.TotalProviderLockedCollateral) + fml = types.BigAdd(fml, s.TotalClientStorageFee) + return fml, nil +} + +func (s *state13) BalancesChanged(otherState State) (bool, error) { + otherState13, ok := otherState.(*state13) + if !ok { + // there's no way to compare different versions of the state, so let's + // just say that means the state of balances has changed + return true, nil + } + return !s.State.EscrowTable.Equals(otherState13.State.EscrowTable) || !s.State.LockedTable.Equals(otherState13.State.LockedTable), nil +} + +func (s *state13) StatesChanged(otherState State) (bool, error) { + otherState13, ok := otherState.(*state13) + if !ok { + // there's no way to compare different versions of the state, so let's + // just say that means the state of balances has changed + return true, nil + } + return !s.State.States.Equals(otherState13.State.States), nil +} + +func (s *state13) States() (DealStates, error) { + stateArray, err := adt13.AsArray(s.store, s.State.States, market13.StatesAmtBitwidth) + if err != nil { + return nil, err + } + return &dealStates13{stateArray}, nil +} + +func (s *state13) ProposalsChanged(otherState State) (bool, error) { + otherState13, ok := otherState.(*state13) + if !ok { + // there's no way to compare different versions of the state, so let's + // just say that means the state of balances has changed + return true, nil + } + return !s.State.Proposals.Equals(otherState13.State.Proposals), nil +} + +func (s *state13) Proposals() (DealProposals, error) { + proposalArray, err := adt13.AsArray(s.store, s.State.Proposals, market13.ProposalsAmtBitwidth) + if err != nil { + return nil, err + } + return &dealProposals13{proposalArray}, nil +} + +func (s *state13) EscrowTable() (BalanceTable, error) { + bt, err := adt13.AsBalanceTable(s.store, s.State.EscrowTable) + if err != nil { + return nil, err + } + return &balanceTable13{bt}, nil +} + +func (s *state13) LockedTable() (BalanceTable, error) { + bt, err := adt13.AsBalanceTable(s.store, s.State.LockedTable) + if err != nil { + return nil, err + } + return &balanceTable13{bt}, nil +} + +func (s *state13) VerifyDealsForActivation( + minerAddr address.Address, deals []abi.DealID, currEpoch, sectorExpiry abi.ChainEpoch, +) (weight, verifiedWeight abi.DealWeight, err error) { + w, vw, _, err := market13.ValidateDealsForActivation(&s.State, s.store, deals, minerAddr, sectorExpiry, currEpoch) + return w, vw, err +} + +func (s *state13) NextID() (abi.DealID, error) { + return s.State.NextID, nil +} + +type balanceTable13 struct { + *adt13.BalanceTable +} + +func (bt *balanceTable13) ForEach(cb func(address.Address, abi.TokenAmount) error) error { + asMap := (*adt13.Map)(bt.BalanceTable) + var ta abi.TokenAmount + return asMap.ForEach(&ta, func(key string) error { + a, err := address.NewFromBytes([]byte(key)) + if err != nil { + return err + } + return cb(a, ta) + }) +} + +type dealStates13 struct { + adt.Array +} + +func (s *dealStates13) Get(dealID abi.DealID) (DealState, bool, error) { + var deal13 market13.DealState + found, err := s.Array.Get(uint64(dealID), &deal13) + if err != nil { + return nil, false, err + } + if !found { + return nil, false, nil + } + deal := fromV13DealState(deal13) + return deal, true, nil +} + +func (s *dealStates13) ForEach(cb func(dealID abi.DealID, ds DealState) error) error { + var ds13 market13.DealState + return s.Array.ForEach(&ds13, func(idx int64) error { + return cb(abi.DealID(idx), fromV13DealState(ds13)) + }) +} + +func (s *dealStates13) decode(val *cbg.Deferred) (DealState, error) { + var ds13 market13.DealState + if err := ds13.UnmarshalCBOR(bytes.NewReader(val.Raw)); err != nil { + return nil, err + } + ds := fromV13DealState(ds13) + return ds, nil +} + +func (s *dealStates13) array() adt.Array { + return s.Array +} + +type dealStateV13 struct { + ds13 market13.DealState +} + +func (d dealStateV13) SectorStartEpoch() abi.ChainEpoch { + return d.ds13.SectorStartEpoch +} + +func (d dealStateV13) LastUpdatedEpoch() abi.ChainEpoch { + return d.ds13.LastUpdatedEpoch +} + +func (d dealStateV13) SlashEpoch() abi.ChainEpoch { + return d.ds13.SlashEpoch +} + +func (d dealStateV13) Equals(other DealState) bool { + if ov13, ok := other.(dealStateV13); ok { + return d.ds13 == ov13.ds13 + } + + if d.SectorStartEpoch() != other.SectorStartEpoch() { + return false + } + if d.LastUpdatedEpoch() != other.LastUpdatedEpoch() { + return false + } + if d.SlashEpoch() != other.SlashEpoch() { + return false + } + + return true +} + +var _ DealState = (*dealStateV13)(nil) + +func fromV13DealState(v13 market13.DealState) DealState { + return dealStateV13{v13} +} + +type dealProposals13 struct { + adt.Array +} + +func (s *dealProposals13) Get(dealID abi.DealID) (*DealProposal, bool, error) { + var proposal13 market13.DealProposal + found, err := s.Array.Get(uint64(dealID), &proposal13) + if err != nil { + return nil, false, err + } + if !found { + return nil, false, nil + } + + proposal, err := fromV13DealProposal(proposal13) + if err != nil { + return nil, true, xerrors.Errorf("decoding proposal: %w", err) + } + + return &proposal, true, nil +} + +func (s *dealProposals13) ForEach(cb func(dealID abi.DealID, dp DealProposal) error) error { + var dp13 market13.DealProposal + return s.Array.ForEach(&dp13, func(idx int64) error { + dp, err := fromV13DealProposal(dp13) + if err != nil { + return xerrors.Errorf("decoding proposal: %w", err) + } + + return cb(abi.DealID(idx), dp) + }) +} + +func (s *dealProposals13) decode(val *cbg.Deferred) (*DealProposal, error) { + var dp13 market13.DealProposal + if err := dp13.UnmarshalCBOR(bytes.NewReader(val.Raw)); err != nil { + return nil, err + } + + dp, err := fromV13DealProposal(dp13) + if err != nil { + return nil, err + } + + return &dp, nil +} + +func (s *dealProposals13) array() adt.Array { + return s.Array +} + +func fromV13DealProposal(v13 market13.DealProposal) (DealProposal, error) { + + label, err := fromV13Label(v13.Label) + + if err != nil { + return DealProposal{}, xerrors.Errorf("error setting deal label: %w", err) + } + + return DealProposal{ + PieceCID: v13.PieceCID, + PieceSize: v13.PieceSize, + VerifiedDeal: v13.VerifiedDeal, + Client: v13.Client, + Provider: v13.Provider, + + Label: label, + + StartEpoch: v13.StartEpoch, + EndEpoch: v13.EndEpoch, + StoragePricePerEpoch: v13.StoragePricePerEpoch, + + ProviderCollateral: v13.ProviderCollateral, + ClientCollateral: v13.ClientCollateral, + }, nil +} + +func fromV13Label(v13 market13.DealLabel) (DealLabel, error) { + if v13.IsString() { + str, err := v13.ToString() + if err != nil { + return markettypes.EmptyDealLabel, xerrors.Errorf("failed to convert string label to string: %w", err) + } + return markettypes.NewLabelFromString(str) + } + + bs, err := v13.ToBytes() + if err != nil { + return markettypes.EmptyDealLabel, xerrors.Errorf("failed to convert bytes label to bytes: %w", err) + } + return markettypes.NewLabelFromBytes(bs) +} + +func (s *state13) GetState() interface{} { + return &s.State +} + +var _ PublishStorageDealsReturn = (*publishStorageDealsReturn13)(nil) + +func decodePublishStorageDealsReturn13(b []byte) (PublishStorageDealsReturn, error) { + var retval market13.PublishStorageDealsReturn + if err := retval.UnmarshalCBOR(bytes.NewReader(b)); err != nil { + return nil, xerrors.Errorf("failed to unmarshal PublishStorageDealsReturn: %w", err) + } + + return &publishStorageDealsReturn13{retval}, nil +} + +type publishStorageDealsReturn13 struct { + market13.PublishStorageDealsReturn +} + +func (r *publishStorageDealsReturn13) IsDealValid(index uint64) (bool, int, error) { + + set, err := r.ValidDeals.IsSet(index) + if err != nil || !set { + return false, -1, err + } + maskBf, err := bitfield.NewFromIter(&rlepluslazy.RunSliceIterator{ + Runs: []rlepluslazy.Run{rlepluslazy.Run{Val: true, Len: index}}}) + if err != nil { + return false, -1, err + } + before, err := bitfield.IntersectBitField(maskBf, r.ValidDeals) + if err != nil { + return false, -1, err + } + outIdx, err := before.Count() + if err != nil { + return false, -1, err + } + return set, int(outIdx), nil + +} + +func (r *publishStorageDealsReturn13) DealIDs() ([]abi.DealID, error) { + return r.IDs, nil +} + +func (s *state13) GetAllocationIdForPendingDeal(dealId abi.DealID) (verifregtypes.AllocationId, error) { + + allocations, err := adt13.AsMap(s.store, s.PendingDealAllocationIds, builtin.DefaultHamtBitwidth) + if err != nil { + return verifregtypes.NoAllocationID, xerrors.Errorf("failed to load allocation id for %d: %w", dealId, err) + } + + var allocationId cbg.CborInt + found, err := allocations.Get(abi.UIntKey(uint64(dealId)), &allocationId) + if err != nil { + return verifregtypes.NoAllocationID, xerrors.Errorf("failed to load allocation id for %d: %w", dealId, err) + } + if !found { + return verifregtypes.NoAllocationID, nil + } + + return verifregtypes.AllocationId(allocationId), nil + +} + +func (s *state13) ActorKey() string { + return manifest.MarketKey +} + +func (s *state13) ActorVersion() actorstypes.Version { + return actorstypes.Version13 +} + +func (s *state13) Code() cid.Cid { + code, ok := actors.GetActorCodeID(s.ActorVersion(), s.ActorKey()) + if !ok { + panic(fmt.Errorf("didn't find actor %v code id for actor version %d", s.ActorKey(), s.ActorVersion())) + } + + return code +} diff --git a/chain/actors/builtin/market/v2.go b/chain/actors/builtin/market/v2.go index ba84e3b03..5ced3c8a3 100644 --- a/chain/actors/builtin/market/v2.go +++ b/chain/actors/builtin/market/v2.go @@ -154,7 +154,7 @@ type dealStates2 struct { adt.Array } -func (s *dealStates2) Get(dealID abi.DealID) (*DealState, bool, error) { +func (s *dealStates2) Get(dealID abi.DealID) (DealState, bool, error) { var deal2 market2.DealState found, err := s.Array.Get(uint64(dealID), &deal2) if err != nil { @@ -164,7 +164,7 @@ func (s *dealStates2) Get(dealID abi.DealID) (*DealState, bool, error) { return nil, false, nil } deal := fromV2DealState(deal2) - return &deal, true, nil + return deal, true, nil } func (s *dealStates2) ForEach(cb func(dealID abi.DealID, ds DealState) error) error { @@ -174,28 +174,57 @@ func (s *dealStates2) ForEach(cb func(dealID abi.DealID, ds DealState) error) er }) } -func (s *dealStates2) decode(val *cbg.Deferred) (*DealState, error) { +func (s *dealStates2) decode(val *cbg.Deferred) (DealState, error) { var ds2 market2.DealState if err := ds2.UnmarshalCBOR(bytes.NewReader(val.Raw)); err != nil { return nil, err } ds := fromV2DealState(ds2) - return &ds, nil + return ds, nil } func (s *dealStates2) array() adt.Array { return s.Array } -func fromV2DealState(v2 market2.DealState) DealState { - ret := DealState{ - SectorStartEpoch: v2.SectorStartEpoch, - LastUpdatedEpoch: v2.LastUpdatedEpoch, - SlashEpoch: v2.SlashEpoch, - VerifiedClaim: 0, +type dealStateV2 struct { + ds2 market2.DealState +} + +func (d dealStateV2) SectorStartEpoch() abi.ChainEpoch { + return d.ds2.SectorStartEpoch +} + +func (d dealStateV2) LastUpdatedEpoch() abi.ChainEpoch { + return d.ds2.LastUpdatedEpoch +} + +func (d dealStateV2) SlashEpoch() abi.ChainEpoch { + return d.ds2.SlashEpoch +} + +func (d dealStateV2) Equals(other DealState) bool { + if ov2, ok := other.(dealStateV2); ok { + return d.ds2 == ov2.ds2 } - return ret + if d.SectorStartEpoch() != other.SectorStartEpoch() { + return false + } + if d.LastUpdatedEpoch() != other.LastUpdatedEpoch() { + return false + } + if d.SlashEpoch() != other.SlashEpoch() { + return false + } + + return true +} + +var _ DealState = (*dealStateV2)(nil) + +func fromV2DealState(v2 market2.DealState) DealState { + return dealStateV2{v2} } type dealProposals2 struct { diff --git a/chain/actors/builtin/market/v3.go b/chain/actors/builtin/market/v3.go index f6a0891e7..35dd9c29a 100644 --- a/chain/actors/builtin/market/v3.go +++ b/chain/actors/builtin/market/v3.go @@ -149,7 +149,7 @@ type dealStates3 struct { adt.Array } -func (s *dealStates3) Get(dealID abi.DealID) (*DealState, bool, error) { +func (s *dealStates3) Get(dealID abi.DealID) (DealState, bool, error) { var deal3 market3.DealState found, err := s.Array.Get(uint64(dealID), &deal3) if err != nil { @@ -159,7 +159,7 @@ func (s *dealStates3) Get(dealID abi.DealID) (*DealState, bool, error) { return nil, false, nil } deal := fromV3DealState(deal3) - return &deal, true, nil + return deal, true, nil } func (s *dealStates3) ForEach(cb func(dealID abi.DealID, ds DealState) error) error { @@ -169,28 +169,57 @@ func (s *dealStates3) ForEach(cb func(dealID abi.DealID, ds DealState) error) er }) } -func (s *dealStates3) decode(val *cbg.Deferred) (*DealState, error) { +func (s *dealStates3) decode(val *cbg.Deferred) (DealState, error) { var ds3 market3.DealState if err := ds3.UnmarshalCBOR(bytes.NewReader(val.Raw)); err != nil { return nil, err } ds := fromV3DealState(ds3) - return &ds, nil + return ds, nil } func (s *dealStates3) array() adt.Array { return s.Array } -func fromV3DealState(v3 market3.DealState) DealState { - ret := DealState{ - SectorStartEpoch: v3.SectorStartEpoch, - LastUpdatedEpoch: v3.LastUpdatedEpoch, - SlashEpoch: v3.SlashEpoch, - VerifiedClaim: 0, +type dealStateV3 struct { + ds3 market3.DealState +} + +func (d dealStateV3) SectorStartEpoch() abi.ChainEpoch { + return d.ds3.SectorStartEpoch +} + +func (d dealStateV3) LastUpdatedEpoch() abi.ChainEpoch { + return d.ds3.LastUpdatedEpoch +} + +func (d dealStateV3) SlashEpoch() abi.ChainEpoch { + return d.ds3.SlashEpoch +} + +func (d dealStateV3) Equals(other DealState) bool { + if ov3, ok := other.(dealStateV3); ok { + return d.ds3 == ov3.ds3 } - return ret + if d.SectorStartEpoch() != other.SectorStartEpoch() { + return false + } + if d.LastUpdatedEpoch() != other.LastUpdatedEpoch() { + return false + } + if d.SlashEpoch() != other.SlashEpoch() { + return false + } + + return true +} + +var _ DealState = (*dealStateV3)(nil) + +func fromV3DealState(v3 market3.DealState) DealState { + return dealStateV3{v3} } type dealProposals3 struct { diff --git a/chain/actors/builtin/market/v4.go b/chain/actors/builtin/market/v4.go index 629e833b6..bc9e61c88 100644 --- a/chain/actors/builtin/market/v4.go +++ b/chain/actors/builtin/market/v4.go @@ -149,7 +149,7 @@ type dealStates4 struct { adt.Array } -func (s *dealStates4) Get(dealID abi.DealID) (*DealState, bool, error) { +func (s *dealStates4) Get(dealID abi.DealID) (DealState, bool, error) { var deal4 market4.DealState found, err := s.Array.Get(uint64(dealID), &deal4) if err != nil { @@ -159,7 +159,7 @@ func (s *dealStates4) Get(dealID abi.DealID) (*DealState, bool, error) { return nil, false, nil } deal := fromV4DealState(deal4) - return &deal, true, nil + return deal, true, nil } func (s *dealStates4) ForEach(cb func(dealID abi.DealID, ds DealState) error) error { @@ -169,28 +169,57 @@ func (s *dealStates4) ForEach(cb func(dealID abi.DealID, ds DealState) error) er }) } -func (s *dealStates4) decode(val *cbg.Deferred) (*DealState, error) { +func (s *dealStates4) decode(val *cbg.Deferred) (DealState, error) { var ds4 market4.DealState if err := ds4.UnmarshalCBOR(bytes.NewReader(val.Raw)); err != nil { return nil, err } ds := fromV4DealState(ds4) - return &ds, nil + return ds, nil } func (s *dealStates4) array() adt.Array { return s.Array } -func fromV4DealState(v4 market4.DealState) DealState { - ret := DealState{ - SectorStartEpoch: v4.SectorStartEpoch, - LastUpdatedEpoch: v4.LastUpdatedEpoch, - SlashEpoch: v4.SlashEpoch, - VerifiedClaim: 0, +type dealStateV4 struct { + ds4 market4.DealState +} + +func (d dealStateV4) SectorStartEpoch() abi.ChainEpoch { + return d.ds4.SectorStartEpoch +} + +func (d dealStateV4) LastUpdatedEpoch() abi.ChainEpoch { + return d.ds4.LastUpdatedEpoch +} + +func (d dealStateV4) SlashEpoch() abi.ChainEpoch { + return d.ds4.SlashEpoch +} + +func (d dealStateV4) Equals(other DealState) bool { + if ov4, ok := other.(dealStateV4); ok { + return d.ds4 == ov4.ds4 } - return ret + if d.SectorStartEpoch() != other.SectorStartEpoch() { + return false + } + if d.LastUpdatedEpoch() != other.LastUpdatedEpoch() { + return false + } + if d.SlashEpoch() != other.SlashEpoch() { + return false + } + + return true +} + +var _ DealState = (*dealStateV4)(nil) + +func fromV4DealState(v4 market4.DealState) DealState { + return dealStateV4{v4} } type dealProposals4 struct { diff --git a/chain/actors/builtin/market/v5.go b/chain/actors/builtin/market/v5.go index 892588979..63743ba8d 100644 --- a/chain/actors/builtin/market/v5.go +++ b/chain/actors/builtin/market/v5.go @@ -149,7 +149,7 @@ type dealStates5 struct { adt.Array } -func (s *dealStates5) Get(dealID abi.DealID) (*DealState, bool, error) { +func (s *dealStates5) Get(dealID abi.DealID) (DealState, bool, error) { var deal5 market5.DealState found, err := s.Array.Get(uint64(dealID), &deal5) if err != nil { @@ -159,7 +159,7 @@ func (s *dealStates5) Get(dealID abi.DealID) (*DealState, bool, error) { return nil, false, nil } deal := fromV5DealState(deal5) - return &deal, true, nil + return deal, true, nil } func (s *dealStates5) ForEach(cb func(dealID abi.DealID, ds DealState) error) error { @@ -169,28 +169,57 @@ func (s *dealStates5) ForEach(cb func(dealID abi.DealID, ds DealState) error) er }) } -func (s *dealStates5) decode(val *cbg.Deferred) (*DealState, error) { +func (s *dealStates5) decode(val *cbg.Deferred) (DealState, error) { var ds5 market5.DealState if err := ds5.UnmarshalCBOR(bytes.NewReader(val.Raw)); err != nil { return nil, err } ds := fromV5DealState(ds5) - return &ds, nil + return ds, nil } func (s *dealStates5) array() adt.Array { return s.Array } -func fromV5DealState(v5 market5.DealState) DealState { - ret := DealState{ - SectorStartEpoch: v5.SectorStartEpoch, - LastUpdatedEpoch: v5.LastUpdatedEpoch, - SlashEpoch: v5.SlashEpoch, - VerifiedClaim: 0, +type dealStateV5 struct { + ds5 market5.DealState +} + +func (d dealStateV5) SectorStartEpoch() abi.ChainEpoch { + return d.ds5.SectorStartEpoch +} + +func (d dealStateV5) LastUpdatedEpoch() abi.ChainEpoch { + return d.ds5.LastUpdatedEpoch +} + +func (d dealStateV5) SlashEpoch() abi.ChainEpoch { + return d.ds5.SlashEpoch +} + +func (d dealStateV5) Equals(other DealState) bool { + if ov5, ok := other.(dealStateV5); ok { + return d.ds5 == ov5.ds5 } - return ret + if d.SectorStartEpoch() != other.SectorStartEpoch() { + return false + } + if d.LastUpdatedEpoch() != other.LastUpdatedEpoch() { + return false + } + if d.SlashEpoch() != other.SlashEpoch() { + return false + } + + return true +} + +var _ DealState = (*dealStateV5)(nil) + +func fromV5DealState(v5 market5.DealState) DealState { + return dealStateV5{v5} } type dealProposals5 struct { diff --git a/chain/actors/builtin/market/v6.go b/chain/actors/builtin/market/v6.go index b57d49f91..5900eace9 100644 --- a/chain/actors/builtin/market/v6.go +++ b/chain/actors/builtin/market/v6.go @@ -151,7 +151,7 @@ type dealStates6 struct { adt.Array } -func (s *dealStates6) Get(dealID abi.DealID) (*DealState, bool, error) { +func (s *dealStates6) Get(dealID abi.DealID) (DealState, bool, error) { var deal6 market6.DealState found, err := s.Array.Get(uint64(dealID), &deal6) if err != nil { @@ -161,7 +161,7 @@ func (s *dealStates6) Get(dealID abi.DealID) (*DealState, bool, error) { return nil, false, nil } deal := fromV6DealState(deal6) - return &deal, true, nil + return deal, true, nil } func (s *dealStates6) ForEach(cb func(dealID abi.DealID, ds DealState) error) error { @@ -171,28 +171,57 @@ func (s *dealStates6) ForEach(cb func(dealID abi.DealID, ds DealState) error) er }) } -func (s *dealStates6) decode(val *cbg.Deferred) (*DealState, error) { +func (s *dealStates6) decode(val *cbg.Deferred) (DealState, error) { var ds6 market6.DealState if err := ds6.UnmarshalCBOR(bytes.NewReader(val.Raw)); err != nil { return nil, err } ds := fromV6DealState(ds6) - return &ds, nil + return ds, nil } func (s *dealStates6) array() adt.Array { return s.Array } -func fromV6DealState(v6 market6.DealState) DealState { - ret := DealState{ - SectorStartEpoch: v6.SectorStartEpoch, - LastUpdatedEpoch: v6.LastUpdatedEpoch, - SlashEpoch: v6.SlashEpoch, - VerifiedClaim: 0, +type dealStateV6 struct { + ds6 market6.DealState +} + +func (d dealStateV6) SectorStartEpoch() abi.ChainEpoch { + return d.ds6.SectorStartEpoch +} + +func (d dealStateV6) LastUpdatedEpoch() abi.ChainEpoch { + return d.ds6.LastUpdatedEpoch +} + +func (d dealStateV6) SlashEpoch() abi.ChainEpoch { + return d.ds6.SlashEpoch +} + +func (d dealStateV6) Equals(other DealState) bool { + if ov6, ok := other.(dealStateV6); ok { + return d.ds6 == ov6.ds6 } - return ret + if d.SectorStartEpoch() != other.SectorStartEpoch() { + return false + } + if d.LastUpdatedEpoch() != other.LastUpdatedEpoch() { + return false + } + if d.SlashEpoch() != other.SlashEpoch() { + return false + } + + return true +} + +var _ DealState = (*dealStateV6)(nil) + +func fromV6DealState(v6 market6.DealState) DealState { + return dealStateV6{v6} } type dealProposals6 struct { diff --git a/chain/actors/builtin/market/v7.go b/chain/actors/builtin/market/v7.go index 56a1db328..f51f070c7 100644 --- a/chain/actors/builtin/market/v7.go +++ b/chain/actors/builtin/market/v7.go @@ -151,7 +151,7 @@ type dealStates7 struct { adt.Array } -func (s *dealStates7) Get(dealID abi.DealID) (*DealState, bool, error) { +func (s *dealStates7) Get(dealID abi.DealID) (DealState, bool, error) { var deal7 market7.DealState found, err := s.Array.Get(uint64(dealID), &deal7) if err != nil { @@ -161,7 +161,7 @@ func (s *dealStates7) Get(dealID abi.DealID) (*DealState, bool, error) { return nil, false, nil } deal := fromV7DealState(deal7) - return &deal, true, nil + return deal, true, nil } func (s *dealStates7) ForEach(cb func(dealID abi.DealID, ds DealState) error) error { @@ -171,28 +171,57 @@ func (s *dealStates7) ForEach(cb func(dealID abi.DealID, ds DealState) error) er }) } -func (s *dealStates7) decode(val *cbg.Deferred) (*DealState, error) { +func (s *dealStates7) decode(val *cbg.Deferred) (DealState, error) { var ds7 market7.DealState if err := ds7.UnmarshalCBOR(bytes.NewReader(val.Raw)); err != nil { return nil, err } ds := fromV7DealState(ds7) - return &ds, nil + return ds, nil } func (s *dealStates7) array() adt.Array { return s.Array } -func fromV7DealState(v7 market7.DealState) DealState { - ret := DealState{ - SectorStartEpoch: v7.SectorStartEpoch, - LastUpdatedEpoch: v7.LastUpdatedEpoch, - SlashEpoch: v7.SlashEpoch, - VerifiedClaim: 0, +type dealStateV7 struct { + ds7 market7.DealState +} + +func (d dealStateV7) SectorStartEpoch() abi.ChainEpoch { + return d.ds7.SectorStartEpoch +} + +func (d dealStateV7) LastUpdatedEpoch() abi.ChainEpoch { + return d.ds7.LastUpdatedEpoch +} + +func (d dealStateV7) SlashEpoch() abi.ChainEpoch { + return d.ds7.SlashEpoch +} + +func (d dealStateV7) Equals(other DealState) bool { + if ov7, ok := other.(dealStateV7); ok { + return d.ds7 == ov7.ds7 } - return ret + if d.SectorStartEpoch() != other.SectorStartEpoch() { + return false + } + if d.LastUpdatedEpoch() != other.LastUpdatedEpoch() { + return false + } + if d.SlashEpoch() != other.SlashEpoch() { + return false + } + + return true +} + +var _ DealState = (*dealStateV7)(nil) + +func fromV7DealState(v7 market7.DealState) DealState { + return dealStateV7{v7} } type dealProposals7 struct { diff --git a/chain/actors/builtin/market/v8.go b/chain/actors/builtin/market/v8.go index 9c68ee1fd..f9bf25f9c 100644 --- a/chain/actors/builtin/market/v8.go +++ b/chain/actors/builtin/market/v8.go @@ -152,7 +152,7 @@ type dealStates8 struct { adt.Array } -func (s *dealStates8) Get(dealID abi.DealID) (*DealState, bool, error) { +func (s *dealStates8) Get(dealID abi.DealID) (DealState, bool, error) { var deal8 market8.DealState found, err := s.Array.Get(uint64(dealID), &deal8) if err != nil { @@ -162,7 +162,7 @@ func (s *dealStates8) Get(dealID abi.DealID) (*DealState, bool, error) { return nil, false, nil } deal := fromV8DealState(deal8) - return &deal, true, nil + return deal, true, nil } func (s *dealStates8) ForEach(cb func(dealID abi.DealID, ds DealState) error) error { @@ -172,28 +172,57 @@ func (s *dealStates8) ForEach(cb func(dealID abi.DealID, ds DealState) error) er }) } -func (s *dealStates8) decode(val *cbg.Deferred) (*DealState, error) { +func (s *dealStates8) decode(val *cbg.Deferred) (DealState, error) { var ds8 market8.DealState if err := ds8.UnmarshalCBOR(bytes.NewReader(val.Raw)); err != nil { return nil, err } ds := fromV8DealState(ds8) - return &ds, nil + return ds, nil } func (s *dealStates8) array() adt.Array { return s.Array } -func fromV8DealState(v8 market8.DealState) DealState { - ret := DealState{ - SectorStartEpoch: v8.SectorStartEpoch, - LastUpdatedEpoch: v8.LastUpdatedEpoch, - SlashEpoch: v8.SlashEpoch, - VerifiedClaim: 0, +type dealStateV8 struct { + ds8 market8.DealState +} + +func (d dealStateV8) SectorStartEpoch() abi.ChainEpoch { + return d.ds8.SectorStartEpoch +} + +func (d dealStateV8) LastUpdatedEpoch() abi.ChainEpoch { + return d.ds8.LastUpdatedEpoch +} + +func (d dealStateV8) SlashEpoch() abi.ChainEpoch { + return d.ds8.SlashEpoch +} + +func (d dealStateV8) Equals(other DealState) bool { + if ov8, ok := other.(dealStateV8); ok { + return d.ds8 == ov8.ds8 } - return ret + if d.SectorStartEpoch() != other.SectorStartEpoch() { + return false + } + if d.LastUpdatedEpoch() != other.LastUpdatedEpoch() { + return false + } + if d.SlashEpoch() != other.SlashEpoch() { + return false + } + + return true +} + +var _ DealState = (*dealStateV8)(nil) + +func fromV8DealState(v8 market8.DealState) DealState { + return dealStateV8{v8} } type dealProposals8 struct { diff --git a/chain/actors/builtin/market/v9.go b/chain/actors/builtin/market/v9.go index d692c15cc..3b5be4dfa 100644 --- a/chain/actors/builtin/market/v9.go +++ b/chain/actors/builtin/market/v9.go @@ -153,7 +153,7 @@ type dealStates9 struct { adt.Array } -func (s *dealStates9) Get(dealID abi.DealID) (*DealState, bool, error) { +func (s *dealStates9) Get(dealID abi.DealID) (DealState, bool, error) { var deal9 market9.DealState found, err := s.Array.Get(uint64(dealID), &deal9) if err != nil { @@ -163,7 +163,7 @@ func (s *dealStates9) Get(dealID abi.DealID) (*DealState, bool, error) { return nil, false, nil } deal := fromV9DealState(deal9) - return &deal, true, nil + return deal, true, nil } func (s *dealStates9) ForEach(cb func(dealID abi.DealID, ds DealState) error) error { @@ -173,30 +173,57 @@ func (s *dealStates9) ForEach(cb func(dealID abi.DealID, ds DealState) error) er }) } -func (s *dealStates9) decode(val *cbg.Deferred) (*DealState, error) { +func (s *dealStates9) decode(val *cbg.Deferred) (DealState, error) { var ds9 market9.DealState if err := ds9.UnmarshalCBOR(bytes.NewReader(val.Raw)); err != nil { return nil, err } ds := fromV9DealState(ds9) - return &ds, nil + return ds, nil } func (s *dealStates9) array() adt.Array { return s.Array } -func fromV9DealState(v9 market9.DealState) DealState { - ret := DealState{ - SectorStartEpoch: v9.SectorStartEpoch, - LastUpdatedEpoch: v9.LastUpdatedEpoch, - SlashEpoch: v9.SlashEpoch, - VerifiedClaim: 0, +type dealStateV9 struct { + ds9 market9.DealState +} + +func (d dealStateV9) SectorStartEpoch() abi.ChainEpoch { + return d.ds9.SectorStartEpoch +} + +func (d dealStateV9) LastUpdatedEpoch() abi.ChainEpoch { + return d.ds9.LastUpdatedEpoch +} + +func (d dealStateV9) SlashEpoch() abi.ChainEpoch { + return d.ds9.SlashEpoch +} + +func (d dealStateV9) Equals(other DealState) bool { + if ov9, ok := other.(dealStateV9); ok { + return d.ds9 == ov9.ds9 } - ret.VerifiedClaim = verifregtypes.AllocationId(v9.VerifiedClaim) + if d.SectorStartEpoch() != other.SectorStartEpoch() { + return false + } + if d.LastUpdatedEpoch() != other.LastUpdatedEpoch() { + return false + } + if d.SlashEpoch() != other.SlashEpoch() { + return false + } - return ret + return true +} + +var _ DealState = (*dealStateV9)(nil) + +func fromV9DealState(v9 market9.DealState) DealState { + return dealStateV9{v9} } type dealProposals9 struct { diff --git a/chain/actors/builtin/miner/actor.go.template b/chain/actors/builtin/miner/actor.go.template index ead254847..0f7204ec7 100644 --- a/chain/actors/builtin/miner/actor.go.template +++ b/chain/actors/builtin/miner/actor.go.template @@ -17,7 +17,8 @@ import ( "github.com/filecoin-project/lotus/chain/actors/adt" "github.com/filecoin-project/lotus/chain/types" - miner12 "github.com/filecoin-project/go-state-types/builtin/v12/miner" + + minertypes13 "github.com/filecoin-project/go-state-types/builtin/v13/miner" minertypes "github.com/filecoin-project/go-state-types/builtin/v9/miner" "github.com/filecoin-project/go-state-types/manifest" @@ -153,7 +154,7 @@ type Partition interface { UnprovenSectors() (bitfield.BitField, error) } -type SectorOnChainInfo = miner12.SectorOnChainInfo +type SectorOnChainInfo = minertypes13.SectorOnChainInfo func PreferredSealProofTypeFromWindowPoStType(nver network.Version, proof abi.RegisteredPoStProof, configWantSynthetic bool) (abi.RegisteredSealProof, error) { // We added support for the new proofs in network version 7, and removed support for the old @@ -240,7 +241,9 @@ type DeclareFaultsParams = minertypes.DeclareFaultsParams type ProveCommitAggregateParams = minertypes.ProveCommitAggregateParams type ProveCommitSectorParams = minertypes.ProveCommitSectorParams type ProveReplicaUpdatesParams = minertypes.ProveReplicaUpdatesParams +type ProveReplicaUpdatesParams2 = minertypes.ProveReplicaUpdatesParams2 type ReplicaUpdate = minertypes.ReplicaUpdate +type ReplicaUpdate2 = minertypes.ReplicaUpdate2 type PreCommitSectorBatchParams = minertypes.PreCommitSectorBatchParams type PreCommitSectorBatchParams2 = minertypes.PreCommitSectorBatchParams2 type ExtendSectorExpiration2Params = minertypes.ExtendSectorExpiration2Params @@ -249,6 +252,13 @@ type ExpirationExtension2 = minertypes.ExpirationExtension2 type CompactPartitionsParams = minertypes.CompactPartitionsParams type WithdrawBalanceParams = minertypes.WithdrawBalanceParams +type PieceActivationManifest = minertypes13.PieceActivationManifest +type ProveCommitSectors3Params = minertypes13.ProveCommitSectors3Params +type SectorActivationManifest = minertypes13.SectorActivationManifest +type ProveReplicaUpdates3Params = minertypes13.ProveReplicaUpdates3Params +type SectorUpdateManifest = minertypes13.SectorUpdateManifest +type SectorOnChainInfoFlags = minertypes13.SectorOnChainInfoFlags + var QAPowerMax = minertypes.QAPowerMax type WindowPostVerifyInfo = proof.WindowPoStVerifyInfo diff --git a/chain/actors/builtin/miner/miner.go b/chain/actors/builtin/miner/miner.go index 151688d04..cdf0046f5 100644 --- a/chain/actors/builtin/miner/miner.go +++ b/chain/actors/builtin/miner/miner.go @@ -9,7 +9,7 @@ import ( "github.com/filecoin-project/go-state-types/abi" actorstypes "github.com/filecoin-project/go-state-types/actors" "github.com/filecoin-project/go-state-types/big" - miner12 "github.com/filecoin-project/go-state-types/builtin/v12/miner" + minertypes13 "github.com/filecoin-project/go-state-types/builtin/v13/miner" minertypes "github.com/filecoin-project/go-state-types/builtin/v9/miner" "github.com/filecoin-project/go-state-types/cbor" "github.com/filecoin-project/go-state-types/dline" @@ -52,6 +52,9 @@ func Load(store adt.Store, act *types.Actor) (State, error) { case actorstypes.Version12: return load12(store, act.Head) + case actorstypes.Version13: + return load13(store, act.Head) + } } @@ -122,6 +125,9 @@ func MakeState(store adt.Store, av actors.Version) (State, error) { case actors.Version12: return make12(store) + case actors.Version13: + return make13(store) + } return nil, xerrors.Errorf("unknown actor version %d", av) } @@ -213,7 +219,7 @@ type Partition interface { UnprovenSectors() (bitfield.BitField, error) } -type SectorOnChainInfo = miner12.SectorOnChainInfo +type SectorOnChainInfo = minertypes13.SectorOnChainInfo func PreferredSealProofTypeFromWindowPoStType(nver network.Version, proof abi.RegisteredPoStProof, configWantSynthetic bool) (abi.RegisteredSealProof, error) { // We added support for the new proofs in network version 7, and removed support for the old @@ -300,7 +306,9 @@ type DeclareFaultsParams = minertypes.DeclareFaultsParams type ProveCommitAggregateParams = minertypes.ProveCommitAggregateParams type ProveCommitSectorParams = minertypes.ProveCommitSectorParams type ProveReplicaUpdatesParams = minertypes.ProveReplicaUpdatesParams +type ProveReplicaUpdatesParams2 = minertypes.ProveReplicaUpdatesParams2 type ReplicaUpdate = minertypes.ReplicaUpdate +type ReplicaUpdate2 = minertypes.ReplicaUpdate2 type PreCommitSectorBatchParams = minertypes.PreCommitSectorBatchParams type PreCommitSectorBatchParams2 = minertypes.PreCommitSectorBatchParams2 type ExtendSectorExpiration2Params = minertypes.ExtendSectorExpiration2Params @@ -309,6 +317,13 @@ type ExpirationExtension2 = minertypes.ExpirationExtension2 type CompactPartitionsParams = minertypes.CompactPartitionsParams type WithdrawBalanceParams = minertypes.WithdrawBalanceParams +type PieceActivationManifest = minertypes13.PieceActivationManifest +type ProveCommitSectors3Params = minertypes13.ProveCommitSectors3Params +type SectorActivationManifest = minertypes13.SectorActivationManifest +type ProveReplicaUpdates3Params = minertypes13.ProveReplicaUpdates3Params +type SectorUpdateManifest = minertypes13.SectorUpdateManifest +type SectorOnChainInfoFlags = minertypes13.SectorOnChainInfoFlags + var QAPowerMax = minertypes.QAPowerMax type WindowPostVerifyInfo = proof.WindowPoStVerifyInfo @@ -375,5 +390,6 @@ func AllCodes() []cid.Cid { (&state10{}).Code(), (&state11{}).Code(), (&state12{}).Code(), + (&state13{}).Code(), } } diff --git a/chain/actors/builtin/miner/v13.go b/chain/actors/builtin/miner/v13.go new file mode 100644 index 000000000..d61c2a9a2 --- /dev/null +++ b/chain/actors/builtin/miner/v13.go @@ -0,0 +1,594 @@ +package miner + +import ( + "bytes" + "errors" + "fmt" + + "github.com/ipfs/go-cid" + cbg "github.com/whyrusleeping/cbor-gen" + "golang.org/x/xerrors" + + "github.com/filecoin-project/go-bitfield" + rle "github.com/filecoin-project/go-bitfield/rle" + "github.com/filecoin-project/go-state-types/abi" + actorstypes "github.com/filecoin-project/go-state-types/actors" + builtin13 "github.com/filecoin-project/go-state-types/builtin" + miner13 "github.com/filecoin-project/go-state-types/builtin/v13/miner" + adt13 "github.com/filecoin-project/go-state-types/builtin/v13/util/adt" + "github.com/filecoin-project/go-state-types/dline" + "github.com/filecoin-project/go-state-types/manifest" + + "github.com/filecoin-project/lotus/chain/actors" + "github.com/filecoin-project/lotus/chain/actors/adt" +) + +var _ State = (*state13)(nil) + +func load13(store adt.Store, root cid.Cid) (State, error) { + out := state13{store: store} + err := store.Get(store.Context(), root, &out) + if err != nil { + return nil, err + } + return &out, nil +} + +func make13(store adt.Store) (State, error) { + out := state13{store: store} + out.State = miner13.State{} + return &out, nil +} + +type state13 struct { + miner13.State + store adt.Store +} + +type deadline13 struct { + miner13.Deadline + store adt.Store +} + +type partition13 struct { + miner13.Partition + store adt.Store +} + +func (s *state13) AvailableBalance(bal abi.TokenAmount) (available abi.TokenAmount, err error) { + defer func() { + if r := recover(); r != nil { + err = xerrors.Errorf("failed to get available balance: %w", r) + available = abi.NewTokenAmount(0) + } + }() + // this panics if the miner doesn't have enough funds to cover their locked pledge + available, err = s.GetAvailableBalance(bal) + return available, err +} + +func (s *state13) VestedFunds(epoch abi.ChainEpoch) (abi.TokenAmount, error) { + return s.CheckVestedFunds(s.store, epoch) +} + +func (s *state13) LockedFunds() (LockedFunds, error) { + return LockedFunds{ + VestingFunds: s.State.LockedFunds, + InitialPledgeRequirement: s.State.InitialPledge, + PreCommitDeposits: s.State.PreCommitDeposits, + }, nil +} + +func (s *state13) FeeDebt() (abi.TokenAmount, error) { + return s.State.FeeDebt, nil +} + +func (s *state13) InitialPledge() (abi.TokenAmount, error) { + return s.State.InitialPledge, nil +} + +func (s *state13) PreCommitDeposits() (abi.TokenAmount, error) { + return s.State.PreCommitDeposits, nil +} + +// Returns nil, nil if sector is not found +func (s *state13) GetSector(num abi.SectorNumber) (*SectorOnChainInfo, error) { + info, ok, err := s.State.GetSector(s.store, num) + if !ok || err != nil { + return nil, err + } + + ret := fromV13SectorOnChainInfo(*info) + return &ret, nil +} + +func (s *state13) FindSector(num abi.SectorNumber) (*SectorLocation, error) { + dlIdx, partIdx, err := s.State.FindSector(s.store, num) + if err != nil { + return nil, err + } + return &SectorLocation{ + Deadline: dlIdx, + Partition: partIdx, + }, nil +} + +func (s *state13) NumLiveSectors() (uint64, error) { + dls, err := s.State.LoadDeadlines(s.store) + if err != nil { + return 0, err + } + var total uint64 + if err := dls.ForEach(s.store, func(dlIdx uint64, dl *miner13.Deadline) error { + total += dl.LiveSectors + return nil + }); err != nil { + return 0, err + } + return total, nil +} + +// GetSectorExpiration returns the effective expiration of the given sector. +// +// If the sector does not expire early, the Early expiration field is 0. +func (s *state13) GetSectorExpiration(num abi.SectorNumber) (*SectorExpiration, error) { + dls, err := s.State.LoadDeadlines(s.store) + if err != nil { + return nil, err + } + // NOTE: this can be optimized significantly. + // 1. If the sector is non-faulty, it will expire on-time (can be + // learned from the sector info). + // 2. If it's faulty, it will expire early within the first 42 entries + // of the expiration queue. + + stopErr := errors.New("stop") + out := SectorExpiration{} + err = dls.ForEach(s.store, func(dlIdx uint64, dl *miner13.Deadline) error { + partitions, err := dl.PartitionsArray(s.store) + if err != nil { + return err + } + quant := s.State.QuantSpecForDeadline(dlIdx) + var part miner13.Partition + return partitions.ForEach(&part, func(partIdx int64) error { + if found, err := part.Sectors.IsSet(uint64(num)); err != nil { + return err + } else if !found { + return nil + } + if found, err := part.Terminated.IsSet(uint64(num)); err != nil { + return err + } else if found { + // already terminated + return stopErr + } + + q, err := miner13.LoadExpirationQueue(s.store, part.ExpirationsEpochs, quant, miner13.PartitionExpirationAmtBitwidth) + if err != nil { + return err + } + var exp miner13.ExpirationSet + return q.ForEach(&exp, func(epoch int64) error { + if early, err := exp.EarlySectors.IsSet(uint64(num)); err != nil { + return err + } else if early { + out.Early = abi.ChainEpoch(epoch) + return nil + } + if onTime, err := exp.OnTimeSectors.IsSet(uint64(num)); err != nil { + return err + } else if onTime { + out.OnTime = abi.ChainEpoch(epoch) + return stopErr + } + return nil + }) + }) + }) + if err == stopErr { + err = nil + } + if err != nil { + return nil, err + } + if out.Early == 0 && out.OnTime == 0 { + return nil, xerrors.Errorf("failed to find sector %d", num) + } + return &out, nil +} + +func (s *state13) GetPrecommittedSector(num abi.SectorNumber) (*SectorPreCommitOnChainInfo, error) { + info, ok, err := s.State.GetPrecommittedSector(s.store, num) + if !ok || err != nil { + return nil, err + } + + ret := fromV13SectorPreCommitOnChainInfo(*info) + + return &ret, nil +} + +func (s *state13) ForEachPrecommittedSector(cb func(SectorPreCommitOnChainInfo) error) error { + precommitted, err := adt13.AsMap(s.store, s.State.PreCommittedSectors, builtin13.DefaultHamtBitwidth) + if err != nil { + return err + } + + var info miner13.SectorPreCommitOnChainInfo + if err := precommitted.ForEach(&info, func(_ string) error { + return cb(fromV13SectorPreCommitOnChainInfo(info)) + }); err != nil { + return err + } + + return nil +} + +func (s *state13) LoadSectors(snos *bitfield.BitField) ([]*SectorOnChainInfo, error) { + sectors, err := miner13.LoadSectors(s.store, s.State.Sectors) + if err != nil { + return nil, err + } + + // If no sector numbers are specified, load all. + if snos == nil { + infos := make([]*SectorOnChainInfo, 0, sectors.Length()) + var info13 miner13.SectorOnChainInfo + if err := sectors.ForEach(&info13, func(_ int64) error { + info := fromV13SectorOnChainInfo(info13) + infos = append(infos, &info) + return nil + }); err != nil { + return nil, err + } + return infos, nil + } + + // Otherwise, load selected. + infos13, err := sectors.Load(*snos) + if err != nil { + return nil, err + } + infos := make([]*SectorOnChainInfo, len(infos13)) + for i, info13 := range infos13 { + info := fromV13SectorOnChainInfo(*info13) + infos[i] = &info + } + return infos, nil +} + +func (s *state13) loadAllocatedSectorNumbers() (bitfield.BitField, error) { + var allocatedSectors bitfield.BitField + err := s.store.Get(s.store.Context(), s.State.AllocatedSectors, &allocatedSectors) + return allocatedSectors, err +} + +func (s *state13) IsAllocated(num abi.SectorNumber) (bool, error) { + allocatedSectors, err := s.loadAllocatedSectorNumbers() + if err != nil { + return false, err + } + + return allocatedSectors.IsSet(uint64(num)) +} + +func (s *state13) GetProvingPeriodStart() (abi.ChainEpoch, error) { + return s.State.ProvingPeriodStart, nil +} + +func (s *state13) UnallocatedSectorNumbers(count int) ([]abi.SectorNumber, error) { + allocatedSectors, err := s.loadAllocatedSectorNumbers() + if err != nil { + return nil, err + } + + allocatedRuns, err := allocatedSectors.RunIterator() + if err != nil { + return nil, err + } + + unallocatedRuns, err := rle.Subtract( + &rle.RunSliceIterator{Runs: []rle.Run{{Val: true, Len: abi.MaxSectorNumber}}}, + allocatedRuns, + ) + if err != nil { + return nil, err + } + + iter, err := rle.BitsFromRuns(unallocatedRuns) + if err != nil { + return nil, err + } + + sectors := make([]abi.SectorNumber, 0, count) + for iter.HasNext() && len(sectors) < count { + nextNo, err := iter.Next() + if err != nil { + return nil, err + } + sectors = append(sectors, abi.SectorNumber(nextNo)) + } + + return sectors, nil +} + +func (s *state13) GetAllocatedSectors() (*bitfield.BitField, error) { + var allocatedSectors bitfield.BitField + if err := s.store.Get(s.store.Context(), s.State.AllocatedSectors, &allocatedSectors); err != nil { + return nil, err + } + + return &allocatedSectors, nil +} + +func (s *state13) LoadDeadline(idx uint64) (Deadline, error) { + dls, err := s.State.LoadDeadlines(s.store) + if err != nil { + return nil, err + } + dl, err := dls.LoadDeadline(s.store, idx) + if err != nil { + return nil, err + } + return &deadline13{*dl, s.store}, nil +} + +func (s *state13) ForEachDeadline(cb func(uint64, Deadline) error) error { + dls, err := s.State.LoadDeadlines(s.store) + if err != nil { + return err + } + return dls.ForEach(s.store, func(i uint64, dl *miner13.Deadline) error { + return cb(i, &deadline13{*dl, s.store}) + }) +} + +func (s *state13) NumDeadlines() (uint64, error) { + return miner13.WPoStPeriodDeadlines, nil +} + +func (s *state13) DeadlinesChanged(other State) (bool, error) { + other13, ok := other.(*state13) + if !ok { + // treat an upgrade as a change, always + return true, nil + } + + return !s.State.Deadlines.Equals(other13.Deadlines), nil +} + +func (s *state13) MinerInfoChanged(other State) (bool, error) { + other0, ok := other.(*state13) + if !ok { + // treat an upgrade as a change, always + return true, nil + } + return !s.State.Info.Equals(other0.State.Info), nil +} + +func (s *state13) Info() (MinerInfo, error) { + info, err := s.State.GetInfo(s.store) + if err != nil { + return MinerInfo{}, err + } + + mi := MinerInfo{ + Owner: info.Owner, + Worker: info.Worker, + ControlAddresses: info.ControlAddresses, + + PendingWorkerKey: (*WorkerKeyChange)(info.PendingWorkerKey), + + PeerId: info.PeerId, + Multiaddrs: info.Multiaddrs, + WindowPoStProofType: info.WindowPoStProofType, + SectorSize: info.SectorSize, + WindowPoStPartitionSectors: info.WindowPoStPartitionSectors, + ConsensusFaultElapsed: info.ConsensusFaultElapsed, + + Beneficiary: info.Beneficiary, + BeneficiaryTerm: BeneficiaryTerm(info.BeneficiaryTerm), + PendingBeneficiaryTerm: (*PendingBeneficiaryChange)(info.PendingBeneficiaryTerm), + } + + return mi, nil +} + +func (s *state13) DeadlineInfo(epoch abi.ChainEpoch) (*dline.Info, error) { + return s.State.RecordedDeadlineInfo(epoch), nil +} + +func (s *state13) DeadlineCronActive() (bool, error) { + return s.State.DeadlineCronActive, nil +} + +func (s *state13) sectors() (adt.Array, error) { + return adt13.AsArray(s.store, s.Sectors, miner13.SectorsAmtBitwidth) +} + +func (s *state13) decodeSectorOnChainInfo(val *cbg.Deferred) (SectorOnChainInfo, error) { + var si miner13.SectorOnChainInfo + err := si.UnmarshalCBOR(bytes.NewReader(val.Raw)) + if err != nil { + return SectorOnChainInfo{}, err + } + + return fromV13SectorOnChainInfo(si), nil +} + +func (s *state13) precommits() (adt.Map, error) { + return adt13.AsMap(s.store, s.PreCommittedSectors, builtin13.DefaultHamtBitwidth) +} + +func (s *state13) decodeSectorPreCommitOnChainInfo(val *cbg.Deferred) (SectorPreCommitOnChainInfo, error) { + var sp miner13.SectorPreCommitOnChainInfo + err := sp.UnmarshalCBOR(bytes.NewReader(val.Raw)) + if err != nil { + return SectorPreCommitOnChainInfo{}, err + } + + return fromV13SectorPreCommitOnChainInfo(sp), nil +} + +func (s *state13) EraseAllUnproven() error { + + dls, err := s.State.LoadDeadlines(s.store) + if err != nil { + return err + } + + err = dls.ForEach(s.store, func(dindx uint64, dl *miner13.Deadline) error { + ps, err := dl.PartitionsArray(s.store) + if err != nil { + return err + } + + var part miner13.Partition + err = ps.ForEach(&part, func(pindx int64) error { + _ = part.ActivateUnproven() + err = ps.Set(uint64(pindx), &part) + return nil + }) + + if err != nil { + return err + } + + dl.Partitions, err = ps.Root() + if err != nil { + return err + } + + return dls.UpdateDeadline(s.store, dindx, dl) + }) + if err != nil { + return err + } + + return s.State.SaveDeadlines(s.store, dls) + +} + +func (d *deadline13) LoadPartition(idx uint64) (Partition, error) { + p, err := d.Deadline.LoadPartition(d.store, idx) + if err != nil { + return nil, err + } + return &partition13{*p, d.store}, nil +} + +func (d *deadline13) ForEachPartition(cb func(uint64, Partition) error) error { + ps, err := d.Deadline.PartitionsArray(d.store) + if err != nil { + return err + } + var part miner13.Partition + return ps.ForEach(&part, func(i int64) error { + return cb(uint64(i), &partition13{part, d.store}) + }) +} + +func (d *deadline13) PartitionsChanged(other Deadline) (bool, error) { + other13, ok := other.(*deadline13) + if !ok { + // treat an upgrade as a change, always + return true, nil + } + + return !d.Deadline.Partitions.Equals(other13.Deadline.Partitions), nil +} + +func (d *deadline13) PartitionsPoSted() (bitfield.BitField, error) { + return d.Deadline.PartitionsPoSted, nil +} + +func (d *deadline13) DisputableProofCount() (uint64, error) { + + ops, err := d.OptimisticProofsSnapshotArray(d.store) + if err != nil { + return 0, err + } + + return ops.Length(), nil + +} + +func (p *partition13) AllSectors() (bitfield.BitField, error) { + return p.Partition.Sectors, nil +} + +func (p *partition13) FaultySectors() (bitfield.BitField, error) { + return p.Partition.Faults, nil +} + +func (p *partition13) RecoveringSectors() (bitfield.BitField, error) { + return p.Partition.Recoveries, nil +} + +func (p *partition13) UnprovenSectors() (bitfield.BitField, error) { + return p.Partition.Unproven, nil +} + +func fromV13SectorOnChainInfo(v13 miner13.SectorOnChainInfo) SectorOnChainInfo { + info := SectorOnChainInfo{ + SectorNumber: v13.SectorNumber, + SealProof: v13.SealProof, + SealedCID: v13.SealedCID, + DealIDs: v13.DealIDs, + Activation: v13.Activation, + Expiration: v13.Expiration, + DealWeight: v13.DealWeight, + VerifiedDealWeight: v13.VerifiedDealWeight, + InitialPledge: v13.InitialPledge, + ExpectedDayReward: v13.ExpectedDayReward, + ExpectedStoragePledge: v13.ExpectedStoragePledge, + + SectorKeyCID: v13.SectorKeyCID, + + PowerBaseEpoch: v13.PowerBaseEpoch, + ReplacedDayReward: v13.ReplacedDayReward, + } + return info +} + +func fromV13SectorPreCommitOnChainInfo(v13 miner13.SectorPreCommitOnChainInfo) SectorPreCommitOnChainInfo { + ret := SectorPreCommitOnChainInfo{ + Info: SectorPreCommitInfo{ + SealProof: v13.Info.SealProof, + SectorNumber: v13.Info.SectorNumber, + SealedCID: v13.Info.SealedCID, + SealRandEpoch: v13.Info.SealRandEpoch, + DealIDs: v13.Info.DealIDs, + Expiration: v13.Info.Expiration, + UnsealedCid: nil, + }, + PreCommitDeposit: v13.PreCommitDeposit, + PreCommitEpoch: v13.PreCommitEpoch, + } + + ret.Info.UnsealedCid = v13.Info.UnsealedCid + + return ret +} + +func (s *state13) GetState() interface{} { + return &s.State +} + +func (s *state13) ActorKey() string { + return manifest.MinerKey +} + +func (s *state13) ActorVersion() actorstypes.Version { + return actorstypes.Version13 +} + +func (s *state13) Code() cid.Cid { + code, ok := actors.GetActorCodeID(s.ActorVersion(), s.ActorKey()) + if !ok { + panic(fmt.Errorf("didn't find actor %v code id for actor version %d", s.ActorKey(), s.ActorVersion())) + } + + return code +} diff --git a/chain/actors/builtin/multisig/message10.go b/chain/actors/builtin/multisig/message10.go index 8f7bb5a6f..59dd4dde0 100644 --- a/chain/actors/builtin/multisig/message10.go +++ b/chain/actors/builtin/multisig/message10.go @@ -8,7 +8,7 @@ import ( actorstypes "github.com/filecoin-project/go-state-types/actors" builtintypes "github.com/filecoin-project/go-state-types/builtin" multisig10 "github.com/filecoin-project/go-state-types/builtin/v10/multisig" - init12 "github.com/filecoin-project/go-state-types/builtin/v12/init" + init13 "github.com/filecoin-project/go-state-types/builtin/v13/init" "github.com/filecoin-project/go-state-types/manifest" "github.com/filecoin-project/lotus/chain/actors" @@ -57,7 +57,7 @@ func (m message10) Create( } // new actors are created by invoking 'exec' on the init actor with the constructor params - execParams := &init12.ExecParams{ + execParams := &init13.ExecParams{ CodeCID: code, ConstructorParams: enc, } diff --git a/chain/actors/builtin/multisig/message11.go b/chain/actors/builtin/multisig/message11.go index 4c7520d5d..89bee0255 100644 --- a/chain/actors/builtin/multisig/message11.go +++ b/chain/actors/builtin/multisig/message11.go @@ -8,7 +8,7 @@ import ( actorstypes "github.com/filecoin-project/go-state-types/actors" builtintypes "github.com/filecoin-project/go-state-types/builtin" multisig11 "github.com/filecoin-project/go-state-types/builtin/v11/multisig" - init12 "github.com/filecoin-project/go-state-types/builtin/v12/init" + init13 "github.com/filecoin-project/go-state-types/builtin/v13/init" "github.com/filecoin-project/go-state-types/manifest" "github.com/filecoin-project/lotus/chain/actors" @@ -57,7 +57,7 @@ func (m message11) Create( } // new actors are created by invoking 'exec' on the init actor with the constructor params - execParams := &init12.ExecParams{ + execParams := &init13.ExecParams{ CodeCID: code, ConstructorParams: enc, } diff --git a/chain/actors/builtin/multisig/message12.go b/chain/actors/builtin/multisig/message12.go index 43658c04b..326026c93 100644 --- a/chain/actors/builtin/multisig/message12.go +++ b/chain/actors/builtin/multisig/message12.go @@ -7,8 +7,8 @@ import ( "github.com/filecoin-project/go-state-types/abi" actorstypes "github.com/filecoin-project/go-state-types/actors" builtintypes "github.com/filecoin-project/go-state-types/builtin" - init12 "github.com/filecoin-project/go-state-types/builtin/v12/init" multisig12 "github.com/filecoin-project/go-state-types/builtin/v12/multisig" + init13 "github.com/filecoin-project/go-state-types/builtin/v13/init" "github.com/filecoin-project/go-state-types/manifest" "github.com/filecoin-project/lotus/chain/actors" @@ -57,7 +57,7 @@ func (m message12) Create( } // new actors are created by invoking 'exec' on the init actor with the constructor params - execParams := &init12.ExecParams{ + execParams := &init13.ExecParams{ CodeCID: code, ConstructorParams: enc, } diff --git a/chain/actors/builtin/multisig/message13.go b/chain/actors/builtin/multisig/message13.go new file mode 100644 index 000000000..94a9cbfbf --- /dev/null +++ b/chain/actors/builtin/multisig/message13.go @@ -0,0 +1,77 @@ +package multisig + +import ( + "golang.org/x/xerrors" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" + actorstypes "github.com/filecoin-project/go-state-types/actors" + builtintypes "github.com/filecoin-project/go-state-types/builtin" + init13 "github.com/filecoin-project/go-state-types/builtin/v13/init" + multisig13 "github.com/filecoin-project/go-state-types/builtin/v13/multisig" + "github.com/filecoin-project/go-state-types/manifest" + + "github.com/filecoin-project/lotus/chain/actors" + init_ "github.com/filecoin-project/lotus/chain/actors/builtin/init" + "github.com/filecoin-project/lotus/chain/types" +) + +type message13 struct{ message0 } + +func (m message13) Create( + signers []address.Address, threshold uint64, + unlockStart, unlockDuration abi.ChainEpoch, + initialAmount abi.TokenAmount, +) (*types.Message, error) { + + lenAddrs := uint64(len(signers)) + + if lenAddrs < threshold { + return nil, xerrors.Errorf("cannot require signing of more addresses than provided for multisig") + } + + if threshold == 0 { + threshold = lenAddrs + } + + if m.from == address.Undef { + return nil, xerrors.Errorf("must provide source address") + } + + // Set up constructor parameters for multisig + msigParams := &multisig13.ConstructorParams{ + Signers: signers, + NumApprovalsThreshold: threshold, + UnlockDuration: unlockDuration, + StartEpoch: unlockStart, + } + + enc, actErr := actors.SerializeParams(msigParams) + if actErr != nil { + return nil, actErr + } + + code, ok := actors.GetActorCodeID(actorstypes.Version13, manifest.MultisigKey) + if !ok { + return nil, xerrors.Errorf("failed to get multisig code ID") + } + + // new actors are created by invoking 'exec' on the init actor with the constructor params + execParams := &init13.ExecParams{ + CodeCID: code, + ConstructorParams: enc, + } + + enc, actErr = actors.SerializeParams(execParams) + if actErr != nil { + return nil, actErr + } + + return &types.Message{ + To: init_.Address, + From: m.from, + Method: builtintypes.MethodsInit.Exec, + Params: enc, + Value: initialAmount, + }, nil +} diff --git a/chain/actors/builtin/multisig/message8.go b/chain/actors/builtin/multisig/message8.go index 390c94691..5d79fe6c5 100644 --- a/chain/actors/builtin/multisig/message8.go +++ b/chain/actors/builtin/multisig/message8.go @@ -7,7 +7,7 @@ import ( "github.com/filecoin-project/go-state-types/abi" actorstypes "github.com/filecoin-project/go-state-types/actors" builtintypes "github.com/filecoin-project/go-state-types/builtin" - init12 "github.com/filecoin-project/go-state-types/builtin/v12/init" + init13 "github.com/filecoin-project/go-state-types/builtin/v13/init" multisig8 "github.com/filecoin-project/go-state-types/builtin/v8/multisig" "github.com/filecoin-project/go-state-types/manifest" @@ -57,7 +57,7 @@ func (m message8) Create( } // new actors are created by invoking 'exec' on the init actor with the constructor params - execParams := &init12.ExecParams{ + execParams := &init13.ExecParams{ CodeCID: code, ConstructorParams: enc, } diff --git a/chain/actors/builtin/multisig/message9.go b/chain/actors/builtin/multisig/message9.go index 907bec7d5..9003b7e38 100644 --- a/chain/actors/builtin/multisig/message9.go +++ b/chain/actors/builtin/multisig/message9.go @@ -7,7 +7,7 @@ import ( "github.com/filecoin-project/go-state-types/abi" actorstypes "github.com/filecoin-project/go-state-types/actors" builtintypes "github.com/filecoin-project/go-state-types/builtin" - init12 "github.com/filecoin-project/go-state-types/builtin/v12/init" + init13 "github.com/filecoin-project/go-state-types/builtin/v13/init" multisig9 "github.com/filecoin-project/go-state-types/builtin/v9/multisig" "github.com/filecoin-project/go-state-types/manifest" @@ -57,7 +57,7 @@ func (m message9) Create( } // new actors are created by invoking 'exec' on the init actor with the constructor params - execParams := &init12.ExecParams{ + execParams := &init13.ExecParams{ CodeCID: code, ConstructorParams: enc, } diff --git a/chain/actors/builtin/multisig/multisig.go b/chain/actors/builtin/multisig/multisig.go index 71a3b7b22..08da9bd2d 100644 --- a/chain/actors/builtin/multisig/multisig.go +++ b/chain/actors/builtin/multisig/multisig.go @@ -12,7 +12,7 @@ import ( "github.com/filecoin-project/go-state-types/abi" actorstypes "github.com/filecoin-project/go-state-types/actors" builtintypes "github.com/filecoin-project/go-state-types/builtin" - msig12 "github.com/filecoin-project/go-state-types/builtin/v12/multisig" + msig13 "github.com/filecoin-project/go-state-types/builtin/v13/multisig" "github.com/filecoin-project/go-state-types/cbor" "github.com/filecoin-project/go-state-types/manifest" builtin0 "github.com/filecoin-project/specs-actors/actors/builtin" @@ -51,6 +51,9 @@ func Load(store adt.Store, act *types.Actor) (State, error) { case actorstypes.Version12: return load12(store, act.Head) + case actorstypes.Version13: + return load13(store, act.Head) + } } @@ -121,6 +124,9 @@ func MakeState(store adt.Store, av actorstypes.Version, signers []address.Addres case actorstypes.Version12: return make12(store, signers, threshold, startEpoch, unlockDuration, initialBalance) + case actorstypes.Version13: + return make13(store, signers, threshold, startEpoch, unlockDuration, initialBalance) + } return nil, xerrors.Errorf("unknown actor version %d", av) } @@ -147,7 +153,7 @@ type State interface { GetState() interface{} } -type Transaction = msig12.Transaction +type Transaction = msig13.Transaction var Methods = builtintypes.MethodsMultisig @@ -189,6 +195,9 @@ func Message(version actorstypes.Version, from address.Address) MessageBuilder { case actorstypes.Version12: return message12{message0{from}} + + case actorstypes.Version13: + return message13{message0{from}} default: panic(fmt.Sprintf("unsupported actors version: %d", version)) } @@ -212,13 +221,13 @@ type MessageBuilder interface { } // this type is the same between v0 and v2 -type ProposalHashData = msig12.ProposalHashData -type ProposeReturn = msig12.ProposeReturn -type ProposeParams = msig12.ProposeParams -type ApproveReturn = msig12.ApproveReturn +type ProposalHashData = msig13.ProposalHashData +type ProposeReturn = msig13.ProposeReturn +type ProposeParams = msig13.ProposeParams +type ApproveReturn = msig13.ApproveReturn func txnParams(id uint64, data *ProposalHashData) ([]byte, error) { - params := msig12.TxnIDParams{ID: msig12.TxnID(id)} + params := msig13.TxnIDParams{ID: msig13.TxnID(id)} if data != nil { if data.Requester.Protocol() != address.ID { return nil, xerrors.Errorf("proposer address must be an ID address, was %s", data.Requester) @@ -254,5 +263,6 @@ func AllCodes() []cid.Cid { (&state10{}).Code(), (&state11{}).Code(), (&state12{}).Code(), + (&state13{}).Code(), } } diff --git a/chain/actors/builtin/multisig/v13.go b/chain/actors/builtin/multisig/v13.go new file mode 100644 index 000000000..57dd66976 --- /dev/null +++ b/chain/actors/builtin/multisig/v13.go @@ -0,0 +1,138 @@ +package multisig + +import ( + "bytes" + "encoding/binary" + "fmt" + + "github.com/ipfs/go-cid" + cbg "github.com/whyrusleeping/cbor-gen" + "golang.org/x/xerrors" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" + actorstypes "github.com/filecoin-project/go-state-types/actors" + builtin13 "github.com/filecoin-project/go-state-types/builtin" + msig13 "github.com/filecoin-project/go-state-types/builtin/v13/multisig" + adt13 "github.com/filecoin-project/go-state-types/builtin/v13/util/adt" + "github.com/filecoin-project/go-state-types/manifest" + + "github.com/filecoin-project/lotus/chain/actors" + "github.com/filecoin-project/lotus/chain/actors/adt" +) + +var _ State = (*state13)(nil) + +func load13(store adt.Store, root cid.Cid) (State, error) { + out := state13{store: store} + err := store.Get(store.Context(), root, &out) + if err != nil { + return nil, err + } + return &out, nil +} + +func make13(store adt.Store, signers []address.Address, threshold uint64, startEpoch abi.ChainEpoch, unlockDuration abi.ChainEpoch, initialBalance abi.TokenAmount) (State, error) { + out := state13{store: store} + out.State = msig13.State{} + out.State.Signers = signers + out.State.NumApprovalsThreshold = threshold + out.State.StartEpoch = startEpoch + out.State.UnlockDuration = unlockDuration + out.State.InitialBalance = initialBalance + + em, err := adt13.StoreEmptyMap(store, builtin13.DefaultHamtBitwidth) + if err != nil { + return nil, err + } + + out.State.PendingTxns = em + + return &out, nil +} + +type state13 struct { + msig13.State + store adt.Store +} + +func (s *state13) LockedBalance(currEpoch abi.ChainEpoch) (abi.TokenAmount, error) { + return s.State.AmountLocked(currEpoch - s.State.StartEpoch), nil +} + +func (s *state13) StartEpoch() (abi.ChainEpoch, error) { + return s.State.StartEpoch, nil +} + +func (s *state13) UnlockDuration() (abi.ChainEpoch, error) { + return s.State.UnlockDuration, nil +} + +func (s *state13) InitialBalance() (abi.TokenAmount, error) { + return s.State.InitialBalance, nil +} + +func (s *state13) Threshold() (uint64, error) { + return s.State.NumApprovalsThreshold, nil +} + +func (s *state13) Signers() ([]address.Address, error) { + return s.State.Signers, nil +} + +func (s *state13) ForEachPendingTxn(cb func(id int64, txn Transaction) error) error { + arr, err := adt13.AsMap(s.store, s.State.PendingTxns, builtin13.DefaultHamtBitwidth) + if err != nil { + return err + } + var out msig13.Transaction + return arr.ForEach(&out, func(key string) error { + txid, n := binary.Varint([]byte(key)) + if n <= 0 { + return xerrors.Errorf("invalid pending transaction key: %v", key) + } + return cb(txid, (Transaction)(out)) //nolint:unconvert + }) +} + +func (s *state13) PendingTxnChanged(other State) (bool, error) { + other13, ok := other.(*state13) + if !ok { + // treat an upgrade as a change, always + return true, nil + } + return !s.State.PendingTxns.Equals(other13.PendingTxns), nil +} + +func (s *state13) transactions() (adt.Map, error) { + return adt13.AsMap(s.store, s.PendingTxns, builtin13.DefaultHamtBitwidth) +} + +func (s *state13) decodeTransaction(val *cbg.Deferred) (Transaction, error) { + var tx msig13.Transaction + if err := tx.UnmarshalCBOR(bytes.NewReader(val.Raw)); err != nil { + return Transaction{}, err + } + return Transaction(tx), nil +} + +func (s *state13) GetState() interface{} { + return &s.State +} + +func (s *state13) ActorKey() string { + return manifest.MultisigKey +} + +func (s *state13) ActorVersion() actorstypes.Version { + return actorstypes.Version13 +} + +func (s *state13) Code() cid.Cid { + code, ok := actors.GetActorCodeID(s.ActorVersion(), s.ActorKey()) + if !ok { + panic(fmt.Errorf("didn't find actor %v code id for actor version %d", s.ActorKey(), s.ActorVersion())) + } + + return code +} diff --git a/chain/actors/builtin/paych/message13.go b/chain/actors/builtin/paych/message13.go new file mode 100644 index 000000000..1614ec608 --- /dev/null +++ b/chain/actors/builtin/paych/message13.go @@ -0,0 +1,109 @@ +package paych + +import ( + "golang.org/x/xerrors" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" + actorstypes "github.com/filecoin-project/go-state-types/actors" + builtin13 "github.com/filecoin-project/go-state-types/builtin" + init13 "github.com/filecoin-project/go-state-types/builtin/v13/init" + paych13 "github.com/filecoin-project/go-state-types/builtin/v13/paych" + paychtypes "github.com/filecoin-project/go-state-types/builtin/v8/paych" + + "github.com/filecoin-project/lotus/chain/actors" + init_ "github.com/filecoin-project/lotus/chain/actors/builtin/init" + "github.com/filecoin-project/lotus/chain/types" +) + +type message13 struct{ from address.Address } + +func (m message13) Create(to address.Address, initialAmount abi.TokenAmount) (*types.Message, error) { + + actorCodeID, ok := actors.GetActorCodeID(actorstypes.Version13, "paymentchannel") + if !ok { + return nil, xerrors.Errorf("error getting actor paymentchannel code id for actor version %d", 13) + } + + params, aerr := actors.SerializeParams(&paych13.ConstructorParams{From: m.from, To: to}) + if aerr != nil { + return nil, aerr + } + enc, aerr := actors.SerializeParams(&init13.ExecParams{ + CodeCID: actorCodeID, + ConstructorParams: params, + }) + if aerr != nil { + return nil, aerr + } + + return &types.Message{ + To: init_.Address, + From: m.from, + Value: initialAmount, + Method: builtin13.MethodsInit.Exec, + Params: enc, + }, nil +} + +func (m message13) Update(paych address.Address, sv *paychtypes.SignedVoucher, secret []byte) (*types.Message, error) { + params, aerr := actors.SerializeParams(&paych13.UpdateChannelStateParams{ + + Sv: toV13SignedVoucher(*sv), + + Secret: secret, + }) + if aerr != nil { + return nil, aerr + } + + return &types.Message{ + To: paych, + From: m.from, + Value: abi.NewTokenAmount(0), + Method: builtin13.MethodsPaych.UpdateChannelState, + Params: params, + }, nil +} + +func toV13SignedVoucher(sv paychtypes.SignedVoucher) paych13.SignedVoucher { + merges := make([]paych13.Merge, len(sv.Merges)) + for i := range sv.Merges { + merges[i] = paych13.Merge{ + Lane: sv.Merges[i].Lane, + Nonce: sv.Merges[i].Nonce, + } + } + + return paych13.SignedVoucher{ + ChannelAddr: sv.ChannelAddr, + TimeLockMin: sv.TimeLockMin, + TimeLockMax: sv.TimeLockMax, + SecretHash: sv.SecretHash, + Extra: (*paych13.ModVerifyParams)(sv.Extra), + Lane: sv.Lane, + Nonce: sv.Nonce, + Amount: sv.Amount, + MinSettleHeight: sv.MinSettleHeight, + Merges: merges, + Signature: sv.Signature, + } +} + +func (m message13) Settle(paych address.Address) (*types.Message, error) { + return &types.Message{ + To: paych, + From: m.from, + Value: abi.NewTokenAmount(0), + Method: builtin13.MethodsPaych.Settle, + }, nil +} + +func (m message13) Collect(paych address.Address) (*types.Message, error) { + return &types.Message{ + To: paych, + From: m.from, + Value: abi.NewTokenAmount(0), + Method: builtin13.MethodsPaych.Collect, + }, nil +} diff --git a/chain/actors/builtin/paych/paych.go b/chain/actors/builtin/paych/paych.go index fc8908cb4..2b5c78edf 100644 --- a/chain/actors/builtin/paych/paych.go +++ b/chain/actors/builtin/paych/paych.go @@ -53,6 +53,9 @@ func Load(store adt.Store, act *types.Actor) (State, error) { case actorstypes.Version12: return load12(store, act.Head) + case actorstypes.Version13: + return load13(store, act.Head) + } } @@ -173,6 +176,9 @@ func Message(version actorstypes.Version, from address.Address) MessageBuilder { case actorstypes.Version12: return message12{from} + case actorstypes.Version13: + return message13{from} + default: panic(fmt.Sprintf("unsupported actors version: %d", version)) } @@ -215,5 +221,6 @@ func AllCodes() []cid.Cid { (&state10{}).Code(), (&state11{}).Code(), (&state12{}).Code(), + (&state13{}).Code(), } } diff --git a/chain/actors/builtin/paych/v13.go b/chain/actors/builtin/paych/v13.go new file mode 100644 index 000000000..c5a10c571 --- /dev/null +++ b/chain/actors/builtin/paych/v13.go @@ -0,0 +1,135 @@ +package paych + +import ( + "fmt" + + "github.com/ipfs/go-cid" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" + actorstypes "github.com/filecoin-project/go-state-types/actors" + "github.com/filecoin-project/go-state-types/big" + paych13 "github.com/filecoin-project/go-state-types/builtin/v13/paych" + adt13 "github.com/filecoin-project/go-state-types/builtin/v13/util/adt" + "github.com/filecoin-project/go-state-types/manifest" + + "github.com/filecoin-project/lotus/chain/actors" + "github.com/filecoin-project/lotus/chain/actors/adt" +) + +var _ State = (*state13)(nil) + +func load13(store adt.Store, root cid.Cid) (State, error) { + out := state13{store: store} + err := store.Get(store.Context(), root, &out) + if err != nil { + return nil, err + } + return &out, nil +} + +func make13(store adt.Store) (State, error) { + out := state13{store: store} + out.State = paych13.State{} + return &out, nil +} + +type state13 struct { + paych13.State + store adt.Store + lsAmt *adt13.Array +} + +// Channel owner, who has funded the actor +func (s *state13) From() (address.Address, error) { + return s.State.From, nil +} + +// Recipient of payouts from channel +func (s *state13) To() (address.Address, error) { + return s.State.To, nil +} + +// Height at which the channel can be `Collected` +func (s *state13) SettlingAt() (abi.ChainEpoch, error) { + return s.State.SettlingAt, nil +} + +// Amount successfully redeemed through the payment channel, paid out on `Collect()` +func (s *state13) ToSend() (abi.TokenAmount, error) { + return s.State.ToSend, nil +} + +func (s *state13) getOrLoadLsAmt() (*adt13.Array, error) { + if s.lsAmt != nil { + return s.lsAmt, nil + } + + // Get the lane state from the chain + lsamt, err := adt13.AsArray(s.store, s.State.LaneStates, paych13.LaneStatesAmtBitwidth) + if err != nil { + return nil, err + } + + s.lsAmt = lsamt + return lsamt, nil +} + +// Get total number of lanes +func (s *state13) LaneCount() (uint64, error) { + lsamt, err := s.getOrLoadLsAmt() + if err != nil { + return 0, err + } + return lsamt.Length(), nil +} + +func (s *state13) GetState() interface{} { + return &s.State +} + +// Iterate lane states +func (s *state13) ForEachLaneState(cb func(idx uint64, dl LaneState) error) error { + // Get the lane state from the chain + lsamt, err := s.getOrLoadLsAmt() + if err != nil { + return err + } + + // Note: we use a map instead of an array to store laneStates because the + // client sets the lane ID (the index) and potentially they could use a + // very large index. + var ls paych13.LaneState + return lsamt.ForEach(&ls, func(i int64) error { + return cb(uint64(i), &laneState13{ls}) + }) +} + +type laneState13 struct { + paych13.LaneState +} + +func (ls *laneState13) Redeemed() (big.Int, error) { + return ls.LaneState.Redeemed, nil +} + +func (ls *laneState13) Nonce() (uint64, error) { + return ls.LaneState.Nonce, nil +} + +func (s *state13) ActorKey() string { + return manifest.PaychKey +} + +func (s *state13) ActorVersion() actorstypes.Version { + return actorstypes.Version13 +} + +func (s *state13) Code() cid.Cid { + code, ok := actors.GetActorCodeID(s.ActorVersion(), s.ActorKey()) + if !ok { + panic(fmt.Errorf("didn't find actor %v code id for actor version %d", s.ActorKey(), s.ActorVersion())) + } + + return code +} diff --git a/chain/actors/builtin/power/power.go b/chain/actors/builtin/power/power.go index 9b64ded38..e263e3f87 100644 --- a/chain/actors/builtin/power/power.go +++ b/chain/actors/builtin/power/power.go @@ -9,7 +9,7 @@ import ( "github.com/filecoin-project/go-state-types/abi" actorstypes "github.com/filecoin-project/go-state-types/actors" "github.com/filecoin-project/go-state-types/big" - builtin12 "github.com/filecoin-project/go-state-types/builtin" + builtin13 "github.com/filecoin-project/go-state-types/builtin" "github.com/filecoin-project/go-state-types/cbor" "github.com/filecoin-project/go-state-types/manifest" builtin0 "github.com/filecoin-project/specs-actors/actors/builtin" @@ -27,8 +27,8 @@ import ( ) var ( - Address = builtin12.StoragePowerActorAddr - Methods = builtin12.MethodsPower + Address = builtin13.StoragePowerActorAddr + Methods = builtin13.MethodsPower ) func Load(store adt.Store, act *types.Actor) (State, error) { @@ -54,6 +54,9 @@ func Load(store adt.Store, act *types.Actor) (State, error) { case actorstypes.Version12: return load12(store, act.Head) + case actorstypes.Version13: + return load13(store, act.Head) + } } @@ -124,6 +127,9 @@ func MakeState(store adt.Store, av actorstypes.Version) (State, error) { case actorstypes.Version12: return make12(store) + case actorstypes.Version13: + return make13(store) + } return nil, xerrors.Errorf("unknown actor version %d", av) } @@ -190,5 +196,6 @@ func AllCodes() []cid.Cid { (&state10{}).Code(), (&state11{}).Code(), (&state12{}).Code(), + (&state13{}).Code(), } } diff --git a/chain/actors/builtin/power/v13.go b/chain/actors/builtin/power/v13.go new file mode 100644 index 000000000..4cf761b16 --- /dev/null +++ b/chain/actors/builtin/power/v13.go @@ -0,0 +1,207 @@ +package power + +import ( + "bytes" + "fmt" + + "github.com/ipfs/go-cid" + cbg "github.com/whyrusleeping/cbor-gen" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" + actorstypes "github.com/filecoin-project/go-state-types/actors" + builtin13 "github.com/filecoin-project/go-state-types/builtin" + power13 "github.com/filecoin-project/go-state-types/builtin/v13/power" + adt13 "github.com/filecoin-project/go-state-types/builtin/v13/util/adt" + "github.com/filecoin-project/go-state-types/manifest" + + "github.com/filecoin-project/lotus/chain/actors" + "github.com/filecoin-project/lotus/chain/actors/adt" + "github.com/filecoin-project/lotus/chain/actors/builtin" +) + +var _ State = (*state13)(nil) + +func load13(store adt.Store, root cid.Cid) (State, error) { + out := state13{store: store} + err := store.Get(store.Context(), root, &out) + if err != nil { + return nil, err + } + return &out, nil +} + +func make13(store adt.Store) (State, error) { + out := state13{store: store} + + s, err := power13.ConstructState(store) + if err != nil { + return nil, err + } + + out.State = *s + + return &out, nil +} + +type state13 struct { + power13.State + store adt.Store +} + +func (s *state13) TotalLocked() (abi.TokenAmount, error) { + return s.TotalPledgeCollateral, nil +} + +func (s *state13) TotalPower() (Claim, error) { + return Claim{ + RawBytePower: s.TotalRawBytePower, + QualityAdjPower: s.TotalQualityAdjPower, + }, nil +} + +// Committed power to the network. Includes miners below the minimum threshold. +func (s *state13) TotalCommitted() (Claim, error) { + return Claim{ + RawBytePower: s.TotalBytesCommitted, + QualityAdjPower: s.TotalQABytesCommitted, + }, nil +} + +func (s *state13) MinerPower(addr address.Address) (Claim, bool, error) { + claims, err := s.claims() + if err != nil { + return Claim{}, false, err + } + var claim power13.Claim + ok, err := claims.Get(abi.AddrKey(addr), &claim) + if err != nil { + return Claim{}, false, err + } + return Claim{ + RawBytePower: claim.RawBytePower, + QualityAdjPower: claim.QualityAdjPower, + }, ok, nil +} + +func (s *state13) MinerNominalPowerMeetsConsensusMinimum(a address.Address) (bool, error) { + return s.State.MinerNominalPowerMeetsConsensusMinimum(s.store, a) +} + +func (s *state13) TotalPowerSmoothed() (builtin.FilterEstimate, error) { + return builtin.FilterEstimate(s.State.ThisEpochQAPowerSmoothed), nil +} + +func (s *state13) MinerCounts() (uint64, uint64, error) { + return uint64(s.State.MinerAboveMinPowerCount), uint64(s.State.MinerCount), nil +} + +func (s *state13) ListAllMiners() ([]address.Address, error) { + claims, err := s.claims() + if err != nil { + return nil, err + } + + var miners []address.Address + err = claims.ForEach(nil, func(k string) error { + a, err := address.NewFromBytes([]byte(k)) + if err != nil { + return err + } + miners = append(miners, a) + return nil + }) + if err != nil { + return nil, err + } + + return miners, nil +} + +func (s *state13) ForEachClaim(cb func(miner address.Address, claim Claim) error) error { + claims, err := s.claims() + if err != nil { + return err + } + + var claim power13.Claim + return claims.ForEach(&claim, func(k string) error { + a, err := address.NewFromBytes([]byte(k)) + if err != nil { + return err + } + return cb(a, Claim{ + RawBytePower: claim.RawBytePower, + QualityAdjPower: claim.QualityAdjPower, + }) + }) +} + +func (s *state13) ClaimsChanged(other State) (bool, error) { + other13, ok := other.(*state13) + if !ok { + // treat an upgrade as a change, always + return true, nil + } + return !s.State.Claims.Equals(other13.State.Claims), nil +} + +func (s *state13) SetTotalQualityAdjPower(p abi.StoragePower) error { + s.State.TotalQualityAdjPower = p + return nil +} + +func (s *state13) SetTotalRawBytePower(p abi.StoragePower) error { + s.State.TotalRawBytePower = p + return nil +} + +func (s *state13) SetThisEpochQualityAdjPower(p abi.StoragePower) error { + s.State.ThisEpochQualityAdjPower = p + return nil +} + +func (s *state13) SetThisEpochRawBytePower(p abi.StoragePower) error { + s.State.ThisEpochRawBytePower = p + return nil +} + +func (s *state13) GetState() interface{} { + return &s.State +} + +func (s *state13) claims() (adt.Map, error) { + return adt13.AsMap(s.store, s.Claims, builtin13.DefaultHamtBitwidth) +} + +func (s *state13) decodeClaim(val *cbg.Deferred) (Claim, error) { + var ci power13.Claim + if err := ci.UnmarshalCBOR(bytes.NewReader(val.Raw)); err != nil { + return Claim{}, err + } + return fromV13Claim(ci), nil +} + +func fromV13Claim(v13 power13.Claim) Claim { + return Claim{ + RawBytePower: v13.RawBytePower, + QualityAdjPower: v13.QualityAdjPower, + } +} + +func (s *state13) ActorKey() string { + return manifest.PowerKey +} + +func (s *state13) ActorVersion() actorstypes.Version { + return actorstypes.Version13 +} + +func (s *state13) Code() cid.Cid { + code, ok := actors.GetActorCodeID(s.ActorVersion(), s.ActorKey()) + if !ok { + panic(fmt.Errorf("didn't find actor %v code id for actor version %d", s.ActorKey(), s.ActorVersion())) + } + + return code +} diff --git a/chain/actors/builtin/registry.go b/chain/actors/builtin/registry.go index 6ba5fef03..93768580b 100644 --- a/chain/actors/builtin/registry.go +++ b/chain/actors/builtin/registry.go @@ -58,6 +58,22 @@ import ( reward12 "github.com/filecoin-project/go-state-types/builtin/v12/reward" system12 "github.com/filecoin-project/go-state-types/builtin/v12/system" verifreg12 "github.com/filecoin-project/go-state-types/builtin/v12/verifreg" + account13 "github.com/filecoin-project/go-state-types/builtin/v13/account" + cron13 "github.com/filecoin-project/go-state-types/builtin/v13/cron" + datacap13 "github.com/filecoin-project/go-state-types/builtin/v13/datacap" + eam13 "github.com/filecoin-project/go-state-types/builtin/v13/eam" + ethaccount13 "github.com/filecoin-project/go-state-types/builtin/v13/ethaccount" + evm13 "github.com/filecoin-project/go-state-types/builtin/v13/evm" + _init13 "github.com/filecoin-project/go-state-types/builtin/v13/init" + market13 "github.com/filecoin-project/go-state-types/builtin/v13/market" + miner13 "github.com/filecoin-project/go-state-types/builtin/v13/miner" + multisig13 "github.com/filecoin-project/go-state-types/builtin/v13/multisig" + paych13 "github.com/filecoin-project/go-state-types/builtin/v13/paych" + placeholder13 "github.com/filecoin-project/go-state-types/builtin/v13/placeholder" + power13 "github.com/filecoin-project/go-state-types/builtin/v13/power" + reward13 "github.com/filecoin-project/go-state-types/builtin/v13/reward" + system13 "github.com/filecoin-project/go-state-types/builtin/v13/system" + verifreg13 "github.com/filecoin-project/go-state-types/builtin/v13/verifreg" account8 "github.com/filecoin-project/go-state-types/builtin/v8/account" cron8 "github.com/filecoin-project/go-state-types/builtin/v8/cron" _init8 "github.com/filecoin-project/go-state-types/builtin/v8/init" @@ -617,6 +633,110 @@ func MakeRegistry(av actorstypes.Version) []RegistryEntry { } } + case actorstypes.Version13: + for key, codeID := range codeIDs { + switch key { + case manifest.AccountKey: + registry = append(registry, RegistryEntry{ + code: codeID, + methods: account13.Methods, + state: new(account13.State), + }) + case manifest.CronKey: + registry = append(registry, RegistryEntry{ + code: codeID, + methods: cron13.Methods, + state: new(cron13.State), + }) + case manifest.InitKey: + registry = append(registry, RegistryEntry{ + code: codeID, + methods: _init13.Methods, + state: new(_init13.State), + }) + case manifest.MarketKey: + registry = append(registry, RegistryEntry{ + code: codeID, + methods: market13.Methods, + state: new(market13.State), + }) + case manifest.MinerKey: + registry = append(registry, RegistryEntry{ + code: codeID, + methods: miner13.Methods, + state: new(miner13.State), + }) + case manifest.MultisigKey: + registry = append(registry, RegistryEntry{ + code: codeID, + methods: multisig13.Methods, + state: new(multisig13.State), + }) + case manifest.PaychKey: + registry = append(registry, RegistryEntry{ + code: codeID, + methods: paych13.Methods, + state: new(paych13.State), + }) + case manifest.PowerKey: + registry = append(registry, RegistryEntry{ + code: codeID, + methods: power13.Methods, + state: new(power13.State), + }) + case manifest.RewardKey: + registry = append(registry, RegistryEntry{ + code: codeID, + methods: reward13.Methods, + state: new(reward13.State), + }) + case manifest.SystemKey: + registry = append(registry, RegistryEntry{ + code: codeID, + methods: system13.Methods, + state: new(system13.State), + }) + case manifest.VerifregKey: + registry = append(registry, RegistryEntry{ + code: codeID, + methods: verifreg13.Methods, + state: new(verifreg13.State), + }) + case manifest.DatacapKey: + registry = append(registry, RegistryEntry{ + code: codeID, + methods: datacap13.Methods, + state: new(datacap13.State), + }) + + case manifest.EvmKey: + registry = append(registry, RegistryEntry{ + code: codeID, + methods: evm13.Methods, + state: new(evm13.State), + }) + case manifest.EamKey: + registry = append(registry, RegistryEntry{ + code: codeID, + methods: eam13.Methods, + state: nil, + }) + case manifest.PlaceholderKey: + registry = append(registry, RegistryEntry{ + code: codeID, + methods: placeholder13.Methods, + state: nil, + }) + case manifest.EthAccountKey: + registry = append(registry, RegistryEntry{ + code: codeID, + methods: ethaccount13.Methods, + state: nil, + }) + + } + } + default: panic("expected version v8 and up only, use specs-actors for v0-7") } diff --git a/chain/actors/builtin/reward/reward.go b/chain/actors/builtin/reward/reward.go index 3c6463645..a6c8bff5b 100644 --- a/chain/actors/builtin/reward/reward.go +++ b/chain/actors/builtin/reward/reward.go @@ -6,7 +6,7 @@ import ( "github.com/filecoin-project/go-state-types/abi" actorstypes "github.com/filecoin-project/go-state-types/actors" - builtin12 "github.com/filecoin-project/go-state-types/builtin" + builtin13 "github.com/filecoin-project/go-state-types/builtin" "github.com/filecoin-project/go-state-types/cbor" "github.com/filecoin-project/go-state-types/manifest" builtin0 "github.com/filecoin-project/specs-actors/actors/builtin" @@ -25,8 +25,8 @@ import ( ) var ( - Address = builtin12.RewardActorAddr - Methods = builtin12.MethodsReward + Address = builtin13.RewardActorAddr + Methods = builtin13.MethodsReward ) func Load(store adt.Store, act *types.Actor) (State, error) { @@ -52,6 +52,9 @@ func Load(store adt.Store, act *types.Actor) (State, error) { case actorstypes.Version12: return load12(store, act.Head) + case actorstypes.Version13: + return load13(store, act.Head) + } } @@ -122,6 +125,9 @@ func MakeState(store adt.Store, av actorstypes.Version, currRealizedPower abi.St case actorstypes.Version12: return make12(store, currRealizedPower) + case actorstypes.Version13: + return make13(store, currRealizedPower) + } return nil, xerrors.Errorf("unknown actor version %d", av) } @@ -166,5 +172,6 @@ func AllCodes() []cid.Cid { (&state10{}).Code(), (&state11{}).Code(), (&state12{}).Code(), + (&state13{}).Code(), } } diff --git a/chain/actors/builtin/reward/v13.go b/chain/actors/builtin/reward/v13.go new file mode 100644 index 000000000..e8d343cab --- /dev/null +++ b/chain/actors/builtin/reward/v13.go @@ -0,0 +1,120 @@ +package reward + +import ( + "fmt" + + "github.com/ipfs/go-cid" + + "github.com/filecoin-project/go-state-types/abi" + actorstypes "github.com/filecoin-project/go-state-types/actors" + miner13 "github.com/filecoin-project/go-state-types/builtin/v13/miner" + reward13 "github.com/filecoin-project/go-state-types/builtin/v13/reward" + smoothing13 "github.com/filecoin-project/go-state-types/builtin/v13/util/smoothing" + "github.com/filecoin-project/go-state-types/manifest" + + "github.com/filecoin-project/lotus/chain/actors" + "github.com/filecoin-project/lotus/chain/actors/adt" + "github.com/filecoin-project/lotus/chain/actors/builtin" +) + +var _ State = (*state13)(nil) + +func load13(store adt.Store, root cid.Cid) (State, error) { + out := state13{store: store} + err := store.Get(store.Context(), root, &out) + if err != nil { + return nil, err + } + return &out, nil +} + +func make13(store adt.Store, currRealizedPower abi.StoragePower) (State, error) { + out := state13{store: store} + out.State = *reward13.ConstructState(currRealizedPower) + return &out, nil +} + +type state13 struct { + reward13.State + store adt.Store +} + +func (s *state13) ThisEpochReward() (abi.TokenAmount, error) { + return s.State.ThisEpochReward, nil +} + +func (s *state13) ThisEpochRewardSmoothed() (builtin.FilterEstimate, error) { + + return builtin.FilterEstimate{ + PositionEstimate: s.State.ThisEpochRewardSmoothed.PositionEstimate, + VelocityEstimate: s.State.ThisEpochRewardSmoothed.VelocityEstimate, + }, nil + +} + +func (s *state13) ThisEpochBaselinePower() (abi.StoragePower, error) { + return s.State.ThisEpochBaselinePower, nil +} + +func (s *state13) TotalStoragePowerReward() (abi.TokenAmount, error) { + return s.State.TotalStoragePowerReward, nil +} + +func (s *state13) EffectiveBaselinePower() (abi.StoragePower, error) { + return s.State.EffectiveBaselinePower, nil +} + +func (s *state13) EffectiveNetworkTime() (abi.ChainEpoch, error) { + return s.State.EffectiveNetworkTime, nil +} + +func (s *state13) CumsumBaseline() (reward13.Spacetime, error) { + return s.State.CumsumBaseline, nil +} + +func (s *state13) CumsumRealized() (reward13.Spacetime, error) { + return s.State.CumsumRealized, nil +} + +func (s *state13) InitialPledgeForPower(qaPower abi.StoragePower, networkTotalPledge abi.TokenAmount, networkQAPower *builtin.FilterEstimate, circSupply abi.TokenAmount) (abi.TokenAmount, error) { + return miner13.InitialPledgeForPower( + qaPower, + s.State.ThisEpochBaselinePower, + s.State.ThisEpochRewardSmoothed, + smoothing13.FilterEstimate{ + PositionEstimate: networkQAPower.PositionEstimate, + VelocityEstimate: networkQAPower.VelocityEstimate, + }, + circSupply, + ), nil +} + +func (s *state13) PreCommitDepositForPower(networkQAPower builtin.FilterEstimate, sectorWeight abi.StoragePower) (abi.TokenAmount, error) { + return miner13.PreCommitDepositForPower(s.State.ThisEpochRewardSmoothed, + smoothing13.FilterEstimate{ + PositionEstimate: networkQAPower.PositionEstimate, + VelocityEstimate: networkQAPower.VelocityEstimate, + }, + sectorWeight), nil +} + +func (s *state13) GetState() interface{} { + return &s.State +} + +func (s *state13) ActorKey() string { + return manifest.RewardKey +} + +func (s *state13) ActorVersion() actorstypes.Version { + return actorstypes.Version13 +} + +func (s *state13) Code() cid.Cid { + code, ok := actors.GetActorCodeID(s.ActorVersion(), s.ActorKey()) + if !ok { + panic(fmt.Errorf("didn't find actor %v code id for actor version %d", s.ActorKey(), s.ActorVersion())) + } + + return code +} diff --git a/chain/actors/builtin/system/system.go b/chain/actors/builtin/system/system.go index 2a2b703bb..1526a1fc0 100644 --- a/chain/actors/builtin/system/system.go +++ b/chain/actors/builtin/system/system.go @@ -5,7 +5,7 @@ import ( "golang.org/x/xerrors" actorstypes "github.com/filecoin-project/go-state-types/actors" - builtin12 "github.com/filecoin-project/go-state-types/builtin" + builtin13 "github.com/filecoin-project/go-state-types/builtin" "github.com/filecoin-project/go-state-types/manifest" builtin0 "github.com/filecoin-project/specs-actors/actors/builtin" builtin2 "github.com/filecoin-project/specs-actors/v2/actors/builtin" @@ -21,7 +21,7 @@ import ( ) var ( - Address = builtin12.SystemActorAddr + Address = builtin13.SystemActorAddr ) func Load(store adt.Store, act *types.Actor) (State, error) { @@ -47,6 +47,9 @@ func Load(store adt.Store, act *types.Actor) (State, error) { case actorstypes.Version12: return load12(store, act.Head) + case actorstypes.Version13: + return load13(store, act.Head) + } } @@ -117,6 +120,9 @@ func MakeState(store adt.Store, av actorstypes.Version, builtinActors cid.Cid) ( case actorstypes.Version12: return make12(store, builtinActors) + case actorstypes.Version13: + return make13(store, builtinActors) + } return nil, xerrors.Errorf("unknown actor version %d", av) } @@ -145,5 +151,6 @@ func AllCodes() []cid.Cid { (&state10{}).Code(), (&state11{}).Code(), (&state12{}).Code(), + (&state13{}).Code(), } } diff --git a/chain/actors/builtin/system/v13.go b/chain/actors/builtin/system/v13.go new file mode 100644 index 000000000..8facf0033 --- /dev/null +++ b/chain/actors/builtin/system/v13.go @@ -0,0 +1,72 @@ +package system + +import ( + "fmt" + + "github.com/ipfs/go-cid" + + actorstypes "github.com/filecoin-project/go-state-types/actors" + system13 "github.com/filecoin-project/go-state-types/builtin/v13/system" + "github.com/filecoin-project/go-state-types/manifest" + + "github.com/filecoin-project/lotus/chain/actors" + "github.com/filecoin-project/lotus/chain/actors/adt" +) + +var _ State = (*state13)(nil) + +func load13(store adt.Store, root cid.Cid) (State, error) { + out := state13{store: store} + err := store.Get(store.Context(), root, &out) + if err != nil { + return nil, err + } + return &out, nil +} + +func make13(store adt.Store, builtinActors cid.Cid) (State, error) { + out := state13{store: store} + out.State = system13.State{ + BuiltinActors: builtinActors, + } + return &out, nil +} + +type state13 struct { + system13.State + store adt.Store +} + +func (s *state13) GetState() interface{} { + return &s.State +} + +func (s *state13) GetBuiltinActors() cid.Cid { + + return s.State.BuiltinActors + +} + +func (s *state13) SetBuiltinActors(c cid.Cid) error { + + s.State.BuiltinActors = c + return nil + +} + +func (s *state13) ActorKey() string { + return manifest.SystemKey +} + +func (s *state13) ActorVersion() actorstypes.Version { + return actorstypes.Version13 +} + +func (s *state13) Code() cid.Cid { + code, ok := actors.GetActorCodeID(s.ActorVersion(), s.ActorKey()) + if !ok { + panic(fmt.Errorf("didn't find actor %v code id for actor version %d", s.ActorKey(), s.ActorVersion())) + } + + return code +} diff --git a/chain/actors/builtin/verifreg/actor.go.template b/chain/actors/builtin/verifreg/actor.go.template index 991c6717b..f4467d979 100644 --- a/chain/actors/builtin/verifreg/actor.go.template +++ b/chain/actors/builtin/verifreg/actor.go.template @@ -81,8 +81,10 @@ type State interface { ForEachClient(func(addr address.Address, dcap abi.StoragePower) error) error GetAllocation(clientIdAddr address.Address, allocationId AllocationId) (*Allocation, bool, error) GetAllocations(clientIdAddr address.Address) (map[AllocationId]Allocation, error) + GetAllAllocations() (map[AllocationId]Allocation, error) GetClaim(providerIdAddr address.Address, claimId ClaimId) (*Claim, bool, error) GetClaims(providerIdAddr address.Address) (map[ClaimId]Claim, error) + GetAllClaims() (map[ClaimId]Claim, error) GetClaimIdsBySector(providerIdAddr address.Address) (map[abi.SectorNumber][]ClaimId, error) GetState() interface{} } diff --git a/chain/actors/builtin/verifreg/state.go.template b/chain/actors/builtin/verifreg/state.go.template index adcbc22c2..7835b16b6 100644 --- a/chain/actors/builtin/verifreg/state.go.template +++ b/chain/actors/builtin/verifreg/state.go.template @@ -145,6 +145,21 @@ func (s *state{{.v}}) GetAllocations(clientIdAddr address.Address) (map[Allocati {{end}} } +func (s *state{{.v}}) GetAllAllocations() (map[AllocationId]Allocation, error) { +{{if (le .v 8)}} + return nil, xerrors.Errorf("unsupported in actors v{{.v}}") +{{else}} + v{{.v}}Map, err := s.State.GetAllAllocations(s.store) + + retMap := make(map[AllocationId]Allocation, len(v{{.v}}Map)) + for k, v := range v{{.v}}Map { + retMap[AllocationId(k)] = Allocation(v) + } + + return retMap, err +{{end}} +} + func (s *state{{.v}}) GetClaim(providerIdAddr address.Address, claimId verifreg9.ClaimId) (*Claim, bool, error) { {{if (le .v 8)}} return nil, false, xerrors.Errorf("unsupported in actors v{{.v}}") @@ -170,6 +185,22 @@ func (s *state{{.v}}) GetClaims(providerIdAddr address.Address) (map[ClaimId]Cla {{end}} } +func (s *state{{.v}}) GetAllClaims() (map[ClaimId]Claim, error) { +{{if (le .v 8)}} + return nil, xerrors.Errorf("unsupported in actors v{{.v}}") +{{else}} + v{{.v}}Map, err := s.State.GetAllClaims(s.store) + + retMap := make(map[ClaimId]Claim, len(v{{.v}}Map)) + for k, v := range v{{.v}}Map { + retMap[ClaimId(k)] = Claim(v) + } + + return retMap, err + +{{end}} +} + func (s *state{{.v}}) GetClaimIdsBySector(providerIdAddr address.Address) (map[abi.SectorNumber][]ClaimId, error) { {{if (le .v 8)}} return nil, xerrors.Errorf("unsupported in actors v{{.v}}") diff --git a/chain/actors/builtin/verifreg/v0.go b/chain/actors/builtin/verifreg/v0.go index 9913c42c0..4129e7a2d 100644 --- a/chain/actors/builtin/verifreg/v0.go +++ b/chain/actors/builtin/verifreg/v0.go @@ -106,6 +106,12 @@ func (s *state0) GetAllocations(clientIdAddr address.Address) (map[AllocationId] } +func (s *state0) GetAllAllocations() (map[AllocationId]Allocation, error) { + + return nil, xerrors.Errorf("unsupported in actors v0") + +} + func (s *state0) GetClaim(providerIdAddr address.Address, claimId verifreg9.ClaimId) (*Claim, bool, error) { return nil, false, xerrors.Errorf("unsupported in actors v0") @@ -118,6 +124,12 @@ func (s *state0) GetClaims(providerIdAddr address.Address) (map[ClaimId]Claim, e } +func (s *state0) GetAllClaims() (map[ClaimId]Claim, error) { + + return nil, xerrors.Errorf("unsupported in actors v0") + +} + func (s *state0) GetClaimIdsBySector(providerIdAddr address.Address) (map[abi.SectorNumber][]ClaimId, error) { return nil, xerrors.Errorf("unsupported in actors v0") diff --git a/chain/actors/builtin/verifreg/v10.go b/chain/actors/builtin/verifreg/v10.go index 256f4d2f8..85f85c7f2 100644 --- a/chain/actors/builtin/verifreg/v10.go +++ b/chain/actors/builtin/verifreg/v10.go @@ -114,6 +114,19 @@ func (s *state10) GetAllocations(clientIdAddr address.Address) (map[AllocationId } +func (s *state10) GetAllAllocations() (map[AllocationId]Allocation, error) { + + v10Map, err := s.State.GetAllAllocations(s.store) + + retMap := make(map[AllocationId]Allocation, len(v10Map)) + for k, v := range v10Map { + retMap[AllocationId(k)] = Allocation(v) + } + + return retMap, err + +} + func (s *state10) GetClaim(providerIdAddr address.Address, claimId verifreg9.ClaimId) (*Claim, bool, error) { claim, ok, err := s.FindClaim(s.store, providerIdAddr, verifreg10.ClaimId(claimId)) @@ -134,6 +147,19 @@ func (s *state10) GetClaims(providerIdAddr address.Address) (map[ClaimId]Claim, } +func (s *state10) GetAllClaims() (map[ClaimId]Claim, error) { + + v10Map, err := s.State.GetAllClaims(s.store) + + retMap := make(map[ClaimId]Claim, len(v10Map)) + for k, v := range v10Map { + retMap[ClaimId(k)] = Claim(v) + } + + return retMap, err + +} + func (s *state10) GetClaimIdsBySector(providerIdAddr address.Address) (map[abi.SectorNumber][]ClaimId, error) { v10Map, err := s.LoadClaimsToMap(s.store, providerIdAddr) diff --git a/chain/actors/builtin/verifreg/v11.go b/chain/actors/builtin/verifreg/v11.go index 7b7b9e4c0..5a8e1cb0c 100644 --- a/chain/actors/builtin/verifreg/v11.go +++ b/chain/actors/builtin/verifreg/v11.go @@ -114,6 +114,19 @@ func (s *state11) GetAllocations(clientIdAddr address.Address) (map[AllocationId } +func (s *state11) GetAllAllocations() (map[AllocationId]Allocation, error) { + + v11Map, err := s.State.GetAllAllocations(s.store) + + retMap := make(map[AllocationId]Allocation, len(v11Map)) + for k, v := range v11Map { + retMap[AllocationId(k)] = Allocation(v) + } + + return retMap, err + +} + func (s *state11) GetClaim(providerIdAddr address.Address, claimId verifreg9.ClaimId) (*Claim, bool, error) { claim, ok, err := s.FindClaim(s.store, providerIdAddr, verifreg11.ClaimId(claimId)) @@ -134,6 +147,19 @@ func (s *state11) GetClaims(providerIdAddr address.Address) (map[ClaimId]Claim, } +func (s *state11) GetAllClaims() (map[ClaimId]Claim, error) { + + v11Map, err := s.State.GetAllClaims(s.store) + + retMap := make(map[ClaimId]Claim, len(v11Map)) + for k, v := range v11Map { + retMap[ClaimId(k)] = Claim(v) + } + + return retMap, err + +} + func (s *state11) GetClaimIdsBySector(providerIdAddr address.Address) (map[abi.SectorNumber][]ClaimId, error) { v11Map, err := s.LoadClaimsToMap(s.store, providerIdAddr) diff --git a/chain/actors/builtin/verifreg/v12.go b/chain/actors/builtin/verifreg/v12.go index 77a113fbe..7c9a493f1 100644 --- a/chain/actors/builtin/verifreg/v12.go +++ b/chain/actors/builtin/verifreg/v12.go @@ -114,6 +114,19 @@ func (s *state12) GetAllocations(clientIdAddr address.Address) (map[AllocationId } +func (s *state12) GetAllAllocations() (map[AllocationId]Allocation, error) { + + v12Map, err := s.State.GetAllAllocations(s.store) + + retMap := make(map[AllocationId]Allocation, len(v12Map)) + for k, v := range v12Map { + retMap[AllocationId(k)] = Allocation(v) + } + + return retMap, err + +} + func (s *state12) GetClaim(providerIdAddr address.Address, claimId verifreg9.ClaimId) (*Claim, bool, error) { claim, ok, err := s.FindClaim(s.store, providerIdAddr, verifreg12.ClaimId(claimId)) @@ -134,6 +147,19 @@ func (s *state12) GetClaims(providerIdAddr address.Address) (map[ClaimId]Claim, } +func (s *state12) GetAllClaims() (map[ClaimId]Claim, error) { + + v12Map, err := s.State.GetAllClaims(s.store) + + retMap := make(map[ClaimId]Claim, len(v12Map)) + for k, v := range v12Map { + retMap[ClaimId(k)] = Claim(v) + } + + return retMap, err + +} + func (s *state12) GetClaimIdsBySector(providerIdAddr address.Address) (map[abi.SectorNumber][]ClaimId, error) { v12Map, err := s.LoadClaimsToMap(s.store, providerIdAddr) diff --git a/chain/actors/builtin/verifreg/v13.go b/chain/actors/builtin/verifreg/v13.go new file mode 100644 index 000000000..0c487a2f7 --- /dev/null +++ b/chain/actors/builtin/verifreg/v13.go @@ -0,0 +1,196 @@ +package verifreg + +import ( + "fmt" + + "github.com/ipfs/go-cid" + "golang.org/x/xerrors" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" + actorstypes "github.com/filecoin-project/go-state-types/actors" + "github.com/filecoin-project/go-state-types/big" + builtin13 "github.com/filecoin-project/go-state-types/builtin" + adt13 "github.com/filecoin-project/go-state-types/builtin/v13/util/adt" + verifreg13 "github.com/filecoin-project/go-state-types/builtin/v13/verifreg" + verifreg9 "github.com/filecoin-project/go-state-types/builtin/v9/verifreg" + "github.com/filecoin-project/go-state-types/manifest" + + "github.com/filecoin-project/lotus/chain/actors" + "github.com/filecoin-project/lotus/chain/actors/adt" +) + +var _ State = (*state13)(nil) + +func load13(store adt.Store, root cid.Cid) (State, error) { + out := state13{store: store} + err := store.Get(store.Context(), root, &out) + if err != nil { + return nil, err + } + return &out, nil +} + +func make13(store adt.Store, rootKeyAddress address.Address) (State, error) { + out := state13{store: store} + + s, err := verifreg13.ConstructState(store, rootKeyAddress) + if err != nil { + return nil, err + } + + out.State = *s + + return &out, nil +} + +type state13 struct { + verifreg13.State + store adt.Store +} + +func (s *state13) RootKey() (address.Address, error) { + return s.State.RootKey, nil +} + +func (s *state13) VerifiedClientDataCap(addr address.Address) (bool, abi.StoragePower, error) { + + return false, big.Zero(), xerrors.Errorf("unsupported in actors v13") + +} + +func (s *state13) VerifierDataCap(addr address.Address) (bool, abi.StoragePower, error) { + return getDataCap(s.store, actors.Version13, s.verifiers, addr) +} + +func (s *state13) RemoveDataCapProposalID(verifier address.Address, client address.Address) (bool, uint64, error) { + return getRemoveDataCapProposalID(s.store, actors.Version13, s.removeDataCapProposalIDs, verifier, client) +} + +func (s *state13) ForEachVerifier(cb func(addr address.Address, dcap abi.StoragePower) error) error { + return forEachCap(s.store, actors.Version13, s.verifiers, cb) +} + +func (s *state13) ForEachClient(cb func(addr address.Address, dcap abi.StoragePower) error) error { + + return xerrors.Errorf("unsupported in actors v13") + +} + +func (s *state13) verifiedClients() (adt.Map, error) { + + return nil, xerrors.Errorf("unsupported in actors v13") + +} + +func (s *state13) verifiers() (adt.Map, error) { + return adt13.AsMap(s.store, s.Verifiers, builtin13.DefaultHamtBitwidth) +} + +func (s *state13) removeDataCapProposalIDs() (adt.Map, error) { + return adt13.AsMap(s.store, s.RemoveDataCapProposalIDs, builtin13.DefaultHamtBitwidth) +} + +func (s *state13) GetState() interface{} { + return &s.State +} + +func (s *state13) GetAllocation(clientIdAddr address.Address, allocationId verifreg9.AllocationId) (*Allocation, bool, error) { + + alloc, ok, err := s.FindAllocation(s.store, clientIdAddr, verifreg13.AllocationId(allocationId)) + return (*Allocation)(alloc), ok, err +} + +func (s *state13) GetAllocations(clientIdAddr address.Address) (map[AllocationId]Allocation, error) { + + v13Map, err := s.LoadAllocationsToMap(s.store, clientIdAddr) + + retMap := make(map[AllocationId]Allocation, len(v13Map)) + for k, v := range v13Map { + retMap[AllocationId(k)] = Allocation(v) + } + + return retMap, err + +} + +func (s *state13) GetAllAllocations() (map[AllocationId]Allocation, error) { + + v13Map, err := s.State.GetAllAllocations(s.store) + + retMap := make(map[AllocationId]Allocation, len(v13Map)) + for k, v := range v13Map { + retMap[AllocationId(k)] = Allocation(v) + } + + return retMap, err + +} + +func (s *state13) GetClaim(providerIdAddr address.Address, claimId verifreg9.ClaimId) (*Claim, bool, error) { + + claim, ok, err := s.FindClaim(s.store, providerIdAddr, verifreg13.ClaimId(claimId)) + return (*Claim)(claim), ok, err + +} + +func (s *state13) GetClaims(providerIdAddr address.Address) (map[ClaimId]Claim, error) { + + v13Map, err := s.LoadClaimsToMap(s.store, providerIdAddr) + + retMap := make(map[ClaimId]Claim, len(v13Map)) + for k, v := range v13Map { + retMap[ClaimId(k)] = Claim(v) + } + + return retMap, err + +} + +func (s *state13) GetAllClaims() (map[ClaimId]Claim, error) { + + v13Map, err := s.State.GetAllClaims(s.store) + + retMap := make(map[ClaimId]Claim, len(v13Map)) + for k, v := range v13Map { + retMap[ClaimId(k)] = Claim(v) + } + + return retMap, err + +} + +func (s *state13) GetClaimIdsBySector(providerIdAddr address.Address) (map[abi.SectorNumber][]ClaimId, error) { + + v13Map, err := s.LoadClaimsToMap(s.store, providerIdAddr) + + retMap := make(map[abi.SectorNumber][]ClaimId) + for k, v := range v13Map { + claims, ok := retMap[v.Sector] + if !ok { + retMap[v.Sector] = []ClaimId{ClaimId(k)} + } else { + retMap[v.Sector] = append(claims, ClaimId(k)) + } + } + + return retMap, err + +} + +func (s *state13) ActorKey() string { + return manifest.VerifregKey +} + +func (s *state13) ActorVersion() actorstypes.Version { + return actorstypes.Version13 +} + +func (s *state13) Code() cid.Cid { + code, ok := actors.GetActorCodeID(s.ActorVersion(), s.ActorKey()) + if !ok { + panic(fmt.Errorf("didn't find actor %v code id for actor version %d", s.ActorKey(), s.ActorVersion())) + } + + return code +} diff --git a/chain/actors/builtin/verifreg/v2.go b/chain/actors/builtin/verifreg/v2.go index 31f7f775d..7f71639e6 100644 --- a/chain/actors/builtin/verifreg/v2.go +++ b/chain/actors/builtin/verifreg/v2.go @@ -106,6 +106,12 @@ func (s *state2) GetAllocations(clientIdAddr address.Address) (map[AllocationId] } +func (s *state2) GetAllAllocations() (map[AllocationId]Allocation, error) { + + return nil, xerrors.Errorf("unsupported in actors v2") + +} + func (s *state2) GetClaim(providerIdAddr address.Address, claimId verifreg9.ClaimId) (*Claim, bool, error) { return nil, false, xerrors.Errorf("unsupported in actors v2") @@ -118,6 +124,12 @@ func (s *state2) GetClaims(providerIdAddr address.Address) (map[ClaimId]Claim, e } +func (s *state2) GetAllClaims() (map[ClaimId]Claim, error) { + + return nil, xerrors.Errorf("unsupported in actors v2") + +} + func (s *state2) GetClaimIdsBySector(providerIdAddr address.Address) (map[abi.SectorNumber][]ClaimId, error) { return nil, xerrors.Errorf("unsupported in actors v2") diff --git a/chain/actors/builtin/verifreg/v3.go b/chain/actors/builtin/verifreg/v3.go index 3ea016fd5..3e8ea9a1f 100644 --- a/chain/actors/builtin/verifreg/v3.go +++ b/chain/actors/builtin/verifreg/v3.go @@ -107,6 +107,12 @@ func (s *state3) GetAllocations(clientIdAddr address.Address) (map[AllocationId] } +func (s *state3) GetAllAllocations() (map[AllocationId]Allocation, error) { + + return nil, xerrors.Errorf("unsupported in actors v3") + +} + func (s *state3) GetClaim(providerIdAddr address.Address, claimId verifreg9.ClaimId) (*Claim, bool, error) { return nil, false, xerrors.Errorf("unsupported in actors v3") @@ -119,6 +125,12 @@ func (s *state3) GetClaims(providerIdAddr address.Address) (map[ClaimId]Claim, e } +func (s *state3) GetAllClaims() (map[ClaimId]Claim, error) { + + return nil, xerrors.Errorf("unsupported in actors v3") + +} + func (s *state3) GetClaimIdsBySector(providerIdAddr address.Address) (map[abi.SectorNumber][]ClaimId, error) { return nil, xerrors.Errorf("unsupported in actors v3") diff --git a/chain/actors/builtin/verifreg/v4.go b/chain/actors/builtin/verifreg/v4.go index 464cc9fdc..1dc438864 100644 --- a/chain/actors/builtin/verifreg/v4.go +++ b/chain/actors/builtin/verifreg/v4.go @@ -107,6 +107,12 @@ func (s *state4) GetAllocations(clientIdAddr address.Address) (map[AllocationId] } +func (s *state4) GetAllAllocations() (map[AllocationId]Allocation, error) { + + return nil, xerrors.Errorf("unsupported in actors v4") + +} + func (s *state4) GetClaim(providerIdAddr address.Address, claimId verifreg9.ClaimId) (*Claim, bool, error) { return nil, false, xerrors.Errorf("unsupported in actors v4") @@ -119,6 +125,12 @@ func (s *state4) GetClaims(providerIdAddr address.Address) (map[ClaimId]Claim, e } +func (s *state4) GetAllClaims() (map[ClaimId]Claim, error) { + + return nil, xerrors.Errorf("unsupported in actors v4") + +} + func (s *state4) GetClaimIdsBySector(providerIdAddr address.Address) (map[abi.SectorNumber][]ClaimId, error) { return nil, xerrors.Errorf("unsupported in actors v4") diff --git a/chain/actors/builtin/verifreg/v5.go b/chain/actors/builtin/verifreg/v5.go index 17901dd23..a7505330c 100644 --- a/chain/actors/builtin/verifreg/v5.go +++ b/chain/actors/builtin/verifreg/v5.go @@ -107,6 +107,12 @@ func (s *state5) GetAllocations(clientIdAddr address.Address) (map[AllocationId] } +func (s *state5) GetAllAllocations() (map[AllocationId]Allocation, error) { + + return nil, xerrors.Errorf("unsupported in actors v5") + +} + func (s *state5) GetClaim(providerIdAddr address.Address, claimId verifreg9.ClaimId) (*Claim, bool, error) { return nil, false, xerrors.Errorf("unsupported in actors v5") @@ -119,6 +125,12 @@ func (s *state5) GetClaims(providerIdAddr address.Address) (map[ClaimId]Claim, e } +func (s *state5) GetAllClaims() (map[ClaimId]Claim, error) { + + return nil, xerrors.Errorf("unsupported in actors v5") + +} + func (s *state5) GetClaimIdsBySector(providerIdAddr address.Address) (map[abi.SectorNumber][]ClaimId, error) { return nil, xerrors.Errorf("unsupported in actors v5") diff --git a/chain/actors/builtin/verifreg/v6.go b/chain/actors/builtin/verifreg/v6.go index 68fac64cb..93424152e 100644 --- a/chain/actors/builtin/verifreg/v6.go +++ b/chain/actors/builtin/verifreg/v6.go @@ -107,6 +107,12 @@ func (s *state6) GetAllocations(clientIdAddr address.Address) (map[AllocationId] } +func (s *state6) GetAllAllocations() (map[AllocationId]Allocation, error) { + + return nil, xerrors.Errorf("unsupported in actors v6") + +} + func (s *state6) GetClaim(providerIdAddr address.Address, claimId verifreg9.ClaimId) (*Claim, bool, error) { return nil, false, xerrors.Errorf("unsupported in actors v6") @@ -119,6 +125,12 @@ func (s *state6) GetClaims(providerIdAddr address.Address) (map[ClaimId]Claim, e } +func (s *state6) GetAllClaims() (map[ClaimId]Claim, error) { + + return nil, xerrors.Errorf("unsupported in actors v6") + +} + func (s *state6) GetClaimIdsBySector(providerIdAddr address.Address) (map[abi.SectorNumber][]ClaimId, error) { return nil, xerrors.Errorf("unsupported in actors v6") diff --git a/chain/actors/builtin/verifreg/v7.go b/chain/actors/builtin/verifreg/v7.go index e8f3ac739..bd67aee5f 100644 --- a/chain/actors/builtin/verifreg/v7.go +++ b/chain/actors/builtin/verifreg/v7.go @@ -106,6 +106,12 @@ func (s *state7) GetAllocations(clientIdAddr address.Address) (map[AllocationId] } +func (s *state7) GetAllAllocations() (map[AllocationId]Allocation, error) { + + return nil, xerrors.Errorf("unsupported in actors v7") + +} + func (s *state7) GetClaim(providerIdAddr address.Address, claimId verifreg9.ClaimId) (*Claim, bool, error) { return nil, false, xerrors.Errorf("unsupported in actors v7") @@ -118,6 +124,12 @@ func (s *state7) GetClaims(providerIdAddr address.Address) (map[ClaimId]Claim, e } +func (s *state7) GetAllClaims() (map[ClaimId]Claim, error) { + + return nil, xerrors.Errorf("unsupported in actors v7") + +} + func (s *state7) GetClaimIdsBySector(providerIdAddr address.Address) (map[abi.SectorNumber][]ClaimId, error) { return nil, xerrors.Errorf("unsupported in actors v7") diff --git a/chain/actors/builtin/verifreg/v8.go b/chain/actors/builtin/verifreg/v8.go index 89393c4d9..1515c1c5b 100644 --- a/chain/actors/builtin/verifreg/v8.go +++ b/chain/actors/builtin/verifreg/v8.go @@ -106,6 +106,12 @@ func (s *state8) GetAllocations(clientIdAddr address.Address) (map[AllocationId] } +func (s *state8) GetAllAllocations() (map[AllocationId]Allocation, error) { + + return nil, xerrors.Errorf("unsupported in actors v8") + +} + func (s *state8) GetClaim(providerIdAddr address.Address, claimId verifreg9.ClaimId) (*Claim, bool, error) { return nil, false, xerrors.Errorf("unsupported in actors v8") @@ -118,6 +124,12 @@ func (s *state8) GetClaims(providerIdAddr address.Address) (map[ClaimId]Claim, e } +func (s *state8) GetAllClaims() (map[ClaimId]Claim, error) { + + return nil, xerrors.Errorf("unsupported in actors v8") + +} + func (s *state8) GetClaimIdsBySector(providerIdAddr address.Address) (map[abi.SectorNumber][]ClaimId, error) { return nil, xerrors.Errorf("unsupported in actors v8") diff --git a/chain/actors/builtin/verifreg/v9.go b/chain/actors/builtin/verifreg/v9.go index ce63c7f94..41422615b 100644 --- a/chain/actors/builtin/verifreg/v9.go +++ b/chain/actors/builtin/verifreg/v9.go @@ -113,6 +113,19 @@ func (s *state9) GetAllocations(clientIdAddr address.Address) (map[AllocationId] } +func (s *state9) GetAllAllocations() (map[AllocationId]Allocation, error) { + + v9Map, err := s.State.GetAllAllocations(s.store) + + retMap := make(map[AllocationId]Allocation, len(v9Map)) + for k, v := range v9Map { + retMap[AllocationId(k)] = Allocation(v) + } + + return retMap, err + +} + func (s *state9) GetClaim(providerIdAddr address.Address, claimId verifreg9.ClaimId) (*Claim, bool, error) { claim, ok, err := s.FindClaim(s.store, providerIdAddr, verifreg9.ClaimId(claimId)) @@ -133,6 +146,19 @@ func (s *state9) GetClaims(providerIdAddr address.Address) (map[ClaimId]Claim, e } +func (s *state9) GetAllClaims() (map[ClaimId]Claim, error) { + + v9Map, err := s.State.GetAllClaims(s.store) + + retMap := make(map[ClaimId]Claim, len(v9Map)) + for k, v := range v9Map { + retMap[ClaimId(k)] = Claim(v) + } + + return retMap, err + +} + func (s *state9) GetClaimIdsBySector(providerIdAddr address.Address) (map[abi.SectorNumber][]ClaimId, error) { v9Map, err := s.LoadClaimsToMap(s.store, providerIdAddr) diff --git a/chain/actors/builtin/verifreg/verifreg.go b/chain/actors/builtin/verifreg/verifreg.go index de906f521..2d66d9028 100644 --- a/chain/actors/builtin/verifreg/verifreg.go +++ b/chain/actors/builtin/verifreg/verifreg.go @@ -7,7 +7,7 @@ import ( "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/abi" actorstypes "github.com/filecoin-project/go-state-types/actors" - builtin12 "github.com/filecoin-project/go-state-types/builtin" + builtin13 "github.com/filecoin-project/go-state-types/builtin" verifregtypes "github.com/filecoin-project/go-state-types/builtin/v9/verifreg" "github.com/filecoin-project/go-state-types/cbor" "github.com/filecoin-project/go-state-types/manifest" @@ -25,8 +25,8 @@ import ( ) var ( - Address = builtin12.VerifiedRegistryActorAddr - Methods = builtin12.MethodsVerifiedRegistry + Address = builtin13.VerifiedRegistryActorAddr + Methods = builtin13.MethodsVerifiedRegistry ) func Load(store adt.Store, act *types.Actor) (State, error) { @@ -52,6 +52,9 @@ func Load(store adt.Store, act *types.Actor) (State, error) { case actorstypes.Version12: return load12(store, act.Head) + case actorstypes.Version13: + return load13(store, act.Head) + } } @@ -122,6 +125,9 @@ func MakeState(store adt.Store, av actorstypes.Version, rootKeyAddress address.A case actorstypes.Version12: return make12(store, rootKeyAddress) + case actorstypes.Version13: + return make13(store, rootKeyAddress) + } return nil, xerrors.Errorf("unknown actor version %d", av) } @@ -141,8 +147,10 @@ type State interface { ForEachClient(func(addr address.Address, dcap abi.StoragePower) error) error GetAllocation(clientIdAddr address.Address, allocationId AllocationId) (*Allocation, bool, error) GetAllocations(clientIdAddr address.Address) (map[AllocationId]Allocation, error) + GetAllAllocations() (map[AllocationId]Allocation, error) GetClaim(providerIdAddr address.Address, claimId ClaimId) (*Claim, bool, error) GetClaims(providerIdAddr address.Address) (map[ClaimId]Claim, error) + GetAllClaims() (map[ClaimId]Claim, error) GetClaimIdsBySector(providerIdAddr address.Address) (map[abi.SectorNumber][]ClaimId, error) GetState() interface{} } @@ -161,6 +169,7 @@ func AllCodes() []cid.Cid { (&state10{}).Code(), (&state11{}).Code(), (&state12{}).Code(), + (&state13{}).Code(), } } diff --git a/chain/actors/policy/policy.go b/chain/actors/policy/policy.go index 6d2b41154..b8d23903c 100644 --- a/chain/actors/policy/policy.go +++ b/chain/actors/policy/policy.go @@ -9,6 +9,7 @@ import ( builtin10 "github.com/filecoin-project/go-state-types/builtin" builtin11 "github.com/filecoin-project/go-state-types/builtin" builtin12 "github.com/filecoin-project/go-state-types/builtin" + builtin13 "github.com/filecoin-project/go-state-types/builtin" builtin8 "github.com/filecoin-project/go-state-types/builtin" builtin9 "github.com/filecoin-project/go-state-types/builtin" market10 "github.com/filecoin-project/go-state-types/builtin/v10/market" @@ -19,8 +20,11 @@ import ( verifreg11 "github.com/filecoin-project/go-state-types/builtin/v11/verifreg" market12 "github.com/filecoin-project/go-state-types/builtin/v12/market" miner12 "github.com/filecoin-project/go-state-types/builtin/v12/miner" - paych12 "github.com/filecoin-project/go-state-types/builtin/v12/paych" verifreg12 "github.com/filecoin-project/go-state-types/builtin/v12/verifreg" + market13 "github.com/filecoin-project/go-state-types/builtin/v13/market" + miner13 "github.com/filecoin-project/go-state-types/builtin/v13/miner" + paych13 "github.com/filecoin-project/go-state-types/builtin/v13/paych" + verifreg13 "github.com/filecoin-project/go-state-types/builtin/v13/verifreg" market8 "github.com/filecoin-project/go-state-types/builtin/v8/market" miner8 "github.com/filecoin-project/go-state-types/builtin/v8/miner" verifreg8 "github.com/filecoin-project/go-state-types/builtin/v8/verifreg" @@ -59,14 +63,14 @@ import ( ) const ( - ChainFinality = miner12.ChainFinality + ChainFinality = miner13.ChainFinality SealRandomnessLookback = ChainFinality - PaychSettleDelay = paych12.SettleDelay - MaxPreCommitRandomnessLookback = builtin12.EpochsInDay + SealRandomnessLookback + PaychSettleDelay = paych13.SettleDelay + MaxPreCommitRandomnessLookback = builtin13.EpochsInDay + SealRandomnessLookback ) var ( - MarketDefaultAllocationTermBuffer = market12.MarketDefaultAllocationTermBuffer + MarketDefaultAllocationTermBuffer = market13.MarketDefaultAllocationTermBuffer ) // SetSupportedProofTypes sets supported proof types, across all actor versions. @@ -181,11 +185,13 @@ func SetPreCommitChallengeDelay(delay abi.ChainEpoch) { miner12.PreCommitChallengeDelay = delay + miner13.PreCommitChallengeDelay = delay + } // TODO: this function shouldn't really exist. Instead, the API should expose the precommit delay. func GetPreCommitChallengeDelay() abi.ChainEpoch { - return miner12.PreCommitChallengeDelay + return miner13.PreCommitChallengeDelay } // SetConsensusMinerMinPower sets the minimum power of an individual miner must @@ -239,6 +245,10 @@ func SetConsensusMinerMinPower(p abi.StoragePower) { policy.ConsensusMinerMinPower = p } + for _, policy := range builtin13.PoStProofPolicies { + policy.ConsensusMinerMinPower = p + } + } // SetMinVerifiedDealSize sets the minimum size of a verified deal. This should @@ -269,6 +279,8 @@ func SetMinVerifiedDealSize(size abi.StoragePower) { verifreg12.MinVerifiedDealSize = size + verifreg13.MinVerifiedDealSize = size + } func GetMaxProveCommitDuration(ver actorstypes.Version, t abi.RegisteredSealProof) (abi.ChainEpoch, error) { @@ -322,6 +334,10 @@ func GetMaxProveCommitDuration(ver actorstypes.Version, t abi.RegisteredSealProo return miner12.MaxProveCommitDuration[t], nil + case actorstypes.Version13: + + return miner13.MaxProveCommitDuration[t], nil + default: return 0, xerrors.Errorf("unsupported actors version") } @@ -387,6 +403,11 @@ func SetProviderCollateralSupplyTarget(num, denom big.Int) { Denominator: denom, } + market13.ProviderCollateralSupplyTarget = builtin13.BigFrac{ + Numerator: num, + Denominator: denom, + } + } func DealProviderCollateralBounds( @@ -460,13 +481,18 @@ func DealProviderCollateralBounds( min, max := market12.DealProviderCollateralBounds(size, verified, rawBytePower, qaPower, baselinePower, circulatingFil) return min, max, nil + case actorstypes.Version13: + + min, max := market13.DealProviderCollateralBounds(size, verified, rawBytePower, qaPower, baselinePower, circulatingFil) + return min, max, nil + default: return big.Zero(), big.Zero(), xerrors.Errorf("unsupported actors version") } } func DealDurationBounds(pieceSize abi.PaddedPieceSize) (min, max abi.ChainEpoch) { - return market12.DealDurationBounds(pieceSize) + return market13.DealDurationBounds(pieceSize) } // Sets the challenge window and scales the proving period to match (such that @@ -549,6 +575,13 @@ func SetWPoStChallengeWindow(period abi.ChainEpoch) { // scale it if we're scaling the challenge period. miner12.WPoStDisputeWindow = period * 30 + miner13.WPoStChallengeWindow = period + miner13.WPoStProvingPeriod = period * abi.ChainEpoch(miner13.WPoStPeriodDeadlines) + + // by default, this is 2x finality which is 30 periods. + // scale it if we're scaling the challenge period. + miner13.WPoStDisputeWindow = period * 30 + } func GetWinningPoStSectorSetLookback(nwVer network.Version) abi.ChainEpoch { @@ -603,6 +636,9 @@ func GetMaxSectorExpirationExtension(nv network.Version) (abi.ChainEpoch, error) case actorstypes.Version12: return miner12.MaxSectorExpirationExtension, nil + case actorstypes.Version13: + return miner13.MaxSectorExpirationExtension, nil + default: return 0, xerrors.Errorf("unsupported network version") } @@ -610,11 +646,11 @@ func GetMaxSectorExpirationExtension(nv network.Version) (abi.ChainEpoch, error) } func GetMinSectorExpiration() abi.ChainEpoch { - return miner12.MinSectorExpiration + return miner13.MinSectorExpiration } func GetMaxPoStPartitions(nv network.Version, p abi.RegisteredPoStProof) (int, error) { - sectorsPerPart, err := builtin12.PoStProofWindowPoStPartitionSectors(p) + sectorsPerPart, err := builtin13.PoStProofWindowPoStPartitionSectors(p) if err != nil { return 0, err } @@ -623,7 +659,7 @@ func GetMaxPoStPartitions(nv network.Version, p abi.RegisteredPoStProof) (int, e return 0, err } - return min(miner12.PoStedPartitionsMax, int(uint64(maxSectors)/sectorsPerPart)), nil + return min(miner13.PoStedPartitionsMax, int(uint64(maxSectors)/sectorsPerPart)), nil } func GetDefaultAggregationProof() abi.RegisteredAggregationProof { @@ -635,7 +671,7 @@ func GetSectorMaxLifetime(proof abi.RegisteredSealProof, nwVer network.Version) return builtin4.SealProofPoliciesV0[proof].SectorMaxLifetime } - return builtin12.SealProofPoliciesV11[proof].SectorMaxLifetime + return builtin13.SealProofPoliciesV11[proof].SectorMaxLifetime } func GetAddressedSectorsMax(nwVer network.Version) (int, error) { @@ -681,6 +717,9 @@ func GetAddressedSectorsMax(nwVer network.Version) (int, error) { case actorstypes.Version12: return miner12.AddressedSectorsMax, nil + case actorstypes.Version13: + return miner13.AddressedSectorsMax, nil + default: return 0, xerrors.Errorf("unsupported network version") } @@ -742,6 +781,10 @@ func GetDeclarationsMax(nwVer network.Version) (int, error) { return miner12.DeclarationsMax, nil + case actorstypes.Version13: + + return miner13.DeclarationsMax, nil + default: return 0, xerrors.Errorf("unsupported network version") } @@ -802,6 +845,10 @@ func AggregateProveCommitNetworkFee(nwVer network.Version, aggregateSize int, ba return miner12.AggregateProveCommitNetworkFee(aggregateSize, baseFee), nil + case actorstypes.Version13: + + return miner13.AggregateProveCommitNetworkFee(aggregateSize, baseFee), nil + default: return big.Zero(), xerrors.Errorf("unsupported network version") } @@ -862,6 +909,10 @@ func AggregatePreCommitNetworkFee(nwVer network.Version, aggregateSize int, base return miner12.AggregatePreCommitNetworkFee(aggregateSize, baseFee), nil + case actorstypes.Version13: + + return miner13.AggregatePreCommitNetworkFee(aggregateSize, baseFee), nil + default: return big.Zero(), xerrors.Errorf("unsupported network version") } diff --git a/chain/actors/version.go b/chain/actors/version.go index 92c0da006..8d84bbc1d 100644 --- a/chain/actors/version.go +++ b/chain/actors/version.go @@ -14,9 +14,9 @@ const ({{range .actorVersions}} /* inline-gen start */ -var LatestVersion = 12 +var LatestVersion = 13 -var Versions = []int{0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12} +var Versions = []int{0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13} const ( Version0 Version = 0 @@ -31,6 +31,7 @@ const ( Version10 Version = 10 Version11 Version = 11 Version12 Version = 12 + Version13 Version = 13 ) /* inline-gen end */ diff --git a/chain/beacon/beacon.go b/chain/beacon/beacon.go index ff75b8579..45baa6624 100644 --- a/chain/beacon/beacon.go +++ b/chain/beacon/beacon.go @@ -43,31 +43,31 @@ type BeaconPoint struct { // been posted on chain. type RandomBeacon interface { Entry(context.Context, uint64) <-chan Response - VerifyEntry(types.BeaconEntry, types.BeaconEntry) error + VerifyEntry(entry types.BeaconEntry, prevEntrySig []byte) error MaxBeaconRoundForEpoch(network.Version, abi.ChainEpoch) uint64 + IsChained() bool } func ValidateBlockValues(bSchedule Schedule, nv network.Version, h *types.BlockHeader, parentEpoch abi.ChainEpoch, prevEntry types.BeaconEntry) error { - { - parentBeacon := bSchedule.BeaconForEpoch(parentEpoch) - currBeacon := bSchedule.BeaconForEpoch(h.Height) - if parentBeacon != currBeacon { - if len(h.BeaconEntries) != 2 { - return xerrors.Errorf("expected two beacon entries at beacon fork, got %d", len(h.BeaconEntries)) - } - err := currBeacon.VerifyEntry(h.BeaconEntries[1], h.BeaconEntries[0]) - if err != nil { - return xerrors.Errorf("beacon at fork point invalid: (%v, %v): %w", - h.BeaconEntries[1], h.BeaconEntries[0], err) - } - return nil + + parentBeacon := bSchedule.BeaconForEpoch(parentEpoch) + currBeacon := bSchedule.BeaconForEpoch(h.Height) + // When we have "chained" beacons, two entries at a fork are required. + if parentBeacon != currBeacon && currBeacon.IsChained() { + if len(h.BeaconEntries) != 2 { + return xerrors.Errorf("expected two beacon entries at beacon fork, got %d", len(h.BeaconEntries)) } + err := currBeacon.VerifyEntry(h.BeaconEntries[1], h.BeaconEntries[0].Data) + if err != nil { + return xerrors.Errorf("beacon at fork point invalid: (%v, %v): %w", + h.BeaconEntries[1], h.BeaconEntries[0], err) + } + return nil } - // TODO: fork logic - b := bSchedule.BeaconForEpoch(h.Height) - maxRound := b.MaxBeaconRoundForEpoch(nv, h.Height) + maxRound := currBeacon.MaxBeaconRoundForEpoch(nv, h.Height) + // We don't expect to ever actually meet this condition if maxRound == prevEntry.Round { if len(h.BeaconEntries) != 0 { return xerrors.Errorf("expected not to have any beacon entries in this block, got %d", len(h.BeaconEntries)) @@ -79,23 +79,31 @@ func ValidateBlockValues(bSchedule Schedule, nv network.Version, h *types.BlockH return xerrors.Errorf("expected to have beacon entries in this block, but didn't find any") } - // Verify that the last beacon entry's round corresponds to the round we expect + // We skip verifying the genesis entry when randomness is "chained". + if currBeacon.IsChained() && prevEntry.Round == 0 { + return nil + } + last := h.BeaconEntries[len(h.BeaconEntries)-1] if last.Round != maxRound { return xerrors.Errorf("expected final beacon entry in block to be at round %d, got %d", maxRound, last.Round) } - // Verify that all other entries' rounds are as expected for the epochs in between parentEpoch and h.Height - for i, e := range h.BeaconEntries { - correctRound := b.MaxBeaconRoundForEpoch(nv, parentEpoch+abi.ChainEpoch(i)+1) - if e.Round != correctRound { - return xerrors.Errorf("unexpected beacon round %d, expected %d for epoch %d", e.Round, correctRound, parentEpoch+abi.ChainEpoch(i)) + // If the beacon is UNchained, verify that the block only includes the rounds we want for the epochs in between parentEpoch and h.Height + // For chained beacons, you must have all the rounds forming a valid chain with prevEntry, so we can skip this step + if !currBeacon.IsChained() { + // Verify that all other entries' rounds are as expected for the epochs in between parentEpoch and h.Height + for i, e := range h.BeaconEntries { + correctRound := currBeacon.MaxBeaconRoundForEpoch(nv, parentEpoch+abi.ChainEpoch(i)+1) + if e.Round != correctRound { + return xerrors.Errorf("unexpected beacon round %d, expected %d for epoch %d", e.Round, correctRound, parentEpoch+abi.ChainEpoch(i)) + } } } // Verify the beacon entries themselves for i, e := range h.BeaconEntries { - if err := b.VerifyEntry(e, prevEntry); err != nil { + if err := currBeacon.VerifyEntry(e, prevEntry.Data); err != nil { return xerrors.Errorf("beacon entry %d (%d - %x (%d)) was invalid: %w", i, e.Round, e.Data, len(e.Data), err) } prevEntry = e @@ -105,34 +113,32 @@ func ValidateBlockValues(bSchedule Schedule, nv network.Version, h *types.BlockH } func BeaconEntriesForBlock(ctx context.Context, bSchedule Schedule, nv network.Version, epoch abi.ChainEpoch, parentEpoch abi.ChainEpoch, prev types.BeaconEntry) ([]types.BeaconEntry, error) { - { - parentBeacon := bSchedule.BeaconForEpoch(parentEpoch) - currBeacon := bSchedule.BeaconForEpoch(epoch) - if parentBeacon != currBeacon { - // Fork logic - round := currBeacon.MaxBeaconRoundForEpoch(nv, epoch) - out := make([]types.BeaconEntry, 2) - rch := currBeacon.Entry(ctx, round-1) - res := <-rch - if res.Err != nil { - return nil, xerrors.Errorf("getting entry %d returned error: %w", round-1, res.Err) - } - out[0] = res.Entry - rch = currBeacon.Entry(ctx, round) - res = <-rch - if res.Err != nil { - return nil, xerrors.Errorf("getting entry %d returned error: %w", round, res.Err) - } - out[1] = res.Entry - return out, nil + // When we have "chained" beacons, two entries at a fork are required. + parentBeacon := bSchedule.BeaconForEpoch(parentEpoch) + currBeacon := bSchedule.BeaconForEpoch(epoch) + if parentBeacon != currBeacon && currBeacon.IsChained() { + // Fork logic + round := currBeacon.MaxBeaconRoundForEpoch(nv, epoch) + out := make([]types.BeaconEntry, 2) + rch := currBeacon.Entry(ctx, round-1) + res := <-rch + if res.Err != nil { + return nil, xerrors.Errorf("getting entry %d returned error: %w", round-1, res.Err) } + out[0] = res.Entry + rch = currBeacon.Entry(ctx, round) + res = <-rch + if res.Err != nil { + return nil, xerrors.Errorf("getting entry %d returned error: %w", round, res.Err) + } + out[1] = res.Entry + return out, nil } - beacon := bSchedule.BeaconForEpoch(epoch) - start := build.Clock.Now() - maxRound := beacon.MaxBeaconRoundForEpoch(nv, epoch) + maxRound := currBeacon.MaxBeaconRoundForEpoch(nv, epoch) + // We don't expect this to ever be the case if maxRound == prev.Round { return nil, nil } @@ -144,8 +150,8 @@ func BeaconEntriesForBlock(ctx context.Context, bSchedule Schedule, nv network.V var out []types.BeaconEntry for currEpoch := epoch; currEpoch > parentEpoch; currEpoch-- { - currRound := beacon.MaxBeaconRoundForEpoch(nv, currEpoch) - rch := beacon.Entry(ctx, currRound) + currRound := currBeacon.MaxBeaconRoundForEpoch(nv, currEpoch) + rch := currBeacon.Entry(ctx, currRound) select { case resp := <-rch: if resp.Err != nil { diff --git a/chain/beacon/drand/drand.go b/chain/beacon/drand/drand.go index 1b78daadd..852a1a31c 100644 --- a/chain/beacon/drand/drand.go +++ b/chain/beacon/drand/drand.go @@ -8,7 +8,7 @@ import ( dchain "github.com/drand/drand/chain" dclient "github.com/drand/drand/client" hclient "github.com/drand/drand/client/http" - "github.com/drand/drand/common/scheme" + dcrypto "github.com/drand/drand/crypto" dlog "github.com/drand/drand/log" gclient "github.com/drand/drand/lp2p/client" "github.com/drand/kyber" @@ -37,7 +37,8 @@ var log = logging.Logger("drand") // // The root trust for the Drand chain is configured from build.DrandChain. type DrandBeacon struct { - client dclient.Client + isChained bool + client dclient.Client pubkey kyber.Point @@ -47,10 +48,18 @@ type DrandBeacon struct { drandGenTime uint64 filGenTime uint64 filRoundTime uint64 + scheme *dcrypto.Scheme localCache *lru.Cache[uint64, *types.BeaconEntry] } +// IsChained tells us whether this particular beacon operates in "chained mode". Prior to Drand +// quicknet, beacons form a chain. After the introduction of quicknet, they do not, so we need to +// change how we interact with beacon entries. (See FIP-0063) +func (db *DrandBeacon) IsChained() bool { + return db.isChained +} + // DrandHTTPClient interface overrides the user agent used by drand type DrandHTTPClient interface { SetUserAgent(string) @@ -68,6 +77,10 @@ func (l *logger) Named(s string) dlog.Logger { return &logger{l.SugaredLogger.Named(s)} } +func (l *logger) AddCallerSkip(skip int) dlog.Logger { + return &logger{l.SugaredLogger.With(zap.AddCallerSkip(skip))} +} + func NewDrandBeacon(genesisTs, interval uint64, ps *pubsub.PubSub, config dtypes.DrandConfig) (*DrandBeacon, error) { if genesisTs == 0 { panic("what are you doing this cant be zero") @@ -112,10 +125,16 @@ func NewDrandBeacon(genesisTs, interval uint64, ps *pubsub.PubSub, config dtypes } db := &DrandBeacon{ + isChained: config.IsChained, client: client, localCache: lc, } + sch, err := dcrypto.GetSchemeByIDWithDefault(drandChain.Scheme) + if err != nil { + return nil, err + } + db.scheme = sch db.pubkey = drandChain.PublicKey db.interval = drandChain.Period db.drandGenTime = uint64(drandChain.GenesisTime) @@ -164,29 +183,26 @@ func (db *DrandBeacon) getCachedValue(round uint64) *types.BeaconEntry { return v } -func (db *DrandBeacon) VerifyEntry(curr types.BeaconEntry, prev types.BeaconEntry) error { - if prev.Round == 0 { - // TODO handle genesis better - return nil - } - - if be := db.getCachedValue(curr.Round); be != nil { - if !bytes.Equal(curr.Data, be.Data) { +func (db *DrandBeacon) VerifyEntry(entry types.BeaconEntry, prevEntrySig []byte) error { + if be := db.getCachedValue(entry.Round); be != nil { + if !bytes.Equal(entry.Data, be.Data) { return xerrors.New("invalid beacon value, does not match cached good value") } // return no error if the value is in the cache already return nil } b := &dchain.Beacon{ - PreviousSig: prev.Data, - Round: curr.Round, - Signature: curr.Data, - } - err := dchain.NewVerifier(scheme.GetSchemeFromEnv()).VerifyBeacon(*b, db.pubkey) - if err == nil { - db.cacheValue(curr) + PreviousSig: prevEntrySig, + Round: entry.Round, + Signature: entry.Data, } + err := db.scheme.VerifyBeacon(b, db.pubkey) + if err != nil { + return xerrors.Errorf("failed to verify beacon: %w", err) + } + + db.cacheValue(entry) return nil } diff --git a/chain/beacon/drand/drand_test.go b/chain/beacon/drand/drand_test.go index 355d6937f..c35c0da18 100644 --- a/chain/beacon/drand/drand_test.go +++ b/chain/beacon/drand/drand_test.go @@ -39,7 +39,15 @@ func TestMaxBeaconRoundForEpoch(t *testing.T) { todayTs := uint64(1652222222) db, err := NewDrandBeacon(todayTs, build.BlockDelaySecs, nil, build.DrandConfigs[build.DrandTestnet]) assert.NoError(t, err) + assert.True(t, db.IsChained()) mbr15 := db.MaxBeaconRoundForEpoch(network.Version15, 100) mbr16 := db.MaxBeaconRoundForEpoch(network.Version16, 100) assert.Equal(t, mbr15+1, mbr16) } + +func TestQuicknetIsChained(t *testing.T) { + todayTs := uint64(1652222222) + db, err := NewDrandBeacon(todayTs, build.BlockDelaySecs, nil, build.DrandConfigs[build.DrandQuicknet]) + assert.NoError(t, err) + assert.False(t, db.IsChained()) +} diff --git a/chain/beacon/mock.go b/chain/beacon/mock.go index 3f26da109..ab6a98ebf 100644 --- a/chain/beacon/mock.go +++ b/chain/beacon/mock.go @@ -20,6 +20,10 @@ type mockBeacon struct { interval time.Duration } +func (mb *mockBeacon) IsChained() bool { + return true +} + func NewMockBeacon(interval time.Duration) RandomBeacon { mb := &mockBeacon{interval: interval} @@ -47,7 +51,7 @@ func (mb *mockBeacon) Entry(ctx context.Context, index uint64) <-chan Response { return out } -func (mb *mockBeacon) VerifyEntry(from types.BeaconEntry, to types.BeaconEntry) error { +func (mb *mockBeacon) VerifyEntry(from types.BeaconEntry, _prevEntrySig []byte) error { // TODO: cache this, especially for bls oe := mb.entryForIndex(from.Round) if !bytes.Equal(from.Data, oe.Data) { diff --git a/chain/consensus/compute_state.go b/chain/consensus/compute_state.go index 4b993b3e7..78369ec20 100644 --- a/chain/consensus/compute_state.go +++ b/chain/consensus/compute_state.go @@ -53,6 +53,7 @@ func NewActorRegistry() *vm.ActorRegistry { inv.Register(actorstypes.Version10, vm.ActorsVersionPredicate(actorstypes.Version10), builtin.MakeRegistry(actorstypes.Version10)) inv.Register(actorstypes.Version11, vm.ActorsVersionPredicate(actorstypes.Version11), builtin.MakeRegistry(actorstypes.Version11)) inv.Register(actorstypes.Version12, vm.ActorsVersionPredicate(actorstypes.Version12), builtin.MakeRegistry(actorstypes.Version12)) + inv.Register(actorstypes.Version13, vm.ActorsVersionPredicate(actorstypes.Version13), builtin.MakeRegistry(actorstypes.Version13)) return inv } diff --git a/chain/consensus/filcns/filecoin.go b/chain/consensus/filcns/filecoin.go index a6883a41a..8565f3613 100644 --- a/chain/consensus/filcns/filecoin.go +++ b/chain/consensus/filcns/filecoin.go @@ -129,6 +129,7 @@ func (filec *FilecoinEC) ValidateBlock(ctx context.Context, b *types.FullBlock) return xerrors.Errorf("failed to get lookback tipset for block: %w", err) } + // TODO: Optimization: See https://github.com/filecoin-project/lotus/issues/11597 prevBeacon, err := filec.store.GetLatestBeaconEntry(ctx, baseTs) if err != nil { return xerrors.Errorf("failed to get latest beacon entry: %w", err) diff --git a/chain/consensus/filcns/upgrades.go b/chain/consensus/filcns/upgrades.go index f411c40b9..6478bf1d4 100644 --- a/chain/consensus/filcns/upgrades.go +++ b/chain/consensus/filcns/upgrades.go @@ -24,6 +24,7 @@ import ( nv19 "github.com/filecoin-project/go-state-types/builtin/v11/migration" system11 "github.com/filecoin-project/go-state-types/builtin/v11/system" nv21 "github.com/filecoin-project/go-state-types/builtin/v12/migration" + nv22 "github.com/filecoin-project/go-state-types/builtin/v13/migration" nv17 "github.com/filecoin-project/go-state-types/builtin/v9/migration" "github.com/filecoin-project/go-state-types/manifest" "github.com/filecoin-project/go-state-types/migration" @@ -280,12 +281,22 @@ func DefaultUpgradeSchedule() stmgr.UpgradeSchedule { Height: build.UpgradeWatermelonFixHeight, Network: network.Version21, Migration: buildUpgradeActorsV12MinerFix(calibnetv12BuggyMinerCID1, calibnetv12BuggyManifestCID2), + }, { + Height: build.UpgradeWatermelonFix2Height, + Network: network.Version21, + Migration: buildUpgradeActorsV12MinerFix(calibnetv12BuggyMinerCID2, calibnetv12CorrectManifestCID1), + }, { + Height: build.UpgradeDragonHeight, + Network: network.Version22, + Migration: UpgradeActorsV13, + PreMigrations: []stmgr.PreMigration{{ + PreMigration: PreUpgradeActorsV13, + StartWithin: 120, + DontStartWithin: 15, + StopWithin: 10, + }}, + Expensive: true, }, - { - Height: build.UpgradeWatermelonFix2Height, - Network: network.Version21, - Migration: buildUpgradeActorsV12MinerFix(calibnetv12BuggyMinerCID2, calibnetv12CorrectManifestCID1), - }, } for _, u := range updates { @@ -2146,6 +2157,110 @@ func buildUpgradeActorsV12MinerFix(oldBuggyMinerCID, newManifestCID cid.Cid) fun } } +func PreUpgradeActorsV13(ctx context.Context, sm *stmgr.StateManager, cache stmgr.MigrationCache, root cid.Cid, epoch abi.ChainEpoch, ts *types.TipSet) error { + // Use half the CPUs for pre-migration, but leave at least 3. + workerCount := MigrationMaxWorkerCount + if workerCount <= 4 { + workerCount = 1 + } else { + workerCount /= 2 + } + + lbts, lbRoot, err := stmgr.GetLookbackTipSetForRound(ctx, sm, ts, epoch) + if err != nil { + return xerrors.Errorf("error getting lookback ts for premigration: %w", err) + } + + config := migration.Config{ + MaxWorkers: uint(workerCount), + ProgressLogPeriod: time.Minute * 5, + UpgradeEpoch: build.UpgradeDragonHeight, + } + + _, err = upgradeActorsV13Common(ctx, sm, cache, lbRoot, epoch, lbts, config) + return err +} + +func UpgradeActorsV13(ctx context.Context, sm *stmgr.StateManager, cache stmgr.MigrationCache, cb stmgr.ExecMonitor, + root cid.Cid, epoch abi.ChainEpoch, ts *types.TipSet) (cid.Cid, error) { + // Use all the CPUs except 2. + workerCount := MigrationMaxWorkerCount - 3 + if workerCount <= 0 { + workerCount = 1 + } + config := migration.Config{ + MaxWorkers: uint(workerCount), + JobQueueSize: 1000, + ResultQueueSize: 100, + ProgressLogPeriod: 10 * time.Second, + UpgradeEpoch: build.UpgradeDragonHeight, + } + newRoot, err := upgradeActorsV13Common(ctx, sm, cache, root, epoch, ts, config) + if err != nil { + return cid.Undef, xerrors.Errorf("migrating actors v11 state: %w", err) + } + return newRoot, nil +} + +func upgradeActorsV13Common( + ctx context.Context, sm *stmgr.StateManager, cache stmgr.MigrationCache, + root cid.Cid, epoch abi.ChainEpoch, ts *types.TipSet, + config migration.Config, +) (cid.Cid, error) { + writeStore := blockstore.NewAutobatch(ctx, sm.ChainStore().StateBlockstore(), units.GiB/4) + adtStore := store.ActorStore(ctx, writeStore) + // ensure that the manifest is loaded in the blockstore + if err := bundle.LoadBundles(ctx, writeStore, actorstypes.Version13); err != nil { + return cid.Undef, xerrors.Errorf("failed to load manifest bundle: %w", err) + } + + // Load the state root. + var stateRoot types.StateRoot + if err := adtStore.Get(ctx, root, &stateRoot); err != nil { + return cid.Undef, xerrors.Errorf("failed to decode state root: %w", err) + } + + if stateRoot.Version != types.StateTreeVersion5 { + return cid.Undef, xerrors.Errorf( + "expected state root version 5 for actors v13 upgrade, got %d", + stateRoot.Version, + ) + } + + manifest, ok := actors.GetManifest(actorstypes.Version13) + if !ok { + return cid.Undef, xerrors.Errorf("no manifest CID for v13 upgrade") + } + + // Perform the migration + newHamtRoot, err := nv22.MigrateStateTree(ctx, adtStore, manifest, stateRoot.Actors, epoch, config, + migrationLogger{}, cache) + if err != nil { + return cid.Undef, xerrors.Errorf("upgrading to actors v11: %w", err) + } + + // Persist the result. + newRoot, err := adtStore.Put(ctx, &types.StateRoot{ + Version: types.StateTreeVersion5, + Actors: newHamtRoot, + Info: stateRoot.Info, + }) + if err != nil { + return cid.Undef, xerrors.Errorf("failed to persist new state root: %w", err) + } + + // Persists the new tree and shuts down the flush worker + if err := writeStore.Flush(ctx); err != nil { + return cid.Undef, xerrors.Errorf("writeStore flush failed: %w", err) + } + + if err := writeStore.Shutdown(ctx); err != nil { + return cid.Undef, xerrors.Errorf("writeStore shutdown failed: %w", err) + } + + return newRoot, nil +} + //////////////////// // Example upgrade function if upgrade requires only code changes diff --git a/chain/events/cache.go b/chain/events/cache.go index 2eba1f085..67c59cb50 100644 --- a/chain/events/cache.go +++ b/chain/events/cache.go @@ -26,7 +26,7 @@ type cache struct { uncachedAPI } -func newCache(api EventAPI, gcConfidence abi.ChainEpoch) *cache { +func newCache(api EventHelperAPI, gcConfidence abi.ChainEpoch) *cache { return &cache{ newTSCache(api, gcConfidence), newMessageCache(api), diff --git a/chain/events/events.go b/chain/events/events.go index c68b62a64..a9da32cbb 100644 --- a/chain/events/events.go +++ b/chain/events/events.go @@ -28,7 +28,7 @@ type TipSetObserver interface { Revert(ctx context.Context, from, to *types.TipSet) error } -type EventAPI interface { +type EventHelperAPI interface { ChainNotify(context.Context) (<-chan []*api.HeadChange, error) ChainGetBlockMessages(context.Context, cid.Cid) (*api.BlockMessages, error) ChainGetTipSetByHeight(context.Context, abi.ChainEpoch, types.TipSetKey) (*types.TipSet, error) @@ -47,7 +47,7 @@ type Events struct { *hcEvents } -func newEventsWithGCConfidence(ctx context.Context, api EventAPI, gcConfidence abi.ChainEpoch) (*Events, error) { +func newEventsWithGCConfidence(ctx context.Context, api EventHelperAPI, gcConfidence abi.ChainEpoch) (*Events, error) { cache := newCache(api, gcConfidence) ob := newObserver(cache, gcConfidence) @@ -61,7 +61,7 @@ func newEventsWithGCConfidence(ctx context.Context, api EventAPI, gcConfidence a return &Events{ob, he, headChange}, nil } -func NewEvents(ctx context.Context, api EventAPI) (*Events, error) { +func NewEvents(ctx context.Context, api EventHelperAPI) (*Events, error) { gcConfidence := 2 * build.ForkLengthThreshold return newEventsWithGCConfidence(ctx, api, gcConfidence) } diff --git a/chain/events/events_called.go b/chain/events/events_called.go index 3ac02b2f7..98e594c47 100644 --- a/chain/events/events_called.go +++ b/chain/events/events_called.go @@ -69,7 +69,7 @@ type queuedEvent struct { // Manages chain head change events, which may be forward (new tipset added to // chain) or backward (chain branch discarded in favour of heavier branch) type hcEvents struct { - cs EventAPI + cs EventHelperAPI lk sync.Mutex lastTs *types.TipSet @@ -94,7 +94,7 @@ type hcEvents struct { watcherEvents } -func newHCEvents(api EventAPI, obs *observer) *hcEvents { +func newHCEvents(api EventHelperAPI, obs *observer) *hcEvents { e := &hcEvents{ cs: api, confQueue: map[triggerH]map[msgH][]*queuedEvent{}, @@ -326,14 +326,14 @@ type headChangeAPI interface { // watcherEvents watches for a state change type watcherEvents struct { - cs EventAPI + cs EventHelperAPI hcAPI headChangeAPI lk sync.RWMutex matchers map[triggerID]StateMatchFunc } -func newWatcherEvents(hcAPI headChangeAPI, cs EventAPI) watcherEvents { +func newWatcherEvents(hcAPI headChangeAPI, cs EventHelperAPI) watcherEvents { return watcherEvents{ cs: cs, hcAPI: hcAPI, @@ -426,14 +426,14 @@ func (we *watcherEvents) StateChanged(check CheckFunc, scHnd StateChangeHandler, // messageEvents watches for message calls to actors type messageEvents struct { - cs EventAPI + cs EventHelperAPI hcAPI headChangeAPI lk sync.RWMutex matchers map[triggerID]MsgMatchFunc } -func newMessageEvents(hcAPI headChangeAPI, cs EventAPI) messageEvents { +func newMessageEvents(hcAPI headChangeAPI, cs EventHelperAPI) messageEvents { return messageEvents{ cs: cs, hcAPI: hcAPI, diff --git a/chain/events/events_height.go b/chain/events/events_height.go index 5789be753..1296a7f25 100644 --- a/chain/events/events_height.go +++ b/chain/events/events_height.go @@ -22,7 +22,7 @@ type heightHandler struct { } type heightEvents struct { - api EventAPI + api EventHelperAPI gcConfidence abi.ChainEpoch lk sync.Mutex @@ -31,7 +31,7 @@ type heightEvents struct { lastGc abi.ChainEpoch //nolint:structcheck } -func newHeightEvents(api EventAPI, obs *observer, gcConfidence abi.ChainEpoch) *heightEvents { +func newHeightEvents(api EventHelperAPI, obs *observer, gcConfidence abi.ChainEpoch) *heightEvents { he := &heightEvents{ api: api, gcConfidence: gcConfidence, diff --git a/chain/events/events_test.go b/chain/events/events_test.go index f16434355..a1309b90a 100644 --- a/chain/events/events_test.go +++ b/chain/events/events_test.go @@ -358,7 +358,7 @@ func (fcs *fakeCS) advance(rev, app, drop int, msgs map[int]cid.Cid, nulls ...in fcs.sub(nil, nil) } -var _ EventAPI = &fakeCS{} +var _ EventHelperAPI = &fakeCS{} func TestAt(t *testing.T) { //stm: @EVENTS_HEIGHT_CHAIN_AT_001, @EVENTS_HEIGHT_REVERT_001 diff --git a/chain/events/filter/event.go b/chain/events/filter/event.go index 24192a53e..1669d840e 100644 --- a/chain/events/filter/event.go +++ b/chain/events/filter/event.go @@ -27,14 +27,24 @@ func isIndexedValue(b uint8) bool { return b&(types.EventFlagIndexedKey|types.EventFlagIndexedValue) > 0 } -type EventFilter struct { - id types.FilterID - minHeight abi.ChainEpoch // minimum epoch to apply filter or -1 if no minimum - maxHeight abi.ChainEpoch // maximum epoch to apply filter or -1 if no maximum - tipsetCid cid.Cid - addresses []address.Address // list of f4 actor addresses that are extpected to emit the event - keys map[string][][]byte // map of key names to a list of alternate values that may match - maxResults int // maximum number of results to collect, 0 is unlimited +type AddressResolver func(context.Context, abi.ActorID, *types.TipSet) (address.Address, bool) + +type EventFilter interface { + Filter + + TakeCollectedEvents(context.Context) []*CollectedEvent + CollectEvents(context.Context, *TipSetEvents, bool, AddressResolver) error +} + +type eventFilter struct { + id types.FilterID + minHeight abi.ChainEpoch // minimum epoch to apply filter or -1 if no minimum + maxHeight abi.ChainEpoch // maximum epoch to apply filter or -1 if no maximum + tipsetCid cid.Cid + addresses []address.Address // list of actor addresses that are extpected to emit the event + + keysWithCodec map[string][]types.ActorEventBlock // map of key names to a list of alternate values that may match + maxResults int // maximum number of results to collect, 0 is unlimited mu sync.Mutex collected []*CollectedEvent @@ -42,11 +52,11 @@ type EventFilter struct { ch chan<- interface{} } -var _ Filter = (*EventFilter)(nil) +var _ Filter = (*eventFilter)(nil) type CollectedEvent struct { Entries []types.EventEntry - EmitterAddr address.Address // f4 address of emitter + EmitterAddr address.Address // address of emitter EventIdx int // index of the event within the list of emitted events Reverted bool Height abi.ChainEpoch @@ -55,24 +65,24 @@ type CollectedEvent struct { MsgCid cid.Cid // cid of message that produced event } -func (f *EventFilter) ID() types.FilterID { +func (f *eventFilter) ID() types.FilterID { return f.id } -func (f *EventFilter) SetSubChannel(ch chan<- interface{}) { +func (f *eventFilter) SetSubChannel(ch chan<- interface{}) { f.mu.Lock() defer f.mu.Unlock() f.ch = ch f.collected = nil } -func (f *EventFilter) ClearSubChannel() { +func (f *eventFilter) ClearSubChannel() { f.mu.Lock() defer f.mu.Unlock() f.ch = nil } -func (f *EventFilter) CollectEvents(ctx context.Context, te *TipSetEvents, revert bool, resolver func(ctx context.Context, emitter abi.ActorID, ts *types.TipSet) (address.Address, bool)) error { +func (f *eventFilter) CollectEvents(ctx context.Context, te *TipSetEvents, revert bool, resolver AddressResolver) error { if !f.matchTipset(te) { return nil } @@ -137,13 +147,13 @@ func (f *EventFilter) CollectEvents(ctx context.Context, te *TipSetEvents, rever return nil } -func (f *EventFilter) setCollectedEvents(ces []*CollectedEvent) { +func (f *eventFilter) setCollectedEvents(ces []*CollectedEvent) { f.mu.Lock() f.collected = ces f.mu.Unlock() } -func (f *EventFilter) TakeCollectedEvents(ctx context.Context) []*CollectedEvent { +func (f *eventFilter) TakeCollectedEvents(ctx context.Context) []*CollectedEvent { f.mu.Lock() collected := f.collected f.collected = nil @@ -153,14 +163,14 @@ func (f *EventFilter) TakeCollectedEvents(ctx context.Context) []*CollectedEvent return collected } -func (f *EventFilter) LastTaken() time.Time { +func (f *eventFilter) LastTaken() time.Time { f.mu.Lock() defer f.mu.Unlock() return f.lastTaken } // matchTipset reports whether this filter matches the given tipset -func (f *EventFilter) matchTipset(te *TipSetEvents) bool { +func (f *eventFilter) matchTipset(te *TipSetEvents) bool { if f.tipsetCid != cid.Undef { tsCid, err := te.Cid() if err != nil { @@ -178,7 +188,7 @@ func (f *EventFilter) matchTipset(te *TipSetEvents) bool { return true } -func (f *EventFilter) matchAddress(o address.Address) bool { +func (f *eventFilter) matchAddress(o address.Address) bool { if len(f.addresses) == 0 { return true } @@ -193,8 +203,8 @@ func (f *EventFilter) matchAddress(o address.Address) bool { return false } -func (f *EventFilter) matchKeys(ees []types.EventEntry) bool { - if len(f.keys) == 0 { +func (f *eventFilter) matchKeys(ees []types.EventEntry) bool { + if len(f.keysWithCodec) == 0 { return true } // TODO: optimize this naive algorithm @@ -216,19 +226,19 @@ func (f *EventFilter) matchKeys(ees []types.EventEntry) bool { continue } - wantlist, ok := f.keys[keyname] + wantlist, ok := f.keysWithCodec[keyname] if !ok || len(wantlist) == 0 { continue } for _, w := range wantlist { - if bytes.Equal(w, ee.Value) { + if bytes.Equal(w.Value, ee.Value) && w.Codec == ee.Codec { matched[keyname] = true break } } - if len(matched) == len(f.keys) { + if len(matched) == len(f.keysWithCodec) { // all keys have been matched return true } @@ -296,7 +306,7 @@ type EventFilterManager struct { EventIndex *EventIndex mu sync.Mutex // guards mutations to filters - filters map[types.FilterID]*EventFilter + filters map[types.FilterID]EventFilter currentHeight abi.ChainEpoch } @@ -362,7 +372,8 @@ func (m *EventFilterManager) Revert(ctx context.Context, from, to *types.TipSet) return nil } -func (m *EventFilterManager) Install(ctx context.Context, minHeight, maxHeight abi.ChainEpoch, tipsetCid cid.Cid, addresses []address.Address, keys map[string][][]byte) (*EventFilter, error) { +func (m *EventFilterManager) Install(ctx context.Context, minHeight, maxHeight abi.ChainEpoch, tipsetCid cid.Cid, addresses []address.Address, + keysWithCodec map[string][]types.ActorEventBlock, excludeReverted bool) (EventFilter, error) { m.mu.Lock() currentHeight := m.currentHeight m.mu.Unlock() @@ -376,26 +387,26 @@ func (m *EventFilterManager) Install(ctx context.Context, minHeight, maxHeight a return nil, xerrors.Errorf("new filter id: %w", err) } - f := &EventFilter{ - id: id, - minHeight: minHeight, - maxHeight: maxHeight, - tipsetCid: tipsetCid, - addresses: addresses, - keys: keys, - maxResults: m.MaxFilterResults, + f := &eventFilter{ + id: id, + minHeight: minHeight, + maxHeight: maxHeight, + tipsetCid: tipsetCid, + addresses: addresses, + keysWithCodec: keysWithCodec, + maxResults: m.MaxFilterResults, } if m.EventIndex != nil && minHeight != -1 && minHeight < currentHeight { // Filter needs historic events - if err := m.EventIndex.PrefillFilter(ctx, f, true); err != nil { + if err := m.EventIndex.prefillFilter(ctx, f, excludeReverted); err != nil { return nil, err } } m.mu.Lock() if m.filters == nil { - m.filters = make(map[types.FilterID]*EventFilter) + m.filters = make(map[types.FilterID]EventFilter) } m.filters[id] = f m.mu.Unlock() diff --git a/chain/events/filter/event_test.go b/chain/events/filter/event_test.go index 329573bc1..c650b71eb 100644 --- a/chain/events/filter/event_test.go +++ b/chain/events/filter/event_test.go @@ -22,6 +22,19 @@ import ( "github.com/filecoin-project/lotus/chain/types" ) +func keysToKeysWithCodec(keys map[string][][]byte) map[string][]types.ActorEventBlock { + keysWithCodec := make(map[string][]types.ActorEventBlock) + for k, v := range keys { + for _, vv := range v { + keysWithCodec[k] = append(keysWithCodec[k], types.ActorEventBlock{ + Codec: cid.Raw, + Value: vv, + }) + } + } + return keysWithCodec +} + func TestEventFilterCollectEvents(t *testing.T) { rng := pseudo.New(pseudo.NewSource(299792458)) a1 := randomF4Addr(t, rng) @@ -73,13 +86,13 @@ func TestEventFilterCollectEvents(t *testing.T) { testCases := []struct { name string - filter *EventFilter + filter *eventFilter te *TipSetEvents want []*CollectedEvent }{ { name: "nomatch tipset min height", - filter: &EventFilter{ + filter: &eventFilter{ minHeight: 14001, maxHeight: -1, }, @@ -88,7 +101,7 @@ func TestEventFilterCollectEvents(t *testing.T) { }, { name: "nomatch tipset max height", - filter: &EventFilter{ + filter: &eventFilter{ minHeight: -1, maxHeight: 13999, }, @@ -97,7 +110,7 @@ func TestEventFilterCollectEvents(t *testing.T) { }, { name: "match tipset min height", - filter: &EventFilter{ + filter: &eventFilter{ minHeight: 14000, maxHeight: -1, }, @@ -106,7 +119,7 @@ func TestEventFilterCollectEvents(t *testing.T) { }, { name: "match tipset cid", - filter: &EventFilter{ + filter: &eventFilter{ minHeight: -1, maxHeight: -1, tipsetCid: cid14000, @@ -116,7 +129,7 @@ func TestEventFilterCollectEvents(t *testing.T) { }, { name: "nomatch address", - filter: &EventFilter{ + filter: &eventFilter{ minHeight: -1, maxHeight: -1, addresses: []address.Address{a2}, @@ -126,7 +139,7 @@ func TestEventFilterCollectEvents(t *testing.T) { }, { name: "match address", - filter: &EventFilter{ + filter: &eventFilter{ minHeight: -1, maxHeight: -1, addresses: []address.Address{a1}, @@ -136,124 +149,124 @@ func TestEventFilterCollectEvents(t *testing.T) { }, { name: "match one entry", - filter: &EventFilter{ + filter: &eventFilter{ minHeight: -1, maxHeight: -1, - keys: map[string][][]byte{ + keysWithCodec: keysToKeysWithCodec(map[string][][]byte{ "type": { []byte("approval"), }, - }, + }), }, te: events14000, want: oneCollectedEvent, }, { name: "match one entry with alternate values", - filter: &EventFilter{ + filter: &eventFilter{ minHeight: -1, maxHeight: -1, - keys: map[string][][]byte{ + keysWithCodec: keysToKeysWithCodec(map[string][][]byte{ "type": { []byte("cancel"), []byte("propose"), []byte("approval"), }, - }, + }), }, te: events14000, want: oneCollectedEvent, }, { name: "nomatch one entry by missing value", - filter: &EventFilter{ + filter: &eventFilter{ minHeight: -1, maxHeight: -1, - keys: map[string][][]byte{ + keysWithCodec: keysToKeysWithCodec(map[string][][]byte{ "type": { []byte("cancel"), []byte("propose"), }, - }, + }), }, te: events14000, want: noCollectedEvents, }, { name: "nomatch one entry by missing key", - filter: &EventFilter{ + filter: &eventFilter{ minHeight: -1, maxHeight: -1, - keys: map[string][][]byte{ + keysWithCodec: keysToKeysWithCodec(map[string][][]byte{ "method": { []byte("approval"), }, - }, + }), }, te: events14000, want: noCollectedEvents, }, { name: "match one entry with multiple keys", - filter: &EventFilter{ + filter: &eventFilter{ minHeight: -1, maxHeight: -1, - keys: map[string][][]byte{ + keysWithCodec: keysToKeysWithCodec(map[string][][]byte{ "type": { []byte("approval"), }, "signer": { []byte("addr1"), }, - }, + }), }, te: events14000, want: oneCollectedEvent, }, { name: "nomatch one entry with one mismatching key", - filter: &EventFilter{ + filter: &eventFilter{ minHeight: -1, maxHeight: -1, - keys: map[string][][]byte{ + keysWithCodec: keysToKeysWithCodec(map[string][][]byte{ "type": { []byte("approval"), }, "approver": { []byte("addr1"), }, - }, + }), }, te: events14000, want: noCollectedEvents, }, { name: "nomatch one entry with one mismatching value", - filter: &EventFilter{ + filter: &eventFilter{ minHeight: -1, maxHeight: -1, - keys: map[string][][]byte{ + keysWithCodec: keysToKeysWithCodec(map[string][][]byte{ "type": { []byte("approval"), }, "signer": { []byte("addr2"), }, - }, + }), }, te: events14000, want: noCollectedEvents, }, { name: "nomatch one entry with one unindexed key", - filter: &EventFilter{ + filter: &eventFilter{ minHeight: -1, maxHeight: -1, - keys: map[string][][]byte{ + keysWithCodec: keysToKeysWithCodec(map[string][][]byte{ "amount": { []byte("2988181"), }, - }, + }), }, te: events14000, want: noCollectedEvents, diff --git a/chain/events/filter/index.go b/chain/events/filter/index.go index 0f4a54c5b..49be57c79 100644 --- a/chain/events/filter/index.go +++ b/chain/events/filter/index.go @@ -501,7 +501,7 @@ func (ei *EventIndex) CollectEvents(ctx context.Context, te *TipSetEvents, rever } // PrefillFilter fills a filter's collection of events from the historic index -func (ei *EventIndex) PrefillFilter(ctx context.Context, f *EventFilter, excludeReverted bool) error { +func (ei *EventIndex) prefillFilter(ctx context.Context, f *eventFilter, excludeReverted bool) error { clauses := []string{} values := []any{} joins := []string{} @@ -534,9 +534,9 @@ func (ei *EventIndex) PrefillFilter(ctx context.Context, f *EventFilter, exclude clauses = append(clauses, "("+strings.Join(subclauses, " OR ")+")") } - if len(f.keys) > 0 { + if len(f.keysWithCodec) > 0 { join := 0 - for key, vals := range f.keys { + for key, vals := range f.keysWithCodec { if len(vals) > 0 { join++ joinAlias := fmt.Sprintf("ee%d", join) @@ -545,8 +545,8 @@ func (ei *EventIndex) PrefillFilter(ctx context.Context, f *EventFilter, exclude values = append(values, key) subclauses := []string{} for _, val := range vals { - subclauses = append(subclauses, fmt.Sprintf("%s.value=?", joinAlias)) - values = append(values, val) + subclauses = append(subclauses, fmt.Sprintf("(%s.value=? AND %[1]s.codec=?)", joinAlias)) + values = append(values, val.Value, val.Codec) } clauses = append(clauses, "("+strings.Join(subclauses, " OR ")+")") } diff --git a/chain/events/filter/index_test.go b/chain/events/filter/index_test.go index f9b1b14ad..ce3f7b78a 100644 --- a/chain/events/filter/index_test.go +++ b/chain/events/filter/index_test.go @@ -82,13 +82,13 @@ func TestEventIndexPrefillFilter(t *testing.T) { testCases := []struct { name string - filter *EventFilter + filter *eventFilter te *TipSetEvents want []*CollectedEvent }{ { name: "nomatch tipset min height", - filter: &EventFilter{ + filter: &eventFilter{ minHeight: 14001, maxHeight: -1, }, @@ -97,7 +97,7 @@ func TestEventIndexPrefillFilter(t *testing.T) { }, { name: "nomatch tipset max height", - filter: &EventFilter{ + filter: &eventFilter{ minHeight: -1, maxHeight: 13999, }, @@ -106,7 +106,7 @@ func TestEventIndexPrefillFilter(t *testing.T) { }, { name: "match tipset min height", - filter: &EventFilter{ + filter: &eventFilter{ minHeight: 14000, maxHeight: -1, }, @@ -115,7 +115,7 @@ func TestEventIndexPrefillFilter(t *testing.T) { }, { name: "match tipset cid", - filter: &EventFilter{ + filter: &eventFilter{ minHeight: -1, maxHeight: -1, tipsetCid: cid14000, @@ -125,7 +125,7 @@ func TestEventIndexPrefillFilter(t *testing.T) { }, { name: "nomatch address", - filter: &EventFilter{ + filter: &eventFilter{ minHeight: -1, maxHeight: -1, addresses: []address.Address{a2}, @@ -135,7 +135,7 @@ func TestEventIndexPrefillFilter(t *testing.T) { }, { name: "match address", - filter: &EventFilter{ + filter: &eventFilter{ minHeight: -1, maxHeight: -1, addresses: []address.Address{a1}, @@ -145,124 +145,124 @@ func TestEventIndexPrefillFilter(t *testing.T) { }, { name: "match one entry", - filter: &EventFilter{ + filter: &eventFilter{ minHeight: -1, maxHeight: -1, - keys: map[string][][]byte{ + keysWithCodec: keysToKeysWithCodec(map[string][][]byte{ "type": { []byte("approval"), }, - }, + }), }, te: events14000, want: oneCollectedEvent, }, { name: "match one entry with alternate values", - filter: &EventFilter{ + filter: &eventFilter{ minHeight: -1, maxHeight: -1, - keys: map[string][][]byte{ + keysWithCodec: keysToKeysWithCodec(map[string][][]byte{ "type": { []byte("cancel"), []byte("propose"), []byte("approval"), }, - }, + }), }, te: events14000, want: oneCollectedEvent, }, { name: "nomatch one entry by missing value", - filter: &EventFilter{ + filter: &eventFilter{ minHeight: -1, maxHeight: -1, - keys: map[string][][]byte{ + keysWithCodec: keysToKeysWithCodec(map[string][][]byte{ "type": { []byte("cancel"), []byte("propose"), }, - }, + }), }, te: events14000, want: noCollectedEvents, }, { name: "nomatch one entry by missing key", - filter: &EventFilter{ + filter: &eventFilter{ minHeight: -1, maxHeight: -1, - keys: map[string][][]byte{ + keysWithCodec: keysToKeysWithCodec(map[string][][]byte{ "method": { []byte("approval"), }, - }, + }), }, te: events14000, want: noCollectedEvents, }, { name: "match one entry with multiple keys", - filter: &EventFilter{ + filter: &eventFilter{ minHeight: -1, maxHeight: -1, - keys: map[string][][]byte{ + keysWithCodec: keysToKeysWithCodec(map[string][][]byte{ "type": { []byte("approval"), }, "signer": { []byte("addr1"), }, - }, + }), }, te: events14000, want: oneCollectedEvent, }, { name: "nomatch one entry with one mismatching key", - filter: &EventFilter{ + filter: &eventFilter{ minHeight: -1, maxHeight: -1, - keys: map[string][][]byte{ + keysWithCodec: keysToKeysWithCodec(map[string][][]byte{ "type": { []byte("approval"), }, "approver": { []byte("addr1"), }, - }, + }), }, te: events14000, want: noCollectedEvents, }, { name: "nomatch one entry with one mismatching value", - filter: &EventFilter{ + filter: &eventFilter{ minHeight: -1, maxHeight: -1, - keys: map[string][][]byte{ + keysWithCodec: keysToKeysWithCodec(map[string][][]byte{ "type": { []byte("approval"), }, "signer": { []byte("addr2"), }, - }, + }), }, te: events14000, want: noCollectedEvents, }, { name: "nomatch one entry with one unindexed key", - filter: &EventFilter{ + filter: &eventFilter{ minHeight: -1, maxHeight: -1, - keys: map[string][][]byte{ + keysWithCodec: keysToKeysWithCodec(map[string][][]byte{ "amount": { []byte("2988181"), }, - }, + }), }, te: events14000, want: noCollectedEvents, @@ -272,7 +272,7 @@ func TestEventIndexPrefillFilter(t *testing.T) { for _, tc := range testCases { tc := tc // appease lint t.Run(tc.name, func(t *testing.T) { - if err := ei.PrefillFilter(context.Background(), tc.filter, false); err != nil { + if err := ei.prefillFilter(context.Background(), tc.filter, false); err != nil { require.NoError(t, err, "prefill filter events") } @@ -409,13 +409,13 @@ func TestEventIndexPrefillFilterExcludeReverted(t *testing.T) { inclusiveTestCases := []struct { name string - filter *EventFilter + filter *eventFilter te *TipSetEvents want []*CollectedEvent }{ { name: "nomatch tipset min height", - filter: &EventFilter{ + filter: &eventFilter{ minHeight: 14001, maxHeight: -1, }, @@ -424,7 +424,7 @@ func TestEventIndexPrefillFilterExcludeReverted(t *testing.T) { }, { name: "nomatch tipset max height", - filter: &EventFilter{ + filter: &eventFilter{ minHeight: -1, maxHeight: 13999, }, @@ -433,7 +433,7 @@ func TestEventIndexPrefillFilterExcludeReverted(t *testing.T) { }, { name: "match tipset min height", - filter: &EventFilter{ + filter: &eventFilter{ minHeight: 14000, maxHeight: -1, }, @@ -442,7 +442,7 @@ func TestEventIndexPrefillFilterExcludeReverted(t *testing.T) { }, { name: "match tipset cid", - filter: &EventFilter{ + filter: &eventFilter{ minHeight: -1, maxHeight: -1, tipsetCid: cid14000, @@ -452,7 +452,7 @@ func TestEventIndexPrefillFilterExcludeReverted(t *testing.T) { }, { name: "match tipset cid", - filter: &EventFilter{ + filter: &eventFilter{ minHeight: -1, maxHeight: -1, tipsetCid: reveredCID14000, @@ -462,7 +462,7 @@ func TestEventIndexPrefillFilterExcludeReverted(t *testing.T) { }, { name: "nomatch address", - filter: &EventFilter{ + filter: &eventFilter{ minHeight: -1, maxHeight: -1, addresses: []address.Address{a3}, @@ -472,7 +472,7 @@ func TestEventIndexPrefillFilterExcludeReverted(t *testing.T) { }, { name: "match address 2", - filter: &EventFilter{ + filter: &eventFilter{ minHeight: -1, maxHeight: -1, addresses: []address.Address{a2}, @@ -482,7 +482,7 @@ func TestEventIndexPrefillFilterExcludeReverted(t *testing.T) { }, { name: "match address 1", - filter: &EventFilter{ + filter: &eventFilter{ minHeight: -1, maxHeight: -1, addresses: []address.Address{a1}, @@ -492,155 +492,155 @@ func TestEventIndexPrefillFilterExcludeReverted(t *testing.T) { }, { name: "match one entry", - filter: &EventFilter{ + filter: &eventFilter{ minHeight: -1, maxHeight: -1, - keys: map[string][][]byte{ + keysWithCodec: keysToKeysWithCodec(map[string][][]byte{ "type": { []byte("approval"), }, - }, + }), }, te: events14000, want: twoCollectedEvent, }, { name: "match one entry with alternate values", - filter: &EventFilter{ + filter: &eventFilter{ minHeight: -1, maxHeight: -1, - keys: map[string][][]byte{ + keysWithCodec: keysToKeysWithCodec(map[string][][]byte{ "type": { []byte("cancel"), []byte("propose"), []byte("approval"), }, - }, + }), }, te: events14000, want: twoCollectedEvent, }, { name: "nomatch one entry by missing value", - filter: &EventFilter{ + filter: &eventFilter{ minHeight: -1, maxHeight: -1, - keys: map[string][][]byte{ + keysWithCodec: keysToKeysWithCodec(map[string][][]byte{ "type": { []byte("cancel"), []byte("propose"), }, - }, + }), }, te: events14000, want: noCollectedEvents, }, { name: "nomatch one entry by missing key", - filter: &EventFilter{ + filter: &eventFilter{ minHeight: -1, maxHeight: -1, - keys: map[string][][]byte{ + keysWithCodec: keysToKeysWithCodec(map[string][][]byte{ "method": { []byte("approval"), }, - }, + }), }, te: events14000, want: noCollectedEvents, }, { name: "match one entry with multiple keys", - filter: &EventFilter{ + filter: &eventFilter{ minHeight: -1, maxHeight: -1, - keys: map[string][][]byte{ + keysWithCodec: keysToKeysWithCodec(map[string][][]byte{ "type": { []byte("approval"), }, "signer": { []byte("addr1"), }, - }, + }), }, te: events14000, want: oneCollectedEvent, }, { name: "match one entry with multiple keys", - filter: &EventFilter{ + filter: &eventFilter{ minHeight: -1, maxHeight: -1, - keys: map[string][][]byte{ + keysWithCodec: keysToKeysWithCodec(map[string][][]byte{ "type": { []byte("approval"), }, "signer": { []byte("addr2"), }, - }, + }), }, te: revertedEvents14000, want: oneCollectedRevertedEvent, }, { name: "nomatch one entry with one mismatching key", - filter: &EventFilter{ + filter: &eventFilter{ minHeight: -1, maxHeight: -1, - keys: map[string][][]byte{ + keysWithCodec: keysToKeysWithCodec(map[string][][]byte{ "type": { []byte("approval"), }, "approver": { []byte("addr1"), }, - }, + }), }, te: events14000, want: noCollectedEvents, }, { name: "nomatch one entry with one mismatching value", - filter: &EventFilter{ + filter: &eventFilter{ minHeight: -1, maxHeight: -1, - keys: map[string][][]byte{ + keysWithCodec: keysToKeysWithCodec(map[string][][]byte{ "type": { []byte("approval"), }, "signer": { []byte("addr3"), }, - }, + }), }, te: events14000, want: noCollectedEvents, }, { name: "nomatch one entry with one unindexed key", - filter: &EventFilter{ + filter: &eventFilter{ minHeight: -1, maxHeight: -1, - keys: map[string][][]byte{ + keysWithCodec: keysToKeysWithCodec(map[string][][]byte{ "amount": { []byte("2988181"), }, - }, + }), }, te: events14000, want: noCollectedEvents, }, { name: "nomatch one entry with one unindexed key", - filter: &EventFilter{ + filter: &eventFilter{ minHeight: -1, maxHeight: -1, - keys: map[string][][]byte{ + keysWithCodec: keysToKeysWithCodec(map[string][][]byte{ "amount": { []byte("2988182"), }, - }, + }), }, te: events14000, want: noCollectedEvents, @@ -649,13 +649,13 @@ func TestEventIndexPrefillFilterExcludeReverted(t *testing.T) { exclusiveTestCases := []struct { name string - filter *EventFilter + filter *eventFilter te *TipSetEvents want []*CollectedEvent }{ { name: "nomatch tipset min height", - filter: &EventFilter{ + filter: &eventFilter{ minHeight: 14001, maxHeight: -1, }, @@ -664,7 +664,7 @@ func TestEventIndexPrefillFilterExcludeReverted(t *testing.T) { }, { name: "nomatch tipset max height", - filter: &EventFilter{ + filter: &eventFilter{ minHeight: -1, maxHeight: 13999, }, @@ -673,7 +673,7 @@ func TestEventIndexPrefillFilterExcludeReverted(t *testing.T) { }, { name: "match tipset min height", - filter: &EventFilter{ + filter: &eventFilter{ minHeight: 14000, maxHeight: -1, }, @@ -682,7 +682,7 @@ func TestEventIndexPrefillFilterExcludeReverted(t *testing.T) { }, { name: "match tipset cid", - filter: &EventFilter{ + filter: &eventFilter{ minHeight: -1, maxHeight: -1, tipsetCid: cid14000, @@ -692,7 +692,7 @@ func TestEventIndexPrefillFilterExcludeReverted(t *testing.T) { }, { name: "match tipset cid but reverted", - filter: &EventFilter{ + filter: &eventFilter{ minHeight: -1, maxHeight: -1, tipsetCid: reveredCID14000, @@ -702,7 +702,7 @@ func TestEventIndexPrefillFilterExcludeReverted(t *testing.T) { }, { name: "nomatch address", - filter: &EventFilter{ + filter: &eventFilter{ minHeight: -1, maxHeight: -1, addresses: []address.Address{a3}, @@ -712,7 +712,7 @@ func TestEventIndexPrefillFilterExcludeReverted(t *testing.T) { }, { name: "nomatch address 2 but reverted", - filter: &EventFilter{ + filter: &eventFilter{ minHeight: -1, maxHeight: -1, addresses: []address.Address{a2}, @@ -722,7 +722,7 @@ func TestEventIndexPrefillFilterExcludeReverted(t *testing.T) { }, { name: "match address", - filter: &EventFilter{ + filter: &eventFilter{ minHeight: -1, maxHeight: -1, addresses: []address.Address{a1}, @@ -732,141 +732,141 @@ func TestEventIndexPrefillFilterExcludeReverted(t *testing.T) { }, { name: "match one entry", - filter: &EventFilter{ + filter: &eventFilter{ minHeight: -1, maxHeight: -1, - keys: map[string][][]byte{ + keysWithCodec: keysToKeysWithCodec(map[string][][]byte{ "type": { []byte("approval"), }, - }, + }), }, te: events14000, want: oneCollectedEvent, }, { name: "match one entry with alternate values", - filter: &EventFilter{ + filter: &eventFilter{ minHeight: -1, maxHeight: -1, - keys: map[string][][]byte{ + keysWithCodec: keysToKeysWithCodec(map[string][][]byte{ "type": { []byte("cancel"), []byte("propose"), []byte("approval"), }, - }, + }), }, te: events14000, want: oneCollectedEvent, }, { name: "nomatch one entry by missing value", - filter: &EventFilter{ + filter: &eventFilter{ minHeight: -1, maxHeight: -1, - keys: map[string][][]byte{ + keysWithCodec: keysToKeysWithCodec(map[string][][]byte{ "type": { []byte("cancel"), []byte("propose"), }, - }, + }), }, te: events14000, want: noCollectedEvents, }, { name: "nomatch one entry by missing key", - filter: &EventFilter{ + filter: &eventFilter{ minHeight: -1, maxHeight: -1, - keys: map[string][][]byte{ + keysWithCodec: keysToKeysWithCodec(map[string][][]byte{ "method": { []byte("approval"), }, - }, + }), }, te: events14000, want: noCollectedEvents, }, { name: "match one entry with multiple keys", - filter: &EventFilter{ + filter: &eventFilter{ minHeight: -1, maxHeight: -1, - keys: map[string][][]byte{ + keysWithCodec: keysToKeysWithCodec(map[string][][]byte{ "type": { []byte("approval"), }, "signer": { []byte("addr1"), }, - }, + }), }, te: events14000, want: oneCollectedEvent, }, { name: "nomatch one entry with one mismatching key", - filter: &EventFilter{ + filter: &eventFilter{ minHeight: -1, maxHeight: -1, - keys: map[string][][]byte{ + keysWithCodec: keysToKeysWithCodec(map[string][][]byte{ "type": { []byte("approval"), }, "approver": { []byte("addr1"), }, - }, + }), }, te: events14000, want: noCollectedEvents, }, { name: "nomatch one entry with matching reverted value", - filter: &EventFilter{ + filter: &eventFilter{ minHeight: -1, maxHeight: -1, - keys: map[string][][]byte{ + keysWithCodec: keysToKeysWithCodec(map[string][][]byte{ "type": { []byte("approval"), }, "signer": { []byte("addr2"), }, - }, + }), }, te: events14000, want: noCollectedEvents, }, { name: "nomatch one entry with one mismatching value", - filter: &EventFilter{ + filter: &eventFilter{ minHeight: -1, maxHeight: -1, - keys: map[string][][]byte{ + keysWithCodec: keysToKeysWithCodec(map[string][][]byte{ "type": { []byte("approval"), }, "signer": { []byte("addr3"), }, - }, + }), }, te: events14000, want: noCollectedEvents, }, { name: "nomatch one entry with one unindexed key", - filter: &EventFilter{ + filter: &eventFilter{ minHeight: -1, maxHeight: -1, - keys: map[string][][]byte{ + keysWithCodec: keysToKeysWithCodec(map[string][][]byte{ "amount": { []byte("2988181"), }, - }, + }), }, te: events14000, want: noCollectedEvents, @@ -876,7 +876,7 @@ func TestEventIndexPrefillFilterExcludeReverted(t *testing.T) { for _, tc := range inclusiveTestCases { tc := tc // appease lint t.Run(tc.name, func(t *testing.T) { - if err := ei.PrefillFilter(context.Background(), tc.filter, false); err != nil { + if err := ei.prefillFilter(context.Background(), tc.filter, false); err != nil { require.NoError(t, err, "prefill filter events") } @@ -888,7 +888,7 @@ func TestEventIndexPrefillFilterExcludeReverted(t *testing.T) { for _, tc := range exclusiveTestCases { tc := tc // appease lint t.Run(tc.name, func(t *testing.T) { - if err := ei.PrefillFilter(context.Background(), tc.filter, true); err != nil { + if err := ei.prefillFilter(context.Background(), tc.filter, true); err != nil { require.NoError(t, err, "prefill filter events") } diff --git a/chain/events/message_cache.go b/chain/events/message_cache.go index 96f6bcbd7..24b3c934a 100644 --- a/chain/events/message_cache.go +++ b/chain/events/message_cache.go @@ -11,13 +11,13 @@ import ( ) type messageCache struct { - api EventAPI + api EventHelperAPI blockMsgLk sync.Mutex blockMsgCache *arc.ARCCache[cid.Cid, *api.BlockMessages] } -func newMessageCache(a EventAPI) *messageCache { +func newMessageCache(a EventHelperAPI) *messageCache { blsMsgCache, _ := arc.NewARC[cid.Cid, *api.BlockMessages](500) return &messageCache{ diff --git a/chain/events/observer.go b/chain/events/observer.go index 446218585..0b021f996 100644 --- a/chain/events/observer.go +++ b/chain/events/observer.go @@ -17,7 +17,7 @@ import ( ) type observer struct { - api EventAPI + api EventHelperAPI gcConfidence abi.ChainEpoch diff --git a/chain/events/state/predicates.go b/chain/events/state/predicates.go index ff05156a6..e4e8b8f7e 100644 --- a/chain/events/state/predicates.go +++ b/chain/events/state/predicates.go @@ -242,7 +242,7 @@ func (sp *StatePredicates) DealStateChangedForIDs(dealIds []abi.DealID) DiffDeal } existenceChanged := oldFound != newFound - valueChanged := (oldFound && newFound) && *oldDeal != *newDeal + valueChanged := (oldFound && newFound) && !oldDeal.Equals(newDeal) if existenceChanged || valueChanged { changedDeals[dealID] = market.DealStateChange{ID: dealID, From: oldDeal, To: newDeal} } diff --git a/chain/events/state/predicates_test.go b/chain/events/state/predicates_test.go index 52fc2668a..79c1d2e0e 100644 --- a/chain/events/state/predicates_test.go +++ b/chain/events/state/predicates_test.go @@ -177,11 +177,11 @@ func TestMarketPredicates(t *testing.T) { require.Contains(t, changedDealIDs, abi.DealID(1)) require.Contains(t, changedDealIDs, abi.DealID(2)) deal1 := changedDealIDs[abi.DealID(1)] - if deal1.From.LastUpdatedEpoch != 2 || deal1.To.LastUpdatedEpoch != 3 { + if deal1.From.LastUpdatedEpoch() != 2 || deal1.To.LastUpdatedEpoch() != 3 { t.Fatal("Unexpected change to LastUpdatedEpoch") } deal2 := changedDealIDs[abi.DealID(2)] - if deal2.From.LastUpdatedEpoch != 5 || deal2.To != nil { + if deal2.From.LastUpdatedEpoch() != 5 || deal2.To != nil { t.Fatal("Expected To to be nil") } @@ -243,8 +243,8 @@ func TestMarketPredicates(t *testing.T) { require.Len(t, changedDeals.Modified, 1) require.Equal(t, abi.DealID(1), changedDeals.Modified[0].ID) - require.True(t, dealEquality(*newDeal1, *changedDeals.Modified[0].To)) - require.True(t, dealEquality(*oldDeal1, *changedDeals.Modified[0].From)) + require.True(t, dealEquality(*newDeal1, changedDeals.Modified[0].To)) + require.True(t, dealEquality(*oldDeal1, changedDeals.Modified[0].From)) require.Equal(t, abi.DealID(2), changedDeals.Removed[0].ID) }) @@ -579,7 +579,7 @@ func newSectorPreCommitInfo(sectorNo abi.SectorNumber, sealed cid.Cid, expiratio } func dealEquality(expected market2.DealState, actual market.DealState) bool { - return expected.LastUpdatedEpoch == actual.LastUpdatedEpoch && - expected.SectorStartEpoch == actual.SectorStartEpoch && - expected.SlashEpoch == actual.SlashEpoch + return expected.LastUpdatedEpoch == actual.LastUpdatedEpoch() && + expected.SectorStartEpoch == actual.SectorStartEpoch() && + expected.SlashEpoch == actual.SlashEpoch() } diff --git a/chain/exchange/cbor_gen.go b/chain/exchange/cbor_gen.go index 71c75869d..4aa74f0c0 100644 --- a/chain/exchange/cbor_gen.go +++ b/chain/exchange/cbor_gen.go @@ -35,7 +35,7 @@ func (t *Request) MarshalCBOR(w io.Writer) error { } // t.Head ([]cid.Cid) (slice) - if len(t.Head) > cbg.MaxLength { + if len(t.Head) > 8192 { return xerrors.Errorf("Slice value in field t.Head was too long") } @@ -95,7 +95,7 @@ func (t *Request) UnmarshalCBOR(r io.Reader) (err error) { return err } - if extra > cbg.MaxLength { + if extra > 8192 { return fmt.Errorf("t.Head: array too large (%d)", extra) } @@ -126,9 +126,9 @@ func (t *Request) UnmarshalCBOR(r io.Reader) (err error) { t.Head[i] = c } + } } - // t.Length (uint64) (uint64) { @@ -181,7 +181,7 @@ func (t *Response) MarshalCBOR(w io.Writer) error { } // t.ErrorMessage (string) (string) - if len(t.ErrorMessage) > cbg.MaxLength { + if len(t.ErrorMessage) > 8192 { return xerrors.Errorf("Value in field t.ErrorMessage was too long") } @@ -193,7 +193,7 @@ func (t *Response) MarshalCBOR(w io.Writer) error { } // t.Chain ([]*exchange.BSTipSet) (slice) - if len(t.Chain) > cbg.MaxLength { + if len(t.Chain) > 8192 { return xerrors.Errorf("Slice value in field t.Chain was too long") } @@ -204,6 +204,7 @@ func (t *Response) MarshalCBOR(w io.Writer) error { if err := v.MarshalCBOR(cw); err != nil { return err } + } return nil } @@ -248,7 +249,7 @@ func (t *Response) UnmarshalCBOR(r io.Reader) (err error) { // t.ErrorMessage (string) (string) { - sval, err := cbg.ReadString(cr) + sval, err := cbg.ReadStringWithMax(cr, 8192) if err != nil { return err } @@ -262,7 +263,7 @@ func (t *Response) UnmarshalCBOR(r io.Reader) (err error) { return err } - if extra > cbg.MaxLength { + if extra > 8192 { return fmt.Errorf("t.Chain: array too large (%d)", extra) } @@ -300,9 +301,9 @@ func (t *Response) UnmarshalCBOR(r io.Reader) (err error) { } } + } } - return nil } @@ -332,10 +333,11 @@ func (t *CompactedMessagesCBOR) MarshalCBOR(w io.Writer) error { if err := v.MarshalCBOR(cw); err != nil { return err } + } // t.BlsIncludes ([]exchange.messageIndices) (slice) - if len(t.BlsIncludes) > cbg.MaxLength { + if len(t.BlsIncludes) > 8192 { return xerrors.Errorf("Slice value in field t.BlsIncludes was too long") } @@ -346,6 +348,7 @@ func (t *CompactedMessagesCBOR) MarshalCBOR(w io.Writer) error { if err := v.MarshalCBOR(cw); err != nil { return err } + } // t.Secpk ([]*types.SignedMessage) (slice) @@ -360,10 +363,11 @@ func (t *CompactedMessagesCBOR) MarshalCBOR(w io.Writer) error { if err := v.MarshalCBOR(cw); err != nil { return err } + } // t.SecpkIncludes ([]exchange.messageIndices) (slice) - if len(t.SecpkIncludes) > cbg.MaxLength { + if len(t.SecpkIncludes) > 8192 { return xerrors.Errorf("Slice value in field t.SecpkIncludes was too long") } @@ -374,6 +378,7 @@ func (t *CompactedMessagesCBOR) MarshalCBOR(w io.Writer) error { if err := v.MarshalCBOR(cw); err != nil { return err } + } return nil } @@ -446,9 +451,9 @@ func (t *CompactedMessagesCBOR) UnmarshalCBOR(r io.Reader) (err error) { } } + } } - // t.BlsIncludes ([]exchange.messageIndices) (slice) maj, extra, err = cr.ReadHeader() @@ -456,7 +461,7 @@ func (t *CompactedMessagesCBOR) UnmarshalCBOR(r io.Reader) (err error) { return err } - if extra > cbg.MaxLength { + if extra > 8192 { return fmt.Errorf("t.BlsIncludes: array too large (%d)", extra) } @@ -484,9 +489,9 @@ func (t *CompactedMessagesCBOR) UnmarshalCBOR(r io.Reader) (err error) { } } + } } - // t.Secpk ([]*types.SignedMessage) (slice) maj, extra, err = cr.ReadHeader() @@ -532,9 +537,9 @@ func (t *CompactedMessagesCBOR) UnmarshalCBOR(r io.Reader) (err error) { } } + } } - // t.SecpkIncludes ([]exchange.messageIndices) (slice) maj, extra, err = cr.ReadHeader() @@ -542,7 +547,7 @@ func (t *CompactedMessagesCBOR) UnmarshalCBOR(r io.Reader) (err error) { return err } - if extra > cbg.MaxLength { + if extra > 8192 { return fmt.Errorf("t.SecpkIncludes: array too large (%d)", extra) } @@ -570,9 +575,9 @@ func (t *CompactedMessagesCBOR) UnmarshalCBOR(r io.Reader) (err error) { } } + } } - return nil } @@ -591,7 +596,7 @@ func (t *BSTipSet) MarshalCBOR(w io.Writer) error { } // t.Blocks ([]*types.BlockHeader) (slice) - if len(t.Blocks) > cbg.MaxLength { + if len(t.Blocks) > 8192 { return xerrors.Errorf("Slice value in field t.Blocks was too long") } @@ -602,6 +607,7 @@ func (t *BSTipSet) MarshalCBOR(w io.Writer) error { if err := v.MarshalCBOR(cw); err != nil { return err } + } // t.Messages (exchange.CompactedMessages) (struct) @@ -641,7 +647,7 @@ func (t *BSTipSet) UnmarshalCBOR(r io.Reader) (err error) { return err } - if extra > cbg.MaxLength { + if extra > 8192 { return fmt.Errorf("t.Blocks: array too large (%d)", extra) } @@ -679,9 +685,9 @@ func (t *BSTipSet) UnmarshalCBOR(r io.Reader) (err error) { } } + } } - // t.Messages (exchange.CompactedMessages) (struct) { diff --git a/chain/gen/genesis/miners.go b/chain/gen/genesis/miners.go index df8900cab..9ae39cf35 100644 --- a/chain/gen/genesis/miners.go +++ b/chain/gen/genesis/miners.go @@ -374,13 +374,33 @@ func SetupStorageMiners(ctx context.Context, cs *store.ChainStore, sys vm.Syscal // Commit sectors { for pi, preseal := range m.Sectors { - params := &minertypes.SectorPreCommitInfo{ - SealProof: preseal.ProofType, - SectorNumber: preseal.SectorID, - SealedCID: preseal.CommR, - SealRandEpoch: -1, - DealIDs: []abi.DealID{minerInfos[i].dealIDs[pi]}, - Expiration: minerInfos[i].presealExp, // TODO: Allow setting externally! + var paramEnc []byte + var preCommitMethodNum abi.MethodNum + if nv >= network.Version22 { + paramEnc = mustEnc(&miner.PreCommitSectorBatchParams2{ + Sectors: []miner.SectorPreCommitInfo{ + { + SealProof: preseal.ProofType, + SectorNumber: preseal.SectorID, + SealedCID: preseal.CommR, + SealRandEpoch: -1, + DealIDs: []abi.DealID{minerInfos[i].dealIDs[pi]}, + Expiration: minerInfos[i].presealExp, // TODO: Allow setting externally! + UnsealedCid: &preseal.CommD, + }, + }, + }) + preCommitMethodNum = builtintypes.MethodsMiner.PreCommitSectorBatch2 + } else { + paramEnc = mustEnc(&minertypes.SectorPreCommitInfo{ + SealProof: preseal.ProofType, + SectorNumber: preseal.SectorID, + SealedCID: preseal.CommR, + SealRandEpoch: -1, + DealIDs: []abi.DealID{minerInfos[i].dealIDs[pi]}, + Expiration: minerInfos[i].presealExp, // TODO: Allow setting externally! + }) + preCommitMethodNum = builtintypes.MethodsMiner.PreCommitSector } sectorWeight := minerInfos[i].sectorWeight[pi] @@ -463,7 +483,7 @@ func SetupStorageMiners(ctx context.Context, cs *store.ChainStore, sys vm.Syscal pledge = big.Add(pcd, pledge) - _, err = doExecValue(ctx, genesisVm, minerInfos[i].maddr, m.Worker, pledge, builtintypes.MethodsMiner.PreCommitSector, mustEnc(params)) + _, err = doExecValue(ctx, genesisVm, minerInfos[i].maddr, m.Worker, pledge, preCommitMethodNum, paramEnc) if err != nil { return cid.Undef, xerrors.Errorf("failed to confirm presealed sectors: %w", err) } diff --git a/chain/state/statetree.go b/chain/state/statetree.go index 61d7d500a..1a6497d04 100644 --- a/chain/state/statetree.go +++ b/chain/state/statetree.go @@ -156,7 +156,7 @@ func VersionForNetwork(ver network.Version) (types.StateTreeVersion, error) { case network.Version13, network.Version14, network.Version15, network.Version16, network.Version17: return types.StateTreeVersion4, nil - case network.Version18, network.Version19, network.Version20, network.Version21: + case network.Version18, network.Version19, network.Version20, network.Version21, network.Version22: return types.StateTreeVersion5, nil default: diff --git a/chain/stmgr/actors.go b/chain/stmgr/actors.go index 56744fa74..f1d615e8d 100644 --- a/chain/stmgr/actors.go +++ b/chain/stmgr/actors.go @@ -284,7 +284,7 @@ func GetStorageDeal(ctx context.Context, sm *StateManager, dealID abi.DealID, ts return &api.MarketDeal{ Proposal: *proposal, - State: *st, + State: api.MakeDealState(st), }, nil } diff --git a/chain/stmgr/forks.go b/chain/stmgr/forks.go index 2f18bde82..6d6f9ef65 100644 --- a/chain/stmgr/forks.go +++ b/chain/stmgr/forks.go @@ -200,10 +200,6 @@ func (sm *StateManager) HandleStateForks(ctx context.Context, root cid.Cid, heig log.Errorw("FAILED migration", "height", height, "from", root, "error", err) return cid.Undef, err } - // Yes, we update the cache, even for the final upgrade epoch. Why? Reverts. This - // can save us a _lot_ of time because very few actors will have changed if we - // do a small revert then need to re-run the migration. - u.cache.Update(tmpCache) log.Warnw("COMPLETED migration", "height", height, "from", root, diff --git a/chain/types/actor_event.go b/chain/types/actor_event.go new file mode 100644 index 000000000..bf95189e1 --- /dev/null +++ b/chain/types/actor_event.go @@ -0,0 +1,67 @@ +package types + +import ( + "github.com/ipfs/go-cid" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" +) + +type ActorEventBlock struct { + // The value codec to match when filtering event values. + Codec uint64 `json:"codec"` + + // The value to want to match on associated with the corresponding "event key" + // when filtering events. + // Should be a byte array encoded with the specified codec. + // Assumes base64 encoding when converting to/from JSON strings. + Value []byte `json:"value"` +} + +type ActorEventFilter struct { + // Matches events from one of these actors, or any actor if empty. + // For now, this MUST be a Filecoin address. + Addresses []address.Address `json:"addresses,omitempty"` + + // Matches events with the specified key/values, or all events if empty. + // If the value is an empty slice, the filter will match on the key only, accepting any value. + Fields map[string][]ActorEventBlock `json:"fields,omitempty"` + + // The height of the earliest tipset to include in the query. If empty, the query starts at the + // last finalized tipset. + // NOTE: In a future upgrade, this will be strict when set and will result in an error if a filter + // cannot be fulfilled by the depth of history available in the node. Currently, the node will + // nott return an error, but will return starting from the epoch it has data for. + FromHeight *abi.ChainEpoch `json:"fromHeight,omitempty"` + + // The height of the latest tipset to include in the query. If empty, the query ends at the + // latest tipset. + ToHeight *abi.ChainEpoch `json:"toHeight,omitempty"` + + // Restricts events returned to those emitted from messages contained in this tipset. + // If `TipSetKey` is legt empty in the filter criteria, then neither `FromHeight` nor `ToHeight` are allowed. + TipSetKey *TipSetKey `json:"tipsetKey,omitempty"` +} + +type ActorEvent struct { + // Event entries in log form. + Entries []EventEntry `json:"entries"` + + // Filecoin address of the actor that emitted this event. + // NOTE: In a future upgrade, this will change to always be an ID address. Currently this will be + // either the f4 address, or ID address if an f4 is not available for this actor. + Emitter address.Address `json:"emitter"` + + // Reverted is set to true if the message that produced this event was reverted because of a network re-org + // in that case, the event should be considered as reverted as well. + Reverted bool `json:"reverted"` + + // Height of the tipset that contained the message that produced this event. + Height abi.ChainEpoch `json:"height"` + + // The tipset that contained the message that produced this event. + TipSetKey TipSetKey `json:"tipsetKey"` + + // CID of message that produced this event. + MsgCid cid.Cid `json:"msgCid"` +} diff --git a/chain/types/actor_event_test.go b/chain/types/actor_event_test.go new file mode 100644 index 000000000..8c50b1717 --- /dev/null +++ b/chain/types/actor_event_test.go @@ -0,0 +1,125 @@ +package types + +import ( + "encoding/json" + pseudo "math/rand" + "testing" + + "github.com/ipfs/go-cid" + mh "github.com/multiformats/go-multihash" + "github.com/stretchr/testify/require" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" + builtintypes "github.com/filecoin-project/go-state-types/builtin" +) + +func TestJSONMarshalling(t *testing.T) { + rng := pseudo.New(pseudo.NewSource(0)) + t.Run("actor event with entries", + testJsonMarshalling( + ActorEvent{ + Entries: []EventEntry{ + { + Key: "key1", + Codec: 0x51, + Value: []byte("value1"), + }, + { + Key: "key2", + Codec: 0x52, + Value: []byte("value2"), + }, + }, + Emitter: randomF4Addr(t, rng), + Reverted: false, + Height: 1001, + TipSetKey: NewTipSetKey(randomCid(t, rng)), + MsgCid: randomCid(t, rng), + }, + `{"entries":[{"Flags":0,"Key":"key1","Codec":81,"Value":"dmFsdWUx"},{"Flags":0,"Key":"key2","Codec":82,"Value":"dmFsdWUy"}],"emitter":"f410fagkp3qx2f76maqot74jaiw3tzbxe76k76zrkl3xifk67isrnbn2sll3yua","reverted":false,"height":1001,"tipsetKey":[{"/":"bafkqacx3dag26sfht3qlcdi"}],"msgCid":{"/":"bafkqacrziziykd6uuf4islq"}}`, + ), + ) + + t.Run("actor event filter", + testJsonMarshalling( + ActorEventFilter{ + Addresses: []address.Address{ + randomF4Addr(t, pseudo.New(pseudo.NewSource(0))), + randomF4Addr(t, pseudo.New(pseudo.NewSource(0))), + }, + Fields: map[string][]ActorEventBlock{ + "key1": { + { + Codec: 0x51, + Value: []byte("value1"), + }, + }, + "key2": { + { + Codec: 0x52, + Value: []byte("value2"), + }, + }, + }, + FromHeight: heightOf(0), + ToHeight: heightOf(100), + TipSetKey: randomTipSetKey(t, rng), + }, + `{"addresses":["f410fagkp3qx2f76maqot74jaiw3tzbxe76k76zrkl3xifk67isrnbn2sll3yua","f410fagkp3qx2f76maqot74jaiw3tzbxe76k76zrkl3xifk67isrnbn2sll3yua"],"fields":{"key1":[{"codec":81,"value":"dmFsdWUx"}],"key2":[{"codec":82,"value":"dmFsdWUy"}]},"fromHeight":0,"toHeight":100,"tipsetKey":[{"/":"bafkqacxcqxwocuiukv4aq5i"}]}`, + ), + ) + t.Run("actor event block", + testJsonMarshalling( + ActorEventBlock{ + Codec: 1, + Value: []byte("test"), + }, + `{"codec":1,"value":"dGVzdA=="}`, + ), + ) +} + +func testJsonMarshalling[V ActorEvent | ActorEventBlock | ActorEventFilter](subject V, expect string) func(t *testing.T) { + return func(t *testing.T) { + gotMarshalled, err := json.Marshal(subject) + require.NoError(t, err) + require.JSONEqf(t, expect, string(gotMarshalled), "serialization mismatch") + var gotUnmarshalled V + require.NoError(t, json.Unmarshal([]byte(expect), &gotUnmarshalled)) + require.Equal(t, subject, gotUnmarshalled) + } +} + +func heightOf(h int64) *abi.ChainEpoch { + hp := abi.ChainEpoch(h) + return &hp +} + +func randomTipSetKey(tb testing.TB, rng *pseudo.Rand) *TipSetKey { + tb.Helper() + tk := NewTipSetKey(randomCid(tb, rng)) + return &tk +} + +func randomF4Addr(tb testing.TB, rng *pseudo.Rand) address.Address { + tb.Helper() + addr, err := address.NewDelegatedAddress(builtintypes.EthereumAddressManagerActorID, randomBytes(32, rng)) + require.NoError(tb, err) + + return addr +} + +func randomCid(tb testing.TB, rng *pseudo.Rand) cid.Cid { + tb.Helper() + cb := cid.V1Builder{Codec: cid.Raw, MhType: mh.IDENTITY} + c, err := cb.Sum(randomBytes(10, rng)) + require.NoError(tb, err) + return c +} + +func randomBytes(n int, rng *pseudo.Rand) []byte { + buf := make([]byte, n) + rng.Read(buf) + return buf +} diff --git a/chain/types/cbor_gen.go b/chain/types/cbor_gen.go index 74a278ce4..dde703cee 100644 --- a/chain/types/cbor_gen.go +++ b/chain/types/cbor_gen.go @@ -55,7 +55,7 @@ func (t *BlockHeader) MarshalCBOR(w io.Writer) error { } // t.BeaconEntries ([]types.BeaconEntry) (slice) - if len(t.BeaconEntries) > cbg.MaxLength { + if len(t.BeaconEntries) > 8192 { return xerrors.Errorf("Slice value in field t.BeaconEntries was too long") } @@ -66,10 +66,11 @@ func (t *BlockHeader) MarshalCBOR(w io.Writer) error { if err := v.MarshalCBOR(cw); err != nil { return err } + } // t.WinPoStProof ([]proof.PoStProof) (slice) - if len(t.WinPoStProof) > cbg.MaxLength { + if len(t.WinPoStProof) > 8192 { return xerrors.Errorf("Slice value in field t.WinPoStProof was too long") } @@ -80,10 +81,11 @@ func (t *BlockHeader) MarshalCBOR(w io.Writer) error { if err := v.MarshalCBOR(cw); err != nil { return err } + } // t.Parents ([]cid.Cid) (slice) - if len(t.Parents) > cbg.MaxLength { + if len(t.Parents) > 8192 { return xerrors.Errorf("Slice value in field t.Parents was too long") } @@ -238,7 +240,7 @@ func (t *BlockHeader) UnmarshalCBOR(r io.Reader) (err error) { return err } - if extra > cbg.MaxLength { + if extra > 8192 { return fmt.Errorf("t.BeaconEntries: array too large (%d)", extra) } @@ -266,9 +268,9 @@ func (t *BlockHeader) UnmarshalCBOR(r io.Reader) (err error) { } } + } } - // t.WinPoStProof ([]proof.PoStProof) (slice) maj, extra, err = cr.ReadHeader() @@ -276,7 +278,7 @@ func (t *BlockHeader) UnmarshalCBOR(r io.Reader) (err error) { return err } - if extra > cbg.MaxLength { + if extra > 8192 { return fmt.Errorf("t.WinPoStProof: array too large (%d)", extra) } @@ -304,9 +306,9 @@ func (t *BlockHeader) UnmarshalCBOR(r io.Reader) (err error) { } } + } } - // t.Parents ([]cid.Cid) (slice) maj, extra, err = cr.ReadHeader() @@ -314,7 +316,7 @@ func (t *BlockHeader) UnmarshalCBOR(r io.Reader) (err error) { return err } - if extra > cbg.MaxLength { + if extra > 8192 { return fmt.Errorf("t.Parents: array too large (%d)", extra) } @@ -345,9 +347,9 @@ func (t *BlockHeader) UnmarshalCBOR(r io.Reader) (err error) { t.Parents[i] = c } + } } - // t.ParentWeight (big.Int) (struct) { @@ -360,10 +362,10 @@ func (t *BlockHeader) UnmarshalCBOR(r io.Reader) (err error) { // t.Height (abi.ChainEpoch) (int64) { maj, extra, err := cr.ReadHeader() - var extraI int64 if err != nil { return err } + var extraI int64 switch maj { case cbg.MajUnsignedInt: extraI = int64(extra) @@ -511,7 +513,7 @@ func (t *Ticket) MarshalCBOR(w io.Writer) error { } // t.VRFProof ([]uint8) (slice) - if len(t.VRFProof) > cbg.ByteArrayMaxLen { + if len(t.VRFProof) > 2097152 { return xerrors.Errorf("Byte array in field t.VRFProof was too long") } @@ -519,9 +521,10 @@ func (t *Ticket) MarshalCBOR(w io.Writer) error { return err } - if _, err := cw.Write(t.VRFProof[:]); err != nil { + if _, err := cw.Write(t.VRFProof); err != nil { return err } + return nil } @@ -555,7 +558,7 @@ func (t *Ticket) UnmarshalCBOR(r io.Reader) (err error) { return err } - if extra > cbg.ByteArrayMaxLen { + if extra > 2097152 { return fmt.Errorf("t.VRFProof: byte array too large (%d)", extra) } if maj != cbg.MajByteString { @@ -566,9 +569,10 @@ func (t *Ticket) UnmarshalCBOR(r io.Reader) (err error) { t.VRFProof = make([]uint8, extra) } - if _, err := io.ReadFull(cr, t.VRFProof[:]); err != nil { + if _, err := io.ReadFull(cr, t.VRFProof); err != nil { return err } + return nil } @@ -598,7 +602,7 @@ func (t *ElectionProof) MarshalCBOR(w io.Writer) error { } // t.VRFProof ([]uint8) (slice) - if len(t.VRFProof) > cbg.ByteArrayMaxLen { + if len(t.VRFProof) > 2097152 { return xerrors.Errorf("Byte array in field t.VRFProof was too long") } @@ -606,9 +610,10 @@ func (t *ElectionProof) MarshalCBOR(w io.Writer) error { return err } - if _, err := cw.Write(t.VRFProof[:]); err != nil { + if _, err := cw.Write(t.VRFProof); err != nil { return err } + return nil } @@ -638,10 +643,10 @@ func (t *ElectionProof) UnmarshalCBOR(r io.Reader) (err error) { // t.WinCount (int64) (int64) { maj, extra, err := cr.ReadHeader() - var extraI int64 if err != nil { return err } + var extraI int64 switch maj { case cbg.MajUnsignedInt: extraI = int64(extra) @@ -667,7 +672,7 @@ func (t *ElectionProof) UnmarshalCBOR(r io.Reader) (err error) { return err } - if extra > cbg.ByteArrayMaxLen { + if extra > 2097152 { return fmt.Errorf("t.VRFProof: byte array too large (%d)", extra) } if maj != cbg.MajByteString { @@ -678,9 +683,10 @@ func (t *ElectionProof) UnmarshalCBOR(r io.Reader) (err error) { t.VRFProof = make([]uint8, extra) } - if _, err := io.ReadFull(cr, t.VRFProof[:]); err != nil { + if _, err := io.ReadFull(cr, t.VRFProof); err != nil { return err } + return nil } @@ -753,7 +759,7 @@ func (t *Message) MarshalCBOR(w io.Writer) error { } // t.Params ([]uint8) (slice) - if len(t.Params) > cbg.ByteArrayMaxLen { + if len(t.Params) > 2097152 { return xerrors.Errorf("Byte array in field t.Params was too long") } @@ -761,9 +767,10 @@ func (t *Message) MarshalCBOR(w io.Writer) error { return err } - if _, err := cw.Write(t.Params[:]); err != nil { + if _, err := cw.Write(t.Params); err != nil { return err } + return nil } @@ -848,10 +855,10 @@ func (t *Message) UnmarshalCBOR(r io.Reader) (err error) { // t.GasLimit (int64) (int64) { maj, extra, err := cr.ReadHeader() - var extraI int64 if err != nil { return err } + var extraI int64 switch maj { case cbg.MajUnsignedInt: extraI = int64(extra) @@ -909,7 +916,7 @@ func (t *Message) UnmarshalCBOR(r io.Reader) (err error) { return err } - if extra > cbg.ByteArrayMaxLen { + if extra > 2097152 { return fmt.Errorf("t.Params: byte array too large (%d)", extra) } if maj != cbg.MajByteString { @@ -920,9 +927,10 @@ func (t *Message) UnmarshalCBOR(r io.Reader) (err error) { t.Params = make([]uint8, extra) } - if _, err := io.ReadFull(cr, t.Params[:]); err != nil { + if _, err := io.ReadFull(cr, t.Params); err != nil { return err } + return nil } @@ -1343,7 +1351,7 @@ func (t *BlockMsg) MarshalCBOR(w io.Writer) error { } // t.BlsMessages ([]cid.Cid) (slice) - if len(t.BlsMessages) > cbg.MaxLength { + if len(t.BlsMessages) > 8192 { return xerrors.Errorf("Slice value in field t.BlsMessages was too long") } @@ -1359,7 +1367,7 @@ func (t *BlockMsg) MarshalCBOR(w io.Writer) error { } // t.SecpkMessages ([]cid.Cid) (slice) - if len(t.SecpkMessages) > cbg.MaxLength { + if len(t.SecpkMessages) > 8192 { return xerrors.Errorf("Slice value in field t.SecpkMessages was too long") } @@ -1425,7 +1433,7 @@ func (t *BlockMsg) UnmarshalCBOR(r io.Reader) (err error) { return err } - if extra > cbg.MaxLength { + if extra > 8192 { return fmt.Errorf("t.BlsMessages: array too large (%d)", extra) } @@ -1456,9 +1464,9 @@ func (t *BlockMsg) UnmarshalCBOR(r io.Reader) (err error) { t.BlsMessages[i] = c } + } } - // t.SecpkMessages ([]cid.Cid) (slice) maj, extra, err = cr.ReadHeader() @@ -1466,7 +1474,7 @@ func (t *BlockMsg) UnmarshalCBOR(r io.Reader) (err error) { return err } - if extra > cbg.MaxLength { + if extra > 8192 { return fmt.Errorf("t.SecpkMessages: array too large (%d)", extra) } @@ -1497,9 +1505,9 @@ func (t *BlockMsg) UnmarshalCBOR(r io.Reader) (err error) { t.SecpkMessages[i] = c } + } } - return nil } @@ -1518,7 +1526,7 @@ func (t *ExpTipSet) MarshalCBOR(w io.Writer) error { } // t.Cids ([]cid.Cid) (slice) - if len(t.Cids) > cbg.MaxLength { + if len(t.Cids) > 8192 { return xerrors.Errorf("Slice value in field t.Cids was too long") } @@ -1534,7 +1542,7 @@ func (t *ExpTipSet) MarshalCBOR(w io.Writer) error { } // t.Blocks ([]*types.BlockHeader) (slice) - if len(t.Blocks) > cbg.MaxLength { + if len(t.Blocks) > 8192 { return xerrors.Errorf("Slice value in field t.Blocks was too long") } @@ -1545,6 +1553,7 @@ func (t *ExpTipSet) MarshalCBOR(w io.Writer) error { if err := v.MarshalCBOR(cw); err != nil { return err } + } // t.Height (abi.ChainEpoch) (int64) @@ -1557,6 +1566,7 @@ func (t *ExpTipSet) MarshalCBOR(w io.Writer) error { return err } } + return nil } @@ -1590,7 +1600,7 @@ func (t *ExpTipSet) UnmarshalCBOR(r io.Reader) (err error) { return err } - if extra > cbg.MaxLength { + if extra > 8192 { return fmt.Errorf("t.Cids: array too large (%d)", extra) } @@ -1621,9 +1631,9 @@ func (t *ExpTipSet) UnmarshalCBOR(r io.Reader) (err error) { t.Cids[i] = c } + } } - // t.Blocks ([]*types.BlockHeader) (slice) maj, extra, err = cr.ReadHeader() @@ -1631,7 +1641,7 @@ func (t *ExpTipSet) UnmarshalCBOR(r io.Reader) (err error) { return err } - if extra > cbg.MaxLength { + if extra > 8192 { return fmt.Errorf("t.Blocks: array too large (%d)", extra) } @@ -1669,16 +1679,16 @@ func (t *ExpTipSet) UnmarshalCBOR(r io.Reader) (err error) { } } + } } - // t.Height (abi.ChainEpoch) (int64) { maj, extra, err := cr.ReadHeader() - var extraI int64 if err != nil { return err } + var extraI int64 switch maj { case cbg.MajUnsignedInt: extraI = int64(extra) @@ -1721,7 +1731,7 @@ func (t *BeaconEntry) MarshalCBOR(w io.Writer) error { } // t.Data ([]uint8) (slice) - if len(t.Data) > cbg.ByteArrayMaxLen { + if len(t.Data) > 2097152 { return xerrors.Errorf("Byte array in field t.Data was too long") } @@ -1729,9 +1739,10 @@ func (t *BeaconEntry) MarshalCBOR(w io.Writer) error { return err } - if _, err := cw.Write(t.Data[:]); err != nil { + if _, err := cw.Write(t.Data); err != nil { return err } + return nil } @@ -1779,7 +1790,7 @@ func (t *BeaconEntry) UnmarshalCBOR(r io.Reader) (err error) { return err } - if extra > cbg.ByteArrayMaxLen { + if extra > 2097152 { return fmt.Errorf("t.Data: byte array too large (%d)", extra) } if maj != cbg.MajByteString { @@ -1790,9 +1801,10 @@ func (t *BeaconEntry) UnmarshalCBOR(r io.Reader) (err error) { t.Data = make([]uint8, extra) } - if _, err := io.ReadFull(cr, t.Data[:]); err != nil { + if _, err := io.ReadFull(cr, t.Data); err != nil { return err } + return nil } @@ -1908,6 +1920,7 @@ func (t *StateInfo0) MarshalCBOR(w io.Writer) error { if _, err := cw.Write(lengthBufStateInfo0); err != nil { return err } + return nil } @@ -1958,7 +1971,7 @@ func (t *Event) MarshalCBOR(w io.Writer) error { } // t.Entries ([]types.EventEntry) (slice) - if len(t.Entries) > cbg.MaxLength { + if len(t.Entries) > 8192 { return xerrors.Errorf("Slice value in field t.Entries was too long") } @@ -1969,6 +1982,7 @@ func (t *Event) MarshalCBOR(w io.Writer) error { if err := v.MarshalCBOR(cw); err != nil { return err } + } return nil } @@ -2017,7 +2031,7 @@ func (t *Event) UnmarshalCBOR(r io.Reader) (err error) { return err } - if extra > cbg.MaxLength { + if extra > 8192 { return fmt.Errorf("t.Entries: array too large (%d)", extra) } @@ -2045,9 +2059,9 @@ func (t *Event) UnmarshalCBOR(r io.Reader) (err error) { } } + } } - return nil } @@ -2071,7 +2085,7 @@ func (t *EventEntry) MarshalCBOR(w io.Writer) error { } // t.Key (string) (string) - if len(t.Key) > cbg.MaxLength { + if len(t.Key) > 8192 { return xerrors.Errorf("Value in field t.Key was too long") } @@ -2089,7 +2103,7 @@ func (t *EventEntry) MarshalCBOR(w io.Writer) error { } // t.Value ([]uint8) (slice) - if len(t.Value) > cbg.ByteArrayMaxLen { + if len(t.Value) > 2097152 { return xerrors.Errorf("Byte array in field t.Value was too long") } @@ -2097,9 +2111,10 @@ func (t *EventEntry) MarshalCBOR(w io.Writer) error { return err } - if _, err := cw.Write(t.Value[:]); err != nil { + if _, err := cw.Write(t.Value); err != nil { return err } + return nil } @@ -2142,7 +2157,7 @@ func (t *EventEntry) UnmarshalCBOR(r io.Reader) (err error) { // t.Key (string) (string) { - sval, err := cbg.ReadString(cr) + sval, err := cbg.ReadStringWithMax(cr, 8192) if err != nil { return err } @@ -2170,7 +2185,7 @@ func (t *EventEntry) UnmarshalCBOR(r io.Reader) (err error) { return err } - if extra > cbg.ByteArrayMaxLen { + if extra > 2097152 { return fmt.Errorf("t.Value: byte array too large (%d)", extra) } if maj != cbg.MajByteString { @@ -2181,9 +2196,10 @@ func (t *EventEntry) UnmarshalCBOR(r io.Reader) (err error) { t.Value = make([]uint8, extra) } - if _, err := io.ReadFull(cr, t.Value[:]); err != nil { + if _, err := io.ReadFull(cr, t.Value); err != nil { return err } + return nil } @@ -2202,7 +2218,7 @@ func (t *GasTrace) MarshalCBOR(w io.Writer) error { } // t.Name (string) (string) - if len(t.Name) > cbg.MaxLength { + if len(t.Name) > 8192 { return xerrors.Errorf("Value in field t.Name was too long") } @@ -2256,6 +2272,7 @@ func (t *GasTrace) MarshalCBOR(w io.Writer) error { return err } } + return nil } @@ -2285,7 +2302,7 @@ func (t *GasTrace) UnmarshalCBOR(r io.Reader) (err error) { // t.Name (string) (string) { - sval, err := cbg.ReadString(cr) + sval, err := cbg.ReadStringWithMax(cr, 8192) if err != nil { return err } @@ -2295,10 +2312,10 @@ func (t *GasTrace) UnmarshalCBOR(r io.Reader) (err error) { // t.TotalGas (int64) (int64) { maj, extra, err := cr.ReadHeader() - var extraI int64 if err != nil { return err } + var extraI int64 switch maj { case cbg.MajUnsignedInt: extraI = int64(extra) @@ -2320,10 +2337,10 @@ func (t *GasTrace) UnmarshalCBOR(r io.Reader) (err error) { // t.ComputeGas (int64) (int64) { maj, extra, err := cr.ReadHeader() - var extraI int64 if err != nil { return err } + var extraI int64 switch maj { case cbg.MajUnsignedInt: extraI = int64(extra) @@ -2345,10 +2362,10 @@ func (t *GasTrace) UnmarshalCBOR(r io.Reader) (err error) { // t.StorageGas (int64) (int64) { maj, extra, err := cr.ReadHeader() - var extraI int64 if err != nil { return err } + var extraI int64 switch maj { case cbg.MajUnsignedInt: extraI = int64(extra) @@ -2370,10 +2387,10 @@ func (t *GasTrace) UnmarshalCBOR(r io.Reader) (err error) { // t.TimeTaken (time.Duration) (int64) { maj, extra, err := cr.ReadHeader() - var extraI int64 if err != nil { return err } + var extraI int64 switch maj { case cbg.MajUnsignedInt: extraI = int64(extra) @@ -2507,7 +2524,7 @@ func (t *MessageTrace) MarshalCBOR(w io.Writer) error { } // t.Params ([]uint8) (slice) - if len(t.Params) > cbg.ByteArrayMaxLen { + if len(t.Params) > 2097152 { return xerrors.Errorf("Byte array in field t.Params was too long") } @@ -2515,7 +2532,7 @@ func (t *MessageTrace) MarshalCBOR(w io.Writer) error { return err } - if _, err := cw.Write(t.Params[:]); err != nil { + if _, err := cw.Write(t.Params); err != nil { return err } @@ -2609,7 +2626,7 @@ func (t *MessageTrace) UnmarshalCBOR(r io.Reader) (err error) { return err } - if extra > cbg.ByteArrayMaxLen { + if extra > 2097152 { return fmt.Errorf("t.Params: byte array too large (%d)", extra) } if maj != cbg.MajByteString { @@ -2620,9 +2637,10 @@ func (t *MessageTrace) UnmarshalCBOR(r io.Reader) (err error) { t.Params = make([]uint8, extra) } - if _, err := io.ReadFull(cr, t.Params[:]); err != nil { + if _, err := io.ReadFull(cr, t.Params); err != nil { return err } + // t.ParamsCodec (uint64) (uint64) { @@ -2697,7 +2715,7 @@ func (t *ReturnTrace) MarshalCBOR(w io.Writer) error { } // t.Return ([]uint8) (slice) - if len(t.Return) > cbg.ByteArrayMaxLen { + if len(t.Return) > 2097152 { return xerrors.Errorf("Byte array in field t.Return was too long") } @@ -2705,7 +2723,7 @@ func (t *ReturnTrace) MarshalCBOR(w io.Writer) error { return err } - if _, err := cw.Write(t.Return[:]); err != nil { + if _, err := cw.Write(t.Return); err != nil { return err } @@ -2744,10 +2762,10 @@ func (t *ReturnTrace) UnmarshalCBOR(r io.Reader) (err error) { // t.ExitCode (exitcode.ExitCode) (int64) { maj, extra, err := cr.ReadHeader() - var extraI int64 if err != nil { return err } + var extraI int64 switch maj { case cbg.MajUnsignedInt: extraI = int64(extra) @@ -2773,7 +2791,7 @@ func (t *ReturnTrace) UnmarshalCBOR(r io.Reader) (err error) { return err } - if extra > cbg.ByteArrayMaxLen { + if extra > 2097152 { return fmt.Errorf("t.Return: byte array too large (%d)", extra) } if maj != cbg.MajByteString { @@ -2784,9 +2802,10 @@ func (t *ReturnTrace) UnmarshalCBOR(r io.Reader) (err error) { t.Return = make([]uint8, extra) } - if _, err := io.ReadFull(cr, t.Return[:]); err != nil { + if _, err := io.ReadFull(cr, t.Return); err != nil { return err } + // t.ReturnCodec (uint64) (uint64) { @@ -2845,6 +2864,7 @@ func (t *ExecutionTrace) MarshalCBOR(w io.Writer) error { if err := v.MarshalCBOR(cw); err != nil { return err } + } // t.Subcalls ([]types.ExecutionTrace) (slice) @@ -2859,6 +2879,7 @@ func (t *ExecutionTrace) MarshalCBOR(w io.Writer) error { if err := v.MarshalCBOR(cw); err != nil { return err } + } return nil } @@ -2968,9 +2989,9 @@ func (t *ExecutionTrace) UnmarshalCBOR(r io.Reader) (err error) { } } + } } - // t.Subcalls ([]types.ExecutionTrace) (slice) maj, extra, err = cr.ReadHeader() @@ -3006,8 +3027,8 @@ func (t *ExecutionTrace) UnmarshalCBOR(r io.Reader) (err error) { } } + } } - return nil } diff --git a/chain/types/ethtypes/eth_types.go b/chain/types/ethtypes/eth_types.go index acc0d5c94..35fed87d8 100644 --- a/chain/types/ethtypes/eth_types.go +++ b/chain/types/ethtypes/eth_types.go @@ -610,7 +610,7 @@ type EthFilterSpec struct { Topics EthTopicSpec `json:"topics"` // Restricts event logs returned to those emitted from messages contained in this tipset. - // If BlockHash is present in in the filter criteria, then neither FromBlock nor ToBlock are allowed. + // If BlockHash is present in the filter criteria, then neither FromBlock nor ToBlock are allowed. // Added in EIP-234 BlockHash *EthHash `json:"blockHash,omitempty"` } diff --git a/chain/types/event.go b/chain/types/event.go index 106a120e2..5f6415d49 100644 --- a/chain/types/event.go +++ b/chain/types/event.go @@ -28,7 +28,7 @@ type EventEntry struct { // The event value's codec Codec uint64 - // The event value + // The event value. It is encoded using the codec specified above Value []byte } diff --git a/cli/client.go b/cli/client.go index 88f7ed208..81299b8fb 100644 --- a/cli/client.go +++ b/cli/client.go @@ -1770,7 +1770,7 @@ func dealFromDealInfo(ctx context.Context, full v0api.FullNode, head *types.TipS if v.DealID == 0 { return deal{ LocalDeal: v, - OnChainDealState: *market.EmptyDealState(), + OnChainDealState: market.EmptyDealState(), } } @@ -1781,7 +1781,7 @@ func dealFromDealInfo(ctx context.Context, full v0api.FullNode, head *types.TipS return deal{ LocalDeal: v, - OnChainDealState: onChain.State, + OnChainDealState: onChain.State.Iface(), } } @@ -1807,13 +1807,13 @@ func outputStorageDeals(ctx context.Context, out io.Writer, full v0api.FullNode, fmt.Fprintf(w, "Created\tDealCid\tDealId\tProvider\tState\tOn Chain?\tSlashed?\tPieceCID\tSize\tPrice\tDuration\tTransferChannelID\tTransferStatus\tVerified\tMessage\n") for _, d := range deals { onChain := "N" - if d.OnChainDealState.SectorStartEpoch != -1 { - onChain = fmt.Sprintf("Y (epoch %d)", d.OnChainDealState.SectorStartEpoch) + if d.OnChainDealState.SectorStartEpoch() != -1 { + onChain = fmt.Sprintf("Y (epoch %d)", d.OnChainDealState.SectorStartEpoch()) } slashed := "N" - if d.OnChainDealState.SlashEpoch != -1 { - slashed = fmt.Sprintf("Y (epoch %d)", d.OnChainDealState.SlashEpoch) + if d.OnChainDealState.SlashEpoch() != -1 { + slashed = fmt.Sprintf("Y (epoch %d)", d.OnChainDealState.SlashEpoch()) } price := types.FIL(types.BigMul(d.LocalDeal.PricePerEpoch, types.NewInt(d.LocalDeal.Duration))) @@ -1869,13 +1869,13 @@ func outputStorageDeals(ctx context.Context, out io.Writer, full v0api.FullNode, propcid := ellipsis(d.LocalDeal.ProposalCid.String(), 8) onChain := "N" - if d.OnChainDealState.SectorStartEpoch != -1 { - onChain = fmt.Sprintf("Y (epoch %d)", d.OnChainDealState.SectorStartEpoch) + if d.OnChainDealState.SectorStartEpoch() != -1 { + onChain = fmt.Sprintf("Y (epoch %d)", d.OnChainDealState.SectorStartEpoch()) } slashed := "N" - if d.OnChainDealState.SlashEpoch != -1 { - slashed = fmt.Sprintf("Y (epoch %d)", d.OnChainDealState.SlashEpoch) + if d.OnChainDealState.SlashEpoch() != -1 { + slashed = fmt.Sprintf("Y (epoch %d)", d.OnChainDealState.SlashEpoch()) } piece := ellipsis(d.LocalDeal.PieceCID.String(), 8) diff --git a/cli/filplus.go b/cli/filplus.go index 569229439..a4982cecb 100644 --- a/cli/filplus.go +++ b/cli/filplus.go @@ -7,6 +7,7 @@ import ( "fmt" "os" "strconv" + "strings" cbor "github.com/ipfs/go-ipld-cbor" "github.com/urfave/cli/v2" @@ -233,16 +234,21 @@ var filplusListClientsCmd = &cli.Command{ var filplusListAllocationsCmd = &cli.Command{ Name: "list-allocations", - Usage: "List allocations made by client", + Usage: "List allocations available in verified registry actor or made by a client if specified", ArgsUsage: "clientAddress", Flags: []cli.Flag{ &cli.BoolFlag{ Name: "expired", Usage: "list only expired allocations", }, + &cli.BoolFlag{ + Name: "json", + Usage: "output results in json format", + Value: false, + }, }, Action: func(cctx *cli.Context) error { - if cctx.NArg() != 1 { + if cctx.NArg() > 1 { return IncorrectNumArgs(cctx) } @@ -253,14 +259,76 @@ var filplusListAllocationsCmd = &cli.Command{ defer closer() ctx := ReqContext(cctx) - clientAddr, err := address.NewFromString(cctx.Args().Get(0)) - if err != nil { - return err - } + writeOut := func(tsHeight abi.ChainEpoch, allocations map[verifreg.AllocationId]verifreg.Allocation, json, expired bool) error { + // Map Keys. Corresponds to the standard tablewriter output + allocationID := "AllocationID" + client := "Client" + provider := "Miner" + pieceCid := "PieceCid" + pieceSize := "PieceSize" + tMin := "TermMin" + tMax := "TermMax" + expr := "Expiration" - clientIdAddr, err := api.StateLookupID(ctx, clientAddr, types.EmptyTSK) - if err != nil { - return err + // One-to-one mapping between tablewriter keys and JSON keys + tableKeysToJsonKeys := map[string]string{ + allocationID: strings.ToLower(allocationID), + client: strings.ToLower(client), + provider: strings.ToLower(provider), + pieceCid: strings.ToLower(pieceCid), + pieceSize: strings.ToLower(pieceSize), + tMin: strings.ToLower(tMin), + tMax: strings.ToLower(tMax), + expr: strings.ToLower(expr), + } + + var allocs []map[string]interface{} + + for key, val := range allocations { + if tsHeight > val.Expiration || !expired { + alloc := map[string]interface{}{ + allocationID: key, + client: val.Client, + provider: val.Provider, + pieceCid: val.Data, + pieceSize: val.Size, + tMin: val.TermMin, + tMax: val.TermMax, + expr: val.Expiration, + } + allocs = append(allocs, alloc) + } + } + + if json { + // get a new list of allocations with json keys instead of tablewriter keys + var jsonAllocs []map[string]interface{} + for _, alloc := range allocs { + jsonAlloc := make(map[string]interface{}) + for k, v := range alloc { + jsonAlloc[tableKeysToJsonKeys[k]] = v + } + jsonAllocs = append(jsonAllocs, jsonAlloc) + } + // then return this! + return PrintJson(jsonAllocs) + } + // Init the tablewriter's columns + tw := tablewriter.New( + tablewriter.Col(allocationID), + tablewriter.Col(client), + tablewriter.Col(provider), + tablewriter.Col(pieceCid), + tablewriter.Col(pieceSize), + tablewriter.Col(tMin), + tablewriter.Col(tMax), + tablewriter.NewLineCol(expr)) + // populate it with content + for _, alloc := range allocs { + tw.Write(alloc) + } + // return the corresponding string + return tw.Flush(os.Stdout) } store := adt.WrapStore(ctx, cbor.NewCborStore(blockstore.NewAPIBlockstore(api))) @@ -280,41 +348,38 @@ var filplusListAllocationsCmd = &cli.Command{ return err } - allocationsMap, err := verifregState.GetAllocations(clientIdAddr) + if cctx.NArg() == 1 { + clientAddr, err := address.NewFromString(cctx.Args().Get(0)) + if err != nil { + return err + } + + clientIdAddr, err := api.StateLookupID(ctx, clientAddr, types.EmptyTSK) + if err != nil { + return err + } + + allocationsMap, err := verifregState.GetAllocations(clientIdAddr) + if err != nil { + return err + } + + return writeOut(ts.Height(), allocationsMap, cctx.Bool("json"), cctx.Bool("expired")) + } + + allocationsMap, err := verifregState.GetAllAllocations() if err != nil { return err } - tw := tablewriter.New( - tablewriter.Col("ID"), - tablewriter.Col("Provider"), - tablewriter.Col("Data"), - tablewriter.Col("Size"), - tablewriter.Col("TermMin"), - tablewriter.Col("TermMax"), - tablewriter.Col("Expiration"), - ) + return writeOut(ts.Height(), allocationsMap, cctx.Bool("json"), cctx.Bool("expired")) - for allocationId, allocation := range allocationsMap { - if ts.Height() > allocation.Expiration || !cctx.IsSet("expired") { - tw.Write(map[string]interface{}{ - "ID": allocationId, - "Provider": allocation.Provider, - "Data": allocation.Data, - "Size": allocation.Size, - "TermMin": allocation.TermMin, - "TermMax": allocation.TermMax, - "Expiration": allocation.Expiration, - }) - } - } - return tw.Flush(os.Stdout) }, } var filplusListClaimsCmd = &cli.Command{ Name: "list-claims", - Usage: "List claims made by provider", + Usage: "List claims available in verified registry actor or made by provider if specified", ArgsUsage: "providerAddress", Flags: []cli.Flag{ &cli.BoolFlag{ @@ -323,7 +388,7 @@ var filplusListClaimsCmd = &cli.Command{ }, }, Action: func(cctx *cli.Context) error { - if cctx.NArg() != 1 { + if cctx.NArg() > 1 { return IncorrectNumArgs(cctx) } @@ -334,14 +399,81 @@ var filplusListClaimsCmd = &cli.Command{ defer closer() ctx := ReqContext(cctx) - providerAddr, err := address.NewFromString(cctx.Args().Get(0)) - if err != nil { - return err - } + writeOut := func(tsHeight abi.ChainEpoch, claims map[verifreg.ClaimId]verifreg.Claim, json, expired bool) error { + // Map Keys. Corresponds to the standard tablewriter output + claimID := "ClaimID" + provider := "Provider" + client := "Client" + data := "Data" + size := "Size" + tMin := "TermMin" + tMax := "TermMax" + tStart := "TermStart" + sector := "Sector" - providerIdAddr, err := api.StateLookupID(ctx, providerAddr, types.EmptyTSK) - if err != nil { - return err + // One-to-one mapping between tablewriter keys and JSON keys + tableKeysToJsonKeys := map[string]string{ + claimID: strings.ToLower(claimID), + provider: strings.ToLower(provider), + client: strings.ToLower(client), + data: strings.ToLower(data), + size: strings.ToLower(size), + tMin: strings.ToLower(tMin), + tMax: strings.ToLower(tMax), + tStart: strings.ToLower(tStart), + sector: strings.ToLower(sector), + } + + var claimList []map[string]interface{} + + for key, val := range claims { + if tsHeight > val.TermStart+val.TermMax || !expired { + claim := map[string]interface{}{ + claimID: key, + provider: val.Provider, + client: val.Client, + data: val.Data, + size: val.Size, + tMin: val.TermMin, + tMax: val.TermMax, + tStart: val.TermStart, + sector: val.Sector, + } + claimList = append(claimList, claim) + } + } + + if json { + // get a new list of claims with json keys instead of tablewriter keys + var jsonClaims []map[string]interface{} + for _, claim := range claimList { + jsonClaim := make(map[string]interface{}) + for k, v := range claim { + jsonClaim[tableKeysToJsonKeys[k]] = v + } + jsonClaims = append(jsonClaims, jsonClaim) + } + // then return this! + return PrintJson(jsonClaims) + } + // Init the tablewriter's columns + tw := tablewriter.New( + tablewriter.Col(claimID), + tablewriter.Col(client), + tablewriter.Col(provider), + tablewriter.Col(data), + tablewriter.Col(size), + tablewriter.Col(tMin), + tablewriter.Col(tMax), + tablewriter.Col(tStart), + tablewriter.NewLineCol(sector)) + // populate it with content + for _, alloc := range claimList { + + tw.Write(alloc) + } + // return the corresponding string + return tw.Flush(os.Stdout) } store := adt.WrapStore(ctx, cbor.NewCborStore(blockstore.NewAPIBlockstore(api))) @@ -361,39 +493,31 @@ var filplusListClaimsCmd = &cli.Command{ return err } - claimsMap, err := verifregState.GetClaims(providerIdAddr) + if cctx.NArg() == 1 { + providerAddr, err := address.NewFromString(cctx.Args().Get(0)) + if err != nil { + return err + } + + providerIdAddr, err := api.StateLookupID(ctx, providerAddr, types.EmptyTSK) + if err != nil { + return err + } + + claimsMap, err := verifregState.GetClaims(providerIdAddr) + if err != nil { + return err + } + + return writeOut(ts.Height(), claimsMap, cctx.Bool("json"), cctx.Bool("expired")) + } + + claimsMap, err := verifregState.GetAllClaims() if err != nil { return err } - tw := tablewriter.New( - tablewriter.Col("ID"), - tablewriter.Col("Provider"), - tablewriter.Col("Client"), - tablewriter.Col("Data"), - tablewriter.Col("Size"), - tablewriter.Col("TermMin"), - tablewriter.Col("TermMax"), - tablewriter.Col("TermStart"), - tablewriter.Col("Sector"), - ) - - for claimId, claim := range claimsMap { - if ts.Height() > claim.TermMax+claim.TermStart || !cctx.IsSet("expired") { - tw.Write(map[string]interface{}{ - "ID": claimId, - "Provider": claim.Provider, - "Client": claim.Client, - "Data": claim.Data, - "Size": claim.Size, - "TermMin": claim.TermMin, - "TermMax": claim.TermMax, - "TermStart": claim.TermStart, - "Sector": claim.Sector, - }) - } - } - return tw.Flush(os.Stdout) + return writeOut(ts.Height(), claimsMap, cctx.Bool("json"), cctx.Bool("expired")) }, } diff --git a/cli/util.go b/cli/util.go index 03de817f9..de161f590 100644 --- a/cli/util.go +++ b/cli/util.go @@ -2,6 +2,8 @@ package cli import ( "context" + "encoding/json" + "fmt" "os" "github.com/fatih/color" @@ -37,3 +39,13 @@ func parseTipSet(ctx context.Context, api v0api.FullNode, vals []string) (*types return types.NewTipSet(headers) } + +func PrintJson(obj interface{}) error { + resJson, err := json.MarshalIndent(obj, "", " ") + if err != nil { + return fmt.Errorf("marshalling json: %w", err) + } + + fmt.Println(string(resJson)) + return nil +} diff --git a/cmd/lotus-bench/amt_internal.go b/cmd/lotus-bench/amt_internal.go deleted file mode 100644 index f0e3035b7..000000000 --- a/cmd/lotus-bench/amt_internal.go +++ /dev/null @@ -1,312 +0,0 @@ -// Copied from go-amt-ipld https://github.com/filecoin-project/go-amt-ipld/tree/master/internal -// which for some reason is a go internal package and therefore cannot be imported - -package main - -import ( - "fmt" - "io" - "math" - "sort" - - cid "github.com/ipfs/go-cid" - cbg "github.com/whyrusleeping/cbor-gen" - xerrors "golang.org/x/xerrors" -) - -type AMTRoot struct { - BitWidth uint64 - Height uint64 - Count uint64 - AMTNode AMTNode -} - -type AMTNode struct { - Bmap []byte - Links []cid.Cid - Values []*cbg.Deferred -} - -// Code generated by github.com/whyrusleeping/cbor-gen. DO NOT EDIT. - -var _ = xerrors.Errorf -var _ = cid.Undef -var _ = math.E -var _ = sort.Sort - -var lengthBufAMTRoot = []byte{132} - -func (t *AMTRoot) MarshalCBOR(w io.Writer) error { - if t == nil { - _, err := w.Write(cbg.CborNull) - return err - } - - cw := cbg.NewCborWriter(w) - - if _, err := cw.Write(lengthBufAMTRoot); err != nil { - return err - } - - // t.BitWidth (uint64) (uint64) - - if err := cw.WriteMajorTypeHeader(cbg.MajUnsignedInt, t.BitWidth); err != nil { - return err - } - - // t.Height (uint64) (uint64) - - if err := cw.WriteMajorTypeHeader(cbg.MajUnsignedInt, t.Height); err != nil { - return err - } - - // t.Count (uint64) (uint64) - - if err := cw.WriteMajorTypeHeader(cbg.MajUnsignedInt, t.Count); err != nil { - return err - } - - // t.AMTNode (internal.AMTNode) (struct) - if err := t.AMTNode.MarshalCBOR(cw); err != nil { - return err - } - return nil -} - -func (t *AMTRoot) UnmarshalCBOR(r io.Reader) (err error) { - *t = AMTRoot{} - - cr := cbg.NewCborReader(r) - - maj, extra, err := cr.ReadHeader() - if err != nil { - return err - } - defer func() { - if err == io.EOF { - err = io.ErrUnexpectedEOF - } - }() - - if maj != cbg.MajArray { - return fmt.Errorf("cbor input should be of type array") - } - - if extra != 4 { - return fmt.Errorf("cbor input had wrong number of fields") - } - - // t.BitWidth (uint64) (uint64) - - { - - maj, extra, err = cr.ReadHeader() - if err != nil { - return err - } - if maj != cbg.MajUnsignedInt { - return fmt.Errorf("wrong type for uint64 field") - } - t.BitWidth = extra - - } - // t.Height (uint64) (uint64) - - { - - maj, extra, err = cr.ReadHeader() - if err != nil { - return err - } - if maj != cbg.MajUnsignedInt { - return fmt.Errorf("wrong type for uint64 field") - } - t.Height = extra - - } - // t.Count (uint64) (uint64) - - { - - maj, extra, err = cr.ReadHeader() - if err != nil { - return err - } - if maj != cbg.MajUnsignedInt { - return fmt.Errorf("wrong type for uint64 field") - } - t.Count = extra - - } - // t.AMTNode (internal.AMTNode) (struct) - - { - - if err := t.AMTNode.UnmarshalCBOR(cr); err != nil { - return xerrors.Errorf("unmarshaling t.AMTNode: %w", err) - } - - } - return nil -} - -var lengthBufAMTNode = []byte{131} - -func (t *AMTNode) MarshalCBOR(w io.Writer) error { - if t == nil { - _, err := w.Write(cbg.CborNull) - return err - } - - cw := cbg.NewCborWriter(w) - - if _, err := cw.Write(lengthBufAMTNode); err != nil { - return err - } - - // t.Bmap ([]uint8) (slice) - if len(t.Bmap) > cbg.ByteArrayMaxLen { - return xerrors.Errorf("Byte array in field t.Bmap was too long") - } - - if err := cw.WriteMajorTypeHeader(cbg.MajByteString, uint64(len(t.Bmap))); err != nil { - return err - } - - if _, err := cw.Write(t.Bmap[:]); err != nil { - return err - } - - // t.Links ([]cid.Cid) (slice) - if len(t.Links) > cbg.MaxLength { - return xerrors.Errorf("Slice value in field t.Links was too long") - } - - if err := cw.WriteMajorTypeHeader(cbg.MajArray, uint64(len(t.Links))); err != nil { - return err - } - for _, v := range t.Links { - if err := cbg.WriteCid(w, v); err != nil { - return xerrors.Errorf("failed writing cid field t.Links: %w", err) - } - } - - // t.Values ([]*typegen.Deferred) (slice) - if len(t.Values) > cbg.MaxLength { - return xerrors.Errorf("Slice value in field t.Values was too long") - } - - if err := cw.WriteMajorTypeHeader(cbg.MajArray, uint64(len(t.Values))); err != nil { - return err - } - for _, v := range t.Values { - if err := v.MarshalCBOR(cw); err != nil { - return err - } - } - return nil -} - -func (t *AMTNode) UnmarshalCBOR(r io.Reader) (err error) { - *t = AMTNode{} - - cr := cbg.NewCborReader(r) - - maj, extra, err := cr.ReadHeader() - if err != nil { - return err - } - defer func() { - if err == io.EOF { - err = io.ErrUnexpectedEOF - } - }() - - if maj != cbg.MajArray { - return fmt.Errorf("cbor input should be of type array") - } - - if extra != 3 { - return fmt.Errorf("cbor input had wrong number of fields") - } - - // t.Bmap ([]uint8) (slice) - - maj, extra, err = cr.ReadHeader() - if err != nil { - return err - } - - if extra > cbg.ByteArrayMaxLen { - return fmt.Errorf("t.Bmap: byte array too large (%d)", extra) - } - if maj != cbg.MajByteString { - return fmt.Errorf("expected byte array") - } - - if extra > 0 { - t.Bmap = make([]uint8, extra) - } - - if _, err := io.ReadFull(cr, t.Bmap[:]); err != nil { - return err - } - // t.Links ([]cid.Cid) (slice) - - maj, extra, err = cr.ReadHeader() - if err != nil { - return err - } - - if extra > cbg.MaxLength { - return fmt.Errorf("t.Links: array too large (%d)", extra) - } - - if maj != cbg.MajArray { - return fmt.Errorf("expected cbor array") - } - - if extra > 0 { - t.Links = make([]cid.Cid, extra) - } - - for i := 0; i < int(extra); i++ { - - c, err := cbg.ReadCid(cr) - if err != nil { - return xerrors.Errorf("reading cid field t.Links failed: %w", err) - } - t.Links[i] = c - } - - // t.Values ([]*typegen.Deferred) (slice) - - maj, extra, err = cr.ReadHeader() - if err != nil { - return err - } - - if extra > cbg.MaxLength { - return fmt.Errorf("t.Values: array too large (%d)", extra) - } - - if maj != cbg.MajArray { - return fmt.Errorf("expected cbor array") - } - - if extra > 0 { - t.Values = make([]*cbg.Deferred, extra) - } - - for i := 0; i < int(extra); i++ { - - var v cbg.Deferred - if err := v.UnmarshalCBOR(cr); err != nil { - return err - } - - t.Values[i] = &v - } - - return nil -} diff --git a/cmd/lotus-bench/main.go b/cmd/lotus-bench/main.go index 7d3c0cde0..545ed1eb9 100644 --- a/cmd/lotus-bench/main.go +++ b/cmd/lotus-bench/main.go @@ -1,7 +1,6 @@ package main import ( - "bytes" "context" "crypto/rand" "encoding/json" @@ -9,16 +8,9 @@ import ( "math/big" "os" "path/filepath" - "sync" "time" "github.com/docker/go-units" - "github.com/ipfs/boxo/blockservice" - "github.com/ipfs/boxo/ipld/merkledag" - "github.com/ipfs/go-cid" - offline "github.com/ipfs/go-ipfs-exchange-offline" - cbor "github.com/ipfs/go-ipld-cbor" - format "github.com/ipfs/go-ipld-format" logging "github.com/ipfs/go-log/v2" "github.com/minio/blake2b-simd" "github.com/mitchellh/go-homedir" @@ -28,14 +20,10 @@ import ( "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-paramfetch" "github.com/filecoin-project/go-state-types/abi" - "github.com/filecoin-project/go-state-types/builtin/v9/verifreg" prooftypes "github.com/filecoin-project/go-state-types/proof" - adt "github.com/filecoin-project/specs-actors/v6/actors/util/adt" lapi "github.com/filecoin-project/lotus/api" - "github.com/filecoin-project/lotus/blockstore" "github.com/filecoin-project/lotus/build" - "github.com/filecoin-project/lotus/chain/actors/builtin/market" "github.com/filecoin-project/lotus/chain/actors/builtin/miner" "github.com/filecoin-project/lotus/chain/types" lcli "github.com/filecoin-project/lotus/cli" @@ -116,7 +104,6 @@ func main() { DisableSliceFlagSeparator: true, Commands: []*cli.Command{ proveCmd, - amtBenchCmd, sealBenchCmd, simpleCmd, importBenchCmd, @@ -131,211 +118,6 @@ func main() { } } -type amtStatCollector struct { - ds format.NodeGetter - walk func(format.Node) ([]*format.Link, error) - - statsLk sync.Mutex - totalAMTLinks int - totalAMTValues int - totalAMTLinkNodes int - totalAMTValueNodes int - totalAMTLinkNodeSize int - totalAMTValueNodeSize int -} - -func (asc *amtStatCollector) String() string { - asc.statsLk.Lock() - defer asc.statsLk.Unlock() - - str := "\n------------\n" - str += fmt.Sprintf("Link Count: %d\n", asc.totalAMTLinks) - str += fmt.Sprintf("Value Count: %d\n", asc.totalAMTValues) - str += fmt.Sprintf("%d link nodes %d bytes\n", asc.totalAMTLinkNodes, asc.totalAMTLinkNodeSize) - str += fmt.Sprintf("%d value nodes %d bytes\n", asc.totalAMTValueNodes, asc.totalAMTValueNodeSize) - str += fmt.Sprintf("Total bytes: %d\n------------\n", asc.totalAMTLinkNodeSize+asc.totalAMTValueNodeSize) - return str -} - -func (asc *amtStatCollector) record(ctx context.Context, nd format.Node) error { - size, err := nd.Size() - if err != nil { - return err - } - - var node AMTNode - if err := node.UnmarshalCBOR(bytes.NewReader(nd.RawData())); err != nil { - // try to deserialize root - var root AMTRoot - if err := root.UnmarshalCBOR(bytes.NewReader(nd.RawData())); err != nil { - return err - } - node = root.AMTNode - } - - asc.statsLk.Lock() - defer asc.statsLk.Unlock() - - link := len(node.Links) > 0 - value := len(node.Values) > 0 - - if link { - asc.totalAMTLinks += len(node.Links) - asc.totalAMTLinkNodes++ - asc.totalAMTLinkNodeSize += int(size) - } else if value { - asc.totalAMTValues += len(node.Values) - asc.totalAMTValueNodes++ - asc.totalAMTValueNodeSize += int(size) - } else { - return xerrors.Errorf("unexpected AMT node %x: neither link nor value", nd.RawData()) - } - - return nil -} - -func (asc *amtStatCollector) walkLinks(ctx context.Context, c cid.Cid) ([]*format.Link, error) { - nd, err := asc.ds.Get(ctx, c) - if err != nil { - return nil, err - } - - if err := asc.record(ctx, nd); err != nil { - return nil, err - } - - return asc.walk(nd) -} - -func carWalkFunc(nd format.Node) (out []*format.Link, err error) { - for _, link := range nd.Links() { - if link.Cid.Prefix().Codec == cid.FilCommitmentSealed || link.Cid.Prefix().Codec == cid.FilCommitmentUnsealed { - continue - } - out = append(out, link) - } - return out, nil -} - -var amtBenchCmd = &cli.Command{ - Name: "amt", - Usage: "Benchmark AMT churn", - Flags: []cli.Flag{ - &cli.IntFlag{ - Name: "rounds", - Usage: "rounds of churn to measure", - Value: 1, - }, - &cli.IntFlag{ - Name: "interval", - Usage: "AMT idx interval for churning values", - Value: 2880, - }, - &cli.IntFlag{ - Name: "bitwidth", - Usage: "AMT bitwidth", - Value: 6, - }, - }, - Action: func(c *cli.Context) error { - bs := blockstore.NewMemory() - ctx := c.Context - store := adt.WrapStore(ctx, cbor.NewCborStore(bs)) - - // Setup in memory blockstore - bitwidth := c.Int("bitwidth") - array, err := adt.MakeEmptyArray(store, bitwidth) - if err != nil { - return err - } - - // Using motivating empirical example: market actor states AMT - // Create 40,000,000 states for realistic workload - fmt.Printf("Populating AMT\n") - for i := 0; i < 40000000; i++ { - if err := array.Set(uint64(i), &market.DealState{ - SectorStartEpoch: abi.ChainEpoch(2000000 + i), - LastUpdatedEpoch: abi.ChainEpoch(-1), - SlashEpoch: -1, - VerifiedClaim: verifreg.AllocationId(i), - }); err != nil { - return err - } - } - - r, err := array.Root() - if err != nil { - return err - } - - // Measure ratio of internal / leaf nodes / sizes - dag := merkledag.NewDAGService(blockservice.New(bs, offline.Exchange(bs))) - asc := &amtStatCollector{ - ds: dag, - walk: carWalkFunc, - } - - fmt.Printf("Measuring AMT\n") - seen := cid.NewSet() - if err := merkledag.Walk(ctx, asc.walkLinks, r, seen.Visit, merkledag.Concurrent()); err != nil { - return err - } - - fmt.Printf("%s\n", asc) - - // Overwrite ids with idx % interval: one epoch of market cron - rounds := c.Int("rounds") - interval := c.Int("interval") - - fmt.Printf("Overwrite 1 out of %d values for %d rounds\n", interval, rounds) - array, err = adt.AsArray(store, r, bitwidth) - if err != nil { - return err - } - roots := make([]cid.Cid, rounds) - for j := 0; j < rounds; j++ { - if j%10 == 0 { - fmt.Printf("round: %d\n", j) - } - for i := j; i < 40000000; i += interval { - if i%interval == j { - if err := array.Set(uint64(i), &market.DealState{ - SectorStartEpoch: abi.ChainEpoch(2000000 + i), - LastUpdatedEpoch: abi.ChainEpoch(1), - SlashEpoch: -1, - VerifiedClaim: verifreg.AllocationId(i), - }); err != nil { - return err - } - } - } - roots[j], err = array.Root() - if err != nil { - return err - } - - } - - // Measure churn - dag = merkledag.NewDAGService(blockservice.New(bs, offline.Exchange(bs))) - asc = &amtStatCollector{ - ds: dag, - walk: carWalkFunc, - } - - fmt.Printf("Measuring %d rounds of churn\n", rounds) - - for _, r := range roots { - if err := merkledag.Walk(ctx, asc.walkLinks, r, seen.Visit, merkledag.Concurrent()); err != nil { - return err - } - } - - fmt.Printf("%s\n", asc) - return nil - }, -} - var sealBenchCmd = &cli.Command{ Name: "sealing", Usage: "Benchmark seal and winning post and window post", diff --git a/cmd/lotus-miner/init.go b/cmd/lotus-miner/init.go index 1a4a98fc4..9ab4e8b05 100644 --- a/cmd/lotus-miner/init.go +++ b/cmd/lotus-miner/init.go @@ -55,6 +55,7 @@ import ( "github.com/filecoin-project/lotus/storage" "github.com/filecoin-project/lotus/storage/paths" pipeline "github.com/filecoin-project/lotus/storage/pipeline" + "github.com/filecoin-project/lotus/storage/pipeline/piece" "github.com/filecoin-project/lotus/storage/sealer" "github.com/filecoin-project/lotus/storage/sealer/ffiwrapper" "github.com/filecoin-project/lotus/storage/sealer/storiface" @@ -327,21 +328,21 @@ func migratePreSealMeta(ctx context.Context, api v1api.FullNode, metadata string info := &pipeline.SectorInfo{ State: pipeline.Proving, SectorNumber: sector.SectorID, - Pieces: []lapi.SectorPiece{ - { + Pieces: []pipeline.SafeSectorPiece{ + pipeline.SafePiece(lapi.SectorPiece{ Piece: abi.PieceInfo{ Size: abi.PaddedPieceSize(meta.SectorSize), PieceCID: commD, }, - DealInfo: &lapi.PieceDealInfo{ + DealInfo: &piece.PieceDealInfo{ DealID: dealID, DealProposal: §or.Deal, - DealSchedule: lapi.DealSchedule{ + DealSchedule: piece.DealSchedule{ StartEpoch: sector.Deal.StartEpoch, EndEpoch: sector.Deal.EndEpoch, }, }, - }, + }), }, CommD: &commD, CommR: &commR, diff --git a/cmd/lotus-shed/fip-0036.go b/cmd/lotus-shed/fip-0036.go deleted file mode 100644 index 4c8456c04..000000000 --- a/cmd/lotus-shed/fip-0036.go +++ /dev/null @@ -1,554 +0,0 @@ -package main - -import ( - "context" - "encoding/json" - "fmt" - "io" - "os" - "sort" - "strconv" - - "github.com/ipfs/go-cid" - cbor "github.com/ipfs/go-ipld-cbor" - "github.com/mitchellh/go-homedir" - "github.com/urfave/cli/v2" - "golang.org/x/xerrors" - - "github.com/filecoin-project/go-address" - "github.com/filecoin-project/go-state-types/abi" - "github.com/filecoin-project/go-state-types/big" - - "github.com/filecoin-project/lotus/chain/actors/adt" - "github.com/filecoin-project/lotus/chain/actors/builtin" - "github.com/filecoin-project/lotus/chain/actors/builtin/market" - "github.com/filecoin-project/lotus/chain/actors/builtin/miner" - "github.com/filecoin-project/lotus/chain/actors/builtin/multisig" - "github.com/filecoin-project/lotus/chain/actors/builtin/power" - "github.com/filecoin-project/lotus/chain/consensus/filcns" - "github.com/filecoin-project/lotus/chain/state" - "github.com/filecoin-project/lotus/chain/store" - "github.com/filecoin-project/lotus/chain/types" - "github.com/filecoin-project/lotus/node/repo" -) - -type Option uint64 - -const ( - Approve Option = 49 - Reject Option = 50 -) - -type Vote struct { - ID uint64 - OptionID Option - SignerAddress address.Address -} - -type msigVote struct { - Multisig msigBriefInfo - ApproveCount uint64 - RejectCount uint64 -} - -// https://filpoll.io/poll/16 -// snapshot height: 2162760 -// state root: bafy2bzacebdnzh43hw66bmvguk65wiwr5ssaejlq44fpdei2ysfh3eefpdlqs -var fip36PollCmd = &cli.Command{ - Name: "fip36poll", - Usage: "Process the FIP0036 FilPoll result", - ArgsUsage: "[state root, votes]", - Flags: []cli.Flag{ - &cli.StringFlag{ - Name: "repo", - Value: "~/.lotus", - }, - }, - Subcommands: []*cli.Command{ - finalResultCmd, - }, -} - -var finalResultCmd = &cli.Command{ - Name: "results", - Usage: "get poll results", - ArgsUsage: "[state root] [height] [votes json]", - Flags: []cli.Flag{ - &cli.StringFlag{ - Name: "repo", - Value: "~/.lotus", - }, - }, - - Action: func(cctx *cli.Context) error { - if cctx.NArg() != 3 { - return xerrors.New("filpoll0036 results [state root] [height] [votes.json]") - } - - ctx := context.TODO() - if !cctx.Args().Present() { - return fmt.Errorf("must pass state root") - } - - sroot, err := cid.Decode(cctx.Args().First()) - if err != nil { - return fmt.Errorf("failed to parse input: %w", err) - } - - fsrepo, err := repo.NewFS(cctx.String("repo")) - if err != nil { - return err - } - - lkrepo, err := fsrepo.Lock(repo.FullNode) - if err != nil { - return err - } - - defer lkrepo.Close() //nolint:errcheck - - bs, err := lkrepo.Blockstore(ctx, repo.UniversalBlockstore) - if err != nil { - return fmt.Errorf("failed to open blockstore: %w", err) - } - - defer func() { - if c, ok := bs.(io.Closer); ok { - if err := c.Close(); err != nil { - log.Warnf("failed to close blockstore: %s", err) - } - } - }() - - mds, err := lkrepo.Datastore(context.Background(), "/metadata") - if err != nil { - return err - } - - cs := store.NewChainStore(bs, bs, mds, filcns.Weight, nil) - defer cs.Close() //nolint:errcheck - - cst := cbor.NewCborStore(bs) - store := adt.WrapStore(ctx, cst) - - st, err := state.LoadStateTree(cst, sroot) - if err != nil { - return err - } - - height, err := strconv.Atoi(cctx.Args().Get(1)) - if err != nil { - return err - } - - //get all the votes' signer ID address && their vote - vj, err := homedir.Expand(cctx.Args().Get(2)) - if err != nil { - return xerrors.Errorf("fail to get votes json") - } - votes, err := getVotesMap(vj) - if err != nil { - return xerrors.Errorf("failed to get voters: %w\n", err) - } - - type minerBriefInfo struct { - rawBytePower abi.StoragePower - dealPower abi.StoragePower - balance abi.TokenAmount - } - - // power actor - pa, err := st.GetActor(power.Address) - if err != nil { - return xerrors.Errorf("failed to get power actor: %w\n", err) - } - - powerState, err := power.Load(store, pa) - if err != nil { - return xerrors.Errorf("failed to get power state: %w\n", err) - } - - //market actor - ma, err := st.GetActor(market.Address) - if err != nil { - return xerrors.Errorf("fail to get market actor: %w\n", err) - } - - marketState, err := market.Load(store, ma) - if err != nil { - return xerrors.Errorf("fail to load market state: %w\n", err) - } - - lookupId := func(addr address.Address) address.Address { - ret, err := st.LookupID(addr) - if err != nil { - panic(err) - } - - return ret - } - - // we need to build several pieces of information, as we traverse the state tree: - // a map of accounts to every msig that they are a signer of - accountsToMultisigs := make(map[address.Address][]address.Address) - // a map of multisigs to some info about them for quick lookup - msigActorsInfo := make(map[address.Address]msigBriefInfo) - // a map of actors (accounts+multisigs) to every miner that they are an owner of - ownerMap := make(map[address.Address][]address.Address) - // a map of accounts to every miner that they are a worker of - workerMap := make(map[address.Address][]address.Address) - // a map of miners to some info about them for quick lookup - minerActorsInfo := make(map[address.Address]minerBriefInfo) - // a map of client addresses to deal data stored in proposals - clientToDealStorage := make(map[address.Address]abi.StoragePower) - - fmt.Println("iterating over all actors") - count := 0 - err = st.ForEach(func(addr address.Address, act *types.Actor) error { - if count%200000 == 0 { - fmt.Println("processed ", count, " actors building maps") - } - count++ - if builtin.IsMultisigActor(act.Code) { - ms, err := multisig.Load(store, act) - if err != nil { - return fmt.Errorf("load msig failed %v", err) - - } - - // TODO: Confirm that these are always ID addresses - signers, err := ms.Signers() - if err != nil { - return xerrors.Errorf("fail to get msig signers: %w", err) - } - for _, s := range signers { - signerId := lookupId(s) - accountsToMultisigs[signerId] = append(accountsToMultisigs[signerId], addr) - } - - locked, err := ms.LockedBalance(abi.ChainEpoch(height)) - if err != nil { - return xerrors.Errorf("failed to compute locked multisig balance: %w", err) - } - - threshold, _ := ms.Threshold() - info := msigBriefInfo{ - ID: addr, - Signer: signers, - Balance: big.Max(big.Zero(), types.BigSub(act.Balance, locked)), - Threshold: threshold, - } - msigActorsInfo[addr] = info - } - - if builtin.IsStorageMinerActor(act.Code) { - m, err := miner.Load(store, act) - if err != nil { - return xerrors.Errorf("fail to load miner actor: %w", err) - } - - info, err := m.Info() - if err != nil { - return xerrors.Errorf("fail to get miner info: %w\n", err) - } - - ownerId := lookupId(info.Owner) - ownerMap[ownerId] = append(ownerMap[ownerId], addr) - - workerId := lookupId(info.Worker) - workerMap[workerId] = append(workerMap[workerId], addr) - - lockedFunds, err := m.LockedFunds() - if err != nil { - return err - } - - bal := big.Sub(act.Balance, lockedFunds.TotalLockedFunds()) - bal = big.Max(big.Zero(), bal) - - pow, ok, err := powerState.MinerPower(addr) - if err != nil { - return err - } - - if !ok { - pow.RawBytePower = big.Zero() - } - - minerActorsInfo[addr] = minerBriefInfo{ - rawBytePower: pow.RawBytePower, - // gets added up outside this loop - dealPower: big.Zero(), - balance: bal, - } - } - - return nil - }) - - if err != nil { - return err - } - - fmt.Println("iterating over proposals") - dealProposals, err := marketState.Proposals() - if err != nil { - return err - } - - dealStates, err := marketState.States() - if err != nil { - return err - } - - if err := dealProposals.ForEach(func(dealID abi.DealID, d market.DealProposal) error { - - dealState, ok, err := dealStates.Get(dealID) - if err != nil { - return err - } - if !ok || dealState.SectorStartEpoch == -1 { - // effectively a continue - return nil - } - - clientId := lookupId(d.Client) - if cd, found := clientToDealStorage[clientId]; found { - clientToDealStorage[clientId] = big.Add(cd, big.NewInt(int64(d.PieceSize))) - } else { - clientToDealStorage[clientId] = big.NewInt(int64(d.PieceSize)) - } - - providerId := lookupId(d.Provider) - mai, found := minerActorsInfo[providerId] - - if !found { - return xerrors.Errorf("didn't find miner %s", providerId) - } - - mai.dealPower = big.Add(mai.dealPower, big.NewInt(int64(d.PieceSize))) - minerActorsInfo[providerId] = mai - return nil - }); err != nil { - return xerrors.Errorf("fail to get deals") - } - - // now tabulate votes - - approveBalance := abi.NewTokenAmount(0) - rejectionBalance := abi.NewTokenAmount(0) - clientApproveBytes := big.Zero() - clientRejectBytes := big.Zero() - msigPendingVotes := make(map[address.Address]msigVote) //map[msig ID]msigVote - msigVotes := make(map[address.Address]Option) - minerVotes := make(map[address.Address]Option) - fmt.Println("counting account and multisig votes") - for _, vote := range votes { - signerId, err := st.LookupID(vote.SignerAddress) - if err != nil { - fmt.Println("voter ", vote.SignerAddress, " not found in state tree, skipping") - continue - } - - //process votes for regular accounts - accountActor, err := st.GetActor(signerId) - if err != nil { - return xerrors.Errorf("fail to get account account for signer: %w\n", err) - } - - clientBytes, ok := clientToDealStorage[signerId] - if !ok { - clientBytes = big.Zero() - } - - if vote.OptionID == Approve { - approveBalance = types.BigAdd(approveBalance, accountActor.Balance) - clientApproveBytes = big.Add(clientApproveBytes, clientBytes) - } else { - rejectionBalance = types.BigAdd(rejectionBalance, accountActor.Balance) - clientRejectBytes = big.Add(clientRejectBytes, clientBytes) - } - - if minerInfos, found := ownerMap[signerId]; found { - for _, minerInfo := range minerInfos { - minerVotes[minerInfo] = vote.OptionID - } - } - if minerInfos, found := workerMap[signerId]; found { - for _, minerInfo := range minerInfos { - if _, ok := minerVotes[minerInfo]; !ok { - minerVotes[minerInfo] = vote.OptionID - } - } - } - - //process msigs - // There is a possibility that enough signers have voted for BOTH options in the poll to be above the threshold - // Because we are iterating over votes in order they arrived, the first option to go over the threshold will win - // This is in line with onchain behaviour (consider a case where signers are competing to withdraw all the funds - // in an msig into 2 different accounts) - if mss, found := accountsToMultisigs[signerId]; found { - for _, ms := range mss { //get all the msig signer has - if _, ok := msigVotes[ms]; ok { - // msig has already voted, skip - continue - } - if mpv, found := msigPendingVotes[ms]; found { //other signers of the multisig have voted, yet the threshold has not met - if vote.OptionID == Approve { - if mpv.ApproveCount+1 == mpv.Multisig.Threshold { //met threshold - approveBalance = types.BigAdd(approveBalance, mpv.Multisig.Balance) - delete(msigPendingVotes, ms) //threshold, can skip later signer votes - msigVotes[ms] = vote.OptionID - - } else { - mpv.ApproveCount++ - msigPendingVotes[ms] = mpv - } - } else { - if mpv.RejectCount+1 == mpv.Multisig.Threshold { //met threshold - rejectionBalance = types.BigAdd(rejectionBalance, mpv.Multisig.Balance) - delete(msigPendingVotes, ms) //threshold, can skip later signer votes - msigVotes[ms] = vote.OptionID - - } else { - mpv.RejectCount++ - msigPendingVotes[ms] = mpv - } - } - } else { //first vote received from one of the signers of the msig - msi, ok := msigActorsInfo[ms] - if !ok { - return xerrors.Errorf("didn't find msig %s in msig map", ms) - } - - if msi.Threshold == 1 { //met threshold with this signer's single vote - if vote.OptionID == Approve { - approveBalance = types.BigAdd(approveBalance, msi.Balance) - msigVotes[ms] = Approve - - } else { - rejectionBalance = types.BigAdd(rejectionBalance, msi.Balance) - msigVotes[ms] = Reject - } - } else { //threshold not met, add to pending vote - if vote.OptionID == Approve { - msigPendingVotes[ms] = msigVote{ - Multisig: msi, - ApproveCount: 1, - } - } else { - msigPendingVotes[ms] = msigVote{ - Multisig: msi, - RejectCount: 1, - } - } - } - } - } - } - } - - for s, v := range msigVotes { - if minerInfos, found := ownerMap[s]; found { - for _, minerInfo := range minerInfos { - minerVotes[minerInfo] = v - } - } - if minerInfos, found := workerMap[s]; found { - for _, minerInfo := range minerInfos { - if _, ok := minerVotes[minerInfo]; !ok { - minerVotes[minerInfo] = v - } - } - } - } - - approveRBP := big.Zero() - approveDealPower := big.Zero() - rejectionRBP := big.Zero() - rejectionDealPower := big.Zero() - fmt.Println("adding up miner votes") - for minerAddr, vote := range minerVotes { - mbi, ok := minerActorsInfo[minerAddr] - if !ok { - return xerrors.Errorf("failed to find miner info for %s", minerAddr) - } - - if vote == Approve { - approveBalance = big.Add(approveBalance, mbi.balance) - approveRBP = big.Add(approveRBP, mbi.rawBytePower) - approveDealPower = big.Add(approveDealPower, mbi.dealPower) - } else { - rejectionBalance = big.Add(rejectionBalance, mbi.balance) - rejectionRBP = big.Add(rejectionRBP, mbi.rawBytePower) - rejectionDealPower = big.Add(rejectionDealPower, mbi.dealPower) - } - } - - fmt.Println("Total acceptance token: ", approveBalance) - fmt.Println("Total rejection token: ", rejectionBalance) - - fmt.Println("Total acceptance SP deal power: ", approveDealPower) - fmt.Println("Total rejection SP deal power: ", rejectionDealPower) - - fmt.Println("Total acceptance SP rb power: ", approveRBP) - fmt.Println("Total rejection SP rb power: ", rejectionRBP) - - fmt.Println("Total acceptance Client rb power: ", clientApproveBytes) - fmt.Println("Total rejection Client rb power: ", clientRejectBytes) - - fmt.Println("\n\nFinal results **drumroll**") - if rejectionBalance.GreaterThanEqual(big.Mul(approveBalance, big.NewInt(3))) { - fmt.Println("token holders VETO FIP-0036!") - } else if approveBalance.LessThanEqual(rejectionBalance) { - fmt.Println("token holders REJECT FIP-0036") - } else { - fmt.Println("token holders ACCEPT FIP-0036") - } - - if rejectionDealPower.GreaterThanEqual(big.Mul(approveDealPower, big.NewInt(3))) { - fmt.Println("SPs by deal data stored VETO FIP-0036!") - } else if approveDealPower.LessThanEqual(rejectionDealPower) { - fmt.Println("SPs by deal data stored REJECT FIP-0036") - } else { - fmt.Println("SPs by deal data stored ACCEPT FIP-0036") - } - - if rejectionRBP.GreaterThanEqual(big.Mul(approveRBP, big.NewInt(3))) { - fmt.Println("SPs by total raw byte power VETO FIP-0036!") - } else if approveRBP.LessThanEqual(rejectionRBP) { - fmt.Println("SPs by total raw byte power REJECT FIP-0036") - } else { - fmt.Println("SPs by total raw byte power ACCEPT FIP-0036") - } - - if clientRejectBytes.GreaterThanEqual(big.Mul(clientApproveBytes, big.NewInt(3))) { - fmt.Println("Storage Clients VETO FIP-0036!") - } else if clientApproveBytes.LessThanEqual(clientRejectBytes) { - fmt.Println("Storage Clients REJECT FIP-0036") - } else { - fmt.Println("Storage Clients ACCEPT FIP-0036") - } - - return nil - }, -} - -// Returns voted sorted by votes from earliest to latest -func getVotesMap(file string) ([]Vote, error) { - var votes []Vote - vb, err := os.ReadFile(file) - if err != nil { - return nil, xerrors.Errorf("read vote: %w", err) - } - - if err := json.Unmarshal(vb, &votes); err != nil { - return nil, xerrors.Errorf("unmarshal vote: %w", err) - } - - sort.SliceStable(votes, func(i, j int) bool { - return votes[i].ID < votes[j].ID - }) - - return votes, nil -} diff --git a/cmd/lotus-shed/indexes.go b/cmd/lotus-shed/indexes.go index be7d43e05..620933e25 100644 --- a/cmd/lotus-shed/indexes.go +++ b/cmd/lotus-shed/indexes.go @@ -9,13 +9,11 @@ import ( "strings" "github.com/mitchellh/go-homedir" - "github.com/multiformats/go-varint" "github.com/urfave/cli/v2" "golang.org/x/xerrors" "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/abi" - builtintypes "github.com/filecoin-project/go-state-types/builtin" "github.com/filecoin-project/go-state-types/crypto" "github.com/filecoin-project/go-state-types/exitcode" @@ -109,6 +107,7 @@ var backfillEventsCmd = &cli.Command{ addressLookups := make(map[abi.ActorID]address.Address) + // TODO: We don't need this address resolution anymore once https://github.com/filecoin-project/lotus/issues/11594 lands resolveFn := func(ctx context.Context, emitter abi.ActorID, ts *types.TipSet) (address.Address, bool) { // we only want to match using f4 addresses idAddr, err := address.NewIDAddress(uint64(emitter)) @@ -118,18 +117,9 @@ var backfillEventsCmd = &cli.Command{ actor, err := api.StateGetActor(ctx, idAddr, ts.Key()) if err != nil || actor.Address == nil { - return address.Undef, false + return idAddr, true } - // if robust address is not f4 then we won't match against it so bail early - if actor.Address.Protocol() != address.Delegated { - return address.Undef, false - } - - // we have an f4 address, make sure it's assigned by the EAM - if namespace, _, err := varint.FromUvarint(actor.Address.Payload()); err != nil || namespace != builtintypes.EthereumAddressManagerActorID { - return address.Undef, false - } return *actor.Address, true } diff --git a/cmd/lotus-shed/main.go b/cmd/lotus-shed/main.go index d5a2ad2cd..e9f9f3b6b 100644 --- a/cmd/lotus-shed/main.go +++ b/cmd/lotus-shed/main.go @@ -84,7 +84,6 @@ func main() { diffCmd, itestdCmd, msigCmd, - fip36PollCmd, invariantsCmd, gasTraceCmd, replayOfflineCmd, diff --git a/cmd/lotus-shed/market.go b/cmd/lotus-shed/market.go index 4436e3c40..6fb1566b6 100644 --- a/cmd/lotus-shed/market.go +++ b/cmd/lotus-shed/market.go @@ -387,7 +387,7 @@ var marketDealsTotalStorageCmd = &cli.Command{ count := 0 for _, deal := range deals { - if market.IsDealActive(deal.State) { + if market.IsDealActive(deal.State.Iface()) { dealStorage := big.NewIntUnsigned(uint64(deal.Proposal.PieceSize)) total = big.Add(total, dealStorage) count++ diff --git a/cmd/lotus-shed/shedgen/cbor_gen.go b/cmd/lotus-shed/shedgen/cbor_gen.go index f2a79fe7d..10b41827f 100644 --- a/cmd/lotus-shed/shedgen/cbor_gen.go +++ b/cmd/lotus-shed/shedgen/cbor_gen.go @@ -31,7 +31,7 @@ func (t *CarbNode) MarshalCBOR(w io.Writer) error { } // t.Sub ([]cid.Cid) (slice) - if len("Sub") > cbg.MaxLength { + if len("Sub") > 8192 { return xerrors.Errorf("Value in field \"Sub\" was too long") } @@ -42,7 +42,7 @@ func (t *CarbNode) MarshalCBOR(w io.Writer) error { return err } - if len(t.Sub) > cbg.MaxLength { + if len(t.Sub) > 8192 { return xerrors.Errorf("Slice value in field t.Sub was too long") } @@ -88,7 +88,7 @@ func (t *CarbNode) UnmarshalCBOR(r io.Reader) (err error) { for i := uint64(0); i < n; i++ { { - sval, err := cbg.ReadString(cr) + sval, err := cbg.ReadStringWithMax(cr, 8192) if err != nil { return err } @@ -105,7 +105,7 @@ func (t *CarbNode) UnmarshalCBOR(r io.Reader) (err error) { return err } - if extra > cbg.MaxLength { + if extra > 8192 { return fmt.Errorf("t.Sub: array too large (%d)", extra) } @@ -136,6 +136,7 @@ func (t *CarbNode) UnmarshalCBOR(r io.Reader) (err error) { t.Sub[i] = c } + } } diff --git a/conformance/chaos/cbor_gen.go b/conformance/chaos/cbor_gen.go index d74ae0946..09d48ad10 100644 --- a/conformance/chaos/cbor_gen.go +++ b/conformance/chaos/cbor_gen.go @@ -37,7 +37,7 @@ func (t *State) MarshalCBOR(w io.Writer) error { } // t.Value (string) (string) - if len(t.Value) > cbg.MaxLength { + if len(t.Value) > 8192 { return xerrors.Errorf("Value in field t.Value was too long") } @@ -49,7 +49,7 @@ func (t *State) MarshalCBOR(w io.Writer) error { } // t.Unmarshallable ([]*chaos.UnmarshallableCBOR) (slice) - if len(t.Unmarshallable) > cbg.MaxLength { + if len(t.Unmarshallable) > 8192 { return xerrors.Errorf("Slice value in field t.Unmarshallable was too long") } @@ -60,6 +60,7 @@ func (t *State) MarshalCBOR(w io.Writer) error { if err := v.MarshalCBOR(cw); err != nil { return err } + } return nil } @@ -90,7 +91,7 @@ func (t *State) UnmarshalCBOR(r io.Reader) (err error) { // t.Value (string) (string) { - sval, err := cbg.ReadString(cr) + sval, err := cbg.ReadStringWithMax(cr, 8192) if err != nil { return err } @@ -104,7 +105,7 @@ func (t *State) UnmarshalCBOR(r io.Reader) (err error) { return err } - if extra > cbg.MaxLength { + if extra > 8192 { return fmt.Errorf("t.Unmarshallable: array too large (%d)", extra) } @@ -142,9 +143,9 @@ func (t *State) UnmarshalCBOR(r io.Reader) (err error) { } } + } } - return nil } @@ -174,7 +175,7 @@ func (t *CallerValidationArgs) MarshalCBOR(w io.Writer) error { } // t.Addrs ([]address.Address) (slice) - if len(t.Addrs) > cbg.MaxLength { + if len(t.Addrs) > 8192 { return xerrors.Errorf("Slice value in field t.Addrs was too long") } @@ -185,10 +186,11 @@ func (t *CallerValidationArgs) MarshalCBOR(w io.Writer) error { if err := v.MarshalCBOR(cw); err != nil { return err } + } // t.Types ([]cid.Cid) (slice) - if len(t.Types) > cbg.MaxLength { + if len(t.Types) > 8192 { return xerrors.Errorf("Slice value in field t.Types was too long") } @@ -231,10 +233,10 @@ func (t *CallerValidationArgs) UnmarshalCBOR(r io.Reader) (err error) { // t.Branch (chaos.CallerValidationBranch) (int64) { maj, extra, err := cr.ReadHeader() - var extraI int64 if err != nil { return err } + var extraI int64 switch maj { case cbg.MajUnsignedInt: extraI = int64(extra) @@ -260,7 +262,7 @@ func (t *CallerValidationArgs) UnmarshalCBOR(r io.Reader) (err error) { return err } - if extra > cbg.MaxLength { + if extra > 8192 { return fmt.Errorf("t.Addrs: array too large (%d)", extra) } @@ -288,9 +290,9 @@ func (t *CallerValidationArgs) UnmarshalCBOR(r io.Reader) (err error) { } } + } } - // t.Types ([]cid.Cid) (slice) maj, extra, err = cr.ReadHeader() @@ -298,7 +300,7 @@ func (t *CallerValidationArgs) UnmarshalCBOR(r io.Reader) (err error) { return err } - if extra > cbg.MaxLength { + if extra > 8192 { return fmt.Errorf("t.Types: array too large (%d)", extra) } @@ -329,9 +331,9 @@ func (t *CallerValidationArgs) UnmarshalCBOR(r io.Reader) (err error) { t.Types[i] = c } + } } - return nil } @@ -562,7 +564,7 @@ func (t *SendArgs) MarshalCBOR(w io.Writer) error { } // t.Params ([]uint8) (slice) - if len(t.Params) > cbg.ByteArrayMaxLen { + if len(t.Params) > 2097152 { return xerrors.Errorf("Byte array in field t.Params was too long") } @@ -570,9 +572,10 @@ func (t *SendArgs) MarshalCBOR(w io.Writer) error { return err } - if _, err := cw.Write(t.Params[:]); err != nil { + if _, err := cw.Write(t.Params); err != nil { return err } + return nil } @@ -638,7 +641,7 @@ func (t *SendArgs) UnmarshalCBOR(r io.Reader) (err error) { return err } - if extra > cbg.ByteArrayMaxLen { + if extra > 2097152 { return fmt.Errorf("t.Params: byte array too large (%d)", extra) } if maj != cbg.MajByteString { @@ -649,9 +652,10 @@ func (t *SendArgs) UnmarshalCBOR(r io.Reader) (err error) { t.Params = make([]uint8, extra) } - if _, err := io.ReadFull(cr, t.Params[:]); err != nil { + if _, err := io.ReadFull(cr, t.Params); err != nil { return err } + return nil } @@ -670,7 +674,7 @@ func (t *SendReturn) MarshalCBOR(w io.Writer) error { } // t.Return (builtin.CBORBytes) (slice) - if len(t.Return) > cbg.ByteArrayMaxLen { + if len(t.Return) > 2097152 { return xerrors.Errorf("Byte array in field t.Return was too long") } @@ -678,7 +682,7 @@ func (t *SendReturn) MarshalCBOR(w io.Writer) error { return err } - if _, err := cw.Write(t.Return[:]); err != nil { + if _, err := cw.Write(t.Return); err != nil { return err } @@ -692,6 +696,7 @@ func (t *SendReturn) MarshalCBOR(w io.Writer) error { return err } } + return nil } @@ -725,7 +730,7 @@ func (t *SendReturn) UnmarshalCBOR(r io.Reader) (err error) { return err } - if extra > cbg.ByteArrayMaxLen { + if extra > 2097152 { return fmt.Errorf("t.Return: byte array too large (%d)", extra) } if maj != cbg.MajByteString { @@ -736,16 +741,17 @@ func (t *SendReturn) UnmarshalCBOR(r io.Reader) (err error) { t.Return = make([]uint8, extra) } - if _, err := io.ReadFull(cr, t.Return[:]); err != nil { + if _, err := io.ReadFull(cr, t.Return); err != nil { return err } + // t.Code (exitcode.ExitCode) (int64) { maj, extra, err := cr.ReadHeader() - var extraI int64 if err != nil { return err } + var extraI int64 switch maj { case cbg.MajUnsignedInt: extraI = int64(extra) @@ -782,7 +788,7 @@ func (t *MutateStateArgs) MarshalCBOR(w io.Writer) error { } // t.Value (string) (string) - if len(t.Value) > cbg.MaxLength { + if len(t.Value) > 8192 { return xerrors.Errorf("Value in field t.Value was too long") } @@ -803,6 +809,7 @@ func (t *MutateStateArgs) MarshalCBOR(w io.Writer) error { return err } } + return nil } @@ -832,7 +839,7 @@ func (t *MutateStateArgs) UnmarshalCBOR(r io.Reader) (err error) { // t.Value (string) (string) { - sval, err := cbg.ReadString(cr) + sval, err := cbg.ReadStringWithMax(cr, 8192) if err != nil { return err } @@ -842,10 +849,10 @@ func (t *MutateStateArgs) UnmarshalCBOR(r io.Reader) (err error) { // t.Branch (chaos.MutateStateBranch) (int64) { maj, extra, err := cr.ReadHeader() - var extraI int64 if err != nil { return err } + var extraI int64 switch maj { case cbg.MajUnsignedInt: extraI = int64(extra) @@ -893,7 +900,7 @@ func (t *AbortWithArgs) MarshalCBOR(w io.Writer) error { } // t.Message (string) (string) - if len(t.Message) > cbg.MaxLength { + if len(t.Message) > 8192 { return xerrors.Errorf("Value in field t.Message was too long") } @@ -937,10 +944,10 @@ func (t *AbortWithArgs) UnmarshalCBOR(r io.Reader) (err error) { // t.Code (exitcode.ExitCode) (int64) { maj, extra, err := cr.ReadHeader() - var extraI int64 if err != nil { return err } + var extraI int64 switch maj { case cbg.MajUnsignedInt: extraI = int64(extra) @@ -962,7 +969,7 @@ func (t *AbortWithArgs) UnmarshalCBOR(r io.Reader) (err error) { // t.Message (string) (string) { - sval, err := cbg.ReadString(cr) + sval, err := cbg.ReadStringWithMax(cr, 8192) if err != nil { return err } @@ -1094,10 +1101,10 @@ func (t *InspectRuntimeReturn) UnmarshalCBOR(r io.Reader) (err error) { // t.CurrEpoch (abi.ChainEpoch) (int64) { maj, extra, err := cr.ReadHeader() - var extraI int64 if err != nil { return err } + var extraI int64 switch maj { case cbg.MajUnsignedInt: extraI = int64(extra) diff --git a/documentation/en/api-v0-methods-miner.md b/documentation/en/api-v0-methods-miner.md index 57070caed..b133930bc 100644 --- a/documentation/en/api-v0-methods-miner.md +++ b/documentation/en/api-v0-methods-miner.md @@ -475,7 +475,7 @@ Inputs: ], "Bw==", 10101, - 21 + 22 ] ``` @@ -826,8 +826,7 @@ Response: "State": { "SectorStartEpoch": 10101, "LastUpdatedEpoch": 10101, - "SlashEpoch": 10101, - "VerifiedClaim": 0 + "SlashEpoch": 10101 } } ] @@ -1426,8 +1425,7 @@ Response: "State": { "SectorStartEpoch": 10101, "LastUpdatedEpoch": 10101, - "SlashEpoch": 10101, - "VerifiedClaim": 0 + "SlashEpoch": 10101 } } ] @@ -2910,6 +2908,14 @@ Inputs: "StartEpoch": 10101, "EndEpoch": 10101 }, + "PieceActivationManifest": { + "CID": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Size": 2032, + "VerifiedAllocationKey": null, + "Notify": null + }, "KeepUnsealed": true } ] @@ -3212,6 +3218,14 @@ Inputs: "StartEpoch": 10101, "EndEpoch": 10101 }, + "PieceActivationManifest": { + "CID": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Size": 2032, + "VerifiedAllocationKey": null, + "Notify": null + }, "KeepUnsealed": true } } @@ -3556,6 +3570,14 @@ Response: "StartEpoch": 10101, "EndEpoch": 10101 }, + "PieceActivationManifest": { + "CID": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Size": 2032, + "VerifiedAllocationKey": null, + "Notify": null + }, "KeepUnsealed": true } } diff --git a/documentation/en/api-v0-methods-provider.md b/documentation/en/api-v0-methods-provider.md index 43846035a..a1ccc17c2 100644 --- a/documentation/en/api-v0-methods-provider.md +++ b/documentation/en/api-v0-methods-provider.md @@ -69,6 +69,14 @@ Inputs: "StartEpoch": 10101, "EndEpoch": 10101 }, + "PieceActivationManifest": { + "CID": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "Size": 2032, + "VerifiedAllocationKey": null, + "Notify": null + }, "KeepUnsealed": true }, 9, diff --git a/documentation/en/api-v0-methods.md b/documentation/en/api-v0-methods.md index 411190339..eedfd227e 100644 --- a/documentation/en/api-v0-methods.md +++ b/documentation/en/api-v0-methods.md @@ -170,6 +170,8 @@ * [StateDealProviderCollateralBounds](#StateDealProviderCollateralBounds) * [StateDecodeParams](#StateDecodeParams) * [StateGetActor](#StateGetActor) + * [StateGetAllAllocations](#StateGetAllAllocations) + * [StateGetAllClaims](#StateGetAllClaims) * [StateGetAllocation](#StateGetAllocation) * [StateGetAllocationForPendingDeal](#StateGetAllocationForPendingDeal) * [StateGetAllocations](#StateGetAllocations) @@ -4732,7 +4734,7 @@ Perms: read Inputs: ```json [ - 21 + 22 ] ``` @@ -4747,7 +4749,7 @@ Perms: read Inputs: ```json [ - 21 + 22 ] ``` @@ -5313,6 +5315,50 @@ Response: } ``` +### StateGetAllAllocations +StateGetAllAllocations returns the all the allocations available in verified registry actor. + + +Perms: read + +Inputs: +```json +[ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] +] +``` + +Response: `{}` + +### StateGetAllClaims +StateGetAllClaims returns the all the claims available in verified registry actor. + + +Perms: read + +Inputs: +```json +[ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] +] +``` + +Response: `{}` + ### StateGetAllocation StateGetAllocation returns the allocation for a given address and allocation ID. @@ -5513,7 +5559,9 @@ Response: "UpgradeHyggeHeight": 10101, "UpgradeLightningHeight": 10101, "UpgradeThunderHeight": 10101, - "UpgradeWatermelonHeight": 10101 + "UpgradeWatermelonHeight": 10101, + "UpgradeDragonHeight": 10101, + "UpgradePhoenixHeight": 10101 }, "Eip155ChainID": 123 } @@ -5794,8 +5842,7 @@ Response: "State": { "SectorStartEpoch": 10101, "LastUpdatedEpoch": 10101, - "SlashEpoch": 10101, - "VerifiedClaim": 0 + "SlashEpoch": 10101 } } } @@ -5873,8 +5920,7 @@ Response: "State": { "SectorStartEpoch": 10101, "LastUpdatedEpoch": 10101, - "SlashEpoch": 10101, - "VerifiedClaim": 0 + "SlashEpoch": 10101 } } ``` @@ -6439,7 +6485,7 @@ Inputs: ] ``` -Response: `21` +Response: `22` ### StateReadState StateReadState returns the indicated actor's state. diff --git a/documentation/en/api-v1-unstable-methods.md b/documentation/en/api-v1-unstable-methods.md index c65c102d3..287cfce53 100644 --- a/documentation/en/api-v1-unstable-methods.md +++ b/documentation/en/api-v1-unstable-methods.md @@ -115,6 +115,8 @@ * [GasEstimateGasLimit](#GasEstimateGasLimit) * [GasEstimateGasPremium](#GasEstimateGasPremium) * [GasEstimateMessageGas](#GasEstimateMessageGas) +* [Get](#Get) + * [GetActorEvents](#GetActorEvents) * [I](#I) * [ID](#ID) * [Log](#Log) @@ -227,8 +229,11 @@ * [StateDecodeParams](#StateDecodeParams) * [StateEncodeParams](#StateEncodeParams) * [StateGetActor](#StateGetActor) + * [StateGetAllAllocations](#StateGetAllAllocations) + * [StateGetAllClaims](#StateGetAllClaims) * [StateGetAllocation](#StateGetAllocation) * [StateGetAllocationForPendingDeal](#StateGetAllocationForPendingDeal) + * [StateGetAllocationIdForPendingDeal](#StateGetAllocationIdForPendingDeal) * [StateGetAllocations](#StateGetAllocations) * [StateGetBeaconEntry](#StateGetBeaconEntry) * [StateGetClaim](#StateGetClaim) @@ -276,6 +281,8 @@ * [StateVerifiedRegistryRootKey](#StateVerifiedRegistryRootKey) * [StateVerifierStatus](#StateVerifierStatus) * [StateWaitMsg](#StateWaitMsg) +* [Subscribe](#Subscribe) + * [SubscribeActorEvents](#SubscribeActorEvents) * [Sync](#Sync) * [SyncCheckBad](#SyncCheckBad) * [SyncCheckpoint](#SyncCheckpoint) @@ -3376,6 +3383,72 @@ Response: } ``` +## Get + + +### GetActorEvents +GetActorEvents returns all user-programmed and built-in actor events that match the given +filter. +This is a request/response API. +Results available from this API may be limited by the MaxFilterResults and MaxFilterHeightRange +configuration options and also the amount of historical data available in the node. + +This is an EXPERIMENTAL API and may be subject to change. + + +Perms: read + +Inputs: +```json +[ + { + "addresses": [ + "f01234" + ], + "fields": { + "abc": [ + { + "codec": 81, + "value": "ZGRhdGE=" + } + ] + }, + "fromHeight": 1010, + "toHeight": 1020 + } +] +``` + +Response: +```json +[ + { + "entries": [ + { + "Flags": 7, + "Key": "string value", + "Codec": 42, + "Value": "Ynl0ZSBhcnJheQ==" + } + ], + "emitter": "f01234", + "reverted": true, + "height": 10101, + "tipsetKey": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ], + "msgCid": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + } +] +``` + ## I @@ -6225,7 +6298,7 @@ Perms: read Inputs: ```json [ - 21 + 22 ] ``` @@ -6240,7 +6313,7 @@ Perms: read Inputs: ```json [ - 21 + 22 ] ``` @@ -6857,6 +6930,50 @@ Response: } ``` +### StateGetAllAllocations +StateGetAllAllocations returns the all the allocations available in verified registry actor. + + +Perms: read + +Inputs: +```json +[ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] +] +``` + +Response: `{}` + +### StateGetAllClaims +StateGetAllClaims returns the all the claims available in verified registry actor. + + +Perms: read + +Inputs: +```json +[ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] +] +``` + +Response: `{}` + ### StateGetAllocation StateGetAllocation returns the allocation for a given address and allocation ID. @@ -6931,6 +7048,29 @@ Response: } ``` +### StateGetAllocationIdForPendingDeal +StateGetAllocationIdForPendingDeal is like StateGetAllocationForPendingDeal except it returns the allocation ID + + +Perms: read + +Inputs: +```json +[ + 5432, + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] +] +``` + +Response: `0` + ### StateGetAllocations StateGetAllocations returns the all the allocations for a given client. @@ -7081,7 +7221,9 @@ Response: "UpgradeHyggeHeight": 10101, "UpgradeLightningHeight": 10101, "UpgradeThunderHeight": 10101, - "UpgradeWatermelonHeight": 10101 + "UpgradeWatermelonHeight": 10101, + "UpgradeDragonHeight": 10101, + "UpgradePhoenixHeight": 10101 }, "Eip155ChainID": 123 } @@ -7388,8 +7530,7 @@ Response: "State": { "SectorStartEpoch": 10101, "LastUpdatedEpoch": 10101, - "SlashEpoch": 10101, - "VerifiedClaim": 0 + "SlashEpoch": 10101 } } } @@ -7467,8 +7608,7 @@ Response: "State": { "SectorStartEpoch": 10101, "LastUpdatedEpoch": 10101, - "SlashEpoch": 10101, - "VerifiedClaim": 0 + "SlashEpoch": 10101 } } ``` @@ -8061,7 +8201,7 @@ Inputs: ] ``` -Response: `21` +Response: `22` ### StateReadState StateReadState returns the indicated actor's state. @@ -8658,6 +8798,77 @@ Response: } ``` +## Subscribe + + +### SubscribeActorEvents +SubscribeActorEvents returns a long-lived stream of all user-programmed and built-in actor +events that match the given filter. +Events that match the given filter are written to the stream in real-time as they are emitted +from the FVM. +The response stream is closed when the client disconnects, when a ToHeight is specified and is +reached, or if there is an error while writing an event to the stream. +This API also allows clients to read all historical events matching the given filter before any +real-time events are written to the response stream if the filter specifies an earlier +FromHeight. +Results available from this API may be limited by the MaxFilterResults and MaxFilterHeightRange +configuration options and also the amount of historical data available in the node. + +Note: this API is only available via websocket connections. +This is an EXPERIMENTAL API and may be subject to change. + + +Perms: read + +Inputs: +```json +[ + { + "addresses": [ + "f01234" + ], + "fields": { + "abc": [ + { + "codec": 81, + "value": "ZGRhdGE=" + } + ] + }, + "fromHeight": 1010, + "toHeight": 1020 + } +] +``` + +Response: +```json +{ + "entries": [ + { + "Flags": 7, + "Key": "string value", + "Codec": 42, + "Value": "Ynl0ZSBhcnJheQ==" + } + ], + "emitter": "f01234", + "reverted": true, + "height": 10101, + "tipsetKey": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ], + "msgCid": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } +} +``` + ## Sync The Sync method group contains methods for interacting with and observing the lotus sync service. diff --git a/documentation/en/cli-lotus-miner.md b/documentation/en/cli-lotus-miner.md index 2ea89e6ce..0f6704275 100644 --- a/documentation/en/cli-lotus-miner.md +++ b/documentation/en/cli-lotus-miner.md @@ -7,7 +7,7 @@ USAGE: lotus-miner [global options] command [command options] [arguments...] VERSION: - 1.25.3-dev + 1.27.0-dev COMMANDS: init Initialize a lotus miner repo diff --git a/documentation/en/cli-lotus-provider.md b/documentation/en/cli-lotus-provider.md index 984d1a2d5..be3b5ec6e 100644 --- a/documentation/en/cli-lotus-provider.md +++ b/documentation/en/cli-lotus-provider.md @@ -7,7 +7,7 @@ USAGE: lotus-provider [global options] command [command options] [arguments...] VERSION: - 1.25.3-dev + 1.27.0-dev COMMANDS: cli Execute cli commands diff --git a/documentation/en/cli-lotus-worker.md b/documentation/en/cli-lotus-worker.md index 0e0fee157..043c16202 100644 --- a/documentation/en/cli-lotus-worker.md +++ b/documentation/en/cli-lotus-worker.md @@ -7,7 +7,7 @@ USAGE: lotus-worker [global options] command [command options] [arguments...] VERSION: - 1.25.3-dev + 1.27.0-dev COMMANDS: run Start lotus worker diff --git a/documentation/en/cli-lotus.md b/documentation/en/cli-lotus.md index ff62980dc..65dd92f01 100644 --- a/documentation/en/cli-lotus.md +++ b/documentation/en/cli-lotus.md @@ -7,7 +7,7 @@ USAGE: lotus [global options] command [command options] [arguments...] VERSION: - 1.25.3-dev + 1.27.0-dev COMMANDS: daemon Start a lotus daemon process @@ -1188,8 +1188,8 @@ COMMANDS: check-client-datacap check verified client remaining bytes check-notary-datacap check a notary's remaining bytes sign-remove-data-cap-proposal allows a notary to sign a Remove Data Cap Proposal - list-allocations List allocations made by client - list-claims List claims made by provider + list-allocations List allocations available in verified registry actor or made by a client if specified + list-claims List claims available in verified registry actor or made by provider if specified remove-expired-allocations remove expired allocations (if no allocations are specified all eligible allocations are removed) remove-expired-claims remove expired claims (if no claims are specified all eligible claims are removed) help, h Shows a list of commands or help for one command @@ -1275,20 +1275,21 @@ OPTIONS: ### lotus filplus list-allocations ``` NAME: - lotus filplus list-allocations - List allocations made by client + lotus filplus list-allocations - List allocations available in verified registry actor or made by a client if specified USAGE: lotus filplus list-allocations [command options] clientAddress OPTIONS: --expired list only expired allocations (default: false) + --json output results in json format (default: false) --help, -h show help ``` ### lotus filplus list-claims ``` NAME: - lotus filplus list-claims - List claims made by provider + lotus filplus list-claims - List claims available in verified registry actor or made by provider if specified USAGE: lotus filplus list-claims [command options] providerAddress diff --git a/documentation/en/default-lotus-config.toml b/documentation/en/default-lotus-config.toml index 9f9836bc0..420c192bd 100644 --- a/documentation/en/default-lotus-config.toml +++ b/documentation/en/default-lotus-config.toml @@ -276,9 +276,8 @@ #EthTxHashMappingLifetimeDays = 0 [Fevm.Events] - # EnableEthRPC enables APIs that # DisableRealTimeFilterAPI will disable the RealTimeFilterAPI that can create and query filters for actor events as they are emitted. - # The API is enabled when EnableEthRPC is true, but can be disabled selectively with this flag. + # The API is enabled when EnableEthRPC or Events.EnableActorEventsAPI is true, but can be disabled selectively with this flag. # # type: bool # env var: LOTUS_FEVM_EVENTS_DISABLEREALTIMEFILTERAPI @@ -286,7 +285,7 @@ # DisableHistoricFilterAPI will disable the HistoricFilterAPI that can create and query filters for actor events # that occurred in the past. HistoricFilterAPI maintains a queryable index of events. - # The API is enabled when EnableEthRPC is true, but can be disabled selectively with this flag. + # The API is enabled when EnableEthRPC or Events.EnableActorEventsAPI is true, but can be disabled selectively with this flag. # # type: bool # env var: LOTUS_FEVM_EVENTS_DISABLEHISTORICFILTERAPI @@ -328,6 +327,17 @@ #DatabasePath = "" +[Events] + # EnableActorEventsAPI enables the Actor events API that enables clients to consume events + # emitted by (smart contracts + built-in Actors). + # This will also enable the RealTimeFilterAPI and HistoricFilterAPI by default, but they can be + # disabled by setting their respective Disable* options in Fevm.Events. + # + # type: bool + # env var: LOTUS_EVENTS_ENABLEACTOREVENTSAPI + #EnableActorEventsAPI = false + + [Index] # EXPERIMENTAL FEATURE. USE WITH CAUTION # EnableMsgIndex enables indexing of messages on chain. diff --git a/documentation/en/default-lotus-miner-config.toml b/documentation/en/default-lotus-miner-config.toml index a65e82e95..17fd24fa3 100644 --- a/documentation/en/default-lotus-miner-config.toml +++ b/documentation/en/default-lotus-miner-config.toml @@ -702,6 +702,30 @@ # env var: LOTUS_SEALING_USESYNTHETICPOREP #UseSyntheticPoRep = false + # Whether to abort if any sector activation in a batch fails (newly sealed sectors, only with ProveCommitSectors3). + # + # type: bool + # env var: LOTUS_SEALING_REQUIREACTIVATIONSUCCESS + #RequireActivationSuccess = false + + # Whether to abort if any piece activation notification returns a non-zero exit code (newly sealed sectors, only with ProveCommitSectors3). + # + # type: bool + # env var: LOTUS_SEALING_REQUIREACTIVATIONSUCCESSUPDATE + #RequireActivationSuccessUpdate = false + + # Whether to abort if any sector activation in a batch fails (updating sectors, only with ProveReplicaUpdates3). + # + # type: bool + # env var: LOTUS_SEALING_REQUIRENOTIFICATIONSUCCESS + #RequireNotificationSuccess = false + + # Whether to abort if any piece activation notification returns a non-zero exit code (updating sectors, only with ProveReplicaUpdates3). + # + # type: bool + # env var: LOTUS_SEALING_REQUIRENOTIFICATIONSUCCESSUPDATE + #RequireNotificationSuccessUpdate = false + [Storage] # type: int diff --git a/gateway/node.go b/gateway/node.go index f2464d274..f3ecb7640 100644 --- a/gateway/node.go +++ b/gateway/node.go @@ -146,6 +146,9 @@ type TargetAPI interface { Web3ClientVersion(ctx context.Context) (string, error) EthTraceBlock(ctx context.Context, blkNum string) ([]*ethtypes.EthTraceBlock, error) EthTraceReplayBlockTransactions(ctx context.Context, blkNum string, traceTypes []string) ([]*ethtypes.EthTraceReplayBlockTransaction, error) + + GetActorEvents(ctx context.Context, filter *types.ActorEventFilter) ([]*types.ActorEvent, error) + SubscribeActorEvents(ctx context.Context, filter *types.ActorEventFilter) (<-chan *types.ActorEvent, error) } var _ TargetAPI = *new(api.FullNode) // gateway depends on latest diff --git a/gateway/proxy_fil.go b/gateway/proxy_fil.go index eb8a354ed..e7ad3bdb4 100644 --- a/gateway/proxy_fil.go +++ b/gateway/proxy_fil.go @@ -437,6 +437,20 @@ func (gw *Node) StateWaitMsg(ctx context.Context, msg cid.Cid, confidence uint64 return gw.target.StateWaitMsg(ctx, msg, confidence, limit, allowReplaced) } +func (gw *Node) GetActorEvents(ctx context.Context, filter *types.ActorEventFilter) ([]*types.ActorEvent, error) { + if err := gw.limit(ctx, stateRateLimitTokens); err != nil { + return nil, err + } + return gw.target.GetActorEvents(ctx, filter) +} + +func (gw *Node) SubscribeActorEvents(ctx context.Context, filter *types.ActorEventFilter) (<-chan *types.ActorEvent, error) { + if err := gw.limit(ctx, stateRateLimitTokens); err != nil { + return nil, err + } + return gw.target.SubscribeActorEvents(ctx, filter) +} + func (gw *Node) StateReadState(ctx context.Context, actor address.Address, tsk types.TipSetKey) (*api.ActorState, error) { if err := gw.limit(ctx, stateRateLimitTokens); err != nil { return nil, err diff --git a/gen/inlinegen-data.json b/gen/inlinegen-data.json index cf72d24fa..70c8fff61 100644 --- a/gen/inlinegen-data.json +++ b/gen/inlinegen-data.json @@ -1,7 +1,7 @@ { - "actorVersions": [0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], - "latestActorsVersion": 12, + "actorVersions": [0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13], + "latestActorsVersion": 13, - "networkVersions": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21], - "latestNetworkVersion": 21 + "networkVersions": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22], + "latestNetworkVersion": 22 } diff --git a/gen/main.go b/gen/main.go index d84343739..f1fe3876a 100644 --- a/gen/main.go +++ b/gen/main.go @@ -14,6 +14,7 @@ import ( "github.com/filecoin-project/lotus/cmd/lotus-shed/shedgen" "github.com/filecoin-project/lotus/node/hello" "github.com/filecoin-project/lotus/paychmgr" + "github.com/filecoin-project/lotus/storage/pipeline/piece" sectorstorage "github.com/filecoin-project/lotus/storage/sealer" "github.com/filecoin-project/lotus/storage/sealer/storiface" ) @@ -64,9 +65,7 @@ func main() { api.SealedRefs{}, api.SealTicket{}, api.SealSeed{}, - api.PieceDealInfo{}, api.SectorPiece{}, - api.DealSchedule{}, ) if err != nil { fmt.Println(err) @@ -111,6 +110,15 @@ func main() { os.Exit(1) } + err = gen.WriteMapEncodersToFile("./storage/pipeline/piece/cbor_gen.go", "piece", + piece.PieceDealInfo{}, + piece.DealSchedule{}, + ) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + err = gen.WriteMapEncodersToFile("./storage/sealer/cbor_gen.go", "sealer", sectorstorage.Call{}, sectorstorage.WorkState{}, diff --git a/go.mod b/go.mod index 583a1760e..b8f6c38f2 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ retract v1.20.2 // Wrongfully cherry picked PR, use v1.20.2+ instead. require ( contrib.go.opencensus.io/exporter/prometheus v0.4.2 - github.com/BurntSushi/toml v1.2.1 + github.com/BurntSushi/toml v1.3.0 github.com/DataDog/zstd v1.4.5 github.com/GeertJohan/go.rice v1.0.3 github.com/Gurpartap/async v0.0.0-20180927173644-4f7f499dd9ee @@ -23,14 +23,14 @@ require ( github.com/detailyang/go-fallocate v0.0.0-20180908115635-432fa640bd2e github.com/dgraph-io/badger/v2 v2.2007.4 github.com/docker/go-units v0.5.0 - github.com/drand/drand v1.4.9 - github.com/drand/kyber v1.1.15 + github.com/drand/drand v1.5.7 + github.com/drand/kyber v1.2.0 github.com/dustin/go-humanize v1.0.1 github.com/elastic/go-elasticsearch/v7 v7.14.0 github.com/elastic/go-sysinfo v1.7.0 github.com/elastic/gosigar v0.14.2 github.com/etclabscore/go-openrpc-reflect v0.0.36 - github.com/fatih/color v1.13.0 + github.com/fatih/color v1.15.0 github.com/filecoin-project/dagstore v0.5.2 github.com/filecoin-project/filecoin-ffi v0.30.4-0.20220519234331-bfd1f5f9fe38 github.com/filecoin-project/go-address v1.1.0 @@ -47,7 +47,7 @@ require ( github.com/filecoin-project/go-jsonrpc v0.3.1 github.com/filecoin-project/go-padreader v0.0.1 github.com/filecoin-project/go-paramfetch v0.0.4 - github.com/filecoin-project/go-state-types v0.12.8 + github.com/filecoin-project/go-state-types v0.13.0-rc.2 github.com/filecoin-project/go-statemachine v1.0.3 github.com/filecoin-project/go-statestore v0.2.0 github.com/filecoin-project/go-storedcounter v0.1.0 @@ -90,7 +90,6 @@ require ( github.com/ipfs/go-fs-lock v0.0.7 github.com/ipfs/go-graphsync v0.14.6 github.com/ipfs/go-ipfs-blocksutil v0.0.1 - github.com/ipfs/go-ipfs-exchange-offline v0.3.0 github.com/ipfs/go-ipld-cbor v0.0.6 github.com/ipfs/go-ipld-format v0.5.0 github.com/ipfs/go-log/v2 v2.5.1 @@ -122,7 +121,7 @@ require ( github.com/minio/sha256-simd v1.0.1 github.com/mitchellh/go-homedir v1.1.0 github.com/multiformats/go-base32 v0.1.0 - github.com/multiformats/go-multiaddr v0.12.1 + github.com/multiformats/go-multiaddr v0.12.2 github.com/multiformats/go-multiaddr-dns v0.3.1 github.com/multiformats/go-multibase v0.2.0 github.com/multiformats/go-multicodec v0.9.0 @@ -141,7 +140,7 @@ require ( github.com/triplewz/poseidon v0.0.0-20230828015038-79d8165c88ed github.com/urfave/cli/v2 v2.25.5 github.com/whyrusleeping/bencher v0.0.0-20190829221104-bb6607aa8bba - github.com/whyrusleeping/cbor-gen v0.0.0-20230923211252-36a87e1ba72f + github.com/whyrusleeping/cbor-gen v0.1.0 github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7 github.com/xeipuuv/gojsonschema v1.2.0 github.com/xorcare/golden v0.6.1-0.20191112154924-b87f686d7542 @@ -156,11 +155,11 @@ require ( go.uber.org/fx v1.20.0 go.uber.org/multierr v1.11.0 go.uber.org/zap v1.25.0 - golang.org/x/crypto v0.17.0 + golang.org/x/crypto v0.18.0 golang.org/x/net v0.14.0 golang.org/x/sync v0.3.0 - golang.org/x/sys v0.15.0 - golang.org/x/term v0.15.0 + golang.org/x/sys v0.16.0 + golang.org/x/term v0.16.0 golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846 golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 @@ -188,9 +187,9 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect - github.com/dgraph-io/ristretto v0.1.0 // indirect + github.com/dgraph-io/ristretto v0.1.1 // indirect github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect - github.com/drand/kyber-bls12381 v0.2.3 // indirect + github.com/drand/kyber-bls12381 v0.3.1 // indirect github.com/elastic/go-windows v1.0.0 // indirect github.com/etclabscore/go-jsonschema-walk v0.0.6 // indirect github.com/filecoin-project/go-amt-ipld/v2 v2.1.0 // indirect @@ -201,10 +200,9 @@ require ( github.com/filecoin-project/go-hamt-ipld/v3 v3.1.0 // indirect github.com/flynn/noise v1.0.0 // indirect github.com/francoispqt/gojay v1.2.13 // indirect - github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/gdamore/encoding v1.0.0 // indirect github.com/go-kit/log v0.2.1 // indirect - github.com/go-logfmt/logfmt v0.5.1 // indirect + github.com/go-logfmt/logfmt v0.6.0 // indirect github.com/go-logr/logr v1.2.4 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.2.5 // indirect @@ -214,7 +212,7 @@ require ( github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/glog v1.1.0 // indirect + github.com/golang/glog v1.1.1 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/golang/snappy v0.0.4 // indirect @@ -256,7 +254,7 @@ require ( github.com/jpillora/backoff v1.0.0 // indirect github.com/kilic/bls12-381 v0.1.0 // indirect github.com/klauspost/compress v1.16.7 // indirect - github.com/klauspost/cpuid/v2 v2.2.5 // indirect + github.com/klauspost/cpuid/v2 v2.2.6 // indirect github.com/koron/go-ssdp v0.0.4 // indirect github.com/libp2p/go-cidranger v1.1.0 // indirect github.com/libp2p/go-flow-metrics v0.1.0 // indirect @@ -290,7 +288,7 @@ require ( github.com/petar/GoLLRB v0.0.0-20210522233825-ae3b015fd3e9 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_model v0.4.0 // indirect - github.com/prometheus/common v0.42.0 // indirect + github.com/prometheus/common v0.44.0 // indirect github.com/prometheus/procfs v0.10.1 // indirect github.com/prometheus/statsd_exporter v0.22.7 // indirect github.com/quic-go/qpack v0.4.0 // indirect @@ -301,7 +299,7 @@ require ( github.com/rs/cors v1.7.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/shirou/gopsutil v2.18.12+incompatible // indirect - github.com/sirupsen/logrus v1.9.0 // indirect + github.com/sirupsen/logrus v1.9.2 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect github.com/tidwall/gjson v1.14.4 // indirect github.com/twmb/murmur3 v1.1.6 // indirect @@ -324,7 +322,7 @@ require ( golang.org/x/mod v0.12.0 // indirect golang.org/x/text v0.14.0 // indirect gonum.org/v1/gonum v0.13.0 // indirect - google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc // indirect google.golang.org/grpc v1.55.0 // indirect google.golang.org/protobuf v1.30.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/go.sum b/go.sum index 566ce37c6..3efa6b8e2 100644 --- a/go.sum +++ b/go.sum @@ -46,8 +46,8 @@ github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOv github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 h1:cTp8I5+VIoKjsnZuH8vjyaysT/ses3EvZeaV/1UkF2M= github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= -github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/BurntSushi/toml v1.3.0 h1:Ws8e5YmnrGEHzZEzg0YvK/7COGYtTC5PbaH9oSSbgfA= +github.com/BurntSushi/toml v1.3.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ= @@ -96,6 +96,8 @@ github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8V github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/ardanlabs/darwin/v2 v2.0.0 h1:XCisQMgQ5EG+ZvSEcADEo+pyfIMKyWAGnn5o2TgriYE= +github.com/ardanlabs/darwin/v2 v2.0.0/go.mod h1:MubZ2e9DAYGaym0mClSOi183NYahrrfKxvSy1HMhoes= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= @@ -232,8 +234,8 @@ github.com/dgraph-io/badger/v2 v2.2007.4 h1:TRWBQg8UrlUhaFdco01nO2uXwzKS7zd+HVdw github.com/dgraph-io/badger/v2 v2.2007.4/go.mod h1:vSw/ax2qojzbN6eXHIx6KPKtCSHJN/Uz0X0VPruTIhk= github.com/dgraph-io/ristretto v0.0.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= -github.com/dgraph-io/ristretto v0.1.0 h1:Jv3CGQHp9OjuMBSne1485aDpUkTKEcUqF+jm/LuerPI= -github.com/dgraph-io/ristretto v0.1.0/go.mod h1:fux0lOrBhrVCJd3lcTHsIJhq1T2rokOu6v9Vcb3Q9ug= +github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8= +github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-farm v0.0.0-20190104051053-3adb47b1fb0f/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= @@ -242,19 +244,12 @@ github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUn github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/drand/bls12-381 v0.3.2/go.mod h1:dtcLgPtYT38L3NO6mPDYH0nbpc5tjPassDqiniuAt4Y= -github.com/drand/drand v1.4.9 h1:WE8Jf/l+7B/rheCMCLZTp5xk0/a05t+ciwBvORq9jXM= -github.com/drand/drand v1.4.9/go.mod h1:vsmJ/kDoVLv1NC0nFihzBPmIFvMGmYtgJewzRBBRVSc= -github.com/drand/kyber v1.0.1-0.20200110225416-8de27ed8c0e2/go.mod h1:UpXoA0Upd1N9l4TvRPHr1qAUBBERj6JQ/mnKI3BPEmw= -github.com/drand/kyber v1.0.2/go.mod h1:x6KOpK7avKj0GJ4emhXFP5n7M7W7ChAPmnQh/OL6vRw= -github.com/drand/kyber v1.1.4/go.mod h1:9+IgTq7kadePhZg7eRwSD7+bA+bmvqRK+8DtmoV5a3U= -github.com/drand/kyber v1.1.10/go.mod h1:UkHLsI4W6+jT5PvNxmc0cvQAgppjTUpX+XCsN9TXmRo= -github.com/drand/kyber v1.1.15 h1:YNL02FPOA98GmlIhh5FuEJWhz1ZCp6tOUVFN7ujBJPE= -github.com/drand/kyber v1.1.15/go.mod h1:tw0l70U6aWCkc4vDr8u/canpOOOiUNJlzsmeElhBfe0= -github.com/drand/kyber-bls12381 v0.2.0/go.mod h1:zQip/bHdeEB6HFZSU3v+d3cQE0GaBVQw9aR2E7AdoeI= -github.com/drand/kyber-bls12381 v0.2.1/go.mod h1:JwWn4nHO9Mp4F5qCie5sVIPQZ0X6cw8XAeMRvc/GXBE= -github.com/drand/kyber-bls12381 v0.2.3 h1:wueWtqjj71wnwm6fYR8MAQk4q8bKVK9WukrGGcaVxzk= -github.com/drand/kyber-bls12381 v0.2.3/go.mod h1:FsudUZf6Xu61u/gYrDHEHf6lKIKluJdnX7WJe4hkMh4= +github.com/drand/drand v1.5.7 h1:5f2D5aH1nEfVI9S6tl2p9bgIDMZ92oltmiY12Kh+eYU= +github.com/drand/drand v1.5.7/go.mod h1:jrJ0244yOHNL5V04vazk3mFatjAWm3i6dg6URWwgbXk= +github.com/drand/kyber v1.2.0 h1:22SbBxsKbgQnJUoyYKIfG909PhBsj0vtANeu4BX5xgE= +github.com/drand/kyber v1.2.0/go.mod h1:6TqFlCc7NGOiNVTF9pF2KcDRfllPd9XOkExuG5Xtwfo= +github.com/drand/kyber-bls12381 v0.3.1 h1:KWb8l/zYTP5yrvKTgvhOrk2eNPscbMiUOIeWBnmUxGo= +github.com/drand/kyber-bls12381 v0.3.1/go.mod h1:H4y9bLPu7KZA/1efDg+jtJ7emKx+ro3PU7/jWUVt140= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= @@ -283,8 +278,8 @@ github.com/etclabscore/go-openrpc-reflect v0.0.36 h1:kSqNB2U8RVoW4si+4fsv13NGNkR github.com/etclabscore/go-openrpc-reflect v0.0.36/go.mod h1:0404Ky3igAasAOpyj1eESjstTyneBAIk5PgJFbK4s5E= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.8.0/go.mod h1:3l45GVGkyrnYNl9HoIjnp2NnNWvh6hLAqD8yTfGjnw8= -github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= -github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= +github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/filecoin-project/dagstore v0.5.2 h1:Nd6oXdnolbbVhpMpkYT5PJHOjQp4OBSntHpMV5pxj3c= @@ -349,8 +344,8 @@ github.com/filecoin-project/go-state-types v0.1.0/go.mod h1:ezYnPf0bNkTsDibL/psS github.com/filecoin-project/go-state-types v0.1.6/go.mod h1:UwGVoMsULoCK+bWjEdd/xLCvLAQFBC7EDT477SKml+Q= github.com/filecoin-project/go-state-types v0.1.10/go.mod h1:UwGVoMsULoCK+bWjEdd/xLCvLAQFBC7EDT477SKml+Q= github.com/filecoin-project/go-state-types v0.11.2-0.20230712101859-8f37624fa540/go.mod h1:SyNPwTsU7I22gL2r0OAPcImvLoTVfgRwdK/Y5rR1zz8= -github.com/filecoin-project/go-state-types v0.12.8 h1:W/UObdAsv+LbB9EfyLg92DSYoatzUWmlfV8FGyh30VA= -github.com/filecoin-project/go-state-types v0.12.8/go.mod h1:gR2NV0CSGSQwopxF+3In9nDh1sqvoYukLcs5vK0AHCA= +github.com/filecoin-project/go-state-types v0.13.0-rc.2 h1:JHyDDx/nV8sbQNgjUfhumiGWh8Dedc8psbiVtD0YOh0= +github.com/filecoin-project/go-state-types v0.13.0-rc.2/go.mod h1:cHpOPup9H1g2T29dKHAjC2sc7/Ef5ypjuW9A3I+e9yY= github.com/filecoin-project/go-statemachine v0.0.0-20200925024713-05bd7c71fbfe/go.mod h1:FGwQgZAt2Gh5mjlwJUlVB62JeYdo+if0xWxSEfBD9ig= github.com/filecoin-project/go-statemachine v1.0.3 h1:N07o6alys+V1tNoSTi4WuuoeNC4erS/6jE74+NsgQuk= github.com/filecoin-project/go-statemachine v1.0.3/go.mod h1:jZdXXiHa61n4NmgWFG4w8tnqgvZVHYbJ3yW7+y8bF54= @@ -426,8 +421,9 @@ github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBj github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA= github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= +github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= +github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= @@ -480,8 +476,8 @@ github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69 github.com/gogo/status v1.1.1 h1:DuHXlSFHNKqTQ+/ACf5Vs6r4X/dH2EgIzR9Vr+H65kg= github.com/gogo/status v1.1.1/go.mod h1:jpG3dM5QPcqu19Hg8lkUhBFBa3TcLs1DG7+2Jqci7oU= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE= -github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ= +github.com/golang/glog v1.1.1 h1:jxpi2eWoU84wbX9iIEyAeeoac3FLuifZpY9tcNUD9kw= +github.com/golang/glog v1.1.1/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -589,8 +585,8 @@ github.com/gregdhill/go-openrpc v0.0.0-20220114144539-ae6f44720487 h1:NyaWOSkqFK github.com/gregdhill/go-openrpc v0.0.0-20220114144539-ae6f44720487/go.mod h1:a1eRkbhd3DYpRH2lnuUsVG+QMTI+v0hGnsis8C9hMrA= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw= -github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= +github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI= +github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= @@ -900,6 +896,8 @@ github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g= +github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ= github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901 h1:rp+c0RAYOWj8l6qbCUTSiRLG/iKnW3K3/QfPPuSsBt4= github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901/go.mod h1:Z86h9688Y0wesXCyonoVr47MasHilkuLMqGhRZ4Hpak= github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= @@ -929,9 +927,6 @@ github.com/kami-zh/go-capturer v0.0.0-20171211120116-e492ea43421d/go.mod h1:P2vi github.com/karrick/godirwalk v1.10.12/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= -github.com/kilic/bls12-381 v0.0.0-20200607163746-32e1441c8a9f/go.mod h1:XXfR6YFCRSrkEXbNlIyDsgXVNJWVUV30m/ebkVy9n6s= -github.com/kilic/bls12-381 v0.0.0-20200731194930-64c428e1bff5/go.mod h1:XXfR6YFCRSrkEXbNlIyDsgXVNJWVUV30m/ebkVy9n6s= -github.com/kilic/bls12-381 v0.0.0-20200820230200-6b2c19996391/go.mod h1:XXfR6YFCRSrkEXbNlIyDsgXVNJWVUV30m/ebkVy9n6s= github.com/kilic/bls12-381 v0.1.0 h1:encrdjqKMEvabVQ7qYOKu1OvhqpK4s47wDYtNiPtlp4= github.com/kilic/bls12-381 v0.1.0/go.mod h1:vDTTHJONJ6G+P2R74EhnyotQDTliQDnFEwhdmfzw1ig= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= @@ -946,9 +941,8 @@ github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQs github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.6/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.2.3/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= -github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= -github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc= +github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/koalacxr/quantile v0.0.1 h1:wAW+SQ286Erny9wOjVww96t8ws+x5Zj6AKHDULUK+o0= github.com/koalacxr/quantile v0.0.1/go.mod h1:bGN/mCZLZ4lrSDHRQ6Lglj9chowGux8sGUIND+DQeD0= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -973,8 +967,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= -github.com/lib/pq v1.10.0 h1:Zx5DJFEYQXio93kgXnQ09fXNiUKsqv4OUEu2UtGcB1E= -github.com/lib/pq v1.10.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= +github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/libp2p/go-addr-util v0.0.1/go.mod h1:4ac6O7n9rIAKB1dnd+s8IbbMXkt+oBpzX4/+RACcnlQ= github.com/libp2p/go-addr-util v0.0.2/go.mod h1:Ecd6Fb3yIuLzq4bD7VcywcVSBtefcAwnUISBM3WG15E= github.com/libp2p/go-buffer-pool v0.0.1/go.mod h1:xtyIz9PMobb13WaxR6Zo1Pd1zXJKYg0a8KiIvDp3TzQ= @@ -1207,14 +1201,12 @@ github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaO github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= @@ -1291,8 +1283,8 @@ github.com/multiformats/go-multiaddr v0.2.2/go.mod h1:NtfXiOtHvghW9KojvtySjH5y0u github.com/multiformats/go-multiaddr v0.3.0/go.mod h1:dF9kph9wfJ+3VLAaeBqo9Of8x4fJxp6ggJGteB8HQTI= github.com/multiformats/go-multiaddr v0.3.1/go.mod h1:uPbspcUPd5AfaP6ql3ujFY+QWzmBD8uLLL4bXW0XfGc= github.com/multiformats/go-multiaddr v0.3.3/go.mod h1:lCKNGP1EQ1eZ35Za2wlqnabm9xQkib3fyB+nZXHLag0= -github.com/multiformats/go-multiaddr v0.12.1 h1:vm+BA/WZA8QZDp1pF1FWhi5CT3g1tbi5GJmqpb6wnlk= -github.com/multiformats/go-multiaddr v0.12.1/go.mod h1:7mPkiBMmLeFipt+nNSq9pHZUeJSt8lHBgH6yhj0YQzE= +github.com/multiformats/go-multiaddr v0.12.2 h1:9G9sTY/wCYajKa9lyfWPmpZAwe6oV+Wb1zcmMS1HG24= +github.com/multiformats/go-multiaddr v0.12.2/go.mod h1:GKyaTYjZRdcUhyOetrxTk9z0cW+jA/YrnqTOvKgi44M= github.com/multiformats/go-multiaddr-dns v0.0.1/go.mod h1:9kWcqw/Pj6FwxAwW38n/9403szc57zJPs45fmnznu3Q= github.com/multiformats/go-multiaddr-dns v0.0.2/go.mod h1:9kWcqw/Pj6FwxAwW38n/9403szc57zJPs45fmnznu3Q= github.com/multiformats/go-multiaddr-dns v0.2.0/go.mod h1:TJ5pr5bBO7Y1B18djPuRsVkduhQH2YqYSbxWJzYGdK0= @@ -1358,9 +1350,8 @@ github.com/nikkolasg/hexjson v0.1.0 h1:Cgi1MSZVQFoJKYeRpBNEcdF3LB+Zo4fYKsDz7h8uJ github.com/nikkolasg/hexjson v0.1.0/go.mod h1:fbGbWFZ0FmJMFbpCMtJpwb0tudVxSSZ+Es2TsCg57cA= github.com/nkovacs/streamquote v1.0.0 h1:PmVIV08Zlx2lZK5fFZlMZ04eHcDTIFJCv/5/0twVUow= github.com/nkovacs/streamquote v1.0.0/go.mod h1:BN+NaZ2CmdKqUuTUXUEm9j95B2TRbpOWpxbJYzzgUsc= +github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= -github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= @@ -1462,8 +1453,8 @@ github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9 github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= github.com/prometheus/common v0.35.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= -github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= -github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= +github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY= +github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= @@ -1500,8 +1491,8 @@ github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6So github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= @@ -1516,8 +1507,8 @@ github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM= github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA= github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= -github.com/sercand/kuberesolver v2.4.0+incompatible h1:WE2OlRf6wjLxHwNkkFLQGaZcVLEXjMjBPjjEU5vksH8= -github.com/sercand/kuberesolver v2.4.0+incompatible/go.mod h1:lWF3GL0xptCB/vCiJPl/ZshwPsX/n4Y7u0CW9E7aQIQ= +github.com/sercand/kuberesolver/v4 v4.0.0 h1:frL7laPDG/lFm5n98ODmWnn+cvPpzlkf3LhzuPhcHP4= +github.com/sercand/kuberesolver/v4 v4.0.0/go.mod h1:F4RGyuRmMAjeXHKL+w4P7AwUnPceEAPAhxUgXZjKgvM= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/shirou/gopsutil v2.18.12+incompatible h1:1eaJvGomDnH74/5cF4CTmTbLHAriGFsTZppLXDX93OM= github.com/shirou/gopsutil v2.18.12+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= @@ -1549,8 +1540,8 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= -github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/sirupsen/logrus v1.9.2 h1:oxx1eChJGI6Uks2ZC4W1zpLlVgqB8ner4EuQwV4Ik1Y= +github.com/sirupsen/logrus v1.9.2/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/assertions v1.0.0/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM= github.com/smartystreets/assertions v1.0.1/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM= @@ -1599,7 +1590,6 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= @@ -1652,8 +1642,8 @@ github.com/warpfork/go-wish v0.0.0-20190328234359-8b3e70f8e830/go.mod h1:x6AKhvS github.com/warpfork/go-wish v0.0.0-20200122115046-b9ea61034e4a/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw= github.com/warpfork/go-wish v0.0.0-20220906213052-39a1cc7a02d0 h1:GDDkbFiaK8jsSDJfjId/PEGEShv6ugrt4kYsC5UIDaQ= github.com/warpfork/go-wish v0.0.0-20220906213052-39a1cc7a02d0/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw= -github.com/weaveworks/common v0.0.0-20220810113439-c65105d60b18 h1:JN4YR/TNWiZEAHHImrVA2u4DPI+aqPOar23ICUnYZTQ= -github.com/weaveworks/common v0.0.0-20220810113439-c65105d60b18/go.mod h1:YfOOLoW1Q/jIIu0WLeSwgStmrKjuJEZSKTAUc+0KFvE= +github.com/weaveworks/common v0.0.0-20230531151736-e2613bee6b73 h1:CMM9+/AgM77vaMXMQedzqPRMuNwjbI0EcdofPqxc9F8= +github.com/weaveworks/common v0.0.0-20230531151736-e2613bee6b73/go.mod h1:rgbeLfJUtEr+G74cwFPR1k/4N0kDeaeSv/qhUNE4hm8= github.com/weaveworks/promrus v1.2.0 h1:jOLf6pe6/vss4qGHjXmGz4oDJQA+AOCqEL3FvvZGz7M= github.com/weaveworks/promrus v1.2.0/go.mod h1:SaE82+OJ91yqjrE1rsvBWVzNZKcHYFtMUyS1+Ogs/KA= github.com/whyrusleeping/bencher v0.0.0-20190829221104-bb6607aa8bba h1:X4n8JG2e2biEZZXdBKt9HX7DN3bYGFUqljqqy0DqgnY= @@ -1674,8 +1664,8 @@ github.com/whyrusleeping/cbor-gen v0.0.0-20200826160007-0b9f6c5fb163/go.mod h1:f github.com/whyrusleeping/cbor-gen v0.0.0-20210118024343-169e9d70c0c2/go.mod h1:fgkXqYy7bV2cFeIEOkVTZS/WjXARfBqSH6Q2qHL33hQ= github.com/whyrusleeping/cbor-gen v0.0.0-20210303213153-67a261a1d291/go.mod h1:fgkXqYy7bV2cFeIEOkVTZS/WjXARfBqSH6Q2qHL33hQ= github.com/whyrusleeping/cbor-gen v0.0.0-20220323183124-98fa8256a799/go.mod h1:fgkXqYy7bV2cFeIEOkVTZS/WjXARfBqSH6Q2qHL33hQ= -github.com/whyrusleeping/cbor-gen v0.0.0-20230923211252-36a87e1ba72f h1:SBuSxXJL0/ZJMtTxbXZgHZkThl9dNrzyaNhlyaqscRo= -github.com/whyrusleeping/cbor-gen v0.0.0-20230923211252-36a87e1ba72f/go.mod h1:fgkXqYy7bV2cFeIEOkVTZS/WjXARfBqSH6Q2qHL33hQ= +github.com/whyrusleeping/cbor-gen v0.1.0 h1:Jneeq3V5enErVcuL0NKEbD1Gi+iOvEeFhXOV1S1Fc6g= +github.com/whyrusleeping/cbor-gen v0.1.0/go.mod h1:pM99HXyEbSQHcosHc0iW7YFmwnscr+t9Te4ibko05so= github.com/whyrusleeping/chunker v0.0.0-20181014151217-fe64bd25879f h1:jQa4QT2UP9WYv2nzyawpKMOCl+Z/jW7djv2/J50lj9E= github.com/whyrusleeping/chunker v0.0.0-20181014151217-fe64bd25879f/go.mod h1:p9UJB6dDgdPgMJZs7UjUOdulKyRr9fqkS+6JKAInPy8= github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1 h1:EKhdznlJHPMoKr0XTrX+IlJs1LH3lyx2nfr1dOlZ79k= @@ -1718,15 +1708,11 @@ github.com/zyedidia/generic v1.2.1 h1:Zv5KS/N2m0XZZiuLS82qheRG4X1o5gsWreGb0hR7XD github.com/zyedidia/generic v1.2.1/go.mod h1:ly2RBz4mnz1yeuVbQA/VFwGjK3mnHGRj1JuoG336Bis= go.dedis.ch/fixbuf v1.0.3 h1:hGcV9Cd/znUxlusJ64eAlExS+5cJDIyTyEG+otu5wQs= go.dedis.ch/fixbuf v1.0.3/go.mod h1:yzJMt34Wa5xD37V5RTdmp38cz3QhMagdGoem9anUalw= -go.dedis.ch/kyber/v3 v3.0.4/go.mod h1:OzvaEnPvKlyrWyp3kGXlFdp7ap1VC6RkZDTaPikqhsQ= -go.dedis.ch/kyber/v3 v3.0.9/go.mod h1:rhNjUUg6ahf8HEg5HUvVBYoWY4boAafX8tYxX+PS+qg= -go.dedis.ch/protobuf v1.0.5/go.mod h1:eIV4wicvi6JK0q/QnfIEGeSFNG0ZeB24kzut5+HaRLo= -go.dedis.ch/protobuf v1.0.7/go.mod h1:pv5ysfkDX/EawiPqcW3ikOxsL5t+BqnV6xHSmE79KI4= go.dedis.ch/protobuf v1.0.11 h1:FTYVIEzY/bfl37lu3pR4lIj+F9Vp1jE8oh91VmxKgLo= go.dedis.ch/protobuf v1.0.11/go.mod h1:97QR256dnkimeNdfmURz0wAMNVbd1VmLXhG1CrTYrJ4= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU= -go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= +go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ= +go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= @@ -1801,7 +1787,6 @@ golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190123085648-057139ce5d2b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190225124518-7f87c0fbb88b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -1818,13 +1803,10 @@ golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190927123631-a832865fa7ad/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200602180216-279210d13fed/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= @@ -1834,8 +1816,9 @@ golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= -golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= +golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -1982,7 +1965,6 @@ golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190124100055-b90733256f2e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190219092855-153ac476189d/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -2011,7 +1993,6 @@ golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191025021431-6c3a3bfe00ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191025090151-53bf42e6b339/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -2037,9 +2018,7 @@ golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200812155832-6a926be9bd1d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200926100807-9d91bd62050c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -2063,19 +2042,18 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220708085239-5a0f0661e09d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -2083,8 +2061,9 @@ golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuX golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= -golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= +golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE= +golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -2236,8 +2215,12 @@ google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7Fc google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 h1:DdoeryqhaXp1LtT/emMP1BRJPHHKFi5akj/nbx/zNTA= -google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= +google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc h1:8DyZCyvI8mE1IdLy/60bS+52xfymkE72wv1asokgtao= +google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:xZnkP7mREFX5MORlOPEzLMr+90PPZQ2QWzrVTWfAq64= +google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc h1:kVKPf/IiYSBWEWtkIn6wZXwWGCnLKcC8oWfZvXjsGnM= +google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc h1:XSJ8Vk1SWuNr8S18z1NZSziL0CPIXLCCMDOEFtHBOFc= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= diff --git a/itests/deadlines_test.go b/itests/deadlines_test.go index fb28f4509..70da4be5a 100644 --- a/itests/deadlines_test.go +++ b/itests/deadlines_test.go @@ -4,6 +4,7 @@ package itests import ( "bytes" "context" + "strings" "testing" "time" @@ -16,7 +17,6 @@ import ( "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/big" "github.com/filecoin-project/go-state-types/builtin" - minertypes "github.com/filecoin-project/go-state-types/builtin/v8/miner" "github.com/filecoin-project/go-state-types/exitcode" miner2 "github.com/filecoin-project/specs-actors/v2/actors/builtin/miner" @@ -183,13 +183,17 @@ func TestDeadlineToggling(t *testing.T) { cr, err := cid.Parse("bagboea4b5abcatlxechwbp7kjpjguna6r6q7ejrhe6mdp3lf34pmswn27pkkiekz") require.NoError(t, err) - params := &minertypes.SectorPreCommitInfo{ - Expiration: 2880 * 300, - SectorNumber: 22, - SealProof: kit.TestSpt, + params := &miner.PreCommitSectorBatchParams2{ + Sectors: []miner.SectorPreCommitInfo{ + { + Expiration: 2880 * 300, + SectorNumber: 22, + SealProof: kit.TestSpt, - SealedCID: cr, - SealRandEpoch: head.Height() - 200, + SealedCID: cr, + SealRandEpoch: head.Height() - 200, + }, + }, } enc := new(bytes.Buffer) @@ -199,7 +203,7 @@ func TestDeadlineToggling(t *testing.T) { To: maddrE, From: defaultFrom, Value: types.FromFil(1), - Method: builtin.MethodsMiner.PreCommitSector, + Method: builtin.MethodsMiner.PreCommitSectorBatch2, Params: enc.Bytes(), }, nil) require.NoError(t, err) @@ -286,14 +290,18 @@ func TestDeadlineToggling(t *testing.T) { sp, aerr := actors.SerializeParams(terminateSectorParams) require.NoError(t, aerr) - smsg, err := client.MpoolPushMessage(ctx, &types.Message{ - From: defaultFrom, - To: maddrD, - Method: builtin.MethodsMiner.TerminateSectors, + var smsg *types.SignedMessage + require.Eventually(t, func() bool { + smsg, err = client.MpoolPushMessage(ctx, &types.Message{ + From: defaultFrom, + To: maddrD, + Method: builtin.MethodsMiner.TerminateSectors, - Value: big.Zero(), - Params: sp, - }, nil) + Value: big.Zero(), + Params: sp, + }, nil) + return err == nil || !strings.Contains(err.Error(), "cannot terminate sectors in immutable deadline") + }, 60*time.Second, 100*time.Millisecond) require.NoError(t, err) t.Log("sent termination message:", smsg.Cid()) diff --git a/itests/direct_data_onboard_test.go b/itests/direct_data_onboard_test.go new file mode 100644 index 000000000..703419ad1 --- /dev/null +++ b/itests/direct_data_onboard_test.go @@ -0,0 +1,408 @@ +package itests + +import ( + "bytes" + "context" + "crypto/rand" + "fmt" + "testing" + "time" + + "github.com/ipfs/go-cid" + "github.com/ipld/go-ipld-prime" + "github.com/ipld/go-ipld-prime/codec/dagcbor" + "github.com/ipld/go-ipld-prime/node/basicnode" + "github.com/multiformats/go-multicodec" + "github.com/stretchr/testify/require" + + "github.com/filecoin-project/go-address" + cborutil "github.com/filecoin-project/go-cbor-util" + "github.com/filecoin-project/go-commp-utils/nonffi" + "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/go-state-types/big" + market2 "github.com/filecoin-project/go-state-types/builtin/v9/market" + "github.com/filecoin-project/go-state-types/exitcode" + "github.com/filecoin-project/go-state-types/network" + + "github.com/filecoin-project/lotus/chain/actors/builtin/market" + minertypes "github.com/filecoin-project/lotus/chain/actors/builtin/miner" + "github.com/filecoin-project/lotus/chain/consensus/filcns" + "github.com/filecoin-project/lotus/chain/stmgr" + "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/itests/kit" + "github.com/filecoin-project/lotus/lib/must" + "github.com/filecoin-project/lotus/node/config" + "github.com/filecoin-project/lotus/storage/pipeline/piece" +) + +func TestActors13Migration(t *testing.T) { + + var ( + blocktime = 2 * time.Millisecond + ctx = context.Background() + ) + client, _, ens := kit.EnsembleMinimal(t, kit.ThroughRPC(), kit.UpgradeSchedule(stmgr.Upgrade{ + Network: network.Version21, + Height: -1, + }, stmgr.Upgrade{ + Network: network.Version22, + Height: 10, + Migration: filcns.UpgradeActorsV13, + })) + ens.InterconnectAll().BeginMiningMustPost(blocktime) + + // mine until 15 + client.WaitTillChain(ctx, kit.HeightAtLeast(15)) +} + +func TestOnboardRawPiece(t *testing.T) { + kit.QuietMiningLogs() + + var ( + blocktime = 2 * time.Millisecond + ctx = context.Background() + ) + + client, miner, ens := kit.EnsembleMinimal(t, kit.ThroughRPC()) + ens.InterconnectAll().BeginMiningMustPost(blocktime) + + pieceSize := abi.PaddedPieceSize(2048).Unpadded() + pieceData := make([]byte, pieceSize) + _, _ = rand.Read(pieceData) + + dc, err := miner.ComputeDataCid(ctx, pieceSize, bytes.NewReader(pieceData)) + require.NoError(t, err) + + head, err := client.ChainHead(ctx) + require.NoError(t, err) + + so, err := miner.SectorAddPieceToAny(ctx, pieceSize, bytes.NewReader(pieceData), piece.PieceDealInfo{ + PublishCid: nil, + DealID: 0, + DealProposal: nil, + DealSchedule: piece.DealSchedule{ + StartEpoch: head.Height() + 2880*2, + EndEpoch: head.Height() + 2880*400, + }, + KeepUnsealed: true, + PieceActivationManifest: &minertypes.PieceActivationManifest{ + CID: dc.PieceCID, + Size: dc.Size, + VerifiedAllocationKey: nil, + Notify: nil, + }, + }) + require.NoError(t, err) + + // wait for sector to commit + + // wait for sector to commit and enter proving state + toCheck := map[abi.SectorNumber]struct{}{ + so.Sector: {}, + } + + miner.WaitSectorsProving(ctx, toCheck) + + si, err := miner.SectorsStatus(ctx, so.Sector, false) + require.NoError(t, err) + require.Equal(t, dc.PieceCID, *si.CommD) +} + +func TestOnboardMixedMarketDDO(t *testing.T) { + kit.QuietMiningLogs() + + var ( + blocktime = 2 * time.Millisecond + ctx = context.Background() + ) + + client, miner, ens := kit.EnsembleMinimal(t, kit.ThroughRPC(), kit.MutateSealingConfig(func(sc *config.SealingConfig) { + sc.RequireActivationSuccess = true + sc.RequireNotificationSuccess = true + })) + ens.InterconnectAll().BeginMiningMustPost(blocktime) + + maddr, err := miner.ActorAddress(ctx) + require.NoError(t, err) + + mi, err := client.StateMinerInfo(ctx, maddr, types.EmptyTSK) + require.NoError(t, err) + + var pieces []abi.PieceInfo + var dealID abi.DealID + + { + // market piece + pieceSize := abi.PaddedPieceSize(2048 / 2).Unpadded() + pieceData := make([]byte, pieceSize) + _, _ = rand.Read(pieceData) + + dc, err := miner.ComputeDataCid(ctx, pieceSize, bytes.NewReader(pieceData)) + require.NoError(t, err) + pieces = append(pieces, dc) + + head, err := client.ChainHead(ctx) + require.NoError(t, err) + + // PSD + + psdParams := market2.PublishStorageDealsParams{ + Deals: []market2.ClientDealProposal{ + makeMarketDealProposal(t, client, miner, dc.PieceCID, pieceSize.Padded(), head.Height()+2880*2, head.Height()+2880*400), + }, + } + + psdMsg := &types.Message{ + To: market.Address, + From: mi.Worker, + + Method: market.Methods.PublishStorageDeals, + Params: must.One(cborutil.Dump(&psdParams)), + } + + smsg, err := client.MpoolPushMessage(ctx, psdMsg, nil) + require.NoError(t, err) + + r, err := client.StateWaitMsg(ctx, smsg.Cid(), 1, stmgr.LookbackNoLimit, true) + require.NoError(t, err) + + require.Equal(t, exitcode.Ok, r.Receipt.ExitCode) + + nv, err := client.StateNetworkVersion(ctx, types.EmptyTSK) + require.NoError(t, err) + + res, err := market.DecodePublishStorageDealsReturn(r.Receipt.Return, nv) + require.NoError(t, err) + dealID = must.One(res.DealIDs())[0] + + mcid := smsg.Cid() + + so, err := miner.SectorAddPieceToAny(ctx, pieceSize, bytes.NewReader(pieceData), piece.PieceDealInfo{ + PublishCid: &mcid, + DealID: dealID, + DealProposal: &psdParams.Deals[0].Proposal, + DealSchedule: piece.DealSchedule{ + StartEpoch: head.Height() + 2880*2, + EndEpoch: head.Height() + 2880*400, + }, + PieceActivationManifest: nil, + KeepUnsealed: true, + }) + require.NoError(t, err) + + require.Equal(t, abi.PaddedPieceSize(0), so.Offset) + require.Equal(t, abi.SectorNumber(2), so.Sector) + } + + { + // raw ddo piece + + pieceSize := abi.PaddedPieceSize(2048 / 2).Unpadded() + pieceData := make([]byte, pieceSize) + _, _ = rand.Read(pieceData) + + dc, err := miner.ComputeDataCid(ctx, pieceSize, bytes.NewReader(pieceData)) + require.NoError(t, err) + pieces = append(pieces, dc) + + head, err := client.ChainHead(ctx) + require.NoError(t, err) + + so, err := miner.SectorAddPieceToAny(ctx, pieceSize, bytes.NewReader(pieceData), piece.PieceDealInfo{ + PublishCid: nil, + DealID: 0, + DealProposal: nil, + DealSchedule: piece.DealSchedule{ + StartEpoch: head.Height() + 2880*2, + EndEpoch: head.Height() + 2880*400, + }, + KeepUnsealed: false, + PieceActivationManifest: &minertypes.PieceActivationManifest{ + CID: dc.PieceCID, + Size: dc.Size, + VerifiedAllocationKey: nil, + Notify: nil, + }, + }) + require.NoError(t, err) + + require.Equal(t, abi.PaddedPieceSize(1024), so.Offset) + require.Equal(t, abi.SectorNumber(2), so.Sector) + } + + toCheck := map[abi.SectorNumber]struct{}{ + 2: {}, + } + + miner.WaitSectorsProving(ctx, toCheck) + + expectCommD, err := nonffi.GenerateUnsealedCID(abi.RegisteredSealProof_StackedDrg2KiBV1_1, pieces) + require.NoError(t, err) + + si, err := miner.SectorsStatus(ctx, 2, false) + require.NoError(t, err) + require.Equal(t, expectCommD, *si.CommD) + + ds, err := client.StateMarketStorageDeal(ctx, dealID, types.EmptyTSK) + require.NoError(t, err) + + require.NotEqual(t, -1, ds.State.SectorStartEpoch) + + { + deals, err := client.StateMarketDeals(ctx, types.EmptyTSK) + require.NoError(t, err) + for id, deal := range deals { + fmt.Println("Deal", id, deal.Proposal.PieceCID, deal.Proposal.PieceSize, deal.Proposal.Client, deal.Proposal.Provider) + } + + // check actor events, verify deal-published is as expected + minerIdAddr, err := client.StateLookupID(ctx, maddr, types.EmptyTSK) + require.NoError(t, err) + minerId, err := address.IDFromAddress(minerIdAddr) + require.NoError(t, err) + caddr, err := client.WalletDefaultAddress(context.Background()) + require.NoError(t, err) + clientIdAddr, err := client.StateLookupID(ctx, caddr, types.EmptyTSK) + require.NoError(t, err) + clientId, err := address.IDFromAddress(clientIdAddr) + require.NoError(t, err) + + fmt.Println("minerId", minerId, "clientId", clientId) + for _, piece := range pieces { + fmt.Println("piece", piece.PieceCID, piece.Size) + } + + // check "deal-published" actor event + var epochZero abi.ChainEpoch + allEvents, err := miner.FullNode.GetActorEvents(ctx, &types.ActorEventFilter{ + FromHeight: &epochZero, + }) + require.NoError(t, err) + for _, key := range []string{"deal-published", "deal-activated", "sector-precommitted", "sector-activated"} { + var found bool + keyBytes := must.One(ipld.Encode(basicnode.NewString(key), dagcbor.Encode)) + for _, event := range allEvents { + for _, e := range event.Entries { + if e.Key == "$type" && bytes.Equal(e.Value, keyBytes) { + found = true + switch key { + case "deal-published", "deal-activated": + expectedEntries := []types.EventEntry{ + {Flags: 0x03, Codec: uint64(multicodec.Cbor), Key: "$type", Value: keyBytes}, + {Flags: 0x03, Codec: uint64(multicodec.Cbor), Key: "id", Value: must.One(ipld.Encode(basicnode.NewInt(2), dagcbor.Encode))}, + {Flags: 0x03, Codec: uint64(multicodec.Cbor), Key: "client", Value: must.One(ipld.Encode(basicnode.NewInt(int64(clientId)), dagcbor.Encode))}, + {Flags: 0x03, Codec: uint64(multicodec.Cbor), Key: "provider", Value: must.One(ipld.Encode(basicnode.NewInt(int64(minerId)), dagcbor.Encode))}, + } + require.ElementsMatch(t, expectedEntries, event.Entries) + } + break + } + } + } + require.True(t, found, "expected to find event %s", key) + } + } +} + +func TestOnboardRawPieceSnap(t *testing.T) { + kit.QuietMiningLogs() + + var ( + blocktime = 2 * time.Millisecond + ctx = context.Background() + ) + + client, miner, ens := kit.EnsembleMinimal(t, kit.ThroughRPC(), kit.MutateSealingConfig(func(sc *config.SealingConfig) { + sc.PreferNewSectorsForDeals = false + sc.MakeNewSectorForDeals = false + sc.MakeCCSectorsAvailable = true + sc.AggregateCommits = false + })) + ens.InterconnectAll().BeginMiningMustPost(blocktime) + + miner.PledgeSectors(ctx, 1, 0, nil) + sl, err := miner.SectorsListNonGenesis(ctx) + require.NoError(t, err) + require.Len(t, sl, 1, "expected 1 sector") + + snum := sl[0] + + maddr, err := miner.ActorAddress(ctx) + require.NoError(t, err) + + client.WaitForSectorActive(ctx, t, snum, maddr) + + pieceSize := abi.PaddedPieceSize(2048).Unpadded() + pieceData := make([]byte, pieceSize) + _, _ = rand.Read(pieceData) + + dc, err := miner.ComputeDataCid(ctx, pieceSize, bytes.NewReader(pieceData)) + require.NoError(t, err) + + head, err := client.ChainHead(ctx) + require.NoError(t, err) + + so, err := miner.SectorAddPieceToAny(ctx, pieceSize, bytes.NewReader(pieceData), piece.PieceDealInfo{ + PublishCid: nil, + DealID: 0, + DealProposal: nil, + DealSchedule: piece.DealSchedule{ + StartEpoch: head.Height() + 2880*2, + EndEpoch: head.Height() + 2880*400, // todo set so that it works with the sector + }, + KeepUnsealed: false, + PieceActivationManifest: &minertypes.PieceActivationManifest{ + CID: dc.PieceCID, + Size: dc.Size, + VerifiedAllocationKey: nil, + Notify: nil, + }, + }) + require.NoError(t, err) + + // wait for sector to commit + + // wait for sector to commit and enter proving state + toCheck := map[abi.SectorNumber]struct{}{ + so.Sector: {}, + } + + miner.WaitSectorsProving(ctx, toCheck) +} + +func makeMarketDealProposal(t *testing.T, client *kit.TestFullNode, miner *kit.TestMiner, data cid.Cid, ps abi.PaddedPieceSize, start, end abi.ChainEpoch) market2.ClientDealProposal { + ca, err := client.WalletDefaultAddress(context.Background()) + require.NoError(t, err) + + ma, err := miner.ActorAddress(context.Background()) + require.NoError(t, err) + + dp := market2.DealProposal{ + PieceCID: data, + PieceSize: ps, + VerifiedDeal: false, + Client: ca, + Provider: ma, + Label: must.One(market2.NewLabelFromString("wat")), + StartEpoch: start, + EndEpoch: end, + StoragePricePerEpoch: big.Zero(), + ProviderCollateral: abi.TokenAmount{}, // below + ClientCollateral: big.Zero(), + } + + cb, err := client.StateDealProviderCollateralBounds(context.Background(), dp.PieceSize, dp.VerifiedDeal, types.EmptyTSK) + require.NoError(t, err) + dp.ProviderCollateral = big.Div(big.Mul(cb.Min, big.NewInt(2)), big.NewInt(2)) + + buf, err := cborutil.Dump(&dp) + require.NoError(t, err) + sig, err := client.WalletSign(context.Background(), ca, buf) + require.NoError(t, err) + + return market2.ClientDealProposal{ + Proposal: dp, + ClientSignature: *sig, + } + +} diff --git a/itests/direct_data_onboard_verified_test.go b/itests/direct_data_onboard_verified_test.go new file mode 100644 index 000000000..0c3de2448 --- /dev/null +++ b/itests/direct_data_onboard_verified_test.go @@ -0,0 +1,713 @@ +package itests + +import ( + "bytes" + "context" + "crypto/rand" + "fmt" + "strings" + "testing" + "time" + + "github.com/ipfs/go-cid" + "github.com/ipld/go-ipld-prime" + "github.com/ipld/go-ipld-prime/codec/dagcbor" + "github.com/ipld/go-ipld-prime/codec/dagjson" + "github.com/ipld/go-ipld-prime/datamodel" + cidlink "github.com/ipld/go-ipld-prime/linking/cid" + "github.com/ipld/go-ipld-prime/node/basicnode" + "github.com/ipld/go-ipld-prime/node/bindnode" + "github.com/multiformats/go-multicodec" + "github.com/stretchr/testify/require" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/go-state-types/big" + "github.com/filecoin-project/go-state-types/builtin" + minertypes13 "github.com/filecoin-project/go-state-types/builtin/v13/miner" + verifregtypes13 "github.com/filecoin-project/go-state-types/builtin/v13/verifreg" + datacap2 "github.com/filecoin-project/go-state-types/builtin/v9/datacap" + verifregtypes9 "github.com/filecoin-project/go-state-types/builtin/v9/verifreg" + + lapi "github.com/filecoin-project/lotus/api" + "github.com/filecoin-project/lotus/api/v1api" + "github.com/filecoin-project/lotus/chain/actors" + "github.com/filecoin-project/lotus/chain/actors/builtin/datacap" + minertypes "github.com/filecoin-project/lotus/chain/actors/builtin/miner" + "github.com/filecoin-project/lotus/chain/actors/builtin/verifreg" + "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/chain/wallet/key" + "github.com/filecoin-project/lotus/itests/kit" + "github.com/filecoin-project/lotus/lib/must" + "github.com/filecoin-project/lotus/storage/pipeline/piece" +) + +func TestOnboardRawPieceVerified_WithActorEvents(t *testing.T) { + kit.QuietMiningLogs() + + var ( + blocktime = 2 * time.Millisecond + ctx = context.Background() + ) + + rootKey, err := key.GenerateKey(types.KTSecp256k1) + require.NoError(t, err) + + verifierKey, err := key.GenerateKey(types.KTSecp256k1) + require.NoError(t, err) + + verifiedClientKey, err := key.GenerateKey(types.KTBLS) + require.NoError(t, err) + + bal, err := types.ParseFIL("100fil") + require.NoError(t, err) + + client, miner, ens := kit.EnsembleMinimal(t, kit.ThroughRPC(), + kit.RootVerifier(rootKey, abi.NewTokenAmount(bal.Int64())), + kit.Account(verifierKey, abi.NewTokenAmount(bal.Int64())), + kit.Account(verifiedClientKey, abi.NewTokenAmount(bal.Int64())), + ) + + /* --- Setup subscription channels for ActorEvents --- */ + + // subscribe only to miner's actor events + minerEvtsChan, err := miner.FullNode.SubscribeActorEvents(ctx, &types.ActorEventFilter{ + Addresses: []address.Address{miner.ActorAddr}, + }) + require.NoError(t, err) + + // subscribe only to sector-activated events + sectorActivatedCbor := must.One(ipld.Encode(basicnode.NewString("sector-activated"), dagcbor.Encode)) + sectorActivatedEvtsChan, err := miner.FullNode.SubscribeActorEvents(ctx, &types.ActorEventFilter{ + Fields: map[string][]types.ActorEventBlock{ + "$type": { + {Codec: uint64(multicodec.Cbor), Value: sectorActivatedCbor}, + }, + }, + }) + require.NoError(t, err) + + /* --- Start mining --- */ + + ens.InterconnectAll().BeginMiningMustPost(blocktime) + + minerId, err := address.IDFromAddress(miner.ActorAddr) + require.NoError(t, err) + + miner.PledgeSectors(ctx, 1, 0, nil) + sl, err := miner.SectorsListNonGenesis(ctx) + require.NoError(t, err) + require.Len(t, sl, 1, "expected 1 sector") + + snum := sl[0] + + maddr, err := miner.ActorAddress(ctx) + require.NoError(t, err) + + client.WaitForSectorActive(ctx, t, snum, maddr) + + /* --- Setup verified registry and client and allocate datacap to client */ + + verifierAddr, verifiedClientAddr := ddoVerifiedSetupVerifiedClient(ctx, t, client, rootKey, verifierKey, verifiedClientKey) + + /* --- Prepare piece for onboarding --- */ + + pieceSize := abi.PaddedPieceSize(2048).Unpadded() + pieceData := make([]byte, pieceSize) + _, _ = rand.Read(pieceData) + + dc, err := miner.ComputeDataCid(ctx, pieceSize, bytes.NewReader(pieceData)) + require.NoError(t, err) + + /* --- Allocate datacap for the piece by the verified client --- */ + + clientId, allocationId := ddoVerifiedSetupAllocations(ctx, t, client, minerId, dc, verifiedClientAddr) + + head, err := client.ChainHead(ctx) + require.NoError(t, err) + + // subscribe to actor events up until the current head + initialEventsChan, err := miner.FullNode.SubscribeActorEvents(ctx, &types.ActorEventFilter{ + FromHeight: epochPtr(0), + ToHeight: epochPtr(int64(head.Height())), + }) + require.NoError(t, err) + + /* --- Onboard the piece --- */ + + so, si := ddoVerifiedOnboardPiece(ctx, t, miner, clientId, allocationId, dc, pieceData) + + // check that we have one allocation because the real allocation has been claimed by the miner for the piece + allocations, err := client.StateGetAllocations(ctx, verifiedClientAddr, types.EmptyTSK) + require.NoError(t, err) + require.Len(t, allocations, 1) // allocation has been claimed, leaving the bogus one + + ddoVerifiedRemoveAllocations(ctx, t, client, verifiedClientAddr, clientId) + + // check that we have no more allocations + allocations, err = client.StateGetAllocations(ctx, verifiedClientAddr, types.EmptyTSK) + require.NoError(t, err) + require.Len(t, allocations, 0) + + /* --- Tests for ActorEvents --- */ + + t.Logf("Inspecting events as they appear in message receipts") + + // construct ActorEvents from messages and receipts + eventsFromMessages := ddoVerifiedBuildActorEventsFromMessages(ctx, t, miner.FullNode) + fmt.Println("Events from message receipts:") + printEvents(t, eventsFromMessages) + + // check for precisely these events and ensure they contain what we expect; don't be strict on + // other events to make sure we're forward-compatible as new events are added + + { + precommitedEvents := filterEvents(eventsFromMessages, "sector-precommitted") + require.Len(t, precommitedEvents, 2) + + expectedEntries := []types.EventEntry{ + {Flags: 0x03, Codec: uint64(multicodec.Cbor), Key: "$type", Value: must.One(ipld.Encode(basicnode.NewString("sector-precommitted"), dagcbor.Encode))}, + // first sector to start mining is CC + {Flags: 0x03, Codec: uint64(multicodec.Cbor), Key: "sector", Value: must.One(ipld.Encode(basicnode.NewInt(int64(so.Sector)-1), dagcbor.Encode))}, + } + require.ElementsMatch(t, expectedEntries, precommitedEvents[0].Entries) + + // second sector has our piece + expectedEntries[1].Value = must.One(ipld.Encode(basicnode.NewInt(int64(so.Sector)), dagcbor.Encode)) + require.ElementsMatch(t, expectedEntries, precommitedEvents[1].Entries) + } + + { + activatedEvents := filterEvents(eventsFromMessages, "sector-activated") + require.Len(t, activatedEvents, 2) + + expectedEntries := []types.EventEntry{ + {Flags: 0x03, Codec: uint64(multicodec.Cbor), Key: "$type", Value: must.One(ipld.Encode(basicnode.NewString("sector-activated"), dagcbor.Encode))}, + // first sector to start mining is CC + {Flags: 0x03, Codec: uint64(multicodec.Cbor), Key: "sector", Value: must.One(ipld.Encode(basicnode.NewInt(int64(so.Sector)-1), dagcbor.Encode))}, + {Flags: 0x03, Codec: uint64(multicodec.Cbor), Key: "unsealed-cid", Value: must.One(ipld.Encode(datamodel.Null, dagcbor.Encode))}, + } + require.ElementsMatch(t, expectedEntries, activatedEvents[0].Entries) + + // second sector has our piece, and only our piece, so usealed-cid matches piece-cid, + // unfortunately we don't have a case with multiple pieces + expectedEntries[1].Value = must.One(ipld.Encode(basicnode.NewInt(int64(so.Sector)), dagcbor.Encode)) + expectedEntries[2].Value = must.One(ipld.Encode(basicnode.NewLink(cidlink.Link{Cid: dc.PieceCID}), dagcbor.Encode)) + expectedEntries = append(expectedEntries, + types.EventEntry{Flags: 0x03, Codec: uint64(multicodec.Cbor), Key: "piece-cid", Value: must.One(ipld.Encode(basicnode.NewLink(cidlink.Link{Cid: dc.PieceCID}), dagcbor.Encode))}, + types.EventEntry{Flags: 0x01, Codec: uint64(multicodec.Cbor), Key: "piece-size", Value: must.One(ipld.Encode(basicnode.NewInt(int64(pieceSize.Padded())), dagcbor.Encode))}, + ) + require.ElementsMatch(t, expectedEntries, activatedEvents[1].Entries) + } + + { + verifierBalanceEvents := filterEvents(eventsFromMessages, "verifier-balance") + require.Len(t, verifierBalanceEvents, 2) + + verifierIdAddr, err := client.StateLookupID(ctx, verifierAddr, types.EmptyTSK) + require.NoError(t, err) + verifierId, err := address.IDFromAddress(verifierIdAddr) + require.NoError(t, err) + + verifierEntry := types.EventEntry{Flags: 0x03, Codec: uint64(multicodec.Cbor), Key: "verifier", Value: must.One(ipld.Encode(basicnode.NewInt(int64(verifierId)), dagcbor.Encode))} + require.Contains(t, verifierBalanceEvents[0].Entries, verifierEntry) + require.Contains(t, verifierBalanceEvents[1].Entries, verifierEntry) + } + + { + allocationEvents := filterEvents(eventsFromMessages, "allocation") + require.Len(t, allocationEvents, 2) + + expectedEntries := []types.EventEntry{ + {Flags: 0x03, Codec: uint64(multicodec.Cbor), Key: "$type", Value: must.One(ipld.Encode(basicnode.NewString("allocation"), dagcbor.Encode))}, + // first, bogus, allocation + {Flags: 0x03, Codec: uint64(multicodec.Cbor), Key: "id", Value: must.One(ipld.Encode(basicnode.NewInt(int64(allocationId)-1), dagcbor.Encode))}, + {Flags: 0x03, Codec: uint64(multicodec.Cbor), Key: "provider", Value: must.One(ipld.Encode(basicnode.NewInt(int64(minerId)), dagcbor.Encode))}, + {Flags: 0x03, Codec: uint64(multicodec.Cbor), Key: "client", Value: must.One(ipld.Encode(basicnode.NewInt(int64(clientId)), dagcbor.Encode))}, + } + require.ElementsMatch(t, expectedEntries, allocationEvents[0].Entries) + + // the second, real allocation + expectedEntries[1].Value = must.One(ipld.Encode(basicnode.NewInt(int64(allocationId)), dagcbor.Encode)) + require.ElementsMatch(t, expectedEntries, allocationEvents[1].Entries) + } + + { + allocationEvents := filterEvents(eventsFromMessages, "allocation-removed") + require.Len(t, allocationEvents, 1) + + // manual removal of the bogus allocation + expectedEntries := []types.EventEntry{ + {Flags: 0x03, Codec: uint64(multicodec.Cbor), Key: "$type", Value: must.One(ipld.Encode(basicnode.NewString("allocation-removed"), dagcbor.Encode))}, + {Flags: 0x03, Codec: uint64(multicodec.Cbor), Key: "id", Value: must.One(ipld.Encode(basicnode.NewInt(int64(allocationId)-1), dagcbor.Encode))}, + {Flags: 0x03, Codec: uint64(multicodec.Cbor), Key: "provider", Value: must.One(ipld.Encode(basicnode.NewInt(int64(minerId)), dagcbor.Encode))}, + {Flags: 0x03, Codec: uint64(multicodec.Cbor), Key: "client", Value: must.One(ipld.Encode(basicnode.NewInt(int64(clientId)), dagcbor.Encode))}, + } + require.ElementsMatch(t, expectedEntries, allocationEvents[0].Entries) + } + + { + claimEvents := filterEvents(eventsFromMessages, "claim") + require.Len(t, claimEvents, 1) + + expectedEntries := []types.EventEntry{ + {Flags: 0x03, Codec: uint64(multicodec.Cbor), Key: "$type", Value: must.One(ipld.Encode(basicnode.NewString("claim"), dagcbor.Encode))}, + // claimId inherits from its original allocationId + {Flags: 0x03, Codec: uint64(multicodec.Cbor), Key: "id", Value: must.One(ipld.Encode(basicnode.NewInt(int64(allocationId)), dagcbor.Encode))}, + {Flags: 0x03, Codec: uint64(multicodec.Cbor), Key: "provider", Value: must.One(ipld.Encode(basicnode.NewInt(int64(minerId)), dagcbor.Encode))}, + {Flags: 0x03, Codec: uint64(multicodec.Cbor), Key: "client", Value: must.One(ipld.Encode(basicnode.NewInt(int64(clientId)), dagcbor.Encode))}, + } + require.ElementsMatch(t, expectedEntries, claimEvents[0].Entries) + } + + // verify that we can trace a datacap allocation through to a claim with the events, since this + // information is not completely available from the state tree + claims := ddoVerifiedBuildClaimsFromMessages(ctx, t, eventsFromMessages, miner.FullNode) + for _, claim := range claims { + p, err := address.NewIDAddress(uint64(claim.Provider)) + require.NoError(t, err) + c, err := address.NewIDAddress(uint64(claim.Client)) + require.NoError(t, err) + fmt.Printf("Claim\n", + p, c, claim.Data, claim.Size, claim.TermMin, claim.TermMax, claim.TermStart, claim.Sector) + } + require.Equal(t, []*verifregtypes9.Claim{ + { + Provider: abi.ActorID(minerId), + Client: clientId, + Data: dc.PieceCID, + Size: dc.Size, + TermMin: verifregtypes13.MinimumVerifiedAllocationTerm, + TermMax: verifregtypes13.MaximumVerifiedAllocationTerm, + TermStart: si.Activation, + Sector: so.Sector, + }, + }, claims) + + // construct ActorEvents from GetActorEvents API + t.Logf("Inspecting full events list from GetActorEvents") + allEvtsFromGetAPI, err := miner.FullNode.GetActorEvents(ctx, &types.ActorEventFilter{ + FromHeight: epochPtr(0), + }) + require.NoError(t, err) + fmt.Println("Events from GetActorEvents:") + printEvents(t, allEvtsFromGetAPI) + // compare events from messages and receipts with events from GetActorEvents API + require.Equal(t, eventsFromMessages, allEvtsFromGetAPI) + + // construct ActorEvents from subscription channel for just the miner actor + t.Logf("Inspecting only miner's events list from SubscribeActorEvents") + var subMinerEvts []*types.ActorEvent + for evt := range minerEvtsChan { + subMinerEvts = append(subMinerEvts, evt) + if len(subMinerEvts) == 4 { + break + } + } + var allMinerEvts []*types.ActorEvent + for _, evt := range eventsFromMessages { + if evt.Emitter == miner.ActorAddr { + allMinerEvts = append(allMinerEvts, evt) + } + } + // compare events from messages and receipts with events from subscription channel + require.Equal(t, allMinerEvts, subMinerEvts) + + // construct ActorEvents from subscription channels for just the sector-activated events + var sectorActivatedEvts []*types.ActorEvent + for _, evt := range eventsFromMessages { + for _, entry := range evt.Entries { + if entry.Key == "$type" && bytes.Equal(entry.Value, sectorActivatedCbor) { + sectorActivatedEvts = append(sectorActivatedEvts, evt) + break + } + } + } + require.Len(t, sectorActivatedEvts, 2) // sanity check + + t.Logf("Inspecting only sector-activated events list from real-time SubscribeActorEvents") + var subscribedSectorActivatedEvts []*types.ActorEvent + for evt := range sectorActivatedEvtsChan { + subscribedSectorActivatedEvts = append(subscribedSectorActivatedEvts, evt) + if len(subscribedSectorActivatedEvts) == 2 { + break + } + } + // compare events from messages and receipts with events from subscription channel + require.Equal(t, sectorActivatedEvts, subscribedSectorActivatedEvts) + + // same thing but use historical event fetching to see the same list + t.Logf("Inspecting only sector-activated events list from historical SubscribeActorEvents") + sectorActivatedEvtsChan, err = miner.FullNode.SubscribeActorEvents(ctx, &types.ActorEventFilter{ + Fields: map[string][]types.ActorEventBlock{ + "$type": { + {Codec: uint64(multicodec.Cbor), Value: sectorActivatedCbor}, + }, + }, + FromHeight: epochPtr(0), + }) + require.NoError(t, err) + subscribedSectorActivatedEvts = subscribedSectorActivatedEvts[:0] + for evt := range sectorActivatedEvtsChan { + subscribedSectorActivatedEvts = append(subscribedSectorActivatedEvts, evt) + if len(subscribedSectorActivatedEvts) == 2 { + break + } + } + // compare events from messages and receipts with events from subscription channel + require.Equal(t, sectorActivatedEvts, subscribedSectorActivatedEvts) + + // check that our `ToHeight` filter works as expected + t.Logf("Inspecting only initial list of events SubscribeActorEvents with ToHeight") + var initialEvents []*types.ActorEvent + for evt := range initialEventsChan { + initialEvents = append(initialEvents, evt) + } + // sector-precommitted, sector-activated, verifier-balance, verifier-balance, allocation, allocation + require.Equal(t, eventsFromMessages[0:6], initialEvents) + + // construct ActorEvents from subscription channel for all actor events + t.Logf("Inspecting full events list from historical SubscribeActorEvents") + allEvtsChan, err := miner.FullNode.SubscribeActorEvents(ctx, &types.ActorEventFilter{ + FromHeight: epochPtr(0), + }) + require.NoError(t, err) + var prefillEvts []*types.ActorEvent + for evt := range allEvtsChan { + prefillEvts = append(prefillEvts, evt) + if len(prefillEvts) == len(eventsFromMessages) { + break + } + } + // compare events from messages and receipts with events from subscription channel + require.Equal(t, eventsFromMessages, prefillEvts) + t.Logf("All done comparing events") + + // NOTE: There is a delay in finishing this test because the SubscribeActorEvents + // with the ToHeight (initialEventsChan) has to wait at least a full actual epoch before + // realising that there's no more events for that filter. itests run with a different block + // speed than the ActorEventHandler is aware of. +} + +func ddoVerifiedSetupAllocations( + ctx context.Context, + t *testing.T, + node v1api.FullNode, + minerId uint64, + dc abi.PieceInfo, + verifiedClientAddr address.Address, +) (clientID abi.ActorID, allocationID verifregtypes13.AllocationId) { + + head, err := node.ChainHead(ctx) + require.NoError(t, err) + + // design this one to expire so we can observe allocation-removed + expiringAllocationHeight := head.Height() + 100 + allocationRequestBork := verifregtypes13.AllocationRequest{ + Provider: abi.ActorID(minerId), + Data: cid.MustParse("baga6ea4seaaqa"), + Size: dc.Size, + TermMin: verifregtypes13.MinimumVerifiedAllocationTerm, + TermMax: verifregtypes13.MaximumVerifiedAllocationTerm, + Expiration: expiringAllocationHeight, + } + allocationRequest := verifregtypes13.AllocationRequest{ + Provider: abi.ActorID(minerId), + Data: dc.PieceCID, + Size: dc.Size, + TermMin: verifregtypes13.MinimumVerifiedAllocationTerm, + TermMax: verifregtypes13.MaximumVerifiedAllocationTerm, + Expiration: verifregtypes13.MaximumVerifiedAllocationExpiration, + } + + allocationRequests := verifregtypes13.AllocationRequests{ + Allocations: []verifregtypes13.AllocationRequest{allocationRequestBork, allocationRequest}, + } + + receiverParams, aerr := actors.SerializeParams(&allocationRequests) + require.NoError(t, aerr) + + transferParams, aerr := actors.SerializeParams(&datacap2.TransferParams{ + To: builtin.VerifiedRegistryActorAddr, + Amount: big.Mul(big.NewInt(int64(dc.Size*2)), builtin.TokenPrecision), + OperatorData: receiverParams, + }) + require.NoError(t, aerr) + + msg := &types.Message{ + To: builtin.DatacapActorAddr, + From: verifiedClientAddr, + Method: datacap.Methods.TransferExported, + Params: transferParams, + Value: big.Zero(), + } + + sm, err := node.MpoolPushMessage(ctx, msg, nil) + require.NoError(t, err) + + res, err := node.StateWaitMsg(ctx, sm.Cid(), 1, lapi.LookbackNoLimit, true) + require.NoError(t, err) + require.EqualValues(t, 0, res.Receipt.ExitCode) + + // check that we have an allocation + allocations, err := node.StateGetAllocations(ctx, verifiedClientAddr, types.EmptyTSK) + require.NoError(t, err) + require.Len(t, allocations, 2) // allocation waiting to be claimed + + for key, value := range allocations { + if value.Data == dc.PieceCID { + allocationID = verifregtypes13.AllocationId(key) + clientID = value.Client + break + } + } + require.NotEqual(t, verifregtypes13.AllocationId(0), allocationID) // found it in there + return clientID, allocationID +} + +func ddoVerifiedOnboardPiece(ctx context.Context, t *testing.T, miner *kit.TestMiner, clientId abi.ActorID, allocationId verifregtypes13.AllocationId, dc abi.PieceInfo, pieceData []byte) (lapi.SectorOffset, lapi.SectorInfo) { + head, err := miner.FullNode.ChainHead(ctx) + require.NoError(t, err) + + so, err := miner.SectorAddPieceToAny(ctx, dc.Size.Unpadded(), bytes.NewReader(pieceData), piece.PieceDealInfo{ + PublishCid: nil, + DealID: 0, + DealProposal: nil, + DealSchedule: piece.DealSchedule{ + StartEpoch: head.Height() + 2880*2, + EndEpoch: head.Height() + 2880*400, + }, + KeepUnsealed: true, + PieceActivationManifest: &minertypes.PieceActivationManifest{ + CID: dc.PieceCID, + Size: dc.Size, + VerifiedAllocationKey: &minertypes13.VerifiedAllocationKey{Client: clientId, ID: allocationId}, + Notify: nil, + }, + }) + require.NoError(t, err) + + // wait for sector to commit + miner.WaitSectorsProving(ctx, map[abi.SectorNumber]struct{}{ + so.Sector: {}, + }) + + // Verify that the piece has been onboarded + + si, err := miner.SectorsStatus(ctx, so.Sector, true) + require.NoError(t, err) + require.Equal(t, dc.PieceCID, *si.CommD) + + require.Equal(t, si.DealWeight, big.Zero()) + require.Equal(t, si.VerifiedDealWeight, big.Mul(big.NewInt(int64(dc.Size)), big.NewInt(int64(si.Expiration-si.Activation)))) + + return so, si +} + +func ddoVerifiedRemoveAllocations(ctx context.Context, t *testing.T, node v1api.FullNode, verifiedClientAddr address.Address, clientId abi.ActorID) { + // trigger an allocation removal + removalParams, aerr := actors.SerializeParams(&verifregtypes13.RemoveExpiredAllocationsParams{Client: clientId}) + require.NoError(t, aerr) + + msg := &types.Message{ + To: builtin.VerifiedRegistryActorAddr, + From: verifiedClientAddr, + Method: verifreg.Methods.RemoveExpiredAllocations, + Params: removalParams, + Value: big.Zero(), + } + + sm, err := node.MpoolPushMessage(ctx, msg, nil) + require.NoError(t, err) + + res, err := node.StateWaitMsg(ctx, sm.Cid(), 1, lapi.LookbackNoLimit, true) + require.NoError(t, err) + require.EqualValues(t, 0, res.Receipt.ExitCode) +} + +func ddoVerifiedBuildClaimsFromMessages(ctx context.Context, t *testing.T, eventsFromMessages []*types.ActorEvent, node v1api.FullNode) []*verifregtypes9.Claim { + claimKeyCbor := must.One(ipld.Encode(basicnode.NewString("claim"), dagcbor.Encode)) + claims := make([]*verifregtypes9.Claim, 0) + for _, event := range eventsFromMessages { + var isClaim bool + var claimId int64 = -1 + var providerId int64 = -1 + for _, e := range event.Entries { + if e.Key == "$type" && bytes.Equal(e.Value, claimKeyCbor) { + isClaim = true + } else if isClaim && e.Key == "id" { + nd, err := ipld.DecodeUsingPrototype(e.Value, dagcbor.Decode, bindnode.Prototype((*int64)(nil), nil)) + require.NoError(t, err) + claimId = *bindnode.Unwrap(nd).(*int64) + } else if isClaim && e.Key == "provider" { + nd, err := ipld.DecodeUsingPrototype(e.Value, dagcbor.Decode, bindnode.Prototype((*int64)(nil), nil)) + require.NoError(t, err) + providerId = *bindnode.Unwrap(nd).(*int64) + } + if isClaim && claimId != -1 && providerId != -1 { + provider, err := address.NewIDAddress(uint64(providerId)) + require.NoError(t, err) + claim, err := node.StateGetClaim(ctx, provider, verifregtypes9.ClaimId(claimId), types.EmptyTSK) + require.NoError(t, err) + claims = append(claims, claim) + } + } + } + return claims +} + +func ddoVerifiedBuildActorEventsFromMessages(ctx context.Context, t *testing.T, node v1api.FullNode) []*types.ActorEvent { + actorEvents := make([]*types.ActorEvent, 0) + + head, err := node.ChainHead(ctx) + require.NoError(t, err) + var lastts types.TipSetKey + for height := 0; height < int(head.Height()); height++ { + // for each tipset + ts, err := node.ChainGetTipSetByHeight(ctx, abi.ChainEpoch(height), types.EmptyTSK) + require.NoError(t, err) + if ts.Key() == lastts { + continue + } + lastts = ts.Key() + messages, err := node.ChainGetMessagesInTipset(ctx, ts.Key()) + require.NoError(t, err) + if len(messages) == 0 { + continue + } + for _, m := range messages { + receipt, err := node.StateSearchMsg(ctx, types.EmptyTSK, m.Cid, -1, false) + require.NoError(t, err) + require.NotNil(t, receipt) + // receipt + if receipt.Receipt.EventsRoot != nil { + events, err := node.ChainGetEvents(ctx, *receipt.Receipt.EventsRoot) + require.NoError(t, err) + for _, evt := range events { + // for each event + addr, err := address.NewIDAddress(uint64(evt.Emitter)) + require.NoError(t, err) + + actorEvents = append(actorEvents, &types.ActorEvent{ + Entries: evt.Entries, + Emitter: addr, + Reverted: false, + Height: ts.Height(), + TipSetKey: ts.Key(), + MsgCid: m.Cid, + }) + } + } + } + } + return actorEvents +} + +func ddoVerifiedSetupVerifiedClient(ctx context.Context, t *testing.T, client *kit.TestFullNode, rootKey *key.Key, verifierKey *key.Key, verifiedClientKey *key.Key) (address.Address, address.Address) { + // import the root key. + rootAddr, err := client.WalletImport(ctx, &rootKey.KeyInfo) + require.NoError(t, err) + + // import the verifiers' keys. + verifierAddr, err := client.WalletImport(ctx, &verifierKey.KeyInfo) + require.NoError(t, err) + + // import the verified client's key. + verifiedClientAddr, err := client.WalletImport(ctx, &verifiedClientKey.KeyInfo) + require.NoError(t, err) + + allowance := big.NewInt(100000000000) + params, aerr := actors.SerializeParams(&verifregtypes13.AddVerifierParams{Address: verifierAddr, Allowance: allowance}) + require.NoError(t, aerr) + + msg := &types.Message{ + From: rootAddr, + To: verifreg.Address, + Method: verifreg.Methods.AddVerifier, + Params: params, + Value: big.Zero(), + } + + sm, err := client.MpoolPushMessage(ctx, msg, nil) + require.NoError(t, err, "AddVerifier failed") + + res, err := client.StateWaitMsg(ctx, sm.Cid(), 1, lapi.LookbackNoLimit, true) + require.NoError(t, err) + require.EqualValues(t, 0, res.Receipt.ExitCode) + + verifierAllowance, err := client.StateVerifierStatus(ctx, verifierAddr, types.EmptyTSK) + require.NoError(t, err) + require.Equal(t, allowance, *verifierAllowance) + + // assign datacap to a client + initialDatacap := big.NewInt(10000) + + params, aerr = actors.SerializeParams(&verifregtypes13.AddVerifiedClientParams{Address: verifiedClientAddr, Allowance: initialDatacap}) + require.NoError(t, aerr) + + msg = &types.Message{ + From: verifierAddr, + To: verifreg.Address, + Method: verifreg.Methods.AddVerifiedClient, + Params: params, + Value: big.Zero(), + } + + sm, err = client.MpoolPushMessage(ctx, msg, nil) + require.NoError(t, err) + + res, err = client.StateWaitMsg(ctx, sm.Cid(), 1, lapi.LookbackNoLimit, true) + require.NoError(t, err) + require.EqualValues(t, 0, res.Receipt.ExitCode) + + return verifierAddr, verifiedClientAddr +} + +func filterEvents(events []*types.ActorEvent, key string) []*types.ActorEvent { + keyBytes := must.One(ipld.Encode(basicnode.NewString(key), dagcbor.Encode)) + filtered := make([]*types.ActorEvent, 0) + for _, event := range events { + for _, e := range event.Entries { + if e.Key == "$type" && bytes.Equal(e.Value, keyBytes) { + filtered = append(filtered, event) + break + } + } + } + return filtered +} + +func printEvents(t *testing.T, events []*types.ActorEvent) { + for _, event := range events { + entryStrings := []string{ + fmt.Sprintf("height=%d", event.Height), + fmt.Sprintf("msg=%s", event.MsgCid), + fmt.Sprintf("emitter=%s", event.Emitter), + fmt.Sprintf("reverted=%t", event.Reverted), + } + for _, e := range event.Entries { + // for each event entry + entryStrings = append(entryStrings, fmt.Sprintf("%s=%s", e.Key, eventValueToDagJson(t, e.Codec, e.Value))) + } + fmt.Printf("Event<%s>\n", strings.Join(entryStrings, ", ")) + } +} + +// eventValueToDagJson converts an ActorEvent value to a JSON string for printing. +func eventValueToDagJson(t *testing.T, codec uint64, data []byte) string { + switch codec { + case uint64(multicodec.Cbor): + nd, err := ipld.Decode(data, dagcbor.Decode) + require.NoError(t, err) + byts, err := ipld.Encode(nd, dagjson.Encode) + require.NoError(t, err) + return string(byts) + default: + return fmt.Sprintf("0x%x", data) + } +} + +func epochPtr(ei int64) *abi.ChainEpoch { + ep := abi.ChainEpoch(ei) + return &ep +} diff --git a/itests/kit/ensemble.go b/itests/kit/ensemble.go index a515b0e99..c315b21c9 100644 --- a/itests/kit/ensemble.go +++ b/itests/kit/ensemble.go @@ -1099,14 +1099,14 @@ func importPreSealMeta(ctx context.Context, meta genesis.Miner, mds dtypes.Metad info := &pipeline.SectorInfo{ State: pipeline.Proving, SectorNumber: sector.SectorID, - Pieces: []api.SectorPiece{ - { + Pieces: []pipeline.SafeSectorPiece{ + pipeline.SafePiece(api.SectorPiece{ Piece: abi.PieceInfo{ Size: abi.PaddedPieceSize(meta.SectorSize), PieceCID: commD, }, DealInfo: nil, // todo: likely possible to get, but not really that useful - }, + }), }, CommD: &commD, CommR: &commR, diff --git a/itests/kit/ensemble_opts_nv.go b/itests/kit/ensemble_opts_nv.go index d5bb1930e..18b531e13 100644 --- a/itests/kit/ensemble_opts_nv.go +++ b/itests/kit/ensemble_opts_nv.go @@ -35,12 +35,12 @@ func LatestActorsAt(upgradeHeight abi.ChainEpoch) EnsembleOpt { }) /* inline-gen start */ return UpgradeSchedule(stmgr.Upgrade{ - Network: network.Version20, + Network: network.Version21, Height: -1, }, stmgr.Upgrade{ - Network: network.Version21, + Network: network.Version22, Height: upgradeHeight, - Migration: filcns.UpgradeActorsV12, + Migration: filcns.UpgradeActorsV13, }) /* inline-gen end */ } diff --git a/itests/kit/log.go b/itests/kit/log.go index 0c66427f9..2cb597095 100644 --- a/itests/kit/log.go +++ b/itests/kit/log.go @@ -23,6 +23,7 @@ func QuietMiningLogs() { _ = logging.SetLogLevel("rpc", "ERROR") _ = logging.SetLogLevel("consensus-common", "ERROR") _ = logging.SetLogLevel("dht/RtRefreshManager", "ERROR") + _ = logging.SetLogLevel("consensus-common", "WARN") } func QuietAllLogsExcept(names ...string) { diff --git a/itests/kit/node_opts.go b/itests/kit/node_opts.go index 9af284148..09e789951 100644 --- a/itests/kit/node_opts.go +++ b/itests/kit/node_opts.go @@ -1,6 +1,8 @@ package kit import ( + "math" + "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/big" @@ -63,6 +65,8 @@ var DefaultNodeOpts = nodeOpts{ // test defaults cfg.Fevm.EnableEthRPC = true + cfg.Fevm.Events.MaxFilterHeightRange = math.MaxInt64 + cfg.Events.EnableActorEventsAPI = true return nil }, }, diff --git a/itests/pending_deal_allocation_test.go b/itests/pending_deal_allocation_test.go index c1e0531cf..60b755ac0 100644 --- a/itests/pending_deal_allocation_test.go +++ b/itests/pending_deal_allocation_test.go @@ -180,10 +180,6 @@ func TestGetAllocationForPendingDeal(t *testing.T) { dealIds, err := ret.DealIDs() require.NoError(t, err) - dealInfo, err := api.StateMarketStorageDeal(ctx, dealIds[0], types.EmptyTSK) - require.NoError(t, err) - require.Equal(t, verifregtypes.AllocationId(0), dealInfo.State.VerifiedClaim) // Allocation in State should not be set yet, because it's in the allocation map - allocation, err := api.StateGetAllocationForPendingDeal(ctx, dealIds[0], types.EmptyTSK) require.NoError(t, err) require.Equal(t, dealProposal.PieceCID, allocation.Data) diff --git a/itests/sector_terminate_test.go b/itests/sector_terminate_test.go index 34b325f2a..57cffb006 100644 --- a/itests/sector_terminate_test.go +++ b/itests/sector_terminate_test.go @@ -2,10 +2,15 @@ package itests import ( + "bytes" "context" "testing" "time" + "github.com/ipld/go-ipld-prime" + "github.com/ipld/go-ipld-prime/codec/dagcbor" + "github.com/ipld/go-ipld-prime/node/basicnode" + "github.com/multiformats/go-multicodec" "github.com/stretchr/testify/require" "github.com/filecoin-project/go-bitfield" @@ -13,6 +18,7 @@ import ( "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/itests/kit" + "github.com/filecoin-project/lotus/lib/must" sealing "github.com/filecoin-project/lotus/storage/pipeline" ) @@ -164,4 +170,31 @@ loop: require.Equal(t, p.MinerPower, p.TotalPower) require.Equal(t, types.NewInt(uint64(ssz)*uint64(nSectors-1)), p.MinerPower.RawBytePower) + + // check "sector-terminated" actor event + var epochZero abi.ChainEpoch + allEvents, err := miner.FullNode.GetActorEvents(ctx, &types.ActorEventFilter{ + FromHeight: &epochZero, + }) + require.NoError(t, err) + for _, key := range []string{"sector-precommitted", "sector-activated", "sector-terminated"} { + var found bool + keyBytes := must.One(ipld.Encode(basicnode.NewString(key), dagcbor.Encode)) + for _, event := range allEvents { + for _, e := range event.Entries { + if e.Key == "$type" && bytes.Equal(e.Value, keyBytes) { + found = true + if key == "sector-terminated" { + expectedEntries := []types.EventEntry{ + {Flags: 0x03, Codec: uint64(multicodec.Cbor), Key: "$type", Value: keyBytes}, + {Flags: 0x03, Codec: uint64(multicodec.Cbor), Key: "sector", Value: must.One(ipld.Encode(basicnode.NewInt(int64(toTerminate)), dagcbor.Encode))}, + } + require.ElementsMatch(t, expectedEntries, event.Entries) + } + break + } + } + } + require.True(t, found, "expected to find event %s", key) + } } diff --git a/itests/verifreg_test.go b/itests/verifreg_test.go index ffe50c72b..07a31477d 100644 --- a/itests/verifreg_test.go +++ b/itests/verifreg_test.go @@ -503,3 +503,152 @@ func makeVerifier(ctx context.Context, t *testing.T, api *impl.FullNodeAPI, root require.NoError(t, err) require.Equal(t, allowance, *verifierAllowance) } + +func TestVerifiedListAllAllocationsAndClaims(t *testing.T) { + blockTime := 100 * time.Millisecond + + rootKey, err := key.GenerateKey(types.KTSecp256k1) + require.NoError(t, err) + + verifier1Key, err := key.GenerateKey(types.KTSecp256k1) + require.NoError(t, err) + + verifiedClientKey, err := key.GenerateKey(types.KTBLS) + require.NoError(t, err) + + bal, err := types.ParseFIL("100fil") + require.NoError(t, err) + + node, miner, ens := kit.EnsembleMinimal(t, kit.MockProofs(), + kit.RootVerifier(rootKey, abi.NewTokenAmount(bal.Int64())), + kit.Account(verifier1Key, abi.NewTokenAmount(bal.Int64())), + kit.Account(verifiedClientKey, abi.NewTokenAmount(bal.Int64())), + ) + + ens.InterconnectAll().BeginMining(blockTime) + + api := node.FullNode.(*impl.FullNodeAPI) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + // get VRH + vrh, err := api.StateVerifiedRegistryRootKey(ctx, types.TipSetKey{}) + fmt.Println(vrh.String()) + require.NoError(t, err) + + // import the root key. + rootAddr, err := api.WalletImport(ctx, &rootKey.KeyInfo) + require.NoError(t, err) + + // import the verifiers' keys. + verifier1Addr, err := api.WalletImport(ctx, &verifier1Key.KeyInfo) + require.NoError(t, err) + + // import the verified client's key. + verifiedClientAddr, err := api.WalletImport(ctx, &verifiedClientKey.KeyInfo) + require.NoError(t, err) + + // resolve all keys + + // make the 2 verifiers + + makeVerifier(ctx, t, api, rootAddr, verifier1Addr) + + // assign datacap to a client + initialDatacap := big.NewInt(20000) + + params, err := actors.SerializeParams(&verifregst.AddVerifiedClientParams{Address: verifiedClientAddr, Allowance: initialDatacap}) + require.NoError(t, err) + + msg := &types.Message{ + From: verifier1Addr, + To: verifreg.Address, + Method: verifreg.Methods.AddVerifiedClient, + Params: params, + Value: big.Zero(), + } + + sm, err := api.MpoolPushMessage(ctx, msg, nil) + require.NoError(t, err) + + res, err := api.StateWaitMsg(ctx, sm.Cid(), 1, lapi.LookbackNoLimit, true) + require.NoError(t, err) + require.EqualValues(t, 0, res.Receipt.ExitCode) + + // check datacap balance + dcap, err := api.StateVerifiedClientStatus(ctx, verifiedClientAddr, types.EmptyTSK) + require.NoError(t, err) + require.Equal(t, *dcap, initialDatacap) + + minerId, err := address.IDFromAddress(miner.ActorAddr) + require.NoError(t, err) + + allocationRequest1 := verifregst.AllocationRequest{ + Provider: abi.ActorID(minerId), + Data: cid.MustParse("baga6ea4seaaqa"), + Size: abi.PaddedPieceSize(initialDatacap.Uint64() / 2), + TermMin: verifregst.MinimumVerifiedAllocationTerm, + TermMax: verifregst.MinimumVerifiedAllocationTerm, + Expiration: verifregst.MaximumVerifiedAllocationExpiration, + } + + allocationRequest2 := verifregst.AllocationRequest{ + Provider: abi.ActorID(minerId), + Data: cid.MustParse("baga6ea4seaaqc"), + Size: abi.PaddedPieceSize(initialDatacap.Uint64() / 2), + TermMin: verifregst.MinimumVerifiedAllocationTerm, + TermMax: verifregst.MinimumVerifiedAllocationTerm, + Expiration: verifregst.MaximumVerifiedAllocationExpiration, + } + + allocationRequests := verifregst.AllocationRequests{ + Allocations: []verifregst.AllocationRequest{allocationRequest1, allocationRequest2}, + } + + receiverParams, err := actors.SerializeParams(&allocationRequests) + require.NoError(t, err) + + transferParams, err := actors.SerializeParams(&datacap2.TransferParams{ + To: builtin.VerifiedRegistryActorAddr, + Amount: big.Mul(initialDatacap, builtin.TokenPrecision), + OperatorData: receiverParams, + }) + require.NoError(t, err) + + msg = &types.Message{ + To: builtin.DatacapActorAddr, + From: verifiedClientAddr, + Method: datacap.Methods.TransferExported, + Params: transferParams, + Value: big.Zero(), + } + + sm, err = api.MpoolPushMessage(ctx, msg, nil) + require.NoError(t, err) + + res, err = api.StateWaitMsg(ctx, sm.Cid(), 1, lapi.LookbackNoLimit, true) + require.NoError(t, err) + require.EqualValues(t, 0, res.Receipt.ExitCode) + + allocations, err := api.StateGetAllAllocations(ctx, types.EmptyTSK) + require.NoError(t, err) + + require.Equal(t, 2, len(allocations)) + + var pcids []string + + for _, a := range allocations { + clientIdAddr, err := api.StateLookupID(ctx, verifiedClientAddr, types.EmptyTSK) + require.NoError(t, err) + clientId, err := address.IDFromAddress(clientIdAddr) + require.NoError(t, err) + require.Equal(t, abi.ActorID(clientId), a.Client) + require.Equal(t, abi.ActorID(minerId), a.Provider) + require.Equal(t, abi.PaddedPieceSize(10000), a.Size) + pcids = append(pcids, a.Data.String()) + } + + require.ElementsMatch(t, []string{"baga6ea4seaaqa", "baga6ea4seaaqc"}, pcids) + + // TODO: Add claims check to this test once https://github.com/filecoin-project/lotus/pull/11618 lands +} diff --git a/lib/result/result.go b/lib/result/result.go index 56a9ffab7..4f3a12ee8 100644 --- a/lib/result/result.go +++ b/lib/result/result.go @@ -1,5 +1,7 @@ package result +import "encoding/json" + // Result is a small wrapper type encapsulating Value/Error tuples, mostly for // use when sending values across channels // NOTE: Avoid adding any functionality to this, any "nice" things added here will @@ -39,3 +41,13 @@ func (r Result[T]) Assert(noErrFn func(err error, msgAndArgs ...interface{})) T return r.Value } + +// MarshalJSON implements the json.Marshaler interface, marshalling string error correctly +// this method makes the display in log.Infow nicer +func (r Result[T]) MarshalJSON() ([]byte, error) { + if r.Error != nil { + return json.Marshal(map[string]string{"Error": r.Error.Error()}) + } + + return json.Marshal(map[string]interface{}{"Value": r.Value}) +} diff --git a/markets/storageadapter/ondealsectorcommitted_test.go b/markets/storageadapter/ondealsectorcommitted_test.go index 1d7519ff9..e3d318780 100644 --- a/markets/storageadapter/ondealsectorcommitted_test.go +++ b/markets/storageadapter/ondealsectorcommitted_test.go @@ -55,21 +55,21 @@ func TestOnDealSectorPreCommitted(t *testing.T) { } unfinishedDeal := &api.MarketDeal{ Proposal: proposal, - State: market.DealState{ + State: api.MarketDealState{ SectorStartEpoch: -1, LastUpdatedEpoch: 2, }, } activeDeal := &api.MarketDeal{ Proposal: proposal, - State: market.DealState{ + State: api.MarketDealState{ SectorStartEpoch: 1, LastUpdatedEpoch: 2, }, } slashedDeal := &api.MarketDeal{ Proposal: proposal, - State: market.DealState{ + State: api.MarketDealState{ SectorStartEpoch: 1, LastUpdatedEpoch: 2, SlashEpoch: 2, @@ -277,21 +277,21 @@ func TestOnDealSectorCommitted(t *testing.T) { } unfinishedDeal := &api.MarketDeal{ Proposal: proposal, - State: market.DealState{ + State: api.MarketDealState{ SectorStartEpoch: -1, LastUpdatedEpoch: 2, }, } activeDeal := &api.MarketDeal{ Proposal: proposal, - State: market.DealState{ + State: api.MarketDealState{ SectorStartEpoch: 1, LastUpdatedEpoch: 2, }, } slashedDeal := &api.MarketDeal{ Proposal: proposal, - State: market.DealState{ + State: api.MarketDealState{ SectorStartEpoch: 1, LastUpdatedEpoch: 2, SlashEpoch: 2, diff --git a/markets/storageadapter/provider.go b/markets/storageadapter/provider.go index bdfce6f55..11742c879 100644 --- a/markets/storageadapter/provider.go +++ b/markets/storageadapter/provider.go @@ -32,6 +32,7 @@ import ( "github.com/filecoin-project/lotus/node/config" "github.com/filecoin-project/lotus/node/modules/helpers" pipeline "github.com/filecoin-project/lotus/storage/pipeline" + "github.com/filecoin-project/lotus/storage/pipeline/piece" "github.com/filecoin-project/lotus/storage/sectorblocks" ) @@ -92,11 +93,11 @@ func (n *ProviderNodeAdapter) OnDealComplete(ctx context.Context, deal storagema return nil, xerrors.Errorf("deal.PublishCid can't be nil") } - sdInfo := api.PieceDealInfo{ + sdInfo := piece.PieceDealInfo{ DealID: deal.DealID, DealProposal: &deal.Proposal, PublishCid: deal.PublishCid, - DealSchedule: api.DealSchedule{ + DealSchedule: piece.DealSchedule{ StartEpoch: deal.ClientDealProposal.Proposal.StartEpoch, EndEpoch: deal.ClientDealProposal.Proposal.EndEpoch, }, diff --git a/node/builder_chain.go b/node/builder_chain.go index 348916010..cf6c56428 100644 --- a/node/builder_chain.go +++ b/node/builder_chain.go @@ -17,6 +17,7 @@ import ( "github.com/filecoin-project/lotus/chain/consensus" "github.com/filecoin-project/lotus/chain/consensus/filcns" "github.com/filecoin-project/lotus/chain/events" + "github.com/filecoin-project/lotus/chain/events/filter" "github.com/filecoin-project/lotus/chain/exchange" "github.com/filecoin-project/lotus/chain/gen/slashfilter" "github.com/filecoin-project/lotus/chain/index" @@ -153,6 +154,7 @@ var ChainNode = Options( Override(new(stmgr.StateManagerAPI), rpcstmgr.NewRPCStateManager), Override(new(full.EthModuleAPI), From(new(api.Gateway))), Override(new(full.EthEventAPI), From(new(api.Gateway))), + Override(new(full.ActorEventAPI), From(new(api.Gateway))), ), // Full node API / service startup @@ -250,13 +252,14 @@ func ConfigFullNode(c interface{}) Option { ), // Actor event filtering support - Override(new(events.EventAPI), From(new(modules.EventAPI))), + Override(new(events.EventHelperAPI), From(new(modules.EventHelperAPI))), + Override(new(*filter.EventFilterManager), modules.EventFilterManager(cfg.Fevm)), // in lite-mode Eth api is provided by gateway ApplyIf(isFullNode, If(cfg.Fevm.EnableEthRPC, Override(new(full.EthModuleAPI), modules.EthModuleAPI(cfg.Fevm)), - Override(new(full.EthEventAPI), modules.EthEventAPI(cfg.Fevm)), + Override(new(full.EthEventAPI), modules.EthEventHandler(cfg.Fevm)), ), If(!cfg.Fevm.EnableEthRPC, Override(new(full.EthModuleAPI), &full.EthModuleDummy{}), @@ -264,6 +267,15 @@ func ConfigFullNode(c interface{}) Option { ), ), + ApplyIf(isFullNode, + If(cfg.Events.EnableActorEventsAPI, + Override(new(full.ActorEventAPI), modules.ActorEventHandler(cfg.Events.EnableActorEventsAPI, cfg.Fevm)), + ), + If(!cfg.Events.EnableActorEventsAPI, + Override(new(full.ActorEventAPI), &full.ActorEventDummy{}), + ), + ), + // enable message index for full node when configured by the user, otherwise use dummy. If(cfg.Index.EnableMsgIndex, Override(new(index.MsgIndex), modules.MsgIndex)), If(!cfg.Index.EnableMsgIndex, Override(new(index.MsgIndex), modules.DummyMsgIndex)), diff --git a/node/config/def.go b/node/config/def.go index c660fdb60..475f37517 100644 --- a/node/config/def.go +++ b/node/config/def.go @@ -118,6 +118,9 @@ func DefaultFullNode() *FullNode { MaxFilterHeightRange: 2880, // conservative limit of one day }, }, + Events: EventsConfig{ + EnableActorEventsAPI: false, + }, } } diff --git a/node/config/doc_gen.go b/node/config/doc_gen.go index 6cb93a50e..f28c5abd8 100644 --- a/node/config/doc_gen.go +++ b/node/config/doc_gen.go @@ -362,9 +362,8 @@ see https://lotus.filecoin.io/storage-providers/advanced-configurations/market/# Name: "DisableRealTimeFilterAPI", Type: "bool", - Comment: `EnableEthRPC enables APIs that -DisableRealTimeFilterAPI will disable the RealTimeFilterAPI that can create and query filters for actor events as they are emitted. -The API is enabled when EnableEthRPC is true, but can be disabled selectively with this flag.`, + Comment: `DisableRealTimeFilterAPI will disable the RealTimeFilterAPI that can create and query filters for actor events as they are emitted. +The API is enabled when EnableEthRPC or Events.EnableActorEventsAPI is true, but can be disabled selectively with this flag.`, }, { Name: "DisableHistoricFilterAPI", @@ -372,7 +371,7 @@ The API is enabled when EnableEthRPC is true, but can be disabled selectively wi Comment: `DisableHistoricFilterAPI will disable the HistoricFilterAPI that can create and query filters for actor events that occurred in the past. HistoricFilterAPI maintains a queryable index of events. -The API is enabled when EnableEthRPC is true, but can be disabled selectively with this flag.`, +The API is enabled when EnableEthRPC or Events.EnableActorEventsAPI is true, but can be disabled selectively with this flag.`, }, { Name: "FilterTTL", @@ -410,6 +409,17 @@ the database must already exist and be writeable. If a relative path is provided relative to the CWD (current working directory).`, }, }, + "EventsConfig": { + { + Name: "EnableActorEventsAPI", + Type: "bool", + + Comment: `EnableActorEventsAPI enables the Actor events API that enables clients to consume events +emitted by (smart contracts + built-in Actors). +This will also enable the RealTimeFilterAPI and HistoricFilterAPI by default, but they can be +disabled by setting their respective Disable* options in Fevm.Events.`, + }, + }, "FaultReporterConfig": { { Name: "EnableConsensusFaultReporter", @@ -500,6 +510,12 @@ Set to 0 to keep all mappings`, Comment: ``, }, + { + Name: "Events", + Type: "EventsConfig", + + Comment: ``, + }, { Name: "Index", Type: "IndexConfig", @@ -1680,6 +1696,30 @@ Submitting a smaller number of prove commits per epoch would reduce the possibil Comment: `UseSyntheticPoRep, when set to true, will reduce the amount of cache data held on disk after the completion of PreCommit 2 to 11GiB.`, }, + { + Name: "RequireActivationSuccess", + Type: "bool", + + Comment: `Whether to abort if any sector activation in a batch fails (newly sealed sectors, only with ProveCommitSectors3).`, + }, + { + Name: "RequireActivationSuccessUpdate", + Type: "bool", + + Comment: `Whether to abort if any piece activation notification returns a non-zero exit code (newly sealed sectors, only with ProveCommitSectors3).`, + }, + { + Name: "RequireNotificationSuccess", + Type: "bool", + + Comment: `Whether to abort if any sector activation in a batch fails (updating sectors, only with ProveReplicaUpdates3).`, + }, + { + Name: "RequireNotificationSuccessUpdate", + Type: "bool", + + Comment: `Whether to abort if any piece activation notification returns a non-zero exit code (updating sectors, only with ProveReplicaUpdates3).`, + }, }, "Splitstore": { { diff --git a/node/config/types.go b/node/config/types.go index 6c2812086..789d24103 100644 --- a/node/config/types.go +++ b/node/config/types.go @@ -27,6 +27,7 @@ type FullNode struct { Fees FeeConfig Chainstore Chainstore Fevm FevmConfig + Events EventsConfig Index IndexConfig FaultReporter FaultReporterConfig } @@ -588,6 +589,15 @@ type SealingConfig struct { // UseSyntheticPoRep, when set to true, will reduce the amount of cache data held on disk after the completion of PreCommit 2 to 11GiB. UseSyntheticPoRep bool + + // Whether to abort if any sector activation in a batch fails (newly sealed sectors, only with ProveCommitSectors3). + RequireActivationSuccess bool + // Whether to abort if any piece activation notification returns a non-zero exit code (newly sealed sectors, only with ProveCommitSectors3). + RequireActivationSuccessUpdate bool + // Whether to abort if any sector activation in a batch fails (updating sectors, only with ProveReplicaUpdates3). + RequireNotificationSuccess bool + // Whether to abort if any piece activation notification returns a non-zero exit code (updating sectors, only with ProveReplicaUpdates3). + RequireNotificationSuccessUpdate bool } type SealerConfig struct { @@ -859,14 +869,13 @@ type FevmConfig struct { } type Events struct { - // EnableEthRPC enables APIs that // DisableRealTimeFilterAPI will disable the RealTimeFilterAPI that can create and query filters for actor events as they are emitted. - // The API is enabled when EnableEthRPC is true, but can be disabled selectively with this flag. + // The API is enabled when EnableEthRPC or Events.EnableActorEventsAPI is true, but can be disabled selectively with this flag. DisableRealTimeFilterAPI bool // DisableHistoricFilterAPI will disable the HistoricFilterAPI that can create and query filters for actor events // that occurred in the past. HistoricFilterAPI maintains a queryable index of events. - // The API is enabled when EnableEthRPC is true, but can be disabled selectively with this flag. + // The API is enabled when EnableEthRPC or Events.EnableActorEventsAPI is true, but can be disabled selectively with this flag. DisableHistoricFilterAPI bool // FilterTTL specifies the time to live for actor event filters. Filters that haven't been accessed longer than @@ -895,6 +904,14 @@ type Events struct { // Set upper bound on index size } +type EventsConfig struct { + // EnableActorEventsAPI enables the Actor events API that enables clients to consume events + // emitted by (smart contracts + built-in Actors). + // This will also enable the RealTimeFilterAPI and HistoricFilterAPI by default, but they can be + // disabled by setting their respective Disable* options in Fevm.Events. + EnableActorEventsAPI bool +} + type IndexConfig struct { // EXPERIMENTAL FEATURE. USE WITH CAUTION // EnableMsgIndex enables indexing of messages on chain. @@ -918,6 +935,7 @@ type HarmonyDB struct { // The port to find Yugabyte. Blank for default. Port string } + type FaultReporterConfig struct { // EnableConsensusFaultReporter controls whether the node will monitor and // report consensus faults. When enabled, the node will watch for malicious diff --git a/node/hello/cbor_gen.go b/node/hello/cbor_gen.go index 78e950f6f..91a270ff7 100644 --- a/node/hello/cbor_gen.go +++ b/node/hello/cbor_gen.go @@ -35,7 +35,7 @@ func (t *HelloMessage) MarshalCBOR(w io.Writer) error { } // t.HeaviestTipSet ([]cid.Cid) (slice) - if len(t.HeaviestTipSet) > cbg.MaxLength { + if len(t.HeaviestTipSet) > 8192 { return xerrors.Errorf("Slice value in field t.HeaviestTipSet was too long") } @@ -105,7 +105,7 @@ func (t *HelloMessage) UnmarshalCBOR(r io.Reader) (err error) { return err } - if extra > cbg.MaxLength { + if extra > 8192 { return fmt.Errorf("t.HeaviestTipSet: array too large (%d)", extra) } @@ -136,16 +136,16 @@ func (t *HelloMessage) UnmarshalCBOR(r io.Reader) (err error) { t.HeaviestTipSet[i] = c } + } } - // t.HeaviestTipSetHeight (abi.ChainEpoch) (int64) { maj, extra, err := cr.ReadHeader() - var extraI int64 if err != nil { return err } + var extraI int64 switch maj { case cbg.MajUnsignedInt: extraI = int64(extra) @@ -223,6 +223,7 @@ func (t *LatencyMessage) MarshalCBOR(w io.Writer) error { return err } } + return nil } @@ -252,10 +253,10 @@ func (t *LatencyMessage) UnmarshalCBOR(r io.Reader) (err error) { // t.TArrival (int64) (int64) { maj, extra, err := cr.ReadHeader() - var extraI int64 if err != nil { return err } + var extraI int64 switch maj { case cbg.MajUnsignedInt: extraI = int64(extra) @@ -277,10 +278,10 @@ func (t *LatencyMessage) UnmarshalCBOR(r io.Reader) (err error) { // t.TSent (int64) (int64) { maj, extra, err := cr.ReadHeader() - var extraI int64 if err != nil { return err } + var extraI int64 switch maj { case cbg.MajUnsignedInt: extraI = int64(extra) diff --git a/node/impl/full.go b/node/impl/full.go index bc555c8c2..527a55384 100644 --- a/node/impl/full.go +++ b/node/impl/full.go @@ -35,6 +35,7 @@ type FullNodeAPI struct { full.WalletAPI full.SyncAPI full.EthAPI + full.ActorEventsAPI DS dtypes.MetadataDS NetworkName dtypes.NetworkName diff --git a/node/impl/full/actor_events.go b/node/impl/full/actor_events.go new file mode 100644 index 000000000..fecd1d2b6 --- /dev/null +++ b/node/impl/full/actor_events.go @@ -0,0 +1,376 @@ +package full + +import ( + "context" + "fmt" + "time" + + "github.com/ipfs/go-cid" + "github.com/raulk/clock" + "go.uber.org/fx" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" + + "github.com/filecoin-project/lotus/api" + "github.com/filecoin-project/lotus/chain/events/filter" + "github.com/filecoin-project/lotus/chain/types" +) + +type ActorEventAPI interface { + GetActorEvents(ctx context.Context, filter *types.ActorEventFilter) ([]*types.ActorEvent, error) + SubscribeActorEvents(ctx context.Context, filter *types.ActorEventFilter) (<-chan *types.ActorEvent, error) +} + +var ( + _ ActorEventAPI = *new(api.FullNode) + _ ActorEventAPI = *new(api.Gateway) +) + +type ChainAccessor interface { + GetHeaviestTipSet() *types.TipSet +} + +type EventFilterManager interface { + Install( + ctx context.Context, + minHeight, maxHeight abi.ChainEpoch, + tipsetCid cid.Cid, + addresses []address.Address, + keysWithCodec map[string][]types.ActorEventBlock, + excludeReverted bool, + ) (filter.EventFilter, error) + Remove(ctx context.Context, id types.FilterID) error +} + +type ActorEventsAPI struct { + fx.In + ActorEventAPI +} + +type ActorEventHandler struct { + chain ChainAccessor + eventFilterManager EventFilterManager + blockDelay time.Duration + maxFilterHeightRange abi.ChainEpoch + clock clock.Clock +} + +var _ ActorEventAPI = (*ActorEventHandler)(nil) + +func NewActorEventHandler( + chain ChainAccessor, + eventFilterManager EventFilterManager, + blockDelay time.Duration, + maxFilterHeightRange abi.ChainEpoch, +) *ActorEventHandler { + return &ActorEventHandler{ + chain: chain, + eventFilterManager: eventFilterManager, + blockDelay: blockDelay, + maxFilterHeightRange: maxFilterHeightRange, + clock: clock.New(), + } +} + +func NewActorEventHandlerWithClock( + chain ChainAccessor, + eventFilterManager EventFilterManager, + blockDelay time.Duration, + maxFilterHeightRange abi.ChainEpoch, + clock clock.Clock, +) *ActorEventHandler { + return &ActorEventHandler{ + chain: chain, + eventFilterManager: eventFilterManager, + blockDelay: blockDelay, + maxFilterHeightRange: maxFilterHeightRange, + clock: clock, + } +} + +func (a *ActorEventHandler) GetActorEvents(ctx context.Context, evtFilter *types.ActorEventFilter) ([]*types.ActorEvent, error) { + if a.eventFilterManager == nil { + return nil, api.ErrNotSupported + } + + if evtFilter == nil { + evtFilter = &types.ActorEventFilter{} + } + params, err := a.parseFilter(*evtFilter) + if err != nil { + return nil, err + } + + // Install a filter just for this call, collect events, remove the filter + tipSetCid, err := params.GetTipSetCid() + if err != nil { + return nil, fmt.Errorf("failed to get tipset cid: %w", err) + } + f, err := a.eventFilterManager.Install(ctx, params.MinHeight, params.MaxHeight, tipSetCid, evtFilter.Addresses, evtFilter.Fields, false) + if err != nil { + return nil, err + } + defer func() { + // Remove the temporary filter regardless of the original context. + if err := a.eventFilterManager.Remove(context.Background(), f.ID()); err != nil { + log.Warnf("failed to remove filter: %s", err) + } + }() + return getCollected(ctx, f), nil +} + +type filterParams struct { + MinHeight abi.ChainEpoch + MaxHeight abi.ChainEpoch + TipSetKey types.TipSetKey +} + +func (fp filterParams) GetTipSetCid() (cid.Cid, error) { + if fp.TipSetKey.IsEmpty() { + return cid.Undef, nil + } + return fp.TipSetKey.Cid() +} + +func (a *ActorEventHandler) parseFilter(f types.ActorEventFilter) (*filterParams, error) { + if f.TipSetKey != nil && !f.TipSetKey.IsEmpty() { + if f.FromHeight != nil || f.ToHeight != nil { + return nil, fmt.Errorf("cannot specify both TipSetKey and FromHeight/ToHeight") + } + + return &filterParams{ + MinHeight: 0, + MaxHeight: 0, + TipSetKey: *f.TipSetKey, + }, nil + } + + min, max, err := parseHeightRange(a.chain.GetHeaviestTipSet().Height(), f.FromHeight, f.ToHeight, a.maxFilterHeightRange) + if err != nil { + return nil, err + } + + return &filterParams{ + MinHeight: min, + MaxHeight: max, + TipSetKey: types.EmptyTSK, + }, nil +} + +// parseHeightRange is similar to eth's parseBlockRange but with slightly different semantics but +// results in equivalent values that we can plug in to the EventFilterManager. +// +// * Uses "height", allowing for nillable values rather than strings +// * No "latest" and "earliest", those are now represented by nil on the way in and -1 on the way out +// * No option for hex representation +func parseHeightRange(heaviest abi.ChainEpoch, fromHeight, toHeight *abi.ChainEpoch, maxRange abi.ChainEpoch) (minHeight abi.ChainEpoch, maxHeight abi.ChainEpoch, err error) { + if fromHeight != nil && *fromHeight < 0 { + return 0, 0, fmt.Errorf("range 'from' must be greater than or equal to 0") + } + if fromHeight == nil { + minHeight = -1 + } else { + minHeight = *fromHeight + } + if toHeight == nil { + maxHeight = -1 + } else { + maxHeight = *toHeight + } + + // Validate height ranges are within limits set by node operator + if minHeight == -1 && maxHeight > 0 { + // Here the client is looking for events between the head and some future height + if maxHeight-heaviest > maxRange { + return 0, 0, fmt.Errorf("invalid epoch range: 'to' height is too far in the future (maximum: %d)", maxRange) + } + } else if minHeight >= 0 && maxHeight == -1 { + // Here the client is looking for events between some time in the past and the current head + if heaviest-minHeight > maxRange { + return 0, 0, fmt.Errorf("invalid epoch range: 'from' height is too far in the past (maximum: %d)", maxRange) + } + } else if minHeight >= 0 && maxHeight >= 0 { + if minHeight > maxHeight { + return 0, 0, fmt.Errorf("invalid epoch range: 'to' height (%d) must be after 'from' height (%d)", minHeight, maxHeight) + } else if maxHeight-minHeight > maxRange { + return 0, 0, fmt.Errorf("invalid epoch range: range between to and 'from' heights is too large (maximum: %d)", maxRange) + } + } + return minHeight, maxHeight, nil +} + +func (a *ActorEventHandler) SubscribeActorEvents(ctx context.Context, evtFilter *types.ActorEventFilter) (<-chan *types.ActorEvent, error) { + if a.eventFilterManager == nil { + return nil, api.ErrNotSupported + } + + if evtFilter == nil { + evtFilter = &types.ActorEventFilter{} + } + params, err := a.parseFilter(*evtFilter) + if err != nil { + return nil, err + } + + tipSetCid, err := params.GetTipSetCid() + if err != nil { + return nil, fmt.Errorf("failed to get tipset cid: %w", err) + } + fm, err := a.eventFilterManager.Install(ctx, params.MinHeight, params.MaxHeight, tipSetCid, evtFilter.Addresses, evtFilter.Fields, false) + if err != nil { + return nil, err + } + + // The goal for the code below is to send events on the `out` channel as fast as possible and not + // let it get too far behind the rate at which the events are generated. + // For historical events, we aim to send all events within a single block's time (30s on mainnet). + // This ensures that the client can catch up quickly enough to start receiving new events. + // For ongoing events, we also aim to send all events within a single block's time, so we never + // want to be buffering events (approximately) more than one epoch behind the current head. + // It's approximate because we only update our notion of "current epoch" once per ~blocktime. + + out := make(chan *types.ActorEvent) + + // When we start sending real-time events, we want to make sure that we don't fall behind more + // than one epoch's worth of events (approximately). Capture this value now, before we send + // historical events to allow for a little bit of slack in the historical event sending. + minBacklogHeight := a.chain.GetHeaviestTipSet().Height() - 1 + + go func() { + defer func() { + // tell the caller we're done + close(out) + fm.ClearSubChannel() + if err := a.eventFilterManager.Remove(ctx, fm.ID()); err != nil { + log.Warnf("failed to remove filter: %s", err) + } + }() + + // Handle any historical events that our filter may have picked up ----------------------------- + + evs := getCollected(ctx, fm) + if len(evs) > 0 { + // ensure we get all events out on the channel within one block's time (30s on mainnet) + timer := a.clock.Timer(a.blockDelay) + for _, ev := range evs { + select { + case out <- ev: + case <-timer.C: + log.Errorf("closing event subscription due to slow event sending rate") + timer.Stop() + return + case <-ctx.Done(): + timer.Stop() + return + } + } + timer.Stop() + } + + // for the case where we have a MaxHeight set, we don't get a signal from the filter when we + // reach that height, so we need to check it ourselves, do it now but also in the loop + if params.MaxHeight > 0 && minBacklogHeight+1 >= params.MaxHeight { + return + } + + // Handle ongoing events from the filter ------------------------------------------------------- + + in := make(chan interface{}, 256) + fm.SetSubChannel(in) + + var buffer []*types.ActorEvent + nextBacklogHeightUpdate := a.clock.Now().Add(a.blockDelay) + + collectEvent := func(ev interface{}) bool { + ce, ok := ev.(*filter.CollectedEvent) + if !ok { + log.Errorf("got unexpected value from event filter: %T", ev) + return false + } + + if ce.Height < minBacklogHeight { + // since we mostly care about buffer size, we only trigger a too-slow close when the buffer + // increases, i.e. we collect a new event + log.Errorf("closing event subscription due to slow event sending rate") + return false + } + + buffer = append(buffer, &types.ActorEvent{ + Entries: ce.Entries, + Emitter: ce.EmitterAddr, + Reverted: ce.Reverted, + Height: ce.Height, + TipSetKey: ce.TipSetKey, + MsgCid: ce.MsgCid, + }) + return true + } + + ticker := a.clock.Ticker(a.blockDelay) + defer ticker.Stop() + + for ctx.Err() == nil { + if len(buffer) > 0 { + select { + case ev, ok := <-in: // incoming event + if !ok || !collectEvent(ev) { + return + } + case out <- buffer[0]: // successful send + buffer[0] = nil + buffer = buffer[1:] + case <-ticker.C: + // check that our backlog isn't too big by looking at the oldest event + if buffer[0].Height < minBacklogHeight { + log.Errorf("closing event subscription due to slow event sending rate") + return + } + case <-ctx.Done(): + return + } + } else { + select { + case ev, ok := <-in: // incoming event + if !ok || !collectEvent(ev) { + return + } + case <-ctx.Done(): + return + case <-ticker.C: + currentHeight := a.chain.GetHeaviestTipSet().Height() + if params.MaxHeight > 0 && currentHeight > params.MaxHeight { + // we've reached the filter's MaxHeight, we're done so we can close the channel + return + } + } + } + + if a.clock.Now().After(nextBacklogHeightUpdate) { + minBacklogHeight = a.chain.GetHeaviestTipSet().Height() - 1 + nextBacklogHeightUpdate = a.clock.Now().Add(a.blockDelay) + } + } + }() + + return out, nil +} + +func getCollected(ctx context.Context, f filter.EventFilter) []*types.ActorEvent { + ces := f.TakeCollectedEvents(ctx) + + var out []*types.ActorEvent + + for _, e := range ces { + out = append(out, &types.ActorEvent{ + Entries: e.Entries, + Emitter: e.EmitterAddr, + Reverted: e.Reverted, + Height: e.Height, + TipSetKey: e.TipSetKey, + MsgCid: e.MsgCid, + }) + } + + return out +} diff --git a/node/impl/full/actor_events_test.go b/node/impl/full/actor_events_test.go new file mode 100644 index 000000000..ab446e57b --- /dev/null +++ b/node/impl/full/actor_events_test.go @@ -0,0 +1,780 @@ +package full + +import ( + "context" + "fmt" + pseudo "math/rand" + "sync" + "testing" + "time" + + "github.com/ipfs/go-cid" + "github.com/multiformats/go-multihash" + "github.com/raulk/clock" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/go-state-types/crypto" + + "github.com/filecoin-project/lotus/chain/events/filter" + "github.com/filecoin-project/lotus/chain/types" +) + +var testCid = cid.MustParse("bafyreicmaj5hhoy5mgqvamfhgexxyergw7hdeshizghodwkjg6qmpoco7i") + +func TestParseHeightRange(t *testing.T) { + testCases := []struct { + name string + heaviest abi.ChainEpoch + from *abi.ChainEpoch + to *abi.ChainEpoch + maxRange abi.ChainEpoch + minOut abi.ChainEpoch + maxOut abi.ChainEpoch + errStr string + }{ + { + name: "fails when both are specified and range is greater than max allowed range", + heaviest: 100, + from: epochPtr(256), + to: epochPtr(512), + maxRange: 10, + minOut: 0, + maxOut: 0, + errStr: "too large", + }, + { + name: "fails when min is specified and range is greater than max allowed range", + heaviest: 500, + from: epochPtr(16), + to: nil, + maxRange: 10, + minOut: 0, + maxOut: 0, + errStr: "'from' height is too far in the past", + }, + { + name: "fails when max is specified and range is greater than max allowed range", + heaviest: 500, + from: nil, + to: epochPtr(65536), + maxRange: 10, + minOut: 0, + maxOut: 0, + errStr: "'to' height is too far in the future", + }, + { + name: "fails when from is greater than to", + heaviest: 100, + from: epochPtr(512), + to: epochPtr(256), + maxRange: 10, + minOut: 0, + maxOut: 0, + errStr: "must be after", + }, + { + name: "works when range is valid (nil from)", + heaviest: 500, + from: nil, + to: epochPtr(48), + maxRange: 1000, + minOut: -1, + maxOut: 48, + }, + { + name: "works when range is valid (nil to)", + heaviest: 500, + from: epochPtr(0), + to: nil, + maxRange: 1000, + minOut: 0, + maxOut: -1, + }, + { + name: "works when range is valid (nil from and to)", + heaviest: 500, + from: nil, + to: nil, + maxRange: 1000, + minOut: -1, + maxOut: -1, + }, + { + name: "works when range is valid and specified", + heaviest: 500, + from: epochPtr(16), + to: epochPtr(48), + maxRange: 1000, + minOut: 16, + maxOut: 48, + }, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + req := require.New(t) + min, max, err := parseHeightRange(tc.heaviest, tc.from, tc.to, tc.maxRange) + req.Equal(tc.minOut, min) + req.Equal(tc.maxOut, max) + if tc.errStr != "" { + t.Log(err) + req.Error(err) + req.Contains(err.Error(), tc.errStr) + } else { + req.NoError(err) + } + }) + } +} + +func TestGetActorEvents(t *testing.T) { + ctx := context.Background() + req := require.New(t) + + const ( + seed = 984651320 + maxFilterHeightRange = 100 + ) + + t.Logf("seed: %d", seed) + rng := pseudo.New(pseudo.NewSource(seed)) + + minerAddr, err := address.NewIDAddress(uint64(rng.Int63())) + req.NoError(err) + + testCases := []struct { + name string + filter *types.ActorEventFilter + currentHeight int64 + installMinHeight int64 + installMaxHeight int64 + installTipSetKey cid.Cid + installAddresses []address.Address + installKeysWithCodec map[string][]types.ActorEventBlock + installExcludeReverted bool + expectErr string + }{ + { + name: "nil filter", + filter: nil, + installMinHeight: -1, + installMaxHeight: -1, + }, + { + name: "empty filter", + filter: &types.ActorEventFilter{}, + installMinHeight: -1, + installMaxHeight: -1, + }, + { + name: "basic height range filter", + filter: &types.ActorEventFilter{ + FromHeight: epochPtr(0), + ToHeight: epochPtr(maxFilterHeightRange), + }, + installMinHeight: 0, + installMaxHeight: maxFilterHeightRange, + }, + { + name: "from, no to height", + filter: &types.ActorEventFilter{ + FromHeight: epochPtr(0), + }, + currentHeight: maxFilterHeightRange - 1, + installMinHeight: 0, + installMaxHeight: -1, + }, + { + name: "to, no from height", + filter: &types.ActorEventFilter{ + ToHeight: epochPtr(maxFilterHeightRange - 1), + }, + installMinHeight: -1, + installMaxHeight: maxFilterHeightRange - 1, + }, + { + name: "from, no to height, too far", + filter: &types.ActorEventFilter{ + FromHeight: epochPtr(0), + }, + currentHeight: maxFilterHeightRange + 1, + expectErr: "invalid epoch range: 'from' height is too far in the past", + }, + { + name: "to, no from height, too far", + filter: &types.ActorEventFilter{ + ToHeight: epochPtr(maxFilterHeightRange + 1), + }, + currentHeight: 0, + expectErr: "invalid epoch range: 'to' height is too far in the future", + }, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + efm := newMockEventFilterManager(t) + collectedEvents := makeCollectedEvents(t, rng, 0, 1, 10) + filter := newMockFilter(ctx, t, rng, collectedEvents) + + if tc.expectErr == "" { + efm.expectInstall(abi.ChainEpoch(tc.installMinHeight), abi.ChainEpoch(tc.installMaxHeight), tc.installTipSetKey, tc.installAddresses, tc.installKeysWithCodec, tc.installExcludeReverted, filter) + } + + ts, err := types.NewTipSet([]*types.BlockHeader{newBlockHeader(minerAddr, tc.currentHeight)}) + req.NoError(err) + chain := newMockChainAccessor(t, ts) + + handler := NewActorEventHandler(chain, efm, 50*time.Millisecond, maxFilterHeightRange) + + gotEvents, err := handler.GetActorEvents(ctx, tc.filter) + if tc.expectErr != "" { + req.Error(err) + req.Contains(err.Error(), tc.expectErr) + } else { + req.NoError(err) + expectedEvents := collectedToActorEvents(collectedEvents) + req.Equal(expectedEvents, gotEvents) + efm.requireRemoved(filter.ID()) + } + }) + } +} + +func TestSubscribeActorEvents(t *testing.T) { + const ( + seed = 984651320 + maxFilterHeightRange = 100 + blockDelay = 30 * time.Second + filterStartHeight = 0 + currentHeight = 10 + finishHeight = 20 + eventsPerEpoch = 2 + ) + t.Logf("seed: %d", seed) + rng := pseudo.New(pseudo.NewSource(seed)) + mockClock := clock.NewMock() + + minerAddr, err := address.NewIDAddress(uint64(rng.Int63())) + require.NoError(t, err) + + for _, tc := range []struct { + name string + receiveSpeed time.Duration // how fast will we receive all events _per epoch_ + expectComplete bool // do we expect this to succeed? + endEpoch int // -1 for no end + }{ + {"fast", 0, true, -1}, + {"fast with end", 0, true, finishHeight}, + {"half block speed", blockDelay / 2, true, -1}, + {"half block speed with end", blockDelay / 2, true, finishHeight}, + // testing exactly blockDelay is a border case and will be flaky + {"1.5 block speed", blockDelay * 3 / 2, false, -1}, + {"twice block speed", blockDelay * 2, false, -1}, + } { + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) + defer cancel() + + tc := tc + t.Run(tc.name, func(t *testing.T) { + req := require.New(t) + + mockClock.Set(time.Now()) + mockFilterManager := newMockEventFilterManager(t) + allEvents := makeCollectedEvents(t, rng, filterStartHeight, eventsPerEpoch, finishHeight) + historicalEvents := allEvents[0 : (currentHeight-filterStartHeight)*eventsPerEpoch] + mockFilter := newMockFilter(ctx, t, rng, historicalEvents) + mockFilterManager.expectInstall(abi.ChainEpoch(0), abi.ChainEpoch(tc.endEpoch), cid.Undef, nil, nil, false, mockFilter) + + ts, err := types.NewTipSet([]*types.BlockHeader{newBlockHeader(minerAddr, currentHeight)}) + req.NoError(err) + mockChain := newMockChainAccessor(t, ts) + + handler := NewActorEventHandlerWithClock(mockChain, mockFilterManager, blockDelay, maxFilterHeightRange, mockClock) + + aef := &types.ActorEventFilter{FromHeight: epochPtr(0)} + if tc.endEpoch >= 0 { + aef.ToHeight = epochPtr(tc.endEpoch) + } + eventChan, err := handler.SubscribeActorEvents(ctx, aef) + req.NoError(err) + + // assume we can cleanly pick up all historical events in one go + var gotEvents []*types.ActorEvent + for len(gotEvents) < len(historicalEvents) && ctx.Err() == nil { + select { + case e, ok := <-eventChan: + req.True(ok) + gotEvents = append(gotEvents, e) + case <-ctx.Done(): + t.Fatalf("timed out waiting for event") + } + } + req.Equal(collectedToActorEvents(historicalEvents), gotEvents) + + mockClock.Add(blockDelay) + nextReceiveTime := mockClock.Now() + + // Ticker to simulate both time and the chain advancing, including emitting events at + // the right time directly to the filter. + + var wg sync.WaitGroup + wg.Add(1) + go func() { + defer wg.Done() + for thisHeight := int64(currentHeight); ctx.Err() == nil; thisHeight++ { + ts, err := types.NewTipSet([]*types.BlockHeader{newBlockHeader(minerAddr, thisHeight)}) + req.NoError(err) + mockChain.setHeaviestTipSet(ts) + + var eventsThisEpoch []*filter.CollectedEvent + if thisHeight <= finishHeight { + eventsThisEpoch = allEvents[(thisHeight-filterStartHeight)*eventsPerEpoch : (thisHeight-filterStartHeight+2)*eventsPerEpoch] + } + for i := 0; i < eventsPerEpoch && ctx.Err() == nil; i++ { + if len(eventsThisEpoch) > 0 { + mockFilter.sendEventToChannel(eventsThisEpoch[0]) + eventsThisEpoch = eventsThisEpoch[1:] + } + select { + case <-time.After(2 * time.Millisecond): // allow everyone to catch a breath + mockClock.Add(blockDelay / eventsPerEpoch) + case <-ctx.Done(): + return + } + } + + if thisHeight == finishHeight+1 && tc.expectComplete && tc.endEpoch < 0 && ctx.Err() == nil { + // at finish+1, for the case where we expect clean completion and there is no ToEpoch + // set on the filter, if we send one more event at the next height so we end up with + // something uncollected in the buffer, causing a disconnect + evt := makeCollectedEvents(t, rng, finishHeight+1, 1, finishHeight+1)[0] + mockFilter.sendEventToChannel(evt) + } // else if endEpoch is set, we expect the chain advance to force closure + } + }() + + // Client collecting events off the channel + + var prematureEnd bool + for thisHeight := int64(currentHeight); thisHeight <= finishHeight && !prematureEnd && ctx.Err() == nil; thisHeight++ { + // delay to simulate latency + select { + case <-mockClock.After(nextReceiveTime.Sub(mockClock.Now())): + case <-ctx.Done(): + t.Fatalf("timed out simulating receive delay") + } + + // collect eventsPerEpoch more events + var newEvents []*types.ActorEvent + for len(newEvents) < eventsPerEpoch && !prematureEnd && ctx.Err() == nil { + select { + case e, ok := <-eventChan: // receive the events from the subscription + if ok { + newEvents = append(newEvents, e) + } else { + prematureEnd = true + } + case <-ctx.Done(): + t.Fatalf("timed out waiting for event") + } + nextReceiveTime = nextReceiveTime.Add(tc.receiveSpeed) + } + + if tc.expectComplete || !prematureEnd { + // sanity check that we got what we expected this epoch + req.Len(newEvents, eventsPerEpoch) + epochEvents := allEvents[(thisHeight)*eventsPerEpoch : (thisHeight+1)*eventsPerEpoch] + req.Equal(collectedToActorEvents(epochEvents), newEvents) + gotEvents = append(gotEvents, newEvents...) + } + } + + req.Equal(tc.expectComplete, !prematureEnd, "expected to complete") + if tc.expectComplete { + req.Len(gotEvents, len(allEvents)) + req.Equal(collectedToActorEvents(allEvents), gotEvents) + } else { + req.NotEqual(len(gotEvents), len(allEvents)) + } + + // cleanup + mockFilter.requireClearSubChannelCalledEventually(500 * time.Millisecond) + mockFilterManager.requireRemovedEventually(mockFilter.ID(), 500*time.Millisecond) + cancel() + wg.Wait() // wait for the chain to stop advancing + }) + } +} + +func TestSubscribeActorEvents_OnlyHistorical(t *testing.T) { + // Similar to TestSubscribeActorEvents but we set an explicit end that caps out at the current height + const ( + seed = 984651320 + maxFilterHeightRange = 100 + blockDelay = 30 * time.Second + filterStartHeight = 0 + currentHeight = 10 + eventsPerEpoch = 2 + ) + t.Logf("seed: %d", seed) + rng := pseudo.New(pseudo.NewSource(seed)) + mockClock := clock.NewMock() + + minerAddr, err := address.NewIDAddress(uint64(rng.Int63())) + require.NoError(t, err) + + for _, tc := range []struct { + name string + blockTimeToComplete float64 // fraction of a block time that it takes to receive all events + expectComplete bool // do we expect this to succeed? + }{ + {"fast", 0, true}, + {"half block speed", 0.5, true}, + {"1.5 block speed", 1.5, false}, + {"twice block speed", 2, false}, + } { + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) + defer cancel() + + tc := tc + t.Run(tc.name, func(t *testing.T) { + req := require.New(t) + + mockClock.Set(time.Now()) + mockFilterManager := newMockEventFilterManager(t) + allEvents := makeCollectedEvents(t, rng, filterStartHeight, eventsPerEpoch, currentHeight) + mockFilter := newMockFilter(ctx, t, rng, allEvents) + mockFilterManager.expectInstall(abi.ChainEpoch(0), abi.ChainEpoch(currentHeight), cid.Undef, nil, nil, false, mockFilter) + + ts, err := types.NewTipSet([]*types.BlockHeader{newBlockHeader(minerAddr, currentHeight)}) + req.NoError(err) + mockChain := newMockChainAccessor(t, ts) + + handler := NewActorEventHandlerWithClock(mockChain, mockFilterManager, blockDelay, maxFilterHeightRange, mockClock) + + aef := &types.ActorEventFilter{FromHeight: epochPtr(0), ToHeight: epochPtr(currentHeight)} + eventChan, err := handler.SubscribeActorEvents(ctx, aef) + req.NoError(err) + + var gotEvents []*types.ActorEvent + + // assume we can cleanly pick up all historical events in one go + receiveLoop: + for ctx.Err() == nil { + select { + case e, ok := <-eventChan: + if ok { + gotEvents = append(gotEvents, e) + mockClock.Add(time.Duration(float64(blockDelay) * tc.blockTimeToComplete / float64(len(allEvents)))) + // no need to advance the chain, we're also testing that's not necessary + time.Sleep(2 * time.Millisecond) // catch a breath + } else { + break receiveLoop + } + case <-ctx.Done(): + t.Fatalf("timed out waiting for event, got %d/%d events", len(gotEvents), len(allEvents)) + } + } + if tc.expectComplete { + req.Equal(collectedToActorEvents(allEvents), gotEvents) + } else { + req.NotEqual(len(gotEvents), len(allEvents)) + } + // advance the chain and observe cleanup + ts, err = types.NewTipSet([]*types.BlockHeader{newBlockHeader(minerAddr, currentHeight+1)}) + req.NoError(err) + mockChain.setHeaviestTipSet(ts) + mockClock.Add(blockDelay) + mockFilterManager.requireRemovedEventually(mockFilter.ID(), 1*time.Second) + }) + } +} + +var ( + _ ChainAccessor = (*mockChainAccessor)(nil) + _ filter.EventFilter = (*mockFilter)(nil) + _ EventFilterManager = (*mockEventFilterManager)(nil) +) + +type mockChainAccessor struct { + t *testing.T + ts *types.TipSet + lk sync.Mutex +} + +func newMockChainAccessor(t *testing.T, ts *types.TipSet) *mockChainAccessor { + return &mockChainAccessor{t: t, ts: ts} +} + +func (m *mockChainAccessor) setHeaviestTipSet(ts *types.TipSet) { + m.lk.Lock() + defer m.lk.Unlock() + m.ts = ts +} + +func (m *mockChainAccessor) GetHeaviestTipSet() *types.TipSet { + m.lk.Lock() + defer m.lk.Unlock() + return m.ts +} + +type mockFilter struct { + t *testing.T + ctx context.Context + id types.FilterID + lastTaken time.Time + ch chan<- interface{} + historicalEvents []*filter.CollectedEvent + subChannelCalls int + clearSubChannelCalls int + lk sync.Mutex +} + +func newMockFilter(ctx context.Context, t *testing.T, rng *pseudo.Rand, historicalEvents []*filter.CollectedEvent) *mockFilter { + t.Helper() + var id [32]byte + _, err := rng.Read(id[:]) + require.NoError(t, err) + return &mockFilter{ + t: t, + ctx: ctx, + id: id, + historicalEvents: historicalEvents, + } +} + +func (m *mockFilter) sendEventToChannel(e *filter.CollectedEvent) { + m.lk.Lock() + defer m.lk.Unlock() + if m.ch != nil { + select { + case m.ch <- e: + case <-m.ctx.Done(): + } + } +} + +func (m *mockFilter) requireClearSubChannelCalledEventually(timeout time.Duration) { + m.t.Helper() + require.Eventually(m.t, + func() bool { + m.lk.Lock() + c := m.clearSubChannelCalls + m.lk.Unlock() + switch c { + case 0: + return false + case 1: + return true + default: + m.t.Fatalf("ClearSubChannel called more than once: %d", c) + return false + } + }, timeout, 10*time.Millisecond, "ClearSubChannel is not called exactly once") +} + +func (m *mockFilter) ID() types.FilterID { + return m.id +} + +func (m *mockFilter) LastTaken() time.Time { + return m.lastTaken +} + +func (m *mockFilter) SetSubChannel(ch chan<- interface{}) { + m.t.Helper() + m.lk.Lock() + defer m.lk.Unlock() + m.subChannelCalls++ + m.ch = ch +} + +func (m *mockFilter) ClearSubChannel() { + m.t.Helper() + m.lk.Lock() + defer m.lk.Unlock() + m.clearSubChannelCalls++ + m.ch = nil +} + +func (m *mockFilter) TakeCollectedEvents(context.Context) []*filter.CollectedEvent { + e := m.historicalEvents + m.historicalEvents = nil + m.lastTaken = time.Now() + return e +} + +func (m *mockFilter) CollectEvents(context.Context, *filter.TipSetEvents, bool, filter.AddressResolver) error { + m.t.Fatalf("unexpected call to CollectEvents") + return nil +} + +type filterManagerExpectation struct { + minHeight, maxHeight abi.ChainEpoch + tipsetCid cid.Cid + addresses []address.Address + keysWithCodec map[string][]types.ActorEventBlock + excludeReverted bool + returnFilter filter.EventFilter +} + +type mockEventFilterManager struct { + t *testing.T + expectations []filterManagerExpectation + removed []types.FilterID + lk sync.Mutex +} + +func newMockEventFilterManager(t *testing.T) *mockEventFilterManager { + return &mockEventFilterManager{t: t} +} + +func (m *mockEventFilterManager) expectInstall( + minHeight, maxHeight abi.ChainEpoch, + tipsetCid cid.Cid, + addresses []address.Address, + keysWithCodec map[string][]types.ActorEventBlock, + excludeReverted bool, + returnFilter filter.EventFilter) { + + m.t.Helper() + m.expectations = append(m.expectations, filterManagerExpectation{ + minHeight: minHeight, + maxHeight: maxHeight, + tipsetCid: tipsetCid, + addresses: addresses, + keysWithCodec: keysWithCodec, + excludeReverted: excludeReverted, + returnFilter: returnFilter, + }) +} + +func (m *mockEventFilterManager) requireRemoved(id types.FilterID) { + m.t.Helper() + m.lk.Lock() + defer m.lk.Unlock() + require.Contains(m.t, m.removed, id) +} + +func (m *mockEventFilterManager) requireRemovedEventually(id types.FilterID, timeout time.Duration) { + m.t.Helper() + require.Eventuallyf(m.t, func() bool { + m.lk.Lock() + defer m.lk.Unlock() + if len(m.removed) == 0 { + return false + } + assert.Contains(m.t, m.removed, id) + return true + }, timeout, 10*time.Millisecond, "filter %x not removed", id) +} + +func (m *mockEventFilterManager) Install( + _ context.Context, + minHeight, maxHeight abi.ChainEpoch, + tipsetCid cid.Cid, + addresses []address.Address, + keysWithCodec map[string][]types.ActorEventBlock, + excludeReverted bool, +) (filter.EventFilter, error) { + + require.True(m.t, len(m.expectations) > 0, "unexpected call to Install") + exp := m.expectations[0] + m.expectations = m.expectations[1:] + // check the expectation matches the call then return the attached filter + require.Equal(m.t, exp.minHeight, minHeight) + require.Equal(m.t, exp.maxHeight, maxHeight) + require.Equal(m.t, exp.tipsetCid, tipsetCid) + require.Equal(m.t, exp.addresses, addresses) + require.Equal(m.t, exp.keysWithCodec, keysWithCodec) + require.Equal(m.t, exp.excludeReverted, excludeReverted) + return exp.returnFilter, nil +} + +func (m *mockEventFilterManager) Remove(_ context.Context, id types.FilterID) error { + m.lk.Lock() + defer m.lk.Unlock() + m.removed = append(m.removed, id) + return nil +} + +func newBlockHeader(minerAddr address.Address, height int64) *types.BlockHeader { + return &types.BlockHeader{ + Miner: minerAddr, + Ticket: &types.Ticket{ + VRFProof: []byte("vrf proof0000000vrf proof0000000"), + }, + ElectionProof: &types.ElectionProof{ + VRFProof: []byte("vrf proof0000000vrf proof0000000"), + }, + Parents: []cid.Cid{testCid, testCid}, + ParentMessageReceipts: testCid, + BLSAggregate: &crypto.Signature{Type: crypto.SigTypeBLS, Data: []byte("sign me up")}, + ParentWeight: types.NewInt(123125126212), + Messages: testCid, + Height: abi.ChainEpoch(height), + ParentStateRoot: testCid, + BlockSig: &crypto.Signature{Type: crypto.SigTypeBLS, Data: []byte("sign me up")}, + ParentBaseFee: types.NewInt(3432432843291), + } +} + +func epochPtr(i int) *abi.ChainEpoch { + e := abi.ChainEpoch(i) + return &e +} + +func collectedToActorEvents(collected []*filter.CollectedEvent) []*types.ActorEvent { + var out []*types.ActorEvent + for _, c := range collected { + out = append(out, &types.ActorEvent{ + Entries: c.Entries, + Emitter: c.EmitterAddr, + Reverted: c.Reverted, + Height: c.Height, + TipSetKey: c.TipSetKey, + MsgCid: c.MsgCid, + }) + } + return out +} + +func makeCollectedEvents(t *testing.T, rng *pseudo.Rand, eventStartHeight, eventsPerHeight, eventEndHeight int64) []*filter.CollectedEvent { + var out []*filter.CollectedEvent + for h := eventStartHeight; h <= eventEndHeight; h++ { + for i := int64(0); i < eventsPerHeight; i++ { + out = append(out, makeCollectedEvent(t, rng, types.NewTipSetKey(mkCid(t, fmt.Sprintf("h=%d", h))), abi.ChainEpoch(h))) + } + } + return out +} + +func makeCollectedEvent(t *testing.T, rng *pseudo.Rand, tsKey types.TipSetKey, height abi.ChainEpoch) *filter.CollectedEvent { + addr, err := address.NewIDAddress(uint64(rng.Int63())) + require.NoError(t, err) + + return &filter.CollectedEvent{ + Entries: []types.EventEntry{ + {Flags: 0x01, Key: "k1", Codec: cid.Raw, Value: []byte("v1")}, + {Flags: 0x01, Key: "k2", Codec: cid.Raw, Value: []byte("v2")}, + }, + EmitterAddr: addr, + EventIdx: 0, + Reverted: false, + Height: height, + TipSetKey: tsKey, + MsgIdx: 0, + MsgCid: testCid, + } +} + +func mkCid(t *testing.T, s string) cid.Cid { + h, err := multihash.Sum([]byte(s), multihash.SHA2_256, -1) + require.NoError(t, err) + return cid.NewCidV1(cid.Raw, h) +} diff --git a/node/impl/full/dummy.go b/node/impl/full/dummy.go index 11ff95a63..abe52dec6 100644 --- a/node/impl/full/dummy.go +++ b/node/impl/full/dummy.go @@ -11,6 +11,7 @@ import ( "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/lotus/api" + "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/types/ethtypes" ) @@ -188,3 +189,17 @@ func (e *EthModuleDummy) EthTraceReplayBlockTransactions(ctx context.Context, bl var _ EthModuleAPI = &EthModuleDummy{} var _ EthEventAPI = &EthModuleDummy{} + +var ErrActorEventModuleDisabled = errors.New("module disabled, enable with Events.EnableActorEventsAPI") + +type ActorEventDummy struct{} + +func (a *ActorEventDummy) GetActorEvents(ctx context.Context, filter *types.ActorEventFilter) ([]*types.ActorEvent, error) { + return nil, ErrActorEventModuleDisabled +} + +func (a *ActorEventDummy) SubscribeActorEvents(ctx context.Context, filter *types.ActorEventFilter) (<-chan *types.ActorEvent, error) { + return nil, ErrActorEventModuleDisabled +} + +var _ ActorEventAPI = &ActorEventDummy{} diff --git a/node/impl/full/eth.go b/node/impl/full/eth.go index 031a83605..e7aeafa90 100644 --- a/node/impl/full/eth.go +++ b/node/impl/full/eth.go @@ -12,6 +12,7 @@ import ( "time" "github.com/ipfs/go-cid" + "github.com/multiformats/go-multicodec" cbg "github.com/whyrusleeping/cbor-gen" "go.uber.org/fx" "golang.org/x/xerrors" @@ -136,7 +137,7 @@ type EthModule struct { var _ EthModuleAPI = (*EthModule)(nil) -type EthEvent struct { +type EthEventHandler struct { Chain *store.ChainStore EventFilterManager *filter.EventFilterManager TipSetFilterManager *filter.TipSetFilterManager @@ -147,7 +148,7 @@ type EthEvent struct { SubscribtionCtx context.Context } -var _ EthEventAPI = (*EthEvent)(nil) +var _ EthEventAPI = (*EthEventHandler)(nil) type EthAPI struct { fx.In @@ -1203,7 +1204,7 @@ func (a *EthModule) EthCall(ctx context.Context, tx ethtypes.EthCall, blkParam e return ethtypes.EthBytes{}, nil } -func (e *EthEvent) EthGetLogs(ctx context.Context, filterSpec *ethtypes.EthFilterSpec) (*ethtypes.EthFilterResult, error) { +func (e *EthEventHandler) EthGetLogs(ctx context.Context, filterSpec *ethtypes.EthFilterSpec) (*ethtypes.EthFilterResult, error) { if e.EventFilterManager == nil { return nil, api.ErrNotSupported } @@ -1220,7 +1221,7 @@ func (e *EthEvent) EthGetLogs(ctx context.Context, filterSpec *ethtypes.EthFilte return ethFilterResultFromEvents(ctx, ces, e.SubManager.StateAPI) } -func (e *EthEvent) EthGetFilterChanges(ctx context.Context, id ethtypes.EthFilterID) (*ethtypes.EthFilterResult, error) { +func (e *EthEventHandler) EthGetFilterChanges(ctx context.Context, id ethtypes.EthFilterID) (*ethtypes.EthFilterResult, error) { if e.FilterStore == nil { return nil, api.ErrNotSupported } @@ -1242,7 +1243,7 @@ func (e *EthEvent) EthGetFilterChanges(ctx context.Context, id ethtypes.EthFilte return nil, xerrors.Errorf("unknown filter type") } -func (e *EthEvent) EthGetFilterLogs(ctx context.Context, id ethtypes.EthFilterID) (*ethtypes.EthFilterResult, error) { +func (e *EthEventHandler) EthGetFilterLogs(ctx context.Context, id ethtypes.EthFilterID) (*ethtypes.EthFilterResult, error) { if e.FilterStore == nil { return nil, api.ErrNotSupported } @@ -1260,7 +1261,65 @@ func (e *EthEvent) EthGetFilterLogs(ctx context.Context, id ethtypes.EthFilterID return nil, xerrors.Errorf("wrong filter type") } -func (e *EthEvent) installEthFilterSpec(ctx context.Context, filterSpec *ethtypes.EthFilterSpec) (*filter.EventFilter, error) { +// parseBlockRange is similar to actor event's parseHeightRange but with slightly different semantics +// +// * "block" instead of "height" +// * strings that can have "latest" and "earliest" and nil +// * hex strings for actual heights +func parseBlockRange(heaviest abi.ChainEpoch, fromBlock, toBlock *string, maxRange abi.ChainEpoch) (minHeight abi.ChainEpoch, maxHeight abi.ChainEpoch, err error) { + if fromBlock == nil || *fromBlock == "latest" || len(*fromBlock) == 0 { + minHeight = heaviest + } else if *fromBlock == "earliest" { + minHeight = 0 + } else { + if !strings.HasPrefix(*fromBlock, "0x") { + return 0, 0, xerrors.Errorf("FromBlock is not a hex") + } + epoch, err := ethtypes.EthUint64FromHex(*fromBlock) + if err != nil { + return 0, 0, xerrors.Errorf("invalid epoch") + } + minHeight = abi.ChainEpoch(epoch) + } + + if toBlock == nil || *toBlock == "latest" || len(*toBlock) == 0 { + // here latest means the latest at the time + maxHeight = -1 + } else if *toBlock == "earliest" { + maxHeight = 0 + } else { + if !strings.HasPrefix(*toBlock, "0x") { + return 0, 0, xerrors.Errorf("ToBlock is not a hex") + } + epoch, err := ethtypes.EthUint64FromHex(*toBlock) + if err != nil { + return 0, 0, xerrors.Errorf("invalid epoch") + } + maxHeight = abi.ChainEpoch(epoch) + } + + // Validate height ranges are within limits set by node operator + if minHeight == -1 && maxHeight > 0 { + // Here the client is looking for events between the head and some future height + if maxHeight-heaviest > maxRange { + return 0, 0, xerrors.Errorf("invalid epoch range: to block is too far in the future (maximum: %d)", maxRange) + } + } else if minHeight >= 0 && maxHeight == -1 { + // Here the client is looking for events between some time in the past and the current head + if heaviest-minHeight > maxRange { + return 0, 0, xerrors.Errorf("invalid epoch range: from block is too far in the past (maximum: %d)", maxRange) + } + } else if minHeight >= 0 && maxHeight >= 0 { + if minHeight > maxHeight { + return 0, 0, xerrors.Errorf("invalid epoch range: to block (%d) must be after from block (%d)", minHeight, maxHeight) + } else if maxHeight-minHeight > maxRange { + return 0, 0, xerrors.Errorf("invalid epoch range: range between to and from blocks is too large (maximum: %d)", maxRange) + } + } + return minHeight, maxHeight, nil +} + +func (e *EthEventHandler) installEthFilterSpec(ctx context.Context, filterSpec *ethtypes.EthFilterSpec) (filter.EventFilter, error) { var ( minHeight abi.ChainEpoch maxHeight abi.ChainEpoch @@ -1276,64 +1335,11 @@ func (e *EthEvent) installEthFilterSpec(ctx context.Context, filterSpec *ethtype tipsetCid = filterSpec.BlockHash.ToCid() } else { - if filterSpec.FromBlock == nil || *filterSpec.FromBlock == "latest" { - ts := e.Chain.GetHeaviestTipSet() - minHeight = ts.Height() - } else if *filterSpec.FromBlock == "earliest" { - minHeight = 0 - } else if *filterSpec.FromBlock == "pending" { - return nil, api.ErrNotSupported - } else { - if !strings.HasPrefix(*filterSpec.FromBlock, "0x") { - return nil, xerrors.Errorf("FromBlock is not a hex") - } - epoch, err := ethtypes.EthUint64FromHex(*filterSpec.FromBlock) - if err != nil { - return nil, xerrors.Errorf("invalid epoch") - } - minHeight = abi.ChainEpoch(epoch) + var err error + minHeight, maxHeight, err = parseBlockRange(e.Chain.GetHeaviestTipSet().Height(), filterSpec.FromBlock, filterSpec.ToBlock, e.MaxFilterHeightRange) + if err != nil { + return nil, err } - - if filterSpec.ToBlock == nil || *filterSpec.ToBlock == "latest" { - // here latest means the latest at the time - maxHeight = -1 - } else if *filterSpec.ToBlock == "earliest" { - maxHeight = 0 - } else if *filterSpec.ToBlock == "pending" { - return nil, api.ErrNotSupported - } else { - if !strings.HasPrefix(*filterSpec.ToBlock, "0x") { - return nil, xerrors.Errorf("ToBlock is not a hex") - } - epoch, err := ethtypes.EthUint64FromHex(*filterSpec.ToBlock) - if err != nil { - return nil, xerrors.Errorf("invalid epoch") - } - maxHeight = abi.ChainEpoch(epoch) - } - - // Validate height ranges are within limits set by node operator - if minHeight == -1 && maxHeight > 0 { - // Here the client is looking for events between the head and some future height - ts := e.Chain.GetHeaviestTipSet() - if maxHeight-ts.Height() > e.MaxFilterHeightRange { - return nil, xerrors.Errorf("invalid epoch range: to block is too far in the future (maximum: %d)", e.MaxFilterHeightRange) - } - } else if minHeight >= 0 && maxHeight == -1 { - // Here the client is looking for events between some time in the past and the current head - ts := e.Chain.GetHeaviestTipSet() - if ts.Height()-minHeight > e.MaxFilterHeightRange { - return nil, xerrors.Errorf("invalid epoch range: from block is too far in the past (maximum: %d)", e.MaxFilterHeightRange) - } - - } else if minHeight >= 0 && maxHeight >= 0 { - if minHeight > maxHeight { - return nil, xerrors.Errorf("invalid epoch range: to block (%d) must be after from block (%d)", minHeight, maxHeight) - } else if maxHeight-minHeight > e.MaxFilterHeightRange { - return nil, xerrors.Errorf("invalid epoch range: range between to and from blocks is too large (maximum: %d)", e.MaxFilterHeightRange) - } - } - } // Convert all addresses to filecoin f4 addresses @@ -1350,10 +1356,23 @@ func (e *EthEvent) installEthFilterSpec(ctx context.Context, filterSpec *ethtype return nil, err } - return e.EventFilterManager.Install(ctx, minHeight, maxHeight, tipsetCid, addresses, keys) + return e.EventFilterManager.Install(ctx, minHeight, maxHeight, tipsetCid, addresses, keysToKeysWithCodec(keys), true) } -func (e *EthEvent) EthNewFilter(ctx context.Context, filterSpec *ethtypes.EthFilterSpec) (ethtypes.EthFilterID, error) { +func keysToKeysWithCodec(keys map[string][][]byte) map[string][]types.ActorEventBlock { + keysWithCodec := make(map[string][]types.ActorEventBlock) + for k, v := range keys { + for _, vv := range v { + keysWithCodec[k] = append(keysWithCodec[k], types.ActorEventBlock{ + Codec: uint64(multicodec.Raw), // FEVM smart contract events are always encoded with the `raw` Codec. + Value: vv, + }) + } + } + return keysWithCodec +} + +func (e *EthEventHandler) EthNewFilter(ctx context.Context, filterSpec *ethtypes.EthFilterSpec) (ethtypes.EthFilterID, error) { if e.FilterStore == nil || e.EventFilterManager == nil { return ethtypes.EthFilterID{}, api.ErrNotSupported } @@ -1375,7 +1394,7 @@ func (e *EthEvent) EthNewFilter(ctx context.Context, filterSpec *ethtypes.EthFil return ethtypes.EthFilterID(f.ID()), nil } -func (e *EthEvent) EthNewBlockFilter(ctx context.Context) (ethtypes.EthFilterID, error) { +func (e *EthEventHandler) EthNewBlockFilter(ctx context.Context) (ethtypes.EthFilterID, error) { if e.FilterStore == nil || e.TipSetFilterManager == nil { return ethtypes.EthFilterID{}, api.ErrNotSupported } @@ -1398,7 +1417,7 @@ func (e *EthEvent) EthNewBlockFilter(ctx context.Context) (ethtypes.EthFilterID, return ethtypes.EthFilterID(f.ID()), nil } -func (e *EthEvent) EthNewPendingTransactionFilter(ctx context.Context) (ethtypes.EthFilterID, error) { +func (e *EthEventHandler) EthNewPendingTransactionFilter(ctx context.Context) (ethtypes.EthFilterID, error) { if e.FilterStore == nil || e.MemPoolFilterManager == nil { return ethtypes.EthFilterID{}, api.ErrNotSupported } @@ -1421,7 +1440,7 @@ func (e *EthEvent) EthNewPendingTransactionFilter(ctx context.Context) (ethtypes return ethtypes.EthFilterID(f.ID()), nil } -func (e *EthEvent) EthUninstallFilter(ctx context.Context, id ethtypes.EthFilterID) (bool, error) { +func (e *EthEventHandler) EthUninstallFilter(ctx context.Context, id ethtypes.EthFilterID) (bool, error) { if e.FilterStore == nil { return false, api.ErrNotSupported } @@ -1441,9 +1460,9 @@ func (e *EthEvent) EthUninstallFilter(ctx context.Context, id ethtypes.EthFilter return true, nil } -func (e *EthEvent) uninstallFilter(ctx context.Context, f filter.Filter) error { +func (e *EthEventHandler) uninstallFilter(ctx context.Context, f filter.Filter) error { switch f.(type) { - case *filter.EventFilter: + case filter.EventFilter: err := e.EventFilterManager.Remove(ctx, f.ID()) if err != nil && !errors.Is(err, filter.ErrFilterNotFound) { return err @@ -1471,7 +1490,7 @@ const ( EthSubscribeEventTypePendingTransactions = "newPendingTransactions" ) -func (e *EthEvent) EthSubscribe(ctx context.Context, p jsonrpc.RawParams) (ethtypes.EthSubscriptionID, error) { +func (e *EthEventHandler) EthSubscribe(ctx context.Context, p jsonrpc.RawParams) (ethtypes.EthSubscriptionID, error) { params, err := jsonrpc.DecodeParams[ethtypes.EthSubscribeParams](p) if err != nil { return ethtypes.EthSubscriptionID{}, xerrors.Errorf("decoding params: %w", err) @@ -1524,7 +1543,7 @@ func (e *EthEvent) EthSubscribe(ctx context.Context, p jsonrpc.RawParams) (ethty } } - f, err := e.EventFilterManager.Install(ctx, -1, -1, cid.Undef, addresses, keys) + f, err := e.EventFilterManager.Install(ctx, -1, -1, cid.Undef, addresses, keysToKeysWithCodec(keys), true) if err != nil { // clean up any previous filters added and stop the sub _, _ = e.EthUnsubscribe(ctx, sub.id) @@ -1547,7 +1566,7 @@ func (e *EthEvent) EthSubscribe(ctx context.Context, p jsonrpc.RawParams) (ethty return sub.id, nil } -func (e *EthEvent) EthUnsubscribe(ctx context.Context, id ethtypes.EthSubscriptionID) (bool, error) { +func (e *EthEventHandler) EthUnsubscribe(ctx context.Context, id ethtypes.EthSubscriptionID) (bool, error) { if e.SubManager == nil { return false, api.ErrNotSupported } @@ -1561,7 +1580,7 @@ func (e *EthEvent) EthUnsubscribe(ctx context.Context, id ethtypes.EthSubscripti } // GC runs a garbage collection loop, deleting filters that have not been used within the ttl window -func (e *EthEvent) GC(ctx context.Context, ttl time.Duration) { +func (e *EthEventHandler) GC(ctx context.Context, ttl time.Duration) { if e.FilterStore == nil { return } diff --git a/node/impl/full/eth_event.go b/node/impl/full/eth_events.go similarity index 100% rename from node/impl/full/eth_event.go rename to node/impl/full/eth_events.go diff --git a/node/impl/full/eth_test.go b/node/impl/full/eth_test.go index 05c3f2575..6f9d8f297 100644 --- a/node/impl/full/eth_test.go +++ b/node/impl/full/eth_test.go @@ -3,6 +3,7 @@ package full import ( "bytes" "encoding/hex" + "fmt" "testing" "github.com/ipfs/go-cid" @@ -10,12 +11,87 @@ import ( "github.com/stretchr/testify/require" cbg "github.com/whyrusleeping/cbor-gen" + "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/big" "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/types/ethtypes" ) +func TestParseBlockRange(t *testing.T) { + pstring := func(s string) *string { return &s } + + tcs := map[string]struct { + heaviest abi.ChainEpoch + from *string + to *string + maxRange abi.ChainEpoch + minOut abi.ChainEpoch + maxOut abi.ChainEpoch + errStr string + }{ + "fails when both are specified and range is greater than max allowed range": { + heaviest: 100, + from: pstring("0x100"), + to: pstring("0x200"), + maxRange: 10, + minOut: 0, + maxOut: 0, + errStr: "too large", + }, + "fails when min is specified and range is greater than max allowed range": { + heaviest: 500, + from: pstring("0x10"), + to: pstring("latest"), + maxRange: 10, + minOut: 0, + maxOut: 0, + errStr: "too far in the past", + }, + "fails when max is specified and range is greater than max allowed range": { + heaviest: 500, + from: pstring("earliest"), + to: pstring("0x10000"), + maxRange: 10, + minOut: 0, + maxOut: 0, + errStr: "too large", + }, + "works when range is valid": { + heaviest: 500, + from: pstring("earliest"), + to: pstring("latest"), + maxRange: 1000, + minOut: 0, + maxOut: -1, + }, + "works when range is valid and specified": { + heaviest: 500, + from: pstring("0x10"), + to: pstring("0x30"), + maxRange: 1000, + minOut: 16, + maxOut: 48, + }, + } + + for name, tc := range tcs { + tc2 := tc + t.Run(name, func(t *testing.T) { + min, max, err := parseBlockRange(tc2.heaviest, tc2.from, tc2.to, tc2.maxRange) + require.Equal(t, tc2.minOut, min) + require.Equal(t, tc2.maxOut, max) + if tc2.errStr != "" { + fmt.Println(err) + require.Error(t, err) + require.Contains(t, err.Error(), tc2.errStr) + } else { + require.NoError(t, err) + } + }) + } +} + func TestEthLogFromEvent(t *testing.T) { // basic empty data, topics, ok := ethLogFromEvent(nil) diff --git a/node/impl/full/state.go b/node/impl/full/state.go index 0e92c8e5b..dda889832 100644 --- a/node/impl/full/state.go +++ b/node/impl/full/state.go @@ -762,7 +762,7 @@ func (a *StateAPI) StateMarketDeals(ctx context.Context, tsk types.TipSetKey) (m } out[strconv.FormatInt(int64(dealID), 10)] = &api.MarketDeal{ Proposal: d, - State: *s, + State: api.MakeDealState(s), } return nil }); err != nil { @@ -779,18 +779,27 @@ func (m *StateModule) StateMarketStorageDeal(ctx context.Context, dealId abi.Dea return stmgr.GetStorageDeal(ctx, m.StateManager, dealId, ts) } -func (a *StateAPI) StateGetAllocationForPendingDeal(ctx context.Context, dealId abi.DealID, tsk types.TipSetKey) (*verifreg.Allocation, error) { +func (a *StateAPI) StateGetAllocationIdForPendingDeal(ctx context.Context, dealId abi.DealID, tsk types.TipSetKey) (verifreg.AllocationId, error) { ts, err := a.Chain.GetTipSetFromKey(ctx, tsk) if err != nil { - return nil, xerrors.Errorf("loading tipset %s: %w", tsk, err) + return verifreg.NoAllocationID, xerrors.Errorf("loading tipset %s: %w", tsk, err) } st, err := a.StateManager.GetMarketState(ctx, ts) if err != nil { - return nil, err + return verifreg.NoAllocationID, err } allocationId, err := st.GetAllocationIdForPendingDeal(dealId) + if err != nil { + return verifreg.NoAllocationID, err + } + + return allocationId, nil +} + +func (a *StateAPI) StateGetAllocationForPendingDeal(ctx context.Context, dealId abi.DealID, tsk types.TipSetKey) (*verifreg.Allocation, error) { + allocationId, err := a.StateGetAllocationIdForPendingDeal(ctx, dealId, tsk) if err != nil { return nil, err } @@ -857,6 +866,25 @@ func (a *StateAPI) StateGetAllocations(ctx context.Context, clientAddr address.A return allocations, nil } +func (a *StateAPI) StateGetAllAllocations(ctx context.Context, tsk types.TipSetKey) (map[verifreg.AllocationId]verifreg.Allocation, error) { + ts, err := a.Chain.GetTipSetFromKey(ctx, tsk) + if err != nil { + return nil, xerrors.Errorf("loading tipset %s: %w", tsk, err) + } + + st, err := a.StateManager.GetVerifregState(ctx, ts) + if err != nil { + return nil, xerrors.Errorf("loading verifreg state: %w", err) + } + + allocations, err := st.GetAllAllocations() + if err != nil { + return nil, xerrors.Errorf("getting all allocations: %w", err) + } + + return allocations, nil +} + func (a *StateAPI) StateGetClaim(ctx context.Context, providerAddr address.Address, claimId verifreg.ClaimId, tsk types.TipSetKey) (*verifreg.Claim, error) { idAddr, err := a.StateLookupID(ctx, providerAddr, tsk) if err != nil { @@ -908,6 +936,25 @@ func (a *StateAPI) StateGetClaims(ctx context.Context, providerAddr address.Addr return claims, nil } +func (a *StateAPI) StateGetAllClaims(ctx context.Context, tsk types.TipSetKey) (map[verifreg.ClaimId]verifreg.Claim, error) { + ts, err := a.Chain.GetTipSetFromKey(ctx, tsk) + if err != nil { + return nil, xerrors.Errorf("loading tipset %s: %w", tsk, err) + } + + st, err := a.StateManager.GetVerifregState(ctx, ts) + if err != nil { + return nil, xerrors.Errorf("loading verifreg state: %w", err) + } + + claims, err := st.GetAllClaims() + if err != nil { + return nil, xerrors.Errorf("getting all claims: %w", err) + } + + return claims, nil +} + func (a *StateAPI) StateComputeDataCID(ctx context.Context, maddr address.Address, sectorType abi.RegisteredSealProof, deals []abi.DealID, tsk types.TipSetKey) (cid.Cid, error) { nv, err := a.StateNetworkVersion(ctx, tsk) if err != nil { @@ -1914,6 +1961,8 @@ func (a *StateAPI) StateGetNetworkParams(ctx context.Context) (*api.NetworkParam UpgradeLightningHeight: build.UpgradeLightningHeight, UpgradeThunderHeight: build.UpgradeThunderHeight, UpgradeWatermelonHeight: build.UpgradeWatermelonHeight, + UpgradeDragonHeight: build.UpgradeDragonHeight, + UpgradePhoenixHeight: build.UpgradePhoenixHeight, }, }, nil } diff --git a/node/impl/storminer.go b/node/impl/storminer.go index 2ce42c327..90248a355 100644 --- a/node/impl/storminer.go +++ b/node/impl/storminer.go @@ -54,6 +54,7 @@ import ( "github.com/filecoin-project/lotus/storage/ctladdr" "github.com/filecoin-project/lotus/storage/paths" sealing "github.com/filecoin-project/lotus/storage/pipeline" + "github.com/filecoin-project/lotus/storage/pipeline/piece" "github.com/filecoin-project/lotus/storage/pipeline/sealiface" "github.com/filecoin-project/lotus/storage/sealer" "github.com/filecoin-project/lotus/storage/sealer/fsutil" @@ -243,7 +244,7 @@ func (sm *StorageMinerAPI) SectorsStatus(ctx context.Context, sid abi.SectorNumb return sInfo, nil } -func (sm *StorageMinerAPI) SectorAddPieceToAny(ctx context.Context, size abi.UnpaddedPieceSize, r storiface.Data, d api.PieceDealInfo) (api.SectorOffset, error) { +func (sm *StorageMinerAPI) SectorAddPieceToAny(ctx context.Context, size abi.UnpaddedPieceSize, r storiface.Data, d piece.PieceDealInfo) (api.SectorOffset, error) { so, err := sm.Miner.SectorAddPieceToAny(ctx, size, r, d) if err != nil { // jsonrpc doesn't support returning values with errors, make sure we never do that @@ -506,7 +507,7 @@ func (sm *StorageMinerAPI) ComputeWindowPoSt(ctx context.Context, dlIdx uint64, } func (sm *StorageMinerAPI) ComputeDataCid(ctx context.Context, pieceSize abi.UnpaddedPieceSize, pieceData storiface.Data) (abi.PieceInfo, error) { - return sm.StorageMgr.DataCid(ctx, pieceSize, pieceData) + return sm.IStorageMgr.DataCid(ctx, pieceSize, pieceData) } func (sm *StorageMinerAPI) WorkerConnect(ctx context.Context, url string) error { diff --git a/node/modules/actorevent.go b/node/modules/actorevent.go index 4ce04cefd..135a34e5b 100644 --- a/node/modules/actorevent.go +++ b/node/modules/actorevent.go @@ -5,13 +5,12 @@ import ( "path/filepath" "time" - "github.com/multiformats/go-varint" "go.uber.org/fx" "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/abi" - builtintypes "github.com/filecoin-project/go-state-types/builtin" + "github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/chain/events" "github.com/filecoin-project/lotus/chain/events/filter" "github.com/filecoin-project/lotus/chain/messagepool" @@ -24,20 +23,20 @@ import ( "github.com/filecoin-project/lotus/node/repo" ) -type EventAPI struct { +type EventHelperAPI struct { fx.In full.ChainAPI full.StateAPI } -var _ events.EventAPI = &EventAPI{} +var _ events.EventHelperAPI = &EventHelperAPI{} -func EthEventAPI(cfg config.FevmConfig) func(helpers.MetricsCtx, repo.LockedRepo, fx.Lifecycle, *store.ChainStore, *stmgr.StateManager, EventAPI, *messagepool.MessagePool, full.StateAPI, full.ChainAPI) (*full.EthEvent, 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) (*full.EthEvent, error) { +func EthEventHandler(cfg config.FevmConfig) func(helpers.MetricsCtx, repo.LockedRepo, fx.Lifecycle, *filter.EventFilterManager, *store.ChainStore, *stmgr.StateManager, EventHelperAPI, *messagepool.MessagePool, full.StateAPI, full.ChainAPI) (*full.EthEventHandler, error) { + return func(mctx helpers.MetricsCtx, r repo.LockedRepo, lc fx.Lifecycle, fm *filter.EventFilterManager, cs *store.ChainStore, sm *stmgr.StateManager, evapi EventHelperAPI, mp *messagepool.MessagePool, stateapi full.StateAPI, chainapi full.ChainAPI) (*full.EthEventHandler, error) { ctx := helpers.LifecycleCtx(mctx, lc) - ee := &full.EthEvent{ + ee := &full.EthEventHandler{ Chain: cs, MaxFilterHeightRange: abi.ChainEpoch(cfg.Events.MaxFilterHeightRange), SubscribtionCtx: ctx, @@ -64,6 +63,41 @@ func EthEventAPI(cfg config.FevmConfig) func(helpers.MetricsCtx, repo.LockedRepo }, }) + ee.TipSetFilterManager = &filter.TipSetFilterManager{ + MaxFilterResults: cfg.Events.MaxFilterResults, + } + ee.MemPoolFilterManager = &filter.MemPoolFilterManager{ + MaxFilterResults: cfg.Events.MaxFilterResults, + } + ee.EventFilterManager = fm + + lc.Append(fx.Hook{ + OnStart: func(context.Context) error { + ev, err := events.NewEvents(ctx, &evapi) + if err != nil { + return err + } + // ignore returned tipsets + _ = ev.Observe(ee.TipSetFilterManager) + + ch, err := mp.Updates(ctx) + if err != nil { + return err + } + go ee.MemPoolFilterManager.WaitForMpoolUpdates(ctx, ch) + + return nil + }, + }) + + return ee, nil + } +} + +func EventFilterManager(cfg config.FevmConfig) func(helpers.MetricsCtx, repo.LockedRepo, fx.Lifecycle, *store.ChainStore, *stmgr.StateManager, EventHelperAPI, full.ChainAPI) (*filter.EventFilterManager, error) { + return func(mctx helpers.MetricsCtx, r repo.LockedRepo, lc fx.Lifecycle, cs *store.ChainStore, sm *stmgr.StateManager, evapi EventHelperAPI, chainapi full.ChainAPI) (*filter.EventFilterManager, error) { + ctx := helpers.LifecycleCtx(mctx, lc) + // Enable indexing of actor events var eventIndex *filter.EventIndex if !cfg.Events.DisableHistoricFilterAPI { @@ -91,11 +125,12 @@ func EthEventAPI(cfg config.FevmConfig) func(helpers.MetricsCtx, repo.LockedRepo }) } - ee.EventFilterManager = &filter.EventFilterManager{ + fm := &filter.EventFilterManager{ ChainStore: cs, EventIndex: eventIndex, // will be nil unless EnableHistoricFilterAPI is true + // TODO: + // We don't need this address resolution anymore once https://github.com/filecoin-project/lotus/issues/11594 lands AddressResolver: func(ctx context.Context, emitter abi.ActorID, ts *types.TipSet) (address.Address, bool) { - // we only want to match using f4 addresses idAddr, err := address.NewIDAddress(uint64(emitter)) if err != nil { return address.Undef, false @@ -103,28 +138,14 @@ func EthEventAPI(cfg config.FevmConfig) func(helpers.MetricsCtx, repo.LockedRepo actor, err := sm.LoadActor(ctx, idAddr, ts) if err != nil || actor.Address == nil { - return address.Undef, false + return idAddr, true } - // if robust address is not f4 then we won't match against it so bail early - if actor.Address.Protocol() != address.Delegated { - return address.Undef, false - } - // we have an f4 address, make sure it's assigned by the EAM - if namespace, _, err := varint.FromUvarint(actor.Address.Payload()); err != nil || namespace != builtintypes.EthereumAddressManagerActorID { - return address.Undef, false - } return *actor.Address, true }, MaxFilterResults: cfg.Events.MaxFilterResults, } - ee.TipSetFilterManager = &filter.TipSetFilterManager{ - MaxFilterResults: cfg.Events.MaxFilterResults, - } - ee.MemPoolFilterManager = &filter.MemPoolFilterManager{ - MaxFilterResults: cfg.Events.MaxFilterResults, - } lc.Append(fx.Hook{ OnStart: func(context.Context) error { @@ -132,20 +153,27 @@ func EthEventAPI(cfg config.FevmConfig) func(helpers.MetricsCtx, repo.LockedRepo if err != nil { return err } - // ignore returned tipsets - _ = ev.Observe(ee.EventFilterManager) - _ = ev.Observe(ee.TipSetFilterManager) - - ch, err := mp.Updates(ctx) - if err != nil { - return err - } - go ee.MemPoolFilterManager.WaitForMpoolUpdates(ctx, ch) - + _ = ev.Observe(fm) return nil }, }) - return ee, nil + return fm, nil + } +} + +func ActorEventHandler(enable bool, fevmCfg config.FevmConfig) func(helpers.MetricsCtx, repo.LockedRepo, fx.Lifecycle, *filter.EventFilterManager, *store.ChainStore, *stmgr.StateManager, EventHelperAPI, *messagepool.MessagePool, full.StateAPI, full.ChainAPI) (*full.ActorEventHandler, error) { + return func(mctx helpers.MetricsCtx, r repo.LockedRepo, lc fx.Lifecycle, fm *filter.EventFilterManager, cs *store.ChainStore, sm *stmgr.StateManager, evapi EventHelperAPI, mp *messagepool.MessagePool, stateapi full.StateAPI, chainapi full.ChainAPI) (*full.ActorEventHandler, error) { + + if !enable || fevmCfg.Events.DisableRealTimeFilterAPI { + fm = nil + } + + return full.NewActorEventHandler( + cs, + fm, + time.Duration(build.BlockDelaySecs)*time.Second, + abi.ChainEpoch(fevmCfg.Events.MaxFilterHeightRange), + ), nil } } diff --git a/node/modules/dtypes/beacon.go b/node/modules/dtypes/beacon.go index 28bbdf281..91dd5cf57 100644 --- a/node/modules/dtypes/beacon.go +++ b/node/modules/dtypes/beacon.go @@ -13,4 +13,5 @@ type DrandConfig struct { Servers []string Relays []string ChainInfoJSON string + IsChained bool // Prior to Drand quicknet, beacons form a chain, post quicknet they do not (FIP-0063) } diff --git a/node/modules/ethmodule.go b/node/modules/ethmodule.go index 0255b6198..b36416e4e 100644 --- a/node/modules/ethmodule.go +++ b/node/modules/ethmodule.go @@ -21,8 +21,8 @@ import ( "github.com/filecoin-project/lotus/node/repo" ) -func EthModuleAPI(cfg config.FevmConfig) func(helpers.MetricsCtx, repo.LockedRepo, fx.Lifecycle, *store.ChainStore, *stmgr.StateManager, EventAPI, *messagepool.MessagePool, full.StateAPI, full.ChainAPI, full.MpoolAPI, full.SyncAPI) (*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, syncapi full.SyncAPI) (*full.EthModule, error) { +func EthModuleAPI(cfg config.FevmConfig) func(helpers.MetricsCtx, repo.LockedRepo, fx.Lifecycle, *store.ChainStore, *stmgr.StateManager, EventHelperAPI, *messagepool.MessagePool, full.StateAPI, full.ChainAPI, full.MpoolAPI, full.SyncAPI) (*full.EthModule, error) { + return func(mctx helpers.MetricsCtx, r repo.LockedRepo, lc fx.Lifecycle, cs *store.ChainStore, sm *stmgr.StateManager, evapi EventHelperAPI, mp *messagepool.MessagePool, stateapi full.StateAPI, chainapi full.ChainAPI, mpoolapi full.MpoolAPI, syncapi full.SyncAPI) (*full.EthModule, error) { sqlitePath, err := r.SqlitePath() if err != nil { return nil, err diff --git a/node/modules/storageminer.go b/node/modules/storageminer.go index 11635a240..1b9988b95 100644 --- a/node/modules/storageminer.go +++ b/node/modules/storageminer.go @@ -1023,6 +1023,11 @@ func NewSetSealConfigFunc(r repo.LockedRepo) (dtypes.SetSealingConfigFunc, error TerminateBatchWait: config.Duration(cfg.TerminateBatchWait), MaxSectorProveCommitsSubmittedPerEpoch: cfg.MaxSectorProveCommitsSubmittedPerEpoch, UseSyntheticPoRep: cfg.UseSyntheticPoRep, + + RequireActivationSuccess: cfg.RequireActivationSuccess, + RequireActivationSuccessUpdate: cfg.RequireActivationSuccessUpdate, + RequireNotificationSuccess: cfg.RequireNotificationSuccess, + RequireNotificationSuccessUpdate: cfg.RequireNotificationSuccessUpdate, } c.SetSealingConfig(newCfg) }) @@ -1068,6 +1073,11 @@ func ToSealingConfig(dealmakingCfg config.DealmakingConfig, sealingCfg config.Se TerminateBatchMin: sealingCfg.TerminateBatchMin, TerminateBatchWait: time.Duration(sealingCfg.TerminateBatchWait), UseSyntheticPoRep: sealingCfg.UseSyntheticPoRep, + + RequireActivationSuccess: sealingCfg.RequireActivationSuccess, + RequireActivationSuccessUpdate: sealingCfg.RequireActivationSuccessUpdate, + RequireNotificationSuccess: sealingCfg.RequireNotificationSuccess, + RequireNotificationSuccessUpdate: sealingCfg.RequireNotificationSuccessUpdate, } } diff --git a/paychmgr/cbor_gen.go b/paychmgr/cbor_gen.go index f97c176a3..3f8aaa7b5 100644 --- a/paychmgr/cbor_gen.go +++ b/paychmgr/cbor_gen.go @@ -34,7 +34,7 @@ func (t *VoucherInfo) MarshalCBOR(w io.Writer) error { } // t.Proof ([]uint8) (slice) - if len("Proof") > cbg.MaxLength { + if len("Proof") > 8192 { return xerrors.Errorf("Value in field \"Proof\" was too long") } @@ -45,7 +45,7 @@ func (t *VoucherInfo) MarshalCBOR(w io.Writer) error { return err } - if len(t.Proof) > cbg.ByteArrayMaxLen { + if len(t.Proof) > 2097152 { return xerrors.Errorf("Byte array in field t.Proof was too long") } @@ -53,12 +53,12 @@ func (t *VoucherInfo) MarshalCBOR(w io.Writer) error { return err } - if _, err := cw.Write(t.Proof[:]); err != nil { + if _, err := cw.Write(t.Proof); err != nil { return err } // t.Voucher (paych.SignedVoucher) (struct) - if len("Voucher") > cbg.MaxLength { + if len("Voucher") > 8192 { return xerrors.Errorf("Value in field \"Voucher\" was too long") } @@ -74,7 +74,7 @@ func (t *VoucherInfo) MarshalCBOR(w io.Writer) error { } // t.Submitted (bool) (bool) - if len("Submitted") > cbg.MaxLength { + if len("Submitted") > 8192 { return xerrors.Errorf("Value in field \"Submitted\" was too long") } @@ -120,7 +120,7 @@ func (t *VoucherInfo) UnmarshalCBOR(r io.Reader) (err error) { for i := uint64(0); i < n; i++ { { - sval, err := cbg.ReadString(cr) + sval, err := cbg.ReadStringWithMax(cr, 8192) if err != nil { return err } @@ -137,7 +137,7 @@ func (t *VoucherInfo) UnmarshalCBOR(r io.Reader) (err error) { return err } - if extra > cbg.ByteArrayMaxLen { + if extra > 2097152 { return fmt.Errorf("t.Proof: byte array too large (%d)", extra) } if maj != cbg.MajByteString { @@ -148,9 +148,10 @@ func (t *VoucherInfo) UnmarshalCBOR(r io.Reader) (err error) { t.Proof = make([]uint8, extra) } - if _, err := io.ReadFull(cr, t.Proof[:]); err != nil { + if _, err := io.ReadFull(cr, t.Proof); err != nil { return err } + // t.Voucher (paych.SignedVoucher) (struct) case "Voucher": @@ -211,7 +212,7 @@ func (t *ChannelInfo) MarshalCBOR(w io.Writer) error { } // t.Amount (big.Int) (struct) - if len("Amount") > cbg.MaxLength { + if len("Amount") > 8192 { return xerrors.Errorf("Value in field \"Amount\" was too long") } @@ -227,7 +228,7 @@ func (t *ChannelInfo) MarshalCBOR(w io.Writer) error { } // t.Target (address.Address) (struct) - if len("Target") > cbg.MaxLength { + if len("Target") > 8192 { return xerrors.Errorf("Value in field \"Target\" was too long") } @@ -243,7 +244,7 @@ func (t *ChannelInfo) MarshalCBOR(w io.Writer) error { } // t.Channel (address.Address) (struct) - if len("Channel") > cbg.MaxLength { + if len("Channel") > 8192 { return xerrors.Errorf("Value in field \"Channel\" was too long") } @@ -259,7 +260,7 @@ func (t *ChannelInfo) MarshalCBOR(w io.Writer) error { } // t.Control (address.Address) (struct) - if len("Control") > cbg.MaxLength { + if len("Control") > 8192 { return xerrors.Errorf("Value in field \"Control\" was too long") } @@ -275,7 +276,7 @@ func (t *ChannelInfo) MarshalCBOR(w io.Writer) error { } // t.NextLane (uint64) (uint64) - if len("NextLane") > cbg.MaxLength { + if len("NextLane") > 8192 { return xerrors.Errorf("Value in field \"NextLane\" was too long") } @@ -291,7 +292,7 @@ func (t *ChannelInfo) MarshalCBOR(w io.Writer) error { } // t.Settling (bool) (bool) - if len("Settling") > cbg.MaxLength { + if len("Settling") > 8192 { return xerrors.Errorf("Value in field \"Settling\" was too long") } @@ -307,7 +308,7 @@ func (t *ChannelInfo) MarshalCBOR(w io.Writer) error { } // t.Vouchers ([]*paychmgr.VoucherInfo) (slice) - if len("Vouchers") > cbg.MaxLength { + if len("Vouchers") > 8192 { return xerrors.Errorf("Value in field \"Vouchers\" was too long") } @@ -318,7 +319,7 @@ func (t *ChannelInfo) MarshalCBOR(w io.Writer) error { return err } - if len(t.Vouchers) > cbg.MaxLength { + if len(t.Vouchers) > 8192 { return xerrors.Errorf("Slice value in field t.Vouchers was too long") } @@ -329,10 +330,11 @@ func (t *ChannelInfo) MarshalCBOR(w io.Writer) error { if err := v.MarshalCBOR(cw); err != nil { return err } + } // t.ChannelID (string) (string) - if len("ChannelID") > cbg.MaxLength { + if len("ChannelID") > 8192 { return xerrors.Errorf("Value in field \"ChannelID\" was too long") } @@ -343,7 +345,7 @@ func (t *ChannelInfo) MarshalCBOR(w io.Writer) error { return err } - if len(t.ChannelID) > cbg.MaxLength { + if len(t.ChannelID) > 8192 { return xerrors.Errorf("Value in field t.ChannelID was too long") } @@ -355,7 +357,7 @@ func (t *ChannelInfo) MarshalCBOR(w io.Writer) error { } // t.CreateMsg (cid.Cid) (struct) - if len("CreateMsg") > cbg.MaxLength { + if len("CreateMsg") > 8192 { return xerrors.Errorf("Value in field \"CreateMsg\" was too long") } @@ -377,7 +379,7 @@ func (t *ChannelInfo) MarshalCBOR(w io.Writer) error { } // t.Direction (uint64) (uint64) - if len("Direction") > cbg.MaxLength { + if len("Direction") > 8192 { return xerrors.Errorf("Value in field \"Direction\" was too long") } @@ -393,7 +395,7 @@ func (t *ChannelInfo) MarshalCBOR(w io.Writer) error { } // t.AddFundsMsg (cid.Cid) (struct) - if len("AddFundsMsg") > cbg.MaxLength { + if len("AddFundsMsg") > 8192 { return xerrors.Errorf("Value in field \"AddFundsMsg\" was too long") } @@ -415,7 +417,7 @@ func (t *ChannelInfo) MarshalCBOR(w io.Writer) error { } // t.PendingAmount (big.Int) (struct) - if len("PendingAmount") > cbg.MaxLength { + if len("PendingAmount") > 8192 { return xerrors.Errorf("Value in field \"PendingAmount\" was too long") } @@ -431,7 +433,7 @@ func (t *ChannelInfo) MarshalCBOR(w io.Writer) error { } // t.AvailableAmount (big.Int) (struct) - if len("AvailableAmount") > cbg.MaxLength { + if len("AvailableAmount") > 8192 { return xerrors.Errorf("Value in field \"AvailableAmount\" was too long") } @@ -447,7 +449,7 @@ func (t *ChannelInfo) MarshalCBOR(w io.Writer) error { } // t.PendingAvailableAmount (big.Int) (struct) - if len("PendingAvailableAmount") > cbg.MaxLength { + if len("PendingAvailableAmount") > 8192 { return xerrors.Errorf("Value in field \"PendingAvailableAmount\" was too long") } @@ -493,7 +495,7 @@ func (t *ChannelInfo) UnmarshalCBOR(r io.Reader) (err error) { for i := uint64(0); i < n; i++ { { - sval, err := cbg.ReadString(cr) + sval, err := cbg.ReadStringWithMax(cr, 8192) if err != nil { return err } @@ -593,7 +595,7 @@ func (t *ChannelInfo) UnmarshalCBOR(r io.Reader) (err error) { return err } - if extra > cbg.MaxLength { + if extra > 8192 { return fmt.Errorf("t.Vouchers: array too large (%d)", extra) } @@ -631,14 +633,14 @@ func (t *ChannelInfo) UnmarshalCBOR(r io.Reader) (err error) { } } + } } - // t.ChannelID (string) (string) case "ChannelID": { - sval, err := cbg.ReadString(cr) + sval, err := cbg.ReadStringWithMax(cr, 8192) if err != nil { return err } @@ -758,7 +760,7 @@ func (t *MsgInfo) MarshalCBOR(w io.Writer) error { } // t.Err (string) (string) - if len("Err") > cbg.MaxLength { + if len("Err") > 8192 { return xerrors.Errorf("Value in field \"Err\" was too long") } @@ -769,7 +771,7 @@ func (t *MsgInfo) MarshalCBOR(w io.Writer) error { return err } - if len(t.Err) > cbg.MaxLength { + if len(t.Err) > 8192 { return xerrors.Errorf("Value in field t.Err was too long") } @@ -781,7 +783,7 @@ func (t *MsgInfo) MarshalCBOR(w io.Writer) error { } // t.MsgCid (cid.Cid) (struct) - if len("MsgCid") > cbg.MaxLength { + if len("MsgCid") > 8192 { return xerrors.Errorf("Value in field \"MsgCid\" was too long") } @@ -797,7 +799,7 @@ func (t *MsgInfo) MarshalCBOR(w io.Writer) error { } // t.Received (bool) (bool) - if len("Received") > cbg.MaxLength { + if len("Received") > 8192 { return xerrors.Errorf("Value in field \"Received\" was too long") } @@ -813,7 +815,7 @@ func (t *MsgInfo) MarshalCBOR(w io.Writer) error { } // t.ChannelID (string) (string) - if len("ChannelID") > cbg.MaxLength { + if len("ChannelID") > 8192 { return xerrors.Errorf("Value in field \"ChannelID\" was too long") } @@ -824,7 +826,7 @@ func (t *MsgInfo) MarshalCBOR(w io.Writer) error { return err } - if len(t.ChannelID) > cbg.MaxLength { + if len(t.ChannelID) > 8192 { return xerrors.Errorf("Value in field t.ChannelID was too long") } @@ -866,7 +868,7 @@ func (t *MsgInfo) UnmarshalCBOR(r io.Reader) (err error) { for i := uint64(0); i < n; i++ { { - sval, err := cbg.ReadString(cr) + sval, err := cbg.ReadStringWithMax(cr, 8192) if err != nil { return err } @@ -879,7 +881,7 @@ func (t *MsgInfo) UnmarshalCBOR(r io.Reader) (err error) { case "Err": { - sval, err := cbg.ReadString(cr) + sval, err := cbg.ReadStringWithMax(cr, 8192) if err != nil { return err } @@ -921,7 +923,7 @@ func (t *MsgInfo) UnmarshalCBOR(r io.Reader) (err error) { case "ChannelID": { - sval, err := cbg.ReadString(cr) + sval, err := cbg.ReadStringWithMax(cr, 8192) if err != nil { return err } diff --git a/storage/pipeline/cbor_gen.go b/storage/pipeline/cbor_gen.go index c832f8a14..f32c48b4d 100644 --- a/storage/pipeline/cbor_gen.go +++ b/storage/pipeline/cbor_gen.go @@ -14,7 +14,6 @@ import ( abi "github.com/filecoin-project/go-state-types/abi" - api "github.com/filecoin-project/lotus/api" storiface "github.com/filecoin-project/lotus/storage/sealer/storiface" ) @@ -36,7 +35,7 @@ func (t *SectorInfo) MarshalCBOR(w io.Writer) error { } // t.Log ([]sealing.Log) (slice) - if len("Log") > cbg.MaxLength { + if len("Log") > 8192 { return xerrors.Errorf("Value in field \"Log\" was too long") } @@ -47,7 +46,7 @@ func (t *SectorInfo) MarshalCBOR(w io.Writer) error { return err } - if len(t.Log) > cbg.MaxLength { + if len(t.Log) > 8192 { return xerrors.Errorf("Slice value in field t.Log was too long") } @@ -58,10 +57,11 @@ func (t *SectorInfo) MarshalCBOR(w io.Writer) error { if err := v.MarshalCBOR(cw); err != nil { return err } + } // t.CommD (cid.Cid) (struct) - if len("CommD") > cbg.MaxLength { + if len("CommD") > 8192 { return xerrors.Errorf("Value in field \"CommD\" was too long") } @@ -83,7 +83,7 @@ func (t *SectorInfo) MarshalCBOR(w io.Writer) error { } // t.CommR (cid.Cid) (struct) - if len("CommR") > cbg.MaxLength { + if len("CommR") > 8192 { return xerrors.Errorf("Value in field \"CommR\" was too long") } @@ -105,7 +105,7 @@ func (t *SectorInfo) MarshalCBOR(w io.Writer) error { } // t.Proof ([]uint8) (slice) - if len("Proof") > cbg.MaxLength { + if len("Proof") > 8192 { return xerrors.Errorf("Value in field \"Proof\" was too long") } @@ -116,7 +116,7 @@ func (t *SectorInfo) MarshalCBOR(w io.Writer) error { return err } - if len(t.Proof) > cbg.ByteArrayMaxLen { + if len(t.Proof) > 2097152 { return xerrors.Errorf("Byte array in field t.Proof was too long") } @@ -124,12 +124,12 @@ func (t *SectorInfo) MarshalCBOR(w io.Writer) error { return err } - if _, err := cw.Write(t.Proof[:]); err != nil { + if _, err := cw.Write(t.Proof); err != nil { return err } // t.State (sealing.SectorState) (string) - if len("State") > cbg.MaxLength { + if len("State") > 8192 { return xerrors.Errorf("Value in field \"State\" was too long") } @@ -140,7 +140,7 @@ func (t *SectorInfo) MarshalCBOR(w io.Writer) error { return err } - if len(t.State) > cbg.MaxLength { + if len(t.State) > 8192 { return xerrors.Errorf("Value in field t.State was too long") } @@ -151,8 +151,8 @@ func (t *SectorInfo) MarshalCBOR(w io.Writer) error { return err } - // t.Pieces ([]api.SectorPiece) (slice) - if len("Pieces") > cbg.MaxLength { + // t.Pieces ([]sealing.SafeSectorPiece) (slice) + if len("Pieces") > 8192 { return xerrors.Errorf("Value in field \"Pieces\" was too long") } @@ -163,7 +163,7 @@ func (t *SectorInfo) MarshalCBOR(w io.Writer) error { return err } - if len(t.Pieces) > cbg.MaxLength { + if len(t.Pieces) > 8192 { return xerrors.Errorf("Slice value in field t.Pieces was too long") } @@ -174,10 +174,11 @@ func (t *SectorInfo) MarshalCBOR(w io.Writer) error { if err := v.MarshalCBOR(cw); err != nil { return err } + } // t.Return (sealing.ReturnState) (string) - if len("Return") > cbg.MaxLength { + if len("Return") > 8192 { return xerrors.Errorf("Value in field \"Return\" was too long") } @@ -188,7 +189,7 @@ func (t *SectorInfo) MarshalCBOR(w io.Writer) error { return err } - if len(t.Return) > cbg.MaxLength { + if len(t.Return) > 8192 { return xerrors.Errorf("Value in field t.Return was too long") } @@ -200,7 +201,7 @@ func (t *SectorInfo) MarshalCBOR(w io.Writer) error { } // t.LastErr (string) (string) - if len("LastErr") > cbg.MaxLength { + if len("LastErr") > 8192 { return xerrors.Errorf("Value in field \"LastErr\" was too long") } @@ -211,7 +212,7 @@ func (t *SectorInfo) MarshalCBOR(w io.Writer) error { return err } - if len(t.LastErr) > cbg.MaxLength { + if len(t.LastErr) > 8192 { return xerrors.Errorf("Value in field t.LastErr was too long") } @@ -222,8 +223,8 @@ func (t *SectorInfo) MarshalCBOR(w io.Writer) error { return err } - // t.CCPieces ([]api.SectorPiece) (slice) - if len("CCPieces") > cbg.MaxLength { + // t.CCPieces ([]sealing.SafeSectorPiece) (slice) + if len("CCPieces") > 8192 { return xerrors.Errorf("Value in field \"CCPieces\" was too long") } @@ -234,7 +235,7 @@ func (t *SectorInfo) MarshalCBOR(w io.Writer) error { return err } - if len(t.CCPieces) > cbg.MaxLength { + if len(t.CCPieces) > 8192 { return xerrors.Errorf("Slice value in field t.CCPieces was too long") } @@ -245,10 +246,11 @@ func (t *SectorInfo) MarshalCBOR(w io.Writer) error { if err := v.MarshalCBOR(cw); err != nil { return err } + } // t.CCUpdate (bool) (bool) - if len("CCUpdate") > cbg.MaxLength { + if len("CCUpdate") > 8192 { return xerrors.Errorf("Value in field \"CCUpdate\" was too long") } @@ -264,7 +266,7 @@ func (t *SectorInfo) MarshalCBOR(w io.Writer) error { } // t.SeedEpoch (abi.ChainEpoch) (int64) - if len("SeedEpoch") > cbg.MaxLength { + if len("SeedEpoch") > 8192 { return xerrors.Errorf("Value in field \"SeedEpoch\" was too long") } @@ -286,7 +288,7 @@ func (t *SectorInfo) MarshalCBOR(w io.Writer) error { } // t.SeedValue (abi.InteractiveSealRandomness) (slice) - if len("SeedValue") > cbg.MaxLength { + if len("SeedValue") > 8192 { return xerrors.Errorf("Value in field \"SeedValue\" was too long") } @@ -297,7 +299,7 @@ func (t *SectorInfo) MarshalCBOR(w io.Writer) error { return err } - if len(t.SeedValue) > cbg.ByteArrayMaxLen { + if len(t.SeedValue) > 2097152 { return xerrors.Errorf("Byte array in field t.SeedValue was too long") } @@ -305,12 +307,12 @@ func (t *SectorInfo) MarshalCBOR(w io.Writer) error { return err } - if _, err := cw.Write(t.SeedValue[:]); err != nil { + if _, err := cw.Write(t.SeedValue); err != nil { return err } // t.SectorType (abi.RegisteredSealProof) (int64) - if len("SectorType") > cbg.MaxLength { + if len("SectorType") > 8192 { return xerrors.Errorf("Value in field \"SectorType\" was too long") } @@ -332,7 +334,7 @@ func (t *SectorInfo) MarshalCBOR(w io.Writer) error { } // t.TicketEpoch (abi.ChainEpoch) (int64) - if len("TicketEpoch") > cbg.MaxLength { + if len("TicketEpoch") > 8192 { return xerrors.Errorf("Value in field \"TicketEpoch\" was too long") } @@ -354,7 +356,7 @@ func (t *SectorInfo) MarshalCBOR(w io.Writer) error { } // t.TicketValue (abi.SealRandomness) (slice) - if len("TicketValue") > cbg.MaxLength { + if len("TicketValue") > 8192 { return xerrors.Errorf("Value in field \"TicketValue\" was too long") } @@ -365,7 +367,7 @@ func (t *SectorInfo) MarshalCBOR(w io.Writer) error { return err } - if len(t.TicketValue) > cbg.ByteArrayMaxLen { + if len(t.TicketValue) > 2097152 { return xerrors.Errorf("Byte array in field t.TicketValue was too long") } @@ -373,12 +375,12 @@ func (t *SectorInfo) MarshalCBOR(w io.Writer) error { return err } - if _, err := cw.Write(t.TicketValue[:]); err != nil { + if _, err := cw.Write(t.TicketValue); err != nil { return err } // t.CreationTime (int64) (int64) - if len("CreationTime") > cbg.MaxLength { + if len("CreationTime") > 8192 { return xerrors.Errorf("Value in field \"CreationTime\" was too long") } @@ -400,7 +402,7 @@ func (t *SectorInfo) MarshalCBOR(w io.Writer) error { } // t.SectorNumber (abi.SectorNumber) (uint64) - if len("SectorNumber") > cbg.MaxLength { + if len("SectorNumber") > 8192 { return xerrors.Errorf("Value in field \"SectorNumber\" was too long") } @@ -416,7 +418,7 @@ func (t *SectorInfo) MarshalCBOR(w io.Writer) error { } // t.TerminatedAt (abi.ChainEpoch) (int64) - if len("TerminatedAt") > cbg.MaxLength { + if len("TerminatedAt") > 8192 { return xerrors.Errorf("Value in field \"TerminatedAt\" was too long") } @@ -438,7 +440,7 @@ func (t *SectorInfo) MarshalCBOR(w io.Writer) error { } // t.UpdateSealed (cid.Cid) (struct) - if len("UpdateSealed") > cbg.MaxLength { + if len("UpdateSealed") > 8192 { return xerrors.Errorf("Value in field \"UpdateSealed\" was too long") } @@ -460,7 +462,7 @@ func (t *SectorInfo) MarshalCBOR(w io.Writer) error { } // t.CommitMessage (cid.Cid) (struct) - if len("CommitMessage") > cbg.MaxLength { + if len("CommitMessage") > 8192 { return xerrors.Errorf("Value in field \"CommitMessage\" was too long") } @@ -482,7 +484,7 @@ func (t *SectorInfo) MarshalCBOR(w io.Writer) error { } // t.InvalidProofs (uint64) (uint64) - if len("InvalidProofs") > cbg.MaxLength { + if len("InvalidProofs") > 8192 { return xerrors.Errorf("Value in field \"InvalidProofs\" was too long") } @@ -498,7 +500,7 @@ func (t *SectorInfo) MarshalCBOR(w io.Writer) error { } // t.PreCommit1Out (storiface.PreCommit1Out) (slice) - if len("PreCommit1Out") > cbg.MaxLength { + if len("PreCommit1Out") > 8192 { return xerrors.Errorf("Value in field \"PreCommit1Out\" was too long") } @@ -509,7 +511,7 @@ func (t *SectorInfo) MarshalCBOR(w io.Writer) error { return err } - if len(t.PreCommit1Out) > cbg.ByteArrayMaxLen { + if len(t.PreCommit1Out) > 2097152 { return xerrors.Errorf("Byte array in field t.PreCommit1Out was too long") } @@ -517,12 +519,12 @@ func (t *SectorInfo) MarshalCBOR(w io.Writer) error { return err } - if _, err := cw.Write(t.PreCommit1Out[:]); err != nil { + if _, err := cw.Write(t.PreCommit1Out); err != nil { return err } // t.FaultReportMsg (cid.Cid) (struct) - if len("FaultReportMsg") > cbg.MaxLength { + if len("FaultReportMsg") > 8192 { return xerrors.Errorf("Value in field \"FaultReportMsg\" was too long") } @@ -544,7 +546,7 @@ func (t *SectorInfo) MarshalCBOR(w io.Writer) error { } // t.UpdateUnsealed (cid.Cid) (struct) - if len("UpdateUnsealed") > cbg.MaxLength { + if len("UpdateUnsealed") > 8192 { return xerrors.Errorf("Value in field \"UpdateUnsealed\" was too long") } @@ -566,7 +568,7 @@ func (t *SectorInfo) MarshalCBOR(w io.Writer) error { } // t.PreCommit1Fails (uint64) (uint64) - if len("PreCommit1Fails") > cbg.MaxLength { + if len("PreCommit1Fails") > 8192 { return xerrors.Errorf("Value in field \"PreCommit1Fails\" was too long") } @@ -582,7 +584,7 @@ func (t *SectorInfo) MarshalCBOR(w io.Writer) error { } // t.PreCommit2Fails (uint64) (uint64) - if len("PreCommit2Fails") > cbg.MaxLength { + if len("PreCommit2Fails") > 8192 { return xerrors.Errorf("Value in field \"PreCommit2Fails\" was too long") } @@ -598,7 +600,7 @@ func (t *SectorInfo) MarshalCBOR(w io.Writer) error { } // t.PreCommitTipSet (types.TipSetKey) (struct) - if len("PreCommitTipSet") > cbg.MaxLength { + if len("PreCommitTipSet") > 8192 { return xerrors.Errorf("Value in field \"PreCommitTipSet\" was too long") } @@ -614,7 +616,7 @@ func (t *SectorInfo) MarshalCBOR(w io.Writer) error { } // t.RemoteDataCache (storiface.SectorLocation) (struct) - if len("RemoteDataCache") > cbg.MaxLength { + if len("RemoteDataCache") > 8192 { return xerrors.Errorf("Value in field \"RemoteDataCache\" was too long") } @@ -630,7 +632,7 @@ func (t *SectorInfo) MarshalCBOR(w io.Writer) error { } // t.PreCommitDeposit (big.Int) (struct) - if len("PreCommitDeposit") > cbg.MaxLength { + if len("PreCommitDeposit") > 8192 { return xerrors.Errorf("Value in field \"PreCommitDeposit\" was too long") } @@ -646,7 +648,7 @@ func (t *SectorInfo) MarshalCBOR(w io.Writer) error { } // t.PreCommitMessage (cid.Cid) (struct) - if len("PreCommitMessage") > cbg.MaxLength { + if len("PreCommitMessage") > 8192 { return xerrors.Errorf("Value in field \"PreCommitMessage\" was too long") } @@ -668,7 +670,7 @@ func (t *SectorInfo) MarshalCBOR(w io.Writer) error { } // t.RemoteDataSealed (storiface.SectorLocation) (struct) - if len("RemoteDataSealed") > cbg.MaxLength { + if len("RemoteDataSealed") > 8192 { return xerrors.Errorf("Value in field \"RemoteDataSealed\" was too long") } @@ -684,7 +686,7 @@ func (t *SectorInfo) MarshalCBOR(w io.Writer) error { } // t.TerminateMessage (cid.Cid) (struct) - if len("TerminateMessage") > cbg.MaxLength { + if len("TerminateMessage") > 8192 { return xerrors.Errorf("Value in field \"TerminateMessage\" was too long") } @@ -706,7 +708,7 @@ func (t *SectorInfo) MarshalCBOR(w io.Writer) error { } // t.RemoteDataUnsealed (storiface.SectorLocation) (struct) - if len("RemoteDataUnsealed") > cbg.MaxLength { + if len("RemoteDataUnsealed") > 8192 { return xerrors.Errorf("Value in field \"RemoteDataUnsealed\" was too long") } @@ -722,7 +724,7 @@ func (t *SectorInfo) MarshalCBOR(w io.Writer) error { } // t.ReplicaUpdateProof (storiface.ReplicaUpdateProof) (slice) - if len("ReplicaUpdateProof") > cbg.MaxLength { + if len("ReplicaUpdateProof") > 8192 { return xerrors.Errorf("Value in field \"ReplicaUpdateProof\" was too long") } @@ -733,7 +735,7 @@ func (t *SectorInfo) MarshalCBOR(w io.Writer) error { return err } - if len(t.ReplicaUpdateProof) > cbg.ByteArrayMaxLen { + if len(t.ReplicaUpdateProof) > 2097152 { return xerrors.Errorf("Byte array in field t.ReplicaUpdateProof was too long") } @@ -741,12 +743,12 @@ func (t *SectorInfo) MarshalCBOR(w io.Writer) error { return err } - if _, err := cw.Write(t.ReplicaUpdateProof[:]); err != nil { + if _, err := cw.Write(t.ReplicaUpdateProof); err != nil { return err } // t.RemoteDataFinalized (bool) (bool) - if len("RemoteDataFinalized") > cbg.MaxLength { + if len("RemoteDataFinalized") > 8192 { return xerrors.Errorf("Value in field \"RemoteDataFinalized\" was too long") } @@ -762,7 +764,7 @@ func (t *SectorInfo) MarshalCBOR(w io.Writer) error { } // t.ReplicaUpdateMessage (cid.Cid) (struct) - if len("ReplicaUpdateMessage") > cbg.MaxLength { + if len("ReplicaUpdateMessage") > 8192 { return xerrors.Errorf("Value in field \"ReplicaUpdateMessage\" was too long") } @@ -784,7 +786,7 @@ func (t *SectorInfo) MarshalCBOR(w io.Writer) error { } // t.RemoteCommit1Endpoint (string) (string) - if len("RemoteCommit1Endpoint") > cbg.MaxLength { + if len("RemoteCommit1Endpoint") > 8192 { return xerrors.Errorf("Value in field \"RemoteCommit1Endpoint\" was too long") } @@ -795,7 +797,7 @@ func (t *SectorInfo) MarshalCBOR(w io.Writer) error { return err } - if len(t.RemoteCommit1Endpoint) > cbg.MaxLength { + if len(t.RemoteCommit1Endpoint) > 8192 { return xerrors.Errorf("Value in field t.RemoteCommit1Endpoint was too long") } @@ -807,7 +809,7 @@ func (t *SectorInfo) MarshalCBOR(w io.Writer) error { } // t.RemoteCommit2Endpoint (string) (string) - if len("RemoteCommit2Endpoint") > cbg.MaxLength { + if len("RemoteCommit2Endpoint") > 8192 { return xerrors.Errorf("Value in field \"RemoteCommit2Endpoint\" was too long") } @@ -818,7 +820,7 @@ func (t *SectorInfo) MarshalCBOR(w io.Writer) error { return err } - if len(t.RemoteCommit2Endpoint) > cbg.MaxLength { + if len(t.RemoteCommit2Endpoint) > 8192 { return xerrors.Errorf("Value in field t.RemoteCommit2Endpoint was too long") } @@ -830,7 +832,7 @@ func (t *SectorInfo) MarshalCBOR(w io.Writer) error { } // t.RemoteSealingDoneEndpoint (string) (string) - if len("RemoteSealingDoneEndpoint") > cbg.MaxLength { + if len("RemoteSealingDoneEndpoint") > 8192 { return xerrors.Errorf("Value in field \"RemoteSealingDoneEndpoint\" was too long") } @@ -841,7 +843,7 @@ func (t *SectorInfo) MarshalCBOR(w io.Writer) error { return err } - if len(t.RemoteSealingDoneEndpoint) > cbg.MaxLength { + if len(t.RemoteSealingDoneEndpoint) > 8192 { return xerrors.Errorf("Value in field t.RemoteSealingDoneEndpoint was too long") } @@ -883,7 +885,7 @@ func (t *SectorInfo) UnmarshalCBOR(r io.Reader) (err error) { for i := uint64(0); i < n; i++ { { - sval, err := cbg.ReadString(cr) + sval, err := cbg.ReadStringWithMax(cr, 8192) if err != nil { return err } @@ -900,7 +902,7 @@ func (t *SectorInfo) UnmarshalCBOR(r io.Reader) (err error) { return err } - if extra > cbg.MaxLength { + if extra > 8192 { return fmt.Errorf("t.Log: array too large (%d)", extra) } @@ -928,9 +930,9 @@ func (t *SectorInfo) UnmarshalCBOR(r io.Reader) (err error) { } } + } } - // t.CommD (cid.Cid) (struct) case "CommD": @@ -985,7 +987,7 @@ func (t *SectorInfo) UnmarshalCBOR(r io.Reader) (err error) { return err } - if extra > cbg.ByteArrayMaxLen { + if extra > 2097152 { return fmt.Errorf("t.Proof: byte array too large (%d)", extra) } if maj != cbg.MajByteString { @@ -996,21 +998,22 @@ func (t *SectorInfo) UnmarshalCBOR(r io.Reader) (err error) { t.Proof = make([]uint8, extra) } - if _, err := io.ReadFull(cr, t.Proof[:]); err != nil { + if _, err := io.ReadFull(cr, t.Proof); err != nil { return err } + // t.State (sealing.SectorState) (string) case "State": { - sval, err := cbg.ReadString(cr) + sval, err := cbg.ReadStringWithMax(cr, 8192) if err != nil { return err } t.State = SectorState(sval) } - // t.Pieces ([]api.SectorPiece) (slice) + // t.Pieces ([]sealing.SafeSectorPiece) (slice) case "Pieces": maj, extra, err = cr.ReadHeader() @@ -1018,7 +1021,7 @@ func (t *SectorInfo) UnmarshalCBOR(r io.Reader) (err error) { return err } - if extra > cbg.MaxLength { + if extra > 8192 { return fmt.Errorf("t.Pieces: array too large (%d)", extra) } @@ -1027,7 +1030,7 @@ func (t *SectorInfo) UnmarshalCBOR(r io.Reader) (err error) { } if extra > 0 { - t.Pieces = make([]api.SectorPiece, extra) + t.Pieces = make([]SafeSectorPiece, extra) } for i := 0; i < int(extra); i++ { @@ -1046,14 +1049,14 @@ func (t *SectorInfo) UnmarshalCBOR(r io.Reader) (err error) { } } + } } - // t.Return (sealing.ReturnState) (string) case "Return": { - sval, err := cbg.ReadString(cr) + sval, err := cbg.ReadStringWithMax(cr, 8192) if err != nil { return err } @@ -1064,14 +1067,14 @@ func (t *SectorInfo) UnmarshalCBOR(r io.Reader) (err error) { case "LastErr": { - sval, err := cbg.ReadString(cr) + sval, err := cbg.ReadStringWithMax(cr, 8192) if err != nil { return err } t.LastErr = string(sval) } - // t.CCPieces ([]api.SectorPiece) (slice) + // t.CCPieces ([]sealing.SafeSectorPiece) (slice) case "CCPieces": maj, extra, err = cr.ReadHeader() @@ -1079,7 +1082,7 @@ func (t *SectorInfo) UnmarshalCBOR(r io.Reader) (err error) { return err } - if extra > cbg.MaxLength { + if extra > 8192 { return fmt.Errorf("t.CCPieces: array too large (%d)", extra) } @@ -1088,7 +1091,7 @@ func (t *SectorInfo) UnmarshalCBOR(r io.Reader) (err error) { } if extra > 0 { - t.CCPieces = make([]api.SectorPiece, extra) + t.CCPieces = make([]SafeSectorPiece, extra) } for i := 0; i < int(extra); i++ { @@ -1107,9 +1110,9 @@ func (t *SectorInfo) UnmarshalCBOR(r io.Reader) (err error) { } } + } } - // t.CCUpdate (bool) (bool) case "CCUpdate": @@ -1132,10 +1135,10 @@ func (t *SectorInfo) UnmarshalCBOR(r io.Reader) (err error) { case "SeedEpoch": { maj, extra, err := cr.ReadHeader() - var extraI int64 if err != nil { return err } + var extraI int64 switch maj { case cbg.MajUnsignedInt: extraI = int64(extra) @@ -1162,7 +1165,7 @@ func (t *SectorInfo) UnmarshalCBOR(r io.Reader) (err error) { return err } - if extra > cbg.ByteArrayMaxLen { + if extra > 2097152 { return fmt.Errorf("t.SeedValue: byte array too large (%d)", extra) } if maj != cbg.MajByteString { @@ -1173,17 +1176,18 @@ func (t *SectorInfo) UnmarshalCBOR(r io.Reader) (err error) { t.SeedValue = make([]uint8, extra) } - if _, err := io.ReadFull(cr, t.SeedValue[:]); err != nil { + if _, err := io.ReadFull(cr, t.SeedValue); err != nil { return err } + // t.SectorType (abi.RegisteredSealProof) (int64) case "SectorType": { maj, extra, err := cr.ReadHeader() - var extraI int64 if err != nil { return err } + var extraI int64 switch maj { case cbg.MajUnsignedInt: extraI = int64(extra) @@ -1206,10 +1210,10 @@ func (t *SectorInfo) UnmarshalCBOR(r io.Reader) (err error) { case "TicketEpoch": { maj, extra, err := cr.ReadHeader() - var extraI int64 if err != nil { return err } + var extraI int64 switch maj { case cbg.MajUnsignedInt: extraI = int64(extra) @@ -1236,7 +1240,7 @@ func (t *SectorInfo) UnmarshalCBOR(r io.Reader) (err error) { return err } - if extra > cbg.ByteArrayMaxLen { + if extra > 2097152 { return fmt.Errorf("t.TicketValue: byte array too large (%d)", extra) } if maj != cbg.MajByteString { @@ -1247,17 +1251,18 @@ func (t *SectorInfo) UnmarshalCBOR(r io.Reader) (err error) { t.TicketValue = make([]uint8, extra) } - if _, err := io.ReadFull(cr, t.TicketValue[:]); err != nil { + if _, err := io.ReadFull(cr, t.TicketValue); err != nil { return err } + // t.CreationTime (int64) (int64) case "CreationTime": { maj, extra, err := cr.ReadHeader() - var extraI int64 if err != nil { return err } + var extraI int64 switch maj { case cbg.MajUnsignedInt: extraI = int64(extra) @@ -1295,10 +1300,10 @@ func (t *SectorInfo) UnmarshalCBOR(r io.Reader) (err error) { case "TerminatedAt": { maj, extra, err := cr.ReadHeader() - var extraI int64 if err != nil { return err } + var extraI int64 switch maj { case cbg.MajUnsignedInt: extraI = int64(extra) @@ -1386,7 +1391,7 @@ func (t *SectorInfo) UnmarshalCBOR(r io.Reader) (err error) { return err } - if extra > cbg.ByteArrayMaxLen { + if extra > 2097152 { return fmt.Errorf("t.PreCommit1Out: byte array too large (%d)", extra) } if maj != cbg.MajByteString { @@ -1397,9 +1402,10 @@ func (t *SectorInfo) UnmarshalCBOR(r io.Reader) (err error) { t.PreCommit1Out = make([]uint8, extra) } - if _, err := io.ReadFull(cr, t.PreCommit1Out[:]); err != nil { + if _, err := io.ReadFull(cr, t.PreCommit1Out); err != nil { return err } + // t.FaultReportMsg (cid.Cid) (struct) case "FaultReportMsg": @@ -1610,7 +1616,7 @@ func (t *SectorInfo) UnmarshalCBOR(r io.Reader) (err error) { return err } - if extra > cbg.ByteArrayMaxLen { + if extra > 2097152 { return fmt.Errorf("t.ReplicaUpdateProof: byte array too large (%d)", extra) } if maj != cbg.MajByteString { @@ -1621,9 +1627,10 @@ func (t *SectorInfo) UnmarshalCBOR(r io.Reader) (err error) { t.ReplicaUpdateProof = make([]uint8, extra) } - if _, err := io.ReadFull(cr, t.ReplicaUpdateProof[:]); err != nil { + if _, err := io.ReadFull(cr, t.ReplicaUpdateProof); err != nil { return err } + // t.RemoteDataFinalized (bool) (bool) case "RemoteDataFinalized": @@ -1669,7 +1676,7 @@ func (t *SectorInfo) UnmarshalCBOR(r io.Reader) (err error) { case "RemoteCommit1Endpoint": { - sval, err := cbg.ReadString(cr) + sval, err := cbg.ReadStringWithMax(cr, 8192) if err != nil { return err } @@ -1680,7 +1687,7 @@ func (t *SectorInfo) UnmarshalCBOR(r io.Reader) (err error) { case "RemoteCommit2Endpoint": { - sval, err := cbg.ReadString(cr) + sval, err := cbg.ReadStringWithMax(cr, 8192) if err != nil { return err } @@ -1691,7 +1698,7 @@ func (t *SectorInfo) UnmarshalCBOR(r io.Reader) (err error) { case "RemoteSealingDoneEndpoint": { - sval, err := cbg.ReadString(cr) + sval, err := cbg.ReadStringWithMax(cr, 8192) if err != nil { return err } @@ -1720,7 +1727,7 @@ func (t *Log) MarshalCBOR(w io.Writer) error { } // t.Kind (string) (string) - if len("Kind") > cbg.MaxLength { + if len("Kind") > 8192 { return xerrors.Errorf("Value in field \"Kind\" was too long") } @@ -1731,7 +1738,7 @@ func (t *Log) MarshalCBOR(w io.Writer) error { return err } - if len(t.Kind) > cbg.MaxLength { + if len(t.Kind) > 8192 { return xerrors.Errorf("Value in field t.Kind was too long") } @@ -1743,7 +1750,7 @@ func (t *Log) MarshalCBOR(w io.Writer) error { } // t.Trace (string) (string) - if len("Trace") > cbg.MaxLength { + if len("Trace") > 8192 { return xerrors.Errorf("Value in field \"Trace\" was too long") } @@ -1754,7 +1761,7 @@ func (t *Log) MarshalCBOR(w io.Writer) error { return err } - if len(t.Trace) > cbg.MaxLength { + if len(t.Trace) > 8192 { return xerrors.Errorf("Value in field t.Trace was too long") } @@ -1766,7 +1773,7 @@ func (t *Log) MarshalCBOR(w io.Writer) error { } // t.Message (string) (string) - if len("Message") > cbg.MaxLength { + if len("Message") > 8192 { return xerrors.Errorf("Value in field \"Message\" was too long") } @@ -1777,7 +1784,7 @@ func (t *Log) MarshalCBOR(w io.Writer) error { return err } - if len(t.Message) > cbg.MaxLength { + if len(t.Message) > 8192 { return xerrors.Errorf("Value in field t.Message was too long") } @@ -1789,7 +1796,7 @@ func (t *Log) MarshalCBOR(w io.Writer) error { } // t.Timestamp (uint64) (uint64) - if len("Timestamp") > cbg.MaxLength { + if len("Timestamp") > 8192 { return xerrors.Errorf("Value in field \"Timestamp\" was too long") } @@ -1836,7 +1843,7 @@ func (t *Log) UnmarshalCBOR(r io.Reader) (err error) { for i := uint64(0); i < n; i++ { { - sval, err := cbg.ReadString(cr) + sval, err := cbg.ReadStringWithMax(cr, 8192) if err != nil { return err } @@ -1849,7 +1856,7 @@ func (t *Log) UnmarshalCBOR(r io.Reader) (err error) { case "Kind": { - sval, err := cbg.ReadString(cr) + sval, err := cbg.ReadStringWithMax(cr, 8192) if err != nil { return err } @@ -1860,7 +1867,7 @@ func (t *Log) UnmarshalCBOR(r io.Reader) (err error) { case "Trace": { - sval, err := cbg.ReadString(cr) + sval, err := cbg.ReadStringWithMax(cr, 8192) if err != nil { return err } @@ -1871,7 +1878,7 @@ func (t *Log) UnmarshalCBOR(r io.Reader) (err error) { case "Message": { - sval, err := cbg.ReadString(cr) + sval, err := cbg.ReadStringWithMax(cr, 8192) if err != nil { return err } diff --git a/storage/pipeline/checks.go b/storage/pipeline/checks.go index ecd160231..1f21b9c63 100644 --- a/storage/pipeline/checks.go +++ b/storage/pipeline/checks.go @@ -4,6 +4,7 @@ import ( "bytes" "context" + "github.com/ipfs/go-cid" "golang.org/x/xerrors" "github.com/filecoin-project/go-address" @@ -12,9 +13,9 @@ import ( "github.com/filecoin-project/go-state-types/crypto" prooftypes "github.com/filecoin-project/go-state-types/proof" - "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/chain/actors/policy" "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/storage/sealer/ffiwrapper" ) // TODO: For now we handle this by halting state execution, when we get jsonrpc reconnecting @@ -41,7 +42,7 @@ type ErrCommitWaitFailed struct{ error } type ErrBadRU struct{ error } type ErrBadPR struct{ error } -func checkPieces(ctx context.Context, maddr address.Address, sn abi.SectorNumber, pieces []api.SectorPiece, api SealingAPI, mustHaveDeals bool) error { +func checkPieces(ctx context.Context, maddr address.Address, sn abi.SectorNumber, pieces []SafeSectorPiece, api SealingAPI, mustHaveDeals bool) error { ts, err := api.ChainHead(ctx) if err != nil { return &ErrApi{xerrors.Errorf("getting chain head: %w", err)} @@ -51,43 +52,84 @@ func checkPieces(ctx context.Context, maddr address.Address, sn abi.SectorNumber var offset abi.PaddedPieceSize for i, p := range pieces { + p, i := p, i + // check that the piece is correctly aligned - if offset%p.Piece.Size != 0 { - return &ErrInvalidPiece{xerrors.Errorf("sector %d piece %d is not aligned: size=%xh offset=%xh off-by=%xh", sn, i, p.Piece.Size, offset, offset%p.Piece.Size)} + if offset%p.Piece().Size != 0 { + return &ErrInvalidPiece{xerrors.Errorf("sector %d piece %d is not aligned: size=%xh offset=%xh off-by=%xh", sn, i, p.Piece().Size, offset, offset%p.Piece().Size)} } - offset += p.Piece.Size + offset += p.Piece().Size - // if no deal is associated with the piece, ensure that we added it as - // filler (i.e. ensure that it has a zero PieceCID) - if p.DealInfo == nil { - exp := zerocomm.ZeroPieceCommitment(p.Piece.Size.Unpadded()) - if !p.Piece.PieceCID.Equals(exp) { - return &ErrInvalidPiece{xerrors.Errorf("sector %d piece %d had non-zero PieceCID %+v", sn, i, p.Piece.PieceCID)} - } - continue - } + err := p.handleDealInfo(handleDealInfoParams{ + FillerHandler: func(pi UniversalPieceInfo) error { + // if no deal is associated with the piece, ensure that we added it as + // filler (i.e. ensure that it has a zero PieceCID) - dealCount++ + exp := zerocomm.ZeroPieceCommitment(p.Piece().Size.Unpadded()) + if !p.Piece().PieceCID.Equals(exp) { + return &ErrInvalidPiece{xerrors.Errorf("sector %d piece %d had non-zero PieceCID %+v", sn, i, p.Piece().PieceCID)} + } - deal, err := api.StateMarketStorageDeal(ctx, p.DealInfo.DealID, ts.Key()) + return nil + }, + BuiltinMarketHandler: func(pi UniversalPieceInfo) error { + dealCount++ + + deal, err := api.StateMarketStorageDeal(ctx, p.Impl().DealID, ts.Key()) + if err != nil { + return &ErrInvalidDeals{xerrors.Errorf("getting deal %d for piece %d: %w", p.Impl().DealID, i, err)} + } + + if deal.Proposal.Provider != maddr { + return &ErrInvalidDeals{xerrors.Errorf("piece %d (of %d) of sector %d refers deal %d with wrong provider: %s != %s", i, len(pieces), sn, p.Impl().DealID, deal.Proposal.Provider, maddr)} + } + + if deal.Proposal.PieceCID != p.Piece().PieceCID { + return &ErrInvalidDeals{xerrors.Errorf("piece %d (of %d) of sector %d refers deal %d with wrong PieceCID: %s != %s", i, len(pieces), sn, p.Impl().DealID, p.Impl().DealProposal.PieceCID, deal.Proposal.PieceCID)} + } + + if p.Piece().Size != deal.Proposal.PieceSize { + return &ErrInvalidDeals{xerrors.Errorf("piece %d (of %d) of sector %d refers deal %d with different size: %d != %d", i, len(pieces), sn, p.Impl().DealID, p.Piece().Size, deal.Proposal.PieceSize)} + } + + if ts.Height() >= deal.Proposal.StartEpoch { + return &ErrExpiredDeals{xerrors.Errorf("piece %d (of %d) of sector %d refers expired deal %d - should start at %d, head %d", i, len(pieces), sn, p.Impl().DealID, deal.Proposal.StartEpoch, ts.Height())} + } + + return nil + }, + DDOHandler: func(pi UniversalPieceInfo) error { + dealCount++ + + // try to get allocation to see if that still works + all, err := pi.GetAllocation(ctx, api, ts.Key()) + if err != nil { + return xerrors.Errorf("getting deal %d allocation: %w", p.Impl().DealID, err) + } + if all != nil { + mid, err := address.IDFromAddress(maddr) + if err != nil { + return xerrors.Errorf("getting miner id: %w", err) + } + + if all.Provider != abi.ActorID(mid) { + return xerrors.Errorf("allocation provider doesn't match miner") + } + + if ts.Height() >= all.Expiration { + return &ErrExpiredDeals{xerrors.Errorf("piece allocation %d (of %d) of sector %d refers expired deal %d - should start at %d, head %d", i, len(pieces), sn, p.Impl().DealID, all.Expiration, ts.Height())} + } + + if all.Size < p.Piece().Size { + return &ErrInvalidDeals{xerrors.Errorf("piece allocation %d (of %d) of sector %d refers deal %d with different size: %d != %d", i, len(pieces), sn, p.Impl().DealID, p.Piece().Size, all.Size)} + } + } + + return nil + }, + }) if err != nil { - return &ErrInvalidDeals{xerrors.Errorf("getting deal %d for piece %d: %w", p.DealInfo.DealID, i, err)} - } - - if deal.Proposal.Provider != maddr { - return &ErrInvalidDeals{xerrors.Errorf("piece %d (of %d) of sector %d refers deal %d with wrong provider: %s != %s", i, len(pieces), sn, p.DealInfo.DealID, deal.Proposal.Provider, maddr)} - } - - if deal.Proposal.PieceCID != p.Piece.PieceCID { - return &ErrInvalidDeals{xerrors.Errorf("piece %d (of %d) of sector %d refers deal %d with wrong PieceCID: %s != %s", i, len(pieces), sn, p.DealInfo.DealID, p.Piece.PieceCID, deal.Proposal.PieceCID)} - } - - if p.Piece.Size != deal.Proposal.PieceSize { - return &ErrInvalidDeals{xerrors.Errorf("piece %d (of %d) of sector %d refers deal %d with different size: %d != %d", i, len(pieces), sn, p.DealInfo.DealID, p.Piece.Size, deal.Proposal.PieceSize)} - } - - if ts.Height() >= deal.Proposal.StartEpoch { - return &ErrExpiredDeals{xerrors.Errorf("piece %d (of %d) of sector %d refers expired deal %d - should start at %d, head %d", i, len(pieces), sn, p.DealInfo.DealID, deal.Proposal.StartEpoch, ts.Height())} + return err } } @@ -106,8 +148,8 @@ func checkPrecommit(ctx context.Context, maddr address.Address, si SectorInfo, t return err } - if si.hasDeals() { - commD, err := api.StateComputeDataCID(ctx, maddr, si.SectorType, si.dealIDs(), tsk) + if si.hasData() { + commD, err := computeUnsealedCIDFromPieces(si) if err != nil { return &ErrApi{xerrors.Errorf("calling StateComputeDataCommitment: %w", err)} } @@ -223,8 +265,7 @@ func (m *Sealing) checkCommit(ctx context.Context, si SectorInfo, proof []byte, } // check that sector info is good after running a replica update -func checkReplicaUpdate(ctx context.Context, maddr address.Address, si SectorInfo, tsk types.TipSetKey, api SealingAPI) error { - +func checkReplicaUpdate(ctx context.Context, maddr address.Address, si SectorInfo, api SealingAPI) error { if err := checkPieces(ctx, maddr, si.SectorNumber, si.Pieces, api, true); err != nil { return err } @@ -232,9 +273,9 @@ func checkReplicaUpdate(ctx context.Context, maddr address.Address, si SectorInf return xerrors.Errorf("replica update on sector not marked for update") } - commD, err := api.StateComputeDataCID(ctx, maddr, si.SectorType, si.dealIDs(), tsk) + commD, err := computeUnsealedCIDFromPieces(si) if err != nil { - return &ErrApi{xerrors.Errorf("calling StateComputeDataCommitment: %w", err)} + return xerrors.Errorf("computing unsealed CID from pieces: %w", err) } if si.UpdateUnsealed == nil { @@ -253,5 +294,9 @@ func checkReplicaUpdate(ctx context.Context, maddr address.Address, si SectorInf } return nil - +} + +func computeUnsealedCIDFromPieces(si SectorInfo) (cid.Cid, error) { + pcs := si.pieceInfos() + return ffiwrapper.GenerateUnsealedCID(si.SectorType, pcs) } diff --git a/storage/pipeline/commit_batch.go b/storage/pipeline/commit_batch.go index 754f31763..096f27e4c 100644 --- a/storage/pipeline/commit_batch.go +++ b/storage/pipeline/commit_batch.go @@ -23,6 +23,7 @@ import ( "github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/chain/actors/builtin/miner" "github.com/filecoin-project/lotus/chain/actors/policy" + "github.com/filecoin-project/lotus/chain/messagepool" "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/node/config" "github.com/filecoin-project/lotus/node/modules/dtypes" @@ -57,6 +58,9 @@ type AggregateInput struct { Spt abi.RegisteredSealProof Info proof.AggregateSealVerifyInfo Proof []byte + + ActivationManifest miner.SectorActivationManifest + DealIDPrecommit bool } type CommitBatcher struct { @@ -209,13 +213,18 @@ func (b *CommitBatcher) maybeStartBatch(notif bool) ([]sealiface.CommitBatchRes, return nil, nil } - var res []sealiface.CommitBatchRes + var res, resV1 []sealiface.CommitBatchRes ts, err := b.api.ChainHead(b.mctx) if err != nil { return nil, err } + nv, err := b.api.StateNetworkVersion(b.mctx, ts.Key()) + if err != nil { + return nil, xerrors.Errorf("getting network version: %s", err) + } + blackedOut := func() bool { const nv16BlackoutWindow = abi.ChainEpoch(20) // a magik number if ts.Height() <= build.UpgradeSkyrHeight && build.UpgradeSkyrHeight-ts.Height() < nv16BlackoutWindow { @@ -232,25 +241,67 @@ func (b *CommitBatcher) maybeStartBatch(notif bool) ([]sealiface.CommitBatchRes, } } - if individual { - res, err = b.processIndividually(cfg) - } else { + if nv >= MinDDONetworkVersion { + // After nv21, we have a new ProveCommitSectors2 method, which supports + // batching without aggregation, but it doesn't support onboarding + // sectors which were precommitted with DealIDs in the precommit message. + // We prefer it for all other sectors, so first we use the new processBatchV2 + var sectors []abi.SectorNumber for sn := range b.todo { sectors = append(sectors, sn) } - res, err = b.processBatch(cfg, sectors) + res, err = b.processBatchV2(cfg, sectors, nv, !individual) + if err != nil { + err = xerrors.Errorf("processBatchV2: %w", err) + } + + // Mark sectors as done + for _, r := range res { + if err != nil { + r.Error = err.Error() + } + + for _, sn := range r.Sectors { + for _, ch := range b.waiting[sn] { + ch <- r // buffered + } + + delete(b.waiting, sn) + delete(b.todo, sn) + delete(b.cutoffs, sn) + } + } } if err != nil { - log.Warnf("CommitBatcher maybeStartBatch individual:%v processBatch %v", individual, err) + log.Warnf("CommitBatcher maybeStartBatch processBatch-ddo %v", err) } if err != nil && len(res) == 0 { return nil, err } - for _, r := range res { + if individual { + resV1, err = b.processIndividually(cfg) + } else { + var sectors []abi.SectorNumber + for sn := range b.todo { + sectors = append(sectors, sn) + } + resV1, err = b.processBatchV1(cfg, sectors, nv) + } + + if err != nil { + log.Warnf("CommitBatcher maybeStartBatch individual:%v processBatch %v", individual, err) + } + + if err != nil && len(resV1) == 0 { + return nil, err + } + + // Mark the rest as processed + for _, r := range resV1 { if err != nil { r.Error = err.Error() } @@ -266,10 +317,170 @@ func (b *CommitBatcher) maybeStartBatch(notif bool) ([]sealiface.CommitBatchRes, } } + res = append(res, resV1...) + return res, nil } -func (b *CommitBatcher) processBatch(cfg sealiface.Config, sectors []abi.SectorNumber) ([]sealiface.CommitBatchRes, error) { +// processBatchV2 processes a batch of sectors after nv22. It will always send +// ProveCommitSectors3Params which may contain either individual proofs or an +// aggregate proof depending on SP condition and network conditions. +func (b *CommitBatcher) processBatchV2(cfg sealiface.Config, sectors []abi.SectorNumber, nv network.Version, aggregate bool) ([]sealiface.CommitBatchRes, error) { + ts, err := b.api.ChainHead(b.mctx) + if err != nil { + return nil, err + } + + total := len(sectors) + + res := sealiface.CommitBatchRes{ + FailedSectors: map[abi.SectorNumber]string{}, + } + + params := miner.ProveCommitSectors3Params{ + RequireActivationSuccess: cfg.RequireActivationSuccess, + RequireNotificationSuccess: cfg.RequireNotificationSuccess, + } + + infos := make([]proof.AggregateSealVerifyInfo, 0, total) + collateral := big.Zero() + + for _, sector := range sectors { + if b.todo[sector].DealIDPrecommit { + // can't process sectors precommitted with deal IDs with ProveCommitSectors2 + continue + } + + res.Sectors = append(res.Sectors, sector) + + sc, err := b.getSectorCollateral(sector, ts.Key()) + if err != nil { + res.FailedSectors[sector] = err.Error() + continue + } + + collateral = big.Add(collateral, sc) + + params.SectorActivations = append(params.SectorActivations, b.todo[sector].ActivationManifest) + params.SectorProofs = append(params.SectorProofs, b.todo[sector].Proof) + + infos = append(infos, b.todo[sector].Info) + } + + if len(infos) == 0 { + return nil, nil + } + + sort.Slice(infos, func(i, j int) bool { + return infos[i].Number < infos[j].Number + }) + + proofs := make([][]byte, 0, total) + for _, info := range infos { + proofs = append(proofs, b.todo[info.Number].Proof) + } + + needFunds := collateral + + if aggregate { + params.SectorProofs = nil // can't be set when aggregating + arp, err := b.aggregateProofType(nv) + if err != nil { + res.Error = err.Error() + return []sealiface.CommitBatchRes{res}, xerrors.Errorf("getting aggregate proof type: %w", err) + } + params.AggregateProofType = &arp + + mid, err := address.IDFromAddress(b.maddr) + if err != nil { + res.Error = err.Error() + return []sealiface.CommitBatchRes{res}, xerrors.Errorf("getting miner id: %w", err) + } + + params.AggregateProof, err = b.prover.AggregateSealProofs(proof.AggregateSealVerifyProofAndInfos{ + Miner: abi.ActorID(mid), + SealProof: b.todo[infos[0].Number].Spt, + AggregateProof: arp, + Infos: infos, + }, proofs) + if err != nil { + res.Error = err.Error() + return []sealiface.CommitBatchRes{res}, xerrors.Errorf("aggregating proofs: %w", err) + } + + aggFeeRaw, err := policy.AggregateProveCommitNetworkFee(nv, len(infos), ts.MinTicketBlock().ParentBaseFee) + if err != nil { + res.Error = err.Error() + log.Errorf("getting aggregate commit network fee: %s", err) + return []sealiface.CommitBatchRes{res}, xerrors.Errorf("getting aggregate commit network fee: %s", err) + } + + aggFee := big.Div(big.Mul(aggFeeRaw, aggFeeNum), aggFeeDen) + + needFunds = big.Add(collateral, aggFee) + } + + needFunds, err = collateralSendAmount(b.mctx, b.api, b.maddr, cfg, needFunds) + if err != nil { + res.Error = err.Error() + return []sealiface.CommitBatchRes{res}, err + } + + maxFee := b.feeCfg.MaxCommitBatchGasFee.FeeForSectors(len(infos)) + goodFunds := big.Add(maxFee, needFunds) + + mi, err := b.api.StateMinerInfo(b.mctx, b.maddr, types.EmptyTSK) + if err != nil { + res.Error = err.Error() + return []sealiface.CommitBatchRes{res}, xerrors.Errorf("couldn't get miner info: %w", err) + } + + from, _, err := b.addrSel.AddressFor(b.mctx, b.api, mi, api.CommitAddr, goodFunds, needFunds) + if err != nil { + res.Error = err.Error() + return []sealiface.CommitBatchRes{res}, xerrors.Errorf("no good address found: %w", err) + } + + enc := new(bytes.Buffer) + if err := params.MarshalCBOR(enc); err != nil { + res.Error = err.Error() + return []sealiface.CommitBatchRes{res}, xerrors.Errorf("couldn't serialize ProveCommitSectors2Params: %w", err) + } + + _, err = simulateMsgGas(b.mctx, b.api, from, b.maddr, builtin.MethodsMiner.ProveCommitSectors3, needFunds, maxFee, enc.Bytes()) + + if err != nil && (!api.ErrorIsIn(err, []error{&api.ErrOutOfGas{}}) || len(sectors) < miner.MinAggregatedSectors*2) { + log.Errorf("simulating CommitBatch message failed: %s", err) + res.Error = err.Error() + return []sealiface.CommitBatchRes{res}, xerrors.Errorf("simulating CommitBatch message failed: %w", err) + } + + msgTooLarge := len(enc.Bytes()) > (messagepool.MaxMessageSize - 128) + + // If we're out of gas, split the batch in half and evaluate again + if api.ErrorIsIn(err, []error{&api.ErrOutOfGas{}}) || msgTooLarge { + log.Warnf("CommitAggregate message ran out of gas or is too large, splitting batch in half and trying again (sectors: %d, params: %d)", len(sectors), len(enc.Bytes())) + mid := len(sectors) / 2 + ret0, _ := b.processBatchV2(cfg, sectors[:mid], nv, aggregate) + ret1, _ := b.processBatchV2(cfg, sectors[mid:], nv, aggregate) + + return append(ret0, ret1...), nil + } + + mcid, err := sendMsg(b.mctx, b.api, from, b.maddr, builtin.MethodsMiner.ProveCommitSectors3, needFunds, maxFee, enc.Bytes()) + if err != nil { + return []sealiface.CommitBatchRes{res}, xerrors.Errorf("sending message failed (params size: %d, sectors: %d, agg: %t): %w", len(enc.Bytes()), len(sectors), aggregate, err) + } + + res.Msg = &mcid + + log.Infow("Sent ProveCommitSectors2 message", "cid", mcid, "from", from, "todo", total, "sectors", len(infos)) + + return []sealiface.CommitBatchRes{res}, nil +} + +// processBatchV1 processes a batch of sectors before nv22. It always sends out an aggregate message. +func (b *CommitBatcher) processBatchV1(cfg sealiface.Config, sectors []abi.SectorNumber, nv network.Version) ([]sealiface.CommitBatchRes, error) { ts, err := b.api.ChainHead(b.mctx) if err != nil { return nil, err @@ -322,13 +533,6 @@ func (b *CommitBatcher) processBatch(cfg sealiface.Config, sectors []abi.SectorN return []sealiface.CommitBatchRes{res}, xerrors.Errorf("getting miner id: %w", err) } - nv, err := b.api.StateNetworkVersion(b.mctx, ts.Key()) - if err != nil { - res.Error = err.Error() - log.Errorf("getting network version: %s", err) - return []sealiface.CommitBatchRes{res}, xerrors.Errorf("getting network version: %s", err) - } - arp, err := b.aggregateProofType(nv) if err != nil { res.Error = err.Error() @@ -396,8 +600,8 @@ func (b *CommitBatcher) processBatch(cfg sealiface.Config, sectors []abi.SectorN if api.ErrorIsIn(err, []error{&api.ErrOutOfGas{}}) { log.Warnf("CommitAggregate message ran out of gas, splitting batch in half and trying again (sectors: %d)", len(sectors)) mid := len(sectors) / 2 - ret0, _ := b.processBatch(cfg, sectors[:mid]) - ret1, _ := b.processBatch(cfg, sectors[mid:]) + ret0, _ := b.processBatchV1(cfg, sectors[:mid], nv) + ret1, _ := b.processBatchV1(cfg, sectors[mid:], nv) return append(ret0, ret1...), nil } @@ -484,6 +688,10 @@ func (b *CommitBatcher) processIndividually(cfg sealiface.Config) ([]sealiface.C } func (b *CommitBatcher) processSingle(cfg sealiface.Config, mi api.MinerInfo, avail *abi.TokenAmount, sn abi.SectorNumber, info AggregateInput, tsk types.TipSetKey) (cid.Cid, error) { + return b.processSingleV1(cfg, mi, avail, sn, info, tsk) +} + +func (b *CommitBatcher) processSingleV1(cfg sealiface.Config, mi api.MinerInfo, avail *abi.TokenAmount, sn abi.SectorNumber, info AggregateInput, tsk types.TipSetKey) (cid.Cid, error) { enc := new(bytes.Buffer) params := &miner.ProveCommitSectorParams{ SectorNumber: sn, @@ -646,11 +854,15 @@ func (b *CommitBatcher) getCommitCutoff(si SectorInfo) (time.Time, error) { cutoffEpoch := pci.PreCommitEpoch + mpcd for _, p := range si.Pieces { - if p.DealInfo == nil { + if !p.HasDealInfo() { continue } - startEpoch := p.DealInfo.DealSchedule.StartEpoch + startEpoch, err := p.StartEpoch() + if err != nil { + log.Errorf("getting deal start epoch: %s", err) + return time.Now(), err + } if startEpoch < cutoffEpoch { cutoffEpoch = startEpoch } diff --git a/storage/pipeline/commit_batch_test.go b/storage/pipeline/commit_batch_test.go deleted file mode 100644 index 5ae2f171a..000000000 --- a/storage/pipeline/commit_batch_test.go +++ /dev/null @@ -1,498 +0,0 @@ -// stm: #unit -package sealing_test - -import ( - "bytes" - "context" - "sort" - "sync" - "testing" - "time" - - "github.com/golang/mock/gomock" - "github.com/ipfs/go-cid" - "github.com/stretchr/testify/require" - - "github.com/filecoin-project/go-address" - "github.com/filecoin-project/go-state-types/abi" - "github.com/filecoin-project/go-state-types/big" - minertypes "github.com/filecoin-project/go-state-types/builtin/v9/miner" - "github.com/filecoin-project/go-state-types/crypto" - "github.com/filecoin-project/go-state-types/network" - prooftypes "github.com/filecoin-project/go-state-types/proof" - miner5 "github.com/filecoin-project/specs-actors/v5/actors/builtin/miner" - - "github.com/filecoin-project/lotus/api" - "github.com/filecoin-project/lotus/chain/types" - "github.com/filecoin-project/lotus/storage/ctladdr" - pipeline "github.com/filecoin-project/lotus/storage/pipeline" - "github.com/filecoin-project/lotus/storage/pipeline/mocks" - "github.com/filecoin-project/lotus/storage/pipeline/sealiface" - "github.com/filecoin-project/lotus/storage/sealer/storiface" -) - -func TestCommitBatcher(t *testing.T) { - //stm: @CHAIN_STATE_MINER_PRE_COM_INFO_001, @CHAIN_STATE_MINER_INFO_001, @CHAIN_STATE_NETWORK_VERSION_001 - t0123, err := address.NewFromString("t0123") - require.NoError(t, err) - - ctx := context.Background() - - as := asel(func(ctx context.Context, mi api.MinerInfo, use api.AddrUse, goodFunds, minFunds abi.TokenAmount) (address.Address, abi.TokenAmount, error) { - return t0123, big.Zero(), nil - }) - - maxBatch := miner5.MaxAggregatedSectors - minBatch := miner5.MinAggregatedSectors - - cfg := func() (sealiface.Config, error) { - return sealiface.Config{ - MaxWaitDealsSectors: 2, - MaxSealingSectors: 0, - MaxSealingSectorsForDeals: 0, - WaitDealsDelay: time.Hour * 6, - AlwaysKeepUnsealedCopy: true, - - MaxPreCommitBatch: miner5.PreCommitSectorBatchMaxSize, - PreCommitBatchWait: 24 * time.Hour, - PreCommitBatchSlack: 3 * time.Hour, - - AggregateCommits: true, - MinCommitBatch: minBatch, - MaxCommitBatch: maxBatch, - CommitBatchWait: 24 * time.Hour, - CommitBatchSlack: 1 * time.Hour, - - AggregateAboveBaseFee: types.BigMul(types.PicoFil, types.NewInt(150)), // 0.15 nFIL - BatchPreCommitAboveBaseFee: types.BigMul(types.PicoFil, types.NewInt(150)), // 0.15 nFIL - - TerminateBatchMin: 1, - TerminateBatchMax: 100, - TerminateBatchWait: 5 * time.Minute, - }, nil - } - - type promise func(t *testing.T) - type action func(t *testing.T, s *mocks.MockCommitBatcherApi, pcb *pipeline.CommitBatcher) promise - - actions := func(as ...action) action { - return func(t *testing.T, s *mocks.MockCommitBatcherApi, pcb *pipeline.CommitBatcher) promise { - var ps []promise - for _, a := range as { - p := a(t, s, pcb) - if p != nil { - ps = append(ps, p) - } - } - - if len(ps) > 0 { - return func(t *testing.T) { - for _, p := range ps { - p(t) - } - } - } - return nil - } - } - - addSector := func(sn abi.SectorNumber, aboveBalancer bool) action { - return func(t *testing.T, s *mocks.MockCommitBatcherApi, pcb *pipeline.CommitBatcher) promise { - var pcres sealiface.CommitBatchRes - var pcerr error - done := sync.Mutex{} - done.Lock() - - si := pipeline.SectorInfo{ - SectorNumber: sn, - } - - basefee := types.PicoFil - if aboveBalancer { - basefee = types.NanoFil - } - - s.EXPECT().ChainHead(gomock.Any()).Return(makeBFTs(t, basefee, 1), nil) - s.EXPECT().StateNetworkVersion(gomock.Any(), gomock.Any()).Return(network.Version13, nil) - s.EXPECT().StateSectorPreCommitInfo(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(&minertypes.SectorPreCommitOnChainInfo{ - PreCommitDeposit: big.Zero(), - }, nil) - - go func() { - defer done.Unlock() - pcres, pcerr = pcb.AddCommit(ctx, si, pipeline.AggregateInput{ - Info: prooftypes.AggregateSealVerifyInfo{ - Number: sn, - }, - }) - }() - - return func(t *testing.T) { - done.Lock() - require.NoError(t, pcerr) - require.Empty(t, pcres.Error) - require.Contains(t, pcres.Sectors, si.SectorNumber) - } - } - } - - addSectors := func(sectors []abi.SectorNumber, aboveBalancer bool) action { - as := make([]action, len(sectors)) - for i, sector := range sectors { - as[i] = addSector(sector, aboveBalancer) - } - return actions(as...) - } - - waitPending := func(n int) action { - return func(t *testing.T, s *mocks.MockCommitBatcherApi, pcb *pipeline.CommitBatcher) promise { - require.Eventually(t, func() bool { - p, err := pcb.Pending(ctx) - require.NoError(t, err) - return len(p) == n - }, time.Second*5, 10*time.Millisecond) - - return nil - } - } - - //stm: @CHAIN_STATE_MINER_INFO_001, @CHAIN_STATE_NETWORK_VERSION_001, @CHAIN_STATE_MINER_GET_COLLATERAL_001 - expectSend := func(expect []abi.SectorNumber, aboveBalancer, failOnePCI bool) action { - return func(t *testing.T, s *mocks.MockCommitBatcherApi, pcb *pipeline.CommitBatcher) promise { - s.EXPECT().StateMinerInfo(gomock.Any(), gomock.Any(), gomock.Any()).Return(api.MinerInfo{Owner: t0123, Worker: t0123}, nil) - - ti := len(expect) - batch := false - if ti >= minBatch { - batch = true - ti = 1 - } - - basefee := types.PicoFil - if aboveBalancer { - basefee = types.NanoFil - } - - s.EXPECT().ChainHead(gomock.Any()).Return(makeBFTs(t, basefee, 1), nil) - /*if batch { - s.EXPECT().ChainBaseFee(gomock.Any(), gomock.Any()).Return(basefee, nil) - }*/ - - if !aboveBalancer { - batch = false - ti = len(expect) - } - - s.EXPECT().ChainHead(gomock.Any()).Return(makeBFTs(t, basefee, 1), nil) - - pciC := len(expect) - if failOnePCI { - s.EXPECT().StateSectorPreCommitInfo(gomock.Any(), gomock.Any(), abi.SectorNumber(1), gomock.Any()).Return(nil, nil).Times(1) // not found - pciC = len(expect) - 1 - if !batch { - ti-- - } - } - s.EXPECT().StateSectorPreCommitInfo(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(&minertypes.SectorPreCommitOnChainInfo{ - PreCommitDeposit: big.Zero(), - }, nil).Times(pciC) - s.EXPECT().StateMinerInitialPledgeCollateral(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(big.Zero(), nil).Times(pciC) - - if batch { - s.EXPECT().StateNetworkVersion(gomock.Any(), gomock.Any()).Return(network.Version13, nil) - s.EXPECT().GasEstimateMessageGas(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(&types.Message{GasLimit: 100000}, nil) - } - - s.EXPECT().MpoolPushMessage(gomock.Any(), funMatcher(func(i interface{}) bool { - b := i.(*types.Message) - if batch { - var params miner5.ProveCommitAggregateParams - require.NoError(t, params.UnmarshalCBOR(bytes.NewReader(b.Params))) - for _, number := range expect { - set, err := params.SectorNumbers.IsSet(uint64(number)) - require.NoError(t, err) - require.True(t, set) - } - } else { - var params miner5.ProveCommitSectorParams - require.NoError(t, params.UnmarshalCBOR(bytes.NewReader(b.Params))) - } - return true - }), gomock.Any()).Return(dummySmsg, nil).Times(ti) - return nil - } - } - - expectProcessBatch := func(expect []abi.SectorNumber, aboveBalancer, failOnePCI bool, gasOverLimit bool) action { - return func(t *testing.T, s *mocks.MockCommitBatcherApi, pcb *pipeline.CommitBatcher) promise { - s.EXPECT().StateMinerInfo(gomock.Any(), gomock.Any(), gomock.Any()).Return(api.MinerInfo{Owner: t0123, Worker: t0123}, nil) - - ti := len(expect) - batch := false - if ti >= minBatch { - batch = true - ti = 1 - } - - if !aboveBalancer { - batch = false - ti = len(expect) - } - - pciC := len(expect) - if failOnePCI { - s.EXPECT().StateSectorPreCommitInfo(gomock.Any(), gomock.Any(), abi.SectorNumber(1), gomock.Any()).Return(nil, nil).Times(1) // not found - pciC = len(expect) - 1 - if !batch { - ti-- - } - } - s.EXPECT().StateSectorPreCommitInfo(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(&minertypes.SectorPreCommitOnChainInfo{ - PreCommitDeposit: big.Zero(), - }, nil).Times(pciC) - s.EXPECT().StateMinerInitialPledgeCollateral(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(big.Zero(), nil).Times(pciC) - - if batch { - s.EXPECT().StateNetworkVersion(gomock.Any(), gomock.Any()).Return(network.Version18, nil) - if gasOverLimit { - s.EXPECT().GasEstimateMessageGas(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, &api.ErrOutOfGas{}) - } else { - s.EXPECT().GasEstimateMessageGas(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(&types.Message{GasLimit: 100000}, nil) - } - - } - return nil - } - } - - flush := func(expect []abi.SectorNumber, aboveBalancer, failOnePCI bool) action { - return func(t *testing.T, s *mocks.MockCommitBatcherApi, pcb *pipeline.CommitBatcher) promise { - _ = expectSend(expect, aboveBalancer, failOnePCI)(t, s, pcb) - - batch := len(expect) >= minBatch && aboveBalancer - - r, err := pcb.Flush(ctx) - require.NoError(t, err) - if batch { - require.Len(t, r, 1) - require.Empty(t, r[0].Error) - sort.Slice(r[0].Sectors, func(i, j int) bool { - return r[0].Sectors[i] < r[0].Sectors[j] - }) - require.Equal(t, expect, r[0].Sectors) - if !failOnePCI { - require.Len(t, r[0].FailedSectors, 0) - } else { - require.Len(t, r[0].FailedSectors, 1) - _, found := r[0].FailedSectors[1] - require.True(t, found) - } - } else { - require.Len(t, r, len(expect)) - for _, res := range r { - require.Len(t, res.Sectors, 1) - require.Empty(t, res.Error) - } - sort.Slice(r, func(i, j int) bool { - return r[i].Sectors[0] < r[j].Sectors[0] - }) - for i, res := range r { - require.Equal(t, abi.SectorNumber(i), res.Sectors[0]) - if failOnePCI && res.Sectors[0] == 1 { - require.Len(t, res.FailedSectors, 1) - _, found := res.FailedSectors[1] - require.True(t, found) - } else { - require.Empty(t, res.FailedSectors) - } - } - } - - return nil - } - } - - getSectors := func(n int) []abi.SectorNumber { - out := make([]abi.SectorNumber, n) - for i := range out { - out[i] = abi.SectorNumber(i) - } - return out - } - - tcs := map[string]struct { - actions []action - }{ - "addSingle-aboveBalancer": { - actions: []action{ - addSector(0, true), - waitPending(1), - flush([]abi.SectorNumber{0}, true, false), - }, - }, - "addTwo-aboveBalancer": { - actions: []action{ - addSectors(getSectors(2), true), - waitPending(2), - flush(getSectors(2), true, false), - }, - }, - "addAte-aboveBalancer": { - actions: []action{ - addSectors(getSectors(8), true), - waitPending(8), - flush(getSectors(8), true, false), - }, - }, - "addMax-aboveBalancer": { - actions: []action{ - expectSend(getSectors(maxBatch), true, false), - addSectors(getSectors(maxBatch), true), - }, - }, - "addMax-aboveBalancer-gasAboveLimit": { - actions: []action{ - expectProcessBatch(getSectors(maxBatch), true, false, true), - expectSend(getSectors(maxBatch)[:maxBatch/2], true, false), - expectSend(getSectors(maxBatch)[maxBatch/2:], true, false), - addSectors(getSectors(maxBatch), true), - }, - }, - "addSingle-belowBalancer": { - actions: []action{ - addSector(0, false), - waitPending(1), - flush([]abi.SectorNumber{0}, false, false), - }, - }, - "addTwo-belowBalancer": { - actions: []action{ - addSectors(getSectors(2), false), - waitPending(2), - flush(getSectors(2), false, false), - }, - }, - "addAte-belowBalancer": { - actions: []action{ - addSectors(getSectors(8), false), - waitPending(8), - flush(getSectors(8), false, false), - }, - }, - "addMax-belowBalancer": { - actions: []action{ - expectSend(getSectors(maxBatch), false, false), - addSectors(getSectors(maxBatch), false), - }, - }, - - "addAte-aboveBalancer-failOne": { - actions: []action{ - addSectors(getSectors(8), true), - waitPending(8), - flush(getSectors(8), true, true), - }, - }, - "addAte-belowBalancer-failOne": { - actions: []action{ - addSectors(getSectors(8), false), - waitPending(8), - flush(getSectors(8), false, true), - }, - }, - } - - for name, tc := range tcs { - tc := tc - - t.Run(name, func(t *testing.T) { - // create go mock controller here - mockCtrl := gomock.NewController(t) - // when test is done, assert expectations on all mock objects. - defer mockCtrl.Finish() - - // create them mocks - pcapi := mocks.NewMockCommitBatcherApi(mockCtrl) - - pcb := pipeline.NewCommitBatcher(ctx, t0123, pcapi, as, fc, cfg, &fakeProver{}) - - var promises []promise - - for _, a := range tc.actions { - p := a(t, pcapi, pcb) - if p != nil { - promises = append(promises, p) - } - } - - for _, p := range promises { - p(t) - } - - err := pcb.Stop(ctx) - require.NoError(t, err) - }) - } -} - -type fakeProver struct{} - -func (f fakeProver) AggregateSealProofs(aggregateInfo prooftypes.AggregateSealVerifyProofAndInfos, proofs [][]byte) ([]byte, error) { - return []byte("Trust me, I'm a proof"), nil -} - -var _ storiface.Prover = &fakeProver{} - -var dummyAddr = func() address.Address { - a, _ := address.NewFromString("t00") - return a -}() - -func makeBFTs(t *testing.T, basefee abi.TokenAmount, h abi.ChainEpoch) *types.TipSet { - dummyCid, _ := cid.Parse("bafkqaaa") - - var ts, err = types.NewTipSet([]*types.BlockHeader{ - { - Height: h, - Miner: dummyAddr, - - Parents: []cid.Cid{}, - - Ticket: &types.Ticket{VRFProof: []byte{byte(h % 2)}}, - - ParentStateRoot: dummyCid, - Messages: dummyCid, - ParentMessageReceipts: dummyCid, - - BlockSig: &crypto.Signature{Type: crypto.SigTypeBLS}, - BLSAggregate: &crypto.Signature{Type: crypto.SigTypeBLS}, - - ParentBaseFee: basefee, - }, - }) - if t != nil { - require.NoError(t, err) - } - - return ts -} - -func makeTs(t *testing.T, h abi.ChainEpoch) *types.TipSet { - return makeBFTs(t, big.NewInt(0), h) -} - -var dummySmsg = &types.SignedMessage{ - Message: types.Message{ - From: dummyAddr, - To: dummyAddr, - }, - Signature: crypto.Signature{Type: crypto.SigTypeBLS}, -} - -type asel func(ctx context.Context, mi api.MinerInfo, use api.AddrUse, goodFunds, minFunds abi.TokenAmount) (address.Address, abi.TokenAmount, error) - -func (s asel) AddressFor(ctx context.Context, _ ctladdr.NodeApi, mi api.MinerInfo, use api.AddrUse, goodFunds, minFunds abi.TokenAmount) (address.Address, abi.TokenAmount, error) { - return s(ctx, mi, use, goodFunds, minFunds) -} - -var _ pipeline.AddressSelector = asel(nil) diff --git a/storage/pipeline/currentdealinfo_test.go b/storage/pipeline/currentdealinfo_test.go index 21141a35d..1ea05dc35 100644 --- a/storage/pipeline/currentdealinfo_test.go +++ b/storage/pipeline/currentdealinfo_test.go @@ -80,21 +80,21 @@ func TestGetCurrentDealInfo(t *testing.T) { } successDeal := &api.MarketDeal{ Proposal: proposal, - State: market.DealState{ + State: api.MarketDealState{ SectorStartEpoch: 1, LastUpdatedEpoch: 2, }, } earlierDeal := &api.MarketDeal{ Proposal: otherProposal, - State: market.DealState{ + State: api.MarketDealState{ SectorStartEpoch: 1, LastUpdatedEpoch: 2, }, } anotherDeal := &api.MarketDeal{ Proposal: anotherProposal, - State: market.DealState{ + State: api.MarketDealState{ SectorStartEpoch: 1, LastUpdatedEpoch: 2, }, diff --git a/storage/pipeline/fsm_events.go b/storage/pipeline/fsm_events.go index a798a884b..94cd53e82 100644 --- a/storage/pipeline/fsm_events.go +++ b/storage/pipeline/fsm_events.go @@ -88,7 +88,7 @@ func (evt SectorAddPiece) apply(state *SectorInfo) { } type SectorPieceAdded struct { - NewPieces []api.SectorPiece + NewPieces []SafeSectorPiece } func (evt SectorPieceAdded) apply(state *SectorInfo) { @@ -114,9 +114,11 @@ type SectorPacked struct{ FillerPieces []abi.PieceInfo } func (evt SectorPacked) apply(state *SectorInfo) { for idx := range evt.FillerPieces { - state.Pieces = append(state.Pieces, api.SectorPiece{ - Piece: evt.FillerPieces[idx], - DealInfo: nil, // filler pieces don't have deals associated with them + state.Pieces = append(state.Pieces, SafeSectorPiece{ + real: api.SectorPiece{ + Piece: evt.FillerPieces[idx], + DealInfo: nil, // filler pieces don't have deals associated with them + }, }) } } @@ -419,7 +421,8 @@ type SectorUpdateDealIDs struct { func (evt SectorUpdateDealIDs) apply(state *SectorInfo) { for i, id := range evt.Updates { - state.Pieces[i].DealInfo.DealID = id + // NOTE: all update deals are builtin-market deals + state.Pieces[i].real.DealInfo.DealID = id } } diff --git a/storage/pipeline/input.go b/storage/pipeline/input.go index b595f533d..53c96fc55 100644 --- a/storage/pipeline/input.go +++ b/storage/pipeline/input.go @@ -5,7 +5,6 @@ import ( "sort" "time" - "github.com/ipfs/go-cid" "go.uber.org/zap" "golang.org/x/xerrors" @@ -13,14 +12,15 @@ import ( "github.com/filecoin-project/go-padreader" "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/big" - "github.com/filecoin-project/go-state-types/network" "github.com/filecoin-project/go-statemachine" "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/chain/actors/policy" "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/lib/result" "github.com/filecoin-project/lotus/storage/pipeline/lib/nullreader" + "github.com/filecoin-project/lotus/storage/pipeline/piece" "github.com/filecoin-project/lotus/storage/pipeline/sealiface" "github.com/filecoin-project/lotus/storage/sealer" "github.com/filecoin-project/lotus/storage/sealer/ffiwrapper" @@ -32,10 +32,15 @@ func (m *Sealing) handleWaitDeals(ctx statemachine.Context, sector SectorInfo) e var used abi.UnpaddedPieceSize var lastDealEnd abi.ChainEpoch for _, piece := range sector.Pieces { - used += piece.Piece.Size.Unpadded() + used += piece.Piece().Size.Unpadded() - if piece.DealInfo != nil && piece.DealInfo.DealProposal.EndEpoch > lastDealEnd { - lastDealEnd = piece.DealInfo.DealProposal.EndEpoch + endEpoch, err := piece.EndEpoch() + if err != nil { + return xerrors.Errorf("piece.EndEpoch: %w", err) + } + + if piece.HasDealInfo() && endEpoch > lastDealEnd { + lastDealEnd = endEpoch } } @@ -65,9 +70,9 @@ func (m *Sealing) handleWaitDeals(ctx statemachine.Context, sector SectorInfo) e if _, has := m.openSectors[sid]; !has { m.openSectors[sid] = &openSector{ used: used, - maybeAccept: func(cid cid.Cid) error { + maybeAccept: func(pk piece.PieceKey) error { // todo check deal start deadline (configurable) - m.assignedPieces[sid] = append(m.assignedPieces[sid], cid) + m.assignedPieces[sid] = append(m.assignedPieces[sid], pk) return ctx.Send(SectorAddPiece{}) }, @@ -94,7 +99,7 @@ func (m *Sealing) handleWaitDeals(ctx statemachine.Context, sector SectorInfo) e func (m *Sealing) maybeStartSealing(ctx statemachine.Context, sector SectorInfo, used abi.UnpaddedPieceSize) (bool, error) { log := log.WithOptions(zap.Fields( zap.Uint64("sector", uint64(sector.SectorNumber)), - zap.Int("deals", len(sector.dealIDs())), + zap.Int("dataPieces", len(sector.nonPaddingPieceInfos())), )) now := time.Now() @@ -117,7 +122,7 @@ func (m *Sealing) maybeStartSealing(ctx statemachine.Context, sector SectorInfo, return false, xerrors.Errorf("getting per-sector deal limit: %w", err) } - if len(sector.dealIDs()) >= maxDeals { + if len(sector.nonPaddingPieceInfos()) >= maxDeals { // can't accept more deals log.Infow("starting to seal deal sector", "trigger", "maxdeals") return true, ctx.Send(SectorStartPacking{}) @@ -146,13 +151,24 @@ func (m *Sealing) maybeStartSealing(ctx statemachine.Context, sector SectorInfo, var dealSafeSealEpoch abi.ChainEpoch for _, piece := range sector.Pieces { - if piece.DealInfo == nil { + if !piece.HasDealInfo() { continue } - dealSafeSealEpoch = piece.DealInfo.DealProposal.StartEpoch - cfg.StartEpochSealingBuffer + startEpoch, err := piece.StartEpoch() + if err != nil { + log.Errorw("failed to get start epoch for deal", "piece", piece.String(), "error", err) + continue // not ideal, but skipping the check should break things less + } + + dealSafeSealEpoch = startEpoch - cfg.StartEpochSealingBuffer + + alloc, err := piece.GetAllocation(ctx.Context(), m.Api, types.EmptyTSK) + if err != nil { + log.Errorw("failed to get allocation for deal", "piece", piece.String(), "error", err) + continue // not ideal, but skipping the check should break things less + } - alloc, _ := m.Api.StateGetAllocationForPendingDeal(ctx.Context(), piece.DealInfo.DealID, types.EmptyTSK) // alloc is nil if this is not a verified deal in nv17 or later if alloc == nil { continue @@ -210,8 +226,8 @@ func (m *Sealing) handleAddPiece(ctx statemachine.Context, sector SectorInfo) er var offset abi.UnpaddedPieceSize pieceSizes := make([]abi.UnpaddedPieceSize, len(sector.Pieces)) for i, p := range sector.Pieces { - pieceSizes[i] = p.Piece.Size.Unpadded() - offset += p.Piece.Size.Unpadded() + pieceSizes[i] = p.Piece().Size.Unpadded() + offset += p.Piece().Size.Unpadded() } maxDeals, err := getDealPerSectorLimit(ssize) @@ -227,7 +243,7 @@ func (m *Sealing) handleAddPiece(ctx statemachine.Context, sector SectorInfo) er return xerrors.Errorf("piece %s assigned to sector %d not found", piece, sector.SectorNumber) } - if len(sector.dealIDs())+(i+1) > maxDeals { + if len(sector.nonPaddingPieceInfos())+(i+1) > maxDeals { // todo: this is rather unlikely to happen, but in case it does, return the deal to waiting queue instead of failing it deal.accepted(sector.SectorNumber, offset, xerrors.Errorf("too many deals assigned to sector %d, dropping deal", sector.SectorNumber)) continue @@ -263,8 +279,10 @@ func (m *Sealing) handleAddPiece(ctx statemachine.Context, sector SectorInfo) er } pieceSizes = append(pieceSizes, p.Unpadded()) - res.NewPieces = append(res.NewPieces, api.SectorPiece{ - Piece: ppi, + res.NewPieces = append(res.NewPieces, SafeSectorPiece{ + api.SectorPiece{ + Piece: ppi, + }, }) } @@ -278,22 +296,26 @@ func (m *Sealing) handleAddPiece(ctx statemachine.Context, sector SectorInfo) er deal.accepted(sector.SectorNumber, offset, err) return ctx.Send(SectorAddPieceFailed{err}) } - if !ppi.PieceCID.Equals(deal.deal.DealProposal.PieceCID) { - err = xerrors.Errorf("got unexpected piece CID: expected:%s, got:%s", deal.deal.DealProposal.PieceCID, ppi.PieceCID) + if !ppi.PieceCID.Equals(deal.deal.PieceCID()) { + err = xerrors.Errorf("got unexpected piece CID: expected:%s, got:%s", deal.deal.PieceCID(), ppi.PieceCID) deal.accepted(sector.SectorNumber, offset, err) return ctx.Send(SectorAddPieceFailed{err}) } - log.Infow("deal added to a sector", "deal", deal.deal.DealID, "sector", sector.SectorNumber, "piece", ppi.PieceCID) + log.Infow("deal added to a sector", "pieceID", deal.deal.String(), "sector", sector.SectorNumber, "piece", ppi.PieceCID) deal.accepted(sector.SectorNumber, offset, nil) offset += deal.size pieceSizes = append(pieceSizes, deal.size) - res.NewPieces = append(res.NewPieces, api.SectorPiece{ - Piece: ppi, - DealInfo: &deal.deal, + dinfo := deal.deal.Impl() + + res.NewPieces = append(res.NewPieces, SafeSectorPiece{ + api.SectorPiece{ + Piece: ppi, + DealInfo: &dinfo, + }, }) } @@ -304,8 +326,13 @@ func (m *Sealing) handleAddPieceFailed(ctx statemachine.Context, sector SectorIn return ctx.Send(SectorRetryWaitDeals{}) } -func (m *Sealing) SectorAddPieceToAny(ctx context.Context, size abi.UnpaddedPieceSize, data storiface.Data, deal api.PieceDealInfo) (api.SectorOffset, error) { - log.Infof("Adding piece for deal %d (publish msg: %s)", deal.DealID, deal.PublishCid) +func (m *Sealing) SectorAddPieceToAny(ctx context.Context, size abi.UnpaddedPieceSize, data storiface.Data, pieceInfo piece.PieceDealInfo) (api.SectorOffset, error) { + return m.sectorAddPieceToAny(ctx, size, data, &pieceInfo) +} + +func (m *Sealing) sectorAddPieceToAny(ctx context.Context, size abi.UnpaddedPieceSize, data storiface.Data, pieceInfo UniversalPieceInfo) (api.SectorOffset, error) { + log.Infof("Adding piece %s", pieceInfo.String()) + if (padreader.PaddedSize(uint64(size))) != size { return api.SectorOffset{}, xerrors.Errorf("cannot allocate unpadded piece") } @@ -324,10 +351,6 @@ func (m *Sealing) SectorAddPieceToAny(ctx context.Context, size abi.UnpaddedPiec return api.SectorOffset{}, xerrors.Errorf("piece cannot fit into a sector") } - if _, err := deal.DealProposal.Cid(); err != nil { - return api.SectorOffset{}, xerrors.Errorf("getting proposal CID: %w", err) - } - cfg, err := m.getConfig() if err != nil { return api.SectorOffset{}, xerrors.Errorf("getting config: %w", err) @@ -337,19 +360,34 @@ func (m *Sealing) SectorAddPieceToAny(ctx context.Context, size abi.UnpaddedPiec if err != nil { return api.SectorOffset{}, xerrors.Errorf("couldnt get chain head: %w", err) } - if ts.Height()+cfg.StartEpochSealingBuffer > deal.DealProposal.StartEpoch { - return api.SectorOffset{}, xerrors.Errorf( - "cannot add piece for deal with piece CID %s: current epoch %d has passed deal proposal start epoch %d", - deal.DealProposal.PieceCID, ts.Height(), deal.DealProposal.StartEpoch) + + nv, err := m.Api.StateNetworkVersion(ctx, types.EmptyTSK) + if err != nil { + return api.SectorOffset{}, xerrors.Errorf("getting network version: %w", err) } - claimTerms, err := m.getClaimTerms(ctx, deal, ts.Key()) + if err := pieceInfo.Valid(nv); err != nil { + return api.SectorOffset{}, xerrors.Errorf("piece metadata invalid: %w", err) + } + + startEpoch, err := pieceInfo.StartEpoch() + if err != nil { + return api.SectorOffset{}, xerrors.Errorf("getting last start epoch: %w", err) + } + + if ts.Height()+cfg.StartEpochSealingBuffer > startEpoch { + return api.SectorOffset{}, xerrors.Errorf( + "cannot add piece for deal with piece CID %s: current epoch %d has passed deal proposal start epoch %d", + pieceInfo.PieceCID(), ts.Height(), startEpoch) + } + + claimTerms, err := m.getClaimTerms(ctx, pieceInfo, ts.Key()) if err != nil { return api.SectorOffset{}, err } m.inputLk.Lock() - if pp, exist := m.pendingPieces[proposalCID(deal)]; exist { + if pp, exist := m.pendingPieces[pieceInfo.Key()]; exist { m.inputLk.Unlock() // we already have a pre-existing add piece call for this deal, let's wait for it to finish and see if it's successful @@ -366,7 +404,7 @@ func (m *Sealing) SectorAddPieceToAny(ctx context.Context, size abi.UnpaddedPiec } // addPendingPiece takes over m.inputLk - pp := m.addPendingPiece(ctx, size, data, deal, claimTerms, sp) + pp := m.addPendingPiece(ctx, size, data, pieceInfo, claimTerms, sp) res, err := waitAddPieceResp(ctx, pp) if err != nil { @@ -375,32 +413,41 @@ func (m *Sealing) SectorAddPieceToAny(ctx context.Context, size abi.UnpaddedPiec return api.SectorOffset{Sector: res.sn, Offset: res.offset.Padded()}, res.err } -func (m *Sealing) getClaimTerms(ctx context.Context, deal api.PieceDealInfo, tsk types.TipSetKey) (pieceClaimBounds, error) { +func (m *Sealing) getClaimTerms(ctx context.Context, deal UniversalPieceInfo, tsk types.TipSetKey) (pieceClaimBounds, error) { + + all, err := deal.GetAllocation(ctx, m.Api, tsk) + if err != nil { + return pieceClaimBounds{}, err + } + if all != nil { + startEpoch, err := deal.StartEpoch() + if err != nil { + return pieceClaimBounds{}, err + } + + return pieceClaimBounds{ + claimTermEnd: startEpoch + all.TermMax, + }, nil + } + nv, err := m.Api.StateNetworkVersion(ctx, tsk) if err != nil { return pieceClaimBounds{}, err } - if nv >= network.Version17 { - all, err := m.Api.StateGetAllocationForPendingDeal(ctx, deal.DealID, tsk) - if err != nil { - return pieceClaimBounds{}, err - } - if all != nil { - return pieceClaimBounds{ - claimTermEnd: deal.DealProposal.StartEpoch + all.TermMax, - }, nil - } + endEpoch, err := deal.EndEpoch() + if err != nil { + return pieceClaimBounds{}, err } // no allocation for this deal, so just use a really high number for "term end" return pieceClaimBounds{ - claimTermEnd: deal.DealProposal.EndEpoch + policy.GetSectorMaxLifetime(abi.RegisteredSealProof_StackedDrg32GiBV1_1, network.Version17), + claimTermEnd: endEpoch + policy.GetSectorMaxLifetime(abi.RegisteredSealProof_StackedDrg32GiBV1_1, nv), }, nil } // called with m.inputLk; transfers the lock to another goroutine! -func (m *Sealing) addPendingPiece(ctx context.Context, size abi.UnpaddedPieceSize, data storiface.Data, deal api.PieceDealInfo, ct pieceClaimBounds, sp abi.RegisteredSealProof) *pendingPiece { +func (m *Sealing) addPendingPiece(ctx context.Context, size abi.UnpaddedPieceSize, data storiface.Data, deal UniversalPieceInfo, ct pieceClaimBounds, sp abi.RegisteredSealProof) *pendingPiece { doneCh := make(chan struct{}) pp := &pendingPiece{ size: size, @@ -417,14 +464,12 @@ func (m *Sealing) addPendingPiece(ctx context.Context, size abi.UnpaddedPieceSiz close(pp.doneCh) } - log.Debugw("new pending piece", "dealId", deal.DealID, - "piece", deal.DealProposal.PieceCID, - "size", size, - "dealStart", deal.DealSchedule.StartEpoch, - "dealEnd", deal.DealSchedule.EndEpoch, + log.Debugw("new pending piece", "pieceID", deal.String(), + "dealStart", result.Wrap(deal.StartEpoch()), + "dealEnd", result.Wrap(deal.EndEpoch()), "termEnd", ct.claimTermEnd) - m.pendingPieces[proposalCID(deal)] = pp + m.pendingPieces[deal.Key()] = pp go func() { defer m.inputLk.Unlock() if err := m.updateInput(ctx, sp); err != nil { @@ -489,7 +534,7 @@ func (m *Sealing) updateInput(ctx context.Context, sp abi.RegisteredSealProof) e type match struct { sector abi.SectorID - deal cid.Cid + deal piece.PieceKey dealEnd abi.ChainEpoch claimTermEnd abi.ChainEpoch @@ -499,7 +544,7 @@ func (m *Sealing) updateInput(ctx context.Context, sp abi.RegisteredSealProof) e } var matches []match - toAssign := map[cid.Cid]struct{}{} // used to maybe create new sectors + toAssign := map[piece.PieceKey]struct{}{} // used to maybe create new sectors // todo: this is distinctly O(n^2), may need to be optimized for tiny deals and large scale miners // (unlikely to be a problem now) @@ -523,12 +568,18 @@ func (m *Sealing) updateInput(ctx context.Context, sp abi.RegisteredSealProof) e continue } + endEpoch, err := piece.deal.EndEpoch() + if err != nil { + log.Errorf("failed to get end epoch for deal %s", piece.deal) + continue + } + if piece.size <= avail { // (note: if we have enough space for the piece, we also have enough space for inter-piece padding) matches = append(matches, match{ sector: id, deal: proposalCid, - dealEnd: piece.deal.DealProposal.EndEpoch, + dealEnd: endEpoch, claimTermEnd: piece.claimTerms.claimTermEnd, size: piece.size, @@ -609,7 +660,7 @@ func (m *Sealing) updateInput(ctx context.Context, sp abi.RegisteredSealProof) e } // pendingPieceIndex is an index in the Sealing.pendingPieces map -type pendingPieceIndex cid.Cid +type pendingPieceIndex piece.PieceKey type pieceBound struct { epoch abi.ChainEpoch @@ -633,13 +684,21 @@ func (m *Sealing) pendingPieceEpochBounds() []pieceBound { continue } + endEpoch, err := piece.deal.EndEpoch() + if err != nil { + // this really should never happen, at this point we have validated + // the piece enough times + log.Errorf("failed to get end epoch for deal %s: %v", ppi, err) + continue + } + // start bound on deal end - if boundsByEpoch[piece.deal.DealProposal.EndEpoch] == nil { - boundsByEpoch[piece.deal.DealProposal.EndEpoch] = &pieceBound{ - epoch: piece.deal.DealProposal.EndEpoch, + if boundsByEpoch[endEpoch] == nil { + boundsByEpoch[endEpoch] = &pieceBound{ + epoch: endEpoch, } } - boundsByEpoch[piece.deal.DealProposal.EndEpoch].boundStart = append(boundsByEpoch[piece.deal.DealProposal.EndEpoch].boundStart, pendingPieceIndex(ppi)) + boundsByEpoch[endEpoch].boundStart = append(boundsByEpoch[endEpoch].boundStart, pendingPieceIndex(ppi)) // end bound on term max if boundsByEpoch[piece.claimTerms.claimTermEnd] == nil { @@ -662,10 +721,10 @@ func (m *Sealing) pendingPieceEpochBounds() []pieceBound { var curBoundBytes abi.UnpaddedPieceSize for i, bound := range out { for _, ppi := range bound.boundStart { - curBoundBytes += m.pendingPieces[cid.Cid(ppi)].size + curBoundBytes += m.pendingPieces[piece.PieceKey(ppi)].size } for _, ppi := range bound.boundEnd { - curBoundBytes -= m.pendingPieces[cid.Cid(ppi)].size + curBoundBytes -= m.pendingPieces[piece.PieceKey(ppi)].size } out[i].dealBytesInBound = curBoundBytes @@ -896,15 +955,17 @@ func (m *Sealing) SectorsStatus(ctx context.Context, sid abi.SectorNumber, showO deals := make([]abi.DealID, len(info.Pieces)) pieces := make([]api.SectorPiece, len(info.Pieces)) for i, piece := range info.Pieces { - pieces[i].Piece = piece.Piece - if piece.DealInfo == nil { + // todo make this work with DDO deals in some reasonable way + + pieces[i].Piece = piece.Piece() + if !piece.HasDealInfo() || piece.Impl().PublishCid == nil { continue } - pdi := *piece.DealInfo // copy + pdi := piece.DealInfo().Impl() // copy pieces[i].DealInfo = &pdi - deals[i] = piece.DealInfo.DealID + deals[i] = piece.DealInfo().Impl().DealID } log := make([]api.SectorLog, len(info.Log)) @@ -955,14 +1016,4 @@ func (m *Sealing) SectorsStatus(ctx context.Context, sid abi.SectorNumber, showO return sInfo, nil } -func proposalCID(deal api.PieceDealInfo) cid.Cid { - pc, err := deal.DealProposal.Cid() - if err != nil { - log.Errorf("DealProposal.Cid error: %+v", err) - return cid.Undef - } - - return pc -} - var _ sectorblocks.SectorBuilder = &Sealing{} diff --git a/storage/pipeline/mocks/api.go b/storage/pipeline/mocks/api.go index 126dbba7d..3990f1a2f 100644 --- a/storage/pipeline/mocks/api.go +++ b/storage/pipeline/mocks/api.go @@ -9,13 +9,14 @@ import ( reflect "reflect" gomock "github.com/golang/mock/gomock" + blocks "github.com/ipfs/go-block-format" cid "github.com/ipfs/go-cid" address "github.com/filecoin-project/go-address" bitfield "github.com/filecoin-project/go-bitfield" abi "github.com/filecoin-project/go-state-types/abi" big "github.com/filecoin-project/go-state-types/big" - miner "github.com/filecoin-project/go-state-types/builtin/v12/miner" + miner "github.com/filecoin-project/go-state-types/builtin/v13/miner" miner0 "github.com/filecoin-project/go-state-types/builtin/v9/miner" verifreg "github.com/filecoin-project/go-state-types/builtin/v9/verifreg" crypto "github.com/filecoin-project/go-state-types/crypto" @@ -65,6 +66,21 @@ func (mr *MockSealingAPIMockRecorder) ChainGetMessage(arg0, arg1 interface{}) *g return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ChainGetMessage", reflect.TypeOf((*MockSealingAPI)(nil).ChainGetMessage), arg0, arg1) } +// ChainHasObj mocks base method. +func (m *MockSealingAPI) ChainHasObj(arg0 context.Context, arg1 cid.Cid) (bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ChainHasObj", arg0, arg1) + ret0, _ := ret[0].(bool) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ChainHasObj indicates an expected call of ChainHasObj. +func (mr *MockSealingAPIMockRecorder) ChainHasObj(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ChainHasObj", reflect.TypeOf((*MockSealingAPI)(nil).ChainHasObj), arg0, arg1) +} + // ChainHead mocks base method. func (m *MockSealingAPI) ChainHead(arg0 context.Context) (*types.TipSet, error) { m.ctrl.T.Helper() @@ -80,6 +96,20 @@ func (mr *MockSealingAPIMockRecorder) ChainHead(arg0 interface{}) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ChainHead", reflect.TypeOf((*MockSealingAPI)(nil).ChainHead), arg0) } +// ChainPutObj mocks base method. +func (m *MockSealingAPI) ChainPutObj(arg0 context.Context, arg1 blocks.Block) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ChainPutObj", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// ChainPutObj indicates an expected call of ChainPutObj. +func (mr *MockSealingAPIMockRecorder) ChainPutObj(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ChainPutObj", reflect.TypeOf((*MockSealingAPI)(nil).ChainPutObj), arg0, arg1) +} + // ChainReadObj mocks base method. func (m *MockSealingAPI) ChainReadObj(arg0 context.Context, arg1 cid.Cid) ([]byte, error) { m.ctrl.T.Helper() @@ -140,19 +170,34 @@ func (mr *MockSealingAPIMockRecorder) StateAccountKey(arg0, arg1, arg2 interface return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StateAccountKey", reflect.TypeOf((*MockSealingAPI)(nil).StateAccountKey), arg0, arg1, arg2) } -// StateComputeDataCID mocks base method. -func (m *MockSealingAPI) StateComputeDataCID(arg0 context.Context, arg1 address.Address, arg2 abi.RegisteredSealProof, arg3 []abi.DealID, arg4 types.TipSetKey) (cid.Cid, error) { +// StateGetActor mocks base method. +func (m *MockSealingAPI) StateGetActor(arg0 context.Context, arg1 address.Address, arg2 types.TipSetKey) (*types.ActorV5, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "StateComputeDataCID", arg0, arg1, arg2, arg3, arg4) - ret0, _ := ret[0].(cid.Cid) + ret := m.ctrl.Call(m, "StateGetActor", arg0, arg1, arg2) + ret0, _ := ret[0].(*types.ActorV5) ret1, _ := ret[1].(error) return ret0, ret1 } -// StateComputeDataCID indicates an expected call of StateComputeDataCID. -func (mr *MockSealingAPIMockRecorder) StateComputeDataCID(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call { +// StateGetActor indicates an expected call of StateGetActor. +func (mr *MockSealingAPIMockRecorder) StateGetActor(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StateComputeDataCID", reflect.TypeOf((*MockSealingAPI)(nil).StateComputeDataCID), arg0, arg1, arg2, arg3, arg4) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StateGetActor", reflect.TypeOf((*MockSealingAPI)(nil).StateGetActor), arg0, arg1, arg2) +} + +// StateGetAllocation mocks base method. +func (m *MockSealingAPI) StateGetAllocation(arg0 context.Context, arg1 address.Address, arg2 verifreg.AllocationId, arg3 types.TipSetKey) (*verifreg.Allocation, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "StateGetAllocation", arg0, arg1, arg2, arg3) + ret0, _ := ret[0].(*verifreg.Allocation) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// StateGetAllocation indicates an expected call of StateGetAllocation. +func (mr *MockSealingAPIMockRecorder) StateGetAllocation(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StateGetAllocation", reflect.TypeOf((*MockSealingAPI)(nil).StateGetAllocation), arg0, arg1, arg2, arg3) } // StateGetAllocationForPendingDeal mocks base method. @@ -170,6 +215,21 @@ func (mr *MockSealingAPIMockRecorder) StateGetAllocationForPendingDeal(arg0, arg return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StateGetAllocationForPendingDeal", reflect.TypeOf((*MockSealingAPI)(nil).StateGetAllocationForPendingDeal), arg0, arg1, arg2) } +// StateGetAllocationIdForPendingDeal mocks base method. +func (m *MockSealingAPI) StateGetAllocationIdForPendingDeal(arg0 context.Context, arg1 abi.DealID, arg2 types.TipSetKey) (verifreg.AllocationId, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "StateGetAllocationIdForPendingDeal", arg0, arg1, arg2) + ret0, _ := ret[0].(verifreg.AllocationId) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// StateGetAllocationIdForPendingDeal indicates an expected call of StateGetAllocationIdForPendingDeal. +func (mr *MockSealingAPIMockRecorder) StateGetAllocationIdForPendingDeal(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StateGetAllocationIdForPendingDeal", reflect.TypeOf((*MockSealingAPI)(nil).StateGetAllocationIdForPendingDeal), arg0, arg1, arg2) +} + // StateGetRandomnessFromBeacon mocks base method. func (m *MockSealingAPI) StateGetRandomnessFromBeacon(arg0 context.Context, arg1 crypto.DomainSeparationTag, arg2 abi.ChainEpoch, arg3 []byte, arg4 types.TipSetKey) (abi.Randomness, error) { m.ctrl.T.Helper() @@ -440,6 +500,21 @@ func (mr *MockSealingAPIMockRecorder) StateSectorPreCommitInfo(arg0, arg1, arg2, return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StateSectorPreCommitInfo", reflect.TypeOf((*MockSealingAPI)(nil).StateSectorPreCommitInfo), arg0, arg1, arg2, arg3) } +// StateVMCirculatingSupplyInternal mocks base method. +func (m *MockSealingAPI) StateVMCirculatingSupplyInternal(arg0 context.Context, arg1 types.TipSetKey) (api.CirculatingSupply, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "StateVMCirculatingSupplyInternal", arg0, arg1) + ret0, _ := ret[0].(api.CirculatingSupply) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// StateVMCirculatingSupplyInternal indicates an expected call of StateVMCirculatingSupplyInternal. +func (mr *MockSealingAPIMockRecorder) StateVMCirculatingSupplyInternal(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StateVMCirculatingSupplyInternal", reflect.TypeOf((*MockSealingAPI)(nil).StateVMCirculatingSupplyInternal), arg0, arg1) +} + // StateWaitMsg mocks base method. func (m *MockSealingAPI) StateWaitMsg(arg0 context.Context, arg1 cid.Cid, arg2 uint64, arg3 abi.ChainEpoch, arg4 bool) (*api.MsgLookup, error) { m.ctrl.T.Helper() diff --git a/storage/pipeline/mocks/mock_precommit_batcher.go b/storage/pipeline/mocks/mock_precommit_batcher.go index 68cce7fb0..fd46f601b 100644 --- a/storage/pipeline/mocks/mock_precommit_batcher.go +++ b/storage/pipeline/mocks/mock_precommit_batcher.go @@ -103,6 +103,21 @@ func (mr *MockPreCommitBatcherApiMockRecorder) StateAccountKey(arg0, arg1, arg2 return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StateAccountKey", reflect.TypeOf((*MockPreCommitBatcherApi)(nil).StateAccountKey), arg0, arg1, arg2) } +// StateGetAllocation mocks base method. +func (m *MockPreCommitBatcherApi) StateGetAllocation(arg0 context.Context, arg1 address.Address, arg2 verifreg.AllocationId, arg3 types.TipSetKey) (*verifreg.Allocation, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "StateGetAllocation", arg0, arg1, arg2, arg3) + ret0, _ := ret[0].(*verifreg.Allocation) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// StateGetAllocation indicates an expected call of StateGetAllocation. +func (mr *MockPreCommitBatcherApiMockRecorder) StateGetAllocation(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StateGetAllocation", reflect.TypeOf((*MockPreCommitBatcherApi)(nil).StateGetAllocation), arg0, arg1, arg2, arg3) +} + // StateGetAllocationForPendingDeal mocks base method. func (m *MockPreCommitBatcherApi) StateGetAllocationForPendingDeal(arg0 context.Context, arg1 abi.DealID, arg2 types.TipSetKey) (*verifreg.Allocation, error) { m.ctrl.T.Helper() diff --git a/storage/pipeline/piece/cbor_gen.go b/storage/pipeline/piece/cbor_gen.go new file mode 100644 index 000000000..ccf44e54b --- /dev/null +++ b/storage/pipeline/piece/cbor_gen.go @@ -0,0 +1,451 @@ +// Code generated by github.com/whyrusleeping/cbor-gen. DO NOT EDIT. + +package piece + +import ( + "fmt" + "io" + "math" + "sort" + + cid "github.com/ipfs/go-cid" + cbg "github.com/whyrusleeping/cbor-gen" + xerrors "golang.org/x/xerrors" + + abi "github.com/filecoin-project/go-state-types/abi" + miner "github.com/filecoin-project/go-state-types/builtin/v13/miner" + market "github.com/filecoin-project/go-state-types/builtin/v9/market" +) + +var _ = xerrors.Errorf +var _ = cid.Undef +var _ = math.E +var _ = sort.Sort + +func (t *PieceDealInfo) MarshalCBOR(w io.Writer) error { + if t == nil { + _, err := w.Write(cbg.CborNull) + return err + } + + cw := cbg.NewCborWriter(w) + + if _, err := cw.Write([]byte{166}); err != nil { + return err + } + + // t.DealID (abi.DealID) (uint64) + if len("DealID") > 8192 { + return xerrors.Errorf("Value in field \"DealID\" was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("DealID"))); err != nil { + return err + } + if _, err := cw.WriteString(string("DealID")); err != nil { + return err + } + + if err := cw.WriteMajorTypeHeader(cbg.MajUnsignedInt, uint64(t.DealID)); err != nil { + return err + } + + // t.PublishCid (cid.Cid) (struct) + if len("PublishCid") > 8192 { + return xerrors.Errorf("Value in field \"PublishCid\" was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("PublishCid"))); err != nil { + return err + } + if _, err := cw.WriteString(string("PublishCid")); err != nil { + return err + } + + if t.PublishCid == nil { + if _, err := cw.Write(cbg.CborNull); err != nil { + return err + } + } else { + if err := cbg.WriteCid(cw, *t.PublishCid); err != nil { + return xerrors.Errorf("failed to write cid field t.PublishCid: %w", err) + } + } + + // t.DealProposal (market.DealProposal) (struct) + if len("DealProposal") > 8192 { + return xerrors.Errorf("Value in field \"DealProposal\" was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("DealProposal"))); err != nil { + return err + } + if _, err := cw.WriteString(string("DealProposal")); err != nil { + return err + } + + if err := t.DealProposal.MarshalCBOR(cw); err != nil { + return err + } + + // t.DealSchedule (piece.DealSchedule) (struct) + if len("DealSchedule") > 8192 { + return xerrors.Errorf("Value in field \"DealSchedule\" was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("DealSchedule"))); err != nil { + return err + } + if _, err := cw.WriteString(string("DealSchedule")); err != nil { + return err + } + + if err := t.DealSchedule.MarshalCBOR(cw); err != nil { + return err + } + + // t.KeepUnsealed (bool) (bool) + if len("KeepUnsealed") > 8192 { + return xerrors.Errorf("Value in field \"KeepUnsealed\" was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("KeepUnsealed"))); err != nil { + return err + } + if _, err := cw.WriteString(string("KeepUnsealed")); err != nil { + return err + } + + if err := cbg.WriteBool(w, t.KeepUnsealed); err != nil { + return err + } + + // t.PieceActivationManifest (miner.PieceActivationManifest) (struct) + if len("PieceActivationManifest") > 8192 { + return xerrors.Errorf("Value in field \"PieceActivationManifest\" was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("PieceActivationManifest"))); err != nil { + return err + } + if _, err := cw.WriteString(string("PieceActivationManifest")); err != nil { + return err + } + + if err := t.PieceActivationManifest.MarshalCBOR(cw); err != nil { + return err + } + return nil +} + +func (t *PieceDealInfo) UnmarshalCBOR(r io.Reader) (err error) { + *t = PieceDealInfo{} + + cr := cbg.NewCborReader(r) + + maj, extra, err := cr.ReadHeader() + if err != nil { + return err + } + defer func() { + if err == io.EOF { + err = io.ErrUnexpectedEOF + } + }() + + if maj != cbg.MajMap { + return fmt.Errorf("cbor input should be of type map") + } + + if extra > cbg.MaxLength { + return fmt.Errorf("PieceDealInfo: map struct too large (%d)", extra) + } + + var name string + n := extra + + for i := uint64(0); i < n; i++ { + + { + sval, err := cbg.ReadStringWithMax(cr, 8192) + if err != nil { + return err + } + + name = string(sval) + } + + switch name { + // t.DealID (abi.DealID) (uint64) + case "DealID": + + { + + maj, extra, err = cr.ReadHeader() + if err != nil { + return err + } + if maj != cbg.MajUnsignedInt { + return fmt.Errorf("wrong type for uint64 field") + } + t.DealID = abi.DealID(extra) + + } + // t.PublishCid (cid.Cid) (struct) + case "PublishCid": + + { + + b, err := cr.ReadByte() + if err != nil { + return err + } + if b != cbg.CborNull[0] { + if err := cr.UnreadByte(); err != nil { + return err + } + + c, err := cbg.ReadCid(cr) + if err != nil { + return xerrors.Errorf("failed to read cid field t.PublishCid: %w", err) + } + + t.PublishCid = &c + } + + } + // t.DealProposal (market.DealProposal) (struct) + case "DealProposal": + + { + + b, err := cr.ReadByte() + if err != nil { + return err + } + if b != cbg.CborNull[0] { + if err := cr.UnreadByte(); err != nil { + return err + } + t.DealProposal = new(market.DealProposal) + if err := t.DealProposal.UnmarshalCBOR(cr); err != nil { + return xerrors.Errorf("unmarshaling t.DealProposal pointer: %w", err) + } + } + + } + // t.DealSchedule (piece.DealSchedule) (struct) + case "DealSchedule": + + { + + if err := t.DealSchedule.UnmarshalCBOR(cr); err != nil { + return xerrors.Errorf("unmarshaling t.DealSchedule: %w", err) + } + + } + // t.KeepUnsealed (bool) (bool) + case "KeepUnsealed": + + maj, extra, err = cr.ReadHeader() + if err != nil { + return err + } + if maj != cbg.MajOther { + return fmt.Errorf("booleans must be major type 7") + } + switch extra { + case 20: + t.KeepUnsealed = false + case 21: + t.KeepUnsealed = true + default: + return fmt.Errorf("booleans are either major type 7, value 20 or 21 (got %d)", extra) + } + // t.PieceActivationManifest (miner.PieceActivationManifest) (struct) + case "PieceActivationManifest": + + { + + b, err := cr.ReadByte() + if err != nil { + return err + } + if b != cbg.CborNull[0] { + if err := cr.UnreadByte(); err != nil { + return err + } + t.PieceActivationManifest = new(miner.PieceActivationManifest) + if err := t.PieceActivationManifest.UnmarshalCBOR(cr); err != nil { + return xerrors.Errorf("unmarshaling t.PieceActivationManifest pointer: %w", err) + } + } + + } + + default: + // Field doesn't exist on this type, so ignore it + cbg.ScanForLinks(r, func(cid.Cid) {}) + } + } + + return nil +} +func (t *DealSchedule) MarshalCBOR(w io.Writer) error { + if t == nil { + _, err := w.Write(cbg.CborNull) + return err + } + + cw := cbg.NewCborWriter(w) + + if _, err := cw.Write([]byte{162}); err != nil { + return err + } + + // t.EndEpoch (abi.ChainEpoch) (int64) + if len("EndEpoch") > 8192 { + return xerrors.Errorf("Value in field \"EndEpoch\" was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("EndEpoch"))); err != nil { + return err + } + if _, err := cw.WriteString(string("EndEpoch")); err != nil { + return err + } + + if t.EndEpoch >= 0 { + if err := cw.WriteMajorTypeHeader(cbg.MajUnsignedInt, uint64(t.EndEpoch)); err != nil { + return err + } + } else { + if err := cw.WriteMajorTypeHeader(cbg.MajNegativeInt, uint64(-t.EndEpoch-1)); err != nil { + return err + } + } + + // t.StartEpoch (abi.ChainEpoch) (int64) + if len("StartEpoch") > 8192 { + return xerrors.Errorf("Value in field \"StartEpoch\" was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("StartEpoch"))); err != nil { + return err + } + if _, err := cw.WriteString(string("StartEpoch")); err != nil { + return err + } + + if t.StartEpoch >= 0 { + if err := cw.WriteMajorTypeHeader(cbg.MajUnsignedInt, uint64(t.StartEpoch)); err != nil { + return err + } + } else { + if err := cw.WriteMajorTypeHeader(cbg.MajNegativeInt, uint64(-t.StartEpoch-1)); err != nil { + return err + } + } + + return nil +} + +func (t *DealSchedule) UnmarshalCBOR(r io.Reader) (err error) { + *t = DealSchedule{} + + cr := cbg.NewCborReader(r) + + maj, extra, err := cr.ReadHeader() + if err != nil { + return err + } + defer func() { + if err == io.EOF { + err = io.ErrUnexpectedEOF + } + }() + + if maj != cbg.MajMap { + return fmt.Errorf("cbor input should be of type map") + } + + if extra > cbg.MaxLength { + return fmt.Errorf("DealSchedule: map struct too large (%d)", extra) + } + + var name string + n := extra + + for i := uint64(0); i < n; i++ { + + { + sval, err := cbg.ReadStringWithMax(cr, 8192) + if err != nil { + return err + } + + name = string(sval) + } + + switch name { + // t.EndEpoch (abi.ChainEpoch) (int64) + case "EndEpoch": + { + maj, extra, err := cr.ReadHeader() + if err != nil { + return err + } + var extraI int64 + switch maj { + case cbg.MajUnsignedInt: + extraI = int64(extra) + if extraI < 0 { + return fmt.Errorf("int64 positive overflow") + } + case cbg.MajNegativeInt: + extraI = int64(extra) + if extraI < 0 { + return fmt.Errorf("int64 negative overflow") + } + extraI = -1 - extraI + default: + return fmt.Errorf("wrong type for int64 field: %d", maj) + } + + t.EndEpoch = abi.ChainEpoch(extraI) + } + // t.StartEpoch (abi.ChainEpoch) (int64) + case "StartEpoch": + { + maj, extra, err := cr.ReadHeader() + if err != nil { + return err + } + var extraI int64 + switch maj { + case cbg.MajUnsignedInt: + extraI = int64(extra) + if extraI < 0 { + return fmt.Errorf("int64 positive overflow") + } + case cbg.MajNegativeInt: + extraI = int64(extra) + if extraI < 0 { + return fmt.Errorf("int64 negative overflow") + } + extraI = -1 - extraI + default: + return fmt.Errorf("wrong type for int64 field: %d", maj) + } + + t.StartEpoch = abi.ChainEpoch(extraI) + } + + default: + // Field doesn't exist on this type, so ignore it + cbg.ScanForLinks(r, func(cid.Cid) {}) + } + } + + return nil +} diff --git a/storage/pipeline/piece/piece_info.go b/storage/pipeline/piece/piece_info.go new file mode 100644 index 000000000..7ee8f7029 --- /dev/null +++ b/storage/pipeline/piece/piece_info.go @@ -0,0 +1,186 @@ +package piece + +import ( + "context" + "fmt" + + "github.com/ipfs/go-cid" + "golang.org/x/xerrors" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/go-state-types/network" + + "github.com/filecoin-project/lotus/chain/actors/builtin/market" + "github.com/filecoin-project/lotus/chain/actors/builtin/miner" + verifregtypes "github.com/filecoin-project/lotus/chain/actors/builtin/verifreg" + "github.com/filecoin-project/lotus/chain/types" +) + +// DealInfo is a tuple of deal identity and its schedule +type PieceDealInfo struct { + // "Old" builtin-market deal info + PublishCid *cid.Cid + DealID abi.DealID + DealProposal *market.DealProposal + + // Common deal info, required for all pieces + // TODO: https://github.com/filecoin-project/lotus/issues/11237 + DealSchedule DealSchedule + + // Direct Data Onboarding + // When PieceActivationManifest is set, builtin-market deal info must not be set + PieceActivationManifest *miner.PieceActivationManifest + + // Best-effort deal asks + KeepUnsealed bool +} + +// DealSchedule communicates the time interval of a storage deal. The deal must +// appear in a sealed (proven) sector no later than StartEpoch, otherwise it +// is invalid. +type DealSchedule struct { + StartEpoch abi.ChainEpoch + EndEpoch abi.ChainEpoch +} + +func (ds *PieceDealInfo) isBuiltinMarketDeal() bool { + return ds.PublishCid != nil +} + +// Valid validates the deal info after being accepted through RPC, checks that +// the deal metadata is well-formed. +func (ds *PieceDealInfo) Valid(nv network.Version) error { + hasLegacyDealInfo := ds.PublishCid != nil && ds.DealID != 0 && ds.DealProposal != nil + hasPieceActivationManifest := ds.PieceActivationManifest != nil + + if hasLegacyDealInfo && hasPieceActivationManifest { + return xerrors.Errorf("piece deal info has both legacy deal info and piece activation manifest") + } + + if !hasLegacyDealInfo && !hasPieceActivationManifest { + return xerrors.Errorf("piece deal info has neither legacy deal info nor piece activation manifest") + } + + if hasLegacyDealInfo { + if _, err := ds.DealProposal.Cid(); err != nil { + return xerrors.Errorf("checking proposal CID: %w", err) + } + } + + if ds.DealSchedule.StartEpoch <= 0 { + return xerrors.Errorf("invalid deal start epoch %d", ds.DealSchedule.StartEpoch) + } + if ds.DealSchedule.EndEpoch <= 0 { + return xerrors.Errorf("invalid deal end epoch %d", ds.DealSchedule.EndEpoch) + } + if ds.DealSchedule.EndEpoch <= ds.DealSchedule.StartEpoch { + return xerrors.Errorf("invalid deal end epoch %d (start %d)", ds.DealSchedule.EndEpoch, ds.DealSchedule.StartEpoch) + } + + if hasPieceActivationManifest { + if nv < network.Version22 { + return xerrors.Errorf("direct-data-onboarding pieces aren't accepted before network version 22") + } + + // todo any more checks seem reasonable to put here? + } + + return nil +} + +type AllocationAPI interface { + StateGetAllocationForPendingDeal(ctx context.Context, dealId abi.DealID, tsk types.TipSetKey) (*verifregtypes.Allocation, error) + StateGetAllocation(ctx context.Context, clientAddr address.Address, allocationId verifregtypes.AllocationId, tsk types.TipSetKey) (*verifregtypes.Allocation, error) +} + +func (ds *PieceDealInfo) GetAllocation(ctx context.Context, aapi AllocationAPI, tsk types.TipSetKey) (*verifregtypes.Allocation, error) { + switch { + case ds.isBuiltinMarketDeal(): + return aapi.StateGetAllocationForPendingDeal(ctx, ds.DealID, tsk) + default: + if ds.PieceActivationManifest.VerifiedAllocationKey == nil { + return nil, nil + } + + caddr, err := address.NewIDAddress(uint64(ds.PieceActivationManifest.VerifiedAllocationKey.Client)) + if err != nil { + return nil, err + } + + all, err := aapi.StateGetAllocation(ctx, caddr, verifregtypes.AllocationId(ds.PieceActivationManifest.VerifiedAllocationKey.ID), tsk) + if err != nil { + return nil, err + } + + if all == nil { + return nil, nil + } + + if all.Client != ds.PieceActivationManifest.VerifiedAllocationKey.Client { + return nil, xerrors.Errorf("allocation client mismatch: %d != %d", all.Client, ds.PieceActivationManifest.VerifiedAllocationKey.Client) + } + + return all, nil + } +} + +// StartEpoch returns the last epoch in which the sector containing this deal +// must be sealed (committed) in order for the deal to be valid. +func (ds *PieceDealInfo) StartEpoch() (abi.ChainEpoch, error) { + switch { + case ds.isBuiltinMarketDeal(): + return ds.DealSchedule.StartEpoch, nil + default: + // note - when implementing make sure to cache any dynamically computed values + // todo do we want a smarter mechanism here + return ds.DealSchedule.StartEpoch, nil + } +} + +// EndEpoch returns the minimum epoch until which the sector containing this +// deal must be committed until. +func (ds *PieceDealInfo) EndEpoch() (abi.ChainEpoch, error) { + switch { + case ds.isBuiltinMarketDeal(): + return ds.DealSchedule.EndEpoch, nil + default: + // note - when implementing make sure to cache any dynamically computed values + // todo do we want a smarter mechanism here + return ds.DealSchedule.StartEpoch, nil + } +} + +func (ds *PieceDealInfo) PieceCID() cid.Cid { + switch { + case ds.isBuiltinMarketDeal(): + return ds.DealProposal.PieceCID + default: + return ds.PieceActivationManifest.CID + } +} + +func (ds *PieceDealInfo) String() string { + switch { + case ds.isBuiltinMarketDeal(): + return fmt.Sprintf("BuiltinMarket{DealID: %d, PieceCID: %s, PublishCid: %s}", ds.DealID, ds.DealProposal.PieceCID, ds.PublishCid) + default: + // todo check that VAlloc doesn't print as a pointer + return fmt.Sprintf("DirectDataOnboarding{PieceCID: %s, VAllloc: %x}", ds.PieceActivationManifest.CID, ds.PieceActivationManifest.VerifiedAllocationKey) + } +} + +func (ds *PieceDealInfo) KeepUnsealedRequested() bool { + return ds.KeepUnsealed +} + +type PieceKey string + +// Key returns a unique identifier for this deal info, for use in maps. +func (ds *PieceDealInfo) Key() PieceKey { + return PieceKey(ds.String()) +} + +func (ds *PieceDealInfo) Impl() PieceDealInfo { + return *ds +} diff --git a/storage/pipeline/pledge.go b/storage/pipeline/pledge.go new file mode 100644 index 000000000..04567fca1 --- /dev/null +++ b/storage/pipeline/pledge.go @@ -0,0 +1,114 @@ +package sealing + +import ( + "context" + + cbor "github.com/ipfs/go-ipld-cbor" + "golang.org/x/xerrors" + + "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/go-state-types/big" + + bstore "github.com/filecoin-project/lotus/blockstore" + "github.com/filecoin-project/lotus/chain/actors/adt" + "github.com/filecoin-project/lotus/chain/actors/builtin" + "github.com/filecoin-project/lotus/chain/actors/builtin/power" + "github.com/filecoin-project/lotus/chain/actors/builtin/reward" + "github.com/filecoin-project/lotus/chain/types" +) + +var initialPledgeNum = types.NewInt(110) +var initialPledgeDen = types.NewInt(100) + +func (m *Sealing) pledgeForPower(ctx context.Context, addedPower abi.StoragePower) (abi.TokenAmount, error) { + store := adt.WrapStore(ctx, cbor.NewCborStore(bstore.NewAPIBlockstore(m.Api))) + + // load power actor + var ( + powerSmoothed builtin.FilterEstimate + pledgeCollateral abi.TokenAmount + ) + if act, err := m.Api.StateGetActor(ctx, power.Address, types.EmptyTSK); err != nil { + return types.EmptyInt, xerrors.Errorf("loading power actor: %w", err) + } else if s, err := power.Load(store, act); err != nil { + return types.EmptyInt, xerrors.Errorf("loading power actor state: %w", err) + } else if p, err := s.TotalPowerSmoothed(); err != nil { + return types.EmptyInt, xerrors.Errorf("failed to determine total power: %w", err) + } else if c, err := s.TotalLocked(); err != nil { + return types.EmptyInt, xerrors.Errorf("failed to determine pledge collateral: %w", err) + } else { + powerSmoothed = p + pledgeCollateral = c + } + + // load reward actor + rewardActor, err := m.Api.StateGetActor(ctx, reward.Address, types.EmptyTSK) + if err != nil { + return types.EmptyInt, xerrors.Errorf("loading reward actor: %w", err) + } + + rewardState, err := reward.Load(store, rewardActor) + if err != nil { + return types.EmptyInt, xerrors.Errorf("loading reward actor state: %w", err) + } + + // get circulating supply + circSupply, err := m.Api.StateVMCirculatingSupplyInternal(ctx, types.EmptyTSK) + if err != nil { + return big.Zero(), xerrors.Errorf("getting circulating supply: %w", err) + } + + // do the calculation + initialPledge, err := rewardState.InitialPledgeForPower( + addedPower, + pledgeCollateral, + &powerSmoothed, + circSupply.FilCirculating, + ) + if err != nil { + return big.Zero(), xerrors.Errorf("calculating initial pledge: %w", err) + } + + return types.BigDiv(types.BigMul(initialPledge, initialPledgeNum), initialPledgeDen), nil +} + +func (m *Sealing) sectorWeight(ctx context.Context, sector SectorInfo, expiration abi.ChainEpoch) (abi.StoragePower, error) { + spt, err := m.currentSealProof(ctx) + if err != nil { + return types.EmptyInt, xerrors.Errorf("getting seal proof type: %w", err) + } + + ssize, err := spt.SectorSize() + if err != nil { + return types.EmptyInt, xerrors.Errorf("getting sector size: %w", err) + } + + ts, err := m.Api.ChainHead(ctx) + if err != nil { + return types.EmptyInt, xerrors.Errorf("getting chain head: %w", err) + } + + // get verified deal infos + var w, vw = big.Zero(), big.Zero() + + for _, piece := range sector.Pieces { + if !piece.HasDealInfo() { + // todo StateMinerInitialPledgeCollateral doesn't add cc/padding to non-verified weight, is that correct? + continue + } + + alloc, err := piece.GetAllocation(ctx, m.Api, ts.Key()) + if err != nil || alloc == nil { + w = big.Add(w, abi.NewStoragePower(int64(piece.Piece().Size))) + continue + } + + vw = big.Add(vw, abi.NewStoragePower(int64(piece.Piece().Size))) + } + + // load market actor + duration := expiration - ts.Height() + sectorWeight := builtin.QAPowerForWeight(ssize, duration, w, vw) + + return sectorWeight, nil +} diff --git a/storage/pipeline/precommit_batch.go b/storage/pipeline/precommit_batch.go index 3a86c8628..099988010 100644 --- a/storage/pipeline/precommit_batch.go +++ b/storage/pipeline/precommit_batch.go @@ -36,6 +36,7 @@ type PreCommitBatcherApi interface { ChainHead(ctx context.Context) (*types.TipSet, error) StateNetworkVersion(ctx context.Context, tsk types.TipSetKey) (network.Version, error) StateGetAllocationForPendingDeal(ctx context.Context, dealId abi.DealID, tsk types.TipSetKey) (*verifregtypes.Allocation, error) + StateGetAllocation(ctx context.Context, clientAddr address.Address, allocationId verifregtypes.AllocationId, tsk types.TipSetKey) (*verifregtypes.Allocation, error) // Address selector WalletBalance(context.Context, address.Address) (types.BigInt, error) @@ -428,11 +429,18 @@ func (b *PreCommitBatcher) Stop(ctx context.Context) error { func getDealStartCutoff(si SectorInfo) abi.ChainEpoch { cutoffEpoch := si.TicketEpoch + policy.MaxPreCommitRandomnessLookback for _, p := range si.Pieces { - if p.DealInfo == nil { + if !p.HasDealInfo() { + continue + } + + startEpoch, err := p.StartEpoch() + if err != nil { + // almost definitely can't happen, but if it does there's less harm in + // just logging the error and moving on + log.Errorw("failed to get deal start epoch", "error", err) continue } - startEpoch := p.DealInfo.DealSchedule.StartEpoch if startEpoch < cutoffEpoch { cutoffEpoch = startEpoch } @@ -444,15 +452,19 @@ func getDealStartCutoff(si SectorInfo) abi.ChainEpoch { func (b *PreCommitBatcher) getAllocationCutoff(si SectorInfo) abi.ChainEpoch { cutoff := si.TicketEpoch + policy.MaxPreCommitRandomnessLookback for _, p := range si.Pieces { - if p.DealInfo == nil { + if !p.HasDealInfo() { continue } - alloc, _ := b.api.StateGetAllocationForPendingDeal(b.mctx, p.DealInfo.DealID, types.EmptyTSK) + alloc, err := p.GetAllocation(b.mctx, b.api, types.EmptyTSK) + if err != nil { + log.Errorw("failed to get deal allocation", "error", err) + } // alloc is nil if this is not a verified deal in nv17 or later if alloc == nil { continue } + if alloc.Expiration < cutoff { cutoff = alloc.Expiration } diff --git a/storage/pipeline/precommit_batch_test.go b/storage/pipeline/precommit_batch_test.go deleted file mode 100644 index 1f3aaf244..000000000 --- a/storage/pipeline/precommit_batch_test.go +++ /dev/null @@ -1,291 +0,0 @@ -// stm: #unit -package sealing_test - -import ( - "bytes" - "context" - "sort" - "sync" - "testing" - "time" - - "github.com/golang/mock/gomock" - "github.com/stretchr/testify/require" - - "github.com/filecoin-project/go-address" - "github.com/filecoin-project/go-state-types/abi" - "github.com/filecoin-project/go-state-types/big" - minertypes "github.com/filecoin-project/go-state-types/builtin/v9/miner" - "github.com/filecoin-project/go-state-types/network" - miner6 "github.com/filecoin-project/specs-actors/v6/actors/builtin/miner" - - "github.com/filecoin-project/lotus/api" - "github.com/filecoin-project/lotus/chain/types" - "github.com/filecoin-project/lotus/node/config" - pipeline "github.com/filecoin-project/lotus/storage/pipeline" - "github.com/filecoin-project/lotus/storage/pipeline/mocks" - "github.com/filecoin-project/lotus/storage/pipeline/sealiface" -) - -var fc = config.MinerFeeConfig{ - MaxPreCommitGasFee: types.FIL(types.FromFil(1)), - MaxCommitGasFee: types.FIL(types.FromFil(1)), - MaxTerminateGasFee: types.FIL(types.FromFil(1)), - MaxPreCommitBatchGasFee: config.BatchFeeConfig{Base: types.FIL(types.FromFil(3)), PerSector: types.FIL(types.FromFil(1))}, - MaxCommitBatchGasFee: config.BatchFeeConfig{Base: types.FIL(types.FromFil(3)), PerSector: types.FIL(types.FromFil(1))}, -} - -func TestPrecommitBatcher(t *testing.T) { - //stm: @CHAIN_STATE_MINER_CALCULATE_DEADLINE_001 - t0123, err := address.NewFromString("t0123") - require.NoError(t, err) - - ctx := context.Background() - - as := asel(func(ctx context.Context, mi api.MinerInfo, use api.AddrUse, goodFunds, minFunds abi.TokenAmount) (address.Address, abi.TokenAmount, error) { - return t0123, big.Zero(), nil - }) - - maxBatch := miner6.PreCommitSectorBatchMaxSize - - cfg := func() (sealiface.Config, error) { - return sealiface.Config{ - MaxWaitDealsSectors: 2, - MaxSealingSectors: 0, - MaxSealingSectorsForDeals: 0, - WaitDealsDelay: time.Hour * 6, - AlwaysKeepUnsealedCopy: true, - - MaxPreCommitBatch: maxBatch, - PreCommitBatchWait: 24 * time.Hour, - PreCommitBatchSlack: 3 * time.Hour, - BatchPreCommitAboveBaseFee: big.NewInt(10000), - - AggregateCommits: true, - MinCommitBatch: miner6.MinAggregatedSectors, - MaxCommitBatch: miner6.MaxAggregatedSectors, - CommitBatchWait: 24 * time.Hour, - CommitBatchSlack: 1 * time.Hour, - - TerminateBatchMin: 1, - TerminateBatchMax: 100, - TerminateBatchWait: 5 * time.Minute, - }, nil - } - - type promise func(t *testing.T) - type action func(t *testing.T, s *mocks.MockPreCommitBatcherApi, pcb *pipeline.PreCommitBatcher) promise - - actions := func(as ...action) action { - return func(t *testing.T, s *mocks.MockPreCommitBatcherApi, pcb *pipeline.PreCommitBatcher) promise { - var ps []promise - for _, a := range as { - p := a(t, s, pcb) - if p != nil { - ps = append(ps, p) - } - } - - if len(ps) > 0 { - return func(t *testing.T) { - for _, p := range ps { - p(t) - } - } - } - return nil - } - } - - addSector := func(sn abi.SectorNumber, aboveBalancer bool) action { - return func(t *testing.T, s *mocks.MockPreCommitBatcherApi, pcb *pipeline.PreCommitBatcher) promise { - var pcres sealiface.PreCommitBatchRes - var pcerr error - done := sync.Mutex{} - done.Lock() - - si := pipeline.SectorInfo{ - SectorNumber: sn, - } - - basefee := big.NewInt(9999) - if aboveBalancer { - basefee = big.NewInt(10001) - } - - s.EXPECT().ChainHead(gomock.Any()).Return(makeBFTs(t, basefee, 1), nil).MaxTimes(2) // once in AddPreCommit - - go func() { - defer done.Unlock() - pcres, pcerr = pcb.AddPreCommit(ctx, si, big.Zero(), &minertypes.SectorPreCommitInfo{ - SectorNumber: si.SectorNumber, - SealedCID: fakePieceCid(t), - DealIDs: nil, - Expiration: 0, - }) - }() - - return func(t *testing.T) { - done.Lock() - require.NoError(t, pcerr) - require.Empty(t, pcres.Error) - require.Contains(t, pcres.Sectors, si.SectorNumber) - } - } - } - - addSectors := func(sectors []abi.SectorNumber, aboveBalancer bool) action { - as := make([]action, len(sectors)) - for i, sector := range sectors { - as[i] = addSector(sector, aboveBalancer) - } - return actions(as...) - } - - waitPending := func(n int) action { - return func(t *testing.T, s *mocks.MockPreCommitBatcherApi, pcb *pipeline.PreCommitBatcher) promise { - require.Eventually(t, func() bool { - p, err := pcb.Pending(ctx) - require.NoError(t, err) - return len(p) == n - }, time.Second*5, 10*time.Millisecond) - - return nil - } - } - - //stm: @CHAIN_STATE_MINER_INFO_001, @CHAIN_STATE_NETWORK_VERSION_001 - expectSend := func(expect []abi.SectorNumber, gasOverLimit bool) action { - return func(t *testing.T, s *mocks.MockPreCommitBatcherApi, pcb *pipeline.PreCommitBatcher) promise { - s.EXPECT().StateMinerInfo(gomock.Any(), gomock.Any(), gomock.Any()).Return(api.MinerInfo{Owner: t0123, Worker: t0123}, nil) - if gasOverLimit { - s.EXPECT().GasEstimateMessageGas(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, &api.ErrOutOfGas{}) - } else { - s.EXPECT().GasEstimateMessageGas(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(&types.Message{GasLimit: 100000}, nil) - } - - if !gasOverLimit { - s.EXPECT().MpoolPushMessage(gomock.Any(), funMatcher(func(i interface{}) bool { - b := i.(*types.Message) - var params miner6.PreCommitSectorBatchParams - require.NoError(t, params.UnmarshalCBOR(bytes.NewReader(b.Params))) - for s, number := range expect { - require.Equal(t, number, params.Sectors[s].SectorNumber) - } - return true - }), gomock.Any()).Return(dummySmsg, nil) - } - return nil - } - } - - expectInitialCalls := func() action { - return func(t *testing.T, s *mocks.MockPreCommitBatcherApi, pcb *pipeline.PreCommitBatcher) promise { - s.EXPECT().ChainHead(gomock.Any()).Return(makeBFTs(t, big.NewInt(10001), 1), nil) - return nil - } - } - - flush := func(expect []abi.SectorNumber) action { - return func(t *testing.T, s *mocks.MockPreCommitBatcherApi, pcb *pipeline.PreCommitBatcher) promise { - _ = expectInitialCalls()(t, s, pcb) - _ = expectSend(expect, false)(t, s, pcb) - - r, err := pcb.Flush(ctx) - require.NoError(t, err) - require.Len(t, r, 1) - require.Empty(t, r[0].Error) - sort.Slice(r[0].Sectors, func(i, j int) bool { - return r[0].Sectors[i] < r[0].Sectors[j] - }) - require.Equal(t, expect, r[0].Sectors) - - return nil - } - } - - getSectors := func(n int) []abi.SectorNumber { - out := make([]abi.SectorNumber, n) - for i := range out { - out[i] = abi.SectorNumber(i) - } - return out - } - - tcs := map[string]struct { - actions []action - }{ - "addSingle": { - actions: []action{ - addSector(0, true), - waitPending(1), - flush([]abi.SectorNumber{0}), - }, - }, - "addMax": { - actions: []action{ - expectInitialCalls(), - expectSend(getSectors(maxBatch), false), - addSectors(getSectors(maxBatch), true), - }, - }, - "addMax-gasAboveLimit": { - actions: []action{ - expectInitialCalls(), - expectSend(getSectors(maxBatch), true), - expectSend(getSectors(maxBatch)[:maxBatch/2], false), - expectSend(getSectors(maxBatch)[maxBatch/2:], false), - addSectors(getSectors(maxBatch), true), - }, - }, - "addOne-belowBaseFee": { - actions: []action{ - expectSend(getSectors(1), false), - addSectors(getSectors(1), false), - }, - }, - } - - for name, tc := range tcs { - tc := tc - - t.Run(name, func(t *testing.T) { - // create go mock controller here - mockCtrl := gomock.NewController(t) - // when test is done, assert expectations on all mock objects. - defer mockCtrl.Finish() - - // create them mocks - pcapi := mocks.NewMockPreCommitBatcherApi(mockCtrl) - pcapi.EXPECT().StateNetworkVersion(gomock.Any(), gomock.Any()).Return(network.Version20, nil).AnyTimes() - - pcb := pipeline.NewPreCommitBatcher(ctx, t0123, pcapi, as, fc, cfg) - - var promises []promise - - for _, a := range tc.actions { - p := a(t, pcapi, pcb) - if p != nil { - promises = append(promises, p) - } - } - - for _, p := range promises { - p(t) - } - - err := pcb.Stop(ctx) - require.NoError(t, err) - }) - } -} - -type funMatcher func(interface{}) bool - -func (funMatcher) Matches(interface{}) bool { - return true -} - -func (funMatcher) String() string { - return "fun" -} diff --git a/storage/pipeline/precommit_policy.go b/storage/pipeline/precommit_policy.go index 6e234f930..6df44d407 100644 --- a/storage/pipeline/precommit_policy.go +++ b/storage/pipeline/precommit_policy.go @@ -9,7 +9,6 @@ import ( "github.com/filecoin-project/go-state-types/builtin/v8/miner" "github.com/filecoin-project/go-state-types/network" - "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/chain/actors/builtin" "github.com/filecoin-project/lotus/chain/actors/policy" "github.com/filecoin-project/lotus/chain/types" @@ -17,7 +16,7 @@ import ( ) type PreCommitPolicy interface { - Expiration(ctx context.Context, ps ...api.SectorPiece) (abi.ChainEpoch, error) + Expiration(ctx context.Context, ps ...SafeSectorPiece) (abi.ChainEpoch, error) } type Chain interface { @@ -60,7 +59,7 @@ func NewBasicPreCommitPolicy(api Chain, cfgGetter dtypes.GetSealingConfigFunc, p // Expiration produces the pre-commit sector expiration epoch for an encoded // replica containing the provided enumeration of pieces and deals. -func (p *BasicPreCommitPolicy) Expiration(ctx context.Context, ps ...api.SectorPiece) (abi.ChainEpoch, error) { +func (p *BasicPreCommitPolicy) Expiration(ctx context.Context, ps ...SafeSectorPiece) (abi.ChainEpoch, error) { ts, err := p.api.ChainHead(ctx) if err != nil { return 0, err @@ -69,17 +68,22 @@ func (p *BasicPreCommitPolicy) Expiration(ctx context.Context, ps ...api.SectorP var end *abi.ChainEpoch for _, p := range ps { - if p.DealInfo == nil { + if !p.HasDealInfo() { continue } - if p.DealInfo.DealSchedule.EndEpoch < ts.Height() { + endEpoch, err := p.EndEpoch() + if err != nil { + return 0, xerrors.Errorf("failed to get end epoch: %w", err) + } + + if endEpoch < ts.Height() { log.Warnf("piece schedule %+v ended before current epoch %d", p, ts.Height()) continue } - if end == nil || *end < p.DealInfo.DealSchedule.EndEpoch { - tmp := p.DealInfo.DealSchedule.EndEpoch + if end == nil || *end < endEpoch { + tmp := endEpoch end = &tmp } } diff --git a/storage/pipeline/precommit_policy_test.go b/storage/pipeline/precommit_policy_test.go index 7865560de..ec2a61ff2 100644 --- a/storage/pipeline/precommit_policy_test.go +++ b/storage/pipeline/precommit_policy_test.go @@ -11,6 +11,8 @@ import ( commcid "github.com/filecoin-project/go-fil-commcid" "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/go-state-types/big" + "github.com/filecoin-project/go-state-types/crypto" "github.com/filecoin-project/go-state-types/network" "github.com/filecoin-project/lotus/api" @@ -20,6 +22,7 @@ import ( "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/node/modules/dtypes" pipeline "github.com/filecoin-project/lotus/storage/pipeline" + "github.com/filecoin-project/lotus/storage/pipeline/piece" "github.com/filecoin-project/lotus/storage/pipeline/sealiface" ) @@ -47,6 +50,39 @@ func (f *fakeChain) StateNetworkVersion(ctx context.Context, tsk types.TipSetKey return build.TestNetworkVersion, nil } +func makeBFTs(t *testing.T, basefee abi.TokenAmount, h abi.ChainEpoch) *types.TipSet { + dummyCid, _ := cid.Parse("bafkqaaa") + + var ts, err = types.NewTipSet([]*types.BlockHeader{ + { + Height: h, + Miner: builtin.SystemActorAddr, + + Parents: []cid.Cid{}, + + Ticket: &types.Ticket{VRFProof: []byte{byte(h % 2)}}, + + ParentStateRoot: dummyCid, + Messages: dummyCid, + ParentMessageReceipts: dummyCid, + + BlockSig: &crypto.Signature{Type: crypto.SigTypeBLS}, + BLSAggregate: &crypto.Signature{Type: crypto.SigTypeBLS}, + + ParentBaseFee: basefee, + }, + }) + if t != nil { + require.NoError(t, err) + } + + return ts +} + +func makeTs(t *testing.T, h abi.ChainEpoch) *types.TipSet { + return makeBFTs(t, big.NewInt(0), h) +} + func (f *fakeChain) ChainHead(ctx context.Context) (*types.TipSet, error) { return makeTs(nil, f.h), nil } @@ -58,6 +94,10 @@ func fakePieceCid(t *testing.T) cid.Cid { return fakePieceCid } +func cidPtr(c cid.Cid) *cid.Cid { + return &c +} + func TestBasicPolicyEmptySector(t *testing.T) { cfg := fakeConfigGetter(nil) h := abi.ChainEpoch(55) @@ -97,33 +137,35 @@ func TestBasicPolicyMostConstrictiveSchedule(t *testing.T) { h: abi.ChainEpoch(55), }, cfg, 2) longestDealEpochEnd := abi.ChainEpoch(547300) - pieces := []api.SectorPiece{ - { + pieces := []pipeline.SafeSectorPiece{ + pipeline.SafePiece(api.SectorPiece{ Piece: abi.PieceInfo{ Size: abi.PaddedPieceSize(1024), PieceCID: fakePieceCid(t), }, - DealInfo: &api.PieceDealInfo{ - DealID: abi.DealID(42), - DealSchedule: api.DealSchedule{ + DealInfo: &piece.PieceDealInfo{ + PublishCid: cidPtr(fakePieceCid(t)), // pretend this is a valid builtin-market deal + DealID: abi.DealID(42), + DealSchedule: piece.DealSchedule{ StartEpoch: abi.ChainEpoch(70), EndEpoch: abi.ChainEpoch(547275), }, }, - }, - { + }), + pipeline.SafePiece(api.SectorPiece{ Piece: abi.PieceInfo{ Size: abi.PaddedPieceSize(1024), PieceCID: fakePieceCid(t), }, - DealInfo: &api.PieceDealInfo{ - DealID: abi.DealID(43), - DealSchedule: api.DealSchedule{ + DealInfo: &piece.PieceDealInfo{ + PublishCid: cidPtr(fakePieceCid(t)), // pretend this is a valid builtin-market deal + DealID: abi.DealID(43), + DealSchedule: piece.DealSchedule{ StartEpoch: abi.ChainEpoch(80), EndEpoch: longestDealEpochEnd, }, }, - }, + }), } exp, err := policy.Expiration(context.Background(), pieces...) @@ -138,20 +180,21 @@ func TestBasicPolicyIgnoresExistingScheduleIfExpired(t *testing.T) { h: abi.ChainEpoch(55), }, cfg, 0) - pieces := []api.SectorPiece{ - { + pieces := []pipeline.SafeSectorPiece{ + pipeline.SafePiece(api.SectorPiece{ Piece: abi.PieceInfo{ Size: abi.PaddedPieceSize(1024), PieceCID: fakePieceCid(t), }, - DealInfo: &api.PieceDealInfo{ - DealID: abi.DealID(44), - DealSchedule: api.DealSchedule{ + DealInfo: &piece.PieceDealInfo{ + PublishCid: cidPtr(fakePieceCid(t)), // pretend this is a valid builtin-market deal + DealID: abi.DealID(44), + DealSchedule: piece.DealSchedule{ StartEpoch: abi.ChainEpoch(1), EndEpoch: abi.ChainEpoch(10), }, }, - }, + }), } exp, err := pcp.Expiration(context.Background(), pieces...) @@ -170,27 +213,28 @@ func TestMissingDealIsIgnored(t *testing.T) { h: abi.ChainEpoch(55), }, cfg, 0) - pieces := []api.SectorPiece{ - { + pieces := []pipeline.SafeSectorPiece{ + pipeline.SafePiece(api.SectorPiece{ Piece: abi.PieceInfo{ Size: abi.PaddedPieceSize(1024), PieceCID: fakePieceCid(t), }, - DealInfo: &api.PieceDealInfo{ - DealID: abi.DealID(44), - DealSchedule: api.DealSchedule{ + DealInfo: &piece.PieceDealInfo{ + PublishCid: cidPtr(fakePieceCid(t)), // pretend this is a valid builtin-market deal + DealID: abi.DealID(44), + DealSchedule: piece.DealSchedule{ StartEpoch: abi.ChainEpoch(1), EndEpoch: abi.ChainEpoch(547300), }, }, - }, - { + }), + pipeline.SafePiece(api.SectorPiece{ Piece: abi.PieceInfo{ Size: abi.PaddedPieceSize(1024), PieceCID: fakePieceCid(t), }, DealInfo: nil, - }, + }), } exp, err := policy.Expiration(context.Background(), pieces...) diff --git a/storage/pipeline/receive.go b/storage/pipeline/receive.go index 8427eba54..231afbc39 100644 --- a/storage/pipeline/receive.go +++ b/storage/pipeline/receive.go @@ -86,6 +86,11 @@ func (m *Sealing) checkSectorMeta(ctx context.Context, meta api.RemoteSectorMeta return SectorInfo{}, xerrors.Errorf("getting chain head: %w", err) } + nv, err := m.Api.StateNetworkVersion(ctx, ts.Key()) + if err != nil { + return SectorInfo{}, xerrors.Errorf("getting network version: %w", err) + } + var info SectorInfo var validatePoRep bool @@ -217,9 +222,24 @@ func (m *Sealing) checkSectorMeta(ctx context.Context, meta api.RemoteSectorMeta info.State = ReceiveSector info.SectorNumber = meta.Sector.Number - info.Pieces = meta.Pieces + info.Pieces = make([]SafeSectorPiece, len(meta.Pieces)) info.SectorType = meta.Type + for i, piece := range meta.Pieces { + info.Pieces[i] = SafeSectorPiece{ + real: piece, + } + + if !info.Pieces[i].HasDealInfo() { + continue // cc + } + + err := info.Pieces[i].DealInfo().Valid(nv) + if err != nil { + return SectorInfo{}, xerrors.Errorf("piece %d deal info invalid: %w", i, err) + } + } + if meta.RemoteSealingDoneEndpoint != "" { // validate the url if _, err := url.Parse(meta.RemoteSealingDoneEndpoint); err != nil { @@ -229,7 +249,7 @@ func (m *Sealing) checkSectorMeta(ctx context.Context, meta api.RemoteSectorMeta info.RemoteSealingDoneEndpoint = meta.RemoteSealingDoneEndpoint } - if err := checkPieces(ctx, m.maddr, meta.Sector.Number, meta.Pieces, m.Api, false); err != nil { + if err := checkPieces(ctx, m.maddr, meta.Sector.Number, info.Pieces, m.Api, false); err != nil { return SectorInfo{}, xerrors.Errorf("checking pieces: %w", err) } diff --git a/storage/pipeline/sealiface/config.go b/storage/pipeline/sealiface/config.go index e41b143ec..2ac6e0d58 100644 --- a/storage/pipeline/sealiface/config.go +++ b/storage/pipeline/sealiface/config.go @@ -62,4 +62,9 @@ type Config struct { TerminateBatchWait time.Duration UseSyntheticPoRep bool + + RequireActivationSuccess bool + RequireActivationSuccessUpdate bool + RequireNotificationSuccess bool + RequireNotificationSuccessUpdate bool } diff --git a/storage/pipeline/sealing.go b/storage/pipeline/sealing.go index 936bd8b39..75791fae8 100644 --- a/storage/pipeline/sealing.go +++ b/storage/pipeline/sealing.go @@ -5,6 +5,7 @@ import ( "sync" "time" + blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" "github.com/ipfs/go-datastore" "github.com/ipfs/go-datastore/namespace" @@ -25,12 +26,15 @@ import ( "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/chain/actors/builtin/miner" lminer "github.com/filecoin-project/lotus/chain/actors/builtin/miner" + "github.com/filecoin-project/lotus/chain/actors/builtin/verifreg" "github.com/filecoin-project/lotus/chain/events" "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/journal" + "github.com/filecoin-project/lotus/lib/result" "github.com/filecoin-project/lotus/node/config" "github.com/filecoin-project/lotus/node/modules/dtypes" "github.com/filecoin-project/lotus/storage/ctladdr" + "github.com/filecoin-project/lotus/storage/pipeline/piece" "github.com/filecoin-project/lotus/storage/pipeline/sealiface" "github.com/filecoin-project/lotus/storage/sealer" "github.com/filecoin-project/lotus/storage/sealer/storiface" @@ -49,7 +53,6 @@ type SealingAPI interface { StateSearchMsg(ctx context.Context, from types.TipSetKey, msg cid.Cid, limit abi.ChainEpoch, allowReplaced bool) (*api.MsgLookup, error) StateSectorPreCommitInfo(ctx context.Context, maddr address.Address, sectorNumber abi.SectorNumber, tsk types.TipSetKey) (*miner.SectorPreCommitOnChainInfo, error) - StateComputeDataCID(ctx context.Context, maddr address.Address, sectorType abi.RegisteredSealProof, deals []abi.DealID, tsk types.TipSetKey) (cid.Cid, error) StateSectorGetInfo(ctx context.Context, maddr address.Address, sectorNumber abi.SectorNumber, tsk types.TipSetKey) (*miner.SectorOnChainInfo, error) StateSectorPartition(ctx context.Context, maddr address.Address, sectorNumber abi.SectorNumber, tsk types.TipSetKey) (*lminer.SectorLocation, error) StateLookupID(context.Context, address.Address, types.TipSetKey) (address.Address, error) @@ -72,6 +75,13 @@ type SealingAPI interface { ChainReadObj(context.Context, cid.Cid) ([]byte, error) StateMinerAllocated(context.Context, address.Address, types.TipSetKey) (*bitfield.BitField, error) StateGetAllocationForPendingDeal(ctx context.Context, dealId abi.DealID, tsk types.TipSetKey) (*verifregtypes.Allocation, error) + StateGetAllocationIdForPendingDeal(ctx context.Context, dealId abi.DealID, tsk types.TipSetKey) (verifreg.AllocationId, error) + StateGetAllocation(ctx context.Context, clientAddr address.Address, allocationId verifregtypes.AllocationId, tsk types.TipSetKey) (*verifregtypes.Allocation, error) + + StateGetActor(ctx context.Context, actor address.Address, tsk types.TipSetKey) (*types.Actor, error) + StateVMCirculatingSupplyInternal(ctx context.Context, tsk types.TipSetKey) (api.CirculatingSupply, error) + ChainHasObj(ctx context.Context, c cid.Cid) (bool, error) + ChainPutObj(ctx context.Context, block blocks.Block) error // Address selector WalletBalance(context.Context, address.Address) (types.BigInt, error) @@ -110,8 +120,8 @@ type Sealing struct { inputLk sync.Mutex openSectors map[abi.SectorID]*openSector sectorTimers map[abi.SectorID]*time.Timer - pendingPieces map[cid.Cid]*pendingPiece - assignedPieces map[abi.SectorID][]cid.Cid + pendingPieces map[piece.PieceKey]*pendingPiece + assignedPieces map[abi.SectorID][]piece.PieceKey nextDealSector *abi.SectorNumber // used to prevent a race where we could create a new sector more than once available map[abi.SectorID]struct{} @@ -139,16 +149,16 @@ type openSector struct { number abi.SectorNumber ccUpdate bool - maybeAccept func(cid.Cid) error // called with inputLk + maybeAccept func(key piece.PieceKey) error // called with inputLk } func (o *openSector) checkDealAssignable(piece *pendingPiece, expF expFn) (bool, error) { log := log.With( "sector", o.number, - "deal", piece.deal.DealID, - "dealEnd", piece.deal.DealProposal.EndEpoch, - "dealStart", piece.deal.DealProposal.StartEpoch, + "piece", piece.deal.String(), + "dealEnd", result.Wrap(piece.deal.EndEpoch()), + "dealStart", result.Wrap(piece.deal.StartEpoch()), "dealClaimEnd", piece.claimTerms.claimTermEnd, "lastAssignedDealEnd", o.lastDealEnd, @@ -181,7 +191,12 @@ func (o *openSector) checkDealAssignable(piece *pendingPiece, expF expFn) (bool, return false, nil } - if sectorExpiration < piece.deal.DealProposal.EndEpoch { + endEpoch, err := piece.deal.EndEpoch() + if err != nil { + return false, xerrors.Errorf("failed to get end epoch: %w", err) + } + + if sectorExpiration < endEpoch { log.Debugw("deal not assignable to sector", "reason", "sector expiration less than deal expiration") return false, nil } @@ -205,7 +220,7 @@ type pendingPiece struct { resp *pieceAcceptResp size abi.UnpaddedPieceSize - deal api.PieceDealInfo + deal UniversalPieceInfo claimTerms pieceClaimBounds @@ -215,10 +230,10 @@ type pendingPiece struct { accepted func(abi.SectorNumber, abi.UnpaddedPieceSize, error) } -func New(mctx context.Context, api SealingAPI, fc config.MinerFeeConfig, events Events, maddr address.Address, ds datastore.Batching, sealer sealer.SectorManager, verif storiface.Verifier, prov storiface.Prover, pcp PreCommitPolicy, gc dtypes.GetSealingConfigFunc, journal journal.Journal, addrSel AddressSelector) *Sealing { +func New(mctx context.Context, sapi SealingAPI, fc config.MinerFeeConfig, events Events, maddr address.Address, ds datastore.Batching, sealer sealer.SectorManager, verif storiface.Verifier, prov storiface.Prover, pcp PreCommitPolicy, gc dtypes.GetSealingConfigFunc, journal journal.Journal, addrSel AddressSelector) *Sealing { s := &Sealing{ - Api: api, - DealInfo: &CurrentDealInfoManager{api}, + Api: sapi, + DealInfo: &CurrentDealInfoManager{sapi}, ds: ds, @@ -232,8 +247,8 @@ func New(mctx context.Context, api SealingAPI, fc config.MinerFeeConfig, events openSectors: map[abi.SectorID]*openSector{}, sectorTimers: map[abi.SectorID]*time.Timer{}, - pendingPieces: map[cid.Cid]*pendingPiece{}, - assignedPieces: map[abi.SectorID][]cid.Cid{}, + pendingPieces: map[piece.PieceKey]*pendingPiece{}, + assignedPieces: map[abi.SectorID][]piece.PieceKey{}, available: map[abi.SectorID]struct{}{}, @@ -242,9 +257,9 @@ func New(mctx context.Context, api SealingAPI, fc config.MinerFeeConfig, events addrSel: addrSel, - terminator: NewTerminationBatcher(mctx, maddr, api, addrSel, fc, gc), - precommiter: NewPreCommitBatcher(mctx, maddr, api, addrSel, fc, gc), - commiter: NewCommitBatcher(mctx, maddr, api, addrSel, fc, gc, prov), + terminator: NewTerminationBatcher(mctx, maddr, sapi, addrSel, fc, gc), + precommiter: NewPreCommitBatcher(mctx, maddr, sapi, addrSel, fc, gc), + commiter: NewCommitBatcher(mctx, maddr, sapi, addrSel, fc, gc, prov), getConfig: gc, diff --git a/storage/pipeline/states_failed.go b/storage/pipeline/states_failed.go index 3323c4c9b..3e4ea4dde 100644 --- a/storage/pipeline/states_failed.go +++ b/storage/pipeline/states_failed.go @@ -235,7 +235,7 @@ func (m *Sealing) handleSubmitReplicaUpdateFailed(ctx statemachine.Context, sect return nil } - if err := checkReplicaUpdate(ctx.Context(), m.maddr, sector, ts.Key(), m.Api); err != nil { + if err := checkReplicaUpdate(ctx.Context(), m.maddr, sector, m.Api); err != nil { switch err.(type) { case *ErrApi: log.Errorf("handleSubmitReplicaUpdateFailed: api error, not proceeding: %+v", err) @@ -265,7 +265,7 @@ func (m *Sealing) handleSubmitReplicaUpdateFailed(ctx statemachine.Context, sect } if !active { err := xerrors.Errorf("sector marked for upgrade %d no longer active, aborting upgrade", sector.SectorNumber) - log.Errorf(err.Error()) + log.Errorf("%s", err) return ctx.Send(SectorAbortUpgrade{err}) } @@ -466,7 +466,7 @@ func (m *Sealing) handleAbortUpgrade(ctx statemachine.Context, sector SectorInfo // failWith is a mutator or global mutator func (m *Sealing) handleRecoverDealIDsOrFailWith(ctx statemachine.Context, sector SectorInfo, failWith interface{}) error { - toFix, paddingPieces, err := recoveryPiecesToFix(ctx.Context(), m.Api, sector, m.maddr) + toFix, nonBuiltinMarketPieces, err := recoveryPiecesToFix(ctx.Context(), m.Api, sector, m.maddr) if err != nil { return err } @@ -478,33 +478,35 @@ func (m *Sealing) handleRecoverDealIDsOrFailWith(ctx statemachine.Context, secto updates := map[int]abi.DealID{} for _, i := range toFix { + // note: all toFix pieces are builtin-market pieces + p := sector.Pieces[i] - if p.DealInfo.PublishCid == nil { + if p.Impl().PublishCid == nil { // TODO: check if we are in an early enough state try to remove this piece - log.Errorf("can't fix sector deals: piece %d (of %d) of sector %d has nil DealInfo.PublishCid (refers to deal %d)", i, len(sector.Pieces), sector.SectorNumber, p.DealInfo.DealID) + log.Errorf("can't fix sector deals: piece %d (of %d) of sector %d has nil DealInfo.PublishCid (refers to deal %d)", i, len(sector.Pieces), sector.SectorNumber, p.Impl().DealID) // Not much to do here (and this can only happen for old spacerace sectors) return ctx.Send(failWith) } var dp *market.DealProposal - if p.DealInfo.DealProposal != nil { - mdp := *p.DealInfo.DealProposal + if p.Impl().DealProposal != nil { + mdp := *p.Impl().DealProposal dp = &mdp } - res, err := m.DealInfo.GetCurrentDealInfo(ctx.Context(), ts.Key(), dp, *p.DealInfo.PublishCid) + res, err := m.DealInfo.GetCurrentDealInfo(ctx.Context(), ts.Key(), dp, *p.Impl().PublishCid) if err != nil { failed[i] = xerrors.Errorf("getting current deal info for piece %d: %w", i, err) continue } if res.MarketDeal == nil { - failed[i] = xerrors.Errorf("nil market deal (%d,%d,%d,%s)", i, sector.SectorNumber, p.DealInfo.DealID, p.Piece.PieceCID) + failed[i] = xerrors.Errorf("nil market deal (%d,%d,%d,%s)", i, sector.SectorNumber, p.Impl().DealID, p.Impl().DealProposal.PieceCID) continue } - if res.MarketDeal.Proposal.PieceCID != p.Piece.PieceCID { - failed[i] = xerrors.Errorf("recovered piece (%d) deal in sector %d (dealid %d) has different PieceCID %s != %s", i, sector.SectorNumber, p.DealInfo.DealID, p.Piece.PieceCID, res.MarketDeal.Proposal.PieceCID) + if res.MarketDeal.Proposal.PieceCID != p.PieceCID() { + failed[i] = xerrors.Errorf("recovered piece (%d) deal in sector %d (dealid %d) has different PieceCID %s != %s", i, sector.SectorNumber, p.Impl().DealID, p.Impl().DealProposal.PieceCID, res.MarketDeal.Proposal.PieceCID) continue } @@ -517,7 +519,7 @@ func (m *Sealing) handleRecoverDealIDsOrFailWith(ctx statemachine.Context, secto merr = multierror.Append(merr, e) } - if len(failed)+paddingPieces == len(sector.Pieces) { + if len(failed)+nonBuiltinMarketPieces == len(sector.Pieces) { log.Errorf("removing sector %d: all deals expired or unrecoverable: %+v", sector.SectorNumber, merr) return ctx.Send(failWith) } @@ -542,6 +544,7 @@ func (m *Sealing) handleSnapDealsRecoverDealIDs(ctx statemachine.Context, sector return m.handleRecoverDealIDsOrFailWith(ctx, sector, SectorAbortUpgrade{xerrors.New("failed recovering deal ids")}) } +// recoveryPiecesToFix returns the list of sector piece indexes to fix, and the number of non-builtin-market pieces func recoveryPiecesToFix(ctx context.Context, api SealingAPI, sector SectorInfo, maddr address.Address) ([]int, int, error) { ts, err := api.ChainHead(ctx) if err != nil { @@ -549,51 +552,68 @@ func recoveryPiecesToFix(ctx context.Context, api SealingAPI, sector SectorInfo, } var toFix []int - paddingPieces := 0 + nonBuiltinMarketPieces := 0 for i, p := range sector.Pieces { - // if no deal is associated with the piece, ensure that we added it as - // filler (i.e. ensure that it has a zero PieceCID) - if p.DealInfo == nil { - exp := zerocomm.ZeroPieceCommitment(p.Piece.Size.Unpadded()) - if !p.Piece.PieceCID.Equals(exp) { - return nil, 0, xerrors.Errorf("sector %d piece %d had non-zero PieceCID %+v", sector.SectorNumber, i, p.Piece.PieceCID) - } - paddingPieces++ - continue - } + i, p := i, p + + err := p.handleDealInfo(handleDealInfoParams{ + FillerHandler: func(info UniversalPieceInfo) error { + // if no deal is associated with the piece, ensure that we added it as + // filler (i.e. ensure that it has a zero PieceCID) + exp := zerocomm.ZeroPieceCommitment(p.Piece().Size.Unpadded()) + if !info.PieceCID().Equals(exp) { + return xerrors.Errorf("sector %d piece %d had non-zero PieceCID %+v", sector.SectorNumber, i, p.Piece().PieceCID) + } + nonBuiltinMarketPieces++ + return nil + }, + BuiltinMarketHandler: func(info UniversalPieceInfo) error { + deal, err := api.StateMarketStorageDeal(ctx, p.DealInfo().Impl().DealID, ts.Key()) + if err != nil { + log.Warnf("getting deal %d for piece %d: %+v", p.DealInfo().Impl().DealID, i, err) + toFix = append(toFix, i) + return nil + } + + if deal.Proposal.Provider != maddr { + log.Warnf("piece %d (of %d) of sector %d refers deal %d with wrong provider: %s != %s", i, len(sector.Pieces), sector.SectorNumber, p.Impl().DealID, deal.Proposal.Provider, maddr) + toFix = append(toFix, i) + return nil + } + + if deal.Proposal.PieceCID != p.Piece().PieceCID { + log.Warnf("piece %d (of %d) of sector %d refers deal %d with wrong PieceCID: %s != %s", i, len(sector.Pieces), sector.SectorNumber, p.Impl().DealID, p.Piece().PieceCID, deal.Proposal.PieceCID) + toFix = append(toFix, i) + return nil + } + + if p.Piece().Size != deal.Proposal.PieceSize { + log.Warnf("piece %d (of %d) of sector %d refers deal %d with different size: %d != %d", i, len(sector.Pieces), sector.SectorNumber, p.Impl().DealID, p.Piece().Size, deal.Proposal.PieceSize) + toFix = append(toFix, i) + return nil + } + + if ts.Height() >= deal.Proposal.StartEpoch { + // TODO: check if we are in an early enough state (before precommit), try to remove the offending pieces + // (tricky as we have to 'defragment' the sector while doing that, and update piece references for retrieval) + return xerrors.Errorf("can't fix sector deals: piece %d (of %d) of sector %d refers expired deal %d - should start at %d, head %d", i, len(sector.Pieces), sector.SectorNumber, p.Impl().DealID, deal.Proposal.StartEpoch, ts.Height()) + } + + return nil + }, + DDOHandler: func(info UniversalPieceInfo) error { + // DDO pieces have no repair strategy + + nonBuiltinMarketPieces++ + return nil + }, + }) - deal, err := api.StateMarketStorageDeal(ctx, p.DealInfo.DealID, ts.Key()) if err != nil { - log.Warnf("getting deal %d for piece %d: %+v", p.DealInfo.DealID, i, err) - toFix = append(toFix, i) - continue - } - - if deal.Proposal.Provider != maddr { - log.Warnf("piece %d (of %d) of sector %d refers deal %d with wrong provider: %s != %s", i, len(sector.Pieces), sector.SectorNumber, p.DealInfo.DealID, deal.Proposal.Provider, maddr) - toFix = append(toFix, i) - continue - } - - if deal.Proposal.PieceCID != p.Piece.PieceCID { - log.Warnf("piece %d (of %d) of sector %d refers deal %d with wrong PieceCID: %s != %s", i, len(sector.Pieces), sector.SectorNumber, p.DealInfo.DealID, p.Piece.PieceCID, deal.Proposal.PieceCID) - toFix = append(toFix, i) - continue - } - - if p.Piece.Size != deal.Proposal.PieceSize { - log.Warnf("piece %d (of %d) of sector %d refers deal %d with different size: %d != %d", i, len(sector.Pieces), sector.SectorNumber, p.DealInfo.DealID, p.Piece.Size, deal.Proposal.PieceSize) - toFix = append(toFix, i) - continue - } - - if ts.Height() >= deal.Proposal.StartEpoch { - // TODO: check if we are in an early enough state (before precommit), try to remove the offending pieces - // (tricky as we have to 'defragment' the sector while doing that, and update piece references for retrieval) - return nil, 0, xerrors.Errorf("can't fix sector deals: piece %d (of %d) of sector %d refers expired deal %d - should start at %d, head %d", i, len(sector.Pieces), sector.SectorNumber, p.DealInfo.DealID, deal.Proposal.StartEpoch, ts.Height()) + return nil, 0, xerrors.Errorf("checking piece %d: %w", i, err) } } - return toFix, paddingPieces, nil + return toFix, nonBuiltinMarketPieces, nil } diff --git a/storage/pipeline/states_failed_test.go b/storage/pipeline/states_failed_test.go index f6846c8f5..bc658d59b 100644 --- a/storage/pipeline/states_failed_test.go +++ b/storage/pipeline/states_failed_test.go @@ -23,6 +23,7 @@ import ( "github.com/filecoin-project/lotus/chain/types" pipeline "github.com/filecoin-project/lotus/storage/pipeline" "github.com/filecoin-project/lotus/storage/pipeline/mocks" + "github.com/filecoin-project/lotus/storage/pipeline/piece" ) func TestStateRecoverDealIDs(t *testing.T) { @@ -76,16 +77,16 @@ func TestStateRecoverDealIDs(t *testing.T) { // TODO sctx should satisfy an interface so it can be useable for mocking. This will fail because we are passing in an empty context now to get this to build. // https://github.com/filecoin-project/lotus/issues/7867 err := fakeSealing.HandleRecoverDealIDs(statemachine.Context{}, pipeline.SectorInfo{ - Pieces: []api2.SectorPiece{ - { - DealInfo: &api2.PieceDealInfo{ + Pieces: []pipeline.SafeSectorPiece{ + pipeline.SafePiece(api2.SectorPiece{ + DealInfo: &piece.PieceDealInfo{ DealID: dealId, PublishCid: &pc, }, Piece: abi.PieceInfo{ PieceCID: idCid("oldPieceCID"), }, - }, + }), }, }) require.NoError(t, err) diff --git a/storage/pipeline/states_replica_update.go b/storage/pipeline/states_replica_update.go index 6717f49a6..380078e75 100644 --- a/storage/pipeline/states_replica_update.go +++ b/storage/pipeline/states_replica_update.go @@ -22,7 +22,7 @@ import ( func (m *Sealing) handleReplicaUpdate(ctx statemachine.Context, sector SectorInfo) error { // if the sector ended up not having any deals, abort the upgrade - if !sector.hasDeals() { + if !sector.hasData() { return ctx.Send(SectorAbortUpgrade{xerrors.New("sector had no deals")}) } @@ -58,7 +58,7 @@ func (m *Sealing) handleProveReplicaUpdate(ctx statemachine.Context, sector Sect } if !active { err := xerrors.Errorf("sector marked for upgrade %d no longer active, aborting upgrade", sector.SectorNumber) - log.Errorf(err.Error()) + log.Errorf("%s", err) return ctx.Send(SectorAbortUpgrade{err}) } @@ -82,14 +82,13 @@ func (m *Sealing) handleProveReplicaUpdate(ctx statemachine.Context, sector Sect } func (m *Sealing) handleSubmitReplicaUpdate(ctx statemachine.Context, sector SectorInfo) error { - ts, err := m.Api.ChainHead(ctx.Context()) if err != nil { log.Errorf("handleSubmitReplicaUpdate: api error, not proceeding: %+v", err) return nil } - if err := checkReplicaUpdate(ctx.Context(), m.maddr, sector, ts.Key(), m.Api); err != nil { + if err := checkReplicaUpdate(ctx.Context(), m.maddr, sector, m.Api); err != nil { return ctx.Send(SectorSubmitReplicaUpdateFailed{}) } @@ -114,24 +113,8 @@ func (m *Sealing) handleSubmitReplicaUpdate(ctx statemachine.Context, sector Sec log.Errorf("failed to get update proof type from seal proof: %+v", err) return ctx.Send(SectorSubmitReplicaUpdateFailed{}) } - enc := new(bytes.Buffer) - params := &miner.ProveReplicaUpdatesParams{ - Updates: []miner.ReplicaUpdate{ - { - SectorID: sector.SectorNumber, - Deadline: sl.Deadline, - Partition: sl.Partition, - NewSealedSectorCID: *sector.UpdateSealed, - Deals: sector.dealIDs(), - UpdateProofType: updateProof, - ReplicaProof: sector.ReplicaUpdateProof, - }, - }, - } - if err := params.MarshalCBOR(enc); err != nil { - log.Errorf("failed to serialize update replica params: %w", err) - return ctx.Send(SectorSubmitReplicaUpdateFailed{}) - } + + // figure out from address and collateral cfg, err := m.getConfig() if err != nil { @@ -140,34 +123,24 @@ func (m *Sealing) handleSubmitReplicaUpdate(ctx statemachine.Context, sector Sec onChainInfo, err := m.Api.StateSectorGetInfo(ctx.Context(), m.maddr, sector.SectorNumber, ts.Key()) if err != nil { - log.Errorf("handleSubmitReplicaUpdate: api error, not proceeding: %+v", err) - return nil + log.Errorf("failed to get sector info: %+v", err) + return ctx.Send(SectorSubmitReplicaUpdateFailed{}) } if onChainInfo == nil { - return xerrors.Errorf("sector not found %d", sector.SectorNumber) + log.Errorw("on chain info was nil", "sector", sector.SectorNumber) + return ctx.Send(SectorSubmitReplicaUpdateFailed{}) } - sp, err := m.currentSealProof(ctx.Context()) + weightUpdate, err := m.sectorWeight(ctx.Context(), sector, onChainInfo.Expiration) if err != nil { - log.Errorf("sealer failed to return current seal proof not proceeding: %+v", err) - return nil - } - virtualPCI := miner.SectorPreCommitInfo{ - SealProof: sp, - SectorNumber: sector.SectorNumber, - SealedCID: *sector.UpdateSealed, - //SealRandEpoch: 0, - DealIDs: sector.dealIDs(), - Expiration: onChainInfo.Expiration, - //ReplaceCapacity: false, - //ReplaceSectorDeadline: 0, - //ReplaceSectorPartition: 0, - //ReplaceSectorNumber: 0, + log.Errorf("failed to get sector weight: %+v", err) + return ctx.Send(SectorSubmitReplicaUpdateFailed{}) } - collateral, err := m.Api.StateMinerInitialPledgeCollateral(ctx.Context(), m.maddr, virtualPCI, ts.Key()) + collateral, err := m.pledgeForPower(ctx.Context(), weightUpdate) if err != nil { - return xerrors.Errorf("getting initial pledge collateral: %w", err) + log.Errorf("failed to get pledge for power: %+v", err) + return ctx.Send(SectorSubmitReplicaUpdateFailed{}) } collateral = big.Sub(collateral, onChainInfo.InitialPledge) @@ -194,13 +167,81 @@ func (m *Sealing) handleSubmitReplicaUpdate(ctx statemachine.Context, sector Sec log.Errorf("no good address to send replica update message from: %+v", err) return ctx.Send(SectorSubmitReplicaUpdateFailed{}) } - mcid, err := sendMsg(ctx.Context(), m.Api, from, m.maddr, builtin.MethodsMiner.ProveReplicaUpdates, collateral, big.Int(m.feeCfg.MaxCommitGasFee), enc.Bytes()) + + // figure out message type + + pams, deals, err := m.processPieces(ctx.Context(), sector) + if err != nil { + log.Errorf("failed to process pieces: %+v", err) + return ctx.Send(SectorSubmitReplicaUpdateFailed{}) + } + + if len(pams) > 0 { + // PRU3 + + params := &miner.ProveReplicaUpdates3Params{ + SectorUpdates: []miner.SectorUpdateManifest{ + { + Sector: sector.SectorNumber, + Deadline: sl.Deadline, + Partition: sl.Partition, + NewSealedCID: *sector.UpdateSealed, + Pieces: pams, + }, + }, + SectorProofs: [][]byte{sector.ReplicaUpdateProof}, + UpdateProofsType: updateProof, + //AggregateProof + //AggregateProofType + RequireActivationSuccess: cfg.RequireActivationSuccessUpdate, + RequireNotificationSuccess: cfg.RequireNotificationSuccessUpdate, + } + + enc := new(bytes.Buffer) + if err := params.MarshalCBOR(enc); err != nil { + log.Errorf("failed to serialize update replica params: %w", err) + return ctx.Send(SectorSubmitReplicaUpdateFailed{}) + } + + mcid, err := sendMsg(ctx.Context(), m.Api, from, m.maddr, builtin.MethodsMiner.ProveReplicaUpdates3, collateral, big.Int(m.feeCfg.MaxCommitGasFee), enc.Bytes()) + if err != nil { + log.Errorf("handleSubmitReplicaUpdate: error sending message: %+v", err) + return ctx.Send(SectorSubmitReplicaUpdateFailed{}) + } + + return ctx.Send(SectorReplicaUpdateSubmitted{Message: mcid}) + } + + // PRU2 + params := &miner.ProveReplicaUpdatesParams2{ + Updates: []miner.ReplicaUpdate2{ + { + SectorID: sector.SectorNumber, + Deadline: sl.Deadline, + Partition: sl.Partition, + NewSealedSectorCID: *sector.UpdateSealed, + NewUnsealedSectorCID: *sector.UpdateUnsealed, + UpdateProofType: updateProof, + ReplicaProof: sector.ReplicaUpdateProof, + Deals: deals, + }, + }, + } + + enc := new(bytes.Buffer) + if err := params.MarshalCBOR(enc); err != nil { + log.Errorf("failed to serialize update replica params: %w", err) + return ctx.Send(SectorSubmitReplicaUpdateFailed{}) + } + + mcid, err := sendMsg(ctx.Context(), m.Api, from, m.maddr, builtin.MethodsMiner.ProveReplicaUpdates2, collateral, big.Int(m.feeCfg.MaxCommitGasFee), enc.Bytes()) if err != nil { log.Errorf("handleSubmitReplicaUpdate: error sending message: %+v", err) return ctx.Send(SectorSubmitReplicaUpdateFailed{}) } return ctx.Send(SectorReplicaUpdateSubmitted{Message: mcid}) + } func (m *Sealing) handleWaitMutable(ctx statemachine.Context, sector SectorInfo) error { diff --git a/storage/pipeline/states_sealing.go b/storage/pipeline/states_sealing.go index 5c91161ef..aef394789 100644 --- a/storage/pipeline/states_sealing.go +++ b/storage/pipeline/states_sealing.go @@ -12,11 +12,16 @@ import ( "github.com/ipfs/go-cid" "golang.org/x/xerrors" + "github.com/filecoin-project/go-address" + cborutil "github.com/filecoin-project/go-cbor-util" "github.com/filecoin-project/go-commp-utils/zerocomm" "github.com/filecoin-project/go-state-types/abi" actorstypes "github.com/filecoin-project/go-state-types/actors" "github.com/filecoin-project/go-state-types/big" "github.com/filecoin-project/go-state-types/builtin" + miner2 "github.com/filecoin-project/go-state-types/builtin/v13/miner" + verifreg13 "github.com/filecoin-project/go-state-types/builtin/v13/verifreg" + "github.com/filecoin-project/go-state-types/builtin/v9/verifreg" "github.com/filecoin-project/go-state-types/crypto" "github.com/filecoin-project/go-state-types/exitcode" "github.com/filecoin-project/go-state-types/network" @@ -25,6 +30,7 @@ import ( "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/build" + "github.com/filecoin-project/lotus/chain/actors/builtin/market" "github.com/filecoin-project/lotus/chain/actors/builtin/miner" "github.com/filecoin-project/lotus/chain/actors/policy" "github.com/filecoin-project/lotus/chain/types" @@ -32,6 +38,8 @@ import ( "github.com/filecoin-project/lotus/storage/sealer/storiface" ) +const MinDDONetworkVersion = network.Version22 + var DealSectorPriority = 1024 var MaxTicketAge = policy.MaxPreCommitRandomnessLookback @@ -59,7 +67,7 @@ func (m *Sealing) handlePacking(ctx statemachine.Context, sector SectorInfo) err m.cleanupAssignedDeals(sector) // if this is a snapdeals sector, but it ended up not having any deals, abort the upgrade - if sector.State == SnapDealsPacking && !sector.hasDeals() { + if sector.State == SnapDealsPacking && !sector.hasData() { return ctx.Send(SectorAbortUpgrade{xerrors.New("sector had no deals")}) } @@ -67,7 +75,7 @@ func (m *Sealing) handlePacking(ctx statemachine.Context, sector SectorInfo) err var allocated abi.UnpaddedPieceSize for _, piece := range sector.Pieces { - allocated += piece.Piece.Size.Unpadded() + allocated += piece.Piece().Size.Unpadded() } ssize, err := sector.SectorType.SectorSize() @@ -417,11 +425,47 @@ func (m *Sealing) preCommitInfo(ctx statemachine.Context, sector SectorInfo) (*m SealedCID: *sector.CommR, SealRandEpoch: sector.TicketEpoch, - DealIDs: sector.dealIDs(), } - if sector.hasDeals() { + if sector.hasData() { + // only CC sectors don't have UnsealedCID params.UnsealedCid = sector.CommD + + // true when the sector has non-builtin-marked data + sectorIsDDO := false + + for _, piece := range sector.Pieces { + err := piece.handleDealInfo(handleDealInfoParams{ + FillerHandler: func(info UniversalPieceInfo) error { + return nil // ignore + }, + BuiltinMarketHandler: func(info UniversalPieceInfo) error { + if sectorIsDDO { + return nil // will be passed later in the Commit message + } + params.DealIDs = append(params.DealIDs, info.Impl().DealID) + return nil + }, + DDOHandler: func(info UniversalPieceInfo) error { + if nv < MinDDONetworkVersion { + return xerrors.Errorf("DDO sectors are not supported on network version %d", nv) + } + + log.Infow("DDO piece in sector", "sector", sector.SectorNumber, "piece", info.String()) + + sectorIsDDO = true + + // DDO sectors don't carry DealIDs, we will pass those + // deals in the Commit message later + params.DealIDs = nil + return nil + }, + }) + + if err != nil { + return nil, big.Zero(), types.EmptyTSK, xerrors.Errorf("handleDealInfo: %w", err) + } + } } collateral, err := m.Api.StateMinerPreCommitDepositForPower(ctx.Context(), m.maddr, *params, ts.Key()) @@ -572,10 +616,6 @@ func (m *Sealing) handleCommitting(ctx statemachine.Context, sector SectorInfo) return xerrors.Errorf("getting config: %w", err) } - log.Info("scheduling seal proof computation...") - - log.Infof("KOMIT %d %x(%d); %x(%d); %v; r:%s; d:%s", sector.SectorNumber, sector.TicketValue, sector.TicketEpoch, sector.SeedValue, sector.SeedEpoch, sector.pieceInfos(), sector.CommR, sector.CommD) - if sector.CommD == nil || sector.CommR == nil { return ctx.Send(SectorCommitFailed{xerrors.Errorf("sector had nil commR or commD")}) } @@ -700,6 +740,8 @@ func (m *Sealing) handleCommitting(ctx statemachine.Context, sector SectorInfo) } func (m *Sealing) handleSubmitCommit(ctx statemachine.Context, sector SectorInfo) error { + // TODO: Deprecate this path, always go through batcher, just respect the AggregateCommits config in there + cfg, err := m.getConfig() if err != nil { return xerrors.Errorf("getting config: %w", err) @@ -783,11 +825,113 @@ func (m *Sealing) handleSubmitCommit(ctx statemachine.Context, sector SectorInfo }) } +// processPieces returns either: +// - a list of piece activation manifests +// - a list of deal IDs, if all non-filler pieces are deal-id pieces +func (m *Sealing) processPieces(ctx context.Context, sector SectorInfo) ([]miner.PieceActivationManifest, []abi.DealID, error) { + pams := make([]miner.PieceActivationManifest, 0, len(sector.Pieces)) + dealIDs := make([]abi.DealID, 0, len(sector.Pieces)) + var hasDDO bool + + for _, piece := range sector.Pieces { + piece := piece + + // first figure out if this is a ddo sector + err := piece.handleDealInfo(handleDealInfoParams{ + FillerHandler: func(info UniversalPieceInfo) error { + // Fillers are implicit (todo review: Are they??) + return nil + }, + BuiltinMarketHandler: func(info UniversalPieceInfo) error { + return nil + }, + DDOHandler: func(info UniversalPieceInfo) error { + hasDDO = true + return nil + }, + }) + if err != nil { + return nil, nil, xerrors.Errorf("handleDealInfo: %w", err) + } + } + for _, piece := range sector.Pieces { + piece := piece + + err := piece.handleDealInfo(handleDealInfoParams{ + FillerHandler: func(info UniversalPieceInfo) error { + // Fillers are implicit (todo review: Are they??) + return nil + }, + BuiltinMarketHandler: func(info UniversalPieceInfo) error { + if hasDDO { + alloc, err := m.Api.StateGetAllocationIdForPendingDeal(ctx, info.Impl().DealID, types.EmptyTSK) + if err != nil { + return xerrors.Errorf("getting allocation for deal %d: %w", info.Impl().DealID, err) + } + clid, err := m.Api.StateLookupID(ctx, info.Impl().DealProposal.Client, types.EmptyTSK) + if err != nil { + return xerrors.Errorf("getting client address for deal %d: %w", info.Impl().DealID, err) + } + + clientId, err := address.IDFromAddress(clid) + if err != nil { + return xerrors.Errorf("getting client address for deal %d: %w", info.Impl().DealID, err) + } + + var vac *miner2.VerifiedAllocationKey + if alloc != verifreg.NoAllocationID { + vac = &miner2.VerifiedAllocationKey{ + Client: abi.ActorID(clientId), + ID: verifreg13.AllocationId(alloc), + } + } + + payload, err := cborutil.Dump(info.Impl().DealID) + if err != nil { + return xerrors.Errorf("serializing deal id: %w", err) + } + + pams = append(pams, miner.PieceActivationManifest{ + CID: piece.Piece().PieceCID, + Size: piece.Piece().Size, + VerifiedAllocationKey: vac, + Notify: []miner2.DataActivationNotification{ + { + Address: market.Address, + Payload: payload, + }, + }, + }) + + return nil + } + + dealIDs = append(dealIDs, info.Impl().DealID) + return nil + }, + DDOHandler: func(info UniversalPieceInfo) error { + pams = append(pams, *piece.Impl().PieceActivationManifest) + return nil + }, + }) + if err != nil { + return nil, nil, xerrors.Errorf("handleDealInfo: %w", err) + } + } + + return pams, dealIDs, nil +} + func (m *Sealing) handleSubmitCommitAggregate(ctx statemachine.Context, sector SectorInfo) error { if sector.CommD == nil || sector.CommR == nil { return ctx.Send(SectorCommitFailed{xerrors.Errorf("sector had nil commR or commD")}) } + pams, dealIDs, err := m.processPieces(ctx.Context(), sector) + if err != nil { + return err + } + res, err := m.commiter.AddCommit(ctx.Context(), sector, AggregateInput{ Info: proof.AggregateSealVerifyInfo{ Number: sector.SectorNumber, @@ -796,8 +940,14 @@ func (m *Sealing) handleSubmitCommitAggregate(ctx statemachine.Context, sector S SealedCID: *sector.CommR, UnsealedCID: *sector.CommD, }, - Proof: sector.Proof, // todo: this correct?? + Proof: sector.Proof, Spt: sector.SectorType, + + ActivationManifest: miner2.SectorActivationManifest{ + SectorNumber: sector.SectorNumber, + Pieces: pams, + }, + DealIDPrecommit: len(dealIDs) > 0, }) if err != nil || res.Error != "" { @@ -875,7 +1025,7 @@ func (m *Sealing) handleFinalizeSector(ctx statemachine.Context, sector SectorIn return ctx.Send(SectorFinalizeFailed{xerrors.Errorf("finalize sector: %w", err)}) } - if cfg.MakeCCSectorsAvailable && !sector.hasDeals() { + if cfg.MakeCCSectorsAvailable && !sector.hasData() { return ctx.Send(SectorFinalizedAvailable{}) } return ctx.Send(SectorFinalized{}) diff --git a/storage/pipeline/types.go b/storage/pipeline/types.go index e752eb2b9..48ae60546 100644 --- a/storage/pipeline/types.go +++ b/storage/pipeline/types.go @@ -2,14 +2,20 @@ package sealing import ( "context" + "encoding/json" + "io" "github.com/ipfs/go-cid" + "golang.org/x/xerrors" "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/big" + "github.com/filecoin-project/go-state-types/network" "github.com/filecoin-project/lotus/api" + "github.com/filecoin-project/lotus/chain/actors/builtin/verifreg" "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/storage/pipeline/piece" "github.com/filecoin-project/lotus/storage/sealer" "github.com/filecoin-project/lotus/storage/sealer/storiface" ) @@ -41,6 +47,20 @@ const ( RetCommitFailed = ReturnState(CommitFailed) ) +type UniversalPieceInfo interface { + Impl() piece.PieceDealInfo + String() string + Key() piece.PieceKey + + Valid(nv network.Version) error + StartEpoch() (abi.ChainEpoch, error) + EndEpoch() (abi.ChainEpoch, error) + PieceCID() cid.Cid + KeepUnsealedRequested() bool + + GetAllocation(ctx context.Context, aapi piece.AllocationAPI, tsk types.TipSetKey) (*verifreg.Allocation, error) +} + type SectorInfo struct { State SectorState SectorNumber abi.SectorNumber @@ -49,7 +69,7 @@ type SectorInfo struct { // Packing CreationTime int64 // unix seconds - Pieces []api.SectorPiece + Pieces []SafeSectorPiece // PreCommit1 TicketValue abi.SealRandomness @@ -79,7 +99,7 @@ type SectorInfo struct { // CCUpdate CCUpdate bool - CCPieces []api.SectorPiece + CCPieces []SafeSectorPiece UpdateSealed *cid.Cid UpdateUnsealed *cid.Cid ReplicaUpdateProof storiface.ReplicaUpdateProof @@ -113,18 +133,19 @@ type SectorInfo struct { func (t *SectorInfo) pieceInfos() []abi.PieceInfo { out := make([]abi.PieceInfo, len(t.Pieces)) for i, p := range t.Pieces { - out[i] = p.Piece + out[i] = p.Piece() } return out } -func (t *SectorInfo) dealIDs() []abi.DealID { - out := make([]abi.DealID, 0, len(t.Pieces)) - for _, p := range t.Pieces { - if p.DealInfo == nil { +func (t *SectorInfo) nonPaddingPieceInfos() []abi.PieceInfo { + out := make([]abi.PieceInfo, len(t.Pieces)) + for i, p := range t.Pieces { + if !p.HasDealInfo() { continue } - out = append(out, p.DealInfo.DealID) + + out[i] = p.Piece() } return out } @@ -132,14 +153,14 @@ func (t *SectorInfo) dealIDs() []abi.DealID { func (t *SectorInfo) existingPieceSizes() []abi.UnpaddedPieceSize { out := make([]abi.UnpaddedPieceSize, len(t.Pieces)) for i, p := range t.Pieces { - out[i] = p.Piece.Size.Unpadded() + out[i] = p.Piece().Size.Unpadded() } return out } -func (t *SectorInfo) hasDeals() bool { +func (t *SectorInfo) hasData() bool { for _, piece := range t.Pieces { - if piece.DealInfo != nil { + if piece.HasDealInfo() { return true } } @@ -151,7 +172,7 @@ func (t *SectorInfo) sealingCtx(ctx context.Context) context.Context { // TODO: can also take start epoch into account to give priority to sectors // we need sealed sooner - if t.hasDeals() { + if t.hasData() { return sealer.WithPriority(ctx, DealSectorPriority) } @@ -160,19 +181,19 @@ func (t *SectorInfo) sealingCtx(ctx context.Context) context.Context { // Returns list of offset/length tuples of sector data ranges which clients // requested to keep unsealed -func (t *SectorInfo) keepUnsealedRanges(pieces []api.SectorPiece, invert, alwaysKeep bool) []storiface.Range { +func (t *SectorInfo) keepUnsealedRanges(pieces []SafeSectorPiece, invert, alwaysKeep bool) []storiface.Range { var out []storiface.Range var at abi.UnpaddedPieceSize for _, piece := range pieces { - psize := piece.Piece.Size.Unpadded() + psize := piece.Piece().Size.Unpadded() at += psize - if piece.DealInfo == nil { + if !piece.HasDealInfo() { continue } - keep := piece.DealInfo.KeepUnsealed || alwaysKeep + keep := piece.DealInfo().KeepUnsealedRequested() || alwaysKeep if keep == invert { continue @@ -195,3 +216,110 @@ type SealingStateEvt struct { After SectorState Error string } + +// SafeSectorPiece is a wrapper around SectorPiece which makes it hard to misuse +// especially by making it hard to access raw Deal / DDO info +type SafeSectorPiece struct { + real api.SectorPiece +} + +func SafePiece(piece api.SectorPiece) SafeSectorPiece { + return SafeSectorPiece{piece} +} + +var _ UniversalPieceInfo = &SafeSectorPiece{} + +func (sp *SafeSectorPiece) Piece() abi.PieceInfo { + return sp.real.Piece +} + +func (sp *SafeSectorPiece) HasDealInfo() bool { + return sp.real.DealInfo != nil +} + +func (sp *SafeSectorPiece) DealInfo() UniversalPieceInfo { + return sp.real.DealInfo +} + +// cbor passthrough +func (sp *SafeSectorPiece) UnmarshalCBOR(r io.Reader) (err error) { + return sp.real.UnmarshalCBOR(r) +} + +func (sp *SafeSectorPiece) MarshalCBOR(w io.Writer) error { + return sp.real.MarshalCBOR(w) +} + +// json passthrough +func (sp *SafeSectorPiece) UnmarshalJSON(b []byte) error { + return json.Unmarshal(b, &sp.real) +} + +func (sp *SafeSectorPiece) MarshalJSON() ([]byte, error) { + return json.Marshal(sp.real) +} + +type handleDealInfoParams struct { + FillerHandler func(UniversalPieceInfo) error + BuiltinMarketHandler func(UniversalPieceInfo) error + DDOHandler func(UniversalPieceInfo) error +} + +func (sp *SafeSectorPiece) handleDealInfo(params handleDealInfoParams) error { + if !sp.HasDealInfo() { + if params.FillerHandler == nil { + return xerrors.Errorf("FillerHandler is not provided") + } + return params.FillerHandler(sp) + } + + if sp.real.DealInfo.PublishCid != nil { + if params.BuiltinMarketHandler == nil { + return xerrors.Errorf("BuiltinMarketHandler is not provided") + } + return params.BuiltinMarketHandler(sp) + } + + if params.DDOHandler == nil { + return xerrors.Errorf("DDOHandler is not provided") + } + return params.DDOHandler(sp) +} + +// SectorPiece Proxy + +func (sp *SafeSectorPiece) Impl() piece.PieceDealInfo { + return sp.real.DealInfo.Impl() +} + +func (sp *SafeSectorPiece) String() string { + return sp.real.DealInfo.String() +} + +func (sp *SafeSectorPiece) Key() piece.PieceKey { + return sp.real.DealInfo.Key() +} + +func (sp *SafeSectorPiece) Valid(nv network.Version) error { + return sp.real.DealInfo.Valid(nv) +} + +func (sp *SafeSectorPiece) StartEpoch() (abi.ChainEpoch, error) { + return sp.real.DealInfo.StartEpoch() +} + +func (sp *SafeSectorPiece) EndEpoch() (abi.ChainEpoch, error) { + return sp.real.DealInfo.EndEpoch() +} + +func (sp *SafeSectorPiece) PieceCID() cid.Cid { + return sp.real.DealInfo.PieceCID() +} + +func (sp *SafeSectorPiece) KeepUnsealedRequested() bool { + return sp.real.DealInfo.KeepUnsealedRequested() +} + +func (sp *SafeSectorPiece) GetAllocation(ctx context.Context, aapi piece.AllocationAPI, tsk types.TipSetKey) (*verifreg.Allocation, error) { + return sp.real.DealInfo.GetAllocation(ctx, aapi, tsk) +} diff --git a/storage/pipeline/types_test.go b/storage/pipeline/types_test.go index b8fbb113a..d92b68d55 100644 --- a/storage/pipeline/types_test.go +++ b/storage/pipeline/types_test.go @@ -13,6 +13,7 @@ import ( tutils "github.com/filecoin-project/specs-actors/v2/support/testing" "github.com/filecoin-project/lotus/api" + "github.com/filecoin-project/lotus/storage/pipeline/piece" ) func TestSectorInfoSerialization(t *testing.T) { @@ -23,9 +24,9 @@ func TestSectorInfoSerialization(t *testing.T) { t.Fatal(err) } - dealInfo := api.PieceDealInfo{ + dealInfo := piece.PieceDealInfo{ DealID: d, - DealSchedule: api.DealSchedule{ + DealSchedule: piece.DealSchedule{ StartEpoch: 0, EndEpoch: 100, }, @@ -43,13 +44,13 @@ func TestSectorInfoSerialization(t *testing.T) { si := &SectorInfo{ State: "stateful", SectorNumber: 234, - Pieces: []api.SectorPiece{{ + Pieces: []SafeSectorPiece{{real: api.SectorPiece{ Piece: abi.PieceInfo{ Size: 5, PieceCID: dummyCid, }, DealInfo: &dealInfo, - }}, + }}}, CommD: &dummyCid, CommR: nil, Proof: nil, @@ -77,8 +78,8 @@ func TestSectorInfoSerialization(t *testing.T) { assert.Equal(t, si.State, si2.State) assert.Equal(t, si.SectorNumber, si2.SectorNumber) - assert.Equal(t, si.Pieces[0].DealInfo.DealID, si2.Pieces[0].DealInfo.DealID) - assert.Equal(t, si.Pieces[0].DealInfo.DealProposal.PieceCID, si2.Pieces[0].DealInfo.DealProposal.PieceCID) + assert.Equal(t, si.Pieces[0].Impl().DealID, si2.Pieces[0].Impl().DealID) + assert.Equal(t, si.Pieces[0].Impl().DealProposal.PieceCID, si2.Pieces[0].Impl().DealProposal.PieceCID) assert.Equal(t, *si.CommD, *si2.CommD) assert.DeepEqual(t, si.TicketValue, si2.TicketValue) assert.Equal(t, si.TicketEpoch, si2.TicketEpoch) diff --git a/storage/pipeline/upgrade_queue.go b/storage/pipeline/upgrade_queue.go index 9d9e1ca46..5e3392a9f 100644 --- a/storage/pipeline/upgrade_queue.go +++ b/storage/pipeline/upgrade_queue.go @@ -21,7 +21,7 @@ func (m *Sealing) MarkForUpgrade(ctx context.Context, id abi.SectorNumber) error return xerrors.Errorf("unable to snap-up sectors not in the 'Proving' state") } - if si.hasDeals() { + if si.hasData() { return xerrors.Errorf("not a committed-capacity sector, has deals") } diff --git a/storage/sealer/cbor_gen.go b/storage/sealer/cbor_gen.go index 22da1b520..e4b8e644d 100644 --- a/storage/sealer/cbor_gen.go +++ b/storage/sealer/cbor_gen.go @@ -33,7 +33,7 @@ func (t *Call) MarshalCBOR(w io.Writer) error { } // t.ID (storiface.CallID) (struct) - if len("ID") > cbg.MaxLength { + if len("ID") > 8192 { return xerrors.Errorf("Value in field \"ID\" was too long") } @@ -49,7 +49,7 @@ func (t *Call) MarshalCBOR(w io.Writer) error { } // t.State (sealer.CallState) (uint64) - if len("State") > cbg.MaxLength { + if len("State") > 8192 { return xerrors.Errorf("Value in field \"State\" was too long") } @@ -65,7 +65,7 @@ func (t *Call) MarshalCBOR(w io.Writer) error { } // t.Result (sealer.ManyBytes) (struct) - if len("Result") > cbg.MaxLength { + if len("Result") > 8192 { return xerrors.Errorf("Value in field \"Result\" was too long") } @@ -81,7 +81,7 @@ func (t *Call) MarshalCBOR(w io.Writer) error { } // t.RetType (sealer.ReturnType) (string) - if len("RetType") > cbg.MaxLength { + if len("RetType") > 8192 { return xerrors.Errorf("Value in field \"RetType\" was too long") } @@ -92,7 +92,7 @@ func (t *Call) MarshalCBOR(w io.Writer) error { return err } - if len(t.RetType) > cbg.MaxLength { + if len(t.RetType) > 8192 { return xerrors.Errorf("Value in field t.RetType was too long") } @@ -134,7 +134,7 @@ func (t *Call) UnmarshalCBOR(r io.Reader) (err error) { for i := uint64(0); i < n; i++ { { - sval, err := cbg.ReadString(cr) + sval, err := cbg.ReadStringWithMax(cr, 8192) if err != nil { return err } @@ -192,7 +192,7 @@ func (t *Call) UnmarshalCBOR(r io.Reader) (err error) { case "RetType": { - sval, err := cbg.ReadString(cr) + sval, err := cbg.ReadStringWithMax(cr, 8192) if err != nil { return err } @@ -221,7 +221,7 @@ func (t *WorkState) MarshalCBOR(w io.Writer) error { } // t.ID (sealer.WorkID) (struct) - if len("ID") > cbg.MaxLength { + if len("ID") > 8192 { return xerrors.Errorf("Value in field \"ID\" was too long") } @@ -237,7 +237,7 @@ func (t *WorkState) MarshalCBOR(w io.Writer) error { } // t.Status (sealer.WorkStatus) (string) - if len("Status") > cbg.MaxLength { + if len("Status") > 8192 { return xerrors.Errorf("Value in field \"Status\" was too long") } @@ -248,7 +248,7 @@ func (t *WorkState) MarshalCBOR(w io.Writer) error { return err } - if len(t.Status) > cbg.MaxLength { + if len(t.Status) > 8192 { return xerrors.Errorf("Value in field t.Status was too long") } @@ -260,7 +260,7 @@ func (t *WorkState) MarshalCBOR(w io.Writer) error { } // t.StartTime (int64) (int64) - if len("StartTime") > cbg.MaxLength { + if len("StartTime") > 8192 { return xerrors.Errorf("Value in field \"StartTime\" was too long") } @@ -282,7 +282,7 @@ func (t *WorkState) MarshalCBOR(w io.Writer) error { } // t.WorkError (string) (string) - if len("WorkError") > cbg.MaxLength { + if len("WorkError") > 8192 { return xerrors.Errorf("Value in field \"WorkError\" was too long") } @@ -293,7 +293,7 @@ func (t *WorkState) MarshalCBOR(w io.Writer) error { return err } - if len(t.WorkError) > cbg.MaxLength { + if len(t.WorkError) > 8192 { return xerrors.Errorf("Value in field t.WorkError was too long") } @@ -305,7 +305,7 @@ func (t *WorkState) MarshalCBOR(w io.Writer) error { } // t.WorkerCall (storiface.CallID) (struct) - if len("WorkerCall") > cbg.MaxLength { + if len("WorkerCall") > 8192 { return xerrors.Errorf("Value in field \"WorkerCall\" was too long") } @@ -321,7 +321,7 @@ func (t *WorkState) MarshalCBOR(w io.Writer) error { } // t.WorkerHostname (string) (string) - if len("WorkerHostname") > cbg.MaxLength { + if len("WorkerHostname") > 8192 { return xerrors.Errorf("Value in field \"WorkerHostname\" was too long") } @@ -332,7 +332,7 @@ func (t *WorkState) MarshalCBOR(w io.Writer) error { return err } - if len(t.WorkerHostname) > cbg.MaxLength { + if len(t.WorkerHostname) > 8192 { return xerrors.Errorf("Value in field t.WorkerHostname was too long") } @@ -374,7 +374,7 @@ func (t *WorkState) UnmarshalCBOR(r io.Reader) (err error) { for i := uint64(0); i < n; i++ { { - sval, err := cbg.ReadString(cr) + sval, err := cbg.ReadStringWithMax(cr, 8192) if err != nil { return err } @@ -397,7 +397,7 @@ func (t *WorkState) UnmarshalCBOR(r io.Reader) (err error) { case "Status": { - sval, err := cbg.ReadString(cr) + sval, err := cbg.ReadStringWithMax(cr, 8192) if err != nil { return err } @@ -408,10 +408,10 @@ func (t *WorkState) UnmarshalCBOR(r io.Reader) (err error) { case "StartTime": { maj, extra, err := cr.ReadHeader() - var extraI int64 if err != nil { return err } + var extraI int64 switch maj { case cbg.MajUnsignedInt: extraI = int64(extra) @@ -434,7 +434,7 @@ func (t *WorkState) UnmarshalCBOR(r io.Reader) (err error) { case "WorkError": { - sval, err := cbg.ReadString(cr) + sval, err := cbg.ReadStringWithMax(cr, 8192) if err != nil { return err } @@ -455,7 +455,7 @@ func (t *WorkState) UnmarshalCBOR(r io.Reader) (err error) { case "WorkerHostname": { - sval, err := cbg.ReadString(cr) + sval, err := cbg.ReadStringWithMax(cr, 8192) if err != nil { return err } @@ -484,7 +484,7 @@ func (t *WorkID) MarshalCBOR(w io.Writer) error { } // t.Method (sealtasks.TaskType) (string) - if len("Method") > cbg.MaxLength { + if len("Method") > 8192 { return xerrors.Errorf("Value in field \"Method\" was too long") } @@ -495,7 +495,7 @@ func (t *WorkID) MarshalCBOR(w io.Writer) error { return err } - if len(t.Method) > cbg.MaxLength { + if len(t.Method) > 8192 { return xerrors.Errorf("Value in field t.Method was too long") } @@ -507,7 +507,7 @@ func (t *WorkID) MarshalCBOR(w io.Writer) error { } // t.Params (string) (string) - if len("Params") > cbg.MaxLength { + if len("Params") > 8192 { return xerrors.Errorf("Value in field \"Params\" was too long") } @@ -518,7 +518,7 @@ func (t *WorkID) MarshalCBOR(w io.Writer) error { return err } - if len(t.Params) > cbg.MaxLength { + if len(t.Params) > 8192 { return xerrors.Errorf("Value in field t.Params was too long") } @@ -560,7 +560,7 @@ func (t *WorkID) UnmarshalCBOR(r io.Reader) (err error) { for i := uint64(0); i < n; i++ { { - sval, err := cbg.ReadString(cr) + sval, err := cbg.ReadStringWithMax(cr, 8192) if err != nil { return err } @@ -573,7 +573,7 @@ func (t *WorkID) UnmarshalCBOR(r io.Reader) (err error) { case "Method": { - sval, err := cbg.ReadString(cr) + sval, err := cbg.ReadStringWithMax(cr, 8192) if err != nil { return err } @@ -584,7 +584,7 @@ func (t *WorkID) UnmarshalCBOR(r io.Reader) (err error) { case "Params": { - sval, err := cbg.ReadString(cr) + sval, err := cbg.ReadStringWithMax(cr, 8192) if err != nil { return err } diff --git a/storage/sealer/storiface/cbor_gen.go b/storage/sealer/storiface/cbor_gen.go index 0b42136ea..79d6219eb 100644 --- a/storage/sealer/storiface/cbor_gen.go +++ b/storage/sealer/storiface/cbor_gen.go @@ -31,7 +31,7 @@ func (t *CallID) MarshalCBOR(w io.Writer) error { } // t.ID (uuid.UUID) (array) - if len("ID") > cbg.MaxLength { + if len("ID") > 8192 { return xerrors.Errorf("Value in field \"ID\" was too long") } @@ -42,7 +42,7 @@ func (t *CallID) MarshalCBOR(w io.Writer) error { return err } - if len(t.ID) > cbg.ByteArrayMaxLen { + if len(t.ID) > 2097152 { return xerrors.Errorf("Byte array in field t.ID was too long") } @@ -55,7 +55,7 @@ func (t *CallID) MarshalCBOR(w io.Writer) error { } // t.Sector (abi.SectorID) (struct) - if len("Sector") > cbg.MaxLength { + if len("Sector") > 8192 { return xerrors.Errorf("Value in field \"Sector\" was too long") } @@ -101,7 +101,7 @@ func (t *CallID) UnmarshalCBOR(r io.Reader) (err error) { for i := uint64(0); i < n; i++ { { - sval, err := cbg.ReadString(cr) + sval, err := cbg.ReadStringWithMax(cr, 8192) if err != nil { return err } @@ -118,19 +118,17 @@ func (t *CallID) UnmarshalCBOR(r io.Reader) (err error) { return err } - if extra > cbg.ByteArrayMaxLen { + if extra > 2097152 { return fmt.Errorf("t.ID: byte array too large (%d)", extra) } if maj != cbg.MajByteString { return fmt.Errorf("expected byte array") } - if extra != 16 { return fmt.Errorf("expected array to have 16 elements") } t.ID = [16]uint8{} - if _, err := io.ReadFull(cr, t.ID[:]); err != nil { return err } @@ -166,7 +164,7 @@ func (t *SecDataHttpHeader) MarshalCBOR(w io.Writer) error { } // t.Key (string) (string) - if len("Key") > cbg.MaxLength { + if len("Key") > 8192 { return xerrors.Errorf("Value in field \"Key\" was too long") } @@ -177,7 +175,7 @@ func (t *SecDataHttpHeader) MarshalCBOR(w io.Writer) error { return err } - if len(t.Key) > cbg.MaxLength { + if len(t.Key) > 8192 { return xerrors.Errorf("Value in field t.Key was too long") } @@ -189,7 +187,7 @@ func (t *SecDataHttpHeader) MarshalCBOR(w io.Writer) error { } // t.Value (string) (string) - if len("Value") > cbg.MaxLength { + if len("Value") > 8192 { return xerrors.Errorf("Value in field \"Value\" was too long") } @@ -200,7 +198,7 @@ func (t *SecDataHttpHeader) MarshalCBOR(w io.Writer) error { return err } - if len(t.Value) > cbg.MaxLength { + if len(t.Value) > 8192 { return xerrors.Errorf("Value in field t.Value was too long") } @@ -242,7 +240,7 @@ func (t *SecDataHttpHeader) UnmarshalCBOR(r io.Reader) (err error) { for i := uint64(0); i < n; i++ { { - sval, err := cbg.ReadString(cr) + sval, err := cbg.ReadStringWithMax(cr, 8192) if err != nil { return err } @@ -255,7 +253,7 @@ func (t *SecDataHttpHeader) UnmarshalCBOR(r io.Reader) (err error) { case "Key": { - sval, err := cbg.ReadString(cr) + sval, err := cbg.ReadStringWithMax(cr, 8192) if err != nil { return err } @@ -266,7 +264,7 @@ func (t *SecDataHttpHeader) UnmarshalCBOR(r io.Reader) (err error) { case "Value": { - sval, err := cbg.ReadString(cr) + sval, err := cbg.ReadStringWithMax(cr, 8192) if err != nil { return err } @@ -295,7 +293,7 @@ func (t *SectorLocation) MarshalCBOR(w io.Writer) error { } // t.URL (string) (string) - if len("URL") > cbg.MaxLength { + if len("URL") > 8192 { return xerrors.Errorf("Value in field \"URL\" was too long") } @@ -306,7 +304,7 @@ func (t *SectorLocation) MarshalCBOR(w io.Writer) error { return err } - if len(t.URL) > cbg.MaxLength { + if len(t.URL) > 8192 { return xerrors.Errorf("Value in field t.URL was too long") } @@ -318,7 +316,7 @@ func (t *SectorLocation) MarshalCBOR(w io.Writer) error { } // t.Local (bool) (bool) - if len("Local") > cbg.MaxLength { + if len("Local") > 8192 { return xerrors.Errorf("Value in field \"Local\" was too long") } @@ -334,7 +332,7 @@ func (t *SectorLocation) MarshalCBOR(w io.Writer) error { } // t.Headers ([]storiface.SecDataHttpHeader) (slice) - if len("Headers") > cbg.MaxLength { + if len("Headers") > 8192 { return xerrors.Errorf("Value in field \"Headers\" was too long") } @@ -345,7 +343,7 @@ func (t *SectorLocation) MarshalCBOR(w io.Writer) error { return err } - if len(t.Headers) > cbg.MaxLength { + if len(t.Headers) > 8192 { return xerrors.Errorf("Slice value in field t.Headers was too long") } @@ -356,6 +354,7 @@ func (t *SectorLocation) MarshalCBOR(w io.Writer) error { if err := v.MarshalCBOR(cw); err != nil { return err } + } return nil } @@ -389,7 +388,7 @@ func (t *SectorLocation) UnmarshalCBOR(r io.Reader) (err error) { for i := uint64(0); i < n; i++ { { - sval, err := cbg.ReadString(cr) + sval, err := cbg.ReadStringWithMax(cr, 8192) if err != nil { return err } @@ -402,7 +401,7 @@ func (t *SectorLocation) UnmarshalCBOR(r io.Reader) (err error) { case "URL": { - sval, err := cbg.ReadString(cr) + sval, err := cbg.ReadStringWithMax(cr, 8192) if err != nil { return err } @@ -435,7 +434,7 @@ func (t *SectorLocation) UnmarshalCBOR(r io.Reader) (err error) { return err } - if extra > cbg.MaxLength { + if extra > 8192 { return fmt.Errorf("t.Headers: array too large (%d)", extra) } @@ -463,6 +462,7 @@ func (t *SectorLocation) UnmarshalCBOR(r io.Reader) (err error) { } } + } } diff --git a/storage/sectorblocks/blocks.go b/storage/sectorblocks/blocks.go index 1593174bd..4b84e18fb 100644 --- a/storage/sectorblocks/blocks.go +++ b/storage/sectorblocks/blocks.go @@ -19,6 +19,7 @@ import ( "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/node/modules/dtypes" + "github.com/filecoin-project/lotus/storage/pipeline/piece" "github.com/filecoin-project/lotus/storage/sealer/storiface" ) @@ -48,7 +49,7 @@ func DsKeyToDealID(key datastore.Key) (uint64, error) { } type SectorBuilder interface { - SectorAddPieceToAny(ctx context.Context, size abi.UnpaddedPieceSize, r storiface.Data, d api.PieceDealInfo) (api.SectorOffset, error) + SectorAddPieceToAny(ctx context.Context, size abi.UnpaddedPieceSize, r storiface.Data, d piece.PieceDealInfo) (api.SectorOffset, error) SectorsStatus(ctx context.Context, sid abi.SectorNumber, showOnChainInfo bool) (api.SectorInfo, error) } @@ -100,7 +101,7 @@ func (st *SectorBlocks) writeRef(ctx context.Context, dealID abi.DealID, sectorI return st.keys.Put(ctx, DealIDToDsKey(dealID), newRef) // TODO: batch somehow } -func (st *SectorBlocks) AddPiece(ctx context.Context, size abi.UnpaddedPieceSize, r io.Reader, d api.PieceDealInfo) (abi.SectorNumber, abi.PaddedPieceSize, error) { +func (st *SectorBlocks) AddPiece(ctx context.Context, size abi.UnpaddedPieceSize, r io.Reader, d piece.PieceDealInfo) (abi.SectorNumber, abi.PaddedPieceSize, error) { so, err := st.SectorBuilder.SectorAddPieceToAny(ctx, size, r, d) if err != nil { return 0, 0, err diff --git a/storage/wdpost/wdpost_run_test.go b/storage/wdpost/wdpost_run_test.go index a6b37fd54..381f0e818 100644 --- a/storage/wdpost/wdpost_run_test.go +++ b/storage/wdpost/wdpost_run_test.go @@ -16,7 +16,6 @@ import ( actorstypes "github.com/filecoin-project/go-state-types/actors" "github.com/filecoin-project/go-state-types/big" "github.com/filecoin-project/go-state-types/builtin" - miner12 "github.com/filecoin-project/go-state-types/builtin/v12/miner" minertypes "github.com/filecoin-project/go-state-types/builtin/v9/miner" "github.com/filecoin-project/go-state-types/crypto" "github.com/filecoin-project/go-state-types/dline" @@ -28,6 +27,7 @@ import ( "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/chain/actors" + "github.com/filecoin-project/lotus/chain/actors/builtin/miner" "github.com/filecoin-project/lotus/chain/actors/policy" "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/journal" @@ -74,13 +74,13 @@ func (m *mockStorageMinerAPI) StateMinerPartitions(ctx context.Context, a addres return m.partitions, nil } -func (m *mockStorageMinerAPI) StateMinerSectors(ctx context.Context, address address.Address, snos *bitfield.BitField, key types.TipSetKey) ([]*miner12.SectorOnChainInfo, error) { - var sis []*miner12.SectorOnChainInfo +func (m *mockStorageMinerAPI) StateMinerSectors(ctx context.Context, address address.Address, snos *bitfield.BitField, key types.TipSetKey) ([]*miner.SectorOnChainInfo, error) { + var sis []*miner.SectorOnChainInfo if snos == nil { panic("unsupported") } _ = snos.ForEach(func(i uint64) error { - sis = append(sis, &miner12.SectorOnChainInfo{ + sis = append(sis, &miner.SectorOnChainInfo{ SectorNumber: abi.SectorNumber(i), }) return nil From c1c5deb458f6a2c4a441d8ca3fe5369609a3da15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 12 Mar 2024 10:44:56 +0100 Subject: [PATCH 239/394] feat: mpool: Cache actors in lite mode (#11668) --- chain/messagepool/provider.go | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/chain/messagepool/provider.go b/chain/messagepool/provider.go index 764e6c13a..8d1a97153 100644 --- a/chain/messagepool/provider.go +++ b/chain/messagepool/provider.go @@ -5,6 +5,7 @@ import ( "errors" "time" + lru "github.com/hashicorp/golang-lru/v2" "github.com/ipfs/go-cid" pubsub "github.com/libp2p/go-libp2p-pubsub" "golang.org/x/xerrors" @@ -16,6 +17,8 @@ import ( "github.com/filecoin-project/lotus/chain/stmgr" "github.com/filecoin-project/lotus/chain/store" "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/lib/must" + "github.com/filecoin-project/lotus/lib/result" ) var ( @@ -39,10 +42,19 @@ type Provider interface { IsLite() bool } +type actorCacheKey struct { + types.TipSetKey + address.Address +} + +var nonceCacheSize = 128 + type mpoolProvider struct { sm *stmgr.StateManager ps *pubsub.PubSub + liteActorCache *lru.Cache[actorCacheKey, result.Result[*types.Actor]] + lite MpoolNonceAPI } @@ -53,18 +65,31 @@ func NewProvider(sm *stmgr.StateManager, ps *pubsub.PubSub) Provider { } func NewProviderLite(sm *stmgr.StateManager, ps *pubsub.PubSub, noncer MpoolNonceAPI) Provider { - return &mpoolProvider{sm: sm, ps: ps, lite: noncer} + return &mpoolProvider{ + sm: sm, + ps: ps, + lite: noncer, + liteActorCache: must.One(lru.New[actorCacheKey, result.Result[*types.Actor]](nonceCacheSize)), + } } func (mpp *mpoolProvider) IsLite() bool { return mpp.lite != nil } -func (mpp *mpoolProvider) getActorLite(addr address.Address, ts *types.TipSet) (*types.Actor, error) { +func (mpp *mpoolProvider) getActorLite(addr address.Address, ts *types.TipSet) (act *types.Actor, err error) { if !mpp.IsLite() { return nil, errors.New("should not use getActorLite on non lite Provider") } + if c, ok := mpp.liteActorCache.Get(actorCacheKey{ts.Key(), addr}); ok { + return c.Unwrap() + } + + defer func() { + mpp.liteActorCache.Add(actorCacheKey{ts.Key(), addr}, result.Wrap(act, err)) + }() + n, err := mpp.lite.GetNonce(context.TODO(), addr, ts.Key()) if err != nil { return nil, xerrors.Errorf("getting nonce over lite: %w", err) From 1c1b10c66e431d2333a802eaad89a3d7576b97cc Mon Sep 17 00:00:00 2001 From: Aarsh Shah Date: Tue, 12 Mar 2024 18:22:02 +0400 Subject: [PATCH 240/394] enable storing events (#11712) --- node/builder_chain.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/builder_chain.go b/node/builder_chain.go index cf6c56428..a4a0babf7 100644 --- a/node/builder_chain.go +++ b/node/builder_chain.go @@ -223,7 +223,7 @@ func ConfigFullNode(c interface{}) Option { // If the Eth JSON-RPC is enabled, enable storing events at the ChainStore. // This is the case even if real-time and historic filtering are disabled, // as it enables us to serve logs in eth_getTransactionReceipt. - If(cfg.Fevm.EnableEthRPC, Override(StoreEventsKey, modules.EnableStoringEvents)), + If(cfg.Fevm.EnableEthRPC || cfg.Events.EnableActorEventsAPI, Override(StoreEventsKey, modules.EnableStoringEvents)), Override(new(dtypes.ClientImportMgr), modules.ClientImportMgr), From 77dd1f512e3282df97521b3b1e4d4ad476359699 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 12 Mar 2024 17:02:38 +0100 Subject: [PATCH 241/394] sealing pipeline: Output DDO pieces in SectorStatus (#11709) --- storage/pipeline/input.go | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/storage/pipeline/input.go b/storage/pipeline/input.go index 53c96fc55..335e080ae 100644 --- a/storage/pipeline/input.go +++ b/storage/pipeline/input.go @@ -952,20 +952,30 @@ func (m *Sealing) SectorsStatus(ctx context.Context, sid abi.SectorNumber, showO return api.SectorInfo{}, err } + nv, err := m.Api.StateNetworkVersion(ctx, types.EmptyTSK) + if err != nil { + return api.SectorInfo{}, xerrors.Errorf("getting network version: %w", err) + } + deals := make([]abi.DealID, len(info.Pieces)) pieces := make([]api.SectorPiece, len(info.Pieces)) for i, piece := range info.Pieces { - // todo make this work with DDO deals in some reasonable way - pieces[i].Piece = piece.Piece() - if !piece.HasDealInfo() || piece.Impl().PublishCid == nil { + + if !piece.HasDealInfo() { + continue + } + + pdi := piece.Impl() + if pdi.Valid(nv) != nil { continue } - pdi := piece.DealInfo().Impl() // copy pieces[i].DealInfo = &pdi - deals[i] = piece.DealInfo().Impl().DealID + if pdi.PublishCid != nil { + deals[i] = pdi.DealID + } } log := make([]api.SectorLog, len(info.Log)) From 9d73a70fc1e8267b8ede6d59da4705d241b229cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 12 Mar 2024 17:05:04 +0100 Subject: [PATCH 242/394] sealing pipeline: Fix failing ProveCommit3 aggregate (#11710) * itests: Repro failing ProveCommit3 aggregate * commit batch: Correctly sort sectors in processBatchV2 * fix imports * ci: Bigger instance for sector_pledge test * itests: Use Must-Post mining in TestPledgeBatching --- .circleci/config.yml | 1 + .circleci/template.yml | 2 +- itests/kit/ensemble_opts.go | 8 +++++++- itests/sector_pledge_test.go | 20 +++++++++++++++----- storage/pipeline/commit_batch.go | 13 ++++++------- 5 files changed, 30 insertions(+), 14 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 7b67d4a7f..0dafceb09 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -933,6 +933,7 @@ workflows: - build suite: itest-sector_pledge target: "./itests/sector_pledge_test.go" + resource_class: 2xlarge get-params: true - test: diff --git a/.circleci/template.yml b/.circleci/template.yml index 883e43a8d..b7462b027 100644 --- a/.circleci/template.yml +++ b/.circleci/template.yml @@ -545,7 +545,7 @@ workflows: - build suite: itest-[[ $name ]] target: "./itests/[[ $file ]]" - [[- if or (eq $name "worker") (eq $name "deals_concurrent") (eq $name "wdpost_worker_config")]] + [[- if or (eq $name "worker") (eq $name "deals_concurrent") (eq $name "wdpost_worker_config") (eq $name "sector_pledge")]] resource_class: 2xlarge [[- end]] [[- if or (eq $name "wdpost") (eq $name "sector_pledge")]] diff --git a/itests/kit/ensemble_opts.go b/itests/kit/ensemble_opts.go index d264da2bb..95bdd8da8 100644 --- a/itests/kit/ensemble_opts.go +++ b/itests/kit/ensemble_opts.go @@ -35,7 +35,13 @@ var DefaultEnsembleOpts = ensembleOpts{ } // MockProofs activates mock proofs for the entire ensemble. -func MockProofs() EnsembleOpt { +func MockProofs(e ...bool) EnsembleOpt { + if len(e) > 0 && !e[0] { + return func(opts *ensembleOpts) error { + return nil + } + } + return func(opts *ensembleOpts) error { opts.mockProofs = true // since we're using mock proofs, we don't need to download diff --git a/itests/sector_pledge_test.go b/itests/sector_pledge_test.go index b4e5c1133..eb93cfe9a 100644 --- a/itests/sector_pledge_test.go +++ b/itests/sector_pledge_test.go @@ -12,6 +12,7 @@ import ( "github.com/stretchr/testify/require" "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/go-state-types/big" miner5 "github.com/filecoin-project/specs-actors/v5/actors/builtin/miner" "github.com/filecoin-project/lotus/api" @@ -39,7 +40,7 @@ func TestPledgeSectors(t *testing.T) { defer cancel() _, miner, ens := kit.EnsembleMinimal(t, kit.MockProofs()) - ens.InterconnectAll().BeginMining(blockTime) + ens.InterconnectAll().BeginMiningMustPost(blockTime) miner.PledgeSectors(ctx, nSectors, 0, nil) } @@ -65,12 +66,18 @@ func TestPledgeBatching(t *testing.T) { //stm: @SECTOR_PRE_COMMIT_FLUSH_001, @SECTOR_COMMIT_FLUSH_001 blockTime := 50 * time.Millisecond - runTest := func(t *testing.T, nSectors int) { + runTest := func(t *testing.T, nSectors int, aggregate bool) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - client, miner, ens := kit.EnsembleMinimal(t, kit.MockProofs()) - ens.InterconnectAll().BeginMining(blockTime) + kit.QuietMiningLogs() + + client, miner, ens := kit.EnsembleMinimal(t, kit.MockProofs(!aggregate), kit.MutateSealingConfig(func(sc *config.SealingConfig) { + if aggregate { + sc.AggregateAboveBaseFee = types.FIL(big.Zero()) + } + })) + ens.InterconnectAll().BeginMiningMustPost(blockTime) client.WaitTillChain(ctx, kit.HeightAtLeast(10)) @@ -114,7 +121,10 @@ func TestPledgeBatching(t *testing.T) { } t.Run("100", func(t *testing.T) { - runTest(t, 100) + runTest(t, 100, false) + }) + t.Run("10-agg", func(t *testing.T) { + runTest(t, 10, true) }) } diff --git a/storage/pipeline/commit_batch.go b/storage/pipeline/commit_batch.go index 096f27e4c..a4688b741 100644 --- a/storage/pipeline/commit_batch.go +++ b/storage/pipeline/commit_batch.go @@ -331,6 +331,9 @@ func (b *CommitBatcher) processBatchV2(cfg sealiface.Config, sectors []abi.Secto return nil, err } + // sort sectors by number + sort.Slice(sectors, func(i, j int) bool { return sectors[i] < sectors[j] }) + total := len(sectors) res := sealiface.CommitBatchRes{ @@ -371,10 +374,6 @@ func (b *CommitBatcher) processBatchV2(cfg sealiface.Config, sectors []abi.Secto return nil, nil } - sort.Slice(infos, func(i, j int) bool { - return infos[i].Number < infos[j].Number - }) - proofs := make([][]byte, 0, total) for _, info := range infos { proofs = append(proofs, b.todo[info.Number].Proof) @@ -450,7 +449,7 @@ func (b *CommitBatcher) processBatchV2(cfg sealiface.Config, sectors []abi.Secto _, err = simulateMsgGas(b.mctx, b.api, from, b.maddr, builtin.MethodsMiner.ProveCommitSectors3, needFunds, maxFee, enc.Bytes()) if err != nil && (!api.ErrorIsIn(err, []error{&api.ErrOutOfGas{}}) || len(sectors) < miner.MinAggregatedSectors*2) { - log.Errorf("simulating CommitBatch message failed: %s", err) + log.Errorf("simulating CommitBatch message failed (%x): %s", enc.Bytes(), err) res.Error = err.Error() return []sealiface.CommitBatchRes{res}, xerrors.Errorf("simulating CommitBatch message failed: %w", err) } @@ -474,7 +473,7 @@ func (b *CommitBatcher) processBatchV2(cfg sealiface.Config, sectors []abi.Secto res.Msg = &mcid - log.Infow("Sent ProveCommitSectors2 message", "cid", mcid, "from", from, "todo", total, "sectors", len(infos)) + log.Infow("Sent ProveCommitSectors3 message", "cid", mcid, "from", from, "todo", total, "sectors", len(infos)) return []sealiface.CommitBatchRes{res}, nil } @@ -591,7 +590,7 @@ func (b *CommitBatcher) processBatchV1(cfg sealiface.Config, sectors []abi.Secto _, err = simulateMsgGas(b.mctx, b.api, from, b.maddr, builtin.MethodsMiner.ProveCommitAggregate, needFunds, maxFee, enc.Bytes()) if err != nil && (!api.ErrorIsIn(err, []error{&api.ErrOutOfGas{}}) || len(sectors) < miner.MinAggregatedSectors*2) { - log.Errorf("simulating CommitBatch message failed: %s", err) + log.Errorf("simulating CommitBatch message failed (%x): %s", enc.Bytes(), err) res.Error = err.Error() return []sealiface.CommitBatchRes{res}, xerrors.Errorf("simulating CommitBatch message failed: %w", err) } From 2d80e75e19d0a38f6793ace929c2f5a4b28c3ad5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 12 Mar 2024 17:11:50 +0100 Subject: [PATCH 243/394] fix: commit batch: Always go through commit batcher (#11704) * fix: commit batch: Always go through commit batcher * fix sealing fsm tests --- storage/pipeline/commit_batch.go | 6 +- storage/pipeline/fsm.go | 6 +- storage/pipeline/fsm_test.go | 23 ++++---- storage/pipeline/sector_state.go | 2 +- storage/pipeline/states_sealing.go | 88 ++---------------------------- 5 files changed, 21 insertions(+), 104 deletions(-) diff --git a/storage/pipeline/commit_batch.go b/storage/pipeline/commit_batch.go index a4688b741..d702d3078 100644 --- a/storage/pipeline/commit_batch.go +++ b/storage/pipeline/commit_batch.go @@ -209,7 +209,7 @@ func (b *CommitBatcher) maybeStartBatch(notif bool) ([]sealiface.CommitBatchRes, return nil, xerrors.Errorf("getting config: %w", err) } - if notif && total < cfg.MaxCommitBatch { + if notif && total < cfg.MaxCommitBatch && cfg.AggregateCommits { return nil, nil } @@ -233,7 +233,7 @@ func (b *CommitBatcher) maybeStartBatch(notif bool) ([]sealiface.CommitBatchRes, return false } - individual := (total < cfg.MinCommitBatch) || (total < miner.MinAggregatedSectors) || blackedOut() + individual := (total < cfg.MinCommitBatch) || (total < miner.MinAggregatedSectors) || blackedOut() || !cfg.AggregateCommits if !individual && !cfg.AggregateAboveBaseFee.Equals(big.Zero()) { if ts.MinTicketBlock().ParentBaseFee.LessThan(cfg.AggregateAboveBaseFee) { @@ -443,7 +443,7 @@ func (b *CommitBatcher) processBatchV2(cfg sealiface.Config, sectors []abi.Secto enc := new(bytes.Buffer) if err := params.MarshalCBOR(enc); err != nil { res.Error = err.Error() - return []sealiface.CommitBatchRes{res}, xerrors.Errorf("couldn't serialize ProveCommitSectors2Params: %w", err) + return []sealiface.CommitBatchRes{res}, xerrors.Errorf("couldn't serialize ProveCommitSectors3Params: %w", err) } _, err = simulateMsgGas(b.mctx, b.api, from, b.maddr, builtin.MethodsMiner.ProveCommitSectors3, needFunds, maxFee, enc.Bytes()) diff --git a/storage/pipeline/fsm.go b/storage/pipeline/fsm.go index ac3dafa86..6829f5210 100644 --- a/storage/pipeline/fsm.go +++ b/storage/pipeline/fsm.go @@ -127,8 +127,8 @@ var fsmPlanners = map[SectorState]func(events []statemachine.Event, state *Secto ), Committing: planCommitting, CommitFinalize: planOne( - on(SectorFinalized{}, SubmitCommit), - on(SectorFinalizedAvailable{}, SubmitCommit), + on(SectorFinalized{}, SubmitCommitAggregate), + on(SectorFinalizedAvailable{}, SubmitCommitAggregate), on(SectorFinalizeFailed{}, CommitFinalizeFailed), ), SubmitCommit: planOne( @@ -674,7 +674,7 @@ func planCommitting(events []statemachine.Event, state *SectorInfo) (uint64, err } case SectorCommitted: // the normal case e.apply(state) - state.State = SubmitCommit + state.State = SubmitCommitAggregate case SectorProofReady: // early finalize e.apply(state) state.State = CommitFinalize diff --git a/storage/pipeline/fsm_test.go b/storage/pipeline/fsm_test.go index 7d7201953..c403fb129 100644 --- a/storage/pipeline/fsm_test.go +++ b/storage/pipeline/fsm_test.go @@ -70,10 +70,10 @@ func TestHappyPath(t *testing.T) { require.Equal(m.t, m.state.State, Committing) m.planSingle(SectorCommitted{}) - require.Equal(m.t, m.state.State, SubmitCommit) + require.Equal(m.t, m.state.State, SubmitCommitAggregate) - m.planSingle(SectorCommitSubmitted{}) - require.Equal(m.t, m.state.State, CommitWait) + m.planSingle(SectorCommitAggregateSent{}) + require.Equal(m.t, m.state.State, CommitAggregateWait) m.planSingle(SectorProving{}) require.Equal(m.t, m.state.State, FinalizeSector) @@ -81,7 +81,7 @@ func TestHappyPath(t *testing.T) { m.planSingle(SectorFinalized{}) require.Equal(m.t, m.state.State, Proving) - expected := []SectorState{Packing, GetTicket, PreCommit1, PreCommit2, SubmitPreCommitBatch, PreCommitBatchWait, WaitSeed, Committing, SubmitCommit, CommitWait, FinalizeSector, Proving} + expected := []SectorState{Packing, GetTicket, PreCommit1, PreCommit2, SubmitPreCommitBatch, PreCommitBatchWait, WaitSeed, Committing, SubmitCommitAggregate, CommitAggregateWait, FinalizeSector, Proving} for i, n := range notif { if n.before.State != expected[i] { t.Fatalf("expected before state: %s, got: %s", expected[i], n.before.State) @@ -135,9 +135,6 @@ func TestHappyPathFinalizeEarly(t *testing.T) { require.Equal(m.t, m.state.State, CommitFinalize) m.planSingle(SectorFinalized{}) - require.Equal(m.t, m.state.State, SubmitCommit) - - m.planSingle(SectorSubmitCommitAggregate{}) require.Equal(m.t, m.state.State, SubmitCommitAggregate) m.planSingle(SectorCommitAggregateSent{}) @@ -149,7 +146,7 @@ func TestHappyPathFinalizeEarly(t *testing.T) { m.planSingle(SectorFinalized{}) require.Equal(m.t, m.state.State, Proving) - expected := []SectorState{Packing, GetTicket, PreCommit1, PreCommit2, SubmitPreCommitBatch, PreCommitBatchWait, WaitSeed, Committing, CommitFinalize, SubmitCommit, SubmitCommitAggregate, CommitAggregateWait, FinalizeSector, Proving} + expected := []SectorState{Packing, GetTicket, PreCommit1, PreCommit2, SubmitPreCommitBatch, PreCommitBatchWait, WaitSeed, Committing, CommitFinalize, SubmitCommitAggregate, CommitAggregateWait, FinalizeSector, Proving} for i, n := range notif { if n.before.State != expected[i] { t.Fatalf("expected before state: %s, got: %s", expected[i], n.before.State) @@ -188,9 +185,9 @@ func TestCommitFinalizeFailed(t *testing.T) { require.Equal(m.t, m.state.State, CommitFinalize) m.planSingle(SectorFinalized{}) - require.Equal(m.t, m.state.State, SubmitCommit) + require.Equal(m.t, m.state.State, SubmitCommitAggregate) - expected := []SectorState{Committing, CommitFinalize, CommitFinalizeFailed, CommitFinalize, SubmitCommit} + expected := []SectorState{Committing, CommitFinalize, CommitFinalizeFailed, CommitFinalize, SubmitCommitAggregate} for i, n := range notif { if n.before.State != expected[i] { t.Fatalf("expected before state: %s, got: %s", expected[i], n.before.State) @@ -242,10 +239,10 @@ func TestSeedRevert(t *testing.T) { // not changing the seed this time _, _, err = m.s.plan([]statemachine.Event{{User: SectorSeedReady{SeedValue: nil, SeedEpoch: 5}}, {User: SectorCommitted{}}}, m.state) require.NoError(t, err) - require.Equal(m.t, m.state.State, SubmitCommit) + require.Equal(m.t, m.state.State, SubmitCommitAggregate) - m.planSingle(SectorCommitSubmitted{}) - require.Equal(m.t, m.state.State, CommitWait) + m.planSingle(SectorCommitAggregateSent{}) + require.Equal(m.t, m.state.State, CommitAggregateWait) m.planSingle(SectorProving{}) require.Equal(m.t, m.state.State, FinalizeSector) diff --git a/storage/pipeline/sector_state.go b/storage/pipeline/sector_state.go index e1f5bfd69..9e7f75171 100644 --- a/storage/pipeline/sector_state.go +++ b/storage/pipeline/sector_state.go @@ -94,7 +94,7 @@ const ( CommitFinalizeFailed SectorState = "CommitFinalizeFailed" // single commit - SubmitCommit SectorState = "SubmitCommit" // send commit message to the chain + SubmitCommit SectorState = "SubmitCommit" // send commit message to the chain (deprecated) CommitWait SectorState = "CommitWait" // wait for the commit message to land on chain SubmitCommitAggregate SectorState = "SubmitCommitAggregate" diff --git a/storage/pipeline/states_sealing.go b/storage/pipeline/states_sealing.go index aef394789..4f40ac7c7 100644 --- a/storage/pipeline/states_sealing.go +++ b/storage/pipeline/states_sealing.go @@ -18,7 +18,6 @@ import ( "github.com/filecoin-project/go-state-types/abi" actorstypes "github.com/filecoin-project/go-state-types/actors" "github.com/filecoin-project/go-state-types/big" - "github.com/filecoin-project/go-state-types/builtin" miner2 "github.com/filecoin-project/go-state-types/builtin/v13/miner" verifreg13 "github.com/filecoin-project/go-state-types/builtin/v13/verifreg" "github.com/filecoin-project/go-state-types/builtin/v9/verifreg" @@ -740,89 +739,10 @@ func (m *Sealing) handleCommitting(ctx statemachine.Context, sector SectorInfo) } func (m *Sealing) handleSubmitCommit(ctx statemachine.Context, sector SectorInfo) error { - // TODO: Deprecate this path, always go through batcher, just respect the AggregateCommits config in there - - cfg, err := m.getConfig() - if err != nil { - return xerrors.Errorf("getting config: %w", err) - } - - if cfg.AggregateCommits { - nv, err := m.Api.StateNetworkVersion(ctx.Context(), types.EmptyTSK) - if err != nil { - return xerrors.Errorf("getting network version: %w", err) - } - - if nv >= network.Version13 { - return ctx.Send(SectorSubmitCommitAggregate{}) - } - } - - ts, err := m.Api.ChainHead(ctx.Context()) - if err != nil { - log.Errorf("handleSubmitCommit: api error, not proceeding: %+v", err) - return nil - } - - if err := m.checkCommit(ctx.Context(), sector, sector.Proof, ts.Key()); err != nil { - return ctx.Send(SectorCommitFailed{xerrors.Errorf("commit check error: %w", err)}) - } - - enc := new(bytes.Buffer) - params := &miner.ProveCommitSectorParams{ - SectorNumber: sector.SectorNumber, - Proof: sector.Proof, - } - - if err := params.MarshalCBOR(enc); err != nil { - return ctx.Send(SectorCommitFailed{xerrors.Errorf("could not serialize commit sector parameters: %w", err)}) - } - - mi, err := m.Api.StateMinerInfo(ctx.Context(), m.maddr, ts.Key()) - if err != nil { - log.Errorf("handleCommitting: api error, not proceeding: %+v", err) - return nil - } - - pci, err := m.Api.StateSectorPreCommitInfo(ctx.Context(), m.maddr, sector.SectorNumber, ts.Key()) - if err != nil { - return xerrors.Errorf("getting precommit info: %w", err) - } - if pci == nil { - return ctx.Send(SectorCommitFailed{error: xerrors.Errorf("precommit info not found on chain")}) - } - - collateral, err := m.Api.StateMinerInitialPledgeCollateral(ctx.Context(), m.maddr, pci.Info, ts.Key()) - if err != nil { - return xerrors.Errorf("getting initial pledge collateral: %w", err) - } - - collateral = big.Sub(collateral, pci.PreCommitDeposit) - if collateral.LessThan(big.Zero()) { - collateral = big.Zero() - } - - collateral, err = collateralSendAmount(ctx.Context(), m.Api, m.maddr, cfg, collateral) - if err != nil { - return err - } - - goodFunds := big.Add(collateral, big.Int(m.feeCfg.MaxCommitGasFee)) - - from, _, err := m.addrSel.AddressFor(ctx.Context(), m.Api, mi, api.CommitAddr, goodFunds, collateral) - if err != nil { - return ctx.Send(SectorCommitFailed{xerrors.Errorf("no good address to send commit message from: %w", err)}) - } - - // TODO: check seed / ticket / deals are up to date - mcid, err := sendMsg(ctx.Context(), m.Api, from, m.maddr, builtin.MethodsMiner.ProveCommitSector, collateral, big.Int(m.feeCfg.MaxCommitGasFee), enc.Bytes()) - if err != nil { - return ctx.Send(SectorCommitFailed{xerrors.Errorf("pushing message to mpool: %w", err)}) - } - - return ctx.Send(SectorCommitSubmitted{ - Message: mcid, - }) + // like precommit this is a deprecated state, but we keep it around for + // existing state machines + // todo: drop after nv21 + return ctx.Send(SectorSubmitCommitAggregate{}) } // processPieces returns either: From 81e65dba5676a93d077d0d420e56eeb9f9a0d27f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 12 Mar 2024 17:22:14 +0100 Subject: [PATCH 244/394] sealing pipeline: Fix panic on padding pieces in WaitDeals (#11708) * sealing pipeline: Fix panic on padding pieces in WaitDeals * sealing pipeline: Catch panics --- storage/pipeline/fsm.go | 24 ++++++++++++++++++++++-- storage/pipeline/input.go | 6 +++++- storage/pipeline/types.go | 28 ++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 3 deletions(-) diff --git a/storage/pipeline/fsm.go b/storage/pipeline/fsm.go index 6829f5210..ced6867d1 100644 --- a/storage/pipeline/fsm.go +++ b/storage/pipeline/fsm.go @@ -11,6 +11,7 @@ import ( "net/http" "os" "reflect" + "runtime" "time" "golang.org/x/xerrors" @@ -39,8 +40,27 @@ func (m *Sealing) Plan(events []statemachine.Event, user interface{}) (interface return nil, processed, nil } - return func(ctx statemachine.Context, si SectorInfo) error { - err := next(ctx, si) + return func(ctx statemachine.Context, si SectorInfo) (err error) { + // handle panics + defer func() { + if r := recover(); r != nil { + buf := make([]byte, 1<<16) + n := runtime.Stack(buf, false) + buf = buf[:n] + + l := Log{ + Timestamp: uint64(time.Now().Unix()), + Message: fmt.Sprintf("panic: %v\n%s", r, buf), + Kind: "panic", + } + si.logAppend(l) + + err = fmt.Errorf("panic: %v\n%s", r, buf) + } + }() + + // execute the next state + err = next(ctx, si) if err != nil { log.Errorf("unhandled sector error (%d): %+v", si.SectorNumber, err) return nil diff --git a/storage/pipeline/input.go b/storage/pipeline/input.go index 335e080ae..8862380d8 100644 --- a/storage/pipeline/input.go +++ b/storage/pipeline/input.go @@ -34,12 +34,16 @@ func (m *Sealing) handleWaitDeals(ctx statemachine.Context, sector SectorInfo) e for _, piece := range sector.Pieces { used += piece.Piece().Size.Unpadded() + if !piece.HasDealInfo() { + continue + } + endEpoch, err := piece.EndEpoch() if err != nil { return xerrors.Errorf("piece.EndEpoch: %w", err) } - if piece.HasDealInfo() && endEpoch > lastDealEnd { + if endEpoch > lastDealEnd { lastDealEnd = endEpoch } } diff --git a/storage/pipeline/types.go b/storage/pipeline/types.go index 48ae60546..7b263dd6a 100644 --- a/storage/pipeline/types.go +++ b/storage/pipeline/types.go @@ -289,10 +289,18 @@ func (sp *SafeSectorPiece) handleDealInfo(params handleDealInfoParams) error { // SectorPiece Proxy func (sp *SafeSectorPiece) Impl() piece.PieceDealInfo { + if !sp.HasDealInfo() { + return piece.PieceDealInfo{} + } + return sp.real.DealInfo.Impl() } func (sp *SafeSectorPiece) String() string { + if !sp.HasDealInfo() { + return "" + } + return sp.real.DealInfo.String() } @@ -305,21 +313,41 @@ func (sp *SafeSectorPiece) Valid(nv network.Version) error { } func (sp *SafeSectorPiece) StartEpoch() (abi.ChainEpoch, error) { + if !sp.HasDealInfo() { + return 0, xerrors.Errorf("no deal info") + } + return sp.real.DealInfo.StartEpoch() } func (sp *SafeSectorPiece) EndEpoch() (abi.ChainEpoch, error) { + if !sp.HasDealInfo() { + return 0, xerrors.Errorf("no deal info") + } + return sp.real.DealInfo.EndEpoch() } func (sp *SafeSectorPiece) PieceCID() cid.Cid { + if !sp.HasDealInfo() { + return sp.real.Piece.PieceCID + } + return sp.real.DealInfo.PieceCID() } func (sp *SafeSectorPiece) KeepUnsealedRequested() bool { + if !sp.HasDealInfo() { + return false + } + return sp.real.DealInfo.KeepUnsealedRequested() } func (sp *SafeSectorPiece) GetAllocation(ctx context.Context, aapi piece.AllocationAPI, tsk types.TipSetKey) (*verifreg.Allocation, error) { + if !sp.HasDealInfo() { + return nil, xerrors.Errorf("no deal info") + } + return sp.real.DealInfo.GetAllocation(ctx, aapi, tsk) } From 0b2613ec21a3024f1632adde32010346b969c912 Mon Sep 17 00:00:00 2001 From: qwdsds <50311944+qwdsds@users.noreply.github.com> Date: Tue, 12 Mar 2024 16:10:57 +0800 Subject: [PATCH 245/394] chore: fix typos --- storage/pipeline/commit_batch.go | 2 +- storage/sealer/partialfile/partialfile.go | 4 ++-- storage/sealer/sched_worker.go | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/storage/pipeline/commit_batch.go b/storage/pipeline/commit_batch.go index d702d3078..ecb8569d0 100644 --- a/storage/pipeline/commit_batch.go +++ b/storage/pipeline/commit_batch.go @@ -841,7 +841,7 @@ func (b *CommitBatcher) getCommitCutoff(si SectorInfo) (time.Time, error) { } av, err := actorstypes.VersionForNetwork(nv) if err != nil { - log.Errorf("unsupported network vrsion: %s", err) + log.Errorf("unsupported network version: %s", err) return time.Now(), err } mpcd, err := policy.GetMaxProveCommitDuration(av, si.SectorType) diff --git a/storage/sealer/partialfile/partialfile.go b/storage/sealer/partialfile/partialfile.go index bb3cfb590..f811f3349 100644 --- a/storage/sealer/partialfile/partialfile.go +++ b/storage/sealer/partialfile/partialfile.go @@ -64,7 +64,7 @@ func writeTrailer(maxPieceSize int64, w *os.File, r rlepluslazy.RunIterator) err func CreatePartialFile(maxPieceSize abi.PaddedPieceSize, path string) (*PartialFile, error) { f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0644) // nolint if err != nil { - return nil, xerrors.Errorf("openning partial file '%s': %w", path, err) + return nil, xerrors.Errorf("opening partial file '%s': %w", path, err) } err = func() error { @@ -99,7 +99,7 @@ func CreatePartialFile(maxPieceSize abi.PaddedPieceSize, path string) (*PartialF func OpenPartialFile(maxPieceSize abi.PaddedPieceSize, path string) (*PartialFile, error) { f, err := os.OpenFile(path, os.O_RDWR, 0644) // nolint if err != nil { - return nil, xerrors.Errorf("openning partial file '%s': %w", path, err) + return nil, xerrors.Errorf("opening partial file '%s': %w", path, err) } st, err := f.Stat() diff --git a/storage/sealer/sched_worker.go b/storage/sealer/sched_worker.go index 35acd755d..b482be589 100644 --- a/storage/sealer/sched_worker.go +++ b/storage/sealer/sched_worker.go @@ -137,7 +137,7 @@ func (sw *schedWorker) handleWorker() { } // wait for more tasks to be assigned by the main scheduler or for the worker - // to finish precessing a task + // to finish processing a task update, pokeSched, ok := sw.waitForUpdates() if !ok { return From e060cd2f37774a408eaeac9beb7eced6aebec516 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 28 Feb 2024 20:57:12 +0100 Subject: [PATCH 246/394] lppiece: Implement Piece Park --- cmd/lotus-provider/tasks/tasks.go | 17 +- .../harmonydb/sql/20240228-piece-park.sql | 30 ++++ node/config/types.go | 8 + provider/lpffi/piece_funcs.go | 65 +++++++ provider/lppiece/task_park_piece.go | 162 ++++++++++++++++++ provider/lpseal/task_trees.go | 11 ++ storage/paths/http_handler.go | 19 +- storage/sealer/ffiwrapper/basicfs/fs.go | 3 + storage/sealer/storiface/filetype.go | 19 +- storage/sealer/storiface/storage.go | 11 ++ 10 files changed, 326 insertions(+), 19 deletions(-) create mode 100644 lib/harmony/harmonydb/sql/20240228-piece-park.sql create mode 100644 provider/lpffi/piece_funcs.go create mode 100644 provider/lppiece/task_park_piece.go diff --git a/cmd/lotus-provider/tasks/tasks.go b/cmd/lotus-provider/tasks/tasks.go index 6ecd680d0..11c12216b 100644 --- a/cmd/lotus-provider/tasks/tasks.go +++ b/cmd/lotus-provider/tasks/tasks.go @@ -3,6 +3,9 @@ package tasks import ( "context" + "github.com/filecoin-project/lotus/lib/lazy" + "github.com/filecoin-project/lotus/lib/must" + "github.com/filecoin-project/lotus/provider/lppiece" logging "github.com/ipfs/go-log/v2" "github.com/samber/lo" @@ -64,6 +67,18 @@ func StartTasks(ctx context.Context, dependencies *deps.Deps) (*harmonytask.Task } } + slrLazy := lazy.MakeLazy(func() (*lpffi.SealCalls, error) { + return lpffi.NewSealCalls(stor, lstor, si), nil + }) + + { + // Piece handling + if cfg.Subsystems.EnableParkPiece { + parkPieceTask := lppiece.NewParkPieceTask(db, must.One(slrLazy.Val()), cfg.Subsystems.ParkPieceMaxTasks) + activeTasks = append(activeTasks, parkPieceTask) + } + } + hasAnySealingTask := cfg.Subsystems.EnableSealSDR || cfg.Subsystems.EnableSealSDRTrees || cfg.Subsystems.EnableSendPrecommitMsg || @@ -79,7 +94,7 @@ func StartTasks(ctx context.Context, dependencies *deps.Deps) (*harmonytask.Task sp = lpseal.NewPoller(db, full) go sp.RunPoller(ctx) - slr = lpffi.NewSealCalls(stor, lstor, si) + slr = must.One(slrLazy.Val()) } // NOTE: Tasks with the LEAST priority are at the top diff --git a/lib/harmony/harmonydb/sql/20240228-piece-park.sql b/lib/harmony/harmonydb/sql/20240228-piece-park.sql new file mode 100644 index 000000000..c58ecdafe --- /dev/null +++ b/lib/harmony/harmonydb/sql/20240228-piece-park.sql @@ -0,0 +1,30 @@ +create table parked_pieces ( + id serial primary key, + created_at timestamp default current_timestamp, + + piece_cid text not null, + piece_padded_size bigint not null, + piece_raw_size text not null, + + complete boolean not null default false +); + +create table parked_piece_refs ( + ref_id serial primary key, + piece_id int not null, + + foreign key (piece_id) references parked_pieces(id) on delete cascade +); + +create table park_piece_tasks ( + task_id bigint not null + constraint park_piece_tasks_pk + primary key, + + piece_ref_id int not null, + + data_url text not null, + data_headers jsonb not null default '{}', + data_raw_size bigint not null, + data_delete_on_finalize bool not null +); diff --git a/node/config/types.go b/node/config/types.go index 789d24103..d770512c5 100644 --- a/node/config/types.go +++ b/node/config/types.go @@ -112,6 +112,14 @@ type ProviderSubsystemsConfig struct { EnableWinningPost bool WinningPostMaxTasks int + // EnableParkPiece enables the "piece parking" task to run on this node. This task is responsible for fetching + // pieces from the network and storing them in the storage subsystem until sectors are sealed. This task is + // only applicable when integrating with boost, and should be enabled on nodes which will hold deal data + // from boost until sectors containing the related pieces have the TreeD/TreeR constructed. + // Note that future Curio implementations will have a separate task type for fetching pieces from the internet. + EnableParkPiece bool + ParkPieceMaxTasks int + // EnableSealSDR enables SDR tasks to run. SDR is the long sequential computation // creating 11 layer files in sector cache directory. // diff --git a/provider/lpffi/piece_funcs.go b/provider/lpffi/piece_funcs.go new file mode 100644 index 000000000..4e2816786 --- /dev/null +++ b/provider/lpffi/piece_funcs.go @@ -0,0 +1,65 @@ +package lpffi + +import ( + "context" + "github.com/filecoin-project/lotus/storage/sealer/storiface" + "golang.org/x/xerrors" + "io" + "os" + "time" +) + +func (sb *SealCalls) WritePiece(ctx context.Context, pieceID storiface.PieceNumber, size int64, data io.Reader) error { + // todo: config(?): allow setting PathStorage for this + // todo storage reservations + paths, done, err := sb.sectors.AcquireSector(ctx, nil, pieceID.Ref(), storiface.FTNone, storiface.FTPiece, storiface.PathSealing) + if err != nil { + return err + } + defer done() + + dest := paths.Piece + tempDest := dest + ".tmp" + + destFile, err := os.OpenFile(tempDest, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) + if err != nil { + return xerrors.Errorf("creating temp piece file '%s': %w", tempDest, err) + } + + removeTemp := true + defer func() { + if removeTemp { + rerr := os.Remove(tempDest) + if rerr != nil { + log.Errorf("removing temp file: %+v", rerr) + } + } + }() + + copyStart := time.Now() + + n, err := io.CopyBuffer(destFile, io.LimitReader(data, size), make([]byte, 8<<20)) + if err != nil { + _ = destFile.Close() + return xerrors.Errorf("copying piece data: %w", err) + } + + if err := destFile.Close(); err != nil { + return xerrors.Errorf("closing temp piece file: %w", err) + } + + if n != size { + return xerrors.Errorf("short write: %d", n) + } + + copyEnd := time.Now() + + log.Infow("wrote parked piece", "piece", pieceID, "size", size, "duration", copyEnd.Sub(copyStart), "dest", dest, "MiB/s", float64(size)/(1<<20)/copyEnd.Sub(copyStart).Seconds()) + + if err := os.Rename(tempDest, dest); err != nil { + return xerrors.Errorf("rename temp piece to dest %s -> %s: %w", tempDest, dest, err) + } + + removeTemp = false + return nil +} diff --git a/provider/lppiece/task_park_piece.go b/provider/lppiece/task_park_piece.go new file mode 100644 index 000000000..370775803 --- /dev/null +++ b/provider/lppiece/task_park_piece.go @@ -0,0 +1,162 @@ +package lppiece + +import ( + "context" + "encoding/json" + "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/lotus/lib/harmony/harmonydb" + "github.com/filecoin-project/lotus/lib/harmony/harmonytask" + "github.com/filecoin-project/lotus/lib/harmony/resources" + "github.com/filecoin-project/lotus/lib/promise" + "github.com/filecoin-project/lotus/provider/lpffi" + "github.com/filecoin-project/lotus/provider/lpseal" + "github.com/filecoin-project/lotus/storage/sealer/storiface" + "github.com/ipfs/go-cid" + logging "github.com/ipfs/go-log/v2" + "golang.org/x/xerrors" + "net/http" + "time" +) + +var log = logging.Logger("lppiece") + +// ParkPieceTask gets a piece from some origin, and parks it in storage +// Pieces are always f00, piece ID is mapped to pieceCID in the DB +type ParkPieceTask struct { + db *harmonydb.DB + sc *lpffi.SealCalls + + TF promise.Promise[harmonytask.AddTaskFunc] + + max int +} + +func NewParkPieceTask(db *harmonydb.DB, sc *lpffi.SealCalls, max int) *ParkPieceTask { + return &ParkPieceTask{ + db: db, + sc: sc, + + max: max, + } +} + +func (p *ParkPieceTask) PullPiece(ctx context.Context, pieceCID cid.Cid, rawSize int64, paddedSize abi.PaddedPieceSize, dataUrl string, headers http.Header) error { + p.TF.Val(ctx)(func(id harmonytask.TaskID, tx *harmonydb.Tx) (shouldCommit bool, err error) { + var pieceID int + err = tx.QueryRow(`INSERT INTO parked_pieces (piece_cid, piece_padded_size, piece_raw_size) VALUES ($1, $2, $3) RETURNING id`, pieceCID.String(), int64(paddedSize), rawSize).Scan(&pieceID) + if err != nil { + return false, xerrors.Errorf("inserting parked piece: %w", err) + } + + var refID int + err = tx.QueryRow(`INSERT INTO parked_piece_refs (piece_id) VALUES ($1) RETURNING ref_id`, pieceID).Scan(&refID) + if err != nil { + return false, xerrors.Errorf("inserting parked piece ref: %w", err) + } + + headersJson, err := json.Marshal(headers) + if err != nil { + return false, xerrors.Errorf("marshaling headers: %w", err) + } + + _, err = tx.Exec(`INSERT INTO park_piece_tasks (task_id, piece_ref_id, data_url, data_headers, data_raw_size, data_delete_on_finalize) + VALUES ($1, $2, $3, $4, $5, $6)`, id, refID, dataUrl, headersJson, rawSize, false) + if err != nil { + return false, xerrors.Errorf("inserting park piece task: %w", err) + } + + return true, nil + }) + + return nil +} + +func (p *ParkPieceTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (done bool, err error) { + ctx := context.Background() + + var taskData []struct { + PieceID int `db:"id"` + PieceCreatedAt time.Time `db:"created_at"` + PieceCID string `db:"piece_cid"` + Complete bool `db:"complete"` + + DataURL string `db:"data_url"` + DataHeaders string `db:"data_headers"` + DataRawSize int64 `db:"data_raw_size"` + DataDeleteOnFinalize bool `db:"data_delete_on_finalize"` + } + + err = p.db.Select(ctx, &taskData, ` + select + pp.id, + pp.created_at, + pp.piece_cid, + pp.complete, + ppt.data_url, + ppt.data_headers, + ppt.data_raw_size, + ppt.data_delete_on_finalize + from park_piece_tasks ppt + join parked_piece_refs ppr on ppt.piece_ref_id = ppr.ref_id + join parked_pieces pp on ppr.piece_id = pp.id + where ppt.task_id = $1 + `, taskID) + if err != nil { + return false, err + } + + if len(taskData) != 1 { + return false, xerrors.Errorf("expected 1 task, got %d", len(taskData)) + } + + if taskData[0].Complete { + log.Warnw("park piece task already complete", "task_id", taskID, "piece_cid", taskData[0].PieceCID) + return true, nil + } + + upr := &lpseal.UrlPieceReader{ + Url: taskData[0].DataURL, + RawSize: taskData[0].DataRawSize, + } + defer func() { + _ = upr.Close() + }() + + pnum := storiface.PieceNumber(taskData[0].PieceID) + + if err := p.sc.WritePiece(ctx, pnum, taskData[0].DataRawSize, upr); err != nil { + return false, xerrors.Errorf("write piece: %w", err) + } + + _, err = p.db.Exec(ctx, `update parked_pieces set complete = true where id = $1`, taskData[0].PieceID) + if err != nil { + return false, xerrors.Errorf("marking piece as complete: %w", err) + } + + return true, nil +} + +func (p *ParkPieceTask) CanAccept(ids []harmonytask.TaskID, engine *harmonytask.TaskEngine) (*harmonytask.TaskID, error) { + id := ids[0] + return &id, nil +} + +func (p *ParkPieceTask) TypeDetails() harmonytask.TaskTypeDetails { + return harmonytask.TaskTypeDetails{ + Max: p.max, + Name: "ParkPiece", + Cost: resources.Resources{ + Cpu: 1, + Gpu: 0, + Ram: 64 << 20, + Storage: nil, // TODO + }, + MaxFailures: 10, + } +} + +func (p *ParkPieceTask) Adder(taskFunc harmonytask.AddTaskFunc) { + p.TF.Set(taskFunc) +} + +var _ harmonytask.TaskInterface = &ParkPieceTask{} diff --git a/provider/lpseal/task_trees.go b/provider/lpseal/task_trees.go index da0fcf1e9..a632c5e87 100644 --- a/provider/lpseal/task_trees.go +++ b/provider/lpseal/task_trees.go @@ -200,6 +200,7 @@ type UrlPieceReader struct { RawSize int64 // the exact number of bytes read, if we read more or less that's an error readSoFar int64 + closed bool active io.ReadCloser // auto-closed on EOF } @@ -239,6 +240,7 @@ func (u *UrlPieceReader) Read(p []byte) (n int, err error) { // If EOF is reached, close the reader if err == io.EOF { cerr := u.active.Close() + u.closed = true if cerr != nil { log.Errorf("error closing http piece reader: %s", cerr) } @@ -253,4 +255,13 @@ func (u *UrlPieceReader) Read(p []byte) (n int, err error) { return n, err } +func (u *UrlPieceReader) Close() error { + if !u.closed { + u.closed = true + return u.active.Close() + } + + return nil +} + var _ harmonytask.TaskInterface = &TreesTask{} diff --git a/storage/paths/http_handler.go b/storage/paths/http_handler.go index 57de578a6..92f4162db 100644 --- a/storage/paths/http_handler.go +++ b/storage/paths/http_handler.go @@ -9,12 +9,10 @@ import ( "strconv" "time" + "github.com/filecoin-project/go-state-types/abi" "github.com/gorilla/mux" "github.com/ipfs/go-cid" logging "github.com/ipfs/go-log/v2" - "golang.org/x/xerrors" - - "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/lotus/storage/sealer/partialfile" "github.com/filecoin-project/lotus/storage/sealer/storiface" @@ -340,18 +338,5 @@ func (handler *FetchHandler) generatePoRepVanillaProof(w http.ResponseWriter, r } func FileTypeFromString(t string) (storiface.SectorFileType, error) { - switch t { - case storiface.FTUnsealed.String(): - return storiface.FTUnsealed, nil - case storiface.FTSealed.String(): - return storiface.FTSealed, nil - case storiface.FTCache.String(): - return storiface.FTCache, nil - case storiface.FTUpdate.String(): - return storiface.FTUpdate, nil - case storiface.FTUpdateCache.String(): - return storiface.FTUpdateCache, nil - default: - return 0, xerrors.Errorf("unknown sector file type: '%s'", t) - } + return storiface.TypeFromString(t) } diff --git a/storage/sealer/ffiwrapper/basicfs/fs.go b/storage/sealer/ffiwrapper/basicfs/fs.go index 4fd8e271f..47c7f526e 100644 --- a/storage/sealer/ffiwrapper/basicfs/fs.go +++ b/storage/sealer/ffiwrapper/basicfs/fs.go @@ -39,6 +39,9 @@ func (b *Provider) AcquireSector(ctx context.Context, id storiface.SectorRef, ex if err := os.Mkdir(filepath.Join(b.Root, storiface.FTUpdateCache.String()), 0755); err != nil && !os.IsExist(err) { // nolint return storiface.SectorPaths{}, nil, err } + if err := os.Mkdir(filepath.Join(b.Root, storiface.FTPiece.String()), 0755); err != nil && !os.IsExist(err) { // nolint + return storiface.SectorPaths{}, nil, err + } done := func() {} diff --git a/storage/sealer/storiface/filetype.go b/storage/sealer/storiface/filetype.go index ec3c5450c..887dda688 100644 --- a/storage/sealer/storiface/filetype.go +++ b/storage/sealer/storiface/filetype.go @@ -9,16 +9,22 @@ import ( ) const ( + // "regular" sectors FTUnsealed SectorFileType = 1 << iota FTSealed FTCache + + // snap FTUpdate FTUpdateCache + // Piece Park + FTPiece + FileTypes = iota ) -var PathTypes = []SectorFileType{FTUnsealed, FTSealed, FTCache, FTUpdate, FTUpdateCache} +var PathTypes = []SectorFileType{FTUnsealed, FTSealed, FTCache, FTUpdate, FTUpdateCache, FTPiece} const ( FTNone SectorFileType = 0 @@ -39,6 +45,7 @@ var FSOverheadSeal = map[SectorFileType]int{ // 10x overheads FTUpdate: FSOverheadDen, FTUpdateCache: FSOverheadDen*2 + 1, FTCache: 141, // 11 layers + D(2x ssize) + C + R' + FTPiece: FSOverheadDen, } // sector size * disk / fs overhead. FSOverheadDen is like the unit of sector size @@ -49,6 +56,7 @@ var FsOverheadFinalized = map[SectorFileType]int{ FTUpdate: FSOverheadDen, FTUpdateCache: 1, FTCache: 1, + FTPiece: FSOverheadDen, } type SectorFileType int @@ -65,6 +73,8 @@ func TypeFromString(s string) (SectorFileType, error) { return FTUpdate, nil case "update-cache": return FTUpdateCache, nil + case "piece": + return FTPiece, nil default: return 0, xerrors.Errorf("unknown sector file type '%s'", s) } @@ -82,6 +92,8 @@ func (t SectorFileType) String() string { return "update" case FTUpdateCache: return "update-cache" + case FTPiece: + return "piece" default: return fmt.Sprintf("", t, (t & ((1 << FileTypes) - 1)).Strings()) } @@ -206,6 +218,7 @@ type SectorPaths struct { Cache string Update string UpdateCache string + Piece string } func ParseSectorID(baseName string) (abi.SectorID, error) { @@ -242,6 +255,8 @@ func PathByType(sps SectorPaths, fileType SectorFileType) string { return sps.Update case FTUpdateCache: return sps.UpdateCache + case FTPiece: + return sps.Piece } panic("requested unknown path type") @@ -259,5 +274,7 @@ func SetPathByType(sps *SectorPaths, fileType SectorFileType, p string) { sps.Update = p case FTUpdateCache: sps.UpdateCache = p + case FTPiece: + sps.Piece = p } } diff --git a/storage/sealer/storiface/storage.go b/storage/sealer/storiface/storage.go index fe4e1e208..75cc9399c 100644 --- a/storage/sealer/storiface/storage.go +++ b/storage/sealer/storiface/storage.go @@ -20,6 +20,17 @@ type SectorRef struct { var NoSectorRef = SectorRef{} +// PieceNumber is a reference to a piece in the storage system; mapping between +// pieces in the storage system and piece CIDs is maintained by the storage index +type PieceNumber uint64 + +func (pn PieceNumber) Ref() SectorRef { + return SectorRef{ + ID: abi.SectorID{Miner: 0, Number: abi.SectorNumber(pn)}, + ProofType: abi.RegisteredSealProof_StackedDrg64GiBV1, // This only cares about TreeD which is the same for all sizes + } +} + type ProverPoSt interface { GenerateWinningPoSt(ctx context.Context, minerID abi.ActorID, sectorInfo []proof.ExtendedSectorInfo, randomness abi.PoStRandomness) ([]proof.PoStProof, error) GenerateWindowPoSt(ctx context.Context, minerID abi.ActorID, ppt abi.RegisteredPoStProof, sectorInfo []proof.ExtendedSectorInfo, randomness abi.PoStRandomness) (proof []proof.PoStProof, skipped []abi.SectorID, err error) From 0800e6e5a7c015b89057d15616745330ca64f79c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 28 Feb 2024 21:50:12 +0100 Subject: [PATCH 247/394] make gen --- build/openrpc/miner.json | 10 ++++++---- cmd/lotus-provider/tasks/tasks.go | 6 +++--- documentation/en/api-v0-methods-miner.md | 2 ++ .../en/default-lotus-provider-config.toml | 12 ++++++++++++ .../harmonydb/sql/20240228-piece-park.sql | 16 +++++++++++----- node/config/doc_gen.go | 16 ++++++++++++++++ provider/lpffi/piece_funcs.go | 6 ++++-- provider/lppiece/task_park_piece.go | 13 ++++++++----- storage/paths/http_handler.go | 3 ++- 9 files changed, 64 insertions(+), 20 deletions(-) diff --git a/build/openrpc/miner.json b/build/openrpc/miner.json index 0686987c8..2b0887ae2 100644 --- a/build/openrpc/miner.json +++ b/build/openrpc/miner.json @@ -9711,6 +9711,7 @@ 0, 1, 0, + 0, 0 ], "Read": [ @@ -9718,6 +9719,7 @@ 3, 0, 0, + 0, 0 ] } @@ -9736,8 +9738,8 @@ "title": "number", "type": "number" }, - "maxItems": 5, - "minItems": 5, + "maxItems": 6, + "minItems": 6, "type": "array" }, "Sector": { @@ -9760,8 +9762,8 @@ "title": "number", "type": "number" }, - "maxItems": 5, - "minItems": 5, + "maxItems": 6, + "minItems": 6, "type": "array" } }, diff --git a/cmd/lotus-provider/tasks/tasks.go b/cmd/lotus-provider/tasks/tasks.go index 11c12216b..71e794545 100644 --- a/cmd/lotus-provider/tasks/tasks.go +++ b/cmd/lotus-provider/tasks/tasks.go @@ -3,9 +3,6 @@ package tasks import ( "context" - "github.com/filecoin-project/lotus/lib/lazy" - "github.com/filecoin-project/lotus/lib/must" - "github.com/filecoin-project/lotus/provider/lppiece" logging "github.com/ipfs/go-log/v2" "github.com/samber/lo" @@ -13,11 +10,14 @@ import ( "github.com/filecoin-project/lotus/cmd/lotus-provider/deps" "github.com/filecoin-project/lotus/lib/harmony/harmonytask" + "github.com/filecoin-project/lotus/lib/lazy" + "github.com/filecoin-project/lotus/lib/must" "github.com/filecoin-project/lotus/node/modules" "github.com/filecoin-project/lotus/provider" "github.com/filecoin-project/lotus/provider/chainsched" "github.com/filecoin-project/lotus/provider/lpffi" "github.com/filecoin-project/lotus/provider/lpmessage" + "github.com/filecoin-project/lotus/provider/lppiece" "github.com/filecoin-project/lotus/provider/lpseal" "github.com/filecoin-project/lotus/provider/lpwinning" ) diff --git a/documentation/en/api-v0-methods-miner.md b/documentation/en/api-v0-methods-miner.md index b133930bc..dd2f511db 100644 --- a/documentation/en/api-v0-methods-miner.md +++ b/documentation/en/api-v0-methods-miner.md @@ -3954,6 +3954,7 @@ Response: 0, 1, 0, + 0, 0 ], "Read": [ @@ -3961,6 +3962,7 @@ Response: 3, 0, 0, + 0, 0 ] } diff --git a/documentation/en/default-lotus-provider-config.toml b/documentation/en/default-lotus-provider-config.toml index dd9921769..83a7c243d 100644 --- a/documentation/en/default-lotus-provider-config.toml +++ b/documentation/en/default-lotus-provider-config.toml @@ -25,6 +25,18 @@ # type: int #WinningPostMaxTasks = 0 + # EnableParkPiece enables the "piece parking" task to run on this node. This task is responsible for fetching + # pieces from the network and storing them in the storage subsystem until sectors are sealed. This task is + # only applicable when integrating with boost, and should be enabled on nodes which will hold deal data + # from boost until sectors containing the related pieces have the TreeD/TreeR constructed. + # Note that future Curio implementations will have a separate task type for fetching pieces from the internet. + # + # type: bool + #EnableParkPiece = false + + # type: int + #ParkPieceMaxTasks = 0 + # EnableSealSDR enables SDR tasks to run. SDR is the long sequential computation # creating 11 layer files in sector cache directory. # diff --git a/lib/harmony/harmonydb/sql/20240228-piece-park.sql b/lib/harmony/harmonydb/sql/20240228-piece-park.sql index c58ecdafe..a1bf0efa9 100644 --- a/lib/harmony/harmonydb/sql/20240228-piece-park.sql +++ b/lib/harmony/harmonydb/sql/20240228-piece-park.sql @@ -1,17 +1,23 @@ create table parked_pieces ( - id serial primary key, + id bigserial primary key, created_at timestamp default current_timestamp, piece_cid text not null, piece_padded_size bigint not null, piece_raw_size text not null, - + complete boolean not null default false ); +/* + * This table is used to keep track of the references to the parked pieces + * so that we can delete them when they are no longer needed. + * + * All references into the parked_pieces table should be done through this table. + */ create table parked_piece_refs ( - ref_id serial primary key, - piece_id int not null, + ref_id bigserial primary key, + piece_id bigint not null, foreign key (piece_id) references parked_pieces(id) on delete cascade ); @@ -21,7 +27,7 @@ create table park_piece_tasks ( constraint park_piece_tasks_pk primary key, - piece_ref_id int not null, + piece_ref_id bigint not null, data_url text not null, data_headers jsonb not null default '{}', diff --git a/node/config/doc_gen.go b/node/config/doc_gen.go index f28c5abd8..c49dad3ac 100644 --- a/node/config/doc_gen.go +++ b/node/config/doc_gen.go @@ -1033,6 +1033,22 @@ documentation.`, Comment: ``, }, + { + Name: "EnableParkPiece", + Type: "bool", + + Comment: `EnableParkPiece enables the "piece parking" task to run on this node. This task is responsible for fetching +pieces from the network and storing them in the storage subsystem until sectors are sealed. This task is +only applicable when integrating with boost, and should be enabled on nodes which will hold deal data +from boost until sectors containing the related pieces have the TreeD/TreeR constructed. +Note that future Curio implementations will have a separate task type for fetching pieces from the internet.`, + }, + { + Name: "ParkPieceMaxTasks", + Type: "int", + + Comment: ``, + }, { Name: "EnableSealSDR", Type: "bool", diff --git a/provider/lpffi/piece_funcs.go b/provider/lpffi/piece_funcs.go index 4e2816786..2ee75151b 100644 --- a/provider/lpffi/piece_funcs.go +++ b/provider/lpffi/piece_funcs.go @@ -2,11 +2,13 @@ package lpffi import ( "context" - "github.com/filecoin-project/lotus/storage/sealer/storiface" - "golang.org/x/xerrors" "io" "os" "time" + + "golang.org/x/xerrors" + + "github.com/filecoin-project/lotus/storage/sealer/storiface" ) func (sb *SealCalls) WritePiece(ctx context.Context, pieceID storiface.PieceNumber, size int64, data io.Reader) error { diff --git a/provider/lppiece/task_park_piece.go b/provider/lppiece/task_park_piece.go index 370775803..2ddcd7617 100644 --- a/provider/lppiece/task_park_piece.go +++ b/provider/lppiece/task_park_piece.go @@ -3,7 +3,15 @@ package lppiece import ( "context" "encoding/json" + "net/http" + "time" + + "github.com/ipfs/go-cid" + logging "github.com/ipfs/go-log/v2" + "golang.org/x/xerrors" + "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/lotus/lib/harmony/harmonydb" "github.com/filecoin-project/lotus/lib/harmony/harmonytask" "github.com/filecoin-project/lotus/lib/harmony/resources" @@ -11,11 +19,6 @@ import ( "github.com/filecoin-project/lotus/provider/lpffi" "github.com/filecoin-project/lotus/provider/lpseal" "github.com/filecoin-project/lotus/storage/sealer/storiface" - "github.com/ipfs/go-cid" - logging "github.com/ipfs/go-log/v2" - "golang.org/x/xerrors" - "net/http" - "time" ) var log = logging.Logger("lppiece") diff --git a/storage/paths/http_handler.go b/storage/paths/http_handler.go index 92f4162db..c828f6006 100644 --- a/storage/paths/http_handler.go +++ b/storage/paths/http_handler.go @@ -9,11 +9,12 @@ import ( "strconv" "time" - "github.com/filecoin-project/go-state-types/abi" "github.com/gorilla/mux" "github.com/ipfs/go-cid" logging "github.com/ipfs/go-log/v2" + "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/lotus/storage/sealer/partialfile" "github.com/filecoin-project/lotus/storage/sealer/storiface" "github.com/filecoin-project/lotus/storage/sealer/tarutil" From 1bb228898ea1936724576a1aeb46c4b98f484321 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 29 Feb 2024 11:11:40 +0100 Subject: [PATCH 248/394] Integrate PiecePark into boost-proxy --- cmd/lotus-shed/lpdeal.go | 79 ++++++-- .../harmonydb/sql/20240228-piece-park.sql | 25 ++- provider/lppiece/task_park_piece.go | 170 ++++++++++-------- 3 files changed, 173 insertions(+), 101 deletions(-) diff --git a/cmd/lotus-shed/lpdeal.go b/cmd/lotus-shed/lpdeal.go index 01f886015..d5c718f66 100644 --- a/cmd/lotus-shed/lpdeal.go +++ b/cmd/lotus-shed/lpdeal.go @@ -4,6 +4,8 @@ import ( "bytes" "context" "fmt" + "github.com/filecoin-project/lotus/lib/harmony/harmonydb" + "github.com/google/uuid" "io" "net" "net/http" @@ -14,7 +16,6 @@ import ( "time" "github.com/fatih/color" - "github.com/ipfs/go-cid" "github.com/mitchellh/go-homedir" manet "github.com/multiformats/go-multiaddr/net" "github.com/urfave/cli/v2" @@ -427,7 +428,7 @@ var lpBoostProxyCmd = &cli.Command{ } pieceInfoLk := new(sync.Mutex) - pieceInfos := map[cid.Cid][]pieceInfo{} + pieceInfos := map[uuid.UUID][]pieceInfo{} ast.Internal.SectorAddPieceToAny = func(ctx context.Context, pieceSize abi.UnpaddedPieceSize, pieceData storiface.Data, deal api.PieceDealInfo) (api.SectorOffset, error) { origPieceData := pieceData @@ -449,25 +450,68 @@ var lpBoostProxyCmd = &cli.Command{ done: make(chan struct{}), } + pieceUUID := uuid.New() + pieceInfoLk.Lock() - pieceInfos[deal.DealProposal.PieceCID] = append(pieceInfos[deal.DealProposal.PieceCID], pi) + pieceInfos[pieceUUID] = append(pieceInfos[pieceUUID], pi) pieceInfoLk.Unlock() // /piece?piece_cid=xxxx dataUrl := rootUrl dataUrl.Path = "/piece" - dataUrl.RawQuery = "piece_cid=" + deal.DealProposal.PieceCID.String() + dataUrl.RawQuery = "piece_id=" + pieceUUID.String() + + // add piece entry + + var refID int64 + + comm, err := db.BeginTransaction(ctx, func(tx *harmonydb.Tx) (commit bool, err error) { + // Add parked_piece, on conflict do nothing + var pieceID int64 + err = tx.QueryRow(` + INSERT INTO parked_pieces (piece_cid, piece_padded_size, piece_raw_size) + VALUES ($1, $2, $3) + ON CONFLICT (piece_cid) DO UPDATE + SET piece_cid = EXCLUDED.piece_cid + RETURNING id`, deal.DealProposal.PieceCID.String(), int64(pieceSize.Padded()), int64(pieceSize)).Scan(&pieceID) + if err != nil { + return false, xerrors.Errorf("upserting parked piece and getting id: %w", err) + } + + // Add parked_piece_ref + err = tx.QueryRow(`INSERT INTO parked_piece_refs (piece_id, data_url) + VALUES ($1, $2) RETURNING ref_id`, pieceID, dataUrl.String()).Scan(&refID) + if err != nil { + return false, xerrors.Errorf("inserting parked piece ref: %w", err) + } + + // If everything went well, commit the transaction + return true, nil // This will commit the transaction + }, harmonydb.OptionRetry()) + if err != nil { + return api.SectorOffset{}, xerrors.Errorf("inserting parked piece: %w", err) + } + if !comm { + return api.SectorOffset{}, xerrors.Errorf("piece tx didn't commit") + } + + // wait for piece to be parked + + <-pi.done + + pieceIDUrl := url.URL{ + Scheme: "pieceref", + Opaque: fmt.Sprintf("%d", refID), + } // make a sector - so, err := pin.AllocatePieceToSector(ctx, maddr, deal, int64(pieceSize), dataUrl, nil) + so, err := pin.AllocatePieceToSector(ctx, maddr, deal, int64(pieceSize), pieceIDUrl, nil) if err != nil { return api.SectorOffset{}, err } color.Blue("%s piece assigned to sector f0%d:%d @ %d", deal.DealProposal.PieceCID, mid, so.Sector, so.Offset) - <-pi.done - return so, nil } @@ -484,10 +528,12 @@ var lpBoostProxyCmd = &cli.Command{ ast.Internal.StorageGetLocks = si.StorageGetLocks var pieceHandler http.HandlerFunc = func(w http.ResponseWriter, r *http.Request) { - // /piece?piece_cid=xxxx - pieceCid, err := cid.Decode(r.URL.Query().Get("piece_cid")) + // /piece?piece_id=xxxx + pieceUUID := r.URL.Query().Get("piece_id") + + pu, err := uuid.Parse(pieceUUID) if err != nil { - http.Error(w, "bad piece_cid", http.StatusBadRequest) + http.Error(w, "bad piece id", http.StatusBadRequest) return } @@ -496,13 +542,13 @@ var lpBoostProxyCmd = &cli.Command{ return } - fmt.Printf("%s request for piece from %s\n", pieceCid, r.RemoteAddr) + fmt.Printf("%s request for piece from %s\n", pieceUUID, r.RemoteAddr) pieceInfoLk.Lock() - pis, ok := pieceInfos[pieceCid] + pis, ok := pieceInfos[pu] if !ok { http.Error(w, "piece not found", http.StatusNotFound) - color.Red("%s not found", pieceCid) + color.Red("%s not found", pu) pieceInfoLk.Unlock() return } @@ -511,7 +557,10 @@ var lpBoostProxyCmd = &cli.Command{ pi := pis[0] pis = pis[1:] - pieceInfos[pieceCid] = pis + pieceInfos[pu] = pis + if len(pis) == 0 { + delete(pieceInfos, pu) + } pieceInfoLk.Unlock() @@ -533,7 +582,7 @@ var lpBoostProxyCmd = &cli.Command{ return } - color.Green("%s served %.3f MiB in %s (%.2f MiB/s)", pieceCid, float64(n)/(1024*1024), took, mbps) + color.Green("%s served %.3f MiB in %s (%.2f MiB/s)", pu, float64(n)/(1024*1024), took, mbps) } finalApi := proxy.LoggingAPI[api.StorageMiner, api.StorageMinerStruct](&ast) diff --git a/lib/harmony/harmonydb/sql/20240228-piece-park.sql b/lib/harmony/harmonydb/sql/20240228-piece-park.sql index a1bf0efa9..ebba68fae 100644 --- a/lib/harmony/harmonydb/sql/20240228-piece-park.sql +++ b/lib/harmony/harmonydb/sql/20240228-piece-park.sql @@ -2,11 +2,14 @@ create table parked_pieces ( id bigserial primary key, created_at timestamp default current_timestamp, - piece_cid text not null, + piece_cid text not null unique constraint parked_pieces_piece_cid_key, piece_padded_size bigint not null, piece_raw_size text not null, - complete boolean not null default false + complete boolean not null default false, + task_id bigint default null, + + foreign key (task_id) references harmony_task (id) on delete set null ); /* @@ -14,23 +17,15 @@ create table parked_pieces ( * so that we can delete them when they are no longer needed. * * All references into the parked_pieces table should be done through this table. + * + * data_url is optional for refs which also act as data sources. */ create table parked_piece_refs ( ref_id bigserial primary key, piece_id bigint not null, + data_url text, + data_headers jsonb not null default '{}', + foreign key (piece_id) references parked_pieces(id) on delete cascade ); - -create table park_piece_tasks ( - task_id bigint not null - constraint park_piece_tasks_pk - primary key, - - piece_ref_id bigint not null, - - data_url text not null, - data_headers jsonb not null default '{}', - data_raw_size bigint not null, - data_delete_on_finalize bool not null -); diff --git a/provider/lppiece/task_park_piece.go b/provider/lppiece/task_park_piece.go index 2ddcd7617..7fb5a0ac3 100644 --- a/provider/lppiece/task_park_piece.go +++ b/provider/lppiece/task_park_piece.go @@ -3,15 +3,12 @@ package lppiece import ( "context" "encoding/json" - "net/http" + "strconv" "time" - "github.com/ipfs/go-cid" logging "github.com/ipfs/go-log/v2" "golang.org/x/xerrors" - "github.com/filecoin-project/go-state-types/abi" - "github.com/filecoin-project/lotus/lib/harmony/harmonydb" "github.com/filecoin-project/lotus/lib/harmony/harmonytask" "github.com/filecoin-project/lotus/lib/harmony/resources" @@ -22,6 +19,7 @@ import ( ) var log = logging.Logger("lppiece") +var PieceParkPollInterval = time.Second * 15 // ParkPieceTask gets a piece from some origin, and parks it in storage // Pieces are always f00, piece ID is mapped to pieceCID in the DB @@ -35,108 +33,138 @@ type ParkPieceTask struct { } func NewParkPieceTask(db *harmonydb.DB, sc *lpffi.SealCalls, max int) *ParkPieceTask { - return &ParkPieceTask{ + pt := &ParkPieceTask{ db: db, sc: sc, max: max, } + go pt.pollPieceTasks(context.Background()) + return pt } -func (p *ParkPieceTask) PullPiece(ctx context.Context, pieceCID cid.Cid, rawSize int64, paddedSize abi.PaddedPieceSize, dataUrl string, headers http.Header) error { - p.TF.Val(ctx)(func(id harmonytask.TaskID, tx *harmonydb.Tx) (shouldCommit bool, err error) { - var pieceID int - err = tx.QueryRow(`INSERT INTO parked_pieces (piece_cid, piece_padded_size, piece_raw_size) VALUES ($1, $2, $3) RETURNING id`, pieceCID.String(), int64(paddedSize), rawSize).Scan(&pieceID) - if err != nil { - return false, xerrors.Errorf("inserting parked piece: %w", err) +func (p *ParkPieceTask) pollPieceTasks(ctx context.Context) { + for { + // select parked pieces with no task_id + var pieceIDs []struct { + ID storiface.PieceNumber `db:"id"` } - var refID int - err = tx.QueryRow(`INSERT INTO parked_piece_refs (piece_id) VALUES ($1) RETURNING ref_id`, pieceID).Scan(&refID) + err := p.db.Select(ctx, &pieceIDs, `SELECT id FROM parked_pieces WHERE complete = FALSE AND task_id IS NULL`) if err != nil { - return false, xerrors.Errorf("inserting parked piece ref: %w", err) + log.Errorf("failed to get parked pieces: %s", err) + time.Sleep(PieceParkPollInterval) + continue } - headersJson, err := json.Marshal(headers) - if err != nil { - return false, xerrors.Errorf("marshaling headers: %w", err) + if len(pieceIDs) == 0 { + time.Sleep(PieceParkPollInterval) + continue } - _, err = tx.Exec(`INSERT INTO park_piece_tasks (task_id, piece_ref_id, data_url, data_headers, data_raw_size, data_delete_on_finalize) - VALUES ($1, $2, $3, $4, $5, $6)`, id, refID, dataUrl, headersJson, rawSize, false) - if err != nil { - return false, xerrors.Errorf("inserting park piece task: %w", err) + for _, pieceID := range pieceIDs { + // create a task for each piece + p.TF.Val(ctx)(func(id harmonytask.TaskID, tx *harmonydb.Tx) (shouldCommit bool, err error) { + // update + n, err := tx.Exec(`UPDATE parked_pieces SET task_id = $1 WHERE id = $2 AND complete = FALSE AND task_id IS NULL`, id, pieceID.ID) + if err != nil { + return false, xerrors.Errorf("updating parked piece: %w", err) + } + + // commit only if we updated the piece + return n > 0, nil + }) } - - return true, nil - }) - - return nil + } } func (p *ParkPieceTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (done bool, err error) { ctx := context.Background() - var taskData []struct { - PieceID int `db:"id"` - PieceCreatedAt time.Time `db:"created_at"` - PieceCID string `db:"piece_cid"` - Complete bool `db:"complete"` - - DataURL string `db:"data_url"` - DataHeaders string `db:"data_headers"` - DataRawSize int64 `db:"data_raw_size"` - DataDeleteOnFinalize bool `db:"data_delete_on_finalize"` + // Define a struct to hold piece data. + var piecesData []struct { + PieceID int64 `db:"id"` + PieceCreatedAt time.Time `db:"created_at"` + PieceCID string `db:"piece_cid"` + Complete bool `db:"complete"` + PiecePaddedSize int64 `db:"piece_padded_size"` + PieceRawSize string `db:"piece_raw_size"` } - err = p.db.Select(ctx, &taskData, ` - select - pp.id, - pp.created_at, - pp.piece_cid, - pp.complete, - ppt.data_url, - ppt.data_headers, - ppt.data_raw_size, - ppt.data_delete_on_finalize - from park_piece_tasks ppt - join parked_piece_refs ppr on ppt.piece_ref_id = ppr.ref_id - join parked_pieces pp on ppr.piece_id = pp.id - where ppt.task_id = $1 - `, taskID) + // Select the piece data using the task ID. + err = p.db.Select(ctx, &piecesData, ` + SELECT id, created_at, piece_cid, complete, piece_padded_size, piece_raw_size + FROM parked_pieces + WHERE task_id = $1 + `, taskID) if err != nil { - return false, err + return false, xerrors.Errorf("fetching piece data: %w", err) } - if len(taskData) != 1 { - return false, xerrors.Errorf("expected 1 task, got %d", len(taskData)) + if len(piecesData) == 0 { + return false, xerrors.Errorf("no piece data found for task_id: %d", taskID) } - if taskData[0].Complete { - log.Warnw("park piece task already complete", "task_id", taskID, "piece_cid", taskData[0].PieceCID) + pieceData := piecesData[0] + + if pieceData.Complete { + log.Warnw("park piece task already complete", "task_id", taskID, "piece_cid", pieceData.PieceCID) return true, nil } - upr := &lpseal.UrlPieceReader{ - Url: taskData[0].DataURL, - RawSize: taskData[0].DataRawSize, - } - defer func() { - _ = upr.Close() - }() - - pnum := storiface.PieceNumber(taskData[0].PieceID) - - if err := p.sc.WritePiece(ctx, pnum, taskData[0].DataRawSize, upr); err != nil { - return false, xerrors.Errorf("write piece: %w", err) + // Define a struct for reference data. + var refData []struct { + DataURL string `db:"data_url"` + DataHeaders json.RawMessage `db:"data_headers"` } - _, err = p.db.Exec(ctx, `update parked_pieces set complete = true where id = $1`, taskData[0].PieceID) + // Now, select the first reference data that has a URL. + err = p.db.Select(ctx, &refData, ` + SELECT data_url, data_headers + FROM parked_piece_refs + WHERE piece_id = $1 AND data_url IS NOT NULL + LIMIT 1 + `, pieceData.PieceID) if err != nil { - return false, xerrors.Errorf("marking piece as complete: %w", err) + return false, xerrors.Errorf("fetching reference data: %w", err) } - return true, nil + if len(refData) == 0 { + return false, xerrors.Errorf("no refs found for piece_id: %d", pieceData.PieceID) + } + + // Convert piece_raw_size from string to int64. + pieceRawSize, err := strconv.ParseInt(pieceData.PieceRawSize, 10, 64) + if err != nil { + return false, xerrors.Errorf("parsing piece raw size: %w", err) + } + + if refData[0].DataURL != "" { + upr := &lpseal.UrlPieceReader{ + Url: refData[0].DataURL, + RawSize: pieceRawSize, + } + defer func() { + _ = upr.Close() + }() + + pnum := storiface.PieceNumber(pieceData.PieceID) + + if err := p.sc.WritePiece(ctx, pnum, pieceRawSize, upr); err != nil { + return false, xerrors.Errorf("write piece: %w", err) + } + + // Update the piece as complete after a successful write. + _, err = p.db.Exec(ctx, `UPDATE parked_pieces SET complete = TRUE WHERE id = $1`, pieceData.PieceID) + if err != nil { + return false, xerrors.Errorf("marking piece as complete: %w", err) + } + + return true, nil + } + + // If no URL is found, this indicates an issue since at least one URL is expected. + return false, xerrors.Errorf("no data URL found for piece_id: %d", pieceData.PieceID) } func (p *ParkPieceTask) CanAccept(ids []harmonytask.TaskID, engine *harmonytask.TaskEngine) (*harmonytask.TaskID, error) { From 6b3038d51a2109cae0246b74dd3a7bc6747f7d03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 29 Feb 2024 23:41:55 +0100 Subject: [PATCH 249/394] fix lint --- cmd/lotus-shed/lpdeal.go | 4 ++-- provider/lppiece/task_park_piece.go | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/cmd/lotus-shed/lpdeal.go b/cmd/lotus-shed/lpdeal.go index d5c718f66..0a585bf66 100644 --- a/cmd/lotus-shed/lpdeal.go +++ b/cmd/lotus-shed/lpdeal.go @@ -4,8 +4,6 @@ import ( "bytes" "context" "fmt" - "github.com/filecoin-project/lotus/lib/harmony/harmonydb" - "github.com/google/uuid" "io" "net" "net/http" @@ -16,6 +14,7 @@ import ( "time" "github.com/fatih/color" + "github.com/google/uuid" "github.com/mitchellh/go-homedir" manet "github.com/multiformats/go-multiaddr/net" "github.com/urfave/cli/v2" @@ -36,6 +35,7 @@ import ( "github.com/filecoin-project/lotus/chain/types" lcli "github.com/filecoin-project/lotus/cli" "github.com/filecoin-project/lotus/cmd/lotus-provider/deps" + "github.com/filecoin-project/lotus/lib/harmony/harmonydb" "github.com/filecoin-project/lotus/lib/must" "github.com/filecoin-project/lotus/lib/nullreader" "github.com/filecoin-project/lotus/metrics/proxy" diff --git a/provider/lppiece/task_park_piece.go b/provider/lppiece/task_park_piece.go index 7fb5a0ac3..034ef9b2c 100644 --- a/provider/lppiece/task_park_piece.go +++ b/provider/lppiece/task_park_piece.go @@ -63,6 +63,8 @@ func (p *ParkPieceTask) pollPieceTasks(ctx context.Context) { } for _, pieceID := range pieceIDs { + pieceID := pieceID + // create a task for each piece p.TF.Val(ctx)(func(id harmonytask.TaskID, tx *harmonydb.Tx) (shouldCommit bool, err error) { // update From b90cf19604df174cbf0da1e7263f19fd803f020b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 1 Mar 2024 00:07:51 +0100 Subject: [PATCH 250/394] lpseal: PiecePark in SDRTrees --- provider/lpffi/piece_funcs.go | 4 +++ provider/lpffi/sdr_funcs.go | 4 +-- provider/lpseal/task_trees.go | 58 ++++++++++++++++++++++++++++++++--- storage/paths/remote.go | 40 ++++++++++++++++++++++++ 4 files changed, 100 insertions(+), 6 deletions(-) diff --git a/provider/lpffi/piece_funcs.go b/provider/lpffi/piece_funcs.go index 2ee75151b..986d0fe4d 100644 --- a/provider/lpffi/piece_funcs.go +++ b/provider/lpffi/piece_funcs.go @@ -65,3 +65,7 @@ func (sb *SealCalls) WritePiece(ctx context.Context, pieceID storiface.PieceNumb removeTemp = false return nil } + +func (sb *SealCalls) PieceReader(ctx context.Context, id storiface.PieceNumber) (io.ReadCloser, error) { + return sb.sectors.storage.ReaderSeq(ctx, id.Ref(), storiface.FTPiece) +} diff --git a/provider/lpffi/sdr_funcs.go b/provider/lpffi/sdr_funcs.go index 05a7bbd80..77019e4d2 100644 --- a/provider/lpffi/sdr_funcs.go +++ b/provider/lpffi/sdr_funcs.go @@ -42,7 +42,7 @@ type SealCalls struct { externCalls ExternalSealer*/ } -func NewSealCalls(st paths.Store, ls *paths.Local, si paths.SectorIndex) *SealCalls { +func NewSealCalls(st *paths.Remote, ls *paths.Local, si paths.SectorIndex) *SealCalls { return &SealCalls{ sectors: &storageProvider{ storage: st, @@ -54,7 +54,7 @@ func NewSealCalls(st paths.Store, ls *paths.Local, si paths.SectorIndex) *SealCa } type storageProvider struct { - storage paths.Store + storage *paths.Remote localStore *paths.Local sindex paths.SectorIndex storageReservations *xsync.MapOf[harmonytask.TaskID, *StorageReservation] diff --git a/provider/lpseal/task_trees.go b/provider/lpseal/task_trees.go index a632c5e87..31775d290 100644 --- a/provider/lpseal/task_trees.go +++ b/provider/lpseal/task_trees.go @@ -4,6 +4,8 @@ import ( "context" "io" "net/http" + "net/url" + "strconv" "github.com/ipfs/go-cid" "golang.org/x/xerrors" @@ -88,6 +90,15 @@ func (t *TreesTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (done var dataReader io.Reader var unpaddedData bool + var closers []io.Closer + defer func() { + for _, c := range closers { + if err := c.Close(); err != nil { + log.Errorw("error closing piece reader", "error", err) + } + } + }() + if len(pieces) > 0 { pieceInfos := make([]abi.PieceInfo, len(pieces)) pieceReaders := make([]io.Reader, len(pieces)) @@ -106,10 +117,49 @@ func (t *TreesTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (done // make pieceReader if p.DataUrl != nil { - pieceReaders[i], _ = padreader.New(&UrlPieceReader{ - Url: *p.DataUrl, - RawSize: *p.DataRawSize, - }, uint64(*p.DataRawSize)) + dataUrl := *p.DataUrl + + goUrl, err := url.Parse(dataUrl) + if err != nil { + return false, xerrors.Errorf("parsing data URL: %w", err) + } + + if goUrl.Scheme == "pieceref" { + // url is to a piece reference + + refNum, err := strconv.ParseInt(goUrl.Opaque, 10, 64) + if err != nil { + return false, xerrors.Errorf("parsing piece reference number: %w", err) + } + + // get pieceID + var pieceID []struct { + PieceID storiface.PieceNumber `db:"piece_id"` + } + err = t.db.Select(ctx, &pieceID, `SELECT piece_id FROM parked_piece_refs WHERE ref_id = $1`, refNum) + if err != nil { + return false, xerrors.Errorf("getting pieceID: %w", err) + } + + if len(pieceID) != 1 { + return false, xerrors.Errorf("expected 1 pieceID, got %d", len(pieceID)) + } + + pr, err := t.sc.PieceReader(ctx, pieceID[0].PieceID) + if err != nil { + return false, xerrors.Errorf("getting piece reader: %w", err) + } + + closers = append(closers, pr) + + pieceReaders[i], _ = padreader.New(pr, uint64(*p.DataRawSize)) + } else { + pieceReaders[i], _ = padreader.New(&UrlPieceReader{ + Url: dataUrl, + RawSize: *p.DataRawSize, + }, uint64(*p.DataRawSize)) + } + } else { // padding piece (w/o fr32 padding, added in TreeD) pieceReaders[i] = nullreader.NewNullReader(abi.PaddedPieceSize(p.PieceSize).Unpadded()) } diff --git a/storage/paths/remote.go b/storage/paths/remote.go index 882c98c4a..dfceaeace 100644 --- a/storage/paths/remote.go +++ b/storage/paths/remote.go @@ -747,6 +747,46 @@ func (r *Remote) Reader(ctx context.Context, s storiface.SectorRef, offset, size return nil, nil } +func (r *Remote) ReaderSeq(ctx context.Context, s storiface.SectorRef, ft storiface.SectorFileType) (io.ReadCloser, error) { + paths, _, err := r.local.AcquireSector(ctx, s, ft, storiface.FTNone, storiface.PathStorage, storiface.AcquireMove) + if err != nil { + return nil, xerrors.Errorf("acquire local: %w", err) + } + + path := storiface.PathByType(paths, ft) + if path != "" { + return os.Open(path) + } + + si, err := r.index.StorageFindSector(ctx, s.ID, ft, 0, false) + if err != nil { + log.Debugf("Reader, did not find file on any of the workers %s (%s)", path, ft.String()) + return nil, err + } + + if len(si) == 0 { + return nil, xerrors.Errorf("failed to read sector %v from remote(%d): %w", s, ft, storiface.ErrSectorNotFound) + } + + sort.Slice(si, func(i, j int) bool { + return si[i].Weight > si[j].Weight + }) + + for _, info := range si { + for _, url := range info.URLs { + rd, err := r.readRemote(ctx, url, 0, 0) + if err != nil { + log.Warnw("reading from remote", "url", url, "error", err) + continue + } + + return rd, err + } + } + + return nil, xerrors.Errorf("failed to read sector %v from remote(%d): %w", s, ft, storiface.ErrSectorNotFound) +} + func (r *Remote) Reserve(ctx context.Context, sid storiface.SectorRef, ft storiface.SectorFileType, storageIDs storiface.SectorPaths, overheadTab map[storiface.SectorFileType]int) (func(), error) { log.Warnf("reserve called on remote store, sectorID: %v", sid.ID) return func() { From 10453cd5afa515117c39cee087849e4522ec4151 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 1 Mar 2024 00:23:30 +0100 Subject: [PATCH 251/394] lpseal: Rm piecepark refs in finalize --- provider/lpseal/finalize_pieces.go | 51 ++++++++++++++++++++++++++++++ provider/lpseal/task_finalize.go | 4 +++ 2 files changed, 55 insertions(+) create mode 100644 provider/lpseal/finalize_pieces.go diff --git a/provider/lpseal/finalize_pieces.go b/provider/lpseal/finalize_pieces.go new file mode 100644 index 000000000..8d52bf331 --- /dev/null +++ b/provider/lpseal/finalize_pieces.go @@ -0,0 +1,51 @@ +package lpseal + +import ( + "context" + "net/url" + "strconv" + + "golang.org/x/xerrors" + + "github.com/filecoin-project/go-state-types/abi" + + "github.com/filecoin-project/lotus/lib/harmony/harmonydb" +) + +func DropSectorPieceRefs(ctx context.Context, db *harmonydb.DB, sid abi.SectorID) error { + //_, err := db.Exec(ctx, `SELECT FROM sectors_sdr_initial_pieces WHERE sp_id = $1 AND sector_number = $2`, sid.Miner, sid.Number) + + var PieceURL []struct { + URL string `db:"data_url"` + } + + err := db.Select(ctx, &PieceURL, `SELECT data_url FROM sectors_sdr_initial_pieces WHERE sp_id = $1 AND sector_number = $2`, sid.Miner, sid.Number) + if err != nil { + return xerrors.Errorf("getting piece url: %w", err) + } + + for _, pu := range PieceURL { + gourl, err := url.Parse(pu.URL) + if err != nil { + log.Errorw("failed to parse piece url", "url", pu.URL, "error", err, "miner", sid.Miner, "sector", sid.Number) + continue + } + + if gourl.Scheme == "pieceref" { + refID, err := strconv.ParseInt(gourl.Opaque, 10, 64) + if err != nil { + log.Errorw("failed to parse piece ref id", "url", pu.URL, "error", err, "miner", sid.Miner, "sector", sid.Number) + continue + } + + n, err := db.Exec(ctx, `DELETE FROM parked_piece_refs WHERE ref_id = $1`, refID) + if err != nil { + log.Errorw("failed to delete piece ref", "url", pu.URL, "error", err, "miner", sid.Miner, "sector", sid.Number) + } + + log.Debugw("deleted piece ref", "url", pu.URL, "miner", sid.Miner, "sector", sid.Number, "rows", n) + } + } + + return err +} diff --git a/provider/lpseal/task_finalize.go b/provider/lpseal/task_finalize.go index 8d425f76a..6246af733 100644 --- a/provider/lpseal/task_finalize.go +++ b/provider/lpseal/task_finalize.go @@ -69,6 +69,10 @@ func (f *FinalizeTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (do return false, xerrors.Errorf("finalizing sector: %w", err) } + if err := DropSectorPieceRefs(ctx, f.db, sector.ID); err != nil { + return false, xerrors.Errorf("dropping sector piece refs: %w", err) + } + // set after_finalize _, err = f.db.Exec(ctx, `update sectors_sdr_pipeline set after_finalize=true where task_id_finalize=$1`, taskID) if err != nil { From e93a3e0d4a6c0f25c3494ac5976521a9464afb35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 11 Mar 2024 21:35:16 +0100 Subject: [PATCH 252/394] lppiece: GC task --- cmd/lotus-provider/tasks/tasks.go | 3 +- .../harmonydb/sql/20240228-piece-park.sql | 2 + provider/lpffi/piece_funcs.go | 4 + provider/lppiece/task_cleanup_piece.go | 130 ++++++++++++++++++ 4 files changed, 138 insertions(+), 1 deletion(-) create mode 100644 provider/lppiece/task_cleanup_piece.go diff --git a/cmd/lotus-provider/tasks/tasks.go b/cmd/lotus-provider/tasks/tasks.go index 71e794545..309cebd01 100644 --- a/cmd/lotus-provider/tasks/tasks.go +++ b/cmd/lotus-provider/tasks/tasks.go @@ -75,7 +75,8 @@ func StartTasks(ctx context.Context, dependencies *deps.Deps) (*harmonytask.Task // Piece handling if cfg.Subsystems.EnableParkPiece { parkPieceTask := lppiece.NewParkPieceTask(db, must.One(slrLazy.Val()), cfg.Subsystems.ParkPieceMaxTasks) - activeTasks = append(activeTasks, parkPieceTask) + cleanupPieceTask := lppiece.NewCleanupPieceTask(db, must.One(slrLazy.Val()), 0) + activeTasks = append(activeTasks, parkPieceTask, cleanupPieceTask) } } diff --git a/lib/harmony/harmonydb/sql/20240228-piece-park.sql b/lib/harmony/harmonydb/sql/20240228-piece-park.sql index ebba68fae..b07fd3973 100644 --- a/lib/harmony/harmonydb/sql/20240228-piece-park.sql +++ b/lib/harmony/harmonydb/sql/20240228-piece-park.sql @@ -9,6 +9,8 @@ create table parked_pieces ( complete boolean not null default false, task_id bigint default null, + cleanup_task_id bigint default null, + foreign key (task_id) references harmony_task (id) on delete set null ); diff --git a/provider/lpffi/piece_funcs.go b/provider/lpffi/piece_funcs.go index 986d0fe4d..9ffc0b3d1 100644 --- a/provider/lpffi/piece_funcs.go +++ b/provider/lpffi/piece_funcs.go @@ -69,3 +69,7 @@ func (sb *SealCalls) WritePiece(ctx context.Context, pieceID storiface.PieceNumb func (sb *SealCalls) PieceReader(ctx context.Context, id storiface.PieceNumber) (io.ReadCloser, error) { return sb.sectors.storage.ReaderSeq(ctx, id.Ref(), storiface.FTPiece) } + +func (sb *SealCalls) RemovePiece(ctx context.Context, id storiface.PieceNumber) error { + return sb.sectors.storage.Remove(ctx, id.Ref().ID, storiface.FTPiece, true, nil) +} diff --git a/provider/lppiece/task_cleanup_piece.go b/provider/lppiece/task_cleanup_piece.go new file mode 100644 index 000000000..d7f24037a --- /dev/null +++ b/provider/lppiece/task_cleanup_piece.go @@ -0,0 +1,130 @@ +package lppiece + +import ( + "context" + "time" + + "golang.org/x/xerrors" + + "github.com/filecoin-project/lotus/lib/harmony/harmonydb" + "github.com/filecoin-project/lotus/lib/harmony/harmonytask" + "github.com/filecoin-project/lotus/lib/harmony/resources" + "github.com/filecoin-project/lotus/lib/promise" + "github.com/filecoin-project/lotus/provider/lpffi" + "github.com/filecoin-project/lotus/storage/sealer/storiface" +) + +type CleanupPieceTask struct { + max int + db *harmonydb.DB + sc *lpffi.SealCalls + + TF promise.Promise[harmonytask.AddTaskFunc] +} + +func NewCleanupPieceTask(db *harmonydb.DB, sc *lpffi.SealCalls, max int) *CleanupPieceTask { + pt := &CleanupPieceTask{ + db: db, + sc: sc, + + max: max, + } + go pt.pollCleanupTasks(context.Background()) + return pt +} + +func (c *CleanupPieceTask) pollCleanupTasks(ctx context.Context) { + for { + // select pieces with no refs and null cleanup_task_id + var pieceIDs []struct { + ID storiface.PieceNumber `db:"id"` + } + + err := c.db.Select(ctx, &pieceIDs, `SELECT id FROM parked_pieces WHERE cleanup_task_id IS NULL AND (SELECT count(*) FROM parked_piece_refs WHERE piece_id = parked_pieces.id) = 0`) + if err != nil { + log.Errorf("failed to get parked pieces: %s", err) + time.Sleep(PieceParkPollInterval) + continue + } + + if len(pieceIDs) == 0 { + time.Sleep(PieceParkPollInterval) + continue + } + + for _, pieceID := range pieceIDs { + pieceID := pieceID + + // create a task for each piece + c.TF.Val(ctx)(func(id harmonytask.TaskID, tx *harmonydb.Tx) (shouldCommit bool, err error) { + // update + n, err := tx.Exec(`UPDATE parked_pieces SET cleanup_task_id = $1 WHERE id = $2 AND (SELECT count(*) FROM parked_piece_refs WHERE piece_id = parked_pieces.id) = 0`, id, pieceID.ID) + if err != nil { + return false, xerrors.Errorf("updating parked piece: %w", err) + } + + // commit only if we updated the piece + return n > 0, nil + }) + } + } +} + +func (c *CleanupPieceTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (done bool, err error) { + ctx := context.Background() + + // select by cleanup_task_id + var pieceID int64 + + err = c.db.QueryRow(ctx, "SELECT piece_id FROM parked_pieces WHERE cleanup_task_id = $1", taskID).Scan(&pieceID) + if err != nil { + return false, xerrors.Errorf("query parked_piece: %w", err) + } + + // delete from parked_pieces where id = $1 where ref count = 0 + // note: we delete from the db first because that guarantees that the piece is no longer in use + // if storage delete fails, it will be retried later is other cleanup tasks + n, err := c.db.Exec(ctx, "DELETE FROM parked_pieces WHERE id = $1 AND (SELECT count(*) FROM parked_piece_refs WHERE piece_id = $1) = 0", pieceID) + if err != nil { + return false, xerrors.Errorf("delete parked_piece: %w", err) + } + + if n == 0 { + return true, nil + } + + // remove from storage + err = c.sc.RemovePiece(ctx, storiface.PieceNumber(pieceID)) + if err != nil { + log.Errorw("remove piece", "piece_id", pieceID, "error", err) + } + + return true, nil +} + +func (c *CleanupPieceTask) CanAccept(ids []harmonytask.TaskID, engine *harmonytask.TaskEngine) (*harmonytask.TaskID, error) { + // the remove call runs on paths.Remote storage, so it doesn't really matter where it runs + + id := ids[0] + return &id, nil +} + +func (c *CleanupPieceTask) TypeDetails() harmonytask.TaskTypeDetails { + return harmonytask.TaskTypeDetails{ + Max: c.max, + Name: "DropPiece", + Cost: resources.Resources{ + Cpu: 1, + Gpu: 0, + Ram: 64 << 20, + Storage: nil, + }, + MaxFailures: 10, + } +} + +func (c *CleanupPieceTask) Adder(taskFunc harmonytask.AddTaskFunc) { + c.TF.Set(taskFunc) +} + +var _ harmonytask.TaskInterface = &CleanupPieceTask{} From 27317a94896f93a6bec14e2af3c23c0bcb2c1971 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 14 Mar 2024 12:24:00 +0100 Subject: [PATCH 253/394] fix 20240228-piece-park.sql --- lib/harmony/harmonydb/sql/20240228-piece-park.sql | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/harmony/harmonydb/sql/20240228-piece-park.sql b/lib/harmony/harmonydb/sql/20240228-piece-park.sql index b07fd3973..8b6719dc5 100644 --- a/lib/harmony/harmonydb/sql/20240228-piece-park.sql +++ b/lib/harmony/harmonydb/sql/20240228-piece-park.sql @@ -2,7 +2,7 @@ create table parked_pieces ( id bigserial primary key, created_at timestamp default current_timestamp, - piece_cid text not null unique constraint parked_pieces_piece_cid_key, + piece_cid text not null, piece_padded_size bigint not null, piece_raw_size text not null, @@ -11,7 +11,8 @@ create table parked_pieces ( cleanup_task_id bigint default null, - foreign key (task_id) references harmony_task (id) on delete set null + foreign key (task_id) references harmony_task (id) on delete set null, + unique (piece_cid) ); /* From 465ec58a7c7a705321b331ace7947731dd96ca07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 14 Mar 2024 13:03:14 +0100 Subject: [PATCH 254/394] lpdeal: Fix adapter deadlock with duplicate pieces --- cmd/lotus-shed/lpdeal.go | 56 +++++++++++++++---- .../harmonydb/sql/20240228-piece-park.sql | 2 +- 2 files changed, 47 insertions(+), 11 deletions(-) diff --git a/cmd/lotus-shed/lpdeal.go b/cmd/lotus-shed/lpdeal.go index 0a585bf66..b6237e13f 100644 --- a/cmd/lotus-shed/lpdeal.go +++ b/cmd/lotus-shed/lpdeal.go @@ -3,6 +3,7 @@ package main import ( "bytes" "context" + "database/sql" "fmt" "io" "net" @@ -452,6 +453,8 @@ var lpBoostProxyCmd = &cli.Command{ pieceUUID := uuid.New() + color.Blue("%s %s piece assign request with id %s", deal.DealProposal.PieceCID, deal.DealProposal.Provider, pieceUUID) + pieceInfoLk.Lock() pieceInfos[pieceUUID] = append(pieceInfos[pieceUUID], pi) pieceInfoLk.Unlock() @@ -464,23 +467,36 @@ var lpBoostProxyCmd = &cli.Command{ // add piece entry var refID int64 + var pieceWasCreated bool comm, err := db.BeginTransaction(ctx, func(tx *harmonydb.Tx) (commit bool, err error) { - // Add parked_piece, on conflict do nothing var pieceID int64 - err = tx.QueryRow(` - INSERT INTO parked_pieces (piece_cid, piece_padded_size, piece_raw_size) - VALUES ($1, $2, $3) - ON CONFLICT (piece_cid) DO UPDATE - SET piece_cid = EXCLUDED.piece_cid - RETURNING id`, deal.DealProposal.PieceCID.String(), int64(pieceSize.Padded()), int64(pieceSize)).Scan(&pieceID) + // Attempt to select the piece ID first + err = tx.QueryRow(`SELECT id FROM parked_pieces WHERE piece_cid = $1`, deal.DealProposal.PieceCID.String()).Scan(&pieceID) + if err != nil { - return false, xerrors.Errorf("upserting parked piece and getting id: %w", err) + if err == sql.ErrNoRows { + // Piece does not exist, attempt to insert + err = tx.QueryRow(` + INSERT INTO parked_pieces (piece_cid, piece_padded_size, piece_raw_size) + VALUES ($1, $2, $3) + ON CONFLICT (piece_cid) DO NOTHING + RETURNING id`, deal.DealProposal.PieceCID.String(), int64(pieceSize.Padded()), int64(pieceSize)).Scan(&pieceID) + if err != nil { + return false, xerrors.Errorf("inserting new parked piece and getting id: %w", err) + } + pieceWasCreated = true // New piece was created + } else { + // Some other error occurred during select + return false, xerrors.Errorf("checking existing parked piece: %w", err) + } + } else { + pieceWasCreated = false // Piece already exists, no new piece was created } // Add parked_piece_ref err = tx.QueryRow(`INSERT INTO parked_piece_refs (piece_id, data_url) - VALUES ($1, $2) RETURNING ref_id`, pieceID, dataUrl.String()).Scan(&refID) + VALUES ($1, $2) RETURNING ref_id`, pieceID, dataUrl.String()).Scan(&refID) if err != nil { return false, xerrors.Errorf("inserting parked piece ref: %w", err) } @@ -496,8 +512,28 @@ var lpBoostProxyCmd = &cli.Command{ } // wait for piece to be parked + if pieceWasCreated { + <-pi.done + } else { + // If the piece was not created, we need to close the done channel + close(pi.done) - <-pi.done + go func() { + // close the data reader (drain to eof if it's not a closer) + if closer, ok := pieceData.(io.Closer); ok { + if err := closer.Close(); err != nil { + log.Warnw("closing pieceData in DataCid", "error", err) + } + } else { + log.Warnw("pieceData is not an io.Closer", "type", fmt.Sprintf("%T", pieceData)) + + _, err := io.Copy(io.Discard, pieceData) + if err != nil { + log.Warnw("draining pieceData in DataCid", "error", err) + } + } + }() + } pieceIDUrl := url.URL{ Scheme: "pieceref", diff --git a/lib/harmony/harmonydb/sql/20240228-piece-park.sql b/lib/harmony/harmonydb/sql/20240228-piece-park.sql index 8b6719dc5..95792628a 100644 --- a/lib/harmony/harmonydb/sql/20240228-piece-park.sql +++ b/lib/harmony/harmonydb/sql/20240228-piece-park.sql @@ -4,7 +4,7 @@ create table parked_pieces ( piece_cid text not null, piece_padded_size bigint not null, - piece_raw_size text not null, + piece_raw_size bigint not null, complete boolean not null default false, task_id bigint default null, From b123e700eaed4045e05a55b8673b3165474470cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 14 Mar 2024 13:14:00 +0100 Subject: [PATCH 255/394] fix curio AcquireSector with reservations --- provider/lpffi/sdr_funcs.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/provider/lpffi/sdr_funcs.go b/provider/lpffi/sdr_funcs.go index 77019e4d2..6ebcd1faf 100644 --- a/provider/lpffi/sdr_funcs.go +++ b/provider/lpffi/sdr_funcs.go @@ -69,7 +69,7 @@ func (l *storageProvider) AcquireSector(ctx context.Context, taskID *harmonytask if taskID != nil { resv, ok = l.storageReservations.Load(*taskID) } - if ok { + if ok && resv != nil { if resv.Alloc != allocate || resv.Existing != existing { // this should never happen, only when task definition is wrong return storiface.SectorPaths{}, nil, xerrors.Errorf("storage reservation type mismatch") @@ -78,6 +78,7 @@ func (l *storageProvider) AcquireSector(ctx context.Context, taskID *harmonytask log.Debugw("using existing storage reservation", "task", taskID, "sector", sector, "existing", existing, "allocate", allocate) paths = resv.Paths + storageIDs = resv.PathIDs releaseStorage = resv.Release } else { var err error From 6b361443ba8c0d20d4045c47d07e05b62d4b5ed0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 14 Mar 2024 14:29:33 +0100 Subject: [PATCH 256/394] sdrtrees: Fix min expiration math --- provider/lpseal/task_submit_precommit.go | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/provider/lpseal/task_submit_precommit.go b/provider/lpseal/task_submit_precommit.go index 9f6233f39..f7229a125 100644 --- a/provider/lpseal/task_submit_precommit.go +++ b/provider/lpseal/task_submit_precommit.go @@ -9,12 +9,15 @@ import ( "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/abi" + actorstypes "github.com/filecoin-project/go-state-types/actors" "github.com/filecoin-project/go-state-types/big" "github.com/filecoin-project/go-state-types/builtin" miner12 "github.com/filecoin-project/go-state-types/builtin/v12/miner" + "github.com/filecoin-project/go-state-types/network" "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/chain/actors/builtin/miner" + "github.com/filecoin-project/lotus/chain/actors/policy" "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/lib/harmony/harmonydb" "github.com/filecoin-project/lotus/lib/harmony/harmonytask" @@ -27,6 +30,7 @@ import ( type SubmitPrecommitTaskApi interface { StateMinerPreCommitDepositForPower(context.Context, address.Address, miner.SectorPreCommitInfo, types.TipSetKey) (big.Int, error) StateMinerInfo(context.Context, address.Address, types.TipSetKey) (api.MinerInfo, error) + StateNetworkVersion(context.Context, types.TipSetKey) (network.Version, error) ctladdr.NodeApi } @@ -136,6 +140,23 @@ func (s *SubmitPrecommitTask) Do(taskID harmonytask.TaskID, stillOwned func() bo } } + nv, err := s.api.StateNetworkVersion(ctx, types.EmptyTSK) + if err != nil { + return false, xerrors.Errorf("getting network version: %w", err) + } + av, err := actorstypes.VersionForNetwork(nv) + if err != nil { + return false, xerrors.Errorf("failed to get actors version: %w", err) + } + msd, err := policy.GetMaxProveCommitDuration(av, sectorParams.RegSealProof) + if err != nil { + return false, xerrors.Errorf("failed to get max prove commit duration: %w", err) + } + + if minExpiration := sectorParams.TicketEpoch + policy.MaxPreCommitRandomnessLookback + msd + miner.MinSectorExpiration; params.Sectors[0].Expiration < minExpiration { + params.Sectors[0].Expiration = minExpiration + } + var pbuf bytes.Buffer if err := params.MarshalCBOR(&pbuf); err != nil { return false, xerrors.Errorf("serializing params: %w", err) From ad37cf5ead5a7269d61e6c0dfa152474139b0dff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 14 Mar 2024 14:37:27 +0100 Subject: [PATCH 257/394] harmony: Dev env var to override gpu count --- lib/harmony/resources/getGPU.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/lib/harmony/resources/getGPU.go b/lib/harmony/resources/getGPU.go index 3489e7491..62d5c091e 100644 --- a/lib/harmony/resources/getGPU.go +++ b/lib/harmony/resources/getGPU.go @@ -4,12 +4,23 @@ package resources import ( + "os" + "strconv" "strings" ffi "github.com/filecoin-project/filecoin-ffi" ) func getGPUDevices() float64 { // GPU boolean + if nstr := os.Getenv("HARMONY_OVERRIDE_GPUS"); nstr != "" { + n, err := strconv.ParseFloat(nstr, 64) + if err != nil { + logger.Errorf("parsing HARMONY_OVERRIDE_GPUS failed: %+v", err) + } else { + return n + } + } + gpus, err := ffi.GetGPUDevices() logger.Infow("GPUs", "list", gpus) if err != nil { From 4bed223e3028461271bb188029235a15536bc66a Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Thu, 14 Mar 2024 12:40:31 -0400 Subject: [PATCH 258/394] fix: shed: backfill events from correct starting height (#11720) --- cmd/lotus-shed/indexes.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/lotus-shed/indexes.go b/cmd/lotus-shed/indexes.go index 620933e25..12ebe0082 100644 --- a/cmd/lotus-shed/indexes.go +++ b/cmd/lotus-shed/indexes.go @@ -72,7 +72,7 @@ var backfillEventsCmd = &cli.Command{ } if cctx.IsSet("from") { // we need to fetch the tipset after the epoch being specified since we will need to advance currTs - currTs, err = api.ChainGetTipSetByHeight(ctx, abi.ChainEpoch(cctx.Int("from")+1), currTs.Key()) + currTs, err = api.ChainGetTipSetAfterHeight(ctx, abi.ChainEpoch(cctx.Int("from")+1), currTs.Key()) if err != nil { return err } From b134f3ff639c226ef50eff330ebaee1d59cfac39 Mon Sep 17 00:00:00 2001 From: Jiaying Wang <42981373+jennijuju@users.noreply.github.com> Date: Fri, 15 Mar 2024 02:00:53 +0800 Subject: [PATCH 259/394] Update CODEOWNERS (#11688) --- .github/CODEOWNERS | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index c6570734a..5eeba24f2 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,6 +1,3 @@ # Reference # https://docs.github.com/en/github/creating-cloning-and-archiving-repositories/creating-a-repository-on-github/about-code-owners -# Global owners -# Ensure maintainers team is a requested reviewer for non-draft PRs -* @jennijuju @Stebalien @snadrus @aarshkshah1992 @magik6k @zenground0 @arajasek @rjan90 @masih @rvagg From 277cbf9229793e859e6fa7337bdeb76e4d435800 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Thu, 14 Mar 2024 14:04:13 -0400 Subject: [PATCH 260/394] fix: stmgr: do not use cached migration results if absent from the blockstore (#11663) --- chain/stmgr/forks.go | 10 +++++++++- chain/stmgr/stmgr.go | 4 ++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/chain/stmgr/forks.go b/chain/stmgr/forks.go index 6d6f9ef65..9a2361961 100644 --- a/chain/stmgr/forks.go +++ b/chain/stmgr/forks.go @@ -182,7 +182,15 @@ func (sm *StateManager) HandleStateForks(ctx context.Context, root cid.Cid, heig if err == nil { if ok { log.Infow("CACHED migration", "height", height, "from", root, "to", migCid) - return migCid, nil + foundMigratedRoot, err := sm.ChainStore().StateBlockstore().Has(ctx, migCid) + if err != nil { + log.Errorw("failed to check whether previous migration result is present", "err", err) + } else if !foundMigratedRoot { + log.Errorw("cached migration result not found in blockstore, running migration again") + u.migrationResultCache.Delete(ctx, root) + } else { + return migCid, nil + } } } else if !errors.Is(err, datastore.ErrNotFound) { log.Errorw("failed to lookup previous migration result", "err", err) diff --git a/chain/stmgr/stmgr.go b/chain/stmgr/stmgr.go index 49913e442..cd35d6d19 100644 --- a/chain/stmgr/stmgr.go +++ b/chain/stmgr/stmgr.go @@ -113,6 +113,10 @@ func (m *migrationResultCache) Store(ctx context.Context, root cid.Cid, resultCi return nil } +func (m *migrationResultCache) Delete(ctx context.Context, root cid.Cid) { + _ = m.ds.Delete(ctx, m.keyForMigration(root)) +} + type Executor interface { NewActorRegistry() *vm.ActorRegistry ExecuteTipSet(ctx context.Context, sm *StateManager, ts *types.TipSet, em ExecMonitor, vmTracing bool) (stateroot cid.Cid, rectsroot cid.Cid, err error) From b909db394ed1a694421ffc1a6a9d41c1dff27207 Mon Sep 17 00:00:00 2001 From: qwdsds <50311944+qwdsds@users.noreply.github.com> Date: Fri, 15 Mar 2024 03:12:02 +0800 Subject: [PATCH 261/394] feat: add StateMinerDeadlines to gateway (#11700) Add StateMinerDeadlines to gateway. fixes #11693 --- api/api_gateway.go | 1 + api/proxy_gen.go | 13 + build/openrpc/full.json | 486 ++++++++++++++++++------------------- build/openrpc/gateway.json | 281 ++++++++++++++------- build/openrpc/miner.json | 260 ++++++++++---------- build/openrpc/worker.json | 74 +++--- 6 files changed, 611 insertions(+), 504 deletions(-) diff --git a/api/api_gateway.go b/api/api_gateway.go index e71a8b712..25187a679 100644 --- a/api/api_gateway.go +++ b/api/api_gateway.go @@ -77,6 +77,7 @@ type Gateway interface { StateMarketBalance(ctx context.Context, addr address.Address, tsk types.TipSetKey) (MarketBalance, error) StateMarketStorageDeal(ctx context.Context, dealId abi.DealID, tsk types.TipSetKey) (*MarketDeal, error) StateMinerInfo(ctx context.Context, actor address.Address, tsk types.TipSetKey) (MinerInfo, error) + StateMinerDeadlines(context.Context, address.Address, types.TipSetKey) ([]Deadline, error) StateMinerProvingDeadline(ctx context.Context, addr address.Address, tsk types.TipSetKey) (*dline.Info, error) StateMinerPower(context.Context, address.Address, types.TipSetKey) (*MinerPower, error) StateNetworkName(context.Context) (dtypes.NetworkName, error) diff --git a/api/proxy_gen.go b/api/proxy_gen.go index bd33ae445..6e809b763 100644 --- a/api/proxy_gen.go +++ b/api/proxy_gen.go @@ -807,6 +807,8 @@ type GatewayMethods struct { StateMarketStorageDeal func(p0 context.Context, p1 abi.DealID, p2 types.TipSetKey) (*MarketDeal, error) `` + StateMinerDeadlines func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) ([]Deadline, error) `` + StateMinerInfo func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (MinerInfo, error) `` StateMinerPower func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (*MinerPower, error) `` @@ -5125,6 +5127,17 @@ func (s *GatewayStub) StateMarketStorageDeal(p0 context.Context, p1 abi.DealID, return nil, ErrNotSupported } +func (s *GatewayStruct) StateMinerDeadlines(p0 context.Context, p1 address.Address, p2 types.TipSetKey) ([]Deadline, error) { + if s.Internal.StateMinerDeadlines == nil { + return *new([]Deadline), ErrNotSupported + } + return s.Internal.StateMinerDeadlines(p0, p1, p2) +} + +func (s *GatewayStub) StateMinerDeadlines(p0 context.Context, p1 address.Address, p2 types.TipSetKey) ([]Deadline, error) { + return *new([]Deadline), ErrNotSupported +} + func (s *GatewayStruct) StateMinerInfo(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (MinerInfo, error) { if s.Internal.StateMinerInfo == nil { return *new(MinerInfo), ErrNotSupported diff --git a/build/openrpc/full.json b/build/openrpc/full.json index fec670a41..fa1d019b6 100644 --- a/build/openrpc/full.json +++ b/build/openrpc/full.json @@ -37,7 +37,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1498" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1500" } }, { @@ -60,7 +60,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1509" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1511" } }, { @@ -103,7 +103,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1520" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1522" } }, { @@ -214,7 +214,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1542" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1544" } }, { @@ -454,7 +454,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1553" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1555" } }, { @@ -685,7 +685,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1564" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1566" } }, { @@ -784,7 +784,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1575" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1577" } }, { @@ -816,7 +816,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1586" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1588" } }, { @@ -922,7 +922,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1597" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1599" } }, { @@ -1019,7 +1019,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1608" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1610" } }, { @@ -1078,7 +1078,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1619" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1621" } }, { @@ -1171,7 +1171,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1630" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1632" } }, { @@ -1255,7 +1255,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1641" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1643" } }, { @@ -1355,7 +1355,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1652" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1654" } }, { @@ -1411,7 +1411,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1663" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1665" } }, { @@ -1484,7 +1484,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1674" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1676" } }, { @@ -1557,7 +1557,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1685" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1687" } }, { @@ -1604,7 +1604,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1696" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1698" } }, { @@ -1636,7 +1636,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1707" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1709" } }, { @@ -1691,7 +1691,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1718" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1720" } }, { @@ -1743,7 +1743,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1740" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1742" } }, { @@ -1780,7 +1780,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1751" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1753" } }, { @@ -1827,7 +1827,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1762" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1764" } }, { @@ -1874,7 +1874,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1773" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1775" } }, { @@ -1954,7 +1954,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1784" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1786" } }, { @@ -2006,7 +2006,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1795" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1797" } }, { @@ -2065,7 +2065,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1806" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1808" } }, { @@ -2136,7 +2136,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1817" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1819" } }, { @@ -2177,7 +2177,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1828" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1830" } }, { @@ -2245,7 +2245,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1850" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1852" } }, { @@ -2306,7 +2306,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1861" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1863" } }, { @@ -2413,7 +2413,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1872" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1874" } }, { @@ -2569,7 +2569,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1883" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1885" } }, { @@ -2635,7 +2635,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1894" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1896" } }, { @@ -2976,7 +2976,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1905" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1907" } }, { @@ -3021,7 +3021,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1916" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1918" } }, { @@ -3068,7 +3068,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1949" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1951" } }, { @@ -3139,7 +3139,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1960" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1962" } }, { @@ -3282,7 +3282,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1971" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1973" } }, { @@ -3612,7 +3612,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1982" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1984" } }, { @@ -3680,7 +3680,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1993" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1995" } }, { @@ -3914,7 +3914,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2004" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2006" } }, { @@ -4077,7 +4077,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2015" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2017" } }, { @@ -4160,7 +4160,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2026" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2028" } }, { @@ -4201,7 +4201,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2037" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2039" } }, { @@ -4272,7 +4272,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2048" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2050" } }, { @@ -4416,7 +4416,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2059" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2061" } }, { @@ -4456,7 +4456,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2070" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2072" } }, { @@ -4497,7 +4497,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2081" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2083" } }, { @@ -4622,7 +4622,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2092" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2094" } }, { @@ -4747,7 +4747,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2103" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2105" } }, { @@ -4786,7 +4786,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2114" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2116" } }, { @@ -4833,7 +4833,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2125" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2127" } }, { @@ -4888,7 +4888,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2136" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2138" } }, { @@ -4917,7 +4917,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2147" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2149" } }, { @@ -5054,7 +5054,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2158" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2160" } }, { @@ -5083,7 +5083,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2169" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2171" } }, { @@ -5137,7 +5137,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2180" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2182" } }, { @@ -5228,7 +5228,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2191" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2193" } }, { @@ -5256,7 +5256,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2202" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2204" } }, { @@ -5346,7 +5346,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2213" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2215" } }, { @@ -5602,7 +5602,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2224" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2226" } }, { @@ -5847,7 +5847,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2235" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2237" } }, { @@ -5903,7 +5903,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2246" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2248" } }, { @@ -5950,7 +5950,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2257" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2259" } }, { @@ -6048,7 +6048,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2268" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2270" } }, { @@ -6114,7 +6114,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2279" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2281" } }, { @@ -6180,7 +6180,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2290" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2292" } }, { @@ -6289,7 +6289,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2301" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2303" } }, { @@ -6347,7 +6347,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2312" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2314" } }, { @@ -6469,7 +6469,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2323" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2325" } }, { @@ -6673,7 +6673,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2334" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2336" } }, { @@ -6868,7 +6868,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2345" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2347" } }, { @@ -7055,7 +7055,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2356" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2358" } }, { @@ -7259,7 +7259,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2367" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2369" } }, { @@ -7350,7 +7350,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2378" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2380" } }, { @@ -7408,7 +7408,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2389" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2391" } }, { @@ -7666,7 +7666,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2400" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2402" } }, { @@ -7941,7 +7941,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2411" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2413" } }, { @@ -7969,7 +7969,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2422" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2424" } }, { @@ -8007,7 +8007,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2433" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2435" } }, { @@ -8115,7 +8115,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2444" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2446" } }, { @@ -8153,7 +8153,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2455" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2457" } }, { @@ -8182,7 +8182,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2466" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2468" } }, { @@ -8245,7 +8245,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2477" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2479" } }, { @@ -8308,7 +8308,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2488" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2490" } }, { @@ -8353,7 +8353,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2499" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2501" } }, { @@ -8475,7 +8475,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2510" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2512" } }, { @@ -8630,7 +8630,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2521" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2523" } }, { @@ -8684,7 +8684,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2532" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2534" } }, { @@ -8738,7 +8738,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2543" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2545" } }, { @@ -8793,7 +8793,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2554" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2556" } }, { @@ -8936,7 +8936,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2565" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2567" } }, { @@ -9063,7 +9063,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2576" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2578" } }, { @@ -9165,7 +9165,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2587" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2589" } }, { @@ -9388,7 +9388,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2598" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2600" } }, { @@ -9571,7 +9571,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2609" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2611" } }, { @@ -9651,7 +9651,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2620" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2622" } }, { @@ -9696,7 +9696,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2631" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2633" } }, { @@ -9752,7 +9752,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2642" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2644" } }, { @@ -9832,7 +9832,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2653" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2655" } }, { @@ -9912,7 +9912,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2664" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2666" } }, { @@ -10397,7 +10397,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2675" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2677" } }, { @@ -10591,7 +10591,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2686" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2688" } }, { @@ -10746,7 +10746,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2697" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2699" } }, { @@ -10995,7 +10995,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2708" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2710" } }, { @@ -11150,7 +11150,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2719" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2721" } }, { @@ -11327,7 +11327,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2730" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2732" } }, { @@ -11425,7 +11425,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2741" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2743" } }, { @@ -11590,7 +11590,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2752" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2754" } }, { @@ -11629,7 +11629,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2763" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2765" } }, { @@ -11694,7 +11694,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2774" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2776" } }, { @@ -11740,7 +11740,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2785" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2787" } }, { @@ -11890,7 +11890,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2796" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2798" } }, { @@ -12027,7 +12027,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2807" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2809" } }, { @@ -12258,7 +12258,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2818" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2820" } }, { @@ -12395,7 +12395,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2829" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2831" } }, { @@ -12560,7 +12560,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2840" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2842" } }, { @@ -12637,7 +12637,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2851" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2853" } }, { @@ -12832,7 +12832,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2873" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2875" } }, { @@ -13011,7 +13011,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2884" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2886" } }, { @@ -13173,7 +13173,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2895" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2897" } }, { @@ -13321,7 +13321,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2906" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2908" } }, { @@ -13549,7 +13549,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2917" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2919" } }, { @@ -13697,7 +13697,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2928" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2930" } }, { @@ -13909,7 +13909,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2939" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2941" } }, { @@ -14115,7 +14115,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2950" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2952" } }, { @@ -14183,7 +14183,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2961" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2963" } }, { @@ -14300,7 +14300,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2972" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2974" } }, { @@ -14391,7 +14391,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2983" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2985" } }, { @@ -14477,7 +14477,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2994" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2996" } }, { @@ -14672,7 +14672,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3005" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3007" } }, { @@ -14834,7 +14834,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3016" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3018" } }, { @@ -15030,7 +15030,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3027" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3029" } }, { @@ -15210,7 +15210,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3038" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3040" } }, { @@ -15373,7 +15373,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3049" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3051" } }, { @@ -15400,7 +15400,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3060" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3062" } }, { @@ -15427,7 +15427,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3071" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3073" } }, { @@ -15526,7 +15526,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3082" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3084" } }, { @@ -15572,7 +15572,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3093" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3095" } }, { @@ -15672,7 +15672,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3104" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3106" } }, { @@ -15788,7 +15788,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3115" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3117" } }, { @@ -15836,7 +15836,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3126" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3128" } }, { @@ -15928,7 +15928,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3137" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3139" } }, { @@ -16043,7 +16043,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3148" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3150" } }, { @@ -16091,7 +16091,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3159" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3161" } }, { @@ -16128,7 +16128,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3170" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3172" } }, { @@ -16400,7 +16400,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3181" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3183" } }, { @@ -16448,7 +16448,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3192" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3194" } }, { @@ -16506,7 +16506,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3203" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3205" } }, { @@ -16711,7 +16711,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3214" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3216" } }, { @@ -16914,7 +16914,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3225" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3227" } }, { @@ -17083,7 +17083,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3236" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3238" } }, { @@ -17287,7 +17287,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3247" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3249" } }, { @@ -17454,7 +17454,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3258" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3260" } }, { @@ -17661,7 +17661,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3269" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3271" } }, { @@ -17729,7 +17729,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3280" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3282" } }, { @@ -17781,7 +17781,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3291" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3293" } }, { @@ -17830,7 +17830,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3302" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3304" } }, { @@ -17921,7 +17921,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3313" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3315" } }, { @@ -18427,7 +18427,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3324" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3326" } }, { @@ -18533,7 +18533,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3335" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3337" } }, { @@ -18585,7 +18585,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3346" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3348" } }, { @@ -19137,7 +19137,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3357" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3359" } }, { @@ -19251,7 +19251,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3368" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3370" } }, { @@ -19348,7 +19348,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3379" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3381" } }, { @@ -19448,7 +19448,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3390" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3392" } }, { @@ -19536,7 +19536,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3401" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3403" } }, { @@ -19636,7 +19636,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3412" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3414" } }, { @@ -19723,7 +19723,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3423" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3425" } }, { @@ -19814,7 +19814,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3434" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3436" } }, { @@ -19939,7 +19939,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3445" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3447" } }, { @@ -20048,7 +20048,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3456" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3458" } }, { @@ -20118,7 +20118,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3467" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3469" } }, { @@ -20221,7 +20221,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3478" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3480" } }, { @@ -20282,7 +20282,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3489" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3491" } }, { @@ -20412,7 +20412,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3500" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3502" } }, { @@ -20519,7 +20519,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3511" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3513" } }, { @@ -20728,7 +20728,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3522" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3524" } }, { @@ -20805,7 +20805,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3533" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3535" } }, { @@ -20882,7 +20882,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3544" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3546" } }, { @@ -20991,7 +20991,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3555" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3557" } }, { @@ -21100,7 +21100,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3566" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3568" } }, { @@ -21161,7 +21161,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3577" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3579" } }, { @@ -21271,7 +21271,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3588" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3590" } }, { @@ -21332,7 +21332,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3599" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3601" } }, { @@ -21400,7 +21400,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3610" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3612" } }, { @@ -21468,7 +21468,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3621" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3623" } }, { @@ -21549,7 +21549,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3632" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3634" } }, { @@ -21698,7 +21698,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3643" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3645" } }, { @@ -21770,7 +21770,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3654" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3656" } }, { @@ -21929,7 +21929,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3665" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3667" } }, { @@ -22094,7 +22094,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3676" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3678" } }, { @@ -22164,7 +22164,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3687" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3689" } }, { @@ -22232,7 +22232,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3698" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3700" } }, { @@ -22325,7 +22325,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3709" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3711" } }, { @@ -22396,7 +22396,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3720" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3722" } }, { @@ -22597,7 +22597,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3731" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3733" } }, { @@ -22729,7 +22729,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3742" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3744" } }, { @@ -22866,7 +22866,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3753" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3755" } }, { @@ -22977,7 +22977,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3764" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3766" } }, { @@ -23109,7 +23109,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3775" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3777" } }, { @@ -23240,7 +23240,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3786" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3788" } }, { @@ -23311,7 +23311,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3797" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3799" } }, { @@ -23395,7 +23395,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3808" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3810" } }, { @@ -23481,7 +23481,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3819" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3821" } }, { @@ -23664,7 +23664,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3830" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3832" } }, { @@ -23691,7 +23691,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3841" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3843" } }, { @@ -23744,7 +23744,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3852" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3854" } }, { @@ -23832,7 +23832,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3863" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3865" } }, { @@ -24283,7 +24283,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3874" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3876" } }, { @@ -24450,7 +24450,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3885" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3887" } }, { @@ -24548,7 +24548,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3896" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3898" } }, { @@ -24721,7 +24721,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3907" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3909" } }, { @@ -24819,7 +24819,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3918" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3920" } }, { @@ -24970,7 +24970,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3929" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3931" } }, { @@ -25055,7 +25055,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3940" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3942" } }, { @@ -25123,7 +25123,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3951" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3953" } }, { @@ -25175,7 +25175,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3962" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3964" } }, { @@ -25243,7 +25243,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3973" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3975" } }, { @@ -25404,7 +25404,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3984" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3986" } }, { @@ -25451,7 +25451,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4006" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4008" } }, { @@ -25498,7 +25498,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4017" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4019" } }, { @@ -25541,7 +25541,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4039" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4041" } }, { @@ -25637,7 +25637,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4050" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4052" } }, { @@ -25903,7 +25903,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4061" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4063" } }, { @@ -25926,7 +25926,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4072" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4074" } }, { @@ -25969,7 +25969,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4083" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4085" } }, { @@ -26020,7 +26020,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4094" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4096" } }, { @@ -26065,7 +26065,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4105" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4107" } }, { @@ -26093,7 +26093,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4116" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4118" } }, { @@ -26133,7 +26133,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4127" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4129" } }, { @@ -26192,7 +26192,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4138" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4140" } }, { @@ -26236,7 +26236,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4149" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4151" } }, { @@ -26295,7 +26295,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4160" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4162" } }, { @@ -26332,7 +26332,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4171" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4173" } }, { @@ -26376,7 +26376,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4182" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4184" } }, { @@ -26416,7 +26416,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4193" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4195" } }, { @@ -26491,7 +26491,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4204" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4206" } }, { @@ -26699,7 +26699,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4215" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4217" } }, { @@ -26743,7 +26743,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4226" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4228" } }, { @@ -26833,7 +26833,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4237" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4239" } }, { @@ -26860,7 +26860,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4248" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4250" } } ] diff --git a/build/openrpc/gateway.json b/build/openrpc/gateway.json index 8c0f3e68b..834732406 100644 --- a/build/openrpc/gateway.json +++ b/build/openrpc/gateway.json @@ -242,7 +242,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4259" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4261" } }, { @@ -473,7 +473,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4270" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4272" } }, { @@ -505,7 +505,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4281" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4283" } }, { @@ -611,7 +611,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4292" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4294" } }, { @@ -704,7 +704,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4303" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4305" } }, { @@ -788,7 +788,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4314" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4316" } }, { @@ -888,7 +888,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4325" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4327" } }, { @@ -944,7 +944,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4336" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4338" } }, { @@ -1017,7 +1017,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4347" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4349" } }, { @@ -1090,7 +1090,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4358" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4360" } }, { @@ -1137,7 +1137,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4369" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4371" } }, { @@ -1169,7 +1169,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4380" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4382" } }, { @@ -1206,7 +1206,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4402" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4404" } }, { @@ -1253,7 +1253,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4413" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4415" } }, { @@ -1293,7 +1293,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4424" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4426" } }, { @@ -1340,7 +1340,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4435" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4437" } }, { @@ -1369,7 +1369,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4446" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4448" } }, { @@ -1506,7 +1506,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4457" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4459" } }, { @@ -1535,7 +1535,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4468" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4470" } }, { @@ -1589,7 +1589,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4479" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4481" } }, { @@ -1680,7 +1680,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4490" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4492" } }, { @@ -1708,7 +1708,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4501" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4503" } }, { @@ -1798,7 +1798,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4512" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4514" } }, { @@ -2054,7 +2054,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4523" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4525" } }, { @@ -2299,7 +2299,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4534" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4536" } }, { @@ -2355,7 +2355,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4545" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4547" } }, { @@ -2402,7 +2402,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4556" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4558" } }, { @@ -2500,7 +2500,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4567" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4569" } }, { @@ -2566,7 +2566,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4578" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4580" } }, { @@ -2632,7 +2632,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4589" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4591" } }, { @@ -2741,7 +2741,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4600" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4602" } }, { @@ -2799,7 +2799,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4611" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4613" } }, { @@ -2921,7 +2921,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4622" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4624" } }, { @@ -3108,7 +3108,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4633" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4635" } }, { @@ -3312,7 +3312,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4644" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4646" } }, { @@ -3403,7 +3403,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4655" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4657" } }, { @@ -3461,7 +3461,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4666" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4668" } }, { @@ -3719,7 +3719,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4677" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4679" } }, { @@ -3994,7 +3994,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4688" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4690" } }, { @@ -4022,7 +4022,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4699" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4701" } }, { @@ -4060,7 +4060,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4710" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4712" } }, { @@ -4168,7 +4168,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4721" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4723" } }, { @@ -4206,7 +4206,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4732" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4734" } }, { @@ -4235,7 +4235,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4743" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4745" } }, { @@ -4298,7 +4298,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4754" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4756" } }, { @@ -4361,7 +4361,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4765" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4767" } }, { @@ -4406,7 +4406,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4776" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4778" } }, { @@ -4528,7 +4528,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4787" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4789" } }, { @@ -4683,7 +4683,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4798" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4800" } }, { @@ -4737,7 +4737,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4809" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4811" } }, { @@ -4791,7 +4791,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4820" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4822" } }, { @@ -4893,7 +4893,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4831" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4833" } }, { @@ -5116,7 +5116,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4842" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4844" } }, { @@ -5299,7 +5299,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4853" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4855" } }, { @@ -5493,7 +5493,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4864" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4866" } }, { @@ -5539,7 +5539,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4875" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4877" } }, { @@ -5689,7 +5689,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4886" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4888" } }, { @@ -5826,7 +5826,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4897" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4899" } }, { @@ -5894,7 +5894,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4908" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4910" } }, { @@ -6011,7 +6011,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4919" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4921" } }, { @@ -6102,7 +6102,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4930" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4932" } }, { @@ -6188,7 +6188,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4941" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4943" } }, { @@ -6215,7 +6215,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4952" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4954" } }, { @@ -6242,7 +6242,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4963" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4965" } }, { @@ -6310,7 +6310,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4974" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4976" } }, { @@ -6816,7 +6816,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4985" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4987" } }, { @@ -6913,7 +6913,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4996" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4998" } }, { @@ -7013,7 +7013,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5007" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5009" } }, { @@ -7113,7 +7113,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5018" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5020" } }, { @@ -7238,7 +7238,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5029" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5031" } }, { @@ -7347,7 +7347,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5040" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5042" } }, { @@ -7450,7 +7450,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5051" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5053" } }, { @@ -7580,7 +7580,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5062" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5064" } }, { @@ -7687,7 +7687,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5073" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5075" } }, { @@ -7748,7 +7748,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5084" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5086" } }, { @@ -7816,7 +7816,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5095" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5097" } }, { @@ -7897,7 +7897,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5106" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5108" } }, { @@ -8056,7 +8056,100 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5117" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5119" + } + }, + { + "name": "Filecoin.StateMinerDeadlines", + "description": "```go\nfunc (s *GatewayStruct) StateMinerDeadlines(p0 context.Context, p1 address.Address, p2 types.TipSetKey) ([]Deadline, error) {\n\tif s.Internal.StateMinerDeadlines == nil {\n\t\treturn *new([]Deadline), ErrNotSupported\n\t}\n\treturn s.Internal.StateMinerDeadlines(p0, p1, p2)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "address.Address", + "summary": "", + "schema": { + "examples": [ + "f01234" + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + }, + { + "name": "p2", + "description": "types.TipSetKey", + "summary": "", + "schema": { + "examples": [ + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] + ], + "additionalProperties": false, + "type": [ + "object" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "[]Deadline", + "description": "[]Deadline", + "summary": "", + "schema": { + "examples": [ + [ + { + "PostSubmissions": [ + 5, + 1 + ], + "DisputableProofCount": 42 + } + ] + ], + "items": [ + { + "additionalProperties": false, + "properties": { + "DisputableProofCount": { + "title": "number", + "type": "number" + }, + "PostSubmissions": { + "additionalProperties": false, + "type": "object" + } + }, + "type": [ + "object" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5130" } }, { @@ -8257,7 +8350,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5128" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5141" } }, { @@ -8368,7 +8461,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5139" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5152" } }, { @@ -8499,7 +8592,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5150" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5163" } }, { @@ -8585,7 +8678,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5161" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5174" } }, { @@ -8612,7 +8705,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5172" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5185" } }, { @@ -8665,7 +8758,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5183" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5196" } }, { @@ -8753,7 +8846,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5194" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5207" } }, { @@ -9204,7 +9297,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5205" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5218" } }, { @@ -9371,7 +9464,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5216" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5229" } }, { @@ -9544,7 +9637,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5227" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5240" } }, { @@ -9612,7 +9705,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5238" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5251" } }, { @@ -9680,7 +9773,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5249" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5262" } }, { @@ -9841,7 +9934,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5260" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5273" } }, { @@ -9886,7 +9979,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5282" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5295" } }, { @@ -9931,7 +10024,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5293" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5306" } }, { @@ -9958,7 +10051,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5304" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5317" } } ] diff --git a/build/openrpc/miner.json b/build/openrpc/miner.json index 0686987c8..bc6f4e67d 100644 --- a/build/openrpc/miner.json +++ b/build/openrpc/miner.json @@ -30,7 +30,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5700" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5713" } }, { @@ -109,7 +109,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5711" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5724" } }, { @@ -155,7 +155,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5722" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5735" } }, { @@ -203,7 +203,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5733" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5746" } }, { @@ -251,7 +251,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5744" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5757" } }, { @@ -354,7 +354,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5755" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5768" } }, { @@ -428,7 +428,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5766" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5779" } }, { @@ -591,7 +591,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5777" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5790" } }, { @@ -742,7 +742,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5788" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5801" } }, { @@ -781,7 +781,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5799" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5812" } }, { @@ -833,7 +833,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5810" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5823" } }, { @@ -872,7 +872,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5832" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5845" } }, { @@ -924,7 +924,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5843" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5856" } }, { @@ -996,7 +996,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5854" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5867" } }, { @@ -1035,7 +1035,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5865" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5878" } }, { @@ -1074,7 +1074,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5876" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5889" } }, { @@ -1101,7 +1101,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5887" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5900" } }, { @@ -1128,7 +1128,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5898" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5911" } }, { @@ -1155,7 +1155,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5909" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5922" } }, { @@ -1182,7 +1182,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5920" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5933" } }, { @@ -1209,7 +1209,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5931" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5944" } }, { @@ -1236,7 +1236,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5942" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5955" } }, { @@ -1294,7 +1294,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5953" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5966" } }, { @@ -1421,7 +1421,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5964" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5977" } }, { @@ -1461,7 +1461,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5975" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5988" } }, { @@ -1500,7 +1500,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5986" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5999" } }, { @@ -1539,7 +1539,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5997" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6010" } }, { @@ -1578,7 +1578,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6008" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6021" } }, { @@ -1617,7 +1617,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6019" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6032" } }, { @@ -1656,7 +1656,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6030" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6043" } }, { @@ -1695,7 +1695,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6041" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6054" } }, { @@ -1747,7 +1747,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6052" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6065" } }, { @@ -1770,7 +1770,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6063" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6076" } }, { @@ -1813,7 +1813,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6074" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6087" } }, { @@ -1884,7 +1884,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6085" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6098" } }, { @@ -2265,7 +2265,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6096" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6109" } }, { @@ -2364,7 +2364,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6118" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6131" } }, { @@ -2415,7 +2415,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6140" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6153" } }, { @@ -2473,7 +2473,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6151" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6164" } }, { @@ -2616,7 +2616,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6162" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6175" } }, { @@ -2743,7 +2743,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6173" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6186" } }, { @@ -3007,7 +3007,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6184" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6197" } }, { @@ -3044,7 +3044,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6195" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6208" } }, { @@ -3182,7 +3182,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6206" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6219" } }, { @@ -3205,7 +3205,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6217" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6230" } }, { @@ -3276,7 +3276,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6228" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6241" } }, { @@ -3319,7 +3319,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6239" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6252" } }, { @@ -3426,7 +3426,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6250" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6263" } }, { @@ -3489,7 +3489,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6261" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6274" } }, { @@ -3521,7 +3521,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6272" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6285" } }, { @@ -3609,7 +3609,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6283" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6296" } }, { @@ -3700,7 +3700,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6294" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6307" } }, { @@ -3740,7 +3740,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6305" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6318" } }, { @@ -3780,7 +3780,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6316" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6329" } }, { @@ -3821,7 +3821,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6327" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6340" } }, { @@ -3889,7 +3889,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6338" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6351" } }, { @@ -4020,7 +4020,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6349" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6362" } }, { @@ -4151,7 +4151,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6360" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6373" } }, { @@ -4251,7 +4251,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6371" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6384" } }, { @@ -4351,7 +4351,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6382" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6395" } }, { @@ -4451,7 +4451,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6393" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6406" } }, { @@ -4551,7 +4551,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6404" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6417" } }, { @@ -4651,7 +4651,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6415" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6428" } }, { @@ -4751,7 +4751,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6426" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6439" } }, { @@ -4875,7 +4875,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6437" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6450" } }, { @@ -4999,7 +4999,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6448" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6461" } }, { @@ -5114,7 +5114,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6459" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6472" } }, { @@ -5214,7 +5214,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6470" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6483" } }, { @@ -5347,7 +5347,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6481" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6494" } }, { @@ -5471,7 +5471,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6492" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6505" } }, { @@ -5595,7 +5595,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6503" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6516" } }, { @@ -5719,7 +5719,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6514" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6527" } }, { @@ -5852,7 +5852,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6525" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6538" } }, { @@ -5952,7 +5952,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6536" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6549" } }, { @@ -5993,7 +5993,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6547" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6560" } }, { @@ -6065,7 +6065,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6558" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6571" } }, { @@ -6115,7 +6115,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6569" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6582" } }, { @@ -6159,7 +6159,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6580" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6593" } }, { @@ -6200,7 +6200,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6591" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6604" } }, { @@ -6444,7 +6444,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6602" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6615" } }, { @@ -6518,7 +6518,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6613" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6626" } }, { @@ -6568,7 +6568,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6624" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6637" } }, { @@ -6597,7 +6597,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6635" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6648" } }, { @@ -6626,7 +6626,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6646" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6659" } }, { @@ -6682,7 +6682,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6657" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6670" } }, { @@ -6705,7 +6705,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6668" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6681" } }, { @@ -6765,7 +6765,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6679" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6692" } }, { @@ -6804,7 +6804,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6690" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6703" } }, { @@ -6844,7 +6844,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6701" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6714" } }, { @@ -6917,7 +6917,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6712" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6725" } }, { @@ -6981,7 +6981,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6723" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6736" } }, { @@ -7044,7 +7044,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6734" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6747" } }, { @@ -7094,7 +7094,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6745" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6758" } }, { @@ -7653,7 +7653,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6756" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6769" } }, { @@ -7694,7 +7694,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6767" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6780" } }, { @@ -7735,7 +7735,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6778" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6791" } }, { @@ -7776,7 +7776,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6789" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6802" } }, { @@ -7817,7 +7817,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6800" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6813" } }, { @@ -7858,7 +7858,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6811" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6824" } }, { @@ -7889,7 +7889,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6822" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6835" } }, { @@ -7939,7 +7939,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6833" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6846" } }, { @@ -7980,7 +7980,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6844" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6857" } }, { @@ -8019,7 +8019,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6855" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6868" } }, { @@ -8083,7 +8083,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6866" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6879" } }, { @@ -8141,7 +8141,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6877" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6890" } }, { @@ -8588,7 +8588,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6888" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6901" } }, { @@ -8624,7 +8624,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6899" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6912" } }, { @@ -8767,7 +8767,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6910" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6923" } }, { @@ -8823,7 +8823,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6921" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6934" } }, { @@ -8862,7 +8862,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6932" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6945" } }, { @@ -9021,7 +9021,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6943" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6956" } }, { @@ -9073,7 +9073,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6954" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6967" } }, { @@ -9230,7 +9230,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6965" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6978" } }, { @@ -9330,7 +9330,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6976" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6989" } }, { @@ -9384,7 +9384,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6987" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7000" } }, { @@ -9423,7 +9423,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6998" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7011" } }, { @@ -9508,7 +9508,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7009" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7022" } }, { @@ -9684,7 +9684,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7020" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7033" } }, { @@ -9780,7 +9780,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7031" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7044" } }, { @@ -9894,7 +9894,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7042" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7055" } }, { @@ -9948,7 +9948,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7053" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7066" } }, { @@ -9982,7 +9982,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7064" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7077" } }, { @@ -10069,7 +10069,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7075" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7088" } }, { @@ -10123,7 +10123,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7086" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7099" } }, { @@ -10223,7 +10223,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7097" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7110" } }, { @@ -10300,7 +10300,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7108" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7121" } }, { @@ -10391,7 +10391,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7119" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7132" } }, { @@ -10430,7 +10430,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7130" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7143" } }, { @@ -10546,7 +10546,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7141" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7154" } }, { @@ -12646,7 +12646,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7152" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7165" } } ] diff --git a/build/openrpc/worker.json b/build/openrpc/worker.json index 2cea4e877..6604b5925 100644 --- a/build/openrpc/worker.json +++ b/build/openrpc/worker.json @@ -161,7 +161,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7240" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7253" } }, { @@ -252,7 +252,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7251" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7264" } }, { @@ -420,7 +420,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7262" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7275" } }, { @@ -447,7 +447,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7273" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7286" } }, { @@ -597,7 +597,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7284" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7297" } }, { @@ -700,7 +700,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7295" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7308" } }, { @@ -803,7 +803,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7306" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7319" } }, { @@ -925,7 +925,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7317" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7330" } }, { @@ -1135,7 +1135,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7328" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7341" } }, { @@ -1306,7 +1306,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7339" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7352" } }, { @@ -3350,7 +3350,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7350" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7363" } }, { @@ -3470,7 +3470,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7361" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7374" } }, { @@ -3531,7 +3531,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7372" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7385" } }, { @@ -3569,7 +3569,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7383" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7396" } }, { @@ -3729,7 +3729,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7394" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7407" } }, { @@ -3913,7 +3913,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7405" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7418" } }, { @@ -4054,7 +4054,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7416" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7429" } }, { @@ -4107,7 +4107,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7427" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7440" } }, { @@ -4250,7 +4250,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7438" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7451" } }, { @@ -4474,7 +4474,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7449" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7462" } }, { @@ -4601,7 +4601,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7460" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7473" } }, { @@ -4768,7 +4768,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7471" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7484" } }, { @@ -4895,7 +4895,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7482" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7495" } }, { @@ -4933,7 +4933,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7493" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7506" } }, { @@ -4972,7 +4972,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7504" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7517" } }, { @@ -4995,7 +4995,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7515" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7528" } }, { @@ -5034,7 +5034,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7526" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7539" } }, { @@ -5057,7 +5057,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7537" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7550" } }, { @@ -5096,7 +5096,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7548" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7561" } }, { @@ -5130,7 +5130,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7559" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7572" } }, { @@ -5184,7 +5184,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7570" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7583" } }, { @@ -5223,7 +5223,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7581" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7594" } }, { @@ -5262,7 +5262,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7592" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7605" } }, { @@ -5297,7 +5297,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7603" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7616" } }, { @@ -5477,7 +5477,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7614" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7627" } }, { @@ -5506,7 +5506,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7625" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7638" } }, { @@ -5529,7 +5529,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7636" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7649" } } ] From 5dfec4ab36f0e57d695a2e99104e8dd368d0456e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 14 Mar 2024 14:44:58 +0100 Subject: [PATCH 262/394] lppiece: Fix piece cleanup task --- cmd/lotus-shed/lpdeal.go | 4 ++-- lib/harmony/harmonydb/sql/20240228-piece-park.sql | 1 + provider/lppiece/task_cleanup_piece.go | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/cmd/lotus-shed/lpdeal.go b/cmd/lotus-shed/lpdeal.go index b6237e13f..0819ddc13 100644 --- a/cmd/lotus-shed/lpdeal.go +++ b/cmd/lotus-shed/lpdeal.go @@ -3,7 +3,6 @@ package main import ( "bytes" "context" - "database/sql" "fmt" "io" "net" @@ -16,6 +15,7 @@ import ( "github.com/fatih/color" "github.com/google/uuid" + "github.com/jackc/pgx/v5" "github.com/mitchellh/go-homedir" manet "github.com/multiformats/go-multiaddr/net" "github.com/urfave/cli/v2" @@ -475,7 +475,7 @@ var lpBoostProxyCmd = &cli.Command{ err = tx.QueryRow(`SELECT id FROM parked_pieces WHERE piece_cid = $1`, deal.DealProposal.PieceCID.String()).Scan(&pieceID) if err != nil { - if err == sql.ErrNoRows { + if err == pgx.ErrNoRows { // Piece does not exist, attempt to insert err = tx.QueryRow(` INSERT INTO parked_pieces (piece_cid, piece_padded_size, piece_raw_size) diff --git a/lib/harmony/harmonydb/sql/20240228-piece-park.sql b/lib/harmony/harmonydb/sql/20240228-piece-park.sql index 95792628a..9ee6b447f 100644 --- a/lib/harmony/harmonydb/sql/20240228-piece-park.sql +++ b/lib/harmony/harmonydb/sql/20240228-piece-park.sql @@ -12,6 +12,7 @@ create table parked_pieces ( cleanup_task_id bigint default null, foreign key (task_id) references harmony_task (id) on delete set null, + foreign key (cleanup_task_id) references harmony_task (id) on delete set null, unique (piece_cid) ); diff --git a/provider/lppiece/task_cleanup_piece.go b/provider/lppiece/task_cleanup_piece.go index d7f24037a..eeb4a44f0 100644 --- a/provider/lppiece/task_cleanup_piece.go +++ b/provider/lppiece/task_cleanup_piece.go @@ -76,7 +76,7 @@ func (c *CleanupPieceTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) // select by cleanup_task_id var pieceID int64 - err = c.db.QueryRow(ctx, "SELECT piece_id FROM parked_pieces WHERE cleanup_task_id = $1", taskID).Scan(&pieceID) + err = c.db.QueryRow(ctx, "SELECT id FROM parked_pieces WHERE cleanup_task_id = $1", taskID).Scan(&pieceID) if err != nil { return false, xerrors.Errorf("query parked_piece: %w", err) } From b56af9b8b03962ca7fbd00c27f0a18beeb9ed7be Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Thu, 14 Mar 2024 21:03:42 +0000 Subject: [PATCH 263/394] Fix go imports at least 2x faster (#11695) Use native go implementation to sort and adjust imports. Compared to the previous bash version, this is at least 2X faster. --- Makefile | 6 +-- scripts/fiximports | 22 --------- scripts/fiximports/main.go | 92 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 95 insertions(+), 25 deletions(-) delete mode 100755 scripts/fiximports create mode 100644 scripts/fiximports/main.go diff --git a/Makefile b/Makefile index b6e523b12..5724e38fa 100644 --- a/Makefile +++ b/Makefile @@ -332,7 +332,7 @@ actors-code-gen: $(GOCC) fmt ./... actors-gen: actors-code-gen - ./scripts/fiximports + $(GOCC) run ./scripts/fiximports .PHONY: actors-gen bundle-gen: @@ -392,10 +392,10 @@ docsgen-openrpc-gateway: docsgen-openrpc-bin .PHONY: docsgen docsgen-md-bin docsgen-openrpc-bin fiximports: - ./scripts/fiximports + $(GOCC) run ./scripts/fiximports gen: actors-code-gen type-gen cfgdoc-gen docsgen api-gen circleci - ./scripts/fiximports + $(GOCC) run ./scripts/fiximports @echo ">>> IF YOU'VE MODIFIED THE CLI OR CONFIG, REMEMBER TO ALSO RUN 'make docsgen-cli'" .PHONY: gen diff --git a/scripts/fiximports b/scripts/fiximports deleted file mode 100755 index 140cebbce..000000000 --- a/scripts/fiximports +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env bash - -sed_replace='/import (/ { - :1 - $!N - s/\n\n/\'$'\n''/ - /)/!b1 -}' - -go_files() { - find . -type f -name \*.go -not -name \*_cbor_gen.go | grep -v './extern/filecoin-ffi' | grep -v './extern/test-vectors' -} - -# Because -i works differently on macOS, we need to use a different sed command -if [[ "$OSTYPE" == "darwin"* ]]; then - go_files | xargs -I '{}' sed -i '' -e "$sed_replace" '{}' -else - go_files | xargs -I '{}' sed -i -e "$sed_replace" '{}' -fi - -go_files | xargs -I '{}' goimports -w -local "github.com/filecoin-project" '{}' -go_files | xargs -I '{}' goimports -w -local "github.com/filecoin-project/lotus" '{}' diff --git a/scripts/fiximports/main.go b/scripts/fiximports/main.go new file mode 100644 index 000000000..427975855 --- /dev/null +++ b/scripts/fiximports/main.go @@ -0,0 +1,92 @@ +package main + +import ( + "bytes" + "fmt" + "io" + "io/fs" + "os" + "path/filepath" + "regexp" + "strings" + + "golang.org/x/tools/imports" +) + +var ( + // groupByPrefixes is the list of import prefixes that should _each_ be grouped separately. + // See: imports.LocalPrefix. + groupByPrefixes = []string{ + "github.com/filecoin-project", + "github.com/filecoin-project/lotus", + } + newline = []byte("\n") + importBlockRegex = regexp.MustCompile(`(?s)import\s*\((.*?)\)`) + consecutiveNewlinesRegex = regexp.MustCompile(`\n\s*\n`) +) + +func main() { + if err := filepath.Walk(".", func(path string, info fs.FileInfo, err error) error { + switch { + case err != nil: + return err + case // Skip the entire "./extern/..." directory and its contents. + strings.HasPrefix(path, "extern/"): + return filepath.SkipDir + case // Skip directories, generated cborgen go files and any other non-go files. + info.IsDir(), + strings.HasSuffix(info.Name(), "_cbor_gen.go"), + !strings.HasSuffix(info.Name(), ".go"): + return nil + } + return fixGoImports(path) + }); err != nil { + fmt.Printf("Error fixing go imports: %v\n", err) + os.Exit(1) + } +} + +func fixGoImports(path string) error { + sourceFile, err := os.OpenFile(path, os.O_RDWR, 0666) + if err != nil { + return err + } + defer func() { _ = sourceFile.Close() }() + + source, err := io.ReadAll(sourceFile) + if err != nil { + return err + } + formatted := collapseImportNewlines(source) + for _, prefix := range groupByPrefixes { + imports.LocalPrefix = prefix + formatted, err = imports.Process(path, formatted, nil) + if err != nil { + return err + } + } + if !bytes.Equal(source, formatted) { + if err := replaceFileContent(sourceFile, formatted); err != nil { + return err + } + } + return nil +} + +func replaceFileContent(target *os.File, replacement []byte) error { + if _, err := target.Seek(0, io.SeekStart); err != nil { + return err + } + written, err := target.Write(replacement) + if err != nil { + return err + } + return target.Truncate(int64(written)) +} + +func collapseImportNewlines(content []byte) []byte { + return importBlockRegex.ReplaceAllFunc(content, func(importBlock []byte) []byte { + // Replace consecutive newlines with a single newline within the import block + return consecutiveNewlinesRegex.ReplaceAll(importBlock, newline) + }) +} From 47910cfe82b57060f711a06e5557185a46157b4c Mon Sep 17 00:00:00 2001 From: shuangcui Date: Fri, 15 Mar 2024 09:59:02 +0800 Subject: [PATCH 264/394] chore: remove repetitive words Signed-off-by: shuangcui --- CHANGELOG.md | 4 ++-- chain/events/state/predicates.go | 2 +- chain/messagepool/selection_test.go | 2 +- chain/sync_manager.go | 2 +- cmd/lotus-sim/simulation/simulation.go | 2 +- conformance/driver.go | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index eca451ecd..3e1f6f457 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3691,7 +3691,7 @@ This is a **highly recommended** but optional Lotus v1.11.1 release that introd - Config for deal publishing control addresses ([filecoin-project/lotus#6697](https://github.com/filecoin-project/lotus/pull/6697)) - Set `DealPublishControl` to set the wallet used for sending `PublishStorageDeals` messages, instructions [here](https://lotus.filecoin.io/storage-providers/operate/addresses/#control-addresses). - Config UX improvements ([filecoin-project/lotus#6848](https://github.com/filecoin-project/lotus/pull/6848)) - - You can now preview the the default and updated node config by running `lotus/lotus-miner config default/updated` + - You can now preview the default and updated node config by running `lotus/lotus-miner config default/updated` ## New Features - ⭐️⭐️⭐️ Support standalone miner-market process ([filecoin-project/lotus#6356](https://github.com/filecoin-project/lotus/pull/6356)) @@ -5138,7 +5138,7 @@ This consensus-breaking release of Lotus upgrades the actors version to v2.0.0. #### Mining -- Increased ExpectedSealDuration and and WaitDealsDelay (https://github.com/filecoin-project/lotus/pull/3743) +- Increased ExpectedSealDuration and WaitDealsDelay (https://github.com/filecoin-project/lotus/pull/3743) - Miner backup/restore commands (https://github.com/filecoin-project/lotus/pull/4133) - lotus-miner: add more help text to storage / attach (https://github.com/filecoin-project/lotus/pull/3961) - Reject deals that are > 7 days in the future in the BasicDealFilter (https://github.com/filecoin-project/lotus/pull/4173) diff --git a/chain/events/state/predicates.go b/chain/events/state/predicates.go index e4e8b8f7e..298f787ff 100644 --- a/chain/events/state/predicates.go +++ b/chain/events/state/predicates.go @@ -369,7 +369,7 @@ func (sp *StatePredicates) OnMinerPreCommitChange() DiffMinerActorStateFunc { // DiffPaymentChannelStateFunc is function that compares two states for the payment channel type DiffPaymentChannelStateFunc func(ctx context.Context, oldState paych.State, newState paych.State) (changed bool, user UserData, err error) -// OnPaymentChannelActorChanged calls diffPaymentChannelState when the state changes for the the payment channel actor +// OnPaymentChannelActorChanged calls diffPaymentChannelState when the state changes for the payment channel actor func (sp *StatePredicates) OnPaymentChannelActorChanged(paychAddr address.Address, diffPaymentChannelState DiffPaymentChannelStateFunc) DiffTipSetKeyFunc { return sp.OnActorStateChanged(paychAddr, func(ctx context.Context, oldActorState, newActorState *types.Actor) (changed bool, user UserData, err error) { oldState, err := paych.Load(adt.WrapStore(ctx, sp.cst), oldActorState) diff --git a/chain/messagepool/selection_test.go b/chain/messagepool/selection_test.go index 17e0f34f4..48846bb7e 100644 --- a/chain/messagepool/selection_test.go +++ b/chain/messagepool/selection_test.go @@ -1191,7 +1191,7 @@ func TestOptimalMessageSelection2(t *testing.T) { func TestOptimalMessageSelection3(t *testing.T) { //stm: @TOKEN_WALLET_NEW_001, @CHAIN_MEMPOOL_SELECT_001 - // this test uses 10 actors sending a block of messages to each other, with the the first + // this test uses 10 actors sending a block of messages to each other, with the first // actors paying higher gas premium than the subsequent actors. // We select with a low ticket quality; the chain dependent merging algorithm should pick // messages from the median actor from the start diff --git a/chain/sync_manager.go b/chain/sync_manager.go index 3369c3b5a..2f54ac8bb 100644 --- a/chain/sync_manager.go +++ b/chain/sync_manager.go @@ -357,7 +357,7 @@ func (sm *syncManager) selectInitialSyncTarget() (*types.TipSet, error) { return buckets.Heaviest(), nil } -// adds a tipset to the potential sync targets; returns true if there is a a tipset to work on. +// adds a tipset to the potential sync targets; returns true if there is a tipset to work on. // this could be either a restart, eg because there is no currently scheduled sync work or a worker // failed or a potential fork. func (sm *syncManager) addSyncTarget(ts *types.TipSet) (*types.TipSet, bool, error) { diff --git a/cmd/lotus-sim/simulation/simulation.go b/cmd/lotus-sim/simulation/simulation.go index 47d06aeda..d73a033cf 100644 --- a/cmd/lotus-sim/simulation/simulation.go +++ b/cmd/lotus-sim/simulation/simulation.go @@ -117,7 +117,7 @@ func (sim *Simulation) saveConfig() error { var simulationPrefix = datastore.NewKey("/simulation") -// key returns the the key in the form /simulation//. For example, +// key returns the key in the form /simulation//. For example, // /simulation/head/default. func (sim *Simulation) key(subkey string) datastore.Key { return simulationPrefix.ChildString(subkey).ChildString(sim.name) diff --git a/conformance/driver.go b/conformance/driver.go index 3c62ca7b9..d4f46942c 100644 --- a/conformance/driver.go +++ b/conformance/driver.go @@ -100,7 +100,7 @@ type ExecuteTipsetParams struct { // ExecuteTipset executes the supplied tipset on top of the state represented // by the preroot CID. // -// This method returns the the receipts root, the poststate root, and the VM +// This method returns the receipts root, the poststate root, and the VM // message results. The latter _include_ implicit messages, such as cron ticks // and reward withdrawal per miner. func (d *Driver) ExecuteTipset(bs blockstore.Blockstore, ds ds.Batching, params ExecuteTipsetParams) (*ExecuteTipsetResult, error) { From d5f4d807d72e3be661463c529b1dcfa2c55a0b9b Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Wed, 13 Mar 2024 18:22:25 +0000 Subject: [PATCH 265/394] Prevent DDL re-execution during event index schema migrations This enhancement optimizes the schema migration process for the event index by preventing the redundant execution of Data Definition Language (DDL) statements that define the event schema. Traditionally, these DDL statements were grouped into a single slice, reflecting the most current version of the event index schema. With each migration, this slice was updated to the latest schema iteration, executing all statements in bulk. Initially, this method sufficed as migrations were focused on adding indices to existing table columns. However, as the database schema evolves to meet new requirements, such as the forthcoming migrations that involve changes to table schemas (notably, indexing events by emitter actor ID instead of addresses), the prior approach of bulk execution of DDL statements becomes unsuitable: it will no longer be safe to repeatedly execute DDL statements in previous migrations, because the upcoming one changes `event` table column structure. To address this issue, the work here has isolated the event index schema migrations on a per-version basis. This adjustment ensures that only the necessary DDL statements are executed during each migration, avoiding the inefficiencies and potential errors associated with redundant executions. The work here should also minimize the refactoring required for future migrations, facilitating a smoother introduction of significant schema updates. --- chain/events/filter/index.go | 137 ++++++++++++++++++++++------------- 1 file changed, 87 insertions(+), 50 deletions(-) diff --git a/chain/events/filter/index.go b/chain/events/filter/index.go index 49be57c79..83998767e 100644 --- a/chain/events/filter/index.go +++ b/chain/events/filter/index.go @@ -45,8 +45,8 @@ var ddls = []string{ reverted INTEGER NOT NULL )`, - `CREATE INDEX IF NOT EXISTS height_tipset_key ON event (height,tipset_key)`, - `CREATE INDEX IF NOT EXISTS event_emitter_addr ON event (emitter_addr)`, + createIndexEventHeightTipsetKey, + createIndexEventEmitterAddr, `CREATE TABLE IF NOT EXISTS event_entry ( event_id INTEGER, @@ -57,11 +57,11 @@ var ddls = []string{ value BLOB NOT NULL )`, - `CREATE INDEX IF NOT EXISTS event_entry_key_index ON event_entry (key)`, + createIndexEventEntryKey, // metadata containing version of schema `CREATE TABLE IF NOT EXISTS _meta ( - version UINT64 NOT NULL UNIQUE + version UINT64 NOT NULL UNIQUE )`, `INSERT OR IGNORE INTO _meta (version) VALUES (1)`, @@ -81,6 +81,10 @@ const ( insertEntry = `INSERT OR IGNORE INTO event_entry(event_id, indexed, flags, key, codec, value) VALUES(?, ?, ?, ?, ?, ?)` revertEventsInTipset = `UPDATE event SET reverted=true WHERE height=? AND tipset_key=?` restoreEvent = `UPDATE event SET reverted=false WHERE height=? AND tipset_key=? AND tipset_key_cid=? AND emitter_addr=? AND event_index=? AND message_cid=? AND message_index=?` + + createIndexEventHeightTipsetKey = `CREATE INDEX IF NOT EXISTS height_tipset_key ON event (height,tipset_key)` + createIndexEventEmitterAddr = `CREATE INDEX IF NOT EXISTS event_emitter_addr ON event (emitter_addr)` + createIndexEventEntryKey = `CREATE INDEX IF NOT EXISTS event_entry_key_index ON event_entry (key)` ) type EventIndex struct { @@ -125,43 +129,43 @@ func (ei *EventIndex) initStatements() (err error) { func (ei *EventIndex) migrateToVersion2(ctx context.Context, chainStore *store.ChainStore) error { now := time.Now() - tx, err := ei.db.Begin() + tx, err := ei.db.BeginTx(ctx, nil) if err != nil { return xerrors.Errorf("begin transaction: %w", err) } // rollback the transaction (a no-op if the transaction was already committed) - defer tx.Rollback() //nolint:errcheck + defer func() { _ = tx.Rollback() }() // create some temporary indices to help speed up the migration - _, err = tx.Exec("CREATE INDEX IF NOT EXISTS tmp_height_tipset_key_cid ON event (height,tipset_key_cid)") + _, err = tx.ExecContext(ctx, "CREATE INDEX IF NOT EXISTS tmp_height_tipset_key_cid ON event (height,tipset_key_cid)") if err != nil { return xerrors.Errorf("create index tmp_height_tipset_key_cid: %w", err) } - _, err = tx.Exec("CREATE INDEX IF NOT EXISTS tmp_tipset_key_cid ON event (tipset_key_cid)") + _, err = tx.ExecContext(ctx, "CREATE INDEX IF NOT EXISTS tmp_tipset_key_cid ON event (tipset_key_cid)") if err != nil { return xerrors.Errorf("create index tmp_tipset_key_cid: %w", err) } - stmtDeleteOffChainEvent, err := tx.Prepare("DELETE FROM event WHERE tipset_key_cid!=? and height=?") + stmtDeleteOffChainEvent, err := tx.PrepareContext(ctx, "DELETE FROM event WHERE tipset_key_cid!=? and height=?") if err != nil { return xerrors.Errorf("prepare stmtDeleteOffChainEvent: %w", err) } - stmtSelectEvent, err := tx.Prepare("SELECT id FROM event WHERE tipset_key_cid=? ORDER BY message_index ASC, event_index ASC, id DESC LIMIT 1") + stmtSelectEvent, err := tx.PrepareContext(ctx, "SELECT id FROM event WHERE tipset_key_cid=? ORDER BY message_index ASC, event_index ASC, id DESC LIMIT 1") if err != nil { return xerrors.Errorf("prepare stmtSelectEvent: %w", err) } - stmtDeleteEvent, err := tx.Prepare("DELETE FROM event WHERE tipset_key_cid=? AND id 0 { - subclauses := []string{} + subclauses := make([]string, 0, len(f.addresses)) for _, addr := range f.addresses { subclauses = append(subclauses, "emitter_addr=?") values = append(values, addr.Bytes()) @@ -543,7 +580,7 @@ func (ei *EventIndex) prefillFilter(ctx context.Context, f *eventFilter, exclude joins = append(joins, fmt.Sprintf("event_entry %s on event.id=%[1]s.event_id", joinAlias)) clauses = append(clauses, fmt.Sprintf("%s.indexed=1 AND %[1]s.key=?", joinAlias)) values = append(values, key) - subclauses := []string{} + subclauses := make([]string, 0, len(vals)) for _, val := range vals { subclauses = append(subclauses, fmt.Sprintf("(%s.value=? AND %[1]s.codec=?)", joinAlias)) values = append(values, val.Value, val.Codec) From 6ca55d18a71de7097fc2ffe835cb29c185dae5c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 15 Mar 2024 14:10:48 +0100 Subject: [PATCH 266/394] address review --- provider/lppiece/task_park_piece.go | 27 ++++++++++++++++++++++++++- provider/lpseal/task_sdr.go | 2 -- storage/paths/remote.go | 2 ++ 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/provider/lppiece/task_park_piece.go b/provider/lppiece/task_park_piece.go index 034ef9b2c..62bb91660 100644 --- a/provider/lppiece/task_park_piece.go +++ b/provider/lppiece/task_park_piece.go @@ -175,6 +175,8 @@ func (p *ParkPieceTask) CanAccept(ids []harmonytask.TaskID, engine *harmonytask. } func (p *ParkPieceTask) TypeDetails() harmonytask.TaskTypeDetails { + const maxSizePiece = 64 << 30 + return harmonytask.TaskTypeDetails{ Max: p.max, Name: "ParkPiece", @@ -182,12 +184,35 @@ func (p *ParkPieceTask) TypeDetails() harmonytask.TaskTypeDetails { Cpu: 1, Gpu: 0, Ram: 64 << 20, - Storage: nil, // TODO + Storage: p.sc.Storage(p.taskToRef, storiface.FTPiece, storiface.FTNone, maxSizePiece, storiface.PathSealing), }, MaxFailures: 10, } } +func (p *ParkPieceTask) taskToRef(id harmonytask.TaskID) (lpffi.SectorRef, error) { + var pieceIDs []struct { + ID storiface.PieceNumber `db:"id"` + } + + err := p.db.Select(context.Background(), &pieceIDs, `SELECT id FROM parked_pieces WHERE task_id = $1`, id) + if err != nil { + return lpffi.SectorRef{}, xerrors.Errorf("getting piece id: %w", err) + } + + if len(pieceIDs) != 1 { + return lpffi.SectorRef{}, xerrors.Errorf("expected 1 piece id, got %d", len(pieceIDs)) + } + + pref := pieceIDs[0].ID.Ref() + + return lpffi.SectorRef{ + SpID: int64(pref.ID.Miner), + SectorNumber: int64(pref.ID.Number), + RegSealProof: pref.ProofType, + }, nil +} + func (p *ParkPieceTask) Adder(taskFunc harmonytask.AddTaskFunc) { p.TF.Set(taskFunc) } diff --git a/provider/lpseal/task_sdr.go b/provider/lpseal/task_sdr.go index 694ff6f46..ab86c6a82 100644 --- a/provider/lpseal/task_sdr.go +++ b/provider/lpseal/task_sdr.go @@ -187,8 +187,6 @@ func (s *SDRTask) getTicket(ctx context.Context, maddr address.Address) (abi.Sea } func (s *SDRTask) CanAccept(ids []harmonytask.TaskID, engine *harmonytask.TaskEngine) (*harmonytask.TaskID, error) { - // todo check storage (reserve too?) - id := ids[0] return &id, nil } diff --git a/storage/paths/remote.go b/storage/paths/remote.go index dfceaeace..9ff719954 100644 --- a/storage/paths/remote.go +++ b/storage/paths/remote.go @@ -747,6 +747,8 @@ func (r *Remote) Reader(ctx context.Context, s storiface.SectorRef, offset, size return nil, nil } +// ReaderSeq creates a simple sequential reader for a file. Does not work for +// file types which are a directory (e.g. FTCache). func (r *Remote) ReaderSeq(ctx context.Context, s storiface.SectorRef, ft storiface.SectorFileType) (io.ReadCloser, error) { paths, _, err := r.local.AcquireSector(ctx, s, ft, storiface.FTNone, storiface.PathStorage, storiface.AcquireMove) if err != nil { From 018b7662d525ee11d00dda01945e9f73ffe1ccdb Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Fri, 15 Mar 2024 12:22:25 +1100 Subject: [PATCH 267/394] fix(events,gateway): check that filter.FromHeight isn't too far back for gateway calls --- gateway/proxy_fil.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/gateway/proxy_fil.go b/gateway/proxy_fil.go index e7ad3bdb4..b3480752b 100644 --- a/gateway/proxy_fil.go +++ b/gateway/proxy_fil.go @@ -441,6 +441,11 @@ func (gw *Node) GetActorEvents(ctx context.Context, filter *types.ActorEventFilt if err := gw.limit(ctx, stateRateLimitTokens); err != nil { return nil, err } + if filter != nil && filter.FromHeight != nil { + if err := gw.checkTipSetHeight(ctx, *filter.FromHeight, types.EmptyTSK); err != nil { + return nil, err + } + } return gw.target.GetActorEvents(ctx, filter) } @@ -448,6 +453,11 @@ func (gw *Node) SubscribeActorEvents(ctx context.Context, filter *types.ActorEve if err := gw.limit(ctx, stateRateLimitTokens); err != nil { return nil, err } + if filter != nil && filter.FromHeight != nil { + if err := gw.checkTipSetHeight(ctx, *filter.FromHeight, types.EmptyTSK); err != nil { + return nil, err + } + } return gw.target.SubscribeActorEvents(ctx, filter) } From b7faf23f893ef59be204ec994e8c757f70b83b73 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Fri, 15 Mar 2024 10:27:21 -0400 Subject: [PATCH 268/394] feat: add ChainGetEvents to the gateway API (#11724) --- api/api_gateway.go | 1 + api/proxy_gen.go | 13 + build/openrpc/full.json | 486 ++++++++++++++++++------------------- build/openrpc/gateway.json | 289 ++++++++++++++-------- build/openrpc/miner.json | 260 ++++++++++---------- build/openrpc/worker.json | 74 +++--- gateway/node.go | 1 + gateway/proxy_fil.go | 7 + 8 files changed, 626 insertions(+), 505 deletions(-) diff --git a/api/api_gateway.go b/api/api_gateway.go index 25187a679..c5bed3621 100644 --- a/api/api_gateway.go +++ b/api/api_gateway.go @@ -133,4 +133,5 @@ type Gateway interface { GetActorEvents(ctx context.Context, filter *types.ActorEventFilter) ([]*types.ActorEvent, error) SubscribeActorEvents(ctx context.Context, filter *types.ActorEventFilter) (<-chan *types.ActorEvent, error) + ChainGetEvents(context.Context, cid.Cid) ([]types.Event, error) } diff --git a/api/proxy_gen.go b/api/proxy_gen.go index 6e809b763..ae48591ec 100644 --- a/api/proxy_gen.go +++ b/api/proxy_gen.go @@ -653,6 +653,8 @@ type GatewayMethods struct { ChainGetBlockMessages func(p0 context.Context, p1 cid.Cid) (*BlockMessages, error) `` + ChainGetEvents func(p0 context.Context, p1 cid.Cid) ([]types.Event, error) `` + ChainGetGenesis func(p0 context.Context) (*types.TipSet, error) `` ChainGetMessage func(p0 context.Context, p1 cid.Cid) (*types.Message, error) `` @@ -4280,6 +4282,17 @@ func (s *GatewayStub) ChainGetBlockMessages(p0 context.Context, p1 cid.Cid) (*Bl return nil, ErrNotSupported } +func (s *GatewayStruct) ChainGetEvents(p0 context.Context, p1 cid.Cid) ([]types.Event, error) { + if s.Internal.ChainGetEvents == nil { + return *new([]types.Event), ErrNotSupported + } + return s.Internal.ChainGetEvents(p0, p1) +} + +func (s *GatewayStub) ChainGetEvents(p0 context.Context, p1 cid.Cid) ([]types.Event, error) { + return *new([]types.Event), ErrNotSupported +} + func (s *GatewayStruct) ChainGetGenesis(p0 context.Context) (*types.TipSet, error) { if s.Internal.ChainGetGenesis == nil { return nil, ErrNotSupported diff --git a/build/openrpc/full.json b/build/openrpc/full.json index fa1d019b6..993eb4fe4 100644 --- a/build/openrpc/full.json +++ b/build/openrpc/full.json @@ -37,7 +37,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1500" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1502" } }, { @@ -60,7 +60,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1511" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1513" } }, { @@ -103,7 +103,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1522" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1524" } }, { @@ -214,7 +214,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1544" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1546" } }, { @@ -454,7 +454,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1555" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1557" } }, { @@ -685,7 +685,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1566" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1568" } }, { @@ -784,7 +784,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1577" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1579" } }, { @@ -816,7 +816,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1588" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1590" } }, { @@ -922,7 +922,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1599" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1601" } }, { @@ -1019,7 +1019,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1610" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1612" } }, { @@ -1078,7 +1078,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1621" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1623" } }, { @@ -1171,7 +1171,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1632" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1634" } }, { @@ -1255,7 +1255,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1643" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1645" } }, { @@ -1355,7 +1355,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1654" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1656" } }, { @@ -1411,7 +1411,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1665" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1667" } }, { @@ -1484,7 +1484,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1676" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1678" } }, { @@ -1557,7 +1557,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1687" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1689" } }, { @@ -1604,7 +1604,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1698" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1700" } }, { @@ -1636,7 +1636,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1709" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1711" } }, { @@ -1691,7 +1691,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1720" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1722" } }, { @@ -1743,7 +1743,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1742" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1744" } }, { @@ -1780,7 +1780,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1753" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1755" } }, { @@ -1827,7 +1827,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1764" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1766" } }, { @@ -1874,7 +1874,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1775" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1777" } }, { @@ -1954,7 +1954,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1786" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1788" } }, { @@ -2006,7 +2006,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1797" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1799" } }, { @@ -2065,7 +2065,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1808" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1810" } }, { @@ -2136,7 +2136,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1819" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1821" } }, { @@ -2177,7 +2177,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1830" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1832" } }, { @@ -2245,7 +2245,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1852" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1854" } }, { @@ -2306,7 +2306,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1863" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1865" } }, { @@ -2413,7 +2413,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1874" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1876" } }, { @@ -2569,7 +2569,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1885" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1887" } }, { @@ -2635,7 +2635,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1896" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1898" } }, { @@ -2976,7 +2976,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1907" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1909" } }, { @@ -3021,7 +3021,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1918" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1920" } }, { @@ -3068,7 +3068,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1951" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1953" } }, { @@ -3139,7 +3139,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1962" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1964" } }, { @@ -3282,7 +3282,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1973" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1975" } }, { @@ -3612,7 +3612,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1984" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1986" } }, { @@ -3680,7 +3680,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1995" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1997" } }, { @@ -3914,7 +3914,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2006" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2008" } }, { @@ -4077,7 +4077,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2017" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2019" } }, { @@ -4160,7 +4160,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2028" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2030" } }, { @@ -4201,7 +4201,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2039" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2041" } }, { @@ -4272,7 +4272,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2050" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2052" } }, { @@ -4416,7 +4416,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2061" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2063" } }, { @@ -4456,7 +4456,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2072" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2074" } }, { @@ -4497,7 +4497,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2083" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2085" } }, { @@ -4622,7 +4622,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2094" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2096" } }, { @@ -4747,7 +4747,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2105" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2107" } }, { @@ -4786,7 +4786,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2116" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2118" } }, { @@ -4833,7 +4833,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2127" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2129" } }, { @@ -4888,7 +4888,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2138" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2140" } }, { @@ -4917,7 +4917,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2149" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2151" } }, { @@ -5054,7 +5054,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2160" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2162" } }, { @@ -5083,7 +5083,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2171" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2173" } }, { @@ -5137,7 +5137,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2182" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2184" } }, { @@ -5228,7 +5228,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2193" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2195" } }, { @@ -5256,7 +5256,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2204" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2206" } }, { @@ -5346,7 +5346,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2215" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2217" } }, { @@ -5602,7 +5602,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2226" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2228" } }, { @@ -5847,7 +5847,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2237" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2239" } }, { @@ -5903,7 +5903,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2248" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2250" } }, { @@ -5950,7 +5950,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2259" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2261" } }, { @@ -6048,7 +6048,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2270" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2272" } }, { @@ -6114,7 +6114,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2281" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2283" } }, { @@ -6180,7 +6180,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2292" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2294" } }, { @@ -6289,7 +6289,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2303" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2305" } }, { @@ -6347,7 +6347,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2314" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2316" } }, { @@ -6469,7 +6469,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2325" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2327" } }, { @@ -6673,7 +6673,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2336" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2338" } }, { @@ -6868,7 +6868,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2347" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2349" } }, { @@ -7055,7 +7055,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2358" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2360" } }, { @@ -7259,7 +7259,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2369" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2371" } }, { @@ -7350,7 +7350,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2380" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2382" } }, { @@ -7408,7 +7408,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2391" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2393" } }, { @@ -7666,7 +7666,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2402" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2404" } }, { @@ -7941,7 +7941,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2413" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2415" } }, { @@ -7969,7 +7969,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2424" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2426" } }, { @@ -8007,7 +8007,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2435" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2437" } }, { @@ -8115,7 +8115,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2446" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2448" } }, { @@ -8153,7 +8153,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2457" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2459" } }, { @@ -8182,7 +8182,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2468" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2470" } }, { @@ -8245,7 +8245,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2479" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2481" } }, { @@ -8308,7 +8308,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2490" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2492" } }, { @@ -8353,7 +8353,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2501" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2503" } }, { @@ -8475,7 +8475,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2512" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2514" } }, { @@ -8630,7 +8630,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2523" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2525" } }, { @@ -8684,7 +8684,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2534" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2536" } }, { @@ -8738,7 +8738,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2545" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2547" } }, { @@ -8793,7 +8793,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2556" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2558" } }, { @@ -8936,7 +8936,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2567" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2569" } }, { @@ -9063,7 +9063,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2578" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2580" } }, { @@ -9165,7 +9165,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2589" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2591" } }, { @@ -9388,7 +9388,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2600" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2602" } }, { @@ -9571,7 +9571,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2611" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2613" } }, { @@ -9651,7 +9651,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2622" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2624" } }, { @@ -9696,7 +9696,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2633" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2635" } }, { @@ -9752,7 +9752,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2644" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2646" } }, { @@ -9832,7 +9832,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2655" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2657" } }, { @@ -9912,7 +9912,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2666" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2668" } }, { @@ -10397,7 +10397,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2677" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2679" } }, { @@ -10591,7 +10591,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2688" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2690" } }, { @@ -10746,7 +10746,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2699" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2701" } }, { @@ -10995,7 +10995,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2710" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2712" } }, { @@ -11150,7 +11150,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2721" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2723" } }, { @@ -11327,7 +11327,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2732" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2734" } }, { @@ -11425,7 +11425,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2743" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2745" } }, { @@ -11590,7 +11590,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2754" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2756" } }, { @@ -11629,7 +11629,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2765" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2767" } }, { @@ -11694,7 +11694,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2776" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2778" } }, { @@ -11740,7 +11740,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2787" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2789" } }, { @@ -11890,7 +11890,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2798" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2800" } }, { @@ -12027,7 +12027,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2809" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2811" } }, { @@ -12258,7 +12258,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2820" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2822" } }, { @@ -12395,7 +12395,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2831" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2833" } }, { @@ -12560,7 +12560,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2842" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2844" } }, { @@ -12637,7 +12637,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2853" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2855" } }, { @@ -12832,7 +12832,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2875" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2877" } }, { @@ -13011,7 +13011,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2886" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2888" } }, { @@ -13173,7 +13173,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2897" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2899" } }, { @@ -13321,7 +13321,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2908" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2910" } }, { @@ -13549,7 +13549,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2919" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2921" } }, { @@ -13697,7 +13697,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2930" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2932" } }, { @@ -13909,7 +13909,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2941" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2943" } }, { @@ -14115,7 +14115,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2952" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2954" } }, { @@ -14183,7 +14183,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2963" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2965" } }, { @@ -14300,7 +14300,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2974" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2976" } }, { @@ -14391,7 +14391,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2985" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2987" } }, { @@ -14477,7 +14477,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2996" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2998" } }, { @@ -14672,7 +14672,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3007" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3009" } }, { @@ -14834,7 +14834,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3018" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3020" } }, { @@ -15030,7 +15030,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3029" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3031" } }, { @@ -15210,7 +15210,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3040" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3042" } }, { @@ -15373,7 +15373,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3051" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3053" } }, { @@ -15400,7 +15400,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3062" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3064" } }, { @@ -15427,7 +15427,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3073" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3075" } }, { @@ -15526,7 +15526,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3084" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3086" } }, { @@ -15572,7 +15572,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3095" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3097" } }, { @@ -15672,7 +15672,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3106" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3108" } }, { @@ -15788,7 +15788,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3117" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3119" } }, { @@ -15836,7 +15836,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3128" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3130" } }, { @@ -15928,7 +15928,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3139" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3141" } }, { @@ -16043,7 +16043,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3150" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3152" } }, { @@ -16091,7 +16091,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3161" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3163" } }, { @@ -16128,7 +16128,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3172" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3174" } }, { @@ -16400,7 +16400,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3183" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3185" } }, { @@ -16448,7 +16448,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3194" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3196" } }, { @@ -16506,7 +16506,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3205" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3207" } }, { @@ -16711,7 +16711,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3216" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3218" } }, { @@ -16914,7 +16914,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3227" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3229" } }, { @@ -17083,7 +17083,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3238" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3240" } }, { @@ -17287,7 +17287,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3249" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3251" } }, { @@ -17454,7 +17454,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3260" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3262" } }, { @@ -17661,7 +17661,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3271" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3273" } }, { @@ -17729,7 +17729,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3282" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3284" } }, { @@ -17781,7 +17781,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3293" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3295" } }, { @@ -17830,7 +17830,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3304" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3306" } }, { @@ -17921,7 +17921,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3315" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3317" } }, { @@ -18427,7 +18427,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3326" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3328" } }, { @@ -18533,7 +18533,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3337" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3339" } }, { @@ -18585,7 +18585,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3348" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3350" } }, { @@ -19137,7 +19137,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3359" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3361" } }, { @@ -19251,7 +19251,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3370" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3372" } }, { @@ -19348,7 +19348,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3381" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3383" } }, { @@ -19448,7 +19448,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3392" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3394" } }, { @@ -19536,7 +19536,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3403" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3405" } }, { @@ -19636,7 +19636,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3414" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3416" } }, { @@ -19723,7 +19723,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3425" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3427" } }, { @@ -19814,7 +19814,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3436" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3438" } }, { @@ -19939,7 +19939,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3447" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3449" } }, { @@ -20048,7 +20048,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3458" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3460" } }, { @@ -20118,7 +20118,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3469" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3471" } }, { @@ -20221,7 +20221,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3480" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3482" } }, { @@ -20282,7 +20282,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3491" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3493" } }, { @@ -20412,7 +20412,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3502" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3504" } }, { @@ -20519,7 +20519,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3513" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3515" } }, { @@ -20728,7 +20728,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3524" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3526" } }, { @@ -20805,7 +20805,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3535" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3537" } }, { @@ -20882,7 +20882,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3546" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3548" } }, { @@ -20991,7 +20991,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3557" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3559" } }, { @@ -21100,7 +21100,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3568" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3570" } }, { @@ -21161,7 +21161,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3579" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3581" } }, { @@ -21271,7 +21271,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3590" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3592" } }, { @@ -21332,7 +21332,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3601" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3603" } }, { @@ -21400,7 +21400,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3612" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3614" } }, { @@ -21468,7 +21468,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3623" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3625" } }, { @@ -21549,7 +21549,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3634" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3636" } }, { @@ -21698,7 +21698,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3645" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3647" } }, { @@ -21770,7 +21770,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3656" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3658" } }, { @@ -21929,7 +21929,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3667" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3669" } }, { @@ -22094,7 +22094,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3678" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3680" } }, { @@ -22164,7 +22164,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3689" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3691" } }, { @@ -22232,7 +22232,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3700" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3702" } }, { @@ -22325,7 +22325,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3711" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3713" } }, { @@ -22396,7 +22396,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3722" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3724" } }, { @@ -22597,7 +22597,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3733" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3735" } }, { @@ -22729,7 +22729,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3744" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3746" } }, { @@ -22866,7 +22866,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3755" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3757" } }, { @@ -22977,7 +22977,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3766" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3768" } }, { @@ -23109,7 +23109,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3777" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3779" } }, { @@ -23240,7 +23240,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3788" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3790" } }, { @@ -23311,7 +23311,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3799" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3801" } }, { @@ -23395,7 +23395,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3810" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3812" } }, { @@ -23481,7 +23481,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3821" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3823" } }, { @@ -23664,7 +23664,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3832" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3834" } }, { @@ -23691,7 +23691,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3843" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3845" } }, { @@ -23744,7 +23744,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3854" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3856" } }, { @@ -23832,7 +23832,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3865" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3867" } }, { @@ -24283,7 +24283,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3876" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3878" } }, { @@ -24450,7 +24450,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3887" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3889" } }, { @@ -24548,7 +24548,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3898" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3900" } }, { @@ -24721,7 +24721,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3909" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3911" } }, { @@ -24819,7 +24819,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3920" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3922" } }, { @@ -24970,7 +24970,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3931" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3933" } }, { @@ -25055,7 +25055,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3942" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3944" } }, { @@ -25123,7 +25123,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3953" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3955" } }, { @@ -25175,7 +25175,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3964" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3966" } }, { @@ -25243,7 +25243,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3975" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3977" } }, { @@ -25404,7 +25404,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3986" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3988" } }, { @@ -25451,7 +25451,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4008" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4010" } }, { @@ -25498,7 +25498,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4019" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4021" } }, { @@ -25541,7 +25541,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4041" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4043" } }, { @@ -25637,7 +25637,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4052" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4054" } }, { @@ -25903,7 +25903,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4063" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4065" } }, { @@ -25926,7 +25926,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4074" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4076" } }, { @@ -25969,7 +25969,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4085" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4087" } }, { @@ -26020,7 +26020,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4096" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4098" } }, { @@ -26065,7 +26065,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4107" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4109" } }, { @@ -26093,7 +26093,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4118" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4120" } }, { @@ -26133,7 +26133,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4129" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4131" } }, { @@ -26192,7 +26192,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4140" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4142" } }, { @@ -26236,7 +26236,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4151" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4153" } }, { @@ -26295,7 +26295,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4162" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4164" } }, { @@ -26332,7 +26332,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4173" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4175" } }, { @@ -26376,7 +26376,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4184" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4186" } }, { @@ -26416,7 +26416,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4195" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4197" } }, { @@ -26491,7 +26491,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4206" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4208" } }, { @@ -26699,7 +26699,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4217" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4219" } }, { @@ -26743,7 +26743,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4228" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4230" } }, { @@ -26833,7 +26833,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4239" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4241" } }, { @@ -26860,7 +26860,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4250" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4252" } } ] diff --git a/build/openrpc/gateway.json b/build/openrpc/gateway.json index 834732406..94e2dcd1c 100644 --- a/build/openrpc/gateway.json +++ b/build/openrpc/gateway.json @@ -242,7 +242,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4261" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4263" } }, { @@ -473,7 +473,106 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4272" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4274" + } + }, + { + "name": "Filecoin.ChainGetEvents", + "description": "```go\nfunc (s *GatewayStruct) ChainGetEvents(p0 context.Context, p1 cid.Cid) ([]types.Event, error) {\n\tif s.Internal.ChainGetEvents == nil {\n\t\treturn *new([]types.Event), ErrNotSupported\n\t}\n\treturn s.Internal.ChainGetEvents(p0, p1)\n}\n```", + "summary": "There are not yet any comments for this method.", + "paramStructure": "by-position", + "params": [ + { + "name": "p1", + "description": "cid.Cid", + "summary": "", + "schema": { + "title": "Content Identifier", + "description": "Cid represents a self-describing content addressed identifier. It is formed by a Version, a Codec (which indicates a multicodec-packed content type) and a Multihash.", + "examples": [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + } + ], + "type": [ + "string" + ] + }, + "required": true, + "deprecated": false + } + ], + "result": { + "name": "[]types.Event", + "description": "[]types.Event", + "summary": "", + "schema": { + "examples": [ + [ + { + "Emitter": 1000, + "Entries": [ + { + "Flags": 7, + "Key": "string value", + "Codec": 42, + "Value": "Ynl0ZSBhcnJheQ==" + } + ] + } + ] + ], + "items": [ + { + "additionalProperties": false, + "properties": { + "Emitter": { + "title": "number", + "type": "number" + }, + "Entries": { + "items": { + "additionalProperties": false, + "properties": { + "Codec": { + "title": "number", + "type": "number" + }, + "Flags": { + "title": "number", + "type": "number" + }, + "Key": { + "type": "string" + }, + "Value": { + "media": { + "binaryEncoding": "base64" + }, + "type": "string" + } + }, + "type": "object" + }, + "type": "array" + } + }, + "type": [ + "object" + ] + } + ], + "type": [ + "array" + ] + }, + "required": true, + "deprecated": false + }, + "deprecated": false, + "externalDocs": { + "description": "Github remote link", + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4285" } }, { @@ -505,7 +604,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4283" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4296" } }, { @@ -611,7 +710,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4294" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4307" } }, { @@ -704,7 +803,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4305" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4318" } }, { @@ -788,7 +887,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4316" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4329" } }, { @@ -888,7 +987,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4327" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4340" } }, { @@ -944,7 +1043,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4338" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4351" } }, { @@ -1017,7 +1116,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4349" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4362" } }, { @@ -1090,7 +1189,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4360" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4373" } }, { @@ -1137,7 +1236,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4371" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4384" } }, { @@ -1169,7 +1268,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4382" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4395" } }, { @@ -1206,7 +1305,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4404" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4417" } }, { @@ -1253,7 +1352,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4415" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4428" } }, { @@ -1293,7 +1392,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4426" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4439" } }, { @@ -1340,7 +1439,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4437" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4450" } }, { @@ -1369,7 +1468,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4448" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4461" } }, { @@ -1506,7 +1605,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4459" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4472" } }, { @@ -1535,7 +1634,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4470" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4483" } }, { @@ -1589,7 +1688,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4481" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4494" } }, { @@ -1680,7 +1779,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4492" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4505" } }, { @@ -1708,7 +1807,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4503" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4516" } }, { @@ -1798,7 +1897,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4514" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4527" } }, { @@ -2054,7 +2153,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4525" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4538" } }, { @@ -2299,7 +2398,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4536" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4549" } }, { @@ -2355,7 +2454,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4547" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4560" } }, { @@ -2402,7 +2501,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4558" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4571" } }, { @@ -2500,7 +2599,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4569" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4582" } }, { @@ -2566,7 +2665,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4580" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4593" } }, { @@ -2632,7 +2731,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4591" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4604" } }, { @@ -2741,7 +2840,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4602" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4615" } }, { @@ -2799,7 +2898,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4613" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4626" } }, { @@ -2921,7 +3020,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4624" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4637" } }, { @@ -3108,7 +3207,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4635" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4648" } }, { @@ -3312,7 +3411,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4646" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4659" } }, { @@ -3403,7 +3502,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4657" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4670" } }, { @@ -3461,7 +3560,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4668" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4681" } }, { @@ -3719,7 +3818,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4679" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4692" } }, { @@ -3994,7 +4093,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4690" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4703" } }, { @@ -4022,7 +4121,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4701" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4714" } }, { @@ -4060,7 +4159,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4712" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4725" } }, { @@ -4168,7 +4267,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4723" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4736" } }, { @@ -4206,7 +4305,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4734" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4747" } }, { @@ -4235,7 +4334,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4745" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4758" } }, { @@ -4298,7 +4397,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4756" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4769" } }, { @@ -4361,7 +4460,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4767" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4780" } }, { @@ -4406,7 +4505,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4778" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4791" } }, { @@ -4528,7 +4627,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4789" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4802" } }, { @@ -4683,7 +4782,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4800" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4813" } }, { @@ -4737,7 +4836,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4811" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4824" } }, { @@ -4791,7 +4890,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4822" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4835" } }, { @@ -4893,7 +4992,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4833" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4846" } }, { @@ -5116,7 +5215,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4844" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4857" } }, { @@ -5299,7 +5398,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4855" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4868" } }, { @@ -5493,7 +5592,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4866" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4879" } }, { @@ -5539,7 +5638,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4877" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4890" } }, { @@ -5689,7 +5788,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4888" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4901" } }, { @@ -5826,7 +5925,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4899" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4912" } }, { @@ -5894,7 +5993,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4910" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4923" } }, { @@ -6011,7 +6110,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4921" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4934" } }, { @@ -6102,7 +6201,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4932" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4945" } }, { @@ -6188,7 +6287,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4943" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4956" } }, { @@ -6215,7 +6314,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4954" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4967" } }, { @@ -6242,7 +6341,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4965" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4978" } }, { @@ -6310,7 +6409,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4976" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4989" } }, { @@ -6816,7 +6915,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4987" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5000" } }, { @@ -6913,7 +7012,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4998" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5011" } }, { @@ -7013,7 +7112,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5009" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5022" } }, { @@ -7113,7 +7212,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5020" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5033" } }, { @@ -7238,7 +7337,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5031" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5044" } }, { @@ -7347,7 +7446,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5042" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5055" } }, { @@ -7450,7 +7549,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5053" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5066" } }, { @@ -7580,7 +7679,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5064" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5077" } }, { @@ -7687,7 +7786,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5075" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5088" } }, { @@ -7748,7 +7847,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5086" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5099" } }, { @@ -7816,7 +7915,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5097" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5110" } }, { @@ -7897,7 +7996,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5108" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5121" } }, { @@ -8056,7 +8155,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5119" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5132" } }, { @@ -8149,7 +8248,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5130" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5143" } }, { @@ -8350,7 +8449,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5141" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5154" } }, { @@ -8461,7 +8560,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5152" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5165" } }, { @@ -8592,7 +8691,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5163" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5176" } }, { @@ -8678,7 +8777,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5174" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5187" } }, { @@ -8705,7 +8804,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5185" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5198" } }, { @@ -8758,7 +8857,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5196" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5209" } }, { @@ -8846,7 +8945,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5207" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5220" } }, { @@ -9297,7 +9396,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5218" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5231" } }, { @@ -9464,7 +9563,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5229" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5242" } }, { @@ -9637,7 +9736,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5240" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5253" } }, { @@ -9705,7 +9804,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5251" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5264" } }, { @@ -9773,7 +9872,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5262" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5275" } }, { @@ -9934,7 +10033,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5273" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5286" } }, { @@ -9979,7 +10078,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5295" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5308" } }, { @@ -10024,7 +10123,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5306" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5319" } }, { @@ -10051,7 +10150,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5317" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5330" } } ] diff --git a/build/openrpc/miner.json b/build/openrpc/miner.json index bc6f4e67d..6157e3323 100644 --- a/build/openrpc/miner.json +++ b/build/openrpc/miner.json @@ -30,7 +30,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5713" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5726" } }, { @@ -109,7 +109,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5724" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5737" } }, { @@ -155,7 +155,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5735" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5748" } }, { @@ -203,7 +203,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5746" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5759" } }, { @@ -251,7 +251,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5757" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5770" } }, { @@ -354,7 +354,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5768" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5781" } }, { @@ -428,7 +428,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5779" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5792" } }, { @@ -591,7 +591,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5790" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5803" } }, { @@ -742,7 +742,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5801" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5814" } }, { @@ -781,7 +781,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5812" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5825" } }, { @@ -833,7 +833,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5823" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5836" } }, { @@ -872,7 +872,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5845" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5858" } }, { @@ -924,7 +924,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5856" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5869" } }, { @@ -996,7 +996,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5867" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5880" } }, { @@ -1035,7 +1035,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5878" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5891" } }, { @@ -1074,7 +1074,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5889" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5902" } }, { @@ -1101,7 +1101,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5900" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5913" } }, { @@ -1128,7 +1128,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5911" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5924" } }, { @@ -1155,7 +1155,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5922" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5935" } }, { @@ -1182,7 +1182,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5933" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5946" } }, { @@ -1209,7 +1209,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5944" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5957" } }, { @@ -1236,7 +1236,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5955" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5968" } }, { @@ -1294,7 +1294,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5966" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5979" } }, { @@ -1421,7 +1421,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5977" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5990" } }, { @@ -1461,7 +1461,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5988" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6001" } }, { @@ -1500,7 +1500,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5999" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6012" } }, { @@ -1539,7 +1539,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6010" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6023" } }, { @@ -1578,7 +1578,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6021" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6034" } }, { @@ -1617,7 +1617,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6032" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6045" } }, { @@ -1656,7 +1656,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6043" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6056" } }, { @@ -1695,7 +1695,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6054" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6067" } }, { @@ -1747,7 +1747,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6065" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6078" } }, { @@ -1770,7 +1770,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6076" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6089" } }, { @@ -1813,7 +1813,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6087" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6100" } }, { @@ -1884,7 +1884,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6098" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6111" } }, { @@ -2265,7 +2265,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6109" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6122" } }, { @@ -2364,7 +2364,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6131" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6144" } }, { @@ -2415,7 +2415,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6153" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6166" } }, { @@ -2473,7 +2473,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6164" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6177" } }, { @@ -2616,7 +2616,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6175" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6188" } }, { @@ -2743,7 +2743,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6186" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6199" } }, { @@ -3007,7 +3007,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6197" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6210" } }, { @@ -3044,7 +3044,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6208" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6221" } }, { @@ -3182,7 +3182,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6219" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6232" } }, { @@ -3205,7 +3205,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6230" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6243" } }, { @@ -3276,7 +3276,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6241" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6254" } }, { @@ -3319,7 +3319,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6252" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6265" } }, { @@ -3426,7 +3426,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6263" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6276" } }, { @@ -3489,7 +3489,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6274" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6287" } }, { @@ -3521,7 +3521,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6285" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6298" } }, { @@ -3609,7 +3609,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6296" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6309" } }, { @@ -3700,7 +3700,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6307" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6320" } }, { @@ -3740,7 +3740,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6318" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6331" } }, { @@ -3780,7 +3780,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6329" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6342" } }, { @@ -3821,7 +3821,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6340" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6353" } }, { @@ -3889,7 +3889,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6351" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6364" } }, { @@ -4020,7 +4020,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6362" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6375" } }, { @@ -4151,7 +4151,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6373" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6386" } }, { @@ -4251,7 +4251,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6384" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6397" } }, { @@ -4351,7 +4351,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6395" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6408" } }, { @@ -4451,7 +4451,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6406" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6419" } }, { @@ -4551,7 +4551,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6417" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6430" } }, { @@ -4651,7 +4651,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6428" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6441" } }, { @@ -4751,7 +4751,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6439" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6452" } }, { @@ -4875,7 +4875,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6450" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6463" } }, { @@ -4999,7 +4999,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6461" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6474" } }, { @@ -5114,7 +5114,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6472" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6485" } }, { @@ -5214,7 +5214,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6483" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6496" } }, { @@ -5347,7 +5347,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6494" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6507" } }, { @@ -5471,7 +5471,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6505" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6518" } }, { @@ -5595,7 +5595,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6516" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6529" } }, { @@ -5719,7 +5719,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6527" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6540" } }, { @@ -5852,7 +5852,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6538" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6551" } }, { @@ -5952,7 +5952,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6549" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6562" } }, { @@ -5993,7 +5993,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6560" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6573" } }, { @@ -6065,7 +6065,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6571" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6584" } }, { @@ -6115,7 +6115,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6582" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6595" } }, { @@ -6159,7 +6159,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6593" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6606" } }, { @@ -6200,7 +6200,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6604" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6617" } }, { @@ -6444,7 +6444,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6615" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6628" } }, { @@ -6518,7 +6518,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6626" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6639" } }, { @@ -6568,7 +6568,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6637" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6650" } }, { @@ -6597,7 +6597,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6648" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6661" } }, { @@ -6626,7 +6626,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6659" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6672" } }, { @@ -6682,7 +6682,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6670" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6683" } }, { @@ -6705,7 +6705,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6681" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6694" } }, { @@ -6765,7 +6765,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6692" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6705" } }, { @@ -6804,7 +6804,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6703" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6716" } }, { @@ -6844,7 +6844,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6714" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6727" } }, { @@ -6917,7 +6917,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6725" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6738" } }, { @@ -6981,7 +6981,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6736" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6749" } }, { @@ -7044,7 +7044,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6747" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6760" } }, { @@ -7094,7 +7094,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6758" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6771" } }, { @@ -7653,7 +7653,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6769" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6782" } }, { @@ -7694,7 +7694,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6780" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6793" } }, { @@ -7735,7 +7735,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6791" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6804" } }, { @@ -7776,7 +7776,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6802" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6815" } }, { @@ -7817,7 +7817,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6813" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6826" } }, { @@ -7858,7 +7858,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6824" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6837" } }, { @@ -7889,7 +7889,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6835" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6848" } }, { @@ -7939,7 +7939,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6846" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6859" } }, { @@ -7980,7 +7980,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6857" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6870" } }, { @@ -8019,7 +8019,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6868" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6881" } }, { @@ -8083,7 +8083,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6879" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6892" } }, { @@ -8141,7 +8141,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6890" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6903" } }, { @@ -8588,7 +8588,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6901" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6914" } }, { @@ -8624,7 +8624,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6912" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6925" } }, { @@ -8767,7 +8767,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6923" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6936" } }, { @@ -8823,7 +8823,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6934" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6947" } }, { @@ -8862,7 +8862,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6945" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6958" } }, { @@ -9021,7 +9021,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6956" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6969" } }, { @@ -9073,7 +9073,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6967" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6980" } }, { @@ -9230,7 +9230,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6978" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6991" } }, { @@ -9330,7 +9330,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6989" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7002" } }, { @@ -9384,7 +9384,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7000" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7013" } }, { @@ -9423,7 +9423,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7011" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7024" } }, { @@ -9508,7 +9508,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7022" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7035" } }, { @@ -9684,7 +9684,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7033" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7046" } }, { @@ -9780,7 +9780,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7044" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7057" } }, { @@ -9894,7 +9894,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7055" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7068" } }, { @@ -9948,7 +9948,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7066" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7079" } }, { @@ -9982,7 +9982,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7077" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7090" } }, { @@ -10069,7 +10069,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7088" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7101" } }, { @@ -10123,7 +10123,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7099" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7112" } }, { @@ -10223,7 +10223,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7110" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7123" } }, { @@ -10300,7 +10300,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7121" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7134" } }, { @@ -10391,7 +10391,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7132" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7145" } }, { @@ -10430,7 +10430,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7143" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7156" } }, { @@ -10546,7 +10546,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7154" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7167" } }, { @@ -12646,7 +12646,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7165" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7178" } } ] diff --git a/build/openrpc/worker.json b/build/openrpc/worker.json index 6604b5925..0afaa17ae 100644 --- a/build/openrpc/worker.json +++ b/build/openrpc/worker.json @@ -161,7 +161,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7253" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7266" } }, { @@ -252,7 +252,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7264" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7277" } }, { @@ -420,7 +420,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7275" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7288" } }, { @@ -447,7 +447,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7286" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7299" } }, { @@ -597,7 +597,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7297" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7310" } }, { @@ -700,7 +700,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7308" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7321" } }, { @@ -803,7 +803,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7319" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7332" } }, { @@ -925,7 +925,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7330" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7343" } }, { @@ -1135,7 +1135,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7341" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7354" } }, { @@ -1306,7 +1306,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7352" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7365" } }, { @@ -3350,7 +3350,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7363" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7376" } }, { @@ -3470,7 +3470,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7374" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7387" } }, { @@ -3531,7 +3531,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7385" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7398" } }, { @@ -3569,7 +3569,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7396" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7409" } }, { @@ -3729,7 +3729,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7407" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7420" } }, { @@ -3913,7 +3913,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7418" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7431" } }, { @@ -4054,7 +4054,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7429" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7442" } }, { @@ -4107,7 +4107,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7440" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7453" } }, { @@ -4250,7 +4250,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7451" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7464" } }, { @@ -4474,7 +4474,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7462" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7475" } }, { @@ -4601,7 +4601,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7473" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7486" } }, { @@ -4768,7 +4768,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7484" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7497" } }, { @@ -4895,7 +4895,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7495" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7508" } }, { @@ -4933,7 +4933,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7506" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7519" } }, { @@ -4972,7 +4972,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7517" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7530" } }, { @@ -4995,7 +4995,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7528" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7541" } }, { @@ -5034,7 +5034,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7539" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7552" } }, { @@ -5057,7 +5057,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7550" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7563" } }, { @@ -5096,7 +5096,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7561" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7574" } }, { @@ -5130,7 +5130,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7572" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7585" } }, { @@ -5184,7 +5184,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7583" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7596" } }, { @@ -5223,7 +5223,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7594" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7607" } }, { @@ -5262,7 +5262,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7605" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7618" } }, { @@ -5297,7 +5297,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7616" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7629" } }, { @@ -5477,7 +5477,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7627" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7640" } }, { @@ -5506,7 +5506,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7638" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7651" } }, { @@ -5529,7 +5529,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7649" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7662" } } ] diff --git a/gateway/node.go b/gateway/node.go index f3ecb7640..814d9c8cb 100644 --- a/gateway/node.go +++ b/gateway/node.go @@ -149,6 +149,7 @@ type TargetAPI interface { GetActorEvents(ctx context.Context, filter *types.ActorEventFilter) ([]*types.ActorEvent, error) SubscribeActorEvents(ctx context.Context, filter *types.ActorEventFilter) (<-chan *types.ActorEvent, error) + ChainGetEvents(ctx context.Context, eventsRoot cid.Cid) ([]types.Event, error) } var _ TargetAPI = *new(api.FullNode) // gateway depends on latest diff --git a/gateway/proxy_fil.go b/gateway/proxy_fil.go index b3480752b..e86d9e0c5 100644 --- a/gateway/proxy_fil.go +++ b/gateway/proxy_fil.go @@ -461,6 +461,13 @@ func (gw *Node) SubscribeActorEvents(ctx context.Context, filter *types.ActorEve return gw.target.SubscribeActorEvents(ctx, filter) } +func (gw *Node) ChainGetEvents(ctx context.Context, eventsRoot cid.Cid) ([]types.Event, error) { + if err := gw.limit(ctx, chainRateLimitTokens); err != nil { + return nil, err + } + return gw.target.ChainGetEvents(ctx, eventsRoot) +} + func (gw *Node) StateReadState(ctx context.Context, actor address.Address, tsk types.TipSetKey) (*api.ActorState, error) { if err := gw.limit(ctx, stateRateLimitTokens); err != nil { return nil, err From 81ba6ab6f095e7d783911912879f86e7e5c4fe6f Mon Sep 17 00:00:00 2001 From: "Andrew Jackson (Ajax)" Date: Fri, 15 Mar 2024 16:38:13 -0500 Subject: [PATCH 269/394] feat: Curio - Easy Migration (#11617) * feat: lp mig - first few steps * lp mig: default tasks * code comments * docs * lp-mig-progress * shared * comments and todos * fix: curio: rename lotus-provider to curio (#11645) * rename provider to curio * install gotext * fix lint errors, mod tidy * fix typo * fix API_INFO and add gotext to circleCI * add back gotext * add gotext after remerge * lp: channels doc * finish easy-migration TODOs * out generate * merging and more renames * avoid make-all * minor doc stuff * cu: make gen * make gen fix * make gen * tryfix * go mod tidy * minor ez migration fixes * ez setup - ui cleanups * better error message * guided setup colors * better path to saveconfigtolayer * loadconfigwithupgrades fix * readMiner oops * guided - homedir * err if miner is running * prompt error should exit * process already running, miner_id sectors in migration * dont prompt for language a second time * check miner stopped * unlock repo * render and sql oops * curio easyMig - some fixes * easyMigration runs successfully * lint * review fixes * fix backup path * fixes1 * fixes2 * fixes 3 --------- Co-authored-by: LexLuthr <88259624+LexLuthr@users.noreply.github.com> Co-authored-by: LexLuthr --- .circleci/config.yml | 2 +- .gitignore | 5 +- Dockerfile | 8 +- Makefile | 54 +- api/{api_lp.go => api_curio.go} | 2 +- api/client/client.go | 6 +- api/docgen/docgen.go | 8 +- api/proxy_gen.go | 280 ++--- api/v1api/latest.go | 2 +- api/version.go | 2 +- build/openrpc/full.json | 486 ++++---- build/openrpc/gateway.json | 192 +-- cli/util/api.go | 164 +-- cmd/{lotus-provider => curio}/cli.go | 6 +- cmd/{lotus-provider => curio}/config.go | 42 +- cmd/{lotus-provider => curio}/config_new.go | 6 +- cmd/curio/deps/apiinfo.go | 94 ++ cmd/{lotus-provider => curio}/deps/deps.go | 160 ++- cmd/curio/guidedsetup/guidedsetup.go | 590 +++++++++ cmd/curio/guidedsetup/shared.go | 264 ++++ cmd/curio/internal/translations/catalog.go | 331 +++++ .../internal/translations/knowns/main.go | 82 ++ .../translations/locales/en/out.gotext.json | 1104 +++++++++++++++++ .../locales/ko/messages.gotext.json | 704 +++++++++++ .../translations/locales/ko/out.gotext.json | 89 ++ .../locales/zh/messages.gotext.json | 704 +++++++++++ .../translations/locales/zh/out.gotext.json | 89 ++ .../internal/translations/translations.go | 27 + cmd/curio/internal/translations/updateLang.sh | 8 + cmd/{lotus-provider => curio}/main.go | 43 +- cmd/curio/migrate.go | 71 ++ cmd/{lotus-provider => curio}/pipeline.go | 6 +- cmd/{lotus-provider => curio}/proving.go | 14 +- cmd/{lotus-provider => curio}/rpc/rpc.go | 52 +- cmd/{lotus-provider => curio}/run.go | 20 +- cmd/{lotus-provider => curio}/stop.go | 2 +- cmd/{lotus-provider => curio}/storage.go | 10 +- cmd/{lotus-provider => curio}/tasks/tasks.go | 52 +- cmd/lotus-provider/config_migrate.go | 254 ---- cmd/lotus-shed/{lpdeal.go => deal.go} | 10 +- {provider => curiosrc}/address.go | 6 +- curiosrc/builder.go | 46 + .../chainsched/chain_sched.go | 22 +- {provider/lpffi => curiosrc/ffi}/sdr_funcs.go | 6 +- .../lpffi => curiosrc/ffi}/task_storage.go | 2 +- .../market}/deal_ingest.go | 6 +- .../market}/fakelm/iface.go | 0 .../market}/fakelm/lmimpl.go | 8 +- .../lpmessage => curiosrc/message}/sender.go | 6 +- .../lpmessage => curiosrc/message}/watch.go | 6 +- .../multictladdr/multiaddresses.go | 2 +- .../lpproof => curiosrc/proof}/treed_build.go | 2 +- .../proof}/treed_build_test.go | 2 +- {provider/lpseal => curiosrc/seal}/README.md | 0 {provider/lpseal => curiosrc/seal}/poller.go | 2 +- .../seal}/poller_commit_msg.go | 2 +- .../seal}/poller_precommit_msg.go | 2 +- .../seal}/sector_num_alloc.go | 2 +- .../lpseal => curiosrc/seal}/task_finalize.go | 10 +- .../seal}/task_movestorage.go | 8 +- .../lpseal => curiosrc/seal}/task_porep.go | 8 +- .../lpseal => curiosrc/seal}/task_sdr.go | 16 +- .../seal}/task_submit_commit.go | 10 +- .../seal}/task_submit_precommit.go | 10 +- .../lpseal => curiosrc/seal}/task_trees.go | 8 +- .../seal}/task_trees_test.go | 2 +- .../lpweb => curiosrc/web}/api/debug/debug.go | 6 +- curiosrc/web/api/routes.go | 13 + .../lpweb => curiosrc/web}/hapi/robust_rpc.go | 0 .../lpweb => curiosrc/web}/hapi/routes.go | 4 +- .../lpweb => curiosrc/web}/hapi/simpleinfo.go | 2 +- .../web}/hapi/simpleinfo_pipeline_porep.go | 0 .../web}/hapi/watch_actor.go | 0 .../web}/hapi/web/actor_summary.gohtml | 0 .../web}/hapi/web/chain_rpcs.gohtml | 0 .../web}/hapi/web/cluster_machines.gohtml | 0 .../web}/hapi/web/cluster_task_history.gohtml | 0 .../web}/hapi/web/cluster_tasks.gohtml | 0 .../hapi/web/pipeline_porep_sectors.gohtml | 0 .../web}/hapi/web/pipline_porep.gohtml | 0 {provider/lpweb => curiosrc/web}/srv.go | 15 +- .../web}/static/chain-connectivity.js | 0 .../lpweb => curiosrc/web}/static/index.html | 4 +- .../lpweb => curiosrc/web}/static/main.css | 0 .../web}/static/pipeline_porep.html | 0 .../window}/compute_do.go | 4 +- .../window}/compute_task.go | 8 +- .../window}/faults_simple.go | 2 +- .../window}/recover_task.go | 14 +- .../window}/submit_task.go | 12 +- .../winning}/winning_task.go | 4 +- ...ds-provider.md => api-v0-methods-curio.md} | 1 + .../{cli-lotus-provider.md => cli-curio.md} | 266 ++-- ...-config.toml => default-curio-config.toml} | 0 go.mod | 13 +- go.sum | 20 +- itests/kit/ensemble.go | 6 +- itests/kit/node_full.go | 4 +- lib/harmony/harmonydb/harmonydb.go | 2 +- .../harmonydb/sql/20240212-common-layers.sql | 42 + lib/harmony/resources/resources.go | 2 +- node/config/def.go | 10 +- node/config/doc_gen.go | 584 ++++----- node/config/load.go | 3 +- node/config/types.go | 14 +- node/modules/storageminer_svc.go | 6 +- node/repo/fsrepo.go | 22 +- node/repo/interface.go | 2 +- provider/builder.go | 46 - provider/lpweb/api/routes.go | 13 - .../{lotus-provider.service => curio.service} | 6 +- scripts/generate-lotus-cli.py | 2 +- 112 files changed, 5702 insertions(+), 1689 deletions(-) rename api/{api_lp.go => api_curio.go} (98%) rename cmd/{lotus-provider => curio}/cli.go (96%) rename cmd/{lotus-provider => curio}/config.go (92%) rename cmd/{lotus-provider => curio}/config_new.go (95%) create mode 100644 cmd/curio/deps/apiinfo.go rename cmd/{lotus-provider => curio}/deps/deps.go (63%) create mode 100644 cmd/curio/guidedsetup/guidedsetup.go create mode 100644 cmd/curio/guidedsetup/shared.go create mode 100644 cmd/curio/internal/translations/catalog.go create mode 100644 cmd/curio/internal/translations/knowns/main.go create mode 100644 cmd/curio/internal/translations/locales/en/out.gotext.json create mode 100644 cmd/curio/internal/translations/locales/ko/messages.gotext.json create mode 100644 cmd/curio/internal/translations/locales/ko/out.gotext.json create mode 100644 cmd/curio/internal/translations/locales/zh/messages.gotext.json create mode 100644 cmd/curio/internal/translations/locales/zh/out.gotext.json create mode 100644 cmd/curio/internal/translations/translations.go create mode 100755 cmd/curio/internal/translations/updateLang.sh rename cmd/{lotus-provider => curio}/main.go (75%) create mode 100644 cmd/curio/migrate.go rename cmd/{lotus-provider => curio}/pipeline.go (92%) rename cmd/{lotus-provider => curio}/proving.go (94%) rename cmd/{lotus-provider => curio}/rpc/rpc.go (80%) rename cmd/{lotus-provider => curio}/run.go (88%) rename cmd/{lotus-provider => curio}/stop.go (91%) rename cmd/{lotus-provider => curio}/storage.go (97%) rename cmd/{lotus-provider => curio}/tasks/tasks.go (64%) delete mode 100644 cmd/lotus-provider/config_migrate.go rename cmd/lotus-shed/{lpdeal.go => deal.go} (98%) rename {provider => curiosrc}/address.go (89%) create mode 100644 curiosrc/builder.go rename {provider => curiosrc}/chainsched/chain_sched.go (77%) rename {provider/lpffi => curiosrc/ffi}/sdr_funcs.go (98%) rename {provider/lpffi => curiosrc/ffi}/task_storage.go (99%) rename {provider/lpmarket => curiosrc/market}/deal_ingest.go (95%) rename {provider/lpmarket => curiosrc/market}/fakelm/iface.go (100%) rename {provider/lpmarket => curiosrc/market}/fakelm/lmimpl.go (98%) rename {provider/lpmessage => curiosrc/message}/sender.go (98%) rename {provider/lpmessage => curiosrc/message}/watch.go (97%) rename {provider => curiosrc}/multictladdr/multiaddresses.go (97%) rename {provider/lpproof => curiosrc/proof}/treed_build.go (99%) rename {provider/lpproof => curiosrc/proof}/treed_build_test.go (99%) rename {provider/lpseal => curiosrc/seal}/README.md (100%) rename {provider/lpseal => curiosrc/seal}/poller.go (99%) rename {provider/lpseal => curiosrc/seal}/poller_commit_msg.go (99%) rename {provider/lpseal => curiosrc/seal}/poller_precommit_msg.go (99%) rename {provider/lpseal => curiosrc/seal}/sector_num_alloc.go (99%) rename {provider/lpseal => curiosrc/seal}/task_finalize.go (94%) rename {provider/lpseal => curiosrc/seal}/task_movestorage.go (94%) rename {provider/lpseal => curiosrc/seal}/task_porep.go (97%) rename {provider/lpseal => curiosrc/seal}/task_sdr.go (94%) rename {provider/lpseal => curiosrc/seal}/task_submit_commit.go (94%) rename {provider/lpseal => curiosrc/seal}/task_submit_precommit.go (95%) rename {provider/lpseal => curiosrc/seal}/task_trees.go (97%) rename {provider/lpseal => curiosrc/seal}/task_trees_test.go (99%) rename {provider/lpweb => curiosrc/web}/api/debug/debug.go (97%) create mode 100644 curiosrc/web/api/routes.go rename {provider/lpweb => curiosrc/web}/hapi/robust_rpc.go (100%) rename {provider/lpweb => curiosrc/web}/hapi/routes.go (89%) rename {provider/lpweb => curiosrc/web}/hapi/simpleinfo.go (99%) rename {provider/lpweb => curiosrc/web}/hapi/simpleinfo_pipeline_porep.go (100%) rename {provider/lpweb => curiosrc/web}/hapi/watch_actor.go (100%) rename {provider/lpweb => curiosrc/web}/hapi/web/actor_summary.gohtml (100%) rename {provider/lpweb => curiosrc/web}/hapi/web/chain_rpcs.gohtml (100%) rename {provider/lpweb => curiosrc/web}/hapi/web/cluster_machines.gohtml (100%) rename {provider/lpweb => curiosrc/web}/hapi/web/cluster_task_history.gohtml (100%) rename {provider/lpweb => curiosrc/web}/hapi/web/cluster_tasks.gohtml (100%) rename {provider/lpweb => curiosrc/web}/hapi/web/pipeline_porep_sectors.gohtml (100%) rename {provider/lpweb => curiosrc/web}/hapi/web/pipline_porep.gohtml (100%) rename {provider/lpweb => curiosrc/web}/srv.go (84%) rename {provider/lpweb => curiosrc/web}/static/chain-connectivity.js (100%) rename {provider/lpweb => curiosrc/web}/static/index.html (97%) rename {provider/lpweb => curiosrc/web}/static/main.css (100%) rename {provider/lpweb => curiosrc/web}/static/pipeline_porep.html (100%) rename {provider/lpwindow => curiosrc/window}/compute_do.go (99%) rename {provider/lpwindow => curiosrc/window}/compute_task.go (98%) rename {provider/lpwindow => curiosrc/window}/faults_simple.go (99%) rename {provider/lpwindow => curiosrc/window}/recover_task.go (96%) rename {provider/lpwindow => curiosrc/window}/submit_task.go (95%) rename {provider/lpwinning => curiosrc/winning}/winning_task.go (99%) rename documentation/en/{api-v0-methods-provider.md => api-v0-methods-curio.md} (99%) rename documentation/en/{cli-lotus-provider.md => cli-curio.md} (55%) rename documentation/en/{default-lotus-provider-config.toml => default-curio-config.toml} (100%) create mode 100644 lib/harmony/harmonydb/sql/20240212-common-layers.sql delete mode 100644 provider/builder.go delete mode 100644 provider/lpweb/api/routes.go rename scripts/{lotus-provider.service => curio.service} (55%) diff --git a/.circleci/config.yml b/.circleci/config.yml index 0dafceb09..30ebaf5d9 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1031,7 +1031,7 @@ workflows: requires: - build suite: utest-unit-rest - target: "./blockstore/... ./build/... ./chain/... ./conformance/... ./gateway/... ./journal/... ./lib/... ./markets/... ./paychmgr/... ./provider/... ./tools/..." + target: "./blockstore/... ./build/... ./chain/... ./conformance/... ./curiosrc/... ./gateway/... ./journal/... ./lib/... ./markets/... ./paychmgr/... ./tools/..." resource_class: 2xlarge - test: name: test-unit-storage diff --git a/.gitignore b/.gitignore index c40a76fd0..ea354ba73 100644 --- a/.gitignore +++ b/.gitignore @@ -6,7 +6,7 @@ /lotus-chainwatch /lotus-shed /lotus-sim -/lotus-provider +/curio /lotus-townhall /lotus-fountain /lotus-stats @@ -36,6 +36,9 @@ build/paramfetch.sh /darwin /linux *.snap +curio +devgen.car +localnet.json *-fuzz.zip /chain/types/work_msg/ diff --git a/Dockerfile b/Dockerfile index 14469533b..5b77b134a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -109,7 +109,7 @@ COPY --from=lotus-builder /opt/filecoin/lotus-wallet /usr/local/bin/ COPY --from=lotus-builder /opt/filecoin/lotus-gateway /usr/local/bin/ COPY --from=lotus-builder /opt/filecoin/lotus-miner /usr/local/bin/ COPY --from=lotus-builder /opt/filecoin/lotus-worker /usr/local/bin/ -COPY --from=lotus-builder /opt/filecoin/lotus-provider /usr/local/bin/ +COPY --from=lotus-builder /opt/filecoin/curio /usr/local/bin/ COPY --from=lotus-builder /opt/filecoin/lotus-stats /usr/local/bin/ COPY --from=lotus-builder /opt/filecoin/lotus-fountain /usr/local/bin/ @@ -118,13 +118,13 @@ RUN mkdir /var/lib/lotus RUN mkdir /var/lib/lotus-miner RUN mkdir /var/lib/lotus-worker RUN mkdir /var/lib/lotus-wallet -RUN mkdir /var/lib/lotus-provider +RUN mkdir /var/lib/curio RUN chown fc: /var/tmp/filecoin-proof-parameters RUN chown fc: /var/lib/lotus RUN chown fc: /var/lib/lotus-miner RUN chown fc: /var/lib/lotus-worker RUN chown fc: /var/lib/lotus-wallet -RUN chown fc: /var/lib/lotus-provider +RUN chown fc: /var/lib/curio VOLUME /var/tmp/filecoin-proof-parameters @@ -132,7 +132,7 @@ VOLUME /var/lib/lotus VOLUME /var/lib/lotus-miner VOLUME /var/lib/lotus-worker VOLUME /var/lib/lotus-wallet -VOLUME /var/lib/lotus-provider +VOLUME /var/lib/curio EXPOSE 1234 EXPOSE 2345 diff --git a/Makefile b/Makefile index 5724e38fa..6a1c9cc8a 100644 --- a/Makefile +++ b/Makefile @@ -66,7 +66,7 @@ CLEAN+=build/.update-modules deps: $(BUILD_DEPS) .PHONY: deps -build-devnets: build lotus-seed lotus-shed lotus-provider +build-devnets: build lotus-seed lotus-shed curio .PHONY: build-devnets debug: GOFLAGS+=-tags=debug @@ -97,14 +97,14 @@ lotus-miner: $(BUILD_DEPS) .PHONY: lotus-miner BINS+=lotus-miner -lotus-provider: $(BUILD_DEPS) - rm -f lotus-provider - $(GOCC) build $(GOFLAGS) -o lotus-provider ./cmd/lotus-provider -.PHONY: lotus-provider -BINS+=lotus-provider +curio: $(BUILD_DEPS) + rm -f curio + $(GOCC) build $(GOFLAGS) -o curio ./cmd/curio +.PHONY: curio +BINS+=curio -lp2k: GOFLAGS+=-tags=2k -lp2k: lotus-provider +cu2k: GOFLAGS+=-tags=2k +cu2k: curio lotus-worker: $(BUILD_DEPS) rm -f lotus-worker @@ -124,13 +124,13 @@ lotus-gateway: $(BUILD_DEPS) .PHONY: lotus-gateway BINS+=lotus-gateway -build: lotus lotus-miner lotus-worker lotus-provider +build: lotus lotus-miner lotus-worker curio @[[ $$(type -P "lotus") ]] && echo "Caution: you have \ an existing lotus binary in your PATH. This may cause problems if you don't run 'sudo make install'" || true .PHONY: build -install: install-daemon install-miner install-worker install-provider +install: install-daemon install-miner install-worker install-curio install-daemon: install -C ./lotus /usr/local/bin/lotus @@ -138,8 +138,8 @@ install-daemon: install-miner: install -C ./lotus-miner /usr/local/bin/lotus-miner -install-provider: - install -C ./lotus-provider /usr/local/bin/lotus-provider +install-curio: + install -C ./curio /usr/local/bin/curio install-worker: install -C ./lotus-worker /usr/local/bin/lotus-worker @@ -156,8 +156,8 @@ uninstall-daemon: uninstall-miner: rm -f /usr/local/bin/lotus-miner -uninstall-provider: - rm -f /usr/local/bin/lotus-provider +uninstall-curio: + rm -f /usr/local/bin/curio uninstall-worker: rm -f /usr/local/bin/lotus-worker @@ -260,13 +260,13 @@ install-miner-service: install-miner install-daemon-service @echo "To start the service, run: 'sudo systemctl start lotus-miner'" @echo "To enable the service on startup, run: 'sudo systemctl enable lotus-miner'" -install-provider-service: install-provider install-daemon-service +install-curio-service: install-curio install-daemon-service mkdir -p /etc/systemd/system mkdir -p /var/log/lotus - install -C -m 0644 ./scripts/lotus-provider.service /etc/systemd/system/lotus-provider.service + install -C -m 0644 ./scripts/curio.service /etc/systemd/system/curio.service systemctl daemon-reload @echo - @echo "lotus-provider service installed. Don't forget to run 'sudo systemctl start lotus-provider' to start it and 'sudo systemctl enable lotus-provider' for it to be enabled on startup." + @echo "Curio service installed. Don't forget to run 'sudo systemctl start curio' to start it and 'sudo systemctl enable curio' for it to be enabled on startup." install-main-services: install-miner-service @@ -286,10 +286,10 @@ clean-miner-service: rm -f /etc/systemd/system/lotus-miner.service systemctl daemon-reload -clean-provider-service: - -systemctl stop lotus-provider - -systemctl disable lotus-provider - rm -f /etc/systemd/system/lotus-provider.service +clean-curio-service: + -systemctl stop curio + -systemctl disable curio + rm -f /etc/systemd/system/curio.service systemctl daemon-reload clean-main-services: clean-daemon-service @@ -366,7 +366,7 @@ docsgen-md-bin: api-gen actors-gen docsgen-openrpc-bin: api-gen actors-gen $(GOCC) build $(GOFLAGS) -o docgen-openrpc ./api/docgen-openrpc/cmd -docsgen-md: docsgen-md-full docsgen-md-storage docsgen-md-worker docsgen-md-provider +docsgen-md: docsgen-md-full docsgen-md-storage docsgen-md-worker docsgen-md-curio docsgen-md-full: docsgen-md-bin ./docgen-md "api/api_full.go" "FullNode" "api" "./api" > documentation/en/api-v1-unstable-methods.md @@ -375,8 +375,8 @@ docsgen-md-storage: docsgen-md-bin ./docgen-md "api/api_storage.go" "StorageMiner" "api" "./api" > documentation/en/api-v0-methods-miner.md docsgen-md-worker: docsgen-md-bin ./docgen-md "api/api_worker.go" "Worker" "api" "./api" > documentation/en/api-v0-methods-worker.md -docsgen-md-provider: docsgen-md-bin - ./docgen-md "api/api_lp.go" "Provider" "api" "./api" > documentation/en/api-v0-methods-provider.md +docsgen-md-curio: docsgen-md-bin + ./docgen-md "api/api_curio.go" "Curio" "api" "./api" > documentation/en/api-v0-methods-curio.md docsgen-openrpc: docsgen-openrpc-full docsgen-openrpc-storage docsgen-openrpc-worker docsgen-openrpc-gateway @@ -401,16 +401,16 @@ gen: actors-code-gen type-gen cfgdoc-gen docsgen api-gen circleci jen: gen -snap: lotus lotus-miner lotus-worker lotus-provider +snap: lotus lotus-miner lotus-worker curio snapcraft # snapcraft upload ./lotus_*.snap # separate from gen because it needs binaries -docsgen-cli: lotus lotus-miner lotus-worker lotus-provider +docsgen-cli: lotus lotus-miner lotus-worker curio python3 ./scripts/generate-lotus-cli.py ./lotus config default > documentation/en/default-lotus-config.toml ./lotus-miner config default > documentation/en/default-lotus-miner-config.toml - ./lotus-provider config default > documentation/en/default-lotus-provider-config.toml + ./curio config default > documentation/en/default-curio-config.toml .PHONY: docsgen-cli print-%: diff --git a/api/api_lp.go b/api/api_curio.go similarity index 98% rename from api/api_lp.go rename to api/api_curio.go index 15e219fa5..c94c8a9ab 100644 --- a/api/api_lp.go +++ b/api/api_curio.go @@ -12,7 +12,7 @@ import ( "github.com/filecoin-project/lotus/storage/sealer/storiface" ) -type LotusProvider interface { +type Curio interface { Version(context.Context) (Version, error) //perm:admin AllocatePieceToSector(ctx context.Context, maddr address.Address, piece PieceDealInfo, rawSize int64, source url.URL, header http.Header) (SectorOffset, error) //perm:write diff --git a/api/client/client.go b/api/client/client.go index 4d51221f9..4a8ff9272 100644 --- a/api/client/client.go +++ b/api/client/client.go @@ -15,9 +15,9 @@ import ( "github.com/filecoin-project/lotus/lib/rpcenc" ) -// NewProviderRpc creates a new http jsonrpc client. -func NewProviderRpc(ctx context.Context, addr string, requestHeader http.Header) (api.LotusProvider, jsonrpc.ClientCloser, error) { - var res v1api.LotusProviderStruct +// NewCurioRpc creates a new http jsonrpc client. +func NewCurioRpc(ctx context.Context, addr string, requestHeader http.Header) (api.Curio, jsonrpc.ClientCloser, error) { + var res v1api.CurioStruct closer, err := jsonrpc.NewMergeClient(ctx, addr, "Filecoin", api.GetInternalStructs(&res), requestHeader, jsonrpc.WithErrors(api.RPCErrors)) diff --git a/api/docgen/docgen.go b/api/docgen/docgen.go index 29ac1d3e8..2d34a0903 100644 --- a/api/docgen/docgen.go +++ b/api/docgen/docgen.go @@ -456,10 +456,10 @@ func GetAPIType(name, pkg string) (i interface{}, t reflect.Type, permStruct []r i = &api.GatewayStruct{} t = reflect.TypeOf(new(struct{ api.Gateway })).Elem() permStruct = append(permStruct, reflect.TypeOf(api.GatewayStruct{}.Internal)) - case "Provider": - i = &api.LotusProviderStruct{} - t = reflect.TypeOf(new(struct{ api.LotusProvider })).Elem() - permStruct = append(permStruct, reflect.TypeOf(api.LotusProviderStruct{}.Internal)) + case "Curio": + i = &api.CurioStruct{} + t = reflect.TypeOf(new(struct{ api.Curio })).Elem() + permStruct = append(permStruct, reflect.TypeOf(api.CurioStruct{}.Internal)) default: panic("unknown type") } diff --git a/api/proxy_gen.go b/api/proxy_gen.go index ae48591ec..58214ae8b 100644 --- a/api/proxy_gen.go +++ b/api/proxy_gen.go @@ -115,6 +115,35 @@ type CommonNetStub struct { NetStub } +type CurioStruct struct { + Internal CurioMethods +} + +type CurioMethods struct { + AllocatePieceToSector func(p0 context.Context, p1 address.Address, p2 PieceDealInfo, p3 int64, p4 url.URL, p5 http.Header) (SectorOffset, error) `perm:"write"` + + Shutdown func(p0 context.Context) error `perm:"admin"` + + StorageAddLocal func(p0 context.Context, p1 string) error `perm:"admin"` + + StorageDetachLocal func(p0 context.Context, p1 string) error `perm:"admin"` + + StorageFindSector func(p0 context.Context, p1 abi.SectorID, p2 storiface.SectorFileType, p3 abi.SectorSize, p4 bool) ([]storiface.SectorStorageInfo, error) `perm:"admin"` + + StorageInfo func(p0 context.Context, p1 storiface.ID) (storiface.StorageInfo, error) `perm:"admin"` + + StorageList func(p0 context.Context) (map[storiface.ID][]storiface.Decl, error) `perm:"admin"` + + StorageLocal func(p0 context.Context) (map[storiface.ID]string, error) `perm:"admin"` + + StorageStat func(p0 context.Context, p1 storiface.ID) (fsutil.FsStat, error) `perm:"admin"` + + Version func(p0 context.Context) (Version, error) `perm:"admin"` +} + +type CurioStub struct { +} + type EthSubscriberStruct struct { Internal EthSubscriberMethods } @@ -849,35 +878,6 @@ type GatewayMethods struct { type GatewayStub struct { } -type LotusProviderStruct struct { - Internal LotusProviderMethods -} - -type LotusProviderMethods struct { - AllocatePieceToSector func(p0 context.Context, p1 address.Address, p2 PieceDealInfo, p3 int64, p4 url.URL, p5 http.Header) (SectorOffset, error) `perm:"write"` - - Shutdown func(p0 context.Context) error `perm:"admin"` - - StorageAddLocal func(p0 context.Context, p1 string) error `perm:"admin"` - - StorageDetachLocal func(p0 context.Context, p1 string) error `perm:"admin"` - - StorageFindSector func(p0 context.Context, p1 abi.SectorID, p2 storiface.SectorFileType, p3 abi.SectorSize, p4 bool) ([]storiface.SectorStorageInfo, error) `perm:"admin"` - - StorageInfo func(p0 context.Context, p1 storiface.ID) (storiface.StorageInfo, error) `perm:"admin"` - - StorageList func(p0 context.Context) (map[storiface.ID][]storiface.Decl, error) `perm:"admin"` - - StorageLocal func(p0 context.Context) (map[storiface.ID]string, error) `perm:"admin"` - - StorageStat func(p0 context.Context, p1 storiface.ID) (fsutil.FsStat, error) `perm:"admin"` - - Version func(p0 context.Context) (Version, error) `perm:"admin"` -} - -type LotusProviderStub struct { -} - type NetStruct struct { Internal NetMethods } @@ -1488,6 +1488,116 @@ func (s *CommonStub) Version(p0 context.Context) (APIVersion, error) { return *new(APIVersion), ErrNotSupported } +func (s *CurioStruct) AllocatePieceToSector(p0 context.Context, p1 address.Address, p2 PieceDealInfo, p3 int64, p4 url.URL, p5 http.Header) (SectorOffset, error) { + if s.Internal.AllocatePieceToSector == nil { + return *new(SectorOffset), ErrNotSupported + } + return s.Internal.AllocatePieceToSector(p0, p1, p2, p3, p4, p5) +} + +func (s *CurioStub) AllocatePieceToSector(p0 context.Context, p1 address.Address, p2 PieceDealInfo, p3 int64, p4 url.URL, p5 http.Header) (SectorOffset, error) { + return *new(SectorOffset), ErrNotSupported +} + +func (s *CurioStruct) Shutdown(p0 context.Context) error { + if s.Internal.Shutdown == nil { + return ErrNotSupported + } + return s.Internal.Shutdown(p0) +} + +func (s *CurioStub) Shutdown(p0 context.Context) error { + return ErrNotSupported +} + +func (s *CurioStruct) StorageAddLocal(p0 context.Context, p1 string) error { + if s.Internal.StorageAddLocal == nil { + return ErrNotSupported + } + return s.Internal.StorageAddLocal(p0, p1) +} + +func (s *CurioStub) StorageAddLocal(p0 context.Context, p1 string) error { + return ErrNotSupported +} + +func (s *CurioStruct) StorageDetachLocal(p0 context.Context, p1 string) error { + if s.Internal.StorageDetachLocal == nil { + return ErrNotSupported + } + return s.Internal.StorageDetachLocal(p0, p1) +} + +func (s *CurioStub) StorageDetachLocal(p0 context.Context, p1 string) error { + return ErrNotSupported +} + +func (s *CurioStruct) StorageFindSector(p0 context.Context, p1 abi.SectorID, p2 storiface.SectorFileType, p3 abi.SectorSize, p4 bool) ([]storiface.SectorStorageInfo, error) { + if s.Internal.StorageFindSector == nil { + return *new([]storiface.SectorStorageInfo), ErrNotSupported + } + return s.Internal.StorageFindSector(p0, p1, p2, p3, p4) +} + +func (s *CurioStub) StorageFindSector(p0 context.Context, p1 abi.SectorID, p2 storiface.SectorFileType, p3 abi.SectorSize, p4 bool) ([]storiface.SectorStorageInfo, error) { + return *new([]storiface.SectorStorageInfo), ErrNotSupported +} + +func (s *CurioStruct) StorageInfo(p0 context.Context, p1 storiface.ID) (storiface.StorageInfo, error) { + if s.Internal.StorageInfo == nil { + return *new(storiface.StorageInfo), ErrNotSupported + } + return s.Internal.StorageInfo(p0, p1) +} + +func (s *CurioStub) StorageInfo(p0 context.Context, p1 storiface.ID) (storiface.StorageInfo, error) { + return *new(storiface.StorageInfo), ErrNotSupported +} + +func (s *CurioStruct) StorageList(p0 context.Context) (map[storiface.ID][]storiface.Decl, error) { + if s.Internal.StorageList == nil { + return *new(map[storiface.ID][]storiface.Decl), ErrNotSupported + } + return s.Internal.StorageList(p0) +} + +func (s *CurioStub) StorageList(p0 context.Context) (map[storiface.ID][]storiface.Decl, error) { + return *new(map[storiface.ID][]storiface.Decl), ErrNotSupported +} + +func (s *CurioStruct) StorageLocal(p0 context.Context) (map[storiface.ID]string, error) { + if s.Internal.StorageLocal == nil { + return *new(map[storiface.ID]string), ErrNotSupported + } + return s.Internal.StorageLocal(p0) +} + +func (s *CurioStub) StorageLocal(p0 context.Context) (map[storiface.ID]string, error) { + return *new(map[storiface.ID]string), ErrNotSupported +} + +func (s *CurioStruct) StorageStat(p0 context.Context, p1 storiface.ID) (fsutil.FsStat, error) { + if s.Internal.StorageStat == nil { + return *new(fsutil.FsStat), ErrNotSupported + } + return s.Internal.StorageStat(p0, p1) +} + +func (s *CurioStub) StorageStat(p0 context.Context, p1 storiface.ID) (fsutil.FsStat, error) { + return *new(fsutil.FsStat), ErrNotSupported +} + +func (s *CurioStruct) Version(p0 context.Context) (Version, error) { + if s.Internal.Version == nil { + return *new(Version), ErrNotSupported + } + return s.Internal.Version(p0) +} + +func (s *CurioStub) Version(p0 context.Context) (Version, error) { + return *new(Version), ErrNotSupported +} + func (s *EthSubscriberStruct) EthSubscription(p0 context.Context, p1 jsonrpc.RawParams) error { if s.Internal.EthSubscription == nil { return ErrNotSupported @@ -5338,116 +5448,6 @@ func (s *GatewayStub) Web3ClientVersion(p0 context.Context) (string, error) { return "", ErrNotSupported } -func (s *LotusProviderStruct) AllocatePieceToSector(p0 context.Context, p1 address.Address, p2 PieceDealInfo, p3 int64, p4 url.URL, p5 http.Header) (SectorOffset, error) { - if s.Internal.AllocatePieceToSector == nil { - return *new(SectorOffset), ErrNotSupported - } - return s.Internal.AllocatePieceToSector(p0, p1, p2, p3, p4, p5) -} - -func (s *LotusProviderStub) AllocatePieceToSector(p0 context.Context, p1 address.Address, p2 PieceDealInfo, p3 int64, p4 url.URL, p5 http.Header) (SectorOffset, error) { - return *new(SectorOffset), ErrNotSupported -} - -func (s *LotusProviderStruct) Shutdown(p0 context.Context) error { - if s.Internal.Shutdown == nil { - return ErrNotSupported - } - return s.Internal.Shutdown(p0) -} - -func (s *LotusProviderStub) Shutdown(p0 context.Context) error { - return ErrNotSupported -} - -func (s *LotusProviderStruct) StorageAddLocal(p0 context.Context, p1 string) error { - if s.Internal.StorageAddLocal == nil { - return ErrNotSupported - } - return s.Internal.StorageAddLocal(p0, p1) -} - -func (s *LotusProviderStub) StorageAddLocal(p0 context.Context, p1 string) error { - return ErrNotSupported -} - -func (s *LotusProviderStruct) StorageDetachLocal(p0 context.Context, p1 string) error { - if s.Internal.StorageDetachLocal == nil { - return ErrNotSupported - } - return s.Internal.StorageDetachLocal(p0, p1) -} - -func (s *LotusProviderStub) StorageDetachLocal(p0 context.Context, p1 string) error { - return ErrNotSupported -} - -func (s *LotusProviderStruct) StorageFindSector(p0 context.Context, p1 abi.SectorID, p2 storiface.SectorFileType, p3 abi.SectorSize, p4 bool) ([]storiface.SectorStorageInfo, error) { - if s.Internal.StorageFindSector == nil { - return *new([]storiface.SectorStorageInfo), ErrNotSupported - } - return s.Internal.StorageFindSector(p0, p1, p2, p3, p4) -} - -func (s *LotusProviderStub) StorageFindSector(p0 context.Context, p1 abi.SectorID, p2 storiface.SectorFileType, p3 abi.SectorSize, p4 bool) ([]storiface.SectorStorageInfo, error) { - return *new([]storiface.SectorStorageInfo), ErrNotSupported -} - -func (s *LotusProviderStruct) StorageInfo(p0 context.Context, p1 storiface.ID) (storiface.StorageInfo, error) { - if s.Internal.StorageInfo == nil { - return *new(storiface.StorageInfo), ErrNotSupported - } - return s.Internal.StorageInfo(p0, p1) -} - -func (s *LotusProviderStub) StorageInfo(p0 context.Context, p1 storiface.ID) (storiface.StorageInfo, error) { - return *new(storiface.StorageInfo), ErrNotSupported -} - -func (s *LotusProviderStruct) StorageList(p0 context.Context) (map[storiface.ID][]storiface.Decl, error) { - if s.Internal.StorageList == nil { - return *new(map[storiface.ID][]storiface.Decl), ErrNotSupported - } - return s.Internal.StorageList(p0) -} - -func (s *LotusProviderStub) StorageList(p0 context.Context) (map[storiface.ID][]storiface.Decl, error) { - return *new(map[storiface.ID][]storiface.Decl), ErrNotSupported -} - -func (s *LotusProviderStruct) StorageLocal(p0 context.Context) (map[storiface.ID]string, error) { - if s.Internal.StorageLocal == nil { - return *new(map[storiface.ID]string), ErrNotSupported - } - return s.Internal.StorageLocal(p0) -} - -func (s *LotusProviderStub) StorageLocal(p0 context.Context) (map[storiface.ID]string, error) { - return *new(map[storiface.ID]string), ErrNotSupported -} - -func (s *LotusProviderStruct) StorageStat(p0 context.Context, p1 storiface.ID) (fsutil.FsStat, error) { - if s.Internal.StorageStat == nil { - return *new(fsutil.FsStat), ErrNotSupported - } - return s.Internal.StorageStat(p0, p1) -} - -func (s *LotusProviderStub) StorageStat(p0 context.Context, p1 storiface.ID) (fsutil.FsStat, error) { - return *new(fsutil.FsStat), ErrNotSupported -} - -func (s *LotusProviderStruct) Version(p0 context.Context) (Version, error) { - if s.Internal.Version == nil { - return *new(Version), ErrNotSupported - } - return s.Internal.Version(p0) -} - -func (s *LotusProviderStub) Version(p0 context.Context) (Version, error) { - return *new(Version), ErrNotSupported -} - func (s *NetStruct) ID(p0 context.Context) (peer.ID, error) { if s.Internal.ID == nil { return *new(peer.ID), ErrNotSupported @@ -7673,10 +7673,10 @@ func (s *WorkerStub) WaitQuiet(p0 context.Context) error { var _ ChainIO = new(ChainIOStruct) var _ Common = new(CommonStruct) var _ CommonNet = new(CommonNetStruct) +var _ Curio = new(CurioStruct) var _ EthSubscriber = new(EthSubscriberStruct) var _ FullNode = new(FullNodeStruct) var _ Gateway = new(GatewayStruct) -var _ LotusProvider = new(LotusProviderStruct) var _ Net = new(NetStruct) var _ Signable = new(SignableStruct) var _ StorageMiner = new(StorageMinerStruct) diff --git a/api/v1api/latest.go b/api/v1api/latest.go index b8eeed2de..a1e63b6ad 100644 --- a/api/v1api/latest.go +++ b/api/v1api/latest.go @@ -13,4 +13,4 @@ func PermissionedFullAPI(a FullNode) FullNode { return api.PermissionedFullAPI(a) } -type LotusProviderStruct = api.LotusProviderStruct +type CurioStruct = api.CurioStruct diff --git a/api/version.go b/api/version.go index e968bf93b..124f53dab 100644 --- a/api/version.go +++ b/api/version.go @@ -60,7 +60,7 @@ var ( MinerAPIVersion0 = newVer(1, 5, 0) WorkerAPIVersion0 = newVer(1, 7, 0) - ProviderAPIVersion0 = newVer(1, 0, 0) + CurioAPIVersion0 = newVer(1, 0, 0) ) //nolint:varcheck,deadcode diff --git a/build/openrpc/full.json b/build/openrpc/full.json index 993eb4fe4..bf949dbf5 100644 --- a/build/openrpc/full.json +++ b/build/openrpc/full.json @@ -37,7 +37,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1502" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1612" } }, { @@ -60,7 +60,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1513" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1623" } }, { @@ -103,7 +103,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1524" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1634" } }, { @@ -214,7 +214,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1546" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1656" } }, { @@ -454,7 +454,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1557" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1667" } }, { @@ -685,7 +685,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1568" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1678" } }, { @@ -784,7 +784,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1579" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1689" } }, { @@ -816,7 +816,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1590" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1700" } }, { @@ -922,7 +922,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1601" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1711" } }, { @@ -1019,7 +1019,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1612" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1722" } }, { @@ -1078,7 +1078,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1623" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1733" } }, { @@ -1171,7 +1171,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1634" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1744" } }, { @@ -1255,7 +1255,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1645" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1755" } }, { @@ -1355,7 +1355,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1656" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1766" } }, { @@ -1411,7 +1411,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1667" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1777" } }, { @@ -1484,7 +1484,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1678" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1788" } }, { @@ -1557,7 +1557,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1689" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1799" } }, { @@ -1604,7 +1604,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1700" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1810" } }, { @@ -1636,7 +1636,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1711" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1821" } }, { @@ -1691,7 +1691,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1722" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1832" } }, { @@ -1743,7 +1743,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1744" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1854" } }, { @@ -1780,7 +1780,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1755" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1865" } }, { @@ -1827,7 +1827,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1766" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1876" } }, { @@ -1874,7 +1874,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1777" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1887" } }, { @@ -1954,7 +1954,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1788" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1898" } }, { @@ -2006,7 +2006,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1799" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1909" } }, { @@ -2065,7 +2065,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1810" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1920" } }, { @@ -2136,7 +2136,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1821" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1931" } }, { @@ -2177,7 +2177,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1832" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1942" } }, { @@ -2245,7 +2245,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1854" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1964" } }, { @@ -2306,7 +2306,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1865" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1975" } }, { @@ -2413,7 +2413,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1876" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1986" } }, { @@ -2569,7 +2569,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1887" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1997" } }, { @@ -2635,7 +2635,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1898" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2008" } }, { @@ -2976,7 +2976,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1909" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2019" } }, { @@ -3021,7 +3021,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1920" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2030" } }, { @@ -3068,7 +3068,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1953" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2063" } }, { @@ -3139,7 +3139,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1964" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2074" } }, { @@ -3282,7 +3282,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1975" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2085" } }, { @@ -3612,7 +3612,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1986" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2096" } }, { @@ -3680,7 +3680,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1997" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2107" } }, { @@ -3914,7 +3914,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2008" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2118" } }, { @@ -4077,7 +4077,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2019" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2129" } }, { @@ -4160,7 +4160,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2030" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2140" } }, { @@ -4201,7 +4201,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2041" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2151" } }, { @@ -4272,7 +4272,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2052" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2162" } }, { @@ -4416,7 +4416,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2063" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2173" } }, { @@ -4456,7 +4456,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2074" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2184" } }, { @@ -4497,7 +4497,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2085" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2195" } }, { @@ -4622,7 +4622,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2096" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2206" } }, { @@ -4747,7 +4747,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2107" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2217" } }, { @@ -4786,7 +4786,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2118" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2228" } }, { @@ -4833,7 +4833,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2129" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2239" } }, { @@ -4888,7 +4888,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2140" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2250" } }, { @@ -4917,7 +4917,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2151" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2261" } }, { @@ -5054,7 +5054,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2162" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2272" } }, { @@ -5083,7 +5083,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2173" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2283" } }, { @@ -5137,7 +5137,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2184" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2294" } }, { @@ -5228,7 +5228,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2195" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2305" } }, { @@ -5256,7 +5256,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2206" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2316" } }, { @@ -5346,7 +5346,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2217" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2327" } }, { @@ -5602,7 +5602,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2228" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2338" } }, { @@ -5847,7 +5847,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2239" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2349" } }, { @@ -5903,7 +5903,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2250" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2360" } }, { @@ -5950,7 +5950,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2261" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2371" } }, { @@ -6048,7 +6048,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2272" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2382" } }, { @@ -6114,7 +6114,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2283" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2393" } }, { @@ -6180,7 +6180,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2294" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2404" } }, { @@ -6289,7 +6289,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2305" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2415" } }, { @@ -6347,7 +6347,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2316" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2426" } }, { @@ -6469,7 +6469,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2327" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2437" } }, { @@ -6673,7 +6673,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2338" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2448" } }, { @@ -6868,7 +6868,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2349" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2459" } }, { @@ -7055,7 +7055,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2360" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2470" } }, { @@ -7259,7 +7259,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2371" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2481" } }, { @@ -7350,7 +7350,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2382" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2492" } }, { @@ -7408,7 +7408,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2393" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2503" } }, { @@ -7666,7 +7666,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2404" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2514" } }, { @@ -7941,7 +7941,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2415" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2525" } }, { @@ -7969,7 +7969,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2426" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2536" } }, { @@ -8007,7 +8007,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2437" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2547" } }, { @@ -8115,7 +8115,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2448" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2558" } }, { @@ -8153,7 +8153,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2459" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2569" } }, { @@ -8182,7 +8182,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2470" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2580" } }, { @@ -8245,7 +8245,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2481" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2591" } }, { @@ -8308,7 +8308,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2492" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2602" } }, { @@ -8353,7 +8353,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2503" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2613" } }, { @@ -8475,7 +8475,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2514" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2624" } }, { @@ -8630,7 +8630,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2525" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2635" } }, { @@ -8684,7 +8684,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2536" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2646" } }, { @@ -8738,7 +8738,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2547" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2657" } }, { @@ -8793,7 +8793,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2558" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2668" } }, { @@ -8936,7 +8936,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2569" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2679" } }, { @@ -9063,7 +9063,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2580" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2690" } }, { @@ -9165,7 +9165,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2591" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2701" } }, { @@ -9388,7 +9388,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2602" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2712" } }, { @@ -9571,7 +9571,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2613" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2723" } }, { @@ -9651,7 +9651,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2624" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2734" } }, { @@ -9696,7 +9696,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2635" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2745" } }, { @@ -9752,7 +9752,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2646" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2756" } }, { @@ -9832,7 +9832,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2657" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2767" } }, { @@ -9912,7 +9912,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2668" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2778" } }, { @@ -10397,7 +10397,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2679" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2789" } }, { @@ -10591,7 +10591,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2690" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2800" } }, { @@ -10746,7 +10746,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2701" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2811" } }, { @@ -10995,7 +10995,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2712" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2822" } }, { @@ -11150,7 +11150,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2723" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2833" } }, { @@ -11327,7 +11327,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2734" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2844" } }, { @@ -11425,7 +11425,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2745" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2855" } }, { @@ -11590,7 +11590,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2756" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2866" } }, { @@ -11629,7 +11629,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2767" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2877" } }, { @@ -11694,7 +11694,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2778" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2888" } }, { @@ -11740,7 +11740,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2789" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2899" } }, { @@ -11890,7 +11890,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2800" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2910" } }, { @@ -12027,7 +12027,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2811" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2921" } }, { @@ -12258,7 +12258,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2822" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2932" } }, { @@ -12395,7 +12395,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2833" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2943" } }, { @@ -12560,7 +12560,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2844" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2954" } }, { @@ -12637,7 +12637,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2855" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2965" } }, { @@ -12832,7 +12832,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2877" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2987" } }, { @@ -13011,7 +13011,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2888" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2998" } }, { @@ -13173,7 +13173,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2899" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3009" } }, { @@ -13321,7 +13321,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2910" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3020" } }, { @@ -13549,7 +13549,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2921" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3031" } }, { @@ -13697,7 +13697,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2932" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3042" } }, { @@ -13909,7 +13909,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2943" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3053" } }, { @@ -14115,7 +14115,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2954" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3064" } }, { @@ -14183,7 +14183,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2965" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3075" } }, { @@ -14300,7 +14300,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2976" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3086" } }, { @@ -14391,7 +14391,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2987" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3097" } }, { @@ -14477,7 +14477,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2998" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3108" } }, { @@ -14672,7 +14672,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3009" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3119" } }, { @@ -14834,7 +14834,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3020" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3130" } }, { @@ -15030,7 +15030,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3031" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3141" } }, { @@ -15210,7 +15210,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3042" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3152" } }, { @@ -15373,7 +15373,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3053" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3163" } }, { @@ -15400,7 +15400,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3064" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3174" } }, { @@ -15427,7 +15427,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3075" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3185" } }, { @@ -15526,7 +15526,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3086" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3196" } }, { @@ -15572,7 +15572,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3097" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3207" } }, { @@ -15672,7 +15672,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3108" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3218" } }, { @@ -15788,7 +15788,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3119" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3229" } }, { @@ -15836,7 +15836,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3130" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3240" } }, { @@ -15928,7 +15928,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3141" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3251" } }, { @@ -16043,7 +16043,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3152" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3262" } }, { @@ -16091,7 +16091,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3163" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3273" } }, { @@ -16128,7 +16128,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3174" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3284" } }, { @@ -16400,7 +16400,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3185" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3295" } }, { @@ -16448,7 +16448,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3196" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3306" } }, { @@ -16506,7 +16506,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3207" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3317" } }, { @@ -16711,7 +16711,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3218" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3328" } }, { @@ -16914,7 +16914,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3229" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3339" } }, { @@ -17083,7 +17083,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3240" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3350" } }, { @@ -17287,7 +17287,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3251" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3361" } }, { @@ -17454,7 +17454,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3262" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3372" } }, { @@ -17661,7 +17661,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3273" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3383" } }, { @@ -17729,7 +17729,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3284" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3394" } }, { @@ -17781,7 +17781,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3295" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3405" } }, { @@ -17830,7 +17830,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3306" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3416" } }, { @@ -17921,7 +17921,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3317" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3427" } }, { @@ -18427,7 +18427,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3328" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3438" } }, { @@ -18533,7 +18533,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3339" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3449" } }, { @@ -18585,7 +18585,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3350" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3460" } }, { @@ -19137,7 +19137,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3361" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3471" } }, { @@ -19251,7 +19251,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3372" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3482" } }, { @@ -19348,7 +19348,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3383" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3493" } }, { @@ -19448,7 +19448,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3394" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3504" } }, { @@ -19536,7 +19536,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3405" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3515" } }, { @@ -19636,7 +19636,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3416" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3526" } }, { @@ -19723,7 +19723,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3427" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3537" } }, { @@ -19814,7 +19814,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3438" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3548" } }, { @@ -19939,7 +19939,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3449" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3559" } }, { @@ -20048,7 +20048,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3460" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3570" } }, { @@ -20118,7 +20118,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3471" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3581" } }, { @@ -20221,7 +20221,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3482" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3592" } }, { @@ -20282,7 +20282,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3493" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3603" } }, { @@ -20412,7 +20412,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3504" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3614" } }, { @@ -20519,7 +20519,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3515" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3625" } }, { @@ -20728,7 +20728,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3526" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3636" } }, { @@ -20805,7 +20805,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3537" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3647" } }, { @@ -20882,7 +20882,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3548" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3658" } }, { @@ -20991,7 +20991,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3559" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3669" } }, { @@ -21100,7 +21100,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3570" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3680" } }, { @@ -21161,7 +21161,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3581" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3691" } }, { @@ -21271,7 +21271,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3592" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3702" } }, { @@ -21332,7 +21332,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3603" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3713" } }, { @@ -21400,7 +21400,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3614" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3724" } }, { @@ -21468,7 +21468,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3625" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3735" } }, { @@ -21549,7 +21549,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3636" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3746" } }, { @@ -21698,7 +21698,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3647" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3757" } }, { @@ -21770,7 +21770,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3658" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3768" } }, { @@ -21929,7 +21929,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3669" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3779" } }, { @@ -22094,7 +22094,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3680" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3790" } }, { @@ -22164,7 +22164,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3691" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3801" } }, { @@ -22232,7 +22232,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3702" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3812" } }, { @@ -22325,7 +22325,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3713" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3823" } }, { @@ -22396,7 +22396,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3724" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3834" } }, { @@ -22597,7 +22597,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3735" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3845" } }, { @@ -22729,7 +22729,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3746" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3856" } }, { @@ -22866,7 +22866,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3757" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3867" } }, { @@ -22977,7 +22977,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3768" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3878" } }, { @@ -23109,7 +23109,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3779" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3889" } }, { @@ -23240,7 +23240,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3790" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3900" } }, { @@ -23311,7 +23311,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3801" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3911" } }, { @@ -23395,7 +23395,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3812" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3922" } }, { @@ -23481,7 +23481,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3823" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3933" } }, { @@ -23664,7 +23664,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3834" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3944" } }, { @@ -23691,7 +23691,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3845" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3955" } }, { @@ -23744,7 +23744,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3856" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3966" } }, { @@ -23832,7 +23832,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3867" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3977" } }, { @@ -24283,7 +24283,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3878" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3988" } }, { @@ -24450,7 +24450,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3889" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3999" } }, { @@ -24548,7 +24548,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3900" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4010" } }, { @@ -24721,7 +24721,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3911" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4021" } }, { @@ -24819,7 +24819,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3922" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4032" } }, { @@ -24970,7 +24970,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3933" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4043" } }, { @@ -25055,7 +25055,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3944" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4054" } }, { @@ -25123,7 +25123,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3955" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4065" } }, { @@ -25175,7 +25175,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3966" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4076" } }, { @@ -25243,7 +25243,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3977" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4087" } }, { @@ -25404,7 +25404,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3988" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4098" } }, { @@ -25451,7 +25451,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4010" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4120" } }, { @@ -25498,7 +25498,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4021" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4131" } }, { @@ -25541,7 +25541,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4043" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4153" } }, { @@ -25637,7 +25637,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4054" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4164" } }, { @@ -25903,7 +25903,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4065" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4175" } }, { @@ -25926,7 +25926,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4076" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4186" } }, { @@ -25969,7 +25969,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4087" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4197" } }, { @@ -26020,7 +26020,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4098" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4208" } }, { @@ -26065,7 +26065,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4109" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4219" } }, { @@ -26093,7 +26093,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4120" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4230" } }, { @@ -26133,7 +26133,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4131" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4241" } }, { @@ -26192,7 +26192,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4142" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4252" } }, { @@ -26236,7 +26236,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4153" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4263" } }, { @@ -26295,7 +26295,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4164" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4274" } }, { @@ -26332,7 +26332,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4175" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4285" } }, { @@ -26376,7 +26376,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4186" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4296" } }, { @@ -26416,7 +26416,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4197" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4307" } }, { @@ -26491,7 +26491,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4208" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4318" } }, { @@ -26699,7 +26699,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4219" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4329" } }, { @@ -26743,7 +26743,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4230" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4340" } }, { @@ -26833,7 +26833,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4241" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4351" } }, { @@ -26860,7 +26860,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4252" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4362" } } ] diff --git a/build/openrpc/gateway.json b/build/openrpc/gateway.json index 94e2dcd1c..b2ffc2ab5 100644 --- a/build/openrpc/gateway.json +++ b/build/openrpc/gateway.json @@ -242,7 +242,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4263" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4373" } }, { @@ -473,7 +473,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4274" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4384" } }, { @@ -572,7 +572,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4285" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4395" } }, { @@ -604,7 +604,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4296" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4406" } }, { @@ -710,7 +710,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4307" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4417" } }, { @@ -803,7 +803,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4318" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4428" } }, { @@ -887,7 +887,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4329" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4439" } }, { @@ -987,7 +987,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4340" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4450" } }, { @@ -1043,7 +1043,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4351" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4461" } }, { @@ -1116,7 +1116,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4362" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4472" } }, { @@ -1189,7 +1189,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4373" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4483" } }, { @@ -1236,7 +1236,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4384" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4494" } }, { @@ -1268,7 +1268,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4395" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4505" } }, { @@ -1305,7 +1305,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4417" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4527" } }, { @@ -1352,7 +1352,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4428" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4538" } }, { @@ -1392,7 +1392,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4439" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4549" } }, { @@ -1439,7 +1439,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4450" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4560" } }, { @@ -1468,7 +1468,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4461" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4571" } }, { @@ -1605,7 +1605,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4472" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4582" } }, { @@ -1634,7 +1634,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4483" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4593" } }, { @@ -1688,7 +1688,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4494" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4604" } }, { @@ -1779,7 +1779,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4505" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4615" } }, { @@ -1807,7 +1807,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4516" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4626" } }, { @@ -1897,7 +1897,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4527" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4637" } }, { @@ -2153,7 +2153,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4538" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4648" } }, { @@ -2398,7 +2398,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4549" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4659" } }, { @@ -2454,7 +2454,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4560" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4670" } }, { @@ -2501,7 +2501,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4571" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4681" } }, { @@ -2599,7 +2599,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4582" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4692" } }, { @@ -2665,7 +2665,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4593" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4703" } }, { @@ -2731,7 +2731,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4604" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4714" } }, { @@ -2840,7 +2840,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4615" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4725" } }, { @@ -2898,7 +2898,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4626" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4736" } }, { @@ -3020,7 +3020,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4637" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4747" } }, { @@ -3207,7 +3207,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4648" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4758" } }, { @@ -3411,7 +3411,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4659" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4769" } }, { @@ -3502,7 +3502,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4670" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4780" } }, { @@ -3560,7 +3560,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4681" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4791" } }, { @@ -3818,7 +3818,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4692" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4802" } }, { @@ -4093,7 +4093,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4703" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4813" } }, { @@ -4121,7 +4121,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4714" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4824" } }, { @@ -4159,7 +4159,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4725" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4835" } }, { @@ -4267,7 +4267,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4736" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4846" } }, { @@ -4305,7 +4305,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4747" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4857" } }, { @@ -4334,7 +4334,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4758" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4868" } }, { @@ -4397,7 +4397,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4769" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4879" } }, { @@ -4460,7 +4460,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4780" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4890" } }, { @@ -4505,7 +4505,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4791" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4901" } }, { @@ -4627,7 +4627,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4802" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4912" } }, { @@ -4782,7 +4782,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4813" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4923" } }, { @@ -4836,7 +4836,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4824" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4934" } }, { @@ -4890,7 +4890,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4835" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4945" } }, { @@ -4992,7 +4992,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4846" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4956" } }, { @@ -5215,7 +5215,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4857" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4967" } }, { @@ -5398,7 +5398,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4868" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4978" } }, { @@ -5592,7 +5592,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4879" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4989" } }, { @@ -5638,7 +5638,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4890" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5000" } }, { @@ -5788,7 +5788,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4901" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5011" } }, { @@ -5925,7 +5925,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4912" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5022" } }, { @@ -5993,7 +5993,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4923" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5033" } }, { @@ -6110,7 +6110,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4934" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5044" } }, { @@ -6201,7 +6201,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4945" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5055" } }, { @@ -6287,7 +6287,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4956" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5066" } }, { @@ -6314,7 +6314,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4967" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5077" } }, { @@ -6341,7 +6341,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4978" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5088" } }, { @@ -6409,7 +6409,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4989" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5099" } }, { @@ -6915,7 +6915,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5000" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5110" } }, { @@ -7012,7 +7012,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5011" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5121" } }, { @@ -7112,7 +7112,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5022" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5132" } }, { @@ -7212,7 +7212,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5033" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5143" } }, { @@ -7337,7 +7337,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5044" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5154" } }, { @@ -7446,7 +7446,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5055" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5165" } }, { @@ -7549,7 +7549,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5066" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5176" } }, { @@ -7679,7 +7679,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5077" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5187" } }, { @@ -7786,7 +7786,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5088" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5198" } }, { @@ -7847,7 +7847,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5099" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5209" } }, { @@ -7915,7 +7915,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5110" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5220" } }, { @@ -7996,7 +7996,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5121" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5231" } }, { @@ -8155,7 +8155,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5132" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5242" } }, { @@ -8248,7 +8248,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5143" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5253" } }, { @@ -8449,7 +8449,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5154" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5264" } }, { @@ -8560,7 +8560,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5165" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5275" } }, { @@ -8691,7 +8691,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5176" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5286" } }, { @@ -8777,7 +8777,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5187" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5297" } }, { @@ -8804,7 +8804,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5198" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5308" } }, { @@ -8857,7 +8857,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5209" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5319" } }, { @@ -8945,7 +8945,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5220" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5330" } }, { @@ -9396,7 +9396,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5231" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5341" } }, { @@ -9563,7 +9563,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5242" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5352" } }, { @@ -9736,7 +9736,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5253" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5363" } }, { @@ -9804,7 +9804,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5264" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5374" } }, { @@ -9872,7 +9872,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5275" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5385" } }, { @@ -10033,7 +10033,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5286" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5396" } }, { @@ -10078,7 +10078,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5308" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5418" } }, { @@ -10123,7 +10123,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5319" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5429" } }, { @@ -10150,7 +10150,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5330" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5440" } } ] diff --git a/cli/util/api.go b/cli/util/api.go index fe1ac1536..7940f67c6 100644 --- a/cli/util/api.go +++ b/cli/util/api.go @@ -76,42 +76,8 @@ func GetAPIInfoMulti(ctx *cli.Context, t repo.RepoType) ([]APIInfo, error) { if path == "" { continue } - - p, err := homedir.Expand(path) - if err != nil { - return []APIInfo{}, xerrors.Errorf("could not expand home dir (%s): %w", f, err) - } - - r, err := repo.NewFS(p) - if err != nil { - return []APIInfo{}, xerrors.Errorf("could not open repo at path: %s; %w", p, err) - } - - exists, err := r.Exists() - if err != nil { - return []APIInfo{}, xerrors.Errorf("repo.Exists returned an error: %w", err) - } - - if !exists { - return []APIInfo{}, errors.New("repo directory does not exist. Make sure your configuration is correct") - } - - ma, err := r.APIEndpoint() - if err != nil { - return []APIInfo{}, xerrors.Errorf("could not get api endpoint: %w", err) - } - - token, err := r.APIToken() - if err != nil { - log.Warnf("Couldn't load CLI token, capabilities may be limited: %v", err) - } - - return []APIInfo{{ - Addr: ma.String(), - Token: token, - }}, nil + return GetAPIInfoFromRepoPath(path, t) } - for _, env := range fallbacksEnvs { env, ok := os.LookupEnv(env) if ok { @@ -122,6 +88,42 @@ func GetAPIInfoMulti(ctx *cli.Context, t repo.RepoType) ([]APIInfo, error) { return []APIInfo{}, fmt.Errorf("could not determine API endpoint for node type: %v. Try setting environment variable: %s", t.Type(), primaryEnv) } +func GetAPIInfoFromRepoPath(path string, t repo.RepoType) ([]APIInfo, error) { + p, err := homedir.Expand(path) + if err != nil { + return []APIInfo{}, xerrors.Errorf("could not expand home dir (%s): %w", path, err) + } + + r, err := repo.NewFS(p) + if err != nil { + return []APIInfo{}, xerrors.Errorf("could not open repo at path: %s; %w", p, err) + } + + exists, err := r.Exists() + if err != nil { + return []APIInfo{}, xerrors.Errorf("repo.Exists returned an error: %w", err) + } + + if !exists { + return []APIInfo{}, errors.New("repo directory does not exist. Make sure your configuration is correct") + } + + ma, err := r.APIEndpoint() + if err != nil { + return []APIInfo{}, xerrors.Errorf("could not get api endpoint: %w", err) + } + + token, err := r.APIToken() + if err != nil { + log.Warnf("Couldn't load CLI token, capabilities may be limited: %v", err) + } + + return []APIInfo{{ + Addr: ma.String(), + Token: token, + }}, nil +} + func GetAPIInfo(ctx *cli.Context, t repo.RepoType) (APIInfo, error) { ainfos, err := GetAPIInfoMulti(ctx, t) if err != nil || len(ainfos) == 0 { @@ -164,28 +166,6 @@ func GetRawAPIMulti(ctx *cli.Context, t repo.RepoType, version string) ([]HttpHe return httpHeads, nil } -func GetRawAPIMultiV2(ctx *cli.Context, ainfoCfg []string, version string) ([]HttpHead, error) { - var httpHeads []HttpHead - - if len(ainfoCfg) == 0 { - return httpHeads, xerrors.Errorf("could not get API info: none configured. \nConsider getting base.toml with './lotus-provider config get base >/tmp/base.toml' \nthen adding \n[APIs] \n ChainApiInfo = [\" result_from lotus auth api-info --perm=admin \"]\n and updating it with './lotus-provider config set /tmp/base.toml'") - } - for _, i := range ainfoCfg { - ainfo := ParseApiInfo(i) - addr, err := ainfo.DialArgs(version) - if err != nil { - return httpHeads, xerrors.Errorf("could not get DialArgs: %w", err) - } - httpHeads = append(httpHeads, HttpHead{addr: addr, header: ainfo.AuthHeader()}) - } - - if IsVeryVerbose { - _, _ = fmt.Fprintf(ctx.App.Writer, "using raw API %s endpoint: %s\n", version, httpHeads[0].addr) - } - - return httpHeads, nil -} - func GetRawAPI(ctx *cli.Context, t repo.RepoType, version string) (string, http.Header, error) { heads, err := GetRawAPIMulti(ctx, t, version) if err != nil { @@ -342,14 +322,14 @@ func GetFullNodeAPIV1Single(ctx *cli.Context) (v1api.FullNode, jsonrpc.ClientClo } type GetFullNodeOptions struct { - ethSubHandler api.EthSubscriber + EthSubHandler api.EthSubscriber } type GetFullNodeOption func(*GetFullNodeOptions) func FullNodeWithEthSubscribtionHandler(sh api.EthSubscriber) GetFullNodeOption { return func(opts *GetFullNodeOptions) { - opts.ethSubHandler = sh + opts.EthSubHandler = sh } } @@ -364,8 +344,8 @@ func GetFullNodeAPIV1(ctx *cli.Context, opts ...GetFullNodeOption) (v1api.FullNo } var rpcOpts []jsonrpc.Option - if options.ethSubHandler != nil { - rpcOpts = append(rpcOpts, jsonrpc.WithClientHandler("Filecoin", options.ethSubHandler), jsonrpc.WithClientHandlerAlias("eth_subscription", "Filecoin.EthSubscription")) + if options.EthSubHandler != nil { + rpcOpts = append(rpcOpts, jsonrpc.WithClientHandler("Filecoin", options.EthSubHandler), jsonrpc.WithClientHandlerAlias("eth_subscription", "Filecoin.EthSubscription")) } heads, err := GetRawAPIMulti(ctx, repo.FullNode, "v1") @@ -415,68 +395,6 @@ func GetFullNodeAPIV1(ctx *cli.Context, opts ...GetFullNodeOption) (v1api.FullNo return &v1API, finalCloser, nil } -func GetFullNodeAPIV1LotusProvider(ctx *cli.Context, ainfoCfg []string, opts ...GetFullNodeOption) (v1api.FullNode, jsonrpc.ClientCloser, error) { - if tn, ok := ctx.App.Metadata["testnode-full"]; ok { - return tn.(v1api.FullNode), func() {}, nil - } - - var options GetFullNodeOptions - for _, opt := range opts { - opt(&options) - } - - var rpcOpts []jsonrpc.Option - if options.ethSubHandler != nil { - rpcOpts = append(rpcOpts, jsonrpc.WithClientHandler("Filecoin", options.ethSubHandler), jsonrpc.WithClientHandlerAlias("eth_subscription", "Filecoin.EthSubscription")) - } - - heads, err := GetRawAPIMultiV2(ctx, ainfoCfg, "v1") - if err != nil { - return nil, nil, err - } - - if IsVeryVerbose { - _, _ = fmt.Fprintln(ctx.App.Writer, "using full node API v1 endpoint:", heads[0].addr) - } - - var fullNodes []api.FullNode - var closers []jsonrpc.ClientCloser - - for _, head := range heads { - v1api, closer, err := client.NewFullNodeRPCV1(ctx.Context, head.addr, head.header, rpcOpts...) - if err != nil { - log.Warnf("Not able to establish connection to node with addr: %s, Reason: %s", head.addr, err.Error()) - continue - } - fullNodes = append(fullNodes, v1api) - closers = append(closers, closer) - } - - // When running in cluster mode and trying to establish connections to multiple nodes, fail - // if less than 2 lotus nodes are actually running - if len(heads) > 1 && len(fullNodes) < 2 { - return nil, nil, xerrors.Errorf("Not able to establish connection to more than a single node") - } - - finalCloser := func() { - for _, c := range closers { - c() - } - } - - var v1API api.FullNodeStruct - FullNodeProxy(fullNodes, &v1API) - - v, err := v1API.Version(ctx.Context) - if err != nil { - return nil, nil, err - } - if !v.APIVersion.EqMajorMinor(api.FullAPIVersion1) { - return nil, nil, xerrors.Errorf("Remote API version didn't match (expected %s, remote %s)", api.FullAPIVersion1, v.APIVersion) - } - return &v1API, finalCloser, nil -} - type GetStorageMinerOptions struct { PreferHttp bool } diff --git a/cmd/lotus-provider/cli.go b/cmd/curio/cli.go similarity index 96% rename from cmd/lotus-provider/cli.go rename to cmd/curio/cli.go index dc1bdde16..ea3c13d72 100644 --- a/cmd/lotus-provider/cli.go +++ b/cmd/curio/cli.go @@ -18,8 +18,8 @@ import ( "github.com/filecoin-project/lotus/api" lcli "github.com/filecoin-project/lotus/cli" - "github.com/filecoin-project/lotus/cmd/lotus-provider/deps" - "github.com/filecoin-project/lotus/cmd/lotus-provider/rpc" + "github.com/filecoin-project/lotus/cmd/curio/deps" + "github.com/filecoin-project/lotus/cmd/curio/rpc" ) const providerEnvVar = "PROVIDER_API_INFO" @@ -177,7 +177,7 @@ var cliCmd = &cli.Command{ } { - api, closer, err := rpc.GetProviderAPI(cctx) + api, closer, err := rpc.GetCurioAPI(cctx) if err != nil { return err } diff --git a/cmd/lotus-provider/config.go b/cmd/curio/config.go similarity index 92% rename from cmd/lotus-provider/config.go rename to cmd/curio/config.go index 1f155f543..20f2d0597 100644 --- a/cmd/lotus-provider/config.go +++ b/cmd/curio/config.go @@ -15,7 +15,7 @@ import ( "github.com/urfave/cli/v2" "golang.org/x/xerrors" - "github.com/filecoin-project/lotus/cmd/lotus-provider/deps" + "github.com/filecoin-project/lotus/cmd/curio/deps" "github.com/filecoin-project/lotus/lib/harmony/harmonydb" "github.com/filecoin-project/lotus/node/config" ) @@ -48,7 +48,7 @@ var configDefaultCmd = &cli.Command{ }, Action: func(cctx *cli.Context) error { comment := !cctx.Bool("no-comment") - cfg, err := getDefaultConfig(comment) + cfg, err := deps.GetDefaultConfig(comment) if err != nil { return err } @@ -58,15 +58,6 @@ var configDefaultCmd = &cli.Command{ }, } -func getDefaultConfig(comment bool) (string, error) { - c := config.DefaultLotusProvider() - cb, err := config.ConfigUpdate(c, nil, config.Commented(comment), config.DefaultKeepUncommented(), config.NoEnv()) - if err != nil { - return "", err - } - return string(cb), nil -} - var configSetCmd = &cli.Command{ Name: "set", Aliases: []string{"add", "update", "create"}, @@ -106,12 +97,12 @@ var configSetCmd = &cli.Command{ return fmt.Errorf("cannot read stream/file %w", err) } - lp := config.DefaultLotusProvider() // ensure it's toml - _, err = deps.LoadConfigWithUpgrades(string(bytes), lp) + curioConfig := config.DefaultCurioConfig() // ensure it's toml + _, err = deps.LoadConfigWithUpgrades(string(bytes), curioConfig) if err != nil { return fmt.Errorf("cannot decode file: %w", err) } - _ = lp + _ = curioConfig err = setConfig(db, name, string(bytes)) @@ -216,7 +207,7 @@ var configRmCmd = &cli.Command{ var configViewCmd = &cli.Command{ Name: "interpret", Aliases: []string{"view", "stacked", "stack"}, - Usage: "Interpret stacked config layers by this version of lotus-provider, with system-generated comments.", + Usage: "Interpret stacked config layers by this version of curio, with system-generated comments.", ArgsUsage: "a list of layers to be interpreted as the final config", Flags: []cli.Flag{ &cli.StringSliceFlag{ @@ -230,11 +221,11 @@ var configViewCmd = &cli.Command{ if err != nil { return err } - lp, err := deps.GetConfig(cctx, db) + curioConfig, err := deps.GetConfig(cctx, db) if err != nil { return err } - cb, err := config.ConfigUpdate(lp, config.DefaultLotusProvider(), config.Commented(true), config.DefaultKeepUncommented(), config.NoEnv()) + cb, err := config.ConfigUpdate(curioConfig, config.DefaultCurioConfig(), config.Commented(true), config.DefaultKeepUncommented(), config.NoEnv()) if err != nil { return xerrors.Errorf("cannot interpret config: %w", err) } @@ -299,12 +290,12 @@ var configEditCmd = &cli.Command{ } if cctx.IsSet("source") && source != layer && !cctx.Bool("no-interpret-source") { - lp := config.DefaultLotusProvider() + lp := config.DefaultCurioConfig() if _, err := toml.Decode(sourceConfig, lp); err != nil { return xerrors.Errorf("parsing source config: %w", err) } - cb, err := config.ConfigUpdate(lp, config.DefaultLotusProvider(), config.Commented(true), config.DefaultKeepUncommented(), config.NoEnv()) + cb, err := config.ConfigUpdate(lp, config.DefaultCurioConfig(), config.Commented(true), config.DefaultKeepUncommented(), config.NoEnv()) if err != nil { return xerrors.Errorf("interpreting source config: %w", err) } @@ -362,9 +353,18 @@ var configEditCmd = &cli.Command{ }, } +func getDefaultConfig(comment bool) (string, error) { + c := config.DefaultCurioConfig() + cb, err := config.ConfigUpdate(c, nil, config.Commented(comment), config.DefaultKeepUncommented(), config.NoEnv()) + if err != nil { + return "", err + } + return string(cb), nil +} + func diff(sourceConf, newConf string) (string, error) { - lpSrc := config.DefaultLotusProvider() - lpNew := config.DefaultLotusProvider() + lpSrc := config.DefaultCurioConfig() + lpNew := config.DefaultCurioConfig() _, err := toml.Decode(sourceConf, lpSrc) if err != nil { diff --git a/cmd/lotus-provider/config_new.go b/cmd/curio/config_new.go similarity index 95% rename from cmd/lotus-provider/config_new.go rename to cmd/curio/config_new.go index e9d8382ac..38ca44597 100644 --- a/cmd/lotus-provider/config_new.go +++ b/cmd/curio/config_new.go @@ -18,7 +18,7 @@ import ( "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/chain/types" cliutil "github.com/filecoin-project/lotus/cli/util" - "github.com/filecoin-project/lotus/cmd/lotus-provider/deps" + "github.com/filecoin-project/lotus/cmd/curio/deps" "github.com/filecoin-project/lotus/node/config" "github.com/filecoin-project/lotus/node/repo" ) @@ -73,7 +73,7 @@ var configNewCmd = &cli.Command{ msg := "Layer " + configColor(name) + ` created. ` // setup config - lpCfg := config.DefaultLotusProvider() + lpCfg := config.DefaultCurioConfig() for _, addr := range cctx.Args().Slice() { maddr, err := address.NewFromString(addr) @@ -86,7 +86,7 @@ var configNewCmd = &cli.Command{ return xerrors.Errorf("Failed to get miner info: %w", err) } - lpCfg.Addresses = append(lpCfg.Addresses, config.LotusProviderAddresses{ + lpCfg.Addresses = append(lpCfg.Addresses, config.CurioAddresses{ PreCommitControl: nil, CommitControl: nil, TerminateControl: nil, diff --git a/cmd/curio/deps/apiinfo.go b/cmd/curio/deps/apiinfo.go new file mode 100644 index 000000000..0dd96d817 --- /dev/null +++ b/cmd/curio/deps/apiinfo.go @@ -0,0 +1,94 @@ +package deps + +import ( + "fmt" + "net/http" + + "github.com/urfave/cli/v2" + "golang.org/x/xerrors" + + "github.com/filecoin-project/go-jsonrpc" + + "github.com/filecoin-project/lotus/api" + "github.com/filecoin-project/lotus/api/client" + "github.com/filecoin-project/lotus/api/v1api" + cliutil "github.com/filecoin-project/lotus/cli/util" +) + +func getFullNodeAPIV1Curio(ctx *cli.Context, ainfoCfg []string, opts ...cliutil.GetFullNodeOption) (v1api.FullNode, jsonrpc.ClientCloser, error) { + if tn, ok := ctx.App.Metadata["testnode-full"]; ok { + return tn.(v1api.FullNode), func() {}, nil + } + + var options cliutil.GetFullNodeOptions + for _, opt := range opts { + opt(&options) + } + + var rpcOpts []jsonrpc.Option + if options.EthSubHandler != nil { + rpcOpts = append(rpcOpts, jsonrpc.WithClientHandler("Filecoin", options.EthSubHandler), jsonrpc.WithClientHandlerAlias("eth_subscription", "Filecoin.EthSubscription")) + } + + var httpHeads []httpHead + version := "v1" + { + if len(ainfoCfg) == 0 { + return nil, nil, xerrors.Errorf("could not get API info: none configured. \nConsider getting base.toml with './curio config get base >/tmp/base.toml' \nthen adding \n[APIs] \n ChainApiInfo = [\" result_from lotus auth api-info --perm=admin \"]\n and updating it with './curio config set /tmp/base.toml'") + } + for _, i := range ainfoCfg { + ainfo := cliutil.ParseApiInfo(i) + addr, err := ainfo.DialArgs(version) + if err != nil { + return nil, nil, xerrors.Errorf("could not get DialArgs: %w", err) + } + httpHeads = append(httpHeads, httpHead{addr: addr, header: ainfo.AuthHeader()}) + } + } + + if cliutil.IsVeryVerbose { + _, _ = fmt.Fprintln(ctx.App.Writer, "using full node API v1 endpoint:", httpHeads[0].addr) + } + + var fullNodes []api.FullNode + var closers []jsonrpc.ClientCloser + + for _, head := range httpHeads { + v1api, closer, err := client.NewFullNodeRPCV1(ctx.Context, head.addr, head.header, rpcOpts...) + if err != nil { + log.Warnf("Not able to establish connection to node with addr: %s, Reason: %s", head.addr, err.Error()) + continue + } + fullNodes = append(fullNodes, v1api) + closers = append(closers, closer) + } + + // When running in cluster mode and trying to establish connections to multiple nodes, fail + // if less than 2 lotus nodes are actually running + if len(httpHeads) > 1 && len(fullNodes) < 2 { + return nil, nil, xerrors.Errorf("Not able to establish connection to more than a single node") + } + + finalCloser := func() { + for _, c := range closers { + c() + } + } + + var v1API api.FullNodeStruct + cliutil.FullNodeProxy(fullNodes, &v1API) + + v, err := v1API.Version(ctx.Context) + if err != nil { + return nil, nil, err + } + if !v.APIVersion.EqMajorMinor(api.FullAPIVersion1) { + return nil, nil, xerrors.Errorf("Remote API version didn't match (expected %s, remote %s)", api.FullAPIVersion1, v.APIVersion) + } + return &v1API, finalCloser, nil +} + +type httpHead struct { + addr string + header http.Header +} diff --git a/cmd/lotus-provider/deps/deps.go b/cmd/curio/deps/deps.go similarity index 63% rename from cmd/lotus-provider/deps/deps.go rename to cmd/curio/deps/deps.go index e03e7dd24..4ce660739 100644 --- a/cmd/lotus-provider/deps/deps.go +++ b/cmd/curio/deps/deps.go @@ -1,4 +1,4 @@ -// Package deps provides the dependencies for the lotus provider node. +// Package deps provides the dependencies for the curio node. package deps import ( @@ -9,8 +9,9 @@ import ( "fmt" "net" "net/http" + "net/url" "os" - "regexp" + "path/filepath" "strings" "github.com/BurntSushi/toml" @@ -18,6 +19,7 @@ import ( ds "github.com/ipfs/go-datastore" dssync "github.com/ipfs/go-datastore/sync" logging "github.com/ipfs/go-log/v2" + "github.com/samber/lo" "github.com/urfave/cli/v2" "golang.org/x/xerrors" @@ -27,7 +29,8 @@ import ( "github.com/filecoin-project/go-statestore" "github.com/filecoin-project/lotus/api" - cliutil "github.com/filecoin-project/lotus/cli/util" + curio "github.com/filecoin-project/lotus/curiosrc" + "github.com/filecoin-project/lotus/curiosrc/multictladdr" "github.com/filecoin-project/lotus/journal" "github.com/filecoin-project/lotus/journal/alerting" "github.com/filecoin-project/lotus/journal/fsjournal" @@ -36,25 +39,90 @@ import ( "github.com/filecoin-project/lotus/node/modules" "github.com/filecoin-project/lotus/node/modules/dtypes" "github.com/filecoin-project/lotus/node/repo" - "github.com/filecoin-project/lotus/provider" - "github.com/filecoin-project/lotus/provider/multictladdr" "github.com/filecoin-project/lotus/storage/paths" "github.com/filecoin-project/lotus/storage/sealer" "github.com/filecoin-project/lotus/storage/sealer/ffiwrapper" "github.com/filecoin-project/lotus/storage/sealer/storiface" ) -var log = logging.Logger("lotus-provider/deps") +var log = logging.Logger("curio/deps") func MakeDB(cctx *cli.Context) (*harmonydb.DB, error) { - dbConfig := config.HarmonyDB{ - Username: cctx.String("db-user"), - Password: cctx.String("db-password"), - Hosts: strings.Split(cctx.String("db-host"), ","), - Database: cctx.String("db-name"), - Port: cctx.String("db-port"), + // #1 CLI opts + fromCLI := func() (*harmonydb.DB, error) { + dbConfig := config.HarmonyDB{ + Username: cctx.String("db-user"), + Password: cctx.String("db-password"), + Hosts: strings.Split(cctx.String("db-host"), ","), + Database: cctx.String("db-name"), + Port: cctx.String("db-port"), + } + return harmonydb.NewFromConfig(dbConfig) } - return harmonydb.NewFromConfig(dbConfig) + + readToml := func(path string) (*harmonydb.DB, error) { + cfg, err := config.FromFile(path) + if err != nil { + return nil, err + } + if c, ok := cfg.(*config.StorageMiner); ok { + return harmonydb.NewFromConfig(c.HarmonyDB) + } + return nil, errors.New("not a miner config") + } + + // #2 Try local miner config + fromMinerEnv := func() (*harmonydb.DB, error) { + v := os.Getenv("LOTUS_MINER_PATH") + if v == "" { + return nil, errors.New("no miner env") + } + return readToml(filepath.Join(v, "config.toml")) + + } + + fromMiner := func() (*harmonydb.DB, error) { + u, err := os.UserHomeDir() + if err != nil { + return nil, err + } + return readToml(filepath.Join(u, ".lotusminer/config.toml")) + } + fromEnv := func() (*harmonydb.DB, error) { + // #3 Try env + u, err := url.Parse(os.Getenv("CURIO_DB")) + if err != nil { + return nil, errors.New("no db connection string found in CURIO_DB env") + } + cfg := config.DefaultStorageMiner().HarmonyDB + if u.User.Username() != "" { + cfg.Username = u.User.Username() + } + if p, ok := u.User.Password(); ok && p != "" { + cfg.Password = p + } + if u.Hostname() != "" { + cfg.Hosts = []string{u.Hostname()} + } + if u.Port() != "" { + cfg.Port = u.Port() + } + if strings.TrimPrefix(u.Path, "/") != "" { + cfg.Database = strings.TrimPrefix(u.Path, "/") + } + + return harmonydb.NewFromConfig(cfg) + } + + for _, f := range []func() (*harmonydb.DB, error){fromCLI, fromMinerEnv, fromMiner, fromEnv} { + db, err := f() + if err != nil { + continue + } + return db, nil + } + log.Error("No db connection string found. User CLI args or env var: set CURIO_DB=postgres://USER:PASSWORD@HOST:PORT/DATABASE") + return fromCLI() //in-case it's not about bad config. } type JwtPayload struct { @@ -93,8 +161,8 @@ func GetDeps(ctx context.Context, cctx *cli.Context) (*Deps, error) { } type Deps struct { - Cfg *config.LotusProviderConfig - DB *harmonydb.DB + Cfg *config.CurioConfig // values + DB *harmonydb.DB // has itest capability Full api.FullNode Verif storiface.Verifier LW *sealer.LocalWorker @@ -128,7 +196,7 @@ func (deps *Deps) PopulateRemainingDeps(ctx context.Context, cctx *cli.Context, return err } if !ok { - if err := r.Init(repo.Provider); err != nil { + if err := r.Init(repo.Curio); err != nil { return err } } @@ -156,7 +224,7 @@ func (deps *Deps) PopulateRemainingDeps(ctx context.Context, cctx *cli.Context, } if deps.As == nil { - deps.As, err = provider.AddressSelector(deps.Cfg.Addresses)() + deps.As, err = curio.AddressSelector(deps.Cfg.Addresses)() if err != nil { return err } @@ -186,7 +254,7 @@ func (deps *Deps) PopulateRemainingDeps(ctx context.Context, cctx *cli.Context, if v := os.Getenv("FULLNODE_API_INFO"); v != "" { cfgApiInfo = []string{v} } - deps.Full, fullCloser, err = cliutil.GetFullNodeAPIV1LotusProvider(cctx, cfgApiInfo) + deps.Full, fullCloser, err = getFullNodeAPIV1Curio(cctx, cfgApiInfo) if err != nil { return err } @@ -236,7 +304,7 @@ Get it with: jq .PrivateKey ~/.lotus-miner/keystore/MF2XI2BNNJ3XILLQOJUXMYLUMU`, wstates := statestore.New(dssync.MutexWrap(ds.NewMapDatastore())) // todo localWorker isn't the abstraction layer we want to use here, we probably want to go straight to ffiwrapper - // maybe with a lotus-provider specific abstraction. LocalWorker does persistent call tracking which we probably + // maybe with a curio specific abstraction. LocalWorker does persistent call tracking which we probably // don't need (ehh.. maybe we do, the async callback system may actually work decently well with harmonytask) deps.LW = sealer.NewLocalWorker(sealer.WorkerConfig{ MaxParallelChallengeReads: deps.Cfg.Proving.ParallelCheckLimit, @@ -273,21 +341,30 @@ Get it with: jq .PrivateKey ~/.lotus-miner/keystore/MF2XI2BNNJ3XILLQOJUXMYLUMU`, return nil } -var oldAddresses = regexp.MustCompile("(?i)^\\[addresses\\]$") - -func LoadConfigWithUpgrades(text string, lp *config.LotusProviderConfig) (toml.MetaData, error) { +func LoadConfigWithUpgrades(text string, curioConfigWithDefaults *config.CurioConfig) (toml.MetaData, error) { // allow migration from old config format that was limited to 1 wallet setup. - newText := oldAddresses.ReplaceAllString(text, "[[addresses]]") - - if text != newText { - log.Warnw("Upgraded config!", "old", text, "new", newText) + newText := strings.Join(lo.Map(strings.Split(text, "\n"), func(line string, _ int) string { + if strings.EqualFold(line, "[addresses]") { + return "[[addresses]]" + } + return line + }), "\n") + meta, err := toml.Decode(newText, &curioConfigWithDefaults) + for i := range curioConfigWithDefaults.Addresses { + if curioConfigWithDefaults.Addresses[i].PreCommitControl == nil { + curioConfigWithDefaults.Addresses[i].PreCommitControl = []string{} + } + if curioConfigWithDefaults.Addresses[i].CommitControl == nil { + curioConfigWithDefaults.Addresses[i].CommitControl = []string{} + } + if curioConfigWithDefaults.Addresses[i].TerminateControl == nil { + curioConfigWithDefaults.Addresses[i].TerminateControl = []string{} + } } - - meta, err := toml.Decode(newText, &lp) return meta, err } -func GetConfig(cctx *cli.Context, db *harmonydb.DB) (*config.LotusProviderConfig, error) { - lp := config.DefaultLotusProvider() +func GetConfig(cctx *cli.Context, db *harmonydb.DB) (*config.CurioConfig, error) { + curioConfig := config.DefaultCurioConfig() have := []string{} layers := append([]string{"base"}, cctx.StringSlice("layers")...) // Always stack on top of "base" layer for _, layer := range layers { @@ -298,26 +375,35 @@ func GetConfig(cctx *cli.Context, db *harmonydb.DB) (*config.LotusProviderConfig return nil, fmt.Errorf("missing layer '%s' ", layer) } if layer == "base" { - return nil, errors.New(`lotus-provider defaults to a layer named 'base'. - Either use 'migrate' command or edit a base.toml and upload it with: lotus-provider config set base.toml`) + return nil, errors.New(`curio defaults to a layer named 'base'. + Either use 'migrate' command or edit a base.toml and upload it with: curio config set base.toml`) } return nil, fmt.Errorf("could not read layer '%s': %w", layer, err) } - meta, err := LoadConfigWithUpgrades(text, lp) + meta, err := LoadConfigWithUpgrades(text, curioConfig) if err != nil { - return lp, fmt.Errorf("could not read layer, bad toml %s: %w", layer, err) + return curioConfig, fmt.Errorf("could not read layer, bad toml %s: %w", layer, err) } for _, k := range meta.Keys() { have = append(have, strings.Join(k, " ")) } - log.Infow("Using layer", "layer", layer, "config", lp) + log.Infow("Using layer", "layer", layer, "config", curioConfig) } _ = have // FUTURE: verify that required fields are here. // If config includes 3rd-party config, consider JSONSchema as a way that // 3rd-parties can dynamically include config requirements and we can // validate the config. Because of layering, we must validate @ startup. - return lp, nil + return curioConfig, nil +} + +func GetDefaultConfig(comment bool) (string, error) { + c := config.DefaultCurioConfig() + cb, err := config.ConfigUpdate(c, nil, config.Commented(comment), config.DefaultKeepUncommented(), config.NoEnv()) + if err != nil { + return "", err + } + return string(cb), nil } func GetDepsCLI(ctx context.Context, cctx *cli.Context) (*Deps, error) { @@ -331,7 +417,7 @@ func GetDepsCLI(ctx context.Context, cctx *cli.Context) (*Deps, error) { return nil, err } - full, fullCloser, err := cliutil.GetFullNodeAPIV1LotusProvider(cctx, cfg.Apis.ChainApiInfo) + full, fullCloser, err := getFullNodeAPIV1Curio(cctx, cfg.Apis.ChainApiInfo) if err != nil { return nil, err } diff --git a/cmd/curio/guidedsetup/guidedsetup.go b/cmd/curio/guidedsetup/guidedsetup.go new file mode 100644 index 000000000..038b07e3f --- /dev/null +++ b/cmd/curio/guidedsetup/guidedsetup.go @@ -0,0 +1,590 @@ +// guidedSetup for migration from lotus-miner to Curio +// +// IF STRINGS CHANGED { +// follow instructions at ../internal/translations/translations.go +// } +package guidedsetup + +import ( + "bytes" + "context" + "encoding/base64" + "encoding/json" + "fmt" + "io" + "math/bits" + "net/http" + "os" + "os/signal" + "path" + "reflect" + "strings" + "syscall" + "time" + + "github.com/BurntSushi/toml" + "github.com/charmbracelet/lipgloss" + "github.com/manifoldco/promptui" + "github.com/mitchellh/go-homedir" + "github.com/samber/lo" + "github.com/urfave/cli/v2" + "golang.org/x/text/language" + "golang.org/x/text/message" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-jsonrpc" + + "github.com/filecoin-project/lotus/api" + "github.com/filecoin-project/lotus/api/v0api" + "github.com/filecoin-project/lotus/build" + "github.com/filecoin-project/lotus/chain/types" + cliutil "github.com/filecoin-project/lotus/cli/util" + _ "github.com/filecoin-project/lotus/cmd/curio/internal/translations" + "github.com/filecoin-project/lotus/lib/harmony/harmonydb" + "github.com/filecoin-project/lotus/node/config" + "github.com/filecoin-project/lotus/node/repo" +) + +// URL to upload user-selected fields to help direct developer's focus. +const DeveloperFocusRequestURL = "https://curiostorage.org/cgi-bin/savedata.php" + +var GuidedsetupCmd = &cli.Command{ + Name: "guided-setup", + Usage: "Run the guided setup for migrating from lotus-miner to Curio", + Flags: []cli.Flag{ + &cli.StringFlag{ // for cliutil.GetFullNodeAPI + Name: "repo", + EnvVars: []string{"LOTUS_PATH"}, + Hidden: true, + Value: "~/.lotus", + }, + }, + Action: func(cctx *cli.Context) (err error) { + T, say := SetupLanguage() + setupCtrlC(say) + + say(header, "This interactive tool migrates lotus-miner to Curio in 5 minutes.") + say(notice, "Each step needs your confirmation and can be reversed. Press Ctrl+C to exit at any time.") + + // Run the migration steps + migrationData := MigrationData{ + T: T, + say: say, + selectTemplates: &promptui.SelectTemplates{ + Help: T("Use the arrow keys to navigate: ↓ ↑ → ← "), + }, + cctx: cctx, + } + for _, step := range migrationSteps { + step(&migrationData) + } + for _, closer := range migrationData.closers { + closer() + } + return nil + }, +} + +func setupCtrlC(say func(style lipgloss.Style, key message.Reference, a ...interface{})) { + c := make(chan os.Signal, 1) + signal.Notify(c, os.Interrupt, syscall.SIGTERM) + go func() { + <-c + say(notice, "Ctrl+C pressed in Terminal") + os.Exit(2) + }() +} + +var ( + header = lipgloss.NewStyle(). + Align(lipgloss.Left). + Foreground(lipgloss.Color("#00FF00")). + Background(lipgloss.Color("#242424")). + BorderStyle(lipgloss.NormalBorder()). + Width(60).Margin(1) + + notice = lipgloss.NewStyle(). + Align(lipgloss.Left). + Bold(true). + Foreground(lipgloss.Color("#CCCCCC")). + Background(lipgloss.Color("#333300")).MarginBottom(1) + + green = lipgloss.NewStyle(). + Align(lipgloss.Left). + Foreground(lipgloss.Color("#00FF00")). + Background(lipgloss.Color("#000000")) + + plain = lipgloss.NewStyle().Align(lipgloss.Left) + + section = lipgloss.NewStyle(). + Align(lipgloss.Left). + Foreground(lipgloss.Color("#000000")). + Background(lipgloss.Color("#FFFFFF")). + Underline(true) + + code = lipgloss.NewStyle(). + Align(lipgloss.Left). + Foreground(lipgloss.Color("#00FF00")). + Background(lipgloss.Color("#f8f9fa")) +) + +func SetupLanguage() (func(key message.Reference, a ...interface{}) string, func(style lipgloss.Style, key message.Reference, a ...interface{})) { + langText := "en" + problem := false + if len(os.Getenv("LANG")) > 1 { + langText = os.Getenv("LANG")[:2] + } else { + problem = true + } + + lang, err := language.Parse(langText) + if err != nil { + lang = language.English + problem = true + fmt.Println("Error parsing language") + } + + langs := message.DefaultCatalog.Languages() + have := lo.SliceToMap(langs, func(t language.Tag) (string, bool) { return t.String(), true }) + if _, ok := have[lang.String()]; !ok { + lang = language.English + problem = true + } + if problem { + _ = os.Setenv("LANG", "en-US") // for later users of this function + notice.Copy().AlignHorizontal(lipgloss.Right). + Render("$LANG=" + langText + " unsupported. Available: " + strings.Join(lo.Keys(have), ", ")) + fmt.Println("Defaulting to English. Please reach out to the Curio team if you would like to have additional language support.") + } + return func(key message.Reference, a ...interface{}) string { + return message.NewPrinter(lang).Sprintf(key, a...) + }, func(sty lipgloss.Style, key message.Reference, a ...interface{}) { + msg := message.NewPrinter(lang).Sprintf(key, a...) + fmt.Println(sty.Render(msg)) + } +} + +type migrationStep func(*MigrationData) + +var migrationSteps = []migrationStep{ + readMinerConfig, // Tells them to be on the miner machine + yugabyteConnect, // Miner is updated + configToDB, // work on base configuration migration. + verifySectors, // Verify the sectors are in the database + doc, + oneLastThing, + complete, +} + +type MigrationData struct { + T func(key message.Reference, a ...interface{}) string + say func(style lipgloss.Style, key message.Reference, a ...interface{}) + selectTemplates *promptui.SelectTemplates + MinerConfigPath string + MinerConfig *config.StorageMiner + DB *harmonydb.DB + MinerID address.Address + full v0api.FullNode + cctx *cli.Context + closers []jsonrpc.ClientCloser +} + +func complete(d *MigrationData) { + stepCompleted(d, d.T("Lotus-Miner to Curio Migration.")) + d.say(plain, "Try the web interface with %s for further guided improvements.", "--layers=gui") + d.say(plain, "You can now migrate your market node (%s), if applicable.", "Boost") +} +func configToDB(d *MigrationData) { + d.say(section, "Migrating lotus-miner config.toml to Curio in-database configuration.") + + { + var closer jsonrpc.ClientCloser + var err error + d.full, closer, err = cliutil.GetFullNodeAPI(d.cctx) + d.closers = append(d.closers, closer) + if err != nil { + d.say(notice, "Error getting API: %s", err.Error()) + os.Exit(1) + } + } + ainfo, err := cliutil.GetAPIInfo(d.cctx, repo.FullNode) + if err != nil { + d.say(notice, "could not get API info for FullNode: %w", err) + os.Exit(1) + } + token, err := d.full.AuthNew(context.Background(), api.AllPermissions) + if err != nil { + d.say(notice, "Error getting token: %s", err.Error()) + os.Exit(1) + } + + chainApiInfo := fmt.Sprintf("%s:%s", string(token), ainfo.Addr) + + d.MinerID, err = SaveConfigToLayer(d.MinerConfigPath, "", false, chainApiInfo) + if err != nil { + d.say(notice, "Error saving config to layer: %s. Aborting Migration", err.Error()) + os.Exit(1) + } +} + +// bucket returns the power's 4 highest bits (rounded down). +func bucket(power *api.MinerPower) uint64 { + rawQAP := power.TotalPower.QualityAdjPower.Uint64() + magnitude := lo.Max([]int{bits.Len64(rawQAP), 5}) + + // shifting erases resolution so we cannot distinguish SPs of similar scales. + return rawQAP >> (uint64(magnitude) - 4) << (uint64(magnitude - 4)) +} + +type uploadType int + +const uploadTypeIndividual uploadType = 0 +const uploadTypeAggregate uploadType = 1 + +// const uploadTypeHint uploadType = 2 +const uploadTypeNothing uploadType = 3 + +func oneLastThing(d *MigrationData) { + d.say(section, "The Curio team wants to improve the software you use. Tell the team you're using `%s`.", "curio") + i, _, err := (&promptui.Select{ + Label: d.T("Select what you want to share with the Curio team."), + Items: []string{ + d.T("Individual Data: Miner ID, Curio version, chain (%s or %s). Signed.", "mainnet", "calibration"), + d.T("Aggregate-Anonymous: version, chain, and Miner power (bucketed)."), + d.T("Hint: I am someone running Curio on whichever chain."), + d.T("Nothing.")}, + Templates: d.selectTemplates, + }).Run() + preference := uploadType(i) + if err != nil { + d.say(notice, "Aborting remaining steps.", err.Error()) + os.Exit(1) + } + if preference != uploadTypeNothing { + msgMap := map[string]any{ + "domain": "curio-newuser", + "net": build.BuildTypeString(), + } + if preference == uploadTypeIndividual || preference == uploadTypeAggregate { + // articles of incorporation + power, err := d.full.StateMinerPower(context.Background(), d.MinerID, types.EmptyTSK) + if err != nil { + d.say(notice, "Error getting miner power: %s", err.Error()) + os.Exit(1) + } + msgMap["version"] = build.BuildVersion + msgMap["net"] = build.BuildType + msgMap["power"] = map[uploadType]uint64{ + uploadTypeIndividual: power.MinerPower.QualityAdjPower.Uint64(), + uploadTypeAggregate: bucket(power)}[preference] + + if preference == uploadTypeIndividual { // Sign it + msgMap["miner_id"] = d.MinerID + msg, err := json.Marshal(msgMap) + if err != nil { + d.say(notice, "Error marshalling message: %s", err.Error()) + os.Exit(1) + } + mi, err := d.full.StateMinerInfo(context.Background(), d.MinerID, types.EmptyTSK) + if err != nil { + d.say(notice, "Error getting miner info: %s", err.Error()) + os.Exit(1) + } + sig, err := d.full.WalletSign(context.Background(), mi.Worker, msg) + if err != nil { + d.say(notice, "Error signing message: %s", err.Error()) + os.Exit(1) + } + msgMap["signature"] = base64.StdEncoding.EncodeToString(sig.Data) + } + } + msg, err := json.Marshal(msgMap) + if err != nil { + d.say(notice, "Error marshalling message: %s", err.Error()) + os.Exit(1) + } + + resp, err := http.DefaultClient.Post(DeveloperFocusRequestURL, "application/json", bytes.NewReader(msg)) + if err != nil { + d.say(notice, "Error sending message: %s", err.Error()) + } + if resp != nil { + defer func() { _ = resp.Body.Close() }() + if resp.StatusCode != 200 { + b, err := io.ReadAll(resp.Body) + if err == nil { + d.say(notice, "Error sending message: Status %s, Message: ", resp.Status, string(b)) + } + } else { + stepCompleted(d, d.T("Message sent.")) + } + } + } +} + +func doc(d *MigrationData) { + d.say(plain, "Documentation: ") + d.say(plain, "The '%s' layer stores common configuration. All curio instances can include it in their %s argument.", "base", "--layers") + d.say(plain, "You can add other layers for per-machine configuration changes.") + + d.say(plain, "Filecoin %s channels: %s and %s", "Slack", "#fil-curio-help", "#fil-curio-dev") + + d.say(plain, "Start multiple Curio instances with the '%s' layer to redundancy.", "post") + //d.say(plain, "Point your browser to your web GUI to complete setup with %s and advanced featues.", "Boost") + d.say(plain, "One database can serve multiple miner IDs: Run a migration for each lotus-miner.") +} + +func verifySectors(d *MigrationData) { + var i []int + var lastError string + fmt.Println() + d.say(section, "Please start (or restart) %s now that database credentials are in %s.", "lotus-miner", "config.toml") + d.say(notice, "Waiting for %s to write sectors into Yugabyte.", "lotus-miner") + + mid, err := address.IDFromAddress(d.MinerID) + if err != nil { + d.say(notice, "Error interpreting miner ID: %s: ID: %s", err.Error(), d.MinerID.String()) + os.Exit(1) + } + + for { + err := d.DB.Select(context.Background(), &i, ` + SELECT count(*) FROM sector_location WHERE miner_id=$1`, mid) + if err != nil { + if err.Error() != lastError { + d.say(notice, "Error verifying sectors: %s", err.Error()) + lastError = err.Error() + } + continue + } + if i[0] > 0 { + break + } + fmt.Print(".") + time.Sleep(5 * time.Second) + } + d.say(plain, "The sectors are in the database. The database is ready for %s.", "Curio") + d.say(notice, "Now shut down lotus-miner and move the systems to %s.", "Curio") + + _, err = (&promptui.Prompt{Label: d.T("Press return to continue")}).Run() + if err != nil { + d.say(notice, "Aborting migration.") + os.Exit(1) + } + stepCompleted(d, d.T("Sectors verified. %d sector locations found.", i)) +} + +func yugabyteConnect(d *MigrationData) { + harmonyCfg := config.DefaultStorageMiner().HarmonyDB //copy the config to a local variable + if d.MinerConfig != nil { + harmonyCfg = d.MinerConfig.HarmonyDB //copy the config to a local variable + } + var err error + d.DB, err = harmonydb.NewFromConfig(harmonyCfg) + if err == nil { + goto yugabyteConnected + } + for { + i, _, err := (&promptui.Select{ + Label: d.T("Enter the info to connect to your Yugabyte database installation (https://download.yugabyte.com/)"), + Items: []string{ + d.T("Host: %s", strings.Join(harmonyCfg.Hosts, ",")), + d.T("Port: %s", harmonyCfg.Port), + d.T("Username: %s", harmonyCfg.Username), + d.T("Password: %s", harmonyCfg.Password), + d.T("Database: %s", harmonyCfg.Database), + d.T("Continue to connect and update schema.")}, + Size: 6, + Templates: d.selectTemplates, + }).Run() + if err != nil { + d.say(notice, "Database config error occurred, abandoning migration: %s ", err.Error()) + os.Exit(1) + } + switch i { + case 0: + host, err := (&promptui.Prompt{ + Label: d.T("Enter the Yugabyte database host(s)"), + }).Run() + if err != nil { + d.say(notice, "No host provided") + continue + } + harmonyCfg.Hosts = strings.Split(host, ",") + case 1, 2, 3, 4: + val, err := (&promptui.Prompt{ + Label: d.T("Enter the Yugabyte database %s", []string{"port", "username", "password", "database"}[i-1]), + }).Run() + if err != nil { + d.say(notice, "No value provided") + continue + } + switch i { + case 1: + harmonyCfg.Port = val + case 2: + harmonyCfg.Username = val + case 3: + harmonyCfg.Password = val + case 4: + harmonyCfg.Database = val + } + continue + case 5: + d.DB, err = harmonydb.NewFromConfig(harmonyCfg) + if err != nil { + if err.Error() == "^C" { + os.Exit(1) + } + d.say(notice, "Error connecting to Yugabyte database: %s", err.Error()) + continue + } + goto yugabyteConnected + } + } + +yugabyteConnected: + d.say(plain, "Connected to Yugabyte. Schema is current.") + if !reflect.DeepEqual(harmonyCfg, d.MinerConfig.HarmonyDB) || !d.MinerConfig.Subsystems.EnableSectorIndexDB { + d.MinerConfig.HarmonyDB = harmonyCfg + d.MinerConfig.Subsystems.EnableSectorIndexDB = true + + d.say(plain, "Enabling Sector Indexing in the database.") + buf, err := config.ConfigUpdate(d.MinerConfig, config.DefaultStorageMiner()) + if err != nil { + d.say(notice, "Error encoding config.toml: %s", err.Error()) + os.Exit(1) + } + _, err = (&promptui.Prompt{ + Label: d.T("Press return to update %s with Yugabyte info. A Backup file will be written to that folder before changes are made.", "config.toml")}).Run() + if err != nil { + os.Exit(1) + } + p, err := homedir.Expand(d.MinerConfigPath) + if err != nil { + d.say(notice, "Error expanding path: %s", err.Error()) + os.Exit(1) + } + tomlPath := path.Join(p, "config.toml") + stat, err := os.Stat(tomlPath) + if err != nil { + d.say(notice, "Error reading filemode of config.toml: %s", err.Error()) + os.Exit(1) + } + fBackup, err := os.CreateTemp(p, "config-backup-*.toml") + if err != nil { + d.say(notice, "Error creating backup file: %s", err.Error()) + os.Exit(1) + } + fBackupContents, err := os.ReadFile(tomlPath) + if err != nil { + d.say(notice, "Error reading config.toml: %s", err.Error()) + os.Exit(1) + } + _, err = fBackup.Write(fBackupContents) + if err != nil { + d.say(notice, "Error writing backup file: %s", err.Error()) + os.Exit(1) + } + err = fBackup.Close() + if err != nil { + d.say(notice, "Error closing backup file: %s", err.Error()) + os.Exit(1) + } + + filemode := stat.Mode() + err = os.WriteFile(path.Join(p, "config.toml"), buf, filemode) + if err != nil { + d.say(notice, "Error writing config.toml: %s", err.Error()) + os.Exit(1) + } + d.say(section, "Restart Lotus Miner. ") + } + stepCompleted(d, d.T("Connected to Yugabyte")) +} + +func readMinerConfig(d *MigrationData) { + d.say(plain, "To start, ensure your sealing pipeline is drained and shut-down lotus-miner.") + + verifyPath := func(dir string) (*config.StorageMiner, error) { + cfg := config.DefaultStorageMiner() + dir, err := homedir.Expand(dir) + if err != nil { + return nil, err + } + _, err = toml.DecodeFile(path.Join(dir, "config.toml"), &cfg) + return cfg, err + } + + dirs := map[string]*config.StorageMiner{"~/.lotusminer": nil, "~/.lotus-miner-local-net": nil} + if v := os.Getenv("LOTUS_MINER_PATH"); v != "" { + dirs[v] = nil + } + for dir := range dirs { + cfg, err := verifyPath(dir) + if err != nil { + delete(dirs, dir) + } + dirs[dir] = cfg + } + + var otherPath bool + if len(dirs) > 0 { + _, str, err := (&promptui.Select{ + Label: d.T("Select the location of your lotus-miner config directory?"), + Items: append(lo.Keys(dirs), d.T("Other")), + Templates: d.selectTemplates, + }).Run() + if err != nil { + if err.Error() == "^C" { + os.Exit(1) + } + otherPath = true + } else { + if str == d.T("Other") { + otherPath = true + } else { + d.MinerConfigPath = str + d.MinerConfig = dirs[str] + } + } + } + if otherPath { + minerPathEntry: + str, err := (&promptui.Prompt{ + Label: d.T("Enter the path to the configuration directory used by %s", "lotus-miner"), + }).Run() + if err != nil { + d.say(notice, "No path provided, abandoning migration ") + os.Exit(1) + } + cfg, err := verifyPath(str) + if err != nil { + d.say(notice, "Cannot read the config.toml file in the provided directory, Error: %s", err.Error()) + goto minerPathEntry + } + d.MinerConfigPath = str + d.MinerConfig = cfg + } + + // Try to lock Miner repo to verify that lotus-miner is not running + { + r, err := repo.NewFS(d.MinerConfigPath) + if err != nil { + d.say(plain, "Could not create repo from directory: %s. Aborting migration", err.Error()) + os.Exit(1) + } + lr, err := r.Lock(repo.StorageMiner) + if err != nil { + d.say(plain, "Could not lock miner repo. Your miner must be stopped: %s\n Aborting migration", err.Error()) + os.Exit(1) + } + _ = lr.Close() + } + + stepCompleted(d, d.T("Read Miner Config")) +} +func stepCompleted(d *MigrationData, step string) { + fmt.Print(green.Render("✔ ")) + d.say(plain, "Step Complete: %s\n", step) +} diff --git a/cmd/curio/guidedsetup/shared.go b/cmd/curio/guidedsetup/shared.go new file mode 100644 index 000000000..e4b840bf2 --- /dev/null +++ b/cmd/curio/guidedsetup/shared.go @@ -0,0 +1,264 @@ +package guidedsetup + +import ( + "bytes" + "context" + "encoding/base64" + "errors" + "fmt" + "os" + "path" + "strings" + + "github.com/BurntSushi/toml" + "github.com/ipfs/go-datastore" + "github.com/samber/lo" + "golang.org/x/xerrors" + + "github.com/filecoin-project/go-address" + + "github.com/filecoin-project/lotus/cmd/curio/deps" + "github.com/filecoin-project/lotus/lib/harmony/harmonydb" + "github.com/filecoin-project/lotus/node/config" + "github.com/filecoin-project/lotus/node/modules" + "github.com/filecoin-project/lotus/node/repo" +) + +const ( + FlagMinerRepo = "miner-repo" +) + +const FlagMinerRepoDeprecation = "storagerepo" + +func SaveConfigToLayer(minerRepoPath, layerName string, overwrite bool, chainApiInfo string) (minerAddress address.Address, err error) { + _, say := SetupLanguage() + ctx := context.Background() + + r, err := repo.NewFS(minerRepoPath) + if err != nil { + return minerAddress, err + } + + ok, err := r.Exists() + if err != nil { + return minerAddress, err + } + + if !ok { + return minerAddress, fmt.Errorf("repo not initialized at: %s", minerRepoPath) + } + + lr, err := r.LockRO(repo.StorageMiner) + if err != nil { + return minerAddress, fmt.Errorf("locking repo: %w", err) + } + defer func() { + err = lr.Close() + if err != nil { + fmt.Println("error closing repo: ", err) + } + }() + + cfgNode, err := lr.Config() + if err != nil { + return minerAddress, fmt.Errorf("getting node config: %w", err) + } + smCfg := cfgNode.(*config.StorageMiner) + + db, err := harmonydb.NewFromConfig(smCfg.HarmonyDB) + if err != nil { + return minerAddress, fmt.Errorf("could not reach the database. Ensure the Miner config toml's HarmonyDB entry"+ + " is setup to reach Yugabyte correctly: %w", err) + } + + var titles []string + err = db.Select(ctx, &titles, `SELECT title FROM harmony_config WHERE LENGTH(config) > 0`) + if err != nil { + return minerAddress, fmt.Errorf("miner cannot reach the db. Ensure the config toml's HarmonyDB entry"+ + " is setup to reach Yugabyte correctly: %s", err.Error()) + } + + // Copy over identical settings: + + buf, err := os.ReadFile(path.Join(lr.Path(), "config.toml")) + if err != nil { + return minerAddress, fmt.Errorf("could not read config.toml: %w", err) + } + curioCfg := config.DefaultCurioConfig() + + ensureEmptyArrays(curioCfg) + _, err = deps.LoadConfigWithUpgrades(string(buf), curioCfg) + + if err != nil { + return minerAddress, fmt.Errorf("could not decode toml: %w", err) + } + + // Populate Miner Address + mmeta, err := lr.Datastore(ctx, "/metadata") + if err != nil { + return minerAddress, xerrors.Errorf("opening miner metadata datastore: %w", err) + } + defer func() { + // _ = mmeta.Close() + }() + + maddrBytes, err := mmeta.Get(ctx, datastore.NewKey("miner-address")) + if err != nil { + return minerAddress, xerrors.Errorf("getting miner address datastore entry: %w", err) + } + + addr, err := address.NewFromBytes(maddrBytes) + if err != nil { + return minerAddress, xerrors.Errorf("parsing miner actor address: %w", err) + } + + minerAddress = addr + + curioCfg.Addresses = []config.CurioAddresses{{ + MinerAddresses: []string{addr.String()}, + PreCommitControl: smCfg.Addresses.PreCommitControl, + CommitControl: smCfg.Addresses.CommitControl, + TerminateControl: smCfg.Addresses.TerminateControl, + DisableOwnerFallback: smCfg.Addresses.DisableOwnerFallback, + DisableWorkerFallback: smCfg.Addresses.DisableWorkerFallback, + }} + + ks, err := lr.KeyStore() + if err != nil { + return minerAddress, xerrors.Errorf("keystore err: %w", err) + } + js, err := ks.Get(modules.JWTSecretName) + if err != nil { + return minerAddress, xerrors.Errorf("error getting JWTSecretName: %w", err) + } + + curioCfg.Apis.StorageRPCSecret = base64.StdEncoding.EncodeToString(js.PrivateKey) + + curioCfg.Apis.ChainApiInfo = append(curioCfg.Apis.ChainApiInfo, chainApiInfo) + // Express as configTOML + configTOML := &bytes.Buffer{} + if err = toml.NewEncoder(configTOML).Encode(curioCfg); err != nil { + return minerAddress, err + } + + if lo.Contains(titles, "base") { + // append addresses + var baseCfg = config.DefaultCurioConfig() + var baseText string + err = db.QueryRow(ctx, "SELECT config FROM harmony_config WHERE title='base'").Scan(&baseText) + if err != nil { + return minerAddress, xerrors.Errorf("Cannot load base config: %w", err) + } + ensureEmptyArrays(baseCfg) + _, err := deps.LoadConfigWithUpgrades(baseText, baseCfg) + if err != nil { + return minerAddress, xerrors.Errorf("Cannot load base config: %w", err) + } + for _, addr := range baseCfg.Addresses { + if lo.Contains(addr.MinerAddresses, curioCfg.Addresses[0].MinerAddresses[0]) { + goto skipWritingToBase + } + } + // write to base + { + baseCfg.Addresses = append(baseCfg.Addresses, curioCfg.Addresses[0]) + baseCfg.Addresses = lo.Filter(baseCfg.Addresses, func(a config.CurioAddresses, _ int) bool { + return len(a.MinerAddresses) > 0 + }) + + cb, err := config.ConfigUpdate(baseCfg, config.DefaultCurioConfig(), config.Commented(true), config.DefaultKeepUncommented(), config.NoEnv()) + if err != nil { + return minerAddress, xerrors.Errorf("cannot interpret config: %w", err) + } + _, err = db.Exec(ctx, "UPDATE harmony_config SET config=$1 WHERE title='base'", string(cb)) + if err != nil { + return minerAddress, xerrors.Errorf("cannot update base config: %w", err) + } + say(plain, "Configuration 'base' was updated to include this miner's address and its wallet setup.") + } + say(plain, "Compare the configurations %s to %s. Changes between the miner IDs other than wallet addreses should be a new, minimal layer for runners that need it.", "base", "mig-"+curioCfg.Addresses[0].MinerAddresses[0]) + skipWritingToBase: + } else if layerName == "" { + cfg, err := deps.GetDefaultConfig(true) + if err != nil { + return minerAddress, xerrors.Errorf("Cannot get default config: %w", err) + } + _, err = db.Exec(ctx, `INSERT INTO harmony_config (title, config) VALUES ('base', $1) + ON CONFLICT(title) DO UPDATE SET config=EXCLUDED.config`, cfg) + + if err != nil { + return minerAddress, xerrors.Errorf("Cannot insert base config: %w", err) + } + say(notice, "Configuration 'base' was created to include this miner's address and its wallet setup.") + } + + if layerName == "" { // only make mig if base exists and we are different. // compare to base. + layerName = fmt.Sprintf("mig-%s", curioCfg.Addresses[0].MinerAddresses[0]) + overwrite = true + } else { + if lo.Contains(titles, layerName) && !overwrite { + return minerAddress, errors.New("the overwrite flag is needed to replace existing layer: " + layerName) + } + } + if overwrite { + _, err := db.Exec(ctx, "DELETE FROM harmony_config WHERE title=$1", layerName) + if err != nil { + return minerAddress, xerrors.Errorf("Cannot delete existing layer: %w", err) + } + } + + _, err = db.Exec(ctx, "INSERT INTO harmony_config (title, config) VALUES ($1, $2)", layerName, configTOML.String()) + if err != nil { + return minerAddress, xerrors.Errorf("Cannot insert layer after layer created message: %w", err) + } + say(plain, "Layer %s created. ", layerName) + + dbSettings := getDBSettings(*smCfg) + say(plain, "To work with the config: ") + fmt.Println(code.Render(`curio ` + dbSettings + ` config edit base`)) + say(plain, `To run Curio: With machine or cgroup isolation, use the command (with example layer selection):`) + fmt.Println(code.Render(`curio ` + dbSettings + ` run --layer=post`)) + return minerAddress, nil +} + +func getDBSettings(smCfg config.StorageMiner) string { + dbSettings := "" + def := config.DefaultStorageMiner().HarmonyDB + if def.Hosts[0] != smCfg.HarmonyDB.Hosts[0] { + dbSettings += ` --db-host="` + strings.Join(smCfg.HarmonyDB.Hosts, ",") + `"` + } + if def.Port != smCfg.HarmonyDB.Port { + dbSettings += " --db-port=" + smCfg.HarmonyDB.Port + } + if def.Username != smCfg.HarmonyDB.Username { + dbSettings += ` --db-user="` + smCfg.HarmonyDB.Username + `"` + } + if def.Password != smCfg.HarmonyDB.Password { + dbSettings += ` --db-password="` + smCfg.HarmonyDB.Password + `"` + } + if def.Database != smCfg.HarmonyDB.Database { + dbSettings += ` --db-name="` + smCfg.HarmonyDB.Database + `"` + } + return dbSettings +} + +func ensureEmptyArrays(cfg *config.CurioConfig) { + if cfg.Addresses == nil { + cfg.Addresses = []config.CurioAddresses{} + } else { + for i := range cfg.Addresses { + if cfg.Addresses[i].PreCommitControl == nil { + cfg.Addresses[i].PreCommitControl = []string{} + } + if cfg.Addresses[i].CommitControl == nil { + cfg.Addresses[i].CommitControl = []string{} + } + if cfg.Addresses[i].TerminateControl == nil { + cfg.Addresses[i].TerminateControl = []string{} + } + } + } + if cfg.Apis.ChainApiInfo == nil { + cfg.Apis.ChainApiInfo = []string{} + } +} diff --git a/cmd/curio/internal/translations/catalog.go b/cmd/curio/internal/translations/catalog.go new file mode 100644 index 000000000..1aca33033 --- /dev/null +++ b/cmd/curio/internal/translations/catalog.go @@ -0,0 +1,331 @@ +// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. + +package translations + +import ( + "golang.org/x/text/language" + "golang.org/x/text/message" + "golang.org/x/text/message/catalog" +) + +type dictionary struct { + index []uint32 + data string +} + +func (d *dictionary) Lookup(key string) (data string, ok bool) { + p, ok := messageKeyToIndex[key] + if !ok { + return "", false + } + start, end := d.index[p], d.index[p+1] + if start == end { + return "", false + } + return d.data[start:end], true +} + +func init() { + dict := map[string]catalog.Dictionary{ + "en": &dictionary{index: enIndex, data: enData}, + "ko": &dictionary{index: koIndex, data: koData}, + "zh": &dictionary{index: zhIndex, data: zhData}, + } + fallback := language.MustParse("en") + cat, err := catalog.NewFromMap(dict, catalog.Fallback(fallback)) + if err != nil { + panic(err) + } + message.DefaultCatalog = cat +} + +var messageKeyToIndex = map[string]int{ + "Aborting migration.": 41, + "Aborting remaining steps.": 19, + "Aggregate-Anonymous: version, net, and Miner power (bucketed).": 16, + "Cannot read the config.toml file in the provided directory, Error: %s": 70, + "Compare the configurations %s to %s. Changes between the miner IDs other than wallet addreses should be a new, minimal layer for runners that need it.": 76, + "Configuration 'base' was created to include this miner's address and its wallet setup.": 77, + "Configuration 'base' was updated to include this miner's address and its wallet setup.": 75, + "Connected to Yugabyte": 64, + "Connected to Yugabyte. Schema is current.": 56, + "Continue to connect and update schema.": 49, + "Could not create repo from directory: %s. Aborting migration": 71, + "Could not lock miner repo. Your miner must be stopped: %s\n Aborting migration": 72, + "Ctrl+C pressed in Terminal": 3, + "Database config error occurred, abandoning migration: %s ": 50, + "Database: %s": 48, + "Documentation: ": 27, + "Each step needs your confirmation and can be reversed. Press Ctrl+C to exit at any time.": 1, + "Enabling Sector Indexing in the database.": 57, + "Enter the Yugabyte database %s": 53, + "Enter the Yugabyte database host(s)": 51, + "Enter the info to connect to your Yugabyte database installation (https://download.yugabyte.com/)": 43, + "Enter the path to the configuration directory used by %s": 68, + "Error connecting to Yugabyte database: %s": 55, + "Error connecting to lotus node: %s %s": 9, + "Error encoding config.toml: %s": 58, + "Error expanding path: %s": 60, + "Error getting miner info: %s": 22, + "Error getting miner power: %s": 20, + "Error getting token: %s": 11, + "Error interpreting miner ID: %s: ID: %s": 36, + "Error marshalling message: %s": 21, + "Error reading filemode of config.toml: %s": 61, + "Error reading from database: %s. Aborting Migration.": 8, + "Error saving config to layer: %s. Aborting Migration": 12, + "Error sending message: %s": 24, + "Error sending message: Status %s, Message: ": 25, + "Error signing message: %s": 23, + "Error verifying sectors: %s": 37, + "Error writing config.toml: %s": 62, + "Filecoin %s channels: %s and %s": 30, + "Hint: I am someone running Curio on net.": 17, + "Host: %s": 44, + "Individual Data: Miner ID, Curio version, net (%s or %s). Signed.": 15, + "Layer %s created. ": 78, + "Lotus-Miner to Curio Migration.": 4, + "Message sent.": 26, + "Migrating config.toml to database.": 7, + "No host provided": 52, + "No path provided, abandoning migration ": 69, + "No value provided": 54, + "Nothing.": 18, + "Now shut down lotus-miner and move the systems to %s.": 39, + "One database can serve multiple miner IDs: Run a migration for each lotus-miner.": 33, + "Other": 67, + "Password: %s": 47, + "Please start (or restart) %s now that database credentials are in %s.": 34, + "Point your browser to your web GUI to complete setup with %s and advanced featues.": 32, + "Port: %s": 45, + "Press return to continue": 40, + "Press return to update %s with Yugabyte info. Backup the file now.": 59, + "Protocol Labs wants to improve the software you use. Tell the team you're using Curio.": 13, + "Read Miner Config": 73, + "Restart Lotus Miner. ": 63, + "Sectors verified. %d sector locations found.": 42, + "Select the location of your lotus-miner config directory?": 66, + "Select what you want to share with the Curio team.": 14, + "Start multiple Curio instances with the '%s' layer to redundancy.": 31, + "Step Complete: %s\n": 74, + "The '%s' layer stores common configuration. All curio instances can include it in their %s argument.": 28, + "The sectors are in the database. The database is ready for %s.": 38, + "This interactive tool migrates lotus-miner to Curio in 5 minutes.": 0, + "To run Curio: With machine or cgroup isolation, use the command (with example layer selection):": 80, + "To start, ensure your sealing pipeline is drained and shut-down lotus-miner.": 65, + "To work with the config: ": 79, + "Try the web interface with %s for further guided improvements.": 5, + "Use the arrow keys to navigate: ↓ ↑ → ← ": 2, + "Username: %s": 46, + "Waiting for %s to write sectors into Yugabyte.": 35, + "You can add other layers for per-machine configuration changes.": 29, + "You can now migrate your market node (%s), if applicable.": 6, + "could not get API info for FullNode: %w": 10, +} + +var enIndex = []uint32{ // 82 elements + // Entry 0 - 1F + 0x00000000, 0x00000042, 0x0000009b, 0x000000d0, + 0x000000eb, 0x0000010b, 0x0000014d, 0x0000018a, + 0x000001ad, 0x000001e5, 0x00000211, 0x0000023c, + 0x00000257, 0x0000028f, 0x000002e6, 0x00000319, + 0x00000361, 0x000003a0, 0x000003c9, 0x000003d2, + 0x000003ec, 0x0000040d, 0x0000042e, 0x0000044e, + 0x0000046b, 0x00000488, 0x000004bb, 0x000004c9, + 0x000004dd, 0x00000548, 0x00000588, 0x000005b1, + // Entry 20 - 3F + 0x000005f6, 0x0000064c, 0x0000069d, 0x000006e9, + 0x0000071b, 0x00000749, 0x00000768, 0x000007aa, + 0x000007e3, 0x000007fc, 0x00000810, 0x00000840, + 0x000008a2, 0x000008ae, 0x000008ba, 0x000008ca, + 0x000008da, 0x000008ea, 0x00000911, 0x00000952, + 0x00000976, 0x00000987, 0x000009a9, 0x000009bb, + 0x000009e8, 0x00000a12, 0x00000a3c, 0x00000a5e, + 0x00000aa4, 0x00000ac0, 0x00000aed, 0x00000b0e, + // Entry 40 - 5F + 0x00000b28, 0x00000b3e, 0x00000b8b, 0x00000bc5, + 0x00000bcb, 0x00000c07, 0x00000c33, 0x00000c7c, + 0x00000cbc, 0x00000d0d, 0x00000d1f, 0x00000d39, + 0x00000d90, 0x00000e2d, 0x00000e84, 0x00000e9e, + 0x00000ebc, 0x00000f1c, +} // Size: 352 bytes + +const enData string = "" + // Size: 3868 bytes + "\x02This interactive tool migrates lotus-miner to Curio in 5 minutes." + + "\x02Each step needs your confirmation and can be reversed. Press Ctrl+C " + + "to exit at any time.\x04\x00\x01 0\x02Use the arrow keys to navigate: ↓ " + + "↑ → ←\x02Ctrl+C pressed in Terminal\x02Lotus-Miner to Curio Migration." + + "\x02Try the web interface with %[1]s for further guided improvements." + + "\x02You can now migrate your market node (%[1]s), if applicable.\x02Migr" + + "ating config.toml to database.\x02Error reading from database: %[1]s. Ab" + + "orting Migration.\x02Error connecting to lotus node: %[1]s %[2]s\x02coul" + + "d not get API info for FullNode: %[1]w\x02Error getting token: %[1]s\x02" + + "Error saving config to layer: %[1]s. Aborting Migration\x02Protocol Labs" + + " wants to improve the software you use. Tell the team you're using Curio" + + ".\x02Select what you want to share with the Curio team.\x02Individual Da" + + "ta: Miner ID, Curio version, net (%[1]s or %[2]s). Signed.\x02Aggregate-" + + "Anonymous: version, net, and Miner power (bucketed).\x02Hint: I am someo" + + "ne running Curio on net.\x02Nothing.\x02Aborting remaining steps.\x02Err" + + "or getting miner power: %[1]s\x02Error marshalling message: %[1]s\x02Err" + + "or getting miner info: %[1]s\x02Error signing message: %[1]s\x02Error se" + + "nding message: %[1]s\x04\x00\x01 .\x02Error sending message: Status %[1]" + + "s, Message:\x02Message sent.\x04\x00\x01 \x0f\x02Documentation:\x02The '" + + "%[1]s' layer stores common configuration. All curio instances can includ" + + "e it in their %[2]s argument.\x02You can add other layers for per-machin" + + "e configuration changes.\x02Filecoin %[1]s channels: %[2]s and %[3]s\x02" + + "Start multiple Curio instances with the '%[1]s' layer to redundancy.\x02" + + "Point your browser to your web GUI to complete setup with %[1]s and adva" + + "nced featues.\x02One database can serve multiple miner IDs: Run a migrat" + + "ion for each lotus-miner.\x02Please start (or restart) %[1]s now that da" + + "tabase credentials are in %[2]s.\x02Waiting for %[1]s to write sectors i" + + "nto Yugabyte.\x02Error interpreting miner ID: %[1]s: ID: %[2]s\x02Error " + + "verifying sectors: %[1]s\x02The sectors are in the database. The databas" + + "e is ready for %[1]s.\x02Now shut down lotus-miner and move the systems " + + "to %[1]s.\x02Press return to continue\x02Aborting migration.\x02Sectors " + + "verified. %[1]d sector locations found.\x02Enter the info to connect to " + + "your Yugabyte database installation (https://download.yugabyte.com/)\x02" + + "Host: %[1]s\x02Port: %[1]s\x02Username: %[1]s\x02Password: %[1]s\x02Data" + + "base: %[1]s\x02Continue to connect and update schema.\x04\x00\x01 <\x02D" + + "atabase config error occurred, abandoning migration: %[1]s\x02Enter the " + + "Yugabyte database host(s)\x02No host provided\x02Enter the Yugabyte data" + + "base %[1]s\x02No value provided\x02Error connecting to Yugabyte database" + + ": %[1]s\x02Connected to Yugabyte. Schema is current.\x02Enabling Sector " + + "Indexing in the database.\x02Error encoding config.toml: %[1]s\x02Press " + + "return to update %[1]s with Yugabyte info. Backup the file now.\x02Error" + + " expanding path: %[1]s\x02Error reading filemode of config.toml: %[1]s" + + "\x02Error writing config.toml: %[1]s\x04\x00\x01 \x15\x02Restart Lotus M" + + "iner.\x02Connected to Yugabyte\x02To start, ensure your sealing pipeline" + + " is drained and shut-down lotus-miner.\x02Select the location of your lo" + + "tus-miner config directory?\x02Other\x02Enter the path to the configurat" + + "ion directory used by %[1]s\x04\x00\x01 '\x02No path provided, abandonin" + + "g migration\x02Cannot read the config.toml file in the provided director" + + "y, Error: %[1]s\x02Could not create repo from directory: %[1]s. Aborting" + + " migration\x02Could not lock miner repo. Your miner must be stopped: %[1" + + "]s\x0a Aborting migration\x02Read Miner Config\x04\x00\x01\x0a\x15\x02St" + + "ep Complete: %[1]s\x02Configuration 'base' was updated to include this m" + + "iner's address and its wallet setup.\x02Compare the configurations %[1]s" + + " to %[2]s. Changes between the miner IDs other than wallet addreses shou" + + "ld be a new, minimal layer for runners that need it.\x02Configuration 'b" + + "ase' was created to include this miner's address and its wallet setup." + + "\x04\x00\x01 \x15\x02Layer %[1]s created.\x04\x00\x01 \x19\x02To work wi" + + "th the config:\x02To run Curio: With machine or cgroup isolation, use th" + + "e command (with example layer selection):" + +var koIndex = []uint32{ // 82 elements + // Entry 0 - 1F + 0x00000000, 0x0000004d, 0x000000c8, 0x0000010c, + 0x0000012d, 0x00000150, 0x00000150, 0x000001a0, + 0x000001da, 0x0000022f, 0x0000022f, 0x0000022f, + 0x0000022f, 0x00000287, 0x00000315, 0x0000034e, + 0x000003aa, 0x000003f4, 0x00000437, 0x00000452, + 0x00000477, 0x000004b1, 0x000004e4, 0x0000051e, + 0x00000548, 0x00000572, 0x000005b4, 0x000005d8, + 0x000005e5, 0x0000066b, 0x000006bd, 0x000006bd, + // Entry 20 - 3F + 0x000006bd, 0x0000071e, 0x0000071e, 0x0000071e, + 0x00000762, 0x00000762, 0x00000789, 0x000007f4, + 0x0000083e, 0x00000865, 0x00000880, 0x000008cf, + 0x0000093d, 0x0000094e, 0x0000095c, 0x00000974, + 0x00000988, 0x000009a2, 0x000009cc, 0x00000a2f, + 0x00000a6b, 0x00000a95, 0x00000acd, 0x00000af1, + 0x00000b45, 0x00000b86, 0x00000b86, 0x00000bcd, + 0x00000c39, 0x00000c39, 0x00000c88, 0x00000cc6, + // Entry 40 - 5F + 0x00000cea, 0x00000d00, 0x00000d6b, 0x00000dba, + 0x00000dc1, 0x00000e09, 0x00000e5b, 0x00000eb5, + 0x00000eb5, 0x00000eb5, 0x00000ecd, 0x00000ee7, + 0x00000f51, 0x0000100b, 0x0000106f, 0x0000109e, + 0x0000109e, 0x0000112a, +} // Size: 352 bytes + +const koData string = "" + // Size: 4394 bytes + "\x02이 대화형 도구는 5분 안에 lotus-miner를 Curio로 이주합니다.\x02각 단계는 확인이 필요하며 되돌릴 수 있" + + "습니다. 언제든지 Ctrl+C를 눌러 종료할 수 있습니다.\x04\x00\x01 ?\x02화살표 키를 사용하여 이동하세요: ↓" + + " ↑ → ←\x02터미널에서 Ctrl+C가 눌림\x02Lotus-Miner에서 Curio로 이주.\x02해당하는 경우 이제 시장 " + + "노드를 이주할 수 있습니다 (%[1]s).\x02config.toml을 데이터베이스로 이주 중입니다.\x02데이터베이스에서 읽" + + "는 중 오류 발생: %[1]s. 마이그레이션 중단.\x02레이어에 구성을 저장하는 중 오류 발생: %[1]s. 마이그레이션 중" + + "단\x02Protocol Labs는 당신이 사용하는 소프트웨어를 개선하고 싶어합니다. Curio를 사용 중이라고 팀에 알려주세" + + "요.\x02Curio 팀과 공유하고 싶은 것을 선택하세요.\x02개별 데이터: 마이너 ID, Curio 버전, 네트워크 (%[" + + "1]s 또는 %[2]s). 서명됨.\x02집계-익명: 버전, 네트워크, 그리고 마이너 파워 (버킷).\x02힌트: 네트워크에서 C" + + "urio를 실행 중인 사람입니다.\x02아무것도 없습니다.\x02나머지 단계를 중단합니다.\x02마이너 파워를 가져오는 중 오류 " + + "발생: %[1]s\x02메시지를 마샬하는 중 오류 발생: %[1]s\x02마이너 정보를 가져오는 중 오류 발생: %[1]s" + + "\x02메시지 서명 중 오류 발생: %[1]s\x02메시지 전송 중 오류 발생: %[1]s\x04\x00\x01 =\x02메시지 " + + "전송 중 오류 발생: 상태 %[1]s, 메시지:\x02메시지가 전송되었습니다.\x04\x00\x01 \x08\x02문서:" + + "\x02'%[1]s' 레이어에는 공통 구성이 저장됩니다. 모든 Curio 인스턴스는 %[2]s 인수에 포함시킬 수 있습니다." + + "\x02기계별 구성 변경을 위해 다른 레이어를 추가할 수 있습니다.\x02브라우저를 웹 GUI로 이동하여 %[1]s 및 고급 기능" + + "으로 설정을 완료하세요.\x02%[1]s가 Yugabyte에 섹터를 기록하도록 대기 중입니다.\x02섹터 확인 중 오류 발생:" + + " %[1]s\x02섹터가 데이터베이스에 있습니다. 데이터베이스가 %[1]s를 위해 준비되었습니다.\x02이제 lotus-miner" + + "를 종료하고 시스템을 %[1]s로 이동하세요.\x02계속하려면 리턴을 누르세요\x02마이그레이션 중단.\x02섹터가 확인되었습" + + "니다. %[1]d개의 섹터 위치를 찾았습니다.\x02Yugabyte 데이터베이스 설치에 연결할 정보를 입력하십시오 (https" + + "://download.yugabyte.com/)\x02호스트: %[1]s\x02포트: %[1]s\x02사용자 이름: %[1]s" + + "\x02비밀번호: %[1]s\x02데이터베이스: %[1]s\x02계속 연결 및 스키마 업데이트.\x04\x00\x01 ^\x02데" + + "이터베이스 구성 오류가 발생하여 마이그레이션을 포기합니다: %[1]s\x02Yugabyte 데이터베이스 호스트를 입력하십시오" + + "\x02호스트가 제공되지 않았습니다\x02Yugabyte 데이터베이스 %[1]s을 입력하십시오\x02값이 제공되지 않았습니다" + + "\x02Yugabyte 데이터베이스에 연결하는 중 오류가 발생했습니다: %[1]s\x02Yugabyte에 연결되었습니다. 스키마가" + + " 현재입니다.\x02config.toml을 인코딩하는 중 오류가 발생했습니다: %[1]s\x02%[1]s을 Yugabyte 정보로" + + " 업데이트하려면 리턴을 누르세요. 지금 파일을 백업하세요.\x02config.toml의 파일 모드를 읽는 중 오류가 발생했습니다:" + + " %[1]s\x02config.toml을 쓰는 중 오류가 발생했습니다: %[1]s\x04\x00\x01 \x1f\x02로터스 마이" + + "너 재시작.\x02Yugabyte에 연결됨\x02시작하려면 밀봉 파이프라인이 비어 있고 lotus-miner가 종료되었는지 확" + + "인하세요.\x02로터스 마이너 구성 디렉토리의 위치를 선택하시겠습니까?\x02기타\x02%[1]s에서 사용하는 구성 디렉터리 " + + "경로를 입력하세요.\x04\x00\x01 M\x02경로가 제공되지 않았으므로 마이그레이션을 포기합니다\x02제공된 디렉토리에서" + + " config.toml 파일을 읽을 수 없습니다. 오류: %[1]s\x02마이너 구성 읽기\x04\x00\x01\x0a\x15" + + "\x02단계 완료: %[1]s\x02이 마이너의 주소와 지갑 설정을 포함하도록 구성 'base'가 업데이트되었습니다.\x02구성 " + + "%[1]s를 %[2]s과 비교하세요. 지갑 주소 이외의 마이너 ID 사이의 변경 사항은 필요한 실행자를 위한 새로운 최소한의 레이" + + "어여야 합니다.\x02이 마이너의 주소와 지갑 설정을 포함하도록 구성 'base'가 생성되었습니다.\x04\x00\x01 *" + + "\x02레이어 %[1]s가 생성되었습니다.\x02Curio를 실행하려면: 기계 또는 cgroup 격리를 사용하여 다음 명령을 사용" + + "하세요 (예제 레이어 선택과 함께):" + +var zhIndex = []uint32{ // 82 elements + // Entry 0 - 1F + 0x00000000, 0x00000048, 0x00000097, 0x000000ca, + 0x000000e3, 0x00000100, 0x00000100, 0x00000141, + 0x0000016b, 0x000001a4, 0x000001a4, 0x000001a4, + 0x000001a4, 0x000001dd, 0x0000022f, 0x0000025c, + 0x000002a9, 0x000002e7, 0x00000317, 0x00000321, + 0x00000337, 0x0000035b, 0x00000379, 0x0000039d, + 0x000003bb, 0x000003d9, 0x0000040e, 0x00000421, + 0x00000430, 0x0000048a, 0x000004c7, 0x000004c7, + // Entry 20 - 3F + 0x000004c7, 0x0000051e, 0x0000051e, 0x0000051e, + 0x00000544, 0x00000544, 0x00000562, 0x0000059e, + 0x000005d0, 0x000005e0, 0x000005f0, 0x00000623, + 0x0000067d, 0x0000068c, 0x0000069b, 0x000006ad, + 0x000006bc, 0x000006ce, 0x000006ed, 0x00000725, + 0x0000074a, 0x0000075a, 0x00000778, 0x00000785, + 0x000007b1, 0x000007de, 0x000007de, 0x00000801, + 0x00000845, 0x00000845, 0x00000874, 0x00000897, + // Entry 40 - 5F + 0x000008b7, 0x000008cc, 0x00000917, 0x00000947, + 0x0000094e, 0x00000978, 0x0000099c, 0x000009e0, + 0x000009e0, 0x000009e0, 0x000009f3, 0x00000a0d, + 0x00000a59, 0x00000adb, 0x00000b27, 0x00000b41, + 0x00000b41, 0x00000b98, +} // Size: 352 bytes + +const zhData string = "" + // Size: 2968 bytes + "\x02这个交互式工具可以在5分钟内将lotus-miner迁移到Curio。\x02每一步都需要您的确认,并且可以撤销。随时按Ctrl+C退出" + + "。\x04\x00\x01 .\x02使用箭头键进行导航:↓ ↑ → ←\x02在终端中按下Ctrl+C\x02Lotus-Miner到Cu" + + "rio迁移。\x02如果适用,您现在可以迁移您的市场节点(%[1]s)。\x02正在将config.toml迁移到数据库。\x02读取数据库时出" + + "错:%[1]s。正在中止迁移。\x02保存配置到层时出错:%[1]s。正在中止迁移\x02Protocol Labs希望改进您使用的软件。告" + + "诉团队您正在使用Curio。\x02选择您想与Curio团队分享的内容。\x02个人数据:矿工ID、Curio版本、网络(%[1]s或%[2" + + "]s)。已签名。\x02聚合-匿名:版本、网络和矿工功率(分桶)。\x02提示:我是在网络上运行Curio的人。\x02没有。\x02中止剩余步" + + "骤。\x02获取矿工功率时出错:%[1]s\x02整理消息时出错:%[1]s\x02获取矿工信息时出错:%[1]s\x02签署消息时出错:%" + + "[1]s\x02发送消息时出错:%[1]s\x04\x00\x01 0\x02发送消息时出错:状态%[1]s,消息:\x02消息已发送。\x04" + + "\x00\x01 \x0a\x02文档:\x02'%[1]s'层存储通用配置。所有Curio实例都可以在其%[2]s参数中包含它。\x02您可以" + + "添加其他层进行每台机器的配置更改。\x02将您的浏览器指向您的网络GUI,以使用%[1]s和高级功能完成设置。\x02等待%[1]s将扇区写" + + "入Yugabyte。\x02验证扇区时出错:%[1]s\x02扇区在数据库中。数据库已准备好用于%[1]s。\x02现在关闭lotus-mi" + + "ner并将系统移至%[1]s。\x02按回车继续\x02中止迁移。\x02扇区已验证。发现了%[1]d个扇区位置。\x02输入连接到您的Yuga" + + "byte数据库安装的信息(https://download.yugabyte.com/)\x02主机:%[1]s\x02端口:%[1]s\x02" + + "用户名:%[1]s\x02密码:%[1]s\x02数据库:%[1]s\x02继续连接和更新架构。\x04\x00\x01 3\x02发生数据" + + "库配置错误,放弃迁移:%[1]s\x02输入Yugabyte数据库主机(S)\x02未提供主机\x02输入Yugabyte数据库 %[1]s" + + "\x02未提供值\x02连接到Yugabyte数据库时出错:%[1]s\x02已连接到Yugabyte。模式是当前的。\x02编码config." + + "toml时出错:%[1]s\x02按回车更新%[1]s以获取Yugabyte信息。现在备份文件。\x02读取config.toml文件模式时出错" + + ":%[1]s\x02写入config.toml时出错:%[1]s\x04\x00\x01 \x1b\x02重新启动Lotus Miner。" + + "\x02已连接到Yugabyte\x02开始之前,请确保您的密封管道已排空并关闭lotus-miner。\x02选择您的lotus-miner配" + + "置目录的位置?\x02其他\x02输入%[1]s使用的配置目录的路径\x04\x00\x01 \x1f\x02未提供路径,放弃迁移\x02无" + + "法读取提供的目录中的config.toml文件,错误:%[1]s\x02读取矿工配置\x04\x00\x01\x0a\x15\x02步骤完成" + + ":%[1]s\x02配置'base'已更新,包含了这个矿工的地址和其钱包设置。\x02比较配置%[1]s和%[2]s。矿工ID之间除了钱包地" + + "址的变化应该是需要的运行者的一个新的、最小的层。\x02配置'base'已创建,包括了这个矿工的地址和其钱包设置。\x04\x00\x01 " + + "\x15\x02层%[1]s已创建。\x02运行Curio:使用机器或cgroup隔离,使用命令(附带示例层选择):" + + // Total table size 12286 bytes (11KiB); checksum: 15B16994 diff --git a/cmd/curio/internal/translations/knowns/main.go b/cmd/curio/internal/translations/knowns/main.go new file mode 100644 index 000000000..a30a94065 --- /dev/null +++ b/cmd/curio/internal/translations/knowns/main.go @@ -0,0 +1,82 @@ +package main + +import ( + "bytes" + "encoding/json" + "fmt" + "os" + "path" + + "github.com/samber/lo" +) + +func main() { + for _, arg := range os.Args { + handleKnowns(arg) + } +} + +func handleKnowns(pathStart string) { + outpath := path.Join(pathStart, "out.gotext.json") + b, err := os.ReadFile(outpath) + if err != nil { + fmt.Println("cannot open "+outpath+":", err) + return + } + type TMsg struct { + ID string `json:"id"` + Translation string `json:"translation"` + Message string `json:"message"` + Placeholder json.RawMessage `json:"placeholder"` + } + type Dataformat struct { + Language string `json:"language"` + Messages []TMsg `json:"messages"` + } + var outData Dataformat + err = json.NewDecoder(bytes.NewBuffer(b)).Decode(&outData) + if err != nil { + fmt.Println("cannot decode "+outpath+":", err) + return + } + + f, err := os.Open(path.Join(pathStart, "messages.gotext.json")) + if err != nil { + fmt.Println("cannot open "+path.Join(pathStart, "messages.gotext.json")+":", err) + return + } + defer func() { _ = f.Close() }() + + var msgData Dataformat + err = json.NewDecoder(f).Decode(&msgData) + if err != nil { + fmt.Println("cannot decode "+path.Join(pathStart, "messages.gotext.json")+":", err) + return + } + + knowns := map[string]string{} + for _, msg := range msgData.Messages { + knowns[msg.ID] = msg.Translation + } + + toTranslate := lo.Filter(outData.Messages, func(msg TMsg, _ int) bool { + _, ok := knowns[msg.ID] + return !ok + }) + + outData.Messages = toTranslate // drop the "done" messages + var outJSON bytes.Buffer + enc := json.NewEncoder(&outJSON) + enc.SetIndent(" ", " ") + err = enc.Encode(outData) + if err != nil { + fmt.Println("cannot encode "+outpath+":", err) + return + } + err = os.WriteFile(outpath, outJSON.Bytes(), 0644) + if err != nil { + fmt.Println("cannot write "+outpath+":", err) + return + } + fmt.Println("rearranged successfully") +} diff --git a/cmd/curio/internal/translations/locales/en/out.gotext.json b/cmd/curio/internal/translations/locales/en/out.gotext.json new file mode 100644 index 000000000..1cd3957d4 --- /dev/null +++ b/cmd/curio/internal/translations/locales/en/out.gotext.json @@ -0,0 +1,1104 @@ +{ + "language": "en", + "messages": [ + { + "id": "This interactive tool migrates lotus-miner to Curio in 5 minutes.", + "message": "This interactive tool migrates lotus-miner to Curio in 5 minutes.", + "translation": "This interactive tool migrates lotus-miner to Curio in 5 minutes.", + "translatorComment": "Copied from source.", + "fuzzy": true + }, + { + "id": "Each step needs your confirmation and can be reversed. Press Ctrl+C to exit at any time.", + "message": "Each step needs your confirmation and can be reversed. Press Ctrl+C to exit at any time.", + "translation": "Each step needs your confirmation and can be reversed. Press Ctrl+C to exit at any time.", + "translatorComment": "Copied from source.", + "fuzzy": true + }, + { + "id": "Use the arrow keys to navigate: ↓ ↑ → ←", + "message": "Use the arrow keys to navigate: ↓ ↑ → ←", + "translation": "Use the arrow keys to navigate: ↓ ↑ → ←", + "translatorComment": "Copied from source.", + "fuzzy": true + }, + { + "id": "Ctrl+C pressed in Terminal", + "message": "Ctrl+C pressed in Terminal", + "translation": "Ctrl+C pressed in Terminal", + "translatorComment": "Copied from source.", + "fuzzy": true + }, + { + "id": "Lotus-Miner to Curio Migration.", + "message": "Lotus-Miner to Curio Migration.", + "translation": "Lotus-Miner to Curio Migration.", + "translatorComment": "Copied from source.", + "fuzzy": true + }, + { + "id": "Try the web interface with {__layersgui} for further guided improvements.", + "message": "Try the web interface with {__layersgui} for further guided improvements.", + "translation": "Try the web interface with {__layersgui} for further guided improvements.", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "__layersgui", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "\"--layers=gui\"" + } + ], + "fuzzy": true + }, + { + "id": "You can now migrate your market node ({Boost}), if applicable.", + "message": "You can now migrate your market node ({Boost}), if applicable.", + "translation": "You can now migrate your market node ({Boost}), if applicable.", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Boost", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "\"Boost\"" + } + ], + "fuzzy": true + }, + { + "id": "Migrating config.toml to database.", + "message": "Migrating config.toml to database.", + "translation": "Migrating config.toml to database.", + "translatorComment": "Copied from source.", + "fuzzy": true + }, + { + "id": "Error reading from database: {Error}. Aborting Migration.", + "message": "Error reading from database: {Error}. Aborting Migration.", + "translation": "Error reading from database: {Error}. Aborting Migration.", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Error", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "err.Error()" + } + ], + "fuzzy": true + }, + { + "id": "Error connecting to lotus node: {Error} {Error_1}", + "message": "Error connecting to lotus node: {Error} {Error_1}", + "translation": "Error connecting to lotus node: {Error} {Error_1}", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Error", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "err.Error()" + }, + { + "id": "Error_1", + "string": "%[2]s", + "type": "string", + "underlyingType": "string", + "argNum": 2, + "expr": "err2.Error()" + } + ], + "fuzzy": true + }, + { + "id": "could not get API info for FullNode: {Err}", + "message": "could not get API info for FullNode: {Err}", + "translation": "could not get API info for FullNode: {Err}", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Err", + "string": "%[1]w", + "type": "error", + "underlyingType": "interface{Error() string}", + "argNum": 1, + "expr": "err" + } + ], + "fuzzy": true + }, + { + "id": "Error getting token: {Error}", + "message": "Error getting token: {Error}", + "translation": "Error getting token: {Error}", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Error", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "err.Error()" + } + ], + "fuzzy": true + }, + { + "id": "Error saving config to layer: {Error}. Aborting Migration", + "message": "Error saving config to layer: {Error}. Aborting Migration", + "translation": "Error saving config to layer: {Error}. Aborting Migration", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Error", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "err.Error()" + } + ], + "fuzzy": true + }, + { + "id": "Protocol Labs wants to improve the software you use. Tell the team you're using Curio.", + "message": "Protocol Labs wants to improve the software you use. Tell the team you're using Curio.", + "translation": "Protocol Labs wants to improve the software you use. Tell the team you're using Curio.", + "translatorComment": "Copied from source.", + "fuzzy": true + }, + { + "id": "Select what you want to share with the Curio team.", + "message": "Select what you want to share with the Curio team.", + "translation": "Select what you want to share with the Curio team.", + "translatorComment": "Copied from source.", + "fuzzy": true + }, + { + "id": "Individual Data: Miner ID, Curio version, net ({Mainnet} or {Testnet}). Signed.", + "message": "Individual Data: Miner ID, Curio version, net ({Mainnet} or {Testnet}). Signed.", + "translation": "Individual Data: Miner ID, Curio version, net ({Mainnet} or {Testnet}). Signed.", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Mainnet", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "\"mainnet\"" + }, + { + "id": "Testnet", + "string": "%[2]s", + "type": "string", + "underlyingType": "string", + "argNum": 2, + "expr": "\"testnet\"" + } + ], + "fuzzy": true + }, + { + "id": "Aggregate-Anonymous: version, net, and Miner power (bucketed).", + "message": "Aggregate-Anonymous: version, net, and Miner power (bucketed).", + "translation": "Aggregate-Anonymous: version, net, and Miner power (bucketed).", + "translatorComment": "Copied from source.", + "fuzzy": true + }, + { + "id": "Hint: I am someone running Curio on net.", + "message": "Hint: I am someone running Curio on net.", + "translation": "Hint: I am someone running Curio on net.", + "translatorComment": "Copied from source.", + "fuzzy": true + }, + { + "id": "Nothing.", + "message": "Nothing.", + "translation": "Nothing.", + "translatorComment": "Copied from source.", + "fuzzy": true + }, + { + "id": "Aborting remaining steps.", + "message": "Aborting remaining steps.", + "translation": "Aborting remaining steps.", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Error", + "string": "%[1]v", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "err.Error()" + } + ], + "fuzzy": true + }, + { + "id": "Error getting miner power: {Error}", + "message": "Error getting miner power: {Error}", + "translation": "Error getting miner power: {Error}", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Error", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "err.Error()" + } + ], + "fuzzy": true + }, + { + "id": "Error marshalling message: {Error}", + "message": "Error marshalling message: {Error}", + "translation": "Error marshalling message: {Error}", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Error", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "err.Error()" + } + ], + "fuzzy": true + }, + { + "id": "Error getting miner info: {Error}", + "message": "Error getting miner info: {Error}", + "translation": "Error getting miner info: {Error}", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Error", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "err.Error()" + } + ], + "fuzzy": true + }, + { + "id": "Error signing message: {Error}", + "message": "Error signing message: {Error}", + "translation": "Error signing message: {Error}", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Error", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "err.Error()" + } + ], + "fuzzy": true + }, + { + "id": "Error sending message: {Error}", + "message": "Error sending message: {Error}", + "translation": "Error sending message: {Error}", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Error", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "err.Error()" + } + ], + "fuzzy": true + }, + { + "id": "Error sending message: Status {Status}, Message:", + "message": "Error sending message: Status {Status}, Message:", + "translation": "Error sending message: Status {Status}, Message:", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Status", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "resp.Status" + }, + { + "id": "Stringb", + "string": "%[2]v", + "type": "string", + "underlyingType": "string", + "argNum": 2, + "expr": "string(b)" + } + ], + "fuzzy": true + }, + { + "id": "Message sent.", + "message": "Message sent.", + "translation": "Message sent.", + "translatorComment": "Copied from source.", + "fuzzy": true + }, + { + "id": "Documentation:", + "message": "Documentation:", + "translation": "Documentation:", + "translatorComment": "Copied from source.", + "fuzzy": true + }, + { + "id": "The '{Base}' layer stores common configuration. All curio instances can include it in their {__layers} argument.", + "message": "The '{Base}' layer stores common configuration. All curio instances can include it in their {__layers} argument.", + "translation": "The '{Base}' layer stores common configuration. All curio instances can include it in their {__layers} argument.", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Base", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "\"base\"" + }, + { + "id": "__layers", + "string": "%[2]s", + "type": "string", + "underlyingType": "string", + "argNum": 2, + "expr": "\"--layers\"" + } + ], + "fuzzy": true + }, + { + "id": "You can add other layers for per-machine configuration changes.", + "message": "You can add other layers for per-machine configuration changes.", + "translation": "You can add other layers for per-machine configuration changes.", + "translatorComment": "Copied from source.", + "fuzzy": true + }, + { + "id": "Filecoin {Slack} channels: {Fil_curio_help} and {Fil_curio_dev}", + "message": "Filecoin {Slack} channels: {Fil_curio_help} and {Fil_curio_dev}", + "translation": "Filecoin {Slack} channels: {Fil_curio_help} and {Fil_curio_dev}", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Slack", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "\"Slack\"" + }, + { + "id": "Fil_curio_help", + "string": "%[2]s", + "type": "string", + "underlyingType": "string", + "argNum": 2, + "expr": "\"#fil-curio-help\"" + }, + { + "id": "Fil_curio_dev", + "string": "%[3]s", + "type": "string", + "underlyingType": "string", + "argNum": 3, + "expr": "\"#fil-curio-dev\"" + } + ], + "fuzzy": true + }, + { + "id": "Start multiple Curio instances with the '{Post}' layer to redundancy.", + "message": "Start multiple Curio instances with the '{Post}' layer to redundancy.", + "translation": "Start multiple Curio instances with the '{Post}' layer to redundancy.", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Post", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "\"post\"" + } + ], + "fuzzy": true + }, + { + "id": "Point your browser to your web GUI to complete setup with {Boost} and advanced featues.", + "message": "Point your browser to your web GUI to complete setup with {Boost} and advanced featues.", + "translation": "Point your browser to your web GUI to complete setup with {Boost} and advanced featues.", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Boost", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "\"Boost\"" + } + ], + "fuzzy": true + }, + { + "id": "One database can serve multiple miner IDs: Run a migration for each lotus-miner.", + "message": "One database can serve multiple miner IDs: Run a migration for each lotus-miner.", + "translation": "One database can serve multiple miner IDs: Run a migration for each lotus-miner.", + "translatorComment": "Copied from source.", + "fuzzy": true + }, + { + "id": "Please start (or restart) {Lotus_miner} now that database credentials are in {Toml}.", + "message": "Please start (or restart) {Lotus_miner} now that database credentials are in {Toml}.", + "translation": "Please start (or restart) {Lotus_miner} now that database credentials are in {Toml}.", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Lotus_miner", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "\"lotus-miner\"" + }, + { + "id": "Toml", + "string": "%[2]s", + "type": "string", + "underlyingType": "string", + "argNum": 2, + "expr": "\"config.toml\"" + } + ], + "fuzzy": true + }, + { + "id": "Waiting for {Lotus_miner} to write sectors into Yugabyte.", + "message": "Waiting for {Lotus_miner} to write sectors into Yugabyte.", + "translation": "Waiting for {Lotus_miner} to write sectors into Yugabyte.", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Lotus_miner", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "\"lotus-miner\"" + } + ], + "fuzzy": true + }, + { + "id": "Error interpreting miner ID: {Error}: ID: {String}", + "message": "Error interpreting miner ID: {Error}: ID: {String}", + "translation": "Error interpreting miner ID: {Error}: ID: {String}", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Error", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "err.Error()" + }, + { + "id": "String", + "string": "%[2]s", + "type": "string", + "underlyingType": "string", + "argNum": 2, + "expr": "d.MinerID.String()" + } + ], + "fuzzy": true + }, + { + "id": "Error verifying sectors: {Error}", + "message": "Error verifying sectors: {Error}", + "translation": "Error verifying sectors: {Error}", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Error", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "err.Error()" + } + ], + "fuzzy": true + }, + { + "id": "The sectors are in the database. The database is ready for {Curio}.", + "message": "The sectors are in the database. The database is ready for {Curio}.", + "translation": "The sectors are in the database. The database is ready for {Curio}.", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Curio", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "\"Curio\"" + } + ], + "fuzzy": true + }, + { + "id": "Now shut down lotus-miner and move the systems to {Curio}.", + "message": "Now shut down lotus-miner and move the systems to {Curio}.", + "translation": "Now shut down lotus-miner and move the systems to {Curio}.", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Curio", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "\"Curio\"" + } + ], + "fuzzy": true + }, + { + "id": "Press return to continue", + "message": "Press return to continue", + "translation": "Press return to continue", + "translatorComment": "Copied from source.", + "fuzzy": true + }, + { + "id": "Aborting migration.", + "message": "Aborting migration.", + "translation": "Aborting migration.", + "translatorComment": "Copied from source.", + "fuzzy": true + }, + { + "id": "Sectors verified. {I} sector locations found.", + "message": "Sectors verified. {I} sector locations found.", + "translation": "Sectors verified. {I} sector locations found.", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "I", + "string": "%[1]d", + "type": "[]int", + "underlyingType": "[]int", + "argNum": 1, + "expr": "i" + } + ], + "fuzzy": true + }, + { + "id": "Enter the info to connect to your Yugabyte database installation (https://download.yugabyte.com/)", + "message": "Enter the info to connect to your Yugabyte database installation (https://download.yugabyte.com/)", + "translation": "Enter the info to connect to your Yugabyte database installation (https://download.yugabyte.com/)", + "translatorComment": "Copied from source.", + "fuzzy": true + }, + { + "id": "Host: {Hosts_}", + "message": "Host: {Hosts_}", + "translation": "Host: {Hosts_}", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Hosts_", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "strings.Join(harmonyCfg.Hosts, \",\")" + } + ], + "fuzzy": true + }, + { + "id": "Port: {Port}", + "message": "Port: {Port}", + "translation": "Port: {Port}", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Port", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "harmonyCfg.Port" + } + ], + "fuzzy": true + }, + { + "id": "Username: {Username}", + "message": "Username: {Username}", + "translation": "Username: {Username}", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Username", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "harmonyCfg.Username" + } + ], + "fuzzy": true + }, + { + "id": "Password: {Password}", + "message": "Password: {Password}", + "translation": "Password: {Password}", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Password", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "harmonyCfg.Password" + } + ], + "fuzzy": true + }, + { + "id": "Database: {Database}", + "message": "Database: {Database}", + "translation": "Database: {Database}", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Database", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "harmonyCfg.Database" + } + ], + "fuzzy": true + }, + { + "id": "Continue to connect and update schema.", + "message": "Continue to connect and update schema.", + "translation": "Continue to connect and update schema.", + "translatorComment": "Copied from source.", + "fuzzy": true + }, + { + "id": "Database config error occurred, abandoning migration: {Error}", + "message": "Database config error occurred, abandoning migration: {Error}", + "translation": "Database config error occurred, abandoning migration: {Error}", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Error", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "err.Error()" + } + ], + "fuzzy": true + }, + { + "id": "Enter the Yugabyte database host(s)", + "message": "Enter the Yugabyte database host(s)", + "translation": "Enter the Yugabyte database host(s)", + "translatorComment": "Copied from source.", + "fuzzy": true + }, + { + "id": "No host provided", + "message": "No host provided", + "translation": "No host provided", + "translatorComment": "Copied from source.", + "fuzzy": true + }, + { + "id": "Enter the Yugabyte database {Stringport_username_password_databasei_1}", + "message": "Enter the Yugabyte database {Stringport_username_password_databasei_1}", + "translation": "Enter the Yugabyte database {Stringport_username_password_databasei_1}", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Stringport_username_password_databasei_1", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "[]string{\"port\", \"username\", \"password\", \"database\"}[i-1]" + } + ], + "fuzzy": true + }, + { + "id": "No value provided", + "message": "No value provided", + "translation": "No value provided", + "translatorComment": "Copied from source.", + "fuzzy": true + }, + { + "id": "Error connecting to Yugabyte database: {Error}", + "message": "Error connecting to Yugabyte database: {Error}", + "translation": "Error connecting to Yugabyte database: {Error}", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Error", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "err.Error()" + } + ], + "fuzzy": true + }, + { + "id": "Connected to Yugabyte. Schema is current.", + "message": "Connected to Yugabyte. Schema is current.", + "translation": "Connected to Yugabyte. Schema is current.", + "translatorComment": "Copied from source.", + "fuzzy": true + }, + { + "id": "Enabling Sector Indexing in the database.", + "message": "Enabling Sector Indexing in the database.", + "translation": "Enabling Sector Indexing in the database.", + "translatorComment": "Copied from source.", + "fuzzy": true + }, + { + "id": "Error encoding config.toml: {Error}", + "message": "Error encoding config.toml: {Error}", + "translation": "Error encoding config.toml: {Error}", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Error", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "err.Error()" + } + ], + "fuzzy": true + }, + { + "id": "Press return to update {Toml} with Yugabyte info. Backup the file now.", + "message": "Press return to update {Toml} with Yugabyte info. Backup the file now.", + "translation": "Press return to update {Toml} with Yugabyte info. Backup the file now.", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Toml", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "\"config.toml\"" + } + ], + "fuzzy": true + }, + { + "id": "Error expanding path: {Error}", + "message": "Error expanding path: {Error}", + "translation": "Error expanding path: {Error}", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Error", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "err.Error()" + } + ], + "fuzzy": true + }, + { + "id": "Error reading filemode of config.toml: {Error}", + "message": "Error reading filemode of config.toml: {Error}", + "translation": "Error reading filemode of config.toml: {Error}", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Error", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "err.Error()" + } + ], + "fuzzy": true + }, + { + "id": "Error writing config.toml: {Error}", + "message": "Error writing config.toml: {Error}", + "translation": "Error writing config.toml: {Error}", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Error", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "err.Error()" + } + ], + "fuzzy": true + }, + { + "id": "Restart Lotus Miner.", + "message": "Restart Lotus Miner.", + "translation": "Restart Lotus Miner.", + "translatorComment": "Copied from source.", + "fuzzy": true + }, + { + "id": "Connected to Yugabyte", + "message": "Connected to Yugabyte", + "translation": "Connected to Yugabyte", + "translatorComment": "Copied from source.", + "fuzzy": true + }, + { + "id": "To start, ensure your sealing pipeline is drained and shut-down lotus-miner.", + "message": "To start, ensure your sealing pipeline is drained and shut-down lotus-miner.", + "translation": "To start, ensure your sealing pipeline is drained and shut-down lotus-miner.", + "translatorComment": "Copied from source.", + "fuzzy": true + }, + { + "id": "Select the location of your lotus-miner config directory?", + "message": "Select the location of your lotus-miner config directory?", + "translation": "Select the location of your lotus-miner config directory?", + "translatorComment": "Copied from source.", + "fuzzy": true + }, + { + "id": "Other", + "message": "Other", + "translation": "Other", + "translatorComment": "Copied from source.", + "fuzzy": true + }, + { + "id": "Enter the path to the configuration directory used by {Lotus_miner}", + "message": "Enter the path to the configuration directory used by {Lotus_miner}", + "translation": "Enter the path to the configuration directory used by {Lotus_miner}", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Lotus_miner", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "\"lotus-miner\"" + } + ], + "fuzzy": true + }, + { + "id": "No path provided, abandoning migration", + "message": "No path provided, abandoning migration", + "translation": "No path provided, abandoning migration", + "translatorComment": "Copied from source.", + "fuzzy": true + }, + { + "id": "Cannot read the config.toml file in the provided directory, Error: {Error}", + "message": "Cannot read the config.toml file in the provided directory, Error: {Error}", + "translation": "Cannot read the config.toml file in the provided directory, Error: {Error}", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Error", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "err.Error()" + } + ], + "fuzzy": true + }, + { + "id": "Could not create repo from directory: {Error}. Aborting migration", + "message": "Could not create repo from directory: {Error}. Aborting migration", + "translation": "Could not create repo from directory: {Error}. Aborting migration", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Error", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "err.Error()" + } + ], + "fuzzy": true + }, + { + "id": "Could not lock miner repo. Your miner must be stopped: {Error}\n Aborting migration", + "message": "Could not lock miner repo. Your miner must be stopped: {Error}\n Aborting migration", + "translation": "Could not lock miner repo. Your miner must be stopped: {Error}\n Aborting migration", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Error", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "err.Error()" + } + ], + "fuzzy": true + }, + { + "id": "Read Miner Config", + "message": "Read Miner Config", + "translation": "Read Miner Config", + "translatorComment": "Copied from source.", + "fuzzy": true + }, + { + "id": "Step Complete: {Step}", + "message": "Step Complete: {Step}", + "translation": "Step Complete: {Step}", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Step", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "step" + } + ], + "fuzzy": true + }, + { + "id": "Configuration 'base' was updated to include this miner's address and its wallet setup.", + "message": "Configuration 'base' was updated to include this miner's address and its wallet setup.", + "translation": "Configuration 'base' was updated to include this miner's address and its wallet setup.", + "translatorComment": "Copied from source.", + "fuzzy": true + }, + { + "id": "Compare the configurations {Base} to {MinerAddresses0}. Changes between the miner IDs other than wallet addreses should be a new, minimal layer for runners that need it.", + "message": "Compare the configurations {Base} to {MinerAddresses0}. Changes between the miner IDs other than wallet addreses should be a new, minimal layer for runners that need it.", + "translation": "Compare the configurations {Base} to {MinerAddresses0}. Changes between the miner IDs other than wallet addreses should be a new, minimal layer for runners that need it.", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Base", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "\"base\"" + }, + { + "id": "MinerAddresses0", + "string": "%[2]s", + "type": "string", + "underlyingType": "string", + "argNum": 2, + "expr": "\"mig-\" + curioCfg.Addresses[0].MinerAddresses[0]" + } + ], + "fuzzy": true + }, + { + "id": "Configuration 'base' was created to include this miner's address and its wallet setup.", + "message": "Configuration 'base' was created to include this miner's address and its wallet setup.", + "translation": "Configuration 'base' was created to include this miner's address and its wallet setup.", + "translatorComment": "Copied from source.", + "fuzzy": true + }, + { + "id": "Layer {LayerName} created.", + "message": "Layer {LayerName} created.", + "translation": "Layer {LayerName} created.", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "LayerName", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "layerName" + } + ], + "fuzzy": true + }, + { + "id": "To work with the config:", + "message": "To work with the config:", + "translation": "To work with the config:", + "translatorComment": "Copied from source.", + "fuzzy": true + }, + { + "id": "To run Curio: With machine or cgroup isolation, use the command (with example layer selection):", + "message": "To run Curio: With machine or cgroup isolation, use the command (with example layer selection):", + "translation": "To run Curio: With machine or cgroup isolation, use the command (with example layer selection):", + "translatorComment": "Copied from source.", + "fuzzy": true + } + ] +} \ No newline at end of file diff --git a/cmd/curio/internal/translations/locales/ko/messages.gotext.json b/cmd/curio/internal/translations/locales/ko/messages.gotext.json new file mode 100644 index 000000000..ab0f8c6af --- /dev/null +++ b/cmd/curio/internal/translations/locales/ko/messages.gotext.json @@ -0,0 +1,704 @@ +{ + "language": "ko", + "messages": [ + { + "id": "This interactive tool will walk you through migration of Curio.\nPress Ctrl+C to exit at any time.", + "message": "This interactive tool will walk you through migration of Curio.\nPress Ctrl+C to exit at any time.", + "translation": "이 대화형 도구는 Curio 마이그레이션 과정을 안내합니다.\n언제든지 종료하려면 Ctrl+C를 누르십시오." + }, + { + "id": "This tool confirms each action it does.", + "message": "This tool confirms each action it does.", + "translation": "이 도구는 수행하는 각 작업을 확인합니다." + }, + { + "id": "Ctrl+C pressed in Terminal", + "message": "Ctrl+C pressed in Terminal", + "translation": "터미널에서 Ctrl+C가 눌림" + }, + { + "id": "Verifying Sectors exist in Yugabyte.", + "message": "Verifying Sectors exist in Yugabyte.", + "translation": "Yugabyte에 섹터가 존재하는지 확인 중." + }, + { + "id": "Error verifying sectors: {Error}", + "message": "Error verifying sectors: {Error}", + "translation": "섹터 확인 중 오류 발생: {Error}", + "placeholders": [ + { + "id": "Error", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "err.Error()" + } + ] + }, + { + "id": "Sectors verified. {I} sectors found.", + "message": "Sectors verified. {I} sectors found.", + "translation": "섹터가 확인되었습니다. {I}개의 섹터가 발견되었습니다.", + "placeholders": [ + { + "id": "I", + "string": "%[1]d", + "type": "[]int", + "underlyingType": "[]int", + "argNum": 1, + "expr": "i" + } + ] + }, + { + "id": "Never remove the database info from the config.toml for lotus-miner as it avoids double PoSt.", + "message": "Never remove the database info from the config.toml for lotus-miner as it avoids double PoSt.", + "translation": "로터스 마이너의 config.toml에서 데이터베이스 정보를 제거하지 마십시오. 두 번의 PoSt를 피하기 위함입니다." + }, + { + "id": "Enter the info to connect to your Yugabyte database installation (https://download.yugabyte.com/)", + "message": "Enter the info to connect to your Yugabyte database installation (https://download.yugabyte.com/)", + "translation": "Yugabyte 데이터베이스 설치에 연결할 정보를 입력하십시오 (https://download.yugabyte.com/)" + }, + { + "id": "Host: {Hosts_}", + "message": "Host: {Hosts_}", + "translation": "호스트: {Hosts_}", + "placeholders": [ + { + "id": "Hosts_", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "strings.Join(harmonycfg.Hosts, \",\")" + } + ] + }, + { + "id": "Port: {Port}", + "message": "Port: {Port}", + "translation": "포트: {Port}", + "placeholders": [ + { + "id": "Port", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "harmonycfg.Port" + } + ] + }, + { + "id": "Username: {Username}", + "message": "Username: {Username}", + "translation": "사용자 이름: {Username}", + "placeholders": [ + { + "id": "Username", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "harmonycfg.Username" + } + ] + }, + { + "id": "Password: {Password}", + "message": "Password: {Password}", + "translation": "비밀번호: {Password}", + "placeholders": [ + { + "id": "Password", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "harmonycfg.Password" + } + ] + }, + { + "id": "Database: {Database}", + "message": "Database: {Database}", + "translation": "데이터베이스: {Database}", + "placeholders": [ + { + "id": "Database", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "harmonycfg.Database" + } + ] + }, + { + "id": "Continue to connect and update schema.", + "message": "Continue to connect and update schema.", + "translation": "계속 연결 및 스키마 업데이트." + }, + { + "id": "Database config error occurred, abandoning migration: {Error}", + "message": "Database config error occurred, abandoning migration: {Error}", + "translation": "데이터베이스 구성 오류가 발생하여 마이그레이션을 포기합니다: {Error}", + "placeholders": [ + { + "id": "Error", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "err.Error()" + } + ] + }, + { + "id": "Enter the Yugabyte database host(s)", + "message": "Enter the Yugabyte database host(s)", + "translation": "Yugabyte 데이터베이스 호스트를 입력하십시오" + }, + { + "id": "No host provided", + "message": "No host provided", + "translation": "호스트가 제공되지 않았습니다" + }, + { + "id": "Enter the Yugabyte database {Stringport_username_password_databasei_1}", + "message": "Enter the Yugabyte database {Stringport_username_password_databasei_1}", + "translation": "Yugabyte 데이터베이스 {Stringport_username_password_databasei_1}을 입력하십시오", + "placeholders": [ + { + "id": "Stringport_username_password_databasei_1", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "[]string{\"port\", \"username\", \"password\", \"database\"}[i-1]" + } + ] + }, + { + "id": "No value provided", + "message": "No value provided", + "translation": "값이 제공되지 않았습니다" + }, + { + "id": "Error connecting to Yugabyte database: {Error}", + "message": "Error connecting to Yugabyte database: {Error}", + "translation": "Yugabyte 데이터베이스에 연결하는 중 오류가 발생했습니다: {Error}", + "placeholders": [ + { + "id": "Error", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "err.Error()" + } + ] + }, + { + "id": "Connected to Yugabyte. Schema is current.", + "message": "Connected to Yugabyte. Schema is current.", + "translation": "Yugabyte에 연결되었습니다. 스키마가 현재입니다." + }, + { + "id": "Error encoding config.toml: {Error}", + "message": "Error encoding config.toml: {Error}", + "translation": "config.toml을 인코딩하는 중 오류가 발생했습니다: {Error}", + "placeholders": [ + { + "id": "Error", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "err.Error()" + } + ] + }, + { + "id": "Error reading filemode of config.toml: {Error}", + "message": "Error reading filemode of config.toml: {Error}", + "translation": "config.toml의 파일 모드를 읽는 중 오류가 발생했습니다: {Error}", + "placeholders": [ + { + "id": "Error", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "err.Error()" + } + ] + }, + { + "id": "Error writing config.toml: {Error}", + "message": "Error writing config.toml: {Error}", + "translation": "config.toml을 쓰는 중 오류가 발생했습니다: {Error}", + "placeholders": [ + { + "id": "Error", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "err.Error()" + } + ] + }, + { + "id": "Restart Lotus Miner.", + "message": "Restart Lotus Miner.", + "translation": "로터스 마이너 재시작." + }, + { + "id": "Connected to Yugabyte", + "message": "Connected to Yugabyte", + "translation": "Yugabyte에 연결됨" + }, + { + "id": "Select the location of your lotus-miner config directory?", + "message": "Select the location of your lotus-miner config directory?", + "translation": "로터스 마이너 구성 디렉토리의 위치를 선택하시겠습니까?" + }, + { + "id": "Other", + "message": "Other", + "translation": "기타" + }, + { + "id": "Enter the path to the configuration directory used by lotus-miner", + "message": "Enter the path to the configuration directory used by lotus-miner", + "translation": "로터스 마이너에서 사용하는 구성 디렉토리의 경로를 입력하십시오" + }, + { + "id": "No path provided, abandoning migration", + "message": "No path provided, abandoning migration", + "translation": "경로가 제공되지 않았으므로 마이그레이션을 포기합니다" + }, + { + "id": "Cannot read the config.toml file in the provided directory, Error: {Error}", + "message": "Cannot read the config.toml file in the provided directory, Error: {Error}", + "translation": "제공된 디렉토리에서 config.toml 파일을 읽을 수 없습니다. 오류: {Error}", + "placeholders": [ + { + "id": "Error", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "err.Error()" + } + ] + }, + { + "id": "Read Miner Config", + "message": "Read Miner Config", + "translation": "마이너 구성 읽기" + }, + { + "id": "Completed Step: {Step}", + "message": "Completed Step: {Step}", + "translation": "단계 완료: {Step}", + "placeholders": [ + { + "id": "Step", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "step" + } + ] + }, + { + "id": "This interactive tool migrates lotus-miner to Curio in 5 minutes.", + "translation": "이 대화형 도구는 5분 안에 lotus-miner를 Curio로 이주합니다.", + "message": "This interactive tool migrates lotus-miner to Curio in 5 minutes.", + "placeholder": null + }, + { + "id": "Each step needs your confirmation and can be reversed. Press Ctrl+C to exit at any time.", + "translation": "각 단계는 확인이 필요하며 되돌릴 수 있습니다. 언제든지 Ctrl+C를 눌러 종료할 수 있습니다.", + "message": "Each step needs your confirmation and can be reversed. Press Ctrl+C to exit at any time.", + "placeholder": null + }, + { + "id": "Use the arrow keys to navigate: ↓ ↑ → ←", + "translation": "화살표 키를 사용하여 이동하세요: ↓ ↑ → ←", + "message": "Use the arrow keys to navigate: ↓ ↑ → ←", + "placeholder": null + }, + { + "id": "Lotus-Miner to Curio Migration.", + "translation": "Lotus-Miner에서 Curio로 이주.", + "message": "Lotus-Miner to Curio Migration.", + "placeholder": null + }, + { + "id": "Try the web interface with for further guided improvements.", + "translation": "더 나은 안내를 위해 웹 인터페이스를 사용해보세요.", + "message": "Try the web interface with for further guided improvements.", + "placeholder": null + }, + { + "id": "You can now migrate your market node ({Boost}), if applicable.", + "translation": "해당하는 경우 이제 시장 노드를 이주할 수 있습니다 ({Boost}).", + "message": "You can now migrate your market node ({Boost}), if applicable.", + "placeholder": null + }, + { + "id": "Migrating config.toml to database.", + "translation": "config.toml을 데이터베이스로 이주 중입니다.", + "message": "Migrating config.toml to database.", + "placeholder": null + }, + { + "id": "Error reading from database: {Error}. Aborting Migration.", + "translation": "데이터베이스에서 읽는 중 오류 발생: {Error}. 마이그레이션 중단.", + "message": "Error reading from database: {Error}. Aborting Migration.", + "placeholder": null + }, + { + "id": "cannot read API: {Error}. Aborting Migration", + "translation": "API를 읽을 수 없습니다: {Error}. 마이그레이션 중단", + "message": "cannot read API: {Error}. Aborting Migration", + "placeholder": null + }, + { + "id": "Error saving config to layer: {Error}. Aborting Migration", + "translation": "레이어에 구성을 저장하는 중 오류 발생: {Error}. 마이그레이션 중단", + "message": "Error saving config to layer: {Error}. Aborting Migration", + "placeholder": null + }, + { + "id": "Protocol Labs wants to improve the software you use. Tell the team you're using Curio.", + "translation": "Protocol Labs는 당신이 사용하는 소프트웨어를 개선하고 싶어합니다. Curio를 사용 중이라고 팀에 알려주세요.", + "message": "Protocol Labs wants to improve the software you use. Tell the team you're using Curio.", + "placeholder": null + }, + { + "id": "Select what you want to share with the Curio team.", + "translation": "Curio 팀과 공유하고 싶은 것을 선택하세요.", + "message": "Select what you want to share with the Curio team.", + "placeholder": null + }, + { + "id": "Individual Data: Miner ID, Curio version, net ({Mainnet} or {Testnet}). Signed.", + "translation": "개별 데이터: 마이너 ID, Curio 버전, 네트워크 ({Mainnet} 또는 {Testnet}). 서명됨.", + "message": "Individual Data: Miner ID, Curio version, net ({Mainnet} or {Testnet}). Signed.", + "placeholder": null + }, + { + "id": "Aggregate-Anonymous: version, net, and Miner power (bucketed).", + "translation": "집계-익명: 버전, 네트워크, 그리고 마이너 파워 (버킷).", + "message": "Aggregate-Anonymous: version, net, and Miner power (bucketed).", + "placeholder": null + }, + { + "id": "Hint: I am someone running Curio on net.", + "translation": "힌트: 네트워크에서 Curio를 실행 중인 사람입니다.", + "message": "Hint: I am someone running Curio on net.", + "placeholder": null + }, + { + "id": "Nothing.", + "translation": "아무것도 없습니다.", + "message": "Nothing.", + "placeholder": null + }, + { + "id": "Aborting remaining steps.", + "translation": "나머지 단계를 중단합니다.", + "message": "Aborting remaining steps.", + "placeholder": null + }, + { + "id": "Error connecting to lotus node: {Error}", + "translation": "로터스 노드에 연결하는 중 오류 발생: {Error}", + "message": "Error connecting to lotus node: {Error}", + "placeholder": null + }, + { + "id": "Error getting miner power: {Error}", + "translation": "마이너 파워를 가져오는 중 오류 발생: {Error}", + "message": "Error getting miner power: {Error}", + "placeholder": null + }, + { + "id": "Error marshalling message: {Error}", + "translation": "메시지를 마샬하는 중 오류 발생: {Error}", + "message": "Error marshalling message: {Error}", + "placeholder": null + }, + { + "id": "Error getting miner info: {Error}", + "translation": "마이너 정보를 가져오는 중 오류 발생: {Error}", + "message": "Error getting miner info: {Error}", + "placeholder": null + }, + { + "id": "Error signing message: {Error}", + "translation": "메시지 서명 중 오류 발생: {Error}", + "message": "Error signing message: {Error}", + "placeholder": null + }, + { + "id": "Error sending message: {Error}", + "translation": "메시지 전송 중 오류 발생: {Error}", + "message": "Error sending message: {Error}", + "placeholder": null + }, + { + "id": "Error sending message: Status {Status}, Message:", + "translation": "메시지 전송 중 오류 발생: 상태 {Status}, 메시지:", + "message": "Error sending message: Status {Status}, Message:", + "placeholder": null + }, + { + "id": "Message sent.", + "translation": "메시지가 전송되었습니다.", + "message": "Message sent.", + "placeholder": null + }, + { + "id": "Documentation:", + "translation": "문서:", + "message": "Documentation:", + "placeholder": null + }, + { + "id": "The '{Base}' layer stores common configuration. All curio instances can include it in their {__layers} argument.", + "translation": "'{Base}' 레이어에는 공통 구성이 저장됩니다. 모든 Curio 인스턴스는 {__layers} 인수에 포함시킬 수 있습니다.", + "message": "The '{Base}' layer stores common configuration. All curio instances can include it in their {__layers} argument.", + "placeholder": null + }, + { + "id": "You can add other layers for per-machine configuration changes.", + "translation": "기계별 구성 변경을 위해 다른 레이어를 추가할 수 있습니다.", + "message": "You can add other layers for per-machine configuration changes.", + "placeholder": null + }, + { + "id": "Join {Fil_curio_help} in Filecoin {Slack} for help.", + "translation": "도움을 위해 Filecoin {Slack}의 {Fil_curio_help}에 가입하세요.", + "message": "Join {Fil_curio_help} in Filecoin {Slack} for help.", + "placeholder": null + }, + { + "id": "Join {Fil_curio_dev} in Filecoin {Slack} to follow development and feedback!", + "translation": "개발과 피드백을 따르려면 Filecoin {Slack}의 {Fil_curio_dev}에 가입하세요!", + "message": "Join {Fil_curio_dev} in Filecoin {Slack} to follow development and feedback!", + "placeholder": null + }, + { + "id": "Want PoST redundancy? Run many Curio instances with the '{Post}' layer.", + "translation": "PoST 중복성이 필요하신가요? '{Post}' 레이어와 함께 여러 Curio 인스턴스를 실행하세요.", + "message": "Want PoST redundancy? Run many Curio instances with the '{Post}' layer.", + "placeholder": null + }, + { + "id": "Point your browser to your web GUI to complete setup with {Boost} and advanced featues.", + "translation": "브라우저를 웹 GUI로 이동하여 {Boost} 및 고급 기능으로 설정을 완료하세요.", + "message": "Point your browser to your web GUI to complete setup with {Boost} and advanced featues.", + "placeholder": null + }, + { + "id": "For SPs with multiple Miner IDs, run 1 migration per lotus-miner all to the same 1 database. The cluster will serve all Miner IDs.", + "translation": "여러 마이너 ID가 있는 SP의 경우 각 lotus-miner당 1회 마이그레이션을 동일한 1개의 데이터베이스로 모두 실행하세요. 클러스터는 모든 마이너 ID를 제공합니다.", + "message": "For SPs with multiple Miner IDs, run 1 migration per lotus-miner all to the same 1 database. The cluster will serve all Miner IDs.", + "placeholder": null + }, + { + "id": "Please start {Lotus_miner} now that database credentials are in {Toml}.", + "translation": "데이터베이스 자격 증명이 {Toml}에 있으므로 이제 {Lotus_miner}를 시작하세요.", + "message": "Please start {Lotus_miner} now that database credentials are in {Toml}.", + "placeholder": null + }, + { + "id": "Waiting for {Lotus_miner} to write sectors into Yugabyte.", + "translation": "{Lotus_miner}가 Yugabyte에 섹터를 기록하도록 대기 중입니다.", + "message": "Waiting for {Lotus_miner} to write sectors into Yugabyte.", + "placeholder": null + }, + { + "id": "The sectors are in the database. The database is ready for {Curio}.", + "translation": "섹터가 데이터베이스에 있습니다. 데이터베이스가 {Curio}를 위해 준비되었습니다.", + "message": "The sectors are in the database. The database is ready for {Curio}.", + "placeholder": null + }, + { + "id": "Now shut down lotus-miner and move the systems to {Curio}.", + "translation": "이제 lotus-miner를 종료하고 시스템을 {Curio}로 이동하세요.", + "message": "Now shut down lotus-miner and move the systems to {Curio}.", + "placeholder": null + }, + { + "id": "Press return to continue", + "translation": "계속하려면 리턴을 누르세요", + "message": "Press return to continue", + "placeholder": null + }, + { + "id": "Aborting migration.", + "translation": "마이그레이션 중단.", + "message": "Aborting migration.", + "placeholder": null + }, + { + "id": "Sectors verified. {I} sector locations found.", + "translation": "섹터가 확인되었습니다. {I}개의 섹터 위치를 찾았습니다.", + "message": "Sectors verified. {I} sector locations found.", + "placeholder": null + }, + { + "id": "Press return to update {Toml} with Yugabyte info. Backup the file now.", + "translation": "{Toml}을 Yugabyte 정보로 업데이트하려면 리턴을 누르세요. 지금 파일을 백업하세요.", + "message": "Press return to update {Toml} with Yugabyte info. Backup the file now.", + "placeholder": null + }, + { + "id": "To start, ensure your sealing pipeline is drained and shut-down lotus-miner.", + "translation": "시작하려면 밀봉 파이프라인이 비어 있고 lotus-miner가 종료되었는지 확인하세요.", + "message": "To start, ensure your sealing pipeline is drained and shut-down lotus-miner.", + "placeholder": null + }, + { + "id": "Enter the path to the configuration directory used by {Lotus_miner}", + "translation": "{Lotus_miner}에서 사용하는 구성 디렉터리 경로를 입력하세요.", + "message": "Enter the path to the configuration directory used by {Lotus_miner}", + "placeholder": null + }, + { + "id": "Step Complete: {Step}", + "translation": "단계 완료: {Step}", + "message": "Step Complete: {Step}", + "placeholder": null + }, + { + "id": "Configuration 'base' was updated to include this miner's address and its wallet setup.", + "translation": "이 마이너의 주소와 지갑 설정을 포함하도록 구성 'base'가 업데이트되었습니다.", + "message": "Configuration 'base' was updated to include this miner's address and its wallet setup.", + "placeholder": null + }, + { + "id": "Compare the configurations {Base} to {MinerAddresses0}. Changes between the miner IDs other than wallet addreses should be a new, minimal layer for runners that need it.", + "translation": "구성 {Base}를 {MinerAddresses0}과 비교하세요. 지갑 주소 이외의 마이너 ID 사이의 변경 사항은 필요한 실행자를 위한 새로운 최소한의 레이어여야 합니다.", + "message": "Compare the configurations {Base} to {MinerAddresses0}. Changes between the miner IDs other than wallet addreses should be a new, minimal layer for runners that need it.", + "placeholder": null + }, + { + "id": "Configuration 'base' was created to include this miner's address and its wallet setup.", + "translation": "이 마이너의 주소와 지갑 설정을 포함하도록 구성 'base'가 생성되었습니다.", + "message": "Configuration 'base' was created to include this miner's address and its wallet setup.", + "placeholder": null + }, + { + "id": "Layer {LayerName} created.", + "translation": "레이어 {LayerName}가 생성되었습니다.", + "message": "Layer {LayerName} created.", + "placeholder": null + }, + { + "id": "To work with the config: \\n", + "translation": "구성을 사용하려면: \\n", + "message": "To work with the config: \\n", + "placeholder": null + }, + { + "id": "To run Curio: With machine or cgroup isolation, use the command (with example layer selection):", + "translation": "Curio를 실행하려면: 기계 또는 cgroup 격리를 사용하여 다음 명령을 사용하세요 (예제 레이어 선택과 함께):", + "message": "To run Curio: With machine or cgroup isolation, use the command (with example layer selection):", + "placeholder": null + }, + { + "id": "Try the web interface with {__layersgui} for further guided improvements.", + "translation": "더 많은 안내를 위해 {__layersgui}를 사용하여 웹 인터페이스를 시도하세요.", + "message": "Try the web interface with {__layersgui} for further guided improvements.", + "placeholder": null + }, + { + "id": "Error connecting to lotus node: {Error} {Error_1}", + "translation": "lotus 노드에 연결하는 중 오류 발생: {Error} {Error_1}", + "message": "Error connecting to lotus node: {Error} {Error_1}", + "placeholder": null + }, + { + "id": "could not get API info for FullNode: {Err}", + "translation": "FullNode의 API 정보를 가져올 수 없습니다: {Err}", + "message": "could not get API info for FullNode: {Err}", + "placeholder": null + }, + { + "id": "Error getting token: {Error}", + "translation": "토큰을 가져오는 중 오류 발생: {Error}", + "message": "Error getting token: {Error}", + "placeholder": null + }, + { + "id": "Filecoin {Slack} channels: {Fil_curio_help} and {Fil_curio_dev}", + "translation": "Filecoin {Slack} 채널: {Fil_curio_help} 및 {Fil_curio_dev}", + "message": "Filecoin {Slack} channels: {Fil_curio_help} and {Fil_curio_dev}", + "placeholder": null + }, + { + "id": "Start multiple Curio instances with the '{Post}' layer to redundancy.", + "translation": "'{Post}' 레이어로 여러 Curio 인스턴스를 시작하여 중복성을 확보하세요.", + "message": "Start multiple Curio instances with the '{Post}' layer to redundancy.", + "placeholder": null + }, + { + "id": "One database can serve multiple miner IDs: Run a migration for each lotus-miner.", + "translation": "한 개의 데이터베이스는 여러 광부 ID를 제공할 수 있습니다: 각 lotus-miner에 대해 마이그레이션을 실행하세요.", + "message": "One database can serve multiple miner IDs: Run a migration for each lotus-miner.", + "placeholder": null + }, + { + "id": "Please start (or restart) {Lotus_miner} now that database credentials are in {Toml}.", + "translation": "데이터베이스 자격 증명이 {Toml}에 입력되었으므로 지금 {Lotus_miner}을 시작하거나 다시 시작하세요.", + "message": "Please start (or restart) {Lotus_miner} now that database credentials are in {Toml}.", + "placeholder": null + }, + { + "id": "Error interpreting miner ID: {Error}: ID: {String}", + "translation": "광부 ID를 해석하는 중 오류 발생: {Error}: ID: {String}", + "message": "Error interpreting miner ID: {Error}: ID: {String}", + "placeholder": null + }, + { + "id": "Enabling Sector Indexing in the database.", + "translation": "데이터베이스에서 Sector Indexing을 활성화합니다.", + "message": "Enabling Sector Indexing in the database.", + "placeholder": null + }, + { + "id": "Error expanding path: {Error}", + "translation": "경로를 확장하는 중 오류 발생: {Error}", + "message": "Error expanding path: {Error}", + "placeholder": null + }, + { + "id": "Could not create repo from directory: {Error}. Aborting migration", + "translation": "디렉토리에서 저장소를 생성할 수 없습니다: {Error}. 마이그레이션을 중단합니다.", + "message": "Could not create repo from directory: {Error}. Aborting migration", + "placeholder": null + }, + { + "id": "Could not lock miner repo. Your miner must be stopped: {Error}\n Aborting migration", + "translation": "광부 저장소를 잠금 해제할 수 없습니다. 귀하의 광부를 중지해야 합니다: {Error}\n 마이그레이션을 중단합니다.", + "message": "Could not lock miner repo. Your miner must be stopped: {Error}\n Aborting migration", + "placeholder": null + }, + { + "id": "To work with the config:", + "translation": "구성 파일을 사용하려면:", + "message": "To work with the config:", + "placeholder": null + } + ] +} \ No newline at end of file diff --git a/cmd/curio/internal/translations/locales/ko/out.gotext.json b/cmd/curio/internal/translations/locales/ko/out.gotext.json new file mode 100644 index 000000000..9eb8915c0 --- /dev/null +++ b/cmd/curio/internal/translations/locales/ko/out.gotext.json @@ -0,0 +1,89 @@ +{ + "language": "ko", + "messages": [ + { + "id": "Try the web interface with {__layersgui} for further guided improvements.", + "translation": "", + "message": "Try the web interface with {__layersgui} for further guided improvements.", + "placeholder": null + }, + { + "id": "Error connecting to lotus node: {Error} {Error_1}", + "translation": "", + "message": "Error connecting to lotus node: {Error} {Error_1}", + "placeholder": null + }, + { + "id": "could not get API info for FullNode: {Err}", + "translation": "", + "message": "could not get API info for FullNode: {Err}", + "placeholder": null + }, + { + "id": "Error getting token: {Error}", + "translation": "", + "message": "Error getting token: {Error}", + "placeholder": null + }, + { + "id": "Filecoin {Slack} channels: {Fil_curio_help} and {Fil_curio_dev}", + "translation": "", + "message": "Filecoin {Slack} channels: {Fil_curio_help} and {Fil_curio_dev}", + "placeholder": null + }, + { + "id": "Start multiple Curio instances with the '{Post}' layer to redundancy.", + "translation": "", + "message": "Start multiple Curio instances with the '{Post}' layer to redundancy.", + "placeholder": null + }, + { + "id": "One database can serve multiple miner IDs: Run a migration for each lotus-miner.", + "translation": "", + "message": "One database can serve multiple miner IDs: Run a migration for each lotus-miner.", + "placeholder": null + }, + { + "id": "Please start (or restart) {Lotus_miner} now that database credentials are in {Toml}.", + "translation": "", + "message": "Please start (or restart) {Lotus_miner} now that database credentials are in {Toml}.", + "placeholder": null + }, + { + "id": "Error interpreting miner ID: {Error}: ID: {String}", + "translation": "", + "message": "Error interpreting miner ID: {Error}: ID: {String}", + "placeholder": null + }, + { + "id": "Enabling Sector Indexing in the database.", + "translation": "", + "message": "Enabling Sector Indexing in the database.", + "placeholder": null + }, + { + "id": "Error expanding path: {Error}", + "translation": "", + "message": "Error expanding path: {Error}", + "placeholder": null + }, + { + "id": "Could not create repo from directory: {Error}. Aborting migration", + "translation": "", + "message": "Could not create repo from directory: {Error}. Aborting migration", + "placeholder": null + }, + { + "id": "Could not lock miner repo. Your miner must be stopped: {Error}\n Aborting migration", + "translation": "", + "message": "Could not lock miner repo. Your miner must be stopped: {Error}\n Aborting migration", + "placeholder": null + }, + { + "id": "To work with the config:", + "translation": "", + "message": "To work with the config:", + "placeholder": null + } + ] + } diff --git a/cmd/curio/internal/translations/locales/zh/messages.gotext.json b/cmd/curio/internal/translations/locales/zh/messages.gotext.json new file mode 100644 index 000000000..ffb5f3bde --- /dev/null +++ b/cmd/curio/internal/translations/locales/zh/messages.gotext.json @@ -0,0 +1,704 @@ +{ + "language": "zh", + "messages": [ + { + "id": "This interactive tool will walk you through migration of Curio.\nPress Ctrl+C to exit at any time.", + "message": "This interactive tool will walk you through migration of Curio.\nPress Ctrl+C to exit at any time.", + "translation": "此互动工具将引导您完成Curio的迁移。\n随时按Ctrl+C退出。" + }, + { + "id": "This tool confirms each action it does.", + "message": "This tool confirms each action it does.", + "translation": "此工具确认其执行的每个操作。" + }, + { + "id": "Ctrl+C pressed in Terminal", + "message": "Ctrl+C pressed in Terminal", + "translation": "在终端中按下Ctrl+C" + }, + { + "id": "Verifying Sectors exist in Yugabyte.", + "message": "Verifying Sectors exist in Yugabyte.", + "translation": "正在验证Yugabyte中的扇区是否存在。" + }, + { + "id": "Error verifying sectors: {Error}", + "message": "Error verifying sectors: {Error}", + "translation": "验证扇区时出错:{Error}", + "placeholders": [ + { + "id": "Error", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "err.Error()" + } + ] + }, + { + "id": "Sectors verified. {I} sectors found.", + "message": "Sectors verified. {I} sectors found.", + "translation": "已验证扇区。找到了{I}个扇区。", + "placeholders": [ + { + "id": "I", + "string": "%[1]d", + "type": "[]int", + "underlyingType": "[]int", + "argNum": 1, + "expr": "i" + } + ] + }, + { + "id": "Never remove the database info from the config.toml for lotus-miner as it avoids double PoSt.", + "message": "Never remove the database info from the config.toml for lotus-miner as it avoids double PoSt.", + "translation": "从config.toml中永远不要删除lotus-miner的数据库信息,因为它避免了双PoSt。" + }, + { + "id": "Enter the info to connect to your Yugabyte database installation (https://download.yugabyte.com/)", + "message": "Enter the info to connect to your Yugabyte database installation (https://download.yugabyte.com/)", + "translation": "输入连接到您的Yugabyte数据库安装的信息(https://download.yugabyte.com/)" + }, + { + "id": "Host: {Hosts_}", + "message": "Host: {Hosts_}", + "translation": "主机:{Hosts_}", + "placeholders": [ + { + "id": "Hosts_", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "strings.Join(harmonycfg.Hosts, \",\")" + } + ] + }, + { + "id": "Port: {Port}", + "message": "Port: {Port}", + "translation": "端口:{Port}", + "placeholders": [ + { + "id": "Port", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "harmonycfg.Port" + } + ] + }, + { + "id": "Username: {Username}", + "message": "Username: {Username}", + "translation": "用户名:{Username}", + "placeholders": [ + { + "id": "Username", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "harmonycfg.Username" + } + ] + }, + { + "id": "Password: {Password}", + "message": "Password: {Password}", + "translation": "密码:{Password}", + "placeholders": [ + { + "id": "Password", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "harmonycfg.Password" + } + ] + }, + { + "id": "Database: {Database}", + "message": "Database: {Database}", + "translation": "数据库:{Database}", + "placeholders": [ + { + "id": "Database", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "harmonycfg.Database" + } + ] + }, + { + "id": "Continue to connect and update schema.", + "message": "Continue to connect and update schema.", + "translation": "继续连接和更新架构。" + }, + { + "id": "Database config error occurred, abandoning migration: {Error}", + "message": "Database config error occurred, abandoning migration: {Error}", + "translation": "发生数据库配置错误,放弃迁移:{Error}", + "placeholders": [ + { + "id": "Error", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "err.Error()" + } + ] + }, + { + "id": "Enter the Yugabyte database host(s)", + "message": "Enter the Yugabyte database host(s)", + "translation": "输入Yugabyte数据库主机(S)" + }, + { + "id": "No host provided", + "message": "No host provided", + "translation": "未提供主机" + }, + { + "id": "Enter the Yugabyte database {Stringport_username_password_databasei_1}", + "message": "Enter the Yugabyte database {Stringport_username_password_databasei_1}", + "translation": "输入Yugabyte数据库 {Stringport_username_password_databasei_1}", + "placeholders": [ + { + "id": "Stringport_username_password_databasei_1", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "[]string{\"port\", \"username\", \"password\", \"database\"}[i-1]" + } + ] + }, + { + "id": "No value provided", + "message": "No value provided", + "translation": "未提供值" + }, + { + "id": "Error connecting to Yugabyte database: {Error}", + "message": "Error connecting to Yugabyte database: {Error}", + "translation": "连接到Yugabyte数据库时出错:{Error}", + "placeholders": [ + { + "id": "Error", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "err.Error()" + } + ] + }, + { + "id": "Connected to Yugabyte. Schema is current.", + "message": "Connected to Yugabyte. Schema is current.", + "translation": "已连接到Yugabyte。模式是当前的。" + }, + { + "id": "Error encoding config.toml: {Error}", + "message": "Error encoding config.toml: {Error}", + "translation": "编码config.toml时出错:{Error}", + "placeholders": [ + { + "id": "Error", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "err.Error()" + } + ] + }, + { + "id": "Error reading filemode of config.toml: {Error}", + "message": "Error reading filemode of config.toml: {Error}", + "translation": "读取config.toml文件模式时出错:{Error}", + "placeholders": [ + { + "id": "Error", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "err.Error()" + } + ] + }, + { + "id": "Error writing config.toml: {Error}", + "message": "Error writing config.toml: {Error}", + "translation": "写入config.toml时出错:{Error}", + "placeholders": [ + { + "id": "Error", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "err.Error()" + } + ] + }, + { + "id": "Restart Lotus Miner.", + "message": "Restart Lotus Miner.", + "translation": "重新启动Lotus Miner。" + }, + { + "id": "Connected to Yugabyte", + "message": "Connected to Yugabyte", + "translation": "已连接到Yugabyte" + }, + { + "id": "Select the location of your lotus-miner config directory?", + "message": "Select the location of your lotus-miner config directory?", + "translation": "选择您的lotus-miner配置目录的位置?" + }, + { + "id": "Other", + "message": "Other", + "translation": "其他" + }, + { + "id": "Enter the path to the configuration directory used by lotus-miner", + "message": "Enter the path to the configuration directory used by lotus-miner", + "translation": "输入lotus-miner使用的配置目录的路径" + }, + { + "id": "No path provided, abandoning migration", + "message": "No path provided, abandoning migration", + "translation": "未提供路径,放弃迁移" + }, + { + "id": "Cannot read the config.toml file in the provided directory, Error: {Error}", + "message": "Cannot read the config.toml file in the provided directory, Error: {Error}", + "translation": "无法读取提供的目录中的config.toml文件,错误:{Error}", + "placeholders": [ + { + "id": "Error", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "err.Error()" + } + ] + }, + { + "id": "Read Miner Config", + "message": "Read Miner Config", + "translation": "读取矿工配置" + }, + { + "id": "Completed Step: {Step}", + "message": "Completed Step: {Step}", + "translation": "完成步骤:{Step}", + "placeholders": [ + { + "id": "Step", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "step" + } + ] + }, + { + "id": "This interactive tool migrates lotus-miner to Curio in 5 minutes.", + "translation": "这个交互式工具可以在5分钟内将lotus-miner迁移到Curio。", + "message": "This interactive tool migrates lotus-miner to Curio in 5 minutes.", + "placeholder": null + }, + { + "id": "Each step needs your confirmation and can be reversed. Press Ctrl+C to exit at any time.", + "translation": "每一步都需要您的确认,并且可以撤销。随时按Ctrl+C退出。", + "message": "Each step needs your confirmation and can be reversed. Press Ctrl+C to exit at any time.", + "placeholder": null + }, + { + "id": "Use the arrow keys to navigate: ↓ ↑ → ←", + "translation": "使用箭头键进行导航:↓ ↑ → ←", + "message": "Use the arrow keys to navigate: ↓ ↑ → ←", + "placeholder": null + }, + { + "id": "Lotus-Miner to Curio Migration.", + "translation": "Lotus-Miner到Curio迁移。", + "message": "Lotus-Miner to Curio Migration.", + "placeholder": null + }, + { + "id": "Try the web interface with for further guided improvements.", + "translation": "尝试使用网页界面进行进一步的指导改进。", + "message": "Try the web interface with for further guided improvements.", + "placeholder": null + }, + { + "id": "You can now migrate your market node ({Boost}), if applicable.", + "translation": "如果适用,您现在可以迁移您的市场节点({Boost})。", + "message": "You can now migrate your market node ({Boost}), if applicable.", + "placeholder": null + }, + { + "id": "Migrating config.toml to database.", + "translation": "正在将config.toml迁移到数据库。", + "message": "Migrating config.toml to database.", + "placeholder": null + }, + { + "id": "Error reading from database: {Error}. Aborting Migration.", + "translation": "读取数据库时出错:{Error}。正在中止迁移。", + "message": "Error reading from database: {Error}. Aborting Migration.", + "placeholder": null + }, + { + "id": "cannot read API: {Error}. Aborting Migration", + "translation": "无法读取API:{Error}。正在中止迁移", + "message": "cannot read API: {Error}. Aborting Migration", + "placeholder": null + }, + { + "id": "Error saving config to layer: {Error}. Aborting Migration", + "translation": "保存配置到层时出错:{Error}。正在中止迁移", + "message": "Error saving config to layer: {Error}. Aborting Migration", + "placeholder": null + }, + { + "id": "Protocol Labs wants to improve the software you use. Tell the team you're using Curio.", + "translation": "Protocol Labs希望改进您使用的软件。告诉团队您正在使用Curio。", + "message": "Protocol Labs wants to improve the software you use. Tell the team you're using Curio.", + "placeholder": null + }, + { + "id": "Select what you want to share with the Curio team.", + "translation": "选择您想与Curio团队分享的内容。", + "message": "Select what you want to share with the Curio team.", + "placeholder": null + }, + { + "id": "Individual Data: Miner ID, Curio version, net ({Mainnet} or {Testnet}). Signed.", + "translation": "个人数据:矿工ID、Curio版本、网络({Mainnet}或{Testnet})。已签名。", + "message": "Individual Data: Miner ID, Curio version, net ({Mainnet} or {Testnet}). Signed.", + "placeholder": null + }, + { + "id": "Aggregate-Anonymous: version, net, and Miner power (bucketed).", + "translation": "聚合-匿名:版本、网络和矿工功率(分桶)。", + "message": "Aggregate-Anonymous: version, net, and Miner power (bucketed).", + "placeholder": null + }, + { + "id": "Hint: I am someone running Curio on net.", + "translation": "提示:我是在网络上运行Curio的人。", + "message": "Hint: I am someone running Curio on net.", + "placeholder": null + }, + { + "id": "Nothing.", + "translation": "没有。", + "message": "Nothing.", + "placeholder": null + }, + { + "id": "Aborting remaining steps.", + "translation": "中止剩余步骤。", + "message": "Aborting remaining steps.", + "placeholder": null + }, + { + "id": "Error connecting to lotus node: {Error}", + "translation": "连接到莲花节点时出错:{Error}", + "message": "Error connecting to lotus node: {Error}", + "placeholder": null + }, + { + "id": "Error getting miner power: {Error}", + "translation": "获取矿工功率时出错:{Error}", + "message": "Error getting miner power: {Error}", + "placeholder": null + }, + { + "id": "Error marshalling message: {Error}", + "translation": "整理消息时出错:{Error}", + "message": "Error marshalling message: {Error}", + "placeholder": null + }, + { + "id": "Error getting miner info: {Error}", + "translation": "获取矿工信息时出错:{Error}", + "message": "Error getting miner info: {Error}", + "placeholder": null + }, + { + "id": "Error signing message: {Error}", + "translation": "签署消息时出错:{Error}", + "message": "Error signing message: {Error}", + "placeholder": null + }, + { + "id": "Error sending message: {Error}", + "translation": "发送消息时出错:{Error}", + "message": "Error sending message: {Error}", + "placeholder": null + }, + { + "id": "Error sending message: Status {Status}, Message:", + "translation": "发送消息时出错:状态{Status},消息:", + "message": "Error sending message: Status {Status}, Message:", + "placeholder": null + }, + { + "id": "Message sent.", + "translation": "消息已发送。", + "message": "Message sent.", + "placeholder": null + }, + { + "id": "Documentation:", + "translation": "文档:", + "message": "Documentation:", + "placeholder": null + }, + { + "id": "The '{Base}' layer stores common configuration. All curio instances can include it in their {__layers} argument.", + "translation": "'{Base}'层存储通用配置。所有Curio实例都可以在其{__layers}参数中包含它。", + "message": "The '{Base}' layer stores common configuration. All curio instances can include it in their {__layers} argument.", + "placeholder": null + }, + { + "id": "You can add other layers for per-machine configuration changes.", + "translation": "您可以添加其他层进行每台机器的配置更改。", + "message": "You can add other layers for per-machine configuration changes.", + "placeholder": null + }, + { + "id": "Join {Fil_curio_help} in Filecoin {Slack} for help.", + "translation": "加入Filecoin {Slack}中的{Fil_curio_help}寻求帮助。", + "message": "Join {Fil_curio_help} in Filecoin {Slack} for help.", + "placeholder": null + }, + { + "id": "Join {Fil_curio_dev} in Filecoin {Slack} to follow development and feedback!", + "translation": "加入Filecoin {Slack}中的{Fil_curio_dev}来跟踪开发和反馈!", + "message": "Join {Fil_curio_dev} in Filecoin {Slack} to follow development and feedback!", + "placeholder": null + }, + { + "id": "Want PoST redundancy? Run many Curio instances with the '{Post}' layer.", + "translation": "需要PoST冗余?使用'{Post}'层运行多个Curio实例。", + "message": "Want PoST redundancy? Run many Curio instances with the '{Post}' layer.", + "placeholder": null + }, + { + "id": "Point your browser to your web GUI to complete setup with {Boost} and advanced featues.", + "translation": "将您的浏览器指向您的网络GUI,以使用{Boost}和高级功能完成设置。", + "message": "Point your browser to your web GUI to complete setup with {Boost} and advanced featues.", + "placeholder": null + }, + { + "id": "For SPs with multiple Miner IDs, run 1 migration per lotus-miner all to the same 1 database. The cluster will serve all Miner IDs.", + "translation": "对于具有多个矿工ID的SP,针对所有lotus-miner运行1次迁移到同一个数据库。集群将服务所有矿工ID。", + "message": "For SPs with multiple Miner IDs, run 1 migration per lotus-miner all to the same 1 database. The cluster will serve all Miner IDs.", + "placeholder": null + }, + { + "id": "Please start {Lotus_miner} now that database credentials are in {Toml}.", + "translation": "现在数据库凭证在{Toml}中,请启动{Lotus_miner}。", + "message": "Please start {Lotus_miner} now that database credentials are in {Toml}.", + "placeholder": null + }, + { + "id": "Waiting for {Lotus_miner} to write sectors into Yugabyte.", + "translation": "等待{Lotus_miner}将扇区写入Yugabyte。", + "message": "Waiting for {Lotus_miner} to write sectors into Yugabyte.", + "placeholder": null + }, + { + "id": "The sectors are in the database. The database is ready for {Curio}.", + "translation": "扇区在数据库中。数据库已准备好用于{Curio}。", + "message": "The sectors are in the database. The database is ready for {Curio}.", + "placeholder": null + }, + { + "id": "Now shut down lotus-miner and move the systems to {Curio}.", + "translation": "现在关闭lotus-miner并将系统移至{Curio}。", + "message": "Now shut down lotus-miner and move the systems to {Curio}.", + "placeholder": null + }, + { + "id": "Press return to continue", + "translation": "按回车继续", + "message": "Press return to continue", + "placeholder": null + }, + { + "id": "Aborting migration.", + "translation": "中止迁移。", + "message": "Aborting migration.", + "placeholder": null + }, + { + "id": "Sectors verified. {I} sector locations found.", + "translation": "扇区已验证。发现了{I}个扇区位置。", + "message": "Sectors verified. {I} sector locations found.", + "placeholder": null + }, + { + "id": "Press return to update {Toml} with Yugabyte info. Backup the file now.", + "translation": "按回车更新{Toml}以获取Yugabyte信息。现在备份文件。", + "message": "Press return to update {Toml} with Yugabyte info. Backup the file now.", + "placeholder": null + }, + { + "id": "To start, ensure your sealing pipeline is drained and shut-down lotus-miner.", + "translation": "开始之前,请确保您的密封管道已排空并关闭lotus-miner。", + "message": "To start, ensure your sealing pipeline is drained and shut-down lotus-miner.", + "placeholder": null + }, + { + "id": "Enter the path to the configuration directory used by {Lotus_miner}", + "translation": "输入{Lotus_miner}使用的配置目录的路径", + "message": "Enter the path to the configuration directory used by {Lotus_miner}", + "placeholder": null + }, + { + "id": "Step Complete: {Step}", + "translation": "步骤完成:{Step}", + "message": "Step Complete: {Step}", + "placeholder": null + }, + { + "id": "Configuration 'base' was updated to include this miner's address and its wallet setup.", + "translation": "配置'base'已更新,包含了这个矿工的地址和其钱包设置。", + "message": "Configuration 'base' was updated to include this miner's address and its wallet setup.", + "placeholder": null + }, + { + "id": "Compare the configurations {Base} to {MinerAddresses0}. Changes between the miner IDs other than wallet addreses should be a new, minimal layer for runners that need it.", + "translation": "比较配置{Base}和{MinerAddresses0}。矿工ID之间除了钱包地址的变化应该是需要的运行者的一个新的、最小的层。", + "message": "Compare the configurations {Base} to {MinerAddresses0}. Changes between the miner IDs other than wallet addreses should be a new, minimal layer for runners that need it.", + "placeholder": null + }, + { + "id": "Configuration 'base' was created to include this miner's address and its wallet setup.", + "translation": "配置'base'已创建,包括了这个矿工的地址和其钱包设置。", + "message": "Configuration 'base' was created to include this miner's address and its wallet setup.", + "placeholder": null + }, + { + "id": "Layer {LayerName} created.", + "translation": "层{LayerName}已创建。", + "message": "Layer {LayerName} created.", + "placeholder": null + }, + { + "id": "To work with the config: \\n", + "translation": "要使用配置:\\n", + "message": "To work with the config: \\n", + "placeholder": null + }, + { + "id": "To run Curio: With machine or cgroup isolation, use the command (with example layer selection):", + "translation": "运行Curio:使用机器或cgroup隔离,使用命令(附带示例层选择):", + "message": "To run Curio: With machine or cgroup isolation, use the command (with example layer selection):", + "placeholder": null + }, + { + "id": "Try the web interface with {__layersgui} for further guided improvements.", + "translation": "尝试使用{__layersgui}的Web界面进行进一步引导式改进。", + "message": "Try the web interface with {__layersgui} for further guided improvements.", + "placeholder": null + }, + { + "id": "Error connecting to lotus node: {Error} {Error_1}", + "translation": "连接到lotus节点时出错:{Error} {Error_1}", + "message": "Error connecting to lotus node: {Error} {Error_1}", + "placeholder": null + }, + { + "id": "could not get API info for FullNode: {Err}", + "translation": "无法获取FullNode的API信息:{Err}", + "message": "could not get API info for FullNode: {Err}", + "placeholder": null + }, + { + "id": "Error getting token: {Error}", + "translation": "获取令牌时出错:{Error}", + "message": "Error getting token: {Error}", + "placeholder": null + }, + { + "id": "Filecoin {Slack} channels: {Fil_curio_help} and {Fil_curio_dev}", + "translation": "Filecoin {Slack} 频道:{Fil_curio_help} 和 {Fil_curio_dev}", + "message": "Filecoin {Slack} channels: {Fil_curio_help} and {Fil_curio_dev}", + "placeholder": null + }, + { + "id": "Start multiple Curio instances with the '{Post}' layer to redundancy.", + "translation": "使用'{Post}'层启动多个Curio实例以实现冗余。", + "message": "Start multiple Curio instances with the '{Post}' layer to redundancy.", + "placeholder": null + }, + { + "id": "One database can serve multiple miner IDs: Run a migration for each lotus-miner.", + "translation": "一个数据库可以服务多个矿工ID:为每个lotus-miner运行迁移。", + "message": "One database can serve multiple miner IDs: Run a migration for each lotus-miner.", + "placeholder": null + }, + { + "id": "Please start (or restart) {Lotus_miner} now that database credentials are in {Toml}.", + "translation": "请立即启动(或重新启动){Lotus_miner},因为数据库凭据已在{Toml}中。", + "message": "Please start (or restart) {Lotus_miner} now that database credentials are in {Toml}.", + "placeholder": null + }, + { + "id": "Error interpreting miner ID: {Error}: ID: {String}", + "translation": "解释矿工ID时出错:{Error}:ID:{String}", + "message": "Error interpreting miner ID: {Error}: ID: {String}", + "placeholder": null + }, + { + "id": "Enabling Sector Indexing in the database.", + "translation": "在数据库中启用扇区索引。", + "message": "Enabling Sector Indexing in the database.", + "placeholder": null + }, + { + "id": "Error expanding path: {Error}", + "translation": "扩展路径时出错:{Error}", + "message": "Error expanding path: {Error}", + "placeholder": null + }, + { + "id": "Could not create repo from directory: {Error}. Aborting migration", + "translation": "无法从目录创建repo:{Error}。 中止迁移", + "message": "Could not create repo from directory: {Error}. Aborting migration", + "placeholder": null + }, + { + "id": "Could not lock miner repo. Your miner must be stopped: {Error}\n Aborting migration", + "translation": "无法锁定矿工repo。 您的矿工必须停止:{Error}\n 中止迁移", + "message": "Could not lock miner repo. Your miner must be stopped: {Error}\n Aborting migration", + "placeholder": null + }, + { + "id": "To work with the config:", + "translation": "要使用配置:", + "message": "To work with the config:", + "placeholder": null + } + ] +} \ No newline at end of file diff --git a/cmd/curio/internal/translations/locales/zh/out.gotext.json b/cmd/curio/internal/translations/locales/zh/out.gotext.json new file mode 100644 index 000000000..37ac5f933 --- /dev/null +++ b/cmd/curio/internal/translations/locales/zh/out.gotext.json @@ -0,0 +1,89 @@ +{ + "language": "zh", + "messages": [ + { + "id": "Try the web interface with {__layersgui} for further guided improvements.", + "translation": "", + "message": "Try the web interface with {__layersgui} for further guided improvements.", + "placeholder": null + }, + { + "id": "Error connecting to lotus node: {Error} {Error_1}", + "translation": "", + "message": "Error connecting to lotus node: {Error} {Error_1}", + "placeholder": null + }, + { + "id": "could not get API info for FullNode: {Err}", + "translation": "", + "message": "could not get API info for FullNode: {Err}", + "placeholder": null + }, + { + "id": "Error getting token: {Error}", + "translation": "", + "message": "Error getting token: {Error}", + "placeholder": null + }, + { + "id": "Filecoin {Slack} channels: {Fil_curio_help} and {Fil_curio_dev}", + "translation": "", + "message": "Filecoin {Slack} channels: {Fil_curio_help} and {Fil_curio_dev}", + "placeholder": null + }, + { + "id": "Start multiple Curio instances with the '{Post}' layer to redundancy.", + "translation": "", + "message": "Start multiple Curio instances with the '{Post}' layer to redundancy.", + "placeholder": null + }, + { + "id": "One database can serve multiple miner IDs: Run a migration for each lotus-miner.", + "translation": "", + "message": "One database can serve multiple miner IDs: Run a migration for each lotus-miner.", + "placeholder": null + }, + { + "id": "Please start (or restart) {Lotus_miner} now that database credentials are in {Toml}.", + "translation": "", + "message": "Please start (or restart) {Lotus_miner} now that database credentials are in {Toml}.", + "placeholder": null + }, + { + "id": "Error interpreting miner ID: {Error}: ID: {String}", + "translation": "", + "message": "Error interpreting miner ID: {Error}: ID: {String}", + "placeholder": null + }, + { + "id": "Enabling Sector Indexing in the database.", + "translation": "", + "message": "Enabling Sector Indexing in the database.", + "placeholder": null + }, + { + "id": "Error expanding path: {Error}", + "translation": "", + "message": "Error expanding path: {Error}", + "placeholder": null + }, + { + "id": "Could not create repo from directory: {Error}. Aborting migration", + "translation": "", + "message": "Could not create repo from directory: {Error}. Aborting migration", + "placeholder": null + }, + { + "id": "Could not lock miner repo. Your miner must be stopped: {Error}\n Aborting migration", + "translation": "", + "message": "Could not lock miner repo. Your miner must be stopped: {Error}\n Aborting migration", + "placeholder": null + }, + { + "id": "To work with the config:", + "translation": "", + "message": "To work with the config:", + "placeholder": null + } + ] + } diff --git a/cmd/curio/internal/translations/translations.go b/cmd/curio/internal/translations/translations.go new file mode 100644 index 000000000..361e8e894 --- /dev/null +++ b/cmd/curio/internal/translations/translations.go @@ -0,0 +1,27 @@ +// Usage: +// To UPDATE translations: +// +// 1. add/change strings in guidedsetup folder that use d.T() or d.say(). +// +// 2. run `go generate` in the cmd/curio/internal/translations/ folder. +// +// 3. ChatGPT 3.5 can translate the ./locales/??/out.gotext.json files' +// which ONLY include the un-translated messages. +// APPEND to the messages.gotext.json files's array. +// +// ChatGPT fuss: +// - on a good day, you may need to hit "continue generating". +// - > 60? you'll need to give it sections of the file. +// +// 4. Re-import with `go generate` again. +// +// To ADD a language: +// 1. Add it to the list in updateLang.sh +// 2. Run `go generate` in the cmd/curio/internal/translations/ folder. +// 3. Follow the "Update translations" steps here. +// 4. Code will auto-detect the new language and use it. +// +// FUTURE Reliability: OpenAPI automation. +package translations + +//go:generate ./updateLang.sh diff --git a/cmd/curio/internal/translations/updateLang.sh b/cmd/curio/internal/translations/updateLang.sh new file mode 100755 index 000000000..6325ab146 --- /dev/null +++ b/cmd/curio/internal/translations/updateLang.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +#OP: Only run if some file in ../guidedsetup* is newer than catalog.go +# Change this condition if using translations more widely. +if [ "$(find ../../guidedsetup/* -newermt "$(date -d '1 minute ago')" -newer catalog.go)" ] || [ "$(find locales/* -newermt "$(date -d '1 minute ago')" -newer catalog.go)" ]; then + gotext -srclang=en update -out=catalog.go -lang=en,zh,ko github.com/filecoin-project/lotus/cmd/curio/guidedsetup + go run knowns/main.go locales/zh locales/ko +fi diff --git a/cmd/lotus-provider/main.go b/cmd/curio/main.go similarity index 75% rename from cmd/lotus-provider/main.go rename to cmd/curio/main.go index b4342d49a..8c64af379 100644 --- a/cmd/lotus-provider/main.go +++ b/cmd/curio/main.go @@ -16,7 +16,8 @@ import ( "github.com/filecoin-project/lotus/build" lcli "github.com/filecoin-project/lotus/cli" cliutil "github.com/filecoin-project/lotus/cli/util" - "github.com/filecoin-project/lotus/cmd/lotus-provider/deps" + "github.com/filecoin-project/lotus/cmd/curio/deps" + "github.com/filecoin-project/lotus/cmd/curio/guidedsetup" "github.com/filecoin-project/lotus/lib/lotuslog" "github.com/filecoin-project/lotus/lib/tracing" "github.com/filecoin-project/lotus/node/repo" @@ -24,7 +25,7 @@ import ( var log = logging.Logger("main") -func SetupCloseHandler() { +func setupCloseHandler() { c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt, syscall.SIGTERM) go func() { @@ -36,7 +37,6 @@ func SetupCloseHandler() { } func main() { - SetupCloseHandler() lotuslog.SetupLogLevels() @@ -48,10 +48,12 @@ func main() { configCmd, testCmd, webCmd, + guidedsetup.GuidedsetupCmd, + configMigrateCmd, sealCmd, } - jaeger := tracing.SetupJaegerTracing("lotus") + jaeger := tracing.SetupJaegerTracing("curio") defer func() { if jaeger != nil { _ = jaeger.ForceFlush(context.Background()) @@ -65,7 +67,7 @@ func main() { if jaeger != nil { _ = jaeger.Shutdown(cctx.Context) } - jaeger = tracing.SetupJaegerTracing("lotus/" + cmd.Name) + jaeger = tracing.SetupJaegerTracing("curio/" + cmd.Name) if cctx.IsSet("color") { color.NoColor = !cctx.Bool("color") @@ -80,10 +82,14 @@ func main() { } app := &cli.App{ - Name: "lotus-provider", + Name: "curio", Usage: "Filecoin decentralized storage network provider", Version: build.UserVersion(), EnableBashCompletion: true, + Before: func(c *cli.Context) error { + setupCloseHandler() + return nil + }, Flags: []cli.Flag{ &cli.BoolFlag{ // examined in the Before above @@ -93,48 +99,45 @@ func main() { }, &cli.StringFlag{ Name: "panic-reports", - EnvVars: []string{"LOTUS_PANIC_REPORT_PATH"}, + EnvVars: []string{"CURIO_PANIC_REPORT_PATH"}, Hidden: true, - Value: "~/.lotusprovider", // should follow --repo default + Value: "~/.curio", // should follow --repo default }, &cli.StringFlag{ Name: "db-host", - EnvVars: []string{"LOTUS_DB_HOST"}, + EnvVars: []string{"CURIO_DB_HOST", "CURIO_HARMONYDB_HOSTS"}, Usage: "Command separated list of hostnames for yugabyte cluster", Value: "yugabyte", }, &cli.StringFlag{ Name: "db-name", - EnvVars: []string{"LOTUS_DB_NAME", "LOTUS_HARMONYDB_HOSTS"}, + EnvVars: []string{"CURIO_DB_NAME", "CURIO_HARMONYDB_NAME"}, Value: "yugabyte", }, &cli.StringFlag{ Name: "db-user", - EnvVars: []string{"LOTUS_DB_USER", "LOTUS_HARMONYDB_USERNAME"}, + EnvVars: []string{"CURIO_DB_USER", "CURIO_HARMONYDB_USERNAME"}, Value: "yugabyte", }, &cli.StringFlag{ Name: "db-password", - EnvVars: []string{"LOTUS_DB_PASSWORD", "LOTUS_HARMONYDB_PASSWORD"}, + EnvVars: []string{"CURIO_DB_PASSWORD", "CURIO_HARMONYDB_PASSWORD"}, Value: "yugabyte", }, &cli.StringFlag{ Name: "db-port", - EnvVars: []string{"LOTUS_DB_PORT", "LOTUS_HARMONYDB_PORT"}, + EnvVars: []string{"CURIO_DB_PORT", "CURIO_HARMONYDB_PORT"}, Hidden: true, Value: "5433", }, &cli.StringFlag{ Name: deps.FlagRepoPath, - EnvVars: []string{"LOTUS_REPO_PATH"}, - Value: "~/.lotusprovider", + EnvVars: []string{"CURIO_REPO_PATH"}, + Value: "~/.curio", }, cliutil.FlagVeryVerbose, }, Commands: append(local, lcli.CommonCommands...), - Before: func(c *cli.Context) error { - return nil - }, After: func(c *cli.Context) error { if r := recover(); r != nil { p, err := homedir.Expand(c.String(FlagMinerRepo)) @@ -143,7 +146,7 @@ func main() { panic(r) } - // Generate report in LOTUS_PATH and re-raise panic + // Generate report in CURIO_PATH and re-raise panic build.GeneratePanicReport(c.String("panic-reports"), p, c.App.Name) panic(r) } @@ -151,6 +154,6 @@ func main() { }, } app.Setup() - app.Metadata["repoType"] = repo.Provider + app.Metadata["repoType"] = repo.Curio lcli.RunApp(app) } diff --git a/cmd/curio/migrate.go b/cmd/curio/migrate.go new file mode 100644 index 000000000..5cf55bcb4 --- /dev/null +++ b/cmd/curio/migrate.go @@ -0,0 +1,71 @@ +package main + +import ( + "fmt" + + "github.com/urfave/cli/v2" + "golang.org/x/xerrors" + + cliutil "github.com/filecoin-project/lotus/cli/util" + "github.com/filecoin-project/lotus/cmd/curio/guidedsetup" + "github.com/filecoin-project/lotus/node/repo" +) + +var configMigrateCmd = &cli.Command{ + Name: "from-miner", + Usage: "Express a database config (for curio) from an existing miner.", + Description: "Express a database config (for curio) from an existing miner.", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: FlagMinerRepo, + Aliases: []string{FlagMinerRepoDeprecation}, + EnvVars: []string{"LOTUS_MINER_PATH", "LOTUS_STORAGE_PATH"}, + Value: "~/.lotusminer", + Usage: fmt.Sprintf("Specify miner repo path. flag(%s) and env(LOTUS_STORAGE_PATH) are DEPRECATION, will REMOVE SOON", FlagMinerRepoDeprecation), + }, + &cli.StringFlag{ + Name: "repo", + EnvVars: []string{"LOTUS_PATH"}, + Hidden: true, + Value: "~/.lotus", + }, + &cli.StringFlag{ + Name: "to-layer", + Aliases: []string{"t"}, + Usage: "The layer name for this data push. 'base' is recommended for single-miner setup.", + }, + &cli.BoolFlag{ + Name: "overwrite", + Aliases: []string{"o"}, + Usage: "Use this with --to-layer to replace an existing layer", + }, + }, + Action: fromMiner, +} + +const ( + FlagMinerRepo = "miner-repo" +) + +const FlagMinerRepoDeprecation = "storagerepo" + +func fromMiner(cctx *cli.Context) (err error) { + minerRepoPath := cctx.String(FlagMinerRepo) + layerName := cctx.String("to-layer") + overwrite := cctx.Bool("overwrite") + + // Populate API Key + _, header, err := cliutil.GetRawAPI(cctx, repo.FullNode, "v0") + if err != nil { + return fmt.Errorf("cannot read API: %w", err) + } + + ainfo, err := cliutil.GetAPIInfo(&cli.Context{}, repo.FullNode) + if err != nil { + return xerrors.Errorf(`could not get API info for FullNode: %w + Set the environment variable to the value of "lotus auth api-info --perm=admin"`, err) + } + chainApiInfo := header.Get("Authorization")[7:] + ":" + ainfo.Addr + _, err = guidedsetup.SaveConfigToLayer(minerRepoPath, layerName, overwrite, chainApiInfo) + return err +} diff --git a/cmd/lotus-provider/pipeline.go b/cmd/curio/pipeline.go similarity index 92% rename from cmd/lotus-provider/pipeline.go rename to cmd/curio/pipeline.go index 74c49b16a..1c3f5d94a 100644 --- a/cmd/lotus-provider/pipeline.go +++ b/cmd/curio/pipeline.go @@ -12,9 +12,9 @@ import ( "github.com/filecoin-project/lotus/chain/actors/builtin/miner" "github.com/filecoin-project/lotus/chain/types" lcli "github.com/filecoin-project/lotus/cli" - "github.com/filecoin-project/lotus/cmd/lotus-provider/deps" + "github.com/filecoin-project/lotus/cmd/curio/deps" + "github.com/filecoin-project/lotus/curiosrc/seal" "github.com/filecoin-project/lotus/lib/harmony/harmonydb" - "github.com/filecoin-project/lotus/provider/lpseal" ) var sealCmd = &cli.Command{ @@ -113,7 +113,7 @@ var sealStartCmd = &cli.Command{ return xerrors.Errorf("getting seal proof type: %w", err) } - num, err := lpseal.AllocateSectorNumbers(ctx, dep.Full, dep.DB, act, cctx.Int("count"), func(tx *harmonydb.Tx, numbers []abi.SectorNumber) (bool, error) { + num, err := seal.AllocateSectorNumbers(ctx, dep.Full, dep.DB, act, cctx.Int("count"), func(tx *harmonydb.Tx, numbers []abi.SectorNumber) (bool, error) { for _, n := range numbers { _, err := tx.Exec("insert into sectors_sdr_pipeline (sp_id, sector_number, reg_seal_proof) values ($1, $2, $3)", mid, n, spt) if err != nil { diff --git a/cmd/lotus-provider/proving.go b/cmd/curio/proving.go similarity index 94% rename from cmd/lotus-provider/proving.go rename to cmd/curio/proving.go index c003c907f..3b5a3e0e4 100644 --- a/cmd/lotus-provider/proving.go +++ b/cmd/curio/proving.go @@ -15,9 +15,9 @@ import ( "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/dline" - "github.com/filecoin-project/lotus/cmd/lotus-provider/deps" + "github.com/filecoin-project/lotus/cmd/curio/deps" + curio "github.com/filecoin-project/lotus/curiosrc" "github.com/filecoin-project/lotus/lib/harmony/harmonydb" - "github.com/filecoin-project/lotus/provider" ) var testCmd = &cli.Command{ @@ -27,6 +27,9 @@ var testCmd = &cli.Command{ //provingInfoCmd, wdPostCmd, }, + Before: func(cctx *cli.Context) error { + return nil + }, } var wdPostCmd = &cli.Command{ @@ -47,7 +50,7 @@ var wdPostCmd = &cli.Command{ var wdPostTaskCmd = &cli.Command{ Name: "task", Aliases: []string{"scheduled", "schedule", "async", "asynchronous"}, - Usage: "Test the windowpost scheduler by running it on the next available lotus-provider. ", + Usage: "Test the windowpost scheduler by running it on the next available curio. ", Flags: []cli.Flag{ &cli.Uint64Flag{ Name: "deadline", @@ -148,7 +151,7 @@ It will not send any messages to the chain. Since it can compute any deadline, o &cli.StringFlag{ Name: "storage-json", Usage: "path to json file containing storage config", - Value: "~/.lotus-provider/storage.json", + Value: "~/.curio/storage.json", }, &cli.Uint64Flag{ Name: "partition", @@ -164,7 +167,8 @@ It will not send any messages to the chain. Since it can compute any deadline, o return err } - wdPostTask, wdPoStSubmitTask, derlareRecoverTask, err := provider.WindowPostScheduler(ctx, deps.Cfg.Fees, deps.Cfg.Proving, deps.Full, deps.Verif, deps.LW, nil, nil, + wdPostTask, wdPoStSubmitTask, derlareRecoverTask, err := curio.WindowPostScheduler( + ctx, deps.Cfg.Fees, deps.Cfg.Proving, deps.Full, deps.Verif, deps.LW, nil, nil, deps.As, deps.Maddrs, deps.DB, deps.Stor, deps.Si, deps.Cfg.Subsystems.WindowPostMaxTasks) if err != nil { return err diff --git a/cmd/lotus-provider/rpc/rpc.go b/cmd/curio/rpc/rpc.go similarity index 80% rename from cmd/lotus-provider/rpc/rpc.go rename to cmd/curio/rpc/rpc.go index 54eec45e8..4f3f278d1 100644 --- a/cmd/lotus-provider/rpc/rpc.go +++ b/cmd/curio/rpc/rpc.go @@ -27,34 +27,33 @@ import ( "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/api/client" cliutil "github.com/filecoin-project/lotus/cli/util" - "github.com/filecoin-project/lotus/cmd/lotus-provider/deps" + "github.com/filecoin-project/lotus/cmd/curio/deps" + "github.com/filecoin-project/lotus/curiosrc/market" + "github.com/filecoin-project/lotus/curiosrc/web" "github.com/filecoin-project/lotus/lib/rpcenc" "github.com/filecoin-project/lotus/metrics" "github.com/filecoin-project/lotus/metrics/proxy" "github.com/filecoin-project/lotus/node/repo" - "github.com/filecoin-project/lotus/provider/lpmarket" - "github.com/filecoin-project/lotus/provider/lpweb" "github.com/filecoin-project/lotus/storage/paths" "github.com/filecoin-project/lotus/storage/sealer/fsutil" "github.com/filecoin-project/lotus/storage/sealer/storiface" ) -var log = logging.Logger("lp/rpc") - +var log = logging.Logger("curio/rpc") var permissioned = os.Getenv("LOTUS_DISABLE_AUTH_PERMISSIONED") != "1" -func LotusProviderHandler( +func CurioHandler( authv func(ctx context.Context, token string) ([]auth.Permission, error), remote http.HandlerFunc, - a api.LotusProvider, + a api.Curio, permissioned bool) http.Handler { mux := mux.NewRouter() readerHandler, readerServerOpt := rpcenc.ReaderParamDecoder() rpcServer := jsonrpc.NewServer(jsonrpc.WithServerErrors(api.RPCErrors), readerServerOpt) - wapi := proxy.MetricedAPI[api.LotusProvider, api.LotusProviderStruct](a) + wapi := proxy.MetricedAPI[api.Curio, api.CurioStruct](a) if permissioned { - wapi = api.PermissionedAPI[api.LotusProvider, api.LotusProviderStruct](wapi) + wapi = api.PermissionedAPI[api.Curio, api.CurioStruct](wapi) } rpcServer.Register("Filecoin", wapi) @@ -76,13 +75,16 @@ func LotusProviderHandler( return ah } -type ProviderAPI struct { +type CurioAPI struct { *deps.Deps paths.SectorIndex ShutdownChan chan struct{} } -func (p *ProviderAPI) StorageDetachLocal(ctx context.Context, path string) error { +func (p *CurioAPI) Version(context.Context) (api.Version, error) { + return api.CurioAPIVersion0, nil +} +func (p *CurioAPI) StorageDetachLocal(ctx context.Context, path string) error { path, err := homedir.Expand(path) if err != nil { return xerrors.Errorf("expanding local path: %w", err) @@ -130,7 +132,7 @@ func (p *ProviderAPI) StorageDetachLocal(ctx context.Context, path string) error return p.LocalStore.ClosePath(ctx, localPath.ID) } -func (p *ProviderAPI) StorageLocal(ctx context.Context) (map[storiface.ID]string, error) { +func (p *CurioAPI) StorageLocal(ctx context.Context) (map[storiface.ID]string, error) { ps, err := p.LocalStore.Local(ctx) if err != nil { return nil, err @@ -144,27 +146,23 @@ func (p *ProviderAPI) StorageLocal(ctx context.Context) (map[storiface.ID]string return out, nil } -func (p *ProviderAPI) StorageStat(ctx context.Context, id storiface.ID) (fsutil.FsStat, error) { +func (p *CurioAPI) StorageStat(ctx context.Context, id storiface.ID) (fsutil.FsStat, error) { return p.Stor.FsStat(ctx, id) } -func (p *ProviderAPI) Version(context.Context) (api.Version, error) { - return api.ProviderAPIVersion0, nil -} - -func (p *ProviderAPI) AllocatePieceToSector(ctx context.Context, maddr address.Address, piece api.PieceDealInfo, rawSize int64, source url.URL, header http.Header) (api.SectorOffset, error) { - di := lpmarket.NewPieceIngester(p.Deps.DB, p.Deps.Full) +func (p *CurioAPI) AllocatePieceToSector(ctx context.Context, maddr address.Address, piece api.PieceDealInfo, rawSize int64, source url.URL, header http.Header) (api.SectorOffset, error) { + di := market.NewPieceIngester(p.Deps.DB, p.Deps.Full) return di.AllocatePieceToSector(ctx, maddr, piece, rawSize, source, header) } // Trigger shutdown -func (p *ProviderAPI) Shutdown(context.Context) error { +func (p *CurioAPI) Shutdown(context.Context) error { close(p.ShutdownChan) return nil } -func (p *ProviderAPI) StorageAddLocal(ctx context.Context, path string) error { +func (p *CurioAPI) StorageAddLocal(ctx context.Context, path string) error { path, err := homedir.Expand(path) if err != nil { return xerrors.Errorf("expanding local path: %w", err) @@ -212,10 +210,10 @@ func ListenAndServe(ctx context.Context, dependencies *deps.Deps, shutdownChan c } // Serve the RPC. srv := &http.Server{ - Handler: LotusProviderHandler( + Handler: CurioHandler( authVerify, remoteHandler, - &ProviderAPI{dependencies, dependencies.Si, shutdownChan}, + &CurioAPI{dependencies, dependencies.Si, shutdownChan}, permissioned), ReadHeaderTimeout: time.Minute * 3, BaseContext: func(listener net.Listener) context.Context { @@ -230,7 +228,7 @@ func ListenAndServe(ctx context.Context, dependencies *deps.Deps, shutdownChan c eg.Go(srv.ListenAndServe) if dependencies.Cfg.Subsystems.EnableWebGui { - web, err := lpweb.GetSrv(ctx, dependencies) + web, err := web.GetSrv(ctx, dependencies) if err != nil { return err } @@ -252,8 +250,8 @@ func ListenAndServe(ctx context.Context, dependencies *deps.Deps, shutdownChan c return eg.Wait() } -func GetProviderAPI(ctx *cli.Context) (api.LotusProvider, jsonrpc.ClientCloser, error) { - addr, headers, err := cliutil.GetRawAPI(ctx, repo.Provider, "v0") +func GetCurioAPI(ctx *cli.Context) (api.Curio, jsonrpc.ClientCloser, error) { + addr, headers, err := cliutil.GetRawAPI(ctx, repo.Curio, "v0") if err != nil { return nil, nil, err } @@ -272,5 +270,5 @@ func GetProviderAPI(ctx *cli.Context) (api.LotusProvider, jsonrpc.ClientCloser, addr = u.String() - return client.NewProviderRpc(ctx.Context, addr, headers) + return client.NewCurioRpc(ctx.Context, addr, headers) } diff --git a/cmd/lotus-provider/run.go b/cmd/curio/run.go similarity index 88% rename from cmd/lotus-provider/run.go rename to cmd/curio/run.go index 018f746f0..35fdf4a4d 100644 --- a/cmd/lotus-provider/run.go +++ b/cmd/curio/run.go @@ -14,9 +14,9 @@ import ( "github.com/filecoin-project/lotus/build" lcli "github.com/filecoin-project/lotus/cli" - "github.com/filecoin-project/lotus/cmd/lotus-provider/deps" - "github.com/filecoin-project/lotus/cmd/lotus-provider/rpc" - "github.com/filecoin-project/lotus/cmd/lotus-provider/tasks" + "github.com/filecoin-project/lotus/cmd/curio/deps" + "github.com/filecoin-project/lotus/cmd/curio/rpc" + "github.com/filecoin-project/lotus/cmd/curio/tasks" "github.com/filecoin-project/lotus/lib/ulimit" "github.com/filecoin-project/lotus/metrics" "github.com/filecoin-project/lotus/node" @@ -28,7 +28,7 @@ type stackTracer interface { var runCmd = &cli.Command{ Name: "run", - Usage: "Start a lotus provider process", + Usage: "Start a Curio process", Flags: []cli.Flag{ &cli.StringFlag{ Name: "listen", @@ -53,12 +53,12 @@ var runCmd = &cli.Command{ &cli.StringFlag{ Name: "storage-json", Usage: "path to json file containing storage config", - Value: "~/.lotus-provider/storage.json", + Value: "~/.curio/storage.json", }, &cli.StringFlag{ Name: "journal", Usage: "path to journal files", - Value: "~/.lotus-provider/", + Value: "~/.curio/", }, &cli.StringSliceFlag{ Name: "layers", @@ -89,7 +89,7 @@ var runCmd = &cli.Command{ ctx, _ := tag.New(lcli.DaemonContext(cctx), tag.Insert(metrics.Version, build.BuildVersion), tag.Insert(metrics.Commit, build.CurrentCommit), - tag.Insert(metrics.NodeType, "provider"), + tag.Insert(metrics.NodeType, "curio"), ) shutdownChan := make(chan struct{}) { @@ -135,7 +135,7 @@ var runCmd = &cli.Command{ return err } finishCh := node.MonitorShutdown(shutdownChan) //node.ShutdownHandler{Component: "rpc server", StopFunc: rpcStopper}, - //node.ShutdownHandler{Component: "provider", StopFunc: stop}, + //node.ShutdownHandler{Component: "curio", StopFunc: stop}, <-finishCh return nil @@ -144,8 +144,8 @@ var runCmd = &cli.Command{ var webCmd = &cli.Command{ Name: "web", - Usage: "Start lotus provider web interface", - Description: `Start an instance of lotus provider web interface. + Usage: "Start Curio web interface", + Description: `Start an instance of Curio web interface. This creates the 'web' layer if it does not exist, then calls run with that layer.`, Flags: []cli.Flag{ &cli.StringFlag{ diff --git a/cmd/lotus-provider/stop.go b/cmd/curio/stop.go similarity index 91% rename from cmd/lotus-provider/stop.go rename to cmd/curio/stop.go index 3376d762a..5c7bb9880 100644 --- a/cmd/lotus-provider/stop.go +++ b/cmd/curio/stop.go @@ -10,7 +10,7 @@ import ( var stopCmd = &cli.Command{ Name: "stop", - Usage: "Stop a running lotus provider", + Usage: "Stop a running Curio process", Flags: []cli.Flag{}, Action: func(cctx *cli.Context) error { api, closer, err := lcli.GetAPI(cctx) diff --git a/cmd/lotus-provider/storage.go b/cmd/curio/storage.go similarity index 97% rename from cmd/lotus-provider/storage.go rename to cmd/curio/storage.go index 987501816..7b57e9438 100644 --- a/cmd/lotus-provider/storage.go +++ b/cmd/curio/storage.go @@ -23,7 +23,7 @@ import ( "github.com/filecoin-project/lotus/chain/types" lcli "github.com/filecoin-project/lotus/cli" - "github.com/filecoin-project/lotus/cmd/lotus-provider/rpc" + "github.com/filecoin-project/lotus/cmd/curio/rpc" "github.com/filecoin-project/lotus/storage/sealer/fsutil" "github.com/filecoin-project/lotus/storage/sealer/storiface" ) @@ -104,7 +104,7 @@ over time }, }, Action: func(cctx *cli.Context) error { - minerApi, closer, err := rpc.GetProviderAPI(cctx) + minerApi, closer, err := rpc.GetCurioAPI(cctx) if err != nil { return err } @@ -182,7 +182,7 @@ var storageDetachCmd = &cli.Command{ }, ArgsUsage: "[path]", Action: func(cctx *cli.Context) error { - minerApi, closer, err := rpc.GetProviderAPI(cctx) + minerApi, closer, err := rpc.GetCurioAPI(cctx) if err != nil { return err } @@ -213,7 +213,7 @@ var storageListCmd = &cli.Command{ //storageListSectorsCmd, }, Action: func(cctx *cli.Context) error { - minerApi, closer, err := rpc.GetProviderAPI(cctx) + minerApi, closer, err := rpc.GetCurioAPI(cctx) if err != nil { return err } @@ -412,7 +412,7 @@ var storageFindCmd = &cli.Command{ Usage: "find sector in the storage system", ArgsUsage: "[miner address] [sector number]", Action: func(cctx *cli.Context) error { - minerApi, closer, err := rpc.GetProviderAPI(cctx) + minerApi, closer, err := rpc.GetCurioAPI(cctx) if err != nil { return err } diff --git a/cmd/lotus-provider/tasks/tasks.go b/cmd/curio/tasks/tasks.go similarity index 64% rename from cmd/lotus-provider/tasks/tasks.go rename to cmd/curio/tasks/tasks.go index 6ecd680d0..cdda9d199 100644 --- a/cmd/lotus-provider/tasks/tasks.go +++ b/cmd/curio/tasks/tasks.go @@ -1,4 +1,4 @@ -// Package tasks contains tasks that can be run by the lotus-provider command. +// Package tasks contains tasks that can be run by the curio command. package tasks import ( @@ -8,18 +8,18 @@ import ( "github.com/samber/lo" "golang.org/x/xerrors" - "github.com/filecoin-project/lotus/cmd/lotus-provider/deps" + "github.com/filecoin-project/lotus/cmd/curio/deps" + curio "github.com/filecoin-project/lotus/curiosrc" + "github.com/filecoin-project/lotus/curiosrc/chainsched" + "github.com/filecoin-project/lotus/curiosrc/ffi" + "github.com/filecoin-project/lotus/curiosrc/message" + "github.com/filecoin-project/lotus/curiosrc/seal" + "github.com/filecoin-project/lotus/curiosrc/winning" "github.com/filecoin-project/lotus/lib/harmony/harmonytask" "github.com/filecoin-project/lotus/node/modules" - "github.com/filecoin-project/lotus/provider" - "github.com/filecoin-project/lotus/provider/chainsched" - "github.com/filecoin-project/lotus/provider/lpffi" - "github.com/filecoin-project/lotus/provider/lpmessage" - "github.com/filecoin-project/lotus/provider/lpseal" - "github.com/filecoin-project/lotus/provider/lpwinning" ) -var log = logging.Logger("lotus-provider/deps") +var log = logging.Logger("curio/deps") func StartTasks(ctx context.Context, dependencies *deps.Deps) (*harmonytask.TaskEngine, error) { cfg := dependencies.Cfg @@ -34,7 +34,7 @@ func StartTasks(ctx context.Context, dependencies *deps.Deps) (*harmonytask.Task si := dependencies.Si var activeTasks []harmonytask.TaskInterface - sender, sendTask := lpmessage.NewSender(full, full, db) + sender, sendTask := message.NewSender(full, full, db) activeTasks = append(activeTasks, sendTask) chainSched := chainsched.New(full) @@ -48,8 +48,10 @@ func StartTasks(ctx context.Context, dependencies *deps.Deps) (*harmonytask.Task // PoSt if cfg.Subsystems.EnableWindowPost { - wdPostTask, wdPoStSubmitTask, derlareRecoverTask, err := provider.WindowPostScheduler(ctx, cfg.Fees, cfg.Proving, full, verif, lw, sender, - chainSched, as, maddrs, db, stor, si, cfg.Subsystems.WindowPostMaxTasks) + wdPostTask, wdPoStSubmitTask, derlareRecoverTask, err := curio.WindowPostScheduler( + ctx, cfg.Fees, cfg.Proving, full, verif, lw, sender, chainSched, + as, maddrs, db, stor, si, cfg.Subsystems.WindowPostMaxTasks) + if err != nil { return nil, err } @@ -58,7 +60,7 @@ func StartTasks(ctx context.Context, dependencies *deps.Deps) (*harmonytask.Task } if cfg.Subsystems.EnableWinningPost { - winPoStTask := lpwinning.NewWinPostTask(cfg.Subsystems.WinningPostMaxTasks, db, lw, verif, full, maddrs) + winPoStTask := winning.NewWinPostTask(cfg.Subsystems.WinningPostMaxTasks, db, lw, verif, full, maddrs) activeTasks = append(activeTasks, winPoStTask) needProofParams = true } @@ -73,40 +75,40 @@ func StartTasks(ctx context.Context, dependencies *deps.Deps) (*harmonytask.Task { // Sealing - var sp *lpseal.SealPoller - var slr *lpffi.SealCalls + var sp *seal.SealPoller + var slr *ffi.SealCalls if hasAnySealingTask { - sp = lpseal.NewPoller(db, full) + sp = seal.NewPoller(db, full) go sp.RunPoller(ctx) - slr = lpffi.NewSealCalls(stor, lstor, si) + slr = ffi.NewSealCalls(stor, lstor, si) } // NOTE: Tasks with the LEAST priority are at the top if cfg.Subsystems.EnableSealSDR { - sdrTask := lpseal.NewSDRTask(full, db, sp, slr, cfg.Subsystems.SealSDRMaxTasks) + sdrTask := seal.NewSDRTask(full, db, sp, slr, cfg.Subsystems.SealSDRMaxTasks) activeTasks = append(activeTasks, sdrTask) } if cfg.Subsystems.EnableSealSDRTrees { - treesTask := lpseal.NewTreesTask(sp, db, slr, cfg.Subsystems.SealSDRTreesMaxTasks) - finalizeTask := lpseal.NewFinalizeTask(cfg.Subsystems.FinalizeMaxTasks, sp, slr, db) + treesTask := seal.NewTreesTask(sp, db, slr, cfg.Subsystems.SealSDRTreesMaxTasks) + finalizeTask := seal.NewFinalizeTask(cfg.Subsystems.FinalizeMaxTasks, sp, slr, db) activeTasks = append(activeTasks, treesTask, finalizeTask) } if cfg.Subsystems.EnableSendPrecommitMsg { - precommitTask := lpseal.NewSubmitPrecommitTask(sp, db, full, sender, as, cfg.Fees.MaxPreCommitGasFee) + precommitTask := seal.NewSubmitPrecommitTask(sp, db, full, sender, as, cfg.Fees.MaxPreCommitGasFee) activeTasks = append(activeTasks, precommitTask) } if cfg.Subsystems.EnablePoRepProof { - porepTask := lpseal.NewPoRepTask(db, full, sp, slr, cfg.Subsystems.PoRepProofMaxTasks) + porepTask := seal.NewPoRepTask(db, full, sp, slr, cfg.Subsystems.PoRepProofMaxTasks) activeTasks = append(activeTasks, porepTask) needProofParams = true } if cfg.Subsystems.EnableMoveStorage { - moveStorageTask := lpseal.NewMoveStorageTask(sp, slr, db, cfg.Subsystems.MoveStorageMaxTasks) + moveStorageTask := seal.NewMoveStorageTask(sp, slr, db, cfg.Subsystems.MoveStorageMaxTasks) activeTasks = append(activeTasks, moveStorageTask) } if cfg.Subsystems.EnableSendCommitMsg { - commitTask := lpseal.NewSubmitCommitTask(sp, db, full, sender, as, cfg.Fees.MaxCommitGasFee) + commitTask := seal.NewSubmitCommitTask(sp, db, full, sender, as, cfg.Fees.MaxCommitGasFee) activeTasks = append(activeTasks, commitTask) } } @@ -134,7 +136,7 @@ func StartTasks(ctx context.Context, dependencies *deps.Deps) (*harmonytask.Task } if hasAnySealingTask { - watcher, err := lpmessage.NewMessageWatcher(db, ht, chainSched, full) + watcher, err := message.NewMessageWatcher(db, ht, chainSched, full) if err != nil { return nil, err } diff --git a/cmd/lotus-provider/config_migrate.go b/cmd/lotus-provider/config_migrate.go deleted file mode 100644 index 572a798a5..000000000 --- a/cmd/lotus-provider/config_migrate.go +++ /dev/null @@ -1,254 +0,0 @@ -package main - -import ( - "bytes" - "context" - "encoding/base64" - "errors" - "fmt" - "os" - "path" - "strings" - - "github.com/BurntSushi/toml" - "github.com/fatih/color" - "github.com/ipfs/go-datastore" - "github.com/samber/lo" - "github.com/urfave/cli/v2" - "golang.org/x/xerrors" - - "github.com/filecoin-project/go-address" - - cliutil "github.com/filecoin-project/lotus/cli/util" - "github.com/filecoin-project/lotus/cmd/lotus-provider/deps" - "github.com/filecoin-project/lotus/lib/harmony/harmonydb" - "github.com/filecoin-project/lotus/node/config" - "github.com/filecoin-project/lotus/node/modules" - "github.com/filecoin-project/lotus/node/repo" -) - -var configMigrateCmd = &cli.Command{ - Name: "from-miner", - Usage: "Express a database config (for lotus-provider) from an existing miner.", - Description: "Express a database config (for lotus-provider) from an existing miner.", - Flags: []cli.Flag{ - &cli.StringFlag{ - Name: FlagMinerRepo, - Aliases: []string{FlagMinerRepoDeprecation}, - EnvVars: []string{"LOTUS_MINER_PATH", "LOTUS_STORAGE_PATH"}, - Value: "~/.lotusminer", - Usage: "Miner repo path", - }, - &cli.StringFlag{ - Name: "repo", - EnvVars: []string{"LOTUS_PATH"}, - Hidden: true, - Value: "~/.lotus", - }, - &cli.StringFlag{ - Name: "to-layer", - Aliases: []string{"t"}, - Usage: "The layer name for this data push. 'base' is recommended for single-miner setup.", - }, - &cli.BoolFlag{ - Name: "overwrite", - Aliases: []string{"o"}, - Usage: "Use this with --to-layer to replace an existing layer", - }, - }, - Action: fromMiner, -} - -const ( - FlagMinerRepo = "miner-repo" -) - -const FlagMinerRepoDeprecation = "storagerepo" - -func fromMiner(cctx *cli.Context) (err error) { - ctx := context.Background() - cliCommandColor := color.New(color.FgHiBlue).SprintFunc() - configColor := color.New(color.FgHiGreen).SprintFunc() - - r, err := repo.NewFS(cctx.String(FlagMinerRepo)) - if err != nil { - return err - } - - ok, err := r.Exists() - if err != nil { - return err - } - - if !ok { - return fmt.Errorf("repo not initialized") - } - - lr, err := r.LockRO(repo.StorageMiner) - if err != nil { - return fmt.Errorf("locking repo: %w", err) - } - defer func() { _ = lr.Close() }() - - cfgNode, err := lr.Config() - if err != nil { - return fmt.Errorf("getting node config: %w", err) - } - smCfg := cfgNode.(*config.StorageMiner) - - db, err := harmonydb.NewFromConfig(smCfg.HarmonyDB) - if err != nil { - return fmt.Errorf("could not reach the database. Ensure the Miner config toml's HarmonyDB entry"+ - " is setup to reach Yugabyte correctly: %w", err) - } - - var titles []string - err = db.Select(ctx, &titles, `SELECT title FROM harmony_config WHERE LENGTH(config) > 0`) - if err != nil { - return fmt.Errorf("miner cannot reach the db. Ensure the config toml's HarmonyDB entry"+ - " is setup to reach Yugabyte correctly: %s", err.Error()) - } - name := cctx.String("to-layer") - if name == "" { - name = fmt.Sprintf("mig%d", len(titles)) - } else { - if lo.Contains(titles, name) && !cctx.Bool("overwrite") { - return errors.New("the overwrite flag is needed to replace existing layer: " + name) - } - } - msg := "Layer " + configColor(name) + ` created. ` - - // Copy over identical settings: - - buf, err := os.ReadFile(path.Join(lr.Path(), "config.toml")) - if err != nil { - return fmt.Errorf("could not read config.toml: %w", err) - } - lpCfg := config.DefaultLotusProvider() - _, err = deps.LoadConfigWithUpgrades(string(buf), lpCfg) - if err != nil { - return fmt.Errorf("could not decode toml: %w", err) - } - - // Populate Miner Address - mmeta, err := lr.Datastore(ctx, "/metadata") - if err != nil { - return xerrors.Errorf("opening miner metadata datastore: %w", err) - } - defer func() { - _ = mmeta.Close() - }() - - maddrBytes, err := mmeta.Get(ctx, datastore.NewKey("miner-address")) - if err != nil { - return xerrors.Errorf("getting miner address datastore entry: %w", err) - } - - addr, err := address.NewFromBytes(maddrBytes) - if err != nil { - return xerrors.Errorf("parsing miner actor address: %w", err) - } - - lpCfg.Addresses = []config.LotusProviderAddresses{{ - MinerAddresses: []string{addr.String()}, - }} - - ks, err := lr.KeyStore() - if err != nil { - return xerrors.Errorf("keystore err: %w", err) - } - js, err := ks.Get(modules.JWTSecretName) - if err != nil { - return xerrors.Errorf("error getting JWTSecretName: %w", err) - } - - lpCfg.Apis.StorageRPCSecret = base64.StdEncoding.EncodeToString(js.PrivateKey) - - // Populate API Key - _, header, err := cliutil.GetRawAPI(cctx, repo.FullNode, "v0") - if err != nil { - return fmt.Errorf("cannot read API: %w", err) - } - - ainfo, err := cliutil.GetAPIInfo(&cli.Context{}, repo.FullNode) - if err != nil { - return xerrors.Errorf(`could not get API info for FullNode: %w - Set the environment variable to the value of "lotus auth api-info --perm=admin"`, err) - } - lpCfg.Apis.ChainApiInfo = []string{header.Get("Authorization")[7:] + ":" + ainfo.Addr} - - // WindowPoSt message - msg += "\n!! Before running lotus-provider with Window PoSt enabled, ensure any miner/worker answering of WindowPost is disabled by " + - "(on Miner) " + configColor("DisableBuiltinWindowPoSt=true") + " and (on Workers) not enabling windowpost on CLI or via " + - "environment variable " + configColor("LOTUS_WORKER_WINDOWPOST") + "." - - // WinningPoSt message - msg += "\n!! Before running lotus-provider with Winning PoSt enabled, ensure any miner/worker answering of WinningPost is disabled by " + - "(on Miner) " + configColor("DisableBuiltinWinningPoSt=true") + " and (on Workers) not enabling winningpost on CLI or via " + - "environment variable " + configColor("LOTUS_WORKER_WINNINGPOST") + "." - - // Express as configTOML - configTOML := &bytes.Buffer{} - if err = toml.NewEncoder(configTOML).Encode(lpCfg); err != nil { - return err - } - - if !lo.Contains(titles, "base") { - cfg, err := getDefaultConfig(true) - if err != nil { - return xerrors.Errorf("Cannot get default config: %w", err) - } - _, err = db.Exec(ctx, "INSERT INTO harmony_config (title, config) VALUES ('base', $1)", cfg) - - if err != nil { - return err - } - } - - if cctx.Bool("overwrite") { - i, err := db.Exec(ctx, "DELETE FROM harmony_config WHERE title=$1", name) - if i != 0 { - fmt.Println("Overwriting existing layer") - } - if err != nil { - fmt.Println("Got error while deleting existing layer: " + err.Error()) - } - } - - _, err = db.Exec(ctx, "INSERT INTO harmony_config (title, config) VALUES ($1, $2)", name, configTOML.String()) - if err != nil { - return err - } - - dbSettings := "" - def := config.DefaultStorageMiner().HarmonyDB - if def.Hosts[0] != smCfg.HarmonyDB.Hosts[0] { - dbSettings += ` --db-host="` + strings.Join(smCfg.HarmonyDB.Hosts, ",") + `"` - } - if def.Port != smCfg.HarmonyDB.Port { - dbSettings += " --db-port=" + smCfg.HarmonyDB.Port - } - if def.Username != smCfg.HarmonyDB.Username { - dbSettings += ` --db-user="` + smCfg.HarmonyDB.Username + `"` - } - if def.Password != smCfg.HarmonyDB.Password { - dbSettings += ` --db-password="` + smCfg.HarmonyDB.Password + `"` - } - if def.Database != smCfg.HarmonyDB.Database { - dbSettings += ` --db-name="` + smCfg.HarmonyDB.Database + `"` - } - - var layerMaybe string - if name != "base" { - layerMaybe = "--layer=" + name - } - - msg += ` -To work with the config: -` + cliCommandColor(`lotus-provider `+dbSettings+` config help `) - msg += ` -To run Lotus Provider: in its own machine or cgroup without other files, use the command: -` + cliCommandColor(`lotus-provider `+dbSettings+` run `+layerMaybe) - fmt.Println(msg) - return nil -} diff --git a/cmd/lotus-shed/lpdeal.go b/cmd/lotus-shed/deal.go similarity index 98% rename from cmd/lotus-shed/lpdeal.go rename to cmd/lotus-shed/deal.go index 01f886015..f8ef54563 100644 --- a/cmd/lotus-shed/lpdeal.go +++ b/cmd/lotus-shed/deal.go @@ -34,13 +34,13 @@ import ( "github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/chain/types" lcli "github.com/filecoin-project/lotus/cli" - "github.com/filecoin-project/lotus/cmd/lotus-provider/deps" + "github.com/filecoin-project/lotus/cmd/curio/deps" + cumarket "github.com/filecoin-project/lotus/curiosrc/market" + "github.com/filecoin-project/lotus/curiosrc/market/fakelm" "github.com/filecoin-project/lotus/lib/must" "github.com/filecoin-project/lotus/lib/nullreader" "github.com/filecoin-project/lotus/metrics/proxy" "github.com/filecoin-project/lotus/node" - "github.com/filecoin-project/lotus/provider/lpmarket" - "github.com/filecoin-project/lotus/provider/lpmarket/fakelm" "github.com/filecoin-project/lotus/storage/paths" "github.com/filecoin-project/lotus/storage/sealer/storiface" ) @@ -88,7 +88,7 @@ var lpUtilStartDealCmd = &cli.Command{ } // open rpc - var rpc api.LotusProviderStruct + var rpc api.CurioStruct closer2, err := jsonrpc.NewMergeClient(ctx, cctx.String("provider-rpc"), "Filecoin", []interface{}{&rpc.Internal}, nil) if err != nil { return xerrors.Errorf("open rpc: %w", err) @@ -368,7 +368,7 @@ var lpBoostProxyCmd = &cli.Command{ defer closer() - pin := lpmarket.NewPieceIngester(db, full) + pin := cumarket.NewPieceIngester(db, full) si := paths.NewDBIndex(nil, db) diff --git a/provider/address.go b/curiosrc/address.go similarity index 89% rename from provider/address.go rename to curiosrc/address.go index 84a10a5d7..6d1738f2d 100644 --- a/provider/address.go +++ b/curiosrc/address.go @@ -1,4 +1,4 @@ -package provider +package curio import ( "golang.org/x/xerrors" @@ -6,11 +6,11 @@ import ( "github.com/filecoin-project/go-address" "github.com/filecoin-project/lotus/api" + "github.com/filecoin-project/lotus/curiosrc/multictladdr" "github.com/filecoin-project/lotus/node/config" - "github.com/filecoin-project/lotus/provider/multictladdr" ) -func AddressSelector(addrConf []config.LotusProviderAddresses) func() (*multictladdr.MultiAddressSelector, error) { +func AddressSelector(addrConf []config.CurioAddresses) func() (*multictladdr.MultiAddressSelector, error) { return func() (*multictladdr.MultiAddressSelector, error) { as := &multictladdr.MultiAddressSelector{ MinerMap: make(map[address.Address]api.AddressConfig), diff --git a/curiosrc/builder.go b/curiosrc/builder.go new file mode 100644 index 000000000..3a3294a29 --- /dev/null +++ b/curiosrc/builder.go @@ -0,0 +1,46 @@ +package curio + +import ( + "context" + "time" + + "github.com/filecoin-project/lotus/api" + "github.com/filecoin-project/lotus/curiosrc/chainsched" + "github.com/filecoin-project/lotus/curiosrc/message" + "github.com/filecoin-project/lotus/curiosrc/multictladdr" + "github.com/filecoin-project/lotus/curiosrc/window" + "github.com/filecoin-project/lotus/lib/harmony/harmonydb" + "github.com/filecoin-project/lotus/node/config" + dtypes "github.com/filecoin-project/lotus/node/modules/dtypes" + "github.com/filecoin-project/lotus/storage/paths" + "github.com/filecoin-project/lotus/storage/sealer" + "github.com/filecoin-project/lotus/storage/sealer/storiface" +) + +//var log = logging.Logger("provider") + +func WindowPostScheduler(ctx context.Context, fc config.CurioFees, pc config.ProvingConfig, + api api.FullNode, verif storiface.Verifier, lw *sealer.LocalWorker, sender *message.Sender, chainSched *chainsched.CurioChainSched, + as *multictladdr.MultiAddressSelector, addresses map[dtypes.MinerAddress]bool, db *harmonydb.DB, + stor paths.Store, idx paths.SectorIndex, max int) (*window.WdPostTask, *window.WdPostSubmitTask, *window.WdPostRecoverDeclareTask, error) { + + // todo config + ft := window.NewSimpleFaultTracker(stor, idx, pc.ParallelCheckLimit, time.Duration(pc.SingleCheckTimeout), time.Duration(pc.PartitionCheckTimeout)) + + computeTask, err := window.NewWdPostTask(db, api, ft, lw, verif, chainSched, addresses, max) + if err != nil { + return nil, nil, nil, err + } + + submitTask, err := window.NewWdPostSubmitTask(chainSched, sender, db, api, fc.MaxWindowPoStGasFee, as) + if err != nil { + return nil, nil, nil, err + } + + recoverTask, err := window.NewWdPostRecoverDeclareTask(sender, db, api, ft, as, chainSched, fc.MaxWindowPoStGasFee, addresses) + if err != nil { + return nil, nil, nil, err + } + + return computeTask, submitTask, recoverTask, nil +} diff --git a/provider/chainsched/chain_sched.go b/curiosrc/chainsched/chain_sched.go similarity index 77% rename from provider/chainsched/chain_sched.go rename to curiosrc/chainsched/chain_sched.go index 238baac54..42a387fbc 100644 --- a/provider/chainsched/chain_sched.go +++ b/curiosrc/chainsched/chain_sched.go @@ -14,29 +14,29 @@ import ( "github.com/filecoin-project/lotus/chain/types" ) -var log = logging.Logger("chainsched") +var log = logging.Logger("curio/chainsched") type NodeAPI interface { ChainHead(context.Context) (*types.TipSet, error) ChainNotify(context.Context) (<-chan []*api.HeadChange, error) } -type ProviderChainSched struct { +type CurioChainSched struct { api NodeAPI callbacks []UpdateFunc started bool } -func New(api NodeAPI) *ProviderChainSched { - return &ProviderChainSched{ +func New(api NodeAPI) *CurioChainSched { + return &CurioChainSched{ api: api, } } type UpdateFunc func(ctx context.Context, revert, apply *types.TipSet) error -func (s *ProviderChainSched) AddHandler(ch UpdateFunc) error { +func (s *CurioChainSched) AddHandler(ch UpdateFunc) error { if s.started { return xerrors.Errorf("cannot add handler after start") } @@ -45,7 +45,7 @@ func (s *ProviderChainSched) AddHandler(ch UpdateFunc) error { return nil } -func (s *ProviderChainSched) Run(ctx context.Context) { +func (s *CurioChainSched) Run(ctx context.Context) { s.started = true var ( @@ -88,7 +88,7 @@ func (s *ProviderChainSched) Run(ctx context.Context) { continue } - ctx, span := trace.StartSpan(ctx, "ProviderChainSched.headChange") + ctx, span := trace.StartSpan(ctx, "CurioChainSched.headChange") s.update(ctx, nil, chg.Val) @@ -97,7 +97,7 @@ func (s *ProviderChainSched) Run(ctx context.Context) { continue } - ctx, span := trace.StartSpan(ctx, "ProviderChainSched.headChange") + ctx, span := trace.StartSpan(ctx, "CurioChainSched.headChange") var lowest, highest *types.TipSet = nil, nil @@ -122,15 +122,15 @@ func (s *ProviderChainSched) Run(ctx context.Context) { } } -func (s *ProviderChainSched) update(ctx context.Context, revert, apply *types.TipSet) { +func (s *CurioChainSched) update(ctx context.Context, revert, apply *types.TipSet) { if apply == nil { - log.Error("no new tipset in ProviderChainSched.update") + log.Error("no new tipset in CurioChainSched.update") return } for _, ch := range s.callbacks { if err := ch(ctx, revert, apply); err != nil { - log.Errorf("handling head updates in provider chain sched: %+v", err) + log.Errorf("handling head updates in curio chain sched: %+v", err) } } } diff --git a/provider/lpffi/sdr_funcs.go b/curiosrc/ffi/sdr_funcs.go similarity index 98% rename from provider/lpffi/sdr_funcs.go rename to curiosrc/ffi/sdr_funcs.go index 05a7bbd80..0520fd555 100644 --- a/provider/lpffi/sdr_funcs.go +++ b/curiosrc/ffi/sdr_funcs.go @@ -1,4 +1,4 @@ -package lpffi +package ffi import ( "context" @@ -19,8 +19,8 @@ import ( "github.com/filecoin-project/go-state-types/abi" proof2 "github.com/filecoin-project/go-state-types/proof" + "github.com/filecoin-project/lotus/curiosrc/proof" "github.com/filecoin-project/lotus/lib/harmony/harmonytask" - "github.com/filecoin-project/lotus/provider/lpproof" "github.com/filecoin-project/lotus/storage/paths" "github.com/filecoin-project/lotus/storage/sealer/proofpaths" "github.com/filecoin-project/lotus/storage/sealer/storiface" @@ -151,7 +151,7 @@ func (sb *SealCalls) TreeD(ctx context.Context, sector storiface.SectorRef, size } defer releaseSector() - return lpproof.BuildTreeD(data, unpaddedData, filepath.Join(paths.Cache, proofpaths.TreeDName), size) + return proof.BuildTreeD(data, unpaddedData, filepath.Join(paths.Cache, proofpaths.TreeDName), size) } func (sb *SealCalls) TreeRC(ctx context.Context, sector storiface.SectorRef, unsealed cid.Cid) (cid.Cid, cid.Cid, error) { diff --git a/provider/lpffi/task_storage.go b/curiosrc/ffi/task_storage.go similarity index 99% rename from provider/lpffi/task_storage.go rename to curiosrc/ffi/task_storage.go index 01d6671c7..ddc5e00a3 100644 --- a/provider/lpffi/task_storage.go +++ b/curiosrc/ffi/task_storage.go @@ -1,4 +1,4 @@ -package lpffi +package ffi import ( "context" diff --git a/provider/lpmarket/deal_ingest.go b/curiosrc/market/deal_ingest.go similarity index 95% rename from provider/lpmarket/deal_ingest.go rename to curiosrc/market/deal_ingest.go index 0858cf801..ea382717a 100644 --- a/provider/lpmarket/deal_ingest.go +++ b/curiosrc/market/deal_ingest.go @@ -1,4 +1,4 @@ -package lpmarket +package market import ( "context" @@ -17,8 +17,8 @@ import ( "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/chain/actors/builtin/miner" "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/curiosrc/seal" "github.com/filecoin-project/lotus/lib/harmony/harmonydb" - "github.com/filecoin-project/lotus/provider/lpseal" ) type Ingester interface { @@ -72,7 +72,7 @@ func (p *PieceIngester) AllocatePieceToSector(ctx context.Context, maddr address return api.SectorOffset{}, xerrors.Errorf("getting miner ID: %w", err) } - num, err := lpseal.AllocateSectorNumbers(ctx, p.api, p.db, maddr, 1, func(tx *harmonydb.Tx, numbers []abi.SectorNumber) (bool, error) { + num, err := seal.AllocateSectorNumbers(ctx, p.api, p.db, maddr, 1, func(tx *harmonydb.Tx, numbers []abi.SectorNumber) (bool, error) { if len(numbers) != 1 { return false, xerrors.Errorf("expected one sector number") } diff --git a/provider/lpmarket/fakelm/iface.go b/curiosrc/market/fakelm/iface.go similarity index 100% rename from provider/lpmarket/fakelm/iface.go rename to curiosrc/market/fakelm/iface.go diff --git a/provider/lpmarket/fakelm/lmimpl.go b/curiosrc/market/fakelm/lmimpl.go similarity index 98% rename from provider/lpmarket/fakelm/lmimpl.go rename to curiosrc/market/fakelm/lmimpl.go index abbc4d04d..1f6c5b91d 100644 --- a/provider/lpmarket/fakelm/lmimpl.go +++ b/curiosrc/market/fakelm/lmimpl.go @@ -20,9 +20,9 @@ import ( "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/chain/actors/builtin/miner" "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/curiosrc/market" "github.com/filecoin-project/lotus/lib/harmony/harmonydb" "github.com/filecoin-project/lotus/node/config" - "github.com/filecoin-project/lotus/provider/lpmarket" "github.com/filecoin-project/lotus/storage/paths" sealing "github.com/filecoin-project/lotus/storage/pipeline" "github.com/filecoin-project/lotus/storage/sealer/storiface" @@ -37,12 +37,12 @@ type LMRPCProvider struct { ssize abi.SectorSize - pi lpmarket.Ingester + pi market.Ingester db *harmonydb.DB confLayer string } -func NewLMRPCProvider(si paths.SectorIndex, full api.FullNode, maddr address.Address, minerID abi.ActorID, ssize abi.SectorSize, pi lpmarket.Ingester, db *harmonydb.DB, confLayer string) *LMRPCProvider { +func NewLMRPCProvider(si paths.SectorIndex, full api.FullNode, maddr address.Address, minerID abi.ActorID, ssize abi.SectorSize, pi market.Ingester, db *harmonydb.DB, confLayer string) *LMRPCProvider { return &LMRPCProvider{ si: si, full: full, @@ -343,7 +343,7 @@ func (l *LMRPCProvider) AuthNew(ctx context.Context, perms []auth.Permission) ([ return nil, xerrors.Errorf("no harmony config found") } - lp := config.DefaultLotusProvider() + lp := config.DefaultCurioConfig() if _, err := toml.Decode(cs[0].Config, lp); err != nil { return nil, xerrors.Errorf("decode harmony config: %w", err) } diff --git a/provider/lpmessage/sender.go b/curiosrc/message/sender.go similarity index 98% rename from provider/lpmessage/sender.go rename to curiosrc/message/sender.go index 97f889ecc..614bc0be2 100644 --- a/provider/lpmessage/sender.go +++ b/curiosrc/message/sender.go @@ -1,4 +1,4 @@ -package lpmessage +package message import ( "bytes" @@ -22,7 +22,7 @@ import ( "github.com/filecoin-project/lotus/lib/promise" ) -var log = logging.Logger("lpmessage") +var log = logging.Logger("curio/message") var SendLockedWait = 100 * time.Millisecond @@ -187,7 +187,7 @@ func (s *SendTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (done b } else { // Note: this handles an unlikely edge-case: // We have previously signed the message but either failed to send it or failed to update the db - // note that when that happens the likely cause is the provider process losing its db connection + // note that when that happens the likely cause is the curio process losing its db connection // or getting killed before it can update the db. In that case the message lock will still be held // so it will be safe to rebroadcast the signed message diff --git a/provider/lpmessage/watch.go b/curiosrc/message/watch.go similarity index 97% rename from provider/lpmessage/watch.go rename to curiosrc/message/watch.go index 1a04a67a1..2253df284 100644 --- a/provider/lpmessage/watch.go +++ b/curiosrc/message/watch.go @@ -1,4 +1,4 @@ -package lpmessage +package message import ( "context" @@ -12,9 +12,9 @@ import ( "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/curiosrc/chainsched" "github.com/filecoin-project/lotus/lib/harmony/harmonydb" "github.com/filecoin-project/lotus/lib/harmony/harmonytask" - "github.com/filecoin-project/lotus/provider/chainsched" ) const MinConfidence = 6 @@ -38,7 +38,7 @@ type MessageWatcher struct { bestTs atomic.Pointer[types.TipSetKey] } -func NewMessageWatcher(db *harmonydb.DB, ht *harmonytask.TaskEngine, pcs *chainsched.ProviderChainSched, api MessageWaiterApi) (*MessageWatcher, error) { +func NewMessageWatcher(db *harmonydb.DB, ht *harmonytask.TaskEngine, pcs *chainsched.CurioChainSched, api MessageWaiterApi) (*MessageWatcher, error) { mw := &MessageWatcher{ db: db, ht: ht, diff --git a/provider/multictladdr/multiaddresses.go b/curiosrc/multictladdr/multiaddresses.go similarity index 97% rename from provider/multictladdr/multiaddresses.go rename to curiosrc/multictladdr/multiaddresses.go index 4f1947729..af751ff17 100644 --- a/provider/multictladdr/multiaddresses.go +++ b/curiosrc/multictladdr/multiaddresses.go @@ -14,7 +14,7 @@ import ( "github.com/filecoin-project/lotus/storage/ctladdr" ) -var log = logging.Logger("multictladdr") +var log = logging.Logger("curio/multictladdr") type MultiAddressSelector struct { MinerMap map[address.Address]api.AddressConfig diff --git a/provider/lpproof/treed_build.go b/curiosrc/proof/treed_build.go similarity index 99% rename from provider/lpproof/treed_build.go rename to curiosrc/proof/treed_build.go index 12d0ee93e..59161caa9 100644 --- a/provider/lpproof/treed_build.go +++ b/curiosrc/proof/treed_build.go @@ -1,4 +1,4 @@ -package lpproof +package proof import ( "io" diff --git a/provider/lpproof/treed_build_test.go b/curiosrc/proof/treed_build_test.go similarity index 99% rename from provider/lpproof/treed_build_test.go rename to curiosrc/proof/treed_build_test.go index f9bbde20a..f69e98322 100644 --- a/provider/lpproof/treed_build_test.go +++ b/curiosrc/proof/treed_build_test.go @@ -1,4 +1,4 @@ -package lpproof +package proof import ( "bufio" diff --git a/provider/lpseal/README.md b/curiosrc/seal/README.md similarity index 100% rename from provider/lpseal/README.md rename to curiosrc/seal/README.md diff --git a/provider/lpseal/poller.go b/curiosrc/seal/poller.go similarity index 99% rename from provider/lpseal/poller.go rename to curiosrc/seal/poller.go index cbdcb3fd9..568280bdb 100644 --- a/provider/lpseal/poller.go +++ b/curiosrc/seal/poller.go @@ -1,4 +1,4 @@ -package lpseal +package seal import ( "context" diff --git a/provider/lpseal/poller_commit_msg.go b/curiosrc/seal/poller_commit_msg.go similarity index 99% rename from provider/lpseal/poller_commit_msg.go rename to curiosrc/seal/poller_commit_msg.go index 19784e719..9a88129b0 100644 --- a/provider/lpseal/poller_commit_msg.go +++ b/curiosrc/seal/poller_commit_msg.go @@ -1,4 +1,4 @@ -package lpseal +package seal import ( "context" diff --git a/provider/lpseal/poller_precommit_msg.go b/curiosrc/seal/poller_precommit_msg.go similarity index 99% rename from provider/lpseal/poller_precommit_msg.go rename to curiosrc/seal/poller_precommit_msg.go index 22deadc0e..4372cbb92 100644 --- a/provider/lpseal/poller_precommit_msg.go +++ b/curiosrc/seal/poller_precommit_msg.go @@ -1,4 +1,4 @@ -package lpseal +package seal import ( "context" diff --git a/provider/lpseal/sector_num_alloc.go b/curiosrc/seal/sector_num_alloc.go similarity index 99% rename from provider/lpseal/sector_num_alloc.go rename to curiosrc/seal/sector_num_alloc.go index 6958a5e6b..010ebee39 100644 --- a/provider/lpseal/sector_num_alloc.go +++ b/curiosrc/seal/sector_num_alloc.go @@ -1,4 +1,4 @@ -package lpseal +package seal import ( "context" diff --git a/provider/lpseal/task_finalize.go b/curiosrc/seal/task_finalize.go similarity index 94% rename from provider/lpseal/task_finalize.go rename to curiosrc/seal/task_finalize.go index 8d425f76a..72610b5e5 100644 --- a/provider/lpseal/task_finalize.go +++ b/curiosrc/seal/task_finalize.go @@ -1,4 +1,4 @@ -package lpseal +package seal import ( "context" @@ -7,21 +7,21 @@ import ( "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/lotus/curiosrc/ffi" "github.com/filecoin-project/lotus/lib/harmony/harmonydb" "github.com/filecoin-project/lotus/lib/harmony/harmonytask" "github.com/filecoin-project/lotus/lib/harmony/resources" - "github.com/filecoin-project/lotus/provider/lpffi" "github.com/filecoin-project/lotus/storage/sealer/storiface" ) type FinalizeTask struct { max int sp *SealPoller - sc *lpffi.SealCalls + sc *ffi.SealCalls db *harmonydb.DB } -func NewFinalizeTask(max int, sp *SealPoller, sc *lpffi.SealCalls, db *harmonydb.DB) *FinalizeTask { +func NewFinalizeTask(max int, sp *SealPoller, sc *ffi.SealCalls, db *harmonydb.DB) *FinalizeTask { return &FinalizeTask{ max: max, sp: sp, @@ -86,7 +86,7 @@ func (f *FinalizeTask) CanAccept(ids []harmonytask.TaskID, engine *harmonytask.T StorageID string `db:"storage_id"` } - if 4 != storiface.FTCache { + if storiface.FTCache != 4 { panic("storiface.FTCache != 4") } diff --git a/provider/lpseal/task_movestorage.go b/curiosrc/seal/task_movestorage.go similarity index 94% rename from provider/lpseal/task_movestorage.go rename to curiosrc/seal/task_movestorage.go index 2085214b7..9092ec829 100644 --- a/provider/lpseal/task_movestorage.go +++ b/curiosrc/seal/task_movestorage.go @@ -1,4 +1,4 @@ -package lpseal +package seal import ( "context" @@ -7,22 +7,22 @@ import ( "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/lotus/curiosrc/ffi" "github.com/filecoin-project/lotus/lib/harmony/harmonydb" "github.com/filecoin-project/lotus/lib/harmony/harmonytask" "github.com/filecoin-project/lotus/lib/harmony/resources" - "github.com/filecoin-project/lotus/provider/lpffi" "github.com/filecoin-project/lotus/storage/sealer/storiface" ) type MoveStorageTask struct { sp *SealPoller - sc *lpffi.SealCalls + sc *ffi.SealCalls db *harmonydb.DB max int } -func NewMoveStorageTask(sp *SealPoller, sc *lpffi.SealCalls, db *harmonydb.DB, max int) *MoveStorageTask { +func NewMoveStorageTask(sp *SealPoller, sc *ffi.SealCalls, db *harmonydb.DB, max int) *MoveStorageTask { return &MoveStorageTask{ max: max, sp: sp, diff --git a/provider/lpseal/task_porep.go b/curiosrc/seal/task_porep.go similarity index 97% rename from provider/lpseal/task_porep.go rename to curiosrc/seal/task_porep.go index ba0283e0c..fb03ce59b 100644 --- a/provider/lpseal/task_porep.go +++ b/curiosrc/seal/task_porep.go @@ -1,4 +1,4 @@ -package lpseal +package seal import ( "bytes" @@ -12,10 +12,10 @@ import ( "github.com/filecoin-project/go-state-types/crypto" "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/curiosrc/ffi" "github.com/filecoin-project/lotus/lib/harmony/harmonydb" "github.com/filecoin-project/lotus/lib/harmony/harmonytask" "github.com/filecoin-project/lotus/lib/harmony/resources" - "github.com/filecoin-project/lotus/provider/lpffi" "github.com/filecoin-project/lotus/storage/sealer/storiface" ) @@ -28,12 +28,12 @@ type PoRepTask struct { db *harmonydb.DB api PoRepAPI sp *SealPoller - sc *lpffi.SealCalls + sc *ffi.SealCalls max int } -func NewPoRepTask(db *harmonydb.DB, api PoRepAPI, sp *SealPoller, sc *lpffi.SealCalls, maxPoRep int) *PoRepTask { +func NewPoRepTask(db *harmonydb.DB, api PoRepAPI, sp *SealPoller, sc *ffi.SealCalls, maxPoRep int) *PoRepTask { return &PoRepTask{ db: db, api: api, diff --git a/provider/lpseal/task_sdr.go b/curiosrc/seal/task_sdr.go similarity index 94% rename from provider/lpseal/task_sdr.go rename to curiosrc/seal/task_sdr.go index 694ff6f46..c9cf55316 100644 --- a/provider/lpseal/task_sdr.go +++ b/curiosrc/seal/task_sdr.go @@ -1,4 +1,4 @@ -package lpseal +package seal import ( "bytes" @@ -16,10 +16,10 @@ import ( "github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/chain/actors/policy" "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/curiosrc/ffi" "github.com/filecoin-project/lotus/lib/harmony/harmonydb" "github.com/filecoin-project/lotus/lib/harmony/harmonytask" "github.com/filecoin-project/lotus/lib/harmony/resources" - "github.com/filecoin-project/lotus/provider/lpffi" "github.com/filecoin-project/lotus/storage/sealer/storiface" ) @@ -35,12 +35,12 @@ type SDRTask struct { db *harmonydb.DB sp *SealPoller - sc *lpffi.SealCalls + sc *ffi.SealCalls max int } -func NewSDRTask(api SDRAPI, db *harmonydb.DB, sp *SealPoller, sc *lpffi.SealCalls, maxSDR int) *SDRTask { +func NewSDRTask(api SDRAPI, db *harmonydb.DB, sp *SealPoller, sc *ffi.SealCalls, maxSDR int) *SDRTask { return &SDRTask{ api: api, db: db, @@ -223,16 +223,16 @@ func (s *SDRTask) Adder(taskFunc harmonytask.AddTaskFunc) { s.sp.pollers[pollerSDR].Set(taskFunc) } -func (s *SDRTask) taskToSector(id harmonytask.TaskID) (lpffi.SectorRef, error) { - var refs []lpffi.SectorRef +func (s *SDRTask) taskToSector(id harmonytask.TaskID) (ffi.SectorRef, error) { + var refs []ffi.SectorRef err := s.db.Select(context.Background(), &refs, `SELECT sp_id, sector_number, reg_seal_proof FROM sectors_sdr_pipeline WHERE task_id_sdr = $1`, id) if err != nil { - return lpffi.SectorRef{}, xerrors.Errorf("getting sector ref: %w", err) + return ffi.SectorRef{}, xerrors.Errorf("getting sector ref: %w", err) } if len(refs) != 1 { - return lpffi.SectorRef{}, xerrors.Errorf("expected 1 sector ref, got %d", len(refs)) + return ffi.SectorRef{}, xerrors.Errorf("expected 1 sector ref, got %d", len(refs)) } return refs[0], nil diff --git a/provider/lpseal/task_submit_commit.go b/curiosrc/seal/task_submit_commit.go similarity index 94% rename from provider/lpseal/task_submit_commit.go rename to curiosrc/seal/task_submit_commit.go index 5b46b7e00..d7f133db7 100644 --- a/provider/lpseal/task_submit_commit.go +++ b/curiosrc/seal/task_submit_commit.go @@ -1,4 +1,4 @@ -package lpseal +package seal import ( "bytes" @@ -14,11 +14,11 @@ import ( "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/chain/actors/builtin/miner" "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/curiosrc/message" + "github.com/filecoin-project/lotus/curiosrc/multictladdr" "github.com/filecoin-project/lotus/lib/harmony/harmonydb" "github.com/filecoin-project/lotus/lib/harmony/harmonytask" "github.com/filecoin-project/lotus/lib/harmony/resources" - "github.com/filecoin-project/lotus/provider/lpmessage" - "github.com/filecoin-project/lotus/provider/multictladdr" "github.com/filecoin-project/lotus/storage/ctladdr" ) @@ -35,13 +35,13 @@ type SubmitCommitTask struct { db *harmonydb.DB api SubmitCommitAPI - sender *lpmessage.Sender + sender *message.Sender as *multictladdr.MultiAddressSelector maxFee types.FIL } -func NewSubmitCommitTask(sp *SealPoller, db *harmonydb.DB, api SubmitCommitAPI, sender *lpmessage.Sender, as *multictladdr.MultiAddressSelector, maxFee types.FIL) *SubmitCommitTask { +func NewSubmitCommitTask(sp *SealPoller, db *harmonydb.DB, api SubmitCommitAPI, sender *message.Sender, as *multictladdr.MultiAddressSelector, maxFee types.FIL) *SubmitCommitTask { return &SubmitCommitTask{ sp: sp, db: db, diff --git a/provider/lpseal/task_submit_precommit.go b/curiosrc/seal/task_submit_precommit.go similarity index 95% rename from provider/lpseal/task_submit_precommit.go rename to curiosrc/seal/task_submit_precommit.go index 9f6233f39..b04473fe7 100644 --- a/provider/lpseal/task_submit_precommit.go +++ b/curiosrc/seal/task_submit_precommit.go @@ -1,4 +1,4 @@ -package lpseal +package seal import ( "bytes" @@ -16,11 +16,11 @@ import ( "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/chain/actors/builtin/miner" "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/curiosrc/message" + "github.com/filecoin-project/lotus/curiosrc/multictladdr" "github.com/filecoin-project/lotus/lib/harmony/harmonydb" "github.com/filecoin-project/lotus/lib/harmony/harmonytask" "github.com/filecoin-project/lotus/lib/harmony/resources" - "github.com/filecoin-project/lotus/provider/lpmessage" - "github.com/filecoin-project/lotus/provider/multictladdr" "github.com/filecoin-project/lotus/storage/ctladdr" ) @@ -34,13 +34,13 @@ type SubmitPrecommitTask struct { sp *SealPoller db *harmonydb.DB api SubmitPrecommitTaskApi - sender *lpmessage.Sender + sender *message.Sender as *multictladdr.MultiAddressSelector maxFee types.FIL } -func NewSubmitPrecommitTask(sp *SealPoller, db *harmonydb.DB, api SubmitPrecommitTaskApi, sender *lpmessage.Sender, as *multictladdr.MultiAddressSelector, maxFee types.FIL) *SubmitPrecommitTask { +func NewSubmitPrecommitTask(sp *SealPoller, db *harmonydb.DB, api SubmitPrecommitTaskApi, sender *message.Sender, as *multictladdr.MultiAddressSelector, maxFee types.FIL) *SubmitPrecommitTask { return &SubmitPrecommitTask{ sp: sp, db: db, diff --git a/provider/lpseal/task_trees.go b/curiosrc/seal/task_trees.go similarity index 97% rename from provider/lpseal/task_trees.go rename to curiosrc/seal/task_trees.go index da0fcf1e9..6afef245b 100644 --- a/provider/lpseal/task_trees.go +++ b/curiosrc/seal/task_trees.go @@ -1,4 +1,4 @@ -package lpseal +package seal import ( "context" @@ -13,10 +13,10 @@ import ( "github.com/filecoin-project/go-padreader" "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/lotus/curiosrc/ffi" "github.com/filecoin-project/lotus/lib/harmony/harmonydb" "github.com/filecoin-project/lotus/lib/harmony/harmonytask" "github.com/filecoin-project/lotus/lib/harmony/resources" - "github.com/filecoin-project/lotus/provider/lpffi" "github.com/filecoin-project/lotus/storage/pipeline/lib/nullreader" "github.com/filecoin-project/lotus/storage/sealer/storiface" ) @@ -24,12 +24,12 @@ import ( type TreesTask struct { sp *SealPoller db *harmonydb.DB - sc *lpffi.SealCalls + sc *ffi.SealCalls max int } -func NewTreesTask(sp *SealPoller, db *harmonydb.DB, sc *lpffi.SealCalls, maxTrees int) *TreesTask { +func NewTreesTask(sp *SealPoller, db *harmonydb.DB, sc *ffi.SealCalls, maxTrees int) *TreesTask { return &TreesTask{ sp: sp, db: db, diff --git a/provider/lpseal/task_trees_test.go b/curiosrc/seal/task_trees_test.go similarity index 99% rename from provider/lpseal/task_trees_test.go rename to curiosrc/seal/task_trees_test.go index 204fa57fc..b65ddd4e8 100644 --- a/provider/lpseal/task_trees_test.go +++ b/curiosrc/seal/task_trees_test.go @@ -1,4 +1,4 @@ -package lpseal +package seal import ( "io" diff --git a/provider/lpweb/api/debug/debug.go b/curiosrc/web/api/debug/debug.go similarity index 97% rename from provider/lpweb/api/debug/debug.go rename to curiosrc/web/api/debug/debug.go index 845684519..c0e89ab8e 100644 --- a/provider/lpweb/api/debug/debug.go +++ b/curiosrc/web/api/debug/debug.go @@ -1,4 +1,4 @@ -// Package debug provides the API for various debug endpoints in lotus-provider. +// Package debug provides the API for various debug endpoints in curio. package debug import ( @@ -18,10 +18,10 @@ import ( "github.com/filecoin-project/lotus/api/client" "github.com/filecoin-project/lotus/build" cliutil "github.com/filecoin-project/lotus/cli/util" - "github.com/filecoin-project/lotus/cmd/lotus-provider/deps" + "github.com/filecoin-project/lotus/cmd/curio/deps" ) -var log = logging.Logger("lp/web/debug") +var log = logging.Logger("curio/web/debug") type debug struct { *deps.Deps diff --git a/curiosrc/web/api/routes.go b/curiosrc/web/api/routes.go new file mode 100644 index 000000000..c030f7d53 --- /dev/null +++ b/curiosrc/web/api/routes.go @@ -0,0 +1,13 @@ +// Package api provides the HTTP API for the lotus curio web gui. +package api + +import ( + "github.com/gorilla/mux" + + "github.com/filecoin-project/lotus/cmd/curio/deps" + "github.com/filecoin-project/lotus/curiosrc/web/api/debug" +) + +func Routes(r *mux.Router, deps *deps.Deps) { + debug.Routes(r.PathPrefix("/debug").Subrouter(), deps) +} diff --git a/provider/lpweb/hapi/robust_rpc.go b/curiosrc/web/hapi/robust_rpc.go similarity index 100% rename from provider/lpweb/hapi/robust_rpc.go rename to curiosrc/web/hapi/robust_rpc.go diff --git a/provider/lpweb/hapi/routes.go b/curiosrc/web/hapi/routes.go similarity index 89% rename from provider/lpweb/hapi/routes.go rename to curiosrc/web/hapi/routes.go index 2e752935d..fd2f24e94 100644 --- a/provider/lpweb/hapi/routes.go +++ b/curiosrc/web/hapi/routes.go @@ -8,7 +8,7 @@ import ( logging "github.com/ipfs/go-log/v2" "golang.org/x/xerrors" - "github.com/filecoin-project/lotus/cmd/lotus-provider/deps" + "github.com/filecoin-project/lotus/cmd/curio/deps" ) //go:embed web/* @@ -39,4 +39,4 @@ func Routes(r *mux.Router, deps *deps.Deps) error { return nil } -var log = logging.Logger("lpweb") +var log = logging.Logger("curio/web") diff --git a/provider/lpweb/hapi/simpleinfo.go b/curiosrc/web/hapi/simpleinfo.go similarity index 99% rename from provider/lpweb/hapi/simpleinfo.go rename to curiosrc/web/hapi/simpleinfo.go index ff76ae990..e639ef427 100644 --- a/provider/lpweb/hapi/simpleinfo.go +++ b/curiosrc/web/hapi/simpleinfo.go @@ -98,7 +98,7 @@ var templateDev = os.Getenv("LOTUS_WEB_DEV") == "1" func (a *app) executeTemplate(w http.ResponseWriter, name string, data interface{}) { if templateDev { - fs := os.DirFS("./provider/lpweb/hapi/web") + fs := os.DirFS("./cmd/curio/web/hapi/web") a.t = template.Must(template.ParseFS(fs, "*")) } if err := a.t.ExecuteTemplate(w, name, data); err != nil { diff --git a/provider/lpweb/hapi/simpleinfo_pipeline_porep.go b/curiosrc/web/hapi/simpleinfo_pipeline_porep.go similarity index 100% rename from provider/lpweb/hapi/simpleinfo_pipeline_porep.go rename to curiosrc/web/hapi/simpleinfo_pipeline_porep.go diff --git a/provider/lpweb/hapi/watch_actor.go b/curiosrc/web/hapi/watch_actor.go similarity index 100% rename from provider/lpweb/hapi/watch_actor.go rename to curiosrc/web/hapi/watch_actor.go diff --git a/provider/lpweb/hapi/web/actor_summary.gohtml b/curiosrc/web/hapi/web/actor_summary.gohtml similarity index 100% rename from provider/lpweb/hapi/web/actor_summary.gohtml rename to curiosrc/web/hapi/web/actor_summary.gohtml diff --git a/provider/lpweb/hapi/web/chain_rpcs.gohtml b/curiosrc/web/hapi/web/chain_rpcs.gohtml similarity index 100% rename from provider/lpweb/hapi/web/chain_rpcs.gohtml rename to curiosrc/web/hapi/web/chain_rpcs.gohtml diff --git a/provider/lpweb/hapi/web/cluster_machines.gohtml b/curiosrc/web/hapi/web/cluster_machines.gohtml similarity index 100% rename from provider/lpweb/hapi/web/cluster_machines.gohtml rename to curiosrc/web/hapi/web/cluster_machines.gohtml diff --git a/provider/lpweb/hapi/web/cluster_task_history.gohtml b/curiosrc/web/hapi/web/cluster_task_history.gohtml similarity index 100% rename from provider/lpweb/hapi/web/cluster_task_history.gohtml rename to curiosrc/web/hapi/web/cluster_task_history.gohtml diff --git a/provider/lpweb/hapi/web/cluster_tasks.gohtml b/curiosrc/web/hapi/web/cluster_tasks.gohtml similarity index 100% rename from provider/lpweb/hapi/web/cluster_tasks.gohtml rename to curiosrc/web/hapi/web/cluster_tasks.gohtml diff --git a/provider/lpweb/hapi/web/pipeline_porep_sectors.gohtml b/curiosrc/web/hapi/web/pipeline_porep_sectors.gohtml similarity index 100% rename from provider/lpweb/hapi/web/pipeline_porep_sectors.gohtml rename to curiosrc/web/hapi/web/pipeline_porep_sectors.gohtml diff --git a/provider/lpweb/hapi/web/pipline_porep.gohtml b/curiosrc/web/hapi/web/pipline_porep.gohtml similarity index 100% rename from provider/lpweb/hapi/web/pipline_porep.gohtml rename to curiosrc/web/hapi/web/pipline_porep.gohtml diff --git a/provider/lpweb/srv.go b/curiosrc/web/srv.go similarity index 84% rename from provider/lpweb/srv.go rename to curiosrc/web/srv.go index e7640bd1a..2bc2cfa29 100644 --- a/provider/lpweb/srv.go +++ b/curiosrc/web/srv.go @@ -1,5 +1,5 @@ -// Package lpweb defines the HTTP web server for static files and endpoints. -package lpweb +// Package web defines the HTTP web server for static files and endpoints. +package web import ( "context" @@ -16,10 +16,10 @@ import ( "github.com/gorilla/mux" "go.opencensus.io/tag" - "github.com/filecoin-project/lotus/cmd/lotus-provider/deps" + "github.com/filecoin-project/lotus/cmd/curio/deps" + "github.com/filecoin-project/lotus/curiosrc/web/api" + "github.com/filecoin-project/lotus/curiosrc/web/hapi" "github.com/filecoin-project/lotus/metrics" - "github.com/filecoin-project/lotus/provider/lpweb/api" - "github.com/filecoin-project/lotus/provider/lpweb/hapi" ) //go:embed static @@ -41,7 +41,8 @@ func GetSrv(ctx context.Context, deps *deps.Deps) (*http.Server, error) { var static fs.FS = static if webDev { - static = os.DirFS("./provider/lpweb") + basePath = "cmd/curio/web/static" + static = os.DirFS(basePath) } mx.NotFoundHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -71,7 +72,7 @@ func GetSrv(ctx context.Context, deps *deps.Deps) (*http.Server, error) { return &http.Server{ Handler: http.HandlerFunc(mx.ServeHTTP), BaseContext: func(listener net.Listener) context.Context { - ctx, _ := tag.New(context.Background(), tag.Upsert(metrics.APIInterface, "lotus-provider")) + ctx, _ := tag.New(context.Background(), tag.Upsert(metrics.APIInterface, "curio")) return ctx }, Addr: deps.Cfg.Subsystems.GuiAddress, diff --git a/provider/lpweb/static/chain-connectivity.js b/curiosrc/web/static/chain-connectivity.js similarity index 100% rename from provider/lpweb/static/chain-connectivity.js rename to curiosrc/web/static/chain-connectivity.js diff --git a/provider/lpweb/static/index.html b/curiosrc/web/static/index.html similarity index 97% rename from provider/lpweb/static/index.html rename to curiosrc/web/static/index.html index 95e237803..4f9c21b50 100644 --- a/provider/lpweb/static/index.html +++ b/curiosrc/web/static/index.html @@ -1,6 +1,6 @@ - Lotus Provider Cluster Overview + Curio Cluster Overview @@ -39,7 +39,7 @@ diff --git a/lib/harmony/harmonytask/harmonytask.go b/lib/harmony/harmonytask/harmonytask.go index 01f2febba..2d8036e2f 100644 --- a/lib/harmony/harmonytask/harmonytask.go +++ b/lib/harmony/harmonytask/harmonytask.go @@ -178,7 +178,7 @@ func New( } } if !h.considerWork(workSourceRecover, []TaskID{TaskID(w.ID)}) { - log.Error("Strange: Unable to accept previously owned task: ", w.ID, w.Name) + log.Errorw("Strange: Unable to accept previously owned task", "id", w.ID, "type", w.Name) } } } From 7e4b989e32343ba6add38057550b2c8fb59536c8 Mon Sep 17 00:00:00 2001 From: Phi Date: Thu, 18 Jan 2024 09:34:47 +0100 Subject: [PATCH 290/394] Update release_issue_template.md Update release_issue_template.md --- documentation/misc/RELEASE_ISSUE_TEMPLATE.md | 54 ++------------------ 1 file changed, 5 insertions(+), 49 deletions(-) diff --git a/documentation/misc/RELEASE_ISSUE_TEMPLATE.md b/documentation/misc/RELEASE_ISSUE_TEMPLATE.md index 42f44b955..167d1096c 100644 --- a/documentation/misc/RELEASE_ISSUE_TEMPLATE.md +++ b/documentation/misc/RELEASE_ISSUE_TEMPLATE.md @@ -13,62 +13,19 @@ **Note for whoever is owning the release:** please capture notes as comments in this issue for anything you noticed that could be improved for future releases. There is a *Post Release* step below for incorporating changes back into the [RELEASE_ISSUE_TEMPLATE](https://github.com/filecoin-project/lotus/blob/master/documentation/misc/RELEASE_ISSUE_TEMPLATE.md), and this is easier done by collecting notes from along the way rather than just thinking about it at the end. -First steps: - - [ ] Set shell variables vX.Y.Z `export X=1 && export Y=24 && export Z=3` - - [ ] FFI: Fork a new branch (`release/lotus-vX.Y.Z`) from the filecoin-ffi `master` branch: `git checkout master && git checkout -b release/lotus-v$X.$Y.$Z` - - [ ] FFI: Tag the head of `release/lotus-vX.Y.Z` as `vX.Y.Z-pre1`: `git tag -a v$X.$Y.$Z-pre1 -m"release"` - - [ ] Open and land a PR in lotus `master` that updates the FFI dependency to `vX.Y.Z-pre1` as cut in the previous step - - [ ] Fork a new branch (`release/vX.Y.Z`) from `master` and make any further release related changes to this branch. If any "non-trivial" changes get added to the release, uncheck all the checkboxes and return to this stage. - - [ ] Bump the version in `build/version.go` in the `master` branch to `vX.Y.(Z+1)-dev` (bump from feature release) or `vX.(Y+1).0-dev` (bump from mandatory release). - - [ ] Run `make gen && make docsgen-cli` before committing changes - +- [ ] Fork a new branch (`release/vX.Y.Z`) from `master` and make any further release related changes to this branch. If any "non-trivial" changes get added to the release, uncheck all the checkboxes and return to this stage. +- [ ] Bump the version in `build/version.go` in the `master` branch to `vX.Y.(Z+1)-dev` (bump from feature release) or `vX.(Y+1).0-dev` (bump from mandatory release). Run make gen and make docsgen-cli before committing changes + **Prepping an RC**: -- [ ] version string in `build/version.go` has been updated (in the `release/vX.Y.Z` branch) +- [ ] version string in `build/version.go` needs to be updated to end with '-rcX' (in the `release/vX.Y.Z` branch) - [ ] run `make gen && make docsgen-cli` - [ ] Generate changelog using the script at scripts/mkreleaselog - [ ] Add contents of generated text to lotus/CHANGELOG.md in addition to other details -- [ ] Commit using PR +- [ ] Commit using PR - [ ] tag commit with `vX.Y.Z-rcN` - [ ] cut a pre-release [here](https://github.com/filecoin-project/lotus/releases/new?prerelease=true) -**Testing an RC**: - -- [ ] **Stage 0 - Automated Testing** - - Automated Testing - - [ ] CI: Ensure that all tests are passing. - - [ ] Testground tests - -- [ ] **Stage 1 - Internal Testing** - - Binaries - - [ ] Ensure the RC release has downloadable binaries - - Upgrade our testnet infra - - [ ] Wait 24 hours, confirm nodes stay in sync - - Upgrade our mainnet infra - - [ ] Subset of development full archival nodes - - [ ] Subset of bootstrappers (1 per region) - - [ ] Confirm nodes stay in sync - - Metrics report - - Block validation time - - Memory / CPU usage - - Number of goroutines - - IPLD block read latency - - Bandwidth usage - - [ ] If anything has worsened significantly, investigate + fix - - Confirm the following work (some combination of Testground / Calibnet / Mainnet / beta users) - - [ ] Seal a sector - - [ ] make a deal - - [ ] Submit a PoSt - - [ ] (optional) let a sector go faulty, and see it be recovered - -- [ ] **Stage 2 - Community Testing** - - [ ] Test with [SPX](https://github.com/filecoin-project/lotus/discussions/7461) fellows - - [ ] Work on documentations for new features, configuration changes and so on. - -- [ ] **Stage 3 - Community Prod Testing** - - [ ] Update the [CHANGELOG.md](https://github.com/filecoin-project/lotus/blob/master/CHANGELOG.md) to the state that can be used as release note. - - [ ] Invite the wider community through (link to the release issue) - **Stable Release** - [ ] Final preparation - [ ] Verify that version string in [`version.go`](https://github.com/filecoin-project/lotus/blob/master/build/version.go) has been updated. @@ -78,7 +35,6 @@ First steps: - [ ] Tag this merge commit (on the `releases` branch) with `vX.Y.Z` - [ ] Cut the release [here](https://github.com/filecoin-project/lotus/releases/new?prerelease=false&target=releases). - **Post-Release** - [ ] Merge the `releases` branch back into `master`, ignoring the changes to `version.go` (keep the `-dev` version from master). Do NOT delete the `releases` branch when doing so! - [ ] Update [RELEASE_ISSUE_TEMPLATE.md](https://github.com/filecoin-project/lotus/blob/master/documentation/misc/RELEASE_ISSUE_TEMPLATE.md) with any improvements determined from this latest release iteration. From d5df13e66e1bb036846e0ce22ef4e9039c8c1b9a Mon Sep 17 00:00:00 2001 From: Phi-rjan Date: Tue, 26 Mar 2024 08:44:44 +0100 Subject: [PATCH 291/394] Update documentation/misc/RELEASE_ISSUE_TEMPLATE.md Co-authored-by: Rod Vagg --- documentation/misc/RELEASE_ISSUE_TEMPLATE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/misc/RELEASE_ISSUE_TEMPLATE.md b/documentation/misc/RELEASE_ISSUE_TEMPLATE.md index 167d1096c..1088afffc 100644 --- a/documentation/misc/RELEASE_ISSUE_TEMPLATE.md +++ b/documentation/misc/RELEASE_ISSUE_TEMPLATE.md @@ -22,7 +22,7 @@ - [ ] run `make gen && make docsgen-cli` - [ ] Generate changelog using the script at scripts/mkreleaselog - [ ] Add contents of generated text to lotus/CHANGELOG.md in addition to other details -- [ ] Commit using PR +- [ ] Commit using PR - [ ] tag commit with `vX.Y.Z-rcN` - [ ] cut a pre-release [here](https://github.com/filecoin-project/lotus/releases/new?prerelease=true) From 3cb9b6c71856d52866e7852aefb6185bbaba016a Mon Sep 17 00:00:00 2001 From: Phi Date: Tue, 26 Mar 2024 08:48:27 +0100 Subject: [PATCH 292/394] Add testing step Call-out that testing of the RC should be done. --- documentation/misc/RELEASE_ISSUE_TEMPLATE.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/documentation/misc/RELEASE_ISSUE_TEMPLATE.md b/documentation/misc/RELEASE_ISSUE_TEMPLATE.md index 1088afffc..05d239dec 100644 --- a/documentation/misc/RELEASE_ISSUE_TEMPLATE.md +++ b/documentation/misc/RELEASE_ISSUE_TEMPLATE.md @@ -26,6 +26,10 @@ - [ ] tag commit with `vX.Y.Z-rcN` - [ ] cut a pre-release [here](https://github.com/filecoin-project/lotus/releases/new?prerelease=true) +**Testing** + +Test the release candidate thoroughly, including automated and manual tests to ensure stability and functionality across various environments and scenarios. + **Stable Release** - [ ] Final preparation - [ ] Verify that version string in [`version.go`](https://github.com/filecoin-project/lotus/blob/master/build/version.go) has been updated. From 3f4eaf0b82c2b4ca22dcf81cc8ee7b4e5d9c7f9d Mon Sep 17 00:00:00 2001 From: Piotr Galar Date: Tue, 26 Mar 2024 15:06:42 +0000 Subject: [PATCH 293/394] ci: ci: create gh workflow that runs go tests (#11762) * ci: create gh action that installs go from go.mod * ci: create gh action that installs ubuntu dependencies * ci: create gh action that starts yugabyte db * ci: create gh workflow that runs go tests * ci: test the test workflow * ci: delete duplicate line from runner configuration * Revert "ci: test the test workflow" This reverts commit d1da190c375f7104dc1f5151b6f2e8c405d1fb82. * ci: match the circleci runners more accurately * ci: fetch all filecoin-ffi dependencies * ci: rename fetch/install job to cache to better match underlying tasks * ci: set permissions required by the test workflow explicitly --- .github/actions/start-yugabytedb/action.yml | 16 + .github/workflows/test.yml | 321 ++++++++++++++++++++ 2 files changed, 337 insertions(+) create mode 100644 .github/actions/start-yugabytedb/action.yml create mode 100644 .github/workflows/test.yml diff --git a/.github/actions/start-yugabytedb/action.yml b/.github/actions/start-yugabytedb/action.yml new file mode 100644 index 000000000..13c480c66 --- /dev/null +++ b/.github/actions/start-yugabytedb/action.yml @@ -0,0 +1,16 @@ +name: Start YugabyteDB +description: Install Yugabyte Database for Filecoin Lotus + +runs: + using: composite + steps: + - run: docker run --rm --name yugabyte -d -p 5433:5433 yugabytedb/yugabyte:2.18.0.0-b65 bin/yugabyted start --daemon=false + shell: bash + - run: | + while true; do + status=$(docker exec yugabyte bin/yugabyted status); + echo $status; + echo $status | grep Running && break; + sleep 1; + done + shell: bash diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 000000000..491a69d8e --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,321 @@ +name: Test + +on: + pull_request: + push: + branches: + - master + - release/* + workflow_dispatch: + +defaults: + run: + shell: bash + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + discover: + name: Discover Test Groups + runs-on: ubuntu-latest + outputs: + groups: ${{ steps.test.outputs.groups }} + steps: + - uses: actions/checkout@v4 + with: + submodules: 'recursive' + - id: test + env: + # Unit test groups other than unit-rest + utests: | + [ + {"name": "unit-cli", "packages": ["./cli/...", "./cmd/...", "./api/..."]}, + {"name": "unit-storage", "packages": ["./storage/...", "./extern/..."]}, + {"name": "unit-node", "packages": ["./node/..."]} + ] + # Other tests that require special configuration + otests: | + [ + { + "name": "multicore-sdr", + "packages": ["./storage/sealer/ffiwrapper"], + "go_test_flags": "-run=TestMulticoreSDR", + "test_rustproofs_logs": "1" + }, { + "name": "conformance", + "packages": ["./conformance"], + "go_test_flags": "-run=TestConformance", + "skip_conformance": "0" + } + ] + # Mapping from test group names to custom runner labels + # The jobs default to running on the default hosted runners (4 CPU, 16 RAM). + # We use self-hosted xlarge (4 CPU, 8 RAM; and large - 2 CPU, 4 RAM) runners + # to extend the available runner capacity (60 default hosted runners). + # We use self-hosted 4xlarge (16 CPU, 32 RAM; and 2xlarge - 8 CPU, 16 RAM) self-hosted + # to support resource intensive jobs. + # In CircleCI, the jobs defaulted to running on medium+ runners (3 CPU, 6 RAM). + # The following jobs were scheduled to run on 2xlarge runners (16 CPU, 32 RAM): + # - itest-deals_concurrent (✅) + # - itest-sector_pledge (✅) + # - itest-wdpost_worker_config (❌) + # - itest-worker (✅) + # - unit-cli (❌) + # - unit-rest (❌) + runners: | + { + "itest-deals_concurrent": ["self-hosted", "linux", "x64", "4xlarge"], + "itest-sector_pledge": ["self-hosted", "linux", "x64", "4xlarge"], + "itest-worker": ["self-hosted", "linux", "x64", "4xlarge"], + + "itest-gateway": ["self-hosted", "linux", "x64", "2xlarge"], + "itest-sector_import_full": ["self-hosted", "linux", "x64", "2xlarge"], + "itest-sector_import_simple": ["self-hosted", "linux", "x64", "2xlarge"], + "itest-wdpost": ["self-hosted", "linux", "x64", "2xlarge"], + "unit-storage": ["self-hosted", "linux", "x64", "2xlarge"], + + "itest-batch_deal": ["self-hosted", "linux", "x64", "xlarge"], + "itest-cli": ["self-hosted", "linux", "x64", "xlarge"], + "itest-deals_512mb": ["self-hosted", "linux", "x64", "xlarge"], + "itest-deals_anycid": ["self-hosted", "linux", "x64", "xlarge"], + "itest-deals_invalid_utf8_label": ["self-hosted", "linux", "x64", "xlarge"], + "itest-deals_max_staging_deals": ["self-hosted", "linux", "x64", "xlarge"], + "itest-deals_partial_retrieval": ["self-hosted", "linux", "x64", "xlarge"], + "itest-deals_publish": ["self-hosted", "linux", "x64", "xlarge"], + "itest-deals_remote_retrieval": ["self-hosted", "linux", "x64", "xlarge"], + "itest-decode_params": ["self-hosted", "linux", "x64", "xlarge"], + "itest-dup_mpool_messages": ["self-hosted", "linux", "x64", "xlarge"], + "itest-eth_account_abstraction": ["self-hosted", "linux", "x64", "xlarge"], + "itest-eth_api": ["self-hosted", "linux", "x64", "xlarge"], + "itest-eth_balance": ["self-hosted", "linux", "x64", "xlarge"], + "itest-eth_bytecode": ["self-hosted", "linux", "x64", "xlarge"], + "itest-eth_config": ["self-hosted", "linux", "x64", "xlarge"], + "itest-eth_conformance": ["self-hosted", "linux", "x64", "xlarge"], + "itest-eth_deploy": ["self-hosted", "linux", "x64", "xlarge"], + "itest-eth_fee_history": ["self-hosted", "linux", "x64", "xlarge"], + "itest-eth_transactions": ["self-hosted", "linux", "x64", "xlarge"], + "itest-fevm_address": ["self-hosted", "linux", "x64", "xlarge"], + "itest-fevm_events": ["self-hosted", "linux", "x64", "xlarge"], + "itest-gas_estimation": ["self-hosted", "linux", "x64", "xlarge"], + "itest-get_messages_in_ts": ["self-hosted", "linux", "x64", "xlarge"], + "itest-lite_migration": ["self-hosted", "linux", "x64", "xlarge"], + "itest-lookup_robust_address": ["self-hosted", "linux", "x64", "xlarge"], + "itest-mempool": ["self-hosted", "linux", "x64", "xlarge"], + "itest-mpool_msg_uuid": ["self-hosted", "linux", "x64", "xlarge"], + "itest-mpool_push_with_uuid": ["self-hosted", "linux", "x64", "xlarge"], + "itest-msgindex": ["self-hosted", "linux", "x64", "xlarge"], + "itest-multisig": ["self-hosted", "linux", "x64", "xlarge"], + "itest-net": ["self-hosted", "linux", "x64", "xlarge"], + "itest-nonce": ["self-hosted", "linux", "x64", "xlarge"], + "itest-path_detach_redeclare": ["self-hosted", "linux", "x64", "xlarge"], + "itest-pending_deal_allocation": ["self-hosted", "linux", "x64", "xlarge"], + "itest-remove_verifreg_datacap": ["self-hosted", "linux", "x64", "xlarge"], + "itest-sector_miner_collateral": ["self-hosted", "linux", "x64", "xlarge"], + "itest-sector_numassign": ["self-hosted", "linux", "x64", "xlarge"], + "itest-self_sent_txn": ["self-hosted", "linux", "x64", "xlarge"], + "itest-verifreg": ["self-hosted", "linux", "x64", "xlarge"], + "multicore-sdr": ["self-hosted", "linux", "x64", "xlarge"], + "unit-node": ["self-hosted", "linux", "x64", "xlarge"] + } + # A list of test groups that require YugabyteDB to be running + # In CircleCI, all jobs had yugabytedb running as a sidecar. + yugabytedb: | + ["itest-harmonydb", "itest-harmonytask"] + # A list of test groups that require Proof Parameters to be fetched + # In CircleCI, only the following jobs had get-params set: + # - unit-cli (✅) + # - unit-storage (✅) + # - itest-sector_pledge (✅) + # - itest-wdpost (✅) + parameters: | + [ + "conformance", + "itest-api", + "itest-deals_offline", + "itest-deals_padding", + "itest-deals_partial_retrieval_dm-level", + "itest-deals_pricing", + "itest-deals", + "itest-direct_data_onboard_verified", + "itest-direct_data_onboard", + "itest-net", + "itest-path_detach_redeclare", + "itest-path_type_filters", + "itest-sealing_resources", + "itest-sector_finalize_early", + "itest-sector_import_full", + "itest-sector_import_simple", + "itest-sector_pledge", + "itest-sector_unseal", + "itest-wdpost_no_miner_storage", + "itest-wdpost_worker_config", + "itest-wdpost", + "itest-worker_upgrade", + "itest-worker", + "multicore-sdr", + "unit-cli", + "unit-storage" + ] + run: | + # Create a list of integration test groups + itests="$( + find ./itests -name "*_test.go" | \ + jq -R '{ + "name": "itest-\(. | split("/") | .[2] | sub("_test.go$";""))", + "packages": [.] + }' | jq -s + )" + + # Create a list of packages that are covered by the integration and unit tests + packages="$(jq -n --argjson utests "$utests" '$utests | map(.packages) | flatten | . + ["./itests/..."]')" + + # Create a new group for the unit tests that are not yet covered + rest="$( + find . -name "*_test.go" | cut -d/ -f2 | sort | uniq | \ + jq -R '"./\(.)/..."' | \ + jq -s --argjson p "$packages" '{"name": "unit-rest", "packages": (. - $p)}' + )" + + # Combine the groups for integration tests, unit tests, the new unit-rest group, and the other tests + groups="$(jq -n --argjson i "$itests" --argjson u "$utests" --argjson r "$rest" --argjson o "$otests" '$i + $u + [$r] + $o')" + + # Apply custom runner labels to the groups + groups="$(jq -n --argjson g "$groups" --argjson r "$runners" '$g | map(. + {"runner": (.name as $n | $r | .[$n]) })')" + + # Apply the needs_yugabytedb flag to the groups + groups="$(jq -n --argjson g "$groups" --argjson y "$yugabytedb" '$g | map(. + {"needs_yugabytedb": ([.name] | inside($y)) })')" + + # Apply the needs_parameters flag to the groups + groups="$(jq -n --argjson g "$groups" --argjson p "$parameters" '$g | map(. + {"needs_parameters": ([.name] | inside($p)) })')" + + # Output the groups + echo "groups=$groups" + echo "groups=$(jq -nc --argjson g "$groups" '$g')" >> $GITHUB_OUTPUT + cache: + name: Cache Dependencies + runs-on: ubuntu-latest + outputs: + fetch_params_key: ${{ steps.fetch_params.outputs.key }} + fetch_params_path: ${{ steps.fetch_params.outputs.path }} + make_deps_key: ${{ steps.make_deps.outputs.key }} + make_deps_path: ${{ steps.make_deps.outputs.path }} + steps: + - uses: actions/checkout@v4 + with: + submodules: 'recursive' + - id: fetch_params + env: + CACHE_KEY: fetch-params-${{ hashFiles('./extern/filecoin-ffi/parameters.json') }} + CACHE_PATH: | + /var/tmp/filecoin-proof-parameters/ + run: | + echo -e "key=$CACHE_KEY" | tee -a $GITHUB_OUTPUT + echo -e "path< Date: Tue, 26 Mar 2024 17:19:53 +0000 Subject: [PATCH 294/394] fix: ci: allow master main sync to write to the repository (#11784) --- .github/workflows/sync-master-main.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/sync-master-main.yaml b/.github/workflows/sync-master-main.yaml index 6a7e9a650..05a60f089 100644 --- a/.github/workflows/sync-master-main.yaml +++ b/.github/workflows/sync-master-main.yaml @@ -3,6 +3,10 @@ on: push: branches: - master + +permissions: + contents: write + jobs: sync: runs-on: ubuntu-latest From c7b64bd6a952192a6f88e2d60c18a34aef0c4160 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 22 Mar 2024 15:12:03 +0100 Subject: [PATCH 295/394] curio: Storage reservations when fetching --- curiosrc/ffi/sdr_funcs.go | 22 +++++++-- curiosrc/ffi/task_storage.go | 45 ++++++----------- storage/paths/interface.go | 2 +- storage/paths/local.go | 2 +- storage/paths/remote.go | 73 ++++++++++++++++------------ storage/sealer/storiface/filetype.go | 35 +++++++++++++ storage/sealer/storiface/paths.go | 12 +++++ 7 files changed, 124 insertions(+), 67 deletions(-) diff --git a/curiosrc/ffi/sdr_funcs.go b/curiosrc/ffi/sdr_funcs.go index 52d90f70d..4dc66bbde 100644 --- a/curiosrc/ffi/sdr_funcs.go +++ b/curiosrc/ffi/sdr_funcs.go @@ -80,6 +80,23 @@ func (l *storageProvider) AcquireSector(ctx context.Context, taskID *harmonytask paths = resv.Paths storageIDs = resv.PathIDs releaseStorage = resv.Release + + if len(existing.AllSet()) > 0 { + // there are some "existing" files in the reservation. Some of them may need fetching, so call l.storage.AcquireSector + // (which unlike in the reservation code will be called on the paths.Remote instance) to ensure that the files are + // present locally. Note that we do not care about 'allocate' reqeuests, those files don't exist, and are just + // proposed paths with a reservation of space. + + _, checkPathIDs, err := l.storage.AcquireSector(ctx, sector, existing, storiface.FTNone, sealing, storiface.AcquireMove, storiface.AcquireInto(storiface.PathsWithIDs{Paths: paths, IDs: storageIDs})) + if err != nil { + return storiface.SectorPaths{}, nil, xerrors.Errorf("acquire reserved existing files: %w", err) + } + + // assert that checkPathIDs is the same as storageIDs + if storageIDs.Subset(existing) != checkPathIDs.Subset(existing) { + return storiface.SectorPaths{}, nil, xerrors.Errorf("acquire reserved existing files: pathIDs mismatch %#v != %#v", storageIDs, checkPathIDs) + } + } } else { var err error paths, storageIDs, err = l.storage.AcquireSector(ctx, sector, existing, allocate, sealing, storiface.AcquireMove) @@ -143,10 +160,7 @@ func (sb *SealCalls) GenerateSDR(ctx context.Context, taskID harmonytask.TaskID, } func (sb *SealCalls) TreeD(ctx context.Context, sector storiface.SectorRef, size abi.PaddedPieceSize, data io.Reader, unpaddedData bool) (cid.Cid, error) { - maybeUns := storiface.FTNone - // todo sectors with data - - paths, releaseSector, err := sb.sectors.AcquireSector(ctx, nil, sector, storiface.FTCache, maybeUns, storiface.PathSealing) + paths, releaseSector, err := sb.sectors.AcquireSector(ctx, nil, sector, storiface.FTCache, storiface.FTNone, storiface.PathSealing) if err != nil { return cid.Undef, xerrors.Errorf("acquiring sector paths: %w", err) } diff --git a/curiosrc/ffi/task_storage.go b/curiosrc/ffi/task_storage.go index ddc5e00a3..0fcb030dc 100644 --- a/curiosrc/ffi/task_storage.go +++ b/curiosrc/ffi/task_storage.go @@ -10,7 +10,6 @@ import ( "github.com/filecoin-project/lotus/lib/harmony/harmonytask" "github.com/filecoin-project/lotus/lib/harmony/resources" - "github.com/filecoin-project/lotus/lib/must" "github.com/filecoin-project/lotus/storage/sealer/storiface" ) @@ -111,6 +110,11 @@ func (t *TaskStorage) HasCapacity() bool { } func (t *TaskStorage) Claim(taskID int) error { + // TaskStorage Claim Attempts to reserve storage for the task + // A: Create a reservation for files to be allocated + // B: Create a reservation for existing files to be fetched into local storage + // C: Create a reservation for existing files in local storage which may be extended (e.g. sector cache when computing Trees) + ctx := context.Background() sectorRef, err := t.taskToSectorRef(harmonytask.TaskID(taskID)) @@ -121,7 +125,7 @@ func (t *TaskStorage) Claim(taskID int) error { // storage writelock sector lkctx, cancel := context.WithCancel(ctx) - allocate := storiface.FTCache + requestedTypes := t.alloc | t.existing lockAcquireTimuout := time.Second * 10 lockAcquireTimer := time.NewTimer(lockAcquireTimuout) @@ -135,7 +139,7 @@ func (t *TaskStorage) Claim(taskID int) error { } }() - if err := t.sc.sectors.sindex.StorageLock(lkctx, sectorRef.ID(), storiface.FTNone, allocate); err != nil { + if err := t.sc.sectors.sindex.StorageLock(lkctx, sectorRef.ID(), storiface.FTNone, requestedTypes); err != nil { // timer will expire return xerrors.Errorf("claim StorageLock: %w", err) } @@ -149,39 +153,18 @@ func (t *TaskStorage) Claim(taskID int) error { lockAcquireTimer.Reset(0) }() - // find anywhere - // if found return nil, for now - s, err := t.sc.sectors.sindex.StorageFindSector(ctx, sectorRef.ID(), allocate, must.One(sectorRef.RegSealProof.SectorSize()), false) - if err != nil { - return xerrors.Errorf("claim StorageFindSector: %w", err) - } - - lp, err := t.sc.sectors.localStore.Local(ctx) - if err != nil { - return err - } - - // see if there are any non-local sector files in storage - for _, info := range s { - for _, l := range lp { - if l.ID == info.ID { - continue - } - - // TODO: Create reservation for fetching; This will require quite a bit more refactoring, but for now we'll - // only care about new allocations - return nil - } - } - - // acquire a path to make a reservation in - pathsFs, pathIDs, err := t.sc.sectors.localStore.AcquireSector(ctx, sectorRef.Ref(), storiface.FTNone, allocate, storiface.PathSealing, storiface.AcquireMove) + // First see what we have locally. We are putting allocate and existing together because local acquire will look + // for existing files for allocate requests, separately existing files which aren't found locally will be need to + // be fetched, so we will need to create reservations for that too. + // NOTE localStore.AcquireSector does not open or create any files, nor does it reserve space. It only proposes + // paths to be used. + pathsFs, pathIDs, err := t.sc.sectors.localStore.AcquireSector(ctx, sectorRef.Ref(), storiface.FTNone, requestedTypes, storiface.PathSealing, storiface.AcquireMove) if err != nil { return err } // reserve the space - release, err := t.sc.sectors.localStore.Reserve(ctx, sectorRef.Ref(), allocate, pathIDs, storiface.FSOverheadSeal) + release, err := t.sc.sectors.localStore.Reserve(ctx, sectorRef.Ref(), requestedTypes, pathIDs, storiface.FSOverheadSeal) if err != nil { return err } diff --git a/storage/paths/interface.go b/storage/paths/interface.go index 4ff206c6d..27d6ee541 100644 --- a/storage/paths/interface.go +++ b/storage/paths/interface.go @@ -35,7 +35,7 @@ type PartialFileHandler interface { //go:generate go run github.com/golang/mock/mockgen -destination=mocks/store.go -package=mocks . Store type Store interface { - AcquireSector(ctx context.Context, s storiface.SectorRef, existing storiface.SectorFileType, allocate storiface.SectorFileType, sealing storiface.PathType, op storiface.AcquireMode) (paths storiface.SectorPaths, stores storiface.SectorPaths, err error) + AcquireSector(ctx context.Context, s storiface.SectorRef, existing storiface.SectorFileType, allocate storiface.SectorFileType, sealing storiface.PathType, op storiface.AcquireMode, opts ...storiface.AcquireOption) (paths storiface.SectorPaths, stores storiface.SectorPaths, err error) Remove(ctx context.Context, s abi.SectorID, types storiface.SectorFileType, force bool, keepIn []storiface.ID) error // like remove, but doesn't remove the primary sector copy, nor the last diff --git a/storage/paths/local.go b/storage/paths/local.go index 7dd7c1256..3efe099e6 100644 --- a/storage/paths/local.go +++ b/storage/paths/local.go @@ -460,7 +460,7 @@ func (st *Local) Reserve(ctx context.Context, sid storiface.SectorRef, ft storif return done, nil } -func (st *Local) AcquireSector(ctx context.Context, sid storiface.SectorRef, existing storiface.SectorFileType, allocate storiface.SectorFileType, pathType storiface.PathType, op storiface.AcquireMode) (storiface.SectorPaths, storiface.SectorPaths, error) { +func (st *Local) AcquireSector(ctx context.Context, sid storiface.SectorRef, existing storiface.SectorFileType, allocate storiface.SectorFileType, pathType storiface.PathType, op storiface.AcquireMode, opts ...storiface.AcquireOption) (storiface.SectorPaths, storiface.SectorPaths, error) { if existing|allocate != existing^allocate { return storiface.SectorPaths{}, storiface.SectorPaths{}, xerrors.New("can't both find and allocate a sector") } diff --git a/storage/paths/remote.go b/storage/paths/remote.go index 9ff719954..94b7f511a 100644 --- a/storage/paths/remote.go +++ b/storage/paths/remote.go @@ -93,11 +93,28 @@ func NewRemote(local Store, index SectorIndex, auth http.Header, fetchLimit int, } } -func (r *Remote) AcquireSector(ctx context.Context, s storiface.SectorRef, existing storiface.SectorFileType, allocate storiface.SectorFileType, pathType storiface.PathType, op storiface.AcquireMode) (storiface.SectorPaths, storiface.SectorPaths, error) { +func (r *Remote) AcquireSector(ctx context.Context, s storiface.SectorRef, existing storiface.SectorFileType, allocate storiface.SectorFileType, pathType storiface.PathType, op storiface.AcquireMode, opts ...storiface.AcquireOption) (storiface.SectorPaths, storiface.SectorPaths, error) { if existing|allocate != existing^allocate { return storiface.SectorPaths{}, storiface.SectorPaths{}, xerrors.New("can't both find and allocate a sector") } + settings := storiface.AcquireSettings{ + // Into will tell us which paths things should be fetched into or allocated in. + Into: nil, + } + for _, o := range opts { + o(&settings) + } + + if settings.Into != nil { + if !allocate.IsNone() { + return storiface.SectorPaths{}, storiface.SectorPaths{}, xerrors.New("cannot specify Into with allocate") + } + if !settings.Into.HasAllSet(existing) { + return storiface.SectorPaths{}, storiface.SectorPaths{}, xerrors.New("Into has to have all existing paths") + } + } + // First make sure that no other goroutines are trying to fetch this sector; // wait if there are any. for { @@ -134,47 +151,43 @@ func (r *Remote) AcquireSector(ctx context.Context, s storiface.SectorRef, exist } var toFetch storiface.SectorFileType - for _, fileType := range storiface.PathTypes { - if fileType&existing == 0 { - continue - } - + for _, fileType := range existing.AllSet() { if storiface.PathByType(paths, fileType) == "" { toFetch |= fileType } } // get a list of paths to fetch data into. Note: file type filters will apply inside this call. - fetchPaths, ids, err := r.local.AcquireSector(ctx, s, storiface.FTNone, toFetch, pathType, op) - if err != nil { - return storiface.SectorPaths{}, storiface.SectorPaths{}, xerrors.Errorf("allocate local sector for fetching: %w", err) - } + var fetchPaths, fetchIDs storiface.SectorPaths - overheadTable := storiface.FSOverheadSeal - if pathType == storiface.PathStorage { - overheadTable = storiface.FsOverheadFinalized - } - - // If any path types weren't found in local storage, try fetching them - - // First reserve storage - releaseStorage, err := r.local.Reserve(ctx, s, toFetch, ids, overheadTable) - if err != nil { - return storiface.SectorPaths{}, storiface.SectorPaths{}, xerrors.Errorf("reserving storage space: %w", err) - } - defer releaseStorage() - - for _, fileType := range storiface.PathTypes { - if fileType&existing == 0 { - continue + if settings.Into == nil { + // fetching without existing reservation, so allocate paths and create a reservation + fetchPaths, fetchIDs, err = r.local.AcquireSector(ctx, s, storiface.FTNone, toFetch, pathType, op) + if err != nil { + return storiface.SectorPaths{}, storiface.SectorPaths{}, xerrors.Errorf("allocate local sector for fetching: %w", err) } - if storiface.PathByType(paths, fileType) != "" { - continue + overheadTable := storiface.FSOverheadSeal + if pathType == storiface.PathStorage { + overheadTable = storiface.FsOverheadFinalized } + // If any path types weren't found in local storage, try fetching them + + // First reserve storage + releaseStorage, err := r.local.Reserve(ctx, s, toFetch, fetchIDs, overheadTable) + if err != nil { + return storiface.SectorPaths{}, storiface.SectorPaths{}, xerrors.Errorf("reserving storage space: %w", err) + } + defer releaseStorage() + } else { + fetchPaths = settings.Into.Paths + fetchIDs = settings.Into.IDs + } + + for _, fileType := range toFetch.AllSet() { dest := storiface.PathByType(fetchPaths, fileType) - storageID := storiface.PathByType(ids, fileType) + storageID := storiface.PathByType(fetchIDs, fileType) url, err := r.acquireFromRemote(ctx, s.ID, fileType, dest) if err != nil { diff --git a/storage/sealer/storiface/filetype.go b/storage/sealer/storiface/filetype.go index 109e494a8..422f87cf3 100644 --- a/storage/sealer/storiface/filetype.go +++ b/storage/sealer/storiface/filetype.go @@ -214,6 +214,10 @@ func (t SectorFileType) All() [FileTypes]bool { return out } +func (t SectorFileType) IsNone() bool { + return t == 0 +} + type SectorPaths struct { ID abi.SectorID @@ -225,6 +229,28 @@ type SectorPaths struct { Piece string } +func (sp SectorPaths) HasAllSet(ft SectorFileType) bool { + for _, fileType := range ft.AllSet() { + if PathByType(sp, fileType) == "" { + return false + } + } + + return true +} + +func (sp SectorPaths) Subset(filter SectorFileType) SectorPaths { + var out SectorPaths + + for _, fileType := range filter.AllSet() { + SetPathByType(&out, fileType, PathByType(sp, fileType)) + } + + out.ID = sp.ID + + return out +} + func ParseSectorID(baseName string) (abi.SectorID, error) { var n abi.SectorNumber var mid abi.ActorID @@ -282,3 +308,12 @@ func SetPathByType(sps *SectorPaths, fileType SectorFileType, p string) { sps.Piece = p } } + +type PathsWithIDs struct { + Paths SectorPaths + IDs SectorPaths +} + +func (p PathsWithIDs) HasAllSet(ft SectorFileType) bool { + return p.Paths.HasAllSet(ft) && p.IDs.HasAllSet(ft) +} diff --git a/storage/sealer/storiface/paths.go b/storage/sealer/storiface/paths.go index 2cb4f34d3..0f0eaeadf 100644 --- a/storage/sealer/storiface/paths.go +++ b/storage/sealer/storiface/paths.go @@ -25,3 +25,15 @@ type SectorLock struct { type SectorLocks struct { Locks []SectorLock } + +type AcquireSettings struct { + Into *PathsWithIDs +} + +type AcquireOption func(*AcquireSettings) + +func AcquireInto(pathIDs PathsWithIDs) AcquireOption { + return func(settings *AcquireSettings) { + settings.Into = &pathIDs + } +} From 7b00cc58277d49a601ef352f5aa0287ae0ee43fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sun, 24 Mar 2024 11:22:43 +0100 Subject: [PATCH 296/394] curio: Add task storage to SDRTrees --- curiosrc/ffi/sdr_funcs.go | 34 ++++++++++------ curiosrc/ffi/task_storage.go | 2 +- curiosrc/seal/task_trees.go | 41 ++++++++++++------- storage/paths/local.go | 79 ++++++++++++++++++++++-------------- storage/paths/remote.go | 4 ++ 5 files changed, 102 insertions(+), 58 deletions(-) diff --git a/curiosrc/ffi/sdr_funcs.go b/curiosrc/ffi/sdr_funcs.go index 4dc66bbde..74a3270cf 100644 --- a/curiosrc/ffi/sdr_funcs.go +++ b/curiosrc/ffi/sdr_funcs.go @@ -98,6 +98,8 @@ func (l *storageProvider) AcquireSector(ctx context.Context, taskID *harmonytask } } } else { + // No related reservation, acquire storage as usual + var err error paths, storageIDs, err = l.storage.AcquireSector(ctx, sector, existing, allocate, sealing, storiface.AcquireMove) if err != nil { @@ -159,17 +161,7 @@ func (sb *SealCalls) GenerateSDR(ctx context.Context, taskID harmonytask.TaskID, return nil } -func (sb *SealCalls) TreeD(ctx context.Context, sector storiface.SectorRef, size abi.PaddedPieceSize, data io.Reader, unpaddedData bool) (cid.Cid, error) { - paths, releaseSector, err := sb.sectors.AcquireSector(ctx, nil, sector, storiface.FTCache, storiface.FTNone, storiface.PathSealing) - if err != nil { - return cid.Undef, xerrors.Errorf("acquiring sector paths: %w", err) - } - defer releaseSector() - - return proof.BuildTreeD(data, unpaddedData, filepath.Join(paths.Cache, proofpaths.TreeDName), size) -} - -func (sb *SealCalls) TreeRC(ctx context.Context, sector storiface.SectorRef, unsealed cid.Cid) (cid.Cid, cid.Cid, error) { +func (sb *SealCalls) TreeDRC(ctx context.Context, sector storiface.SectorRef, unsealed cid.Cid, size abi.PaddedPieceSize, data io.Reader, unpaddedData bool) (cid.Cid, cid.Cid, error) { p1o, err := sb.makePhase1Out(unsealed, sector.ProofType) if err != nil { return cid.Undef, cid.Undef, xerrors.Errorf("make phase1 output: %w", err) @@ -181,6 +173,15 @@ func (sb *SealCalls) TreeRC(ctx context.Context, sector storiface.SectorRef, uns } defer releaseSector() + treeDUnsealed, err := proof.BuildTreeD(data, unpaddedData, filepath.Join(paths.Cache, proofpaths.TreeDName), size) + if err != nil { + return cid.Undef, cid.Undef, xerrors.Errorf("building tree-d: %w", err) + } + + if treeDUnsealed != unsealed { + return cid.Undef, cid.Undef, xerrors.Errorf("tree-d cid mismatch with supplied unsealed cid") + } + { // create sector-sized file at paths.Sealed; PC2 transforms it into a sealed sector in-place ssize, err := sector.ProofType.SectorSize() @@ -224,7 +225,16 @@ func (sb *SealCalls) TreeRC(ctx context.Context, sector storiface.SectorRef, uns } } - return ffi.SealPreCommitPhase2(p1o, paths.Cache, paths.Sealed) + sl, uns, err := ffi.SealPreCommitPhase2(p1o, paths.Cache, paths.Sealed) + if err != nil { + return cid.Undef, cid.Undef, xerrors.Errorf("computing seal proof: %w", err) + } + + if uns != unsealed { + return cid.Undef, cid.Undef, xerrors.Errorf("unsealed cid changed after sealing") + } + + return sl, uns, nil } func (sb *SealCalls) GenerateSynthPoRep() { diff --git a/curiosrc/ffi/task_storage.go b/curiosrc/ffi/task_storage.go index 0fcb030dc..30dd90417 100644 --- a/curiosrc/ffi/task_storage.go +++ b/curiosrc/ffi/task_storage.go @@ -158,7 +158,7 @@ func (t *TaskStorage) Claim(taskID int) error { // be fetched, so we will need to create reservations for that too. // NOTE localStore.AcquireSector does not open or create any files, nor does it reserve space. It only proposes // paths to be used. - pathsFs, pathIDs, err := t.sc.sectors.localStore.AcquireSector(ctx, sectorRef.Ref(), storiface.FTNone, requestedTypes, storiface.PathSealing, storiface.AcquireMove) + pathsFs, pathIDs, err := t.sc.sectors.localStore.AcquireSector(ctx, sectorRef.Ref(), storiface.FTNone, requestedTypes, t.pathType, storiface.AcquireMove) if err != nil { return err } diff --git a/curiosrc/seal/task_trees.go b/curiosrc/seal/task_trees.go index fa22f8d4a..ccf9ab5aa 100644 --- a/curiosrc/seal/task_trees.go +++ b/curiosrc/seal/task_trees.go @@ -186,22 +186,12 @@ func (t *TreesTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (done ProofType: sectorParams.RegSealProof, } - // D - treeUnsealed, err := t.sc.TreeD(ctx, sref, abi.PaddedPieceSize(ssize), dataReader, unpaddedData) - if err != nil { - return false, xerrors.Errorf("computing tree d: %w", err) - } - - // R / C - sealed, unsealed, err := t.sc.TreeRC(ctx, sref, commd) + // D / R / C + sealed, unsealed, err := t.sc.TreeDRC(ctx, sref, commd, abi.PaddedPieceSize(ssize), dataReader, unpaddedData) if err != nil { return false, xerrors.Errorf("computing tree r and c: %w", err) } - if unsealed != treeUnsealed { - return false, xerrors.Errorf("tree-d and tree-r/c unsealed CIDs disagree") - } - // todo synth porep // todo porep challenge check @@ -228,13 +218,19 @@ func (t *TreesTask) CanAccept(ids []harmonytask.TaskID, engine *harmonytask.Task } func (t *TreesTask) TypeDetails() harmonytask.TaskTypeDetails { + ssize := abi.SectorSize(32 << 30) // todo task details needs taskID to get correct sector size + if isDevnet { + ssize = abi.SectorSize(2 << 20) + } + return harmonytask.TaskTypeDetails{ Max: t.max, Name: "SDRTrees", Cost: resources.Resources{ - Cpu: 1, - Gpu: 1, - Ram: 8000 << 20, // todo + Cpu: 1, + Gpu: 1, + Ram: 8000 << 20, // todo + Storage: t.sc.Storage(t.taskToSector, storiface.FTSealed, storiface.FTCache, ssize, storiface.PathSealing), }, MaxFailures: 3, Follows: nil, @@ -245,6 +241,21 @@ func (t *TreesTask) Adder(taskFunc harmonytask.AddTaskFunc) { t.sp.pollers[pollerTrees].Set(taskFunc) } +func (t *TreesTask) taskToSector(id harmonytask.TaskID) (ffi.SectorRef, error) { + var refs []ffi.SectorRef + + err := t.db.Select(context.Background(), &refs, `SELECT sp_id, sector_number, reg_seal_proof FROM sectors_sdr_pipeline WHERE task_id_tree_r = $1`, id) + if err != nil { + return ffi.SectorRef{}, xerrors.Errorf("getting sector ref: %w", err) + } + + if len(refs) != 1 { + return ffi.SectorRef{}, xerrors.Errorf("expected 1 sector ref, got %d", len(refs)) + } + + return refs[0], nil +} + type UrlPieceReader struct { Url string RawSize int64 // the exact number of bytes read, if we read more or less that's an error diff --git a/storage/paths/local.go b/storage/paths/local.go index 3efe099e6..a71f60430 100644 --- a/storage/paths/local.go +++ b/storage/paths/local.go @@ -53,7 +53,15 @@ type path struct { reservations map[abi.SectorID]storiface.SectorFileType } -func (p *path) stat(ls LocalStorage) (fsutil.FsStat, error) { +// statExistingSectorForReservation is optional parameter for stat method +// which will make it take into account existing sectors when calculating +// available space for new reservations +type statExistingSectorForReservation struct { + id abi.SectorID + ft storiface.SectorFileType +} + +func (p *path) stat(ls LocalStorage, newReserve ...statExistingSectorForReservation) (fsutil.FsStat, error) { start := time.Now() stat, err := ls.Stat(p.local) @@ -63,34 +71,49 @@ func (p *path) stat(ls LocalStorage) (fsutil.FsStat, error) { stat.Reserved = p.reserved + accountExistingFiles := func(id abi.SectorID, fileType storiface.SectorFileType) error { + sp := p.sectorPath(id, fileType) + + used, err := ls.DiskUsage(sp) + if err == os.ErrNotExist { + p, ferr := tempFetchDest(sp, false) + if ferr != nil { + return ferr + } + + used, err = ls.DiskUsage(p) + } + if err != nil { + // we don't care about 'not exist' errors, as storage can be + // reserved before any files are written, so this error is not + // unexpected + if !os.IsNotExist(err) { + log.Warnf("getting disk usage of '%s': %+v", p.sectorPath(id, fileType), err) + } + return nil + } + + stat.Reserved -= used + return nil + } + for id, ft := range p.reservations { - for _, fileType := range storiface.PathTypes { - if fileType&ft == 0 { + for _, fileType := range ft.AllSet() { + if err := accountExistingFiles(id, fileType); err != nil { + return fsutil.FsStat{}, err + } + } + } + for _, reservation := range newReserve { + for _, fileType := range reservation.ft.AllSet() { + if p.reservations[reservation.id]&fileType != 0 { + // already accounted for continue } - sp := p.sectorPath(id, fileType) - - used, err := ls.DiskUsage(sp) - if err == os.ErrNotExist { - p, ferr := tempFetchDest(sp, false) - if ferr != nil { - return fsutil.FsStat{}, ferr - } - - used, err = ls.DiskUsage(p) + if err := accountExistingFiles(reservation.id, fileType); err != nil { + return fsutil.FsStat{}, err } - if err != nil { - // we don't care about 'not exist' errors, as storage can be - // reserved before any files are written, so this error is not - // unexpected - if !os.IsNotExist(err) { - log.Warnf("getting disk usage of '%s': %+v", p.sectorPath(id, fileType), err) - } - continue - } - - stat.Reserved -= used } } @@ -414,11 +437,7 @@ func (st *Local) Reserve(ctx context.Context, sid storiface.SectorRef, ft storif deferredDone() }() - for _, fileType := range storiface.PathTypes { - if fileType&ft == 0 { - continue - } - + for _, fileType := range ft.AllSet() { id := storiface.ID(storiface.PathByType(storageIDs, fileType)) p, ok := st.paths[id] @@ -426,7 +445,7 @@ func (st *Local) Reserve(ctx context.Context, sid storiface.SectorRef, ft storif return nil, errPathNotFound } - stat, err := p.stat(st.localStorage) + stat, err := p.stat(st.localStorage, statExistingSectorForReservation{sid.ID, fileType}) if err != nil { return nil, xerrors.Errorf("getting local storage stat: %w", err) } diff --git a/storage/paths/remote.go b/storage/paths/remote.go index 94b7f511a..8532357b4 100644 --- a/storage/paths/remote.go +++ b/storage/paths/remote.go @@ -167,6 +167,8 @@ func (r *Remote) AcquireSector(ctx context.Context, s storiface.SectorRef, exist return storiface.SectorPaths{}, storiface.SectorPaths{}, xerrors.Errorf("allocate local sector for fetching: %w", err) } + log.Debugw("Fetching sector data without existing reservation", "sector", s, "toFetch", toFetch, "fetchPaths", fetchPaths, "fetchIDs", fetchIDs) + overheadTable := storiface.FSOverheadSeal if pathType == storiface.PathStorage { overheadTable = storiface.FsOverheadFinalized @@ -183,6 +185,8 @@ func (r *Remote) AcquireSector(ctx context.Context, s storiface.SectorRef, exist } else { fetchPaths = settings.Into.Paths fetchIDs = settings.Into.IDs + + log.Debugw("Fetching sector data with existing reservation", "sector", s, "toFetch", toFetch, "fetchPaths", fetchPaths, "fetchIDs", fetchIDs) } for _, fileType := range toFetch.AllSet() { From c7ca271507ba0da6c33d0f00279709bffd19635d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sun, 24 Mar 2024 11:33:23 +0100 Subject: [PATCH 297/394] rebuild storage mocks --- storage/paths/mocks/store.go | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/storage/paths/mocks/store.go b/storage/paths/mocks/store.go index 244b4fc91..d7fa226e6 100644 --- a/storage/paths/mocks/store.go +++ b/storage/paths/mocks/store.go @@ -41,9 +41,13 @@ func (m *MockStore) EXPECT() *MockStoreMockRecorder { } // AcquireSector mocks base method. -func (m *MockStore) AcquireSector(arg0 context.Context, arg1 storiface.SectorRef, arg2, arg3 storiface.SectorFileType, arg4 storiface.PathType, arg5 storiface.AcquireMode) (storiface.SectorPaths, storiface.SectorPaths, error) { +func (m *MockStore) AcquireSector(arg0 context.Context, arg1 storiface.SectorRef, arg2, arg3 storiface.SectorFileType, arg4 storiface.PathType, arg5 storiface.AcquireMode, arg6 ...storiface.AcquireOption) (storiface.SectorPaths, storiface.SectorPaths, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "AcquireSector", arg0, arg1, arg2, arg3, arg4, arg5) + varargs := []interface{}{arg0, arg1, arg2, arg3, arg4, arg5} + for _, a := range arg6 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "AcquireSector", varargs...) ret0, _ := ret[0].(storiface.SectorPaths) ret1, _ := ret[1].(storiface.SectorPaths) ret2, _ := ret[2].(error) @@ -51,9 +55,10 @@ func (m *MockStore) AcquireSector(arg0 context.Context, arg1 storiface.SectorRef } // AcquireSector indicates an expected call of AcquireSector. -func (mr *MockStoreMockRecorder) AcquireSector(arg0, arg1, arg2, arg3, arg4, arg5 interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) AcquireSector(arg0, arg1, arg2, arg3, arg4, arg5 interface{}, arg6 ...interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AcquireSector", reflect.TypeOf((*MockStore)(nil).AcquireSector), arg0, arg1, arg2, arg3, arg4, arg5) + varargs := append([]interface{}{arg0, arg1, arg2, arg3, arg4, arg5}, arg6...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AcquireSector", reflect.TypeOf((*MockStore)(nil).AcquireSector), varargs...) } // FsStat mocks base method. From 021385116a72a7211b291d77d1ac57f7d4edfb45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sun, 24 Mar 2024 14:48:25 +0100 Subject: [PATCH 298/394] paths: Fix local existing allocate requests in Finalize --- itests/path_type_filters_test.go | 1 + storage/paths/local.go | 30 +++++++++++++++++++++--------- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/itests/path_type_filters_test.go b/itests/path_type_filters_test.go index c668976ac..a2e204932 100644 --- a/itests/path_type_filters_test.go +++ b/itests/path_type_filters_test.go @@ -15,6 +15,7 @@ import ( ) func TestPathTypeFilters(t *testing.T) { + kit.QuietMiningLogs() runTest := func(t *testing.T, name string, asserts func(t *testing.T, ctx context.Context, miner *kit.TestMiner, run func())) { t.Run(name, func(t *testing.T) { diff --git a/storage/paths/local.go b/storage/paths/local.go index a71f60430..68999940f 100644 --- a/storage/paths/local.go +++ b/storage/paths/local.go @@ -495,6 +495,22 @@ func (st *Local) AcquireSector(ctx context.Context, sid storiface.SectorRef, exi var out storiface.SectorPaths var storageIDs storiface.SectorPaths + allocPathOk := func(canSeal, canStore bool, allowTypes, denyTypes []string, fileType storiface.SectorFileType) bool { + if (pathType == storiface.PathSealing) && !canSeal { + return false + } + + if (pathType == storiface.PathStorage) && !canStore { + return false + } + + if !fileType.Allowed(allowTypes, denyTypes) { + return false + } + + return true + } + // First find existing files for _, fileType := range storiface.PathTypes { // also try to find existing sectors if we're allocating @@ -520,6 +536,10 @@ func (st *Local) AcquireSector(ctx context.Context, sid storiface.SectorRef, exi continue } + if allocate.Has(fileType) && !allocPathOk(info.CanSeal, info.CanStore, info.AllowTypes, info.DenyTypes, fileType) { + continue // allocate request for a path of different type + } + spath := p.sectorPath(sid.ID, fileType) storiface.SetPathByType(&out, fileType, spath) storiface.SetPathByType(&storageIDs, fileType, string(info.ID)) @@ -554,15 +574,7 @@ func (st *Local) AcquireSector(ctx context.Context, sid storiface.SectorRef, exi continue } - if (pathType == storiface.PathSealing) && !si.CanSeal { - continue - } - - if (pathType == storiface.PathStorage) && !si.CanStore { - continue - } - - if !fileType.Allowed(si.AllowTypes, si.DenyTypes) { + if !allocPathOk(si.CanSeal, si.CanStore, si.AllowTypes, si.DenyTypes, fileType) { continue } From ae7829acd7f95e5f54f6ab2df1aa00953c00a4d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 25 Mar 2024 12:12:33 +0100 Subject: [PATCH 299/394] fix: curio seal: Check ticket age before sending PreCommit message --- curiosrc/seal/task_submit_precommit.go | 48 ++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/curiosrc/seal/task_submit_precommit.go b/curiosrc/seal/task_submit_precommit.go index 9cc8d446b..0f896cb93 100644 --- a/curiosrc/seal/task_submit_precommit.go +++ b/curiosrc/seal/task_submit_precommit.go @@ -28,6 +28,7 @@ import ( ) type SubmitPrecommitTaskApi interface { + ChainHead(context.Context) (*types.TipSet, error) StateMinerPreCommitDepositForPower(context.Context, address.Address, miner.SectorPreCommitInfo, types.TipSetKey) (big.Int, error) StateMinerInfo(context.Context, address.Address, types.TipSetKey) (api.MinerInfo, error) StateNetworkVersion(context.Context, types.TipSetKey) (network.Version, error) @@ -59,6 +60,8 @@ func NewSubmitPrecommitTask(sp *SealPoller, db *harmonydb.DB, api SubmitPrecommi func (s *SubmitPrecommitTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (done bool, err error) { ctx := context.Background() + // 1. Load sector info + var sectorParamsArr []struct { SpID int64 `db:"sp_id"` SectorNumber int64 `db:"sector_number"` @@ -96,6 +99,8 @@ func (s *SubmitPrecommitTask) Do(taskID harmonytask.TaskID, stillOwned func() bo return false, xerrors.Errorf("parsing unsealed CID: %w", err) } + // 2. Prepare message params + params := miner.PreCommitSectorBatchParams2{} expiration := sectorParams.TicketEpoch + miner12.MaxSectorExpirationExtension @@ -157,6 +162,26 @@ func (s *SubmitPrecommitTask) Do(taskID harmonytask.TaskID, stillOwned func() bo params.Sectors[0].Expiration = minExpiration } + // 3. Check precommit + + { + record, err := s.checkPrecommit(ctx, params) + if err != nil { + if record { + _, perr := s.db.Exec(ctx, `UPDATE sectors_sdr_pipeline + SET failed = TRUE, failed_at = NOW(), failed_reason = 'precommit-check', failed_reason_msg = $1 + WHERE task_id_precommit_msg = $2`, err.Error(), taskID) + if perr != nil { + return false, xerrors.Errorf("persisting precommit check error: %w", perr) + } + } + + return record, xerrors.Errorf("checking precommit: %w", err) + } + } + + // 4. Prepare and send message + var pbuf bytes.Buffer if err := params.MarshalCBOR(&pbuf); err != nil { return false, xerrors.Errorf("serializing params: %w", err) @@ -210,6 +235,29 @@ func (s *SubmitPrecommitTask) Do(taskID harmonytask.TaskID, stillOwned func() bo return true, nil } +func (s *SubmitPrecommitTask) checkPrecommit(ctx context.Context, params miner.PreCommitSectorBatchParams2) (record bool, err error) { + if len(params.Sectors) != 1 { + return false, xerrors.Errorf("expected 1 sector") + } + + preCommitInfo := params.Sectors[0] + + head, err := s.api.ChainHead(ctx) + if err != nil { + return false, xerrors.Errorf("getting chain head: %w", err) + } + height := head.Height() + + //never commit P2 message before, check ticket expiration + ticketEarliest := height - policy.MaxPreCommitRandomnessLookback + + if preCommitInfo.SealRandEpoch < ticketEarliest { + return true, xerrors.Errorf("ticket expired: seal height: %d, head: %d", preCommitInfo.SealRandEpoch+policy.SealRandomnessLookback, height) + } + + return true, nil +} + func (s *SubmitPrecommitTask) CanAccept(ids []harmonytask.TaskID, engine *harmonytask.TaskEngine) (*harmonytask.TaskID, error) { id := ids[0] return &id, nil From 795ee314c00876101f3bf8c04b70ecfd9e1894c8 Mon Sep 17 00:00:00 2001 From: LexLuthr <88259624+LexLuthr@users.noreply.github.com> Date: Thu, 28 Mar 2024 13:09:41 +0400 Subject: [PATCH 300/394] remove from-miner cmd (#11793) --- cmd/curio/config.go | 1 - cmd/curio/main.go | 5 ++- cmd/curio/migrate.go | 70 ----------------------------------- documentation/en/cli-curio.md | 38 ------------------- 4 files changed, 4 insertions(+), 110 deletions(-) diff --git a/cmd/curio/config.go b/cmd/curio/config.go index 20f2d0597..2938118ad 100644 --- a/cmd/curio/config.go +++ b/cmd/curio/config.go @@ -31,7 +31,6 @@ var configCmd = &cli.Command{ configViewCmd, configRmCmd, configEditCmd, - configMigrateCmd, configNewCmd, }, } diff --git a/cmd/curio/main.go b/cmd/curio/main.go index 8c64af379..2175d24c5 100644 --- a/cmd/curio/main.go +++ b/cmd/curio/main.go @@ -25,6 +25,10 @@ import ( var log = logging.Logger("main") +const ( + FlagMinerRepo = "miner-repo" +) + func setupCloseHandler() { c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt, syscall.SIGTERM) @@ -49,7 +53,6 @@ func main() { testCmd, webCmd, guidedsetup.GuidedsetupCmd, - configMigrateCmd, sealCmd, } diff --git a/cmd/curio/migrate.go b/cmd/curio/migrate.go index 5cf55bcb4..06ab7d0f9 100644 --- a/cmd/curio/migrate.go +++ b/cmd/curio/migrate.go @@ -1,71 +1 @@ package main - -import ( - "fmt" - - "github.com/urfave/cli/v2" - "golang.org/x/xerrors" - - cliutil "github.com/filecoin-project/lotus/cli/util" - "github.com/filecoin-project/lotus/cmd/curio/guidedsetup" - "github.com/filecoin-project/lotus/node/repo" -) - -var configMigrateCmd = &cli.Command{ - Name: "from-miner", - Usage: "Express a database config (for curio) from an existing miner.", - Description: "Express a database config (for curio) from an existing miner.", - Flags: []cli.Flag{ - &cli.StringFlag{ - Name: FlagMinerRepo, - Aliases: []string{FlagMinerRepoDeprecation}, - EnvVars: []string{"LOTUS_MINER_PATH", "LOTUS_STORAGE_PATH"}, - Value: "~/.lotusminer", - Usage: fmt.Sprintf("Specify miner repo path. flag(%s) and env(LOTUS_STORAGE_PATH) are DEPRECATION, will REMOVE SOON", FlagMinerRepoDeprecation), - }, - &cli.StringFlag{ - Name: "repo", - EnvVars: []string{"LOTUS_PATH"}, - Hidden: true, - Value: "~/.lotus", - }, - &cli.StringFlag{ - Name: "to-layer", - Aliases: []string{"t"}, - Usage: "The layer name for this data push. 'base' is recommended for single-miner setup.", - }, - &cli.BoolFlag{ - Name: "overwrite", - Aliases: []string{"o"}, - Usage: "Use this with --to-layer to replace an existing layer", - }, - }, - Action: fromMiner, -} - -const ( - FlagMinerRepo = "miner-repo" -) - -const FlagMinerRepoDeprecation = "storagerepo" - -func fromMiner(cctx *cli.Context) (err error) { - minerRepoPath := cctx.String(FlagMinerRepo) - layerName := cctx.String("to-layer") - overwrite := cctx.Bool("overwrite") - - // Populate API Key - _, header, err := cliutil.GetRawAPI(cctx, repo.FullNode, "v0") - if err != nil { - return fmt.Errorf("cannot read API: %w", err) - } - - ainfo, err := cliutil.GetAPIInfo(&cli.Context{}, repo.FullNode) - if err != nil { - return xerrors.Errorf(`could not get API info for FullNode: %w - Set the environment variable to the value of "lotus auth api-info --perm=admin"`, err) - } - chainApiInfo := header.Get("Authorization")[7:] + ":" + ainfo.Addr - _, err = guidedsetup.SaveConfigToLayer(minerRepoPath, layerName, overwrite, chainApiInfo) - return err -} diff --git a/documentation/en/cli-curio.md b/documentation/en/cli-curio.md index 08ed8ab90..6976dfa2e 100644 --- a/documentation/en/cli-curio.md +++ b/documentation/en/cli-curio.md @@ -17,7 +17,6 @@ COMMANDS: test Utility functions for testing web Start Curio web interface guided-setup Run the guided setup for migrating from lotus-miner to Curio - from-miner Express a database config (for curio) from an existing miner. seal Manage the sealing pipeline version Print version help, h Shows a list of commands or help for one command @@ -106,7 +105,6 @@ COMMANDS: interpret, view, stacked, stack Interpret stacked config layers by this version of curio, with system-generated comments. remove, rm, del, delete Remove a named config layer. edit edit a config layer - from-miner Express a database config (for curio) from an existing miner. new-cluster Create new configuration for a new cluster help, h Shows a list of commands or help for one command @@ -206,24 +204,6 @@ OPTIONS: --help, -h show help ``` -### curio config from-miner -``` -NAME: - curio from-miner - Express a database config (for curio) from an existing miner. - -USAGE: - curio from-miner [command options] [arguments...] - -DESCRIPTION: - Express a database config (for curio) from an existing miner. - -OPTIONS: - --miner-repo value, --storagerepo value Specify miner repo path. flag(storagerepo) and env(LOTUS_STORAGE_PATH) are DEPRECATION, will REMOVE SOON (default: "~/.lotusminer") [$LOTUS_MINER_PATH, $LOTUS_STORAGE_PATH] - --to-layer value, -t value The layer name for this data push. 'base' is recommended for single-miner setup. - --overwrite, -o Use this with --to-layer to replace an existing layer (default: false) - --help, -h show help -``` - ### curio config new-cluster ``` NAME: @@ -334,24 +314,6 @@ OPTIONS: --help, -h show help ``` -## curio from-miner -``` -NAME: - curio from-miner - Express a database config (for curio) from an existing miner. - -USAGE: - curio from-miner [command options] [arguments...] - -DESCRIPTION: - Express a database config (for curio) from an existing miner. - -OPTIONS: - --miner-repo value, --storagerepo value Specify miner repo path. flag(storagerepo) and env(LOTUS_STORAGE_PATH) are DEPRECATION, will REMOVE SOON (default: "~/.lotusminer") [$LOTUS_MINER_PATH, $LOTUS_STORAGE_PATH] - --to-layer value, -t value The layer name for this data push. 'base' is recommended for single-miner setup. - --overwrite, -o Use this with --to-layer to replace an existing layer (default: false) - --help, -h show help -``` - ## curio seal ``` NAME: From 0e4677618fa97f4d6981b559592928b7360577ee Mon Sep 17 00:00:00 2001 From: LexLuthr <88259624+LexLuthr@users.noreply.github.com> Date: Thu, 28 Mar 2024 15:18:28 +0400 Subject: [PATCH 301/394] fix: CLI: adjust TermMax for extend-claim used by a different client (#11764) * fix Datacap TermMax * fix itest * add batching * make batch size variable * docsgen-cli * reduce batch size, fix batch dc * apply suggestion from review * put back TODO --------- Co-authored-by: LexLuthr --- cli/filplus.go | 187 +++++++++++++------- documentation/en/cli-lotus.md | 10 +- itests/direct_data_onboard_verified_test.go | 14 +- 3 files changed, 135 insertions(+), 76 deletions(-) diff --git a/cli/filplus.go b/cli/filplus.go index fbb922a24..8c39c21e8 100644 --- a/cli/filplus.go +++ b/cli/filplus.go @@ -934,7 +934,11 @@ var filplusSignRemoveDataCapProposal = &cli.Command{ var filplusExtendClaimCmd = &cli.Command{ Name: "extend-claim", - Usage: "extend claim expiration (TermMax)", + Usage: "extends claim expiration (TermMax)", + UsageText: `Extends claim expiration (TermMax). +If the client is original client then claim can be extended to maximum 5 years and no Datacap is required. +If the client id different then claim can be extended up to maximum 5 years from now and Datacap is required. +`, Flags: []cli.Flag{ &cli.Int64Flag{ Name: "term-max", @@ -966,6 +970,11 @@ var filplusExtendClaimCmd = &cli.Command{ Usage: "number of block confirmations to wait for", Value: int(build.MessageConfidence), }, + &cli.IntFlag{ + Name: "batch-size", + Usage: "number of extend requests per batch. If set incorrectly, this will lead to out of gas error", + Value: 500, + }, }, ArgsUsage: " ... or ...", Action: func(cctx *cli.Context) error { @@ -1069,7 +1078,7 @@ var filplusExtendClaimCmd = &cli.Command{ } } - msgs, err := CreateExtendClaimMsg(ctx, api, claimMap, miners, clientAddr, abi.ChainEpoch(tmax), all, cctx.Bool("assume-yes")) + msgs, err := CreateExtendClaimMsg(ctx, api, claimMap, miners, clientAddr, abi.ChainEpoch(tmax), all, cctx.Bool("assume-yes"), cctx.Int("batch-size")) if err != nil { return err } @@ -1122,7 +1131,7 @@ type ProvInfo struct { // 6. Extend all claims for multiple miner IDs with different client address (2 messages) // 7. Extend specified claims for a miner ID with different client address (2 messages) // 8. Extend specific claims for specific miner ID with different client address (2 messages) -func CreateExtendClaimMsg(ctx context.Context, api api.FullNode, pcm map[verifregtypes13.ClaimId]ProvInfo, miners []string, wallet address.Address, tmax abi.ChainEpoch, all, assumeYes bool) ([]*types.Message, error) { +func CreateExtendClaimMsg(ctx context.Context, api api.FullNode, pcm map[verifregtypes13.ClaimId]ProvInfo, miners []string, wallet address.Address, tmax abi.ChainEpoch, all, assumeYes bool, batchSize int) ([]*types.Message, error) { ac, err := api.StateLookupID(ctx, wallet, types.EmptyTSK) if err != nil { @@ -1141,7 +1150,7 @@ func CreateExtendClaimMsg(ctx context.Context, api api.FullNode, pcm map[verifre } var terms []verifregtypes13.ClaimTerm - var newClaims []verifregtypes13.ClaimExtensionRequest + newClaims := make(map[verifregtypes13.ClaimExtensionRequest]big.Int) rDataCap := big.NewInt(0) // If --all is set @@ -1162,17 +1171,23 @@ func CreateExtendClaimMsg(ctx context.Context, api api.FullNode, pcm map[verifre for claimID, claim := range claims { claimID := claimID claim := claim - if claim.TermMax < tmax && claim.TermStart+claim.TermMax > head.Height() { - // If client is not same - needs to burn datacap - if claim.Client != wid { - newClaims = append(newClaims, verifregtypes13.ClaimExtensionRequest{ + // If the client is not the original client - burn datacap + if claim.Client != wid { + // The new duration should be greater than the original deal duration and claim should not already be expired + if head.Height()+tmax-claim.TermStart > claim.TermMax-claim.TermStart && claim.TermStart+claim.TermMax > head.Height() { + req := verifregtypes13.ClaimExtensionRequest{ Claim: verifregtypes13.ClaimId(claimID), Provider: abi.ActorID(mid), - TermMax: tmax, - }) + TermMax: head.Height() + tmax - claim.TermStart, + } + newClaims[req] = big.NewInt(int64(claim.Size)) rDataCap.Add(big.NewInt(int64(claim.Size)).Int, rDataCap.Int) - continue } + // If new duration shorter than the original duration then do nothing + continue + } + // For original client, compare duration(TermMax) and claim should not already be expired + if claim.TermMax < tmax && claim.TermStart+claim.TermMax > head.Height() { terms = append(terms, verifregtypes13.ClaimTerm{ ClaimId: verifregtypes13.ClaimId(claimID), TermMax: tmax, @@ -1204,17 +1219,23 @@ func CreateExtendClaimMsg(ctx context.Context, api api.FullNode, pcm map[verifre if !ok { return nil, xerrors.Errorf("claim %d not found for provider %s", claimID, miners[0]) } - if claim.TermMax < tmax && claim.TermStart+claim.TermMax > head.Height() { - // If client is not same - needs to burn datacap - if claim.Client != wid { - newClaims = append(newClaims, verifregtypes13.ClaimExtensionRequest{ + // If the client is not the original client - burn datacap + if claim.Client != wid { + // The new duration should be greater than the original deal duration and claim should not already be expired + if head.Height()+tmax-claim.TermStart > claim.TermMax-claim.TermStart && claim.TermStart+claim.TermMax > head.Height() { + req := verifregtypes13.ClaimExtensionRequest{ Claim: claimID, Provider: abi.ActorID(mid), - TermMax: tmax, - }) + TermMax: head.Height() + tmax - claim.TermStart, + } + newClaims[req] = big.NewInt(int64(claim.Size)) rDataCap.Add(big.NewInt(int64(claim.Size)).Int, rDataCap.Int) - continue } + // If new duration shorter than the original duration then do nothing + continue + } + // For original client, compare duration(TermMax) and claim should not already be expired + if claim.TermMax < tmax && claim.TermStart+claim.TermMax > head.Height() { terms = append(terms, verifregtypes13.ClaimTerm{ ClaimId: claimID, TermMax: tmax, @@ -1235,17 +1256,23 @@ func CreateExtendClaimMsg(ctx context.Context, api api.FullNode, pcm map[verifre if claim == nil { return nil, xerrors.Errorf("claim %d not found in the actor state", claimID) } - if claim.TermMax < tmax && claim.TermStart+claim.TermMax > head.Height() { - // If client is not same - needs to burn datacap - if claim.Client != wid { - newClaims = append(newClaims, verifregtypes13.ClaimExtensionRequest{ + // If the client is not the original client - burn datacap + if claim.Client != wid { + // The new duration should be greater than the original deal duration and claim should not already be expired + if head.Height()+tmax-claim.TermStart > claim.TermMax-claim.TermStart && claim.TermStart+claim.TermMax > head.Height() { + req := verifregtypes13.ClaimExtensionRequest{ Claim: claimID, Provider: prov.ID, - TermMax: tmax, - }) + TermMax: head.Height() + tmax - claim.TermStart, + } + newClaims[req] = big.NewInt(int64(claim.Size)) rDataCap.Add(big.NewInt(int64(claim.Size)).Int, rDataCap.Int) - continue } + // If new duration shorter than the original duration then do nothing + continue + } + // For original client, compare duration(TermMax) and claim should not already be expired + if claim.TermMax < tmax && claim.TermStart+claim.TermMax > head.Height() { terms = append(terms, verifregtypes13.ClaimTerm{ ClaimId: claimID, TermMax: tmax, @@ -1258,22 +1285,29 @@ func CreateExtendClaimMsg(ctx context.Context, api api.FullNode, pcm map[verifre var msgs []*types.Message if len(terms) > 0 { - params, err := actors.SerializeParams(&verifregtypes13.ExtendClaimTermsParams{ - Terms: terms, - }) + // Batch in 500 to avoid running out of gas + for i := 0; i < len(terms); i += batchSize { + batchEnd := i + batchSize + if batchEnd > len(terms) { + batchEnd = len(terms) + } - if err != nil { - return nil, xerrors.Errorf("failed to searialise the parameters: %s", err) + batch := terms[i:batchEnd] + + params, err := actors.SerializeParams(&verifregtypes13.ExtendClaimTermsParams{ + Terms: batch, + }) + if err != nil { + return nil, xerrors.Errorf("failed to searialise the parameters: %s", err) + } + oclaimMsg := &types.Message{ + To: verifreg.Address, + From: wallet, + Method: verifreg.Methods.ExtendClaimTerms, + Params: params, + } + msgs = append(msgs, oclaimMsg) } - - oclaimMsg := &types.Message{ - To: verifreg.Address, - From: wallet, - Method: verifreg.Methods.ExtendClaimTerms, - Params: params, - } - - msgs = append(msgs, oclaimMsg) } if len(newClaims) > 0 { @@ -1292,32 +1326,6 @@ func CreateExtendClaimMsg(ctx context.Context, api api.FullNode, pcm map[verifre return nil, xerrors.Errorf("requested datacap %s is greater then the available datacap %s", rDataCap, aDataCap) } - ncparams, err := actors.SerializeParams(&verifregtypes13.AllocationRequests{ - Extensions: newClaims, - }) - - if err != nil { - return nil, xerrors.Errorf("failed to searialise the parameters: %s", err) - } - - transferParams, err := actors.SerializeParams(&datacap2.TransferParams{ - To: builtin.VerifiedRegistryActorAddr, - Amount: big.Mul(rDataCap, builtin.TokenPrecision), - OperatorData: ncparams, - }) - - if err != nil { - return nil, xerrors.Errorf("failed to serialize transfer parameters: %s", err) - } - - nclaimMsg := &types.Message{ - To: builtin.DatacapActorAddr, - From: wallet, - Method: datacap.Methods.TransferExported, - Params: transferParams, - Value: big.Zero(), - } - if !assumeYes { out := fmt.Sprintf("Some of the specified allocation have a different client address and will require %d Datacap to extend. Proceed? Yes [Y/y] / No [N/n], Ctrl+C (^C) to exit", rDataCap.Int) validate := func(input string) error { @@ -1353,7 +1361,54 @@ func CreateExtendClaimMsg(ctx context.Context, api api.FullNode, pcm map[verifre } } - msgs = append(msgs, nclaimMsg) + // Create a map of just keys, so we can easily batch based on the numeric keys + keys := make([]verifregtypes13.ClaimExtensionRequest, 0, len(newClaims)) + for k := range newClaims { + keys = append(keys, k) + } + + // Batch in 500 to avoid running out of gas + for i := 0; i < len(keys); i += batchSize { + batchEnd := i + batchSize + if batchEnd > len(newClaims) { + batchEnd = len(newClaims) + } + + batch := keys[i:batchEnd] + + // Calculate Datacap for this batch + dcap := big.NewInt(0) + for _, k := range batch { + dc := newClaims[k] + dcap.Add(dcap.Int, dc.Int) + } + + ncparams, err := actors.SerializeParams(&verifregtypes13.AllocationRequests{ + Extensions: batch, + }) + if err != nil { + return nil, xerrors.Errorf("failed to searialise the parameters: %s", err) + } + + transferParams, err := actors.SerializeParams(&datacap2.TransferParams{ + To: builtin.VerifiedRegistryActorAddr, + Amount: big.Mul(dcap, builtin.TokenPrecision), + OperatorData: ncparams, + }) + + if err != nil { + return nil, xerrors.Errorf("failed to serialize transfer parameters: %s", err) + } + + nclaimMsg := &types.Message{ + To: builtin.DatacapActorAddr, + From: wallet, + Method: datacap.Methods.TransferExported, + Params: transferParams, + Value: big.Zero(), + } + msgs = append(msgs, nclaimMsg) + } } return msgs, nil diff --git a/documentation/en/cli-lotus.md b/documentation/en/cli-lotus.md index ad04b68ec..36f1e1059 100644 --- a/documentation/en/cli-lotus.md +++ b/documentation/en/cli-lotus.md @@ -1192,7 +1192,7 @@ COMMANDS: list-claims List claims available in verified registry actor or made by provider if specified remove-expired-allocations remove expired allocations (if no allocations are specified all eligible allocations are removed) remove-expired-claims remove expired claims (if no claims are specified all eligible claims are removed) - extend-claim extend claim expiration (TermMax) + extend-claim extends claim expiration (TermMax) help, h Shows a list of commands or help for one command OPTIONS: @@ -1329,10 +1329,13 @@ OPTIONS: ### lotus filplus extend-claim ``` NAME: - lotus filplus extend-claim - extend claim expiration (TermMax) + lotus filplus extend-claim - extends claim expiration (TermMax) USAGE: - lotus filplus extend-claim [command options] ... or ... + Extends claim expiration (TermMax). + If the client is original client then claim can be extended to maximum 5 years and no Datacap is required. + If the client id different then claim can be extended up to maximum 5 years from now and Datacap is required. + OPTIONS: --term-max value, --tmax value The maximum period for which a provider can earn quality-adjusted power for the piece (epochs). Default is 5 years. (default: 5256000) @@ -1341,6 +1344,7 @@ OPTIONS: --miner value, -m value, --provider value, -p value [ --miner value, -m value, --provider value, -p value ] storage provider address[es] --assume-yes, -y, --yes automatic yes to prompts; assume 'yes' as answer to all prompts and run non-interactively (default: false) --confidence value number of block confirmations to wait for (default: 5) + --batch-size value number of extend requests per batch. If set incorrectly, this will lead to out of gas error (default: 500) --help, -h show help ``` diff --git a/itests/direct_data_onboard_verified_test.go b/itests/direct_data_onboard_verified_test.go index 530b3cca6..7415570a3 100644 --- a/itests/direct_data_onboard_verified_test.go +++ b/itests/direct_data_onboard_verified_test.go @@ -840,12 +840,11 @@ func TestVerifiedDDOExtendClaim(t *testing.T) { pcm[verifregtypes13.ClaimId(allocationId)] = prov // Extend claim with same client - msgs, err := cli.CreateExtendClaimMsg(ctx, client.FullNode, pcm, []string{}, verifiedClientAddr1, (builtin.EpochsInYear*3)+3000, false, true) + msgs, err := cli.CreateExtendClaimMsg(ctx, client.FullNode, pcm, []string{}, verifiedClientAddr1, (builtin.EpochsInYear*3)+3000, false, true, 100) require.NoError(t, err) require.NotNil(t, msgs) require.Len(t, msgs, 1) - // MpoolBatchPushMessage method will take care of gas estimation and funds check smsg, err := client.MpoolPushMessage(ctx, msgs[0], nil) require.NoError(t, err) @@ -859,11 +858,11 @@ func TestVerifiedDDOExtendClaim(t *testing.T) { require.EqualValues(t, newclaim.TermMax-oldclaim.TermMax, 3000) // Extend claim with non-verified client | should fail - _, err = cli.CreateExtendClaimMsg(ctx, client.FullNode, pcm, []string{}, unverifiedClient.Address, verifregtypes13.MaximumVerifiedAllocationTerm, false, true) + _, err = cli.CreateExtendClaimMsg(ctx, client.FullNode, pcm, []string{}, unverifiedClient.Address, verifregtypes13.MaximumVerifiedAllocationTerm, false, true, 100) require.ErrorContains(t, err, "does not have any datacap") // Extend all claim with verified client - msgs, err = cli.CreateExtendClaimMsg(ctx, client.FullNode, nil, []string{miner.ActorAddr.String()}, verifiedClientAddr2, verifregtypes13.MaximumVerifiedAllocationTerm, true, true) + msgs, err = cli.CreateExtendClaimMsg(ctx, client.FullNode, nil, []string{miner.ActorAddr.String()}, verifiedClientAddr2, verifregtypes13.MaximumVerifiedAllocationTerm, true, true, 100) require.NoError(t, err) require.Len(t, msgs, 1) smsg, err = client.MpoolPushMessage(ctx, msgs[0], nil) @@ -873,14 +872,15 @@ func TestVerifiedDDOExtendClaim(t *testing.T) { require.True(t, wait.Receipt.ExitCode.IsSuccess()) // Extend all claims with lower TermMax - msgs, err = cli.CreateExtendClaimMsg(ctx, client.FullNode, pcm, []string{}, verifiedClientAddr2, builtin.EpochsInYear*4, false, true) + msgs, err = cli.CreateExtendClaimMsg(ctx, client.FullNode, pcm, []string{}, verifiedClientAddr2, builtin.EpochsInYear*4, false, true, 100) require.NoError(t, err) require.Nil(t, msgs) newclaim, err = client.StateGetClaim(ctx, miner.ActorAddr, verifreg.ClaimId(allocationId), types.EmptyTSK) require.NoError(t, err) require.NotNil(t, newclaim) - require.EqualValues(t, newclaim.TermMax, verifregtypes13.MaximumVerifiedAllocationTerm) - // TODO: check "claim-updated" message + // TODO: check "claim-updated" event + // New TermMax should be more than 5 years + require.Greater(t, int(newclaim.TermMax), verifregtypes13.MaximumVerifiedAllocationTerm) } From 8062f200bdcdf2753a54f3221839fdcd56bf317f Mon Sep 17 00:00:00 2001 From: LexLuthr <88259624+LexLuthr@users.noreply.github.com> Date: Sat, 30 Mar 2024 20:40:32 +0400 Subject: [PATCH 302/394] fix: curio: refactor curio graceful shutdown (#11794) * refactor curio shutdown * apply suggestions, remove provider cli --- cmd/curio/run.go | 3 +- documentation/en/cli-lotus-provider.md | 410 ------------------------- itests/harmonytask_test.go | 12 +- itests/kit/ensemble.go | 2 +- lib/harmony/harmonytask/harmonytask.go | 56 +++- 5 files changed, 53 insertions(+), 430 deletions(-) delete mode 100644 documentation/en/cli-lotus-provider.md diff --git a/cmd/curio/run.go b/cmd/curio/run.go index 35fdf4a4d..5974a5405 100644 --- a/cmd/curio/run.go +++ b/cmd/curio/run.go @@ -5,7 +5,6 @@ import ( "fmt" "os" "strings" - "time" "github.com/pkg/errors" "github.com/urfave/cli/v2" @@ -128,7 +127,7 @@ var runCmd = &cli.Command{ if err != nil { return nil } - defer taskEngine.GracefullyTerminate(time.Hour) + defer taskEngine.GracefullyTerminate() err = rpc.ListenAndServe(ctx, dependencies, shutdownChan) // Monitor for shutdown. if err != nil { diff --git a/documentation/en/cli-lotus-provider.md b/documentation/en/cli-lotus-provider.md deleted file mode 100644 index 29cfb7721..000000000 --- a/documentation/en/cli-lotus-provider.md +++ /dev/null @@ -1,410 +0,0 @@ -# lotus-provider -``` -NAME: - lotus-provider - Filecoin decentralized storage network provider - -USAGE: - lotus-provider [global options] command [command options] [arguments...] - -VERSION: - 1.26.0 - -COMMANDS: - run Start a lotus provider process - stop Stop a running lotus provider - config Manage node config by layers. The layer 'base' will always be applied. - test Utility functions for testing - version Print version - help, h Shows a list of commands or help for one command - DEVELOPER: - auth Manage RPC permissions - log Manage logging - wait-api Wait for lotus api to come online - fetch-params Fetch proving parameters - -GLOBAL OPTIONS: - --color use color in display output (default: depends on output being a TTY) - --db-host value Command separated list of hostnames for yugabyte cluster (default: "yugabyte") [$LOTUS_DB_HOST] - --db-name value (default: "yugabyte") [$LOTUS_DB_NAME, $LOTUS_HARMONYDB_HOSTS] - --db-user value (default: "yugabyte") [$LOTUS_DB_USER, $LOTUS_HARMONYDB_USERNAME] - --db-password value (default: "yugabyte") [$LOTUS_DB_PASSWORD, $LOTUS_HARMONYDB_PASSWORD] - --layers value (default: "base") [$LOTUS_LAYERS, $LOTUS_CONFIG_LAYERS] - --repo-path value (default: "~/.lotusprovider") [$LOTUS_REPO_PATH] - --vv enables very verbose mode, useful for debugging the CLI (default: false) - --help, -h show help - --version, -v print the version -``` - -## lotus-provider run -``` -NAME: - lotus-provider run - Start a lotus provider process - -USAGE: - lotus-provider run [command options] [arguments...] - -OPTIONS: - --listen value host address and port the worker api will listen on (default: "0.0.0.0:12300") [$LOTUS_WORKER_LISTEN] - --nosync don't check full-node sync status (default: false) - --manage-fdlimit manage open file limit (default: true) - --layers value [ --layers value ] list of layers to be interpreted (atop defaults). Default: base (default: "base") - --storage-json value path to json file containing storage config (default: "~/.lotus-provider/storage.json") - --journal value path to journal files (default: "~/.lotus-provider/") - --help, -h show help -``` - -## lotus-provider stop -``` -NAME: - lotus-provider stop - Stop a running lotus provider - -USAGE: - lotus-provider stop [command options] [arguments...] - -OPTIONS: - --help, -h show help -``` - -## lotus-provider config -``` -NAME: - lotus-provider config - Manage node config by layers. The layer 'base' will always be applied. - -USAGE: - lotus-provider config command [command options] [arguments...] - -COMMANDS: - default, defaults Print default node config - set, add, update, create Set a config layer or the base by providing a filename or stdin. - get, cat, show Get a config layer by name. You may want to pipe the output to a file, or use 'less' - list, ls List config layers you can get. - interpret, view, stacked, stack Interpret stacked config layers by this version of lotus-provider, with system-generated comments. - remove, rm, del, delete Remove a named config layer. - from-miner Express a database config (for lotus-provider) from an existing miner. - help, h Shows a list of commands or help for one command - -OPTIONS: - --help, -h show help -``` - -### lotus-provider config default -``` -NAME: - lotus-provider config default - Print default node config - -USAGE: - lotus-provider config default [command options] [arguments...] - -OPTIONS: - --no-comment don't comment default values (default: false) - --help, -h show help -``` - -### lotus-provider config set -``` -NAME: - lotus-provider config set - Set a config layer or the base by providing a filename or stdin. - -USAGE: - lotus-provider config set [command options] a layer's file name - -OPTIONS: - --title value title of the config layer (req'd for stdin) - --help, -h show help -``` - -### lotus-provider config get -``` -NAME: - lotus-provider config get - Get a config layer by name. You may want to pipe the output to a file, or use 'less' - -USAGE: - lotus-provider config get [command options] layer name - -OPTIONS: - --help, -h show help -``` - -### lotus-provider config list -``` -NAME: - lotus-provider config list - List config layers you can get. - -USAGE: - lotus-provider config list [command options] [arguments...] - -OPTIONS: - --help, -h show help -``` - -### lotus-provider config interpret -``` -NAME: - lotus-provider config interpret - Interpret stacked config layers by this version of lotus-provider, with system-generated comments. - -USAGE: - lotus-provider config interpret [command options] a list of layers to be interpreted as the final config - -OPTIONS: - --layers value [ --layers value ] comma or space separated list of layers to be interpreted (default: "base") - --help, -h show help -``` - -### lotus-provider config remove -``` -NAME: - lotus-provider config remove - Remove a named config layer. - -USAGE: - lotus-provider config remove [command options] [arguments...] - -OPTIONS: - --help, -h show help -``` - -### lotus-provider config from-miner -``` -NAME: - lotus-provider config from-miner - Express a database config (for lotus-provider) from an existing miner. - -USAGE: - lotus-provider config from-miner [command options] [arguments...] - -DESCRIPTION: - Express a database config (for lotus-provider) from an existing miner. - -OPTIONS: - --miner-repo value, --storagerepo value Specify miner repo path. flag(storagerepo) and env(LOTUS_STORAGE_PATH) are DEPRECATION, will REMOVE SOON (default: "~/.lotusminer") [$LOTUS_MINER_PATH, $LOTUS_STORAGE_PATH] - --to-layer value, -t value The layer name for this data push. 'base' is recommended for single-miner setup. - --overwrite, -o Use this with --to-layer to replace an existing layer (default: false) - --help, -h show help -``` - -## lotus-provider test -``` -NAME: - lotus-provider test - Utility functions for testing - -USAGE: - lotus-provider test command [command options] [arguments...] - -COMMANDS: - window-post, wd, windowpost, wdpost Compute a proof-of-spacetime for a sector (requires the sector to be pre-sealed). These will not send to the chain. - help, h Shows a list of commands or help for one command - -OPTIONS: - --help, -h show help -``` - -### lotus-provider test window-post -``` -NAME: - lotus-provider test window-post - Compute a proof-of-spacetime for a sector (requires the sector to be pre-sealed). These will not send to the chain. - -USAGE: - lotus-provider test window-post command [command options] [arguments...] - -COMMANDS: - here, cli Compute WindowPoSt for performance and configuration testing. - task, scheduled, schedule, async, asynchronous Test the windowpost scheduler by running it on the next available lotus-provider. - help, h Shows a list of commands or help for one command - -OPTIONS: - --help, -h show help -``` - -#### lotus-provider test window-post here -``` -NAME: - lotus-provider test window-post here - Compute WindowPoSt for performance and configuration testing. - -USAGE: - lotus-provider test window-post here [command options] [deadline index] - -DESCRIPTION: - Note: This command is intended to be used to verify PoSt compute performance. - It will not send any messages to the chain. Since it can compute any deadline, output may be incorrectly timed for the chain. - -OPTIONS: - --deadline value deadline to compute WindowPoSt for (default: 0) - --layers value [ --layers value ] list of layers to be interpreted (atop defaults). Default: base (default: "base") - --storage-json value path to json file containing storage config (default: "~/.lotus-provider/storage.json") - --partition value partition to compute WindowPoSt for (default: 0) - --help, -h show help -``` - -#### lotus-provider test window-post task -``` -NAME: - lotus-provider test window-post task - Test the windowpost scheduler by running it on the next available lotus-provider. - -USAGE: - lotus-provider test window-post task [command options] [arguments...] - -OPTIONS: - --deadline value deadline to compute WindowPoSt for (default: 0) - --layers value [ --layers value ] list of layers to be interpreted (atop defaults). Default: base (default: "base") - --help, -h show help -``` - -## lotus-provider version -``` -NAME: - lotus-provider version - Print version - -USAGE: - lotus-provider version [command options] [arguments...] - -OPTIONS: - --help, -h show help -``` - -## lotus-provider auth -``` -NAME: - lotus-provider auth - Manage RPC permissions - -USAGE: - lotus-provider auth command [command options] [arguments...] - -COMMANDS: - create-token Create token - api-info Get token with API info required to connect to this node - help, h Shows a list of commands or help for one command - -OPTIONS: - --help, -h show help -``` - -### lotus-provider auth create-token -``` -NAME: - lotus-provider auth create-token - Create token - -USAGE: - lotus-provider auth create-token [command options] [arguments...] - -OPTIONS: - --perm value permission to assign to the token, one of: read, write, sign, admin - --help, -h show help -``` - -### lotus-provider auth api-info -``` -NAME: - lotus-provider auth api-info - Get token with API info required to connect to this node - -USAGE: - lotus-provider auth api-info [command options] [arguments...] - -OPTIONS: - --perm value permission to assign to the token, one of: read, write, sign, admin - --help, -h show help -``` - -## lotus-provider log -``` -NAME: - lotus-provider log - Manage logging - -USAGE: - lotus-provider log command [command options] [arguments...] - -COMMANDS: - list List log systems - set-level Set log level - alerts Get alert states - help, h Shows a list of commands or help for one command - -OPTIONS: - --help, -h show help -``` - -### lotus-provider log list -``` -NAME: - lotus-provider log list - List log systems - -USAGE: - lotus-provider log list [command options] [arguments...] - -OPTIONS: - --help, -h show help -``` - -### lotus-provider log set-level -``` -NAME: - lotus-provider log set-level - Set log level - -USAGE: - lotus-provider log set-level [command options] [level] - -DESCRIPTION: - Set the log level for logging systems: - - The system flag can be specified multiple times. - - eg) log set-level --system chain --system chainxchg debug - - Available Levels: - debug - info - warn - error - - Environment Variables: - GOLOG_LOG_LEVEL - Default log level for all log systems - GOLOG_LOG_FMT - Change output log format (json, nocolor) - GOLOG_FILE - Write logs to file - GOLOG_OUTPUT - Specify whether to output to file, stderr, stdout or a combination, i.e. file+stderr - - -OPTIONS: - --system value [ --system value ] limit to log system - --help, -h show help -``` - -### lotus-provider log alerts -``` -NAME: - lotus-provider log alerts - Get alert states - -USAGE: - lotus-provider log alerts [command options] [arguments...] - -OPTIONS: - --all get all (active and inactive) alerts (default: false) - --help, -h show help -``` - -## lotus-provider wait-api -``` -NAME: - lotus-provider wait-api - Wait for lotus api to come online - -USAGE: - lotus-provider wait-api [command options] [arguments...] - -CATEGORY: - DEVELOPER - -OPTIONS: - --timeout value duration to wait till fail (default: 30s) - --help, -h show help -``` - -## lotus-provider fetch-params -``` -NAME: - lotus-provider fetch-params - Fetch proving parameters - -USAGE: - lotus-provider fetch-params [command options] [sectorSize] - -CATEGORY: - DEVELOPER - -OPTIONS: - --help, -h show help -``` diff --git a/itests/harmonytask_test.go b/itests/harmonytask_test.go index 463f131d8..beef04c8d 100644 --- a/itests/harmonytask_test.go +++ b/itests/harmonytask_test.go @@ -90,7 +90,7 @@ func TestHarmonyTasks(t *testing.T) { e, err := harmonytask.New(cdb, []harmonytask.TaskInterface{t1}, "test:1") require.NoError(t, err) time.Sleep(time.Second) // do the work. FLAKYNESS RISK HERE. - e.GracefullyTerminate(time.Minute) + e.GracefullyTerminate() expected := []string{"taskResult56", "taskResult73"} sort.Strings(t1.WorkCompleted) require.Equal(t, expected, t1.WorkCompleted, "unexpected results") @@ -173,8 +173,8 @@ func TestHarmonyTasksWith2PartiesPolling(t *testing.T) { worker, err := harmonytask.New(cdb, []harmonytask.TaskInterface{workerParty}, "test:2") require.NoError(t, err) time.Sleep(time.Second) // do the work. FLAKYNESS RISK HERE. - sender.GracefullyTerminate(time.Second * 5) - worker.GracefullyTerminate(time.Second * 5) + sender.GracefullyTerminate() + worker.GracefullyTerminate() sort.Strings(dest) require.Equal(t, []string{"A", "B"}, dest) }) @@ -204,7 +204,7 @@ func TestWorkStealing(t *testing.T) { worker, err := harmonytask.New(cdb, []harmonytask.TaskInterface{fooLetterSaver(t, cdb, &dest)}, "test:2") require.ErrorIs(t, err, nil) time.Sleep(time.Second) // do the work. FLAKYNESS RISK HERE. - worker.GracefullyTerminate(time.Second * 5) + worker.GracefullyTerminate() require.Equal(t, []string{"M"}, dest) }) } @@ -243,8 +243,8 @@ func TestTaskRetry(t *testing.T) { rcv, err := harmonytask.New(cdb, []harmonytask.TaskInterface{fails2xPerMsg}, "test:2") require.NoError(t, err) time.Sleep(time.Second) - sender.GracefullyTerminate(time.Hour) - rcv.GracefullyTerminate(time.Hour) + sender.GracefullyTerminate() + rcv.GracefullyTerminate() sort.Strings(dest) require.Equal(t, []string{"A", "B"}, dest) type hist struct { diff --git a/itests/kit/ensemble.go b/itests/kit/ensemble.go index 9588d2526..03a36dc45 100644 --- a/itests/kit/ensemble.go +++ b/itests/kit/ensemble.go @@ -913,7 +913,7 @@ func (n *Ensemble) Start() *Ensemble { if err != nil { return nil } - defer taskEngine.GracefullyTerminate(time.Hour) + defer taskEngine.GracefullyTerminate() err = rpc.ListenAndServe(ctx, p.Deps, shutdownChan) // Monitor for shutdown. require.NoError(n.t, err) diff --git a/lib/harmony/harmonytask/harmonytask.go b/lib/harmony/harmonytask/harmonytask.go index 2d8036e2f..dc71b299c 100644 --- a/lib/harmony/harmonytask/harmonytask.go +++ b/lib/harmony/harmonytask/harmonytask.go @@ -193,22 +193,56 @@ func New( // GracefullyTerminate hangs until all present tasks have completed. // Call this to cleanly exit the process. As some processes are long-running, // passing a deadline will ignore those still running (to be picked-up later). -func (e *TaskEngine) GracefullyTerminate(deadline time.Duration) { +func (e *TaskEngine) GracefullyTerminate() { + + // call the cancel func to avoid picking up any new tasks. Running tasks have context.Background() + // Call shutdown to stop posting heartbeat to DB. e.grace() e.reg.Shutdown() - deadlineChan := time.NewTimer(deadline).C -top: - for _, h := range e.handlers { - if h.Count.Load() > 0 { - select { - case <-deadlineChan: - return - default: - time.Sleep(time.Millisecond) - goto top + + // If there are any Post tasks then wait till Timeout and check again + // When no Post tasks are active, break out of loop and call the shutdown function + for { + timeout := time.Millisecond + for _, h := range e.handlers { + if h.TaskTypeDetails.Name == "WinPost" && h.Count.Load() > 0 { + timeout = time.Second + log.Infof("node shutdown deferred for %f seconds", timeout.Seconds()) + continue + } + if h.TaskTypeDetails.Name == "WdPost" && h.Count.Load() > 0 { + timeout = time.Second * 3 + log.Infof("node shutdown deferred for %f seconds due to running WdPost task", timeout.Seconds()) + continue + } + + if h.TaskTypeDetails.Name == "WdPostSubmit" && h.Count.Load() > 0 { + timeout = time.Second + log.Infof("node shutdown deferred for %f seconds due to running WdPostSubmit task", timeout.Seconds()) + continue + } + + if h.TaskTypeDetails.Name == "WdPostRecover" && h.Count.Load() > 0 { + timeout = time.Second + log.Infof("node shutdown deferred for %f seconds due to running WdPostRecover task", timeout.Seconds()) + continue + } + + // Test tasks for itest + if h.TaskTypeDetails.Name == "ThingOne" && h.Count.Load() > 0 { + timeout = time.Second + log.Infof("node shutdown deferred for %f seconds due to running itest task", timeout.Seconds()) + continue } } + if timeout > time.Millisecond { + time.Sleep(timeout) + continue + } + break } + + return } func (e *TaskEngine) poller() { From b95e95f4d609ce2c59408c7ddc6191e3ff7eab34 Mon Sep 17 00:00:00 2001 From: "Andrew Jackson (Ajax)" Date: Mon, 1 Apr 2024 10:30:35 -0500 Subject: [PATCH 303/394] feat: SPTool (#11788) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * sptool: Initial structure * sptool: Port lotus-miner actor withdraw * sptool: Make cli docsgen happy * actors are done * info * proving * sptool the rest * fixed gitignore * lints * oops * 2 * terminate * fixes * sptool: improve sectors list --------- Co-authored-by: Łukasz Magiera --- .gitignore | 2 +- Makefile | 24 +- cli/clicommands/cmd.go | 30 + cli/clicommands/state.go | 70 ++ cli/client.go | 2 +- cli/cmd.go | 23 - cli/filplus.go | 2 +- cli/info.go | 2 +- cli/multisig.go | 2 +- cli/paych.go | 2 +- cli/send.go | 2 +- cli/send_test.go | 4 +- cli/spcli/actor.go | 1240 ++++++++++++++++++++++++ cli/spcli/info.go | 121 +++ cli/spcli/proving.go | 451 +++++++++ cli/spcli/sectors.go | 1398 +++++++++++++++++++++++++++ cli/spcli/statemeta.go | 95 ++ cli/spcli/util.go | 9 + cli/state.go | 147 --- cli/wallet.go | 2 +- cmd/lotus-miner/actor.go | 1291 +------------------------ cmd/lotus-miner/actor_test.go | 7 +- cmd/lotus-miner/info.go | 97 +- cmd/lotus-miner/info_all.go | 11 +- cmd/lotus-miner/main.go | 14 + cmd/lotus-miner/precommits-info.go | 56 -- cmd/lotus-miner/proving.go | 433 +-------- cmd/lotus-miner/sectors.go | 1026 +------------------- cmd/lotus-miner/storage.go | 5 +- cmd/lotus-shed/sectors.go | 141 +-- cmd/lotus/main.go | 3 +- cmd/sptool/actor.go | 140 +++ cmd/sptool/main.go | 84 ++ cmd/sptool/proving.go | 18 + cmd/sptool/sector.go | 356 +++++++ documentation/en/cli-curio.md | 37 +- documentation/en/cli-lotus-miner.md | 318 +++--- documentation/en/cli-sptool.md | 441 +++++++++ itests/cli_test.go | 4 +- itests/gateway_test.go | 4 +- itests/multisig/suite.go | 4 +- itests/paych_cli_test.go | 10 +- node/impl/storminer.go | 14 +- scripts/generate-lotus-cli.py | 6 +- 44 files changed, 4765 insertions(+), 3383 deletions(-) create mode 100644 cli/clicommands/cmd.go create mode 100644 cli/clicommands/state.go create mode 100644 cli/spcli/actor.go create mode 100644 cli/spcli/info.go create mode 100644 cli/spcli/proving.go create mode 100644 cli/spcli/sectors.go create mode 100644 cli/spcli/statemeta.go create mode 100644 cli/spcli/util.go delete mode 100644 cmd/lotus-miner/precommits-info.go create mode 100644 cmd/sptool/actor.go create mode 100644 cmd/sptool/main.go create mode 100644 cmd/sptool/proving.go create mode 100644 cmd/sptool/sector.go create mode 100644 documentation/en/cli-sptool.md diff --git a/.gitignore b/.gitignore index ea354ba73..d620e557b 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ /lotus-shed /lotus-sim /curio +/sptool /lotus-townhall /lotus-fountain /lotus-stats @@ -36,7 +37,6 @@ build/paramfetch.sh /darwin /linux *.snap -curio devgen.car localnet.json diff --git a/Makefile b/Makefile index 6a1c9cc8a..83e3fd89d 100644 --- a/Makefile +++ b/Makefile @@ -66,7 +66,7 @@ CLEAN+=build/.update-modules deps: $(BUILD_DEPS) .PHONY: deps -build-devnets: build lotus-seed lotus-shed curio +build-devnets: build lotus-seed lotus-shed curio sptool .PHONY: build-devnets debug: GOFLAGS+=-tags=debug @@ -106,6 +106,12 @@ BINS+=curio cu2k: GOFLAGS+=-tags=2k cu2k: curio +sptool: $(BUILD_DEPS) + rm -f sptool + $(GOCC) build $(GOFLAGS) -o sptool ./cmd/sptool +.PHONY: sptool +BINS+=sptool + lotus-worker: $(BUILD_DEPS) rm -f lotus-worker $(GOCC) build $(GOFLAGS) -o lotus-worker ./cmd/lotus-worker @@ -124,13 +130,13 @@ lotus-gateway: $(BUILD_DEPS) .PHONY: lotus-gateway BINS+=lotus-gateway -build: lotus lotus-miner lotus-worker curio +build: lotus lotus-miner lotus-worker curio sptool @[[ $$(type -P "lotus") ]] && echo "Caution: you have \ an existing lotus binary in your PATH. This may cause problems if you don't run 'sudo make install'" || true .PHONY: build -install: install-daemon install-miner install-worker install-curio +install: install-daemon install-miner install-worker install-curio install-sptool install-daemon: install -C ./lotus /usr/local/bin/lotus @@ -141,6 +147,9 @@ install-miner: install-curio: install -C ./curio /usr/local/bin/curio +install-sptool: + install -C ./sptool /usr/local/bin/sptool + install-worker: install -C ./lotus-worker /usr/local/bin/lotus-worker @@ -159,6 +168,9 @@ uninstall-miner: uninstall-curio: rm -f /usr/local/bin/curio +uninstall-sptool: + rm -f /usr/local/bin/sptool + uninstall-worker: rm -f /usr/local/bin/lotus-worker @@ -260,7 +272,7 @@ install-miner-service: install-miner install-daemon-service @echo "To start the service, run: 'sudo systemctl start lotus-miner'" @echo "To enable the service on startup, run: 'sudo systemctl enable lotus-miner'" -install-curio-service: install-curio install-daemon-service +install-curio-service: install-curio install-sptool install-daemon-service mkdir -p /etc/systemd/system mkdir -p /var/log/lotus install -C -m 0644 ./scripts/curio.service /etc/systemd/system/curio.service @@ -401,12 +413,12 @@ gen: actors-code-gen type-gen cfgdoc-gen docsgen api-gen circleci jen: gen -snap: lotus lotus-miner lotus-worker curio +snap: lotus lotus-miner lotus-worker curio sptool snapcraft # snapcraft upload ./lotus_*.snap # separate from gen because it needs binaries -docsgen-cli: lotus lotus-miner lotus-worker curio +docsgen-cli: lotus lotus-miner lotus-worker curio sptool python3 ./scripts/generate-lotus-cli.py ./lotus config default > documentation/en/default-lotus-config.toml ./lotus-miner config default > documentation/en/default-lotus-miner-config.toml diff --git a/cli/clicommands/cmd.go b/cli/clicommands/cmd.go new file mode 100644 index 000000000..a37ce329a --- /dev/null +++ b/cli/clicommands/cmd.go @@ -0,0 +1,30 @@ +package clicommands + +import ( + "github.com/urfave/cli/v2" + + lcli "github.com/filecoin-project/lotus/cli" +) + +var Commands = []*cli.Command{ + lcli.WithCategory("basic", lcli.SendCmd), + lcli.WithCategory("basic", lcli.WalletCmd), + lcli.WithCategory("basic", lcli.InfoCmd), + lcli.WithCategory("basic", lcli.ClientCmd), + lcli.WithCategory("basic", lcli.MultisigCmd), + lcli.WithCategory("basic", lcli.FilplusCmd), + lcli.WithCategory("basic", lcli.PaychCmd), + lcli.WithCategory("developer", lcli.AuthCmd), + lcli.WithCategory("developer", lcli.MpoolCmd), + lcli.WithCategory("developer", StateCmd), + lcli.WithCategory("developer", lcli.ChainCmd), + lcli.WithCategory("developer", lcli.LogCmd), + lcli.WithCategory("developer", lcli.WaitApiCmd), + lcli.WithCategory("developer", lcli.FetchParamCmd), + lcli.WithCategory("developer", lcli.EvmCmd), + lcli.WithCategory("network", lcli.NetCmd), + lcli.WithCategory("network", lcli.SyncCmd), + lcli.WithCategory("status", lcli.StatusCmd), + lcli.PprofCmd, + lcli.VersionCmd, +} diff --git a/cli/clicommands/state.go b/cli/clicommands/state.go new file mode 100644 index 000000000..e990cceb0 --- /dev/null +++ b/cli/clicommands/state.go @@ -0,0 +1,70 @@ +// Package clicommands contains only the cli.Command definitions that are +// common to sptool and miner. These are here because they can't be referenced +// in cli/spcli or cli/ because of the import cycle with all the other cli functions. +package clicommands + +import ( + "github.com/urfave/cli/v2" + + "github.com/filecoin-project/go-address" + + lcli "github.com/filecoin-project/lotus/cli" + "github.com/filecoin-project/lotus/cli/spcli" +) + +var StateCmd = &cli.Command{ + Name: "state", + Usage: "Interact with and query filecoin chain state", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "tipset", + Usage: "specify tipset to call method on (pass comma separated array of cids)", + }, + }, + Subcommands: []*cli.Command{ + lcli.StatePowerCmd, + lcli.StateSectorsCmd, + lcli.StateActiveSectorsCmd, + lcli.StateListActorsCmd, + lcli.StateListMinersCmd, + lcli.StateCircSupplyCmd, + lcli.StateSectorCmd, + lcli.StateGetActorCmd, + lcli.StateLookupIDCmd, + lcli.StateReplayCmd, + lcli.StateSectorSizeCmd, + lcli.StateReadStateCmd, + lcli.StateListMessagesCmd, + lcli.StateComputeStateCmd, + lcli.StateCallCmd, + lcli.StateGetDealSetCmd, + lcli.StateWaitMsgCmd, + lcli.StateSearchMsgCmd, + StateMinerInfo, + lcli.StateMarketCmd, + lcli.StateExecTraceCmd, + lcli.StateNtwkVersionCmd, + lcli.StateMinerProvingDeadlineCmd, + lcli.StateSysActorCIDsCmd, + }, +} + +var StateMinerInfo = &cli.Command{ + Name: "miner-info", + Usage: "Retrieve miner information", + ArgsUsage: "[minerAddress]", + Action: func(cctx *cli.Context) error { + addressGetter := func(_ *cli.Context) (address.Address, error) { + if cctx.NArg() != 1 { + return address.Address{}, lcli.IncorrectNumArgs(cctx) + } + + return address.NewFromString(cctx.Args().First()) + } + err := spcli.InfoCmd(addressGetter).Action(cctx) + if err != nil { + return err + } + return nil + }, +} diff --git a/cli/client.go b/cli/client.go index 81299b8fb..302e31e98 100644 --- a/cli/client.go +++ b/cli/client.go @@ -74,7 +74,7 @@ func GetCidEncoder(cctx *cli.Context) (cidenc.Encoder, error) { return e, nil } -var clientCmd = &cli.Command{ +var ClientCmd = &cli.Command{ Name: "client", Usage: "Make deals, store data, retrieve data", Subcommands: []*cli.Command{ diff --git a/cli/cmd.go b/cli/cmd.go index 802df0c99..76c0ab300 100644 --- a/cli/cmd.go +++ b/cli/cmd.go @@ -66,29 +66,6 @@ var CommonCommands = []*cli.Command{ VersionCmd, } -var Commands = []*cli.Command{ - WithCategory("basic", sendCmd), - WithCategory("basic", walletCmd), - WithCategory("basic", infoCmd), - WithCategory("basic", clientCmd), - WithCategory("basic", multisigCmd), - WithCategory("basic", filplusCmd), - WithCategory("basic", paychCmd), - WithCategory("developer", AuthCmd), - WithCategory("developer", MpoolCmd), - WithCategory("developer", StateCmd), - WithCategory("developer", ChainCmd), - WithCategory("developer", LogCmd), - WithCategory("developer", WaitApiCmd), - WithCategory("developer", FetchParamCmd), - WithCategory("developer", EvmCmd), - WithCategory("network", NetCmd), - WithCategory("network", SyncCmd), - WithCategory("status", StatusCmd), - PprofCmd, - VersionCmd, -} - func WithCategory(cat string, cmd *cli.Command) *cli.Command { cmd.Category = strings.ToUpper(cat) return cmd diff --git a/cli/filplus.go b/cli/filplus.go index 8c39c21e8..b8e887498 100644 --- a/cli/filplus.go +++ b/cli/filplus.go @@ -39,7 +39,7 @@ import ( "github.com/filecoin-project/lotus/lib/tablewriter" ) -var filplusCmd = &cli.Command{ +var FilplusCmd = &cli.Command{ Name: "filplus", Usage: "Interact with the verified registry actor used by Filplus", Flags: []cli.Flag{}, diff --git a/cli/info.go b/cli/info.go index 8b36be488..a406fc480 100644 --- a/cli/info.go +++ b/cli/info.go @@ -23,7 +23,7 @@ import ( "github.com/filecoin-project/lotus/journal/alerting" ) -var infoCmd = &cli.Command{ +var InfoCmd = &cli.Command{ Name: "info", Usage: "Print node info", Action: infoCmdAct, diff --git a/cli/multisig.go b/cli/multisig.go index 1af2a4c9e..290cf6700 100644 --- a/cli/multisig.go +++ b/cli/multisig.go @@ -32,7 +32,7 @@ import ( "github.com/filecoin-project/lotus/chain/types" ) -var multisigCmd = &cli.Command{ +var MultisigCmd = &cli.Command{ Name: "msig", Usage: "Interact with a multisig wallet", Flags: []cli.Flag{ diff --git a/cli/paych.go b/cli/paych.go index 1067d0913..46b043d6a 100644 --- a/cli/paych.go +++ b/cli/paych.go @@ -20,7 +20,7 @@ import ( "github.com/filecoin-project/lotus/paychmgr" ) -var paychCmd = &cli.Command{ +var PaychCmd = &cli.Command{ Name: "paych", Usage: "Manage payment channels", Subcommands: []*cli.Command{ diff --git a/cli/send.go b/cli/send.go index cfa2515c0..89c79e109 100644 --- a/cli/send.go +++ b/cli/send.go @@ -19,7 +19,7 @@ import ( "github.com/filecoin-project/lotus/chain/types/ethtypes" ) -var sendCmd = &cli.Command{ +var SendCmd = &cli.Command{ Name: "send", Usage: "Send funds between accounts", ArgsUsage: "[targetAddress] [amount]", diff --git a/cli/send_test.go b/cli/send_test.go index 2c59a9641..59b8942f4 100644 --- a/cli/send_test.go +++ b/cli/send_test.go @@ -45,7 +45,7 @@ func TestSendCLI(t *testing.T) { oneFil := abi.TokenAmount(types.MustParseFIL("1")) t.Run("simple", func(t *testing.T) { - app, mockSrvcs, buf, done := newMockApp(t, sendCmd) + app, mockSrvcs, buf, done := newMockApp(t, SendCmd) defer done() arbtProto := &api.MessagePrototype{ @@ -76,7 +76,7 @@ func TestSendEthereum(t *testing.T) { oneFil := abi.TokenAmount(types.MustParseFIL("1")) t.Run("simple", func(t *testing.T) { - app, mockSrvcs, buf, done := newMockApp(t, sendCmd) + app, mockSrvcs, buf, done := newMockApp(t, SendCmd) defer done() testEthAddr, err := ethtypes.CastEthAddress(make([]byte, 20)) diff --git a/cli/spcli/actor.go b/cli/spcli/actor.go new file mode 100644 index 000000000..296d5ffb1 --- /dev/null +++ b/cli/spcli/actor.go @@ -0,0 +1,1240 @@ +package spcli + +import ( + "bytes" + "fmt" + "strconv" + + cbor "github.com/ipfs/go-ipld-cbor" + "github.com/libp2p/go-libp2p/core/peer" + ma "github.com/multiformats/go-multiaddr" + "github.com/urfave/cli/v2" + "golang.org/x/xerrors" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-bitfield" + rlepluslazy "github.com/filecoin-project/go-bitfield/rle" + "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/go-state-types/big" + "github.com/filecoin-project/go-state-types/builtin" + "github.com/filecoin-project/go-state-types/builtin/v9/miner" + "github.com/filecoin-project/go-state-types/network" + + "github.com/filecoin-project/lotus/api" + lapi "github.com/filecoin-project/lotus/api" + "github.com/filecoin-project/lotus/blockstore" + "github.com/filecoin-project/lotus/build" + "github.com/filecoin-project/lotus/chain/actors" + "github.com/filecoin-project/lotus/chain/actors/adt" + lminer "github.com/filecoin-project/lotus/chain/actors/builtin/miner" + "github.com/filecoin-project/lotus/chain/types" + lcli "github.com/filecoin-project/lotus/cli" + "github.com/filecoin-project/lotus/node/impl" +) + +func ActorWithdrawCmd(getActor ActorAddressGetter) *cli.Command { + return &cli.Command{ + Name: "withdraw", + Usage: "withdraw available balance to beneficiary", + ArgsUsage: "[amount (FIL)]", + Flags: []cli.Flag{ + &cli.IntFlag{ + Name: "confidence", + Usage: "number of block confirmations to wait for", + Value: int(build.MessageConfidence), + }, + &cli.BoolFlag{ + Name: "beneficiary", + Usage: "send withdraw message from the beneficiary address", + }, + }, + Action: func(cctx *cli.Context) error { + amount := abi.NewTokenAmount(0) + + if cctx.Args().Present() { + f, err := types.ParseFIL(cctx.Args().First()) + if err != nil { + return xerrors.Errorf("parsing 'amount' argument: %w", err) + } + + amount = abi.TokenAmount(f) + } + + api, acloser, err := lcli.GetFullNodeAPIV1(cctx) + if err != nil { + return err + } + defer acloser() + + ctx := lcli.ReqContext(cctx) + + maddr, err := getActor(cctx) + if err != nil { + return err + } + + res, err := impl.WithdrawBalance(ctx, api, maddr, amount, !cctx.IsSet("beneficiary")) + if err != nil { + return err + } + + fmt.Printf("Requested withdrawal in message %s\nwaiting for it to be included in a block..\n", res) + + // wait for it to get mined into a block + wait, err := api.StateWaitMsg(ctx, res, uint64(cctx.Int("confidence")), lapi.LookbackNoLimit, true) + if err != nil { + return xerrors.Errorf("Timeout waiting for withdrawal message %s", res) + } + + if wait.Receipt.ExitCode.IsError() { + return xerrors.Errorf("Failed to execute withdrawal message %s: %w", wait.Message, wait.Receipt.ExitCode.Error()) + } + + nv, err := api.StateNetworkVersion(ctx, wait.TipSet) + if err != nil { + return err + } + + if nv >= network.Version14 { + var withdrawn abi.TokenAmount + if err := withdrawn.UnmarshalCBOR(bytes.NewReader(wait.Receipt.Return)); err != nil { + return err + } + + fmt.Printf("Successfully withdrew %s \n", types.FIL(withdrawn)) + if withdrawn.LessThan(amount) { + fmt.Printf("Note that this is less than the requested amount of %s\n", types.FIL(amount)) + } + } + + return nil + }, + } +} + +func ActorSetAddrsCmd(getActor ActorAddressGetter) *cli.Command { + return &cli.Command{ + Name: "set-addresses", + Aliases: []string{"set-addrs"}, + Usage: "set addresses that your miner can be publicly dialed on", + ArgsUsage: "", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "from", + Usage: "optionally specify the account to send the message from", + }, + &cli.Int64Flag{ + Name: "gas-limit", + Usage: "set gas limit", + Value: 0, + }, + &cli.BoolFlag{ + Name: "unset", + Usage: "unset address", + Value: false, + }, + }, + Action: func(cctx *cli.Context) error { + args := cctx.Args().Slice() + unset := cctx.Bool("unset") + if len(args) == 0 && !unset { + return cli.ShowSubcommandHelp(cctx) + } + if len(args) > 0 && unset { + return fmt.Errorf("unset can only be used with no arguments") + } + + api, acloser, err := lcli.GetFullNodeAPI(cctx) + if err != nil { + return err + } + defer acloser() + + ctx := lcli.ReqContext(cctx) + + var addrs []abi.Multiaddrs + for _, a := range args { + maddr, err := ma.NewMultiaddr(a) + if err != nil { + return fmt.Errorf("failed to parse %q as a multiaddr: %w", a, err) + } + + maddrNop2p, strip := ma.SplitFunc(maddr, func(c ma.Component) bool { + return c.Protocol().Code == ma.P_P2P + }) + + if strip != nil { + fmt.Println("Stripping peerid ", strip, " from ", maddr) + } + addrs = append(addrs, maddrNop2p.Bytes()) + } + + maddr, err := getActor(cctx) + if err != nil { + return err + } + + minfo, err := api.StateMinerInfo(ctx, maddr, types.EmptyTSK) + if err != nil { + return err + } + + fromAddr := minfo.Worker + if from := cctx.String("from"); from != "" { + addr, err := address.NewFromString(from) + if err != nil { + return err + } + + fromAddr = addr + } + + fromId, err := api.StateLookupID(ctx, fromAddr, types.EmptyTSK) + if err != nil { + return err + } + + if !isController(minfo, fromId) { + return xerrors.Errorf("sender isn't a controller of miner: %s", fromId) + } + + params, err := actors.SerializeParams(&miner.ChangeMultiaddrsParams{NewMultiaddrs: addrs}) + if err != nil { + return err + } + + gasLimit := cctx.Int64("gas-limit") + + smsg, err := api.MpoolPushMessage(ctx, &types.Message{ + To: maddr, + From: fromId, + Value: types.NewInt(0), + GasLimit: gasLimit, + Method: builtin.MethodsMiner.ChangeMultiaddrs, + Params: params, + }, nil) + if err != nil { + return err + } + + fmt.Printf("Requested multiaddrs change in message %s\n", smsg.Cid()) + return nil + + }, + } +} + +func ActorSetPeeridCmd(getActor ActorAddressGetter) *cli.Command { + return &cli.Command{ + Name: "set-peer-id", + Usage: "set the peer id of your miner", + ArgsUsage: "", + Flags: []cli.Flag{ + &cli.Int64Flag{ + Name: "gas-limit", + Usage: "set gas limit", + Value: 0, + }, + }, + Action: func(cctx *cli.Context) error { + + api, acloser, err := lcli.GetFullNodeAPI(cctx) + if err != nil { + return err + } + defer acloser() + + ctx := lcli.ReqContext(cctx) + + if cctx.NArg() != 1 { + return lcli.IncorrectNumArgs(cctx) + } + + pid, err := peer.Decode(cctx.Args().Get(0)) + if err != nil { + return fmt.Errorf("failed to parse input as a peerId: %w", err) + } + + maddr, err := getActor(cctx) + if err != nil { + return err + } + + minfo, err := api.StateMinerInfo(ctx, maddr, types.EmptyTSK) + if err != nil { + return err + } + + params, err := actors.SerializeParams(&miner.ChangePeerIDParams{NewID: abi.PeerID(pid)}) + if err != nil { + return err + } + + gasLimit := cctx.Int64("gas-limit") + + smsg, err := api.MpoolPushMessage(ctx, &types.Message{ + To: maddr, + From: minfo.Worker, + Value: types.NewInt(0), + GasLimit: gasLimit, + Method: builtin.MethodsMiner.ChangePeerID, + Params: params, + }, nil) + if err != nil { + return err + } + + fmt.Printf("Requested peerid change in message %s\n", smsg.Cid()) + return nil + + }, + } +} + +func ActorRepayDebtCmd(getActor ActorAddressGetter) *cli.Command { + return &cli.Command{ + Name: "repay-debt", + Usage: "pay down a miner's debt", + ArgsUsage: "[amount (FIL)]", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "from", + Usage: "optionally specify the account to send funds from", + }, + }, + Action: func(cctx *cli.Context) error { + api, acloser, err := lcli.GetFullNodeAPI(cctx) + if err != nil { + return err + } + defer acloser() + + ctx := lcli.ReqContext(cctx) + + maddr, err := getActor(cctx) + if err != nil { + return err + } + + mi, err := api.StateMinerInfo(ctx, maddr, types.EmptyTSK) + if err != nil { + return err + } + + var amount abi.TokenAmount + if cctx.Args().Present() { + f, err := types.ParseFIL(cctx.Args().First()) + if err != nil { + return xerrors.Errorf("parsing 'amount' argument: %w", err) + } + + amount = abi.TokenAmount(f) + } else { + mact, err := api.StateGetActor(ctx, maddr, types.EmptyTSK) + if err != nil { + return err + } + + store := adt.WrapStore(ctx, cbor.NewCborStore(blockstore.NewAPIBlockstore(api))) + + mst, err := lminer.Load(store, mact) + if err != nil { + return err + } + + amount, err = mst.FeeDebt() + if err != nil { + return err + } + + } + + fromAddr := mi.Worker + if from := cctx.String("from"); from != "" { + addr, err := address.NewFromString(from) + if err != nil { + return err + } + + fromAddr = addr + } + + fromId, err := api.StateLookupID(ctx, fromAddr, types.EmptyTSK) + if err != nil { + return err + } + + if !isController(mi, fromId) { + return xerrors.Errorf("sender isn't a controller of miner: %s", fromId) + } + + smsg, err := api.MpoolPushMessage(ctx, &types.Message{ + To: maddr, + From: fromId, + Value: amount, + Method: builtin.MethodsMiner.RepayDebt, + Params: nil, + }, nil) + if err != nil { + return err + } + + fmt.Printf("Sent repay debt message %s\n", smsg.Cid()) + + return nil + }, + } +} + +func ActorControlCmd(getActor ActorAddressGetter, actorControlListCmd *cli.Command) *cli.Command { + return &cli.Command{ + Name: "control", + Usage: "Manage control addresses", + Subcommands: []*cli.Command{ + actorControlListCmd, + actorControlSet(getActor), + }, + } +} + +func actorControlSet(getActor ActorAddressGetter) *cli.Command { + return &cli.Command{ + Name: "set", + Usage: "Set control address(-es)", + ArgsUsage: "[...address]", + Flags: []cli.Flag{ + &cli.BoolFlag{ + Name: "really-do-it", + Usage: "Actually send transaction performing the action", + Value: false, + }, + }, + Action: func(cctx *cli.Context) error { + + api, acloser, err := lcli.GetFullNodeAPI(cctx) + if err != nil { + return err + } + defer acloser() + + ctx := lcli.ReqContext(cctx) + + maddr, err := getActor(cctx) + if err != nil { + return err + } + + mi, err := api.StateMinerInfo(ctx, maddr, types.EmptyTSK) + if err != nil { + return err + } + + del := map[address.Address]struct{}{} + existing := map[address.Address]struct{}{} + for _, controlAddress := range mi.ControlAddresses { + ka, err := api.StateAccountKey(ctx, controlAddress, types.EmptyTSK) + if err != nil { + return err + } + + del[ka] = struct{}{} + existing[ka] = struct{}{} + } + + var toSet []address.Address + + for i, as := range cctx.Args().Slice() { + a, err := address.NewFromString(as) + if err != nil { + return xerrors.Errorf("parsing address %d: %w", i, err) + } + + ka, err := api.StateAccountKey(ctx, a, types.EmptyTSK) + if err != nil { + return err + } + + // make sure the address exists on chain + _, err = api.StateLookupID(ctx, ka, types.EmptyTSK) + if err != nil { + return xerrors.Errorf("looking up %s: %w", ka, err) + } + + delete(del, ka) + toSet = append(toSet, ka) + } + + for a := range del { + fmt.Println("Remove", a) + } + for _, a := range toSet { + if _, exists := existing[a]; !exists { + fmt.Println("Add", a) + } + } + + if !cctx.Bool("really-do-it") { + fmt.Println("Pass --really-do-it to actually execute this action") + return nil + } + + cwp := &miner.ChangeWorkerAddressParams{ + NewWorker: mi.Worker, + NewControlAddrs: toSet, + } + + sp, err := actors.SerializeParams(cwp) + if err != nil { + return xerrors.Errorf("serializing params: %w", err) + } + + smsg, err := api.MpoolPushMessage(ctx, &types.Message{ + From: mi.Owner, + To: maddr, + Method: builtin.MethodsMiner.ChangeWorkerAddress, + + Value: big.Zero(), + Params: sp, + }, nil) + if err != nil { + return xerrors.Errorf("mpool push: %w", err) + } + + fmt.Println("Message CID:", smsg.Cid()) + + return nil + }, + } +} + +func ActorSetOwnerCmd(getActor ActorAddressGetter) *cli.Command { + return &cli.Command{ + Name: "set-owner", + Usage: "Set owner address (this command should be invoked twice, first with the old owner as the senderAddress, and then with the new owner)", + ArgsUsage: "[newOwnerAddress senderAddress]", + Flags: []cli.Flag{ + &cli.BoolFlag{ + Name: "really-do-it", + Usage: "Actually send transaction performing the action", + Value: false, + }, + }, + Action: func(cctx *cli.Context) error { + if cctx.NArg() != 2 { + return lcli.IncorrectNumArgs(cctx) + } + + if !cctx.Bool("really-do-it") { + fmt.Println("Pass --really-do-it to actually execute this action") + return nil + } + + api, acloser, err := lcli.GetFullNodeAPI(cctx) + if err != nil { + return err + } + defer acloser() + + ctx := lcli.ReqContext(cctx) + + na, err := address.NewFromString(cctx.Args().First()) + if err != nil { + return err + } + + newAddrId, err := api.StateLookupID(ctx, na, types.EmptyTSK) + if err != nil { + return err + } + + fa, err := address.NewFromString(cctx.Args().Get(1)) + if err != nil { + return err + } + + fromAddrId, err := api.StateLookupID(ctx, fa, types.EmptyTSK) + if err != nil { + return err + } + + maddr, err := getActor(cctx) + if err != nil { + return err + } + + mi, err := api.StateMinerInfo(ctx, maddr, types.EmptyTSK) + if err != nil { + return err + } + + if fromAddrId != mi.Owner && fromAddrId != newAddrId { + return xerrors.New("from address must either be the old owner or the new owner") + } + + sp, err := actors.SerializeParams(&newAddrId) + if err != nil { + return xerrors.Errorf("serializing params: %w", err) + } + + smsg, err := api.MpoolPushMessage(ctx, &types.Message{ + From: fromAddrId, + To: maddr, + Method: builtin.MethodsMiner.ChangeOwnerAddress, + Value: big.Zero(), + Params: sp, + }, nil) + if err != nil { + return xerrors.Errorf("mpool push: %w", err) + } + + fmt.Println("Message CID:", smsg.Cid()) + + // wait for it to get mined into a block + wait, err := api.StateWaitMsg(ctx, smsg.Cid(), build.MessageConfidence) + if err != nil { + return err + } + + // check it executed successfully + if wait.Receipt.ExitCode.IsError() { + fmt.Println("owner change failed!") + return err + } + + fmt.Println("message succeeded!") + + return nil + }, + } +} + +func ActorProposeChangeWorkerCmd(getActor ActorAddressGetter) *cli.Command { + return &cli.Command{ + Name: "propose-change-worker", + Usage: "Propose a worker address change", + ArgsUsage: "[address]", + Flags: []cli.Flag{ + &cli.BoolFlag{ + Name: "really-do-it", + Usage: "Actually send transaction performing the action", + Value: false, + }, + }, + Action: func(cctx *cli.Context) error { + if !cctx.Args().Present() { + return fmt.Errorf("must pass address of new worker address") + } + + api, acloser, err := lcli.GetFullNodeAPI(cctx) + if err != nil { + return err + } + defer acloser() + + ctx := lcli.ReqContext(cctx) + + na, err := address.NewFromString(cctx.Args().First()) + if err != nil { + return err + } + + newAddr, err := api.StateLookupID(ctx, na, types.EmptyTSK) + if err != nil { + return err + } + + maddr, err := getActor(cctx) + if err != nil { + return err + } + + mi, err := api.StateMinerInfo(ctx, maddr, types.EmptyTSK) + if err != nil { + return err + } + + if mi.NewWorker.Empty() { + if mi.Worker == newAddr { + return fmt.Errorf("worker address already set to %s", na) + } + } else { + if mi.NewWorker == newAddr { + return fmt.Errorf("change to worker address %s already pending", na) + } + } + + if !cctx.Bool("really-do-it") { + fmt.Fprintln(cctx.App.Writer, "Pass --really-do-it to actually execute this action") + return nil + } + + cwp := &miner.ChangeWorkerAddressParams{ + NewWorker: newAddr, + NewControlAddrs: mi.ControlAddresses, + } + + sp, err := actors.SerializeParams(cwp) + if err != nil { + return xerrors.Errorf("serializing params: %w", err) + } + + smsg, err := api.MpoolPushMessage(ctx, &types.Message{ + From: mi.Owner, + To: maddr, + Method: builtin.MethodsMiner.ChangeWorkerAddress, + Value: big.Zero(), + Params: sp, + }, nil) + if err != nil { + return xerrors.Errorf("mpool push: %w", err) + } + + fmt.Fprintln(cctx.App.Writer, "Propose Message CID:", smsg.Cid()) + + // wait for it to get mined into a block + wait, err := api.StateWaitMsg(ctx, smsg.Cid(), build.MessageConfidence) + if err != nil { + return err + } + + // check it executed successfully + if wait.Receipt.ExitCode.IsError() { + return fmt.Errorf("propose worker change failed") + } + + mi, err = api.StateMinerInfo(ctx, maddr, wait.TipSet) + if err != nil { + return err + } + if mi.NewWorker != newAddr { + return fmt.Errorf("Proposed worker address change not reflected on chain: expected '%s', found '%s'", na, mi.NewWorker) + } + + fmt.Fprintf(cctx.App.Writer, "Worker key change to %s successfully sent, change happens at height %d.\n", na, mi.WorkerChangeEpoch) + fmt.Fprintf(cctx.App.Writer, "If you have no active deadlines, call 'confirm-change-worker' at or after height %d to complete.\n", mi.WorkerChangeEpoch) + + return nil + }, + } +} + +func ActorProposeChangeBeneficiaryCmd(getActor ActorAddressGetter) *cli.Command { + + return &cli.Command{ + Name: "propose-change-beneficiary", + Usage: "Propose a beneficiary address change", + ArgsUsage: "[beneficiaryAddress quota expiration]", + Flags: []cli.Flag{ + &cli.BoolFlag{ + Name: "really-do-it", + Usage: "Actually send transaction performing the action", + Value: false, + }, + &cli.BoolFlag{ + Name: "overwrite-pending-change", + Usage: "Overwrite the current beneficiary change proposal", + Value: false, + }, + &cli.StringFlag{ + Name: "actor", + Usage: "specify the address of miner actor", + }, + }, + Action: func(cctx *cli.Context) error { + if cctx.NArg() != 3 { + return lcli.IncorrectNumArgs(cctx) + } + + api, acloser, err := lcli.GetFullNodeAPI(cctx) + if err != nil { + return xerrors.Errorf("getting fullnode api: %w", err) + } + defer acloser() + + ctx := lcli.ReqContext(cctx) + + na, err := address.NewFromString(cctx.Args().Get(0)) + if err != nil { + return xerrors.Errorf("parsing beneficiary address: %w", err) + } + + newAddr, err := api.StateLookupID(ctx, na, types.EmptyTSK) + if err != nil { + return xerrors.Errorf("looking up new beneficiary address: %w", err) + } + + quota, err := types.ParseFIL(cctx.Args().Get(1)) + if err != nil { + return xerrors.Errorf("parsing quota: %w", err) + } + + expiration, err := strconv.ParseInt(cctx.Args().Get(2), 10, 64) + if err != nil { + return xerrors.Errorf("parsing expiration: %w", err) + } + + maddr, err := getActor(cctx) + if err != nil { + return xerrors.Errorf("getting miner address: %w", err) + } + + mi, err := api.StateMinerInfo(ctx, maddr, types.EmptyTSK) + if err != nil { + return xerrors.Errorf("getting miner info: %w", err) + } + + if mi.Beneficiary == mi.Owner && newAddr == mi.Owner { + return fmt.Errorf("beneficiary %s already set to owner address", mi.Beneficiary) + } + + if mi.PendingBeneficiaryTerm != nil { + fmt.Println("WARNING: replacing Pending Beneficiary Term of:") + fmt.Println("Beneficiary: ", mi.PendingBeneficiaryTerm.NewBeneficiary) + fmt.Println("Quota:", mi.PendingBeneficiaryTerm.NewQuota) + fmt.Println("Expiration Epoch:", mi.PendingBeneficiaryTerm.NewExpiration) + + if !cctx.Bool("overwrite-pending-change") { + return fmt.Errorf("must pass --overwrite-pending-change to replace current pending beneficiary change. Please review CAREFULLY") + } + } + + if !cctx.Bool("really-do-it") { + fmt.Println("Pass --really-do-it to actually execute this action. Review what you're about to approve CAREFULLY please") + return nil + } + + params := &miner.ChangeBeneficiaryParams{ + NewBeneficiary: newAddr, + NewQuota: abi.TokenAmount(quota), + NewExpiration: abi.ChainEpoch(expiration), + } + + sp, err := actors.SerializeParams(params) + if err != nil { + return xerrors.Errorf("serializing params: %w", err) + } + + smsg, err := api.MpoolPushMessage(ctx, &types.Message{ + From: mi.Owner, + To: maddr, + Method: builtin.MethodsMiner.ChangeBeneficiary, + Value: big.Zero(), + Params: sp, + }, nil) + if err != nil { + return xerrors.Errorf("mpool push: %w", err) + } + + fmt.Println("Propose Message CID:", smsg.Cid()) + + // wait for it to get mined into a block + wait, err := api.StateWaitMsg(ctx, smsg.Cid(), build.MessageConfidence) + if err != nil { + return xerrors.Errorf("waiting for message to be included in block: %w", err) + } + + // check it executed successfully + if wait.Receipt.ExitCode.IsError() { + return fmt.Errorf("propose beneficiary change failed") + } + + updatedMinerInfo, err := api.StateMinerInfo(ctx, maddr, wait.TipSet) + if err != nil { + return xerrors.Errorf("getting miner info: %w", err) + } + + if updatedMinerInfo.PendingBeneficiaryTerm == nil && updatedMinerInfo.Beneficiary == newAddr { + fmt.Println("Beneficiary address successfully changed") + } else { + fmt.Println("Beneficiary address change awaiting additional confirmations") + } + + return nil + }, + } +} + +func ActorConfirmChangeWorkerCmd(getActor ActorAddressGetter) *cli.Command { + return &cli.Command{ + Name: "confirm-change-worker", + Usage: "Confirm a worker address change", + ArgsUsage: "[address]", + Flags: []cli.Flag{ + &cli.BoolFlag{ + Name: "really-do-it", + Usage: "Actually send transaction performing the action", + Value: false, + }, + }, + Action: func(cctx *cli.Context) error { + if !cctx.Args().Present() { + return fmt.Errorf("must pass address of new worker address") + } + + api, acloser, err := lcli.GetFullNodeAPI(cctx) + if err != nil { + return err + } + defer acloser() + + ctx := lcli.ReqContext(cctx) + + na, err := address.NewFromString(cctx.Args().First()) + if err != nil { + return err + } + + newAddr, err := api.StateLookupID(ctx, na, types.EmptyTSK) + if err != nil { + return err + } + + maddr, err := getActor(cctx) + if err != nil { + return err + } + + mi, err := api.StateMinerInfo(ctx, maddr, types.EmptyTSK) + if err != nil { + return err + } + + if mi.NewWorker.Empty() { + return xerrors.Errorf("no worker key change proposed") + } else if mi.NewWorker != newAddr { + return xerrors.Errorf("worker key %s does not match current worker key proposal %s", newAddr, mi.NewWorker) + } + + if head, err := api.ChainHead(ctx); err != nil { + return xerrors.Errorf("failed to get the chain head: %w", err) + } else if head.Height() < mi.WorkerChangeEpoch { + return xerrors.Errorf("worker key change cannot be confirmed until %d, current height is %d", mi.WorkerChangeEpoch, head.Height()) + } + + if !cctx.Bool("really-do-it") { + fmt.Println("Pass --really-do-it to actually execute this action") + return nil + } + + smsg, err := api.MpoolPushMessage(ctx, &types.Message{ + From: mi.Owner, + To: maddr, + Method: builtin.MethodsMiner.ConfirmChangeWorkerAddress, + Value: big.Zero(), + }, nil) + if err != nil { + return xerrors.Errorf("mpool push: %w", err) + } + + fmt.Println("Confirm Message CID:", smsg.Cid()) + + // wait for it to get mined into a block + wait, err := api.StateWaitMsg(ctx, smsg.Cid(), build.MessageConfidence) + if err != nil { + return err + } + + // check it executed successfully + if wait.Receipt.ExitCode.IsError() { + fmt.Fprintln(cctx.App.Writer, "Worker change failed!") + return err + } + + mi, err = api.StateMinerInfo(ctx, maddr, wait.TipSet) + if err != nil { + return err + } + if mi.Worker != newAddr { + return fmt.Errorf("Confirmed worker address change not reflected on chain: expected '%s', found '%s'", newAddr, mi.Worker) + } + + return nil + }, + } +} + +func ActorConfirmChangeBeneficiaryCmd(getActor ActorAddressGetter) *cli.Command { + return &cli.Command{ + Name: "confirm-change-beneficiary", + Usage: "Confirm a beneficiary address change", + ArgsUsage: "[minerID]", + Flags: []cli.Flag{ + &cli.BoolFlag{ + Name: "really-do-it", + Usage: "Actually send transaction performing the action", + Value: false, + }, + &cli.BoolFlag{ + Name: "existing-beneficiary", + Usage: "send confirmation from the existing beneficiary address", + }, + &cli.BoolFlag{ + Name: "new-beneficiary", + Usage: "send confirmation from the new beneficiary address", + }, + }, + Action: func(cctx *cli.Context) error { + if cctx.NArg() != 1 { + return lcli.IncorrectNumArgs(cctx) + } + + api, acloser, err := lcli.GetFullNodeAPI(cctx) + if err != nil { + return xerrors.Errorf("getting fullnode api: %w", err) + } + defer acloser() + + ctx := lcli.ReqContext(cctx) + + maddr, err := address.NewFromString(cctx.Args().First()) + if err != nil { + return xerrors.Errorf("parsing beneficiary address: %w", err) + } + + mi, err := api.StateMinerInfo(ctx, maddr, types.EmptyTSK) + if err != nil { + return xerrors.Errorf("getting miner info: %w", err) + } + + if mi.PendingBeneficiaryTerm == nil { + return fmt.Errorf("no pending beneficiary term found for miner %s", maddr) + } + + if (cctx.IsSet("existing-beneficiary") && cctx.IsSet("new-beneficiary")) || (!cctx.IsSet("existing-beneficiary") && !cctx.IsSet("new-beneficiary")) { + return lcli.ShowHelp(cctx, fmt.Errorf("must pass exactly one of --existing-beneficiary or --new-beneficiary")) + } + + var fromAddr address.Address + if cctx.IsSet("existing-beneficiary") { + if mi.PendingBeneficiaryTerm.ApprovedByBeneficiary { + return fmt.Errorf("beneficiary change already approved by current beneficiary") + } + fromAddr = mi.Beneficiary + } else { + if mi.PendingBeneficiaryTerm.ApprovedByNominee { + return fmt.Errorf("beneficiary change already approved by new beneficiary") + } + fromAddr = mi.PendingBeneficiaryTerm.NewBeneficiary + } + + fmt.Println("Confirming Pending Beneficiary Term of:") + fmt.Println("Beneficiary: ", mi.PendingBeneficiaryTerm.NewBeneficiary) + fmt.Println("Quota:", mi.PendingBeneficiaryTerm.NewQuota) + fmt.Println("Expiration Epoch:", mi.PendingBeneficiaryTerm.NewExpiration) + + if !cctx.Bool("really-do-it") { + fmt.Println("Pass --really-do-it to actually execute this action. Review what you're about to approve CAREFULLY please") + return nil + } + + params := &miner.ChangeBeneficiaryParams{ + NewBeneficiary: mi.PendingBeneficiaryTerm.NewBeneficiary, + NewQuota: mi.PendingBeneficiaryTerm.NewQuota, + NewExpiration: mi.PendingBeneficiaryTerm.NewExpiration, + } + + sp, err := actors.SerializeParams(params) + if err != nil { + return xerrors.Errorf("serializing params: %w", err) + } + + smsg, err := api.MpoolPushMessage(ctx, &types.Message{ + From: fromAddr, + To: maddr, + Method: builtin.MethodsMiner.ChangeBeneficiary, + Value: big.Zero(), + Params: sp, + }, nil) + if err != nil { + return xerrors.Errorf("mpool push: %w", err) + } + + fmt.Println("Confirm Message CID:", smsg.Cid()) + + // wait for it to get mined into a block + wait, err := api.StateWaitMsg(ctx, smsg.Cid(), build.MessageConfidence) + if err != nil { + return xerrors.Errorf("waiting for message to be included in block: %w", err) + } + + // check it executed successfully + if wait.Receipt.ExitCode.IsError() { + return fmt.Errorf("confirm beneficiary change failed with code %d", wait.Receipt.ExitCode) + } + + updatedMinerInfo, err := api.StateMinerInfo(ctx, maddr, types.EmptyTSK) + if err != nil { + return err + } + + if updatedMinerInfo.PendingBeneficiaryTerm == nil && updatedMinerInfo.Beneficiary == mi.PendingBeneficiaryTerm.NewBeneficiary { + fmt.Println("Beneficiary address successfully changed") + } else { + fmt.Println("Beneficiary address change awaiting additional confirmations") + } + + return nil + }, + } +} + +func ActorCompactAllocatedCmd(getActor ActorAddressGetter) *cli.Command { + return &cli.Command{ + Name: "compact-allocated", + Usage: "compact allocated sectors bitfield", + Flags: []cli.Flag{ + &cli.Uint64Flag{ + Name: "mask-last-offset", + Usage: "Mask sector IDs from 0 to 'highest_allocated - offset'", + }, + &cli.Uint64Flag{ + Name: "mask-upto-n", + Usage: "Mask sector IDs from 0 to 'n'", + }, + &cli.BoolFlag{ + Name: "really-do-it", + Usage: "Actually send transaction performing the action", + Value: false, + }, + }, + Action: func(cctx *cli.Context) error { + if !cctx.Bool("really-do-it") { + fmt.Println("Pass --really-do-it to actually execute this action") + return nil + } + + if !cctx.Args().Present() { + return xerrors.Errorf("must pass address of new owner address") + } + + api, acloser, err := lcli.GetFullNodeAPI(cctx) + if err != nil { + return err + } + defer acloser() + + ctx := lcli.ReqContext(cctx) + + maddr, err := getActor(cctx) + if err != nil { + return err + } + + mact, err := api.StateGetActor(ctx, maddr, types.EmptyTSK) + if err != nil { + return err + } + + store := adt.WrapStore(ctx, cbor.NewCborStore(blockstore.NewAPIBlockstore(api))) + + mst, err := lminer.Load(store, mact) + if err != nil { + return err + } + + allocs, err := mst.GetAllocatedSectors() + if err != nil { + return err + } + + var maskBf bitfield.BitField + + { + exclusiveFlags := []string{"mask-last-offset", "mask-upto-n"} + hasFlag := false + for _, f := range exclusiveFlags { + if hasFlag && cctx.IsSet(f) { + return xerrors.Errorf("more than one 'mask` flag set") + } + hasFlag = hasFlag || cctx.IsSet(f) + } + } + switch { + case cctx.IsSet("mask-last-offset"): + last, err := allocs.Last() + if err != nil { + return err + } + + m := cctx.Uint64("mask-last-offset") + if last <= m+1 { + return xerrors.Errorf("highest allocated sector lower than mask offset %d: %d", m+1, last) + } + // securty to not brick a miner + if last > 1<<60 { + return xerrors.Errorf("very high last sector number, refusing to mask: %d", last) + } + + maskBf, err = bitfield.NewFromIter(&rlepluslazy.RunSliceIterator{ + Runs: []rlepluslazy.Run{{Val: true, Len: last - m}}}) + if err != nil { + return xerrors.Errorf("forming bitfield: %w", err) + } + case cctx.IsSet("mask-upto-n"): + n := cctx.Uint64("mask-upto-n") + maskBf, err = bitfield.NewFromIter(&rlepluslazy.RunSliceIterator{ + Runs: []rlepluslazy.Run{{Val: true, Len: n}}}) + if err != nil { + return xerrors.Errorf("forming bitfield: %w", err) + } + default: + return xerrors.Errorf("no 'mask' flags set") + } + + mi, err := api.StateMinerInfo(ctx, maddr, types.EmptyTSK) + if err != nil { + return err + } + + params := &miner.CompactSectorNumbersParams{ + MaskSectorNumbers: maskBf, + } + + sp, err := actors.SerializeParams(params) + if err != nil { + return xerrors.Errorf("serializing params: %w", err) + } + + smsg, err := api.MpoolPushMessage(ctx, &types.Message{ + From: mi.Worker, + To: maddr, + Method: builtin.MethodsMiner.CompactSectorNumbers, + Value: big.Zero(), + Params: sp, + }, nil) + if err != nil { + return xerrors.Errorf("mpool push: %w", err) + } + + fmt.Println("CompactSectorNumbers Message CID:", smsg.Cid()) + + // wait for it to get mined into a block + wait, err := api.StateWaitMsg(ctx, smsg.Cid(), build.MessageConfidence) + if err != nil { + return err + } + + // check it executed successfully + if wait.Receipt.ExitCode.IsError() { + fmt.Println("Sector Bitfield compaction failed") + return err + } + + return nil + }, + } +} + +func isController(mi api.MinerInfo, addr address.Address) bool { + if addr == mi.Owner || addr == mi.Worker { + return true + } + + for _, ca := range mi.ControlAddresses { + if addr == ca { + return true + } + } + + return false +} diff --git a/cli/spcli/info.go b/cli/spcli/info.go new file mode 100644 index 000000000..69436b2c7 --- /dev/null +++ b/cli/spcli/info.go @@ -0,0 +1,121 @@ +package spcli + +import ( + "fmt" + + "github.com/fatih/color" + "github.com/multiformats/go-multiaddr" + "github.com/urfave/cli/v2" + "golang.org/x/xerrors" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/big" + + "github.com/filecoin-project/lotus/chain/types" + lcli "github.com/filecoin-project/lotus/cli" + cliutil "github.com/filecoin-project/lotus/cli/util" +) + +func InfoCmd(getActorAddress ActorAddressGetter) *cli.Command { + return &cli.Command{ + Name: "info", + Usage: "Print miner actor info", + Action: func(cctx *cli.Context) error { + api, closer, err := cliutil.GetFullNodeAPI(cctx) + if err != nil { + return err + } + defer closer() + + ctx := cliutil.ReqContext(cctx) + + ts, err := lcli.LoadTipSet(ctx, cctx, api) + if err != nil { + return err + } + + addr, err := getActorAddress(cctx) + if err != nil { + return err + } + mi, err := api.StateMinerInfo(ctx, addr, ts.Key()) + if err != nil { + return err + } + + availableBalance, err := api.StateMinerAvailableBalance(ctx, addr, ts.Key()) + if err != nil { + return xerrors.Errorf("getting miner available balance: %w", err) + } + fmt.Printf("Available Balance: %s\n", types.FIL(availableBalance)) + fmt.Printf("Owner:\t%s\n", mi.Owner) + fmt.Printf("Worker:\t%s\n", mi.Worker) + for i, controlAddress := range mi.ControlAddresses { + fmt.Printf("Control %d: \t%s\n", i, controlAddress) + } + if mi.Beneficiary != address.Undef { + fmt.Printf("Beneficiary:\t%s\n", mi.Beneficiary) + if mi.Beneficiary != mi.Owner { + fmt.Printf("Beneficiary Quota:\t%s\n", mi.BeneficiaryTerm.Quota) + fmt.Printf("Beneficiary Used Quota:\t%s\n", mi.BeneficiaryTerm.UsedQuota) + fmt.Printf("Beneficiary Expiration:\t%s\n", mi.BeneficiaryTerm.Expiration) + } + } + if mi.PendingBeneficiaryTerm != nil { + fmt.Printf("Pending Beneficiary Term:\n") + fmt.Printf("New Beneficiary:\t%s\n", mi.PendingBeneficiaryTerm.NewBeneficiary) + fmt.Printf("New Quota:\t%s\n", mi.PendingBeneficiaryTerm.NewQuota) + fmt.Printf("New Expiration:\t%s\n", mi.PendingBeneficiaryTerm.NewExpiration) + fmt.Printf("Approved By Beneficiary:\t%t\n", mi.PendingBeneficiaryTerm.ApprovedByBeneficiary) + fmt.Printf("Approved By Nominee:\t%t\n", mi.PendingBeneficiaryTerm.ApprovedByNominee) + } + + fmt.Printf("PeerID:\t%s\n", mi.PeerId) + fmt.Printf("Multiaddrs:\t") + for _, addr := range mi.Multiaddrs { + a, err := multiaddr.NewMultiaddrBytes(addr) + if err != nil { + return xerrors.Errorf("undecodable listen address: %w", err) + } + fmt.Printf("%s ", a) + } + fmt.Println() + fmt.Printf("Consensus Fault End:\t%d\n", mi.ConsensusFaultElapsed) + + fmt.Printf("SectorSize:\t%s (%d)\n", types.SizeStr(types.NewInt(uint64(mi.SectorSize))), mi.SectorSize) + pow, err := api.StateMinerPower(ctx, addr, ts.Key()) + if err != nil { + return err + } + + fmt.Printf("Byte Power: %s / %s (%0.4f%%)\n", + color.BlueString(types.SizeStr(pow.MinerPower.RawBytePower)), + types.SizeStr(pow.TotalPower.RawBytePower), + types.BigDivFloat( + types.BigMul(pow.MinerPower.RawBytePower, big.NewInt(100)), + pow.TotalPower.RawBytePower, + ), + ) + + fmt.Printf("Actual Power: %s / %s (%0.4f%%)\n", + color.GreenString(types.DeciStr(pow.MinerPower.QualityAdjPower)), + types.DeciStr(pow.TotalPower.QualityAdjPower), + types.BigDivFloat( + types.BigMul(pow.MinerPower.QualityAdjPower, big.NewInt(100)), + pow.TotalPower.QualityAdjPower, + ), + ) + + fmt.Println() + + cd, err := api.StateMinerProvingDeadline(ctx, addr, ts.Key()) + if err != nil { + return xerrors.Errorf("getting miner info: %w", err) + } + + fmt.Printf("Proving Period Start:\t%s\n", cliutil.EpochTime(cd.CurrentEpoch, cd.PeriodStart)) + + return nil + }, + } +} diff --git a/cli/spcli/proving.go b/cli/spcli/proving.go new file mode 100644 index 000000000..ed4251f1b --- /dev/null +++ b/cli/spcli/proving.go @@ -0,0 +1,451 @@ +package spcli + +import ( + "bytes" + "fmt" + "os" + "strconv" + "strings" + "text/tabwriter" + "time" + + "github.com/fatih/color" + "github.com/urfave/cli/v2" + "golang.org/x/xerrors" + + "github.com/filecoin-project/go-bitfield" + "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/go-state-types/dline" + + "github.com/filecoin-project/lotus/blockstore" + "github.com/filecoin-project/lotus/build" + "github.com/filecoin-project/lotus/chain/actors/builtin/miner" + "github.com/filecoin-project/lotus/chain/store" + "github.com/filecoin-project/lotus/chain/types" + lcli "github.com/filecoin-project/lotus/cli" + cliutil "github.com/filecoin-project/lotus/cli/util" +) + +func ProvingInfoCmd(getActorAddress ActorAddressGetter) *cli.Command { + return &cli.Command{ + Name: "info", + Usage: "View current state information", + Action: func(cctx *cli.Context) error { + api, acloser, err := lcli.GetFullNodeAPI(cctx) + if err != nil { + return err + } + defer acloser() + + ctx := lcli.ReqContext(cctx) + + maddr, err := getActorAddress(cctx) + if err != nil { + return err + } + + head, err := api.ChainHead(ctx) + if err != nil { + return xerrors.Errorf("getting chain head: %w", err) + } + + mact, err := api.StateGetActor(ctx, maddr, head.Key()) + if err != nil { + return err + } + + stor := store.ActorStore(ctx, blockstore.NewAPIBlockstore(api)) + + mas, err := miner.Load(stor, mact) + if err != nil { + return err + } + + cd, err := api.StateMinerProvingDeadline(ctx, maddr, head.Key()) + if err != nil { + return xerrors.Errorf("getting miner info: %w", err) + } + + fmt.Printf("Miner: %s\n", color.BlueString("%s", maddr)) + + proving := uint64(0) + faults := uint64(0) + recovering := uint64(0) + curDeadlineSectors := uint64(0) + + if err := mas.ForEachDeadline(func(dlIdx uint64, dl miner.Deadline) error { + return dl.ForEachPartition(func(partIdx uint64, part miner.Partition) error { + if bf, err := part.LiveSectors(); err != nil { + return err + } else if count, err := bf.Count(); err != nil { + return err + } else { + proving += count + if dlIdx == cd.Index { + curDeadlineSectors += count + } + } + + if bf, err := part.FaultySectors(); err != nil { + return err + } else if count, err := bf.Count(); err != nil { + return err + } else { + faults += count + } + + if bf, err := part.RecoveringSectors(); err != nil { + return err + } else if count, err := bf.Count(); err != nil { + return err + } else { + recovering += count + } + + return nil + }) + }); err != nil { + return xerrors.Errorf("walking miner deadlines and partitions: %w", err) + } + + var faultPerc float64 + if proving > 0 { + faultPerc = float64(faults * 100 / proving) + } + + fmt.Printf("Current Epoch: %d\n", cd.CurrentEpoch) + + fmt.Printf("Proving Period Boundary: %d\n", cd.PeriodStart%cd.WPoStProvingPeriod) + fmt.Printf("Proving Period Start: %s\n", cliutil.EpochTimeTs(cd.CurrentEpoch, cd.PeriodStart, head)) + fmt.Printf("Next Period Start: %s\n\n", cliutil.EpochTimeTs(cd.CurrentEpoch, cd.PeriodStart+cd.WPoStProvingPeriod, head)) + + fmt.Printf("Faults: %d (%.2f%%)\n", faults, faultPerc) + fmt.Printf("Recovering: %d\n", recovering) + + fmt.Printf("Deadline Index: %d\n", cd.Index) + fmt.Printf("Deadline Sectors: %d\n", curDeadlineSectors) + fmt.Printf("Deadline Open: %s\n", cliutil.EpochTime(cd.CurrentEpoch, cd.Open)) + fmt.Printf("Deadline Close: %s\n", cliutil.EpochTime(cd.CurrentEpoch, cd.Close)) + fmt.Printf("Deadline Challenge: %s\n", cliutil.EpochTime(cd.CurrentEpoch, cd.Challenge)) + fmt.Printf("Deadline FaultCutoff: %s\n", cliutil.EpochTime(cd.CurrentEpoch, cd.FaultCutoff)) + return nil + }, + } +} + +func ProvingDeadlinesCmd(getActorAddress ActorAddressGetter) *cli.Command { + return &cli.Command{ + Name: "deadlines", + Usage: "View the current proving period deadlines information", + Flags: []cli.Flag{ + &cli.BoolFlag{ + Name: "all", + Usage: "Count all sectors (only live sectors are counted by default)", + Aliases: []string{"a"}, + }, + }, + Action: func(cctx *cli.Context) error { + api, acloser, err := lcli.GetFullNodeAPI(cctx) + if err != nil { + return err + } + defer acloser() + + ctx := lcli.ReqContext(cctx) + + maddr, err := getActorAddress(cctx) + if err != nil { + return err + } + + deadlines, err := api.StateMinerDeadlines(ctx, maddr, types.EmptyTSK) + if err != nil { + return xerrors.Errorf("getting deadlines: %w", err) + } + + di, err := api.StateMinerProvingDeadline(ctx, maddr, types.EmptyTSK) + if err != nil { + return xerrors.Errorf("getting deadlines: %w", err) + } + + head, err := api.ChainHead(ctx) + if err != nil { + return err + } + + fmt.Printf("Miner: %s\n", color.BlueString("%s", maddr)) + + tw := tabwriter.NewWriter(os.Stdout, 2, 4, 2, ' ', 0) + _, _ = fmt.Fprintln(tw, "deadline\topen\tpartitions\tsectors (faults)\tproven partitions") + + for dlIdx, deadline := range deadlines { + partitions, err := api.StateMinerPartitions(ctx, maddr, uint64(dlIdx), types.EmptyTSK) + if err != nil { + return xerrors.Errorf("getting partitions for deadline %d: %w", dlIdx, err) + } + + provenPartitions, err := deadline.PostSubmissions.Count() + if err != nil { + return err + } + + sectors := uint64(0) + faults := uint64(0) + var partitionCount int + + for _, partition := range partitions { + if !cctx.Bool("all") { + sc, err := partition.LiveSectors.Count() + if err != nil { + return err + } + + if sc > 0 { + partitionCount++ + } + + sectors += sc + } else { + sc, err := partition.AllSectors.Count() + if err != nil { + return err + } + + partitionCount++ + sectors += sc + } + + fc, err := partition.FaultySectors.Count() + if err != nil { + return err + } + + faults += fc + } + + var cur string + if di.Index == uint64(dlIdx) { + cur += "\t(current)" + } + + _, _ = fmt.Fprintf(tw, "%d\t%s\t%d\t%d (%d)\t%d%s\n", dlIdx, deadlineOpenTime(head, uint64(dlIdx), di), + partitionCount, sectors, faults, provenPartitions, cur) + } + + return tw.Flush() + }, + } +} + +func deadlineOpenTime(ts *types.TipSet, dlIdx uint64, di *dline.Info) string { + gapIdx := dlIdx - di.Index + gapHeight := uint64(di.WPoStProvingPeriod) / di.WPoStPeriodDeadlines * gapIdx + + openHeight := di.Open + abi.ChainEpoch(gapHeight) + genesisBlockTimestamp := ts.MinTimestamp() - uint64(ts.Height())*build.BlockDelaySecs + + return time.Unix(int64(genesisBlockTimestamp+build.BlockDelaySecs*uint64(openHeight)), 0).Format(time.TimeOnly) +} + +func ProvingDeadlineInfoCmd(getActorAddress ActorAddressGetter) *cli.Command { + return &cli.Command{ + Name: "deadline", + Usage: "View the current proving period deadline information by its index", + Flags: []cli.Flag{ + &cli.BoolFlag{ + Name: "sector-nums", + Aliases: []string{"n"}, + Usage: "Print sector/fault numbers belonging to this deadline", + }, + &cli.BoolFlag{ + Name: "bitfield", + Aliases: []string{"b"}, + Usage: "Print partition bitfield stats", + }, + }, + ArgsUsage: "", + Action: func(cctx *cli.Context) error { + + if cctx.NArg() != 1 { + return lcli.IncorrectNumArgs(cctx) + } + + dlIdx, err := strconv.ParseUint(cctx.Args().Get(0), 10, 64) + if err != nil { + return xerrors.Errorf("could not parse deadline index: %w", err) + } + + api, acloser, err := lcli.GetFullNodeAPI(cctx) + if err != nil { + return err + } + defer acloser() + + ctx := lcli.ReqContext(cctx) + + maddr, err := getActorAddress(cctx) + if err != nil { + return err + } + + deadlines, err := api.StateMinerDeadlines(ctx, maddr, types.EmptyTSK) + if err != nil { + return xerrors.Errorf("getting deadlines: %w", err) + } + + di, err := api.StateMinerProvingDeadline(ctx, maddr, types.EmptyTSK) + if err != nil { + return xerrors.Errorf("getting deadlines: %w", err) + } + + partitions, err := api.StateMinerPartitions(ctx, maddr, dlIdx, types.EmptyTSK) + if err != nil { + return xerrors.Errorf("getting partitions for deadline %d: %w", dlIdx, err) + } + + head, err := api.ChainHead(ctx) + if err != nil { + return err + } + + provenPartitions, err := deadlines[dlIdx].PostSubmissions.Count() + if err != nil { + return err + } + + fmt.Printf("Deadline Index: %d\n", dlIdx) + fmt.Printf("Deadline Open: %s\n", deadlineOpenTime(head, dlIdx, di)) + fmt.Printf("Partitions: %d\n", len(partitions)) + fmt.Printf("Proven Partitions: %d\n", provenPartitions) + fmt.Printf("Current: %t\n\n", di.Index == dlIdx) + + for pIdx, partition := range partitions { + fmt.Printf("Partition Index: %d\n", pIdx) + + printStats := func(bf bitfield.BitField, name string) error { + count, err := bf.Count() + if err != nil { + return err + } + + rit, err := bf.RunIterator() + if err != nil { + return err + } + + if cctx.Bool("bitfield") { + var ones, zeros, oneRuns, zeroRuns, invalid uint64 + for rit.HasNext() { + r, err := rit.NextRun() + if err != nil { + return xerrors.Errorf("next run: %w", err) + } + if !r.Valid() { + invalid++ + } + if r.Val { + ones += r.Len + oneRuns++ + } else { + zeros += r.Len + zeroRuns++ + } + } + + var buf bytes.Buffer + if err := bf.MarshalCBOR(&buf); err != nil { + return err + } + sz := len(buf.Bytes()) + szstr := types.SizeStr(types.NewInt(uint64(sz))) + + fmt.Printf("\t%s Sectors:%s%d (bitfield - runs %d+%d=%d - %d 0s %d 1s - %d inv - %s %dB)\n", name, strings.Repeat(" ", 18-len(name)), count, zeroRuns, oneRuns, zeroRuns+oneRuns, zeros, ones, invalid, szstr, sz) + } else { + fmt.Printf("\t%s Sectors:%s%d\n", name, strings.Repeat(" ", 18-len(name)), count) + } + + if cctx.Bool("sector-nums") { + nums, err := bf.All(count) + if err != nil { + return err + } + fmt.Printf("\t%s Sector Numbers:%s%v\n", name, strings.Repeat(" ", 12-len(name)), nums) + } + + return nil + } + + if err := printStats(partition.AllSectors, "All"); err != nil { + return err + } + if err := printStats(partition.LiveSectors, "Live"); err != nil { + return err + } + if err := printStats(partition.ActiveSectors, "Active"); err != nil { + return err + } + if err := printStats(partition.FaultySectors, "Faulty"); err != nil { + return err + } + if err := printStats(partition.RecoveringSectors, "Recovering"); err != nil { + return err + } + } + return nil + }, + } +} + +func ProvingFaultsCmd(getActorAddress ActorAddressGetter) *cli.Command { + return &cli.Command{ + Name: "faults", + Usage: "View the currently known proving faulty sectors information", + Action: func(cctx *cli.Context) error { + api, acloser, err := lcli.GetFullNodeAPI(cctx) + if err != nil { + return err + } + defer acloser() + + ctx := lcli.ReqContext(cctx) + + stor := store.ActorStore(ctx, blockstore.NewAPIBlockstore(api)) + + maddr, err := getActorAddress(cctx) + if err != nil { + return err + } + + mact, err := api.StateGetActor(ctx, maddr, types.EmptyTSK) + if err != nil { + return err + } + + mas, err := miner.Load(stor, mact) + if err != nil { + return err + } + + fmt.Printf("Miner: %s\n", color.BlueString("%s", maddr)) + + tw := tabwriter.NewWriter(os.Stdout, 2, 4, 2, ' ', 0) + _, _ = fmt.Fprintln(tw, "deadline\tpartition\tsectors") + err = mas.ForEachDeadline(func(dlIdx uint64, dl miner.Deadline) error { + return dl.ForEachPartition(func(partIdx uint64, part miner.Partition) error { + faults, err := part.FaultySectors() + if err != nil { + return err + } + return faults.ForEach(func(num uint64) error { + _, _ = fmt.Fprintf(tw, "%d\t%d\t%d\n", dlIdx, partIdx, num) + return nil + }) + }) + }) + if err != nil { + return err + } + return tw.Flush() + }, + } +} diff --git a/cli/spcli/sectors.go b/cli/spcli/sectors.go new file mode 100644 index 000000000..5528c6438 --- /dev/null +++ b/cli/spcli/sectors.go @@ -0,0 +1,1398 @@ +package spcli + +import ( + "bufio" + "encoding/csv" + "encoding/json" + "errors" + "fmt" + "math" + "os" + "sort" + "strconv" + "strings" + "time" + + "github.com/fatih/color" + cbor "github.com/ipfs/go-ipld-cbor" + "github.com/urfave/cli/v2" + "golang.org/x/xerrors" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-bitfield" + "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/go-state-types/big" + "github.com/filecoin-project/go-state-types/builtin" + miner2 "github.com/filecoin-project/specs-actors/v2/actors/builtin/miner" + + "github.com/filecoin-project/lotus/api" + "github.com/filecoin-project/lotus/blockstore" + "github.com/filecoin-project/lotus/chain/actors" + "github.com/filecoin-project/lotus/chain/actors/adt" + "github.com/filecoin-project/lotus/chain/actors/builtin/miner" + "github.com/filecoin-project/lotus/chain/actors/builtin/verifreg" + "github.com/filecoin-project/lotus/chain/actors/policy" + "github.com/filecoin-project/lotus/chain/types" + lcli "github.com/filecoin-project/lotus/cli" + cliutil "github.com/filecoin-project/lotus/cli/util" + "github.com/filecoin-project/lotus/lib/tablewriter" +) + +type OnDiskInfoGetter func(cctx *cli.Context, id abi.SectorNumber, onChainInfo bool) (api.SectorInfo, error) + +func SectorsStatusCmd(getActorAddress ActorAddressGetter, getOnDiskInfo OnDiskInfoGetter) *cli.Command { + return &cli.Command{ + Name: "status", + Usage: "Get the seal status of a sector by its number", + ArgsUsage: "", + Flags: []cli.Flag{ + &cli.BoolFlag{ + Name: "log", + Usage: "display event log", + Aliases: []string{"l"}, + }, + &cli.BoolFlag{ + Name: "on-chain-info", + Usage: "show sector on chain info", + Aliases: []string{"c"}, + }, + &cli.BoolFlag{ + Name: "partition-info", + Usage: "show partition related info", + Aliases: []string{"p"}, + }, + &cli.BoolFlag{ + Name: "proof", + Usage: "print snark proof bytes as hex", + }, + }, + Action: func(cctx *cli.Context) error { + ctx := lcli.ReqContext(cctx) + + if cctx.NArg() != 1 { + return lcli.IncorrectNumArgs(cctx) + } + + id, err := strconv.ParseUint(cctx.Args().First(), 10, 64) + if err != nil { + return err + } + + onChainInfo := cctx.Bool("on-chain-info") + + var status api.SectorInfo + if getOnDiskInfo != nil { + status, err = getOnDiskInfo(cctx, abi.SectorNumber(id), onChainInfo) + if err != nil { + return err + } + fmt.Printf("SectorID:\t%d\n", status.SectorID) + fmt.Printf("Status:\t\t%s\n", status.State) + fmt.Printf("CIDcommD:\t%s\n", status.CommD) + fmt.Printf("CIDcommR:\t%s\n", status.CommR) + fmt.Printf("Ticket:\t\t%x\n", status.Ticket.Value) + fmt.Printf("TicketH:\t%d\n", status.Ticket.Epoch) + fmt.Printf("Seed:\t\t%x\n", status.Seed.Value) + fmt.Printf("SeedH:\t\t%d\n", status.Seed.Epoch) + fmt.Printf("Precommit:\t%s\n", status.PreCommitMsg) + fmt.Printf("Commit:\t\t%s\n", status.CommitMsg) + if cctx.Bool("proof") { + fmt.Printf("Proof:\t\t%x\n", status.Proof) + } + fmt.Printf("Deals:\t\t%v\n", status.Deals) + fmt.Printf("Retries:\t%d\n", status.Retries) + if status.LastErr != "" { + fmt.Printf("Last Error:\t\t%s\n", status.LastErr) + } + + fmt.Printf("\nExpiration Info\n") + fmt.Printf("OnTime:\t\t%v\n", status.OnTime) + fmt.Printf("Early:\t\t%v\n", status.Early) + + } else { + onChainInfo = true + } + + maddr, err := getActorAddress(cctx) + if err != nil { + return err + } + + if onChainInfo { + fullApi, closer, err := lcli.GetFullNodeAPI(cctx) + if err != nil { + return err + } + defer closer() + + head, err := fullApi.ChainHead(ctx) + if err != nil { + return xerrors.Errorf("getting chain head: %w", err) + } + + status, err := fullApi.StateSectorGetInfo(ctx, maddr, abi.SectorNumber(id), head.Key()) + if err != nil { + return err + } + + mid, err := address.IDFromAddress(maddr) + if err != nil { + return err + } + fmt.Printf("\nSector On Chain Info\n") + fmt.Printf("SealProof:\t\t%x\n", status.SealProof) + fmt.Printf("Activation:\t\t%v\n", status.Activation) + fmt.Printf("Expiration:\t\t%v\n", status.Expiration) + fmt.Printf("DealWeight:\t\t%v\n", status.DealWeight) + fmt.Printf("VerifiedDealWeight:\t\t%v\n", status.VerifiedDealWeight) + fmt.Printf("InitialPledge:\t\t%v\n", types.FIL(status.InitialPledge)) + fmt.Printf("SectorID:\t\t{Miner: %v, Number: %v}\n", abi.ActorID(mid), status.SectorNumber) + } + + if cctx.Bool("partition-info") { + fullApi, nCloser, err := lcli.GetFullNodeAPI(cctx) + if err != nil { + return err + } + defer nCloser() + + maddr, err := getActorAddress(cctx) + if err != nil { + return err + } + + mact, err := fullApi.StateGetActor(ctx, maddr, types.EmptyTSK) + if err != nil { + return err + } + + tbs := blockstore.NewTieredBstore(blockstore.NewAPIBlockstore(fullApi), blockstore.NewMemory()) + mas, err := miner.Load(adt.WrapStore(ctx, cbor.NewCborStore(tbs)), mact) + if err != nil { + return err + } + + errFound := errors.New("found") + if err := mas.ForEachDeadline(func(dlIdx uint64, dl miner.Deadline) error { + return dl.ForEachPartition(func(partIdx uint64, part miner.Partition) error { + pas, err := part.AllSectors() + if err != nil { + return err + } + + set, err := pas.IsSet(id) + if err != nil { + return err + } + if set { + fmt.Printf("\nDeadline:\t%d\n", dlIdx) + fmt.Printf("Partition:\t%d\n", partIdx) + + checkIn := func(name string, bg func() (bitfield.BitField, error)) error { + bf, err := bg() + if err != nil { + return err + } + + set, err := bf.IsSet(id) + if err != nil { + return err + } + setstr := "no" + if set { + setstr = "yes" + } + fmt.Printf("%s: \t%s\n", name, setstr) + return nil + } + + if err := checkIn("Unproven", part.UnprovenSectors); err != nil { + return err + } + if err := checkIn("Live", part.LiveSectors); err != nil { + return err + } + if err := checkIn("Active", part.ActiveSectors); err != nil { + return err + } + if err := checkIn("Faulty", part.FaultySectors); err != nil { + return err + } + if err := checkIn("Recovering", part.RecoveringSectors); err != nil { + return err + } + + return errFound + } + + return nil + }) + }); err != errFound { + if err != nil { + return err + } + + fmt.Println("\nNot found in any partition") + } + } + + if cctx.Bool("log") { + fmt.Printf("--------\nEvent Log:\n") + + for i, l := range status.Log { + fmt.Printf("%d.\t%s:\t[%s]\t%s\n", i, time.Unix(int64(l.Timestamp), 0), l.Kind, l.Message) + if l.Trace != "" { + fmt.Printf("\t%s\n", l.Trace) + } + } + } + return nil + }, + } +} + +func SectorsListUpgradeBoundsCmd(getActorAddress ActorAddressGetter) *cli.Command { + return &cli.Command{ + Name: "upgrade-bounds", + Usage: "Output upgrade bounds for available sectors", + Flags: []cli.Flag{ + &cli.IntFlag{ + Name: "buckets", + Value: 25, + }, + &cli.BoolFlag{ + Name: "csv", + Usage: "output machine-readable values", + }, + &cli.BoolFlag{ + Name: "deal-terms", + Usage: "bucket by how many deal-sectors can start at a given expiration", + }, + }, + Action: func(cctx *cli.Context) error { + fullApi, closer2, err := lcli.GetFullNodeAPI(cctx) + if err != nil { + return err + } + defer closer2() + + ctx := lcli.ReqContext(cctx) + + head, err := fullApi.ChainHead(ctx) + if err != nil { + return xerrors.Errorf("getting chain head: %w", err) + } + + maddr, err := getActorAddress(cctx) + if err != nil { + return err + } + + list, err := fullApi.StateMinerActiveSectors(ctx, maddr, head.Key()) + if err != nil { + return err + } + filter := bitfield.New() + + for _, s := range list { + filter.Set(uint64(s.SectorNumber)) + } + sset, err := fullApi.StateMinerSectors(ctx, maddr, &filter, head.Key()) + if err != nil { + return err + } + + if len(sset) == 0 { + return nil + } + + var minExpiration, maxExpiration abi.ChainEpoch + + for _, s := range sset { + if s.Expiration < minExpiration || minExpiration == 0 { + minExpiration = s.Expiration + } + if s.Expiration > maxExpiration { + maxExpiration = s.Expiration + } + } + + buckets := cctx.Int("buckets") + bucketSize := (maxExpiration - minExpiration) / abi.ChainEpoch(buckets) + bucketCounts := make([]int, buckets+1) + + for b := range bucketCounts { + bucketMin := minExpiration + abi.ChainEpoch(b)*bucketSize + bucketMax := minExpiration + abi.ChainEpoch(b+1)*bucketSize + + if cctx.Bool("deal-terms") { + bucketMax = bucketMax + policy.MarketDefaultAllocationTermBuffer + } + + for _, s := range sset { + isInBucket := s.Expiration >= bucketMin && s.Expiration < bucketMax + + if isInBucket { + bucketCounts[b]++ + } + } + + } + + // Creating CSV writer + writer := csv.NewWriter(os.Stdout) + + // Writing CSV headers + err = writer.Write([]string{"Max Expiration in Bucket", "Sector Count"}) + if err != nil { + return xerrors.Errorf("writing csv headers: %w", err) + } + + // Writing bucket details + + if cctx.Bool("csv") { + for i := 0; i < buckets; i++ { + maxExp := minExpiration + abi.ChainEpoch(i+1)*bucketSize + + timeStr := strconv.FormatInt(int64(maxExp), 10) + + err = writer.Write([]string{ + timeStr, + strconv.Itoa(bucketCounts[i]), + }) + if err != nil { + return xerrors.Errorf("writing csv row: %w", err) + } + } + + // Flush to make sure all data is written to the underlying writer + writer.Flush() + + if err := writer.Error(); err != nil { + return xerrors.Errorf("flushing csv writer: %w", err) + } + + return nil + } + + tw := tablewriter.New( + tablewriter.Col("Bucket Expiration"), + tablewriter.Col("Sector Count"), + tablewriter.Col("Bar"), + ) + + var barCols = 40 + var maxCount int + + for _, c := range bucketCounts { + if c > maxCount { + maxCount = c + } + } + + for i := 0; i < buckets; i++ { + maxExp := minExpiration + abi.ChainEpoch(i+1)*bucketSize + timeStr := cliutil.EpochTime(head.Height(), maxExp) + + tw.Write(map[string]interface{}{ + "Bucket Expiration": timeStr, + "Sector Count": color.YellowString("%d", bucketCounts[i]), + "Bar": "[" + color.GreenString(strings.Repeat("|", bucketCounts[i]*barCols/maxCount)) + strings.Repeat(" ", barCols-bucketCounts[i]*barCols/maxCount) + "]", + }) + } + + return tw.Flush(os.Stdout) + }, + } +} + +func SectorPreCommitsCmd(getActorAddress ActorAddressGetter) *cli.Command { + return &cli.Command{ + Name: "precommits", + Usage: "Print on-chain precommit info", + Action: func(cctx *cli.Context) error { + ctx := lcli.ReqContext(cctx) + mapi, closer, err := lcli.GetFullNodeAPI(cctx) + if err != nil { + return err + } + defer closer() + maddr, err := getActorAddress(cctx) + if err != nil { + return err + } + mact, err := mapi.StateGetActor(ctx, maddr, types.EmptyTSK) + if err != nil { + return err + } + store := adt.WrapStore(ctx, cbor.NewCborStore(blockstore.NewAPIBlockstore(mapi))) + mst, err := miner.Load(store, mact) + if err != nil { + return err + } + preCommitSector := make([]miner.SectorPreCommitOnChainInfo, 0) + err = mst.ForEachPrecommittedSector(func(info miner.SectorPreCommitOnChainInfo) error { + preCommitSector = append(preCommitSector, info) + return err + }) + less := func(i, j int) bool { + return preCommitSector[i].Info.SectorNumber <= preCommitSector[j].Info.SectorNumber + } + sort.Slice(preCommitSector, less) + for _, info := range preCommitSector { + fmt.Printf("%s: %s\n", info.Info.SectorNumber, info.PreCommitEpoch) + } + + return nil + }, + } +} + +func SectorsCheckExpireCmd(getActorAddress ActorAddressGetter) *cli.Command { + return &cli.Command{ + Name: "check-expire", + Usage: "Inspect expiring sectors", + Flags: []cli.Flag{ + &cli.Int64Flag{ + Name: "cutoff", + Usage: "skip sectors whose current expiration is more than epochs from now, defaults to 60 days", + Value: 172800, + }, + }, + Action: func(cctx *cli.Context) error { + + fullApi, nCloser, err := lcli.GetFullNodeAPI(cctx) + if err != nil { + return err + } + defer nCloser() + + ctx := lcli.ReqContext(cctx) + + maddr, err := getActorAddress(cctx) + if err != nil { + return err + } + + head, err := fullApi.ChainHead(ctx) + if err != nil { + return err + } + currEpoch := head.Height() + + nv, err := fullApi.StateNetworkVersion(ctx, types.EmptyTSK) + if err != nil { + return err + } + + sectors, err := fullApi.StateMinerActiveSectors(ctx, maddr, types.EmptyTSK) + if err != nil { + return err + } + + n := 0 + for _, s := range sectors { + if s.Expiration-currEpoch <= abi.ChainEpoch(cctx.Int64("cutoff")) { + sectors[n] = s + n++ + } + } + sectors = sectors[:n] + + sort.Slice(sectors, func(i, j int) bool { + if sectors[i].Expiration == sectors[j].Expiration { + return sectors[i].SectorNumber < sectors[j].SectorNumber + } + return sectors[i].Expiration < sectors[j].Expiration + }) + + tw := tablewriter.New( + tablewriter.Col("ID"), + tablewriter.Col("SealProof"), + tablewriter.Col("InitialPledge"), + tablewriter.Col("Activation"), + tablewriter.Col("Expiration"), + tablewriter.Col("MaxExpiration"), + tablewriter.Col("MaxExtendNow")) + + for _, sector := range sectors { + MaxExpiration := sector.Activation + policy.GetSectorMaxLifetime(sector.SealProof, nv) + maxExtension, err := policy.GetMaxSectorExpirationExtension(nv) + if err != nil { + return xerrors.Errorf("failed to get max extension: %w", err) + } + + MaxExtendNow := currEpoch + maxExtension + + if MaxExtendNow > MaxExpiration { + MaxExtendNow = MaxExpiration + } + + tw.Write(map[string]interface{}{ + "ID": sector.SectorNumber, + "SealProof": sector.SealProof, + "InitialPledge": types.FIL(sector.InitialPledge).Short(), + "Activation": cliutil.EpochTime(currEpoch, sector.Activation), + "Expiration": cliutil.EpochTime(currEpoch, sector.Expiration), + "MaxExpiration": cliutil.EpochTime(currEpoch, MaxExpiration), + "MaxExtendNow": cliutil.EpochTime(currEpoch, MaxExtendNow), + }) + } + + return tw.Flush(os.Stdout) + }, + } +} + +func SectorsExtendCmd(getActorAddress ActorAddressGetter) *cli.Command { + return &cli.Command{ + Name: "extend", + Usage: "Extend expiring sectors while not exceeding each sector's max life", + ArgsUsage: "", + Flags: []cli.Flag{ + &cli.Int64Flag{ + Name: "from", + Usage: "only consider sectors whose current expiration epoch is in the range of [from, to], defaults to: now + 120 (1 hour)", + }, + &cli.Int64Flag{ + Name: "to", + Usage: "only consider sectors whose current expiration epoch is in the range of [from, to], defaults to: now + 92160 (32 days)", + }, + &cli.StringFlag{ + Name: "sector-file", + Usage: "provide a file containing one sector number in each line, ignoring above selecting criteria", + }, + &cli.StringFlag{ + Name: "exclude", + Usage: "optionally provide a file containing excluding sectors", + }, + &cli.Int64Flag{ + Name: "extension", + Usage: "try to extend selected sectors by this number of epochs, defaults to 540 days", + Value: 1555200, + }, + &cli.Int64Flag{ + Name: "new-expiration", + Usage: "try to extend selected sectors to this epoch, ignoring extension", + }, + &cli.BoolFlag{ + Name: "only-cc", + Usage: "only extend CC sectors (useful for making sector ready for snap upgrade)", + }, + &cli.BoolFlag{ + Name: "drop-claims", + Usage: "drop claims for sectors that can be extended, but only by dropping some of their verified power claims", + }, + &cli.Int64Flag{ + Name: "tolerance", + Usage: "don't try to extend sectors by fewer than this number of epochs, defaults to 7 days", + Value: 20160, + }, + &cli.StringFlag{ + Name: "max-fee", + Usage: "use up to this amount of FIL for one message. pass this flag to avoid message congestion.", + Value: "0", + }, + &cli.Int64Flag{ + Name: "max-sectors", + Usage: "the maximum number of sectors contained in each message", + }, + &cli.BoolFlag{ + Name: "really-do-it", + Usage: "pass this flag to really extend sectors, otherwise will only print out json representation of parameters", + }, + }, + Action: func(cctx *cli.Context) error { + mf, err := types.ParseFIL(cctx.String("max-fee")) + if err != nil { + return err + } + + spec := &api.MessageSendSpec{MaxFee: abi.TokenAmount(mf)} + + fullApi, nCloser, err := lcli.GetFullNodeAPI(cctx) + if err != nil { + return err + } + defer nCloser() + + ctx := lcli.ReqContext(cctx) + + maddr, err := getActorAddress(cctx) + if err != nil { + return err + } + + head, err := fullApi.ChainHead(ctx) + if err != nil { + return err + } + currEpoch := head.Height() + + nv, err := fullApi.StateNetworkVersion(ctx, types.EmptyTSK) + if err != nil { + return err + } + + activeSet, err := fullApi.StateMinerActiveSectors(ctx, maddr, types.EmptyTSK) + if err != nil { + return err + } + + activeSectorsInfo := make(map[abi.SectorNumber]*miner.SectorOnChainInfo, len(activeSet)) + for _, info := range activeSet { + activeSectorsInfo[info.SectorNumber] = info + } + + mact, err := fullApi.StateGetActor(ctx, maddr, types.EmptyTSK) + if err != nil { + return err + } + + tbs := blockstore.NewTieredBstore(blockstore.NewAPIBlockstore(fullApi), blockstore.NewMemory()) + adtStore := adt.WrapStore(ctx, cbor.NewCborStore(tbs)) + mas, err := miner.Load(adtStore, mact) + if err != nil { + return err + } + + activeSectorsLocation := make(map[abi.SectorNumber]*miner.SectorLocation, len(activeSet)) + + if err := mas.ForEachDeadline(func(dlIdx uint64, dl miner.Deadline) error { + return dl.ForEachPartition(func(partIdx uint64, part miner.Partition) error { + pas, err := part.ActiveSectors() + if err != nil { + return err + } + + return pas.ForEach(func(i uint64) error { + activeSectorsLocation[abi.SectorNumber(i)] = &miner.SectorLocation{ + Deadline: dlIdx, + Partition: partIdx, + } + return nil + }) + }) + }); err != nil { + return err + } + + excludeSet := make(map[abi.SectorNumber]struct{}) + if cctx.IsSet("exclude") { + excludeSectors, err := getSectorsFromFile(cctx.String("exclude")) + if err != nil { + return err + } + + for _, id := range excludeSectors { + excludeSet[id] = struct{}{} + } + } + + var sectors []abi.SectorNumber + if cctx.Args().Present() { + if cctx.IsSet("sector-file") { + return xerrors.Errorf("sector-file specified along with command line params") + } + + for i, s := range cctx.Args().Slice() { + id, err := strconv.ParseUint(s, 10, 64) + if err != nil { + return xerrors.Errorf("could not parse sector %d: %w", i, err) + } + + sectors = append(sectors, abi.SectorNumber(id)) + } + } else if cctx.IsSet("sector-file") { + sectors, err = getSectorsFromFile(cctx.String("sector-file")) + if err != nil { + return err + } + } else { + from := currEpoch + 120 + to := currEpoch + 92160 + + if cctx.IsSet("from") { + from = abi.ChainEpoch(cctx.Int64("from")) + } + + if cctx.IsSet("to") { + to = abi.ChainEpoch(cctx.Int64("to")) + } + + for _, si := range activeSet { + if si.Expiration >= from && si.Expiration <= to { + sectors = append(sectors, si.SectorNumber) + } + } + } + + var sis []*miner.SectorOnChainInfo + for _, id := range sectors { + if _, exclude := excludeSet[id]; exclude { + continue + } + + si, found := activeSectorsInfo[id] + if !found { + return xerrors.Errorf("sector %d is not active", id) + } + if len(si.DealIDs) > 0 && cctx.Bool("only-cc") { + continue + } + + sis = append(sis, si) + } + + withinTolerance := func(a, b abi.ChainEpoch) bool { + diff := a - b + if diff < 0 { + diff = -diff + } + + return diff <= abi.ChainEpoch(cctx.Int64("tolerance")) + } + + extensions := map[miner.SectorLocation]map[abi.ChainEpoch][]abi.SectorNumber{} + for _, si := range sis { + extension := abi.ChainEpoch(cctx.Int64("extension")) + newExp := si.Expiration + extension + + if cctx.IsSet("new-expiration") { + newExp = abi.ChainEpoch(cctx.Int64("new-expiration")) + } + + maxExtension, err := policy.GetMaxSectorExpirationExtension(nv) + if err != nil { + return xerrors.Errorf("failed to get max extension: %w", err) + } + + maxExtendNow := currEpoch + maxExtension + if newExp > maxExtendNow { + newExp = maxExtendNow + } + + maxExp := si.Activation + policy.GetSectorMaxLifetime(si.SealProof, nv) + if newExp > maxExp { + newExp = maxExp + } + + if newExp <= si.Expiration || withinTolerance(newExp, si.Expiration) { + continue + } + + l, found := activeSectorsLocation[si.SectorNumber] + if !found { + return xerrors.Errorf("location for sector %d not found", si.SectorNumber) + } + + es, found := extensions[*l] + if !found { + ne := make(map[abi.ChainEpoch][]abi.SectorNumber) + ne[newExp] = []abi.SectorNumber{si.SectorNumber} + extensions[*l] = ne + } else { + added := false + for exp := range es { + if withinTolerance(newExp, exp) { + es[exp] = append(es[exp], si.SectorNumber) + added = true + break + } + } + + if !added { + es[newExp] = []abi.SectorNumber{si.SectorNumber} + } + } + } + + verifregAct, err := fullApi.StateGetActor(ctx, builtin.VerifiedRegistryActorAddr, types.EmptyTSK) + if err != nil { + return xerrors.Errorf("failed to lookup verifreg actor: %w", err) + } + + verifregSt, err := verifreg.Load(adtStore, verifregAct) + if err != nil { + return xerrors.Errorf("failed to load verifreg state: %w", err) + } + + claimsMap, err := verifregSt.GetClaims(maddr) + if err != nil { + return xerrors.Errorf("failed to lookup claims for miner: %w", err) + } + + claimIdsBySector, err := verifregSt.GetClaimIdsBySector(maddr) + if err != nil { + return xerrors.Errorf("failed to lookup claim IDs by sector: %w", err) + } + + sectorsMax, err := policy.GetAddressedSectorsMax(nv) + if err != nil { + return err + } + + declMax, err := policy.GetDeclarationsMax(nv) + if err != nil { + return err + } + + addrSectors := sectorsMax + if cctx.Int("max-sectors") != 0 { + addrSectors = cctx.Int("max-sectors") + if addrSectors > sectorsMax { + return xerrors.Errorf("the specified max-sectors exceeds the maximum limit") + } + } + + var params []miner.ExtendSectorExpiration2Params + + p := miner.ExtendSectorExpiration2Params{} + scount := 0 + + for l, exts := range extensions { + for newExp, numbers := range exts { + sectorsWithoutClaimsToExtend := bitfield.New() + var sectorsWithClaims []miner.SectorClaim + for _, sectorNumber := range numbers { + claimIdsToMaintain := make([]verifreg.ClaimId, 0) + claimIdsToDrop := make([]verifreg.ClaimId, 0) + cannotExtendSector := false + claimIds, ok := claimIdsBySector[sectorNumber] + // Nothing to check, add to ccSectors + if !ok { + sectorsWithoutClaimsToExtend.Set(uint64(sectorNumber)) + } else { + for _, claimId := range claimIds { + claim, ok := claimsMap[claimId] + if !ok { + return xerrors.Errorf("failed to find claim for claimId %d", claimId) + } + claimExpiration := claim.TermStart + claim.TermMax + // can be maintained in the extended sector + if claimExpiration > newExp { + claimIdsToMaintain = append(claimIdsToMaintain, claimId) + } else { + sectorInfo, ok := activeSectorsInfo[sectorNumber] + if !ok { + return xerrors.Errorf("failed to find sector in active sector set: %w", err) + } + if !cctx.Bool("drop-claims") || + // FIP-0045 requires the claim minimum duration to have passed + currEpoch <= (claim.TermStart+claim.TermMin) || + // FIP-0045 requires the sector to be in its last 30 days of life + (currEpoch <= sectorInfo.Expiration-builtin.EndOfLifeClaimDropPeriod) { + fmt.Printf("skipping sector %d because claim %d does not live long enough \n", sectorNumber, claimId) + cannotExtendSector = true + break + } + + claimIdsToDrop = append(claimIdsToDrop, claimId) + } + } + if cannotExtendSector { + continue + } + + if len(claimIdsToMaintain)+len(claimIdsToDrop) != 0 { + sectorsWithClaims = append(sectorsWithClaims, miner.SectorClaim{ + SectorNumber: sectorNumber, + MaintainClaims: claimIdsToMaintain, + DropClaims: claimIdsToDrop, + }) + } + } + } + + sectorsWithoutClaimsCount, err := sectorsWithoutClaimsToExtend.Count() + if err != nil { + return xerrors.Errorf("failed to count cc sectors: %w", err) + } + + sectorsInDecl := int(sectorsWithoutClaimsCount) + len(sectorsWithClaims) + scount += sectorsInDecl + + if scount > addrSectors || len(p.Extensions) >= declMax { + params = append(params, p) + p = miner.ExtendSectorExpiration2Params{} + scount = sectorsInDecl + } + + p.Extensions = append(p.Extensions, miner.ExpirationExtension2{ + Deadline: l.Deadline, + Partition: l.Partition, + Sectors: SectorNumsToBitfield(numbers), + SectorsWithClaims: sectorsWithClaims, + NewExpiration: newExp, + }) + + } + } + + // if we have any sectors, then one last append is needed here + if scount != 0 { + params = append(params, p) + } + + if len(params) == 0 { + fmt.Println("nothing to extend") + return nil + } + + mi, err := fullApi.StateMinerInfo(ctx, maddr, types.EmptyTSK) + if err != nil { + return xerrors.Errorf("getting miner info: %w", err) + } + + stotal := 0 + + for i := range params { + scount := 0 + for _, ext := range params[i].Extensions { + count, err := ext.Sectors.Count() + if err != nil { + return err + } + scount += int(count) + } + fmt.Printf("Extending %d sectors: ", scount) + stotal += scount + + if !cctx.Bool("really-do-it") { + pp, err := NewPseudoExtendParams(¶ms[i]) + if err != nil { + return err + } + + data, err := json.MarshalIndent(pp, "", " ") + if err != nil { + return err + } + + fmt.Println("\n", string(data)) + continue + } + + sp, aerr := actors.SerializeParams(¶ms[i]) + if aerr != nil { + return xerrors.Errorf("serializing params: %w", err) + } + + smsg, err := fullApi.MpoolPushMessage(ctx, &types.Message{ + From: mi.Worker, + To: maddr, + Method: builtin.MethodsMiner.ExtendSectorExpiration2, + Value: big.Zero(), + Params: sp, + }, spec) + if err != nil { + return xerrors.Errorf("mpool push message: %w", err) + } + + fmt.Println(smsg.Cid()) + } + + fmt.Printf("%d sectors extended\n", stotal) + + return nil + }, + } +} + +func SectorNumsToBitfield(sectors []abi.SectorNumber) bitfield.BitField { + var numbers []uint64 + for _, sector := range sectors { + numbers = append(numbers, uint64(sector)) + } + + return bitfield.NewFromSet(numbers) +} + +func getSectorsFromFile(filePath string) ([]abi.SectorNumber, error) { + file, err := os.Open(filePath) + if err != nil { + return nil, err + } + + scanner := bufio.NewScanner(file) + sectors := make([]abi.SectorNumber, 0) + + for scanner.Scan() { + line := scanner.Text() + + id, err := strconv.ParseUint(line, 10, 64) + if err != nil { + return nil, xerrors.Errorf("could not parse %s as sector id: %s", line, err) + } + + sectors = append(sectors, abi.SectorNumber(id)) + } + + if err = file.Close(); err != nil { + return nil, err + } + + return sectors, nil +} + +func NewPseudoExtendParams(p *miner.ExtendSectorExpiration2Params) (*PseudoExtendSectorExpirationParams, error) { + res := PseudoExtendSectorExpirationParams{} + for _, ext := range p.Extensions { + scount, err := ext.Sectors.Count() + if err != nil { + return nil, err + } + + sectors, err := ext.Sectors.All(scount) + if err != nil { + return nil, err + } + + res.Extensions = append(res.Extensions, PseudoExpirationExtension{ + Deadline: ext.Deadline, + Partition: ext.Partition, + Sectors: ArrayToString(sectors), + NewExpiration: ext.NewExpiration, + }) + } + return &res, nil +} + +type PseudoExtendSectorExpirationParams struct { + Extensions []PseudoExpirationExtension +} + +type PseudoExpirationExtension struct { + Deadline uint64 + Partition uint64 + Sectors string + NewExpiration abi.ChainEpoch +} + +// ArrayToString Example: {1,3,4,5,8,9} -> "1,3-5,8-9" +func ArrayToString(array []uint64) string { + sort.Slice(array, func(i, j int) bool { + return array[i] < array[j] + }) + + var sarray []string + s := "" + + for i, elm := range array { + if i == 0 { + s = strconv.FormatUint(elm, 10) + continue + } + if elm == array[i-1] { + continue // filter out duplicates + } else if elm == array[i-1]+1 { + s = strings.Split(s, "-")[0] + "-" + strconv.FormatUint(elm, 10) + } else { + sarray = append(sarray, s) + s = strconv.FormatUint(elm, 10) + } + } + + if s != "" { + sarray = append(sarray, s) + } + + return strings.Join(sarray, ",") +} + +func SectorsCompactPartitionsCmd(getActorAddress ActorAddressGetter) *cli.Command { + return &cli.Command{ + Name: "compact-partitions", + Usage: "removes dead sectors from partitions and reduces the number of partitions used if possible", + Flags: []cli.Flag{ + &cli.Uint64Flag{ + Name: "deadline", + Usage: "the deadline to compact the partitions in", + Required: true, + }, + &cli.Int64SliceFlag{ + Name: "partitions", + Usage: "list of partitions to compact sectors in", + Required: true, + }, + &cli.BoolFlag{ + Name: "really-do-it", + Usage: "Actually send transaction performing the action", + Value: false, + }, + }, + Action: func(cctx *cli.Context) error { + fullNodeAPI, acloser, err := lcli.GetFullNodeAPI(cctx) + if err != nil { + return err + } + defer acloser() + + ctx := lcli.ReqContext(cctx) + + maddr, err := getActorAddress(cctx) + if err != nil { + return err + } + + minfo, err := fullNodeAPI.StateMinerInfo(ctx, maddr, types.EmptyTSK) + if err != nil { + return err + } + + deadline := cctx.Uint64("deadline") + if deadline > miner.WPoStPeriodDeadlines { + return fmt.Errorf("deadline %d out of range", deadline) + } + + parts := cctx.Int64Slice("partitions") + if len(parts) <= 0 { + return fmt.Errorf("must include at least one partition to compact") + } + fmt.Printf("compacting %d partitions\n", len(parts)) + + var makeMsgForPartitions func(partitionsBf bitfield.BitField) ([]*types.Message, error) + makeMsgForPartitions = func(partitionsBf bitfield.BitField) ([]*types.Message, error) { + params := miner.CompactPartitionsParams{ + Deadline: deadline, + Partitions: partitionsBf, + } + + sp, aerr := actors.SerializeParams(¶ms) + if aerr != nil { + return nil, xerrors.Errorf("serializing params: %w", err) + } + + msg := &types.Message{ + From: minfo.Worker, + To: maddr, + Method: builtin.MethodsMiner.CompactPartitions, + Value: big.Zero(), + Params: sp, + } + + estimatedMsg, err := fullNodeAPI.GasEstimateMessageGas(ctx, msg, nil, types.EmptyTSK) + if err != nil && errors.Is(err, &api.ErrOutOfGas{}) { + // the message is too big -- split into 2 + partitionsSlice, err := partitionsBf.All(math.MaxUint64) + if err != nil { + return nil, err + } + + partitions1 := bitfield.New() + for i := 0; i < len(partitionsSlice)/2; i++ { + partitions1.Set(uint64(i)) + } + + msgs1, err := makeMsgForPartitions(partitions1) + if err != nil { + return nil, err + } + + // time for the second half + partitions2 := bitfield.New() + for i := len(partitionsSlice) / 2; i < len(partitionsSlice); i++ { + partitions2.Set(uint64(i)) + } + + msgs2, err := makeMsgForPartitions(partitions2) + if err != nil { + return nil, err + } + + return append(msgs1, msgs2...), nil + } else if err != nil { + return nil, err + } + + return []*types.Message{estimatedMsg}, nil + } + + partitions := bitfield.New() + for _, partition := range parts { + partitions.Set(uint64(partition)) + } + + msgs, err := makeMsgForPartitions(partitions) + if err != nil { + return xerrors.Errorf("failed to make messages: %w", err) + } + + // Actually send the messages if really-do-it provided, simulate otherwise + if cctx.Bool("really-do-it") { + smsgs, err := fullNodeAPI.MpoolBatchPushMessage(ctx, msgs, nil) + if err != nil { + return xerrors.Errorf("mpool push: %w", err) + } + + if len(smsgs) == 1 { + fmt.Printf("Requested compact partitions in message %s\n", smsgs[0].Cid()) + } else { + fmt.Printf("Requested compact partitions in %d messages\n\n", len(smsgs)) + for _, v := range smsgs { + fmt.Println(v.Cid()) + } + } + + for _, v := range smsgs { + wait, err := fullNodeAPI.StateWaitMsg(ctx, v.Cid(), 2) + if err != nil { + return err + } + + // check it executed successfully + if wait.Receipt.ExitCode.IsError() { + fmt.Println(cctx.App.Writer, "compact partitions msg %s failed!", v.Cid()) + return err + } + } + + return nil + } + + for i, v := range msgs { + fmt.Printf("total of %d CompactPartitions msgs would be sent\n", len(msgs)) + + estMsg, err := fullNodeAPI.GasEstimateMessageGas(ctx, v, nil, types.EmptyTSK) + if err != nil { + return err + } + + fmt.Printf("msg %d would cost up to %s\n", i+1, types.FIL(estMsg.RequiredFunds())) + } + + return nil + + }, + } +} + +func TerminateSectorCmd(getActorAddress ActorAddressGetter) *cli.Command { + return &cli.Command{ + Name: "terminate", + Usage: "Forcefully terminate a sector (WARNING: This means losing power and pay a one-time termination penalty(including collateral) for the terminated sector)", + ArgsUsage: "[sectorNum1 sectorNum2 ...]", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "actor", + Usage: "specify the address of miner actor", + }, + &cli.BoolFlag{ + Name: "really-do-it", + Usage: "pass this flag if you know what you are doing", + }, + &cli.StringFlag{ + Name: "from", + Usage: "specify the address to send the terminate message from", + }, + }, + Action: func(cctx *cli.Context) error { + if cctx.NArg() < 1 { + return lcli.ShowHelp(cctx, fmt.Errorf("at least one sector must be specified")) + } + + var maddr address.Address + if act := cctx.String("actor"); act != "" { + var err error + maddr, err = address.NewFromString(act) + if err != nil { + return fmt.Errorf("parsing address %s: %w", act, err) + } + } + + if !cctx.Bool("really-do-it") { + return fmt.Errorf("this is a command for advanced users, only use it if you are sure of what you are doing") + } + + nodeApi, closer, err := lcli.GetFullNodeAPI(cctx) + if err != nil { + return err + } + defer closer() + + ctx := lcli.ReqContext(cctx) + + if maddr.Empty() { + maddr, err = getActorAddress(cctx) + if err != nil { + return err + } + } + + mi, err := nodeApi.StateMinerInfo(ctx, maddr, types.EmptyTSK) + if err != nil { + return err + } + + terminationDeclarationParams := []miner2.TerminationDeclaration{} + + for _, sn := range cctx.Args().Slice() { + sectorNum, err := strconv.ParseUint(sn, 10, 64) + if err != nil { + return fmt.Errorf("could not parse sector number: %w", err) + } + + sectorbit := bitfield.New() + sectorbit.Set(sectorNum) + + loca, err := nodeApi.StateSectorPartition(ctx, maddr, abi.SectorNumber(sectorNum), types.EmptyTSK) + if err != nil { + return fmt.Errorf("get state sector partition %s", err) + } + + para := miner2.TerminationDeclaration{ + Deadline: loca.Deadline, + Partition: loca.Partition, + Sectors: sectorbit, + } + + terminationDeclarationParams = append(terminationDeclarationParams, para) + } + + terminateSectorParams := &miner2.TerminateSectorsParams{ + Terminations: terminationDeclarationParams, + } + + sp, err := actors.SerializeParams(terminateSectorParams) + if err != nil { + return xerrors.Errorf("serializing params: %w", err) + } + + var fromAddr address.Address + if from := cctx.String("from"); from != "" { + var err error + fromAddr, err = address.NewFromString(from) + if err != nil { + return fmt.Errorf("parsing address %s: %w", from, err) + } + } else { + fromAddr = mi.Worker + } + + smsg, err := nodeApi.MpoolPushMessage(ctx, &types.Message{ + From: fromAddr, + To: maddr, + Method: builtin.MethodsMiner.TerminateSectors, + + Value: big.Zero(), + Params: sp, + }, nil) + if err != nil { + return xerrors.Errorf("mpool push message: %w", err) + } + + fmt.Println("sent termination message:", smsg.Cid()) + + wait, err := nodeApi.StateWaitMsg(ctx, smsg.Cid(), uint64(cctx.Int("confidence"))) + if err != nil { + return err + } + + if wait.Receipt.ExitCode.IsError() { + return fmt.Errorf("terminate sectors message returned exit %d", wait.Receipt.ExitCode) + } + + return nil + }, + } +} diff --git a/cli/spcli/statemeta.go b/cli/spcli/statemeta.go new file mode 100644 index 000000000..72de87807 --- /dev/null +++ b/cli/spcli/statemeta.go @@ -0,0 +1,95 @@ +package spcli + +import ( + "github.com/fatih/color" + + sealing "github.com/filecoin-project/lotus/storage/pipeline" +) + +type StateMeta struct { + I int + Col color.Attribute + State sealing.SectorState +} + +var StateOrder = map[sealing.SectorState]StateMeta{} +var StateList = []StateMeta{ + {Col: 39, State: "Total"}, + {Col: color.FgGreen, State: sealing.Proving}, + {Col: color.FgGreen, State: sealing.Available}, + {Col: color.FgGreen, State: sealing.UpdateActivating}, + + {Col: color.FgMagenta, State: sealing.ReceiveSector}, + + {Col: color.FgBlue, State: sealing.Empty}, + {Col: color.FgBlue, State: sealing.WaitDeals}, + {Col: color.FgBlue, State: sealing.AddPiece}, + {Col: color.FgBlue, State: sealing.SnapDealsWaitDeals}, + {Col: color.FgBlue, State: sealing.SnapDealsAddPiece}, + + {Col: color.FgRed, State: sealing.UndefinedSectorState}, + {Col: color.FgYellow, State: sealing.Packing}, + {Col: color.FgYellow, State: sealing.GetTicket}, + {Col: color.FgYellow, State: sealing.PreCommit1}, + {Col: color.FgYellow, State: sealing.PreCommit2}, + {Col: color.FgYellow, State: sealing.PreCommitting}, + {Col: color.FgYellow, State: sealing.PreCommitWait}, + {Col: color.FgYellow, State: sealing.SubmitPreCommitBatch}, + {Col: color.FgYellow, State: sealing.PreCommitBatchWait}, + {Col: color.FgYellow, State: sealing.WaitSeed}, + {Col: color.FgYellow, State: sealing.Committing}, + {Col: color.FgYellow, State: sealing.CommitFinalize}, + {Col: color.FgYellow, State: sealing.SubmitCommit}, + {Col: color.FgYellow, State: sealing.CommitWait}, + {Col: color.FgYellow, State: sealing.SubmitCommitAggregate}, + {Col: color.FgYellow, State: sealing.CommitAggregateWait}, + {Col: color.FgYellow, State: sealing.FinalizeSector}, + {Col: color.FgYellow, State: sealing.SnapDealsPacking}, + {Col: color.FgYellow, State: sealing.UpdateReplica}, + {Col: color.FgYellow, State: sealing.ProveReplicaUpdate}, + {Col: color.FgYellow, State: sealing.SubmitReplicaUpdate}, + {Col: color.FgYellow, State: sealing.ReplicaUpdateWait}, + {Col: color.FgYellow, State: sealing.WaitMutable}, + {Col: color.FgYellow, State: sealing.FinalizeReplicaUpdate}, + {Col: color.FgYellow, State: sealing.ReleaseSectorKey}, + + {Col: color.FgCyan, State: sealing.Terminating}, + {Col: color.FgCyan, State: sealing.TerminateWait}, + {Col: color.FgCyan, State: sealing.TerminateFinality}, + {Col: color.FgCyan, State: sealing.TerminateFailed}, + {Col: color.FgCyan, State: sealing.Removing}, + {Col: color.FgCyan, State: sealing.Removed}, + {Col: color.FgCyan, State: sealing.AbortUpgrade}, + + {Col: color.FgRed, State: sealing.FailedUnrecoverable}, + {Col: color.FgRed, State: sealing.AddPieceFailed}, + {Col: color.FgRed, State: sealing.SealPreCommit1Failed}, + {Col: color.FgRed, State: sealing.SealPreCommit2Failed}, + {Col: color.FgRed, State: sealing.PreCommitFailed}, + {Col: color.FgRed, State: sealing.ComputeProofFailed}, + {Col: color.FgRed, State: sealing.RemoteCommitFailed}, + {Col: color.FgRed, State: sealing.CommitFailed}, + {Col: color.FgRed, State: sealing.CommitFinalizeFailed}, + {Col: color.FgRed, State: sealing.PackingFailed}, + {Col: color.FgRed, State: sealing.FinalizeFailed}, + {Col: color.FgRed, State: sealing.Faulty}, + {Col: color.FgRed, State: sealing.FaultReported}, + {Col: color.FgRed, State: sealing.FaultedFinal}, + {Col: color.FgRed, State: sealing.RemoveFailed}, + {Col: color.FgRed, State: sealing.DealsExpired}, + {Col: color.FgRed, State: sealing.RecoverDealIDs}, + {Col: color.FgRed, State: sealing.SnapDealsAddPieceFailed}, + {Col: color.FgRed, State: sealing.SnapDealsDealsExpired}, + {Col: color.FgRed, State: sealing.ReplicaUpdateFailed}, + {Col: color.FgRed, State: sealing.ReleaseSectorKeyFailed}, + {Col: color.FgRed, State: sealing.FinalizeReplicaUpdateFailed}, +} + +func init() { + for i, state := range StateList { + StateOrder[state.State] = StateMeta{ + I: i, + Col: state.Col, + } + } +} diff --git a/cli/spcli/util.go b/cli/spcli/util.go new file mode 100644 index 000000000..71ac371fe --- /dev/null +++ b/cli/spcli/util.go @@ -0,0 +1,9 @@ +package spcli + +import ( + "github.com/urfave/cli/v2" + + "github.com/filecoin-project/go-address" +) + +type ActorAddressGetter func(cctx *cli.Context) (address address.Address, err error) diff --git a/cli/state.go b/cli/state.go index f7d7e7127..343e68b53 100644 --- a/cli/state.go +++ b/cli/state.go @@ -17,10 +17,8 @@ import ( "text/tabwriter" "time" - "github.com/fatih/color" "github.com/ipfs/go-cid" cbor "github.com/ipfs/go-ipld-cbor" - "github.com/multiformats/go-multiaddr" "github.com/urfave/cli/v2" cbg "github.com/whyrusleeping/cbor-gen" "golang.org/x/xerrors" @@ -47,43 +45,6 @@ import ( cliutil "github.com/filecoin-project/lotus/cli/util" ) -var StateCmd = &cli.Command{ - Name: "state", - Usage: "Interact with and query filecoin chain state", - Flags: []cli.Flag{ - &cli.StringFlag{ - Name: "tipset", - Usage: "specify tipset to call method on (pass comma separated array of cids)", - }, - }, - Subcommands: []*cli.Command{ - StatePowerCmd, - StateSectorsCmd, - StateActiveSectorsCmd, - StateListActorsCmd, - StateListMinersCmd, - StateCircSupplyCmd, - StateSectorCmd, - StateGetActorCmd, - StateLookupIDCmd, - StateReplayCmd, - StateSectorSizeCmd, - StateReadStateCmd, - StateListMessagesCmd, - StateComputeStateCmd, - StateCallCmd, - StateGetDealSetCmd, - StateWaitMsgCmd, - StateSearchMsgCmd, - StateMinerInfo, - StateMarketCmd, - StateExecTraceCmd, - StateNtwkVersionCmd, - StateMinerProvingDeadlineCmd, - StateSysActorCIDsCmd, - }, -} - var StateMinerProvingDeadlineCmd = &cli.Command{ Name: "miner-proving-deadline", Usage: "Retrieve information about a given miner's proving deadline", @@ -127,114 +88,6 @@ var StateMinerProvingDeadlineCmd = &cli.Command{ }, } -var StateMinerInfo = &cli.Command{ - Name: "miner-info", - Usage: "Retrieve miner information", - ArgsUsage: "[minerAddress]", - Action: func(cctx *cli.Context) error { - api, closer, err := GetFullNodeAPI(cctx) - if err != nil { - return err - } - defer closer() - - ctx := ReqContext(cctx) - - if cctx.NArg() != 1 { - return IncorrectNumArgs(cctx) - } - - addr, err := address.NewFromString(cctx.Args().First()) - if err != nil { - return err - } - - ts, err := LoadTipSet(ctx, cctx, api) - if err != nil { - return err - } - - mi, err := api.StateMinerInfo(ctx, addr, ts.Key()) - if err != nil { - return err - } - - availableBalance, err := api.StateMinerAvailableBalance(ctx, addr, ts.Key()) - if err != nil { - return xerrors.Errorf("getting miner available balance: %w", err) - } - fmt.Printf("Available Balance: %s\n", types.FIL(availableBalance)) - fmt.Printf("Owner:\t%s\n", mi.Owner) - fmt.Printf("Worker:\t%s\n", mi.Worker) - for i, controlAddress := range mi.ControlAddresses { - fmt.Printf("Control %d: \t%s\n", i, controlAddress) - } - if mi.Beneficiary != address.Undef { - fmt.Printf("Beneficiary:\t%s\n", mi.Beneficiary) - if mi.Beneficiary != mi.Owner { - fmt.Printf("Beneficiary Quota:\t%s\n", mi.BeneficiaryTerm.Quota) - fmt.Printf("Beneficiary Used Quota:\t%s\n", mi.BeneficiaryTerm.UsedQuota) - fmt.Printf("Beneficiary Expiration:\t%s\n", mi.BeneficiaryTerm.Expiration) - } - } - if mi.PendingBeneficiaryTerm != nil { - fmt.Printf("Pending Beneficiary Term:\n") - fmt.Printf("New Beneficiary:\t%s\n", mi.PendingBeneficiaryTerm.NewBeneficiary) - fmt.Printf("New Quota:\t%s\n", mi.PendingBeneficiaryTerm.NewQuota) - fmt.Printf("New Expiration:\t%s\n", mi.PendingBeneficiaryTerm.NewExpiration) - fmt.Printf("Approved By Beneficiary:\t%t\n", mi.PendingBeneficiaryTerm.ApprovedByBeneficiary) - fmt.Printf("Approved By Nominee:\t%t\n", mi.PendingBeneficiaryTerm.ApprovedByNominee) - } - - fmt.Printf("PeerID:\t%s\n", mi.PeerId) - fmt.Printf("Multiaddrs:\t") - for _, addr := range mi.Multiaddrs { - a, err := multiaddr.NewMultiaddrBytes(addr) - if err != nil { - return xerrors.Errorf("undecodable listen address: %w", err) - } - fmt.Printf("%s ", a) - } - fmt.Println() - fmt.Printf("Consensus Fault End:\t%d\n", mi.ConsensusFaultElapsed) - - fmt.Printf("SectorSize:\t%s (%d)\n", types.SizeStr(types.NewInt(uint64(mi.SectorSize))), mi.SectorSize) - pow, err := api.StateMinerPower(ctx, addr, ts.Key()) - if err != nil { - return err - } - - fmt.Printf("Byte Power: %s / %s (%0.4f%%)\n", - color.BlueString(types.SizeStr(pow.MinerPower.RawBytePower)), - types.SizeStr(pow.TotalPower.RawBytePower), - types.BigDivFloat( - types.BigMul(pow.MinerPower.RawBytePower, big.NewInt(100)), - pow.TotalPower.RawBytePower, - ), - ) - - fmt.Printf("Actual Power: %s / %s (%0.4f%%)\n", - color.GreenString(types.DeciStr(pow.MinerPower.QualityAdjPower)), - types.DeciStr(pow.TotalPower.QualityAdjPower), - types.BigDivFloat( - types.BigMul(pow.MinerPower.QualityAdjPower, big.NewInt(100)), - pow.TotalPower.QualityAdjPower, - ), - ) - - fmt.Println() - - cd, err := api.StateMinerProvingDeadline(ctx, addr, ts.Key()) - if err != nil { - return xerrors.Errorf("getting miner info: %w", err) - } - - fmt.Printf("Proving Period Start:\t%s\n", cliutil.EpochTime(cd.CurrentEpoch, cd.PeriodStart)) - - return nil - }, -} - func ParseTipSetString(ts string) ([]cid.Cid, error) { strs := strings.Split(ts, ",") diff --git a/cli/wallet.go b/cli/wallet.go index faf7bc239..4af8dca58 100644 --- a/cli/wallet.go +++ b/cli/wallet.go @@ -27,7 +27,7 @@ import ( "github.com/filecoin-project/lotus/lib/tablewriter" ) -var walletCmd = &cli.Command{ +var WalletCmd = &cli.Command{ Name: "wallet", Usage: "Manage wallet", Subcommands: []*cli.Command{ diff --git a/cmd/lotus-miner/actor.go b/cmd/lotus-miner/actor.go index 6d76cc07f..1ff613fc1 100644 --- a/cmd/lotus-miner/actor.go +++ b/cmd/lotus-miner/actor.go @@ -1,38 +1,20 @@ package main import ( - "bytes" "fmt" "os" - "strconv" "strings" "github.com/fatih/color" - "github.com/ipfs/go-cid" - cbor "github.com/ipfs/go-ipld-cbor" - "github.com/libp2p/go-libp2p/core/peer" - ma "github.com/multiformats/go-multiaddr" "github.com/urfave/cli/v2" - "golang.org/x/xerrors" "github.com/filecoin-project/go-address" - "github.com/filecoin-project/go-bitfield" - rlepluslazy "github.com/filecoin-project/go-bitfield/rle" - "github.com/filecoin-project/go-state-types/abi" - "github.com/filecoin-project/go-state-types/big" - "github.com/filecoin-project/go-state-types/builtin" - "github.com/filecoin-project/go-state-types/builtin/v9/miner" - "github.com/filecoin-project/go-state-types/network" - "github.com/filecoin-project/lotus/api" - "github.com/filecoin-project/lotus/blockstore" - "github.com/filecoin-project/lotus/build" - "github.com/filecoin-project/lotus/chain/actors" - "github.com/filecoin-project/lotus/chain/actors/adt" + lapi "github.com/filecoin-project/lotus/api" builtin2 "github.com/filecoin-project/lotus/chain/actors/builtin" - lminer "github.com/filecoin-project/lotus/chain/actors/builtin/miner" "github.com/filecoin-project/lotus/chain/types" lcli "github.com/filecoin-project/lotus/cli" + "github.com/filecoin-project/lotus/cli/spcli" "github.com/filecoin-project/lotus/lib/tablewriter" ) @@ -40,399 +22,37 @@ var actorCmd = &cli.Command{ Name: "actor", Usage: "manipulate the miner actor", Subcommands: []*cli.Command{ - actorSetAddrsCmd, - actorWithdrawCmd, - actorRepayDebtCmd, - actorSetPeeridCmd, - actorSetOwnerCmd, - actorControl, - actorProposeChangeWorker, - actorConfirmChangeWorker, - actorCompactAllocatedCmd, - actorProposeChangeBeneficiary, - actorConfirmChangeBeneficiary, + spcli.ActorSetAddrsCmd(LMActorGetter), + spcli.ActorWithdrawCmd(LMActorGetter), + spcli.ActorRepayDebtCmd(LMActorGetter), + spcli.ActorSetPeeridCmd(LMActorGetter), + spcli.ActorSetOwnerCmd(LMConfigOrActorGetter), + spcli.ActorControlCmd(LMConfigOrActorGetter, actorControlListCmd), + spcli.ActorProposeChangeWorkerCmd(LMActorGetter), + spcli.ActorConfirmChangeWorkerCmd(LMActorGetter), + spcli.ActorCompactAllocatedCmd(LMActorGetter), + spcli.ActorProposeChangeBeneficiaryCmd(LMActorGetter), + spcli.ActorConfirmChangeBeneficiaryCmd(LMConfigOrActorGetter), }, } -var actorSetAddrsCmd = &cli.Command{ - Name: "set-addresses", - Aliases: []string{"set-addrs"}, - Usage: "set addresses that your miner can be publicly dialed on", - ArgsUsage: "", - Flags: []cli.Flag{ - &cli.StringFlag{ - Name: "from", - Usage: "optionally specify the account to send the message from", - }, - &cli.Int64Flag{ - Name: "gas-limit", - Usage: "set gas limit", - Value: 0, - }, - &cli.BoolFlag{ - Name: "unset", - Usage: "unset address", - Value: false, - }, - }, - Action: func(cctx *cli.Context) error { - args := cctx.Args().Slice() - unset := cctx.Bool("unset") - if len(args) == 0 && !unset { - return cli.ShowSubcommandHelp(cctx) - } - if len(args) > 0 && unset { - return fmt.Errorf("unset can only be used with no arguments") - } - - minerApi, closer, err := lcli.GetStorageMinerAPI(cctx) - if err != nil { - return err - } - defer closer() - - api, acloser, err := lcli.GetFullNodeAPI(cctx) - if err != nil { - return err - } - defer acloser() - - ctx := lcli.ReqContext(cctx) - - var addrs []abi.Multiaddrs - for _, a := range args { - maddr, err := ma.NewMultiaddr(a) - if err != nil { - return fmt.Errorf("failed to parse %q as a multiaddr: %w", a, err) - } - - maddrNop2p, strip := ma.SplitFunc(maddr, func(c ma.Component) bool { - return c.Protocol().Code == ma.P_P2P - }) - - if strip != nil { - fmt.Println("Stripping peerid ", strip, " from ", maddr) - } - addrs = append(addrs, maddrNop2p.Bytes()) - } - - maddr, err := minerApi.ActorAddress(ctx) - if err != nil { - return err - } - - minfo, err := api.StateMinerInfo(ctx, maddr, types.EmptyTSK) - if err != nil { - return err - } - - fromAddr := minfo.Worker - if from := cctx.String("from"); from != "" { - addr, err := address.NewFromString(from) - if err != nil { - return err - } - - fromAddr = addr - } - - fromId, err := api.StateLookupID(ctx, fromAddr, types.EmptyTSK) - if err != nil { - return err - } - - if !isController(minfo, fromId) { - return xerrors.Errorf("sender isn't a controller of miner: %s", fromId) - } - - params, err := actors.SerializeParams(&miner.ChangeMultiaddrsParams{NewMultiaddrs: addrs}) - if err != nil { - return err - } - - gasLimit := cctx.Int64("gas-limit") - - smsg, err := api.MpoolPushMessage(ctx, &types.Message{ - To: maddr, - From: fromId, - Value: types.NewInt(0), - GasLimit: gasLimit, - Method: builtin.MethodsMiner.ChangeMultiaddrs, - Params: params, - }, nil) - if err != nil { - return err - } - - fmt.Printf("Requested multiaddrs change in message %s\n", smsg.Cid()) - return nil - - }, +func LMConfigOrActorGetter(cctx *cli.Context) (address.Address, error) { + ctx := lcli.ReqContext(cctx) + return getActorAddress(ctx, cctx) } -var actorSetPeeridCmd = &cli.Command{ - Name: "set-peer-id", - Usage: "set the peer id of your miner", - ArgsUsage: "", - Flags: []cli.Flag{ - &cli.Int64Flag{ - Name: "gas-limit", - Usage: "set gas limit", - Value: 0, - }, - }, - Action: func(cctx *cli.Context) error { - minerApi, closer, err := lcli.GetStorageMinerAPI(cctx) - if err != nil { - return err - } - defer closer() +func getControlAddresses(cctx *cli.Context, actor address.Address) (lapi.AddressConfig, error) { + minerApi, closer, err := lcli.GetStorageMinerAPI(cctx) + if err != nil { + return lapi.AddressConfig{}, err + } + defer closer() - api, acloser, err := lcli.GetFullNodeAPI(cctx) - if err != nil { - return err - } - defer acloser() - - ctx := lcli.ReqContext(cctx) - - if cctx.NArg() != 1 { - return lcli.IncorrectNumArgs(cctx) - } - - pid, err := peer.Decode(cctx.Args().Get(0)) - if err != nil { - return fmt.Errorf("failed to parse input as a peerId: %w", err) - } - - maddr, err := minerApi.ActorAddress(ctx) - if err != nil { - return err - } - - minfo, err := api.StateMinerInfo(ctx, maddr, types.EmptyTSK) - if err != nil { - return err - } - - params, err := actors.SerializeParams(&miner.ChangePeerIDParams{NewID: abi.PeerID(pid)}) - if err != nil { - return err - } - - gasLimit := cctx.Int64("gas-limit") - - smsg, err := api.MpoolPushMessage(ctx, &types.Message{ - To: maddr, - From: minfo.Worker, - Value: types.NewInt(0), - GasLimit: gasLimit, - Method: builtin.MethodsMiner.ChangePeerID, - Params: params, - }, nil) - if err != nil { - return err - } - - fmt.Printf("Requested peerid change in message %s\n", smsg.Cid()) - return nil - - }, + ctx := lcli.ReqContext(cctx) + return minerApi.ActorAddressConfig(ctx) } -var actorWithdrawCmd = &cli.Command{ - Name: "withdraw", - Usage: "withdraw available balance to beneficiary", - ArgsUsage: "[amount (FIL)]", - Flags: []cli.Flag{ - &cli.IntFlag{ - Name: "confidence", - Usage: "number of block confirmations to wait for", - Value: int(build.MessageConfidence), - }, - &cli.BoolFlag{ - Name: "beneficiary", - Usage: "send withdraw message from the beneficiary address", - }, - }, - Action: func(cctx *cli.Context) error { - amount := abi.NewTokenAmount(0) - - if cctx.Args().Present() { - f, err := types.ParseFIL(cctx.Args().First()) - if err != nil { - return xerrors.Errorf("parsing 'amount' argument: %w", err) - } - - amount = abi.TokenAmount(f) - } - - minerApi, closer, err := lcli.GetStorageMinerAPI(cctx) - if err != nil { - return err - } - defer closer() - - api, acloser, err := lcli.GetFullNodeAPI(cctx) - if err != nil { - return err - } - defer acloser() - - ctx := lcli.ReqContext(cctx) - - var res cid.Cid - if cctx.IsSet("beneficiary") { - res, err = minerApi.BeneficiaryWithdrawBalance(ctx, amount) - } else { - res, err = minerApi.ActorWithdrawBalance(ctx, amount) - } - if err != nil { - return err - } - - fmt.Printf("Requested withdrawal in message %s\nwaiting for it to be included in a block..\n", res) - - // wait for it to get mined into a block - wait, err := api.StateWaitMsg(ctx, res, uint64(cctx.Int("confidence"))) - if err != nil { - return xerrors.Errorf("Timeout waiting for withdrawal message %s", res) - } - - if wait.Receipt.ExitCode.IsError() { - return xerrors.Errorf("Failed to execute withdrawal message %s: %w", wait.Message, wait.Receipt.ExitCode.Error()) - } - - nv, err := api.StateNetworkVersion(ctx, wait.TipSet) - if err != nil { - return err - } - - if nv >= network.Version14 { - var withdrawn abi.TokenAmount - if err := withdrawn.UnmarshalCBOR(bytes.NewReader(wait.Receipt.Return)); err != nil { - return err - } - - fmt.Printf("Successfully withdrew %s \n", types.FIL(withdrawn)) - if withdrawn.LessThan(amount) { - fmt.Printf("Note that this is less than the requested amount of %s\n", types.FIL(amount)) - } - } - - return nil - }, -} - -var actorRepayDebtCmd = &cli.Command{ - Name: "repay-debt", - Usage: "pay down a miner's debt", - ArgsUsage: "[amount (FIL)]", - Flags: []cli.Flag{ - &cli.StringFlag{ - Name: "from", - Usage: "optionally specify the account to send funds from", - }, - }, - Action: func(cctx *cli.Context) error { - minerApi, closer, err := lcli.GetStorageMinerAPI(cctx) - if err != nil { - return err - } - defer closer() - - api, acloser, err := lcli.GetFullNodeAPI(cctx) - if err != nil { - return err - } - defer acloser() - - ctx := lcli.ReqContext(cctx) - - maddr, err := minerApi.ActorAddress(ctx) - if err != nil { - return err - } - - mi, err := api.StateMinerInfo(ctx, maddr, types.EmptyTSK) - if err != nil { - return err - } - - var amount abi.TokenAmount - if cctx.Args().Present() { - f, err := types.ParseFIL(cctx.Args().First()) - if err != nil { - return xerrors.Errorf("parsing 'amount' argument: %w", err) - } - - amount = abi.TokenAmount(f) - } else { - mact, err := api.StateGetActor(ctx, maddr, types.EmptyTSK) - if err != nil { - return err - } - - store := adt.WrapStore(ctx, cbor.NewCborStore(blockstore.NewAPIBlockstore(api))) - - mst, err := lminer.Load(store, mact) - if err != nil { - return err - } - - amount, err = mst.FeeDebt() - if err != nil { - return err - } - - } - - fromAddr := mi.Worker - if from := cctx.String("from"); from != "" { - addr, err := address.NewFromString(from) - if err != nil { - return err - } - - fromAddr = addr - } - - fromId, err := api.StateLookupID(ctx, fromAddr, types.EmptyTSK) - if err != nil { - return err - } - - if !isController(mi, fromId) { - return xerrors.Errorf("sender isn't a controller of miner: %s", fromId) - } - - smsg, err := api.MpoolPushMessage(ctx, &types.Message{ - To: maddr, - From: fromId, - Value: amount, - Method: builtin.MethodsMiner.RepayDebt, - Params: nil, - }, nil) - if err != nil { - return err - } - - fmt.Printf("Sent repay debt message %s\n", smsg.Cid()) - - return nil - }, -} - -var actorControl = &cli.Command{ - Name: "control", - Usage: "Manage control addresses", - Subcommands: []*cli.Command{ - actorControlList, - actorControlSet, - }, -} - -var actorControlList = &cli.Command{ +var actorControlListCmd = &cli.Command{ Name: "list", Usage: "Get currently set control addresses", Flags: []cli.Flag{ @@ -441,12 +61,6 @@ var actorControlList = &cli.Command{ }, }, Action: func(cctx *cli.Context) error { - minerApi, closer, err := lcli.GetStorageMinerAPI(cctx) - if err != nil { - return err - } - defer closer() - api, acloser, err := lcli.GetFullNodeAPIV1(cctx) if err != nil { return err @@ -455,7 +69,7 @@ var actorControlList = &cli.Command{ ctx := lcli.ReqContext(cctx) - maddr, err := getActorAddress(ctx, cctx) + maddr, err := LMActorOrEnvGetter(cctx) if err != nil { return err } @@ -473,11 +87,10 @@ var actorControlList = &cli.Command{ tablewriter.Col("balance"), ) - ac, err := minerApi.ActorAddressConfig(ctx) + ac, err := getControlAddresses(cctx, maddr) if err != nil { return err } - commit := map[address.Address]struct{}{} precommit := map[address.Address]struct{}{} terminate := map[address.Address]struct{}{} @@ -600,853 +213,3 @@ var actorControlList = &cli.Command{ return tw.Flush(os.Stdout) }, } - -var actorControlSet = &cli.Command{ - Name: "set", - Usage: "Set control address(-es)", - ArgsUsage: "[...address]", - Flags: []cli.Flag{ - &cli.BoolFlag{ - Name: "really-do-it", - Usage: "Actually send transaction performing the action", - Value: false, - }, - }, - Action: func(cctx *cli.Context) error { - minerApi, closer, err := lcli.GetStorageMinerAPI(cctx) - if err != nil { - return err - } - defer closer() - - api, acloser, err := lcli.GetFullNodeAPI(cctx) - if err != nil { - return err - } - defer acloser() - - ctx := lcli.ReqContext(cctx) - - maddr, err := minerApi.ActorAddress(ctx) - if err != nil { - return err - } - - mi, err := api.StateMinerInfo(ctx, maddr, types.EmptyTSK) - if err != nil { - return err - } - - del := map[address.Address]struct{}{} - existing := map[address.Address]struct{}{} - for _, controlAddress := range mi.ControlAddresses { - ka, err := api.StateAccountKey(ctx, controlAddress, types.EmptyTSK) - if err != nil { - return err - } - - del[ka] = struct{}{} - existing[ka] = struct{}{} - } - - var toSet []address.Address - - for i, as := range cctx.Args().Slice() { - a, err := address.NewFromString(as) - if err != nil { - return xerrors.Errorf("parsing address %d: %w", i, err) - } - - ka, err := api.StateAccountKey(ctx, a, types.EmptyTSK) - if err != nil { - return err - } - - // make sure the address exists on chain - _, err = api.StateLookupID(ctx, ka, types.EmptyTSK) - if err != nil { - return xerrors.Errorf("looking up %s: %w", ka, err) - } - - delete(del, ka) - toSet = append(toSet, ka) - } - - for a := range del { - fmt.Println("Remove", a) - } - for _, a := range toSet { - if _, exists := existing[a]; !exists { - fmt.Println("Add", a) - } - } - - if !cctx.Bool("really-do-it") { - fmt.Println("Pass --really-do-it to actually execute this action") - return nil - } - - cwp := &miner.ChangeWorkerAddressParams{ - NewWorker: mi.Worker, - NewControlAddrs: toSet, - } - - sp, err := actors.SerializeParams(cwp) - if err != nil { - return xerrors.Errorf("serializing params: %w", err) - } - - smsg, err := api.MpoolPushMessage(ctx, &types.Message{ - From: mi.Owner, - To: maddr, - Method: builtin.MethodsMiner.ChangeWorkerAddress, - - Value: big.Zero(), - Params: sp, - }, nil) - if err != nil { - return xerrors.Errorf("mpool push: %w", err) - } - - fmt.Println("Message CID:", smsg.Cid()) - - return nil - }, -} - -var actorSetOwnerCmd = &cli.Command{ - Name: "set-owner", - Usage: "Set owner address (this command should be invoked twice, first with the old owner as the senderAddress, and then with the new owner)", - ArgsUsage: "[newOwnerAddress senderAddress]", - Flags: []cli.Flag{ - &cli.BoolFlag{ - Name: "really-do-it", - Usage: "Actually send transaction performing the action", - Value: false, - }, - }, - Action: func(cctx *cli.Context) error { - if cctx.NArg() != 2 { - return lcli.IncorrectNumArgs(cctx) - } - - if !cctx.Bool("really-do-it") { - fmt.Println("Pass --really-do-it to actually execute this action") - return nil - } - - api, acloser, err := lcli.GetFullNodeAPI(cctx) - if err != nil { - return err - } - defer acloser() - - ctx := lcli.ReqContext(cctx) - - na, err := address.NewFromString(cctx.Args().First()) - if err != nil { - return err - } - - newAddrId, err := api.StateLookupID(ctx, na, types.EmptyTSK) - if err != nil { - return err - } - - fa, err := address.NewFromString(cctx.Args().Get(1)) - if err != nil { - return err - } - - fromAddrId, err := api.StateLookupID(ctx, fa, types.EmptyTSK) - if err != nil { - return err - } - - maddr, err := getActorAddress(ctx, cctx) - if err != nil { - return err - } - - mi, err := api.StateMinerInfo(ctx, maddr, types.EmptyTSK) - if err != nil { - return err - } - - if fromAddrId != mi.Owner && fromAddrId != newAddrId { - return xerrors.New("from address must either be the old owner or the new owner") - } - - sp, err := actors.SerializeParams(&newAddrId) - if err != nil { - return xerrors.Errorf("serializing params: %w", err) - } - - smsg, err := api.MpoolPushMessage(ctx, &types.Message{ - From: fromAddrId, - To: maddr, - Method: builtin.MethodsMiner.ChangeOwnerAddress, - Value: big.Zero(), - Params: sp, - }, nil) - if err != nil { - return xerrors.Errorf("mpool push: %w", err) - } - - fmt.Println("Message CID:", smsg.Cid()) - - // wait for it to get mined into a block - wait, err := api.StateWaitMsg(ctx, smsg.Cid(), build.MessageConfidence) - if err != nil { - return err - } - - // check it executed successfully - if wait.Receipt.ExitCode.IsError() { - fmt.Println("owner change failed!") - return err - } - - fmt.Println("message succeeded!") - - return nil - }, -} - -var actorProposeChangeWorker = &cli.Command{ - Name: "propose-change-worker", - Usage: "Propose a worker address change", - ArgsUsage: "[address]", - Flags: []cli.Flag{ - &cli.BoolFlag{ - Name: "really-do-it", - Usage: "Actually send transaction performing the action", - Value: false, - }, - }, - Action: func(cctx *cli.Context) error { - if !cctx.Args().Present() { - return fmt.Errorf("must pass address of new worker address") - } - - minerApi, closer, err := lcli.GetStorageMinerAPI(cctx) - if err != nil { - return err - } - defer closer() - - api, acloser, err := lcli.GetFullNodeAPI(cctx) - if err != nil { - return err - } - defer acloser() - - ctx := lcli.ReqContext(cctx) - - na, err := address.NewFromString(cctx.Args().First()) - if err != nil { - return err - } - - newAddr, err := api.StateLookupID(ctx, na, types.EmptyTSK) - if err != nil { - return err - } - - maddr, err := minerApi.ActorAddress(ctx) - if err != nil { - return err - } - - mi, err := api.StateMinerInfo(ctx, maddr, types.EmptyTSK) - if err != nil { - return err - } - - if mi.NewWorker.Empty() { - if mi.Worker == newAddr { - return fmt.Errorf("worker address already set to %s", na) - } - } else { - if mi.NewWorker == newAddr { - return fmt.Errorf("change to worker address %s already pending", na) - } - } - - if !cctx.Bool("really-do-it") { - fmt.Fprintln(cctx.App.Writer, "Pass --really-do-it to actually execute this action") - return nil - } - - cwp := &miner.ChangeWorkerAddressParams{ - NewWorker: newAddr, - NewControlAddrs: mi.ControlAddresses, - } - - sp, err := actors.SerializeParams(cwp) - if err != nil { - return xerrors.Errorf("serializing params: %w", err) - } - - smsg, err := api.MpoolPushMessage(ctx, &types.Message{ - From: mi.Owner, - To: maddr, - Method: builtin.MethodsMiner.ChangeWorkerAddress, - Value: big.Zero(), - Params: sp, - }, nil) - if err != nil { - return xerrors.Errorf("mpool push: %w", err) - } - - fmt.Fprintln(cctx.App.Writer, "Propose Message CID:", smsg.Cid()) - - // wait for it to get mined into a block - wait, err := api.StateWaitMsg(ctx, smsg.Cid(), build.MessageConfidence) - if err != nil { - return err - } - - // check it executed successfully - if wait.Receipt.ExitCode.IsError() { - return fmt.Errorf("propose worker change failed") - } - - mi, err = api.StateMinerInfo(ctx, maddr, wait.TipSet) - if err != nil { - return err - } - if mi.NewWorker != newAddr { - return fmt.Errorf("Proposed worker address change not reflected on chain: expected '%s', found '%s'", na, mi.NewWorker) - } - - fmt.Fprintf(cctx.App.Writer, "Worker key change to %s successfully sent, change happens at height %d.\n", na, mi.WorkerChangeEpoch) - fmt.Fprintf(cctx.App.Writer, "If you have no active deadlines, call 'confirm-change-worker' at or after height %d to complete.\n", mi.WorkerChangeEpoch) - - return nil - }, -} - -var actorProposeChangeBeneficiary = &cli.Command{ - Name: "propose-change-beneficiary", - Usage: "Propose a beneficiary address change", - ArgsUsage: "[beneficiaryAddress quota expiration]", - Flags: []cli.Flag{ - &cli.BoolFlag{ - Name: "really-do-it", - Usage: "Actually send transaction performing the action", - Value: false, - }, - &cli.BoolFlag{ - Name: "overwrite-pending-change", - Usage: "Overwrite the current beneficiary change proposal", - Value: false, - }, - &cli.StringFlag{ - Name: "actor", - Usage: "specify the address of miner actor", - }, - }, - Action: func(cctx *cli.Context) error { - if cctx.NArg() != 3 { - return lcli.IncorrectNumArgs(cctx) - } - - api, acloser, err := lcli.GetFullNodeAPI(cctx) - if err != nil { - return xerrors.Errorf("getting fullnode api: %w", err) - } - defer acloser() - - ctx := lcli.ReqContext(cctx) - - na, err := address.NewFromString(cctx.Args().Get(0)) - if err != nil { - return xerrors.Errorf("parsing beneficiary address: %w", err) - } - - newAddr, err := api.StateLookupID(ctx, na, types.EmptyTSK) - if err != nil { - return xerrors.Errorf("looking up new beneficiary address: %w", err) - } - - quota, err := types.ParseFIL(cctx.Args().Get(1)) - if err != nil { - return xerrors.Errorf("parsing quota: %w", err) - } - - expiration, err := strconv.ParseInt(cctx.Args().Get(2), 10, 64) - if err != nil { - return xerrors.Errorf("parsing expiration: %w", err) - } - - maddr, err := getActorAddress(ctx, cctx) - if err != nil { - return xerrors.Errorf("getting miner address: %w", err) - } - - mi, err := api.StateMinerInfo(ctx, maddr, types.EmptyTSK) - if err != nil { - return xerrors.Errorf("getting miner info: %w", err) - } - - if mi.Beneficiary == mi.Owner && newAddr == mi.Owner { - return fmt.Errorf("beneficiary %s already set to owner address", mi.Beneficiary) - } - - if mi.PendingBeneficiaryTerm != nil { - fmt.Println("WARNING: replacing Pending Beneficiary Term of:") - fmt.Println("Beneficiary: ", mi.PendingBeneficiaryTerm.NewBeneficiary) - fmt.Println("Quota:", mi.PendingBeneficiaryTerm.NewQuota) - fmt.Println("Expiration Epoch:", mi.PendingBeneficiaryTerm.NewExpiration) - - if !cctx.Bool("overwrite-pending-change") { - return fmt.Errorf("must pass --overwrite-pending-change to replace current pending beneficiary change. Please review CAREFULLY") - } - } - - if !cctx.Bool("really-do-it") { - fmt.Println("Pass --really-do-it to actually execute this action. Review what you're about to approve CAREFULLY please") - return nil - } - - params := &miner.ChangeBeneficiaryParams{ - NewBeneficiary: newAddr, - NewQuota: abi.TokenAmount(quota), - NewExpiration: abi.ChainEpoch(expiration), - } - - sp, err := actors.SerializeParams(params) - if err != nil { - return xerrors.Errorf("serializing params: %w", err) - } - - smsg, err := api.MpoolPushMessage(ctx, &types.Message{ - From: mi.Owner, - To: maddr, - Method: builtin.MethodsMiner.ChangeBeneficiary, - Value: big.Zero(), - Params: sp, - }, nil) - if err != nil { - return xerrors.Errorf("mpool push: %w", err) - } - - fmt.Println("Propose Message CID:", smsg.Cid()) - - // wait for it to get mined into a block - wait, err := api.StateWaitMsg(ctx, smsg.Cid(), build.MessageConfidence) - if err != nil { - return xerrors.Errorf("waiting for message to be included in block: %w", err) - } - - // check it executed successfully - if wait.Receipt.ExitCode.IsError() { - return fmt.Errorf("propose beneficiary change failed") - } - - updatedMinerInfo, err := api.StateMinerInfo(ctx, maddr, wait.TipSet) - if err != nil { - return xerrors.Errorf("getting miner info: %w", err) - } - - if updatedMinerInfo.PendingBeneficiaryTerm == nil && updatedMinerInfo.Beneficiary == newAddr { - fmt.Println("Beneficiary address successfully changed") - } else { - fmt.Println("Beneficiary address change awaiting additional confirmations") - } - - return nil - }, -} - -var actorConfirmChangeWorker = &cli.Command{ - Name: "confirm-change-worker", - Usage: "Confirm a worker address change", - ArgsUsage: "[address]", - Flags: []cli.Flag{ - &cli.BoolFlag{ - Name: "really-do-it", - Usage: "Actually send transaction performing the action", - Value: false, - }, - }, - Action: func(cctx *cli.Context) error { - if !cctx.Args().Present() { - return fmt.Errorf("must pass address of new worker address") - } - - minerApi, closer, err := lcli.GetStorageMinerAPI(cctx) - if err != nil { - return err - } - defer closer() - - api, acloser, err := lcli.GetFullNodeAPI(cctx) - if err != nil { - return err - } - defer acloser() - - ctx := lcli.ReqContext(cctx) - - na, err := address.NewFromString(cctx.Args().First()) - if err != nil { - return err - } - - newAddr, err := api.StateLookupID(ctx, na, types.EmptyTSK) - if err != nil { - return err - } - - maddr, err := minerApi.ActorAddress(ctx) - if err != nil { - return err - } - - mi, err := api.StateMinerInfo(ctx, maddr, types.EmptyTSK) - if err != nil { - return err - } - - if mi.NewWorker.Empty() { - return xerrors.Errorf("no worker key change proposed") - } else if mi.NewWorker != newAddr { - return xerrors.Errorf("worker key %s does not match current worker key proposal %s", newAddr, mi.NewWorker) - } - - if head, err := api.ChainHead(ctx); err != nil { - return xerrors.Errorf("failed to get the chain head: %w", err) - } else if head.Height() < mi.WorkerChangeEpoch { - return xerrors.Errorf("worker key change cannot be confirmed until %d, current height is %d", mi.WorkerChangeEpoch, head.Height()) - } - - if !cctx.Bool("really-do-it") { - fmt.Println("Pass --really-do-it to actually execute this action") - return nil - } - - smsg, err := api.MpoolPushMessage(ctx, &types.Message{ - From: mi.Owner, - To: maddr, - Method: builtin.MethodsMiner.ConfirmChangeWorkerAddress, - Value: big.Zero(), - }, nil) - if err != nil { - return xerrors.Errorf("mpool push: %w", err) - } - - fmt.Println("Confirm Message CID:", smsg.Cid()) - - // wait for it to get mined into a block - wait, err := api.StateWaitMsg(ctx, smsg.Cid(), build.MessageConfidence) - if err != nil { - return err - } - - // check it executed successfully - if wait.Receipt.ExitCode.IsError() { - fmt.Fprintln(cctx.App.Writer, "Worker change failed!") - return err - } - - mi, err = api.StateMinerInfo(ctx, maddr, wait.TipSet) - if err != nil { - return err - } - if mi.Worker != newAddr { - return fmt.Errorf("Confirmed worker address change not reflected on chain: expected '%s', found '%s'", newAddr, mi.Worker) - } - - return nil - }, -} - -var actorConfirmChangeBeneficiary = &cli.Command{ - Name: "confirm-change-beneficiary", - Usage: "Confirm a beneficiary address change", - ArgsUsage: "[minerID]", - Flags: []cli.Flag{ - &cli.BoolFlag{ - Name: "really-do-it", - Usage: "Actually send transaction performing the action", - Value: false, - }, - &cli.BoolFlag{ - Name: "existing-beneficiary", - Usage: "send confirmation from the existing beneficiary address", - }, - &cli.BoolFlag{ - Name: "new-beneficiary", - Usage: "send confirmation from the new beneficiary address", - }, - }, - Action: func(cctx *cli.Context) error { - if cctx.NArg() != 1 { - return lcli.IncorrectNumArgs(cctx) - } - - api, acloser, err := lcli.GetFullNodeAPI(cctx) - if err != nil { - return xerrors.Errorf("getting fullnode api: %w", err) - } - defer acloser() - - ctx := lcli.ReqContext(cctx) - - maddr, err := address.NewFromString(cctx.Args().First()) - if err != nil { - return xerrors.Errorf("parsing beneficiary address: %w", err) - } - - mi, err := api.StateMinerInfo(ctx, maddr, types.EmptyTSK) - if err != nil { - return xerrors.Errorf("getting miner info: %w", err) - } - - if mi.PendingBeneficiaryTerm == nil { - return fmt.Errorf("no pending beneficiary term found for miner %s", maddr) - } - - if (cctx.IsSet("existing-beneficiary") && cctx.IsSet("new-beneficiary")) || (!cctx.IsSet("existing-beneficiary") && !cctx.IsSet("new-beneficiary")) { - return lcli.ShowHelp(cctx, fmt.Errorf("must pass exactly one of --existing-beneficiary or --new-beneficiary")) - } - - var fromAddr address.Address - if cctx.IsSet("existing-beneficiary") { - if mi.PendingBeneficiaryTerm.ApprovedByBeneficiary { - return fmt.Errorf("beneficiary change already approved by current beneficiary") - } - fromAddr = mi.Beneficiary - } else { - if mi.PendingBeneficiaryTerm.ApprovedByNominee { - return fmt.Errorf("beneficiary change already approved by new beneficiary") - } - fromAddr = mi.PendingBeneficiaryTerm.NewBeneficiary - } - - fmt.Println("Confirming Pending Beneficiary Term of:") - fmt.Println("Beneficiary: ", mi.PendingBeneficiaryTerm.NewBeneficiary) - fmt.Println("Quota:", mi.PendingBeneficiaryTerm.NewQuota) - fmt.Println("Expiration Epoch:", mi.PendingBeneficiaryTerm.NewExpiration) - - if !cctx.Bool("really-do-it") { - fmt.Println("Pass --really-do-it to actually execute this action. Review what you're about to approve CAREFULLY please") - return nil - } - - params := &miner.ChangeBeneficiaryParams{ - NewBeneficiary: mi.PendingBeneficiaryTerm.NewBeneficiary, - NewQuota: mi.PendingBeneficiaryTerm.NewQuota, - NewExpiration: mi.PendingBeneficiaryTerm.NewExpiration, - } - - sp, err := actors.SerializeParams(params) - if err != nil { - return xerrors.Errorf("serializing params: %w", err) - } - - smsg, err := api.MpoolPushMessage(ctx, &types.Message{ - From: fromAddr, - To: maddr, - Method: builtin.MethodsMiner.ChangeBeneficiary, - Value: big.Zero(), - Params: sp, - }, nil) - if err != nil { - return xerrors.Errorf("mpool push: %w", err) - } - - fmt.Println("Confirm Message CID:", smsg.Cid()) - - // wait for it to get mined into a block - wait, err := api.StateWaitMsg(ctx, smsg.Cid(), build.MessageConfidence) - if err != nil { - return xerrors.Errorf("waiting for message to be included in block: %w", err) - } - - // check it executed successfully - if wait.Receipt.ExitCode.IsError() { - return fmt.Errorf("confirm beneficiary change failed with code %d", wait.Receipt.ExitCode) - } - - updatedMinerInfo, err := api.StateMinerInfo(ctx, maddr, types.EmptyTSK) - if err != nil { - return err - } - - if updatedMinerInfo.PendingBeneficiaryTerm == nil && updatedMinerInfo.Beneficiary == mi.PendingBeneficiaryTerm.NewBeneficiary { - fmt.Println("Beneficiary address successfully changed") - } else { - fmt.Println("Beneficiary address change awaiting additional confirmations") - } - - return nil - }, -} - -var actorCompactAllocatedCmd = &cli.Command{ - Name: "compact-allocated", - Usage: "compact allocated sectors bitfield", - Flags: []cli.Flag{ - &cli.Uint64Flag{ - Name: "mask-last-offset", - Usage: "Mask sector IDs from 0 to 'highest_allocated - offset'", - }, - &cli.Uint64Flag{ - Name: "mask-upto-n", - Usage: "Mask sector IDs from 0 to 'n'", - }, - &cli.BoolFlag{ - Name: "really-do-it", - Usage: "Actually send transaction performing the action", - Value: false, - }, - }, - Action: func(cctx *cli.Context) error { - if !cctx.Bool("really-do-it") { - fmt.Println("Pass --really-do-it to actually execute this action") - return nil - } - - if !cctx.Args().Present() { - return fmt.Errorf("must pass address of new owner address") - } - - minerApi, closer, err := lcli.GetStorageMinerAPI(cctx) - if err != nil { - return err - } - defer closer() - - api, acloser, err := lcli.GetFullNodeAPI(cctx) - if err != nil { - return err - } - defer acloser() - - ctx := lcli.ReqContext(cctx) - - maddr, err := minerApi.ActorAddress(ctx) - if err != nil { - return err - } - - mact, err := api.StateGetActor(ctx, maddr, types.EmptyTSK) - if err != nil { - return err - } - - store := adt.WrapStore(ctx, cbor.NewCborStore(blockstore.NewAPIBlockstore(api))) - - mst, err := lminer.Load(store, mact) - if err != nil { - return err - } - - allocs, err := mst.GetAllocatedSectors() - if err != nil { - return err - } - - var maskBf bitfield.BitField - - { - exclusiveFlags := []string{"mask-last-offset", "mask-upto-n"} - hasFlag := false - for _, f := range exclusiveFlags { - if hasFlag && cctx.IsSet(f) { - return xerrors.Errorf("more than one 'mask` flag set") - } - hasFlag = hasFlag || cctx.IsSet(f) - } - } - switch { - case cctx.IsSet("mask-last-offset"): - last, err := allocs.Last() - if err != nil { - return err - } - - m := cctx.Uint64("mask-last-offset") - if last <= m+1 { - return xerrors.Errorf("highest allocated sector lower than mask offset %d: %d", m+1, last) - } - // securty to not brick a miner - if last > 1<<60 { - return xerrors.Errorf("very high last sector number, refusing to mask: %d", last) - } - - maskBf, err = bitfield.NewFromIter(&rlepluslazy.RunSliceIterator{ - Runs: []rlepluslazy.Run{{Val: true, Len: last - m}}}) - if err != nil { - return xerrors.Errorf("forming bitfield: %w", err) - } - case cctx.IsSet("mask-upto-n"): - n := cctx.Uint64("mask-upto-n") - maskBf, err = bitfield.NewFromIter(&rlepluslazy.RunSliceIterator{ - Runs: []rlepluslazy.Run{{Val: true, Len: n}}}) - if err != nil { - return xerrors.Errorf("forming bitfield: %w", err) - } - default: - return xerrors.Errorf("no 'mask' flags set") - } - - mi, err := api.StateMinerInfo(ctx, maddr, types.EmptyTSK) - if err != nil { - return err - } - - params := &miner.CompactSectorNumbersParams{ - MaskSectorNumbers: maskBf, - } - - sp, err := actors.SerializeParams(params) - if err != nil { - return xerrors.Errorf("serializing params: %w", err) - } - - smsg, err := api.MpoolPushMessage(ctx, &types.Message{ - From: mi.Worker, - To: maddr, - Method: builtin.MethodsMiner.CompactSectorNumbers, - Value: big.Zero(), - Params: sp, - }, nil) - if err != nil { - return xerrors.Errorf("mpool push: %w", err) - } - - fmt.Println("CompactSectorNumbers Message CID:", smsg.Cid()) - - // wait for it to get mined into a block - wait, err := api.StateWaitMsg(ctx, smsg.Cid(), build.MessageConfidence) - if err != nil { - return err - } - - // check it executed successfully - if wait.Receipt.ExitCode.IsError() { - fmt.Println("Propose owner change failed!") - return err - } - - return nil - }, -} - -func isController(mi api.MinerInfo, addr address.Address) bool { - if addr == mi.Owner || addr == mi.Worker { - return true - } - - for _, ca := range mi.ControlAddresses { - if addr == ca { - return true - } - } - - return false -} diff --git a/cmd/lotus-miner/actor_test.go b/cmd/lotus-miner/actor_test.go index dfb452213..5f9e923e6 100644 --- a/cmd/lotus-miner/actor_test.go +++ b/cmd/lotus-miner/actor_test.go @@ -19,6 +19,7 @@ import ( "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/chain/actors/policy" "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/cli/spcli" "github.com/filecoin-project/lotus/itests/kit" "github.com/filecoin-project/lotus/node/repo" ) @@ -67,7 +68,7 @@ func TestWorkerKeyChange(t *testing.T) { // Initialize wallet. kit.SendFunds(ctx, t, client1, newKey, abi.NewTokenAmount(0)) - require.NoError(t, run(actorProposeChangeWorker, "--really-do-it", newKey.String())) + require.NoError(t, run(spcli.ActorProposeChangeWorkerCmd(LMActorGetter), "--really-do-it", newKey.String())) result := output.String() output.Reset() @@ -82,12 +83,12 @@ func TestWorkerKeyChange(t *testing.T) { require.NotZero(t, targetEpoch) // Too early. - require.Error(t, run(actorConfirmChangeWorker, "--really-do-it", newKey.String())) + require.Error(t, run(spcli.ActorConfirmChangeWorkerCmd(LMActorGetter), "--really-do-it", newKey.String())) output.Reset() client1.WaitTillChain(ctx, kit.HeightAtLeast(abi.ChainEpoch(targetEpoch))) - require.NoError(t, run(actorConfirmChangeWorker, "--really-do-it", newKey.String())) + require.NoError(t, run(spcli.ActorConfirmChangeWorkerCmd(LMActorGetter), "--really-do-it", newKey.String())) output.Reset() head, err := client1.ChainHead(ctx) diff --git a/cmd/lotus-miner/info.go b/cmd/lotus-miner/info.go index 6d8ade340..52b230daa 100644 --- a/cmd/lotus-miner/info.go +++ b/cmd/lotus-miner/info.go @@ -29,6 +29,7 @@ import ( "github.com/filecoin-project/lotus/chain/actors/builtin/reward" "github.com/filecoin-project/lotus/chain/types" lcli "github.com/filecoin-project/lotus/cli" + "github.com/filecoin-project/lotus/cli/spcli" cliutil "github.com/filecoin-project/lotus/cli/util" "github.com/filecoin-project/lotus/journal/alerting" sealing "github.com/filecoin-project/lotus/storage/pipeline" @@ -369,94 +370,6 @@ func handleMiningInfo(ctx context.Context, cctx *cli.Context, fullapi v1api.Full return nil } -type stateMeta struct { - i int - col color.Attribute - state sealing.SectorState -} - -var stateOrder = map[sealing.SectorState]stateMeta{} -var stateList = []stateMeta{ - {col: 39, state: "Total"}, - {col: color.FgGreen, state: sealing.Proving}, - {col: color.FgGreen, state: sealing.Available}, - {col: color.FgGreen, state: sealing.UpdateActivating}, - - {col: color.FgMagenta, state: sealing.ReceiveSector}, - - {col: color.FgBlue, state: sealing.Empty}, - {col: color.FgBlue, state: sealing.WaitDeals}, - {col: color.FgBlue, state: sealing.AddPiece}, - {col: color.FgBlue, state: sealing.SnapDealsWaitDeals}, - {col: color.FgBlue, state: sealing.SnapDealsAddPiece}, - - {col: color.FgRed, state: sealing.UndefinedSectorState}, - {col: color.FgYellow, state: sealing.Packing}, - {col: color.FgYellow, state: sealing.GetTicket}, - {col: color.FgYellow, state: sealing.PreCommit1}, - {col: color.FgYellow, state: sealing.PreCommit2}, - {col: color.FgYellow, state: sealing.PreCommitting}, - {col: color.FgYellow, state: sealing.PreCommitWait}, - {col: color.FgYellow, state: sealing.SubmitPreCommitBatch}, - {col: color.FgYellow, state: sealing.PreCommitBatchWait}, - {col: color.FgYellow, state: sealing.WaitSeed}, - {col: color.FgYellow, state: sealing.Committing}, - {col: color.FgYellow, state: sealing.CommitFinalize}, - {col: color.FgYellow, state: sealing.SubmitCommit}, - {col: color.FgYellow, state: sealing.CommitWait}, - {col: color.FgYellow, state: sealing.SubmitCommitAggregate}, - {col: color.FgYellow, state: sealing.CommitAggregateWait}, - {col: color.FgYellow, state: sealing.FinalizeSector}, - {col: color.FgYellow, state: sealing.SnapDealsPacking}, - {col: color.FgYellow, state: sealing.UpdateReplica}, - {col: color.FgYellow, state: sealing.ProveReplicaUpdate}, - {col: color.FgYellow, state: sealing.SubmitReplicaUpdate}, - {col: color.FgYellow, state: sealing.ReplicaUpdateWait}, - {col: color.FgYellow, state: sealing.WaitMutable}, - {col: color.FgYellow, state: sealing.FinalizeReplicaUpdate}, - {col: color.FgYellow, state: sealing.ReleaseSectorKey}, - - {col: color.FgCyan, state: sealing.Terminating}, - {col: color.FgCyan, state: sealing.TerminateWait}, - {col: color.FgCyan, state: sealing.TerminateFinality}, - {col: color.FgCyan, state: sealing.TerminateFailed}, - {col: color.FgCyan, state: sealing.Removing}, - {col: color.FgCyan, state: sealing.Removed}, - {col: color.FgCyan, state: sealing.AbortUpgrade}, - - {col: color.FgRed, state: sealing.FailedUnrecoverable}, - {col: color.FgRed, state: sealing.AddPieceFailed}, - {col: color.FgRed, state: sealing.SealPreCommit1Failed}, - {col: color.FgRed, state: sealing.SealPreCommit2Failed}, - {col: color.FgRed, state: sealing.PreCommitFailed}, - {col: color.FgRed, state: sealing.ComputeProofFailed}, - {col: color.FgRed, state: sealing.RemoteCommitFailed}, - {col: color.FgRed, state: sealing.CommitFailed}, - {col: color.FgRed, state: sealing.CommitFinalizeFailed}, - {col: color.FgRed, state: sealing.PackingFailed}, - {col: color.FgRed, state: sealing.FinalizeFailed}, - {col: color.FgRed, state: sealing.Faulty}, - {col: color.FgRed, state: sealing.FaultReported}, - {col: color.FgRed, state: sealing.FaultedFinal}, - {col: color.FgRed, state: sealing.RemoveFailed}, - {col: color.FgRed, state: sealing.DealsExpired}, - {col: color.FgRed, state: sealing.RecoverDealIDs}, - {col: color.FgRed, state: sealing.SnapDealsAddPieceFailed}, - {col: color.FgRed, state: sealing.SnapDealsDealsExpired}, - {col: color.FgRed, state: sealing.ReplicaUpdateFailed}, - {col: color.FgRed, state: sealing.ReleaseSectorKeyFailed}, - {col: color.FgRed, state: sealing.FinalizeReplicaUpdateFailed}, -} - -func init() { - for i, state := range stateList { - stateOrder[state.state] = stateMeta{ - i: i, - col: state.col, - } - } -} - func sectorsInfo(ctx context.Context, mapi api.StorageMiner) error { summary, err := mapi.SectorsSummary(ctx) if err != nil { @@ -471,17 +384,17 @@ func sectorsInfo(ctx context.Context, mapi api.StorageMiner) error { } buckets["Total"] = total - var sorted []stateMeta + var sorted []spcli.StateMeta for state, i := range buckets { - sorted = append(sorted, stateMeta{i: i, state: state}) + sorted = append(sorted, spcli.StateMeta{I: i, State: state}) } sort.Slice(sorted, func(i, j int) bool { - return stateOrder[sorted[i].state].i < stateOrder[sorted[j].state].i + return spcli.StateOrder[sorted[i].State].I < spcli.StateOrder[sorted[j].State].I }) for _, s := range sorted { - _, _ = color.New(stateOrder[s.state].col).Printf("\t%s: %d\n", s.state, s.i) + _, _ = color.New(spcli.StateOrder[s.State].Col).Printf("\t%s: %d\n", s.State, s.I) } return nil diff --git a/cmd/lotus-miner/info_all.go b/cmd/lotus-miner/info_all.go index 2cf07385c..5b83467a2 100644 --- a/cmd/lotus-miner/info_all.go +++ b/cmd/lotus-miner/info_all.go @@ -8,6 +8,7 @@ import ( "github.com/urfave/cli/v2" lcli "github.com/filecoin-project/lotus/cli" + "github.com/filecoin-project/lotus/cli/spcli" ) var _test = false @@ -82,17 +83,17 @@ var infoAllCmd = &cli.Command{ } fmt.Println("\n#: Proving Info") - if err := provingInfoCmd.Action(cctx); err != nil { + if err := spcli.ProvingInfoCmd(LMActorOrEnvGetter).Action(cctx); err != nil { fmt.Println("ERROR: ", err) } fmt.Println("\n#: Proving Deadlines") - if err := provingDeadlinesCmd.Action(cctx); err != nil { + if err := spcli.ProvingDeadlinesCmd(LMActorOrEnvGetter).Action(cctx); err != nil { fmt.Println("ERROR: ", err) } fmt.Println("\n#: Proving Faults") - if err := provingFaultsCmd.Action(cctx); err != nil { + if err := spcli.ProvingFaultsCmd(LMActorOrEnvGetter).Action(cctx); err != nil { fmt.Println("ERROR: ", err) } @@ -237,7 +238,7 @@ var infoAllCmd = &cli.Command{ fmt.Printf("\n##: Sector %d Status\n", s) fs := &flag.FlagSet{} - for _, f := range sectorsStatusCmd.Flags { + for _, f := range spcli.SectorsStatusCmd(LMActorOrEnvGetter, getOnDiskInfo).Flags { if err := f.Apply(fs); err != nil { fmt.Println("ERROR: ", err) } @@ -246,7 +247,7 @@ var infoAllCmd = &cli.Command{ fmt.Println("ERROR: ", err) } - if err := sectorsStatusCmd.Action(cli.NewContext(cctx.App, fs, cctx)); err != nil { + if err := spcli.SectorsStatusCmd(LMActorOrEnvGetter, getOnDiskInfo).Action(cli.NewContext(cctx.App, fs, cctx)); err != nil { fmt.Println("ERROR: ", err) } diff --git a/cmd/lotus-miner/main.go b/cmd/lotus-miner/main.go index 911e98e26..1fc7abfa8 100644 --- a/cmd/lotus-miner/main.go +++ b/cmd/lotus-miner/main.go @@ -197,3 +197,17 @@ func setHidden(cmd *cli.Command) *cli.Command { cmd.Hidden = true return cmd } + +func LMActorOrEnvGetter(cctx *cli.Context) (address.Address, error) { + return getActorAddress(cctx.Context, cctx) +} + +func LMActorGetter(cctx *cli.Context) (address.Address, error) { + minerApi, closer, err := lcli.GetStorageMinerAPI(cctx) + if err != nil { + return address.Undef, err + } + defer closer() + + return minerApi.ActorAddress(cctx.Context) +} diff --git a/cmd/lotus-miner/precommits-info.go b/cmd/lotus-miner/precommits-info.go deleted file mode 100644 index 3f9e8c927..000000000 --- a/cmd/lotus-miner/precommits-info.go +++ /dev/null @@ -1,56 +0,0 @@ -package main - -import ( - "fmt" - "sort" - - cbor "github.com/ipfs/go-ipld-cbor" - "github.com/urfave/cli/v2" - - "github.com/filecoin-project/specs-actors/v7/actors/util/adt" - - "github.com/filecoin-project/lotus/blockstore" - "github.com/filecoin-project/lotus/chain/actors/builtin/miner" - "github.com/filecoin-project/lotus/chain/types" - lcli "github.com/filecoin-project/lotus/cli" -) - -var sectorPreCommitsCmd = &cli.Command{ - Name: "precommits", - Usage: "Print on-chain precommit info", - Action: func(cctx *cli.Context) error { - ctx := lcli.ReqContext(cctx) - mapi, closer, err := lcli.GetFullNodeAPI(cctx) - if err != nil { - return err - } - defer closer() - maddr, err := getActorAddress(ctx, cctx) - if err != nil { - return err - } - mact, err := mapi.StateGetActor(ctx, maddr, types.EmptyTSK) - if err != nil { - return err - } - store := adt.WrapStore(ctx, cbor.NewCborStore(blockstore.NewAPIBlockstore(mapi))) - mst, err := miner.Load(store, mact) - if err != nil { - return err - } - preCommitSector := make([]miner.SectorPreCommitOnChainInfo, 0) - err = mst.ForEachPrecommittedSector(func(info miner.SectorPreCommitOnChainInfo) error { - preCommitSector = append(preCommitSector, info) - return err - }) - less := func(i, j int) bool { - return preCommitSector[i].Info.SectorNumber <= preCommitSector[j].Info.SectorNumber - } - sort.Slice(preCommitSector, less) - for _, info := range preCommitSector { - fmt.Printf("%s: %s\n", info.Info.SectorNumber, info.PreCommitEpoch) - } - - return nil - }, -} diff --git a/cmd/lotus-miner/proving.go b/cmd/lotus-miner/proving.go index 575dded5a..9048da8e2 100644 --- a/cmd/lotus-miner/proving.go +++ b/cmd/lotus-miner/proving.go @@ -1,12 +1,10 @@ package main import ( - "bytes" "encoding/json" "fmt" "os" "strconv" - "strings" "sync" "text/tabwriter" "time" @@ -17,18 +15,13 @@ import ( "golang.org/x/xerrors" "github.com/filecoin-project/go-address" - "github.com/filecoin-project/go-bitfield" "github.com/filecoin-project/go-state-types/abi" - "github.com/filecoin-project/go-state-types/dline" "github.com/filecoin-project/go-state-types/proof" - "github.com/filecoin-project/lotus/blockstore" "github.com/filecoin-project/lotus/build" - "github.com/filecoin-project/lotus/chain/actors/builtin/miner" - "github.com/filecoin-project/lotus/chain/store" "github.com/filecoin-project/lotus/chain/types" lcli "github.com/filecoin-project/lotus/cli" - cliutil "github.com/filecoin-project/lotus/cli/util" + "github.com/filecoin-project/lotus/cli/spcli" "github.com/filecoin-project/lotus/storage/sealer/storiface" ) @@ -36,10 +29,10 @@ var provingCmd = &cli.Command{ Name: "proving", Usage: "View proving information", Subcommands: []*cli.Command{ - provingInfoCmd, - provingDeadlinesCmd, - provingDeadlineInfoCmd, - provingFaultsCmd, + spcli.ProvingInfoCmd(LMActorOrEnvGetter), + spcli.ProvingDeadlinesCmd(LMActorOrEnvGetter), + spcli.ProvingDeadlineInfoCmd(LMActorOrEnvGetter), + spcli.ProvingFaultsCmd(LMActorOrEnvGetter), provingCheckProvableCmd, workersCmd(false), provingComputeCmd, @@ -47,422 +40,6 @@ var provingCmd = &cli.Command{ }, } -var provingFaultsCmd = &cli.Command{ - Name: "faults", - Usage: "View the currently known proving faulty sectors information", - Action: func(cctx *cli.Context) error { - api, acloser, err := lcli.GetFullNodeAPI(cctx) - if err != nil { - return err - } - defer acloser() - - ctx := lcli.ReqContext(cctx) - - stor := store.ActorStore(ctx, blockstore.NewAPIBlockstore(api)) - - maddr, err := getActorAddress(ctx, cctx) - if err != nil { - return err - } - - mact, err := api.StateGetActor(ctx, maddr, types.EmptyTSK) - if err != nil { - return err - } - - mas, err := miner.Load(stor, mact) - if err != nil { - return err - } - - fmt.Printf("Miner: %s\n", color.BlueString("%s", maddr)) - - tw := tabwriter.NewWriter(os.Stdout, 2, 4, 2, ' ', 0) - _, _ = fmt.Fprintln(tw, "deadline\tpartition\tsectors") - err = mas.ForEachDeadline(func(dlIdx uint64, dl miner.Deadline) error { - return dl.ForEachPartition(func(partIdx uint64, part miner.Partition) error { - faults, err := part.FaultySectors() - if err != nil { - return err - } - return faults.ForEach(func(num uint64) error { - _, _ = fmt.Fprintf(tw, "%d\t%d\t%d\n", dlIdx, partIdx, num) - return nil - }) - }) - }) - if err != nil { - return err - } - return tw.Flush() - }, -} - -var provingInfoCmd = &cli.Command{ - Name: "info", - Usage: "View current state information", - Action: func(cctx *cli.Context) error { - api, acloser, err := lcli.GetFullNodeAPI(cctx) - if err != nil { - return err - } - defer acloser() - - ctx := lcli.ReqContext(cctx) - - maddr, err := getActorAddress(ctx, cctx) - if err != nil { - return err - } - - head, err := api.ChainHead(ctx) - if err != nil { - return xerrors.Errorf("getting chain head: %w", err) - } - - mact, err := api.StateGetActor(ctx, maddr, head.Key()) - if err != nil { - return err - } - - stor := store.ActorStore(ctx, blockstore.NewAPIBlockstore(api)) - - mas, err := miner.Load(stor, mact) - if err != nil { - return err - } - - cd, err := api.StateMinerProvingDeadline(ctx, maddr, head.Key()) - if err != nil { - return xerrors.Errorf("getting miner info: %w", err) - } - - fmt.Printf("Miner: %s\n", color.BlueString("%s", maddr)) - - proving := uint64(0) - faults := uint64(0) - recovering := uint64(0) - curDeadlineSectors := uint64(0) - - if err := mas.ForEachDeadline(func(dlIdx uint64, dl miner.Deadline) error { - return dl.ForEachPartition(func(partIdx uint64, part miner.Partition) error { - if bf, err := part.LiveSectors(); err != nil { - return err - } else if count, err := bf.Count(); err != nil { - return err - } else { - proving += count - if dlIdx == cd.Index { - curDeadlineSectors += count - } - } - - if bf, err := part.FaultySectors(); err != nil { - return err - } else if count, err := bf.Count(); err != nil { - return err - } else { - faults += count - } - - if bf, err := part.RecoveringSectors(); err != nil { - return err - } else if count, err := bf.Count(); err != nil { - return err - } else { - recovering += count - } - - return nil - }) - }); err != nil { - return xerrors.Errorf("walking miner deadlines and partitions: %w", err) - } - - var faultPerc float64 - if proving > 0 { - faultPerc = float64(faults * 100 / proving) - } - - fmt.Printf("Current Epoch: %d\n", cd.CurrentEpoch) - - fmt.Printf("Proving Period Boundary: %d\n", cd.PeriodStart%cd.WPoStProvingPeriod) - fmt.Printf("Proving Period Start: %s\n", cliutil.EpochTimeTs(cd.CurrentEpoch, cd.PeriodStart, head)) - fmt.Printf("Next Period Start: %s\n\n", cliutil.EpochTimeTs(cd.CurrentEpoch, cd.PeriodStart+cd.WPoStProvingPeriod, head)) - - fmt.Printf("Faults: %d (%.2f%%)\n", faults, faultPerc) - fmt.Printf("Recovering: %d\n", recovering) - - fmt.Printf("Deadline Index: %d\n", cd.Index) - fmt.Printf("Deadline Sectors: %d\n", curDeadlineSectors) - fmt.Printf("Deadline Open: %s\n", cliutil.EpochTime(cd.CurrentEpoch, cd.Open)) - fmt.Printf("Deadline Close: %s\n", cliutil.EpochTime(cd.CurrentEpoch, cd.Close)) - fmt.Printf("Deadline Challenge: %s\n", cliutil.EpochTime(cd.CurrentEpoch, cd.Challenge)) - fmt.Printf("Deadline FaultCutoff: %s\n", cliutil.EpochTime(cd.CurrentEpoch, cd.FaultCutoff)) - return nil - }, -} - -var provingDeadlinesCmd = &cli.Command{ - Name: "deadlines", - Usage: "View the current proving period deadlines information", - Flags: []cli.Flag{ - &cli.BoolFlag{ - Name: "all", - Usage: "Count all sectors (only live sectors are counted by default)", - Aliases: []string{"a"}, - }, - }, - Action: func(cctx *cli.Context) error { - api, acloser, err := lcli.GetFullNodeAPI(cctx) - if err != nil { - return err - } - defer acloser() - - ctx := lcli.ReqContext(cctx) - - maddr, err := getActorAddress(ctx, cctx) - if err != nil { - return err - } - - deadlines, err := api.StateMinerDeadlines(ctx, maddr, types.EmptyTSK) - if err != nil { - return xerrors.Errorf("getting deadlines: %w", err) - } - - di, err := api.StateMinerProvingDeadline(ctx, maddr, types.EmptyTSK) - if err != nil { - return xerrors.Errorf("getting deadlines: %w", err) - } - - head, err := api.ChainHead(ctx) - if err != nil { - return err - } - - fmt.Printf("Miner: %s\n", color.BlueString("%s", maddr)) - - tw := tabwriter.NewWriter(os.Stdout, 2, 4, 2, ' ', 0) - _, _ = fmt.Fprintln(tw, "deadline\topen\tpartitions\tsectors (faults)\tproven partitions") - - for dlIdx, deadline := range deadlines { - partitions, err := api.StateMinerPartitions(ctx, maddr, uint64(dlIdx), types.EmptyTSK) - if err != nil { - return xerrors.Errorf("getting partitions for deadline %d: %w", dlIdx, err) - } - - provenPartitions, err := deadline.PostSubmissions.Count() - if err != nil { - return err - } - - sectors := uint64(0) - faults := uint64(0) - var partitionCount int - - for _, partition := range partitions { - if !cctx.Bool("all") { - sc, err := partition.LiveSectors.Count() - if err != nil { - return err - } - - if sc > 0 { - partitionCount++ - } - - sectors += sc - } else { - sc, err := partition.AllSectors.Count() - if err != nil { - return err - } - - partitionCount++ - sectors += sc - } - - fc, err := partition.FaultySectors.Count() - if err != nil { - return err - } - - faults += fc - } - - var cur string - if di.Index == uint64(dlIdx) { - cur += "\t(current)" - } - - _, _ = fmt.Fprintf(tw, "%d\t%s\t%d\t%d (%d)\t%d%s\n", dlIdx, deadlineOpenTime(head, uint64(dlIdx), di), - partitionCount, sectors, faults, provenPartitions, cur) - } - - return tw.Flush() - }, -} - -func deadlineOpenTime(ts *types.TipSet, dlIdx uint64, di *dline.Info) string { - gapIdx := dlIdx - di.Index - gapHeight := uint64(di.WPoStProvingPeriod) / di.WPoStPeriodDeadlines * gapIdx - - openHeight := di.Open + abi.ChainEpoch(gapHeight) - genesisBlockTimestamp := ts.MinTimestamp() - uint64(ts.Height())*build.BlockDelaySecs - - return time.Unix(int64(genesisBlockTimestamp+build.BlockDelaySecs*uint64(openHeight)), 0).Format(time.TimeOnly) -} - -var provingDeadlineInfoCmd = &cli.Command{ - Name: "deadline", - Usage: "View the current proving period deadline information by its index", - Flags: []cli.Flag{ - &cli.BoolFlag{ - Name: "sector-nums", - Aliases: []string{"n"}, - Usage: "Print sector/fault numbers belonging to this deadline", - }, - &cli.BoolFlag{ - Name: "bitfield", - Aliases: []string{"b"}, - Usage: "Print partition bitfield stats", - }, - }, - ArgsUsage: "", - Action: func(cctx *cli.Context) error { - - if cctx.NArg() != 1 { - return lcli.IncorrectNumArgs(cctx) - } - - dlIdx, err := strconv.ParseUint(cctx.Args().Get(0), 10, 64) - if err != nil { - return xerrors.Errorf("could not parse deadline index: %w", err) - } - - api, acloser, err := lcli.GetFullNodeAPI(cctx) - if err != nil { - return err - } - defer acloser() - - ctx := lcli.ReqContext(cctx) - - maddr, err := getActorAddress(ctx, cctx) - if err != nil { - return err - } - - deadlines, err := api.StateMinerDeadlines(ctx, maddr, types.EmptyTSK) - if err != nil { - return xerrors.Errorf("getting deadlines: %w", err) - } - - di, err := api.StateMinerProvingDeadline(ctx, maddr, types.EmptyTSK) - if err != nil { - return xerrors.Errorf("getting deadlines: %w", err) - } - - partitions, err := api.StateMinerPartitions(ctx, maddr, dlIdx, types.EmptyTSK) - if err != nil { - return xerrors.Errorf("getting partitions for deadline %d: %w", dlIdx, err) - } - - head, err := api.ChainHead(ctx) - if err != nil { - return err - } - - provenPartitions, err := deadlines[dlIdx].PostSubmissions.Count() - if err != nil { - return err - } - - fmt.Printf("Deadline Index: %d\n", dlIdx) - fmt.Printf("Deadline Open: %s\n", deadlineOpenTime(head, dlIdx, di)) - fmt.Printf("Partitions: %d\n", len(partitions)) - fmt.Printf("Proven Partitions: %d\n", provenPartitions) - fmt.Printf("Current: %t\n\n", di.Index == dlIdx) - - for pIdx, partition := range partitions { - fmt.Printf("Partition Index: %d\n", pIdx) - - printStats := func(bf bitfield.BitField, name string) error { - count, err := bf.Count() - if err != nil { - return err - } - - rit, err := bf.RunIterator() - if err != nil { - return err - } - - if cctx.Bool("bitfield") { - var ones, zeros, oneRuns, zeroRuns, invalid uint64 - for rit.HasNext() { - r, err := rit.NextRun() - if err != nil { - return xerrors.Errorf("next run: %w", err) - } - if !r.Valid() { - invalid++ - } - if r.Val { - ones += r.Len - oneRuns++ - } else { - zeros += r.Len - zeroRuns++ - } - } - - var buf bytes.Buffer - if err := bf.MarshalCBOR(&buf); err != nil { - return err - } - sz := len(buf.Bytes()) - szstr := types.SizeStr(types.NewInt(uint64(sz))) - - fmt.Printf("\t%s Sectors:%s%d (bitfield - runs %d+%d=%d - %d 0s %d 1s - %d inv - %s %dB)\n", name, strings.Repeat(" ", 18-len(name)), count, zeroRuns, oneRuns, zeroRuns+oneRuns, zeros, ones, invalid, szstr, sz) - } else { - fmt.Printf("\t%s Sectors:%s%d\n", name, strings.Repeat(" ", 18-len(name)), count) - } - - if cctx.Bool("sector-nums") { - nums, err := bf.All(count) - if err != nil { - return err - } - fmt.Printf("\t%s Sector Numbers:%s%v\n", name, strings.Repeat(" ", 12-len(name)), nums) - } - - return nil - } - - if err := printStats(partition.AllSectors, "All"); err != nil { - return err - } - if err := printStats(partition.LiveSectors, "Live"); err != nil { - return err - } - if err := printStats(partition.ActiveSectors, "Active"); err != nil { - return err - } - if err := printStats(partition.FaultySectors, "Faulty"); err != nil { - return err - } - if err := printStats(partition.RecoveringSectors, "Recovering"); err != nil { - return err - } - } - return nil - }, -} - var provingCheckProvableCmd = &cli.Command{ Name: "check", Usage: "Check sectors provable", diff --git a/cmd/lotus-miner/sectors.go b/cmd/lotus-miner/sectors.go index 3e4439eb8..a3ffb8335 100644 --- a/cmd/lotus-miner/sectors.go +++ b/cmd/lotus-miner/sectors.go @@ -3,10 +3,7 @@ package main import ( "bufio" "encoding/csv" - "encoding/json" - "errors" "fmt" - "math" "os" "sort" "strconv" @@ -23,18 +20,16 @@ import ( "github.com/filecoin-project/go-bitfield" "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/big" - "github.com/filecoin-project/go-state-types/builtin" "github.com/filecoin-project/go-state-types/network" "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/blockstore" - "github.com/filecoin-project/lotus/chain/actors" "github.com/filecoin-project/lotus/chain/actors/adt" "github.com/filecoin-project/lotus/chain/actors/builtin/miner" - "github.com/filecoin-project/lotus/chain/actors/builtin/verifreg" "github.com/filecoin-project/lotus/chain/actors/policy" "github.com/filecoin-project/lotus/chain/types" lcli "github.com/filecoin-project/lotus/cli" + "github.com/filecoin-project/lotus/cli/spcli" cliutil "github.com/filecoin-project/lotus/cli/util" "github.com/filecoin-project/lotus/lib/result" "github.com/filecoin-project/lotus/lib/strle" @@ -48,16 +43,16 @@ var sectorsCmd = &cli.Command{ Name: "sectors", Usage: "interact with sector store", Subcommands: []*cli.Command{ - sectorsStatusCmd, + spcli.SectorsStatusCmd(LMActorOrEnvGetter, getOnDiskInfo), sectorsListCmd, sectorsRefsCmd, sectorsUpdateCmd, sectorsPledgeCmd, sectorsNumbersCmd, - sectorPreCommitsCmd, - sectorsCheckExpireCmd, + spcli.SectorPreCommitsCmd(LMActorOrEnvGetter), + spcli.SectorsCheckExpireCmd(LMActorOrEnvGetter), sectorsExpiredCmd, - sectorsExtendCmd, + spcli.SectorsExtendCmd(LMActorOrEnvGetter), sectorsTerminateCmd, sectorsRemoveCmd, sectorsSnapUpCmd, @@ -67,11 +62,20 @@ var sectorsCmd = &cli.Command{ sectorsCapacityCollateralCmd, sectorsBatching, sectorsRefreshPieceMatchingCmd, - sectorsCompactPartitionsCmd, + spcli.SectorsCompactPartitionsCmd(LMActorOrEnvGetter), sectorsUnsealCmd, }, } +func getOnDiskInfo(cctx *cli.Context, id abi.SectorNumber, onChainInfo bool) (api.SectorInfo, error) { + minerApi, closer, err := lcli.GetStorageMinerAPI(cctx) + if err != nil { + return api.SectorInfo{}, err + } + defer closer() + return minerApi.SectorsStatus(cctx.Context, id, onChainInfo) +} + var sectorsPledgeCmd = &cli.Command{ Name: "pledge", Usage: "store random data in a sector", @@ -94,187 +98,6 @@ var sectorsPledgeCmd = &cli.Command{ }, } -var sectorsStatusCmd = &cli.Command{ - Name: "status", - Usage: "Get the seal status of a sector by its number", - ArgsUsage: "", - Flags: []cli.Flag{ - &cli.BoolFlag{ - Name: "log", - Usage: "display event log", - Aliases: []string{"l"}, - }, - &cli.BoolFlag{ - Name: "on-chain-info", - Usage: "show sector on chain info", - Aliases: []string{"c"}, - }, - &cli.BoolFlag{ - Name: "partition-info", - Usage: "show partition related info", - Aliases: []string{"p"}, - }, - &cli.BoolFlag{ - Name: "proof", - Usage: "print snark proof bytes as hex", - }, - }, - Action: func(cctx *cli.Context) error { - minerApi, closer, err := lcli.GetStorageMinerAPI(cctx) - if err != nil { - return err - } - defer closer() - ctx := lcli.ReqContext(cctx) - - if cctx.NArg() != 1 { - return lcli.IncorrectNumArgs(cctx) - } - - id, err := strconv.ParseUint(cctx.Args().First(), 10, 64) - if err != nil { - return err - } - - onChainInfo := cctx.Bool("on-chain-info") - status, err := minerApi.SectorsStatus(ctx, abi.SectorNumber(id), onChainInfo) - if err != nil { - return err - } - - fmt.Printf("SectorID:\t%d\n", status.SectorID) - fmt.Printf("Status:\t\t%s\n", status.State) - fmt.Printf("CIDcommD:\t%s\n", status.CommD) - fmt.Printf("CIDcommR:\t%s\n", status.CommR) - fmt.Printf("Ticket:\t\t%x\n", status.Ticket.Value) - fmt.Printf("TicketH:\t%d\n", status.Ticket.Epoch) - fmt.Printf("Seed:\t\t%x\n", status.Seed.Value) - fmt.Printf("SeedH:\t\t%d\n", status.Seed.Epoch) - fmt.Printf("Precommit:\t%s\n", status.PreCommitMsg) - fmt.Printf("Commit:\t\t%s\n", status.CommitMsg) - if cctx.Bool("proof") { - fmt.Printf("Proof:\t\t%x\n", status.Proof) - } - fmt.Printf("Deals:\t\t%v\n", status.Deals) - fmt.Printf("Retries:\t%d\n", status.Retries) - if status.LastErr != "" { - fmt.Printf("Last Error:\t\t%s\n", status.LastErr) - } - - if onChainInfo { - fmt.Printf("\nSector On Chain Info\n") - fmt.Printf("SealProof:\t\t%x\n", status.SealProof) - fmt.Printf("Activation:\t\t%v\n", status.Activation) - fmt.Printf("Expiration:\t\t%v\n", status.Expiration) - fmt.Printf("DealWeight:\t\t%v\n", status.DealWeight) - fmt.Printf("VerifiedDealWeight:\t\t%v\n", status.VerifiedDealWeight) - fmt.Printf("InitialPledge:\t\t%v\n", types.FIL(status.InitialPledge)) - fmt.Printf("\nExpiration Info\n") - fmt.Printf("OnTime:\t\t%v\n", status.OnTime) - fmt.Printf("Early:\t\t%v\n", status.Early) - } - - if cctx.Bool("partition-info") { - fullApi, nCloser, err := lcli.GetFullNodeAPI(cctx) - if err != nil { - return err - } - defer nCloser() - - maddr, err := getActorAddress(ctx, cctx) - if err != nil { - return err - } - - mact, err := fullApi.StateGetActor(ctx, maddr, types.EmptyTSK) - if err != nil { - return err - } - - tbs := blockstore.NewTieredBstore(blockstore.NewAPIBlockstore(fullApi), blockstore.NewMemory()) - mas, err := miner.Load(adt.WrapStore(ctx, cbor.NewCborStore(tbs)), mact) - if err != nil { - return err - } - - errFound := errors.New("found") - if err := mas.ForEachDeadline(func(dlIdx uint64, dl miner.Deadline) error { - return dl.ForEachPartition(func(partIdx uint64, part miner.Partition) error { - pas, err := part.AllSectors() - if err != nil { - return err - } - - set, err := pas.IsSet(id) - if err != nil { - return err - } - if set { - fmt.Printf("\nDeadline:\t%d\n", dlIdx) - fmt.Printf("Partition:\t%d\n", partIdx) - - checkIn := func(name string, bg func() (bitfield.BitField, error)) error { - bf, err := bg() - if err != nil { - return err - } - - set, err := bf.IsSet(id) - if err != nil { - return err - } - setstr := "no" - if set { - setstr = "yes" - } - fmt.Printf("%s: \t%s\n", name, setstr) - return nil - } - - if err := checkIn("Unproven", part.UnprovenSectors); err != nil { - return err - } - if err := checkIn("Live", part.LiveSectors); err != nil { - return err - } - if err := checkIn("Active", part.ActiveSectors); err != nil { - return err - } - if err := checkIn("Faulty", part.FaultySectors); err != nil { - return err - } - if err := checkIn("Recovering", part.RecoveringSectors); err != nil { - return err - } - - return errFound - } - - return nil - }) - }); err != errFound { - if err != nil { - return err - } - - fmt.Println("\nNot found in any partition") - } - } - - if cctx.Bool("log") { - fmt.Printf("--------\nEvent Log:\n") - - for i, l := range status.Log { - fmt.Printf("%d.\t%s:\t[%s]\t%s\n", i, time.Unix(int64(l.Timestamp), 0), l.Kind, l.Message) - if l.Trace != "" { - fmt.Printf("\t%s\n", l.Trace) - } - } - } - return nil - }, -} - var sectorsListCmd = &cli.Command{ Name: "list", Usage: "List sectors", @@ -494,7 +317,7 @@ var sectorsListCmd = &cli.Command{ m := map[string]interface{}{ "ID": s, - "State": color.New(stateOrder[sealing.SectorState(st.State)].col).Sprint(st.State), + "State": color.New(spcli.StateOrder[sealing.SectorState(st.State)].Col).Sprint(st.State), "OnChain": yesno(inSSet), "Active": yesno(inASet), } @@ -778,654 +601,6 @@ var sectorsRefsCmd = &cli.Command{ }, } -var sectorsCheckExpireCmd = &cli.Command{ - Name: "check-expire", - Usage: "Inspect expiring sectors", - Flags: []cli.Flag{ - &cli.Int64Flag{ - Name: "cutoff", - Usage: "skip sectors whose current expiration is more than epochs from now, defaults to 60 days", - Value: 172800, - }, - }, - Action: func(cctx *cli.Context) error { - - fullApi, nCloser, err := lcli.GetFullNodeAPI(cctx) - if err != nil { - return err - } - defer nCloser() - - ctx := lcli.ReqContext(cctx) - - maddr, err := getActorAddress(ctx, cctx) - if err != nil { - return err - } - - head, err := fullApi.ChainHead(ctx) - if err != nil { - return err - } - currEpoch := head.Height() - - nv, err := fullApi.StateNetworkVersion(ctx, types.EmptyTSK) - if err != nil { - return err - } - - sectors, err := fullApi.StateMinerActiveSectors(ctx, maddr, types.EmptyTSK) - if err != nil { - return err - } - - n := 0 - for _, s := range sectors { - if s.Expiration-currEpoch <= abi.ChainEpoch(cctx.Int64("cutoff")) { - sectors[n] = s - n++ - } - } - sectors = sectors[:n] - - sort.Slice(sectors, func(i, j int) bool { - if sectors[i].Expiration == sectors[j].Expiration { - return sectors[i].SectorNumber < sectors[j].SectorNumber - } - return sectors[i].Expiration < sectors[j].Expiration - }) - - tw := tablewriter.New( - tablewriter.Col("ID"), - tablewriter.Col("SealProof"), - tablewriter.Col("InitialPledge"), - tablewriter.Col("Activation"), - tablewriter.Col("Expiration"), - tablewriter.Col("MaxExpiration"), - tablewriter.Col("MaxExtendNow")) - - for _, sector := range sectors { - MaxExpiration := sector.Activation + policy.GetSectorMaxLifetime(sector.SealProof, nv) - maxExtension, err := policy.GetMaxSectorExpirationExtension(nv) - if err != nil { - return xerrors.Errorf("failed to get max extension: %w", err) - } - - MaxExtendNow := currEpoch + maxExtension - - if MaxExtendNow > MaxExpiration { - MaxExtendNow = MaxExpiration - } - - tw.Write(map[string]interface{}{ - "ID": sector.SectorNumber, - "SealProof": sector.SealProof, - "InitialPledge": types.FIL(sector.InitialPledge).Short(), - "Activation": cliutil.EpochTime(currEpoch, sector.Activation), - "Expiration": cliutil.EpochTime(currEpoch, sector.Expiration), - "MaxExpiration": cliutil.EpochTime(currEpoch, MaxExpiration), - "MaxExtendNow": cliutil.EpochTime(currEpoch, MaxExtendNow), - }) - } - - return tw.Flush(os.Stdout) - }, -} - -type PseudoExpirationExtension struct { - Deadline uint64 - Partition uint64 - Sectors string - NewExpiration abi.ChainEpoch -} - -type PseudoExtendSectorExpirationParams struct { - Extensions []PseudoExpirationExtension -} - -func NewPseudoExtendParams(p *miner.ExtendSectorExpiration2Params) (*PseudoExtendSectorExpirationParams, error) { - res := PseudoExtendSectorExpirationParams{} - for _, ext := range p.Extensions { - scount, err := ext.Sectors.Count() - if err != nil { - return nil, err - } - - sectors, err := ext.Sectors.All(scount) - if err != nil { - return nil, err - } - - res.Extensions = append(res.Extensions, PseudoExpirationExtension{ - Deadline: ext.Deadline, - Partition: ext.Partition, - Sectors: ArrayToString(sectors), - NewExpiration: ext.NewExpiration, - }) - } - return &res, nil -} - -// ArrayToString Example: {1,3,4,5,8,9} -> "1,3-5,8-9" -func ArrayToString(array []uint64) string { - sort.Slice(array, func(i, j int) bool { - return array[i] < array[j] - }) - - var sarray []string - s := "" - - for i, elm := range array { - if i == 0 { - s = strconv.FormatUint(elm, 10) - continue - } - if elm == array[i-1] { - continue // filter out duplicates - } else if elm == array[i-1]+1 { - s = strings.Split(s, "-")[0] + "-" + strconv.FormatUint(elm, 10) - } else { - sarray = append(sarray, s) - s = strconv.FormatUint(elm, 10) - } - } - - if s != "" { - sarray = append(sarray, s) - } - - return strings.Join(sarray, ",") -} - -func getSectorsFromFile(filePath string) ([]abi.SectorNumber, error) { - file, err := os.Open(filePath) - if err != nil { - return nil, err - } - - scanner := bufio.NewScanner(file) - sectors := make([]abi.SectorNumber, 0) - - for scanner.Scan() { - line := scanner.Text() - - id, err := strconv.ParseUint(line, 10, 64) - if err != nil { - return nil, xerrors.Errorf("could not parse %s as sector id: %s", line, err) - } - - sectors = append(sectors, abi.SectorNumber(id)) - } - - if err = file.Close(); err != nil { - return nil, err - } - - return sectors, nil -} - -func SectorNumsToBitfield(sectors []abi.SectorNumber) bitfield.BitField { - var numbers []uint64 - for _, sector := range sectors { - numbers = append(numbers, uint64(sector)) - } - - return bitfield.NewFromSet(numbers) -} - -var sectorsExtendCmd = &cli.Command{ - Name: "extend", - Usage: "Extend expiring sectors while not exceeding each sector's max life", - ArgsUsage: "", - Flags: []cli.Flag{ - &cli.Int64Flag{ - Name: "from", - Usage: "only consider sectors whose current expiration epoch is in the range of [from, to], defaults to: now + 120 (1 hour)", - }, - &cli.Int64Flag{ - Name: "to", - Usage: "only consider sectors whose current expiration epoch is in the range of [from, to], defaults to: now + 92160 (32 days)", - }, - &cli.StringFlag{ - Name: "sector-file", - Usage: "provide a file containing one sector number in each line, ignoring above selecting criteria", - }, - &cli.StringFlag{ - Name: "exclude", - Usage: "optionally provide a file containing excluding sectors", - }, - &cli.Int64Flag{ - Name: "extension", - Usage: "try to extend selected sectors by this number of epochs, defaults to 540 days", - Value: 1555200, - }, - &cli.Int64Flag{ - Name: "new-expiration", - Usage: "try to extend selected sectors to this epoch, ignoring extension", - }, - &cli.BoolFlag{ - Name: "only-cc", - Usage: "only extend CC sectors (useful for making sector ready for snap upgrade)", - }, - &cli.BoolFlag{ - Name: "drop-claims", - Usage: "drop claims for sectors that can be extended, but only by dropping some of their verified power claims", - }, - &cli.Int64Flag{ - Name: "tolerance", - Usage: "don't try to extend sectors by fewer than this number of epochs, defaults to 7 days", - Value: 20160, - }, - &cli.StringFlag{ - Name: "max-fee", - Usage: "use up to this amount of FIL for one message. pass this flag to avoid message congestion.", - Value: "0", - }, - &cli.Int64Flag{ - Name: "max-sectors", - Usage: "the maximum number of sectors contained in each message", - }, - &cli.BoolFlag{ - Name: "really-do-it", - Usage: "pass this flag to really extend sectors, otherwise will only print out json representation of parameters", - }, - }, - Action: func(cctx *cli.Context) error { - mf, err := types.ParseFIL(cctx.String("max-fee")) - if err != nil { - return err - } - - spec := &api.MessageSendSpec{MaxFee: abi.TokenAmount(mf)} - - fullApi, nCloser, err := lcli.GetFullNodeAPI(cctx) - if err != nil { - return err - } - defer nCloser() - - ctx := lcli.ReqContext(cctx) - - maddr, err := getActorAddress(ctx, cctx) - if err != nil { - return err - } - - head, err := fullApi.ChainHead(ctx) - if err != nil { - return err - } - currEpoch := head.Height() - - nv, err := fullApi.StateNetworkVersion(ctx, types.EmptyTSK) - if err != nil { - return err - } - - activeSet, err := fullApi.StateMinerActiveSectors(ctx, maddr, types.EmptyTSK) - if err != nil { - return err - } - - activeSectorsInfo := make(map[abi.SectorNumber]*miner.SectorOnChainInfo, len(activeSet)) - for _, info := range activeSet { - activeSectorsInfo[info.SectorNumber] = info - } - - mact, err := fullApi.StateGetActor(ctx, maddr, types.EmptyTSK) - if err != nil { - return err - } - - tbs := blockstore.NewTieredBstore(blockstore.NewAPIBlockstore(fullApi), blockstore.NewMemory()) - adtStore := adt.WrapStore(ctx, cbor.NewCborStore(tbs)) - mas, err := miner.Load(adtStore, mact) - if err != nil { - return err - } - - activeSectorsLocation := make(map[abi.SectorNumber]*miner.SectorLocation, len(activeSet)) - - if err := mas.ForEachDeadline(func(dlIdx uint64, dl miner.Deadline) error { - return dl.ForEachPartition(func(partIdx uint64, part miner.Partition) error { - pas, err := part.ActiveSectors() - if err != nil { - return err - } - - return pas.ForEach(func(i uint64) error { - activeSectorsLocation[abi.SectorNumber(i)] = &miner.SectorLocation{ - Deadline: dlIdx, - Partition: partIdx, - } - return nil - }) - }) - }); err != nil { - return err - } - - excludeSet := make(map[abi.SectorNumber]struct{}) - if cctx.IsSet("exclude") { - excludeSectors, err := getSectorsFromFile(cctx.String("exclude")) - if err != nil { - return err - } - - for _, id := range excludeSectors { - excludeSet[id] = struct{}{} - } - } - - var sectors []abi.SectorNumber - if cctx.Args().Present() { - if cctx.IsSet("sector-file") { - return xerrors.Errorf("sector-file specified along with command line params") - } - - for i, s := range cctx.Args().Slice() { - id, err := strconv.ParseUint(s, 10, 64) - if err != nil { - return xerrors.Errorf("could not parse sector %d: %w", i, err) - } - - sectors = append(sectors, abi.SectorNumber(id)) - } - } else if cctx.IsSet("sector-file") { - sectors, err = getSectorsFromFile(cctx.String("sector-file")) - if err != nil { - return err - } - } else { - from := currEpoch + 120 - to := currEpoch + 92160 - - if cctx.IsSet("from") { - from = abi.ChainEpoch(cctx.Int64("from")) - } - - if cctx.IsSet("to") { - to = abi.ChainEpoch(cctx.Int64("to")) - } - - for _, si := range activeSet { - if si.Expiration >= from && si.Expiration <= to { - sectors = append(sectors, si.SectorNumber) - } - } - } - - var sis []*miner.SectorOnChainInfo - for _, id := range sectors { - if _, exclude := excludeSet[id]; exclude { - continue - } - - si, found := activeSectorsInfo[id] - if !found { - return xerrors.Errorf("sector %d is not active", id) - } - if len(si.DealIDs) > 0 && cctx.Bool("only-cc") { - continue - } - - sis = append(sis, si) - } - - withinTolerance := func(a, b abi.ChainEpoch) bool { - diff := a - b - if diff < 0 { - diff = -diff - } - - return diff <= abi.ChainEpoch(cctx.Int64("tolerance")) - } - - extensions := map[miner.SectorLocation]map[abi.ChainEpoch][]abi.SectorNumber{} - for _, si := range sis { - extension := abi.ChainEpoch(cctx.Int64("extension")) - newExp := si.Expiration + extension - - if cctx.IsSet("new-expiration") { - newExp = abi.ChainEpoch(cctx.Int64("new-expiration")) - } - - maxExtension, err := policy.GetMaxSectorExpirationExtension(nv) - if err != nil { - return xerrors.Errorf("failed to get max extension: %w", err) - } - - maxExtendNow := currEpoch + maxExtension - if newExp > maxExtendNow { - newExp = maxExtendNow - } - - maxExp := si.Activation + policy.GetSectorMaxLifetime(si.SealProof, nv) - if newExp > maxExp { - newExp = maxExp - } - - if newExp <= si.Expiration || withinTolerance(newExp, si.Expiration) { - continue - } - - l, found := activeSectorsLocation[si.SectorNumber] - if !found { - return xerrors.Errorf("location for sector %d not found", si.SectorNumber) - } - - es, found := extensions[*l] - if !found { - ne := make(map[abi.ChainEpoch][]abi.SectorNumber) - ne[newExp] = []abi.SectorNumber{si.SectorNumber} - extensions[*l] = ne - } else { - added := false - for exp := range es { - if withinTolerance(newExp, exp) { - es[exp] = append(es[exp], si.SectorNumber) - added = true - break - } - } - - if !added { - es[newExp] = []abi.SectorNumber{si.SectorNumber} - } - } - } - - verifregAct, err := fullApi.StateGetActor(ctx, builtin.VerifiedRegistryActorAddr, types.EmptyTSK) - if err != nil { - return xerrors.Errorf("failed to lookup verifreg actor: %w", err) - } - - verifregSt, err := verifreg.Load(adtStore, verifregAct) - if err != nil { - return xerrors.Errorf("failed to load verifreg state: %w", err) - } - - claimsMap, err := verifregSt.GetClaims(maddr) - if err != nil { - return xerrors.Errorf("failed to lookup claims for miner: %w", err) - } - - claimIdsBySector, err := verifregSt.GetClaimIdsBySector(maddr) - if err != nil { - return xerrors.Errorf("failed to lookup claim IDs by sector: %w", err) - } - - sectorsMax, err := policy.GetAddressedSectorsMax(nv) - if err != nil { - return err - } - - declMax, err := policy.GetDeclarationsMax(nv) - if err != nil { - return err - } - - addrSectors := sectorsMax - if cctx.Int("max-sectors") != 0 { - addrSectors = cctx.Int("max-sectors") - if addrSectors > sectorsMax { - return xerrors.Errorf("the specified max-sectors exceeds the maximum limit") - } - } - - var params []miner.ExtendSectorExpiration2Params - - p := miner.ExtendSectorExpiration2Params{} - scount := 0 - - for l, exts := range extensions { - for newExp, numbers := range exts { - sectorsWithoutClaimsToExtend := bitfield.New() - var sectorsWithClaims []miner.SectorClaim - for _, sectorNumber := range numbers { - claimIdsToMaintain := make([]verifreg.ClaimId, 0) - claimIdsToDrop := make([]verifreg.ClaimId, 0) - cannotExtendSector := false - claimIds, ok := claimIdsBySector[sectorNumber] - // Nothing to check, add to ccSectors - if !ok { - sectorsWithoutClaimsToExtend.Set(uint64(sectorNumber)) - } else { - for _, claimId := range claimIds { - claim, ok := claimsMap[claimId] - if !ok { - return xerrors.Errorf("failed to find claim for claimId %d", claimId) - } - claimExpiration := claim.TermStart + claim.TermMax - // can be maintained in the extended sector - if claimExpiration > newExp { - claimIdsToMaintain = append(claimIdsToMaintain, claimId) - } else { - sectorInfo, ok := activeSectorsInfo[sectorNumber] - if !ok { - return xerrors.Errorf("failed to find sector in active sector set: %w", err) - } - if !cctx.Bool("drop-claims") || - // FIP-0045 requires the claim minimum duration to have passed - currEpoch <= (claim.TermStart+claim.TermMin) || - // FIP-0045 requires the sector to be in its last 30 days of life - (currEpoch <= sectorInfo.Expiration-builtin.EndOfLifeClaimDropPeriod) { - fmt.Printf("skipping sector %d because claim %d does not live long enough \n", sectorNumber, claimId) - cannotExtendSector = true - break - } - - claimIdsToDrop = append(claimIdsToDrop, claimId) - } - } - if cannotExtendSector { - continue - } - - if len(claimIdsToMaintain)+len(claimIdsToDrop) != 0 { - sectorsWithClaims = append(sectorsWithClaims, miner.SectorClaim{ - SectorNumber: sectorNumber, - MaintainClaims: claimIdsToMaintain, - DropClaims: claimIdsToDrop, - }) - } - } - } - - sectorsWithoutClaimsCount, err := sectorsWithoutClaimsToExtend.Count() - if err != nil { - return xerrors.Errorf("failed to count cc sectors: %w", err) - } - - sectorsInDecl := int(sectorsWithoutClaimsCount) + len(sectorsWithClaims) - scount += sectorsInDecl - - if scount > addrSectors || len(p.Extensions) >= declMax { - params = append(params, p) - p = miner.ExtendSectorExpiration2Params{} - scount = sectorsInDecl - } - - p.Extensions = append(p.Extensions, miner.ExpirationExtension2{ - Deadline: l.Deadline, - Partition: l.Partition, - Sectors: SectorNumsToBitfield(numbers), - SectorsWithClaims: sectorsWithClaims, - NewExpiration: newExp, - }) - - } - } - - // if we have any sectors, then one last append is needed here - if scount != 0 { - params = append(params, p) - } - - if len(params) == 0 { - fmt.Println("nothing to extend") - return nil - } - - mi, err := fullApi.StateMinerInfo(ctx, maddr, types.EmptyTSK) - if err != nil { - return xerrors.Errorf("getting miner info: %w", err) - } - - stotal := 0 - - for i := range params { - scount := 0 - for _, ext := range params[i].Extensions { - count, err := ext.Sectors.Count() - if err != nil { - return err - } - scount += int(count) - } - fmt.Printf("Extending %d sectors: ", scount) - stotal += scount - - if !cctx.Bool("really-do-it") { - pp, err := NewPseudoExtendParams(¶ms[i]) - if err != nil { - return err - } - - data, err := json.MarshalIndent(pp, "", " ") - if err != nil { - return err - } - - fmt.Println("\n", string(data)) - continue - } - - sp, aerr := actors.SerializeParams(¶ms[i]) - if aerr != nil { - return xerrors.Errorf("serializing params: %w", err) - } - - smsg, err := fullApi.MpoolPushMessage(ctx, &types.Message{ - From: mi.Worker, - To: maddr, - Method: builtin.MethodsMiner.ExtendSectorExpiration2, - Value: big.Zero(), - Params: sp, - }, spec) - if err != nil { - return xerrors.Errorf("mpool push message: %w", err) - } - - fmt.Println(smsg.Cid()) - } - - fmt.Printf("%d sectors extended\n", stotal) - - return nil - }, -} - var sectorsTerminateCmd = &cli.Command{ Name: "terminate", Usage: "Terminate sector on-chain then remove (WARNING: This means losing power and collateral for the removed sector)", @@ -2238,175 +1413,6 @@ func yesno(b bool) string { return color.RedString("NO") } -var sectorsCompactPartitionsCmd = &cli.Command{ - Name: "compact-partitions", - Usage: "removes dead sectors from partitions and reduces the number of partitions used if possible", - Flags: []cli.Flag{ - &cli.Uint64Flag{ - Name: "deadline", - Usage: "the deadline to compact the partitions in", - Required: true, - }, - &cli.Int64SliceFlag{ - Name: "partitions", - Usage: "list of partitions to compact sectors in", - Required: true, - }, - &cli.BoolFlag{ - Name: "really-do-it", - Usage: "Actually send transaction performing the action", - Value: false, - }, - &cli.StringFlag{ - Name: "actor", - Usage: "Specify the address of the miner to run this command", - }, - }, - Action: func(cctx *cli.Context) error { - fullNodeAPI, acloser, err := lcli.GetFullNodeAPI(cctx) - if err != nil { - return err - } - defer acloser() - - ctx := lcli.ReqContext(cctx) - - maddr, err := getActorAddress(ctx, cctx) - if err != nil { - return err - } - - minfo, err := fullNodeAPI.StateMinerInfo(ctx, maddr, types.EmptyTSK) - if err != nil { - return err - } - - deadline := cctx.Uint64("deadline") - if deadline > miner.WPoStPeriodDeadlines { - return fmt.Errorf("deadline %d out of range", deadline) - } - - parts := cctx.Int64Slice("partitions") - if len(parts) <= 0 { - return fmt.Errorf("must include at least one partition to compact") - } - fmt.Printf("compacting %d partitions\n", len(parts)) - - var makeMsgForPartitions func(partitionsBf bitfield.BitField) ([]*types.Message, error) - makeMsgForPartitions = func(partitionsBf bitfield.BitField) ([]*types.Message, error) { - params := miner.CompactPartitionsParams{ - Deadline: deadline, - Partitions: partitionsBf, - } - - sp, aerr := actors.SerializeParams(¶ms) - if aerr != nil { - return nil, xerrors.Errorf("serializing params: %w", err) - } - - msg := &types.Message{ - From: minfo.Worker, - To: maddr, - Method: builtin.MethodsMiner.CompactPartitions, - Value: big.Zero(), - Params: sp, - } - - estimatedMsg, err := fullNodeAPI.GasEstimateMessageGas(ctx, msg, nil, types.EmptyTSK) - if err != nil && xerrors.Is(err, &api.ErrOutOfGas{}) { - // the message is too big -- split into 2 - partitionsSlice, err := partitionsBf.All(math.MaxUint64) - if err != nil { - return nil, err - } - - partitions1 := bitfield.New() - for i := 0; i < len(partitionsSlice)/2; i++ { - partitions1.Set(uint64(i)) - } - - msgs1, err := makeMsgForPartitions(partitions1) - if err != nil { - return nil, err - } - - // time for the second half - partitions2 := bitfield.New() - for i := len(partitionsSlice) / 2; i < len(partitionsSlice); i++ { - partitions2.Set(uint64(i)) - } - - msgs2, err := makeMsgForPartitions(partitions2) - if err != nil { - return nil, err - } - - return append(msgs1, msgs2...), nil - } else if err != nil { - return nil, err - } - - return []*types.Message{estimatedMsg}, nil - } - - partitions := bitfield.New() - for _, partition := range parts { - partitions.Set(uint64(partition)) - } - - msgs, err := makeMsgForPartitions(partitions) - if err != nil { - return xerrors.Errorf("failed to make messages: %w", err) - } - - // Actually send the messages if really-do-it provided, simulate otherwise - if cctx.Bool("really-do-it") { - smsgs, err := fullNodeAPI.MpoolBatchPushMessage(ctx, msgs, nil) - if err != nil { - return xerrors.Errorf("mpool push: %w", err) - } - - if len(smsgs) == 1 { - fmt.Printf("Requested compact partitions in message %s\n", smsgs[0].Cid()) - } else { - fmt.Printf("Requested compact partitions in %d messages\n\n", len(smsgs)) - for _, v := range smsgs { - fmt.Println(v.Cid()) - } - } - - for _, v := range smsgs { - wait, err := fullNodeAPI.StateWaitMsg(ctx, v.Cid(), 2) - if err != nil { - return err - } - - // check it executed successfully - if wait.Receipt.ExitCode.IsError() { - fmt.Println(cctx.App.Writer, "compact partitions msg %s failed!", v.Cid()) - return err - } - } - - return nil - } - - for i, v := range msgs { - fmt.Printf("total of %d CompactPartitions msgs would be sent\n", len(msgs)) - - estMsg, err := fullNodeAPI.GasEstimateMessageGas(ctx, v, nil, types.EmptyTSK) - if err != nil { - return err - } - - fmt.Printf("msg %d would cost up to %s\n", i+1, types.FIL(estMsg.RequiredFunds())) - } - - return nil - - }, -} - var sectorsNumbersCmd = &cli.Command{ Name: "numbers", Usage: "manage sector number assignments", diff --git a/cmd/lotus-miner/storage.go b/cmd/lotus-miner/storage.go index fdd5b5696..b39fe2bf7 100644 --- a/cmd/lotus-miner/storage.go +++ b/cmd/lotus-miner/storage.go @@ -27,6 +27,7 @@ import ( "github.com/filecoin-project/lotus/api/v0api" "github.com/filecoin-project/lotus/chain/types" lcli "github.com/filecoin-project/lotus/cli" + "github.com/filecoin-project/lotus/cli/spcli" "github.com/filecoin-project/lotus/lib/tablewriter" sealing "github.com/filecoin-project/lotus/storage/pipeline" "github.com/filecoin-project/lotus/storage/sealer/fsutil" @@ -803,7 +804,7 @@ var storageListSectorsCmd = &cli.Command{ "Storage": color.New(sc1).Sprint(e.storage), "Sector": e.id, "Type": e.ft.String(), - "State": color.New(stateOrder[sealing.SectorState(e.state)].col).Sprint(e.state), + "State": color.New(spcli.StateOrder[sealing.SectorState(e.state)].Col).Sprint(e.state), "Primary": maybeStr(e.primary, color.FgGreen, "primary") + maybeStr(e.copy, color.FgBlue, "copy") + maybeStr(e.main, color.FgRed, "main"), "Path use": maybeStr(e.seal, color.FgMagenta, "seal ") + maybeStr(e.store, color.FgCyan, "store"), "URLs": e.urls, @@ -995,7 +996,7 @@ var storageLocks = &cli.Command{ return xerrors.Errorf("getting sector status(%d): %w", lock.Sector.Number, err) } - lockstr := fmt.Sprintf("%d\t%s\t", lock.Sector.Number, color.New(stateOrder[sealing.SectorState(st.State)].col).Sprint(st.State)) + lockstr := fmt.Sprintf("%d\t%s\t", lock.Sector.Number, color.New(spcli.StateOrder[sealing.SectorState(st.State)].Col).Sprint(st.State)) for i := 0; i < storiface.FileTypes; i++ { if lock.Write[i] > 0 { diff --git a/cmd/lotus-shed/sectors.go b/cmd/lotus-shed/sectors.go index 899e0f290..176f232fe 100644 --- a/cmd/lotus-shed/sectors.go +++ b/cmd/lotus-shed/sectors.go @@ -31,6 +31,7 @@ import ( "github.com/filecoin-project/lotus/chain/actors" "github.com/filecoin-project/lotus/chain/types" lcli "github.com/filecoin-project/lotus/cli" + "github.com/filecoin-project/lotus/cli/spcli" "github.com/filecoin-project/lotus/lib/parmap" "github.com/filecoin-project/lotus/node/repo" "github.com/filecoin-project/lotus/storage/paths" @@ -44,7 +45,7 @@ var sectorsCmd = &cli.Command{ Usage: "Tools for interacting with sectors", Flags: []cli.Flag{}, Subcommands: []*cli.Command{ - terminateSectorCmd, + spcli.TerminateSectorCmd(shedGetActor), terminateSectorPenaltyEstimationCmd, visAllocatedSectorsCmd, dumpRLESectorCmd, @@ -53,138 +54,14 @@ var sectorsCmd = &cli.Command{ }, } -var terminateSectorCmd = &cli.Command{ - Name: "terminate", - Usage: "Forcefully terminate a sector (WARNING: This means losing power and pay a one-time termination penalty(including collateral) for the terminated sector)", - ArgsUsage: "[sectorNum1 sectorNum2 ...]", - Flags: []cli.Flag{ - &cli.StringFlag{ - Name: "actor", - Usage: "specify the address of miner actor", - }, - &cli.BoolFlag{ - Name: "really-do-it", - Usage: "pass this flag if you know what you are doing", - }, - &cli.StringFlag{ - Name: "from", - Usage: "specify the address to send the terminate message from", - }, - }, - Action: func(cctx *cli.Context) error { - if cctx.NArg() < 1 { - return lcli.ShowHelp(cctx, fmt.Errorf("at least one sector must be specified")) - } +func shedGetActor(cctx *cli.Context) (address.Address, error) { + minerApi, acloser, err := lcli.GetStorageMinerAPI(cctx) + if err != nil { + return address.Address{}, err + } + defer acloser() - var maddr address.Address - if act := cctx.String("actor"); act != "" { - var err error - maddr, err = address.NewFromString(act) - if err != nil { - return fmt.Errorf("parsing address %s: %w", act, err) - } - } - - if !cctx.Bool("really-do-it") { - return fmt.Errorf("this is a command for advanced users, only use it if you are sure of what you are doing") - } - - nodeApi, closer, err := lcli.GetFullNodeAPI(cctx) - if err != nil { - return err - } - defer closer() - - ctx := lcli.ReqContext(cctx) - - if maddr.Empty() { - minerApi, acloser, err := lcli.GetStorageMinerAPI(cctx) - if err != nil { - return err - } - defer acloser() - - maddr, err = minerApi.ActorAddress(ctx) - if err != nil { - return err - } - } - - mi, err := nodeApi.StateMinerInfo(ctx, maddr, types.EmptyTSK) - if err != nil { - return err - } - - terminationDeclarationParams := []miner2.TerminationDeclaration{} - - for _, sn := range cctx.Args().Slice() { - sectorNum, err := strconv.ParseUint(sn, 10, 64) - if err != nil { - return fmt.Errorf("could not parse sector number: %w", err) - } - - sectorbit := bitfield.New() - sectorbit.Set(sectorNum) - - loca, err := nodeApi.StateSectorPartition(ctx, maddr, abi.SectorNumber(sectorNum), types.EmptyTSK) - if err != nil { - return fmt.Errorf("get state sector partition %s", err) - } - - para := miner2.TerminationDeclaration{ - Deadline: loca.Deadline, - Partition: loca.Partition, - Sectors: sectorbit, - } - - terminationDeclarationParams = append(terminationDeclarationParams, para) - } - - terminateSectorParams := &miner2.TerminateSectorsParams{ - Terminations: terminationDeclarationParams, - } - - sp, err := actors.SerializeParams(terminateSectorParams) - if err != nil { - return xerrors.Errorf("serializing params: %w", err) - } - - var fromAddr address.Address - if from := cctx.String("from"); from != "" { - var err error - fromAddr, err = address.NewFromString(from) - if err != nil { - return fmt.Errorf("parsing address %s: %w", from, err) - } - } else { - fromAddr = mi.Worker - } - - smsg, err := nodeApi.MpoolPushMessage(ctx, &types.Message{ - From: fromAddr, - To: maddr, - Method: builtin.MethodsMiner.TerminateSectors, - - Value: big.Zero(), - Params: sp, - }, nil) - if err != nil { - return xerrors.Errorf("mpool push message: %w", err) - } - - fmt.Println("sent termination message:", smsg.Cid()) - - wait, err := nodeApi.StateWaitMsg(ctx, smsg.Cid(), uint64(cctx.Int("confidence"))) - if err != nil { - return err - } - - if wait.Receipt.ExitCode.IsError() { - return fmt.Errorf("terminate sectors message returned exit %d", wait.Receipt.ExitCode) - } - - return nil - }, + return minerApi.ActorAddress(cctx.Context) } func findPenaltyInInternalExecutions(prefix string, trace []types.ExecutionTrace) { diff --git a/cmd/lotus/main.go b/cmd/lotus/main.go index 85324e466..fce9a6136 100644 --- a/cmd/lotus/main.go +++ b/cmd/lotus/main.go @@ -13,6 +13,7 @@ import ( "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/build" lcli "github.com/filecoin-project/lotus/cli" + "github.com/filecoin-project/lotus/cli/clicommands" cliutil "github.com/filecoin-project/lotus/cli/util" "github.com/filecoin-project/lotus/lib/lotuslog" "github.com/filecoin-project/lotus/lib/tracing" @@ -112,7 +113,7 @@ func main() { return nil }, - Commands: append(local, lcli.Commands...), + Commands: append(local, clicommands.Commands...), } app.Setup() diff --git a/cmd/sptool/actor.go b/cmd/sptool/actor.go new file mode 100644 index 000000000..fb0d5e966 --- /dev/null +++ b/cmd/sptool/actor.go @@ -0,0 +1,140 @@ +package main + +import ( + "fmt" + "os" + "strings" + + "github.com/fatih/color" + "github.com/urfave/cli/v2" + + "github.com/filecoin-project/go-address" + + builtin2 "github.com/filecoin-project/lotus/chain/actors/builtin" + "github.com/filecoin-project/lotus/chain/types" + lcli "github.com/filecoin-project/lotus/cli" + "github.com/filecoin-project/lotus/cli/spcli" + "github.com/filecoin-project/lotus/lib/tablewriter" +) + +var actorCmd = &cli.Command{ + Name: "actor", + Usage: "Manage Filecoin Miner Actor Metadata", + Subcommands: []*cli.Command{ + spcli.ActorSetAddrsCmd(SPTActorGetter), + spcli.ActorWithdrawCmd(SPTActorGetter), + spcli.ActorRepayDebtCmd(SPTActorGetter), + spcli.ActorSetPeeridCmd(SPTActorGetter), + spcli.ActorSetOwnerCmd(SPTActorGetter), + spcli.ActorControlCmd(SPTActorGetter, actorControlListCmd(SPTActorGetter)), + spcli.ActorProposeChangeWorkerCmd(SPTActorGetter), + spcli.ActorConfirmChangeWorkerCmd(SPTActorGetter), + spcli.ActorCompactAllocatedCmd(SPTActorGetter), + spcli.ActorProposeChangeBeneficiaryCmd(SPTActorGetter), + spcli.ActorConfirmChangeBeneficiaryCmd(SPTActorGetter), + }, +} + +func actorControlListCmd(getActor spcli.ActorAddressGetter) *cli.Command { + return &cli.Command{ + Name: "list", + Usage: "Get currently set control addresses. Note: This excludes most roles as they are not known to the immediate chain state.", + Flags: []cli.Flag{ + &cli.BoolFlag{ + Name: "verbose", + }, + }, + Action: func(cctx *cli.Context) error { + api, acloser, err := lcli.GetFullNodeAPIV1(cctx) + if err != nil { + return err + } + defer acloser() + + ctx := lcli.ReqContext(cctx) + + maddr, err := getActor(cctx) + if err != nil { + return err + } + + mi, err := api.StateMinerInfo(ctx, maddr, types.EmptyTSK) + if err != nil { + return err + } + + tw := tablewriter.New( + tablewriter.Col("name"), + tablewriter.Col("ID"), + tablewriter.Col("key"), + tablewriter.Col("use"), + tablewriter.Col("balance"), + ) + + post := map[address.Address]struct{}{} + + for _, ca := range mi.ControlAddresses { + post[ca] = struct{}{} + } + + printKey := func(name string, a address.Address) { + var actor *types.Actor + if actor, err = api.StateGetActor(ctx, a, types.EmptyTSK); err != nil { + fmt.Printf("%s\t%s: error getting actor: %s\n", name, a, err) + return + } + b := actor.Balance + + var k = a + // 'a' maybe a 'robust', in that case, 'StateAccountKey' returns an error. + if builtin2.IsAccountActor(actor.Code) { + if k, err = api.StateAccountKey(ctx, a, types.EmptyTSK); err != nil { + fmt.Printf("%s\t%s: error getting account key: %s\n", name, a, err) + return + } + } + kstr := k.String() + if !cctx.Bool("verbose") { + if len(kstr) > 9 { + kstr = kstr[:6] + "..." + } + } + + bstr := types.FIL(b).String() + switch { + case b.LessThan(types.FromFil(10)): + bstr = color.RedString(bstr) + case b.LessThan(types.FromFil(50)): + bstr = color.YellowString(bstr) + default: + bstr = color.GreenString(bstr) + } + + var uses []string + if a == mi.Worker { + uses = append(uses, color.YellowString("other")) + } + if _, ok := post[a]; ok { + uses = append(uses, color.GreenString("post")) + } + + tw.Write(map[string]interface{}{ + "name": name, + "ID": a, + "key": kstr, + "use": strings.Join(uses, " "), + "balance": bstr, + }) + } + + printKey("owner", mi.Owner) + printKey("worker", mi.Worker) + printKey("beneficiary", mi.Beneficiary) + for i, ca := range mi.ControlAddresses { + printKey(fmt.Sprintf("control-%d", i), ca) + } + + return tw.Flush(os.Stdout) + }, + } +} diff --git a/cmd/sptool/main.go b/cmd/sptool/main.go new file mode 100644 index 000000000..7970b8db3 --- /dev/null +++ b/cmd/sptool/main.go @@ -0,0 +1,84 @@ +package main + +import ( + "context" + "fmt" + "os" + "os/signal" + + logging "github.com/ipfs/go-log/v2" + "github.com/urfave/cli/v2" + + "github.com/filecoin-project/go-address" + + "github.com/filecoin-project/lotus/build" + "github.com/filecoin-project/lotus/cli/spcli" +) + +var log = logging.Logger("sptool") + +func main() { + local := []*cli.Command{ + actorCmd, + spcli.InfoCmd(SPTActorGetter), + sectorsCmd, + provingCmd, + //multiSigCmd, + } + + app := &cli.App{ + Name: "sptool", + Usage: "Manage Filecoin Miner Actor", + Version: build.UserVersion(), + Commands: local, + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "repo", + EnvVars: []string{"LOTUS_PATH"}, + Hidden: true, + Value: "~/.lotus", // TODO: Consider XDG_DATA_HOME + }, + &cli.StringFlag{ + Name: "log-level", + Value: "info", + }, + &cli.StringFlag{ + Name: "actor", + Required: os.Getenv("LOTUS_DOCS_GENERATION") != "1", + Usage: "miner actor to manage", + EnvVars: []string{"SP_ADDRESS"}, + }, + }, + Before: func(cctx *cli.Context) error { + return logging.SetLogLevel("sptool", cctx.String("sptool")) + }, + } + + // terminate early on ctrl+c + c := make(chan os.Signal, 1) + signal.Notify(c, os.Interrupt) + ctx, cancel := context.WithCancel(context.Background()) + go func() { + <-c + cancel() + fmt.Println("Received interrupt, shutting down... Press CTRL+C again to force shutdown") + <-c + fmt.Println("Forcing stop") + os.Exit(1) + }() + + if err := app.RunContext(ctx, os.Args); err != nil { + log.Errorf("%+v", err) + os.Exit(1) + return + } + +} + +func SPTActorGetter(cctx *cli.Context) (address.Address, error) { + addr, err := address.NewFromString(cctx.String("actor")) + if err != nil { + return address.Undef, fmt.Errorf("parsing address: %w", err) + } + return addr, nil +} diff --git a/cmd/sptool/proving.go b/cmd/sptool/proving.go new file mode 100644 index 000000000..87c67b5f4 --- /dev/null +++ b/cmd/sptool/proving.go @@ -0,0 +1,18 @@ +package main + +import ( + "github.com/urfave/cli/v2" + + "github.com/filecoin-project/lotus/cli/spcli" +) + +var provingCmd = &cli.Command{ + Name: "proving", + Usage: "View proving information", + Subcommands: []*cli.Command{ + spcli.ProvingInfoCmd(SPTActorGetter), + spcli.ProvingDeadlinesCmd(SPTActorGetter), + spcli.ProvingDeadlineInfoCmd(SPTActorGetter), + spcli.ProvingFaultsCmd(SPTActorGetter), + }, +} diff --git a/cmd/sptool/sector.go b/cmd/sptool/sector.go new file mode 100644 index 000000000..8f33053d3 --- /dev/null +++ b/cmd/sptool/sector.go @@ -0,0 +1,356 @@ +package main + +import ( + "fmt" + "os" + "sort" + + "github.com/docker/go-units" + "github.com/fatih/color" + cbor "github.com/ipfs/go-ipld-cbor" + "github.com/urfave/cli/v2" + "golang.org/x/xerrors" + + "github.com/filecoin-project/go-bitfield" + "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/go-state-types/big" + + "github.com/filecoin-project/lotus/blockstore" + "github.com/filecoin-project/lotus/chain/actors/adt" + "github.com/filecoin-project/lotus/chain/actors/builtin/miner" + "github.com/filecoin-project/lotus/chain/actors/policy" + "github.com/filecoin-project/lotus/chain/types" + lcli "github.com/filecoin-project/lotus/cli" + "github.com/filecoin-project/lotus/cli/spcli" + cliutil "github.com/filecoin-project/lotus/cli/util" + "github.com/filecoin-project/lotus/lib/tablewriter" +) + +var sectorsCmd = &cli.Command{ + Name: "sectors", + Usage: "interact with sector store", + Subcommands: []*cli.Command{ + spcli.SectorsStatusCmd(SPTActorGetter, nil), + sectorsListCmd, // in-house b/c chain-only is so different. Needs Curio *web* implementation + spcli.SectorPreCommitsCmd(SPTActorGetter), + spcli.SectorsCheckExpireCmd(SPTActorGetter), + sectorsExpiredCmd, // in-house b/c chain-only is so different + spcli.SectorsExtendCmd(SPTActorGetter), + //spcli.SectorsTerminateCmd(SPTActorGetter), // Could not trace through the state-machine + spcli.SectorsCompactPartitionsCmd(SPTActorGetter), + }} + +var sectorsExpiredCmd = &cli.Command{ + Name: "expired", + Usage: "Get or cleanup expired sectors", + Flags: []cli.Flag{ + &cli.Int64Flag{ + Name: "expired-epoch", + Usage: "epoch at which to check sector expirations", + DefaultText: "WinningPoSt lookback epoch", + }, + }, + Action: func(cctx *cli.Context) error { + fullApi, nCloser, err := lcli.GetFullNodeAPI(cctx) + if err != nil { + return xerrors.Errorf("getting fullnode api: %w", err) + } + defer nCloser() + ctx := lcli.ReqContext(cctx) + + head, err := fullApi.ChainHead(ctx) + if err != nil { + return xerrors.Errorf("getting chain head: %w", err) + } + + lbEpoch := abi.ChainEpoch(cctx.Int64("expired-epoch")) + if !cctx.IsSet("expired-epoch") { + nv, err := fullApi.StateNetworkVersion(ctx, head.Key()) + if err != nil { + return xerrors.Errorf("getting network version: %w", err) + } + + lbEpoch = head.Height() - policy.GetWinningPoStSectorSetLookback(nv) + if lbEpoch < 0 { + return xerrors.Errorf("too early to terminate sectors") + } + } + + if cctx.IsSet("confirm-remove-count") && !cctx.IsSet("expired-epoch") { + return xerrors.Errorf("--expired-epoch must be specified with --confirm-remove-count") + } + + lbts, err := fullApi.ChainGetTipSetByHeight(ctx, lbEpoch, head.Key()) + if err != nil { + return xerrors.Errorf("getting lookback tipset: %w", err) + } + + maddr, err := SPTActorGetter(cctx) + if err != nil { + return xerrors.Errorf("getting actor address: %w", err) + } + + // toCheck is a working bitfield which will only contain terminated sectors + toCheck := bitfield.New() + { + sectors, err := fullApi.StateMinerSectors(ctx, maddr, nil, lbts.Key()) + if err != nil { + return xerrors.Errorf("getting sector on chain info: %w", err) + } + + for _, sector := range sectors { + if sector.Expiration <= lbts.Height() { + toCheck.Set(uint64(sector.SectorNumber)) + } + } + } + + mact, err := fullApi.StateGetActor(ctx, maddr, lbts.Key()) + if err != nil { + return err + } + + tbs := blockstore.NewTieredBstore(blockstore.NewAPIBlockstore(fullApi), blockstore.NewMemory()) + mas, err := miner.Load(adt.WrapStore(ctx, cbor.NewCborStore(tbs)), mact) + if err != nil { + return err + } + + alloc, err := mas.GetAllocatedSectors() + if err != nil { + return xerrors.Errorf("getting allocated sectors: %w", err) + } + + // only allocated sectors can be expired, + toCheck, err = bitfield.IntersectBitField(toCheck, *alloc) + if err != nil { + return xerrors.Errorf("intersecting bitfields: %w", err) + } + + if err := mas.ForEachDeadline(func(dlIdx uint64, dl miner.Deadline) error { + return dl.ForEachPartition(func(partIdx uint64, part miner.Partition) error { + live, err := part.LiveSectors() + if err != nil { + return err + } + + toCheck, err = bitfield.SubtractBitField(toCheck, live) + if err != nil { + return err + } + + unproven, err := part.UnprovenSectors() + if err != nil { + return err + } + + toCheck, err = bitfield.SubtractBitField(toCheck, unproven) + + return err + }) + }); err != nil { + return err + } + + err = mas.ForEachPrecommittedSector(func(pci miner.SectorPreCommitOnChainInfo) error { + toCheck.Unset(uint64(pci.Info.SectorNumber)) + return nil + }) + if err != nil { + return err + } + + // toCheck now only contains sectors which either failed to precommit or are expired/terminated + fmt.Printf("Sectors that either failed to precommit or are expired/terminated:\n") + + err = toCheck.ForEach(func(u uint64) error { + fmt.Println(abi.SectorNumber(u)) + + return nil + }) + if err != nil { + return err + } + + return nil + }, +} + +var sectorsListCmd = &cli.Command{ + Name: "list", + Usage: "List sectors", + Flags: []cli.Flag{ + /* + &cli.BoolFlag{ + Name: "show-removed", + Usage: "show removed sectors", + Aliases: []string{"r"}, + }, + &cli.BoolFlag{ + Name: "fast", + Usage: "don't show on-chain info for better performance", + Aliases: []string{"f"}, + }, + &cli.BoolFlag{ + Name: "events", + Usage: "display number of events the sector has received", + Aliases: []string{"e"}, + }, + &cli.BoolFlag{ + Name: "initial-pledge", + Usage: "display initial pledge", + Aliases: []string{"p"}, + }, + &cli.BoolFlag{ + Name: "seal-time", + Usage: "display how long it took for the sector to be sealed", + Aliases: []string{"t"}, + }, + &cli.StringFlag{ + Name: "states", + Usage: "filter sectors by a comma-separated list of states", + }, + &cli.BoolFlag{ + Name: "unproven", + Usage: "only show sectors which aren't in the 'Proving' state", + Aliases: []string{"u"}, + }, + */ + }, + Subcommands: []*cli.Command{ + //sectorsListUpgradeBoundsCmd, + }, + Action: func(cctx *cli.Context) error { + fullApi, closer2, err := lcli.GetFullNodeAPI(cctx) // TODO: consider storing full node address in config + if err != nil { + return err + } + defer closer2() + + ctx := lcli.ReqContext(cctx) + + maddr, err := SPTActorGetter(cctx) + if err != nil { + return err + } + + head, err := fullApi.ChainHead(ctx) + if err != nil { + return err + } + + activeSet, err := fullApi.StateMinerActiveSectors(ctx, maddr, head.Key()) + if err != nil { + return err + } + activeIDs := make(map[abi.SectorNumber]struct{}, len(activeSet)) + for _, info := range activeSet { + activeIDs[info.SectorNumber] = struct{}{} + } + + sset, err := fullApi.StateMinerSectors(ctx, maddr, nil, head.Key()) + if err != nil { + return err + } + commitedIDs := make(map[abi.SectorNumber]struct{}, len(sset)) + for _, info := range sset { + commitedIDs[info.SectorNumber] = struct{}{} + } + + sort.Slice(sset, func(i, j int) bool { + return sset[i].SectorNumber < sset[j].SectorNumber + }) + + tw := tablewriter.New( + tablewriter.Col("ID"), + tablewriter.Col("State"), + tablewriter.Col("OnChain"), + tablewriter.Col("Active"), + tablewriter.Col("Expiration"), + tablewriter.Col("SealTime"), + tablewriter.Col("Events"), + tablewriter.Col("Deals"), + tablewriter.Col("DealWeight"), + tablewriter.Col("VerifiedPower"), + tablewriter.Col("Pledge"), + tablewriter.NewLineCol("Error"), + tablewriter.NewLineCol("RecoveryTimeout")) + + fast := cctx.Bool("fast") + + for _, st := range sset { + s := st.SectorNumber + + _, inSSet := commitedIDs[s] + _, inASet := activeIDs[s] + + const verifiedPowerGainMul = 9 + dw, vp := .0, .0 + { + rdw := big.Add(st.DealWeight, st.VerifiedDealWeight) + dw = float64(big.Div(rdw, big.NewInt(int64(st.Expiration-st.Activation))).Uint64()) + vp = float64(big.Div(big.Mul(st.VerifiedDealWeight, big.NewInt(verifiedPowerGainMul)), big.NewInt(int64(st.Expiration-st.Activation))).Uint64()) + } + + var deals int + for _, deal := range st.DealIDs { + if deal != 0 { + deals++ + } + } + + exp := st.Expiration + // if st.OnTime > 0 && st.OnTime < exp { + // exp = st.OnTime // Can be different when the sector was CC upgraded + // } + + m := map[string]interface{}{ + "ID": s, + //"State": color.New(spcli.StateOrder[sealing.SectorState(st.State)].Col).Sprint(st.State), + "OnChain": yesno(inSSet), + "Active": yesno(inASet), + } + + if deals > 0 { + m["Deals"] = color.GreenString("%d", deals) + } else { + m["Deals"] = color.BlueString("CC") + // if st.ToUpgrade { + // m["Deals"] = color.CyanString("CC(upgrade)") + // } + } + + if !fast { + if !inSSet { + m["Expiration"] = "n/a" + } else { + m["Expiration"] = cliutil.EpochTime(head.Height(), exp) + // if st.Early > 0 { + // m["RecoveryTimeout"] = color.YellowString(cliutil.EpochTime(head.Height(), st.Early)) + // } + } + if inSSet && cctx.Bool("initial-pledge") { + m["Pledge"] = types.FIL(st.InitialPledge).Short() + } + } + + if !fast && deals > 0 { + m["DealWeight"] = units.BytesSize(dw) + if vp > 0 { + m["VerifiedPower"] = color.GreenString(units.BytesSize(vp)) + } + } + + tw.Write(m) + } + + return tw.Flush(os.Stdout) + }, +} + +func yesno(b bool) string { + if b { + return color.GreenString("YES") + } + return color.RedString("NO") +} diff --git a/documentation/en/cli-curio.md b/documentation/en/cli-curio.md index 6976dfa2e..296a45c43 100644 --- a/documentation/en/cli-curio.md +++ b/documentation/en/cli-curio.md @@ -18,13 +18,12 @@ COMMANDS: web Start Curio web interface guided-setup Run the guided setup for migrating from lotus-miner to Curio seal Manage the sealing pipeline + auth Manage RPC permissions + log Manage logging + wait-api Wait for lotus api to come online + fetch-params Fetch proving parameters version Print version help, h Shows a list of commands or help for one command - DEVELOPER: - auth Manage RPC permissions - log Manage logging - wait-api Wait for lotus api to come online - fetch-params Fetch proving parameters GLOBAL OPTIONS: --color use color in display output (default: depends on output being a TTY) @@ -348,18 +347,6 @@ OPTIONS: --help, -h show help ``` -## curio version -``` -NAME: - curio version - Print version - -USAGE: - curio version [command options] [arguments...] - -OPTIONS: - --help, -h show help -``` - ## curio auth ``` NAME: @@ -487,9 +474,6 @@ NAME: USAGE: curio wait-api [command options] [arguments...] -CATEGORY: - DEVELOPER - OPTIONS: --timeout value duration to wait till fail (default: 30s) --help, -h show help @@ -503,8 +487,17 @@ NAME: USAGE: curio fetch-params [command options] [sectorSize] -CATEGORY: - DEVELOPER +OPTIONS: + --help, -h show help +``` + +## curio version +``` +NAME: + curio version - Print version + +USAGE: + curio version [command options] [arguments...] OPTIONS: --help, -h show help diff --git a/documentation/en/cli-lotus-miner.md b/documentation/en/cli-lotus-miner.md index 0f6704275..ed068624e 100644 --- a/documentation/en/cli-lotus-miner.md +++ b/documentation/en/cli-lotus-miner.md @@ -10,21 +10,20 @@ VERSION: 1.27.0-dev COMMANDS: - init Initialize a lotus miner repo - run Start a lotus miner process - stop Stop a running lotus miner - config Manage node config - backup Create node metadata backup - version Print version - help, h Shows a list of commands or help for one command + init Initialize a lotus miner repo + run Start a lotus miner process + stop Stop a running lotus miner + config Manage node config + backup Create node metadata backup + auth Manage RPC permissions + log Manage logging + wait-api Wait for lotus api to come online + fetch-params Fetch proving parameters + version Print version + help, h Shows a list of commands or help for one command CHAIN: actor manipulate the miner actor info Print miner info - DEVELOPER: - auth Manage RPC permissions - log Manage logging - wait-api Wait for lotus api to come online - fetch-params Fetch proving parameters STORAGE: sectors interact with sector store proving View proving information @@ -194,6 +193,150 @@ OPTIONS: --help, -h show help ``` +## lotus-miner auth +``` +NAME: + lotus-miner auth - Manage RPC permissions + +USAGE: + lotus-miner auth command [command options] [arguments...] + +COMMANDS: + create-token Create token + api-info Get token with API info required to connect to this node + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help +``` + +### lotus-miner auth create-token +``` +NAME: + lotus-miner auth create-token - Create token + +USAGE: + lotus-miner auth create-token [command options] [arguments...] + +OPTIONS: + --perm value permission to assign to the token, one of: read, write, sign, admin + --help, -h show help +``` + +### lotus-miner auth api-info +``` +NAME: + lotus-miner auth api-info - Get token with API info required to connect to this node + +USAGE: + lotus-miner auth api-info [command options] [arguments...] + +OPTIONS: + --perm value permission to assign to the token, one of: read, write, sign, admin + --help, -h show help +``` + +## lotus-miner log +``` +NAME: + lotus-miner log - Manage logging + +USAGE: + lotus-miner log command [command options] [arguments...] + +COMMANDS: + list List log systems + set-level Set log level + alerts Get alert states + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help +``` + +### lotus-miner log list +``` +NAME: + lotus-miner log list - List log systems + +USAGE: + lotus-miner log list [command options] [arguments...] + +OPTIONS: + --help, -h show help +``` + +### lotus-miner log set-level +``` +NAME: + lotus-miner log set-level - Set log level + +USAGE: + lotus-miner log set-level [command options] [level] + +DESCRIPTION: + Set the log level for logging systems: + + The system flag can be specified multiple times. + + eg) log set-level --system chain --system chainxchg debug + + Available Levels: + debug + info + warn + error + + Environment Variables: + GOLOG_LOG_LEVEL - Default log level for all log systems + GOLOG_LOG_FMT - Change output log format (json, nocolor) + GOLOG_FILE - Write logs to file + GOLOG_OUTPUT - Specify whether to output to file, stderr, stdout or a combination, i.e. file+stderr + + +OPTIONS: + --system value [ --system value ] limit to log system + --help, -h show help +``` + +### lotus-miner log alerts +``` +NAME: + lotus-miner log alerts - Get alert states + +USAGE: + lotus-miner log alerts [command options] [arguments...] + +OPTIONS: + --all get all (active and inactive) alerts (default: false) + --help, -h show help +``` + +## lotus-miner wait-api +``` +NAME: + lotus-miner wait-api - Wait for lotus api to come online + +USAGE: + lotus-miner wait-api [command options] [arguments...] + +OPTIONS: + --timeout value duration to wait till fail (default: 30s) + --help, -h show help +``` + +## lotus-miner fetch-params +``` +NAME: + lotus-miner fetch-params - Fetch proving parameters + +USAGE: + lotus-miner fetch-params [command options] [sectorSize] + +OPTIONS: + --help, -h show help +``` + ## lotus-miner version ``` NAME: @@ -444,156 +587,6 @@ OPTIONS: --help, -h show help ``` -## lotus-miner auth -``` -NAME: - lotus-miner auth - Manage RPC permissions - -USAGE: - lotus-miner auth command [command options] [arguments...] - -COMMANDS: - create-token Create token - api-info Get token with API info required to connect to this node - help, h Shows a list of commands or help for one command - -OPTIONS: - --help, -h show help -``` - -### lotus-miner auth create-token -``` -NAME: - lotus-miner auth create-token - Create token - -USAGE: - lotus-miner auth create-token [command options] [arguments...] - -OPTIONS: - --perm value permission to assign to the token, one of: read, write, sign, admin - --help, -h show help -``` - -### lotus-miner auth api-info -``` -NAME: - lotus-miner auth api-info - Get token with API info required to connect to this node - -USAGE: - lotus-miner auth api-info [command options] [arguments...] - -OPTIONS: - --perm value permission to assign to the token, one of: read, write, sign, admin - --help, -h show help -``` - -## lotus-miner log -``` -NAME: - lotus-miner log - Manage logging - -USAGE: - lotus-miner log command [command options] [arguments...] - -COMMANDS: - list List log systems - set-level Set log level - alerts Get alert states - help, h Shows a list of commands or help for one command - -OPTIONS: - --help, -h show help -``` - -### lotus-miner log list -``` -NAME: - lotus-miner log list - List log systems - -USAGE: - lotus-miner log list [command options] [arguments...] - -OPTIONS: - --help, -h show help -``` - -### lotus-miner log set-level -``` -NAME: - lotus-miner log set-level - Set log level - -USAGE: - lotus-miner log set-level [command options] [level] - -DESCRIPTION: - Set the log level for logging systems: - - The system flag can be specified multiple times. - - eg) log set-level --system chain --system chainxchg debug - - Available Levels: - debug - info - warn - error - - Environment Variables: - GOLOG_LOG_LEVEL - Default log level for all log systems - GOLOG_LOG_FMT - Change output log format (json, nocolor) - GOLOG_FILE - Write logs to file - GOLOG_OUTPUT - Specify whether to output to file, stderr, stdout or a combination, i.e. file+stderr - - -OPTIONS: - --system value [ --system value ] limit to log system - --help, -h show help -``` - -### lotus-miner log alerts -``` -NAME: - lotus-miner log alerts - Get alert states - -USAGE: - lotus-miner log alerts [command options] [arguments...] - -OPTIONS: - --all get all (active and inactive) alerts (default: false) - --help, -h show help -``` - -## lotus-miner wait-api -``` -NAME: - lotus-miner wait-api - Wait for lotus api to come online - -USAGE: - lotus-miner wait-api [command options] [arguments...] - -CATEGORY: - DEVELOPER - -OPTIONS: - --timeout value duration to wait till fail (default: 30s) - --help, -h show help -``` - -## lotus-miner fetch-params -``` -NAME: - lotus-miner fetch-params - Fetch proving parameters - -USAGE: - lotus-miner fetch-params [command options] [sectorSize] - -CATEGORY: - DEVELOPER - -OPTIONS: - --help, -h show help -``` - ## lotus-miner sectors ``` NAME: @@ -1039,7 +1032,6 @@ OPTIONS: --deadline value the deadline to compact the partitions in (default: 0) --partitions value [ --partitions value ] list of partitions to compact sectors in --really-do-it Actually send transaction performing the action (default: false) - --actor value Specify the address of the miner to run this command --help, -h show help ``` diff --git a/documentation/en/cli-sptool.md b/documentation/en/cli-sptool.md new file mode 100644 index 000000000..7b888a884 --- /dev/null +++ b/documentation/en/cli-sptool.md @@ -0,0 +1,441 @@ +# sptool +``` +NAME: + sptool - Manage Filecoin Miner Actor + +USAGE: + sptool [global options] command [command options] [arguments...] + +VERSION: + 1.27.0-dev + +COMMANDS: + actor Manage Filecoin Miner Actor Metadata + info Print miner actor info + sectors interact with sector store + proving View proving information + help, h Shows a list of commands or help for one command + +GLOBAL OPTIONS: + --log-level value (default: "info") + --actor value miner actor to manage [$SP_ADDRESS] + --help, -h show help + --version, -v print the version +``` + +## sptool actor +``` +NAME: + sptool actor - Manage Filecoin Miner Actor Metadata + +USAGE: + sptool actor command [command options] [arguments...] + +COMMANDS: + set-addresses, set-addrs set addresses that your miner can be publicly dialed on + withdraw withdraw available balance to beneficiary + repay-debt pay down a miner's debt + set-peer-id set the peer id of your miner + set-owner Set owner address (this command should be invoked twice, first with the old owner as the senderAddress, and then with the new owner) + control Manage control addresses + propose-change-worker Propose a worker address change + confirm-change-worker Confirm a worker address change + compact-allocated compact allocated sectors bitfield + propose-change-beneficiary Propose a beneficiary address change + confirm-change-beneficiary Confirm a beneficiary address change + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help +``` + +### sptool actor set-addresses +``` +NAME: + sptool actor set-addresses - set addresses that your miner can be publicly dialed on + +USAGE: + sptool actor set-addresses [command options] + +OPTIONS: + --from value optionally specify the account to send the message from + --gas-limit value set gas limit (default: 0) + --unset unset address (default: false) + --help, -h show help +``` + +### sptool actor withdraw +``` +NAME: + sptool actor withdraw - withdraw available balance to beneficiary + +USAGE: + sptool actor withdraw [command options] [amount (FIL)] + +OPTIONS: + --confidence value number of block confirmations to wait for (default: 5) + --beneficiary send withdraw message from the beneficiary address (default: false) + --help, -h show help +``` + +### sptool actor repay-debt +``` +NAME: + sptool actor repay-debt - pay down a miner's debt + +USAGE: + sptool actor repay-debt [command options] [amount (FIL)] + +OPTIONS: + --from value optionally specify the account to send funds from + --help, -h show help +``` + +### sptool actor set-peer-id +``` +NAME: + sptool actor set-peer-id - set the peer id of your miner + +USAGE: + sptool actor set-peer-id [command options] + +OPTIONS: + --gas-limit value set gas limit (default: 0) + --help, -h show help +``` + +### sptool actor set-owner +``` +NAME: + sptool actor set-owner - Set owner address (this command should be invoked twice, first with the old owner as the senderAddress, and then with the new owner) + +USAGE: + sptool actor set-owner [command options] [newOwnerAddress senderAddress] + +OPTIONS: + --really-do-it Actually send transaction performing the action (default: false) + --help, -h show help +``` + +### sptool actor control +``` +NAME: + sptool actor control - Manage control addresses + +USAGE: + sptool actor control command [command options] [arguments...] + +COMMANDS: + list Get currently set control addresses. Note: This excludes most roles as they are not known to the immediate chain state. + set Set control address(-es) + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help +``` + +#### sptool actor control list +``` +NAME: + sptool actor control list - Get currently set control addresses. Note: This excludes most roles as they are not known to the immediate chain state. + +USAGE: + sptool actor control list [command options] [arguments...] + +OPTIONS: + --verbose (default: false) + --help, -h show help +``` + +#### sptool actor control set +``` +NAME: + sptool actor control set - Set control address(-es) + +USAGE: + sptool actor control set [command options] [...address] + +OPTIONS: + --really-do-it Actually send transaction performing the action (default: false) + --help, -h show help +``` + +### sptool actor propose-change-worker +``` +NAME: + sptool actor propose-change-worker - Propose a worker address change + +USAGE: + sptool actor propose-change-worker [command options] [address] + +OPTIONS: + --really-do-it Actually send transaction performing the action (default: false) + --help, -h show help +``` + +### sptool actor confirm-change-worker +``` +NAME: + sptool actor confirm-change-worker - Confirm a worker address change + +USAGE: + sptool actor confirm-change-worker [command options] [address] + +OPTIONS: + --really-do-it Actually send transaction performing the action (default: false) + --help, -h show help +``` + +### sptool actor compact-allocated +``` +NAME: + sptool actor compact-allocated - compact allocated sectors bitfield + +USAGE: + sptool actor compact-allocated [command options] [arguments...] + +OPTIONS: + --mask-last-offset value Mask sector IDs from 0 to 'highest_allocated - offset' (default: 0) + --mask-upto-n value Mask sector IDs from 0 to 'n' (default: 0) + --really-do-it Actually send transaction performing the action (default: false) + --help, -h show help +``` + +### sptool actor propose-change-beneficiary +``` +NAME: + sptool actor propose-change-beneficiary - Propose a beneficiary address change + +USAGE: + sptool actor propose-change-beneficiary [command options] [beneficiaryAddress quota expiration] + +OPTIONS: + --really-do-it Actually send transaction performing the action (default: false) + --overwrite-pending-change Overwrite the current beneficiary change proposal (default: false) + --actor value specify the address of miner actor + --help, -h show help +``` + +### sptool actor confirm-change-beneficiary +``` +NAME: + sptool actor confirm-change-beneficiary - Confirm a beneficiary address change + +USAGE: + sptool actor confirm-change-beneficiary [command options] [minerID] + +OPTIONS: + --really-do-it Actually send transaction performing the action (default: false) + --existing-beneficiary send confirmation from the existing beneficiary address (default: false) + --new-beneficiary send confirmation from the new beneficiary address (default: false) + --help, -h show help +``` + +## sptool info +``` +NAME: + sptool info - Print miner actor info + +USAGE: + sptool info [command options] [arguments...] + +OPTIONS: + --help, -h show help +``` + +## sptool sectors +``` +NAME: + sptool sectors - interact with sector store + +USAGE: + sptool sectors command [command options] [arguments...] + +COMMANDS: + status Get the seal status of a sector by its number + list List sectors + precommits Print on-chain precommit info + check-expire Inspect expiring sectors + expired Get or cleanup expired sectors + extend Extend expiring sectors while not exceeding each sector's max life + compact-partitions removes dead sectors from partitions and reduces the number of partitions used if possible + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help +``` + +### sptool sectors status +``` +NAME: + sptool sectors status - Get the seal status of a sector by its number + +USAGE: + sptool sectors status [command options] + +OPTIONS: + --log, -l display event log (default: false) + --on-chain-info, -c show sector on chain info (default: false) + --partition-info, -p show partition related info (default: false) + --proof print snark proof bytes as hex (default: false) + --help, -h show help +``` + +### sptool sectors list +``` +NAME: + sptool sectors list - List sectors + +USAGE: + sptool sectors list [command options] [arguments...] + +OPTIONS: + --help, -h show help +``` + +### sptool sectors precommits +``` +NAME: + sptool sectors precommits - Print on-chain precommit info + +USAGE: + sptool sectors precommits [command options] [arguments...] + +OPTIONS: + --help, -h show help +``` + +### sptool sectors check-expire +``` +NAME: + sptool sectors check-expire - Inspect expiring sectors + +USAGE: + sptool sectors check-expire [command options] [arguments...] + +OPTIONS: + --cutoff value skip sectors whose current expiration is more than epochs from now, defaults to 60 days (default: 172800) + --help, -h show help +``` + +### sptool sectors expired +``` +NAME: + sptool sectors expired - Get or cleanup expired sectors + +USAGE: + sptool sectors expired [command options] [arguments...] + +OPTIONS: + --expired-epoch value epoch at which to check sector expirations (default: WinningPoSt lookback epoch) + --help, -h show help +``` + +### sptool sectors extend +``` +NAME: + sptool sectors extend - Extend expiring sectors while not exceeding each sector's max life + +USAGE: + sptool sectors extend [command options] + +OPTIONS: + --from value only consider sectors whose current expiration epoch is in the range of [from, to], defaults to: now + 120 (1 hour) (default: 0) + --to value only consider sectors whose current expiration epoch is in the range of [from, to], defaults to: now + 92160 (32 days) (default: 0) + --sector-file value provide a file containing one sector number in each line, ignoring above selecting criteria + --exclude value optionally provide a file containing excluding sectors + --extension value try to extend selected sectors by this number of epochs, defaults to 540 days (default: 1555200) + --new-expiration value try to extend selected sectors to this epoch, ignoring extension (default: 0) + --only-cc only extend CC sectors (useful for making sector ready for snap upgrade) (default: false) + --drop-claims drop claims for sectors that can be extended, but only by dropping some of their verified power claims (default: false) + --tolerance value don't try to extend sectors by fewer than this number of epochs, defaults to 7 days (default: 20160) + --max-fee value use up to this amount of FIL for one message. pass this flag to avoid message congestion. (default: "0") + --max-sectors value the maximum number of sectors contained in each message (default: 0) + --really-do-it pass this flag to really extend sectors, otherwise will only print out json representation of parameters (default: false) + --help, -h show help +``` + +### sptool sectors compact-partitions +``` +NAME: + sptool sectors compact-partitions - removes dead sectors from partitions and reduces the number of partitions used if possible + +USAGE: + sptool sectors compact-partitions [command options] [arguments...] + +OPTIONS: + --deadline value the deadline to compact the partitions in (default: 0) + --partitions value [ --partitions value ] list of partitions to compact sectors in + --really-do-it Actually send transaction performing the action (default: false) + --help, -h show help +``` + +## sptool proving +``` +NAME: + sptool proving - View proving information + +USAGE: + sptool proving command [command options] [arguments...] + +COMMANDS: + info View current state information + deadlines View the current proving period deadlines information + deadline View the current proving period deadline information by its index + faults View the currently known proving faulty sectors information + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help +``` + +### sptool proving info +``` +NAME: + sptool proving info - View current state information + +USAGE: + sptool proving info [command options] [arguments...] + +OPTIONS: + --help, -h show help +``` + +### sptool proving deadlines +``` +NAME: + sptool proving deadlines - View the current proving period deadlines information + +USAGE: + sptool proving deadlines [command options] [arguments...] + +OPTIONS: + --all, -a Count all sectors (only live sectors are counted by default) (default: false) + --help, -h show help +``` + +### sptool proving deadline +``` +NAME: + sptool proving deadline - View the current proving period deadline information by its index + +USAGE: + sptool proving deadline [command options] + +OPTIONS: + --sector-nums, -n Print sector/fault numbers belonging to this deadline (default: false) + --bitfield, -b Print partition bitfield stats (default: false) + --help, -h show help +``` + +### sptool proving faults +``` +NAME: + sptool proving faults - View the currently known proving faulty sectors information + +USAGE: + sptool proving faults [command options] [arguments...] + +OPTIONS: + --help, -h show help +``` diff --git a/itests/cli_test.go b/itests/cli_test.go index a323c0863..d2a087635 100644 --- a/itests/cli_test.go +++ b/itests/cli_test.go @@ -6,7 +6,7 @@ import ( "testing" "time" - "github.com/filecoin-project/lotus/cli" + "github.com/filecoin-project/lotus/cli/clicommands" "github.com/filecoin-project/lotus/itests/kit" ) @@ -23,5 +23,5 @@ func TestClient(t *testing.T) { blockTime := 5 * time.Millisecond client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC()) ens.InterconnectAll().BeginMining(blockTime) - kit.RunClientTest(t, cli.Commands, client) + kit.RunClientTest(t, clicommands.Commands, client) } diff --git a/itests/gateway_test.go b/itests/gateway_test.go index d20b3bd1a..2dc4e1034 100644 --- a/itests/gateway_test.go +++ b/itests/gateway_test.go @@ -24,7 +24,7 @@ import ( "github.com/filecoin-project/lotus/api/client" "github.com/filecoin-project/lotus/chain/stmgr" "github.com/filecoin-project/lotus/chain/types" - "github.com/filecoin-project/lotus/cli" + "github.com/filecoin-project/lotus/cli/clicommands" "github.com/filecoin-project/lotus/gateway" "github.com/filecoin-project/lotus/itests/kit" "github.com/filecoin-project/lotus/itests/multisig" @@ -231,7 +231,7 @@ func TestGatewayCLIDealFlow(t *testing.T) { ctx := context.Background() nodes := startNodesWithFunds(ctx, t, blocktime, maxLookbackCap, maxStateWaitLookbackLimit) - kit.RunClientTest(t, cli.Commands, nodes.lite) + kit.RunClientTest(t, clicommands.Commands, nodes.lite) } type testNodes struct { diff --git a/itests/multisig/suite.go b/itests/multisig/suite.go index 9a81d0bf9..61ca68d5e 100644 --- a/itests/multisig/suite.go +++ b/itests/multisig/suite.go @@ -13,14 +13,14 @@ import ( "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/chain/types" - "github.com/filecoin-project/lotus/cli" + "github.com/filecoin-project/lotus/cli/clicommands" "github.com/filecoin-project/lotus/itests/kit" ) func RunMultisigTests(t *testing.T, client *kit.TestFullNode) { // Create mock CLI ctx := context.Background() - mockCLI := kit.NewMockCLI(ctx, t, cli.Commands, api.NodeFull) + mockCLI := kit.NewMockCLI(ctx, t, clicommands.Commands, api.NodeFull) clientCLI := mockCLI.Client(client.ListenAddr) // Create some wallets on the node to use for testing multisig diff --git a/itests/paych_cli_test.go b/itests/paych_cli_test.go index f86f5d8de..1079aade9 100644 --- a/itests/paych_cli_test.go +++ b/itests/paych_cli_test.go @@ -25,7 +25,7 @@ import ( "github.com/filecoin-project/lotus/chain/actors/builtin/paych" "github.com/filecoin-project/lotus/chain/events" "github.com/filecoin-project/lotus/chain/types" - "github.com/filecoin-project/lotus/cli" + "github.com/filecoin-project/lotus/cli/clicommands" "github.com/filecoin-project/lotus/itests/kit" ) @@ -51,7 +51,7 @@ func TestPaymentChannelsBasic(t *testing.T) { creatorAddr, receiverAddr := startPaychCreatorReceiverMiner(ctx, t, &paymentCreator, &paymentReceiver, blocktime) // Create mock CLI - mockCLI := kit.NewMockCLI(ctx, t, cli.Commands, api.NodeFull) + mockCLI := kit.NewMockCLI(ctx, t, clicommands.Commands, api.NodeFull) creatorCLI := mockCLI.Client(paymentCreator.ListenAddr) receiverCLI := mockCLI.Client(paymentReceiver.ListenAddr) @@ -126,7 +126,7 @@ func TestPaymentChannelStatus(t *testing.T) { creatorAddr, receiverAddr := startPaychCreatorReceiverMiner(ctx, t, &paymentCreator, &paymentReceiver, blocktime) // Create mock CLI - mockCLI := kit.NewMockCLI(ctx, t, cli.Commands, api.NodeFull) + mockCLI := kit.NewMockCLI(ctx, t, clicommands.Commands, api.NodeFull) creatorCLI := mockCLI.Client(paymentCreator.ListenAddr) // creator: paych status-by-from-to @@ -212,7 +212,7 @@ func TestPaymentChannelVouchers(t *testing.T) { creatorAddr, receiverAddr := startPaychCreatorReceiverMiner(ctx, t, &paymentCreator, &paymentReceiver, blocktime) // Create mock CLI - mockCLI := kit.NewMockCLI(ctx, t, cli.Commands, api.NodeFull) + mockCLI := kit.NewMockCLI(ctx, t, clicommands.Commands, api.NodeFull) creatorCLI := mockCLI.Client(paymentCreator.ListenAddr) receiverCLI := mockCLI.Client(paymentReceiver.ListenAddr) @@ -350,7 +350,7 @@ func TestPaymentChannelVoucherCreateShortfall(t *testing.T) { creatorAddr, receiverAddr := startPaychCreatorReceiverMiner(ctx, t, &paymentCreator, &paymentReceiver, blocktime) // Create mock CLI - mockCLI := kit.NewMockCLI(ctx, t, cli.Commands, api.NodeFull) + mockCLI := kit.NewMockCLI(ctx, t, clicommands.Commands, api.NodeFull) creatorCLI := mockCLI.Client(paymentCreator.ListenAddr) // creator: paych add-funds diff --git a/node/impl/storminer.go b/node/impl/storminer.go index 90248a355..bd4824940 100644 --- a/node/impl/storminer.go +++ b/node/impl/storminer.go @@ -1377,15 +1377,15 @@ func (sm *StorageMinerAPI) RuntimeSubsystems(context.Context) (res api.MinerSubs } func (sm *StorageMinerAPI) ActorWithdrawBalance(ctx context.Context, amount abi.TokenAmount) (cid.Cid, error) { - return sm.withdrawBalance(ctx, amount, true) + return WithdrawBalance(ctx, sm.Full, sm.Miner.Address(), amount, true) } func (sm *StorageMinerAPI) BeneficiaryWithdrawBalance(ctx context.Context, amount abi.TokenAmount) (cid.Cid, error) { - return sm.withdrawBalance(ctx, amount, false) + return WithdrawBalance(ctx, sm.Full, sm.Miner.Address(), amount, false) } -func (sm *StorageMinerAPI) withdrawBalance(ctx context.Context, amount abi.TokenAmount, fromOwner bool) (cid.Cid, error) { - available, err := sm.Full.StateMinerAvailableBalance(ctx, sm.Miner.Address(), types.EmptyTSK) +func WithdrawBalance(ctx context.Context, full api.FullNode, maddr address.Address, amount abi.TokenAmount, fromOwner bool) (cid.Cid, error) { + available, err := full.StateMinerAvailableBalance(ctx, maddr, types.EmptyTSK) if err != nil { return cid.Undef, xerrors.Errorf("Error getting miner balance: %w", err) } @@ -1405,7 +1405,7 @@ func (sm *StorageMinerAPI) withdrawBalance(ctx context.Context, amount abi.Token return cid.Undef, err } - mi, err := sm.Full.StateMinerInfo(ctx, sm.Miner.Address(), types.EmptyTSK) + mi, err := full.StateMinerInfo(ctx, maddr, types.EmptyTSK) if err != nil { return cid.Undef, xerrors.Errorf("Error getting miner's owner address: %w", err) } @@ -1417,8 +1417,8 @@ func (sm *StorageMinerAPI) withdrawBalance(ctx context.Context, amount abi.Token sender = mi.Beneficiary } - smsg, err := sm.Full.MpoolPushMessage(ctx, &types.Message{ - To: sm.Miner.Address(), + smsg, err := full.MpoolPushMessage(ctx, &types.Message{ + To: maddr, From: sender, Value: types.NewInt(0), Method: builtintypes.MethodsMiner.WithdrawBalance, diff --git a/scripts/generate-lotus-cli.py b/scripts/generate-lotus-cli.py index 305716b09..c06e2f2a5 100644 --- a/scripts/generate-lotus-cli.py +++ b/scripts/generate-lotus-cli.py @@ -51,8 +51,12 @@ if __name__ == "__main__": for e in [ "LOTUS_PATH", "LOTUS_MARKETS_PATH", "LOTUS_MINER_PATH", "LOTUS_STORAGE_PATH", "LOTUS_WORKER_PATH", "WORKER_PATH", "LOTUS_PANIC_REPORT_PATH", "WALLET_PATH" ]: os.environ.pop(e, None) + # Set env var telling the binaries that we're generating docs + os.putenv("LOTUS_DOCS_GENERATION", "1") + os.putenv("LOTUS_VERSION_IGNORE_COMMIT", "1") generate_lotus_cli('lotus') generate_lotus_cli('lotus-miner') generate_lotus_cli('lotus-worker') - generate_lotus_cli('curio') \ No newline at end of file + generate_lotus_cli('curio') + generate_lotus_cli('sptool') \ No newline at end of file From 86279403ead86d53ef646e2414dffc22ccd1607c Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Tue, 2 Apr 2024 16:15:18 +1100 Subject: [PATCH 304/394] docs: actor events API and DDO documentation (#11615) --- documentation/en/actor-events-api.md | 430 ++++++++++++++++++ .../en/data-onboarding-visibility.md | 71 +++ 2 files changed, 501 insertions(+) create mode 100644 documentation/en/actor-events-api.md create mode 100644 documentation/en/data-onboarding-visibility.md diff --git a/documentation/en/actor-events-api.md b/documentation/en/actor-events-api.md new file mode 100644 index 000000000..801d101b5 --- /dev/null +++ b/documentation/en/actor-events-api.md @@ -0,0 +1,430 @@ +# Actor Events and Lotus APIs + +* [Background](#background) +* [ActorEvent structure](#actorevent-structure) +* [Querying Lotus for ActorEvents](#querying-lotus-for-actorevents) +* [Retrieving events from message receipts](#retrieving-events-from-message-receipts) +* [Current builtin actor event schemas](#current-builtin-actor-event-schemas) + * [Verified registry actor events](#verified-registry-actor-events) + * [Verifier balance](#verifier-balance) + * [Allocation](#allocation) + * [Allocation removed](#allocation-removed) + * [Claim](#claim) + * [Claim updated](#claim-updated) + * [Claim removed](#claim-removed) + * [Market actor events](#market-actor-events) + * [Deal published](#deal-published) + * [Deal activated](#deal-activated) + * [Deal terminated](#deal-terminated) + * [Deal completed](#deal-completed) + * [Miner actor events](#miner-actor-events) + * [Sector precommitted](#sector-precommitted) + * [Sector activated](#sector-activated) + * [Sector updated](#sector-updated) + * [Sector terminated](#sector-terminated) + +## Background + +Actor events are a fire-and-forget mechanism for actors in Filecoin to signal events that occur during execution of their methods to external observers. Actor events are intended to be used by tooling and applications that need to observe and react to events that occur within the chain. The events themselves are not stored in chain state, although a root CID for an array (AMT) of all events emitted for a single message is recorded on message receipts, which are themselves referenced as an array (AMT) in the `ParentMessageReceipts` in each `BlockHeader` of a tipset. A node may optionally retain historical events for querying, but this is not guaranteed and not essential as it does not affect the chain state. + +The FVM already has this capability and new events for builtin actors have been added to support a range of new features, starting at network version 22 with a focus on some information gaps for consumers of data onboarding activity insight due to the introduction of [Direct Data Onboarding (DDO)](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0076.md), plus some additional events related to data onboarding, deal lifecycles, sector lifecycles, and DataCap activity. Additional events are expected to be added in the future to support other features and use cases. + +Builtin actor events share basic similarities to the existing events emitted by user-programmed actors in FVM, but each have a specific schema that reflects their specific concerns. They also all use CBOR encoding for their values. There are also new APIs in Lotus to support querying for these events that bear some similarities to the existing FEVM `Eth*` APIs for querying events but are unique to builtin actors. + +## ActorEvent structure + +Introduced in [FIP-0049](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0049.md), events use a structured logging style of composition, containing a list of entries that define properties of the event. The log entries are described below as `EventEntry` and have the same schema for user-programmed and builtin actor events. `ActorEvent` is specifically for representing builtin actor events and includes the list of entries, the actor that emitted the event, and some metadata about the event. + +```ipldsch +type ActorEvent struct { + entries [EventEntry] # Event entries in log form. + emitter Address # Filecoin address of the actor that emitted this event. + # Reverted is set to true if the message that produced this event was reverted because of a + # network re-org in that case, the event should be considered as reverted as well. + reverted Bool + height ChainEpoch # Height of the tipset that contained the message that produced this event. + tipsetCid &Any # CID of the tipset that contained the message that produced this event. + msgCid &Any # CID of message that produced this event. +} + +type EventEntry struct { + flags Int # A bitmap conveying metadata or hints about this entry. + key String # The key of this entry. + codec Int # The value's IPLD codec. + value Bytes # The value of this entry as a byte string, encoded with 'codec'. +} +``` + +A `flags` field is used to convey metadata or hints about the entry, currently this is used to provide an indication of the suitability of that field for indexing. Suitability for indexing is only a hint, and typically relates to the queriability of the content of that field. + +* A `flag` of `0x00` indicates that neither the key nor value are suitable for indexing. +* A `flag` of `0x01` indicates that the key only is suitable for indexing. +* A `flag` of `0x02` indicates that the value is suitable for indexing. +* A `flag` of `0x03` indicates that both the key and value are suitable for indexing. + +Typically events contain entires that use either use `0x01` or `0x03` flags. + +The structured logging style of composition should be seen in contrast to an alternative representation as a plain map or struct where the keys represent the fields of the event and the values represent the values of those fields. Some entries may duplicate keys, in which case that particular field of the event could be represented as an array. Builtin actor events are sufficiently well defined that translation to such a format is possible, but left up to the user. + +## Querying Lotus for ActorEvents + +Two Lotus APIs are provided that can be used to obtain direct access to events stored on the node being queried (a node may not have all historical events stored and available for query): + +- **[`GetActorEventsRaw`](https://github.com/filecoin-project/lotus/blob/master/documentation/en/api-v1-unstable-methods.md#GetActorEventsRaw)** will return all available historical actor events that match a given *filter* argument. +- **[`SubscribeActorEventsRaw`](https://github.com/filecoin-project/lotus/blob/master/documentation/en/api-v1-unstable-methods.md#SubscribeActorEventsRaw)** will return a long-lived stream providing all available actor events that match a given *filter* argument as they are generated. Optionally also providing a list of historical events. This API is available via websocket from the Lotus API RPC. + +Both APIs take an `EventFilter` as an argument to determine which events to return. This event filter optionally comprises the following: + +- `fromEpoch` determines when to start looking for matching events, either an epoch (in hex form), the string `earliest` or `latest` . A node is not guaranteed to have historical blocks for a particular epoch however `earliest` is intended to provide events from the begining of the available list. +- `toEpoch` determines when to stop looking for matching events, either an epoch (in hex form), the string `earliest` or `latest`. +- `addresses` will match a list of addresses that an event comes *from* (currently just a builtin actor address). +- `fields` is a key to value mapping that matches specific event entries. Each field being matched is a property in the `fields` map and the value of that property is an array of maps, where each entry is a possible matching value for that entry. Each possible match contains a `codec` integer (currently just CBOR `0x51` for builtin actor events described in this document) and a `value` bytes blob (Base64 encoded) of the encoded field value (e.g. a Base64 encoded form of a CBOR encoded key string, such as an actor ID or an event ID). Matching first involves finding if an event’s entries contain one of the desired `key`s, then checking that one of the value matchers for that `key` field matches the value. Value matching is performed both on the `codec` and the `value` bytes. If an event’s entry is matched, the entire event is considered a match. This may be used to query for particular event types, such as `allocation`. +An example `fields` with a single matcher would look like: `"fields": { "abc": [{ "codec": 81, "value": "ZGRhdGE=" }]}` where the key being matched is `abc` with the CBOR codec (`0x51` = `81`) and value is the unicode string `data` encoded as CBOR (then encoded in Base64 to supply to the filter). +- `tipsetCid` matches a particular TipSet. If this is provided, both `fromBlock` and `toBlock` will be ignored. + +Described as an [IPLD Schema](https://ipld.io/docs/schemas/), the event filter is: + +```ipldsch +type EventFilter struct { + fromEpoch optional String + toEpoch optional String + addresses optional [Address] + fields optional {String:[ActorEventValue]} + tipsetCid optional &Any +} + +type Address string # Address of an actor + +type ActorEventValue struct { + codec Int # typically the CBOR codec (0x51) + value Bytes # typically the CBOR encoded value +} +``` + +## Retrieving events from message receipts + +The Lotus API `ChainGetEvents` can be used to retrieve events given an event root CID. This CID is attached to the message receipt that generated the events. The `StateSearchMsg` API can be used to retrieve the message receipt given a message CID, the receipt contains the `EventsRoot` CID. The events returned from `ChainGetEvents` contain roughly the same information as the `ActorEvent` structure, including the `EventEntry` log array. + +## Current builtin actor event schemas + +Schemas for currently implemented builtin actor events are provided below. They follow the log structure, where each line in the schema table represents an `EventEntry` in the `ActorEvent` entry list. For simplicity, the `flags` are presented as either `k` for `0x01` (index key) or `kv` for `0x03` (index key and value) and the `codec` is always `0x51` for builtin actors so is omitted. + +_Note that the "bigint" CBOR encoding format used below is the same as is used for encoding bigints on the Filecoin chain: a byte array representing a big-endian unsigned integer, compatible with the Golang `big.Int` byte representation, with a `0x00` (positive) or `0x01` (negative) prefix; with a zero-length array representing a value of `0`._ + +The following events are defined in FIP-0083. Additional events will be added here as they are accepted by FIP. + +### Verified registry actor events + +#### Verifier balance + +The `verifier-balance` event is emitted when the balance of a verifier is updated in the Verified Registry actor. + +| Key | Value | Flags | +|-------------|-------------------------------------|-------| +| `"$type"` | `"verifier-balance"` (string) | kv | +| `"verifier"`| (int) | kv | +| `"balance"` | (bigint) | k | + +In structured form, this event would look like: + +```ipldsch +type DataCap Bytes # A bigint representing a DataCap + +type VerifierBalanceEvent struct { + verifier Int + balance DataCap +} +``` + +#### Allocation + +The `allocation` event is emitted when a verified client allocates DataCap to a specific data piece and storage provider. + +| Key | Value | Flags | +| ------------ | ----------------------- | ----- | +| `"$type"` | `"allocation"` (string) | kv | +| `"id"` | (int) | kv | +| `"client"` | (int) | kv | +| `"provider"` | (int) | kv | + +In structured form, this event would look like: + +```ipldsch +type AllocationEvent struct { + id Int + client Int + provider Int +} +``` + +#### Allocation removed + +The `allocation-removed` event is emitted when a DataCap allocation that is past its expiration epoch is removed. + +| Key | Value | Flags | +| ------------ | ------------------------------- | ----- | +| `"$type"` | `"allocation-removed"` (string) | kv | +| `"id"` | (int) | kv | +| `"client"` | (int) | kv | +| `"provider"` | (int) | kv | + +In structured form, this event would look like: + +```ipldsch +type AllocationRemovedEvent struct { + id Int + client Int + provider Int +} +``` + +#### Claim + +The `claim` event is emitted when a client allocation is claimed by a storage provider after the corresponding verified data is provably committed to the chain. + +| Key | Value | Flags | +| ------------ | ----------------------- | ----- | +| `"$type"` | `"claim"` (string) | kv | +| `"id"` | (int) | kv | +| `"client"` | (int) | kv | +| `"provider"` | (int) | kv | + +In structured form, this event would look like: + +```ipldsch +type ClaimEvent struct { + id Int + client Int + provider Int +} +``` + +#### Claim updated + +The `claim-updated` event is emitted when the term of an existing allocation is extended by the client. + +| Key | Value | Flags | +| ------------ | -------------------------- | ----- | +| `"$type"` | `"claim-updated"` (string) | kv | +| `"id"` | (int) | kv | +| `"client"` | (int) | kv | +| `"provider"` | (int) | kv | + +In structured form, this event would look like: + +```ipldsch +type ClaimUpdatedEvent struct { + id Int + client Int + provider Int +} +``` + +#### Claim removed + +The `claim-removed` event is emitted when an expired claim is removed by the Verified Registry actor. + +| Key | Value | Flags | +| ------------ | -------------------------- | ----- | +| `"$type"` | `"claim-removed"` (string) | kv | +| `"id"` | (int) | kv | +| `"client"` | (int) | kv | +| `"provider"` | (int) | kv | + +In structured form, this event would look like: + +```ipldsch +type ClaimRemovedEvent struct { + id Int + client Int + provider Int +} +``` + +### Market actor events + +The Market actor emits the following deal lifecycle events: + +#### Deal published + +The `deal-published` event is emitted for each new deal that is successfully published by a storage provider. + +| Key | Value | Flags | +| ----------- | --------------------------------- | ----- | +| `"$type"` | `"deal-published"` (string) | kv | +| `"id"` | (int) | kv | +| `"client"` | (int) | kv | +| `"provider"`| (int) | kv | + +In structured form, this event would look like: + +```ipldsch +type DealPublishedEvent struct { + id Int + client Int + provider Int +} +``` + +#### Deal activated + +The `deal-activated` event is emitted for each deal that is successfully activated. + +| Key | Value | Flags | +| ------------ | --------------------------------- | ----- | +| `"$type"` | `"deal-activated"` (string) | kv | +| `"id"` | (int) | kv | +| `"client"` | (int) | kv | +| `"provider"` | (int) | kv | + +In structured form, this event would look like: + +```ipldsch +type DealActivatedEvent struct { + id Int + client Int + provider Int +} +``` + +#### Deal terminated + +The `deal-terminated` event is emitted by the market actor cron job when it processes deals that were marked as terminated by the `OnMinerSectorsTerminate` method. + +[FIP-0074](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0074.md) ensures that terminated deals are processed immediately in the `OnMinerSectorsTerminate` method rather than being submitted for deferred processing to the market actor cron job. As of network version 22 this event will be emitted to indicate that a deal has been terminated for deals made after network version 22. + +| Key | Value | Flags | +| ----------- | --------------------------------- | ----- | +| `"$type"` | `"deal-terminated"` (string) | kv | +| `"id"` | (int) | kv | +| `"client"` | (int) | kv | +| `"provider"`| (int) | kv | + +In structured form, this event would look like: + +```ipldsch +type DealTerminatedEvent struct { + id Int + client Int + provider Int +} +``` + +#### Deal completed + +The `deal-completed` event is emitted when a deal is marked as successfully complete by the Market actor cron job. The cron job will deem a deal to be successfully completed if it is past it’s end epoch without being slashed. + +[FIP-0074](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0074.md) ensures that the processing of completed deals is done as part of a method called by the storage provider thus making this event available to clients and also to ensure that storage providers pay the gas costs of processing deal completion and event emission. This applies to new deals made after network version 22. For deals made before network version 22, this event will be emitted by the market actor cron job. + +| Key | Value | Flags | +| ----------- | --------------------------------- | ----- | +| `"$type"` | `"deal-completed"` (string) | kv | +| `"id"` | (int) | kv | +| `"client"` | (int) | kv | +| `"provider"`| (int) | kv | + +In structured form, this event would look like: + +```ipldsch +type DealCompletedEvent struct { + id Int + client Int + provider Int +} +``` + +### Miner actor events + +The Miner actor emits the following sector lifecycle events: + +#### Sector precommitted + +The `sector-precommitted` event is emitted for each new sector that is successfully pre-committed by a storage provider. + +| Key | Value | Flags | +| ---------- | -------------------------------- | ----- | +| `"$type"` | `"sector-precommitted"` (string) | kv | +| `"sector"` | (int) | kv | + +In structured form, this event would look like: + +```ipldsch +type SectorPrecommittedEvent struct { + sector Int +} +``` + +#### Sector activated + +The `sector-activated` event is emitted for each pre-committed sector that is successfully activated by a storage provider. For now, sector activation corresponds 1:1 with prove-committing a sector but this can change in the future. + +| Key | Value | Flags | +| ---------------- | ------------------------------------------------------------- | ----- | +| `"$type"` | `"sector-activated"` (string) | kv | +| `"sector"` | (int) | kv | +| `"unsealed-cid"` | (nullable CID) (null means sector has no data) | kv | +| `"piece-cid"` | (CID) | kv | +| `"piece-size"` | (int) | k | + +_Note that both `"piece-cid"` and `"piece-size"` entries will be included for each piece in the sector, so the keys are repeated._ + +In structured form, this event would look like: + +```ipldsch +type PieceDescription struct { + cid &Any + size Int +} + +type SectorActivatedEvent struct { + sector Int + unsealedCid nullable &Any + pieces [PieceDescription] +} +``` + +#### Sector updated + +The `sector-updated` event is emitted for each CC sector that is updated to contained actual sealed data. + +| Key | Value | Flags | +| --------------- | ------------------------------------------------------------- | ----- | +| `"$type"` | `"sector-updated"` (string) | kv | +| `"sector"` | (int) | kv | +| `"unsealed-cid"`| (nullable CID) (null means sector has no data) | kv | +| `"piece-cid"` | (CID) | kv | +| `"piece-size"` | (int) | k | + +_Note that both `"piece-cid"` and `"piece-size"` entries will be included for each piece in the sector, so the keys are repeated._ + +In structured form, this event would look like: + +```ipldsch +type PieceDescription struct { + cid &Any + size Int +} + +type SectorUpdatedEvent struct { + sector Int + unsealedCid nullable &Any + pieces [PieceDescription] +} +``` + +#### Sector terminated + +The `sector-terminated` event is emitted for each sector that is marked as terminated by a storage provider. + +| Key | Value | Flags | +| ----------- | ------------------------------ | ----- | +| `"$type"` | `"sector-terminated"` (string) | kv | +| `"sector"` | (int) | kv | + +In structured form, this event would look like: + +```ipldsch +type SectorTerminatedEvent struct { + sector Int +} +``` diff --git a/documentation/en/data-onboarding-visibility.md b/documentation/en/data-onboarding-visibility.md new file mode 100644 index 000000000..3225753dd --- /dev/null +++ b/documentation/en/data-onboarding-visibility.md @@ -0,0 +1,71 @@ +# Data Onboarding Visibility + +* [Introduction and background](#introduction-and-background) +* [DDO information flow](#ddo-information-flow) +* [Relevant message contents](#relevant-message-contents) +* [Relevant builtin actor events](#relevant-builtin-actor-events) + +## Introduction and background + +**Direct Data Onboarding** (DDO) as defined in **[FIP-0076](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0076.md)** provides an optional data onboarding path that is both gas-efficient and paves a path toward eventual smart contract mediated onboarding mechanisms. The existing market actor and market actor mediated onboarding pathway remains largely unchanged but is now optional; it is anticipated that the gas savings alone will see a significant shift away from use of the market actor. + +Historically, a large amount of tooling was built around Filecoin that makes use of the market actor (f05) to quantify various data-related metrics. A shift in behaviour toward data onboarding that bypasses the market actor requires adaption in order to have continuity with the some of the data-related metrics being collected. This will continue to be true as Filecoin evolves to enable onboarding mechanisms mediated by smart contracts. + +**Deal-lifecycle actor events** as defined in **[FIP-0083](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0083.md)** and detailed in [Actor Events and Lotus APIs](./actor-events-api.md) introduced the first batch of fire-and-forget externally observable events for builtin actors. The FVM already had this capability and these new first events for builtin actors were added to increase the visibility of information around data onboarding, particularly in light of the introduction of DDO which will require metrics gatherers to rely on mechanisms other than the market actor to collect data. + +Actor events are an optional method for gaining insight into data onboarding activities and data / storage lifecycles. Messages may also be used as a source of truth for data onboarding metrics, but are more complex to consume and may not be suitable for some workflows. For verified (Filecoin Plus) data, the verified registry actor (f06) should be used as the primary source of truth for data lifecycles; however FIP-0076 introduces the possibility of "sparkling data" which is not verified and not mediated through the builtin market actor or possibly any other actor. This data currently still requires piece commitments to be detailed as part of a miner's sector commitments, and so may be observed through messages and actor events that carry sector commitment piece manifests. + +## DDO information flow + +The most basic direct onboarding workflow as viewed by the chain is simply: + +- At PreCommit, an SP must specify a sector’s data commitment (unsealed CID, CommP) *(but does not need to specify the structure of that data nor any deals or verified allocations)*. +- At ProveCommit or ReplicaUpdate, an SP specifies the pieces of data (CommP and their size) comprising a sector in order to satisfy the data commitment. + +This basic form does not touch either the market actor or the verified registry actor. Importantly, it does not result in piece information being stored on chain, even though the ProveCommit message contains this information for the purpose of verifying the sector commitment. This is the most significant change from onboarding mechanics prior to network version 22. + +There are two possible additions to this flow: + +- Prior to PreCommit, an SP publishes a storage deal to the builtin market actor, in which case deal information exists on chain as it does with non-DDO deals today; then + - at ProveCommit, or ReplicaUpdate, the SP can notify an actor of the commitment. Currently this can only be the builtin market actor (in the future this may be a list of arbitrary user defined actors), in which case it will be used to activate a deal previously proposed on chain. +- At ProveCommit, or ReplicaUpdate, the SP can claim DataCap that was previously allocated by the client for a particular piece. + +💡 **The builtin market actor should not be used as single a source of truth regarding data onboarding activities.** The builtin market actor is only a source of truth for data onboarding mediated by the builtin market actor. + +💡 **The builtin market actor should not be used as a source of truth regarding verified claims and metrics related to FIL+ usage (size, clients, profiders).** The `VerifiedClaim` property of `DealState` has been removed from the builtin market actor. Instead, the verified registry should be used as the only source of truth regarding both allocations and claims. + +💡 **Sector data commitments and their constituent pieces are only stored on chain in the verified registry claims in the case of verified data (pieces) onboarded in any mechanism (DDO and/or builtin market actor).** Piece information for data onboarded that is not verified ("sparkling data") and not mediated through the builtin market actor will only appear in messages and actor events. Messages and actor events may be used as a source of truth for data sector commitments. + +## Relevant message contents + +Even though chain state is less informative for data onboarding not mediated through the builtin market actor, messages used for chain execution will continue to provide all relevant information and may be used to determine the number and size of pieces within a sector, as well as any DataCap claimed for specific pieces and therefore the client allocating the DataCap. + +The most important messages for this purpose are as follows: + +At ProveCommit, a [`ProveCommitSectors3`](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0076.md#provecommitsectors3) message will contain a `SectorActivations` property which is a list of `SectorActivationManifest`, one for each sector being activated. Within this per-sector manifest is a list of `Pieces` which details all of the pieces contributing to the sector commitment, each one is a `PieceActivationManifest` of the form: + +```ipldsch +type PieceActivationManifest struct { + CID &Any # Piece data commitment (CommP) + Size Int # Padded piece size + VerifiedAllocationKey nullable VerifiedAllocationKey # Identifies a verified allocation to be claimed + Notify DataActivationNotification # Notifications to be sent to other actors after activation +} +``` + +This manifest contains the piece commitment as well as an optional `VerifiedAllocationKey` which lists a client and an allocation to claim from the verified registry actor. + +At ReplicaUpdate, the [`ProveReplicaUpdates3`](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0076.md#provereplicaupdates3) message will contain a `SectorUpdates` property which is a list of `SectorUpdateManifest`, one for each sector being updated. This manifest mirrors the `SectorActivationManifest` for ProveCommit, containing a list of `Pieces` which may be similarly inspected for the relevant data. + +- **Pieces**: All piece information for each sector's data commitment may be collected from the piece manifests. +- **Verified data**: Claims may be cross-referenced with the verified registry state to access the details of the allocation, including piece information for the claim. The `StateGetClaim` Lotus API call provides this information. + +💡 Making use of the message contents directly is not a trivial activity. Messages need to be filtered by actor and method number, exit code needs to be checked from the receipt, and the parameters would need to be decoded according to the relevant schema for that message. Actor events exist to make this somewhat easier although may not be suitable for some workflows. + +## Relevant builtin actor events + +Depending on usage and data consumption workflow, consuming builtin actor events using the APIs detailed in [Actor Events and Lotus APIs](./actor-events-api.md), may be simpler and more suitable. The following events are relevant to DDO and may be used to determine the number and size of pieces within a sector, as well as any DataCap claimed for specific pieces and therefore the client allocating the DataCap. + +The [`sector-activated`](./actor-events-api.md#sector-activated) and [`sector-updated`](./actor-events-api.md#sector-updated) events are emitted by the miner actor and contain the piece information for each sector. This is submitted to the miner actor by the storage provider in the form of a piece manifest and is summarised as a list of pieces in the events. Both piece CID (CommP) and piece size are available in the event data. + +The [`claim`](./actor-events-api.md#claim) event is emitted by the verified registry actor and contains the client and provider for each claim. This event contains the claim ID which can be used to cross-reference with the verified registry state to access the details of the allocation, including piece information for the claim. The `StateGetClaim` Lotus API call provides this information. From 2a0d8970e8ef1b4c974750139118720708f4ce60 Mon Sep 17 00:00:00 2001 From: Piotr Galar Date: Tue, 2 Apr 2024 14:22:09 +0100 Subject: [PATCH 305/394] ci: ci: migrate the release workflow to github actions (#11785) * ci: rename install ubuntu deps to install system dependencies * ci: migrate the release workflow to github actions * ci: set permissions required by the newly added workflows explicitly * ci: prevent duplicate release and docker publishing * ci: trigger docker workflow on push to master * ci: do not pass tokens to goreleaser on dry release runs * ci: specify higher permissions on a job level --- .github/actions/export-circle-env/action.yml | 14 +++ .../install-system-dependencies/action.yml | 19 +++ .../actions/install-ubuntu-deps/action.yml | 10 -- .github/workflows/build.yml | 4 +- .github/workflows/builtin-actor-tests.yml | 4 + .github/workflows/check.yml | 8 +- .github/workflows/docker.yml | 100 ++++++++++++++++ .github/workflows/release.yml | 113 ++++++++++++++++++ .github/workflows/stale.yml | 6 +- .github/workflows/sync-master-main.yaml | 6 +- .github/workflows/test.yml | 7 +- .goreleaser.yaml | 6 +- 12 files changed, 271 insertions(+), 26 deletions(-) create mode 100644 .github/actions/export-circle-env/action.yml create mode 100644 .github/actions/install-system-dependencies/action.yml delete mode 100644 .github/actions/install-ubuntu-deps/action.yml create mode 100644 .github/workflows/docker.yml create mode 100644 .github/workflows/release.yml diff --git a/.github/actions/export-circle-env/action.yml b/.github/actions/export-circle-env/action.yml new file mode 100644 index 000000000..ded4bbb05 --- /dev/null +++ b/.github/actions/export-circle-env/action.yml @@ -0,0 +1,14 @@ +name: Export Circle Env +description: Export CircleCI environment variables for Filecoin Lotus + +runs: + using: composite + steps: + - run: | + if [[ "$GITHUB_REF" == refs/tags/* ]]; then + echo "CIRCLE_TAG=${GITHUB_REF#refs/tags/}" | tee -a $GITHUB_ENV + fi + echo "CIRCLE_PROJECT_USERNAME=$GITHUB_REPOSITORY_OWNER" | tee -a $GITHUB_ENV + echo "CIRCLE_PROJECT_REPONAME=${GITHUB_REPOSITORY#$GITHUB_REPOSITORY_OWNER/}" | tee -a $GITHUB_ENV + echo "CIRCLE_SHA1=$GITHUB_SHA" | tee -a $GITHUB_ENV + shell: bash diff --git a/.github/actions/install-system-dependencies/action.yml b/.github/actions/install-system-dependencies/action.yml new file mode 100644 index 000000000..0a048f8e1 --- /dev/null +++ b/.github/actions/install-system-dependencies/action.yml @@ -0,0 +1,19 @@ +name: Install System Dependencies +description: Install System dependencies for Filecoin Lotus + +runs: + using: composite + steps: + - if: runner.os == 'Linux' + run: | + sudo apt-get update -y + sudo apt-get install -y ocl-icd-opencl-dev libhwloc-dev pkg-config + shell: bash + - if: runner.os == 'macOS' + env: + HOMEBREW_NO_AUTO_UPDATE: '1' + run: | + brew install hwloc pkg-config + echo "CPATH=$(brew --prefix)/include" | tee -a $GITHUB_ENV + echo "LIBRARY_PATH=$(brew --prefix)/lib" | tee -a $GITHUB_ENV + shell: bash diff --git a/.github/actions/install-ubuntu-deps/action.yml b/.github/actions/install-ubuntu-deps/action.yml deleted file mode 100644 index 395e8d47b..000000000 --- a/.github/actions/install-ubuntu-deps/action.yml +++ /dev/null @@ -1,10 +0,0 @@ -name: Install Ubuntu Dependencies -description: Install Ubuntu dependencies for Filecoin Lotus - -runs: - using: composite - steps: - - run: | - sudo apt-get update -y - sudo apt-get install -y ocl-icd-opencl-dev libhwloc-dev pkg-config - shell: bash diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index cb56c05de..ce46a4a5f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,6 +16,8 @@ concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true +permissions: {} + jobs: build: name: Build @@ -24,6 +26,6 @@ jobs: - uses: actions/checkout@v4 with: submodules: 'recursive' - - uses: ./.github/actions/install-ubuntu-deps + - uses: ./.github/actions/install-system-dependencies - uses: ./.github/actions/install-go - run: make deps lotus diff --git a/.github/workflows/builtin-actor-tests.yml b/.github/workflows/builtin-actor-tests.yml index e0d95de51..93d4c669e 100644 --- a/.github/workflows/builtin-actor-tests.yml +++ b/.github/workflows/builtin-actor-tests.yml @@ -1,4 +1,5 @@ name: Built-in Actors + on: push: paths: @@ -6,6 +7,9 @@ on: - build/builtin_actors_gen.go branches: - release/* + +permissions: {} + jobs: release: name: Release Tests diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 50c926a31..7a77dc70b 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -16,6 +16,8 @@ concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true +permissions: {} + jobs: check-docsgen: name: Check (docs-check) @@ -24,7 +26,7 @@ jobs: - uses: actions/checkout@v4 with: submodules: 'recursive' - - uses: ./.github/actions/install-ubuntu-deps + - uses: ./.github/actions/install-system-dependencies - uses: ./.github/actions/install-go - run: go install golang.org/x/tools/cmd/goimports - run: make deps @@ -37,7 +39,7 @@ jobs: - uses: actions/checkout@v4 with: submodules: 'recursive' - - uses: ./.github/actions/install-ubuntu-deps + - uses: ./.github/actions/install-system-dependencies - uses: ./.github/actions/install-go - run: make deps lotus - run: go install golang.org/x/tools/cmd/goimports @@ -53,7 +55,7 @@ jobs: - uses: actions/checkout@v4 with: submodules: 'recursive' - - uses: ./.github/actions/install-ubuntu-deps + - uses: ./.github/actions/install-system-dependencies - uses: ./.github/actions/install-go - run: go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest - run: make deps diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 000000000..8e0ca57a5 --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,100 @@ +name: Docker + +on: + push: + branches: + - master + - release/* + tags: + - v* + schedule: + - cron: '0 0 * * *' + workflow_dispatch: + +defaults: + run: + shell: bash + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: {} + +jobs: + docker: + name: Docker (${{ matrix.image }} / ${{ matrix.network }}) [publish=${{ github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/') }}] + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + image: + - lotus-all-in-one + network: + - mainnet + - butterflynet + - calibnet + - debug + include: + - image: lotus + network: mainnet + env: + # Do not publish until CircleCI is deprecated + PUBLISH: false + # PUBLISH: ${{ github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/') }} + steps: + - id: channel + env: + IS_MASTER: ${{ github.ref == 'refs/heads/master' }} + IS_TAG: ${{ startsWith(github.ref, 'refs/tags/') }} + IS_RC: ${{ endsWith(github.ref, '-rc') }} + IS_SCHEDULED: ${{ github.event_name == 'schedule' }} + run: | + channel='' + if [[ "$IS_MASTER" == 'true' ]]; then + if [[ "$IS_SCHEDULED" == 'true' ]]; then + channel=nightly + else + channel=master + fi + elif [[ "$IS_TAG" == 'true' ]]; then + if [[ "$IS_RC" == 'true' ]]; then + channel=candidate + else + channel=stable + fi + fi + echo "channel=$channel" | tee -a $GITHUB_ENV + - uses: actions/checkout@v4 + with: + submodules: 'recursive' + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Docker meta + id: meta + uses: docker/metadata-action@v5 + with: + images: filecoin/${{ matrix.image }} + tags: | + type=schedule + type=raw,enable=${{ github.event_name != 'schedule' && steps.channel.outputs.channel != '' }},value=${{ steps.channel.outputs.channel }} + type=ref,event=tag + type=sha,prefix= + flavor: | + latest=false + suffix=${{ matrix.network != 'mainnet' && format('-{0}', matrix.network) || '' }} + - if: env.PUBLISH == 'true' + name: Login to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ vars.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - name: Build and push if channel is set (channel=${{ steps.channel.outputs.channel }}) + uses: docker/build-push-action@v5 + with: + context: . + push: ${{ env.PUBLISH == 'true' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + build-args: | + ${{ matrix.network != 'mainnet' && format('GOFLAGS=-tags={0}', matrix.network) || ''}} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 000000000..35e139b7d --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,113 @@ +name: Release + +on: + push: + branches: + - ci/* + - release/* + tags: + - v* + workflow_dispatch: + +defaults: + run: + shell: bash + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: {} + +jobs: + build: + name: Build (${{ matrix.os }}/${{ matrix.arch }}) + runs-on: ${{ matrix.runner }} + strategy: + fail-fast: false + matrix: + include: + - runner: ubuntu-latest + os: Linux + arch: X64 + - runner: macos-13 + os: macOS + arch: X64 + - runner: macos-14 + os: macOS + arch: ARM64 + steps: + - env: + OS: ${{ matrix.os }} + ARCH: ${{ matrix.arch }} + run: | + if [[ "$OS" != "$RUNNER_OS" || "$ARCH" != "$RUNNER_ARCH" ]]; then + echo "::error title=Unexpected Runner::Expected $OS/$ARCH, got $RUNNER_OS/$RUNNER_ARCH" + exit 1 + fi + - uses: actions/checkout@v4 + with: + submodules: 'recursive' + - uses: ./.github/actions/export-circle-env + - uses: ./.github/actions/install-system-dependencies + - uses: ./.github/actions/install-go + - env: + GITHUB_TOKEN: ${{ github.token }} + run: make deps lotus lotus-miner lotus-worker + - if: runner.os == 'macOS' + run: otool -hv lotus + - run: ./scripts/version-check.sh ./lotus + - uses: actions/upload-artifact@v4 + with: + name: lotus-${{ matrix.os }}-${{ matrix.arch }} + path: | + lotus + lotus-miner + lotus-worker + release: + name: Release [publish=${{ startsWith(github.ref, 'refs/tags/') }}] + permissions: + # This enables the job to create and/or update GitHub releases + contents: write + runs-on: ubuntu-latest + needs: [build] + env: + # Do not publish until CircleCI is deprecated + PUBLISH: false + # PUBLISH: ${{ startsWith(github.ref, 'refs/tags/') }} + steps: + - uses: actions/checkout@v4 + with: + submodules: 'recursive' + fetch-depth: 0 + - uses: actions/download-artifact@v4 + with: + name: lotus-Linux-X64 + path: linux_amd64_v1 + - uses: actions/download-artifact@v4 + with: + name: lotus-macOS-X64 + path: darwin_amd64_v1 + - uses: actions/download-artifact@v4 + with: + name: lotus-macOS-ARM64 + path: darwin_arm64 + - uses: ./.github/actions/export-circle-env + - uses: ./.github/actions/install-go + - uses: ipfs/download-ipfs-distribution-action@v1 + with: + name: kubo + version: v0.16.0 + - uses: goreleaser/goreleaser-action@7ec5c2b0c6cdda6e8bbb49444bc797dd33d74dd8 # v5.0.0 + with: + distribution: goreleaser-pro + version: latest + args: release --clean --debug ${{ env.PUBLISH == 'false' && '--snapshot' || '' }} + env: + GITHUB_TOKEN: ${{ env.PUBLISH == 'true' && github.token || '' }} + GORELEASER_KEY: ${{ env.PUBLISH == 'true' && secrets.GORELEASER_KEY || '' }} + - run: ./scripts/generate-checksums.sh + - if: env.PUBLISH == 'true' + env: + GITHUB_TOKEN: ${{ github.token }} + run: ./scripts/publish-checksums.sh diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index c81d72430..3116da07c 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -4,14 +4,14 @@ on: schedule: - cron: '0 12 * * *' +permissions: {} + jobs: stale: - - runs-on: ubuntu-latest permissions: issues: write pull-requests: write - + runs-on: ubuntu-latest steps: - uses: actions/stale@v9 with: diff --git a/.github/workflows/sync-master-main.yaml b/.github/workflows/sync-master-main.yaml index 05a60f089..b629b5604 100644 --- a/.github/workflows/sync-master-main.yaml +++ b/.github/workflows/sync-master-main.yaml @@ -1,14 +1,16 @@ name: sync-master-main + on: push: branches: - master -permissions: - contents: write +permissions: {} jobs: sync: + permissions: + contents: write runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 491a69d8e..127dd58d3 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -16,8 +16,7 @@ concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true -permissions: - contents: read +permissions: {} jobs: discover: @@ -239,7 +238,7 @@ jobs: path: ${{ steps.make_deps.outputs.path }} lookup-only: true - if: steps.restore_fetch_params.outputs.cache-hit != 'true' - uses: ./.github/actions/install-ubuntu-deps + uses: ./.github/actions/install-system-dependencies - if: steps.restore_fetch_params.outputs.cache-hit != 'true' uses: ./.github/actions/install-go - if: steps.restore_fetch_params.outputs.cache-hit != 'true' || steps.restore_make_deps.outputs.cache-hit != 'true' @@ -272,7 +271,7 @@ jobs: - uses: actions/checkout@v4 with: submodules: 'recursive' - - uses: ./.github/actions/install-ubuntu-deps + - uses: ./.github/actions/install-system-dependencies - uses: ./.github/actions/install-go - run: go install gotest.tools/gotestsum@latest - name: Restore cached make deps outputs diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 766f4f30a..f855ee969 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -27,7 +27,7 @@ builds: - goos: linux goarch: arm64 prebuilt: - path: /tmp/workspace/{{ .Os }}_{{ .Arch }}{{ with .Amd64 }}_{{ . }}{{ end }}/lotus + path: '{{ if index .Env "GITHUB_WORKSPACE" }}{{ .Env.GITHUB_WORKSPACE }}{{ else }}/tmp/workspace{{ end }}/{{ .Os }}_{{ .Arch }}{{ with .Amd64 }}_{{ . }}{{ end }}/lotus' - id: lotus-miner binary: lotus-miner builder: prebuilt @@ -43,7 +43,7 @@ builds: - goos: linux goarch: arm64 prebuilt: - path: /tmp/workspace/{{ .Os }}_{{ .Arch }}{{ with .Amd64 }}_{{ . }}{{ end }}/lotus-miner + path: '{{ if index .Env "GITHUB_WORKSPACE" }}{{ .Env.GITHUB_WORKSPACE }}{{ else }}/tmp/workspace{{ end }}/{{ .Os }}_{{ .Arch }}{{ with .Amd64 }}_{{ . }}{{ end }}/lotus-miner' - id: lotus-worker binary: lotus-worker builder: prebuilt @@ -59,7 +59,7 @@ builds: - goos: linux goarch: arm64 prebuilt: - path: /tmp/workspace/{{ .Os }}_{{ .Arch }}{{ with .Amd64 }}_{{ . }}{{ end }}/lotus-worker + path: '{{ if index .Env "GITHUB_WORKSPACE" }}{{ .Env.GITHUB_WORKSPACE }}{{ else }}/tmp/workspace{{ end }}/{{ .Os }}_{{ .Arch }}{{ with .Amd64 }}_{{ . }}{{ end }}/lotus-worker' archives: - id: primary From a40cced0d92a7c30afbd74c300695342e0102796 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 2 Apr 2024 18:15:50 +0200 Subject: [PATCH 306/394] feat: curioweb: add favicon (#11804) --- curiosrc/web/hapi/web/root.gohtml | 1 + curiosrc/web/static/favicon.svg | 1 + curiosrc/web/static/index.html | 6 ++++++ 3 files changed, 8 insertions(+) create mode 100644 curiosrc/web/static/favicon.svg diff --git a/curiosrc/web/hapi/web/root.gohtml b/curiosrc/web/hapi/web/root.gohtml index aa7bc586a..b3a41e800 100644 --- a/curiosrc/web/hapi/web/root.gohtml +++ b/curiosrc/web/hapi/web/root.gohtml @@ -6,6 +6,7 @@ +
diff --git a/curiosrc/web/static/favicon.svg b/curiosrc/web/static/favicon.svg new file mode 100644 index 000000000..91f132959 --- /dev/null +++ b/curiosrc/web/static/favicon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/curiosrc/web/static/index.html b/curiosrc/web/static/index.html index 674b4a88e..04050ff6e 100644 --- a/curiosrc/web/static/index.html +++ b/curiosrc/web/static/index.html @@ -34,10 +34,16 @@ .deadline-faulty { background-color: red; } + .logo { + display: inline-block; + }
+

Curio Cluster

From 23a3c747207ed6d4e8a8c53eab07b9372d27e55e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 2 Apr 2024 18:16:59 +0200 Subject: [PATCH 307/394] feat: curio: Add schemas for DDO deal support (#11805) --- lib/harmony/harmonydb/sql/20231217-sdr-pipeline.sql | 6 ++++++ .../harmonydb/sql/20240402-sdr-pipeline-ddo-deal-info.sql | 8 ++++++++ 2 files changed, 14 insertions(+) create mode 100644 lib/harmony/harmonydb/sql/20240402-sdr-pipeline-ddo-deal-info.sql diff --git a/lib/harmony/harmonydb/sql/20231217-sdr-pipeline.sql b/lib/harmony/harmonydb/sql/20231217-sdr-pipeline.sql index 3a75d8a96..31f103139 100644 --- a/lib/harmony/harmonydb/sql/20231217-sdr-pipeline.sql +++ b/lib/harmony/harmonydb/sql/20231217-sdr-pipeline.sql @@ -115,6 +115,12 @@ create table sectors_sdr_initial_pieces ( f05_deal_start_epoch bigint, f05_deal_end_epoch bigint, + -- ddo deal info + -- added in 20240402-sdr-pipeline-ddo-deal-info.sql + -- direct_start_epoch bigint, + -- direct_end_epoch bigint, + -- direct_piece_activation_manifest jsonb, + -- foreign key foreign key (sp_id, sector_number) references sectors_sdr_pipeline (sp_id, sector_number) on delete cascade, diff --git a/lib/harmony/harmonydb/sql/20240402-sdr-pipeline-ddo-deal-info.sql b/lib/harmony/harmonydb/sql/20240402-sdr-pipeline-ddo-deal-info.sql new file mode 100644 index 000000000..2230ef574 --- /dev/null +++ b/lib/harmony/harmonydb/sql/20240402-sdr-pipeline-ddo-deal-info.sql @@ -0,0 +1,8 @@ +ALTER TABLE sectors_sdr_initial_pieces + ADD COLUMN direct_start_epoch BIGINT; + +ALTER TABLE sectors_sdr_initial_pieces + ADD COLUMN direct_end_epoch BIGINT; + +ALTER TABLE sectors_sdr_initial_pieces + ADD COLUMN direct_piece_activation_manifest JSONB; From 71fee359d1156fc58445ea5e504153819ec68af7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 2 Apr 2024 18:18:27 +0200 Subject: [PATCH 308/394] feat: spcli: sectors extend improvements (#11798) * sptool: Initial structure * sptool: Port lotus-miner actor withdraw * sptool: Make cli docsgen happy * actors are done * info * proving * sptool the rest * fixed gitignore * lints * oops * 2 * terminate * fixes * sptool: improve sectors list * feat: spcli: Add client to claim info in sectors extend * spcli: Fix extend skipping sectors with claims --------- Co-authored-by: Andrew Jackson (Ajax) --- cli/spcli/sectors.go | 44 ++++++++++++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/cli/spcli/sectors.go b/cli/spcli/sectors.go index 5528c6438..5b8bf4139 100644 --- a/cli/spcli/sectors.go +++ b/cli/spcli/sectors.go @@ -141,8 +141,8 @@ func SectorsStatusCmd(getActorAddress ActorAddressGetter, getOnDiskInfo OnDiskIn } fmt.Printf("\nSector On Chain Info\n") fmt.Printf("SealProof:\t\t%x\n", status.SealProof) - fmt.Printf("Activation:\t\t%v\n", status.Activation) - fmt.Printf("Expiration:\t\t%v\n", status.Expiration) + fmt.Printf("Activation:\t\t%v\n", cliutil.EpochTime(head.Height(), status.Activation)) + fmt.Printf("Expiration:\t\t%s\n", cliutil.EpochTime(head.Height(), status.Expiration)) fmt.Printf("DealWeight:\t\t%v\n", status.DealWeight) fmt.Printf("VerifiedDealWeight:\t\t%v\n", status.VerifiedDealWeight) fmt.Printf("InitialPledge:\t\t%v\n", types.FIL(status.InitialPledge)) @@ -853,6 +853,7 @@ func SectorsExtendCmd(getActorAddress ActorAddressGetter) *cli.Command { for l, exts := range extensions { for newExp, numbers := range exts { sectorsWithoutClaimsToExtend := bitfield.New() + numbersToExtend := make([]abi.SectorNumber, 0, len(numbers)) var sectorsWithClaims []miner.SectorClaim for _, sectorNumber := range numbers { claimIdsToMaintain := make([]verifreg.ClaimId, 0) @@ -862,6 +863,7 @@ func SectorsExtendCmd(getActorAddress ActorAddressGetter) *cli.Command { // Nothing to check, add to ccSectors if !ok { sectorsWithoutClaimsToExtend.Set(uint64(sectorNumber)) + numbersToExtend = append(numbersToExtend, sectorNumber) } else { for _, claimId := range claimIds { claim, ok := claimsMap[claimId] @@ -882,13 +884,15 @@ func SectorsExtendCmd(getActorAddress ActorAddressGetter) *cli.Command { currEpoch <= (claim.TermStart+claim.TermMin) || // FIP-0045 requires the sector to be in its last 30 days of life (currEpoch <= sectorInfo.Expiration-builtin.EndOfLifeClaimDropPeriod) { - fmt.Printf("skipping sector %d because claim %d does not live long enough \n", sectorNumber, claimId) + fmt.Printf("skipping sector %d because claim %d (client f0%s, piece %s) does not live long enough \n", sectorNumber, claimId, claim.Client, claim.Data) cannotExtendSector = true break } claimIdsToDrop = append(claimIdsToDrop, claimId) } + + numbersToExtend = append(numbersToExtend, sectorNumber) } if cannotExtendSector { continue @@ -921,7 +925,7 @@ func SectorsExtendCmd(getActorAddress ActorAddressGetter) *cli.Command { p.Extensions = append(p.Extensions, miner.ExpirationExtension2{ Deadline: l.Deadline, Partition: l.Partition, - Sectors: SectorNumsToBitfield(numbers), + Sectors: SectorNumsToBitfield(numbersToExtend), SectorsWithClaims: sectorsWithClaims, NewExpiration: newExp, }) @@ -958,6 +962,19 @@ func SectorsExtendCmd(getActorAddress ActorAddressGetter) *cli.Command { fmt.Printf("Extending %d sectors: ", scount) stotal += scount + sp, aerr := actors.SerializeParams(¶ms[i]) + if aerr != nil { + return xerrors.Errorf("serializing params: %w", err) + } + + m := &types.Message{ + From: mi.Worker, + To: maddr, + Method: builtin.MethodsMiner.ExtendSectorExpiration2, + Value: big.Zero(), + Params: sp, + } + if !cctx.Bool("really-do-it") { pp, err := NewPseudoExtendParams(¶ms[i]) if err != nil { @@ -970,21 +987,16 @@ func SectorsExtendCmd(getActorAddress ActorAddressGetter) *cli.Command { } fmt.Println("\n", string(data)) + + _, err = fullApi.GasEstimateMessageGas(ctx, m, spec, types.EmptyTSK) + if err != nil { + return xerrors.Errorf("simulating message execution: %w", err) + } + continue } - sp, aerr := actors.SerializeParams(¶ms[i]) - if aerr != nil { - return xerrors.Errorf("serializing params: %w", err) - } - - smsg, err := fullApi.MpoolPushMessage(ctx, &types.Message{ - From: mi.Worker, - To: maddr, - Method: builtin.MethodsMiner.ExtendSectorExpiration2, - Value: big.Zero(), - Params: sp, - }, spec) + smsg, err := fullApi.MpoolPushMessage(ctx, m, spec) if err != nil { return xerrors.Errorf("mpool push message: %w", err) } From b27c861485695d3f5bb92bcb281abc95f4d90fb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 2 Apr 2024 18:21:21 +0200 Subject: [PATCH 309/394] curio: Cleanup proving config (#11751) --- curiosrc/builder.go | 2 +- documentation/en/default-curio-config.toml | 19 ---- node/config/def.go | 2 +- node/config/doc_gen.go | 105 ++++++++++++++++++++- node/config/types.go | 86 ++++++++++++++++- 5 files changed, 191 insertions(+), 23 deletions(-) diff --git a/curiosrc/builder.go b/curiosrc/builder.go index 3a3294a29..3cd4bd0cd 100644 --- a/curiosrc/builder.go +++ b/curiosrc/builder.go @@ -19,7 +19,7 @@ import ( //var log = logging.Logger("provider") -func WindowPostScheduler(ctx context.Context, fc config.CurioFees, pc config.ProvingConfig, +func WindowPostScheduler(ctx context.Context, fc config.CurioFees, pc config.CurioProvingConfig, api api.FullNode, verif storiface.Verifier, lw *sealer.LocalWorker, sender *message.Sender, chainSched *chainsched.CurioChainSched, as *multictladdr.MultiAddressSelector, addresses map[dtypes.MinerAddress]bool, db *harmonydb.DB, stor paths.Store, idx paths.SectorIndex, max int) (*window.WdPostTask, *window.WdPostSubmitTask, *window.WdPostRecoverDeclareTask, error) { diff --git a/documentation/en/default-curio-config.toml b/documentation/en/default-curio-config.toml index 83a7c243d..11c073696 100644 --- a/documentation/en/default-curio-config.toml +++ b/documentation/en/default-curio-config.toml @@ -235,25 +235,6 @@ # type: Duration #PartitionCheckTimeout = "20m0s" - # Disable Window PoSt computation on the lotus-miner process even if no window PoSt workers are present. - # - # WARNING: If no windowPoSt workers are connected, window PoSt WILL FAIL resulting in faulty sectors which will need - # to be recovered. Before enabling this option, make sure your PoSt workers work correctly. - # - # After changing this option, confirm that the new value works in your setup by invoking - # 'lotus-miner proving compute window-post 0' - # - # type: bool - #DisableBuiltinWindowPoSt = false - - # Disable Winning PoSt computation on the lotus-miner process even if no winning PoSt workers are present. - # - # WARNING: If no WinningPoSt workers are connected, Winning PoSt WILL FAIL resulting in lost block rewards. - # Before enabling this option, make sure your PoSt workers work correctly. - # - # type: bool - #DisableBuiltinWinningPoSt = false - # Disable WindowPoSt provable sector readability checks. # # In normal operation, when preparing to compute WindowPoSt, lotus-miner will perform a round of reading challenges diff --git a/node/config/def.go b/node/config/def.go index f725f60d3..661bed56f 100644 --- a/node/config/def.go +++ b/node/config/def.go @@ -356,7 +356,7 @@ func DefaultCurioConfig() *CurioConfig { TerminateControl: []string{}, MinerAddresses: []string{}, }}, - Proving: ProvingConfig{ + Proving: CurioProvingConfig{ ParallelCheckLimit: 32, PartitionCheckTimeout: Duration(20 * time.Minute), SingleCheckTimeout: Duration(10 * time.Minute), diff --git a/node/config/doc_gen.go b/node/config/doc_gen.go index b1b91d9c9..b61a136ce 100644 --- a/node/config/doc_gen.go +++ b/node/config/doc_gen.go @@ -228,7 +228,7 @@ over the worker address if this flag is set.`, }, { Name: "Proving", - Type: "ProvingConfig", + Type: "CurioProvingConfig", Comment: ``, }, @@ -295,6 +295,109 @@ over the worker address if this flag is set.`, Comment: ``, }, }, + "CurioProvingConfig": { + { + Name: "ParallelCheckLimit", + Type: "int", + + Comment: `Maximum number of sector checks to run in parallel. (0 = unlimited) + +WARNING: Setting this value too high may make the node crash by running out of stack +WARNING: Setting this value too low may make sector challenge reading much slower, resulting in failed PoSt due +to late submission. + +After changing this option, confirm that the new value works in your setup by invoking +'lotus-miner proving compute window-post 0'`, + }, + { + Name: "SingleCheckTimeout", + Type: "Duration", + + Comment: `Maximum amount of time a proving pre-check can take for a sector. If the check times out the sector will be skipped + +WARNING: Setting this value too low risks in sectors being skipped even though they are accessible, just reading the +test challenge took longer than this timeout +WARNING: Setting this value too high risks missing PoSt deadline in case IO operations related to this sector are +blocked (e.g. in case of disconnected NFS mount)`, + }, + { + Name: "PartitionCheckTimeout", + Type: "Duration", + + Comment: `Maximum amount of time a proving pre-check can take for an entire partition. If the check times out, sectors in +the partition which didn't get checked on time will be skipped + +WARNING: Setting this value too low risks in sectors being skipped even though they are accessible, just reading the +test challenge took longer than this timeout +WARNING: Setting this value too high risks missing PoSt deadline in case IO operations related to this partition are +blocked or slow`, + }, + { + Name: "DisableWDPoStPreChecks", + Type: "bool", + + Comment: `Disable WindowPoSt provable sector readability checks. + +In normal operation, when preparing to compute WindowPoSt, lotus-miner will perform a round of reading challenges +from all sectors to confirm that those sectors can be proven. Challenges read in this process are discarded, as +we're only interested in checking that sector data can be read. + +When using builtin proof computation (no PoSt workers, and DisableBuiltinWindowPoSt is set to false), this process +can save a lot of time and compute resources in the case that some sectors are not readable - this is caused by +the builtin logic not skipping snark computation when some sectors need to be skipped. + +When using PoSt workers, this process is mostly redundant, with PoSt workers challenges will be read once, and +if challenges for some sectors aren't readable, those sectors will just get skipped. + +Disabling sector pre-checks will slightly reduce IO load when proving sectors, possibly resulting in shorter +time to produce window PoSt. In setups with good IO capabilities the effect of this option on proving time should +be negligible. + +NOTE: It likely is a bad idea to disable sector pre-checks in setups with no PoSt workers. + +NOTE: Even when this option is enabled, recovering sectors will be checked before recovery declaration message is +sent to the chain + +After changing this option, confirm that the new value works in your setup by invoking +'lotus-miner proving compute window-post 0'`, + }, + { + Name: "MaxPartitionsPerPoStMessage", + Type: "int", + + Comment: `Maximum number of partitions to prove in a single SubmitWindowPoSt messace. 0 = network limit (3 in nv21) + +A single partition may contain up to 2349 32GiB sectors, or 2300 64GiB sectors. +// +Note that setting this value lower may result in less efficient gas use - more messages will be sent, +to prove each deadline, resulting in more total gas use (but each message will have lower gas limit) + +Setting this value above the network limit has no effect`, + }, + { + Name: "MaxPartitionsPerRecoveryMessage", + Type: "int", + + Comment: `In some cases when submitting DeclareFaultsRecovered messages, +there may be too many recoveries to fit in a BlockGasLimit. +In those cases it may be necessary to set this value to something low (eg 1); +Note that setting this value lower may result in less efficient gas use - more messages will be sent than needed, +resulting in more total gas use (but each message will have lower gas limit)`, + }, + { + Name: "SingleRecoveringPartitionPerPostMessage", + Type: "bool", + + Comment: `Enable single partition per PoSt Message for partitions containing recovery sectors + +In cases when submitting PoSt messages which contain recovering sectors, the default network limit may still be +too high to fit in the block gas limit. In those cases, it becomes useful to only house the single partition +with recovering sectors in the post message + +Note that setting this value lower may result in less efficient gas use - more messages will be sent, +to prove each deadline, resulting in more total gas use (but each message will have lower gas limit)`, + }, + }, "CurioSubsystemsConfig": { { Name: "EnableWindowPost", diff --git a/node/config/types.go b/node/config/types.go index 3872bffde..09e8d7800 100644 --- a/node/config/types.go +++ b/node/config/types.go @@ -73,7 +73,7 @@ type CurioConfig struct { // Addresses of wallets per MinerAddress (one of the fields). Addresses []CurioAddresses - Proving ProvingConfig + Proving CurioProvingConfig Journal JournalConfig Apis ApisConfig } @@ -723,6 +723,90 @@ type CurioAddresses struct { MinerAddresses []string } +type CurioProvingConfig struct { + // Maximum number of sector checks to run in parallel. (0 = unlimited) + // + // WARNING: Setting this value too high may make the node crash by running out of stack + // WARNING: Setting this value too low may make sector challenge reading much slower, resulting in failed PoSt due + // to late submission. + // + // After changing this option, confirm that the new value works in your setup by invoking + // 'lotus-miner proving compute window-post 0' + ParallelCheckLimit int + + // Maximum amount of time a proving pre-check can take for a sector. If the check times out the sector will be skipped + // + // WARNING: Setting this value too low risks in sectors being skipped even though they are accessible, just reading the + // test challenge took longer than this timeout + // WARNING: Setting this value too high risks missing PoSt deadline in case IO operations related to this sector are + // blocked (e.g. in case of disconnected NFS mount) + SingleCheckTimeout Duration + + // Maximum amount of time a proving pre-check can take for an entire partition. If the check times out, sectors in + // the partition which didn't get checked on time will be skipped + // + // WARNING: Setting this value too low risks in sectors being skipped even though they are accessible, just reading the + // test challenge took longer than this timeout + // WARNING: Setting this value too high risks missing PoSt deadline in case IO operations related to this partition are + // blocked or slow + PartitionCheckTimeout Duration + + // Disable WindowPoSt provable sector readability checks. + // + // In normal operation, when preparing to compute WindowPoSt, lotus-miner will perform a round of reading challenges + // from all sectors to confirm that those sectors can be proven. Challenges read in this process are discarded, as + // we're only interested in checking that sector data can be read. + // + // When using builtin proof computation (no PoSt workers, and DisableBuiltinWindowPoSt is set to false), this process + // can save a lot of time and compute resources in the case that some sectors are not readable - this is caused by + // the builtin logic not skipping snark computation when some sectors need to be skipped. + // + // When using PoSt workers, this process is mostly redundant, with PoSt workers challenges will be read once, and + // if challenges for some sectors aren't readable, those sectors will just get skipped. + // + // Disabling sector pre-checks will slightly reduce IO load when proving sectors, possibly resulting in shorter + // time to produce window PoSt. In setups with good IO capabilities the effect of this option on proving time should + // be negligible. + // + // NOTE: It likely is a bad idea to disable sector pre-checks in setups with no PoSt workers. + // + // NOTE: Even when this option is enabled, recovering sectors will be checked before recovery declaration message is + // sent to the chain + // + // After changing this option, confirm that the new value works in your setup by invoking + // 'lotus-miner proving compute window-post 0' + DisableWDPoStPreChecks bool + + // Maximum number of partitions to prove in a single SubmitWindowPoSt messace. 0 = network limit (3 in nv21) + // + // A single partition may contain up to 2349 32GiB sectors, or 2300 64GiB sectors. + // // + // Note that setting this value lower may result in less efficient gas use - more messages will be sent, + // to prove each deadline, resulting in more total gas use (but each message will have lower gas limit) + // + // Setting this value above the network limit has no effect + MaxPartitionsPerPoStMessage int + + // Maximum number of partitions to declare in a single DeclareFaultsRecovered message. 0 = no limit. + + // In some cases when submitting DeclareFaultsRecovered messages, + // there may be too many recoveries to fit in a BlockGasLimit. + // In those cases it may be necessary to set this value to something low (eg 1); + // Note that setting this value lower may result in less efficient gas use - more messages will be sent than needed, + // resulting in more total gas use (but each message will have lower gas limit) + MaxPartitionsPerRecoveryMessage int + + // Enable single partition per PoSt Message for partitions containing recovery sectors + // + // In cases when submitting PoSt messages which contain recovering sectors, the default network limit may still be + // too high to fit in the block gas limit. In those cases, it becomes useful to only house the single partition + // with recovering sectors in the post message + // + // Note that setting this value lower may result in less efficient gas use - more messages will be sent, + // to prove each deadline, resulting in more total gas use (but each message will have lower gas limit) + SingleRecoveringPartitionPerPostMessage bool +} + // API contains configs for API endpoint type API struct { // Binding address for the Lotus API From 4ab4922dc80be43134d0ebe36e3f76719de48d73 Mon Sep 17 00:00:00 2001 From: Piotr Galar Date: Wed, 3 Apr 2024 15:35:26 +0100 Subject: [PATCH 310/394] ci: ci: list processes before calling apt-get to enable debugging (#11815) --- .github/actions/install-system-dependencies/action.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/actions/install-system-dependencies/action.yml b/.github/actions/install-system-dependencies/action.yml index 0a048f8e1..f1b090ed7 100644 --- a/.github/actions/install-system-dependencies/action.yml +++ b/.github/actions/install-system-dependencies/action.yml @@ -6,6 +6,8 @@ runs: steps: - if: runner.os == 'Linux' run: | + # List processes to enable debugging in case /var/lib/apt/lists/ is locked + ps aux sudo apt-get update -y sudo apt-get install -y ocl-icd-opencl-dev libhwloc-dev pkg-config shell: bash From f8a5aa0db58636885a0f0ec7f8615f6da1a256b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 3 Apr 2024 18:31:43 +0200 Subject: [PATCH 311/394] feat: curio: Move boost proxy into the main binary (#11783) * feat: curio: Move boost proxy into the main binary * curio: Command for generating market tokens * curio boost rpc: Address review * make docsgen-cli --- cmd/curio/deps/deps.go | 2 +- cmd/curio/main.go | 1 + cmd/curio/market.go | 70 ++++ cmd/curio/run.go | 7 + cmd/lotus-shed/deal.go | 375 ------------------ curiosrc/market/fakelm/lmimpl.go | 45 +-- curiosrc/market/lmrpc/lmrpc.go | 422 +++++++++++++++++++++ documentation/en/cli-curio.md | 30 ++ documentation/en/default-curio-config.toml | 21 + node/config/def.go | 3 +- node/config/doc_gen.go | 22 ++ node/config/types.go | 19 + node/modules/storageminer_svc.go | 101 ----- 13 files changed, 608 insertions(+), 510 deletions(-) create mode 100644 cmd/curio/market.go create mode 100644 curiosrc/market/lmrpc/lmrpc.go diff --git a/cmd/curio/deps/deps.go b/cmd/curio/deps/deps.go index 4ce660739..c25741cbc 100644 --- a/cmd/curio/deps/deps.go +++ b/cmd/curio/deps/deps.go @@ -388,7 +388,7 @@ func GetConfig(cctx *cli.Context, db *harmonydb.DB) (*config.CurioConfig, error) for _, k := range meta.Keys() { have = append(have, strings.Join(k, " ")) } - log.Infow("Using layer", "layer", layer, "config", curioConfig) + log.Debugw("Using layer", "layer", layer, "config", curioConfig) } _ = have // FUTURE: verify that required fields are here. // If config includes 3rd-party config, consider JSONSchema as a way that diff --git a/cmd/curio/main.go b/cmd/curio/main.go index 2175d24c5..9a58768f3 100644 --- a/cmd/curio/main.go +++ b/cmd/curio/main.go @@ -54,6 +54,7 @@ func main() { webCmd, guidedsetup.GuidedsetupCmd, sealCmd, + marketCmd, } jaeger := tracing.SetupJaegerTracing("curio") diff --git a/cmd/curio/market.go b/cmd/curio/market.go new file mode 100644 index 000000000..cc562db93 --- /dev/null +++ b/cmd/curio/market.go @@ -0,0 +1,70 @@ +package main + +import ( + "fmt" + "sort" + + "github.com/urfave/cli/v2" + "golang.org/x/xerrors" + + "github.com/filecoin-project/lotus/cmd/curio/deps" + "github.com/filecoin-project/lotus/curiosrc/market/lmrpc" +) + +var marketCmd = &cli.Command{ + Name: "market", + Subcommands: []*cli.Command{ + marketRPCInfoCmd, + }, +} + +var marketRPCInfoCmd = &cli.Command{ + Flags: []cli.Flag{ + &cli.StringSliceFlag{ + Name: "layers", + Usage: "list of layers to be interpreted (atop defaults). Default: base", + }, + }, + Action: func(cctx *cli.Context) error { + db, err := deps.MakeDB(cctx) + if err != nil { + return err + } + + cfg, err := deps.GetConfig(cctx, db) + if err != nil { + return xerrors.Errorf("get config: %w", err) + } + + ts, err := lmrpc.MakeTokens(cfg) + if err != nil { + return xerrors.Errorf("make tokens: %w", err) + } + + var addrTokens []struct { + Address string + Token string + } + + for address, s := range ts { + addrTokens = append(addrTokens, struct { + Address string + Token string + }{ + Address: address.String(), + Token: s, + }) + } + + sort.Slice(addrTokens, func(i, j int) bool { + return addrTokens[i].Address < addrTokens[j].Address + }) + + for _, at := range addrTokens { + fmt.Printf("[lotus-miner/boost compatible] %s %s\n", at.Address, at.Token) + } + + return nil + }, + Name: "rpc-info", +} diff --git a/cmd/curio/run.go b/cmd/curio/run.go index 5974a5405..0f2785f2a 100644 --- a/cmd/curio/run.go +++ b/cmd/curio/run.go @@ -10,12 +10,14 @@ import ( "github.com/urfave/cli/v2" "go.opencensus.io/stats" "go.opencensus.io/tag" + "golang.org/x/xerrors" "github.com/filecoin-project/lotus/build" lcli "github.com/filecoin-project/lotus/cli" "github.com/filecoin-project/lotus/cmd/curio/deps" "github.com/filecoin-project/lotus/cmd/curio/rpc" "github.com/filecoin-project/lotus/cmd/curio/tasks" + "github.com/filecoin-project/lotus/curiosrc/market/lmrpc" "github.com/filecoin-project/lotus/lib/ulimit" "github.com/filecoin-project/lotus/metrics" "github.com/filecoin-project/lotus/node" @@ -133,6 +135,11 @@ var runCmd = &cli.Command{ if err != nil { return err } + + if err := lmrpc.ServeCurioMarketRPCFromConfig(dependencies.DB, dependencies.Full, dependencies.Cfg); err != nil { + return xerrors.Errorf("starting market RPCs: %w", err) + } + finishCh := node.MonitorShutdown(shutdownChan) //node.ShutdownHandler{Component: "rpc server", StopFunc: rpcStopper}, //node.ShutdownHandler{Component: "curio", StopFunc: stop}, diff --git a/cmd/lotus-shed/deal.go b/cmd/lotus-shed/deal.go index ba6979a14..029e29666 100644 --- a/cmd/lotus-shed/deal.go +++ b/cmd/lotus-shed/deal.go @@ -2,22 +2,15 @@ package main import ( "bytes" - "context" "fmt" "io" - "net" "net/http" "net/http/httptest" "net/url" "os" - "sync" - "time" "github.com/fatih/color" - "github.com/google/uuid" - "github.com/jackc/pgx/v5" "github.com/mitchellh/go-homedir" - manet "github.com/multiformats/go-multiaddr/net" "github.com/urfave/cli/v2" "golang.org/x/xerrors" @@ -32,19 +25,9 @@ import ( "github.com/filecoin-project/go-state-types/builtin/v9/market" "github.com/filecoin-project/lotus/api" - "github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/chain/types" lcli "github.com/filecoin-project/lotus/cli" - "github.com/filecoin-project/lotus/cmd/curio/deps" - cumarket "github.com/filecoin-project/lotus/curiosrc/market" - "github.com/filecoin-project/lotus/curiosrc/market/fakelm" - "github.com/filecoin-project/lotus/lib/harmony/harmonydb" "github.com/filecoin-project/lotus/lib/must" - "github.com/filecoin-project/lotus/lib/nullreader" - "github.com/filecoin-project/lotus/metrics/proxy" - "github.com/filecoin-project/lotus/node" - "github.com/filecoin-project/lotus/storage/paths" - "github.com/filecoin-project/lotus/storage/sealer/storiface" ) var lpUtilCmd = &cli.Command{ @@ -52,7 +35,6 @@ var lpUtilCmd = &cli.Command{ Usage: "lotus provider utility commands", Subcommands: []*cli.Command{ lpUtilStartDealCmd, - lpBoostProxyCmd, }, } @@ -300,360 +282,3 @@ var lpUtilStartDealCmd = &cli.Command{ return nil }, } - -var lpBoostProxyCmd = &cli.Command{ - Name: "boost-proxy", - Usage: "Start a legacy lotus-miner rpc proxy", - Flags: []cli.Flag{ - &cli.StringFlag{ - Name: "actor-address", - Usage: "Address of the miner actor", - Required: true, - }, - - &cli.StringFlag{ - Name: "db-host", - EnvVars: []string{"LOTUS_DB_HOST"}, - Usage: "Command separated list of hostnames for yugabyte cluster", - Value: "yugabyte", - }, - &cli.StringFlag{ - Name: "db-name", - EnvVars: []string{"LOTUS_DB_NAME", "LOTUS_HARMONYDB_HOSTS"}, - Value: "yugabyte", - }, - &cli.StringFlag{ - Name: "db-user", - EnvVars: []string{"LOTUS_DB_USER", "LOTUS_HARMONYDB_USERNAME"}, - Value: "yugabyte", - }, - &cli.StringFlag{ - Name: "db-password", - EnvVars: []string{"LOTUS_DB_PASSWORD", "LOTUS_HARMONYDB_PASSWORD"}, - Value: "yugabyte", - }, - &cli.StringFlag{ - Name: "db-port", - EnvVars: []string{"LOTUS_DB_PORT", "LOTUS_HARMONYDB_PORT"}, - Hidden: true, - Value: "5433", - }, - &cli.StringFlag{ - Name: "layers", - EnvVars: []string{"LOTUS_LAYERS", "LOTUS_CONFIG_LAYERS"}, - Value: "base", - }, - - &cli.StringFlag{ - Name: "listen", - Usage: "Address to listen on", - Value: ":32100", - }, - }, - Action: func(cctx *cli.Context) error { - ctx := lcli.ReqContext(cctx) - - db, err := deps.MakeDB(cctx) - if err != nil { - return err - } - - maddr, err := address.NewFromString(cctx.String("actor-address")) - if err != nil { - return xerrors.Errorf("parsing miner address: %w", err) - } - - full, closer, err := lcli.GetFullNodeAPIV1(cctx) - if err != nil { - return err - } - - defer closer() - - pin := cumarket.NewPieceIngester(db, full) - - si := paths.NewDBIndex(nil, db) - - mid, err := address.IDFromAddress(maddr) - if err != nil { - return xerrors.Errorf("getting miner id: %w", err) - } - - mi, err := full.StateMinerInfo(ctx, maddr, types.EmptyTSK) - if err != nil { - return xerrors.Errorf("getting miner info: %w", err) - } - - lp := fakelm.NewLMRPCProvider(si, full, maddr, abi.ActorID(mid), mi.SectorSize, pin, db, cctx.String("layers")) - - laddr, err := net.ResolveTCPAddr("tcp", cctx.String("listen")) - if err != nil { - return xerrors.Errorf("net resolve: %w", err) - } - - if len(laddr.IP) == 0 { - // set localhost - laddr.IP = net.IPv4(127, 0, 0, 1) - } - rootUrl := url.URL{ - Scheme: "http", - Host: laddr.String(), - } - - ast := api.StorageMinerStruct{} - - ast.CommonStruct.Internal.Version = func(ctx context.Context) (api.APIVersion, error) { - return api.APIVersion{ - Version: "lp-proxy-v0", - APIVersion: api.MinerAPIVersion0, - BlockDelay: build.BlockDelaySecs, - }, nil - } - - ast.CommonStruct.Internal.AuthNew = lp.AuthNew - - ast.Internal.ActorAddress = lp.ActorAddress - ast.Internal.WorkerJobs = lp.WorkerJobs - ast.Internal.SectorsStatus = lp.SectorsStatus - ast.Internal.SectorsList = lp.SectorsList - ast.Internal.SectorsSummary = lp.SectorsSummary - ast.Internal.SectorsListInStates = lp.SectorsListInStates - ast.Internal.StorageRedeclareLocal = lp.StorageRedeclareLocal - ast.Internal.ComputeDataCid = lp.ComputeDataCid - - type pieceInfo struct { - data storiface.Data - size abi.UnpaddedPieceSize - - done chan struct{} - } - - pieceInfoLk := new(sync.Mutex) - pieceInfos := map[uuid.UUID][]pieceInfo{} - - ast.Internal.SectorAddPieceToAny = func(ctx context.Context, pieceSize abi.UnpaddedPieceSize, pieceData storiface.Data, deal api.PieceDealInfo) (api.SectorOffset, error) { - origPieceData := pieceData - defer func() { - closer, ok := origPieceData.(io.Closer) - if !ok { - log.Warnf("DataCid: cannot close pieceData reader %T because it is not an io.Closer", origPieceData) - return - } - if err := closer.Close(); err != nil { - log.Warnw("closing pieceData in DataCid", "error", err) - } - }() - - pi := pieceInfo{ - data: pieceData, - size: pieceSize, - - done: make(chan struct{}), - } - - pieceUUID := uuid.New() - - color.Blue("%s %s piece assign request with id %s", deal.DealProposal.PieceCID, deal.DealProposal.Provider, pieceUUID) - - pieceInfoLk.Lock() - pieceInfos[pieceUUID] = append(pieceInfos[pieceUUID], pi) - pieceInfoLk.Unlock() - - // /piece?piece_cid=xxxx - dataUrl := rootUrl - dataUrl.Path = "/piece" - dataUrl.RawQuery = "piece_id=" + pieceUUID.String() - - // add piece entry - - var refID int64 - var pieceWasCreated bool - - comm, err := db.BeginTransaction(ctx, func(tx *harmonydb.Tx) (commit bool, err error) { - var pieceID int64 - // Attempt to select the piece ID first - err = tx.QueryRow(`SELECT id FROM parked_pieces WHERE piece_cid = $1`, deal.DealProposal.PieceCID.String()).Scan(&pieceID) - - if err != nil { - if err == pgx.ErrNoRows { - // Piece does not exist, attempt to insert - err = tx.QueryRow(` - INSERT INTO parked_pieces (piece_cid, piece_padded_size, piece_raw_size) - VALUES ($1, $2, $3) - ON CONFLICT (piece_cid) DO NOTHING - RETURNING id`, deal.DealProposal.PieceCID.String(), int64(pieceSize.Padded()), int64(pieceSize)).Scan(&pieceID) - if err != nil { - return false, xerrors.Errorf("inserting new parked piece and getting id: %w", err) - } - pieceWasCreated = true // New piece was created - } else { - // Some other error occurred during select - return false, xerrors.Errorf("checking existing parked piece: %w", err) - } - } else { - pieceWasCreated = false // Piece already exists, no new piece was created - } - - // Add parked_piece_ref - err = tx.QueryRow(`INSERT INTO parked_piece_refs (piece_id, data_url) - VALUES ($1, $2) RETURNING ref_id`, pieceID, dataUrl.String()).Scan(&refID) - if err != nil { - return false, xerrors.Errorf("inserting parked piece ref: %w", err) - } - - // If everything went well, commit the transaction - return true, nil // This will commit the transaction - }, harmonydb.OptionRetry()) - if err != nil { - return api.SectorOffset{}, xerrors.Errorf("inserting parked piece: %w", err) - } - if !comm { - return api.SectorOffset{}, xerrors.Errorf("piece tx didn't commit") - } - - // wait for piece to be parked - if pieceWasCreated { - <-pi.done - } else { - // If the piece was not created, we need to close the done channel - close(pi.done) - - go func() { - // close the data reader (drain to eof if it's not a closer) - if closer, ok := pieceData.(io.Closer); ok { - if err := closer.Close(); err != nil { - log.Warnw("closing pieceData in DataCid", "error", err) - } - } else { - log.Warnw("pieceData is not an io.Closer", "type", fmt.Sprintf("%T", pieceData)) - - _, err := io.Copy(io.Discard, pieceData) - if err != nil { - log.Warnw("draining pieceData in DataCid", "error", err) - } - } - }() - } - - pieceIDUrl := url.URL{ - Scheme: "pieceref", - Opaque: fmt.Sprintf("%d", refID), - } - - // make a sector - so, err := pin.AllocatePieceToSector(ctx, maddr, deal, int64(pieceSize), pieceIDUrl, nil) - if err != nil { - return api.SectorOffset{}, err - } - - color.Blue("%s piece assigned to sector f0%d:%d @ %d", deal.DealProposal.PieceCID, mid, so.Sector, so.Offset) - - return so, nil - } - - ast.Internal.StorageList = si.StorageList - ast.Internal.StorageDetach = si.StorageDetach - ast.Internal.StorageReportHealth = si.StorageReportHealth - ast.Internal.StorageDeclareSector = si.StorageDeclareSector - ast.Internal.StorageDropSector = si.StorageDropSector - ast.Internal.StorageFindSector = si.StorageFindSector - ast.Internal.StorageInfo = si.StorageInfo - ast.Internal.StorageBestAlloc = si.StorageBestAlloc - ast.Internal.StorageLock = si.StorageLock - ast.Internal.StorageTryLock = si.StorageTryLock - ast.Internal.StorageGetLocks = si.StorageGetLocks - - var pieceHandler http.HandlerFunc = func(w http.ResponseWriter, r *http.Request) { - // /piece?piece_id=xxxx - pieceUUID := r.URL.Query().Get("piece_id") - - pu, err := uuid.Parse(pieceUUID) - if err != nil { - http.Error(w, "bad piece id", http.StatusBadRequest) - return - } - - if r.Method != http.MethodGet { - http.Error(w, "bad method", http.StatusMethodNotAllowed) - return - } - - fmt.Printf("%s request for piece from %s\n", pieceUUID, r.RemoteAddr) - - pieceInfoLk.Lock() - pis, ok := pieceInfos[pu] - if !ok { - http.Error(w, "piece not found", http.StatusNotFound) - color.Red("%s not found", pu) - pieceInfoLk.Unlock() - return - } - - // pop - pi := pis[0] - pis = pis[1:] - - pieceInfos[pu] = pis - if len(pis) == 0 { - delete(pieceInfos, pu) - } - - pieceInfoLk.Unlock() - - start := time.Now() - - pieceData := io.LimitReader(io.MultiReader( - pi.data, - nullreader.Reader{}, - ), int64(pi.size)) - - n, err := io.Copy(w, pieceData) - close(pi.done) - - took := time.Since(start) - mbps := float64(n) / (1024 * 1024) / took.Seconds() - - if err != nil { - log.Errorf("copying piece data: %s", err) - return - } - - color.Green("%s served %.3f MiB in %s (%.2f MiB/s)", pu, float64(n)/(1024*1024), took, mbps) - } - - finalApi := proxy.LoggingAPI[api.StorageMiner, api.StorageMinerStruct](&ast) - - mh, err := node.MinerHandler(finalApi, false) // todo permissioned - if err != nil { - return err - } - - mux := http.NewServeMux() - mux.Handle("/piece", pieceHandler) - mux.Handle("/", mh) - - { - tok, err := lp.AuthNew(ctx, api.AllPermissions) - if err != nil { - return err - } - - // parse listen into multiaddr - ma, err := manet.FromNetAddr(laddr) - if err != nil { - return xerrors.Errorf("net from addr (%v): %w", laddr, err) - } - - fmt.Printf("Token: %s:%s\n", tok, ma) - } - - server := &http.Server{ - Addr: cctx.String("listen"), - Handler: mux, - ReadTimeout: 48 * time.Hour, - WriteTimeout: 48 * time.Hour, // really high because we block until TreeD - } - - return server.ListenAndServe() - }, -} diff --git a/curiosrc/market/fakelm/lmimpl.go b/curiosrc/market/fakelm/lmimpl.go index 1f6c5b91d..9dc19e627 100644 --- a/curiosrc/market/fakelm/lmimpl.go +++ b/curiosrc/market/fakelm/lmimpl.go @@ -6,7 +6,6 @@ import ( "net/http" "net/url" - "github.com/BurntSushi/toml" "github.com/gbrlsnchs/jwt/v3" "github.com/google/uuid" "golang.org/x/xerrors" @@ -37,21 +36,21 @@ type LMRPCProvider struct { ssize abi.SectorSize - pi market.Ingester - db *harmonydb.DB - confLayer string + pi market.Ingester + db *harmonydb.DB + conf *config.CurioConfig } -func NewLMRPCProvider(si paths.SectorIndex, full api.FullNode, maddr address.Address, minerID abi.ActorID, ssize abi.SectorSize, pi market.Ingester, db *harmonydb.DB, confLayer string) *LMRPCProvider { +func NewLMRPCProvider(si paths.SectorIndex, full api.FullNode, maddr address.Address, minerID abi.ActorID, ssize abi.SectorSize, pi market.Ingester, db *harmonydb.DB, conf *config.CurioConfig) *LMRPCProvider { return &LMRPCProvider{ - si: si, - full: full, - maddr: maddr, - minerID: minerID, - ssize: ssize, - pi: pi, - db: db, - confLayer: confLayer, + si: si, + full: full, + maddr: maddr, + minerID: minerID, + ssize: ssize, + pi: pi, + db: db, + conf: conf, } } @@ -330,24 +329,6 @@ func (l *LMRPCProvider) AllocatePieceToSector(ctx context.Context, maddr address } func (l *LMRPCProvider) AuthNew(ctx context.Context, perms []auth.Permission) ([]byte, error) { - var cs []struct { - Config string - } - - err := l.db.Select(ctx, &cs, "select config from harmony_config where title = $1", l.confLayer) - if err != nil { - return nil, err - } - - if len(cs) == 0 { - return nil, xerrors.Errorf("no harmony config found") - } - - lp := config.DefaultCurioConfig() - if _, err := toml.Decode(cs[0].Config, lp); err != nil { - return nil, xerrors.Errorf("decode harmony config: %w", err) - } - type jwtPayload struct { Allow []auth.Permission } @@ -356,7 +337,7 @@ func (l *LMRPCProvider) AuthNew(ctx context.Context, perms []auth.Permission) ([ Allow: perms, } - sk, err := base64.StdEncoding.DecodeString(lp.Apis.StorageRPCSecret) + sk, err := base64.StdEncoding.DecodeString(l.conf.Apis.StorageRPCSecret) if err != nil { return nil, xerrors.Errorf("decode secret: %w", err) } diff --git a/curiosrc/market/lmrpc/lmrpc.go b/curiosrc/market/lmrpc/lmrpc.go new file mode 100644 index 000000000..b760b7e1e --- /dev/null +++ b/curiosrc/market/lmrpc/lmrpc.go @@ -0,0 +1,422 @@ +package lmrpc + +import ( + "context" + "fmt" + "io" + "net" + "net/http" + "net/url" + "strconv" + "strings" + "sync" + "time" + + "github.com/google/uuid" + logging "github.com/ipfs/go-log/v2" + "github.com/jackc/pgx/v5" + manet "github.com/multiformats/go-multiaddr/net" + "golang.org/x/xerrors" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" + + "github.com/filecoin-project/lotus/api" + "github.com/filecoin-project/lotus/build" + "github.com/filecoin-project/lotus/chain/types" + cumarket "github.com/filecoin-project/lotus/curiosrc/market" + "github.com/filecoin-project/lotus/curiosrc/market/fakelm" + "github.com/filecoin-project/lotus/lib/harmony/harmonydb" + "github.com/filecoin-project/lotus/lib/nullreader" + "github.com/filecoin-project/lotus/metrics/proxy" + "github.com/filecoin-project/lotus/node" + "github.com/filecoin-project/lotus/node/config" + "github.com/filecoin-project/lotus/storage/paths" + "github.com/filecoin-project/lotus/storage/sealer/storiface" +) + +var log = logging.Logger("lmrpc") + +func ServeCurioMarketRPCFromConfig(db *harmonydb.DB, full api.FullNode, cfg *config.CurioConfig) error { + return forEachMarketRPC(cfg, func(maddr string, listen string) error { + addr, err := address.NewFromString(maddr) + if err != nil { + return xerrors.Errorf("parsing actor address: %w", err) + } + + go func() { + err := ServeCurioMarketRPC(db, full, addr, cfg, listen) + if err != nil { + log.Errorf("failed to serve market rpc: %s", err) + } + }() + + return nil + }) +} + +func MakeTokens(cfg *config.CurioConfig) (map[address.Address]string, error) { + out := map[address.Address]string{} + + err := forEachMarketRPC(cfg, func(smaddr string, listen string) error { + ctx := context.Background() + + laddr, err := net.ResolveTCPAddr("tcp", listen) + if err != nil { + return xerrors.Errorf("net resolve: %w", err) + } + + if len(laddr.IP) == 0 { + // set localhost + laddr.IP = net.IPv4(127, 0, 0, 1) + } + + // need minimal provider with just the config + lp := fakelm.NewLMRPCProvider(nil, nil, address.Undef, 0, 0, nil, nil, cfg) + + tok, err := lp.AuthNew(ctx, api.AllPermissions) + if err != nil { + return err + } + + // parse listen into multiaddr + ma, err := manet.FromNetAddr(laddr) + if err != nil { + return xerrors.Errorf("net from addr (%v): %w", laddr, err) + } + + maddr, err := address.NewFromString(smaddr) + if err != nil { + return xerrors.Errorf("parsing actor address: %w", err) + } + + token := fmt.Sprintf("%s:%s", tok, ma) + out[maddr] = token + + return nil + }) + + return out, err +} + +func forEachMarketRPC(cfg *config.CurioConfig, cb func(string, string) error) error { + for n, server := range cfg.Subsystems.BoostAdapters { + n := n + + // server: [f0.. actor address]:[bind address] + // bind address is either a numeric port or a full address + + // first split at first : to get the actor address and the bind address + split := strings.SplitN(server, ":", 2) + + // if the split length is not 2, return an error + if len(split) != 2 { + return fmt.Errorf("bad market rpc server config %d %s, expected [f0.. actor address]:[bind address]", n, server) + } + + // get the actor address and the bind address + strMaddr, strListen := split[0], split[1] + + maddr, err := address.NewFromString(strMaddr) + if err != nil { + return xerrors.Errorf("parsing actor address: %w", err) + } + + // check the listen address + if strListen == "" { + return fmt.Errorf("bad market rpc server config %d %s, expected [f0.. actor address]:[bind address]", n, server) + } + // if listen address is numeric, prepend the default host + if _, err := strconv.Atoi(strListen); err == nil { + strListen = "0.0.0.0:" + strListen + } + // check if the listen address is a valid address + if _, _, err := net.SplitHostPort(strListen); err != nil { + return fmt.Errorf("bad market rpc server config %d %s, expected [f0.. actor address]:[bind address]", n, server) + } + + log.Infow("Starting market RPC server", "actor", maddr, "listen", strListen) + + if err := cb(strMaddr, strListen); err != nil { + return err + } + } + + return nil +} + +func ServeCurioMarketRPC(db *harmonydb.DB, full api.FullNode, maddr address.Address, conf *config.CurioConfig, listen string) error { + ctx := context.Background() + + pin := cumarket.NewPieceIngester(db, full) + + si := paths.NewDBIndex(nil, db) + + mid, err := address.IDFromAddress(maddr) + if err != nil { + return xerrors.Errorf("getting miner id: %w", err) + } + + mi, err := full.StateMinerInfo(ctx, maddr, types.EmptyTSK) + if err != nil { + return xerrors.Errorf("getting miner info: %w", err) + } + + lp := fakelm.NewLMRPCProvider(si, full, maddr, abi.ActorID(mid), mi.SectorSize, pin, db, conf) + + laddr, err := net.ResolveTCPAddr("tcp", listen) + if err != nil { + return xerrors.Errorf("net resolve: %w", err) + } + + if len(laddr.IP) == 0 { + // set localhost + laddr.IP = net.IPv4(127, 0, 0, 1) + } + rootUrl := url.URL{ + Scheme: "http", + Host: laddr.String(), + } + + ast := api.StorageMinerStruct{} + + ast.CommonStruct.Internal.Version = func(ctx context.Context) (api.APIVersion, error) { + return api.APIVersion{ + Version: "curio-proxy-v0", + APIVersion: api.MinerAPIVersion0, + BlockDelay: build.BlockDelaySecs, + }, nil + } + + ast.CommonStruct.Internal.AuthNew = lp.AuthNew + + ast.Internal.ActorAddress = lp.ActorAddress + ast.Internal.WorkerJobs = lp.WorkerJobs + ast.Internal.SectorsStatus = lp.SectorsStatus + ast.Internal.SectorsList = lp.SectorsList + ast.Internal.SectorsSummary = lp.SectorsSummary + ast.Internal.SectorsListInStates = lp.SectorsListInStates + ast.Internal.StorageRedeclareLocal = lp.StorageRedeclareLocal + ast.Internal.ComputeDataCid = lp.ComputeDataCid + + type pieceInfo struct { + data storiface.Data + size abi.UnpaddedPieceSize + + done chan struct{} + } + + pieceInfoLk := new(sync.Mutex) + pieceInfos := map[uuid.UUID][]pieceInfo{} + + ast.Internal.SectorAddPieceToAny = func(ctx context.Context, pieceSize abi.UnpaddedPieceSize, pieceData storiface.Data, deal api.PieceDealInfo) (api.SectorOffset, error) { + origPieceData := pieceData + defer func() { + closer, ok := origPieceData.(io.Closer) + if !ok { + log.Warnf("DataCid: cannot close pieceData reader %T because it is not an io.Closer", origPieceData) + return + } + if err := closer.Close(); err != nil { + log.Warnw("closing pieceData in DataCid", "error", err) + } + }() + + pi := pieceInfo{ + data: pieceData, + size: pieceSize, + + done: make(chan struct{}), + } + + pieceUUID := uuid.New() + + //color.Blue("%s %s piece assign request with id %s", deal.DealProposal.PieceCID, deal.DealProposal.Provider, pieceUUID) + log.Infow("piece assign request", "piece_cid", deal.DealProposal.PieceCID, "provider", deal.DealProposal.Provider, "piece_uuid", pieceUUID) + + pieceInfoLk.Lock() + pieceInfos[pieceUUID] = append(pieceInfos[pieceUUID], pi) + pieceInfoLk.Unlock() + + // /piece?piece_cid=xxxx + dataUrl := rootUrl + dataUrl.Path = "/piece" + dataUrl.RawQuery = "piece_id=" + pieceUUID.String() + + // add piece entry + + var refID int64 + var pieceWasCreated bool + + comm, err := db.BeginTransaction(ctx, func(tx *harmonydb.Tx) (commit bool, err error) { + var pieceID int64 + // Attempt to select the piece ID first + err = tx.QueryRow(`SELECT id FROM parked_pieces WHERE piece_cid = $1`, deal.DealProposal.PieceCID.String()).Scan(&pieceID) + + if err != nil { + if err == pgx.ErrNoRows { + // Piece does not exist, attempt to insert + err = tx.QueryRow(` + INSERT INTO parked_pieces (piece_cid, piece_padded_size, piece_raw_size) + VALUES ($1, $2, $3) + ON CONFLICT (piece_cid) DO NOTHING + RETURNING id`, deal.DealProposal.PieceCID.String(), int64(pieceSize.Padded()), int64(pieceSize)).Scan(&pieceID) + if err != nil { + return false, xerrors.Errorf("inserting new parked piece and getting id: %w", err) + } + pieceWasCreated = true // New piece was created + } else { + // Some other error occurred during select + return false, xerrors.Errorf("checking existing parked piece: %w", err) + } + } else { + pieceWasCreated = false // Piece already exists, no new piece was created + } + + // Add parked_piece_ref + err = tx.QueryRow(`INSERT INTO parked_piece_refs (piece_id, data_url) + VALUES ($1, $2) RETURNING ref_id`, pieceID, dataUrl.String()).Scan(&refID) + if err != nil { + return false, xerrors.Errorf("inserting parked piece ref: %w", err) + } + + // If everything went well, commit the transaction + return true, nil // This will commit the transaction + }, harmonydb.OptionRetry()) + if err != nil { + return api.SectorOffset{}, xerrors.Errorf("inserting parked piece: %w", err) + } + if !comm { + return api.SectorOffset{}, xerrors.Errorf("piece tx didn't commit") + } + + // wait for piece to be parked + if pieceWasCreated { + <-pi.done + } else { + // If the piece was not created, we need to close the done channel + close(pi.done) + + go func() { + // close the data reader (drain to eof if it's not a closer) + if closer, ok := pieceData.(io.Closer); ok { + if err := closer.Close(); err != nil { + log.Warnw("closing pieceData in DataCid", "error", err) + } + } else { + log.Warnw("pieceData is not an io.Closer", "type", fmt.Sprintf("%T", pieceData)) + + _, err := io.Copy(io.Discard, pieceData) + if err != nil { + log.Warnw("draining pieceData in DataCid", "error", err) + } + } + }() + } + + pieceIDUrl := url.URL{ + Scheme: "pieceref", + Opaque: fmt.Sprintf("%d", refID), + } + + // make a sector + so, err := pin.AllocatePieceToSector(ctx, maddr, deal, int64(pieceSize), pieceIDUrl, nil) + if err != nil { + return api.SectorOffset{}, err + } + + log.Infow("piece assigned to sector", "piece_cid", deal.DealProposal.PieceCID, "sector", so.Sector, "offset", so.Offset) + + return so, nil + } + + ast.Internal.StorageList = si.StorageList + ast.Internal.StorageDetach = si.StorageDetach + ast.Internal.StorageReportHealth = si.StorageReportHealth + ast.Internal.StorageDeclareSector = si.StorageDeclareSector + ast.Internal.StorageDropSector = si.StorageDropSector + ast.Internal.StorageFindSector = si.StorageFindSector + ast.Internal.StorageInfo = si.StorageInfo + ast.Internal.StorageBestAlloc = si.StorageBestAlloc + ast.Internal.StorageLock = si.StorageLock + ast.Internal.StorageTryLock = si.StorageTryLock + ast.Internal.StorageGetLocks = si.StorageGetLocks + + var pieceHandler http.HandlerFunc = func(w http.ResponseWriter, r *http.Request) { + // /piece?piece_id=xxxx + pieceUUID := r.URL.Query().Get("piece_id") + + pu, err := uuid.Parse(pieceUUID) + if err != nil { + http.Error(w, "bad piece id", http.StatusBadRequest) + return + } + + if r.Method != http.MethodGet { + http.Error(w, "bad method", http.StatusMethodNotAllowed) + return + } + + fmt.Printf("%s request for piece from %s\n", pieceUUID, r.RemoteAddr) + + pieceInfoLk.Lock() + pis, ok := pieceInfos[pu] + if !ok { + http.Error(w, "piece not found", http.StatusNotFound) + log.Warnw("piece not found", "piece_uuid", pu) + pieceInfoLk.Unlock() + return + } + + // pop + pi := pis[0] + pis = pis[1:] + + pieceInfos[pu] = pis + if len(pis) == 0 { + delete(pieceInfos, pu) + } + + pieceInfoLk.Unlock() + + start := time.Now() + + pieceData := io.LimitReader(io.MultiReader( + pi.data, + nullreader.Reader{}, + ), int64(pi.size)) + + n, err := io.Copy(w, pieceData) + close(pi.done) + + took := time.Since(start) + mbps := float64(n) / (1024 * 1024) / took.Seconds() + + if err != nil { + log.Errorf("copying piece data: %s", err) + return + } + + log.Infow("piece served", "piece_uuid", pu, "size", float64(n)/(1024*1024), "duration", took, "speed", mbps) + } + + finalApi := proxy.LoggingAPI[api.StorageMiner, api.StorageMinerStruct](&ast) + + mh, err := node.MinerHandler(finalApi, false) // todo permissioned + if err != nil { + return err + } + + mux := http.NewServeMux() + mux.Handle("/piece", pieceHandler) + mux.Handle("/", mh) + + server := &http.Server{ + Addr: listen, + Handler: mux, + ReadTimeout: 48 * time.Hour, + WriteTimeout: 48 * time.Hour, // really high because we block until pieces are saved in PiecePark + } + + return server.ListenAndServe() +} diff --git a/documentation/en/cli-curio.md b/documentation/en/cli-curio.md index 296a45c43..f7c4f2d0c 100644 --- a/documentation/en/cli-curio.md +++ b/documentation/en/cli-curio.md @@ -18,6 +18,7 @@ COMMANDS: web Start Curio web interface guided-setup Run the guided setup for migrating from lotus-miner to Curio seal Manage the sealing pipeline + market auth Manage RPC permissions log Manage logging wait-api Wait for lotus api to come online @@ -347,6 +348,35 @@ OPTIONS: --help, -h show help ``` +## curio market +``` +NAME: + curio market + +USAGE: + curio market command [command options] [arguments...] + +COMMANDS: + rpc-info + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help +``` + +### curio market rpc-info +``` +NAME: + curio market rpc-info + +USAGE: + curio market rpc-info [command options] [arguments...] + +OPTIONS: + --layers value [ --layers value ] list of layers to be interpreted (atop defaults). Default: base + --help, -h show help +``` + ## curio auth ``` NAME: diff --git a/documentation/en/default-curio-config.toml b/documentation/en/default-curio-config.toml index 11c073696..f4e4c19b8 100644 --- a/documentation/en/default-curio-config.toml +++ b/documentation/en/default-curio-config.toml @@ -139,6 +139,27 @@ # type: int #MoveStorageMaxTasks = 0 + # BoostAdapters is a list of tuples of miner address and port/ip to listen for market (e.g. boost) requests. + # This interface is compatible with the lotus-miner RPC, implementing a subset needed for storage market operations. + # Strings should be in the format "actor:port" or "actor:ip:port". Default listen address is 0.0.0.0 + # Example: "f0123:32100", "f0123:127.0.0.1:32100". Multiple addresses can be specified. + # + # When a market node like boost gives Curio's market RPC a deal to placing into a sector, Curio will first store the + # deal data in a temporary location "Piece Park" before assigning it to a sector. This requires that at least one + # node in the cluster has the EnableParkPiece option enabled and has sufficient scratch space to store the deal data. + # This is different from lotus-miner which stored the deal data into an "unsealed" sector as soon as the deal was + # received. Deal data in PiecePark is accessed when the sector TreeD and TreeR are computed, but isn't needed for + # the initial SDR layers computation. Pieces in PiecePark are removed after all sectors referencing the piece are + # sealed. + # + # To get API info for boost configuration run 'curio market rpc-info' + # + # NOTE: All deal data will flow through this service, so it should be placed on a machine running boost or on + # a machine which handles ParkPiece tasks. + # + # type: []string + #BoostAdapters = [] + # EnableWebGui enables the web GUI on this lotus-provider instance. The UI has minimal local overhead, but it should # only need to be run on a single machine in the cluster. # diff --git a/node/config/def.go b/node/config/def.go index 661bed56f..0307001b8 100644 --- a/node/config/def.go +++ b/node/config/def.go @@ -330,7 +330,8 @@ const ( func DefaultCurioConfig() *CurioConfig { return &CurioConfig{ Subsystems: CurioSubsystemsConfig{ - GuiAddress: ":4701", + GuiAddress: ":4701", + BoostAdapters: []string{}, }, Fees: CurioFees{ DefaultMaxFee: DefaultDefaultMaxFee, diff --git a/node/config/doc_gen.go b/node/config/doc_gen.go index b61a136ce..3b4124f8a 100644 --- a/node/config/doc_gen.go +++ b/node/config/doc_gen.go @@ -561,6 +561,28 @@ SDRTrees machine into long-term storage. This task runs after the Finalize task. Comment: `The maximum amount of MoveStorage tasks that can run simultaneously. Note that the maximum number of tasks will also be bounded by resources available on the machine. It is recommended that this value is set to a number which uses all available network (or disk) bandwidth on the machine without causing bottlenecks.`, + }, + { + Name: "BoostAdapters", + Type: "[]string", + + Comment: `BoostAdapters is a list of tuples of miner address and port/ip to listen for market (e.g. boost) requests. +This interface is compatible with the lotus-miner RPC, implementing a subset needed for storage market operations. +Strings should be in the format "actor:port" or "actor:ip:port". Default listen address is 0.0.0.0 +Example: "f0123:32100", "f0123:127.0.0.1:32100". Multiple addresses can be specified. + +When a market node like boost gives Curio's market RPC a deal to placing into a sector, Curio will first store the +deal data in a temporary location "Piece Park" before assigning it to a sector. This requires that at least one +node in the cluster has the EnableParkPiece option enabled and has sufficient scratch space to store the deal data. +This is different from lotus-miner which stored the deal data into an "unsealed" sector as soon as the deal was +received. Deal data in PiecePark is accessed when the sector TreeD and TreeR are computed, but isn't needed for +the initial SDR layers computation. Pieces in PiecePark are removed after all sectors referencing the piece are +sealed. + +To get API info for boost configuration run 'curio market rpc-info' + +NOTE: All deal data will flow through this service, so it should be placed on a machine running boost or on +a machine which handles ParkPiece tasks.`, }, { Name: "EnableWebGui", diff --git a/node/config/types.go b/node/config/types.go index 09e8d7800..82d9f4a99 100644 --- a/node/config/types.go +++ b/node/config/types.go @@ -200,6 +200,25 @@ type CurioSubsystemsConfig struct { // uses all available network (or disk) bandwidth on the machine without causing bottlenecks. MoveStorageMaxTasks int + // BoostAdapters is a list of tuples of miner address and port/ip to listen for market (e.g. boost) requests. + // This interface is compatible with the lotus-miner RPC, implementing a subset needed for storage market operations. + // Strings should be in the format "actor:port" or "actor:ip:port". Default listen address is 0.0.0.0 + // Example: "f0123:32100", "f0123:127.0.0.1:32100". Multiple addresses can be specified. + // + // When a market node like boost gives Curio's market RPC a deal to placing into a sector, Curio will first store the + // deal data in a temporary location "Piece Park" before assigning it to a sector. This requires that at least one + // node in the cluster has the EnableParkPiece option enabled and has sufficient scratch space to store the deal data. + // This is different from lotus-miner which stored the deal data into an "unsealed" sector as soon as the deal was + // received. Deal data in PiecePark is accessed when the sector TreeD and TreeR are computed, but isn't needed for + // the initial SDR layers computation. Pieces in PiecePark are removed after all sectors referencing the piece are + // sealed. + // + // To get API info for boost configuration run 'curio market rpc-info' + // + // NOTE: All deal data will flow through this service, so it should be placed on a machine running boost or on + // a machine which handles ParkPiece tasks. + BoostAdapters []string + // EnableWebGui enables the web GUI on this lotus-provider instance. The UI has minimal local overhead, but it should // only need to be run on a single machine in the cluster. EnableWebGui bool diff --git a/node/modules/storageminer_svc.go b/node/modules/storageminer_svc.go index 1a909b4ec..17eb987ef 100644 --- a/node/modules/storageminer_svc.go +++ b/node/modules/storageminer_svc.go @@ -2,26 +2,15 @@ package modules import ( "context" - "strings" "go.uber.org/fx" "golang.org/x/xerrors" - "github.com/filecoin-project/go-address" - "github.com/filecoin-project/go-state-types/abi" - "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/api/client" "github.com/filecoin-project/lotus/api/v1api" - "github.com/filecoin-project/lotus/chain/types" cliutil "github.com/filecoin-project/lotus/cli/util" - "github.com/filecoin-project/lotus/curiosrc/market" - "github.com/filecoin-project/lotus/curiosrc/market/fakelm" - "github.com/filecoin-project/lotus/lib/harmony/harmonydb" - "github.com/filecoin-project/lotus/node/config" "github.com/filecoin-project/lotus/node/modules/helpers" - "github.com/filecoin-project/lotus/storage/paths" - "github.com/filecoin-project/lotus/storage/sealer/storiface" "github.com/filecoin-project/lotus/storage/sectorblocks" ) @@ -30,98 +19,8 @@ type MinerStorageService api.StorageMiner var _ sectorblocks.SectorBuilder = *new(MinerSealingService) -func harmonyApiInfoToConf(apiInfo string) (config.HarmonyDB, error) { - hc := config.HarmonyDB{} - - // apiInfo - harmony:layer:maddr:user:pass:dbname:host:port - - parts := strings.Split(apiInfo, ":") - - if len(parts) != 8 { - return config.HarmonyDB{}, xerrors.Errorf("invalid harmonydb info '%s'", apiInfo) - } - - hc.Username = parts[3] - hc.Password = parts[4] - hc.Database = parts[5] - hc.Hosts = []string{parts[6]} - hc.Port = parts[7] - - return hc, nil -} - -func connectHarmony(apiInfo string, fapi v1api.FullNode, mctx helpers.MetricsCtx, lc fx.Lifecycle) (api.StorageMiner, error) { - log.Info("Connecting to harmonydb") - - hc, err := harmonyApiInfoToConf(apiInfo) - if err != nil { - return nil, err - } - - db, err := harmonydb.NewFromConfig(hc) - if err != nil { - return nil, xerrors.Errorf("connecting to harmonydb: %w", err) - } - - parts := strings.Split(apiInfo, ":") - maddr, err := address.NewFromString(parts[2]) - if err != nil { - return nil, xerrors.Errorf("parsing miner address: %w", err) - } - - pin := market.NewPieceIngester(db, fapi) - - si := paths.NewDBIndex(nil, db) - - mid, err := address.IDFromAddress(maddr) - if err != nil { - return nil, xerrors.Errorf("getting miner id: %w", err) - } - - mi, err := fapi.StateMinerInfo(mctx, maddr, types.EmptyTSK) - if err != nil { - return nil, xerrors.Errorf("getting miner info: %w", err) - } - - lp := fakelm.NewLMRPCProvider(si, fapi, maddr, abi.ActorID(mid), mi.SectorSize, pin, db, parts[1]) - - ast := api.StorageMinerStruct{} - - ast.CommonStruct.Internal.AuthNew = lp.AuthNew - - ast.Internal.ActorAddress = lp.ActorAddress - ast.Internal.WorkerJobs = lp.WorkerJobs - ast.Internal.SectorsStatus = lp.SectorsStatus - ast.Internal.SectorsList = lp.SectorsList - ast.Internal.SectorsSummary = lp.SectorsSummary - ast.Internal.SectorsListInStates = lp.SectorsListInStates - ast.Internal.StorageRedeclareLocal = lp.StorageRedeclareLocal - ast.Internal.ComputeDataCid = lp.ComputeDataCid - ast.Internal.SectorAddPieceToAny = func(p0 context.Context, p1 abi.UnpaddedPieceSize, p2 storiface.Data, p3 api.PieceDealInfo) (api.SectorOffset, error) { - panic("implement me") - } - - ast.Internal.StorageList = si.StorageList - ast.Internal.StorageDetach = si.StorageDetach - ast.Internal.StorageReportHealth = si.StorageReportHealth - ast.Internal.StorageDeclareSector = si.StorageDeclareSector - ast.Internal.StorageDropSector = si.StorageDropSector - ast.Internal.StorageFindSector = si.StorageFindSector - ast.Internal.StorageInfo = si.StorageInfo - ast.Internal.StorageBestAlloc = si.StorageBestAlloc - ast.Internal.StorageLock = si.StorageLock - ast.Internal.StorageTryLock = si.StorageTryLock - ast.Internal.StorageGetLocks = si.StorageGetLocks - - return &ast, nil -} - func connectMinerService(apiInfo string) func(mctx helpers.MetricsCtx, lc fx.Lifecycle, fapi v1api.FullNode) (api.StorageMiner, error) { return func(mctx helpers.MetricsCtx, lc fx.Lifecycle, fapi v1api.FullNode) (api.StorageMiner, error) { - if strings.HasPrefix(apiInfo, "harmony:") { - return connectHarmony(apiInfo, fapi, mctx, lc) - } - ctx := helpers.LifecycleCtx(mctx, lc) info := cliutil.ParseApiInfo(apiInfo) addr, err := info.DialArgs("v0") From 466966c72d432499a48cc8d7138e39ab98968222 Mon Sep 17 00:00:00 2001 From: LexLuthr Date: Wed, 3 Apr 2024 23:04:08 +0400 Subject: [PATCH 312/394] enable 8MiB sectors on Curio --- curiosrc/ffi/sdr_funcs.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/curiosrc/ffi/sdr_funcs.go b/curiosrc/ffi/sdr_funcs.go index 74a3270cf..0f78ca6cd 100644 --- a/curiosrc/ffi/sdr_funcs.go +++ b/curiosrc/ffi/sdr_funcs.go @@ -318,6 +318,22 @@ func (sb *SealCalls) makePhase1Out(unsCid cid.Cid, spt abi.RegisteredSealProof) Size: 64, }) } + + case abi.RegisteredSealProof_StackedDrg8MiBV1_1, abi.RegisteredSealProof_StackedDrg8MiBV1_1_Feat_SyntheticPoRep: + phase1Output.Config.RowsToDiscard = 0 + phase1Output.Config.Size = 524287 + phase1Output.Labels["StackedDrg8MiBV1"] = &Labels{} + phase1Output.RegisteredProof = "StackedDrg8MiBV1_1" + + for i := 0; i < 2; i++ { + phase1Output.Labels["StackedDrg8MiBV1"].Labels = append(phase1Output.Labels["StackedDrg8MiBV1"].Labels, Config{ + ID: fmt.Sprintf("layer-%d", i+1), + Path: "/placeholder", + RowsToDiscard: 0, + Size: 262144, + }) + } + case abi.RegisteredSealProof_StackedDrg512MiBV1_1: phase1Output.Config.RowsToDiscard = 0 phase1Output.Config.Size = 33554431 From 2ccc0db1cc4989d8baff25734f85757dca7ecac7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 2 Apr 2024 16:04:12 +0200 Subject: [PATCH 313/394] feat: curio: Storage reservations in MoveStorage --- curiosrc/ffi/sdr_funcs.go | 21 ++++++++++++++++++-- curiosrc/seal/task_movestorage.go | 29 ++++++++++++++++++++++++---- storage/paths/interface.go | 2 +- storage/paths/local.go | 32 ++++++++++++++++++++++--------- storage/paths/mocks/store.go | 13 +++++++++---- storage/paths/remote.go | 6 +++--- 6 files changed, 80 insertions(+), 23 deletions(-) diff --git a/curiosrc/ffi/sdr_funcs.go b/curiosrc/ffi/sdr_funcs.go index 0f78ca6cd..583a536fe 100644 --- a/curiosrc/ffi/sdr_funcs.go +++ b/curiosrc/ffi/sdr_funcs.go @@ -489,7 +489,7 @@ afterUnsealedMove: return nil } -func (sb *SealCalls) MoveStorage(ctx context.Context, sector storiface.SectorRef) error { +func (sb *SealCalls) MoveStorage(ctx context.Context, sector storiface.SectorRef, taskID *harmonytask.TaskID) error { // only move the unsealed file if it still exists and needs moving moveUnsealed := storiface.FTUnsealed { @@ -505,7 +505,24 @@ func (sb *SealCalls) MoveStorage(ctx context.Context, sector storiface.SectorRef toMove := storiface.FTCache | storiface.FTSealed | moveUnsealed - err := sb.sectors.storage.MoveStorage(ctx, sector, toMove) + var opts []storiface.AcquireOption + if taskID != nil { + resv, ok := sb.sectors.storageReservations.Load(*taskID) + if ok { + defer resv.Release() + + if resv.Alloc != storiface.FTNone { + return xerrors.Errorf("task %d has storage reservation with alloc", taskID) + } + if resv.Existing != toMove|storiface.FTUnsealed { + return xerrors.Errorf("task %d has storage reservation with different existing", taskID) + } + + opts = append(opts, storiface.AcquireInto(storiface.PathsWithIDs{Paths: resv.Paths, IDs: resv.PathIDs})) + } + } + + err := sb.sectors.storage.MoveStorage(ctx, sector, toMove, opts...) if err != nil { return xerrors.Errorf("moving storage: %w", err) } diff --git a/curiosrc/seal/task_movestorage.go b/curiosrc/seal/task_movestorage.go index 9092ec829..6037a390d 100644 --- a/curiosrc/seal/task_movestorage.go +++ b/curiosrc/seal/task_movestorage.go @@ -58,7 +58,7 @@ func (m *MoveStorageTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) ProofType: abi.RegisteredSealProof(task.RegSealProof), } - err = m.sc.MoveStorage(ctx, sector) + err = m.sc.MoveStorage(ctx, sector, &taskID) if err != nil { return false, xerrors.Errorf("moving storage: %w", err) } @@ -136,18 +136,39 @@ func (m *MoveStorageTask) CanAccept(ids []harmonytask.TaskID, engine *harmonytas } func (m *MoveStorageTask) TypeDetails() harmonytask.TaskTypeDetails { + ssize := abi.SectorSize(32 << 30) // todo task details needs taskID to get correct sector size + if isDevnet { + ssize = abi.SectorSize(2 << 20) + } + return harmonytask.TaskTypeDetails{ Max: m.max, Name: "MoveStorage", Cost: resources.Resources{ - Cpu: 1, - Gpu: 0, - Ram: 128 << 20, + Cpu: 1, + Gpu: 0, + Ram: 128 << 20, + Storage: m.sc.Storage(m.taskToSector, storiface.FTNone, storiface.FTCache|storiface.FTSealed|storiface.FTUnsealed, ssize, storiface.PathStorage), }, MaxFailures: 10, } } +func (m *MoveStorageTask) taskToSector(id harmonytask.TaskID) (ffi.SectorRef, error) { + var refs []ffi.SectorRef + + err := m.db.Select(context.Background(), &refs, `SELECT sp_id, sector_number, reg_seal_proof FROM sectors_sdr_pipeline WHERE task_id_move_storage = $1`, id) + if err != nil { + return ffi.SectorRef{}, xerrors.Errorf("getting sector ref: %w", err) + } + + if len(refs) != 1 { + return ffi.SectorRef{}, xerrors.Errorf("expected 1 sector ref, got %d", len(refs)) + } + + return refs[0], nil +} + func (m *MoveStorageTask) Adder(taskFunc harmonytask.AddTaskFunc) { m.sp.pollers[pollerMoveStorage].Set(taskFunc) } diff --git a/storage/paths/interface.go b/storage/paths/interface.go index 27d6ee541..d3dce8886 100644 --- a/storage/paths/interface.go +++ b/storage/paths/interface.go @@ -43,7 +43,7 @@ type Store interface { RemoveCopies(ctx context.Context, s abi.SectorID, types storiface.SectorFileType) error // move sectors into storage - MoveStorage(ctx context.Context, s storiface.SectorRef, types storiface.SectorFileType) error + MoveStorage(ctx context.Context, s storiface.SectorRef, types storiface.SectorFileType, opts ...storiface.AcquireOption) error FsStat(ctx context.Context, id storiface.ID) (fsutil.FsStat, error) diff --git a/storage/paths/local.go b/storage/paths/local.go index 68999940f..ccb41c2a2 100644 --- a/storage/paths/local.go +++ b/storage/paths/local.go @@ -716,22 +716,36 @@ func (st *Local) removeSector(ctx context.Context, sid abi.SectorID, typ storifa return nil } -func (st *Local) MoveStorage(ctx context.Context, s storiface.SectorRef, types storiface.SectorFileType) error { - dest, destIds, err := st.AcquireSector(ctx, s, storiface.FTNone, types, storiface.PathStorage, storiface.AcquireMove) - if err != nil { - return xerrors.Errorf("acquire dest storage: %w", err) +func (st *Local) MoveStorage(ctx context.Context, s storiface.SectorRef, types storiface.SectorFileType, opts ...storiface.AcquireOption) error { + settings := storiface.AcquireSettings{ + // If into is nil then we're expecting the data to be there already, but make sure here + Into: nil, + } + for _, o := range opts { + o(&settings) } + var err error + var dest, destIds storiface.SectorPaths + if settings.Into == nil { + dest, destIds, err = st.AcquireSector(ctx, s, storiface.FTNone, types, storiface.PathStorage, storiface.AcquireMove) + if err != nil { + return xerrors.Errorf("acquire dest storage: %w", err) + } + } else { + // destination from settings + dest = settings.Into.Paths + destIds = settings.Into.IDs + } + + // note: this calls allocate on types - if data is already in paths of correct type, + // the returned paths are guaranteed to be the same as dest src, srcIds, err := st.AcquireSector(ctx, s, types, storiface.FTNone, storiface.PathStorage, storiface.AcquireMove) if err != nil { return xerrors.Errorf("acquire src storage: %w", err) } - for _, fileType := range storiface.PathTypes { - if fileType&types == 0 { - continue - } - + for _, fileType := range types.AllSet() { sst, err := st.index.StorageInfo(ctx, storiface.ID(storiface.PathByType(srcIds, fileType))) if err != nil { return xerrors.Errorf("failed to get source storage info: %w", err) diff --git a/storage/paths/mocks/store.go b/storage/paths/mocks/store.go index d7fa226e6..1224e6b57 100644 --- a/storage/paths/mocks/store.go +++ b/storage/paths/mocks/store.go @@ -107,17 +107,22 @@ func (mr *MockStoreMockRecorder) GenerateSingleVanillaProof(arg0, arg1, arg2, ar } // MoveStorage mocks base method. -func (m *MockStore) MoveStorage(arg0 context.Context, arg1 storiface.SectorRef, arg2 storiface.SectorFileType) error { +func (m *MockStore) MoveStorage(arg0 context.Context, arg1 storiface.SectorRef, arg2 storiface.SectorFileType, arg3 ...storiface.AcquireOption) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "MoveStorage", arg0, arg1, arg2) + varargs := []interface{}{arg0, arg1, arg2} + for _, a := range arg3 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "MoveStorage", varargs...) ret0, _ := ret[0].(error) return ret0 } // MoveStorage indicates an expected call of MoveStorage. -func (mr *MockStoreMockRecorder) MoveStorage(arg0, arg1, arg2 interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) MoveStorage(arg0, arg1, arg2 interface{}, arg3 ...interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MoveStorage", reflect.TypeOf((*MockStore)(nil).MoveStorage), arg0, arg1, arg2) + varargs := append([]interface{}{arg0, arg1, arg2}, arg3...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MoveStorage", reflect.TypeOf((*MockStore)(nil).MoveStorage), varargs...) } // Remove mocks base method. diff --git a/storage/paths/remote.go b/storage/paths/remote.go index 8532357b4..abf8622e1 100644 --- a/storage/paths/remote.go +++ b/storage/paths/remote.go @@ -325,14 +325,14 @@ func (r *Remote) checkAllocated(ctx context.Context, url string, spt abi.Registe } } -func (r *Remote) MoveStorage(ctx context.Context, s storiface.SectorRef, types storiface.SectorFileType) error { +func (r *Remote) MoveStorage(ctx context.Context, s storiface.SectorRef, types storiface.SectorFileType, opts ...storiface.AcquireOption) error { // Make sure we have the data local - _, _, err := r.AcquireSector(ctx, s, types, storiface.FTNone, storiface.PathStorage, storiface.AcquireMove) + _, _, err := r.AcquireSector(ctx, s, types, storiface.FTNone, storiface.PathStorage, storiface.AcquireMove, opts...) if err != nil { return xerrors.Errorf("acquire src storage (remote): %w", err) } - return r.local.MoveStorage(ctx, s, types) + return r.local.MoveStorage(ctx, s, types, opts...) } func (r *Remote) Remove(ctx context.Context, sid abi.SectorID, typ storiface.SectorFileType, force bool, keepIn []storiface.ID) error { From 316a0d6c2603d417357c1bb5e053e55e9f1457e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 3 Apr 2024 11:26:34 +0200 Subject: [PATCH 314/394] fix: curio: Pass taskID to TreeDRC --- curiosrc/ffi/sdr_funcs.go | 4 ++-- curiosrc/seal/task_trees.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/curiosrc/ffi/sdr_funcs.go b/curiosrc/ffi/sdr_funcs.go index 583a536fe..c68aa3c5c 100644 --- a/curiosrc/ffi/sdr_funcs.go +++ b/curiosrc/ffi/sdr_funcs.go @@ -161,13 +161,13 @@ func (sb *SealCalls) GenerateSDR(ctx context.Context, taskID harmonytask.TaskID, return nil } -func (sb *SealCalls) TreeDRC(ctx context.Context, sector storiface.SectorRef, unsealed cid.Cid, size abi.PaddedPieceSize, data io.Reader, unpaddedData bool) (cid.Cid, cid.Cid, error) { +func (sb *SealCalls) TreeDRC(ctx context.Context, task *harmonytask.TaskID, sector storiface.SectorRef, unsealed cid.Cid, size abi.PaddedPieceSize, data io.Reader, unpaddedData bool) (cid.Cid, cid.Cid, error) { p1o, err := sb.makePhase1Out(unsealed, sector.ProofType) if err != nil { return cid.Undef, cid.Undef, xerrors.Errorf("make phase1 output: %w", err) } - paths, releaseSector, err := sb.sectors.AcquireSector(ctx, nil, sector, storiface.FTCache, storiface.FTSealed, storiface.PathSealing) + paths, releaseSector, err := sb.sectors.AcquireSector(ctx, task, sector, storiface.FTCache, storiface.FTSealed, storiface.PathSealing) if err != nil { return cid.Undef, cid.Undef, xerrors.Errorf("acquiring sector paths: %w", err) } diff --git a/curiosrc/seal/task_trees.go b/curiosrc/seal/task_trees.go index ccf9ab5aa..8a178bd90 100644 --- a/curiosrc/seal/task_trees.go +++ b/curiosrc/seal/task_trees.go @@ -187,9 +187,9 @@ func (t *TreesTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (done } // D / R / C - sealed, unsealed, err := t.sc.TreeDRC(ctx, sref, commd, abi.PaddedPieceSize(ssize), dataReader, unpaddedData) + sealed, unsealed, err := t.sc.TreeDRC(ctx, &taskID, sref, commd, abi.PaddedPieceSize(ssize), dataReader, unpaddedData) if err != nil { - return false, xerrors.Errorf("computing tree r and c: %w", err) + return false, xerrors.Errorf("computing tree d, r and c: %w", err) } // todo synth porep From 6997162c71890b96492186f370506bfb41837f05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 3 Apr 2024 15:13:59 +0200 Subject: [PATCH 315/394] curio storage: Add missing reservation comment in MoveStorage --- curiosrc/ffi/sdr_funcs.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/curiosrc/ffi/sdr_funcs.go b/curiosrc/ffi/sdr_funcs.go index c68aa3c5c..325d30ef2 100644 --- a/curiosrc/ffi/sdr_funcs.go +++ b/curiosrc/ffi/sdr_funcs.go @@ -508,6 +508,9 @@ func (sb *SealCalls) MoveStorage(ctx context.Context, sector storiface.SectorRef var opts []storiface.AcquireOption if taskID != nil { resv, ok := sb.sectors.storageReservations.Load(*taskID) + // if the reservation is missing MoveStorage will simply create one internally. This is fine as the reservation + // will only be missing when the node is restarting, which means that the missing reservations will get recreated + // anyways, and before we start claiming other tasks. if ok { defer resv.Release() From 416d7ef86010a50a0975db20bf1fabc12059c391 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 3 Apr 2024 15:22:42 +0200 Subject: [PATCH 316/394] fix: curio: Pass taskID to WritePiece --- curiosrc/ffi/piece_funcs.go | 5 +++-- curiosrc/piece/task_park_piece.go | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/curiosrc/ffi/piece_funcs.go b/curiosrc/ffi/piece_funcs.go index 9bf4a504b..8b7df01ed 100644 --- a/curiosrc/ffi/piece_funcs.go +++ b/curiosrc/ffi/piece_funcs.go @@ -8,13 +8,14 @@ import ( "golang.org/x/xerrors" + "github.com/filecoin-project/lotus/lib/harmony/harmonytask" "github.com/filecoin-project/lotus/storage/sealer/storiface" ) -func (sb *SealCalls) WritePiece(ctx context.Context, pieceID storiface.PieceNumber, size int64, data io.Reader) error { +func (sb *SealCalls) WritePiece(ctx context.Context, taskID *harmonytask.TaskID, pieceID storiface.PieceNumber, size int64, data io.Reader) error { // todo: config(?): allow setting PathStorage for this // todo storage reservations - paths, done, err := sb.sectors.AcquireSector(ctx, nil, pieceID.Ref(), storiface.FTNone, storiface.FTPiece, storiface.PathSealing) + paths, done, err := sb.sectors.AcquireSector(ctx, taskID, pieceID.Ref(), storiface.FTNone, storiface.FTPiece, storiface.PathSealing) if err != nil { return err } diff --git a/curiosrc/piece/task_park_piece.go b/curiosrc/piece/task_park_piece.go index c8972fdaf..2085637f6 100644 --- a/curiosrc/piece/task_park_piece.go +++ b/curiosrc/piece/task_park_piece.go @@ -152,7 +152,7 @@ func (p *ParkPieceTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (d pnum := storiface.PieceNumber(pieceData.PieceID) - if err := p.sc.WritePiece(ctx, pnum, pieceRawSize, upr); err != nil { + if err := p.sc.WritePiece(ctx, &taskID, pnum, pieceRawSize, upr); err != nil { return false, xerrors.Errorf("write piece: %w", err) } From f1c04c8f96191ebc963c90f59aeaf5d5bfe96602 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 3 Apr 2024 16:25:59 +0200 Subject: [PATCH 317/394] curio cli: Add --local to storage list cli --- cmd/curio/storage.go | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/cmd/curio/storage.go b/cmd/curio/storage.go index 7b57e9438..c5fe77e18 100644 --- a/cmd/curio/storage.go +++ b/cmd/curio/storage.go @@ -207,8 +207,14 @@ var storageDetachCmd = &cli.Command{ } var storageListCmd = &cli.Command{ - Name: "list", - Usage: "list local storage paths", + Name: "list", + Usage: "list local storage paths", + Flags: []cli.Flag{ + &cli.BoolFlag{ + Name: "local", + Usage: "only list local storage paths", + }, + }, Subcommands: []*cli.Command{ //storageListSectorsCmd, }, @@ -238,6 +244,12 @@ var storageListCmd = &cli.Command{ sorted := make([]fsInfo, 0, len(st)) for id, decls := range st { + if cctx.Bool("local") { + if _, ok := local[id]; !ok { + continue + } + } + st, err := minerApi.StorageStat(ctx, id) if err != nil { sorted = append(sorted, fsInfo{ID: id, sectors: decls}) From 9f9dc979fb4a234a024d5d85a0ac430541e72426 Mon Sep 17 00:00:00 2001 From: LexLuthr <88259624+LexLuthr@users.noreply.github.com> Date: Thu, 4 Apr 2024 00:00:14 +0400 Subject: [PATCH 318/394] feat: curio: add miner init (#11775) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * sptool: Initial structure * sptool: Port lotus-miner actor withdraw * sptool: Make cli docsgen happy * actors are done * info * proving * sptool the rest * fixed gitignore * lints * oops * 2 * terminate * fixes * curio new-miner setup * reword doc section * fix curio flags * fix gen * change repetitive prompt * add miner to base * add config test * fix config compare, add tests * go mod tidy * fix itest in GA * fix comparer function * fix compare value match --------- Co-authored-by: Łukasz Magiera Co-authored-by: Andrew Jackson (Ajax) --- .circleci/config.yml | 6 + .github/workflows/test.yml | 2 +- cli/spcli/actor.go | 197 ++- cmd/curio/config.go | 9 - cmd/curio/config_new.go | 112 +- cmd/curio/config_test.go | 416 +++++++ cmd/curio/deps/deps.go | 97 ++ cmd/curio/guidedsetup/guidedsetup.go | 433 +++++-- cmd/curio/internal/translations/catalog.go | 688 ++++++----- .../translations/locales/en/out.gotext.json | 1068 ++++++++++++----- .../locales/ko/messages.gotext.json | 432 ++++++- .../translations/locales/ko/out.gotext.json | 87 +- .../locales/zh/messages.gotext.json | 348 ++++++ .../translations/locales/zh/out.gotext.json | 87 +- cmd/curio/internal/translations/updateLang.sh | 2 +- cmd/curio/main.go | 3 +- cmd/sptool/actor.go | 1 + cmd/sptool/sector.go | 1 - documentation/en/cli-curio.md | 7 +- documentation/en/cli-sptool.md | 17 + go.mod | 2 +- itests/curio_test.go | 67 ++ node/config/load.go | 22 +- 23 files changed, 3160 insertions(+), 944 deletions(-) create mode 100644 cmd/curio/config_test.go create mode 100644 itests/curio_test.go diff --git a/.circleci/config.yml b/.circleci/config.yml index 30ebaf5d9..be70019a3 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -554,6 +554,12 @@ workflows: - build suite: itest-cli target: "./itests/cli_test.go" + - test: + name: test-itest-curio + requires: + - build + suite: itest-curio + target: "./itests/curio_test.go" - test: name: test-itest-deadlines requires: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 127dd58d3..ad274dfa5 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -124,7 +124,7 @@ jobs: # A list of test groups that require YugabyteDB to be running # In CircleCI, all jobs had yugabytedb running as a sidecar. yugabytedb: | - ["itest-harmonydb", "itest-harmonytask"] + ["itest-harmonydb", "itest-harmonytask", "itest-curio"] # A list of test groups that require Proof Parameters to be fetched # In CircleCI, only the following jobs had get-params set: # - unit-cli (✅) diff --git a/cli/spcli/actor.go b/cli/spcli/actor.go index 296d5ffb1..33590de50 100644 --- a/cli/spcli/actor.go +++ b/cli/spcli/actor.go @@ -2,9 +2,11 @@ package spcli import ( "bytes" + "context" "fmt" "strconv" + "github.com/docker/go-units" cbor "github.com/ipfs/go-ipld-cbor" "github.com/libp2p/go-libp2p/core/peer" ma "github.com/multiformats/go-multiaddr" @@ -19,16 +21,20 @@ import ( "github.com/filecoin-project/go-state-types/builtin" "github.com/filecoin-project/go-state-types/builtin/v9/miner" "github.com/filecoin-project/go-state-types/network" + power2 "github.com/filecoin-project/specs-actors/v2/actors/builtin/power" + power6 "github.com/filecoin-project/specs-actors/v6/actors/builtin/power" - "github.com/filecoin-project/lotus/api" lapi "github.com/filecoin-project/lotus/api" + "github.com/filecoin-project/lotus/api/v1api" "github.com/filecoin-project/lotus/blockstore" "github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/chain/actors" "github.com/filecoin-project/lotus/chain/actors/adt" lminer "github.com/filecoin-project/lotus/chain/actors/builtin/miner" + "github.com/filecoin-project/lotus/chain/actors/builtin/power" "github.com/filecoin-project/lotus/chain/types" lcli "github.com/filecoin-project/lotus/cli" + cliutil "github.com/filecoin-project/lotus/cli/util" "github.com/filecoin-project/lotus/node/impl" ) @@ -1225,7 +1231,7 @@ func ActorCompactAllocatedCmd(getActor ActorAddressGetter) *cli.Command { } } -func isController(mi api.MinerInfo, addr address.Address) bool { +func isController(mi lapi.MinerInfo, addr address.Address) bool { if addr == mi.Owner || addr == mi.Worker { return true } @@ -1238,3 +1244,190 @@ func isController(mi api.MinerInfo, addr address.Address) bool { return false } + +var ActorNewMinerCmd = &cli.Command{ + Name: "new-miner", + Usage: "Initializes a new miner actor", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "worker", + Aliases: []string{"w"}, + Usage: "worker key to use for new miner initialisation", + }, + &cli.StringFlag{ + Name: "owner", + Aliases: []string{"o"}, + Usage: "owner key to use for new miner initialisation", + }, + &cli.StringFlag{ + Name: "from", + Aliases: []string{"f"}, + Usage: "address to send actor(miner) creation message from", + }, + &cli.StringFlag{ + Name: "sector-size", + Usage: "specify sector size to use for new miner initialisation", + }, + }, + Action: func(cctx *cli.Context) error { + ctx := cctx.Context + + full, closer, err := cliutil.GetFullNodeAPIV1(cctx) + if err != nil { + return xerrors.Errorf("connecting to full node: %w", err) + } + defer closer() + + var owner address.Address + if cctx.String("owner") == "" { + return xerrors.Errorf("must provide a owner address") + } + owner, err = address.NewFromString(cctx.String("owner")) + + if err != nil { + return err + } + + worker := owner + if cctx.String("worker") != "" { + worker, err = address.NewFromString(cctx.String("worker")) + if err != nil { + return xerrors.Errorf("could not parse worker address: %w", err) + } + } + + sender := owner + if fromstr := cctx.String("from"); fromstr != "" { + faddr, err := address.NewFromString(fromstr) + if err != nil { + return xerrors.Errorf("could not parse from address: %w", err) + } + sender = faddr + } + + if !cctx.IsSet("sector-size") { + return xerrors.Errorf("must define sector size") + } + + sectorSizeInt, err := units.RAMInBytes(cctx.String("sector-size")) + if err != nil { + return err + } + ssize := abi.SectorSize(sectorSizeInt) + + _, err = CreateStorageMiner(ctx, full, owner, worker, sender, ssize, cctx.Uint64("confidence")) + if err != nil { + return err + } + return nil + }, +} + +func CreateStorageMiner(ctx context.Context, fullNode v1api.FullNode, owner, worker, sender address.Address, ssize abi.SectorSize, confidence uint64) (address.Address, error) { + // make sure the sender account exists on chain + _, err := fullNode.StateLookupID(ctx, owner, types.EmptyTSK) + if err != nil { + return address.Undef, xerrors.Errorf("sender must exist on chain: %w", err) + } + + // make sure the worker account exists on chain + _, err = fullNode.StateLookupID(ctx, worker, types.EmptyTSK) + if err != nil { + signed, err := fullNode.MpoolPushMessage(ctx, &types.Message{ + From: sender, + To: worker, + Value: types.NewInt(0), + }, nil) + if err != nil { + return address.Undef, xerrors.Errorf("push worker init: %w", err) + } + + fmt.Printf("Initializing worker account %s, message: %s\n", worker, signed.Cid()) + fmt.Println("Waiting for confirmation") + + mw, err := fullNode.StateWaitMsg(ctx, signed.Cid(), confidence, 2000, true) + if err != nil { + return address.Undef, xerrors.Errorf("waiting for worker init: %w", err) + } + if mw.Receipt.ExitCode != 0 { + return address.Undef, xerrors.Errorf("initializing worker account failed: exit code %d", mw.Receipt.ExitCode) + } + } + + // make sure the owner account exists on chain + _, err = fullNode.StateLookupID(ctx, owner, types.EmptyTSK) + if err != nil { + signed, err := fullNode.MpoolPushMessage(ctx, &types.Message{ + From: sender, + To: owner, + Value: types.NewInt(0), + }, nil) + if err != nil { + return address.Undef, xerrors.Errorf("push owner init: %w", err) + } + + fmt.Printf("Initializing owner account %s, message: %s\n", worker, signed.Cid()) + fmt.Println("Waiting for confirmation") + + mw, err := fullNode.StateWaitMsg(ctx, signed.Cid(), confidence, 2000, true) + if err != nil { + return address.Undef, xerrors.Errorf("waiting for owner init: %w", err) + } + if mw.Receipt.ExitCode != 0 { + return address.Undef, xerrors.Errorf("initializing owner account failed: exit code %d", mw.Receipt.ExitCode) + } + } + + // Note: the correct thing to do would be to call SealProofTypeFromSectorSize if actors version is v3 or later, but this still works + nv, err := fullNode.StateNetworkVersion(ctx, types.EmptyTSK) + if err != nil { + return address.Undef, xerrors.Errorf("failed to get network version: %w", err) + } + spt, err := lminer.WindowPoStProofTypeFromSectorSize(ssize, nv) + if err != nil { + return address.Undef, xerrors.Errorf("getting post proof type: %w", err) + } + + params, err := actors.SerializeParams(&power6.CreateMinerParams{ + Owner: owner, + Worker: worker, + WindowPoStProofType: spt, + }) + if err != nil { + return address.Undef, err + } + + createStorageMinerMsg := &types.Message{ + To: power.Address, + From: sender, + Value: big.Zero(), + + Method: power.Methods.CreateMiner, + Params: params, + } + + signed, err := fullNode.MpoolPushMessage(ctx, createStorageMinerMsg, nil) + if err != nil { + return address.Undef, xerrors.Errorf("pushing createMiner message: %w", err) + } + + fmt.Printf("Pushed CreateMiner message: %s\n", signed.Cid()) + fmt.Println("Waiting for confirmation") + + mw, err := fullNode.StateWaitMsg(ctx, signed.Cid(), confidence, 2000, true) + if err != nil { + return address.Undef, xerrors.Errorf("waiting for createMiner message: %w", err) + } + + if mw.Receipt.ExitCode != 0 { + return address.Undef, xerrors.Errorf("create miner failed: exit code %d", mw.Receipt.ExitCode) + } + + var retval power2.CreateMinerReturn + if err := retval.UnmarshalCBOR(bytes.NewReader(mw.Receipt.Return)); err != nil { + return address.Undef, err + } + + fmt.Printf("New miners address is: %s (%s)\n", retval.IDAddress, retval.RobustAddress) + return retval.IDAddress, nil +} diff --git a/cmd/curio/config.go b/cmd/curio/config.go index 2938118ad..66bb2e8b4 100644 --- a/cmd/curio/config.go +++ b/cmd/curio/config.go @@ -352,15 +352,6 @@ var configEditCmd = &cli.Command{ }, } -func getDefaultConfig(comment bool) (string, error) { - c := config.DefaultCurioConfig() - cb, err := config.ConfigUpdate(c, nil, config.Commented(comment), config.DefaultKeepUncommented(), config.NoEnv()) - if err != nil { - return "", err - } - return string(cb), nil -} - func diff(sourceConf, newConf string) (string, error) { lpSrc := config.DefaultCurioConfig() lpNew := config.DefaultCurioConfig() diff --git a/cmd/curio/config_new.go b/cmd/curio/config_new.go index 38ca44597..65549bd69 100644 --- a/cmd/curio/config_new.go +++ b/cmd/curio/config_new.go @@ -1,25 +1,14 @@ package main import ( - "bytes" - "crypto/rand" - "encoding/base64" "fmt" - "io" - "github.com/BurntSushi/toml" - "github.com/fatih/color" - "github.com/samber/lo" "github.com/urfave/cli/v2" "golang.org/x/xerrors" - "github.com/filecoin-project/go-address" - "github.com/filecoin-project/lotus/api" - "github.com/filecoin-project/lotus/chain/types" cliutil "github.com/filecoin-project/lotus/cli/util" "github.com/filecoin-project/lotus/cmd/curio/deps" - "github.com/filecoin-project/lotus/node/config" "github.com/filecoin-project/lotus/node/repo" ) @@ -36,10 +25,8 @@ var configNewCmd = &cli.Command{ }, }, Action: func(cctx *cli.Context) error { - configColor := color.New(color.FgHiGreen).SprintFunc() - if cctx.Args().Len() < 1 { - return xerrors.New("must specify at least one SP actor address. Use 'lotus-shed miner create'") + return xerrors.New("must specify at least one SP actor address. Use 'lotus-shed miner create' or use 'curio guided-setup'") } ctx := cctx.Context @@ -55,105 +42,16 @@ var configNewCmd = &cli.Command{ } defer closer() - var titles []string - err = db.Select(ctx, &titles, `SELECT title FROM harmony_config WHERE LENGTH(config) > 0`) + ainfo, err := cliutil.GetAPIInfo(cctx, repo.FullNode) if err != nil { - return fmt.Errorf("miner cannot reach the db. Ensure the config toml's HarmonyDB entry"+ - " is setup to reach Yugabyte correctly: %s", err.Error()) + return xerrors.Errorf("could not get API info for FullNode: %w", err) } - name := cctx.String("to-layer") - if name == "" { - name = fmt.Sprintf("cluster%d", len(titles)) - } else { - if lo.Contains(titles, name) && !cctx.Bool("overwrite") { - return xerrors.New("the overwrite flag is needed to replace existing layer: " + name) - } - } - msg := "Layer " + configColor(name) + ` created. ` - - // setup config - lpCfg := config.DefaultCurioConfig() - - for _, addr := range cctx.Args().Slice() { - maddr, err := address.NewFromString(addr) - if err != nil { - return xerrors.Errorf("Invalid address: %s", addr) - } - - _, err = full.StateMinerInfo(ctx, maddr, types.EmptyTSK) - if err != nil { - return xerrors.Errorf("Failed to get miner info: %w", err) - } - - lpCfg.Addresses = append(lpCfg.Addresses, config.CurioAddresses{ - PreCommitControl: nil, - CommitControl: nil, - TerminateControl: nil, - DisableOwnerFallback: false, - DisableWorkerFallback: false, - MinerAddresses: []string{addr}, - }) - } - - { - sk, err := io.ReadAll(io.LimitReader(rand.Reader, 32)) - if err != nil { - return err - } - - lpCfg.Apis.StorageRPCSecret = base64.StdEncoding.EncodeToString(sk) - } - - { - ainfo, err := cliutil.GetAPIInfo(cctx, repo.FullNode) - if err != nil { - return xerrors.Errorf("could not get API info for FullNode: %w", err) - } - - token, err := full.AuthNew(ctx, api.AllPermissions) - if err != nil { - return err - } - - lpCfg.Apis.ChainApiInfo = append(lpCfg.Apis.ChainApiInfo, fmt.Sprintf("%s:%s", string(token), ainfo.Addr)) - } - - // write config - - configTOML := &bytes.Buffer{} - if err = toml.NewEncoder(configTOML).Encode(lpCfg); err != nil { - return err - } - - if !lo.Contains(titles, "base") { - cfg, err := getDefaultConfig(true) - if err != nil { - return xerrors.Errorf("Cannot get default config: %w", err) - } - _, err = db.Exec(ctx, "INSERT INTO harmony_config (title, config) VALUES ('base', $1)", cfg) - - if err != nil { - return err - } - } - - if cctx.Bool("overwrite") { - i, err := db.Exec(ctx, "DELETE FROM harmony_config WHERE title=$1", name) - if i != 0 { - fmt.Println("Overwriting existing layer") - } - if err != nil { - fmt.Println("Got error while deleting existing layer: " + err.Error()) - } - } - - _, err = db.Exec(ctx, "INSERT INTO harmony_config (title, config) VALUES ($1, $2)", name, configTOML.String()) + token, err := full.AuthNew(ctx, api.AllPermissions) if err != nil { return err } - fmt.Println(msg) - return nil + return deps.CreateMinerConfig(ctx, full, db, cctx.Args().Slice(), fmt.Sprintf("%s:%s", string(token), ainfo.Addr)) }, } diff --git a/cmd/curio/config_test.go b/cmd/curio/config_test.go new file mode 100644 index 000000000..31c4b4bd0 --- /dev/null +++ b/cmd/curio/config_test.go @@ -0,0 +1,416 @@ +package main + +import ( + "testing" + + "github.com/samber/lo" + "github.com/stretchr/testify/require" + + "github.com/filecoin-project/lotus/cmd/curio/deps" + "github.com/filecoin-project/lotus/node/config" +) + +var baseText string = ` +[Subsystems] + # EnableWindowPost enables window post to be executed on this lotus-provider instance. Each machine in the cluster + # with WindowPoSt enabled will also participate in the window post scheduler. It is possible to have multiple + # machines with WindowPoSt enabled which will provide redundancy, and in case of multiple partitions per deadline, + # will allow for parallel processing of partitions. + # + # It is possible to have instances handling both WindowPoSt and WinningPoSt, which can provide redundancy without + # the need for additional machines. In setups like this it is generally recommended to run + # partitionsPerDeadline+1 machines. + # + # type: bool + #EnableWindowPost = false + + # type: int + #WindowPostMaxTasks = 0 + + # EnableWinningPost enables winning post to be executed on this lotus-provider instance. + # Each machine in the cluster with WinningPoSt enabled will also participate in the winning post scheduler. + # It is possible to mix machines with WindowPoSt and WinningPoSt enabled, for details see the EnableWindowPost + # documentation. + # + # type: bool + #EnableWinningPost = false + + # type: int + #WinningPostMaxTasks = 0 + + # EnableParkPiece enables the "piece parking" task to run on this node. This task is responsible for fetching + # pieces from the network and storing them in the storage subsystem until sectors are sealed. This task is + # only applicable when integrating with boost, and should be enabled on nodes which will hold deal data + # from boost until sectors containing the related pieces have the TreeD/TreeR constructed. + # Note that future Curio implementations will have a separate task type for fetching pieces from the internet. + # + # type: bool + #EnableParkPiece = false + + # type: int + #ParkPieceMaxTasks = 0 + + # EnableSealSDR enables SDR tasks to run. SDR is the long sequential computation + # creating 11 layer files in sector cache directory. + # + # SDR is the first task in the sealing pipeline. It's inputs are just the hash of the + # unsealed data (CommD), sector number, miner id, and the seal proof type. + # It's outputs are the 11 layer files in the sector cache directory. + # + # In lotus-miner this was run as part of PreCommit1. + # + # type: bool + #EnableSealSDR = false + + # The maximum amount of SDR tasks that can run simultaneously. Note that the maximum number of tasks will + # also be bounded by resources available on the machine. + # + # type: int + #SealSDRMaxTasks = 0 + + # EnableSealSDRTrees enables the SDR pipeline tree-building task to run. + # This task handles encoding of unsealed data into last sdr layer and building + # of TreeR, TreeC and TreeD. + # + # This task runs after SDR + # TreeD is first computed with optional input of unsealed data + # TreeR is computed from replica, which is first computed as field + # addition of the last SDR layer and the bottom layer of TreeD (which is the unsealed data) + # TreeC is computed from the 11 SDR layers + # The 3 trees will later be used to compute the PoRep proof. + # + # In case of SyntheticPoRep challenges for PoRep will be pre-generated at this step, and trees and layers + # will be dropped. SyntheticPoRep works by pre-generating a very large set of challenges (~30GiB on disk) + # then using a small subset of them for the actual PoRep computation. This allows for significant scratch space + # saving between PreCommit and PoRep generation at the expense of more computation (generating challenges in this step) + # + # In lotus-miner this was run as part of PreCommit2 (TreeD was run in PreCommit1). + # Note that nodes with SDRTrees enabled will also answer to Finalize tasks, + # which just remove unneeded tree data after PoRep is computed. + # + # type: bool + #EnableSealSDRTrees = false + + # The maximum amount of SealSDRTrees tasks that can run simultaneously. Note that the maximum number of tasks will + # also be bounded by resources available on the machine. + # + # type: int + #SealSDRTreesMaxTasks = 0 + + # FinalizeMaxTasks is the maximum amount of finalize tasks that can run simultaneously. + # The finalize task is enabled on all machines which also handle SDRTrees tasks. Finalize ALWAYS runs on whichever + # machine holds sector cache files, as it removes unneeded tree data after PoRep is computed. + # Finalize will run in parallel with the SubmitCommitMsg task. + # + # type: int + #FinalizeMaxTasks = 0 + + # EnableSendPrecommitMsg enables the sending of precommit messages to the chain + # from this lotus-provider instance. + # This runs after SDRTrees and uses the output CommD / CommR (roots of TreeD / TreeR) for the message + # + # type: bool + #EnableSendPrecommitMsg = false + + # EnablePoRepProof enables the computation of the porep proof + # + # This task runs after interactive-porep seed becomes available, which happens 150 epochs (75min) after the + # precommit message lands on chain. This task should run on a machine with a GPU. Vanilla PoRep proofs are + # requested from the machine which holds sector cache files which most likely is the machine which ran the SDRTrees + # task. + # + # In lotus-miner this was Commit1 / Commit2 + # + # type: bool + #EnablePoRepProof = false + + # The maximum amount of PoRepProof tasks that can run simultaneously. Note that the maximum number of tasks will + # also be bounded by resources available on the machine. + # + # type: int + #PoRepProofMaxTasks = 0 + + # EnableSendCommitMsg enables the sending of commit messages to the chain + # from this lotus-provider instance. + # + # type: bool + #EnableSendCommitMsg = false + + # EnableMoveStorage enables the move-into-long-term-storage task to run on this lotus-provider instance. + # This tasks should only be enabled on nodes with long-term storage. + # + # The MoveStorage task is the last task in the sealing pipeline. It moves the sealed sector data from the + # SDRTrees machine into long-term storage. This task runs after the Finalize task. + # + # type: bool + #EnableMoveStorage = false + + # The maximum amount of MoveStorage tasks that can run simultaneously. Note that the maximum number of tasks will + # also be bounded by resources available on the machine. It is recommended that this value is set to a number which + # uses all available network (or disk) bandwidth on the machine without causing bottlenecks. + # + # type: int + #MoveStorageMaxTasks = 0 + + # EnableWebGui enables the web GUI on this lotus-provider instance. The UI has minimal local overhead, but it should + # only need to be run on a single machine in the cluster. + # + # type: bool + #EnableWebGui = false + + # The address that should listen for Web GUI requests. + # + # type: string + #GuiAddress = ":4701" + + +[Fees] + # type: types.FIL + #DefaultMaxFee = "0.07 FIL" + + # type: types.FIL + #MaxPreCommitGasFee = "0.025 FIL" + + # type: types.FIL + #MaxCommitGasFee = "0.05 FIL" + + # type: types.FIL + #MaxTerminateGasFee = "0.5 FIL" + + # WindowPoSt is a high-value operation, so the default fee should be high. + # + # type: types.FIL + #MaxWindowPoStGasFee = "5 FIL" + + # type: types.FIL + #MaxPublishDealsFee = "0.05 FIL" + + [Fees.MaxPreCommitBatchGasFee] + # type: types.FIL + #Base = "0 FIL" + + # type: types.FIL + #PerSector = "0.02 FIL" + + [Fees.MaxCommitBatchGasFee] + # type: types.FIL + #Base = "0 FIL" + + # type: types.FIL + #PerSector = "0.03 FIL" + +[[Addresses]] + #PreCommitControl = [] + + #CommitControl = [] + + #TerminateControl = [] + + #DisableOwnerFallback = false + + #DisableWorkerFallback = false + + MinerAddresses = ["t01013"] + + +[[Addresses]] + #PreCommitControl = [] + + #CommitControl = [] + + #TerminateControl = [] + + #DisableOwnerFallback = false + + #DisableWorkerFallback = false + + #MinerAddresses = [] + + +[[Addresses]] + #PreCommitControl = [] + + #CommitControl = [] + + #TerminateControl = [] + + #DisableOwnerFallback = false + + #DisableWorkerFallback = false + + MinerAddresses = ["t01006"] + + +[Proving] + # Maximum number of sector checks to run in parallel. (0 = unlimited) + # + # WARNING: Setting this value too high may make the node crash by running out of stack + # WARNING: Setting this value too low may make sector challenge reading much slower, resulting in failed PoSt due + # to late submission. + # + # After changing this option, confirm that the new value works in your setup by invoking + # 'lotus-miner proving compute window-post 0' + # + # type: int + #ParallelCheckLimit = 32 + + # Maximum amount of time a proving pre-check can take for a sector. If the check times out the sector will be skipped + # + # WARNING: Setting this value too low risks in sectors being skipped even though they are accessible, just reading the + # test challenge took longer than this timeout + # WARNING: Setting this value too high risks missing PoSt deadline in case IO operations related to this sector are + # blocked (e.g. in case of disconnected NFS mount) + # + # type: Duration + #SingleCheckTimeout = "10m0s" + + # Maximum amount of time a proving pre-check can take for an entire partition. If the check times out, sectors in + # the partition which didn't get checked on time will be skipped + # + # WARNING: Setting this value too low risks in sectors being skipped even though they are accessible, just reading the + # test challenge took longer than this timeout + # WARNING: Setting this value too high risks missing PoSt deadline in case IO operations related to this partition are + # blocked or slow + # + # type: Duration + #PartitionCheckTimeout = "20m0s" + + # Disable Window PoSt computation on the lotus-miner process even if no window PoSt workers are present. + # + # WARNING: If no windowPoSt workers are connected, window PoSt WILL FAIL resulting in faulty sectors which will need + # to be recovered. Before enabling this option, make sure your PoSt workers work correctly. + # + # After changing this option, confirm that the new value works in your setup by invoking + # 'lotus-miner proving compute window-post 0' + # + # type: bool + #DisableBuiltinWindowPoSt = false + + # Disable Winning PoSt computation on the lotus-miner process even if no winning PoSt workers are present. + # + # WARNING: If no WinningPoSt workers are connected, Winning PoSt WILL FAIL resulting in lost block rewards. + # Before enabling this option, make sure your PoSt workers work correctly. + # + # type: bool + #DisableBuiltinWinningPoSt = false + + # Disable WindowPoSt provable sector readability checks. + # + # In normal operation, when preparing to compute WindowPoSt, lotus-miner will perform a round of reading challenges + # from all sectors to confirm that those sectors can be proven. Challenges read in this process are discarded, as + # we're only interested in checking that sector data can be read. + # + # When using builtin proof computation (no PoSt workers, and DisableBuiltinWindowPoSt is set to false), this process + # can save a lot of time and compute resources in the case that some sectors are not readable - this is caused by + # the builtin logic not skipping snark computation when some sectors need to be skipped. + # + # When using PoSt workers, this process is mostly redundant, with PoSt workers challenges will be read once, and + # if challenges for some sectors aren't readable, those sectors will just get skipped. + # + # Disabling sector pre-checks will slightly reduce IO load when proving sectors, possibly resulting in shorter + # time to produce window PoSt. In setups with good IO capabilities the effect of this option on proving time should + # be negligible. + # + # NOTE: It likely is a bad idea to disable sector pre-checks in setups with no PoSt workers. + # + # NOTE: Even when this option is enabled, recovering sectors will be checked before recovery declaration message is + # sent to the chain + # + # After changing this option, confirm that the new value works in your setup by invoking + # 'lotus-miner proving compute window-post 0' + # + # type: bool + #DisableWDPoStPreChecks = false + + # Maximum number of partitions to prove in a single SubmitWindowPoSt messace. 0 = network limit (3 in nv21) + # + # A single partition may contain up to 2349 32GiB sectors, or 2300 64GiB sectors. + # // + # Note that setting this value lower may result in less efficient gas use - more messages will be sent, + # to prove each deadline, resulting in more total gas use (but each message will have lower gas limit) + # + # Setting this value above the network limit has no effect + # + # type: int + #MaxPartitionsPerPoStMessage = 0 + + # In some cases when submitting DeclareFaultsRecovered messages, + # there may be too many recoveries to fit in a BlockGasLimit. + # In those cases it may be necessary to set this value to something low (eg 1); + # Note that setting this value lower may result in less efficient gas use - more messages will be sent than needed, + # resulting in more total gas use (but each message will have lower gas limit) + # + # type: int + #MaxPartitionsPerRecoveryMessage = 0 + + # Enable single partition per PoSt Message for partitions containing recovery sectors + # + # In cases when submitting PoSt messages which contain recovering sectors, the default network limit may still be + # too high to fit in the block gas limit. In those cases, it becomes useful to only house the single partition + # with recovering sectors in the post message + # + # Note that setting this value lower may result in less efficient gas use - more messages will be sent, + # to prove each deadline, resulting in more total gas use (but each message will have lower gas limit) + # + # type: bool + #SingleRecoveringPartitionPerPostMessage = false + + +[Journal] + # Events of the form: "system1:event1,system1:event2[,...]" + # + # type: string + #DisabledEvents = "" + + +[Apis] + # ChainApiInfo is the API endpoint for the Lotus daemon. + # + # type: []string + ChainApiInfo = ["eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJBbGxvdyI6WyJyZWFkIiwid3JpdGUiLCJzaWduIiwiYWRtaW4iXX0.T_jmG4DTs9Zjd7rr78862lT7D2U63uz-zqcUKHwcqaU:/dns/localhost/tcp/1234/http"] + + # RPC Secret for the storage subsystem. + # If integrating with lotus-miner this must match the value from + # cat ~/.lotusminer/keystore/MF2XI2BNNJ3XILLQOJUXMYLUMU | jq -r .PrivateKey + # + # type: string + StorageRPCSecret = "HxHe8YLHiY0LjHVw/WT/4XQkPGgRyCEYk+xiFi0Ob0o=" + +` + +func TestConfig(t *testing.T) { + baseCfg := config.DefaultCurioConfig() + + addr1 := config.CurioAddresses{ + PreCommitControl: []string{}, + CommitControl: []string{}, + TerminateControl: []string{"t3qroiebizgkz7pvj26reg5r5mqiftrt5hjdske2jzjmlacqr2qj7ytjncreih2mvujxoypwpfusmwpipvxncq"}, + DisableOwnerFallback: false, + DisableWorkerFallback: false, + MinerAddresses: []string{"t01000"}, + } + + addr2 := config.CurioAddresses{ + MinerAddresses: []string{"t01001"}, + } + + _, err := deps.LoadConfigWithUpgrades(baseText, baseCfg) + require.NoError(t, err) + + baseCfg.Addresses = append(baseCfg.Addresses, addr1) + baseCfg.Addresses = lo.Filter(baseCfg.Addresses, func(a config.CurioAddresses, _ int) bool { + return len(a.MinerAddresses) > 0 + }) + + _, err = config.ConfigUpdate(baseCfg, config.DefaultCurioConfig(), config.Commented(true), config.DefaultKeepUncommented(), config.NoEnv()) + require.NoError(t, err) + + baseCfg.Addresses = append(baseCfg.Addresses, addr2) + baseCfg.Addresses = lo.Filter(baseCfg.Addresses, func(a config.CurioAddresses, _ int) bool { + return len(a.MinerAddresses) > 0 + }) + + _, err = config.ConfigUpdate(baseCfg, config.DefaultCurioConfig(), config.Commented(true), config.DefaultKeepUncommented(), config.NoEnv()) + require.NoError(t, err) + +} diff --git a/cmd/curio/deps/deps.go b/cmd/curio/deps/deps.go index c25741cbc..79881ebb8 100644 --- a/cmd/curio/deps/deps.go +++ b/cmd/curio/deps/deps.go @@ -3,10 +3,12 @@ package deps import ( "context" + "crypto/rand" "database/sql" "encoding/base64" "errors" "fmt" + "io" "net" "net/http" "net/url" @@ -29,6 +31,8 @@ import ( "github.com/filecoin-project/go-statestore" "github.com/filecoin-project/lotus/api" + "github.com/filecoin-project/lotus/api/v1api" + "github.com/filecoin-project/lotus/chain/types" curio "github.com/filecoin-project/lotus/curiosrc" "github.com/filecoin-project/lotus/curiosrc/multictladdr" "github.com/filecoin-project/lotus/journal" @@ -434,3 +438,96 @@ func GetDepsCLI(ctx context.Context, cctx *cli.Context) (*Deps, error) { Full: full, }, nil } + +func CreateMinerConfig(ctx context.Context, full v1api.FullNode, db *harmonydb.DB, miners []string, info string) error { + var titles []string + err := db.Select(ctx, &titles, `SELECT title FROM harmony_config WHERE LENGTH(config) > 0`) + if err != nil { + return fmt.Errorf("cannot reach the db. Ensure that Yugabyte flags are set correctly to"+ + " reach Yugabyte: %s", err.Error()) + } + + // setup config + curioConfig := config.DefaultCurioConfig() + + for _, addr := range miners { + maddr, err := address.NewFromString(addr) + if err != nil { + return xerrors.Errorf("Invalid address: %s", addr) + } + + _, err = full.StateMinerInfo(ctx, maddr, types.EmptyTSK) + if err != nil { + return xerrors.Errorf("Failed to get miner info: %w", err) + } + + curioConfig.Addresses = append(curioConfig.Addresses, config.CurioAddresses{ + PreCommitControl: []string{}, + CommitControl: []string{}, + TerminateControl: []string{}, + DisableOwnerFallback: false, + DisableWorkerFallback: false, + MinerAddresses: []string{addr}, + }) + } + + { + sk, err := io.ReadAll(io.LimitReader(rand.Reader, 32)) + if err != nil { + return err + } + + curioConfig.Apis.StorageRPCSecret = base64.StdEncoding.EncodeToString(sk) + } + + { + curioConfig.Apis.ChainApiInfo = append(curioConfig.Apis.ChainApiInfo, info) + } + + curioConfig.Addresses = lo.Filter(curioConfig.Addresses, func(a config.CurioAddresses, _ int) bool { + return len(a.MinerAddresses) > 0 + }) + + // If no base layer is present + if !lo.Contains(titles, "base") { + cb, err := config.ConfigUpdate(curioConfig, config.DefaultCurioConfig(), config.Commented(true), config.DefaultKeepUncommented(), config.NoEnv()) + if err != nil { + return xerrors.Errorf("Failed to generate default config: %w", err) + } + cfg := string(cb) + _, err = db.Exec(ctx, "INSERT INTO harmony_config (title, config) VALUES ('base', $1)", cfg) + if err != nil { + return xerrors.Errorf("failed to insert the 'base' into the database: %w", err) + } + fmt.Printf("The base layer has been updated with miner[s] %s\n", miners) + return nil + } + + // if base layer is present + baseCfg := config.DefaultCurioConfig() + var baseText string + err = db.QueryRow(ctx, "SELECT config FROM harmony_config WHERE title='base'").Scan(&baseText) + if err != nil { + return xerrors.Errorf("Cannot load base config from database: %w", err) + } + _, err = LoadConfigWithUpgrades(baseText, baseCfg) + if err != nil { + return xerrors.Errorf("Cannot parse base config: %w", err) + } + + baseCfg.Addresses = append(baseCfg.Addresses, curioConfig.Addresses...) + baseCfg.Addresses = lo.Filter(baseCfg.Addresses, func(a config.CurioAddresses, _ int) bool { + return len(a.MinerAddresses) > 0 + }) + + cb, err := config.ConfigUpdate(baseCfg, config.DefaultCurioConfig(), config.Commented(true), config.DefaultKeepUncommented(), config.NoEnv()) + if err != nil { + return xerrors.Errorf("cannot interpret config: %w", err) + } + _, err = db.Exec(ctx, "UPDATE harmony_config SET config=$1 WHERE title='base'", string(cb)) + if err != nil { + return xerrors.Errorf("cannot update base config: %w", err) + } + fmt.Printf("The base layer has been updated with miner[s] %s\n", miners) + return nil +} diff --git a/cmd/curio/guidedsetup/guidedsetup.go b/cmd/curio/guidedsetup/guidedsetup.go index 038b07e3f..1af49d868 100644 --- a/cmd/curio/guidedsetup/guidedsetup.go +++ b/cmd/curio/guidedsetup/guidedsetup.go @@ -8,6 +8,7 @@ package guidedsetup import ( "bytes" "context" + "crypto/rand" "encoding/base64" "encoding/json" "fmt" @@ -18,12 +19,14 @@ import ( "os/signal" "path" "reflect" + "strconv" "strings" "syscall" "time" "github.com/BurntSushi/toml" "github.com/charmbracelet/lipgloss" + "github.com/docker/go-units" "github.com/manifoldco/promptui" "github.com/mitchellh/go-homedir" "github.com/samber/lo" @@ -33,12 +36,15 @@ import ( "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-jsonrpc" + "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/lotus/api" - "github.com/filecoin-project/lotus/api/v0api" + "github.com/filecoin-project/lotus/api/v1api" "github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/cli/spcli" cliutil "github.com/filecoin-project/lotus/cli/util" + "github.com/filecoin-project/lotus/cmd/curio/deps" _ "github.com/filecoin-project/lotus/cmd/curio/internal/translations" "github.com/filecoin-project/lotus/lib/harmony/harmonydb" "github.com/filecoin-project/lotus/node/config" @@ -50,7 +56,7 @@ const DeveloperFocusRequestURL = "https://curiostorage.org/cgi-bin/savedata.php" var GuidedsetupCmd = &cli.Command{ Name: "guided-setup", - Usage: "Run the guided setup for migrating from lotus-miner to Curio", + Usage: "Run the guided setup for migrating from lotus-miner to Curio or Creating a new Curio miner", Flags: []cli.Flag{ &cli.StringFlag{ // for cliutil.GetFullNodeAPI Name: "repo", @@ -63,9 +69,6 @@ var GuidedsetupCmd = &cli.Command{ T, say := SetupLanguage() setupCtrlC(say) - say(header, "This interactive tool migrates lotus-miner to Curio in 5 minutes.") - say(notice, "Each step needs your confirmation and can be reversed. Press Ctrl+C to exit at any time.") - // Run the migration steps migrationData := MigrationData{ T: T, @@ -74,10 +77,25 @@ var GuidedsetupCmd = &cli.Command{ Help: T("Use the arrow keys to navigate: ↓ ↑ → ← "), }, cctx: cctx, + ctx: cctx.Context, } - for _, step := range migrationSteps { - step(&migrationData) + + newOrMigrate(&migrationData) + if migrationData.init { + say(header, "This interactive tool creates a new miner actor and creates the basic configuration layer for it.") + say(notice, "This process is partially idempotent. Once a new miner actor has been created and subsequent steps fail, the user need to run 'curio config new-cluster < miner ID >' to finish the configuration.") + for _, step := range newMinerSteps { + step(&migrationData) + } + } else { + say(header, "This interactive tool migrates lotus-miner to Curio in 5 minutes.") + say(notice, "Each step needs your confirmation and can be reversed. Press Ctrl+C to exit at any time.") + + for _, step := range migrationSteps { + step(&migrationData) + } } + for _, closer := range migrationData.closers { closer() } @@ -164,6 +182,23 @@ func SetupLanguage() (func(key message.Reference, a ...interface{}) string, func } } +func newOrMigrate(d *MigrationData) { + i, _, err := (&promptui.Select{ + Label: d.T("I want to:"), + Items: []string{ + d.T("Migrate from existing Lotus-Miner"), + d.T("Create a new miner")}, + Templates: d.selectTemplates, + }).Run() + if err != nil { + d.say(notice, "Aborting remaining steps.", err.Error()) + os.Exit(1) + } + if i == 1 { + d.init = true + } +} + type migrationStep func(*MigrationData) var migrationSteps = []migrationStep{ @@ -176,6 +211,17 @@ var migrationSteps = []migrationStep{ complete, } +type newMinerStep func(data *MigrationData) + +var newMinerSteps = []newMinerStep{ + stepPresteps, + stepCreateActor, + stepNewMinerConfig, + doc, + oneLastThing, + completeInit, +} + type MigrationData struct { T func(key message.Reference, a ...interface{}) string say func(style lipgloss.Style, key message.Reference, a ...interface{}) @@ -184,9 +230,16 @@ type MigrationData struct { MinerConfig *config.StorageMiner DB *harmonydb.DB MinerID address.Address - full v0api.FullNode + full v1api.FullNode cctx *cli.Context closers []jsonrpc.ClientCloser + ctx context.Context + owner address.Address + worker address.Address + sender address.Address + ssize abi.SectorSize + confidence uint64 + init bool } func complete(d *MigrationData) { @@ -194,13 +247,19 @@ func complete(d *MigrationData) { d.say(plain, "Try the web interface with %s for further guided improvements.", "--layers=gui") d.say(plain, "You can now migrate your market node (%s), if applicable.", "Boost") } + +func completeInit(d *MigrationData) { + stepCompleted(d, d.T("New Miner initialization complete.")) + d.say(plain, "Try the web interface with %s for further guided improvements.", "--layers=gui") +} + func configToDB(d *MigrationData) { d.say(section, "Migrating lotus-miner config.toml to Curio in-database configuration.") { var closer jsonrpc.ClientCloser var err error - d.full, closer, err = cliutil.GetFullNodeAPI(d.cctx) + d.full, closer, err = cliutil.GetFullNodeAPIV1(d.cctx) d.closers = append(d.closers, closer) if err != nil { d.say(notice, "Error getting API: %s", err.Error()) @@ -329,7 +388,7 @@ func doc(d *MigrationData) { d.say(plain, "Filecoin %s channels: %s and %s", "Slack", "#fil-curio-help", "#fil-curio-dev") - d.say(plain, "Start multiple Curio instances with the '%s' layer to redundancy.", "post") + d.say(plain, "Increase reliability using redundancy: start multiple machines with at-least the post layer: 'curio run --layers=post'") //d.say(plain, "Point your browser to your web GUI to complete setup with %s and advanced featues.", "Boost") d.say(plain, "One database can serve multiple miner IDs: Run a migration for each lotus-miner.") } @@ -381,69 +440,11 @@ func yugabyteConnect(d *MigrationData) { } var err error d.DB, err = harmonydb.NewFromConfig(harmonyCfg) - if err == nil { - goto yugabyteConnected - } - for { - i, _, err := (&promptui.Select{ - Label: d.T("Enter the info to connect to your Yugabyte database installation (https://download.yugabyte.com/)"), - Items: []string{ - d.T("Host: %s", strings.Join(harmonyCfg.Hosts, ",")), - d.T("Port: %s", harmonyCfg.Port), - d.T("Username: %s", harmonyCfg.Username), - d.T("Password: %s", harmonyCfg.Password), - d.T("Database: %s", harmonyCfg.Database), - d.T("Continue to connect and update schema.")}, - Size: 6, - Templates: d.selectTemplates, - }).Run() - if err != nil { - d.say(notice, "Database config error occurred, abandoning migration: %s ", err.Error()) - os.Exit(1) - } - switch i { - case 0: - host, err := (&promptui.Prompt{ - Label: d.T("Enter the Yugabyte database host(s)"), - }).Run() - if err != nil { - d.say(notice, "No host provided") - continue - } - harmonyCfg.Hosts = strings.Split(host, ",") - case 1, 2, 3, 4: - val, err := (&promptui.Prompt{ - Label: d.T("Enter the Yugabyte database %s", []string{"port", "username", "password", "database"}[i-1]), - }).Run() - if err != nil { - d.say(notice, "No value provided") - continue - } - switch i { - case 1: - harmonyCfg.Port = val - case 2: - harmonyCfg.Username = val - case 3: - harmonyCfg.Password = val - case 4: - harmonyCfg.Database = val - } - continue - case 5: - d.DB, err = harmonydb.NewFromConfig(harmonyCfg) - if err != nil { - if err.Error() == "^C" { - os.Exit(1) - } - d.say(notice, "Error connecting to Yugabyte database: %s", err.Error()) - continue - } - goto yugabyteConnected - } + if err != nil { + hcfg := getDBDetails(d) + harmonyCfg = *hcfg } -yugabyteConnected: d.say(plain, "Connected to Yugabyte. Schema is current.") if !reflect.DeepEqual(harmonyCfg, d.MinerConfig.HarmonyDB) || !d.MinerConfig.Subsystems.EnableSectorIndexDB { d.MinerConfig.HarmonyDB = harmonyCfg @@ -588,3 +589,293 @@ func stepCompleted(d *MigrationData, step string) { fmt.Print(green.Render("✔ ")) d.say(plain, "Step Complete: %s\n", step) } + +func stepCreateActor(d *MigrationData) { + d.say(plain, "Initializing a new miner actor.") + + for { + i, _, err := (&promptui.Select{ + Label: d.T("Enter the info to create a new miner"), + Items: []string{ + d.T("Owner Address: %s", d.owner.String()), + d.T("Worker Address: %s", d.worker.String()), + d.T("Sender Address: %s", d.sender.String()), + d.T("Sector Size: %d", d.ssize), + d.T("Confidence epochs: %d", d.confidence), + d.T("Continue to verify the addresses and create a new miner actor.")}, + Size: 6, + Templates: d.selectTemplates, + }).Run() + if err != nil { + d.say(notice, "Miner creation error occurred: %s ", err.Error()) + os.Exit(1) + } + switch i { + case 0: + owner, err := (&promptui.Prompt{ + Label: d.T("Enter the owner address"), + }).Run() + if err != nil { + d.say(notice, "No address provided") + continue + } + ownerAddr, err := address.NewFromString(owner) + if err != nil { + d.say(notice, "Failed to parse the address: %s", err.Error()) + } + d.owner = ownerAddr + case 1, 2: + val, err := (&promptui.Prompt{ + Label: d.T("Enter %s address", []string{"worker", "sender"}[i-1]), + Default: d.owner.String(), + }).Run() + if err != nil { + d.say(notice, err.Error()) + continue + } + addr, err := address.NewFromString(val) + if err != nil { + d.say(notice, "Failed to parse the address: %s", err.Error()) + } + switch i { + case 1: + d.worker = addr + case 2: + d.sender = addr + } + continue + case 3: + val, err := (&promptui.Prompt{ + Label: d.T("Enter the sector size"), + }).Run() + if err != nil { + d.say(notice, "No value provided") + continue + } + sectorSize, err := units.RAMInBytes(val) + if err != nil { + d.say(notice, "Failed to parse sector size: %s", err.Error()) + continue + } + d.ssize = abi.SectorSize(sectorSize) + continue + case 4: + confidenceStr, err := (&promptui.Prompt{ + Label: d.T("Confidence epochs"), + Default: strconv.Itoa(5), + }).Run() + if err != nil { + d.say(notice, err.Error()) + continue + } + confidence, err := strconv.ParseUint(confidenceStr, 10, 64) + if err != nil { + d.say(notice, "Failed to parse confidence: %s", err.Error()) + continue + } + d.confidence = confidence + goto minerInit // break out of the for loop once we have all the values + } + } + +minerInit: + miner, err := spcli.CreateStorageMiner(d.ctx, d.full, d.owner, d.worker, d.sender, d.ssize, d.confidence) + if err != nil { + d.say(notice, "Failed to create the miner actor: %s", err.Error()) + os.Exit(1) + } + + d.MinerID = miner + stepCompleted(d, d.T("Miner %s created successfully", miner.String())) +} + +func stepPresteps(d *MigrationData) { + + // Setup and connect to YugabyteDB + _ = getDBDetails(d) + + // Verify HarmonyDB connection + var titles []string + err := d.DB.Select(d.ctx, &titles, `SELECT title FROM harmony_config WHERE LENGTH(config) > 0`) + if err != nil { + d.say(notice, "Cannot reach the DB: %s", err.Error()) + os.Exit(1) + } + + // Get full node API + full, closer, err := cliutil.GetFullNodeAPIV1(d.cctx) + if err != nil { + d.say(notice, "Error connecting to full node API: %s", err.Error()) + os.Exit(1) + } + d.full = full + d.closers = append(d.closers, closer) + stepCompleted(d, d.T("Pre-initialization steps complete")) +} + +func stepNewMinerConfig(d *MigrationData) { + curioCfg := config.DefaultCurioConfig() + curioCfg.Addresses = append(curioCfg.Addresses, config.CurioAddresses{ + PreCommitControl: []string{}, + CommitControl: []string{}, + TerminateControl: []string{}, + DisableOwnerFallback: false, + DisableWorkerFallback: false, + MinerAddresses: []string{d.MinerID.String()}, + }) + + sk, err := io.ReadAll(io.LimitReader(rand.Reader, 32)) + if err != nil { + d.say(notice, "Failed to generate random bytes for secret: %s", err.Error()) + d.say(notice, "Please do not run guided-setup again as miner creation is not idempotent. You need to run 'curio config new-cluster %s' to finish the configuration", d.MinerID.String()) + os.Exit(1) + } + + curioCfg.Apis.StorageRPCSecret = base64.StdEncoding.EncodeToString(sk) + + ainfo, err := cliutil.GetAPIInfo(d.cctx, repo.FullNode) + if err != nil { + d.say(notice, "Failed to get API info for FullNode: %w", err) + d.say(notice, "Please do not run guided-setup again as miner creation is not idempotent. You need to run 'curio config new-cluster %s' to finish the configuration", d.MinerID.String()) + os.Exit(1) + } + + token, err := d.full.AuthNew(d.ctx, api.AllPermissions) + if err != nil { + d.say(notice, "Failed to verify the auth token from daemon node: %s", err.Error()) + d.say(notice, "Please do not run guided-setup again as miner creation is not idempotent. You need to run 'curio config new-cluster %s' to finish the configuration", d.MinerID.String()) + os.Exit(1) + } + + curioCfg.Apis.ChainApiInfo = append(curioCfg.Apis.ChainApiInfo, fmt.Sprintf("%s:%s", string(token), ainfo.Addr)) + + // write config + var titles []string + err = d.DB.Select(d.ctx, &titles, `SELECT title FROM harmony_config WHERE LENGTH(config) > 0`) + if err != nil { + d.say(notice, "Cannot reach the DB: %s", err.Error()) + d.say(notice, "Please do not run guided-setup again as miner creation is not idempotent. You need to run 'curio config new-cluster %s' to finish the configuration", d.MinerID.String()) + os.Exit(1) + } + + // If 'base' layer is not present + if !lo.Contains(titles, "base") { + curioCfg.Addresses = lo.Filter(curioCfg.Addresses, func(a config.CurioAddresses, _ int) bool { + return len(a.MinerAddresses) > 0 + }) + cb, err := config.ConfigUpdate(curioCfg, config.DefaultCurioConfig(), config.Commented(true), config.DefaultKeepUncommented(), config.NoEnv()) + if err != nil { + d.say(notice, "Failed to generate default config: %s", err.Error()) + d.say(notice, "Please do not run guided-setup again as miner creation is not idempotent. You need to run 'curio config new-cluster %s' to finish the configuration", d.MinerID.String()) + os.Exit(1) + } + _, err = d.DB.Exec(d.ctx, "INSERT INTO harmony_config (title, config) VALUES ('base', $1)", string(cb)) + if err != nil { + d.say(notice, "Failed to insert 'base' config layer in database: %s", err.Error()) + d.say(notice, "Please do not run guided-setup again as miner creation is not idempotent. You need to run 'curio config new-cluster %s' to finish the configuration", d.MinerID.String()) + os.Exit(1) + } + stepCompleted(d, d.T("Configuration 'base' was updated to include this miner's address")) + return + } + + // If base layer is already present + baseCfg := config.DefaultCurioConfig() + var baseText string + + err = d.DB.QueryRow(d.ctx, "SELECT config FROM harmony_config WHERE title='base'").Scan(&baseText) + if err != nil { + d.say(notice, "Failed to load base config from database: %s", err.Error()) + d.say(notice, "Please do not run guided-setup again as miner creation is not idempotent. You need to run 'curio config new-cluster %s' to finish the configuration", d.MinerID.String()) + os.Exit(1) + } + _, err = deps.LoadConfigWithUpgrades(baseText, baseCfg) + if err != nil { + d.say(notice, "Failed to parse base config: %s", err.Error()) + d.say(notice, "Please do not run guided-setup again as miner creation is not idempotent. You need to run 'curio config new-cluster %s' to finish the configuration", d.MinerID.String()) + os.Exit(1) + } + + baseCfg.Addresses = append(baseCfg.Addresses, curioCfg.Addresses...) + baseCfg.Addresses = lo.Filter(baseCfg.Addresses, func(a config.CurioAddresses, _ int) bool { + return len(a.MinerAddresses) > 0 + }) + + cb, err := config.ConfigUpdate(baseCfg, config.DefaultCurioConfig(), config.Commented(true), config.DefaultKeepUncommented(), config.NoEnv()) + if err != nil { + d.say(notice, "Failed to regenerate base config: %s", err.Error()) + d.say(notice, "Please do not run guided-setup again as miner creation is not idempotent. You need to run 'curio config new-cluster %s' to finish the configuration", d.MinerID.String()) + os.Exit(1) + } + _, err = d.DB.Exec(d.ctx, "UPDATE harmony_config SET config=$1 WHERE title='base'", string(cb)) + if err != nil { + d.say(notice, "Failed to insert 'base' config layer in database: %s", err.Error()) + d.say(notice, "Please do not run guided-setup again as miner creation is not idempotent. You need to run 'curio config new-cluster %s' to finish the configuration", d.MinerID.String()) + os.Exit(1) + } + + stepCompleted(d, d.T("Configuration 'base' was updated to include this miner's address")) +} + +func getDBDetails(d *MigrationData) *config.HarmonyDB { + harmonyCfg := config.DefaultStorageMiner().HarmonyDB + for { + i, _, err := (&promptui.Select{ + Label: d.T("Enter the info to connect to your Yugabyte database installation (https://download.yugabyte.com/)"), + Items: []string{ + d.T("Host: %s", strings.Join(harmonyCfg.Hosts, ",")), + d.T("Port: %s", harmonyCfg.Port), + d.T("Username: %s", harmonyCfg.Username), + d.T("Password: %s", harmonyCfg.Password), + d.T("Database: %s", harmonyCfg.Database), + d.T("Continue to connect and update schema.")}, + Size: 6, + Templates: d.selectTemplates, + }).Run() + if err != nil { + d.say(notice, "Database config error occurred, abandoning migration: %s ", err.Error()) + os.Exit(1) + } + switch i { + case 0: + host, err := (&promptui.Prompt{ + Label: d.T("Enter the Yugabyte database host(s)"), + }).Run() + if err != nil { + d.say(notice, "No host provided") + continue + } + harmonyCfg.Hosts = strings.Split(host, ",") + case 1, 2, 3, 4: + val, err := (&promptui.Prompt{ + Label: d.T("Enter the Yugabyte database %s", []string{"port", "username", "password", "database"}[i-1]), + }).Run() + if err != nil { + d.say(notice, "No value provided") + continue + } + switch i { + case 1: + harmonyCfg.Port = val + case 2: + harmonyCfg.Username = val + case 3: + harmonyCfg.Password = val + case 4: + harmonyCfg.Database = val + } + continue + case 5: + db, err := harmonydb.NewFromConfig(harmonyCfg) + if err != nil { + if err.Error() == "^C" { + os.Exit(1) + } + d.say(notice, "Error connecting to Yugabyte database: %s", err.Error()) + continue + } + d.DB = db + return &harmonyCfg + } + } +} diff --git a/cmd/curio/internal/translations/catalog.go b/cmd/curio/internal/translations/catalog.go index 1aca33033..44c97dfac 100644 --- a/cmd/curio/internal/translations/catalog.go +++ b/cmd/curio/internal/translations/catalog.go @@ -40,292 +40,436 @@ func init() { } var messageKeyToIndex = map[string]int{ - "Aborting migration.": 41, - "Aborting remaining steps.": 19, - "Aggregate-Anonymous: version, net, and Miner power (bucketed).": 16, - "Cannot read the config.toml file in the provided directory, Error: %s": 70, - "Compare the configurations %s to %s. Changes between the miner IDs other than wallet addreses should be a new, minimal layer for runners that need it.": 76, - "Configuration 'base' was created to include this miner's address and its wallet setup.": 77, - "Configuration 'base' was updated to include this miner's address and its wallet setup.": 75, - "Connected to Yugabyte": 64, - "Connected to Yugabyte. Schema is current.": 56, - "Continue to connect and update schema.": 49, - "Could not create repo from directory: %s. Aborting migration": 71, - "Could not lock miner repo. Your miner must be stopped: %s\n Aborting migration": 72, - "Ctrl+C pressed in Terminal": 3, - "Database config error occurred, abandoning migration: %s ": 50, - "Database: %s": 48, - "Documentation: ": 27, - "Each step needs your confirmation and can be reversed. Press Ctrl+C to exit at any time.": 1, - "Enabling Sector Indexing in the database.": 57, - "Enter the Yugabyte database %s": 53, - "Enter the Yugabyte database host(s)": 51, - "Enter the info to connect to your Yugabyte database installation (https://download.yugabyte.com/)": 43, - "Enter the path to the configuration directory used by %s": 68, - "Error connecting to Yugabyte database: %s": 55, - "Error connecting to lotus node: %s %s": 9, - "Error encoding config.toml: %s": 58, - "Error expanding path: %s": 60, - "Error getting miner info: %s": 22, - "Error getting miner power: %s": 20, - "Error getting token: %s": 11, - "Error interpreting miner ID: %s: ID: %s": 36, - "Error marshalling message: %s": 21, - "Error reading filemode of config.toml: %s": 61, - "Error reading from database: %s. Aborting Migration.": 8, - "Error saving config to layer: %s. Aborting Migration": 12, - "Error sending message: %s": 24, - "Error sending message: Status %s, Message: ": 25, - "Error signing message: %s": 23, - "Error verifying sectors: %s": 37, - "Error writing config.toml: %s": 62, - "Filecoin %s channels: %s and %s": 30, - "Hint: I am someone running Curio on net.": 17, - "Host: %s": 44, - "Individual Data: Miner ID, Curio version, net (%s or %s). Signed.": 15, - "Layer %s created. ": 78, - "Lotus-Miner to Curio Migration.": 4, - "Message sent.": 26, - "Migrating config.toml to database.": 7, - "No host provided": 52, - "No path provided, abandoning migration ": 69, - "No value provided": 54, - "Nothing.": 18, - "Now shut down lotus-miner and move the systems to %s.": 39, - "One database can serve multiple miner IDs: Run a migration for each lotus-miner.": 33, - "Other": 67, - "Password: %s": 47, - "Please start (or restart) %s now that database credentials are in %s.": 34, - "Point your browser to your web GUI to complete setup with %s and advanced featues.": 32, - "Port: %s": 45, - "Press return to continue": 40, - "Press return to update %s with Yugabyte info. Backup the file now.": 59, - "Protocol Labs wants to improve the software you use. Tell the team you're using Curio.": 13, - "Read Miner Config": 73, - "Restart Lotus Miner. ": 63, - "Sectors verified. %d sector locations found.": 42, - "Select the location of your lotus-miner config directory?": 66, - "Select what you want to share with the Curio team.": 14, - "Start multiple Curio instances with the '%s' layer to redundancy.": 31, - "Step Complete: %s\n": 74, - "The '%s' layer stores common configuration. All curio instances can include it in their %s argument.": 28, - "The sectors are in the database. The database is ready for %s.": 38, - "This interactive tool migrates lotus-miner to Curio in 5 minutes.": 0, - "To run Curio: With machine or cgroup isolation, use the command (with example layer selection):": 80, - "To start, ensure your sealing pipeline is drained and shut-down lotus-miner.": 65, - "To work with the config: ": 79, - "Try the web interface with %s for further guided improvements.": 5, - "Use the arrow keys to navigate: ↓ ↑ → ← ": 2, - "Username: %s": 46, - "Waiting for %s to write sectors into Yugabyte.": 35, - "You can add other layers for per-machine configuration changes.": 29, - "You can now migrate your market node (%s), if applicable.": 6, - "could not get API info for FullNode: %w": 10, + "Aborting migration.": 45, + "Aborting remaining steps.": 9, + "Aggregate-Anonymous: version, chain, and Miner power (bucketed).": 22, + "Cannot reach the DB: %s": 90, + "Cannot read the config.toml file in the provided directory, Error: %s": 65, + "Compare the configurations %s to %s. Changes between the miner IDs other than wallet addreses should be a new, minimal layer for runners that need it.": 116, + "Confidence epochs": 86, + "Confidence epochs: %d": 76, + "Configuration 'base' was created to include this miner's address and its wallet setup.": 117, + "Configuration 'base' was updated to include this miner's address": 99, + "Configuration 'base' was updated to include this miner's address and its wallet setup.": 115, + "Connected to Yugabyte": 59, + "Connected to Yugabyte. Schema is current.": 47, + "Continue to connect and update schema.": 109, + "Continue to verify the addresses and create a new miner actor.": 77, + "Could not create repo from directory: %s. Aborting migration": 66, + "Could not lock miner repo. Your miner must be stopped: %s\n Aborting migration": 67, + "Create a new miner": 8, + "Ctrl+C pressed in Terminal": 5, + "Database config error occurred, abandoning migration: %s ": 110, + "Database: %s": 108, + "Documentation: ": 32, + "Each step needs your confirmation and can be reversed. Press Ctrl+C to exit at any time.": 4, + "Enabling Sector Indexing in the database.": 48, + "Enter %s address": 82, + "Enter the Yugabyte database %s": 113, + "Enter the Yugabyte database host(s)": 111, + "Enter the info to connect to your Yugabyte database installation (https://download.yugabyte.com/)": 103, + "Enter the info to create a new miner": 71, + "Enter the owner address": 79, + "Enter the path to the configuration directory used by %s": 63, + "Enter the sector size": 83, + "Error closing backup file: %s": 56, + "Error connecting to Yugabyte database: %s": 114, + "Error connecting to full node API: %s": 91, + "Error creating backup file: %s": 53, + "Error encoding config.toml: %s": 49, + "Error expanding path: %s": 51, + "Error getting API: %s": 15, + "Error getting miner info: %s": 27, + "Error getting miner power: %s": 25, + "Error getting token: %s": 17, + "Error interpreting miner ID: %s: ID: %s": 40, + "Error marshalling message: %s": 26, + "Error reading config.toml: %s": 54, + "Error reading filemode of config.toml: %s": 52, + "Error saving config to layer: %s. Aborting Migration": 18, + "Error sending message: %s": 29, + "Error sending message: Status %s, Message: ": 30, + "Error signing message: %s": 28, + "Error verifying sectors: %s": 41, + "Error writing backup file: %s": 55, + "Error writing config.toml: %s": 57, + "Failed to create the miner actor: %s": 88, + "Failed to generate default config: %s": 97, + "Failed to generate random bytes for secret: %s": 93, + "Failed to get API info for FullNode: %w": 95, + "Failed to insert 'base' config layer in database: %s": 98, + "Failed to load base config from database: %s": 100, + "Failed to parse base config: %s": 101, + "Failed to parse confidence: %s": 87, + "Failed to parse sector size: %s": 85, + "Failed to parse the address: %s": 81, + "Failed to regenerate base config: %s": 102, + "Failed to verify the auth token from daemon node: %s": 96, + "Filecoin %s channels: %s and %s": 35, + "Hint: I am someone running Curio on whichever chain.": 23, + "Host: %s": 104, + "I want to:": 6, + "Increase reliability using redundancy: start multiple machines with at-least the post layer: 'curio run --layers=post'": 36, + "Individual Data: Miner ID, Curio version, chain (%s or %s). Signed.": 21, + "Initializing a new miner actor.": 70, + "Layer %s created. ": 118, + "Lotus-Miner to Curio Migration.": 10, + "Message sent.": 31, + "Migrate from existing Lotus-Miner": 7, + "Migrating lotus-miner config.toml to Curio in-database configuration.": 14, + "Miner %s created successfully": 89, + "Miner creation error occurred: %s ": 78, + "New Miner initialization complete.": 13, + "No address provided": 80, + "No host provided": 112, + "No path provided, abandoning migration ": 64, + "No value provided": 84, + "Nothing.": 24, + "Now shut down lotus-miner and move the systems to %s.": 43, + "One database can serve multiple miner IDs: Run a migration for each lotus-miner.": 37, + "Other": 62, + "Owner Address: %s": 72, + "Password: %s": 107, + "Please do not run guided-setup again as miner creation is not idempotent. You need to run 'curio config new-cluster %s' to finish the configuration": 94, + "Please start (or restart) %s now that database credentials are in %s.": 38, + "Port: %s": 105, + "Pre-initialization steps complete": 92, + "Press return to continue": 44, + "Press return to update %s with Yugabyte info. A Backup file will be written to that folder before changes are made.": 50, + "Read Miner Config": 68, + "Restart Lotus Miner. ": 58, + "Sector Size: %d": 75, + "Sectors verified. %d sector locations found.": 46, + "Select the location of your lotus-miner config directory?": 61, + "Select what you want to share with the Curio team.": 20, + "Sender Address: %s": 74, + "Step Complete: %s\n": 69, + "The '%s' layer stores common configuration. All curio instances can include it in their %s argument.": 33, + "The Curio team wants to improve the software you use. Tell the team you're using `%s`.": 19, + "The sectors are in the database. The database is ready for %s.": 42, + "This interactive tool creates a new miner actor and creates the basic configuration layer for it.": 1, + "This interactive tool migrates lotus-miner to Curio in 5 minutes.": 3, + "This process is partially idempotent. Once a new miner actor has been created and subsequent steps fail, the user need to run 'curio config new-cluster < miner ID >' to finish the configuration.": 2, + "To run Curio: With machine or cgroup isolation, use the command (with example layer selection):": 120, + "To start, ensure your sealing pipeline is drained and shut-down lotus-miner.": 60, + "To work with the config: ": 119, + "Try the web interface with %s for further guided improvements.": 11, + "Use the arrow keys to navigate: ↓ ↑ → ← ": 0, + "Username: %s": 106, + "Waiting for %s to write sectors into Yugabyte.": 39, + "Worker Address: %s": 73, + "You can add other layers for per-machine configuration changes.": 34, + "You can now migrate your market node (%s), if applicable.": 12, + "could not get API info for FullNode: %w": 16, } -var enIndex = []uint32{ // 82 elements +var enIndex = []uint32{ // 122 elements // Entry 0 - 1F - 0x00000000, 0x00000042, 0x0000009b, 0x000000d0, - 0x000000eb, 0x0000010b, 0x0000014d, 0x0000018a, - 0x000001ad, 0x000001e5, 0x00000211, 0x0000023c, - 0x00000257, 0x0000028f, 0x000002e6, 0x00000319, - 0x00000361, 0x000003a0, 0x000003c9, 0x000003d2, - 0x000003ec, 0x0000040d, 0x0000042e, 0x0000044e, - 0x0000046b, 0x00000488, 0x000004bb, 0x000004c9, - 0x000004dd, 0x00000548, 0x00000588, 0x000005b1, + 0x00000000, 0x00000035, 0x00000097, 0x0000015a, + 0x0000019c, 0x000001f5, 0x00000210, 0x0000021b, + 0x0000023d, 0x00000250, 0x0000026a, 0x0000028a, + 0x000002cc, 0x00000309, 0x0000032c, 0x00000372, + 0x0000038b, 0x000003b6, 0x000003d1, 0x00000409, + 0x00000463, 0x00000496, 0x000004e0, 0x00000521, + 0x00000556, 0x0000055f, 0x00000580, 0x000005a1, + 0x000005c1, 0x000005de, 0x000005fb, 0x0000062e, // Entry 20 - 3F - 0x000005f6, 0x0000064c, 0x0000069d, 0x000006e9, - 0x0000071b, 0x00000749, 0x00000768, 0x000007aa, - 0x000007e3, 0x000007fc, 0x00000810, 0x00000840, - 0x000008a2, 0x000008ae, 0x000008ba, 0x000008ca, - 0x000008da, 0x000008ea, 0x00000911, 0x00000952, - 0x00000976, 0x00000987, 0x000009a9, 0x000009bb, - 0x000009e8, 0x00000a12, 0x00000a3c, 0x00000a5e, - 0x00000aa4, 0x00000ac0, 0x00000aed, 0x00000b0e, + 0x0000063c, 0x00000650, 0x000006bb, 0x000006fb, + 0x00000724, 0x0000079b, 0x000007ec, 0x00000838, + 0x0000086a, 0x00000898, 0x000008b7, 0x000008f9, + 0x00000932, 0x0000094b, 0x0000095f, 0x0000098f, + 0x000009b9, 0x000009e3, 0x00000a05, 0x00000a7c, + 0x00000a98, 0x00000ac5, 0x00000ae7, 0x00000b08, + 0x00000b29, 0x00000b4a, 0x00000b6b, 0x00000b85, + 0x00000b9b, 0x00000be8, 0x00000c22, 0x00000c28, // Entry 40 - 5F - 0x00000b28, 0x00000b3e, 0x00000b8b, 0x00000bc5, - 0x00000bcb, 0x00000c07, 0x00000c33, 0x00000c7c, - 0x00000cbc, 0x00000d0d, 0x00000d1f, 0x00000d39, - 0x00000d90, 0x00000e2d, 0x00000e84, 0x00000e9e, - 0x00000ebc, 0x00000f1c, -} // Size: 352 bytes + 0x00000c64, 0x00000c90, 0x00000cd9, 0x00000d19, + 0x00000d6a, 0x00000d7c, 0x00000d96, 0x00000db6, + 0x00000ddb, 0x00000df0, 0x00000e06, 0x00000e1c, + 0x00000e2f, 0x00000e48, 0x00000e87, 0x00000eb1, + 0x00000ec9, 0x00000edd, 0x00000f00, 0x00000f14, + 0x00000f2a, 0x00000f3c, 0x00000f5f, 0x00000f71, + 0x00000f93, 0x00000fbb, 0x00000fdc, 0x00000ff7, + 0x00001020, 0x00001042, 0x00001074, 0x0000110b, + // Entry 60 - 7F + 0x00001136, 0x0000116e, 0x00001197, 0x000011cf, + 0x00001210, 0x00001240, 0x00001263, 0x0000128b, + 0x000012ed, 0x000012f9, 0x00001305, 0x00001315, + 0x00001325, 0x00001335, 0x0000135c, 0x0000139d, + 0x000013c1, 0x000013d2, 0x000013f4, 0x00001421, + 0x00001478, 0x00001515, 0x0000156c, 0x00001586, + 0x000015a4, 0x00001604, +} // Size: 512 bytes -const enData string = "" + // Size: 3868 bytes - "\x02This interactive tool migrates lotus-miner to Curio in 5 minutes." + - "\x02Each step needs your confirmation and can be reversed. Press Ctrl+C " + - "to exit at any time.\x04\x00\x01 0\x02Use the arrow keys to navigate: ↓ " + - "↑ → ←\x02Ctrl+C pressed in Terminal\x02Lotus-Miner to Curio Migration." + - "\x02Try the web interface with %[1]s for further guided improvements." + - "\x02You can now migrate your market node (%[1]s), if applicable.\x02Migr" + - "ating config.toml to database.\x02Error reading from database: %[1]s. Ab" + - "orting Migration.\x02Error connecting to lotus node: %[1]s %[2]s\x02coul" + - "d not get API info for FullNode: %[1]w\x02Error getting token: %[1]s\x02" + - "Error saving config to layer: %[1]s. Aborting Migration\x02Protocol Labs" + - " wants to improve the software you use. Tell the team you're using Curio" + - ".\x02Select what you want to share with the Curio team.\x02Individual Da" + - "ta: Miner ID, Curio version, net (%[1]s or %[2]s). Signed.\x02Aggregate-" + - "Anonymous: version, net, and Miner power (bucketed).\x02Hint: I am someo" + - "ne running Curio on net.\x02Nothing.\x02Aborting remaining steps.\x02Err" + - "or getting miner power: %[1]s\x02Error marshalling message: %[1]s\x02Err" + - "or getting miner info: %[1]s\x02Error signing message: %[1]s\x02Error se" + - "nding message: %[1]s\x04\x00\x01 .\x02Error sending message: Status %[1]" + - "s, Message:\x02Message sent.\x04\x00\x01 \x0f\x02Documentation:\x02The '" + - "%[1]s' layer stores common configuration. All curio instances can includ" + - "e it in their %[2]s argument.\x02You can add other layers for per-machin" + - "e configuration changes.\x02Filecoin %[1]s channels: %[2]s and %[3]s\x02" + - "Start multiple Curio instances with the '%[1]s' layer to redundancy.\x02" + - "Point your browser to your web GUI to complete setup with %[1]s and adva" + - "nced featues.\x02One database can serve multiple miner IDs: Run a migrat" + - "ion for each lotus-miner.\x02Please start (or restart) %[1]s now that da" + - "tabase credentials are in %[2]s.\x02Waiting for %[1]s to write sectors i" + - "nto Yugabyte.\x02Error interpreting miner ID: %[1]s: ID: %[2]s\x02Error " + - "verifying sectors: %[1]s\x02The sectors are in the database. The databas" + - "e is ready for %[1]s.\x02Now shut down lotus-miner and move the systems " + - "to %[1]s.\x02Press return to continue\x02Aborting migration.\x02Sectors " + - "verified. %[1]d sector locations found.\x02Enter the info to connect to " + - "your Yugabyte database installation (https://download.yugabyte.com/)\x02" + - "Host: %[1]s\x02Port: %[1]s\x02Username: %[1]s\x02Password: %[1]s\x02Data" + - "base: %[1]s\x02Continue to connect and update schema.\x04\x00\x01 <\x02D" + - "atabase config error occurred, abandoning migration: %[1]s\x02Enter the " + - "Yugabyte database host(s)\x02No host provided\x02Enter the Yugabyte data" + - "base %[1]s\x02No value provided\x02Error connecting to Yugabyte database" + - ": %[1]s\x02Connected to Yugabyte. Schema is current.\x02Enabling Sector " + - "Indexing in the database.\x02Error encoding config.toml: %[1]s\x02Press " + - "return to update %[1]s with Yugabyte info. Backup the file now.\x02Error" + - " expanding path: %[1]s\x02Error reading filemode of config.toml: %[1]s" + - "\x02Error writing config.toml: %[1]s\x04\x00\x01 \x15\x02Restart Lotus M" + - "iner.\x02Connected to Yugabyte\x02To start, ensure your sealing pipeline" + - " is drained and shut-down lotus-miner.\x02Select the location of your lo" + - "tus-miner config directory?\x02Other\x02Enter the path to the configurat" + - "ion directory used by %[1]s\x04\x00\x01 '\x02No path provided, abandonin" + - "g migration\x02Cannot read the config.toml file in the provided director" + - "y, Error: %[1]s\x02Could not create repo from directory: %[1]s. Aborting" + - " migration\x02Could not lock miner repo. Your miner must be stopped: %[1" + - "]s\x0a Aborting migration\x02Read Miner Config\x04\x00\x01\x0a\x15\x02St" + - "ep Complete: %[1]s\x02Configuration 'base' was updated to include this m" + - "iner's address and its wallet setup.\x02Compare the configurations %[1]s" + - " to %[2]s. Changes between the miner IDs other than wallet addreses shou" + - "ld be a new, minimal layer for runners that need it.\x02Configuration 'b" + - "ase' was created to include this miner's address and its wallet setup." + - "\x04\x00\x01 \x15\x02Layer %[1]s created.\x04\x00\x01 \x19\x02To work wi" + - "th the config:\x02To run Curio: With machine or cgroup isolation, use th" + - "e command (with example layer selection):" +const enData string = "" + // Size: 5636 bytes + "\x04\x00\x01 0\x02Use the arrow keys to navigate: ↓ ↑ → ←\x02This intera" + + "ctive tool creates a new miner actor and creates the basic configuration" + + " layer for it.\x02This process is partially idempotent. Once a new miner" + + " actor has been created and subsequent steps fail, the user need to run " + + "'curio config new-cluster < miner ID >' to finish the configuration.\x02" + + "This interactive tool migrates lotus-miner to Curio in 5 minutes.\x02Eac" + + "h step needs your confirmation and can be reversed. Press Ctrl+C to exit" + + " at any time.\x02Ctrl+C pressed in Terminal\x02I want to:\x02Migrate fro" + + "m existing Lotus-Miner\x02Create a new miner\x02Aborting remaining steps" + + ".\x02Lotus-Miner to Curio Migration.\x02Try the web interface with %[1]s" + + " for further guided improvements.\x02You can now migrate your market nod" + + "e (%[1]s), if applicable.\x02New Miner initialization complete.\x02Migra" + + "ting lotus-miner config.toml to Curio in-database configuration.\x02Erro" + + "r getting API: %[1]s\x02could not get API info for FullNode: %[1]w\x02Er" + + "ror getting token: %[1]s\x02Error saving config to layer: %[1]s. Abortin" + + "g Migration\x02The Curio team wants to improve the software you use. Tel" + + "l the team you're using `%[1]s`.\x02Select what you want to share with t" + + "he Curio team.\x02Individual Data: Miner ID, Curio version, chain (%[1]s" + + " or %[2]s). Signed.\x02Aggregate-Anonymous: version, chain, and Miner po" + + "wer (bucketed).\x02Hint: I am someone running Curio on whichever chain." + + "\x02Nothing.\x02Error getting miner power: %[1]s\x02Error marshalling me" + + "ssage: %[1]s\x02Error getting miner info: %[1]s\x02Error signing message" + + ": %[1]s\x02Error sending message: %[1]s\x04\x00\x01 .\x02Error sending m" + + "essage: Status %[1]s, Message:\x02Message sent.\x04\x00\x01 \x0f\x02Docu" + + "mentation:\x02The '%[1]s' layer stores common configuration. All curio i" + + "nstances can include it in their %[2]s argument.\x02You can add other la" + + "yers for per-machine configuration changes.\x02Filecoin %[1]s channels: " + + "%[2]s and %[3]s\x02Increase reliability using redundancy: start multiple" + + " machines with at-least the post layer: 'curio run --layers=post'\x02One" + + " database can serve multiple miner IDs: Run a migration for each lotus-m" + + "iner.\x02Please start (or restart) %[1]s now that database credentials a" + + "re in %[2]s.\x02Waiting for %[1]s to write sectors into Yugabyte.\x02Err" + + "or interpreting miner ID: %[1]s: ID: %[2]s\x02Error verifying sectors: %" + + "[1]s\x02The sectors are in the database. The database is ready for %[1]s" + + ".\x02Now shut down lotus-miner and move the systems to %[1]s.\x02Press r" + + "eturn to continue\x02Aborting migration.\x02Sectors verified. %[1]d sect" + + "or locations found.\x02Connected to Yugabyte. Schema is current.\x02Enab" + + "ling Sector Indexing in the database.\x02Error encoding config.toml: %[1" + + "]s\x02Press return to update %[1]s with Yugabyte info. A Backup file wil" + + "l be written to that folder before changes are made.\x02Error expanding " + + "path: %[1]s\x02Error reading filemode of config.toml: %[1]s\x02Error cre" + + "ating backup file: %[1]s\x02Error reading config.toml: %[1]s\x02Error wr" + + "iting backup file: %[1]s\x02Error closing backup file: %[1]s\x02Error wr" + + "iting config.toml: %[1]s\x04\x00\x01 \x15\x02Restart Lotus Miner.\x02Con" + + "nected to Yugabyte\x02To start, ensure your sealing pipeline is drained " + + "and shut-down lotus-miner.\x02Select the location of your lotus-miner co" + + "nfig directory?\x02Other\x02Enter the path to the configuration director" + + "y used by %[1]s\x04\x00\x01 '\x02No path provided, abandoning migration" + + "\x02Cannot read the config.toml file in the provided directory, Error: %" + + "[1]s\x02Could not create repo from directory: %[1]s. Aborting migration" + + "\x02Could not lock miner repo. Your miner must be stopped: %[1]s\x0a Abo" + + "rting migration\x02Read Miner Config\x04\x00\x01\x0a\x15\x02Step Complet" + + "e: %[1]s\x02Initializing a new miner actor.\x02Enter the info to create " + + "a new miner\x02Owner Address: %[1]s\x02Worker Address: %[1]s\x02Sender A" + + "ddress: %[1]s\x02Sector Size: %[1]d\x02Confidence epochs: %[1]d\x02Conti" + + "nue to verify the addresses and create a new miner actor.\x04\x00\x01 %" + + "\x02Miner creation error occurred: %[1]s\x02Enter the owner address\x02N" + + "o address provided\x02Failed to parse the address: %[1]s\x02Enter %[1]s " + + "address\x02Enter the sector size\x02No value provided\x02Failed to parse" + + " sector size: %[1]s\x02Confidence epochs\x02Failed to parse confidence: " + + "%[1]s\x02Failed to create the miner actor: %[1]s\x02Miner %[1]s created " + + "successfully\x02Cannot reach the DB: %[1]s\x02Error connecting to full n" + + "ode API: %[1]s\x02Pre-initialization steps complete\x02Failed to generat" + + "e random bytes for secret: %[1]s\x02Please do not run guided-setup again" + + " as miner creation is not idempotent. You need to run 'curio config new-" + + "cluster %[1]s' to finish the configuration\x02Failed to get API info for" + + " FullNode: %[1]w\x02Failed to verify the auth token from daemon node: %[" + + "1]s\x02Failed to generate default config: %[1]s\x02Failed to insert 'bas" + + "e' config layer in database: %[1]s\x02Configuration 'base' was updated t" + + "o include this miner's address\x02Failed to load base config from databa" + + "se: %[1]s\x02Failed to parse base config: %[1]s\x02Failed to regenerate " + + "base config: %[1]s\x02Enter the info to connect to your Yugabyte databas" + + "e installation (https://download.yugabyte.com/)\x02Host: %[1]s\x02Port: " + + "%[1]s\x02Username: %[1]s\x02Password: %[1]s\x02Database: %[1]s\x02Contin" + + "ue to connect and update schema.\x04\x00\x01 <\x02Database config error " + + "occurred, abandoning migration: %[1]s\x02Enter the Yugabyte database hos" + + "t(s)\x02No host provided\x02Enter the Yugabyte database %[1]s\x02Error c" + + "onnecting to Yugabyte database: %[1]s\x02Configuration 'base' was update" + + "d to include this miner's address and its wallet setup.\x02Compare the c" + + "onfigurations %[1]s to %[2]s. Changes between the miner IDs other than w" + + "allet addreses should be a new, minimal layer for runners that need it." + + "\x02Configuration 'base' was created to include this miner's address and" + + " its wallet setup.\x04\x00\x01 \x15\x02Layer %[1]s created.\x04\x00\x01 " + + "\x19\x02To work with the config:\x02To run Curio: With machine or cgroup" + + " isolation, use the command (with example layer selection):" -var koIndex = []uint32{ // 82 elements +var koIndex = []uint32{ // 122 elements // Entry 0 - 1F - 0x00000000, 0x0000004d, 0x000000c8, 0x0000010c, - 0x0000012d, 0x00000150, 0x00000150, 0x000001a0, - 0x000001da, 0x0000022f, 0x0000022f, 0x0000022f, - 0x0000022f, 0x00000287, 0x00000315, 0x0000034e, - 0x000003aa, 0x000003f4, 0x00000437, 0x00000452, - 0x00000477, 0x000004b1, 0x000004e4, 0x0000051e, - 0x00000548, 0x00000572, 0x000005b4, 0x000005d8, - 0x000005e5, 0x0000066b, 0x000006bd, 0x000006bd, + 0x00000000, 0x00000044, 0x000000c1, 0x000001c1, + 0x0000020e, 0x00000289, 0x000002aa, 0x000002bc, + 0x000002e5, 0x00000300, 0x00000325, 0x00000348, + 0x000003a2, 0x000003f2, 0x00000418, 0x00000471, + 0x00000490, 0x000004cc, 0x000004fc, 0x00000554, + 0x000005e0, 0x00000619, 0x0000066f, 0x000006ad, + 0x000006fb, 0x00000716, 0x00000750, 0x00000783, + 0x000007bd, 0x000007e7, 0x00000811, 0x00000853, // Entry 20 - 3F - 0x000006bd, 0x0000071e, 0x0000071e, 0x0000071e, - 0x00000762, 0x00000762, 0x00000789, 0x000007f4, - 0x0000083e, 0x00000865, 0x00000880, 0x000008cf, - 0x0000093d, 0x0000094e, 0x0000095c, 0x00000974, - 0x00000988, 0x000009a2, 0x000009cc, 0x00000a2f, - 0x00000a6b, 0x00000a95, 0x00000acd, 0x00000af1, - 0x00000b45, 0x00000b86, 0x00000b86, 0x00000bcd, - 0x00000c39, 0x00000c39, 0x00000c88, 0x00000cc6, + 0x00000877, 0x00000884, 0x0000090a, 0x0000095c, + 0x00000983, 0x00000a1f, 0x00000ab1, 0x00000b2c, + 0x00000b70, 0x00000bae, 0x00000bd5, 0x00000c40, + 0x00000c8a, 0x00000cb1, 0x00000ccc, 0x00000d1b, + 0x00000d5c, 0x00000d9c, 0x00000de3, 0x00000e89, + 0x00000eb9, 0x00000f08, 0x00000f2b, 0x00000f4c, + 0x00000f6f, 0x00000f92, 0x00000fd0, 0x00000ff4, + 0x0000100a, 0x00001075, 0x000010c4, 0x000010cb, // Entry 40 - 5F - 0x00000cea, 0x00000d00, 0x00000d6b, 0x00000dba, - 0x00000dc1, 0x00000e09, 0x00000e5b, 0x00000eb5, - 0x00000eb5, 0x00000eb5, 0x00000ecd, 0x00000ee7, - 0x00000f51, 0x0000100b, 0x0000106f, 0x0000109e, - 0x0000109e, 0x0000112a, -} // Size: 352 bytes + 0x00001113, 0x00001165, 0x000011bf, 0x00001229, + 0x000012ba, 0x000012d2, 0x000012ec, 0x00001310, + 0x00001343, 0x0000135b, 0x00001373, 0x0000138b, + 0x000013a0, 0x000013b8, 0x0000140f, 0x0000143a, + 0x00001452, 0x00001479, 0x0000149c, 0x000014b0, + 0x000014c5, 0x000014e9, 0x00001513, 0x00001524, + 0x0000154a, 0x00001570, 0x000015a9, 0x000015e1, + 0x00001619, 0x00001638, 0x00001684, 0x00001742, + // Entry 60 - 7F + 0x0000178e, 0x000017dc, 0x000017ff, 0x0000185b, + 0x000018ab, 0x00001900, 0x00001943, 0x00001982, + 0x000019f0, 0x00001a01, 0x00001a0f, 0x00001a27, + 0x00001a3b, 0x00001a55, 0x00001a7f, 0x00001ae2, + 0x00001b1e, 0x00001b48, 0x00001b80, 0x00001bd4, + 0x00001c3e, 0x00001cf8, 0x00001d5c, 0x00001d8b, + 0x00001db2, 0x00001e3e, +} // Size: 512 bytes -const koData string = "" + // Size: 4394 bytes - "\x02이 대화형 도구는 5분 안에 lotus-miner를 Curio로 이주합니다.\x02각 단계는 확인이 필요하며 되돌릴 수 있" + - "습니다. 언제든지 Ctrl+C를 눌러 종료할 수 있습니다.\x04\x00\x01 ?\x02화살표 키를 사용하여 이동하세요: ↓" + - " ↑ → ←\x02터미널에서 Ctrl+C가 눌림\x02Lotus-Miner에서 Curio로 이주.\x02해당하는 경우 이제 시장 " + - "노드를 이주할 수 있습니다 (%[1]s).\x02config.toml을 데이터베이스로 이주 중입니다.\x02데이터베이스에서 읽" + - "는 중 오류 발생: %[1]s. 마이그레이션 중단.\x02레이어에 구성을 저장하는 중 오류 발생: %[1]s. 마이그레이션 중" + - "단\x02Protocol Labs는 당신이 사용하는 소프트웨어를 개선하고 싶어합니다. Curio를 사용 중이라고 팀에 알려주세" + - "요.\x02Curio 팀과 공유하고 싶은 것을 선택하세요.\x02개별 데이터: 마이너 ID, Curio 버전, 네트워크 (%[" + - "1]s 또는 %[2]s). 서명됨.\x02집계-익명: 버전, 네트워크, 그리고 마이너 파워 (버킷).\x02힌트: 네트워크에서 C" + - "urio를 실행 중인 사람입니다.\x02아무것도 없습니다.\x02나머지 단계를 중단합니다.\x02마이너 파워를 가져오는 중 오류 " + - "발생: %[1]s\x02메시지를 마샬하는 중 오류 발생: %[1]s\x02마이너 정보를 가져오는 중 오류 발생: %[1]s" + - "\x02메시지 서명 중 오류 발생: %[1]s\x02메시지 전송 중 오류 발생: %[1]s\x04\x00\x01 =\x02메시지 " + - "전송 중 오류 발생: 상태 %[1]s, 메시지:\x02메시지가 전송되었습니다.\x04\x00\x01 \x08\x02문서:" + - "\x02'%[1]s' 레이어에는 공통 구성이 저장됩니다. 모든 Curio 인스턴스는 %[2]s 인수에 포함시킬 수 있습니다." + - "\x02기계별 구성 변경을 위해 다른 레이어를 추가할 수 있습니다.\x02브라우저를 웹 GUI로 이동하여 %[1]s 및 고급 기능" + - "으로 설정을 완료하세요.\x02%[1]s가 Yugabyte에 섹터를 기록하도록 대기 중입니다.\x02섹터 확인 중 오류 발생:" + - " %[1]s\x02섹터가 데이터베이스에 있습니다. 데이터베이스가 %[1]s를 위해 준비되었습니다.\x02이제 lotus-miner" + - "를 종료하고 시스템을 %[1]s로 이동하세요.\x02계속하려면 리턴을 누르세요\x02마이그레이션 중단.\x02섹터가 확인되었습" + - "니다. %[1]d개의 섹터 위치를 찾았습니다.\x02Yugabyte 데이터베이스 설치에 연결할 정보를 입력하십시오 (https" + - "://download.yugabyte.com/)\x02호스트: %[1]s\x02포트: %[1]s\x02사용자 이름: %[1]s" + - "\x02비밀번호: %[1]s\x02데이터베이스: %[1]s\x02계속 연결 및 스키마 업데이트.\x04\x00\x01 ^\x02데" + - "이터베이스 구성 오류가 발생하여 마이그레이션을 포기합니다: %[1]s\x02Yugabyte 데이터베이스 호스트를 입력하십시오" + - "\x02호스트가 제공되지 않았습니다\x02Yugabyte 데이터베이스 %[1]s을 입력하십시오\x02값이 제공되지 않았습니다" + - "\x02Yugabyte 데이터베이스에 연결하는 중 오류가 발생했습니다: %[1]s\x02Yugabyte에 연결되었습니다. 스키마가" + - " 현재입니다.\x02config.toml을 인코딩하는 중 오류가 발생했습니다: %[1]s\x02%[1]s을 Yugabyte 정보로" + - " 업데이트하려면 리턴을 누르세요. 지금 파일을 백업하세요.\x02config.toml의 파일 모드를 읽는 중 오류가 발생했습니다:" + - " %[1]s\x02config.toml을 쓰는 중 오류가 발생했습니다: %[1]s\x04\x00\x01 \x1f\x02로터스 마이" + - "너 재시작.\x02Yugabyte에 연결됨\x02시작하려면 밀봉 파이프라인이 비어 있고 lotus-miner가 종료되었는지 확" + - "인하세요.\x02로터스 마이너 구성 디렉토리의 위치를 선택하시겠습니까?\x02기타\x02%[1]s에서 사용하는 구성 디렉터리 " + - "경로를 입력하세요.\x04\x00\x01 M\x02경로가 제공되지 않았으므로 마이그레이션을 포기합니다\x02제공된 디렉토리에서" + - " config.toml 파일을 읽을 수 없습니다. 오류: %[1]s\x02마이너 구성 읽기\x04\x00\x01\x0a\x15" + - "\x02단계 완료: %[1]s\x02이 마이너의 주소와 지갑 설정을 포함하도록 구성 'base'가 업데이트되었습니다.\x02구성 " + - "%[1]s를 %[2]s과 비교하세요. 지갑 주소 이외의 마이너 ID 사이의 변경 사항은 필요한 실행자를 위한 새로운 최소한의 레이" + - "어여야 합니다.\x02이 마이너의 주소와 지갑 설정을 포함하도록 구성 'base'가 생성되었습니다.\x04\x00\x01 *" + - "\x02레이어 %[1]s가 생성되었습니다.\x02Curio를 실행하려면: 기계 또는 cgroup 격리를 사용하여 다음 명령을 사용" + - "하세요 (예제 레이어 선택과 함께):" +const koData string = "" + // Size: 7742 bytes + "\x04\x00\x01 ?\x02화살표 키를 사용하여 이동하세요: ↓ ↑ → ←\x02이 대화형 도구는 새로운 채굴자 액터를 생성" + + "하고 그에 대한 기본 구성 레이어를 생성합니다.\x02이 프로세스는 부분적으로 항등원적입니다. 새로운 채굴자 액터가 생성되었고" + + " 후속 단계가 실패하는 경우 사용자는 구성을 완료하기 위해 'curio config new-cluster < 채굴자 ID >'를 " + + "실행해야 합니다.\x02이 대화형 도구는 5분 안에 lotus-miner를 Curio로 이주합니다.\x02각 단계는 확인이 필" + + "요하며 되돌릴 수 있습니다. 언제든지 Ctrl+C를 눌러 종료할 수 있습니다.\x02터미널에서 Ctrl+C가 눌림\x02나는 " + + "원한다:\x02기존의 Lotus-Miner에서 이전하기\x02새로운 채굴자 생성\x02나머지 단계를 중단합니다.\x02Lotu" + + "s-Miner에서 Curio로 이주.\x02더 많은 안내를 위해 %[1]s를 사용하여 웹 인터페이스를 시도하세요.\x02해당하는 " + + "경우 이제 시장 노드를 이주할 수 있습니다 (%[1]s).\x02새로운 채굴자 초기화 완료.\x02lotus-miner con" + + "fig.toml을 Curio의 데이터베이스 구성으로 이전 중입니다.\x02API 가져오기 오류: %[1]s\x02FullNode의" + + " API 정보를 가져올 수 없습니다: %[1]w\x02토큰을 가져오는 중 오류 발생: %[1]s\x02레이어에 구성을 저장하는 중" + + " 오류 발생: %[1]s. 마이그레이션 중단\x02Curio 팀은 당신이 사용하는 소프트웨어를 개선하고자 합니다. 팀에게 `%[1" + + "]s`를 사용 중이라고 알려주세요.\x02Curio 팀과 공유하고 싶은 것을 선택하세요.\x02개별 데이터: 채굴자 ID, Cur" + + "io 버전, 체인 (%[1]s 또는 %[2]s). 서명됨.\x02집계-익명: 버전, 체인, 및 채굴자 파워 (버킷).\x02힌트:" + + " 나는 어떤 체인에서든 Curio를 실행 중인 사람입니다.\x02아무것도 없습니다.\x02마이너 파워를 가져오는 중 오류 발생: " + + "%[1]s\x02메시지를 마샬하는 중 오류 발생: %[1]s\x02마이너 정보를 가져오는 중 오류 발생: %[1]s\x02메시지 " + + "서명 중 오류 발생: %[1]s\x02메시지 전송 중 오류 발생: %[1]s\x04\x00\x01 =\x02메시지 전송 중 오" + + "류 발생: 상태 %[1]s, 메시지:\x02메시지가 전송되었습니다.\x04\x00\x01 \x08\x02문서:\x02'%[1]" + + "s' 레이어에는 공통 구성이 저장됩니다. 모든 Curio 인스턴스는 %[2]s 인수에 포함시킬 수 있습니다.\x02기계별 구성 변" + + "경을 위해 다른 레이어를 추가할 수 있습니다.\x02Filecoin %[1]s 채널: %[2]s 및 %[3]s\x02신뢰성 향" + + "상을 위한 중복성 사용: 적어도 post 레이어를 사용하여 여러 대의 기계를 시작하십시오: 'curio run --layers" + + "=post'\x02한 개의 데이터베이스는 여러 광부 ID를 제공할 수 있습니다: 각 lotus-miner에 대해 마이그레이션을 실" + + "행하세요.\x02데이터베이스 자격 증명이 %[2]s에 입력되었으므로 지금 %[1]s을 시작하거나 다시 시작하세요.\x02%[1" + + "]s가 Yugabyte에 섹터를 기록하도록 대기 중입니다.\x02광부 ID를 해석하는 중 오류 발생: %[1]s: ID: %[2]" + + "s\x02섹터 확인 중 오류 발생: %[1]s\x02섹터가 데이터베이스에 있습니다. 데이터베이스가 %[1]s를 위해 준비되었습니다" + + ".\x02이제 lotus-miner를 종료하고 시스템을 %[1]s로 이동하세요.\x02계속하려면 리턴을 누르세요\x02마이그레이션" + + " 중단.\x02섹터가 확인되었습니다. %[1]d개의 섹터 위치를 찾았습니다.\x02Yugabyte에 연결되었습니다. 스키마가 현재" + + "입니다.\x02데이터베이스에서 Sector Indexing을 활성화합니다.\x02config.toml을 인코딩하는 중 오류가 " + + "발생했습니다: %[1]s\x02%[1]s을 Yugabyte 정보로 업데이트하려면 리턴 키를 누르세요. 변경 사항을 적용하기 전" + + "에 해당 폴더에 백업 파일이 작성됩니다.\x02경로를 확장하는 중 오류 발생: %[1]s\x02config.toml의 파일 모" + + "드를 읽는 중 오류가 발생했습니다: %[1]s\x02백업 파일 생성 오류: %[1]s\x02config.toml 읽기 오류: " + + "%[1]s\x02백업 파일 쓰기 오류: %[1]s\x02백업 파일 닫기 오류: %[1]s\x02config.toml을 쓰는 중 오" + + "류가 발생했습니다: %[1]s\x04\x00\x01 \x1f\x02로터스 마이너 재시작.\x02Yugabyte에 연결됨\x02" + + "시작하려면 밀봉 파이프라인이 비어 있고 lotus-miner가 종료되었는지 확인하세요.\x02로터스 마이너 구성 디렉토리의 위" + + "치를 선택하시겠습니까?\x02기타\x02%[1]s에서 사용하는 구성 디렉터리 경로를 입력하세요.\x04\x00\x01 M" + + "\x02경로가 제공되지 않았으므로 마이그레이션을 포기합니다\x02제공된 디렉토리에서 config.toml 파일을 읽을 수 없습니다" + + ". 오류: %[1]s\x02디렉토리에서 저장소를 생성할 수 없습니다: %[1]s. 마이그레이션을 중단합니다.\x02광부 저장소를 " + + "잠금 해제할 수 없습니다. 귀하의 광부를 중지해야 합니다: %[1]s\x0a 마이그레이션을 중단합니다.\x02마이너 구성 읽기" + + "\x04\x00\x01\x0a\x15\x02단계 완료: %[1]s\x02새 채굴자 액터 초기화 중.\x02새 채굴자를 생성하기 위" + + "한 정보 입력\x02소유자 주소: %[1]s\x02작업자 주소: %[1]s\x02송신자 주소: %[1]s\x02섹터 크기: %" + + "[1]d\x02신뢰 에포크: %[1]d\x02주소를 확인하고 새 채굴자 액터를 생성하려면 계속 진행하세요.\x04\x00\x01 " + + "&\x02채굴자 생성 오류 발생: %[1]s\x02소유자 주소 입력\x02주소가 제공되지 않았습니다\x02주소 구문 분석 실패: " + + "%[1]s\x02%[1]s 주소 입력\x02섹터 크기 입력\x02값이 제공되지 않았습니다\x02섹터 크기 구문 분석 실패: %[1" + + "]s\x02신뢰 에포크\x02신뢰도 구문 분석 실패: %[1]s\x02채굴자 액터 생성 실패: %[1]s\x02%[1]s 채굴자가" + + " 성공적으로 생성되었습니다\x02데이터베이스에 연결할 수 없습니다: %[1]s\x02풀 노드 API에 연결하는 중 오류 발생: %" + + "[1]s\x02사전 초기화 단계 완료\x02비밀번호를 위한 랜덤 바이트 생성에 실패했습니다: %[1]s\x02마이너 생성은 ide" + + "mpotent하지 않으므로 가이드 설정을 다시 실행하지 마십시오. 구성을 완료하려면 'curio config new-cluster" + + " %[1]s'를 실행해야 합니다.\x02FullNode에 대한 API 정보를 가져오는 데 실패했습니다: %[1]w\x02데몬 노드" + + "로부터 인증 토큰을 확인하는 중 오류 발생: %[1]s\x02기본 구성 생성 실패: %[1]s\x02데이터베이스에 'base'" + + " 구성 레이어를 삽입하는 데 실패했습니다: %[1]s\x02이 마이너 주소를 포함한 구성 'base'가 업데이트되었습니다.\x02" + + "데이터베이스에서 기본 구성을 로드하는 데 실패했습니다: %[1]s\x02기본 구성을 구문 분석하는 데 실패했습니다: %[1]s" + + "\x02기본 구성을 재생성하는 데 실패했습니다: %[1]s\x02Yugabyte 데이터베이스 설치에 연결할 정보를 입력하십시오 (" + + "https://download.yugabyte.com/)\x02호스트: %[1]s\x02포트: %[1]s\x02사용자 이름: %[" + + "1]s\x02비밀번호: %[1]s\x02데이터베이스: %[1]s\x02계속 연결 및 스키마 업데이트.\x04\x00\x01 ^" + + "\x02데이터베이스 구성 오류가 발생하여 마이그레이션을 포기합니다: %[1]s\x02Yugabyte 데이터베이스 호스트를 입력하십" + + "시오\x02호스트가 제공되지 않았습니다\x02Yugabyte 데이터베이스 %[1]s을 입력하십시오\x02Yugabyte 데이터" + + "베이스에 연결하는 중 오류가 발생했습니다: %[1]s\x02이 마이너의 주소와 지갑 설정을 포함하도록 구성 'base'가 업데" + + "이트되었습니다.\x02구성 %[1]s를 %[2]s과 비교하세요. 지갑 주소 이외의 마이너 ID 사이의 변경 사항은 필요한 실행" + + "자를 위한 새로운 최소한의 레이어여야 합니다.\x02이 마이너의 주소와 지갑 설정을 포함하도록 구성 'base'가 생성되었습니" + + "다.\x04\x00\x01 *\x02레이어 %[1]s가 생성되었습니다.\x04\x00\x01 \x22\x02구성 파일을 사용하" + + "려면:\x02Curio를 실행하려면: 기계 또는 cgroup 격리를 사용하여 다음 명령을 사용하세요 (예제 레이어 선택과 함께" + + "):" -var zhIndex = []uint32{ // 82 elements +var zhIndex = []uint32{ // 122 elements // Entry 0 - 1F - 0x00000000, 0x00000048, 0x00000097, 0x000000ca, - 0x000000e3, 0x00000100, 0x00000100, 0x00000141, - 0x0000016b, 0x000001a4, 0x000001a4, 0x000001a4, - 0x000001a4, 0x000001dd, 0x0000022f, 0x0000025c, - 0x000002a9, 0x000002e7, 0x00000317, 0x00000321, - 0x00000337, 0x0000035b, 0x00000379, 0x0000039d, - 0x000003bb, 0x000003d9, 0x0000040e, 0x00000421, - 0x00000430, 0x0000048a, 0x000004c7, 0x000004c7, + 0x00000000, 0x00000033, 0x0000008b, 0x00000134, + 0x0000017c, 0x000001cb, 0x000001e4, 0x000001f1, + 0x00000211, 0x0000022a, 0x00000240, 0x0000025d, + 0x0000029c, 0x000002dd, 0x000002f9, 0x0000033e, + 0x0000035b, 0x00000384, 0x000003a2, 0x000003db, + 0x0000042f, 0x0000045c, 0x000004ab, 0x000004e6, + 0x0000051b, 0x00000525, 0x00000549, 0x00000567, + 0x0000058b, 0x000005a9, 0x000005c7, 0x000005fc, // Entry 20 - 3F - 0x000004c7, 0x0000051e, 0x0000051e, 0x0000051e, - 0x00000544, 0x00000544, 0x00000562, 0x0000059e, - 0x000005d0, 0x000005e0, 0x000005f0, 0x00000623, - 0x0000067d, 0x0000068c, 0x0000069b, 0x000006ad, - 0x000006bc, 0x000006ce, 0x000006ed, 0x00000725, - 0x0000074a, 0x0000075a, 0x00000778, 0x00000785, - 0x000007b1, 0x000007de, 0x000007de, 0x00000801, - 0x00000845, 0x00000845, 0x00000874, 0x00000897, + 0x0000060f, 0x0000061e, 0x00000678, 0x000006b5, + 0x000006dd, 0x0000073c, 0x0000078c, 0x000007df, + 0x00000805, 0x00000832, 0x00000850, 0x0000088c, + 0x000008be, 0x000008ce, 0x000008de, 0x00000911, + 0x0000093e, 0x00000963, 0x00000986, 0x000009fe, + 0x00000a1c, 0x00000a4b, 0x00000a6f, 0x00000a94, + 0x00000ab8, 0x00000adc, 0x00000aff, 0x00000b1f, + 0x00000b34, 0x00000b7f, 0x00000baf, 0x00000bb6, // Entry 40 - 5F - 0x000008b7, 0x000008cc, 0x00000917, 0x00000947, - 0x0000094e, 0x00000978, 0x0000099c, 0x000009e0, - 0x000009e0, 0x000009e0, 0x000009f3, 0x00000a0d, - 0x00000a59, 0x00000adb, 0x00000b27, 0x00000b41, - 0x00000b41, 0x00000b98, -} // Size: 352 bytes + 0x00000be0, 0x00000c04, 0x00000c48, 0x00000c7a, + 0x00000cc3, 0x00000cd6, 0x00000cf0, 0x00000d0f, + 0x00000d34, 0x00000d4c, 0x00000d61, 0x00000d79, + 0x00000d8d, 0x00000da4, 0x00000dd5, 0x00000dfa, + 0x00000e10, 0x00000e20, 0x00000e3a, 0x00000e4e, + 0x00000e61, 0x00000e6e, 0x00000e8e, 0x00000e9e, + 0x00000ebb, 0x00000edb, 0x00000ef5, 0x00000f12, + 0x00000f43, 0x00000f5c, 0x00000f85, 0x00001012, + // Entry 60 - 7F + 0x0000103e, 0x00001079, 0x00001099, 0x000010ca, + 0x000010fd, 0x0000112a, 0x0000114b, 0x00001171, + 0x000011cb, 0x000011da, 0x000011e9, 0x000011fb, + 0x0000120a, 0x0000121c, 0x0000123b, 0x00001273, + 0x00001298, 0x000012a8, 0x000012c6, 0x000012f2, + 0x0000133e, 0x000013c0, 0x0000140c, 0x00001426, + 0x0000143e, 0x00001495, +} // Size: 512 bytes -const zhData string = "" + // Size: 2968 bytes - "\x02这个交互式工具可以在5分钟内将lotus-miner迁移到Curio。\x02每一步都需要您的确认,并且可以撤销。随时按Ctrl+C退出" + - "。\x04\x00\x01 .\x02使用箭头键进行导航:↓ ↑ → ←\x02在终端中按下Ctrl+C\x02Lotus-Miner到Cu" + - "rio迁移。\x02如果适用,您现在可以迁移您的市场节点(%[1]s)。\x02正在将config.toml迁移到数据库。\x02读取数据库时出" + - "错:%[1]s。正在中止迁移。\x02保存配置到层时出错:%[1]s。正在中止迁移\x02Protocol Labs希望改进您使用的软件。告" + - "诉团队您正在使用Curio。\x02选择您想与Curio团队分享的内容。\x02个人数据:矿工ID、Curio版本、网络(%[1]s或%[2" + - "]s)。已签名。\x02聚合-匿名:版本、网络和矿工功率(分桶)。\x02提示:我是在网络上运行Curio的人。\x02没有。\x02中止剩余步" + - "骤。\x02获取矿工功率时出错:%[1]s\x02整理消息时出错:%[1]s\x02获取矿工信息时出错:%[1]s\x02签署消息时出错:%" + - "[1]s\x02发送消息时出错:%[1]s\x04\x00\x01 0\x02发送消息时出错:状态%[1]s,消息:\x02消息已发送。\x04" + - "\x00\x01 \x0a\x02文档:\x02'%[1]s'层存储通用配置。所有Curio实例都可以在其%[2]s参数中包含它。\x02您可以" + - "添加其他层进行每台机器的配置更改。\x02将您的浏览器指向您的网络GUI,以使用%[1]s和高级功能完成设置。\x02等待%[1]s将扇区写" + - "入Yugabyte。\x02验证扇区时出错:%[1]s\x02扇区在数据库中。数据库已准备好用于%[1]s。\x02现在关闭lotus-mi" + - "ner并将系统移至%[1]s。\x02按回车继续\x02中止迁移。\x02扇区已验证。发现了%[1]d个扇区位置。\x02输入连接到您的Yuga" + - "byte数据库安装的信息(https://download.yugabyte.com/)\x02主机:%[1]s\x02端口:%[1]s\x02" + - "用户名:%[1]s\x02密码:%[1]s\x02数据库:%[1]s\x02继续连接和更新架构。\x04\x00\x01 3\x02发生数据" + - "库配置错误,放弃迁移:%[1]s\x02输入Yugabyte数据库主机(S)\x02未提供主机\x02输入Yugabyte数据库 %[1]s" + - "\x02未提供值\x02连接到Yugabyte数据库时出错:%[1]s\x02已连接到Yugabyte。模式是当前的。\x02编码config." + - "toml时出错:%[1]s\x02按回车更新%[1]s以获取Yugabyte信息。现在备份文件。\x02读取config.toml文件模式时出错" + - ":%[1]s\x02写入config.toml时出错:%[1]s\x04\x00\x01 \x1b\x02重新启动Lotus Miner。" + - "\x02已连接到Yugabyte\x02开始之前,请确保您的密封管道已排空并关闭lotus-miner。\x02选择您的lotus-miner配" + - "置目录的位置?\x02其他\x02输入%[1]s使用的配置目录的路径\x04\x00\x01 \x1f\x02未提供路径,放弃迁移\x02无" + - "法读取提供的目录中的config.toml文件,错误:%[1]s\x02读取矿工配置\x04\x00\x01\x0a\x15\x02步骤完成" + - ":%[1]s\x02配置'base'已更新,包含了这个矿工的地址和其钱包设置。\x02比较配置%[1]s和%[2]s。矿工ID之间除了钱包地" + - "址的变化应该是需要的运行者的一个新的、最小的层。\x02配置'base'已创建,包括了这个矿工的地址和其钱包设置。\x04\x00\x01 " + - "\x15\x02层%[1]s已创建。\x02运行Curio:使用机器或cgroup隔离,使用命令(附带示例层选择):" +const zhData string = "" + // Size: 5269 bytes + "\x04\x00\x01 .\x02使用箭头键进行导航:↓ ↑ → ←\x02此交互式工具将创建一个新的矿工角色,并为其创建基本配置层。\x02" + + "该过程部分幂等。一旦创建了新的矿工角色,并且随后的步骤失败,用户需要运行 'curio config new-cluster < 矿工 ID" + + " >' 来完成配置。\x02这个交互式工具可以在5分钟内将lotus-miner迁移到Curio。\x02每一步都需要您的确认,并且可以撤销。随" + + "时按Ctrl+C退出。\x02在终端中按下Ctrl+C\x02我想要:\x02从现有的 Lotus-Miner 迁移\x02创建一个新的矿工" + + "\x02中止剩余步骤。\x02Lotus-Miner到Curio迁移。\x02尝试使用%[1]s的Web界面进行进一步引导式改进。\x02如果适" + + "用,您现在可以迁移您的市场节点(%[1]s)。\x02新矿工初始化完成。\x02将 lotus-miner config.toml 迁移到 " + + "Curio 的数据库配置中。\x02获取 API 时出错:%[1]s\x02无法获取FullNode的API信息:%[1]w\x02获取令牌时出" + + "错:%[1]s\x02保存配置到层时出错:%[1]s。正在中止迁移\x02Curio 团队希望改进您使用的软件。告诉团队您正在使用 `%[1" + + "]s`。\x02选择您想与Curio团队分享的内容。\x02个人数据:矿工 ID,Curio 版本,链(%[1]s 或 %[2]s)。签名。" + + "\x02聚合-匿名:版本,链和矿工算力(分桶)。\x02提示:我是在任何链上运行 Curio 的人。\x02没有。\x02获取矿工功率时出错:%" + + "[1]s\x02整理消息时出错:%[1]s\x02获取矿工信息时出错:%[1]s\x02签署消息时出错:%[1]s\x02发送消息时出错:%[1" + + "]s\x04\x00\x01 0\x02发送消息时出错:状态%[1]s,消息:\x02消息已发送。\x04\x00\x01 \x0a\x02文档" + + ":\x02'%[1]s'层存储通用配置。所有Curio实例都可以在其%[2]s参数中包含它。\x02您可以添加其他层进行每台机器的配置更改。" + + "\x02Filecoin %[1]s 频道:%[2]s 和 %[3]s\x02通过冗余增加可靠性:使用至少后层启动多台机器:'curio run" + + " --layers=post'\x02一个数据库可以服务多个矿工ID:为每个lotus-miner运行迁移。\x02请立即启动(或重新启动)%[" + + "1]s,因为数据库凭据已在%[2]s中。\x02等待%[1]s将扇区写入Yugabyte。\x02解释矿工ID时出错:%[1]s:ID:%[2]" + + "s\x02验证扇区时出错:%[1]s\x02扇区在数据库中。数据库已准备好用于%[1]s。\x02现在关闭lotus-miner并将系统移至%[" + + "1]s。\x02按回车继续\x02中止迁移。\x02扇区已验证。发现了%[1]d个扇区位置。\x02已连接到Yugabyte。模式是当前的。" + + "\x02在数据库中启用扇区索引。\x02编码config.toml时出错:%[1]s\x02按回车键更新 %[1]s 以包含 Yugabyte " + + "信息。在进行更改之前,将在该文件夹中写入备份文件。\x02扩展路径时出错:%[1]s\x02读取config.toml文件模式时出错:%[1" + + "]s\x02创建备份文件时出错:%[1]s\x02读取 config.toml 时出错:%[1]s\x02写入备份文件时出错:%[1]s\x02" + + "关闭备份文件时出错:%[1]s\x02写入config.toml时出错:%[1]s\x04\x00\x01 \x1b\x02重新启动Lotu" + + "s Miner。\x02已连接到Yugabyte\x02开始之前,请确保您的密封管道已排空并关闭lotus-miner。\x02选择您的lotu" + + "s-miner配置目录的位置?\x02其他\x02输入%[1]s使用的配置目录的路径\x04\x00\x01 \x1f\x02未提供路径,放弃迁" + + "移\x02无法读取提供的目录中的config.toml文件,错误:%[1]s\x02无法从目录创建repo:%[1]s。 中止迁移\x02无" + + "法锁定矿工repo。 您的矿工必须停止:%[1]s\x0a 中止迁移\x02读取矿工配置\x04\x00\x01\x0a\x15\x02步骤" + + "完成:%[1]s\x02初始化新的矿工角色。\x02输入创建新矿工所需的信息\x02所有者地址:%[1]s\x02工作地址:%[1]s" + + "\x02发送者地址:%[1]s\x02扇区大小: %[1]d\x02置信度时期: %[1]d\x02继续验证地址并创建新的矿工角色。\x04" + + "\x00\x01 \x02矿工创建错误发生: %[1]s\x02输入所有者地址\x02未提供地址\x02解析地址失败: %[1]s\x02输入" + + " %[1]s 地址\x02输入扇区大小\x02未提供值\x02解析扇区大小失败: %[1]s\x02置信度时期\x02解析置信度失败: %[1]" + + "s\x02创建矿工角色失败: %[1]s\x02矿工 %[1]s 创建成功\x02无法访问数据库: %[1]s\x02连接到完整节点 API 时" + + "发生错误: %[1]s\x02预初始化步骤完成\x02生成密码的随机字节失败: %[1]s\x02请不要再次运行引导设置,因为矿工创建不是幂" + + "等的。 您需要运行 'curio config new-cluster %[1]s' 来完成配置。\x02无法获取 FullNode 的 A" + + "PI 信息: %[1]w\x02无法验证来自守护进程节点的授权令牌: %[1]s\x02无法生成默认配置: %[1]s\x02无法将 'base" + + "' 配置层插入数据库: %[1]s\x02配置 'base' 已更新以包含此矿工的地址\x02从数据库加载基本配置失败:%[1]s\x02解析基" + + "本配置失败:%[1]s\x02重新生成基本配置失败: %[1]s\x02输入连接到您的Yugabyte数据库安装的信息(https://do" + + "wnload.yugabyte.com/)\x02主机:%[1]s\x02端口:%[1]s\x02用户名:%[1]s\x02密码:%[1]s" + + "\x02数据库:%[1]s\x02继续连接和更新架构。\x04\x00\x01 3\x02发生数据库配置错误,放弃迁移:%[1]s\x02输入Y" + + "ugabyte数据库主机(S)\x02未提供主机\x02输入Yugabyte数据库 %[1]s\x02连接到Yugabyte数据库时出错:%[1" + + "]s\x02配置'base'已更新,包含了这个矿工的地址和其钱包设置。\x02比较配置%[1]s和%[2]s。矿工ID之间除了钱包地址的变化应该" + + "是需要的运行者的一个新的、最小的层。\x02配置'base'已创建,包括了这个矿工的地址和其钱包设置。\x04\x00\x01 \x15" + + "\x02层%[1]s已创建。\x04\x00\x01 \x13\x02要使用配置:\x02运行Curio:使用机器或cgroup隔离,使用命令(" + + "附带示例层选择):" - // Total table size 12286 bytes (11KiB); checksum: 15B16994 + // Total table size 20183 bytes (19KiB); checksum: 4FD3E25A diff --git a/cmd/curio/internal/translations/locales/en/out.gotext.json b/cmd/curio/internal/translations/locales/en/out.gotext.json index 1cd3957d4..db8a264ee 100644 --- a/cmd/curio/internal/translations/locales/en/out.gotext.json +++ b/cmd/curio/internal/translations/locales/en/out.gotext.json @@ -1,6 +1,27 @@ { "language": "en", "messages": [ + { + "id": "Use the arrow keys to navigate: ↓ ↑ → ←", + "message": "Use the arrow keys to navigate: ↓ ↑ → ←", + "translation": "Use the arrow keys to navigate: ↓ ↑ → ←", + "translatorComment": "Copied from source.", + "fuzzy": true + }, + { + "id": "This interactive tool creates a new miner actor and creates the basic configuration layer for it.", + "message": "This interactive tool creates a new miner actor and creates the basic configuration layer for it.", + "translation": "This interactive tool creates a new miner actor and creates the basic configuration layer for it.", + "translatorComment": "Copied from source.", + "fuzzy": true + }, + { + "id": "This process is partially idempotent. Once a new miner actor has been created and subsequent steps fail, the user need to run 'curio config new-cluster \u003c miner ID \u003e' to finish the configuration.", + "message": "This process is partially idempotent. Once a new miner actor has been created and subsequent steps fail, the user need to run 'curio config new-cluster \u003c miner ID \u003e' to finish the configuration.", + "translation": "This process is partially idempotent. Once a new miner actor has been created and subsequent steps fail, the user need to run 'curio config new-cluster \u003c miner ID \u003e' to finish the configuration.", + "translatorComment": "Copied from source.", + "fuzzy": true + }, { "id": "This interactive tool migrates lotus-miner to Curio in 5 minutes.", "message": "This interactive tool migrates lotus-miner to Curio in 5 minutes.", @@ -15,13 +36,6 @@ "translatorComment": "Copied from source.", "fuzzy": true }, - { - "id": "Use the arrow keys to navigate: ↓ ↑ → ←", - "message": "Use the arrow keys to navigate: ↓ ↑ → ←", - "translation": "Use the arrow keys to navigate: ↓ ↑ → ←", - "translatorComment": "Copied from source.", - "fuzzy": true - }, { "id": "Ctrl+C pressed in Terminal", "message": "Ctrl+C pressed in Terminal", @@ -29,6 +43,44 @@ "translatorComment": "Copied from source.", "fuzzy": true }, + { + "id": "I want to:", + "message": "I want to:", + "translation": "I want to:", + "translatorComment": "Copied from source.", + "fuzzy": true + }, + { + "id": "Migrate from existing Lotus-Miner", + "message": "Migrate from existing Lotus-Miner", + "translation": "Migrate from existing Lotus-Miner", + "translatorComment": "Copied from source.", + "fuzzy": true + }, + { + "id": "Create a new miner", + "message": "Create a new miner", + "translation": "Create a new miner", + "translatorComment": "Copied from source.", + "fuzzy": true + }, + { + "id": "Aborting remaining steps.", + "message": "Aborting remaining steps.", + "translation": "Aborting remaining steps.", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Error", + "string": "%[1]v", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "err.Error()" + } + ], + "fuzzy": true + }, { "id": "Lotus-Miner to Curio Migration.", "message": "Lotus-Miner to Curio Migration.", @@ -71,16 +123,23 @@ "fuzzy": true }, { - "id": "Migrating config.toml to database.", - "message": "Migrating config.toml to database.", - "translation": "Migrating config.toml to database.", + "id": "New Miner initialization complete.", + "message": "New Miner initialization complete.", + "translation": "New Miner initialization complete.", "translatorComment": "Copied from source.", "fuzzy": true }, { - "id": "Error reading from database: {Error}. Aborting Migration.", - "message": "Error reading from database: {Error}. Aborting Migration.", - "translation": "Error reading from database: {Error}. Aborting Migration.", + "id": "Migrating lotus-miner config.toml to Curio in-database configuration.", + "message": "Migrating lotus-miner config.toml to Curio in-database configuration.", + "translation": "Migrating lotus-miner config.toml to Curio in-database configuration.", + "translatorComment": "Copied from source.", + "fuzzy": true + }, + { + "id": "Error getting API: {Error}", + "message": "Error getting API: {Error}", + "translation": "Error getting API: {Error}", "translatorComment": "Copied from source.", "placeholders": [ { @@ -94,31 +153,6 @@ ], "fuzzy": true }, - { - "id": "Error connecting to lotus node: {Error} {Error_1}", - "message": "Error connecting to lotus node: {Error} {Error_1}", - "translation": "Error connecting to lotus node: {Error} {Error_1}", - "translatorComment": "Copied from source.", - "placeholders": [ - { - "id": "Error", - "string": "%[1]s", - "type": "string", - "underlyingType": "string", - "argNum": 1, - "expr": "err.Error()" - }, - { - "id": "Error_1", - "string": "%[2]s", - "type": "string", - "underlyingType": "string", - "argNum": 2, - "expr": "err2.Error()" - } - ], - "fuzzy": true - }, { "id": "could not get API info for FullNode: {Err}", "message": "could not get API info for FullNode: {Err}", @@ -171,10 +205,20 @@ "fuzzy": true }, { - "id": "Protocol Labs wants to improve the software you use. Tell the team you're using Curio.", - "message": "Protocol Labs wants to improve the software you use. Tell the team you're using Curio.", - "translation": "Protocol Labs wants to improve the software you use. Tell the team you're using Curio.", + "id": "The Curio team wants to improve the software you use. Tell the team you're using `{Curio}`.", + "message": "The Curio team wants to improve the software you use. Tell the team you're using `{Curio}`.", + "translation": "The Curio team wants to improve the software you use. Tell the team you're using `{Curio}`.", "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Curio", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "\"curio\"" + } + ], "fuzzy": true }, { @@ -185,9 +229,9 @@ "fuzzy": true }, { - "id": "Individual Data: Miner ID, Curio version, net ({Mainnet} or {Testnet}). Signed.", - "message": "Individual Data: Miner ID, Curio version, net ({Mainnet} or {Testnet}). Signed.", - "translation": "Individual Data: Miner ID, Curio version, net ({Mainnet} or {Testnet}). Signed.", + "id": "Individual Data: Miner ID, Curio version, chain ({Mainnet} or {Calibration}). Signed.", + "message": "Individual Data: Miner ID, Curio version, chain ({Mainnet} or {Calibration}). Signed.", + "translation": "Individual Data: Miner ID, Curio version, chain ({Mainnet} or {Calibration}). Signed.", "translatorComment": "Copied from source.", "placeholders": [ { @@ -199,27 +243,27 @@ "expr": "\"mainnet\"" }, { - "id": "Testnet", + "id": "Calibration", "string": "%[2]s", "type": "string", "underlyingType": "string", "argNum": 2, - "expr": "\"testnet\"" + "expr": "\"calibration\"" } ], "fuzzy": true }, { - "id": "Aggregate-Anonymous: version, net, and Miner power (bucketed).", - "message": "Aggregate-Anonymous: version, net, and Miner power (bucketed).", - "translation": "Aggregate-Anonymous: version, net, and Miner power (bucketed).", + "id": "Aggregate-Anonymous: version, chain, and Miner power (bucketed).", + "message": "Aggregate-Anonymous: version, chain, and Miner power (bucketed).", + "translation": "Aggregate-Anonymous: version, chain, and Miner power (bucketed).", "translatorComment": "Copied from source.", "fuzzy": true }, { - "id": "Hint: I am someone running Curio on net.", - "message": "Hint: I am someone running Curio on net.", - "translation": "Hint: I am someone running Curio on net.", + "id": "Hint: I am someone running Curio on whichever chain.", + "message": "Hint: I am someone running Curio on whichever chain.", + "translation": "Hint: I am someone running Curio on whichever chain.", "translatorComment": "Copied from source.", "fuzzy": true }, @@ -230,23 +274,6 @@ "translatorComment": "Copied from source.", "fuzzy": true }, - { - "id": "Aborting remaining steps.", - "message": "Aborting remaining steps.", - "translation": "Aborting remaining steps.", - "translatorComment": "Copied from source.", - "placeholders": [ - { - "id": "Error", - "string": "%[1]v", - "type": "string", - "underlyingType": "string", - "argNum": 1, - "expr": "err.Error()" - } - ], - "fuzzy": true - }, { "id": "Error getting miner power: {Error}", "message": "Error getting miner power: {Error}", @@ -437,37 +464,10 @@ "fuzzy": true }, { - "id": "Start multiple Curio instances with the '{Post}' layer to redundancy.", - "message": "Start multiple Curio instances with the '{Post}' layer to redundancy.", - "translation": "Start multiple Curio instances with the '{Post}' layer to redundancy.", + "id": "Increase reliability using redundancy: start multiple machines with at-least the post layer: 'curio run --layers=post'", + "message": "Increase reliability using redundancy: start multiple machines with at-least the post layer: 'curio run --layers=post'", + "translation": "Increase reliability using redundancy: start multiple machines with at-least the post layer: 'curio run --layers=post'", "translatorComment": "Copied from source.", - "placeholders": [ - { - "id": "Post", - "string": "%[1]s", - "type": "string", - "underlyingType": "string", - "argNum": 1, - "expr": "\"post\"" - } - ], - "fuzzy": true - }, - { - "id": "Point your browser to your web GUI to complete setup with {Boost} and advanced featues.", - "message": "Point your browser to your web GUI to complete setup with {Boost} and advanced featues.", - "translation": "Point your browser to your web GUI to complete setup with {Boost} and advanced featues.", - "translatorComment": "Copied from source.", - "placeholders": [ - { - "id": "Boost", - "string": "%[1]s", - "type": "string", - "underlyingType": "string", - "argNum": 1, - "expr": "\"Boost\"" - } - ], "fuzzy": true }, { @@ -626,177 +626,6 @@ ], "fuzzy": true }, - { - "id": "Enter the info to connect to your Yugabyte database installation (https://download.yugabyte.com/)", - "message": "Enter the info to connect to your Yugabyte database installation (https://download.yugabyte.com/)", - "translation": "Enter the info to connect to your Yugabyte database installation (https://download.yugabyte.com/)", - "translatorComment": "Copied from source.", - "fuzzy": true - }, - { - "id": "Host: {Hosts_}", - "message": "Host: {Hosts_}", - "translation": "Host: {Hosts_}", - "translatorComment": "Copied from source.", - "placeholders": [ - { - "id": "Hosts_", - "string": "%[1]s", - "type": "string", - "underlyingType": "string", - "argNum": 1, - "expr": "strings.Join(harmonyCfg.Hosts, \",\")" - } - ], - "fuzzy": true - }, - { - "id": "Port: {Port}", - "message": "Port: {Port}", - "translation": "Port: {Port}", - "translatorComment": "Copied from source.", - "placeholders": [ - { - "id": "Port", - "string": "%[1]s", - "type": "string", - "underlyingType": "string", - "argNum": 1, - "expr": "harmonyCfg.Port" - } - ], - "fuzzy": true - }, - { - "id": "Username: {Username}", - "message": "Username: {Username}", - "translation": "Username: {Username}", - "translatorComment": "Copied from source.", - "placeholders": [ - { - "id": "Username", - "string": "%[1]s", - "type": "string", - "underlyingType": "string", - "argNum": 1, - "expr": "harmonyCfg.Username" - } - ], - "fuzzy": true - }, - { - "id": "Password: {Password}", - "message": "Password: {Password}", - "translation": "Password: {Password}", - "translatorComment": "Copied from source.", - "placeholders": [ - { - "id": "Password", - "string": "%[1]s", - "type": "string", - "underlyingType": "string", - "argNum": 1, - "expr": "harmonyCfg.Password" - } - ], - "fuzzy": true - }, - { - "id": "Database: {Database}", - "message": "Database: {Database}", - "translation": "Database: {Database}", - "translatorComment": "Copied from source.", - "placeholders": [ - { - "id": "Database", - "string": "%[1]s", - "type": "string", - "underlyingType": "string", - "argNum": 1, - "expr": "harmonyCfg.Database" - } - ], - "fuzzy": true - }, - { - "id": "Continue to connect and update schema.", - "message": "Continue to connect and update schema.", - "translation": "Continue to connect and update schema.", - "translatorComment": "Copied from source.", - "fuzzy": true - }, - { - "id": "Database config error occurred, abandoning migration: {Error}", - "message": "Database config error occurred, abandoning migration: {Error}", - "translation": "Database config error occurred, abandoning migration: {Error}", - "translatorComment": "Copied from source.", - "placeholders": [ - { - "id": "Error", - "string": "%[1]s", - "type": "string", - "underlyingType": "string", - "argNum": 1, - "expr": "err.Error()" - } - ], - "fuzzy": true - }, - { - "id": "Enter the Yugabyte database host(s)", - "message": "Enter the Yugabyte database host(s)", - "translation": "Enter the Yugabyte database host(s)", - "translatorComment": "Copied from source.", - "fuzzy": true - }, - { - "id": "No host provided", - "message": "No host provided", - "translation": "No host provided", - "translatorComment": "Copied from source.", - "fuzzy": true - }, - { - "id": "Enter the Yugabyte database {Stringport_username_password_databasei_1}", - "message": "Enter the Yugabyte database {Stringport_username_password_databasei_1}", - "translation": "Enter the Yugabyte database {Stringport_username_password_databasei_1}", - "translatorComment": "Copied from source.", - "placeholders": [ - { - "id": "Stringport_username_password_databasei_1", - "string": "%[1]s", - "type": "string", - "underlyingType": "string", - "argNum": 1, - "expr": "[]string{\"port\", \"username\", \"password\", \"database\"}[i-1]" - } - ], - "fuzzy": true - }, - { - "id": "No value provided", - "message": "No value provided", - "translation": "No value provided", - "translatorComment": "Copied from source.", - "fuzzy": true - }, - { - "id": "Error connecting to Yugabyte database: {Error}", - "message": "Error connecting to Yugabyte database: {Error}", - "translation": "Error connecting to Yugabyte database: {Error}", - "translatorComment": "Copied from source.", - "placeholders": [ - { - "id": "Error", - "string": "%[1]s", - "type": "string", - "underlyingType": "string", - "argNum": 1, - "expr": "err.Error()" - } - ], - "fuzzy": true - }, { "id": "Connected to Yugabyte. Schema is current.", "message": "Connected to Yugabyte. Schema is current.", @@ -829,9 +658,9 @@ "fuzzy": true }, { - "id": "Press return to update {Toml} with Yugabyte info. Backup the file now.", - "message": "Press return to update {Toml} with Yugabyte info. Backup the file now.", - "translation": "Press return to update {Toml} with Yugabyte info. Backup the file now.", + "id": "Press return to update {Toml} with Yugabyte info. A Backup file will be written to that folder before changes are made.", + "message": "Press return to update {Toml} with Yugabyte info. A Backup file will be written to that folder before changes are made.", + "translation": "Press return to update {Toml} with Yugabyte info. A Backup file will be written to that folder before changes are made.", "translatorComment": "Copied from source.", "placeholders": [ { @@ -879,6 +708,74 @@ ], "fuzzy": true }, + { + "id": "Error creating backup file: {Error}", + "message": "Error creating backup file: {Error}", + "translation": "Error creating backup file: {Error}", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Error", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "err.Error()" + } + ], + "fuzzy": true + }, + { + "id": "Error reading config.toml: {Error}", + "message": "Error reading config.toml: {Error}", + "translation": "Error reading config.toml: {Error}", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Error", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "err.Error()" + } + ], + "fuzzy": true + }, + { + "id": "Error writing backup file: {Error}", + "message": "Error writing backup file: {Error}", + "translation": "Error writing backup file: {Error}", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Error", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "err.Error()" + } + ], + "fuzzy": true + }, + { + "id": "Error closing backup file: {Error}", + "message": "Error closing backup file: {Error}", + "translation": "Error closing backup file: {Error}", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Error", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "err.Error()" + } + ], + "fuzzy": true + }, { "id": "Error writing config.toml: {Error}", "message": "Error writing config.toml: {Error}", @@ -1030,6 +927,631 @@ ], "fuzzy": true }, + { + "id": "Initializing a new miner actor.", + "message": "Initializing a new miner actor.", + "translation": "Initializing a new miner actor.", + "translatorComment": "Copied from source.", + "fuzzy": true + }, + { + "id": "Enter the info to create a new miner", + "message": "Enter the info to create a new miner", + "translation": "Enter the info to create a new miner", + "translatorComment": "Copied from source.", + "fuzzy": true + }, + { + "id": "Owner Address: {String}", + "message": "Owner Address: {String}", + "translation": "Owner Address: {String}", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "String", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "d.owner.String()" + } + ], + "fuzzy": true + }, + { + "id": "Worker Address: {String}", + "message": "Worker Address: {String}", + "translation": "Worker Address: {String}", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "String", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "d.worker.String()" + } + ], + "fuzzy": true + }, + { + "id": "Sender Address: {String}", + "message": "Sender Address: {String}", + "translation": "Sender Address: {String}", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "String", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "d.sender.String()" + } + ], + "fuzzy": true + }, + { + "id": "Sector Size: {Ssize}", + "message": "Sector Size: {Ssize}", + "translation": "Sector Size: {Ssize}", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Ssize", + "string": "%[1]d", + "type": "github.com/filecoin-project/go-state-types/abi.SectorSize", + "underlyingType": "uint64", + "argNum": 1, + "expr": "d.ssize" + } + ], + "fuzzy": true + }, + { + "id": "Confidence epochs: {Confidence}", + "message": "Confidence epochs: {Confidence}", + "translation": "Confidence epochs: {Confidence}", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Confidence", + "string": "%[1]d", + "type": "uint64", + "underlyingType": "uint64", + "argNum": 1, + "expr": "d.confidence" + } + ], + "fuzzy": true + }, + { + "id": "Continue to verify the addresses and create a new miner actor.", + "message": "Continue to verify the addresses and create a new miner actor.", + "translation": "Continue to verify the addresses and create a new miner actor.", + "translatorComment": "Copied from source.", + "fuzzy": true + }, + { + "id": "Miner creation error occurred: {Error}", + "message": "Miner creation error occurred: {Error}", + "translation": "Miner creation error occurred: {Error}", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Error", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "err.Error()" + } + ], + "fuzzy": true + }, + { + "id": "Enter the owner address", + "message": "Enter the owner address", + "translation": "Enter the owner address", + "translatorComment": "Copied from source.", + "fuzzy": true + }, + { + "id": "No address provided", + "message": "No address provided", + "translation": "No address provided", + "translatorComment": "Copied from source.", + "fuzzy": true + }, + { + "id": "Failed to parse the address: {Error}", + "message": "Failed to parse the address: {Error}", + "translation": "Failed to parse the address: {Error}", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Error", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "err.Error()" + } + ], + "fuzzy": true + }, + { + "id": "Enter {Stringworker_senderi_1} address", + "message": "Enter {Stringworker_senderi_1} address", + "translation": "Enter {Stringworker_senderi_1} address", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Stringworker_senderi_1", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "[]string{\"worker\", \"sender\"}[i-1]" + } + ], + "fuzzy": true + }, + { + "id": "Enter the sector size", + "message": "Enter the sector size", + "translation": "Enter the sector size", + "translatorComment": "Copied from source.", + "fuzzy": true + }, + { + "id": "No value provided", + "message": "No value provided", + "translation": "No value provided", + "translatorComment": "Copied from source.", + "fuzzy": true + }, + { + "id": "Failed to parse sector size: {Error}", + "message": "Failed to parse sector size: {Error}", + "translation": "Failed to parse sector size: {Error}", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Error", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "err.Error()" + } + ], + "fuzzy": true + }, + { + "id": "Confidence epochs", + "message": "Confidence epochs", + "translation": "Confidence epochs", + "translatorComment": "Copied from source.", + "fuzzy": true + }, + { + "id": "Failed to parse confidence: {Error}", + "message": "Failed to parse confidence: {Error}", + "translation": "Failed to parse confidence: {Error}", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Error", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "err.Error()" + } + ], + "fuzzy": true + }, + { + "id": "Failed to create the miner actor: {Error}", + "message": "Failed to create the miner actor: {Error}", + "translation": "Failed to create the miner actor: {Error}", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Error", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "err.Error()" + } + ], + "fuzzy": true + }, + { + "id": "Miner {String} created successfully", + "message": "Miner {String} created successfully", + "translation": "Miner {String} created successfully", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "String", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "miner.String()" + } + ], + "fuzzy": true + }, + { + "id": "Cannot reach the DB: {Error}", + "message": "Cannot reach the DB: {Error}", + "translation": "Cannot reach the DB: {Error}", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Error", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "err.Error()" + } + ], + "fuzzy": true + }, + { + "id": "Error connecting to full node API: {Error}", + "message": "Error connecting to full node API: {Error}", + "translation": "Error connecting to full node API: {Error}", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Error", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "err.Error()" + } + ], + "fuzzy": true + }, + { + "id": "Pre-initialization steps complete", + "message": "Pre-initialization steps complete", + "translation": "Pre-initialization steps complete", + "translatorComment": "Copied from source.", + "fuzzy": true + }, + { + "id": "Failed to generate random bytes for secret: {Error}", + "message": "Failed to generate random bytes for secret: {Error}", + "translation": "Failed to generate random bytes for secret: {Error}", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Error", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "err.Error()" + } + ], + "fuzzy": true + }, + { + "id": "Please do not run guided-setup again as miner creation is not idempotent. You need to run 'curio config new-cluster {String}' to finish the configuration", + "message": "Please do not run guided-setup again as miner creation is not idempotent. You need to run 'curio config new-cluster {String}' to finish the configuration", + "translation": "Please do not run guided-setup again as miner creation is not idempotent. You need to run 'curio config new-cluster {String}' to finish the configuration", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "String", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "d.MinerID.String()" + } + ], + "fuzzy": true + }, + { + "id": "Failed to get API info for FullNode: {Err}", + "message": "Failed to get API info for FullNode: {Err}", + "translation": "Failed to get API info for FullNode: {Err}", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Err", + "string": "%[1]w", + "type": "error", + "underlyingType": "interface{Error() string}", + "argNum": 1, + "expr": "err" + } + ], + "fuzzy": true + }, + { + "id": "Failed to verify the auth token from daemon node: {Error}", + "message": "Failed to verify the auth token from daemon node: {Error}", + "translation": "Failed to verify the auth token from daemon node: {Error}", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Error", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "err.Error()" + } + ], + "fuzzy": true + }, + { + "id": "Failed to generate default config: {Error}", + "message": "Failed to generate default config: {Error}", + "translation": "Failed to generate default config: {Error}", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Error", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "err.Error()" + } + ], + "fuzzy": true + }, + { + "id": "Failed to insert 'base' config layer in database: {Error}", + "message": "Failed to insert 'base' config layer in database: {Error}", + "translation": "Failed to insert 'base' config layer in database: {Error}", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Error", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "err.Error()" + } + ], + "fuzzy": true + }, + { + "id": "Configuration 'base' was updated to include this miner's address", + "message": "Configuration 'base' was updated to include this miner's address", + "translation": "Configuration 'base' was updated to include this miner's address", + "translatorComment": "Copied from source.", + "fuzzy": true + }, + { + "id": "Failed to load base config from database: {Error}", + "message": "Failed to load base config from database: {Error}", + "translation": "Failed to load base config from database: {Error}", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Error", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "err.Error()" + } + ], + "fuzzy": true + }, + { + "id": "Failed to parse base config: {Error}", + "message": "Failed to parse base config: {Error}", + "translation": "Failed to parse base config: {Error}", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Error", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "err.Error()" + } + ], + "fuzzy": true + }, + { + "id": "Failed to regenerate base config: {Error}", + "message": "Failed to regenerate base config: {Error}", + "translation": "Failed to regenerate base config: {Error}", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Error", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "err.Error()" + } + ], + "fuzzy": true + }, + { + "id": "Enter the info to connect to your Yugabyte database installation (https://download.yugabyte.com/)", + "message": "Enter the info to connect to your Yugabyte database installation (https://download.yugabyte.com/)", + "translation": "Enter the info to connect to your Yugabyte database installation (https://download.yugabyte.com/)", + "translatorComment": "Copied from source.", + "fuzzy": true + }, + { + "id": "Host: {Hosts_}", + "message": "Host: {Hosts_}", + "translation": "Host: {Hosts_}", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Hosts_", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "strings.Join(harmonyCfg.Hosts, \",\")" + } + ], + "fuzzy": true + }, + { + "id": "Port: {Port}", + "message": "Port: {Port}", + "translation": "Port: {Port}", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Port", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "harmonyCfg.Port" + } + ], + "fuzzy": true + }, + { + "id": "Username: {Username}", + "message": "Username: {Username}", + "translation": "Username: {Username}", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Username", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "harmonyCfg.Username" + } + ], + "fuzzy": true + }, + { + "id": "Password: {Password}", + "message": "Password: {Password}", + "translation": "Password: {Password}", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Password", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "harmonyCfg.Password" + } + ], + "fuzzy": true + }, + { + "id": "Database: {Database}", + "message": "Database: {Database}", + "translation": "Database: {Database}", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Database", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "harmonyCfg.Database" + } + ], + "fuzzy": true + }, + { + "id": "Continue to connect and update schema.", + "message": "Continue to connect and update schema.", + "translation": "Continue to connect and update schema.", + "translatorComment": "Copied from source.", + "fuzzy": true + }, + { + "id": "Database config error occurred, abandoning migration: {Error}", + "message": "Database config error occurred, abandoning migration: {Error}", + "translation": "Database config error occurred, abandoning migration: {Error}", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Error", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "err.Error()" + } + ], + "fuzzy": true + }, + { + "id": "Enter the Yugabyte database host(s)", + "message": "Enter the Yugabyte database host(s)", + "translation": "Enter the Yugabyte database host(s)", + "translatorComment": "Copied from source.", + "fuzzy": true + }, + { + "id": "No host provided", + "message": "No host provided", + "translation": "No host provided", + "translatorComment": "Copied from source.", + "fuzzy": true + }, + { + "id": "Enter the Yugabyte database {Stringport_username_password_databasei_1}", + "message": "Enter the Yugabyte database {Stringport_username_password_databasei_1}", + "translation": "Enter the Yugabyte database {Stringport_username_password_databasei_1}", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Stringport_username_password_databasei_1", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "[]string{\"port\", \"username\", \"password\", \"database\"}[i-1]" + } + ], + "fuzzy": true + }, + { + "id": "Error connecting to Yugabyte database: {Error}", + "message": "Error connecting to Yugabyte database: {Error}", + "translation": "Error connecting to Yugabyte database: {Error}", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Error", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "err.Error()" + } + ], + "fuzzy": true + }, { "id": "Configuration 'base' was updated to include this miner's address and its wallet setup.", "message": "Configuration 'base' was updated to include this miner's address and its wallet setup.", diff --git a/cmd/curio/internal/translations/locales/ko/messages.gotext.json b/cmd/curio/internal/translations/locales/ko/messages.gotext.json index ab0f8c6af..61107880c 100644 --- a/cmd/curio/internal/translations/locales/ko/messages.gotext.json +++ b/cmd/curio/internal/translations/locales/ko/messages.gotext.json @@ -621,84 +621,462 @@ "translation": "더 많은 안내를 위해 {__layersgui}를 사용하여 웹 인터페이스를 시도하세요.", "message": "Try the web interface with {__layersgui} for further guided improvements.", "placeholder": null - }, - { + }, + { "id": "Error connecting to lotus node: {Error} {Error_1}", "translation": "lotus 노드에 연결하는 중 오류 발생: {Error} {Error_1}", "message": "Error connecting to lotus node: {Error} {Error_1}", "placeholder": null - }, - { + }, + { "id": "could not get API info for FullNode: {Err}", "translation": "FullNode의 API 정보를 가져올 수 없습니다: {Err}", "message": "could not get API info for FullNode: {Err}", "placeholder": null - }, - { + }, + { "id": "Error getting token: {Error}", "translation": "토큰을 가져오는 중 오류 발생: {Error}", "message": "Error getting token: {Error}", "placeholder": null - }, - { + }, + { "id": "Filecoin {Slack} channels: {Fil_curio_help} and {Fil_curio_dev}", "translation": "Filecoin {Slack} 채널: {Fil_curio_help} 및 {Fil_curio_dev}", "message": "Filecoin {Slack} channels: {Fil_curio_help} and {Fil_curio_dev}", "placeholder": null - }, - { + }, + { "id": "Start multiple Curio instances with the '{Post}' layer to redundancy.", "translation": "'{Post}' 레이어로 여러 Curio 인스턴스를 시작하여 중복성을 확보하세요.", "message": "Start multiple Curio instances with the '{Post}' layer to redundancy.", "placeholder": null - }, - { + }, + { "id": "One database can serve multiple miner IDs: Run a migration for each lotus-miner.", "translation": "한 개의 데이터베이스는 여러 광부 ID를 제공할 수 있습니다: 각 lotus-miner에 대해 마이그레이션을 실행하세요.", "message": "One database can serve multiple miner IDs: Run a migration for each lotus-miner.", "placeholder": null - }, - { + }, + { "id": "Please start (or restart) {Lotus_miner} now that database credentials are in {Toml}.", "translation": "데이터베이스 자격 증명이 {Toml}에 입력되었으므로 지금 {Lotus_miner}을 시작하거나 다시 시작하세요.", "message": "Please start (or restart) {Lotus_miner} now that database credentials are in {Toml}.", "placeholder": null - }, - { + }, + { "id": "Error interpreting miner ID: {Error}: ID: {String}", "translation": "광부 ID를 해석하는 중 오류 발생: {Error}: ID: {String}", "message": "Error interpreting miner ID: {Error}: ID: {String}", "placeholder": null - }, - { + }, + { "id": "Enabling Sector Indexing in the database.", "translation": "데이터베이스에서 Sector Indexing을 활성화합니다.", "message": "Enabling Sector Indexing in the database.", "placeholder": null - }, - { + }, + { "id": "Error expanding path: {Error}", "translation": "경로를 확장하는 중 오류 발생: {Error}", "message": "Error expanding path: {Error}", "placeholder": null - }, - { + }, + { "id": "Could not create repo from directory: {Error}. Aborting migration", "translation": "디렉토리에서 저장소를 생성할 수 없습니다: {Error}. 마이그레이션을 중단합니다.", "message": "Could not create repo from directory: {Error}. Aborting migration", "placeholder": null - }, - { + }, + { "id": "Could not lock miner repo. Your miner must be stopped: {Error}\n Aborting migration", "translation": "광부 저장소를 잠금 해제할 수 없습니다. 귀하의 광부를 중지해야 합니다: {Error}\n 마이그레이션을 중단합니다.", "message": "Could not lock miner repo. Your miner must be stopped: {Error}\n Aborting migration", "placeholder": null - }, - { + }, + { "id": "To work with the config:", "translation": "구성 파일을 사용하려면:", "message": "To work with the config:", "placeholder": null - } + }, + { + "id": "This interactive tool creates a new miner actor and creates the basic configuration layer for it.", + "translation": "이 대화형 도구는 새로운 채굴자 액터를 생성하고 그에 대한 기본 구성 레이어를 생성합니다.", + "message": "This interactive tool creates a new miner actor and creates the basic configuration layer for it.", + "placeholder": null + }, + { + "id": "This process is partially idempotent. Once a new miner actor has been created and subsequent steps fail, the user need to run 'curio config new-cluster {Arg_1}' to finish the configuration.", + "translation": "이 프로세스는 부분적으로 idempotent합니다. 새로운 채굴자 액터가 생성되었고 후속 단계가 실패하면 사용자는 구성을 완료하기 위해 'curio config new-cluster {Arg_1}'를 실행해야 합니다.", + "message": "This process is partially idempotent. Once a new miner actor has been created and subsequent steps fail, the user need to run 'curio config new-cluster {Arg_1}' to finish the configuration.", + "placeholder": null + }, + { + "id": "Choose if you with to create a new miner or migrate from existing Lotus-Miner", + "translation": "새 채굴자를 생성할지 기존의 Lotus-Miner에서 이전할지 선택하세요.", + "message": "Choose if you with to create a new miner or migrate from existing Lotus-Miner", + "placeholder": null + }, + { + "id": "Migrate from existing Lotus-Miner", + "translation": "기존의 Lotus-Miner에서 이전하기", + "message": "Migrate from existing Lotus-Miner", + "placeholder": null + }, + { + "id": "Create a new miner", + "translation": "새로운 채굴자 생성", + "message": "Create a new miner", + "placeholder": null + }, + { + "id": "New Miner initialization complete.", + "translation": "새로운 채굴자 초기화 완료.", + "message": "New Miner initialization complete.", + "placeholder": null + }, + { + "id": "Migrating lotus-miner config.toml to Curio in-database configuration.", + "translation": "lotus-miner config.toml을 Curio의 데이터베이스 구성으로 이전 중입니다.", + "message": "Migrating lotus-miner config.toml to Curio in-database configuration.", + "placeholder": null + }, + { + "id": "Error getting API: {Error}", + "translation": "API 가져오기 오류: {Error}", + "message": "Error getting API: {Error}", + "placeholder": null + }, + { + "id": "The Curio team wants to improve the software you use. Tell the team you're using {Curio}.", + "translation": "Curio 팀은 당신이 사용하는 소프트웨어를 개선하고자 합니다. 팀에게 {Curio}를 사용 중이라고 알려주세요.", + "message": "The Curio team wants to improve the software you use. Tell the team you're using {Curio}.", + "placeholder": null + }, + { + "id": "Individual Data: Miner ID, Curio version, chain ({Mainnet} or {Calibration}). Signed.", + "translation": "개별 데이터: 채굴자 ID, Curio 버전, 체인 ({Mainnet} 또는 {Calibration}). 서명됨.", + "message": "Individual Data: Miner ID, Curio version, chain ({Mainnet} or {Calibration}). Signed.", + "placeholder": null + }, + { + "id": "Aggregate-Anonymous: version, chain, and Miner power (bucketed).", + "translation": "집계-익명: 버전, 체인, 및 채굴자 파워 (버킷).", + "message": "Aggregate-Anonymous: version, chain, and Miner power (bucketed).", + "placeholder": null + }, + { + "id": "Hint: I am someone running Curio on whichever chain.", + "translation": "힌트: 나는 어떤 체인에서든 Curio를 실행 중인 사람입니다.", + "message": "Hint: I am someone running Curio on whichever chain.", + "placeholder": null + }, + { + "id": "Press return to update {Toml} with Yugabyte info. A Backup file will be written to that folder before changes are made.", + "translation": "{Toml}을 Yugabyte 정보로 업데이트하려면 리턴 키를 누르세요. 변경 사항을 적용하기 전에 해당 폴더에 백업 파일이 작성됩니다.", + "message": "Press return to update {Toml} with Yugabyte info. A Backup file will be written to that folder before changes are made.", + "placeholder": null + }, + { + "id": "Error creating backup file: {Error}", + "translation": "백업 파일 생성 오류: {Error}", + "message": "Error creating backup file: {Error}", + "placeholder": null + }, + { + "id": "Error reading config.toml: {Error}", + "translation": "config.toml 읽기 오류: {Error}", + "message": "Error reading config.toml: {Error}", + "placeholder": null + }, + { + "id": "Error writing backup file: {Error}", + "translation": "백업 파일 쓰기 오류: {Error}", + "message": "Error writing backup file: {Error}", + "placeholder": null + }, + { + "id": "Error closing backup file: {Error}", + "translation": "백업 파일 닫기 오류: {Error}", + "message": "Error closing backup file: {Error}", + "placeholder": null + }, + { + "id": "Initializing a new miner actor.", + "translation": "새 채굴자 액터 초기화 중.", + "message": "Initializing a new miner actor.", + "placeholder": null + }, + { + "id": "Enter the info to create a new miner", + "translation": "새 채굴자를 생성하기 위한 정보 입력", + "message": "Enter the info to create a new miner", + "placeholder": null + }, + { + "id": "Owner Address: {String}", + "translation": "소유자 주소: {String}", + "message": "Owner Address: {String}", + "placeholder": null + }, + { + "id": "Worker Address: {String}", + "translation": "작업자 주소: {String}", + "message": "Worker Address: {String}", + "placeholder": null + }, + { + "id": "Sender Address: {String}", + "translation": "송신자 주소: {String}", + "message": "Sender Address: {String}", + "placeholder": null + }, + { + "id": "Sector Size: {Ssize}", + "translation": "섹터 크기: {Ssize}", + "message": "Sector Size: {Ssize}", + "placeholder": null + }, + { + "id": "Confidence: {Confidence}", + "translation": "신뢰도: {Confidence}", + "message": "Confidence: {Confidence}", + "placeholder": null + }, + { + "id": "Continue to verify the addresses and create a new miner actor.", + "translation": "주소를 확인하고 새 채굴자 액터를 생성하려면 계속 진행하세요.", + "message": "Continue to verify the addresses and create a new miner actor.", + "placeholder": null + }, + { + "id": "Miner creation error occurred: {Error}", + "translation": "채굴자 생성 오류 발생: {Error}", + "message": "Miner creation error occurred: {Error}", + "placeholder": null + }, + { + "id": "Enter the owner address", + "translation": "소유자 주소 입력", + "message": "Enter the owner address", + "placeholder": null + }, + { + "id": "No address provided", + "translation": "주소가 제공되지 않았습니다", + "message": "No address provided", + "placeholder": null + }, + { + "id": "Failed to parse the address: {Error}", + "translation": "주소 구문 분석 실패: {Error}", + "message": "Failed to parse the address: {Error}", + "placeholder": null + }, + { + "id": "Enter {Stringworker_senderi_1} address", + "translation": "{Stringworker_senderi_1} 주소 입력", + "message": "Enter {Stringworker_senderi_1} address", + "placeholder": null + }, + { + "id": "Enter the sector size", + "translation": "섹터 크기 입력", + "message": "Enter the sector size", + "placeholder": null + }, + { + "id": "Failed to parse sector size: {Error}", + "translation": "섹터 크기 구문 분석 실패: {Error}", + "message": "Failed to parse sector size: {Error}", + "placeholder": null + }, + { + "id": "Enter the confidence", + "translation": "신뢰도 입력", + "message": "Enter the confidence", + "placeholder": null + }, + { + "id": "Failed to parse confidence: {Error}", + "translation": "신뢰도 구문 분석 실패: {Error}", + "message": "Failed to parse confidence: {Error}", + "placeholder": null + }, + { + "id": "Failed to create the miner actor: {Error}", + "translation": "채굴자 액터 생성 실패: {Error}", + "message": "Failed to create the miner actor: {Error}", + "placeholder": null + }, + { + "id": "Miner {String} created successfully", + "translation": "{String} 채굴자가 성공적으로 생성되었습니다", + "message": "Miner {String} created successfully", + "placeholder": null + }, + { + "id": "Cannot reach the DB: {Error}", + "translation": "데이터베이스에 연결할 수 없습니다: {Error}", + "message": "Cannot reach the DB: {Error}", + "placeholder": null + }, + { + "id": "Error connecting to full node API: {Error}", + "translation": "풀 노드 API에 연결하는 중 오류 발생: {Error}", + "message": "Error connecting to full node API: {Error}", + "placeholder": null + }, + { + "id": "Pre-initialization steps complete", + "translation": "사전 초기화 단계 완료", + "message": "Pre-initialization steps complete", + "placeholder": null + }, + { + "id": "Failed to random bytes for secret: {Error}", + "translation": "비밀을 위한 랜덤 바이트 생성 실패: {Error}", + "message": "Failed to random bytes for secret: {Error}", + "placeholder": null + }, + { + "id": "Please do not run guided-setup again as miner creation is not idempotent. You need to run 'curio config new-cluster {String}' to finish the configuration", + "translation": "마이너 생성은 idempotent하지 않으므로 가이드 설정을 다시 실행하지 마십시오. 구성을 완료하려면 'curio config new-cluster {String}'를 실행해야 합니다.", + "message": "Please do not run guided-setup again as miner creation is not idempotent. You need to run 'curio config new-cluster {String}' to finish the configuration", + "placeholder": null + }, + { + "id": "Failed to verify the auth token from daemon node: {Error}", + "translation": "데몬 노드로부터 인증 토큰을 확인하는 중 오류 발생: {Error}", + "message": "Failed to verify the auth token from daemon node: {Error}", + "placeholder": null + }, + { + "id": "Failed to encode the config: {Error}", + "translation": "구성을 인코딩하는 중 오류 발생: {Error}", + "message": "Failed to encode the config: {Error}", + "placeholder": null + }, + { + "id": "Failed to generate default config: {Error}", + "translation": "기본 구성 생성 실패: {Error}", + "message": "Failed to generate default config: {Error}", + "placeholder": null + }, + { + "id": "Failed to inset 'base' config layer in database: {Error}", + "translation": "데이터베이스에 'base' 구성 레이어 삽입 실패: {Error}", + "message": "Failed to inset 'base' config layer in database: {Error}", + "placeholder": null + }, + { + "id": "Failed to inset '{String}' config layer in database: {Error}", + "translation": "데이터베이스에 '{String}' 구성 레이어 삽입 실패: {Error}", + "message": "Failed to inset '{String}' config layer in database: {Error}", + "placeholder": null + }, + { + "id": "New Curio configuration layer '{String}' created", + "translation": "새로운 Curio 구성 레이어 '{String}'가 생성되었습니다", + "message": "New Curio configuration layer '{String}' created", + "placeholder": null + }, + { + "id": "The Curio team wants to improve the software you use. Tell the team you're using `{Curio}`.", + "translation": "Curio 팀은 당신이 사용하는 소프트웨어를 개선하고자 합니다. 팀에게 `{Curio}`를 사용 중이라고 알려주세요.", + "message": "The Curio team wants to improve the software you use. Tell the team you're using `{Curio}`.", + "placeholder": null + }, + { + "id": "Confidence epochs: {Confidence}", + "translation": "신뢰 에포크: {Confidence}", + "message": "Confidence epochs: {Confidence}", + "placeholder": null + }, + { + "id": "Failed to generate random bytes for secret: {Error}", + "translation": "비밀번호를 위한 랜덤 바이트 생성에 실패했습니다: {Error}", + "message": "Failed to generate random bytes for secret: {Error}", + "placeholder": null + }, + { + "id": "Failed to get API info for FullNode: {Err}", + "translation": "FullNode에 대한 API 정보를 가져오는 데 실패했습니다: {Err}", + "message": "Failed to get API info for FullNode: {Err}", + "placeholder": null + }, + { + "id": "Failed to insert 'base' config layer in database: {Error}", + "translation": "데이터베이스에 'base' 구성 레이어를 삽입하는 데 실패했습니다: {Error}", + "message": "Failed to insert 'base' config layer in database: {Error}", + "placeholder": null + }, + { + "id": "Failed to insert '{String}' config layer in database: {Error}", + "translation": "데이터베이스에 '{String}' 구성 레이어를 삽입하는 데 실패했습니다: {Error}", + "message": "Failed to insert '{String}' config layer in database: {Error}", + "placeholder": null + }, + { + "id": "This process is partially idempotent. Once a new miner actor has been created and subsequent steps fail, the user need to run 'curio config new-cluster \u003c miner ID \u003e' to finish the configuration.", + "translation": "이 프로세스는 부분적으로 항등원적입니다. 새로운 채굴자 액터가 생성되었고 후속 단계가 실패하는 경우 사용자는 구성을 완료하기 위해 'curio config new-cluster \u003c 채굴자 ID \u003e'를 실행해야 합니다.", + "message": "This process is partially idempotent. Once a new miner actor has been created and subsequent steps fail, the user need to run 'curio config new-cluster \u003c miner ID \u003e' to finish the configuration.", + "placeholder": null + }, + { + "id": "Confidence epochs", + "translation": "신뢰 에포크", + "message": "Confidence epochs", + "placeholder": null + }, + { + "id": "Increase reliability using redundancy: start multiple machines with at-least the post layer: 'curio run --layers=post'", + "translation": "신뢰성 향상을 위한 중복성 사용: 적어도 post 레이어를 사용하여 여러 대의 기계를 시작하십시오: 'curio run --layers=post'", + "message": "Increase reliability using redundancy: start multiple machines with at-least the post layer: 'curio run --layers=post'", + "placeholder": null + }, + { + "id": "I want to:", + "translation": "나는 원한다:", + "message": "I want to:", + "placeholder": null + }, + { + "id": "Configuration 'base' was updated to include this miner's address", + "translation": "이 마이너 주소를 포함한 구성 'base'가 업데이트되었습니다.", + "message": "Configuration 'base' was updated to include this miner's address", + "placeholder": null + }, + { + "id": "Cannot load base config: {Error}", + "translation": "기본 구성을 불러올 수 없습니다: {Error}", + "message": "Cannot load base config: {Error}", + "placeholder": null + }, + { + "id": "Failed to load base config: {Error}", + "translation": "기본 구성을 로드하는 데 실패했습니다: {Error}", + "message": "Failed to load base config: {Error}", + "placeholder": null + }, + { + "id": "Failed to regenerate base config: {Error}", + "translation": "기본 구성을 재생성하는 데 실패했습니다: {Error}", + "message": "Failed to regenerate base config: {Error}", + "placeholder": null + }, + { + "id": "Failed to load base config from database: {Error}", + "translation": "데이터베이스에서 기본 구성을 로드하는 데 실패했습니다: {Error}", + "message": "Failed to load base config from database: {Error}", + "placeholder": null + }, + { + "id": "Failed to parse base config: {Error}", + "translation": "기본 구성을 구문 분석하는 데 실패했습니다: {Error}", + "message": "Failed to parse base config: {Error}", + "placeholder": null + } ] } \ No newline at end of file diff --git a/cmd/curio/internal/translations/locales/ko/out.gotext.json b/cmd/curio/internal/translations/locales/ko/out.gotext.json index 9eb8915c0..8e0014cd4 100644 --- a/cmd/curio/internal/translations/locales/ko/out.gotext.json +++ b/cmd/curio/internal/translations/locales/ko/out.gotext.json @@ -1,89 +1,4 @@ { "language": "ko", - "messages": [ - { - "id": "Try the web interface with {__layersgui} for further guided improvements.", - "translation": "", - "message": "Try the web interface with {__layersgui} for further guided improvements.", - "placeholder": null - }, - { - "id": "Error connecting to lotus node: {Error} {Error_1}", - "translation": "", - "message": "Error connecting to lotus node: {Error} {Error_1}", - "placeholder": null - }, - { - "id": "could not get API info for FullNode: {Err}", - "translation": "", - "message": "could not get API info for FullNode: {Err}", - "placeholder": null - }, - { - "id": "Error getting token: {Error}", - "translation": "", - "message": "Error getting token: {Error}", - "placeholder": null - }, - { - "id": "Filecoin {Slack} channels: {Fil_curio_help} and {Fil_curio_dev}", - "translation": "", - "message": "Filecoin {Slack} channels: {Fil_curio_help} and {Fil_curio_dev}", - "placeholder": null - }, - { - "id": "Start multiple Curio instances with the '{Post}' layer to redundancy.", - "translation": "", - "message": "Start multiple Curio instances with the '{Post}' layer to redundancy.", - "placeholder": null - }, - { - "id": "One database can serve multiple miner IDs: Run a migration for each lotus-miner.", - "translation": "", - "message": "One database can serve multiple miner IDs: Run a migration for each lotus-miner.", - "placeholder": null - }, - { - "id": "Please start (or restart) {Lotus_miner} now that database credentials are in {Toml}.", - "translation": "", - "message": "Please start (or restart) {Lotus_miner} now that database credentials are in {Toml}.", - "placeholder": null - }, - { - "id": "Error interpreting miner ID: {Error}: ID: {String}", - "translation": "", - "message": "Error interpreting miner ID: {Error}: ID: {String}", - "placeholder": null - }, - { - "id": "Enabling Sector Indexing in the database.", - "translation": "", - "message": "Enabling Sector Indexing in the database.", - "placeholder": null - }, - { - "id": "Error expanding path: {Error}", - "translation": "", - "message": "Error expanding path: {Error}", - "placeholder": null - }, - { - "id": "Could not create repo from directory: {Error}. Aborting migration", - "translation": "", - "message": "Could not create repo from directory: {Error}. Aborting migration", - "placeholder": null - }, - { - "id": "Could not lock miner repo. Your miner must be stopped: {Error}\n Aborting migration", - "translation": "", - "message": "Could not lock miner repo. Your miner must be stopped: {Error}\n Aborting migration", - "placeholder": null - }, - { - "id": "To work with the config:", - "translation": "", - "message": "To work with the config:", - "placeholder": null - } - ] + "messages": [] } diff --git a/cmd/curio/internal/translations/locales/zh/messages.gotext.json b/cmd/curio/internal/translations/locales/zh/messages.gotext.json index ffb5f3bde..fc9a0f313 100644 --- a/cmd/curio/internal/translations/locales/zh/messages.gotext.json +++ b/cmd/curio/internal/translations/locales/zh/messages.gotext.json @@ -699,6 +699,354 @@ "translation": "要使用配置:", "message": "To work with the config:", "placeholder": null + }, + { + "id": "This interactive tool creates a new miner actor and creates the basic configuration layer for it.", + "translation": "此交互式工具将创建一个新的矿工角色,并为其创建基本配置层。", + "message": "This interactive tool creates a new miner actor and creates the basic configuration layer for it.", + "placeholder": null + }, + { + "id": "This process is partially idempotent. Once a new miner actor has been created and subsequent steps fail, the user need to run 'curio config new-cluster {Arg_1}' to finish the configuration.", + "translation": "此过程在某种程度上是幂等的。一旦创建了新的矿工角色,并且后续步骤失败,用户需要运行'curio config new-cluster {Arg_1}'来完成配置。", + "message": "This process is partially idempotent. Once a new miner actor has been created and subsequent steps fail, the user need to run 'curio config new-cluster {Arg_1}' to finish the configuration.", + "placeholder": null + }, + { + "id": "Choose if you with to create a new miner or migrate from existing Lotus-Miner", + "translation": "选择您是否要创建新矿工或从现有的 Lotus-Miner 迁移", + "message": "Choose if you with to create a new miner or migrate from existing Lotus-Miner", + "placeholder": null + }, + { + "id": "Migrate from existing Lotus-Miner", + "translation": "从现有的 Lotus-Miner 迁移", + "message": "Migrate from existing Lotus-Miner", + "placeholder": null + }, + { + "id": "Create a new miner", + "translation": "创建一个新的矿工", + "message": "Create a new miner", + "placeholder": null + }, + { + "id": "New Miner initialization complete.", + "translation": "新矿工初始化完成。", + "message": "New Miner initialization complete.", + "placeholder": null + }, + { + "id": "Migrating lotus-miner config.toml to Curio in-database configuration.", + "translation": "将 lotus-miner config.toml 迁移到 Curio 的数据库配置中。", + "message": "Migrating lotus-miner config.toml to Curio in-database configuration.", + "placeholder": null + }, + { + "id": "Error getting API: {Error}", + "translation": "获取 API 时出错:{Error}", + "message": "Error getting API: {Error}", + "placeholder": null + }, + { + "id": "The Curio team wants to improve the software you use. Tell the team you're using `{Curio}`.", + "translation": "Curio 团队希望改进您使用的软件。告诉团队您正在使用 `{Curio}`。", + "message": "The Curio team wants to improve the software you use. Tell the team you're using `{Curio}`.", + "placeholder": null + }, + { + "id": "Individual Data: Miner ID, Curio version, chain ({Mainnet} or {Calibration}). Signed.", + "translation": "个人数据:矿工 ID,Curio 版本,链({Mainnet} 或 {Calibration})。签名。", + "message": "Individual Data: Miner ID, Curio version, chain ({Mainnet} or {Calibration}). Signed.", + "placeholder": null + }, + { + "id": "Aggregate-Anonymous: version, chain, and Miner power (bucketed).", + "translation": "聚合-匿名:版本,链和矿工算力(分桶)。", + "message": "Aggregate-Anonymous: version, chain, and Miner power (bucketed).", + "placeholder": null + }, + { + "id": "Hint: I am someone running Curio on whichever chain.", + "translation": "提示:我是在任何链上运行 Curio 的人。", + "message": "Hint: I am someone running Curio on whichever chain.", + "placeholder": null + }, + { + "id": "Press return to update {Toml} with Yugabyte info. A Backup file will be written to that folder before changes are made.", + "translation": "按回车键更新 {Toml} 以包含 Yugabyte 信息。在进行更改之前,将在该文件夹中写入备份文件。", + "message": "Press return to update {Toml} with Yugabyte info. A Backup file will be written to that folder before changes are made.", + "placeholder": null + }, + { + "id": "Error creating backup file: {Error}", + "translation": "创建备份文件时出错:{Error}", + "message": "Error creating backup file: {Error}", + "placeholder": null + }, + { + "id": "Error reading config.toml: {Error}", + "translation": "读取 config.toml 时出错:{Error}", + "message": "Error reading config.toml: {Error}", + "placeholder": null + }, + { + "id": "Error writing backup file: {Error}", + "translation": "写入备份文件时出错:{Error}", + "message": "Error writing backup file: {Error}", + "placeholder": null + }, + { + "id": "Error closing backup file: {Error}", + "translation": "关闭备份文件时出错:{Error}", + "message": "Error closing backup file: {Error}", + "placeholder": null + }, + { + "id": "Initializing a new miner actor.", + "translation": "初始化新的矿工角色。", + "message": "Initializing a new miner actor.", + "placeholder": null + }, + { + "id": "Enter the info to create a new miner", + "translation": "输入创建新矿工所需的信息", + "message": "Enter the info to create a new miner", + "placeholder": null + }, + { + "id": "Owner Address: {String}", + "translation": "所有者地址:{String}", + "message": "Owner Address: {String}", + "placeholder": null + }, + { + "id": "Worker Address: {String}", + "translation": "工作地址:{String}", + "message": "Worker Address: {String}", + "placeholder": null + }, + { + "id": "Sender Address: {String}", + "translation": "发送者地址:{String}", + "message": "Sender Address: {String}", + "placeholder": null + }, + { + "id": "Sector Size: {Ssize}", + "translation": "扇区大小: {Ssize}", + "message": "Sector Size: {Ssize}", + "placeholder": null + }, + { + "id": "Confidence epochs: {Confidence}", + "translation": "置信度时期: {Confidence}", + "message": "Confidence epochs: {Confidence}", + "placeholder": null + }, + { + "id": "Continue to verify the addresses and create a new miner actor.", + "translation": "继续验证地址并创建新的矿工角色。", + "message": "Continue to verify the addresses and create a new miner actor.", + "placeholder": null + }, + { + "id": "Miner creation error occurred: {Error}", + "translation": "矿工创建错误发生: {Error}", + "message": "Miner creation error occurred: {Error}", + "placeholder": null + }, + { + "id": "Enter the owner address", + "translation": "输入所有者地址", + "message": "Enter the owner address", + "placeholder": null + }, + { + "id": "No address provided", + "translation": "未提供地址", + "message": "No address provided", + "placeholder": null + }, + { + "id": "Failed to parse the address: {Error}", + "translation": "解析地址失败: {Error}", + "message": "Failed to parse the address: {Error}", + "placeholder": null + }, + { + "id": "Enter {Stringworker_senderi_1} address", + "translation": "输入 {Stringworker_senderi_1} 地址", + "message": "Enter {Stringworker_senderi_1} address", + "placeholder": null + }, + { + "id": "Enter the sector size", + "translation": "输入扇区大小", + "message": "Enter the sector size", + "placeholder": null + }, + { + "id": "Failed to parse sector size: {Error}", + "translation": "解析扇区大小失败: {Error}", + "message": "Failed to parse sector size: {Error}", + "placeholder": null + }, + { + "id": "Enter the confidence", + "translation": "输入置信度", + "message": "Enter the confidence", + "placeholder": null + }, + { + "id": "Failed to parse confidence: {Error}", + "translation": "解析置信度失败: {Error}", + "message": "Failed to parse confidence: {Error}", + "placeholder": null + }, + { + "id": "Failed to create the miner actor: {Error}", + "translation": "创建矿工角色失败: {Error}", + "message": "Failed to create the miner actor: {Error}", + "placeholder": null + }, + { + "id": "Miner {String} created successfully", + "translation": "矿工 {String} 创建成功", + "message": "Miner {String} created successfully", + "placeholder": null + }, + { + "id": "Cannot reach the DB: {Error}", + "translation": "无法访问数据库: {Error}", + "message": "Cannot reach the DB: {Error}", + "placeholder": null + }, + { + "id": "Error connecting to full node API: {Error}", + "translation": "连接到完整节点 API 时发生错误: {Error}", + "message": "Error connecting to full node API: {Error}", + "placeholder": null + }, + { + "id": "Pre-initialization steps complete", + "translation": "预初始化步骤完成", + "message": "Pre-initialization steps complete", + "placeholder": null + }, + { + "id": "Failed to generate random bytes for secret: {Error}", + "translation": "生成密码的随机字节失败: {Error}", + "message": "Failed to generate random bytes for secret: {Error}", + "placeholder": null + }, + { + "id": "Please do not run guided-setup again as miner creation is not idempotent. You need to run 'curio config new-cluster {String}' to finish the configuration", + "translation": "请不要再次运行引导设置,因为矿工创建不是幂等的。 您需要运行 'curio config new-cluster {String}' 来完成配置。", + "message": "Please do not run guided-setup again as miner creation is not idempotent. You need to run 'curio config new-cluster {String}' to finish the configuration", + "placeholder": null + }, + { + "id": "Failed to get API info for FullNode: {Err}", + "translation": "无法获取 FullNode 的 API 信息: {Err}", + "message": "Failed to get API info for FullNode: {Err}", + "placeholder": null + }, + { + "id": "Failed to verify the auth token from daemon node: {Error}", + "translation": "无法验证来自守护进程节点的授权令牌: {Error}", + "message": "Failed to verify the auth token from daemon node: {Error}", + "placeholder": null + }, + { + "id": "Failed to encode the config: {Error}", + "translation": "无法编码配置: {Error}", + "message": "Failed to encode the config: {Error}", + "placeholder": null + }, + { + "id": "Failed to generate default config: {Error}", + "translation": "无法生成默认配置: {Error}", + "message": "Failed to generate default config: {Error}", + "placeholder": null + }, + { + "id": "Failed to insert 'base' config layer in database: {Error}", + "translation": "无法将 'base' 配置层插入数据库: {Error}", + "message": "Failed to insert 'base' config layer in database: {Error}", + "placeholder": null + }, + { + "id": "Failed to insert '{String}' config layer in database: {Error}", + "translation": "无法将 '{String}' 配置层插入数据库: {Error}", + "message": "Failed to insert '{String}' config layer in database: {Error}", + "placeholder": null + }, + { + "id": "New Curio configuration layer '{String}' created", + "translation": "新的 Curio 配置层 '{String}' 已创建", + "message": "New Curio configuration layer '{String}' created", + "placeholder": null + }, + { + "id": "This process is partially idempotent. Once a new miner actor has been created and subsequent steps fail, the user need to run 'curio config new-cluster \u003c miner ID \u003e' to finish the configuration.", + "translation": "该过程部分幂等。一旦创建了新的矿工角色,并且随后的步骤失败,用户需要运行 'curio config new-cluster \u003c 矿工 ID \u003e' 来完成配置。", + "message": "This process is partially idempotent. Once a new miner actor has been created and subsequent steps fail, the user need to run 'curio config new-cluster \u003c miner ID \u003e' to finish the configuration.", + "placeholder": null + }, + { + "id": "Confidence epochs", + "translation": "置信度时期", + "message": "Confidence epochs", + "placeholder": null + }, + { + "id": "Increase reliability using redundancy: start multiple machines with at-least the post layer: 'curio run --layers=post'", + "translation": "通过冗余增加可靠性:使用至少后层启动多台机器:'curio run --layers=post'", + "message": "Increase reliability using redundancy: start multiple machines with at-least the post layer: 'curio run --layers=post'", + "placeholder": null + }, + { + "id": "I want to:", + "translation": "我想要:", + "message": "I want to:", + "placeholder": null + }, + { + "id": "Configuration 'base' was updated to include this miner's address", + "translation": "配置 'base' 已更新以包含此矿工的地址", + "message": "Configuration 'base' was updated to include this miner's address", + "placeholder": null + }, + { + "id": "Cannot load base config: {Error}", + "translation": "无法加载基本配置: {Error}", + "message": "Cannot load base config: {Error}", + "placeholder": null + }, + { + "id": "Failed to load base config: {Error}", + "translation": "加载基本配置失败: {Error}", + "message": "Failed to load base config: {Error}", + "placeholder": null + }, + { + "id": "Failed to regenerate base config: {Error}", + "translation": "重新生成基本配置失败: {Error}", + "message": "Failed to regenerate base config: {Error}", + "placeholder": null + }, + { + "id": "Failed to load base config from database: {Error}", + "translation": "从数据库加载基本配置失败:{Error}", + "message": "Failed to load base config from database: {Error}", + "placeholder": null + }, + { + "id": "Failed to parse base config: {Error}", + "translation": "解析基本配置失败:{Error}", + "message": "Failed to parse base config: {Error}", + "placeholder": null } ] } \ No newline at end of file diff --git a/cmd/curio/internal/translations/locales/zh/out.gotext.json b/cmd/curio/internal/translations/locales/zh/out.gotext.json index 37ac5f933..bb9d25e4c 100644 --- a/cmd/curio/internal/translations/locales/zh/out.gotext.json +++ b/cmd/curio/internal/translations/locales/zh/out.gotext.json @@ -1,89 +1,4 @@ { "language": "zh", - "messages": [ - { - "id": "Try the web interface with {__layersgui} for further guided improvements.", - "translation": "", - "message": "Try the web interface with {__layersgui} for further guided improvements.", - "placeholder": null - }, - { - "id": "Error connecting to lotus node: {Error} {Error_1}", - "translation": "", - "message": "Error connecting to lotus node: {Error} {Error_1}", - "placeholder": null - }, - { - "id": "could not get API info for FullNode: {Err}", - "translation": "", - "message": "could not get API info for FullNode: {Err}", - "placeholder": null - }, - { - "id": "Error getting token: {Error}", - "translation": "", - "message": "Error getting token: {Error}", - "placeholder": null - }, - { - "id": "Filecoin {Slack} channels: {Fil_curio_help} and {Fil_curio_dev}", - "translation": "", - "message": "Filecoin {Slack} channels: {Fil_curio_help} and {Fil_curio_dev}", - "placeholder": null - }, - { - "id": "Start multiple Curio instances with the '{Post}' layer to redundancy.", - "translation": "", - "message": "Start multiple Curio instances with the '{Post}' layer to redundancy.", - "placeholder": null - }, - { - "id": "One database can serve multiple miner IDs: Run a migration for each lotus-miner.", - "translation": "", - "message": "One database can serve multiple miner IDs: Run a migration for each lotus-miner.", - "placeholder": null - }, - { - "id": "Please start (or restart) {Lotus_miner} now that database credentials are in {Toml}.", - "translation": "", - "message": "Please start (or restart) {Lotus_miner} now that database credentials are in {Toml}.", - "placeholder": null - }, - { - "id": "Error interpreting miner ID: {Error}: ID: {String}", - "translation": "", - "message": "Error interpreting miner ID: {Error}: ID: {String}", - "placeholder": null - }, - { - "id": "Enabling Sector Indexing in the database.", - "translation": "", - "message": "Enabling Sector Indexing in the database.", - "placeholder": null - }, - { - "id": "Error expanding path: {Error}", - "translation": "", - "message": "Error expanding path: {Error}", - "placeholder": null - }, - { - "id": "Could not create repo from directory: {Error}. Aborting migration", - "translation": "", - "message": "Could not create repo from directory: {Error}. Aborting migration", - "placeholder": null - }, - { - "id": "Could not lock miner repo. Your miner must be stopped: {Error}\n Aborting migration", - "translation": "", - "message": "Could not lock miner repo. Your miner must be stopped: {Error}\n Aborting migration", - "placeholder": null - }, - { - "id": "To work with the config:", - "translation": "", - "message": "To work with the config:", - "placeholder": null - } - ] + "messages": [] } diff --git a/cmd/curio/internal/translations/updateLang.sh b/cmd/curio/internal/translations/updateLang.sh index 6325ab146..984f63fd5 100755 --- a/cmd/curio/internal/translations/updateLang.sh +++ b/cmd/curio/internal/translations/updateLang.sh @@ -2,7 +2,7 @@ #OP: Only run if some file in ../guidedsetup* is newer than catalog.go # Change this condition if using translations more widely. -if [ "$(find ../../guidedsetup/* -newermt "$(date -d '1 minute ago')" -newer catalog.go)" ] || [ "$(find locales/* -newermt "$(date -d '1 minute ago')" -newer catalog.go)" ]; then +if [ "$(find ../../guidedsetup/* -newer catalog.go)" ] || [ "$(find locales/* -newer catalog.go)" ]; then gotext -srclang=en update -out=catalog.go -lang=en,zh,ko github.com/filecoin-project/lotus/cmd/curio/guidedsetup go run knowns/main.go locales/zh locales/ko fi diff --git a/cmd/curio/main.go b/cmd/curio/main.go index 9a58768f3..bb506f190 100644 --- a/cmd/curio/main.go +++ b/cmd/curio/main.go @@ -111,7 +111,7 @@ func main() { Name: "db-host", EnvVars: []string{"CURIO_DB_HOST", "CURIO_HARMONYDB_HOSTS"}, Usage: "Command separated list of hostnames for yugabyte cluster", - Value: "yugabyte", + Value: "127.0.0.1", }, &cli.StringFlag{ Name: "db-name", @@ -131,7 +131,6 @@ func main() { &cli.StringFlag{ Name: "db-port", EnvVars: []string{"CURIO_DB_PORT", "CURIO_HARMONYDB_PORT"}, - Hidden: true, Value: "5433", }, &cli.StringFlag{ diff --git a/cmd/sptool/actor.go b/cmd/sptool/actor.go index fb0d5e966..f17568a31 100644 --- a/cmd/sptool/actor.go +++ b/cmd/sptool/actor.go @@ -32,6 +32,7 @@ var actorCmd = &cli.Command{ spcli.ActorCompactAllocatedCmd(SPTActorGetter), spcli.ActorProposeChangeBeneficiaryCmd(SPTActorGetter), spcli.ActorConfirmChangeBeneficiaryCmd(SPTActorGetter), + spcli.ActorNewMinerCmd, }, } diff --git a/cmd/sptool/sector.go b/cmd/sptool/sector.go index 8f33053d3..b8e003c99 100644 --- a/cmd/sptool/sector.go +++ b/cmd/sptool/sector.go @@ -280,7 +280,6 @@ var sectorsListCmd = &cli.Command{ for _, st := range sset { s := st.SectorNumber - _, inSSet := commitedIDs[s] _, inASet := activeIDs[s] diff --git a/documentation/en/cli-curio.md b/documentation/en/cli-curio.md index f7c4f2d0c..88e8c1cce 100644 --- a/documentation/en/cli-curio.md +++ b/documentation/en/cli-curio.md @@ -16,7 +16,7 @@ COMMANDS: config Manage node config by layers. The layer 'base' will always be applied at Curio start-up. test Utility functions for testing web Start Curio web interface - guided-setup Run the guided setup for migrating from lotus-miner to Curio + guided-setup Run the guided setup for migrating from lotus-miner to Curio or Creating a new Curio miner seal Manage the sealing pipeline market auth Manage RPC permissions @@ -28,10 +28,11 @@ COMMANDS: GLOBAL OPTIONS: --color use color in display output (default: depends on output being a TTY) - --db-host value Command separated list of hostnames for yugabyte cluster (default: "yugabyte") [$CURIO_DB_HOST, $CURIO_HARMONYDB_HOSTS] + --db-host value Command separated list of hostnames for yugabyte cluster (default: "127.0.0.1") [$CURIO_DB_HOST, $CURIO_HARMONYDB_HOSTS] --db-name value (default: "yugabyte") [$CURIO_DB_NAME, $CURIO_HARMONYDB_NAME] --db-user value (default: "yugabyte") [$CURIO_DB_USER, $CURIO_HARMONYDB_USERNAME] --db-password value (default: "yugabyte") [$CURIO_DB_PASSWORD, $CURIO_HARMONYDB_PASSWORD] + --db-port value (default: "5433") [$CURIO_DB_PORT, $CURIO_HARMONYDB_PORT] --repo-path value (default: "~/.curio") [$CURIO_REPO_PATH] --vv enables very verbose mode, useful for debugging the CLI (default: false) --help, -h show help @@ -305,7 +306,7 @@ OPTIONS: ## curio guided-setup ``` NAME: - curio guided-setup - Run the guided setup for migrating from lotus-miner to Curio + curio guided-setup - Run the guided setup for migrating from lotus-miner to Curio or Creating a new Curio miner USAGE: curio guided-setup [command options] [arguments...] diff --git a/documentation/en/cli-sptool.md b/documentation/en/cli-sptool.md index 7b888a884..a9bd90897 100644 --- a/documentation/en/cli-sptool.md +++ b/documentation/en/cli-sptool.md @@ -43,6 +43,7 @@ COMMANDS: compact-allocated compact allocated sectors bitfield propose-change-beneficiary Propose a beneficiary address change confirm-change-beneficiary Confirm a beneficiary address change + new-miner Initializes a new miner actor help, h Shows a list of commands or help for one command OPTIONS: @@ -231,6 +232,22 @@ OPTIONS: --help, -h show help ``` +### sptool actor new-miner +``` +NAME: + sptool actor new-miner - Initializes a new miner actor + +USAGE: + sptool actor new-miner [command options] [arguments...] + +OPTIONS: + --worker value, -w value worker key to use for new miner initialisation + --owner value, -o value owner key to use for new miner initialisation + --from value, -f value address to send actor(miner) creation message from + --sector-size value specify sector size to use for new miner initialisation + --help, -h show help +``` + ## sptool info ``` NAME: diff --git a/go.mod b/go.mod index 82f1838bb..7787753c3 100644 --- a/go.mod +++ b/go.mod @@ -69,6 +69,7 @@ require ( github.com/georgysavva/scany/v2 v2.0.0 github.com/go-openapi/spec v0.19.11 github.com/golang/mock v1.6.0 + github.com/google/go-cmp v0.6.0 github.com/google/uuid v1.5.0 github.com/gorilla/mux v1.8.1 github.com/gorilla/websocket v1.5.1 @@ -222,7 +223,6 @@ require ( github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/golang/snappy v0.0.4 // indirect - github.com/google/go-cmp v0.6.0 // indirect github.com/google/gopacket v1.1.19 // indirect github.com/google/pprof v0.0.0-20240207164012-fb44976bdcd5 // indirect github.com/hannahhoward/cbor-gen-for v0.0.0-20230214144701-5d17c9d5243c // indirect diff --git a/itests/curio_test.go b/itests/curio_test.go new file mode 100644 index 000000000..997352dd3 --- /dev/null +++ b/itests/curio_test.go @@ -0,0 +1,67 @@ +package itests + +import ( + "context" + "testing" + "time" + + "github.com/docker/go-units" + "github.com/stretchr/testify/require" + + "github.com/filecoin-project/go-state-types/abi" + + "github.com/filecoin-project/lotus/cli/spcli" + "github.com/filecoin-project/lotus/cmd/curio/deps" + "github.com/filecoin-project/lotus/itests/kit" + "github.com/filecoin-project/lotus/node/config" + "github.com/filecoin-project/lotus/node/impl" +) + +func TestCurioNewActor(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + full, miner, esemble := kit.EnsembleMinimal(t, + kit.LatestActorsAt(-1), + kit.MockProofs(), + kit.WithSectorIndexDB(), + ) + + esemble.Start() + blockTime := 100 * time.Millisecond + esemble.BeginMining(blockTime) + + db := miner.BaseAPI.(*impl.StorageMinerAPI).HarmonyDB + + var titles []string + err := db.Select(ctx, &titles, `SELECT title FROM harmony_config WHERE LENGTH(config) > 0`) + require.NoError(t, err) + require.NotEmpty(t, titles) + require.NotContains(t, titles, "base") + + addr := miner.OwnerKey.Address + sectorSizeInt, err := units.RAMInBytes("8MiB") + require.NoError(t, err) + + maddr, err := spcli.CreateStorageMiner(ctx, full, addr, addr, addr, abi.SectorSize(sectorSizeInt), 0) + require.NoError(t, err) + + err = deps.CreateMinerConfig(ctx, full, db, []string{maddr.String()}, "FULL NODE API STRING") + require.NoError(t, err) + + err = db.Select(ctx, &titles, `SELECT title FROM harmony_config WHERE LENGTH(config) > 0`) + require.NoError(t, err) + require.Contains(t, titles, "base") + baseCfg := config.DefaultCurioConfig() + var baseText string + + err = db.QueryRow(ctx, "SELECT config FROM harmony_config WHERE title='base'").Scan(&baseText) + require.NoError(t, err) + _, err = deps.LoadConfigWithUpgrades(baseText, baseCfg) + require.NoError(t, err) + + require.NotNil(t, baseCfg.Addresses) + require.GreaterOrEqual(t, len(baseCfg.Addresses), 1) + + require.Contains(t, baseCfg.Addresses[0].MinerAddresses, maddr.String()) +} diff --git a/node/config/load.go b/node/config/load.go index 1b8df1696..1d5a87458 100644 --- a/node/config/load.go +++ b/node/config/load.go @@ -4,13 +4,17 @@ import ( "bytes" "fmt" "io" + "math/big" "os" "reflect" "regexp" + "sort" "strings" "unicode" "github.com/BurntSushi/toml" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" "github.com/kelseyhightower/envconfig" "golang.org/x/xerrors" ) @@ -395,8 +399,22 @@ func ConfigUpdate(cfgCur, cfgDef interface{}, opts ...UpdateCfgOpt) ([]byte, err return nil, xerrors.Errorf("parsing updated config: %w", err) } - if !reflect.DeepEqual(cfgCur, cfgUpdated) { - return nil, xerrors.Errorf("updated config didn't match current config:") + opts := []cmp.Option{ + // This equality function compares big.Int + cmpopts.IgnoreUnexported(big.Int{}), + cmp.Comparer(func(x, y []string) bool { + tx, ty := reflect.TypeOf(x), reflect.TypeOf(y) + if tx.Kind() == reflect.Slice && ty.Kind() == reflect.Slice && tx.Elem().Kind() == reflect.String && ty.Elem().Kind() == reflect.String { + sort.Strings(x) + sort.Strings(y) + return strings.Join(x, "\n") == strings.Join(y, "\n") + } + return false + }), + } + + if !cmp.Equal(cfgUpdated, cfgCur, opts...) { + return nil, xerrors.Errorf("updated config didn't match current config") } } From 1645c329979cd9b409c19dd76b6cf28cb499bfe0 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 3 Apr 2024 19:10:31 -0700 Subject: [PATCH 319/394] test: eth: deflake EthBlockHashesCorrect_MultiBlockTipset (#11808) Increase the chances of having multiple blocks at the same height by aligning block times across test miners. Also, make the block hash test faster. --- itests/eth_block_hash_test.go | 24 +++++++++++++++--------- itests/kit/blockminer.go | 7 ++++--- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/itests/eth_block_hash_test.go b/itests/eth_block_hash_test.go index 8debefa49..927b64fc5 100644 --- a/itests/eth_block_hash_test.go +++ b/itests/eth_block_hash_test.go @@ -26,30 +26,36 @@ import ( func TestEthBlockHashesCorrect_MultiBlockTipset(t *testing.T) { // miner is connected to the first node, and we want to observe the chain // from the second node. - blocktime := 250 * time.Millisecond + blocktime := 100 * time.Millisecond n1, m1, m2, ens := kit.EnsembleOneTwo(t, kit.MockProofs(), kit.ThroughRPC(), ) ens.InterconnectAll().BeginMining(blocktime) - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - n1.WaitTillChain(ctx, kit.HeightAtLeast(abi.ChainEpoch(5))) - defer cancel() + { + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + n1.WaitTillChain(ctx, kit.HeightAtLeast(abi.ChainEpoch(5))) + cancel() + } var n2 kit.TestFullNode ens.FullNode(&n2, kit.ThroughRPC()).Start().Connect(n2, n1) - // find the first tipset where all miners mined a block. - ctx, cancel = context.WithTimeout(context.Background(), 5*time.Minute) - n2.WaitTillChain(ctx, kit.BlocksMinedByAll(m1.ActorAddr, m2.ActorAddr)) - defer cancel() + { + // find the first tipset where all miners mined a block. + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) + n2.WaitTillChain(ctx, kit.BlocksMinedByAll(m1.ActorAddr, m2.ActorAddr)) + cancel() + } head, err := n2.ChainHead(context.Background()) require.NoError(t, err) + ctx := context.Background() + // let the chain run a little bit longer to minimise the chance of reorgs - n2.WaitTillChain(ctx, kit.HeightAtLeast(head.Height()+50)) + n2.WaitTillChain(ctx, kit.HeightAtLeast(head.Height()+10)) tsk := head.Key() for i := 1; i <= int(head.Height()); i++ { diff --git a/itests/kit/blockminer.go b/itests/kit/blockminer.go index bd527910d..40d23a6cd 100644 --- a/itests/kit/blockminer.go +++ b/itests/kit/blockminer.go @@ -256,8 +256,6 @@ func (bm *BlockMiner) MineBlocksMustPost(ctx context.Context, blocktime time.Dur } func (bm *BlockMiner) MineBlocks(ctx context.Context, blocktime time.Duration) { - time.Sleep(time.Second) - // wrap context in a cancellable context. ctx, bm.cancel = context.WithCancel(ctx) @@ -278,8 +276,11 @@ func (bm *BlockMiner) MineBlocks(ctx context.Context, blocktime time.Duration) { default: } + now := time.Duration(time.Now().UnixNano()) + delay := blocktime - (now % blocktime) + select { - case <-time.After(blocktime): + case <-time.After(delay): case <-ctx.Done(): return } From a1264c8c8402b868a69dc34311fd09cebd669f93 Mon Sep 17 00:00:00 2001 From: Jiaying Wang <42981373+jennijuju@users.noreply.github.com> Date: Wed, 3 Apr 2024 22:42:24 -0400 Subject: [PATCH 320/394] update deps (#11819) --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 7787753c3..6fe41e67f 100644 --- a/go.mod +++ b/go.mod @@ -111,7 +111,7 @@ require ( github.com/kelseyhightower/envconfig v1.4.0 github.com/koalacxr/quantile v0.0.1 github.com/libp2p/go-buffer-pool v0.1.0 - github.com/libp2p/go-libp2p v0.33.1 + github.com/libp2p/go-libp2p v0.33.2 github.com/libp2p/go-libp2p-kad-dht v0.25.2 github.com/libp2p/go-libp2p-pubsub v0.10.0 github.com/libp2p/go-libp2p-record v0.2.0 @@ -125,7 +125,7 @@ require ( github.com/minio/sha256-simd v1.0.1 github.com/mitchellh/go-homedir v1.1.0 github.com/multiformats/go-base32 v0.1.0 - github.com/multiformats/go-multiaddr v0.12.2 + github.com/multiformats/go-multiaddr v0.12.3 github.com/multiformats/go-multiaddr-dns v0.3.1 github.com/multiformats/go-multibase v0.2.0 github.com/multiformats/go-multicodec v0.9.0 diff --git a/go.sum b/go.sum index edfb8164c..228c83a78 100644 --- a/go.sum +++ b/go.sum @@ -1005,8 +1005,8 @@ github.com/libp2p/go-libp2p v0.7.0/go.mod h1:hZJf8txWeCduQRDC/WSqBGMxaTHCOYHt2xS github.com/libp2p/go-libp2p v0.7.4/go.mod h1:oXsBlTLF1q7pxr+9w6lqzS1ILpyHsaBPniVO7zIHGMw= github.com/libp2p/go-libp2p v0.8.1/go.mod h1:QRNH9pwdbEBpx5DTJYg+qxcVaDMAz3Ee/qDKwXujH5o= github.com/libp2p/go-libp2p v0.14.3/go.mod h1:d12V4PdKbpL0T1/gsUNN8DfgMuRPDX8bS2QxCZlwRH0= -github.com/libp2p/go-libp2p v0.33.1 h1:tvJl9b9M6nSLBtZSXSguq+/lRhRj2oLRkyhBmQNMFLA= -github.com/libp2p/go-libp2p v0.33.1/go.mod h1:zOUTMjG4I7TXwMndNyOBn/CNtVBLlvBlnxfi+8xzx+E= +github.com/libp2p/go-libp2p v0.33.2 h1:vCdwnFxoGOXMKmaGHlDSnL4bM3fQeW8pgIa9DECnb40= +github.com/libp2p/go-libp2p v0.33.2/go.mod h1:zTeppLuCvUIkT118pFVzA8xzP/p2dJYOMApCkFh0Yww= github.com/libp2p/go-libp2p-asn-util v0.4.1 h1:xqL7++IKD9TBFMgnLPZR6/6iYhawHKHl950SO9L6n94= github.com/libp2p/go-libp2p-asn-util v0.4.1/go.mod h1:d/NI6XZ9qxw67b4e+NgpQexCIiFYJjErASrYW4PFDN8= github.com/libp2p/go-libp2p-autonat v0.1.0/go.mod h1:1tLf2yXxiE/oKGtDwPYWTSYG3PtvYlJmg7NeVtPRqH8= @@ -1302,8 +1302,8 @@ github.com/multiformats/go-multiaddr v0.2.2/go.mod h1:NtfXiOtHvghW9KojvtySjH5y0u github.com/multiformats/go-multiaddr v0.3.0/go.mod h1:dF9kph9wfJ+3VLAaeBqo9Of8x4fJxp6ggJGteB8HQTI= github.com/multiformats/go-multiaddr v0.3.1/go.mod h1:uPbspcUPd5AfaP6ql3ujFY+QWzmBD8uLLL4bXW0XfGc= github.com/multiformats/go-multiaddr v0.3.3/go.mod h1:lCKNGP1EQ1eZ35Za2wlqnabm9xQkib3fyB+nZXHLag0= -github.com/multiformats/go-multiaddr v0.12.2 h1:9G9sTY/wCYajKa9lyfWPmpZAwe6oV+Wb1zcmMS1HG24= -github.com/multiformats/go-multiaddr v0.12.2/go.mod h1:GKyaTYjZRdcUhyOetrxTk9z0cW+jA/YrnqTOvKgi44M= +github.com/multiformats/go-multiaddr v0.12.3 h1:hVBXvPRcKG0w80VinQ23P5t7czWgg65BmIvQKjDydU8= +github.com/multiformats/go-multiaddr v0.12.3/go.mod h1:sBXrNzucqkFJhvKOiwwLyqamGa/P5EIXNPLovyhQCII= github.com/multiformats/go-multiaddr-dns v0.0.1/go.mod h1:9kWcqw/Pj6FwxAwW38n/9403szc57zJPs45fmnznu3Q= github.com/multiformats/go-multiaddr-dns v0.0.2/go.mod h1:9kWcqw/Pj6FwxAwW38n/9403szc57zJPs45fmnznu3Q= github.com/multiformats/go-multiaddr-dns v0.2.0/go.mod h1:TJ5pr5bBO7Y1B18djPuRsVkduhQH2YqYSbxWJzYGdK0= From 800ae977d36f957c3b9d8d1eae0248fc5e5bb4f1 Mon Sep 17 00:00:00 2001 From: LexLuthr Date: Thu, 4 Apr 2024 16:06:21 +0400 Subject: [PATCH 321/394] rename lotus-provider --- cmd/curio/cli.go | 2 +- cmd/curio/config.go | 2 +- cmd/curio/config_test.go | 12 ++++++------ cmd/curio/storage.go | 2 +- cmd/lotus-shed/deal.go | 12 ++++++------ curiosrc/ffi/sdr_funcs.go | 2 +- curiosrc/market/fakelm/iface.go | 2 +- curiosrc/seal/README.md | 4 ++-- documentation/en/cli-curio.md | 2 +- documentation/en/default-curio-config.toml | 12 ++++++------ node/config/doc_gen.go | 12 ++++++------ node/config/types.go | 12 ++++++------ 12 files changed, 38 insertions(+), 38 deletions(-) diff --git a/cmd/curio/cli.go b/cmd/curio/cli.go index 58383e2a7..727de122d 100644 --- a/cmd/curio/cli.go +++ b/cmd/curio/cli.go @@ -30,7 +30,7 @@ var cliCmd = &cli.Command{ Flags: []cli.Flag{ &cli.StringFlag{ Name: "machine", - Usage: "machine host:port (lotus-provider run --listen address)", + Usage: "machine host:port (curio run --listen address)", }, }, Before: func(cctx *cli.Context) error { diff --git a/cmd/curio/config.go b/cmd/curio/config.go index 66bb2e8b4..16b7d89c3 100644 --- a/cmd/curio/config.go +++ b/cmd/curio/config.go @@ -402,7 +402,7 @@ func diff(sourceConf, newConf string) (string, error) { } func edit(editor, cfg string) (string, error) { - file, err := os.CreateTemp("", "lotus-provider-config-*.toml") + file, err := os.CreateTemp("", "curio-config-*.toml") if err != nil { return "", err } diff --git a/cmd/curio/config_test.go b/cmd/curio/config_test.go index 31c4b4bd0..e476f23d0 100644 --- a/cmd/curio/config_test.go +++ b/cmd/curio/config_test.go @@ -12,7 +12,7 @@ import ( var baseText string = ` [Subsystems] - # EnableWindowPost enables window post to be executed on this lotus-provider instance. Each machine in the cluster + # EnableWindowPost enables window post to be executed on this curio instance. Each machine in the cluster # with WindowPoSt enabled will also participate in the window post scheduler. It is possible to have multiple # machines with WindowPoSt enabled which will provide redundancy, and in case of multiple partitions per deadline, # will allow for parallel processing of partitions. @@ -27,7 +27,7 @@ var baseText string = ` # type: int #WindowPostMaxTasks = 0 - # EnableWinningPost enables winning post to be executed on this lotus-provider instance. + # EnableWinningPost enables winning post to be executed on this curio instance. # Each machine in the cluster with WinningPoSt enabled will also participate in the winning post scheduler. # It is possible to mix machines with WindowPoSt and WinningPoSt enabled, for details see the EnableWindowPost # documentation. @@ -106,7 +106,7 @@ var baseText string = ` #FinalizeMaxTasks = 0 # EnableSendPrecommitMsg enables the sending of precommit messages to the chain - # from this lotus-provider instance. + # from this curio instance. # This runs after SDRTrees and uses the output CommD / CommR (roots of TreeD / TreeR) for the message # # type: bool @@ -131,12 +131,12 @@ var baseText string = ` #PoRepProofMaxTasks = 0 # EnableSendCommitMsg enables the sending of commit messages to the chain - # from this lotus-provider instance. + # from this curio instance. # # type: bool #EnableSendCommitMsg = false - # EnableMoveStorage enables the move-into-long-term-storage task to run on this lotus-provider instance. + # EnableMoveStorage enables the move-into-long-term-storage task to run on this curio instance. # This tasks should only be enabled on nodes with long-term storage. # # The MoveStorage task is the last task in the sealing pipeline. It moves the sealed sector data from the @@ -152,7 +152,7 @@ var baseText string = ` # type: int #MoveStorageMaxTasks = 0 - # EnableWebGui enables the web GUI on this lotus-provider instance. The UI has minimal local overhead, but it should + # EnableWebGui enables the web GUI on this curio instance. The UI has minimal local overhead, but it should # only need to be run on a single machine in the cluster. # # type: bool diff --git a/cmd/curio/storage.go b/cmd/curio/storage.go index c5fe77e18..9a073e037 100644 --- a/cmd/curio/storage.go +++ b/cmd/curio/storage.go @@ -54,7 +54,7 @@ var storageAttachCmd = &cli.Command{ Usage: "attach local storage path", ArgsUsage: "[path]", Description: `Storage can be attached to the miner using this command. The storage volume -list is stored local to the miner in storage.json set in lotus-provider run. We do not +list is stored local to the miner in storage.json set in curio run. We do not recommend manually modifying this value without further understanding of the storage system. diff --git a/cmd/lotus-shed/deal.go b/cmd/lotus-shed/deal.go index 029e29666..3a350976e 100644 --- a/cmd/lotus-shed/deal.go +++ b/cmd/lotus-shed/deal.go @@ -31,8 +31,8 @@ import ( ) var lpUtilCmd = &cli.Command{ - Name: "provider-util", - Usage: "lotus provider utility commands", + Name: "curio-util", + Usage: "curio utility commands", Subcommands: []*cli.Command{ lpUtilStartDealCmd, }, @@ -40,11 +40,11 @@ var lpUtilCmd = &cli.Command{ var lpUtilStartDealCmd = &cli.Command{ Name: "start-deal", - Usage: "start a deal with a specific lotus-provider instance", + Usage: "start a deal with a specific curio instance", ArgsUsage: "[dataFile] [miner]", Flags: []cli.Flag{ &cli.StringFlag{ - Name: "provider-rpc", + Name: "curio-rpc", Value: "http://127.0.0.1:12300", }, }, @@ -73,7 +73,7 @@ var lpUtilStartDealCmd = &cli.Command{ // open rpc var rpc api.CurioStruct - closer2, err := jsonrpc.NewMergeClient(ctx, cctx.String("provider-rpc"), "Filecoin", []interface{}{&rpc.Internal}, nil) + closer2, err := jsonrpc.NewMergeClient(ctx, cctx.String("curio-rpc"), "Filecoin", []interface{}{&rpc.Internal}, nil) if err != nil { return xerrors.Errorf("open rpc: %w", err) } @@ -84,7 +84,7 @@ var lpUtilStartDealCmd = &cli.Command{ return xerrors.Errorf("rpc version: %w", err) } - fmt.Printf("* provider version: %s\n", v.String()) + fmt.Printf("* curio version: %s\n", v.String()) // open data file data, err := homedir.Expand(cctx.Args().Get(0)) diff --git a/curiosrc/ffi/sdr_funcs.go b/curiosrc/ffi/sdr_funcs.go index 325d30ef2..74a498219 100644 --- a/curiosrc/ffi/sdr_funcs.go +++ b/curiosrc/ffi/sdr_funcs.go @@ -393,7 +393,7 @@ func (sb *SealCalls) LocalStorage(ctx context.Context) ([]storiface.StoragePath, func (sb *SealCalls) FinalizeSector(ctx context.Context, sector storiface.SectorRef, keepUnsealed bool) error { alloc := storiface.FTNone if keepUnsealed { - // note: In lotus-provider we don't write the unsealed file in any of the previous stages, it's only written here from tree-d + // note: In Curio we don't write the unsealed file in any of the previous stages, it's only written here from tree-d alloc = storiface.FTUnsealed } diff --git a/curiosrc/market/fakelm/iface.go b/curiosrc/market/fakelm/iface.go index 4e49e09c4..1bc91b35e 100644 --- a/curiosrc/market/fakelm/iface.go +++ b/curiosrc/market/fakelm/iface.go @@ -12,7 +12,7 @@ import ( "github.com/filecoin-project/lotus/storage/sealer/storiface" ) -// MinimalLMApi is a subset of the LotusMiner API that is exposed by lotus-provider +// MinimalLMApi is a subset of the LotusMiner API that is exposed by Curio // for consumption by boost type MinimalLMApi interface { ActorAddress(context.Context) (address.Address, error) diff --git a/curiosrc/seal/README.md b/curiosrc/seal/README.md index 2f9055f86..b148e4204 100644 --- a/curiosrc/seal/README.md +++ b/curiosrc/seal/README.md @@ -1,8 +1,8 @@ -# Lotus-Provider Sealer +# Curio Sealer ## Overview -The lotus-provider sealer is a collection of harmony tasks and a common poller +The Curio sealer is a collection of harmony tasks and a common poller which implement the sealing functionality of the Filecoin protocol. ## Pipeline Tasks diff --git a/documentation/en/cli-curio.md b/documentation/en/cli-curio.md index 88e8c1cce..16b374687 100644 --- a/documentation/en/cli-curio.md +++ b/documentation/en/cli-curio.md @@ -52,7 +52,7 @@ COMMANDS: help, h Shows a list of commands or help for one command OPTIONS: - --machine value machine host:port (lotus-provider run --listen address) + --machine value machine host:port (curio run --listen address) --help, -h show help ``` diff --git a/documentation/en/default-curio-config.toml b/documentation/en/default-curio-config.toml index f4e4c19b8..3ecadeb52 100644 --- a/documentation/en/default-curio-config.toml +++ b/documentation/en/default-curio-config.toml @@ -1,5 +1,5 @@ [Subsystems] - # EnableWindowPost enables window post to be executed on this lotus-provider instance. Each machine in the cluster + # EnableWindowPost enables window post to be executed on this curio instance. Each machine in the cluster # with WindowPoSt enabled will also participate in the window post scheduler. It is possible to have multiple # machines with WindowPoSt enabled which will provide redundancy, and in case of multiple partitions per deadline, # will allow for parallel processing of partitions. @@ -14,7 +14,7 @@ # type: int #WindowPostMaxTasks = 0 - # EnableWinningPost enables winning post to be executed on this lotus-provider instance. + # EnableWinningPost enables winning post to be executed on this curio instance. # Each machine in the cluster with WinningPoSt enabled will also participate in the winning post scheduler. # It is possible to mix machines with WindowPoSt and WinningPoSt enabled, for details see the EnableWindowPost # documentation. @@ -93,7 +93,7 @@ #FinalizeMaxTasks = 0 # EnableSendPrecommitMsg enables the sending of precommit messages to the chain - # from this lotus-provider instance. + # from this curio instance. # This runs after SDRTrees and uses the output CommD / CommR (roots of TreeD / TreeR) for the message # # type: bool @@ -118,12 +118,12 @@ #PoRepProofMaxTasks = 0 # EnableSendCommitMsg enables the sending of commit messages to the chain - # from this lotus-provider instance. + # from this curio instance. # # type: bool #EnableSendCommitMsg = false - # EnableMoveStorage enables the move-into-long-term-storage task to run on this lotus-provider instance. + # EnableMoveStorage enables the move-into-long-term-storage task to run on this curio instance. # This tasks should only be enabled on nodes with long-term storage. # # The MoveStorage task is the last task in the sealing pipeline. It moves the sealed sector data from the @@ -160,7 +160,7 @@ # type: []string #BoostAdapters = [] - # EnableWebGui enables the web GUI on this lotus-provider instance. The UI has minimal local overhead, but it should + # EnableWebGui enables the web GUI on this curio instance. The UI has minimal local overhead, but it should # only need to be run on a single machine in the cluster. # # type: bool diff --git a/node/config/doc_gen.go b/node/config/doc_gen.go index 3b4124f8a..9a4b91c4a 100644 --- a/node/config/doc_gen.go +++ b/node/config/doc_gen.go @@ -403,7 +403,7 @@ to prove each deadline, resulting in more total gas use (but each message will h Name: "EnableWindowPost", Type: "bool", - Comment: `EnableWindowPost enables window post to be executed on this lotus-provider instance. Each machine in the cluster + Comment: `EnableWindowPost enables window post to be executed on this curio instance. Each machine in the cluster with WindowPoSt enabled will also participate in the window post scheduler. It is possible to have multiple machines with WindowPoSt enabled which will provide redundancy, and in case of multiple partitions per deadline, will allow for parallel processing of partitions. @@ -422,7 +422,7 @@ partitionsPerDeadline+1 machines.`, Name: "EnableWinningPost", Type: "bool", - Comment: `EnableWinningPost enables winning post to be executed on this lotus-provider instance. + Comment: `EnableWinningPost enables winning post to be executed on this curio instance. Each machine in the cluster with WinningPoSt enabled will also participate in the winning post scheduler. It is possible to mix machines with WindowPoSt and WinningPoSt enabled, for details see the EnableWindowPost documentation.`, @@ -514,7 +514,7 @@ Finalize will run in parallel with the SubmitCommitMsg task.`, Type: "bool", Comment: `EnableSendPrecommitMsg enables the sending of precommit messages to the chain -from this lotus-provider instance. +from this curio instance. This runs after SDRTrees and uses the output CommD / CommR (roots of TreeD / TreeR) for the message`, }, { @@ -542,13 +542,13 @@ also be bounded by resources available on the machine.`, Type: "bool", Comment: `EnableSendCommitMsg enables the sending of commit messages to the chain -from this lotus-provider instance.`, +from this curio instance.`, }, { Name: "EnableMoveStorage", Type: "bool", - Comment: `EnableMoveStorage enables the move-into-long-term-storage task to run on this lotus-provider instance. + Comment: `EnableMoveStorage enables the move-into-long-term-storage task to run on this curio instance. This tasks should only be enabled on nodes with long-term storage. The MoveStorage task is the last task in the sealing pipeline. It moves the sealed sector data from the @@ -588,7 +588,7 @@ a machine which handles ParkPiece tasks.`, Name: "EnableWebGui", Type: "bool", - Comment: `EnableWebGui enables the web GUI on this lotus-provider instance. The UI has minimal local overhead, but it should + Comment: `EnableWebGui enables the web GUI on this curio instance. The UI has minimal local overhead, but it should only need to be run on a single machine in the cluster.`, }, { diff --git a/node/config/types.go b/node/config/types.go index 82d9f4a99..6d782fba4 100644 --- a/node/config/types.go +++ b/node/config/types.go @@ -94,7 +94,7 @@ type JournalConfig struct { } type CurioSubsystemsConfig struct { - // EnableWindowPost enables window post to be executed on this lotus-provider instance. Each machine in the cluster + // EnableWindowPost enables window post to be executed on this curio instance. Each machine in the cluster // with WindowPoSt enabled will also participate in the window post scheduler. It is possible to have multiple // machines with WindowPoSt enabled which will provide redundancy, and in case of multiple partitions per deadline, // will allow for parallel processing of partitions. @@ -105,7 +105,7 @@ type CurioSubsystemsConfig struct { EnableWindowPost bool WindowPostMaxTasks int - // EnableWinningPost enables winning post to be executed on this lotus-provider instance. + // EnableWinningPost enables winning post to be executed on this curio instance. // Each machine in the cluster with WinningPoSt enabled will also participate in the winning post scheduler. // It is possible to mix machines with WindowPoSt and WinningPoSt enabled, for details see the EnableWindowPost // documentation. @@ -166,7 +166,7 @@ type CurioSubsystemsConfig struct { FinalizeMaxTasks int // EnableSendPrecommitMsg enables the sending of precommit messages to the chain - // from this lotus-provider instance. + // from this curio instance. // This runs after SDRTrees and uses the output CommD / CommR (roots of TreeD / TreeR) for the message EnableSendPrecommitMsg bool @@ -185,10 +185,10 @@ type CurioSubsystemsConfig struct { PoRepProofMaxTasks int // EnableSendCommitMsg enables the sending of commit messages to the chain - // from this lotus-provider instance. + // from this curio instance. EnableSendCommitMsg bool - // EnableMoveStorage enables the move-into-long-term-storage task to run on this lotus-provider instance. + // EnableMoveStorage enables the move-into-long-term-storage task to run on this curio instance. // This tasks should only be enabled on nodes with long-term storage. // // The MoveStorage task is the last task in the sealing pipeline. It moves the sealed sector data from the @@ -219,7 +219,7 @@ type CurioSubsystemsConfig struct { // a machine which handles ParkPiece tasks. BoostAdapters []string - // EnableWebGui enables the web GUI on this lotus-provider instance. The UI has minimal local overhead, but it should + // EnableWebGui enables the web GUI on this curio instance. The UI has minimal local overhead, but it should // only need to be run on a single machine in the cluster. EnableWebGui bool From 7bd21bc675bd1ef46b2fb07e01b87196a77c6740 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 4 Apr 2024 07:35:54 -0700 Subject: [PATCH 322/394] feat: ipfs: remove IPFS client backend (#11661) This was used to run an internal IPFS node... --- blockstore/ipfs.go | 154 --------------------- documentation/en/default-lotus-config.toml | 16 --- go.mod | 5 - go.sum | 10 -- node/builder_chain.go | 8 -- node/config/doc_gen.go | 24 ---- node/config/types.go | 4 - node/modules/ipfs.go | 50 ------- node/repo/repo_test.go | 4 +- 9 files changed, 2 insertions(+), 273 deletions(-) delete mode 100644 blockstore/ipfs.go delete mode 100644 node/modules/ipfs.go diff --git a/blockstore/ipfs.go b/blockstore/ipfs.go deleted file mode 100644 index f0606519f..000000000 --- a/blockstore/ipfs.go +++ /dev/null @@ -1,154 +0,0 @@ -package blockstore - -import ( - "bytes" - "context" - "io" - - "github.com/ipfs/boxo/path" - blocks "github.com/ipfs/go-block-format" - "github.com/ipfs/go-cid" - "github.com/multiformats/go-multiaddr" - "github.com/multiformats/go-multihash" - "golang.org/x/xerrors" - - rpc "github.com/filecoin-project/kubo-api-client" - iface "github.com/filecoin-project/kubo-api-client/coreiface" - "github.com/filecoin-project/kubo-api-client/coreiface/options" -) - -type IPFSBlockstore struct { - ctx context.Context - api, offlineAPI iface.CoreAPI -} - -var _ BasicBlockstore = (*IPFSBlockstore)(nil) - -func NewLocalIPFSBlockstore(ctx context.Context, onlineMode bool) (Blockstore, error) { - localApi, err := rpc.NewLocalApi() - if err != nil { - return nil, xerrors.Errorf("getting local ipfs api: %w", err) - } - api, err := localApi.WithOptions(options.Api.Offline(!onlineMode)) - if err != nil { - return nil, xerrors.Errorf("setting offline mode: %s", err) - } - - offlineAPI := api - if onlineMode { - offlineAPI, err = localApi.WithOptions(options.Api.Offline(true)) - if err != nil { - return nil, xerrors.Errorf("applying offline mode: %s", err) - } - } - - bs := &IPFSBlockstore{ - ctx: ctx, - api: api, - offlineAPI: offlineAPI, - } - - return Adapt(bs), nil -} - -func NewRemoteIPFSBlockstore(ctx context.Context, maddr multiaddr.Multiaddr, onlineMode bool) (Blockstore, error) { - httpApi, err := rpc.NewApi(maddr) - if err != nil { - return nil, xerrors.Errorf("setting remote ipfs api: %w", err) - } - api, err := httpApi.WithOptions(options.Api.Offline(!onlineMode)) - if err != nil { - return nil, xerrors.Errorf("applying offline mode: %s", err) - } - - offlineAPI := api - if onlineMode { - offlineAPI, err = httpApi.WithOptions(options.Api.Offline(true)) - if err != nil { - return nil, xerrors.Errorf("applying offline mode: %s", err) - } - } - - bs := &IPFSBlockstore{ - ctx: ctx, - api: api, - offlineAPI: offlineAPI, - } - - return Adapt(bs), nil -} - -func (i *IPFSBlockstore) DeleteBlock(ctx context.Context, cid cid.Cid) error { - return xerrors.Errorf("not supported") -} - -func (i *IPFSBlockstore) Has(ctx context.Context, cid cid.Cid) (bool, error) { - _, err := i.offlineAPI.Block().Stat(ctx, path.FromCid(cid)) - if err != nil { - // The underlying client is running in Offline mode. - // Stat() will fail with an err if the block isn't in the - // blockstore. If that's the case, return false without - // an error since that's the original intention of this method. - if err.Error() == "blockservice: key not found" { - return false, nil - } - return false, xerrors.Errorf("getting ipfs block: %w", err) - } - - return true, nil -} - -func (i *IPFSBlockstore) Get(ctx context.Context, cid cid.Cid) (blocks.Block, error) { - rd, err := i.api.Block().Get(ctx, path.FromCid(cid)) - if err != nil { - return nil, xerrors.Errorf("getting ipfs block: %w", err) - } - - data, err := io.ReadAll(rd) - if err != nil { - return nil, err - } - - return blocks.NewBlockWithCid(data, cid) -} - -func (i *IPFSBlockstore) GetSize(ctx context.Context, cid cid.Cid) (int, error) { - st, err := i.api.Block().Stat(ctx, path.FromCid(cid)) - if err != nil { - return 0, xerrors.Errorf("getting ipfs block: %w", err) - } - - return st.Size(), nil -} - -func (i *IPFSBlockstore) Put(ctx context.Context, block blocks.Block) error { - mhd, err := multihash.Decode(block.Cid().Hash()) - if err != nil { - return err - } - - _, err = i.api.Block().Put(ctx, bytes.NewReader(block.RawData()), - options.Block.Hash(mhd.Code, mhd.Length), - options.Block.Format(multihash.Codes[block.Cid().Type()])) - return err -} - -func (i *IPFSBlockstore) PutMany(ctx context.Context, blocks []blocks.Block) error { - // TODO: could be done in parallel - - for _, block := range blocks { - if err := i.Put(ctx, block); err != nil { - return err - } - } - - return nil -} - -func (i *IPFSBlockstore) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { - return nil, xerrors.Errorf("not supported") -} - -func (i *IPFSBlockstore) HashOnRead(enabled bool) { - return // TODO: We could technically support this, but.. -} diff --git a/documentation/en/default-lotus-config.toml b/documentation/en/default-lotus-config.toml index 85ce178ce..8d3c6a427 100644 --- a/documentation/en/default-lotus-config.toml +++ b/documentation/en/default-lotus-config.toml @@ -129,22 +129,6 @@ [Client] - # type: bool - # env var: LOTUS_CLIENT_USEIPFS - #UseIpfs = false - - # type: bool - # env var: LOTUS_CLIENT_IPFSONLINEMODE - #IpfsOnlineMode = false - - # type: string - # env var: LOTUS_CLIENT_IPFSMADDR - #IpfsMAddr = "" - - # type: bool - # env var: LOTUS_CLIENT_IPFSUSEFORRETRIEVAL - #IpfsUseForRetrieval = false - # The maximum number of simultaneous data transfers between the client # and storage providers for storage deals # diff --git a/go.mod b/go.mod index 6fe41e67f..bb1b33aef 100644 --- a/go.mod +++ b/go.mod @@ -53,7 +53,6 @@ require ( github.com/filecoin-project/go-statemachine v1.0.3 github.com/filecoin-project/go-statestore v0.2.0 github.com/filecoin-project/go-storedcounter v0.1.0 - github.com/filecoin-project/kubo-api-client v0.27.0 github.com/filecoin-project/pubsub v1.0.0 github.com/filecoin-project/specs-actors v0.9.15 github.com/filecoin-project/specs-actors/v2 v2.3.6 @@ -184,7 +183,6 @@ require ( github.com/benbjohnson/clock v1.3.5 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bep/debounce v1.2.1 // indirect - github.com/blang/semver/v4 v4.0.0 // indirect github.com/cespare/xxhash v1.1.0 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/cilium/ebpf v0.9.1 // indirect @@ -233,7 +231,6 @@ require ( github.com/ipfs/go-bitfield v1.1.0 // indirect github.com/ipfs/go-blockservice v0.5.1 // indirect github.com/ipfs/go-ipfs-blockstore v1.3.0 // indirect - github.com/ipfs/go-ipfs-cmds v0.10.0 // indirect github.com/ipfs/go-ipfs-delay v0.0.1 // indirect github.com/ipfs/go-ipfs-ds-help v1.1.0 // indirect github.com/ipfs/go-ipfs-exchange-interface v0.2.0 // indirect @@ -302,7 +299,6 @@ require ( github.com/quic-go/quic-go v0.42.0 // indirect github.com/quic-go/webtransport-go v0.6.0 // indirect github.com/rivo/uniseg v0.2.0 // indirect - github.com/rs/cors v1.7.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/shirou/gopsutil v2.18.12+incompatible // indirect github.com/sirupsen/logrus v1.9.2 // indirect @@ -311,7 +307,6 @@ require ( github.com/twmb/murmur3 v1.1.6 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasttemplate v1.0.1 // indirect - github.com/whyrusleeping/base32 v0.0.0-20170828182744-c30ac30633cc // indirect github.com/whyrusleeping/cbor v0.0.0-20171005072247-63513f603b11 // indirect github.com/whyrusleeping/chunker v0.0.0-20181014151217-fe64bd25879f // indirect github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1 // indirect diff --git a/go.sum b/go.sum index 228c83a78..0107afce8 100644 --- a/go.sum +++ b/go.sum @@ -122,8 +122,6 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY= github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= -github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= github.com/btcsuite/btcd v0.0.0-20190213025234-306aecffea32/go.mod h1:DrZx5ec/dmnfpw9KyYoQyYo7d0KEvTkk/5M/vbZjAr8= github.com/btcsuite/btcd v0.0.0-20190523000118-16327141da8c/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI= @@ -363,8 +361,6 @@ github.com/filecoin-project/go-statestore v0.2.0 h1:cRRO0aPLrxKQCZ2UOQbzFGn4WDNd github.com/filecoin-project/go-statestore v0.2.0/go.mod h1:8sjBYbS35HwPzct7iT4lIXjLlYyPor80aU7t7a/Kspo= github.com/filecoin-project/go-storedcounter v0.1.0 h1:Mui6wSUBC+cQGHbDUBcO7rfh5zQkWJM/CpAZa/uOuus= github.com/filecoin-project/go-storedcounter v0.1.0/go.mod h1:4ceukaXi4vFURIoxYMfKzaRF5Xv/Pinh2oTnoxpv+z8= -github.com/filecoin-project/kubo-api-client v0.27.0 h1:rQNbReJCCQ8L107VIQR0qjAlEqdDQRYOhDKYcKGcnPI= -github.com/filecoin-project/kubo-api-client v0.27.0/go.mod h1:1+geFlaV8oJRJ4IlVTqL3QC3T1f5N0aGSptErrtcMQs= github.com/filecoin-project/pubsub v1.0.0 h1:ZTmT27U07e54qV1mMiQo4HDr0buo8I1LDHBYLXlsNXM= github.com/filecoin-project/pubsub v1.0.0/go.mod h1:GkpB33CcUtUNrLPhJgfdy4FDx4OMNR9k+46DHx/Lqrg= github.com/filecoin-project/specs-actors v0.9.13/go.mod h1:TS1AW/7LbG+615j4NsjMK1qlpAwaFsG9w0V2tg2gSao= @@ -748,8 +744,6 @@ github.com/ipfs/go-ipfs-blocksutil v0.0.1/go.mod h1:Yq4M86uIOmxmGPUHv/uI7uKqZNtL github.com/ipfs/go-ipfs-chunker v0.0.1/go.mod h1:tWewYK0we3+rMbOh7pPFGDyypCtvGcBFymgY4rSDLAw= github.com/ipfs/go-ipfs-chunker v0.0.5 h1:ojCf7HV/m+uS2vhUGWcogIIxiO5ubl5O57Q7NapWLY8= github.com/ipfs/go-ipfs-chunker v0.0.5/go.mod h1:jhgdF8vxRHycr00k13FM8Y0E+6BoalYeobXmUyTreP8= -github.com/ipfs/go-ipfs-cmds v0.10.0 h1:ZB4+RgYaH4UARfJY0uLKl5UXgApqnRjKbuCiJVcErYk= -github.com/ipfs/go-ipfs-cmds v0.10.0/go.mod h1:sX5d7jkCft9XLPnkgEfXY0z2UBOB5g6fh/obBS0enJE= github.com/ipfs/go-ipfs-delay v0.0.0-20181109222059-70721b86a9a8/go.mod h1:8SP1YXK1M1kXuc4KJZINY3TQQ03J2rwBG9QfXmbRPrw= github.com/ipfs/go-ipfs-delay v0.0.1 h1:r/UXYyRcddO6thwOnhiznIAiSvxMECGgtv35Xs1IeRQ= github.com/ipfs/go-ipfs-delay v0.0.1/go.mod h1:8SP1YXK1M1kXuc4KJZINY3TQQ03J2rwBG9QfXmbRPrw= @@ -1510,8 +1504,6 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= -github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= -github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/zerolog v1.21.0/go.mod h1:ZPhntP/xmq1nnND05hhpAh2QMhSsA4UN3MGZ6O2J3hM= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= @@ -1664,8 +1656,6 @@ github.com/weaveworks/common v0.0.0-20230531151736-e2613bee6b73 h1:CMM9+/AgM77va github.com/weaveworks/common v0.0.0-20230531151736-e2613bee6b73/go.mod h1:rgbeLfJUtEr+G74cwFPR1k/4N0kDeaeSv/qhUNE4hm8= github.com/weaveworks/promrus v1.2.0 h1:jOLf6pe6/vss4qGHjXmGz4oDJQA+AOCqEL3FvvZGz7M= github.com/weaveworks/promrus v1.2.0/go.mod h1:SaE82+OJ91yqjrE1rsvBWVzNZKcHYFtMUyS1+Ogs/KA= -github.com/whyrusleeping/base32 v0.0.0-20170828182744-c30ac30633cc h1:BCPnHtcboadS0DvysUuJXZ4lWVv5Bh5i7+tbIyi+ck4= -github.com/whyrusleeping/base32 v0.0.0-20170828182744-c30ac30633cc/go.mod h1:r45hJU7yEoA81k6MWNhpMj/kms0n14dkzkxYHoB96UM= github.com/whyrusleeping/bencher v0.0.0-20190829221104-bb6607aa8bba h1:X4n8JG2e2biEZZXdBKt9HX7DN3bYGFUqljqqy0DqgnY= github.com/whyrusleeping/bencher v0.0.0-20190829221104-bb6607aa8bba/go.mod h1:CHQnYnQUEPydYCwuy8lmTHfGmdw9TKrhWV0xLx8l0oM= github.com/whyrusleeping/cbor v0.0.0-20171005072247-63513f603b11 h1:5HZfQkwe0mIfyDmc1Em5GqlNRzcdtlv4HTNmdpt7XH0= diff --git a/node/builder_chain.go b/node/builder_chain.go index 73c9cfb18..0b40e4530 100644 --- a/node/builder_chain.go +++ b/node/builder_chain.go @@ -183,7 +183,6 @@ func ConfigFullNode(c interface{}) Option { enableLibp2pNode := true // always enable libp2p for full nodes - ipfsMaddr := cfg.Client.IpfsMAddr return Options( ConfigCommon(&cfg.Common, enableLibp2pNode), @@ -229,13 +228,6 @@ func ConfigFullNode(c interface{}) Option { Override(new(dtypes.ClientBlockstore), modules.ClientBlockstore), - If(cfg.Client.UseIpfs, - Override(new(dtypes.ClientBlockstore), modules.IpfsClientBlockstore(ipfsMaddr, cfg.Client.IpfsOnlineMode)), - Override(new(storagemarket.BlockstoreAccessor), modules.IpfsStorageBlockstoreAccessor), - If(cfg.Client.IpfsUseForRetrieval, - Override(new(retrievalmarket.BlockstoreAccessor), modules.IpfsRetrievalBlockstoreAccessor), - ), - ), Override(new(dtypes.Graphsync), modules.Graphsync(cfg.Client.SimultaneousTransfersForStorage, cfg.Client.SimultaneousTransfersForRetrieval)), Override(new(retrievalmarket.RetrievalClient), modules.RetrievalClient(cfg.Client.OffChainRetrieval)), diff --git a/node/config/doc_gen.go b/node/config/doc_gen.go index 9a4b91c4a..e9bfb6fba 100644 --- a/node/config/doc_gen.go +++ b/node/config/doc_gen.go @@ -86,30 +86,6 @@ your node if metadata log is disabled`, }, }, "Client": { - { - Name: "UseIpfs", - Type: "bool", - - Comment: ``, - }, - { - Name: "IpfsOnlineMode", - Type: "bool", - - Comment: ``, - }, - { - Name: "IpfsMAddr", - Type: "string", - - Comment: ``, - }, - { - Name: "IpfsUseForRetrieval", - Type: "bool", - - Comment: ``, - }, { Name: "SimultaneousTransfersForStorage", Type: "uint64", diff --git a/node/config/types.go b/node/config/types.go index 6d782fba4..d133fcadd 100644 --- a/node/config/types.go +++ b/node/config/types.go @@ -940,10 +940,6 @@ type Splitstore struct { // // Full Node type Client struct { - UseIpfs bool - IpfsOnlineMode bool - IpfsMAddr string - IpfsUseForRetrieval bool // The maximum number of simultaneous data transfers between the client // and storage providers for storage deals SimultaneousTransfersForStorage uint64 diff --git a/node/modules/ipfs.go b/node/modules/ipfs.go deleted file mode 100644 index cb9deb6fd..000000000 --- a/node/modules/ipfs.go +++ /dev/null @@ -1,50 +0,0 @@ -package modules - -import ( - bstore "github.com/ipfs/boxo/blockstore" - "github.com/multiformats/go-multiaddr" - "go.uber.org/fx" - "golang.org/x/xerrors" - - "github.com/filecoin-project/go-fil-markets/retrievalmarket" - "github.com/filecoin-project/go-fil-markets/storagemarket" - - "github.com/filecoin-project/lotus/blockstore" - "github.com/filecoin-project/lotus/markets/retrievaladapter" - "github.com/filecoin-project/lotus/markets/storageadapter" - "github.com/filecoin-project/lotus/node/modules/dtypes" - "github.com/filecoin-project/lotus/node/modules/helpers" -) - -func IpfsStorageBlockstoreAccessor(ipfsBlockstore dtypes.ClientBlockstore) storagemarket.BlockstoreAccessor { - return storageadapter.NewFixedBlockstoreAccessor(bstore.Blockstore(ipfsBlockstore)) -} - -func IpfsRetrievalBlockstoreAccessor(ipfsBlockstore dtypes.ClientBlockstore) retrievalmarket.BlockstoreAccessor { - return retrievaladapter.NewFixedBlockstoreAccessor(bstore.Blockstore(ipfsBlockstore)) -} - -// IpfsClientBlockstore returns a ClientBlockstore implementation backed by an IPFS node. -// If ipfsMaddr is empty, a local IPFS node is assumed considering IPFS_PATH configuration. -// If ipfsMaddr is not empty, it will connect to the remote IPFS node with the provided multiaddress. -// The flag useForRetrieval indicates if the IPFS node will also be used for storing retrieving deals. -func IpfsClientBlockstore(ipfsMaddr string, onlineMode bool) func(helpers.MetricsCtx, fx.Lifecycle, dtypes.ClientImportMgr) (dtypes.ClientBlockstore, error) { - return func(mctx helpers.MetricsCtx, lc fx.Lifecycle, localStore dtypes.ClientImportMgr) (dtypes.ClientBlockstore, error) { - var err error - var ipfsbs blockstore.BasicBlockstore - if ipfsMaddr != "" { - var ma multiaddr.Multiaddr - ma, err = multiaddr.NewMultiaddr(ipfsMaddr) - if err != nil { - return nil, xerrors.Errorf("parsing ipfs multiaddr: %w", err) - } - ipfsbs, err = blockstore.NewRemoteIPFSBlockstore(helpers.LifecycleCtx(mctx, lc), ma, onlineMode) - } else { - ipfsbs, err = blockstore.NewLocalIPFSBlockstore(helpers.LifecycleCtx(mctx, lc), onlineMode) - } - if err != nil { - return nil, xerrors.Errorf("constructing ipfs blockstore: %w", err) - } - return blockstore.WrapIDStore(ipfsbs), nil - } -} diff --git a/node/repo/repo_test.go b/node/repo/repo_test.go index c78afa9db..6390fe7d2 100644 --- a/node/repo/repo_test.go +++ b/node/repo/repo_test.go @@ -56,7 +56,7 @@ func basicTest(t *testing.T, repo Repo) { // mutate config and persist back to repo err = lrepo.SetConfig(func(c interface{}) { cfg := c.(*config.FullNode) - cfg.Client.IpfsMAddr = "duvall" + cfg.FaultReporter.ConsensusFaultReporterAddress = "duvall" }) assert.NoError(t, err) @@ -64,7 +64,7 @@ func basicTest(t *testing.T, repo Repo) { c2, err := lrepo.Config() require.NoError(t, err) cfg2 := c2.(*config.FullNode) - require.Equal(t, cfg2.Client.IpfsMAddr, "duvall") + require.Equal(t, cfg2.FaultReporter.ConsensusFaultReporterAddress, "duvall") err = lrepo.Close() assert.NoError(t, err, "should be able to close") From a99d8c87915dbe6631e90310428beed6b4741e67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 4 Apr 2024 17:25:06 +0200 Subject: [PATCH 323/394] feat: curio: Cleanup files after failed TreeDRC (#11827) --- curiosrc/ffi/sdr_funcs.go | 30 ++++++++++++++++++++++++- curiosrc/proof/treed_build.go | 9 ++++++++ storage/sealer/proofpaths/cachefiles.go | 24 ++++++++++++++++++++ 3 files changed, 62 insertions(+), 1 deletion(-) diff --git a/curiosrc/ffi/sdr_funcs.go b/curiosrc/ffi/sdr_funcs.go index 74a498219..dba10951f 100644 --- a/curiosrc/ffi/sdr_funcs.go +++ b/curiosrc/ffi/sdr_funcs.go @@ -161,7 +161,7 @@ func (sb *SealCalls) GenerateSDR(ctx context.Context, taskID harmonytask.TaskID, return nil } -func (sb *SealCalls) TreeDRC(ctx context.Context, task *harmonytask.TaskID, sector storiface.SectorRef, unsealed cid.Cid, size abi.PaddedPieceSize, data io.Reader, unpaddedData bool) (cid.Cid, cid.Cid, error) { +func (sb *SealCalls) TreeDRC(ctx context.Context, task *harmonytask.TaskID, sector storiface.SectorRef, unsealed cid.Cid, size abi.PaddedPieceSize, data io.Reader, unpaddedData bool) (scid cid.Cid, ucid cid.Cid, err error) { p1o, err := sb.makePhase1Out(unsealed, sector.ProofType) if err != nil { return cid.Undef, cid.Undef, xerrors.Errorf("make phase1 output: %w", err) @@ -173,6 +173,15 @@ func (sb *SealCalls) TreeDRC(ctx context.Context, task *harmonytask.TaskID, sect } defer releaseSector() + defer func() { + if err != nil { + clerr := removeDRCTrees(paths.Cache) + if clerr != nil { + log.Errorw("removing tree files after TreeDRC error", "error", clerr, "exec-error", err, "sector", sector, "cache", paths.Cache) + } + } + }() + treeDUnsealed, err := proof.BuildTreeD(data, unpaddedData, filepath.Join(paths.Cache, proofpaths.TreeDName), size) if err != nil { return cid.Undef, cid.Undef, xerrors.Errorf("building tree-d: %w", err) @@ -237,6 +246,25 @@ func (sb *SealCalls) TreeDRC(ctx context.Context, task *harmonytask.TaskID, sect return sl, uns, nil } +func removeDRCTrees(cache string) error { + // list files in cache + files, err := os.ReadDir(cache) + if err != nil { + return xerrors.Errorf("listing cache: %w", err) + } + + for _, file := range files { + if proofpaths.IsTreeFile(file.Name()) { + err := os.Remove(filepath.Join(cache, file.Name())) + if err != nil { + return xerrors.Errorf("removing tree file: %w", err) + } + } + } + + return nil +} + func (sb *SealCalls) GenerateSynthPoRep() { panic("todo") } diff --git a/curiosrc/proof/treed_build.go b/curiosrc/proof/treed_build.go index 59161caa9..7145c9257 100644 --- a/curiosrc/proof/treed_build.go +++ b/curiosrc/proof/treed_build.go @@ -58,6 +58,15 @@ func BuildTreeD(data io.Reader, unpaddedData bool, outPath string, size abi.Padd } defer func() { cerr := out.Close() + + if err != nil { + // remove the file, it's probably bad + rerr := os.Remove(outPath) + if rerr != nil { + err = multierror.Append(err, rerr) + } + } + if cerr != nil { err = multierror.Append(err, cerr) } diff --git a/storage/sealer/proofpaths/cachefiles.go b/storage/sealer/proofpaths/cachefiles.go index d624e0777..628ab1585 100644 --- a/storage/sealer/proofpaths/cachefiles.go +++ b/storage/sealer/proofpaths/cachefiles.go @@ -2,6 +2,7 @@ package proofpaths import ( "fmt" + "regexp" "github.com/filecoin-project/go-state-types/abi" ) @@ -9,6 +10,29 @@ import ( const dataFilePrefix = "sc-02-data-" const TreeDName = dataFilePrefix + "tree-d.dat" +const TreeRLastPrefix = dataFilePrefix + "tree-r-last-" +const TreeCPrefix = dataFilePrefix + "tree-c-" + +func IsFileTreeD(baseName string) bool { + return baseName == TreeDName +} + +func IsFileTreeRLast(baseName string) bool { + // TreeRLastPrefix.dat + reg := fmt.Sprintf(`^%s\d+\.dat$`, TreeRLastPrefix) + return regexp.MustCompile(reg).MatchString(baseName) +} + +func IsFileTreeC(baseName string) bool { + // TreeCPrefix.dat + reg := fmt.Sprintf(`^%s\d+\.dat$`, TreeCPrefix) + return regexp.MustCompile(reg).MatchString(baseName) +} + +func IsTreeFile(baseName string) bool { + return IsFileTreeD(baseName) || IsFileTreeRLast(baseName) || IsFileTreeC(baseName) +} + func LayerFileName(layer int) string { return fmt.Sprintf("%slayer-%d.dat", dataFilePrefix, layer) } From 2951d038a89dd313134aa5dbaf418628742cce84 Mon Sep 17 00:00:00 2001 From: LexLuthr Date: Mon, 1 Apr 2024 22:19:00 +0400 Subject: [PATCH 324/394] add miner fliter --- api/api_storage.go | 10 +- api/proxy_gen.go | 8 +- build/openrpc/miner.json | 91 +++++++- cmd/curio/tasks/tasks.go | 18 +- curiosrc/ffi/task_storage.go | 2 +- curiosrc/piece/task_park_piece.go | 5 +- curiosrc/web/hapi/simpleinfo.go | 8 +- documentation/en/api-v0-methods-curio.md | 12 + documentation/en/api-v0-methods-miner.md | 27 ++- .../sql/20240401-storage-segregation.sql | 1 + storage/paths/db_index.go | 205 ++++++++++++++---- storage/paths/index.go | 153 ++++++++++++- storage/paths/index_test.go | 58 +++++ storage/paths/local.go | 121 ++++++++--- storage/paths/mocks/index.go | 8 +- storage/sealer/manager.go | 32 +-- storage/sealer/sched_test.go | 2 +- storage/sealer/selector_alloc.go | 6 +- storage/sealer/selector_existing.go | 4 +- storage/sealer/selector_move.go | 5 +- storage/sealer/storiface/filetype.go | 159 ++++++++++++++ storage/sealer/storiface/index.go | 14 +- storage/sealer/storiface/storage.go | 8 + 23 files changed, 834 insertions(+), 123 deletions(-) create mode 100644 lib/harmony/harmonydb/sql/20240401-storage-segregation.sql diff --git a/api/api_storage.go b/api/api_storage.go index b24ee2af3..410fa2af1 100644 --- a/api/api_storage.go +++ b/api/api_storage.go @@ -200,11 +200,11 @@ type StorageMiner interface { // StorageBestAlloc returns list of paths where sector files of the specified type can be allocated, ordered by preference. // Paths with more weight and more % of free space are preferred. // Note: This method doesn't filter paths based on AllowTypes/DenyTypes. - StorageBestAlloc(ctx context.Context, allocate storiface.SectorFileType, ssize abi.SectorSize, pathType storiface.PathType) ([]storiface.StorageInfo, error) //perm:admin - StorageLock(ctx context.Context, sector abi.SectorID, read storiface.SectorFileType, write storiface.SectorFileType) error //perm:admin - StorageTryLock(ctx context.Context, sector abi.SectorID, read storiface.SectorFileType, write storiface.SectorFileType) (bool, error) //perm:admin - StorageList(ctx context.Context) (map[storiface.ID][]storiface.Decl, error) //perm:admin - StorageGetLocks(ctx context.Context) (storiface.SectorLocks, error) //perm:admin + StorageBestAlloc(ctx context.Context, allocate storiface.SectorFileType, ssize abi.SectorSize, pathType storiface.PathType, miner abi.ActorID) ([]storiface.StorageInfo, error) //perm:admin + StorageLock(ctx context.Context, sector abi.SectorID, read storiface.SectorFileType, write storiface.SectorFileType) error //perm:admin + StorageTryLock(ctx context.Context, sector abi.SectorID, read storiface.SectorFileType, write storiface.SectorFileType) (bool, error) //perm:admin + StorageList(ctx context.Context) (map[storiface.ID][]storiface.Decl, error) //perm:admin + StorageGetLocks(ctx context.Context) (storiface.SectorLocks, error) //perm:admin StorageLocal(ctx context.Context) (map[storiface.ID]string, error) //perm:admin StorageStat(ctx context.Context, id storiface.ID) (fsutil.FsStat, error) //perm:admin diff --git a/api/proxy_gen.go b/api/proxy_gen.go index 1cd44092e..5530c79b7 100644 --- a/api/proxy_gen.go +++ b/api/proxy_gen.go @@ -1185,7 +1185,7 @@ type StorageMinerMethods struct { StorageAuthVerify func(p0 context.Context, p1 string) ([]auth.Permission, error) `perm:"read"` - StorageBestAlloc func(p0 context.Context, p1 storiface.SectorFileType, p2 abi.SectorSize, p3 storiface.PathType) ([]storiface.StorageInfo, error) `perm:"admin"` + StorageBestAlloc func(p0 context.Context, p1 storiface.SectorFileType, p2 abi.SectorSize, p3 storiface.PathType, p4 abi.ActorID) ([]storiface.StorageInfo, error) `perm:"admin"` StorageDeclareSector func(p0 context.Context, p1 storiface.ID, p2 abi.SectorID, p3 storiface.SectorFileType, p4 bool) error `perm:"admin"` @@ -6988,14 +6988,14 @@ func (s *StorageMinerStub) StorageAuthVerify(p0 context.Context, p1 string) ([]a return *new([]auth.Permission), ErrNotSupported } -func (s *StorageMinerStruct) StorageBestAlloc(p0 context.Context, p1 storiface.SectorFileType, p2 abi.SectorSize, p3 storiface.PathType) ([]storiface.StorageInfo, error) { +func (s *StorageMinerStruct) StorageBestAlloc(p0 context.Context, p1 storiface.SectorFileType, p2 abi.SectorSize, p3 storiface.PathType, p4 abi.ActorID) ([]storiface.StorageInfo, error) { if s.Internal.StorageBestAlloc == nil { return *new([]storiface.StorageInfo), ErrNotSupported } - return s.Internal.StorageBestAlloc(p0, p1, p2, p3) + return s.Internal.StorageBestAlloc(p0, p1, p2, p3, p4) } -func (s *StorageMinerStub) StorageBestAlloc(p0 context.Context, p1 storiface.SectorFileType, p2 abi.SectorSize, p3 storiface.PathType) ([]storiface.StorageInfo, error) { +func (s *StorageMinerStub) StorageBestAlloc(p0 context.Context, p1 storiface.SectorFileType, p2 abi.SectorSize, p3 storiface.PathType, p4 abi.ActorID) ([]storiface.StorageInfo, error) { return *new([]storiface.StorageInfo), ErrNotSupported } diff --git a/build/openrpc/miner.json b/build/openrpc/miner.json index cb6141188..a06a1c72c 100644 --- a/build/openrpc/miner.json +++ b/build/openrpc/miner.json @@ -8897,11 +8897,23 @@ ], "DenyTypes": [ "string value" + ], + "AllowMiners": [ + "string value" + ], + "DenyMiners": [ + "string value" ] } ], "additionalProperties": false, "properties": { + "AllowMiners": { + "items": { + "type": "string" + }, + "type": "array" + }, "AllowTo": { "items": { "type": "string" @@ -8920,6 +8932,12 @@ "CanStore": { "type": "boolean" }, + "DenyMiners": { + "items": { + "type": "string" + }, + "type": "array" + }, "DenyTypes": { "items": { "type": "string" @@ -9078,7 +9096,7 @@ }, { "name": "Filecoin.StorageBestAlloc", - "description": "```go\nfunc (s *StorageMinerStruct) StorageBestAlloc(p0 context.Context, p1 storiface.SectorFileType, p2 abi.SectorSize, p3 storiface.PathType) ([]storiface.StorageInfo, error) {\n\tif s.Internal.StorageBestAlloc == nil {\n\t\treturn *new([]storiface.StorageInfo), ErrNotSupported\n\t}\n\treturn s.Internal.StorageBestAlloc(p0, p1, p2, p3)\n}\n```", + "description": "```go\nfunc (s *StorageMinerStruct) StorageBestAlloc(p0 context.Context, p1 storiface.SectorFileType, p2 abi.SectorSize, p3 storiface.PathType, p4 abi.ActorID) ([]storiface.StorageInfo, error) {\n\tif s.Internal.StorageBestAlloc == nil {\n\t\treturn *new([]storiface.StorageInfo), ErrNotSupported\n\t}\n\treturn s.Internal.StorageBestAlloc(p0, p1, p2, p3, p4)\n}\n```", "summary": "StorageBestAlloc returns list of paths where sector files of the specified type can be allocated, ordered by preference.\nPaths with more weight and more % of free space are preferred.\nNote: This method doesn't filter paths based on AllowTypes/DenyTypes.\n", "paramStructure": "by-position", "params": [ @@ -9130,6 +9148,23 @@ }, "required": true, "deprecated": false + }, + { + "name": "p4", + "description": "abi.ActorID", + "summary": "", + "schema": { + "title": "number", + "description": "Number is a number", + "examples": [ + 1000 + ], + "type": [ + "number" + ] + }, + "required": true, + "deprecated": false } ], "result": { @@ -9159,6 +9194,12 @@ ], "DenyTypes": [ "string value" + ], + "AllowMiners": [ + "string value" + ], + "DenyMiners": [ + "string value" ] } ] @@ -9167,6 +9208,12 @@ { "additionalProperties": false, "properties": { + "AllowMiners": { + "items": { + "type": "string" + }, + "type": "array" + }, "AllowTo": { "items": { "type": "string" @@ -9185,6 +9232,12 @@ "CanStore": { "type": "boolean" }, + "DenyMiners": { + "items": { + "type": "string" + }, + "type": "array" + }, "DenyTypes": { "items": { "type": "string" @@ -9620,6 +9673,12 @@ ], "DenyTypes": [ "string value" + ], + "AllowMiners": [ + "string value" + ], + "DenyMiners": [ + "string value" ] } ] @@ -9628,6 +9687,12 @@ { "additionalProperties": false, "properties": { + "AllowMiners": { + "items": { + "type": "string" + }, + "type": "array" + }, "AllowTypes": { "items": { "type": "string" @@ -9646,6 +9711,12 @@ "CanStore": { "type": "boolean" }, + "DenyMiners": { + "items": { + "type": "string" + }, + "type": "array" + }, "DenyTypes": { "items": { "type": "string" @@ -9833,11 +9904,23 @@ ], "DenyTypes": [ "string value" + ], + "AllowMiners": [ + "string value" + ], + "DenyMiners": [ + "string value" ] } ], "additionalProperties": false, "properties": { + "AllowMiners": { + "items": { + "type": "string" + }, + "type": "array" + }, "AllowTo": { "items": { "type": "string" @@ -9856,6 +9939,12 @@ "CanStore": { "type": "boolean" }, + "DenyMiners": { + "items": { + "type": "string" + }, + "type": "array" + }, "DenyTypes": { "items": { "type": "string" diff --git a/cmd/curio/tasks/tasks.go b/cmd/curio/tasks/tasks.go index a35d3d2a5..9e36a93c7 100644 --- a/cmd/curio/tasks/tasks.go +++ b/cmd/curio/tasks/tasks.go @@ -8,6 +8,8 @@ import ( "github.com/samber/lo" "golang.org/x/xerrors" + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/lotus/cmd/curio/deps" curio "github.com/filecoin-project/lotus/curiosrc" "github.com/filecoin-project/lotus/curiosrc/chainsched" @@ -37,6 +39,20 @@ func StartTasks(ctx context.Context, dependencies *deps.Deps) (*harmonytask.Task si := dependencies.Si var activeTasks []harmonytask.TaskInterface + // Get all miner address from config + var miners []address.Address + for _, ad := range cfg.Addresses { + ad := ad + for _, m := range ad.MinerAddresses { + m := m + maddr, err := address.NewFromString(m) + if err != nil { + return nil, xerrors.Errorf("failed to parse the miner address: %w", err) + } + miners = append(miners, maddr) + } + } + sender, sendTask := message.NewSender(full, full, db) activeTasks = append(activeTasks, sendTask) @@ -76,7 +92,7 @@ func StartTasks(ctx context.Context, dependencies *deps.Deps) (*harmonytask.Task { // Piece handling if cfg.Subsystems.EnableParkPiece { - parkPieceTask := piece.NewParkPieceTask(db, must.One(slrLazy.Val()), cfg.Subsystems.ParkPieceMaxTasks) + parkPieceTask := piece.NewParkPieceTask(db, must.One(slrLazy.Val()), cfg.Subsystems.ParkPieceMaxTasks, miners) cleanupPieceTask := piece.NewCleanupPieceTask(db, must.One(slrLazy.Val()), 0) activeTasks = append(activeTasks, parkPieceTask, cleanupPieceTask) } diff --git a/curiosrc/ffi/task_storage.go b/curiosrc/ffi/task_storage.go index 30dd90417..18973164d 100644 --- a/curiosrc/ffi/task_storage.go +++ b/curiosrc/ffi/task_storage.go @@ -68,7 +68,7 @@ func (sb *SealCalls) Storage(taskToSectorRef func(taskID harmonytask.TaskID) (Se func (t *TaskStorage) HasCapacity() bool { ctx := context.Background() - paths, err := t.sc.sectors.sindex.StorageBestAlloc(ctx, t.alloc, t.ssize, t.pathType) + paths, err := t.sc.sectors.sindex.StorageBestAlloc(ctx, t.alloc, t.ssize, t.pathType, abi.ActorID(0)) if err != nil { log.Errorf("finding best alloc in HasCapacity: %+v", err) return false diff --git a/curiosrc/piece/task_park_piece.go b/curiosrc/piece/task_park_piece.go index 2085637f6..22823057e 100644 --- a/curiosrc/piece/task_park_piece.go +++ b/curiosrc/piece/task_park_piece.go @@ -9,6 +9,8 @@ import ( logging "github.com/ipfs/go-log/v2" "golang.org/x/xerrors" + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/lotus/curiosrc/ffi" "github.com/filecoin-project/lotus/curiosrc/seal" "github.com/filecoin-project/lotus/lib/harmony/harmonydb" @@ -32,13 +34,14 @@ type ParkPieceTask struct { max int } -func NewParkPieceTask(db *harmonydb.DB, sc *ffi.SealCalls, max int) *ParkPieceTask { +func NewParkPieceTask(db *harmonydb.DB, sc *ffi.SealCalls, max int, miners []address.Address) *ParkPieceTask { pt := &ParkPieceTask{ db: db, sc: sc, max: max, } + go pt.pollPieceTasks(context.Background()) return pt } diff --git a/curiosrc/web/hapi/simpleinfo.go b/curiosrc/web/hapi/simpleinfo.go index 014f154f8..5c353372e 100644 --- a/curiosrc/web/hapi/simpleinfo.go +++ b/curiosrc/web/hapi/simpleinfo.go @@ -393,6 +393,8 @@ type machineInfo struct { FSAvailable int64 Reserved int64 Used int64 + AllowMiners string + DenyMiners string LastHeartbeat time.Time HeartbeatErr *string @@ -458,7 +460,7 @@ func (a *app) clusterNodeInfo(ctx context.Context, id int64) (*machineInfo, erro } // query storage info - rows2, err := a.db.Query(ctx, "SELECT storage_id, weight, max_storage, can_seal, can_store, groups, allow_to, allow_types, deny_types, capacity, available, fs_available, reserved, used, last_heartbeat, heartbeat_err FROM storage_path WHERE urls LIKE '%' || $1 || '%'", summaries[0].Info.Host) + rows2, err := a.db.Query(ctx, "SELECT storage_id, weight, max_storage, can_seal, can_store, groups, allow_to, allow_types, deny_types, capacity, available, fs_available, reserved, used, allow_miners, deny_miners, last_heartbeat, heartbeat_err FROM storage_path WHERE urls LIKE '%' || $1 || '%'", summaries[0].Info.Host) if err != nil { return nil, err } @@ -481,13 +483,15 @@ func (a *app) clusterNodeInfo(ctx context.Context, id int64) (*machineInfo, erro FSAvailable int64 Reserved int64 Used int64 + AllowMiners string + DenyMiners string LastHeartbeat time.Time HeartbeatErr *string UsedPercent float64 ReservedPercent float64 } - if err := rows2.Scan(&s.ID, &s.Weight, &s.MaxStorage, &s.CanSeal, &s.CanStore, &s.Groups, &s.AllowTo, &s.AllowTypes, &s.DenyTypes, &s.Capacity, &s.Available, &s.FSAvailable, &s.Reserved, &s.Used, &s.LastHeartbeat, &s.HeartbeatErr); err != nil { + if err := rows2.Scan(&s.ID, &s.Weight, &s.MaxStorage, &s.CanSeal, &s.CanStore, &s.Groups, &s.AllowTo, &s.AllowTypes, &s.DenyTypes, &s.Capacity, &s.Available, &s.FSAvailable, &s.Reserved, &s.Used, &s.AllowMiners, &s.DenyMiners, &s.LastHeartbeat, &s.HeartbeatErr); err != nil { return nil, err } diff --git a/documentation/en/api-v0-methods-curio.md b/documentation/en/api-v0-methods-curio.md index 84cc20595..4cca31b34 100644 --- a/documentation/en/api-v0-methods-curio.md +++ b/documentation/en/api-v0-methods-curio.md @@ -179,6 +179,12 @@ Response: ], "DenyTypes": [ "string value" + ], + "AllowMiners": [ + "string value" + ], + "DenyMiners": [ + "string value" ] } ] @@ -218,6 +224,12 @@ Response: ], "DenyTypes": [ "string value" + ], + "AllowMiners": [ + "string value" + ], + "DenyMiners": [ + "string value" ] } ``` diff --git a/documentation/en/api-v0-methods-miner.md b/documentation/en/api-v0-methods-miner.md index dd2f511db..802cd3ce5 100644 --- a/documentation/en/api-v0-methods-miner.md +++ b/documentation/en/api-v0-methods-miner.md @@ -3735,6 +3735,12 @@ Inputs: ], "DenyTypes": [ "string value" + ], + "AllowMiners": [ + "string value" + ], + "DenyMiners": [ + "string value" ] }, { @@ -3782,7 +3788,8 @@ Inputs: [ 1, 34359738368, - "sealing" + "sealing", + 1000 ] ``` @@ -3809,6 +3816,12 @@ Response: ], "DenyTypes": [ "string value" + ], + "AllowMiners": [ + "string value" + ], + "DenyMiners": [ + "string value" ] } ] @@ -3928,6 +3941,12 @@ Response: ], "DenyTypes": [ "string value" + ], + "AllowMiners": [ + "string value" + ], + "DenyMiners": [ + "string value" ] } ] @@ -4004,6 +4023,12 @@ Response: ], "DenyTypes": [ "string value" + ], + "AllowMiners": [ + "string value" + ], + "DenyMiners": [ + "string value" ] } ``` diff --git a/lib/harmony/harmonydb/sql/20240401-storage-segregation.sql b/lib/harmony/harmonydb/sql/20240401-storage-segregation.sql new file mode 100644 index 000000000..bcc29e68c --- /dev/null +++ b/lib/harmony/harmonydb/sql/20240401-storage-segregation.sql @@ -0,0 +1 @@ +ALTER TABLE storage_path ADD COLUMN IF NOT EXISTS allow_miners varchar, ADD COLUMN IF NOT EXISTS deny_miners varchar; -- comma separated list of miner addresses \ No newline at end of file diff --git a/storage/paths/db_index.go b/storage/paths/db_index.go index 063415712..0ca4cca80 100644 --- a/storage/paths/db_index.go +++ b/storage/paths/db_index.go @@ -15,6 +15,7 @@ import ( "go.opencensus.io/tag" "golang.org/x/xerrors" + "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/lotus/journal/alerting" @@ -200,7 +201,7 @@ func (dbi *DBIndex) StorageAttach(ctx context.Context, si storiface.StorageInfo, currUrls = union(currUrls, si.URLs) _, err = tx.Exec( - "UPDATE storage_path set urls=$1, weight=$2, max_storage=$3, can_seal=$4, can_store=$5, groups=$6, allow_to=$7, allow_types=$8, deny_types=$9, last_heartbeat=NOW() WHERE storage_id=$10", + "UPDATE storage_path set urls=$1, weight=$2, max_storage=$3, can_seal=$4, can_store=$5, groups=$6, allow_to=$7, allow_types=$8, deny_types=$9, allow_miners=$10, deny_miners=$11, last_heartbeat=NOW() WHERE storage_id=$12", strings.Join(currUrls, ","), si.Weight, si.MaxStorage, @@ -210,6 +211,8 @@ func (dbi *DBIndex) StorageAttach(ctx context.Context, si storiface.StorageInfo, strings.Join(si.AllowTo, ","), strings.Join(si.AllowTypes, ","), strings.Join(si.DenyTypes, ","), + strings.Join(si.AllowMiners, ","), + strings.Join(si.DenyMiners, ","), si.ID) if err != nil { return false, xerrors.Errorf("storage attach UPDATE fails: %v", err) @@ -221,7 +224,7 @@ func (dbi *DBIndex) StorageAttach(ctx context.Context, si storiface.StorageInfo, // Insert storage id _, err = tx.Exec( "INSERT INTO storage_path "+ - "Values($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, NOW())", + "Values($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, NOW(), $16, $17)", si.ID, strings.Join(si.URLs, ","), si.Weight, @@ -236,7 +239,9 @@ func (dbi *DBIndex) StorageAttach(ctx context.Context, si storiface.StorageInfo, st.Available, st.FSAvailable, st.Reserved, - st.Used) + st.Used, + strings.Join(si.AllowMiners, ","), + strings.Join(si.DenyMiners, ",")) if err != nil { return false, xerrors.Errorf("StorageAttach insert fails: %v", err) } @@ -532,14 +537,16 @@ func (dbi *DBIndex) StorageFindSector(ctx context.Context, s abi.SectorID, ft st // 7. Storage path is part of the groups which are allowed from the storage paths which already hold the sector var rows []struct { - StorageId string - Urls string - Weight uint64 - CanSeal bool - CanStore bool - Groups string - AllowTypes string - DenyTypes string + StorageId string + Urls string + Weight uint64 + CanSeal bool + CanStore bool + Groups string + AllowTypes string + DenyTypes string + AllowMiners string + DenyMiners string } err = dbi.harmonyDB.Select(ctx, &rows, `SELECT storage_id, @@ -549,7 +556,9 @@ func (dbi *DBIndex) StorageFindSector(ctx context.Context, s abi.SectorID, ft st can_store, groups, allow_types, - deny_types + deny_types, + allow_miners, + deny_miners FROM storage_path WHERE can_seal=true and available >= $1 @@ -570,6 +579,53 @@ func (dbi *DBIndex) StorageFindSector(ctx context.Context, s abi.SectorID, ft st continue } + if len(row.AllowMiners) > 0 { + found := false + allowMiners := splitString(row.AllowMiners) + for _, m := range allowMiners { + m := m + maddr, err := address.NewFromString(m) + if err != nil { + return nil, xerrors.Errorf("parsing miner address: %w", err) + } + mid, err := address.IDFromAddress(maddr) + if err != nil { + return nil, xerrors.Errorf("parsing miner ID: %w", err) + } + if abi.ActorID(mid) == s.Miner { + found = true + break + } + } + if !found { + log.Debugf("not selecting on %s, not allowed by allow miners filters", row.StorageId) + continue + } + } + if len(row.DenyMiners) > 0 { + found := false + denyMiners := splitString(row.DenyMiners) + for _, m := range denyMiners { + m := m + maddr, err := address.NewFromString(m) + if err != nil { + return nil, xerrors.Errorf("parsing miner address: %w", err) + } + mid, err := address.IDFromAddress(maddr) + if err != nil { + return nil, xerrors.Errorf("parsing miner ID: %w", err) + } + if abi.ActorID(mid) == s.Miner { + found = true + break + } + } + if found { + log.Debugf("not selecting on %s, not allowed by deny miners filters", row.StorageId) + continue + } + } + if allowList != nil { groups := splitString(row.Groups) allow := false @@ -618,19 +674,21 @@ func (dbi *DBIndex) StorageFindSector(ctx context.Context, s abi.SectorID, ft st func (dbi *DBIndex) StorageInfo(ctx context.Context, id storiface.ID) (storiface.StorageInfo, error) { var qResults []struct { - Urls string - Weight uint64 - MaxStorage uint64 - CanSeal bool - CanStore bool - Groups string - AllowTo string - AllowTypes string - DenyTypes string + Urls string + Weight uint64 + MaxStorage uint64 + CanSeal bool + CanStore bool + Groups string + AllowTo string + AllowTypes string + DenyTypes string + AllowMiners string + DenyMiners string } err := dbi.harmonyDB.Select(ctx, &qResults, - "SELECT urls, weight, max_storage, can_seal, can_store, groups, allow_to, allow_types, deny_types "+ + "SELECT urls, weight, max_storage, can_seal, can_store, groups, allow_to, allow_types, deny_types, allow_miners, deny_miners "+ "FROM storage_path WHERE storage_id=$1", string(id)) if err != nil { return storiface.StorageInfo{}, xerrors.Errorf("StorageInfo query fails: %v", err) @@ -647,11 +705,13 @@ func (dbi *DBIndex) StorageInfo(ctx context.Context, id storiface.ID) (storiface sinfo.AllowTo = splitString(qResults[0].AllowTo) sinfo.AllowTypes = splitString(qResults[0].AllowTypes) sinfo.DenyTypes = splitString(qResults[0].DenyTypes) + sinfo.AllowMiners = splitString(qResults[0].AllowMiners) + sinfo.DenyMiners = splitString(qResults[0].DenyMiners) return sinfo, nil } -func (dbi *DBIndex) StorageBestAlloc(ctx context.Context, allocate storiface.SectorFileType, ssize abi.SectorSize, pathType storiface.PathType) ([]storiface.StorageInfo, error) { +func (dbi *DBIndex) StorageBestAlloc(ctx context.Context, allocate storiface.SectorFileType, ssize abi.SectorSize, pathType storiface.PathType, miner abi.ActorID) ([]storiface.StorageInfo, error) { var err error var spaceReq uint64 switch pathType { @@ -667,16 +727,18 @@ func (dbi *DBIndex) StorageBestAlloc(ctx context.Context, allocate storiface.Sec } var rows []struct { - StorageId string - Urls string - Weight uint64 - MaxStorage uint64 - CanSeal bool - CanStore bool - Groups string - AllowTo string - AllowTypes string - DenyTypes string + StorageId string + Urls string + Weight uint64 + MaxStorage uint64 + CanSeal bool + CanStore bool + Groups string + AllowTo string + AllowTypes string + DenyTypes string + AllowMiners string + DenyMiners string } err = dbi.harmonyDB.Select(ctx, &rows, @@ -689,7 +751,9 @@ func (dbi *DBIndex) StorageBestAlloc(ctx context.Context, allocate storiface.Sec groups, allow_to, allow_types, - deny_types + deny_types, + allow_miners, + deny_miners FROM storage_path WHERE available >= $1 and NOW()-($2 * INTERVAL '1 second') < last_heartbeat @@ -700,24 +764,77 @@ func (dbi *DBIndex) StorageBestAlloc(ctx context.Context, allocate storiface.Sec SkippedHeartbeatThresh.Seconds(), pathType == storiface.PathSealing, pathType == storiface.PathStorage, + miner.String(), ) if err != nil { return nil, xerrors.Errorf("Querying for best storage sectors fails with err %w: ", err) } var result []storiface.StorageInfo + for _, row := range rows { + // Matching with 0 as a workaround to avoid having minerID + // present when calling TaskStorage.HasCapacity() + if !(miner == abi.ActorID(0)) { + if len(row.AllowMiners) > 0 { + found := false + allowMiners := splitString(row.AllowMiners) + for _, m := range allowMiners { + m := m + maddr, err := address.NewFromString(m) + if err != nil { + return nil, xerrors.Errorf("parsing miner address: %w", err) + } + mid, err := address.IDFromAddress(maddr) + if err != nil { + return nil, xerrors.Errorf("parsing miner ID: %w", err) + } + if abi.ActorID(mid) == miner { + found = true + break + } + } + if !found { + continue + } + } + if len(row.DenyMiners) > 0 { + found := false + denyMiners := splitString(row.DenyMiners) + for _, m := range denyMiners { + m := m + maddr, err := address.NewFromString(m) + if err != nil { + return nil, xerrors.Errorf("parsing miner address: %w", err) + } + mid, err := address.IDFromAddress(maddr) + if err != nil { + return nil, xerrors.Errorf("parsing miner ID: %w", err) + } + if abi.ActorID(mid) == miner { + found = true + break + } + } + if found { + continue + } + } + } + result = append(result, storiface.StorageInfo{ - ID: storiface.ID(row.StorageId), - URLs: splitString(row.Urls), - Weight: row.Weight, - MaxStorage: row.MaxStorage, - CanSeal: row.CanSeal, - CanStore: row.CanStore, - Groups: splitString(row.Groups), - AllowTo: splitString(row.AllowTo), - AllowTypes: splitString(row.AllowTypes), - DenyTypes: splitString(row.DenyTypes), + ID: storiface.ID(row.StorageId), + URLs: splitString(row.Urls), + Weight: row.Weight, + MaxStorage: row.MaxStorage, + CanSeal: row.CanSeal, + CanStore: row.CanStore, + Groups: splitString(row.Groups), + AllowTo: splitString(row.AllowTo), + AllowTypes: splitString(row.AllowTypes), + DenyTypes: splitString(row.DenyTypes), + AllowMiners: splitString(row.AllowMiners), + DenyMiners: splitString(row.DenyMiners), }) } diff --git a/storage/paths/index.go b/storage/paths/index.go index 49ee11e09..1e90d2cb3 100644 --- a/storage/paths/index.go +++ b/storage/paths/index.go @@ -14,6 +14,7 @@ import ( "go.opencensus.io/tag" "golang.org/x/xerrors" + "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/big" @@ -38,7 +39,7 @@ type SectorIndex interface { // part of storage-miner api StorageDropSector(ctx context.Context, storageID storiface.ID, s abi.SectorID, ft storiface.SectorFileType) error StorageFindSector(ctx context.Context, sector abi.SectorID, ft storiface.SectorFileType, ssize abi.SectorSize, allowFetch bool) ([]storiface.SectorStorageInfo, error) - StorageBestAlloc(ctx context.Context, allocate storiface.SectorFileType, ssize abi.SectorSize, pathType storiface.PathType) ([]storiface.StorageInfo, error) + StorageBestAlloc(ctx context.Context, allocate storiface.SectorFileType, ssize abi.SectorSize, pathType storiface.PathType, miner abi.ActorID) ([]storiface.StorageInfo, error) // atomically acquire locks on all sector file types. close ctx to unlock StorageLock(ctx context.Context, sector abi.SectorID, read storiface.SectorFileType, write storiface.SectorFileType) error @@ -61,6 +62,7 @@ type storageEntry struct { heartbeatErr error } +// MemIndex represents an in-memory index of storage sectors and storage entries. type MemIndex struct { *indexLocks lk sync.RWMutex @@ -206,6 +208,8 @@ func (i *MemIndex) StorageAttach(ctx context.Context, si storiface.StorageInfo, i.stores[si.ID].info.AllowTo = si.AllowTo i.stores[si.ID].info.AllowTypes = allow i.stores[si.ID].info.DenyTypes = deny + i.stores[si.ID].info.AllowMiners = si.AllowMiners + i.stores[si.ID].info.DenyMiners = si.DenyMiners return nil } @@ -476,8 +480,10 @@ func (i *MemIndex) StorageFindSector(ctx context.Context, s abi.SectorID, ft sto Primary: isprimary[id], - AllowTypes: st.info.AllowTypes, - DenyTypes: st.info.DenyTypes, + AllowTypes: st.info.AllowTypes, + DenyTypes: st.info.DenyTypes, + AllowMiners: st.info.AllowMiners, + DenyMiners: st.info.DenyMiners, }) } @@ -492,6 +498,52 @@ func (i *MemIndex) StorageFindSector(ctx context.Context, s abi.SectorID, ft sto continue } + if len(st.info.AllowMiners) > 0 { + found := false + for _, m := range st.info.AllowMiners { + minerIDStr := m + maddr, err := address.NewFromString(minerIDStr) + if err != nil { + return nil, xerrors.Errorf("parsing miner address: %w", err) + } + mid, err := address.IDFromAddress(maddr) + if err != nil { + return nil, xerrors.Errorf("converting miner address to ID: %w", err) + } + if abi.ActorID(mid) == s.Miner { + found = true + break + } + } + if !found { + log.Debugf("not allocating on %s, miner %s not allowed", st.info.ID, s.Miner.String()) + continue + } + } + + if len(st.info.DenyMiners) > 0 { + found := false + for _, m := range st.info.DenyMiners { + minerIDStr := m + maddr, err := address.NewFromString(minerIDStr) + if err != nil { + return nil, xerrors.Errorf("parsing miner address: %w", err) + } + mid, err := address.IDFromAddress(maddr) + if err != nil { + return nil, xerrors.Errorf("converting miner address to ID: %w", err) + } + if abi.ActorID(mid) == s.Miner { + found = true + break + } + } + if found { + log.Debugf("not allocating on %s, miner %s denied", st.info.ID, s.Miner.String()) + continue + } + } + if spaceReq > uint64(st.fsi.Available) { log.Debugf("not selecting on %s, out of space (available: %d, need: %d)", st.info.ID, st.fsi.Available, spaceReq) continue @@ -555,8 +607,10 @@ func (i *MemIndex) StorageFindSector(ctx context.Context, s abi.SectorID, ft sto Primary: false, - AllowTypes: st.info.AllowTypes, - DenyTypes: st.info.DenyTypes, + AllowTypes: st.info.AllowTypes, + DenyTypes: st.info.DenyTypes, + AllowMiners: st.info.AllowMiners, + DenyMiners: st.info.DenyMiners, }) } } @@ -564,6 +618,21 @@ func (i *MemIndex) StorageFindSector(ctx context.Context, s abi.SectorID, ft sto return out, nil } +// StorageInfo retrieves the storage information for a given storage ID. +// +// The method first acquires a read lock on the MemIndex to ensure thread-safety. +// It then checks if the storage ID exists in the stores map. If not, it returns +// an error indicating that the sector store was not found. +// +// Finally, it returns the storage information of the selected storage. +// +// Parameters: +// - ctx: the context.Context object for cancellation and timeouts +// - id: the ID of the storage to retrieve information for +// +// Returns: +// - storiface.StorageInfo: the storage information of the selected storage ID +// - error: an error indicating any issues encountered during the process func (i *MemIndex) StorageInfo(ctx context.Context, id storiface.ID) (storiface.StorageInfo, error) { i.lk.RLock() defer i.lk.RUnlock() @@ -576,7 +645,33 @@ func (i *MemIndex) StorageInfo(ctx context.Context, id storiface.ID) (storiface. return *si.info, nil } -func (i *MemIndex) StorageBestAlloc(ctx context.Context, allocate storiface.SectorFileType, ssize abi.SectorSize, pathType storiface.PathType) ([]storiface.StorageInfo, error) { +// StorageBestAlloc selects the best available storage options for allocating +// a sector file. It takes into account the allocation type (sealing or storage), +// sector size, and path type (sealing or storage). +// +// The method first estimates the required space for the allocation based on the +// sector size and path type. It then iterates through all available storage options +// and filters out those that cannot be used for the given path type. It also filters +// out storage options that do not have enough available space or have not received +// heartbeats within a certain threshold. +// +// The remaining storage options are sorted based on their available space and weight, +// with higher availability and weight being prioritized. The method then returns +// the information of the selected storage options. +// +// If no suitable storage options are found, it returns an error indicating that +// no good path is available. +// +// Parameters: +// - ctx: the context.Context object for cancellation and timeouts +// - allocate: the type of allocation (sealing or storage) +// - ssize: the size of the sector file +// - pathType: the path type (sealing or storage) +// +// Returns: +// - []storiface.StorageInfo: the information of the selected storage options +// - error: an error indicating any issues encountered during the process +func (i *MemIndex) StorageBestAlloc(ctx context.Context, allocate storiface.SectorFileType, ssize abi.SectorSize, pathType storiface.PathType, miner abi.ActorID) ([]storiface.StorageInfo, error) { i.lk.RLock() defer i.lk.RUnlock() @@ -604,6 +699,52 @@ func (i *MemIndex) StorageBestAlloc(ctx context.Context, allocate storiface.Sect continue } + if len(p.info.AllowMiners) > 0 { + found := false + for _, m := range p.info.AllowMiners { + minerIDStr := m + maddr, err := address.NewFromString(minerIDStr) + if err != nil { + return nil, xerrors.Errorf("parsing miner address: %w", err) + } + mid, err := address.IDFromAddress(maddr) + if err != nil { + return nil, xerrors.Errorf("converting miner address to ID: %w", err) + } + if abi.ActorID(mid) == miner { + found = true + break + } + } + if !found { + log.Debugf("not allocating on %s, miner %s not allowed", p.info.ID, miner.String()) + continue + } + } + + if len(p.info.DenyMiners) > 0 { + found := false + for _, m := range p.info.DenyMiners { + minerIDStr := m + maddr, err := address.NewFromString(minerIDStr) + if err != nil { + return nil, xerrors.Errorf("parsing miner address: %w", err) + } + mid, err := address.IDFromAddress(maddr) + if err != nil { + return nil, xerrors.Errorf("converting miner address to ID: %w", err) + } + if abi.ActorID(mid) == miner { + found = true + break + } + } + if found { + log.Debugf("not allocating on %s, miner %s denied", p.info.ID, miner.String()) + continue + } + } + if spaceReq > uint64(p.fsi.Available) { log.Debugf("not allocating on %s, out of space (available: %d, need: %d)", p.info.ID, p.fsi.Available, spaceReq) continue diff --git a/storage/paths/index_test.go b/storage/paths/index_test.go index 96e17ce7d..8793b8814 100644 --- a/storage/paths/index_test.go +++ b/storage/paths/index_test.go @@ -152,3 +152,61 @@ func TestFindAllow(t *testing.T) { } } } + +func TestStorageBestAlloc(t *testing.T) { + idx := NewMemIndex(nil) + + dummyStorageInfo := storiface.StorageInfo{ + ID: storiface.ID("dummy"), + CanSeal: true, + CanStore: true, + URLs: []string{"http://localhost:9999/"}, + Weight: 10, + AllowMiners: []string{ + "t001", + }, + } + + dummyFsStat := fsutil.FsStat{ + Capacity: 10000000000, + Available: 7000000000, + Reserved: 100000000, + Used: 3000000000, + } + + err := idx.StorageAttach(context.Background(), dummyStorageInfo, dummyFsStat) + require.NoError(t, err) + + t.Run("PathSealing", func(t *testing.T) { + result, err := idx.StorageBestAlloc(context.Background(), storiface.FTUnsealed, 123, storiface.PathSealing, 1) + require.Equal(t, err, nil) + require.NotNil(t, result) + require.Equal(t, len(result), 1) + require.Equal(t, result[0].ID, dummyStorageInfo.ID) + }) + + t.Run("PathStorage", func(t *testing.T) { + result, err := idx.StorageBestAlloc(context.Background(), storiface.FTUnsealed, 123, storiface.PathStorage, 1) + require.Equal(t, err, nil) + require.NotNil(t, result) + require.Equal(t, len(result), 1) + require.Equal(t, result[0].ID, dummyStorageInfo.ID) + }) + + t.Run("NotAllowedMiner", func(t *testing.T) { + _, err := idx.StorageBestAlloc(context.Background(), storiface.FTUnsealed, 123, storiface.PathSealing, 2) + require.Error(t, err) + }) + + t.Run("NoAvailableSpace", func(t *testing.T) { + bigSectorSize := abi.SectorSize(10000000000) + _, err := idx.StorageBestAlloc(context.Background(), storiface.FTUnsealed, bigSectorSize, storiface.PathSealing, 1) + require.Error(t, err) + }) + + t.Run("AllowedMiner", func(t *testing.T) { + _, err := idx.StorageBestAlloc(context.Background(), storiface.FTUnsealed, 123, storiface.PathSealing, 1) + require.NoError(t, err) + }) + +} diff --git a/storage/paths/local.go b/storage/paths/local.go index ccb41c2a2..4d4cc1f48 100644 --- a/storage/paths/local.go +++ b/storage/paths/local.go @@ -14,6 +14,7 @@ import ( "golang.org/x/xerrors" ffi "github.com/filecoin-project/filecoin-ffi" + "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/proof" @@ -204,16 +205,18 @@ func (st *Local) OpenPath(ctx context.Context, p string) error { } err = st.index.StorageAttach(ctx, storiface.StorageInfo{ - ID: meta.ID, - URLs: st.urls, - Weight: meta.Weight, - MaxStorage: meta.MaxStorage, - CanSeal: meta.CanSeal, - CanStore: meta.CanStore, - Groups: meta.Groups, - AllowTo: meta.AllowTo, - AllowTypes: meta.AllowTypes, - DenyTypes: meta.DenyTypes, + ID: meta.ID, + URLs: st.urls, + Weight: meta.Weight, + MaxStorage: meta.MaxStorage, + CanSeal: meta.CanSeal, + CanStore: meta.CanStore, + Groups: meta.Groups, + AllowTo: meta.AllowTo, + AllowTypes: meta.AllowTypes, + DenyTypes: meta.DenyTypes, + AllowMiners: meta.AllowMiners, + DenyMiners: meta.DenyMiners, }, fst) if err != nil { return xerrors.Errorf("declaring storage in index: %w", err) @@ -294,16 +297,18 @@ func (st *Local) Redeclare(ctx context.Context, filterId *storiface.ID, dropMiss } err = st.index.StorageAttach(ctx, storiface.StorageInfo{ - ID: id, - URLs: st.urls, - Weight: meta.Weight, - MaxStorage: meta.MaxStorage, - CanSeal: meta.CanSeal, - CanStore: meta.CanStore, - Groups: meta.Groups, - AllowTo: meta.AllowTo, - AllowTypes: meta.AllowTypes, - DenyTypes: meta.DenyTypes, + ID: id, + URLs: st.urls, + Weight: meta.Weight, + MaxStorage: meta.MaxStorage, + CanSeal: meta.CanSeal, + CanStore: meta.CanStore, + Groups: meta.Groups, + AllowTo: meta.AllowTo, + AllowTypes: meta.AllowTypes, + DenyTypes: meta.DenyTypes, + AllowMiners: meta.AllowMiners, + DenyMiners: meta.DenyMiners, }, fst) if err != nil { return xerrors.Errorf("redeclaring storage in index: %w", err) @@ -495,20 +500,64 @@ func (st *Local) AcquireSector(ctx context.Context, sid storiface.SectorRef, exi var out storiface.SectorPaths var storageIDs storiface.SectorPaths - allocPathOk := func(canSeal, canStore bool, allowTypes, denyTypes []string, fileType storiface.SectorFileType) bool { + allocPathOk := func(canSeal, canStore bool, allowTypes, denyTypes, allowMiners, denyMiners []string, fileType storiface.SectorFileType, miner abi.ActorID) (bool, error) { if (pathType == storiface.PathSealing) && !canSeal { - return false + return false, nil } if (pathType == storiface.PathStorage) && !canStore { - return false + return false, nil } if !fileType.Allowed(allowTypes, denyTypes) { - return false + return false, nil } - return true + if len(allowMiners) > 0 { + found := false + for _, m := range allowMiners { + minerIDStr := m + maddr, err := address.NewFromString(minerIDStr) + if err != nil { + return false, xerrors.Errorf("parsing miner address: %w", err) + } + mid, err := address.IDFromAddress(maddr) + if err != nil { + return false, xerrors.Errorf("converting miner address to ID: %w", err) + } + if abi.ActorID(mid) == miner { + found = true + break + } + } + if !found { + return false, nil + } + } + + if len(denyMiners) > 0 { + found := false + for _, m := range denyMiners { + minerIDStr := m + maddr, err := address.NewFromString(minerIDStr) + if err != nil { + return false, xerrors.Errorf("parsing miner address: %w", err) + } + mid, err := address.IDFromAddress(maddr) + if err != nil { + return false, xerrors.Errorf("converting miner address to ID: %w", err) + } + if abi.ActorID(mid) == miner { + found = true + break + } + } + if found { + return false, nil + } + } + + return true, nil } // First find existing files @@ -536,8 +585,15 @@ func (st *Local) AcquireSector(ctx context.Context, sid storiface.SectorRef, exi continue } - if allocate.Has(fileType) && !allocPathOk(info.CanSeal, info.CanStore, info.AllowTypes, info.DenyTypes, fileType) { - continue // allocate request for a path of different type + if allocate.Has(fileType) { + ok, err := allocPathOk(info.CanSeal, info.CanStore, info.AllowTypes, info.DenyTypes, info.AllowMiners, info.DenyMiners, fileType, sid.ID.Miner) + if err != nil { + log.Debug(err) + continue + } + if !ok { + continue // allocate request for a path of different type + } } spath := p.sectorPath(sid.ID, fileType) @@ -556,7 +612,7 @@ func (st *Local) AcquireSector(ctx context.Context, sid storiface.SectorRef, exi continue } - sis, err := st.index.StorageBestAlloc(ctx, fileType, ssize, pathType) + sis, err := st.index.StorageBestAlloc(ctx, fileType, ssize, pathType, sid.ID.Miner) if err != nil { return storiface.SectorPaths{}, storiface.SectorPaths{}, xerrors.Errorf("finding best storage for allocating : %w", err) } @@ -574,7 +630,14 @@ func (st *Local) AcquireSector(ctx context.Context, sid storiface.SectorRef, exi continue } - if !allocPathOk(si.CanSeal, si.CanStore, si.AllowTypes, si.DenyTypes, fileType) { + alloc, err := allocPathOk(si.CanSeal, si.CanStore, si.AllowTypes, si.DenyTypes, si.AllowMiners, si.DenyMiners, fileType, sid.ID.Miner) + + if err != nil { + log.Debug(err) + continue + } + + if !alloc { continue } diff --git a/storage/paths/mocks/index.go b/storage/paths/mocks/index.go index 6fdcb03b9..1835661ff 100644 --- a/storage/paths/mocks/index.go +++ b/storage/paths/mocks/index.go @@ -54,18 +54,18 @@ func (mr *MockSectorIndexMockRecorder) StorageAttach(arg0, arg1, arg2 interface{ } // StorageBestAlloc mocks base method. -func (m *MockSectorIndex) StorageBestAlloc(arg0 context.Context, arg1 storiface.SectorFileType, arg2 abi.SectorSize, arg3 storiface.PathType) ([]storiface.StorageInfo, error) { +func (m *MockSectorIndex) StorageBestAlloc(arg0 context.Context, arg1 storiface.SectorFileType, arg2 abi.SectorSize, arg3 storiface.PathType, arg4 abi.ActorID) ([]storiface.StorageInfo, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "StorageBestAlloc", arg0, arg1, arg2, arg3) + ret := m.ctrl.Call(m, "StorageBestAlloc", arg0, arg1, arg2, arg3, arg4) ret0, _ := ret[0].([]storiface.StorageInfo) ret1, _ := ret[1].(error) return ret0, ret1 } // StorageBestAlloc indicates an expected call of StorageBestAlloc. -func (mr *MockSectorIndexMockRecorder) StorageBestAlloc(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { +func (mr *MockSectorIndexMockRecorder) StorageBestAlloc(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StorageBestAlloc", reflect.TypeOf((*MockSectorIndex)(nil).StorageBestAlloc), arg0, arg1, arg2, arg3) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StorageBestAlloc", reflect.TypeOf((*MockSectorIndex)(nil).StorageBestAlloc), arg0, arg1, arg2, arg3, arg4) } // StorageDeclareSector mocks base method. diff --git a/storage/sealer/manager.go b/storage/sealer/manager.go index 41b3a1b39..00514c79e 100644 --- a/storage/sealer/manager.go +++ b/storage/sealer/manager.go @@ -356,7 +356,7 @@ func (m *Manager) SectorsUnsealPiece(ctx context.Context, sector storiface.Secto // selector will schedule the Unseal task on a worker that either already has the sealed sector files or has space in // one of it's sealing scratch spaces to store them after fetching them from another worker. - selector := newExistingSelector(m.index, sector.ID, storiface.FTSealed|storiface.FTCache, true) + selector := newExistingSelector(m.index, sector.ID, storiface.FTSealed|storiface.FTCache, sector.ID.Miner, true) log.Debugf("will schedule unseal for sector %d", sector.ID) err = m.sched.Schedule(ctx, sector, sealtasks.TTUnseal, selector, unsealFetch, func(ctx context.Context, w Worker) error { @@ -377,7 +377,7 @@ func (m *Manager) SectorsUnsealPiece(ctx context.Context, sector storiface.Secto } // get a selector for moving unsealed sector into long-term storage - fetchSel := newMoveSelector(m.index, sector.ID, storiface.FTUnsealed, storiface.PathStorage, !m.disallowRemoteFinalize) + fetchSel := newMoveSelector(m.index, sector.ID, storiface.FTUnsealed, storiface.PathStorage, sector.ID.Miner, !m.disallowRemoteFinalize) // move unsealed sector to long-term storage // Possible TODO: Add an option to not keep the unsealed sector in long term storage? @@ -431,9 +431,9 @@ func (m *Manager) AddPiece(ctx context.Context, sector storiface.SectorRef, exis var selector WorkerSelector var err error if len(existingPieces) == 0 { // new - selector = newAllocSelector(m.index, storiface.FTUnsealed, storiface.PathSealing) + selector = newAllocSelector(m.index, storiface.FTUnsealed, storiface.PathSealing, sector.ID.Miner) } else { // use existing - selector = newExistingSelector(m.index, sector.ID, storiface.FTUnsealed, false) + selector = newExistingSelector(m.index, sector.ID, storiface.FTUnsealed, sector.ID.Miner, false) } var out abi.PieceInfo @@ -484,7 +484,7 @@ func (m *Manager) SealPreCommit1(ctx context.Context, sector storiface.SectorRef // TODO: also consider where the unsealed data sits - selector := newAllocSelector(m.index, storiface.FTCache|storiface.FTSealed, storiface.PathSealing) + selector := newAllocSelector(m.index, storiface.FTCache|storiface.FTSealed, storiface.PathSealing, sector.ID.Miner) err = m.sched.Schedule(ctx, sector, sealtasks.TTPreCommit1, selector, m.schedFetch(sector, storiface.FTUnsealed, storiface.PathSealing, storiface.AcquireMove), func(ctx context.Context, w Worker) error { err := m.startWork(ctx, w, wk)(w.SealPreCommit1(ctx, sector, ticket, pieces)) @@ -533,7 +533,7 @@ func (m *Manager) SealPreCommit2(ctx context.Context, sector storiface.SectorRef return storiface.SectorCids{}, xerrors.Errorf("acquiring sector lock: %w", err) } - selector := newExistingSelector(m.index, sector.ID, storiface.FTCache|storiface.FTSealed, true) + selector := newExistingSelector(m.index, sector.ID, storiface.FTCache|storiface.FTSealed, sector.ID.Miner, true) err = m.sched.Schedule(ctx, sector, sealtasks.TTPreCommit2, selector, m.schedFetch(sector, storiface.FTCache|storiface.FTSealed, storiface.PathSealing, storiface.AcquireMove), func(ctx context.Context, w Worker) error { err := m.startWork(ctx, w, wk)(w.SealPreCommit2(ctx, sector, phase1Out)) @@ -585,7 +585,7 @@ func (m *Manager) SealCommit1(ctx context.Context, sector storiface.SectorRef, t // NOTE: We set allowFetch to false in so that we always execute on a worker // with direct access to the data. We want to do that because this step is // generally very cheap / fast, and transferring data is not worth the effort - selector := newExistingSelector(m.index, sector.ID, storiface.FTCache|storiface.FTSealed, false) + selector := newExistingSelector(m.index, sector.ID, storiface.FTCache|storiface.FTSealed, sector.ID.Miner, false) err = m.sched.Schedule(ctx, sector, sealtasks.TTCommit1, selector, m.schedFetch(sector, storiface.FTCache|storiface.FTSealed, storiface.PathSealing, storiface.AcquireMove), func(ctx context.Context, w Worker) error { err := m.startWork(ctx, w, wk)(w.SealCommit1(ctx, sector, ticket, seed, pieces, cids)) @@ -700,7 +700,7 @@ func (m *Manager) FinalizeSector(ctx context.Context, sector storiface.SectorRef // do the cache trimming wherever the likely still very large cache lives. // we really don't want to move it. - selector := newExistingSelector(m.index, sector.ID, storiface.FTCache, false) + selector := newExistingSelector(m.index, sector.ID, storiface.FTCache, sector.ID.Miner, false) err = m.sched.Schedule(ctx, sector, sealtasks.TTFinalize, selector, m.schedFetch(sector, storiface.FTCache, cachePathType, storiface.AcquireMove), @@ -713,7 +713,7 @@ func (m *Manager) FinalizeSector(ctx context.Context, sector storiface.SectorRef } // get a selector for moving stuff into long-term storage - fetchSel := newMoveSelector(m.index, sector.ID, storiface.FTCache|storiface.FTSealed, storiface.PathStorage, !m.disallowRemoteFinalize) + fetchSel := newMoveSelector(m.index, sector.ID, storiface.FTCache|storiface.FTSealed, storiface.PathStorage, sector.ID.Miner, !m.disallowRemoteFinalize) // only move the unsealed file if it still exists and needs moving moveUnsealed := storiface.FTUnsealed @@ -769,7 +769,7 @@ func (m *Manager) FinalizeReplicaUpdate(ctx context.Context, sector storiface.Se // do the cache trimming wherever the likely still large cache lives. // we really don't want to move it. - selector := newExistingSelector(m.index, sector.ID, storiface.FTUpdateCache, false) + selector := newExistingSelector(m.index, sector.ID, storiface.FTUpdateCache, sector.ID.Miner, false) err := m.sched.Schedule(ctx, sector, sealtasks.TTFinalizeReplicaUpdate, selector, m.schedFetch(sector, storiface.FTCache|storiface.FTUpdateCache, pathType, storiface.AcquireMove), @@ -783,7 +783,7 @@ func (m *Manager) FinalizeReplicaUpdate(ctx context.Context, sector storiface.Se move := func(types storiface.SectorFileType) error { // get a selector for moving stuff into long-term storage - fetchSel := newMoveSelector(m.index, sector.ID, types, storiface.PathStorage, !m.disallowRemoteFinalize) + fetchSel := newMoveSelector(m.index, sector.ID, types, storiface.PathStorage, sector.ID.Miner, !m.disallowRemoteFinalize) err = m.sched.Schedule(ctx, sector, sealtasks.TTFetch, fetchSel, m.schedFetch(sector, types, storiface.PathStorage, storiface.AcquireMove), @@ -834,7 +834,7 @@ func (m *Manager) ReleaseUnsealed(ctx context.Context, sector storiface.SectorRe return nil } - selector := newExistingSelector(m.index, sector.ID, storiface.FTUnsealed, false) + selector := newExistingSelector(m.index, sector.ID, storiface.FTUnsealed, sector.ID.Miner, false) return m.sched.Schedule(ctx, sector, sealtasks.TTFinalizeUnsealed, selector, m.schedFetch(sector, storiface.FTUnsealed, pathType, storiface.AcquireMove), func(ctx context.Context, w Worker) error { _, err := m.waitSimpleCall(ctx)(w.ReleaseUnsealed(ctx, sector, keepUnsealed)) @@ -906,7 +906,7 @@ func (m *Manager) GenerateSectorKeyFromData(ctx context.Context, sector storifac // NOTE: We set allowFetch to false in so that we always execute on a worker // with direct access to the data. We want to do that because this step is // generally very cheap / fast, and transferring data is not worth the effort - selector := newExistingSelector(m.index, sector.ID, storiface.FTUnsealed|storiface.FTUpdate|storiface.FTUpdateCache|storiface.FTCache, true) + selector := newExistingSelector(m.index, sector.ID, storiface.FTUnsealed|storiface.FTUpdate|storiface.FTUpdateCache|storiface.FTCache, sector.ID.Miner, true) err = m.sched.Schedule(ctx, sector, sealtasks.TTRegenSectorKey, selector, m.schedFetch(sector, storiface.FTUpdate|storiface.FTUnsealed, storiface.PathSealing, storiface.AcquireMove), func(ctx context.Context, w Worker) error { err := m.startWork(ctx, w, wk)(w.GenerateSectorKeyFromData(ctx, sector, commD)) @@ -984,7 +984,7 @@ func (m *Manager) ReplicaUpdate(ctx context.Context, sector storiface.SectorRef, return storiface.ReplicaUpdateOut{}, xerrors.Errorf("acquiring sector lock: %w", err) } - selector := newAllocSelector(m.index, storiface.FTUpdate|storiface.FTUpdateCache, storiface.PathSealing) + selector := newAllocSelector(m.index, storiface.FTUpdate|storiface.FTUpdateCache, storiface.PathSealing, sector.ID.Miner) err = m.sched.Schedule(ctx, sector, sealtasks.TTReplicaUpdate, selector, m.schedFetch(sector, storiface.FTUnsealed|storiface.FTSealed|storiface.FTCache, storiface.PathSealing, storiface.AcquireCopy), func(ctx context.Context, w Worker) error { err := m.startWork(ctx, w, wk)(w.ReplicaUpdate(ctx, sector, pieces)) @@ -1035,7 +1035,7 @@ func (m *Manager) ProveReplicaUpdate1(ctx context.Context, sector storiface.Sect // NOTE: We set allowFetch to false in so that we always execute on a worker // with direct access to the data. We want to do that because this step is // generally very cheap / fast, and transferring data is not worth the effort - selector := newExistingSelector(m.index, sector.ID, storiface.FTUpdate|storiface.FTUpdateCache, false) + selector := newExistingSelector(m.index, sector.ID, storiface.FTUpdate|storiface.FTUpdateCache, sector.ID.Miner, false) err = m.sched.Schedule(ctx, sector, sealtasks.TTProveReplicaUpdate1, selector, m.schedFetch(sector, storiface.FTSealed|storiface.FTCache|storiface.FTUpdate|storiface.FTUpdateCache, storiface.PathSealing, storiface.AcquireCopy), func(ctx context.Context, w Worker) error { @@ -1153,7 +1153,7 @@ func (m *Manager) DownloadSectorData(ctx context.Context, sector storiface.Secto ptype = storiface.PathStorage } - selector := newAllocSelector(m.index, toFetch, ptype) + selector := newAllocSelector(m.index, toFetch, ptype, sector.ID.Miner) err = m.sched.Schedule(ctx, sector, sealtasks.TTDownloadSector, selector, schedNop, func(ctx context.Context, w Worker) error { err := m.startWork(ctx, w, wk)(w.DownloadSectorData(ctx, sector, finalized, src)) diff --git a/storage/sealer/sched_test.go b/storage/sealer/sched_test.go index 03e947b8a..a991ff3fd 100644 --- a/storage/sealer/sched_test.go +++ b/storage/sealer/sched_test.go @@ -274,7 +274,7 @@ func TestSched(t *testing.T) { done := make(chan struct{}) rm.done[taskName] = done - sel := newAllocSelector(index, storiface.FTCache, storiface.PathSealing) + sel := newAllocSelector(index, storiface.FTCache, storiface.PathSealing, abi.ActorID(1000)) rm.wg.Add(1) go func() { diff --git a/storage/sealer/selector_alloc.go b/storage/sealer/selector_alloc.go index 130f74461..c2267f4ec 100644 --- a/storage/sealer/selector_alloc.go +++ b/storage/sealer/selector_alloc.go @@ -16,13 +16,15 @@ type allocSelector struct { index paths.SectorIndex alloc storiface.SectorFileType ptype storiface.PathType + miner abi.ActorID } -func newAllocSelector(index paths.SectorIndex, alloc storiface.SectorFileType, ptype storiface.PathType) *allocSelector { +func newAllocSelector(index paths.SectorIndex, alloc storiface.SectorFileType, ptype storiface.PathType, miner abi.ActorID) *allocSelector { return &allocSelector{ index: index, alloc: alloc, ptype: ptype, + miner: miner, } } @@ -50,7 +52,7 @@ func (s *allocSelector) Ok(ctx context.Context, task sealtasks.TaskType, spt abi return false, false, xerrors.Errorf("getting sector size: %w", err) } - best, err := s.index.StorageBestAlloc(ctx, s.alloc, ssize, s.ptype) + best, err := s.index.StorageBestAlloc(ctx, s.alloc, ssize, s.ptype, s.miner) if err != nil { return false, false, xerrors.Errorf("finding best alloc storage: %w", err) } diff --git a/storage/sealer/selector_existing.go b/storage/sealer/selector_existing.go index c1e082db8..86610862a 100644 --- a/storage/sealer/selector_existing.go +++ b/storage/sealer/selector_existing.go @@ -16,14 +16,16 @@ type existingSelector struct { index paths.SectorIndex sector abi.SectorID fileType storiface.SectorFileType + miner abi.ActorID allowFetch bool } -func newExistingSelector(index paths.SectorIndex, sector abi.SectorID, alloc storiface.SectorFileType, allowFetch bool) *existingSelector { +func newExistingSelector(index paths.SectorIndex, sector abi.SectorID, alloc storiface.SectorFileType, miner abi.ActorID, allowFetch bool) *existingSelector { return &existingSelector{ index: index, sector: sector, fileType: alloc, + miner: miner, allowFetch: allowFetch, } } diff --git a/storage/sealer/selector_move.go b/storage/sealer/selector_move.go index fde4b3c59..3f07a75de 100644 --- a/storage/sealer/selector_move.go +++ b/storage/sealer/selector_move.go @@ -17,10 +17,11 @@ type moveSelector struct { sector abi.SectorID alloc storiface.SectorFileType destPtype storiface.PathType + miner abi.ActorID allowRemote bool } -func newMoveSelector(index paths.SectorIndex, sector abi.SectorID, alloc storiface.SectorFileType, destPtype storiface.PathType, allowRemote bool) *moveSelector { +func newMoveSelector(index paths.SectorIndex, sector abi.SectorID, alloc storiface.SectorFileType, destPtype storiface.PathType, miner abi.ActorID, allowRemote bool) *moveSelector { return &moveSelector{ index: index, sector: sector, @@ -67,7 +68,7 @@ func (s *moveSelector) Ok(ctx context.Context, task sealtasks.TaskType, spt abi. } } - best, err := s.index.StorageBestAlloc(ctx, s.alloc, ssize, s.destPtype) + best, err := s.index.StorageBestAlloc(ctx, s.alloc, ssize, s.destPtype, s.miner) if err != nil { return false, false, xerrors.Errorf("finding best dest storage: %w", err) } diff --git a/storage/sealer/storiface/filetype.go b/storage/sealer/storiface/filetype.go index 422f87cf3..dc8bebc7e 100644 --- a/storage/sealer/storiface/filetype.go +++ b/storage/sealer/storiface/filetype.go @@ -8,6 +8,28 @@ import ( "github.com/filecoin-project/go-state-types/abi" ) +// FTUnsealed represents an unsealed sector file type. +// FTSealed represents a sealed sector file type. +// FTCache represents a cache sector file type. +// FTUpdate represents an update sector file type. +// FTUpdateCache represents an update cache sector file type. +// FTPiece represents a Piece Park sector file type. +// FileTypes represents the total number of file types. +// +// The SectorFileType type is an integer type that represents different sector file types. +// It has several methods to manipulate and query the file type. +// The String method returns a string representation of the file type. +// The Strings method returns a slice of string representations of all the file types that are set in the receiver object. +// The AllSet method returns a slice of all the file types that are set in the receiver object. +// The Has method checks whether a specific file type is set in the receiver object. +// The SealSpaceUse method calculates the space used by the receiver object in sealing a sector of a given size. +// The SubAllowed method removes selected file types from the receiver object based on a list of allowed and denied file types. +// The Unset method removes selected file types from the receiver object. +// The AnyAllowed method checks whether any file types in the receiver object are allowed based on a list of allowed and denied file types. +// The Allowed method checks whether all file types in the receiver object are allowed based on a list of allowed and denied file types. +// The StoreSpaceUse method calculates the space used by the receiver object in storing a sector of a given size. +// The All method returns an array that represents which file types are set in the receiver object. +// The IsNone method checks whether the receiver object represents no file types. const ( // "regular" sectors FTUnsealed SectorFileType = 1 << iota @@ -24,12 +46,28 @@ const ( FileTypes = iota ) +// PathTypes is a slice of SectorFileType that represents different types of sector file paths. +// It contains the following types of sector file paths: +// - FTUnsealed: represents regular unsealed sectors +// - FTSealed: represents sealed sectors +// - FTCache: represents cache sectors +// - FTUpdate: represents snap sectors +// - FTUpdateCache: represents snap cache sectors +// - FTPiece: represents Piece Park sectors var PathTypes = []SectorFileType{FTUnsealed, FTSealed, FTCache, FTUpdate, FTUpdateCache, FTPiece} +// FTNone represents a sector file type of none. This constant is used in the StorageLock method to specify that a sector should not have any file types locked. +// Example usage: +// err := m.index.StorageLock(ctx, sector.ID, storiface.FTNone, storiface.FTSealed|storiface.FTUnsealed|storiface.FTCache) const ( FTNone SectorFileType = 0 ) +// FTAll represents the combination of all available sector file types. +// It is a variable of type SectorFileType. +// The value of FTAll is calculated by iterating over the PathTypes slice and using the |= operator to perform a bitwise OR operation on each path type. +// The result is assigned to the variable out and returned. +// FTAll is immediately invoked as a function using the anonymous function syntax, so the result is returned as soon as it is calculated. var FTAll = func() (out SectorFileType) { for _, pathType := range PathTypes { out |= pathType @@ -37,8 +75,10 @@ var FTAll = func() (out SectorFileType) { return out }() +// FSOverheadDen represents the constant value 10, which is used to calculate the overhead in various storage space utilization calculations. const FSOverheadDen = 10 +// FSOverheadSeal is a map that represents the overheads for different SectorFileType in sealed sectors. var FSOverheadSeal = map[SectorFileType]int{ // 10x overheads FTUnsealed: FSOverheadDen, FTSealed: FSOverheadDen, @@ -50,6 +90,30 @@ var FSOverheadSeal = map[SectorFileType]int{ // 10x overheads // sector size * disk / fs overhead. FSOverheadDen is like the unit of sector size +// FsOverheadFinalized is a map that represents the finalized overhead for different types of SectorFileType. +// The keys in the map are the SectorFileType values, and the values are integers representing the overhead. +// It is used to calculate the storage space usage for different types of sectors, as shown in the example below: +// +// func (t SectorFileType) StoreSpaceUse(ssize abi.SectorSize) (uint64, error) { +// var need uint64 +// for _, pathType := range PathTypes { +// if !t.Has(pathType) { +// continue +// } +// +// oh, ok := FsOverheadFinalized[pathType] +// if !ok { +// return 0, xerrors.Errorf("no finalized overhead info for %s", pathType) +// } +// +// need += uint64(oh) * uint64(ssize) / FSOverheadDen +// } +// +// return need, nil +// } +// +// The overhead value is retrieved from FsOverheadFinalized by using the SectorFileType value as the key. +// If the overhead value is not found in the map, an error is returned indicating that there is no finalized overhead information for the given sector type. var FsOverheadFinalized = map[SectorFileType]int{ FTUnsealed: FSOverheadDen, FTSealed: FSOverheadDen, @@ -59,8 +123,13 @@ var FsOverheadFinalized = map[SectorFileType]int{ FTPiece: FSOverheadDen, } +// SectorFileType represents the type of a sector file +// TypeFromString converts a string to a SectorFileType type SectorFileType int +// TypeFromString converts a string representation of a SectorFileType to its corresponding value. +// It returns the SectorFileType and nil error if the string matches one of the existing types. +// If the string does not match any type, it returns 0 and an error. func TypeFromString(s string) (SectorFileType, error) { switch s { case "unsealed": @@ -80,6 +149,7 @@ func TypeFromString(s string) (SectorFileType, error) { } } +// String returns a string representation of the SectorFileType. func (t SectorFileType) String() string { switch t { case FTUnsealed: @@ -99,6 +169,12 @@ func (t SectorFileType) String() string { } } +// Strings returns a slice of strings representing the names of the SectorFileType values that are set in the receiver value. +// Example usage: +// +// fileType := SectorFileType(FTSealed | FTCache) +// names := fileType.Strings() // names = ["sealed", "cache"] +// fmt.Println(names) func (t SectorFileType) Strings() []string { var out []string for _, fileType := range PathTypes { @@ -111,6 +187,7 @@ func (t SectorFileType) Strings() []string { return out } +// AllSet returns a slice of SectorFileType values that are set in the SectorFileType receiver value func (t SectorFileType) AllSet() []SectorFileType { var out []SectorFileType for _, fileType := range PathTypes { @@ -123,10 +200,33 @@ func (t SectorFileType) AllSet() []SectorFileType { return out } +// Has checks if the SectorFileType has a specific singleType. func (t SectorFileType) Has(singleType SectorFileType) bool { return t&singleType == singleType } +// SealSpaceUse calculates the amount of space needed for sealing the sector +// based on the given sector size. It iterates over the different path types +// and calculates the space needed for each path type using the FSOverheadSeal +// map. The overhead value is multiplied by the sector size and divided by the +// FSOverheadDen constant. The total space needed is accumulated and returned. +// If there is no seal overhead information for a particular path type, an error +// is returned. +// +// Example usage: +// +// fileType := FTSealed | FTCache +// sectorSize := abi.SectorSize(32 << 20) // 32 MiB +// spaceNeeded, err := fileType.SealSpaceUse(sectorSize) +// +// Parameters: +// +// ssize: The size of the sector +// +// Returns: +// +// uint64: The amount of space needed for sealing the sector +// error: If there is no seal overhead information for a path type func (t SectorFileType) SealSpaceUse(ssize abi.SectorSize) (uint64, error) { var need uint64 for _, pathType := range PathTypes { @@ -145,6 +245,15 @@ func (t SectorFileType) SealSpaceUse(ssize abi.SectorSize) (uint64, error) { return need, nil } +// The method takes in two parameters: allowTypes and denyTypes, both of which are slices of strings. +// If allowTypes is not empty, the method sets a denyMask with all bits set to 1, and then iterates over each allowType, +// converting it to a SectorFileType using the TypeFromString function and unsetting the corresponding bit in the denyMask. +// If a string in allowTypes cannot be converted to a valid SectorFileType, it is ignored. +// After processing allowTypes, the method iterates over each denyType, converting it to a SectorFileType using the TypeFromString function +// and setting the corresponding bit in the denyMask. +// If a string in denyTypes cannot be converted to a valid SectorFileType, it is ignored. +// Finally, the method returns the bitwise AND of the original SectorFileType and the denyMask. +// The returned SectorFileType will only allow the types specified in allowTypes and exclude the types specified in denyTypes.` func (t SectorFileType) SubAllowed(allowTypes []string, denyTypes []string) SectorFileType { var denyMask SectorFileType // 1s deny @@ -174,18 +283,30 @@ func (t SectorFileType) SubAllowed(allowTypes []string, denyTypes []string) Sect return t & denyMask } +// Unset removes the specified sector file type(s) from the current SectorFileType value. +// It performs a bitwise AND operation between the current value and the bitwise complement of the toUnset value. +// The result is returned as a new SectorFileType value. +// Any bits that are set in toUnset will be cleared in the result. +// Usage: result = value.Unset(typesToUnset) func (t SectorFileType) Unset(toUnset SectorFileType) SectorFileType { return t &^ toUnset } +// AnyAllowed checks if the SectorFileType has any allowed types and no denied types. func (t SectorFileType) AnyAllowed(allowTypes []string, denyTypes []string) bool { return t.SubAllowed(allowTypes, denyTypes) != t } +// Allowed checks if the SectorFileType is allowed based on the given allowTypes and denyTypes. +// Returns true if the SectorFileType is allowed, otherwise false. func (t SectorFileType) Allowed(allowTypes []string, denyTypes []string) bool { return t.SubAllowed(allowTypes, denyTypes) == 0 } +// StoreSpaceUse calculates the space used for storing sectors of a specific file type. +// It takes the sector size as input and returns the total space needed in bytes and an error, if any. +// The calculation is based on the finalized overhead information for the file type. +// If the overhead information is not available for a particular file type, an error will be returned. func (t SectorFileType) StoreSpaceUse(ssize abi.SectorSize) (uint64, error) { var need uint64 for _, pathType := range PathTypes { @@ -204,6 +325,7 @@ func (t SectorFileType) StoreSpaceUse(ssize abi.SectorSize) (uint64, error) { return need, nil } +// All returns an array indicating whether each FileTypes flag is set in the SectorFileType. func (t SectorFileType) All() [FileTypes]bool { var out [FileTypes]bool @@ -214,10 +336,13 @@ func (t SectorFileType) All() [FileTypes]bool { return out } +// IsNone checks if the SectorFileType value is equal to zero. +// It returns true if the value is zero, indicating that the type is none. func (t SectorFileType) IsNone() bool { return t == 0 } +// SectorPaths represents the paths for different sector files. type SectorPaths struct { ID abi.SectorID @@ -229,6 +354,7 @@ type SectorPaths struct { Piece string } +// HasAllSet checks if all paths of a SectorPaths struct are set for a given SectorFileType. func (sp SectorPaths) HasAllSet(ft SectorFileType) bool { for _, fileType := range ft.AllSet() { if PathByType(sp, fileType) == "" { @@ -239,6 +365,9 @@ func (sp SectorPaths) HasAllSet(ft SectorFileType) bool { return true } +// Subset returns a new instance of SectorPaths that contains only the paths specified by the filter SectorFileType. +// It iterates over each fileType in the filter, retrieves the corresponding path from the original SectorPaths instance, and sets it in the new instance. +// Finally, it sets the ID field of the new instance to be the same as the original instance. func (sp SectorPaths) Subset(filter SectorFileType) SectorPaths { var out SectorPaths @@ -251,6 +380,24 @@ func (sp SectorPaths) Subset(filter SectorFileType) SectorPaths { return out } +// ParseSectorID parses a sector ID from a given base name. +// It expects the format "s-t0%d-%d", where the first %d represents the miner ID +// and the second %d represents the sector number. +// +// Parameters: +// - baseName: The base name from which to parse the sector ID. +// +// Returns: +// - abi.SectorID: The parsed sector ID. +// - error: An error if parsing fails. +// +// Example usage: +// +// id, err := ParseSectorID(baseName) +// if err != nil { +// // handle error +// } +// // use id func ParseSectorID(baseName string) (abi.SectorID, error) { var n abi.SectorNumber var mid abi.ActorID @@ -269,10 +416,19 @@ func ParseSectorID(baseName string) (abi.SectorID, error) { }, nil } +// SectorName returns the name of a sector in the format "s-t0-" +// +// Parameters: +// - sid: The sector ID +// +// Returns: +// - The name of the sector as a string func SectorName(sid abi.SectorID) string { return fmt.Sprintf("s-t0%d-%d", sid.Miner, sid.Number) } +// PathByType returns the path associated with the specified fileType in the given SectorPaths. +// It panics if the requested path type is unknown. func PathByType(sps SectorPaths, fileType SectorFileType) string { switch fileType { case FTUnsealed: @@ -309,11 +465,14 @@ func SetPathByType(sps *SectorPaths, fileType SectorFileType, p string) { } } +// PathsWithIDs represents paths and IDs for sector files. type PathsWithIDs struct { Paths SectorPaths IDs SectorPaths } +// HasAllSet checks if all paths and IDs in PathsWithIDs have a corresponding path set for the specified SectorFileType. +// It returns true if all paths and IDs are set, and false otherwise. func (p PathsWithIDs) HasAllSet(ft SectorFileType) bool { return p.Paths.HasAllSet(ft) && p.IDs.HasAllSet(ft) } diff --git a/storage/sealer/storiface/index.go b/storage/sealer/storiface/index.go index 653bd2fba..3bf695687 100644 --- a/storage/sealer/storiface/index.go +++ b/storage/sealer/storiface/index.go @@ -86,6 +86,14 @@ type StorageInfo struct { // - "update-cache" // Any other value will generate a warning and be ignored. DenyTypes []string + + // AllowMiners lists miner IDs which are allowed to store their sector data into + // this path. If empty, all miner IDs are allowed + AllowMiners []string + + // DenyMiners lists miner IDs which are denied to store their sector data into + // this path + DenyMiners []string } type HealthReport struct { @@ -104,8 +112,10 @@ type SectorStorageInfo struct { Primary bool - AllowTypes []string - DenyTypes []string + AllowTypes []string + DenyTypes []string + AllowMiners []string + DenyMiners []string } type Decl struct { diff --git a/storage/sealer/storiface/storage.go b/storage/sealer/storiface/storage.go index 75cc9399c..143c3b5d5 100644 --- a/storage/sealer/storiface/storage.go +++ b/storage/sealer/storiface/storage.go @@ -225,4 +225,12 @@ type LocalStorageMeta struct { // - "update-cache" // Any other value will generate a warning and be ignored. DenyTypes []string + + // AllowMiners lists miner IDs which are allowed to store their sector data into + // this path. If empty, all miner IDs are allowed + AllowMiners []string + + // DenyMiners lists miner IDs which are denied to store their sector data into + // this path + DenyMiners []string } From d1a52cf04d31c05dc585c2aa6386248a5f9f5ff6 Mon Sep 17 00:00:00 2001 From: LexLuthr Date: Wed, 3 Apr 2024 00:34:03 +0400 Subject: [PATCH 325/394] refactor filter decision code --- storage/paths/db_index.go | 120 ++++++++--------------------- storage/paths/index.go | 154 +++++++++++++++++--------------------- storage/paths/local.go | 55 +++----------- 3 files changed, 114 insertions(+), 215 deletions(-) diff --git a/storage/paths/db_index.go b/storage/paths/db_index.go index 0ca4cca80..daebc3607 100644 --- a/storage/paths/db_index.go +++ b/storage/paths/db_index.go @@ -15,7 +15,6 @@ import ( "go.opencensus.io/tag" "golang.org/x/xerrors" - "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/lotus/journal/alerting" @@ -578,52 +577,23 @@ func (dbi *DBIndex) StorageFindSector(ctx context.Context, s abi.SectorID, ft st log.Debugf("not selecting on %s, not allowed by file type filters", row.StorageId) continue } - - if len(row.AllowMiners) > 0 { - found := false - allowMiners := splitString(row.AllowMiners) - for _, m := range allowMiners { - m := m - maddr, err := address.NewFromString(m) - if err != nil { - return nil, xerrors.Errorf("parsing miner address: %w", err) - } - mid, err := address.IDFromAddress(maddr) - if err != nil { - return nil, xerrors.Errorf("parsing miner ID: %w", err) - } - if abi.ActorID(mid) == s.Miner { - found = true - break - } - } - if !found { - log.Debugf("not selecting on %s, not allowed by allow miners filters", row.StorageId) - continue - } + allowMiners := splitString(row.AllowMiners) + proceed, err := MinerFilter(allowMiners, false, s.Miner) + if err != nil { + return nil, err } - if len(row.DenyMiners) > 0 { - found := false - denyMiners := splitString(row.DenyMiners) - for _, m := range denyMiners { - m := m - maddr, err := address.NewFromString(m) - if err != nil { - return nil, xerrors.Errorf("parsing miner address: %w", err) - } - mid, err := address.IDFromAddress(maddr) - if err != nil { - return nil, xerrors.Errorf("parsing miner ID: %w", err) - } - if abi.ActorID(mid) == s.Miner { - found = true - break - } - } - if found { - log.Debugf("not selecting on %s, not allowed by deny miners filters", row.StorageId) - continue - } + if !proceed { + log.Debugf("not allocating on %s, miner %s not allowed", row.StorageId, s.Miner.String()) + continue + } + denyMiners := splitString(row.DenyMiners) + proceed, err = MinerFilter(denyMiners, true, s.Miner) + if err != nil { + return nil, err + } + if !proceed { + log.Debugf("not allocating on %s, miner %s denied", row.StorageId, s.Miner.String()) + continue } if allowList != nil { @@ -776,49 +746,23 @@ func (dbi *DBIndex) StorageBestAlloc(ctx context.Context, allocate storiface.Sec // Matching with 0 as a workaround to avoid having minerID // present when calling TaskStorage.HasCapacity() if !(miner == abi.ActorID(0)) { - if len(row.AllowMiners) > 0 { - found := false - allowMiners := splitString(row.AllowMiners) - for _, m := range allowMiners { - m := m - maddr, err := address.NewFromString(m) - if err != nil { - return nil, xerrors.Errorf("parsing miner address: %w", err) - } - mid, err := address.IDFromAddress(maddr) - if err != nil { - return nil, xerrors.Errorf("parsing miner ID: %w", err) - } - if abi.ActorID(mid) == miner { - found = true - break - } - } - if !found { - continue - } + allowMiners := splitString(row.AllowMiners) + proceed, err := MinerFilter(allowMiners, false, miner) + if err != nil { + return nil, err } - if len(row.DenyMiners) > 0 { - found := false - denyMiners := splitString(row.DenyMiners) - for _, m := range denyMiners { - m := m - maddr, err := address.NewFromString(m) - if err != nil { - return nil, xerrors.Errorf("parsing miner address: %w", err) - } - mid, err := address.IDFromAddress(maddr) - if err != nil { - return nil, xerrors.Errorf("parsing miner ID: %w", err) - } - if abi.ActorID(mid) == miner { - found = true - break - } - } - if found { - continue - } + if !proceed { + log.Debugf("not allocating on %s, miner %s not allowed", row.StorageId, miner.String()) + continue + } + denyMiners := splitString(row.DenyMiners) + proceed, err = MinerFilter(denyMiners, true, miner) + if err != nil { + return nil, err + } + if !proceed { + log.Debugf("not allocating on %s, miner %s denied", row.StorageId, miner.String()) + continue } } diff --git a/storage/paths/index.go b/storage/paths/index.go index 1e90d2cb3..391e42972 100644 --- a/storage/paths/index.go +++ b/storage/paths/index.go @@ -498,50 +498,24 @@ func (i *MemIndex) StorageFindSector(ctx context.Context, s abi.SectorID, ft sto continue } - if len(st.info.AllowMiners) > 0 { - found := false - for _, m := range st.info.AllowMiners { - minerIDStr := m - maddr, err := address.NewFromString(minerIDStr) - if err != nil { - return nil, xerrors.Errorf("parsing miner address: %w", err) - } - mid, err := address.IDFromAddress(maddr) - if err != nil { - return nil, xerrors.Errorf("converting miner address to ID: %w", err) - } - if abi.ActorID(mid) == s.Miner { - found = true - break - } - } - if !found { - log.Debugf("not allocating on %s, miner %s not allowed", st.info.ID, s.Miner.String()) - continue - } + proceed, err := MinerFilter(st.info.AllowMiners, false, s.Miner) + if err != nil { + return nil, err } - if len(st.info.DenyMiners) > 0 { - found := false - for _, m := range st.info.DenyMiners { - minerIDStr := m - maddr, err := address.NewFromString(minerIDStr) - if err != nil { - return nil, xerrors.Errorf("parsing miner address: %w", err) - } - mid, err := address.IDFromAddress(maddr) - if err != nil { - return nil, xerrors.Errorf("converting miner address to ID: %w", err) - } - if abi.ActorID(mid) == s.Miner { - found = true - break - } - } - if found { - log.Debugf("not allocating on %s, miner %s denied", st.info.ID, s.Miner.String()) - continue - } + if !proceed { + log.Debugf("not allocating on %s, miner %s not allowed", st.info.ID, s.Miner.String()) + continue + } + + proceed, err = MinerFilter(st.info.DenyMiners, true, s.Miner) + if err != nil { + return nil, err + } + + if !proceed { + log.Debugf("not allocating on %s, miner %s denied", st.info.ID, s.Miner.String()) + continue } if spaceReq > uint64(st.fsi.Available) { @@ -699,50 +673,24 @@ func (i *MemIndex) StorageBestAlloc(ctx context.Context, allocate storiface.Sect continue } - if len(p.info.AllowMiners) > 0 { - found := false - for _, m := range p.info.AllowMiners { - minerIDStr := m - maddr, err := address.NewFromString(minerIDStr) - if err != nil { - return nil, xerrors.Errorf("parsing miner address: %w", err) - } - mid, err := address.IDFromAddress(maddr) - if err != nil { - return nil, xerrors.Errorf("converting miner address to ID: %w", err) - } - if abi.ActorID(mid) == miner { - found = true - break - } - } - if !found { - log.Debugf("not allocating on %s, miner %s not allowed", p.info.ID, miner.String()) - continue - } + proceed, err := MinerFilter(p.info.AllowMiners, false, miner) + if err != nil { + return nil, err } - if len(p.info.DenyMiners) > 0 { - found := false - for _, m := range p.info.DenyMiners { - minerIDStr := m - maddr, err := address.NewFromString(minerIDStr) - if err != nil { - return nil, xerrors.Errorf("parsing miner address: %w", err) - } - mid, err := address.IDFromAddress(maddr) - if err != nil { - return nil, xerrors.Errorf("converting miner address to ID: %w", err) - } - if abi.ActorID(mid) == miner { - found = true - break - } - } - if found { - log.Debugf("not allocating on %s, miner %s denied", p.info.ID, miner.String()) - continue - } + if !proceed { + log.Debugf("not allocating on %s, miner %s not allowed", p.info.ID, miner.String()) + continue + } + + proceed, err = MinerFilter(p.info.DenyMiners, true, miner) + if err != nil { + return nil, err + } + + if !proceed { + log.Debugf("not allocating on %s, miner %s denied", p.info.ID, miner.String()) + continue } if spaceReq > uint64(p.fsi.Available) { @@ -802,3 +750,41 @@ func (i *MemIndex) FindSector(id abi.SectorID, typ storiface.SectorFileType) ([] } var _ SectorIndex = &MemIndex{} + +func MinerFilter(miners []string, deny bool, miner abi.ActorID) (bool, error) { + if len(miners) > 0 { + found := false + for _, m := range miners { + minerIDStr := m + maddr, err := address.NewFromString(minerIDStr) + if err != nil { + return false, xerrors.Errorf("parsing miner address: %w", err) + } + mid, err := address.IDFromAddress(maddr) + if err != nil { + return false, xerrors.Errorf("converting miner address to ID: %w", err) + } + if abi.ActorID(mid) == miner { + found = true + break + } + } + // If not found in list + if !found { + // If this is allowed list + if !deny { + return false, nil + } + // If this is denied list + return true, nil + } + // If found in list and + // If this is allowed list + if !deny { + return true, nil + } + // If this is denied list + return false, nil + } + return true, nil +} diff --git a/storage/paths/local.go b/storage/paths/local.go index 4d4cc1f48..018537c82 100644 --- a/storage/paths/local.go +++ b/storage/paths/local.go @@ -14,7 +14,6 @@ import ( "golang.org/x/xerrors" ffi "github.com/filecoin-project/filecoin-ffi" - "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/proof" @@ -512,49 +511,19 @@ func (st *Local) AcquireSector(ctx context.Context, sid storiface.SectorRef, exi if !fileType.Allowed(allowTypes, denyTypes) { return false, nil } - - if len(allowMiners) > 0 { - found := false - for _, m := range allowMiners { - minerIDStr := m - maddr, err := address.NewFromString(minerIDStr) - if err != nil { - return false, xerrors.Errorf("parsing miner address: %w", err) - } - mid, err := address.IDFromAddress(maddr) - if err != nil { - return false, xerrors.Errorf("converting miner address to ID: %w", err) - } - if abi.ActorID(mid) == miner { - found = true - break - } - } - if !found { - return false, nil - } + proceed, err := MinerFilter(allowMiners, false, miner) + if err != nil { + return false, err } - - if len(denyMiners) > 0 { - found := false - for _, m := range denyMiners { - minerIDStr := m - maddr, err := address.NewFromString(minerIDStr) - if err != nil { - return false, xerrors.Errorf("parsing miner address: %w", err) - } - mid, err := address.IDFromAddress(maddr) - if err != nil { - return false, xerrors.Errorf("converting miner address to ID: %w", err) - } - if abi.ActorID(mid) == miner { - found = true - break - } - } - if found { - return false, nil - } + if !proceed { + return false, nil + } + proceed, err = MinerFilter(denyMiners, true, miner) + if err != nil { + return false, err + } + if !proceed { + return false, nil } return true, nil From 243bf1a0b3f320f55d5657d6c9a386c254504887 Mon Sep 17 00:00:00 2001 From: LexLuthr Date: Thu, 4 Apr 2024 17:42:21 +0400 Subject: [PATCH 326/394] refactor MinerFilter func, update comments --- .../sql/20240401-storage-miner-filter.sql | 2 + .../sql/20240401-storage-segregation.sql | 1 - storage/paths/db_index.go | 29 ++------ storage/paths/index.go | 69 +++++++------------ storage/paths/local.go | 9 +-- storage/sealer/storiface/filetype.go | 64 +++++++---------- 6 files changed, 56 insertions(+), 118 deletions(-) create mode 100644 lib/harmony/harmonydb/sql/20240401-storage-miner-filter.sql delete mode 100644 lib/harmony/harmonydb/sql/20240401-storage-segregation.sql diff --git a/lib/harmony/harmonydb/sql/20240401-storage-miner-filter.sql b/lib/harmony/harmonydb/sql/20240401-storage-miner-filter.sql new file mode 100644 index 000000000..27cecafe6 --- /dev/null +++ b/lib/harmony/harmonydb/sql/20240401-storage-miner-filter.sql @@ -0,0 +1,2 @@ +ALTER TABLE storage_path ADD COLUMN IF NOT EXISTS allow_miners varchar DEFAULT ''; +ALTER TABLE storage_path ADD COLUMN IF NOT EXISTS deny_miners varchar DEFAULT ''; -- comma separated list of miner addresses \ No newline at end of file diff --git a/lib/harmony/harmonydb/sql/20240401-storage-segregation.sql b/lib/harmony/harmonydb/sql/20240401-storage-segregation.sql deleted file mode 100644 index bcc29e68c..000000000 --- a/lib/harmony/harmonydb/sql/20240401-storage-segregation.sql +++ /dev/null @@ -1 +0,0 @@ -ALTER TABLE storage_path ADD COLUMN IF NOT EXISTS allow_miners varchar, ADD COLUMN IF NOT EXISTS deny_miners varchar; -- comma separated list of miner addresses \ No newline at end of file diff --git a/storage/paths/db_index.go b/storage/paths/db_index.go index daebc3607..948398e33 100644 --- a/storage/paths/db_index.go +++ b/storage/paths/db_index.go @@ -24,6 +24,8 @@ import ( "github.com/filecoin-project/lotus/storage/sealer/storiface" ) +const NoMinerFilter = abi.ActorID(0) + var errAlreadyLocked = errors.New("already locked") type DBIndex struct { @@ -578,21 +580,13 @@ func (dbi *DBIndex) StorageFindSector(ctx context.Context, s abi.SectorID, ft st continue } allowMiners := splitString(row.AllowMiners) - proceed, err := MinerFilter(allowMiners, false, s.Miner) - if err != nil { - return nil, err - } - if !proceed { - log.Debugf("not allocating on %s, miner %s not allowed", row.StorageId, s.Miner.String()) - continue - } denyMiners := splitString(row.DenyMiners) - proceed, err = MinerFilter(denyMiners, true, s.Miner) + proceed, msg, err := MinerFilter(allowMiners, denyMiners, s.Miner) if err != nil { return nil, err } if !proceed { - log.Debugf("not allocating on %s, miner %s denied", row.StorageId, s.Miner.String()) + log.Debugf("not allocating on %s, miner %s %s", row.StorageId, s.Miner.String(), msg) continue } @@ -734,7 +728,6 @@ func (dbi *DBIndex) StorageBestAlloc(ctx context.Context, allocate storiface.Sec SkippedHeartbeatThresh.Seconds(), pathType == storiface.PathSealing, pathType == storiface.PathStorage, - miner.String(), ) if err != nil { return nil, xerrors.Errorf("Querying for best storage sectors fails with err %w: ", err) @@ -745,23 +738,15 @@ func (dbi *DBIndex) StorageBestAlloc(ctx context.Context, allocate storiface.Sec for _, row := range rows { // Matching with 0 as a workaround to avoid having minerID // present when calling TaskStorage.HasCapacity() - if !(miner == abi.ActorID(0)) { + if !(miner == NoMinerFilter) { allowMiners := splitString(row.AllowMiners) - proceed, err := MinerFilter(allowMiners, false, miner) - if err != nil { - return nil, err - } - if !proceed { - log.Debugf("not allocating on %s, miner %s not allowed", row.StorageId, miner.String()) - continue - } denyMiners := splitString(row.DenyMiners) - proceed, err = MinerFilter(denyMiners, true, miner) + proceed, msg, err := MinerFilter(allowMiners, denyMiners, miner) if err != nil { return nil, err } if !proceed { - log.Debugf("not allocating on %s, miner %s denied", row.StorageId, miner.String()) + log.Debugf("not allocating on %s, miner %s %s", row.StorageId, miner.String(), msg) continue } } diff --git a/storage/paths/index.go b/storage/paths/index.go index 391e42972..e09b887e0 100644 --- a/storage/paths/index.go +++ b/storage/paths/index.go @@ -498,23 +498,13 @@ func (i *MemIndex) StorageFindSector(ctx context.Context, s abi.SectorID, ft sto continue } - proceed, err := MinerFilter(st.info.AllowMiners, false, s.Miner) + proceed, msg, err := MinerFilter(st.info.AllowMiners, st.info.DenyMiners, s.Miner) if err != nil { return nil, err } if !proceed { - log.Debugf("not allocating on %s, miner %s not allowed", st.info.ID, s.Miner.String()) - continue - } - - proceed, err = MinerFilter(st.info.DenyMiners, true, s.Miner) - if err != nil { - return nil, err - } - - if !proceed { - log.Debugf("not allocating on %s, miner %s denied", st.info.ID, s.Miner.String()) + log.Debugf("not allocating on %s, miner %s %s", st.info.ID, s.Miner.String(), msg) continue } @@ -673,23 +663,13 @@ func (i *MemIndex) StorageBestAlloc(ctx context.Context, allocate storiface.Sect continue } - proceed, err := MinerFilter(p.info.AllowMiners, false, miner) + proceed, msg, err := MinerFilter(p.info.AllowMiners, p.info.DenyMiners, miner) if err != nil { return nil, err } if !proceed { - log.Debugf("not allocating on %s, miner %s not allowed", p.info.ID, miner.String()) - continue - } - - proceed, err = MinerFilter(p.info.DenyMiners, true, miner) - if err != nil { - return nil, err - } - - if !proceed { - log.Debugf("not allocating on %s, miner %s denied", p.info.ID, miner.String()) + log.Debugf("not allocating on %s, miner %s %s", p.info.ID, miner.String(), msg) continue } @@ -751,10 +731,9 @@ func (i *MemIndex) FindSector(id abi.SectorID, typ storiface.SectorFileType) ([] var _ SectorIndex = &MemIndex{} -func MinerFilter(miners []string, deny bool, miner abi.ActorID) (bool, error) { - if len(miners) > 0 { - found := false - for _, m := range miners { +func MinerFilter(allowMiners, denyMiners []string, miner abi.ActorID) (bool, string, error) { + checkMinerInList := func(minersList []string, miner abi.ActorID) (bool, error) { + for _, m := range minersList { minerIDStr := m maddr, err := address.NewFromString(minerIDStr) if err != nil { @@ -765,26 +744,24 @@ func MinerFilter(miners []string, deny bool, miner abi.ActorID) (bool, error) { return false, xerrors.Errorf("converting miner address to ID: %w", err) } if abi.ActorID(mid) == miner { - found = true - break + return true, nil } } - // If not found in list - if !found { - // If this is allowed list - if !deny { - return false, nil - } - // If this is denied list - return true, nil - } - // If found in list and - // If this is allowed list - if !deny { - return true, nil - } - // If this is denied list return false, nil } - return true, nil + + if len(allowMiners) > 0 { + found, err := checkMinerInList(allowMiners, miner) + if err != nil || !found { + return false, "not allowed", err + } + } + + if len(denyMiners) > 0 { + found, err := checkMinerInList(denyMiners, miner) + if err != nil || found { + return false, "denied", err + } + } + return true, "", nil } diff --git a/storage/paths/local.go b/storage/paths/local.go index 018537c82..340e4f279 100644 --- a/storage/paths/local.go +++ b/storage/paths/local.go @@ -511,14 +511,7 @@ func (st *Local) AcquireSector(ctx context.Context, sid storiface.SectorRef, exi if !fileType.Allowed(allowTypes, denyTypes) { return false, nil } - proceed, err := MinerFilter(allowMiners, false, miner) - if err != nil { - return false, err - } - if !proceed { - return false, nil - } - proceed, err = MinerFilter(denyMiners, true, miner) + proceed, _, err := MinerFilter(allowMiners, denyMiners, miner) if err != nil { return false, err } diff --git a/storage/sealer/storiface/filetype.go b/storage/sealer/storiface/filetype.go index dc8bebc7e..0aa28861b 100644 --- a/storage/sealer/storiface/filetype.go +++ b/storage/sealer/storiface/filetype.go @@ -8,28 +8,28 @@ import ( "github.com/filecoin-project/go-state-types/abi" ) -// FTUnsealed represents an unsealed sector file type. -// FTSealed represents a sealed sector file type. -// FTCache represents a cache sector file type. -// FTUpdate represents an update sector file type. -// FTUpdateCache represents an update cache sector file type. -// FTPiece represents a Piece Park sector file type. -// FileTypes represents the total number of file types. +// FTUnsealed represents an unsealed sector file. +// FTSealed represents a sealed sector file. +// FTCache represents a cache sector file. +// FTUpdate represents an update sector file. +// FTUpdateCache represents an update cache sector file. +// FTPiece represents a Piece Park sector file. +// FileTypes represents the total number of file. // -// The SectorFileType type is an integer type that represents different sector file types. -// It has several methods to manipulate and query the file type. -// The String method returns a string representation of the file type. -// The Strings method returns a slice of string representations of all the file types that are set in the receiver object. -// The AllSet method returns a slice of all the file types that are set in the receiver object. +// The SectorFileType type is an integer type that represents different sector file. +// It has several methods to manipulate and query the file. +// The String method returns a string representation of the file. +// The Strings method returns a slice of string representations of all the file that are set in the receiver object. +// The AllSet method returns a slice of all the file that are set in the receiver object. // The Has method checks whether a specific file type is set in the receiver object. // The SealSpaceUse method calculates the space used by the receiver object in sealing a sector of a given size. -// The SubAllowed method removes selected file types from the receiver object based on a list of allowed and denied file types. -// The Unset method removes selected file types from the receiver object. -// The AnyAllowed method checks whether any file types in the receiver object are allowed based on a list of allowed and denied file types. -// The Allowed method checks whether all file types in the receiver object are allowed based on a list of allowed and denied file types. +// The SubAllowed method removes selected file from the receiver object based on a list of allowed and denied file. +// The Unset method removes selected file from the receiver object. +// The AnyAllowed method checks whether any file in the receiver object are allowed based on a list of allowed and denied file. +// The Allowed method checks whether all file in the receiver object are allowed based on a list of allowed and denied file. // The StoreSpaceUse method calculates the space used by the receiver object in storing a sector of a given size. -// The All method returns an array that represents which file types are set in the receiver object. -// The IsNone method checks whether the receiver object represents no file types. +// The All method returns an array that represents which file are set in the receiver object. +// The IsNone method checks whether the receiver object represents no file. const ( // "regular" sectors FTUnsealed SectorFileType = 1 << iota @@ -56,14 +56,14 @@ const ( // - FTPiece: represents Piece Park sectors var PathTypes = []SectorFileType{FTUnsealed, FTSealed, FTCache, FTUpdate, FTUpdateCache, FTPiece} -// FTNone represents a sector file type of none. This constant is used in the StorageLock method to specify that a sector should not have any file types locked. +// FTNone represents a sector file type of none. This constant is used in the StorageLock method to specify that a sector should not have any file locked. // Example usage: // err := m.index.StorageLock(ctx, sector.ID, storiface.FTNone, storiface.FTSealed|storiface.FTUnsealed|storiface.FTCache) const ( FTNone SectorFileType = 0 ) -// FTAll represents the combination of all available sector file types. +// FTAll represents the combination of all available sector file. // It is a variable of type SectorFileType. // The value of FTAll is calculated by iterating over the PathTypes slice and using the |= operator to perform a bitwise OR operation on each path type. // The result is assigned to the variable out and returned. @@ -78,7 +78,7 @@ var FTAll = func() (out SectorFileType) { // FSOverheadDen represents the constant value 10, which is used to calculate the overhead in various storage space utilization calculations. const FSOverheadDen = 10 -// FSOverheadSeal is a map that represents the overheads for different SectorFileType in sealed sectors. +// FSOverheadSeal is a map that represents the overheads for different SectorFileType in sectors which are being sealed. var FSOverheadSeal = map[SectorFileType]int{ // 10x overheads FTUnsealed: FSOverheadDen, FTSealed: FSOverheadDen, @@ -93,27 +93,9 @@ var FSOverheadSeal = map[SectorFileType]int{ // 10x overheads // FsOverheadFinalized is a map that represents the finalized overhead for different types of SectorFileType. // The keys in the map are the SectorFileType values, and the values are integers representing the overhead. // It is used to calculate the storage space usage for different types of sectors, as shown in the example below: -// -// func (t SectorFileType) StoreSpaceUse(ssize abi.SectorSize) (uint64, error) { -// var need uint64 -// for _, pathType := range PathTypes { -// if !t.Has(pathType) { -// continue -// } -// -// oh, ok := FsOverheadFinalized[pathType] -// if !ok { -// return 0, xerrors.Errorf("no finalized overhead info for %s", pathType) -// } -// -// need += uint64(oh) * uint64(ssize) / FSOverheadDen -// } -// -// return need, nil -// } -// // The overhead value is retrieved from FsOverheadFinalized by using the SectorFileType value as the key. -// If the overhead value is not found in the map, an error is returned indicating that there is no finalized overhead information for the given sector type. +// If the overhead value is not found in the map, an error is returned indicating that there is no finalized +// overhead information for the given sector type. var FsOverheadFinalized = map[SectorFileType]int{ FTUnsealed: FSOverheadDen, FTSealed: FSOverheadDen, From a24028c3c038d9e83fc9d27c70cb77428f314ab1 Mon Sep 17 00:00:00 2001 From: LexLuthr Date: Thu, 4 Apr 2024 18:28:16 +0400 Subject: [PATCH 327/394] apply suggestions --- cmd/curio/tasks/tasks.go | 18 +----------------- curiosrc/ffi/task_storage.go | 3 ++- curiosrc/piece/task_park_piece.go | 4 +--- storage/paths/db_index.go | 2 +- 4 files changed, 5 insertions(+), 22 deletions(-) diff --git a/cmd/curio/tasks/tasks.go b/cmd/curio/tasks/tasks.go index 9e36a93c7..a35d3d2a5 100644 --- a/cmd/curio/tasks/tasks.go +++ b/cmd/curio/tasks/tasks.go @@ -8,8 +8,6 @@ import ( "github.com/samber/lo" "golang.org/x/xerrors" - "github.com/filecoin-project/go-address" - "github.com/filecoin-project/lotus/cmd/curio/deps" curio "github.com/filecoin-project/lotus/curiosrc" "github.com/filecoin-project/lotus/curiosrc/chainsched" @@ -39,20 +37,6 @@ func StartTasks(ctx context.Context, dependencies *deps.Deps) (*harmonytask.Task si := dependencies.Si var activeTasks []harmonytask.TaskInterface - // Get all miner address from config - var miners []address.Address - for _, ad := range cfg.Addresses { - ad := ad - for _, m := range ad.MinerAddresses { - m := m - maddr, err := address.NewFromString(m) - if err != nil { - return nil, xerrors.Errorf("failed to parse the miner address: %w", err) - } - miners = append(miners, maddr) - } - } - sender, sendTask := message.NewSender(full, full, db) activeTasks = append(activeTasks, sendTask) @@ -92,7 +76,7 @@ func StartTasks(ctx context.Context, dependencies *deps.Deps) (*harmonytask.Task { // Piece handling if cfg.Subsystems.EnableParkPiece { - parkPieceTask := piece.NewParkPieceTask(db, must.One(slrLazy.Val()), cfg.Subsystems.ParkPieceMaxTasks, miners) + parkPieceTask := piece.NewParkPieceTask(db, must.One(slrLazy.Val()), cfg.Subsystems.ParkPieceMaxTasks) cleanupPieceTask := piece.NewCleanupPieceTask(db, must.One(slrLazy.Val()), 0) activeTasks = append(activeTasks, parkPieceTask, cleanupPieceTask) } diff --git a/curiosrc/ffi/task_storage.go b/curiosrc/ffi/task_storage.go index 18973164d..4d30eaff9 100644 --- a/curiosrc/ffi/task_storage.go +++ b/curiosrc/ffi/task_storage.go @@ -10,6 +10,7 @@ import ( "github.com/filecoin-project/lotus/lib/harmony/harmonytask" "github.com/filecoin-project/lotus/lib/harmony/resources" + storagePaths "github.com/filecoin-project/lotus/storage/paths" "github.com/filecoin-project/lotus/storage/sealer/storiface" ) @@ -68,7 +69,7 @@ func (sb *SealCalls) Storage(taskToSectorRef func(taskID harmonytask.TaskID) (Se func (t *TaskStorage) HasCapacity() bool { ctx := context.Background() - paths, err := t.sc.sectors.sindex.StorageBestAlloc(ctx, t.alloc, t.ssize, t.pathType, abi.ActorID(0)) + paths, err := t.sc.sectors.sindex.StorageBestAlloc(ctx, t.alloc, t.ssize, t.pathType, storagePaths.NoMinerFilter) if err != nil { log.Errorf("finding best alloc in HasCapacity: %+v", err) return false diff --git a/curiosrc/piece/task_park_piece.go b/curiosrc/piece/task_park_piece.go index 22823057e..68a94a295 100644 --- a/curiosrc/piece/task_park_piece.go +++ b/curiosrc/piece/task_park_piece.go @@ -9,8 +9,6 @@ import ( logging "github.com/ipfs/go-log/v2" "golang.org/x/xerrors" - "github.com/filecoin-project/go-address" - "github.com/filecoin-project/lotus/curiosrc/ffi" "github.com/filecoin-project/lotus/curiosrc/seal" "github.com/filecoin-project/lotus/lib/harmony/harmonydb" @@ -34,7 +32,7 @@ type ParkPieceTask struct { max int } -func NewParkPieceTask(db *harmonydb.DB, sc *ffi.SealCalls, max int, miners []address.Address) *ParkPieceTask { +func NewParkPieceTask(db *harmonydb.DB, sc *ffi.SealCalls, max int) *ParkPieceTask { pt := &ParkPieceTask{ db: db, sc: sc, diff --git a/storage/paths/db_index.go b/storage/paths/db_index.go index 948398e33..f79e6941d 100644 --- a/storage/paths/db_index.go +++ b/storage/paths/db_index.go @@ -738,7 +738,7 @@ func (dbi *DBIndex) StorageBestAlloc(ctx context.Context, allocate storiface.Sec for _, row := range rows { // Matching with 0 as a workaround to avoid having minerID // present when calling TaskStorage.HasCapacity() - if !(miner == NoMinerFilter) { + if miner != NoMinerFilter { allowMiners := splitString(row.AllowMiners) denyMiners := splitString(row.DenyMiners) proceed, msg, err := MinerFilter(allowMiners, denyMiners, miner) From fd64e38b39c9050ced07d5556febaa02a3dcfa84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 4 Apr 2024 18:17:04 +0200 Subject: [PATCH 328/394] fix: paths/local: Fix on-disk storage accounting in new reservations (#11825) --- curiosrc/seal/task_trees.go | 2 -- storage/paths/local.go | 50 ++++++++++++++++++++++--------------- 2 files changed, 30 insertions(+), 22 deletions(-) diff --git a/curiosrc/seal/task_trees.go b/curiosrc/seal/task_trees.go index 8a178bd90..7994c354a 100644 --- a/curiosrc/seal/task_trees.go +++ b/curiosrc/seal/task_trees.go @@ -211,8 +211,6 @@ func (t *TreesTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (done } func (t *TreesTask) CanAccept(ids []harmonytask.TaskID, engine *harmonytask.TaskEngine) (*harmonytask.TaskID, error) { - // todo reserve storage - id := ids[0] return &id, nil } diff --git a/storage/paths/local.go b/storage/paths/local.go index 340e4f279..cc5789801 100644 --- a/storage/paths/local.go +++ b/storage/paths/local.go @@ -61,24 +61,25 @@ type statExistingSectorForReservation struct { ft storiface.SectorFileType } -func (p *path) stat(ls LocalStorage, newReserve ...statExistingSectorForReservation) (fsutil.FsStat, error) { +func (p *path) stat(ls LocalStorage, newReserve ...statExistingSectorForReservation) (stat fsutil.FsStat, newResvOnDisk int64, err error) { start := time.Now() - stat, err := ls.Stat(p.local) + stat, err = ls.Stat(p.local) if err != nil { - return fsutil.FsStat{}, xerrors.Errorf("stat %s: %w", p.local, err) + return fsutil.FsStat{}, 0, xerrors.Errorf("stat %s: %w", p.local, err) } stat.Reserved = p.reserved + var newReserveOnDisk int64 - accountExistingFiles := func(id abi.SectorID, fileType storiface.SectorFileType) error { + accountExistingFiles := func(id abi.SectorID, fileType storiface.SectorFileType) (int64, error) { sp := p.sectorPath(id, fileType) used, err := ls.DiskUsage(sp) if err == os.ErrNotExist { p, ferr := tempFetchDest(sp, false) if ferr != nil { - return ferr + return 0, ferr } used, err = ls.DiskUsage(p) @@ -90,18 +91,19 @@ func (p *path) stat(ls LocalStorage, newReserve ...statExistingSectorForReservat if !os.IsNotExist(err) { log.Warnf("getting disk usage of '%s': %+v", p.sectorPath(id, fileType), err) } - return nil + return 0, nil } - stat.Reserved -= used - return nil + return used, nil } for id, ft := range p.reservations { for _, fileType := range ft.AllSet() { - if err := accountExistingFiles(id, fileType); err != nil { - return fsutil.FsStat{}, err + onDisk, err := accountExistingFiles(id, fileType) + if err != nil { + return fsutil.FsStat{}, 0, err } + stat.Reserved -= onDisk } } for _, reservation := range newReserve { @@ -111,9 +113,11 @@ func (p *path) stat(ls LocalStorage, newReserve ...statExistingSectorForReservat continue } - if err := accountExistingFiles(reservation.id, fileType); err != nil { - return fsutil.FsStat{}, err + onDisk, err := accountExistingFiles(reservation.id, fileType) + if err != nil { + return fsutil.FsStat{}, 0, err } + newReserveOnDisk += onDisk } } @@ -130,7 +134,7 @@ func (p *path) stat(ls LocalStorage, newReserve ...statExistingSectorForReservat if p.maxStorage > 0 { used, err := ls.DiskUsage(p.local) if err != nil { - return fsutil.FsStat{}, err + return fsutil.FsStat{}, 0, err } stat.Max = int64(p.maxStorage) @@ -150,7 +154,7 @@ func (p *path) stat(ls LocalStorage, newReserve ...statExistingSectorForReservat log.Warnw("slow storage stat", "took", time.Now().Sub(start), "reservations", len(p.reservations)) } - return stat, err + return stat, newReserveOnDisk, err } func (p *path) sectorPath(sid abi.SectorID, fileType storiface.SectorFileType) string { @@ -198,7 +202,7 @@ func (st *Local) OpenPath(ctx context.Context, p string) error { reservations: map[abi.SectorID]storiface.SectorFileType{}, } - fst, err := out.stat(st.localStorage) + fst, _, err := out.stat(st.localStorage) if err != nil { return err } @@ -282,7 +286,7 @@ func (st *Local) Redeclare(ctx context.Context, filterId *storiface.ID, dropMiss return xerrors.Errorf("unmarshalling storage metadata for %s: %w", p.local, err) } - fst, err := p.stat(st.localStorage) + fst, _, err := p.stat(st.localStorage) if err != nil { return err } @@ -408,7 +412,7 @@ func (st *Local) reportStorage(ctx context.Context) { toReport := map[storiface.ID]storiface.HealthReport{} for id, p := range st.paths { - stat, err := p.stat(st.localStorage) + stat, _, err := p.stat(st.localStorage) r := storiface.HealthReport{Stat: stat} if err != nil { r.Err = err.Error() @@ -449,14 +453,19 @@ func (st *Local) Reserve(ctx context.Context, sid storiface.SectorRef, ft storif return nil, errPathNotFound } - stat, err := p.stat(st.localStorage, statExistingSectorForReservation{sid.ID, fileType}) + stat, resvOnDisk, err := p.stat(st.localStorage, statExistingSectorForReservation{sid.ID, fileType}) if err != nil { return nil, xerrors.Errorf("getting local storage stat: %w", err) } overhead := int64(overheadTab[fileType]) * int64(ssize) / storiface.FSOverheadDen - if stat.Available < overhead { + if overhead-resvOnDisk < 0 { + log.Errorw("negative overhead vs on-disk data", "overhead", overhead, "on-disk", resvOnDisk, "id", id, "sector", sid, "fileType", fileType) + resvOnDisk = overhead + } + + if stat.Available < overhead-resvOnDisk { return nil, storiface.Err(storiface.ErrTempAllocateSpace, xerrors.Errorf("can't reserve %d bytes in '%s' (id:%s), only %d available", overhead, p.local, id, stat.Available)) } @@ -823,7 +832,8 @@ func (st *Local) FsStat(ctx context.Context, id storiface.ID) (fsutil.FsStat, er return fsutil.FsStat{}, errPathNotFound } - return p.stat(st.localStorage) + stat, _, err := p.stat(st.localStorage) + return stat, err } func (st *Local) GenerateSingleVanillaProof(ctx context.Context, minerID abi.ActorID, si storiface.PostSectorChallenge, ppt abi.RegisteredPoStProof) ([]byte, error) { From 37c24bba4dbb05a5b93a7e36ab391acfd0e98d77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 4 Apr 2024 23:16:34 +0200 Subject: [PATCH 329/394] fix: piece: Don't return StartEport in PieceDealInfo.EndEpoch (#11832) --- storage/pipeline/piece/piece_info.go | 2 +- storage/pipeline/precommit_policy_test.go | 35 +++++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/storage/pipeline/piece/piece_info.go b/storage/pipeline/piece/piece_info.go index 7ee8f7029..48e15751a 100644 --- a/storage/pipeline/piece/piece_info.go +++ b/storage/pipeline/piece/piece_info.go @@ -147,7 +147,7 @@ func (ds *PieceDealInfo) EndEpoch() (abi.ChainEpoch, error) { default: // note - when implementing make sure to cache any dynamically computed values // todo do we want a smarter mechanism here - return ds.DealSchedule.StartEpoch, nil + return ds.DealSchedule.EndEpoch, nil } } diff --git a/storage/pipeline/precommit_policy_test.go b/storage/pipeline/precommit_policy_test.go index ec2a61ff2..6329e90d3 100644 --- a/storage/pipeline/precommit_policy_test.go +++ b/storage/pipeline/precommit_policy_test.go @@ -18,6 +18,7 @@ import ( "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/chain/actors/builtin" + "github.com/filecoin-project/lotus/chain/actors/builtin/miner" "github.com/filecoin-project/lotus/chain/actors/policy" "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/node/modules/dtypes" @@ -242,3 +243,37 @@ func TestMissingDealIsIgnored(t *testing.T) { assert.Equal(t, 547300, int(exp)) } + +func TestBasicPolicyDDO(t *testing.T) { + cfg := fakeConfigGetter(nil) + pcp := pipeline.NewBasicPreCommitPolicy(&fakeChain{ + h: abi.ChainEpoch(55), + }, cfg, 0) + + pieces := []pipeline.SafeSectorPiece{ + pipeline.SafePiece(api.SectorPiece{ + Piece: abi.PieceInfo{ + Size: abi.PaddedPieceSize(1024), + PieceCID: fakePieceCid(t), + }, + DealInfo: &piece.PieceDealInfo{ + PublishCid: nil, + DealID: abi.DealID(44), + DealSchedule: piece.DealSchedule{ + StartEpoch: abi.ChainEpoch(100_000), + EndEpoch: abi.ChainEpoch(1500_000), + }, + PieceActivationManifest: &miner.PieceActivationManifest{ + Size: 0, + VerifiedAllocationKey: nil, + Notify: nil, + }, + }, + }), + } + + exp, err := pcp.Expiration(context.Background(), pieces...) + require.NoError(t, err) + + assert.Equal(t, abi.ChainEpoch(1500_000), exp) +} From 385fb7a054c4f366721b868a0e52c5436d05e1d9 Mon Sep 17 00:00:00 2001 From: Phi-rjan Date: Fri, 5 Apr 2024 10:37:58 +0200 Subject: [PATCH 330/394] fix: test: TestForkPreMigration hanging when env-var is set (#11838) * Unset env-var for TestForkPreMigration Unset LOTUS_DISABLE_PRE_MIGRATIONS env-var for TestForkPreMigration test, so it does not hang * Err-check to make lint happy Err-check to make lint happy --- chain/stmgr/forks_test.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/chain/stmgr/forks_test.go b/chain/stmgr/forks_test.go index bf8793488..8c0227553 100644 --- a/chain/stmgr/forks_test.go +++ b/chain/stmgr/forks_test.go @@ -375,6 +375,20 @@ func testForkRefuseCall(t *testing.T, nullsBefore, nullsAfter int) { } func TestForkPreMigration(t *testing.T) { + // Backup the original value of the DISABLE_PRE_MIGRATIONS environment variable + originalValue, _ := os.LookupEnv("LOTUS_DISABLE_PRE_MIGRATIONS") + + // Unset the DISABLE_PRE_MIGRATIONS environment variable for the test + if err := os.Unsetenv("LOTUS_DISABLE_PRE_MIGRATIONS"); err != nil { + t.Fatalf("failed to unset LOTUS_DISABLE_PRE_MIGRATIONS: %v", err) + } + + // Restore the original DISABLE_PRE_MIGRATIONS environment variable at the end of the test + defer func() { + if err := os.Setenv("LOTUS_DISABLE_PRE_MIGRATIONS", originalValue); err != nil { + t.Fatalf("failed to restore LOTUS_DISABLE_PRE_MIGRATIONS: %v", err) + } + }() //stm: @CHAIN_GEN_NEXT_TIPSET_001, //stm: @CHAIN_STATE_RESOLVE_TO_KEY_ADDR_001, @CHAIN_STATE_SET_VM_CONSTRUCTOR_001 logging.SetAllLoggers(logging.LevelInfo) From 485050ee7c14417659c0fb49e1bf26c6819f0b20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 4 Apr 2024 12:12:35 +0200 Subject: [PATCH 331/394] fix: harmony: Try other tasks when storage claim fails --- lib/harmony/harmonytask/task_type_handler.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/lib/harmony/harmonytask/task_type_handler.go b/lib/harmony/harmonytask/task_type_handler.go index 2b2521949..7aecd380f 100644 --- a/lib/harmony/harmonytask/task_type_handler.go +++ b/lib/harmony/harmonytask/task_type_handler.go @@ -85,6 +85,7 @@ top: } // 3. What does the impl say? +canAcceptAgain: tID, err := h.CanAccept(ids, h.TaskEngine) if err != nil { log.Error(err) @@ -100,6 +101,18 @@ top: if h.TaskTypeDetails.Cost.Storage != nil { if err = h.TaskTypeDetails.Cost.Storage.Claim(int(*tID)); err != nil { log.Infow("did not accept task", "task_id", strconv.Itoa(int(*tID)), "reason", "storage claim failed", "name", h.Name, "error", err) + + if len(ids) > 1 { + var tryAgain = make([]TaskID, 0, len(ids)-1) + for _, id := range ids { + if id != *tID { + tryAgain = append(tryAgain, id) + } + } + ids = tryAgain + goto canAcceptAgain + } + return false } releaseStorage = func() { From c827cf1c0e6bf6eedc816d9c4bfb634194b1f3ff Mon Sep 17 00:00:00 2001 From: LexLuthr <88259624+LexLuthr@users.noreply.github.com> Date: Fri, 5 Apr 2024 16:23:41 +0400 Subject: [PATCH 332/394] fix: sealing: improve gasEstimate logging (#11840) * improve gasEstimate logging * use fmt for hex --- storage/pipeline/commit_batch.go | 8 ++++---- storage/pipeline/precommit_batch.go | 2 +- storage/pipeline/utils.go | 14 +++++++++++++- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/storage/pipeline/commit_batch.go b/storage/pipeline/commit_batch.go index ecb8569d0..e7d89076f 100644 --- a/storage/pipeline/commit_batch.go +++ b/storage/pipeline/commit_batch.go @@ -449,9 +449,9 @@ func (b *CommitBatcher) processBatchV2(cfg sealiface.Config, sectors []abi.Secto _, err = simulateMsgGas(b.mctx, b.api, from, b.maddr, builtin.MethodsMiner.ProveCommitSectors3, needFunds, maxFee, enc.Bytes()) if err != nil && (!api.ErrorIsIn(err, []error{&api.ErrOutOfGas{}}) || len(sectors) < miner.MinAggregatedSectors*2) { - log.Errorf("simulating CommitBatch message failed (%x): %s", enc.Bytes(), err) + log.Errorf("simulating CommitBatch %s", err) res.Error = err.Error() - return []sealiface.CommitBatchRes{res}, xerrors.Errorf("simulating CommitBatch message failed: %w", err) + return []sealiface.CommitBatchRes{res}, xerrors.Errorf("simulating CommitBatch %w", err) } msgTooLarge := len(enc.Bytes()) > (messagepool.MaxMessageSize - 128) @@ -590,9 +590,9 @@ func (b *CommitBatcher) processBatchV1(cfg sealiface.Config, sectors []abi.Secto _, err = simulateMsgGas(b.mctx, b.api, from, b.maddr, builtin.MethodsMiner.ProveCommitAggregate, needFunds, maxFee, enc.Bytes()) if err != nil && (!api.ErrorIsIn(err, []error{&api.ErrOutOfGas{}}) || len(sectors) < miner.MinAggregatedSectors*2) { - log.Errorf("simulating CommitBatch message failed (%x): %s", enc.Bytes(), err) + log.Errorf("simulating CommitBatch %s", err) res.Error = err.Error() - return []sealiface.CommitBatchRes{res}, xerrors.Errorf("simulating CommitBatch message failed: %w", err) + return []sealiface.CommitBatchRes{res}, xerrors.Errorf("simulating CommitBatch %w", err) } // If we're out of gas, split the batch in half and evaluate again diff --git a/storage/pipeline/precommit_batch.go b/storage/pipeline/precommit_batch.go index 099988010..55bead590 100644 --- a/storage/pipeline/precommit_batch.go +++ b/storage/pipeline/precommit_batch.go @@ -291,7 +291,7 @@ func (b *PreCommitBatcher) processPreCommitBatch(cfg sealiface.Config, bf abi.To if err != nil && (!api.ErrorIsIn(err, []error{&api.ErrOutOfGas{}}) || len(entries) == 1) { res.Error = err.Error() - return []sealiface.PreCommitBatchRes{res}, xerrors.Errorf("simulating PreCommitBatch message failed: %w", err) + return []sealiface.PreCommitBatchRes{res}, xerrors.Errorf("simulating PreCommitBatch %w", err) } // If we're out of gas, split the batch in half and evaluate again diff --git a/storage/pipeline/utils.go b/storage/pipeline/utils.go index 4b99a5bea..ac519b6ac 100644 --- a/storage/pipeline/utils.go +++ b/storage/pipeline/utils.go @@ -1,7 +1,9 @@ package sealing import ( + "bytes" "context" + "fmt" "math/bits" "github.com/ipfs/go-cid" @@ -105,7 +107,17 @@ func simulateMsgGas(ctx context.Context, sa interface { Params: params, } - return sa.GasEstimateMessageGas(ctx, &msg, nil, types.EmptyTSK) + var b bytes.Buffer + err := msg.MarshalCBOR(&b) + if err != nil { + return nil, xerrors.Errorf("failed to unmarshal the signed message: %w", err) + } + + gmsg, err := sa.GasEstimateMessageGas(ctx, &msg, nil, types.EmptyTSK) + if err != nil { + err = fmt.Errorf("message %x failed: %w", b.Bytes(), err) + } + return gmsg, err } func sendMsg(ctx context.Context, sa interface { From 83421e2742d41b8b71dc0b68cbafa534d6028518 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 5 Apr 2024 18:39:49 +0200 Subject: [PATCH 333/394] fix: lmcli: make 'sectors list' DDO-aware (#11839) * fix: lmcli: make 'sectors list' DDO-aware * lmcli: Add ddo info to sectors status --- cli/spcli/sectors.go | 22 ++++++++++++++++++++++ cmd/lotus-miner/sectors.go | 9 +++++++++ 2 files changed, 31 insertions(+) diff --git a/cli/spcli/sectors.go b/cli/spcli/sectors.go index 5b8bf4139..1b230ce04 100644 --- a/cli/spcli/sectors.go +++ b/cli/spcli/sectors.go @@ -11,6 +11,7 @@ import ( "sort" "strconv" "strings" + "sync" "time" "github.com/fatih/color" @@ -109,6 +110,27 @@ func SectorsStatusCmd(getActorAddress ActorAddressGetter, getOnDiskInfo OnDiskIn fmt.Printf("OnTime:\t\t%v\n", status.OnTime) fmt.Printf("Early:\t\t%v\n", status.Early) + var pamsHeaderOnce sync.Once + + for pi, piece := range status.Pieces { + if piece.DealInfo == nil { + continue + } + if piece.DealInfo.PieceActivationManifest == nil { + continue + } + pamsHeaderOnce.Do(func() { + fmt.Printf("\nPiece Activation Manifests\n") + }) + + pam := piece.DealInfo.PieceActivationManifest + + fmt.Printf("Piece %d: %s %s verif-alloc:%+v\n", pi, pam.CID, types.SizeStr(types.NewInt(uint64(pam.Size))), pam.VerifiedAllocationKey) + for ni, notification := range pam.Notify { + fmt.Printf("\tNotify %d: %s (%x)\n", ni, notification.Address, notification.Payload) + } + } + } else { onChainInfo = true } diff --git a/cmd/lotus-miner/sectors.go b/cmd/lotus-miner/sectors.go index a3ffb8335..d0c13d333 100644 --- a/cmd/lotus-miner/sectors.go +++ b/cmd/lotus-miner/sectors.go @@ -310,6 +310,13 @@ var sectorsListCmd = &cli.Command{ } } + var pams int + for _, p := range st.Pieces { + if p.DealInfo != nil && p.DealInfo.PieceActivationManifest != nil { + pams++ + } + } + exp := st.Expiration if st.OnTime > 0 && st.OnTime < exp { exp = st.OnTime // Can be different when the sector was CC upgraded @@ -324,6 +331,8 @@ var sectorsListCmd = &cli.Command{ if deals > 0 { m["Deals"] = color.GreenString("%d", deals) + } else if pams > 0 { + m["Deals"] = color.MagentaString("DDO:%d", pams) } else { m["Deals"] = color.BlueString("CC") if st.ToUpgrade { From 8163e6a91e9693456eef9758657db165e88a04e3 Mon Sep 17 00:00:00 2001 From: Piotr Galar Date: Fri, 5 Apr 2024 17:45:04 +0100 Subject: [PATCH 334/394] ci: cancel in progress runs on pull requests only (#11842) --- .github/workflows/build.yml | 2 +- .github/workflows/check.yml | 2 +- .github/workflows/docker.yml | 4 ---- .github/workflows/release.yml | 4 ---- .github/workflows/test.yml | 2 +- 5 files changed, 3 insertions(+), 11 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ce46a4a5f..d7dd59e14 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -14,7 +14,7 @@ defaults: concurrency: group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true + cancel-in-progress: ${{ github.event_name == 'pull_request' }} permissions: {} diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 7a77dc70b..8d19589f7 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -14,7 +14,7 @@ defaults: concurrency: group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true + cancel-in-progress: ${{ github.event_name == 'pull_request' }} permissions: {} diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 8e0ca57a5..9e6bf59f1 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -15,10 +15,6 @@ defaults: run: shell: bash -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - permissions: {} jobs: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 35e139b7d..dd8a9cfbc 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,10 +13,6 @@ defaults: run: shell: bash -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - permissions: {} jobs: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ad274dfa5..647806e0f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,7 +14,7 @@ defaults: concurrency: group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true + cancel-in-progress: ${{ github.event_name == 'pull_request' }} permissions: {} From dd6fef807b3f4ef1e4b760658f42001234adb529 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 5 Apr 2024 20:58:00 +0200 Subject: [PATCH 335/394] fix: sealing: Use PRU3 After Nv22 (#11831) --- storage/pipeline/states_replica_update.go | 8 +++- storage/pipeline/states_sealing.go | 45 ++++++++++++----------- 2 files changed, 31 insertions(+), 22 deletions(-) diff --git a/storage/pipeline/states_replica_update.go b/storage/pipeline/states_replica_update.go index 380078e75..a0d92891c 100644 --- a/storage/pipeline/states_replica_update.go +++ b/storage/pipeline/states_replica_update.go @@ -11,6 +11,7 @@ import ( "github.com/filecoin-project/go-state-types/big" "github.com/filecoin-project/go-state-types/builtin" "github.com/filecoin-project/go-state-types/exitcode" + "github.com/filecoin-project/go-state-types/network" "github.com/filecoin-project/go-statemachine" "github.com/filecoin-project/lotus/api" @@ -170,7 +171,12 @@ func (m *Sealing) handleSubmitReplicaUpdate(ctx statemachine.Context, sector Sec // figure out message type - pams, deals, err := m.processPieces(ctx.Context(), sector) + nv, err := m.Api.StateNetworkVersion(ctx.Context(), ts.Key()) + if err != nil { + log.Errorf("failed to get network version: %+v", err) + } + + pams, deals, err := m.processPieces(ctx.Context(), sector, nv >= network.Version22) if err != nil { log.Errorf("failed to process pieces: %+v", err) return ctx.Send(SectorSubmitReplicaUpdateFailed{}) diff --git a/storage/pipeline/states_sealing.go b/storage/pipeline/states_sealing.go index 4f40ac7c7..81ee85853 100644 --- a/storage/pipeline/states_sealing.go +++ b/storage/pipeline/states_sealing.go @@ -748,30 +748,33 @@ func (m *Sealing) handleSubmitCommit(ctx statemachine.Context, sector SectorInfo // processPieces returns either: // - a list of piece activation manifests // - a list of deal IDs, if all non-filler pieces are deal-id pieces -func (m *Sealing) processPieces(ctx context.Context, sector SectorInfo) ([]miner.PieceActivationManifest, []abi.DealID, error) { +func (m *Sealing) processPieces(ctx context.Context, sector SectorInfo, forceDDO bool) ([]miner.PieceActivationManifest, []abi.DealID, error) { pams := make([]miner.PieceActivationManifest, 0, len(sector.Pieces)) dealIDs := make([]abi.DealID, 0, len(sector.Pieces)) - var hasDDO bool + hasDDO := forceDDO - for _, piece := range sector.Pieces { - piece := piece + if !forceDDO { + // if not forcing DDO, check if we have any DDO pieces + for _, piece := range sector.Pieces { + piece := piece - // first figure out if this is a ddo sector - err := piece.handleDealInfo(handleDealInfoParams{ - FillerHandler: func(info UniversalPieceInfo) error { - // Fillers are implicit (todo review: Are they??) - return nil - }, - BuiltinMarketHandler: func(info UniversalPieceInfo) error { - return nil - }, - DDOHandler: func(info UniversalPieceInfo) error { - hasDDO = true - return nil - }, - }) - if err != nil { - return nil, nil, xerrors.Errorf("handleDealInfo: %w", err) + // first figure out if this is a ddo sector + err := piece.handleDealInfo(handleDealInfoParams{ + FillerHandler: func(info UniversalPieceInfo) error { + // Fillers are implicit (todo review: Are they??) + return nil + }, + BuiltinMarketHandler: func(info UniversalPieceInfo) error { + return nil + }, + DDOHandler: func(info UniversalPieceInfo) error { + hasDDO = true + return nil + }, + }) + if err != nil { + return nil, nil, xerrors.Errorf("handleDealInfo: %w", err) + } } } for _, piece := range sector.Pieces { @@ -847,7 +850,7 @@ func (m *Sealing) handleSubmitCommitAggregate(ctx statemachine.Context, sector S return ctx.Send(SectorCommitFailed{xerrors.Errorf("sector had nil commR or commD")}) } - pams, dealIDs, err := m.processPieces(ctx.Context(), sector) + pams, dealIDs, err := m.processPieces(ctx.Context(), sector, false) if err != nil { return err } From e68b762b40cd650410d5b203c578e196f32427d9 Mon Sep 17 00:00:00 2001 From: beck <34204218+beck-8@users.noreply.github.com> Date: Mon, 8 Apr 2024 16:06:34 +0800 Subject: [PATCH 336/394] fix rDataCap datacap (#11811) --- cli/filplus.go | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/cli/filplus.go b/cli/filplus.go index b8e887498..7b876ea32 100644 --- a/cli/filplus.go +++ b/cli/filplus.go @@ -1311,21 +1311,6 @@ func CreateExtendClaimMsg(ctx context.Context, api api.FullNode, pcm map[verifre } if len(newClaims) > 0 { - // Get datacap balance - aDataCap, err := api.StateVerifiedClientStatus(ctx, wallet, types.EmptyTSK) - if err != nil { - return nil, err - } - - if aDataCap == nil { - return nil, xerrors.Errorf("wallet %s does not have any datacap", wallet) - } - - // Check that we have enough data cap to make the allocation - if rDataCap.GreaterThan(big.NewInt(aDataCap.Int64())) { - return nil, xerrors.Errorf("requested datacap %s is greater then the available datacap %s", rDataCap, aDataCap) - } - if !assumeYes { out := fmt.Sprintf("Some of the specified allocation have a different client address and will require %d Datacap to extend. Proceed? Yes [Y/y] / No [N/n], Ctrl+C (^C) to exit", rDataCap.Int) validate := func(input string) error { @@ -1361,6 +1346,21 @@ func CreateExtendClaimMsg(ctx context.Context, api api.FullNode, pcm map[verifre } } + // Get datacap balance + aDataCap, err := api.StateVerifiedClientStatus(ctx, wallet, types.EmptyTSK) + if err != nil { + return nil, err + } + + if aDataCap == nil { + return nil, xerrors.Errorf("wallet %s does not have any datacap", wallet) + } + + // Check that we have enough data cap to make the allocation + if rDataCap.GreaterThan(big.NewInt(aDataCap.Int64())) { + return nil, xerrors.Errorf("requested datacap %s is greater then the available datacap %s", rDataCap, aDataCap) + } + // Create a map of just keys, so we can easily batch based on the numeric keys keys := make([]verifregtypes13.ClaimExtensionRequest, 0, len(newClaims)) for k := range newClaims { From 23b61c305ad15bee3d87e1eb6a73e76ce1f23002 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 9 Apr 2024 13:20:59 -0700 Subject: [PATCH 337/394] test: eth: wait longer for chain to settle in block hash test (#11863) I shrunk this down to 10 when I last deflaked this test, but I was too optimistic. I'm bringing this back up to 50. See https://gist.github.com/Stebalien/701039637361b3b9cb328d7c2d61beda --- itests/eth_block_hash_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/itests/eth_block_hash_test.go b/itests/eth_block_hash_test.go index 927b64fc5..e7da435ba 100644 --- a/itests/eth_block_hash_test.go +++ b/itests/eth_block_hash_test.go @@ -55,7 +55,7 @@ func TestEthBlockHashesCorrect_MultiBlockTipset(t *testing.T) { ctx := context.Background() // let the chain run a little bit longer to minimise the chance of reorgs - n2.WaitTillChain(ctx, kit.HeightAtLeast(head.Height()+10)) + n2.WaitTillChain(ctx, kit.HeightAtLeast(head.Height()+50)) tsk := head.Key() for i := 1; i <= int(head.Height()); i++ { From 6443afa2bb133f45a791a2277dd8cad88d6324bf Mon Sep 17 00:00:00 2001 From: Aarsh Shah Date: Wed, 10 Apr 2024 00:32:02 +0400 Subject: [PATCH 338/394] fix: api: fix EthSubscribe tipsets off by one (#11858) Eth subscribe tipsets API should only return tipsets that have been executed. We do this by only returning the parent tipset of the latest tipset received by ETH Subscribe from it's TipSetFilter subscription. Closes #11807 Subsumes #11816 --- itests/eth_filter_test.go | 35 +++++++++++++++++++++++++++++++++++ node/impl/full/eth_events.go | 25 +++++++++++++++++++++---- 2 files changed, 56 insertions(+), 4 deletions(-) diff --git a/itests/eth_filter_test.go b/itests/eth_filter_test.go index 9212e60fc..d77a0ce14 100644 --- a/itests/eth_filter_test.go +++ b/itests/eth_filter_test.go @@ -137,6 +137,41 @@ func TestEthNewPendingTransactionFilter(t *testing.T) { } } +func TestEthNewHeadsSubSimple(t *testing.T) { + require := require.New(t) + + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + kit.QuietAllLogsExcept("events", "messagepool") + + client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC(), kit.WithEthRPC()) + ens.InterconnectAll().BeginMining(10 * time.Millisecond) + + // install filter + subId, err := client.EthSubscribe(ctx, res.Wrap[jsonrpc.RawParams](json.Marshal(ethtypes.EthSubscribeParams{EventType: "newHeads"})).Assert(require.NoError)) + require.NoError(err) + + err = client.EthSubRouter.AddSub(ctx, subId, func(ctx context.Context, resp *ethtypes.EthSubscriptionResponse) error { + rs := *resp + block, ok := rs.Result.(map[string]interface{}) + require.True(ok) + blockNumber, ok := block["number"].(string) + require.True(ok) + + blk, err := client.EthGetBlockByNumber(ctx, blockNumber, false) + require.NoError(err) + require.NotNil(blk) + fmt.Printf("block: %v\n", blk) + // block hashes should match + require.Equal(block["hash"], blk.Hash.String()) + + return nil + }) + require.NoError(err) + time.Sleep(2 * time.Second) +} + func TestEthNewPendingTransactionSub(t *testing.T) { require := require.New(t) diff --git a/node/impl/full/eth_events.go b/node/impl/full/eth_events.go index 063590d8d..7baba1e81 100644 --- a/node/impl/full/eth_events.go +++ b/node/impl/full/eth_events.go @@ -254,6 +254,8 @@ type ethSubscription struct { sendQueueLen int toSend *queue.Queue[[]byte] sendCond chan struct{} + + lastSentTipset *types.TipSetKey } func (e *ethSubscription) addFilter(ctx context.Context, f filter.Filter) { @@ -341,12 +343,27 @@ func (e *ethSubscription) start(ctx context.Context) { e.send(ctx, r) } case *types.TipSet: - ev, err := newEthBlockFromFilecoinTipSet(ctx, vt, true, e.Chain, e.StateAPI) - if err != nil { - break + // Skip processing for tipset at epoch 0 as it has no parent + if vt.Height() == 0 { + continue + } + // Check if the parent has already been processed + parentTipSetKey := vt.Parents() + if e.lastSentTipset != nil && (*e.lastSentTipset) == parentTipSetKey { + continue + } + parentTipSet, loadErr := e.Chain.LoadTipSet(ctx, parentTipSetKey) + if loadErr != nil { + log.Warnw("failed to load parent tipset", "tipset", parentTipSetKey, "error", loadErr) + continue + } + ethBlock, ethBlockErr := newEthBlockFromFilecoinTipSet(ctx, parentTipSet, true, e.Chain, e.StateAPI) + if ethBlockErr != nil { + continue } - e.send(ctx, ev) + e.send(ctx, ethBlock) + e.lastSentTipset = &parentTipSetKey case *types.SignedMessage: // mpool txid evs, err := ethFilterResultFromMessages([]*types.SignedMessage{vt}) if err != nil { From 0d2942c4a4017206430bd0b738c7d1400c9b4887 Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Thu, 11 Apr 2024 00:00:10 +1000 Subject: [PATCH 339/394] fix(events): properly decorate events db errors (#11856) --- node/modules/actorevent.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/node/modules/actorevent.go b/node/modules/actorevent.go index d92da1940..78ac5b18b 100644 --- a/node/modules/actorevent.go +++ b/node/modules/actorevent.go @@ -6,6 +6,7 @@ import ( "time" "go.uber.org/fx" + "golang.org/x/xerrors" "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/abi" @@ -105,7 +106,7 @@ func EventFilterManager(cfg config.EventsConfig) func(helpers.MetricsCtx, repo.L if cfg.DatabasePath == "" { sqlitePath, err := r.SqlitePath() if err != nil { - return nil, err + return nil, xerrors.Errorf("failed to resolve event index database path: %w", err) } dbPath = filepath.Join(sqlitePath, "events.db") } else { @@ -115,7 +116,7 @@ func EventFilterManager(cfg config.EventsConfig) func(helpers.MetricsCtx, repo.L var err error eventIndex, err = filter.NewEventIndex(ctx, dbPath, chainapi.Chain) if err != nil { - return nil, err + return nil, xerrors.Errorf("failed to initialize event index database: %w", err) } lc.Append(fx.Hook{ From 6d9bb2e0bf7dcc68ee4f6f4faf40a12fc3c65765 Mon Sep 17 00:00:00 2001 From: wersfeds Date: Wed, 10 Apr 2024 22:01:29 +0800 Subject: [PATCH 340/394] chore: fix typos (#11848) --- chain/consensus/filcns/upgrades.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/chain/consensus/filcns/upgrades.go b/chain/consensus/filcns/upgrades.go index 3b7afa286..eaa4f3b20 100644 --- a/chain/consensus/filcns/upgrades.go +++ b/chain/consensus/filcns/upgrades.go @@ -1893,7 +1893,7 @@ func UpgradeActorsV12(ctx context.Context, sm *stmgr.StateManager, cache stmgr.M } newRoot, err := upgradeActorsV12Common(ctx, sm, cache, root, epoch, ts, config) if err != nil { - return cid.Undef, xerrors.Errorf("migrating actors v11 state: %w", err) + return cid.Undef, xerrors.Errorf("migrating actors v12 state: %w", err) } return newRoot, nil } @@ -2210,7 +2210,7 @@ func UpgradeActorsV13(ctx context.Context, sm *stmgr.StateManager, cache stmgr.M } newRoot, err := upgradeActorsV13Common(ctx, sm, cache, root, epoch, ts, config) if err != nil { - return cid.Undef, xerrors.Errorf("migrating actors v11 state: %w", err) + return cid.Undef, xerrors.Errorf("migrating actors v13 state: %w", err) } return newRoot, nil } From a67e7adf0499e15c940d82a8e5d63c6da1883bfc Mon Sep 17 00:00:00 2001 From: Nagaprasad V R <81755170+Nagaprasadvr@users.noreply.github.com> Date: Wed, 10 Apr 2024 19:46:04 +0530 Subject: [PATCH 341/394] refactor: cli: Avoid disputing WindowedPoSt messages when the target miner has no balance to pay rewards (#11800) In https://github.com/filecoin-project/lotus/issues/11703, a user came across an edge case where an SP: 1. Submitted a bad PoSt. 2. Terminated their sectors. 3. Withdrew all funds (after paying all fees). This left the SP's miner actor with a bad proof but without any funds to pay for disputes. On the other hand, given that the sectors have been terminated and termination fees were already paid we don't really care about fining the SP for the bad proofs anyways. But... we still don't want to submit dispute messages in this case because doing so isn't free and doesn't really serve a purpose. So add a check to fetch miner wallet balance and only send disputing messages if balance is non zero. fixes https://github.com/filecoin-project/lotus/issues/11715 --- cli/disputer.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/cli/disputer.go b/cli/disputer.go index de3f50324..f6680daec 100644 --- a/cli/disputer.go +++ b/cli/disputer.go @@ -361,6 +361,15 @@ var disputerStartCmd = &cli.Command{ // for a given miner, index, and maxPostIndex, tries to dispute posts from 0...postsSnapshotted-1 // returns a list of DisputeWindowedPoSt msgs that are expected to succeed if sent func makeDisputeWindowedPosts(ctx context.Context, api v0api.FullNode, dl minerDeadline, postsSnapshotted uint64, sender address.Address) ([]*types.Message, error) { + // CHECK: if miner waller balance is zero then skip sending dispute message + walletBalance, err := api.WalletBalance(ctx, dl.miner) + if err != nil { + return nil, xerrors.Errorf("failed to get wallet balance while checking to send dispute messages to miner %w: %w", dl.miner, err) + } + if walletBalance.IsZero() { + disputeLog.Warnw("wallet balance is zero, skipping dispute message", "wallet", dl.miner) + return nil, nil + } disputes := make([]*types.Message, 0) for i := uint64(0); i < postsSnapshotted; i++ { From afa9032833b2a76c9be665af21c7a656865c20e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 10 Apr 2024 23:59:23 +0200 Subject: [PATCH 342/394] fix: spcli: add reference to the terminate command (#11851) * fix: spcli: add reference to the terminate command * make gen --- cmd/sptool/sector.go | 2 +- documentation/en/cli-sptool.md | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/cmd/sptool/sector.go b/cmd/sptool/sector.go index b8e003c99..8f341b5cc 100644 --- a/cmd/sptool/sector.go +++ b/cmd/sptool/sector.go @@ -36,7 +36,7 @@ var sectorsCmd = &cli.Command{ spcli.SectorsCheckExpireCmd(SPTActorGetter), sectorsExpiredCmd, // in-house b/c chain-only is so different spcli.SectorsExtendCmd(SPTActorGetter), - //spcli.SectorsTerminateCmd(SPTActorGetter), // Could not trace through the state-machine + spcli.TerminateSectorCmd(SPTActorGetter), spcli.SectorsCompactPartitionsCmd(SPTActorGetter), }} diff --git a/documentation/en/cli-sptool.md b/documentation/en/cli-sptool.md index a9bd90897..e116f759f 100644 --- a/documentation/en/cli-sptool.md +++ b/documentation/en/cli-sptool.md @@ -275,6 +275,7 @@ COMMANDS: check-expire Inspect expiring sectors expired Get or cleanup expired sectors extend Extend expiring sectors while not exceeding each sector's max life + terminate Forcefully terminate a sector (WARNING: This means losing power and pay a one-time termination penalty(including collateral) for the terminated sector) compact-partitions removes dead sectors from partitions and reduces the number of partitions used if possible help, h Shows a list of commands or help for one command @@ -372,6 +373,21 @@ OPTIONS: --help, -h show help ``` +### sptool sectors terminate +``` +NAME: + sptool sectors terminate - Forcefully terminate a sector (WARNING: This means losing power and pay a one-time termination penalty(including collateral) for the terminated sector) + +USAGE: + sptool sectors terminate [command options] [sectorNum1 sectorNum2 ...] + +OPTIONS: + --actor value specify the address of miner actor + --really-do-it pass this flag if you know what you are doing (default: false) + --from value specify the address to send the terminate message from + --help, -h show help +``` + ### sptool sectors compact-partitions ``` NAME: From d7d849cf20f7885b6ea77df095b5e3ddad24195b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 11 Apr 2024 18:30:53 +0200 Subject: [PATCH 343/394] feat: curioweb: Sector info page (#11846) * feat: curioweb: Sector info page * Address review --- curiosrc/web/hapi/robust_rpc.go | 9 + curiosrc/web/hapi/routes.go | 3 + curiosrc/web/hapi/simpleinfo.go | 256 ++++++++++++++++++ .../web/hapi/simpleinfo_pipeline_porep.go | 214 +++++++-------- .../hapi/web/pipeline_porep_sectors.gohtml | 246 ++++++++--------- curiosrc/web/static/main.css | 50 ++++ curiosrc/web/static/pipeline_porep.html | 51 ---- storage/paths/db_index.go | 8 +- 8 files changed, 553 insertions(+), 284 deletions(-) diff --git a/curiosrc/web/hapi/robust_rpc.go b/curiosrc/web/hapi/robust_rpc.go index e9914bfa6..c10b43a03 100644 --- a/curiosrc/web/hapi/robust_rpc.go +++ b/curiosrc/web/hapi/robust_rpc.go @@ -4,10 +4,18 @@ import ( "context" "time" + lru "github.com/hashicorp/golang-lru/v2" + blocks "github.com/ipfs/go-block-format" + "github.com/filecoin-project/lotus/api/client" + "github.com/filecoin-project/lotus/blockstore" + "github.com/filecoin-project/lotus/chain/store" cliutil "github.com/filecoin-project/lotus/cli/util" + "github.com/filecoin-project/lotus/lib/must" ) +var ChainBlockCache = must.One(lru.New[blockstore.MhString, blocks.Block](4096)) + func (a *app) watchRpc() { ticker := time.NewTicker(watchInterval) for { @@ -84,6 +92,7 @@ func (a *app) updateRpc(ctx context.Context) error { }() a.workingApi = v1api + a.stor = store.ActorStore(ctx, blockstore.NewReadCachedBlockstore(blockstore.NewAPIBlockstore(a.workingApi), ChainBlockCache)) } } diff --git a/curiosrc/web/hapi/routes.go b/curiosrc/web/hapi/routes.go index 430b02efb..61724ec0a 100644 --- a/curiosrc/web/hapi/routes.go +++ b/curiosrc/web/hapi/routes.go @@ -41,6 +41,9 @@ func Routes(r *mux.Router, deps *deps.Deps) error { // node info page r.HandleFunc("/node/{id}", a.nodeInfo) + + // sector info page + r.HandleFunc("/sector/{sp}/{id}", a.sectorInfo) return nil } diff --git a/curiosrc/web/hapi/simpleinfo.go b/curiosrc/web/hapi/simpleinfo.go index 5c353372e..3bab1c46c 100644 --- a/curiosrc/web/hapi/simpleinfo.go +++ b/curiosrc/web/hapi/simpleinfo.go @@ -8,15 +8,23 @@ import ( "os" "sort" "strconv" + "strings" "sync" "text/template" "time" "github.com/gorilla/mux" + "github.com/samber/lo" "golang.org/x/xerrors" + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/lotus/api/v1api" + "github.com/filecoin-project/lotus/chain/actors/adt" "github.com/filecoin-project/lotus/lib/harmony/harmonydb" + "github.com/filecoin-project/lotus/lib/must" + "github.com/filecoin-project/lotus/storage/paths" + "github.com/filecoin-project/lotus/storage/sealer/storiface" ) type app struct { @@ -25,6 +33,7 @@ type app struct { rpcInfoLk sync.Mutex workingApi v1api.FullNode + stor adt.Store actorInfoLk sync.Mutex actorInfos []actorInfo @@ -128,6 +137,253 @@ func (a *app) nodeInfo(writer http.ResponseWriter, request *http.Request) { a.executePageTemplate(writer, "node_info", "Node Info", mi) } +func (a *app) sectorInfo(w http.ResponseWriter, r *http.Request) { + params := mux.Vars(r) + + id, ok := params["id"] + if !ok { + http.Error(w, "missing id", http.StatusBadRequest) + return + } + + intid, err := strconv.ParseInt(id, 10, 64) + if err != nil { + http.Error(w, "invalid id", http.StatusBadRequest) + return + } + + sp, ok := params["sp"] + if !ok { + http.Error(w, "missing sp", http.StatusBadRequest) + return + } + + maddr, err := address.NewFromString(sp) + if err != nil { + http.Error(w, "invalid sp", http.StatusBadRequest) + return + } + + spid, err := address.IDFromAddress(maddr) + if err != nil { + http.Error(w, "invalid sp", http.StatusBadRequest) + return + } + + ctx := r.Context() + var tasks []PipelineTask + + err = a.db.Select(ctx, &tasks, `SELECT + sp_id, sector_number, + create_time, + task_id_sdr, after_sdr, + task_id_tree_d, after_tree_d, + task_id_tree_c, after_tree_c, + task_id_tree_r, after_tree_r, + task_id_precommit_msg, after_precommit_msg, + after_precommit_msg_success, seed_epoch, + task_id_porep, porep_proof, after_porep, + task_id_finalize, after_finalize, + task_id_move_storage, after_move_storage, + task_id_commit_msg, after_commit_msg, + after_commit_msg_success, + failed, failed_reason + FROM sectors_sdr_pipeline WHERE sp_id = $1 AND sector_number = $2`, spid, intid) + if err != nil { + http.Error(w, xerrors.Errorf("failed to fetch pipeline task info: %w", err).Error(), http.StatusInternalServerError) + return + } + + if len(tasks) == 0 { + http.Error(w, "sector not found", http.StatusInternalServerError) + return + } + + head, err := a.workingApi.ChainHead(ctx) + if err != nil { + http.Error(w, xerrors.Errorf("failed to fetch chain head: %w", err).Error(), http.StatusInternalServerError) + return + } + epoch := head.Height() + + mbf, err := a.getMinerBitfields(ctx, maddr, a.stor) + if err != nil { + http.Error(w, xerrors.Errorf("failed to load miner bitfields: %w", err).Error(), http.StatusInternalServerError) + return + } + + task := tasks[0] + + afterSeed := task.SeedEpoch != nil && *task.SeedEpoch <= int64(epoch) + + var sectorLocations []struct { + CanSeal, CanStore bool + FileType storiface.SectorFileType `db:"sector_filetype"` + StorageID string `db:"storage_id"` + Urls string `db:"urls"` + } + + err = a.db.Select(ctx, §orLocations, `SELECT p.can_seal, p.can_store, l.sector_filetype, l.storage_id, p.urls FROM sector_location l + JOIN storage_path p ON l.storage_id = p.storage_id + WHERE l.sector_num = $1 and l.miner_id = $2 ORDER BY p.can_seal, p.can_store, l.storage_id`, intid, spid) + if err != nil { + http.Error(w, xerrors.Errorf("failed to fetch sector locations: %w", err).Error(), http.StatusInternalServerError) + return + } + + type fileLocations struct { + StorageID string + Urls []string + } + + type locationTable struct { + PathType *string + PathTypeRowSpan int + + FileType *string + FileTypeRowSpan int + + Locations []fileLocations + } + locs := []locationTable{} + + for i, loc := range sectorLocations { + loc := loc + + urlList := strings.Split(loc.Urls, paths.URLSeparator) + + fLoc := fileLocations{ + StorageID: loc.StorageID, + Urls: urlList, + } + + var pathTypeStr *string + var fileTypeStr *string + pathTypeRowSpan := 1 + fileTypeRowSpan := 1 + + pathType := "None" + if loc.CanSeal && loc.CanStore { + pathType = "Seal/Store" + } else if loc.CanSeal { + pathType = "Seal" + } else if loc.CanStore { + pathType = "Store" + } + pathTypeStr = &pathType + + fileType := loc.FileType.String() + fileTypeStr = &fileType + + if i > 0 { + prevNonNilPathTypeLoc := i - 1 + for prevNonNilPathTypeLoc > 0 && locs[prevNonNilPathTypeLoc].PathType == nil { + prevNonNilPathTypeLoc-- + } + if *locs[prevNonNilPathTypeLoc].PathType == *pathTypeStr { + pathTypeRowSpan = 0 + pathTypeStr = nil + locs[prevNonNilPathTypeLoc].PathTypeRowSpan++ + // only if we have extended path type we may need to extend file type + + prevNonNilFileTypeLoc := i - 1 + for prevNonNilFileTypeLoc > 0 && locs[prevNonNilFileTypeLoc].FileType == nil { + prevNonNilFileTypeLoc-- + } + if *locs[prevNonNilFileTypeLoc].FileType == *fileTypeStr { + fileTypeRowSpan = 0 + fileTypeStr = nil + locs[prevNonNilFileTypeLoc].FileTypeRowSpan++ + } + } + } + + locTable := locationTable{ + PathType: pathTypeStr, + PathTypeRowSpan: pathTypeRowSpan, + FileType: fileTypeStr, + FileTypeRowSpan: fileTypeRowSpan, + Locations: []fileLocations{fLoc}, + } + + locs = append(locs, locTable) + + } + + // TaskIDs + taskIDs := map[int64]struct{}{} + var htasks []taskSummary + { + // get non-nil task IDs + appendNonNil := func(id *int64) { + if id != nil { + taskIDs[*id] = struct{}{} + } + } + appendNonNil(task.TaskSDR) + appendNonNil(task.TaskTreeD) + appendNonNil(task.TaskTreeC) + appendNonNil(task.TaskTreeR) + appendNonNil(task.TaskPrecommitMsg) + appendNonNil(task.TaskPoRep) + appendNonNil(task.TaskFinalize) + appendNonNil(task.TaskMoveStorage) + appendNonNil(task.TaskCommitMsg) + + if len(taskIDs) > 0 { + ids := lo.Keys(taskIDs) + + var dbtasks []struct { + OwnerID *string `db:"owner_id"` + HostAndPort *string `db:"host_and_port"` + TaskID int64 `db:"id"` + Name string `db:"name"` + UpdateTime time.Time `db:"update_time"` + } + err = a.db.Select(ctx, &dbtasks, `SELECT t.owner_id, hm.host_and_port, t.id, t.name, t.update_time FROM harmony_task t LEFT JOIN curio.harmony_machines hm ON hm.id = t.owner_id WHERE t.id = ANY($1)`, ids) + if err != nil { + http.Error(w, fmt.Sprintf("failed to fetch task names: %v", err), http.StatusInternalServerError) + return + } + + for _, tn := range dbtasks { + htasks = append(htasks, taskSummary{ + Name: tn.Name, + SincePosted: time.Since(tn.UpdateTime.Local()).Round(time.Second).String(), + Owner: tn.HostAndPort, + OwnerID: tn.OwnerID, + ID: tn.TaskID, + }) + } + } + } + + mi := struct { + SectorNumber int64 + PipelinePoRep sectorListEntry + + Locations []locationTable + Tasks []taskSummary + }{ + SectorNumber: intid, + PipelinePoRep: sectorListEntry{ + PipelineTask: tasks[0], + AfterSeed: afterSeed, + + ChainAlloc: must.One(mbf.alloc.IsSet(uint64(task.SectorNumber))), + ChainSector: must.One(mbf.sectorSet.IsSet(uint64(task.SectorNumber))), + ChainActive: must.One(mbf.active.IsSet(uint64(task.SectorNumber))), + ChainUnproven: must.One(mbf.unproven.IsSet(uint64(task.SectorNumber))), + ChainFaulty: must.One(mbf.faulty.IsSet(uint64(task.SectorNumber))), + }, + + Locations: locs, + Tasks: htasks, + } + + a.executePageTemplate(w, "sector_info", "Sector Info", mi) +} + var templateDev = os.Getenv("LOTUS_WEB_DEV") == "1" func (a *app) executeTemplate(w http.ResponseWriter, name string, data interface{}) { diff --git a/curiosrc/web/hapi/simpleinfo_pipeline_porep.go b/curiosrc/web/hapi/simpleinfo_pipeline_porep.go index 236f87cc2..a37cd14ab 100644 --- a/curiosrc/web/hapi/simpleinfo_pipeline_porep.go +++ b/curiosrc/web/hapi/simpleinfo_pipeline_porep.go @@ -1,71 +1,81 @@ package hapi import ( + "context" "net/http" "time" - lru "github.com/hashicorp/golang-lru/v2" - blocks "github.com/ipfs/go-block-format" "golang.org/x/xerrors" "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-bitfield" - "github.com/filecoin-project/lotus/blockstore" + "github.com/filecoin-project/lotus/chain/actors/adt" "github.com/filecoin-project/lotus/chain/actors/builtin/miner" - "github.com/filecoin-project/lotus/chain/store" "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/lib/must" ) -var ChainBlockCache = must.One(lru.New[blockstore.MhString, blocks.Block](4096)) +type PipelineTask struct { + SpID int64 `db:"sp_id"` + SectorNumber int64 `db:"sector_number"` + + CreateTime time.Time `db:"create_time"` + + TaskSDR *int64 `db:"task_id_sdr"` + AfterSDR bool `db:"after_sdr"` + + TaskTreeD *int64 `db:"task_id_tree_d"` + AfterTreeD bool `db:"after_tree_d"` + + TaskTreeC *int64 `db:"task_id_tree_c"` + AfterTreeC bool `db:"after_tree_c"` + + TaskTreeR *int64 `db:"task_id_tree_r"` + AfterTreeR bool `db:"after_tree_r"` + + TaskPrecommitMsg *int64 `db:"task_id_precommit_msg"` + AfterPrecommitMsg bool `db:"after_precommit_msg"` + + AfterPrecommitMsgSuccess bool `db:"after_precommit_msg_success"` + SeedEpoch *int64 `db:"seed_epoch"` + + TaskPoRep *int64 `db:"task_id_porep"` + PoRepProof []byte `db:"porep_proof"` + AfterPoRep bool `db:"after_porep"` + + TaskFinalize *int64 `db:"task_id_finalize"` + AfterFinalize bool `db:"after_finalize"` + + TaskMoveStorage *int64 `db:"task_id_move_storage"` + AfterMoveStorage bool `db:"after_move_storage"` + + TaskCommitMsg *int64 `db:"task_id_commit_msg"` + AfterCommitMsg bool `db:"after_commit_msg"` + + AfterCommitMsgSuccess bool `db:"after_commit_msg_success"` + + Failed bool `db:"failed"` + FailedReason string `db:"failed_reason"` +} + +type sectorListEntry struct { + PipelineTask + + Address address.Address + CreateTime string + AfterSeed bool + + ChainAlloc, ChainSector, ChainActive, ChainUnproven, ChainFaulty bool +} + +type minerBitfields struct { + alloc, sectorSet, active, unproven, faulty bitfield.BitField +} func (a *app) pipelinePorepSectors(w http.ResponseWriter, r *http.Request) { ctx := r.Context() - type PipelineTask struct { - SpID int64 `db:"sp_id"` - SectorNumber int64 `db:"sector_number"` - - CreateTime time.Time `db:"create_time"` - - TaskSDR *int64 `db:"task_id_sdr"` - AfterSDR bool `db:"after_sdr"` - - TaskTreeD *int64 `db:"task_id_tree_d"` - AfterTreeD bool `db:"after_tree_d"` - - TaskTreeC *int64 `db:"task_id_tree_c"` - AfterTreeC bool `db:"after_tree_c"` - - TaskTreeR *int64 `db:"task_id_tree_r"` - AfterTreeR bool `db:"after_tree_r"` - - TaskPrecommitMsg *int64 `db:"task_id_precommit_msg"` - AfterPrecommitMsg bool `db:"after_precommit_msg"` - - AfterPrecommitMsgSuccess bool `db:"after_precommit_msg_success"` - SeedEpoch *int64 `db:"seed_epoch"` - - TaskPoRep *int64 `db:"task_id_porep"` - PoRepProof []byte `db:"porep_proof"` - AfterPoRep bool `db:"after_porep"` - - TaskFinalize *int64 `db:"task_id_finalize"` - AfterFinalize bool `db:"after_finalize"` - - TaskMoveStorage *int64 `db:"task_id_move_storage"` - AfterMoveStorage bool `db:"after_move_storage"` - - TaskCommitMsg *int64 `db:"task_id_commit_msg"` - AfterCommitMsg bool `db:"after_commit_msg"` - - AfterCommitMsgSuccess bool `db:"after_commit_msg_success"` - - Failed bool `db:"failed"` - FailedReason string `db:"failed_reason"` - } - var tasks []PipelineTask err := a.db.Select(ctx, &tasks, `SELECT @@ -89,16 +99,6 @@ func (a *app) pipelinePorepSectors(w http.ResponseWriter, r *http.Request) { return } - type sectorListEntry struct { - PipelineTask - - Address address.Address - CreateTime string - AfterSeed bool - - ChainAlloc, ChainSector, ChainActive, ChainUnproven, ChainFaulty bool - } - head, err := a.workingApi.ChainHead(ctx) if err != nil { http.Error(w, xerrors.Errorf("failed to fetch chain head: %w", err).Error(), http.StatusInternalServerError) @@ -106,11 +106,6 @@ func (a *app) pipelinePorepSectors(w http.ResponseWriter, r *http.Request) { } epoch := head.Height() - stor := store.ActorStore(ctx, blockstore.NewReadCachedBlockstore(blockstore.NewAPIBlockstore(a.workingApi), ChainBlockCache)) - - type minerBitfields struct { - alloc, sectorSet, active, unproven, faulty bitfield.BitField - } minerBitfieldCache := map[address.Address]minerBitfields{} sectorList := make([]sectorListEntry, 0, len(tasks)) @@ -127,55 +122,11 @@ func (a *app) pipelinePorepSectors(w http.ResponseWriter, r *http.Request) { mbf, ok := minerBitfieldCache[addr] if !ok { - act, err := a.workingApi.StateGetActor(ctx, addr, types.EmptyTSK) + mbf, err := a.getMinerBitfields(ctx, addr, a.stor) if err != nil { - http.Error(w, xerrors.Errorf("failed to load actor: %w", err).Error(), http.StatusInternalServerError) + http.Error(w, xerrors.Errorf("failed to load miner bitfields: %w", err).Error(), http.StatusInternalServerError) return } - - mas, err := miner.Load(stor, act) - if err != nil { - http.Error(w, xerrors.Errorf("failed to load miner actor: %w", err).Error(), http.StatusInternalServerError) - return - } - - activeSectors, err := miner.AllPartSectors(mas, miner.Partition.ActiveSectors) - if err != nil { - http.Error(w, xerrors.Errorf("failed to load active sectors: %w", err).Error(), http.StatusInternalServerError) - return - } - - allSectors, err := miner.AllPartSectors(mas, miner.Partition.AllSectors) - if err != nil { - http.Error(w, xerrors.Errorf("failed to load all sectors: %w", err).Error(), http.StatusInternalServerError) - return - } - - unproved, err := miner.AllPartSectors(mas, miner.Partition.UnprovenSectors) - if err != nil { - http.Error(w, xerrors.Errorf("failed to load unproven sectors: %w", err).Error(), http.StatusInternalServerError) - return - } - - faulty, err := miner.AllPartSectors(mas, miner.Partition.FaultySectors) - if err != nil { - http.Error(w, xerrors.Errorf("failed to load faulty sectors: %w", err).Error(), http.StatusInternalServerError) - return - } - - alloc, err := mas.GetAllocatedSectors() - if err != nil { - http.Error(w, xerrors.Errorf("failed to load allocated sectors: %w", err).Error(), http.StatusInternalServerError) - return - } - - mbf = minerBitfields{ - alloc: *alloc, - sectorSet: allSectors, - active: activeSectors, - unproven: unproved, - faulty: faulty, - } minerBitfieldCache[addr] = mbf } @@ -197,3 +148,48 @@ func (a *app) pipelinePorepSectors(w http.ResponseWriter, r *http.Request) { a.executeTemplate(w, "pipeline_porep_sectors", sectorList) } + +func (a *app) getMinerBitfields(ctx context.Context, addr address.Address, stor adt.Store) (minerBitfields, error) { + act, err := a.workingApi.StateGetActor(ctx, addr, types.EmptyTSK) + if err != nil { + return minerBitfields{}, xerrors.Errorf("failed to load actor: %w", err) + } + + mas, err := miner.Load(stor, act) + if err != nil { + return minerBitfields{}, xerrors.Errorf("failed to load miner actor: %w", err) + } + + activeSectors, err := miner.AllPartSectors(mas, miner.Partition.ActiveSectors) + if err != nil { + return minerBitfields{}, xerrors.Errorf("failed to load active sectors: %w", err) + } + + allSectors, err := miner.AllPartSectors(mas, miner.Partition.AllSectors) + if err != nil { + return minerBitfields{}, xerrors.Errorf("failed to load all sectors: %w", err) + } + + unproved, err := miner.AllPartSectors(mas, miner.Partition.UnprovenSectors) + if err != nil { + return minerBitfields{}, xerrors.Errorf("failed to load unproven sectors: %w", err) + } + + faulty, err := miner.AllPartSectors(mas, miner.Partition.FaultySectors) + if err != nil { + return minerBitfields{}, xerrors.Errorf("failed to load faulty sectors: %w", err) + } + + alloc, err := mas.GetAllocatedSectors() + if err != nil { + return minerBitfields{}, xerrors.Errorf("failed to load allocated sectors: %w", err) + } + + return minerBitfields{ + alloc: *alloc, + sectorSet: allSectors, + active: activeSectors, + unproven: unproved, + faulty: faulty, + }, nil +} diff --git a/curiosrc/web/hapi/web/pipeline_porep_sectors.gohtml b/curiosrc/web/hapi/web/pipeline_porep_sectors.gohtml index f5069fe6d..3949de8e2 100644 --- a/curiosrc/web/hapi/web/pipeline_porep_sectors.gohtml +++ b/curiosrc/web/hapi/web/pipeline_porep_sectors.gohtml @@ -1,132 +1,136 @@ +{{define "sector_porep_state"}} + + + + + + + + + + + + + + + + + + + + + + + + +
+
SDR
+
+ {{if .AfterSDR}}done{{else}} + {{if ne .TaskSDR nil}}T:{{.TaskSDR}}{{else}}--{{end}} + {{end}} +
+
+
TreeC
+
+ {{if .AfterTreeC}}done{{else}} + {{if ne .TaskTreeC nil}}T:{{.TaskTreeC}}{{else}}--{{end}} + {{end}} +
+
+
PComm Msg
+
+ {{if .AfterPrecommitMsg}}done{{else}} + {{if ne .TaskPrecommitMsg nil}}T:{{.TaskPrecommitMsg}}{{else}}--{{end}} + {{end}} +
+
+
PComm Wait
+
+ {{if .AfterPrecommitMsgSuccess}}done{{else}} + -- + {{end}} +
+
+
Wait Seed
+
+ {{if .AfterSeed}}done{{else}} + {{if ne .SeedEpoch nil}}@{{.SeedEpoch}}{{else}}--{{end}} + {{end}} +
+
+
PoRep
+
+ {{if .AfterPoRep}}done{{else}} + {{if ne .TaskPoRep nil}}T:{{.TaskPoRep}}{{else}}--{{end}} + {{end}} +
+
+
Clear Cache
+
+ {{if .AfterFinalize}}done{{else}} + {{if ne .TaskFinalize nil}}T:{{.TaskFinalize}}{{else}}--{{end}} + {{end}} +
+
+
Move Storage
+
+ {{if .AfterMoveStorage}}done{{else}} + {{if ne .TaskMoveStorage nil}}T:{{.TaskMoveStorage}}{{else}}--{{end}} + {{end}} +
+
+
On Chain
+
{{if .ChainSector}}yes{{else}}{{if .ChainAlloc}}allocated{{else}}no{{end}}{{end}}
+
+
TreeD
+
+ {{if .AfterTreeD}}done{{else}} + {{if ne .TaskTreeD nil}}T:{{.TaskTreeD}}{{else}}--{{end}} + {{end}} +
+
+
TreeR
+
+ {{if .AfterTreeR}}done{{else}} + {{if ne .TaskTreeR nil}}T:{{.TaskTreeR}}{{else}}--{{end}} + {{end}} +
+
+
Commit Msg
+
+ {{if .AfterCommitMsg}}done{{else}} + {{if ne .TaskCommitMsg nil}}T:{{.TaskCommitMsg}}{{else}}--{{end}} + {{end}} +
+
+
Commit Wait
+
+ {{if .AfterCommitMsgSuccess}}done{{else}} + -- + {{end}} +
+
+
Active
+
{{if .ChainActive}}yes{{else}} + {{if .ChainUnproven}}unproven{{else}} + {{if .ChainFaulty}}faulty{{else}}no{{end}} + {{end}} + {{end}} +
+
+{{end}} + {{define "pipeline_porep_sectors"}} {{range .}} {{.Address}} {{.CreateTime}} - - - - - - - - - - - - - - - - - - - - - - - - -
-
SDR
-
- {{if .AfterSDR}}done{{else}} - {{if ne .TaskSDR nil}}T:{{.TaskSDR}}{{else}}--{{end}} - {{end}} -
-
-
TreeC
-
- {{if .AfterTreeC}}done{{else}} - {{if ne .TaskTreeC nil}}T:{{.TaskTreeC}}{{else}}--{{end}} - {{end}} -
-
-
PComm Msg
-
- {{if .AfterPrecommitMsg}}done{{else}} - {{if ne .TaskPrecommitMsg nil}}T:{{.TaskPrecommitMsg}}{{else}}--{{end}} - {{end}} -
-
-
PComm Wait
-
- {{if .AfterPrecommitMsgSuccess}}done{{else}} - -- - {{end}} -
-
-
Wait Seed
-
- {{if .AfterSeed}}done{{else}} - {{if ne .SeedEpoch nil}}@{{.SeedEpoch}}{{else}}--{{end}} - {{end}} -
-
-
PoRep
-
- {{if .AfterPoRep}}done{{else}} - {{if ne .TaskPoRep nil}}T:{{.TaskPoRep}}{{else}}--{{end}} - {{end}} -
-
-
Clear Cache
-
- {{if .AfterFinalize}}done{{else}} - {{if ne .TaskFinalize nil}}T:{{.TaskFinalize}}{{else}}--{{end}} - {{end}} -
-
-
Move Storage
-
- {{if .AfterMoveStorage}}done{{else}} - {{if ne .TaskMoveStorage nil}}T:{{.TaskMoveStorage}}{{else}}--{{end}} - {{end}} -
-
-
On Chain
-
{{if .ChainSector}}yes{{else}}{{if .ChainAlloc}}allocated{{else}}no{{end}}{{end}}
-
-
TreeD
-
- {{if .AfterTreeD}}done{{else}} - {{if ne .TaskTreeD nil}}T:{{.TaskTreeD}}{{else}}--{{end}} - {{end}} -
-
-
TreeR
-
- {{if .AfterTreeR}}done{{else}} - {{if ne .TaskTreeR nil}}T:{{.TaskTreeR}}{{else}}--{{end}} - {{end}} -
-
-
Commit Msg
-
- {{if .AfterCommitMsg}}done{{else}} - {{if ne .TaskCommitMsg nil}}T:{{.TaskCommitMsg}}{{else}}--{{end}} - {{end}} -
-
-
Commit Wait
-
- {{if .AfterCommitMsgSuccess}}done{{else}} - -- - {{end}} -
-
-
Active
-
{{if .ChainActive}}yes{{else}} - {{if .ChainUnproven}}unproven{{else}} - {{if .ChainFaulty}}faulty{{else}}no{{end}} - {{end}} - {{end}} -
-
+ {{template "sector_porep_state" .}} - DETAILS + DETAILS diff --git a/curiosrc/web/static/main.css b/curiosrc/web/static/main.css index d9f314420..76b90d0b5 100644 --- a/curiosrc/web/static/main.css +++ b/curiosrc/web/static/main.css @@ -68,3 +68,53 @@ a:hover { color: deeppink; } } + +/* Path: curiosrc/web/hapi/web/pipeline_porep_sectors.gohtml */ +.porep-pipeline-table, .porep-state { + color: #d0d0d0; +} + +.porep-pipeline-table td, .porep-pipeline-table th { + border-left: none; + border-collapse: collapse; +} + +.porep-pipeline-table tr:nth-child(odd) { + border-top: 6px solid #999999; + +} + +.porep-pipeline-table tr:first-child, .porep-pipeline-table tr:first-child { + border-top: none; +} + +.porep-state { + border-collapse: collapse; +} + +.porep-state td, .porep-state th { + border-left: 1px solid #f0f0f0; + border-right: 1px solid #f0f0f0; + + padding: 1px 5px; + + text-align: center; + font-size: 0.7em; +} + +.porep-state tr { + border-top: 1px solid #f0f0f0; +} +.porep-state tr:first-child { + border-top: none; +} + +.pipeline-active { + background-color: #303060; +} +.pipeline-success { + background-color: #306030; +} +.pipeline-failed { + background-color: #603030; +} diff --git a/curiosrc/web/static/pipeline_porep.html b/curiosrc/web/static/pipeline_porep.html index 8696bdd26..d596163b7 100644 --- a/curiosrc/web/static/pipeline_porep.html +++ b/curiosrc/web/static/pipeline_porep.html @@ -5,57 +5,6 @@ -
diff --git a/storage/paths/db_index.go b/storage/paths/db_index.go index f79e6941d..470d5c33c 100644 --- a/storage/paths/db_index.go +++ b/storage/paths/db_index.go @@ -26,6 +26,8 @@ import ( const NoMinerFilter = abi.ActorID(0) +const URLSeparator = "," + var errAlreadyLocked = errors.New("already locked") type DBIndex struct { @@ -197,13 +199,13 @@ func (dbi *DBIndex) StorageAttach(ctx context.Context, si storiface.StorageInfo, if storageId.Valid { var currUrls []string if urls.Valid { - currUrls = strings.Split(urls.String, ",") + currUrls = strings.Split(urls.String, URLSeparator) } currUrls = union(currUrls, si.URLs) _, err = tx.Exec( "UPDATE storage_path set urls=$1, weight=$2, max_storage=$3, can_seal=$4, can_store=$5, groups=$6, allow_to=$7, allow_types=$8, deny_types=$9, allow_miners=$10, deny_miners=$11, last_heartbeat=NOW() WHERE storage_id=$12", - strings.Join(currUrls, ","), + strings.Join(currUrls, URLSeparator), si.Weight, si.MaxStorage, si.CanSeal, @@ -277,7 +279,7 @@ func (dbi *DBIndex) StorageDetach(ctx context.Context, id storiface.ID, url stri } if len(modUrls) > 0 { - newUrls := strings.Join(modUrls, ",") + newUrls := strings.Join(modUrls, URLSeparator) _, err := dbi.harmonyDB.Exec(ctx, "UPDATE storage_path set urls=$1 WHERE storage_id=$2", newUrls, id) if err != nil { return err From 3cc62e04e9f49ffd0a234c5fb567ebe61444d8de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 11 Apr 2024 18:35:09 +0200 Subject: [PATCH 344/394] feat: curio: Cleanup data copies after seal ops (#11847) * feat: curio: Cleanup data copies after seal ops * curio: make ensureOneCopy safer * curio: Don't randomly remove cache files * Address review --- curiosrc/ffi/piece_funcs.go | 2 +- curiosrc/ffi/sdr_funcs.go | 53 ++++++++++++++++++++++++++++++------- storage/paths/local.go | 4 ++- 3 files changed, 47 insertions(+), 12 deletions(-) diff --git a/curiosrc/ffi/piece_funcs.go b/curiosrc/ffi/piece_funcs.go index 8b7df01ed..a548f5cc2 100644 --- a/curiosrc/ffi/piece_funcs.go +++ b/curiosrc/ffi/piece_funcs.go @@ -15,7 +15,7 @@ import ( func (sb *SealCalls) WritePiece(ctx context.Context, taskID *harmonytask.TaskID, pieceID storiface.PieceNumber, size int64, data io.Reader) error { // todo: config(?): allow setting PathStorage for this // todo storage reservations - paths, done, err := sb.sectors.AcquireSector(ctx, taskID, pieceID.Ref(), storiface.FTNone, storiface.FTPiece, storiface.PathSealing) + paths, _, done, err := sb.sectors.AcquireSector(ctx, taskID, pieceID.Ref(), storiface.FTNone, storiface.FTPiece, storiface.PathSealing) if err != nil { return err } diff --git a/curiosrc/ffi/sdr_funcs.go b/curiosrc/ffi/sdr_funcs.go index dba10951f..5987c7856 100644 --- a/curiosrc/ffi/sdr_funcs.go +++ b/curiosrc/ffi/sdr_funcs.go @@ -60,7 +60,7 @@ type storageProvider struct { storageReservations *xsync.MapOf[harmonytask.TaskID, *StorageReservation] } -func (l *storageProvider) AcquireSector(ctx context.Context, taskID *harmonytask.TaskID, sector storiface.SectorRef, existing, allocate storiface.SectorFileType, sealing storiface.PathType) (storiface.SectorPaths, func(), error) { +func (l *storageProvider) AcquireSector(ctx context.Context, taskID *harmonytask.TaskID, sector storiface.SectorRef, existing, allocate storiface.SectorFileType, sealing storiface.PathType) (fspaths, ids storiface.SectorPaths, release func(), err error) { var paths, storageIDs storiface.SectorPaths var releaseStorage func() @@ -72,7 +72,7 @@ func (l *storageProvider) AcquireSector(ctx context.Context, taskID *harmonytask if ok && resv != nil { if resv.Alloc != allocate || resv.Existing != existing { // this should never happen, only when task definition is wrong - return storiface.SectorPaths{}, nil, xerrors.Errorf("storage reservation type mismatch") + return storiface.SectorPaths{}, storiface.SectorPaths{}, nil, xerrors.Errorf("storage reservation type mismatch") } log.Debugw("using existing storage reservation", "task", taskID, "sector", sector, "existing", existing, "allocate", allocate) @@ -89,12 +89,12 @@ func (l *storageProvider) AcquireSector(ctx context.Context, taskID *harmonytask _, checkPathIDs, err := l.storage.AcquireSector(ctx, sector, existing, storiface.FTNone, sealing, storiface.AcquireMove, storiface.AcquireInto(storiface.PathsWithIDs{Paths: paths, IDs: storageIDs})) if err != nil { - return storiface.SectorPaths{}, nil, xerrors.Errorf("acquire reserved existing files: %w", err) + return storiface.SectorPaths{}, storiface.SectorPaths{}, nil, xerrors.Errorf("acquire reserved existing files: %w", err) } // assert that checkPathIDs is the same as storageIDs if storageIDs.Subset(existing) != checkPathIDs.Subset(existing) { - return storiface.SectorPaths{}, nil, xerrors.Errorf("acquire reserved existing files: pathIDs mismatch %#v != %#v", storageIDs, checkPathIDs) + return storiface.SectorPaths{}, storiface.SectorPaths{}, nil, xerrors.Errorf("acquire reserved existing files: pathIDs mismatch %#v != %#v", storageIDs, checkPathIDs) } } } else { @@ -103,18 +103,18 @@ func (l *storageProvider) AcquireSector(ctx context.Context, taskID *harmonytask var err error paths, storageIDs, err = l.storage.AcquireSector(ctx, sector, existing, allocate, sealing, storiface.AcquireMove) if err != nil { - return storiface.SectorPaths{}, nil, err + return storiface.SectorPaths{}, storiface.SectorPaths{}, nil, err } releaseStorage, err = l.localStore.Reserve(ctx, sector, allocate, storageIDs, storiface.FSOverheadSeal) if err != nil { - return storiface.SectorPaths{}, nil, xerrors.Errorf("reserving storage space: %w", err) + return storiface.SectorPaths{}, storiface.SectorPaths{}, nil, xerrors.Errorf("reserving storage space: %w", err) } } log.Debugf("acquired sector %d (e:%d; a:%d): %v", sector, existing, allocate, paths) - return paths, func() { + return paths, storageIDs, func() { releaseStorage() for _, fileType := range storiface.PathTypes { @@ -131,7 +131,7 @@ func (l *storageProvider) AcquireSector(ctx context.Context, taskID *harmonytask } func (sb *SealCalls) GenerateSDR(ctx context.Context, taskID harmonytask.TaskID, sector storiface.SectorRef, ticket abi.SealRandomness, commKcid cid.Cid) error { - paths, releaseSector, err := sb.sectors.AcquireSector(ctx, &taskID, sector, storiface.FTNone, storiface.FTCache, storiface.PathSealing) + paths, pathIDs, releaseSector, err := sb.sectors.AcquireSector(ctx, &taskID, sector, storiface.FTNone, storiface.FTCache, storiface.PathSealing) if err != nil { return xerrors.Errorf("acquiring sector paths: %w", err) } @@ -158,6 +158,31 @@ func (sb *SealCalls) GenerateSDR(ctx context.Context, taskID harmonytask.TaskID, return xerrors.Errorf("generating SDR %d (%s): %w", sector.ID.Number, paths.Unsealed, err) } + if err := sb.ensureOneCopy(ctx, sector.ID, pathIDs, storiface.FTCache); err != nil { + return xerrors.Errorf("ensure one copy: %w", err) + } + + return nil +} + +// ensureOneCopy makes sure that there is only one version of sector data. +// Usually called after a successful operation was done successfully on sector data. +func (sb *SealCalls) ensureOneCopy(ctx context.Context, sid abi.SectorID, pathIDs storiface.SectorPaths, fts storiface.SectorFileType) error { + if !pathIDs.HasAllSet(fts) { + return xerrors.Errorf("ensure one copy: not all paths are set") + } + + for _, fileType := range fts.AllSet() { + pid := storiface.PathByType(pathIDs, fileType) + keepIn := []storiface.ID{storiface.ID(pid)} + + log.Debugw("ensureOneCopy", "sector", sid, "type", fileType, "keep", keepIn) + + if err := sb.sectors.storage.Remove(ctx, sid, fileType, true, keepIn); err != nil { + return err + } + } + return nil } @@ -167,7 +192,7 @@ func (sb *SealCalls) TreeDRC(ctx context.Context, task *harmonytask.TaskID, sect return cid.Undef, cid.Undef, xerrors.Errorf("make phase1 output: %w", err) } - paths, releaseSector, err := sb.sectors.AcquireSector(ctx, task, sector, storiface.FTCache, storiface.FTSealed, storiface.PathSealing) + paths, pathIDs, releaseSector, err := sb.sectors.AcquireSector(ctx, task, sector, storiface.FTCache, storiface.FTSealed, storiface.PathSealing) if err != nil { return cid.Undef, cid.Undef, xerrors.Errorf("acquiring sector paths: %w", err) } @@ -243,6 +268,10 @@ func (sb *SealCalls) TreeDRC(ctx context.Context, task *harmonytask.TaskID, sect return cid.Undef, cid.Undef, xerrors.Errorf("unsealed cid changed after sealing") } + if err := sb.ensureOneCopy(ctx, sector.ID, pathIDs, storiface.FTCache|storiface.FTSealed); err != nil { + return cid.Undef, cid.Undef, xerrors.Errorf("ensure one copy: %w", err) + } + return sl, uns, nil } @@ -425,7 +454,7 @@ func (sb *SealCalls) FinalizeSector(ctx context.Context, sector storiface.Sector alloc = storiface.FTUnsealed } - sectorPaths, releaseSector, err := sb.sectors.AcquireSector(ctx, nil, sector, storiface.FTCache, alloc, storiface.PathSealing) + sectorPaths, pathIDs, releaseSector, err := sb.sectors.AcquireSector(ctx, nil, sector, storiface.FTCache, alloc, storiface.PathSealing) if err != nil { return xerrors.Errorf("acquiring sector paths: %w", err) } @@ -514,6 +543,10 @@ afterUnsealedMove: return xerrors.Errorf("clearing cache: %w", err) } + if err := sb.ensureOneCopy(ctx, sector.ID, pathIDs, storiface.FTCache|alloc); err != nil { + return xerrors.Errorf("ensure one copy: %w", err) + } + return nil } diff --git a/storage/paths/local.go b/storage/paths/local.go index cc5789801..74fde977d 100644 --- a/storage/paths/local.go +++ b/storage/paths/local.go @@ -663,6 +663,8 @@ func (st *Local) Remove(ctx context.Context, sid abi.SectorID, typ storiface.Sec return xerrors.New("delete expects one file type") } + log.Debugw("Remove called", "sid", sid, "type", typ, "force", force, "keepIn", keepIn) + si, err := st.index.StorageFindSector(ctx, sid, typ, 0, false) if err != nil { return xerrors.Errorf("finding existing sector %d(t:%d) failed: %w", sid, typ, err) @@ -739,7 +741,7 @@ func (st *Local) removeSector(ctx context.Context, sid abi.SectorID, typ storifa } spath := p.sectorPath(sid, typ) - log.Infof("remove %s", spath) + log.Infow("remove", "path", spath, "id", sid, "type", typ, "storage", storage) if err := os.RemoveAll(spath); err != nil { log.Errorf("removing sector (%v) from %s: %+v", sid, spath, err) From 3931710c72d20264c4281b5567bc5f57462a8839 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 12 Apr 2024 04:36:38 +0200 Subject: [PATCH 345/394] feat: curio/lmrpc: Ingest backpressure (#11865) --- curiosrc/market/lmrpc/lmrpc.go | 134 ++++++++++++++++----- documentation/en/default-curio-config.toml | 31 +++++ node/config/def.go | 5 + node/config/doc_gen.go | 40 ++++++ node/config/types.go | 26 ++++ 5 files changed, 209 insertions(+), 27 deletions(-) diff --git a/curiosrc/market/lmrpc/lmrpc.go b/curiosrc/market/lmrpc/lmrpc.go index b760b7e1e..0f58be45c 100644 --- a/curiosrc/market/lmrpc/lmrpc.go +++ b/curiosrc/market/lmrpc/lmrpc.go @@ -37,6 +37,8 @@ import ( var log = logging.Logger("lmrpc") +const backpressureWaitTime = 30 * time.Second + func ServeCurioMarketRPCFromConfig(db *harmonydb.DB, full api.FullNode, cfg *config.CurioConfig) error { return forEachMarketRPC(cfg, func(maddr string, listen string) error { addr, err := address.NewFromString(maddr) @@ -248,46 +250,72 @@ func ServeCurioMarketRPC(db *harmonydb.DB, full api.FullNode, maddr address.Addr var refID int64 var pieceWasCreated bool - comm, err := db.BeginTransaction(ctx, func(tx *harmonydb.Tx) (commit bool, err error) { - var pieceID int64 - // Attempt to select the piece ID first - err = tx.QueryRow(`SELECT id FROM parked_pieces WHERE piece_cid = $1`, deal.DealProposal.PieceCID.String()).Scan(&pieceID) + for { + var backpressureWait bool - if err != nil { - if err == pgx.ErrNoRows { - // Piece does not exist, attempt to insert - err = tx.QueryRow(` + comm, err := db.BeginTransaction(ctx, func(tx *harmonydb.Tx) (commit bool, err error) { + // BACKPRESSURE + wait, err := maybeApplyBackpressure(tx, conf.Ingest) + if err != nil { + return false, xerrors.Errorf("backpressure checks: %w", err) + } + if wait { + backpressureWait = true + return false, nil + } + + var pieceID int64 + // Attempt to select the piece ID first + err = tx.QueryRow(`SELECT id FROM parked_pieces WHERE piece_cid = $1`, deal.DealProposal.PieceCID.String()).Scan(&pieceID) + + if err != nil { + if err == pgx.ErrNoRows { + // Piece does not exist, attempt to insert + err = tx.QueryRow(` INSERT INTO parked_pieces (piece_cid, piece_padded_size, piece_raw_size) VALUES ($1, $2, $3) ON CONFLICT (piece_cid) DO NOTHING RETURNING id`, deal.DealProposal.PieceCID.String(), int64(pieceSize.Padded()), int64(pieceSize)).Scan(&pieceID) - if err != nil { - return false, xerrors.Errorf("inserting new parked piece and getting id: %w", err) + if err != nil { + return false, xerrors.Errorf("inserting new parked piece and getting id: %w", err) + } + pieceWasCreated = true // New piece was created + } else { + // Some other error occurred during select + return false, xerrors.Errorf("checking existing parked piece: %w", err) } - pieceWasCreated = true // New piece was created } else { - // Some other error occurred during select - return false, xerrors.Errorf("checking existing parked piece: %w", err) + pieceWasCreated = false // Piece already exists, no new piece was created } - } else { - pieceWasCreated = false // Piece already exists, no new piece was created - } - // Add parked_piece_ref - err = tx.QueryRow(`INSERT INTO parked_piece_refs (piece_id, data_url) + // Add parked_piece_ref + err = tx.QueryRow(`INSERT INTO parked_piece_refs (piece_id, data_url) VALUES ($1, $2) RETURNING ref_id`, pieceID, dataUrl.String()).Scan(&refID) + if err != nil { + return false, xerrors.Errorf("inserting parked piece ref: %w", err) + } + + // If everything went well, commit the transaction + return true, nil // This will commit the transaction + }, harmonydb.OptionRetry()) if err != nil { - return false, xerrors.Errorf("inserting parked piece ref: %w", err) + return api.SectorOffset{}, xerrors.Errorf("inserting parked piece: %w", err) + } + if !comm { + if backpressureWait { + // Backpressure was applied, wait and try again + select { + case <-time.After(backpressureWaitTime): + case <-ctx.Done(): + return api.SectorOffset{}, xerrors.Errorf("context done while waiting for backpressure: %w", ctx.Err()) + } + continue + } + + return api.SectorOffset{}, xerrors.Errorf("piece tx didn't commit") } - // If everything went well, commit the transaction - return true, nil // This will commit the transaction - }, harmonydb.OptionRetry()) - if err != nil { - return api.SectorOffset{}, xerrors.Errorf("inserting parked piece: %w", err) - } - if !comm { - return api.SectorOffset{}, xerrors.Errorf("piece tx didn't commit") + break } // wait for piece to be parked @@ -420,3 +448,55 @@ func ServeCurioMarketRPC(db *harmonydb.DB, full api.FullNode, maddr address.Addr return server.ListenAndServe() } + +func maybeApplyBackpressure(tx *harmonydb.Tx, cfg config.CurioIngestConfig) (wait bool, err error) { + var bufferedSDR, bufferedTrees, bufferedPoRep int + err = tx.QueryRow(`WITH BufferedSDR AS ( + SELECT SUM(buffered_count) AS buffered_sdr_count + FROM ( + SELECT COUNT(p.task_id_sdr) - COUNT(t.owner_id) AS buffered_count + FROM sectors_sdr_pipeline p + LEFT JOIN harmony_task t ON p.task_id_sdr = t.id + WHERE p.after_sdr = false + UNION ALL + SELECT COUNT(1) AS buffered_count + FROM parked_pieces + WHERE complete = false + ) AS subquery +), + BufferedTrees AS ( + SELECT COUNT(p.task_id_tree_r) - COUNT(t.owner_id) AS buffered_trees_count + FROM sectors_sdr_pipeline p + LEFT JOIN harmony_task t ON p.task_id_tree_r = t.id + WHERE p.after_sdr = true AND p.after_tree_r = false + ), + BufferedPoRep AS ( + SELECT COUNT(p.task_id_porep) - COUNT(t.owner_id) AS buffered_porep_count + FROM sectors_sdr_pipeline p + LEFT JOIN harmony_task t ON p.task_id_porep = t.id + WHERE p.after_tree_r = true AND p.after_porep = false + ) +SELECT + (SELECT buffered_sdr_count FROM BufferedSDR) AS total_buffered, + (SELECT buffered_trees_count FROM BufferedTrees) AS buffered_trees_count, + (SELECT buffered_porep_count FROM BufferedPoRep) AS buffered_porep_count +`).Scan(&bufferedSDR, &bufferedTrees, &bufferedPoRep) + if err != nil { + return false, xerrors.Errorf("counting parked pieces: %w", err) + } + + if cfg.MaxQueueSDR != 0 && bufferedSDR > cfg.MaxQueueSDR { + log.Debugw("backpressure", "reason", "too many SDR tasks", "buffered", bufferedSDR, "max", cfg.MaxQueueSDR) + return true, nil + } + if cfg.MaxQueueTrees != 0 && bufferedTrees > cfg.MaxQueueTrees { + log.Debugw("backpressure", "reason", "too many tree tasks", "buffered", bufferedTrees, "max", cfg.MaxQueueTrees) + return true, nil + } + if cfg.MaxQueuePoRep != 0 && bufferedPoRep > cfg.MaxQueuePoRep { + log.Debugw("backpressure", "reason", "too many PoRep tasks", "buffered", bufferedPoRep, "max", cfg.MaxQueuePoRep) + return true, nil + } + + return false, nil +} diff --git a/documentation/en/default-curio-config.toml b/documentation/en/default-curio-config.toml index 3ecadeb52..afcb7608a 100644 --- a/documentation/en/default-curio-config.toml +++ b/documentation/en/default-curio-config.toml @@ -318,6 +318,37 @@ #SingleRecoveringPartitionPerPostMessage = false +[Ingest] + # Maximum number of sectors that can be queued waiting for SDR to start processing. + # 0 = unlimited + # Note: This mechanism will delay taking deal data from markets, providing backpressure to the market subsystem. + # The SDR queue includes deals which are in the process of entering the sealing pipeline - size of this queue + # will also impact the maximum number of ParkPiece tasks which can run concurrently. + # + # SDR queue is the first queue in the sealing pipeline, meaning that it should be used as the primary backpressure mechanism. + # + # type: int + #MaxQueueSDR = 8 + + # Maximum number of sectors that can be queued waiting for SDRTrees to start processing. + # 0 = unlimited + # Note: This mechanism will delay taking deal data from markets, providing backpressure to the market subsystem. + # In case of the trees tasks it is possible that this queue grows more than this limit, the backpressure is only + # applied to sectors entering the pipeline. + # + # type: int + #MaxQueueTrees = 0 + + # Maximum number of sectors that can be queued waiting for PoRep to start processing. + # 0 = unlimited + # Note: This mechanism will delay taking deal data from markets, providing backpressure to the market subsystem. + # Like with the trees tasks, it is possible that this queue grows more than this limit, the backpressure is only + # applied to sectors entering the pipeline. + # + # type: int + #MaxQueuePoRep = 0 + + [Journal] # Events of the form: "system1:event1,system1:event2[,...]" # diff --git a/node/config/def.go b/node/config/def.go index 0307001b8..999f015e5 100644 --- a/node/config/def.go +++ b/node/config/def.go @@ -362,5 +362,10 @@ func DefaultCurioConfig() *CurioConfig { PartitionCheckTimeout: Duration(20 * time.Minute), SingleCheckTimeout: Duration(10 * time.Minute), }, + Ingest: CurioIngestConfig{ + MaxQueueSDR: 8, // default to 8 sectors before sdr + MaxQueueTrees: 0, // default don't use this limit + MaxQueuePoRep: 0, // default don't use this limit + }, } } diff --git a/node/config/doc_gen.go b/node/config/doc_gen.go index e9bfb6fba..74549163e 100644 --- a/node/config/doc_gen.go +++ b/node/config/doc_gen.go @@ -208,6 +208,12 @@ over the worker address if this flag is set.`, Comment: ``, }, + { + Name: "Ingest", + Type: "CurioIngestConfig", + + Comment: ``, + }, { Name: "Journal", Type: "JournalConfig", @@ -271,6 +277,40 @@ over the worker address if this flag is set.`, Comment: ``, }, }, + "CurioIngestConfig": { + { + Name: "MaxQueueSDR", + Type: "int", + + Comment: `Maximum number of sectors that can be queued waiting for SDR to start processing. +0 = unlimited +Note: This mechanism will delay taking deal data from markets, providing backpressure to the market subsystem. +The SDR queue includes deals which are in the process of entering the sealing pipeline - size of this queue +will also impact the maximum number of ParkPiece tasks which can run concurrently. + +SDR queue is the first queue in the sealing pipeline, meaning that it should be used as the primary backpressure mechanism.`, + }, + { + Name: "MaxQueueTrees", + Type: "int", + + Comment: `Maximum number of sectors that can be queued waiting for SDRTrees to start processing. +0 = unlimited +Note: This mechanism will delay taking deal data from markets, providing backpressure to the market subsystem. +In case of the trees tasks it is possible that this queue grows more than this limit, the backpressure is only +applied to sectors entering the pipeline.`, + }, + { + Name: "MaxQueuePoRep", + Type: "int", + + Comment: `Maximum number of sectors that can be queued waiting for PoRep to start processing. +0 = unlimited +Note: This mechanism will delay taking deal data from markets, providing backpressure to the market subsystem. +Like with the trees tasks, it is possible that this queue grows more than this limit, the backpressure is only +applied to sectors entering the pipeline.`, + }, + }, "CurioProvingConfig": { { Name: "ParallelCheckLimit", diff --git a/node/config/types.go b/node/config/types.go index d133fcadd..c15df320f 100644 --- a/node/config/types.go +++ b/node/config/types.go @@ -74,6 +74,7 @@ type CurioConfig struct { // Addresses of wallets per MinerAddress (one of the fields). Addresses []CurioAddresses Proving CurioProvingConfig + Ingest CurioIngestConfig Journal JournalConfig Apis ApisConfig } @@ -826,6 +827,31 @@ type CurioProvingConfig struct { SingleRecoveringPartitionPerPostMessage bool } +type CurioIngestConfig struct { + // Maximum number of sectors that can be queued waiting for SDR to start processing. + // 0 = unlimited + // Note: This mechanism will delay taking deal data from markets, providing backpressure to the market subsystem. + // The SDR queue includes deals which are in the process of entering the sealing pipeline - size of this queue + // will also impact the maximum number of ParkPiece tasks which can run concurrently. + // + // SDR queue is the first queue in the sealing pipeline, meaning that it should be used as the primary backpressure mechanism. + MaxQueueSDR int + + // Maximum number of sectors that can be queued waiting for SDRTrees to start processing. + // 0 = unlimited + // Note: This mechanism will delay taking deal data from markets, providing backpressure to the market subsystem. + // In case of the trees tasks it is possible that this queue grows more than this limit, the backpressure is only + // applied to sectors entering the pipeline. + MaxQueueTrees int + + // Maximum number of sectors that can be queued waiting for PoRep to start processing. + // 0 = unlimited + // Note: This mechanism will delay taking deal data from markets, providing backpressure to the market subsystem. + // Like with the trees tasks, it is possible that this queue grows more than this limit, the backpressure is only + // applied to sectors entering the pipeline. + MaxQueuePoRep int +} + // API contains configs for API endpoint type API struct { // Binding address for the Lotus API From 70f90612c26002697ea2b21c47dbb253f53cc48d Mon Sep 17 00:00:00 2001 From: Aloxaf Date: Sat, 13 Apr 2024 03:27:55 +0800 Subject: [PATCH 346/394] fix: cli: json flag (#11868) --- cli/filplus.go | 5 +++++ documentation/en/cli-lotus.md | 1 + 2 files changed, 6 insertions(+) diff --git a/cli/filplus.go b/cli/filplus.go index 7b876ea32..717fa0d83 100644 --- a/cli/filplus.go +++ b/cli/filplus.go @@ -394,6 +394,11 @@ var filplusListClaimsCmd = &cli.Command{ Name: "expired", Usage: "list only expired claims", }, + &cli.BoolFlag{ + Name: "json", + Usage: "output results in json format", + Value: false, + }, }, Action: func(cctx *cli.Context) error { if cctx.NArg() > 1 { diff --git a/documentation/en/cli-lotus.md b/documentation/en/cli-lotus.md index 36f1e1059..28338393f 100644 --- a/documentation/en/cli-lotus.md +++ b/documentation/en/cli-lotus.md @@ -1297,6 +1297,7 @@ USAGE: OPTIONS: --expired list only expired claims (default: false) + --json output results in json format (default: false) --help, -h show help ``` From cd39a09fa575684556083c7fa4b2e124c98b8691 Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Sun, 14 Apr 2024 07:31:56 +1000 Subject: [PATCH 347/394] test(tipsetkey): add tests for derived CID forms Sanity checking and making it easier for alternative implementation compatibility --- chain/types/tipset_key_test.go | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/chain/types/tipset_key_test.go b/chain/types/tipset_key_test.go index 5fbecb3ea..d7a933b54 100644 --- a/chain/types/tipset_key_test.go +++ b/chain/types/tipset_key_test.go @@ -18,10 +18,10 @@ import ( func TestTipSetKey(t *testing.T) { //stm: @TYPES_TIPSETKEY_FROM_BYTES_001, @TYPES_TIPSETKEY_NEW_001 cb := cid.V1Builder{Codec: cid.DagCBOR, MhType: multihash.BLAKE2B_MIN + 31} + // distinct, but arbitrary CIDs, pretending dag-cbor encoding but they are just a multihash over bytes c1, _ := cb.Sum([]byte("a")) c2, _ := cb.Sum([]byte("b")) c3, _ := cb.Sum([]byte("c")) - fmt.Println(len(c1.Bytes())) t.Run("zero value", func(t *testing.T) { assert.Equal(t, EmptyTSK, NewTipSetKey()) @@ -36,6 +36,22 @@ func TestTipSetKey(t *testing.T) { assert.Equal(t, []cid.Cid{c1, c1}, NewTipSetKey(c1, c1).Cids()) }) + t.Run("derived CID", func(t *testing.T) { + assert.Equal(t, "bafy2bzacecesrkxghscnq7vatble2hqdvwat6ed23vdu4vvo3uuggsoaya7ki", c1.String()) // sanity check + actualCid, err := NewTipSetKey().Cid() + require.NoError(t, err) + assert.Equal(t, "bafy2bzacea456askyutsf7uk4ta2q5aojrlcji4mhaqokbfalgvoq4ueeh4l2", actualCid.String(), "empty TSK") + actualCid, err = NewTipSetKey(c1).Cid() + require.NoError(t, err) + assert.Equal(t, "bafy2bzacealem6larzxhf7aggj3cozcefqez3jlksx2tuxehwdil27otcmy4q", actualCid.String()) + actualCid, err = NewTipSetKey(c1, c2, c3).Cid() + require.NoError(t, err) + assert.Equal(t, "bafy2bzacecbnwngwfvxuciumcfudiaoqozisp3hus5im5lg4urrwlxbueissu", actualCid.String()) + + // The key doesn't check for duplicates. + assert.Equal(t, []cid.Cid{c1, c1}, NewTipSetKey(c1, c1).Cids()) + }) + t.Run("equality", func(t *testing.T) { assert.Equal(t, NewTipSetKey(), NewTipSetKey()) assert.Equal(t, NewTipSetKey(c1), NewTipSetKey(c1)) From 20de759aeeec755208cdcebfe1aa54c384d36a6e Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Sun, 14 Apr 2024 15:12:19 -0500 Subject: [PATCH 348/394] feat: fvm: optimize FVM lanes a bit (#11875) This is showing up in profiles so I figured I'd optimize it a bit: 1. Avoid holding locks while recording metrics. 2. Slightly reduce allocations by re-using the metrics "mutators". Also, use the passed context for better tracing. This is unlikely to make a huge difference, but it may help RPC providers a _tiny_ bit and doesn't really move the complexity needle. --- chain/vm/execution.go | 70 +++++++++++++++++++------------------------ 1 file changed, 31 insertions(+), 39 deletions(-) diff --git a/chain/vm/execution.go b/chain/vm/execution.go index ea3a97193..4fb626f43 100644 --- a/chain/vm/execution.go +++ b/chain/vm/execution.go @@ -41,14 +41,14 @@ func newVMExecutor(vmi Interface, lane ExecutionLane) Interface { } func (e *vmExecutor) ApplyMessage(ctx context.Context, cmsg types.ChainMsg) (*ApplyRet, error) { - token := execution.getToken(e.lane) + token := execution.getToken(ctx, e.lane) defer token.Done() return e.vmi.ApplyMessage(ctx, cmsg) } func (e *vmExecutor) ApplyImplicitMessage(ctx context.Context, msg *types.Message) (*ApplyRet, error) { - token := execution.getToken(e.lane) + token := execution.getToken(ctx, e.lane) defer token.Done() return e.vmi.ApplyImplicitMessage(ctx, msg) @@ -61,6 +61,7 @@ func (e *vmExecutor) Flush(ctx context.Context) (cid.Cid, error) { type executionToken struct { lane ExecutionLane reserved int + ctx context.Context } func (token *executionToken) Done() { @@ -77,78 +78,69 @@ type executionEnv struct { reserved int } -func (e *executionEnv) getToken(lane ExecutionLane) *executionToken { - metricsUp(metrics.VMExecutionWaiting, lane) - defer metricsDown(metrics.VMExecutionWaiting, lane) +func (e *executionEnv) getToken(ctx context.Context, lane ExecutionLane) *executionToken { + metricsUp(ctx, metrics.VMExecutionWaiting, lane) + defer metricsDown(ctx, metrics.VMExecutionWaiting, lane) e.mx.Lock() - defer e.mx.Unlock() - switch lane { - case ExecutionLaneDefault: + reserving := 0 + if lane == ExecutionLaneDefault { for e.available <= e.reserved { e.cond.Wait() } - e.available-- - - metricsUp(metrics.VMExecutionRunning, lane) - return &executionToken{lane: lane, reserved: 0} - - case ExecutionLanePriority: + } else { for e.available == 0 { e.cond.Wait() } - - e.available-- - - reserving := 0 if e.reserved > 0 { e.reserved-- reserving = 1 } - - metricsUp(metrics.VMExecutionRunning, lane) - return &executionToken{lane: lane, reserved: reserving} - - default: - // already checked at interface boundary in NewVM, so this is appropriate - panic("bogus execution lane") } + + e.available-- + e.mx.Unlock() + + metricsUp(ctx, metrics.VMExecutionRunning, lane) + return &executionToken{lane: lane, reserved: reserving, ctx: ctx} } func (e *executionEnv) putToken(token *executionToken) { e.mx.Lock() - defer e.mx.Unlock() e.available++ e.reserved += token.reserved // Note: Signal is unsound, because a priority token could wake up a non-priority - // goroutnie and lead to deadlock. So Broadcast it must be. + // goroutine and lead to deadlock. So Broadcast it must be. e.cond.Broadcast() + e.mx.Unlock() - metricsDown(metrics.VMExecutionRunning, token.lane) + metricsDown(token.ctx, metrics.VMExecutionRunning, token.lane) } -func metricsUp(metric *stats.Int64Measure, lane ExecutionLane) { - metricsAdjust(metric, lane, 1) +func metricsUp(ctx context.Context, metric *stats.Int64Measure, lane ExecutionLane) { + metricsAdjust(ctx, metric, lane, 1) } -func metricsDown(metric *stats.Int64Measure, lane ExecutionLane) { - metricsAdjust(metric, lane, -1) +func metricsDown(ctx context.Context, metric *stats.Int64Measure, lane ExecutionLane) { + metricsAdjust(ctx, metric, lane, -1) } -func metricsAdjust(metric *stats.Int64Measure, lane ExecutionLane, delta int) { - laneName := "default" +var ( + defaultLaneTag = tag.Upsert(metrics.ExecutionLane, "default") + priorityLaneTag = tag.Upsert(metrics.ExecutionLane, "priority") +) + +func metricsAdjust(ctx context.Context, metric *stats.Int64Measure, lane ExecutionLane, delta int) { + laneTag := defaultLaneTag if lane > ExecutionLaneDefault { - laneName = "priority" + laneTag = priorityLaneTag } - ctx, _ := tag.New( - context.Background(), - tag.Upsert(metrics.ExecutionLane, laneName), - ) + ctx, _ = tag.New(ctx, laneTag) stats.Record(ctx, metric.M(int64(delta))) } From 11cdb27bbbc145f5ff869886bb4fafdf5aa7f60b Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Mon, 15 Apr 2024 08:33:41 +1000 Subject: [PATCH 349/394] doc(release): minor doc updates to release process --- documentation/misc/RELEASE_ISSUE_TEMPLATE.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/documentation/misc/RELEASE_ISSUE_TEMPLATE.md b/documentation/misc/RELEASE_ISSUE_TEMPLATE.md index 05d239dec..e26e06b32 100644 --- a/documentation/misc/RELEASE_ISSUE_TEMPLATE.md +++ b/documentation/misc/RELEASE_ISSUE_TEMPLATE.md @@ -35,12 +35,13 @@ Test the release candidate thoroughly, including automated and manual tests to e - [ ] Verify that version string in [`version.go`](https://github.com/filecoin-project/lotus/blob/master/build/version.go) has been updated. - [ ] Verify that codegen is up to date (`make gen && make docsgen-cli`) - [ ] Ensure that [CHANGELOG.md](https://github.com/filecoin-project/lotus/blob/master/CHANGELOG.md) is up to date - - [ ] Merge `release-vX.Y.Z` into the `releases` branch. - - [ ] Tag this merge commit (on the `releases` branch) with `vX.Y.Z` + - [ ] Open a pull request against the `releases` branch with a merge of `release-vX.Y.Z`. - [ ] Cut the release [here](https://github.com/filecoin-project/lotus/releases/new?prerelease=false&target=releases). + - The target should be the `releases` branch. + - Either make the tag locally and push to the `releases` branch, or allow GitHub to create a new tag via the UI when the release is published. **Post-Release** - - [ ] Merge the `releases` branch back into `master`, ignoring the changes to `version.go` (keep the `-dev` version from master). Do NOT delete the `releases` branch when doing so! + - [ ] Open a pull request against the `master` branch with a merge of the `releases` branch. Conflict resolution should ignore the changes to `version.go` (keep the `-dev` version from master). Do NOT delete the `releases` branch when doing so! - [ ] Update [RELEASE_ISSUE_TEMPLATE.md](https://github.com/filecoin-project/lotus/blob/master/documentation/misc/RELEASE_ISSUE_TEMPLATE.md) with any improvements determined from this latest release iteration. - [ ] Create an issue using [RELEASE_ISSUE_TEMPLATE.md](https://github.com/filecoin-project/lotus/blob/master/documentation/misc/RELEASE_ISSUE_TEMPLATE.md) for the _next_ release. From ae15eb19223597716a4597c72d656fb54dc08b4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 15 Apr 2024 17:19:53 +0200 Subject: [PATCH 350/394] fix: curio: Start BoostAdapters before blocking rpc serve (#11871) * fix: curio: Start BoostAdapters before blocking rpc serve * curio/lmrpc: Override unspecified rpc address to localhost --- cmd/curio/run.go | 8 ++++---- curiosrc/market/lmrpc/lmrpc.go | 10 ++++------ 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/cmd/curio/run.go b/cmd/curio/run.go index 0f2785f2a..d360b6af4 100644 --- a/cmd/curio/run.go +++ b/cmd/curio/run.go @@ -131,15 +131,15 @@ var runCmd = &cli.Command{ } defer taskEngine.GracefullyTerminate() + if err := lmrpc.ServeCurioMarketRPCFromConfig(dependencies.DB, dependencies.Full, dependencies.Cfg); err != nil { + return xerrors.Errorf("starting market RPCs: %w", err) + } + err = rpc.ListenAndServe(ctx, dependencies, shutdownChan) // Monitor for shutdown. if err != nil { return err } - if err := lmrpc.ServeCurioMarketRPCFromConfig(dependencies.DB, dependencies.Full, dependencies.Cfg); err != nil { - return xerrors.Errorf("starting market RPCs: %w", err) - } - finishCh := node.MonitorShutdown(shutdownChan) //node.ShutdownHandler{Component: "rpc server", StopFunc: rpcStopper}, //node.ShutdownHandler{Component: "curio", StopFunc: stop}, diff --git a/curiosrc/market/lmrpc/lmrpc.go b/curiosrc/market/lmrpc/lmrpc.go index 0f58be45c..dd7880289 100644 --- a/curiosrc/market/lmrpc/lmrpc.go +++ b/curiosrc/market/lmrpc/lmrpc.go @@ -68,9 +68,8 @@ func MakeTokens(cfg *config.CurioConfig) (map[address.Address]string, error) { return xerrors.Errorf("net resolve: %w", err) } - if len(laddr.IP) == 0 { - // set localhost - laddr.IP = net.IPv4(127, 0, 0, 1) + if len(laddr.IP) == 0 || laddr.IP.IsUnspecified() { + return xerrors.Errorf("market rpc server listen address must be a specific address, not %s (probably missing bind IP)", listen) } // need minimal provider with just the config @@ -171,9 +170,8 @@ func ServeCurioMarketRPC(db *harmonydb.DB, full api.FullNode, maddr address.Addr return xerrors.Errorf("net resolve: %w", err) } - if len(laddr.IP) == 0 { - // set localhost - laddr.IP = net.IPv4(127, 0, 0, 1) + if len(laddr.IP) == 0 || laddr.IP.IsUnspecified() { + return xerrors.Errorf("market rpc server listen address must be a specific address, not %s (probably missing bind IP)", listen) } rootUrl := url.URL{ Scheme: "http", From 50ed73de298719382c5291a893a79f08c134a380 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 15 Apr 2024 17:48:45 +0200 Subject: [PATCH 351/394] fix: curio: Check deal start epoch passed in PrecommitSubmit (#11873) --- curiosrc/seal/task_submit_precommit.go | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/curiosrc/seal/task_submit_precommit.go b/curiosrc/seal/task_submit_precommit.go index 0f896cb93..d42bcbe0d 100644 --- a/curiosrc/seal/task_submit_precommit.go +++ b/curiosrc/seal/task_submit_precommit.go @@ -101,6 +101,11 @@ func (s *SubmitPrecommitTask) Do(taskID harmonytask.TaskID, stillOwned func() bo // 2. Prepare message params + head, err := s.api.ChainHead(ctx) + if err != nil { + return false, xerrors.Errorf("getting chain head: %w", err) + } + params := miner.PreCommitSectorBatchParams2{} expiration := sectorParams.TicketEpoch + miner12.MaxSectorExpirationExtension @@ -119,12 +124,13 @@ func (s *SubmitPrecommitTask) Do(taskID harmonytask.TaskID, stillOwned func() bo PieceCID string `db:"piece_cid"` PieceSize int64 `db:"piece_size"` - F05DealID int64 `db:"f05_deal_id"` - F05DealEndEpoch int64 `db:"f05_deal_end_epoch"` + F05DealID int64 `db:"f05_deal_id"` + F05DealEndEpoch int64 `db:"f05_deal_end_epoch"` + F05DealStartEpoch int64 `db:"f05_deal_start_epoch"` } err = s.db.Select(ctx, &pieces, ` - SELECT piece_index, piece_cid, piece_size, f05_deal_id, f05_deal_end_epoch + SELECT piece_index, piece_cid, piece_size, f05_deal_id, f05_deal_end_epoch, f05_deal_start_epoch FROM sectors_sdr_initial_pieces WHERE sp_id = $1 AND sector_number = $2 ORDER BY piece_index ASC`, sectorParams.SpID, sectorParams.SectorNumber) if err != nil { @@ -139,6 +145,17 @@ func (s *SubmitPrecommitTask) Do(taskID harmonytask.TaskID, stillOwned func() bo params.Sectors[0].UnsealedCid = &unsealedCID params.Sectors[0].Expiration = abi.ChainEpoch(pieces[0].F05DealEndEpoch) + if abi.ChainEpoch(pieces[0].F05DealStartEpoch) < head.Height() { + // deal start epoch is in the past, can't precommit this sector anymore + _, perr := s.db.Exec(ctx, `UPDATE sectors_sdr_pipeline + SET failed = TRUE, failed_at = NOW(), failed_reason = 'past-start-epoch', failed_reason_msg = 'precommit: start epoch is in the past' + WHERE task_id_precommit_msg = $1`, taskID) + if perr != nil { + return false, xerrors.Errorf("persisting precommit start epoch expiry: %w", perr) + } + return true, xerrors.Errorf("deal start epoch is in the past") + } + for _, p := range pieces { params.Sectors[0].DealIDs = append(params.Sectors[0].DealIDs, abi.DealID(p.F05DealID)) } From bc43bd6d699d71318d07ad0072826c80ffc0e43f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 16 Apr 2024 00:33:06 +0200 Subject: [PATCH 352/394] fix: local storage reservations fixes (#11866) * paths: Debugging local storage reservations * paths: Log when individual reservation is less than on-disk space * paths: fix debug reservations print * paths: More reserve logs * paths: More more reserve logs * paths: add stacks to duplicate done call log * curio: task storage: Release storage at most once * curio: cleanup before restarting sdr * address review * paths: Simplify reservation release logic --- curiosrc/ffi/sdr_funcs.go | 8 +++ curiosrc/ffi/task_storage.go | 8 ++- storage/paths/local.go | 125 +++++++++++++++++++++++++---------- 3 files changed, 105 insertions(+), 36 deletions(-) diff --git a/curiosrc/ffi/sdr_funcs.go b/curiosrc/ffi/sdr_funcs.go index 5987c7856..e9ce62831 100644 --- a/curiosrc/ffi/sdr_funcs.go +++ b/curiosrc/ffi/sdr_funcs.go @@ -148,6 +148,14 @@ func (sb *SealCalls) GenerateSDR(ctx context.Context, taskID harmonytask.TaskID, return xerrors.Errorf("computing replica id: %w", err) } + // make sure the cache dir is empty + if err := os.RemoveAll(paths.Cache); err != nil { + return xerrors.Errorf("removing cache dir: %w", err) + } + if err := os.MkdirAll(paths.Cache, 0755); err != nil { + return xerrors.Errorf("mkdir cache dir: %w", err) + } + // generate new sector key err = ffi.GenerateSDR( sector.ProofType, diff --git a/curiosrc/ffi/task_storage.go b/curiosrc/ffi/task_storage.go index 4d30eaff9..f01a472fa 100644 --- a/curiosrc/ffi/task_storage.go +++ b/curiosrc/ffi/task_storage.go @@ -2,6 +2,7 @@ package ffi import ( "context" + "sync" "time" "golang.org/x/xerrors" @@ -170,9 +171,14 @@ func (t *TaskStorage) Claim(taskID int) error { return err } + var releaseOnce sync.Once + releaseFunc := func() { + releaseOnce.Do(release) + } + sres := &StorageReservation{ SectorRef: sectorRef, - Release: release, + Release: releaseFunc, Paths: pathsFs, PathIDs: pathIDs, diff --git a/storage/paths/local.go b/storage/paths/local.go index 74fde977d..006854bbf 100644 --- a/storage/paths/local.go +++ b/storage/paths/local.go @@ -7,6 +7,7 @@ import ( "math/rand" "os" "path/filepath" + "runtime" "sync" "time" @@ -45,20 +46,26 @@ type Local struct { localLk sync.RWMutex } +type sectorFile struct { + sid abi.SectorID + ft storiface.SectorFileType +} + type path struct { local string // absolute local path maxStorage uint64 reserved int64 - reservations map[abi.SectorID]storiface.SectorFileType + reservations map[sectorFile]int64 } // statExistingSectorForReservation is optional parameter for stat method // which will make it take into account existing sectors when calculating // available space for new reservations type statExistingSectorForReservation struct { - id abi.SectorID - ft storiface.SectorFileType + id abi.SectorID + ft storiface.SectorFileType + overhead int64 } func (p *path) stat(ls LocalStorage, newReserve ...statExistingSectorForReservation) (stat fsutil.FsStat, newResvOnDisk int64, err error) { @@ -72,7 +79,7 @@ func (p *path) stat(ls LocalStorage, newReserve ...statExistingSectorForReservat stat.Reserved = p.reserved var newReserveOnDisk int64 - accountExistingFiles := func(id abi.SectorID, fileType storiface.SectorFileType) (int64, error) { + accountExistingFiles := func(id abi.SectorID, fileType storiface.SectorFileType, overhead int64) (int64, error) { sp := p.sectorPath(id, fileType) used, err := ls.DiskUsage(sp) @@ -94,35 +101,58 @@ func (p *path) stat(ls LocalStorage, newReserve ...statExistingSectorForReservat return 0, nil } + log.Debugw("accounting existing files", "id", id, "fileType", fileType, "path", sp, "used", used, "overhead", overhead) return used, nil } - for id, ft := range p.reservations { - for _, fileType := range ft.AllSet() { - onDisk, err := accountExistingFiles(id, fileType) - if err != nil { - return fsutil.FsStat{}, 0, err - } - stat.Reserved -= onDisk + for id, oh := range p.reservations { + onDisk, err := accountExistingFiles(id.sid, id.ft, oh) + if err != nil { + return fsutil.FsStat{}, 0, err } + if onDisk > oh { + log.Warnw("reserved space on disk is greater than expected", "id", id.sid, "fileType", id.ft, "onDisk", onDisk, "oh", oh) + onDisk = oh + } + + stat.Reserved -= onDisk } for _, reservation := range newReserve { for _, fileType := range reservation.ft.AllSet() { - if p.reservations[reservation.id]&fileType != 0 { + log.Debugw("accounting existing files for new reservation", "id", reservation.id, "fileType", fileType, "overhead", reservation.overhead) + + resID := sectorFile{reservation.id, fileType} + + if _, has := p.reservations[resID]; has { // already accounted for continue } - onDisk, err := accountExistingFiles(reservation.id, fileType) + onDisk, err := accountExistingFiles(reservation.id, fileType, reservation.overhead) if err != nil { return fsutil.FsStat{}, 0, err } + if onDisk > reservation.overhead { + log.Warnw("reserved space on disk is greater than expected (new resv)", "id", reservation.id, "fileType", fileType, "onDisk", onDisk, "oh", reservation.overhead) + onDisk = reservation.overhead + } + newReserveOnDisk += onDisk } } if stat.Reserved < 0 { - log.Warnf("negative reserved storage: p.reserved=%d, reserved: %d", p.reserved, stat.Reserved) + //log.Warnf("negative reserved storage: p.reserved=%d, reserved: %d", p.reserved, stat.Reserved) + var jsonReservations []map[string]interface{} + for id, res := range p.reservations { + jsonReservations = append(jsonReservations, map[string]interface{}{ + "id": id.sid, + "ft": id.ft, + "res": res, + }) + } + + log.Warnw("negative reserved storage", "reserved", stat.Reserved, "origResv", p.reserved, "reservations", len(p.reservations), "newReserveOnDisk", newReserveOnDisk, "reservations", jsonReservations) stat.Reserved = 0 } @@ -199,7 +229,7 @@ func (st *Local) OpenPath(ctx context.Context, p string) error { maxStorage: meta.MaxStorage, reserved: 0, - reservations: map[abi.SectorID]storiface.SectorFileType{}, + reservations: map[sectorFile]int64{}, } fst, _, err := out.stat(st.localStorage) @@ -430,7 +460,7 @@ func (st *Local) reportStorage(ctx context.Context) { } } -func (st *Local) Reserve(ctx context.Context, sid storiface.SectorRef, ft storiface.SectorFileType, storageIDs storiface.SectorPaths, overheadTab map[storiface.SectorFileType]int) (func(), error) { +func (st *Local) Reserve(ctx context.Context, sid storiface.SectorRef, ft storiface.SectorFileType, storageIDs storiface.SectorPaths, overheadTab map[storiface.SectorFileType]int) (release func(), err error) { ssize, err := sid.ProofType.SectorSize() if err != nil { return nil, err @@ -438,11 +468,35 @@ func (st *Local) Reserve(ctx context.Context, sid storiface.SectorRef, ft storif st.localLk.Lock() - done := func() {} - deferredDone := func() { done() } + var releaseCalled bool + + // double release debug guard + var firstDonebuf []byte + var releaseFuncs []func() + + release = func() { + for _, releaseFunc := range releaseFuncs { + releaseFunc() + } + + // debug guard against double release call + if releaseCalled { + curStack := make([]byte, 20480) + curStack = curStack[:runtime.Stack(curStack, false)] + + log.Errorw("double release call", "sector", sid, "fileType", ft, "prevStack", string(firstDonebuf), "curStack", string(curStack)) + } + + firstDonebuf = make([]byte, 20480) + firstDonebuf = firstDonebuf[:runtime.Stack(firstDonebuf, false)] + + releaseCalled = true + } + + cleanupOnError := func() { release() } defer func() { st.localLk.Unlock() - deferredDone() + cleanupOnError() }() for _, fileType := range ft.AllSet() { @@ -453,13 +507,13 @@ func (st *Local) Reserve(ctx context.Context, sid storiface.SectorRef, ft storif return nil, errPathNotFound } - stat, resvOnDisk, err := p.stat(st.localStorage, statExistingSectorForReservation{sid.ID, fileType}) + overhead := int64(overheadTab[fileType]) * int64(ssize) / storiface.FSOverheadDen + + stat, resvOnDisk, err := p.stat(st.localStorage, statExistingSectorForReservation{sid.ID, fileType, overhead}) if err != nil { return nil, xerrors.Errorf("getting local storage stat: %w", err) } - overhead := int64(overheadTab[fileType]) * int64(ssize) / storiface.FSOverheadDen - if overhead-resvOnDisk < 0 { log.Errorw("negative overhead vs on-disk data", "overhead", overhead, "on-disk", resvOnDisk, "id", id, "sector", sid, "fileType", fileType) resvOnDisk = overhead @@ -469,27 +523,28 @@ func (st *Local) Reserve(ctx context.Context, sid storiface.SectorRef, ft storif return nil, storiface.Err(storiface.ErrTempAllocateSpace, xerrors.Errorf("can't reserve %d bytes in '%s' (id:%s), only %d available", overhead, p.local, id, stat.Available)) } + resID := sectorFile{sid.ID, fileType} + + log.Debugw("reserve add", "id", id, "sector", sid, "fileType", fileType, "overhead", overhead, "reserved-before", p.reserved, "reserved-after", p.reserved+overhead) + p.reserved += overhead - p.reservations[sid.ID] |= fileType - - prevDone := done - saveFileType := fileType - done = func() { - prevDone() + p.reservations[resID] = overhead + releaseFuncs = append(releaseFuncs, func() { st.localLk.Lock() defer st.localLk.Unlock() + log.Debugw("reserve release", "id", id, "sector", sid, "fileType", fileType, "overhead", overhead, "reserved-before", p.reserved, "reserved-after", p.reserved-overhead) + p.reserved -= overhead - p.reservations[sid.ID] ^= saveFileType - if p.reservations[sid.ID] == storiface.FTNone { - delete(p.reservations, sid.ID) - } - } + delete(p.reservations, resID) + }) } - deferredDone = func() {} - return done, nil + // no errors, don't cleanup, caller will call release + cleanupOnError = func() {} + + return release, nil } func (st *Local) AcquireSector(ctx context.Context, sid storiface.SectorRef, existing storiface.SectorFileType, allocate storiface.SectorFileType, pathType storiface.PathType, op storiface.AcquireMode, opts ...storiface.AcquireOption) (storiface.SectorPaths, storiface.SectorPaths, error) { From 3ebb92a976a867c7f5d5a18fe07e3ef78e7c19e0 Mon Sep 17 00:00:00 2001 From: LexLuthr <88259624+LexLuthr@users.noreply.github.com> Date: Tue, 16 Apr 2024 04:27:31 +0400 Subject: [PATCH 353/394] fix: curio: fix incorrect null check for varchar column (#11881) * err check empty string * log skipped tasks --- lib/harmony/harmonytask/harmonytask.go | 3 ++- storage/paths/db_index.go | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/harmony/harmonytask/harmonytask.go b/lib/harmony/harmonytask/harmonytask.go index dc71b299c..2d537868d 100644 --- a/lib/harmony/harmonytask/harmonytask.go +++ b/lib/harmony/harmonytask/harmonytask.go @@ -313,7 +313,8 @@ func (e *TaskEngine) pollerTryAllWork() bool { resources.CleanupMachines(e.ctx, e.db) } for _, v := range e.handlers { - if v.AssertMachineHasCapacity() != nil { + if err := v.AssertMachineHasCapacity(); err != nil { + log.Debugf("skipped scheduling %s type tasks on due to %s", v.Name, err.Error()) continue } var unownedTasks []TaskID diff --git a/storage/paths/db_index.go b/storage/paths/db_index.go index 470d5c33c..792395445 100644 --- a/storage/paths/db_index.go +++ b/storage/paths/db_index.go @@ -723,7 +723,7 @@ func (dbi *DBIndex) StorageBestAlloc(ctx context.Context, allocate storiface.Sec FROM storage_path WHERE available >= $1 and NOW()-($2 * INTERVAL '1 second') < last_heartbeat - and heartbeat_err is null + and heartbeat_err = '' and (($3 and can_seal = TRUE) or ($4 and can_store = TRUE)) order by (available::numeric * weight) desc`, spaceReq, From 649a1b51531ccea4d4aa1f6b84ace97a2944425e Mon Sep 17 00:00:00 2001 From: LexLuthr <88259624+LexLuthr@users.noreply.github.com> Date: Tue, 16 Apr 2024 04:29:56 +0400 Subject: [PATCH 354/394] fix: curio: common commands (#11879) * fix common commands * fix start log * make gen --- api/api_curio.go | 3 + api/proxy_gen.go | 26 ++ build/openrpc/full.json | 486 +++++++++++------------ build/openrpc/gateway.json | 192 ++++----- build/openrpc/miner.json | 260 ++++++------ build/openrpc/worker.json | 74 ++-- cmd/curio/cli.go | 47 +++ cmd/curio/log.go | 105 +++++ cmd/curio/main.go | 31 +- cmd/curio/rpc/rpc.go | 8 + cmd/curio/tasks/tasks.go | 11 +- documentation/en/api-v0-methods-curio.md | 35 ++ documentation/en/cli-curio.md | 162 +------- 13 files changed, 780 insertions(+), 660 deletions(-) create mode 100644 cmd/curio/log.go diff --git a/api/api_curio.go b/api/api_curio.go index c94c8a9ab..6f31ef7e0 100644 --- a/api/api_curio.go +++ b/api/api_curio.go @@ -25,6 +25,9 @@ type Curio interface { StorageInfo(context.Context, storiface.ID) (storiface.StorageInfo, error) //perm:admin StorageFindSector(ctx context.Context, sector abi.SectorID, ft storiface.SectorFileType, ssize abi.SectorSize, allowFetch bool) ([]storiface.SectorStorageInfo, error) //perm:admin + LogList(ctx context.Context) ([]string, error) //perm:read + LogSetLevel(ctx context.Context, subsystem, level string) error //perm:admin + // Trigger shutdown Shutdown(context.Context) error //perm:admin } diff --git a/api/proxy_gen.go b/api/proxy_gen.go index 5530c79b7..c36c62d57 100644 --- a/api/proxy_gen.go +++ b/api/proxy_gen.go @@ -122,6 +122,10 @@ type CurioStruct struct { type CurioMethods struct { AllocatePieceToSector func(p0 context.Context, p1 address.Address, p2 PieceDealInfo, p3 int64, p4 url.URL, p5 http.Header) (SectorOffset, error) `perm:"write"` + LogList func(p0 context.Context) ([]string, error) `perm:"read"` + + LogSetLevel func(p0 context.Context, p1 string, p2 string) error `perm:"admin"` + Shutdown func(p0 context.Context) error `perm:"admin"` StorageAddLocal func(p0 context.Context, p1 string) error `perm:"admin"` @@ -1499,6 +1503,28 @@ func (s *CurioStub) AllocatePieceToSector(p0 context.Context, p1 address.Address return *new(SectorOffset), ErrNotSupported } +func (s *CurioStruct) LogList(p0 context.Context) ([]string, error) { + if s.Internal.LogList == nil { + return *new([]string), ErrNotSupported + } + return s.Internal.LogList(p0) +} + +func (s *CurioStub) LogList(p0 context.Context) ([]string, error) { + return *new([]string), ErrNotSupported +} + +func (s *CurioStruct) LogSetLevel(p0 context.Context, p1 string, p2 string) error { + if s.Internal.LogSetLevel == nil { + return ErrNotSupported + } + return s.Internal.LogSetLevel(p0, p1, p2) +} + +func (s *CurioStub) LogSetLevel(p0 context.Context, p1 string, p2 string) error { + return ErrNotSupported +} + func (s *CurioStruct) Shutdown(p0 context.Context) error { if s.Internal.Shutdown == nil { return ErrNotSupported diff --git a/build/openrpc/full.json b/build/openrpc/full.json index 35e2fd9e4..ae12bc302 100644 --- a/build/openrpc/full.json +++ b/build/openrpc/full.json @@ -37,7 +37,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1612" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1638" } }, { @@ -60,7 +60,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1623" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1649" } }, { @@ -103,7 +103,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1634" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1660" } }, { @@ -214,7 +214,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1656" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1682" } }, { @@ -454,7 +454,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1667" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1693" } }, { @@ -685,7 +685,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1678" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1704" } }, { @@ -784,7 +784,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1689" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1715" } }, { @@ -816,7 +816,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1700" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1726" } }, { @@ -922,7 +922,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1711" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1737" } }, { @@ -1019,7 +1019,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1722" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1748" } }, { @@ -1078,7 +1078,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1733" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1759" } }, { @@ -1171,7 +1171,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1744" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1770" } }, { @@ -1255,7 +1255,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1755" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1781" } }, { @@ -1355,7 +1355,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1766" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1792" } }, { @@ -1411,7 +1411,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1777" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1803" } }, { @@ -1484,7 +1484,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1788" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1814" } }, { @@ -1557,7 +1557,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1799" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1825" } }, { @@ -1604,7 +1604,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1810" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1836" } }, { @@ -1636,7 +1636,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1821" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1847" } }, { @@ -1691,7 +1691,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1832" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1858" } }, { @@ -1743,7 +1743,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1854" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1880" } }, { @@ -1780,7 +1780,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1865" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1891" } }, { @@ -1827,7 +1827,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1876" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1902" } }, { @@ -1874,7 +1874,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1887" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1913" } }, { @@ -1954,7 +1954,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1898" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1924" } }, { @@ -2006,7 +2006,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1909" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1935" } }, { @@ -2065,7 +2065,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1920" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1946" } }, { @@ -2136,7 +2136,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1931" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1957" } }, { @@ -2177,7 +2177,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1942" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1968" } }, { @@ -2245,7 +2245,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1964" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1990" } }, { @@ -2306,7 +2306,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1975" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2001" } }, { @@ -2413,7 +2413,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1986" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2012" } }, { @@ -2569,7 +2569,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1997" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2023" } }, { @@ -2635,7 +2635,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2008" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2034" } }, { @@ -2976,7 +2976,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2019" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2045" } }, { @@ -3021,7 +3021,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2030" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2056" } }, { @@ -3068,7 +3068,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2063" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2089" } }, { @@ -3139,7 +3139,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2074" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2100" } }, { @@ -3282,7 +3282,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2085" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2111" } }, { @@ -3612,7 +3612,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2096" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2122" } }, { @@ -3680,7 +3680,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2107" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2133" } }, { @@ -3914,7 +3914,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2118" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2144" } }, { @@ -4077,7 +4077,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2129" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2155" } }, { @@ -4160,7 +4160,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2140" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2166" } }, { @@ -4201,7 +4201,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2151" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2177" } }, { @@ -4272,7 +4272,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2162" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2188" } }, { @@ -4416,7 +4416,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2173" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2199" } }, { @@ -4456,7 +4456,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2184" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2210" } }, { @@ -4497,7 +4497,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2195" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2221" } }, { @@ -4622,7 +4622,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2206" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2232" } }, { @@ -4747,7 +4747,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2217" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2243" } }, { @@ -4786,7 +4786,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2228" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2254" } }, { @@ -4833,7 +4833,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2239" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2265" } }, { @@ -4888,7 +4888,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2250" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2276" } }, { @@ -4917,7 +4917,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2261" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2287" } }, { @@ -5054,7 +5054,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2272" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2298" } }, { @@ -5083,7 +5083,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2283" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2309" } }, { @@ -5137,7 +5137,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2294" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2320" } }, { @@ -5228,7 +5228,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2305" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2331" } }, { @@ -5256,7 +5256,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2316" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2342" } }, { @@ -5346,7 +5346,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2327" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2353" } }, { @@ -5602,7 +5602,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2338" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2364" } }, { @@ -5847,7 +5847,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2349" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2375" } }, { @@ -5903,7 +5903,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2360" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2386" } }, { @@ -5950,7 +5950,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2371" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2397" } }, { @@ -6048,7 +6048,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2382" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2408" } }, { @@ -6114,7 +6114,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2393" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2419" } }, { @@ -6180,7 +6180,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2404" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2430" } }, { @@ -6289,7 +6289,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2415" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2441" } }, { @@ -6347,7 +6347,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2426" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2452" } }, { @@ -6469,7 +6469,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2437" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2463" } }, { @@ -6673,7 +6673,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2448" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2474" } }, { @@ -6868,7 +6868,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2459" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2485" } }, { @@ -7055,7 +7055,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2470" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2496" } }, { @@ -7259,7 +7259,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2481" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2507" } }, { @@ -7350,7 +7350,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2492" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2518" } }, { @@ -7408,7 +7408,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2503" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2529" } }, { @@ -7666,7 +7666,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2514" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2540" } }, { @@ -7941,7 +7941,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2525" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2551" } }, { @@ -7969,7 +7969,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2536" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2562" } }, { @@ -8007,7 +8007,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2547" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2573" } }, { @@ -8115,7 +8115,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2558" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2584" } }, { @@ -8153,7 +8153,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2569" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2595" } }, { @@ -8182,7 +8182,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2580" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2606" } }, { @@ -8245,7 +8245,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2591" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2617" } }, { @@ -8308,7 +8308,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2602" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2628" } }, { @@ -8353,7 +8353,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2613" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2639" } }, { @@ -8475,7 +8475,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2624" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2650" } }, { @@ -8630,7 +8630,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2635" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2661" } }, { @@ -8684,7 +8684,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2646" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2672" } }, { @@ -8738,7 +8738,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2657" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2683" } }, { @@ -8793,7 +8793,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2668" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2694" } }, { @@ -8936,7 +8936,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2679" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2705" } }, { @@ -9063,7 +9063,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2690" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2716" } }, { @@ -9165,7 +9165,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2701" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2727" } }, { @@ -9388,7 +9388,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2712" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2738" } }, { @@ -9571,7 +9571,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2723" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2749" } }, { @@ -9651,7 +9651,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2734" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2760" } }, { @@ -9696,7 +9696,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2745" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2771" } }, { @@ -9752,7 +9752,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2756" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2782" } }, { @@ -9832,7 +9832,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2767" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2793" } }, { @@ -9912,7 +9912,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2778" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2804" } }, { @@ -10397,7 +10397,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2789" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2815" } }, { @@ -10591,7 +10591,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2800" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2826" } }, { @@ -10746,7 +10746,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2811" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2837" } }, { @@ -10995,7 +10995,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2822" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2848" } }, { @@ -11150,7 +11150,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2833" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2859" } }, { @@ -11327,7 +11327,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2844" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2870" } }, { @@ -11425,7 +11425,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2855" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2881" } }, { @@ -11590,7 +11590,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2866" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2892" } }, { @@ -11629,7 +11629,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2877" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2903" } }, { @@ -11694,7 +11694,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2888" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2914" } }, { @@ -11740,7 +11740,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2899" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2925" } }, { @@ -11890,7 +11890,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2910" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2936" } }, { @@ -12027,7 +12027,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2921" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2947" } }, { @@ -12258,7 +12258,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2932" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2958" } }, { @@ -12395,7 +12395,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2943" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2969" } }, { @@ -12560,7 +12560,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2954" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2980" } }, { @@ -12637,7 +12637,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2965" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2991" } }, { @@ -12832,7 +12832,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2987" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3013" } }, { @@ -13011,7 +13011,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2998" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3024" } }, { @@ -13173,7 +13173,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3009" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3035" } }, { @@ -13321,7 +13321,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3020" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3046" } }, { @@ -13549,7 +13549,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3031" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3057" } }, { @@ -13697,7 +13697,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3042" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3068" } }, { @@ -13909,7 +13909,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3053" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3079" } }, { @@ -14115,7 +14115,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3064" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3090" } }, { @@ -14183,7 +14183,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3075" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3101" } }, { @@ -14300,7 +14300,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3086" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3112" } }, { @@ -14391,7 +14391,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3097" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3123" } }, { @@ -14477,7 +14477,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3108" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3134" } }, { @@ -14672,7 +14672,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3119" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3145" } }, { @@ -14834,7 +14834,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3130" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3156" } }, { @@ -15030,7 +15030,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3141" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3167" } }, { @@ -15210,7 +15210,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3152" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3178" } }, { @@ -15373,7 +15373,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3163" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3189" } }, { @@ -15400,7 +15400,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3174" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3200" } }, { @@ -15427,7 +15427,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3185" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3211" } }, { @@ -15526,7 +15526,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3196" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3222" } }, { @@ -15572,7 +15572,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3207" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3233" } }, { @@ -15672,7 +15672,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3218" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3244" } }, { @@ -15788,7 +15788,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3229" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3255" } }, { @@ -15836,7 +15836,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3240" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3266" } }, { @@ -15928,7 +15928,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3251" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3277" } }, { @@ -16043,7 +16043,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3262" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3288" } }, { @@ -16091,7 +16091,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3273" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3299" } }, { @@ -16128,7 +16128,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3284" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3310" } }, { @@ -16400,7 +16400,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3295" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3321" } }, { @@ -16448,7 +16448,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3306" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3332" } }, { @@ -16506,7 +16506,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3317" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3343" } }, { @@ -16711,7 +16711,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3328" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3354" } }, { @@ -16914,7 +16914,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3339" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3365" } }, { @@ -17083,7 +17083,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3350" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3376" } }, { @@ -17287,7 +17287,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3361" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3387" } }, { @@ -17454,7 +17454,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3372" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3398" } }, { @@ -17661,7 +17661,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3383" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3409" } }, { @@ -17729,7 +17729,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3394" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3420" } }, { @@ -17781,7 +17781,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3405" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3431" } }, { @@ -17830,7 +17830,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3416" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3442" } }, { @@ -17921,7 +17921,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3427" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3453" } }, { @@ -18427,7 +18427,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3438" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3464" } }, { @@ -18533,7 +18533,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3449" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3475" } }, { @@ -18585,7 +18585,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3460" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3486" } }, { @@ -19137,7 +19137,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3471" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3497" } }, { @@ -19251,7 +19251,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3482" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3508" } }, { @@ -19348,7 +19348,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3493" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3519" } }, { @@ -19448,7 +19448,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3504" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3530" } }, { @@ -19536,7 +19536,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3515" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3541" } }, { @@ -19636,7 +19636,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3526" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3552" } }, { @@ -19723,7 +19723,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3537" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3563" } }, { @@ -19814,7 +19814,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3548" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3574" } }, { @@ -19939,7 +19939,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3559" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3585" } }, { @@ -20048,7 +20048,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3570" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3596" } }, { @@ -20118,7 +20118,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3581" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3607" } }, { @@ -20221,7 +20221,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3592" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3618" } }, { @@ -20282,7 +20282,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3603" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3629" } }, { @@ -20412,7 +20412,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3614" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3640" } }, { @@ -20519,7 +20519,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3625" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3651" } }, { @@ -20728,7 +20728,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3636" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3662" } }, { @@ -20805,7 +20805,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3647" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3673" } }, { @@ -20882,7 +20882,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3658" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3684" } }, { @@ -20991,7 +20991,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3669" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3695" } }, { @@ -21100,7 +21100,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3680" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3706" } }, { @@ -21161,7 +21161,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3691" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3717" } }, { @@ -21271,7 +21271,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3702" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3728" } }, { @@ -21332,7 +21332,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3713" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3739" } }, { @@ -21400,7 +21400,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3724" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3750" } }, { @@ -21468,7 +21468,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3735" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3761" } }, { @@ -21549,7 +21549,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3746" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3772" } }, { @@ -21698,7 +21698,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3757" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3783" } }, { @@ -21770,7 +21770,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3768" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3794" } }, { @@ -21929,7 +21929,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3779" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3805" } }, { @@ -22094,7 +22094,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3790" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3816" } }, { @@ -22164,7 +22164,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3801" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3827" } }, { @@ -22232,7 +22232,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3812" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3838" } }, { @@ -22325,7 +22325,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3823" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3849" } }, { @@ -22396,7 +22396,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3834" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3860" } }, { @@ -22597,7 +22597,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3845" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3871" } }, { @@ -22729,7 +22729,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3856" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3882" } }, { @@ -22866,7 +22866,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3867" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3893" } }, { @@ -22977,7 +22977,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3878" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3904" } }, { @@ -23109,7 +23109,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3889" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3915" } }, { @@ -23240,7 +23240,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3900" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3926" } }, { @@ -23311,7 +23311,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3911" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3937" } }, { @@ -23395,7 +23395,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3922" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3948" } }, { @@ -23481,7 +23481,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3933" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3959" } }, { @@ -23664,7 +23664,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3944" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3970" } }, { @@ -23691,7 +23691,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3955" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3981" } }, { @@ -23744,7 +23744,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3966" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3992" } }, { @@ -23832,7 +23832,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3977" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4003" } }, { @@ -24283,7 +24283,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3988" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4014" } }, { @@ -24450,7 +24450,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3999" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4025" } }, { @@ -24548,7 +24548,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4010" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4036" } }, { @@ -24721,7 +24721,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4021" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4047" } }, { @@ -24819,7 +24819,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4032" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4058" } }, { @@ -24970,7 +24970,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4043" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4069" } }, { @@ -25055,7 +25055,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4054" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4080" } }, { @@ -25123,7 +25123,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4065" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4091" } }, { @@ -25175,7 +25175,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4076" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4102" } }, { @@ -25243,7 +25243,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4087" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4113" } }, { @@ -25404,7 +25404,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4098" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4124" } }, { @@ -25451,7 +25451,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4120" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4146" } }, { @@ -25498,7 +25498,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4131" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4157" } }, { @@ -25541,7 +25541,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4153" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4179" } }, { @@ -25637,7 +25637,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4164" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4190" } }, { @@ -25903,7 +25903,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4175" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4201" } }, { @@ -25926,7 +25926,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4186" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4212" } }, { @@ -25969,7 +25969,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4197" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4223" } }, { @@ -26020,7 +26020,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4208" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4234" } }, { @@ -26065,7 +26065,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4219" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4245" } }, { @@ -26093,7 +26093,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4230" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4256" } }, { @@ -26133,7 +26133,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4241" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4267" } }, { @@ -26192,7 +26192,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4252" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4278" } }, { @@ -26236,7 +26236,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4263" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4289" } }, { @@ -26295,7 +26295,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4274" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4300" } }, { @@ -26332,7 +26332,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4285" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4311" } }, { @@ -26376,7 +26376,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4296" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4322" } }, { @@ -26416,7 +26416,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4307" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4333" } }, { @@ -26491,7 +26491,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4318" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4344" } }, { @@ -26699,7 +26699,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4329" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4355" } }, { @@ -26743,7 +26743,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4340" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4366" } }, { @@ -26833,7 +26833,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4351" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4377" } }, { @@ -26860,7 +26860,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4362" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4388" } } ] diff --git a/build/openrpc/gateway.json b/build/openrpc/gateway.json index 039648e59..faa2c8536 100644 --- a/build/openrpc/gateway.json +++ b/build/openrpc/gateway.json @@ -242,7 +242,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4373" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4399" } }, { @@ -473,7 +473,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4384" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4410" } }, { @@ -572,7 +572,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4395" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4421" } }, { @@ -604,7 +604,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4406" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4432" } }, { @@ -710,7 +710,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4417" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4443" } }, { @@ -803,7 +803,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4428" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4454" } }, { @@ -887,7 +887,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4439" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4465" } }, { @@ -987,7 +987,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4450" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4476" } }, { @@ -1043,7 +1043,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4461" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4487" } }, { @@ -1116,7 +1116,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4472" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4498" } }, { @@ -1189,7 +1189,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4483" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4509" } }, { @@ -1236,7 +1236,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4494" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4520" } }, { @@ -1268,7 +1268,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4505" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4531" } }, { @@ -1305,7 +1305,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4527" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4553" } }, { @@ -1352,7 +1352,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4538" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4564" } }, { @@ -1392,7 +1392,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4549" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4575" } }, { @@ -1439,7 +1439,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4560" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4586" } }, { @@ -1468,7 +1468,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4571" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4597" } }, { @@ -1605,7 +1605,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4582" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4608" } }, { @@ -1634,7 +1634,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4593" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4619" } }, { @@ -1688,7 +1688,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4604" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4630" } }, { @@ -1779,7 +1779,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4615" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4641" } }, { @@ -1807,7 +1807,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4626" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4652" } }, { @@ -1897,7 +1897,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4637" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4663" } }, { @@ -2153,7 +2153,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4648" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4674" } }, { @@ -2398,7 +2398,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4659" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4685" } }, { @@ -2454,7 +2454,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4670" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4696" } }, { @@ -2501,7 +2501,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4681" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4707" } }, { @@ -2599,7 +2599,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4692" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4718" } }, { @@ -2665,7 +2665,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4703" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4729" } }, { @@ -2731,7 +2731,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4714" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4740" } }, { @@ -2840,7 +2840,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4725" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4751" } }, { @@ -2898,7 +2898,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4736" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4762" } }, { @@ -3020,7 +3020,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4747" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4773" } }, { @@ -3207,7 +3207,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4758" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4784" } }, { @@ -3411,7 +3411,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4769" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4795" } }, { @@ -3502,7 +3502,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4780" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4806" } }, { @@ -3560,7 +3560,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4791" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4817" } }, { @@ -3818,7 +3818,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4802" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4828" } }, { @@ -4093,7 +4093,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4813" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4839" } }, { @@ -4121,7 +4121,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4824" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4850" } }, { @@ -4159,7 +4159,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4835" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4861" } }, { @@ -4267,7 +4267,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4846" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4872" } }, { @@ -4305,7 +4305,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4857" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4883" } }, { @@ -4334,7 +4334,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4868" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4894" } }, { @@ -4397,7 +4397,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4879" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4905" } }, { @@ -4460,7 +4460,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4890" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4916" } }, { @@ -4505,7 +4505,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4901" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4927" } }, { @@ -4627,7 +4627,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4912" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4938" } }, { @@ -4782,7 +4782,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4923" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4949" } }, { @@ -4836,7 +4836,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4934" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4960" } }, { @@ -4890,7 +4890,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4945" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4971" } }, { @@ -4992,7 +4992,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4956" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4982" } }, { @@ -5215,7 +5215,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4967" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4993" } }, { @@ -5398,7 +5398,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4978" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5004" } }, { @@ -5592,7 +5592,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4989" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5015" } }, { @@ -5638,7 +5638,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5000" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5026" } }, { @@ -5788,7 +5788,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5011" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5037" } }, { @@ -5925,7 +5925,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5022" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5048" } }, { @@ -5993,7 +5993,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5033" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5059" } }, { @@ -6110,7 +6110,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5044" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5070" } }, { @@ -6201,7 +6201,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5055" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5081" } }, { @@ -6287,7 +6287,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5066" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5092" } }, { @@ -6314,7 +6314,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5077" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5103" } }, { @@ -6341,7 +6341,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5088" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5114" } }, { @@ -6409,7 +6409,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5099" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5125" } }, { @@ -6915,7 +6915,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5110" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5136" } }, { @@ -7012,7 +7012,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5121" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5147" } }, { @@ -7112,7 +7112,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5132" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5158" } }, { @@ -7212,7 +7212,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5143" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5169" } }, { @@ -7337,7 +7337,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5154" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5180" } }, { @@ -7446,7 +7446,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5165" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5191" } }, { @@ -7549,7 +7549,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5176" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5202" } }, { @@ -7679,7 +7679,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5187" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5213" } }, { @@ -7786,7 +7786,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5198" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5224" } }, { @@ -7847,7 +7847,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5209" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5235" } }, { @@ -7915,7 +7915,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5220" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5246" } }, { @@ -7996,7 +7996,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5231" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5257" } }, { @@ -8155,7 +8155,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5242" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5268" } }, { @@ -8248,7 +8248,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5253" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5279" } }, { @@ -8449,7 +8449,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5264" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5290" } }, { @@ -8560,7 +8560,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5275" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5301" } }, { @@ -8691,7 +8691,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5286" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5312" } }, { @@ -8777,7 +8777,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5297" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5323" } }, { @@ -8804,7 +8804,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5308" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5334" } }, { @@ -8857,7 +8857,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5319" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5345" } }, { @@ -8945,7 +8945,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5330" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5356" } }, { @@ -9396,7 +9396,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5341" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5367" } }, { @@ -9563,7 +9563,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5352" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5378" } }, { @@ -9736,7 +9736,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5363" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5389" } }, { @@ -9804,7 +9804,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5374" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5400" } }, { @@ -9872,7 +9872,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5385" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5411" } }, { @@ -10033,7 +10033,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5396" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5422" } }, { @@ -10078,7 +10078,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5418" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5444" } }, { @@ -10123,7 +10123,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5429" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5455" } }, { @@ -10150,7 +10150,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5440" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5466" } } ] diff --git a/build/openrpc/miner.json b/build/openrpc/miner.json index a06a1c72c..bc374e63e 100644 --- a/build/openrpc/miner.json +++ b/build/openrpc/miner.json @@ -30,7 +30,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5726" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5752" } }, { @@ -109,7 +109,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5737" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5763" } }, { @@ -155,7 +155,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5748" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5774" } }, { @@ -203,7 +203,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5759" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5785" } }, { @@ -251,7 +251,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5770" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5796" } }, { @@ -354,7 +354,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5781" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5807" } }, { @@ -428,7 +428,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5792" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5818" } }, { @@ -591,7 +591,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5803" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5829" } }, { @@ -742,7 +742,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5814" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5840" } }, { @@ -781,7 +781,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5825" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5851" } }, { @@ -833,7 +833,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5836" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5862" } }, { @@ -872,7 +872,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5858" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5884" } }, { @@ -924,7 +924,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5869" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5895" } }, { @@ -996,7 +996,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5880" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5906" } }, { @@ -1035,7 +1035,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5891" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5917" } }, { @@ -1074,7 +1074,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5902" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5928" } }, { @@ -1101,7 +1101,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5913" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5939" } }, { @@ -1128,7 +1128,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5924" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5950" } }, { @@ -1155,7 +1155,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5935" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5961" } }, { @@ -1182,7 +1182,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5946" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5972" } }, { @@ -1209,7 +1209,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5957" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5983" } }, { @@ -1236,7 +1236,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5968" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5994" } }, { @@ -1294,7 +1294,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5979" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6005" } }, { @@ -1421,7 +1421,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5990" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6016" } }, { @@ -1461,7 +1461,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6001" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6027" } }, { @@ -1500,7 +1500,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6012" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6038" } }, { @@ -1539,7 +1539,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6023" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6049" } }, { @@ -1578,7 +1578,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6034" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6060" } }, { @@ -1617,7 +1617,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6045" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6071" } }, { @@ -1656,7 +1656,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6056" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6082" } }, { @@ -1695,7 +1695,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6067" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6093" } }, { @@ -1747,7 +1747,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6078" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6104" } }, { @@ -1770,7 +1770,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6089" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6115" } }, { @@ -1813,7 +1813,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6100" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6126" } }, { @@ -1884,7 +1884,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6111" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6137" } }, { @@ -2265,7 +2265,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6122" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6148" } }, { @@ -2364,7 +2364,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6144" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6170" } }, { @@ -2415,7 +2415,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6166" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6192" } }, { @@ -2473,7 +2473,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6177" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6203" } }, { @@ -2616,7 +2616,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6188" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6214" } }, { @@ -2743,7 +2743,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6199" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6225" } }, { @@ -3007,7 +3007,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6210" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6236" } }, { @@ -3044,7 +3044,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6221" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6247" } }, { @@ -3182,7 +3182,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6232" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6258" } }, { @@ -3205,7 +3205,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6243" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6269" } }, { @@ -3276,7 +3276,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6254" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6280" } }, { @@ -3319,7 +3319,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6265" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6291" } }, { @@ -3426,7 +3426,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6276" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6302" } }, { @@ -3489,7 +3489,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6287" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6313" } }, { @@ -3521,7 +3521,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6298" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6324" } }, { @@ -3609,7 +3609,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6309" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6335" } }, { @@ -3700,7 +3700,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6320" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6346" } }, { @@ -3740,7 +3740,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6331" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6357" } }, { @@ -3780,7 +3780,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6342" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6368" } }, { @@ -3821,7 +3821,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6353" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6379" } }, { @@ -3889,7 +3889,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6364" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6390" } }, { @@ -4020,7 +4020,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6375" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6401" } }, { @@ -4151,7 +4151,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6386" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6412" } }, { @@ -4251,7 +4251,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6397" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6423" } }, { @@ -4351,7 +4351,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6408" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6434" } }, { @@ -4451,7 +4451,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6419" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6445" } }, { @@ -4551,7 +4551,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6430" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6456" } }, { @@ -4651,7 +4651,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6441" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6467" } }, { @@ -4751,7 +4751,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6452" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6478" } }, { @@ -4875,7 +4875,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6463" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6489" } }, { @@ -4999,7 +4999,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6474" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6500" } }, { @@ -5114,7 +5114,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6485" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6511" } }, { @@ -5214,7 +5214,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6496" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6522" } }, { @@ -5347,7 +5347,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6507" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6533" } }, { @@ -5471,7 +5471,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6518" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6544" } }, { @@ -5595,7 +5595,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6529" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6555" } }, { @@ -5719,7 +5719,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6540" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6566" } }, { @@ -5852,7 +5852,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6551" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6577" } }, { @@ -5952,7 +5952,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6562" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6588" } }, { @@ -5993,7 +5993,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6573" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6599" } }, { @@ -6065,7 +6065,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6584" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6610" } }, { @@ -6115,7 +6115,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6595" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6621" } }, { @@ -6159,7 +6159,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6606" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6632" } }, { @@ -6200,7 +6200,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6617" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6643" } }, { @@ -6444,7 +6444,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6628" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6654" } }, { @@ -6518,7 +6518,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6639" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6665" } }, { @@ -6568,7 +6568,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6650" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6676" } }, { @@ -6597,7 +6597,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6661" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6687" } }, { @@ -6626,7 +6626,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6672" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6698" } }, { @@ -6682,7 +6682,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6683" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6709" } }, { @@ -6705,7 +6705,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6694" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6720" } }, { @@ -6765,7 +6765,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6705" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6731" } }, { @@ -6804,7 +6804,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6716" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6742" } }, { @@ -6844,7 +6844,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6727" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6753" } }, { @@ -6917,7 +6917,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6738" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6764" } }, { @@ -6981,7 +6981,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6749" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6775" } }, { @@ -7044,7 +7044,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6760" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6786" } }, { @@ -7094,7 +7094,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6771" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6797" } }, { @@ -7653,7 +7653,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6782" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6808" } }, { @@ -7694,7 +7694,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6793" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6819" } }, { @@ -7735,7 +7735,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6804" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6830" } }, { @@ -7776,7 +7776,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6815" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6841" } }, { @@ -7817,7 +7817,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6826" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6852" } }, { @@ -7858,7 +7858,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6837" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6863" } }, { @@ -7889,7 +7889,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6848" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6874" } }, { @@ -7939,7 +7939,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6859" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6885" } }, { @@ -7980,7 +7980,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6870" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6896" } }, { @@ -8019,7 +8019,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6881" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6907" } }, { @@ -8083,7 +8083,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6892" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6918" } }, { @@ -8141,7 +8141,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6903" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6929" } }, { @@ -8588,7 +8588,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6914" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6940" } }, { @@ -8624,7 +8624,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6925" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6951" } }, { @@ -8767,7 +8767,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6936" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6962" } }, { @@ -8823,7 +8823,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6947" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6973" } }, { @@ -8862,7 +8862,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6958" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6984" } }, { @@ -9039,7 +9039,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6969" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6995" } }, { @@ -9091,7 +9091,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6980" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7006" } }, { @@ -9283,7 +9283,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6991" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7017" } }, { @@ -9383,7 +9383,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7002" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7028" } }, { @@ -9437,7 +9437,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7013" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7039" } }, { @@ -9476,7 +9476,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7024" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7050" } }, { @@ -9561,7 +9561,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7035" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7061" } }, { @@ -9755,7 +9755,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7046" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7072" } }, { @@ -9853,7 +9853,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7057" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7083" } }, { @@ -9985,7 +9985,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7068" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7094" } }, { @@ -10039,7 +10039,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7079" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7105" } }, { @@ -10073,7 +10073,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7090" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7116" } }, { @@ -10160,7 +10160,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7101" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7127" } }, { @@ -10214,7 +10214,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7112" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7138" } }, { @@ -10314,7 +10314,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7123" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7149" } }, { @@ -10391,7 +10391,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7134" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7160" } }, { @@ -10482,7 +10482,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7145" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7171" } }, { @@ -10521,7 +10521,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7156" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7182" } }, { @@ -10637,7 +10637,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7167" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7193" } }, { @@ -12737,7 +12737,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7178" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7204" } } ] diff --git a/build/openrpc/worker.json b/build/openrpc/worker.json index 0afaa17ae..220d2e1c9 100644 --- a/build/openrpc/worker.json +++ b/build/openrpc/worker.json @@ -161,7 +161,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7266" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7292" } }, { @@ -252,7 +252,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7277" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7303" } }, { @@ -420,7 +420,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7288" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7314" } }, { @@ -447,7 +447,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7299" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7325" } }, { @@ -597,7 +597,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7310" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7336" } }, { @@ -700,7 +700,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7321" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7347" } }, { @@ -803,7 +803,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7332" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7358" } }, { @@ -925,7 +925,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7343" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7369" } }, { @@ -1135,7 +1135,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7354" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7380" } }, { @@ -1306,7 +1306,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7365" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7391" } }, { @@ -3350,7 +3350,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7376" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7402" } }, { @@ -3470,7 +3470,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7387" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7413" } }, { @@ -3531,7 +3531,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7398" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7424" } }, { @@ -3569,7 +3569,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7409" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7435" } }, { @@ -3729,7 +3729,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7420" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7446" } }, { @@ -3913,7 +3913,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7431" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7457" } }, { @@ -4054,7 +4054,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7442" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7468" } }, { @@ -4107,7 +4107,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7453" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7479" } }, { @@ -4250,7 +4250,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7464" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7490" } }, { @@ -4474,7 +4474,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7475" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7501" } }, { @@ -4601,7 +4601,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7486" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7512" } }, { @@ -4768,7 +4768,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7497" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7523" } }, { @@ -4895,7 +4895,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7508" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7534" } }, { @@ -4933,7 +4933,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7519" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7545" } }, { @@ -4972,7 +4972,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7530" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7556" } }, { @@ -4995,7 +4995,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7541" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7567" } }, { @@ -5034,7 +5034,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7552" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7578" } }, { @@ -5057,7 +5057,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7563" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7589" } }, { @@ -5096,7 +5096,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7574" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7600" } }, { @@ -5130,7 +5130,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7585" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7611" } }, { @@ -5184,7 +5184,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7596" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7622" } }, { @@ -5223,7 +5223,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7607" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7633" } }, { @@ -5262,7 +5262,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7618" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7644" } }, { @@ -5297,7 +5297,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7629" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7655" } }, { @@ -5477,7 +5477,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7640" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7666" } }, { @@ -5506,7 +5506,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7651" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7677" } }, { @@ -5529,7 +5529,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7662" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7688" } } ] diff --git a/cmd/curio/cli.go b/cmd/curio/cli.go index 727de122d..3eec4b617 100644 --- a/cmd/curio/cli.go +++ b/cmd/curio/cli.go @@ -2,7 +2,9 @@ package main import ( "bufio" + "context" "encoding/base64" + "errors" "fmt" "net" "os" @@ -195,5 +197,50 @@ var cliCmd = &cli.Command{ }, Subcommands: []*cli.Command{ storageCmd, + logCmd, + waitApiCmd, + }, +} + +var waitApiCmd = &cli.Command{ + Name: "wait-api", + Usage: "Wait for Curio api to come online", + Flags: []cli.Flag{ + &cli.DurationFlag{ + Name: "timeout", + Usage: "duration to wait till fail", + Value: time.Second * 30, + }, + }, + Action: func(cctx *cli.Context) error { + ctx := lcli.ReqContext(cctx) + ctx, cancel := context.WithTimeout(ctx, cctx.Duration("timeout")) + defer cancel() + for { + if ctx.Err() != nil { + break + } + + api, closer, err := rpc.GetCurioAPI(cctx) + if err != nil { + fmt.Printf("Not online yet... (%s)\n", err) + time.Sleep(time.Second) + continue + } + defer closer() + + _, err = api.Version(ctx) + if err != nil { + return err + } + + return nil + } + + if errors.Is(ctx.Err(), context.DeadlineExceeded) { + return fmt.Errorf("timed out waiting for api to come online") + } + + return ctx.Err() }, } diff --git a/cmd/curio/log.go b/cmd/curio/log.go new file mode 100644 index 000000000..0af41a679 --- /dev/null +++ b/cmd/curio/log.go @@ -0,0 +1,105 @@ +package main + +import ( + "fmt" + + "github.com/urfave/cli/v2" + "golang.org/x/xerrors" + + lcli "github.com/filecoin-project/lotus/cli" + "github.com/filecoin-project/lotus/cmd/curio/rpc" +) + +var logCmd = &cli.Command{ + Name: "log", + Usage: "Manage logging", + Subcommands: []*cli.Command{ + LogList, + LogSetLevel, + }, +} + +var LogList = &cli.Command{ + Name: "list", + Usage: "List log systems", + Action: func(cctx *cli.Context) error { + minerApi, closer, err := rpc.GetCurioAPI(cctx) + if err != nil { + return err + } + defer closer() + + ctx := lcli.ReqContext(cctx) + + systems, err := minerApi.LogList(ctx) + if err != nil { + return err + } + + for _, system := range systems { + fmt.Println(system) + } + + return nil + }, +} + +var LogSetLevel = &cli.Command{ + Name: "set-level", + Usage: "Set log level", + ArgsUsage: "[level]", + Description: `Set the log level for logging systems: + + The system flag can be specified multiple times. + + eg) log set-level --system chain --system chainxchg debug + + Available Levels: + debug + info + warn + error + + Environment Variables: + GOLOG_LOG_LEVEL - Default log level for all log systems + GOLOG_LOG_FMT - Change output log format (json, nocolor) + GOLOG_FILE - Write logs to file + GOLOG_OUTPUT - Specify whether to output to file, stderr, stdout or a combination, i.e. file+stderr +`, + Flags: []cli.Flag{ + &cli.StringSliceFlag{ + Name: "system", + Usage: "limit to log system", + Value: &cli.StringSlice{}, + }, + }, + Action: func(cctx *cli.Context) error { + minerApi, closer, err := rpc.GetCurioAPI(cctx) + if err != nil { + return err + } + defer closer() + ctx := lcli.ReqContext(cctx) + + if !cctx.Args().Present() { + return fmt.Errorf("level is required") + } + + systems := cctx.StringSlice("system") + if len(systems) == 0 { + var err error + systems, err = minerApi.LogList(ctx) + if err != nil { + return err + } + } + + for _, system := range systems { + if err := minerApi.LogSetLevel(ctx, system, cctx.Args().First()); err != nil { + return xerrors.Errorf("setting log level on %s: %v", system, err) + } + } + + return nil + }, +} diff --git a/cmd/curio/main.go b/cmd/curio/main.go index bb506f190..9a092dad0 100644 --- a/cmd/curio/main.go +++ b/cmd/curio/main.go @@ -8,10 +8,14 @@ import ( "runtime/pprof" "syscall" + "github.com/docker/go-units" "github.com/fatih/color" logging "github.com/ipfs/go-log/v2" "github.com/mitchellh/go-homedir" "github.com/urfave/cli/v2" + "golang.org/x/xerrors" + + "github.com/filecoin-project/go-paramfetch" "github.com/filecoin-project/lotus/build" lcli "github.com/filecoin-project/lotus/cli" @@ -45,7 +49,6 @@ func main() { lotuslog.SetupLogLevels() local := []*cli.Command{ - //initCmd, cliCmd, runCmd, stopCmd, @@ -55,6 +58,7 @@ func main() { guidedsetup.GuidedsetupCmd, sealCmd, marketCmd, + fetchParamCmd, } jaeger := tracing.SetupJaegerTracing("curio") @@ -140,7 +144,7 @@ func main() { }, cliutil.FlagVeryVerbose, }, - Commands: append(local, lcli.CommonCommands...), + Commands: local, After: func(c *cli.Context) error { if r := recover(); r != nil { p, err := homedir.Expand(c.String(FlagMinerRepo)) @@ -160,3 +164,26 @@ func main() { app.Metadata["repoType"] = repo.Curio lcli.RunApp(app) } + +var fetchParamCmd = &cli.Command{ + Name: "fetch-params", + Usage: "Fetch proving parameters", + ArgsUsage: "[sectorSize]", + Action: func(cctx *cli.Context) error { + if cctx.NArg() != 1 { + return xerrors.Errorf("incorrect number of arguments") + } + sectorSizeInt, err := units.RAMInBytes(cctx.Args().First()) + if err != nil { + return xerrors.Errorf("error parsing sector size (specify as \"32GiB\", for instance): %w", err) + } + sectorSize := uint64(sectorSizeInt) + + err = paramfetch.GetParams(lcli.ReqContext(cctx), build.ParametersJSON(), build.SrsJSON(), sectorSize) + if err != nil { + return xerrors.Errorf("fetching proof parameters: %w", err) + } + + return nil + }, +} diff --git a/cmd/curio/rpc/rpc.go b/cmd/curio/rpc/rpc.go index 4f3f278d1..d210a93ac 100644 --- a/cmd/curio/rpc/rpc.go +++ b/cmd/curio/rpc/rpc.go @@ -181,6 +181,14 @@ func (p *CurioAPI) StorageAddLocal(ctx context.Context, path string) error { return nil } +func (p *CurioAPI) LogList(ctx context.Context) ([]string, error) { + return logging.GetSubsystems(), nil +} + +func (p *CurioAPI) LogSetLevel(ctx context.Context, subsystem, level string) error { + return logging.SetLogLevel(subsystem, level) +} + func ListenAndServe(ctx context.Context, dependencies *deps.Deps, shutdownChan chan struct{}) error { fh := &paths.FetchHandler{Local: dependencies.LocalStore, PfHandler: &paths.DefaultPartialFileHandler{}} remoteHandler := func(w http.ResponseWriter, r *http.Request) { diff --git a/cmd/curio/tasks/tasks.go b/cmd/curio/tasks/tasks.go index a35d3d2a5..35faba765 100644 --- a/cmd/curio/tasks/tasks.go +++ b/cmd/curio/tasks/tasks.go @@ -8,6 +8,8 @@ import ( "github.com/samber/lo" "golang.org/x/xerrors" + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/lotus/cmd/curio/deps" curio "github.com/filecoin-project/lotus/curiosrc" "github.com/filecoin-project/lotus/curiosrc/chainsched" @@ -137,8 +139,13 @@ func StartTasks(ctx context.Context, dependencies *deps.Deps) (*harmonytask.Task } } - log.Infow("This lotus_provider instance handles", - "miner_addresses", maddrs, + minerAddresses := make([]string, 0, len(maddrs)) + for k := range maddrs { + minerAddresses = append(minerAddresses, address.Address(k).String()) + } + + log.Infow("This Curio instance handles", + "miner_addresses", minerAddresses, "tasks", lo.Map(activeTasks, func(t harmonytask.TaskInterface, _ int) string { return t.TypeDetails().Name })) // harmony treats the first task as highest priority, so reverse the order diff --git a/documentation/en/api-v0-methods-curio.md b/documentation/en/api-v0-methods-curio.md index 4cca31b34..e94dca8b0 100644 --- a/documentation/en/api-v0-methods-curio.md +++ b/documentation/en/api-v0-methods-curio.md @@ -4,6 +4,9 @@ * [Version](#Version) * [Allocate](#Allocate) * [AllocatePieceToSector](#AllocatePieceToSector) +* [Log](#Log) + * [LogList](#LogList) + * [LogSetLevel](#LogSetLevel) * [Storage](#Storage) * [StorageAddLocal](#StorageAddLocal) * [StorageDetachLocal](#StorageDetachLocal) @@ -110,6 +113,38 @@ Response: } ``` +## Log + + +### LogList + + +Perms: read + +Inputs: `null` + +Response: +```json +[ + "string value" +] +``` + +### LogSetLevel + + +Perms: admin + +Inputs: +```json +[ + "string value", + "string value" +] +``` + +Response: `{}` + ## Storage diff --git a/documentation/en/cli-curio.md b/documentation/en/cli-curio.md index 16b374687..3203d0eaf 100644 --- a/documentation/en/cli-curio.md +++ b/documentation/en/cli-curio.md @@ -19,11 +19,7 @@ COMMANDS: guided-setup Run the guided setup for migrating from lotus-miner to Curio or Creating a new Curio miner seal Manage the sealing pipeline market - auth Manage RPC permissions - log Manage logging - wait-api Wait for lotus api to come online fetch-params Fetch proving parameters - version Print version help, h Shows a list of commands or help for one command GLOBAL OPTIONS: @@ -48,8 +44,10 @@ USAGE: curio cli command [command options] [arguments...] COMMANDS: - storage manage sector storage - help, h Shows a list of commands or help for one command + storage manage sector storage + log Manage logging + wait-api Wait for Curio api to come online + help, h Shows a list of commands or help for one command OPTIONS: --machine value machine host:port (curio run --listen address) @@ -60,6 +58,14 @@ OPTIONS: ``` ``` +### curio cli log +``` +``` + +### curio cli wait-api +``` +``` + ## curio run ``` NAME: @@ -378,138 +384,6 @@ OPTIONS: --help, -h show help ``` -## curio auth -``` -NAME: - curio auth - Manage RPC permissions - -USAGE: - curio auth command [command options] [arguments...] - -COMMANDS: - create-token Create token - api-info Get token with API info required to connect to this node - help, h Shows a list of commands or help for one command - -OPTIONS: - --help, -h show help -``` - -### curio auth create-token -``` -NAME: - curio auth create-token - Create token - -USAGE: - curio auth create-token [command options] [arguments...] - -OPTIONS: - --perm value permission to assign to the token, one of: read, write, sign, admin - --help, -h show help -``` - -### curio auth api-info -``` -NAME: - curio auth api-info - Get token with API info required to connect to this node - -USAGE: - curio auth api-info [command options] [arguments...] - -OPTIONS: - --perm value permission to assign to the token, one of: read, write, sign, admin - --help, -h show help -``` - -## curio log -``` -NAME: - curio log - Manage logging - -USAGE: - curio log command [command options] [arguments...] - -COMMANDS: - list List log systems - set-level Set log level - alerts Get alert states - help, h Shows a list of commands or help for one command - -OPTIONS: - --help, -h show help -``` - -### curio log list -``` -NAME: - curio log list - List log systems - -USAGE: - curio log list [command options] [arguments...] - -OPTIONS: - --help, -h show help -``` - -### curio log set-level -``` -NAME: - curio log set-level - Set log level - -USAGE: - curio log set-level [command options] [level] - -DESCRIPTION: - Set the log level for logging systems: - - The system flag can be specified multiple times. - - eg) log set-level --system chain --system chainxchg debug - - Available Levels: - debug - info - warn - error - - Environment Variables: - GOLOG_LOG_LEVEL - Default log level for all log systems - GOLOG_LOG_FMT - Change output log format (json, nocolor) - GOLOG_FILE - Write logs to file - GOLOG_OUTPUT - Specify whether to output to file, stderr, stdout or a combination, i.e. file+stderr - - -OPTIONS: - --system value [ --system value ] limit to log system - --help, -h show help -``` - -### curio log alerts -``` -NAME: - curio log alerts - Get alert states - -USAGE: - curio log alerts [command options] [arguments...] - -OPTIONS: - --all get all (active and inactive) alerts (default: false) - --help, -h show help -``` - -## curio wait-api -``` -NAME: - curio wait-api - Wait for lotus api to come online - -USAGE: - curio wait-api [command options] [arguments...] - -OPTIONS: - --timeout value duration to wait till fail (default: 30s) - --help, -h show help -``` - ## curio fetch-params ``` NAME: @@ -521,15 +395,3 @@ USAGE: OPTIONS: --help, -h show help ``` - -## curio version -``` -NAME: - curio version - Print version - -USAGE: - curio version [command options] [arguments...] - -OPTIONS: - --help, -h show help -``` From da92001c71a7a1a61a987d916271b4da2063c50d Mon Sep 17 00:00:00 2001 From: "Andrew Jackson (Ajax)" Date: Thu, 1 Feb 2024 15:03:09 -0600 Subject: [PATCH 355/394] feat/boredom --- itests/harmonytask_test.go | 37 +++++++++++++ lib/harmony/harmonytask/harmonytask.go | 57 ++++++++++++++++---- lib/harmony/harmonytask/task_type_handler.go | 12 +++-- 3 files changed, 92 insertions(+), 14 deletions(-) diff --git a/itests/harmonytask_test.go b/itests/harmonytask_test.go index beef04c8d..d4aa7550c 100644 --- a/itests/harmonytask_test.go +++ b/itests/harmonytask_test.go @@ -264,3 +264,40 @@ func TestTaskRetry(t *testing.T) { {2, false, "error: intentional 'error'"}}, res) }) } + +func TestBoredom(t *testing.T) { + //t.Parallel() + withDbSetup(t, func(m *kit.TestMiner) { + cdb := m.BaseAPI.(*impl.StorageMinerAPI).HarmonyDB + harmonytask.POLL_DURATION = time.Millisecond * 100 + var taskID harmonytask.TaskID + var ran bool + boredParty := &passthru{ + dtl: harmonytask.TaskTypeDetails{ + Name: "boredTest", + Max: -1, + Cost: resources.Resources{}, + IAmBored: func(add harmonytask.AddTaskFunc) error { + add(func(tID harmonytask.TaskID, tx *harmonydb.Tx) (bool, error) { + taskID = tID + return true, nil + }) + return nil + }, + }, + canAccept: func(list []harmonytask.TaskID, e *harmonytask.TaskEngine) (*harmonytask.TaskID, error) { + require.Equal(t, harmonytask.WorkSourceIAmBored, e.WorkOrigin) + return &list[0], nil + }, + do: func(tID harmonytask.TaskID, stillOwned func() bool) (done bool, err error) { + require.Equal(t, taskID, tID) + ran = true + return true, nil + }, + } + ht, err := harmonytask.New(cdb, []harmonytask.TaskInterface{boredParty}, "test:1") + require.NoError(t, err) + require.Eventually(t, func() bool { return ran }, time.Second, time.Millisecond*100) + ht.GracefullyTerminate(time.Hour) + }) +} diff --git a/lib/harmony/harmonytask/harmonytask.go b/lib/harmony/harmonytask/harmonytask.go index 2d537868d..4f5daa963 100644 --- a/lib/harmony/harmonytask/harmonytask.go +++ b/lib/harmony/harmonytask/harmonytask.go @@ -39,6 +39,12 @@ type TaskTypeDetails struct { // NOTE: if refatoring tasks, see if your task is // necessary. Ex: Is the sector state correct for your stage to run? Follows map[string]func(TaskID, AddTaskFunc) (bool, error) + + // IAmBored is called (when populated) when there's capacity but no work. + // Tasks added will be proposed to CanAccept() on this machine. + // CanAccept() can read taskEngine's WorkOrigin string to learn about a task. + // Ex: make new CC sectors, clean-up, or retrying pipelines that failed in later states. + IAmBored func(AddTaskFunc) error } // TaskInterface must be implemented in order to have a task used by harmonytask. @@ -97,17 +103,21 @@ type TaskInterface interface { type AddTaskFunc func(extraInfo func(TaskID, *harmonydb.Tx) (shouldCommit bool, seriousError error)) type TaskEngine struct { - ctx context.Context - handlers []*taskTypeHandler - db *harmonydb.DB - reg *resources.Reg - grace context.CancelFunc - taskMap map[string]*taskTypeHandler - ownerID int - follows map[string][]followStruct + // Static After New() + ctx context.Context + handlers []*taskTypeHandler + db *harmonydb.DB + reg *resources.Reg + grace context.CancelFunc + taskMap map[string]*taskTypeHandler + ownerID int + follows map[string][]followStruct + hostAndPort string + + // synchronous to the single-threaded poller lastFollowTime time.Time lastCleanup atomic.Value - hostAndPort string + WorkOrigin string } type followStruct struct { f func(TaskID, AddTaskFunc) (bool, error) @@ -177,7 +187,7 @@ func New( continue // not really fatal, but not great } } - if !h.considerWork(workSourceRecover, []TaskID{TaskID(w.ID)}) { + if !h.considerWork(WorkSourceRecover, []TaskID{TaskID(w.ID)}) { log.Errorw("Strange: Unable to accept previously owned task", "id", w.ID, "type", w.Name) } } @@ -327,13 +337,38 @@ func (e *TaskEngine) pollerTryAllWork() bool { continue } if len(unownedTasks) > 0 { - accepted := v.considerWork(workSourcePoller, unownedTasks) + accepted := v.considerWork(WorkSourcePoller, unownedTasks) if accepted { return true // accept new work slowly and in priority order } log.Warn("Work not accepted for " + strconv.Itoa(len(unownedTasks)) + " " + v.Name + " task(s)") } } + // if no work was accepted, are we bored? Then find work in priority order. + for _, v := range e.handlers { + if v.AssertMachineHasCapacity() != nil { + continue + } + if v.TaskTypeDetails.IAmBored != nil { + var added []TaskID + err := v.TaskTypeDetails.IAmBored(func(extraInfo func(TaskID, *harmonydb.Tx) (shouldCommit bool, seriousError error)) { + v.AddTask(func(tID TaskID, tx *harmonydb.Tx) (shouldCommit bool, seriousError error) { + b, err := extraInfo(tID, tx) + if err == nil { + added = append(added, tID) + } + return b, err + }) + }) + if err != nil { + log.Error("IAmBored failed: ", err) + continue + } + if added != nil { // tiny chance a fail could make these bogus, but considerWork should then fail. + v.considerWork(WorkSourceIAmBored, added) + } + } + } return false } diff --git a/lib/harmony/harmonytask/task_type_handler.go b/lib/harmony/harmonytask/task_type_handler.go index 7aecd380f..a8c6e58b8 100644 --- a/lib/harmony/harmonytask/task_type_handler.go +++ b/lib/harmony/harmonytask/task_type_handler.go @@ -53,8 +53,9 @@ retryAddTask: } const ( - workSourcePoller = "poller" - workSourceRecover = "recovered" + WorkSourcePoller = "poller" + WorkSourceRecover = "recovered" + WorkSourceIAmBored = "bored" ) // considerWork is called to attempt to start work on a task-id of this task type. @@ -84,9 +85,14 @@ top: return false } + h.TaskEngine.WorkOrigin = from + // 3. What does the impl say? canAcceptAgain: tID, err := h.CanAccept(ids, h.TaskEngine) + + h.TaskEngine.WorkOrigin = "" + if err != nil { log.Error(err) return false @@ -123,7 +129,7 @@ canAcceptAgain: } // if recovering we don't need to try to claim anything because those tasks are already claimed by us - if from != workSourceRecover { + if from != WorkSourceRecover { // 4. Can we claim the work for our hostname? ct, err := h.TaskEngine.db.Exec(h.TaskEngine.ctx, "UPDATE harmony_task SET owner_id=$1 WHERE id=$2 AND owner_id IS NULL", h.TaskEngine.ownerID, *tID) if err != nil { From ccf81ecee57b35b0b82f0f910f4358d7a45e2ee7 Mon Sep 17 00:00:00 2001 From: "Andrew Jackson (Ajax)" Date: Mon, 12 Feb 2024 19:11:57 -0600 Subject: [PATCH 356/394] lint --- lib/harmony/harmonytask/harmonytask.go | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/harmony/harmonytask/harmonytask.go b/lib/harmony/harmonytask/harmonytask.go index 4f5daa963..0c66891d0 100644 --- a/lib/harmony/harmonytask/harmonytask.go +++ b/lib/harmony/harmonytask/harmonytask.go @@ -346,6 +346,7 @@ func (e *TaskEngine) pollerTryAllWork() bool { } // if no work was accepted, are we bored? Then find work in priority order. for _, v := range e.handlers { + v := v if v.AssertMachineHasCapacity() != nil { continue } From 2dd7db20f5a9da45b0c71e1052b39239442a5a5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 16 Apr 2024 12:03:41 +0200 Subject: [PATCH 357/394] curio: Fix tests --- itests/harmonytask_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/itests/harmonytask_test.go b/itests/harmonytask_test.go index d4aa7550c..95a3db875 100644 --- a/itests/harmonytask_test.go +++ b/itests/harmonytask_test.go @@ -298,6 +298,6 @@ func TestBoredom(t *testing.T) { ht, err := harmonytask.New(cdb, []harmonytask.TaskInterface{boredParty}, "test:1") require.NoError(t, err) require.Eventually(t, func() bool { return ran }, time.Second, time.Millisecond*100) - ht.GracefullyTerminate(time.Hour) + ht.GracefullyTerminate() }) } From fd7f1a95e2cf508ce182dda87c7173013f11e482 Mon Sep 17 00:00:00 2001 From: "Andrew Jackson (Ajax)" Date: Tue, 16 Apr 2024 09:30:27 -0500 Subject: [PATCH 358/394] feat: curio: web based config edit (#11822) * cfg edit 1 * jsonschema deps * feat: lp mig - first few steps * lp mig: default tasks * code comments * docs * lp-mig-progress * shared * comments and todos * fix: curio: rename lotus-provider to curio (#11645) * rename provider to curio * install gotext * fix lint errors, mod tidy * fix typo * fix API_INFO and add gotext to circleCI * add back gotext * add gotext after remerge * lp: channels doc * finish easy-migration TODOs * out generate * merging and more renames * avoid make-all * minor doc stuff * cu: make gen * make gen fix * make gen * tryfix * go mod tidy * minor ez migration fixes * ez setup - ui cleanups * better error message * guided setup colors * better path to saveconfigtolayer * loadconfigwithupgrades fix * readMiner oops * guided - homedir * err if miner is running * prompt error should exit * process already running, miner_id sectors in migration * dont prompt for language a second time * check miner stopped * unlock repo * render and sql oops * curio easyMig - some fixes * easyMigration runs successfully * lint * part 2 of last * message * merge addtl * fixing guided setup for myself * warn-on-no-post * EditorLoads * cleanups and styles * create info * fix tests * make gen * change layout, add help button * Duration custom json * mjs naming --------- Co-authored-by: LexLuthr <88259624+LexLuthr@users.noreply.github.com> Co-authored-by: LexLuthr Co-authored-by: LexLuthr --- .gitignore | 1 + chain/types/fil.go | 9 + cli/mpool.go | 2 +- cmd/curio/cli.go | 3 + cmd/curio/config_test.go | 22 + cmd/curio/deps/deps.go | 4 + cmd/curio/guidedsetup/guidedsetup.go | 6 +- cmd/curio/guidedsetup/shared.go | 44 +- cmd/curio/internal/translations/catalog.go | 445 +++++++++--------- .../translations/locales/en/out.gotext.json | 42 +- .../locales/ko/messages.gotext.json | 24 + .../locales/zh/messages.gotext.json | 24 + cmd/curio/rpc/rpc.go | 7 +- cmd/curio/run.go | 6 +- cmd/curio/stop.go | 1 + cmd/curio/tasks/tasks.go | 59 +++ curiosrc/web/api/apihelper/apihelper.go | 19 + curiosrc/web/api/config/config.go | 180 +++++++ curiosrc/web/api/routes.go | 2 + curiosrc/web/hapi/watch_actor.go | 2 + curiosrc/web/hapi/web/root.gohtml | 3 - ...connectivity.js => chain-connectivity.mjs} | 0 curiosrc/web/static/config/edit.html | 150 ++++++ curiosrc/web/static/config/index.html | 89 ++++ curiosrc/web/static/index.html | 4 +- curiosrc/web/static/ux/curio-ux.mjs | 50 ++ documentation/en/cli-curio.md | 113 ++++- go.mod | 10 +- go.sum | 15 +- itests/mpool_msg_uuid_test.go | 4 +- itests/mpool_push_with_uuid_test.go | 4 +- .../harmonydb/sql/20240404-machine_detail.sql | 12 + node/config/def.go | 13 +- scripts/generate-lotus-cli.py | 2 +- 34 files changed, 1072 insertions(+), 299 deletions(-) create mode 100644 curiosrc/web/api/apihelper/apihelper.go create mode 100644 curiosrc/web/api/config/config.go rename curiosrc/web/static/{chain-connectivity.js => chain-connectivity.mjs} (100%) create mode 100644 curiosrc/web/static/config/edit.html create mode 100644 curiosrc/web/static/config/index.html create mode 100644 curiosrc/web/static/ux/curio-ux.mjs create mode 100644 lib/harmony/harmonydb/sql/20240404-machine_detail.sql diff --git a/.gitignore b/.gitignore index d620e557b..a40ab0bd5 100644 --- a/.gitignore +++ b/.gitignore @@ -39,6 +39,7 @@ build/paramfetch.sh *.snap devgen.car localnet.json +/*.ndjson *-fuzz.zip /chain/types/work_msg/ diff --git a/chain/types/fil.go b/chain/types/fil.go index d20b3c021..960a42f28 100644 --- a/chain/types/fil.go +++ b/chain/types/fil.go @@ -6,6 +6,8 @@ import ( "math/big" "strings" + "github.com/invopop/jsonschema" + "github.com/filecoin-project/lotus/build" ) @@ -138,5 +140,12 @@ func MustParseFIL(s string) FIL { return n } +func (f FIL) JSONSchema() *jsonschema.Schema { + return &jsonschema.Schema{ + Type: "string", + Pattern: `^((\d+(\.\d+)?|0x[0-9a-fA-F]+))( ([aA]([tT][tT][oO])?)?[fF][iI][lL])?$`, + } +} + var _ encoding.TextMarshaler = (*FIL)(nil) var _ encoding.TextUnmarshaler = (*FIL)(nil) diff --git a/cli/mpool.go b/cli/mpool.go index c83fb4b61..f38a900fb 100644 --- a/cli/mpool.go +++ b/cli/mpool.go @@ -491,7 +491,7 @@ var MpoolReplaceCmd = &cli.Command{ msg.GasFeeCap = big.Max(retm.GasFeeCap, msg.GasPremium) mff := func() (abi.TokenAmount, error) { - return abi.TokenAmount(config.DefaultDefaultMaxFee), nil + return abi.TokenAmount(config.DefaultDefaultMaxFee()), nil } messagepool.CapGasFee(mff, &msg, mss) diff --git a/cmd/curio/cli.go b/cmd/curio/cli.go index 3eec4b617..6c9cb7ec6 100644 --- a/cmd/curio/cli.go +++ b/cmd/curio/cli.go @@ -40,6 +40,9 @@ var cliCmd = &cli.Command{ // set already return nil } + if os.Getenv("LOTUS_DOCS_GENERATION") == "1" { + return nil + } db, err := deps.MakeDB(cctx) if err != nil { diff --git a/cmd/curio/config_test.go b/cmd/curio/config_test.go index e476f23d0..f5037abf4 100644 --- a/cmd/curio/config_test.go +++ b/cmd/curio/config_test.go @@ -1,8 +1,11 @@ package main import ( + "reflect" "testing" + "time" + "github.com/invopop/jsonschema" "github.com/samber/lo" "github.com/stretchr/testify/require" @@ -414,3 +417,22 @@ func TestConfig(t *testing.T) { require.NoError(t, err) } + +func TestCustomConfigDurationJson(t *testing.T) { + ref := new(jsonschema.Reflector) + ref.Mapper = func(i reflect.Type) *jsonschema.Schema { + if i == reflect.TypeOf(config.Duration(time.Second)) { + return &jsonschema.Schema{ + Type: "string", + Format: "duration", + } + } + return nil + } + + sch := ref.Reflect(config.CurioConfig{}) + definitions := sch.Definitions["CurioProvingConfig"] + prop, ok := definitions.Properties.Get("SingleCheckTimeout") + require.True(t, ok) + require.Equal(t, prop.Type, "string") +} diff --git a/cmd/curio/deps/deps.go b/cmd/curio/deps/deps.go index 79881ebb8..2050a1caf 100644 --- a/cmd/curio/deps/deps.go +++ b/cmd/curio/deps/deps.go @@ -165,6 +165,7 @@ func GetDeps(ctx context.Context, cctx *cli.Context) (*Deps, error) { } type Deps struct { + Layers []string Cfg *config.CurioConfig // values DB *harmonydb.DB // has itest capability Full api.FullNode @@ -212,6 +213,9 @@ func (deps *Deps) PopulateRemainingDeps(ctx context.Context, cctx *cli.Context, return err } } + if deps.Layers == nil { + deps.Layers = append([]string{"base"}, cctx.StringSlice("layers")...) // Always stack on top of "base" layer + } if deps.Cfg == nil { // The config feeds into task runners & their helpers diff --git a/cmd/curio/guidedsetup/guidedsetup.go b/cmd/curio/guidedsetup/guidedsetup.go index 1af49d868..1bdb8e784 100644 --- a/cmd/curio/guidedsetup/guidedsetup.go +++ b/cmd/curio/guidedsetup/guidedsetup.go @@ -244,7 +244,7 @@ type MigrationData struct { func complete(d *MigrationData) { stepCompleted(d, d.T("Lotus-Miner to Curio Migration.")) - d.say(plain, "Try the web interface with %s for further guided improvements.", "--layers=gui") + d.say(plain, "Try the web interface with %s for further guided improvements.", code.Render("curio run --layers=gui")) d.say(plain, "You can now migrate your market node (%s), if applicable.", "Boost") } @@ -279,7 +279,7 @@ func configToDB(d *MigrationData) { chainApiInfo := fmt.Sprintf("%s:%s", string(token), ainfo.Addr) - d.MinerID, err = SaveConfigToLayer(d.MinerConfigPath, "", false, chainApiInfo) + d.MinerID, err = SaveConfigToLayer(d.MinerConfigPath, chainApiInfo) if err != nil { d.say(notice, "Error saving config to layer: %s. Aborting Migration", err.Error()) os.Exit(1) @@ -423,7 +423,7 @@ func verifySectors(d *MigrationData) { time.Sleep(5 * time.Second) } d.say(plain, "The sectors are in the database. The database is ready for %s.", "Curio") - d.say(notice, "Now shut down lotus-miner and move the systems to %s.", "Curio") + d.say(notice, "Now shut down lotus-miner and lotus-worker and use run %s instead.", code.Render("curio run")) _, err = (&promptui.Prompt{Label: d.T("Press return to continue")}).Run() if err != nil { diff --git a/cmd/curio/guidedsetup/shared.go b/cmd/curio/guidedsetup/shared.go index e4b840bf2..6e7d81c03 100644 --- a/cmd/curio/guidedsetup/shared.go +++ b/cmd/curio/guidedsetup/shared.go @@ -4,7 +4,6 @@ import ( "bytes" "context" "encoding/base64" - "errors" "fmt" "os" "path" @@ -30,7 +29,7 @@ const ( const FlagMinerRepoDeprecation = "storagerepo" -func SaveConfigToLayer(minerRepoPath, layerName string, overwrite bool, chainApiInfo string) (minerAddress address.Address, err error) { +func SaveConfigToLayer(minerRepoPath, chainApiInfo string) (minerAddress address.Address, err error) { _, say := SetupLanguage() ctx := context.Background() @@ -165,6 +164,12 @@ func SaveConfigToLayer(minerRepoPath, layerName string, overwrite bool, chainApi baseCfg.Addresses = lo.Filter(baseCfg.Addresses, func(a config.CurioAddresses, _ int) bool { return len(a.MinerAddresses) > 0 }) + if baseCfg.Apis.ChainApiInfo == nil { + baseCfg.Apis.ChainApiInfo = append(baseCfg.Apis.ChainApiInfo, chainApiInfo) + } + if baseCfg.Apis.StorageRPCSecret == "" { + baseCfg.Apis.StorageRPCSecret = curioCfg.Apis.StorageRPCSecret + } cb, err := config.ConfigUpdate(baseCfg, config.DefaultCurioConfig(), config.Commented(true), config.DefaultKeepUncommented(), config.NoEnv()) if err != nil { @@ -174,44 +179,33 @@ func SaveConfigToLayer(minerRepoPath, layerName string, overwrite bool, chainApi if err != nil { return minerAddress, xerrors.Errorf("cannot update base config: %w", err) } - say(plain, "Configuration 'base' was updated to include this miner's address and its wallet setup.") + say(plain, "Configuration 'base' was updated to include this miner's address (%s) and its wallet setup.", minerAddress) } say(plain, "Compare the configurations %s to %s. Changes between the miner IDs other than wallet addreses should be a new, minimal layer for runners that need it.", "base", "mig-"+curioCfg.Addresses[0].MinerAddresses[0]) skipWritingToBase: - } else if layerName == "" { - cfg, err := deps.GetDefaultConfig(true) - if err != nil { - return minerAddress, xerrors.Errorf("Cannot get default config: %w", err) - } + } else { _, err = db.Exec(ctx, `INSERT INTO harmony_config (title, config) VALUES ('base', $1) - ON CONFLICT(title) DO UPDATE SET config=EXCLUDED.config`, cfg) + ON CONFLICT(title) DO UPDATE SET config=EXCLUDED.config`, configTOML) if err != nil { return minerAddress, xerrors.Errorf("Cannot insert base config: %w", err) } - say(notice, "Configuration 'base' was created to include this miner's address and its wallet setup.") + say(notice, "Configuration 'base' was created to resemble this lotus-miner's config.toml .") } - if layerName == "" { // only make mig if base exists and we are different. // compare to base. - layerName = fmt.Sprintf("mig-%s", curioCfg.Addresses[0].MinerAddresses[0]) - overwrite = true - } else { - if lo.Contains(titles, layerName) && !overwrite { - return minerAddress, errors.New("the overwrite flag is needed to replace existing layer: " + layerName) - } - } - if overwrite { - _, err := db.Exec(ctx, "DELETE FROM harmony_config WHERE title=$1", layerName) + { // make a layer representing the migration + layerName := fmt.Sprintf("mig-%s", curioCfg.Addresses[0].MinerAddresses[0]) + _, err = db.Exec(ctx, "DELETE FROM harmony_config WHERE title=$1", layerName) if err != nil { return minerAddress, xerrors.Errorf("Cannot delete existing layer: %w", err) } - } - _, err = db.Exec(ctx, "INSERT INTO harmony_config (title, config) VALUES ($1, $2)", layerName, configTOML.String()) - if err != nil { - return minerAddress, xerrors.Errorf("Cannot insert layer after layer created message: %w", err) + _, err = db.Exec(ctx, "INSERT INTO harmony_config (title, config) VALUES ($1, $2)", layerName, configTOML.String()) + if err != nil { + return minerAddress, xerrors.Errorf("Cannot insert layer after layer created message: %w", err) + } + say(plain, "Layer %s created. ", layerName) } - say(plain, "Layer %s created. ", layerName) dbSettings := getDBSettings(*smCfg) say(plain, "To work with the config: ") diff --git a/cmd/curio/internal/translations/catalog.go b/cmd/curio/internal/translations/catalog.go index 44c97dfac..6e0ad3087 100644 --- a/cmd/curio/internal/translations/catalog.go +++ b/cmd/curio/internal/translations/catalog.go @@ -48,9 +48,9 @@ var messageKeyToIndex = map[string]int{ "Compare the configurations %s to %s. Changes between the miner IDs other than wallet addreses should be a new, minimal layer for runners that need it.": 116, "Confidence epochs": 86, "Confidence epochs: %d": 76, - "Configuration 'base' was created to include this miner's address and its wallet setup.": 117, - "Configuration 'base' was updated to include this miner's address": 99, - "Configuration 'base' was updated to include this miner's address and its wallet setup.": 115, + "Configuration 'base' was created to resemble this lotus-miner's config.toml .": 117, + "Configuration 'base' was updated to include this miner's address": 99, + "Configuration 'base' was updated to include this miner's address (%s) and its wallet setup.": 115, "Connected to Yugabyte": 59, "Connected to Yugabyte. Schema is current.": 47, "Continue to connect and update schema.": 109, @@ -125,7 +125,7 @@ var messageKeyToIndex = map[string]int{ "No path provided, abandoning migration ": 64, "No value provided": 84, "Nothing.": 24, - "Now shut down lotus-miner and move the systems to %s.": 43, + "Now shut down lotus-miner and lotus-worker and use run %s instead.": 43, "One database can serve multiple miner IDs: Run a migration for each lotus-miner.": 37, "Other": 62, "Owner Address: %s": 72, @@ -177,31 +177,31 @@ var enIndex = []uint32{ // 122 elements 0x0000063c, 0x00000650, 0x000006bb, 0x000006fb, 0x00000724, 0x0000079b, 0x000007ec, 0x00000838, 0x0000086a, 0x00000898, 0x000008b7, 0x000008f9, - 0x00000932, 0x0000094b, 0x0000095f, 0x0000098f, - 0x000009b9, 0x000009e3, 0x00000a05, 0x00000a7c, - 0x00000a98, 0x00000ac5, 0x00000ae7, 0x00000b08, - 0x00000b29, 0x00000b4a, 0x00000b6b, 0x00000b85, - 0x00000b9b, 0x00000be8, 0x00000c22, 0x00000c28, + 0x0000093f, 0x00000958, 0x0000096c, 0x0000099c, + 0x000009c6, 0x000009f0, 0x00000a12, 0x00000a89, + 0x00000aa5, 0x00000ad2, 0x00000af4, 0x00000b15, + 0x00000b36, 0x00000b57, 0x00000b78, 0x00000b92, + 0x00000ba8, 0x00000bf5, 0x00000c2f, 0x00000c35, // Entry 40 - 5F - 0x00000c64, 0x00000c90, 0x00000cd9, 0x00000d19, - 0x00000d6a, 0x00000d7c, 0x00000d96, 0x00000db6, - 0x00000ddb, 0x00000df0, 0x00000e06, 0x00000e1c, - 0x00000e2f, 0x00000e48, 0x00000e87, 0x00000eb1, - 0x00000ec9, 0x00000edd, 0x00000f00, 0x00000f14, - 0x00000f2a, 0x00000f3c, 0x00000f5f, 0x00000f71, - 0x00000f93, 0x00000fbb, 0x00000fdc, 0x00000ff7, - 0x00001020, 0x00001042, 0x00001074, 0x0000110b, + 0x00000c71, 0x00000c9d, 0x00000ce6, 0x00000d26, + 0x00000d77, 0x00000d89, 0x00000da3, 0x00000dc3, + 0x00000de8, 0x00000dfd, 0x00000e13, 0x00000e29, + 0x00000e3c, 0x00000e55, 0x00000e94, 0x00000ebe, + 0x00000ed6, 0x00000eea, 0x00000f0d, 0x00000f21, + 0x00000f37, 0x00000f49, 0x00000f6c, 0x00000f7e, + 0x00000fa0, 0x00000fc8, 0x00000fe9, 0x00001004, + 0x0000102d, 0x0000104f, 0x00001081, 0x00001118, // Entry 60 - 7F - 0x00001136, 0x0000116e, 0x00001197, 0x000011cf, - 0x00001210, 0x00001240, 0x00001263, 0x0000128b, - 0x000012ed, 0x000012f9, 0x00001305, 0x00001315, - 0x00001325, 0x00001335, 0x0000135c, 0x0000139d, - 0x000013c1, 0x000013d2, 0x000013f4, 0x00001421, - 0x00001478, 0x00001515, 0x0000156c, 0x00001586, - 0x000015a4, 0x00001604, + 0x00001143, 0x0000117b, 0x000011a4, 0x000011dc, + 0x0000121d, 0x0000124d, 0x00001270, 0x00001298, + 0x000012fa, 0x00001306, 0x00001312, 0x00001322, + 0x00001332, 0x00001342, 0x00001369, 0x000013aa, + 0x000013ce, 0x000013df, 0x00001401, 0x0000142e, + 0x0000148d, 0x0000152a, 0x00001578, 0x00001592, + 0x000015b0, 0x00001610, } // Size: 512 bytes -const enData string = "" + // Size: 5636 bytes +const enData string = "" + // Size: 5648 bytes "\x04\x00\x01 0\x02Use the arrow keys to navigate: ↓ ↑ → ←\x02This intera" + "ctive tool creates a new miner actor and creates the basic configuration" + " layer for it.\x02This process is partially idempotent. Once a new miner" + @@ -236,207 +236,208 @@ const enData string = "" + // Size: 5636 bytes "re in %[2]s.\x02Waiting for %[1]s to write sectors into Yugabyte.\x02Err" + "or interpreting miner ID: %[1]s: ID: %[2]s\x02Error verifying sectors: %" + "[1]s\x02The sectors are in the database. The database is ready for %[1]s" + - ".\x02Now shut down lotus-miner and move the systems to %[1]s.\x02Press r" + - "eturn to continue\x02Aborting migration.\x02Sectors verified. %[1]d sect" + - "or locations found.\x02Connected to Yugabyte. Schema is current.\x02Enab" + - "ling Sector Indexing in the database.\x02Error encoding config.toml: %[1" + - "]s\x02Press return to update %[1]s with Yugabyte info. A Backup file wil" + - "l be written to that folder before changes are made.\x02Error expanding " + - "path: %[1]s\x02Error reading filemode of config.toml: %[1]s\x02Error cre" + - "ating backup file: %[1]s\x02Error reading config.toml: %[1]s\x02Error wr" + - "iting backup file: %[1]s\x02Error closing backup file: %[1]s\x02Error wr" + - "iting config.toml: %[1]s\x04\x00\x01 \x15\x02Restart Lotus Miner.\x02Con" + - "nected to Yugabyte\x02To start, ensure your sealing pipeline is drained " + - "and shut-down lotus-miner.\x02Select the location of your lotus-miner co" + - "nfig directory?\x02Other\x02Enter the path to the configuration director" + - "y used by %[1]s\x04\x00\x01 '\x02No path provided, abandoning migration" + - "\x02Cannot read the config.toml file in the provided directory, Error: %" + - "[1]s\x02Could not create repo from directory: %[1]s. Aborting migration" + - "\x02Could not lock miner repo. Your miner must be stopped: %[1]s\x0a Abo" + - "rting migration\x02Read Miner Config\x04\x00\x01\x0a\x15\x02Step Complet" + - "e: %[1]s\x02Initializing a new miner actor.\x02Enter the info to create " + - "a new miner\x02Owner Address: %[1]s\x02Worker Address: %[1]s\x02Sender A" + - "ddress: %[1]s\x02Sector Size: %[1]d\x02Confidence epochs: %[1]d\x02Conti" + - "nue to verify the addresses and create a new miner actor.\x04\x00\x01 %" + - "\x02Miner creation error occurred: %[1]s\x02Enter the owner address\x02N" + - "o address provided\x02Failed to parse the address: %[1]s\x02Enter %[1]s " + - "address\x02Enter the sector size\x02No value provided\x02Failed to parse" + - " sector size: %[1]s\x02Confidence epochs\x02Failed to parse confidence: " + - "%[1]s\x02Failed to create the miner actor: %[1]s\x02Miner %[1]s created " + - "successfully\x02Cannot reach the DB: %[1]s\x02Error connecting to full n" + - "ode API: %[1]s\x02Pre-initialization steps complete\x02Failed to generat" + - "e random bytes for secret: %[1]s\x02Please do not run guided-setup again" + - " as miner creation is not idempotent. You need to run 'curio config new-" + - "cluster %[1]s' to finish the configuration\x02Failed to get API info for" + - " FullNode: %[1]w\x02Failed to verify the auth token from daemon node: %[" + - "1]s\x02Failed to generate default config: %[1]s\x02Failed to insert 'bas" + - "e' config layer in database: %[1]s\x02Configuration 'base' was updated t" + - "o include this miner's address\x02Failed to load base config from databa" + - "se: %[1]s\x02Failed to parse base config: %[1]s\x02Failed to regenerate " + - "base config: %[1]s\x02Enter the info to connect to your Yugabyte databas" + - "e installation (https://download.yugabyte.com/)\x02Host: %[1]s\x02Port: " + - "%[1]s\x02Username: %[1]s\x02Password: %[1]s\x02Database: %[1]s\x02Contin" + - "ue to connect and update schema.\x04\x00\x01 <\x02Database config error " + - "occurred, abandoning migration: %[1]s\x02Enter the Yugabyte database hos" + - "t(s)\x02No host provided\x02Enter the Yugabyte database %[1]s\x02Error c" + - "onnecting to Yugabyte database: %[1]s\x02Configuration 'base' was update" + - "d to include this miner's address and its wallet setup.\x02Compare the c" + - "onfigurations %[1]s to %[2]s. Changes between the miner IDs other than w" + - "allet addreses should be a new, minimal layer for runners that need it." + - "\x02Configuration 'base' was created to include this miner's address and" + - " its wallet setup.\x04\x00\x01 \x15\x02Layer %[1]s created.\x04\x00\x01 " + - "\x19\x02To work with the config:\x02To run Curio: With machine or cgroup" + - " isolation, use the command (with example layer selection):" + ".\x02Now shut down lotus-miner and lotus-worker and use run %[1]s instea" + + "d.\x02Press return to continue\x02Aborting migration.\x02Sectors verifie" + + "d. %[1]d sector locations found.\x02Connected to Yugabyte. Schema is cur" + + "rent.\x02Enabling Sector Indexing in the database.\x02Error encoding con" + + "fig.toml: %[1]s\x02Press return to update %[1]s with Yugabyte info. A Ba" + + "ckup file will be written to that folder before changes are made.\x02Err" + + "or expanding path: %[1]s\x02Error reading filemode of config.toml: %[1]s" + + "\x02Error creating backup file: %[1]s\x02Error reading config.toml: %[1]" + + "s\x02Error writing backup file: %[1]s\x02Error closing backup file: %[1]" + + "s\x02Error writing config.toml: %[1]s\x04\x00\x01 \x15\x02Restart Lotus " + + "Miner.\x02Connected to Yugabyte\x02To start, ensure your sealing pipelin" + + "e is drained and shut-down lotus-miner.\x02Select the location of your l" + + "otus-miner config directory?\x02Other\x02Enter the path to the configura" + + "tion directory used by %[1]s\x04\x00\x01 '\x02No path provided, abandoni" + + "ng migration\x02Cannot read the config.toml file in the provided directo" + + "ry, Error: %[1]s\x02Could not create repo from directory: %[1]s. Abortin" + + "g migration\x02Could not lock miner repo. Your miner must be stopped: %[" + + "1]s\x0a Aborting migration\x02Read Miner Config\x04\x00\x01\x0a\x15\x02S" + + "tep Complete: %[1]s\x02Initializing a new miner actor.\x02Enter the info" + + " to create a new miner\x02Owner Address: %[1]s\x02Worker Address: %[1]s" + + "\x02Sender Address: %[1]s\x02Sector Size: %[1]d\x02Confidence epochs: %[" + + "1]d\x02Continue to verify the addresses and create a new miner actor." + + "\x04\x00\x01 %\x02Miner creation error occurred: %[1]s\x02Enter the owne" + + "r address\x02No address provided\x02Failed to parse the address: %[1]s" + + "\x02Enter %[1]s address\x02Enter the sector size\x02No value provided" + + "\x02Failed to parse sector size: %[1]s\x02Confidence epochs\x02Failed to" + + " parse confidence: %[1]s\x02Failed to create the miner actor: %[1]s\x02M" + + "iner %[1]s created successfully\x02Cannot reach the DB: %[1]s\x02Error c" + + "onnecting to full node API: %[1]s\x02Pre-initialization steps complete" + + "\x02Failed to generate random bytes for secret: %[1]s\x02Please do not r" + + "un guided-setup again as miner creation is not idempotent. You need to r" + + "un 'curio config new-cluster %[1]s' to finish the configuration\x02Faile" + + "d to get API info for FullNode: %[1]w\x02Failed to verify the auth token" + + " from daemon node: %[1]s\x02Failed to generate default config: %[1]s\x02" + + "Failed to insert 'base' config layer in database: %[1]s\x02Configuration" + + " 'base' was updated to include this miner's address\x02Failed to load ba" + + "se config from database: %[1]s\x02Failed to parse base config: %[1]s\x02" + + "Failed to regenerate base config: %[1]s\x02Enter the info to connect to " + + "your Yugabyte database installation (https://download.yugabyte.com/)\x02" + + "Host: %[1]s\x02Port: %[1]s\x02Username: %[1]s\x02Password: %[1]s\x02Data" + + "base: %[1]s\x02Continue to connect and update schema.\x04\x00\x01 <\x02D" + + "atabase config error occurred, abandoning migration: %[1]s\x02Enter the " + + "Yugabyte database host(s)\x02No host provided\x02Enter the Yugabyte data" + + "base %[1]s\x02Error connecting to Yugabyte database: %[1]s\x02Configurat" + + "ion 'base' was updated to include this miner's address (%[1]s) and its w" + + "allet setup.\x02Compare the configurations %[1]s to %[2]s. Changes betwe" + + "en the miner IDs other than wallet addreses should be a new, minimal lay" + + "er for runners that need it.\x02Configuration 'base' was created to rese" + + "mble this lotus-miner's config.toml .\x04\x00\x01 \x15\x02Layer %[1]s cr" + + "eated.\x04\x00\x01 \x19\x02To work with the config:\x02To run Curio: Wit" + + "h machine or cgroup isolation, use the command (with example layer selec" + + "tion):" var koIndex = []uint32{ // 122 elements // Entry 0 - 1F 0x00000000, 0x00000044, 0x000000c1, 0x000001c1, 0x0000020e, 0x00000289, 0x000002aa, 0x000002bc, 0x000002e5, 0x00000300, 0x00000325, 0x00000348, - 0x000003a2, 0x000003f2, 0x00000418, 0x00000471, - 0x00000490, 0x000004cc, 0x000004fc, 0x00000554, - 0x000005e0, 0x00000619, 0x0000066f, 0x000006ad, - 0x000006fb, 0x00000716, 0x00000750, 0x00000783, - 0x000007bd, 0x000007e7, 0x00000811, 0x00000853, + 0x000003b2, 0x00000402, 0x00000428, 0x00000481, + 0x000004a0, 0x000004dc, 0x0000050c, 0x00000564, + 0x000005f0, 0x00000629, 0x0000067f, 0x000006bd, + 0x0000070b, 0x00000726, 0x00000760, 0x00000793, + 0x000007cd, 0x000007f7, 0x00000821, 0x00000863, // Entry 20 - 3F - 0x00000877, 0x00000884, 0x0000090a, 0x0000095c, - 0x00000983, 0x00000a1f, 0x00000ab1, 0x00000b2c, - 0x00000b70, 0x00000bae, 0x00000bd5, 0x00000c40, - 0x00000c8a, 0x00000cb1, 0x00000ccc, 0x00000d1b, - 0x00000d5c, 0x00000d9c, 0x00000de3, 0x00000e89, - 0x00000eb9, 0x00000f08, 0x00000f2b, 0x00000f4c, - 0x00000f6f, 0x00000f92, 0x00000fd0, 0x00000ff4, - 0x0000100a, 0x00001075, 0x000010c4, 0x000010cb, + 0x00000887, 0x00000894, 0x0000091a, 0x0000096c, + 0x00000993, 0x00000a2f, 0x00000ac1, 0x00000b3c, + 0x00000b80, 0x00000bbe, 0x00000be5, 0x00000c50, + 0x00000c9d, 0x00000cc4, 0x00000cdf, 0x00000d2e, + 0x00000d6f, 0x00000daf, 0x00000df6, 0x00000e9c, + 0x00000ecc, 0x00000f1b, 0x00000f3e, 0x00000f5f, + 0x00000f82, 0x00000fa5, 0x00000fe3, 0x00001007, + 0x0000101d, 0x00001088, 0x000010d7, 0x000010de, // Entry 40 - 5F - 0x00001113, 0x00001165, 0x000011bf, 0x00001229, - 0x000012ba, 0x000012d2, 0x000012ec, 0x00001310, - 0x00001343, 0x0000135b, 0x00001373, 0x0000138b, - 0x000013a0, 0x000013b8, 0x0000140f, 0x0000143a, - 0x00001452, 0x00001479, 0x0000149c, 0x000014b0, - 0x000014c5, 0x000014e9, 0x00001513, 0x00001524, - 0x0000154a, 0x00001570, 0x000015a9, 0x000015e1, - 0x00001619, 0x00001638, 0x00001684, 0x00001742, + 0x00001126, 0x00001178, 0x000011d2, 0x0000123c, + 0x000012cd, 0x000012e5, 0x000012ff, 0x00001323, + 0x00001356, 0x0000136e, 0x00001386, 0x0000139e, + 0x000013b3, 0x000013cb, 0x00001422, 0x0000144d, + 0x00001465, 0x0000148c, 0x000014af, 0x000014c3, + 0x000014d8, 0x000014fc, 0x00001526, 0x00001537, + 0x0000155d, 0x00001583, 0x000015bc, 0x000015f4, + 0x0000162c, 0x0000164b, 0x00001697, 0x00001755, // Entry 60 - 7F - 0x0000178e, 0x000017dc, 0x000017ff, 0x0000185b, - 0x000018ab, 0x00001900, 0x00001943, 0x00001982, - 0x000019f0, 0x00001a01, 0x00001a0f, 0x00001a27, - 0x00001a3b, 0x00001a55, 0x00001a7f, 0x00001ae2, - 0x00001b1e, 0x00001b48, 0x00001b80, 0x00001bd4, - 0x00001c3e, 0x00001cf8, 0x00001d5c, 0x00001d8b, - 0x00001db2, 0x00001e3e, + 0x000017a1, 0x000017ef, 0x00001812, 0x0000186e, + 0x000018be, 0x00001913, 0x00001956, 0x00001995, + 0x00001a03, 0x00001a14, 0x00001a22, 0x00001a3a, + 0x00001a4e, 0x00001a68, 0x00001a92, 0x00001af5, + 0x00001b31, 0x00001b5b, 0x00001b93, 0x00001be7, + 0x00001c60, 0x00001d1a, 0x00001d71, 0x00001da0, + 0x00001dc7, 0x00001e53, } // Size: 512 bytes -const koData string = "" + // Size: 7742 bytes +const koData string = "" + // Size: 7763 bytes "\x04\x00\x01 ?\x02화살표 키를 사용하여 이동하세요: ↓ ↑ → ←\x02이 대화형 도구는 새로운 채굴자 액터를 생성" + "하고 그에 대한 기본 구성 레이어를 생성합니다.\x02이 프로세스는 부분적으로 항등원적입니다. 새로운 채굴자 액터가 생성되었고" + " 후속 단계가 실패하는 경우 사용자는 구성을 완료하기 위해 'curio config new-cluster < 채굴자 ID >'를 " + "실행해야 합니다.\x02이 대화형 도구는 5분 안에 lotus-miner를 Curio로 이주합니다.\x02각 단계는 확인이 필" + "요하며 되돌릴 수 있습니다. 언제든지 Ctrl+C를 눌러 종료할 수 있습니다.\x02터미널에서 Ctrl+C가 눌림\x02나는 " + "원한다:\x02기존의 Lotus-Miner에서 이전하기\x02새로운 채굴자 생성\x02나머지 단계를 중단합니다.\x02Lotu" + - "s-Miner에서 Curio로 이주.\x02더 많은 안내를 위해 %[1]s를 사용하여 웹 인터페이스를 시도하세요.\x02해당하는 " + - "경우 이제 시장 노드를 이주할 수 있습니다 (%[1]s).\x02새로운 채굴자 초기화 완료.\x02lotus-miner con" + - "fig.toml을 Curio의 데이터베이스 구성으로 이전 중입니다.\x02API 가져오기 오류: %[1]s\x02FullNode의" + - " API 정보를 가져올 수 없습니다: %[1]w\x02토큰을 가져오는 중 오류 발생: %[1]s\x02레이어에 구성을 저장하는 중" + - " 오류 발생: %[1]s. 마이그레이션 중단\x02Curio 팀은 당신이 사용하는 소프트웨어를 개선하고자 합니다. 팀에게 `%[1" + - "]s`를 사용 중이라고 알려주세요.\x02Curio 팀과 공유하고 싶은 것을 선택하세요.\x02개별 데이터: 채굴자 ID, Cur" + - "io 버전, 체인 (%[1]s 또는 %[2]s). 서명됨.\x02집계-익명: 버전, 체인, 및 채굴자 파워 (버킷).\x02힌트:" + - " 나는 어떤 체인에서든 Curio를 실행 중인 사람입니다.\x02아무것도 없습니다.\x02마이너 파워를 가져오는 중 오류 발생: " + - "%[1]s\x02메시지를 마샬하는 중 오류 발생: %[1]s\x02마이너 정보를 가져오는 중 오류 발생: %[1]s\x02메시지 " + - "서명 중 오류 발생: %[1]s\x02메시지 전송 중 오류 발생: %[1]s\x04\x00\x01 =\x02메시지 전송 중 오" + - "류 발생: 상태 %[1]s, 메시지:\x02메시지가 전송되었습니다.\x04\x00\x01 \x08\x02문서:\x02'%[1]" + - "s' 레이어에는 공통 구성이 저장됩니다. 모든 Curio 인스턴스는 %[2]s 인수에 포함시킬 수 있습니다.\x02기계별 구성 변" + - "경을 위해 다른 레이어를 추가할 수 있습니다.\x02Filecoin %[1]s 채널: %[2]s 및 %[3]s\x02신뢰성 향" + - "상을 위한 중복성 사용: 적어도 post 레이어를 사용하여 여러 대의 기계를 시작하십시오: 'curio run --layers" + - "=post'\x02한 개의 데이터베이스는 여러 광부 ID를 제공할 수 있습니다: 각 lotus-miner에 대해 마이그레이션을 실" + - "행하세요.\x02데이터베이스 자격 증명이 %[2]s에 입력되었으므로 지금 %[1]s을 시작하거나 다시 시작하세요.\x02%[1" + - "]s가 Yugabyte에 섹터를 기록하도록 대기 중입니다.\x02광부 ID를 해석하는 중 오류 발생: %[1]s: ID: %[2]" + - "s\x02섹터 확인 중 오류 발생: %[1]s\x02섹터가 데이터베이스에 있습니다. 데이터베이스가 %[1]s를 위해 준비되었습니다" + - ".\x02이제 lotus-miner를 종료하고 시스템을 %[1]s로 이동하세요.\x02계속하려면 리턴을 누르세요\x02마이그레이션" + - " 중단.\x02섹터가 확인되었습니다. %[1]d개의 섹터 위치를 찾았습니다.\x02Yugabyte에 연결되었습니다. 스키마가 현재" + - "입니다.\x02데이터베이스에서 Sector Indexing을 활성화합니다.\x02config.toml을 인코딩하는 중 오류가 " + - "발생했습니다: %[1]s\x02%[1]s을 Yugabyte 정보로 업데이트하려면 리턴 키를 누르세요. 변경 사항을 적용하기 전" + - "에 해당 폴더에 백업 파일이 작성됩니다.\x02경로를 확장하는 중 오류 발생: %[1]s\x02config.toml의 파일 모" + - "드를 읽는 중 오류가 발생했습니다: %[1]s\x02백업 파일 생성 오류: %[1]s\x02config.toml 읽기 오류: " + - "%[1]s\x02백업 파일 쓰기 오류: %[1]s\x02백업 파일 닫기 오류: %[1]s\x02config.toml을 쓰는 중 오" + - "류가 발생했습니다: %[1]s\x04\x00\x01 \x1f\x02로터스 마이너 재시작.\x02Yugabyte에 연결됨\x02" + - "시작하려면 밀봉 파이프라인이 비어 있고 lotus-miner가 종료되었는지 확인하세요.\x02로터스 마이너 구성 디렉토리의 위" + - "치를 선택하시겠습니까?\x02기타\x02%[1]s에서 사용하는 구성 디렉터리 경로를 입력하세요.\x04\x00\x01 M" + - "\x02경로가 제공되지 않았으므로 마이그레이션을 포기합니다\x02제공된 디렉토리에서 config.toml 파일을 읽을 수 없습니다" + - ". 오류: %[1]s\x02디렉토리에서 저장소를 생성할 수 없습니다: %[1]s. 마이그레이션을 중단합니다.\x02광부 저장소를 " + - "잠금 해제할 수 없습니다. 귀하의 광부를 중지해야 합니다: %[1]s\x0a 마이그레이션을 중단합니다.\x02마이너 구성 읽기" + - "\x04\x00\x01\x0a\x15\x02단계 완료: %[1]s\x02새 채굴자 액터 초기화 중.\x02새 채굴자를 생성하기 위" + - "한 정보 입력\x02소유자 주소: %[1]s\x02작업자 주소: %[1]s\x02송신자 주소: %[1]s\x02섹터 크기: %" + - "[1]d\x02신뢰 에포크: %[1]d\x02주소를 확인하고 새 채굴자 액터를 생성하려면 계속 진행하세요.\x04\x00\x01 " + - "&\x02채굴자 생성 오류 발생: %[1]s\x02소유자 주소 입력\x02주소가 제공되지 않았습니다\x02주소 구문 분석 실패: " + - "%[1]s\x02%[1]s 주소 입력\x02섹터 크기 입력\x02값이 제공되지 않았습니다\x02섹터 크기 구문 분석 실패: %[1" + - "]s\x02신뢰 에포크\x02신뢰도 구문 분석 실패: %[1]s\x02채굴자 액터 생성 실패: %[1]s\x02%[1]s 채굴자가" + - " 성공적으로 생성되었습니다\x02데이터베이스에 연결할 수 없습니다: %[1]s\x02풀 노드 API에 연결하는 중 오류 발생: %" + - "[1]s\x02사전 초기화 단계 완료\x02비밀번호를 위한 랜덤 바이트 생성에 실패했습니다: %[1]s\x02마이너 생성은 ide" + - "mpotent하지 않으므로 가이드 설정을 다시 실행하지 마십시오. 구성을 완료하려면 'curio config new-cluster" + - " %[1]s'를 실행해야 합니다.\x02FullNode에 대한 API 정보를 가져오는 데 실패했습니다: %[1]w\x02데몬 노드" + - "로부터 인증 토큰을 확인하는 중 오류 발생: %[1]s\x02기본 구성 생성 실패: %[1]s\x02데이터베이스에 'base'" + - " 구성 레이어를 삽입하는 데 실패했습니다: %[1]s\x02이 마이너 주소를 포함한 구성 'base'가 업데이트되었습니다.\x02" + - "데이터베이스에서 기본 구성을 로드하는 데 실패했습니다: %[1]s\x02기본 구성을 구문 분석하는 데 실패했습니다: %[1]s" + - "\x02기본 구성을 재생성하는 데 실패했습니다: %[1]s\x02Yugabyte 데이터베이스 설치에 연결할 정보를 입력하십시오 (" + - "https://download.yugabyte.com/)\x02호스트: %[1]s\x02포트: %[1]s\x02사용자 이름: %[" + - "1]s\x02비밀번호: %[1]s\x02데이터베이스: %[1]s\x02계속 연결 및 스키마 업데이트.\x04\x00\x01 ^" + - "\x02데이터베이스 구성 오류가 발생하여 마이그레이션을 포기합니다: %[1]s\x02Yugabyte 데이터베이스 호스트를 입력하십" + - "시오\x02호스트가 제공되지 않았습니다\x02Yugabyte 데이터베이스 %[1]s을 입력하십시오\x02Yugabyte 데이터" + - "베이스에 연결하는 중 오류가 발생했습니다: %[1]s\x02이 마이너의 주소와 지갑 설정을 포함하도록 구성 'base'가 업데" + - "이트되었습니다.\x02구성 %[1]s를 %[2]s과 비교하세요. 지갑 주소 이외의 마이너 ID 사이의 변경 사항은 필요한 실행" + - "자를 위한 새로운 최소한의 레이어여야 합니다.\x02이 마이너의 주소와 지갑 설정을 포함하도록 구성 'base'가 생성되었습니" + - "다.\x04\x00\x01 *\x02레이어 %[1]s가 생성되었습니다.\x04\x00\x01 \x22\x02구성 파일을 사용하" + - "려면:\x02Curio를 실행하려면: 기계 또는 cgroup 격리를 사용하여 다음 명령을 사용하세요 (예제 레이어 선택과 함께" + - "):" + "s-Miner에서 Curio로 이주.\x02%[1]s를 사용하여 웹 인터페이스를 시도하고 더 나은 안내된 개선을 진행하세요." + + "\x02해당하는 경우 이제 시장 노드를 이주할 수 있습니다 (%[1]s).\x02새로운 채굴자 초기화 완료.\x02lotus-mi" + + "ner config.toml을 Curio의 데이터베이스 구성으로 이전 중입니다.\x02API 가져오기 오류: %[1]s\x02Fu" + + "llNode의 API 정보를 가져올 수 없습니다: %[1]w\x02토큰을 가져오는 중 오류 발생: %[1]s\x02레이어에 구성을" + + " 저장하는 중 오류 발생: %[1]s. 마이그레이션 중단\x02Curio 팀은 당신이 사용하는 소프트웨어를 개선하고자 합니다. 팀" + + "에게 `%[1]s`를 사용 중이라고 알려주세요.\x02Curio 팀과 공유하고 싶은 것을 선택하세요.\x02개별 데이터: 채굴" + + "자 ID, Curio 버전, 체인 (%[1]s 또는 %[2]s). 서명됨.\x02집계-익명: 버전, 체인, 및 채굴자 파워 (" + + "버킷).\x02힌트: 나는 어떤 체인에서든 Curio를 실행 중인 사람입니다.\x02아무것도 없습니다.\x02마이너 파워를 가" + + "져오는 중 오류 발생: %[1]s\x02메시지를 마샬하는 중 오류 발생: %[1]s\x02마이너 정보를 가져오는 중 오류 발생" + + ": %[1]s\x02메시지 서명 중 오류 발생: %[1]s\x02메시지 전송 중 오류 발생: %[1]s\x04\x00\x01 =" + + "\x02메시지 전송 중 오류 발생: 상태 %[1]s, 메시지:\x02메시지가 전송되었습니다.\x04\x00\x01 \x08\x02" + + "문서:\x02'%[1]s' 레이어에는 공통 구성이 저장됩니다. 모든 Curio 인스턴스는 %[2]s 인수에 포함시킬 수 있습니" + + "다.\x02기계별 구성 변경을 위해 다른 레이어를 추가할 수 있습니다.\x02Filecoin %[1]s 채널: %[2]s 및 " + + "%[3]s\x02신뢰성 향상을 위한 중복성 사용: 적어도 post 레이어를 사용하여 여러 대의 기계를 시작하십시오: 'curio " + + "run --layers=post'\x02한 개의 데이터베이스는 여러 광부 ID를 제공할 수 있습니다: 각 lotus-miner에 " + + "대해 마이그레이션을 실행하세요.\x02데이터베이스 자격 증명이 %[2]s에 입력되었으므로 지금 %[1]s을 시작하거나 다시 시" + + "작하세요.\x02%[1]s가 Yugabyte에 섹터를 기록하도록 대기 중입니다.\x02광부 ID를 해석하는 중 오류 발생: %" + + "[1]s: ID: %[2]s\x02섹터 확인 중 오류 발생: %[1]s\x02섹터가 데이터베이스에 있습니다. 데이터베이스가 %[1" + + "]s를 위해 준비되었습니다.\x02이제 lotus-miner와 lotus-worker를 종료하고 %[1]s을 실행하세요.\x02계" + + "속하려면 리턴을 누르세요\x02마이그레이션 중단.\x02섹터가 확인되었습니다. %[1]d개의 섹터 위치를 찾았습니다.\x02Y" + + "ugabyte에 연결되었습니다. 스키마가 현재입니다.\x02데이터베이스에서 Sector Indexing을 활성화합니다.\x02co" + + "nfig.toml을 인코딩하는 중 오류가 발생했습니다: %[1]s\x02%[1]s을 Yugabyte 정보로 업데이트하려면 리턴 키" + + "를 누르세요. 변경 사항을 적용하기 전에 해당 폴더에 백업 파일이 작성됩니다.\x02경로를 확장하는 중 오류 발생: %[1]s" + + "\x02config.toml의 파일 모드를 읽는 중 오류가 발생했습니다: %[1]s\x02백업 파일 생성 오류: %[1]s\x02" + + "config.toml 읽기 오류: %[1]s\x02백업 파일 쓰기 오류: %[1]s\x02백업 파일 닫기 오류: %[1]s\x02" + + "config.toml을 쓰는 중 오류가 발생했습니다: %[1]s\x04\x00\x01 \x1f\x02로터스 마이너 재시작.\x02" + + "Yugabyte에 연결됨\x02시작하려면 밀봉 파이프라인이 비어 있고 lotus-miner가 종료되었는지 확인하세요.\x02로터스" + + " 마이너 구성 디렉토리의 위치를 선택하시겠습니까?\x02기타\x02%[1]s에서 사용하는 구성 디렉터리 경로를 입력하세요.\x04" + + "\x00\x01 M\x02경로가 제공되지 않았으므로 마이그레이션을 포기합니다\x02제공된 디렉토리에서 config.toml 파일을" + + " 읽을 수 없습니다. 오류: %[1]s\x02디렉토리에서 저장소를 생성할 수 없습니다: %[1]s. 마이그레이션을 중단합니다." + + "\x02광부 저장소를 잠금 해제할 수 없습니다. 귀하의 광부를 중지해야 합니다: %[1]s\x0a 마이그레이션을 중단합니다." + + "\x02마이너 구성 읽기\x04\x00\x01\x0a\x15\x02단계 완료: %[1]s\x02새 채굴자 액터 초기화 중.\x02" + + "새 채굴자를 생성하기 위한 정보 입력\x02소유자 주소: %[1]s\x02작업자 주소: %[1]s\x02송신자 주소: %[1]" + + "s\x02섹터 크기: %[1]d\x02신뢰 에포크: %[1]d\x02주소를 확인하고 새 채굴자 액터를 생성하려면 계속 진행하세요." + + "\x04\x00\x01 &\x02채굴자 생성 오류 발생: %[1]s\x02소유자 주소 입력\x02주소가 제공되지 않았습니다\x02" + + "주소 구문 분석 실패: %[1]s\x02%[1]s 주소 입력\x02섹터 크기 입력\x02값이 제공되지 않았습니다\x02섹터 크" + + "기 구문 분석 실패: %[1]s\x02신뢰 에포크\x02신뢰도 구문 분석 실패: %[1]s\x02채굴자 액터 생성 실패: %[" + + "1]s\x02%[1]s 채굴자가 성공적으로 생성되었습니다\x02데이터베이스에 연결할 수 없습니다: %[1]s\x02풀 노드 API" + + "에 연결하는 중 오류 발생: %[1]s\x02사전 초기화 단계 완료\x02비밀번호를 위한 랜덤 바이트 생성에 실패했습니다: %" + + "[1]s\x02마이너 생성은 idempotent하지 않으므로 가이드 설정을 다시 실행하지 마십시오. 구성을 완료하려면 'curio" + + " config new-cluster %[1]s'를 실행해야 합니다.\x02FullNode에 대한 API 정보를 가져오는 데 실패했" + + "습니다: %[1]w\x02데몬 노드로부터 인증 토큰을 확인하는 중 오류 발생: %[1]s\x02기본 구성 생성 실패: %[1]" + + "s\x02데이터베이스에 'base' 구성 레이어를 삽입하는 데 실패했습니다: %[1]s\x02이 마이너 주소를 포함한 구성 'ba" + + "se'가 업데이트되었습니다.\x02데이터베이스에서 기본 구성을 로드하는 데 실패했습니다: %[1]s\x02기본 구성을 구문 분석하" + + "는 데 실패했습니다: %[1]s\x02기본 구성을 재생성하는 데 실패했습니다: %[1]s\x02Yugabyte 데이터베이스 설" + + "치에 연결할 정보를 입력하십시오 (https://download.yugabyte.com/)\x02호스트: %[1]s\x02포트" + + ": %[1]s\x02사용자 이름: %[1]s\x02비밀번호: %[1]s\x02데이터베이스: %[1]s\x02계속 연결 및 스키마 " + + "업데이트.\x04\x00\x01 ^\x02데이터베이스 구성 오류가 발생하여 마이그레이션을 포기합니다: %[1]s\x02Yuga" + + "byte 데이터베이스 호스트를 입력하십시오\x02호스트가 제공되지 않았습니다\x02Yugabyte 데이터베이스 %[1]s을 입력하" + + "십시오\x02Yugabyte 데이터베이스에 연결하는 중 오류가 발생했습니다: %[1]s\x02기본 설정 'base'가 이 마이" + + "너의 주소(%[1]s) 및 지갑 설정을 포함하도록 업데이트되었습니다.\x02구성 %[1]s를 %[2]s과 비교하세요. 지갑 주" + + "소 이외의 마이너 ID 사이의 변경 사항은 필요한 실행자를 위한 새로운 최소한의 레이어여야 합니다.\x02'base' 설정이 " + + "이 lotus-miner의 config.toml과 유사하게 만들어졌습니다.\x04\x00\x01 *\x02레이어 %[1]s가 " + + "생성되었습니다.\x04\x00\x01 \x22\x02구성 파일을 사용하려면:\x02Curio를 실행하려면: 기계 또는 cgro" + + "up 격리를 사용하여 다음 명령을 사용하세요 (예제 레이어 선택과 함께):" var zhIndex = []uint32{ // 122 elements // Entry 0 - 1F 0x00000000, 0x00000033, 0x0000008b, 0x00000134, 0x0000017c, 0x000001cb, 0x000001e4, 0x000001f1, 0x00000211, 0x0000022a, 0x00000240, 0x0000025d, - 0x0000029c, 0x000002dd, 0x000002f9, 0x0000033e, - 0x0000035b, 0x00000384, 0x000003a2, 0x000003db, - 0x0000042f, 0x0000045c, 0x000004ab, 0x000004e6, - 0x0000051b, 0x00000525, 0x00000549, 0x00000567, - 0x0000058b, 0x000005a9, 0x000005c7, 0x000005fc, + 0x000002a5, 0x000002e6, 0x00000302, 0x00000347, + 0x00000364, 0x0000038d, 0x000003ab, 0x000003e4, + 0x00000438, 0x00000465, 0x000004b4, 0x000004ef, + 0x00000524, 0x0000052e, 0x00000552, 0x00000570, + 0x00000594, 0x000005b2, 0x000005d0, 0x00000605, // Entry 20 - 3F - 0x0000060f, 0x0000061e, 0x00000678, 0x000006b5, - 0x000006dd, 0x0000073c, 0x0000078c, 0x000007df, - 0x00000805, 0x00000832, 0x00000850, 0x0000088c, - 0x000008be, 0x000008ce, 0x000008de, 0x00000911, - 0x0000093e, 0x00000963, 0x00000986, 0x000009fe, - 0x00000a1c, 0x00000a4b, 0x00000a6f, 0x00000a94, - 0x00000ab8, 0x00000adc, 0x00000aff, 0x00000b1f, - 0x00000b34, 0x00000b7f, 0x00000baf, 0x00000bb6, + 0x00000618, 0x00000627, 0x00000681, 0x000006be, + 0x000006e6, 0x00000745, 0x00000795, 0x000007e8, + 0x0000080e, 0x0000083b, 0x00000859, 0x00000895, + 0x000008d9, 0x000008e9, 0x000008f9, 0x0000092c, + 0x00000959, 0x0000097e, 0x000009a1, 0x00000a19, + 0x00000a37, 0x00000a66, 0x00000a8a, 0x00000aaf, + 0x00000ad3, 0x00000af7, 0x00000b1a, 0x00000b3a, + 0x00000b4f, 0x00000b9a, 0x00000bca, 0x00000bd1, // Entry 40 - 5F - 0x00000be0, 0x00000c04, 0x00000c48, 0x00000c7a, - 0x00000cc3, 0x00000cd6, 0x00000cf0, 0x00000d0f, - 0x00000d34, 0x00000d4c, 0x00000d61, 0x00000d79, - 0x00000d8d, 0x00000da4, 0x00000dd5, 0x00000dfa, - 0x00000e10, 0x00000e20, 0x00000e3a, 0x00000e4e, - 0x00000e61, 0x00000e6e, 0x00000e8e, 0x00000e9e, - 0x00000ebb, 0x00000edb, 0x00000ef5, 0x00000f12, - 0x00000f43, 0x00000f5c, 0x00000f85, 0x00001012, + 0x00000bfb, 0x00000c1f, 0x00000c63, 0x00000c95, + 0x00000cde, 0x00000cf1, 0x00000d0b, 0x00000d2a, + 0x00000d4f, 0x00000d67, 0x00000d7c, 0x00000d94, + 0x00000da8, 0x00000dbf, 0x00000df0, 0x00000e15, + 0x00000e2b, 0x00000e3b, 0x00000e55, 0x00000e69, + 0x00000e7c, 0x00000e89, 0x00000ea9, 0x00000eb9, + 0x00000ed6, 0x00000ef6, 0x00000f10, 0x00000f2d, + 0x00000f5e, 0x00000f77, 0x00000fa0, 0x0000102d, // Entry 60 - 7F - 0x0000103e, 0x00001079, 0x00001099, 0x000010ca, - 0x000010fd, 0x0000112a, 0x0000114b, 0x00001171, - 0x000011cb, 0x000011da, 0x000011e9, 0x000011fb, - 0x0000120a, 0x0000121c, 0x0000123b, 0x00001273, - 0x00001298, 0x000012a8, 0x000012c6, 0x000012f2, - 0x0000133e, 0x000013c0, 0x0000140c, 0x00001426, - 0x0000143e, 0x00001495, + 0x00001059, 0x00001094, 0x000010b4, 0x000010e5, + 0x00001118, 0x00001145, 0x00001166, 0x0000118c, + 0x000011e6, 0x000011f5, 0x00001204, 0x00001216, + 0x00001225, 0x00001237, 0x00001256, 0x0000128e, + 0x000012b3, 0x000012c3, 0x000012e1, 0x0000130d, + 0x0000135e, 0x000013e0, 0x00001427, 0x00001441, + 0x00001459, 0x000014b0, } // Size: 512 bytes -const zhData string = "" + // Size: 5269 bytes +const zhData string = "" + // Size: 5296 bytes "\x04\x00\x01 .\x02使用箭头键进行导航:↓ ↑ → ←\x02此交互式工具将创建一个新的矿工角色,并为其创建基本配置层。\x02" + "该过程部分幂等。一旦创建了新的矿工角色,并且随后的步骤失败,用户需要运行 'curio config new-cluster < 矿工 ID" + " >' 来完成配置。\x02这个交互式工具可以在5分钟内将lotus-miner迁移到Curio。\x02每一步都需要您的确认,并且可以撤销。随" + "时按Ctrl+C退出。\x02在终端中按下Ctrl+C\x02我想要:\x02从现有的 Lotus-Miner 迁移\x02创建一个新的矿工" + - "\x02中止剩余步骤。\x02Lotus-Miner到Curio迁移。\x02尝试使用%[1]s的Web界面进行进一步引导式改进。\x02如果适" + - "用,您现在可以迁移您的市场节点(%[1]s)。\x02新矿工初始化完成。\x02将 lotus-miner config.toml 迁移到 " + - "Curio 的数据库配置中。\x02获取 API 时出错:%[1]s\x02无法获取FullNode的API信息:%[1]w\x02获取令牌时出" + - "错:%[1]s\x02保存配置到层时出错:%[1]s。正在中止迁移\x02Curio 团队希望改进您使用的软件。告诉团队您正在使用 `%[1" + - "]s`。\x02选择您想与Curio团队分享的内容。\x02个人数据:矿工 ID,Curio 版本,链(%[1]s 或 %[2]s)。签名。" + + "\x02中止剩余步骤。\x02Lotus-Miner到Curio迁移。\x02尝试使用%[1]s的网络界面进行更进一步的指导性改进。\x02如果" + + "适用,您现在可以迁移您的市场节点(%[1]s)。\x02新矿工初始化完成。\x02将 lotus-miner config.toml 迁移到" + + " Curio 的数据库配置中。\x02获取 API 时出错:%[1]s\x02无法获取FullNode的API信息:%[1]w\x02获取令牌时" + + "出错:%[1]s\x02保存配置到层时出错:%[1]s。正在中止迁移\x02Curio 团队希望改进您使用的软件。告诉团队您正在使用 `%[" + + "1]s`。\x02选择您想与Curio团队分享的内容。\x02个人数据:矿工 ID,Curio 版本,链(%[1]s 或 %[2]s)。签名。" + "\x02聚合-匿名:版本,链和矿工算力(分桶)。\x02提示:我是在任何链上运行 Curio 的人。\x02没有。\x02获取矿工功率时出错:%" + "[1]s\x02整理消息时出错:%[1]s\x02获取矿工信息时出错:%[1]s\x02签署消息时出错:%[1]s\x02发送消息时出错:%[1" + "]s\x04\x00\x01 0\x02发送消息时出错:状态%[1]s,消息:\x02消息已发送。\x04\x00\x01 \x0a\x02文档" + @@ -444,32 +445,32 @@ const zhData string = "" + // Size: 5269 bytes "\x02Filecoin %[1]s 频道:%[2]s 和 %[3]s\x02通过冗余增加可靠性:使用至少后层启动多台机器:'curio run" + " --layers=post'\x02一个数据库可以服务多个矿工ID:为每个lotus-miner运行迁移。\x02请立即启动(或重新启动)%[" + "1]s,因为数据库凭据已在%[2]s中。\x02等待%[1]s将扇区写入Yugabyte。\x02解释矿工ID时出错:%[1]s:ID:%[2]" + - "s\x02验证扇区时出错:%[1]s\x02扇区在数据库中。数据库已准备好用于%[1]s。\x02现在关闭lotus-miner并将系统移至%[" + - "1]s。\x02按回车继续\x02中止迁移。\x02扇区已验证。发现了%[1]d个扇区位置。\x02已连接到Yugabyte。模式是当前的。" + - "\x02在数据库中启用扇区索引。\x02编码config.toml时出错:%[1]s\x02按回车键更新 %[1]s 以包含 Yugabyte " + - "信息。在进行更改之前,将在该文件夹中写入备份文件。\x02扩展路径时出错:%[1]s\x02读取config.toml文件模式时出错:%[1" + - "]s\x02创建备份文件时出错:%[1]s\x02读取 config.toml 时出错:%[1]s\x02写入备份文件时出错:%[1]s\x02" + - "关闭备份文件时出错:%[1]s\x02写入config.toml时出错:%[1]s\x04\x00\x01 \x1b\x02重新启动Lotu" + - "s Miner。\x02已连接到Yugabyte\x02开始之前,请确保您的密封管道已排空并关闭lotus-miner。\x02选择您的lotu" + - "s-miner配置目录的位置?\x02其他\x02输入%[1]s使用的配置目录的路径\x04\x00\x01 \x1f\x02未提供路径,放弃迁" + - "移\x02无法读取提供的目录中的config.toml文件,错误:%[1]s\x02无法从目录创建repo:%[1]s。 中止迁移\x02无" + - "法锁定矿工repo。 您的矿工必须停止:%[1]s\x0a 中止迁移\x02读取矿工配置\x04\x00\x01\x0a\x15\x02步骤" + - "完成:%[1]s\x02初始化新的矿工角色。\x02输入创建新矿工所需的信息\x02所有者地址:%[1]s\x02工作地址:%[1]s" + - "\x02发送者地址:%[1]s\x02扇区大小: %[1]d\x02置信度时期: %[1]d\x02继续验证地址并创建新的矿工角色。\x04" + - "\x00\x01 \x02矿工创建错误发生: %[1]s\x02输入所有者地址\x02未提供地址\x02解析地址失败: %[1]s\x02输入" + - " %[1]s 地址\x02输入扇区大小\x02未提供值\x02解析扇区大小失败: %[1]s\x02置信度时期\x02解析置信度失败: %[1]" + - "s\x02创建矿工角色失败: %[1]s\x02矿工 %[1]s 创建成功\x02无法访问数据库: %[1]s\x02连接到完整节点 API 时" + - "发生错误: %[1]s\x02预初始化步骤完成\x02生成密码的随机字节失败: %[1]s\x02请不要再次运行引导设置,因为矿工创建不是幂" + - "等的。 您需要运行 'curio config new-cluster %[1]s' 来完成配置。\x02无法获取 FullNode 的 A" + - "PI 信息: %[1]w\x02无法验证来自守护进程节点的授权令牌: %[1]s\x02无法生成默认配置: %[1]s\x02无法将 'base" + - "' 配置层插入数据库: %[1]s\x02配置 'base' 已更新以包含此矿工的地址\x02从数据库加载基本配置失败:%[1]s\x02解析基" + - "本配置失败:%[1]s\x02重新生成基本配置失败: %[1]s\x02输入连接到您的Yugabyte数据库安装的信息(https://do" + - "wnload.yugabyte.com/)\x02主机:%[1]s\x02端口:%[1]s\x02用户名:%[1]s\x02密码:%[1]s" + - "\x02数据库:%[1]s\x02继续连接和更新架构。\x04\x00\x01 3\x02发生数据库配置错误,放弃迁移:%[1]s\x02输入Y" + - "ugabyte数据库主机(S)\x02未提供主机\x02输入Yugabyte数据库 %[1]s\x02连接到Yugabyte数据库时出错:%[1" + - "]s\x02配置'base'已更新,包含了这个矿工的地址和其钱包设置。\x02比较配置%[1]s和%[2]s。矿工ID之间除了钱包地址的变化应该" + - "是需要的运行者的一个新的、最小的层。\x02配置'base'已创建,包括了这个矿工的地址和其钱包设置。\x04\x00\x01 \x15" + - "\x02层%[1]s已创建。\x04\x00\x01 \x13\x02要使用配置:\x02运行Curio:使用机器或cgroup隔离,使用命令(" + - "附带示例层选择):" + "s\x02验证扇区时出错:%[1]s\x02扇区在数据库中。数据库已准备好用于%[1]s。\x02现在关闭lotus-miner和lotus-w" + + "orker,改为使用%[1]s运行。\x02按回车继续\x02中止迁移。\x02扇区已验证。发现了%[1]d个扇区位置。\x02已连接到Yuga" + + "byte。模式是当前的。\x02在数据库中启用扇区索引。\x02编码config.toml时出错:%[1]s\x02按回车键更新 %[1]s 以" + + "包含 Yugabyte 信息。在进行更改之前,将在该文件夹中写入备份文件。\x02扩展路径时出错:%[1]s\x02读取config.tom" + + "l文件模式时出错:%[1]s\x02创建备份文件时出错:%[1]s\x02读取 config.toml 时出错:%[1]s\x02写入备份文件时" + + "出错:%[1]s\x02关闭备份文件时出错:%[1]s\x02写入config.toml时出错:%[1]s\x04\x00\x01 \x1b" + + "\x02重新启动Lotus Miner。\x02已连接到Yugabyte\x02开始之前,请确保您的密封管道已排空并关闭lotus-miner。" + + "\x02选择您的lotus-miner配置目录的位置?\x02其他\x02输入%[1]s使用的配置目录的路径\x04\x00\x01 \x1f" + + "\x02未提供路径,放弃迁移\x02无法读取提供的目录中的config.toml文件,错误:%[1]s\x02无法从目录创建repo:%[1]s" + + "。 中止迁移\x02无法锁定矿工repo。 您的矿工必须停止:%[1]s\x0a 中止迁移\x02读取矿工配置\x04\x00\x01" + + "\x0a\x15\x02步骤完成:%[1]s\x02初始化新的矿工角色。\x02输入创建新矿工所需的信息\x02所有者地址:%[1]s\x02工" + + "作地址:%[1]s\x02发送者地址:%[1]s\x02扇区大小: %[1]d\x02置信度时期: %[1]d\x02继续验证地址并创建新的" + + "矿工角色。\x04\x00\x01 \x02矿工创建错误发生: %[1]s\x02输入所有者地址\x02未提供地址\x02解析地址失败: " + + "%[1]s\x02输入 %[1]s 地址\x02输入扇区大小\x02未提供值\x02解析扇区大小失败: %[1]s\x02置信度时期\x02解析" + + "置信度失败: %[1]s\x02创建矿工角色失败: %[1]s\x02矿工 %[1]s 创建成功\x02无法访问数据库: %[1]s\x02" + + "连接到完整节点 API 时发生错误: %[1]s\x02预初始化步骤完成\x02生成密码的随机字节失败: %[1]s\x02请不要再次运行引" + + "导设置,因为矿工创建不是幂等的。 您需要运行 'curio config new-cluster %[1]s' 来完成配置。\x02无法获取" + + " FullNode 的 API 信息: %[1]w\x02无法验证来自守护进程节点的授权令牌: %[1]s\x02无法生成默认配置: %[1]s" + + "\x02无法将 'base' 配置层插入数据库: %[1]s\x02配置 'base' 已更新以包含此矿工的地址\x02从数据库加载基本配置失败" + + ":%[1]s\x02解析基本配置失败:%[1]s\x02重新生成基本配置失败: %[1]s\x02输入连接到您的Yugabyte数据库安装的" + + "信息(https://download.yugabyte.com/)\x02主机:%[1]s\x02端口:%[1]s\x02用户名:%[1]" + + "s\x02密码:%[1]s\x02数据库:%[1]s\x02继续连接和更新架构。\x04\x00\x01 3\x02发生数据库配置错误,放弃迁移" + + ":%[1]s\x02输入Yugabyte数据库主机(S)\x02未提供主机\x02输入Yugabyte数据库 %[1]s\x02连接到Yug" + + "abyte数据库时出错:%[1]s\x02'base'配置已更新,包括该矿工的地址(%[1]s)及其钱包设置。\x02比较配置%[1]s和%[2" + + "]s。矿工ID之间除了钱包地址的变化应该是需要的运行者的一个新的、最小的层。\x02'base'配置已创建,以类似于这个lotus-miner的" + + "config.toml。\x04\x00\x01 \x15\x02层%[1]s已创建。\x04\x00\x01 \x13\x02要使用配置:" + + "\x02运行Curio:使用机器或cgroup隔离,使用命令(附带示例层选择):" - // Total table size 20183 bytes (19KiB); checksum: 4FD3E25A + // Total table size 20243 bytes (19KiB); checksum: AB52E150 diff --git a/cmd/curio/internal/translations/locales/en/out.gotext.json b/cmd/curio/internal/translations/locales/en/out.gotext.json index db8a264ee..604678622 100644 --- a/cmd/curio/internal/translations/locales/en/out.gotext.json +++ b/cmd/curio/internal/translations/locales/en/out.gotext.json @@ -89,18 +89,18 @@ "fuzzy": true }, { - "id": "Try the web interface with {__layersgui} for further guided improvements.", - "message": "Try the web interface with {__layersgui} for further guided improvements.", - "translation": "Try the web interface with {__layersgui} for further guided improvements.", + "id": "Try the web interface with {Rendercurio_run___layersgui} for further guided improvements.", + "message": "Try the web interface with {Rendercurio_run___layersgui} for further guided improvements.", + "translation": "Try the web interface with {Rendercurio_run___layersgui} for further guided improvements.", "translatorComment": "Copied from source.", "placeholders": [ { - "id": "__layersgui", + "id": "Rendercurio_run___layersgui", "string": "%[1]s", "type": "string", "underlyingType": "string", "argNum": 1, - "expr": "\"--layers=gui\"" + "expr": "code.Render(\"curio run --layers=gui\")" } ], "fuzzy": true @@ -579,18 +579,18 @@ "fuzzy": true }, { - "id": "Now shut down lotus-miner and move the systems to {Curio}.", - "message": "Now shut down lotus-miner and move the systems to {Curio}.", - "translation": "Now shut down lotus-miner and move the systems to {Curio}.", + "id": "Now shut down lotus-miner and lotus-worker and use run {Rendercurio_run} instead.", + "message": "Now shut down lotus-miner and lotus-worker and use run {Rendercurio_run} instead.", + "translation": "Now shut down lotus-miner and lotus-worker and use run {Rendercurio_run} instead.", "translatorComment": "Copied from source.", "placeholders": [ { - "id": "Curio", + "id": "Rendercurio_run", "string": "%[1]s", "type": "string", "underlyingType": "string", "argNum": 1, - "expr": "\"Curio\"" + "expr": "code.Render(\"curio run\")" } ], "fuzzy": true @@ -1553,10 +1553,20 @@ "fuzzy": true }, { - "id": "Configuration 'base' was updated to include this miner's address and its wallet setup.", - "message": "Configuration 'base' was updated to include this miner's address and its wallet setup.", - "translation": "Configuration 'base' was updated to include this miner's address and its wallet setup.", + "id": "Configuration 'base' was updated to include this miner's address ({MinerAddress}) and its wallet setup.", + "message": "Configuration 'base' was updated to include this miner's address ({MinerAddress}) and its wallet setup.", + "translation": "Configuration 'base' was updated to include this miner's address ({MinerAddress}) and its wallet setup.", "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "MinerAddress", + "string": "%[1]s", + "type": "github.com/filecoin-project/go-address.Address", + "underlyingType": "struct{str string}", + "argNum": 1, + "expr": "minerAddress" + } + ], "fuzzy": true }, { @@ -1585,9 +1595,9 @@ "fuzzy": true }, { - "id": "Configuration 'base' was created to include this miner's address and its wallet setup.", - "message": "Configuration 'base' was created to include this miner's address and its wallet setup.", - "translation": "Configuration 'base' was created to include this miner's address and its wallet setup.", + "id": "Configuration 'base' was created to resemble this lotus-miner's config.toml .", + "message": "Configuration 'base' was created to resemble this lotus-miner's config.toml .", + "translation": "Configuration 'base' was created to resemble this lotus-miner's config.toml .", "translatorComment": "Copied from source.", "fuzzy": true }, diff --git a/cmd/curio/internal/translations/locales/ko/messages.gotext.json b/cmd/curio/internal/translations/locales/ko/messages.gotext.json index 61107880c..bac1140e8 100644 --- a/cmd/curio/internal/translations/locales/ko/messages.gotext.json +++ b/cmd/curio/internal/translations/locales/ko/messages.gotext.json @@ -1077,6 +1077,30 @@ "translation": "기본 구성을 구문 분석하는 데 실패했습니다: {Error}", "message": "Failed to parse base config: {Error}", "placeholder": null + }, + { + "id": "Try the web interface with {Rendercurio_run___layersgui} for further guided improvements.", + "translation": "{Rendercurio_run___layersgui}를 사용하여 웹 인터페이스를 시도하고 더 나은 안내된 개선을 진행하세요.", + "message": "Try the web interface with {Rendercurio_run___layersgui} for further guided improvements.", + "placeholder": null + }, + { + "id": "Now shut down lotus-miner and lotus-worker and use run {Rendercurio_run} instead.", + "translation": "이제 lotus-miner와 lotus-worker를 종료하고 {Rendercurio_run}을 실행하세요.", + "message": "Now shut down lotus-miner and lotus-worker and use run {Rendercurio_run} instead.", + "placeholder": null + }, + { + "id": "Configuration 'base' was updated to include this miner's address ({MinerAddress}) and its wallet setup.", + "translation": "기본 설정 'base'가 이 마이너의 주소({MinerAddress}) 및 지갑 설정을 포함하도록 업데이트되었습니다.", + "message": "Configuration 'base' was updated to include this miner's address ({MinerAddress}) and its wallet setup.", + "placeholder": null + }, + { + "id": "Configuration 'base' was created to resemble this lotus-miner's config.toml .", + "translation": "'base' 설정이 이 lotus-miner의 config.toml과 유사하게 만들어졌습니다.", + "message": "Configuration 'base' was created to resemble this lotus-miner's config.toml .", + "placeholder": null } ] } \ No newline at end of file diff --git a/cmd/curio/internal/translations/locales/zh/messages.gotext.json b/cmd/curio/internal/translations/locales/zh/messages.gotext.json index fc9a0f313..1e2608fa8 100644 --- a/cmd/curio/internal/translations/locales/zh/messages.gotext.json +++ b/cmd/curio/internal/translations/locales/zh/messages.gotext.json @@ -1047,6 +1047,30 @@ "translation": "解析基本配置失败:{Error}", "message": "Failed to parse base config: {Error}", "placeholder": null + }, + { + "id": "Try the web interface with {Rendercurio_run___layersgui} for further guided improvements.", + "translation": "尝试使用{Rendercurio_run___layersgui}的网络界面进行更进一步的指导性改进。", + "message": "Try the web interface with {Rendercurio_run___layersgui} for further guided improvements.", + "placeholder": null + }, + { + "id": "Now shut down lotus-miner and lotus-worker and use run {Rendercurio_run} instead.", + "translation": "现在关闭lotus-miner和lotus-worker,改为使用{Rendercurio_run}运行。", + "message": "Now shut down lotus-miner and lotus-worker and use run {Rendercurio_run} instead.", + "placeholder": null + }, + { + "id": "Configuration 'base' was updated to include this miner's address ({MinerAddress}) and its wallet setup.", + "translation": "'base'配置已更新,包括该矿工的地址({MinerAddress})及其钱包设置。", + "message": "Configuration 'base' was updated to include this miner's address ({MinerAddress}) and its wallet setup.", + "placeholder": null + }, + { + "id": "Configuration 'base' was created to resemble this lotus-miner's config.toml .", + "translation": "'base'配置已创建,以类似于这个lotus-miner的config.toml。", + "message": "Configuration 'base' was created to resemble this lotus-miner's config.toml .", + "placeholder": null } ] } \ No newline at end of file diff --git a/cmd/curio/rpc/rpc.go b/cmd/curio/rpc/rpc.go index d210a93ac..f45e276c3 100644 --- a/cmd/curio/rpc/rpc.go +++ b/cmd/curio/rpc/rpc.go @@ -252,7 +252,12 @@ func ListenAndServe(ctx context.Context, dependencies *deps.Deps, shutdownChan c } log.Warn("Graceful shutdown successful") }() - log.Infof("Setting up web server at %s", dependencies.Cfg.Subsystems.GuiAddress) + + uiAddress := dependencies.Cfg.Subsystems.GuiAddress + if uiAddress == "" || uiAddress[0] == ':' { + uiAddress = "localhost" + uiAddress + } + log.Infof("GUI: http://%s", uiAddress) eg.Go(web.ListenAndServe) } return eg.Wait() diff --git a/cmd/curio/run.go b/cmd/curio/run.go index d360b6af4..cacacfc0f 100644 --- a/cmd/curio/run.go +++ b/cmd/curio/run.go @@ -62,8 +62,9 @@ var runCmd = &cli.Command{ Value: "~/.curio/", }, &cli.StringSliceFlag{ - Name: "layers", - Usage: "list of layers to be interpreted (atop defaults). Default: base", + Name: "layers", + Aliases: []string{"l", "layer"}, + Usage: "list of layers to be interpreted (atop defaults). Default: base", }, }, Action: func(cctx *cli.Context) (err error) { @@ -169,6 +170,7 @@ var webCmd = &cli.Command{ }, }, Action: func(cctx *cli.Context) error { + db, err := deps.MakeDB(cctx) if err != nil { return err diff --git a/cmd/curio/stop.go b/cmd/curio/stop.go index 5c7bb9880..eb61a34fa 100644 --- a/cmd/curio/stop.go +++ b/cmd/curio/stop.go @@ -13,6 +13,7 @@ var stopCmd = &cli.Command{ Usage: "Stop a running Curio process", Flags: []cli.Flag{}, Action: func(cctx *cli.Context) error { + api, closer, err := lcli.GetAPI(cctx) if err != nil { return err diff --git a/cmd/curio/tasks/tasks.go b/cmd/curio/tasks/tasks.go index 35faba765..3b262efa8 100644 --- a/cmd/curio/tasks/tasks.go +++ b/cmd/curio/tasks/tasks.go @@ -3,9 +3,13 @@ package tasks import ( "context" + "sort" + "strings" + "time" logging "github.com/ipfs/go-log/v2" "github.com/samber/lo" + "golang.org/x/exp/maps" "golang.org/x/xerrors" "github.com/filecoin-project/go-address" @@ -22,6 +26,7 @@ import ( "github.com/filecoin-project/lotus/lib/lazy" "github.com/filecoin-project/lotus/lib/must" "github.com/filecoin-project/lotus/node/modules" + "github.com/filecoin-project/lotus/node/modules/dtypes" ) var log = logging.Logger("curio/deps") @@ -157,6 +162,7 @@ func StartTasks(ctx context.Context, dependencies *deps.Deps) (*harmonytask.Task if err != nil { return nil, err } + go machineDetails(dependencies, activeTasks, ht.ResourcesAvailable().MachineID) if hasAnySealingTask { watcher, err := message.NewMessageWatcher(db, ht, chainSched, full) @@ -172,3 +178,56 @@ func StartTasks(ctx context.Context, dependencies *deps.Deps) (*harmonytask.Task return ht, nil } + +func machineDetails(deps *deps.Deps, activeTasks []harmonytask.TaskInterface, machineID int) { + taskNames := lo.Map(activeTasks, func(item harmonytask.TaskInterface, _ int) string { + return item.TypeDetails().Name + }) + + miners := lo.Map(maps.Keys(deps.Maddrs), func(item dtypes.MinerAddress, _ int) string { + return address.Address(item).String() + }) + sort.Strings(miners) + + _, err := deps.DB.Exec(context.Background(), `INSERT INTO harmony_machine_details + (tasks, layers, startup_time, miners, machine_id) VALUES ($1, $2, $3, $4, $5) + ON CONFLICT (machine_id) DO UPDATE SET tasks=$1, layers=$2, startup_time=$3, miners=$4`, + strings.Join(taskNames, ","), strings.Join(deps.Layers, ","), + time.Now(), strings.Join(miners, ","), machineID) + + if err != nil { + log.Errorf("failed to update machine details: %s", err) + return + } + + // maybePostWarning + if !lo.Contains(taskNames, "WdPost") && !lo.Contains(taskNames, "WinPost") { + // Maybe we aren't running a PoSt for these miners? + var allMachines []struct { + MachineID int `db:"machine_id"` + Miners string `db:"miners"` + Tasks string `db:"tasks"` + } + err := deps.DB.Select(context.Background(), &allMachines, `SELECT machine_id, miners, tasks FROM harmony_machine_details`) + if err != nil { + log.Errorf("failed to get machine details: %s", err) + return + } + + for _, miner := range miners { + var myPostIsHandled bool + for _, m := range allMachines { + if !lo.Contains(strings.Split(m.Miners, ","), miner) { + continue + } + if lo.Contains(strings.Split(m.Tasks, ","), "WdPost") && lo.Contains(strings.Split(m.Tasks, ","), "WinPost") { + myPostIsHandled = true + break + } + } + if !myPostIsHandled { + log.Errorf("No PoSt tasks are running for miner %s. Start handling PoSts immediately with:\n\tcurio run --layers=\"post\" ", miner) + } + } + } +} diff --git a/curiosrc/web/api/apihelper/apihelper.go b/curiosrc/web/api/apihelper/apihelper.go new file mode 100644 index 000000000..07c7898e8 --- /dev/null +++ b/curiosrc/web/api/apihelper/apihelper.go @@ -0,0 +1,19 @@ +package apihelper + +import ( + "net/http" + "runtime/debug" + + logging "github.com/ipfs/go-log/v2" +) + +var log = logging.Logger("lp/web/apihelper") + +func OrHTTPFail(w http.ResponseWriter, err error) { + if err != nil { + w.WriteHeader(500) + _, _ = w.Write([]byte(err.Error())) + log.Errorw("http fail", "err", err, "stack", string(debug.Stack())) + panic(err) + } +} diff --git a/curiosrc/web/api/config/config.go b/curiosrc/web/api/config/config.go new file mode 100644 index 000000000..1e18e792f --- /dev/null +++ b/curiosrc/web/api/config/config.go @@ -0,0 +1,180 @@ +package config + +import ( + "bytes" + "context" + "encoding/json" + "net/http" + "reflect" + "time" + + "github.com/BurntSushi/toml" + "github.com/gorilla/mux" + "github.com/invopop/jsonschema" + + "github.com/filecoin-project/lotus/cmd/curio/deps" + "github.com/filecoin-project/lotus/curiosrc/web/api/apihelper" + "github.com/filecoin-project/lotus/node/config" +) + +type cfg struct { + *deps.Deps +} + +func Routes(r *mux.Router, deps *deps.Deps) { + c := &cfg{deps} + // At menu.html: + r.Methods("GET").Path("/layers").HandlerFunc(c.getLayers) + r.Methods("GET").Path("/topo").HandlerFunc(c.topo) + + // At edit.html: + r.Methods("GET").Path("/schema").HandlerFunc(getSch) + r.Methods("GET").Path("/layers/{layer}").HandlerFunc(c.getLayer) + r.Methods("POST").Path("/layers/{layer}").HandlerFunc(c.setLayer) + r.Methods("GET").Path("/default").HandlerFunc(c.def) +} +func getSch(w http.ResponseWriter, r *http.Request) { + ref := jsonschema.Reflector{ + Mapper: func(i reflect.Type) *jsonschema.Schema { + if i == reflect.TypeOf(config.Duration(time.Second)) { + return &jsonschema.Schema{ + Type: "string", + Format: "duration", + } + } + return nil + }, + } + sch := ref.Reflect(config.CurioConfig{}) + //sch := jsonschema.Reflect(config.CurioConfig{}) + // add comments + for k, doc := range config.Doc { + item, ok := sch.Definitions[k] + if !ok { + continue + } + for _, line := range doc { + item, ok := item.Properties.Get(line.Name) + if !ok { + continue + } + if line.Comment != "" { + extra := make(map[string]any) + type options struct { + InfoText string `json:"infoText"` + } + opt := options{ + InfoText: line.Comment, + } + extra["options"] = opt + item.Extras = extra + } + } + } + + var allOpt func(s *jsonschema.Schema) + allOpt = func(s *jsonschema.Schema) { + s.Required = []string{} + for _, v := range s.Definitions { + v.Required = []string{} + + allOpt(v) + } + } + allOpt(sch) + + apihelper.OrHTTPFail(w, json.NewEncoder(w).Encode(sch)) +} + +func (c *cfg) getLayers(w http.ResponseWriter, r *http.Request) { + var layers []string + apihelper.OrHTTPFail(w, c.DB.Select(context.Background(), &layers, `SELECT title FROM harmony_config ORDER BY title`)) + apihelper.OrHTTPFail(w, json.NewEncoder(w).Encode(layers)) +} + +func (c *cfg) getLayer(w http.ResponseWriter, r *http.Request) { + var layer string + apihelper.OrHTTPFail(w, c.DB.QueryRow(context.Background(), `SELECT config FROM harmony_config WHERE title = $1`, mux.Vars(r)["layer"]).Scan(&layer)) + + // Read the TOML into a struct + configStruct := map[string]any{} // NOT CurioConfig b/c we want to preserve unsets + _, err := toml.Decode(layer, &configStruct) + apihelper.OrHTTPFail(w, err) + + // Encode the struct as JSON + jsonData, err := json.Marshal(configStruct) + apihelper.OrHTTPFail(w, err) + + // Write the JSON response + w.Header().Set("Content-Type", "application/json") + _, err = w.Write(jsonData) + apihelper.OrHTTPFail(w, err) +} + +func (c *cfg) setLayer(w http.ResponseWriter, r *http.Request) { + layer := mux.Vars(r)["layer"] + var configStruct map[string]any + dec := json.NewDecoder(r.Body) + dec.UseNumber() // JSON lib by default treats number is float64() + apihelper.OrHTTPFail(w, dec.Decode(&configStruct)) + + //Encode the struct as TOML + var tomlData bytes.Buffer + err := toml.NewEncoder(&tomlData).Encode(configStruct) + apihelper.OrHTTPFail(w, err) + + configStr := tomlData.String() + + // Generate a full commented string if this is base layer + if layer == "base" { + // Parse the into CurioConfig TOML + curioCfg := config.DefaultCurioConfig() + _, err = deps.LoadConfigWithUpgrades(tomlData.String(), curioCfg) + apihelper.OrHTTPFail(w, err) + cb, err := config.ConfigUpdate(curioCfg, config.DefaultCurioConfig(), config.Commented(true), config.DefaultKeepUncommented(), config.NoEnv()) + apihelper.OrHTTPFail(w, err) + configStr = string(cb) + } + + //Write the TOML to the database + _, err = c.DB.Exec(context.Background(), `INSERT INTO harmony_config (title, config) VALUES ($1, $2) ON CONFLICT (title) DO UPDATE SET config = $2`, layer, configStr) + apihelper.OrHTTPFail(w, err) +} + +func (c *cfg) topo(w http.ResponseWriter, r *http.Request) { + var topology []struct { + Server string `db:"server"` + CPU int `db:"cpu"` + GPU int `db:"gpu"` + RAM int `db:"ram"` + LayersCSV string `db:"layers"` + TasksCSV string `db:"tasks"` + } + apihelper.OrHTTPFail(w, c.DB.Select(context.Background(), &topology, ` + SELECT + m.host_and_port as server, + cpu, gpu, ram, layers, tasks + FROM harmony_machines m JOIN harmony_machine_details d ON m.id=d.machine_id + ORDER BY server`)) + w.Header().Set("Content-Type", "application/json") + apihelper.OrHTTPFail(w, json.NewEncoder(w).Encode(topology)) +} + +func (c *cfg) def(w http.ResponseWriter, r *http.Request) { + cb, err := config.ConfigUpdate(config.DefaultCurioConfig(), nil, config.Commented(false), config.DefaultKeepUncommented(), config.NoEnv()) + apihelper.OrHTTPFail(w, err) + + // Read the TOML into a struct + configStruct := map[string]any{} // NOT CurioConfig b/c we want to preserve unsets + _, err = toml.Decode(string(cb), &configStruct) + apihelper.OrHTTPFail(w, err) + + // Encode the struct as JSON + jsonData, err := json.Marshal(configStruct) + apihelper.OrHTTPFail(w, err) + + // Write the JSON response + w.Header().Set("Content-Type", "application/json") + _, err = w.Write(jsonData) + apihelper.OrHTTPFail(w, err) +} diff --git a/curiosrc/web/api/routes.go b/curiosrc/web/api/routes.go index c030f7d53..6b450055e 100644 --- a/curiosrc/web/api/routes.go +++ b/curiosrc/web/api/routes.go @@ -5,9 +5,11 @@ import ( "github.com/gorilla/mux" "github.com/filecoin-project/lotus/cmd/curio/deps" + "github.com/filecoin-project/lotus/curiosrc/web/api/config" "github.com/filecoin-project/lotus/curiosrc/web/api/debug" ) func Routes(r *mux.Router, deps *deps.Deps) { debug.Routes(r.PathPrefix("/debug").Subrouter(), deps) + config.Routes(r.PathPrefix("/config").Subrouter(), deps) } diff --git a/curiosrc/web/hapi/watch_actor.go b/curiosrc/web/hapi/watch_actor.go index a65608f14..c44dcd779 100644 --- a/curiosrc/web/hapi/watch_actor.go +++ b/curiosrc/web/hapi/watch_actor.go @@ -162,6 +162,8 @@ func (a *app) updateActor(ctx context.Context) error { return xerrors.Errorf("getting worker balance: %w", err) } + sort.Strings(cnames) + actorInfos = append(actorInfos, actorInfo{ Address: addr.String(), CLayers: cnames, diff --git a/curiosrc/web/hapi/web/root.gohtml b/curiosrc/web/hapi/web/root.gohtml index b3a41e800..562bbc209 100644 --- a/curiosrc/web/hapi/web/root.gohtml +++ b/curiosrc/web/hapi/web/root.gohtml @@ -13,9 +13,6 @@

{{.PageTitle}}

-
- version [todo] -

diff --git a/curiosrc/web/static/chain-connectivity.js b/curiosrc/web/static/chain-connectivity.mjs similarity index 100% rename from curiosrc/web/static/chain-connectivity.js rename to curiosrc/web/static/chain-connectivity.mjs diff --git a/curiosrc/web/static/config/edit.html b/curiosrc/web/static/config/edit.html new file mode 100644 index 000000000..37bc8f093 --- /dev/null +++ b/curiosrc/web/static/config/edit.html @@ -0,0 +1,150 @@ + + + + JSON Schema Editor + + + + + + + + + +
+
+
+
+ +
+ +
+ + +
+ +
+ +
+
+
+ + diff --git a/curiosrc/web/static/config/index.html b/curiosrc/web/static/config/index.html new file mode 100644 index 000000000..e93d0787c --- /dev/null +++ b/curiosrc/web/static/config/index.html @@ -0,0 +1,89 @@ + + + + + Configuration Editor + + + + + + + + +

Configuration Editor

+

Click on a layer to edit its configuration

+ +
+ + + \ No newline at end of file diff --git a/curiosrc/web/static/index.html b/curiosrc/web/static/index.html index 04050ff6e..b86c48915 100644 --- a/curiosrc/web/static/index.html +++ b/curiosrc/web/static/index.html @@ -2,7 +2,7 @@ Curio Cluster Overview - + diff --git a/curiosrc/web/hapi/web/root.gohtml b/curiosrc/web/hapi/web/root.gohtml index 562bbc209..114db6462 100644 --- a/curiosrc/web/hapi/web/root.gohtml +++ b/curiosrc/web/hapi/web/root.gohtml @@ -3,12 +3,14 @@ {{.PageTitle}} - + + +

{{.PageTitle}}

@@ -18,6 +20,7 @@
{{.Content}}
+ {{end}} diff --git a/curiosrc/web/hapi/web/sector_info.gohtml b/curiosrc/web/hapi/web/sector_info.gohtml new file mode 100644 index 000000000..afa96a923 --- /dev/null +++ b/curiosrc/web/hapi/web/sector_info.gohtml @@ -0,0 +1,57 @@ +{{define "sector_info"}} +

Sector {{.SectorNumber}}

+
+

PoRep Pipeline

+ {{template "sector_porep_state" .PipelinePoRep}} +
+
+

Storage

+
+ + + + + + + {{range .Locations}} + + {{if .PathType}} + + {{end}} + {{if .FileType}} + + {{end}} + + + + {{range $i, $loc := .Locations}} + {{if gt $i 0}} + + + + + {{end}} + {{end}} + {{end}} +
Path TypeFile TypePath IDHost
{{.PathType}}{{.FileType}}{{(index .Locations 0).StorageID}}{{range (index .Locations 0).Urls}}

{{.}}

{{end}}
{{$loc.StorageID}}{{range $loc.Urls}}

{{.}}

{{end}}
+
+
+

Tasks

+ + + + + + + + {{range .Tasks}} + + + + + + + {{end}} +
Task TypeTask IDPostedWorker
{{.Name}}{{.ID}}{{.SincePosted}}{{if ne nil .OwnerID}}{{.Owner}}{{end}}
+
+{{end}} diff --git a/curiosrc/web/srv.go b/curiosrc/web/srv.go index b4992dc80..b16a9f9af 100644 --- a/curiosrc/web/srv.go +++ b/curiosrc/web/srv.go @@ -29,7 +29,7 @@ var basePath = "/static/" // An dev mode hack for no-restart changes to static and templates. // You still need to recomplie the binary for changes to go code. -var webDev = os.Getenv("LOTUS_WEB_DEV") == "1" +var webDev = os.Getenv("CURIO_WEB_DEV") == "1" func GetSrv(ctx context.Context, deps *deps.Deps) (*http.Server, error) { mx := mux.NewRouter() diff --git a/curiosrc/web/static/chain-connectivity.mjs b/curiosrc/web/static/chain-connectivity.mjs index 8dc15de5d..bf4c80f04 100644 --- a/curiosrc/web/static/chain-connectivity.mjs +++ b/curiosrc/web/static/chain-connectivity.mjs @@ -22,19 +22,6 @@ window.customElements.define('chain-connectivity', class MyElement extends LitEl :host { box-sizing: border-box; /* Don't forgert this to include padding/border inside width calculation */ } - table { - border-collapse: collapse; - } - - table td, table th { - border-left: 1px solid #f0f0f0; - padding: 1px 5px; - } - - table tr td:first-child, table tr th:first-child { - border-left: none; - } - .success { color: green; } @@ -47,7 +34,9 @@ window.customElements.define('chain-connectivity', class MyElement extends LitEl `]; } render = () => html` - + + +
@@ -60,8 +49,8 @@ window.customElements.define('chain-connectivity', class MyElement extends LitEl ${this.data.map(item => html` - - + + `)} diff --git a/curiosrc/web/static/config/edit.html b/curiosrc/web/static/config/edit.html index 37bc8f093..2802316a9 100644 --- a/curiosrc/web/static/config/edit.html +++ b/curiosrc/web/static/config/edit.html @@ -2,13 +2,13 @@ JSON Schema Editor + - - + + /* Fix for dark mode */ + .card.bg-light { + background-color: rgb(11, 22, 34) !important; + } + input.form-control { + background: #111; + color: white; + font-weight: bold; + } + +
@@ -145,6 +155,6 @@
-
+
diff --git a/curiosrc/web/static/config/index.html b/curiosrc/web/static/config/index.html index e93d0787c..769929e53 100644 --- a/curiosrc/web/static/config/index.html +++ b/curiosrc/web/static/config/index.html @@ -38,7 +38,7 @@
RPC Address
${item.Address}${item.Reachable ? html`ok` : html`FAIL`}${item.SyncState === "ok" ? html`ok` : html`${item.SyncState}`}${item.Reachable ? html`ok` : html`FAIL`}${item.SyncState === "ok" ? html`ok` : html`${item.SyncState}`} ${item.Version}
${this.layers.map((layer, index) => html` - +
Used By: ${(f=> f.length?f.map(topo => html`${topo.Server}`):'-')( this.topo.filter(topo => topo.LayersCSV.split(",").includes(layer)))} @@ -80,9 +80,17 @@ }); -

Configuration Editor

-

Click on a layer to edit its configuration

- +
+
+
+
+

Configuration Editor

+

Click on a layer to edit its configuration

+ +
+
+
+
diff --git a/curiosrc/web/static/index.html b/curiosrc/web/static/index.html index b86c48915..0d734d6b7 100644 --- a/curiosrc/web/static/index.html +++ b/curiosrc/web/static/index.html @@ -1,10 +1,12 @@ - + + Curio Cluster Overview - + - - + - -
- -
-

Curio Cluster

-
-
- version [todo] -
-
-
-
-
-

Chain Connectivity

- -
-
-
-

Cluster Machines

- - - - - - - - - - - -
HostIDLast ContactTasks (24h)
-
-
-
-

PoRep Pipeline

- - - - - - - - - - - - - - - - -
AddressSDRTreesPrecommit MsgWait SeedPoRepCommit MsgDoneFailed
-
-
-
-

Actor Summary

- - - - - - - - - - - - - - - -
AddressConfig Layers AvailableQaPDeadlinesBalanceAvailableWorkerWins
-
-
-
-

Recently Finished Tasks

- - - - - - - - - - - - - - - - -
NameIDExecutorPostedStartQueuedTookOutcomeMessage
-
-
-
-

Cluster Tasks

- - - - - - - - - - - -
TaskIDPostedOwner
-
-
+ + +
+
+
+
+

Chain Connectivity

+ +
+
+
+
+
+ +
+
+
+

Cluster Machines

+ + + + + + + + + + + +
HostIDLast ContactTasks (24h)
+
+
+
+
+
+ +
+
+
+

PoRep Pipeline

+ + + + + + + + + + + + + + + + +
AddressSDRTreesPrecommit MsgWait SeedPoRepCommit MsgDoneFailed
+
+
+
+
+
+ +
+
+
+

Actor Summary

+ + + + + + + + + + + + + + + +
AddressConfig Layers AvailableQaPDeadlinesBalanceAvailableWorkerWins
+
+
+
+
+
+ + +
+
+
+

Recently Finished Tasks

+ + + + + + + + + + + + + + + + +
NameIDExecutorPostedStartQueuedTookOutcomeMessage
+
+
+
+
+
+ +
+
+
+

Cluster Tasks

+ + + + + + + + + + + +
TaskIDPostedOwner
+
+
+
+
+
+
+
+ \ No newline at end of file diff --git a/curiosrc/web/static/main.css b/curiosrc/web/static/main.css deleted file mode 100644 index 76b90d0b5..000000000 --- a/curiosrc/web/static/main.css +++ /dev/null @@ -1,120 +0,0 @@ -html, body { - background: #0f0f0f; - color: #ffffff; - padding: 0; - margin: 0; - - font-family: 'Hack', monospace; -} - -table td, table th { - font-size: 13px; -} - -.app-head { - width: 100%; -} -.head-left { - display: inline-block; -} -.head-right { - display: inline-block; - float: right; -} - -table { - border-collapse: collapse; -} - -table td, table th { - border-left: 1px solid #f0f0f0; - padding: 1px 5px; -} - -table tr td:first-child, table tr th:first-child { - border-left: none; -} - -a { - text-decoration: none; -} - -a:link, a:visited { - color: #adf7ad; -} - -a:hover { - color: #88cc60; -} - -.success { - color: greenyellow; -} -.warning { - color: yellow; -} -.error { - color: red; -} - -.dash-tile { - display: flex; - flex-direction: column; - padding: 0.75rem; - background: #3f3f3f; - - & b { - padding-bottom: 0.5rem; - color: deeppink; - } -} - -/* Path: curiosrc/web/hapi/web/pipeline_porep_sectors.gohtml */ -.porep-pipeline-table, .porep-state { - color: #d0d0d0; -} - -.porep-pipeline-table td, .porep-pipeline-table th { - border-left: none; - border-collapse: collapse; -} - -.porep-pipeline-table tr:nth-child(odd) { - border-top: 6px solid #999999; - -} - -.porep-pipeline-table tr:first-child, .porep-pipeline-table tr:first-child { - border-top: none; -} - -.porep-state { - border-collapse: collapse; -} - -.porep-state td, .porep-state th { - border-left: 1px solid #f0f0f0; - border-right: 1px solid #f0f0f0; - - padding: 1px 5px; - - text-align: center; - font-size: 0.7em; -} - -.porep-state tr { - border-top: 1px solid #f0f0f0; -} -.porep-state tr:first-child { - border-top: none; -} - -.pipeline-active { - background-color: #303060; -} -.pipeline-success { - background-color: #306030; -} -.pipeline-failed { - background-color: #603030; -} diff --git a/curiosrc/web/static/pipeline_porep.html b/curiosrc/web/static/pipeline_porep.html index d596163b7..c609aea6f 100644 --- a/curiosrc/web/static/pipeline_porep.html +++ b/curiosrc/web/static/pipeline_porep.html @@ -2,11 +2,71 @@ Lotus Provider PoRep Pipeline - - + + + + - + +

Lotus Provider PoRep Pipeline

@@ -14,20 +74,25 @@

-
-

Sectors

- - - - - - - - - - -
Sector IDCreate TimeState
+
+
+
+

Sectors

+ + + + + + + + + + +
Sector IDCreate TimeState
+
+
+
-
+
\ No newline at end of file diff --git a/curiosrc/web/static/sector/index.html b/curiosrc/web/static/sector/index.html index e3abed9f4..9ac5559cd 100644 --- a/curiosrc/web/static/sector/index.html +++ b/curiosrc/web/static/sector/index.html @@ -119,7 +119,7 @@ rows: '%d rows selected', headerCheckbox: true, }, - scrollY: window.innerHeight - 150, + scrollY: window.innerHeight - 250, deferRender: true, scroller: true, }); diff --git a/curiosrc/web/static/ux/curio-ux.mjs b/curiosrc/web/static/ux/curio-ux.mjs index 6f8048a4e..0b883d1d7 100644 --- a/curiosrc/web/static/ux/curio-ux.mjs +++ b/curiosrc/web/static/ux/curio-ux.mjs @@ -1,38 +1,35 @@ import {LitElement, css, html} from 'https://cdn.jsdelivr.net/gh/lit/dist@3/all/lit-all.min.js'; +//import 'https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.esm.js'; + class CurioUX extends LitElement { static styles = css` - .curio-slot { +\ .curio-slot { } + :host { + display: block; + margin: 2px 3px; + } + `; connectedCallback() { super.connectedCallback(); - const links = [ - "https://unpkg.com/@cds/core/global.min.css", - "https://unpkg.com/@cds/city/css/bundles/default.min.css", - "https://unpkg.com/@cds/core/styles/theme.dark.min.css", - "https://unpkg.com/@clr/ui/clr-ui.min.css", - "https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" - ]; + //"https://unpkg.com/@cds/core/global.min.css", + //"https://unpkg.com/@cds/city/css/bundles/default.min.css", + //"https://unpkg.com/@cds/core/styles/theme.dark.min.css", + //"https://unpkg.com/@clr/ui/clr-ui.min.css", - const head = document.head; - links.forEach(link => { - const linkNode = document.createElement('link'); - linkNode.rel = 'stylesheet'; - linkNode.href = link; - head.appendChild(linkNode); - }); + document.head.innerHTML += ` + + + + + +` - var theme = document.createAttribute('cds-theme'); - theme.value = localStorage.getItem('theme') || 'dark'; - document.body.attributes.setNamedItem(theme); - - var cdsText = document.createAttribute('cds-text'); - cdsText.value = 'body'; - document.body.attributes.setNamedItem(cdsText); - - document.body.style.visibility = 'initial'; + document.documentElement.lang = 'en'; // how Bootstrap & DataTables expect dark mode declared. document.documentElement.classList.add('dark'); @@ -42,9 +39,42 @@ class CurioUX extends LitElement { render() { return html` -
- ${this.message? html`
${this.message}
`: html``} + + + ${this.message? html``: html``}
diff --git a/curiosrc/web/static/ux/main.css b/curiosrc/web/static/ux/main.css new file mode 100644 index 000000000..c73002ff6 --- /dev/null +++ b/curiosrc/web/static/ux/main.css @@ -0,0 +1,53 @@ +@import url('https://fonts.cdnfonts.com/css/metropolis-2'); + +html { + min-height: 100vh; + background: rgb(11, 22, 34); + padding: 0; +} +body { + margin: 0; + padding: 3px 4px; + background: rgb(11, 22, 34); +} +curio-ux { + /* To resemble Clarity Design */ + color: rgb(227, 234, 237); + font-family: Metropolis, monospace; + font-weight: 400; + background: rgb(11, 22, 34); +} + + +.app-head { + width: 100%; +} +.head-left { + display: inline-block; +} +.head-right { + display: inline-block; + float: right; +} + +a { + text-decoration: none; +} + +a:link, a:visited { + color: #adf7ad; +} + +a:hover { + color: #88cc60; +} + +.success { + color: greenyellow; +} +.warning { + color: yellow; +} +.error { + color: red; +} From 06761db7bf02b3afe7948df3cb6d0b89da9ad220 Mon Sep 17 00:00:00 2001 From: parthshah1 Date: Sat, 20 Apr 2024 00:58:17 -0700 Subject: [PATCH 374/394] Fixing dead links (#11907) --- scripts/snap-lotus-entrypoint.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/snap-lotus-entrypoint.sh b/scripts/snap-lotus-entrypoint.sh index a3ab04c5b..68498da4a 100755 --- a/scripts/snap-lotus-entrypoint.sh +++ b/scripts/snap-lotus-entrypoint.sh @@ -1,4 +1,4 @@ -LOTUS_IMPORT_SNAPSHOT="https://fil-chain-snapshots-fallback.s3.amazonaws.com/mainnet/minimal_finality_stateroots_latest.car" +LOTUS_IMPORT_SNAPSHOT="https://forest-archive.chainsafe.dev/latest/mainnet/" LOTUS_BINARY=$(dirname "$0")/lotus GATE="$LOTUS_PATH"/date_initialized if [ ! -f "$GATE" ]; then From 70e0767acf0b20a66123bda65c20a5c87309643e Mon Sep 17 00:00:00 2001 From: Piotr Galar Date: Mon, 22 Apr 2024 11:50:55 +0200 Subject: [PATCH 375/394] ci: ci: create gh workflow that updates sorted pr checks (#11861) * ci: create gh workflow that updates sorted pr checks * ci: use grouped_by_result template for pr checks sticky comment * chore: apply pr review suggestion --- .github/workflows/sorted-pr-checks.yml | 32 ++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 .github/workflows/sorted-pr-checks.yml diff --git a/.github/workflows/sorted-pr-checks.yml b/.github/workflows/sorted-pr-checks.yml new file mode 100644 index 000000000..0c2b0ee8a --- /dev/null +++ b/.github/workflows/sorted-pr-checks.yml @@ -0,0 +1,32 @@ +name: Comment with sorted PR checks + +on: + workflow_dispatch: + inputs: + pull_number: + description: 'Pull request number' + required: true + workflow_run: + workflows: + - Build + - Check + - CodeQL + - Test + types: + - requested + - completed + +permissions: + pull-requests: write + +concurrency: + group: ${{ github.workflow }}-${{ github.event.inputs.pull_number || github.event.workflow_run.pull_requests[0].number || 'unknown' }} + cancel-in-progress: true + +jobs: + comment: + if: github.event.inputs.pull_number || github.event.workflow_run.pull_requests[0] + uses: ipdxco/sorted-pr-checks/.github/workflows/comment.yml@v1 + with: + pull_number: ${{ github.event.inputs.pull_number || github.event.workflow_run.pull_requests[0].number }} + template: unsuccessful_only From 95b6483ad9cfe3e14dc5f6fa02552ee41f8776ba Mon Sep 17 00:00:00 2001 From: ZenGround0 <5515260+ZenGround0@users.noreply.github.com> Date: Mon, 22 Apr 2024 20:01:26 -0400 Subject: [PATCH 376/394] Avoid cfg lookup on chain remove since unenabled splitstore delete is noop anyway (#11916) Co-authored-by: zenground0 --- cmd/lotus/daemon.go | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/cmd/lotus/daemon.go b/cmd/lotus/daemon.go index aeb703432..369938020 100644 --- a/cmd/lotus/daemon.go +++ b/cmd/lotus/daemon.go @@ -661,22 +661,10 @@ func removeExistingChain(cctx *cli.Context, lr repo.Repo) error { } }() - cfg, err := lockedRepo.Config() + log.Info("removing splitstore directory...") + err = deleteSplitstoreDir(lockedRepo) if err != nil { - return xerrors.Errorf("error getting config: %w", err) - } - - fullNodeConfig, ok := cfg.(*config.FullNode) - if !ok { - return xerrors.Errorf("wrong config type: %T", cfg) - } - - if fullNodeConfig.Chainstore.EnableSplitstore { - log.Info("removing splitstore directory...") - err = deleteSplitstoreDir(lockedRepo) - if err != nil { - return xerrors.Errorf("error removing splitstore directory: %w", err) - } + return xerrors.Errorf("error removing splitstore directory: %w", err) } // Get the base repo path From 772f6a38c5a691c13b180552d52cd11f611495cd Mon Sep 17 00:00:00 2001 From: forcedebug <167591285+forcedebug@users.noreply.github.com> Date: Tue, 23 Apr 2024 08:56:43 +0800 Subject: [PATCH 377/394] Fix mismatched method names in comments (#11913) Signed-off-by: forcedebug --- cmd/lotus-sim/simulation/mock/mock.go | 2 +- cmd/lotus-sim/simulation/stages/commit_queue.go | 2 +- cmd/lotus-sim/simulation/stages/precommit_stage.go | 2 +- itests/eth_hash_lookup_test.go | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cmd/lotus-sim/simulation/mock/mock.go b/cmd/lotus-sim/simulation/mock/mock.go index 4699b2aa6..002524070 100644 --- a/cmd/lotus-sim/simulation/mock/mock.go +++ b/cmd/lotus-sim/simulation/mock/mock.go @@ -149,7 +149,7 @@ func MockWindowPoStProof(proofType abi.RegisteredPoStProof, minerAddr address.Ad return proof, nil } -// makeCommR generates a "fake" but valid CommR for a sector. It is unique for the given sector/miner. +// MockCommR generates a "fake" but valid CommR for a sector. It is unique for the given sector/miner. func MockCommR(minerAddr address.Address, sno abi.SectorNumber) cid.Cid { return tutils.MakeCID(fmt.Sprintf("%s:%d", minerAddr, sno), &miner5.SealedCIDPrefix) } diff --git a/cmd/lotus-sim/simulation/stages/commit_queue.go b/cmd/lotus-sim/simulation/stages/commit_queue.go index 60cbfa4ba..f68dc67ed 100644 --- a/cmd/lotus-sim/simulation/stages/commit_queue.go +++ b/cmd/lotus-sim/simulation/stages/commit_queue.go @@ -165,7 +165,7 @@ func (q *commitQueue) advanceEpoch(epoch abi.ChainEpoch) { }) } -// enquueProveCommit enqueues prove-commit for the given pre-commit for the given miner. +// enqueueProveCommit enqueues prove-commit for the given pre-commit for the given miner. func (q *commitQueue) enqueueProveCommit(addr address.Address, preCommitEpoch abi.ChainEpoch, info minertypes.SectorPreCommitInfo) error { // Compute the epoch at which we can start trying to commit. preCommitDelay := policy.GetPreCommitChallengeDelay() diff --git a/cmd/lotus-sim/simulation/stages/precommit_stage.go b/cmd/lotus-sim/simulation/stages/precommit_stage.go index 1a89413d7..a47ec2a6e 100644 --- a/cmd/lotus-sim/simulation/stages/precommit_stage.go +++ b/cmd/lotus-sim/simulation/stages/precommit_stage.go @@ -54,7 +54,7 @@ func (*PreCommitStage) Name() string { return "pre-commit" } -// packPreCommits packs pre-commit messages until the block is full. +// PackMessages packs pre-commit messages until the block is full. func (stage *PreCommitStage) PackMessages(ctx context.Context, bb *blockbuilder.BlockBuilder) (_err error) { if !stage.initialized { if err := stage.load(ctx, bb); err != nil { diff --git a/itests/eth_hash_lookup_test.go b/itests/eth_hash_lookup_test.go index 0a2c11d3c..5edd61d4e 100644 --- a/itests/eth_hash_lookup_test.go +++ b/itests/eth_hash_lookup_test.go @@ -296,7 +296,7 @@ func TestTransactionHashLookupSecpFilecoinMessage(t *testing.T) { require.Equal(t, ethtypes.EthBytes(expected), chainTx.Input) } -// TestTransactionHashLookupSecpFilecoinMessage tests to see if lotus can find a Secp Filecoin Message using the transaction hash +// TestTransactionHashLookupNonexistentMessage tests to see if lotus can find a Secp Filecoin Message using the transaction hash func TestTransactionHashLookupNonexistentMessage(t *testing.T) { kit.QuietMiningLogs() From d23ea7665b0e01be271238d4bbc9e90193874c1f Mon Sep 17 00:00:00 2001 From: Jiaying Wang <42981373+jennijuju@users.noreply.github.com> Date: Tue, 23 Apr 2024 23:04:32 -0400 Subject: [PATCH 378/394] release: v1.26.3 (#11908) (#11915) (#11922) * deps: update dependencies to address migration memory bloat to address memory concerns during a heavy migration Ref: https://github.com/filecoin-project/go-state-types/pull/260 Ref: https://github.com/whyrusleeping/cbor-gen/pull/96 Ref: https://github.com/filecoin-project/go-amt-ipld/pull/90 * release: prep v1.26.3 patch Prep v1.26.3 patch release: - Update changelog, version and make gen + make docsgen-cli * deps: update cbor-gen to tagged version deps: update cbor-gen to tagged version * deps: update go-state-types to tagged version deps: update go-state-types to tagged version v0.13.2 * chore: deps: update go-state-types to v0.13.3 Fixes a panic when we have fewer than 1k proposals. --------- Co-authored-by: Phi-rjan Co-authored-by: Rod Vagg Co-authored-by: Steven Allen --- CHANGELOG.md | 6 + build/openrpc/full.json.gz | Bin 35935 -> 35935 bytes build/openrpc/gateway.json.gz | Bin 12258 -> 12258 bytes build/openrpc/miner.json.gz | Bin 16475 -> 16475 bytes build/openrpc/worker.json.gz | Bin 6049 -> 6048 bytes documentation/en/cli-lotus-provider.md | 410 +++++++++++++++++++++++++ go.mod | 6 +- go.sum | 12 +- 8 files changed, 425 insertions(+), 9 deletions(-) create mode 100644 documentation/en/cli-lotus-provider.md diff --git a/CHANGELOG.md b/CHANGELOG.md index e7dba87a5..1c14c5344 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,12 @@ ## Improvements +# v1.26.3 / 2024-04-22 + +**This is a patch release that addresses high memory load concerns for the Lotus daemon in the coming network migration for network version 22, scheduled on epoch `3855360 - 2024-04-24 - 14:00:00Z`.** + +If your Lotus daemon is running on a machine with less memory and swap than 160GB, you should upgrade to this patch release to ensure you do not encounter any Out-Of-Memory issues during the pre-migration. + # v1.26.2 / 2024-04-08 **This is a mandatory patch release for the Filecoin network version 22 mainnet upgrade, for all node operators.** diff --git a/build/openrpc/full.json.gz b/build/openrpc/full.json.gz index baaf830238c024f8118746efa5064f6a537e08a8..08dda7784c666f634566edc12e0a9ce7f8908c37 100644 GIT binary patch delta 23 fcmcaVgX#VZrU@O4i#K+j>*i=sR+W1w%gz7*frbe+ delta 23 fcmcaVgX#VZrU@O4hc|Yf>*gr`|1##GEIR`Lj?fD# diff --git a/build/openrpc/gateway.json.gz b/build/openrpc/gateway.json.gz index 9c2dfbcf2a23bf48df7a05d645fc7c8b0187be02..9f5dc38ee6814d310fb0e17310fa4f2428409c80 100644 GIT binary patch delta 22 ecmaD9|0sS!GgEfL#@0}Mj&IX;bj8kLVgLYrkO}Sp delta 22 ecmaD9|0sS!Gt=t)jjf^j9FCT&?#IqyVgLYpLL diff --git a/build/openrpc/miner.json.gz b/build/openrpc/miner.json.gz index ba135c01577aa0bd16db70857c2b4c172bf43fc0..5fbd42c3db8b33d8058ebe13972b3c96cd51f997 100644 GIT binary patch delta 23 ecmccJz<9fXaY75D^2XM4_8fj;)wb{5nHd0el?c%Q delta 23 fcmccJz<9fXaY75D^TyV5_8cuo3g5hUXJ!BZe>Ms_ diff --git a/build/openrpc/worker.json.gz b/build/openrpc/worker.json.gz index 58231030637b043f21a2a1a365b6c916da936cc7..0f3cb11c7a3140dc8227754c0ab7a27965626b96 100644 GIT binary patch literal 6048 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 aQBSD`W4K)2F8@CO0RR7_KxV9MSOEZJF2+Ov literal 6049 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 E50#V7RsaA1 diff --git a/documentation/en/cli-lotus-provider.md b/documentation/en/cli-lotus-provider.md new file mode 100644 index 000000000..0fae817d2 --- /dev/null +++ b/documentation/en/cli-lotus-provider.md @@ -0,0 +1,410 @@ +# lotus-provider +``` +NAME: + lotus-provider - Filecoin decentralized storage network provider + +USAGE: + lotus-provider [global options] command [command options] [arguments...] + +VERSION: + 1.26.3 + +COMMANDS: + run Start a lotus provider process + stop Stop a running lotus provider + config Manage node config by layers. The layer 'base' will always be applied. + test Utility functions for testing + version Print version + help, h Shows a list of commands or help for one command + DEVELOPER: + auth Manage RPC permissions + log Manage logging + wait-api Wait for lotus api to come online + fetch-params Fetch proving parameters + +GLOBAL OPTIONS: + --color use color in display output (default: depends on output being a TTY) + --db-host value Command separated list of hostnames for yugabyte cluster (default: "yugabyte") [$LOTUS_DB_HOST] + --db-name value (default: "yugabyte") [$LOTUS_DB_NAME, $LOTUS_HARMONYDB_HOSTS] + --db-user value (default: "yugabyte") [$LOTUS_DB_USER, $LOTUS_HARMONYDB_USERNAME] + --db-password value (default: "yugabyte") [$LOTUS_DB_PASSWORD, $LOTUS_HARMONYDB_PASSWORD] + --layers value (default: "base") [$LOTUS_LAYERS, $LOTUS_CONFIG_LAYERS] + --repo-path value (default: "~/.lotusprovider") [$LOTUS_REPO_PATH] + --vv enables very verbose mode, useful for debugging the CLI (default: false) + --help, -h show help + --version, -v print the version +``` + +## lotus-provider run +``` +NAME: + lotus-provider run - Start a lotus provider process + +USAGE: + lotus-provider run [command options] [arguments...] + +OPTIONS: + --listen value host address and port the worker api will listen on (default: "0.0.0.0:12300") [$LOTUS_WORKER_LISTEN] + --nosync don't check full-node sync status (default: false) + --manage-fdlimit manage open file limit (default: true) + --layers value [ --layers value ] list of layers to be interpreted (atop defaults). Default: base (default: "base") + --storage-json value path to json file containing storage config (default: "~/.lotus-provider/storage.json") + --journal value path to journal files (default: "~/.lotus-provider/") + --help, -h show help +``` + +## lotus-provider stop +``` +NAME: + lotus-provider stop - Stop a running lotus provider + +USAGE: + lotus-provider stop [command options] [arguments...] + +OPTIONS: + --help, -h show help +``` + +## lotus-provider config +``` +NAME: + lotus-provider config - Manage node config by layers. The layer 'base' will always be applied. + +USAGE: + lotus-provider config command [command options] [arguments...] + +COMMANDS: + default, defaults Print default node config + set, add, update, create Set a config layer or the base by providing a filename or stdin. + get, cat, show Get a config layer by name. You may want to pipe the output to a file, or use 'less' + list, ls List config layers you can get. + interpret, view, stacked, stack Interpret stacked config layers by this version of lotus-provider, with system-generated comments. + remove, rm, del, delete Remove a named config layer. + from-miner Express a database config (for lotus-provider) from an existing miner. + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help +``` + +### lotus-provider config default +``` +NAME: + lotus-provider config default - Print default node config + +USAGE: + lotus-provider config default [command options] [arguments...] + +OPTIONS: + --no-comment don't comment default values (default: false) + --help, -h show help +``` + +### lotus-provider config set +``` +NAME: + lotus-provider config set - Set a config layer or the base by providing a filename or stdin. + +USAGE: + lotus-provider config set [command options] a layer's file name + +OPTIONS: + --title value title of the config layer (req'd for stdin) + --help, -h show help +``` + +### lotus-provider config get +``` +NAME: + lotus-provider config get - Get a config layer by name. You may want to pipe the output to a file, or use 'less' + +USAGE: + lotus-provider config get [command options] layer name + +OPTIONS: + --help, -h show help +``` + +### lotus-provider config list +``` +NAME: + lotus-provider config list - List config layers you can get. + +USAGE: + lotus-provider config list [command options] [arguments...] + +OPTIONS: + --help, -h show help +``` + +### lotus-provider config interpret +``` +NAME: + lotus-provider config interpret - Interpret stacked config layers by this version of lotus-provider, with system-generated comments. + +USAGE: + lotus-provider config interpret [command options] a list of layers to be interpreted as the final config + +OPTIONS: + --layers value [ --layers value ] comma or space separated list of layers to be interpreted (default: "base") + --help, -h show help +``` + +### lotus-provider config remove +``` +NAME: + lotus-provider config remove - Remove a named config layer. + +USAGE: + lotus-provider config remove [command options] [arguments...] + +OPTIONS: + --help, -h show help +``` + +### lotus-provider config from-miner +``` +NAME: + lotus-provider config from-miner - Express a database config (for lotus-provider) from an existing miner. + +USAGE: + lotus-provider config from-miner [command options] [arguments...] + +DESCRIPTION: + Express a database config (for lotus-provider) from an existing miner. + +OPTIONS: + --miner-repo value, --storagerepo value Specify miner repo path. flag(storagerepo) and env(LOTUS_STORAGE_PATH) are DEPRECATION, will REMOVE SOON (default: "~/.lotusminer") [$LOTUS_MINER_PATH, $LOTUS_STORAGE_PATH] + --to-layer value, -t value The layer name for this data push. 'base' is recommended for single-miner setup. + --overwrite, -o Use this with --to-layer to replace an existing layer (default: false) + --help, -h show help +``` + +## lotus-provider test +``` +NAME: + lotus-provider test - Utility functions for testing + +USAGE: + lotus-provider test command [command options] [arguments...] + +COMMANDS: + window-post, wd, windowpost, wdpost Compute a proof-of-spacetime for a sector (requires the sector to be pre-sealed). These will not send to the chain. + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help +``` + +### lotus-provider test window-post +``` +NAME: + lotus-provider test window-post - Compute a proof-of-spacetime for a sector (requires the sector to be pre-sealed). These will not send to the chain. + +USAGE: + lotus-provider test window-post command [command options] [arguments...] + +COMMANDS: + here, cli Compute WindowPoSt for performance and configuration testing. + task, scheduled, schedule, async, asynchronous Test the windowpost scheduler by running it on the next available lotus-provider. + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help +``` + +#### lotus-provider test window-post here +``` +NAME: + lotus-provider test window-post here - Compute WindowPoSt for performance and configuration testing. + +USAGE: + lotus-provider test window-post here [command options] [deadline index] + +DESCRIPTION: + Note: This command is intended to be used to verify PoSt compute performance. + It will not send any messages to the chain. Since it can compute any deadline, output may be incorrectly timed for the chain. + +OPTIONS: + --deadline value deadline to compute WindowPoSt for (default: 0) + --layers value [ --layers value ] list of layers to be interpreted (atop defaults). Default: base (default: "base") + --storage-json value path to json file containing storage config (default: "~/.lotus-provider/storage.json") + --partition value partition to compute WindowPoSt for (default: 0) + --help, -h show help +``` + +#### lotus-provider test window-post task +``` +NAME: + lotus-provider test window-post task - Test the windowpost scheduler by running it on the next available lotus-provider. + +USAGE: + lotus-provider test window-post task [command options] [arguments...] + +OPTIONS: + --deadline value deadline to compute WindowPoSt for (default: 0) + --layers value [ --layers value ] list of layers to be interpreted (atop defaults). Default: base (default: "base") + --help, -h show help +``` + +## lotus-provider version +``` +NAME: + lotus-provider version - Print version + +USAGE: + lotus-provider version [command options] [arguments...] + +OPTIONS: + --help, -h show help +``` + +## lotus-provider auth +``` +NAME: + lotus-provider auth - Manage RPC permissions + +USAGE: + lotus-provider auth command [command options] [arguments...] + +COMMANDS: + create-token Create token + api-info Get token with API info required to connect to this node + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help +``` + +### lotus-provider auth create-token +``` +NAME: + lotus-provider auth create-token - Create token + +USAGE: + lotus-provider auth create-token [command options] [arguments...] + +OPTIONS: + --perm value permission to assign to the token, one of: read, write, sign, admin + --help, -h show help +``` + +### lotus-provider auth api-info +``` +NAME: + lotus-provider auth api-info - Get token with API info required to connect to this node + +USAGE: + lotus-provider auth api-info [command options] [arguments...] + +OPTIONS: + --perm value permission to assign to the token, one of: read, write, sign, admin + --help, -h show help +``` + +## lotus-provider log +``` +NAME: + lotus-provider log - Manage logging + +USAGE: + lotus-provider log command [command options] [arguments...] + +COMMANDS: + list List log systems + set-level Set log level + alerts Get alert states + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help +``` + +### lotus-provider log list +``` +NAME: + lotus-provider log list - List log systems + +USAGE: + lotus-provider log list [command options] [arguments...] + +OPTIONS: + --help, -h show help +``` + +### lotus-provider log set-level +``` +NAME: + lotus-provider log set-level - Set log level + +USAGE: + lotus-provider log set-level [command options] [level] + +DESCRIPTION: + Set the log level for logging systems: + + The system flag can be specified multiple times. + + eg) log set-level --system chain --system chainxchg debug + + Available Levels: + debug + info + warn + error + + Environment Variables: + GOLOG_LOG_LEVEL - Default log level for all log systems + GOLOG_LOG_FMT - Change output log format (json, nocolor) + GOLOG_FILE - Write logs to file + GOLOG_OUTPUT - Specify whether to output to file, stderr, stdout or a combination, i.e. file+stderr + + +OPTIONS: + --system value [ --system value ] limit to log system + --help, -h show help +``` + +### lotus-provider log alerts +``` +NAME: + lotus-provider log alerts - Get alert states + +USAGE: + lotus-provider log alerts [command options] [arguments...] + +OPTIONS: + --all get all (active and inactive) alerts (default: false) + --help, -h show help +``` + +## lotus-provider wait-api +``` +NAME: + lotus-provider wait-api - Wait for lotus api to come online + +USAGE: + lotus-provider wait-api [command options] [arguments...] + +CATEGORY: + DEVELOPER + +OPTIONS: + --timeout value duration to wait till fail (default: 30s) + --help, -h show help +``` + +## lotus-provider fetch-params +``` +NAME: + lotus-provider fetch-params - Fetch proving parameters + +USAGE: + lotus-provider fetch-params [command options] [sectorSize] + +CATEGORY: + DEVELOPER + +OPTIONS: + --help, -h show help +``` diff --git a/go.mod b/go.mod index 674148ec3..062aca298 100644 --- a/go.mod +++ b/go.mod @@ -35,7 +35,7 @@ require ( github.com/filecoin-project/dagstore v0.5.2 github.com/filecoin-project/filecoin-ffi v0.30.4-0.20220519234331-bfd1f5f9fe38 github.com/filecoin-project/go-address v1.1.0 - github.com/filecoin-project/go-amt-ipld/v4 v4.2.0 + github.com/filecoin-project/go-amt-ipld/v4 v4.3.0 github.com/filecoin-project/go-bitfield v0.2.4 github.com/filecoin-project/go-cbor-util v0.0.1 github.com/filecoin-project/go-commp-utils v0.1.3 @@ -49,7 +49,7 @@ require ( github.com/filecoin-project/go-jsonrpc v0.3.1 github.com/filecoin-project/go-padreader v0.0.1 github.com/filecoin-project/go-paramfetch v0.0.4 - github.com/filecoin-project/go-state-types v0.13.1 + github.com/filecoin-project/go-state-types v0.13.3 github.com/filecoin-project/go-statemachine v1.0.3 github.com/filecoin-project/go-statestore v0.2.0 github.com/filecoin-project/go-storedcounter v0.1.0 @@ -144,7 +144,7 @@ require ( github.com/triplewz/poseidon v0.0.0-20230828015038-79d8165c88ed github.com/urfave/cli/v2 v2.25.5 github.com/whyrusleeping/bencher v0.0.0-20190829221104-bb6607aa8bba - github.com/whyrusleeping/cbor-gen v0.1.0 + github.com/whyrusleeping/cbor-gen v0.1.1 github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7 github.com/xeipuuv/gojsonschema v1.2.0 github.com/xorcare/golden v0.6.1-0.20191112154924-b87f686d7542 diff --git a/go.sum b/go.sum index 4e6ce0dec..52755ba03 100644 --- a/go.sum +++ b/go.sum @@ -305,8 +305,8 @@ github.com/filecoin-project/go-amt-ipld/v3 v3.0.0/go.mod h1:Qa95YNAbtoVCTSVtX38a github.com/filecoin-project/go-amt-ipld/v3 v3.1.0 h1:ZNJ9tEG5bE72vBWYiuh5bkxJVM3ViHNOmQ7qew9n6RE= github.com/filecoin-project/go-amt-ipld/v3 v3.1.0/go.mod h1:UjM2QhDFrrjD5s1CdnkJkat4ga+LqZBZgTMniypABRo= github.com/filecoin-project/go-amt-ipld/v4 v4.0.0/go.mod h1:gF053YQ4BIpzTNDoEwHZas7U3oAwncDVGvOHyY8oDpE= -github.com/filecoin-project/go-amt-ipld/v4 v4.2.0 h1:DQTXQwMXxaetd+lhZGODjt5qC1WYT7tMAlYrWqI/fwI= -github.com/filecoin-project/go-amt-ipld/v4 v4.2.0/go.mod h1:0eDVF7pROvxrsxvLJx+SJZXqRaXXcEPUcgb/rG0zGU4= +github.com/filecoin-project/go-amt-ipld/v4 v4.3.0 h1:bY42N1gR0DqrLMCKUPzX1VhYVgXaETQm0Um4ohvyEP8= +github.com/filecoin-project/go-amt-ipld/v4 v4.3.0/go.mod h1:39Ep/yBbF6xN94WevLG9qSbglBJepHa5zeEbAE1pYsc= github.com/filecoin-project/go-bitfield v0.2.0/go.mod h1:CNl9WG8hgR5mttCnUErjcQjGvuiZjRqK9rHVBsQF4oM= github.com/filecoin-project/go-bitfield v0.2.3/go.mod h1:CNl9WG8hgR5mttCnUErjcQjGvuiZjRqK9rHVBsQF4oM= github.com/filecoin-project/go-bitfield v0.2.4 h1:uZ7MeE+XfM5lqrHJZ93OnhQKc/rveW8p9au0C68JPgk= @@ -355,8 +355,8 @@ github.com/filecoin-project/go-state-types v0.1.0/go.mod h1:ezYnPf0bNkTsDibL/psS github.com/filecoin-project/go-state-types v0.1.6/go.mod h1:UwGVoMsULoCK+bWjEdd/xLCvLAQFBC7EDT477SKml+Q= github.com/filecoin-project/go-state-types v0.1.10/go.mod h1:UwGVoMsULoCK+bWjEdd/xLCvLAQFBC7EDT477SKml+Q= github.com/filecoin-project/go-state-types v0.11.2-0.20230712101859-8f37624fa540/go.mod h1:SyNPwTsU7I22gL2r0OAPcImvLoTVfgRwdK/Y5rR1zz8= -github.com/filecoin-project/go-state-types v0.13.1 h1:4CivvlcHAIoAtFFVVlZtokynaMQu5XLXGoTKhQkfG1I= -github.com/filecoin-project/go-state-types v0.13.1/go.mod h1:cHpOPup9H1g2T29dKHAjC2sc7/Ef5ypjuW9A3I+e9yY= +github.com/filecoin-project/go-state-types v0.13.3 h1:9JPkC0E6HDtfHbaOIrFiCDzT/Z0jRTb9En4Y4Ci/b3w= +github.com/filecoin-project/go-state-types v0.13.3/go.mod h1:cHpOPup9H1g2T29dKHAjC2sc7/Ef5ypjuW9A3I+e9yY= github.com/filecoin-project/go-statemachine v0.0.0-20200925024713-05bd7c71fbfe/go.mod h1:FGwQgZAt2Gh5mjlwJUlVB62JeYdo+if0xWxSEfBD9ig= github.com/filecoin-project/go-statemachine v1.0.3 h1:N07o6alys+V1tNoSTi4WuuoeNC4erS/6jE74+NsgQuk= github.com/filecoin-project/go-statemachine v1.0.3/go.mod h1:jZdXXiHa61n4NmgWFG4w8tnqgvZVHYbJ3yW7+y8bF54= @@ -1681,8 +1681,8 @@ github.com/whyrusleeping/cbor-gen v0.0.0-20200826160007-0b9f6c5fb163/go.mod h1:f github.com/whyrusleeping/cbor-gen v0.0.0-20210118024343-169e9d70c0c2/go.mod h1:fgkXqYy7bV2cFeIEOkVTZS/WjXARfBqSH6Q2qHL33hQ= github.com/whyrusleeping/cbor-gen v0.0.0-20210303213153-67a261a1d291/go.mod h1:fgkXqYy7bV2cFeIEOkVTZS/WjXARfBqSH6Q2qHL33hQ= github.com/whyrusleeping/cbor-gen v0.0.0-20220323183124-98fa8256a799/go.mod h1:fgkXqYy7bV2cFeIEOkVTZS/WjXARfBqSH6Q2qHL33hQ= -github.com/whyrusleeping/cbor-gen v0.1.0 h1:Jneeq3V5enErVcuL0NKEbD1Gi+iOvEeFhXOV1S1Fc6g= -github.com/whyrusleeping/cbor-gen v0.1.0/go.mod h1:pM99HXyEbSQHcosHc0iW7YFmwnscr+t9Te4ibko05so= +github.com/whyrusleeping/cbor-gen v0.1.1 h1:eKfcJIoxivjMtwfCfmJAqSF56MHcWqyIScXwaC1VBgw= +github.com/whyrusleeping/cbor-gen v0.1.1/go.mod h1:pM99HXyEbSQHcosHc0iW7YFmwnscr+t9Te4ibko05so= github.com/whyrusleeping/chunker v0.0.0-20181014151217-fe64bd25879f h1:jQa4QT2UP9WYv2nzyawpKMOCl+Z/jW7djv2/J50lj9E= github.com/whyrusleeping/chunker v0.0.0-20181014151217-fe64bd25879f/go.mod h1:p9UJB6dDgdPgMJZs7UjUOdulKyRr9fqkS+6JKAInPy8= github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1 h1:EKhdznlJHPMoKr0XTrX+IlJs1LH3lyx2nfr1dOlZ79k= From f907354300baac92c30857b61b918d598365561c Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Tue, 23 Apr 2024 12:34:54 +0100 Subject: [PATCH 379/394] Refactor `LookupID*` APIs in `StateManager` and `StateTree` The naming of `LookupID` can cause confusion when resolving actor IDs vs ID addresses. To avoid this: * Refactor `StateTree` `LookupID` to `LookupIDAddress`, because it returns ID address. * Refactor `StateManager` `LookupID` to `LookupIDAddress` because it also returns ID address via a chain call to `StateTree`. * Introduce a new API `StateManager` dedicated to resolving address to actor ID, called `LookupID` which returns `abi.ActorID`. For context, see: * https://github.com/filecoin-project/lotus/pull/11723#discussion_r1534728607 --- chain/consensus/common.go | 2 +- chain/state/statetree.go | 16 ++++++++-------- chain/stmgr/actors.go | 2 +- chain/stmgr/rpc/rpcstatemanager.go | 2 +- chain/stmgr/searchwait.go | 2 +- chain/stmgr/stmgr.go | 23 ++++++++++++++++++++--- chain/store/messages.go | 6 +++--- chain/vm/runtime.go | 2 +- chain/vm/vm.go | 8 ++++---- itests/migration_test.go | 2 +- node/impl/full/eth_utils.go | 2 +- node/impl/full/state.go | 2 +- 12 files changed, 43 insertions(+), 26 deletions(-) diff --git a/chain/consensus/common.go b/chain/consensus/common.go index a7e5c40d2..8fee0d4c2 100644 --- a/chain/consensus/common.go +++ b/chain/consensus/common.go @@ -220,7 +220,7 @@ func checkBlockMessages(ctx context.Context, sm *stmgr.StateManager, cs *store.C // the sender exists and is an account actor, and the nonces make sense var sender address.Address if nv >= network.Version13 { - sender, err = st.LookupID(m.From) + sender, err = st.LookupIDAddress(m.From) if err != nil { return xerrors.Errorf("failed to lookup sender %s: %w", m.From, err) } diff --git a/chain/state/statetree.go b/chain/state/statetree.go index 1a6497d04..03cd98d95 100644 --- a/chain/state/statetree.go +++ b/chain/state/statetree.go @@ -230,7 +230,7 @@ func NewStateTree(cst cbor.IpldStore, ver types.StateTreeVersion) (*StateTree, e Store: cst, snaps: newStateSnaps(), } - s.lookupIDFun = s.lookupIDinternal + s.lookupIDFun = s.lookupInternalIDAddress return s, nil } @@ -302,13 +302,13 @@ func LoadStateTree(cst cbor.IpldStore, c cid.Cid) (*StateTree, error) { Store: cst, snaps: newStateSnaps(), } - s.lookupIDFun = s.lookupIDinternal + s.lookupIDFun = s.lookupInternalIDAddress return s, nil } func (st *StateTree) SetActor(addr address.Address, act *types.Actor) error { - iaddr, err := st.LookupID(addr) + iaddr, err := st.LookupIDAddress(addr) if err != nil { return xerrors.Errorf("ID lookup failed: %w", err) } @@ -318,7 +318,7 @@ func (st *StateTree) SetActor(addr address.Address, act *types.Actor) error { return nil } -func (st *StateTree) lookupIDinternal(addr address.Address) (address.Address, error) { +func (st *StateTree) lookupInternalIDAddress(addr address.Address) (address.Address, error) { act, err := st.GetActor(init_.Address) if err != nil { return address.Undef, xerrors.Errorf("getting init actor: %w", err) @@ -339,8 +339,8 @@ func (st *StateTree) lookupIDinternal(addr address.Address) (address.Address, er return a, err } -// LookupID gets the ID address of this actor's `addr` stored in the `InitActor`. -func (st *StateTree) LookupID(addr address.Address) (address.Address, error) { +// LookupIDAddress gets the ID address of this actor's `addr` stored in the `InitActor`. +func (st *StateTree) LookupIDAddress(addr address.Address) (address.Address, error) { if addr.Protocol() == address.ID { return addr, nil } @@ -366,7 +366,7 @@ func (st *StateTree) GetActor(addr address.Address) (*types.Actor, error) { } // Transform `addr` to its ID format. - iaddr, err := st.LookupID(addr) + iaddr, err := st.LookupIDAddress(addr) if err != nil { if xerrors.Is(err, types.ErrActorNotFound) { return nil, xerrors.Errorf("resolution lookup failed (%s): %w", addr, err) @@ -411,7 +411,7 @@ func (st *StateTree) DeleteActor(addr address.Address) error { return xerrors.Errorf("DeleteActor called on undefined address") } - iaddr, err := st.LookupID(addr) + iaddr, err := st.LookupIDAddress(addr) if err != nil { if xerrors.Is(err, types.ErrActorNotFound) { return xerrors.Errorf("resolution lookup failed (%s): %w", addr, err) diff --git a/chain/stmgr/actors.go b/chain/stmgr/actors.go index f1d615e8d..0dce2c3cd 100644 --- a/chain/stmgr/actors.go +++ b/chain/stmgr/actors.go @@ -542,7 +542,7 @@ func (sm *StateManager) MarketBalance(ctx context.Context, addr address.Address, return api.MarketBalance{}, err } - addr, err = sm.LookupID(ctx, addr, ts) + addr, err = sm.LookupIDAddress(ctx, addr, ts) if err != nil { return api.MarketBalance{}, err } diff --git a/chain/stmgr/rpc/rpcstatemanager.go b/chain/stmgr/rpc/rpcstatemanager.go index 9186501ea..fa311e1d4 100644 --- a/chain/stmgr/rpc/rpcstatemanager.go +++ b/chain/stmgr/rpc/rpcstatemanager.go @@ -44,7 +44,7 @@ func (s *RPCStateManager) LoadActorTsk(ctx context.Context, addr address.Address return s.gapi.StateGetActor(ctx, addr, tsk) } -func (s *RPCStateManager) LookupID(ctx context.Context, addr address.Address, ts *types.TipSet) (address.Address, error) { +func (s *RPCStateManager) LookupIDAddress(ctx context.Context, addr address.Address, ts *types.TipSet) (address.Address, error) { return s.gapi.StateLookupID(ctx, addr, ts.Key()) } diff --git a/chain/stmgr/searchwait.go b/chain/stmgr/searchwait.go index 356ace23c..3377389b9 100644 --- a/chain/stmgr/searchwait.go +++ b/chain/stmgr/searchwait.go @@ -243,7 +243,7 @@ func (sm *StateManager) searchBackForMsg(ctx context.Context, from *types.TipSet return nil, nil, cid.Undef, xerrors.Errorf("failed to load initital tipset") } - mFromId, err := sm.LookupID(ctx, m.VMMessage().From, from) + mFromId, err := sm.LookupIDAddress(ctx, m.VMMessage().From, from) if err != nil { return nil, nil, cid.Undef, xerrors.Errorf("looking up From id address: %w", err) } diff --git a/chain/stmgr/stmgr.go b/chain/stmgr/stmgr.go index cd35d6d19..d88d7dfd1 100644 --- a/chain/stmgr/stmgr.go +++ b/chain/stmgr/stmgr.go @@ -49,7 +49,7 @@ type StateManagerAPI interface { Call(ctx context.Context, msg *types.Message, ts *types.TipSet) (*api.InvocResult, error) GetPaychState(ctx context.Context, addr address.Address, ts *types.TipSet) (*types.Actor, paych.State, error) LoadActorTsk(ctx context.Context, addr address.Address, tsk types.TipSetKey) (*types.Actor, error) - LookupID(ctx context.Context, addr address.Address, ts *types.TipSet) (address.Address, error) + LookupIDAddress(ctx context.Context, addr address.Address, ts *types.TipSet) (address.Address, error) ResolveToDeterministicAddress(ctx context.Context, addr address.Address, ts *types.TipSet) (address.Address, error) } @@ -400,13 +400,30 @@ func (sm *StateManager) GetBlsPublicKey(ctx context.Context, addr address.Addres return kaddr.Payload(), nil } -func (sm *StateManager) LookupID(ctx context.Context, addr address.Address, ts *types.TipSet) (address.Address, error) { +func (sm *StateManager) LookupIDAddress(_ context.Context, addr address.Address, ts *types.TipSet) (address.Address, error) { + // Check for the fast route first to avoid unnecessary CBOR store instantiation and state tree load. + if addr.Protocol() == address.ID { + return addr, nil + } + cst := cbor.NewCborStore(sm.cs.StateBlockstore()) state, err := state.LoadStateTree(cst, sm.parentState(ts)) if err != nil { return address.Undef, xerrors.Errorf("load state tree: %w", err) } - return state.LookupID(addr) + return state.LookupIDAddress(addr) +} + +func (sm *StateManager) LookupID(ctx context.Context, addr address.Address, ts *types.TipSet) (abi.ActorID, error) { + idAddr, err := sm.LookupIDAddress(ctx, addr, ts) + if err != nil { + return 0, xerrors.Errorf("state manager lookup id: %w", err) + } + id, err := address.IDFromAddress(idAddr) + if err != nil { + return 0, xerrors.Errorf("resolve actor id: id from addr: %w", err) + } + return abi.ActorID(id), nil } func (sm *StateManager) LookupRobustAddress(ctx context.Context, idAddr address.Address, ts *types.TipSet) (address.Address, error) { diff --git a/chain/store/messages.go b/chain/store/messages.go index c23f900d7..4129a9199 100644 --- a/chain/store/messages.go +++ b/chain/store/messages.go @@ -119,7 +119,7 @@ func (cs *ChainStore) BlockMsgsForTipset(ctx context.Context, ts *types.TipSet) var sender address.Address if ts.Height() >= build.UpgradeHyperdriveHeight { if useIds { - sender, err = st.LookupID(m.From) + sender, err = st.LookupIDAddress(m.From) if err != nil { return false, xerrors.Errorf("failed to resolve sender: %w", err) } @@ -131,14 +131,14 @@ func (cs *ChainStore) BlockMsgsForTipset(ctx context.Context, ts *types.TipSet) // uh-oh, we actually have an ID-sender! useIds = true for robust, nonce := range applied { - resolved, err := st.LookupID(robust) + resolved, err := st.LookupIDAddress(robust) if err != nil { return false, xerrors.Errorf("failed to resolve sender: %w", err) } applied[resolved] = nonce } - sender, err = st.LookupID(m.From) + sender, err = st.LookupIDAddress(m.From) if err != nil { return false, xerrors.Errorf("failed to resolve sender: %w", err) } diff --git a/chain/vm/runtime.go b/chain/vm/runtime.go index 355fcea2b..9ca4f55d0 100644 --- a/chain/vm/runtime.go +++ b/chain/vm/runtime.go @@ -111,7 +111,7 @@ func (rt *Runtime) TotalFilCircSupply() abi.TokenAmount { } func (rt *Runtime) ResolveAddress(addr address.Address) (ret address.Address, ok bool) { - r, err := rt.state.LookupID(addr) + r, err := rt.state.LookupIDAddress(addr) if err != nil { if xerrors.Is(err, types.ErrActorNotFound) { return address.Undef, false diff --git a/chain/vm/vm.go b/chain/vm/vm.go index ba404ab1f..1e0591b6c 100644 --- a/chain/vm/vm.go +++ b/chain/vm/vm.go @@ -902,7 +902,7 @@ func (vm *LegacyVM) transfer(from, to address.Address, amt types.BigInt, network return aerrors.Newf(exitcode.SysErrForbidden, "attempted to transfer negative value: %s", amt) } - fromID, err = vm.cstate.LookupID(from) + fromID, err = vm.cstate.LookupIDAddress(from) if err != nil { return aerrors.Fatalf("transfer failed when resolving sender address: %s", err) } @@ -921,7 +921,7 @@ func (vm *LegacyVM) transfer(from, to address.Address, amt types.BigInt, network return nil } - toID, err = vm.cstate.LookupID(to) + toID, err = vm.cstate.LookupIDAddress(to) if err != nil { return aerrors.Fatalf("transfer failed when resolving receiver address: %s", err) } @@ -935,12 +935,12 @@ func (vm *LegacyVM) transfer(from, to address.Address, amt types.BigInt, network return nil } - fromID, err = vm.cstate.LookupID(from) + fromID, err = vm.cstate.LookupIDAddress(from) if err != nil { return aerrors.Fatalf("transfer failed when resolving sender address: %s", err) } - toID, err = vm.cstate.LookupID(to) + toID, err = vm.cstate.LookupIDAddress(to) if err != nil { return aerrors.Fatalf("transfer failed when resolving receiver address: %s", err) } diff --git a/itests/migration_test.go b/itests/migration_test.go index 68991a579..e19aaf45f 100644 --- a/itests/migration_test.go +++ b/itests/migration_test.go @@ -584,7 +584,7 @@ func TestMigrationNV18(t *testing.T) { // check the EthZeroAddress ethZeroAddr, err := (ethtypes.EthAddress{}).ToFilecoinAddress() require.NoError(t, err) - ethZeroAddrID, err := newStateTree.LookupID(ethZeroAddr) + ethZeroAddrID, err := newStateTree.LookupIDAddress(ethZeroAddr) require.NoError(t, err) ethZeroActor, err := newStateTree.GetActor(ethZeroAddrID) require.NoError(t, err) diff --git a/node/impl/full/eth_utils.go b/node/impl/full/eth_utils.go index 50e76f84e..1d7bfac5a 100644 --- a/node/impl/full/eth_utils.go +++ b/node/impl/full/eth_utils.go @@ -392,7 +392,7 @@ func lookupEthAddress(addr address.Address, st *state.StateTree) (ethtypes.EthAd } // Otherwise, resolve the ID addr. - idAddr, err := st.LookupID(addr) + idAddr, err := st.LookupIDAddress(addr) if err != nil { return ethtypes.EthAddress{}, err } diff --git a/node/impl/full/state.go b/node/impl/full/state.go index dda889832..ed6fd6685 100644 --- a/node/impl/full/state.go +++ b/node/impl/full/state.go @@ -486,7 +486,7 @@ func (m *StateModule) StateLookupID(ctx context.Context, addr address.Address, t return address.Undef, xerrors.Errorf("loading tipset %s: %w", tsk, err) } - ret, err := m.StateManager.LookupID(ctx, addr, ts) + ret, err := m.StateManager.LookupIDAddress(ctx, addr, ts) if err != nil && xerrors.Is(err, types.ErrActorNotFound) { return address.Undef, &api.ErrActorNotFound{} } From 65edab42bc597355ff6776422ded9eda88d6e68a Mon Sep 17 00:00:00 2001 From: Phi-rjan Date: Thu, 25 Apr 2024 17:13:18 +0200 Subject: [PATCH 380/394] Add v13 support to invariants-checker (#11931) Add v13 support to invariants-checker --- cmd/lotus-shed/invariants.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cmd/lotus-shed/invariants.go b/cmd/lotus-shed/invariants.go index 5c6fb2d4f..e3507015a 100644 --- a/cmd/lotus-shed/invariants.go +++ b/cmd/lotus-shed/invariants.go @@ -18,6 +18,7 @@ import ( v10 "github.com/filecoin-project/go-state-types/builtin/v10" v11 "github.com/filecoin-project/go-state-types/builtin/v11" v12 "github.com/filecoin-project/go-state-types/builtin/v12" + v13 "github.com/filecoin-project/go-state-types/builtin/v13" v8 "github.com/filecoin-project/go-state-types/builtin/v8" v9 "github.com/filecoin-project/go-state-types/builtin/v9" @@ -186,6 +187,11 @@ var invariantsCmd = &cli.Command{ if err != nil { return xerrors.Errorf("checking state invariants: %w", err) } + case actorstypes.Version13: + messages, err = v13.CheckStateInvariants(actorTree, abi.ChainEpoch(epoch), actorCodeCids) + if err != nil { + return xerrors.Errorf("checking state invariants: %w", err) + } default: return xerrors.Errorf("unsupported actor version: %v", av) } From ec6d3e148381810dbfc230dcafbf2ac91b8b5055 Mon Sep 17 00:00:00 2001 From: Phi-rjan Date: Thu, 25 Apr 2024 21:46:13 +0200 Subject: [PATCH 381/394] chore: docs: nv-skeleton documentation (#11065) * nv-skeleton documentation Add a tutorial for how one can create a nv-skeleton in Lotus * Add footnote for `Add migration` step Add footnote for `Add migration` step * Indent migration-code Indent migration-code to make it show properly as a footnote. * Add ref-fvm and filecoin-ffi checklist Add ref-fvm and filecoin-ffi checklist * Add Filecoin-FFI steps Add Filecoin-FFI steps * Add step to params_butterfly.go Add step to params_butterfly.go * Fix typo Fix typo * Add links to reference PRs Add links to reference PRs * Update ref-fvm list Update ref-fvm list --- .../misc/Building_a_network_skeleton.md | 255 ++++++++++++++++++ 1 file changed, 255 insertions(+) create mode 100644 documentation/misc/Building_a_network_skeleton.md diff --git a/documentation/misc/Building_a_network_skeleton.md b/documentation/misc/Building_a_network_skeleton.md new file mode 100644 index 000000000..5aea6f706 --- /dev/null +++ b/documentation/misc/Building_a_network_skeleton.md @@ -0,0 +1,255 @@ +# Network Upgrade Skeleton in Lotus + +This guide will walk you through the process of creating a skeleton for a network upgrade in Lotus. The process involves making changes in multiple repositories in the following order: + +1. [`ref-fvm`](#ref-fvm-checklist) +2. [`filecoin-ffi`](#filecoin-ffi-checklist) +3. [`go-state-types`](#go-state-types-checklist) +4. [`lotus`](#lotus-checklist) + +Each repository has its own set of steps that need to be followed. This guide will provide detailed instructions for each repository. + +## Setup + +1. Clone the [ref-fvm](https://github.com/filecoin-project/ref-fvm.git) repository. + +2. Clone the [filecoin-ffi](https://github.com/filecoin-project/filecoin-ffi.git) repository. + +3. Clone the [go-state-types](https://github.com/filecoin-project/go-state-types) repository. + +4. In your Lotus repository, add `replace github.com/filecoin-project/go-state-types => ../go-state-types` to the very end of your Lotus `go.mod` file. + - This ensures that your local clone copy of `go-state-types` is used. Any changes you make there will be reflected in your Lotus project. + +## Ref-FVM Checklist + +1. Add support for the new network version in Ref-FVM: + + - In `fvm/Cargo.toml` add `nvXX-dev` as a feature flag in the [features]-section. + - In `fvm/src/gas/price_list.rs`, extend the `price_list_by_network_version` function to support the new network version with the `nvXX-dev` feature flag. + - In fvm/src/machine/default.rs, locate the new function within your machine context. You'll find a SUPPORTED_VERSIONS constant that sets the range of supported network versions. Update this range to include the new network version. Do this by replacing the existing feature flag nvXX-dev and NetworkVersion::VXX with the new ones corresponding to your new network version. + - In `shared/src/version/mod.rs`, in the `NetworkVersion` implementation, you will find a series of constants representing different network versions. To add a new network version, you need to declare a new constant: `pub const (VXX+1): Self = Self(XX+1);` + +You can take a look at [this Ref-FVM PR as a reference](https://github.com/filecoin-project/ref-fvm/pull/1929), which added the skeleton for network version 22. + +## Filecoin-FFI Checklist + +1. Update the `TryFrom` implementation for `EngineVersion` in `rust/src/fvm/engine.rs` + - Add the new network version number (XX+1) to the existing match arm for the network version. + +2. Patch the FVM-dependency (fvm3) in `rust/cargo.toml` to use the custom branch of the FVM created in the [Ref-FVM Checklist](#ref-fvm-checklist)) + - Add `features = ["your-ref-fvm-branch"]` to tell Cargo to use you Ref-FVM branch. + +You can take a look at this [Filecoin-FFI PR as a reference](https://github.com/filecoin-project/filecoin-ffi/pull/438), which added the skeleton for network version 22. + +## Go-State-Types Checklist + +1. Follow the [go-state-types actor version checklist](https://github.com/filecoin-project/go-state-types/blob/master/actors_version_checklist.md): + + - Copy `go-state-types/builtin/vX` to `go-state-types/builtin/v(X+1)`. + - Change all references from vX to v(X+1) in the new files. + - Add new network version to `network/version.go`. + - Add new actors version to `actors/version.go`. + - Add `Version(XX+1) Version = XX+1` as a constant. + - In `func VersionForNetwork` add `case network.Version(XX+1): return Version(XX+1), nil`. + - Add the new version to the gen step of the makefile. + - Add `$(GO_BIN) run ./builtin/v(XX+1)/gen/gen.go`. + +You can take a look at this [Go-State-Types PR as a reference](https://github.com/filecoin-project/go-state-types/pull/232), which added the skeleton for network version 22. + +## Lotus Checklist + +1. Import new actors: + + - Create a mock actor-bundle for the new network version. + - In `/build/actors` run `./pack.sh vXX+1 vXX.0.0` where XX is the current actor bundle version. + +2. Define upgrade heights in `build/params_`: + + - Update the following files: + - `params_2k.go` + - Set previous `UpgradeXxxxxHeight = abi.ChainEpoch(-xx-1)` + - Add `var UpgradeXxxxxHeight = abi.ChainEpoch(200)` + - Add `UpgradeXxxxxHeight = getUpgradeHeight("LOTUS_XXXXX_HEIGHT", UpgradeXXXXHeight)` + - Set `const GenesisNetworkVersion = network.VersionXX` where XX is the network version you are upgrading from. + - `params_butterfly.go` + - set previous upgrade to `var UpgradeXxxxxHeigh = abi.ChainEpoch(-xx-1)` + - Add comment with ?????? signaling that the new upgrade date is unkown + - Add `const UpgradeXxxxxHeight = 999999999999999` + - `params_calibnet.go` + - Add comment with `??????` signaling that the new upgrade date is unkown + - Add `const UpgradeXxxxxHeight = 999999999999999` + - `params_interop.go` + - set previous upgrade to `var UpgradeXxxxxHeigh = abi.ChainEpoch(-xx-1)` + - Add `const UpgradeXxxxxHeight = 50` + - `params_mainnet.go` + - Set previous upgrade to `const UpgradeXxxxxHeight = XX` + - Add comment with ???? signaling that the new upgrade date is unkown + - Add `var UpgradeXxxxxxHeight = abi.ChainEpoch(9999999999)` + - Change the `LOTUS_DISABLE_XXXX` env variable to the new network name + - `params_testground.go` + - Add `UpgradeXxxxxHeight abi.ChainEpoch = (-xx-1)` + +3. Generate adapters: + + - Update `gen/inlinegen-data.json`. + - Add `XX+1` to "actorVersions" and set "latestActorsVersion" to `XX+1`. + - Add `XX+1` to "networkVersions" and set "latestNetworkVersion" to `XX+1`. + + - Run `make actors-gen`. This generates the `/chain/actors/builtin/*` code, `/chain/actors/policy/policy.go` code, `/chain/actors/version.go`, and `/itest/kit/ensemble_opts_nv.go`. + +4. Update `chain/consensus/filcns/upgrades.go`. + - Import `nv(XX+1) "github.com/filecoin-project/go-state-types/builtin/v(XX+1)/migration`. + - Add Schedule. [^1] + - Add Migration. [^2] + +5. Add actorstype to the NewActorRegistry in `/chain/consensus/computestate.go`. + - Add `inv.Register(actorstypes.Version(XX+1), vm.ActorsVersionPredicate(actorstypes.Version(XX+1)), builtin.MakeRegistry(actorstypes.Version(XX+1))`. + +6. Add upgrade field to `api/types.go/ForkUpgradeParams`. + - Add `UpgradeXxxxxHeight abi.ChainEpoch` to `ForkUpgradeParams` struct. + +7. Add upgrade to `node/impl/full/state.go`. + - Add `UpgradeXxxxxHeight: build.UpgradeXxxxxHeight,`. + +8. Add network version to `chain/state/statetree.go`. + - Add `network.VersionXX+1` to `VersionForNetwork` function. + +9. Run `make gen`. + +10. Run `make docsgen-cli`. + +And you're done! This should create a network upgrade skeleton that you are able to run locally with your local go-state-types clones, and a mock Actors-bundle. This will allow you to: + +- Have a local developer network that starts at the current network version. +- Be able to see the Actor CIDs/Actor version for the mock v12-bundle through `lotus state actor-cids --network-version XX+1` +- Have a successful pre-migration. +- Complete Migration at upgrade epoch, but fail immidiately after the upgrade. + +You can take a look at this [Lotus PR as a reference](https://github.com/filecoin-project/lotus/pull/11432), which added the skeleton for network version 22. + +// TODO: Create a video-tutorial going through all the steps + +[^1]: Here is an example of how you can add a schedule: + + ```go + { + Height: build.UpgradeXxxxHeight, + Network: network.Version(XX+1), + Migration: UpgradeActorsV(XX+1), + PreMigrations: []stmgr.PreMigration{{ + PreMigration: PreUpgradeActors(VXX+1), + StartWithin: 120, + DontStartWithin: 15, + StopWithin: 10, + }}, + Expensive: true, + }, + ``` + + This schedule should be added to the `DefaultUpgradeSchedule` function, specifically within the `updates` array. + +[^2]: Here is an example of how you can add a migration: + + ```go + func PreUpgradeActorsV(XX+1)(ctx context.Context, sm *stmgr.StateManager, cache stmgr.MigrationCache, root cid.Cid, epoch abi.ChainEpoch, ts *types.TipSet) error { + // Use half the CPUs for pre-migration, but leave at least 3. + workerCount := MigrationMaxWorkerCount + if workerCount <= 4 { + workerCount = 1 + } else { + workerCount /= 2 + } + + lbts, lbRoot, err := stmgr.GetLookbackTipSetForRound(ctx, sm, ts, epoch) + if err != nil { + return xerrors.Errorf("error getting lookback ts for premigration: %w", err) + } + + config := migration.Config{ + MaxWorkers: uint(workerCount), + ProgressLogPeriod: time.Minute * 5, + } + + _, err = upgradeActorsV(XX+1)Common(ctx, sm, cache, lbRoot, epoch, lbts, config) + return err + } + + func UpgradeActorsV(XX+1)(ctx context.Context, sm *stmgr.StateManager, cache stmgr.MigrationCache, cb stmgr.ExecMonitor, + root cid.Cid, epoch abi.ChainEpoch, ts *types.TipSet) (cid.Cid, error) { + // Use all the CPUs except 2. + workerCount := MigrationMaxWorkerCount - 3 + if workerCount <= 0 { + workerCount = 1 + } + config := migration.Config{ + MaxWorkers: uint(workerCount), + JobQueueSize: 1000, + ResultQueueSize: 100, + ProgressLogPeriod: 10 * time.Second, + } + newRoot, err := upgradeActorsV(XX+1)Common(ctx, sm, cache, root, epoch, ts, config) + if err != nil { + return cid.Undef, xerrors.Errorf("migrating actors v11 state: %w", err) + } + return newRoot, nil + } + + func upgradeActorsV(XX+1)Common( + ctx context.Context, sm *stmgr.StateManager, cache stmgr.MigrationCache, + root cid.Cid, epoch abi.ChainEpoch, ts *types.TipSet, + config migration.Config, + ) (cid.Cid, error) { + writeStore := blockstore.NewAutobatch(ctx, sm.ChainStore().StateBlockstore(), units.GiB/4) + adtStore := store.ActorStore(ctx, writeStore) + // ensure that the manifest is loaded in the blockstore + if err := bundle.LoadBundles(ctx, writeStore, actorstypes.Version(XX+1)); err != nil { + return cid.Undef, xerrors.Errorf("failed to load manifest bundle: %w", err) + } + + // Load the state root. + var stateRoot types.StateRoot + if err := adtStore.Get(ctx, root, &stateRoot); err != nil { + return cid.Undef, xerrors.Errorf("failed to decode state root: %w", err) + } + + if stateRoot.Version != types.StateTreeVersion5 { + return cid.Undef, xerrors.Errorf( + "expected state root version 5 for actors v(XX+1) upgrade, got %d", + stateRoot.Version, + ) + } + + manifest, ok := actors.GetManifest(actorstypes.Version(XX+1)) + if !ok { + return cid.Undef, xerrors.Errorf("no manifest CID for v(XX+1) upgrade") + } + + // Perform the migration + newHamtRoot, err := nv(XX+1).MigrateStateTree(ctx, adtStore, manifest, stateRoot.Actors, epoch, config, + migrationLogger{}, cache) + if err != nil { + return cid.Undef, xerrors.Errorf("upgrading to actors v11: %w", err) + } + + // Persist the result. + newRoot, err := adtStore.Put(ctx, &types.StateRoot{ + Version: types.StateTreeVersion5, + Actors: newHamtRoot, + Info: stateRoot.Info, + }) + if err != nil { + return cid.Undef, xerrors.Errorf("failed to persist new state root: %w", err) + } + + // Persists the new tree and shuts down the flush worker + if err := writeStore.Flush(ctx); err != nil { + return cid.Undef, xerrors.Errorf("writeStore flush failed: %w", err) + } + + if err := writeStore.Shutdown(ctx); err != nil { + return cid.Undef, xerrors.Errorf("writeStore shutdown failed: %w", err) + } + + return newRoot, nil + } + ``` \ No newline at end of file From 0f7c74e499aadf32a0b0e10d04f717dfa818ba04 Mon Sep 17 00:00:00 2001 From: Lee Date: Fri, 26 Apr 2024 16:26:57 +0800 Subject: [PATCH 382/394] feat: curio: add StorageInit api (#11918) * feat: add StorageInit api * remove unused variables * fix gen check --- api/api_curio.go | 1 + api/proxy_gen.go | 13 + build/openrpc/full.json | 486 +++++++++++------------ build/openrpc/gateway.json | 192 ++++----- build/openrpc/miner.json | 260 ++++++------ build/openrpc/worker.json | 74 ++-- cmd/curio/rpc/rpc.go | 38 ++ cmd/curio/storage.go | 30 +- documentation/en/api-v0-methods-curio.md | 40 ++ 9 files changed, 601 insertions(+), 533 deletions(-) diff --git a/api/api_curio.go b/api/api_curio.go index 6f31ef7e0..0eceb484f 100644 --- a/api/api_curio.go +++ b/api/api_curio.go @@ -17,6 +17,7 @@ type Curio interface { AllocatePieceToSector(ctx context.Context, maddr address.Address, piece PieceDealInfo, rawSize int64, source url.URL, header http.Header) (SectorOffset, error) //perm:write + StorageInit(ctx context.Context, path string, opts storiface.LocalStorageMeta) error //perm:admin StorageAddLocal(ctx context.Context, path string) error //perm:admin StorageDetachLocal(ctx context.Context, path string) error //perm:admin StorageList(ctx context.Context) (map[storiface.ID][]storiface.Decl, error) //perm:admin diff --git a/api/proxy_gen.go b/api/proxy_gen.go index c36c62d57..f9e786615 100644 --- a/api/proxy_gen.go +++ b/api/proxy_gen.go @@ -136,6 +136,8 @@ type CurioMethods struct { StorageInfo func(p0 context.Context, p1 storiface.ID) (storiface.StorageInfo, error) `perm:"admin"` + StorageInit func(p0 context.Context, p1 string, p2 storiface.LocalStorageMeta) error `perm:"admin"` + StorageList func(p0 context.Context) (map[storiface.ID][]storiface.Decl, error) `perm:"admin"` StorageLocal func(p0 context.Context) (map[storiface.ID]string, error) `perm:"admin"` @@ -1580,6 +1582,17 @@ func (s *CurioStub) StorageInfo(p0 context.Context, p1 storiface.ID) (storiface. return *new(storiface.StorageInfo), ErrNotSupported } +func (s *CurioStruct) StorageInit(p0 context.Context, p1 string, p2 storiface.LocalStorageMeta) error { + if s.Internal.StorageInit == nil { + return ErrNotSupported + } + return s.Internal.StorageInit(p0, p1, p2) +} + +func (s *CurioStub) StorageInit(p0 context.Context, p1 string, p2 storiface.LocalStorageMeta) error { + return ErrNotSupported +} + func (s *CurioStruct) StorageList(p0 context.Context) (map[storiface.ID][]storiface.Decl, error) { if s.Internal.StorageList == nil { return *new(map[storiface.ID][]storiface.Decl), ErrNotSupported diff --git a/build/openrpc/full.json b/build/openrpc/full.json index ae12bc302..5d422f283 100644 --- a/build/openrpc/full.json +++ b/build/openrpc/full.json @@ -37,7 +37,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1638" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1651" } }, { @@ -60,7 +60,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1649" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1662" } }, { @@ -103,7 +103,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1660" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1673" } }, { @@ -214,7 +214,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1682" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1695" } }, { @@ -454,7 +454,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1693" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1706" } }, { @@ -685,7 +685,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1704" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1717" } }, { @@ -784,7 +784,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1715" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1728" } }, { @@ -816,7 +816,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1726" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1739" } }, { @@ -922,7 +922,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1737" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1750" } }, { @@ -1019,7 +1019,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1748" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1761" } }, { @@ -1078,7 +1078,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1759" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1772" } }, { @@ -1171,7 +1171,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1770" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1783" } }, { @@ -1255,7 +1255,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1781" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1794" } }, { @@ -1355,7 +1355,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1792" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1805" } }, { @@ -1411,7 +1411,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1803" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1816" } }, { @@ -1484,7 +1484,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1814" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1827" } }, { @@ -1557,7 +1557,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1825" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1838" } }, { @@ -1604,7 +1604,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1836" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1849" } }, { @@ -1636,7 +1636,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1847" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1860" } }, { @@ -1691,7 +1691,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1858" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1871" } }, { @@ -1743,7 +1743,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1880" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1893" } }, { @@ -1780,7 +1780,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1891" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1904" } }, { @@ -1827,7 +1827,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1902" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1915" } }, { @@ -1874,7 +1874,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1913" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1926" } }, { @@ -1954,7 +1954,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1924" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1937" } }, { @@ -2006,7 +2006,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1935" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1948" } }, { @@ -2065,7 +2065,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1946" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1959" } }, { @@ -2136,7 +2136,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1957" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1970" } }, { @@ -2177,7 +2177,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1968" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1981" } }, { @@ -2245,7 +2245,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L1990" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2003" } }, { @@ -2306,7 +2306,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2001" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2014" } }, { @@ -2413,7 +2413,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2012" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2025" } }, { @@ -2569,7 +2569,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2023" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2036" } }, { @@ -2635,7 +2635,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2034" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2047" } }, { @@ -2976,7 +2976,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2045" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2058" } }, { @@ -3021,7 +3021,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2056" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2069" } }, { @@ -3068,7 +3068,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2089" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2102" } }, { @@ -3139,7 +3139,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2100" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2113" } }, { @@ -3282,7 +3282,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2111" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2124" } }, { @@ -3612,7 +3612,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2122" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2135" } }, { @@ -3680,7 +3680,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2133" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2146" } }, { @@ -3914,7 +3914,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2144" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2157" } }, { @@ -4077,7 +4077,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2155" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2168" } }, { @@ -4160,7 +4160,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2166" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2179" } }, { @@ -4201,7 +4201,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2177" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2190" } }, { @@ -4272,7 +4272,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2188" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2201" } }, { @@ -4416,7 +4416,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2199" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2212" } }, { @@ -4456,7 +4456,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2210" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2223" } }, { @@ -4497,7 +4497,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2221" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2234" } }, { @@ -4622,7 +4622,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2232" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2245" } }, { @@ -4747,7 +4747,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2243" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2256" } }, { @@ -4786,7 +4786,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2254" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2267" } }, { @@ -4833,7 +4833,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2265" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2278" } }, { @@ -4888,7 +4888,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2276" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2289" } }, { @@ -4917,7 +4917,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2287" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2300" } }, { @@ -5054,7 +5054,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2298" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2311" } }, { @@ -5083,7 +5083,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2309" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2322" } }, { @@ -5137,7 +5137,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2320" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2333" } }, { @@ -5228,7 +5228,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2331" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2344" } }, { @@ -5256,7 +5256,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2342" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2355" } }, { @@ -5346,7 +5346,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2353" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2366" } }, { @@ -5602,7 +5602,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2364" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2377" } }, { @@ -5847,7 +5847,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2375" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2388" } }, { @@ -5903,7 +5903,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2386" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2399" } }, { @@ -5950,7 +5950,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2397" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2410" } }, { @@ -6048,7 +6048,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2408" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2421" } }, { @@ -6114,7 +6114,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2419" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2432" } }, { @@ -6180,7 +6180,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2430" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2443" } }, { @@ -6289,7 +6289,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2441" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2454" } }, { @@ -6347,7 +6347,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2452" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2465" } }, { @@ -6469,7 +6469,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2463" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2476" } }, { @@ -6673,7 +6673,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2474" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2487" } }, { @@ -6868,7 +6868,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2485" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2498" } }, { @@ -7055,7 +7055,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2496" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2509" } }, { @@ -7259,7 +7259,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2507" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2520" } }, { @@ -7350,7 +7350,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2518" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2531" } }, { @@ -7408,7 +7408,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2529" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2542" } }, { @@ -7666,7 +7666,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2540" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2553" } }, { @@ -7941,7 +7941,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2551" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2564" } }, { @@ -7969,7 +7969,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2562" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2575" } }, { @@ -8007,7 +8007,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2573" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2586" } }, { @@ -8115,7 +8115,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2584" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2597" } }, { @@ -8153,7 +8153,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2595" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2608" } }, { @@ -8182,7 +8182,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2606" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2619" } }, { @@ -8245,7 +8245,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2617" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2630" } }, { @@ -8308,7 +8308,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2628" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2641" } }, { @@ -8353,7 +8353,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2639" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2652" } }, { @@ -8475,7 +8475,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2650" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2663" } }, { @@ -8630,7 +8630,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2661" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2674" } }, { @@ -8684,7 +8684,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2672" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2685" } }, { @@ -8738,7 +8738,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2683" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2696" } }, { @@ -8793,7 +8793,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2694" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2707" } }, { @@ -8936,7 +8936,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2705" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2718" } }, { @@ -9063,7 +9063,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2716" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2729" } }, { @@ -9165,7 +9165,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2727" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2740" } }, { @@ -9388,7 +9388,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2738" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2751" } }, { @@ -9571,7 +9571,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2749" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2762" } }, { @@ -9651,7 +9651,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2760" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2773" } }, { @@ -9696,7 +9696,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2771" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2784" } }, { @@ -9752,7 +9752,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2782" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2795" } }, { @@ -9832,7 +9832,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2793" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2806" } }, { @@ -9912,7 +9912,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2804" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2817" } }, { @@ -10397,7 +10397,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2815" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2828" } }, { @@ -10591,7 +10591,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2826" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2839" } }, { @@ -10746,7 +10746,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2837" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2850" } }, { @@ -10995,7 +10995,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2848" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2861" } }, { @@ -11150,7 +11150,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2859" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2872" } }, { @@ -11327,7 +11327,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2870" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2883" } }, { @@ -11425,7 +11425,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2881" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2894" } }, { @@ -11590,7 +11590,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2892" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2905" } }, { @@ -11629,7 +11629,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2903" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2916" } }, { @@ -11694,7 +11694,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2914" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2927" } }, { @@ -11740,7 +11740,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2925" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2938" } }, { @@ -11890,7 +11890,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2936" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2949" } }, { @@ -12027,7 +12027,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2947" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2960" } }, { @@ -12258,7 +12258,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2958" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2971" } }, { @@ -12395,7 +12395,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2969" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2982" } }, { @@ -12560,7 +12560,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2980" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2993" } }, { @@ -12637,7 +12637,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L2991" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3004" } }, { @@ -12832,7 +12832,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3013" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3026" } }, { @@ -13011,7 +13011,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3024" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3037" } }, { @@ -13173,7 +13173,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3035" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3048" } }, { @@ -13321,7 +13321,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3046" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3059" } }, { @@ -13549,7 +13549,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3057" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3070" } }, { @@ -13697,7 +13697,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3068" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3081" } }, { @@ -13909,7 +13909,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3079" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3092" } }, { @@ -14115,7 +14115,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3090" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3103" } }, { @@ -14183,7 +14183,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3101" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3114" } }, { @@ -14300,7 +14300,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3112" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3125" } }, { @@ -14391,7 +14391,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3123" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3136" } }, { @@ -14477,7 +14477,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3134" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3147" } }, { @@ -14672,7 +14672,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3145" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3158" } }, { @@ -14834,7 +14834,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3156" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3169" } }, { @@ -15030,7 +15030,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3167" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3180" } }, { @@ -15210,7 +15210,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3178" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3191" } }, { @@ -15373,7 +15373,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3189" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3202" } }, { @@ -15400,7 +15400,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3200" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3213" } }, { @@ -15427,7 +15427,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3211" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3224" } }, { @@ -15526,7 +15526,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3222" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3235" } }, { @@ -15572,7 +15572,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3233" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3246" } }, { @@ -15672,7 +15672,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3244" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3257" } }, { @@ -15788,7 +15788,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3255" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3268" } }, { @@ -15836,7 +15836,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3266" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3279" } }, { @@ -15928,7 +15928,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3277" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3290" } }, { @@ -16043,7 +16043,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3288" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3301" } }, { @@ -16091,7 +16091,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3299" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3312" } }, { @@ -16128,7 +16128,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3310" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3323" } }, { @@ -16400,7 +16400,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3321" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3334" } }, { @@ -16448,7 +16448,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3332" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3345" } }, { @@ -16506,7 +16506,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3343" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3356" } }, { @@ -16711,7 +16711,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3354" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3367" } }, { @@ -16914,7 +16914,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3365" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3378" } }, { @@ -17083,7 +17083,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3376" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3389" } }, { @@ -17287,7 +17287,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3387" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3400" } }, { @@ -17454,7 +17454,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3398" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3411" } }, { @@ -17661,7 +17661,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3409" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3422" } }, { @@ -17729,7 +17729,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3420" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3433" } }, { @@ -17781,7 +17781,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3431" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3444" } }, { @@ -17830,7 +17830,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3442" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3455" } }, { @@ -17921,7 +17921,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3453" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3466" } }, { @@ -18427,7 +18427,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3464" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3477" } }, { @@ -18533,7 +18533,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3475" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3488" } }, { @@ -18585,7 +18585,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3486" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3499" } }, { @@ -19137,7 +19137,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3497" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3510" } }, { @@ -19251,7 +19251,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3508" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3521" } }, { @@ -19348,7 +19348,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3519" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3532" } }, { @@ -19448,7 +19448,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3530" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3543" } }, { @@ -19536,7 +19536,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3541" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3554" } }, { @@ -19636,7 +19636,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3552" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3565" } }, { @@ -19723,7 +19723,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3563" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3576" } }, { @@ -19814,7 +19814,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3574" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3587" } }, { @@ -19939,7 +19939,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3585" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3598" } }, { @@ -20048,7 +20048,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3596" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3609" } }, { @@ -20118,7 +20118,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3607" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3620" } }, { @@ -20221,7 +20221,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3618" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3631" } }, { @@ -20282,7 +20282,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3629" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3642" } }, { @@ -20412,7 +20412,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3640" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3653" } }, { @@ -20519,7 +20519,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3651" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3664" } }, { @@ -20728,7 +20728,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3662" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3675" } }, { @@ -20805,7 +20805,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3673" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3686" } }, { @@ -20882,7 +20882,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3684" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3697" } }, { @@ -20991,7 +20991,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3695" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3708" } }, { @@ -21100,7 +21100,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3706" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3719" } }, { @@ -21161,7 +21161,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3717" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3730" } }, { @@ -21271,7 +21271,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3728" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3741" } }, { @@ -21332,7 +21332,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3739" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3752" } }, { @@ -21400,7 +21400,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3750" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3763" } }, { @@ -21468,7 +21468,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3761" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3774" } }, { @@ -21549,7 +21549,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3772" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3785" } }, { @@ -21698,7 +21698,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3783" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3796" } }, { @@ -21770,7 +21770,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3794" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3807" } }, { @@ -21929,7 +21929,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3805" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3818" } }, { @@ -22094,7 +22094,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3816" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3829" } }, { @@ -22164,7 +22164,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3827" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3840" } }, { @@ -22232,7 +22232,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3838" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3851" } }, { @@ -22325,7 +22325,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3849" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3862" } }, { @@ -22396,7 +22396,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3860" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3873" } }, { @@ -22597,7 +22597,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3871" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3884" } }, { @@ -22729,7 +22729,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3882" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3895" } }, { @@ -22866,7 +22866,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3893" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3906" } }, { @@ -22977,7 +22977,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3904" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3917" } }, { @@ -23109,7 +23109,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3915" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3928" } }, { @@ -23240,7 +23240,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3926" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3939" } }, { @@ -23311,7 +23311,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3937" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3950" } }, { @@ -23395,7 +23395,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3948" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3961" } }, { @@ -23481,7 +23481,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3959" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3972" } }, { @@ -23664,7 +23664,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3970" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3983" } }, { @@ -23691,7 +23691,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3981" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3994" } }, { @@ -23744,7 +23744,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L3992" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4005" } }, { @@ -23832,7 +23832,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4003" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4016" } }, { @@ -24283,7 +24283,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4014" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4027" } }, { @@ -24450,7 +24450,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4025" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4038" } }, { @@ -24548,7 +24548,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4036" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4049" } }, { @@ -24721,7 +24721,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4047" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4060" } }, { @@ -24819,7 +24819,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4058" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4071" } }, { @@ -24970,7 +24970,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4069" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4082" } }, { @@ -25055,7 +25055,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4080" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4093" } }, { @@ -25123,7 +25123,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4091" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4104" } }, { @@ -25175,7 +25175,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4102" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4115" } }, { @@ -25243,7 +25243,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4113" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4126" } }, { @@ -25404,7 +25404,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4124" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4137" } }, { @@ -25451,7 +25451,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4146" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4159" } }, { @@ -25498,7 +25498,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4157" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4170" } }, { @@ -25541,7 +25541,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4179" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4192" } }, { @@ -25637,7 +25637,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4190" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4203" } }, { @@ -25903,7 +25903,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4201" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4214" } }, { @@ -25926,7 +25926,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4212" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4225" } }, { @@ -25969,7 +25969,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4223" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4236" } }, { @@ -26020,7 +26020,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4234" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4247" } }, { @@ -26065,7 +26065,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4245" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4258" } }, { @@ -26093,7 +26093,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4256" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4269" } }, { @@ -26133,7 +26133,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4267" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4280" } }, { @@ -26192,7 +26192,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4278" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4291" } }, { @@ -26236,7 +26236,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4289" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4302" } }, { @@ -26295,7 +26295,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4300" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4313" } }, { @@ -26332,7 +26332,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4311" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4324" } }, { @@ -26376,7 +26376,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4322" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4335" } }, { @@ -26416,7 +26416,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4333" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4346" } }, { @@ -26491,7 +26491,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4344" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4357" } }, { @@ -26699,7 +26699,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4355" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4368" } }, { @@ -26743,7 +26743,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4366" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4379" } }, { @@ -26833,7 +26833,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4377" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4390" } }, { @@ -26860,7 +26860,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4388" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4401" } } ] diff --git a/build/openrpc/gateway.json b/build/openrpc/gateway.json index faa2c8536..04a2df7d7 100644 --- a/build/openrpc/gateway.json +++ b/build/openrpc/gateway.json @@ -242,7 +242,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4399" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4412" } }, { @@ -473,7 +473,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4410" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4423" } }, { @@ -572,7 +572,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4421" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4434" } }, { @@ -604,7 +604,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4432" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4445" } }, { @@ -710,7 +710,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4443" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4456" } }, { @@ -803,7 +803,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4454" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4467" } }, { @@ -887,7 +887,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4465" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4478" } }, { @@ -987,7 +987,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4476" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4489" } }, { @@ -1043,7 +1043,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4487" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4500" } }, { @@ -1116,7 +1116,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4498" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4511" } }, { @@ -1189,7 +1189,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4509" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4522" } }, { @@ -1236,7 +1236,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4520" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4533" } }, { @@ -1268,7 +1268,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4531" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4544" } }, { @@ -1305,7 +1305,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4553" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4566" } }, { @@ -1352,7 +1352,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4564" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4577" } }, { @@ -1392,7 +1392,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4575" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4588" } }, { @@ -1439,7 +1439,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4586" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4599" } }, { @@ -1468,7 +1468,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4597" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4610" } }, { @@ -1605,7 +1605,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4608" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4621" } }, { @@ -1634,7 +1634,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4619" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4632" } }, { @@ -1688,7 +1688,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4630" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4643" } }, { @@ -1779,7 +1779,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4641" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4654" } }, { @@ -1807,7 +1807,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4652" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4665" } }, { @@ -1897,7 +1897,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4663" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4676" } }, { @@ -2153,7 +2153,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4674" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4687" } }, { @@ -2398,7 +2398,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4685" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4698" } }, { @@ -2454,7 +2454,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4696" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4709" } }, { @@ -2501,7 +2501,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4707" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4720" } }, { @@ -2599,7 +2599,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4718" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4731" } }, { @@ -2665,7 +2665,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4729" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4742" } }, { @@ -2731,7 +2731,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4740" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4753" } }, { @@ -2840,7 +2840,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4751" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4764" } }, { @@ -2898,7 +2898,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4762" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4775" } }, { @@ -3020,7 +3020,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4773" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4786" } }, { @@ -3207,7 +3207,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4784" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4797" } }, { @@ -3411,7 +3411,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4795" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4808" } }, { @@ -3502,7 +3502,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4806" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4819" } }, { @@ -3560,7 +3560,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4817" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4830" } }, { @@ -3818,7 +3818,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4828" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4841" } }, { @@ -4093,7 +4093,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4839" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4852" } }, { @@ -4121,7 +4121,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4850" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4863" } }, { @@ -4159,7 +4159,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4861" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4874" } }, { @@ -4267,7 +4267,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4872" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4885" } }, { @@ -4305,7 +4305,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4883" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4896" } }, { @@ -4334,7 +4334,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4894" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4907" } }, { @@ -4397,7 +4397,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4905" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4918" } }, { @@ -4460,7 +4460,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4916" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4929" } }, { @@ -4505,7 +4505,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4927" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4940" } }, { @@ -4627,7 +4627,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4938" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4951" } }, { @@ -4782,7 +4782,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4949" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4962" } }, { @@ -4836,7 +4836,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4960" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4973" } }, { @@ -4890,7 +4890,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4971" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4984" } }, { @@ -4992,7 +4992,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4982" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4995" } }, { @@ -5215,7 +5215,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L4993" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5006" } }, { @@ -5398,7 +5398,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5004" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5017" } }, { @@ -5592,7 +5592,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5015" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5028" } }, { @@ -5638,7 +5638,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5026" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5039" } }, { @@ -5788,7 +5788,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5037" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5050" } }, { @@ -5925,7 +5925,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5048" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5061" } }, { @@ -5993,7 +5993,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5059" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5072" } }, { @@ -6110,7 +6110,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5070" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5083" } }, { @@ -6201,7 +6201,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5081" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5094" } }, { @@ -6287,7 +6287,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5092" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5105" } }, { @@ -6314,7 +6314,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5103" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5116" } }, { @@ -6341,7 +6341,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5114" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5127" } }, { @@ -6409,7 +6409,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5125" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5138" } }, { @@ -6915,7 +6915,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5136" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5149" } }, { @@ -7012,7 +7012,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5147" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5160" } }, { @@ -7112,7 +7112,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5158" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5171" } }, { @@ -7212,7 +7212,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5169" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5182" } }, { @@ -7337,7 +7337,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5180" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5193" } }, { @@ -7446,7 +7446,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5191" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5204" } }, { @@ -7549,7 +7549,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5202" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5215" } }, { @@ -7679,7 +7679,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5213" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5226" } }, { @@ -7786,7 +7786,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5224" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5237" } }, { @@ -7847,7 +7847,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5235" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5248" } }, { @@ -7915,7 +7915,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5246" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5259" } }, { @@ -7996,7 +7996,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5257" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5270" } }, { @@ -8155,7 +8155,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5268" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5281" } }, { @@ -8248,7 +8248,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5279" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5292" } }, { @@ -8449,7 +8449,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5290" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5303" } }, { @@ -8560,7 +8560,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5301" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5314" } }, { @@ -8691,7 +8691,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5312" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5325" } }, { @@ -8777,7 +8777,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5323" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5336" } }, { @@ -8804,7 +8804,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5334" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5347" } }, { @@ -8857,7 +8857,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5345" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5358" } }, { @@ -8945,7 +8945,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5356" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5369" } }, { @@ -9396,7 +9396,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5367" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5380" } }, { @@ -9563,7 +9563,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5378" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5391" } }, { @@ -9736,7 +9736,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5389" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5402" } }, { @@ -9804,7 +9804,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5400" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5413" } }, { @@ -9872,7 +9872,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5411" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5424" } }, { @@ -10033,7 +10033,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5422" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5435" } }, { @@ -10078,7 +10078,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5444" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5457" } }, { @@ -10123,7 +10123,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5455" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5468" } }, { @@ -10150,7 +10150,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5466" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5479" } } ] diff --git a/build/openrpc/miner.json b/build/openrpc/miner.json index bc374e63e..b94f96abf 100644 --- a/build/openrpc/miner.json +++ b/build/openrpc/miner.json @@ -30,7 +30,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5752" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5765" } }, { @@ -109,7 +109,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5763" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5776" } }, { @@ -155,7 +155,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5774" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5787" } }, { @@ -203,7 +203,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5785" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5798" } }, { @@ -251,7 +251,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5796" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5809" } }, { @@ -354,7 +354,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5807" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5820" } }, { @@ -428,7 +428,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5818" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5831" } }, { @@ -591,7 +591,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5829" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5842" } }, { @@ -742,7 +742,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5840" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5853" } }, { @@ -781,7 +781,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5851" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5864" } }, { @@ -833,7 +833,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5862" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5875" } }, { @@ -872,7 +872,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5884" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5897" } }, { @@ -924,7 +924,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5895" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5908" } }, { @@ -996,7 +996,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5906" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5919" } }, { @@ -1035,7 +1035,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5917" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5930" } }, { @@ -1074,7 +1074,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5928" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5941" } }, { @@ -1101,7 +1101,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5939" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5952" } }, { @@ -1128,7 +1128,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5950" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5963" } }, { @@ -1155,7 +1155,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5961" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5974" } }, { @@ -1182,7 +1182,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5972" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5985" } }, { @@ -1209,7 +1209,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5983" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5996" } }, { @@ -1236,7 +1236,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L5994" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6007" } }, { @@ -1294,7 +1294,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6005" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6018" } }, { @@ -1421,7 +1421,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6016" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6029" } }, { @@ -1461,7 +1461,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6027" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6040" } }, { @@ -1500,7 +1500,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6038" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6051" } }, { @@ -1539,7 +1539,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6049" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6062" } }, { @@ -1578,7 +1578,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6060" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6073" } }, { @@ -1617,7 +1617,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6071" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6084" } }, { @@ -1656,7 +1656,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6082" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6095" } }, { @@ -1695,7 +1695,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6093" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6106" } }, { @@ -1747,7 +1747,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6104" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6117" } }, { @@ -1770,7 +1770,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6115" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6128" } }, { @@ -1813,7 +1813,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6126" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6139" } }, { @@ -1884,7 +1884,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6137" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6150" } }, { @@ -2265,7 +2265,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6148" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6161" } }, { @@ -2364,7 +2364,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6170" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6183" } }, { @@ -2415,7 +2415,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6192" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6205" } }, { @@ -2473,7 +2473,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6203" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6216" } }, { @@ -2616,7 +2616,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6214" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6227" } }, { @@ -2743,7 +2743,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6225" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6238" } }, { @@ -3007,7 +3007,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6236" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6249" } }, { @@ -3044,7 +3044,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6247" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6260" } }, { @@ -3182,7 +3182,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6258" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6271" } }, { @@ -3205,7 +3205,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6269" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6282" } }, { @@ -3276,7 +3276,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6280" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6293" } }, { @@ -3319,7 +3319,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6291" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6304" } }, { @@ -3426,7 +3426,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6302" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6315" } }, { @@ -3489,7 +3489,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6313" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6326" } }, { @@ -3521,7 +3521,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6324" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6337" } }, { @@ -3609,7 +3609,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6335" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6348" } }, { @@ -3700,7 +3700,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6346" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6359" } }, { @@ -3740,7 +3740,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6357" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6370" } }, { @@ -3780,7 +3780,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6368" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6381" } }, { @@ -3821,7 +3821,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6379" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6392" } }, { @@ -3889,7 +3889,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6390" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6403" } }, { @@ -4020,7 +4020,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6401" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6414" } }, { @@ -4151,7 +4151,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6412" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6425" } }, { @@ -4251,7 +4251,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6423" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6436" } }, { @@ -4351,7 +4351,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6434" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6447" } }, { @@ -4451,7 +4451,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6445" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6458" } }, { @@ -4551,7 +4551,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6456" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6469" } }, { @@ -4651,7 +4651,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6467" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6480" } }, { @@ -4751,7 +4751,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6478" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6491" } }, { @@ -4875,7 +4875,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6489" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6502" } }, { @@ -4999,7 +4999,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6500" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6513" } }, { @@ -5114,7 +5114,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6511" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6524" } }, { @@ -5214,7 +5214,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6522" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6535" } }, { @@ -5347,7 +5347,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6533" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6546" } }, { @@ -5471,7 +5471,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6544" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6557" } }, { @@ -5595,7 +5595,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6555" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6568" } }, { @@ -5719,7 +5719,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6566" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6579" } }, { @@ -5852,7 +5852,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6577" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6590" } }, { @@ -5952,7 +5952,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6588" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6601" } }, { @@ -5993,7 +5993,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6599" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6612" } }, { @@ -6065,7 +6065,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6610" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6623" } }, { @@ -6115,7 +6115,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6621" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6634" } }, { @@ -6159,7 +6159,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6632" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6645" } }, { @@ -6200,7 +6200,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6643" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6656" } }, { @@ -6444,7 +6444,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6654" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6667" } }, { @@ -6518,7 +6518,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6665" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6678" } }, { @@ -6568,7 +6568,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6676" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6689" } }, { @@ -6597,7 +6597,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6687" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6700" } }, { @@ -6626,7 +6626,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6698" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6711" } }, { @@ -6682,7 +6682,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6709" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6722" } }, { @@ -6705,7 +6705,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6720" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6733" } }, { @@ -6765,7 +6765,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6731" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6744" } }, { @@ -6804,7 +6804,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6742" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6755" } }, { @@ -6844,7 +6844,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6753" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6766" } }, { @@ -6917,7 +6917,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6764" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6777" } }, { @@ -6981,7 +6981,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6775" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6788" } }, { @@ -7044,7 +7044,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6786" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6799" } }, { @@ -7094,7 +7094,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6797" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6810" } }, { @@ -7653,7 +7653,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6808" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6821" } }, { @@ -7694,7 +7694,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6819" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6832" } }, { @@ -7735,7 +7735,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6830" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6843" } }, { @@ -7776,7 +7776,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6841" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6854" } }, { @@ -7817,7 +7817,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6852" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6865" } }, { @@ -7858,7 +7858,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6863" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6876" } }, { @@ -7889,7 +7889,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6874" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6887" } }, { @@ -7939,7 +7939,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6885" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6898" } }, { @@ -7980,7 +7980,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6896" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6909" } }, { @@ -8019,7 +8019,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6907" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6920" } }, { @@ -8083,7 +8083,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6918" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6931" } }, { @@ -8141,7 +8141,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6929" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6942" } }, { @@ -8588,7 +8588,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6940" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6953" } }, { @@ -8624,7 +8624,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6951" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6964" } }, { @@ -8767,7 +8767,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6962" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6975" } }, { @@ -8823,7 +8823,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6973" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6986" } }, { @@ -8862,7 +8862,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6984" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6997" } }, { @@ -9039,7 +9039,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L6995" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7008" } }, { @@ -9091,7 +9091,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7006" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7019" } }, { @@ -9283,7 +9283,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7017" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7030" } }, { @@ -9383,7 +9383,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7028" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7041" } }, { @@ -9437,7 +9437,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7039" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7052" } }, { @@ -9476,7 +9476,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7050" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7063" } }, { @@ -9561,7 +9561,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7061" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7074" } }, { @@ -9755,7 +9755,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7072" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7085" } }, { @@ -9853,7 +9853,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7083" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7096" } }, { @@ -9985,7 +9985,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7094" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7107" } }, { @@ -10039,7 +10039,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7105" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7118" } }, { @@ -10073,7 +10073,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7116" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7129" } }, { @@ -10160,7 +10160,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7127" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7140" } }, { @@ -10214,7 +10214,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7138" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7151" } }, { @@ -10314,7 +10314,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7149" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7162" } }, { @@ -10391,7 +10391,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7160" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7173" } }, { @@ -10482,7 +10482,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7171" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7184" } }, { @@ -10521,7 +10521,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7182" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7195" } }, { @@ -10637,7 +10637,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7193" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7206" } }, { @@ -12737,7 +12737,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7204" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7217" } } ] diff --git a/build/openrpc/worker.json b/build/openrpc/worker.json index 220d2e1c9..b02ba6139 100644 --- a/build/openrpc/worker.json +++ b/build/openrpc/worker.json @@ -161,7 +161,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7292" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7305" } }, { @@ -252,7 +252,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7303" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7316" } }, { @@ -420,7 +420,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7314" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7327" } }, { @@ -447,7 +447,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7325" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7338" } }, { @@ -597,7 +597,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7336" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7349" } }, { @@ -700,7 +700,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7347" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7360" } }, { @@ -803,7 +803,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7358" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7371" } }, { @@ -925,7 +925,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7369" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7382" } }, { @@ -1135,7 +1135,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7380" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7393" } }, { @@ -1306,7 +1306,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7391" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7404" } }, { @@ -3350,7 +3350,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7402" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7415" } }, { @@ -3470,7 +3470,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7413" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7426" } }, { @@ -3531,7 +3531,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7424" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7437" } }, { @@ -3569,7 +3569,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7435" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7448" } }, { @@ -3729,7 +3729,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7446" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7459" } }, { @@ -3913,7 +3913,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7457" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7470" } }, { @@ -4054,7 +4054,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7468" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7481" } }, { @@ -4107,7 +4107,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7479" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7492" } }, { @@ -4250,7 +4250,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7490" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7503" } }, { @@ -4474,7 +4474,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7501" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7514" } }, { @@ -4601,7 +4601,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7512" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7525" } }, { @@ -4768,7 +4768,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7523" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7536" } }, { @@ -4895,7 +4895,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7534" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7547" } }, { @@ -4933,7 +4933,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7545" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7558" } }, { @@ -4972,7 +4972,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7556" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7569" } }, { @@ -4995,7 +4995,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7567" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7580" } }, { @@ -5034,7 +5034,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7578" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7591" } }, { @@ -5057,7 +5057,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7589" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7602" } }, { @@ -5096,7 +5096,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7600" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7613" } }, { @@ -5130,7 +5130,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7611" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7624" } }, { @@ -5184,7 +5184,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7622" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7635" } }, { @@ -5223,7 +5223,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7633" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7646" } }, { @@ -5262,7 +5262,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7644" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7657" } }, { @@ -5297,7 +5297,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7655" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7668" } }, { @@ -5477,7 +5477,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7666" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7679" } }, { @@ -5506,7 +5506,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7677" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7690" } }, { @@ -5529,7 +5529,7 @@ "deprecated": false, "externalDocs": { "description": "Github remote link", - "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7688" + "url": "https://github.com/filecoin-project/lotus/blob/master/api/proxy_gen.go#L7701" } } ] diff --git a/cmd/curio/rpc/rpc.go b/cmd/curio/rpc/rpc.go index f45e276c3..1b2bb25e6 100644 --- a/cmd/curio/rpc/rpc.go +++ b/cmd/curio/rpc/rpc.go @@ -9,9 +9,11 @@ import ( "net/http" "net/url" "os" + "path/filepath" "time" "github.com/gbrlsnchs/jwt/v3" + "github.com/google/uuid" "github.com/gorilla/mux" logging "github.com/ipfs/go-log/v2" "github.com/mitchellh/go-homedir" @@ -39,6 +41,8 @@ import ( "github.com/filecoin-project/lotus/storage/sealer/storiface" ) +const metaFile = "sectorstore.json" + var log = logging.Logger("curio/rpc") var permissioned = os.Getenv("LOTUS_DISABLE_AUTH_PERMISSIONED") != "1" @@ -162,6 +166,40 @@ func (p *CurioAPI) Shutdown(context.Context) error { return nil } +func (p *CurioAPI) StorageInit(ctx context.Context, path string, opts storiface.LocalStorageMeta) error { + path, err := homedir.Expand(path) + if err != nil { + return xerrors.Errorf("expanding local path: %w", err) + } + + if err := os.MkdirAll(path, 0755); err != nil { + if !os.IsExist(err) { + return err + } + } + _, err = os.Stat(filepath.Join(path, metaFile)) + if !os.IsNotExist(err) { + if err == nil { + return xerrors.Errorf("path is already initialized") + } + return err + } + if opts.ID == "" { + opts.ID = storiface.ID(uuid.New().String()) + } + if !(opts.CanStore || opts.CanSeal) { + return xerrors.Errorf("must specify at least one of --store or --seal") + } + b, err := json.MarshalIndent(opts, "", " ") + if err != nil { + return xerrors.Errorf("marshaling storage config: %w", err) + } + if err := os.WriteFile(filepath.Join(path, metaFile), b, 0644); err != nil { + return xerrors.Errorf("persisting storage metadata (%s): %w", filepath.Join(path, metaFile), err) + } + return nil +} + func (p *CurioAPI) StorageAddLocal(ctx context.Context, path string) error { path, err := homedir.Expand(path) if err != nil { diff --git a/cmd/curio/storage.go b/cmd/curio/storage.go index 9a073e037..2fa6d2d52 100644 --- a/cmd/curio/storage.go +++ b/cmd/curio/storage.go @@ -1,11 +1,8 @@ package main import ( - "encoding/json" "fmt" "math/bits" - "os" - "path/filepath" "sort" "strconv" "strings" @@ -28,8 +25,6 @@ import ( "github.com/filecoin-project/lotus/storage/sealer/storiface" ) -const metaFile = "sectorstore.json" - var storageCmd = &cli.Command{ Name: "storage", Usage: "manage sector storage", @@ -122,20 +117,6 @@ over time } if cctx.Bool("init") { - if err := os.MkdirAll(p, 0755); err != nil { - if !os.IsExist(err) { - return err - } - } - - _, err := os.Stat(filepath.Join(p, metaFile)) - if !os.IsNotExist(err) { - if err == nil { - return xerrors.Errorf("path is already initialized") - } - return err - } - var maxStor int64 if cctx.IsSet("max-storage") { maxStor, err = units.RAMInBytes(cctx.String("max-storage")) @@ -144,7 +125,7 @@ over time } } - cfg := &storiface.LocalStorageMeta{ + cfg := storiface.LocalStorageMeta{ ID: storiface.ID(uuid.New().String()), Weight: cctx.Uint64("weight"), CanSeal: cctx.Bool("seal"), @@ -158,13 +139,8 @@ over time return xerrors.Errorf("must specify at least one of --store or --seal") } - b, err := json.MarshalIndent(cfg, "", " ") - if err != nil { - return xerrors.Errorf("marshaling storage config: %w", err) - } - - if err := os.WriteFile(filepath.Join(p, metaFile), b, 0644); err != nil { - return xerrors.Errorf("persisting storage metadata (%s): %w", filepath.Join(p, metaFile), err) + if err := minerApi.StorageInit(ctx, p, cfg); err != nil { + return xerrors.Errorf("init storage: %w", err) } } diff --git a/documentation/en/api-v0-methods-curio.md b/documentation/en/api-v0-methods-curio.md index e94dca8b0..0bfe09af5 100644 --- a/documentation/en/api-v0-methods-curio.md +++ b/documentation/en/api-v0-methods-curio.md @@ -12,6 +12,7 @@ * [StorageDetachLocal](#StorageDetachLocal) * [StorageFindSector](#StorageFindSector) * [StorageInfo](#StorageInfo) + * [StorageInit](#StorageInit) * [StorageList](#StorageList) * [StorageLocal](#StorageLocal) * [StorageStat](#StorageStat) @@ -269,6 +270,45 @@ Response: } ``` +### StorageInit + + +Perms: admin + +Inputs: +```json +[ + "string value", + { + "ID": "76f1988b-ef30-4d7e-b3ec-9a627f4ba5a8", + "Weight": 42, + "CanSeal": true, + "CanStore": true, + "MaxStorage": 42, + "Groups": [ + "string value" + ], + "AllowTo": [ + "string value" + ], + "AllowTypes": [ + "string value" + ], + "DenyTypes": [ + "string value" + ], + "AllowMiners": [ + "string value" + ], + "DenyMiners": [ + "string value" + ] + } +] +``` + +Response: `{}` + ### StorageList From 5e1d8f661e8db7f384c4af23274303f6186d42e7 Mon Sep 17 00:00:00 2001 From: "Andrew Jackson (Ajax)" Date: Fri, 26 Apr 2024 04:11:42 -0500 Subject: [PATCH 383/394] feat: curio: simpler reservation release logic (#11900) * simpler release logic * oops, plus simpler * simpler --- storage/paths/local.go | 63 +++++++++++++++++++----------------------- 1 file changed, 28 insertions(+), 35 deletions(-) diff --git a/storage/paths/local.go b/storage/paths/local.go index 7e955ccfe..244def377 100644 --- a/storage/paths/local.go +++ b/storage/paths/local.go @@ -461,42 +461,23 @@ func (st *Local) reportStorage(ctx context.Context) { } func (st *Local) Reserve(ctx context.Context, sid storiface.SectorRef, ft storiface.SectorFileType, storageIDs storiface.SectorPaths, overheadTab map[storiface.SectorFileType]int) (release func(), err error) { - ssize, err := sid.ProofType.SectorSize() + var ssize abi.SectorSize + ssize, err = sid.ProofType.SectorSize() if err != nil { return nil, err } + release = func() {} st.localLk.Lock() - var releaseCalled bool - - // double release debug guard - var firstDonebuf []byte - var releaseFuncs []func() - - release = func() { - for _, releaseFunc := range releaseFuncs { - releaseFunc() - } - - // debug guard against double release call - if releaseCalled { - curStack := make([]byte, 20480) - curStack = curStack[:runtime.Stack(curStack, false)] - - log.Errorw("double release call", "sector", sid, "fileType", ft, "prevStack", string(firstDonebuf), "curStack", string(curStack)) - } - - firstDonebuf = make([]byte, 20480) - firstDonebuf = firstDonebuf[:runtime.Stack(firstDonebuf, false)] - - releaseCalled = true - } - - cleanupOnError := func() { release() } defer func() { st.localLk.Unlock() - cleanupOnError() + if err != nil { + release() + release = func() {} + } else { + release = DoubleCallWrap(release) + } }() for _, fileType := range ft.AllSet() { @@ -531,21 +512,33 @@ func (st *Local) Reserve(ctx context.Context, sid storiface.SectorRef, ft storif p.reserved += overhead p.reservations[resID] = overhead - releaseFuncs = append(releaseFuncs, func() { + old_r := release + release = func() { + old_r() st.localLk.Lock() defer st.localLk.Unlock() - log.Debugw("reserve release", "id", id, "sector", sid, "fileType", fileType, "overhead", overhead, "reserved-before", p.reserved, "reserved-after", p.reserved-overhead) - p.reserved -= overhead delete(p.reservations, resID) - }) + } } - // no errors, don't cleanup, caller will call release - cleanupOnError = func() {} + return +} - return release, nil +// DoubleCallWrap wraps a function to make sure it's not called twice +func DoubleCallWrap(f func()) func() { + var stack []byte + return func() { + curStack := make([]byte, 20480) + curStack = curStack[:runtime.Stack(curStack, false)] + if len(stack) > 0 { + log.Warnf("double call from:\n%s\nBut originally from:", curStack, stack) + return + } + stack = curStack + f() + } } func (st *Local) AcquireSector(ctx context.Context, sid storiface.SectorRef, existing storiface.SectorFileType, allocate storiface.SectorFileType, pathType storiface.PathType, op storiface.AcquireMode, opts ...storiface.AcquireOption) (storiface.SectorPaths, storiface.SectorPaths, error) { From 0a28ce471ede8764d1e0a84842476f6eab886851 Mon Sep 17 00:00:00 2001 From: beck <34204218+beck-8@users.noreply.github.com> Date: Sat, 27 Apr 2024 17:17:00 +0800 Subject: [PATCH 384/394] fix NewLine (#11893) --- cli/filplus.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cli/filplus.go b/cli/filplus.go index 717fa0d83..cf37a10f3 100644 --- a/cli/filplus.go +++ b/cli/filplus.go @@ -330,7 +330,7 @@ var filplusListAllocationsCmd = &cli.Command{ tablewriter.Col(pieceSize), tablewriter.Col(tMin), tablewriter.Col(tMax), - tablewriter.NewLineCol(expr)) + tablewriter.Col(expr)) // populate it with content for _, alloc := range allocs { tw.Write(alloc) @@ -479,7 +479,7 @@ var filplusListClaimsCmd = &cli.Command{ tablewriter.Col(tMin), tablewriter.Col(tMax), tablewriter.Col(tStart), - tablewriter.NewLineCol(sector)) + tablewriter.Col(sector)) // populate it with content for _, alloc := range claimList { From ae2cc67f96553dca3e69cd2fa25d94c02e55a1dd Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Mon, 29 Apr 2024 12:19:58 +1000 Subject: [PATCH 385/394] fix(events): check for sync-in-progress (#11932) --- chain/events/filter/event.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/chain/events/filter/event.go b/chain/events/filter/event.go index 1669d840e..0accc551a 100644 --- a/chain/events/filter/event.go +++ b/chain/events/filter/event.go @@ -375,6 +375,10 @@ func (m *EventFilterManager) Revert(ctx context.Context, from, to *types.TipSet) func (m *EventFilterManager) Install(ctx context.Context, minHeight, maxHeight abi.ChainEpoch, tipsetCid cid.Cid, addresses []address.Address, keysWithCodec map[string][]types.ActorEventBlock, excludeReverted bool) (EventFilter, error) { m.mu.Lock() + if m.currentHeight == 0 { + // sync in progress, we haven't had an Apply + m.currentHeight = m.ChainStore.GetHeaviestTipSet().Height() + } currentHeight := m.currentHeight m.mu.Unlock() From 10877d2e662842af06f67ce04f7f3da318d2bb36 Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Fri, 26 Apr 2024 20:24:13 +1000 Subject: [PATCH 386/394] feat(events): adjust indexes in event index db to match query patterns Introduces a v4 migration that just adjusts indexes. Copies some improvements from https://github.com/filecoin-project/lotus/pull/11723 Closes: https://github.com/filecoin-project/lotus/issues/11909 --- chain/events/filter/index.go | 143 ++++++++++++++++++++++++++--------- 1 file changed, 107 insertions(+), 36 deletions(-) diff --git a/chain/events/filter/index.go b/chain/events/filter/index.go index 296d7c1d5..a9651224d 100644 --- a/chain/events/filter/index.go +++ b/chain/events/filter/index.go @@ -26,7 +26,7 @@ var pragmas = []string{ "PRAGMA temp_store = memory", "PRAGMA mmap_size = 30000000000", "PRAGMA page_size = 32768", - "PRAGMA auto_vacuum = NONE", + "PRAGMA auto_vacuum = NONE", // not useful until we implement GC "PRAGMA automatic_index = OFF", "PRAGMA journal_mode = WAL", "PRAGMA read_uncommitted = ON", @@ -45,8 +45,10 @@ var ddls = []string{ reverted INTEGER NOT NULL )`, - createIndexEventHeightTipsetKey, createIndexEventEmitterAddr, + createIndexEventTipsetKeyCid, + createIndexEventHeight, + createIndexEventReverted, `CREATE TABLE IF NOT EXISTS event_entry ( event_id INTEGER, @@ -57,7 +59,9 @@ var ddls = []string{ value BLOB NOT NULL )`, - createIndexEventEntryKey, + createIndexEventEntryIndexedKey, + createIndexEventEntryCodecValue, + createIndexEventEntryEventId, // metadata containing version of schema `CREATE TABLE IF NOT EXISTS _meta ( @@ -67,6 +71,7 @@ var ddls = []string{ `INSERT OR IGNORE INTO _meta (version) VALUES (1)`, `INSERT OR IGNORE INTO _meta (version) VALUES (2)`, `INSERT OR IGNORE INTO _meta (version) VALUES (3)`, + `INSERT OR IGNORE INTO _meta (version) VALUES (4)`, } var ( @@ -74,7 +79,7 @@ var ( ) const ( - schemaVersion = 3 + schemaVersion = 4 eventExists = `SELECT MAX(id) FROM event WHERE height=? AND tipset_key=? AND tipset_key_cid=? AND emitter_addr=? AND event_index=? AND message_cid=? AND message_index=?` insertEvent = `INSERT OR IGNORE INTO event(height, tipset_key, tipset_key_cid, emitter_addr, event_index, message_cid, message_index, reverted) VALUES(?, ?, ?, ?, ?, ?, ?, ?)` @@ -82,9 +87,14 @@ const ( revertEventsInTipset = `UPDATE event SET reverted=true WHERE height=? AND tipset_key=?` restoreEvent = `UPDATE event SET reverted=false WHERE height=? AND tipset_key=? AND tipset_key_cid=? AND emitter_addr=? AND event_index=? AND message_cid=? AND message_index=?` - createIndexEventHeightTipsetKey = `CREATE INDEX IF NOT EXISTS height_tipset_key ON event (height,tipset_key)` - createIndexEventEmitterAddr = `CREATE INDEX IF NOT EXISTS event_emitter_addr ON event (emitter_addr)` - createIndexEventEntryKey = `CREATE INDEX IF NOT EXISTS event_entry_key_index ON event_entry (key)` + createIndexEventEmitterAddr = `CREATE INDEX IF NOT EXISTS event_emitter_addr ON event (emitter_addr)` + createIndexEventTipsetKeyCid = `CREATE INDEX IF NOT EXISTS event_tipset_key_cid ON event (tipset_key_cid);` + createIndexEventHeight = `CREATE INDEX IF NOT EXISTS event_height ON event (height);` + createIndexEventReverted = `CREATE INDEX IF NOT EXISTS event_reverted ON event (reverted);` + + createIndexEventEntryIndexedKey = `CREATE INDEX IF NOT EXISTS event_entry_indexed_key ON event_entry (indexed, key);` + createIndexEventEntryCodecValue = `CREATE INDEX IF NOT EXISTS event_entry_codec_value ON event_entry (codec, value);` + createIndexEventEntryEventId = `CREATE INDEX IF NOT EXISTS event_entry_event_id ON event_entry(event_id);` ) type EventIndex struct { @@ -237,7 +247,7 @@ func (ei *EventIndex) migrateToVersion2(ctx context.Context, chainStore *store.C if err != nil { return xerrors.Errorf("rows affected: %w", err) } - log.Infof("cleaned up %d entries that had deleted events\n", nrRowsAffected) + log.Infof("Cleaned up %d entries that had deleted events\n", nrRowsAffected) // drop the temporary indices after the migration _, err = tx.ExecContext(ctx, "DROP INDEX IF EXISTS tmp_tipset_key_cid") @@ -249,11 +259,9 @@ func (ei *EventIndex) migrateToVersion2(ctx context.Context, chainStore *store.C return xerrors.Errorf("drop index tmp_height_tipset_key_cid: %w", err) } - // create the final index on event.height and event.tipset_key - _, err = tx.ExecContext(ctx, createIndexEventHeightTipsetKey) - if err != nil { - return xerrors.Errorf("create index height_tipset_key: %w", err) - } + // original v2 migration introduced an index: + // CREATE INDEX IF NOT EXISTS height_tipset_key ON event (height,tipset_key) + // which has subsequently been removed in v4, so it's omitted here // increment the schema version to 2 in _meta table. _, err = tx.ExecContext(ctx, "INSERT OR IGNORE INTO _meta (version) VALUES (2)") @@ -266,20 +274,7 @@ func (ei *EventIndex) migrateToVersion2(ctx context.Context, chainStore *store.C return xerrors.Errorf("commit transaction: %w", err) } - // during the migration, we have likely increased the WAL size a lot, so lets do some - // simple DB administration to free up space (VACUUM followed by truncating the WAL file) - // as this would be a good time to do it when no other writes are happening - log.Infof("Performing DB vacuum and wal checkpointing to free up space after the migration") - _, err = ei.db.ExecContext(ctx, "VACUUM") - if err != nil { - log.Warnf("error vacuuming database: %s", err) - } - _, err = ei.db.ExecContext(ctx, "PRAGMA wal_checkpoint(TRUNCATE)") - if err != nil { - log.Warnf("error checkpointing wal: %s", err) - } - - log.Infof("Successfully migrated events to version 2 in %s", time.Since(now)) + log.Infof("Successfully migrated event index from version 1 to version 2 in %s", time.Since(now)) return nil } @@ -301,11 +296,9 @@ func (ei *EventIndex) migrateToVersion3(ctx context.Context) error { return xerrors.Errorf("create index event_emitter_addr: %w", err) } - // create index on event_entry.key index. - _, err = tx.ExecContext(ctx, createIndexEventEntryKey) - if err != nil { - return xerrors.Errorf("create index event_entry_key_index: %w", err) - } + // original v3 migration introduced an index: + // CREATE INDEX IF NOT EXISTS event_entry_key_index ON event_entry (key) + // which has subsequently been removed in v4, so it's omitted here // increment the schema version to 3 in _meta table. _, err = tx.ExecContext(ctx, "INSERT OR IGNORE INTO _meta (version) VALUES (3)") @@ -317,10 +310,78 @@ func (ei *EventIndex) migrateToVersion3(ctx context.Context) error { if err != nil { return xerrors.Errorf("commit transaction: %w", err) } + log.Infof("Successfully migrated event index from version 2 to version 3 in %s", time.Since(now)) + return nil +} + +// migrateToVersion4 migrates the schema from version 3 to version 4 by adjusting indexes to match +// the query patterns of the event filter. +// +// First it drops indexes introduced in previous migrations: +// 1. the index on the event.height and event.tipset_key columns +// 2. the index on the event_entry.key column +// +// And then creating the following indices: +// 1. an index on the event.tipset_key_cid column +// 2. an index on the event.height column +// 3. an index on the event.reverted column +// 4. an index on the event_entry.indexed and event_entry.key columns +// 5. an index on the event_entry.codec and event_entry.value columns +// 6. an index on the event_entry.event_id column +func (ei *EventIndex) migrateToVersion4(ctx context.Context) error { + now := time.Now() + + tx, err := ei.db.BeginTx(ctx, nil) + if err != nil { + return xerrors.Errorf("begin transaction: %w", err) + } + defer func() { _ = tx.Rollback() }() + + for _, create := range []struct { + desc string + query string + }{ + {"drop index height_tipset_key", "DROP INDEX IF EXISTS height_tipset_key;"}, + {"drop index event_entry_key_index", "DROP INDEX IF EXISTS event_entry_key_index;"}, + {"create index event_tipset_key_cid", createIndexEventTipsetKeyCid}, + {"create index event_height", createIndexEventHeight}, + {"create index event_reverted", createIndexEventReverted}, + {"create index event_entry_indexed_key", createIndexEventEntryIndexedKey}, + {"create index event_entry_codec_value", createIndexEventEntryCodecValue}, + {"create index event_entry_event_id", createIndexEventEntryEventId}, + } { + _, err = tx.ExecContext(ctx, create.query) + if err != nil { + return xerrors.Errorf("%s: %w", create.desc, err) + } + } + + err = tx.Commit() + if err != nil { + return xerrors.Errorf("commit transaction: %w", err) + } + + ei.vacuumDBAndCheckpointWAL(ctx) + log.Infof("Successfully migrated events to version 3 in %s", time.Since(now)) return nil } +func (ei *EventIndex) vacuumDBAndCheckpointWAL(ctx context.Context) { + // During the large migrations, we have likely increased the WAL size a lot, so lets do some + // simple DB administration to free up space (VACUUM followed by truncating the WAL file) + // as this would be a good time to do it when no other writes are happening. + log.Infof("Performing DB vacuum and wal checkpointing to free up space after the migration") + _, err := ei.db.ExecContext(ctx, "VACUUM") + if err != nil { + log.Warnf("error vacuuming database: %s", err) + } + _, err = ei.db.ExecContext(ctx, "PRAGMA wal_checkpoint(TRUNCATE)") + if err != nil { + log.Warnf("error checkpointing wal: %s", err) + } +} + func NewEventIndex(ctx context.Context, path string, chainStore *store.ChainStore) (*EventIndex, error) { db, err := sql.Open("sqlite3", path+"?mode=rwc") if err != nil { @@ -358,25 +419,35 @@ func NewEventIndex(ctx context.Context, path string, chainStore *store.ChainStor } if version == 1 { - log.Infof("upgrading event index from version 1 to version 2") + log.Infof("Upgrading event index from version 1 to version 2") err = eventIndex.migrateToVersion2(ctx, chainStore) if err != nil { _ = db.Close() - return nil, xerrors.Errorf("could not migrate sql data to version 2: %w", err) + return nil, xerrors.Errorf("could not migrate event index schema from version 1 to version 2: %w", err) } version = 2 } if version == 2 { - log.Infof("upgrading event index from version 2 to version 3") + log.Infof("Upgrading event index from version 2 to version 3") err = eventIndex.migrateToVersion3(ctx) if err != nil { _ = db.Close() - return nil, xerrors.Errorf("could not migrate sql data to version 2: %w", err) + return nil, xerrors.Errorf("could not migrate event index schema from version 2 to version 3: %w", err) } version = 3 } + if version == 3 { + log.Infof("Upgrading event index from version 3 to version 4") + err = eventIndex.migrateToVersion4(ctx) + if err != nil { + _ = db.Close() + return nil, xerrors.Errorf("could not migrate event index schema from version 3 to version 4: %w", err) + } + version = 4 + } + if version != schemaVersion { _ = db.Close() return nil, xerrors.Errorf("invalid database version: got %d, expected %d", version, schemaVersion) From 15f5f4745d11773fd2b361d6fbeba66e8bb68a92 Mon Sep 17 00:00:00 2001 From: 0x5459 <0x5459@protonmail.com> Date: Mon, 29 Apr 2024 18:21:40 +0800 Subject: [PATCH 387/394] fix(pipeline): should return if error occurred when get network version (#11902) --- storage/pipeline/states_replica_update.go | 1 + 1 file changed, 1 insertion(+) diff --git a/storage/pipeline/states_replica_update.go b/storage/pipeline/states_replica_update.go index a0d92891c..85e3fabee 100644 --- a/storage/pipeline/states_replica_update.go +++ b/storage/pipeline/states_replica_update.go @@ -174,6 +174,7 @@ func (m *Sealing) handleSubmitReplicaUpdate(ctx statemachine.Context, sector Sec nv, err := m.Api.StateNetworkVersion(ctx.Context(), ts.Key()) if err != nil { log.Errorf("failed to get network version: %+v", err) + return ctx.Send(SectorSubmitReplicaUpdateFailed{}) } pams, deals, err := m.processPieces(ctx.Context(), sector, nv >= network.Version22) From aa76a450862559c16bb187968e20b6aaf51d247e Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Mon, 29 Apr 2024 14:06:27 +1000 Subject: [PATCH 388/394] fix(events): correct log msg for v4 events index db migration --- chain/events/filter/index.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chain/events/filter/index.go b/chain/events/filter/index.go index a9651224d..75f5b1c30 100644 --- a/chain/events/filter/index.go +++ b/chain/events/filter/index.go @@ -363,7 +363,7 @@ func (ei *EventIndex) migrateToVersion4(ctx context.Context) error { ei.vacuumDBAndCheckpointWAL(ctx) - log.Infof("Successfully migrated events to version 3 in %s", time.Since(now)) + log.Infof("Successfully migrated event index from version 3 to version 4 in %s", time.Since(now)) return nil } From cee77aa41527239a93f851a8eefea4d698483dd0 Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Tue, 30 Apr 2024 13:49:46 +1000 Subject: [PATCH 389/394] chore: remove duplicate words in strings and comments --- api/api_full.go | 2 +- api/v0api/full.go | 2 +- build/builtin_actors.go | 4 ++-- build/openrpc/full.json | 2 +- chain/events/filter/index.go | 2 +- chain/messagepool/selection.go | 2 +- chain/store/checkpoint_test.go | 2 +- documentation/en/api-v0-methods.md | 2 +- documentation/en/api-v1-unstable-methods.md | 2 +- journal/alerting/alerts.go | 2 +- metrics/metrics.go | 8 ++++---- 11 files changed, 15 insertions(+), 15 deletions(-) diff --git a/api/api_full.go b/api/api_full.go index 85233a712..bbfcae0a2 100644 --- a/api/api_full.go +++ b/api/api_full.go @@ -335,7 +335,7 @@ type FullNode interface { WalletVerify(context.Context, address.Address, []byte, *crypto.Signature) (bool, error) //perm:read // WalletDefaultAddress returns the address marked as default in the wallet. WalletDefaultAddress(context.Context) (address.Address, error) //perm:write - // WalletSetDefault marks the given address as as the default one. + // WalletSetDefault marks the given address as the default one. WalletSetDefault(context.Context, address.Address) error //perm:write // WalletExport returns the private key of an address in the wallet. WalletExport(context.Context, address.Address) (*types.KeyInfo, error) //perm:admin diff --git a/api/v0api/full.go b/api/v0api/full.go index db84ddc87..b61fc1570 100644 --- a/api/v0api/full.go +++ b/api/v0api/full.go @@ -293,7 +293,7 @@ type FullNode interface { WalletVerify(context.Context, address.Address, []byte, *crypto.Signature) (bool, error) //perm:read // WalletDefaultAddress returns the address marked as default in the wallet. WalletDefaultAddress(context.Context) (address.Address, error) //perm:write - // WalletSetDefault marks the given address as as the default one. + // WalletSetDefault marks the given address as the default one. WalletSetDefault(context.Context, address.Address) error //perm:write // WalletExport returns the private key of an address in the wallet. WalletExport(context.Context, address.Address) (*types.KeyInfo, error) //perm:admin diff --git a/build/builtin_actors.go b/build/builtin_actors.go index 6aace0bec..1cc43c374 100644 --- a/build/builtin_actors.go +++ b/build/builtin_actors.go @@ -145,10 +145,10 @@ func readEmbeddedBuiltinActorsMetadata(bundle string) ([]*BuiltinActorsMetadata, ) if !strings.HasPrefix(bundle, "v") { - return nil, xerrors.Errorf("bundle bundle '%q' doesn't start with a 'v'", bundle) + return nil, xerrors.Errorf("bundle '%q' doesn't start with a 'v'", bundle) } if !strings.HasSuffix(bundle, archiveExt) { - return nil, xerrors.Errorf("bundle bundle '%q' doesn't end with '%s'", bundle, archiveExt) + return nil, xerrors.Errorf("bundle '%q' doesn't end with '%s'", bundle, archiveExt) } version, err := strconv.ParseInt(bundle[1:len(bundle)-len(archiveExt)], 10, 0) if err != nil { diff --git a/build/openrpc/full.json b/build/openrpc/full.json index 5d422f283..7dabd7752 100644 --- a/build/openrpc/full.json +++ b/build/openrpc/full.json @@ -26382,7 +26382,7 @@ { "name": "Filecoin.WalletSetDefault", "description": "```go\nfunc (s *FullNodeStruct) WalletSetDefault(p0 context.Context, p1 address.Address) error {\n\tif s.Internal.WalletSetDefault == nil {\n\t\treturn ErrNotSupported\n\t}\n\treturn s.Internal.WalletSetDefault(p0, p1)\n}\n```", - "summary": "WalletSetDefault marks the given address as as the default one.\n", + "summary": "WalletSetDefault marks the given address as the default one.\n", "paramStructure": "by-position", "params": [ { diff --git a/chain/events/filter/index.go b/chain/events/filter/index.go index 75f5b1c30..7b3cae969 100644 --- a/chain/events/filter/index.go +++ b/chain/events/filter/index.go @@ -478,7 +478,7 @@ func (ei *EventIndex) CollectEvents(ctx context.Context, te *TipSetEvents, rever // rollback the transaction (a no-op if the transaction was already committed) defer func() { _ = tx.Rollback() }() - // lets handle the revert case first, since its simpler and we can simply mark all events events in this tipset as reverted and return + // lets handle the revert case first, since its simpler and we can simply mark all events in this tipset as reverted and return if revert { _, err = tx.Stmt(ei.stmtRevertEventsInTipset).Exec(te.msgTs.Height(), te.msgTs.Key().Bytes()) if err != nil { diff --git a/chain/messagepool/selection.go b/chain/messagepool/selection.go index 163bd76f9..0d0ed3cba 100644 --- a/chain/messagepool/selection.go +++ b/chain/messagepool/selection.go @@ -400,7 +400,7 @@ tailLoop: continue tailLoop } - // the merge loop ended after processing all the chains and we we probably have still + // the merge loop ended after processing all the chains and we probably have still // gas to spare; end the loop. break } diff --git a/chain/store/checkpoint_test.go b/chain/store/checkpoint_test.go index a30c4d860..8b775d334 100644 --- a/chain/store/checkpoint_test.go +++ b/chain/store/checkpoint_test.go @@ -52,7 +52,7 @@ func TestChainCheckpoint(t *testing.T) { head = cs.GetHeaviestTipSet() require.True(t, head.Equals(checkpoint)) - // Let the second miner miner mine a fork + // Let the second miner mine a fork last = checkpointParents for i := 0; i < 4; i++ { ts, err := cg.NextTipSetFromMiners(last, cg.Miners[1:], 0) diff --git a/documentation/en/api-v0-methods.md b/documentation/en/api-v0-methods.md index eedfd227e..9a8c4821b 100644 --- a/documentation/en/api-v0-methods.md +++ b/documentation/en/api-v0-methods.md @@ -7594,7 +7594,7 @@ Inputs: Response: `"f01234"` ### WalletSetDefault -WalletSetDefault marks the given address as as the default one. +WalletSetDefault marks the given address as the default one. Perms: write diff --git a/documentation/en/api-v1-unstable-methods.md b/documentation/en/api-v1-unstable-methods.md index dee290b34..8dab4f35f 100644 --- a/documentation/en/api-v1-unstable-methods.md +++ b/documentation/en/api-v1-unstable-methods.md @@ -9284,7 +9284,7 @@ Inputs: Response: `"f01234"` ### WalletSetDefault -WalletSetDefault marks the given address as as the default one. +WalletSetDefault marks the given address as the default one. Perms: write diff --git a/journal/alerting/alerts.go b/journal/alerting/alerts.go index 290d06e2a..1d6dd0a5c 100644 --- a/journal/alerting/alerts.go +++ b/journal/alerting/alerts.go @@ -95,7 +95,7 @@ func (a *Alerting) update(at AlertType, message interface{}, upd func(Alert, jso }{ AlertError: err.Error(), }) - log.Errorw("marshaling marshaling error failed", "type", at, "error", err) + log.Errorw("marshaling error failed", "type", at, "error", err) } a.alerts[at] = upd(alert, rawMsg) diff --git a/metrics/metrics.go b/metrics/metrics.go index 5d6ba79ec..85b9d82ec 100644 --- a/metrics/metrics.go +++ b/metrics/metrics.go @@ -190,11 +190,11 @@ var ( RcmgrAllowPeer = stats.Int64("rcmgr/allow_peer", "Number of allowed peer connections", stats.UnitDimensionless) RcmgrBlockPeer = stats.Int64("rcmgr/block_peer", "Number of blocked peer connections", stats.UnitDimensionless) RcmgrAllowProto = stats.Int64("rcmgr/allow_proto", "Number of allowed streams attached to a protocol", stats.UnitDimensionless) - RcmgrBlockProto = stats.Int64("rcmgr/block_proto", "Number of blocked blocked streams attached to a protocol", stats.UnitDimensionless) - RcmgrBlockProtoPeer = stats.Int64("rcmgr/block_proto", "Number of blocked blocked streams attached to a protocol for a specific peer", stats.UnitDimensionless) + RcmgrBlockProto = stats.Int64("rcmgr/block_proto", "Number of blocked streams attached to a protocol", stats.UnitDimensionless) + RcmgrBlockProtoPeer = stats.Int64("rcmgr/block_proto", "Number of blocked streams attached to a protocol for a specific peer", stats.UnitDimensionless) RcmgrAllowSvc = stats.Int64("rcmgr/allow_svc", "Number of allowed streams attached to a service", stats.UnitDimensionless) - RcmgrBlockSvc = stats.Int64("rcmgr/block_svc", "Number of blocked blocked streams attached to a service", stats.UnitDimensionless) - RcmgrBlockSvcPeer = stats.Int64("rcmgr/block_svc", "Number of blocked blocked streams attached to a service for a specific peer", stats.UnitDimensionless) + RcmgrBlockSvc = stats.Int64("rcmgr/block_svc", "Number of blocked streams attached to a service", stats.UnitDimensionless) + RcmgrBlockSvcPeer = stats.Int64("rcmgr/block_svc", "Number of blocked streams attached to a service for a specific peer", stats.UnitDimensionless) RcmgrAllowMem = stats.Int64("rcmgr/allow_mem", "Number of allowed memory reservations", stats.UnitDimensionless) RcmgrBlockMem = stats.Int64("rcmgr/block_mem", "Number of blocked memory reservations", stats.UnitDimensionless) From 46992f0fd27f42a0407f32b02364ef7a80c9eefe Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Tue, 30 Apr 2024 15:40:19 +1000 Subject: [PATCH 390/394] fix(events): register events index db migration v4 --- chain/events/filter/index.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/chain/events/filter/index.go b/chain/events/filter/index.go index 7b3cae969..9a8f8bca4 100644 --- a/chain/events/filter/index.go +++ b/chain/events/filter/index.go @@ -356,6 +356,10 @@ func (ei *EventIndex) migrateToVersion4(ctx context.Context) error { } } + if _, err = tx.Exec("INSERT OR IGNORE INTO _meta (version) VALUES (4)"); err != nil { + return xerrors.Errorf("increment _meta version: %w", err) + } + err = tx.Commit() if err != nil { return xerrors.Errorf("commit transaction: %w", err) From 914a65cea642e6278b0f134291527b416b927e8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 30 Apr 2024 11:16:44 +0200 Subject: [PATCH 391/394] fix: curio seal: Failed commit retry strategy (#11870) * ffi: improved-error-handling * curio seal: Failed commit retry strategy * use master ffi * mod tidy --- curiosrc/seal/task_porep.go | 52 +++++++++++++++++++++++++++++++++++++ extern/filecoin-ffi | 2 +- go.sum | 6 ++++- 3 files changed, 58 insertions(+), 2 deletions(-) diff --git a/curiosrc/seal/task_porep.go b/curiosrc/seal/task_porep.go index fb03ce59b..58e307bc0 100644 --- a/curiosrc/seal/task_porep.go +++ b/curiosrc/seal/task_porep.go @@ -3,6 +3,7 @@ package seal import ( "bytes" "context" + "strings" "github.com/ipfs/go-cid" "golang.org/x/xerrors" @@ -111,6 +112,15 @@ func (p *PoRepTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (done proof, err := p.sc.PoRepSnark(ctx, sr, sealed, unsealed, sectorParams.TicketValue, abi.InteractiveSealRandomness(rand)) if err != nil { + end, err := p.recoverErrors(ctx, sectorParams.SpID, sectorParams.SectorNumber, err) + if err != nil { + return false, xerrors.Errorf("recover errors: %w", err) + } + if end { + // done, but the error handling has stored a different than success state + return true, nil + } + return false, xerrors.Errorf("failed to compute seal proof: %w", err) } @@ -161,4 +171,46 @@ func (p *PoRepTask) Adder(taskFunc harmonytask.AddTaskFunc) { p.sp.pollers[pollerPoRep].Set(taskFunc) } +func (p *PoRepTask) recoverErrors(ctx context.Context, spid, snum int64, cerr error) (end bool, err error) { + const ( + // rust-fil-proofs error strings + // https://github.com/filecoin-project/rust-fil-proofs/blob/3f018b51b6327b135830899d237a7ba181942d7e/storage-proofs-porep/src/stacked/vanilla/proof.rs#L454C1-L463 + errstrInvalidCommD = "Invalid comm_d detected at challenge_index" + errstrInvalidCommR = "Invalid comm_r detected at challenge_index" + errstrInvalidEncoding = "Invalid encoding proof generated at layer" + ) + + if cerr == nil { + return false, xerrors.Errorf("nil error") + } + + switch { + case strings.Contains(cerr.Error(), errstrInvalidCommD): + fallthrough + case strings.Contains(cerr.Error(), errstrInvalidCommR): + // todo: it might be more optimal to just retry the Trees compute first. + // Invalid CommD/R likely indicates a problem with the data computed in that step + // For now for simplicity just retry the whole thing + fallthrough + case strings.Contains(cerr.Error(), errstrInvalidEncoding): + n, err := p.db.Exec(ctx, `UPDATE sectors_sdr_pipeline + SET after_porep = FALSE, after_sdr = FALSE, after_tree_d = FALSE, + after_tree_r = FALSE, after_tree_c = FALSE + WHERE sp_id = $1 AND sector_number = $2`, + spid, snum) + if err != nil { + return false, xerrors.Errorf("store sdr success: updating pipeline: %w", err) + } + if n != 1 { + return false, xerrors.Errorf("store sdr success: updated %d rows", n) + } + + return true, nil + + default: + // if end is false the original error will be returned by the caller + return false, nil + } +} + var _ harmonytask.TaskInterface = &PoRepTask{} diff --git a/extern/filecoin-ffi b/extern/filecoin-ffi index b715c9403..ed08caaf8 160000 --- a/extern/filecoin-ffi +++ b/extern/filecoin-ffi @@ -1 +1 @@ -Subproject commit b715c9403faf919e95fdc702cd651e842f18d890 +Subproject commit ed08caaf8778e1b6def83efd37fce41574214353 diff --git a/go.sum b/go.sum index 52755ba03..19156912f 100644 --- a/go.sum +++ b/go.sum @@ -305,6 +305,7 @@ github.com/filecoin-project/go-amt-ipld/v3 v3.0.0/go.mod h1:Qa95YNAbtoVCTSVtX38a github.com/filecoin-project/go-amt-ipld/v3 v3.1.0 h1:ZNJ9tEG5bE72vBWYiuh5bkxJVM3ViHNOmQ7qew9n6RE= github.com/filecoin-project/go-amt-ipld/v3 v3.1.0/go.mod h1:UjM2QhDFrrjD5s1CdnkJkat4ga+LqZBZgTMniypABRo= github.com/filecoin-project/go-amt-ipld/v4 v4.0.0/go.mod h1:gF053YQ4BIpzTNDoEwHZas7U3oAwncDVGvOHyY8oDpE= +github.com/filecoin-project/go-amt-ipld/v4 v4.2.0/go.mod h1:0eDVF7pROvxrsxvLJx+SJZXqRaXXcEPUcgb/rG0zGU4= github.com/filecoin-project/go-amt-ipld/v4 v4.3.0 h1:bY42N1gR0DqrLMCKUPzX1VhYVgXaETQm0Um4ohvyEP8= github.com/filecoin-project/go-amt-ipld/v4 v4.3.0/go.mod h1:39Ep/yBbF6xN94WevLG9qSbglBJepHa5zeEbAE1pYsc= github.com/filecoin-project/go-bitfield v0.2.0/go.mod h1:CNl9WG8hgR5mttCnUErjcQjGvuiZjRqK9rHVBsQF4oM= @@ -354,7 +355,7 @@ github.com/filecoin-project/go-state-types v0.0.0-20201102161440-c8033295a1fc/go github.com/filecoin-project/go-state-types v0.1.0/go.mod h1:ezYnPf0bNkTsDibL/psSz5dy4B5awOJ/E7P2Saeep8g= github.com/filecoin-project/go-state-types v0.1.6/go.mod h1:UwGVoMsULoCK+bWjEdd/xLCvLAQFBC7EDT477SKml+Q= github.com/filecoin-project/go-state-types v0.1.10/go.mod h1:UwGVoMsULoCK+bWjEdd/xLCvLAQFBC7EDT477SKml+Q= -github.com/filecoin-project/go-state-types v0.11.2-0.20230712101859-8f37624fa540/go.mod h1:SyNPwTsU7I22gL2r0OAPcImvLoTVfgRwdK/Y5rR1zz8= +github.com/filecoin-project/go-state-types v0.13.1/go.mod h1:cHpOPup9H1g2T29dKHAjC2sc7/Ef5ypjuW9A3I+e9yY= github.com/filecoin-project/go-state-types v0.13.3 h1:9JPkC0E6HDtfHbaOIrFiCDzT/Z0jRTb9En4Y4Ci/b3w= github.com/filecoin-project/go-state-types v0.13.3/go.mod h1:cHpOPup9H1g2T29dKHAjC2sc7/Ef5ypjuW9A3I+e9yY= github.com/filecoin-project/go-statemachine v0.0.0-20200925024713-05bd7c71fbfe/go.mod h1:FGwQgZAt2Gh5mjlwJUlVB62JeYdo+if0xWxSEfBD9ig= @@ -953,6 +954,7 @@ github.com/klauspost/compress v1.17.6/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6K github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.6/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.2.3/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/koalacxr/quantile v0.0.1 h1:wAW+SQ286Erny9wOjVww96t8ws+x5Zj6AKHDULUK+o0= @@ -1681,6 +1683,7 @@ github.com/whyrusleeping/cbor-gen v0.0.0-20200826160007-0b9f6c5fb163/go.mod h1:f github.com/whyrusleeping/cbor-gen v0.0.0-20210118024343-169e9d70c0c2/go.mod h1:fgkXqYy7bV2cFeIEOkVTZS/WjXARfBqSH6Q2qHL33hQ= github.com/whyrusleeping/cbor-gen v0.0.0-20210303213153-67a261a1d291/go.mod h1:fgkXqYy7bV2cFeIEOkVTZS/WjXARfBqSH6Q2qHL33hQ= github.com/whyrusleeping/cbor-gen v0.0.0-20220323183124-98fa8256a799/go.mod h1:fgkXqYy7bV2cFeIEOkVTZS/WjXARfBqSH6Q2qHL33hQ= +github.com/whyrusleeping/cbor-gen v0.1.0/go.mod h1:pM99HXyEbSQHcosHc0iW7YFmwnscr+t9Te4ibko05so= github.com/whyrusleeping/cbor-gen v0.1.1 h1:eKfcJIoxivjMtwfCfmJAqSF56MHcWqyIScXwaC1VBgw= github.com/whyrusleeping/cbor-gen v0.1.1/go.mod h1:pM99HXyEbSQHcosHc0iW7YFmwnscr+t9Te4ibko05so= github.com/whyrusleeping/chunker v0.0.0-20181014151217-fe64bd25879f h1:jQa4QT2UP9WYv2nzyawpKMOCl+Z/jW7djv2/J50lj9E= @@ -2065,6 +2068,7 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220708085239-5a0f0661e09d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= From 1b6bffd339ee1c415c3704a8f9b7080d9b9d2613 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sat, 20 Apr 2024 09:39:38 +0200 Subject: [PATCH 392/394] fix: curio: Update pgx imports, fix db_storage alloc --- curiosrc/market/lmrpc/lmrpc.go | 2 +- lib/harmony/harmonytask/singleton_task.go | 2 +- storage/paths/db_index.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/curiosrc/market/lmrpc/lmrpc.go b/curiosrc/market/lmrpc/lmrpc.go index dd7880289..33c0d34de 100644 --- a/curiosrc/market/lmrpc/lmrpc.go +++ b/curiosrc/market/lmrpc/lmrpc.go @@ -14,8 +14,8 @@ import ( "github.com/google/uuid" logging "github.com/ipfs/go-log/v2" - "github.com/jackc/pgx/v5" manet "github.com/multiformats/go-multiaddr/net" + "github.com/yugabyte/pgx/v5" "golang.org/x/xerrors" "github.com/filecoin-project/go-address" diff --git a/lib/harmony/harmonytask/singleton_task.go b/lib/harmony/harmonytask/singleton_task.go index 720033410..2395f343c 100644 --- a/lib/harmony/harmonytask/singleton_task.go +++ b/lib/harmony/harmonytask/singleton_task.go @@ -4,7 +4,7 @@ import ( "errors" "time" - "github.com/jackc/pgx/v5" + "github.com/yugabyte/pgx/v5" "github.com/filecoin-project/lotus/lib/harmony/harmonydb" "github.com/filecoin-project/lotus/lib/passcall" diff --git a/storage/paths/db_index.go b/storage/paths/db_index.go index 792395445..499c425be 100644 --- a/storage/paths/db_index.go +++ b/storage/paths/db_index.go @@ -723,7 +723,7 @@ func (dbi *DBIndex) StorageBestAlloc(ctx context.Context, allocate storiface.Sec FROM storage_path WHERE available >= $1 and NOW()-($2 * INTERVAL '1 second') < last_heartbeat - and heartbeat_err = '' + and heartbeat_err IS NULL and (($3 and can_seal = TRUE) or ($4 and can_store = TRUE)) order by (available::numeric * weight) desc`, spaceReq, From c2dd67403b8250d05ab0f78747fe6146fd9951cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 24 Apr 2024 21:46:04 +0200 Subject: [PATCH 393/394] feat: curioweb: Improve task_history indexes (#11911) --- lib/harmony/harmonydb/sql/20240317-web-summary-index.sql | 1 + lib/harmony/harmonydb/sql/20240420-web-task-indexes.sql | 9 +++++++++ 2 files changed, 10 insertions(+) create mode 100644 lib/harmony/harmonydb/sql/20240420-web-task-indexes.sql diff --git a/lib/harmony/harmonydb/sql/20240317-web-summary-index.sql b/lib/harmony/harmonydb/sql/20240317-web-summary-index.sql index 28902448d..e1c7e7a79 100644 --- a/lib/harmony/harmonydb/sql/20240317-web-summary-index.sql +++ b/lib/harmony/harmonydb/sql/20240317-web-summary-index.sql @@ -1,4 +1,5 @@ /* Used for webui clusterMachineSummary */ +-- NOTE: This index is changed in 20240420-web-task-indexes.sql CREATE INDEX harmony_task_history_work_index ON harmony_task_history (completed_by_host_and_port ASC, name ASC, result ASC, work_end DESC); diff --git a/lib/harmony/harmonydb/sql/20240420-web-task-indexes.sql b/lib/harmony/harmonydb/sql/20240420-web-task-indexes.sql new file mode 100644 index 000000000..33eb9e736 --- /dev/null +++ b/lib/harmony/harmonydb/sql/20240420-web-task-indexes.sql @@ -0,0 +1,9 @@ +DROP INDEX harmony_task_history_work_index; + +/* + This structure improves clusterMachineSummary query better than the old version, + while at the same time also being usable by clusterTaskHistorySummary (which wasn't + the case with the old index). + */ +create index harmony_task_history_work_index + on harmony_task_history (work_end desc, completed_by_host_and_port asc, name asc, result asc); From 10f7b6ed5f10bb3cd1b8fcc0c78aaf01694c7750 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 30 Apr 2024 19:34:43 +0200 Subject: [PATCH 394/394] mod tidy --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 062aca298..edf83fc3a 100644 --- a/go.mod +++ b/go.mod @@ -107,7 +107,6 @@ require ( github.com/ipni/go-libipni v0.0.8 github.com/ipni/index-provider v0.12.0 github.com/jackc/pgerrcode v0.0.0-20220416144525-469b46aa5efa - github.com/jackc/pgx/v5 v5.4.1 github.com/kelseyhightower/envconfig v1.4.0 github.com/koalacxr/quantile v0.0.1 github.com/libp2p/go-buffer-pool v0.1.0 @@ -251,6 +250,7 @@ require ( github.com/ipsn/go-secp256k1 v0.0.0-20180726113642-9d62b9f0bc52 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 // indirect + github.com/jackc/pgx/v5 v5.4.1 // indirect github.com/jackc/puddle/v2 v2.2.1 // indirect github.com/jackpal/go-nat-pmp v1.0.2 // indirect github.com/jbenet/go-random v0.0.0-20190219211222-123a90aedc0c // indirect
-

Lotus Provider Cluster

+

Curio Cluster

version [todo] diff --git a/provider/lpweb/static/main.css b/curiosrc/web/static/main.css similarity index 100% rename from provider/lpweb/static/main.css rename to curiosrc/web/static/main.css diff --git a/provider/lpweb/static/pipeline_porep.html b/curiosrc/web/static/pipeline_porep.html similarity index 100% rename from provider/lpweb/static/pipeline_porep.html rename to curiosrc/web/static/pipeline_porep.html diff --git a/provider/lpwindow/compute_do.go b/curiosrc/window/compute_do.go similarity index 99% rename from provider/lpwindow/compute_do.go rename to curiosrc/window/compute_do.go index 7089ceb02..2c861ad3d 100644 --- a/provider/lpwindow/compute_do.go +++ b/curiosrc/window/compute_do.go @@ -1,4 +1,4 @@ -package lpwindow +package window import ( "bytes" @@ -24,7 +24,7 @@ import ( "github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/chain/actors/builtin/miner" - types "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/storage/sealer" "github.com/filecoin-project/lotus/storage/sealer/storiface" ) diff --git a/provider/lpwindow/compute_task.go b/curiosrc/window/compute_task.go similarity index 98% rename from provider/lpwindow/compute_task.go rename to curiosrc/window/compute_task.go index 65102d0f0..541a2d5e2 100644 --- a/provider/lpwindow/compute_task.go +++ b/curiosrc/window/compute_task.go @@ -1,4 +1,4 @@ -package lpwindow +package window import ( "bytes" @@ -22,20 +22,20 @@ import ( "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/chain/actors/builtin/miner" "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/curiosrc/chainsched" "github.com/filecoin-project/lotus/lib/harmony/harmonydb" "github.com/filecoin-project/lotus/lib/harmony/harmonytask" "github.com/filecoin-project/lotus/lib/harmony/resources" "github.com/filecoin-project/lotus/lib/harmony/taskhelp" "github.com/filecoin-project/lotus/lib/promise" "github.com/filecoin-project/lotus/node/modules/dtypes" - "github.com/filecoin-project/lotus/provider/chainsched" "github.com/filecoin-project/lotus/storage/sealer" "github.com/filecoin-project/lotus/storage/sealer/sealtasks" "github.com/filecoin-project/lotus/storage/sealer/storiface" "github.com/filecoin-project/lotus/storage/wdpost" ) -var log = logging.Logger("lpwindow") +var log = logging.Logger("curio/window") var EpochsPerDeadline = miner.WPoStProvingPeriod() / abi.ChainEpoch(miner.WPoStPeriodDeadlines) @@ -86,7 +86,7 @@ func NewWdPostTask(db *harmonydb.DB, faultTracker sealer.FaultTracker, prover ProverPoSt, verifier storiface.Verifier, - pcs *chainsched.ProviderChainSched, + pcs *chainsched.CurioChainSched, actors map[dtypes.MinerAddress]bool, max int, ) (*WdPostTask, error) { diff --git a/provider/lpwindow/faults_simple.go b/curiosrc/window/faults_simple.go similarity index 99% rename from provider/lpwindow/faults_simple.go rename to curiosrc/window/faults_simple.go index b596fb5a7..64f5e8650 100644 --- a/provider/lpwindow/faults_simple.go +++ b/curiosrc/window/faults_simple.go @@ -1,4 +1,4 @@ -package lpwindow +package window import ( "context" diff --git a/provider/lpwindow/recover_task.go b/curiosrc/window/recover_task.go similarity index 96% rename from provider/lpwindow/recover_task.go rename to curiosrc/window/recover_task.go index 27c501dde..1ed110978 100644 --- a/provider/lpwindow/recover_task.go +++ b/curiosrc/window/recover_task.go @@ -1,4 +1,4 @@ -package lpwindow +package window import ( "context" @@ -15,20 +15,20 @@ import ( "github.com/filecoin-project/lotus/chain/actors" "github.com/filecoin-project/lotus/chain/actors/builtin/miner" "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/curiosrc/chainsched" + "github.com/filecoin-project/lotus/curiosrc/message" + "github.com/filecoin-project/lotus/curiosrc/multictladdr" "github.com/filecoin-project/lotus/lib/harmony/harmonydb" "github.com/filecoin-project/lotus/lib/harmony/harmonytask" "github.com/filecoin-project/lotus/lib/harmony/resources" "github.com/filecoin-project/lotus/lib/promise" "github.com/filecoin-project/lotus/node/modules/dtypes" - "github.com/filecoin-project/lotus/provider/chainsched" - "github.com/filecoin-project/lotus/provider/lpmessage" - "github.com/filecoin-project/lotus/provider/multictladdr" "github.com/filecoin-project/lotus/storage/sealer" "github.com/filecoin-project/lotus/storage/wdpost" ) type WdPostRecoverDeclareTask struct { - sender *lpmessage.Sender + sender *message.Sender db *harmonydb.DB api WdPostRecoverDeclareTaskApi faultTracker sealer.FaultTracker @@ -57,12 +57,12 @@ type WdPostRecoverDeclareTaskApi interface { StateLookupID(context.Context, address.Address, types.TipSetKey) (address.Address, error) } -func NewWdPostRecoverDeclareTask(sender *lpmessage.Sender, +func NewWdPostRecoverDeclareTask(sender *message.Sender, db *harmonydb.DB, api WdPostRecoverDeclareTaskApi, faultTracker sealer.FaultTracker, as *multictladdr.MultiAddressSelector, - pcs *chainsched.ProviderChainSched, + pcs *chainsched.CurioChainSched, maxDeclareRecoveriesGasFee types.FIL, actors map[dtypes.MinerAddress]bool) (*WdPostRecoverDeclareTask, error) { diff --git a/provider/lpwindow/submit_task.go b/curiosrc/window/submit_task.go similarity index 95% rename from provider/lpwindow/submit_task.go rename to curiosrc/window/submit_task.go index 8d39c40ab..330fd0509 100644 --- a/provider/lpwindow/submit_task.go +++ b/curiosrc/window/submit_task.go @@ -1,4 +1,4 @@ -package lpwindow +package window import ( "bytes" @@ -15,13 +15,13 @@ import ( "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/curiosrc/chainsched" + "github.com/filecoin-project/lotus/curiosrc/message" + "github.com/filecoin-project/lotus/curiosrc/multictladdr" "github.com/filecoin-project/lotus/lib/harmony/harmonydb" "github.com/filecoin-project/lotus/lib/harmony/harmonytask" "github.com/filecoin-project/lotus/lib/harmony/resources" "github.com/filecoin-project/lotus/lib/promise" - "github.com/filecoin-project/lotus/provider/chainsched" - "github.com/filecoin-project/lotus/provider/lpmessage" - "github.com/filecoin-project/lotus/provider/multictladdr" "github.com/filecoin-project/lotus/storage/wdpost" ) @@ -42,7 +42,7 @@ type WdPoStSubmitTaskApi interface { } type WdPostSubmitTask struct { - sender *lpmessage.Sender + sender *message.Sender db *harmonydb.DB api WdPoStSubmitTaskApi @@ -52,7 +52,7 @@ type WdPostSubmitTask struct { submitPoStTF promise.Promise[harmonytask.AddTaskFunc] } -func NewWdPostSubmitTask(pcs *chainsched.ProviderChainSched, send *lpmessage.Sender, db *harmonydb.DB, api WdPoStSubmitTaskApi, maxWindowPoStGasFee types.FIL, as *multictladdr.MultiAddressSelector) (*WdPostSubmitTask, error) { +func NewWdPostSubmitTask(pcs *chainsched.CurioChainSched, send *message.Sender, db *harmonydb.DB, api WdPoStSubmitTaskApi, maxWindowPoStGasFee types.FIL, as *multictladdr.MultiAddressSelector) (*WdPostSubmitTask, error) { res := &WdPostSubmitTask{ sender: send, db: db, diff --git a/provider/lpwinning/winning_task.go b/curiosrc/winning/winning_task.go similarity index 99% rename from provider/lpwinning/winning_task.go rename to curiosrc/winning/winning_task.go index 7f3c97616..064696727 100644 --- a/provider/lpwinning/winning_task.go +++ b/curiosrc/winning/winning_task.go @@ -1,4 +1,4 @@ -package lpwinning +package winning import ( "bytes" @@ -32,7 +32,7 @@ import ( "github.com/filecoin-project/lotus/storage/sealer/storiface" ) -var log = logging.Logger("lpwinning") +var log = logging.Logger("curio/winning") type WinPostTask struct { max int diff --git a/documentation/en/api-v0-methods-provider.md b/documentation/en/api-v0-methods-curio.md similarity index 99% rename from documentation/en/api-v0-methods-provider.md rename to documentation/en/api-v0-methods-curio.md index a1ccc17c2..84cc20595 100644 --- a/documentation/en/api-v0-methods-provider.md +++ b/documentation/en/api-v0-methods-curio.md @@ -16,6 +16,7 @@ ### Shutdown +Trigger shutdown Perms: admin diff --git a/documentation/en/cli-lotus-provider.md b/documentation/en/cli-curio.md similarity index 55% rename from documentation/en/cli-lotus-provider.md rename to documentation/en/cli-curio.md index be3b5ec6e..08ed8ab90 100644 --- a/documentation/en/cli-lotus-provider.md +++ b/documentation/en/cli-curio.md @@ -1,24 +1,26 @@ -# lotus-provider +# curio ``` NAME: - lotus-provider - Filecoin decentralized storage network provider + curio - Filecoin decentralized storage network provider USAGE: - lotus-provider [global options] command [command options] [arguments...] + curio [global options] command [command options] [arguments...] VERSION: 1.27.0-dev COMMANDS: - cli Execute cli commands - run Start a lotus provider process - stop Stop a running lotus provider - config Manage node config by layers. The layer 'base' will always be applied at Curio start-up. - test Utility functions for testing - web Start lotus provider web interface - seal Manage the sealing pipeline - version Print version - help, h Shows a list of commands or help for one command + cli Execute cli commands + run Start a Curio process + stop Stop a running Curio process + config Manage node config by layers. The layer 'base' will always be applied at Curio start-up. + test Utility functions for testing + web Start Curio web interface + guided-setup Run the guided setup for migrating from lotus-miner to Curio + from-miner Express a database config (for curio) from an existing miner. + seal Manage the sealing pipeline + version Print version + help, h Shows a list of commands or help for one command DEVELOPER: auth Manage RPC permissions log Manage logging @@ -27,23 +29,23 @@ COMMANDS: GLOBAL OPTIONS: --color use color in display output (default: depends on output being a TTY) - --db-host value Command separated list of hostnames for yugabyte cluster (default: "yugabyte") [$LOTUS_DB_HOST] - --db-name value (default: "yugabyte") [$LOTUS_DB_NAME, $LOTUS_HARMONYDB_HOSTS] - --db-user value (default: "yugabyte") [$LOTUS_DB_USER, $LOTUS_HARMONYDB_USERNAME] - --db-password value (default: "yugabyte") [$LOTUS_DB_PASSWORD, $LOTUS_HARMONYDB_PASSWORD] - --repo-path value (default: "~/.lotusprovider") [$LOTUS_REPO_PATH] + --db-host value Command separated list of hostnames for yugabyte cluster (default: "yugabyte") [$CURIO_DB_HOST, $CURIO_HARMONYDB_HOSTS] + --db-name value (default: "yugabyte") [$CURIO_DB_NAME, $CURIO_HARMONYDB_NAME] + --db-user value (default: "yugabyte") [$CURIO_DB_USER, $CURIO_HARMONYDB_USERNAME] + --db-password value (default: "yugabyte") [$CURIO_DB_PASSWORD, $CURIO_HARMONYDB_PASSWORD] + --repo-path value (default: "~/.curio") [$CURIO_REPO_PATH] --vv enables very verbose mode, useful for debugging the CLI (default: false) --help, -h show help --version, -v print the version ``` -## lotus-provider cli +## curio cli ``` NAME: - lotus-provider cli - Execute cli commands + curio cli - Execute cli commands USAGE: - lotus-provider cli command [command options] [arguments...] + curio cli command [command options] [arguments...] COMMANDS: storage manage sector storage @@ -54,57 +56,57 @@ OPTIONS: --help, -h show help ``` -### lotus-provider cli storage +### curio cli storage ``` ``` -## lotus-provider run +## curio run ``` NAME: - lotus-provider run - Start a lotus provider process + curio run - Start a Curio process USAGE: - lotus-provider run [command options] [arguments...] + curio run [command options] [arguments...] OPTIONS: --listen value host address and port the worker api will listen on (default: "0.0.0.0:12300") [$LOTUS_WORKER_LISTEN] --nosync don't check full-node sync status (default: false) --manage-fdlimit manage open file limit (default: true) - --storage-json value path to json file containing storage config (default: "~/.lotus-provider/storage.json") - --journal value path to journal files (default: "~/.lotus-provider/") + --storage-json value path to json file containing storage config (default: "~/.curio/storage.json") + --journal value path to journal files (default: "~/.curio/") --layers value [ --layers value ] list of layers to be interpreted (atop defaults). Default: base --help, -h show help ``` -## lotus-provider stop +## curio stop ``` NAME: - lotus-provider stop - Stop a running lotus provider + curio stop - Stop a running Curio process USAGE: - lotus-provider stop [command options] [arguments...] + curio stop [command options] [arguments...] OPTIONS: --help, -h show help ``` -## lotus-provider config +## curio config ``` NAME: - lotus-provider config - Manage node config by layers. The layer 'base' will always be applied at Curio start-up. + curio config - Manage node config by layers. The layer 'base' will always be applied at Curio start-up. USAGE: - lotus-provider config command [command options] [arguments...] + curio config command [command options] [arguments...] COMMANDS: default, defaults Print default node config set, add, update, create Set a config layer or the base by providing a filename or stdin. get, cat, show Get a config layer by name. You may want to pipe the output to a file, or use 'less' list, ls List config layers present in the DB. - interpret, view, stacked, stack Interpret stacked config layers by this version of lotus-provider, with system-generated comments. + interpret, view, stacked, stack Interpret stacked config layers by this version of curio, with system-generated comments. remove, rm, del, delete Remove a named config layer. edit edit a config layer - from-miner Express a database config (for lotus-provider) from an existing miner. + from-miner Express a database config (for curio) from an existing miner. new-cluster Create new configuration for a new cluster help, h Shows a list of commands or help for one command @@ -112,88 +114,88 @@ OPTIONS: --help, -h show help ``` -### lotus-provider config default +### curio config default ``` NAME: - lotus-provider config default - Print default node config + curio config default - Print default node config USAGE: - lotus-provider config default [command options] [arguments...] + curio config default [command options] [arguments...] OPTIONS: --no-comment don't comment default values (default: false) --help, -h show help ``` -### lotus-provider config set +### curio config set ``` NAME: - lotus-provider config set - Set a config layer or the base by providing a filename or stdin. + curio config set - Set a config layer or the base by providing a filename or stdin. USAGE: - lotus-provider config set [command options] a layer's file name + curio config set [command options] a layer's file name OPTIONS: --title value title of the config layer (req'd for stdin) --help, -h show help ``` -### lotus-provider config get +### curio config get ``` NAME: - lotus-provider config get - Get a config layer by name. You may want to pipe the output to a file, or use 'less' + curio config get - Get a config layer by name. You may want to pipe the output to a file, or use 'less' USAGE: - lotus-provider config get [command options] layer name + curio config get [command options] layer name OPTIONS: --help, -h show help ``` -### lotus-provider config list +### curio config list ``` NAME: - lotus-provider config list - List config layers present in the DB. + curio config list - List config layers present in the DB. USAGE: - lotus-provider config list [command options] [arguments...] + curio config list [command options] [arguments...] OPTIONS: --help, -h show help ``` -### lotus-provider config interpret +### curio config interpret ``` NAME: - lotus-provider config interpret - Interpret stacked config layers by this version of lotus-provider, with system-generated comments. + curio config interpret - Interpret stacked config layers by this version of curio, with system-generated comments. USAGE: - lotus-provider config interpret [command options] a list of layers to be interpreted as the final config + curio config interpret [command options] a list of layers to be interpreted as the final config OPTIONS: --layers value [ --layers value ] comma or space separated list of layers to be interpreted (base is always applied) --help, -h show help ``` -### lotus-provider config remove +### curio config remove ``` NAME: - lotus-provider config remove - Remove a named config layer. + curio config remove - Remove a named config layer. USAGE: - lotus-provider config remove [command options] [arguments...] + curio config remove [command options] [arguments...] OPTIONS: --help, -h show help ``` -### lotus-provider config edit +### curio config edit ``` NAME: - lotus-provider config edit - edit a config layer + curio config edit - edit a config layer USAGE: - lotus-provider config edit [command options] [layer name] + curio config edit [command options] [layer name] OPTIONS: --editor value editor to use (default: "vim") [$EDITOR] @@ -204,43 +206,43 @@ OPTIONS: --help, -h show help ``` -### lotus-provider config from-miner +### curio config from-miner ``` NAME: - lotus-provider config from-miner - Express a database config (for lotus-provider) from an existing miner. + curio from-miner - Express a database config (for curio) from an existing miner. USAGE: - lotus-provider config from-miner [command options] [arguments...] + curio from-miner [command options] [arguments...] DESCRIPTION: - Express a database config (for lotus-provider) from an existing miner. + Express a database config (for curio) from an existing miner. OPTIONS: - --miner-repo value, --storagerepo value Miner repo path (default: "~/.lotusminer") [$LOTUS_MINER_PATH, $LOTUS_STORAGE_PATH] + --miner-repo value, --storagerepo value Specify miner repo path. flag(storagerepo) and env(LOTUS_STORAGE_PATH) are DEPRECATION, will REMOVE SOON (default: "~/.lotusminer") [$LOTUS_MINER_PATH, $LOTUS_STORAGE_PATH] --to-layer value, -t value The layer name for this data push. 'base' is recommended for single-miner setup. --overwrite, -o Use this with --to-layer to replace an existing layer (default: false) --help, -h show help ``` -### lotus-provider config new-cluster +### curio config new-cluster ``` NAME: - lotus-provider config new-cluster - Create new configuration for a new cluster + curio config new-cluster - Create new configuration for a new cluster USAGE: - lotus-provider config new-cluster [command options] [SP actor address...] + curio config new-cluster [command options] [SP actor address...] OPTIONS: --help, -h show help ``` -## lotus-provider test +## curio test ``` NAME: - lotus-provider test - Utility functions for testing + curio test - Utility functions for testing USAGE: - lotus-provider test command [command options] [arguments...] + curio test command [command options] [arguments...] COMMANDS: window-post, wd, windowpost, wdpost Compute a proof-of-spacetime for a sector (requires the sector to be pre-sealed). These will not send to the chain. @@ -250,30 +252,30 @@ OPTIONS: --help, -h show help ``` -### lotus-provider test window-post +### curio test window-post ``` NAME: - lotus-provider test window-post - Compute a proof-of-spacetime for a sector (requires the sector to be pre-sealed). These will not send to the chain. + curio test window-post - Compute a proof-of-spacetime for a sector (requires the sector to be pre-sealed). These will not send to the chain. USAGE: - lotus-provider test window-post command [command options] [arguments...] + curio test window-post command [command options] [arguments...] COMMANDS: here, cli Compute WindowPoSt for performance and configuration testing. - task, scheduled, schedule, async, asynchronous Test the windowpost scheduler by running it on the next available lotus-provider. + task, scheduled, schedule, async, asynchronous Test the windowpost scheduler by running it on the next available curio. help, h Shows a list of commands or help for one command OPTIONS: --help, -h show help ``` -#### lotus-provider test window-post here +#### curio test window-post here ``` NAME: - lotus-provider test window-post here - Compute WindowPoSt for performance and configuration testing. + curio test window-post here - Compute WindowPoSt for performance and configuration testing. USAGE: - lotus-provider test window-post here [command options] [deadline index] + curio test window-post here [command options] [deadline index] DESCRIPTION: Note: This command is intended to be used to verify PoSt compute performance. @@ -282,18 +284,18 @@ DESCRIPTION: OPTIONS: --deadline value deadline to compute WindowPoSt for (default: 0) --layers value [ --layers value ] list of layers to be interpreted (atop defaults). Default: base - --storage-json value path to json file containing storage config (default: "~/.lotus-provider/storage.json") + --storage-json value path to json file containing storage config (default: "~/.curio/storage.json") --partition value partition to compute WindowPoSt for (default: 0) --help, -h show help ``` -#### lotus-provider test window-post task +#### curio test window-post task ``` NAME: - lotus-provider test window-post task - Test the windowpost scheduler by running it on the next available lotus-provider. + curio test window-post task - Test the windowpost scheduler by running it on the next available curio. USAGE: - lotus-provider test window-post task [command options] [arguments...] + curio test window-post task [command options] [arguments...] OPTIONS: --deadline value deadline to compute WindowPoSt for (default: 0) @@ -301,16 +303,16 @@ OPTIONS: --help, -h show help ``` -## lotus-provider web +## curio web ``` NAME: - lotus-provider web - Start lotus provider web interface + curio web - Start Curio web interface USAGE: - lotus-provider web [command options] [arguments...] + curio web [command options] [arguments...] DESCRIPTION: - Start an instance of lotus provider web interface. + Start an instance of Curio web interface. This creates the 'web' layer if it does not exist, then calls run with that layer. OPTIONS: @@ -320,13 +322,43 @@ OPTIONS: --help, -h show help ``` -## lotus-provider seal +## curio guided-setup ``` NAME: - lotus-provider seal - Manage the sealing pipeline + curio guided-setup - Run the guided setup for migrating from lotus-miner to Curio USAGE: - lotus-provider seal command [command options] [arguments...] + curio guided-setup [command options] [arguments...] + +OPTIONS: + --help, -h show help +``` + +## curio from-miner +``` +NAME: + curio from-miner - Express a database config (for curio) from an existing miner. + +USAGE: + curio from-miner [command options] [arguments...] + +DESCRIPTION: + Express a database config (for curio) from an existing miner. + +OPTIONS: + --miner-repo value, --storagerepo value Specify miner repo path. flag(storagerepo) and env(LOTUS_STORAGE_PATH) are DEPRECATION, will REMOVE SOON (default: "~/.lotusminer") [$LOTUS_MINER_PATH, $LOTUS_STORAGE_PATH] + --to-layer value, -t value The layer name for this data push. 'base' is recommended for single-miner setup. + --overwrite, -o Use this with --to-layer to replace an existing layer (default: false) + --help, -h show help +``` + +## curio seal +``` +NAME: + curio seal - Manage the sealing pipeline + +USAGE: + curio seal command [command options] [arguments...] COMMANDS: start Start new sealing operations manually @@ -336,13 +368,13 @@ OPTIONS: --help, -h show help ``` -### lotus-provider seal start +### curio seal start ``` NAME: - lotus-provider seal start - Start new sealing operations manually + curio seal start - Start new sealing operations manually USAGE: - lotus-provider seal start [command options] [arguments...] + curio seal start [command options] [arguments...] OPTIONS: --actor value Specify actor address to start sealing sectors for @@ -354,25 +386,25 @@ OPTIONS: --help, -h show help ``` -## lotus-provider version +## curio version ``` NAME: - lotus-provider version - Print version + curio version - Print version USAGE: - lotus-provider version [command options] [arguments...] + curio version [command options] [arguments...] OPTIONS: --help, -h show help ``` -## lotus-provider auth +## curio auth ``` NAME: - lotus-provider auth - Manage RPC permissions + curio auth - Manage RPC permissions USAGE: - lotus-provider auth command [command options] [arguments...] + curio auth command [command options] [arguments...] COMMANDS: create-token Create token @@ -383,39 +415,39 @@ OPTIONS: --help, -h show help ``` -### lotus-provider auth create-token +### curio auth create-token ``` NAME: - lotus-provider auth create-token - Create token + curio auth create-token - Create token USAGE: - lotus-provider auth create-token [command options] [arguments...] + curio auth create-token [command options] [arguments...] OPTIONS: --perm value permission to assign to the token, one of: read, write, sign, admin --help, -h show help ``` -### lotus-provider auth api-info +### curio auth api-info ``` NAME: - lotus-provider auth api-info - Get token with API info required to connect to this node + curio auth api-info - Get token with API info required to connect to this node USAGE: - lotus-provider auth api-info [command options] [arguments...] + curio auth api-info [command options] [arguments...] OPTIONS: --perm value permission to assign to the token, one of: read, write, sign, admin --help, -h show help ``` -## lotus-provider log +## curio log ``` NAME: - lotus-provider log - Manage logging + curio log - Manage logging USAGE: - lotus-provider log command [command options] [arguments...] + curio log command [command options] [arguments...] COMMANDS: list List log systems @@ -427,25 +459,25 @@ OPTIONS: --help, -h show help ``` -### lotus-provider log list +### curio log list ``` NAME: - lotus-provider log list - List log systems + curio log list - List log systems USAGE: - lotus-provider log list [command options] [arguments...] + curio log list [command options] [arguments...] OPTIONS: --help, -h show help ``` -### lotus-provider log set-level +### curio log set-level ``` NAME: - lotus-provider log set-level - Set log level + curio log set-level - Set log level USAGE: - lotus-provider log set-level [command options] [level] + curio log set-level [command options] [level] DESCRIPTION: Set the log level for logging systems: @@ -472,26 +504,26 @@ OPTIONS: --help, -h show help ``` -### lotus-provider log alerts +### curio log alerts ``` NAME: - lotus-provider log alerts - Get alert states + curio log alerts - Get alert states USAGE: - lotus-provider log alerts [command options] [arguments...] + curio log alerts [command options] [arguments...] OPTIONS: --all get all (active and inactive) alerts (default: false) --help, -h show help ``` -## lotus-provider wait-api +## curio wait-api ``` NAME: - lotus-provider wait-api - Wait for lotus api to come online + curio wait-api - Wait for lotus api to come online USAGE: - lotus-provider wait-api [command options] [arguments...] + curio wait-api [command options] [arguments...] CATEGORY: DEVELOPER @@ -501,13 +533,13 @@ OPTIONS: --help, -h show help ``` -## lotus-provider fetch-params +## curio fetch-params ``` NAME: - lotus-provider fetch-params - Fetch proving parameters + curio fetch-params - Fetch proving parameters USAGE: - lotus-provider fetch-params [command options] [sectorSize] + curio fetch-params [command options] [sectorSize] CATEGORY: DEVELOPER diff --git a/documentation/en/default-lotus-provider-config.toml b/documentation/en/default-curio-config.toml similarity index 100% rename from documentation/en/default-lotus-provider-config.toml rename to documentation/en/default-curio-config.toml diff --git a/go.mod b/go.mod index b8f6c38f2..19483b332 100644 --- a/go.mod +++ b/go.mod @@ -17,6 +17,7 @@ require ( github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d github.com/alecthomas/jsonschema v0.0.0-20200530073317-71f438968921 github.com/buger/goterm v1.0.3 + github.com/charmbracelet/lipgloss v0.9.1 github.com/chzyer/readline v1.5.1 github.com/containerd/cgroups v1.1.0 github.com/coreos/go-systemd/v22 v22.5.0 @@ -115,6 +116,7 @@ require ( github.com/libp2p/go-libp2p-routing-helpers v0.7.0 github.com/libp2p/go-maddr-filter v0.1.0 github.com/libp2p/go-msgio v0.3.0 + github.com/manifoldco/promptui v0.9.0 github.com/mattn/go-isatty v0.0.19 github.com/mattn/go-sqlite3 v1.14.16 github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 @@ -160,6 +162,7 @@ require ( golang.org/x/sync v0.3.0 golang.org/x/sys v0.16.0 golang.org/x/term v0.16.0 + golang.org/x/text v0.14.0 golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846 golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 @@ -174,6 +177,7 @@ require ( github.com/StackExchange/wmi v1.2.1 // indirect github.com/akavel/rsrc v0.8.0 // indirect github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect + github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/benbjohnson/clock v1.3.5 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bep/debounce v1.2.1 // indirect @@ -265,17 +269,19 @@ require ( github.com/libp2p/go-netroute v0.2.1 // indirect github.com/libp2p/go-reuseport v0.4.0 // indirect github.com/libp2p/go-yamux/v4 v4.0.1 // indirect - github.com/lucasb-eyer/go-colorful v1.0.3 // indirect + github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/magefile/mage v1.9.0 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd // indirect github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-runewidth v0.0.10 // indirect + github.com/mattn/go-runewidth v0.0.15 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/miekg/dns v1.1.55 // indirect github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b // indirect github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc // indirect github.com/mr-tron/base58 v1.2.0 // indirect + github.com/muesli/reflow v0.3.0 // indirect + github.com/muesli/termenv v0.15.2 // indirect github.com/multiformats/go-base36 v0.2.0 // indirect github.com/multiformats/go-multiaddr-fmt v0.1.0 // indirect github.com/multiformats/go-multistream v0.4.1 // indirect @@ -295,7 +301,7 @@ require ( github.com/quic-go/qtls-go1-20 v0.3.3 // indirect github.com/quic-go/quic-go v0.38.2 // indirect github.com/quic-go/webtransport-go v0.5.3 // indirect - github.com/rivo/uniseg v0.1.0 // indirect + github.com/rivo/uniseg v0.2.0 // indirect github.com/rs/cors v1.7.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/shirou/gopsutil v2.18.12+incompatible // indirect @@ -320,7 +326,6 @@ require ( go4.org v0.0.0-20230225012048-214862532bf5 // indirect golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 // indirect golang.org/x/mod v0.12.0 // indirect - golang.org/x/text v0.14.0 // indirect gonum.org/v1/gonum v0.13.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc // indirect google.golang.org/grpc v1.55.0 // indirect diff --git a/go.sum b/go.sum index 3efa6b8e2..932c70fff 100644 --- a/go.sum +++ b/go.sum @@ -106,6 +106,8 @@ github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6l github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= +github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= +github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o= @@ -159,6 +161,8 @@ github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/charmbracelet/lipgloss v0.9.1 h1:PNyd3jvaJbg4jRHKWXnCj1akQm4rh8dbEzN1p/u1KWg= +github.com/charmbracelet/lipgloss v0.9.1/go.mod h1:1mPmG4cxScwUQALAAnacHaigiiHB9Pmr+v1VEawJl6I= github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM= @@ -1175,8 +1179,9 @@ github.com/libp2p/go-yamux/v4 v4.0.1/go.mod h1:NWjl8ZTLOGlozrXSOZ/HlfG++39iKNnM5 github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= github.com/lucas-clemente/quic-go v0.19.3/go.mod h1:ADXpNbTQjq1hIzCpB+y/k5iz4n4z4IwqoLb94Kh5Hu8= -github.com/lucasb-eyer/go-colorful v1.0.3 h1:QIbQXiugsb+q10B+MI+7DI1oQLdmnep86tWFlaaUAac= github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= +github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= +github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/magefile/mage v1.9.0 h1:t3AU2wNwehMCW97vuqQLtw6puppWXHO+O2MHo5a50XE= @@ -1192,6 +1197,8 @@ github.com/mailru/easyjson v0.7.1/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7 github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA= +github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg= github.com/marten-seemann/qpack v0.2.1/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc= github.com/marten-seemann/qtls v0.10.0/go.mod h1:UvMd1oaYDACI99/oZUYLzMCkBXQVT0aGm99sJhbT8hs= github.com/marten-seemann/qtls-go1-15 v0.1.1/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I= @@ -1214,8 +1221,10 @@ github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APP github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-runewidth v0.0.10 h1:CoZ3S2P7pvtP45xOtBw+/mDL2z0RKI576gSkzRRpdGg= github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= +github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= +github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= +github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y= github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= @@ -1265,6 +1274,10 @@ github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjW github.com/mr-tron/base58 v1.1.3/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= +github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= +github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8= +github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo= +github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8= github.com/multiformats/go-base32 v0.0.3/go.mod h1:pLiuGC8y0QR3Ue4Zug5UzK9LjgbkL8NSQj0zQ5Nz/AA= github.com/multiformats/go-base32 v0.0.4/go.mod h1:jNLFzjPZtp3aIARHbJRZIaPuspdH0J6q39uUM5pnABM= github.com/multiformats/go-base32 v0.1.0 h1:pVx9xoSPqEIQG8o+UbAe7DNi51oej1NtK+aGkbLYxPE= @@ -1485,8 +1498,9 @@ github.com/raulk/clock v1.1.0/go.mod h1:3MpVxdZ/ODBQDxbN+kzshf5OSZwPjtMDx6BBXBmO github.com/raulk/go-watchdog v1.3.0 h1:oUmdlHxdkXRJlwfG0O9omj8ukerm8MEQavSiDTEtBsk= github.com/raulk/go-watchdog v1.3.0/go.mod h1:fIvOnLbF0b0ZwkB9YU4mOW9Did//4vPZtDqv66NfsMU= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/rivo/uniseg v0.1.0 h1:+2KBaVoUmb9XzDsrx/Ct0W/EYOSFf/nWTauy++DprtY= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= diff --git a/itests/kit/ensemble.go b/itests/kit/ensemble.go index c315b21c9..9588d2526 100644 --- a/itests/kit/ensemble.go +++ b/itests/kit/ensemble.go @@ -46,9 +46,9 @@ import ( "github.com/filecoin-project/lotus/chain/stmgr" "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/wallet/key" - "github.com/filecoin-project/lotus/cmd/lotus-provider/deps" - "github.com/filecoin-project/lotus/cmd/lotus-provider/rpc" - "github.com/filecoin-project/lotus/cmd/lotus-provider/tasks" + "github.com/filecoin-project/lotus/cmd/curio/deps" + "github.com/filecoin-project/lotus/cmd/curio/rpc" + "github.com/filecoin-project/lotus/cmd/curio/tasks" "github.com/filecoin-project/lotus/cmd/lotus-seed/seed" "github.com/filecoin-project/lotus/cmd/lotus-worker/sealworker" "github.com/filecoin-project/lotus/gateway" diff --git a/itests/kit/node_full.go b/itests/kit/node_full.go index 697c59aed..c36f05a75 100644 --- a/itests/kit/node_full.go +++ b/itests/kit/node_full.go @@ -22,7 +22,7 @@ import ( "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/wallet/key" cliutil "github.com/filecoin-project/lotus/cli/util" - "github.com/filecoin-project/lotus/cmd/lotus-provider/deps" + "github.com/filecoin-project/lotus/cmd/curio/deps" "github.com/filecoin-project/lotus/gateway" "github.com/filecoin-project/lotus/node" ) @@ -57,7 +57,7 @@ type TestFullNode struct { // TestProviderNode represents a Provider node enrolled in an Ensemble. type TestProviderNode struct { - v1api.LotusProviderStruct + v1api.CurioStruct t *testing.T diff --git a/lib/harmony/harmonydb/harmonydb.go b/lib/harmony/harmonydb/harmonydb.go index a043e903f..3d05f04fb 100644 --- a/lib/harmony/harmonydb/harmonydb.go +++ b/lib/harmony/harmonydb/harmonydb.go @@ -84,7 +84,7 @@ func New(hosts []string, username, password, database, port string, itestID ITes } } - schema := "lotus" + schema := "curio" if itest != "" { schema = "itest_" + itest } diff --git a/lib/harmony/harmonydb/sql/20240212-common-layers.sql b/lib/harmony/harmonydb/sql/20240212-common-layers.sql new file mode 100644 index 000000000..cf72e1750 --- /dev/null +++ b/lib/harmony/harmonydb/sql/20240212-common-layers.sql @@ -0,0 +1,42 @@ +INSERT INTO harmony_config (title, config) VALUES + ('post', ' + [Subsystems] + EnableWindowPost = true + EnableWinningPost = true + '), + + ('gui', ' + [Subsystems] + EnableWebGui = true + '), + + ('seal', ' + [Subsystems] + EnableSealSDR = true + EnableSealSDRTrees = true + EnableSendPrecommitMsg = true + EnablePoRepProof = true + EnableSendCommitMsg = true + EnableMoveStorage = true + '), + + ('seal-gpu', ' + [Subsystems] + EnableSealSDRTrees = true + EnableSendPrecommitMsg = true + '), + ('seal-snark', ' + [Subsystems] + EnablePoRepProof = true + EnableSendCommitMsg = true + '), + ('sdr', ' + [Subsystems] + EnableSealSDR = true + '), + + ('storage', ' + [Subsystems] + EnableMoveStorage = true + ') + ON CONFLICT (title) DO NOTHING; -- SPs may have these names defined already. \ No newline at end of file diff --git a/lib/harmony/resources/resources.go b/lib/harmony/resources/resources.go index 4f67a26af..33bc80d6f 100644 --- a/lib/harmony/resources/resources.go +++ b/lib/harmony/resources/resources.go @@ -131,7 +131,7 @@ func getResources() (res Resources, err error) { } } if found > 1 { - logger.Warn("lotus-provider's defaults are for running alone. Use task maximums or CGroups.") + logger.Warn("curio's defaults are for running alone. Use task maximums or CGroups.") } } diff --git a/node/config/def.go b/node/config/def.go index 475f37517..2e5fb8032 100644 --- a/node/config/def.go +++ b/node/config/def.go @@ -329,12 +329,12 @@ const ( ResourceFilteringDisabled = ResourceFilteringStrategy("disabled") ) -func DefaultLotusProvider() *LotusProviderConfig { - return &LotusProviderConfig{ - Subsystems: ProviderSubsystemsConfig{ +func DefaultCurioConfig() *CurioConfig { + return &CurioConfig{ + Subsystems: CurioSubsystemsConfig{ GuiAddress: ":4701", }, - Fees: LotusProviderFees{ + Fees: CurioFees{ DefaultMaxFee: DefaultDefaultMaxFee, MaxPreCommitGasFee: types.MustParseFIL("0.025"), MaxCommitGasFee: types.MustParseFIL("0.05"), @@ -352,7 +352,7 @@ func DefaultLotusProvider() *LotusProviderConfig { MaxWindowPoStGasFee: types.MustParseFIL("5"), MaxPublishDealsFee: types.MustParseFIL("0.05"), }, - Addresses: []LotusProviderAddresses{{ + Addresses: []CurioAddresses{{ PreCommitControl: []string{}, CommitControl: []string{}, TerminateControl: []string{}, diff --git a/node/config/doc_gen.go b/node/config/doc_gen.go index f28c5abd8..092af75c7 100644 --- a/node/config/doc_gen.go +++ b/node/config/doc_gen.go @@ -165,6 +165,298 @@ of automatically performing on-chain operations.`, Comment: ``, }, }, + "CurioAddresses": { + { + Name: "PreCommitControl", + Type: "[]string", + + Comment: `Addresses to send PreCommit messages from`, + }, + { + Name: "CommitControl", + Type: "[]string", + + Comment: `Addresses to send Commit messages from`, + }, + { + Name: "TerminateControl", + Type: "[]string", + + Comment: ``, + }, + { + Name: "DisableOwnerFallback", + Type: "bool", + + Comment: `DisableOwnerFallback disables usage of the owner address for messages +sent automatically`, + }, + { + Name: "DisableWorkerFallback", + Type: "bool", + + Comment: `DisableWorkerFallback disables usage of the worker address for messages +sent automatically, if control addresses are configured. +A control address that doesn't have enough funds will still be chosen +over the worker address if this flag is set.`, + }, + { + Name: "MinerAddresses", + Type: "[]string", + + Comment: `MinerAddresses are the addresses of the miner actors to use for sending messages`, + }, + }, + "CurioConfig": { + { + Name: "Subsystems", + Type: "CurioSubsystemsConfig", + + Comment: ``, + }, + { + Name: "Fees", + Type: "CurioFees", + + Comment: ``, + }, + { + Name: "Addresses", + Type: "[]CurioAddresses", + + Comment: `Addresses of wallets per MinerAddress (one of the fields).`, + }, + { + Name: "Proving", + Type: "ProvingConfig", + + Comment: ``, + }, + { + Name: "Journal", + Type: "JournalConfig", + + Comment: ``, + }, + { + Name: "Apis", + Type: "ApisConfig", + + Comment: ``, + }, + }, + "CurioFees": { + { + Name: "DefaultMaxFee", + Type: "types.FIL", + + Comment: ``, + }, + { + Name: "MaxPreCommitGasFee", + Type: "types.FIL", + + Comment: ``, + }, + { + Name: "MaxCommitGasFee", + Type: "types.FIL", + + Comment: ``, + }, + { + Name: "MaxPreCommitBatchGasFee", + Type: "BatchFeeConfig", + + Comment: `maxBatchFee = maxBase + maxPerSector * nSectors`, + }, + { + Name: "MaxCommitBatchGasFee", + Type: "BatchFeeConfig", + + Comment: ``, + }, + { + Name: "MaxTerminateGasFee", + Type: "types.FIL", + + Comment: ``, + }, + { + Name: "MaxWindowPoStGasFee", + Type: "types.FIL", + + Comment: `WindowPoSt is a high-value operation, so the default fee should be high.`, + }, + { + Name: "MaxPublishDealsFee", + Type: "types.FIL", + + Comment: ``, + }, + }, + "CurioSubsystemsConfig": { + { + Name: "EnableWindowPost", + Type: "bool", + + Comment: `EnableWindowPost enables window post to be executed on this lotus-provider instance. Each machine in the cluster +with WindowPoSt enabled will also participate in the window post scheduler. It is possible to have multiple +machines with WindowPoSt enabled which will provide redundancy, and in case of multiple partitions per deadline, +will allow for parallel processing of partitions. + +It is possible to have instances handling both WindowPoSt and WinningPoSt, which can provide redundancy without +the need for additional machines. In setups like this it is generally recommended to run +partitionsPerDeadline+1 machines.`, + }, + { + Name: "WindowPostMaxTasks", + Type: "int", + + Comment: ``, + }, + { + Name: "EnableWinningPost", + Type: "bool", + + Comment: `EnableWinningPost enables winning post to be executed on this lotus-provider instance. +Each machine in the cluster with WinningPoSt enabled will also participate in the winning post scheduler. +It is possible to mix machines with WindowPoSt and WinningPoSt enabled, for details see the EnableWindowPost +documentation.`, + }, + { + Name: "WinningPostMaxTasks", + Type: "int", + + Comment: ``, + }, + { + Name: "EnableSealSDR", + Type: "bool", + + Comment: `EnableSealSDR enables SDR tasks to run. SDR is the long sequential computation +creating 11 layer files in sector cache directory. + +SDR is the first task in the sealing pipeline. It's inputs are just the hash of the +unsealed data (CommD), sector number, miner id, and the seal proof type. +It's outputs are the 11 layer files in the sector cache directory. + +In lotus-miner this was run as part of PreCommit1.`, + }, + { + Name: "SealSDRMaxTasks", + Type: "int", + + Comment: `The maximum amount of SDR tasks that can run simultaneously. Note that the maximum number of tasks will +also be bounded by resources available on the machine.`, + }, + { + Name: "EnableSealSDRTrees", + Type: "bool", + + Comment: `EnableSealSDRTrees enables the SDR pipeline tree-building task to run. +This task handles encoding of unsealed data into last sdr layer and building +of TreeR, TreeC and TreeD. + +This task runs after SDR +TreeD is first computed with optional input of unsealed data +TreeR is computed from replica, which is first computed as field +addition of the last SDR layer and the bottom layer of TreeD (which is the unsealed data) +TreeC is computed from the 11 SDR layers +The 3 trees will later be used to compute the PoRep proof. + +In case of SyntheticPoRep challenges for PoRep will be pre-generated at this step, and trees and layers +will be dropped. SyntheticPoRep works by pre-generating a very large set of challenges (~30GiB on disk) +then using a small subset of them for the actual PoRep computation. This allows for significant scratch space +saving between PreCommit and PoRep generation at the expense of more computation (generating challenges in this step) + +In lotus-miner this was run as part of PreCommit2 (TreeD was run in PreCommit1). +Note that nodes with SDRTrees enabled will also answer to Finalize tasks, +which just remove unneeded tree data after PoRep is computed.`, + }, + { + Name: "SealSDRTreesMaxTasks", + Type: "int", + + Comment: `The maximum amount of SealSDRTrees tasks that can run simultaneously. Note that the maximum number of tasks will +also be bounded by resources available on the machine.`, + }, + { + Name: "FinalizeMaxTasks", + Type: "int", + + Comment: `FinalizeMaxTasks is the maximum amount of finalize tasks that can run simultaneously. +The finalize task is enabled on all machines which also handle SDRTrees tasks. Finalize ALWAYS runs on whichever +machine holds sector cache files, as it removes unneeded tree data after PoRep is computed. +Finalize will run in parallel with the SubmitCommitMsg task.`, + }, + { + Name: "EnableSendPrecommitMsg", + Type: "bool", + + Comment: `EnableSendPrecommitMsg enables the sending of precommit messages to the chain +from this lotus-provider instance. +This runs after SDRTrees and uses the output CommD / CommR (roots of TreeD / TreeR) for the message`, + }, + { + Name: "EnablePoRepProof", + Type: "bool", + + Comment: `EnablePoRepProof enables the computation of the porep proof + +This task runs after interactive-porep seed becomes available, which happens 150 epochs (75min) after the +precommit message lands on chain. This task should run on a machine with a GPU. Vanilla PoRep proofs are +requested from the machine which holds sector cache files which most likely is the machine which ran the SDRTrees +task. + +In lotus-miner this was Commit1 / Commit2`, + }, + { + Name: "PoRepProofMaxTasks", + Type: "int", + + Comment: `The maximum amount of PoRepProof tasks that can run simultaneously. Note that the maximum number of tasks will +also be bounded by resources available on the machine.`, + }, + { + Name: "EnableSendCommitMsg", + Type: "bool", + + Comment: `EnableSendCommitMsg enables the sending of commit messages to the chain +from this lotus-provider instance.`, + }, + { + Name: "EnableMoveStorage", + Type: "bool", + + Comment: `EnableMoveStorage enables the move-into-long-term-storage task to run on this lotus-provider instance. +This tasks should only be enabled on nodes with long-term storage. + +The MoveStorage task is the last task in the sealing pipeline. It moves the sealed sector data from the +SDRTrees machine into long-term storage. This task runs after the Finalize task.`, + }, + { + Name: "MoveStorageMaxTasks", + Type: "int", + + Comment: `The maximum amount of MoveStorage tasks that can run simultaneously. Note that the maximum number of tasks will +also be bounded by resources available on the machine. It is recommended that this value is set to a number which +uses all available network (or disk) bandwidth on the machine without causing bottlenecks.`, + }, + { + Name: "EnableWebGui", + Type: "bool", + + Comment: `EnableWebGui enables the web GUI on this lotus-provider instance. The UI has minimal local overhead, but it should +only need to be run on a single machine in the cluster.`, + }, + { + Name: "GuiAddress", + Type: "string", + + Comment: `The address that should listen for Web GUI requests.`, + }, + }, "DAGStoreConfig": { { Name: "RootDir", @@ -699,136 +991,6 @@ closed by the connection manager.`, Comment: `SubsystemLevels specify per-subsystem log levels`, }, }, - "LotusProviderAddresses": { - { - Name: "PreCommitControl", - Type: "[]string", - - Comment: `Addresses to send PreCommit messages from`, - }, - { - Name: "CommitControl", - Type: "[]string", - - Comment: `Addresses to send Commit messages from`, - }, - { - Name: "TerminateControl", - Type: "[]string", - - Comment: ``, - }, - { - Name: "DisableOwnerFallback", - Type: "bool", - - Comment: `DisableOwnerFallback disables usage of the owner address for messages -sent automatically`, - }, - { - Name: "DisableWorkerFallback", - Type: "bool", - - Comment: `DisableWorkerFallback disables usage of the worker address for messages -sent automatically, if control addresses are configured. -A control address that doesn't have enough funds will still be chosen -over the worker address if this flag is set.`, - }, - { - Name: "MinerAddresses", - Type: "[]string", - - Comment: `MinerAddresses are the addresses of the miner actors to use for sending messages`, - }, - }, - "LotusProviderConfig": { - { - Name: "Subsystems", - Type: "ProviderSubsystemsConfig", - - Comment: ``, - }, - { - Name: "Fees", - Type: "LotusProviderFees", - - Comment: ``, - }, - { - Name: "Addresses", - Type: "[]LotusProviderAddresses", - - Comment: `Addresses of wallets per MinerAddress (one of the fields).`, - }, - { - Name: "Proving", - Type: "ProvingConfig", - - Comment: ``, - }, - { - Name: "Journal", - Type: "JournalConfig", - - Comment: ``, - }, - { - Name: "Apis", - Type: "ApisConfig", - - Comment: ``, - }, - }, - "LotusProviderFees": { - { - Name: "DefaultMaxFee", - Type: "types.FIL", - - Comment: ``, - }, - { - Name: "MaxPreCommitGasFee", - Type: "types.FIL", - - Comment: ``, - }, - { - Name: "MaxCommitGasFee", - Type: "types.FIL", - - Comment: ``, - }, - { - Name: "MaxPreCommitBatchGasFee", - Type: "BatchFeeConfig", - - Comment: `maxBatchFee = maxBase + maxPerSector * nSectors`, - }, - { - Name: "MaxCommitBatchGasFee", - Type: "BatchFeeConfig", - - Comment: ``, - }, - { - Name: "MaxTerminateGasFee", - Type: "types.FIL", - - Comment: ``, - }, - { - Name: "MaxWindowPoStGasFee", - Type: "types.FIL", - - Comment: `WindowPoSt is a high-value operation, so the default fee should be high.`, - }, - { - Name: "MaxPublishDealsFee", - Type: "types.FIL", - - Comment: ``, - }, - }, "MinerAddressConfig": { { Name: "PreCommitControl", @@ -998,168 +1160,6 @@ When disabled and no external block producers are configured, all potential block rewards will be missed!`, }, }, - "ProviderSubsystemsConfig": { - { - Name: "EnableWindowPost", - Type: "bool", - - Comment: `EnableWindowPost enables window post to be executed on this lotus-provider instance. Each machine in the cluster -with WindowPoSt enabled will also participate in the window post scheduler. It is possible to have multiple -machines with WindowPoSt enabled which will provide redundancy, and in case of multiple partitions per deadline, -will allow for parallel processing of partitions. - -It is possible to have instances handling both WindowPoSt and WinningPoSt, which can provide redundancy without -the need for additional machines. In setups like this it is generally recommended to run -partitionsPerDeadline+1 machines.`, - }, - { - Name: "WindowPostMaxTasks", - Type: "int", - - Comment: ``, - }, - { - Name: "EnableWinningPost", - Type: "bool", - - Comment: `EnableWinningPost enables winning post to be executed on this lotus-provider instance. -Each machine in the cluster with WinningPoSt enabled will also participate in the winning post scheduler. -It is possible to mix machines with WindowPoSt and WinningPoSt enabled, for details see the EnableWindowPost -documentation.`, - }, - { - Name: "WinningPostMaxTasks", - Type: "int", - - Comment: ``, - }, - { - Name: "EnableSealSDR", - Type: "bool", - - Comment: `EnableSealSDR enables SDR tasks to run. SDR is the long sequential computation -creating 11 layer files in sector cache directory. - -SDR is the first task in the sealing pipeline. It's inputs are just the hash of the -unsealed data (CommD), sector number, miner id, and the seal proof type. -It's outputs are the 11 layer files in the sector cache directory. - -In lotus-miner this was run as part of PreCommit1.`, - }, - { - Name: "SealSDRMaxTasks", - Type: "int", - - Comment: `The maximum amount of SDR tasks that can run simultaneously. Note that the maximum number of tasks will -also be bounded by resources available on the machine.`, - }, - { - Name: "EnableSealSDRTrees", - Type: "bool", - - Comment: `EnableSealSDRTrees enables the SDR pipeline tree-building task to run. -This task handles encoding of unsealed data into last sdr layer and building -of TreeR, TreeC and TreeD. - -This task runs after SDR -TreeD is first computed with optional input of unsealed data -TreeR is computed from replica, which is first computed as field -addition of the last SDR layer and the bottom layer of TreeD (which is the unsealed data) -TreeC is computed from the 11 SDR layers -The 3 trees will later be used to compute the PoRep proof. - -In case of SyntheticPoRep challenges for PoRep will be pre-generated at this step, and trees and layers -will be dropped. SyntheticPoRep works by pre-generating a very large set of challenges (~30GiB on disk) -then using a small subset of them for the actual PoRep computation. This allows for significant scratch space -saving between PreCommit and PoRep generation at the expense of more computation (generating challenges in this step) - -In lotus-miner this was run as part of PreCommit2 (TreeD was run in PreCommit1). -Note that nodes with SDRTrees enabled will also answer to Finalize tasks, -which just remove unneeded tree data after PoRep is computed.`, - }, - { - Name: "SealSDRTreesMaxTasks", - Type: "int", - - Comment: `The maximum amount of SealSDRTrees tasks that can run simultaneously. Note that the maximum number of tasks will -also be bounded by resources available on the machine.`, - }, - { - Name: "FinalizeMaxTasks", - Type: "int", - - Comment: `FinalizeMaxTasks is the maximum amount of finalize tasks that can run simultaneously. -The finalize task is enabled on all machines which also handle SDRTrees tasks. Finalize ALWAYS runs on whichever -machine holds sector cache files, as it removes unneeded tree data after PoRep is computed. -Finalize will run in parallel with the SubmitCommitMsg task.`, - }, - { - Name: "EnableSendPrecommitMsg", - Type: "bool", - - Comment: `EnableSendPrecommitMsg enables the sending of precommit messages to the chain -from this lotus-provider instance. -This runs after SDRTrees and uses the output CommD / CommR (roots of TreeD / TreeR) for the message`, - }, - { - Name: "EnablePoRepProof", - Type: "bool", - - Comment: `EnablePoRepProof enables the computation of the porep proof - -This task runs after interactive-porep seed becomes available, which happens 150 epochs (75min) after the -precommit message lands on chain. This task should run on a machine with a GPU. Vanilla PoRep proofs are -requested from the machine which holds sector cache files which most likely is the machine which ran the SDRTrees -task. - -In lotus-miner this was Commit1 / Commit2`, - }, - { - Name: "PoRepProofMaxTasks", - Type: "int", - - Comment: `The maximum amount of PoRepProof tasks that can run simultaneously. Note that the maximum number of tasks will -also be bounded by resources available on the machine.`, - }, - { - Name: "EnableSendCommitMsg", - Type: "bool", - - Comment: `EnableSendCommitMsg enables the sending of commit messages to the chain -from this lotus-provider instance.`, - }, - { - Name: "EnableMoveStorage", - Type: "bool", - - Comment: `EnableMoveStorage enables the move-into-long-term-storage task to run on this lotus-provider instance. -This tasks should only be enabled on nodes with long-term storage. - -The MoveStorage task is the last task in the sealing pipeline. It moves the sealed sector data from the -SDRTrees machine into long-term storage. This task runs after the Finalize task.`, - }, - { - Name: "MoveStorageMaxTasks", - Type: "int", - - Comment: `The maximum amount of MoveStorage tasks that can run simultaneously. Note that the maximum number of tasks will -also be bounded by resources available on the machine. It is recommended that this value is set to a number which -uses all available network (or disk) bandwidth on the machine without causing bottlenecks.`, - }, - { - Name: "EnableWebGui", - Type: "bool", - - Comment: `EnableWebGui enables the web GUI on this lotus-provider instance. The UI has minimal local overhead, but it should -only need to be run on a single machine in the cluster.`, - }, - { - Name: "GuiAddress", - Type: "string", - - Comment: `The address that should listen for Web GUI requests.`, - }, - }, "ProvingConfig": { { Name: "ParallelCheckLimit", diff --git a/node/config/load.go b/node/config/load.go index fd015d533..96a042941 100644 --- a/node/config/load.go +++ b/node/config/load.go @@ -266,6 +266,7 @@ func ConfigUpdate(cfgCur, cfgDef interface{}, opts ...UpdateCfgOpt) ([]byte, err } // sanity-check that the updated config parses the same way as the current one + if cfgDef != nil { cfgUpdated, err := FromReader(strings.NewReader(nodeStr), cfgDef) if err != nil { @@ -273,7 +274,7 @@ func ConfigUpdate(cfgCur, cfgDef interface{}, opts ...UpdateCfgOpt) ([]byte, err } if !reflect.DeepEqual(cfgCur, cfgUpdated) { - return nil, xerrors.Errorf("updated config didn't match current config") + return nil, xerrors.Errorf("updated config didn't match current config:") } } diff --git a/node/config/types.go b/node/config/types.go index 789d24103..2d108c7de 100644 --- a/node/config/types.go +++ b/node/config/types.go @@ -66,13 +66,13 @@ type StorageMiner struct { HarmonyDB HarmonyDB } -type LotusProviderConfig struct { - Subsystems ProviderSubsystemsConfig +type CurioConfig struct { + Subsystems CurioSubsystemsConfig - Fees LotusProviderFees + Fees CurioFees // Addresses of wallets per MinerAddress (one of the fields). - Addresses []LotusProviderAddresses + Addresses []CurioAddresses Proving ProvingConfig Journal JournalConfig Apis ApisConfig @@ -93,7 +93,7 @@ type JournalConfig struct { DisabledEvents string } -type ProviderSubsystemsConfig struct { +type CurioSubsystemsConfig struct { // EnableWindowPost enables window post to be executed on this lotus-provider instance. Each machine in the cluster // with WindowPoSt enabled will also participate in the window post scheduler. It is possible to have multiple // machines with WindowPoSt enabled which will provide redundancy, and in case of multiple partitions per deadline, @@ -663,7 +663,7 @@ type MinerFeeConfig struct { MaximizeWindowPoStFeeCap bool } -type LotusProviderFees struct { +type CurioFees struct { DefaultMaxFee types.FIL MaxPreCommitGasFee types.FIL MaxCommitGasFee types.FIL @@ -695,7 +695,7 @@ type MinerAddressConfig struct { DisableWorkerFallback bool } -type LotusProviderAddresses struct { +type CurioAddresses struct { // Addresses to send PreCommit messages from PreCommitControl []string // Addresses to send Commit messages from diff --git a/node/modules/storageminer_svc.go b/node/modules/storageminer_svc.go index 99a435a0c..1a909b4ec 100644 --- a/node/modules/storageminer_svc.go +++ b/node/modules/storageminer_svc.go @@ -15,11 +15,11 @@ import ( "github.com/filecoin-project/lotus/api/v1api" "github.com/filecoin-project/lotus/chain/types" cliutil "github.com/filecoin-project/lotus/cli/util" + "github.com/filecoin-project/lotus/curiosrc/market" + "github.com/filecoin-project/lotus/curiosrc/market/fakelm" "github.com/filecoin-project/lotus/lib/harmony/harmonydb" "github.com/filecoin-project/lotus/node/config" "github.com/filecoin-project/lotus/node/modules/helpers" - "github.com/filecoin-project/lotus/provider/lpmarket" - "github.com/filecoin-project/lotus/provider/lpmarket/fakelm" "github.com/filecoin-project/lotus/storage/paths" "github.com/filecoin-project/lotus/storage/sealer/storiface" "github.com/filecoin-project/lotus/storage/sectorblocks" @@ -69,7 +69,7 @@ func connectHarmony(apiInfo string, fapi v1api.FullNode, mctx helpers.MetricsCtx return nil, xerrors.Errorf("parsing miner address: %w", err) } - pin := lpmarket.NewPieceIngester(db, fapi) + pin := market.NewPieceIngester(db, fapi) si := paths.NewDBIndex(nil, db) diff --git a/node/repo/fsrepo.go b/node/repo/fsrepo.go index d8e41fb2b..6cac14c01 100644 --- a/node/repo/fsrepo.go +++ b/node/repo/fsrepo.go @@ -185,28 +185,28 @@ func (worker) APIInfoEnvVars() (primary string, fallbacks []string, deprecated [ return "WORKER_API_INFO", nil, nil } -type provider struct{} +type curio struct{} -var Provider provider +var Curio curio -func (provider) Type() string { - return "Provider" +func (curio) Type() string { + return "Curio" } -func (provider) Config() interface{} { +func (curio) Config() interface{} { return &struct{}{} } -func (provider) APIFlags() []string { - return []string{"provider-api-url"} +func (curio) APIFlags() []string { + return []string{"curio-api-url"} } -func (provider) RepoFlags() []string { - return []string{"provider-repo"} +func (curio) RepoFlags() []string { + return []string{"curio-repo"} } -func (provider) APIInfoEnvVars() (primary string, fallbacks []string, deprecated []string) { - return "PROVIDER_API_INFO", nil, nil +func (curio) APIInfoEnvVars() (primary string, fallbacks []string, deprecated []string) { + return "CURIO_API_INFO", nil, nil } var Wallet wallet diff --git a/node/repo/interface.go b/node/repo/interface.go index 328862b92..11c965bf5 100644 --- a/node/repo/interface.go +++ b/node/repo/interface.go @@ -28,7 +28,7 @@ const ( var ( ErrNoAPIEndpoint = errors.New("API not running (no endpoint)") ErrNoAPIToken = errors.New("API token not set") - ErrRepoAlreadyLocked = errors.New("repo is already locked (lotus daemon already running)") + ErrRepoAlreadyLocked = errors.New("repo is already locked (process is already running)") ErrClosedRepo = errors.New("repo is no longer open") // ErrInvalidBlockstoreDomain is returned by LockedRepo#Blockstore() when diff --git a/provider/builder.go b/provider/builder.go deleted file mode 100644 index 08f18a5d8..000000000 --- a/provider/builder.go +++ /dev/null @@ -1,46 +0,0 @@ -package provider - -import ( - "context" - "time" - - "github.com/filecoin-project/lotus/api" - "github.com/filecoin-project/lotus/lib/harmony/harmonydb" - "github.com/filecoin-project/lotus/node/config" - dtypes "github.com/filecoin-project/lotus/node/modules/dtypes" - "github.com/filecoin-project/lotus/provider/chainsched" - "github.com/filecoin-project/lotus/provider/lpmessage" - "github.com/filecoin-project/lotus/provider/lpwindow" - "github.com/filecoin-project/lotus/provider/multictladdr" - "github.com/filecoin-project/lotus/storage/paths" - "github.com/filecoin-project/lotus/storage/sealer" - "github.com/filecoin-project/lotus/storage/sealer/storiface" -) - -//var log = logging.Logger("provider") - -func WindowPostScheduler(ctx context.Context, fc config.LotusProviderFees, pc config.ProvingConfig, - api api.FullNode, verif storiface.Verifier, lw *sealer.LocalWorker, sender *lpmessage.Sender, chainSched *chainsched.ProviderChainSched, - as *multictladdr.MultiAddressSelector, addresses map[dtypes.MinerAddress]bool, db *harmonydb.DB, - stor paths.Store, idx paths.SectorIndex, max int) (*lpwindow.WdPostTask, *lpwindow.WdPostSubmitTask, *lpwindow.WdPostRecoverDeclareTask, error) { - - // todo config - ft := lpwindow.NewSimpleFaultTracker(stor, idx, pc.ParallelCheckLimit, time.Duration(pc.SingleCheckTimeout), time.Duration(pc.PartitionCheckTimeout)) - - computeTask, err := lpwindow.NewWdPostTask(db, api, ft, lw, verif, chainSched, addresses, max) - if err != nil { - return nil, nil, nil, err - } - - submitTask, err := lpwindow.NewWdPostSubmitTask(chainSched, sender, db, api, fc.MaxWindowPoStGasFee, as) - if err != nil { - return nil, nil, nil, err - } - - recoverTask, err := lpwindow.NewWdPostRecoverDeclareTask(sender, db, api, ft, as, chainSched, fc.MaxWindowPoStGasFee, addresses) - if err != nil { - return nil, nil, nil, err - } - - return computeTask, submitTask, recoverTask, nil -} diff --git a/provider/lpweb/api/routes.go b/provider/lpweb/api/routes.go deleted file mode 100644 index 85b17486f..000000000 --- a/provider/lpweb/api/routes.go +++ /dev/null @@ -1,13 +0,0 @@ -// Package api provides the HTTP API for the lotus provider web gui. -package api - -import ( - "github.com/gorilla/mux" - - "github.com/filecoin-project/lotus/cmd/lotus-provider/deps" - "github.com/filecoin-project/lotus/provider/lpweb/api/debug" -) - -func Routes(r *mux.Router, deps *deps.Deps) { - debug.Routes(r.PathPrefix("/debug").Subrouter(), deps) -} diff --git a/scripts/lotus-provider.service b/scripts/curio.service similarity index 55% rename from scripts/lotus-provider.service rename to scripts/curio.service index ddec181ba..967a788fa 100644 --- a/scripts/lotus-provider.service +++ b/scripts/curio.service @@ -1,11 +1,11 @@ [Unit] -Description=Lotus Provider +Description=Curio After=network.target After=lotus-daemon.service [Service] -ExecStart=/usr/local/bin/lotus-provider run -Environment=GOLOG_FILE="/var/log/lotus/provider.log" +ExecStart=/usr/local/bin/curio run +Environment=GOLOG_FILE="/var/log/curio/curio.log" Environment=GOLOG_LOG_FMT="json" LimitNOFILE=1000000 [Install] diff --git a/scripts/generate-lotus-cli.py b/scripts/generate-lotus-cli.py index 1e1fee9a6..305716b09 100644 --- a/scripts/generate-lotus-cli.py +++ b/scripts/generate-lotus-cli.py @@ -55,4 +55,4 @@ if __name__ == "__main__": generate_lotus_cli('lotus') generate_lotus_cli('lotus-miner') generate_lotus_cli('lotus-worker') - generate_lotus_cli('lotus-provider') \ No newline at end of file + generate_lotus_cli('curio') \ No newline at end of file From 154cf09f523ee0a41e6b4ca5a808cb7dc5d8eb13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sun, 17 Mar 2024 17:40:56 +0100 Subject: [PATCH 270/394] make gen --- curiosrc/piece/task_cleanup_piece.go | 2 +- node/config/doc_gen.go | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/curiosrc/piece/task_cleanup_piece.go b/curiosrc/piece/task_cleanup_piece.go index 43c5d6f0c..ed22ccb46 100644 --- a/curiosrc/piece/task_cleanup_piece.go +++ b/curiosrc/piece/task_cleanup_piece.go @@ -2,11 +2,11 @@ package piece import ( "context" - "github.com/filecoin-project/lotus/curiosrc/ffi" "time" "golang.org/x/xerrors" + "github.com/filecoin-project/lotus/curiosrc/ffi" "github.com/filecoin-project/lotus/lib/harmony/harmonydb" "github.com/filecoin-project/lotus/lib/harmony/harmonytask" "github.com/filecoin-project/lotus/lib/harmony/resources" diff --git a/node/config/doc_gen.go b/node/config/doc_gen.go index 092af75c7..dcb832976 100644 --- a/node/config/doc_gen.go +++ b/node/config/doc_gen.go @@ -330,6 +330,22 @@ documentation.`, Comment: ``, }, + { + Name: "EnableParkPiece", + Type: "bool", + + Comment: `EnableParkPiece enables the "piece parking" task to run on this node. This task is responsible for fetching +pieces from the network and storing them in the storage subsystem until sectors are sealed. This task is +only applicable when integrating with boost, and should be enabled on nodes which will hold deal data +from boost until sectors containing the related pieces have the TreeD/TreeR constructed. +Note that future Curio implementations will have a separate task type for fetching pieces from the internet.`, + }, + { + Name: "ParkPieceMaxTasks", + Type: "int", + + Comment: ``, + }, { Name: "EnableSealSDR", Type: "bool", From b705274099fe3fa6dff8248de0175093fc5bfd5b Mon Sep 17 00:00:00 2001 From: Phi-rjan Date: Mon, 18 Mar 2024 10:31:47 +0100 Subject: [PATCH 271/394] chore: deps: update GST to v0.13.0-rc3 (#11732) chore: deps: update GST to v0.13.0-rc3 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 19483b332..06b80fc36 100644 --- a/go.mod +++ b/go.mod @@ -48,7 +48,7 @@ require ( github.com/filecoin-project/go-jsonrpc v0.3.1 github.com/filecoin-project/go-padreader v0.0.1 github.com/filecoin-project/go-paramfetch v0.0.4 - github.com/filecoin-project/go-state-types v0.13.0-rc.2 + github.com/filecoin-project/go-state-types v0.13.0-rc.3 github.com/filecoin-project/go-statemachine v1.0.3 github.com/filecoin-project/go-statestore v0.2.0 github.com/filecoin-project/go-storedcounter v0.1.0 diff --git a/go.sum b/go.sum index 932c70fff..5f0b05eec 100644 --- a/go.sum +++ b/go.sum @@ -348,8 +348,8 @@ github.com/filecoin-project/go-state-types v0.1.0/go.mod h1:ezYnPf0bNkTsDibL/psS github.com/filecoin-project/go-state-types v0.1.6/go.mod h1:UwGVoMsULoCK+bWjEdd/xLCvLAQFBC7EDT477SKml+Q= github.com/filecoin-project/go-state-types v0.1.10/go.mod h1:UwGVoMsULoCK+bWjEdd/xLCvLAQFBC7EDT477SKml+Q= github.com/filecoin-project/go-state-types v0.11.2-0.20230712101859-8f37624fa540/go.mod h1:SyNPwTsU7I22gL2r0OAPcImvLoTVfgRwdK/Y5rR1zz8= -github.com/filecoin-project/go-state-types v0.13.0-rc.2 h1:JHyDDx/nV8sbQNgjUfhumiGWh8Dedc8psbiVtD0YOh0= -github.com/filecoin-project/go-state-types v0.13.0-rc.2/go.mod h1:cHpOPup9H1g2T29dKHAjC2sc7/Ef5ypjuW9A3I+e9yY= +github.com/filecoin-project/go-state-types v0.13.0-rc.3 h1:gs+5uKYo2hDufhMzVfTWRsTp00rEY6nK/gYtTxj79RY= +github.com/filecoin-project/go-state-types v0.13.0-rc.3/go.mod h1:cHpOPup9H1g2T29dKHAjC2sc7/Ef5ypjuW9A3I+e9yY= github.com/filecoin-project/go-statemachine v0.0.0-20200925024713-05bd7c71fbfe/go.mod h1:FGwQgZAt2Gh5mjlwJUlVB62JeYdo+if0xWxSEfBD9ig= github.com/filecoin-project/go-statemachine v1.0.3 h1:N07o6alys+V1tNoSTi4WuuoeNC4erS/6jE74+NsgQuk= github.com/filecoin-project/go-statemachine v1.0.3/go.mod h1:jZdXXiHa61n4NmgWFG4w8tnqgvZVHYbJ3yW7+y8bF54= From ebf8cf9e624bae5dc0cf9a28cfb9afc826a02cba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 18 Mar 2024 20:03:26 +0100 Subject: [PATCH 272/394] fix: curio: Fix env name in cli.go (#11737) --- cmd/curio/cli.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/curio/cli.go b/cmd/curio/cli.go index ea3c13d72..58383e2a7 100644 --- a/cmd/curio/cli.go +++ b/cmd/curio/cli.go @@ -22,7 +22,7 @@ import ( "github.com/filecoin-project/lotus/cmd/curio/rpc" ) -const providerEnvVar = "PROVIDER_API_INFO" +const providerEnvVar = "CURIO_API_INFO" var cliCmd = &cli.Command{ Name: "cli", From 6179e40b24818b117bc62e7b8c15d84a825169c2 Mon Sep 17 00:00:00 2001 From: Piotr Galar Date: Mon, 18 Mar 2024 19:29:57 +0000 Subject: [PATCH 273/394] ci: chore: Clean up .github/workflows before starting the migration from CircleCI (#11735) * ipdx: delete unused actions * ipdx: update action versions * ipdx: remove label syncer --- .github/labels.yml | 248 ---------------------- .github/workflows/builtin-actor-tests.yml | 6 +- .github/workflows/codeql-analysis.yml | 73 ------- .github/workflows/label-syncer.yml | 17 -- .github/workflows/stale.yml | 6 +- .github/workflows/sync-master-main.yaml | 2 +- .github/workflows/testground-on-push.yml | 29 --- 7 files changed, 6 insertions(+), 375 deletions(-) delete mode 100644 .github/labels.yml delete mode 100644 .github/workflows/codeql-analysis.yml delete mode 100644 .github/workflows/label-syncer.yml delete mode 100644 .github/workflows/testground-on-push.yml diff --git a/.github/labels.yml b/.github/labels.yml deleted file mode 100644 index 7102f1311..000000000 --- a/.github/labels.yml +++ /dev/null @@ -1,248 +0,0 @@ -### -### Special magic GitHub labels -### https://help.github.com/en/github/building-a-strong-community/encouraging-helpful-contributions-to-your-project-with-labels -# -- name: "good first issue" - color: 7057ff - description: "Good for newcomers" -- name: "help wanted" - color: 008672 - description: "Extra attention is needed" - -### -### Goals -# -- name: goal/incentives - color: ff004d - description: "Incentinet" - -### -### Areas -# -- name: area/ux - color: 00A4E0 - description: "Area: UX" -- name: area/chain/vm - color: 00A4E2 - description: "Area: Chain/VM" -- name: area/chain/sync - color: 00A4E4 - description: "Area: Chain/Sync" -- name: area/chain/misc - color: 00A4E6 - description: "Area: Chain/Misc" -- name: area/markets - color: 00A4E8 - description: "Area: Markets" -- name: area/sealing/fsm - color: 0bb1ed - description: "Area: Sealing/FSM" -- name: area/sealing/storage - color: 0EB4F0 - description: "Area: Sealing/Storage" -- name: area/proving - color: 0EB4F0 - description: "Area: Proving" -- name: area/mining - color: 10B6F2 - description: "Area: Mining" -- name: area/client/storage - color: 13B9F5 - description: "Area: Client/Storage" -- name: area/client/retrieval - color: 15BBF7 - description: "Area: Client/Retrieval" -- name: area/wallet - color: 15BBF7 - description: "Area: Wallet" -- name: area/payment-channel - color: ff6767 - description: "Area: Payment Channel" -- name: area/multisig - color: fff0ff - description: "Area: Multisig" -- name: area/networking - color: 273f8a - description: "Area: Networking" - -### -### Kinds -# -- name: kind/bug - color: c92712 - description: "Kind: Bug" -- name: kind/chore - color: fcf0b5 - description: "Kind: Chore" -- name: kind/feature - color: FFF3B8 - description: "Kind: Feature" -- name: kind/improvement - color: FFF5BA - description: "Kind: Improvement" -- name: kind/test - color: FFF8BD - description: "Kind: Test" -- name: kind/question - color: FFFDC2 - description: "Kind: Question" -- name: kind/enhancement - color: FFFFC5 - description: "Kind: Enhancement" -- name: kind/discussion - color: FFFFC7 - description: "Kind: Discussion" - -### -### Difficulties -# -- name: dif/trivial - color: b2b7ff - description: "Can be confidently tackled by newcomers, who are widely unfamiliar with lotus" -- name: dif/easy - color: 7886d7 - description: "An existing lotus user should be able to pick this up" -- name: dif/medium - color: 6574cd - description: "Prior development experience with lotus is likely helpful" -- name: dif/hard - color: 5661b3 - description: "Suggests that having worked on the specific component affected by this issue is important" -- name: dif/expert - color: 2f365f - description: "Requires extensive knowledge of the history, implications, ramifications of the issue" - -### -### Efforts -# -- name: effort/minutes - color: e8fffe - description: "Effort: Minutes" -- name: effort/hours - color: a0f0ed - description: "Effort: Hours" -- name: effort/day - color: 64d5ca - description: "Effort: One Day" -- name: effort/days - color: 4dc0b5 - description: "Effort: Multiple Days" -- name: effort/week - color: 38a89d - description: "Effort: One Week" -- name: effort/weeks - color: 20504f - description: "Effort: Multiple Weeks" - -### -### Impacts -# -- name: impact/regression - color: f1f5f8 - description: "Impact: Regression" -- name: impact/api-breakage - color: ECF0F3 - description: "Impact: API Breakage" -- name: impact/quality - color: E7EBEE - description: "Impact: Quality" -- name: impact/dx - color: E2E6E9 - description: "Impact: Developer Experience" -- name: impact/test-flakiness - color: DDE1E4 - description: "Impact: Test Flakiness" -- name: impact/consensus - color: b20014 - description: "Impact: Consensus" - -### -### Topics -# -- name: topic/interoperability - color: bf0f73 - description: "Topic: Interoperability" -- name: topic/specs - color: CC1C80 - description: "Topic: Specs" -- name: topic/docs - color: D9298D - description: "Topic: Documentation" -- name: topic/architecture - color: E53599 - description: "Topic: Architecture" - -### -### Priorities -### -- name: P0 - color: dd362a - description: "P0: Critical Blocker" -- name: P1 - color: ce8048 - description: "P1: Must be resolved" -- name: P2 - color: dbd81a - description: "P2: Should be resolved" -- name: P3 - color: 9fea8f - description: "P3: Might get resolved" - -### -### Hints -# -#- name: hint/good-first-issue -# color: 7057ff -# description: "Hint: Good First Issue" -#- name: hint/help-wanted -# color: 008672 -# description: "Hint: Help Wanted" -- name: hint/needs-decision - color: 33B9A5 - description: "Hint: Needs Decision" -- name: hint/needs-triage - color: 1AA08C - description: "Hint: Needs Triage" -- name: hint/needs-analysis - color: 26AC98 - description: "Hint: Needs Analysis" -- name: hint/needs-author-input - color: 33B9A5 - description: "Hint: Needs Author Input" -- name: hint/needs-team-input - color: 40C6B2 - description: "Hint: Needs Team Input" -- name: hint/needs-community-input - color: 4DD3BF - description: "Hint: Needs Community Input" -- name: hint/needs-review - color: 5AE0CC - description: "Hint: Needs Review" - -### -### Statuses -# -- name: status/done - color: edb3a6 - description: "Status: Done" -- name: status/deferred - color: E0A699 - description: "Status: Deferred" -- name: status/in-progress - color: D49A8D - description: "Status: In Progress" -- name: status/blocked - color: C78D80 - description: "Status: Blocked" -- name: status/inactive - color: BA8073 - description: "Status: Inactive" -- name: status/waiting - color: AD7366 - description: "Status: Waiting" -- name: status/rotten - color: 7A4033 - description: "Status: Rotten" -- name: status/discarded - color: 6D3326 - description: "Status: Discarded / Won't fix" diff --git a/.github/workflows/builtin-actor-tests.yml b/.github/workflows/builtin-actor-tests.yml index 6cc49cc46..e0d95de51 100644 --- a/.github/workflows/builtin-actor-tests.yml +++ b/.github/workflows/builtin-actor-tests.yml @@ -11,8 +11,8 @@ jobs: name: Release Tests runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-go@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 with: go-version: 1.21 - - run: go test -tags=release ./build \ No newline at end of file + - run: go test -tags=release ./build diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml deleted file mode 100644 index 0cba5457e..000000000 --- a/.github/workflows/codeql-analysis.yml +++ /dev/null @@ -1,73 +0,0 @@ -# For most projects, this workflow file will not need changing; you simply need -# to commit it to your repository. -# -# You may wish to alter this file to override the set of languages analyzed, -# or to provide custom queries or build logic. -# -# ******** NOTE ******** -# We have attempted to detect the languages in your repository. Please check -# the `language` matrix defined below to confirm you have the correct set of -# supported CodeQL languages. -# -name: "CodeQL" - -on: - push: - branches: - - master - - 'release/*' - pull_request: - # The branches below must be a subset of the branches above - branches: - - master - - 'release/*' - -jobs: - analyze: - name: Analyze - runs-on: ubuntu-latest - - strategy: - fail-fast: false - matrix: - language: [ 'go' ] - # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] - # Learn more: - # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed - - steps: - - name: Checkout repository - uses: actions/checkout@v3 - - - uses: actions/setup-go@v3 - with: - go-version: '1.18.8' - - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v2 - with: - languages: go - # If you wish to specify custom queries, you can do so here or in a config file. - # By default, queries listed here will override any specified in a config file. - # Prefix the list here with "+" to use these queries and those in the config file. - # queries: ./path/to/local/query, your-org/your-repo/queries@main - - # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). - # If this step fails, then you should remove it and run the build manually (see below) - - name: Autobuild - uses: github/codeql-action/autobuild@v2 - - # ℹ️ Command-line programs to run using the OS shell. - # 📚 https://git.io/JvXDl - - # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines - # and modify them (or add more) to build your code if your project - # uses a compiled language - - #- run: | - # make bootstrap - # make release - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 diff --git a/.github/workflows/label-syncer.yml b/.github/workflows/label-syncer.yml deleted file mode 100644 index a94b0edb6..000000000 --- a/.github/workflows/label-syncer.yml +++ /dev/null @@ -1,17 +0,0 @@ - -name: Label syncer -on: - push: - paths: - - '.github/labels.yml' - branches: - - master -jobs: - build: - name: Sync labels - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@1.0.0 - - uses: micnncim/action-label-syncer@v1.0.0 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 35b97e369..c81d72430 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -13,9 +13,9 @@ jobs: pull-requests: write steps: - - uses: actions/stale@v3 + - uses: actions/stale@v9 with: - repo-token: ${{ secrets.GITHUB_TOKEN }} + repo-token: ${{ github.token }} stale-issue-message: 'Oops, seems like we needed more information for this issue, please comment with more details or this issue will be closed in 24 hours.' close-issue-message: 'This issue was closed because it is missing author input.' stale-pr-message: 'Thank you for submitting the PR and contributing to lotus! Lotus maintainers need more of your input before merging it, please address the suggested changes or reply to the comments or this PR will be closed in 48 hours. You are always more than welcome to reopen the PR later as well!' @@ -29,5 +29,3 @@ jobs: days-before-pr-close: 2 remove-stale-when-updated: true enable-statistics: true - - diff --git a/.github/workflows/sync-master-main.yaml b/.github/workflows/sync-master-main.yaml index a55454eff..6a7e9a650 100644 --- a/.github/workflows/sync-master-main.yaml +++ b/.github/workflows/sync-master-main.yaml @@ -7,7 +7,7 @@ jobs: sync: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: update remote branch main run: | # overrides the remote branch (origin:github) `main` diff --git a/.github/workflows/testground-on-push.yml b/.github/workflows/testground-on-push.yml deleted file mode 100644 index 8e749bfae..000000000 --- a/.github/workflows/testground-on-push.yml +++ /dev/null @@ -1,29 +0,0 @@ ---- -name: Testground PR Checker - -on: [push] - -jobs: - testground: - runs-on: ubuntu-latest - name: ${{ matrix.composition_file }} - strategy: - matrix: - include: - - backend_addr: ci.testground.ipfs.team - backend_proto: https - plan_directory: testplans/lotus-soup - composition_file: testplans/lotus-soup/_compositions/baseline-k8s-3-1.toml - - backend_addr: ci.testground.ipfs.team - backend_proto: https - plan_directory: testplans/lotus-soup - composition_file: testplans/lotus-soup/_compositions/paych-stress-k8s.toml - steps: - - uses: actions/checkout@v2 - - name: testground run - uses: testground/testground-github-action@v1 - with: - backend_addr: ${{ matrix.backend_addr }} - backend_proto: ${{ matrix.backend_proto }} - plan_directory: ${{ matrix.plan_directory }} - composition_file: ${{ matrix.composition_file }} From 73947ea365893d73daeb921705e984318bbca0c0 Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Tue, 19 Mar 2024 11:37:46 +1100 Subject: [PATCH 274/394] feat(events): add "Raw" suffix to {Get,Subscribe}ActorEvents This is done with the intention to add new {Get,Subscribe}ActorEvents in a future release (i.e. soon!) with both decoded values (dag-json represented) and simplified (no flags or codec). But because this comes with some trade-offs wrt fidelity of information (e.g. likely needing to drop events with badly encoded values, and not retaining original codec), we need to also have a Raw form of these APIs for consumers that want to take on the burden of consuming them as they are. --- CHANGELOG.md | 10 +++--- api/api_full.go | 8 ++--- api/api_gateway.go | 4 +-- api/mocks/mock_full.go | 24 ++++++------- api/proxy_gen.go | 40 ++++++++++----------- build/openrpc/full.json | 6 ++-- build/openrpc/gateway.json | 4 +-- documentation/en/api-v1-unstable-methods.md | 12 +++---- gateway/node.go | 4 +-- gateway/proxy_fil.go | 8 ++--- itests/direct_data_onboard_test.go | 2 +- itests/direct_data_onboard_verified_test.go | 32 ++++++++--------- itests/sector_terminate_test.go | 2 +- node/impl/full/actor_events.go | 8 ++--- node/impl/full/actor_events_test.go | 14 ++++---- node/impl/full/dummy.go | 4 +-- 16 files changed, 92 insertions(+), 90 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e1f6f457..4485fe8dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -132,20 +132,22 @@ Additionally, Filecoin is not Ethereum no matter how much we try to provide API/ [handlefilecoinmethod]: https://fips.filecoin.io/FIPS/fip-0054.html#handlefilecoinmethod-general-handler-for-method-numbers--1024 -### GetActorEvents and SubscribeActorEvents +### GetActorEventsRaw and SubscribeActorEventsRaw [FIP-0049](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0049.md) introduced _Actor Events_ that can be emitted by user programmed actors. [FIP-0083](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0083.md) introduces new events emitted by the builtin Verified Registry, Miner and Market Actors. These new events for builtin actors are being activated with network version 22 to coincide with _Direct Data Onboarding_ as defined in [FIP-0076](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0076.md) which introduces additional flexibility for data onboarding. Sector, Deal and DataCap lifecycles can be tracked with these events, providing visibility and options for programmatic responses to changes in state. -Actor events are available on message receipts, but can now be retrieved from a node using the new `GetActorEvents` and `SubscribeActorEvents` methods. These methods allow for querying and subscribing to actor events, respectively. They depend on the Lotus node both collecting events (with `Fevm.Events.RealTimeFilterAPI` and `Fevm.Events.HistoricFilterAPI`) and being enabled with the new configuration option `Events.EnableActorEventsAPI`. Note that a Lotus node can only respond to requests for historic events that it retains in its event store. +Actor events are available on message receipts, but can now be retrieved from a node using the new `GetActorEventsRaw` and `SubscribeActorEventsRaw` methods. These methods allow for querying and subscribing to actor events, respectively. They depend on the Lotus node both collecting events (with `Fevm.Events.RealTimeFilterAPI` and `Fevm.Events.HistoricFilterAPI`) and being enabled with the new configuration option `Events.EnableActorEventsAPI`. Note that a Lotus node can only respond to requests for historic events that it retains in its event store. -Both `GetActorEvents` and `SubscribeActorEvents` take a filter parameter which can optionally filter events on: +Both `GetActorEventsRaw` and `SubscribeActorEventsRaw` take a filter parameter which can optionally filter events on: * `Addresses` of the actor(s) emitting the event * Specific `Fields` within the event * `FromHeight` and `ToHeight` to filter events by block height * `TipSetKey` to restrict events contained within a specific tipset -`GetActorEvents` provides a one-time query for actor events, while `SubscribeActorEvents` provides a long-lived connection (via websockets) to the Lotus node, allowing for real-time updates on actor events. The subscription can be cancelled by the client at any time. +`GetActorEventsRaw` provides a one-time query for actor events, while `SubscribeActorEventsRaw` provides a long-lived connection (via websockets) to the Lotus node, allowing for real-time updates on actor events. The subscription can be cancelled by the client at any time. + +A future Lotus release may include `GetActorEvents` and `SubscribeActorEvents` methods which will provide a more user-friendly interface to actor events, including deserialization of event data. ### GetAllClaims and GetAllAlocations Additionally the methods `GetAllAllocations` and `GetAllClaims` has been added to the Lotus API. These methods lists all the available allocations and claims available in the actor state. diff --git a/api/api_full.go b/api/api_full.go index e61bb0ded..85233a712 100644 --- a/api/api_full.go +++ b/api/api_full.go @@ -906,16 +906,16 @@ type FullNode interface { // Actor events - // GetActorEvents returns all user-programmed and built-in actor events that match the given + // GetActorEventsRaw returns all user-programmed and built-in actor events that match the given // filter. // This is a request/response API. // Results available from this API may be limited by the MaxFilterResults and MaxFilterHeightRange // configuration options and also the amount of historical data available in the node. // // This is an EXPERIMENTAL API and may be subject to change. - GetActorEvents(ctx context.Context, filter *types.ActorEventFilter) ([]*types.ActorEvent, error) //perm:read + GetActorEventsRaw(ctx context.Context, filter *types.ActorEventFilter) ([]*types.ActorEvent, error) //perm:read - // SubscribeActorEvents returns a long-lived stream of all user-programmed and built-in actor + // SubscribeActorEventsRaw returns a long-lived stream of all user-programmed and built-in actor // events that match the given filter. // Events that match the given filter are written to the stream in real-time as they are emitted // from the FVM. @@ -929,7 +929,7 @@ type FullNode interface { // // Note: this API is only available via websocket connections. // This is an EXPERIMENTAL API and may be subject to change. - SubscribeActorEvents(ctx context.Context, filter *types.ActorEventFilter) (<-chan *types.ActorEvent, error) //perm:read + SubscribeActorEventsRaw(ctx context.Context, filter *types.ActorEventFilter) (<-chan *types.ActorEvent, error) //perm:read } // reverse interface to the client, called after EthSubscribe diff --git a/api/api_gateway.go b/api/api_gateway.go index c5bed3621..e70c00e3a 100644 --- a/api/api_gateway.go +++ b/api/api_gateway.go @@ -131,7 +131,7 @@ type Gateway interface { EthTraceBlock(ctx context.Context, blkNum string) ([]*ethtypes.EthTraceBlock, error) EthTraceReplayBlockTransactions(ctx context.Context, blkNum string, traceTypes []string) ([]*ethtypes.EthTraceReplayBlockTransaction, error) - GetActorEvents(ctx context.Context, filter *types.ActorEventFilter) ([]*types.ActorEvent, error) - SubscribeActorEvents(ctx context.Context, filter *types.ActorEventFilter) (<-chan *types.ActorEvent, error) + GetActorEventsRaw(ctx context.Context, filter *types.ActorEventFilter) ([]*types.ActorEvent, error) + SubscribeActorEventsRaw(ctx context.Context, filter *types.ActorEventFilter) (<-chan *types.ActorEvent, error) ChainGetEvents(context.Context, cid.Cid) ([]types.Event, error) } diff --git a/api/mocks/mock_full.go b/api/mocks/mock_full.go index 215ccdfeb..9960faeff 100644 --- a/api/mocks/mock_full.go +++ b/api/mocks/mock_full.go @@ -1627,19 +1627,19 @@ func (mr *MockFullNodeMockRecorder) GasEstimateMessageGas(arg0, arg1, arg2, arg3 return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GasEstimateMessageGas", reflect.TypeOf((*MockFullNode)(nil).GasEstimateMessageGas), arg0, arg1, arg2, arg3) } -// GetActorEvents mocks base method. -func (m *MockFullNode) GetActorEvents(arg0 context.Context, arg1 *types.ActorEventFilter) ([]*types.ActorEvent, error) { +// GetActorEventsRaw mocks base method. +func (m *MockFullNode) GetActorEventsRaw(arg0 context.Context, arg1 *types.ActorEventFilter) ([]*types.ActorEvent, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetActorEvents", arg0, arg1) + ret := m.ctrl.Call(m, "GetActorEventsRaw", arg0, arg1) ret0, _ := ret[0].([]*types.ActorEvent) ret1, _ := ret[1].(error) return ret0, ret1 } -// GetActorEvents indicates an expected call of GetActorEvents. -func (mr *MockFullNodeMockRecorder) GetActorEvents(arg0, arg1 interface{}) *gomock.Call { +// GetActorEventsRaw indicates an expected call of GetActorEventsRaw. +func (mr *MockFullNodeMockRecorder) GetActorEventsRaw(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetActorEvents", reflect.TypeOf((*MockFullNode)(nil).GetActorEvents), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetActorEventsRaw", reflect.TypeOf((*MockFullNode)(nil).GetActorEventsRaw), arg0, arg1) } // ID mocks base method. @@ -3954,19 +3954,19 @@ func (mr *MockFullNodeMockRecorder) StateWaitMsg(arg0, arg1, arg2, arg3, arg4 in return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StateWaitMsg", reflect.TypeOf((*MockFullNode)(nil).StateWaitMsg), arg0, arg1, arg2, arg3, arg4) } -// SubscribeActorEvents mocks base method. -func (m *MockFullNode) SubscribeActorEvents(arg0 context.Context, arg1 *types.ActorEventFilter) (<-chan *types.ActorEvent, error) { +// SubscribeActorEventsRaw mocks base method. +func (m *MockFullNode) SubscribeActorEventsRaw(arg0 context.Context, arg1 *types.ActorEventFilter) (<-chan *types.ActorEvent, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SubscribeActorEvents", arg0, arg1) + ret := m.ctrl.Call(m, "SubscribeActorEventsRaw", arg0, arg1) ret0, _ := ret[0].(<-chan *types.ActorEvent) ret1, _ := ret[1].(error) return ret0, ret1 } -// SubscribeActorEvents indicates an expected call of SubscribeActorEvents. -func (mr *MockFullNodeMockRecorder) SubscribeActorEvents(arg0, arg1 interface{}) *gomock.Call { +// SubscribeActorEventsRaw indicates an expected call of SubscribeActorEventsRaw. +func (mr *MockFullNodeMockRecorder) SubscribeActorEventsRaw(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubscribeActorEvents", reflect.TypeOf((*MockFullNode)(nil).SubscribeActorEvents), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubscribeActorEventsRaw", reflect.TypeOf((*MockFullNode)(nil).SubscribeActorEventsRaw), arg0, arg1) } // SyncCheckBad mocks base method. diff --git a/api/proxy_gen.go b/api/proxy_gen.go index 58214ae8b..1cd44092e 100644 --- a/api/proxy_gen.go +++ b/api/proxy_gen.go @@ -366,7 +366,7 @@ type FullNodeMethods struct { GasEstimateMessageGas func(p0 context.Context, p1 *types.Message, p2 *MessageSendSpec, p3 types.TipSetKey) (*types.Message, error) `perm:"read"` - GetActorEvents func(p0 context.Context, p1 *types.ActorEventFilter) ([]*types.ActorEvent, error) `perm:"read"` + GetActorEventsRaw func(p0 context.Context, p1 *types.ActorEventFilter) ([]*types.ActorEvent, error) `perm:"read"` MarketAddBalance func(p0 context.Context, p1 address.Address, p2 address.Address, p3 types.BigInt) (cid.Cid, error) `perm:"sign"` @@ -618,7 +618,7 @@ type FullNodeMethods struct { StateWaitMsg func(p0 context.Context, p1 cid.Cid, p2 uint64, p3 abi.ChainEpoch, p4 bool) (*MsgLookup, error) `perm:"read"` - SubscribeActorEvents func(p0 context.Context, p1 *types.ActorEventFilter) (<-chan *types.ActorEvent, error) `perm:"read"` + SubscribeActorEventsRaw func(p0 context.Context, p1 *types.ActorEventFilter) (<-chan *types.ActorEvent, error) `perm:"read"` SyncCheckBad func(p0 context.Context, p1 cid.Cid) (string, error) `perm:"read"` @@ -788,7 +788,7 @@ type GatewayMethods struct { GasEstimateMessageGas func(p0 context.Context, p1 *types.Message, p2 *MessageSendSpec, p3 types.TipSetKey) (*types.Message, error) `` - GetActorEvents func(p0 context.Context, p1 *types.ActorEventFilter) ([]*types.ActorEvent, error) `` + GetActorEventsRaw func(p0 context.Context, p1 *types.ActorEventFilter) ([]*types.ActorEvent, error) `` MinerGetBaseInfo func(p0 context.Context, p1 address.Address, p2 abi.ChainEpoch, p3 types.TipSetKey) (*MiningBaseInfo, error) `` @@ -866,7 +866,7 @@ type GatewayMethods struct { StateWaitMsg func(p0 context.Context, p1 cid.Cid, p2 uint64, p3 abi.ChainEpoch, p4 bool) (*MsgLookup, error) `` - SubscribeActorEvents func(p0 context.Context, p1 *types.ActorEventFilter) (<-chan *types.ActorEvent, error) `` + SubscribeActorEventsRaw func(p0 context.Context, p1 *types.ActorEventFilter) (<-chan *types.ActorEvent, error) `` Version func(p0 context.Context) (APIVersion, error) `` @@ -2720,14 +2720,14 @@ func (s *FullNodeStub) GasEstimateMessageGas(p0 context.Context, p1 *types.Messa return nil, ErrNotSupported } -func (s *FullNodeStruct) GetActorEvents(p0 context.Context, p1 *types.ActorEventFilter) ([]*types.ActorEvent, error) { - if s.Internal.GetActorEvents == nil { +func (s *FullNodeStruct) GetActorEventsRaw(p0 context.Context, p1 *types.ActorEventFilter) ([]*types.ActorEvent, error) { + if s.Internal.GetActorEventsRaw == nil { return *new([]*types.ActorEvent), ErrNotSupported } - return s.Internal.GetActorEvents(p0, p1) + return s.Internal.GetActorEventsRaw(p0, p1) } -func (s *FullNodeStub) GetActorEvents(p0 context.Context, p1 *types.ActorEventFilter) ([]*types.ActorEvent, error) { +func (s *FullNodeStub) GetActorEventsRaw(p0 context.Context, p1 *types.ActorEventFilter) ([]*types.ActorEvent, error) { return *new([]*types.ActorEvent), ErrNotSupported } @@ -4106,14 +4106,14 @@ func (s *FullNodeStub) StateWaitMsg(p0 context.Context, p1 cid.Cid, p2 uint64, p return nil, ErrNotSupported } -func (s *FullNodeStruct) SubscribeActorEvents(p0 context.Context, p1 *types.ActorEventFilter) (<-chan *types.ActorEvent, error) { - if s.Internal.SubscribeActorEvents == nil { +func (s *FullNodeStruct) SubscribeActorEventsRaw(p0 context.Context, p1 *types.ActorEventFilter) (<-chan *types.ActorEvent, error) { + if s.Internal.SubscribeActorEventsRaw == nil { return nil, ErrNotSupported } - return s.Internal.SubscribeActorEvents(p0, p1) + return s.Internal.SubscribeActorEventsRaw(p0, p1) } -func (s *FullNodeStub) SubscribeActorEvents(p0 context.Context, p1 *types.ActorEventFilter) (<-chan *types.ActorEvent, error) { +func (s *FullNodeStub) SubscribeActorEventsRaw(p0 context.Context, p1 *types.ActorEventFilter) (<-chan *types.ActorEvent, error) { return nil, ErrNotSupported } @@ -4975,14 +4975,14 @@ func (s *GatewayStub) GasEstimateMessageGas(p0 context.Context, p1 *types.Messag return nil, ErrNotSupported } -func (s *GatewayStruct) GetActorEvents(p0 context.Context, p1 *types.ActorEventFilter) ([]*types.ActorEvent, error) { - if s.Internal.GetActorEvents == nil { +func (s *GatewayStruct) GetActorEventsRaw(p0 context.Context, p1 *types.ActorEventFilter) ([]*types.ActorEvent, error) { + if s.Internal.GetActorEventsRaw == nil { return *new([]*types.ActorEvent), ErrNotSupported } - return s.Internal.GetActorEvents(p0, p1) + return s.Internal.GetActorEventsRaw(p0, p1) } -func (s *GatewayStub) GetActorEvents(p0 context.Context, p1 *types.ActorEventFilter) ([]*types.ActorEvent, error) { +func (s *GatewayStub) GetActorEventsRaw(p0 context.Context, p1 *types.ActorEventFilter) ([]*types.ActorEvent, error) { return *new([]*types.ActorEvent), ErrNotSupported } @@ -5404,14 +5404,14 @@ func (s *GatewayStub) StateWaitMsg(p0 context.Context, p1 cid.Cid, p2 uint64, p3 return nil, ErrNotSupported } -func (s *GatewayStruct) SubscribeActorEvents(p0 context.Context, p1 *types.ActorEventFilter) (<-chan *types.ActorEvent, error) { - if s.Internal.SubscribeActorEvents == nil { +func (s *GatewayStruct) SubscribeActorEventsRaw(p0 context.Context, p1 *types.ActorEventFilter) (<-chan *types.ActorEvent, error) { + if s.Internal.SubscribeActorEventsRaw == nil { return nil, ErrNotSupported } - return s.Internal.SubscribeActorEvents(p0, p1) + return s.Internal.SubscribeActorEventsRaw(p0, p1) } -func (s *GatewayStub) SubscribeActorEvents(p0 context.Context, p1 *types.ActorEventFilter) (<-chan *types.ActorEvent, error) { +func (s *GatewayStub) SubscribeActorEventsRaw(p0 context.Context, p1 *types.ActorEventFilter) (<-chan *types.ActorEvent, error) { return nil, ErrNotSupported } diff --git a/build/openrpc/full.json b/build/openrpc/full.json index bf949dbf5..35e2fd9e4 100644 --- a/build/openrpc/full.json +++ b/build/openrpc/full.json @@ -9392,9 +9392,9 @@ } }, { - "name": "Filecoin.GetActorEvents", - "description": "```go\nfunc (s *FullNodeStruct) GetActorEvents(p0 context.Context, p1 *types.ActorEventFilter) ([]*types.ActorEvent, error) {\n\tif s.Internal.GetActorEvents == nil {\n\t\treturn *new([]*types.ActorEvent), ErrNotSupported\n\t}\n\treturn s.Internal.GetActorEvents(p0, p1)\n}\n```", - "summary": "GetActorEvents returns all user-programmed and built-in actor events that match the given\nfilter.\nThis is a request/response API.\nResults available from this API may be limited by the MaxFilterResults and MaxFilterHeightRange\nconfiguration options and also the amount of historical data available in the node.\n\nThis is an EXPERIMENTAL API and may be subject to change.\n", + "name": "Filecoin.GetActorEventsRaw", + "description": "```go\nfunc (s *FullNodeStruct) GetActorEventsRaw(p0 context.Context, p1 *types.ActorEventFilter) ([]*types.ActorEvent, error) {\n\tif s.Internal.GetActorEventsRaw == nil {\n\t\treturn *new([]*types.ActorEvent), ErrNotSupported\n\t}\n\treturn s.Internal.GetActorEventsRaw(p0, p1)\n}\n```", + "summary": "GetActorEventsRaw returns all user-programmed and built-in actor events that match the given\nfilter.\nThis is a request/response API.\nResults available from this API may be limited by the MaxFilterResults and MaxFilterHeightRange\nconfiguration options and also the amount of historical data available in the node.\n\nThis is an EXPERIMENTAL API and may be subject to change.\n", "paramStructure": "by-position", "params": [ { diff --git a/build/openrpc/gateway.json b/build/openrpc/gateway.json index b2ffc2ab5..039648e59 100644 --- a/build/openrpc/gateway.json +++ b/build/openrpc/gateway.json @@ -5219,8 +5219,8 @@ } }, { - "name": "Filecoin.GetActorEvents", - "description": "```go\nfunc (s *GatewayStruct) GetActorEvents(p0 context.Context, p1 *types.ActorEventFilter) ([]*types.ActorEvent, error) {\n\tif s.Internal.GetActorEvents == nil {\n\t\treturn *new([]*types.ActorEvent), ErrNotSupported\n\t}\n\treturn s.Internal.GetActorEvents(p0, p1)\n}\n```", + "name": "Filecoin.GetActorEventsRaw", + "description": "```go\nfunc (s *GatewayStruct) GetActorEventsRaw(p0 context.Context, p1 *types.ActorEventFilter) ([]*types.ActorEvent, error) {\n\tif s.Internal.GetActorEventsRaw == nil {\n\t\treturn *new([]*types.ActorEvent), ErrNotSupported\n\t}\n\treturn s.Internal.GetActorEventsRaw(p0, p1)\n}\n```", "summary": "There are not yet any comments for this method.", "paramStructure": "by-position", "params": [ diff --git a/documentation/en/api-v1-unstable-methods.md b/documentation/en/api-v1-unstable-methods.md index 287cfce53..dee290b34 100644 --- a/documentation/en/api-v1-unstable-methods.md +++ b/documentation/en/api-v1-unstable-methods.md @@ -116,7 +116,7 @@ * [GasEstimateGasPremium](#GasEstimateGasPremium) * [GasEstimateMessageGas](#GasEstimateMessageGas) * [Get](#Get) - * [GetActorEvents](#GetActorEvents) + * [GetActorEventsRaw](#GetActorEventsRaw) * [I](#I) * [ID](#ID) * [Log](#Log) @@ -282,7 +282,7 @@ * [StateVerifierStatus](#StateVerifierStatus) * [StateWaitMsg](#StateWaitMsg) * [Subscribe](#Subscribe) - * [SubscribeActorEvents](#SubscribeActorEvents) + * [SubscribeActorEventsRaw](#SubscribeActorEventsRaw) * [Sync](#Sync) * [SyncCheckBad](#SyncCheckBad) * [SyncCheckpoint](#SyncCheckpoint) @@ -3386,8 +3386,8 @@ Response: ## Get -### GetActorEvents -GetActorEvents returns all user-programmed and built-in actor events that match the given +### GetActorEventsRaw +GetActorEventsRaw returns all user-programmed and built-in actor events that match the given filter. This is a request/response API. Results available from this API may be limited by the MaxFilterResults and MaxFilterHeightRange @@ -8801,8 +8801,8 @@ Response: ## Subscribe -### SubscribeActorEvents -SubscribeActorEvents returns a long-lived stream of all user-programmed and built-in actor +### SubscribeActorEventsRaw +SubscribeActorEventsRaw returns a long-lived stream of all user-programmed and built-in actor events that match the given filter. Events that match the given filter are written to the stream in real-time as they are emitted from the FVM. diff --git a/gateway/node.go b/gateway/node.go index 814d9c8cb..e9c695c4a 100644 --- a/gateway/node.go +++ b/gateway/node.go @@ -147,8 +147,8 @@ type TargetAPI interface { EthTraceBlock(ctx context.Context, blkNum string) ([]*ethtypes.EthTraceBlock, error) EthTraceReplayBlockTransactions(ctx context.Context, blkNum string, traceTypes []string) ([]*ethtypes.EthTraceReplayBlockTransaction, error) - GetActorEvents(ctx context.Context, filter *types.ActorEventFilter) ([]*types.ActorEvent, error) - SubscribeActorEvents(ctx context.Context, filter *types.ActorEventFilter) (<-chan *types.ActorEvent, error) + GetActorEventsRaw(ctx context.Context, filter *types.ActorEventFilter) ([]*types.ActorEvent, error) + SubscribeActorEventsRaw(ctx context.Context, filter *types.ActorEventFilter) (<-chan *types.ActorEvent, error) ChainGetEvents(ctx context.Context, eventsRoot cid.Cid) ([]types.Event, error) } diff --git a/gateway/proxy_fil.go b/gateway/proxy_fil.go index e86d9e0c5..9daa0796d 100644 --- a/gateway/proxy_fil.go +++ b/gateway/proxy_fil.go @@ -437,7 +437,7 @@ func (gw *Node) StateWaitMsg(ctx context.Context, msg cid.Cid, confidence uint64 return gw.target.StateWaitMsg(ctx, msg, confidence, limit, allowReplaced) } -func (gw *Node) GetActorEvents(ctx context.Context, filter *types.ActorEventFilter) ([]*types.ActorEvent, error) { +func (gw *Node) GetActorEventsRaw(ctx context.Context, filter *types.ActorEventFilter) ([]*types.ActorEvent, error) { if err := gw.limit(ctx, stateRateLimitTokens); err != nil { return nil, err } @@ -446,10 +446,10 @@ func (gw *Node) GetActorEvents(ctx context.Context, filter *types.ActorEventFilt return nil, err } } - return gw.target.GetActorEvents(ctx, filter) + return gw.target.GetActorEventsRaw(ctx, filter) } -func (gw *Node) SubscribeActorEvents(ctx context.Context, filter *types.ActorEventFilter) (<-chan *types.ActorEvent, error) { +func (gw *Node) SubscribeActorEventsRaw(ctx context.Context, filter *types.ActorEventFilter) (<-chan *types.ActorEvent, error) { if err := gw.limit(ctx, stateRateLimitTokens); err != nil { return nil, err } @@ -458,7 +458,7 @@ func (gw *Node) SubscribeActorEvents(ctx context.Context, filter *types.ActorEve return nil, err } } - return gw.target.SubscribeActorEvents(ctx, filter) + return gw.target.SubscribeActorEventsRaw(ctx, filter) } func (gw *Node) ChainGetEvents(ctx context.Context, eventsRoot cid.Cid) ([]types.Event, error) { diff --git a/itests/direct_data_onboard_test.go b/itests/direct_data_onboard_test.go index 703419ad1..037fa1dbb 100644 --- a/itests/direct_data_onboard_test.go +++ b/itests/direct_data_onboard_test.go @@ -274,7 +274,7 @@ func TestOnboardMixedMarketDDO(t *testing.T) { // check "deal-published" actor event var epochZero abi.ChainEpoch - allEvents, err := miner.FullNode.GetActorEvents(ctx, &types.ActorEventFilter{ + allEvents, err := miner.FullNode.GetActorEventsRaw(ctx, &types.ActorEventFilter{ FromHeight: &epochZero, }) require.NoError(t, err) diff --git a/itests/direct_data_onboard_verified_test.go b/itests/direct_data_onboard_verified_test.go index 0c3de2448..da1cac261 100644 --- a/itests/direct_data_onboard_verified_test.go +++ b/itests/direct_data_onboard_verified_test.go @@ -71,14 +71,14 @@ func TestOnboardRawPieceVerified_WithActorEvents(t *testing.T) { /* --- Setup subscription channels for ActorEvents --- */ // subscribe only to miner's actor events - minerEvtsChan, err := miner.FullNode.SubscribeActorEvents(ctx, &types.ActorEventFilter{ + minerEvtsChan, err := miner.FullNode.SubscribeActorEventsRaw(ctx, &types.ActorEventFilter{ Addresses: []address.Address{miner.ActorAddr}, }) require.NoError(t, err) // subscribe only to sector-activated events sectorActivatedCbor := must.One(ipld.Encode(basicnode.NewString("sector-activated"), dagcbor.Encode)) - sectorActivatedEvtsChan, err := miner.FullNode.SubscribeActorEvents(ctx, &types.ActorEventFilter{ + sectorActivatedEvtsChan, err := miner.FullNode.SubscribeActorEventsRaw(ctx, &types.ActorEventFilter{ Fields: map[string][]types.ActorEventBlock{ "$type": { {Codec: uint64(multicodec.Cbor), Value: sectorActivatedCbor}, @@ -127,7 +127,7 @@ func TestOnboardRawPieceVerified_WithActorEvents(t *testing.T) { require.NoError(t, err) // subscribe to actor events up until the current head - initialEventsChan, err := miner.FullNode.SubscribeActorEvents(ctx, &types.ActorEventFilter{ + initialEventsChan, err := miner.FullNode.SubscribeActorEventsRaw(ctx, &types.ActorEventFilter{ FromHeight: epochPtr(0), ToHeight: epochPtr(int64(head.Height())), }) @@ -284,19 +284,19 @@ func TestOnboardRawPieceVerified_WithActorEvents(t *testing.T) { }, }, claims) - // construct ActorEvents from GetActorEvents API - t.Logf("Inspecting full events list from GetActorEvents") - allEvtsFromGetAPI, err := miner.FullNode.GetActorEvents(ctx, &types.ActorEventFilter{ + // construct ActorEvents from GetActorEventsRaw API + t.Logf("Inspecting full events list from GetActorEventsRaw") + allEvtsFromGetAPI, err := miner.FullNode.GetActorEventsRaw(ctx, &types.ActorEventFilter{ FromHeight: epochPtr(0), }) require.NoError(t, err) - fmt.Println("Events from GetActorEvents:") + fmt.Println("Events from GetActorEventsRaw:") printEvents(t, allEvtsFromGetAPI) - // compare events from messages and receipts with events from GetActorEvents API + // compare events from messages and receipts with events from GetActorEventsRaw API require.Equal(t, eventsFromMessages, allEvtsFromGetAPI) // construct ActorEvents from subscription channel for just the miner actor - t.Logf("Inspecting only miner's events list from SubscribeActorEvents") + t.Logf("Inspecting only miner's events list from SubscribeActorEventsRaw") var subMinerEvts []*types.ActorEvent for evt := range minerEvtsChan { subMinerEvts = append(subMinerEvts, evt) @@ -325,7 +325,7 @@ func TestOnboardRawPieceVerified_WithActorEvents(t *testing.T) { } require.Len(t, sectorActivatedEvts, 2) // sanity check - t.Logf("Inspecting only sector-activated events list from real-time SubscribeActorEvents") + t.Logf("Inspecting only sector-activated events list from real-time SubscribeActorEventsRaw") var subscribedSectorActivatedEvts []*types.ActorEvent for evt := range sectorActivatedEvtsChan { subscribedSectorActivatedEvts = append(subscribedSectorActivatedEvts, evt) @@ -337,8 +337,8 @@ func TestOnboardRawPieceVerified_WithActorEvents(t *testing.T) { require.Equal(t, sectorActivatedEvts, subscribedSectorActivatedEvts) // same thing but use historical event fetching to see the same list - t.Logf("Inspecting only sector-activated events list from historical SubscribeActorEvents") - sectorActivatedEvtsChan, err = miner.FullNode.SubscribeActorEvents(ctx, &types.ActorEventFilter{ + t.Logf("Inspecting only sector-activated events list from historical SubscribeActorEventsRaw") + sectorActivatedEvtsChan, err = miner.FullNode.SubscribeActorEventsRaw(ctx, &types.ActorEventFilter{ Fields: map[string][]types.ActorEventBlock{ "$type": { {Codec: uint64(multicodec.Cbor), Value: sectorActivatedCbor}, @@ -358,7 +358,7 @@ func TestOnboardRawPieceVerified_WithActorEvents(t *testing.T) { require.Equal(t, sectorActivatedEvts, subscribedSectorActivatedEvts) // check that our `ToHeight` filter works as expected - t.Logf("Inspecting only initial list of events SubscribeActorEvents with ToHeight") + t.Logf("Inspecting only initial list of events SubscribeActorEventsRaw with ToHeight") var initialEvents []*types.ActorEvent for evt := range initialEventsChan { initialEvents = append(initialEvents, evt) @@ -367,8 +367,8 @@ func TestOnboardRawPieceVerified_WithActorEvents(t *testing.T) { require.Equal(t, eventsFromMessages[0:6], initialEvents) // construct ActorEvents from subscription channel for all actor events - t.Logf("Inspecting full events list from historical SubscribeActorEvents") - allEvtsChan, err := miner.FullNode.SubscribeActorEvents(ctx, &types.ActorEventFilter{ + t.Logf("Inspecting full events list from historical SubscribeActorEventsRaw") + allEvtsChan, err := miner.FullNode.SubscribeActorEventsRaw(ctx, &types.ActorEventFilter{ FromHeight: epochPtr(0), }) require.NoError(t, err) @@ -383,7 +383,7 @@ func TestOnboardRawPieceVerified_WithActorEvents(t *testing.T) { require.Equal(t, eventsFromMessages, prefillEvts) t.Logf("All done comparing events") - // NOTE: There is a delay in finishing this test because the SubscribeActorEvents + // NOTE: There is a delay in finishing this test because the SubscribeActorEventsRaw // with the ToHeight (initialEventsChan) has to wait at least a full actual epoch before // realising that there's no more events for that filter. itests run with a different block // speed than the ActorEventHandler is aware of. diff --git a/itests/sector_terminate_test.go b/itests/sector_terminate_test.go index 57cffb006..ac586ff8f 100644 --- a/itests/sector_terminate_test.go +++ b/itests/sector_terminate_test.go @@ -173,7 +173,7 @@ loop: // check "sector-terminated" actor event var epochZero abi.ChainEpoch - allEvents, err := miner.FullNode.GetActorEvents(ctx, &types.ActorEventFilter{ + allEvents, err := miner.FullNode.GetActorEventsRaw(ctx, &types.ActorEventFilter{ FromHeight: &epochZero, }) require.NoError(t, err) diff --git a/node/impl/full/actor_events.go b/node/impl/full/actor_events.go index fecd1d2b6..bb192a4cf 100644 --- a/node/impl/full/actor_events.go +++ b/node/impl/full/actor_events.go @@ -18,8 +18,8 @@ import ( ) type ActorEventAPI interface { - GetActorEvents(ctx context.Context, filter *types.ActorEventFilter) ([]*types.ActorEvent, error) - SubscribeActorEvents(ctx context.Context, filter *types.ActorEventFilter) (<-chan *types.ActorEvent, error) + GetActorEventsRaw(ctx context.Context, filter *types.ActorEventFilter) ([]*types.ActorEvent, error) + SubscribeActorEventsRaw(ctx context.Context, filter *types.ActorEventFilter) (<-chan *types.ActorEvent, error) } var ( @@ -89,7 +89,7 @@ func NewActorEventHandlerWithClock( } } -func (a *ActorEventHandler) GetActorEvents(ctx context.Context, evtFilter *types.ActorEventFilter) ([]*types.ActorEvent, error) { +func (a *ActorEventHandler) GetActorEventsRaw(ctx context.Context, evtFilter *types.ActorEventFilter) ([]*types.ActorEvent, error) { if a.eventFilterManager == nil { return nil, api.ErrNotSupported } @@ -200,7 +200,7 @@ func parseHeightRange(heaviest abi.ChainEpoch, fromHeight, toHeight *abi.ChainEp return minHeight, maxHeight, nil } -func (a *ActorEventHandler) SubscribeActorEvents(ctx context.Context, evtFilter *types.ActorEventFilter) (<-chan *types.ActorEvent, error) { +func (a *ActorEventHandler) SubscribeActorEventsRaw(ctx context.Context, evtFilter *types.ActorEventFilter) (<-chan *types.ActorEvent, error) { if a.eventFilterManager == nil { return nil, api.ErrNotSupported } diff --git a/node/impl/full/actor_events_test.go b/node/impl/full/actor_events_test.go index ab446e57b..b4c4e103c 100644 --- a/node/impl/full/actor_events_test.go +++ b/node/impl/full/actor_events_test.go @@ -131,7 +131,7 @@ func TestParseHeightRange(t *testing.T) { } } -func TestGetActorEvents(t *testing.T) { +func TestGetActorEventsRaw(t *testing.T) { ctx := context.Background() req := require.New(t) @@ -231,7 +231,7 @@ func TestGetActorEvents(t *testing.T) { handler := NewActorEventHandler(chain, efm, 50*time.Millisecond, maxFilterHeightRange) - gotEvents, err := handler.GetActorEvents(ctx, tc.filter) + gotEvents, err := handler.GetActorEventsRaw(ctx, tc.filter) if tc.expectErr != "" { req.Error(err) req.Contains(err.Error(), tc.expectErr) @@ -245,7 +245,7 @@ func TestGetActorEvents(t *testing.T) { } } -func TestSubscribeActorEvents(t *testing.T) { +func TestSubscribeActorEventsRaw(t *testing.T) { const ( seed = 984651320 maxFilterHeightRange = 100 @@ -300,7 +300,7 @@ func TestSubscribeActorEvents(t *testing.T) { if tc.endEpoch >= 0 { aef.ToHeight = epochPtr(tc.endEpoch) } - eventChan, err := handler.SubscribeActorEvents(ctx, aef) + eventChan, err := handler.SubscribeActorEventsRaw(ctx, aef) req.NoError(err) // assume we can cleanly pick up all historical events in one go @@ -411,8 +411,8 @@ func TestSubscribeActorEvents(t *testing.T) { } } -func TestSubscribeActorEvents_OnlyHistorical(t *testing.T) { - // Similar to TestSubscribeActorEvents but we set an explicit end that caps out at the current height +func TestSubscribeActorEventsRaw_OnlyHistorical(t *testing.T) { + // Similar to TestSubscribeActorEventsRaw but we set an explicit end that caps out at the current height const ( seed = 984651320 maxFilterHeightRange = 100 @@ -458,7 +458,7 @@ func TestSubscribeActorEvents_OnlyHistorical(t *testing.T) { handler := NewActorEventHandlerWithClock(mockChain, mockFilterManager, blockDelay, maxFilterHeightRange, mockClock) aef := &types.ActorEventFilter{FromHeight: epochPtr(0), ToHeight: epochPtr(currentHeight)} - eventChan, err := handler.SubscribeActorEvents(ctx, aef) + eventChan, err := handler.SubscribeActorEventsRaw(ctx, aef) req.NoError(err) var gotEvents []*types.ActorEvent diff --git a/node/impl/full/dummy.go b/node/impl/full/dummy.go index abe52dec6..1c191afba 100644 --- a/node/impl/full/dummy.go +++ b/node/impl/full/dummy.go @@ -194,11 +194,11 @@ var ErrActorEventModuleDisabled = errors.New("module disabled, enable with Event type ActorEventDummy struct{} -func (a *ActorEventDummy) GetActorEvents(ctx context.Context, filter *types.ActorEventFilter) ([]*types.ActorEvent, error) { +func (a *ActorEventDummy) GetActorEventsRaw(ctx context.Context, filter *types.ActorEventFilter) ([]*types.ActorEvent, error) { return nil, ErrActorEventModuleDisabled } -func (a *ActorEventDummy) SubscribeActorEvents(ctx context.Context, filter *types.ActorEventFilter) (<-chan *types.ActorEvent, error) { +func (a *ActorEventDummy) SubscribeActorEventsRaw(ctx context.Context, filter *types.ActorEventFilter) (<-chan *types.ActorEvent, error) { return nil, ErrActorEventModuleDisabled } From 25711c5ce4e18b7dac53ab82b42f5a7689cd8a83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sun, 17 Mar 2024 21:33:23 +0100 Subject: [PATCH 275/394] curio web: Task summaries in Cluster machines category --- curiosrc/web/hapi/simpleinfo.go | 61 ++++++++++++++++++- curiosrc/web/hapi/web/cluster_machines.gohtml | 4 +- curiosrc/web/static/index.html | 1 - .../sql/20240317-tasksummary-index.sql | 3 + 4 files changed, 66 insertions(+), 3 deletions(-) create mode 100644 lib/harmony/harmonydb/sql/20240317-tasksummary-index.sql diff --git a/curiosrc/web/hapi/simpleinfo.go b/curiosrc/web/hapi/simpleinfo.go index e639ef427..319db88e0 100644 --- a/curiosrc/web/hapi/simpleinfo.go +++ b/curiosrc/web/hapi/simpleinfo.go @@ -5,6 +5,7 @@ import ( "html/template" "net/http" "os" + "sort" "sync" "time" @@ -107,10 +108,18 @@ func (a *app) executeTemplate(w http.ResponseWriter, name string, data interface } } +type machineRecentTask struct { + TaskName string + Success int64 + Fail int64 +} + type machineSummary struct { Address string ID int64 SinceContact string + + RecentTasks []*machineRecentTask } type taskSummary struct { @@ -133,7 +142,47 @@ type taskHistorySummary struct { } func (a *app) clusterMachineSummary(ctx context.Context) ([]machineSummary, error) { - rows, err := a.db.Query(ctx, "SELECT id, host_and_port, last_contact FROM harmony_machines") + // First get task summary for tasks completed in the last 24 hours + // NOTE: This query uses harmony_task_history_work_index, task history may get big + tsrows, err := a.db.Query(ctx, `SELECT hist.completed_by_host_and_port, hist.name, hist.result, count(1) FROM harmony_task_history hist + WHERE hist.work_end > now() - INTERVAL '1 day' + GROUP BY hist.completed_by_host_and_port, hist.name, hist.result + ORDER BY completed_by_host_and_port ASC`) + if err != nil { + return nil, err + } + defer tsrows.Close() + + // Map of machine -> task -> recent task + taskSummaries := map[string]map[string]*machineRecentTask{} + + for tsrows.Next() { + var taskName string + var result bool + var count int64 + var machine string + + if err := tsrows.Scan(&machine, &taskName, &result, &count); err != nil { + return nil, err + } + + if _, ok := taskSummaries[machine]; !ok { + taskSummaries[machine] = map[string]*machineRecentTask{} + } + + if _, ok := taskSummaries[machine][taskName]; !ok { + taskSummaries[machine][taskName] = &machineRecentTask{TaskName: taskName} + } + + if result { + taskSummaries[machine][taskName].Success = count + } else { + taskSummaries[machine][taskName].Fail = count + } + } + + // Then machine summary + rows, err := a.db.Query(ctx, "SELECT id, host_and_port, last_contact FROM harmony_machines order by host_and_port asc") if err != nil { return nil, err // Handle error } @@ -150,6 +199,16 @@ func (a *app) clusterMachineSummary(ctx context.Context) ([]machineSummary, erro m.SinceContact = time.Since(lastContact).Round(time.Second).String() + // Add recent tasks + if ts, ok := taskSummaries[m.Address]; ok { + for _, t := range ts { + m.RecentTasks = append(m.RecentTasks, t) + } + sort.Slice(m.RecentTasks, func(i, j int) bool { + return m.RecentTasks[i].TaskName < m.RecentTasks[j].TaskName + }) + } + summaries = append(summaries, m) } return summaries, nil diff --git a/curiosrc/web/hapi/web/cluster_machines.gohtml b/curiosrc/web/hapi/web/cluster_machines.gohtml index f94f53bf8..b253a0561 100644 --- a/curiosrc/web/hapi/web/cluster_machines.gohtml +++ b/curiosrc/web/hapi/web/cluster_machines.gohtml @@ -3,8 +3,10 @@ {{.Address}} {{.ID}} - todo {{.SinceContact}} + {{range .RecentTasks}} + {{.TaskName}}:{{.Success}}{{if ne 0 .Fail}}({{.Fail}}){{end}} + {{end}} {{end}} {{end}} diff --git a/curiosrc/web/static/index.html b/curiosrc/web/static/index.html index 4f9c21b50..f2f1c087c 100644 --- a/curiosrc/web/static/index.html +++ b/curiosrc/web/static/index.html @@ -59,7 +59,6 @@ Host ID - Config Layers Last Contact diff --git a/lib/harmony/harmonydb/sql/20240317-tasksummary-index.sql b/lib/harmony/harmonydb/sql/20240317-tasksummary-index.sql new file mode 100644 index 000000000..a5f907778 --- /dev/null +++ b/lib/harmony/harmonydb/sql/20240317-tasksummary-index.sql @@ -0,0 +1,3 @@ +/* Used for webui clusterMachineSummary */ +CREATE INDEX harmony_task_history_work_index + ON harmony_task_history (completed_by_host_and_port ASC, name ASC, result ASC, work_end DESC); From 8e4ce107083e1526f57bfbe2776ed561fa67285d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sun, 17 Mar 2024 22:36:08 +0100 Subject: [PATCH 276/394] curio web: Show basic balances in Actor Summary --- curiosrc/web/hapi/simpleinfo.go | 2 ++ curiosrc/web/hapi/watch_actor.go | 34 ++++++++++++++++++++++ curiosrc/web/hapi/web/actor_summary.gohtml | 3 ++ curiosrc/web/static/index.html | 3 ++ 4 files changed, 42 insertions(+) diff --git a/curiosrc/web/hapi/simpleinfo.go b/curiosrc/web/hapi/simpleinfo.go index 319db88e0..e78c1dffb 100644 --- a/curiosrc/web/hapi/simpleinfo.go +++ b/curiosrc/web/hapi/simpleinfo.go @@ -33,6 +33,8 @@ type actorInfo struct { QualityAdjustedPower string RawBytePower string + ActorBalance, ActorAvailable, WorkerBalance string + Deadlines []actorDeadline } diff --git a/curiosrc/web/hapi/watch_actor.go b/curiosrc/web/hapi/watch_actor.go index e785a415b..f7fb73d3f 100644 --- a/curiosrc/web/hapi/watch_actor.go +++ b/curiosrc/web/hapi/watch_actor.go @@ -10,6 +10,9 @@ import ( "github.com/filecoin-project/go-address" + "github.com/filecoin-project/lotus/blockstore" + "github.com/filecoin-project/lotus/chain/actors/builtin/miner" + "github.com/filecoin-project/lotus/chain/store" "github.com/filecoin-project/lotus/chain/types" ) @@ -39,6 +42,8 @@ func (a *app) updateActor(ctx context.Context) error { api := a.workingApi a.rpcInfoLk.Unlock() + stor := store.ActorStore(ctx, blockstore.NewReadCachedBlockstore(blockstore.NewAPIBlockstore(a.workingApi), ChainBlockCache)) + if api == nil { log.Warnw("no working api yet") return nil @@ -76,6 +81,16 @@ func (a *app) updateActor(ctx context.Context) error { return xerrors.Errorf("getting deadlines: %w", err) } + mact, err := api.StateGetActor(ctx, addr, types.EmptyTSK) + if err != nil { + return xerrors.Errorf("getting actor: %w", err) + } + + mas, err := miner.Load(stor, mact) + if err != nil { + return err + } + outDls := []actorDeadline{} for dlidx := range dls { @@ -127,12 +142,31 @@ func (a *app) updateActor(ctx context.Context) error { outDls[pd.Index].Current = true + avail, err := mas.AvailableBalance(mact.Balance) + if err != nil { + return xerrors.Errorf("getting available balance: %w", err) + } + + mi, err := mas.Info() + if err != nil { + return xerrors.Errorf("getting miner info: %w", err) + } + + wbal, err := api.WalletBalance(ctx, mi.Worker) + if err != nil { + return xerrors.Errorf("getting worker balance: %w", err) + } + actorInfos = append(actorInfos, actorInfo{ Address: addr.String(), CLayers: cnames, QualityAdjustedPower: types.DeciStr(p.MinerPower.QualityAdjPower), RawBytePower: types.DeciStr(p.MinerPower.RawBytePower), Deadlines: outDls, + + ActorBalance: types.FIL(mact.Balance).Short(), + ActorAvailable: types.FIL(avail).Short(), + WorkerBalance: types.FIL(wbal).Short(), }) } diff --git a/curiosrc/web/hapi/web/actor_summary.gohtml b/curiosrc/web/hapi/web/actor_summary.gohtml index 31992fb23..92f1139ce 100644 --- a/curiosrc/web/hapi/web/actor_summary.gohtml +++ b/curiosrc/web/hapi/web/actor_summary.gohtml @@ -15,6 +15,9 @@ {{end}}
+ {{.ActorBalance}} + {{.ActorAvailable}} + {{.WorkerBalance}} {{end}} {{end}} \ No newline at end of file diff --git a/curiosrc/web/static/index.html b/curiosrc/web/static/index.html index f2f1c087c..eaabe0173 100644 --- a/curiosrc/web/static/index.html +++ b/curiosrc/web/static/index.html @@ -97,6 +97,9 @@ Config Layers QaP Deadlines + Balance + Available + Worker From 49712a6e880e8b6d750c21bb07952562b434e0ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sun, 17 Mar 2024 23:15:53 +0100 Subject: [PATCH 277/394] curio web: Show SP wins in actor summary --- curiosrc/web/hapi/simpleinfo.go | 2 + curiosrc/web/hapi/watch_actor.go | 60 +++++++++++++++++++ curiosrc/web/hapi/web/actor_summary.gohtml | 7 +++ curiosrc/web/static/index.html | 2 + ...dex.sql => 20240317-web-summary-index.sql} | 4 ++ 5 files changed, 75 insertions(+) rename lib/harmony/harmonydb/sql/{20240317-tasksummary-index.sql => 20240317-web-summary-index.sql} (52%) diff --git a/curiosrc/web/hapi/simpleinfo.go b/curiosrc/web/hapi/simpleinfo.go index e78c1dffb..fd6635059 100644 --- a/curiosrc/web/hapi/simpleinfo.go +++ b/curiosrc/web/hapi/simpleinfo.go @@ -35,6 +35,8 @@ type actorInfo struct { ActorBalance, ActorAvailable, WorkerBalance string + Win1, Win7, Win30 int64 + Deadlines []actorDeadline } diff --git a/curiosrc/web/hapi/watch_actor.go b/curiosrc/web/hapi/watch_actor.go index f7fb73d3f..a65608f14 100644 --- a/curiosrc/web/hapi/watch_actor.go +++ b/curiosrc/web/hapi/watch_actor.go @@ -70,6 +70,11 @@ func (a *app) updateActor(ctx context.Context) error { return err } + wins, err := a.spWins(ctx) + if err != nil { + return xerrors.Errorf("getting sp wins: %w", err) + } + for addr, cnames := range confNameToAddr { p, err := api.StateMinerPower(ctx, addr, types.EmptyTSK) if err != nil { @@ -167,6 +172,10 @@ func (a *app) updateActor(ctx context.Context) error { ActorBalance: types.FIL(mact.Balance).Short(), ActorAvailable: types.FIL(avail).Short(), WorkerBalance: types.FIL(wbal).Short(), + + Win1: wins[addr].Win1, // note: zero values are fine here + Win7: wins[addr].Win7, + Win30: wins[addr].Win30, }) } @@ -199,6 +208,57 @@ func (a *app) loadConfigs(ctx context.Context) (map[string]string, error) { return configs, nil } +type wins struct { + SpID int64 `db:"sp_id"` + Win1 int64 `db:"win1"` + Win7 int64 `db:"win7"` + Win30 int64 `db:"win30"` +} + +func (a *app) spWins(ctx context.Context) (map[address.Address]wins, error) { + var w []wins + + // note: this query uses mining_tasks_won_sp_id_base_compute_time_index + err := a.db.Select(ctx, &w, `WITH wins AS ( + SELECT + sp_id, + base_compute_time, + won + FROM + mining_tasks + WHERE + won = true + AND base_compute_time > NOW() - INTERVAL '30 days' + ) + + SELECT + sp_id, + COUNT(*) FILTER (WHERE base_compute_time > NOW() - INTERVAL '1 day') AS "win1", + COUNT(*) FILTER (WHERE base_compute_time > NOW() - INTERVAL '7 days') AS "win7", + COUNT(*) FILTER (WHERE base_compute_time > NOW() - INTERVAL '30 days') AS "win30" + FROM + wins + GROUP BY + sp_id + ORDER BY + sp_id`) + if err != nil { + return nil, xerrors.Errorf("query win counts: %w", err) + } + + wm := make(map[address.Address]wins) + for _, wi := range w { + ma, err := address.NewIDAddress(uint64(wi.SpID)) + if err != nil { + return nil, xerrors.Errorf("parsing miner address: %w", err) + } + + wm[ma] = wi + } + + return wm, nil +} + func forEachConfig[T any](a *app, cb func(name string, v T) error) error { confs, err := a.loadConfigs(context.Background()) if err != nil { diff --git a/curiosrc/web/hapi/web/actor_summary.gohtml b/curiosrc/web/hapi/web/actor_summary.gohtml index 92f1139ce..bf577d802 100644 --- a/curiosrc/web/hapi/web/actor_summary.gohtml +++ b/curiosrc/web/hapi/web/actor_summary.gohtml @@ -18,6 +18,13 @@ {{.ActorBalance}} {{.ActorAvailable}} {{.WorkerBalance}} + + + + + +
1day:  {{.Win1}}
7day:  {{.Win7}}
30day: {{.Win30}}
+ {{end}} {{end}} \ No newline at end of file diff --git a/curiosrc/web/static/index.html b/curiosrc/web/static/index.html index eaabe0173..674b4a88e 100644 --- a/curiosrc/web/static/index.html +++ b/curiosrc/web/static/index.html @@ -60,6 +60,7 @@ Host ID Last Contact + Tasks (24h) @@ -100,6 +101,7 @@ Balance Available Worker + Wins diff --git a/lib/harmony/harmonydb/sql/20240317-tasksummary-index.sql b/lib/harmony/harmonydb/sql/20240317-web-summary-index.sql similarity index 52% rename from lib/harmony/harmonydb/sql/20240317-tasksummary-index.sql rename to lib/harmony/harmonydb/sql/20240317-web-summary-index.sql index a5f907778..b34707d52 100644 --- a/lib/harmony/harmonydb/sql/20240317-tasksummary-index.sql +++ b/lib/harmony/harmonydb/sql/20240317-web-summary-index.sql @@ -1,3 +1,7 @@ /* Used for webui clusterMachineSummary */ CREATE INDEX harmony_task_history_work_index ON harmony_task_history (completed_by_host_and_port ASC, name ASC, result ASC, work_end DESC); + +/* Used for webui actorSummary sp wins */ +create index mining_tasks_won_sp_id_base_compute_time_index + on mining_tasks (won asc, sp_id asc, base_compute_time desc); From 51b59e3b813fb169e9fd42ec293819baababcdda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 19 Mar 2024 09:04:32 +0100 Subject: [PATCH 278/394] curio web: fix capitalization in 20240317-web-summary-index.sql --- lib/harmony/harmonydb/sql/20240317-web-summary-index.sql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/harmony/harmonydb/sql/20240317-web-summary-index.sql b/lib/harmony/harmonydb/sql/20240317-web-summary-index.sql index b34707d52..28902448d 100644 --- a/lib/harmony/harmonydb/sql/20240317-web-summary-index.sql +++ b/lib/harmony/harmonydb/sql/20240317-web-summary-index.sql @@ -3,5 +3,5 @@ CREATE INDEX harmony_task_history_work_index ON harmony_task_history (completed_by_host_and_port ASC, name ASC, result ASC, work_end DESC); /* Used for webui actorSummary sp wins */ -create index mining_tasks_won_sp_id_base_compute_time_index - on mining_tasks (won asc, sp_id asc, base_compute_time desc); +CREATE INDEX mining_tasks_won_sp_id_base_compute_time_index + ON mining_tasks (won ASC, sp_id ASC, base_compute_time DESC); From 2e75f3b796dd1396baa37d73ea99dc3407b4da8d Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Tue, 19 Mar 2024 20:10:37 +1100 Subject: [PATCH 279/394] fix(events): don't log.Warn for EVM events with non-Raw codec (#11742) Codec != Raw likely means built-in actor events, which we can safely skip, so avoid filling up logs with warnings for every built-in actor event that comes past this point. Fixes: https://github.com/filecoin-project/lotus/issues/11718 --- node/impl/full/eth_events.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/node/impl/full/eth_events.go b/node/impl/full/eth_events.go index 54dd164ac..81ecef64b 100644 --- a/node/impl/full/eth_events.go +++ b/node/impl/full/eth_events.go @@ -39,9 +39,9 @@ func ethLogFromEvent(entries []types.EventEntry) (data []byte, topics []ethtypes // Topics must be non-nil, even if empty. So we might as well pre-allocate for 4 (the max). topics = make([]ethtypes.EthHash, 0, 4) for _, entry := range entries { - // Drop events with non-raw topics to avoid mistakes. + // Drop events with non-raw topics. Built-in actors emit CBOR, and anything else would be + // invalid anyway. if entry.Codec != cid.Raw { - log.Warnw("did not expect an event entry with a non-raw codec", "codec", entry.Codec, "key", entry.Key) return nil, nil, false } // Check if the key is t1..t4 From 02a8848b54e7cba9f7be0c5e2c93f3b4cb81a32a Mon Sep 17 00:00:00 2001 From: LexLuthr <88259624+LexLuthr@users.noreply.github.com> Date: Wed, 20 Mar 2024 13:34:52 +0400 Subject: [PATCH 280/394] feat: CLI: add claim-extend cli (#11711) * add claim-extend cli * fix arg usage * add missing question * fix client addr, datacap prompt * replace waitGrp with errGrp * use promptUI * replace fmt.ErrorF with xerror * apply var name suggestion * GST rc3, update types * add itest * make gen * add changelog --- CHANGELOG.md | 1 + cli/filplus.go | 435 ++++++++++++++++++++ documentation/en/cli-lotus.md | 19 + itests/direct_data_onboard_verified_test.go | 223 ++++++++-- 4 files changed, 640 insertions(+), 38 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4485fe8dc..89aad3563 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ # UNRELEASED ## New features +- feat: CLI: add claim-extend cli (#11711) ([filecoin-project/lotus#11711](https://github.com/filecoin-project/lotus/pull/11711)) ## Improvements diff --git a/cli/filplus.go b/cli/filplus.go index a4982cecb..fbb922a24 100644 --- a/cli/filplus.go +++ b/cli/filplus.go @@ -4,23 +4,30 @@ import ( "bytes" "context" "encoding/hex" + "errors" "fmt" "os" "strconv" "strings" cbor "github.com/ipfs/go-ipld-cbor" + "github.com/manifoldco/promptui" "github.com/urfave/cli/v2" + "golang.org/x/sync/errgroup" "golang.org/x/xerrors" "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/abi" actorstypes "github.com/filecoin-project/go-state-types/actors" "github.com/filecoin-project/go-state-types/big" + "github.com/filecoin-project/go-state-types/builtin" + verifregtypes13 "github.com/filecoin-project/go-state-types/builtin/v13/verifreg" verifregtypes8 "github.com/filecoin-project/go-state-types/builtin/v8/verifreg" + datacap2 "github.com/filecoin-project/go-state-types/builtin/v9/datacap" verifregtypes9 "github.com/filecoin-project/go-state-types/builtin/v9/verifreg" "github.com/filecoin-project/go-state-types/network" + "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/api/v0api" "github.com/filecoin-project/lotus/blockstore" "github.com/filecoin-project/lotus/build" @@ -47,6 +54,7 @@ var filplusCmd = &cli.Command{ filplusListClaimsCmd, filplusRemoveExpiredAllocationsCmd, filplusRemoveExpiredClaimsCmd, + filplusExtendClaimCmd, }, } @@ -923,3 +931,430 @@ var filplusSignRemoveDataCapProposal = &cli.Command{ return nil }, } + +var filplusExtendClaimCmd = &cli.Command{ + Name: "extend-claim", + Usage: "extend claim expiration (TermMax)", + Flags: []cli.Flag{ + &cli.Int64Flag{ + Name: "term-max", + Usage: "The maximum period for which a provider can earn quality-adjusted power for the piece (epochs). Default is 5 years.", + Aliases: []string{"tmax"}, + Value: verifregtypes13.MaximumVerifiedAllocationTerm, + }, + &cli.StringFlag{ + Name: "client", + Usage: "the client address that will used to send the message", + Required: true, + }, + &cli.BoolFlag{ + Name: "all", + Usage: "automatically extend TermMax of all claims for specified miner[s] to --term-max (default: 5 years from claim start epoch)", + }, + &cli.StringSliceFlag{ + Name: "miner", + Usage: "storage provider address[es]", + Aliases: []string{"m", "provider", "p"}, + }, + &cli.BoolFlag{ + Name: "assume-yes", + Usage: "automatic yes to prompts; assume 'yes' as answer to all prompts and run non-interactively", + Aliases: []string{"y", "yes"}, + }, + &cli.IntFlag{ + Name: "confidence", + Usage: "number of block confirmations to wait for", + Value: int(build.MessageConfidence), + }, + }, + ArgsUsage: " ... or ...", + Action: func(cctx *cli.Context) error { + + miners := cctx.StringSlice("miner") + all := cctx.Bool("all") + client := cctx.String("client") + tmax := cctx.Int64("term-max") + + // No miner IDs and no arguments + if len(miners) == 0 && cctx.Args().Len() == 0 { + return xerrors.Errorf("must specify at least one miner ID or argument[s]") + } + + // Single Miner with no claimID and no --all flag + if len(miners) == 1 && cctx.Args().Len() == 0 && !all { + return xerrors.Errorf("must specify either --all flag or claim IDs to extend in argument") + } + + // Multiple Miner with claimIDs + if len(miners) > 1 && cctx.Args().Len() > 0 { + return xerrors.Errorf("either specify multiple miner IDs or multiple arguments") + } + + // Multiple Miner with no claimID and no --all flag + if len(miners) > 1 && cctx.Args().Len() == 0 && !all { + return xerrors.Errorf("must specify --all flag with multiple miner IDs") + } + + // Tmax can't be more than policy max + if tmax > verifregtypes13.MaximumVerifiedAllocationTerm { + return xerrors.Errorf("specified term-max %d is larger than %d maximum allowed by verified regirty actor policy", tmax, verifregtypes13.MaximumVerifiedAllocationTerm) + } + + api, closer, err := GetFullNodeAPIV1(cctx) + if err != nil { + return xerrors.Errorf("failed to get full node api: %s", err) + } + defer closer() + ctx := ReqContext(cctx) + + clientAddr, err := address.NewFromString(client) + if err != nil { + return err + } + + claimMap := make(map[verifregtypes13.ClaimId]ProvInfo) + + // If no miners and arguments are present + if len(miners) == 0 && cctx.Args().Len() > 0 { + for _, arg := range cctx.Args().Slice() { + detail := strings.Split(arg, "=") + if len(detail) > 2 { + return xerrors.Errorf("incorrect argument format: %s", detail) + } + + n, err := strconv.ParseInt(detail[1], 10, 64) + if err != nil { + return xerrors.Errorf("failed to parse the claim ID for %s for argument %s: %s", detail[0], detail, err) + } + + maddr, err := address.NewFromString(detail[0]) + if err != nil { + return err + } + + // Verify that minerID exists + _, err = api.StateMinerInfo(ctx, maddr, types.EmptyTSK) + if err != nil { + return err + } + + mid, err := address.IDFromAddress(maddr) + if err != nil { + return err + } + + pi := ProvInfo{ + Addr: maddr, + ID: abi.ActorID(mid), + } + + claimMap[verifregtypes13.ClaimId(n)] = pi + } + } + + // If 1 miner ID and multiple arguments + if len(miners) == 1 && cctx.Args().Len() > 0 && !all { + for _, arg := range cctx.Args().Slice() { + detail := strings.Split(arg, "=") + if len(detail) > 1 { + return xerrors.Errorf("incorrect argument format %s. Must provide only claim IDs with single miner ID", detail) + } + + n, err := strconv.ParseInt(detail[0], 10, 64) + if err != nil { + return xerrors.Errorf("failed to parse the claim ID for %s for argument %s: %s", detail[0], detail, err) + } + + claimMap[verifregtypes13.ClaimId(n)] = ProvInfo{} + } + } + + msgs, err := CreateExtendClaimMsg(ctx, api, claimMap, miners, clientAddr, abi.ChainEpoch(tmax), all, cctx.Bool("assume-yes")) + if err != nil { + return err + } + + // If not msgs are found then no claims can be extended + if msgs == nil { + fmt.Println("No eligible claims to extend") + return nil + } + + // MpoolBatchPushMessage method will take care of gas estimation and funds check + smsgs, err := api.MpoolBatchPushMessage(ctx, msgs, nil) + if err != nil { + return err + } + + // wait for msgs to get mined into a block + eg := errgroup.Group{} + eg.SetLimit(10) + for _, msg := range smsgs { + msg := msg + eg.Go(func() error { + wait, err := api.StateWaitMsg(ctx, msg.Cid(), uint64(cctx.Int("confidence")), 2000, true) + if err != nil { + return xerrors.Errorf("timeout waiting for message to land on chain %s", wait.Message) + + } + + if wait.Receipt.ExitCode.IsError() { + return xerrors.Errorf("failed to execute message %s: %s", wait.Message, wait.Receipt.ExitCode) + } + return nil + }) + } + return eg.Wait() + }, +} + +type ProvInfo struct { + Addr address.Address + ID abi.ActorID +} + +// CreateExtendClaimMsg creates extend message[s] based on the following conditions +// 1. Extend all claims for a miner ID +// 2. Extend all claims for multiple miner IDs +// 3. Extend specified claims for a miner ID +// 4. Extend specific claims for specific miner ID +// 5. Extend all claims for a miner ID with different client address (2 messages) +// 6. Extend all claims for multiple miner IDs with different client address (2 messages) +// 7. Extend specified claims for a miner ID with different client address (2 messages) +// 8. Extend specific claims for specific miner ID with different client address (2 messages) +func CreateExtendClaimMsg(ctx context.Context, api api.FullNode, pcm map[verifregtypes13.ClaimId]ProvInfo, miners []string, wallet address.Address, tmax abi.ChainEpoch, all, assumeYes bool) ([]*types.Message, error) { + + ac, err := api.StateLookupID(ctx, wallet, types.EmptyTSK) + if err != nil { + return nil, err + } + w, err := address.IDFromAddress(ac) + if err != nil { + return nil, xerrors.Errorf("converting wallet address to ID: %w", err) + } + + wid := abi.ActorID(w) + + head, err := api.ChainHead(ctx) + if err != nil { + return nil, err + } + + var terms []verifregtypes13.ClaimTerm + var newClaims []verifregtypes13.ClaimExtensionRequest + rDataCap := big.NewInt(0) + + // If --all is set + if all { + for _, id := range miners { + maddr, err := address.NewFromString(id) + if err != nil { + return nil, xerrors.Errorf("parsing miner %s: %s", id, err) + } + mid, err := address.IDFromAddress(maddr) + if err != nil { + return nil, xerrors.Errorf("converting miner address to miner ID: %s", err) + } + claims, err := api.StateGetClaims(ctx, maddr, types.EmptyTSK) + if err != nil { + return nil, xerrors.Errorf("getting claims for miner %s: %s", maddr, err) + } + for claimID, claim := range claims { + claimID := claimID + claim := claim + if claim.TermMax < tmax && claim.TermStart+claim.TermMax > head.Height() { + // If client is not same - needs to burn datacap + if claim.Client != wid { + newClaims = append(newClaims, verifregtypes13.ClaimExtensionRequest{ + Claim: verifregtypes13.ClaimId(claimID), + Provider: abi.ActorID(mid), + TermMax: tmax, + }) + rDataCap.Add(big.NewInt(int64(claim.Size)).Int, rDataCap.Int) + continue + } + terms = append(terms, verifregtypes13.ClaimTerm{ + ClaimId: verifregtypes13.ClaimId(claimID), + TermMax: tmax, + Provider: abi.ActorID(mid), + }) + } + } + } + } + + // Single miner and specific claims + if len(miners) == 1 && len(pcm) > 0 { + maddr, err := address.NewFromString(miners[0]) + if err != nil { + return nil, xerrors.Errorf("parsing miner %s: %s", miners[0], err) + } + mid, err := address.IDFromAddress(maddr) + if err != nil { + return nil, xerrors.Errorf("converting miner address to miner ID: %s", err) + } + claims, err := api.StateGetClaims(ctx, maddr, types.EmptyTSK) + if err != nil { + return nil, xerrors.Errorf("getting claims for miner %s: %s", maddr, err) + } + + for claimID := range pcm { + claimID := claimID + claim, ok := claims[verifregtypes9.ClaimId(claimID)] + if !ok { + return nil, xerrors.Errorf("claim %d not found for provider %s", claimID, miners[0]) + } + if claim.TermMax < tmax && claim.TermStart+claim.TermMax > head.Height() { + // If client is not same - needs to burn datacap + if claim.Client != wid { + newClaims = append(newClaims, verifregtypes13.ClaimExtensionRequest{ + Claim: claimID, + Provider: abi.ActorID(mid), + TermMax: tmax, + }) + rDataCap.Add(big.NewInt(int64(claim.Size)).Int, rDataCap.Int) + continue + } + terms = append(terms, verifregtypes13.ClaimTerm{ + ClaimId: claimID, + TermMax: tmax, + Provider: abi.ActorID(mid), + }) + } + } + } + + if len(miners) == 0 && len(pcm) > 0 { + for claimID, prov := range pcm { + prov := prov + claimID := claimID + claim, err := api.StateGetClaim(ctx, prov.Addr, verifregtypes9.ClaimId(claimID), types.EmptyTSK) + if err != nil { + return nil, xerrors.Errorf("could not load the claim %d: %s", claimID, err) + } + if claim == nil { + return nil, xerrors.Errorf("claim %d not found in the actor state", claimID) + } + if claim.TermMax < tmax && claim.TermStart+claim.TermMax > head.Height() { + // If client is not same - needs to burn datacap + if claim.Client != wid { + newClaims = append(newClaims, verifregtypes13.ClaimExtensionRequest{ + Claim: claimID, + Provider: prov.ID, + TermMax: tmax, + }) + rDataCap.Add(big.NewInt(int64(claim.Size)).Int, rDataCap.Int) + continue + } + terms = append(terms, verifregtypes13.ClaimTerm{ + ClaimId: claimID, + TermMax: tmax, + Provider: prov.ID, + }) + } + } + } + + var msgs []*types.Message + + if len(terms) > 0 { + params, err := actors.SerializeParams(&verifregtypes13.ExtendClaimTermsParams{ + Terms: terms, + }) + + if err != nil { + return nil, xerrors.Errorf("failed to searialise the parameters: %s", err) + } + + oclaimMsg := &types.Message{ + To: verifreg.Address, + From: wallet, + Method: verifreg.Methods.ExtendClaimTerms, + Params: params, + } + + msgs = append(msgs, oclaimMsg) + } + + if len(newClaims) > 0 { + // Get datacap balance + aDataCap, err := api.StateVerifiedClientStatus(ctx, wallet, types.EmptyTSK) + if err != nil { + return nil, err + } + + if aDataCap == nil { + return nil, xerrors.Errorf("wallet %s does not have any datacap", wallet) + } + + // Check that we have enough data cap to make the allocation + if rDataCap.GreaterThan(big.NewInt(aDataCap.Int64())) { + return nil, xerrors.Errorf("requested datacap %s is greater then the available datacap %s", rDataCap, aDataCap) + } + + ncparams, err := actors.SerializeParams(&verifregtypes13.AllocationRequests{ + Extensions: newClaims, + }) + + if err != nil { + return nil, xerrors.Errorf("failed to searialise the parameters: %s", err) + } + + transferParams, err := actors.SerializeParams(&datacap2.TransferParams{ + To: builtin.VerifiedRegistryActorAddr, + Amount: big.Mul(rDataCap, builtin.TokenPrecision), + OperatorData: ncparams, + }) + + if err != nil { + return nil, xerrors.Errorf("failed to serialize transfer parameters: %s", err) + } + + nclaimMsg := &types.Message{ + To: builtin.DatacapActorAddr, + From: wallet, + Method: datacap.Methods.TransferExported, + Params: transferParams, + Value: big.Zero(), + } + + if !assumeYes { + out := fmt.Sprintf("Some of the specified allocation have a different client address and will require %d Datacap to extend. Proceed? Yes [Y/y] / No [N/n], Ctrl+C (^C) to exit", rDataCap.Int) + validate := func(input string) error { + if strings.EqualFold(input, "y") || strings.EqualFold(input, "yes") { + return nil + } + if strings.EqualFold(input, "n") || strings.EqualFold(input, "no") { + return nil + } + return errors.New("incorrect input") + } + + templates := &promptui.PromptTemplates{ + Prompt: "{{ . }} ", + Valid: "{{ . | green }} ", + Invalid: "{{ . | red }} ", + Success: "{{ . | cyan | bold }} ", + } + + prompt := promptui.Prompt{ + Label: out, + Templates: templates, + Validate: validate, + } + + input, err := prompt.Run() + if err != nil { + return nil, err + } + if strings.Contains(strings.ToLower(input), "n") { + fmt.Println("Dropping the extension for claims that require Datacap") + return msgs, nil + } + } + + msgs = append(msgs, nclaimMsg) + } + + return msgs, nil +} diff --git a/documentation/en/cli-lotus.md b/documentation/en/cli-lotus.md index 65dd92f01..ad04b68ec 100644 --- a/documentation/en/cli-lotus.md +++ b/documentation/en/cli-lotus.md @@ -1192,6 +1192,7 @@ COMMANDS: list-claims List claims available in verified registry actor or made by provider if specified remove-expired-allocations remove expired allocations (if no allocations are specified all eligible allocations are removed) remove-expired-claims remove expired claims (if no claims are specified all eligible claims are removed) + extend-claim extend claim expiration (TermMax) help, h Shows a list of commands or help for one command OPTIONS: @@ -1325,6 +1326,24 @@ OPTIONS: --help, -h show help ``` +### lotus filplus extend-claim +``` +NAME: + lotus filplus extend-claim - extend claim expiration (TermMax) + +USAGE: + lotus filplus extend-claim [command options] ... or ... + +OPTIONS: + --term-max value, --tmax value The maximum period for which a provider can earn quality-adjusted power for the piece (epochs). Default is 5 years. (default: 5256000) + --client value the client address that will used to send the message + --all automatically extend TermMax of all claims for specified miner[s] to --term-max (default: 5 years from claim start epoch) (default: false) + --miner value, -m value, --provider value, -p value [ --miner value, -m value, --provider value, -p value ] storage provider address[es] + --assume-yes, -y, --yes automatic yes to prompts; assume 'yes' as answer to all prompts and run non-interactively (default: false) + --confidence value number of block confirmations to wait for (default: 5) + --help, -h show help +``` + ## lotus paych ``` NAME: diff --git a/itests/direct_data_onboard_verified_test.go b/itests/direct_data_onboard_verified_test.go index da1cac261..2ac6142c0 100644 --- a/itests/direct_data_onboard_verified_test.go +++ b/itests/direct_data_onboard_verified_test.go @@ -37,6 +37,7 @@ import ( "github.com/filecoin-project/lotus/chain/actors/builtin/verifreg" "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/wallet/key" + "github.com/filecoin-project/lotus/cli" "github.com/filecoin-project/lotus/itests/kit" "github.com/filecoin-project/lotus/lib/must" "github.com/filecoin-project/lotus/storage/pipeline/piece" @@ -108,7 +109,8 @@ func TestOnboardRawPieceVerified_WithActorEvents(t *testing.T) { /* --- Setup verified registry and client and allocate datacap to client */ - verifierAddr, verifiedClientAddr := ddoVerifiedSetupVerifiedClient(ctx, t, client, rootKey, verifierKey, verifiedClientKey) + verifierAddr, verifiedClientAddrses := ddoVerifiedSetupVerifiedClient(ctx, t, client, rootKey, verifierKey, []*key.Key{verifiedClientKey}) + verifiedClientAddr := verifiedClientAddrses[0] /* --- Prepare piece for onboarding --- */ @@ -121,7 +123,7 @@ func TestOnboardRawPieceVerified_WithActorEvents(t *testing.T) { /* --- Allocate datacap for the piece by the verified client --- */ - clientId, allocationId := ddoVerifiedSetupAllocations(ctx, t, client, minerId, dc, verifiedClientAddr) + clientId, allocationId := ddoVerifiedSetupAllocations(ctx, t, client, minerId, dc, verifiedClientAddr, true, 0) head, err := client.ChainHead(ctx) require.NoError(t, err) @@ -396,40 +398,57 @@ func ddoVerifiedSetupAllocations( minerId uint64, dc abi.PieceInfo, verifiedClientAddr address.Address, + setupBorkAlloc bool, + tmax abi.ChainEpoch, ) (clientID abi.ActorID, allocationID verifregtypes13.AllocationId) { + if tmax == 0 { + tmax = verifregtypes13.MaximumVerifiedAllocationTerm + } - head, err := node.ChainHead(ctx) - require.NoError(t, err) + var requests []verifregtypes13.AllocationRequest // design this one to expire so we can observe allocation-removed - expiringAllocationHeight := head.Height() + 100 - allocationRequestBork := verifregtypes13.AllocationRequest{ - Provider: abi.ActorID(minerId), - Data: cid.MustParse("baga6ea4seaaqa"), - Size: dc.Size, - TermMin: verifregtypes13.MinimumVerifiedAllocationTerm, - TermMax: verifregtypes13.MaximumVerifiedAllocationTerm, - Expiration: expiringAllocationHeight, + if setupBorkAlloc { + head, err := node.ChainHead(ctx) + require.NoError(t, err) + expiringAllocationHeight := head.Height() + 100 + allocationRequestBork := verifregtypes13.AllocationRequest{ + Provider: abi.ActorID(minerId), + Data: cid.MustParse("baga6ea4seaaqa"), + Size: dc.Size, + TermMin: verifregtypes13.MinimumVerifiedAllocationTerm, + TermMax: tmax, + Expiration: expiringAllocationHeight, + } + requests = append(requests, allocationRequestBork) } + allocationRequest := verifregtypes13.AllocationRequest{ Provider: abi.ActorID(minerId), Data: dc.PieceCID, Size: dc.Size, TermMin: verifregtypes13.MinimumVerifiedAllocationTerm, - TermMax: verifregtypes13.MaximumVerifiedAllocationTerm, + TermMax: tmax, Expiration: verifregtypes13.MaximumVerifiedAllocationExpiration, } + requests = append(requests, allocationRequest) allocationRequests := verifregtypes13.AllocationRequests{ - Allocations: []verifregtypes13.AllocationRequest{allocationRequestBork, allocationRequest}, + Allocations: requests, } receiverParams, aerr := actors.SerializeParams(&allocationRequests) require.NoError(t, aerr) + var amt abi.TokenAmount + amt = big.Mul(big.NewInt(int64(dc.Size)), builtin.TokenPrecision) + if setupBorkAlloc { + amt = big.Mul(big.NewInt(int64(dc.Size*2)), builtin.TokenPrecision) + } + transferParams, aerr := actors.SerializeParams(&datacap2.TransferParams{ To: builtin.VerifiedRegistryActorAddr, - Amount: big.Mul(big.NewInt(int64(dc.Size*2)), builtin.TokenPrecision), + Amount: amt, OperatorData: receiverParams, }) require.NoError(t, aerr) @@ -452,7 +471,11 @@ func ddoVerifiedSetupAllocations( // check that we have an allocation allocations, err := node.StateGetAllocations(ctx, verifiedClientAddr, types.EmptyTSK) require.NoError(t, err) - require.Len(t, allocations, 2) // allocation waiting to be claimed + if setupBorkAlloc { + require.Len(t, allocations, 2) // allocation waiting to be claimed + } else { + require.Len(t, allocations, 1) // allocation waiting to be claimed + } for key, value := range allocations { if value.Data == dc.PieceCID { @@ -603,18 +626,21 @@ func ddoVerifiedBuildActorEventsFromMessages(ctx context.Context, t *testing.T, return actorEvents } -func ddoVerifiedSetupVerifiedClient(ctx context.Context, t *testing.T, client *kit.TestFullNode, rootKey *key.Key, verifierKey *key.Key, verifiedClientKey *key.Key) (address.Address, address.Address) { +func ddoVerifiedSetupVerifiedClient(ctx context.Context, t *testing.T, client *kit.TestFullNode, rootKey *key.Key, verifierKey *key.Key, verifiedClientKeys []*key.Key) (verifierAddr address.Address, ret []address.Address) { // import the root key. rootAddr, err := client.WalletImport(ctx, &rootKey.KeyInfo) require.NoError(t, err) // import the verifiers' keys. - verifierAddr, err := client.WalletImport(ctx, &verifierKey.KeyInfo) + verifierAddr, err = client.WalletImport(ctx, &verifierKey.KeyInfo) require.NoError(t, err) // import the verified client's key. - verifiedClientAddr, err := client.WalletImport(ctx, &verifiedClientKey.KeyInfo) - require.NoError(t, err) + for _, k := range verifiedClientKeys { + verifiedClientAddr, err := client.WalletImport(ctx, &k.KeyInfo) + require.NoError(t, err) + ret = append(ret, verifiedClientAddr) + } allowance := big.NewInt(100000000000) params, aerr := actors.SerializeParams(&verifregtypes13.AddVerifierParams{Address: verifierAddr, Allowance: allowance}) @@ -639,28 +665,30 @@ func ddoVerifiedSetupVerifiedClient(ctx context.Context, t *testing.T, client *k require.NoError(t, err) require.Equal(t, allowance, *verifierAllowance) - // assign datacap to a client - initialDatacap := big.NewInt(10000) + // assign datacap to clients + for _, ad := range ret { + initialDatacap := big.NewInt(10000) - params, aerr = actors.SerializeParams(&verifregtypes13.AddVerifiedClientParams{Address: verifiedClientAddr, Allowance: initialDatacap}) - require.NoError(t, aerr) + params, aerr = actors.SerializeParams(&verifregtypes13.AddVerifiedClientParams{Address: ad, Allowance: initialDatacap}) + require.NoError(t, aerr) - msg = &types.Message{ - From: verifierAddr, - To: verifreg.Address, - Method: verifreg.Methods.AddVerifiedClient, - Params: params, - Value: big.Zero(), + msg = &types.Message{ + From: verifierAddr, + To: verifreg.Address, + Method: verifreg.Methods.AddVerifiedClient, + Params: params, + Value: big.Zero(), + } + + sm, err = client.MpoolPushMessage(ctx, msg, nil) + require.NoError(t, err) + + res, err = client.StateWaitMsg(ctx, sm.Cid(), 1, lapi.LookbackNoLimit, true) + require.NoError(t, err) + require.EqualValues(t, 0, res.Receipt.ExitCode) } - sm, err = client.MpoolPushMessage(ctx, msg, nil) - require.NoError(t, err) - - res, err = client.StateWaitMsg(ctx, sm.Cid(), 1, lapi.LookbackNoLimit, true) - require.NoError(t, err) - require.EqualValues(t, 0, res.Receipt.ExitCode) - - return verifierAddr, verifiedClientAddr + return } func filterEvents(events []*types.ActorEvent, key string) []*types.ActorEvent { @@ -711,3 +739,122 @@ func epochPtr(ei int64) *abi.ChainEpoch { ep := abi.ChainEpoch(ei) return &ep } + +func TestVerifiedDDOExtendClaim(t *testing.T) { + kit.QuietMiningLogs() + + var ( + blocktime = 2 * time.Millisecond + ctx = context.Background() + ) + + rootKey, err := key.GenerateKey(types.KTSecp256k1) + require.NoError(t, err) + + verifierKey, err := key.GenerateKey(types.KTSecp256k1) + require.NoError(t, err) + + verifiedClientKey1, err := key.GenerateKey(types.KTBLS) + require.NoError(t, err) + + verifiedClientKey2, err := key.GenerateKey(types.KTBLS) + require.NoError(t, err) + + unverifiedClient, err := key.GenerateKey(types.KTBLS) + require.NoError(t, err) + + bal, err := types.ParseFIL("100fil") + require.NoError(t, err) + + client, miner, ens := kit.EnsembleMinimal(t, kit.ThroughRPC(), + kit.RootVerifier(rootKey, abi.NewTokenAmount(bal.Int64())), + kit.Account(verifierKey, abi.NewTokenAmount(bal.Int64())), + kit.Account(verifiedClientKey1, abi.NewTokenAmount(bal.Int64())), + kit.Account(verifiedClientKey2, abi.NewTokenAmount(bal.Int64())), + kit.Account(unverifiedClient, abi.NewTokenAmount(bal.Int64())), + ) + + /* --- Start mining --- */ + + ens.InterconnectAll().BeginMiningMustPost(blocktime) + + minerId, err := address.IDFromAddress(miner.ActorAddr) + require.NoError(t, err) + + /* --- Setup verified registry and clients and allocate datacap to client */ + + _, verifiedClientAddrses := ddoVerifiedSetupVerifiedClient(ctx, t, client, rootKey, verifierKey, []*key.Key{verifiedClientKey1, verifiedClientKey2}) + verifiedClientAddr1 := verifiedClientAddrses[0] + verifiedClientAddr2 := verifiedClientAddrses[1] + + /* --- Prepare piece for onboarding --- */ + + pieceSize := abi.PaddedPieceSize(2048).Unpadded() + pieceData := make([]byte, pieceSize) + _, _ = rand.Read(pieceData) + + dc, err := miner.ComputeDataCid(ctx, pieceSize, bytes.NewReader(pieceData)) + require.NoError(t, err) + + /* --- Allocate datacap for the piece by the verified client --- */ + clientId, allocationId := ddoVerifiedSetupAllocations(ctx, t, client, minerId, dc, verifiedClientAddr1, false, builtin.EpochsInYear*3) + + /* --- Onboard the piece --- */ + + _, _ = ddoVerifiedOnboardPiece(ctx, t, miner, clientId, allocationId, dc, pieceData) + + oldclaim, err := client.StateGetClaim(ctx, miner.ActorAddr, verifreg.ClaimId(allocationId), types.EmptyTSK) + require.NoError(t, err) + require.NotNil(t, oldclaim) + + prov := cli.ProvInfo{ + Addr: miner.ActorAddr, + ID: abi.ActorID(minerId), + } + + pcm := make(map[verifregtypes13.ClaimId]cli.ProvInfo) + pcm[verifregtypes13.ClaimId(allocationId)] = prov + + // Extend claim with same client + msgs, err := cli.CreateExtendClaimMsg(ctx, client.FullNode, pcm, []string{}, verifiedClientAddr1, (builtin.EpochsInYear*3)+3000, false, true) + require.NoError(t, err) + require.NotNil(t, msgs) + require.Len(t, msgs, 1) + + // MpoolBatchPushMessage method will take care of gas estimation and funds check + smsg, err := client.MpoolPushMessage(ctx, msgs[0], nil) + require.NoError(t, err) + + wait, err := client.StateWaitMsg(ctx, smsg.Cid(), 1, 2000, true) + require.NoError(t, err) + require.True(t, wait.Receipt.ExitCode.IsSuccess()) + + newclaim, err := client.StateGetClaim(ctx, miner.ActorAddr, verifreg.ClaimId(allocationId), types.EmptyTSK) + require.NoError(t, err) + require.NotNil(t, newclaim) + require.EqualValues(t, newclaim.TermMax-oldclaim.TermMax, 3000) + + // Extend claim with non-verified client | should fail + _, err = cli.CreateExtendClaimMsg(ctx, client.FullNode, pcm, []string{}, unverifiedClient.Address, verifregtypes13.MaximumVerifiedAllocationTerm, false, true) + require.ErrorContains(t, err, "does not have any datacap") + + // Extend all claim with verified client + msgs, err = cli.CreateExtendClaimMsg(ctx, client.FullNode, nil, []string{miner.ActorAddr.String()}, verifiedClientAddr2, verifregtypes13.MaximumVerifiedAllocationTerm, true, true) + require.NoError(t, err) + require.Len(t, msgs, 1) + smsg, err = client.MpoolPushMessage(ctx, msgs[0], nil) + require.NoError(t, err) + wait, err = client.StateWaitMsg(ctx, smsg.Cid(), 1, 2000, true) + require.NoError(t, err) + require.True(t, wait.Receipt.ExitCode.IsSuccess()) + + // Extend all claims with lower TermMax + msgs, err = cli.CreateExtendClaimMsg(ctx, client.FullNode, pcm, []string{}, verifiedClientAddr2, builtin.EpochsInYear*4, false, true) + require.NoError(t, err) + require.Nil(t, msgs) + + newclaim, err = client.StateGetClaim(ctx, miner.ActorAddr, verifreg.ClaimId(allocationId), types.EmptyTSK) + require.NoError(t, err) + require.NotNil(t, newclaim) + require.EqualValues(t, newclaim.TermMax, verifregtypes13.MaximumVerifiedAllocationTerm) +} From f11a6058b5f2c267266ee9e6c2161055830fb031 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 20 Mar 2024 09:15:20 -0700 Subject: [PATCH 281/394] fix: api: skip evm events where we no longer have the associated txn (#11748) This can happen if, e.g., we run a splitstore garbage collection. fixes #11117 --- node/impl/full/eth_events.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/node/impl/full/eth_events.go b/node/impl/full/eth_events.go index 81ecef64b..063590d8d 100644 --- a/node/impl/full/eth_events.go +++ b/node/impl/full/eth_events.go @@ -121,6 +121,10 @@ func ethFilterResultFromEvents(ctx context.Context, evs []*filter.CollectedEvent if err != nil { return nil, err } + if log.TransactionHash == ethtypes.EmptyEthHash { + // We've garbage collected the message, ignore the events and continue. + continue + } c, err := ev.TipSetKey.Cid() if err != nil { return nil, err From 32efba72cf30e130726775817f848acbb40066db Mon Sep 17 00:00:00 2001 From: "Andrew Jackson (Ajax)" Date: Wed, 20 Mar 2024 18:11:05 -0500 Subject: [PATCH 282/394] harmonydb safety --- lib/harmony/harmonydb/harmonydb.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/harmony/harmonydb/harmonydb.go b/lib/harmony/harmonydb/harmonydb.go index a043e903f..549cf1518 100644 --- a/lib/harmony/harmonydb/harmonydb.go +++ b/lib/harmony/harmonydb/harmonydb.go @@ -263,6 +263,7 @@ func (db *DB) upgrade() error { if len(dir) == 0 { logger.Error("No sql files found.") } + last := "" for _, e := range dir { name := e.Name() if !strings.HasSuffix(name, ".sql") { @@ -273,6 +274,9 @@ func (db *DB) upgrade() error { logger.Debug("DB Schema " + name + " already applied.") continue } + if last[:8] == name[:8] { + return xerrors.Errorf("Two files have the same date prefix: " + last + " and " + name) + } file, err := fs.ReadFile("sql/" + name) if err != nil { logger.Error("weird embed file read err") @@ -296,6 +300,7 @@ func (db *DB) upgrade() error { logger.Error("Cannot update base: " + err.Error()) return xerrors.Errorf("cannot insert into base: %w", err) } + last = name } return nil } From ea19e1df6b8cc6b4d1f9159cf794bfafd67e53bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 21 Mar 2024 11:08:39 +0100 Subject: [PATCH 283/394] chore: revert accidental direct commits to master --- lib/harmony/harmonydb/harmonydb.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/lib/harmony/harmonydb/harmonydb.go b/lib/harmony/harmonydb/harmonydb.go index 7352866d6..3d05f04fb 100644 --- a/lib/harmony/harmonydb/harmonydb.go +++ b/lib/harmony/harmonydb/harmonydb.go @@ -263,7 +263,6 @@ func (db *DB) upgrade() error { if len(dir) == 0 { logger.Error("No sql files found.") } - last := "" for _, e := range dir { name := e.Name() if !strings.HasSuffix(name, ".sql") { @@ -274,9 +273,6 @@ func (db *DB) upgrade() error { logger.Debug("DB Schema " + name + " already applied.") continue } - if last[:8] == name[:8] { - return xerrors.Errorf("Two files have the same date prefix: " + last + " and " + name) - } file, err := fs.ReadFile("sql/" + name) if err != nil { logger.Error("weird embed file read err") @@ -300,7 +296,6 @@ func (db *DB) upgrade() error { logger.Error("Cannot update base: " + err.Error()) return xerrors.Errorf("cannot insert into base: %w", err) } - last = name } return nil } From 566584d45c0a46059e7e28e77020114bdb442868 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 21 Mar 2024 12:49:55 -0700 Subject: [PATCH 284/394] fix: api: return the correct block gas limit in the EthAPI (#11747) The gas limit is proportional to the number of blocks. fixes #11721 --- chain/types/ethtypes/eth_types.go | 4 ++-- itests/eth_block_hash_test.go | 12 ++++++++++++ node/impl/full/eth_utils.go | 2 +- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/chain/types/ethtypes/eth_types.go b/chain/types/ethtypes/eth_types.go index 35fed87d8..2740a3e9d 100644 --- a/chain/types/ethtypes/eth_types.go +++ b/chain/types/ethtypes/eth_types.go @@ -197,7 +197,7 @@ func init() { } } -func NewEthBlock(hasTransactions bool) EthBlock { +func NewEthBlock(hasTransactions bool, tipsetLen int) EthBlock { b := EthBlock{ Sha3Uncles: EmptyUncleHash, // Sha3Uncles set to a hardcoded value which is used by some clients to determine if has no uncles. StateRoot: EmptyEthHash, @@ -208,7 +208,7 @@ func NewEthBlock(hasTransactions bool) EthBlock { Extradata: []byte{}, MixHash: EmptyEthHash, Nonce: EmptyEthNonce, - GasLimit: EthUint64(build.BlockGasLimit), // TODO we map Ethereum blocks to Filecoin tipsets; this is inconsistent. + GasLimit: EthUint64(build.BlockGasLimit * int64(tipsetLen)), Uncles: []EthHash{}, Transactions: []interface{}{}, } diff --git a/itests/eth_block_hash_test.go b/itests/eth_block_hash_test.go index b582c84e3..8debefa49 100644 --- a/itests/eth_block_hash_test.go +++ b/itests/eth_block_hash_test.go @@ -11,6 +11,8 @@ import ( "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/lotus/build" + "github.com/filecoin-project/lotus/chain/types/ethtypes" "github.com/filecoin-project/lotus/itests/kit" ) @@ -49,19 +51,29 @@ func TestEthBlockHashesCorrect_MultiBlockTipset(t *testing.T) { // let the chain run a little bit longer to minimise the chance of reorgs n2.WaitTillChain(ctx, kit.HeightAtLeast(head.Height()+50)) + tsk := head.Key() for i := 1; i <= int(head.Height()); i++ { hex := fmt.Sprintf("0x%x", i) + ts, err := n2.ChainGetTipSetByHeight(ctx, abi.ChainEpoch(i), tsk) + require.NoError(t, err) + ethBlockA, err := n2.EthGetBlockByNumber(ctx, hex, true) // Cannot use static ErrFullRound error for comparison since it gets reserialized as a JSON RPC error. if err != nil && strings.Contains(err.Error(), "null round") { + require.Less(t, ts.Height(), abi.ChainEpoch(i), "did not expect a tipset at epoch %d", i) continue } require.NoError(t, err) + require.Equal(t, ts.Height(), abi.ChainEpoch(i), "expected a tipset at epoch %i", i) ethBlockB, err := n2.EthGetBlockByHash(ctx, ethBlockA.Hash, true) require.NoError(t, err) require.Equal(t, ethBlockA, ethBlockB) + + numBlocks := len(ts.Blocks()) + expGasLimit := ethtypes.EthUint64(int64(numBlocks) * build.BlockGasLimit) + require.Equal(t, expGasLimit, ethBlockB.GasLimit) } } diff --git a/node/impl/full/eth_utils.go b/node/impl/full/eth_utils.go index 6186c2645..50e76f84e 100644 --- a/node/impl/full/eth_utils.go +++ b/node/impl/full/eth_utils.go @@ -225,7 +225,7 @@ func newEthBlockFromFilecoinTipSet(ctx context.Context, ts *types.TipSet, fullTx return ethtypes.EthBlock{}, xerrors.Errorf("failed to load state-tree root %q: %w", stRoot, err) } - block := ethtypes.NewEthBlock(len(msgs) > 0) + block := ethtypes.NewEthBlock(len(msgs) > 0, len(ts.Blocks())) gasUsed := int64(0) for i, msg := range msgs { From e2ab5d68adcf7844b6dd2bfd6fe85e92a64567dd Mon Sep 17 00:00:00 2001 From: Piotr Galar Date: Fri, 22 Mar 2024 12:57:03 +0000 Subject: [PATCH 285/394] ci: ci: create gh workflow that runs go build (#11760) * ci: create gh action that installs go from go.mod * ci: create gh action that installs ubuntu dependencies * ci: create gh workflow that runs go build * ci: test the build workflow * Revert "ci: test the build workflow" This reverts commit 8150510aae4d6d1fb1d719e2d69057a3c0695d32. --- .github/actions/install-go/action.yml | 16 ++++++++++ .../actions/install-ubuntu-deps/action.yml | 10 +++++++ .github/workflows/build.yml | 29 +++++++++++++++++++ 3 files changed, 55 insertions(+) create mode 100644 .github/actions/install-go/action.yml create mode 100644 .github/actions/install-ubuntu-deps/action.yml create mode 100644 .github/workflows/build.yml diff --git a/.github/actions/install-go/action.yml b/.github/actions/install-go/action.yml new file mode 100644 index 000000000..173559284 --- /dev/null +++ b/.github/actions/install-go/action.yml @@ -0,0 +1,16 @@ +name: Install Go +description: Install Go for Filecoin Lotus + +runs: + using: composite + steps: + - uses: actions/setup-go@v5 + with: + go-version: stable + cache: false + - id: go-mod + uses: ipdxco/unified-github-workflows/.github/actions/read-go-mod@main + - uses: actions/setup-go@v5 + with: + go-version: ${{ fromJSON(steps.go-mod.outputs.json).Go }}.x + cache: false diff --git a/.github/actions/install-ubuntu-deps/action.yml b/.github/actions/install-ubuntu-deps/action.yml new file mode 100644 index 000000000..395e8d47b --- /dev/null +++ b/.github/actions/install-ubuntu-deps/action.yml @@ -0,0 +1,10 @@ +name: Install Ubuntu Dependencies +description: Install Ubuntu dependencies for Filecoin Lotus + +runs: + using: composite + steps: + - run: | + sudo apt-get update -y + sudo apt-get install -y ocl-icd-opencl-dev libhwloc-dev pkg-config + shell: bash diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 000000000..cb56c05de --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,29 @@ +name: Build + +on: + pull_request: + push: + branches: + - master + - release/* + workflow_dispatch: + +defaults: + run: + shell: bash + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + build: + name: Build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + submodules: 'recursive' + - uses: ./.github/actions/install-ubuntu-deps + - uses: ./.github/actions/install-go + - run: make deps lotus From ed98bf9ec581c92cab205d4c91c6b04ea744e065 Mon Sep 17 00:00:00 2001 From: Piotr Galar Date: Fri, 22 Mar 2024 12:57:14 +0000 Subject: [PATCH 286/394] ci: ci: create gh workflow that runs go checks (#11761) * ci: create gh action that installs go from go.mod * ci: create gh action that installs ubuntu dependencies * ci: create gh workflow that runs go checks * ci: test the check workflow * Revert "ci: test the check workflow" This reverts commit 1d0759d3c69eae99d17c5a80470b7a40d5b3bc94. --- .github/workflows/check.yml | 80 +++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 .github/workflows/check.yml diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml new file mode 100644 index 000000000..50c926a31 --- /dev/null +++ b/.github/workflows/check.yml @@ -0,0 +1,80 @@ +name: Check + +on: + pull_request: + push: + branches: + - master + - release/* + workflow_dispatch: + +defaults: + run: + shell: bash + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + check-docsgen: + name: Check (docs-check) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + submodules: 'recursive' + - uses: ./.github/actions/install-ubuntu-deps + - uses: ./.github/actions/install-go + - run: go install golang.org/x/tools/cmd/goimports + - run: make deps + - run: make docsgen + - run: git diff --exit-code + check-gen: + name: Check (gen-check) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + submodules: 'recursive' + - uses: ./.github/actions/install-ubuntu-deps + - uses: ./.github/actions/install-go + - run: make deps lotus + - run: go install golang.org/x/tools/cmd/goimports + - run: go install github.com/hannahhoward/cbor-gen-for + - run: make gen + - run: git diff --exit-code + - run: make docsgen-cli + - run: git diff --exit-code + check-lint: + name: Check (lint-all) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + submodules: 'recursive' + - uses: ./.github/actions/install-ubuntu-deps + - uses: ./.github/actions/install-go + - run: go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest + - run: make deps + - run: golangci-lint run -v --timeout 10m --concurrency 4 + check-fmt: + name: Check (gofmt) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + submodules: 'recursive' + - uses: ./.github/actions/install-go + - run: go fmt ./... + - run: git diff --exit-code + check-mod-tidy: + name: Check (mod-tidy-check) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + submodules: 'recursive' + - uses: ./.github/actions/install-go + - run: go mod tidy -v + - run: git diff --exit-code From 8ec74fdb057bdf97f608a41d9ffb8e70ce50634c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sun, 17 Mar 2024 17:41:06 +0100 Subject: [PATCH 287/394] itests: Try to deflake TestSectorImportAfterPC2 --- itests/sector_import_simple_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/itests/sector_import_simple_test.go b/itests/sector_import_simple_test.go index fb1a77a14..ec499b8a7 100644 --- a/itests/sector_import_simple_test.go +++ b/itests/sector_import_simple_test.go @@ -42,7 +42,7 @@ func TestSectorImportAfterPC2(t *testing.T) { // We use two miners so that in case the actively tested miner misses PoSt, we still have a blockchain client, miner, _, ens := kit.EnsembleOneTwo(t, kit.ThroughRPC()) - ens.InterconnectAll().BeginMining(blockTime) + ens.InterconnectAll().BeginMiningMustPost(blockTime) ctx := context.Background() From e86d81b26af70dafab842eb50b6722c18189ab18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sun, 17 Mar 2024 12:19:27 +0100 Subject: [PATCH 288/394] fix: curio seal: Fix partial Finalize retry --- curiosrc/ffi/sdr_funcs.go | 53 +++++++++++++++++++++++++++- itests/sector_import_simple_test.go | 2 +- storage/paths/local.go | 12 +++++-- storage/sealer/storiface/filetype.go | 4 +++ 4 files changed, 66 insertions(+), 5 deletions(-) diff --git a/curiosrc/ffi/sdr_funcs.go b/curiosrc/ffi/sdr_funcs.go index 199f88afe..52d90f70d 100644 --- a/curiosrc/ffi/sdr_funcs.go +++ b/curiosrc/ffi/sdr_funcs.go @@ -374,12 +374,62 @@ func (sb *SealCalls) FinalizeSector(ctx context.Context, sector storiface.Sector // * we truncate the temp file to the sector size // * we move the temp file to the unsealed location - // move tree-d to temp file + // temp path in cache where we'll move tree-d before truncating + // it is in the cache directory so that we can use os.Rename to move it + // to unsealed (which may be on a different filesystem) tempUnsealed := filepath.Join(sectorPaths.Cache, storiface.SectorName(sector.ID)) + + _, terr := os.Stat(tempUnsealed) + tempUnsealedExists := terr == nil + + // First handle an edge case where we have already gone through this step, + // but ClearCache or later steps failed. In that case we'll see tree-d missing and unsealed present + + if _, err := os.Stat(filepath.Join(sectorPaths.Cache, proofpaths.TreeDName)); err != nil { + if os.IsNotExist(err) { + // check that unsealed exists and is the right size + st, err := os.Stat(sectorPaths.Unsealed) + if err != nil { + if os.IsNotExist(err) { + if tempUnsealedExists { + // unsealed file does not exist, but temp unsealed file does + // so we can just resume where the previous attempt left off + goto retryUnsealedMove + } + return xerrors.Errorf("neither unsealed file nor temp-unsealed file exists") + } + return xerrors.Errorf("stat unsealed file: %w", err) + } + if st.Size() != int64(ssize) { + if tempUnsealedExists { + // unsealed file exists but is the wrong size, and temp unsealed file exists + // so we can just resume where the previous attempt left off with some cleanup + + if err := os.Remove(sectorPaths.Unsealed); err != nil { + return xerrors.Errorf("removing unsealed file from last attempt: %w", err) + } + + goto retryUnsealedMove + } + return xerrors.Errorf("unsealed file is not the right size: %d != %d and temp unsealed is missing", st.Size(), ssize) + } + + // all good, just log that this edge case happened + log.Warnw("unsealed file exists but tree-d is missing, skipping move", "sector", sector.ID, "unsealed", sectorPaths.Unsealed, "cache", sectorPaths.Cache) + goto afterUnsealedMove + } + return xerrors.Errorf("stat tree-d file: %w", err) + } + + // If the state in clean do the move + + // move tree-d to temp file if err := os.Rename(filepath.Join(sectorPaths.Cache, proofpaths.TreeDName), tempUnsealed); err != nil { return xerrors.Errorf("moving tree-d to temp file: %w", err) } + retryUnsealedMove: + // truncate sealed file to sector size if err := os.Truncate(tempUnsealed, int64(ssize)); err != nil { return xerrors.Errorf("truncating unsealed file to sector size: %w", err) @@ -391,6 +441,7 @@ func (sb *SealCalls) FinalizeSector(ctx context.Context, sector storiface.Sector } } +afterUnsealedMove: if err := ffi.ClearCache(uint64(ssize), sectorPaths.Cache); err != nil { return xerrors.Errorf("clearing cache: %w", err) } diff --git a/itests/sector_import_simple_test.go b/itests/sector_import_simple_test.go index ec499b8a7..fb1a77a14 100644 --- a/itests/sector_import_simple_test.go +++ b/itests/sector_import_simple_test.go @@ -42,7 +42,7 @@ func TestSectorImportAfterPC2(t *testing.T) { // We use two miners so that in case the actively tested miner misses PoSt, we still have a blockchain client, miner, _, ens := kit.EnsembleOneTwo(t, kit.ThroughRPC()) - ens.InterconnectAll().BeginMiningMustPost(blockTime) + ens.InterconnectAll().BeginMining(blockTime) ctx := context.Background() diff --git a/storage/paths/local.go b/storage/paths/local.go index 71de0becf..7dd7c1256 100644 --- a/storage/paths/local.go +++ b/storage/paths/local.go @@ -476,14 +476,18 @@ func (st *Local) AcquireSector(ctx context.Context, sid storiface.SectorRef, exi var out storiface.SectorPaths var storageIDs storiface.SectorPaths + // First find existing files for _, fileType := range storiface.PathTypes { - if fileType&existing == 0 { + // also try to find existing sectors if we're allocating + if fileType&(existing|allocate) == 0 { continue } si, err := st.index.StorageFindSector(ctx, sid.ID, fileType, ssize, false) if err != nil { - log.Warnf("finding existing sector %d(t:%d) failed: %+v", sid, fileType, err) + if fileType&existing != 0 { + log.Warnf("finding existing sector %d(t:%d) failed: %+v", sid, fileType, err) + } continue } @@ -501,11 +505,13 @@ func (st *Local) AcquireSector(ctx context.Context, sid storiface.SectorRef, exi storiface.SetPathByType(&out, fileType, spath) storiface.SetPathByType(&storageIDs, fileType, string(info.ID)) - existing ^= fileType + existing = existing.Unset(fileType) + allocate = allocate.Unset(fileType) break } } + // Then allocate for allocation requests for _, fileType := range storiface.PathTypes { if fileType&allocate == 0 { continue diff --git a/storage/sealer/storiface/filetype.go b/storage/sealer/storiface/filetype.go index 887dda688..109e494a8 100644 --- a/storage/sealer/storiface/filetype.go +++ b/storage/sealer/storiface/filetype.go @@ -174,6 +174,10 @@ func (t SectorFileType) SubAllowed(allowTypes []string, denyTypes []string) Sect return t & denyMask } +func (t SectorFileType) Unset(toUnset SectorFileType) SectorFileType { + return t &^ toUnset +} + func (t SectorFileType) AnyAllowed(allowTypes []string, denyTypes []string) bool { return t.SubAllowed(allowTypes, denyTypes) != t } From 099fc04d0a3070cf4c53423bc692b41602d01301 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 25 Mar 2024 16:35:54 +0100 Subject: [PATCH 289/394] curio web: node info page (#11745) * curio web: node info page * curioweb: add missing files * curio web: Better hapi route prefixes --- curiosrc/web/hapi/routes.go | 19 +- curiosrc/web/hapi/simpleinfo.go | 292 +++++++++++++++++- curiosrc/web/hapi/web/cluster_machines.gohtml | 2 +- curiosrc/web/hapi/web/cluster_tasks.gohtml | 2 +- curiosrc/web/hapi/web/node_info.gohtml | 100 ++++++ curiosrc/web/hapi/web/root.gohtml | 25 ++ curiosrc/web/srv.go | 4 +- curiosrc/web/static/main.css | 10 +- curiosrc/web/static/pipeline_porep.html | 2 +- lib/harmony/harmonytask/harmonytask.go | 2 +- 10 files changed, 435 insertions(+), 23 deletions(-) create mode 100644 curiosrc/web/hapi/web/node_info.gohtml create mode 100644 curiosrc/web/hapi/web/root.gohtml diff --git a/curiosrc/web/hapi/routes.go b/curiosrc/web/hapi/routes.go index fd2f24e94..430b02efb 100644 --- a/curiosrc/web/hapi/routes.go +++ b/curiosrc/web/hapi/routes.go @@ -2,12 +2,13 @@ package hapi import ( "embed" - "html/template" + "text/template" "github.com/gorilla/mux" logging "github.com/ipfs/go-log/v2" "golang.org/x/xerrors" + "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/cmd/curio/deps" ) @@ -15,7 +16,7 @@ import ( var templateFS embed.FS func Routes(r *mux.Router, deps *deps.Deps) error { - t, err := template.ParseFS(templateFS, "web/*") + t, err := makeTemplate().ParseFS(templateFS, "web/*") if err != nil { return xerrors.Errorf("parse templates: %w", err) } @@ -28,6 +29,7 @@ func Routes(r *mux.Router, deps *deps.Deps) error { go a.watchRpc() go a.watchActor() + // index page (simple info) r.HandleFunc("/simpleinfo/actorsummary", a.actorSummary) r.HandleFunc("/simpleinfo/machines", a.indexMachines) r.HandleFunc("/simpleinfo/tasks", a.indexTasks) @@ -35,8 +37,19 @@ func Routes(r *mux.Router, deps *deps.Deps) error { r.HandleFunc("/simpleinfo/pipeline-porep", a.indexPipelinePorep) // pipeline-porep page - r.HandleFunc("/simpleinfo/pipeline-porep/sectors", a.pipelinePorepSectors) + r.HandleFunc("/pipeline-porep/sectors", a.pipelinePorepSectors) + + // node info page + r.HandleFunc("/node/{id}", a.nodeInfo) return nil } +func makeTemplate() *template.Template { + return template.New("").Funcs(template.FuncMap{ + "toHumanBytes": func(b int64) string { + return types.SizeStr(types.NewInt(uint64(b))) + }, + }) +} + var log = logging.Logger("curio/web") diff --git a/curiosrc/web/hapi/simpleinfo.go b/curiosrc/web/hapi/simpleinfo.go index fd6635059..014f154f8 100644 --- a/curiosrc/web/hapi/simpleinfo.go +++ b/curiosrc/web/hapi/simpleinfo.go @@ -1,14 +1,18 @@ package hapi import ( + "bytes" "context" - "html/template" + "fmt" "net/http" "os" "sort" + "strconv" "sync" + "text/template" "time" + "github.com/gorilla/mux" "golang.org/x/xerrors" "github.com/filecoin-project/lotus/api/v1api" @@ -99,12 +103,37 @@ func (a *app) indexPipelinePorep(w http.ResponseWriter, r *http.Request) { a.executeTemplate(w, "pipeline_porep", s) } +func (a *app) nodeInfo(writer http.ResponseWriter, request *http.Request) { + params := mux.Vars(request) + + id, ok := params["id"] + if !ok { + http.Error(writer, "missing id", http.StatusBadRequest) + return + } + + intid, err := strconv.ParseInt(id, 10, 64) + if err != nil { + http.Error(writer, "invalid id", http.StatusBadRequest) + return + } + + mi, err := a.clusterNodeInfo(request.Context(), intid) + if err != nil { + log.Errorf("machine info: %v", err) + http.Error(writer, "internal server error", http.StatusInternalServerError) + return + } + + a.executePageTemplate(writer, "node_info", "Node Info", mi) +} + var templateDev = os.Getenv("LOTUS_WEB_DEV") == "1" func (a *app) executeTemplate(w http.ResponseWriter, name string, data interface{}) { if templateDev { - fs := os.DirFS("./cmd/curio/web/hapi/web") - a.t = template.Must(template.ParseFS(fs, "*")) + fs := os.DirFS("./curiosrc/web/hapi/web") + a.t = template.Must(makeTemplate().ParseFS(fs, "*")) } if err := a.t.ExecuteTemplate(w, name, data); err != nil { log.Errorf("execute template %s: %v", name, err) @@ -112,6 +141,22 @@ func (a *app) executeTemplate(w http.ResponseWriter, name string, data interface } } +func (a *app) executePageTemplate(w http.ResponseWriter, name, title string, data interface{}) { + if templateDev { + fs := os.DirFS("./curiosrc/web/hapi/web") + a.t = template.Must(makeTemplate().ParseFS(fs, "*")) + } + var contentBuf bytes.Buffer + if err := a.t.ExecuteTemplate(&contentBuf, name, data); err != nil { + log.Errorf("execute template %s: %v", name, err) + http.Error(w, "internal server error", http.StatusInternalServerError) + } + a.executeTemplate(w, "root", map[string]interface{}{ + "PageTitle": title, + "Content": contentBuf.String(), + }) +} + type machineRecentTask struct { TaskName string Success int64 @@ -127,10 +172,10 @@ type machineSummary struct { } type taskSummary struct { - Name string - SincePosted string - Owner *string - ID int64 + Name string + SincePosted string + Owner, OwnerID *string + ID int64 } type taskHistorySummary struct { @@ -219,7 +264,7 @@ func (a *app) clusterMachineSummary(ctx context.Context) ([]machineSummary, erro } func (a *app) clusterTaskSummary(ctx context.Context) ([]taskSummary, error) { - rows, err := a.db.Query(ctx, "SELECT id, name, update_time, owner_id FROM harmony_task order by update_time asc, owner_id") + rows, err := a.db.Query(ctx, "SELECT t.id, t.name, t.update_time, t.owner_id, hm.host_and_port FROM harmony_task t LEFT JOIN curio.harmony_machines hm ON hm.id = t.owner_id ORDER BY t.update_time ASC, t.owner_id") if err != nil { return nil, err // Handle error } @@ -230,7 +275,7 @@ func (a *app) clusterTaskSummary(ctx context.Context) ([]taskSummary, error) { var t taskSummary var posted time.Time - if err := rows.Scan(&t.ID, &t.Name, &posted, &t.Owner); err != nil { + if err := rows.Scan(&t.ID, &t.Name, &posted, &t.OwnerID, &t.Owner); err != nil { return nil, err // Handle error } @@ -321,3 +366,232 @@ func (a *app) porepPipelineSummary(ctx context.Context) ([]porepPipelineSummary, } return summaries, nil } + +type machineInfo struct { + Info struct { + Host string + ID int64 + LastContact string + CPU int64 + Memory int64 + GPU int64 + } + + // Storage + Storage []struct { + ID string + Weight int64 + MaxStorage int64 + CanSeal bool + CanStore bool + Groups string + AllowTo string + AllowTypes string + DenyTypes string + Capacity int64 + Available int64 + FSAvailable int64 + Reserved int64 + Used int64 + LastHeartbeat time.Time + HeartbeatErr *string + + UsedPercent float64 + ReservedPercent float64 + } + + /*TotalStorage struct { + MaxStorage int64 + UsedStorage int64 + + MaxSealStorage int64 + UsedSealStorage int64 + + MaxStoreStorage int64 + UsedStoreStorage int64 + }*/ + + // Tasks + RunningTasks []struct { + ID int64 + Task string + Posted string + + PoRepSector, PoRepSectorSP *int64 + } + + FinishedTasks []struct { + ID int64 + Task string + Posted string + Start string + Queued string + Took string + Outcome string + Message string + } +} + +func (a *app) clusterNodeInfo(ctx context.Context, id int64) (*machineInfo, error) { + rows, err := a.db.Query(ctx, "SELECT id, host_and_port, last_contact, cpu, ram, gpu FROM harmony_machines WHERE id=$1 ORDER BY host_and_port ASC", id) + if err != nil { + return nil, err // Handle error + } + defer rows.Close() + + var summaries []machineInfo + if rows.Next() { + var m machineInfo + var lastContact time.Time + + if err := rows.Scan(&m.Info.ID, &m.Info.Host, &lastContact, &m.Info.CPU, &m.Info.Memory, &m.Info.GPU); err != nil { + return nil, err + } + + m.Info.LastContact = time.Since(lastContact).Round(time.Second).String() + + summaries = append(summaries, m) + } + + if len(summaries) == 0 { + return nil, xerrors.Errorf("machine not found") + } + + // query storage info + rows2, err := a.db.Query(ctx, "SELECT storage_id, weight, max_storage, can_seal, can_store, groups, allow_to, allow_types, deny_types, capacity, available, fs_available, reserved, used, last_heartbeat, heartbeat_err FROM storage_path WHERE urls LIKE '%' || $1 || '%'", summaries[0].Info.Host) + if err != nil { + return nil, err + } + + defer rows2.Close() + + for rows2.Next() { + var s struct { + ID string + Weight int64 + MaxStorage int64 + CanSeal bool + CanStore bool + Groups string + AllowTo string + AllowTypes string + DenyTypes string + Capacity int64 + Available int64 + FSAvailable int64 + Reserved int64 + Used int64 + LastHeartbeat time.Time + HeartbeatErr *string + + UsedPercent float64 + ReservedPercent float64 + } + if err := rows2.Scan(&s.ID, &s.Weight, &s.MaxStorage, &s.CanSeal, &s.CanStore, &s.Groups, &s.AllowTo, &s.AllowTypes, &s.DenyTypes, &s.Capacity, &s.Available, &s.FSAvailable, &s.Reserved, &s.Used, &s.LastHeartbeat, &s.HeartbeatErr); err != nil { + return nil, err + } + + s.UsedPercent = float64(s.Capacity-s.FSAvailable) * 100 / float64(s.Capacity) + s.ReservedPercent = float64(s.Capacity-(s.FSAvailable+s.Reserved))*100/float64(s.Capacity) - s.UsedPercent + + summaries[0].Storage = append(summaries[0].Storage, s) + } + + // tasks + rows3, err := a.db.Query(ctx, "SELECT id, name, posted_time FROM harmony_task WHERE owner_id=$1", summaries[0].Info.ID) + if err != nil { + return nil, err + } + + defer rows3.Close() + + for rows3.Next() { + var t struct { + ID int64 + Task string + Posted string + + PoRepSector *int64 + PoRepSectorSP *int64 + } + + var posted time.Time + if err := rows3.Scan(&t.ID, &t.Task, &posted); err != nil { + return nil, err + } + t.Posted = time.Since(posted).Round(time.Second).String() + + { + // try to find in the porep pipeline + rows4, err := a.db.Query(ctx, `SELECT sp_id, sector_number FROM sectors_sdr_pipeline + WHERE task_id_sdr=$1 + OR task_id_tree_d=$1 + OR task_id_tree_c=$1 + OR task_id_tree_r=$1 + OR task_id_precommit_msg=$1 + OR task_id_porep=$1 + OR task_id_commit_msg=$1 + OR task_id_finalize=$1 + OR task_id_move_storage=$1 + `, t.ID) + if err != nil { + return nil, err + } + + if rows4.Next() { + var spid int64 + var sector int64 + if err := rows4.Scan(&spid, §or); err != nil { + return nil, err + } + t.PoRepSector = §or + t.PoRepSectorSP = &spid + } + + rows4.Close() + } + + summaries[0].RunningTasks = append(summaries[0].RunningTasks, t) + } + + rows5, err := a.db.Query(ctx, `SELECT name, task_id, posted, work_start, work_end, result, err FROM harmony_task_history WHERE completed_by_host_and_port = $1 ORDER BY work_end DESC LIMIT 15`, summaries[0].Info.Host) + if err != nil { + return nil, err + } + defer rows5.Close() + + for rows5.Next() { + var ft struct { + ID int64 + Task string + Posted string + Start string + Queued string + Took string + Outcome string + + Message string + } + + var posted, start, end time.Time + var result bool + if err := rows5.Scan(&ft.Task, &ft.ID, &posted, &start, &end, &result, &ft.Message); err != nil { + return nil, err + } + + ft.Outcome = "Success" + if !result { + ft.Outcome = "Failed" + } + + // Format the times and durations + ft.Posted = posted.Format("02 Jan 06 15:04 MST") + ft.Start = start.Format("02 Jan 06 15:04 MST") + ft.Queued = fmt.Sprintf("%s", start.Sub(posted).Round(time.Second).String()) + ft.Took = fmt.Sprintf("%s", end.Sub(start).Round(time.Second)) + + summaries[0].FinishedTasks = append(summaries[0].FinishedTasks, ft) + } + + return &summaries[0], nil +} diff --git a/curiosrc/web/hapi/web/cluster_machines.gohtml b/curiosrc/web/hapi/web/cluster_machines.gohtml index b253a0561..5fb18b52c 100644 --- a/curiosrc/web/hapi/web/cluster_machines.gohtml +++ b/curiosrc/web/hapi/web/cluster_machines.gohtml @@ -1,7 +1,7 @@ {{define "cluster_machines"}} {{range .}} - {{.Address}} +
{{.Address}} {{.ID}} {{.SinceContact}} {{range .RecentTasks}} diff --git a/curiosrc/web/hapi/web/cluster_tasks.gohtml b/curiosrc/web/hapi/web/cluster_tasks.gohtml index 690ab8cff..b7b3faec0 100644 --- a/curiosrc/web/hapi/web/cluster_tasks.gohtml +++ b/curiosrc/web/hapi/web/cluster_tasks.gohtml @@ -4,7 +4,7 @@ {{.Name}} {{.ID}} {{.SincePosted}} - {{.Owner}} + {{if ne nil .OwnerID}}{{.Owner}}{{end}} {{end}} {{end}} diff --git a/curiosrc/web/hapi/web/node_info.gohtml b/curiosrc/web/hapi/web/node_info.gohtml new file mode 100644 index 000000000..16f60a475 --- /dev/null +++ b/curiosrc/web/hapi/web/node_info.gohtml @@ -0,0 +1,100 @@ +{{define "node_info"}} +

Info

+ + + + + + + + + + + + + + + + + + + +
HostIDLast ContactCPUMemoryGPUDebug
{{.Info.Host}}{{.Info.ID}}{{.Info.LastContact}}{{.Info.CPU}}{{toHumanBytes .Info.Memory}}{{.Info.GPU}}[pprof]
+
+

Storage

+ + + + + + + + + + {{range .Storage}} + + + + + + + + + {{end}} + +
IDTypeCapacityAvailableReserved
{{.ID}} + {{if and (not .CanSeal) (not .CanStore)}}ReadOnly{{end}} + {{if and (.CanSeal) (not .CanStore)}}Seal{{end}} + {{if and (not .CanSeal) (.CanStore)}}Store{{end}} + {{if and (.CanSeal) (.CanStore)}}Seal+Store{{end}} + {{toHumanBytes .Capacity}}{{toHumanBytes .Available}}{{toHumanBytes .Reserved}} +
+
+
+
+
+
+

Tasks

+

Running

+ + + + + + + + {{range .RunningTasks}} + + + + + + + {{end}} +
IDTaskPostedSector
{{.ID}}{{.Task}}{{.Posted}}{{if ne nil .PoRepSector}}f0{{.PoRepSectorSP}}:{{.PoRepSector}}{{end}}
+

Recently Finished

+ + + + + + + + + + + + {{range .FinishedTasks}} + + + + + + + + + + + {{end}} +
IDTaskPostedStartQueuedTookOutcomeMessage
{{.ID}}{{.Task}}{{.Posted}}{{.Start}}{{.Queued}}{{.Took}}{{.Outcome}}{{.Message}}
+{{end}} diff --git a/curiosrc/web/hapi/web/root.gohtml b/curiosrc/web/hapi/web/root.gohtml new file mode 100644 index 000000000..aa7bc586a --- /dev/null +++ b/curiosrc/web/hapi/web/root.gohtml @@ -0,0 +1,25 @@ +{{define "root"}} + + + {{.PageTitle}} + + + + + + +
+
+

{{.PageTitle}}

+
+
+ version [todo] +
+
+
+
+ {{.Content}} +
+ + +{{end}} diff --git a/curiosrc/web/srv.go b/curiosrc/web/srv.go index 2bc2cfa29..b4992dc80 100644 --- a/curiosrc/web/srv.go +++ b/curiosrc/web/srv.go @@ -41,8 +41,8 @@ func GetSrv(ctx context.Context, deps *deps.Deps) (*http.Server, error) { var static fs.FS = static if webDev { - basePath = "cmd/curio/web/static" - static = os.DirFS(basePath) + basePath = "" + static = os.DirFS("curiosrc/web/static") } mx.NotFoundHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { diff --git a/curiosrc/web/static/main.css b/curiosrc/web/static/main.css index e6612b0ac..d9f314420 100644 --- a/curiosrc/web/static/main.css +++ b/curiosrc/web/static/main.css @@ -35,16 +35,16 @@ table tr td:first-child, table tr th:first-child { border-left: none; } -a:link { - color: #cfc; +a { + text-decoration: none; } -a:visited { - color: #dfa; +a:link, a:visited { + color: #adf7ad; } a:hover { - color: #af7; + color: #88cc60; } .success { diff --git a/curiosrc/web/static/pipeline_porep.html b/curiosrc/web/static/pipeline_porep.html index ed90186de..8696bdd26 100644 --- a/curiosrc/web/static/pipeline_porep.html +++ b/curiosrc/web/static/pipeline_porep.html @@ -75,7 +75,7 @@ State - +

Frq6vOXx;#_Ma^oC_}iyo%(XK@y+6zkrKlO@@0& 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�gF0h*%7G@3<_|~ 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