From c03ad9dcfd28b33f8673d74a2f9ddf140f773bf5 Mon Sep 17 00:00:00 2001 From: Alfonso de la Rocha Date: Mon, 12 Sep 2022 19:03:06 +0200 Subject: [PATCH 001/243] wip: draft impl of consistent bcast --- chain/sub/bcast/consistent.go | 132 +++++++++++++++++++++++++++++ chain/sub/bcast/consistent_test.go | 29 +++++++ chain/sub/incoming.go | 13 +++ 3 files changed, 174 insertions(+) create mode 100644 chain/sub/bcast/consistent.go create mode 100644 chain/sub/bcast/consistent_test.go diff --git a/chain/sub/bcast/consistent.go b/chain/sub/bcast/consistent.go new file mode 100644 index 00000000000..918a6819f6f --- /dev/null +++ b/chain/sub/bcast/consistent.go @@ -0,0 +1,132 @@ +package bcast + +import ( + "context" + "encoding/binary" + "fmt" + "sync" + "time" + + "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/lotus/chain/types" + "github.com/ipfs/go-cid" + "github.com/multiformats/go-multihash" +) + +// TODO: Take const out of here and make them build params. +const ( + DELAY = 6 * time.Second + GC_SANITY_CHECK = 5 + GC_LOOKBACK = 2 +) + +type blksInfo struct { + ctx context.Context + cancel context.CancelFunc + blks []cid.Cid +} + +type bcastDict struct { + // TODO: Consider making this a KeyMutexed map + lk sync.RWMutex + blks map[cid.Cid]*blksInfo // map[epoch + VRFProof]blksInfo +} + +type ConsistentBCast struct { + lk sync.Mutex + delay time.Duration + // FIXME: Make this a slice??? Less storage but needs indexing logic. + m map[abi.ChainEpoch]*bcastDict +} + +func newBcastDict(delay time.Duration) *bcastDict { + return &bcastDict{ + blks: make(map[cid.Cid]*blksInfo), + } +} + +// TODO: What if the VRFProof is already small?? We don´t need the CID. Useless computation. +func BCastKey(bh *types.BlockHeader) cid.Cid { + proof := bh.Ticket.VRFProof + binary.PutVarint(proof, int64(bh.Height)) + return cid.NewCidV0(multihash.Multihash(proof)) +} + +func NewConsistentBCast(delay time.Duration) *ConsistentBCast { + return &ConsistentBCast{ + delay: delay, + m: make(map[abi.ChainEpoch]*bcastDict), + } +} + +func cidExists(cids []cid.Cid, c cid.Cid) bool { + for _, v := range cids { + if v == c { + return true + } + } + return false +} + +func (bInfo *blksInfo) eqErr() error { + bInfo.cancel() + return fmt.Errorf("equivocation error detected. Different block with the same ticket already seen") +} + +func (cb *ConsistentBCast) RcvBlock(ctx context.Context, blk *types.BlockMsg) error { + cb.lk.Lock() + bcastDict, ok := cb.m[blk.Header.Height] + if !ok { + bcastDict = newBcastDict(cb.delay) + } + cb.lk.Unlock() + key := BCastKey(blk.Header) + blkCid := blk.Cid() + + bcastDict.lk.Lock() + defer bcastDict.lk.Unlock() + bInfo, ok := bcastDict.blks[key] + if ok { + if len(bInfo.blks) > 1 { + return bInfo.eqErr() + } + + if !cidExists(bInfo.blks, blkCid) { + bInfo.blks = append(bInfo.blks, blkCid) + return bInfo.eqErr() + } + return nil + } + + ctx, cancel := context.WithTimeout(ctx, cb.delay) + bcastDict.blks[key] = &blksInfo{ctx, cancel, []cid.Cid{blkCid}} + return nil +} + +func (cb *ConsistentBCast) WaitForDelivery(bh *types.BlockHeader) error { + bcastDict := cb.m[bh.Height] + key := BCastKey(bh) + bcastDict.lk.RLock() + defer bcastDict.lk.RUnlock() + bInfo, ok := bcastDict.blks[key] + if !ok { + return fmt.Errorf("something went wrong, unknown block with Epoch + VRFProof (cid=%s) in consistent broadcast storage", key) + } + // Wait for the timeout + <-bInfo.ctx.Done() + if len(bInfo.blks) > 1 { + return fmt.Errorf("equivocation detected for epoch %d. Two blocks being broadcast with same VRFProof", bh.Height) + } + return nil +} + +func (cb *ConsistentBCast) GarbageCollect(currEpoch abi.ChainEpoch) { + cb.lk.Lock() + defer cb.lk.Unlock() + + // keep currEpoch-2 and delete a few more in the past + // as a sanity-check + for i := 0; i < GC_SANITY_CHECK; i++ { + delete(cb.m, currEpoch-abi.ChainEpoch(2-i)) + } +} diff --git a/chain/sub/bcast/consistent_test.go b/chain/sub/bcast/consistent_test.go new file mode 100644 index 00000000000..2f3a9e4deae --- /dev/null +++ b/chain/sub/bcast/consistent_test.go @@ -0,0 +1,29 @@ +package bcast_test + +import ( + "crypto/rand" + "testing" + + "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/lotus/chain/types" +) + +func TestSimpleDelivery(t *testing.T) { +} + +func newBlock(t *testing.T, epoch abi.ChainEpoch) *types.BlockMsg { + proof := make([]byte, 10) + _, err := rand.Read(proof) + if err != err { + t.Fatal(err) + } + bh := &types.BlockHeader{ + Ticket: &types.Ticket{ + VRFProof: []byte("vrf proof0000000vrf proof0000000"), + }, + Height: 85919298723, + } + return &types.BlockMsg{ + Header: bh, + } +} diff --git a/chain/sub/incoming.go b/chain/sub/incoming.go index b8427e0368b..e03241c237f 100644 --- a/chain/sub/incoming.go +++ b/chain/sub/incoming.go @@ -27,6 +27,7 @@ import ( "github.com/filecoin-project/lotus/chain/consensus" "github.com/filecoin-project/lotus/chain/messagepool" "github.com/filecoin-project/lotus/chain/store" + "github.com/filecoin-project/lotus/chain/sub/bcast" "github.com/filecoin-project/lotus/chain/sub/ratelimit" "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/metrics" @@ -47,6 +48,7 @@ func HandleIncomingBlocks(ctx context.Context, bsub *pubsub.Subscription, s *cha // Timeout after (block time + propagation delay). This is useless at // this point. timeout := time.Duration(build.BlockDelaySecs+build.PropagationDelaySecs) * time.Second + cb := bcast.NewConsistentBCast(bcast.DELAY) for { msg, err := bsub.Next(ctx) @@ -67,6 +69,9 @@ func HandleIncomingBlocks(ctx context.Context, bsub *pubsub.Subscription, s *cha src := msg.GetFrom() + // Notify consistent broadcast about a new block + cb.RcvBlock(ctx, blk) + go func() { ctx, cancel := context.WithTimeout(ctx, timeout) defer cancel() @@ -102,6 +107,14 @@ func HandleIncomingBlocks(ctx context.Context, bsub *pubsub.Subscription, s *cha log.Warnw("received block with large delay from miner", "block", blk.Cid(), "delay", delay, "miner", blk.Header.Miner) } + if err := cb.WaitForDelivery(blk.Header); err != nil { + log.Errorf("couldn't deliver block to syncer over pubsub: %s; source: %s", err, src) + return + } + + // Garbage collect the broadcast state + cb.GarbageCollect(blk.Header.Height) + if s.InformNewBlock(msg.ReceivedFrom, &types.FullBlock{ Header: blk.Header, BlsMessages: bmsgs, From 76031d72ad955af9f4a04a74b606e959a02f2c77 Mon Sep 17 00:00:00 2001 From: Alfonso de la Rocha Date: Tue, 13 Sep 2022 12:33:28 +0200 Subject: [PATCH 002/243] minor fixes. unit tests added --- chain/sub/bcast/consistent.go | 55 +++++--- chain/sub/bcast/consistent_test.go | 198 ++++++++++++++++++++++++++++- 2 files changed, 231 insertions(+), 22 deletions(-) diff --git a/chain/sub/bcast/consistent.go b/chain/sub/bcast/consistent.go index 918a6819f6f..af31389eeff 100644 --- a/chain/sub/bcast/consistent.go +++ b/chain/sub/bcast/consistent.go @@ -27,9 +27,23 @@ type blksInfo struct { } type bcastDict struct { - // TODO: Consider making this a KeyMutexed map - lk sync.RWMutex - blks map[cid.Cid]*blksInfo // map[epoch + VRFProof]blksInfo + // thread-safe map impl for the dictionary + // sync.Map accepts `any` as keys and values. + // To make it type safe and only support the right + // types we use this auxiliary type. + m *sync.Map +} + +func (bd *bcastDict) load(key multihash.Multihash) (*blksInfo, bool) { + v, ok := bd.m.Load(key.String()) + if !ok { + return nil, ok + } + return v.(*blksInfo), ok +} + +func (bd *bcastDict) store(key multihash.Multihash, d *blksInfo) { + bd.m.Store(key.String(), d) } type ConsistentBCast struct { @@ -40,16 +54,14 @@ type ConsistentBCast struct { } func newBcastDict(delay time.Duration) *bcastDict { - return &bcastDict{ - blks: make(map[cid.Cid]*blksInfo), - } + return &bcastDict{new(sync.Map)} } // TODO: What if the VRFProof is already small?? We don´t need the CID. Useless computation. -func BCastKey(bh *types.BlockHeader) cid.Cid { +func BCastKey(bh *types.BlockHeader) (multihash.Multihash, error) { proof := bh.Ticket.VRFProof binary.PutVarint(proof, int64(bh.Height)) - return cid.NewCidV0(multihash.Multihash(proof)) + return multihash.Sum(proof, multihash.SHA2_256, -1) } func NewConsistentBCast(delay time.Duration) *ConsistentBCast { @@ -78,14 +90,16 @@ func (cb *ConsistentBCast) RcvBlock(ctx context.Context, blk *types.BlockMsg) er bcastDict, ok := cb.m[blk.Header.Height] if !ok { bcastDict = newBcastDict(cb.delay) + cb.m[blk.Header.Height] = bcastDict } cb.lk.Unlock() - key := BCastKey(blk.Header) + key, err := BCastKey(blk.Header) + if err != nil { + return err + } blkCid := blk.Cid() - bcastDict.lk.Lock() - defer bcastDict.lk.Unlock() - bInfo, ok := bcastDict.blks[key] + bInfo, ok := bcastDict.load(key) if ok { if len(bInfo.blks) > 1 { return bInfo.eqErr() @@ -98,17 +112,18 @@ func (cb *ConsistentBCast) RcvBlock(ctx context.Context, blk *types.BlockMsg) er return nil } - ctx, cancel := context.WithTimeout(ctx, cb.delay) - bcastDict.blks[key] = &blksInfo{ctx, cancel, []cid.Cid{blkCid}} + ctx, cancel := context.WithTimeout(ctx, cb.delay*time.Second) + bcastDict.store(key, &blksInfo{ctx, cancel, []cid.Cid{blkCid}}) return nil } func (cb *ConsistentBCast) WaitForDelivery(bh *types.BlockHeader) error { bcastDict := cb.m[bh.Height] - key := BCastKey(bh) - bcastDict.lk.RLock() - defer bcastDict.lk.RUnlock() - bInfo, ok := bcastDict.blks[key] + key, err := BCastKey(bh) + if err != nil { + return err + } + bInfo, ok := bcastDict.load(key) if !ok { return fmt.Errorf("something went wrong, unknown block with Epoch + VRFProof (cid=%s) in consistent broadcast storage", key) } @@ -126,6 +141,10 @@ func (cb *ConsistentBCast) GarbageCollect(currEpoch abi.ChainEpoch) { // keep currEpoch-2 and delete a few more in the past // as a sanity-check + // Garbage collection is triggered before block delivery, + // and we use the sanity-check in case there were a few rounds + // without delivery, and the garbage collection wasn't triggered + // for a few epochs. for i := 0; i < GC_SANITY_CHECK; i++ { delete(cb.m, currEpoch-abi.ChainEpoch(2-i)) } diff --git a/chain/sub/bcast/consistent_test.go b/chain/sub/bcast/consistent_test.go index 2f3a9e4deae..a2945b46b66 100644 --- a/chain/sub/bcast/consistent_test.go +++ b/chain/sub/bcast/consistent_test.go @@ -1,27 +1,217 @@ package bcast_test import ( + "context" "crypto/rand" + "fmt" + mrand "math/rand" + "strconv" + "sync" "testing" + "time" + "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/lotus/chain/sub/bcast" "github.com/filecoin-project/lotus/chain/types" + "github.com/ipfs/go-cid" + "github.com/multiformats/go-multihash" + "github.com/stretchr/testify/require" ) +const TEST_DELAY = 1 + func TestSimpleDelivery(t *testing.T) { + cb := bcast.NewConsistentBCast(TEST_DELAY) + // Check that we wait for delivery. + start := time.Now() + testSimpleDelivery(t, cb, 100, 5) + since := time.Since(start) + require.GreaterOrEqual(t, since, TEST_DELAY*time.Second) } -func newBlock(t *testing.T, epoch abi.ChainEpoch) *types.BlockMsg { +func testSimpleDelivery(t *testing.T, cb *bcast.ConsistentBCast, epoch abi.ChainEpoch, numBlocks int) { + ctx := context.Background() + + wg := new(sync.WaitGroup) + errs := make([]error, 0) + wg.Add(numBlocks) + for i := 0; i < numBlocks; i++ { + go func(i int) { + // Add a random delay in block reception + r := mrand.Intn(200) + time.Sleep(time.Duration(r) * time.Millisecond) + blk := newBlock(t, epoch, randomProof(t), []byte("test"+strconv.Itoa(i))) + cb.RcvBlock(ctx, blk) + err := cb.WaitForDelivery(blk.Header) + if err != nil { + errs = append(errs, err) + } + wg.Done() + }(i) + } + wg.Wait() + + for _, v := range errs { + t.Fatalf("error in delivery: %s", v) + } +} + +func TestSeveralEpochs(t *testing.T) { + cb := bcast.NewConsistentBCast(TEST_DELAY) + numEpochs := 5 + wg := new(sync.WaitGroup) + wg.Add(numEpochs) + for i := 0; i < numEpochs; i++ { + go func(i int) { + // Add a random delay between epochs + r := mrand.Intn(500) + time.Sleep(time.Duration(i*TEST_DELAY)*time.Second + time.Duration(r)*time.Millisecond) + rNumBlocks := mrand.Intn(5) + flip, err := flipCoin(0.7) + require.NoError(t, err) + t.Logf("Running epoch %d with %d with equivocation=%v", i, rNumBlocks, !flip) + if flip { + testSimpleDelivery(t, cb, abi.ChainEpoch(i), rNumBlocks) + } else { + testEquivocation(t, cb, abi.ChainEpoch(i), rNumBlocks) + } + wg.Done() + }(i) + } + wg.Wait() +} + +// bias is expected to be 0-1 +func flipCoin(bias float32) (bool, error) { + if bias > 1 || bias < 0 { + return false, fmt.Errorf("wrong bias. expected (0,1)") + } + r := mrand.Intn(100) + return r < int(bias*100), nil +} + +func testEquivocation(t *testing.T, cb *bcast.ConsistentBCast, epoch abi.ChainEpoch, numBlocks int) { + ctx := context.Background() + + wg := new(sync.WaitGroup) + errs := make([]error, 0) + wg.Add(numBlocks + 1) + for i := 0; i < numBlocks; i++ { + proof := randomProof(t) + // Valid blocks + go func(i int, proof []byte) { + r := mrand.Intn(200) + time.Sleep(time.Duration(r) * time.Millisecond) + blk := newBlock(t, 100, proof, []byte("valid"+strconv.Itoa(i))) + cb.RcvBlock(ctx, blk) + err := cb.WaitForDelivery(blk.Header) + if err != nil { + errs = append(errs, err) + } + wg.Done() + }(i, proof) + + // Equivocation for the last block + if i == numBlocks-1 { + // Attempting equivocation + go func(i int, proof []byte) { + // Use the same proof and the same epoch + blk := newBlock(t, 100, proof, []byte("invalid"+strconv.Itoa(i))) + cb.RcvBlock(ctx, blk) + err := cb.WaitForDelivery(blk.Header) + // Equivocation detected + require.Error(t, err) + wg.Done() + }(i, proof) + } + } + wg.Wait() + + // The equivocated block arrived too late, so + // we delivered all the valid blocks. + require.Len(t, errs, 1) +} + +func TestEquivocation(t *testing.T) { + cb := bcast.NewConsistentBCast(TEST_DELAY) + testEquivocation(t, cb, 100, 5) +} + +func TestFailedEquivocation(t *testing.T) { + cb := bcast.NewConsistentBCast(TEST_DELAY) + ctx := context.Background() + numBlocks := 5 + + wg := new(sync.WaitGroup) + errs := make([]error, 0) + wg.Add(numBlocks + 1) + for i := 0; i < numBlocks; i++ { + proof := randomProof(t) + // Valid blocks + go func(i int, proof []byte) { + r := mrand.Intn(200) + time.Sleep(time.Duration(r) * time.Millisecond) + blk := newBlock(t, 100, proof, []byte("valid"+strconv.Itoa(i))) + cb.RcvBlock(ctx, blk) + err := cb.WaitForDelivery(blk.Header) + if err != nil { + errs = append(errs, err) + } + wg.Done() + }(i, proof) + + // Equivocation for the last block + if i == numBlocks-1 { + // Attempting equivocation + go func(i int, proof []byte) { + // The equivocated block arrives late + time.Sleep(2 * TEST_DELAY * time.Second) + // Use the same proof and the same epoch + blk := newBlock(t, 100, proof, []byte("invalid"+strconv.Itoa(i))) + cb.RcvBlock(ctx, blk) + err := cb.WaitForDelivery(blk.Header) + // Equivocation detected + require.Error(t, err) + wg.Done() + }(i, proof) + } + } + wg.Wait() + + // The equivocated block arrived too late, so + // we delivered all the valid blocks. + require.Len(t, errs, 0) +} + +func randomProof(t *testing.T) []byte { proof := make([]byte, 10) _, err := rand.Read(proof) - if err != err { + if err != nil { + t.Fatal(err) + } + return proof +} + +func newBlock(t *testing.T, epoch abi.ChainEpoch, proof []byte, mCidSeed []byte) *types.BlockMsg { + h, err := multihash.Sum(mCidSeed, multihash.SHA2_256, -1) + if err != nil { + t.Fatal(err) + } + testCid := cid.NewCidV0(h) + addr, err := address.NewIDAddress(10) + if err != nil { t.Fatal(err) } bh := &types.BlockHeader{ + Miner: addr, + ParentStateRoot: testCid, + ParentMessageReceipts: testCid, Ticket: &types.Ticket{ - VRFProof: []byte("vrf proof0000000vrf proof0000000"), + VRFProof: proof, }, - Height: 85919298723, + Height: epoch, + Messages: testCid, } return &types.BlockMsg{ Header: bh, From 1dadff303c2138d9fd9a467acf41a60cb080641b Mon Sep 17 00:00:00 2001 From: Alfonso de la Rocha Date: Tue, 13 Sep 2022 16:06:18 +0200 Subject: [PATCH 003/243] added garbage collection test. minor fix --- chain/sub/bcast/consistent.go | 8 +++++++- chain/sub/bcast/consistent_test.go | 18 ++++++++++-------- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/chain/sub/bcast/consistent.go b/chain/sub/bcast/consistent.go index af31389eeff..3ee1997e838 100644 --- a/chain/sub/bcast/consistent.go +++ b/chain/sub/bcast/consistent.go @@ -85,6 +85,10 @@ func (bInfo *blksInfo) eqErr() error { return fmt.Errorf("equivocation error detected. Different block with the same ticket already seen") } +func (cb *ConsistentBCast) Len() int { + return len(cb.m) +} + func (cb *ConsistentBCast) RcvBlock(ctx context.Context, blk *types.BlockMsg) error { cb.lk.Lock() bcastDict, ok := cb.m[blk.Header.Height] @@ -146,6 +150,8 @@ func (cb *ConsistentBCast) GarbageCollect(currEpoch abi.ChainEpoch) { // without delivery, and the garbage collection wasn't triggered // for a few epochs. for i := 0; i < GC_SANITY_CHECK; i++ { - delete(cb.m, currEpoch-abi.ChainEpoch(2-i)) + if currEpoch > GC_LOOKBACK { + delete(cb.m, currEpoch-abi.ChainEpoch(GC_LOOKBACK+i)) + } } } diff --git a/chain/sub/bcast/consistent_test.go b/chain/sub/bcast/consistent_test.go index a2945b46b66..1b1f4f34aa9 100644 --- a/chain/sub/bcast/consistent_test.go +++ b/chain/sub/bcast/consistent_test.go @@ -38,6 +38,7 @@ func testSimpleDelivery(t *testing.T, cb *bcast.ConsistentBCast, epoch abi.Chain wg.Add(numBlocks) for i := 0; i < numBlocks; i++ { go func(i int) { + defer wg.Done() // Add a random delay in block reception r := mrand.Intn(200) time.Sleep(time.Duration(r) * time.Millisecond) @@ -47,7 +48,6 @@ func testSimpleDelivery(t *testing.T, cb *bcast.ConsistentBCast, epoch abi.Chain if err != nil { errs = append(errs, err) } - wg.Done() }(i) } wg.Wait() @@ -64,6 +64,7 @@ func TestSeveralEpochs(t *testing.T) { wg.Add(numEpochs) for i := 0; i < numEpochs; i++ { go func(i int) { + defer wg.Done() // Add a random delay between epochs r := mrand.Intn(500) time.Sleep(time.Duration(i*TEST_DELAY)*time.Second + time.Duration(r)*time.Millisecond) @@ -76,10 +77,11 @@ func TestSeveralEpochs(t *testing.T) { } else { testEquivocation(t, cb, abi.ChainEpoch(i), rNumBlocks) } - wg.Done() + cb.GarbageCollect(abi.ChainEpoch(i)) }(i) } wg.Wait() + require.Equal(t, cb.Len(), bcast.GC_LOOKBACK) } // bias is expected to be 0-1 @@ -101,28 +103,28 @@ func testEquivocation(t *testing.T, cb *bcast.ConsistentBCast, epoch abi.ChainEp proof := randomProof(t) // Valid blocks go func(i int, proof []byte) { + defer wg.Done() r := mrand.Intn(200) time.Sleep(time.Duration(r) * time.Millisecond) - blk := newBlock(t, 100, proof, []byte("valid"+strconv.Itoa(i))) + blk := newBlock(t, epoch, proof, []byte("valid"+strconv.Itoa(i))) cb.RcvBlock(ctx, blk) err := cb.WaitForDelivery(blk.Header) if err != nil { errs = append(errs, err) } - wg.Done() }(i, proof) // Equivocation for the last block if i == numBlocks-1 { // Attempting equivocation go func(i int, proof []byte) { + defer wg.Done() // Use the same proof and the same epoch - blk := newBlock(t, 100, proof, []byte("invalid"+strconv.Itoa(i))) + blk := newBlock(t, epoch, proof, []byte("invalid"+strconv.Itoa(i))) cb.RcvBlock(ctx, blk) err := cb.WaitForDelivery(blk.Header) // Equivocation detected require.Error(t, err) - wg.Done() }(i, proof) } } @@ -150,6 +152,7 @@ func TestFailedEquivocation(t *testing.T) { proof := randomProof(t) // Valid blocks go func(i int, proof []byte) { + defer wg.Done() r := mrand.Intn(200) time.Sleep(time.Duration(r) * time.Millisecond) blk := newBlock(t, 100, proof, []byte("valid"+strconv.Itoa(i))) @@ -158,13 +161,13 @@ func TestFailedEquivocation(t *testing.T) { if err != nil { errs = append(errs, err) } - wg.Done() }(i, proof) // Equivocation for the last block if i == numBlocks-1 { // Attempting equivocation go func(i int, proof []byte) { + defer wg.Done() // The equivocated block arrives late time.Sleep(2 * TEST_DELAY * time.Second) // Use the same proof and the same epoch @@ -173,7 +176,6 @@ func TestFailedEquivocation(t *testing.T) { err := cb.WaitForDelivery(blk.Header) // Equivocation detected require.Error(t, err) - wg.Done() }(i, proof) } } From d1a4f1dc50bb88ada311ba0b06f69a74703ab624 Mon Sep 17 00:00:00 2001 From: Alfonso de la Rocha Date: Wed, 14 Sep 2022 19:59:29 +0200 Subject: [PATCH 004/243] fixed bugs in consistent broadcast integration --- chain/sub/bcast/consistent.go | 13 +++++++------ chain/sub/incoming.go | 16 +++++++++++----- node/modules/services.go | 2 +- 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/chain/sub/bcast/consistent.go b/chain/sub/bcast/consistent.go index 3ee1997e838..5b6079cf5c1 100644 --- a/chain/sub/bcast/consistent.go +++ b/chain/sub/bcast/consistent.go @@ -53,15 +53,16 @@ type ConsistentBCast struct { m map[abi.ChainEpoch]*bcastDict } -func newBcastDict(delay time.Duration) *bcastDict { +func newBcastDict() *bcastDict { return &bcastDict{new(sync.Map)} } // TODO: What if the VRFProof is already small?? We don´t need the CID. Useless computation. func BCastKey(bh *types.BlockHeader) (multihash.Multihash, error) { - proof := bh.Ticket.VRFProof - binary.PutVarint(proof, int64(bh.Height)) - return multihash.Sum(proof, multihash.SHA2_256, -1) + k := make([]byte, len(bh.Ticket.VRFProof)) + copy(k, bh.Ticket.VRFProof) + binary.PutVarint(k, int64(bh.Height)) + return multihash.Sum(k, multihash.SHA2_256, -1) } func NewConsistentBCast(delay time.Duration) *ConsistentBCast { @@ -93,7 +94,7 @@ func (cb *ConsistentBCast) RcvBlock(ctx context.Context, blk *types.BlockMsg) er cb.lk.Lock() bcastDict, ok := cb.m[blk.Header.Height] if !ok { - bcastDict = newBcastDict(cb.delay) + bcastDict = newBcastDict() cb.m[blk.Header.Height] = bcastDict } cb.lk.Unlock() @@ -116,7 +117,7 @@ func (cb *ConsistentBCast) RcvBlock(ctx context.Context, blk *types.BlockMsg) er return nil } - ctx, cancel := context.WithTimeout(ctx, cb.delay*time.Second) + ctx, cancel := context.WithTimeout(ctx, cb.delay) bcastDict.store(key, &blksInfo{ctx, cancel, []cid.Cid{blkCid}}) return nil } diff --git a/chain/sub/incoming.go b/chain/sub/incoming.go index e03241c237f..6e7cf75ff03 100644 --- a/chain/sub/incoming.go +++ b/chain/sub/incoming.go @@ -44,7 +44,7 @@ var msgCidPrefix = cid.Prefix{ MhLength: 32, } -func HandleIncomingBlocks(ctx context.Context, bsub *pubsub.Subscription, s *chain.Syncer, bs bserv.BlockService, cmgr connmgr.ConnManager) { +func HandleIncomingBlocks(ctx context.Context, bsub *pubsub.Subscription, self peer.ID, s *chain.Syncer, bs bserv.BlockService, cmgr connmgr.ConnManager) { // Timeout after (block time + propagation delay). This is useless at // this point. timeout := time.Duration(build.BlockDelaySecs+build.PropagationDelaySecs) * time.Second @@ -107,13 +107,19 @@ func HandleIncomingBlocks(ctx context.Context, bsub *pubsub.Subscription, s *cha log.Warnw("received block with large delay from miner", "block", blk.Cid(), "delay", delay, "miner", blk.Header.Miner) } - if err := cb.WaitForDelivery(blk.Header); err != nil { - log.Errorf("couldn't deliver block to syncer over pubsub: %s; source: %s", err, src) - return + // When we propose a new block ourselves, the proposed block also gets here through SyncSubmitBlock. + // If we are the block proposers we don't need to wait for delivery, we know the blocks are + // honest. + if src != self { + log.Infof("Waiting for consistent broadcast of block in height: %v", blk.Header.Height) + if err := cb.WaitForDelivery(blk.Header); err != nil { + log.Errorf("couldn't deliver block to syncer over pubsub: %s; source: %s", err, src) + return + } } - // Garbage collect the broadcast state cb.GarbageCollect(blk.Header.Height) + log.Infof("Block in height %v delivered successfully", blk.Header.Height) if s.InformNewBlock(msg.ReceivedFrom, &types.FullBlock{ Header: blk.Header, diff --git a/node/modules/services.go b/node/modules/services.go index 18c0116aae6..36fcf189ede 100644 --- a/node/modules/services.go +++ b/node/modules/services.go @@ -166,7 +166,7 @@ func HandleIncomingBlocks(mctx helpers.MetricsCtx, panic(err) } - go sub.HandleIncomingBlocks(ctx, blocksub, s, bserv, h.ConnManager()) + go sub.HandleIncomingBlocks(ctx, blocksub, h.ID(), s, bserv, h.ConnManager()) } func HandleIncomingMessages(mctx helpers.MetricsCtx, lc fx.Lifecycle, ps *pubsub.PubSub, stmgr *stmgr.StateManager, mpool *messagepool.MessagePool, h host.Host, nn dtypes.NetworkName, bootstrapper dtypes.Bootstrapper) { From 72c80a3461bee4479d260f41a66fd3ad312d9d0e Mon Sep 17 00:00:00 2001 From: Alfonso de la Rocha Date: Fri, 16 Sep 2022 09:22:37 +0200 Subject: [PATCH 005/243] fixed unit tests --- chain/sub/bcast/consistent_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/chain/sub/bcast/consistent_test.go b/chain/sub/bcast/consistent_test.go index 1b1f4f34aa9..5dc2b198c20 100644 --- a/chain/sub/bcast/consistent_test.go +++ b/chain/sub/bcast/consistent_test.go @@ -19,7 +19,7 @@ import ( "github.com/stretchr/testify/require" ) -const TEST_DELAY = 1 +const TEST_DELAY = 1 * time.Second func TestSimpleDelivery(t *testing.T) { cb := bcast.NewConsistentBCast(TEST_DELAY) @@ -27,7 +27,7 @@ func TestSimpleDelivery(t *testing.T) { start := time.Now() testSimpleDelivery(t, cb, 100, 5) since := time.Since(start) - require.GreaterOrEqual(t, since, TEST_DELAY*time.Second) + require.GreaterOrEqual(t, since, TEST_DELAY) } func testSimpleDelivery(t *testing.T, cb *bcast.ConsistentBCast, epoch abi.ChainEpoch, numBlocks int) { @@ -67,7 +67,7 @@ func TestSeveralEpochs(t *testing.T) { defer wg.Done() // Add a random delay between epochs r := mrand.Intn(500) - time.Sleep(time.Duration(i*TEST_DELAY)*time.Second + time.Duration(r)*time.Millisecond) + time.Sleep(time.Duration(i)*TEST_DELAY + time.Duration(r)*time.Millisecond) rNumBlocks := mrand.Intn(5) flip, err := flipCoin(0.7) require.NoError(t, err) @@ -169,7 +169,7 @@ func TestFailedEquivocation(t *testing.T) { go func(i int, proof []byte) { defer wg.Done() // The equivocated block arrives late - time.Sleep(2 * TEST_DELAY * time.Second) + time.Sleep(2 * TEST_DELAY) // Use the same proof and the same epoch blk := newBlock(t, 100, proof, []byte("invalid"+strconv.Itoa(i))) cb.RcvBlock(ctx, blk) From 0209052821620a100099cae81eea303a771ddb0d Mon Sep 17 00:00:00 2001 From: Alfonso de la Rocha Date: Fri, 16 Sep 2022 13:09:27 +0200 Subject: [PATCH 006/243] minor changes --- chain/sub/incoming.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chain/sub/incoming.go b/chain/sub/incoming.go index 6e7cf75ff03..02358068d08 100644 --- a/chain/sub/incoming.go +++ b/chain/sub/incoming.go @@ -119,7 +119,7 @@ func HandleIncomingBlocks(ctx context.Context, bsub *pubsub.Subscription, self p } // Garbage collect the broadcast state cb.GarbageCollect(blk.Header.Height) - log.Infof("Block in height %v delivered successfully", blk.Header.Height) + log.Infof("Block in height %v delivered successfully (cid=)", blk.Header.Height, blk.Cid()) if s.InformNewBlock(msg.ReceivedFrom, &types.FullBlock{ Header: blk.Header, From 749d13359f330d0c5f62db9315de314d428dc1c2 Mon Sep 17 00:00:00 2001 From: Alfonso de la Rocha Date: Mon, 19 Sep 2022 09:11:30 +0200 Subject: [PATCH 007/243] added testground test case for epoch boundary attack --- .../_compositions/epoch_boundary.toml | 71 +++++++++++++++++++ testplans/lotus-soup/epoch_boundary.go | 43 +++++++++++ testplans/lotus-soup/manifest.toml | 38 +++++++++- 3 files changed, 150 insertions(+), 2 deletions(-) create mode 100644 testplans/lotus-soup/_compositions/epoch_boundary.toml create mode 100644 testplans/lotus-soup/epoch_boundary.go diff --git a/testplans/lotus-soup/_compositions/epoch_boundary.toml b/testplans/lotus-soup/_compositions/epoch_boundary.toml new file mode 100644 index 00000000000..70bdb795d02 --- /dev/null +++ b/testplans/lotus-soup/_compositions/epoch_boundary.toml @@ -0,0 +1,71 @@ +[metadata] + name = "lotus-soup" + author = "" + +[global] + plan = "lotus-soup" + case = "epoch-boundary" + total_instances = 4 + builder = "exec:go" + runner = "local:exec" + +[global.build] + selectors = ["testground"] + +[global.run_config] + exposed_ports = { pprof = "6060", node_rpc = "1234", miner_rpc = "2345" } + +[global.build_config] + enable_go_build_cache = true + +[global.run.test_params] + clients = "0" + miners = "3" + genesis_timestamp_offset = "0" + balance = "20000000.5" # These balances will work for maximum 100 nodes, as TotalFilecoin is 2B + sectors = "10" + random_beacon_type = "mock" + mining_mode = "natural" + +[[groups]] + id = "bootstrapper" + [groups.instances] + count = 1 + percentage = 0.0 + [groups.run] + [groups.run.test_params] + role = "bootstrapper" + +[[groups]] + id = "miners" + [groups.instances] + count = 2 + percentage = 0.0 + [groups.run] + [groups.run.test_params] + role = "miner" + +[[groups]] + id = "attacker" + [groups.build] + + # TODO: Configure different versions for the different nodes. + # [[groups.build.dependencies]] + # module = "github.com/filecoin-project/lotus" + # version = "master" + + [groups.instances] + count = 1 + percentage = 0.0 + [groups.run] + [groups.run.test_params] + role = "miner" + +# [[groups]] +# id = "clients" +# [groups.instances] +# count = 0 +# percentage = 0.0 +# [groups.run] +# [groups.run.test_params] +# role = "client" diff --git a/testplans/lotus-soup/epoch_boundary.go b/testplans/lotus-soup/epoch_boundary.go new file mode 100644 index 00000000000..9f30627abec --- /dev/null +++ b/testplans/lotus-soup/epoch_boundary.go @@ -0,0 +1,43 @@ +package main + +import ( + "context" + "time" + + "github.com/filecoin-project/lotus/testplans/lotus-soup/testkit" +) + +// This test runs a set of miners and let them mine for some time. +// Each miner tracks the different blocks they are mining so we can +// process a posteriori the different chains they are mining. +// TODO: Include the attacker. +func epochBoundary(t *testkit.TestEnvironment) error { + t.RecordMessage("running node with role '%s'", t.Role) + + ctx := context.Background() + // Dispatch/forward non-client roles to defaults. + if t.Role != "miner" { + return testkit.HandleDefaultRole(t) + } + m, err := testkit.PrepareMiner(t) + if err != nil { + return err + } + go func() { + miner := m.FullApi + ch, _ := miner.ChainNotify(ctx) + for { + curr := <-ch + // We collect new blocks seen by the node along with its cid. + // We can process the results a posteriori to determine the number of equivocations. + t.RecordMessage("New Block: height=%v, cid=%v", curr[0].Val.Height(), curr[0].Val.Cids()) + } + }() + err = m.RunDefault() + if err != nil { + return err + } + time.Sleep(120 * time.Second) + t.SyncClient.MustSignalAndWait(ctx, testkit.StateDone, t.TestInstanceCount) + return nil +} diff --git a/testplans/lotus-soup/manifest.toml b/testplans/lotus-soup/manifest.toml index 9f5a574440b..3162b53720d 100644 --- a/testplans/lotus-soup/manifest.toml +++ b/testplans/lotus-soup/manifest.toml @@ -9,8 +9,8 @@ enabled = true [builders."docker:go"] enabled = true -build_base_image = "iptestground/oni-buildbase:v15-lotus" -runtime_image = "iptestground/oni-runtime:v10-debug" +# build_base_image = "iptestground/oni-buildbase:v15-lotus" +# runtime_image = "iptestground/oni-runtime:v10-debug" [runners."local:exec"] enabled = true @@ -61,6 +61,40 @@ instances = { min = 1, max = 100, default = 5 } # Bounce connection during push and pull data transfers bounce_conn_data_transfers = { type = "bool", default = false } +[[testcases]] +name = "epoch-boundary" +instances = { min = 1, max = 100, default = 5 } + + [testcases.params] + clients = { type = "int", default = 1 } + miners = { type = "int", default = 1 } + attackers = { type = "int", default = 1 } + balance = { type = "float", default = 1 } + sectors = { type = "int", default = 1 } + role = { type = "string" } + + genesis_timestamp_offset = { type = "int", default = 0 } + + random_beacon_type = { type = "enum", default = "mock", options = ["mock", "local-drand", "external-drand"] } + + # Params relevant to drand nodes. drand nodes should have role="drand", and must all be + # in the same composition group. There must be at least threshold drand nodes. + # To get lotus nodes to actually use the drand nodes, you must set random_beacon_type="local-drand" + # for the lotus node groups. + drand_period = { type = "duration", default="10s" } + drand_threshold = { type = "int", default = 2 } + drand_gossip_relay = { type = "bool", default = true } + drand_log_level = { type = "string", default="info" } + + # Params relevant to pubsub tracing + enable_pubsub_tracer = { type = "bool", default = false } + mining_mode = { type = "enum", default = "synchronized", options = ["synchronized", "natural"] } + + # Fast retrieval + fast_retrieval = { type = "bool", default = false } + + # Bounce connection during push and pull data transfers + bounce_conn_data_transfers = { type = "bool", default = false } [[testcases]] name = "drand-halting" From 8f48631fc48773821fd853b6d9612380b9d74cb4 Mon Sep 17 00:00:00 2001 From: Alfonso de la Rocha Date: Mon, 19 Sep 2022 09:12:13 +0200 Subject: [PATCH 008/243] index new testcase --- testplans/lotus-soup/main.go | 1 + 1 file changed, 1 insertion(+) diff --git a/testplans/lotus-soup/main.go b/testplans/lotus-soup/main.go index b1d17f01878..406dffdd2b4 100644 --- a/testplans/lotus-soup/main.go +++ b/testplans/lotus-soup/main.go @@ -15,6 +15,7 @@ var cases = map[string]interface{}{ "drand-halting": testkit.WrapTestEnvironment(dealsE2E), "drand-outage": testkit.WrapTestEnvironment(dealsE2E), "paych-stress": testkit.WrapTestEnvironment(paych.Stress), + "epoch-boundary": testkit.WrapTestEnvironment(epochBoundary), } func main() { From ebafb6584eb57a4651a0b7b1db3a2c4e9029f229 Mon Sep 17 00:00:00 2001 From: Alfonso de la Rocha Date: Thu, 22 Sep 2022 19:46:25 +0200 Subject: [PATCH 009/243] minor fixes. sync mining. --- .../_compositions/epoch_boundary.toml | 11 ++++++----- testplans/lotus-soup/epoch_boundary.go | 17 +++++++++++++---- testplans/lotus-soup/manifest.toml | 4 ++-- testplans/lotus-soup/testkit/role_miner.go | 1 + 4 files changed, 22 insertions(+), 11 deletions(-) diff --git a/testplans/lotus-soup/_compositions/epoch_boundary.toml b/testplans/lotus-soup/_compositions/epoch_boundary.toml index 70bdb795d02..20709589f04 100644 --- a/testplans/lotus-soup/_compositions/epoch_boundary.toml +++ b/testplans/lotus-soup/_compositions/epoch_boundary.toml @@ -25,7 +25,7 @@ balance = "20000000.5" # These balances will work for maximum 100 nodes, as TotalFilecoin is 2B sectors = "10" random_beacon_type = "mock" - mining_mode = "natural" + mining_mode = "synchronized" [[groups]] id = "bootstrapper" @@ -49,10 +49,11 @@ id = "attacker" [groups.build] - # TODO: Configure different versions for the different nodes. - # [[groups.build.dependencies]] - # module = "github.com/filecoin-project/lotus" - # version = "master" + # Implementation of the attacker. + [[groups.build.dependencies]] + module = "github.com/filecoin-project/lotus" + module = "github.com/adlrocha/lotus" + version = "b9ca4b71bf613ef9a1f21f302238c0d1fc55e65d" [groups.instances] count = 1 diff --git a/testplans/lotus-soup/epoch_boundary.go b/testplans/lotus-soup/epoch_boundary.go index 9f30627abec..9662676c54c 100644 --- a/testplans/lotus-soup/epoch_boundary.go +++ b/testplans/lotus-soup/epoch_boundary.go @@ -10,7 +10,6 @@ import ( // This test runs a set of miners and let them mine for some time. // Each miner tracks the different blocks they are mining so we can // process a posteriori the different chains they are mining. -// TODO: Include the attacker. func epochBoundary(t *testkit.TestEnvironment) error { t.RecordMessage("running node with role '%s'", t.Role) @@ -19,6 +18,7 @@ func epochBoundary(t *testkit.TestEnvironment) error { if t.Role != "miner" { return testkit.HandleDefaultRole(t) } + // prepare miners to run. m, err := testkit.PrepareMiner(t) if err != nil { return err @@ -28,15 +28,24 @@ func epochBoundary(t *testkit.TestEnvironment) error { ch, _ := miner.ChainNotify(ctx) for { curr := <-ch - // We collect new blocks seen by the node along with its cid. - // We can process the results a posteriori to determine the number of equivocations. - t.RecordMessage("New Block: height=%v, cid=%v", curr[0].Val.Height(), curr[0].Val.Cids()) + for _, c := range curr { + if c.Type != "apply" { + continue + } + // We collect new blocks seen by the node along with its cid. + // We can process the results a posteriori to determine the number of equivocations. + ts := c.Val + t.RecordMessage("New Block: height=%v, cid=%v", ts.Height(), ts.Cids()) + } } }() err = m.RunDefault() if err != nil { return err } + + // time to mine in the experiment. + // TODO: Make this configurable and optionally make it a number of epochs. time.Sleep(120 * time.Second) t.SyncClient.MustSignalAndWait(ctx, testkit.StateDone, t.TestInstanceCount) return nil diff --git a/testplans/lotus-soup/manifest.toml b/testplans/lotus-soup/manifest.toml index 3162b53720d..56d20aefd4b 100644 --- a/testplans/lotus-soup/manifest.toml +++ b/testplans/lotus-soup/manifest.toml @@ -9,8 +9,8 @@ enabled = true [builders."docker:go"] enabled = true -# build_base_image = "iptestground/oni-buildbase:v15-lotus" -# runtime_image = "iptestground/oni-runtime:v10-debug" +build_base_image = "iptestground/oni-buildbase:v15-lotus" +runtime_image = "iptestground/oni-runtime:v10-debug" [runners."local:exec"] enabled = true diff --git a/testplans/lotus-soup/testkit/role_miner.go b/testplans/lotus-soup/testkit/role_miner.go index 1a3319add24..291b1f06360 100644 --- a/testplans/lotus-soup/testkit/role_miner.go +++ b/testplans/lotus-soup/testkit/role_miner.go @@ -547,6 +547,7 @@ func (m *LotusMiner) RunDefault() error { defer t.RecordMessage("shutting down mining") defer close(done) + mine := true var i int for i = 0; mine; i++ { // synchronize all miners to mine the next block From 35f8abc72a8ab447771db0b7546ff59c9e4b19fb Mon Sep 17 00:00:00 2001 From: Alfonso de la Rocha Date: Fri, 23 Sep 2022 12:44:55 +0200 Subject: [PATCH 010/243] minor fix in composition --- testplans/lotus-soup/_compositions/epoch_boundary.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testplans/lotus-soup/_compositions/epoch_boundary.toml b/testplans/lotus-soup/_compositions/epoch_boundary.toml index 20709589f04..65511d0610c 100644 --- a/testplans/lotus-soup/_compositions/epoch_boundary.toml +++ b/testplans/lotus-soup/_compositions/epoch_boundary.toml @@ -52,7 +52,7 @@ # Implementation of the attacker. [[groups.build.dependencies]] module = "github.com/filecoin-project/lotus" - module = "github.com/adlrocha/lotus" + target = "github.com/adlrocha/lotus" version = "b9ca4b71bf613ef9a1f21f302238c0d1fc55e65d" [groups.instances] From bf6541857ea83de3c3b9b2029215f41b52353ff8 Mon Sep 17 00:00:00 2001 From: Alfonso de la Rocha Date: Mon, 26 Sep 2022 10:57:56 +0200 Subject: [PATCH 011/243] sync mining and gossipsub attacker depdendency --- testplans/lotus-soup/_compositions/epoch_boundary.toml | 6 ++++++ testplans/lotus-soup/testkit/role_miner.go | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/testplans/lotus-soup/_compositions/epoch_boundary.toml b/testplans/lotus-soup/_compositions/epoch_boundary.toml index 65511d0610c..2bcf9449c1a 100644 --- a/testplans/lotus-soup/_compositions/epoch_boundary.toml +++ b/testplans/lotus-soup/_compositions/epoch_boundary.toml @@ -55,6 +55,12 @@ target = "github.com/adlrocha/lotus" version = "b9ca4b71bf613ef9a1f21f302238c0d1fc55e65d" + # Modification of gossipsub + [[groups.build.dependencies]] + module = "github.com/libp2p/go-libp2p-pubsub" + target = "github.com/adlrocha/go-libp2p-pubsub" + version = "781f60ae88e4bcb79226232832e0a9b5a08dd3ed" + [groups.instances] count = 1 percentage = 0.0 diff --git a/testplans/lotus-soup/testkit/role_miner.go b/testplans/lotus-soup/testkit/role_miner.go index 291b1f06360..3f67d056baf 100644 --- a/testplans/lotus-soup/testkit/role_miner.go +++ b/testplans/lotus-soup/testkit/role_miner.go @@ -547,7 +547,7 @@ func (m *LotusMiner) RunDefault() error { defer t.RecordMessage("shutting down mining") defer close(done) - mine := true + mine = true var i int for i = 0; mine; i++ { // synchronize all miners to mine the next block From 91bd679d1e3e97fab1a63799c69969c569e55067 Mon Sep 17 00:00:00 2001 From: Alfonso de la Rocha Date: Tue, 13 Dec 2022 11:13:07 +0100 Subject: [PATCH 012/243] consistent broadcast delay as build param --- build/params_2k.go | 5 ++ build/params_mainnet.go | 5 ++ chain/sub/bcast/consistent.go | 19 +++-- chain/sub/bcast/consistent_test.go | 2 +- chain/sub/incoming.go | 2 +- .../_compositions/epoch_boundary.toml | 78 ------------------- testplans/lotus-soup/epoch_boundary.go | 52 ------------- 7 files changed, 23 insertions(+), 140 deletions(-) delete mode 100644 testplans/lotus-soup/_compositions/epoch_boundary.toml delete mode 100644 testplans/lotus-soup/epoch_boundary.go diff --git a/build/params_2k.go b/build/params_2k.go index f822d701ed4..48948ea5009 100644 --- a/build/params_2k.go +++ b/build/params_2k.go @@ -6,6 +6,7 @@ package build import ( "os" "strconv" + "time" "github.com/ipfs/go-cid" @@ -131,3 +132,7 @@ const InteractivePoRepConfidence = 6 const BootstrapPeerThreshold = 1 var WhitelistedBlock = cid.Undef + +// Reducing the delivery delay for equivocation of +// consistent broadcast to just one second. +const CBDeliveryDelay = 1 * time.Second diff --git a/build/params_mainnet.go b/build/params_mainnet.go index 2967931312a..93cdb66bd1b 100644 --- a/build/params_mainnet.go +++ b/build/params_mainnet.go @@ -7,6 +7,7 @@ import ( "math" "os" "strconv" + "time" "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/abi" @@ -126,3 +127,7 @@ const BootstrapPeerThreshold = 4 // we skip checks on message validity in this block to sidestep the zero-bls signature var WhitelistedBlock = MustParseCid("bafy2bzaceapyg2uyzk7vueh3xccxkuwbz3nxewjyguoxvhx77malc2lzn2ybi") + +// CBDeliveryDelay is the delay before deliver in the synchronous consistent broadcast. +// This determines the wait time for the detection of potential equivocations. +const CBDeliveryDelay = 6 * time.Second diff --git a/chain/sub/bcast/consistent.go b/chain/sub/bcast/consistent.go index 5b6079cf5c1..3604765616d 100644 --- a/chain/sub/bcast/consistent.go +++ b/chain/sub/bcast/consistent.go @@ -13,11 +13,13 @@ import ( "github.com/multiformats/go-multihash" ) -// TODO: Take const out of here and make them build params. const ( - DELAY = 6 * time.Second - GC_SANITY_CHECK = 5 - GC_LOOKBACK = 2 + // GcSanityCheck determines the number of epochs that in the past + // that will be garbage collected from the current epoch. + GcSanityCheck = 5 + // GcLookback determines the number of epochs kept in the consistent + // broadcast cache. + GcLookback = 2 ) type blksInfo struct { @@ -57,7 +59,8 @@ func newBcastDict() *bcastDict { return &bcastDict{new(sync.Map)} } -// TODO: What if the VRFProof is already small?? We don´t need the CID. Useless computation. +// TODO: the VRFProof may already be small enough so we may not need to use a hash here. +// we can maybe bypass the useless computation. func BCastKey(bh *types.BlockHeader) (multihash.Multihash, error) { k := make([]byte, len(bh.Ticket.VRFProof)) copy(k, bh.Ticket.VRFProof) @@ -150,9 +153,9 @@ func (cb *ConsistentBCast) GarbageCollect(currEpoch abi.ChainEpoch) { // and we use the sanity-check in case there were a few rounds // without delivery, and the garbage collection wasn't triggered // for a few epochs. - for i := 0; i < GC_SANITY_CHECK; i++ { - if currEpoch > GC_LOOKBACK { - delete(cb.m, currEpoch-abi.ChainEpoch(GC_LOOKBACK+i)) + for i := 0; i < GcSanityCheck; i++ { + if currEpoch > GcLookback { + delete(cb.m, currEpoch-abi.ChainEpoch(GcLookback+i)) } } } diff --git a/chain/sub/bcast/consistent_test.go b/chain/sub/bcast/consistent_test.go index 5dc2b198c20..d0629387618 100644 --- a/chain/sub/bcast/consistent_test.go +++ b/chain/sub/bcast/consistent_test.go @@ -81,7 +81,7 @@ func TestSeveralEpochs(t *testing.T) { }(i) } wg.Wait() - require.Equal(t, cb.Len(), bcast.GC_LOOKBACK) + require.Equal(t, cb.Len(), bcast.GcLookback) } // bias is expected to be 0-1 diff --git a/chain/sub/incoming.go b/chain/sub/incoming.go index 02358068d08..cee5a75a6ac 100644 --- a/chain/sub/incoming.go +++ b/chain/sub/incoming.go @@ -48,7 +48,7 @@ func HandleIncomingBlocks(ctx context.Context, bsub *pubsub.Subscription, self p // Timeout after (block time + propagation delay). This is useless at // this point. timeout := time.Duration(build.BlockDelaySecs+build.PropagationDelaySecs) * time.Second - cb := bcast.NewConsistentBCast(bcast.DELAY) + cb := bcast.NewConsistentBCast(build.CBDeliveryDelay) for { msg, err := bsub.Next(ctx) diff --git a/testplans/lotus-soup/_compositions/epoch_boundary.toml b/testplans/lotus-soup/_compositions/epoch_boundary.toml deleted file mode 100644 index 2bcf9449c1a..00000000000 --- a/testplans/lotus-soup/_compositions/epoch_boundary.toml +++ /dev/null @@ -1,78 +0,0 @@ -[metadata] - name = "lotus-soup" - author = "" - -[global] - plan = "lotus-soup" - case = "epoch-boundary" - total_instances = 4 - builder = "exec:go" - runner = "local:exec" - -[global.build] - selectors = ["testground"] - -[global.run_config] - exposed_ports = { pprof = "6060", node_rpc = "1234", miner_rpc = "2345" } - -[global.build_config] - enable_go_build_cache = true - -[global.run.test_params] - clients = "0" - miners = "3" - genesis_timestamp_offset = "0" - balance = "20000000.5" # These balances will work for maximum 100 nodes, as TotalFilecoin is 2B - sectors = "10" - random_beacon_type = "mock" - mining_mode = "synchronized" - -[[groups]] - id = "bootstrapper" - [groups.instances] - count = 1 - percentage = 0.0 - [groups.run] - [groups.run.test_params] - role = "bootstrapper" - -[[groups]] - id = "miners" - [groups.instances] - count = 2 - percentage = 0.0 - [groups.run] - [groups.run.test_params] - role = "miner" - -[[groups]] - id = "attacker" - [groups.build] - - # Implementation of the attacker. - [[groups.build.dependencies]] - module = "github.com/filecoin-project/lotus" - target = "github.com/adlrocha/lotus" - version = "b9ca4b71bf613ef9a1f21f302238c0d1fc55e65d" - - # Modification of gossipsub - [[groups.build.dependencies]] - module = "github.com/libp2p/go-libp2p-pubsub" - target = "github.com/adlrocha/go-libp2p-pubsub" - version = "781f60ae88e4bcb79226232832e0a9b5a08dd3ed" - - [groups.instances] - count = 1 - percentage = 0.0 - [groups.run] - [groups.run.test_params] - role = "miner" - -# [[groups]] -# id = "clients" -# [groups.instances] -# count = 0 -# percentage = 0.0 -# [groups.run] -# [groups.run.test_params] -# role = "client" diff --git a/testplans/lotus-soup/epoch_boundary.go b/testplans/lotus-soup/epoch_boundary.go deleted file mode 100644 index 9662676c54c..00000000000 --- a/testplans/lotus-soup/epoch_boundary.go +++ /dev/null @@ -1,52 +0,0 @@ -package main - -import ( - "context" - "time" - - "github.com/filecoin-project/lotus/testplans/lotus-soup/testkit" -) - -// This test runs a set of miners and let them mine for some time. -// Each miner tracks the different blocks they are mining so we can -// process a posteriori the different chains they are mining. -func epochBoundary(t *testkit.TestEnvironment) error { - t.RecordMessage("running node with role '%s'", t.Role) - - ctx := context.Background() - // Dispatch/forward non-client roles to defaults. - if t.Role != "miner" { - return testkit.HandleDefaultRole(t) - } - // prepare miners to run. - m, err := testkit.PrepareMiner(t) - if err != nil { - return err - } - go func() { - miner := m.FullApi - ch, _ := miner.ChainNotify(ctx) - for { - curr := <-ch - for _, c := range curr { - if c.Type != "apply" { - continue - } - // We collect new blocks seen by the node along with its cid. - // We can process the results a posteriori to determine the number of equivocations. - ts := c.Val - t.RecordMessage("New Block: height=%v, cid=%v", ts.Height(), ts.Cids()) - } - } - }() - err = m.RunDefault() - if err != nil { - return err - } - - // time to mine in the experiment. - // TODO: Make this configurable and optionally make it a number of epochs. - time.Sleep(120 * time.Second) - t.SyncClient.MustSignalAndWait(ctx, testkit.StateDone, t.TestInstanceCount) - return nil -} From f2cc452d4cb1b2f5f2f5c89ced93c222a6a530a4 Mon Sep 17 00:00:00 2001 From: Alfonso de la Rocha Date: Tue, 13 Dec 2022 12:13:20 +0100 Subject: [PATCH 013/243] remove error from rcvBlock. type/docs gen --- build/params_2k.go | 4 ++-- chain/sub/bcast/consistent.go | 25 ++++++++++++++++--------- chain/sub/bcast/consistent_test.go | 8 +++++--- 3 files changed, 23 insertions(+), 14 deletions(-) diff --git a/build/params_2k.go b/build/params_2k.go index 48948ea5009..390357bf273 100644 --- a/build/params_2k.go +++ b/build/params_2k.go @@ -134,5 +134,5 @@ const BootstrapPeerThreshold = 1 var WhitelistedBlock = cid.Undef // Reducing the delivery delay for equivocation of -// consistent broadcast to just one second. -const CBDeliveryDelay = 1 * time.Second +// consistent broadcast to just half a second. +const CBDeliveryDelay = 500 * time.Milisecond diff --git a/chain/sub/bcast/consistent.go b/chain/sub/bcast/consistent.go index 3604765616d..b698454769c 100644 --- a/chain/sub/bcast/consistent.go +++ b/chain/sub/bcast/consistent.go @@ -7,12 +7,17 @@ import ( "sync" "time" - "github.com/filecoin-project/go-state-types/abi" - "github.com/filecoin-project/lotus/chain/types" "github.com/ipfs/go-cid" + logging "github.com/ipfs/go-log/v2" "github.com/multiformats/go-multihash" + + "github.com/filecoin-project/go-state-types/abi" + + "github.com/filecoin-project/lotus/chain/types" ) +var log = logging.Logger("sub-cb") + const ( // GcSanityCheck determines the number of epochs that in the past // that will be garbage collected from the current epoch. @@ -86,14 +91,14 @@ func cidExists(cids []cid.Cid, c cid.Cid) bool { func (bInfo *blksInfo) eqErr() error { bInfo.cancel() - return fmt.Errorf("equivocation error detected. Different block with the same ticket already seen") + return fmt.Errorf("different blocks with the same ticket already seen") } func (cb *ConsistentBCast) Len() int { return len(cb.m) } -func (cb *ConsistentBCast) RcvBlock(ctx context.Context, blk *types.BlockMsg) error { +func (cb *ConsistentBCast) RcvBlock(ctx context.Context, blk *types.BlockMsg) { cb.lk.Lock() bcastDict, ok := cb.m[blk.Header.Height] if !ok { @@ -103,26 +108,28 @@ func (cb *ConsistentBCast) RcvBlock(ctx context.Context, blk *types.BlockMsg) er cb.lk.Unlock() key, err := BCastKey(blk.Header) if err != nil { - return err + log.Errorf("couldn't hash blk info for height %d: %s", blk.Header.Height, err) + return } blkCid := blk.Cid() bInfo, ok := bcastDict.load(key) if ok { if len(bInfo.blks) > 1 { - return bInfo.eqErr() + log.Errorf("equivocation detected for height %d: %s", blk.Header.Height, bInfo.eqErr()) + return } if !cidExists(bInfo.blks, blkCid) { bInfo.blks = append(bInfo.blks, blkCid) - return bInfo.eqErr() + log.Errorf("equivocation detected for height %d: %s", blk.Header.Height, bInfo.eqErr()) + return } - return nil + return } ctx, cancel := context.WithTimeout(ctx, cb.delay) bcastDict.store(key, &blksInfo{ctx, cancel, []cid.Cid{blkCid}}) - return nil } func (cb *ConsistentBCast) WaitForDelivery(bh *types.BlockHeader) error { diff --git a/chain/sub/bcast/consistent_test.go b/chain/sub/bcast/consistent_test.go index d0629387618..ca84ab4b1d3 100644 --- a/chain/sub/bcast/consistent_test.go +++ b/chain/sub/bcast/consistent_test.go @@ -10,13 +10,15 @@ import ( "testing" "time" + "github.com/ipfs/go-cid" + "github.com/multiformats/go-multihash" + "github.com/stretchr/testify/require" + "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/lotus/chain/sub/bcast" "github.com/filecoin-project/lotus/chain/types" - "github.com/ipfs/go-cid" - "github.com/multiformats/go-multihash" - "github.com/stretchr/testify/require" ) const TEST_DELAY = 1 * time.Second From 939e515d23a2d40b24732fe40a7e3f0a17998636 Mon Sep 17 00:00:00 2001 From: Alfonso de la Rocha Date: Tue, 13 Dec 2022 12:45:01 +0100 Subject: [PATCH 014/243] fix race in cb cache --- chain/sub/bcast/consistent.go | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/chain/sub/bcast/consistent.go b/chain/sub/bcast/consistent.go index b698454769c..cc600cb10ff 100644 --- a/chain/sub/bcast/consistent.go +++ b/chain/sub/bcast/consistent.go @@ -53,8 +53,16 @@ func (bd *bcastDict) store(key multihash.Multihash, d *blksInfo) { bd.m.Store(key.String(), d) } +func (bd *bcastDict) blkLen(key multihash.Multihash) int { + v, ok := bd.m.Load(key.String()) + if !ok { + return 0 + } + return len(v.(*blksInfo).blks) +} + type ConsistentBCast struct { - lk sync.Mutex + lk sync.RWMutex delay time.Duration // FIXME: Make this a slice??? Less storage but needs indexing logic. m map[abi.ChainEpoch]*bcastDict @@ -95,6 +103,8 @@ func (bInfo *blksInfo) eqErr() error { } func (cb *ConsistentBCast) Len() int { + cb.lk.RLock() + defer cb.lk.RUnlock() return len(cb.m) } @@ -121,7 +131,7 @@ func (cb *ConsistentBCast) RcvBlock(ctx context.Context, blk *types.BlockMsg) { } if !cidExists(bInfo.blks, blkCid) { - bInfo.blks = append(bInfo.blks, blkCid) + bcastDict.store(key, &blksInfo{bInfo.ctx, bInfo.cancel, append(bInfo.blks, blkCid)}) log.Errorf("equivocation detected for height %d: %s", blk.Header.Height, bInfo.eqErr()) return } @@ -133,7 +143,9 @@ func (cb *ConsistentBCast) RcvBlock(ctx context.Context, blk *types.BlockMsg) { } func (cb *ConsistentBCast) WaitForDelivery(bh *types.BlockHeader) error { + cb.lk.RLock() bcastDict := cb.m[bh.Height] + cb.lk.RUnlock() key, err := BCastKey(bh) if err != nil { return err @@ -144,7 +156,7 @@ func (cb *ConsistentBCast) WaitForDelivery(bh *types.BlockHeader) error { } // Wait for the timeout <-bInfo.ctx.Done() - if len(bInfo.blks) > 1 { + if bcastDict.blkLen(key) > 1 { return fmt.Errorf("equivocation detected for epoch %d. Two blocks being broadcast with same VRFProof", bh.Height) } return nil From d574d04075a2e57885e5bb5f84be2e708d7dba46 Mon Sep 17 00:00:00 2001 From: Alfonso de la Rocha Date: Tue, 13 Dec 2022 13:03:42 +0100 Subject: [PATCH 015/243] set small cb delivery delay for paych itests --- build/params_mainnet.go | 2 +- itests/kit/init.go | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/build/params_mainnet.go b/build/params_mainnet.go index 93cdb66bd1b..1612f4ab9b4 100644 --- a/build/params_mainnet.go +++ b/build/params_mainnet.go @@ -130,4 +130,4 @@ var WhitelistedBlock = MustParseCid("bafy2bzaceapyg2uyzk7vueh3xccxkuwbz3nxewjygu // CBDeliveryDelay is the delay before deliver in the synchronous consistent broadcast. // This determines the wait time for the detection of potential equivocations. -const CBDeliveryDelay = 6 * time.Second +var CBDeliveryDelay = 6 * time.Second diff --git a/itests/kit/init.go b/itests/kit/init.go index 9397c53a218..c6545eddac5 100644 --- a/itests/kit/init.go +++ b/itests/kit/init.go @@ -3,6 +3,7 @@ package kit import ( "fmt" "os" + "time" logging "github.com/ipfs/go-log/v2" @@ -40,6 +41,12 @@ func init() { build.InsecurePoStValidation = true + // NOTE: If we want the payment channel itests to pass that use a + // block time of 5*millisecond, we need to set the consistent broadcast + // delay to something lower than that block time. + // todo: configure such a low delay only for paych tests. + build.CBDeliveryDelay = 2 * time.Millisecond + if err := os.Setenv("BELLMAN_NO_GPU", "1"); err != nil { panic(fmt.Sprintf("failed to set BELLMAN_NO_GPU env variable: %s", err)) } From 3988cc9b2cfbb903336433df3321e2a6c0245741 Mon Sep 17 00:00:00 2001 From: Alfonso de la Rocha Date: Tue, 13 Dec 2022 14:50:02 +0100 Subject: [PATCH 016/243] disabling cb delivery delay for sync tests --- chain/sync_test.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/chain/sync_test.go b/chain/sync_test.go index 18520a07fe4..f4f553df33b 100644 --- a/chain/sync_test.go +++ b/chain/sync_test.go @@ -45,6 +45,11 @@ func init() { policy.SetSupportedProofTypes(abi.RegisteredSealProof_StackedDrg2KiBV1) policy.SetConsensusMinerMinPower(abi.NewStoragePower(2048)) policy.SetMinVerifiedDealSize(abi.NewStoragePower(256)) + + // these tests assume really fast block times. disabling + // the consistent broadcast delay to avoid them from adding + // an unnecessary overhead. + build.CBDeliveryDelay = 2 * time.Millisecond } const source = 0 From 35730637dc5f23bfc9325d2d7155fdafa496475b Mon Sep 17 00:00:00 2001 From: Ian Davis Date: Tue, 14 Feb 2023 16:17:03 +0000 Subject: [PATCH 017/243] fix: tracer: emit raw peer ids for compatibility with libp2p tracer --- node/modules/tracer/tracer.go | 9 ++++----- node/modules/tracer/tracer_test.go | 6 ++---- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/node/modules/tracer/tracer.go b/node/modules/tracer/tracer.go index 0d0a156d93a..72f068d12a1 100644 --- a/node/modules/tracer/tracer.go +++ b/node/modules/tracer/tracer.go @@ -31,7 +31,7 @@ const ( type LotusTraceEvent struct { Type pubsub_pb.TraceEvent_Type `json:"type,omitempty"` - PeerID string `json:"peerID,omitempty"` + PeerID []byte `json:"peerID,omitempty"` Timestamp *int64 `json:"timestamp,omitempty"` PeerScore TraceEventPeerScore `json:"peerScore,omitempty"` SourceAuth string `json:"sourceAuth,omitempty"` @@ -46,7 +46,7 @@ type TopicScore struct { } type TraceEventPeerScore struct { - PeerID string `json:"peerID"` + PeerID []byte `json:"peerID"` Score float64 `json:"score"` AppSpecificScore float64 `json:"appSpecificScore"` IPColocationFactor float64 `json:"ipColocationFactor"` @@ -77,11 +77,11 @@ func (lt *lotusTracer) PeerScores(scores map[peer.ID]*pubsub.PeerScoreSnapshot) evt := &LotusTraceEvent{ Type: *TraceEventPeerScores.Enum(), - PeerID: lt.pid.Pretty(), + PeerID: []byte(lt.pid), Timestamp: &now, SourceAuth: lt.sa, PeerScore: TraceEventPeerScore{ - PeerID: pid.Pretty(), + PeerID: []byte(pid), Score: score.Score, AppSpecificScore: score.AppSpecificScore, IPColocationFactor: score.IPColocationFactor, @@ -104,7 +104,6 @@ func (lt *lotusTracer) TraceLotusEvent(evt *LotusTraceEvent) { log.Errorf("error while transporting peer scores: %s", err) } } - } func (lt *lotusTracer) Trace(evt *pubsub_pb.TraceEvent) { diff --git a/node/modules/tracer/tracer_test.go b/node/modules/tracer/tracer_test.go index 7ade678615b..f0d7b2c0b83 100644 --- a/node/modules/tracer/tracer_test.go +++ b/node/modules/tracer/tracer_test.go @@ -30,16 +30,15 @@ func (ttt *testTracerTransport) Transport(evt TracerTransportEvent) error { } func TestTracer_PeerScores(t *testing.T) { - testTransport := NewTestTraceTransport(t, func(t *testing.T, evt TracerTransportEvent) { - require.Equal(t, peerIDA.Pretty(), evt.lotusTraceEvent.PeerID) + require.Equal(t, []byte(peerIDA), evt.lotusTraceEvent.PeerID) require.Equal(t, "source-auth-token-test", evt.lotusTraceEvent.SourceAuth) require.Equal(t, float64(32), evt.lotusTraceEvent.PeerScore.Score) n := time.Now().UnixNano() require.LessOrEqual(t, *evt.lotusTraceEvent.Timestamp, n) - require.Equal(t, peerIDA.Pretty(), evt.lotusTraceEvent.PeerScore.PeerID) + require.Equal(t, []byte(peerIDA), evt.lotusTraceEvent.PeerScore.PeerID) require.Equal(t, 1, len(evt.lotusTraceEvent.PeerScore.Topics)) topic := evt.lotusTraceEvent.PeerScore.Topics[0] @@ -85,7 +84,6 @@ func TestTracer_PubSubTrace(t *testing.T) { PeerID: []byte(peerIDA), Timestamp: &n, }) - } func TestTracer_MultipleTransports(t *testing.T) { From 7f33db90d88edca1b116324526c09103a805682d Mon Sep 17 00:00:00 2001 From: Marcel Telka Date: Tue, 21 Feb 2023 11:10:38 +0100 Subject: [PATCH 018/243] lotus-miner info should show deals info without admin permission --- cmd/lotus-miner/info.go | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/cmd/lotus-miner/info.go b/cmd/lotus-miner/info.go index d791b076080..4afd1894113 100644 --- a/cmd/lotus-miner/info.go +++ b/cmd/lotus-miner/info.go @@ -347,14 +347,12 @@ func handleMiningInfo(ctx context.Context, cctx *cli.Context, fullapi v1api.Full } } - { - fmt.Println() - - ws, err := nodeApi.WorkerStats(ctx) - if err != nil { - return xerrors.Errorf("getting worker stats: %w", err) - } + fmt.Println() + ws, err := nodeApi.WorkerStats(ctx) + if err != nil { + fmt.Printf("ERROR: getting worker stats: %s\n", err) + } else { workersByType := map[string]int{ sealtasks.WorkerSealing: 0, sealtasks.WorkerWindowPoSt: 0, From 5d3314f121d0c857e86f396ae458f9827a908540 Mon Sep 17 00:00:00 2001 From: Ian Davis Date: Thu, 23 Feb 2023 13:02:20 +0000 Subject: [PATCH 019/243] Touch source to trigger CI --- node/modules/tracer/tracer.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/modules/tracer/tracer.go b/node/modules/tracer/tracer.go index 72f068d12a1..b9b56e80c86 100644 --- a/node/modules/tracer/tracer.go +++ b/node/modules/tracer/tracer.go @@ -77,8 +77,8 @@ func (lt *lotusTracer) PeerScores(scores map[peer.ID]*pubsub.PeerScoreSnapshot) evt := &LotusTraceEvent{ Type: *TraceEventPeerScores.Enum(), - PeerID: []byte(lt.pid), Timestamp: &now, + PeerID: []byte(lt.pid), SourceAuth: lt.sa, PeerScore: TraceEventPeerScore{ PeerID: []byte(pid), From c11ffa58a8283778ee51383bcafe46995b4f2060 Mon Sep 17 00:00:00 2001 From: Alfonso de la Rocha Date: Fri, 10 Mar 2023 09:27:30 +0100 Subject: [PATCH 020/243] address review --- chain/sub/incoming.go | 4 ++-- itests/kit/init.go | 10 ++++------ 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/chain/sub/incoming.go b/chain/sub/incoming.go index cee5a75a6ac..6226e45d8a0 100644 --- a/chain/sub/incoming.go +++ b/chain/sub/incoming.go @@ -111,7 +111,7 @@ func HandleIncomingBlocks(ctx context.Context, bsub *pubsub.Subscription, self p // If we are the block proposers we don't need to wait for delivery, we know the blocks are // honest. if src != self { - log.Infof("Waiting for consistent broadcast of block in height: %v", blk.Header.Height) + log.Debugf("Waiting for consistent broadcast of block in height: %v", blk.Header.Height) if err := cb.WaitForDelivery(blk.Header); err != nil { log.Errorf("couldn't deliver block to syncer over pubsub: %s; source: %s", err, src) return @@ -119,7 +119,7 @@ func HandleIncomingBlocks(ctx context.Context, bsub *pubsub.Subscription, self p } // Garbage collect the broadcast state cb.GarbageCollect(blk.Header.Height) - log.Infof("Block in height %v delivered successfully (cid=)", blk.Header.Height, blk.Cid()) + log.Debugf("Block in height %v delivered successfully (cid=)", blk.Header.Height, blk.Cid()) if s.InformNewBlock(msg.ReceivedFrom, &types.FullBlock{ Header: blk.Header, diff --git a/itests/kit/init.go b/itests/kit/init.go index c6545eddac5..dbcb49aae31 100644 --- a/itests/kit/init.go +++ b/itests/kit/init.go @@ -3,7 +3,6 @@ package kit import ( "fmt" "os" - "time" logging "github.com/ipfs/go-log/v2" @@ -41,11 +40,10 @@ func init() { build.InsecurePoStValidation = true - // NOTE: If we want the payment channel itests to pass that use a - // block time of 5*millisecond, we need to set the consistent broadcast - // delay to something lower than that block time. - // todo: configure such a low delay only for paych tests. - build.CBDeliveryDelay = 2 * time.Millisecond + // Disabling consistent broadcast in itests. Many tests use really fast + // block times, and adding this additional delay for block delivery + // would make these tests to fail. + build.CBDeliveryDelay = 0 if err := os.Setenv("BELLMAN_NO_GPU", "1"); err != nil { panic(fmt.Sprintf("failed to set BELLMAN_NO_GPU env variable: %s", err)) From 6e9a1f44edb3a85d4204860a532e8a1d1165eaf7 Mon Sep 17 00:00:00 2001 From: Ales Dumikau Date: Sat, 11 Mar 2023 10:27:42 +0300 Subject: [PATCH 021/243] Add new methods to gw --- api/api_gateway.go | 3 +++ api/proxy_gen.go | 39 ++++++++++++++++++++++++++++++++++ api/v0api/gateway.go | 3 +++ api/v0api/proxy_gen.go | 39 ++++++++++++++++++++++++++++++++++ build/openrpc/full.json.gz | Bin 33620 -> 33618 bytes build/openrpc/gateway.json.gz | Bin 9246 -> 9482 bytes build/openrpc/miner.json.gz | Bin 16043 -> 16043 bytes build/openrpc/worker.json.gz | Bin 5224 -> 5226 bytes gateway/node.go | 3 +++ gateway/proxy_fil.go | 30 ++++++++++++++++++++++++++ 10 files changed, 117 insertions(+) diff --git a/api/api_gateway.go b/api/api_gateway.go index 66a820221bd..4015dddc457 100644 --- a/api/api_gateway.go +++ b/api/api_gateway.go @@ -33,6 +33,9 @@ import ( // * Generate openrpc blobs type Gateway interface { + StateMinerSectorCount(context.Context, address.Address, types.TipSetKey) (MinerSectors, error) + GasEstimateGasPremium(context.Context, uint64, address.Address, int64, types.TipSetKey) (types.BigInt, error) + StateReplay(context.Context, types.TipSetKey, cid.Cid) (*InvocResult, error) ChainHasObj(context.Context, cid.Cid) (bool, error) ChainPutObj(context.Context, blocks.Block) error ChainHead(ctx context.Context) (*types.TipSet, error) diff --git a/api/proxy_gen.go b/api/proxy_gen.go index 33e6362bd87..992f38925e0 100644 --- a/api/proxy_gen.go +++ b/api/proxy_gen.go @@ -722,6 +722,8 @@ type GatewayMethods struct { EthUnsubscribe func(p0 context.Context, p1 ethtypes.EthSubscriptionID) (bool, error) `` + GasEstimateGasPremium func(p0 context.Context, p1 uint64, p2 address.Address, p3 int64, p4 types.TipSetKey) (types.BigInt, error) `` + GasEstimateMessageGas func(p0 context.Context, p1 *types.Message, p2 *MessageSendSpec, p3 types.TipSetKey) (*types.Message, error) `` MpoolGetNonce func(p0 context.Context, p1 address.Address) (uint64, error) `` @@ -764,12 +766,16 @@ type GatewayMethods struct { StateMinerProvingDeadline func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (*dline.Info, error) `` + StateMinerSectorCount func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (MinerSectors, error) `` + StateNetworkName func(p0 context.Context) (dtypes.NetworkName, error) `` StateNetworkVersion func(p0 context.Context, p1 types.TipSetKey) (apitypes.NetworkVersion, error) `` StateReadState func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (*ActorState, error) `` + StateReplay func(p0 context.Context, p1 types.TipSetKey, p2 cid.Cid) (*InvocResult, error) `` + StateSearchMsg func(p0 context.Context, p1 types.TipSetKey, p2 cid.Cid, p3 abi.ChainEpoch, p4 bool) (*MsgLookup, error) `` StateSectorGetInfo func(p0 context.Context, p1 address.Address, p2 abi.SectorNumber, p3 types.TipSetKey) (*miner.SectorOnChainInfo, error) `` @@ -4586,6 +4592,17 @@ func (s *GatewayStub) EthUnsubscribe(p0 context.Context, p1 ethtypes.EthSubscrip return false, ErrNotSupported } +func (s *GatewayStruct) GasEstimateGasPremium(p0 context.Context, p1 uint64, p2 address.Address, p3 int64, p4 types.TipSetKey) (types.BigInt, error) { + if s.Internal.GasEstimateGasPremium == nil { + return *new(types.BigInt), ErrNotSupported + } + return s.Internal.GasEstimateGasPremium(p0, p1, p2, p3, p4) +} + +func (s *GatewayStub) GasEstimateGasPremium(p0 context.Context, p1 uint64, p2 address.Address, p3 int64, p4 types.TipSetKey) (types.BigInt, error) { + return *new(types.BigInt), ErrNotSupported +} + func (s *GatewayStruct) GasEstimateMessageGas(p0 context.Context, p1 *types.Message, p2 *MessageSendSpec, p3 types.TipSetKey) (*types.Message, error) { if s.Internal.GasEstimateMessageGas == nil { return nil, ErrNotSupported @@ -4817,6 +4834,17 @@ func (s *GatewayStub) StateMinerProvingDeadline(p0 context.Context, p1 address.A return nil, ErrNotSupported } +func (s *GatewayStruct) StateMinerSectorCount(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (MinerSectors, error) { + if s.Internal.StateMinerSectorCount == nil { + return *new(MinerSectors), ErrNotSupported + } + return s.Internal.StateMinerSectorCount(p0, p1, p2) +} + +func (s *GatewayStub) StateMinerSectorCount(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (MinerSectors, error) { + return *new(MinerSectors), ErrNotSupported +} + func (s *GatewayStruct) StateNetworkName(p0 context.Context) (dtypes.NetworkName, error) { if s.Internal.StateNetworkName == nil { return *new(dtypes.NetworkName), ErrNotSupported @@ -4850,6 +4878,17 @@ func (s *GatewayStub) StateReadState(p0 context.Context, p1 address.Address, p2 return nil, ErrNotSupported } +func (s *GatewayStruct) StateReplay(p0 context.Context, p1 types.TipSetKey, p2 cid.Cid) (*InvocResult, error) { + if s.Internal.StateReplay == nil { + return nil, ErrNotSupported + } + return s.Internal.StateReplay(p0, p1, p2) +} + +func (s *GatewayStub) StateReplay(p0 context.Context, p1 types.TipSetKey, p2 cid.Cid) (*InvocResult, error) { + return nil, ErrNotSupported +} + func (s *GatewayStruct) StateSearchMsg(p0 context.Context, p1 types.TipSetKey, p2 cid.Cid, p3 abi.ChainEpoch, p4 bool) (*MsgLookup, error) { if s.Internal.StateSearchMsg == nil { return nil, ErrNotSupported diff --git a/api/v0api/gateway.go b/api/v0api/gateway.go index 2a0bfb2f7f4..83c7bdfb3e5 100644 --- a/api/v0api/gateway.go +++ b/api/v0api/gateway.go @@ -35,6 +35,9 @@ import ( // * Generate openrpc blobs type Gateway interface { + StateMinerSectorCount(context.Context, address.Address, types.TipSetKey) (api.MinerSectors, error) + GasEstimateGasPremium(context.Context, uint64, address.Address, int64, types.TipSetKey) (types.BigInt, error) + StateReplay(context.Context, types.TipSetKey, cid.Cid) (*api.InvocResult, error) ChainHasObj(context.Context, cid.Cid) (bool, error) ChainPutObj(context.Context, blocks.Block) error ChainHead(ctx context.Context) (*types.TipSet, error) diff --git a/api/v0api/proxy_gen.go b/api/v0api/proxy_gen.go index 17a1ae84ae5..35457e8bd02 100644 --- a/api/v0api/proxy_gen.go +++ b/api/v0api/proxy_gen.go @@ -449,6 +449,8 @@ type GatewayMethods struct { ChainReadObj func(p0 context.Context, p1 cid.Cid) ([]byte, error) `` + GasEstimateGasPremium func(p0 context.Context, p1 uint64, p2 address.Address, p3 int64, p4 types.TipSetKey) (types.BigInt, error) `` + GasEstimateMessageGas func(p0 context.Context, p1 *types.Message, p2 *api.MessageSendSpec, p3 types.TipSetKey) (*types.Message, error) `` MpoolGetNonce func(p0 context.Context, p1 address.Address) (uint64, error) `` @@ -487,10 +489,14 @@ type GatewayMethods struct { StateMinerProvingDeadline func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (*dline.Info, error) `` + StateMinerSectorCount func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (api.MinerSectors, error) `` + StateNetworkName func(p0 context.Context) (dtypes.NetworkName, error) `` StateNetworkVersion func(p0 context.Context, p1 types.TipSetKey) (abinetwork.Version, error) `` + StateReplay func(p0 context.Context, p1 types.TipSetKey, p2 cid.Cid) (*api.InvocResult, error) `` + StateSearchMsg func(p0 context.Context, p1 cid.Cid) (*api.MsgLookup, error) `` StateSectorGetInfo func(p0 context.Context, p1 address.Address, p2 abi.SectorNumber, p3 types.TipSetKey) (*miner.SectorOnChainInfo, error) `` @@ -2674,6 +2680,17 @@ func (s *GatewayStub) ChainReadObj(p0 context.Context, p1 cid.Cid) ([]byte, erro return *new([]byte), ErrNotSupported } +func (s *GatewayStruct) GasEstimateGasPremium(p0 context.Context, p1 uint64, p2 address.Address, p3 int64, p4 types.TipSetKey) (types.BigInt, error) { + if s.Internal.GasEstimateGasPremium == nil { + return *new(types.BigInt), ErrNotSupported + } + return s.Internal.GasEstimateGasPremium(p0, p1, p2, p3, p4) +} + +func (s *GatewayStub) GasEstimateGasPremium(p0 context.Context, p1 uint64, p2 address.Address, p3 int64, p4 types.TipSetKey) (types.BigInt, error) { + return *new(types.BigInt), ErrNotSupported +} + func (s *GatewayStruct) GasEstimateMessageGas(p0 context.Context, p1 *types.Message, p2 *api.MessageSendSpec, p3 types.TipSetKey) (*types.Message, error) { if s.Internal.GasEstimateMessageGas == nil { return nil, ErrNotSupported @@ -2883,6 +2900,17 @@ func (s *GatewayStub) StateMinerProvingDeadline(p0 context.Context, p1 address.A return nil, ErrNotSupported } +func (s *GatewayStruct) StateMinerSectorCount(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (api.MinerSectors, error) { + if s.Internal.StateMinerSectorCount == nil { + return *new(api.MinerSectors), ErrNotSupported + } + return s.Internal.StateMinerSectorCount(p0, p1, p2) +} + +func (s *GatewayStub) StateMinerSectorCount(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (api.MinerSectors, error) { + return *new(api.MinerSectors), ErrNotSupported +} + func (s *GatewayStruct) StateNetworkName(p0 context.Context) (dtypes.NetworkName, error) { if s.Internal.StateNetworkName == nil { return *new(dtypes.NetworkName), ErrNotSupported @@ -2905,6 +2933,17 @@ func (s *GatewayStub) StateNetworkVersion(p0 context.Context, p1 types.TipSetKey return *new(abinetwork.Version), ErrNotSupported } +func (s *GatewayStruct) StateReplay(p0 context.Context, p1 types.TipSetKey, p2 cid.Cid) (*api.InvocResult, error) { + if s.Internal.StateReplay == nil { + return nil, ErrNotSupported + } + return s.Internal.StateReplay(p0, p1, p2) +} + +func (s *GatewayStub) StateReplay(p0 context.Context, p1 types.TipSetKey, p2 cid.Cid) (*api.InvocResult, error) { + return nil, ErrNotSupported +} + func (s *GatewayStruct) StateSearchMsg(p0 context.Context, p1 cid.Cid) (*api.MsgLookup, error) { if s.Internal.StateSearchMsg == nil { return nil, ErrNotSupported diff --git a/build/openrpc/full.json.gz b/build/openrpc/full.json.gz index 64a555389d85bd25842fb733c6fccda66c10332f..7086d8c5c12b60015392953ccc2f5121ecf2ab5e 100644 GIT binary patch delta 31318 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 diff --git a/build/openrpc/gateway.json.gz b/build/openrpc/gateway.json.gz index 04a3f4fda29b05a64c02b7d8ab31b7aa77e7b070..d905dceb7819f1d9ff924a59178092ff4516ef61 100644 GIT binary patch delta 9228 zcmV+nB=g&zNQz2-ABzY8000000RQa0Yjc~r_Bi}i7@jw6$4!09%`<(GThpA~W@jhq z?(dwo^K2PpTMY&_h}do?-~B%z0pc!U?2trf&z!~}EnQf5t+n)9wQOiQwx*8M-cEmK zPwlA4oLK7Uw~7%qpgK}NS=e)x>#Gyx=;}i4sQ1uuk!6y99=)CJTOHo34;^(5@yya) zb@c7GYJ$1n^#d}XW+8LuWCoCV4)L*JX?LHY>w+nC)sCt|S96e!3A}&)`Dbc_ZvL; z|3}+YL4IX-{i(kGZa#cB|M}-1wWGS;d=8wII#O?E(18kYpki8BSwXA-bERn3d=5?Q zDih05@C>;M+4UW@quRg$bFx>S_13mr6l^YkeVzC^b}#b<=&mJZ;{VWQa1L0c z2)5e9%7qS1j3x*=8CWMsR~%?N&<$n`6c-wkx1`M&nN#xFOsoK1ccAM+T|p7F9pwTm zC|Ktl>dJVf0OfCjcpU{OCzcL1<<(+_w3&iT9cchVe~h_jV5IqP-`YUCgL*UyUfJ-q z0!&?h0m^5;)eN{Zf8XE|%xwdbt$F9~8ozOWKPu|4;|Hk|9Z%Qx;ny+ z2mQ^m9q3P6S4R_IxX|zCK-V*{ztQv?y(y=En9S>f&z4x@vEfEN{NI0v1U>9`)LTm( zsgrK6KNzYV^@C&0+25CzsX@~4Z(w--47+MaJqPY5G)Gt+9R@!>KzIV|_~n%Y=g6DW z2cH9!RY$#!dPR{^{jX_s|95jd)6D^;?$pd@{5eewhpKF&HxQH%Y_Sn zJX^Wb-R0fV{&!;b7ZdH*-u%8ln_G8yG1ixNU^#Tv2Y+jCpl07C)=hu-gS>z(=%_bn zY68L=eyg_uhW0z^DZn6a^oPd3zzNbDBr+c&(Di`50prTCZ0KO*f7*bWI;zb)N+JDk z6=66J{ty5`FrlIgp?uXE|AZR$H<7`Ag&JKcPQ{GZ^ycG$+0xeZ_d^oNL`Mqok!KuA z06a}Y-2Sc3p^gGhG)5+HR%fPW={{%l5y6FfL%%icPI=}7Aw=NRfpondUdqj&G7 zL6aTLn!lS+>+h}YSpK};8NMa?ZfscNcXQxk=)40qdS^Sl&5VnXp#jkm|(+h9~h*{23mzKJ;^ZeAz zgTr#6dj%Y5;&sm|l2EEe@V6fV(l-e_UkJZ3;Ub3mx{g53tU=0UDVIy-a)fW0?PcFo zOJibxEM(!6!1=KibasatcXGLH!(s2JoXUXv#+yL#O& zkLn;if(LC0RIBsU*uCV-2~+tWU}{Dc36GS}ykGNV%eRTr%%$ ziLk$?>ZsdMuLJzemlo&1{o?vlKJ2Jx_x^`}y|ygel)S7jKaz71s5&rJgYBE@T^FLus-BJ;Hsn8{q!>;UOqygpUi;CYaZcQxhRR@)f6DX zn*25Rb8eI9NNpc4=V?UE&Zw{Uz@1^o91iaGrvuM)p#E!rvNVU&pSy#BKCp%Z{IA)6 zxBJLmE@#fYHoJ#vzWTmQl&~2(Wx+N48Rw-9^)Dn>i{RkF;8L zq~#-MW@qWcYL4TN#LUL;2Z8y-dB)yelRO6+4$D$&t~^{<8j`L>(r_G;WCtT4m!6U3 z{q!Ltks}+yFrGO6AC|@ZUPGYQB>>u&Cq`<#lcxtOA6`Ku<|%SD>pllFE4WG6FJDsx zHX>@!pEfkFuTD-alko>7el44ptzP#SyFUl*zGQ5W#kaXGfZ5d8T+YG}ASoD8K!Hct%jj zZ%YJ98AUP>O9gxmSRsl`&b$ponY*SO0h8Z~!;!3Zm%G{+LTz$*f2a>8`gkzu@4`X% zP}BN*!%2VG9gn+vd&BOarzV4i;=k&+)-!(eA02fLmKOwQzuQsg$Yg${gMpq`d{BQ- z5Fd1gXMxponZLEwl*shXCq@=?z919t4J#;-fmlL}S+Js%n4EbVh%vjS%m>MBvY8Vp zpK{gOCsHk?gJD6mf6E!3oq!zMyP5#}Zbeom`?dUb!I>=4vQw0Cyu#&`FewC1iU`1o z5(HgI8xLXA8_B5(PDtLMfC%cS*b?nA1up4A{=RaM7GxwYyS}y=<|hWfbW+-{yX@|( zf=T~6t#(vUz${+~RwiJiD}+sGX@O=(sTREP41(R{$sLKee_Axh9$SyQ#QQp>0cslt zWw8O&;^*=lB%;8Z^lD@nS1I|2EjVTzkXb^fY)X@SPO9NR>!$sS3UbRdBB$5akTd&RmS<0K;=o$2r0D=2Z;q5Ct>LMhPRioVJ}&NX<}c zhEg+ZEC5ocCNvKRJ=)voftxe z=!qM0N}jlbsPsv!s%^q2v!Q%;$?<}mySBqFsbT{qNLkp7M+94j4qbBpN+cx5z=aFedJz-O->E4nDsc@kveePHUVmp0OnU(w-PfQ6ejg1^jGm zS!t(T0E&Z|#Ut`Ev>;5s20u%zt?fLGenT!A80_yIcC`uA_O#x3GSmj+!~Vg6whwmo z{euBKJlG%f;9+;%1w*a3zpwSdV1KCXgTrBee}AyM3pdT^27Q*%`M(-xblXV?tK@Dp zW1@t)@#j>iGam&;O?=+~|DcIKHVDz3fx#D33ntqP#5ppdBkEIjF~E=%a%^T6*a4;s zH2>SVO~Q3(8P_IZ8rIZ3HZ1D9@9z7khb9xGc?MoZUIc7mV4P;3Q(|R2^0ZuZ{D73M ze+=QEIdn0Y+tCxa#11%RT=o7(TsDbiCMj(lKu`Xcb&t@a0~h^*@tm2j873-u6lFkfiXCn8g!TQR00bx{c?+}ue-k${R9$57g)_mQ%}$2@n5i1;zL z+zjtC+0+yc*clw;On~x79t>sW>tk+Oe^Sak=1f@q;8Euhhy$K)$fy#JIblZ3^2|CT z-MKm?#A2ndJU2BbXXY8z=`r`w%Oc++e$?lDiZJLGBpD_Q@7g&PX%I!83F4?~#vdV+ zaIvOGB3e}&eG*&uAWAb|n)%Yqmu9{+^QDsQv12Qml$Q`M5DnFz_@NhuV;o|hQ^6E|Y`E`Il z)%-6pY$KNuP@(l(k83C=BNnKvX?xRwB)%)s`~DxT+C<5Ugv zsFrf4f~^xMs})xC=nuNRe!r^*#1xpM!UiX{Y^2dEq6FhnqA|B{OjaFukp3N9IQ00Z z>dfbgYLrk5H#mVUdq=)Z-tM;LG_onqD#g(=5=Vz6^rDcB=k3f5AtI_#Df9J;}l z1E%mu`2PD;chLi-oO2|5u+}kCG?fjXHL*($51jnTw~4FDLnod?r{{5+6R&U}kJaR{ zT897kN~<(68shQlI5d5aO#Q;t;j*4kD2FLj%c(Lj>2_Hy8OrCwe3d%s#>%!x!7R zvLJaY0rdru2_VzW=lNCe+dlc$s6iSpr$x$a= z9D1D^>W_SHNLFZYWy=K*cv{|zZ^?EqxofFiG$^eTlhxb_sx)mGC86@?XKd0sO3rD! z_EBa(+HN7WSBC1D~GMF-v7&XO7yReJ}NXsrO60U+Vo*@0WVN)cd90|J3yU-O}n&=~72) ztNj6#MoPo*){9CHzUdNCIg4%k0fUU+eKJL3WFf4LaKXLOLRhIBr{h&#YK$l+j35

bea{Y+XY4s!-=zeKsErwlOlTz+$ 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 delta 8961 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 busrA;s1FZ69{yhd00960W~T&h3HSj3fS9K~ diff --git a/build/openrpc/miner.json.gz b/build/openrpc/miner.json.gz index fd7f08f50ca0e222db9994d75665271c2902deec..b447c666a4e3e42c7edab7221432b72b18e7739c 100644 GIT binary patch delta 15627 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

E^+LMLVb3K>Jw@p zwn^x;K&J&dEzoI!P7C~CT3}5){|@y|{CCkrT{KaDC;Yo;q8A-a^!Vsk7WRx|vbOD> z@s7Zm9hC+4Yny(=J`Vm*_#l3MaMrStX}@Qjh`+_39N-CE6XrvGEGffXp*nNWi%xmw za42EXUo-Qz}LMZXN%8ZC|u{!hd!EYH>8_yt`t`43kdaHGj zR8aM8wnAgwTcToxb~Uf6idD?bC%$&*GR1S6%aLGl1tF#wR`V*aDoCV-O7(Aq)Y1B^ z%Jk?|gRl1_Da0y}osfY#?c?IFkiJ@|_OBYBFVFQEy}gSpceQ5xcxpU8-aP)l00030 M|MnFGlJy${05(8TsQ>@~ diff --git a/build/openrpc/worker.json.gz b/build/openrpc/worker.json.gz index aeb928561373e0427dcebdc71552d170dac4937b..6c3616074c8c084ee49ca617f22da08f31316efb 100644 GIT binary patch delta 4283 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 delta 4259 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 F0RR)aHOBw| diff --git a/gateway/node.go b/gateway/node.go index a97778eacc4..893cbc3126f 100644 --- a/gateway/node.go +++ b/gateway/node.go @@ -43,6 +43,9 @@ const ( // TargetAPI defines the API methods that the Node depends on // (to make it easy to mock for tests) type TargetAPI interface { + GasEstimateGasPremium(context.Context, uint64, address.Address, int64, types.TipSetKey) (types.BigInt, error) + StateReplay(context.Context, types.TipSetKey, cid.Cid) (*api.InvocResult, error) + StateMinerSectorCount(context.Context, address.Address, types.TipSetKey) (api.MinerSectors, error) Version(context.Context) (api.APIVersion, error) ChainGetParentMessages(context.Context, cid.Cid) ([]api.Message, error) ChainGetParentReceipts(context.Context, cid.Cid) ([]*types.MessageReceipt, error) diff --git a/gateway/proxy_fil.go b/gateway/proxy_fil.go index bf6c2dff810..57a0ff48123 100644 --- a/gateway/proxy_fil.go +++ b/gateway/proxy_fil.go @@ -23,6 +23,36 @@ import ( "github.com/filecoin-project/lotus/node/modules/dtypes" ) +func (gw *Node) StateReplay(ctx context.Context, tsk types.TipSetKey, c cid.Cid) (*api.InvocResult, error) { + if err := gw.limit(ctx, chainRateLimitTokens); err != nil { + return nil, err + } + if err := gw.checkTipsetKey(ctx, tsk); err != nil { + return nil, err + } + return gw.target.StateReplay(ctx, tsk, c) +} + +func (gw *Node) GasEstimateGasPremium(ctx context.Context, nblocksincl uint64, sender address.Address, gaslimit int64, tsk types.TipSetKey) (types.BigInt, error) { + if err := gw.limit(ctx, chainRateLimitTokens); err != nil { + return types.BigInt{}, err + } + if err := gw.checkTipsetKey(ctx, tsk); err != nil { + return types.BigInt{}, err + } + return gw.target.GasEstimateGasPremium(ctx, nblocksincl, sender, gaslimit, tsk) +} + +func (gw *Node) StateMinerSectorCount(ctx context.Context, m address.Address, tsk types.TipSetKey) (api.MinerSectors, error) { + if err := gw.limit(ctx, chainRateLimitTokens); err != nil { + return api.MinerSectors{}, err + } + if err := gw.checkTipsetKey(ctx, tsk); err != nil { + return api.MinerSectors{}, err + } + return gw.target.StateMinerSectorCount(ctx, m, tsk) +} + func (gw *Node) Discover(ctx context.Context) (apitypes.OpenRPCDocument, error) { return build.OpenRPCDiscoverJSON_Gateway(), nil } From a0e95ebe972af766a7f6069576b0a077fbbd51d7 Mon Sep 17 00:00:00 2001 From: vyzo Date: Sat, 11 Mar 2023 16:49:07 +0200 Subject: [PATCH 022/243] scaffolding --- chain/index/interface.go | 31 +++++++++ chain/index/msgindex.go | 141 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 172 insertions(+) create mode 100644 chain/index/interface.go create mode 100644 chain/index/msgindex.go diff --git a/chain/index/interface.go b/chain/index/interface.go new file mode 100644 index 00000000000..ea701e81103 --- /dev/null +++ b/chain/index/interface.go @@ -0,0 +1,31 @@ +package index + +import ( + "context" + + "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/lotus/chain/types" + "github.com/ipfs/go-cid" +) + +// MsgInfo is the Message metadata the index tracks. +type MsgInfo struct { + // the message this record refers to + Message cid.Cid + // the epoch whre this message was executed + Epoch abi.ChainEpoch + // the tipset where this messages executed + Tipset types.TipSetKey + // the first block in the tipset where the message was executed + Block cid.Cid + // the index of the message in the block + Index int +} + +// MsgIndex is the interface to the message index +type MsgIndex interface { + // GetMsgInfo retrieves the message metadata through the index. + GetMsgInfo(ctx context.Context, m cid.Cid) (MsgInfo, error) + // Close closes the index + Close() error +} diff --git a/chain/index/msgindex.go b/chain/index/msgindex.go new file mode 100644 index 00000000000..c5bce4915f5 --- /dev/null +++ b/chain/index/msgindex.go @@ -0,0 +1,141 @@ +package index + +import ( + "context" + "database/sql" + "errors" + "io/fs" + "os" + "path" + "time" + + "github.com/ipfs/go-cid" + logging "github.com/ipfs/go-log/v2" + _ "github.com/mattn/go-sqlite3" + "golang.org/x/xerrors" + + "github.com/filecoin-project/lotus/chain/store" + "github.com/filecoin-project/lotus/chain/types" +) + +type msgIndex struct { + cs *store.ChainStore + + db *sql.DB +} + +var _ MsgIndex = (*msgIndex)(nil) + +var log = logging.Logger("chain/index") + +var ( + dbName = "msgindex.db" + + coalesceMinDelay = 100 * time.Millisecond + coalesceMaxDelay = time.Second + coalesceMergeInterval = 100 * time.Millisecond +) + +func NewMsgIndex(basePath string, cs *store.ChainStore) (MsgIndex, error) { + var ( + mkdb bool + dbPath string + err error + ) + + if basePath == ":memory:" { + // for testing + mkdb = true + dbPath = basePath + goto opendb + } + + err = os.MkdirAll(basePath, 0755) + if err != nil { + return nil, xerrors.Errorf("error creating msgindex base directory: %w", err) + } + + dbPath = path.Join(basePath, dbName) + _, err = os.Stat(dbPath) + switch { + case errors.Is(err, fs.ErrNotExist): + mkdb = true + + case err != nil: + return nil, xerrors.Errorf("error stating msgindex database: %w", err) + } + +opendb: + db, err := sql.Open("sqlite3", dbPath) + if err != nil { + // TODO [nice to have]: automaticaly delete corrupt databases + // but for now we can just error and let the operator delete. + return nil, xerrors.Errorf("error opening msgindex database: %w", err) + } + + if mkdb { + err = createTables(db) + if err != nil { + return nil, xerrors.Errorf("error creating msgindex database: %w", err) + } + } else { + err = reconcileIndex(db, cs) + if err != nil { + return nil, xerrors.Errorf("error reconciling msgindex database: %w", err) + } + } + + msgIndex := &msgIndex{db: db, cs: cs} + err = msgIndex.prepareStatements() + if err != nil { + err2 := db.Close() + if err2 != nil { + log.Errorf("error closing msgindex database: %s", err2) + } + + return nil, xerrors.Errorf("error preparing msgindex database statements: %w", err) + } + + rnf := store.WrapHeadChangeCoalescer( + msgIndex.onHeadChange, + coalesceMinDelay, + coalesceMaxDelay, + coalesceMergeInterval, + ) + cs.SubscribeHeadChanges(rnf) + + return msgIndex, nil +} + +// init utilities +func createTables(db *sql.DB) error { + // TODO + return errors.New("TODO: index.createTables") +} + +func reconcileIndex(db *sql.DB, cs *store.ChainStore) error { + // TODO + return errors.New("TODO: index.reconcileIndex") +} + +func (x *msgIndex) prepareStatements() error { + // TODO + return errors.New("TODO: msgIndex.prepareStatements") +} + +// head change notifee +func (x *msgIndex) onHeadChange(rev, app []*types.TipSet) error { + // TODO + return errors.New("TODO: msgIndex.onHeadChange") +} + +// interface +func (x *msgIndex) GetMsgInfo(ctx context.Context, m cid.Cid) (MsgInfo, error) { + // TODO + return MsgInfo{}, errors.New("TODO: msgIndex.GetMsgInfo") +} + +func (x *msgIndex) Close() error { + // TODO + return errors.New("TODO: msgIndex.Close") +} From cc83a7cd350a0124c9e0f6826ad9770798297e89 Mon Sep 17 00:00:00 2001 From: vyzo Date: Sat, 11 Mar 2023 17:12:28 +0200 Subject: [PATCH 023/243] use tipset cid instead of key for posterity. --- chain/index/interface.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/chain/index/interface.go b/chain/index/interface.go index ea701e81103..dcf8ff6fa1e 100644 --- a/chain/index/interface.go +++ b/chain/index/interface.go @@ -4,7 +4,6 @@ import ( "context" "github.com/filecoin-project/go-state-types/abi" - "github.com/filecoin-project/lotus/chain/types" "github.com/ipfs/go-cid" ) @@ -15,7 +14,7 @@ type MsgInfo struct { // the epoch whre this message was executed Epoch abi.ChainEpoch // the tipset where this messages executed - Tipset types.TipSetKey + Tipset cid.Cid // the first block in the tipset where the message was executed Block cid.Cid // the index of the message in the block From eab728c4e71d2936c6a6a2df2b0071f245bb1d06 Mon Sep 17 00:00:00 2001 From: vyzo Date: Sat, 11 Mar 2023 17:25:57 +0200 Subject: [PATCH 024/243] we don't need the block --- chain/index/interface.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/chain/index/interface.go b/chain/index/interface.go index dcf8ff6fa1e..3cb6794b96a 100644 --- a/chain/index/interface.go +++ b/chain/index/interface.go @@ -15,9 +15,7 @@ type MsgInfo struct { Epoch abi.ChainEpoch // the tipset where this messages executed Tipset cid.Cid - // the first block in the tipset where the message was executed - Block cid.Cid - // the index of the message in the block + // the canonical execution order of the message in the tipset Index int } From 9144f9cea794c0936eb6bcf17f88d2c8e90c29fa Mon Sep 17 00:00:00 2001 From: vyzo Date: Sat, 11 Mar 2023 17:55:48 +0200 Subject: [PATCH 025/243] abstract ChainStore interface --- chain/index/msgindex.go | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/chain/index/msgindex.go b/chain/index/msgindex.go index c5bce4915f5..a48d6deef21 100644 --- a/chain/index/msgindex.go +++ b/chain/index/msgindex.go @@ -18,8 +18,16 @@ import ( "github.com/filecoin-project/lotus/chain/types" ) +// chain store interface; we could use store.ChainStore directly, +// but this simplifies unit testing. +type ChainStore interface { + SubscribeHeadChanges(f store.ReorgNotifee) +} + +var _ ChainStore = (*store.ChainStore)(nil) + type msgIndex struct { - cs *store.ChainStore + cs ChainStore db *sql.DB } @@ -36,7 +44,7 @@ var ( coalesceMergeInterval = 100 * time.Millisecond ) -func NewMsgIndex(basePath string, cs *store.ChainStore) (MsgIndex, error) { +func NewMsgIndex(basePath string, cs ChainStore) (MsgIndex, error) { var ( mkdb bool dbPath string @@ -113,7 +121,7 @@ func createTables(db *sql.DB) error { return errors.New("TODO: index.createTables") } -func reconcileIndex(db *sql.DB, cs *store.ChainStore) error { +func reconcileIndex(db *sql.DB, cs ChainStore) error { // TODO return errors.New("TODO: index.reconcileIndex") } From 3649ae373b18ebe294ee6b5642f5bdc305393207 Mon Sep 17 00:00:00 2001 From: vyzo Date: Sat, 11 Mar 2023 18:21:16 +0200 Subject: [PATCH 026/243] implementation details --- chain/index/interface.go | 7 +++-- chain/index/msgindex.go | 62 ++++++++++++++++++++++++++++++++++------ 2 files changed, 59 insertions(+), 10 deletions(-) diff --git a/chain/index/interface.go b/chain/index/interface.go index 3cb6794b96a..a6c07d4be99 100644 --- a/chain/index/interface.go +++ b/chain/index/interface.go @@ -2,19 +2,22 @@ package index import ( "context" + "errors" "github.com/filecoin-project/go-state-types/abi" "github.com/ipfs/go-cid" ) +var ErrNotFound = errors.New("message not found") + // MsgInfo is the Message metadata the index tracks. type MsgInfo struct { // the message this record refers to Message cid.Cid + // the tipset where this messages was executed + Tipset cid.Cid // the epoch whre this message was executed Epoch abi.ChainEpoch - // the tipset where this messages executed - Tipset cid.Cid // the canonical execution order of the message in the tipset Index int } diff --git a/chain/index/msgindex.go b/chain/index/msgindex.go index a48d6deef21..07ff7586912 100644 --- a/chain/index/msgindex.go +++ b/chain/index/msgindex.go @@ -9,13 +9,14 @@ import ( "path" "time" - "github.com/ipfs/go-cid" logging "github.com/ipfs/go-log/v2" _ "github.com/mattn/go-sqlite3" "golang.org/x/xerrors" + "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/lotus/chain/store" "github.com/filecoin-project/lotus/chain/types" + "github.com/ipfs/go-cid" ) // chain store interface; we could use store.ChainStore directly, @@ -29,7 +30,9 @@ var _ ChainStore = (*store.ChainStore)(nil) type msgIndex struct { cs ChainStore - db *sql.DB + db *sql.DB + selectMsgStmt *sql.Stmt + deleteTipSetStmt *sql.Stmt } var _ MsgIndex = (*msgIndex)(nil) @@ -117,8 +120,13 @@ opendb: // init utilities func createTables(db *sql.DB) error { - // TODO - return errors.New("TODO: index.createTables") + // Just a single table for now; ghetto, but this an index so we denormalize to avoid joins. + if _, err := db.Exec("CREATE TABLE Messages (cid VARCHAR(80) PRIMARY KEY, tipset VARCHAR(80), xepoch INTEGER, xindex INTEGER)"); err != nil { + return err + } + + // TODO Should we add an index for tipset to speed up deletion on revert? + return nil } func reconcileIndex(db *sql.DB, cs ChainStore) error { @@ -127,8 +135,20 @@ func reconcileIndex(db *sql.DB, cs ChainStore) error { } func (x *msgIndex) prepareStatements() error { - // TODO - return errors.New("TODO: msgIndex.prepareStatements") + stmt, err := x.db.Prepare("SELECT (tipset, xepoch, xindex) FROM Messages WHERE cid = ?") + if err != nil { + return err + } + x.selectMsgStmt = stmt + + stmt, err = x.db.Prepare("DELETE FROM Messages WHERE tipset = ?") + if err != nil { + return err + } + x.deleteTipSetStmt = stmt + + // TODO reconciliation stmts + return nil } // head change notifee @@ -139,8 +159,34 @@ func (x *msgIndex) onHeadChange(rev, app []*types.TipSet) error { // interface func (x *msgIndex) GetMsgInfo(ctx context.Context, m cid.Cid) (MsgInfo, error) { - // TODO - return MsgInfo{}, errors.New("TODO: msgIndex.GetMsgInfo") + var ( + tipset string + epoch int64 + index int64 + ) + + key := m.String() + row := x.selectMsgStmt.QueryRow(key) + err := row.Scan(&tipset, &epoch, &index) + switch { + case err == sql.ErrNoRows: + return MsgInfo{}, ErrNotFound + + case err != nil: + return MsgInfo{}, xerrors.Errorf("error querying msgindex database: %w", err) + } + + tipsetCid, err := cid.Decode(tipset) + if err != nil { + return MsgInfo{}, xerrors.Errorf("error decoding tipset cid: %w", err) + } + + return MsgInfo{ + Message: m, + Tipset: tipsetCid, + Epoch: abi.ChainEpoch(epoch), + Index: int(index), + }, nil } func (x *msgIndex) Close() error { From 7fcf228bc48dac33d1391002017f502cfe760274 Mon Sep 17 00:00:00 2001 From: vyzo Date: Sat, 11 Mar 2023 18:23:05 +0200 Subject: [PATCH 027/243] better logger name --- chain/index/msgindex.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chain/index/msgindex.go b/chain/index/msgindex.go index 07ff7586912..accaf5fc9d0 100644 --- a/chain/index/msgindex.go +++ b/chain/index/msgindex.go @@ -37,7 +37,7 @@ type msgIndex struct { var _ MsgIndex = (*msgIndex)(nil) -var log = logging.Logger("chain/index") +var log = logging.Logger("msgindex") var ( dbName = "msgindex.db" From d97c6b2f69fbe65f1d570d2f8bd50d047d5f9f76 Mon Sep 17 00:00:00 2001 From: vyzo Date: Sat, 11 Mar 2023 19:11:08 +0200 Subject: [PATCH 028/243] more implementation --- chain/index/msgindex.go | 170 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 158 insertions(+), 12 deletions(-) diff --git a/chain/index/msgindex.go b/chain/index/msgindex.go index accaf5fc9d0..ff5ddbdd956 100644 --- a/chain/index/msgindex.go +++ b/chain/index/msgindex.go @@ -7,6 +7,7 @@ import ( "io/fs" "os" "path" + "sync" "time" logging "github.com/ipfs/go-log/v2" @@ -19,10 +20,21 @@ import ( "github.com/ipfs/go-cid" ) +var log = logging.Logger("msgindex") + +var ( + dbName = "msgindex.db" + + coalesceMinDelay = 100 * time.Millisecond + coalesceMaxDelay = time.Second + coalesceMergeInterval = 100 * time.Millisecond +) + // chain store interface; we could use store.ChainStore directly, // but this simplifies unit testing. type ChainStore interface { SubscribeHeadChanges(f store.ReorgNotifee) + MessagesForBlock(ctx context.Context, b *types.BlockHeader) ([]*types.Message, []*types.SignedMessage, error) } var _ ChainStore = (*store.ChainStore)(nil) @@ -32,20 +44,22 @@ type msgIndex struct { db *sql.DB selectMsgStmt *sql.Stmt + insertMsgStmt *sql.Stmt deleteTipSetStmt *sql.Stmt -} -var _ MsgIndex = (*msgIndex)(nil) + sema chan struct{} + mx sync.Mutex + pend []headChange -var log = logging.Logger("msgindex") + cancel func() +} -var ( - dbName = "msgindex.db" +var _ MsgIndex = (*msgIndex)(nil) - coalesceMinDelay = 100 * time.Millisecond - coalesceMaxDelay = time.Second - coalesceMergeInterval = 100 * time.Millisecond -) +type headChange struct { + rev []*types.TipSet + app []*types.TipSet +} func NewMsgIndex(basePath string, cs ChainStore) (MsgIndex, error) { var ( @@ -96,7 +110,15 @@ opendb: } } - msgIndex := &msgIndex{db: db, cs: cs} + ctx, cancel := context.WithCancel(context.Background()) + + msgIndex := &msgIndex{ + db: db, + cs: cs, + sema: make(chan struct{}, 1), + cancel: cancel, + } + err = msgIndex.prepareStatements() if err != nil { err2 := db.Close() @@ -115,6 +137,8 @@ opendb: ) cs.SubscribeHeadChanges(rnf) + go msgIndex.background(ctx) + return msgIndex, nil } @@ -141,6 +165,12 @@ func (x *msgIndex) prepareStatements() error { } x.selectMsgStmt = stmt + stmt, err = x.db.Prepare("INSERT INTO Messages VALUES (?, ?, ?, ?)") + if err != nil { + return err + } + x.insertMsgStmt = stmt + stmt, err = x.db.Prepare("DELETE FROM Messages WHERE tipset = ?") if err != nil { return err @@ -153,8 +183,124 @@ func (x *msgIndex) prepareStatements() error { // head change notifee func (x *msgIndex) onHeadChange(rev, app []*types.TipSet) error { - // TODO - return errors.New("TODO: msgIndex.onHeadChange") + // do it in the background to avoid blocking head change processing + x.mx.Lock() + x.pend = append(x.pend, headChange{rev: rev, app: app}) + // TODO log loudly if this is building backlog (it shouldn't but better be safe on this) + x.mx.Unlock() + + select { + case x.sema <- struct{}{}: + default: + } + + return nil +} + +func (x *msgIndex) background(ctx context.Context) { + for { + select { + case <-x.sema: + err := x.processHeadChanges(ctx) + if err != nil { + // TODO should we shut down the index altogether? we just log for now. + log.Errorf("error processing head change notifications: %s", err) + } + + case <-ctx.Done(): + return + } + } +} + +func (x *msgIndex) processHeadChanges(ctx context.Context) error { + x.mx.Lock() + pend := x.pend + x.pend = nil + x.mx.Unlock() + + txn, err := x.db.Begin() + if err != nil { + return xerrors.Errorf("error creating transaction: %w", err) + } + + for _, hc := range pend { + for _, ts := range hc.rev { + if err := x.doRevert(ctx, ts); err != nil { + txn.Rollback() + return xerrors.Errorf("error reverting %s: %w", ts, err) + } + } + + for _, ts := range hc.app { + if err := x.doApply(ctx, ts); err != nil { + txn.Rollback() + return xerrors.Errorf("error applying %s: %w", ts, err) + } + } + } + + return txn.Commit() +} + +func (x *msgIndex) doRevert(ctx context.Context, ts *types.TipSet) error { + tskey, err := ts.Key().Cid() + if err != nil { + return xerrors.Errorf("error computing tipset cid: %w", err) + } + + key := tskey.String() + _, err = x.deleteTipSetStmt.Exec(key) + return err +} + +func (x *msgIndex) doApply(ctx context.Context, ts *types.TipSet) error { + tscid, err := ts.Key().Cid() + if err != nil { + return xerrors.Errorf("error computing tipset cid: %w", err) + } + + tskey := tscid.String() + xepoch := int64(ts.Height()) + var xindex int64 + + seen := make(map[string]struct{}) + insert := func(key string) error { + if _, ok := seen[key]; ok { + return nil + } + + if _, err := x.insertMsgStmt.Exec(key, tskey, xepoch, xindex); err != nil { + return err + } + seen[key] = struct{}{} + xindex++ + + return nil + } + + for _, blk := range ts.Blocks() { + bmsgs, smsgs, err := x.cs.MessagesForBlock(ctx, blk) + if err != nil { + return xerrors.Errorf("error retrieving messages for block %s in %s: %w", blk, ts, err) + } + + for _, m := range bmsgs { + key := m.Cid().String() + if err := insert(key); err != nil { + return err + } + } + + for _, m := range smsgs { + key := m.Cid().String() + if err := insert(key); err != nil { + return err + } + } + } + + return nil } // interface From b5dd4e31acfd2b2202882d896d7a96d34745effd Mon Sep 17 00:00:00 2001 From: vyzo Date: Sat, 11 Mar 2023 21:26:11 +0200 Subject: [PATCH 029/243] implement reconciliation --- chain/index/msgindex.go | 74 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 71 insertions(+), 3 deletions(-) diff --git a/chain/index/msgindex.go b/chain/index/msgindex.go index ff5ddbdd956..704145b8947 100644 --- a/chain/index/msgindex.go +++ b/chain/index/msgindex.go @@ -35,6 +35,8 @@ var ( type ChainStore interface { SubscribeHeadChanges(f store.ReorgNotifee) MessagesForBlock(ctx context.Context, b *types.BlockHeader) ([]*types.Message, []*types.SignedMessage, error) + GetHeaviestTipSet() *types.TipSet + GetTipSetFromKey(ctx context.Context, tsk types.TipSetKey) (*types.TipSet, error) } var _ ChainStore = (*store.ChainStore)(nil) @@ -103,6 +105,8 @@ opendb: if err != nil { return nil, xerrors.Errorf("error creating msgindex database: %w", err) } + + // TODO we may consider populating the index in this case. } else { err = reconcileIndex(db, cs) if err != nil { @@ -154,8 +158,73 @@ func createTables(db *sql.DB) error { } func reconcileIndex(db *sql.DB, cs ChainStore) error { - // TODO - return errors.New("TODO: index.reconcileIndex") + // Invariant: after reconciliation, every tipset in the index is in the current chain; ie either + // the chain head or reachable by walking the chain. + // Algorithm: + // 1. Count mesages in index; if none, trivially reconciled. + // TODO we may consider populating the index in that case + // 2. Find the minimum tipset in the index; this will mark the end of the reconciliation walk + // 3. Walk from current tipset until we find a tipset in the index. + // 4. Delete (revert!) all tipsets above the found tipset. + // 5. If the walk ends in the boundary epoch, then delete everything. + // + + row := db.QueryRow("SELECT COUNT(*) FROM Messages") + + var result int64 + if err := row.Scan(&result); err != nil { + return xerrors.Errorf("error counting messages: %w", err) + } + + if result == 0 { + return nil + } + + row = db.QueryRow("SELECT MIN(xepoch) FROM Messages") + if err := row.Scan(&result); err != nil { + return xerrors.Errorf("error finding boundary epoch: %w", err) + } + + boundaryEpoch := abi.ChainEpoch(result) + + countMsgsStmt, err := db.Prepare("SELECT COUNT(*) FROM Messages WHERE tipset = ?") + if err != nil { + return xerrors.Errorf("error preparing statement: %w", err) + } + + curTs := cs.GetHeaviestTipSet() + for curTs != nil && curTs.Height() >= boundaryEpoch { + tsCid, err := curTs.Key().Cid() + if err != nil { + return xerrors.Errorf("error computing tipset cid: %w", err) + } + + key := tsCid.String() + row = countMsgsStmt.QueryRow(key) + if err := row.Scan(&result); err != nil { + return xerrors.Errorf("error counting messages: %w", err) + } + + if result > 0 { + // found it! + boundaryEpoch = curTs.Height() + 1 + break + } + + // walk up + parents := curTs.Parents() + curTs, err = cs.GetTipSetFromKey(context.TODO(), parents) + if err != nil { + return xerrors.Errorf("error walking chain: %w", err) + } + } + + // delete everything above the minEpoch + if _, err = db.Exec("DELETE FROM Messages WHERE xepoch >= ?", int64(boundaryEpoch)); err != nil { + return xerrors.Errorf("error deleting stale reorged out message: %w", err) + } + + return nil } func (x *msgIndex) prepareStatements() error { @@ -177,7 +246,6 @@ func (x *msgIndex) prepareStatements() error { } x.deleteTipSetStmt = stmt - // TODO reconciliation stmts return nil } From bf9ae23c988d17c029c023f1fcebf285ca9580b5 Mon Sep 17 00:00:00 2001 From: vyzo Date: Sat, 11 Mar 2023 22:09:31 +0200 Subject: [PATCH 030/243] implement Close --- chain/index/interface.go | 1 + chain/index/msgindex.go | 37 ++++++++++++++++++++++++++++++++++--- 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/chain/index/interface.go b/chain/index/interface.go index a6c07d4be99..f157741964b 100644 --- a/chain/index/interface.go +++ b/chain/index/interface.go @@ -9,6 +9,7 @@ import ( ) var ErrNotFound = errors.New("message not found") +var ErrClosed = errors.New("index closed") // MsgInfo is the Message metadata the index tracks. type MsgInfo struct { diff --git a/chain/index/msgindex.go b/chain/index/msgindex.go index 704145b8947..79f85af33d8 100644 --- a/chain/index/msgindex.go +++ b/chain/index/msgindex.go @@ -53,7 +53,10 @@ type msgIndex struct { mx sync.Mutex pend []headChange - cancel func() + cancel func() + workers sync.WaitGroup + closeLk sync.RWMutex + closed bool } var _ MsgIndex = (*msgIndex)(nil) @@ -141,6 +144,7 @@ opendb: ) cs.SubscribeHeadChanges(rnf) + msgIndex.workers.Add(1) go msgIndex.background(ctx) return msgIndex, nil @@ -251,6 +255,13 @@ func (x *msgIndex) prepareStatements() error { // head change notifee func (x *msgIndex) onHeadChange(rev, app []*types.TipSet) error { + x.closeLk.RLock() + defer x.closeLk.RUnlock() + + if x.closed { + return nil + } + // do it in the background to avoid blocking head change processing x.mx.Lock() x.pend = append(x.pend, headChange{rev: rev, app: app}) @@ -266,6 +277,8 @@ func (x *msgIndex) onHeadChange(rev, app []*types.TipSet) error { } func (x *msgIndex) background(ctx context.Context) { + defer x.workers.Done() + for { select { case <-x.sema: @@ -373,6 +386,13 @@ func (x *msgIndex) doApply(ctx context.Context, ts *types.TipSet) error { // interface func (x *msgIndex) GetMsgInfo(ctx context.Context, m cid.Cid) (MsgInfo, error) { + x.closeLk.RLock() + defer x.closeLk.RUnlock() + + if x.closed { + return MsgInfo{}, ErrClosed + } + var ( tipset string epoch int64 @@ -404,6 +424,17 @@ func (x *msgIndex) GetMsgInfo(ctx context.Context, m cid.Cid) (MsgInfo, error) { } func (x *msgIndex) Close() error { - // TODO - return errors.New("TODO: msgIndex.Close") + x.closeLk.Lock() + defer x.closeLk.Unlock() + + if x.closed { + return nil + } + + x.closed = true + + x.cancel() + x.workers.Wait() + + return x.db.Close() } From d9ca214309b96fef92f00a5e823ce3e5806e2c2a Mon Sep 17 00:00:00 2001 From: vyzo Date: Sun, 12 Mar 2023 13:21:03 +0200 Subject: [PATCH 031/243] test test test --- chain/index/interface.go | 2 +- chain/index/msgindex.go | 18 +-- chain/index/msgindex_test.go | 293 +++++++++++++++++++++++++++++++++++ 3 files changed, 299 insertions(+), 14 deletions(-) create mode 100644 chain/index/msgindex_test.go diff --git a/chain/index/interface.go b/chain/index/interface.go index f157741964b..8056cd662b4 100644 --- a/chain/index/interface.go +++ b/chain/index/interface.go @@ -16,7 +16,7 @@ type MsgInfo struct { // the message this record refers to Message cid.Cid // the tipset where this messages was executed - Tipset cid.Cid + TipSet cid.Cid // the epoch whre this message was executed Epoch abi.ChainEpoch // the canonical execution order of the message in the tipset diff --git a/chain/index/msgindex.go b/chain/index/msgindex.go index 79f85af33d8..b87484aa6a3 100644 --- a/chain/index/msgindex.go +++ b/chain/index/msgindex.go @@ -73,13 +73,6 @@ func NewMsgIndex(basePath string, cs ChainStore) (MsgIndex, error) { err error ) - if basePath == ":memory:" { - // for testing - mkdb = true - dbPath = basePath - goto opendb - } - err = os.MkdirAll(basePath, 0755) if err != nil { return nil, xerrors.Errorf("error creating msgindex base directory: %w", err) @@ -95,7 +88,6 @@ func NewMsgIndex(basePath string, cs ChainStore) (MsgIndex, error) { return nil, xerrors.Errorf("error stating msgindex database: %w", err) } -opendb: db, err := sql.Open("sqlite3", dbPath) if err != nil { // TODO [nice to have]: automaticaly delete corrupt databases @@ -232,21 +224,21 @@ func reconcileIndex(db *sql.DB, cs ChainStore) error { } func (x *msgIndex) prepareStatements() error { - stmt, err := x.db.Prepare("SELECT (tipset, xepoch, xindex) FROM Messages WHERE cid = ?") + stmt, err := x.db.Prepare("SELECT tipset, xepoch, xindex FROM Messages WHERE cid = ?") if err != nil { - return err + return xerrors.Errorf("prepare selectMsgStmt: %w", err) } x.selectMsgStmt = stmt stmt, err = x.db.Prepare("INSERT INTO Messages VALUES (?, ?, ?, ?)") if err != nil { - return err + return xerrors.Errorf("prepare insertMsgStmt: %w", err) } x.insertMsgStmt = stmt stmt, err = x.db.Prepare("DELETE FROM Messages WHERE tipset = ?") if err != nil { - return err + return xerrors.Errorf("prepare deleteTipSetStmt: %w", err) } x.deleteTipSetStmt = stmt @@ -417,7 +409,7 @@ func (x *msgIndex) GetMsgInfo(ctx context.Context, m cid.Cid) (MsgInfo, error) { return MsgInfo{ Message: m, - Tipset: tipsetCid, + TipSet: tipsetCid, Epoch: abi.ChainEpoch(epoch), Index: int(index), }, nil diff --git a/chain/index/msgindex_test.go b/chain/index/msgindex_test.go new file mode 100644 index 00000000000..f6b0a1c90d5 --- /dev/null +++ b/chain/index/msgindex_test.go @@ -0,0 +1,293 @@ +package index + +import ( + "context" + "errors" + "math/rand" + "os" + "testing" + "time" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/lotus/chain/store" + "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/chain/types/mock" + "github.com/ipfs/go-cid" + "github.com/ipfs/go-libipfs/blocks" + + "github.com/stretchr/testify/require" +) + +func TestBasicMsgIndex(t *testing.T) { + // the most basic of tests: + // 1. Create an index with mock chain store + // 2. Advance the chain for a few tipsets + // 3. Verify that the index contains all messages with the correct tipset/epoch + cs := newMockChainStore() + cs.genesis() + + tmp := t.TempDir() + t.Cleanup(func() { _ = os.RemoveAll(tmp) }) + + msgIndex, err := NewMsgIndex(tmp, cs) + require.NoError(t, err) + + defer msgIndex.Close() //nolint + + for i := 0; i < 10; i++ { + t.Logf("advance to epoch %d", i+1) + err := cs.advance() + require.NoError(t, err) + // wait for the coalescer to notify + time.Sleep(coalesceMinDelay + 10*time.Millisecond) + } + + t.Log("verifying index") + verifyIndex(t, cs, msgIndex) +} + +func TestReorgMsgIndex(t *testing.T) { + // slightly more nuanced test that includes reorgs + // 1. Create an index with mock chain store + // 2. Advance/Reorg the chain for a few tipsets + // 3. Verify that the index contains all messages with the correct tipst/epoch + cs := newMockChainStore() + cs.genesis() + + tmp := t.TempDir() + t.Cleanup(func() { _ = os.RemoveAll(tmp) }) + + msgIndex, err := NewMsgIndex(tmp, cs) + require.NoError(t, err) + + defer msgIndex.Close() //nolint + + for i := 0; i < 10; i++ { + t.Logf("advance to epoch %d", i+1) + err := cs.advance() + require.NoError(t, err) + // wait for the coalescer to notify + time.Sleep(coalesceMinDelay + 10*time.Millisecond) + } + + // a simple reorg + t.Log("doing reorg") + reorgme := cs.curTs + reorgmeParent, err := cs.GetTipSetFromKey(context.Background(), reorgme.Parents()) + require.NoError(t, err) + cs.setHead(reorgmeParent) + reorgmeChild := cs.makeBlk() + cs.reorg([]*types.TipSet{reorgme}, []*types.TipSet{reorgmeChild}) + time.Sleep(coalesceMinDelay + 10*time.Millisecond) + + t.Log("verifying index") + verifyIndex(t, cs, msgIndex) + + t.Log("verifying that reorged messages are not present") + verifyMissing(t, cs, msgIndex, reorgme) +} + +func TestReconcileMsgIndex(t *testing.T) { + // test that exercises the reconciliation code paths + // 1. Create and populate a basic msgindex, similar to TestBasicMsgIndex. + // 2. Close it + // 3. Reorg the mock chain store + // 4. Reopen the index to trigger reconciliation + // 5. Enxure that only the stable messages remain. + cs := newMockChainStore() + cs.genesis() + + tmp := t.TempDir() + t.Cleanup(func() { _ = os.RemoveAll(tmp) }) + + msgIndex, err := NewMsgIndex(tmp, cs) + require.NoError(t, err) + + for i := 0; i < 10; i++ { + t.Logf("advance to epoch %d", i+1) + err := cs.advance() + require.NoError(t, err) + // wait for the coalescer to notify + time.Sleep(coalesceMinDelay + 10*time.Millisecond) + } + + // Close it and reorg + err = msgIndex.Close() + require.NoError(t, err) + cs.notify = nil + + // a simple reorg + t.Log("doing reorg") + reorgme := cs.curTs + reorgmeParent, err := cs.GetTipSetFromKey(context.Background(), reorgme.Parents()) + require.NoError(t, err) + cs.setHead(reorgmeParent) + reorgmeChild := cs.makeBlk() + cs.reorg([]*types.TipSet{reorgme}, []*types.TipSet{reorgmeChild}) + + // reopen to reconcile + msgIndex, err = NewMsgIndex(tmp, cs) + require.NoError(t, err) + + defer msgIndex.Close() //nolint + + t.Log("verifying index") + // need to step one up because the last tipset is not known by the index + cs.setHead(reorgmeParent) + verifyIndex(t, cs, msgIndex) + + t.Log("verifying that reorged and unknown messages are not present") + verifyMissing(t, cs, msgIndex, reorgme, reorgmeChild) +} + +func verifyIndex(t *testing.T, cs *mockChainStore, msgIndex MsgIndex) { + for ts := cs.curTs; ts.Height() > 0; { + t.Logf("verify at height %d", ts.Height()) + blks := ts.Blocks() + if len(blks) == 0 { + break + } + + tsCid, err := ts.Key().Cid() + require.NoError(t, err) + + xindex := 0 + for _, blk := range blks { + msgs, _, _ := cs.MessagesForBlock(context.Background(), blk) + for _, m := range msgs { + minfo, err := msgIndex.GetMsgInfo(context.Background(), m.Cid()) + require.NoError(t, err) + require.Equal(t, tsCid, minfo.TipSet) + require.Equal(t, ts.Height(), minfo.Epoch) + require.Equal(t, xindex, minfo.Index) + xindex++ + } + } + + parents := ts.Parents() + ts, err = cs.GetTipSetFromKey(context.Background(), parents) + require.NoError(t, err) + } +} + +func verifyMissing(t *testing.T, cs *mockChainStore, msgIndex MsgIndex, missing ...*types.TipSet) { + for _, ts := range missing { + for _, blk := range ts.Blocks() { + msgs, _, _ := cs.MessagesForBlock(context.Background(), blk) + for _, m := range msgs { + _, err := msgIndex.GetMsgInfo(context.Background(), m.Cid()) + require.Equal(t, ErrNotFound, err) + } + } + } +} + +type mockChainStore struct { + notify store.ReorgNotifee + + curTs *types.TipSet + tipsets map[types.TipSetKey]*types.TipSet + blockMsgs map[cid.Cid][]*types.Message + + nonce uint64 +} + +var _ ChainStore = (*mockChainStore)(nil) + +var systemAddr address.Address +var rng *rand.Rand + +func init() { + systemAddr, _ = address.NewIDAddress(0) + rng = rand.New(rand.NewSource(314159)) +} + +func newMockChainStore() *mockChainStore { + return &mockChainStore{ + tipsets: make(map[types.TipSetKey]*types.TipSet), + blockMsgs: make(map[cid.Cid][]*types.Message), + } +} + +func (cs *mockChainStore) genesis() { + genBlock := mock.MkBlock(nil, 0, 0) + cs.blockMsgs[genBlock.Cid()] = nil + genTs := mock.TipSet(genBlock) + cs.setHead(genTs) +} + +func (cs *mockChainStore) setHead(ts *types.TipSet) { + cs.curTs = ts + cs.tipsets[ts.Key()] = ts +} + +func (cs *mockChainStore) advance() error { + ts := cs.makeBlk() + return cs.reorg(nil, []*types.TipSet{ts}) +} + +func (cs *mockChainStore) reorg(rev, app []*types.TipSet) error { + for _, ts := range rev { + parents := ts.Parents() + cs.curTs = cs.tipsets[parents] + } + + for _, ts := range app { + cs.tipsets[ts.Key()] = ts + cs.curTs = ts + } + + if cs.notify != nil { + return cs.notify(rev, app) + } + + return nil +} + +func (cs *mockChainStore) makeBlk() *types.TipSet { + height := cs.curTs.Height() + 1 + + blk := mock.MkBlock(cs.curTs, uint64(height), uint64(height)) + blk.Messages = cs.makeGarbageCid() + msg1 := cs.makeMsg() + msg2 := cs.makeMsg() + cs.blockMsgs[blk.Cid()] = []*types.Message{msg1, msg2} + + return mock.TipSet(blk) +} + +func (cs *mockChainStore) makeMsg() *types.Message { + nonce := cs.nonce + cs.nonce++ + return &types.Message{To: systemAddr, From: systemAddr, Nonce: nonce} +} + +func (cs *mockChainStore) makeGarbageCid() cid.Cid { + garbage := blocks.NewBlock([]byte{byte(rng.Intn(256)), byte(rng.Intn(256)), byte(rng.Intn(256))}) + return garbage.Cid() +} + +func (cs *mockChainStore) SubscribeHeadChanges(f store.ReorgNotifee) { + cs.notify = f +} + +func (cs *mockChainStore) MessagesForBlock(ctx context.Context, b *types.BlockHeader) ([]*types.Message, []*types.SignedMessage, error) { + msgs, ok := cs.blockMsgs[b.Cid()] + if !ok { + return nil, nil, errors.New("unknown block") + } + + return msgs, nil, nil +} + +func (cs *mockChainStore) GetHeaviestTipSet() *types.TipSet { + return cs.curTs +} + +func (cs *mockChainStore) GetTipSetFromKey(ctx context.Context, tsk types.TipSetKey) (*types.TipSet, error) { + ts, ok := cs.tipsets[tsk] + if !ok { + return nil, errors.New("unknown tipset") + } + return ts, nil +} From 0d274df977a8178225be2d4bbeefecee425495eb Mon Sep 17 00:00:00 2001 From: vyzo Date: Sun, 12 Mar 2023 13:35:50 +0200 Subject: [PATCH 032/243] use the transaction Luke! --- chain/index/msgindex.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/chain/index/msgindex.go b/chain/index/msgindex.go index b87484aa6a3..425d149cc00 100644 --- a/chain/index/msgindex.go +++ b/chain/index/msgindex.go @@ -292,42 +292,42 @@ func (x *msgIndex) processHeadChanges(ctx context.Context) error { x.pend = nil x.mx.Unlock() - txn, err := x.db.Begin() + tx, err := x.db.Begin() if err != nil { return xerrors.Errorf("error creating transaction: %w", err) } for _, hc := range pend { for _, ts := range hc.rev { - if err := x.doRevert(ctx, ts); err != nil { - txn.Rollback() + if err := x.doRevert(ctx, tx, ts); err != nil { + tx.Rollback() return xerrors.Errorf("error reverting %s: %w", ts, err) } } for _, ts := range hc.app { - if err := x.doApply(ctx, ts); err != nil { - txn.Rollback() + if err := x.doApply(ctx, tx, ts); err != nil { + tx.Rollback() return xerrors.Errorf("error applying %s: %w", ts, err) } } } - return txn.Commit() + return tx.Commit() } -func (x *msgIndex) doRevert(ctx context.Context, ts *types.TipSet) error { +func (x *msgIndex) doRevert(ctx context.Context, tx *sql.Tx, ts *types.TipSet) error { tskey, err := ts.Key().Cid() if err != nil { return xerrors.Errorf("error computing tipset cid: %w", err) } key := tskey.String() - _, err = x.deleteTipSetStmt.Exec(key) + _, err = tx.Stmt(x.deleteTipSetStmt).Exec(key) return err } -func (x *msgIndex) doApply(ctx context.Context, ts *types.TipSet) error { +func (x *msgIndex) doApply(ctx context.Context, tx *sql.Tx, ts *types.TipSet) error { tscid, err := ts.Key().Cid() if err != nil { return xerrors.Errorf("error computing tipset cid: %w", err) @@ -343,7 +343,7 @@ func (x *msgIndex) doApply(ctx context.Context, ts *types.TipSet) error { return nil } - if _, err := x.insertMsgStmt.Exec(key, tskey, xepoch, xindex); err != nil { + if _, err := tx.Stmt(x.insertMsgStmt).Exec(key, tskey, xepoch, xindex); err != nil { return err } seen[key] = struct{}{} From 171734ea31d317c2ca73e9df607ec18109f201c1 Mon Sep 17 00:00:00 2001 From: vyzo Date: Sun, 12 Mar 2023 15:25:07 +0200 Subject: [PATCH 033/243] hook the index into the rest of lotus --- chain/gen/gen.go | 3 ++- chain/index/interface.go | 12 ++++++++++++ chain/stmgr/searchwait.go | 31 ++++++++++++++++++++++++++++++- chain/stmgr/stmgr.go | 10 +++++++--- cmd/lotus/daemon.go | 3 ++- node/builder_chain.go | 2 ++ node/modules/chain.go | 3 ++- node/modules/msgindex.go | 31 +++++++++++++++++++++++++++++++ node/modules/stmgr.go | 5 +++-- 9 files changed, 91 insertions(+), 9 deletions(-) create mode 100644 node/modules/msgindex.go diff --git a/chain/gen/gen.go b/chain/gen/gen.go index de2df97c270..0da8e8318de 100644 --- a/chain/gen/gen.go +++ b/chain/gen/gen.go @@ -34,6 +34,7 @@ import ( "github.com/filecoin-project/lotus/chain/consensus" "github.com/filecoin-project/lotus/chain/consensus/filcns" genesis2 "github.com/filecoin-project/lotus/chain/gen/genesis" + "github.com/filecoin-project/lotus/chain/index" "github.com/filecoin-project/lotus/chain/rand" "github.com/filecoin-project/lotus/chain/stmgr" "github.com/filecoin-project/lotus/chain/store" @@ -256,7 +257,7 @@ func NewGeneratorWithSectorsAndUpgradeSchedule(numSectors int, us stmgr.UpgradeS //return nil, xerrors.Errorf("creating drand beacon: %w", err) //} - sm, err := stmgr.NewStateManager(cs, consensus.NewTipSetExecutor(filcns.RewardFunc), sys, us, beac, ds) + sm, err := stmgr.NewStateManager(cs, consensus.NewTipSetExecutor(filcns.RewardFunc), sys, us, beac, ds, index.DummyMsgIndex) if err != nil { return nil, xerrors.Errorf("initing stmgr: %w", err) } diff --git a/chain/index/interface.go b/chain/index/interface.go index 8056cd662b4..c45a693610b 100644 --- a/chain/index/interface.go +++ b/chain/index/interface.go @@ -30,3 +30,15 @@ type MsgIndex interface { // Close closes the index Close() error } + +type dummyMsgIndex struct{} + +func (_ dummyMsgIndex) GetMsgInfo(ctx context.Context, m cid.Cid) (MsgInfo, error) { + return MsgInfo{}, ErrNotFound +} + +func (_ dummyMsgIndex) Close() error { + return nil +} + +var DummyMsgIndex MsgIndex = dummyMsgIndex{} diff --git a/chain/stmgr/searchwait.go b/chain/stmgr/searchwait.go index 468f33db7df..27a30461108 100644 --- a/chain/stmgr/searchwait.go +++ b/chain/stmgr/searchwait.go @@ -10,6 +10,7 @@ import ( "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/lotus/chain/index" "github.com/filecoin-project/lotus/chain/store" "github.com/filecoin-project/lotus/chain/types" ) @@ -145,7 +146,20 @@ func (sm *StateManager) SearchForMessage(ctx context.Context, head *types.TipSet return head, r, foundMsg, nil } - fts, r, foundMsg, err := sm.searchBackForMsg(ctx, head, msg, lookbackLimit, allowReplaced) + fts, r, foundMsg, err := sm.searchForIndexedMsg(ctx, mcid, msg) + + switch { + case err == nil: + return fts, r, foundMsg, nil + + case errors.Is(err, index.ErrNotFound): + // ok for the index to have incomplete data + + default: + log.Warnf("error searching message index: %s", err) + } + + fts, r, foundMsg, err = sm.searchBackForMsg(ctx, head, msg, lookbackLimit, allowReplaced) if err != nil { log.Warnf("failed to look back through chain for message %s", mcid) @@ -159,6 +173,21 @@ func (sm *StateManager) SearchForMessage(ctx context.Context, head *types.TipSet return fts, r, foundMsg, nil } +func (sm *StateManager) searchForIndexedMsg(ctx context.Context, mcid cid.Cid, m types.ChainMsg) (*types.TipSet, *types.MessageReceipt, cid.Cid, error) { + minfo, err := sm.msgIndex.GetMsgInfo(ctx, mcid) + if err != nil { + return nil, nil, cid.Undef, err + } + + ts, err := sm.cs.GetTipSetByCid(ctx, minfo.TipSet) + if err != nil { + return nil, nil, cid.Undef, err + } + + r, foundMsg, err := sm.tipsetExecutedMessage(ctx, ts, mcid, m.VMMessage(), false) + return ts, r, foundMsg, err +} + // searchBackForMsg searches up to limit tipsets backwards from the given // tipset for a message receipt. // If limit is diff --git a/chain/stmgr/stmgr.go b/chain/stmgr/stmgr.go index b9f8d81bf22..827aeeee571 100644 --- a/chain/stmgr/stmgr.go +++ b/chain/stmgr/stmgr.go @@ -25,6 +25,7 @@ import ( "github.com/filecoin-project/lotus/chain/actors/builtin/paych" "github.com/filecoin-project/lotus/chain/actors/policy" "github.com/filecoin-project/lotus/chain/beacon" + "github.com/filecoin-project/lotus/chain/index" "github.com/filecoin-project/lotus/chain/rand" "github.com/filecoin-project/lotus/chain/state" "github.com/filecoin-project/lotus/chain/store" @@ -136,6 +137,8 @@ type StateManager struct { tsExec Executor tsExecMonitor ExecMonitor beacon beacon.Schedule + + msgIndex index.MsgIndex } // Caches a single state tree @@ -144,7 +147,7 @@ type treeCache struct { tree *state.StateTree } -func NewStateManager(cs *store.ChainStore, exec Executor, sys vm.SyscallBuilder, us UpgradeSchedule, beacon beacon.Schedule, metadataDs dstore.Batching) (*StateManager, error) { +func NewStateManager(cs *store.ChainStore, exec Executor, sys vm.SyscallBuilder, us UpgradeSchedule, beacon beacon.Schedule, metadataDs dstore.Batching, msgIndex index.MsgIndex) (*StateManager, error) { // If we have upgrades, make sure they're in-order and make sense. if err := us.Validate(); err != nil { return nil, err @@ -199,11 +202,12 @@ func NewStateManager(cs *store.ChainStore, exec Executor, sys vm.SyscallBuilder, tree: nil, }, compWait: make(map[string]chan struct{}), + msgIndex: msgIndex, }, nil } -func NewStateManagerWithUpgradeScheduleAndMonitor(cs *store.ChainStore, exec Executor, sys vm.SyscallBuilder, us UpgradeSchedule, b beacon.Schedule, em ExecMonitor, metadataDs dstore.Batching) (*StateManager, error) { - sm, err := NewStateManager(cs, exec, sys, us, b, metadataDs) +func NewStateManagerWithUpgradeScheduleAndMonitor(cs *store.ChainStore, exec Executor, sys vm.SyscallBuilder, us UpgradeSchedule, b beacon.Schedule, em ExecMonitor, metadataDs dstore.Batching, msgIndex index.MsgIndex) (*StateManager, error) { + sm, err := NewStateManager(cs, exec, sys, us, b, metadataDs, msgIndex) if err != nil { return nil, err } diff --git a/cmd/lotus/daemon.go b/cmd/lotus/daemon.go index b3341bd7991..585d4b2ceaa 100644 --- a/cmd/lotus/daemon.go +++ b/cmd/lotus/daemon.go @@ -35,6 +35,7 @@ import ( "github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/chain/consensus" "github.com/filecoin-project/lotus/chain/consensus/filcns" + "github.com/filecoin-project/lotus/chain/index" "github.com/filecoin-project/lotus/chain/stmgr" "github.com/filecoin-project/lotus/chain/store" "github.com/filecoin-project/lotus/chain/types" @@ -540,7 +541,7 @@ func ImportChain(ctx context.Context, r repo.Repo, fname string, snapshot bool) } // TODO: We need to supply the actual beacon after v14 - stm, err := stmgr.NewStateManager(cst, consensus.NewTipSetExecutor(filcns.RewardFunc), vm.Syscalls(ffiwrapper.ProofVerifier), filcns.DefaultUpgradeSchedule(), nil, mds) + stm, err := stmgr.NewStateManager(cst, consensus.NewTipSetExecutor(filcns.RewardFunc), vm.Syscalls(ffiwrapper.ProofVerifier), filcns.DefaultUpgradeSchedule(), nil, mds, index.DummyMsgIndex) if err != nil { return err } diff --git a/node/builder_chain.go b/node/builder_chain.go index d334d782e3f..20817acff45 100644 --- a/node/builder_chain.go +++ b/node/builder_chain.go @@ -20,6 +20,7 @@ import ( "github.com/filecoin-project/lotus/chain/events" "github.com/filecoin-project/lotus/chain/exchange" "github.com/filecoin-project/lotus/chain/gen/slashfilter" + "github.com/filecoin-project/lotus/chain/index" "github.com/filecoin-project/lotus/chain/market" "github.com/filecoin-project/lotus/chain/messagepool" "github.com/filecoin-project/lotus/chain/messagesigner" @@ -79,6 +80,7 @@ var ChainNode = Options( Override(new(stmgr.Executor), consensus.NewTipSetExecutor(filcns.RewardFunc)), Override(new(consensus.Consensus), filcns.NewFilecoinExpectedConsensus), Override(new(*store.ChainStore), modules.ChainStore), + Override(new(index.MsgIndex), modules.MsgIndex), Override(new(*stmgr.StateManager), modules.StateManager), Override(new(dtypes.ChainBitswap), modules.ChainBitswap), Override(new(dtypes.ChainBlockService), modules.ChainBlockService), // todo: unused diff --git a/node/modules/chain.go b/node/modules/chain.go index 0c3bad2c779..762c77e4bf3 100644 --- a/node/modules/chain.go +++ b/node/modules/chain.go @@ -21,6 +21,7 @@ import ( "github.com/filecoin-project/lotus/chain/consensus/filcns" "github.com/filecoin-project/lotus/chain/exchange" "github.com/filecoin-project/lotus/chain/gen/slashfilter" + "github.com/filecoin-project/lotus/chain/index" "github.com/filecoin-project/lotus/chain/messagepool" "github.com/filecoin-project/lotus/chain/stmgr" "github.com/filecoin-project/lotus/chain/store" @@ -123,7 +124,7 @@ func NetworkName(mctx helpers.MetricsCtx, ctx := helpers.LifecycleCtx(mctx, lc) - sm, err := stmgr.NewStateManager(cs, tsexec, syscalls, us, nil, nil) + sm, err := stmgr.NewStateManager(cs, tsexec, syscalls, us, nil, nil, index.DummyMsgIndex) if err != nil { return "", err } diff --git a/node/modules/msgindex.go b/node/modules/msgindex.go new file mode 100644 index 00000000000..296ecde496b --- /dev/null +++ b/node/modules/msgindex.go @@ -0,0 +1,31 @@ +package modules + +import ( + "context" + + "go.uber.org/fx" + + "github.com/filecoin-project/lotus/chain/index" + "github.com/filecoin-project/lotus/chain/store" + "github.com/filecoin-project/lotus/node/repo" +) + +func MsgIndex(lc fx.Lifecycle, cs *store.ChainStore, r repo.LockedRepo) (index.MsgIndex, error) { + basePath, err := r.SqlitePath() + if err != nil { + return nil, err + } + + msgIndex, err := index.NewMsgIndex(basePath, cs) + if err != nil { + return nil, err + } + + lc.Append(fx.Hook{ + OnStop: func(_ context.Context) error { + return msgIndex.Close() + }, + }) + + return msgIndex, nil +} diff --git a/node/modules/stmgr.go b/node/modules/stmgr.go index b8f6f47766e..f3eaee219c5 100644 --- a/node/modules/stmgr.go +++ b/node/modules/stmgr.go @@ -4,14 +4,15 @@ import ( "go.uber.org/fx" "github.com/filecoin-project/lotus/chain/beacon" + "github.com/filecoin-project/lotus/chain/index" "github.com/filecoin-project/lotus/chain/stmgr" "github.com/filecoin-project/lotus/chain/store" "github.com/filecoin-project/lotus/chain/vm" "github.com/filecoin-project/lotus/node/modules/dtypes" ) -func StateManager(lc fx.Lifecycle, cs *store.ChainStore, exec stmgr.Executor, sys vm.SyscallBuilder, us stmgr.UpgradeSchedule, b beacon.Schedule, metadataDs dtypes.MetadataDS) (*stmgr.StateManager, error) { - sm, err := stmgr.NewStateManager(cs, exec, sys, us, b, metadataDs) +func StateManager(lc fx.Lifecycle, cs *store.ChainStore, exec stmgr.Executor, sys vm.SyscallBuilder, us stmgr.UpgradeSchedule, b beacon.Schedule, metadataDs dtypes.MetadataDS, msgIndex index.MsgIndex) (*stmgr.StateManager, error) { + sm, err := stmgr.NewStateManager(cs, exec, sys, us, b, metadataDs, msgIndex) if err != nil { return nil, err } From 0077fa2a98bf3104525de5c3e55e9b84397a2229 Mon Sep 17 00:00:00 2001 From: vyzo Date: Sun, 12 Mar 2023 15:30:05 +0200 Subject: [PATCH 034/243] make gen --- chain/index/interface.go | 3 ++- chain/index/msgindex.go | 3 ++- chain/index/msgindex_test.go | 9 +++++---- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/chain/index/interface.go b/chain/index/interface.go index c45a693610b..bdc8d5e929f 100644 --- a/chain/index/interface.go +++ b/chain/index/interface.go @@ -4,8 +4,9 @@ import ( "context" "errors" - "github.com/filecoin-project/go-state-types/abi" "github.com/ipfs/go-cid" + + "github.com/filecoin-project/go-state-types/abi" ) var ErrNotFound = errors.New("message not found") diff --git a/chain/index/msgindex.go b/chain/index/msgindex.go index 425d149cc00..b89d605c9f2 100644 --- a/chain/index/msgindex.go +++ b/chain/index/msgindex.go @@ -10,14 +10,15 @@ import ( "sync" "time" + "github.com/ipfs/go-cid" logging "github.com/ipfs/go-log/v2" _ "github.com/mattn/go-sqlite3" "golang.org/x/xerrors" "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/lotus/chain/store" "github.com/filecoin-project/lotus/chain/types" - "github.com/ipfs/go-cid" ) var log = logging.Logger("msgindex") diff --git a/chain/index/msgindex_test.go b/chain/index/msgindex_test.go index f6b0a1c90d5..7ed58d82dae 100644 --- a/chain/index/msgindex_test.go +++ b/chain/index/msgindex_test.go @@ -8,14 +8,15 @@ import ( "testing" "time" + "github.com/ipfs/go-cid" + "github.com/ipfs/go-libipfs/blocks" + "github.com/stretchr/testify/require" + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/lotus/chain/store" "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/types/mock" - "github.com/ipfs/go-cid" - "github.com/ipfs/go-libipfs/blocks" - - "github.com/stretchr/testify/require" ) func TestBasicMsgIndex(t *testing.T) { From 14153919889d6aa00de391464ed36b788763681b Mon Sep 17 00:00:00 2001 From: vyzo Date: Sun, 12 Mar 2023 15:33:36 +0200 Subject: [PATCH 035/243] lint lint lint --- chain/index/interface.go | 4 ++-- chain/index/msgindex.go | 4 ++-- chain/index/msgindex_test.go | 6 ++++-- chain/stmgr/forks_test.go | 10 +++++++--- chain/store/store_test.go | 3 ++- cmd/lotus-bench/import.go | 3 ++- cmd/lotus-shed/balances.go | 5 +++-- cmd/lotus-shed/gas-estimation.go | 5 +++-- cmd/lotus-shed/invariants.go | 3 ++- cmd/lotus-shed/migrations.go | 3 ++- cmd/lotus-shed/state-stats.go | 3 ++- cmd/lotus-sim/simulation/node.go | 3 ++- cmd/lotus-sim/simulation/simulation.go | 3 ++- conformance/driver.go | 3 ++- 14 files changed, 37 insertions(+), 21 deletions(-) diff --git a/chain/index/interface.go b/chain/index/interface.go index bdc8d5e929f..d212eba8804 100644 --- a/chain/index/interface.go +++ b/chain/index/interface.go @@ -34,11 +34,11 @@ type MsgIndex interface { type dummyMsgIndex struct{} -func (_ dummyMsgIndex) GetMsgInfo(ctx context.Context, m cid.Cid) (MsgInfo, error) { +func (dummyMsgIndex) GetMsgInfo(ctx context.Context, m cid.Cid) (MsgInfo, error) { return MsgInfo{}, ErrNotFound } -func (_ dummyMsgIndex) Close() error { +func (dummyMsgIndex) Close() error { return nil } diff --git a/chain/index/msgindex.go b/chain/index/msgindex.go index b89d605c9f2..2a28fd32bae 100644 --- a/chain/index/msgindex.go +++ b/chain/index/msgindex.go @@ -301,14 +301,14 @@ func (x *msgIndex) processHeadChanges(ctx context.Context) error { for _, hc := range pend { for _, ts := range hc.rev { if err := x.doRevert(ctx, tx, ts); err != nil { - tx.Rollback() + tx.Rollback() //nolint return xerrors.Errorf("error reverting %s: %w", ts, err) } } for _, ts := range hc.app { if err := x.doApply(ctx, tx, ts); err != nil { - tx.Rollback() + tx.Rollback() //nolint return xerrors.Errorf("error applying %s: %w", ts, err) } } diff --git a/chain/index/msgindex_test.go b/chain/index/msgindex_test.go index 7ed58d82dae..0bb767ab048 100644 --- a/chain/index/msgindex_test.go +++ b/chain/index/msgindex_test.go @@ -78,7 +78,8 @@ func TestReorgMsgIndex(t *testing.T) { require.NoError(t, err) cs.setHead(reorgmeParent) reorgmeChild := cs.makeBlk() - cs.reorg([]*types.TipSet{reorgme}, []*types.TipSet{reorgmeChild}) + err = cs.reorg([]*types.TipSet{reorgme}, []*types.TipSet{reorgmeChild}) + require.NoError(t, err) time.Sleep(coalesceMinDelay + 10*time.Millisecond) t.Log("verifying index") @@ -124,7 +125,8 @@ func TestReconcileMsgIndex(t *testing.T) { require.NoError(t, err) cs.setHead(reorgmeParent) reorgmeChild := cs.makeBlk() - cs.reorg([]*types.TipSet{reorgme}, []*types.TipSet{reorgmeChild}) + err = cs.reorg([]*types.TipSet{reorgme}, []*types.TipSet{reorgmeChild}) + require.NoError(t, err) // reopen to reconcile msgIndex, err = NewMsgIndex(tmp, cs) diff --git a/chain/stmgr/forks_test.go b/chain/stmgr/forks_test.go index a904172cdc4..f91d8997d6c 100644 --- a/chain/stmgr/forks_test.go +++ b/chain/stmgr/forks_test.go @@ -36,6 +36,7 @@ import ( "github.com/filecoin-project/lotus/chain/consensus" "github.com/filecoin-project/lotus/chain/consensus/filcns" "github.com/filecoin-project/lotus/chain/gen" + "github.com/filecoin-project/lotus/chain/index" "github.com/filecoin-project/lotus/chain/stmgr" . "github.com/filecoin-project/lotus/chain/stmgr" "github.com/filecoin-project/lotus/chain/types" @@ -168,7 +169,7 @@ func TestForkHeightTriggers(t *testing.T) { } return st.Flush(ctx) - }}}, cg.BeaconSchedule(), datastore.NewMapDatastore()) + }}}, cg.BeaconSchedule(), datastore.NewMapDatastore(), index.DummyMsgIndex) if err != nil { t.Fatal(err) } @@ -286,7 +287,7 @@ func testForkRefuseCall(t *testing.T, nullsBefore, nullsAfter int) { root cid.Cid, height abi.ChainEpoch, ts *types.TipSet) (cid.Cid, error) { migrationCount++ return root, nil - }}}, cg.BeaconSchedule(), datastore.NewMapDatastore()) + }}}, cg.BeaconSchedule(), datastore.NewMapDatastore(), index.DummyMsgIndex) if err != nil { t.Fatal(err) } @@ -504,7 +505,7 @@ func TestForkPreMigration(t *testing.T) { return nil }, }}}, - }, cg.BeaconSchedule(), datastore.NewMapDatastore()) + }, cg.BeaconSchedule(), datastore.NewMapDatastore(), index.DummyMsgIndex) if err != nil { t.Fatal(err) } @@ -579,6 +580,7 @@ func TestDisablePreMigration(t *testing.T) { }, cg.BeaconSchedule(), datastore.NewMapDatastore(), + index.DummyMsgIndex, ) require.NoError(t, err) require.NoError(t, sm.Start(context.Background())) @@ -633,6 +635,7 @@ func TestMigrtionCache(t *testing.T) { }, cg.BeaconSchedule(), metadataDs, + index.DummyMsgIndex, ) require.NoError(t, err) require.NoError(t, sm.Start(context.Background())) @@ -685,6 +688,7 @@ func TestMigrtionCache(t *testing.T) { }, cg.BeaconSchedule(), metadataDs, + index.DummyMsgIndex, ) require.NoError(t, err) sm.SetVMConstructor(func(ctx context.Context, vmopt *vm.VMOpts) (vm.Interface, error) { diff --git a/chain/store/store_test.go b/chain/store/store_test.go index cc72acc95bb..cea0fdc2a50 100644 --- a/chain/store/store_test.go +++ b/chain/store/store_test.go @@ -18,6 +18,7 @@ import ( "github.com/filecoin-project/lotus/chain/consensus" "github.com/filecoin-project/lotus/chain/consensus/filcns" "github.com/filecoin-project/lotus/chain/gen" + "github.com/filecoin-project/lotus/chain/index" "github.com/filecoin-project/lotus/chain/stmgr" "github.com/filecoin-project/lotus/chain/store" "github.com/filecoin-project/lotus/chain/types" @@ -214,7 +215,7 @@ func TestChainExportImportFull(t *testing.T) { t.Fatal("imported chain differed from exported chain") } - sm, err := stmgr.NewStateManager(cs, consensus.NewTipSetExecutor(filcns.RewardFunc), nil, filcns.DefaultUpgradeSchedule(), cg.BeaconSchedule(), ds) + sm, err := stmgr.NewStateManager(cs, consensus.NewTipSetExecutor(filcns.RewardFunc), nil, filcns.DefaultUpgradeSchedule(), cg.BeaconSchedule(), ds, index.DummyMsgIndex) if err != nil { t.Fatal(err) } diff --git a/cmd/lotus-bench/import.go b/cmd/lotus-bench/import.go index 51c567d90a0..44c152d0cb6 100644 --- a/cmd/lotus-bench/import.go +++ b/cmd/lotus-bench/import.go @@ -36,6 +36,7 @@ import ( badgerbs "github.com/filecoin-project/lotus/blockstore/badger" "github.com/filecoin-project/lotus/chain/consensus" "github.com/filecoin-project/lotus/chain/consensus/filcns" + "github.com/filecoin-project/lotus/chain/index" "github.com/filecoin-project/lotus/chain/stmgr" "github.com/filecoin-project/lotus/chain/store" "github.com/filecoin-project/lotus/chain/types" @@ -229,7 +230,7 @@ var importBenchCmd = &cli.Command{ defer cs.Close() //nolint:errcheck // TODO: We need to supply the actual beacon after v14 - stm, err := stmgr.NewStateManager(cs, consensus.NewTipSetExecutor(filcns.RewardFunc), vm.Syscalls(verifier), filcns.DefaultUpgradeSchedule(), nil, metadataDs) + stm, err := stmgr.NewStateManager(cs, consensus.NewTipSetExecutor(filcns.RewardFunc), vm.Syscalls(verifier), filcns.DefaultUpgradeSchedule(), nil, metadataDs, index.DummyMsgIndex) if err != nil { return err } diff --git a/cmd/lotus-shed/balances.go b/cmd/lotus-shed/balances.go index bae2815834a..28569cd1212 100644 --- a/cmd/lotus-shed/balances.go +++ b/cmd/lotus-shed/balances.go @@ -35,6 +35,7 @@ import ( "github.com/filecoin-project/lotus/chain/consensus" "github.com/filecoin-project/lotus/chain/consensus/filcns" "github.com/filecoin-project/lotus/chain/gen/genesis" + "github.com/filecoin-project/lotus/chain/index" "github.com/filecoin-project/lotus/chain/state" "github.com/filecoin-project/lotus/chain/stmgr" "github.com/filecoin-project/lotus/chain/store" @@ -513,7 +514,7 @@ var chainBalanceStateCmd = &cli.Command{ cst := cbor.NewCborStore(bs) store := adt.WrapStore(ctx, cst) - sm, err := stmgr.NewStateManager(cs, consensus.NewTipSetExecutor(filcns.RewardFunc), vm.Syscalls(ffiwrapper.ProofVerifier), filcns.DefaultUpgradeSchedule(), nil, mds) + sm, err := stmgr.NewStateManager(cs, consensus.NewTipSetExecutor(filcns.RewardFunc), vm.Syscalls(ffiwrapper.ProofVerifier), filcns.DefaultUpgradeSchedule(), nil, mds, index.DummyMsgIndex) if err != nil { return err } @@ -737,7 +738,7 @@ var chainPledgeCmd = &cli.Command{ cst := cbor.NewCborStore(bs) store := adt.WrapStore(ctx, cst) - sm, err := stmgr.NewStateManager(cs, consensus.NewTipSetExecutor(filcns.RewardFunc), vm.Syscalls(ffiwrapper.ProofVerifier), filcns.DefaultUpgradeSchedule(), nil, mds) + sm, err := stmgr.NewStateManager(cs, consensus.NewTipSetExecutor(filcns.RewardFunc), vm.Syscalls(ffiwrapper.ProofVerifier), filcns.DefaultUpgradeSchedule(), nil, mds, index.DummyMsgIndex) if err != nil { return err } diff --git a/cmd/lotus-shed/gas-estimation.go b/cmd/lotus-shed/gas-estimation.go index fe8428d1e79..7a5c35267ab 100644 --- a/cmd/lotus-shed/gas-estimation.go +++ b/cmd/lotus-shed/gas-estimation.go @@ -20,6 +20,7 @@ import ( "github.com/filecoin-project/lotus/chain/beacon/drand" "github.com/filecoin-project/lotus/chain/consensus" "github.com/filecoin-project/lotus/chain/consensus/filcns" + "github.com/filecoin-project/lotus/chain/index" "github.com/filecoin-project/lotus/chain/stmgr" "github.com/filecoin-project/lotus/chain/store" "github.com/filecoin-project/lotus/chain/types" @@ -111,7 +112,7 @@ var gasTraceCmd = &cli.Command{ cs := store.NewChainStore(bs, bs, mds, filcns.Weight, nil) defer cs.Close() //nolint:errcheck - sm, err := stmgr.NewStateManager(cs, consensus.NewTipSetExecutor(filcns.RewardFunc), vm.Syscalls(ffiwrapper.ProofVerifier), filcns.DefaultUpgradeSchedule(), shd, mds) + sm, err := stmgr.NewStateManager(cs, consensus.NewTipSetExecutor(filcns.RewardFunc), vm.Syscalls(ffiwrapper.ProofVerifier), filcns.DefaultUpgradeSchedule(), shd, mds, index.DummyMsgIndex) if err != nil { return err } @@ -212,7 +213,7 @@ var replayOfflineCmd = &cli.Command{ cs := store.NewChainStore(bs, bs, mds, filcns.Weight, nil) defer cs.Close() //nolint:errcheck - sm, err := stmgr.NewStateManager(cs, consensus.NewTipSetExecutor(filcns.RewardFunc), vm.Syscalls(ffiwrapper.ProofVerifier), filcns.DefaultUpgradeSchedule(), shd, mds) + sm, err := stmgr.NewStateManager(cs, consensus.NewTipSetExecutor(filcns.RewardFunc), vm.Syscalls(ffiwrapper.ProofVerifier), filcns.DefaultUpgradeSchedule(), shd, mds, index.DummyMsgIndex) if err != nil { return err } diff --git a/cmd/lotus-shed/invariants.go b/cmd/lotus-shed/invariants.go index b759e2c2c20..45ad43170b0 100644 --- a/cmd/lotus-shed/invariants.go +++ b/cmd/lotus-shed/invariants.go @@ -21,6 +21,7 @@ import ( "github.com/filecoin-project/lotus/chain/actors" "github.com/filecoin-project/lotus/chain/consensus" "github.com/filecoin-project/lotus/chain/consensus/filcns" + "github.com/filecoin-project/lotus/chain/index" "github.com/filecoin-project/lotus/chain/stmgr" "github.com/filecoin-project/lotus/chain/store" "github.com/filecoin-project/lotus/chain/types" @@ -90,7 +91,7 @@ var invariantsCmd = &cli.Command{ cs := store.NewChainStore(bs, bs, mds, filcns.Weight, nil) defer cs.Close() //nolint:errcheck - sm, err := stmgr.NewStateManager(cs, consensus.NewTipSetExecutor(filcns.RewardFunc), vm.Syscalls(ffiwrapper.ProofVerifier), filcns.DefaultUpgradeSchedule(), nil, mds) + sm, err := stmgr.NewStateManager(cs, consensus.NewTipSetExecutor(filcns.RewardFunc), vm.Syscalls(ffiwrapper.ProofVerifier), filcns.DefaultUpgradeSchedule(), nil, mds, index.DummyMsgIndex) if err != nil { return err } diff --git a/cmd/lotus-shed/migrations.go b/cmd/lotus-shed/migrations.go index b3d5da387d4..e2a66f0676a 100644 --- a/cmd/lotus-shed/migrations.go +++ b/cmd/lotus-shed/migrations.go @@ -41,6 +41,7 @@ import ( "github.com/filecoin-project/lotus/chain/actors/builtin/verifreg" "github.com/filecoin-project/lotus/chain/consensus" "github.com/filecoin-project/lotus/chain/consensus/filcns" + "github.com/filecoin-project/lotus/chain/index" "github.com/filecoin-project/lotus/chain/state" "github.com/filecoin-project/lotus/chain/stmgr" "github.com/filecoin-project/lotus/chain/store" @@ -123,7 +124,7 @@ var migrationsCmd = &cli.Command{ defer cs.Close() //nolint:errcheck // Note: we use a map datastore for the metadata to avoid writing / using cached migration results in the metadata store - sm, err := stmgr.NewStateManager(cs, consensus.NewTipSetExecutor(filcns.RewardFunc), vm.Syscalls(ffiwrapper.ProofVerifier), filcns.DefaultUpgradeSchedule(), nil, datastore.NewMapDatastore()) + sm, err := stmgr.NewStateManager(cs, consensus.NewTipSetExecutor(filcns.RewardFunc), vm.Syscalls(ffiwrapper.ProofVerifier), filcns.DefaultUpgradeSchedule(), nil, datastore.NewMapDatastore(), index.DummyMsgIndex) if err != nil { return err } diff --git a/cmd/lotus-shed/state-stats.go b/cmd/lotus-shed/state-stats.go index 521e32c7991..f6fb4166db4 100644 --- a/cmd/lotus-shed/state-stats.go +++ b/cmd/lotus-shed/state-stats.go @@ -26,6 +26,7 @@ import ( "github.com/filecoin-project/lotus/chain/actors/builtin" "github.com/filecoin-project/lotus/chain/consensus" "github.com/filecoin-project/lotus/chain/consensus/filcns" + "github.com/filecoin-project/lotus/chain/index" "github.com/filecoin-project/lotus/chain/stmgr" "github.com/filecoin-project/lotus/chain/store" "github.com/filecoin-project/lotus/chain/types" @@ -308,7 +309,7 @@ to reduce the number of decode operations performed by caching the decoded objec } tsExec := consensus.NewTipSetExecutor(filcns.RewardFunc) - sm, err := stmgr.NewStateManager(cs, tsExec, vm.Syscalls(ffiwrapper.ProofVerifier), filcns.DefaultUpgradeSchedule(), nil, mds) + sm, err := stmgr.NewStateManager(cs, tsExec, vm.Syscalls(ffiwrapper.ProofVerifier), filcns.DefaultUpgradeSchedule(), nil, mds, index.DummyMsgIndex) if err != nil { return err } diff --git a/cmd/lotus-sim/simulation/node.go b/cmd/lotus-sim/simulation/node.go index 9b37da6c8de..1d7786010fe 100644 --- a/cmd/lotus-sim/simulation/node.go +++ b/cmd/lotus-sim/simulation/node.go @@ -12,6 +12,7 @@ import ( "github.com/filecoin-project/lotus/blockstore" "github.com/filecoin-project/lotus/chain/consensus" "github.com/filecoin-project/lotus/chain/consensus/filcns" + "github.com/filecoin-project/lotus/chain/index" "github.com/filecoin-project/lotus/chain/stmgr" "github.com/filecoin-project/lotus/chain/store" "github.com/filecoin-project/lotus/chain/types" @@ -106,7 +107,7 @@ func (nd *Node) LoadSim(ctx context.Context, name string) (*Simulation, error) { if err != nil { return nil, xerrors.Errorf("failed to create upgrade schedule for simulation %s: %w", name, err) } - sim.StateManager, err = stmgr.NewStateManager(nd.Chainstore, consensus.NewTipSetExecutor(filcns.RewardFunc), vm.Syscalls(mock.Verifier), us, nil, nd.MetadataDS) + sim.StateManager, err = stmgr.NewStateManager(nd.Chainstore, consensus.NewTipSetExecutor(filcns.RewardFunc), vm.Syscalls(mock.Verifier), us, nil, nd.MetadataDS, index.DummyMsgIndex) if err != nil { return nil, xerrors.Errorf("failed to create state manager for simulation %s: %w", name, err) } diff --git a/cmd/lotus-sim/simulation/simulation.go b/cmd/lotus-sim/simulation/simulation.go index 294f4cfbc38..47d06aeda91 100644 --- a/cmd/lotus-sim/simulation/simulation.go +++ b/cmd/lotus-sim/simulation/simulation.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/index" "github.com/filecoin-project/lotus/chain/stmgr" "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/vm" @@ -201,7 +202,7 @@ func (sim *Simulation) SetUpgradeHeight(nv network.Version, epoch abi.ChainEpoch if err != nil { return err } - sm, err := stmgr.NewStateManager(sim.Node.Chainstore, consensus.NewTipSetExecutor(filcns.RewardFunc), vm.Syscalls(mock.Verifier), newUpgradeSchedule, nil, sim.Node.MetadataDS) + sm, err := stmgr.NewStateManager(sim.Node.Chainstore, consensus.NewTipSetExecutor(filcns.RewardFunc), vm.Syscalls(mock.Verifier), newUpgradeSchedule, nil, sim.Node.MetadataDS, index.DummyMsgIndex) if err != nil { return err } diff --git a/conformance/driver.go b/conformance/driver.go index 2680a7154df..e0d56d07410 100644 --- a/conformance/driver.go +++ b/conformance/driver.go @@ -22,6 +22,7 @@ import ( "github.com/filecoin-project/lotus/chain/actors/builtin" "github.com/filecoin-project/lotus/chain/consensus" "github.com/filecoin-project/lotus/chain/consensus/filcns" + "github.com/filecoin-project/lotus/chain/index" "github.com/filecoin-project/lotus/chain/state" "github.com/filecoin-project/lotus/chain/stmgr" "github.com/filecoin-project/lotus/chain/store" @@ -108,7 +109,7 @@ func (d *Driver) ExecuteTipset(bs blockstore.Blockstore, ds ds.Batching, params cs = store.NewChainStore(bs, bs, ds, filcns.Weight, nil) tse = consensus.NewTipSetExecutor(filcns.RewardFunc) - sm, err = stmgr.NewStateManager(cs, tse, syscalls, filcns.DefaultUpgradeSchedule(), nil, ds) + sm, err = stmgr.NewStateManager(cs, tse, syscalls, filcns.DefaultUpgradeSchedule(), nil, ds, index.DummyMsgIndex) ) if err != nil { return nil, err From 3b765a30d32d686fb23e74e833b339ea75ce354d Mon Sep 17 00:00:00 2001 From: vyzo Date: Sun, 12 Mar 2023 16:02:51 +0200 Subject: [PATCH 036/243] dummy index for itests --- node/builder.go | 2 ++ node/modules/msgindex.go | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/node/builder.go b/node/builder.go index 76c93cbc6c0..75162f5ea97 100644 --- a/node/builder.go +++ b/node/builder.go @@ -24,6 +24,7 @@ import ( "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/chain/beacon" + "github.com/filecoin-project/lotus/chain/index" "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/journal" "github.com/filecoin-project/lotus/journal/alerting" @@ -390,6 +391,7 @@ func Test() Option { Unset(new(*peermgr.PeerMgr)), Override(new(beacon.Schedule), testing.RandomBeacon), Override(new(*storageadapter.DealPublisher), storageadapter.NewDealPublisher(nil, storageadapter.PublishMsgConfig{})), + Override(new(index.MsgIndex), modules.DummyMsgIndex), ) } diff --git a/node/modules/msgindex.go b/node/modules/msgindex.go index 296ecde496b..23072ade134 100644 --- a/node/modules/msgindex.go +++ b/node/modules/msgindex.go @@ -29,3 +29,7 @@ func MsgIndex(lc fx.Lifecycle, cs *store.ChainStore, r repo.LockedRepo) (index.M return msgIndex, nil } + +func DummyMsgIndex() (index.MsgIndex, error) { + return index.DummyMsgIndex, nil +} From 88d7a4e610d957ba01c55d44320baced8ad5639e Mon Sep 17 00:00:00 2001 From: vyzo Date: Sun, 12 Mar 2023 16:12:14 +0200 Subject: [PATCH 037/243] more lint --- chain/index/msgindex.go | 2 +- cmd/lotus-sim/simulation/node.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/chain/index/msgindex.go b/chain/index/msgindex.go index 2a28fd32bae..78add0f793d 100644 --- a/chain/index/msgindex.go +++ b/chain/index/msgindex.go @@ -356,7 +356,7 @@ func (x *msgIndex) doApply(ctx context.Context, tx *sql.Tx, ts *types.TipSet) er for _, blk := range ts.Blocks() { bmsgs, smsgs, err := x.cs.MessagesForBlock(ctx, blk) if err != nil { - return xerrors.Errorf("error retrieving messages for block %s in %s: %w", blk, ts, err) + return xerrors.Errorf("error retrieving messages for block %s in %s: %w", blk.Cid(), ts, err) } for _, m := range bmsgs { diff --git a/cmd/lotus-sim/simulation/node.go b/cmd/lotus-sim/simulation/node.go index 1d7786010fe..f232e0d21c0 100644 --- a/cmd/lotus-sim/simulation/node.go +++ b/cmd/lotus-sim/simulation/node.go @@ -126,7 +126,7 @@ func (nd *Node) CreateSim(ctx context.Context, name string, head *types.TipSet) if err != nil { return nil, err } - sm, err := stmgr.NewStateManager(nd.Chainstore, consensus.NewTipSetExecutor(filcns.RewardFunc), vm.Syscalls(mock.Verifier), filcns.DefaultUpgradeSchedule(), nil, nd.MetadataDS) + sm, err := stmgr.NewStateManager(nd.Chainstore, consensus.NewTipSetExecutor(filcns.RewardFunc), vm.Syscalls(mock.Verifier), filcns.DefaultUpgradeSchedule(), nil, nd.MetadataDS, index.DummyMsgIndex) if err != nil { return nil, xerrors.Errorf("creating state manager: %w", err) } From df6dfdf8a984bf0ad8a65927f54c5107af4b2e1b Mon Sep 17 00:00:00 2001 From: vyzo Date: Mon, 13 Mar 2023 05:51:24 +0200 Subject: [PATCH 038/243] refactor database - drop the execution index; we don't need it - it is inclusion tipset - use MessagesForTipset - hoist db sql stuffs on top for clarity - add index for tipset on messages --- chain/index/interface.go | 6 +- chain/index/msgindex.go | 130 ++++++++++++++++++++------------------- 2 files changed, 70 insertions(+), 66 deletions(-) diff --git a/chain/index/interface.go b/chain/index/interface.go index d212eba8804..ff46ecad726 100644 --- a/chain/index/interface.go +++ b/chain/index/interface.go @@ -16,12 +16,10 @@ var ErrClosed = errors.New("index closed") type MsgInfo struct { // the message this record refers to Message cid.Cid - // the tipset where this messages was executed + // the tipset where this messages was included TipSet cid.Cid - // the epoch whre this message was executed + // the epoch whre this message was included Epoch abi.ChainEpoch - // the canonical execution order of the message in the tipset - Index int } // MsgIndex is the interface to the message index diff --git a/chain/index/msgindex.go b/chain/index/msgindex.go index 78add0f793d..d2686133cde 100644 --- a/chain/index/msgindex.go +++ b/chain/index/msgindex.go @@ -23,9 +23,36 @@ import ( var log = logging.Logger("msgindex") -var ( - dbName = "msgindex.db" +var dbName = "msgindex.db" +var dbDefs = []string{ + `CREATE TABLE IF NOT EXISTS messages ( + cid VARCHAR(80) PRIMARY KEY, + tipset_cid VARCHAR(80) NOT NULL, + epoch INTEGER NOT NULL + )`, + `CREATE INDEX IF NOT EXISTS tipset_cids ON messages (tipset_cid) + `, + `CREATE TABLE IF NOT EXISTS _meta ( + version UINT64 NOT NULL UNIQUE + )`, + `INSERT OR IGNORE INTO _meta (version) VALUES (1)`, +} +var dbPragmas = []string{} + +const ( + // prepared stmts + dbqGetMessageInfo = "SELECT tipset_cid, epoch FROM messages WHERE cid = ?" + dbqInsertMessage = "INSERT INTO messages VALUES (?, ?, ?)" + dbqDeleteTipsetMessages = "DELETE FROM messages WHERE tipset_cid = ?" + // reconciliation + dbqCountMessages = "SELECT COUNT(*) FROM messages" + dbqMinEpoch = "SELECT MIN(epoch) FROM messages" + dbqCountTipsetMessages = "SELECT COUNT(*) FROM messages WHERE tipset_cid = ?" + dbqDeleteMessagesByEpoch = "DELETE FROM messages WHERE epoch >= ?" +) +// coalescer configuration (TODO: use observer instead) +var ( coalesceMinDelay = 100 * time.Millisecond coalesceMaxDelay = time.Second coalesceMergeInterval = 100 * time.Millisecond @@ -35,7 +62,7 @@ var ( // but this simplifies unit testing. type ChainStore interface { SubscribeHeadChanges(f store.ReorgNotifee) - MessagesForBlock(ctx context.Context, b *types.BlockHeader) ([]*types.Message, []*types.SignedMessage, error) + MessagesForTipset(ctx context.Context, ts *types.TipSet) ([]types.ChainMsg, error) GetHeaviestTipSet() *types.TipSet GetTipSetFromKey(ctx context.Context, tsk types.TipSetKey) (*types.TipSet, error) } @@ -69,8 +96,8 @@ type headChange struct { func NewMsgIndex(basePath string, cs ChainStore) (MsgIndex, error) { var ( - mkdb bool dbPath string + exists bool err error ) @@ -82,8 +109,10 @@ func NewMsgIndex(basePath string, cs ChainStore) (MsgIndex, error) { dbPath = path.Join(basePath, dbName) _, err = os.Stat(dbPath) switch { + case err == nil: + exists = true + case errors.Is(err, fs.ErrNotExist): - mkdb = true case err != nil: return nil, xerrors.Errorf("error stating msgindex database: %w", err) @@ -96,16 +125,13 @@ func NewMsgIndex(basePath string, cs ChainStore) (MsgIndex, error) { return nil, xerrors.Errorf("error opening msgindex database: %w", err) } - if mkdb { - err = createTables(db) - if err != nil { - return nil, xerrors.Errorf("error creating msgindex database: %w", err) - } + if err := prepareDB(db); err != nil { + return nil, xerrors.Errorf("error creating msgindex database: %w", err) + } - // TODO we may consider populating the index in this case. - } else { - err = reconcileIndex(db, cs) - if err != nil { + // TODO we may consider populating the index when first creating the db + if exists { + if err := reconcileIndex(db, cs); err != nil { return nil, xerrors.Errorf("error reconciling msgindex database: %w", err) } } @@ -144,13 +170,19 @@ func NewMsgIndex(basePath string, cs ChainStore) (MsgIndex, error) { } // init utilities -func createTables(db *sql.DB) error { - // Just a single table for now; ghetto, but this an index so we denormalize to avoid joins. - if _, err := db.Exec("CREATE TABLE Messages (cid VARCHAR(80) PRIMARY KEY, tipset VARCHAR(80), xepoch INTEGER, xindex INTEGER)"); err != nil { - return err +func prepareDB(db *sql.DB) error { + for _, stmt := range dbDefs { + if _, err := db.Exec(stmt); err != nil { + return xerrors.Errorf("error executing sql statement '%s': %w", stmt, err) + } + } + + for _, stmt := range dbPragmas { + if _, err := db.Exec(stmt); err != nil { + return xerrors.Errorf("error executing sql statement '%s': %w", stmt, err) + } } - // TODO Should we add an index for tipset to speed up deletion on revert? return nil } @@ -166,7 +198,7 @@ func reconcileIndex(db *sql.DB, cs ChainStore) error { // 5. If the walk ends in the boundary epoch, then delete everything. // - row := db.QueryRow("SELECT COUNT(*) FROM Messages") + row := db.QueryRow(dbqCountMessages) var result int64 if err := row.Scan(&result); err != nil { @@ -177,14 +209,14 @@ func reconcileIndex(db *sql.DB, cs ChainStore) error { return nil } - row = db.QueryRow("SELECT MIN(xepoch) FROM Messages") + row = db.QueryRow(dbqMinEpoch) if err := row.Scan(&result); err != nil { return xerrors.Errorf("error finding boundary epoch: %w", err) } boundaryEpoch := abi.ChainEpoch(result) - countMsgsStmt, err := db.Prepare("SELECT COUNT(*) FROM Messages WHERE tipset = ?") + countMsgsStmt, err := db.Prepare(dbqCountTipsetMessages) if err != nil { return xerrors.Errorf("error preparing statement: %w", err) } @@ -217,7 +249,7 @@ func reconcileIndex(db *sql.DB, cs ChainStore) error { } // delete everything above the minEpoch - if _, err = db.Exec("DELETE FROM Messages WHERE xepoch >= ?", int64(boundaryEpoch)); err != nil { + if _, err = db.Exec(dbqDeleteMessagesByEpoch, int64(boundaryEpoch)); err != nil { return xerrors.Errorf("error deleting stale reorged out message: %w", err) } @@ -225,19 +257,19 @@ func reconcileIndex(db *sql.DB, cs ChainStore) error { } func (x *msgIndex) prepareStatements() error { - stmt, err := x.db.Prepare("SELECT tipset, xepoch, xindex FROM Messages WHERE cid = ?") + stmt, err := x.db.Prepare(dbqGetMessageInfo) if err != nil { return xerrors.Errorf("prepare selectMsgStmt: %w", err) } x.selectMsgStmt = stmt - stmt, err = x.db.Prepare("INSERT INTO Messages VALUES (?, ?, ?, ?)") + stmt, err = x.db.Prepare(dbqInsertMessage) if err != nil { return xerrors.Errorf("prepare insertMsgStmt: %w", err) } x.insertMsgStmt = stmt - stmt, err = x.db.Prepare("DELETE FROM Messages WHERE tipset = ?") + stmt, err = x.db.Prepare(dbqDeleteTipsetMessages) if err != nil { return xerrors.Errorf("prepare deleteTipSetStmt: %w", err) } @@ -335,42 +367,18 @@ func (x *msgIndex) doApply(ctx context.Context, tx *sql.Tx, ts *types.TipSet) er } tskey := tscid.String() - xepoch := int64(ts.Height()) - var xindex int64 + epoch := int64(ts.Height()) - seen := make(map[string]struct{}) - insert := func(key string) error { - if _, ok := seen[key]; ok { - return nil - } - - if _, err := tx.Stmt(x.insertMsgStmt).Exec(key, tskey, xepoch, xindex); err != nil { - return err - } - seen[key] = struct{}{} - xindex++ - - return nil + msgs, err := x.cs.MessagesForTipset(ctx, ts) + if err != nil { + return xerrors.Errorf("error retrieving messages for tipset %s: %w", ts, err) } - for _, blk := range ts.Blocks() { - bmsgs, smsgs, err := x.cs.MessagesForBlock(ctx, blk) - if err != nil { - return xerrors.Errorf("error retrieving messages for block %s in %s: %w", blk.Cid(), ts, err) - } - - for _, m := range bmsgs { - key := m.Cid().String() - if err := insert(key); err != nil { - return err - } - } - - for _, m := range smsgs { - key := m.Cid().String() - if err := insert(key); err != nil { - return err - } + insertStmt := tx.Stmt(x.insertMsgStmt) + for _, msg := range msgs { + key := msg.Cid().String() + if _, err := insertStmt.Exec(key, tskey, epoch); err != nil { + return xerrors.Errorf("error inserting message: %w", err) } } @@ -389,12 +397,11 @@ func (x *msgIndex) GetMsgInfo(ctx context.Context, m cid.Cid) (MsgInfo, error) { var ( tipset string epoch int64 - index int64 ) key := m.String() row := x.selectMsgStmt.QueryRow(key) - err := row.Scan(&tipset, &epoch, &index) + err := row.Scan(&tipset, &epoch) switch { case err == sql.ErrNoRows: return MsgInfo{}, ErrNotFound @@ -412,7 +419,6 @@ func (x *msgIndex) GetMsgInfo(ctx context.Context, m cid.Cid) (MsgInfo, error) { Message: m, TipSet: tipsetCid, Epoch: abi.ChainEpoch(epoch), - Index: int(index), }, nil } From e7448b1bb53ac36a1285cb493af922e4754dc092 Mon Sep 17 00:00:00 2001 From: vyzo Date: Mon, 13 Mar 2023 05:51:33 +0200 Subject: [PATCH 039/243] fix tests --- chain/index/msgindex_test.go | 55 +++++++++++++++++------------------- 1 file changed, 26 insertions(+), 29 deletions(-) diff --git a/chain/index/msgindex_test.go b/chain/index/msgindex_test.go index 0bb767ab048..43b262ce2b0 100644 --- a/chain/index/msgindex_test.go +++ b/chain/index/msgindex_test.go @@ -154,17 +154,13 @@ func verifyIndex(t *testing.T, cs *mockChainStore, msgIndex MsgIndex) { tsCid, err := ts.Key().Cid() require.NoError(t, err) - xindex := 0 - for _, blk := range blks { - msgs, _, _ := cs.MessagesForBlock(context.Background(), blk) - for _, m := range msgs { - minfo, err := msgIndex.GetMsgInfo(context.Background(), m.Cid()) - require.NoError(t, err) - require.Equal(t, tsCid, minfo.TipSet) - require.Equal(t, ts.Height(), minfo.Epoch) - require.Equal(t, xindex, minfo.Index) - xindex++ - } + msgs, err := cs.MessagesForTipset(context.Background(), ts) + require.NoError(t, err) + for _, m := range msgs { + minfo, err := msgIndex.GetMsgInfo(context.Background(), m.Cid()) + require.NoError(t, err) + require.Equal(t, tsCid, minfo.TipSet) + require.Equal(t, ts.Height(), minfo.Epoch) } parents := ts.Parents() @@ -175,12 +171,11 @@ func verifyIndex(t *testing.T, cs *mockChainStore, msgIndex MsgIndex) { func verifyMissing(t *testing.T, cs *mockChainStore, msgIndex MsgIndex, missing ...*types.TipSet) { for _, ts := range missing { - for _, blk := range ts.Blocks() { - msgs, _, _ := cs.MessagesForBlock(context.Background(), blk) - for _, m := range msgs { - _, err := msgIndex.GetMsgInfo(context.Background(), m.Cid()) - require.Equal(t, ErrNotFound, err) - } + msgs, err := cs.MessagesForTipset(context.Background(), ts) + require.NoError(t, err) + for _, m := range msgs { + _, err := msgIndex.GetMsgInfo(context.Background(), m.Cid()) + require.Equal(t, ErrNotFound, err) } } } @@ -188,9 +183,9 @@ func verifyMissing(t *testing.T, cs *mockChainStore, msgIndex MsgIndex, missing type mockChainStore struct { notify store.ReorgNotifee - curTs *types.TipSet - tipsets map[types.TipSetKey]*types.TipSet - blockMsgs map[cid.Cid][]*types.Message + curTs *types.TipSet + tipsets map[types.TipSetKey]*types.TipSet + msgs map[types.TipSetKey][]types.ChainMsg nonce uint64 } @@ -207,15 +202,15 @@ func init() { func newMockChainStore() *mockChainStore { return &mockChainStore{ - tipsets: make(map[types.TipSetKey]*types.TipSet), - blockMsgs: make(map[cid.Cid][]*types.Message), + tipsets: make(map[types.TipSetKey]*types.TipSet), + msgs: make(map[types.TipSetKey][]types.ChainMsg), } } func (cs *mockChainStore) genesis() { genBlock := mock.MkBlock(nil, 0, 0) - cs.blockMsgs[genBlock.Cid()] = nil genTs := mock.TipSet(genBlock) + cs.msgs[genTs.Key()] = nil cs.setHead(genTs) } @@ -252,11 +247,13 @@ func (cs *mockChainStore) makeBlk() *types.TipSet { blk := mock.MkBlock(cs.curTs, uint64(height), uint64(height)) blk.Messages = cs.makeGarbageCid() + + ts := mock.TipSet(blk) msg1 := cs.makeMsg() msg2 := cs.makeMsg() - cs.blockMsgs[blk.Cid()] = []*types.Message{msg1, msg2} + cs.msgs[ts.Key()] = []types.ChainMsg{msg1, msg2} - return mock.TipSet(blk) + return ts } func (cs *mockChainStore) makeMsg() *types.Message { @@ -274,13 +271,13 @@ func (cs *mockChainStore) SubscribeHeadChanges(f store.ReorgNotifee) { cs.notify = f } -func (cs *mockChainStore) MessagesForBlock(ctx context.Context, b *types.BlockHeader) ([]*types.Message, []*types.SignedMessage, error) { - msgs, ok := cs.blockMsgs[b.Cid()] +func (cs *mockChainStore) MessagesForTipset(ctx context.Context, ts *types.TipSet) ([]types.ChainMsg, error) { + msgs, ok := cs.msgs[ts.Key()] if !ok { - return nil, nil, errors.New("unknown block") + return nil, errors.New("unknown tipset") } - return msgs, nil, nil + return msgs, nil } func (cs *mockChainStore) GetHeaviestTipSet() *types.TipSet { From 4a20c9b60fb3c19ce21cf1b5eb11bbb2022296b6 Mon Sep 17 00:00:00 2001 From: vyzo Date: Mon, 13 Mar 2023 05:54:32 +0200 Subject: [PATCH 040/243] cosmetics --- chain/index/msgindex.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/chain/index/msgindex.go b/chain/index/msgindex.go index d2686133cde..cb18eee3b98 100644 --- a/chain/index/msgindex.go +++ b/chain/index/msgindex.go @@ -147,9 +147,8 @@ func NewMsgIndex(basePath string, cs ChainStore) (MsgIndex, error) { err = msgIndex.prepareStatements() if err != nil { - err2 := db.Close() - if err2 != nil { - log.Errorf("error closing msgindex database: %s", err2) + if err := db.Close(); err != nil { + log.Errorf("error closing msgindex database: %s", err) } return nil, xerrors.Errorf("error preparing msgindex database statements: %w", err) From 5461548b7e5b97c06f533354dd8f4ea78d139ffd Mon Sep 17 00:00:00 2001 From: vyzo Date: Mon, 13 Mar 2023 05:57:26 +0200 Subject: [PATCH 041/243] fix comment typo --- chain/index/msgindex.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chain/index/msgindex.go b/chain/index/msgindex.go index cb18eee3b98..e3a73198958 100644 --- a/chain/index/msgindex.go +++ b/chain/index/msgindex.go @@ -189,7 +189,7 @@ func reconcileIndex(db *sql.DB, cs ChainStore) error { // Invariant: after reconciliation, every tipset in the index is in the current chain; ie either // the chain head or reachable by walking the chain. // Algorithm: - // 1. Count mesages in index; if none, trivially reconciled. + // 1. Count messages in index; if none, trivially reconciled. // TODO we may consider populating the index in that case // 2. Find the minimum tipset in the index; this will mark the end of the reconciliation walk // 3. Walk from current tipset until we find a tipset in the index. From b90cfff0aad9bf73225e89c5447c8701b4f45449 Mon Sep 17 00:00:00 2001 From: vyzo Date: Mon, 13 Mar 2023 10:42:48 +0200 Subject: [PATCH 042/243] wire in lifecycle context --- chain/index/msgindex.go | 4 ++-- chain/index/msgindex_test.go | 8 ++++---- node/modules/msgindex.go | 5 +++-- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/chain/index/msgindex.go b/chain/index/msgindex.go index e3a73198958..1c77612d714 100644 --- a/chain/index/msgindex.go +++ b/chain/index/msgindex.go @@ -94,7 +94,7 @@ type headChange struct { app []*types.TipSet } -func NewMsgIndex(basePath string, cs ChainStore) (MsgIndex, error) { +func NewMsgIndex(lctx context.Context, basePath string, cs ChainStore) (MsgIndex, error) { var ( dbPath string exists bool @@ -136,7 +136,7 @@ func NewMsgIndex(basePath string, cs ChainStore) (MsgIndex, error) { } } - ctx, cancel := context.WithCancel(context.Background()) + ctx, cancel := context.WithCancel(lctx) msgIndex := &msgIndex{ db: db, diff --git a/chain/index/msgindex_test.go b/chain/index/msgindex_test.go index 43b262ce2b0..bcca8ce1fa6 100644 --- a/chain/index/msgindex_test.go +++ b/chain/index/msgindex_test.go @@ -30,7 +30,7 @@ func TestBasicMsgIndex(t *testing.T) { tmp := t.TempDir() t.Cleanup(func() { _ = os.RemoveAll(tmp) }) - msgIndex, err := NewMsgIndex(tmp, cs) + msgIndex, err := NewMsgIndex(context.Background(), tmp, cs) require.NoError(t, err) defer msgIndex.Close() //nolint @@ -58,7 +58,7 @@ func TestReorgMsgIndex(t *testing.T) { tmp := t.TempDir() t.Cleanup(func() { _ = os.RemoveAll(tmp) }) - msgIndex, err := NewMsgIndex(tmp, cs) + msgIndex, err := NewMsgIndex(context.Background(), tmp, cs) require.NoError(t, err) defer msgIndex.Close() //nolint @@ -102,7 +102,7 @@ func TestReconcileMsgIndex(t *testing.T) { tmp := t.TempDir() t.Cleanup(func() { _ = os.RemoveAll(tmp) }) - msgIndex, err := NewMsgIndex(tmp, cs) + msgIndex, err := NewMsgIndex(context.Background(), tmp, cs) require.NoError(t, err) for i := 0; i < 10; i++ { @@ -129,7 +129,7 @@ func TestReconcileMsgIndex(t *testing.T) { require.NoError(t, err) // reopen to reconcile - msgIndex, err = NewMsgIndex(tmp, cs) + msgIndex, err = NewMsgIndex(context.Background(), tmp, cs) require.NoError(t, err) defer msgIndex.Close() //nolint diff --git a/node/modules/msgindex.go b/node/modules/msgindex.go index 23072ade134..a758f22cfcb 100644 --- a/node/modules/msgindex.go +++ b/node/modules/msgindex.go @@ -7,16 +7,17 @@ import ( "github.com/filecoin-project/lotus/chain/index" "github.com/filecoin-project/lotus/chain/store" + "github.com/filecoin-project/lotus/node/modules/helpers" "github.com/filecoin-project/lotus/node/repo" ) -func MsgIndex(lc fx.Lifecycle, cs *store.ChainStore, r repo.LockedRepo) (index.MsgIndex, error) { +func MsgIndex(lc fx.Lifecycle, mctx helpers.MetricsCtx, cs *store.ChainStore, r repo.LockedRepo) (index.MsgIndex, error) { basePath, err := r.SqlitePath() if err != nil { return nil, err } - msgIndex, err := index.NewMsgIndex(basePath, cs) + msgIndex, err := index.NewMsgIndex(helpers.LifecycleCtx(mctx, lc), basePath, cs) if err != nil { return nil, err } From bda7ef52da2fcd91bb748a6877fdc8335c2e75a7 Mon Sep 17 00:00:00 2001 From: vyzo Date: Mon, 13 Mar 2023 11:41:46 +0200 Subject: [PATCH 043/243] log rollback errors --- chain/index/msgindex.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/chain/index/msgindex.go b/chain/index/msgindex.go index 1c77612d714..64dfcac67ed 100644 --- a/chain/index/msgindex.go +++ b/chain/index/msgindex.go @@ -332,14 +332,18 @@ func (x *msgIndex) processHeadChanges(ctx context.Context) error { for _, hc := range pend { for _, ts := range hc.rev { if err := x.doRevert(ctx, tx, ts); err != nil { - tx.Rollback() //nolint + if err := tx.Rollback(); err != nil { + log.Errorf("error rolling back transaction: %s", err) + } return xerrors.Errorf("error reverting %s: %w", ts, err) } } for _, ts := range hc.app { if err := x.doApply(ctx, tx, ts); err != nil { - tx.Rollback() //nolint + if err := tx.Rollback(); err != nil { + log.Errorf("error rolling back transaction: %s", err) + } return xerrors.Errorf("error applying %s: %w", ts, err) } } From a11032b10a0e428e37801539c26a23fdb0ebdaf1 Mon Sep 17 00:00:00 2001 From: vyzo Date: Mon, 13 Mar 2023 11:44:10 +0200 Subject: [PATCH 044/243] adjust coalescer delays --- chain/index/msgindex.go | 6 +++--- chain/index/msgindex_test.go | 6 ++++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/chain/index/msgindex.go b/chain/index/msgindex.go index 64dfcac67ed..4b484ca50d3 100644 --- a/chain/index/msgindex.go +++ b/chain/index/msgindex.go @@ -53,9 +53,9 @@ const ( // coalescer configuration (TODO: use observer instead) var ( - coalesceMinDelay = 100 * time.Millisecond - coalesceMaxDelay = time.Second - coalesceMergeInterval = 100 * time.Millisecond + coalesceMinDelay = time.Second + coalesceMaxDelay = 15 * time.Second + coalesceMergeInterval = time.Second ) // chain store interface; we could use store.ChainStore directly, diff --git a/chain/index/msgindex_test.go b/chain/index/msgindex_test.go index bcca8ce1fa6..07fdbdc8e46 100644 --- a/chain/index/msgindex_test.go +++ b/chain/index/msgindex_test.go @@ -198,6 +198,12 @@ var rng *rand.Rand func init() { systemAddr, _ = address.NewIDAddress(0) rng = rand.New(rand.NewSource(314159)) + + // adjust those to make tests snappy + coalesceMinDelay = time.Millisecond + coalesceMaxDelay = 10 * time.Millisecond + coalesceMergeInterval = time.Millisecond + } func newMockChainStore() *mockChainStore { From 3c945e9e3ceba6c5d939cd468c3fa4e86c156383 Mon Sep 17 00:00:00 2001 From: vyzo Date: Mon, 13 Mar 2023 12:00:48 +0200 Subject: [PATCH 045/243] no need to return error from DummyMsgIndex DI constructor --- node/modules/msgindex.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/node/modules/msgindex.go b/node/modules/msgindex.go index a758f22cfcb..72e9840ba33 100644 --- a/node/modules/msgindex.go +++ b/node/modules/msgindex.go @@ -31,6 +31,6 @@ func MsgIndex(lc fx.Lifecycle, mctx helpers.MetricsCtx, cs *store.ChainStore, r return msgIndex, nil } -func DummyMsgIndex() (index.MsgIndex, error) { - return index.DummyMsgIndex, nil +func DummyMsgIndex() index.MsgIndex { + return index.DummyMsgIndex } From db8b593c9bb751128893fa2809ea2c0720bbba64 Mon Sep 17 00:00:00 2001 From: vyzo Date: Mon, 13 Mar 2023 12:01:04 +0200 Subject: [PATCH 046/243] make MsgIndex configurable, disabled by default. --- node/builder_chain.go | 5 ++++- node/config/types.go | 6 ++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/node/builder_chain.go b/node/builder_chain.go index 20817acff45..fcdb26162a7 100644 --- a/node/builder_chain.go +++ b/node/builder_chain.go @@ -80,7 +80,6 @@ var ChainNode = Options( Override(new(stmgr.Executor), consensus.NewTipSetExecutor(filcns.RewardFunc)), Override(new(consensus.Consensus), filcns.NewFilecoinExpectedConsensus), Override(new(*store.ChainStore), modules.ChainStore), - Override(new(index.MsgIndex), modules.MsgIndex), Override(new(*stmgr.StateManager), modules.StateManager), Override(new(dtypes.ChainBitswap), modules.ChainBitswap), Override(new(dtypes.ChainBlockService), modules.ChainBlockService), // todo: unused @@ -277,6 +276,10 @@ func ConfigFullNode(c interface{}) Option { Override(new(full.EthEventAPI), &full.EthModuleDummy{}), ), ), + + // 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/types.go b/node/config/types.go index 5b952d35e46..51ef327d4a0 100644 --- a/node/config/types.go +++ b/node/config/types.go @@ -28,6 +28,7 @@ type FullNode struct { Chainstore Chainstore Cluster UserRaftConfig Fevm FevmConfig + Index IndexConfig } // // Common @@ -726,3 +727,8 @@ type Events struct { // Set a timeout for subscription clients // Set upper bound on index size } + +type IndexConfig struct { + // EnableMsgIndex enables indexing of messages on chain. + EnableMsgIndex bool +} From 0a5618d4066f936e41ab7ab8d051e453a95d4ee8 Mon Sep 17 00:00:00 2001 From: vyzo Date: Mon, 13 Mar 2023 12:10:36 +0200 Subject: [PATCH 047/243] make gen --- node/config/doc_gen.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/node/config/doc_gen.go b/node/config/doc_gen.go index c620847084e..bc7b8a2701c 100644 --- a/node/config/doc_gen.go +++ b/node/config/doc_gen.go @@ -461,6 +461,20 @@ Set to 0 to keep all mappings`, Comment: ``, }, + { + Name: "Index", + Type: "IndexConfig", + + Comment: ``, + }, + }, + "IndexConfig": []DocField{ + { + Name: "EnableMsgIndex", + Type: "bool", + + Comment: `EnableMsgIndex enables indexing of messages on chain.`, + }, }, "IndexProviderConfig": []DocField{ { From 5e011d536b0ff2af2cc2958b9643dc420876e118 Mon Sep 17 00:00:00 2001 From: vyzo Date: Mon, 13 Mar 2023 12:14:30 +0200 Subject: [PATCH 048/243] enhance comment about lookup cid semantics --- chain/index/interface.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/chain/index/interface.go b/chain/index/interface.go index ff46ecad726..8907dc09d94 100644 --- a/chain/index/interface.go +++ b/chain/index/interface.go @@ -25,6 +25,8 @@ type MsgInfo struct { // MsgIndex is the interface to the message index type MsgIndex interface { // GetMsgInfo retrieves the message metadata through the index. + // The lookup is done using the onchain message Cid; that is the signed message Cid + // for SECP messages and unsigned message Cid for BLS messages. GetMsgInfo(ctx context.Context, m cid.Cid) (MsgInfo, error) // Close closes the index Close() error From 0bf6a333b919c100135c72f7a445267a2ee73010 Mon Sep 17 00:00:00 2001 From: vyzo Date: Mon, 13 Mar 2023 12:24:35 +0200 Subject: [PATCH 049/243] more gen --- documentation/en/default-lotus-config.toml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/documentation/en/default-lotus-config.toml b/documentation/en/default-lotus-config.toml index c5132171442..c1b40cd592b 100644 --- a/documentation/en/default-lotus-config.toml +++ b/documentation/en/default-lotus-config.toml @@ -390,3 +390,11 @@ #DatabasePath = "" +[Index] + # EnableMsgIndex enables indexing of messages on chain. + # + # type: bool + # env var: LOTUS_INDEX_ENABLEMSGINDEX + #EnableMsgIndex = false + + From 05cb2428c15addceb96c56d379a20b376338920c Mon Sep 17 00:00:00 2001 From: vyzo Date: Mon, 13 Mar 2023 12:33:19 +0200 Subject: [PATCH 050/243] add confidence check for indexed message epoch --- chain/stmgr/searchwait.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/chain/stmgr/searchwait.go b/chain/stmgr/searchwait.go index 27a30461108..ded55466a26 100644 --- a/chain/stmgr/searchwait.go +++ b/chain/stmgr/searchwait.go @@ -179,6 +179,13 @@ func (sm *StateManager) searchForIndexedMsg(ctx context.Context, mcid cid.Cid, m return nil, nil, cid.Undef, err } + // check the height against the current tipset; minimum confidence requires that the inclusion + // tipset height is lower than the current head + curTs := sm.cs.GetHeaviestTipSet() + if curTs.Height() <= minfo.Epoch { + return nil, nil, cid.Undef, xerrors.Errorf("indexed message does not appear before the current tipset; index epoch: %d, current epoch: %d", minfo.Epoch, curTs.Height()) + } + ts, err := sm.cs.GetTipSetByCid(ctx, minfo.TipSet) if err != nil { return nil, nil, cid.Undef, err From 4b1a40500242f828570af419b60207ef1787a6e3 Mon Sep 17 00:00:00 2001 From: vyzo Date: Mon, 13 Mar 2023 14:55:26 +0200 Subject: [PATCH 051/243] basic msgindex itest --- chain/index/msgindex.go | 28 ++++++++--- chain/index/msgindex_test.go | 15 +++--- itests/msgindex_test.go | 90 ++++++++++++++++++++++++++++++++++++ 3 files changed, 119 insertions(+), 14 deletions(-) create mode 100644 itests/msgindex_test.go diff --git a/chain/index/msgindex.go b/chain/index/msgindex.go index 4b484ca50d3..0140b3e7471 100644 --- a/chain/index/msgindex.go +++ b/chain/index/msgindex.go @@ -52,10 +52,11 @@ const ( ) // coalescer configuration (TODO: use observer instead) +// these are exposed to make tests snappy var ( - coalesceMinDelay = time.Second - coalesceMaxDelay = 15 * time.Second - coalesceMergeInterval = time.Second + CoalesceMinDelay = time.Second + CoalesceMaxDelay = 15 * time.Second + CoalesceMergeInterval = time.Second ) // chain store interface; we could use store.ChainStore directly, @@ -156,9 +157,9 @@ func NewMsgIndex(lctx context.Context, basePath string, cs ChainStore) (MsgIndex rnf := store.WrapHeadChangeCoalescer( msgIndex.onHeadChange, - coalesceMinDelay, - coalesceMaxDelay, - coalesceMergeInterval, + CoalesceMinDelay, + CoalesceMaxDelay, + CoalesceMergeInterval, ) cs.SubscribeHeadChanges(rnf) @@ -440,3 +441,18 @@ func (x *msgIndex) Close() error { return x.db.Close() } + +// informal apis for itests; not exposed in the main interface +func (x *msgIndex) CountMessages() (int64, error) { + x.closeLk.RLock() + defer x.closeLk.RUnlock() + + if x.closed { + return 0, ErrClosed + } + + var result int64 + row := x.db.QueryRow(dbqCountMessages) + err := row.Scan(&result) + return result, err +} diff --git a/chain/index/msgindex_test.go b/chain/index/msgindex_test.go index 07fdbdc8e46..9861a5e7c3d 100644 --- a/chain/index/msgindex_test.go +++ b/chain/index/msgindex_test.go @@ -40,7 +40,7 @@ func TestBasicMsgIndex(t *testing.T) { err := cs.advance() require.NoError(t, err) // wait for the coalescer to notify - time.Sleep(coalesceMinDelay + 10*time.Millisecond) + time.Sleep(CoalesceMinDelay + 10*time.Millisecond) } t.Log("verifying index") @@ -68,7 +68,7 @@ func TestReorgMsgIndex(t *testing.T) { err := cs.advance() require.NoError(t, err) // wait for the coalescer to notify - time.Sleep(coalesceMinDelay + 10*time.Millisecond) + time.Sleep(CoalesceMinDelay + 10*time.Millisecond) } // a simple reorg @@ -80,7 +80,7 @@ func TestReorgMsgIndex(t *testing.T) { reorgmeChild := cs.makeBlk() err = cs.reorg([]*types.TipSet{reorgme}, []*types.TipSet{reorgmeChild}) require.NoError(t, err) - time.Sleep(coalesceMinDelay + 10*time.Millisecond) + time.Sleep(CoalesceMinDelay + 10*time.Millisecond) t.Log("verifying index") verifyIndex(t, cs, msgIndex) @@ -110,7 +110,7 @@ func TestReconcileMsgIndex(t *testing.T) { err := cs.advance() require.NoError(t, err) // wait for the coalescer to notify - time.Sleep(coalesceMinDelay + 10*time.Millisecond) + time.Sleep(CoalesceMinDelay + 10*time.Millisecond) } // Close it and reorg @@ -200,10 +200,9 @@ func init() { rng = rand.New(rand.NewSource(314159)) // adjust those to make tests snappy - coalesceMinDelay = time.Millisecond - coalesceMaxDelay = 10 * time.Millisecond - coalesceMergeInterval = time.Millisecond - + CoalesceMinDelay = time.Millisecond + CoalesceMaxDelay = 10 * time.Millisecond + CoalesceMergeInterval = time.Millisecond } func newMockChainStore() *mockChainStore { diff --git a/itests/msgindex_test.go b/itests/msgindex_test.go new file mode 100644 index 00000000000..6b3834cd81c --- /dev/null +++ b/itests/msgindex_test.go @@ -0,0 +1,90 @@ +package itests + +import ( + "context" + "os" + "sync" + "testing" + "time" + + "github.com/filecoin-project/lotus/chain/index" + "github.com/filecoin-project/lotus/chain/store" + "github.com/filecoin-project/lotus/itests/kit" + "github.com/filecoin-project/lotus/node" + + "github.com/stretchr/testify/require" +) + +func init() { + // adjust those to make tests snappy + index.CoalesceMinDelay = time.Millisecond + index.CoalesceMaxDelay = 10 * time.Millisecond + index.CoalesceMergeInterval = time.Millisecond +} + +func testMsgIndex( + t *testing.T, + name string, + run func(t *testing.T, makeMsgIndex func(cs *store.ChainStore) (index.MsgIndex, error)), + check func(t *testing.T, i int, msgIndex index.MsgIndex), +) { + + // create the message indices in the test context + var mx sync.Mutex + var tmpDirs []string + var msgIndices []index.MsgIndex + + t.Cleanup(func() { + for _, msgIndex := range msgIndices { + _ = msgIndex.Close() + } + + for _, tmp := range tmpDirs { + _ = os.RemoveAll(tmp) + } + }) + + makeMsgIndex := func(cs *store.ChainStore) (index.MsgIndex, error) { + var err error + tmp := t.TempDir() + msgIndex, err := index.NewMsgIndex(context.Background(), tmp, cs) + if err == nil { + mx.Lock() + tmpDirs = append(tmpDirs, tmp) + msgIndices = append(msgIndices, msgIndex) + mx.Unlock() + } + return msgIndex, err + } + + t.Run(name, func(t *testing.T) { + run(t, makeMsgIndex) + }) + + if len(msgIndices) == 0 { + t.Fatal("no message indices") + } + + for i, msgIndex := range msgIndices { + check(t, i, msgIndex) + } +} + +func checkNonEmptyMsgIndex(t *testing.T, _ int, msgIndex index.MsgIndex) { + mi, ok := msgIndex.(interface{ CountMessages() (int64, error) }) + if !ok { + t.Fatal("index does not allow counting") + } + count, err := mi.CountMessages() + require.NoError(t, err) + require.NotEqual(t, count, 0) +} + +func TestMsgIndex(t *testing.T) { + testMsgIndex(t, "testSearchMsg", testSearchMsgWithIndex, checkNonEmptyMsgIndex) +} + +func testSearchMsgWithIndex(t *testing.T, makeMsgIndex func(cs *store.ChainStore) (index.MsgIndex, error)) { + suite := apiSuite{opts: []interface{}{kit.ConstructorOpts(node.Override(new(index.MsgIndex), makeMsgIndex))}} + suite.testSearchMsg(t) +} From 19707924456efa9921c6d813b7b836958d889e36 Mon Sep 17 00:00:00 2001 From: vyzo Date: Mon, 13 Mar 2023 14:56:00 +0200 Subject: [PATCH 052/243] fix bug in searchForIndexedMsg Need to use and return the execution tipset --- chain/stmgr/searchwait.go | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/chain/stmgr/searchwait.go b/chain/stmgr/searchwait.go index ded55466a26..754f0e7a4f9 100644 --- a/chain/stmgr/searchwait.go +++ b/chain/stmgr/searchwait.go @@ -179,20 +179,32 @@ func (sm *StateManager) searchForIndexedMsg(ctx context.Context, mcid cid.Cid, m return nil, nil, cid.Undef, err } - // check the height against the current tipset; minimum confidence requires that the inclusion - // tipset height is lower than the current head + // check the height against the current tipset; minimum execution confidence requires that the + // inclusion tipset height is lower than the current head + 1 curTs := sm.cs.GetHeaviestTipSet() - if curTs.Height() <= minfo.Epoch { + if curTs.Height() <= minfo.Epoch+1 { return nil, nil, cid.Undef, xerrors.Errorf("indexed message does not appear before the current tipset; index epoch: %d, current epoch: %d", minfo.Epoch, curTs.Height()) } - ts, err := sm.cs.GetTipSetByCid(ctx, minfo.TipSet) + // now get the execution tipset + xts, err := sm.cs.GetTipsetByHeight(ctx, minfo.Epoch+1, curTs, false) if err != nil { return nil, nil, cid.Undef, err } - r, foundMsg, err := sm.tipsetExecutedMessage(ctx, ts, mcid, m.VMMessage(), false) - return ts, r, foundMsg, err + // check that it is indeed the parent of the inclusion tipset + parent := xts.Parents() + parentCid, err := parent.Cid() + if err != nil { + return nil, nil, cid.Undef, xerrors.Errorf("error computing tipset cid: %w", err) + } + + if !parentCid.Equals(minfo.TipSet) { + return nil, nil, cid.Undef, xerrors.Errorf("inclusion tipset mismatch: have %s, expected %s", parentCid, minfo.TipSet) + } + + r, foundMsg, err := sm.tipsetExecutedMessage(ctx, xts, mcid, m.VMMessage(), false) + return xts, r, foundMsg, err } // searchBackForMsg searches up to limit tipsets backwards from the given From 5113c72b3a97c632aaef365a7d0975ab13482a76 Mon Sep 17 00:00:00 2001 From: vyzo Date: Mon, 13 Mar 2023 15:09:11 +0200 Subject: [PATCH 053/243] make gen --- .circleci/config.yml | 6 ++++++ itests/msgindex_test.go | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 93019df5ce8..bea04cd86b4 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -818,6 +818,12 @@ workflows: - build suite: itest-mpool_push_with_uuid target: "./itests/mpool_push_with_uuid_test.go" + - test: + name: test-itest-msgindex + requires: + - build + suite: itest-msgindex + target: "./itests/msgindex_test.go" - test: name: test-itest-multisig requires: diff --git a/itests/msgindex_test.go b/itests/msgindex_test.go index 6b3834cd81c..59fda75b98a 100644 --- a/itests/msgindex_test.go +++ b/itests/msgindex_test.go @@ -7,12 +7,12 @@ import ( "testing" "time" + "github.com/stretchr/testify/require" + "github.com/filecoin-project/lotus/chain/index" "github.com/filecoin-project/lotus/chain/store" "github.com/filecoin-project/lotus/itests/kit" "github.com/filecoin-project/lotus/node" - - "github.com/stretchr/testify/require" ) func init() { From 47646cbd6966ee440da5ffe7933e73ec67853f39 Mon Sep 17 00:00:00 2001 From: vyzo Date: Mon, 13 Mar 2023 15:19:07 +0200 Subject: [PATCH 054/243] add optimization TODO comment --- chain/stmgr/searchwait.go | 1 + 1 file changed, 1 insertion(+) diff --git a/chain/stmgr/searchwait.go b/chain/stmgr/searchwait.go index 754f0e7a4f9..e9feeec12ec 100644 --- a/chain/stmgr/searchwait.go +++ b/chain/stmgr/searchwait.go @@ -187,6 +187,7 @@ func (sm *StateManager) searchForIndexedMsg(ctx context.Context, mcid cid.Cid, m } // now get the execution tipset + // TODO optimization: the index should have it implicitly so we can return it in the msginfo. xts, err := sm.cs.GetTipsetByHeight(ctx, minfo.Epoch+1, curTs, false) if err != nil { return nil, nil, cid.Undef, err From 27dc4951eb0c6900017ca9b878b75ca1f29fcdb5 Mon Sep 17 00:00:00 2001 From: vyzo Date: Mon, 13 Mar 2023 16:51:21 +0200 Subject: [PATCH 055/243] lotus-shed tools for msgindex --- cmd/lotus-shed/main.go | 1 + cmd/lotus-shed/msgindex.go | 205 +++++++++++++++++++++++++++++++++++++ 2 files changed, 206 insertions(+) create mode 100644 cmd/lotus-shed/msgindex.go diff --git a/cmd/lotus-shed/main.go b/cmd/lotus-shed/main.go index 19072dd7191..736d874e381 100644 --- a/cmd/lotus-shed/main.go +++ b/cmd/lotus-shed/main.go @@ -83,6 +83,7 @@ func main() { invariantsCmd, gasTraceCmd, replayOfflineCmd, + msgindexCmd, } app := &cli.App{ diff --git a/cmd/lotus-shed/msgindex.go b/cmd/lotus-shed/msgindex.go new file mode 100644 index 00000000000..c3f288422c7 --- /dev/null +++ b/cmd/lotus-shed/msgindex.go @@ -0,0 +1,205 @@ +package main + +import ( + "database/sql" + "fmt" + "path" + + _ "github.com/mattn/go-sqlite3" + "github.com/urfave/cli/v2" + "golang.org/x/xerrors" + + "github.com/filecoin-project/go-state-types/abi" + lcli "github.com/filecoin-project/lotus/cli" + "github.com/ipfs/go-cid" +) + +var msgindexCmd = &cli.Command{ + Name: "msgindex", + Usage: "Tools for managing the message index", + Subcommands: []*cli.Command{ + msgindexBackfillCmd, + msgindexPruneCmd, + }, +} + +var msgindexBackfillCmd = &cli.Command{ + Name: "backfill", + Usage: "Backfill the message index for a number of epochs starting from a specified height", + Flags: []cli.Flag{ + &cli.IntFlag{ + Name: "from", + Value: 0, + Usage: "height to start the backfill; uses the current head if omitted", + }, + &cli.IntFlag{ + Name: "epochs", + Value: 1800, + Usage: "number of epochs to backfill; defaults to 1800 (2 finalities)", + }, + &cli.StringFlag{ + Name: "repo", + Value: "~/.lotus", + Usage: "path to the repo", + }, + }, + Action: func(cctx *cli.Context) error { + api, closer, err := lcli.GetFullNodeAPI(cctx) + if err != nil { + return err + } + + defer closer() + ctx := lcli.ReqContext(cctx) + + curTs, err := api.ChainHead(ctx) + if err != nil { + return err + } + + startHeight := int64(cctx.Int("from")) + if startHeight == 0 { + startHeight = int64(curTs.Height()) - 1 + } + epochs := cctx.Int("epochs") + + dbPath := path.Join(cctx.String("repo"), "sqlite", "msgindex.db") + db, err := sql.Open("sqlite3", dbPath) + if err != nil { + return err + } + + defer func() { + err := db.Close() + if err != nil { + fmt.Printf("ERROR: closing db: %s", err) + } + }() + + tx, err := db.Begin() + if err != nil { + return err + } + + insertStmt, err := tx.Prepare("INSERT INTO messages VALUES (?, ?, ?)") + insertMsg := func(cid, tsCid cid.Cid, epoch abi.ChainEpoch) error { + key := cid.String() + tskey := tsCid.String() + if _, err := insertStmt.Exec(key, tskey, int64(epoch)); err != nil { + return err + } + + return nil + } + rollback := func() { + if err := tx.Rollback(); err != nil { + fmt.Printf("ERROR: rollback: %s", err) + } + } + + for i := 0; i < epochs; i++ { + epoch := abi.ChainEpoch(startHeight - int64(i)) + + ts, err := api.ChainGetTipSetByHeight(ctx, epoch, curTs.Key()) + if err != nil { + rollback() + return err + } + + tsCid, err := ts.Key().Cid() + if err != nil { + rollback() + return err + } + + msgs, err := api.ChainGetMessagesInTipset(ctx, ts.Key()) + if err != nil { + rollback() + return err + } + + for _, msg := range msgs { + if err := insertMsg(msg.Cid, tsCid, epoch); err != nil { + rollback() + return err + } + } + } + + if err := tx.Commit(); err != nil { + return err + } + + return nil + }, +} + +var msgindexPruneCmd = &cli.Command{ + Name: "prune", + Usage: "Prune the message index for messages included before a given epoch", + Flags: []cli.Flag{ + &cli.IntFlag{ + Name: "from", + Usage: "height to start the prune; if negative it indicates epochs from current head", + }, + &cli.StringFlag{ + Name: "repo", + Value: "~/.lotus", + Usage: "path to the repo", + }, + }, + Action: func(cctx *cli.Context) error { + api, closer, err := lcli.GetFullNodeAPI(cctx) + if err != nil { + return err + } + + defer closer() + ctx := lcli.ReqContext(cctx) + + startHeight := int64(cctx.Int("from")) + if startHeight < 0 { + curTs, err := api.ChainHead(ctx) + if err != nil { + return err + } + + startHeight += int64(curTs.Height()) + + if startHeight < 0 { + return xerrors.Errorf("bogus start height %d", startHeight) + } + } + + dbPath := path.Join(cctx.String("repo"), "sqlite", "msgindex.db") + db, err := sql.Open("sqlite3", dbPath) + if err != nil { + return err + } + + defer func() { + err := db.Close() + if err != nil { + fmt.Printf("ERROR: closing db: %s", err) + } + }() + + tx, err := db.Begin() + if err != nil { + return err + } + + if _, err := tx.Exec("DELETE FROM messages WHERE epoch < ?", int64(startHeight)); err != nil { + if err := tx.Rollback(); err != nil { + fmt.Printf("ERROR: rollback: %s", err) + } + return err + } + + if err := tx.Commit(); err != nil { + return err + } + + return nil + }, +} From cafa1eaba4b4bd620d233843186f57b2c2f13eb2 Mon Sep 17 00:00:00 2001 From: vyzo Date: Mon, 13 Mar 2023 16:56:51 +0200 Subject: [PATCH 056/243] fix test for CI test files are run individually... --- itests/msgindex_test.go | 38 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/itests/msgindex_test.go b/itests/msgindex_test.go index 59fda75b98a..f450060c8eb 100644 --- a/itests/msgindex_test.go +++ b/itests/msgindex_test.go @@ -9,10 +9,15 @@ import ( "github.com/stretchr/testify/require" + lapi "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/chain/index" "github.com/filecoin-project/lotus/chain/store" + "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/itests/kit" "github.com/filecoin-project/lotus/node" + + "github.com/filecoin-project/go-state-types/big" + "github.com/filecoin-project/go-state-types/exitcode" ) func init() { @@ -85,6 +90,35 @@ func TestMsgIndex(t *testing.T) { } func testSearchMsgWithIndex(t *testing.T, makeMsgIndex func(cs *store.ChainStore) (index.MsgIndex, error)) { - suite := apiSuite{opts: []interface{}{kit.ConstructorOpts(node.Override(new(index.MsgIndex), makeMsgIndex))}} - suite.testSearchMsg(t) + // copy of apiSuite.testSearchMsgWith; needs to be copied or else CI is angry, tests are built individually there + ctx := context.Background() + + full, _, ens := kit.EnsembleMinimal(t, kit.ConstructorOpts(node.Override(new(index.MsgIndex), makeMsgIndex))) + + senderAddr, err := full.WalletDefaultAddress(ctx) + require.NoError(t, err) + + msg := &types.Message{ + From: senderAddr, + To: senderAddr, + Value: big.Zero(), + } + + ens.BeginMining(100 * time.Millisecond) + + sm, err := full.MpoolPushMessage(ctx, msg, nil) + require.NoError(t, err) + + //stm: @CHAIN_STATE_WAIT_MSG_001 + res, err := full.StateWaitMsg(ctx, sm.Cid(), 1, lapi.LookbackNoLimit, true) + require.NoError(t, err) + + require.Equal(t, exitcode.Ok, res.Receipt.ExitCode, "message not successful") + + //stm: @CHAIN_STATE_SEARCH_MSG_001 + searchRes, err := full.StateSearchMsg(ctx, types.EmptyTSK, sm.Cid(), lapi.LookbackNoLimit, true) + require.NoError(t, err) + require.NotNil(t, searchRes) + + require.Equalf(t, res.TipSet, searchRes.TipSet, "search ts: %s, different from wait ts: %s", searchRes.TipSet, res.TipSet) } From 06f93861bcf9b22a51a13ef63b310c4dd5282575 Mon Sep 17 00:00:00 2001 From: vyzo Date: Mon, 13 Mar 2023 17:00:16 +0200 Subject: [PATCH 057/243] make gen --- cmd/lotus-shed/msgindex.go | 3 ++- itests/msgindex_test.go | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/cmd/lotus-shed/msgindex.go b/cmd/lotus-shed/msgindex.go index c3f288422c7..f04b475990c 100644 --- a/cmd/lotus-shed/msgindex.go +++ b/cmd/lotus-shed/msgindex.go @@ -5,13 +5,14 @@ import ( "fmt" "path" + "github.com/ipfs/go-cid" _ "github.com/mattn/go-sqlite3" "github.com/urfave/cli/v2" "golang.org/x/xerrors" "github.com/filecoin-project/go-state-types/abi" + lcli "github.com/filecoin-project/lotus/cli" - "github.com/ipfs/go-cid" ) var msgindexCmd = &cli.Command{ diff --git a/itests/msgindex_test.go b/itests/msgindex_test.go index f450060c8eb..cb5fd85c9b7 100644 --- a/itests/msgindex_test.go +++ b/itests/msgindex_test.go @@ -9,15 +9,15 @@ import ( "github.com/stretchr/testify/require" + "github.com/filecoin-project/go-state-types/big" + "github.com/filecoin-project/go-state-types/exitcode" + lapi "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/chain/index" "github.com/filecoin-project/lotus/chain/store" "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/itests/kit" "github.com/filecoin-project/lotus/node" - - "github.com/filecoin-project/go-state-types/big" - "github.com/filecoin-project/go-state-types/exitcode" ) func init() { From 6deec4c5f4283de13ed94083b1de99be762b87f8 Mon Sep 17 00:00:00 2001 From: vyzo Date: Mon, 13 Mar 2023 17:26:20 +0200 Subject: [PATCH 058/243] lint --- cmd/lotus-shed/msgindex.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cmd/lotus-shed/msgindex.go b/cmd/lotus-shed/msgindex.go index f04b475990c..d2cb2cf3dab 100644 --- a/cmd/lotus-shed/msgindex.go +++ b/cmd/lotus-shed/msgindex.go @@ -83,6 +83,10 @@ var msgindexBackfillCmd = &cli.Command{ } insertStmt, err := tx.Prepare("INSERT INTO messages VALUES (?, ?, ?)") + if err != nil { + return err + } + insertMsg := func(cid, tsCid cid.Cid, epoch abi.ChainEpoch) error { key := cid.String() tskey := tsCid.String() @@ -190,7 +194,7 @@ var msgindexPruneCmd = &cli.Command{ return err } - if _, err := tx.Exec("DELETE FROM messages WHERE epoch < ?", int64(startHeight)); err != nil { + if _, err := tx.Exec("DELETE FROM messages WHERE epoch < ?", startHeight); err != nil { if err := tx.Rollback(); err != nil { fmt.Printf("ERROR: rollback: %s", err) } From 5ade40cbacd88d74574b6bf4551d65a367266cb3 Mon Sep 17 00:00:00 2001 From: vyzo Date: Mon, 13 Mar 2023 17:27:12 +0200 Subject: [PATCH 059/243] increase coalesce delays for test to deflake on CI --- chain/index/msgindex_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/chain/index/msgindex_test.go b/chain/index/msgindex_test.go index 9861a5e7c3d..24f9b845f2b 100644 --- a/chain/index/msgindex_test.go +++ b/chain/index/msgindex_test.go @@ -200,9 +200,9 @@ func init() { rng = rand.New(rand.NewSource(314159)) // adjust those to make tests snappy - CoalesceMinDelay = time.Millisecond - CoalesceMaxDelay = 10 * time.Millisecond - CoalesceMergeInterval = time.Millisecond + CoalesceMinDelay = 100 * time.Millisecond + CoalesceMaxDelay = time.Second + CoalesceMergeInterval = 100 * time.Millisecond } func newMockChainStore() *mockChainStore { From a3900caeb0261724d317c7dfdd9dcecb08934975 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Thu, 16 Mar 2023 13:38:40 +0100 Subject: [PATCH 060/243] Improve logging by carrying some more information in tasks --- chain/store/snapshot.go | 65 ++++++++++++++++++++++++----------------- 1 file changed, 39 insertions(+), 26 deletions(-) diff --git a/chain/store/snapshot.go b/chain/store/snapshot.go index da568d0fba4..d437d690545 100644 --- a/chain/store/snapshot.go +++ b/chain/store/snapshot.go @@ -167,8 +167,10 @@ func (t walkSchedTaskType) String() string { } type walkTask struct { - c cid.Cid - taskType walkSchedTaskType + c cid.Cid + taskType walkSchedTaskType + topLevelTaskType walkSchedTaskType + topLevelTaskCid cid.Cid } // an ever growing FIFO @@ -317,8 +319,10 @@ func newWalkScheduler(ctx context.Context, store bstore.Blockstore, cfg walkSche cancel() // kill workers return nil, ctx.Err() case s.workerTasks.in <- walkTask{ - c: b.Cid(), - taskType: blockTask, + c: b.Cid(), + taskType: blockTask, + topLevelTaskType: blockTask, + topLevelTaskCid: b.Cid(), }: } } @@ -417,7 +421,7 @@ func (s *walkScheduler) processTask(t walkTask, workerN int) error { blk, err := s.store.Get(s.ctx, t.c) if err != nil { - return xerrors.Errorf("writing object to car, bs.Get: %w", err) + return xerrors.Errorf("writing object to car. Task: %s. Top-Level: %s (%s). bs.Get: %w", t.taskType, t.topLevelTaskType, t.topLevelTaskCid, err) } s.results <- taskResult{ @@ -427,13 +431,8 @@ func (s *walkScheduler) processTask(t walkTask, workerN int) error { // extract relevant dags to walk from the block if t.taskType == blockTask { - blk := t.c - data, err := s.store.Get(s.ctx, blk) - if err != nil { - return err - } var b types.BlockHeader - if err := b.UnmarshalCBOR(bytes.NewBuffer(data.RawData())); err != nil { + if err := b.UnmarshalCBOR(bytes.NewBuffer(blk.RawData())); err != nil { return xerrors.Errorf("unmarshalling block header (cid=%s): %w", blk, err) } if b.Height%1_000 == 0 { @@ -443,13 +442,17 @@ func (s *walkScheduler) processTask(t walkTask, workerN int) error { log.Info("exporting genesis block") for i := range b.Parents { s.enqueueIfNew(walkTask{ - c: b.Parents[i], - taskType: dagTask, + c: b.Parents[i], + taskType: dagTask, + topLevelTaskType: blockTask, + topLevelTaskCid: t.c, }) } s.enqueueIfNew(walkTask{ - c: b.ParentStateRoot, - taskType: stateTask, + c: b.ParentStateRoot, + taskType: stateTask, + topLevelTaskType: stateTask, + topLevelTaskCid: t.c, }) return s.sendFinish(workerN) @@ -457,33 +460,41 @@ func (s *walkScheduler) processTask(t walkTask, workerN int) error { // enqueue block parents for i := range b.Parents { s.enqueueIfNew(walkTask{ - c: b.Parents[i], - taskType: blockTask, + c: b.Parents[i], + taskType: blockTask, + topLevelTaskType: blockTask, + topLevelTaskCid: t.c, }) } if s.cfg.tail.Height() >= b.Height { - log.Debugw("tail reached: only blocks will be exported from now until genesis", "cid", blk.String()) + log.Debugw("tail reached: only blocks will be exported from now until genesis", "cid", t.c.String()) return nil } if s.cfg.includeMessages { // enqueue block messages s.enqueueIfNew(walkTask{ - c: b.Messages, - taskType: messageTask, + c: b.Messages, + taskType: messageTask, + topLevelTaskType: messageTask, + topLevelTaskCid: t.c, }) } if s.cfg.includeReceipts { // enqueue block receipts s.enqueueIfNew(walkTask{ - c: b.ParentMessageReceipts, - taskType: receiptTask, + c: b.ParentMessageReceipts, + taskType: receiptTask, + topLevelTaskType: receiptTask, + topLevelTaskCid: t.c, }) } if s.cfg.includeState { s.enqueueIfNew(walkTask{ - c: b.ParentStateRoot, - taskType: stateTask, + c: b.ParentStateRoot, + taskType: stateTask, + topLevelTaskType: stateTask, + topLevelTaskCid: t.c, }) } @@ -497,8 +508,10 @@ func (s *walkScheduler) processTask(t walkTask, workerN int) error { } s.enqueueIfNew(walkTask{ - c: c, - taskType: dagTask, + c: c, + taskType: dagTask, + topLevelTaskType: t.topLevelTaskType, + topLevelTaskCid: t.topLevelTaskCid, }) }) } From 4b8442d8b94bfdccb8029e154b43c4139de3117e Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Thu, 16 Mar 2023 15:31:58 +0100 Subject: [PATCH 061/243] Chain export: record epoch for every task. --- chain/store/snapshot.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/chain/store/snapshot.go b/chain/store/snapshot.go index d437d690545..0852127c4c8 100644 --- a/chain/store/snapshot.go +++ b/chain/store/snapshot.go @@ -171,6 +171,7 @@ type walkTask struct { taskType walkSchedTaskType topLevelTaskType walkSchedTaskType topLevelTaskCid cid.Cid + epoch abi.ChainEpoch } // an ever growing FIFO @@ -323,6 +324,7 @@ func newWalkScheduler(ctx context.Context, store bstore.Blockstore, cfg walkSche taskType: blockTask, topLevelTaskType: blockTask, topLevelTaskCid: b.Cid(), + epoch: cfg.head.Height(), }: } } @@ -421,7 +423,7 @@ func (s *walkScheduler) processTask(t walkTask, workerN int) error { blk, err := s.store.Get(s.ctx, t.c) if err != nil { - return xerrors.Errorf("writing object to car. Task: %s. Top-Level: %s (%s). bs.Get: %w", t.taskType, t.topLevelTaskType, t.topLevelTaskCid, err) + return xerrors.Errorf("writing object to car. Task: %s. Top-Level: %s (%s). Epoch: %d. bs.Get: %w", t.taskType, t.topLevelTaskType, t.topLevelTaskCid, t.epoch, err) } s.results <- taskResult{ @@ -446,6 +448,7 @@ func (s *walkScheduler) processTask(t walkTask, workerN int) error { taskType: dagTask, topLevelTaskType: blockTask, topLevelTaskCid: t.c, + epoch: 0, }) } s.enqueueIfNew(walkTask{ @@ -453,6 +456,7 @@ func (s *walkScheduler) processTask(t walkTask, workerN int) error { taskType: stateTask, topLevelTaskType: stateTask, topLevelTaskCid: t.c, + epoch: 0, }) return s.sendFinish(workerN) @@ -464,6 +468,7 @@ func (s *walkScheduler) processTask(t walkTask, workerN int) error { taskType: blockTask, topLevelTaskType: blockTask, topLevelTaskCid: t.c, + epoch: b.Height, }) } if s.cfg.tail.Height() >= b.Height { @@ -478,6 +483,7 @@ func (s *walkScheduler) processTask(t walkTask, workerN int) error { taskType: messageTask, topLevelTaskType: messageTask, topLevelTaskCid: t.c, + epoch: b.Height, }) } if s.cfg.includeReceipts { @@ -487,6 +493,7 @@ func (s *walkScheduler) processTask(t walkTask, workerN int) error { taskType: receiptTask, topLevelTaskType: receiptTask, topLevelTaskCid: t.c, + epoch: b.Height, }) } if s.cfg.includeState { @@ -495,6 +502,7 @@ func (s *walkScheduler) processTask(t walkTask, workerN int) error { taskType: stateTask, topLevelTaskType: stateTask, topLevelTaskCid: t.c, + epoch: b.Height, }) } @@ -512,6 +520,7 @@ func (s *walkScheduler) processTask(t walkTask, workerN int) error { taskType: dagTask, topLevelTaskType: t.topLevelTaskType, topLevelTaskCid: t.topLevelTaskCid, + epoch: t.epoch, }) }) } From f59c246c7a539896b4e5dfc084399a2d353d0c79 Mon Sep 17 00:00:00 2001 From: adlrocha Date: Thu, 16 Mar 2023 16:09:45 +0100 Subject: [PATCH 062/243] Update chain/sub/bcast/consistent.go Co-authored-by: Aayush Rajasekaran --- chain/sub/bcast/consistent.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chain/sub/bcast/consistent.go b/chain/sub/bcast/consistent.go index cc600cb10ff..22a0db16007 100644 --- a/chain/sub/bcast/consistent.go +++ b/chain/sub/bcast/consistent.go @@ -19,7 +19,7 @@ import ( var log = logging.Logger("sub-cb") const ( - // GcSanityCheck determines the number of epochs that in the past + // GcSanityCheck determines the number of epochs in the past // that will be garbage collected from the current epoch. GcSanityCheck = 5 // GcLookback determines the number of epochs kept in the consistent From 1676d5148436136c74afc9dcc050106750e5aa42 Mon Sep 17 00:00:00 2001 From: vyzo Date: Thu, 16 Mar 2023 17:17:26 +0200 Subject: [PATCH 063/243] shed: expand homedir in repo path for msgindex tools --- cmd/lotus-shed/msgindex.go | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/cmd/lotus-shed/msgindex.go b/cmd/lotus-shed/msgindex.go index d2cb2cf3dab..fae733bbe64 100644 --- a/cmd/lotus-shed/msgindex.go +++ b/cmd/lotus-shed/msgindex.go @@ -7,6 +7,7 @@ import ( "github.com/ipfs/go-cid" _ "github.com/mattn/go-sqlite3" + "github.com/mitchellh/go-homedir" "github.com/urfave/cli/v2" "golang.org/x/xerrors" @@ -64,7 +65,12 @@ var msgindexBackfillCmd = &cli.Command{ } epochs := cctx.Int("epochs") - dbPath := path.Join(cctx.String("repo"), "sqlite", "msgindex.db") + basePath, err := homedir.Expand(cctx.String("repo")) + if err != nil { + return err + } + + dbPath := path.Join(basePath, "sqlite", "msgindex.db") db, err := sql.Open("sqlite3", dbPath) if err != nil { return err @@ -176,7 +182,12 @@ var msgindexPruneCmd = &cli.Command{ } } - dbPath := path.Join(cctx.String("repo"), "sqlite", "msgindex.db") + basePath, err := homedir.Expand(cctx.String("repo")) + if err != nil { + return err + } + + dbPath := path.Join(basePath, "sqlite", "msgindex.db") db, err := sql.Open("sqlite3", dbPath) if err != nil { return err From 91fccc421ae87623b64617d1c3e0ec5fde7f4a4b Mon Sep 17 00:00:00 2001 From: vyzo Date: Thu, 16 Mar 2023 17:25:20 +0200 Subject: [PATCH 064/243] add ON CONFLICT REPLACE clause in messages --- chain/index/msgindex.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chain/index/msgindex.go b/chain/index/msgindex.go index 0140b3e7471..aa23b4e4d8d 100644 --- a/chain/index/msgindex.go +++ b/chain/index/msgindex.go @@ -26,7 +26,7 @@ var log = logging.Logger("msgindex") var dbName = "msgindex.db" var dbDefs = []string{ `CREATE TABLE IF NOT EXISTS messages ( - cid VARCHAR(80) PRIMARY KEY, + cid VARCHAR(80) PRIMARY KEY ON CONFLICT REPLACE, tipset_cid VARCHAR(80) NOT NULL, epoch INTEGER NOT NULL )`, From 8bceaadda8b97e8591b673d15904dd57856e98c5 Mon Sep 17 00:00:00 2001 From: vyzo Date: Thu, 16 Mar 2023 17:32:06 +0200 Subject: [PATCH 065/243] chain errors in searchForIndexedMsg --- chain/stmgr/searchwait.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/chain/stmgr/searchwait.go b/chain/stmgr/searchwait.go index e9feeec12ec..2d12628677d 100644 --- a/chain/stmgr/searchwait.go +++ b/chain/stmgr/searchwait.go @@ -176,7 +176,7 @@ func (sm *StateManager) SearchForMessage(ctx context.Context, head *types.TipSet func (sm *StateManager) searchForIndexedMsg(ctx context.Context, mcid cid.Cid, m types.ChainMsg) (*types.TipSet, *types.MessageReceipt, cid.Cid, error) { minfo, err := sm.msgIndex.GetMsgInfo(ctx, mcid) if err != nil { - return nil, nil, cid.Undef, err + return nil, nil, cid.Undef, xerrors.Errorf("error looking up message in index: %w", err) } // check the height against the current tipset; minimum execution confidence requires that the @@ -190,7 +190,7 @@ func (sm *StateManager) searchForIndexedMsg(ctx context.Context, mcid cid.Cid, m // TODO optimization: the index should have it implicitly so we can return it in the msginfo. xts, err := sm.cs.GetTipsetByHeight(ctx, minfo.Epoch+1, curTs, false) if err != nil { - return nil, nil, cid.Undef, err + return nil, nil, cid.Undef, xerrors.Errorf("error looking up execution tipset: %w", err) } // check that it is indeed the parent of the inclusion tipset @@ -205,7 +205,7 @@ func (sm *StateManager) searchForIndexedMsg(ctx context.Context, mcid cid.Cid, m } r, foundMsg, err := sm.tipsetExecutedMessage(ctx, xts, mcid, m.VMMessage(), false) - return xts, r, foundMsg, err + return xts, r, foundMsg, xerrors.Errorf("error in tipstExecutedMessage: %w", err) } // searchBackForMsg searches up to limit tipsets backwards from the given From 90c4763e0074727715760e9119e07b987ce0e4a8 Mon Sep 17 00:00:00 2001 From: vyzo Date: Thu, 16 Mar 2023 17:33:12 +0200 Subject: [PATCH 066/243] fix typos --- chain/index/interface.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/chain/index/interface.go b/chain/index/interface.go index 8907dc09d94..f875a94bf79 100644 --- a/chain/index/interface.go +++ b/chain/index/interface.go @@ -16,9 +16,9 @@ var ErrClosed = errors.New("index closed") type MsgInfo struct { // the message this record refers to Message cid.Cid - // the tipset where this messages was included + // the tipset where this message was included TipSet cid.Cid - // the epoch whre this message was included + // the epoch where this message was included Epoch abi.ChainEpoch } From ff22a462533c7b87f5245633a5b121339b11cfc1 Mon Sep 17 00:00:00 2001 From: vyzo Date: Thu, 16 Mar 2023 17:38:46 +0200 Subject: [PATCH 067/243] complain if head change processing is building backlog --- chain/index/msgindex.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/chain/index/msgindex.go b/chain/index/msgindex.go index aa23b4e4d8d..16b47616f76 100644 --- a/chain/index/msgindex.go +++ b/chain/index/msgindex.go @@ -290,9 +290,14 @@ func (x *msgIndex) onHeadChange(rev, app []*types.TipSet) error { // do it in the background to avoid blocking head change processing x.mx.Lock() x.pend = append(x.pend, headChange{rev: rev, app: app}) - // TODO log loudly if this is building backlog (it shouldn't but better be safe on this) + pendLen := len(x.pend) x.mx.Unlock() + // complain loudly if this is building backlog + if pendLen > 10 { + log.Warnf("message index head change processing is building backlog: %d pending head changes", pendLen) + } + select { case x.sema <- struct{}{}: default: From 1e7f5c6a1e8e23de27ac006ee84e1feb0dbc8b05 Mon Sep 17 00:00:00 2001 From: vyzo Date: Thu, 16 Mar 2023 17:41:06 +0200 Subject: [PATCH 068/243] shut down the index if there is an error during head processing --- chain/index/msgindex.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/chain/index/msgindex.go b/chain/index/msgindex.go index 16b47616f76..534a11cd40a 100644 --- a/chain/index/msgindex.go +++ b/chain/index/msgindex.go @@ -314,8 +314,11 @@ func (x *msgIndex) background(ctx context.Context) { case <-x.sema: err := x.processHeadChanges(ctx) if err != nil { - // TODO should we shut down the index altogether? we just log for now. - log.Errorf("error processing head change notifications: %s", err) + // we can't rely on an inconsistent index, so shut it down. + log.Errorf("error processing head change notifications: %s; shutting down message index", err) + if err2 := x.Close(); err2 != nil { + log.Errorf("error shutting down index: %s", err2) + } } case <-ctx.Done(): From 9c087cc52c0e059e4944870c8619399bb33dd7fe Mon Sep 17 00:00:00 2001 From: vyzo Date: Thu, 16 Mar 2023 17:42:09 +0200 Subject: [PATCH 069/243] second error variable name to avoid confusing they yushie. --- chain/index/msgindex.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/chain/index/msgindex.go b/chain/index/msgindex.go index 534a11cd40a..d5c6a252e45 100644 --- a/chain/index/msgindex.go +++ b/chain/index/msgindex.go @@ -341,8 +341,8 @@ func (x *msgIndex) processHeadChanges(ctx context.Context) error { for _, hc := range pend { for _, ts := range hc.rev { if err := x.doRevert(ctx, tx, ts); err != nil { - if err := tx.Rollback(); err != nil { - log.Errorf("error rolling back transaction: %s", err) + if err2 := tx.Rollback(); err2 != nil { + log.Errorf("error rolling back transaction: %s", err2) } return xerrors.Errorf("error reverting %s: %w", ts, err) } @@ -350,8 +350,8 @@ func (x *msgIndex) processHeadChanges(ctx context.Context) error { for _, ts := range hc.app { if err := x.doApply(ctx, tx, ts); err != nil { - if err := tx.Rollback(); err != nil { - log.Errorf("error rolling back transaction: %s", err) + if err2 := tx.Rollback(); err2 != nil { + log.Errorf("error rolling back transaction: %s", err2) } return xerrors.Errorf("error applying %s: %w", ts, err) } From ef2f2b0f89e76941b52a9746142f6e5f0b207348 Mon Sep 17 00:00:00 2001 From: vyzo Date: Thu, 16 Mar 2023 17:43:56 +0200 Subject: [PATCH 070/243] reword funny comment --- chain/stmgr/searchwait.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chain/stmgr/searchwait.go b/chain/stmgr/searchwait.go index 2d12628677d..5d232d105d8 100644 --- a/chain/stmgr/searchwait.go +++ b/chain/stmgr/searchwait.go @@ -193,7 +193,7 @@ func (sm *StateManager) searchForIndexedMsg(ctx context.Context, mcid cid.Cid, m return nil, nil, cid.Undef, xerrors.Errorf("error looking up execution tipset: %w", err) } - // check that it is indeed the parent of the inclusion tipset + // check that the parent of the execution index is indeed the inclusion tipset parent := xts.Parents() parentCid, err := parent.Cid() if err != nil { From 3710768910b07fd4d082c6e47c5bf9790f2a4f41 Mon Sep 17 00:00:00 2001 From: vyzo Date: Thu, 16 Mar 2023 17:45:16 +0200 Subject: [PATCH 071/243] add TODO for WaitForMessage to use the index --- chain/stmgr/searchwait.go | 1 + 1 file changed, 1 insertion(+) diff --git a/chain/stmgr/searchwait.go b/chain/stmgr/searchwait.go index 5d232d105d8..89d0b252f94 100644 --- a/chain/stmgr/searchwait.go +++ b/chain/stmgr/searchwait.go @@ -19,6 +19,7 @@ import ( // happened, with an optional limit to how many epochs it will search. It guarantees that the message has been on // chain for at least confidence epochs without being reverted before returning. func (sm *StateManager) WaitForMessage(ctx context.Context, mcid cid.Cid, confidence uint64, lookbackLimit abi.ChainEpoch, allowReplaced bool) (*types.TipSet, *types.MessageReceipt, cid.Cid, error) { + // TODO use the index to speed this up. ctx, cancel := context.WithCancel(ctx) defer cancel() From 883bbf8701b69d5f96ee61259cb913d972cb3819 Mon Sep 17 00:00:00 2001 From: vyzo Date: Thu, 16 Mar 2023 17:51:28 +0200 Subject: [PATCH 072/243] add sanity check in SearchForIndexedMsg call site Check that receipt is non nil, and the message was indeed found. --- chain/stmgr/searchwait.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/chain/stmgr/searchwait.go b/chain/stmgr/searchwait.go index 89d0b252f94..6b9adff714b 100644 --- a/chain/stmgr/searchwait.go +++ b/chain/stmgr/searchwait.go @@ -151,7 +151,17 @@ func (sm *StateManager) SearchForMessage(ctx context.Context, head *types.TipSet switch { case err == nil: - return fts, r, foundMsg, nil + if r != nil && foundMsg.Defined() { + return fts, r, foundMsg, nil + } + + // debug log this, it's noteworthy + if r == nil { + log.Debugf("missing receipt for message in index for %s", mcid) + } + if !foundMsg.Defined() { + log.Debugf("message %s not found", mcid) + } case errors.Is(err, index.ErrNotFound): // ok for the index to have incomplete data From 8d260d7478a35ee4457b210d55a327074ff8b7a1 Mon Sep 17 00:00:00 2001 From: Alfonso de la Rocha Date: Thu, 16 Mar 2023 17:03:25 +0100 Subject: [PATCH 073/243] address review --- chain/sub/bcast/consistent.go | 61 +++++++++++++++++------------------ chain/sub/incoming.go | 4 +-- 2 files changed, 32 insertions(+), 33 deletions(-) diff --git a/chain/sub/bcast/consistent.go b/chain/sub/bcast/consistent.go index 22a0db16007..89708432c89 100644 --- a/chain/sub/bcast/consistent.go +++ b/chain/sub/bcast/consistent.go @@ -2,14 +2,12 @@ package bcast import ( "context" - "encoding/binary" - "fmt" "sync" "time" "github.com/ipfs/go-cid" logging "github.com/ipfs/go-log/v2" - "github.com/multiformats/go-multihash" + "golang.org/x/xerrors" "github.com/filecoin-project/go-state-types/abi" @@ -24,7 +22,7 @@ const ( GcSanityCheck = 5 // GcLookback determines the number of epochs kept in the consistent // broadcast cache. - GcLookback = 2 + GcLookback = 1000 ) type blksInfo struct { @@ -41,20 +39,20 @@ type bcastDict struct { m *sync.Map } -func (bd *bcastDict) load(key multihash.Multihash) (*blksInfo, bool) { - v, ok := bd.m.Load(key.String()) +func (bd *bcastDict) load(key []byte) (*blksInfo, bool) { + v, ok := bd.m.Load(key) if !ok { return nil, ok } return v.(*blksInfo), ok } -func (bd *bcastDict) store(key multihash.Multihash, d *blksInfo) { - bd.m.Store(key.String(), d) +func (bd *bcastDict) store(key []byte, d *blksInfo) { + bd.m.Store(key, d) } -func (bd *bcastDict) blkLen(key multihash.Multihash) int { - v, ok := bd.m.Load(key.String()) +func (bd *bcastDict) blkLen(key []byte) int { + v, ok := bd.m.Load(key) if !ok { return 0 } @@ -64,21 +62,15 @@ func (bd *bcastDict) blkLen(key multihash.Multihash) int { type ConsistentBCast struct { lk sync.RWMutex delay time.Duration - // FIXME: Make this a slice??? Less storage but needs indexing logic. - m map[abi.ChainEpoch]*bcastDict + m map[abi.ChainEpoch]*bcastDict } func newBcastDict() *bcastDict { return &bcastDict{new(sync.Map)} } -// TODO: the VRFProof may already be small enough so we may not need to use a hash here. -// we can maybe bypass the useless computation. -func BCastKey(bh *types.BlockHeader) (multihash.Multihash, error) { - k := make([]byte, len(bh.Ticket.VRFProof)) - copy(k, bh.Ticket.VRFProof) - binary.PutVarint(k, int64(bh.Height)) - return multihash.Sum(k, multihash.SHA2_256, -1) +func BCastKey(bh *types.BlockHeader) []byte { + return bh.Ticket.VRFProof } func NewConsistentBCast(delay time.Duration) *ConsistentBCast { @@ -99,7 +91,7 @@ func cidExists(cids []cid.Cid, c cid.Cid) bool { func (bInfo *blksInfo) eqErr() error { bInfo.cancel() - return fmt.Errorf("different blocks with the same ticket already seen") + return xerrors.Errorf("different blocks with the same ticket already seen") } func (cb *ConsistentBCast) Len() int { @@ -108,6 +100,17 @@ func (cb *ConsistentBCast) Len() int { return len(cb.m) } +// RcvBlock is called every time a new block is received through the network. +// +// This function keeps track of all the blocks with a specific VRFProof received +// for the same height. Every time a new block with a VRFProof not seen at certain +// height is received, a new timer is triggered to wait for the delay time determined by +// the consistent broadcast before informing the syncer. During this time, if a new +// block with the same VRFProof for that height is received, it means a miner is +// trying to equivocate, and both blocks are discarded. +// +// The delay time should be set to a value high enough to allow any block sent for +// certain epoch to be propagated to a large amount of miners in the network. func (cb *ConsistentBCast) RcvBlock(ctx context.Context, blk *types.BlockMsg) { cb.lk.Lock() bcastDict, ok := cb.m[blk.Header.Height] @@ -116,11 +119,7 @@ func (cb *ConsistentBCast) RcvBlock(ctx context.Context, blk *types.BlockMsg) { cb.m[blk.Header.Height] = bcastDict } cb.lk.Unlock() - key, err := BCastKey(blk.Header) - if err != nil { - log.Errorf("couldn't hash blk info for height %d: %s", blk.Header.Height, err) - return - } + key := BCastKey(blk.Header) blkCid := blk.Cid() bInfo, ok := bcastDict.load(key) @@ -142,22 +141,22 @@ func (cb *ConsistentBCast) RcvBlock(ctx context.Context, blk *types.BlockMsg) { bcastDict.store(key, &blksInfo{ctx, cancel, []cid.Cid{blkCid}}) } +// WaitForDelivery is called before informing the syncer about a new block +// to check if the consistent broadcast delay triggered or if the block should +// be held off for a bit more time. func (cb *ConsistentBCast) WaitForDelivery(bh *types.BlockHeader) error { cb.lk.RLock() bcastDict := cb.m[bh.Height] cb.lk.RUnlock() - key, err := BCastKey(bh) - if err != nil { - return err - } + key := BCastKey(bh) bInfo, ok := bcastDict.load(key) if !ok { - return fmt.Errorf("something went wrong, unknown block with Epoch + VRFProof (cid=%s) in consistent broadcast storage", key) + return xerrors.Errorf("something went wrong, unknown block with Epoch + VRFProof (cid=%s) in consistent broadcast storage", key) } // Wait for the timeout <-bInfo.ctx.Done() if bcastDict.blkLen(key) > 1 { - return fmt.Errorf("equivocation detected for epoch %d. Two blocks being broadcast with same VRFProof", bh.Height) + return xerrors.Errorf("equivocation detected for epoch %d. Two blocks being broadcast with same VRFProof", bh.Height) } return nil } diff --git a/chain/sub/incoming.go b/chain/sub/incoming.go index 6226e45d8a0..6436cc27d0e 100644 --- a/chain/sub/incoming.go +++ b/chain/sub/incoming.go @@ -113,13 +113,13 @@ func HandleIncomingBlocks(ctx context.Context, bsub *pubsub.Subscription, self p if src != self { log.Debugf("Waiting for consistent broadcast of block in height: %v", blk.Header.Height) if err := cb.WaitForDelivery(blk.Header); err != nil { - log.Errorf("couldn't deliver block to syncer over pubsub: %s; source: %s", err, src) + log.Errorf("not informing syncer about new block, potential equivocation detected (cid: %s, source: %s): %s; ", blk.Header.Cid(), src, err) return } } // Garbage collect the broadcast state cb.GarbageCollect(blk.Header.Height) - log.Debugf("Block in height %v delivered successfully (cid=)", blk.Header.Height, blk.Cid()) + log.Debugf("Block in height %v delivered successfully (cid=%s)", blk.Header.Height, blk.Cid()) if s.InformNewBlock(msg.ReceivedFrom, &types.FullBlock{ Header: blk.Header, From 90c2f9dbe234426eec8045cea66048e50248215d Mon Sep 17 00:00:00 2001 From: Alfonso de la Rocha Date: Thu, 16 Mar 2023 17:17:59 +0100 Subject: [PATCH 074/243] minor fix --- chain/sub/bcast/consistent.go | 6 +++--- chain/sub/bcast/consistent_test.go | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/chain/sub/bcast/consistent.go b/chain/sub/bcast/consistent.go index 89708432c89..165476ffb6f 100644 --- a/chain/sub/bcast/consistent.go +++ b/chain/sub/bcast/consistent.go @@ -40,7 +40,7 @@ type bcastDict struct { } func (bd *bcastDict) load(key []byte) (*blksInfo, bool) { - v, ok := bd.m.Load(key) + v, ok := bd.m.Load(string(key)) if !ok { return nil, ok } @@ -48,11 +48,11 @@ func (bd *bcastDict) load(key []byte) (*blksInfo, bool) { } func (bd *bcastDict) store(key []byte, d *blksInfo) { - bd.m.Store(key, d) + bd.m.Store(string(key), d) } func (bd *bcastDict) blkLen(key []byte) int { - v, ok := bd.m.Load(key) + v, ok := bd.m.Load(string(key)) if !ok { return 0 } diff --git a/chain/sub/bcast/consistent_test.go b/chain/sub/bcast/consistent_test.go index ca84ab4b1d3..8beb0574f36 100644 --- a/chain/sub/bcast/consistent_test.go +++ b/chain/sub/bcast/consistent_test.go @@ -61,7 +61,7 @@ func testSimpleDelivery(t *testing.T, cb *bcast.ConsistentBCast, epoch abi.Chain func TestSeveralEpochs(t *testing.T) { cb := bcast.NewConsistentBCast(TEST_DELAY) - numEpochs := 5 + numEpochs := 6 wg := new(sync.WaitGroup) wg.Add(numEpochs) for i := 0; i < numEpochs; i++ { @@ -83,7 +83,7 @@ func TestSeveralEpochs(t *testing.T) { }(i) } wg.Wait() - require.Equal(t, cb.Len(), bcast.GcLookback) + require.Equal(t, cb.Len(), numEpochs) } // bias is expected to be 0-1 From 02db37bf373fdb55f025a6a5b492a30b17316afb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 20 Mar 2023 10:37:48 +0100 Subject: [PATCH 075/243] feat: shed: incoming block-sub chainwatch tool --- cmd/lotus-shed/chainwatch.go | 377 +++++++++++++++++++++++++++++++++++ cmd/lotus-shed/main.go | 1 + 2 files changed, 378 insertions(+) create mode 100644 cmd/lotus-shed/chainwatch.go diff --git a/cmd/lotus-shed/chainwatch.go b/cmd/lotus-shed/chainwatch.go new file mode 100644 index 00000000000..9f6897027af --- /dev/null +++ b/cmd/lotus-shed/chainwatch.go @@ -0,0 +1,377 @@ +package main + +import ( + "container/list" + "context" + "database/sql" + "fmt" + "hash/crc32" + "strconv" + "sync" + "time" + + "github.com/ipfs/go-cid" + "github.com/urfave/cli/v2" + + "github.com/filecoin-project/lotus/api" + "github.com/filecoin-project/lotus/chain/store" + "github.com/filecoin-project/lotus/chain/types" + cliutil "github.com/filecoin-project/lotus/cli/util" +) + +var chainwatchCmd = &cli.Command{ + Name: "chainwatch", + Usage: "lotus chainwatch", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "db", + EnvVars: []string{"CHAINWATCH_DB"}, + Value: "./chainwatch.db", + }, + }, + Subcommands: []*cli.Command{ + chainwatchRunCmd, + chainwatchDotCmd, + }, +} + +var chainwatchDotCmd = &cli.Command{ + Name: "dot", + Usage: "generate dot graphs", + ArgsUsage: " ", + Action: func(cctx *cli.Context) error { + st, err := cwOpenStorage(cctx.String("db")) + if err != nil { + return err + } + + minH, err := strconv.ParseInt(cctx.Args().Get(0), 10, 32) + tosee, err := strconv.ParseInt(cctx.Args().Get(1), 10, 32) + maxH := minH + tosee + + res, err := st.db.Query(`select block, parent, b.miner, b.height, p.height from block_parents + inner join blocks b on block_parents.block = b.cid + inner join blocks p on block_parents.parent = p.cid +where b.height > ? and b.height < ?`, minH, maxH) + + if err != nil { + return err + } + + fmt.Println("digraph D {") + + for res.Next() { + var block, parent, miner string + var height, ph uint64 + if err := res.Scan(&block, &parent, &miner, &height, &ph); err != nil { + return err + } + + bc, err := cid.Parse(block) + if err != nil { + return err + } + + has := st.hasBlock(bc) + + col := crc32.Checksum([]byte(miner), crc32.MakeTable(crc32.Castagnoli))&0xc0c0c0c0 + 0x30303030 + + hasstr := "" + if !has { + //col = 0xffffffff + hasstr = " UNSYNCED" + } + + nulls := height - ph - 1 + for i := uint64(0); i < nulls; i++ { + name := block + "NP" + fmt.Sprint(i) + + fmt.Printf("%s [label = \"NULL:%d\", fillcolor = \"#ffddff\", style=filled, forcelabels=true]\n%s -> %s\n", + name, height-nulls+i, name, parent) + + parent = name + } + + fmt.Printf("%s [label = \"%s:%d%s\", fillcolor = \"#%06x\", style=filled, forcelabels=true]\n%s -> %s\n", block, miner, height, hasstr, col, block, parent) + } + if res.Err() != nil { + return res.Err() + } + + fmt.Println("}") + + return nil + }, +} + +var chainwatchRunCmd = &cli.Command{ + Name: "run", + Usage: "Start lotus chainwatch", + + Action: func(cctx *cli.Context) error { + api, closer, err := cliutil.GetFullNodeAPIV1(cctx) + if err != nil { + return err + } + defer closer() + ctx := cliutil.ReqContext(cctx) + + v, err := api.Version(ctx) + if err != nil { + return err + } + + log.Infof("Remote version: %s", v.Version) + + st, err := cwOpenStorage(cctx.String("db")) // todo flag + if err != nil { + return err + } + defer st.close() + + cwRunSyncer(ctx, api, st) + go cwSubBlocks(ctx, api, st) + + <-ctx.Done() + return nil + }, +} + +func cwSubBlocks(ctx context.Context, api api.FullNode, st *cwStorage) { + sub, err := api.SyncIncomingBlocks(ctx) + if err != nil { + log.Error(err) + return + } + + for bh := range sub { + err := st.storeHeaders(map[cid.Cid]*types.BlockHeader{ + bh.Cid(): bh, + }, false) + if err != nil { + log.Error(err) + } + } +} + +func cwRunSyncer(ctx context.Context, api api.FullNode, st *cwStorage) { + notifs, err := api.ChainNotify(ctx) + if err != nil { + panic(err) + } + go func() { + for notif := range notifs { + for _, change := range notif { + switch change.Type { + case store.HCCurrent: + fallthrough + case store.HCApply: + syncHead(ctx, api, st, change.Val) + case store.HCRevert: + log.Warnf("revert todo") + } + } + } + }() +} + +func syncHead(ctx context.Context, api api.FullNode, st *cwStorage, ts *types.TipSet) { + log.Infof("Getting headers / actors") + + toSync := map[cid.Cid]*types.BlockHeader{} + toVisit := list.New() + + for _, header := range ts.Blocks() { + toVisit.PushBack(header) + } + + for toVisit.Len() > 0 { + bh := toVisit.Remove(toVisit.Back()).(*types.BlockHeader) + + if _, seen := toSync[bh.Cid()]; seen || st.hasBlock(bh.Cid()) { + continue + } + + toSync[bh.Cid()] = bh + + if len(toSync)%500 == 10 { + log.Infof("todo: (%d) %s", len(toSync), bh.Cid()) + } + + if len(bh.Parents) == 0 { + continue + } + + if bh.Height <= 530000 { + continue + } + + pts, err := api.ChainGetTipSet(ctx, types.NewTipSetKey(bh.Parents...)) + if err != nil { + log.Error(err) + continue + } + + for _, header := range pts.Blocks() { + toVisit.PushBack(header) + } + } + + log.Infof("Syncing %d blocks", len(toSync)) + + log.Infof("Persisting headers") + if err := st.storeHeaders(toSync, true); err != nil { + log.Error(err) + return + } + + log.Infof("Sync done") +} + +type cwStorage struct { + db *sql.DB + + headerLk sync.Mutex +} + +func cwOpenStorage(dbSource string) (*cwStorage, error) { + db, err := sql.Open("sqlite3", dbSource) + if err != nil { + return nil, err + } + + st := &cwStorage{db: db} + + return st, st.setup() +} + +func (st *cwStorage) setup() error { + tx, err := st.db.Begin() + if err != nil { + return err + } + _, err = tx.Exec(` +create table if not exists blocks +( + cid text not null + constraint blocks_pk + primary key, + parentWeight numeric not null, + parentStateRoot text not null, + height int not null, + miner text not null + constraint blocks_id_address_map_miner_fk + references id_address_map (address), + timestamp int not null, + vrfproof blob +); + +create unique index if not exists block_cid_uindex + on blocks (cid); + +create table if not exists blocks_synced +( + cid text not null + constraint blocks_synced_pk + primary key + constraint blocks_synced_blocks_cid_fk + references blocks, + add_ts int not null +); + +create unique index if not exists blocks_synced_cid_uindex + on blocks_synced (cid); + +create table if not exists block_parents +( + block text not null + constraint block_parents_blocks_cid_fk + references blocks, + parent text not null + constraint block_parents_blocks_cid_fk_2 + references blocks +); + +create unique index if not exists block_parents_block_parent_uindex + on block_parents (block, parent); + +create unique index if not exists blocks_cid_uindex + on blocks (cid); +`) + if err != nil { + return err + } + return tx.Commit() +} + +func (st *cwStorage) hasBlock(bh cid.Cid) bool { + var exitsts bool + err := st.db.QueryRow(`select exists (select 1 FROM blocks_synced where cid=?)`, bh.String()).Scan(&exitsts) + if err != nil { + log.Error(err) + return false + } + return exitsts +} + +func (st *cwStorage) storeHeaders(bhs map[cid.Cid]*types.BlockHeader, sync bool) error { + st.headerLk.Lock() + defer st.headerLk.Unlock() + + tx, err := st.db.Begin() + if err != nil { + return err + } + + stmt, err := tx.Prepare(`insert into blocks (cid, parentWeight, parentStateRoot, height, miner, "timestamp", vrfproof) values (?, ?, ?, ?, ?, ?, ?) on conflict do nothing`) + if err != nil { + return err + } + defer stmt.Close() + for _, bh := range bhs { + if _, err := stmt.Exec(bh.Cid().String(), + bh.ParentWeight.String(), + bh.ParentStateRoot.String(), + bh.Height, + bh.Miner.String(), + bh.Timestamp, + bh.Ticket.VRFProof, + ); err != nil { + return err + } + } + + stmt2, err := tx.Prepare(`insert into block_parents (block, parent) values (?, ?) on conflict do nothing`) + if err != nil { + return err + } + defer stmt2.Close() + for _, bh := range bhs { + for _, parent := range bh.Parents { + if _, err := stmt2.Exec(bh.Cid().String(), parent.String()); err != nil { + return err + } + } + } + + if sync { + stmt, err := tx.Prepare(`insert into blocks_synced (cid, add_ts) values (?, ?) on conflict do nothing`) + if err != nil { + return err + } + defer stmt.Close() + now := time.Now().Unix() + + for _, bh := range bhs { + if _, err := stmt.Exec(bh.Cid().String(), now); err != nil { + return err + } + } + } + + return tx.Commit() +} + +func (st *cwStorage) close() error { + return st.db.Close() +} diff --git a/cmd/lotus-shed/main.go b/cmd/lotus-shed/main.go index 19072dd7191..d984aa6bd33 100644 --- a/cmd/lotus-shed/main.go +++ b/cmd/lotus-shed/main.go @@ -26,6 +26,7 @@ func main() { base32Cmd, base16Cmd, bitFieldCmd, + chainwatchCmd, cronWcCmd, frozenMinersCmd, dealLabelCmd, From fa7e1ef78eb505dd01ba94d6e3987e13a1f1f119 Mon Sep 17 00:00:00 2001 From: Alfonso de la Rocha Date: Mon, 20 Mar 2023 18:10:34 +0100 Subject: [PATCH 076/243] set CB delay to 2 secs --- build/params_mainnet.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/params_mainnet.go b/build/params_mainnet.go index 1612f4ab9b4..e2e2b8c8e96 100644 --- a/build/params_mainnet.go +++ b/build/params_mainnet.go @@ -130,4 +130,4 @@ var WhitelistedBlock = MustParseCid("bafy2bzaceapyg2uyzk7vueh3xccxkuwbz3nxewjygu // CBDeliveryDelay is the delay before deliver in the synchronous consistent broadcast. // This determines the wait time for the detection of potential equivocations. -var CBDeliveryDelay = 6 * time.Second +var CBDeliveryDelay = 2 * time.Second From 694202025348bbd3081507422488f24badee9878 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Tue, 21 Mar 2023 12:06:46 +0100 Subject: [PATCH 077/243] Ignore blockstore.Get errors when the top-level-task is Receipts --- chain/store/snapshot.go | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/chain/store/snapshot.go b/chain/store/snapshot.go index 0852127c4c8..6277ea416da 100644 --- a/chain/store/snapshot.go +++ b/chain/store/snapshot.go @@ -3,12 +3,14 @@ package store import ( "bytes" "context" + "errors" "fmt" "io" "sync" "time" "github.com/ipfs/go-cid" + format "github.com/ipfs/go-ipld-format" blocks "github.com/ipfs/go-libipfs/blocks" "github.com/ipld/go-car" carutil "github.com/ipld/go-car/util" @@ -170,7 +172,7 @@ type walkTask struct { c cid.Cid taskType walkSchedTaskType topLevelTaskType walkSchedTaskType - topLevelTaskCid cid.Cid + blockCid cid.Cid epoch abi.ChainEpoch } @@ -323,7 +325,7 @@ func newWalkScheduler(ctx context.Context, store bstore.Blockstore, cfg walkSche c: b.Cid(), taskType: blockTask, topLevelTaskType: blockTask, - topLevelTaskCid: b.Cid(), + blockCid: b.Cid(), epoch: cfg.head.Height(), }: } @@ -422,8 +424,17 @@ func (s *walkScheduler) processTask(t walkTask, workerN int) error { } blk, err := s.store.Get(s.ctx, t.c) + if errors.Is(err, format.ErrNotFound{}) && t.topLevelTaskType == receiptTask { + log.Warnw("ignoring not-found block in Receipts", + "block", t.blockCid, + "epoch", t.epoch, + "cid", t.c) + return nil + } if err != nil { - return xerrors.Errorf("writing object to car. Task: %s. Top-Level: %s (%s). Epoch: %d. bs.Get: %w", t.taskType, t.topLevelTaskType, t.topLevelTaskCid, t.epoch, err) + return xerrors.Errorf( + "blockstore.Get(%s). Task: %s. Block: %s (%s). Epoch: %d. Err: %w", + t.c, t.taskType, t.topLevelTaskType, t.blockCid, t.epoch, err) } s.results <- taskResult{ @@ -447,7 +458,7 @@ func (s *walkScheduler) processTask(t walkTask, workerN int) error { c: b.Parents[i], taskType: dagTask, topLevelTaskType: blockTask, - topLevelTaskCid: t.c, + blockCid: b.Parents[i], epoch: 0, }) } @@ -455,7 +466,7 @@ func (s *walkScheduler) processTask(t walkTask, workerN int) error { c: b.ParentStateRoot, taskType: stateTask, topLevelTaskType: stateTask, - topLevelTaskCid: t.c, + blockCid: t.c, epoch: 0, }) @@ -467,7 +478,7 @@ func (s *walkScheduler) processTask(t walkTask, workerN int) error { c: b.Parents[i], taskType: blockTask, topLevelTaskType: blockTask, - topLevelTaskCid: t.c, + blockCid: b.Parents[i], epoch: b.Height, }) } @@ -482,7 +493,7 @@ func (s *walkScheduler) processTask(t walkTask, workerN int) error { c: b.Messages, taskType: messageTask, topLevelTaskType: messageTask, - topLevelTaskCid: t.c, + blockCid: t.c, epoch: b.Height, }) } @@ -492,7 +503,7 @@ func (s *walkScheduler) processTask(t walkTask, workerN int) error { c: b.ParentMessageReceipts, taskType: receiptTask, topLevelTaskType: receiptTask, - topLevelTaskCid: t.c, + blockCid: t.c, epoch: b.Height, }) } @@ -501,7 +512,7 @@ func (s *walkScheduler) processTask(t walkTask, workerN int) error { c: b.ParentStateRoot, taskType: stateTask, topLevelTaskType: stateTask, - topLevelTaskCid: t.c, + blockCid: t.c, epoch: b.Height, }) } @@ -519,7 +530,7 @@ func (s *walkScheduler) processTask(t walkTask, workerN int) error { c: c, taskType: dagTask, topLevelTaskType: t.topLevelTaskType, - topLevelTaskCid: t.topLevelTaskCid, + blockCid: t.blockCid, epoch: t.epoch, }) }) From f48c6268f8389f5c67d000b1a40a905642013623 Mon Sep 17 00:00:00 2001 From: Jorropo Date: Wed, 22 Mar 2023 03:24:48 +0100 Subject: [PATCH 078/243] chore: all: bump go-libipfs --- go.mod | 6 +++--- go.sum | 15 ++++++--------- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index d4dd212abe5..a4794c03a4f 100644 --- a/go.mod +++ b/go.mod @@ -78,7 +78,7 @@ require ( github.com/influxdata/influxdb1-client v0.0.0-20200827194710-b269163b24ab github.com/ipfs/bbloom v0.0.4 github.com/ipfs/go-blockservice v0.5.0 - github.com/ipfs/go-cid v0.3.2 + github.com/ipfs/go-cid v0.4.0 github.com/ipfs/go-cidutil v0.1.0 github.com/ipfs/go-datastore v0.6.0 github.com/ipfs/go-ds-badger2 v0.1.3 @@ -97,7 +97,7 @@ require ( github.com/ipfs/go-ipfs-util v0.0.2 github.com/ipfs/go-ipld-cbor v0.0.6 github.com/ipfs/go-ipld-format v0.4.0 - github.com/ipfs/go-libipfs v0.5.0 + github.com/ipfs/go-libipfs v0.7.0 github.com/ipfs/go-log/v2 v2.5.1 github.com/ipfs/go-merkledag v0.9.0 github.com/ipfs/go-metrics-interface v0.0.1 @@ -244,7 +244,7 @@ require ( github.com/ipfs/go-ipld-legacy v0.1.1 // indirect github.com/ipfs/go-ipns v0.3.0 // indirect github.com/ipfs/go-log v1.0.5 // indirect - github.com/ipfs/go-path v0.3.0 // indirect + github.com/ipfs/go-path v0.3.1 // indirect github.com/ipfs/go-peertaskqueue v0.8.1 // indirect github.com/ipfs/go-verifcid v0.0.2 // indirect github.com/ipld/go-ipld-adl-hamt v0.0.0-20220616142416-9004dbd839e0 // indirect diff --git a/go.sum b/go.sum index 5e20e850fca..1d9e1026268 100644 --- a/go.sum +++ b/go.sum @@ -698,8 +698,8 @@ github.com/ipfs/go-cid v0.0.6/go.mod h1:6Ux9z5e+HpkQdckYoX1PG/6xqKspzlEIR5SDmgqg github.com/ipfs/go-cid v0.0.7/go.mod h1:6Ux9z5e+HpkQdckYoX1PG/6xqKspzlEIR5SDmgqgC/I= github.com/ipfs/go-cid v0.1.0/go.mod h1:rH5/Xv83Rfy8Rw6xG+id3DYAMUVmem1MowoKwdXmN2o= github.com/ipfs/go-cid v0.2.0/go.mod h1:P+HXFDF4CVhaVayiEb4wkAy7zBHxBwsJyt0Y5U6MLro= -github.com/ipfs/go-cid v0.3.2 h1:OGgOd+JCFM+y1DjWPmVH+2/4POtpDzwcr7VgnB7mZXc= -github.com/ipfs/go-cid v0.3.2/go.mod h1:gQ8pKqT/sUxGY+tIwy1RPpAojYu7jAyCp5Tz1svoupw= +github.com/ipfs/go-cid v0.4.0 h1:a4pdZq0sx6ZSxbCizebnKiMCx/xI/aBBFlB73IgH4rA= +github.com/ipfs/go-cid v0.4.0/go.mod h1:uQHwDeX4c6CtyrFwdqyhpNcxVewur1M7l7fNU7LKwZk= github.com/ipfs/go-cidutil v0.1.0 h1:RW5hO7Vcf16dplUU60Hs0AKDkQAVPVplr7lk97CFL+Q= github.com/ipfs/go-cidutil v0.1.0/go.mod h1:e7OEVBMIv9JaOxt9zaGEmAoSlXW9jdFZ5lP/0PwcfpA= github.com/ipfs/go-datastore v0.0.1/go.mod h1:d4KVXhMt913cLBEI/PXAy6ko+W7e9AhyAKBGh803qeE= @@ -731,7 +731,6 @@ github.com/ipfs/go-ds-leveldb v0.5.0 h1:s++MEBbD3ZKc9/8/njrn4flZLnCuY9I79v94gBUN github.com/ipfs/go-ds-leveldb v0.5.0/go.mod h1:d3XG9RUDzQ6V4SHi8+Xgj9j1XuEk1z82lquxrVbml/Q= github.com/ipfs/go-ds-measure v0.2.0 h1:sG4goQe0KDTccHMyT45CY1XyUbxe5VwTKpg2LjApYyQ= github.com/ipfs/go-ds-measure v0.2.0/go.mod h1:SEUD/rE2PwRa4IQEC5FuNAmjJCyYObZr9UvVh8V3JxE= -github.com/ipfs/go-fetcher v1.6.1/go.mod h1:27d/xMV8bodjVs9pugh/RCjjK2OZ68UgAMspMdingNo= github.com/ipfs/go-filestore v1.2.0 h1:O2wg7wdibwxkEDcl7xkuQsPvJFRBVgVSsOJ/GP6z3yU= github.com/ipfs/go-filestore v1.2.0/go.mod h1:HLJrCxRXquTeEEpde4lTLMaE/MYJZD7WHLkp9z6+FF8= github.com/ipfs/go-fs-lock v0.0.6/go.mod h1:OTR+Rj9sHiRubJh3dRhD15Juhd/+w6VPOY28L7zESmM= @@ -805,8 +804,8 @@ github.com/ipfs/go-ipld-legacy v0.1.1 h1:BvD8PEuqwBHLTKqlGFTHSwrwFOMkVESEvwIYwR2 github.com/ipfs/go-ipld-legacy v0.1.1/go.mod h1:8AyKFCjgRPsQFf15ZQgDB8Din4DML/fOmKZkkFkrIEg= github.com/ipfs/go-ipns v0.3.0 h1:ai791nTgVo+zTuq2bLvEGmWP1M0A6kGTXUsgv/Yq67A= github.com/ipfs/go-ipns v0.3.0/go.mod h1:3cLT2rbvgPZGkHJoPO1YMJeh6LtkxopCkKFcio/wE24= -github.com/ipfs/go-libipfs v0.5.0 h1:gtvzeoTdUHPUN4B5izzyBS3Cxtpvi+l7hd2mmjN+teM= -github.com/ipfs/go-libipfs v0.5.0/go.mod h1:iZ9QyhzNr3AkxRXrbQYb//rv7iLyvZJX0GNuc3lJDiQ= +github.com/ipfs/go-libipfs v0.7.0 h1:Mi54WJTODaOL2/ZSm5loi3SwI3jI2OuFWUrQIkJ5cpM= +github.com/ipfs/go-libipfs v0.7.0/go.mod h1:KsIf/03CqhICzyRGyGo68tooiBE2iFbI/rXW7FhAYr0= github.com/ipfs/go-log v0.0.1/go.mod h1:kL1d2/hzSpI0thNYjiKfjanbVNU+IIGA/WnNESY9leM= github.com/ipfs/go-log v1.0.0/go.mod h1:JO7RzlMK6rA+CIxFMLOuB6Wf5b81GDiKElL7UPSIKjA= github.com/ipfs/go-log v1.0.1/go.mod h1:HuWlQttfN6FWNHRhlY5yMk/lW7evQC0HHGOxEwMRR8I= @@ -838,17 +837,15 @@ github.com/ipfs/go-metrics-interface v0.0.1 h1:j+cpbjYvu4R8zbleSs36gvB7jR+wsL2fG github.com/ipfs/go-metrics-interface v0.0.1/go.mod h1:6s6euYU4zowdslK0GKHmqaIZ3j/b/tL7HTWtJ4VPgWY= github.com/ipfs/go-metrics-prometheus v0.0.2 h1:9i2iljLg12S78OhC6UAiXi176xvQGiZaGVF1CUVdE+s= github.com/ipfs/go-metrics-prometheus v0.0.2/go.mod h1:ELLU99AQQNi+zX6GCGm2lAgnzdSH3u5UVlCdqSXnEks= -github.com/ipfs/go-path v0.3.0 h1:tkjga3MtpXyM5v+3EbRvOHEoo+frwi4oumw5K+KYWyA= -github.com/ipfs/go-path v0.3.0/go.mod h1:NOScsVgxfC/eIw4nz6OiGwK42PjaSJ4Y/ZFPn1Xe07I= +github.com/ipfs/go-path v0.3.1 h1:wkeaCWE/NTuuPGlEkLTsED5UkzfKYZpxaFFPgk8ZVLE= +github.com/ipfs/go-path v0.3.1/go.mod h1:eNLsxJEEMxn/CDzUJ6wuNl+6No6tEUhOZcPKsZsYX0E= github.com/ipfs/go-peertaskqueue v0.1.0/go.mod h1:Jmk3IyCcfl1W3jTW3YpghSwSEC6IJ3Vzz/jUmWw8Z0U= github.com/ipfs/go-peertaskqueue v0.7.0/go.mod h1:M/akTIE/z1jGNXMU7kFB4TeSEFvj68ow0Rrb04donIU= github.com/ipfs/go-peertaskqueue v0.8.1 h1:YhxAs1+wxb5jk7RvS0LHdyiILpNmRIRnZVztekOF0pg= 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.2.4/go.mod h1:SUdisfUjNoSDzzhGVxvCL9QO/nKdwXdr+gbMUdqcbYw= github.com/ipfs/go-unixfs v0.4.3 h1:EdDc1sNZNFDUlo4UrVAvvAofVI5EwTnKu8Nv8mgXkWQ= github.com/ipfs/go-unixfs v0.4.3/go.mod h1:TSG7G1UuT+l4pNj91raXAPkX0BhJi3jST1FDTfQ5QyM= -github.com/ipfs/go-unixfsnode v1.1.2/go.mod h1:5dcE2x03pyjHk4JjamXmunTMzz+VUtqvPwZjIEkfV6s= github.com/ipfs/go-unixfsnode v1.5.2 h1:CvsiTt58W2uR5dD8bqQv+aAY0c1qolmXmSyNbPHYiew= github.com/ipfs/go-unixfsnode v1.5.2/go.mod h1:NlOebRwYx8lMCNMdhAhEspYPBD3obp7TE0LvBqHY+ks= github.com/ipfs/go-verifcid v0.0.1/go.mod h1:5Hrva5KBeIog4A+UpqlaIU+DEstipcJYQQZc0g37pY0= From e2ff9027f95bf2d63705e895073e4fb7856a0bc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 23 Mar 2023 12:33:00 +0100 Subject: [PATCH 079/243] shed chainwatch: Appease the linter --- cmd/lotus-shed/chainwatch.go | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/cmd/lotus-shed/chainwatch.go b/cmd/lotus-shed/chainwatch.go index 9f6897027af..a7435a63fa6 100644 --- a/cmd/lotus-shed/chainwatch.go +++ b/cmd/lotus-shed/chainwatch.go @@ -46,7 +46,13 @@ var chainwatchDotCmd = &cli.Command{ } minH, err := strconv.ParseInt(cctx.Args().Get(0), 10, 32) + if err != nil { + return err + } tosee, err := strconv.ParseInt(cctx.Args().Get(1), 10, 32) + if err != nil { + return err + } maxH := minH + tosee res, err := st.db.Query(`select block, parent, b.miner, b.height, p.height from block_parents @@ -127,7 +133,7 @@ var chainwatchRunCmd = &cli.Command{ if err != nil { return err } - defer st.close() + defer st.close() // nolint:errcheck cwRunSyncer(ctx, api, st) go cwSubBlocks(ctx, api, st) @@ -327,7 +333,7 @@ func (st *cwStorage) storeHeaders(bhs map[cid.Cid]*types.BlockHeader, sync bool) if err != nil { return err } - defer stmt.Close() + defer stmt.Close() // nolint:errcheck for _, bh := range bhs { if _, err := stmt.Exec(bh.Cid().String(), bh.ParentWeight.String(), @@ -345,7 +351,7 @@ func (st *cwStorage) storeHeaders(bhs map[cid.Cid]*types.BlockHeader, sync bool) if err != nil { return err } - defer stmt2.Close() + defer stmt2.Close() // nolint:errcheck for _, bh := range bhs { for _, parent := range bh.Parents { if _, err := stmt2.Exec(bh.Cid().String(), parent.String()); err != nil { @@ -359,7 +365,7 @@ func (st *cwStorage) storeHeaders(bhs map[cid.Cid]*types.BlockHeader, sync bool) if err != nil { return err } - defer stmt.Close() + defer stmt.Close() // nolint:errcheck now := time.Now().Unix() for _, bh := range bhs { From 57d4c98d007b761999ab7260e850e9fd96ded131 Mon Sep 17 00:00:00 2001 From: Aayush Date: Thu, 23 Mar 2023 09:50:28 -0400 Subject: [PATCH 080/243] fix: gas estimation: don't special case paych collects --- node/impl/full/gas.go | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/node/impl/full/gas.go b/node/impl/full/gas.go index 24e1b3fbca6..5ed51248af8 100644 --- a/node/impl/full/gas.go +++ b/node/impl/full/gas.go @@ -372,18 +372,6 @@ func gasEstimateGasLimit( } ret = (ret * int64(transitionalMulti*1024)) >> 10 - // Special case for PaymentChannel collect, which is deleting actor - // We ignore errors in this special case since they CAN occur, - // and we just want to detect existing payment channel actors - st, err := smgr.ParentState(ts) - if err == nil { - act, err := st.GetActor(msg.To) - if err == nil && lbuiltin.IsPaymentChannelActor(act.Code) && msgIn.Method == builtin.MethodsPaych.Collect { - // add the refunded gas for DestroyActor back into the gas used - ret += 76e3 - } - } - return ret, nil } From 6550abdfccefad062a3076bed4e5c1f5e6cd2ce1 Mon Sep 17 00:00:00 2001 From: vyzo Date: Thu, 23 Mar 2023 16:53:50 +0200 Subject: [PATCH 081/243] introduce execution lanes --- chain/vm/execution.go | 173 ++++++++++++++++++++++++++++++++++++++++++ chain/vm/vm.go | 2 + chain/vm/vmi.go | 41 +++++++++- 3 files changed, 215 insertions(+), 1 deletion(-) create mode 100644 chain/vm/execution.go diff --git a/chain/vm/execution.go b/chain/vm/execution.go new file mode 100644 index 00000000000..e55883dae41 --- /dev/null +++ b/chain/vm/execution.go @@ -0,0 +1,173 @@ +package vm + +import ( + "context" + "errors" + "os" + "strconv" + "sync" + + "github.com/ipfs/go-cid" + + "github.com/filecoin-project/lotus/chain/types" +) + +const ( + DefaultAvailableExecutionLanes = 4 + DefaultPriorityExecutionLanes = 2 +) + +var ErrExecutorDone = errors.New("executor has been released") + +// the execution environment; see below for definition, methods, and initilization +var execution *executionEnv + +// implementation of vm executor with simple sanity check preventing use after free. +type vmExecutor struct { + lk sync.RWMutex + vmi Interface + token *executionToken + done bool +} + +var _ Executor = (*vmExecutor)(nil) + +func newVMExecutor(vmi Interface, token *executionToken) Executor { + return &vmExecutor{vmi: vmi, token: token} +} + +func (e *vmExecutor) ApplyMessage(ctx context.Context, cmsg types.ChainMsg) (*ApplyRet, error) { + e.lk.RLock() + defer e.lk.RUnlock() + + if e.done { + return nil, ErrExecutorDone + } + + return e.vmi.ApplyMessage(ctx, cmsg) +} + +func (e *vmExecutor) ApplyImplicitMessage(ctx context.Context, msg *types.Message) (*ApplyRet, error) { + e.lk.RLock() + defer e.lk.RUnlock() + + if e.done { + return nil, ErrExecutorDone + } + + return e.vmi.ApplyImplicitMessage(ctx, msg) +} + +func (e *vmExecutor) Flush(ctx context.Context) (cid.Cid, error) { + e.lk.RLock() + defer e.lk.RUnlock() + + if e.done { + return cid.Undef, ErrExecutorDone + } + + return e.vmi.Flush(ctx) +} + +func (e *vmExecutor) Done() { + e.lk.Lock() + defer e.lk.Unlock() + + e.token.Done() + e.token = nil + e.done = true +} + +type executionToken struct { + reserved int +} + +func (token *executionToken) Done() { + execution.putToken(token) +} + +type executionEnv struct { + mx *sync.Mutex + cond *sync.Cond + + // available executors + available int + // reserved executors + reserved int +} + +func (e *executionEnv) getToken(lane ExecutionLane) *executionToken { + e.mx.Lock() + defer e.mx.Unlock() + + switch lane { + case ExecutionLaneDefault: + for e.available <= e.reserved { + e.cond.Wait() + } + + e.available-- + return &executionToken{reserved: 0} + + case ExecutionLanePriority: + for e.available == 0 { + e.cond.Wait() + } + + e.available-- + + reserving := 0 + if e.reserved > 0 { + e.reserved-- + reserving = 1 + } + return &executionToken{reserved: reserving} + + default: + // already checked at interface boundary in NewVM, so this is appropriate + panic("bogus execution lane") + } +} + +func (e *executionEnv) putToken(token *executionToken) { + e.mx.Lock() + defer e.mx.Unlock() + + e.available++ + e.reserved += token.reserved + + e.cond.Broadcast() +} + +func init() { + var available, priority int + var err error + + concurrency := os.Getenv("LOTUS_FVM_CONCURRENCY") + if concurrency == "" { + available = DefaultAvailableExecutionLanes + } + available, err = strconv.Atoi(concurrency) + if err != nil { + panic(err) + } + + reserved := os.Getenv("LOTUS_FVM_CONCURRENCY_RESERVED") + if reserved == "" { + priority = DefaultPriorityExecutionLanes + } + priority, err = strconv.Atoi(reserved) + if err != nil { + panic(err) + } + + mx := &sync.Mutex{} + cond := sync.NewCond(mx) + + execution = &executionEnv{ + mx: mx, + cond: cond, + available: available, + reserved: priority, + } +} diff --git a/chain/vm/vm.go b/chain/vm/vm.go index c8e3f251907..6fbe7933b68 100644 --- a/chain/vm/vm.go +++ b/chain/vm/vm.go @@ -250,6 +250,8 @@ type VMOpts struct { Tracing bool // ReturnEvents decodes and returns emitted events. ReturnEvents bool + // ExecutionLane specifies the execution priority of the created vm + ExecutionLane ExecutionLane } func NewLegacyVM(ctx context.Context, opts *VMOpts) (*LegacyVM, error) { diff --git a/chain/vm/vmi.go b/chain/vm/vmi.go index 01b32d4ad17..e5d5daff8a6 100644 --- a/chain/vm/vmi.go +++ b/chain/vm/vmi.go @@ -2,6 +2,7 @@ package vm import ( "context" + "fmt" "os" cid "github.com/ipfs/go-cid" @@ -17,6 +18,15 @@ var ( StatApplied uint64 ) +type ExecutionLane int + +const ( + // ExecutionLaneDefault signifies a default, non prioritized execution lane. + ExecutionLaneDefault ExecutionLane = iota + // ExecutionLanePriority signifies a prioritized execution lane with reserved resources. + ExecutionLanePriority +) + type Interface interface { // Applies the given message onto the VM's current state, returning the result of the execution ApplyMessage(ctx context.Context, cmsg types.ChainMsg) (*ApplyRet, error) @@ -27,13 +37,24 @@ type Interface interface { Flush(ctx context.Context) (cid.Cid, error) } +// Executor is the general vm execution interface, which is prioritized according to execution langes. +// User must call Done when it is done with this executor to release resource holds by the execution +// environment +type Executor interface { + Interface + + // Done must be called when done with the executor to release resource holds. + // It is an error to invoke Interface methods after Done has been called. + Done() +} + // WARNING: You will not affect your node's execution by misusing this feature, but you will confuse yourself thoroughly! // An envvar that allows the user to specify debug actors bundles to be used by the FVM // alongside regular execution. This is basically only to be used to print out specific logging information. // Message failures, unexpected terminations,gas costs, etc. should all be ignored. var useFvmDebug = os.Getenv("LOTUS_FVM_DEVELOPER_DEBUG") == "1" -func NewVM(ctx context.Context, opts *VMOpts) (Interface, error) { +func newVM(ctx context.Context, opts *VMOpts) (Interface, error) { if opts.NetworkVersion >= network.Version16 { if useFvmDebug { return NewDualExecutionFVM(ctx, opts) @@ -43,3 +64,21 @@ func NewVM(ctx context.Context, opts *VMOpts) (Interface, error) { return NewLegacyVM(ctx, opts) } + +func NewVM(ctx context.Context, opts *VMOpts) (Executor, error) { + switch opts.ExecutionLane { + case ExecutionLaneDefault, ExecutionLanePriority: + default: + return nil, fmt.Errorf("invalid execution lane: %d", opts.ExecutionLane) + } + + token := execution.getToken(opts.ExecutionLane) + + vmi, err := newVM(ctx, opts) + if err != nil { + token.Done() + return nil, err + } + + return newVMExecutor(vmi, token), nil +} From 8bb5d985d4df4bc82f67d11aa9d4366530c54025 Mon Sep 17 00:00:00 2001 From: Aayush Date: Thu, 23 Mar 2023 10:54:31 -0400 Subject: [PATCH 082/243] docs: api: clarify MpoolClear params --- api/api_full.go | 6 ++++-- build/openrpc/full.json.gz | Bin 33808 -> 33856 bytes documentation/en/api-v1-unstable-methods.md | 4 +++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/api/api_full.go b/api/api_full.go index 6ce061865ef..9776a122c43 100644 --- a/api/api_full.go +++ b/api/api_full.go @@ -297,8 +297,10 @@ type FullNode interface { MpoolGetNonce(context.Context, address.Address) (uint64, error) //perm:read MpoolSub(context.Context) (<-chan MpoolUpdate, error) //perm:read - // MpoolClear clears pending messages from the mpool - MpoolClear(context.Context, bool) error //perm:write + // MpoolClear clears pending messages from the mpool. + // If clearLocal is true, ALL messages will be cleared. + // If clearLocal is false, local messages will be protected, all others will be cleared. + MpoolClear(ctx context.Context, clearLocal bool) error //perm:write // MpoolGetConfig returns (a copy of) the current mpool config MpoolGetConfig(context.Context) (*types.MpoolConfig, error) //perm:read diff --git a/build/openrpc/full.json.gz b/build/openrpc/full.json.gz index 18c9e20b2901bfb3dc763d751cec0f5d87328d8f..c5d21c6da07f5c98dfb1a47cdba20964b2955cb3 100644 GIT binary patch delta 33118 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 W&p&_s^Zx??0RR8PF6Sc@$_4=9L3E@5 diff --git a/documentation/en/api-v1-unstable-methods.md b/documentation/en/api-v1-unstable-methods.md index 8d95c7e0cd2..8cb96ca6d02 100644 --- a/documentation/en/api-v1-unstable-methods.md +++ b/documentation/en/api-v1-unstable-methods.md @@ -3941,7 +3941,9 @@ Response: ``` ### MpoolClear -MpoolClear clears pending messages from the mpool +MpoolClear clears pending messages from the mpool. +If clearLocal is true, ALL messages will be cleared. +If clearLocal is false, local messages will be protected, all others will be cleared. Perms: write From 7362556c02877b3c8d4f8069a10b303f287c0552 Mon Sep 17 00:00:00 2001 From: vyzo Date: Thu, 23 Mar 2023 17:17:46 +0200 Subject: [PATCH 083/243] update VM interface references to use the executor, and call Done where appropriate --- chain/consensus/compute_state.go | 10 ++++++++-- chain/gen/genesis/genesis.go | 1 + chain/gen/genesis/miners.go | 6 +++++- chain/stmgr/forks_test.go | 30 ++++++++++++++++++------------ chain/stmgr/stmgr.go | 8 ++++---- conformance/driver.go | 2 +- 6 files changed, 37 insertions(+), 20 deletions(-) diff --git a/chain/consensus/compute_state.go b/chain/consensus/compute_state.go index e627a62d2ac..cf05d612d26 100644 --- a/chain/consensus/compute_state.go +++ b/chain/consensus/compute_state.go @@ -93,7 +93,7 @@ func (t *TipSetExecutor) ApplyBlocks(ctx context.Context, }() ctx = blockstore.WithHotView(ctx) - makeVm := func(base cid.Cid, e abi.ChainEpoch, timestamp uint64) (vm.Interface, error) { + makeVm := func(base cid.Cid, e abi.ChainEpoch, timestamp uint64) (vm.Executor, error) { vmopt := &vm.VMOpts{ StateBase: base, Epoch: e, @@ -109,6 +109,7 @@ func (t *TipSetExecutor) ApplyBlocks(ctx context.Context, TipSetGetter: stmgr.TipSetGetterForTipset(sm.ChainStore(), ts), Tracing: vmTracing, ReturnEvents: sm.ChainStore().IsStoringEvents(), + ExecutionLane: vm.ExecutionLanePriority, } return sm.VMConstructor()(ctx, vmopt) @@ -116,7 +117,7 @@ func (t *TipSetExecutor) ApplyBlocks(ctx context.Context, var cronGas int64 - runCron := func(vmCron vm.Interface, epoch abi.ChainEpoch) error { + runCron := func(vmCron vm.Executor, epoch abi.ChainEpoch) error { cronMsg := &types.Message{ To: cron.Address, From: builtin.SystemActorAddr, @@ -169,13 +170,17 @@ func (t *TipSetExecutor) ApplyBlocks(ctx context.Context, // run cron for null rounds if any if err = runCron(vmCron, i); err != nil { + vmCron.Done() return cid.Undef, cid.Undef, xerrors.Errorf("running cron: %w", err) } pstate, err = vmCron.Flush(ctx) if err != nil { + vmCron.Done() return cid.Undef, cid.Undef, xerrors.Errorf("flushing cron vm: %w", err) } + + vmCron.Done() } // handle state forks @@ -195,6 +200,7 @@ func (t *TipSetExecutor) ApplyBlocks(ctx context.Context, if err != nil { return cid.Undef, cid.Undef, xerrors.Errorf("making vm: %w", err) } + defer vmi.Done() var ( receipts []*types.MessageReceipt diff --git a/chain/gen/genesis/genesis.go b/chain/gen/genesis/genesis.go index 3e88480218e..3ef8de968ec 100644 --- a/chain/gen/genesis/genesis.go +++ b/chain/gen/genesis/genesis.go @@ -496,6 +496,7 @@ func VerifyPreSealedData(ctx context.Context, cs *store.ChainStore, sys vm.Sysca if err != nil { return cid.Undef, xerrors.Errorf("failed to create VM: %w", err) } + defer vm.Done() for mi, m := range template.Miners { for si, s := range m.Sectors { diff --git a/chain/gen/genesis/miners.go b/chain/gen/genesis/miners.go index 5f741fd7c58..6e5be0b0adf 100644 --- a/chain/gen/genesis/miners.go +++ b/chain/gen/genesis/miners.go @@ -88,7 +88,7 @@ func SetupStorageMiners(ctx context.Context, cs *store.ChainStore, sys vm.Syscal return big.Zero(), nil } - newVM := func(base cid.Cid) (vm.Interface, error) { + newVM := func(base cid.Cid) (vm.Executor, error) { vmopt := &vm.VMOpts{ StateBase: base, Epoch: 0, @@ -108,6 +108,7 @@ func SetupStorageMiners(ctx context.Context, cs *store.ChainStore, sys vm.Syscal if err != nil { return cid.Undef, fmt.Errorf("creating vm: %w", err) } + defer genesisVm.Done() if len(miners) == 0 { return cid.Undef, xerrors.New("no genesis miners") @@ -338,6 +339,7 @@ func SetupStorageMiners(ctx context.Context, cs *store.ChainStore, sys vm.Syscal return cid.Undef, xerrors.Errorf("flushing state tree: %w", err) } + genesisVm.Done() genesisVm, err = newVM(nh) if err != nil { return cid.Undef, fmt.Errorf("creating new vm: %w", err) @@ -410,6 +412,7 @@ func SetupStorageMiners(ctx context.Context, cs *store.ChainStore, sys vm.Syscal return cid.Undef, xerrors.Errorf("flushing state tree: %w", err) } + genesisVm.Done() genesisVm, err = newVM(nh) if err != nil { return cid.Undef, fmt.Errorf("creating new vm: %w", err) @@ -517,6 +520,7 @@ func SetupStorageMiners(ctx context.Context, cs *store.ChainStore, sys vm.Syscal return cid.Undef, xerrors.Errorf("flushing state tree: %w", err) } + genesisVm.Done() genesisVm, err = newVM(nh) if err != nil { return cid.Undef, fmt.Errorf("creating new vm: %w", err) diff --git a/chain/stmgr/forks_test.go b/chain/stmgr/forks_test.go index f91d8997d6c..d852e2fdd2a 100644 --- a/chain/stmgr/forks_test.go +++ b/chain/stmgr/forks_test.go @@ -56,6 +56,12 @@ const testForkHeight = 40 type testActor struct { } +type mockExecutor struct { + vm.Interface +} + +func (*mockExecutor) Done() {} + // must use existing actor that an account is allowed to exec. func (testActor) Code() cid.Cid { return builtin0.PaymentChannelActorCodeID } func (testActor) State() cbor.Er { return new(testActorState) } @@ -178,13 +184,13 @@ func TestForkHeightTriggers(t *testing.T) { registry := builtin.MakeRegistryLegacy([]rtt.VMActor{testActor{}}) inv.Register(actorstypes.Version0, nil, registry) - sm.SetVMConstructor(func(ctx context.Context, vmopt *vm.VMOpts) (vm.Interface, error) { + sm.SetVMConstructor(func(ctx context.Context, vmopt *vm.VMOpts) (vm.Executor, error) { nvm, err := vm.NewLegacyVM(ctx, vmopt) if err != nil { return nil, err } nvm.SetInvoker(inv) - return nvm, nil + return &mockExecutor{nvm}, nil }) cg.SetStateManager(sm) @@ -296,13 +302,13 @@ func testForkRefuseCall(t *testing.T, nullsBefore, nullsAfter int) { registry := builtin.MakeRegistryLegacy([]rtt.VMActor{testActor{}}) inv.Register(actorstypes.Version0, nil, registry) - sm.SetVMConstructor(func(ctx context.Context, vmopt *vm.VMOpts) (vm.Interface, error) { + sm.SetVMConstructor(func(ctx context.Context, vmopt *vm.VMOpts) (vm.Executor, error) { nvm, err := vm.NewLegacyVM(ctx, vmopt) if err != nil { return nil, err } nvm.SetInvoker(inv) - return nvm, nil + return &mockExecutor{nvm}, nil }) cg.SetStateManager(sm) @@ -518,13 +524,13 @@ func TestForkPreMigration(t *testing.T) { registry := builtin.MakeRegistryLegacy([]rtt.VMActor{testActor{}}) inv.Register(actorstypes.Version0, nil, registry) - sm.SetVMConstructor(func(ctx context.Context, vmopt *vm.VMOpts) (vm.Interface, error) { + sm.SetVMConstructor(func(ctx context.Context, vmopt *vm.VMOpts) (vm.Executor, error) { nvm, err := vm.NewLegacyVM(ctx, vmopt) if err != nil { return nil, err } nvm.SetInvoker(inv) - return nvm, nil + return &mockExecutor{nvm}, nil }) cg.SetStateManager(sm) @@ -592,11 +598,11 @@ func TestDisablePreMigration(t *testing.T) { registry := builtin.MakeRegistryLegacy([]rtt.VMActor{testActor{}}) inv.Register(actorstypes.Version0, nil, registry) - sm.SetVMConstructor(func(ctx context.Context, vmopt *vm.VMOpts) (vm.Interface, error) { + sm.SetVMConstructor(func(ctx context.Context, vmopt *vm.VMOpts) (vm.Executor, error) { nvm, err := vm.NewLegacyVM(ctx, vmopt) require.NoError(t, err) nvm.SetInvoker(inv) - return nvm, nil + return &mockExecutor{nvm}, nil }) cg.SetStateManager(sm) @@ -647,11 +653,11 @@ func TestMigrtionCache(t *testing.T) { registry := builtin.MakeRegistryLegacy([]rtt.VMActor{testActor{}}) inv.Register(actorstypes.Version0, nil, registry) - sm.SetVMConstructor(func(ctx context.Context, vmopt *vm.VMOpts) (vm.Interface, error) { + sm.SetVMConstructor(func(ctx context.Context, vmopt *vm.VMOpts) (vm.Executor, error) { nvm, err := vm.NewLegacyVM(ctx, vmopt) require.NoError(t, err) nvm.SetInvoker(inv) - return nvm, nil + return &mockExecutor{nvm}, nil }) cg.SetStateManager(sm) @@ -691,11 +697,11 @@ func TestMigrtionCache(t *testing.T) { index.DummyMsgIndex, ) require.NoError(t, err) - sm.SetVMConstructor(func(ctx context.Context, vmopt *vm.VMOpts) (vm.Interface, error) { + sm.SetVMConstructor(func(ctx context.Context, vmopt *vm.VMOpts) (vm.Executor, error) { nvm, err := vm.NewLegacyVM(ctx, vmopt) require.NoError(t, err) nvm.SetInvoker(inv) - return nvm, nil + return &mockExecutor{nvm}, nil }) ctx := context.Background() diff --git a/chain/stmgr/stmgr.go b/chain/stmgr/stmgr.go index 827aeeee571..5f201cf3230 100644 --- a/chain/stmgr/stmgr.go +++ b/chain/stmgr/stmgr.go @@ -125,7 +125,7 @@ type StateManager struct { compWait map[string]chan struct{} stlk sync.Mutex genesisMsigLk sync.Mutex - newVM func(context.Context, *vm.VMOpts) (vm.Interface, error) + newVM func(context.Context, *vm.VMOpts) (vm.Executor, error) Syscalls vm.SyscallBuilder preIgnitionVesting []msig0.State postIgnitionVesting []msig0.State @@ -439,12 +439,12 @@ func (sm *StateManager) ValidateChain(ctx context.Context, ts *types.TipSet) err return nil } -func (sm *StateManager) SetVMConstructor(nvm func(context.Context, *vm.VMOpts) (vm.Interface, error)) { +func (sm *StateManager) SetVMConstructor(nvm func(context.Context, *vm.VMOpts) (vm.Executor, error)) { sm.newVM = nvm } -func (sm *StateManager) VMConstructor() func(context.Context, *vm.VMOpts) (vm.Interface, error) { - return func(ctx context.Context, opts *vm.VMOpts) (vm.Interface, error) { +func (sm *StateManager) VMConstructor() func(context.Context, *vm.VMOpts) (vm.Executor, error) { + return func(ctx context.Context, opts *vm.VMOpts) (vm.Executor, error) { return sm.newVM(ctx, opts) } } diff --git a/conformance/driver.go b/conformance/driver.go index e0d56d07410..c3041be7136 100644 --- a/conformance/driver.go +++ b/conformance/driver.go @@ -158,7 +158,7 @@ func (d *Driver) ExecuteTipset(bs blockstore.Blockstore, ds ds.Batching, params results: []*vm.ApplyRet{}, } - sm.SetVMConstructor(func(ctx context.Context, vmopt *vm.VMOpts) (vm.Interface, error) { + sm.SetVMConstructor(func(ctx context.Context, vmopt *vm.VMOpts) (vm.Executor, error) { vmopt.CircSupplyCalc = func(context.Context, abi.ChainEpoch, *state.StateTree) (abi.TokenAmount, error) { return big.Zero(), nil } From ee6c0f857068587646d9e98847aa2303f8d77913 Mon Sep 17 00:00:00 2001 From: vyzo Date: Thu, 23 Mar 2023 17:28:08 +0200 Subject: [PATCH 084/243] only call Atoi on non empty strings --- chain/vm/execution.go | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/chain/vm/execution.go b/chain/vm/execution.go index e55883dae41..8db0e43132e 100644 --- a/chain/vm/execution.go +++ b/chain/vm/execution.go @@ -146,19 +146,21 @@ func init() { concurrency := os.Getenv("LOTUS_FVM_CONCURRENCY") if concurrency == "" { available = DefaultAvailableExecutionLanes - } - available, err = strconv.Atoi(concurrency) - if err != nil { - panic(err) + } else { + available, err = strconv.Atoi(concurrency) + if err != nil { + panic(err) + } } reserved := os.Getenv("LOTUS_FVM_CONCURRENCY_RESERVED") if reserved == "" { priority = DefaultPriorityExecutionLanes - } - priority, err = strconv.Atoi(reserved) - if err != nil { - panic(err) + } else { + priority, err = strconv.Atoi(reserved) + if err != nil { + panic(err) + } } mx := &sync.Mutex{} From 2bb89d9c30403fc7609e84af0fea5bc769db63ac Mon Sep 17 00:00:00 2001 From: vyzo Date: Thu, 23 Mar 2023 17:34:59 +0200 Subject: [PATCH 085/243] call Executor.Done where appropriate in stmgr uses --- chain/stmgr/call.go | 3 +++ chain/stmgr/utils.go | 1 + 2 files changed, 4 insertions(+) diff --git a/chain/stmgr/call.go b/chain/stmgr/call.go index 901fc2d1253..aa09fbfd37a 100644 --- a/chain/stmgr/call.go +++ b/chain/stmgr/call.go @@ -159,6 +159,8 @@ func (sm *StateManager) callInternal(ctx context.Context, msg *types.Message, pr if err != nil { return nil, xerrors.Errorf("failed to set up vm: %w", err) } + defer vmi.Done() + for i, m := range priorMsgs { _, err = vmi.ApplyMessage(ctx, m) if err != nil { @@ -191,6 +193,7 @@ func (sm *StateManager) callInternal(ctx context.Context, msg *types.Message, pr vmopt.BaseFee = big.Zero() vmopt.StateBase = stateCid + vmi.Done() vmi, err = sm.newVM(ctx, vmopt) if err != nil { return nil, xerrors.Errorf("failed to set up estimation vm: %w", err) diff --git a/chain/stmgr/utils.go b/chain/stmgr/utils.go index c93267d50f8..78129cb164d 100644 --- a/chain/stmgr/utils.go +++ b/chain/stmgr/utils.go @@ -106,6 +106,7 @@ func ComputeState(ctx context.Context, sm *StateManager, height abi.ChainEpoch, if err != nil { return cid.Undef, nil, err } + defer vmi.Done() for i, msg := range msgs { // TODO: Use the signed message length for secp messages From 2a0660447a674111c5a72a849dcc1d709c041b89 Mon Sep 17 00:00:00 2001 From: vyzo Date: Thu, 23 Mar 2023 17:38:36 +0200 Subject: [PATCH 086/243] make token.Done idempotent --- chain/vm/execution.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/chain/vm/execution.go b/chain/vm/execution.go index 8db0e43132e..66f82280b24 100644 --- a/chain/vm/execution.go +++ b/chain/vm/execution.go @@ -73,9 +73,11 @@ func (e *vmExecutor) Done() { e.lk.Lock() defer e.lk.Unlock() - e.token.Done() - e.token = nil - e.done = true + if !e.done { + e.token.Done() + e.token = nil + e.done = true + } } type executionToken struct { From b1669235f7645d20b5e93124230c7149035e2eb5 Mon Sep 17 00:00:00 2001 From: Aayush Date: Thu, 23 Mar 2023 11:57:26 -0400 Subject: [PATCH 087/243] feat: chainstore: optimize BlockMsgsForTipset --- chain/store/messages.go | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/chain/store/messages.go b/chain/store/messages.go index c39cb3f9b9d..ee37b4855ea 100644 --- a/chain/store/messages.go +++ b/chain/store/messages.go @@ -114,12 +114,35 @@ func (cs *ChainStore) BlockMsgsForTipset(ctx context.Context, ts *types.TipSet) return nil, xerrors.Errorf("failed to load state tree at tipset %s: %w", ts, err) } + useIds := false selectMsg := func(m *types.Message) (bool, error) { var sender address.Address if ts.Height() >= build.UpgradeHyperdriveHeight { - sender, err = st.LookupID(m.From) - if err != nil { - return false, err + if useIds { + sender, err = st.LookupID(m.From) + if err != nil { + return false, xerrors.Errorf("failed to resolve sender: %w", err) + } + } else { + if m.From.Protocol() != address.ID { + // we haven't been told to use IDs, just use the robust addr + sender = m.From + } else { + // uh-oh, we actually have an ID-sender! + useIds = true + for robust, nonce := range applied { + resolved, err := st.LookupID(robust) + if err != nil { + return false, xerrors.Errorf("failed to resolve sender: %w", err) + } + applied[resolved] = nonce + } + + sender, err = st.LookupID(m.From) + if err != nil { + return false, xerrors.Errorf("failed to resolve sender: %w", err) + } + } } } else { sender = m.From From 317a87d6699c7b03467e683cf2c0804b5289de99 Mon Sep 17 00:00:00 2001 From: vyzo Date: Thu, 23 Mar 2023 18:52:37 +0200 Subject: [PATCH 088/243] add some sanity checks for execution concurrency parameters --- chain/vm/execution.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/chain/vm/execution.go b/chain/vm/execution.go index 66f82280b24..b2573dffc61 100644 --- a/chain/vm/execution.go +++ b/chain/vm/execution.go @@ -165,6 +165,15 @@ func init() { } } + // some sanity checks + if available < 2 { + panic("insufficient execution concurrency") + } + + if priority > available-1 { + panic("insufficient default execution concurrency") + } + mx := &sync.Mutex{} cond := sync.NewCond(mx) From f11a7f8940071cb369c234c31f04e93a1acf4a5a Mon Sep 17 00:00:00 2001 From: vyzo Date: Thu, 23 Mar 2023 19:51:23 +0200 Subject: [PATCH 089/243] fix incorrect deferred vm release --- chain/gen/genesis/miners.go | 2 +- chain/stmgr/call.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/chain/gen/genesis/miners.go b/chain/gen/genesis/miners.go index 6e5be0b0adf..900389f565d 100644 --- a/chain/gen/genesis/miners.go +++ b/chain/gen/genesis/miners.go @@ -108,7 +108,7 @@ func SetupStorageMiners(ctx context.Context, cs *store.ChainStore, sys vm.Syscal if err != nil { return cid.Undef, fmt.Errorf("creating vm: %w", err) } - defer genesisVm.Done() + defer func() { genesisVm.Done() }() if len(miners) == 0 { return cid.Undef, xerrors.New("no genesis miners") diff --git a/chain/stmgr/call.go b/chain/stmgr/call.go index aa09fbfd37a..9633d6417d7 100644 --- a/chain/stmgr/call.go +++ b/chain/stmgr/call.go @@ -159,7 +159,7 @@ func (sm *StateManager) callInternal(ctx context.Context, msg *types.Message, pr if err != nil { return nil, xerrors.Errorf("failed to set up vm: %w", err) } - defer vmi.Done() + defer func() { vmi.Done() }() for i, m := range priorMsgs { _, err = vmi.ApplyMessage(ctx, m) From dcdd1bc214bb44ac0fe312b92d782226569863f6 Mon Sep 17 00:00:00 2001 From: Aayush Date: Thu, 23 Mar 2023 15:56:59 -0400 Subject: [PATCH 090/243] feat: supply: drop genesis market locked funds --- chain/stmgr/stmgr.go | 3 +-- chain/stmgr/supply.go | 10 +--------- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/chain/stmgr/stmgr.go b/chain/stmgr/stmgr.go index 827aeeee571..2d528c91be3 100644 --- a/chain/stmgr/stmgr.go +++ b/chain/stmgr/stmgr.go @@ -131,8 +131,7 @@ type StateManager struct { postIgnitionVesting []msig0.State postCalicoVesting []msig0.State - genesisPledge abi.TokenAmount - genesisMarketFunds abi.TokenAmount + genesisPledge abi.TokenAmount tsExec Executor tsExecMonitor ExecMonitor diff --git a/chain/stmgr/supply.go b/chain/stmgr/supply.go index a48ff36c7f9..b56792e11dc 100644 --- a/chain/stmgr/supply.go +++ b/chain/stmgr/supply.go @@ -51,17 +51,11 @@ func (sm *StateManager) setupGenesisVestingSchedule(ctx context.Context) error { return xerrors.Errorf("loading state tree: %w", err) } - gmf, err := getFilMarketLocked(ctx, sTree) - if err != nil { - return xerrors.Errorf("setting up genesis market funds: %w", err) - } - gp, err := getFilPowerLocked(ctx, sTree) if err != nil { return xerrors.Errorf("setting up genesis pledge: %w", err) } - sm.genesisMarketFunds = gmf sm.genesisPledge = gp totalsByEpoch := make(map[abi.ChainEpoch]abi.TokenAmount) @@ -202,7 +196,7 @@ func (sm *StateManager) GetFilVested(ctx context.Context, height abi.ChainEpoch) defer sm.genesisMsigLk.Unlock() // TODO: combine all this? - if sm.preIgnitionVesting == nil || sm.genesisPledge.IsZero() || sm.genesisMarketFunds.IsZero() { + if sm.preIgnitionVesting == nil || sm.genesisPledge.IsZero() { err := sm.setupGenesisVestingSchedule(ctx) if err != nil { return vf, xerrors.Errorf("failed to setup pre-ignition vesting schedule: %w", err) @@ -246,8 +240,6 @@ func (sm *StateManager) GetFilVested(ctx context.Context, height abi.ChainEpoch) if height <= build.UpgradeAssemblyHeight { // continue to use preIgnitionGenInfos, nothing changed at the Ignition epoch vf = big.Add(vf, sm.genesisPledge) - // continue to use preIgnitionGenInfos, nothing changed at the Ignition epoch - vf = big.Add(vf, sm.genesisMarketFunds) } return vf, nil From bc87017ea50349b75cd2b5c3cbaaf7df9d9e280e Mon Sep 17 00:00:00 2001 From: Aayush Date: Thu, 23 Mar 2023 16:00:40 -0400 Subject: [PATCH 091/243] feat: supply: only grab genesis msig locks for writes --- chain/stmgr/supply.go | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/chain/stmgr/supply.go b/chain/stmgr/supply.go index b56792e11dc..b48f9af43da 100644 --- a/chain/stmgr/supply.go +++ b/chain/stmgr/supply.go @@ -56,6 +56,8 @@ func (sm *StateManager) setupGenesisVestingSchedule(ctx context.Context) error { return xerrors.Errorf("setting up genesis pledge: %w", err) } + sm.genesisMsigLk.Lock() + defer sm.genesisMsigLk.Unlock() sm.genesisPledge = gp totalsByEpoch := make(map[abi.ChainEpoch]abi.TokenAmount) @@ -122,6 +124,8 @@ func (sm *StateManager) setupPostIgnitionVesting(ctx context.Context) error { totalsByEpoch[sixYears] = big.NewInt(100_000_000) totalsByEpoch[sixYears] = big.Add(totalsByEpoch[sixYears], big.NewInt(300_000_000)) + sm.genesisMsigLk.Lock() + defer sm.genesisMsigLk.Unlock() sm.postIgnitionVesting = make([]msig0.State, 0, len(totalsByEpoch)) for k, v := range totalsByEpoch { ns := msig0.State{ @@ -172,6 +176,9 @@ func (sm *StateManager) setupPostCalicoVesting(ctx context.Context) error { totalsByEpoch[sixYears] = big.Add(totalsByEpoch[sixYears], big.NewInt(300_000_000)) totalsByEpoch[sixYears] = big.Add(totalsByEpoch[sixYears], big.NewInt(9_805_053)) + sm.genesisMsigLk.Lock() + defer sm.genesisMsigLk.Unlock() + sm.postCalicoVesting = make([]msig0.State, 0, len(totalsByEpoch)) for k, v := range totalsByEpoch { ns := msig0.State{ @@ -192,21 +199,20 @@ func (sm *StateManager) setupPostCalicoVesting(ctx context.Context) error { func (sm *StateManager) GetFilVested(ctx context.Context, height abi.ChainEpoch) (abi.TokenAmount, error) { vf := big.Zero() - sm.genesisMsigLk.Lock() - defer sm.genesisMsigLk.Unlock() - // TODO: combine all this? if sm.preIgnitionVesting == nil || sm.genesisPledge.IsZero() { err := sm.setupGenesisVestingSchedule(ctx) if err != nil { return vf, xerrors.Errorf("failed to setup pre-ignition vesting schedule: %w", err) } + } if sm.postIgnitionVesting == nil { err := sm.setupPostIgnitionVesting(ctx) if err != nil { return vf, xerrors.Errorf("failed to setup post-ignition vesting schedule: %w", err) } + } if sm.postCalicoVesting == nil { err := sm.setupPostCalicoVesting(ctx) From 2d9412d97fd1896d0dcd340222b53eb5ec34af9a Mon Sep 17 00:00:00 2001 From: Aayush Date: Wed, 22 Mar 2023 18:01:01 -0400 Subject: [PATCH 092/243] fix: miner: correctly count sector extensions --- cmd/lotus-miner/sectors.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmd/lotus-miner/sectors.go b/cmd/lotus-miner/sectors.go index 93c950d7c0d..cbf1c331843 100644 --- a/cmd/lotus-miner/sectors.go +++ b/cmd/lotus-miner/sectors.go @@ -1160,8 +1160,9 @@ var sectorsExtendCmd = &cli.Command{ } sectorsInDecl := int(sectorsWithoutClaimsCount) + len(sectorsWithClaims) + scount += sectorsInDecl - if scount+sectorsInDecl > addrSectors || len(p.Extensions) >= declMax { + if scount > addrSectors || len(p.Extensions) >= declMax { params = append(params, p) p = miner.ExtendSectorExpiration2Params{} scount = sectorsInDecl From 3070227b00a3f34799d773d44b4f1912f3f3909b Mon Sep 17 00:00:00 2001 From: Aayush Date: Thu, 23 Mar 2023 17:28:11 -0400 Subject: [PATCH 093/243] fix: miner: call ExtendSectorExpiration2 --- cmd/lotus-miner/sectors.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/lotus-miner/sectors.go b/cmd/lotus-miner/sectors.go index cbf1c331843..8d3a4c88470 100644 --- a/cmd/lotus-miner/sectors.go +++ b/cmd/lotus-miner/sectors.go @@ -1231,7 +1231,7 @@ var sectorsExtendCmd = &cli.Command{ smsg, err := fullApi.MpoolPushMessage(ctx, &types.Message{ From: mi.Worker, To: maddr, - Method: builtin.MethodsMiner.ExtendSectorExpiration, + Method: builtin.MethodsMiner.ExtendSectorExpiration2, Value: big.Zero(), Params: sp, }, spec) From 41fce94db48fbed79fe47549cc7b5a0c3c78c708 Mon Sep 17 00:00:00 2001 From: Mikers Date: Thu, 23 Mar 2023 12:27:01 -1000 Subject: [PATCH 094/243] perf: eth: gas estimate set applyTsMessages false (#10546) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * have gas estimate call callInternal with applyTsMessages = false and other calls with applyTsMessages=true for gas caclulation optimization * set applyTsMessages = true in CallWithGas call in shed * update test with new callwithgas api optimization for eth call * Update chain/stmgr/call.go Co-authored-by: Łukasz Magiera * env flag LOTUS_SKIP_APPLY_TS_MESSAGE_CALL_WITH_GAS must be 1 in order to have applyTsMessages change * env flag LOTUS_SKIP_APPLY_TS_MESSAGE_CALL_WITH_GAS must be 1 in order to have applyTsMessages change * make sure that even if we arent apply ts messages we grab ts messages from the particular user who is requesting gas estimation --------- Co-authored-by: Jiaying Wang <42981373+jennijuju@users.noreply.github.com> Co-authored-by: Łukasz Magiera Co-authored-by: Ubuntu --- chain/stmgr/call.go | 22 ++++++++++++++++------ chain/stmgr/forks_test.go | 4 ++-- cmd/lotus-shed/gas-estimation.go | 2 +- node/impl/full/eth.go | 15 +++++++++++++-- node/impl/full/gas.go | 8 +++++++- 5 files changed, 39 insertions(+), 12 deletions(-) diff --git a/chain/stmgr/call.go b/chain/stmgr/call.go index 901fc2d1253..61056528f11 100644 --- a/chain/stmgr/call.go +++ b/chain/stmgr/call.go @@ -52,8 +52,8 @@ func (sm *StateManager) Call(ctx context.Context, msg *types.Message, ts *types. } // CallWithGas calculates the state for a given tipset, and then applies the given message on top of that state. -func (sm *StateManager) CallWithGas(ctx context.Context, msg *types.Message, priorMsgs []types.ChainMsg, ts *types.TipSet) (*api.InvocResult, error) { - return sm.callInternal(ctx, msg, priorMsgs, ts, cid.Undef, sm.GetNetworkVersion, true, true) +func (sm *StateManager) CallWithGas(ctx context.Context, msg *types.Message, priorMsgs []types.ChainMsg, ts *types.TipSet, applyTsMessages bool) (*api.InvocResult, error) { + return sm.callInternal(ctx, msg, priorMsgs, ts, cid.Undef, sm.GetNetworkVersion, true, applyTsMessages) } // CallAtStateAndVersion allows you to specify a message to execute on the given stateCid and network version. @@ -117,12 +117,22 @@ func (sm *StateManager) callInternal(ctx context.Context, msg *types.Message, pr if stateCid == cid.Undef { stateCid = ts.ParentState() } + tsMsgs, err := sm.cs.MessagesForTipset(ctx, ts) + if err != nil { + return nil, xerrors.Errorf("failed to lookup messages for parent tipset: %w", err) + } + if applyTsMessages { - tsMsgs, err := sm.cs.MessagesForTipset(ctx, ts) - if err != nil { - return nil, xerrors.Errorf("failed to lookup messages for parent tipset: %w", err) - } priorMsgs = append(tsMsgs, priorMsgs...) + } else { + var filteredTsMsgs []types.ChainMsg + for _, tsMsg := range tsMsgs { + //TODO we should technically be normalizing the filecoin address of from when we compare here + if tsMsg.VMMessage().From == msg.VMMessage().From { + filteredTsMsgs = append(filteredTsMsgs, tsMsg) + } + } + priorMsgs = append(filteredTsMsgs, priorMsgs...) } // Technically, the tipset we're passing in here should be ts+1, but that may not exist. diff --git a/chain/stmgr/forks_test.go b/chain/stmgr/forks_test.go index f91d8997d6c..bf8793488b6 100644 --- a/chain/stmgr/forks_test.go +++ b/chain/stmgr/forks_test.go @@ -341,7 +341,7 @@ func testForkRefuseCall(t *testing.T, nullsBefore, nullsAfter int) { currentHeight := ts.TipSet.TipSet().Height() // CallWithGas calls on top of the given tipset. - ret, err := sm.CallWithGas(ctx, m, nil, ts.TipSet.TipSet()) + ret, err := sm.CallWithGas(ctx, m, nil, ts.TipSet.TipSet(), true) if parentHeight <= testForkHeight && currentHeight >= testForkHeight { // If I had a fork, or I _will_ have a fork, it should fail. require.Equal(t, ErrExpensiveFork, err) @@ -362,7 +362,7 @@ func testForkRefuseCall(t *testing.T, nullsBefore, nullsAfter int) { // Calls without a tipset should walk back to the last non-fork tipset. // We _verify_ that the migration wasn't run multiple times at the end of the // test. - ret, err = sm.CallWithGas(ctx, m, nil, nil) + ret, err = sm.CallWithGas(ctx, m, nil, nil, true) require.NoError(t, err) require.True(t, ret.MsgRct.ExitCode.IsSuccess()) diff --git a/cmd/lotus-shed/gas-estimation.go b/cmd/lotus-shed/gas-estimation.go index 7a5c35267ab..e02e2a722e1 100644 --- a/cmd/lotus-shed/gas-estimation.go +++ b/cmd/lotus-shed/gas-estimation.go @@ -241,7 +241,7 @@ var replayOfflineCmd = &cli.Command{ } tw := tabwriter.NewWriter(os.Stdout, 8, 2, 2, ' ', tabwriter.AlignRight) - res, err := sm.CallWithGas(ctx, msg, []types.ChainMsg{}, executionTs) + res, err := sm.CallWithGas(ctx, msg, []types.ChainMsg{}, executionTs, true) if err != nil { return err } diff --git a/node/impl/full/eth.go b/node/impl/full/eth.go index 14812e4deff..e2c39ed878f 100644 --- a/node/impl/full/eth.go +++ b/node/impl/full/eth.go @@ -6,6 +6,7 @@ import ( "encoding/json" "errors" "fmt" + "os" "sort" "strconv" "sync" @@ -888,9 +889,14 @@ func (a *EthModule) applyMessage(ctx context.Context, msg *types.Message, tsk ty return nil, xerrors.Errorf("cannot get tipset: %w", err) } + applyTsMessages := true + if os.Getenv("LOTUS_SKIP_APPLY_TS_MESSAGE_CALL_WITH_GAS") == "1" { + applyTsMessages = false + } + // Try calling until we find a height with no migration. for { - res, err = a.StateManager.CallWithGas(ctx, msg, []types.ChainMsg{}, ts) + res, err = a.StateManager.CallWithGas(ctx, msg, []types.ChainMsg{}, ts, applyTsMessages) if err != stmgr.ErrExpensiveFork { break } @@ -959,10 +965,15 @@ func gasSearch( high := msg.GasLimit low := msg.GasLimit + applyTsMessages := true + if os.Getenv("LOTUS_SKIP_APPLY_TS_MESSAGE_CALL_WITH_GAS") == "1" { + applyTsMessages = false + } + canSucceed := func(limit int64) (bool, error) { msg.GasLimit = limit - res, err := smgr.CallWithGas(ctx, &msg, priorMsgs, ts) + res, err := smgr.CallWithGas(ctx, &msg, priorMsgs, ts, applyTsMessages) if err != nil { return false, xerrors.Errorf("CallWithGas failed: %w", err) } diff --git a/node/impl/full/gas.go b/node/impl/full/gas.go index 5ed51248af8..43e04deeac9 100644 --- a/node/impl/full/gas.go +++ b/node/impl/full/gas.go @@ -4,6 +4,7 @@ import ( "context" "math" "math/rand" + "os" "sort" lru "github.com/hashicorp/golang-lru/v2" @@ -276,10 +277,15 @@ func gasEstimateCallWithGas( priorMsgs = append(priorMsgs, m) } + applyTsMessages := true + if os.Getenv("LOTUS_SKIP_APPLY_TS_MESSAGE_CALL_WITH_GAS") == "1" { + applyTsMessages = false + } + // Try calling until we find a height with no migration. var res *api.InvocResult for { - res, err = smgr.CallWithGas(ctx, &msg, priorMsgs, ts) + res, err = smgr.CallWithGas(ctx, &msg, priorMsgs, ts, applyTsMessages) if err != stmgr.ErrExpensiveFork { break } From 59640a8b224730a744f91fed03451532282131f3 Mon Sep 17 00:00:00 2001 From: Fridrik Asmundsson Date: Thu, 23 Mar 2023 11:33:17 +0000 Subject: [PATCH 095/243] Populate the index on snapshot import Fixes: https://github.com/filecoin-project/lotus/issues/10537 --- chain/index/msgindex.go | 62 +++++++++++++++++++++++++++++++++++++++++ cmd/lotus/daemon.go | 7 +++++ 2 files changed, 69 insertions(+) diff --git a/chain/index/msgindex.go b/chain/index/msgindex.go index d5c6a252e45..71a0a860248 100644 --- a/chain/index/msgindex.go +++ b/chain/index/msgindex.go @@ -169,6 +169,68 @@ func NewMsgIndex(lctx context.Context, basePath string, cs ChainStore) (MsgIndex return msgIndex, nil } +func PopulateAfterSnapshot(lctx context.Context, basePath string, cs ChainStore) error { + err := os.MkdirAll(basePath, 0755) + if err != nil { + return xerrors.Errorf("error creating msgindex base directory: %w", err) + } + + dbPath := path.Join(basePath, dbName) + if _, err := os.Stat(dbPath); err == nil { + return xerrors.Errorf("msgindex already exists at %s", dbPath) + } + + db, err := sql.Open("sqlite3", dbPath) + if err != nil { + return xerrors.Errorf("error opening msgindex database: %w", err) + } + defer db.Close() + + if err := prepareDB(db); err != nil { + return xerrors.Errorf("error creating msgindex database: %w", err) + } + + insertStmt, err := db.Prepare(dbqInsertMessage) + if err != nil { + return xerrors.Errorf("prepare insertMsgStmt: %w", err) + } + defer insertStmt.Close() + + curTs := cs.GetHeaviestTipSet() + startHeight := curTs.Height() + for curTs != nil { + tscid, err := curTs.Key().Cid() + if err != nil { + return xerrors.Errorf("error computing tipset cid: %w", err) + } + + tskey := tscid.String() + epoch := int64(curTs.Height()) + + //log.Infof("epoch %d-%d, populating msgindex with tipset %s", curTs.Height(), startHeight-curTs.Height(), tskey) + + msgs, err := cs.MessagesForTipset(lctx, curTs) + if err != nil { + log.Infof("stopping import after %d tipsets", startHeight-curTs.Height()) + break + } + + for _, msg := range msgs { + key := msg.Cid().String() + if _, err := insertStmt.Exec(key, tskey, epoch); err != nil { + return xerrors.Errorf("error inserting message: %w", err) + } + } + + curTs, err = cs.GetTipSetFromKey(lctx, curTs.Parents()) + if err != nil { + return xerrors.Errorf("error walking chain: %w", err) + } + } + + return nil +} + // init utilities func prepareDB(db *sql.DB) error { for _, stmt := range dbDefs { diff --git a/cmd/lotus/daemon.go b/cmd/lotus/daemon.go index 585d4b2ceaa..0fc96775d13 100644 --- a/cmd/lotus/daemon.go +++ b/cmd/lotus/daemon.go @@ -13,6 +13,7 @@ import ( "io/ioutil" "net/http" "os" + "path" "runtime/pprof" "strings" @@ -558,5 +559,11 @@ func ImportChain(ctx context.Context, r repo.Repo, fname string, snapshot bool) return err } + log.Info("populating message index...") + if err := index.PopulateAfterSnapshot(ctx, path.Join(lr.Path(), "sqlite"), cst); err != nil { + return err + } + log.Info("populating message index done") + return nil } From 4b590e2102a36ae2fd0b9511a914c3de5221dee6 Mon Sep 17 00:00:00 2001 From: vyzo Date: Fri, 24 Mar 2023 15:16:22 +0200 Subject: [PATCH 096/243] add vm execution metrics --- chain/vm/execution.go | 39 +++++++++++++++++++++++++++++++++++++-- metrics/metrics.go | 15 +++++++++++++++ 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/chain/vm/execution.go b/chain/vm/execution.go index b2573dffc61..d984d0095c5 100644 --- a/chain/vm/execution.go +++ b/chain/vm/execution.go @@ -7,9 +7,13 @@ import ( "strconv" "sync" + "go.opencensus.io/stats" + "go.opencensus.io/tag" + "github.com/ipfs/go-cid" "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/metrics" ) const ( @@ -81,6 +85,7 @@ func (e *vmExecutor) Done() { } type executionToken struct { + lane ExecutionLane reserved int } @@ -99,6 +104,9 @@ type executionEnv struct { } func (e *executionEnv) getToken(lane ExecutionLane) *executionToken { + metricsUp(metrics.VMExecutionWaiting, lane) + defer metricsDown(metrics.VMExecutionWaiting, lane) + e.mx.Lock() defer e.mx.Unlock() @@ -109,7 +117,9 @@ func (e *executionEnv) getToken(lane ExecutionLane) *executionToken { } e.available-- - return &executionToken{reserved: 0} + + metricsUp(metrics.VMExecutionActive, lane) + return &executionToken{lane: lane, reserved: 0} case ExecutionLanePriority: for e.available == 0 { @@ -123,7 +133,9 @@ func (e *executionEnv) getToken(lane ExecutionLane) *executionToken { e.reserved-- reserving = 1 } - return &executionToken{reserved: reserving} + + metricsUp(metrics.VMExecutionActive, lane) + return &executionToken{lane: lane, reserved: reserving} default: // already checked at interface boundary in NewVM, so this is appropriate @@ -139,6 +151,29 @@ func (e *executionEnv) putToken(token *executionToken) { e.reserved += token.reserved e.cond.Broadcast() + + metricsDown(metrics.VMExecutionActive, token.lane) +} + +func metricsUp(metric *stats.Int64Measure, lane ExecutionLane) { + metricsAdjust(metric, lane, 1) +} + +func metricsDown(metric *stats.Int64Measure, lane ExecutionLane) { + metricsAdjust(metric, lane, -1) +} + +func metricsAdjust(metric *stats.Int64Measure, lane ExecutionLane, delta int) { + laneName := "default" + if lane > ExecutionLaneDefault { + laneName = "priority" + } + + ctx, _ := tag.New( + context.Background(), + tag.Upsert(metrics.ExecutionLane, laneName), + ) + stats.Record(ctx, metric.M(int64(delta))) } func init() { diff --git a/metrics/metrics.go b/metrics/metrics.go index ca638ac273a..61bd86fbd14 100644 --- a/metrics/metrics.go +++ b/metrics/metrics.go @@ -58,6 +58,9 @@ var ( ProtocolID, _ = tag.NewKey("proto") Direction, _ = tag.NewKey("direction") UseFD, _ = tag.NewKey("use_fd") + + // vm execution + ExecutionLane, _ = tag.NewKey("lane") ) // Measures @@ -121,6 +124,8 @@ var ( VMApplyFlush = stats.Float64("vm/applyblocks_flush", "Time spent flushing vm state", stats.UnitMilliseconds) VMSends = stats.Int64("vm/sends", "Counter for sends processed by the VM", stats.UnitDimensionless) VMApplied = stats.Int64("vm/applied", "Counter for messages (including internal messages) processed by the VM", stats.UnitDimensionless) + VMExecutionWaiting = stats.Int64("vm/execution_waiting", "Counter for VM executions waiting to be assigned to a lane", stats.UnitDimensionless) + VMExecutionActive = stats.Int64("vm/execution_default", "Counter for active VM executions", stats.UnitDimensionless) // miner WorkerCallsStarted = stats.Int64("sealing/worker_calls_started", "Counter of started worker tasks", stats.UnitDimensionless) @@ -363,6 +368,16 @@ var ( Measure: VMApplied, Aggregation: view.LastValue(), } + VMExecutionWaitingView = &view.View{ + Measure: VMExecutionWaiting, + Aggregation: view.LastValue(), + TagKeys: []tag.Key{ExecutionLane}, + } + VMExecutionActiveView = &view.View{ + Measure: VMExecutionActive, + Aggregation: view.LastValue(), + TagKeys: []tag.Key{ExecutionLane}, + } // miner WorkerCallsStartedView = &view.View{ From 08134552a49da645b3c08f8f748be37ed6891dcb Mon Sep 17 00:00:00 2001 From: vyzo Date: Fri, 24 Mar 2023 15:48:58 +0200 Subject: [PATCH 097/243] address review comments --- chain/consensus/compute_state.go | 1 + chain/gen/genesis/miners.go | 1 + chain/stmgr/call.go | 1 + chain/vm/execution.go | 14 ++++++++++---- chain/vm/vmi.go | 2 +- metrics/metrics.go | 6 +++--- 6 files changed, 17 insertions(+), 8 deletions(-) diff --git a/chain/consensus/compute_state.go b/chain/consensus/compute_state.go index cf05d612d26..3c1bab9ca78 100644 --- a/chain/consensus/compute_state.go +++ b/chain/consensus/compute_state.go @@ -196,6 +196,7 @@ func (t *TipSetExecutor) ApplyBlocks(ctx context.Context, cronGas = 0 partDone = metrics.Timer(ctx, metrics.VMApplyMessages) + // TODO reorg the code to minimize the execution critical section vmi, err := makeVm(pstate, epoch, ts.MinTimestamp()) if err != nil { return cid.Undef, cid.Undef, xerrors.Errorf("making vm: %w", err) diff --git a/chain/gen/genesis/miners.go b/chain/gen/genesis/miners.go index 900389f565d..09b46a6e76c 100644 --- a/chain/gen/genesis/miners.go +++ b/chain/gen/genesis/miners.go @@ -108,6 +108,7 @@ func SetupStorageMiners(ctx context.Context, cs *store.ChainStore, sys vm.Syscal if err != nil { return cid.Undef, fmt.Errorf("creating vm: %w", err) } + // Note: genesisVm is mutated, so this has to happen in a deferred func; go horror show. defer func() { genesisVm.Done() }() if len(miners) == 0 { diff --git a/chain/stmgr/call.go b/chain/stmgr/call.go index 9633d6417d7..8e18c25df96 100644 --- a/chain/stmgr/call.go +++ b/chain/stmgr/call.go @@ -159,6 +159,7 @@ func (sm *StateManager) callInternal(ctx context.Context, msg *types.Message, pr if err != nil { return nil, xerrors.Errorf("failed to set up vm: %w", err) } + // Note: vmi is mutated, so this has to happen in a deferred func; go horror show. defer func() { vmi.Done() }() for i, m := range priorMsgs { diff --git a/chain/vm/execution.go b/chain/vm/execution.go index d984d0095c5..0edb1388484 100644 --- a/chain/vm/execution.go +++ b/chain/vm/execution.go @@ -17,8 +17,14 @@ import ( ) const ( + // DefaultAvailableExecutionLanes is the number of available execution lanes; it is the bound of + // concurrent active executions. + // This is the default value in filecoin-ffi DefaultAvailableExecutionLanes = 4 - DefaultPriorityExecutionLanes = 2 + // DefaultPriorityExecutionLanes is the number of reserved execution lanes for priority computations. + // This is purely userspace, but we believe it is a reasonable default, even with more available + // lanes. + DefaultPriorityExecutionLanes = 2 ) var ErrExecutorDone = errors.New("executor has been released") @@ -118,7 +124,7 @@ func (e *executionEnv) getToken(lane ExecutionLane) *executionToken { e.available-- - metricsUp(metrics.VMExecutionActive, lane) + metricsUp(metrics.VMExecutionRunning, lane) return &executionToken{lane: lane, reserved: 0} case ExecutionLanePriority: @@ -134,7 +140,7 @@ func (e *executionEnv) getToken(lane ExecutionLane) *executionToken { reserving = 1 } - metricsUp(metrics.VMExecutionActive, lane) + metricsUp(metrics.VMExecutionRunning, lane) return &executionToken{lane: lane, reserved: reserving} default: @@ -152,7 +158,7 @@ func (e *executionEnv) putToken(token *executionToken) { e.cond.Broadcast() - metricsDown(metrics.VMExecutionActive, token.lane) + metricsDown(metrics.VMExecutionRunning, token.lane) } func metricsUp(metric *stats.Int64Measure, lane ExecutionLane) { diff --git a/chain/vm/vmi.go b/chain/vm/vmi.go index e5d5daff8a6..7aa52b58534 100644 --- a/chain/vm/vmi.go +++ b/chain/vm/vmi.go @@ -37,7 +37,7 @@ type Interface interface { Flush(ctx context.Context) (cid.Cid, error) } -// Executor is the general vm execution interface, which is prioritized according to execution langes. +// Executor is the general vm execution interface, which is prioritized according to execution lanes. // User must call Done when it is done with this executor to release resource holds by the execution // environment type Executor interface { diff --git a/metrics/metrics.go b/metrics/metrics.go index 61bd86fbd14..bd1295d1778 100644 --- a/metrics/metrics.go +++ b/metrics/metrics.go @@ -125,7 +125,7 @@ var ( VMSends = stats.Int64("vm/sends", "Counter for sends processed by the VM", stats.UnitDimensionless) VMApplied = stats.Int64("vm/applied", "Counter for messages (including internal messages) processed by the VM", stats.UnitDimensionless) VMExecutionWaiting = stats.Int64("vm/execution_waiting", "Counter for VM executions waiting to be assigned to a lane", stats.UnitDimensionless) - VMExecutionActive = stats.Int64("vm/execution_default", "Counter for active VM executions", stats.UnitDimensionless) + VMExecutionRunning = stats.Int64("vm/execution_running", "Counter for running VM executions", stats.UnitDimensionless) // miner WorkerCallsStarted = stats.Int64("sealing/worker_calls_started", "Counter of started worker tasks", stats.UnitDimensionless) @@ -373,8 +373,8 @@ var ( Aggregation: view.LastValue(), TagKeys: []tag.Key{ExecutionLane}, } - VMExecutionActiveView = &view.View{ - Measure: VMExecutionActive, + VMExecutionRunningView = &view.View{ + Measure: VMExecutionRunning, Aggregation: view.LastValue(), TagKeys: []tag.Key{ExecutionLane}, } From 48c57d394be4c80f3db63793bdb85fa8b68cbd22 Mon Sep 17 00:00:00 2001 From: Fridrik Asmundsson Date: Fri, 24 Mar 2023 15:36:31 +0000 Subject: [PATCH 098/243] Improve performance when populating message indax --- chain/index/msgindex.go | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/chain/index/msgindex.go b/chain/index/msgindex.go index 71a0a860248..477738338a7 100644 --- a/chain/index/msgindex.go +++ b/chain/index/msgindex.go @@ -190,9 +190,20 @@ func PopulateAfterSnapshot(lctx context.Context, basePath string, cs ChainStore) return xerrors.Errorf("error creating msgindex database: %w", err) } - insertStmt, err := db.Prepare(dbqInsertMessage) + tx, err := db.Begin() if err != nil { - return xerrors.Errorf("prepare insertMsgStmt: %w", err) + return xerrors.Errorf("error when starting transaction: %w", err) + } + + rollback := func() { + if err := tx.Rollback(); err != nil { + log.Errorf("error in rollback: %s", err) + } + } + + insertStmt, err := tx.Prepare(dbqInsertMessage) + if err != nil { + return xerrors.Errorf("error preparing insertMsgStmt: %w", err) } defer insertStmt.Close() @@ -201,14 +212,13 @@ func PopulateAfterSnapshot(lctx context.Context, basePath string, cs ChainStore) for curTs != nil { tscid, err := curTs.Key().Cid() if err != nil { + rollback() return xerrors.Errorf("error computing tipset cid: %w", err) } tskey := tscid.String() epoch := int64(curTs.Height()) - //log.Infof("epoch %d-%d, populating msgindex with tipset %s", curTs.Height(), startHeight-curTs.Height(), tskey) - msgs, err := cs.MessagesForTipset(lctx, curTs) if err != nil { log.Infof("stopping import after %d tipsets", startHeight-curTs.Height()) @@ -218,16 +228,23 @@ func PopulateAfterSnapshot(lctx context.Context, basePath string, cs ChainStore) for _, msg := range msgs { key := msg.Cid().String() if _, err := insertStmt.Exec(key, tskey, epoch); err != nil { + rollback() return xerrors.Errorf("error inserting message: %w", err) } } curTs, err = cs.GetTipSetFromKey(lctx, curTs.Parents()) if err != nil { + rollback() return xerrors.Errorf("error walking chain: %w", err) } } + err = tx.Commit() + if err != nil { + return xerrors.Errorf("error commiting transaction: %w", err) + } + return nil } From b8137f6b4dbd5239d78c5cfd5fcf0b8eba5ed62a Mon Sep 17 00:00:00 2001 From: Fridrik Asmundsson Date: Fri, 24 Mar 2023 16:44:26 +0000 Subject: [PATCH 099/243] Delete existing message index when loading from snapshot --- chain/index/msgindex.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/chain/index/msgindex.go b/chain/index/msgindex.go index 477738338a7..89c2bdc9f8d 100644 --- a/chain/index/msgindex.go +++ b/chain/index/msgindex.go @@ -176,8 +176,12 @@ func PopulateAfterSnapshot(lctx context.Context, basePath string, cs ChainStore) } dbPath := path.Join(basePath, dbName) + + // if a database already exists, we try to delete it and create a new one if _, err := os.Stat(dbPath); err == nil { - return xerrors.Errorf("msgindex already exists at %s", dbPath) + if err = os.Remove(dbPath); err != nil { + return xerrors.Errorf("msgindex already exists at %s and can't be deleted", dbPath) + } } db, err := sql.Open("sqlite3", dbPath) From 6f440a6420b067f698db283bb4159fa6d292580b Mon Sep 17 00:00:00 2001 From: Mike Seiler Date: Fri, 24 Mar 2023 15:49:02 -1000 Subject: [PATCH 100/243] change highly contented message pool locks to RWMutexes for performance --- chain/messagepool/check.go | 18 +++++++------- chain/messagepool/messagepool.go | 40 ++++++++++++++++---------------- chain/messagepool/selection.go | 1 + 3 files changed, 30 insertions(+), 29 deletions(-) diff --git a/chain/messagepool/check.go b/chain/messagepool/check.go index b1e2a277846..a1097e7d1f2 100644 --- a/chain/messagepool/check.go +++ b/chain/messagepool/check.go @@ -32,14 +32,14 @@ func (mp *MessagePool) CheckMessages(ctx context.Context, protos []*api.MessageP // CheckPendingMessages performs a set of logical sets for all messages pending from a given actor func (mp *MessagePool) CheckPendingMessages(ctx context.Context, from address.Address) ([][]api.MessageCheckStatus, error) { var msgs []*types.Message - mp.lk.Lock() + mp.lk.RLock() mset, ok := mp.pending[from] if ok { for _, sm := range mset.msgs { msgs = append(msgs, &sm.Message) } } - mp.lk.Unlock() + mp.lk.RUnlock() if len(msgs) == 0 { return nil, nil @@ -58,7 +58,7 @@ func (mp *MessagePool) CheckReplaceMessages(ctx context.Context, replace []*type msgMap := make(map[address.Address]map[uint64]*types.Message) count := 0 - mp.lk.Lock() + mp.lk.RLock() for _, m := range replace { mmap, ok := msgMap[m.From] if !ok { @@ -76,7 +76,7 @@ func (mp *MessagePool) CheckReplaceMessages(ctx context.Context, replace []*type } mmap[m.Nonce] = m } - mp.lk.Unlock() + mp.lk.RUnlock() msgs := make([]*types.Message, 0, count) start := 0 @@ -103,9 +103,9 @@ func (mp *MessagePool) checkMessages(ctx context.Context, msgs []*types.Message, if mp.api.IsLite() { return nil, nil } - mp.curTsLk.Lock() + mp.curTsLk.RLock() curTs := mp.curTs - mp.curTsLk.Unlock() + mp.curTsLk.RUnlock() epoch := curTs.Height() + 1 @@ -143,7 +143,7 @@ func (mp *MessagePool) checkMessages(ctx context.Context, msgs []*types.Message, st, ok := state[m.From] if !ok { - mp.lk.Lock() + mp.lk.RLock() mset, ok := mp.pending[m.From] if ok && !interned { st = &actorState{nextNonce: mset.nextNonce, requiredFunds: mset.requiredFunds} @@ -151,14 +151,14 @@ func (mp *MessagePool) checkMessages(ctx context.Context, msgs []*types.Message, st.requiredFunds = new(stdbig.Int).Add(st.requiredFunds, m.Message.Value.Int) } state[m.From] = st - mp.lk.Unlock() + mp.lk.RUnlock() check.OK = true check.Hint = map[string]interface{}{ "nonce": st.nextNonce, } } else { - mp.lk.Unlock() + mp.lk.RUnlock() stateNonce, err := mp.getStateNonce(ctx, m.From, curTs) if err != nil { diff --git a/chain/messagepool/messagepool.go b/chain/messagepool/messagepool.go index 0d787bd50ef..a7b2106bb76 100644 --- a/chain/messagepool/messagepool.go +++ b/chain/messagepool/messagepool.go @@ -118,7 +118,7 @@ func init() { } type MessagePool struct { - lk sync.Mutex + lk sync.RWMutex ds dtypes.MetadataDS @@ -139,7 +139,7 @@ type MessagePool struct { keyCache map[address.Address]address.Address - curTsLk sync.Mutex // DO NOT LOCK INSIDE lk + curTsLk sync.RWMutex // DO NOT LOCK INSIDE lk curTs *types.TipSet cfgLk sync.RWMutex @@ -1001,19 +1001,19 @@ func (mp *MessagePool) addLocked(ctx context.Context, m *types.SignedMessage, st } func (mp *MessagePool) GetNonce(ctx context.Context, addr address.Address, _ types.TipSetKey) (uint64, error) { - mp.curTsLk.Lock() - defer mp.curTsLk.Unlock() + mp.curTsLk.RLock() + defer mp.curTsLk.RUnlock() - mp.lk.Lock() - defer mp.lk.Unlock() + mp.lk.RLock() + defer mp.lk.RUnlock() return mp.getNonceLocked(ctx, addr, mp.curTs) } // GetActor should not be used. It is only here to satisfy interface mess caused by lite node handling func (mp *MessagePool) GetActor(_ context.Context, addr address.Address, _ types.TipSetKey) (*types.Actor, error) { - mp.curTsLk.Lock() - defer mp.curTsLk.Unlock() + mp.curTsLk.RLock() + defer mp.curTsLk.RUnlock() return mp.api.GetActorAfter(addr, mp.curTs) } @@ -1164,11 +1164,11 @@ func (mp *MessagePool) remove(ctx context.Context, from address.Address, nonce u } func (mp *MessagePool) Pending(ctx context.Context) ([]*types.SignedMessage, *types.TipSet) { - mp.curTsLk.Lock() - defer mp.curTsLk.Unlock() + mp.curTsLk.RLock() + defer mp.curTsLk.RUnlock() - mp.lk.Lock() - defer mp.lk.Unlock() + mp.lk.RLock() + defer mp.lk.RUnlock() return mp.allPending(ctx) } @@ -1184,11 +1184,11 @@ func (mp *MessagePool) allPending(ctx context.Context) ([]*types.SignedMessage, } func (mp *MessagePool) PendingFor(ctx context.Context, a address.Address) ([]*types.SignedMessage, *types.TipSet) { - mp.curTsLk.Lock() - defer mp.curTsLk.Unlock() + mp.curTsLk.RLock() + defer mp.curTsLk.RUnlock() - mp.lk.Lock() - defer mp.lk.Unlock() + mp.lk.RLock() + defer mp.lk.RUnlock() return mp.pendingFor(ctx, a), mp.curTs } @@ -1237,9 +1237,9 @@ func (mp *MessagePool) HeadChange(ctx context.Context, revert []*types.TipSet, a maybeRepub := func(cid cid.Cid) { if !repubTrigger { - mp.lk.Lock() + mp.lk.RLock() _, republished := mp.republished[cid] - mp.lk.Unlock() + mp.lk.RUnlock() if republished { repubTrigger = true } @@ -1310,9 +1310,9 @@ func (mp *MessagePool) HeadChange(ctx context.Context, revert []*types.TipSet, a } if len(revert) > 0 && futureDebug { - mp.lk.Lock() + mp.lk.RLock() msgs, ts := mp.allPending(ctx) - mp.lk.Unlock() + mp.lk.RUnlock() buckets := map[address.Address]*statBucket{} diff --git a/chain/messagepool/selection.go b/chain/messagepool/selection.go index bd504412863..d510cf9508d 100644 --- a/chain/messagepool/selection.go +++ b/chain/messagepool/selection.go @@ -43,6 +43,7 @@ func (mp *MessagePool) SelectMessages(ctx context.Context, ts *types.TipSet, tq mp.curTsLk.Lock() defer mp.curTsLk.Unlock() + //TODO confirm if we can switch to RLock here for performance mp.lk.Lock() defer mp.lk.Unlock() From 90171c8babee8c6003aa85b00aec2f3f3780342c Mon Sep 17 00:00:00 2001 From: Fridrik Asmundsson Date: Sat, 25 Mar 2023 11:17:54 +0000 Subject: [PATCH 101/243] Addressing lint errors --- chain/index/msgindex.go | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/chain/index/msgindex.go b/chain/index/msgindex.go index 89c2bdc9f8d..42a536c160c 100644 --- a/chain/index/msgindex.go +++ b/chain/index/msgindex.go @@ -188,7 +188,11 @@ func PopulateAfterSnapshot(lctx context.Context, basePath string, cs ChainStore) if err != nil { return xerrors.Errorf("error opening msgindex database: %w", err) } - defer db.Close() + defer func() { + if err := db.Close(); err != nil { + log.Errorf("error closing msgindex database: %s", err) + } + }() if err := prepareDB(db); err != nil { return xerrors.Errorf("error creating msgindex database: %w", err) @@ -207,9 +211,13 @@ func PopulateAfterSnapshot(lctx context.Context, basePath string, cs ChainStore) insertStmt, err := tx.Prepare(dbqInsertMessage) if err != nil { - return xerrors.Errorf("error preparing insertMsgStmt: %w", err) + return xerrors.Errorf("error preparing insertStmt: %w", err) } - defer insertStmt.Close() + defer func() { + if err := insertStmt.Close(); err != nil { + log.Errorf("error closing insert statement: %s", err) + } + }() curTs := cs.GetHeaviestTipSet() startHeight := curTs.Height() @@ -246,7 +254,7 @@ func PopulateAfterSnapshot(lctx context.Context, basePath string, cs ChainStore) err = tx.Commit() if err != nil { - return xerrors.Errorf("error commiting transaction: %w", err) + return xerrors.Errorf("error committing transaction: %w", err) } return nil From 0711fdc3dda8a21c182843413c1fc162d3687fc1 Mon Sep 17 00:00:00 2001 From: Phi Date: Mon, 27 Mar 2023 10:04:15 +0200 Subject: [PATCH 102/243] Initialize with same length as partition Initialize the postParam.Partitions slice with the same length as i.Partitions before iterating over it in the loop. --- cmd/lotus-miner/proving.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cmd/lotus-miner/proving.go b/cmd/lotus-miner/proving.go index d58ed1250d5..3ecc58ba7af 100644 --- a/cmd/lotus-miner/proving.go +++ b/cmd/lotus-miner/proving.go @@ -658,6 +658,10 @@ It will not send any messages to the chain.`, for _, i := range res { var postParam SubmitWindowedPoStParams postParam.Deadline = i.Deadline + + // Initialize the postParam.Partitions slice with the same length as i.Partitions + postParam.Partitions = make([]PoStPartition, len(i.Partitions)) + for id, part := range i.Partitions { postParam.Partitions[id].Index = part.Index count, err := part.Skipped.Count() From aebe3d4cf7dd415c53b6dd7f77087747db2cea6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 27 Mar 2023 13:23:29 +0200 Subject: [PATCH 103/243] fix: itests: Don't call t.Error in MineBlocks goroutine --- itests/kit/blockminer.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/itests/kit/blockminer.go b/itests/kit/blockminer.go index 4cd0cc671c8..c099d016e7f 100644 --- a/itests/kit/blockminer.go +++ b/itests/kit/blockminer.go @@ -291,7 +291,8 @@ func (bm *BlockMiner) MineBlocks(ctx context.Context, blocktime time.Duration) { case ctx.Err() != nil: // context fired. return default: // log error - bm.t.Error(err) + bm.t.Logf("MINEBLOCKS loop error: %+v", err) + return } } }() From 6012e65319eedea15efe5280539d9e8a14db427a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 27 Mar 2023 15:34:59 +0200 Subject: [PATCH 104/243] debug batch deal test a bit more --- itests/batch_deal_test.go | 12 ++++++++++++ itests/kit/blockminer.go | 3 ++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/itests/batch_deal_test.go b/itests/batch_deal_test.go index d1bb4053169..9df6155bad6 100644 --- a/itests/batch_deal_test.go +++ b/itests/batch_deal_test.go @@ -37,6 +37,8 @@ func TestBatchDealInput(t *testing.T) { run := func(piece, deals, expectSectors int) func(t *testing.T) { return func(t *testing.T) { + t.Logf("batchtest start") + ctx := context.Background() publishPeriod := 10 * time.Second @@ -73,6 +75,8 @@ func TestBatchDealInput(t *testing.T) { err := miner.MarketSetAsk(ctx, big.Zero(), big.Zero(), 200, 128, 32<<30) require.NoError(t, err) + t.Logf("batchtest ask set") + checkNoPadding := func() { sl, err := miner.SectorsListNonGenesis(ctx) require.NoError(t, err) @@ -118,16 +122,24 @@ func TestBatchDealInput(t *testing.T) { }() } + t.Logf("batchtest deals started") + // Wait for maxDealsPerMsg of the deals to be published for i := 0; i < int(maxDealsPerMsg); i++ { <-done } + t.Logf("batchtest deals published") + checkNoPadding() + t.Logf("batchtest no padding") + sl, err := miner.SectorsListNonGenesis(ctx) require.NoError(t, err) require.Equal(t, len(sl), expectSectors) + + t.Logf("batchtest done") } } diff --git a/itests/kit/blockminer.go b/itests/kit/blockminer.go index c099d016e7f..cbe352dd5a9 100644 --- a/itests/kit/blockminer.go +++ b/itests/kit/blockminer.go @@ -245,7 +245,8 @@ func (bm *BlockMiner) MineBlocksMustPost(ctx context.Context, blocktime time.Dur case ctx.Err() != nil: // context fired. return default: // log error - bm.t.Error(err) + bm.t.Logf("MINEBLOCKS-post loop error: %+v", err) + return } } }() From ddebdfb37cc1efb034aa8fce09c907dab3a5c014 Mon Sep 17 00:00:00 2001 From: vyzo Date: Mon, 27 Mar 2023 20:56:07 +0300 Subject: [PATCH 105/243] add execution metrics to the chain node views --- metrics/metrics.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/metrics/metrics.go b/metrics/metrics.go index bd1295d1778..465fab63d01 100644 --- a/metrics/metrics.go +++ b/metrics/metrics.go @@ -742,6 +742,8 @@ var ChainNodeViews = append([]*view.View{ VMApplyFlushView, VMSendsView, VMAppliedView, + VMExecutionWaitingView, + VMExecutionRunningView, }, DefaultViews...) var MinerNodeViews = append([]*view.View{ From a0f908da5739be310558a96908f9cb8b44bcffde Mon Sep 17 00:00:00 2001 From: vyzo Date: Mon, 27 Mar 2023 23:11:56 +0300 Subject: [PATCH 106/243] use Count instead of LastValue --- metrics/metrics.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/metrics/metrics.go b/metrics/metrics.go index 465fab63d01..58d235acef4 100644 --- a/metrics/metrics.go +++ b/metrics/metrics.go @@ -370,12 +370,12 @@ var ( } VMExecutionWaitingView = &view.View{ Measure: VMExecutionWaiting, - Aggregation: view.LastValue(), + Aggregation: view.Count(), TagKeys: []tag.Key{ExecutionLane}, } VMExecutionRunningView = &view.View{ Measure: VMExecutionRunning, - Aggregation: view.LastValue(), + Aggregation: view.Count(), TagKeys: []tag.Key{ExecutionLane}, } From 6ecaf826af63a01b5f6df76e90adb7cab4cfc8cc Mon Sep 17 00:00:00 2001 From: vyzo Date: Mon, 27 Mar 2023 23:17:41 +0300 Subject: [PATCH 107/243] no, Sum it is. --- metrics/metrics.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/metrics/metrics.go b/metrics/metrics.go index 58d235acef4..13627a663d5 100644 --- a/metrics/metrics.go +++ b/metrics/metrics.go @@ -370,12 +370,12 @@ var ( } VMExecutionWaitingView = &view.View{ Measure: VMExecutionWaiting, - Aggregation: view.Count(), + Aggregation: view.Sum(), TagKeys: []tag.Key{ExecutionLane}, } VMExecutionRunningView = &view.View{ Measure: VMExecutionRunning, - Aggregation: view.Count(), + Aggregation: view.Sum(), TagKeys: []tag.Key{ExecutionLane}, } From 92f6d3e468ed9a77092442e71203e1850fa5320f Mon Sep 17 00:00:00 2001 From: Alfonso de la Rocha Date: Tue, 28 Mar 2023 10:01:43 +0200 Subject: [PATCH 108/243] global locking strategy for blockInfo map --- chain/sub/bcast/consistent.go | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/chain/sub/bcast/consistent.go b/chain/sub/bcast/consistent.go index 165476ffb6f..93621d51341 100644 --- a/chain/sub/bcast/consistent.go +++ b/chain/sub/bcast/consistent.go @@ -32,33 +32,27 @@ type blksInfo struct { } type bcastDict struct { - // thread-safe map impl for the dictionary - // sync.Map accepts `any` as keys and values. - // To make it type safe and only support the right - // types we use this auxiliary type. - m *sync.Map + m map[string]*blksInfo } func (bd *bcastDict) load(key []byte) (*blksInfo, bool) { - v, ok := bd.m.Load(string(key)) + v, ok := bd.m[string(key)] if !ok { return nil, ok } - return v.(*blksInfo), ok + return v, ok } -func (bd *bcastDict) store(key []byte, d *blksInfo) { - bd.m.Store(string(key), d) +func (bd *bcastDict) blkLen(key []byte) int { + return len(bd.m[string(key)].blks) } -func (bd *bcastDict) blkLen(key []byte) int { - v, ok := bd.m.Load(string(key)) - if !ok { - return 0 - } - return len(v.(*blksInfo).blks) +func (bd *bcastDict) store(key []byte, d *blksInfo) { + bd.m[string(key)] = d } +// ConsistentBCast tracks recent information about the +// blocks and tickets received at different epochs type ConsistentBCast struct { lk sync.RWMutex delay time.Duration @@ -66,7 +60,7 @@ type ConsistentBCast struct { } func newBcastDict() *bcastDict { - return &bcastDict{new(sync.Map)} + return &bcastDict{m: make(map[string]*blksInfo)} } func BCastKey(bh *types.BlockHeader) []byte { @@ -113,12 +107,13 @@ func (cb *ConsistentBCast) Len() int { // certain epoch to be propagated to a large amount of miners in the network. func (cb *ConsistentBCast) RcvBlock(ctx context.Context, blk *types.BlockMsg) { cb.lk.Lock() + defer cb.lk.Unlock() bcastDict, ok := cb.m[blk.Header.Height] if !ok { bcastDict = newBcastDict() cb.m[blk.Header.Height] = bcastDict } - cb.lk.Unlock() + key := BCastKey(blk.Header) blkCid := blk.Cid() @@ -147,12 +142,13 @@ func (cb *ConsistentBCast) RcvBlock(ctx context.Context, blk *types.BlockMsg) { func (cb *ConsistentBCast) WaitForDelivery(bh *types.BlockHeader) error { cb.lk.RLock() bcastDict := cb.m[bh.Height] - cb.lk.RUnlock() key := BCastKey(bh) bInfo, ok := bcastDict.load(key) + cb.lk.RUnlock() if !ok { return xerrors.Errorf("something went wrong, unknown block with Epoch + VRFProof (cid=%s) in consistent broadcast storage", key) } + // Wait for the timeout <-bInfo.ctx.Done() if bcastDict.blkLen(key) > 1 { From ad81cd18c2a05d3dee944694abcefa1668bfd951 Mon Sep 17 00:00:00 2001 From: Mikers Date: Tue, 28 Mar 2023 08:11:00 +0000 Subject: [PATCH 109/243] cache the tipset nonce calculation before holding the write lock, and also verify that the the calculation is cached after grabbing the write lock. if it is not cached, give up the lock, calculate, and then grab the write lock again --- chain/messagepool/messagepool.go | 29 +++++++++++++++++++++++++---- chain/messagepool/repub.go | 10 +++++----- 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/chain/messagepool/messagepool.go b/chain/messagepool/messagepool.go index a7b2106bb76..70d15ee52e2 100644 --- a/chain/messagepool/messagepool.go +++ b/chain/messagepool/messagepool.go @@ -763,7 +763,28 @@ func (mp *MessagePool) Add(ctx context.Context, m *types.SignedMessage) error { <-mp.addSema }() - mp.curTsLk.Lock() + //Ensure block calculation is cached without holding the write lock + mp.curTsLk.RLock() + tmpCurTs := mp.curTs + mp.curTsLk.RUnlock() + _, _ = mp.api.GetActorAfter(m.Message.From, tmpCurTs) + _, _ = mp.getStateNonce(ctx, m.Message.From, tmpCurTs) + + //if the newly acquired Ts is not the one we just cached, let go of the lock, cache it and open the lock again and repeat.... + for { + mp.curTsLk.Lock() + writeCurTs := mp.curTs + + if writeCurTs == tmpCurTs { + break // we have this cached we can skip + } + mp.curTsLk.Unlock() + tmpCurTs = writeCurTs + _, _ = mp.api.GetActorAfter(m.Message.From, tmpCurTs) + _, _ = mp.getStateNonce(ctx, m.Message.From, tmpCurTs) + + } + defer mp.curTsLk.Unlock() _, err = mp.addTs(ctx, m, mp.curTs, false, false) @@ -852,14 +873,14 @@ func (mp *MessagePool) addTs(ctx context.Context, m *types.SignedMessage, curTs return false, xerrors.Errorf("minimum expected nonce is %d: %w", snonce, ErrNonceTooLow) } - mp.lk.Lock() - defer mp.lk.Unlock() - senderAct, err := mp.api.GetActorAfter(m.Message.From, curTs) if err != nil { return false, xerrors.Errorf("failed to get sender actor: %w", err) } + mp.lk.Lock() + defer mp.lk.Unlock() + // This message can only be included in the _next_ epoch and beyond, hence the +1. epoch := curTs.Height() + 1 nv := mp.api.StateNetworkVersion(ctx, epoch) diff --git a/chain/messagepool/repub.go b/chain/messagepool/repub.go index 9a1e19b6072..60c8b1d573f 100644 --- a/chain/messagepool/repub.go +++ b/chain/messagepool/repub.go @@ -20,18 +20,18 @@ const repubMsgLimit = 30 var RepublishBatchDelay = 100 * time.Millisecond func (mp *MessagePool) republishPendingMessages(ctx context.Context) error { - mp.curTsLk.Lock() + mp.curTsLk.RLock() ts := mp.curTs baseFee, err := mp.api.ChainComputeBaseFee(context.TODO(), ts) if err != nil { - mp.curTsLk.Unlock() + mp.curTsLk.RUnlock() return xerrors.Errorf("computing basefee: %w", err) } baseFeeLowerBound := getBaseFeeLowerBound(baseFee, baseFeeLowerBoundFactor) pending := make(map[address.Address]map[uint64]*types.SignedMessage) - mp.lk.Lock() + mp.lk.RLock() mp.republished = nil // clear this to avoid races triggering an early republish mp.forEachLocal(ctx, func(ctx context.Context, actor address.Address) { mset, ok, err := mp.getPendingMset(ctx, actor) @@ -54,8 +54,8 @@ func (mp *MessagePool) republishPendingMessages(ctx context.Context) error { pending[actor] = pend }) - mp.lk.Unlock() - mp.curTsLk.Unlock() + mp.lk.RUnlock() + mp.curTsLk.RUnlock() if len(pending) == 0 { return nil From e9d22230254111474742ec694354c405dd07f3db Mon Sep 17 00:00:00 2001 From: Mikers Date: Tue, 28 Mar 2023 08:11:23 +0000 Subject: [PATCH 110/243] disallow infinite loop, since in testing this only runs once --- chain/messagepool/messagepool.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/chain/messagepool/messagepool.go b/chain/messagepool/messagepool.go index 70d15ee52e2..3af9b1572c6 100644 --- a/chain/messagepool/messagepool.go +++ b/chain/messagepool/messagepool.go @@ -770,8 +770,9 @@ func (mp *MessagePool) Add(ctx context.Context, m *types.SignedMessage) error { _, _ = mp.api.GetActorAfter(m.Message.From, tmpCurTs) _, _ = mp.getStateNonce(ctx, m.Message.From, tmpCurTs) + cacheSecondTime := true //if the newly acquired Ts is not the one we just cached, let go of the lock, cache it and open the lock again and repeat.... - for { + for cacheSecondTime { mp.curTsLk.Lock() writeCurTs := mp.curTs @@ -782,6 +783,7 @@ func (mp *MessagePool) Add(ctx context.Context, m *types.SignedMessage) error { tmpCurTs = writeCurTs _, _ = mp.api.GetActorAfter(m.Message.From, tmpCurTs) _, _ = mp.getStateNonce(ctx, m.Message.From, tmpCurTs) + cacheSecondTime = false } From df82a8240ebc1ba15a15e5cd65dfacb1a36e72c7 Mon Sep 17 00:00:00 2001 From: Alfonso de la Rocha Date: Tue, 28 Mar 2023 10:33:26 +0200 Subject: [PATCH 111/243] add comments --- chain/sub/bcast/consistent.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/chain/sub/bcast/consistent.go b/chain/sub/bcast/consistent.go index 93621d51341..75209431dfd 100644 --- a/chain/sub/bcast/consistent.go +++ b/chain/sub/bcast/consistent.go @@ -126,6 +126,9 @@ func (cb *ConsistentBCast) RcvBlock(ctx context.Context, blk *types.BlockMsg) { if !cidExists(bInfo.blks, blkCid) { bcastDict.store(key, &blksInfo{bInfo.ctx, bInfo.cancel, append(bInfo.blks, blkCid)}) + // By calling bInfo.eqErr() inside this log we cancel the context for all blocks waiting for + // the epoch-ticket combination making them to fail and not be sent to the syncer, as + // a potential equivocation is detected. log.Errorf("equivocation detected for height %d: %s", blk.Header.Height, bInfo.eqErr()) return } From 831f8a499d33aab69e9859f1dd4baecff4888e70 Mon Sep 17 00:00:00 2001 From: Mikers Date: Tue, 28 Mar 2023 08:40:41 +0000 Subject: [PATCH 112/243] repub needs Lock not RLock --- chain/messagepool/repub.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/chain/messagepool/repub.go b/chain/messagepool/repub.go index 60c8b1d573f..70467643914 100644 --- a/chain/messagepool/repub.go +++ b/chain/messagepool/repub.go @@ -24,14 +24,15 @@ func (mp *MessagePool) republishPendingMessages(ctx context.Context) error { ts := mp.curTs baseFee, err := mp.api.ChainComputeBaseFee(context.TODO(), ts) + mp.curTsLk.RUnlock() if err != nil { - mp.curTsLk.RUnlock() return xerrors.Errorf("computing basefee: %w", err) } baseFeeLowerBound := getBaseFeeLowerBound(baseFee, baseFeeLowerBoundFactor) pending := make(map[address.Address]map[uint64]*types.SignedMessage) - mp.lk.RLock() + mp.curTsLk.Lock() + mp.lk.Lock() mp.republished = nil // clear this to avoid races triggering an early republish mp.forEachLocal(ctx, func(ctx context.Context, actor address.Address) { mset, ok, err := mp.getPendingMset(ctx, actor) @@ -54,8 +55,8 @@ func (mp *MessagePool) republishPendingMessages(ctx context.Context) error { pending[actor] = pend }) - mp.lk.RUnlock() - mp.curTsLk.RUnlock() + mp.lk.Unlock() + mp.curTsLk.Unlock() if len(pending) == 0 { return nil From 1ea7e05cde70dcdbd61a04d7ca06cd33b5f439fd Mon Sep 17 00:00:00 2001 From: Fridrik Asmundsson Date: Mon, 20 Mar 2023 15:17:27 +0000 Subject: [PATCH 113/243] feat: Add small cache to execution traces This PR adds a small cache to calls to ExecutionTrace which helps improve performance for node operators like exchanges and block explorers. If items is in cache calls to this function will be 2-3x faster. Fixes: https://github.com/filecoin-project/lotus/issues/10504 --- chain/stmgr/execute.go | 7 +++++++ chain/stmgr/stmgr.go | 22 ++++++++++++++++++++-- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/chain/stmgr/execute.go b/chain/stmgr/execute.go index f85ff7c0433..3c26445508c 100644 --- a/chain/stmgr/execute.go +++ b/chain/stmgr/execute.go @@ -128,10 +128,17 @@ func (sm *StateManager) ExecutionTraceWithMonitor(ctx context.Context, ts *types } func (sm *StateManager) ExecutionTrace(ctx context.Context, ts *types.TipSet) (cid.Cid, []*api.InvocResult, error) { + if entry, ok := sm.execTraceCache.Get(ts); ok { + return entry.cid, entry.invocTrace, nil + } + var invocTrace []*api.InvocResult st, err := sm.ExecutionTraceWithMonitor(ctx, ts, &InvocationTracer{trace: &invocTrace}) if err != nil { return cid.Undef, nil, err } + + sm.execTraceCache.Add(ts, tipSetCacheEntry{st, invocTrace}) + return st, invocTrace, nil } diff --git a/chain/stmgr/stmgr.go b/chain/stmgr/stmgr.go index 2d528c91be3..5f0ad33fe5f 100644 --- a/chain/stmgr/stmgr.go +++ b/chain/stmgr/stmgr.go @@ -5,6 +5,7 @@ import ( "fmt" "sync" + lru "github.com/hashicorp/golang-lru/v2" "github.com/ipfs/go-cid" dstore "github.com/ipfs/go-datastore" cbor "github.com/ipfs/go-ipld-cbor" @@ -39,6 +40,8 @@ import ( const LookbackNoLimit = api.LookbackNoLimit const ReceiptAmtBitwidth = 3 +const execTraceCacheSize = 16 + var log = logging.Logger("statemgr") type StateManagerAPI interface { @@ -138,6 +141,10 @@ type StateManager struct { beacon beacon.Schedule msgIndex index.MsgIndex + + // We keep a small cache for calls to ExecutionTrace which helps improve + // performance for node operators like exchanges and block explorers + execTraceCache *lru.ARCCache[*types.TipSet, tipSetCacheEntry] } // Caches a single state tree @@ -146,6 +153,11 @@ type treeCache struct { tree *state.StateTree } +type tipSetCacheEntry struct { + cid cid.Cid + invocTrace []*api.InvocResult +} + func NewStateManager(cs *store.ChainStore, exec Executor, sys vm.SyscallBuilder, us UpgradeSchedule, beacon beacon.Schedule, metadataDs dstore.Batching, msgIndex index.MsgIndex) (*StateManager, error) { // If we have upgrades, make sure they're in-order and make sense. if err := us.Validate(); err != nil { @@ -185,6 +197,11 @@ func NewStateManager(cs *store.ChainStore, exec Executor, sys vm.SyscallBuilder, } } + execTraceCache, err := lru.NewARC[*types.TipSet, tipSetCacheEntry](execTraceCacheSize) + if err != nil { + return nil, err + } + return &StateManager{ networkVersions: networkVersions, latestVersion: lastVersion, @@ -200,8 +217,9 @@ func NewStateManager(cs *store.ChainStore, exec Executor, sys vm.SyscallBuilder, root: cid.Undef, tree: nil, }, - compWait: make(map[string]chan struct{}), - msgIndex: msgIndex, + compWait: make(map[string]chan struct{}), + msgIndex: msgIndex, + execTraceCache: execTraceCache, }, nil } From f84f8a831a5dc11f7881dfb869993e7441aa54f6 Mon Sep 17 00:00:00 2001 From: Fridrik Asmundsson Date: Thu, 23 Mar 2023 13:14:49 +0000 Subject: [PATCH 114/243] Use TipSetKey as key in cache and return copies --- chain/stmgr/execute.go | 27 ++++++++++++++++++++++++--- chain/stmgr/stmgr.go | 9 +++++---- 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/chain/stmgr/execute.go b/chain/stmgr/execute.go index 3c26445508c..76139892830 100644 --- a/chain/stmgr/execute.go +++ b/chain/stmgr/execute.go @@ -128,8 +128,16 @@ func (sm *StateManager) ExecutionTraceWithMonitor(ctx context.Context, ts *types } func (sm *StateManager) ExecutionTrace(ctx context.Context, ts *types.TipSet) (cid.Cid, []*api.InvocResult, error) { - if entry, ok := sm.execTraceCache.Get(ts); ok { - return entry.cid, entry.invocTrace, nil + tsKey := ts.Key() + + { + // check if we have the trace for this tipset in the cache + sm.execTraceCacheLock.Lock() + defer sm.execTraceCacheLock.Unlock() + if entry, ok := sm.execTraceCache.Get(tsKey); ok { + // we have to make a deep copy since caller can modify the invocTrace + return entry.postStateRoot, makeDeepCopy(entry.invocTrace), nil + } } var invocTrace []*api.InvocResult @@ -138,7 +146,20 @@ func (sm *StateManager) ExecutionTrace(ctx context.Context, ts *types.TipSet) (c return cid.Undef, nil, err } - sm.execTraceCache.Add(ts, tipSetCacheEntry{st, invocTrace}) + sm.execTraceCache.Add(tsKey, tipSetCacheEntry{st, makeDeepCopy(invocTrace)}) return st, invocTrace, nil } + +func makeDeepCopy(invocTrace []*api.InvocResult) []*api.InvocResult { + c := make([]*api.InvocResult, len(invocTrace)) + for i, ir := range invocTrace { + if ir == nil { + continue + } + tmp := *ir + c[i] = &tmp + } + + return c +} diff --git a/chain/stmgr/stmgr.go b/chain/stmgr/stmgr.go index 5f0ad33fe5f..b0876215e8f 100644 --- a/chain/stmgr/stmgr.go +++ b/chain/stmgr/stmgr.go @@ -144,7 +144,8 @@ type StateManager struct { // We keep a small cache for calls to ExecutionTrace which helps improve // performance for node operators like exchanges and block explorers - execTraceCache *lru.ARCCache[*types.TipSet, tipSetCacheEntry] + execTraceCache *lru.ARCCache[types.TipSetKey, tipSetCacheEntry] + execTraceCacheLock sync.Mutex } // Caches a single state tree @@ -154,8 +155,8 @@ type treeCache struct { } type tipSetCacheEntry struct { - cid cid.Cid - invocTrace []*api.InvocResult + postStateRoot cid.Cid + invocTrace []*api.InvocResult } func NewStateManager(cs *store.ChainStore, exec Executor, sys vm.SyscallBuilder, us UpgradeSchedule, beacon beacon.Schedule, metadataDs dstore.Batching, msgIndex index.MsgIndex) (*StateManager, error) { @@ -197,7 +198,7 @@ func NewStateManager(cs *store.ChainStore, exec Executor, sys vm.SyscallBuilder, } } - execTraceCache, err := lru.NewARC[*types.TipSet, tipSetCacheEntry](execTraceCacheSize) + execTraceCache, err := lru.NewARC[types.TipSetKey, tipSetCacheEntry](execTraceCacheSize) if err != nil { return nil, err } From 2e45f6f7781c7ad5371bb63fe7db6bf98bd55e5e Mon Sep 17 00:00:00 2001 From: Fridrik Asmundsson Date: Sat, 25 Mar 2023 12:07:37 +0000 Subject: [PATCH 115/243] Dont do locking using defer --- chain/stmgr/execute.go | 23 ++++++++++++++--------- chain/stmgr/stmgr.go | 4 +++- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/chain/stmgr/execute.go b/chain/stmgr/execute.go index 76139892830..8c85a1031bb 100644 --- a/chain/stmgr/execute.go +++ b/chain/stmgr/execute.go @@ -130,15 +130,16 @@ func (sm *StateManager) ExecutionTraceWithMonitor(ctx context.Context, ts *types func (sm *StateManager) ExecutionTrace(ctx context.Context, ts *types.TipSet) (cid.Cid, []*api.InvocResult, error) { tsKey := ts.Key() - { - // check if we have the trace for this tipset in the cache - sm.execTraceCacheLock.Lock() - defer sm.execTraceCacheLock.Unlock() - if entry, ok := sm.execTraceCache.Get(tsKey); ok { - // we have to make a deep copy since caller can modify the invocTrace - return entry.postStateRoot, makeDeepCopy(entry.invocTrace), nil - } + // check if we have the trace for this tipset in the cache + sm.execTraceCacheLock.Lock() + if entry, ok := sm.execTraceCache.Get(tsKey); ok { + // we have to make a deep copy since caller can modify the invocTrace + // and we don't want that to change what we store in cache + invocTraceCopy := makeDeepCopy(entry.invocTrace) + sm.execTraceCacheLock.Unlock() + return entry.postStateRoot, invocTraceCopy, nil } + sm.execTraceCacheLock.Unlock() var invocTrace []*api.InvocResult st, err := sm.ExecutionTraceWithMonitor(ctx, ts, &InvocationTracer{trace: &invocTrace}) @@ -146,7 +147,11 @@ func (sm *StateManager) ExecutionTrace(ctx context.Context, ts *types.TipSet) (c return cid.Undef, nil, err } - sm.execTraceCache.Add(tsKey, tipSetCacheEntry{st, makeDeepCopy(invocTrace)}) + invocTraceCopy := makeDeepCopy(invocTrace) + + sm.execTraceCacheLock.Lock() + sm.execTraceCache.Add(tsKey, tipSetCacheEntry{st, invocTraceCopy}) + sm.execTraceCacheLock.Unlock() return st, invocTrace, nil } diff --git a/chain/stmgr/stmgr.go b/chain/stmgr/stmgr.go index b0876215e8f..bf10665e7d4 100644 --- a/chain/stmgr/stmgr.go +++ b/chain/stmgr/stmgr.go @@ -144,7 +144,9 @@ type StateManager struct { // We keep a small cache for calls to ExecutionTrace which helps improve // performance for node operators like exchanges and block explorers - execTraceCache *lru.ARCCache[types.TipSetKey, tipSetCacheEntry] + execTraceCache *lru.ARCCache[types.TipSetKey, tipSetCacheEntry] + // We need a lock while making the copy as to prevent other callers + // overwrite the cache while making the copy execTraceCacheLock sync.Mutex } From 39b27e709ee8ae4819f309f0f62087b77db594f4 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Tue, 28 Mar 2023 15:05:43 +0200 Subject: [PATCH 116/243] export-range: use debug log instead of warn for not found receipt events --- chain/store/snapshot.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chain/store/snapshot.go b/chain/store/snapshot.go index 6277ea416da..0a878e1e09a 100644 --- a/chain/store/snapshot.go +++ b/chain/store/snapshot.go @@ -425,7 +425,7 @@ func (s *walkScheduler) processTask(t walkTask, workerN int) error { blk, err := s.store.Get(s.ctx, t.c) if errors.Is(err, format.ErrNotFound{}) && t.topLevelTaskType == receiptTask { - log.Warnw("ignoring not-found block in Receipts", + log.Debugw("ignoring not-found block in Receipts", "block", t.blockCid, "epoch", t.epoch, "cid", t.c) From dcd9869842d64fbce4c46d07a608d90f0cbb4baf Mon Sep 17 00:00:00 2001 From: vyzo Date: Tue, 28 Mar 2023 16:58:09 +0300 Subject: [PATCH 117/243] make gen --- chain/vm/execution.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/chain/vm/execution.go b/chain/vm/execution.go index 0edb1388484..1642dc2f8df 100644 --- a/chain/vm/execution.go +++ b/chain/vm/execution.go @@ -7,11 +7,10 @@ import ( "strconv" "sync" + "github.com/ipfs/go-cid" "go.opencensus.io/stats" "go.opencensus.io/tag" - "github.com/ipfs/go-cid" - "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/metrics" ) From 83e2408f819451eb59efdfe6cea15b0aa010d4f6 Mon Sep 17 00:00:00 2001 From: Fridrik Asmundsson Date: Tue, 28 Mar 2023 16:28:47 +0200 Subject: [PATCH 118/243] Only populate message index if config EnableMsgIndex is set --- cmd/lotus/daemon.go | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/cmd/lotus/daemon.go b/cmd/lotus/daemon.go index 0fc96775d13..704d9b470f8 100644 --- a/cmd/lotus/daemon.go +++ b/cmd/lotus/daemon.go @@ -48,6 +48,7 @@ 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" "github.com/filecoin-project/lotus/node/modules" "github.com/filecoin-project/lotus/node/modules/dtypes" "github.com/filecoin-project/lotus/node/modules/testing" @@ -559,11 +560,23 @@ func ImportChain(ctx context.Context, r repo.Repo, fname string, snapshot bool) return err } - log.Info("populating message index...") - if err := index.PopulateAfterSnapshot(ctx, path.Join(lr.Path(), "sqlite"), cst); err != nil { + // populate the message index if user has EnableMsgIndex enabled + // + c, err := lr.Config() + if err != nil { return err } - log.Info("populating message index done") + cfg, ok := c.(*config.FullNode) + if !ok { + return xerrors.Errorf("invalid config for repo, got: %T", c) + } + if cfg.Index.EnableMsgIndex { + log.Info("populating message index...") + if err := index.PopulateAfterSnapshot(ctx, path.Join(lr.Path(), "sqlite"), cst); err != nil { + return err + } + log.Info("populating message index done") + } return nil } From 1a771e4310062ee47b7202ba6fb3fe93747ab061 Mon Sep 17 00:00:00 2001 From: Alfonso de la Rocha Date: Tue, 28 Mar 2023 16:52:32 +0200 Subject: [PATCH 119/243] include a deeper gc round --- chain/sub/bcast/consistent.go | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/chain/sub/bcast/consistent.go b/chain/sub/bcast/consistent.go index 75209431dfd..c8c91e71a64 100644 --- a/chain/sub/bcast/consistent.go +++ b/chain/sub/bcast/consistent.go @@ -19,10 +19,16 @@ var log = logging.Logger("sub-cb") const ( // GcSanityCheck determines the number of epochs in the past // that will be garbage collected from the current epoch. - GcSanityCheck = 5 + GcSanityCheck = 100 // GcLookback determines the number of epochs kept in the consistent // broadcast cache. - GcLookback = 1000 + GcLookback = 5 + // GcDeepCheck determines the number of epochs in the past that we + // we try cleaning in the deep garbage collection round. + GcDeepCheck = 2880 // (24h*60m*60s)/30s per block + // GcDeepInterval determines after the number of epochs for which + // we are going to start a deeper garbage collection round. + GcDeepInterval = 1000 ) type blksInfo struct { @@ -54,9 +60,10 @@ func (bd *bcastDict) store(key []byte, d *blksInfo) { // ConsistentBCast tracks recent information about the // blocks and tickets received at different epochs type ConsistentBCast struct { - lk sync.RWMutex - delay time.Duration - m map[abi.ChainEpoch]*bcastDict + lk sync.RWMutex + delay time.Duration + m map[abi.ChainEpoch]*bcastDict + lastDeepGc abi.ChainEpoch } func newBcastDict() *bcastDict { @@ -160,17 +167,29 @@ func (cb *ConsistentBCast) WaitForDelivery(bh *types.BlockHeader) error { return nil } +// GarbageCollect cleans the consistent broadcast cache periodically. +// +// A light garbage collection is triggered before every block delivery +// while a deeper one is triggered once every GcDeepCheck to ensure +// that nothing was left behind. func (cb *ConsistentBCast) GarbageCollect(currEpoch abi.ChainEpoch) { cb.lk.Lock() defer cb.lk.Unlock() - // keep currEpoch-2 and delete a few more in the past + // perform a deeper sanity check every now and then + gcRange := GcSanityCheck + if cb.lastDeepGc+GcDeepInterval > currEpoch { + gcRange = GcDeepCheck + cb.lastDeepGc = currEpoch + } + + // keep currEpoch-gcRange and delete a few more in the past // as a sanity-check // Garbage collection is triggered before block delivery, // and we use the sanity-check in case there were a few rounds // without delivery, and the garbage collection wasn't triggered // for a few epochs. - for i := 0; i < GcSanityCheck; i++ { + for i := 0; i < gcRange; i++ { if currEpoch > GcLookback { delete(cb.m, currEpoch-abi.ChainEpoch(GcLookback+i)) } From b2b78e9dfa110186ae868b953d60cbc7015022ba Mon Sep 17 00:00:00 2001 From: vyzo Date: Tue, 28 Mar 2023 17:56:35 +0300 Subject: [PATCH 120/243] Update chain/vm/execution.go Co-authored-by: Aayush Rajasekaran --- chain/vm/execution.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chain/vm/execution.go b/chain/vm/execution.go index 1642dc2f8df..58f61ca6d9d 100644 --- a/chain/vm/execution.go +++ b/chain/vm/execution.go @@ -210,7 +210,7 @@ func init() { panic("insufficient execution concurrency") } - if priority > available-1 { + if available <= priority { panic("insufficient default execution concurrency") } From b27121612e1b679a1b558ed51bf297ea9ccd65e7 Mon Sep 17 00:00:00 2001 From: vyzo Date: Tue, 28 Mar 2023 18:03:55 +0300 Subject: [PATCH 121/243] rename confusing variable --- chain/consensus/compute_state.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/chain/consensus/compute_state.go b/chain/consensus/compute_state.go index 3c1bab9ca78..449a0ed207c 100644 --- a/chain/consensus/compute_state.go +++ b/chain/consensus/compute_state.go @@ -264,7 +264,7 @@ func (t *TipSetExecutor) ApplyBlocks(ctx context.Context, return cid.Cid{}, cid.Cid{}, err } - vmCron := partDone() + vmDoCron := partDone() partDone = metrics.Timer(ctx, metrics.VMApplyFlush) rectarr := blockadt.MakeEmptyArray(sm.ChainStore().ActorStore(ctx)) @@ -303,7 +303,7 @@ func (t *TipSetExecutor) ApplyBlocks(ctx context.Context, vmFlush := partDone() partDone = func() time.Duration { return time.Duration(0) } - log.Infow("ApplyBlocks stats", "early", vmEarly, "earlyCronGas", earlyCronGas, "vmMsg", vmMsg, "msgGas", msgGas, "vmCron", vmCron, "cronGas", cronGas, "vmFlush", vmFlush, "epoch", epoch, "tsk", ts.Key()) + log.Infow("ApplyBlocks stats", "early", vmEarly, "earlyCronGas", earlyCronGas, "vmMsg", vmMsg, "msgGas", msgGas, "vmCron", vmDoCron, "cronGas", cronGas, "vmFlush", vmFlush, "epoch", epoch, "tsk", ts.Key()) stats.Record(ctx, metrics.VMSends.M(int64(atomic.LoadUint64(&vm.StatSends))), metrics.VMApplied.M(int64(atomic.LoadUint64(&vm.StatApplied)))) From 71650cd8a4907deac2f1089d7354b17e91cc2a48 Mon Sep 17 00:00:00 2001 From: vyzo Date: Tue, 28 Mar 2023 18:05:00 +0300 Subject: [PATCH 122/243] rename newVM to makeVM for a happy yushie --- chain/vm/vmi.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/chain/vm/vmi.go b/chain/vm/vmi.go index 7aa52b58534..a19c38fceab 100644 --- a/chain/vm/vmi.go +++ b/chain/vm/vmi.go @@ -54,7 +54,7 @@ type Executor interface { // Message failures, unexpected terminations,gas costs, etc. should all be ignored. var useFvmDebug = os.Getenv("LOTUS_FVM_DEVELOPER_DEBUG") == "1" -func newVM(ctx context.Context, opts *VMOpts) (Interface, error) { +func makeVM(ctx context.Context, opts *VMOpts) (Interface, error) { if opts.NetworkVersion >= network.Version16 { if useFvmDebug { return NewDualExecutionFVM(ctx, opts) @@ -74,7 +74,7 @@ func NewVM(ctx context.Context, opts *VMOpts) (Executor, error) { token := execution.getToken(opts.ExecutionLane) - vmi, err := newVM(ctx, opts) + vmi, err := makeVM(ctx, opts) if err != nil { token.Done() return nil, err From ecd13079e7347ee9bf59fb186af675674db51d3f Mon Sep 17 00:00:00 2001 From: Fridrik Asmundsson Date: Tue, 28 Mar 2023 17:08:53 +0200 Subject: [PATCH 123/243] Address review comments --- chain/index/msgindex.go | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/chain/index/msgindex.go b/chain/index/msgindex.go index 42a536c160c..39ba487f2ef 100644 --- a/chain/index/msgindex.go +++ b/chain/index/msgindex.go @@ -211,13 +211,9 @@ func PopulateAfterSnapshot(lctx context.Context, basePath string, cs ChainStore) insertStmt, err := tx.Prepare(dbqInsertMessage) if err != nil { + rollback() return xerrors.Errorf("error preparing insertStmt: %w", err) } - defer func() { - if err := insertStmt.Close(); err != nil { - log.Errorf("error closing insert statement: %s", err) - } - }() curTs := cs.GetHeaviestTipSet() startHeight := curTs.Height() From f24fc836b3cec75eb674590f5e6457c770f2af0d Mon Sep 17 00:00:00 2001 From: Alfonso de la Rocha Date: Tue, 28 Mar 2023 18:22:28 +0200 Subject: [PATCH 124/243] add CB param to all testnet builds --- build/params_butterfly.go | 6 ++++++ build/params_calibnet.go | 5 +++++ build/params_interop.go | 5 +++++ build/params_mainnet.go | 2 +- 4 files changed, 17 insertions(+), 1 deletion(-) diff --git a/build/params_butterfly.go b/build/params_butterfly.go index b00381b444b..137ab7dee97 100644 --- a/build/params_butterfly.go +++ b/build/params_butterfly.go @@ -4,6 +4,8 @@ package build import ( + "time" + "github.com/ipfs/go-cid" "github.com/filecoin-project/go-address" @@ -87,3 +89,7 @@ const BootstrapPeerThreshold = 2 const Eip155ChainId = 3141592 var WhitelistedBlock = cid.Undef + +// CBDeliveryDelay is the delay before deliver in the synchronous consistent broadcast. +// This determines the wait time for the detection of potential equivocations. +const CBDeliveryDelay = 2 * time.Second diff --git a/build/params_calibnet.go b/build/params_calibnet.go index 32923f7a869..7bfca2a4252 100644 --- a/build/params_calibnet.go +++ b/build/params_calibnet.go @@ -6,6 +6,7 @@ package build import ( "os" "strconv" + "time" "github.com/ipfs/go-cid" @@ -122,3 +123,7 @@ const BootstrapPeerThreshold = 4 const Eip155ChainId = 314159 var WhitelistedBlock = cid.Undef + +// CBDeliveryDelay is the delay before deliver in the synchronous consistent broadcast. +// This determines the wait time for the detection of potential equivocations. +const CBDeliveryDelay = 2 * time.Second diff --git a/build/params_interop.go b/build/params_interop.go index 4d94de049c0..0fb86524845 100644 --- a/build/params_interop.go +++ b/build/params_interop.go @@ -6,6 +6,7 @@ package build import ( "os" "strconv" + "time" "github.com/ipfs/go-cid" @@ -128,3 +129,7 @@ const BootstrapPeerThreshold = 2 const Eip155ChainId = 3141592 var WhitelistedBlock = cid.Undef + +// CBDeliveryDelay is the delay before deliver in the synchronous consistent broadcast. +// This determines the wait time for the detection of potential equivocations. +const CBDeliveryDelay = 2 * time.Second diff --git a/build/params_mainnet.go b/build/params_mainnet.go index 5fe4a620255..bb205a827ce 100644 --- a/build/params_mainnet.go +++ b/build/params_mainnet.go @@ -141,4 +141,4 @@ var WhitelistedBlock = MustParseCid("bafy2bzaceapyg2uyzk7vueh3xccxkuwbz3nxewjygu // CBDeliveryDelay is the delay before deliver in the synchronous consistent broadcast. // This determines the wait time for the detection of potential equivocations. -var CBDeliveryDelay = 2 * time.Second +const CBDeliveryDelay = 2 * time.Second From 4b4e7f81a4a0f8fd28bb9eba54346f8e156f6c36 Mon Sep 17 00:00:00 2001 From: Phi Date: Tue, 28 Mar 2023 21:33:44 +0200 Subject: [PATCH 125/243] build: docker: Update GO-version build: docker: Update GO-version --- Dockerfile | 2 +- Dockerfile.lotus | 2 +- README.md | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Dockerfile b/Dockerfile index 81089517b5c..dfdfedce328 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ ##################################### -FROM golang:1.18.8-buster AS lotus-builder +FROM golang:1.19.7-buster 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/Dockerfile.lotus b/Dockerfile.lotus index 2278e8511f2..91373b62f8d 100644 --- a/Dockerfile.lotus +++ b/Dockerfile.lotus @@ -1,6 +1,6 @@ ##### DEPRECATED -FROM golang:1.18.8-buster AS builder-deps +FROM golang:1.19.7-buster AS builder-deps 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/README.md b/README.md index 76cac2c7eeb..b67cb952f18 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.18.8 or higher](https://golang.org/dl/): +To build Lotus, you need a working installation of [Go 1.19.7 or higher](https://golang.org/dl/): ```bash -wget -c https://golang.org/dl/go1.18.8.linux-amd64.tar.gz -O - | sudo tar -xz -C /usr/local +wget -c https://golang.org/dl/go1.19.7.linux-amd64.tar.gz -O - | sudo tar -xz -C /usr/local ``` **TIP:** From 2fa95a09be11deeeadb7fdd5401814178c6d9307 Mon Sep 17 00:00:00 2001 From: Mike Seiler Date: Tue, 28 Mar 2023 11:14:13 -1000 Subject: [PATCH 126/243] clean up cache logic in addTs / fix a bug where the loop was incorrectly releasing the lock 2x --- chain/messagepool/messagepool.go | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/chain/messagepool/messagepool.go b/chain/messagepool/messagepool.go index 3af9b1572c6..fbffd912fa5 100644 --- a/chain/messagepool/messagepool.go +++ b/chain/messagepool/messagepool.go @@ -763,28 +763,26 @@ func (mp *MessagePool) Add(ctx context.Context, m *types.SignedMessage) error { <-mp.addSema }() - //Ensure block calculation is cached without holding the write lock mp.curTsLk.RLock() tmpCurTs := mp.curTs mp.curTsLk.RUnlock() + + //ensures computations are cached without holding lack _, _ = mp.api.GetActorAfter(m.Message.From, tmpCurTs) _, _ = mp.getStateNonce(ctx, m.Message.From, tmpCurTs) - cacheSecondTime := true - //if the newly acquired Ts is not the one we just cached, let go of the lock, cache it and open the lock again and repeat.... - for cacheSecondTime { - mp.curTsLk.Lock() - writeCurTs := mp.curTs - - if writeCurTs == tmpCurTs { - break // we have this cached we can skip - } + mp.curTsLk.Lock() + if tmpCurTs == mp.curTs { + //with the lock enabled, mp.curTs is the same Ts as we just had, so we know that our computations are cached + } else { + //curTs has been updated so we want to cache the new one: + tmpCurTs = mp.curTs + //we want to release the lock, cache the computations then grab it again mp.curTsLk.Unlock() - tmpCurTs = writeCurTs _, _ = mp.api.GetActorAfter(m.Message.From, tmpCurTs) _, _ = mp.getStateNonce(ctx, m.Message.From, tmpCurTs) - cacheSecondTime = false - + mp.curTsLk.Lock() + //now that we have the lock, we continue, we could do this as a loop forever, but that's bad to loop forever, and this was added as an optimization and it seems once is enough because the computation < block time } defer mp.curTsLk.Unlock() From 89b217ee210218aaf2abf16fd8f95fc91871e455 Mon Sep 17 00:00:00 2001 From: Mike Seiler Date: Tue, 28 Mar 2023 12:35:09 -1000 Subject: [PATCH 127/243] move write lock to before verifyMsg --- chain/messagepool/messagepool.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/chain/messagepool/messagepool.go b/chain/messagepool/messagepool.go index fbffd912fa5..1d8478a682d 100644 --- a/chain/messagepool/messagepool.go +++ b/chain/messagepool/messagepool.go @@ -878,9 +878,6 @@ func (mp *MessagePool) addTs(ctx context.Context, m *types.SignedMessage, curTs return false, xerrors.Errorf("failed to get sender actor: %w", err) } - mp.lk.Lock() - defer mp.lk.Unlock() - // This message can only be included in the _next_ epoch and beyond, hence the +1. epoch := curTs.Height() + 1 nv := mp.api.StateNetworkVersion(ctx, epoch) @@ -890,6 +887,9 @@ func (mp *MessagePool) addTs(ctx context.Context, m *types.SignedMessage, curTs return false, xerrors.Errorf("sender actor %s is not a valid top-level sender", m.Message.From) } + mp.lk.Lock() + defer mp.lk.Unlock() + publish, err := mp.verifyMsgBeforeAdd(ctx, m, curTs, local) if err != nil { return false, xerrors.Errorf("verify msg failed: %w", err) From 3477f7ce57fdb3a7367666cda238bccc44cc6940 Mon Sep 17 00:00:00 2001 From: Mike Seiler Date: Tue, 28 Mar 2023 13:14:33 -1000 Subject: [PATCH 128/243] use LRU cache for keyCache to make threadsafe, also have (high) upper bounds on size of cache --- chain/messagepool/messagepool.go | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/chain/messagepool/messagepool.go b/chain/messagepool/messagepool.go index 1d8478a682d..4b003b9316d 100644 --- a/chain/messagepool/messagepool.go +++ b/chain/messagepool/messagepool.go @@ -137,7 +137,7 @@ type MessagePool struct { // do NOT access this map directly, use getPendingMset, setPendingMset, deletePendingMset, forEachPending, and clearPending respectively pending map[address.Address]*msgSet - keyCache map[address.Address]address.Address + keyCache *lru.Cache[address.Address, address.Address] curTsLk sync.RWMutex // DO NOT LOCK INSIDE lk curTs *types.TipSet @@ -372,6 +372,7 @@ func New(ctx context.Context, api Provider, ds dtypes.MetadataDS, us stmgr.Upgra cache, _ := lru.New2Q[cid.Cid, crypto.Signature](build.BlsSignatureCacheSize) verifcache, _ := lru.New2Q[string, struct{}](build.VerifSigCacheSize) noncecache, _ := lru.New[nonceCacheKey, uint64](256) + keycache, _ := lru.New[address.Address, address.Address](1_000_000) cfg, err := loadConfig(ctx, ds) if err != nil { @@ -390,7 +391,7 @@ func New(ctx context.Context, api Provider, ds dtypes.MetadataDS, us stmgr.Upgra repubTrigger: make(chan struct{}, 1), localAddrs: make(map[address.Address]struct{}), pending: make(map[address.Address]*msgSet), - keyCache: make(map[address.Address]address.Address), + keyCache: keycache, minGasPrice: types.NewInt(0), getNtwkVersion: us.GetNtwkVersion, pruneTrigger: make(chan struct{}, 1), @@ -474,8 +475,8 @@ func (mp *MessagePool) TryForEachPendingMessage(f func(cid.Cid) error) error { func (mp *MessagePool) resolveToKey(ctx context.Context, addr address.Address) (address.Address, error) { // check the cache - a, f := mp.keyCache[addr] - if f { + a, ok := mp.keyCache.Get(addr) + if ok { return a, nil } @@ -486,8 +487,8 @@ func (mp *MessagePool) resolveToKey(ctx context.Context, addr address.Address) ( } // place both entries in the cache (may both be key addresses, which is fine) - mp.keyCache[addr] = ka - mp.keyCache[ka] = ka + mp.keyCache.Add(addr, ka) + mp.keyCache.Add(ka, ka) return ka, nil } From 8aa491774118ba4e788b88e9a535303d9dc61f2d Mon Sep 17 00:00:00 2001 From: Fridrik Asmundsson Date: Wed, 29 Mar 2023 13:18:26 +0200 Subject: [PATCH 129/243] Fix bug in searchForIndexedMsg which always returns an error --- chain/stmgr/searchwait.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/chain/stmgr/searchwait.go b/chain/stmgr/searchwait.go index 6b9adff714b..ac6b95ad537 100644 --- a/chain/stmgr/searchwait.go +++ b/chain/stmgr/searchwait.go @@ -216,7 +216,10 @@ func (sm *StateManager) searchForIndexedMsg(ctx context.Context, mcid cid.Cid, m } r, foundMsg, err := sm.tipsetExecutedMessage(ctx, xts, mcid, m.VMMessage(), false) - return xts, r, foundMsg, xerrors.Errorf("error in tipstExecutedMessage: %w", err) + if err != nil { + return nil, nil, cid.Undef, xerrors.Errorf("error in tipstExecutedMessage: %w", err) + } + return xts, r, foundMsg, nil } // searchBackForMsg searches up to limit tipsets backwards from the given From f44fa5d96f9ff6199476e45db0e091f91141413e Mon Sep 17 00:00:00 2001 From: Fridrik Asmundsson Date: Wed, 29 Mar 2023 13:15:42 +0200 Subject: [PATCH 130/243] Use MessageIndex in WaitForMessage --- chain/stmgr/searchwait.go | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/chain/stmgr/searchwait.go b/chain/stmgr/searchwait.go index 6b9adff714b..34ecc8fae13 100644 --- a/chain/stmgr/searchwait.go +++ b/chain/stmgr/searchwait.go @@ -57,10 +57,15 @@ func (sm *StateManager) WaitForMessage(ctx context.Context, mcid cid.Cid, confid var backFm cid.Cid backSearchWait := make(chan struct{}) go func() { - fts, r, foundMsg, err := sm.searchBackForMsg(ctx, head[0].Val, msg, lookbackLimit, allowReplaced) - if err != nil { - log.Warnf("failed to look back through chain for message: %v", err) - return + fts, r, foundMsg, err := sm.searchForIndexedMsg(ctx, mcid, msg) + + found := (err == nil && r != nil && foundMsg.Defined()) + if !found { + fts, r, foundMsg, err = sm.searchBackForMsg(ctx, head[0].Val, msg, lookbackLimit, allowReplaced) + if err != nil { + log.Warnf("failed to look back through chain for message: %v", err) + return + } } backTs = fts From a99946937c3ae82c9b3b89baeb5f883abad92eae Mon Sep 17 00:00:00 2001 From: Phi Date: Wed, 29 Mar 2023 15:32:32 +0200 Subject: [PATCH 131/243] fix: log: Stop logging `file does not exists` Stop logging `file does not exists` errors when retrieving disk usage information. --- storage/paths/localstorage_cached.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/storage/paths/localstorage_cached.go b/storage/paths/localstorage_cached.go index af43d16967b..31e2528ffc0 100644 --- a/storage/paths/localstorage_cached.go +++ b/storage/paths/localstorage_cached.go @@ -1,6 +1,7 @@ package paths import ( + "os" "sync" "time" @@ -103,7 +104,9 @@ func (c *cachedLocalStorage) DiskUsage(path string) (int64, error) { go func() { du, err := c.base.DiskUsage(path) if err != nil { - log.Errorw("error getting disk usage", "path", path, "error", err) + if !os.IsNotExist(err) { + log.Errorw("error getting disk usage", "path", path, "error", err) + } } resCh <- diskUsageResult{ usage: du, From 4184ce9c7571a68821ccf74a7dd7ce329801df97 Mon Sep 17 00:00:00 2001 From: vyzo Date: Wed, 29 Mar 2023 16:45:45 +0300 Subject: [PATCH 132/243] refactor execution lanes: hide the lock --- chain/consensus/compute_state.go | 9 ++----- chain/gen/genesis/genesis.go | 1 - chain/gen/genesis/miners.go | 7 +---- chain/stmgr/call.go | 3 --- chain/stmgr/stmgr.go | 8 +++--- chain/stmgr/utils.go | 1 - chain/vm/execution.go | 46 +++++++------------------------- chain/vm/vmi.go | 18 ++----------- 8 files changed, 18 insertions(+), 75 deletions(-) diff --git a/chain/consensus/compute_state.go b/chain/consensus/compute_state.go index 449a0ed207c..8442b55e9a7 100644 --- a/chain/consensus/compute_state.go +++ b/chain/consensus/compute_state.go @@ -93,7 +93,7 @@ func (t *TipSetExecutor) ApplyBlocks(ctx context.Context, }() ctx = blockstore.WithHotView(ctx) - makeVm := func(base cid.Cid, e abi.ChainEpoch, timestamp uint64) (vm.Executor, error) { + makeVm := func(base cid.Cid, e abi.ChainEpoch, timestamp uint64) (vm.Interface, error) { vmopt := &vm.VMOpts{ StateBase: base, Epoch: e, @@ -117,7 +117,7 @@ func (t *TipSetExecutor) ApplyBlocks(ctx context.Context, var cronGas int64 - runCron := func(vmCron vm.Executor, epoch abi.ChainEpoch) error { + runCron := func(vmCron vm.Interface, epoch abi.ChainEpoch) error { cronMsg := &types.Message{ To: cron.Address, From: builtin.SystemActorAddr, @@ -170,17 +170,13 @@ func (t *TipSetExecutor) ApplyBlocks(ctx context.Context, // run cron for null rounds if any if err = runCron(vmCron, i); err != nil { - vmCron.Done() return cid.Undef, cid.Undef, xerrors.Errorf("running cron: %w", err) } pstate, err = vmCron.Flush(ctx) if err != nil { - vmCron.Done() return cid.Undef, cid.Undef, xerrors.Errorf("flushing cron vm: %w", err) } - - vmCron.Done() } // handle state forks @@ -201,7 +197,6 @@ func (t *TipSetExecutor) ApplyBlocks(ctx context.Context, if err != nil { return cid.Undef, cid.Undef, xerrors.Errorf("making vm: %w", err) } - defer vmi.Done() var ( receipts []*types.MessageReceipt diff --git a/chain/gen/genesis/genesis.go b/chain/gen/genesis/genesis.go index 3ef8de968ec..3e88480218e 100644 --- a/chain/gen/genesis/genesis.go +++ b/chain/gen/genesis/genesis.go @@ -496,7 +496,6 @@ func VerifyPreSealedData(ctx context.Context, cs *store.ChainStore, sys vm.Sysca if err != nil { return cid.Undef, xerrors.Errorf("failed to create VM: %w", err) } - defer vm.Done() for mi, m := range template.Miners { for si, s := range m.Sectors { diff --git a/chain/gen/genesis/miners.go b/chain/gen/genesis/miners.go index 09b46a6e76c..5f741fd7c58 100644 --- a/chain/gen/genesis/miners.go +++ b/chain/gen/genesis/miners.go @@ -88,7 +88,7 @@ func SetupStorageMiners(ctx context.Context, cs *store.ChainStore, sys vm.Syscal return big.Zero(), nil } - newVM := func(base cid.Cid) (vm.Executor, error) { + newVM := func(base cid.Cid) (vm.Interface, error) { vmopt := &vm.VMOpts{ StateBase: base, Epoch: 0, @@ -108,8 +108,6 @@ func SetupStorageMiners(ctx context.Context, cs *store.ChainStore, sys vm.Syscal if err != nil { return cid.Undef, fmt.Errorf("creating vm: %w", err) } - // Note: genesisVm is mutated, so this has to happen in a deferred func; go horror show. - defer func() { genesisVm.Done() }() if len(miners) == 0 { return cid.Undef, xerrors.New("no genesis miners") @@ -340,7 +338,6 @@ func SetupStorageMiners(ctx context.Context, cs *store.ChainStore, sys vm.Syscal return cid.Undef, xerrors.Errorf("flushing state tree: %w", err) } - genesisVm.Done() genesisVm, err = newVM(nh) if err != nil { return cid.Undef, fmt.Errorf("creating new vm: %w", err) @@ -413,7 +410,6 @@ func SetupStorageMiners(ctx context.Context, cs *store.ChainStore, sys vm.Syscal return cid.Undef, xerrors.Errorf("flushing state tree: %w", err) } - genesisVm.Done() genesisVm, err = newVM(nh) if err != nil { return cid.Undef, fmt.Errorf("creating new vm: %w", err) @@ -521,7 +517,6 @@ func SetupStorageMiners(ctx context.Context, cs *store.ChainStore, sys vm.Syscal return cid.Undef, xerrors.Errorf("flushing state tree: %w", err) } - genesisVm.Done() genesisVm, err = newVM(nh) if err != nil { return cid.Undef, fmt.Errorf("creating new vm: %w", err) diff --git a/chain/stmgr/call.go b/chain/stmgr/call.go index 8e18c25df96..816f50d1036 100644 --- a/chain/stmgr/call.go +++ b/chain/stmgr/call.go @@ -159,8 +159,6 @@ func (sm *StateManager) callInternal(ctx context.Context, msg *types.Message, pr if err != nil { return nil, xerrors.Errorf("failed to set up vm: %w", err) } - // Note: vmi is mutated, so this has to happen in a deferred func; go horror show. - defer func() { vmi.Done() }() for i, m := range priorMsgs { _, err = vmi.ApplyMessage(ctx, m) @@ -194,7 +192,6 @@ func (sm *StateManager) callInternal(ctx context.Context, msg *types.Message, pr vmopt.BaseFee = big.Zero() vmopt.StateBase = stateCid - vmi.Done() vmi, err = sm.newVM(ctx, vmopt) if err != nil { return nil, xerrors.Errorf("failed to set up estimation vm: %w", err) diff --git a/chain/stmgr/stmgr.go b/chain/stmgr/stmgr.go index 5f201cf3230..827aeeee571 100644 --- a/chain/stmgr/stmgr.go +++ b/chain/stmgr/stmgr.go @@ -125,7 +125,7 @@ type StateManager struct { compWait map[string]chan struct{} stlk sync.Mutex genesisMsigLk sync.Mutex - newVM func(context.Context, *vm.VMOpts) (vm.Executor, error) + newVM func(context.Context, *vm.VMOpts) (vm.Interface, error) Syscalls vm.SyscallBuilder preIgnitionVesting []msig0.State postIgnitionVesting []msig0.State @@ -439,12 +439,12 @@ func (sm *StateManager) ValidateChain(ctx context.Context, ts *types.TipSet) err return nil } -func (sm *StateManager) SetVMConstructor(nvm func(context.Context, *vm.VMOpts) (vm.Executor, error)) { +func (sm *StateManager) SetVMConstructor(nvm func(context.Context, *vm.VMOpts) (vm.Interface, error)) { sm.newVM = nvm } -func (sm *StateManager) VMConstructor() func(context.Context, *vm.VMOpts) (vm.Executor, error) { - return func(ctx context.Context, opts *vm.VMOpts) (vm.Executor, error) { +func (sm *StateManager) VMConstructor() func(context.Context, *vm.VMOpts) (vm.Interface, error) { + return func(ctx context.Context, opts *vm.VMOpts) (vm.Interface, error) { return sm.newVM(ctx, opts) } } diff --git a/chain/stmgr/utils.go b/chain/stmgr/utils.go index 78129cb164d..c93267d50f8 100644 --- a/chain/stmgr/utils.go +++ b/chain/stmgr/utils.go @@ -106,7 +106,6 @@ func ComputeState(ctx context.Context, sm *StateManager, height abi.ChainEpoch, if err != nil { return cid.Undef, nil, err } - defer vmi.Done() for i, msg := range msgs { // TODO: Use the signed message length for secp messages diff --git a/chain/vm/execution.go b/chain/vm/execution.go index 58f61ca6d9d..fb86a3a7d5e 100644 --- a/chain/vm/execution.go +++ b/chain/vm/execution.go @@ -33,62 +33,34 @@ var execution *executionEnv // implementation of vm executor with simple sanity check preventing use after free. type vmExecutor struct { - lk sync.RWMutex - vmi Interface - token *executionToken - done bool + vmi Interface + lane ExecutionLane } -var _ Executor = (*vmExecutor)(nil) +var _ Interface = (*vmExecutor)(nil) -func newVMExecutor(vmi Interface, token *executionToken) Executor { - return &vmExecutor{vmi: vmi, token: token} +func newVMExecutor(vmi Interface, lane ExecutionLane) Interface { + return &vmExecutor{vmi: vmi, lane: lane} } func (e *vmExecutor) ApplyMessage(ctx context.Context, cmsg types.ChainMsg) (*ApplyRet, error) { - e.lk.RLock() - defer e.lk.RUnlock() - - if e.done { - return nil, ErrExecutorDone - } + token := execution.getToken(e.lane) + defer token.Done() return e.vmi.ApplyMessage(ctx, cmsg) } func (e *vmExecutor) ApplyImplicitMessage(ctx context.Context, msg *types.Message) (*ApplyRet, error) { - e.lk.RLock() - defer e.lk.RUnlock() - - if e.done { - return nil, ErrExecutorDone - } + token := execution.getToken(e.lane) + defer token.Done() return e.vmi.ApplyImplicitMessage(ctx, msg) } func (e *vmExecutor) Flush(ctx context.Context) (cid.Cid, error) { - e.lk.RLock() - defer e.lk.RUnlock() - - if e.done { - return cid.Undef, ErrExecutorDone - } - return e.vmi.Flush(ctx) } -func (e *vmExecutor) Done() { - e.lk.Lock() - defer e.lk.Unlock() - - if !e.done { - e.token.Done() - e.token = nil - e.done = true - } -} - type executionToken struct { lane ExecutionLane reserved int diff --git a/chain/vm/vmi.go b/chain/vm/vmi.go index a19c38fceab..042621ca2d4 100644 --- a/chain/vm/vmi.go +++ b/chain/vm/vmi.go @@ -37,17 +37,6 @@ type Interface interface { Flush(ctx context.Context) (cid.Cid, error) } -// Executor is the general vm execution interface, which is prioritized according to execution lanes. -// User must call Done when it is done with this executor to release resource holds by the execution -// environment -type Executor interface { - Interface - - // Done must be called when done with the executor to release resource holds. - // It is an error to invoke Interface methods after Done has been called. - Done() -} - // WARNING: You will not affect your node's execution by misusing this feature, but you will confuse yourself thoroughly! // An envvar that allows the user to specify debug actors bundles to be used by the FVM // alongside regular execution. This is basically only to be used to print out specific logging information. @@ -65,20 +54,17 @@ func makeVM(ctx context.Context, opts *VMOpts) (Interface, error) { return NewLegacyVM(ctx, opts) } -func NewVM(ctx context.Context, opts *VMOpts) (Executor, error) { +func NewVM(ctx context.Context, opts *VMOpts) (Interface, error) { switch opts.ExecutionLane { case ExecutionLaneDefault, ExecutionLanePriority: default: return nil, fmt.Errorf("invalid execution lane: %d", opts.ExecutionLane) } - token := execution.getToken(opts.ExecutionLane) - vmi, err := makeVM(ctx, opts) if err != nil { - token.Done() return nil, err } - return newVMExecutor(vmi, token), nil + return newVMExecutor(vmi, opts.ExecutionLane), nil } From 52d70d563af0849771ddf4708aa2748e95680e93 Mon Sep 17 00:00:00 2001 From: vyzo Date: Wed, 29 Mar 2023 16:46:37 +0300 Subject: [PATCH 133/243] fix tests --- chain/stmgr/forks_test.go | 30 ++++++++++++------------------ conformance/driver.go | 2 +- 2 files changed, 13 insertions(+), 19 deletions(-) diff --git a/chain/stmgr/forks_test.go b/chain/stmgr/forks_test.go index d852e2fdd2a..f91d8997d6c 100644 --- a/chain/stmgr/forks_test.go +++ b/chain/stmgr/forks_test.go @@ -56,12 +56,6 @@ const testForkHeight = 40 type testActor struct { } -type mockExecutor struct { - vm.Interface -} - -func (*mockExecutor) Done() {} - // must use existing actor that an account is allowed to exec. func (testActor) Code() cid.Cid { return builtin0.PaymentChannelActorCodeID } func (testActor) State() cbor.Er { return new(testActorState) } @@ -184,13 +178,13 @@ func TestForkHeightTriggers(t *testing.T) { registry := builtin.MakeRegistryLegacy([]rtt.VMActor{testActor{}}) inv.Register(actorstypes.Version0, nil, registry) - sm.SetVMConstructor(func(ctx context.Context, vmopt *vm.VMOpts) (vm.Executor, error) { + sm.SetVMConstructor(func(ctx context.Context, vmopt *vm.VMOpts) (vm.Interface, error) { nvm, err := vm.NewLegacyVM(ctx, vmopt) if err != nil { return nil, err } nvm.SetInvoker(inv) - return &mockExecutor{nvm}, nil + return nvm, nil }) cg.SetStateManager(sm) @@ -302,13 +296,13 @@ func testForkRefuseCall(t *testing.T, nullsBefore, nullsAfter int) { registry := builtin.MakeRegistryLegacy([]rtt.VMActor{testActor{}}) inv.Register(actorstypes.Version0, nil, registry) - sm.SetVMConstructor(func(ctx context.Context, vmopt *vm.VMOpts) (vm.Executor, error) { + sm.SetVMConstructor(func(ctx context.Context, vmopt *vm.VMOpts) (vm.Interface, error) { nvm, err := vm.NewLegacyVM(ctx, vmopt) if err != nil { return nil, err } nvm.SetInvoker(inv) - return &mockExecutor{nvm}, nil + return nvm, nil }) cg.SetStateManager(sm) @@ -524,13 +518,13 @@ func TestForkPreMigration(t *testing.T) { registry := builtin.MakeRegistryLegacy([]rtt.VMActor{testActor{}}) inv.Register(actorstypes.Version0, nil, registry) - sm.SetVMConstructor(func(ctx context.Context, vmopt *vm.VMOpts) (vm.Executor, error) { + sm.SetVMConstructor(func(ctx context.Context, vmopt *vm.VMOpts) (vm.Interface, error) { nvm, err := vm.NewLegacyVM(ctx, vmopt) if err != nil { return nil, err } nvm.SetInvoker(inv) - return &mockExecutor{nvm}, nil + return nvm, nil }) cg.SetStateManager(sm) @@ -598,11 +592,11 @@ func TestDisablePreMigration(t *testing.T) { registry := builtin.MakeRegistryLegacy([]rtt.VMActor{testActor{}}) inv.Register(actorstypes.Version0, nil, registry) - sm.SetVMConstructor(func(ctx context.Context, vmopt *vm.VMOpts) (vm.Executor, error) { + sm.SetVMConstructor(func(ctx context.Context, vmopt *vm.VMOpts) (vm.Interface, error) { nvm, err := vm.NewLegacyVM(ctx, vmopt) require.NoError(t, err) nvm.SetInvoker(inv) - return &mockExecutor{nvm}, nil + return nvm, nil }) cg.SetStateManager(sm) @@ -653,11 +647,11 @@ func TestMigrtionCache(t *testing.T) { registry := builtin.MakeRegistryLegacy([]rtt.VMActor{testActor{}}) inv.Register(actorstypes.Version0, nil, registry) - sm.SetVMConstructor(func(ctx context.Context, vmopt *vm.VMOpts) (vm.Executor, error) { + sm.SetVMConstructor(func(ctx context.Context, vmopt *vm.VMOpts) (vm.Interface, error) { nvm, err := vm.NewLegacyVM(ctx, vmopt) require.NoError(t, err) nvm.SetInvoker(inv) - return &mockExecutor{nvm}, nil + return nvm, nil }) cg.SetStateManager(sm) @@ -697,11 +691,11 @@ func TestMigrtionCache(t *testing.T) { index.DummyMsgIndex, ) require.NoError(t, err) - sm.SetVMConstructor(func(ctx context.Context, vmopt *vm.VMOpts) (vm.Executor, error) { + sm.SetVMConstructor(func(ctx context.Context, vmopt *vm.VMOpts) (vm.Interface, error) { nvm, err := vm.NewLegacyVM(ctx, vmopt) require.NoError(t, err) nvm.SetInvoker(inv) - return &mockExecutor{nvm}, nil + return nvm, nil }) ctx := context.Background() diff --git a/conformance/driver.go b/conformance/driver.go index c3041be7136..e0d56d07410 100644 --- a/conformance/driver.go +++ b/conformance/driver.go @@ -158,7 +158,7 @@ func (d *Driver) ExecuteTipset(bs blockstore.Blockstore, ds ds.Batching, params results: []*vm.ApplyRet{}, } - sm.SetVMConstructor(func(ctx context.Context, vmopt *vm.VMOpts) (vm.Executor, error) { + sm.SetVMConstructor(func(ctx context.Context, vmopt *vm.VMOpts) (vm.Interface, error) { vmopt.CircSupplyCalc = func(context.Context, abi.ChainEpoch, *state.StateTree) (abi.TokenAmount, error) { return big.Zero(), nil } From 103d786c720afe125b1063c552cd5533d1c39919 Mon Sep 17 00:00:00 2001 From: Alfonso de la Rocha Date: Wed, 29 Mar 2023 17:43:10 +0200 Subject: [PATCH 134/243] return CBDeliveryDelay into a var --- build/params_2k.go | 2 +- build/params_butterfly.go | 3 ++- build/params_calibnet.go | 3 ++- build/params_interop.go | 3 ++- build/params_mainnet.go | 3 ++- 5 files changed, 9 insertions(+), 5 deletions(-) diff --git a/build/params_2k.go b/build/params_2k.go index fb8b1beea08..8220ce8aaf8 100644 --- a/build/params_2k.go +++ b/build/params_2k.go @@ -143,4 +143,4 @@ var WhitelistedBlock = cid.Undef // Reducing the delivery delay for equivocation of // consistent broadcast to just half a second. -const CBDeliveryDelay = 500 * time.Milisecond +var CBDeliveryDelay = 500 * time.Milisecond diff --git a/build/params_butterfly.go b/build/params_butterfly.go index 137ab7dee97..4fdac1ec882 100644 --- a/build/params_butterfly.go +++ b/build/params_butterfly.go @@ -92,4 +92,5 @@ var WhitelistedBlock = cid.Undef // CBDeliveryDelay is the delay before deliver in the synchronous consistent broadcast. // This determines the wait time for the detection of potential equivocations. -const CBDeliveryDelay = 2 * time.Second +// It is a variable instead of a constant so it can be conveniently configured in tests +var CBDeliveryDelay = 2 * time.Second diff --git a/build/params_calibnet.go b/build/params_calibnet.go index 7bfca2a4252..35ae1796c88 100644 --- a/build/params_calibnet.go +++ b/build/params_calibnet.go @@ -126,4 +126,5 @@ var WhitelistedBlock = cid.Undef // CBDeliveryDelay is the delay before deliver in the synchronous consistent broadcast. // This determines the wait time for the detection of potential equivocations. -const CBDeliveryDelay = 2 * time.Second +// It is a variable instead of a constant so it can be conveniently configured in tests +var CBDeliveryDelay = 2 * time.Second diff --git a/build/params_interop.go b/build/params_interop.go index 0fb86524845..72cfdca3543 100644 --- a/build/params_interop.go +++ b/build/params_interop.go @@ -132,4 +132,5 @@ var WhitelistedBlock = cid.Undef // CBDeliveryDelay is the delay before deliver in the synchronous consistent broadcast. // This determines the wait time for the detection of potential equivocations. -const CBDeliveryDelay = 2 * time.Second +// It is a variable instead of a constant so it can be conveniently configured in tests +var CBDeliveryDelay = 2 * time.Second diff --git a/build/params_mainnet.go b/build/params_mainnet.go index bb205a827ce..d4cf6ff4b5f 100644 --- a/build/params_mainnet.go +++ b/build/params_mainnet.go @@ -141,4 +141,5 @@ var WhitelistedBlock = MustParseCid("bafy2bzaceapyg2uyzk7vueh3xccxkuwbz3nxewjygu // CBDeliveryDelay is the delay before deliver in the synchronous consistent broadcast. // This determines the wait time for the detection of potential equivocations. -const CBDeliveryDelay = 2 * time.Second +// It is a variable instead of a constant so it can be conveniently configured in tests +var CBDeliveryDelay = 2 * time.Second From 41ea5a6f638ca5108a51f70a4cef85d9b98802b7 Mon Sep 17 00:00:00 2001 From: Mikers Date: Wed, 29 Mar 2023 05:44:25 -1000 Subject: [PATCH 135/243] Update chain/messagepool/messagepool.go Co-authored-by: Aayush Rajasekaran --- chain/messagepool/messagepool.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chain/messagepool/messagepool.go b/chain/messagepool/messagepool.go index 4b003b9316d..b0e7b7e2b73 100644 --- a/chain/messagepool/messagepool.go +++ b/chain/messagepool/messagepool.go @@ -768,7 +768,7 @@ func (mp *MessagePool) Add(ctx context.Context, m *types.SignedMessage) error { tmpCurTs := mp.curTs mp.curTsLk.RUnlock() - //ensures computations are cached without holding lack + //ensures computations are cached without holding lock _, _ = mp.api.GetActorAfter(m.Message.From, tmpCurTs) _, _ = mp.getStateNonce(ctx, m.Message.From, tmpCurTs) From 66fc6dc3e5810256cd3638372fda80ac5bd8975e Mon Sep 17 00:00:00 2001 From: Aayush Date: Wed, 29 Mar 2023 15:24:07 -0400 Subject: [PATCH 136/243] refactor: stop using deprecated io/ioutil --- blockstore/ipfs.go | 4 +-- chain/actors/agen/main.go | 25 ++++++++-------- chain/gen/gen.go | 6 ++-- cli/state.go | 3 +- cli/util/retrieval.go | 3 +- cli/wallet.go | 3 +- cmd/lotus-bench/import.go | 3 +- cmd/lotus-bench/main.go | 9 +++--- cmd/lotus-bench/simple.go | 9 +++--- cmd/lotus-fountain/recaptcha.go | 4 +-- cmd/lotus-miner/init.go | 5 ++-- cmd/lotus-miner/init_restore.go | 3 +- cmd/lotus-miner/storage.go | 3 +- cmd/lotus-pcr/main.go | 5 ++-- cmd/lotus-seed/genesis.go | 29 +++++++++---------- cmd/lotus-seed/main.go | 3 +- cmd/lotus-seed/seed/seed.go | 7 ++--- cmd/lotus-shed/base16.go | 3 +- cmd/lotus-shed/base32.go | 3 +- cmd/lotus-shed/base64.go | 3 +- cmd/lotus-shed/bitfield.go | 3 +- cmd/lotus-shed/fip-0036.go | 4 +-- cmd/lotus-shed/jwt.go | 9 +++--- cmd/lotus-shed/keyinfo.go | 7 ++--- cmd/lotus-shed/rpc.go | 3 +- cmd/lotus-worker/main.go | 3 +- cmd/lotus-worker/storage.go | 3 +- cmd/lotus/daemon.go | 5 ++-- conformance/corpus_test.go | 3 +- conformance/runner.go | 3 +- gen/inline-gen/main.go | 7 ++--- .../deals_partial_retrieval_dm-level_test.go | 5 ++-- itests/kit/client.go | 7 ++--- itests/kit/ensemble.go | 4 +-- itests/kit/node_miner.go | 3 +- itests/kit/node_worker.go | 3 +- lib/backupds/backupds_test.go | 3 +- lib/consensus/raft/config.go | 6 ++-- lib/rpcenc/reader.go | 5 ++-- lib/unixfs/filestore.go | 3 +- lib/unixfs/filestore_test.go | 5 ++-- markets/dagstore/mount_test.go | 9 +++--- node/config/cfgdocgen/gen.go | 3 +- node/config/load.go | 3 +- node/config/load_test.go | 7 ++--- node/config/storage.go | 3 +- node/impl/client/client_test.go | 6 ++-- node/modules/core.go | 3 +- node/modules/testing/genesis.go | 3 +- node/repo/fsrepo.go | 15 +++++----- node/repo/memrepo.go | 5 ++-- storage/paths/http_handler_test.go | 8 ++--- storage/paths/local.go | 5 ++-- storage/paths/local_test.go | 3 +- storage/paths/remote.go | 7 ++--- storage/paths/remote_test.go | 11 ++++--- storage/sealer/ffiwrapper/sealer_cgo.go | 3 +- storage/sealer/ffiwrapper/sealer_test.go | 29 +++++++++---------- storage/sealer/fr32/fr32_ffi_cmp_test.go | 5 ++-- storage/sealer/fr32/fr32_test.go | 5 ++-- storage/sealer/fr32/readers_test.go | 4 +-- storage/sealer/manager_test.go | 5 ++-- storage/sealer/mock/mock.go | 3 +- storage/sealer/piece_provider_test.go | 4 +-- 64 files changed, 161 insertions(+), 215 deletions(-) diff --git a/blockstore/ipfs.go b/blockstore/ipfs.go index 756314f2de8..c7dbb480a45 100644 --- a/blockstore/ipfs.go +++ b/blockstore/ipfs.go @@ -3,7 +3,7 @@ package blockstore import ( "bytes" "context" - "io/ioutil" + "io" "github.com/ipfs/go-cid" httpapi "github.com/ipfs/go-ipfs-http-client" @@ -103,7 +103,7 @@ func (i *IPFSBlockstore) Get(ctx context.Context, cid cid.Cid) (blocks.Block, er return nil, xerrors.Errorf("getting ipfs block: %w", err) } - data, err := ioutil.ReadAll(rd) + data, err := io.ReadAll(rd) if err != nil { return nil, err } diff --git a/chain/actors/agen/main.go b/chain/actors/agen/main.go index b9f3a22a434..811ea27e982 100644 --- a/chain/actors/agen/main.go +++ b/chain/actors/agen/main.go @@ -4,7 +4,6 @@ import ( "bytes" "fmt" "go/format" - "io/ioutil" "os" "path/filepath" "strconv" @@ -66,7 +65,7 @@ func generateAdapters() error { } { - af, err := ioutil.ReadFile(filepath.Join(actDir, "actor.go.template")) + af, err := os.ReadFile(filepath.Join(actDir, "actor.go.template")) if err != nil { return xerrors.Errorf("loading actor template: %w", err) } @@ -90,7 +89,7 @@ func generateAdapters() error { return err } - if err := ioutil.WriteFile(filepath.Join(actDir, fmt.Sprintf("%s.go", act)), fmted, 0666); err != nil { + if err := os.WriteFile(filepath.Join(actDir, fmt.Sprintf("%s.go", act)), fmted, 0666); err != nil { return err } } @@ -100,7 +99,7 @@ func generateAdapters() error { } func generateState(actDir string, versions []int) error { - af, err := ioutil.ReadFile(filepath.Join(actDir, "state.go.template")) + af, err := os.ReadFile(filepath.Join(actDir, "state.go.template")) if err != nil { if os.IsNotExist(err) { return nil // skip @@ -123,7 +122,7 @@ func generateState(actDir string, versions []int) error { return err } - if err := ioutil.WriteFile(filepath.Join(actDir, fmt.Sprintf("v%d.go", version)), b.Bytes(), 0666); err != nil { + if err := os.WriteFile(filepath.Join(actDir, fmt.Sprintf("v%d.go", version)), b.Bytes(), 0666); err != nil { return err } } @@ -132,7 +131,7 @@ func generateState(actDir string, versions []int) error { } func generateMessages(actDir string) error { - af, err := ioutil.ReadFile(filepath.Join(actDir, "message.go.template")) + af, err := os.ReadFile(filepath.Join(actDir, "message.go.template")) if err != nil { if os.IsNotExist(err) { return nil // skip @@ -155,7 +154,7 @@ func generateMessages(actDir string) error { return err } - if err := ioutil.WriteFile(filepath.Join(actDir, fmt.Sprintf("message%d.go", version)), b.Bytes(), 0666); err != nil { + if err := os.WriteFile(filepath.Join(actDir, fmt.Sprintf("message%d.go", version)), b.Bytes(), 0666); err != nil { return err } } @@ -165,7 +164,7 @@ func generateMessages(actDir string) error { func generatePolicy(policyPath string) error { - pf, err := ioutil.ReadFile(policyPath + ".template") + pf, err := os.ReadFile(policyPath + ".template") if err != nil { if os.IsNotExist(err) { return nil // skip @@ -187,7 +186,7 @@ func generatePolicy(policyPath string) error { return err } - if err := ioutil.WriteFile(policyPath, b.Bytes(), 0666); err != nil { + if err := os.WriteFile(policyPath, b.Bytes(), 0666); err != nil { return err } @@ -196,7 +195,7 @@ func generatePolicy(policyPath string) error { func generateBuiltin(builtinPath string) error { - bf, err := ioutil.ReadFile(builtinPath + ".template") + bf, err := os.ReadFile(builtinPath + ".template") if err != nil { if os.IsNotExist(err) { return nil // skip @@ -218,7 +217,7 @@ func generateBuiltin(builtinPath string) error { return err } - if err := ioutil.WriteFile(builtinPath, b.Bytes(), 0666); err != nil { + if err := os.WriteFile(builtinPath, b.Bytes(), 0666); err != nil { return err } @@ -227,7 +226,7 @@ func generateBuiltin(builtinPath string) error { func generateRegistry(registryPath string) error { - bf, err := ioutil.ReadFile(registryPath + ".template") + bf, err := os.ReadFile(registryPath + ".template") if err != nil { if os.IsNotExist(err) { return nil // skip @@ -248,7 +247,7 @@ func generateRegistry(registryPath string) error { return err } - if err := ioutil.WriteFile(registryPath, b.Bytes(), 0666); err != nil { + if err := os.WriteFile(registryPath, b.Bytes(), 0666); err != nil { return err } diff --git a/chain/gen/gen.go b/chain/gen/gen.go index 0da8e8318de..98610cd6cb9 100644 --- a/chain/gen/gen.go +++ b/chain/gen/gen.go @@ -5,7 +5,7 @@ import ( "context" "fmt" "io" - "io/ioutil" + "os" "sync/atomic" "time" @@ -168,7 +168,7 @@ func NewGeneratorWithSectorsAndUpgradeSchedule(numSectors int, us stmgr.UpgradeS maddr1 := genesis2.MinerAddress(0) - m1temp, err := ioutil.TempDir("", "preseal") + m1temp, err := os.MkdirTemp("", "preseal") if err != nil { return nil, err } @@ -180,7 +180,7 @@ func NewGeneratorWithSectorsAndUpgradeSchedule(numSectors int, us stmgr.UpgradeS maddr2 := genesis2.MinerAddress(1) - m2temp, err := ioutil.TempDir("", "preseal") + m2temp, err := os.MkdirTemp("", "preseal") if err != nil { return nil, err } diff --git a/cli/state.go b/cli/state.go index c039cb80a7d..3099bff17d6 100644 --- a/cli/state.go +++ b/cli/state.go @@ -9,7 +9,6 @@ import ( "fmt" "html/template" "io" - "io/ioutil" "os" "reflect" "sort" @@ -1090,7 +1089,7 @@ var StateComputeStateCmd = &cli.Command{ var stout *lapi.ComputeStateOutput if csofile := cctx.String("compute-state-output"); csofile != "" { - data, err := ioutil.ReadFile(csofile) + data, err := os.ReadFile(csofile) if err != nil { return err } diff --git a/cli/util/retrieval.go b/cli/util/retrieval.go index 3a2ef60770a..ac34fcf3a48 100644 --- a/cli/util/retrieval.go +++ b/cli/util/retrieval.go @@ -4,7 +4,6 @@ import ( "encoding/json" "fmt" "io" - "io/ioutil" "net/http" "net/url" "path" @@ -65,7 +64,7 @@ func ClientExportStream(apiAddr string, apiAuth http.Header, eref api.ExportRef, } if resp.StatusCode != http.StatusOK { - em, err := ioutil.ReadAll(resp.Body) + em, err := io.ReadAll(resp.Body) if err != nil { return nil, xerrors.Errorf("reading error body: %w", err) } diff --git a/cli/wallet.go b/cli/wallet.go index a936efa94bb..c66275cdd50 100644 --- a/cli/wallet.go +++ b/cli/wallet.go @@ -6,7 +6,6 @@ import ( "encoding/hex" "encoding/json" "fmt" - "io/ioutil" "os" "strings" @@ -337,7 +336,7 @@ var walletImport = &cli.Command{ inpdata = indata } else { - fdata, err := ioutil.ReadFile(cctx.Args().First()) + fdata, err := os.ReadFile(cctx.Args().First()) if err != nil { return err } diff --git a/cmd/lotus-bench/import.go b/cmd/lotus-bench/import.go index 44c152d0cb6..9f43d95381e 100644 --- a/cmd/lotus-bench/import.go +++ b/cmd/lotus-bench/import.go @@ -6,7 +6,6 @@ import ( "encoding/json" "fmt" "io" - "io/ioutil" "math" "net/http" _ "net/http/pprof" @@ -159,7 +158,7 @@ var importBenchCmd = &cli.Command{ if rdir := cctx.String("repodir"); rdir != "" { tdir = rdir } else { - tmp, err := ioutil.TempDir("", "lotus-import-bench") + tmp, err := os.MkdirTemp("", "lotus-import-bench") if err != nil { return err } diff --git a/cmd/lotus-bench/main.go b/cmd/lotus-bench/main.go index 279f2d5fdb9..43972b1877b 100644 --- a/cmd/lotus-bench/main.go +++ b/cmd/lotus-bench/main.go @@ -4,7 +4,6 @@ import ( "context" "encoding/json" "fmt" - "io/ioutil" "math/big" "math/rand" "os" @@ -197,7 +196,7 @@ var sealBenchCmd = &cli.Command{ return xerrors.Errorf("creating sectorbuilder dir: %w", err) } - tsdir, err := ioutil.TempDir(sdir, "bench") + tsdir, err := os.MkdirTemp(sdir, "bench") if err != nil { return err } @@ -287,7 +286,7 @@ var sealBenchCmd = &cli.Command{ // sectorbuilder directory... we need a better way to handle // this in other cases - fdata, err := ioutil.ReadFile(filepath.Join(sbdir, "pre-seal-"+maddr.String()+".json")) + fdata, err := os.ReadFile(filepath.Join(sbdir, "pre-seal-"+maddr.String()+".json")) if err != nil { return err } @@ -637,7 +636,7 @@ func runSeals(sb *ffiwrapper.Sealer, sbfs *basicfs.Provider, numSectors int, par return err } - if err := ioutil.WriteFile(saveC2inp, b, 0664); err != nil { + if err := os.WriteFile(saveC2inp, b, 0664); err != nil { log.Warnf("%+v", err) } } @@ -751,7 +750,7 @@ var proveCmd = &cli.Command{ return xerrors.Errorf("Usage: lotus-bench prove [input.json]") } - inb, err := ioutil.ReadFile(c.Args().First()) + inb, err := os.ReadFile(c.Args().First()) if err != nil { return xerrors.Errorf("reading input file: %w", err) } diff --git a/cmd/lotus-bench/simple.go b/cmd/lotus-bench/simple.go index 87e2c3bc036..a742b0fb38c 100644 --- a/cmd/lotus-bench/simple.go +++ b/cmd/lotus-bench/simple.go @@ -5,7 +5,6 @@ import ( "encoding/base64" "encoding/json" "fmt" - "io/ioutil" "os" "strconv" "time" @@ -444,7 +443,7 @@ var simpleCommit1 = &cli.Command{ return err } - if err := ioutil.WriteFile(cctx.Args().Get(4), b, 0664); err != nil { + if err := os.WriteFile(cctx.Args().Get(4), b, 0664); err != nil { log.Warnf("%+v", err) } @@ -478,7 +477,7 @@ var simpleCommit2 = &cli.Command{ return xerrors.Errorf("Usage: lotus-bench prove [input.json]") } - inb, err := ioutil.ReadFile(c.Args().First()) + inb, err := os.ReadFile(c.Args().First()) if err != nil { return xerrors.Errorf("reading input file: %w", err) } @@ -861,7 +860,7 @@ var simpleProveReplicaUpdate1 = &cli.Command{ return xerrors.Errorf("json marshal vanilla proofs: %w", err) } - if err := ioutil.WriteFile(cctx.Args().Get(7), vpjb, 0666); err != nil { + if err := os.WriteFile(cctx.Args().Get(7), vpjb, 0666); err != nil { return xerrors.Errorf("writing vanilla proofs file: %w", err) } @@ -934,7 +933,7 @@ var simpleProveReplicaUpdate2 = &cli.Command{ return xerrors.Errorf("parse commr: %w", err) } - vpb, err := ioutil.ReadFile(cctx.Args().Get(3)) + vpb, err := os.ReadFile(cctx.Args().Get(3)) if err != nil { return xerrors.Errorf("reading valilla proof file: %w", err) } diff --git a/cmd/lotus-fountain/recaptcha.go b/cmd/lotus-fountain/recaptcha.go index 69359faa3bc..6b2327a033f 100644 --- a/cmd/lotus-fountain/recaptcha.go +++ b/cmd/lotus-fountain/recaptcha.go @@ -6,7 +6,7 @@ package main import ( "encoding/json" - "io/ioutil" + "io" "net/http" "net/url" "os" @@ -63,7 +63,7 @@ func VerifyToken(token, remoteIP string) (Response, error) { return resp, err } - b, err := ioutil.ReadAll(r.Body) + b, err := io.ReadAll(r.Body) _ = r.Body.Close() // close immediately after reading finished if err != nil { return resp, err diff --git a/cmd/lotus-miner/init.go b/cmd/lotus-miner/init.go index 66a6691af18..e752839ece2 100644 --- a/cmd/lotus-miner/init.go +++ b/cmd/lotus-miner/init.go @@ -7,7 +7,6 @@ import ( "encoding/binary" "encoding/json" "fmt" - "io/ioutil" "net/http" "os" "path/filepath" @@ -246,7 +245,7 @@ var initCmd = &cli.Command{ return xerrors.Errorf("marshaling storage config: %w", err) } - if err := ioutil.WriteFile(filepath.Join(lr.Path(), "sectorstore.json"), b, 0644); err != nil { + if err := os.WriteFile(filepath.Join(lr.Path(), "sectorstore.json"), b, 0644); err != nil { return xerrors.Errorf("persisting storage metadata (%s): %w", filepath.Join(lr.Path(), "sectorstore.json"), err) } @@ -292,7 +291,7 @@ func migratePreSealMeta(ctx context.Context, api v1api.FullNode, metadata string return xerrors.Errorf("expanding preseal dir: %w", err) } - b, err := ioutil.ReadFile(metadata) + b, err := os.ReadFile(metadata) if err != nil { return xerrors.Errorf("reading preseal metadata: %w", err) } diff --git a/cmd/lotus-miner/init_restore.go b/cmd/lotus-miner/init_restore.go index f3ab9d04a46..7e28729bbeb 100644 --- a/cmd/lotus-miner/init_restore.go +++ b/cmd/lotus-miner/init_restore.go @@ -3,7 +3,6 @@ package main import ( "context" "encoding/json" - "io/ioutil" "os" "github.com/docker/go-units" @@ -59,7 +58,7 @@ var restoreCmd = &cli.Command{ return xerrors.Errorf("expanding storage config path: %w", err) } - cfb, err := ioutil.ReadFile(cf) + cfb, err := os.ReadFile(cf) if err != nil { return xerrors.Errorf("reading storage config: %w", err) } diff --git a/cmd/lotus-miner/storage.go b/cmd/lotus-miner/storage.go index a5aa354f8d4..89353497dc4 100644 --- a/cmd/lotus-miner/storage.go +++ b/cmd/lotus-miner/storage.go @@ -4,7 +4,6 @@ import ( "context" "encoding/json" "fmt" - "io/ioutil" "math/bits" "os" "path/filepath" @@ -167,7 +166,7 @@ over time return xerrors.Errorf("marshaling storage config: %w", err) } - if err := ioutil.WriteFile(filepath.Join(p, metaFile), b, 0644); err != nil { + 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) } } diff --git a/cmd/lotus-pcr/main.go b/cmd/lotus-pcr/main.go index 57eaf0ad674..199810e03cd 100644 --- a/cmd/lotus-pcr/main.go +++ b/cmd/lotus-pcr/main.go @@ -7,7 +7,6 @@ import ( "encoding/csv" "fmt" "io" - "io/ioutil" "net/http" _ "net/http/pprof" "os" @@ -759,7 +758,7 @@ func (r *refunder) EnsureMinerMinimums(ctx context.Context, tipset *types.TipSet return nil, err } - w := ioutil.Discard + w := io.Discard if len(output) != 0 { f, err := os.Create(output) if err != nil { @@ -1299,7 +1298,7 @@ func loadChainEpoch(fn string) (abi.ChainEpoch, error) { err = f.Close() }() - raw, err := ioutil.ReadAll(f) + raw, err := io.ReadAll(f) if err != nil { return 0, err } diff --git a/cmd/lotus-seed/genesis.go b/cmd/lotus-seed/genesis.go index 1ab6a465afe..9fdce456bf1 100644 --- a/cmd/lotus-seed/genesis.go +++ b/cmd/lotus-seed/genesis.go @@ -4,7 +4,6 @@ import ( "encoding/csv" "encoding/json" "fmt" - "io/ioutil" "os" "strconv" "strings" @@ -80,7 +79,7 @@ var genesisNewCmd = &cli.Command{ return err } - if err := ioutil.WriteFile(genf, genb, 0644); err != nil { + if err := os.WriteFile(genf, genb, 0644); err != nil { return err } @@ -103,7 +102,7 @@ var genesisAddMinerCmd = &cli.Command{ } var template genesis.Template - genb, err := ioutil.ReadFile(genf) + genb, err := os.ReadFile(genf) if err != nil { return xerrors.Errorf("read genesis template: %w", err) } @@ -117,7 +116,7 @@ var genesisAddMinerCmd = &cli.Command{ return xerrors.Errorf("expand preseal file path: %w", err) } miners := map[string]genesis.Miner{} - minb, err := ioutil.ReadFile(minf) + minb, err := os.ReadFile(minf) if err != nil { return xerrors.Errorf("read preseal file: %w", err) } @@ -156,7 +155,7 @@ var genesisAddMinerCmd = &cli.Command{ return err } - if err := ioutil.WriteFile(genf, genb, 0644); err != nil { + if err := os.WriteFile(genf, genb, 0644); err != nil { return err } @@ -196,7 +195,7 @@ var genesisAddMsigsCmd = &cli.Command{ } var template genesis.Template - b, err := ioutil.ReadFile(genf) + b, err := os.ReadFile(genf) if err != nil { return xerrors.Errorf("read genesis template: %w", err) } @@ -237,7 +236,7 @@ var genesisAddMsigsCmd = &cli.Command{ return err } - if err := ioutil.WriteFile(genf, b, 0644); err != nil { + if err := os.WriteFile(genf, b, 0644); err != nil { return err } return nil @@ -339,7 +338,7 @@ var genesisSetVRKCmd = &cli.Command{ } var template genesis.Template - b, err := ioutil.ReadFile(genf) + b, err := os.ReadFile(genf) if err != nil { return xerrors.Errorf("read genesis template: %w", err) } @@ -404,7 +403,7 @@ var genesisSetVRKCmd = &cli.Command{ return err } - if err := ioutil.WriteFile(genf, b, 0644); err != nil { + if err := os.WriteFile(genf, b, 0644); err != nil { return err } return nil @@ -435,7 +434,7 @@ var genesisSetRemainderCmd = &cli.Command{ } var template genesis.Template - b, err := ioutil.ReadFile(genf) + b, err := os.ReadFile(genf) if err != nil { return xerrors.Errorf("read genesis template: %w", err) } @@ -500,7 +499,7 @@ var genesisSetRemainderCmd = &cli.Command{ return err } - if err := ioutil.WriteFile(genf, b, 0644); err != nil { + if err := os.WriteFile(genf, b, 0644); err != nil { return err } return nil @@ -529,7 +528,7 @@ var genesisSetActorVersionCmd = &cli.Command{ } var template genesis.Template - b, err := ioutil.ReadFile(genf) + b, err := os.ReadFile(genf) if err != nil { return xerrors.Errorf("read genesis template: %w", err) } @@ -550,7 +549,7 @@ var genesisSetActorVersionCmd = &cli.Command{ return err } - if err := ioutil.WriteFile(genf, b, 0644); err != nil { + if err := os.WriteFile(genf, b, 0644); err != nil { return err } return nil @@ -607,7 +606,7 @@ var genesisSetVRKSignersCmd = &cli.Command{ } var template genesis.Template - b, err := ioutil.ReadFile(genf) + b, err := os.ReadFile(genf) if err != nil { return xerrors.Errorf("read genesis template: %w", err) } @@ -662,7 +661,7 @@ var genesisSetVRKSignersCmd = &cli.Command{ return err } - if err := ioutil.WriteFile(genf, b, 0644); err != nil { + if err := os.WriteFile(genf, b, 0644); err != nil { return err } return nil diff --git a/cmd/lotus-seed/main.go b/cmd/lotus-seed/main.go index 7567f639395..863a508f228 100644 --- a/cmd/lotus-seed/main.go +++ b/cmd/lotus-seed/main.go @@ -4,7 +4,6 @@ import ( "encoding/hex" "encoding/json" "fmt" - "io/ioutil" "os" "github.com/docker/go-units" @@ -114,7 +113,7 @@ var preSealCmd = &cli.Command{ var k *types.KeyInfo if c.String("key") != "" { k = new(types.KeyInfo) - kh, err := ioutil.ReadFile(c.String("key")) + kh, err := os.ReadFile(c.String("key")) if err != nil { return err } diff --git a/cmd/lotus-seed/seed/seed.go b/cmd/lotus-seed/seed/seed.go index b986737127a..48f00f8a638 100644 --- a/cmd/lotus-seed/seed/seed.go +++ b/cmd/lotus-seed/seed/seed.go @@ -6,7 +6,6 @@ import ( "encoding/hex" "encoding/json" "fmt" - "io/ioutil" "os" "path/filepath" @@ -135,7 +134,7 @@ func PreSeal(maddr address.Address, spt abi.RegisteredSealProof, offset abi.Sect return nil, nil, xerrors.Errorf("marshaling storage config: %w", err) } - if err := ioutil.WriteFile(filepath.Join(sbroot, "sectorstore.json"), b, 0644); err != nil { + if err := os.WriteFile(filepath.Join(sbroot, "sectorstore.json"), b, 0644); err != nil { return nil, nil, xerrors.Errorf("persisting storage metadata (%s): %w", filepath.Join(sbroot, "storage.json"), err) } } @@ -228,7 +227,7 @@ func WriteGenesisMiner(maddr address.Address, sbroot string, gm *genesis.Miner, log.Infof("Writing preseal manifest to %s", filepath.Join(sbroot, "pre-seal-"+maddr.String()+".json")) - if err := ioutil.WriteFile(filepath.Join(sbroot, "pre-seal-"+maddr.String()+".json"), out, 0664); err != nil { + if err := os.WriteFile(filepath.Join(sbroot, "pre-seal-"+maddr.String()+".json"), out, 0664); err != nil { return err } @@ -239,7 +238,7 @@ func WriteGenesisMiner(maddr address.Address, sbroot string, gm *genesis.Miner, } // TODO: allow providing key - if err := ioutil.WriteFile(filepath.Join(sbroot, "pre-seal-"+maddr.String()+".key"), []byte(hex.EncodeToString(b)), 0664); err != nil { + if err := os.WriteFile(filepath.Join(sbroot, "pre-seal-"+maddr.String()+".key"), []byte(hex.EncodeToString(b)), 0664); err != nil { return err } } diff --git a/cmd/lotus-shed/base16.go b/cmd/lotus-shed/base16.go index a5d38481549..28ea4916ffc 100644 --- a/cmd/lotus-shed/base16.go +++ b/cmd/lotus-shed/base16.go @@ -4,7 +4,6 @@ import ( "encoding/hex" "fmt" "io" - "io/ioutil" "os" "strings" @@ -30,7 +29,7 @@ var base16Cmd = &cli.Command{ input = strings.NewReader(cctx.Args().First()) } - bytes, err := ioutil.ReadAll(input) + bytes, err := io.ReadAll(input) if err != nil { return nil } diff --git a/cmd/lotus-shed/base32.go b/cmd/lotus-shed/base32.go index 66e180ddc04..d2ea3dd7ab6 100644 --- a/cmd/lotus-shed/base32.go +++ b/cmd/lotus-shed/base32.go @@ -3,7 +3,6 @@ package main import ( "fmt" "io" - "io/ioutil" "os" "strings" @@ -30,7 +29,7 @@ var base32Cmd = &cli.Command{ input = strings.NewReader(cctx.Args().First()) } - bytes, err := ioutil.ReadAll(input) + bytes, err := io.ReadAll(input) if err != nil { return nil } diff --git a/cmd/lotus-shed/base64.go b/cmd/lotus-shed/base64.go index cacc601528f..19afa66134b 100644 --- a/cmd/lotus-shed/base64.go +++ b/cmd/lotus-shed/base64.go @@ -4,7 +4,6 @@ import ( "encoding/base64" "fmt" "io" - "io/ioutil" "os" "strings" @@ -38,7 +37,7 @@ var base64Cmd = &cli.Command{ input = strings.NewReader(cctx.Args().First()) } - bytes, err := ioutil.ReadAll(input) + bytes, err := io.ReadAll(input) if err != nil { return nil } diff --git a/cmd/lotus-shed/bitfield.go b/cmd/lotus-shed/bitfield.go index f0824de4f2a..5ac75b2c618 100644 --- a/cmd/lotus-shed/bitfield.go +++ b/cmd/lotus-shed/bitfield.go @@ -5,7 +5,6 @@ import ( "encoding/hex" "fmt" "io" - "io/ioutil" "os" "github.com/urfave/cli/v2" @@ -318,7 +317,7 @@ func decodeToByte(cctx *cli.Context, i int) ([]byte, error) { if i > 0 { return nil, xerrors.Errorf("need more than %d args", i) } - r, err := ioutil.ReadAll(os.Stdin) + r, err := io.ReadAll(os.Stdin) if err != nil { return nil, err } diff --git a/cmd/lotus-shed/fip-0036.go b/cmd/lotus-shed/fip-0036.go index 485302b9b6c..4c8456c04ce 100644 --- a/cmd/lotus-shed/fip-0036.go +++ b/cmd/lotus-shed/fip-0036.go @@ -5,7 +5,7 @@ import ( "encoding/json" "fmt" "io" - "io/ioutil" + "os" "sort" "strconv" @@ -537,7 +537,7 @@ var finalResultCmd = &cli.Command{ // Returns voted sorted by votes from earliest to latest func getVotesMap(file string) ([]Vote, error) { var votes []Vote - vb, err := ioutil.ReadFile(file) + vb, err := os.ReadFile(file) if err != nil { return nil, xerrors.Errorf("read vote: %w", err) } diff --git a/cmd/lotus-shed/jwt.go b/cmd/lotus-shed/jwt.go index e8853b419b6..2a24c256933 100644 --- a/cmd/lotus-shed/jwt.go +++ b/cmd/lotus-shed/jwt.go @@ -7,7 +7,6 @@ import ( "encoding/json" "fmt" "io" - "io/ioutil" "os" "strings" @@ -81,7 +80,7 @@ var jwtTokenCmd = &cli.Command{ defer inputFile.Close() //nolint:errcheck input := bufio.NewReader(inputFile) - encoded, err := ioutil.ReadAll(input) + encoded, err := io.ReadAll(input) if err != nil { return err } @@ -123,7 +122,7 @@ var jwtTokenCmd = &cli.Command{ return err } - return ioutil.WriteFile(cctx.String("output"), token, 0600) + return os.WriteFile(cctx.String("output"), token, 0600) }, } @@ -142,7 +141,7 @@ var jwtNewCmd = &cli.Command{ keyName := cctx.Args().First() - sk, err := ioutil.ReadAll(io.LimitReader(rand.Reader, 32)) + sk, err := io.ReadAll(io.LimitReader(rand.Reader, 32)) if err != nil { return err } @@ -184,6 +183,6 @@ var jwtNewCmd = &cli.Command{ } filenameToken := fmt.Sprintf("jwt-%s.token", keyName) - return ioutil.WriteFile(filenameToken, token, 0600) + return os.WriteFile(filenameToken, token, 0600) }, } diff --git a/cmd/lotus-shed/keyinfo.go b/cmd/lotus-shed/keyinfo.go index 38f5ee6fefe..15b584f3d8b 100644 --- a/cmd/lotus-shed/keyinfo.go +++ b/cmd/lotus-shed/keyinfo.go @@ -7,7 +7,6 @@ import ( "encoding/json" "fmt" "io" - "io/ioutil" "os" "path" "strings" @@ -68,7 +67,7 @@ var keyinfoVerifyCmd = &cli.Command{ defer inputFile.Close() //nolint:errcheck input := bufio.NewReader(inputFile) - keyContent, err := ioutil.ReadAll(input) + keyContent, err := io.ReadAll(input) if err != nil { return err } @@ -162,7 +161,7 @@ var keyinfoImportCmd = &cli.Command{ input = bufio.NewReader(inputFile) } - encoded, err := ioutil.ReadAll(input) + encoded, err := io.ReadAll(input) if err != nil { return err } @@ -274,7 +273,7 @@ var keyinfoInfoCmd = &cli.Command{ input = bufio.NewReader(inputFile) } - encoded, err := ioutil.ReadAll(input) + encoded, err := io.ReadAll(input) if err != nil { return err } diff --git a/cmd/lotus-shed/rpc.go b/cmd/lotus-shed/rpc.go index 3be26935837..8ef79912921 100644 --- a/cmd/lotus-shed/rpc.go +++ b/cmd/lotus-shed/rpc.go @@ -6,7 +6,6 @@ import ( "encoding/json" "fmt" "io" - "io/ioutil" "net/http" "net/url" "os" @@ -97,7 +96,7 @@ var rpcCmd = &cli.Command{ return err } - rb, err := ioutil.ReadAll(resp.Body) + rb, err := io.ReadAll(resp.Body) if err != nil { return err } diff --git a/cmd/lotus-worker/main.go b/cmd/lotus-worker/main.go index 42f46c9d83e..e0a9312747c 100644 --- a/cmd/lotus-worker/main.go +++ b/cmd/lotus-worker/main.go @@ -4,7 +4,6 @@ import ( "context" "encoding/json" "fmt" - "io/ioutil" "net" "net/http" "os" @@ -464,7 +463,7 @@ var runCmd = &cli.Command{ return xerrors.Errorf("marshaling storage config: %w", err) } - if err := ioutil.WriteFile(filepath.Join(lr.Path(), "sectorstore.json"), b, 0644); err != nil { + if err := os.WriteFile(filepath.Join(lr.Path(), "sectorstore.json"), b, 0644); err != nil { return xerrors.Errorf("persisting storage metadata (%s): %w", filepath.Join(lr.Path(), "sectorstore.json"), err) } diff --git a/cmd/lotus-worker/storage.go b/cmd/lotus-worker/storage.go index 6b5994c172f..9e4bf1c9ba7 100644 --- a/cmd/lotus-worker/storage.go +++ b/cmd/lotus-worker/storage.go @@ -2,7 +2,6 @@ package main import ( "encoding/json" - "io/ioutil" "os" "path/filepath" @@ -121,7 +120,7 @@ var storageAttachCmd = &cli.Command{ return xerrors.Errorf("marshaling storage config: %w", err) } - if err := ioutil.WriteFile(filepath.Join(p, metaFile), b, 0644); err != nil { + 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) } } diff --git a/cmd/lotus/daemon.go b/cmd/lotus/daemon.go index 704d9b470f8..c02200f26a0 100644 --- a/cmd/lotus/daemon.go +++ b/cmd/lotus/daemon.go @@ -10,7 +10,6 @@ import ( "encoding/json" "fmt" "io" - "io/ioutil" "net/http" "os" "path" @@ -247,7 +246,7 @@ var DaemonCmd = &cli.Command{ var genBytes []byte if cctx.String("genesis") != "" { - genBytes, err = ioutil.ReadFile(cctx.String("genesis")) + genBytes, err = os.ReadFile(cctx.String("genesis")) if err != nil { return xerrors.Errorf("reading genesis: %w", err) } @@ -403,7 +402,7 @@ func importKey(ctx context.Context, api lapi.FullNode, f string) error { return err } - hexdata, err := ioutil.ReadFile(f) + hexdata, err := os.ReadFile(f) if err != nil { return err } diff --git a/conformance/corpus_test.go b/conformance/corpus_test.go index adbebbcc734..ec9f9d51640 100644 --- a/conformance/corpus_test.go +++ b/conformance/corpus_test.go @@ -5,7 +5,6 @@ package conformance import ( "encoding/json" - "io/ioutil" "os" "path/filepath" "strings" @@ -107,7 +106,7 @@ func TestConformance(t *testing.T) { // Run a test for each vector. for _, v := range vectors { path := filepath.Join(corpusRoot, v) - raw, err := ioutil.ReadFile(path) + raw, err := os.ReadFile(path) if err != nil { t.Fatalf("failed to read test raw file: %s", path) } diff --git a/conformance/runner.go b/conformance/runner.go index ff738a4a5ee..827c10a5cb9 100644 --- a/conformance/runner.go +++ b/conformance/runner.go @@ -6,7 +6,6 @@ import ( "context" "encoding/base64" "fmt" - "io/ioutil" "math" "os" "os/exec" @@ -328,7 +327,7 @@ func dumpThreeWayStateDiff(r Reporter, vector *schema.TestVector, bs blockstore. // writeStateToTempCAR writes the provided roots to a temporary CAR that'll be // cleaned up via t.Cleanup(). It returns the full path of the temp file. func writeStateToTempCAR(bs blockstore.Blockstore, roots ...cid.Cid) (string, error) { - tmp, err := ioutil.TempFile("", "lotus-tests-*.car") + tmp, err := os.CreateTemp("", "lotus-tests-*.car") if err != nil { return "", fmt.Errorf("failed to create temp file to dump CAR for diffing: %w", err) } diff --git a/gen/inline-gen/main.go b/gen/inline-gen/main.go index d97134cdd32..4e55816f6b6 100644 --- a/gen/inline-gen/main.go +++ b/gen/inline-gen/main.go @@ -5,7 +5,6 @@ import ( "encoding/json" "fmt" "io/fs" - "io/ioutil" "os" "path/filepath" "strings" @@ -19,7 +18,7 @@ const ( ) func main() { - db, err := ioutil.ReadFile(os.Args[2]) + db, err := os.ReadFile(os.Args[2]) if err != nil { panic(err) } @@ -38,7 +37,7 @@ func main() { if filepath.Ext(path) != ".go" { return nil } - fb, err := ioutil.ReadFile(path) + fb, err := os.ReadFile(path) if err != nil { return err } @@ -110,7 +109,7 @@ func main() { if rewrite { fmt.Printf("write %s\n", path) - if err := ioutil.WriteFile(path, []byte(strings.Join(outLines, "\n")), 0664); err != nil { + if err := os.WriteFile(path, []byte(strings.Join(outLines, "\n")), 0664); err != nil { return err } } diff --git a/itests/deals_partial_retrieval_dm-level_test.go b/itests/deals_partial_retrieval_dm-level_test.go index 156ed67fed6..e3414c19171 100644 --- a/itests/deals_partial_retrieval_dm-level_test.go +++ b/itests/deals_partial_retrieval_dm-level_test.go @@ -5,7 +5,6 @@ import ( "context" "fmt" "io" - "io/ioutil" "os" "testing" "time" @@ -196,7 +195,7 @@ func validateDMUnixFile(r io.Reader) error { } func testDMExportAsCar(ctx context.Context, client *kit.TestFullNode, expDirective api.ExportRef, tempDir string) error { - out, err := ioutil.TempFile(tempDir, "exp-test") + out, err := os.CreateTemp(tempDir, "exp-test") if err != nil { return err } @@ -214,7 +213,7 @@ func testDMExportAsCar(ctx context.Context, client *kit.TestFullNode, expDirecti return validateDMCar(out) } func tesV0RetrievalAsCar(ctx context.Context, client *kit.TestFullNode, retOrder api0.RetrievalOrder, tempDir string) error { - out, err := ioutil.TempFile(tempDir, "exp-test") + out, err := os.CreateTemp(tempDir, "exp-test") if err != nil { return err } diff --git a/itests/kit/client.go b/itests/kit/client.go index f29fecfebb3..134b6b1ceb7 100644 --- a/itests/kit/client.go +++ b/itests/kit/client.go @@ -3,7 +3,6 @@ package kit import ( "context" "fmt" - "io/ioutil" "math/rand" "os" "path/filepath" @@ -110,7 +109,7 @@ func RunClientTest(t *testing.T, cmds []*lcli.Command, clientNode *TestFullNode) // Retrieve the first file from the Miner // client retrieve - tmpdir, err := ioutil.TempDir(os.TempDir(), "test-cli-client") + tmpdir, err := os.MkdirTemp(os.TempDir(), "test-cli-client") require.NoError(t, err) path := filepath.Join(tmpdir, "outfile.dat") @@ -144,13 +143,13 @@ func createRandomFile(rseed, size int) ([]byte, string, error) { data := make([]byte, size) rand.New(rand.NewSource(int64(rseed))).Read(data) - dir, err := ioutil.TempDir(os.TempDir(), "test-make-deal-") + dir, err := os.MkdirTemp(os.TempDir(), "test-make-deal-") if err != nil { return nil, "", err } path := filepath.Join(dir, "sourcefile.dat") - err = ioutil.WriteFile(path, data, 0644) + err = os.WriteFile(path, data, 0644) if err != nil { return nil, "", err } diff --git a/itests/kit/ensemble.go b/itests/kit/ensemble.go index a96aa60ff56..d38518be867 100644 --- a/itests/kit/ensemble.go +++ b/itests/kit/ensemble.go @@ -6,9 +6,9 @@ import ( "crypto/rand" "encoding/binary" "fmt" - "io/ioutil" "net" "net/http" + "os" "sync" "testing" "time" @@ -234,7 +234,7 @@ func (n *Ensemble) MinerEnroll(minerNode *TestMiner, full *TestFullNode, opts .. peerId, err := peer.IDFromPrivateKey(privkey) require.NoError(n.t, err) - tdir, err := ioutil.TempDir("", "preseal-memgen") + tdir, err := os.MkdirTemp("", "preseal-memgen") require.NoError(n.t, err) minerCnt := len(n.inactive.miners) + len(n.active.miners) diff --git a/itests/kit/node_miner.go b/itests/kit/node_miner.go index dd6f3088cde..4b81c9df0bd 100644 --- a/itests/kit/node_miner.go +++ b/itests/kit/node_miner.go @@ -4,7 +4,6 @@ import ( "context" "encoding/json" "fmt" - "io/ioutil" "net" "os" "path/filepath" @@ -204,7 +203,7 @@ func (tm *TestMiner) AddStorage(ctx context.Context, t *testing.T, conf func(*st b, err := json.MarshalIndent(cfg, "", " ") require.NoError(t, err) - err = ioutil.WriteFile(filepath.Join(p, metaFile), b, 0644) + err = os.WriteFile(filepath.Join(p, metaFile), b, 0644) require.NoError(t, err) err = tm.StorageAddLocal(ctx, p) diff --git a/itests/kit/node_worker.go b/itests/kit/node_worker.go index ac200fb0fb2..3674763ed8d 100644 --- a/itests/kit/node_worker.go +++ b/itests/kit/node_worker.go @@ -3,7 +3,6 @@ package kit import ( "context" "encoding/json" - "io/ioutil" "net" "net/http" "os" @@ -67,7 +66,7 @@ func (tm *TestWorker) AddStorage(ctx context.Context, t *testing.T, conf func(*s b, err := json.MarshalIndent(cfg, "", " ") require.NoError(t, err) - err = ioutil.WriteFile(filepath.Join(p, metaFile), b, 0644) + err = os.WriteFile(filepath.Join(p, metaFile), b, 0644) require.NoError(t, err) err = tm.StorageAddLocal(ctx, p) diff --git a/lib/backupds/backupds_test.go b/lib/backupds/backupds_test.go index b76799bfbb5..8909a5f3bbc 100644 --- a/lib/backupds/backupds_test.go +++ b/lib/backupds/backupds_test.go @@ -5,7 +5,6 @@ import ( "bytes" "context" "fmt" - "io/ioutil" "os" "path/filepath" "strings" @@ -77,7 +76,7 @@ func TestLogRestore(t *testing.T) { require.NoError(t, err) require.Equal(t, 1, len(fls)) - bf, err := ioutil.ReadFile(filepath.Join(logdir, fls[0].Name())) + bf, err := os.ReadFile(filepath.Join(logdir, fls[0].Name())) require.NoError(t, err) ds2 := datastore.NewMapDatastore() diff --git a/lib/consensus/raft/config.go b/lib/consensus/raft/config.go index 81bdb7fdc97..bdd82c10815 100644 --- a/lib/consensus/raft/config.go +++ b/lib/consensus/raft/config.go @@ -1,7 +1,7 @@ package consensus import ( - "io/ioutil" + "io" "path/filepath" "time" @@ -69,7 +69,7 @@ func DefaultClusterRaftConfig() *ClusterRaftConfig { cfg.RaftConfig.LocalID = "will_be_set_automatically" // Set up logging - cfg.RaftConfig.LogOutput = ioutil.Discard + cfg.RaftConfig.LogOutput = io.Discard return &cfg } @@ -91,7 +91,7 @@ func NewClusterRaftConfig(userRaftConfig *config.UserRaftConfig) *ClusterRaftCon cfg.RaftConfig.LocalID = "will_be_set_automatically" // Set up logging - cfg.RaftConfig.LogOutput = ioutil.Discard + cfg.RaftConfig.LogOutput = io.Discard return &cfg diff --git a/lib/rpcenc/reader.go b/lib/rpcenc/reader.go index 34b9fcfb404..2dd64473e7e 100644 --- a/lib/rpcenc/reader.go +++ b/lib/rpcenc/reader.go @@ -6,7 +6,6 @@ import ( "errors" "fmt" "io" - "io/ioutil" "net/http" "net/url" "path" @@ -158,7 +157,7 @@ func ReaderParamEncoder(addr string) jsonrpc.Option { } if resp.StatusCode != http.StatusOK { - b, _ := ioutil.ReadAll(resp.Body) + b, _ := io.ReadAll(resp.Body) log.Errorf("sending reader param (%s): non-200 status: %s, msg: '%s'", u.String(), resp.Status, string(b)) return } @@ -182,7 +181,7 @@ func ReaderParamEncoder(addr string) jsonrpc.Option { defer resp.Body.Close() //nolint if resp.StatusCode != http.StatusOK { - b, _ := ioutil.ReadAll(resp.Body) + b, _ := io.ReadAll(resp.Body) log.Errorf("sending reader param (%s): non-200 status: %s, msg: '%s'", u.String(), resp.Status, string(b)) return } diff --git a/lib/unixfs/filestore.go b/lib/unixfs/filestore.go index acd0a62ac1b..0a0b61c4ca7 100644 --- a/lib/unixfs/filestore.go +++ b/lib/unixfs/filestore.go @@ -4,7 +4,6 @@ import ( "context" "fmt" "io" - "io/ioutil" "os" "github.com/ipfs/go-blockservice" @@ -68,7 +67,7 @@ func CreateFilestore(ctx context.Context, srcPath string, dstPath string) (cid.C return cid.Undef, xerrors.Errorf("failed to create reader path file: %w", err) } - f, err := ioutil.TempFile("", "") + f, err := os.CreateTemp("", "") if err != nil { return cid.Undef, xerrors.Errorf("failed to create temp file: %w", err) } diff --git a/lib/unixfs/filestore_test.go b/lib/unixfs/filestore_test.go index f9d5ed6566d..67d380701bd 100644 --- a/lib/unixfs/filestore_test.go +++ b/lib/unixfs/filestore_test.go @@ -5,7 +5,6 @@ import ( "bytes" "context" "io" - "io/ioutil" "os" "strings" "testing" @@ -67,7 +66,7 @@ func TestRoundtripUnixFS_Dense(t *testing.T) { // ensure contents of the initial input file and the output file are identical. fo, err := os.Open(tmpOutput) require.NoError(t, err) - bz2, err := ioutil.ReadAll(fo) + bz2, err := io.ReadAll(fo) require.NoError(t, err) require.NoError(t, fo.Close()) require.Equal(t, inputContents, bz2) @@ -107,7 +106,7 @@ func TestRoundtripUnixFS_Filestore(t *testing.T) { // ensure contents of the initial input file and the output file are identical. fo, err := os.Open(tmpOutput) require.NoError(t, err) - bz2, err := ioutil.ReadAll(fo) + bz2, err := io.ReadAll(fo) require.NoError(t, err) require.NoError(t, fo.Close()) require.Equal(t, inputContents, bz2) diff --git a/markets/dagstore/mount_test.go b/markets/dagstore/mount_test.go index e044603d4f5..d415f8d8856 100644 --- a/markets/dagstore/mount_test.go +++ b/markets/dagstore/mount_test.go @@ -4,7 +4,6 @@ package dagstore import ( "context" "io" - "io/ioutil" "net/url" "strings" "testing" @@ -39,7 +38,7 @@ func TestLotusMount(t *testing.T) { io.ReaderAt io.Seeker }{ - ReadCloser: ioutil.NopCloser(strings.NewReader("testing")), + ReadCloser: io.NopCloser(strings.NewReader("testing")), ReaderAt: nil, Seeker: nil, } @@ -48,7 +47,7 @@ func TestLotusMount(t *testing.T) { io.ReaderAt io.Seeker }{ - ReadCloser: ioutil.NopCloser(strings.NewReader("testing")), + ReadCloser: io.NopCloser(strings.NewReader("testing")), ReaderAt: nil, Seeker: nil, } @@ -66,7 +65,7 @@ func TestLotusMount(t *testing.T) { rd, err := mnt.Fetch(context.Background()) require.NoError(t, err) - bz, err := ioutil.ReadAll(rd) + bz, err := io.ReadAll(rd) require.NoError(t, err) require.NoError(t, rd.Close()) require.Equal(t, []byte("testing"), bz) @@ -85,7 +84,7 @@ func TestLotusMount(t *testing.T) { // fetching on this mount should get us back the same data. rd, err = mnt2.Fetch(context.Background()) require.NoError(t, err) - bz, err = ioutil.ReadAll(rd) + bz, err = io.ReadAll(rd) require.NoError(t, err) require.NoError(t, rd.Close()) require.Equal(t, []byte("testing"), bz) diff --git a/node/config/cfgdocgen/gen.go b/node/config/cfgdocgen/gen.go index 5133501523c..577e85f9da9 100644 --- a/node/config/cfgdocgen/gen.go +++ b/node/config/cfgdocgen/gen.go @@ -2,14 +2,13 @@ package main import ( "fmt" - "io/ioutil" "os" "sort" "strings" ) func run() error { - tfb, err := ioutil.ReadFile("./node/config/types.go") + tfb, err := os.ReadFile("./node/config/types.go") if err != nil { return err } diff --git a/node/config/load.go b/node/config/load.go index 29cb15d27fd..9133509120d 100644 --- a/node/config/load.go +++ b/node/config/load.go @@ -4,7 +4,6 @@ import ( "bytes" "fmt" "io" - "io/ioutil" "os" "reflect" "regexp" @@ -47,7 +46,7 @@ func FromFile(path string, opts ...LoadCfgOpt) (interface{}, error) { return nil, err } defer file.Close() //nolint:errcheck,staticcheck // The file is RO - cfgBs, err := ioutil.ReadAll(file) + cfgBs, err := io.ReadAll(file) if err != nil { return nil, xerrors.Errorf("failed to read config for validation checks %w", err) } diff --git a/node/config/load_test.go b/node/config/load_test.go index cab5268df6c..e17660c19f9 100644 --- a/node/config/load_test.go +++ b/node/config/load_test.go @@ -3,7 +3,6 @@ package config import ( "bytes" - "io/ioutil" "os" "testing" "time" @@ -50,7 +49,7 @@ func TestParitalConfig(t *testing.T) { } { - f, err := ioutil.TempFile("", "config-*.toml") + f, err := os.CreateTemp("", "config-*.toml") fname := f.Name() assert.NoError(err, "tmp file shold not error") @@ -115,7 +114,7 @@ func TestValidateConfigSetsEnableSplitstore(t *testing.T) { assert.False(t, MatchEnableSplitstoreField(string(cfgCommentedOutEnableSS))) // write config with commented out EnableSplitstore to file - f, err := ioutil.TempFile("", "config.toml") + f, err := os.CreateTemp("", "config.toml") fname := f.Name() assert.NoError(t, err) defer func() { @@ -132,7 +131,7 @@ func TestValidateConfigSetsEnableSplitstore(t *testing.T) { // Loading without a config file and a default fails if the default fallback is disabled func TestFailToFallbackToDefault(t *testing.T) { - dir, err := ioutil.TempDir("", "dirWithNoFiles") + dir, err := os.MkdirTemp("", "dirWithNoFiles") assert.NoError(t, err) defer assert.NoError(t, os.RemoveAll(dir)) nonExistantFileName := dir + "/notarealfile" diff --git a/node/config/storage.go b/node/config/storage.go index 2c9d880f92b..dfe067840af 100644 --- a/node/config/storage.go +++ b/node/config/storage.go @@ -3,7 +3,6 @@ package config import ( "encoding/json" "io" - "io/ioutil" "os" "golang.org/x/xerrors" @@ -43,7 +42,7 @@ func WriteStorageFile(path string, config storiface.StorageConfig) error { return xerrors.Errorf("marshaling storage config: %w", err) } - if err := ioutil.WriteFile(path, b, 0644); err != nil { + if err := os.WriteFile(path, b, 0644); err != nil { return xerrors.Errorf("persisting storage config (%s): %w", path, err) } diff --git a/node/impl/client/client_test.go b/node/impl/client/client_test.go index 98092bc93f9..032fef55a4c 100644 --- a/node/impl/client/client_test.go +++ b/node/impl/client/client_test.go @@ -5,7 +5,7 @@ import ( "bytes" "context" "embed" - "io/ioutil" + "os" "path/filepath" "strings" "testing" @@ -77,7 +77,7 @@ func TestImportLocal(t *testing.T) { }) require.NoError(t, err) - outBytes, err := ioutil.ReadFile(out1) + outBytes, err := os.ReadFile(out1) require.NoError(t, err) require.Equal(t, b, outBytes) @@ -128,7 +128,7 @@ func TestImportLocal(t *testing.T) { err = files.WriteTo(file, exportedPath) require.NoError(t, err) - exportedBytes, err := ioutil.ReadFile(exportedPath) + exportedBytes, err := os.ReadFile(exportedPath) require.NoError(t, err) // compare original file to recreated unixfs file. diff --git a/node/modules/core.go b/node/modules/core.go index c74cc2143c1..a0d52c291bc 100644 --- a/node/modules/core.go +++ b/node/modules/core.go @@ -5,7 +5,6 @@ import ( "crypto/rand" "errors" "io" - "io/ioutil" "os" "path/filepath" "time" @@ -147,7 +146,7 @@ func APISecret(keystore types.KeyStore, lr repo.LockedRepo) (*dtypes.APIAlg, err if errors.Is(err, types.ErrKeyInfoNotFound) { log.Warn("Generating new API secret") - sk, err := ioutil.ReadAll(io.LimitReader(rand.Reader, 32)) + sk, err := io.ReadAll(io.LimitReader(rand.Reader, 32)) if err != nil { return nil, err } diff --git a/node/modules/testing/genesis.go b/node/modules/testing/genesis.go index a3d25e36a1d..3876775df1a 100644 --- a/node/modules/testing/genesis.go +++ b/node/modules/testing/genesis.go @@ -5,7 +5,6 @@ import ( "encoding/json" "fmt" "io" - "io/ioutil" "os" "github.com/ipfs/go-blockservice" @@ -60,7 +59,7 @@ func MakeGenesis(outFile, genesisTemplate string) func(bs dtypes.ChainBlockstore return nil, err } - fdata, err := ioutil.ReadFile(genesisTemplate) + fdata, err := os.ReadFile(genesisTemplate) if err != nil { return nil, xerrors.Errorf("reading preseals json: %w", err) } diff --git a/node/repo/fsrepo.go b/node/repo/fsrepo.go index 2dbedd5e7d2..03ddd2d6cef 100644 --- a/node/repo/fsrepo.go +++ b/node/repo/fsrepo.go @@ -6,7 +6,6 @@ import ( "encoding/json" "fmt" "io" - "io/ioutil" "os" "path/filepath" "strings" @@ -329,7 +328,7 @@ func (fsr *FsRepo) APIEndpoint() (multiaddr.Multiaddr, error) { } defer f.Close() //nolint: errcheck // Read only op - data, err := ioutil.ReadAll(f) + data, err := io.ReadAll(f) if err != nil { return nil, xerrors.Errorf("failed to read %q: %w", p, err) } @@ -354,7 +353,7 @@ func (fsr *FsRepo) APIToken() ([]byte, error) { } defer f.Close() //nolint: errcheck // Read only op - tb, err := ioutil.ReadAll(f) + tb, err := io.ReadAll(f) if err != nil { return nil, err } @@ -582,7 +581,7 @@ func (fsr *fsLockedRepo) SetConfig(c func(interface{})) error { } // write buffer of TOML bytes to config file - err = ioutil.WriteFile(fsr.configPath, buf.Bytes(), 0644) + err = os.WriteFile(fsr.configPath, buf.Bytes(), 0644) if err != nil { return err } @@ -635,14 +634,14 @@ func (fsr *fsLockedRepo) SetAPIEndpoint(ma multiaddr.Multiaddr) error { if err := fsr.stillValid(); err != nil { return err } - return ioutil.WriteFile(fsr.join(fsAPI), []byte(ma.String()), 0644) + return os.WriteFile(fsr.join(fsAPI), []byte(ma.String()), 0644) } func (fsr *fsLockedRepo) SetAPIToken(token []byte) error { if err := fsr.stillValid(); err != nil { return err } - return ioutil.WriteFile(fsr.join(fsAPIToken), token, 0600) + return os.WriteFile(fsr.join(fsAPIToken), token, 0600) } func (fsr *fsLockedRepo) KeyStore() (types.KeyStore, error) { @@ -711,7 +710,7 @@ func (fsr *fsLockedRepo) Get(name string) (types.KeyInfo, error) { } defer file.Close() //nolint: errcheck // read only op - data, err := ioutil.ReadAll(file) + data, err := io.ReadAll(file) if err != nil { return types.KeyInfo{}, xerrors.Errorf("reading key '%s': %w", name, err) } @@ -760,7 +759,7 @@ func (fsr *fsLockedRepo) put(rawName string, info types.KeyInfo, retries int) er return xerrors.Errorf("encoding key '%s': %w", name, err) } - err = ioutil.WriteFile(keyPath, keyData, 0600) + err = os.WriteFile(keyPath, keyData, 0600) if err != nil { return xerrors.Errorf("writing key '%s': %w", name, err) } diff --git a/node/repo/memrepo.go b/node/repo/memrepo.go index 7817776a988..6a4b416e204 100644 --- a/node/repo/memrepo.go +++ b/node/repo/memrepo.go @@ -3,7 +3,6 @@ package repo import ( "context" "encoding/json" - "io/ioutil" "os" "path/filepath" "sync" @@ -103,7 +102,7 @@ func (lmem *lockedMemRepo) Path() string { return lmem.mem.tempDir } - t, err := ioutil.TempDir(os.TempDir(), "lotus-memrepo-temp-") + t, err := os.MkdirTemp(os.TempDir(), "lotus-memrepo-temp-") if err != nil { panic(err) // only used in tests, probably fine } @@ -142,7 +141,7 @@ func (lmem *lockedMemRepo) initSectorStore(t string) { panic(err) } - if err := ioutil.WriteFile(filepath.Join(t, "sectorstore.json"), b, 0644); err != nil { + if err := os.WriteFile(filepath.Join(t, "sectorstore.json"), b, 0644); err != nil { panic(err) } } diff --git a/storage/paths/http_handler_test.go b/storage/paths/http_handler_test.go index b03fd20ee49..4987936dd62 100644 --- a/storage/paths/http_handler_test.go +++ b/storage/paths/http_handler_test.go @@ -2,7 +2,7 @@ package paths_test import ( "fmt" - "io/ioutil" + "io" "net/http" "net/http/httptest" "os" @@ -378,7 +378,7 @@ func TestRemoteGetSector(t *testing.T) { if !tc.isDir { // create file - tempFile, err := ioutil.TempFile("", "TestRemoteGetSector-") + tempFile, err := os.CreateTemp("", "TestRemoteGetSector-") require.NoError(t, err) defer func() { @@ -390,7 +390,7 @@ func TestRemoteGetSector(t *testing.T) { path = tempFile.Name() } else { // create dir with a file - tempFile2, err := ioutil.TempFile("", "TestRemoteGetSector-") + tempFile2, err := os.CreateTemp("", "TestRemoteGetSector-") require.NoError(t, err) defer func() { _ = os.Remove(tempFile2.Name()) @@ -435,7 +435,7 @@ func TestRemoteGetSector(t *testing.T) { _ = resp.Body.Close() }() - bz, err := ioutil.ReadAll(resp.Body) + bz, err := io.ReadAll(resp.Body) require.NoError(t, err) // assert expected status code diff --git a/storage/paths/local.go b/storage/paths/local.go index 2182f24ef40..a866f5bbe58 100644 --- a/storage/paths/local.go +++ b/storage/paths/local.go @@ -3,7 +3,6 @@ package paths import ( "context" "encoding/json" - "io/ioutil" "math/bits" "math/rand" "os" @@ -151,7 +150,7 @@ func (st *Local) OpenPath(ctx context.Context, p string) error { st.localLk.Lock() defer st.localLk.Unlock() - mb, err := ioutil.ReadFile(filepath.Join(p, MetaFile)) + mb, err := os.ReadFile(filepath.Join(p, MetaFile)) if err != nil { return xerrors.Errorf("reading storage metadata for %s: %w", p, err) } @@ -247,7 +246,7 @@ func (st *Local) Redeclare(ctx context.Context, filterId *storiface.ID, dropMiss defer st.localLk.Unlock() for id, p := range st.paths { - mb, err := ioutil.ReadFile(filepath.Join(p.local, MetaFile)) + mb, err := os.ReadFile(filepath.Join(p.local, MetaFile)) if err != nil { return xerrors.Errorf("reading storage metadata for %s: %w", p.local, err) } diff --git a/storage/paths/local_test.go b/storage/paths/local_test.go index 6b9f4a54519..bfa138ff6f3 100644 --- a/storage/paths/local_test.go +++ b/storage/paths/local_test.go @@ -3,7 +3,6 @@ package paths import ( "context" "encoding/json" - "io/ioutil" "os" "path/filepath" "testing" @@ -63,7 +62,7 @@ func (t *TestingLocalStorage) init(subpath string) error { return err } - if err := ioutil.WriteFile(metaFile, mb, 0644); err != nil { + if err := os.WriteFile(metaFile, mb, 0644); err != nil { return err } diff --git a/storage/paths/remote.go b/storage/paths/remote.go index 06d1080b3af..852936153bc 100644 --- a/storage/paths/remote.go +++ b/storage/paths/remote.go @@ -5,7 +5,6 @@ import ( "encoding/json" "fmt" "io" - "io/ioutil" "math/bits" "net/http" "net/url" @@ -412,7 +411,7 @@ func (r *Remote) FsStat(ctx context.Context, id storiface.ID) (fsutil.FsStat, er case 404: return fsutil.FsStat{}, errPathNotFound case 500: - b, err := ioutil.ReadAll(resp.Body) + b, err := io.ReadAll(resp.Body) if err != nil { return fsutil.FsStat{}, xerrors.Errorf("fsstat: got http 500, then failed to read the error: %w", err) } @@ -768,7 +767,7 @@ func (r *Remote) GenerateSingleVanillaProof(ctx context.Context, minerID abi.Act log.Debugw("reading vanilla proof from remote not-found response", "url", url, "store", info.ID) continue } - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) if err != nil { return nil, xerrors.Errorf("resp.Body ReadAll: %w", err) } @@ -780,7 +779,7 @@ func (r *Remote) GenerateSingleVanillaProof(ctx context.Context, minerID abi.Act return nil, xerrors.Errorf("non-200 code from %s: '%s'", url, strings.TrimSpace(string(body))) } - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) if err != nil { if err := resp.Body.Close(); err != nil { log.Error("response close: ", err) diff --git a/storage/paths/remote_test.go b/storage/paths/remote_test.go index 2d7fe2c73d9..41d5e8a17a1 100644 --- a/storage/paths/remote_test.go +++ b/storage/paths/remote_test.go @@ -6,7 +6,6 @@ import ( "encoding/json" "fmt" "io" - "io/ioutil" "net/http" "net/http/httptest" "os" @@ -48,7 +47,7 @@ func createTestStorage(t *testing.T, p string, seal bool, att ...*paths.Local) s b, err := json.MarshalIndent(cfg, "", " ") require.NoError(t, err) - require.NoError(t, ioutil.WriteFile(filepath.Join(p, metaFile), b, 0644)) + require.NoError(t, os.WriteFile(filepath.Join(p, metaFile), b, 0644)) for _, s := range att { require.NoError(t, s.OpenPath(context.Background(), p)) @@ -130,7 +129,7 @@ func TestMoveShared(t *testing.T) { data := make([]byte, 2032) data[1] = 54 - require.NoError(t, ioutil.WriteFile(sp.Sealed, data, 0666)) + require.NoError(t, os.WriteFile(sp.Sealed, data, 0666)) fmt.Println("write to ", sp.Sealed) require.NoError(t, index.StorageDeclareSector(ctx, storiface.ID(sid.Sealed), s1ref.ID, storiface.FTSealed, true)) @@ -145,7 +144,7 @@ func TestMoveShared(t *testing.T) { require.Equal(t, id1, storiface.ID(sid.Sealed)) fmt.Println("read from ", sp.Sealed) - read, err := ioutil.ReadFile(sp.Sealed) + read, err := os.ReadFile(sp.Sealed) require.NoError(t, err) require.EqualValues(t, data, read) } @@ -357,7 +356,7 @@ func TestReader(t *testing.T) { mockCheckAllocation(pf, offset, size, emptyPartialFile, true, nil) - f, err := ioutil.TempFile("", "TestReader-") + f, err := os.CreateTemp("", "TestReader-") require.NoError(t, err) _, err = f.Write(bz) require.NoError(t, err) @@ -502,7 +501,7 @@ func TestReader(t *testing.T) { require.NoError(t, os.Remove(f.Name())) } - bz, err := ioutil.ReadAll(rd) + bz, err := io.ReadAll(rd) require.NoError(t, err) require.Equal(t, tc.expectedSectorBytes, bz) } diff --git a/storage/sealer/ffiwrapper/sealer_cgo.go b/storage/sealer/ffiwrapper/sealer_cgo.go index e9ce5746ed0..871012d0bed 100644 --- a/storage/sealer/ffiwrapper/sealer_cgo.go +++ b/storage/sealer/ffiwrapper/sealer_cgo.go @@ -11,7 +11,6 @@ import ( "encoding/base64" "encoding/json" "io" - "io/ioutil" "math/bits" "os" "path/filepath" @@ -1095,7 +1094,7 @@ func (sb *Sealer) FinalizeSectorInto(ctx context.Context, sector storiface.Secto } defer done() - files, err := ioutil.ReadDir(paths.Cache) + files, err := os.ReadDir(paths.Cache) if err != nil { return err } diff --git a/storage/sealer/ffiwrapper/sealer_test.go b/storage/sealer/ffiwrapper/sealer_test.go index dd0c1e1845c..34dea3b1b86 100644 --- a/storage/sealer/ffiwrapper/sealer_test.go +++ b/storage/sealer/ffiwrapper/sealer_test.go @@ -5,7 +5,6 @@ import ( "context" "fmt" "io" - "io/ioutil" "math/rand" "os" "path/filepath" @@ -130,7 +129,7 @@ func (s *seal) unseal(t *testing.T, sb *Sealer, sp *basicfs.Provider, si storifa t.Fatal(err) } - expect, _ := ioutil.ReadAll(data(si.ID.Number, 1016)) + expect, _ := io.ReadAll(data(si.ID.Number, 1016)) if !bytes.Equal(b.Bytes(), expect) { t.Fatal("read wrong bytes") } @@ -160,7 +159,7 @@ func (s *seal) unseal(t *testing.T, sb *Sealer, sp *basicfs.Provider, si storifa t.Fatal(err) } - expect, _ = ioutil.ReadAll(data(si.ID.Number, 1016)) + expect, _ = io.ReadAll(data(si.ID.Number, 1016)) require.Equal(t, expect, b.Bytes()) b.Reset() @@ -240,12 +239,12 @@ func corrupt(t *testing.T, sealer *Sealer, id storiface.SectorRef) { } func getGrothParamFileAndVerifyingKeys(s abi.SectorSize) { - dat, err := ioutil.ReadFile("../../../build/proof-params/parameters.json") + dat, err := os.ReadFile("../../../build/proof-params/parameters.json") if err != nil { panic(err) } - datSrs, err := ioutil.ReadFile("../../../build/proof-params/srs-inner-product.json") + datSrs, err := os.ReadFile("../../../build/proof-params/srs-inner-product.json") if err != nil { panic(err) } @@ -281,7 +280,7 @@ func TestSealAndVerify(t *testing.T) { getGrothParamFileAndVerifyingKeys(sectorSize) - cdir, err := ioutil.TempDir("", "sbtest-c-") + cdir, err := os.MkdirTemp("", "sbtest-c-") if err != nil { t.Fatal(err) } @@ -352,7 +351,7 @@ func TestSealPoStNoCommit(t *testing.T) { getGrothParamFileAndVerifyingKeys(sectorSize) - dir, err := ioutil.TempDir("", "sbtest") + dir, err := os.MkdirTemp("", "sbtest") if err != nil { t.Fatal(err) } @@ -416,7 +415,7 @@ func TestSealAndVerify3(t *testing.T) { getGrothParamFileAndVerifyingKeys(sectorSize) - dir, err := ioutil.TempDir("", "sbtest") + dir, err := os.MkdirTemp("", "sbtest") if err != nil { t.Fatal(err) } @@ -494,7 +493,7 @@ func TestSealAndVerifyAggregate(t *testing.T) { getGrothParamFileAndVerifyingKeys(sectorSize) - cdir, err := ioutil.TempDir("", "sbtest-c-") + cdir, err := os.MkdirTemp("", "sbtest-c-") if err != nil { t.Fatal(err) } @@ -576,7 +575,7 @@ func BenchmarkWriteWithAlignment(b *testing.B) { for i := 0; i < b.N; i++ { b.StopTimer() rf, w, _ := commpffi.ToReadableFile(bytes.NewReader(bytes.Repeat([]byte{0xff, 0}, int(bt/2))), int64(bt)) - tf, _ := ioutil.TempFile("/tmp/", "scrb-") + tf, _ := os.CreateTemp("/tmp/", "scrb-") b.StartTimer() ffi.WriteWithAlignment(abi.RegisteredSealProof_StackedDrg2KiBV1, rf, bt, tf, nil) // nolint:errcheck @@ -735,7 +734,7 @@ func TestGenerateUnsealedCID(t *testing.T) { func TestAddPiece512M(t *testing.T) { sz := abi.PaddedPieceSize(512 << 20).Unpadded() - cdir, err := ioutil.TempDir("", "sbtest-c-") + cdir, err := os.MkdirTemp("", "sbtest-c-") if err != nil { t.Fatal(err) } @@ -779,7 +778,7 @@ func BenchmarkAddPiece512M(b *testing.B) { sz := abi.PaddedPieceSize(512 << 20).Unpadded() b.SetBytes(int64(sz)) - cdir, err := ioutil.TempDir("", "sbtest-c-") + cdir, err := os.MkdirTemp("", "sbtest-c-") if err != nil { b.Fatal(err) } @@ -821,7 +820,7 @@ func BenchmarkAddPiece512M(b *testing.B) { func TestAddPiece512MPadded(t *testing.T) { sz := abi.PaddedPieceSize(512 << 20).Unpadded() - cdir, err := ioutil.TempDir("", "sbtest-c-") + cdir, err := os.MkdirTemp("", "sbtest-c-") if err != nil { t.Fatal(err) } @@ -890,7 +889,7 @@ func TestMulticoreSDR(t *testing.T) { getGrothParamFileAndVerifyingKeys(sectorSize) - dir, err := ioutil.TempDir("", "sbtest") + dir, err := os.MkdirTemp("", "sbtest") if err != nil { t.Fatal(err) } @@ -995,7 +994,7 @@ func TestPoStChallengeAssumptions(t *testing.T) { func TestDCAPCloses(t *testing.T) { sz := abi.PaddedPieceSize(2 << 10).Unpadded() - cdir, err := ioutil.TempDir("", "sbtest-c-") + cdir, err := os.MkdirTemp("", "sbtest-c-") if err != nil { t.Fatal(err) } diff --git a/storage/sealer/fr32/fr32_ffi_cmp_test.go b/storage/sealer/fr32/fr32_ffi_cmp_test.go index 7dece47232e..32afa470eba 100644 --- a/storage/sealer/fr32/fr32_ffi_cmp_test.go +++ b/storage/sealer/fr32/fr32_ffi_cmp_test.go @@ -3,7 +3,6 @@ package fr32_test import ( "bytes" "io" - "io/ioutil" "os" "testing" @@ -17,7 +16,7 @@ import ( ) func TestWriteTwoPcs(t *testing.T) { - tf, _ := ioutil.TempFile("/tmp/", "scrb-") + tf, _ := os.CreateTemp("/tmp/", "scrb-") paddedSize := abi.PaddedPieceSize(16 << 20) n := 2 @@ -43,7 +42,7 @@ func TestWriteTwoPcs(t *testing.T) { panic(err) } - ffiBytes, err := ioutil.ReadAll(tf) + ffiBytes, err := io.ReadAll(tf) if err != nil { panic(err) } diff --git a/storage/sealer/fr32/fr32_test.go b/storage/sealer/fr32/fr32_test.go index f9150e550cd..437fa4e43cb 100644 --- a/storage/sealer/fr32/fr32_test.go +++ b/storage/sealer/fr32/fr32_test.go @@ -3,7 +3,6 @@ package fr32_test import ( "bytes" "io" - "io/ioutil" "math/rand" "os" "testing" @@ -19,7 +18,7 @@ import ( func padFFI(buf []byte) []byte { rf, w, _ := commpffi.ToReadableFile(bytes.NewReader(buf), int64(len(buf))) - tf, _ := ioutil.TempFile("/tmp/", "scrb-") + tf, _ := os.CreateTemp("/tmp/", "scrb-") _, _, _, err := ffi.WriteWithAlignment(abi.RegisteredSealProof_StackedDrg32GiBV1, rf, abi.UnpaddedPieceSize(len(buf)), tf, nil) if err != nil { @@ -33,7 +32,7 @@ func padFFI(buf []byte) []byte { panic(err) } - padded, err := ioutil.ReadAll(tf) + padded, err := io.ReadAll(tf) if err != nil { panic(err) } diff --git a/storage/sealer/fr32/readers_test.go b/storage/sealer/fr32/readers_test.go index 21a5cd9cd83..f84b9d67a2a 100644 --- a/storage/sealer/fr32/readers_test.go +++ b/storage/sealer/fr32/readers_test.go @@ -3,7 +3,7 @@ package fr32_test import ( "bufio" "bytes" - "io/ioutil" + "io" "testing" "github.com/stretchr/testify/require" @@ -27,7 +27,7 @@ func TestUnpadReader(t *testing.T) { } // using bufio reader to make sure reads are big enough for the padreader - it can't handle small reads right now - readered, err := ioutil.ReadAll(bufio.NewReaderSize(r, 512)) + readered, err := io.ReadAll(bufio.NewReaderSize(r, 512)) if err != nil { t.Fatal(err) } diff --git a/storage/sealer/manager_test.go b/storage/sealer/manager_test.go index a44f69a898c..cdc135916f3 100644 --- a/storage/sealer/manager_test.go +++ b/storage/sealer/manager_test.go @@ -7,7 +7,6 @@ import ( "encoding/json" "fmt" "io" - "io/ioutil" "os" "path/filepath" "strings" @@ -46,7 +45,7 @@ func (t testStorage) DiskUsage(path string) (int64, error) { } func newTestStorage(t *testing.T) *testStorage { - tp, err := ioutil.TempDir(os.TempDir(), "sealer-test-") + tp, err := os.MkdirTemp(os.TempDir(), "sealer-test-") require.NoError(t, err) { @@ -58,7 +57,7 @@ func newTestStorage(t *testing.T) *testStorage { }, "", " ") require.NoError(t, err) - err = ioutil.WriteFile(filepath.Join(tp, "sectorstore.json"), b, 0644) + err = os.WriteFile(filepath.Join(tp, "sectorstore.json"), b, 0644) require.NoError(t, err) } diff --git a/storage/sealer/mock/mock.go b/storage/sealer/mock/mock.go index 6e88b86a58a..ab973b3b41e 100644 --- a/storage/sealer/mock/mock.go +++ b/storage/sealer/mock/mock.go @@ -6,7 +6,6 @@ import ( "crypto/sha256" "fmt" "io" - "io/ioutil" "math/rand" "sync" @@ -461,7 +460,7 @@ func (mgr *SectorMgr) ReadPiece(ctx context.Context, sector storiface.SectorRef, io.Seeker io.ReaderAt }{ - ReadCloser: ioutil.NopCloser(br), + ReadCloser: io.NopCloser(br), Seeker: br, ReaderAt: br, }, false, nil diff --git a/storage/sealer/piece_provider_test.go b/storage/sealer/piece_provider_test.go index ea2866e5d2d..4cbc79a93ed 100644 --- a/storage/sealer/piece_provider_test.go +++ b/storage/sealer/piece_provider_test.go @@ -3,7 +3,7 @@ package sealer import ( "bytes" "context" - "io/ioutil" + "io" "math/rand" "net" "net/http" @@ -346,7 +346,7 @@ func (p *pieceProviderTestHarness) readPiece(t *testing.T, offset storiface.Unpa defer func() { _ = rd.Close() }() // Make sure the input matches the output - readData, err := ioutil.ReadAll(rd) + readData, err := io.ReadAll(rd) require.NoError(t, err) require.Equal(t, expectedBytes, readData) } From edae783cf4128c3e8d27f5a12ffae467c8cca8d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 30 Mar 2023 09:50:06 +0200 Subject: [PATCH 137/243] fix: cli: Make `net connect` to miner address work --- cli/net.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/net.go b/cli/net.go index 6b10dbffcfc..2649791e701 100644 --- a/cli/net.go +++ b/cli/net.go @@ -367,7 +367,7 @@ func AddrInfoFromArg(ctx context.Context, cctx *cli.Context) ([]peer.AddrInfo, e pis = append(pis, pi) } - return pis, err + return pis, nil } var NetId = &cli.Command{ From 682ddf6ffa7ffd430c9e843a50428d46790f2daa Mon Sep 17 00:00:00 2001 From: adlrocha Date: Thu, 30 Mar 2023 12:38:00 +0200 Subject: [PATCH 138/243] Update chain/sub/bcast/consistent.go Co-authored-by: Aayush Rajasekaran --- chain/sub/bcast/consistent.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chain/sub/bcast/consistent.go b/chain/sub/bcast/consistent.go index c8c91e71a64..58e8bc98fd8 100644 --- a/chain/sub/bcast/consistent.go +++ b/chain/sub/bcast/consistent.go @@ -25,7 +25,7 @@ const ( GcLookback = 5 // GcDeepCheck determines the number of epochs in the past that we // we try cleaning in the deep garbage collection round. - GcDeepCheck = 2880 // (24h*60m*60s)/30s per block + GcDeepCheck = 2880 // (24h*60m*60s)/30s per epoch // GcDeepInterval determines after the number of epochs for which // we are going to start a deeper garbage collection round. GcDeepInterval = 1000 From 54a80a8a97e635d215c6dc6d9283bc17be48487a Mon Sep 17 00:00:00 2001 From: vyzo Date: Thu, 30 Mar 2023 18:11:44 +0300 Subject: [PATCH 139/243] revert dead code --- chain/consensus/compute_state.go | 5 ++--- chain/stmgr/call.go | 1 - chain/vm/execution.go | 4 +--- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/chain/consensus/compute_state.go b/chain/consensus/compute_state.go index 8442b55e9a7..056aa07250b 100644 --- a/chain/consensus/compute_state.go +++ b/chain/consensus/compute_state.go @@ -192,7 +192,6 @@ func (t *TipSetExecutor) ApplyBlocks(ctx context.Context, cronGas = 0 partDone = metrics.Timer(ctx, metrics.VMApplyMessages) - // TODO reorg the code to minimize the execution critical section vmi, err := makeVm(pstate, epoch, ts.MinTimestamp()) if err != nil { return cid.Undef, cid.Undef, xerrors.Errorf("making vm: %w", err) @@ -259,7 +258,7 @@ func (t *TipSetExecutor) ApplyBlocks(ctx context.Context, return cid.Cid{}, cid.Cid{}, err } - vmDoCron := partDone() + vmCron := partDone() partDone = metrics.Timer(ctx, metrics.VMApplyFlush) rectarr := blockadt.MakeEmptyArray(sm.ChainStore().ActorStore(ctx)) @@ -298,7 +297,7 @@ func (t *TipSetExecutor) ApplyBlocks(ctx context.Context, vmFlush := partDone() partDone = func() time.Duration { return time.Duration(0) } - log.Infow("ApplyBlocks stats", "early", vmEarly, "earlyCronGas", earlyCronGas, "vmMsg", vmMsg, "msgGas", msgGas, "vmCron", vmDoCron, "cronGas", cronGas, "vmFlush", vmFlush, "epoch", epoch, "tsk", ts.Key()) + log.Infow("ApplyBlocks stats", "early", vmEarly, "earlyCronGas", earlyCronGas, "vmMsg", vmMsg, "msgGas", msgGas, "vmCron", vmCron, "cronGas", cronGas, "vmFlush", vmFlush, "epoch", epoch, "tsk", ts.Key()) stats.Record(ctx, metrics.VMSends.M(int64(atomic.LoadUint64(&vm.StatSends))), metrics.VMApplied.M(int64(atomic.LoadUint64(&vm.StatApplied)))) diff --git a/chain/stmgr/call.go b/chain/stmgr/call.go index 816f50d1036..901fc2d1253 100644 --- a/chain/stmgr/call.go +++ b/chain/stmgr/call.go @@ -159,7 +159,6 @@ func (sm *StateManager) callInternal(ctx context.Context, msg *types.Message, pr if err != nil { return nil, xerrors.Errorf("failed to set up vm: %w", err) } - for i, m := range priorMsgs { _, err = vmi.ApplyMessage(ctx, m) if err != nil { diff --git a/chain/vm/execution.go b/chain/vm/execution.go index fb86a3a7d5e..dfa9d98d273 100644 --- a/chain/vm/execution.go +++ b/chain/vm/execution.go @@ -26,9 +26,7 @@ const ( DefaultPriorityExecutionLanes = 2 ) -var ErrExecutorDone = errors.New("executor has been released") - -// the execution environment; see below for definition, methods, and initilization +// the execution environment; see below for definition, methods, and initialization var execution *executionEnv // implementation of vm executor with simple sanity check preventing use after free. From 7b4e68249a2eec6c6fadf14992261682988f51b2 Mon Sep 17 00:00:00 2001 From: vyzo Date: Thu, 30 Mar 2023 18:13:08 +0300 Subject: [PATCH 140/243] add comment about Signal unsoundness --- chain/vm/execution.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/chain/vm/execution.go b/chain/vm/execution.go index dfa9d98d273..37abedb6dc4 100644 --- a/chain/vm/execution.go +++ b/chain/vm/execution.go @@ -125,6 +125,8 @@ func (e *executionEnv) putToken(token *executionToken) { 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. e.cond.Broadcast() metricsDown(metrics.VMExecutionRunning, token.lane) From d71b52825300037dc3bfebb26c96e798b23c91e0 Mon Sep 17 00:00:00 2001 From: vyzo Date: Thu, 30 Mar 2023 18:15:13 +0300 Subject: [PATCH 141/243] reorg initialization code for better readability, remove unused import --- chain/vm/execution.go | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/chain/vm/execution.go b/chain/vm/execution.go index 37abedb6dc4..ea3a9719341 100644 --- a/chain/vm/execution.go +++ b/chain/vm/execution.go @@ -2,7 +2,6 @@ package vm import ( "context" - "errors" "os" "strconv" "sync" @@ -154,23 +153,18 @@ func metricsAdjust(metric *stats.Int64Measure, lane ExecutionLane, delta int) { } func init() { - var available, priority int var err error - concurrency := os.Getenv("LOTUS_FVM_CONCURRENCY") - if concurrency == "" { - available = DefaultAvailableExecutionLanes - } else { + available := DefaultAvailableExecutionLanes + if concurrency := os.Getenv("LOTUS_FVM_CONCURRENCY"); concurrency != "" { available, err = strconv.Atoi(concurrency) if err != nil { panic(err) } } - reserved := os.Getenv("LOTUS_FVM_CONCURRENCY_RESERVED") - if reserved == "" { - priority = DefaultPriorityExecutionLanes - } else { + priority := DefaultPriorityExecutionLanes + if reserved := os.Getenv("LOTUS_FVM_CONCURRENCY_RESERVED"); reserved != "" { priority, err = strconv.Atoi(reserved) if err != nil { panic(err) From c7a6bc2fce11a6c6deafff72c40129e9a647085a Mon Sep 17 00:00:00 2001 From: Aayush Date: Thu, 30 Mar 2023 16:32:32 -0400 Subject: [PATCH 142/243] chore: deps: update to go-state-types v0.11.0-alpha-3 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index a4794c03a4f..764c3591a39 100644 --- a/go.mod +++ b/go.mod @@ -45,7 +45,7 @@ require ( github.com/filecoin-project/go-jsonrpc v0.2.3 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.10.0 + github.com/filecoin-project/go-state-types v0.11.0-alpha-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 1d9e1026268..817af5497f5 100644 --- a/go.sum +++ b/go.sum @@ -347,8 +347,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.8/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.10.0 h1:vsSThZIaPmOxNGG59+8D/HnlWRtlbdOjduH6ye+v8f0= -github.com/filecoin-project/go-state-types v0.10.0/go.mod h1:aLIas+W8BWAfpLWEPUOGMPBdhcVwoCG4pIQSQk26024= +github.com/filecoin-project/go-state-types v0.11.0-alpha-3 h1:LupJwuS2BdUEZbc0Q8N84N43O37XwTREDsdSTpzF1hg= +github.com/filecoin-project/go-state-types v0.11.0-alpha-3/go.mod h1:aLIas+W8BWAfpLWEPUOGMPBdhcVwoCG4pIQSQk26024= 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 661d8608bc4670837a5fe438779af541be05cb40 Mon Sep 17 00:00:00 2001 From: Jennifer Wang Date: Wed, 15 Mar 2023 11:53:00 -0400 Subject: [PATCH 143/243] evm account balances Please enter the commit message for your changes. Lines starting --- cmd/lotus-shed/evmbalance.go | 125 +++++++++++++++++++++++++++++++++++ cmd/lotus-shed/main.go | 1 + 2 files changed, 126 insertions(+) create mode 100644 cmd/lotus-shed/evmbalance.go diff --git a/cmd/lotus-shed/evmbalance.go b/cmd/lotus-shed/evmbalance.go new file mode 100644 index 00000000000..8436046207c --- /dev/null +++ b/cmd/lotus-shed/evmbalance.go @@ -0,0 +1,125 @@ +package main + +import ( + "context" + "fmt" + "io" + + "github.com/filecoin-project/go-state-types/big" + + "github.com/filecoin-project/lotus/chain/actors/builtin" + + "github.com/filecoin-project/go-state-types/abi" + "github.com/ipfs/go-cid" + 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/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" +) + +var evmBalanceCmd = &cli.Command{ + Name: "evm-balance", + Usage: "Balances in eth accounts, evm contracts and placeholders", + ArgsUsage: "[state root]", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "repo", + Value: "~/.lotus", + }, + }, + + Action: func(cctx *cli.Context) error { + if cctx.NArg() != 1 { + return xerrors.New("only needs state root") + } + + 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) + st, err := state.LoadStateTree(cst, sroot) + if err != nil { + return err + } + + fmt.Println("iterating over all actors") + count := 0 + tvlEvm := abi.NewTokenAmount(0) + tvlEthAccount := abi.NewTokenAmount(0) + tvlPlaceholder := abi.NewTokenAmount(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.IsEvmActor(act.Code) { + tvlEvm = types.BigAdd(tvlEvm, act.Balance) + } + + if builtin.IsEthAccountActor(act.Code) { + tvlEthAccount = types.BigAdd(tvlEthAccount, act.Balance) + } + + if builtin.IsPlaceholderActor(act.Code) { + tvlPlaceholder = types.BigAdd(tvlPlaceholder, act.Balance) + } + + return nil + }) + + fmt.Println("TVL in Eth contracts: ", tvlEvm) + fmt.Println("TVL in Eth accounts: ", tvlEthAccount) + fmt.Println("TVL in placeholder: ", tvlPlaceholder) + fmt.Println("Total TVL: ", big.Add(big.Add(tvlEthAccount, tvlPlaceholder), tvlEvm)) + return nil + }, +} diff --git a/cmd/lotus-shed/main.go b/cmd/lotus-shed/main.go index 6bb779859f1..c6efacf533e 100644 --- a/cmd/lotus-shed/main.go +++ b/cmd/lotus-shed/main.go @@ -85,6 +85,7 @@ func main() { gasTraceCmd, replayOfflineCmd, msgindexCmd, + evmBalanceCmd, } app := &cli.App{ From ec90ccf72e15994e00a25ea5e3ec037e5238ce2c Mon Sep 17 00:00:00 2001 From: Jennifer Wang Date: Wed, 15 Mar 2023 12:33:19 -0400 Subject: [PATCH 144/243] point to the hot store --- cmd/lotus-shed/evmbalance.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/lotus-shed/evmbalance.go b/cmd/lotus-shed/evmbalance.go index 8436046207c..baa6a516276 100644 --- a/cmd/lotus-shed/evmbalance.go +++ b/cmd/lotus-shed/evmbalance.go @@ -62,7 +62,7 @@ var evmBalanceCmd = &cli.Command{ defer lkrepo.Close() //nolint:errcheck - bs, err := lkrepo.Blockstore(ctx, repo.UniversalBlockstore) + bs, err := lkrepo.Blockstore(ctx, repo.HotBlockstore) if err != nil { return fmt.Errorf("failed to open blockstore: %w", err) } From 3b0d8fdd296f546281c40669cc6467a85539f105 Mon Sep 17 00:00:00 2001 From: zenground0 Date: Wed, 15 Mar 2023 11:19:10 -0600 Subject: [PATCH 145/243] P0 fix --- cmd/lotus-shed/evmbalance.go | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/cmd/lotus-shed/evmbalance.go b/cmd/lotus-shed/evmbalance.go index baa6a516276..c04f70896f7 100644 --- a/cmd/lotus-shed/evmbalance.go +++ b/cmd/lotus-shed/evmbalance.go @@ -3,10 +3,12 @@ package main import ( "context" "fmt" - "io" + "os" + "path/filepath" "github.com/filecoin-project/go-state-types/big" + badgerbs "github.com/filecoin-project/lotus/blockstore/badger" "github.com/filecoin-project/lotus/chain/actors/builtin" "github.com/filecoin-project/go-state-types/abi" @@ -17,9 +19,7 @@ import ( "github.com/filecoin-project/go-address" - "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" ) @@ -62,26 +62,25 @@ var evmBalanceCmd = &cli.Command{ defer lkrepo.Close() //nolint:errcheck - bs, err := lkrepo.Blockstore(ctx, repo.HotBlockstore) + path, err := lkrepo.SplitstorePath() if err != nil { - return fmt.Errorf("failed to open blockstore: %w", err) + return err } - defer func() { - if c, ok := bs.(io.Closer); ok { - if err := c.Close(); err != nil { - log.Warnf("failed to close blockstore: %s", err) - } - } - }() + path = filepath.Join(path, "hot.badger") + if err := os.MkdirAll(path, 0755); err != nil { + return err + } - mds, err := lkrepo.Datastore(context.Background(), "/metadata") + opts, err := repo.BadgerBlockstoreOptions(repo.HotBlockstore, path, lkrepo.Readonly()) if err != nil { return err } - cs := store.NewChainStore(bs, bs, mds, filcns.Weight, nil) - defer cs.Close() //nolint:errcheck + bs, err := badgerbs.Open(opts) + if err != nil { + return err + } cst := cbor.NewCborStore(bs) st, err := state.LoadStateTree(cst, sroot) From 1a5e8603c7586ab93d27444cd3b6069e312dbaa7 Mon Sep 17 00:00:00 2001 From: zenground0 Date: Wed, 15 Mar 2023 11:20:26 -0600 Subject: [PATCH 146/243] Fix dumb mistake --- cmd/lotus-shed/evmbalance.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/cmd/lotus-shed/evmbalance.go b/cmd/lotus-shed/evmbalance.go index c04f70896f7..3e817631ccc 100644 --- a/cmd/lotus-shed/evmbalance.go +++ b/cmd/lotus-shed/evmbalance.go @@ -1,7 +1,6 @@ package main import ( - "context" "fmt" "os" "path/filepath" @@ -40,7 +39,6 @@ var evmBalanceCmd = &cli.Command{ return xerrors.New("only needs state root") } - ctx := context.TODO() if !cctx.Args().Present() { return fmt.Errorf("must pass state root") } From 07c936746700f5dcd1fbb61da791f394f696ad9d Mon Sep 17 00:00:00 2001 From: Jennifer Wang Date: Wed, 15 Mar 2023 19:39:16 -0400 Subject: [PATCH 147/243] wording --- cmd/lotus-shed/evmbalance.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cmd/lotus-shed/evmbalance.go b/cmd/lotus-shed/evmbalance.go index 3e817631ccc..faf5a5ad561 100644 --- a/cmd/lotus-shed/evmbalance.go +++ b/cmd/lotus-shed/evmbalance.go @@ -113,10 +113,10 @@ var evmBalanceCmd = &cli.Command{ return nil }) - fmt.Println("TVL in Eth contracts: ", tvlEvm) - fmt.Println("TVL in Eth accounts: ", tvlEthAccount) - fmt.Println("TVL in placeholder: ", tvlPlaceholder) - fmt.Println("Total TVL: ", big.Add(big.Add(tvlEthAccount, tvlPlaceholder), tvlEvm)) + fmt.Println("balances in Eth contracts: ", tvlEvm) + fmt.Println("balances in Eth accounts: ", tvlEthAccount) + fmt.Println("balances in placeholder: ", tvlPlaceholder) + fmt.Println("Total balanace: ", big.Add(big.Add(tvlEthAccount, tvlPlaceholder), tvlEvm)) return nil }, } From 77883aa72bcc82ab5c64f76184934af30bc49a5d Mon Sep 17 00:00:00 2001 From: Jennifer Wang Date: Tue, 21 Mar 2023 20:44:34 -0400 Subject: [PATCH 148/243] add accounts --- cmd/lotus-shed/evmbalance.go | 122 ------------------ cmd/lotus-shed/fevmanalytics.go | 222 ++++++++++++++++++++++++++++++++ cmd/lotus-shed/main.go | 2 +- 3 files changed, 223 insertions(+), 123 deletions(-) delete mode 100644 cmd/lotus-shed/evmbalance.go create mode 100644 cmd/lotus-shed/fevmanalytics.go diff --git a/cmd/lotus-shed/evmbalance.go b/cmd/lotus-shed/evmbalance.go deleted file mode 100644 index faf5a5ad561..00000000000 --- a/cmd/lotus-shed/evmbalance.go +++ /dev/null @@ -1,122 +0,0 @@ -package main - -import ( - "fmt" - "os" - "path/filepath" - - "github.com/filecoin-project/go-state-types/big" - - badgerbs "github.com/filecoin-project/lotus/blockstore/badger" - "github.com/filecoin-project/lotus/chain/actors/builtin" - - "github.com/filecoin-project/go-state-types/abi" - "github.com/ipfs/go-cid" - 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/lotus/chain/state" - "github.com/filecoin-project/lotus/chain/types" - "github.com/filecoin-project/lotus/node/repo" -) - -var evmBalanceCmd = &cli.Command{ - Name: "evm-balance", - Usage: "Balances in eth accounts, evm contracts and placeholders", - ArgsUsage: "[state root]", - Flags: []cli.Flag{ - &cli.StringFlag{ - Name: "repo", - Value: "~/.lotus", - }, - }, - - Action: func(cctx *cli.Context) error { - if cctx.NArg() != 1 { - return xerrors.New("only needs state root") - } - - 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 - - path, err := lkrepo.SplitstorePath() - if err != nil { - return err - } - - path = filepath.Join(path, "hot.badger") - if err := os.MkdirAll(path, 0755); err != nil { - return err - } - - opts, err := repo.BadgerBlockstoreOptions(repo.HotBlockstore, path, lkrepo.Readonly()) - if err != nil { - return err - } - - bs, err := badgerbs.Open(opts) - if err != nil { - return err - } - - cst := cbor.NewCborStore(bs) - st, err := state.LoadStateTree(cst, sroot) - if err != nil { - return err - } - - fmt.Println("iterating over all actors") - count := 0 - tvlEvm := abi.NewTokenAmount(0) - tvlEthAccount := abi.NewTokenAmount(0) - tvlPlaceholder := abi.NewTokenAmount(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.IsEvmActor(act.Code) { - tvlEvm = types.BigAdd(tvlEvm, act.Balance) - } - - if builtin.IsEthAccountActor(act.Code) { - tvlEthAccount = types.BigAdd(tvlEthAccount, act.Balance) - } - - if builtin.IsPlaceholderActor(act.Code) { - tvlPlaceholder = types.BigAdd(tvlPlaceholder, act.Balance) - } - - return nil - }) - - fmt.Println("balances in Eth contracts: ", tvlEvm) - fmt.Println("balances in Eth accounts: ", tvlEthAccount) - fmt.Println("balances in placeholder: ", tvlPlaceholder) - fmt.Println("Total balanace: ", big.Add(big.Add(tvlEthAccount, tvlPlaceholder), tvlEvm)) - return nil - }, -} diff --git a/cmd/lotus-shed/fevmanalytics.go b/cmd/lotus-shed/fevmanalytics.go new file mode 100644 index 00000000000..c21cd1830ca --- /dev/null +++ b/cmd/lotus-shed/fevmanalytics.go @@ -0,0 +1,222 @@ +package main + +import ( + "fmt" + "os" + "path/filepath" + + "github.com/filecoin-project/go-state-types/big" + + badgerbs "github.com/filecoin-project/lotus/blockstore/badger" + "github.com/filecoin-project/lotus/chain/actors/builtin" + + "github.com/filecoin-project/go-state-types/abi" + "github.com/ipfs/go-cid" + 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/lotus/chain/state" + "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/node/repo" +) + +var FevmAnalyticsCmd = &cli.Command{ + Name: "evm-analytics", + Usage: "Get FEVM related metrics", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "repo", + Value: "~/.lotus", + }, + }, + Subcommands: []*cli.Command{ + FevmBalanceCmd, + FevmActorsCmd, + }, +} + +var FevmBalanceCmd = &cli.Command{ + Name: "evm-balance", + Usage: "Balances in eth accounts, evm contracts and placeholders", + ArgsUsage: "[state root]", + + Action: func(cctx *cli.Context) error { + if cctx.NArg() != 1 { + return xerrors.New("only needs state root") + } + + 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 + + path, err := lkrepo.SplitstorePath() + if err != nil { + return err + } + + path = filepath.Join(path, "hot.badger") + if err := os.MkdirAll(path, 0755); err != nil { + return err + } + + opts, err := repo.BadgerBlockstoreOptions(repo.HotBlockstore, path, lkrepo.Readonly()) + if err != nil { + return err + } + + bs, err := badgerbs.Open(opts) + if err != nil { + return err + } + + cst := cbor.NewCborStore(bs) + st, err := state.LoadStateTree(cst, sroot) + if err != nil { + return err + } + + fmt.Println("iterating over all actors") + count := 0 + balanceEvm := abi.NewTokenAmount(0) + balanceEthAccount := abi.NewTokenAmount(0) + balancePlaceholder := abi.NewTokenAmount(0) + + err = st.ForEach(func(addr address.Address, act *types.Actor) error { + if count%200000 == 0 { + fmt.Println("processed /n", count) + } + count++ + + if builtin.IsEvmActor(act.Code) { + balanceEvm = types.BigAdd(balanceEvm, act.Balance) + } + + if builtin.IsEthAccountActor(act.Code) { + balanceEthAccount = types.BigAdd(balanceEthAccount, act.Balance) + } + + if builtin.IsPlaceholderActor(act.Code) { + balancePlaceholder = types.BigAdd(balancePlaceholder, act.Balance) + } + + return nil + }) + + fmt.Println("balances in Eth contracts: ", balanceEvm) + fmt.Println("balances in Eth accounts: ", balanceEthAccount) + fmt.Println("balances in placeholder: ", balancePlaceholder) + fmt.Println("Total balances: ", big.Add(big.Add(balanceEthAccount, balancePlaceholder), balanceEvm)) + return nil + }, +} + +var FevmActorsCmd = &cli.Command{ + Name: "evm-actors", + Usage: "actors # in eth accounts, evm contracts and placeholders", + ArgsUsage: "[state root]", + + Action: func(cctx *cli.Context) error { + if cctx.NArg() != 1 { + return xerrors.New("only needs state root") + } + + 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 + + path, err := lkrepo.SplitstorePath() + if err != nil { + return err + } + + path = filepath.Join(path, "hot.badger") + if err := os.MkdirAll(path, 0755); err != nil { + return err + } + + opts, err := repo.BadgerBlockstoreOptions(repo.HotBlockstore, path, lkrepo.Readonly()) + if err != nil { + return err + } + + bs, err := badgerbs.Open(opts) + if err != nil { + return err + } + + cst := cbor.NewCborStore(bs) + st, err := state.LoadStateTree(cst, sroot) + if err != nil { + return err + } + + fmt.Println("iterating over all actors") + count := 0 + EvmCount := 0 + EthAccountCount := 0 + PlaceholderCount := 0 + + err = st.ForEach(func(addr address.Address, act *types.Actor) error { + if count%200000 == 0 { + fmt.Println("processed /n", count) + } + count++ + + if builtin.IsEvmActor(act.Code) { + EvmCount++ + } + + if builtin.IsEthAccountActor(act.Code) { + EthAccountCount++ + } + + if builtin.IsPlaceholderActor(act.Code) { + PlaceholderCount++ + } + + return nil + }) + + fmt.Println("# of Eth contracts: ", EvmCount) + fmt.Println("b# of Eth accounts: ", EthAccountCount) + fmt.Println("# of placeholder: ", PlaceholderCount) + return nil + }, +} diff --git a/cmd/lotus-shed/main.go b/cmd/lotus-shed/main.go index c6efacf533e..1f4d953f990 100644 --- a/cmd/lotus-shed/main.go +++ b/cmd/lotus-shed/main.go @@ -85,7 +85,7 @@ func main() { gasTraceCmd, replayOfflineCmd, msgindexCmd, - evmBalanceCmd, + FevmAnalyticsCmd, } app := &cli.App{ From 4a1eccd4475612d83ad3a664f84add08b5c6d3ed Mon Sep 17 00:00:00 2001 From: Jennifer Wang Date: Tue, 21 Mar 2023 21:31:02 -0400 Subject: [PATCH 149/243] add unique --- cmd/lotus-shed/fevmanalytics.go | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/cmd/lotus-shed/fevmanalytics.go b/cmd/lotus-shed/fevmanalytics.go index c21cd1830ca..f9e7545d774 100644 --- a/cmd/lotus-shed/fevmanalytics.go +++ b/cmd/lotus-shed/fevmanalytics.go @@ -5,6 +5,11 @@ import ( "os" "path/filepath" + evm2 "github.com/filecoin-project/lotus/chain/actors/builtin/evm" + + "github.com/filecoin-project/lotus/chain/actors/adt" + "golang.org/x/net/context" + "github.com/filecoin-project/go-state-types/big" badgerbs "github.com/filecoin-project/lotus/blockstore/badger" @@ -181,7 +186,10 @@ var FevmActorsCmd = &cli.Command{ return err } + ctx := context.TODO() cst := cbor.NewCborStore(bs) + store := adt.WrapStore(ctx, cst) + st, err := state.LoadStateTree(cst, sroot) if err != nil { return err @@ -192,6 +200,7 @@ var FevmActorsCmd = &cli.Command{ EvmCount := 0 EthAccountCount := 0 PlaceholderCount := 0 + ea := []cid.Cid{} err = st.ForEach(func(addr address.Address, act *types.Actor) error { if count%200000 == 0 { @@ -201,6 +210,12 @@ var FevmActorsCmd = &cli.Command{ if builtin.IsEvmActor(act.Code) { EvmCount++ + e, err := evm2.Load(store, act) + if err != nil { + return xerrors.Errorf("fail to load evm actor: %w", err) + } + bcid, err := e.GetBytecodeCID() + ea = append(ea, bcid) } if builtin.IsEthAccountActor(act.Code) { @@ -214,9 +229,23 @@ var FevmActorsCmd = &cli.Command{ return nil }) - fmt.Println("# of Eth contracts: ", EvmCount) + uniquesa := unique(ea) + fmt.Println("# of EVM contracts: ", EvmCount) + fmt.Println("# of unqiue EVM contracts: ", len(uniquesa)) fmt.Println("b# of Eth accounts: ", EthAccountCount) fmt.Println("# of placeholder: ", PlaceholderCount) return nil }, } + +func unique(intSlice []cid.Cid) []cid.Cid { + keys := make(map[cid.Cid]bool) + list := []cid.Cid{} + for _, entry := range intSlice { + if _, value := keys[entry]; !value { + keys[entry] = true + list = append(list, entry) + } + } + return list +} From 28fdc3abb8dae9e913e6116db6c16e5b9cc006b0 Mon Sep 17 00:00:00 2001 From: Jennifer Wang Date: Fri, 31 Mar 2023 15:13:13 -0400 Subject: [PATCH 150/243] docsgen --- cmd/lotus-shed/fevmanalytics.go | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/cmd/lotus-shed/fevmanalytics.go b/cmd/lotus-shed/fevmanalytics.go index f9e7545d774..bb2e38e4282 100644 --- a/cmd/lotus-shed/fevmanalytics.go +++ b/cmd/lotus-shed/fevmanalytics.go @@ -5,24 +5,20 @@ import ( "os" "path/filepath" - evm2 "github.com/filecoin-project/lotus/chain/actors/builtin/evm" - - "github.com/filecoin-project/lotus/chain/actors/adt" - "golang.org/x/net/context" - - "github.com/filecoin-project/go-state-types/big" - - badgerbs "github.com/filecoin-project/lotus/blockstore/badger" - "github.com/filecoin-project/lotus/chain/actors/builtin" - - "github.com/filecoin-project/go-state-types/abi" "github.com/ipfs/go-cid" cbor "github.com/ipfs/go-ipld-cbor" "github.com/urfave/cli/v2" + "golang.org/x/net/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" + badgerbs "github.com/filecoin-project/lotus/blockstore/badger" + "github.com/filecoin-project/lotus/chain/actors/adt" + "github.com/filecoin-project/lotus/chain/actors/builtin" + evm2 "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/node/repo" From b82726f57f6ce8c23339d044e43a518ee16fc015 Mon Sep 17 00:00:00 2001 From: Jennifer Wang Date: Fri, 31 Mar 2023 15:16:24 -0400 Subject: [PATCH 151/243] make lint happy --- cmd/lotus-shed/fevmanalytics.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/cmd/lotus-shed/fevmanalytics.go b/cmd/lotus-shed/fevmanalytics.go index bb2e38e4282..19416b77e12 100644 --- a/cmd/lotus-shed/fevmanalytics.go +++ b/cmd/lotus-shed/fevmanalytics.go @@ -122,6 +122,9 @@ var FevmBalanceCmd = &cli.Command{ return nil }) + if err != nil { + return err + } fmt.Println("balances in Eth contracts: ", balanceEvm) fmt.Println("balances in Eth accounts: ", balanceEthAccount) @@ -211,6 +214,10 @@ var FevmActorsCmd = &cli.Command{ return xerrors.Errorf("fail to load evm actor: %w", err) } bcid, err := e.GetBytecodeCID() + if err != nil { + return err + } + ea = append(ea, bcid) } @@ -224,6 +231,9 @@ var FevmActorsCmd = &cli.Command{ return nil }) + if err != nil { + return err + } uniquesa := unique(ea) fmt.Println("# of EVM contracts: ", EvmCount) From 2278a209e24aff59336723960082e15bd8d2e17a Mon Sep 17 00:00:00 2001 From: Shrenuj Bansal <108157875+shrenujbansal@users.noreply.github.com> Date: Sat, 1 Apr 2023 19:30:32 -0400 Subject: [PATCH 152/243] Add feature to stagger sector prove commit submission (#10543) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add feature to stagger sector prove commit submission * make gen and docsgen as usual * address comments and lint * Update comment Co-authored-by: Łukasz Magiera * make gen for stupid comment * make docsgen * address comments --------- Co-authored-by: Łukasz Magiera --- .../en/default-lotus-miner-config.toml | 10 +++++++++ node/config/doc_gen.go | 10 +++++++++ node/config/types.go | 7 ++++++ node/modules/storageminer.go | 22 ++++++++++--------- storage/pipeline/commit_batch.go | 22 +++++++++++++++++++ storage/pipeline/sealiface/config.go | 2 ++ 6 files changed, 63 insertions(+), 10 deletions(-) diff --git a/documentation/en/default-lotus-miner-config.toml b/documentation/en/default-lotus-miner-config.toml index 584def6086a..f75131af4e5 100644 --- a/documentation/en/default-lotus-miner-config.toml +++ b/documentation/en/default-lotus-miner-config.toml @@ -657,6 +657,16 @@ # env var: LOTUS_SEALING_AGGREGATEABOVEBASEFEE #AggregateAboveBaseFee = "0.00000000032 FIL" + # When submitting several sector prove commit messages simultaneously, this option allows you to + # stagger the number of prove commits submitted per epoch + # This is done because gas estimates for ProveCommits are non deterministic and increasing as a large + # number of sectors get committed within the same epoch resulting in occasionally failed msgs. + # Submitting a smaller number of prove commits per epoch would reduce the possibility of failed msgs + # + # type: uint64 + # env var: LOTUS_SEALING_MAXSECTORPROVECOMMITSSUBMITTEDPEREPOCH + #MaxSectorProveCommitsSubmittedPerEpoch = 0 + # type: uint64 # env var: LOTUS_SEALING_TERMINATEBATCHMAX #TerminateBatchMax = 100 diff --git a/node/config/doc_gen.go b/node/config/doc_gen.go index bc7b8a2701c..26a254bad67 100644 --- a/node/config/doc_gen.go +++ b/node/config/doc_gen.go @@ -1243,6 +1243,16 @@ sending precommit messages to the chain individually`, Comment: `network BaseFee below which to stop doing commit aggregation, instead submitting proofs to the chain individually`, + }, + { + Name: "MaxSectorProveCommitsSubmittedPerEpoch", + Type: "uint64", + + Comment: `When submitting several sector prove commit messages simultaneously, this option allows you to +stagger the number of prove commits submitted per epoch +This is done because gas estimates for ProveCommits are non deterministic and increasing as a large +number of sectors get committed within the same epoch resulting in occasionally failed msgs. +Submitting a smaller number of prove commits per epoch would reduce the possibility of failed msgs`, }, { Name: "TerminateBatchMax", diff --git a/node/config/types.go b/node/config/types.go index 51ef327d4a0..5cbd21bf3f8 100644 --- a/node/config/types.go +++ b/node/config/types.go @@ -422,6 +422,13 @@ type SealingConfig struct { // submitting proofs to the chain individually AggregateAboveBaseFee types.FIL + // When submitting several sector prove commit messages simultaneously, this option allows you to + // stagger the number of prove commits submitted per epoch + // This is done because gas estimates for ProveCommits are non deterministic and increasing as a large + // number of sectors get committed within the same epoch resulting in occasionally failed msgs. + // Submitting a smaller number of prove commits per epoch would reduce the possibility of failed msgs + MaxSectorProveCommitsSubmittedPerEpoch uint64 + TerminateBatchMax uint64 TerminateBatchMin uint64 TerminateBatchWait Duration diff --git a/node/modules/storageminer.go b/node/modules/storageminer.go index 4e4e2dde108..a4147d83d94 100644 --- a/node/modules/storageminer.go +++ b/node/modules/storageminer.go @@ -1014,9 +1014,10 @@ func NewSetSealConfigFunc(r repo.LockedRepo) (dtypes.SetSealingConfigFunc, error AggregateAboveBaseFee: types.FIL(cfg.AggregateAboveBaseFee), BatchPreCommitAboveBaseFee: types.FIL(cfg.BatchPreCommitAboveBaseFee), - TerminateBatchMax: cfg.TerminateBatchMax, - TerminateBatchMin: cfg.TerminateBatchMin, - TerminateBatchWait: config.Duration(cfg.TerminateBatchWait), + TerminateBatchMax: cfg.TerminateBatchMax, + TerminateBatchMin: cfg.TerminateBatchMin, + TerminateBatchWait: config.Duration(cfg.TerminateBatchWait), + MaxSectorProveCommitsSubmittedPerEpoch: cfg.MaxSectorProveCommitsSubmittedPerEpoch, } c.SetSealingConfig(newCfg) }) @@ -1051,13 +1052,14 @@ func ToSealingConfig(dealmakingCfg config.DealmakingConfig, sealingCfg config.Se PreCommitBatchWait: time.Duration(sealingCfg.PreCommitBatchWait), PreCommitBatchSlack: time.Duration(sealingCfg.PreCommitBatchSlack), - AggregateCommits: sealingCfg.AggregateCommits, - MinCommitBatch: sealingCfg.MinCommitBatch, - MaxCommitBatch: sealingCfg.MaxCommitBatch, - CommitBatchWait: time.Duration(sealingCfg.CommitBatchWait), - CommitBatchSlack: time.Duration(sealingCfg.CommitBatchSlack), - AggregateAboveBaseFee: types.BigInt(sealingCfg.AggregateAboveBaseFee), - BatchPreCommitAboveBaseFee: types.BigInt(sealingCfg.BatchPreCommitAboveBaseFee), + AggregateCommits: sealingCfg.AggregateCommits, + MinCommitBatch: sealingCfg.MinCommitBatch, + MaxCommitBatch: sealingCfg.MaxCommitBatch, + CommitBatchWait: time.Duration(sealingCfg.CommitBatchWait), + CommitBatchSlack: time.Duration(sealingCfg.CommitBatchSlack), + AggregateAboveBaseFee: types.BigInt(sealingCfg.AggregateAboveBaseFee), + BatchPreCommitAboveBaseFee: types.BigInt(sealingCfg.BatchPreCommitAboveBaseFee), + MaxSectorProveCommitsSubmittedPerEpoch: sealingCfg.MaxSectorProveCommitsSubmittedPerEpoch, TerminateBatchMax: sealingCfg.TerminateBatchMax, TerminateBatchMin: sealingCfg.TerminateBatchMin, diff --git a/storage/pipeline/commit_batch.go b/storage/pipeline/commit_batch.go index b5651c5fb83..572dff80854 100644 --- a/storage/pipeline/commit_batch.go +++ b/storage/pipeline/commit_batch.go @@ -388,6 +388,7 @@ func (b *CommitBatcher) processBatch(cfg sealiface.Config) ([]sealiface.CommitBa } func (b *CommitBatcher) processIndividually(cfg sealiface.Config) ([]sealiface.CommitBatchRes, error) { + mi, err := b.api.StateMinerInfo(b.mctx, b.maddr, types.EmptyTSK) if err != nil { return nil, xerrors.Errorf("couldn't get miner info: %w", err) @@ -414,12 +415,31 @@ func (b *CommitBatcher) processIndividually(cfg sealiface.Config) ([]sealiface.C var res []sealiface.CommitBatchRes + sectorsProcessed := 0 + for sn, info := range b.todo { r := sealiface.CommitBatchRes{ Sectors: []abi.SectorNumber{sn}, FailedSectors: map[abi.SectorNumber]string{}, } + if cfg.MaxSectorProveCommitsSubmittedPerEpoch > 0 && + uint64(sectorsProcessed) >= cfg.MaxSectorProveCommitsSubmittedPerEpoch { + + tmp := ts + for tmp.Height() <= ts.Height() { + tmp, err = b.api.ChainHead(b.mctx) + if err != nil { + log.Errorf("getting chain head: %+v", err) + return nil, err + } + time.Sleep(3 * time.Second) + } + + sectorsProcessed = 0 + ts = tmp + } + mcid, err := b.processSingle(cfg, mi, &avail, sn, info, ts.Key()) if err != nil { log.Errorf("process single error: %+v", err) // todo: return to user @@ -429,6 +449,8 @@ func (b *CommitBatcher) processIndividually(cfg sealiface.Config) ([]sealiface.C } res = append(res, r) + + sectorsProcessed++ } return res, nil diff --git a/storage/pipeline/sealiface/config.go b/storage/pipeline/sealiface/config.go index 2db155d5c11..67fba27a6a6 100644 --- a/storage/pipeline/sealiface/config.go +++ b/storage/pipeline/sealiface/config.go @@ -58,6 +58,8 @@ type Config struct { AggregateAboveBaseFee abi.TokenAmount BatchPreCommitAboveBaseFee abi.TokenAmount + MaxSectorProveCommitsSubmittedPerEpoch uint64 + TerminateBatchMax uint64 TerminateBatchMin uint64 TerminateBatchWait time.Duration From 8a2a18f43f6d9385f94cc65ce589f2965ec5875f Mon Sep 17 00:00:00 2001 From: Zeng Li <49380688@qq.com> Date: Sun, 2 Apr 2023 19:01:45 +0800 Subject: [PATCH 153/243] Fixed incorrect words that could not be compiled Milisecond is incorrect,resulting in failure to compile. --- build/params_2k.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/params_2k.go b/build/params_2k.go index 8220ce8aaf8..6307bf7a7f1 100644 --- a/build/params_2k.go +++ b/build/params_2k.go @@ -143,4 +143,4 @@ var WhitelistedBlock = cid.Undef // Reducing the delivery delay for equivocation of // consistent broadcast to just half a second. -var CBDeliveryDelay = 500 * time.Milisecond +var CBDeliveryDelay = 500 * time.Millisecond From d211b5eb69d97ef4743c6ec2db07eba6f6c41cf7 Mon Sep 17 00:00:00 2001 From: Phi Date: Mon, 3 Apr 2023 14:04:45 +0200 Subject: [PATCH 154/243] fix: cli: Check if the sector exists Check if the sector exists before running `SectorRemove`, and error out if the SectorID has not bee created. --- cmd/lotus-miner/sectors.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cmd/lotus-miner/sectors.go b/cmd/lotus-miner/sectors.go index 8d3a4c88470..a32f276a876 100644 --- a/cmd/lotus-miner/sectors.go +++ b/cmd/lotus-miner/sectors.go @@ -1392,6 +1392,12 @@ var sectorsRemoveCmd = &cli.Command{ return xerrors.Errorf("could not parse sector number: %w", err) } + // Check if the sector exists + _, err = minerAPI.SectorsStatus(ctx, abi.SectorNumber(id), false) + if err != nil { + return xerrors.Errorf("sectorID %d has not been created yet: %w", id, err) + } + return minerAPI.SectorRemove(ctx, abi.SectorNumber(id)) }, } From 0afd51510bceeefebdd858a85de77dec64412361 Mon Sep 17 00:00:00 2001 From: Aayush Date: Mon, 3 Apr 2023 14:05:45 -0400 Subject: [PATCH 155/243] fix: build: add CBDeliveryDelay to testground --- build/params_testground.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/build/params_testground.go b/build/params_testground.go index 17ea5a59bcb..4fea0394dc3 100644 --- a/build/params_testground.go +++ b/build/params_testground.go @@ -9,6 +9,7 @@ package build import ( "math/big" + "time" "github.com/ipfs/go-cid" @@ -135,3 +136,7 @@ const BootstrapPeerThreshold = 1 // ChainId defines the chain ID used in the Ethereum JSON-RPC endpoint. // As per https://github.com/ethereum-lists/chains const Eip155ChainId = 31415926 + +// Reducing the delivery delay for equivocation of +// consistent broadcast to just half a second. +var CBDeliveryDelay = 500 * time.Millisecond From 07dcc08ecb05cf22b5daf49f833212ebe504ea22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 4 Apr 2023 14:39:01 +0200 Subject: [PATCH 156/243] fix: make state compute --html work with unknown methods --- cli/state.go | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/cli/state.go b/cli/state.go index 3099bff17d6..9031ba870c5 100644 --- a/cli/state.go +++ b/cli/state.go @@ -1268,7 +1268,7 @@ var compStateMsg = ` {{end}} {{if ne .MsgRct.ExitCode 0}} -

+
Exit:
{{.MsgRct.ExitCode}}
{{end}}
@@ -1372,7 +1372,14 @@ func isVerySlow(t time.Duration) bool { } func JsonParams(code cid.Cid, method abi.MethodNum, params []byte) (string, error) { - p, err := stmgr.GetParamType(consensus.NewActorRegistry(), code, method) // todo use api for correct actor registry + ar := consensus.NewActorRegistry() + + _, found := ar.Methods[code][method] + if !found { + return fmt.Sprintf("raw:%x", params), nil + } + + p, err := stmgr.GetParamType(ar, code, method) // todo use api for correct actor registry if err != nil { return "", err } From c281d053d2b0731f4e09d24ea94ecae3524c9668 Mon Sep 17 00:00:00 2001 From: zenground0 Date: Tue, 4 Apr 2023 14:07:36 -0600 Subject: [PATCH 157/243] Limit moving gc threads --- blockstore/badger/blockstore.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/blockstore/badger/blockstore.go b/blockstore/badger/blockstore.go index 1b52eb548d8..5527bf89076 100644 --- a/blockstore/badger/blockstore.go +++ b/blockstore/badger/blockstore.go @@ -396,6 +396,9 @@ func (b *Blockstore) doCopy(from, to *badger.DB) error { if workers < 2 { workers = 2 } + if workers > 8 { + workers = 8 + } stream := from.NewStream() stream.NumGo = workers From d1364caa84f34b5a7c45ac46ab78d91165988d5b Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Sat, 8 Apr 2023 14:57:28 -0700 Subject: [PATCH 158/243] fix: cap the message gas limit at the block gas limit (#10637) Technically, if a message is near the block gas limit, this method could over-estimate past the block gas limit. Instead, cap at the block gas limit. --- node/impl/full/gas.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/node/impl/full/gas.go b/node/impl/full/gas.go index 43e04deeac9..b07d658e296 100644 --- a/node/impl/full/gas.go +++ b/node/impl/full/gas.go @@ -388,6 +388,10 @@ func (m *GasModule) GasEstimateMessageGas(ctx context.Context, msg *types.Messag return nil, err } msg.GasLimit = int64(float64(gasLimit) * m.Mpool.GetConfig().GasLimitOverestimation) + // Gas overestimation can cause us to exceed the block gas limit, cap it. + if msg.GasLimit > build.BlockGasLimit { + msg.GasLimit = build.BlockGasLimit + } } if msg.GasPremium == types.EmptyInt || types.BigCmp(msg.GasPremium, types.NewInt(0)) == 0 { From 9fd69377dfc611032e03a9e64dcf07ea6997b2f7 Mon Sep 17 00:00:00 2001 From: Phi Date: Mon, 10 Apr 2023 09:53:19 +0200 Subject: [PATCH 159/243] fix: unseal: check if sealed sector exists Check if sealed or update sector exists when `SectorsUnsealPiece` is called. --- storage/sealer/manager.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/storage/sealer/manager.go b/storage/sealer/manager.go index db5f9a589ba..3f496b7de45 100644 --- a/storage/sealer/manager.go +++ b/storage/sealer/manager.go @@ -319,6 +319,15 @@ func (m *Manager) SectorsUnsealPiece(ctx context.Context, sector storiface.Secto return xerrors.Errorf("acquiring unseal sector lock: %w", err) } + // Check if sealed or update sector file exists + s, err := m.index.StorageFindSector(ctx, sector.ID, storiface.FTSealed|storiface.FTUpdate, 0, false) + if err != nil { + return xerrors.Errorf("finding sealed or updated sector: %w", err) + } + if len(s) == 0 { + return xerrors.Errorf("sealed or updated sector file not found for sector %d", sector.ID) + } + // if the selected worker does NOT have the sealed files for the sector, instruct it to fetch it from a worker that has them and // put it in the sealing scratch space. sealFetch := PrepareAction{ From da6b565dc1db0565d47a2769754b24c86d9c3bc4 Mon Sep 17 00:00:00 2001 From: ZenGround0 <5515260+ZenGround0@users.noreply.github.com> Date: Mon, 10 Apr 2023 10:13:22 -0400 Subject: [PATCH 160/243] Update config default value (#10605) Co-authored-by: zenground0 --- documentation/en/default-lotus-config.toml | 2 +- node/config/def.go | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/documentation/en/default-lotus-config.toml b/documentation/en/default-lotus-config.toml index 1d6ae8cfba4..cd933235605 100644 --- a/documentation/en/default-lotus-config.toml +++ b/documentation/en/default-lotus-config.toml @@ -242,7 +242,7 @@ # # type: uint64 # env var: LOTUS_CHAINSTORE_SPLITSTORE_HOTSTOREMAXSPACETARGET - #HotStoreMaxSpaceTarget = 0 + #HotStoreMaxSpaceTarget = 650000000000 # When HotStoreMaxSpaceTarget is set Moving GC will be triggered when total moving size # exceeds HotstoreMaxSpaceTarget - HotstoreMaxSpaceThreshold diff --git a/node/config/def.go b/node/config/def.go index 2617ec5ba2f..4020e4ca615 100644 --- a/node/config/def.go +++ b/node/config/def.go @@ -96,6 +96,7 @@ func DefaultFullNode() *FullNode { MarkSetType: "badger", HotStoreFullGCFrequency: 20, + HotStoreMaxSpaceTarget: 650_000_000_000, HotStoreMaxSpaceThreshold: 150_000_000_000, HotstoreMaxSpaceSafetyBuffer: 50_000_000_000, }, From 1e25d7b4531f8c20a28ada7d277a0f0b85e1c29b Mon Sep 17 00:00:00 2001 From: Shrenuj Bansal Date: Mon, 10 Apr 2023 14:52:39 -0400 Subject: [PATCH 161/243] Split precommit batches if gas used exceeds block limit --- node/impl/full/gas.go | 3 ++ storage/pipeline/precommit_batch.go | 50 ++++++++++++++++++++++------- storage/pipeline/sealing.go | 1 + storage/pipeline/utils.go | 15 +++++++++ 4 files changed, 57 insertions(+), 12 deletions(-) diff --git a/node/impl/full/gas.go b/node/impl/full/gas.go index b07d658e296..33d0478734f 100644 --- a/node/impl/full/gas.go +++ b/node/impl/full/gas.go @@ -384,10 +384,13 @@ func gasEstimateGasLimit( func (m *GasModule) GasEstimateMessageGas(ctx context.Context, msg *types.Message, spec *api.MessageSendSpec, _ types.TipSetKey) (*types.Message, error) { if msg.GasLimit == 0 { gasLimit, err := m.GasEstimateGasLimit(ctx, msg, types.EmptyTSK) + log.Errorf("GasEstimateMessageGas GasLimit: %f", gasLimit) if err != nil { return nil, err } msg.GasLimit = int64(float64(gasLimit) * m.Mpool.GetConfig().GasLimitOverestimation) + + log.Errorf("GasEstimateMessageGas GasLimit: %f, GasLimitWithOverestimation", gasLimit, msg.GasLimit) // Gas overestimation can cause us to exceed the block gas limit, cap it. if msg.GasLimit > build.BlockGasLimit { msg.GasLimit = build.BlockGasLimit diff --git a/storage/pipeline/precommit_batch.go b/storage/pipeline/precommit_batch.go index 9bc6243291b..6817df0e5ee 100644 --- a/storage/pipeline/precommit_batch.go +++ b/storage/pipeline/precommit_batch.go @@ -31,6 +31,7 @@ import ( type PreCommitBatcherApi interface { MpoolPushMessage(context.Context, *types.Message, *api.MessageSendSpec) (*types.SignedMessage, error) + GasEstimateMessageGas(context.Context, *types.Message, *api.MessageSendSpec, types.TipSetKey) (*types.Message, error) StateMinerInfo(context.Context, address.Address, types.TipSetKey) (api.MinerInfo, error) StateMinerAvailableBalance(context.Context, address.Address, types.TipSetKey) (big.Int, error) ChainHead(ctx context.Context) (*types.TipSet, error) @@ -319,17 +320,13 @@ func (b *PreCommitBatcher) processSingle(cfg sealiface.Config, mi api.MinerInfo, return mcid, nil } -func (b *PreCommitBatcher) processBatch(cfg sealiface.Config, tsk types.TipSetKey, bf abi.TokenAmount, nv network.Version) ([]sealiface.PreCommitBatchRes, error) { +func (b *PreCommitBatcher) processPreCommitBatch(cfg sealiface.Config, bf abi.TokenAmount, entries []*preCommitEntry, nv network.Version) ([]sealiface.PreCommitBatchRes, error) { + log.Errorf("!!!!!!!!!!!!!!!!processPreCommitBatch: %d entries!!!!!!!!!!!!!!!!", len(entries)) params := miner.PreCommitSectorBatchParams{} deposit := big.Zero() var res sealiface.PreCommitBatchRes - for _, p := range b.todo { - if len(params.Sectors) >= cfg.MaxPreCommitBatch { - log.Infow("precommit batch full") - break - } - + for _, p := range entries { res.Sectors = append(res.Sectors, p.pci.SectorNumber) params.Sectors = append(params.Sectors, *infoToPreCommitSectorParams(p.pci)) deposit = big.Add(deposit, p.deposit) @@ -368,18 +365,47 @@ func (b *PreCommitBatcher) processBatch(cfg sealiface.Config, tsk types.TipSetKe return []sealiface.PreCommitBatchRes{res}, xerrors.Errorf("no good address found: %w", err) } - mcid, err := sendMsg(b.mctx, b.api, from, b.maddr, builtin.MethodsMiner.PreCommitSectorBatch, needFunds, maxFee, enc.Bytes()) - if err != nil { - return []sealiface.PreCommitBatchRes{res}, xerrors.Errorf("sending message failed: %w", err) + msg, err := simulateMsgGas(b.mctx, b.api, from, b.maddr, builtin.MethodsMiner.PreCommitSectorBatch, deposit, maxFee, enc.Bytes()) + + var gasLimit int64 + if msg != nil { + gasLimit = msg.GasLimit + } + log.Errorf("!!!!!!!!!!!!!!simulate Msg err: %w, gasLimit: %d !!!!!!!!!!!!!!!!!!!!", err, gasLimit) + 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) } - res.Msg = &mcid + // If we're out of gas, split the batch in half and try again + if api.ErrorIsIn(err, []error{&api.ErrOutOfGas{}}) { + mid := len(entries) / 2 + ret0, err := b.processPreCommitBatch(cfg, bf, entries[:mid], nv) + ret1, err := b.processPreCommitBatch(cfg, bf, entries[mid:], nv) - log.Infow("Sent PreCommitSectorBatch message", "cid", mcid, "from", from, "sectors", len(b.todo)) + return append(ret0, ret1...), err + } + // If state call succeeds, we can send the message for real + mcid, err := sendMsg(b.mctx, b.api, from, b.maddr, builtin.MethodsMiner.PreCommitSectorBatch, deposit, maxFee, enc.Bytes()) + log.Errorf("!!!!!!!!!!!!!!sendMsg err: %w, mcid: %v!!!!!!!!!!!!!!!!!!!!", err, mcid) + if err != nil { + res.Error = err.Error() + return []sealiface.PreCommitBatchRes{res}, xerrors.Errorf("pushing message to mpool: %w", err) + } + res.Msg = &mcid return []sealiface.PreCommitBatchRes{res}, nil } +func (b *PreCommitBatcher) processBatch(cfg sealiface.Config, tsk types.TipSetKey, bf abi.TokenAmount, nv network.Version) ([]sealiface.PreCommitBatchRes, error) { + var pcEntries []*preCommitEntry + for _, p := range b.todo { + pcEntries = append(pcEntries, p) + } + + return b.processPreCommitBatch(cfg, bf, pcEntries, nv) +} + // register PreCommit, wait for batch message, return message CID func (b *PreCommitBatcher) AddPreCommit(ctx context.Context, s SectorInfo, deposit abi.TokenAmount, in *miner.SectorPreCommitInfo) (res sealiface.PreCommitBatchRes, err error) { ts, err := b.api.ChainHead(b.mctx) diff --git a/storage/pipeline/sealing.go b/storage/pipeline/sealing.go index 0fadb61314d..d664de1e2a9 100644 --- a/storage/pipeline/sealing.go +++ b/storage/pipeline/sealing.go @@ -64,6 +64,7 @@ type SealingAPI interface { StateMinerDeadlines(context.Context, address.Address, types.TipSetKey) ([]api.Deadline, error) StateMinerPartitions(ctx context.Context, m address.Address, dlIdx uint64, tsk types.TipSetKey) ([]api.Partition, error) MpoolPushMessage(context.Context, *types.Message, *api.MessageSendSpec) (*types.SignedMessage, error) + GasEstimateMessageGas(context.Context, *types.Message, *api.MessageSendSpec, types.TipSetKey) (*types.Message, error) ChainHead(ctx context.Context) (*types.TipSet, error) ChainGetMessage(ctx context.Context, mc cid.Cid) (*types.Message, error) StateGetRandomnessFromBeacon(ctx context.Context, personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte, tsk types.TipSetKey) (abi.Randomness, error) diff --git a/storage/pipeline/utils.go b/storage/pipeline/utils.go index 3f8d534cb39..ce4283b6cd5 100644 --- a/storage/pipeline/utils.go +++ b/storage/pipeline/utils.go @@ -94,6 +94,21 @@ func collateralSendAmount(ctx context.Context, api interface { return collateral, nil } +func simulateMsgGas(ctx context.Context, sa interface { + GasEstimateMessageGas(context.Context, *types.Message, *api.MessageSendSpec, types.TipSetKey) (*types.Message, error) +}, + from, to address.Address, method abi.MethodNum, value, maxFee abi.TokenAmount, params []byte) (*types.Message, error) { + msg := types.Message{ + To: to, + From: from, + Value: value, + Method: method, + Params: params, + } + + return sa.GasEstimateMessageGas(ctx, &msg, nil, types.EmptyTSK) +} + func sendMsg(ctx context.Context, sa interface { MpoolPushMessage(context.Context, *types.Message, *api.MessageSendSpec) (*types.SignedMessage, error) }, from, to address.Address, method abi.MethodNum, value, maxFee abi.TokenAmount, params []byte) (cid.Cid, error) { From 8893c62a428843904621fede1914331d13ff392f Mon Sep 17 00:00:00 2001 From: Shrenuj Bansal Date: Mon, 10 Apr 2023 15:39:42 -0400 Subject: [PATCH 162/243] make gen --- storage/pipeline/mocks/api.go | 15 +++++++++++++++ storage/pipeline/mocks/mock_precommit_batcher.go | 15 +++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/storage/pipeline/mocks/api.go b/storage/pipeline/mocks/api.go index 066fe996eec..5c67a1c42e2 100644 --- a/storage/pipeline/mocks/api.go +++ b/storage/pipeline/mocks/api.go @@ -94,6 +94,21 @@ func (mr *MockSealingAPIMockRecorder) ChainReadObj(arg0, arg1 interface{}) *gomo return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ChainReadObj", reflect.TypeOf((*MockSealingAPI)(nil).ChainReadObj), arg0, arg1) } +// GasEstimateMessageGas mocks base method. +func (m *MockSealingAPI) GasEstimateMessageGas(arg0 context.Context, arg1 *types.Message, arg2 *api.MessageSendSpec, arg3 types.TipSetKey) (*types.Message, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GasEstimateMessageGas", arg0, arg1, arg2, arg3) + ret0, _ := ret[0].(*types.Message) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GasEstimateMessageGas indicates an expected call of GasEstimateMessageGas. +func (mr *MockSealingAPIMockRecorder) GasEstimateMessageGas(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GasEstimateMessageGas", reflect.TypeOf((*MockSealingAPI)(nil).GasEstimateMessageGas), arg0, arg1, arg2, arg3) +} + // MpoolPushMessage mocks base method. func (m *MockSealingAPI) MpoolPushMessage(arg0 context.Context, arg1 *types.Message, arg2 *api.MessageSendSpec) (*types.SignedMessage, error) { m.ctrl.T.Helper() diff --git a/storage/pipeline/mocks/mock_precommit_batcher.go b/storage/pipeline/mocks/mock_precommit_batcher.go index 2d514eb7eb0..68cce7fb0f3 100644 --- a/storage/pipeline/mocks/mock_precommit_batcher.go +++ b/storage/pipeline/mocks/mock_precommit_batcher.go @@ -58,6 +58,21 @@ func (mr *MockPreCommitBatcherApiMockRecorder) ChainHead(arg0 interface{}) *gomo return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ChainHead", reflect.TypeOf((*MockPreCommitBatcherApi)(nil).ChainHead), arg0) } +// GasEstimateMessageGas mocks base method. +func (m *MockPreCommitBatcherApi) GasEstimateMessageGas(arg0 context.Context, arg1 *types.Message, arg2 *api.MessageSendSpec, arg3 types.TipSetKey) (*types.Message, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GasEstimateMessageGas", arg0, arg1, arg2, arg3) + ret0, _ := ret[0].(*types.Message) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GasEstimateMessageGas indicates an expected call of GasEstimateMessageGas. +func (mr *MockPreCommitBatcherApiMockRecorder) GasEstimateMessageGas(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GasEstimateMessageGas", reflect.TypeOf((*MockPreCommitBatcherApi)(nil).GasEstimateMessageGas), arg0, arg1, arg2, arg3) +} + // MpoolPushMessage mocks base method. func (m *MockPreCommitBatcherApi) MpoolPushMessage(arg0 context.Context, arg1 *types.Message, arg2 *api.MessageSendSpec) (*types.SignedMessage, error) { m.ctrl.T.Helper() From 6f91dc7c5b2ba3cb8f25be0197be5222c965016a Mon Sep 17 00:00:00 2001 From: Shrenuj Bansal Date: Mon, 10 Apr 2023 16:24:13 -0400 Subject: [PATCH 163/243] populate result error on exit conditions --- storage/pipeline/precommit_batch.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/storage/pipeline/precommit_batch.go b/storage/pipeline/precommit_batch.go index 6817df0e5ee..dc50737a757 100644 --- a/storage/pipeline/precommit_batch.go +++ b/storage/pipeline/precommit_batch.go @@ -334,11 +334,13 @@ func (b *PreCommitBatcher) processPreCommitBatch(cfg sealiface.Config, bf abi.To enc := new(bytes.Buffer) if err := params.MarshalCBOR(enc); err != nil { + res.Error = err.Error() return []sealiface.PreCommitBatchRes{res}, xerrors.Errorf("couldn't serialize PreCommitSectorBatchParams: %w", err) } mi, err := b.api.StateMinerInfo(b.mctx, b.maddr, types.EmptyTSK) if err != nil { + res.Error = err.Error() return []sealiface.PreCommitBatchRes{res}, xerrors.Errorf("couldn't get miner info: %w", err) } @@ -347,6 +349,7 @@ func (b *PreCommitBatcher) processPreCommitBatch(cfg sealiface.Config, bf abi.To aggFeeRaw, err := policy.AggregatePreCommitNetworkFee(nv, len(params.Sectors), bf) if err != nil { log.Errorf("getting aggregate precommit network fee: %s", err) + res.Error = err.Error() return []sealiface.PreCommitBatchRes{res}, xerrors.Errorf("getting aggregate precommit network fee: %s", err) } @@ -380,8 +383,8 @@ func (b *PreCommitBatcher) processPreCommitBatch(cfg sealiface.Config, bf abi.To // If we're out of gas, split the batch in half and try again if api.ErrorIsIn(err, []error{&api.ErrOutOfGas{}}) { mid := len(entries) / 2 - ret0, err := b.processPreCommitBatch(cfg, bf, entries[:mid], nv) - ret1, err := b.processPreCommitBatch(cfg, bf, entries[mid:], nv) + ret0, _ := b.processPreCommitBatch(cfg, bf, entries[:mid], nv) + ret1, _ := b.processPreCommitBatch(cfg, bf, entries[mid:], nv) return append(ret0, ret1...), err } From b4e2e871dcb5d6a70bc569456b6c62678a708eea Mon Sep 17 00:00:00 2001 From: vyzo Date: Tue, 11 Apr 2023 13:12:05 +0300 Subject: [PATCH 164/243] prune excess messages before selection --- chain/messagepool/selection.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/chain/messagepool/selection.go b/chain/messagepool/selection.go index d510cf9508d..e42cc781275 100644 --- a/chain/messagepool/selection.go +++ b/chain/messagepool/selection.go @@ -47,6 +47,16 @@ func (mp *MessagePool) SelectMessages(ctx context.Context, ts *types.TipSet, tq mp.lk.Lock() defer mp.lk.Unlock() + // See if we need to prune before selection; excessive buildup can lead to slow selection, + // so prune if we have too many messages (ignoring the cooldown). + mpCfg := mp.getConfig() + if mp.currentSize > mpCfg.SizeLimitHigh { + log.Infof("too many messages; pruning before selection") + if err := mp.pruneMessages(ctx, ts); err != nil { + log.Warnf("error pruning excess messages: %s", err) + } + } + // if the ticket quality is high enough that the first block has higher probability // than any other block, then we don't bother with optimal selection because the // first block will always have higher effective performance From 4eb4af639aabc865bd4f1410a3769080cf64f97c Mon Sep 17 00:00:00 2001 From: Shrenuj Bansal Date: Wed, 12 Apr 2023 00:30:19 -0400 Subject: [PATCH 165/243] Split PCA msg into smaller batches --- storage/pipeline/commit_batch.go | 48 +++++++++++++++---- storage/pipeline/mocks/mock_commit_batcher.go | 15 ++++++ storage/pipeline/precommit_batch_test.go | 1 + 3 files changed, 55 insertions(+), 9 deletions(-) diff --git a/storage/pipeline/commit_batch.go b/storage/pipeline/commit_batch.go index 572dff80854..8adb7f3cd0e 100644 --- a/storage/pipeline/commit_batch.go +++ b/storage/pipeline/commit_batch.go @@ -37,6 +37,7 @@ var aggFeeDen = big.NewInt(100) type CommitBatcherApi interface { MpoolPushMessage(context.Context, *types.Message, *api.MessageSendSpec) (*types.SignedMessage, error) + GasEstimateMessageGas(context.Context, *types.Message, *api.MessageSendSpec, types.TipSetKey) (*types.Message, error) StateMinerInfo(context.Context, address.Address, types.TipSetKey) (api.MinerInfo, error) ChainHead(ctx context.Context) (*types.TipSet, error) @@ -234,7 +235,11 @@ func (b *CommitBatcher) maybeStartBatch(notif bool) ([]sealiface.CommitBatchRes, if individual { res, err = b.processIndividually(cfg) } else { - res, err = b.processBatch(cfg) + var sectors []abi.SectorNumber + for sn := range b.todo { + sectors = append(sectors, sn) + } + res, err = b.processBatch(cfg, sectors) } if err != nil { @@ -264,13 +269,13 @@ func (b *CommitBatcher) maybeStartBatch(notif bool) ([]sealiface.CommitBatchRes, return res, nil } -func (b *CommitBatcher) processBatch(cfg sealiface.Config) ([]sealiface.CommitBatchRes, error) { +func (b *CommitBatcher) processBatch(cfg sealiface.Config, sectors []abi.SectorNumber) ([]sealiface.CommitBatchRes, error) { ts, err := b.api.ChainHead(b.mctx) if err != nil { return nil, err } - total := len(b.todo) + total := len(sectors) res := sealiface.CommitBatchRes{ FailedSectors: map[abi.SectorNumber]string{}, @@ -284,24 +289,24 @@ func (b *CommitBatcher) processBatch(cfg sealiface.Config) ([]sealiface.CommitBa infos := make([]proof.AggregateSealVerifyInfo, 0, total) collateral := big.Zero() - for id, p := range b.todo { + for _, sector := range sectors { if len(infos) >= cfg.MaxCommitBatch { log.Infow("commit batch full") break } - res.Sectors = append(res.Sectors, id) + res.Sectors = append(res.Sectors, sector) - sc, err := b.getSectorCollateral(id, ts.Key()) + sc, err := b.getSectorCollateral(sector, ts.Key()) if err != nil { - res.FailedSectors[id] = err.Error() + res.FailedSectors[sector] = err.Error() continue } collateral = big.Add(collateral, sc) - params.SectorNumbers.Set(uint64(id)) - infos = append(infos, p.Info) + params.SectorNumbers.Set(uint64(sector)) + infos = append(infos, b.todo[sector].Info) } if len(infos) == 0 { @@ -318,17 +323,20 @@ func (b *CommitBatcher) processBatch(cfg sealiface.Config) ([]sealiface.CommitBa mid, err := address.IDFromAddress(b.maddr) if err != nil { + res.Error = err.Error() 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() return []sealiface.CommitBatchRes{res}, xerrors.Errorf("getting aggregate proof type: %w", err) } @@ -339,16 +347,19 @@ func (b *CommitBatcher) processBatch(cfg sealiface.Config) ([]sealiface.CommitBa Infos: infos, }, proofs) if err != nil { + res.Error = err.Error() return []sealiface.CommitBatchRes{res}, xerrors.Errorf("aggregating proofs: %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 ProveCommitAggregateParams: %w", err) } 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) } @@ -356,6 +367,7 @@ func (b *CommitBatcher) processBatch(cfg sealiface.Config) ([]sealiface.CommitBa 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) } @@ -365,6 +377,7 @@ func (b *CommitBatcher) processBatch(cfg sealiface.Config) ([]sealiface.CommitBa 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 } @@ -372,9 +385,26 @@ func (b *CommitBatcher) processBatch(cfg sealiface.Config) ([]sealiface.CommitBa 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) } + _, 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) { + res.Error = err.Error() + return []sealiface.CommitBatchRes{res}, xerrors.Errorf("simulating CommitBatch message failed: %w", err) + } + + // If we're out of gas, split the batch in half and try again + if api.ErrorIsIn(err, []error{&api.ErrOutOfGas{}}) { + mid := len(sectors) / 2 + ret0, _ := b.processBatch(cfg, sectors[:mid]) + ret1, _ := b.processBatch(cfg, sectors[mid:]) + + return append(ret0, ret1...), err + } + mcid, err := sendMsg(b.mctx, b.api, from, b.maddr, builtin.MethodsMiner.ProveCommitAggregate, needFunds, maxFee, enc.Bytes()) if err != nil { return []sealiface.CommitBatchRes{res}, xerrors.Errorf("sending message failed: %w", err) diff --git a/storage/pipeline/mocks/mock_commit_batcher.go b/storage/pipeline/mocks/mock_commit_batcher.go index c4e7e3eef60..431a47c73d9 100644 --- a/storage/pipeline/mocks/mock_commit_batcher.go +++ b/storage/pipeline/mocks/mock_commit_batcher.go @@ -58,6 +58,21 @@ func (mr *MockCommitBatcherApiMockRecorder) ChainHead(arg0 interface{}) *gomock. return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ChainHead", reflect.TypeOf((*MockCommitBatcherApi)(nil).ChainHead), arg0) } +// GasEstimateMessageGas mocks base method. +func (m *MockCommitBatcherApi) GasEstimateMessageGas(arg0 context.Context, arg1 *types.Message, arg2 *api.MessageSendSpec, arg3 types.TipSetKey) (*types.Message, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GasEstimateMessageGas", arg0, arg1, arg2, arg3) + ret0, _ := ret[0].(*types.Message) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GasEstimateMessageGas indicates an expected call of GasEstimateMessageGas. +func (mr *MockCommitBatcherApiMockRecorder) GasEstimateMessageGas(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GasEstimateMessageGas", reflect.TypeOf((*MockCommitBatcherApi)(nil).GasEstimateMessageGas), arg0, arg1, arg2, arg3) +} + // MpoolPushMessage mocks base method. func (m *MockCommitBatcherApi) MpoolPushMessage(arg0 context.Context, arg1 *types.Message, arg2 *api.MessageSendSpec) (*types.SignedMessage, error) { m.ctrl.T.Helper() diff --git a/storage/pipeline/precommit_batch_test.go b/storage/pipeline/precommit_batch_test.go index 1779128bd62..b9c02530fa1 100644 --- a/storage/pipeline/precommit_batch_test.go +++ b/storage/pipeline/precommit_batch_test.go @@ -162,6 +162,7 @@ func TestPrecommitBatcher(t *testing.T) { s.EXPECT().StateNetworkVersion(gomock.Any(), gomock.Any()).Return(network.Version14, nil) s.EXPECT().StateMinerInfo(gomock.Any(), gomock.Any(), gomock.Any()).Return(api.MinerInfo{Owner: t0123, Worker: t0123}, 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) var params miner6.PreCommitSectorBatchParams From 7a4f69721ce972b1c8792dfeed9d252cf33b605f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 12 Apr 2023 16:05:01 -0700 Subject: [PATCH 166/243] fix: storage: Remove temp fetching files after failed fetch --- storage/paths/remote.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/storage/paths/remote.go b/storage/paths/remote.go index 852936153bc..06bd39bf16e 100644 --- a/storage/paths/remote.go +++ b/storage/paths/remote.go @@ -236,6 +236,10 @@ func (r *Remote) acquireFromRemote(ctx context.Context, s abi.SectorID, fileType err = r.fetchThrottled(ctx, url, tempDest) if err != nil { merr = multierror.Append(merr, xerrors.Errorf("fetch error %s (storage %s) -> %s: %w", url, info.ID, tempDest, err)) + // fetching failed, remove temp file + if rerr := os.RemoveAll(tempDest); rerr != nil { + merr = multierror.Append(merr, xerrors.Errorf("removing temp dest (post-err cleanup): %w", rerr)) + } continue } From 79826447f582211560e9e71f24f7d954451c37cf Mon Sep 17 00:00:00 2001 From: Shrenuj Bansal Date: Wed, 12 Apr 2023 21:45:43 -0400 Subject: [PATCH 167/243] fix unit and integration test breaks --- storage/pipeline/commit_batch.go | 4 +++- storage/pipeline/commit_batch_test.go | 1 + storage/pipeline/precommit_batch.go | 14 ++++---------- 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/storage/pipeline/commit_batch.go b/storage/pipeline/commit_batch.go index 8adb7f3cd0e..09f1357d722 100644 --- a/storage/pipeline/commit_batch.go +++ b/storage/pipeline/commit_batch.go @@ -392,17 +392,19 @@ func (b *CommitBatcher) processBatch(cfg sealiface.Config, sectors []abi.SectorN _, 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) res.Error = err.Error() return []sealiface.CommitBatchRes{res}, xerrors.Errorf("simulating CommitBatch message failed: %w", err) } // If we're out of gas, split the batch in half and try again 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:]) - return append(ret0, ret1...), err + return append(ret0, ret1...), nil } mcid, err := sendMsg(b.mctx, b.api, from, b.maddr, builtin.MethodsMiner.ProveCommitAggregate, needFunds, maxFee, enc.Bytes()) diff --git a/storage/pipeline/commit_batch_test.go b/storage/pipeline/commit_batch_test.go index a8948edcf76..ef45b6b7144 100644 --- a/storage/pipeline/commit_batch_test.go +++ b/storage/pipeline/commit_batch_test.go @@ -201,6 +201,7 @@ func TestCommitBatcher(t *testing.T) { 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: 1000000}, nil) //s.EXPECT().ChainBaseFee(gomock.Any(), gomock.Any()).Return(basefee, nil) } diff --git a/storage/pipeline/precommit_batch.go b/storage/pipeline/precommit_batch.go index dc50737a757..869c4feb57b 100644 --- a/storage/pipeline/precommit_batch.go +++ b/storage/pipeline/precommit_batch.go @@ -321,7 +321,6 @@ func (b *PreCommitBatcher) processSingle(cfg sealiface.Config, mi api.MinerInfo, } func (b *PreCommitBatcher) processPreCommitBatch(cfg sealiface.Config, bf abi.TokenAmount, entries []*preCommitEntry, nv network.Version) ([]sealiface.PreCommitBatchRes, error) { - log.Errorf("!!!!!!!!!!!!!!!!processPreCommitBatch: %d entries!!!!!!!!!!!!!!!!", len(entries)) params := miner.PreCommitSectorBatchParams{} deposit := big.Zero() var res sealiface.PreCommitBatchRes @@ -368,13 +367,8 @@ func (b *PreCommitBatcher) processPreCommitBatch(cfg sealiface.Config, bf abi.To return []sealiface.PreCommitBatchRes{res}, xerrors.Errorf("no good address found: %w", err) } - msg, err := simulateMsgGas(b.mctx, b.api, from, b.maddr, builtin.MethodsMiner.PreCommitSectorBatch, deposit, maxFee, enc.Bytes()) + _, err = simulateMsgGas(b.mctx, b.api, from, b.maddr, builtin.MethodsMiner.PreCommitSectorBatch, needFunds, maxFee, enc.Bytes()) - var gasLimit int64 - if msg != nil { - gasLimit = msg.GasLimit - } - log.Errorf("!!!!!!!!!!!!!!simulate Msg err: %w, gasLimit: %d !!!!!!!!!!!!!!!!!!!!", err, gasLimit) 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) @@ -382,16 +376,16 @@ func (b *PreCommitBatcher) processPreCommitBatch(cfg sealiface.Config, bf abi.To // If we're out of gas, split the batch in half and try again if api.ErrorIsIn(err, []error{&api.ErrOutOfGas{}}) { + log.Warnf("PreCommitBatch out of gas, splitting batch in half and trying again") mid := len(entries) / 2 ret0, _ := b.processPreCommitBatch(cfg, bf, entries[:mid], nv) ret1, _ := b.processPreCommitBatch(cfg, bf, entries[mid:], nv) - return append(ret0, ret1...), err + return append(ret0, ret1...), nil } // If state call succeeds, we can send the message for real - mcid, err := sendMsg(b.mctx, b.api, from, b.maddr, builtin.MethodsMiner.PreCommitSectorBatch, deposit, maxFee, enc.Bytes()) - log.Errorf("!!!!!!!!!!!!!!sendMsg err: %w, mcid: %v!!!!!!!!!!!!!!!!!!!!", err, mcid) + mcid, err := sendMsg(b.mctx, b.api, from, b.maddr, builtin.MethodsMiner.PreCommitSectorBatch, needFunds, maxFee, enc.Bytes()) if err != nil { res.Error = err.Error() return []sealiface.PreCommitBatchRes{res}, xerrors.Errorf("pushing message to mpool: %w", err) From 3f74840b67d7e84949939bf281c7427f4500875e Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 13 Apr 2023 17:20:34 -0700 Subject: [PATCH 168/243] test: events: fix race when recording tipsets (#10665) fixes #10664 --- chain/events/events_test.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/chain/events/events_test.go b/chain/events/events_test.go index ad2dc8e7199..e2450909c6f 100644 --- a/chain/events/events_test.go +++ b/chain/events/events_test.go @@ -174,13 +174,16 @@ func (fcs *fakeCS) makeTs(t *testing.T, parents []cid.Cid, h abi.ChainEpoch, msg }, }) + require.NoError(t, err) + + fcs.mu.Lock() + defer fcs.mu.Unlock() + if fcs.tipsets == nil { fcs.tipsets = map[types.TipSetKey]*types.TipSet{} } fcs.tipsets[ts.Key()] = ts - require.NoError(t, err) - return ts } From 0befed72004df873b8b8a03358b93ceb32d0c9de Mon Sep 17 00:00:00 2001 From: Shrenuj Bansal <108157875+shrenujbansal@users.noreply.github.com> Date: Mon, 17 Apr 2023 12:12:15 -0400 Subject: [PATCH 169/243] Add API and CLI to unseal sector (#10626) --- api/api_storage.go | 2 ++ api/proxy_gen.go | 13 +++++++++++ build/openrpc/full.json.gz | Bin 33856 -> 33857 bytes build/openrpc/gateway.json.gz | Bin 9537 -> 9538 bytes build/openrpc/miner.json.gz | Bin 15866 -> 15921 bytes build/openrpc/worker.json.gz | Bin 5224 -> 5224 bytes cmd/lotus-miner/sectors.go | 25 +++++++++++++++++++++ documentation/en/api-v0-methods-miner.md | 16 ++++++++++++++ documentation/en/cli-lotus-miner.md | 14 ++++++++++++ node/impl/storminer.go | 27 +++++++++++++++++++++++ 10 files changed, 97 insertions(+) diff --git a/api/api_storage.go b/api/api_storage.go index 9e65c1ced7d..a9e632998da 100644 --- a/api/api_storage.go +++ b/api/api_storage.go @@ -129,6 +129,8 @@ type StorageMiner interface { SectorMatchPendingPiecesToOpenSectors(ctx context.Context) error //perm:admin // SectorAbortUpgrade can be called on sectors that are in the process of being upgraded to abort it SectorAbortUpgrade(context.Context, abi.SectorNumber) error //perm:admin + // SectorUnseal unseals the provided sector + SectorUnseal(ctx context.Context, number abi.SectorNumber) error //perm:admin // SectorNumAssignerMeta returns sector number assigner metadata - reserved/allocated SectorNumAssignerMeta(ctx context.Context) (NumAssignerMeta, error) //perm:read diff --git a/api/proxy_gen.go b/api/proxy_gen.go index 538e5815833..303284febaf 100644 --- a/api/proxy_gen.go +++ b/api/proxy_gen.go @@ -1085,6 +1085,8 @@ type StorageMinerMethods struct { SectorTerminatePending func(p0 context.Context) ([]abi.SectorID, error) `perm:"admin"` + SectorUnseal func(p0 context.Context, p1 abi.SectorNumber) error `perm:"admin"` + SectorsList func(p0 context.Context) ([]abi.SectorNumber, error) `perm:"read"` SectorsListInStates func(p0 context.Context, p1 []SectorState) ([]abi.SectorNumber, error) `perm:"read"` @@ -6424,6 +6426,17 @@ func (s *StorageMinerStub) SectorTerminatePending(p0 context.Context) ([]abi.Sec return *new([]abi.SectorID), ErrNotSupported } +func (s *StorageMinerStruct) SectorUnseal(p0 context.Context, p1 abi.SectorNumber) error { + if s.Internal.SectorUnseal == nil { + return ErrNotSupported + } + return s.Internal.SectorUnseal(p0, p1) +} + +func (s *StorageMinerStub) SectorUnseal(p0 context.Context, p1 abi.SectorNumber) error { + return ErrNotSupported +} + func (s *StorageMinerStruct) SectorsList(p0 context.Context) ([]abi.SectorNumber, error) { if s.Internal.SectorsList == nil { return *new([]abi.SectorNumber), ErrNotSupported diff --git a/build/openrpc/full.json.gz b/build/openrpc/full.json.gz index c5d21c6da07f5c98dfb1a47cdba20964b2955cb3..e56fd0fdae6e539bd29666c71d040c2998b06652 100644 GIT binary patch delta 29474 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 delta 6968 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 XY&<@GdHjC?009601(YlV>JkG0Qk0b% diff --git a/build/openrpc/worker.json.gz b/build/openrpc/worker.json.gz index bdea2d97e12d5b015cba88845e201843443b4e29..ea2e3066f8858fa4e6e8f0a096f8d5827571a996 100644 GIT binary patch literal 5224 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 diff --git a/cmd/lotus-miner/sectors.go b/cmd/lotus-miner/sectors.go index a32f276a876..61e491a97b7 100644 --- a/cmd/lotus-miner/sectors.go +++ b/cmd/lotus-miner/sectors.go @@ -67,6 +67,7 @@ var sectorsCmd = &cli.Command{ sectorsBatching, sectorsRefreshPieceMatchingCmd, sectorsCompactPartitionsCmd, + sectorsUnsealCmd, }, } @@ -2254,3 +2255,27 @@ var sectorsNumbersFreeCmd = &cli.Command{ return minerAPI.SectorNumFree(ctx, cctx.Args().First()) }, } + +var sectorsUnsealCmd = &cli.Command{ + Name: "unseal", + Usage: "unseal a sector", + ArgsUsage: "[sector number]", + 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) + } + + sectorNum, err := strconv.ParseUint(cctx.Args().Get(0), 10, 64) + if err != nil { + return xerrors.Errorf("could not parse sector number: %w", err) + } + + return minerAPI.SectorUnseal(ctx, abi.SectorNumber(sectorNum)) + }, +} diff --git a/documentation/en/api-v0-methods-miner.md b/documentation/en/api-v0-methods-miner.md index 1a149930502..d0b0e045d7c 100644 --- a/documentation/en/api-v0-methods-miner.md +++ b/documentation/en/api-v0-methods-miner.md @@ -159,6 +159,7 @@ * [SectorTerminate](#SectorTerminate) * [SectorTerminateFlush](#SectorTerminateFlush) * [SectorTerminatePending](#SectorTerminatePending) + * [SectorUnseal](#SectorUnseal) * [Sectors](#Sectors) * [SectorsList](#SectorsList) * [SectorsListInStates](#SectorsListInStates) @@ -3415,6 +3416,21 @@ Response: ] ``` +### SectorUnseal +SectorUnseal unseals the provided sector + + +Perms: admin + +Inputs: +```json +[ + 9 +] +``` + +Response: `{}` + ## Sectors diff --git a/documentation/en/cli-lotus-miner.md b/documentation/en/cli-lotus-miner.md index f3595dcc1dc..d304dc11da9 100644 --- a/documentation/en/cli-lotus-miner.md +++ b/documentation/en/cli-lotus-miner.md @@ -1671,6 +1671,7 @@ COMMANDS: batching manage batch sector operations match-pending-pieces force a refreshed match of pending pieces to open sectors without manually waiting for more deals compact-partitions removes dead sectors from partitions and reduces the number of partitions used if possible + unseal unseal a sector help, h Shows a list of commands or help for one command OPTIONS: @@ -2086,6 +2087,19 @@ OPTIONS: ``` +### lotus-miner sectors unseal +``` +NAME: + lotus-miner sectors unseal - unseal a sector + +USAGE: + lotus-miner sectors unseal [command options] [sector number] + +OPTIONS: + --help, -h show help (default: false) + +``` + ## lotus-miner proving ``` NAME: diff --git a/node/impl/storminer.go b/node/impl/storminer.go index 44ef7ee6a36..e4fa41c788a 100644 --- a/node/impl/storminer.go +++ b/node/impl/storminer.go @@ -254,6 +254,33 @@ func (sm *StorageMinerAPI) SectorsUnsealPiece(ctx context.Context, sector storif return sm.StorageMgr.SectorsUnsealPiece(ctx, sector, offset, size, randomness, commd) } +func (sm *StorageMinerAPI) SectorUnseal(ctx context.Context, sectorNum abi.SectorNumber) error { + + status, err := sm.Miner.SectorsStatus(ctx, sectorNum, false) + if err != nil { + return err + } + + minerAddr, err := sm.ActorAddress(ctx) + if err != nil { + return err + } + minerID, err := address.IDFromAddress(minerAddr) + if err != nil { + return err + } + + sector := storiface.SectorRef{ + ID: abi.SectorID{ + Miner: abi.ActorID(minerID), + Number: sectorNum, + }, + ProofType: status.SealProof, + } + + return sm.StorageMgr.SectorsUnsealPiece(ctx, sector, storiface.UnpaddedByteIndex(0), abi.UnpaddedPieceSize(0), status.Ticket.Value, status.CommD) +} + // List all staged sectors func (sm *StorageMinerAPI) SectorsList(context.Context) ([]abi.SectorNumber, error) { sectors, err := sm.Miner.ListSectors() From 0cff56a16d47879dd5e9bd409453b53dbe3f12b1 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 17 Apr 2023 09:46:46 -0700 Subject: [PATCH 170/243] feat: badger: add a has check before writing to reduce duplicates --- blockstore/badger/blockstore.go | 35 +++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/blockstore/badger/blockstore.go b/blockstore/badger/blockstore.go index 5527bf89076..90f9bbfcbab 100644 --- a/blockstore/badger/blockstore.go +++ b/blockstore/badger/blockstore.go @@ -732,6 +732,20 @@ func (b *Blockstore) Put(ctx context.Context, block blocks.Block) error { } put := func(db *badger.DB) error { + // Check if we have it before writing it. + switch err := db.View(func(txn *badger.Txn) error { + _, err := txn.Get(k) + return err + }); err { + case badger.ErrKeyNotFound: + case nil: + // Already exists, skip the put. + return nil + default: + return err + } + + // Then write it. err := db.Update(func(txn *badger.Txn) error { return txn.Set(k, block.RawData()) }) @@ -787,12 +801,33 @@ func (b *Blockstore) PutMany(ctx context.Context, blocks []blocks.Block) error { keys = append(keys, k) } + err := b.db.View(func(txn *badger.Txn) error { + for i, k := range keys { + switch _, err := txn.Get(k); err { + case badger.ErrKeyNotFound: + case nil: + keys[i] = nil + default: + // Something is actually wrong + return err + } + } + return nil + }) + if err != nil { + return err + } + put := func(db *badger.DB) error { batch := db.NewWriteBatch() defer batch.Cancel() for i, block := range blocks { k := keys[i] + if k == nil { + // skipped because we already have it. + continue + } if err := batch.Set(k, block.RawData()); err != nil { return err } From e945c0d6f25f490323ec542d535f584022a20a36 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 17 Apr 2023 20:17:10 -0700 Subject: [PATCH 171/243] fix: check for nil bcastDict (#10646) Also hold the lock when checking the length of the blocks in the bcastDict. --- chain/sub/bcast/consistent.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/chain/sub/bcast/consistent.go b/chain/sub/bcast/consistent.go index 58e8bc98fd8..4d1a14db845 100644 --- a/chain/sub/bcast/consistent.go +++ b/chain/sub/bcast/consistent.go @@ -151,16 +151,22 @@ func (cb *ConsistentBCast) RcvBlock(ctx context.Context, blk *types.BlockMsg) { // be held off for a bit more time. func (cb *ConsistentBCast) WaitForDelivery(bh *types.BlockHeader) error { cb.lk.RLock() - bcastDict := cb.m[bh.Height] + defer cb.lk.RUnlock() + + bcastDict, ok := cb.m[bh.Height] + if !ok { + return xerrors.Errorf("block at height %d garbage collected before it could be processed", bh.Height) + } key := BCastKey(bh) bInfo, ok := bcastDict.load(key) - cb.lk.RUnlock() if !ok { return xerrors.Errorf("something went wrong, unknown block with Epoch + VRFProof (cid=%s) in consistent broadcast storage", key) } // Wait for the timeout + cb.lk.RUnlock() <-bInfo.ctx.Done() + cb.lk.RLock() if bcastDict.blkLen(key) > 1 { return xerrors.Errorf("equivocation detected for epoch %d. Two blocks being broadcast with same VRFProof", bh.Height) } From fe42d974a239e48a1e79eeec6dacf1c54007d451 Mon Sep 17 00:00:00 2001 From: Jorropo Date: Sat, 25 Mar 2023 08:33:05 +0100 Subject: [PATCH 172/243] chore: all: migrate from go-libipfs to boxo github.com/ipfs/libipfs/blocks was unmigrated to github.com/ipfs/go-block-format due to compatibility issues with the rest of the IPLD stack. --- api/api_full.go | 2 +- api/api_gateway.go | 2 +- api/docgen/docgen.go | 2 +- api/mocks/mock_full.go | 2 +- api/proxy_gen.go | 2 +- api/v0api/full.go | 2 +- api/v0api/gateway.go | 2 +- api/v0api/proxy_gen.go | 2 +- api/v0api/v0mocks/mock_full.go | 2 +- blockstore/api.go | 2 +- blockstore/autobatch.go | 2 +- blockstore/badger/blockstore.go | 2 +- blockstore/badger/blockstore_test.go | 2 +- blockstore/badger/blockstore_test_suite.go | 2 +- blockstore/buffered.go | 2 +- blockstore/discard.go | 2 +- blockstore/fallback.go | 2 +- blockstore/idstore.go | 2 +- blockstore/ipfs.go | 2 +- blockstore/mem.go | 2 +- blockstore/mem_test.go | 2 +- blockstore/net.go | 2 +- blockstore/net_serve.go | 2 +- blockstore/net_test.go | 2 +- blockstore/splitstore/debug.go | 2 +- blockstore/splitstore/splitstore.go | 2 +- blockstore/splitstore/splitstore_compact.go | 2 +- blockstore/splitstore/splitstore_expose.go | 2 +- blockstore/splitstore/splitstore_reify.go | 2 +- blockstore/splitstore/splitstore_test.go | 2 +- blockstore/splitstore/splitstore_warmup.go | 2 +- blockstore/sync.go | 2 +- blockstore/timed.go | 2 +- blockstore/timed_test.go | 2 +- blockstore/union.go | 2 +- blockstore/union_test.go | 2 +- chain/events/state/mock/api.go | 2 +- chain/gen/genesis/genblock.go | 2 +- chain/index/msgindex_test.go | 2 +- chain/store/messages.go | 2 +- chain/store/snapshot.go | 2 +- chain/store/store.go | 2 +- chain/sub/incoming.go | 2 +- chain/sub/incoming_test.go | 2 +- chain/sync.go | 2 +- chain/types/blockheader.go | 2 +- chain/types/message.go | 2 +- chain/types/signedmessage.go | 2 +- chain/types/tipset_key.go | 2 +- chain/vm/vm.go | 2 +- cmd/lotus-shed/datastore-vlog.go | 2 +- cmd/lotus-shed/export.go | 2 +- cmd/lotus-shed/import-car.go | 2 +- cmd/tvx/stores.go | 2 +- conformance/runner.go | 2 +- gateway/node.go | 2 +- gateway/proxy_fil.go | 2 +- go.mod | 39 +++++----- go.sum | 74 ++++++++++--------- .../deals_partial_retrieval_dm-level_test.go | 2 +- itests/deals_partial_retrieval_test.go | 2 +- itests/kit/deals.go | 2 +- itests/kit/files.go | 2 +- lib/unixfs/filestore.go | 2 +- lib/unixfs/filestore_test.go | 2 +- markets/dagstore/blockstore.go | 2 +- markets/storageadapter/api.go | 2 +- .../ondealsectorcommitted_test.go | 2 +- node/impl/client/client.go | 2 +- node/impl/client/client_test.go | 2 +- node/impl/full/chain.go | 2 +- node/modules/chain.go | 4 +- 72 files changed, 130 insertions(+), 125 deletions(-) diff --git a/api/api_full.go b/api/api_full.go index 9776a122c43..06a14b76e5a 100644 --- a/api/api_full.go +++ b/api/api_full.go @@ -7,8 +7,8 @@ import ( "time" "github.com/google/uuid" + blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" - blocks "github.com/ipfs/go-libipfs/blocks" "github.com/libp2p/go-libp2p/core/peer" "github.com/filecoin-project/go-address" diff --git a/api/api_gateway.go b/api/api_gateway.go index c1e53ccf05f..f4d6c20a03b 100644 --- a/api/api_gateway.go +++ b/api/api_gateway.go @@ -3,8 +3,8 @@ package api import ( "context" + blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" - blocks "github.com/ipfs/go-libipfs/blocks" "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-jsonrpc" diff --git a/api/docgen/docgen.go b/api/docgen/docgen.go index 0a047044649..7a9993bb782 100644 --- a/api/docgen/docgen.go +++ b/api/docgen/docgen.go @@ -14,9 +14,9 @@ import ( "unicode" "github.com/google/uuid" + blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" "github.com/ipfs/go-graphsync" - blocks "github.com/ipfs/go-libipfs/blocks" textselector "github.com/ipld/go-ipld-selector-text-lite" pubsub "github.com/libp2p/go-libp2p-pubsub" "github.com/libp2p/go-libp2p/core/metrics" diff --git a/api/mocks/mock_full.go b/api/mocks/mock_full.go index 83efbffdb27..12632bc2dd8 100644 --- a/api/mocks/mock_full.go +++ b/api/mocks/mock_full.go @@ -12,8 +12,8 @@ import ( gomock "github.com/golang/mock/gomock" uuid "github.com/google/uuid" + blocks "github.com/ipfs/go-block-format" cid "github.com/ipfs/go-cid" - blocks "github.com/ipfs/go-libipfs/blocks" metrics "github.com/libp2p/go-libp2p/core/metrics" network0 "github.com/libp2p/go-libp2p/core/network" peer "github.com/libp2p/go-libp2p/core/peer" diff --git a/api/proxy_gen.go b/api/proxy_gen.go index 538e5815833..48e7d2e28bd 100644 --- a/api/proxy_gen.go +++ b/api/proxy_gen.go @@ -8,8 +8,8 @@ import ( "time" "github.com/google/uuid" + blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" - blocks "github.com/ipfs/go-libipfs/blocks" "github.com/libp2p/go-libp2p/core/metrics" "github.com/libp2p/go-libp2p/core/network" "github.com/libp2p/go-libp2p/core/peer" diff --git a/api/v0api/full.go b/api/v0api/full.go index 86a4ce47a28..322f7244954 100644 --- a/api/v0api/full.go +++ b/api/v0api/full.go @@ -3,8 +3,8 @@ package v0api import ( "context" + blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" - blocks "github.com/ipfs/go-libipfs/blocks" textselector "github.com/ipld/go-ipld-selector-text-lite" "github.com/libp2p/go-libp2p/core/peer" diff --git a/api/v0api/gateway.go b/api/v0api/gateway.go index 83c7bdfb3e5..30eb0d1c478 100644 --- a/api/v0api/gateway.go +++ b/api/v0api/gateway.go @@ -3,8 +3,8 @@ package v0api import ( "context" + blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" - blocks "github.com/ipfs/go-libipfs/blocks" "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/abi" diff --git a/api/v0api/proxy_gen.go b/api/v0api/proxy_gen.go index 91583522544..069527ae8cb 100644 --- a/api/v0api/proxy_gen.go +++ b/api/v0api/proxy_gen.go @@ -5,8 +5,8 @@ package v0api import ( "context" + blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" - blocks "github.com/ipfs/go-libipfs/blocks" "github.com/libp2p/go-libp2p/core/peer" "golang.org/x/xerrors" diff --git a/api/v0api/v0mocks/mock_full.go b/api/v0api/v0mocks/mock_full.go index a4adfc94402..7a722ed257c 100644 --- a/api/v0api/v0mocks/mock_full.go +++ b/api/v0api/v0mocks/mock_full.go @@ -11,8 +11,8 @@ import ( gomock "github.com/golang/mock/gomock" uuid "github.com/google/uuid" + blocks "github.com/ipfs/go-block-format" cid "github.com/ipfs/go-cid" - blocks "github.com/ipfs/go-libipfs/blocks" metrics "github.com/libp2p/go-libp2p/core/metrics" network0 "github.com/libp2p/go-libp2p/core/network" peer "github.com/libp2p/go-libp2p/core/peer" diff --git a/blockstore/api.go b/blockstore/api.go index 251deb13a05..090f53e5a99 100644 --- a/blockstore/api.go +++ b/blockstore/api.go @@ -3,8 +3,8 @@ package blockstore import ( "context" + blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" - blocks "github.com/ipfs/go-libipfs/blocks" "golang.org/x/xerrors" ) diff --git a/blockstore/autobatch.go b/blockstore/autobatch.go index 3cb8eafa6a7..d41d521ef74 100644 --- a/blockstore/autobatch.go +++ b/blockstore/autobatch.go @@ -5,9 +5,9 @@ import ( "sync" "time" + block "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" ipld "github.com/ipfs/go-ipld-format" - block "github.com/ipfs/go-libipfs/blocks" "golang.org/x/xerrors" ) diff --git a/blockstore/badger/blockstore.go b/blockstore/badger/blockstore.go index 5527bf89076..e4cd743d06b 100644 --- a/blockstore/badger/blockstore.go +++ b/blockstore/badger/blockstore.go @@ -13,9 +13,9 @@ import ( "github.com/dgraph-io/badger/v2" "github.com/dgraph-io/badger/v2/options" "github.com/dgraph-io/badger/v2/pb" + blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" ipld "github.com/ipfs/go-ipld-format" - blocks "github.com/ipfs/go-libipfs/blocks" logger "github.com/ipfs/go-log/v2" pool "github.com/libp2p/go-buffer-pool" "github.com/multiformats/go-base32" diff --git a/blockstore/badger/blockstore_test.go b/blockstore/badger/blockstore_test.go index bf85104bb5b..d253f37d95d 100644 --- a/blockstore/badger/blockstore_test.go +++ b/blockstore/badger/blockstore_test.go @@ -10,8 +10,8 @@ import ( "strings" "testing" + blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" - blocks "github.com/ipfs/go-libipfs/blocks" "github.com/stretchr/testify/require" "golang.org/x/sync/errgroup" diff --git a/blockstore/badger/blockstore_test_suite.go b/blockstore/badger/blockstore_test_suite.go index 877eb6e6bdc..7db15590193 100644 --- a/blockstore/badger/blockstore_test_suite.go +++ b/blockstore/badger/blockstore_test_suite.go @@ -9,10 +9,10 @@ import ( "strings" "testing" + blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" u "github.com/ipfs/go-ipfs-util" ipld "github.com/ipfs/go-ipld-format" - blocks "github.com/ipfs/go-libipfs/blocks" "github.com/stretchr/testify/require" "github.com/filecoin-project/lotus/blockstore" diff --git a/blockstore/buffered.go b/blockstore/buffered.go index bc37b2f69ec..2a789b6371a 100644 --- a/blockstore/buffered.go +++ b/blockstore/buffered.go @@ -4,9 +4,9 @@ import ( "context" "os" + block "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" ipld "github.com/ipfs/go-ipld-format" - block "github.com/ipfs/go-libipfs/blocks" ) // buflog is a logger for the buffered blockstore. It is subscoped from the diff --git a/blockstore/discard.go b/blockstore/discard.go index 87879756186..158c7cfbbea 100644 --- a/blockstore/discard.go +++ b/blockstore/discard.go @@ -4,8 +4,8 @@ import ( "context" "io" + blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" - blocks "github.com/ipfs/go-libipfs/blocks" ) var _ Blockstore = (*discardstore)(nil) diff --git a/blockstore/fallback.go b/blockstore/fallback.go index 9fab960f5d7..de948b34600 100644 --- a/blockstore/fallback.go +++ b/blockstore/fallback.go @@ -5,9 +5,9 @@ import ( "sync" "time" + blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" ipld "github.com/ipfs/go-ipld-format" - blocks "github.com/ipfs/go-libipfs/blocks" "golang.org/x/xerrors" ) diff --git a/blockstore/idstore.go b/blockstore/idstore.go index ae807076d32..fb575dca72f 100644 --- a/blockstore/idstore.go +++ b/blockstore/idstore.go @@ -4,8 +4,8 @@ import ( "context" "io" + blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" - blocks "github.com/ipfs/go-libipfs/blocks" mh "github.com/multiformats/go-multihash" "golang.org/x/xerrors" ) diff --git a/blockstore/ipfs.go b/blockstore/ipfs.go index c7dbb480a45..7b356cd0e97 100644 --- a/blockstore/ipfs.go +++ b/blockstore/ipfs.go @@ -5,9 +5,9 @@ import ( "context" "io" + blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" httpapi "github.com/ipfs/go-ipfs-http-client" - blocks "github.com/ipfs/go-libipfs/blocks" iface "github.com/ipfs/interface-go-ipfs-core" "github.com/ipfs/interface-go-ipfs-core/options" "github.com/ipfs/interface-go-ipfs-core/path" diff --git a/blockstore/mem.go b/blockstore/mem.go index 5b06634de11..8dbf4b719ad 100644 --- a/blockstore/mem.go +++ b/blockstore/mem.go @@ -3,9 +3,9 @@ package blockstore import ( "context" + blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" ipld "github.com/ipfs/go-ipld-format" - blocks "github.com/ipfs/go-libipfs/blocks" ) // NewMemory returns a temporary memory-backed blockstore. diff --git a/blockstore/mem_test.go b/blockstore/mem_test.go index 6a5e0d2d11d..4d4a776245b 100644 --- a/blockstore/mem_test.go +++ b/blockstore/mem_test.go @@ -4,8 +4,8 @@ import ( "context" "testing" + blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" - blocks "github.com/ipfs/go-libipfs/blocks" mh "github.com/multiformats/go-multihash" "github.com/stretchr/testify/require" ) diff --git a/blockstore/net.go b/blockstore/net.go index 62aceed71f4..c4a88cfc970 100644 --- a/blockstore/net.go +++ b/blockstore/net.go @@ -8,9 +8,9 @@ import ( "sync" "sync/atomic" + blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" ipld "github.com/ipfs/go-ipld-format" - blocks "github.com/ipfs/go-libipfs/blocks" "github.com/libp2p/go-msgio" cbg "github.com/whyrusleeping/cbor-gen" "golang.org/x/xerrors" diff --git a/blockstore/net_serve.go b/blockstore/net_serve.go index b58d2a5fd19..2540c845e81 100644 --- a/blockstore/net_serve.go +++ b/blockstore/net_serve.go @@ -5,9 +5,9 @@ import ( "context" "encoding/binary" + block "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" ipld "github.com/ipfs/go-ipld-format" - block "github.com/ipfs/go-libipfs/blocks" "github.com/libp2p/go-msgio" cbg "github.com/whyrusleeping/cbor-gen" "golang.org/x/xerrors" diff --git a/blockstore/net_test.go b/blockstore/net_test.go index 6c3060a98f5..d8c33818eb3 100644 --- a/blockstore/net_test.go +++ b/blockstore/net_test.go @@ -6,8 +6,8 @@ import ( "io" "testing" + block "github.com/ipfs/go-block-format" ipld "github.com/ipfs/go-ipld-format" - block "github.com/ipfs/go-libipfs/blocks" "github.com/libp2p/go-msgio" "github.com/stretchr/testify/require" ) diff --git a/blockstore/splitstore/debug.go b/blockstore/splitstore/debug.go index c870a44e1b5..f059ae4ced9 100644 --- a/blockstore/splitstore/debug.go +++ b/blockstore/splitstore/debug.go @@ -12,8 +12,8 @@ import ( "sync" "time" + blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" - blocks "github.com/ipfs/go-libipfs/blocks" "go.uber.org/multierr" "golang.org/x/xerrors" ) diff --git a/blockstore/splitstore/splitstore.go b/blockstore/splitstore/splitstore.go index bd9efb630b2..1b10f09bd0c 100644 --- a/blockstore/splitstore/splitstore.go +++ b/blockstore/splitstore/splitstore.go @@ -8,10 +8,10 @@ import ( "sync/atomic" "time" + blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" dstore "github.com/ipfs/go-datastore" ipld "github.com/ipfs/go-ipld-format" - blocks "github.com/ipfs/go-libipfs/blocks" logging "github.com/ipfs/go-log/v2" "go.opencensus.io/stats" "go.uber.org/multierr" diff --git a/blockstore/splitstore/splitstore_compact.go b/blockstore/splitstore/splitstore_compact.go index e5cfec0e47f..42645fa95b8 100644 --- a/blockstore/splitstore/splitstore_compact.go +++ b/blockstore/splitstore/splitstore_compact.go @@ -10,9 +10,9 @@ import ( "sync/atomic" "time" + blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" ipld "github.com/ipfs/go-ipld-format" - blocks "github.com/ipfs/go-libipfs/blocks" cbg "github.com/whyrusleeping/cbor-gen" "go.opencensus.io/stats" "golang.org/x/sync/errgroup" diff --git a/blockstore/splitstore/splitstore_expose.go b/blockstore/splitstore/splitstore_expose.go index 7461e338db8..93183612968 100644 --- a/blockstore/splitstore/splitstore_expose.go +++ b/blockstore/splitstore/splitstore_expose.go @@ -4,9 +4,9 @@ import ( "context" "errors" + blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" ipld "github.com/ipfs/go-ipld-format" - blocks "github.com/ipfs/go-libipfs/blocks" bstore "github.com/filecoin-project/lotus/blockstore" ) diff --git a/blockstore/splitstore/splitstore_reify.go b/blockstore/splitstore/splitstore_reify.go index 07efedead38..7f54de55f43 100644 --- a/blockstore/splitstore/splitstore_reify.go +++ b/blockstore/splitstore/splitstore_reify.go @@ -5,8 +5,8 @@ import ( "runtime" "sync/atomic" + blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" - blocks "github.com/ipfs/go-libipfs/blocks" "golang.org/x/xerrors" ) diff --git a/blockstore/splitstore/splitstore_test.go b/blockstore/splitstore/splitstore_test.go index 68e1bfb6586..4e168fc543c 100644 --- a/blockstore/splitstore/splitstore_test.go +++ b/blockstore/splitstore/splitstore_test.go @@ -11,11 +11,11 @@ import ( "testing" "time" + blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" "github.com/ipfs/go-datastore" dssync "github.com/ipfs/go-datastore/sync" ipld "github.com/ipfs/go-ipld-format" - blocks "github.com/ipfs/go-libipfs/blocks" logging "github.com/ipfs/go-log/v2" mh "github.com/multiformats/go-multihash" diff --git a/blockstore/splitstore/splitstore_warmup.go b/blockstore/splitstore/splitstore_warmup.go index 23bbad7cada..e387263dae7 100644 --- a/blockstore/splitstore/splitstore_warmup.go +++ b/blockstore/splitstore/splitstore_warmup.go @@ -5,9 +5,9 @@ import ( "sync/atomic" "time" + blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" ipld "github.com/ipfs/go-ipld-format" - blocks "github.com/ipfs/go-libipfs/blocks" "golang.org/x/xerrors" "github.com/filecoin-project/go-state-types/abi" diff --git a/blockstore/sync.go b/blockstore/sync.go index 652943dca1e..4f0cf830ee6 100644 --- a/blockstore/sync.go +++ b/blockstore/sync.go @@ -4,8 +4,8 @@ import ( "context" "sync" + blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" - blocks "github.com/ipfs/go-libipfs/blocks" ) // NewMemorySync returns a thread-safe in-memory blockstore. diff --git a/blockstore/timed.go b/blockstore/timed.go index 3deabb7b88b..01e089a9f85 100644 --- a/blockstore/timed.go +++ b/blockstore/timed.go @@ -6,9 +6,9 @@ import ( "sync" "time" + blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" ipld "github.com/ipfs/go-ipld-format" - blocks "github.com/ipfs/go-libipfs/blocks" "github.com/raulk/clock" "go.uber.org/multierr" ) diff --git a/blockstore/timed_test.go b/blockstore/timed_test.go index fb3aa00c9b4..931f145075b 100644 --- a/blockstore/timed_test.go +++ b/blockstore/timed_test.go @@ -6,8 +6,8 @@ import ( "testing" "time" + blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" - blocks "github.com/ipfs/go-libipfs/blocks" "github.com/raulk/clock" "github.com/stretchr/testify/require" ) diff --git a/blockstore/union.go b/blockstore/union.go index ae6f819558d..e7fafbbbd85 100644 --- a/blockstore/union.go +++ b/blockstore/union.go @@ -3,9 +3,9 @@ package blockstore import ( "context" + blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" ipld "github.com/ipfs/go-ipld-format" - blocks "github.com/ipfs/go-libipfs/blocks" ) type unionBlockstore []Blockstore diff --git a/blockstore/union_test.go b/blockstore/union_test.go index 47aab852a86..57948994770 100644 --- a/blockstore/union_test.go +++ b/blockstore/union_test.go @@ -5,7 +5,7 @@ import ( "context" "testing" - blocks "github.com/ipfs/go-libipfs/blocks" + blocks "github.com/ipfs/go-block-format" "github.com/stretchr/testify/require" ) diff --git a/chain/events/state/mock/api.go b/chain/events/state/mock/api.go index 680e304f56c..cdec4265922 100644 --- a/chain/events/state/mock/api.go +++ b/chain/events/state/mock/api.go @@ -4,8 +4,8 @@ import ( "context" "sync" + blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" - blocks "github.com/ipfs/go-libipfs/blocks" "golang.org/x/xerrors" "github.com/filecoin-project/go-address" diff --git a/chain/gen/genesis/genblock.go b/chain/gen/genesis/genblock.go index 930b3ccff4e..f26659cdfac 100644 --- a/chain/gen/genesis/genblock.go +++ b/chain/gen/genesis/genblock.go @@ -3,8 +3,8 @@ package genesis import ( "encoding/hex" + blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" - blocks "github.com/ipfs/go-libipfs/blocks" "github.com/multiformats/go-multihash" ) diff --git a/chain/index/msgindex_test.go b/chain/index/msgindex_test.go index 24f9b845f2b..4ebdcfd35ba 100644 --- a/chain/index/msgindex_test.go +++ b/chain/index/msgindex_test.go @@ -8,8 +8,8 @@ import ( "testing" "time" + blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" - "github.com/ipfs/go-libipfs/blocks" "github.com/stretchr/testify/require" "github.com/filecoin-project/go-address" diff --git a/chain/store/messages.go b/chain/store/messages.go index ee37b4855ea..3686f74f462 100644 --- a/chain/store/messages.go +++ b/chain/store/messages.go @@ -3,10 +3,10 @@ package store import ( "context" + block "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" cbor "github.com/ipfs/go-ipld-cbor" ipld "github.com/ipfs/go-ipld-format" - block "github.com/ipfs/go-libipfs/blocks" cbg "github.com/whyrusleeping/cbor-gen" "golang.org/x/xerrors" diff --git a/chain/store/snapshot.go b/chain/store/snapshot.go index 0a878e1e09a..66d23675ef3 100644 --- a/chain/store/snapshot.go +++ b/chain/store/snapshot.go @@ -9,9 +9,9 @@ import ( "sync" "time" + blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" format "github.com/ipfs/go-ipld-format" - blocks "github.com/ipfs/go-libipfs/blocks" "github.com/ipld/go-car" carutil "github.com/ipld/go-car/util" carv2 "github.com/ipld/go-car/v2" diff --git a/chain/store/store.go b/chain/store/store.go index d7188a7bfd1..b34ddb2665d 100644 --- a/chain/store/store.go +++ b/chain/store/store.go @@ -12,11 +12,11 @@ import ( "time" lru "github.com/hashicorp/golang-lru/v2" + block "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" dstore "github.com/ipfs/go-datastore" "github.com/ipfs/go-datastore/query" cbor "github.com/ipfs/go-ipld-cbor" - block "github.com/ipfs/go-libipfs/blocks" logging "github.com/ipfs/go-log/v2" "go.opencensus.io/stats" "go.opencensus.io/trace" diff --git a/chain/sub/incoming.go b/chain/sub/incoming.go index 87598bd2e03..f98724361bf 100644 --- a/chain/sub/incoming.go +++ b/chain/sub/incoming.go @@ -8,9 +8,9 @@ import ( "time" lru "github.com/hashicorp/golang-lru/v2" + blocks "github.com/ipfs/go-block-format" bserv "github.com/ipfs/go-blockservice" "github.com/ipfs/go-cid" - blocks "github.com/ipfs/go-libipfs/blocks" logging "github.com/ipfs/go-log/v2" "github.com/ipni/storetheindex/announce/message" pubsub "github.com/libp2p/go-libp2p-pubsub" diff --git a/chain/sub/incoming_test.go b/chain/sub/incoming_test.go index 03f880c581f..0a9504a884f 100644 --- a/chain/sub/incoming_test.go +++ b/chain/sub/incoming_test.go @@ -7,8 +7,8 @@ import ( "testing" "github.com/golang/mock/gomock" + blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" - blocks "github.com/ipfs/go-libipfs/blocks" "github.com/ipni/storetheindex/announce/message" pubsub "github.com/libp2p/go-libp2p-pubsub" pb "github.com/libp2p/go-libp2p-pubsub/pb" diff --git a/chain/sync.go b/chain/sync.go index db7b7fc042d..b0c8b50ff96 100644 --- a/chain/sync.go +++ b/chain/sync.go @@ -11,10 +11,10 @@ import ( "github.com/Gurpartap/async" "github.com/hashicorp/go-multierror" + blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" cbor "github.com/ipfs/go-ipld-cbor" ipld "github.com/ipfs/go-ipld-format" - blocks "github.com/ipfs/go-libipfs/blocks" logging "github.com/ipfs/go-log/v2" "github.com/libp2p/go-libp2p/core/connmgr" "github.com/libp2p/go-libp2p/core/peer" diff --git a/chain/types/blockheader.go b/chain/types/blockheader.go index 559569ba01b..e0b9e6b30bb 100644 --- a/chain/types/blockheader.go +++ b/chain/types/blockheader.go @@ -4,8 +4,8 @@ import ( "bytes" "math/big" + block "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" - block "github.com/ipfs/go-libipfs/blocks" "github.com/minio/blake2b-simd" "golang.org/x/xerrors" diff --git a/chain/types/message.go b/chain/types/message.go index 4304ba659ff..84f7a19a062 100644 --- a/chain/types/message.go +++ b/chain/types/message.go @@ -5,8 +5,8 @@ import ( "encoding/json" "fmt" + block "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" - block "github.com/ipfs/go-libipfs/blocks" "golang.org/x/xerrors" "github.com/filecoin-project/go-address" diff --git a/chain/types/signedmessage.go b/chain/types/signedmessage.go index dc867c5e547..16853171464 100644 --- a/chain/types/signedmessage.go +++ b/chain/types/signedmessage.go @@ -4,8 +4,8 @@ import ( "bytes" "encoding/json" + block "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" - block "github.com/ipfs/go-libipfs/blocks" "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/crypto" diff --git a/chain/types/tipset_key.go b/chain/types/tipset_key.go index 50753ffd245..15e655da7d6 100644 --- a/chain/types/tipset_key.go +++ b/chain/types/tipset_key.go @@ -7,8 +7,8 @@ import ( "io" "strings" + block "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" - block "github.com/ipfs/go-libipfs/blocks" typegen "github.com/whyrusleeping/cbor-gen" "github.com/filecoin-project/go-state-types/abi" diff --git a/chain/vm/vm.go b/chain/vm/vm.go index 6fbe7933b68..58afc14bc18 100644 --- a/chain/vm/vm.go +++ b/chain/vm/vm.go @@ -7,9 +7,9 @@ import ( "sync/atomic" "time" + block "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" cbor "github.com/ipfs/go-ipld-cbor" - block "github.com/ipfs/go-libipfs/blocks" logging "github.com/ipfs/go-log/v2" mh "github.com/multiformats/go-multihash" cbg "github.com/whyrusleeping/cbor-gen" diff --git a/cmd/lotus-shed/datastore-vlog.go b/cmd/lotus-shed/datastore-vlog.go index af412df19da..936d33849f7 100644 --- a/cmd/lotus-shed/datastore-vlog.go +++ b/cmd/lotus-shed/datastore-vlog.go @@ -12,8 +12,8 @@ import ( "strings" "github.com/dgraph-io/badger/v2/y" + block "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" - block "github.com/ipfs/go-libipfs/blocks" "github.com/mitchellh/go-homedir" "github.com/multiformats/go-base32" "github.com/urfave/cli/v2" diff --git a/cmd/lotus-shed/export.go b/cmd/lotus-shed/export.go index d67fa449352..459de3383d3 100644 --- a/cmd/lotus-shed/export.go +++ b/cmd/lotus-shed/export.go @@ -15,11 +15,11 @@ import ( "github.com/dgraph-io/badger/v2" "github.com/dgraph-io/badger/v2/pb" "github.com/dustin/go-humanize" + block "github.com/ipfs/go-block-format" "github.com/ipfs/go-blockservice" "github.com/ipfs/go-cid" offline "github.com/ipfs/go-ipfs-exchange-offline" ipld "github.com/ipfs/go-ipld-format" - block "github.com/ipfs/go-libipfs/blocks" "github.com/ipfs/go-merkledag" "github.com/ipld/go-car" "github.com/multiformats/go-base32" diff --git a/cmd/lotus-shed/import-car.go b/cmd/lotus-shed/import-car.go index 4c636a3af40..973e7b31b84 100644 --- a/cmd/lotus-shed/import-car.go +++ b/cmd/lotus-shed/import-car.go @@ -7,8 +7,8 @@ import ( "io" "os" + block "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" - block "github.com/ipfs/go-libipfs/blocks" "github.com/ipld/go-car" "github.com/urfave/cli/v2" "golang.org/x/xerrors" diff --git a/cmd/tvx/stores.go b/cmd/tvx/stores.go index b863ffa7442..9035c482a0b 100644 --- a/cmd/tvx/stores.go +++ b/cmd/tvx/stores.go @@ -6,6 +6,7 @@ import ( "sync" "github.com/fatih/color" + blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-blockservice" "github.com/ipfs/go-cid" ds "github.com/ipfs/go-datastore" @@ -14,7 +15,6 @@ import ( offline "github.com/ipfs/go-ipfs-exchange-offline" cbor "github.com/ipfs/go-ipld-cbor" format "github.com/ipfs/go-ipld-format" - blocks "github.com/ipfs/go-libipfs/blocks" "github.com/ipfs/go-merkledag" "golang.org/x/xerrors" diff --git a/conformance/runner.go b/conformance/runner.go index 827c10a5cb9..09d4c0621b6 100644 --- a/conformance/runner.go +++ b/conformance/runner.go @@ -13,12 +13,12 @@ import ( "github.com/fatih/color" "github.com/hashicorp/go-multierror" + blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-blockservice" "github.com/ipfs/go-cid" ds "github.com/ipfs/go-datastore" offline "github.com/ipfs/go-ipfs-exchange-offline" format "github.com/ipfs/go-ipld-format" - blocks "github.com/ipfs/go-libipfs/blocks" "github.com/ipfs/go-merkledag" "github.com/ipld/go-car" diff --git a/gateway/node.go b/gateway/node.go index 977c1e5a8ed..a88b2286066 100644 --- a/gateway/node.go +++ b/gateway/node.go @@ -5,8 +5,8 @@ import ( "fmt" "time" + blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" - blocks "github.com/ipfs/go-libipfs/blocks" "go.opencensus.io/stats" "golang.org/x/time/rate" diff --git a/gateway/proxy_fil.go b/gateway/proxy_fil.go index e15d246f859..c15a78f5ff4 100644 --- a/gateway/proxy_fil.go +++ b/gateway/proxy_fil.go @@ -3,8 +3,8 @@ package gateway import ( "context" + blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" - blocks "github.com/ipfs/go-libipfs/blocks" "golang.org/x/xerrors" "github.com/filecoin-project/go-address" diff --git a/go.mod b/go.mod index 764c3591a39..b2a51d9e6aa 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.0 - github.com/BurntSushi/toml v1.1.0 + github.com/BurntSushi/toml v1.2.1 github.com/DataDog/zstd v1.4.5 github.com/GeertJohan/go.rice v1.0.3 github.com/Gurpartap/async v0.0.0-20180927173644-4f7f499dd9ee @@ -77,8 +77,10 @@ require ( 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 + github.com/ipfs/boxo v0.8.0 + github.com/ipfs/go-block-format v0.1.2 github.com/ipfs/go-blockservice v0.5.0 - github.com/ipfs/go-cid v0.4.0 + github.com/ipfs/go-cid v0.4.1 github.com/ipfs/go-cidutil v0.1.0 github.com/ipfs/go-datastore v0.6.0 github.com/ipfs/go-ds-badger2 v0.1.3 @@ -86,7 +88,7 @@ require ( github.com/ipfs/go-ds-measure v0.2.0 github.com/ipfs/go-fs-lock v0.0.7 github.com/ipfs/go-graphsync v0.14.3 - github.com/ipfs/go-ipfs-blockstore v1.2.0 + github.com/ipfs/go-ipfs-blockstore v1.3.0 github.com/ipfs/go-ipfs-blocksutil v0.0.1 github.com/ipfs/go-ipfs-chunker v0.0.5 github.com/ipfs/go-ipfs-ds-help v1.1.0 @@ -97,17 +99,16 @@ require ( github.com/ipfs/go-ipfs-util v0.0.2 github.com/ipfs/go-ipld-cbor v0.0.6 github.com/ipfs/go-ipld-format v0.4.0 - github.com/ipfs/go-libipfs v0.7.0 github.com/ipfs/go-log/v2 v2.5.1 - github.com/ipfs/go-merkledag v0.9.0 + github.com/ipfs/go-merkledag v0.10.0 github.com/ipfs/go-metrics-interface v0.0.1 github.com/ipfs/go-metrics-prometheus v0.0.2 - github.com/ipfs/go-unixfs v0.4.3 - github.com/ipfs/go-unixfsnode v1.5.2 + github.com/ipfs/go-unixfs v0.4.5 + github.com/ipfs/go-unixfsnode v1.6.0 github.com/ipfs/interface-go-ipfs-core v0.11.1 github.com/ipld/go-car v0.5.0 - github.com/ipld/go-car/v2 v2.7.0 - github.com/ipld/go-codec-dagpb v1.5.0 + github.com/ipld/go-car/v2 v2.10.0 + github.com/ipld/go-codec-dagpb v1.6.0 github.com/ipld/go-ipld-prime v0.20.0 github.com/ipld/go-ipld-selector-text-lite v0.0.1 github.com/ipni/index-provider v0.11.0 @@ -115,10 +116,10 @@ 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.26.2 + github.com/libp2p/go-libp2p v0.26.3 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.21.0 + github.com/libp2p/go-libp2p-kad-dht v0.21.1 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 @@ -140,7 +141,7 @@ require ( github.com/prometheus/client_golang v1.14.0 github.com/raulk/clock v1.1.0 github.com/raulk/go-watchdog v1.3.0 - github.com/stretchr/testify v1.8.1 + github.com/stretchr/testify v1.8.2 github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 github.com/urfave/cli/v2 v2.16.3 github.com/whyrusleeping/bencher v0.0.0-20190829221104-bb6607aa8bba @@ -151,7 +152,7 @@ require ( github.com/xorcare/golden v0.6.1-0.20191112154924-b87f686d7542 github.com/zyedidia/generic v1.2.1 go.opencensus.io v0.24.0 - go.opentelemetry.io/otel v1.12.0 + go.opentelemetry.io/otel v1.14.0 go.opentelemetry.io/otel/bridge/opencensus v0.33.0 go.opentelemetry.io/otel/exporters/jaeger v1.2.0 go.opentelemetry.io/otel/sdk v1.11.1 @@ -159,11 +160,11 @@ require ( go.uber.org/fx v1.18.2 go.uber.org/multierr v1.9.0 go.uber.org/zap v1.24.0 - golang.org/x/crypto v0.5.0 - golang.org/x/exp v0.0.0-20230129154200-a960b3787bd2 + golang.org/x/crypto v0.6.0 + golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb golang.org/x/net v0.7.0 golang.org/x/sync v0.1.0 - golang.org/x/sys v0.5.0 + golang.org/x/sys v0.6.0 golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 golang.org/x/tools v0.3.0 golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 @@ -234,7 +235,6 @@ require ( github.com/huin/goupnp v1.0.3 // indirect github.com/iancoleman/orderedmap v0.1.0 // indirect github.com/ipfs/go-bitfield v1.1.0 // indirect - github.com/ipfs/go-block-format v0.1.1 // indirect github.com/ipfs/go-filestore v1.2.0 // indirect github.com/ipfs/go-ipfs-cmds v0.8.2 // indirect github.com/ipfs/go-ipfs-delay v0.0.1 // indirect @@ -243,6 +243,7 @@ require ( github.com/ipfs/go-ipfs-pq v0.0.3 // indirect github.com/ipfs/go-ipld-legacy v0.1.1 // indirect github.com/ipfs/go-ipns v0.3.0 // indirect + github.com/ipfs/go-libipfs v0.7.0 // indirect github.com/ipfs/go-log v1.0.5 // indirect github.com/ipfs/go-path v0.3.1 // indirect github.com/ipfs/go-peertaskqueue v0.8.1 // indirect @@ -284,7 +285,7 @@ require ( 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 - github.com/multiformats/go-multicodec v0.8.0 // indirect + github.com/multiformats/go-multicodec v0.8.1 // indirect github.com/multiformats/go-multistream v0.4.1 // indirect github.com/nikkolasg/hexjson v0.1.0 // indirect github.com/nkovacs/streamquote v1.0.0 // indirect @@ -324,7 +325,7 @@ require ( github.com/zondax/ledger-go v0.12.1 // indirect go.opentelemetry.io/otel/metric v0.33.0 // indirect go.opentelemetry.io/otel/sdk/metric v0.33.0 // indirect - go.opentelemetry.io/otel/trace v1.12.0 // indirect + go.opentelemetry.io/otel/trace v1.14.0 // indirect go.uber.org/dig v1.15.0 // indirect go4.org v0.0.0-20200411211856-f5505b9728dd // indirect golang.org/x/mod v0.7.0 // indirect diff --git a/go.sum b/go.sum index 817af5497f5..cb496d2fcbc 100644 --- a/go.sum +++ b/go.sum @@ -45,8 +45,8 @@ github.com/AndreasBriese/bbloom v0.0.0-20180913140656-343706a395b7/go.mod h1:bOv 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/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I= -github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +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= @@ -391,7 +391,7 @@ github.com/frankban/quicktest v1.14.0/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09 github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= 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.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= +github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/gbrlsnchs/jwt/v3 v3.0.1 h1:lbUmgAKpxnClrKloyIwpxm4OuWeDl5wLk52G91ODPw4= github.com/gbrlsnchs/jwt/v3 v3.0.1/go.mod h1:AncDcjXz18xetI3A6STfXq2w+LuTx8pQ8bGEwRN8zVM= github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko= @@ -670,6 +670,8 @@ github.com/influxdata/influxdb1-client v0.0.0-20200827194710-b269163b24ab/go.mod 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= +github.com/ipfs/boxo v0.8.0 h1:UdjAJmHzQHo/j3g3b1bAcAXCj/GM6iTwvSlBDvPBNBs= +github.com/ipfs/boxo v0.8.0/go.mod h1:RIsi4CnTyQ7AUsNn5gXljJYZlQrHBMnJp94p73liFiA= github.com/ipfs/go-bitfield v1.1.0 h1:fh7FIo8bSwaJEh6DdTWbCeZ1eqOaOkKFI74SCnsWbGA= github.com/ipfs/go-bitfield v1.1.0/go.mod h1:paqf1wjq/D2BBmzfTVFlJQ9IlFOZpg422HL0HqsGWHU= github.com/ipfs/go-bitswap v0.1.0/go.mod h1:FFJEf18E9izuCqUtHxbWEvq+reg7o4CW5wSAE1wsxj0= @@ -680,8 +682,8 @@ github.com/ipfs/go-bitswap v0.11.0 h1:j1WVvhDX1yhG32NTC9xfxnqycqYIlhzEzLXG/cU1Hy 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= -github.com/ipfs/go-block-format v0.1.1 h1:129vSO3zwbsYADcyQWcOYiuCpAqt462SFfqFHdFJhhI= -github.com/ipfs/go-block-format v0.1.1/go.mod h1:+McEIT+g52p+zz5xGAABGSOKrzmrdX97bc0USBdWPUs= +github.com/ipfs/go-block-format v0.1.2 h1:GAjkfhVx1f4YTODS6Esrj1wt2HhrtwTnhEr+DyPUaJo= +github.com/ipfs/go-block-format v0.1.2/go.mod h1:mACVcrxarQKstUU3Yf/RdwbC4DzPV6++rO2a3d+a/KE= github.com/ipfs/go-blockservice v0.1.0/go.mod h1:hzmMScl1kXHg3M2BjTymbVPjv627N7sYcvYaKbop39M= github.com/ipfs/go-blockservice v0.2.1/go.mod h1:k6SiwmgyYgs4M/qt+ww6amPeUH9EISLRBnvUurKJhi8= github.com/ipfs/go-blockservice v0.3.0/go.mod h1:P5ppi8IHDC7O+pA0AlGTF09jruB2h+oP3wVVaZl8sfk= @@ -698,8 +700,8 @@ github.com/ipfs/go-cid v0.0.6/go.mod h1:6Ux9z5e+HpkQdckYoX1PG/6xqKspzlEIR5SDmgqg github.com/ipfs/go-cid v0.0.7/go.mod h1:6Ux9z5e+HpkQdckYoX1PG/6xqKspzlEIR5SDmgqgC/I= github.com/ipfs/go-cid v0.1.0/go.mod h1:rH5/Xv83Rfy8Rw6xG+id3DYAMUVmem1MowoKwdXmN2o= github.com/ipfs/go-cid v0.2.0/go.mod h1:P+HXFDF4CVhaVayiEb4wkAy7zBHxBwsJyt0Y5U6MLro= -github.com/ipfs/go-cid v0.4.0 h1:a4pdZq0sx6ZSxbCizebnKiMCx/xI/aBBFlB73IgH4rA= -github.com/ipfs/go-cid v0.4.0/go.mod h1:uQHwDeX4c6CtyrFwdqyhpNcxVewur1M7l7fNU7LKwZk= +github.com/ipfs/go-cid v0.4.1 h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s= +github.com/ipfs/go-cid v0.4.1/go.mod h1:uQHwDeX4c6CtyrFwdqyhpNcxVewur1M7l7fNU7LKwZk= github.com/ipfs/go-cidutil v0.1.0 h1:RW5hO7Vcf16dplUU60Hs0AKDkQAVPVplr7lk97CFL+Q= github.com/ipfs/go-cidutil v0.1.0/go.mod h1:e7OEVBMIv9JaOxt9zaGEmAoSlXW9jdFZ5lP/0PwcfpA= github.com/ipfs/go-datastore v0.0.1/go.mod h1:d4KVXhMt913cLBEI/PXAy6ko+W7e9AhyAKBGh803qeE= @@ -742,8 +744,9 @@ github.com/ipfs/go-ipfs-blockstore v0.0.1/go.mod h1:d3WClOmRQKFnJ0Jz/jj/zmksX0ma github.com/ipfs/go-ipfs-blockstore v0.1.0/go.mod h1:5aD0AvHPi7mZc6Ci1WCAhiBQu2IsfTduLl+422H6Rqw= github.com/ipfs/go-ipfs-blockstore v0.2.1/go.mod h1:jGesd8EtCM3/zPgx+qr0/feTXGUeRai6adgwC+Q+JvE= github.com/ipfs/go-ipfs-blockstore v1.1.2/go.mod h1:w51tNR9y5+QXB0wkNcHt4O2aSZjTdqaEWaQdSxEyUOY= -github.com/ipfs/go-ipfs-blockstore v1.2.0 h1:n3WTeJ4LdICWs/0VSfjHrlqpPpl6MZ+ySd3j8qz0ykw= github.com/ipfs/go-ipfs-blockstore v1.2.0/go.mod h1:eh8eTFLiINYNSNawfZOC7HOxNTxpB1PFuA5E1m/7exE= +github.com/ipfs/go-ipfs-blockstore v1.3.0 h1:m2EXaWgwTzAfsmt5UdJ7Is6l4gJcaM/A12XwJyvYvMM= +github.com/ipfs/go-ipfs-blockstore v1.3.0/go.mod h1:KgtZyc9fq+P2xJUiCAzbRdhhqJHvsw8u2Dlqy2MyRTE= github.com/ipfs/go-ipfs-blocksutil v0.0.1 h1:Eh/H4pc1hsvhzsQoMEP3Bke/aW5P5rVM1IWFJMcGIPQ= 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= @@ -831,8 +834,8 @@ github.com/ipfs/go-merkledag v0.2.4/go.mod h1:SQiXrtSts3KGNmgOzMICy5c0POOpUNQLvB github.com/ipfs/go-merkledag v0.3.2/go.mod h1:fvkZNNZixVW6cKSZ/JfLlON5OlgTXNdRLz0p6QG/I2M= github.com/ipfs/go-merkledag v0.5.1/go.mod h1:cLMZXx8J08idkp5+id62iVftUQV+HlYJ3PIhDfZsjA4= github.com/ipfs/go-merkledag v0.6.0/go.mod h1:9HSEwRd5sV+lbykiYP+2NC/3o6MZbKNaa4hfNcH5iH0= -github.com/ipfs/go-merkledag v0.9.0 h1:DFC8qZ96Dz1hMT7dtIpcY524eFFDiEWAF8hNJHWW2pk= -github.com/ipfs/go-merkledag v0.9.0/go.mod h1:bPHqkHt5OZ0p1n3iqPeDiw2jIBkjAytRjS3WSBwjq90= +github.com/ipfs/go-merkledag v0.10.0 h1:IUQhj/kzTZfam4e+LnaEpoiZ9vZF6ldimVlby+6OXL4= +github.com/ipfs/go-merkledag v0.10.0/go.mod h1:zkVav8KiYlmbzUzNM6kENzkdP5+qR7+2mCwxkQ6GIj8= github.com/ipfs/go-metrics-interface v0.0.1 h1:j+cpbjYvu4R8zbleSs36gvB7jR+wsL2fGD6n0jO4kdg= github.com/ipfs/go-metrics-interface v0.0.1/go.mod h1:6s6euYU4zowdslK0GKHmqaIZ3j/b/tL7HTWtJ4VPgWY= github.com/ipfs/go-metrics-prometheus v0.0.2 h1:9i2iljLg12S78OhC6UAiXi176xvQGiZaGVF1CUVdE+s= @@ -844,10 +847,10 @@ github.com/ipfs/go-peertaskqueue v0.7.0/go.mod h1:M/akTIE/z1jGNXMU7kFB4TeSEFvj68 github.com/ipfs/go-peertaskqueue v0.8.1 h1:YhxAs1+wxb5jk7RvS0LHdyiILpNmRIRnZVztekOF0pg= 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.3 h1:EdDc1sNZNFDUlo4UrVAvvAofVI5EwTnKu8Nv8mgXkWQ= -github.com/ipfs/go-unixfs v0.4.3/go.mod h1:TSG7G1UuT+l4pNj91raXAPkX0BhJi3jST1FDTfQ5QyM= -github.com/ipfs/go-unixfsnode v1.5.2 h1:CvsiTt58W2uR5dD8bqQv+aAY0c1qolmXmSyNbPHYiew= -github.com/ipfs/go-unixfsnode v1.5.2/go.mod h1:NlOebRwYx8lMCNMdhAhEspYPBD3obp7TE0LvBqHY+ks= +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.6.0 h1:JOSA02yaLylRNi2rlB4ldPr5VcZhcnaIVj5zNLcOjDo= +github.com/ipfs/go-unixfsnode v1.6.0/go.mod h1:PVfoyZkX1B34qzT3vJO4nsLUpRCyhnMuHBznRcXirlk= github.com/ipfs/go-verifcid v0.0.1/go.mod h1:5Hrva5KBeIog4A+UpqlaIU+DEstipcJYQQZc0g37pY0= github.com/ipfs/go-verifcid v0.0.2 h1:XPnUv0XmdH+ZIhLGKg6U2vaPaRDXb9urMyNVCE7uvTs= github.com/ipfs/go-verifcid v0.0.2/go.mod h1:40cD9x1y4OWnFXbLNJYRe7MpNvWlMn3LZAG5Wb4xnPU= @@ -860,12 +863,12 @@ github.com/ipld/go-car v0.1.0/go.mod h1:RCWzaUh2i4mOEkB3W45Vc+9jnS/M6Qay5ooytiBH github.com/ipld/go-car v0.5.0 h1:kcCEa3CvYMs0iE5BzD5sV7O2EwMiCIp3uF8tA6APQT8= github.com/ipld/go-car v0.5.0/go.mod h1:ppiN5GWpjOZU9PgpAZ9HbZd9ZgSpwPMr48fGRJOWmvE= github.com/ipld/go-car/v2 v2.1.1/go.mod h1:+2Yvf0Z3wzkv7NeI69i8tuZ+ft7jyjPYIWZzeVNeFcI= -github.com/ipld/go-car/v2 v2.7.0 h1:OFxJl6X6Ii7y7wYX4R+P8q9+vuz4vaY2Y9u1GHzfxbE= -github.com/ipld/go-car/v2 v2.7.0/go.mod h1:qoqfgPnQYcaAYcfphctffdaNWJIWBR2QN4pjuKUtgao= +github.com/ipld/go-car/v2 v2.10.0 h1:0Wrt0uk3IoBge1PjEokXsS1eOX6v8QxeTxjPQ9TH71M= +github.com/ipld/go-car/v2 v2.10.0/go.mod h1:mBZ4d86IKvL7eKhNHhQgywQ5coZHAGhmG1P+cMrdby8= github.com/ipld/go-codec-dagpb v1.2.0/go.mod h1:6nBN7X7h8EOsEejZGqC7tej5drsdBAXbMHyBT+Fne5s= github.com/ipld/go-codec-dagpb v1.3.0/go.mod h1:ga4JTU3abYApDC3pZ00BC2RSvC3qfBb9MSJkMLSwnhA= -github.com/ipld/go-codec-dagpb v1.5.0 h1:RspDRdsJpLfgCI0ONhTAnbHdySGD4t+LHSPK4X1+R0k= -github.com/ipld/go-codec-dagpb v1.5.0/go.mod h1:0yRIutEFD8o1DGVqw4RSHh+BUTlJA9XWldxaaWR/o4g= +github.com/ipld/go-codec-dagpb v1.6.0 h1:9nYazfyu9B1p3NAgfVdpRco3Fs2nFC72DqVsMj6rOcc= +github.com/ipld/go-codec-dagpb v1.6.0/go.mod h1:ANzFhfP2uMJxRBr8CE+WQWs5UsNa0pYtmKZ+agnUw9s= github.com/ipld/go-ipld-adl-hamt v0.0.0-20220616142416-9004dbd839e0 h1:QAI/Ridj0+foHD6epbxmB4ugxz9B4vmNdYSmQLGa05E= github.com/ipld/go-ipld-adl-hamt v0.0.0-20220616142416-9004dbd839e0/go.mod h1:odxGcpiQZLzP5+yGu84Ljo8y3EzCvNAQKEodHNsHLXA= github.com/ipld/go-ipld-prime v0.0.2-0.20191108012745-28a82f04c785/go.mod h1:bDDSvVz7vaK12FNvMeRYnpRFkSUPNQOiCYQezMD/P3w= @@ -1002,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.26.2 h1:eHEoW/696FP7/6DxOvcrKfTD6Bi0DExxiMSZUJxswA0= -github.com/libp2p/go-libp2p v0.26.2/go.mod h1:x75BN32YbwuY0Awm2Uix4d4KOz+/4piInkp4Wr3yOo8= +github.com/libp2p/go-libp2p v0.26.3 h1:6g/psubqwdaBqNNoidbRKSTBEYgaOuKBhHl8Q5tO+PM= +github.com/libp2p/go-libp2p v0.26.3/go.mod h1:x75BN32YbwuY0Awm2Uix4d4KOz+/4piInkp4Wr3yOo8= github.com/libp2p/go-libp2p-asn-util v0.2.0 h1:rg3+Os8jbnO5DxkC7K/Utdi+DkY3q/d1/1q+8WeNAsw= github.com/libp2p/go-libp2p-asn-util v0.2.0/go.mod h1:WoaWxbHKBymSN41hWSq/lGKJEca7TNm58+gGJi2WsLI= github.com/libp2p/go-libp2p-autonat v0.1.0/go.mod h1:1tLf2yXxiE/oKGtDwPYWTSYG3PtvYlJmg7NeVtPRqH8= @@ -1053,8 +1056,8 @@ github.com/libp2p/go-libp2p-gorpc v0.5.0 h1:mmxxAPdP3JzpYH4KcDf4csXnqtd1HazLPfdy 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.21.0 h1:J0Yd22VA+sk0CJRGMgtfHvLVIkZDyJ3AJGiljywIw5U= -github.com/libp2p/go-libp2p-kad-dht v0.21.0/go.mod h1:Bhm9diAFmc6qcWAr084bHNL159srVZRKADdp96Qqd1I= +github.com/libp2p/go-libp2p-kad-dht v0.21.1 h1:xpfp8/t9+X2ip1l8Umap1/UGNnJ3RHJgKGAEsnRAlTo= +github.com/libp2p/go-libp2p-kad-dht v0.21.1/go.mod h1:Oy8wvbdjpB70eS5AaFaI68tOtrdo3KylTvXDjikxqFo= github.com/libp2p/go-libp2p-kbucket v0.5.0 h1:g/7tVm8ACHDxH29BGrpsQlnNeu+6OF1A9bno/4/U1oA= github.com/libp2p/go-libp2p-kbucket v0.5.0/go.mod h1:zGzGCpQd78b5BNTDGHNDLaTt9aDK/A02xeZp9QeFC4U= github.com/libp2p/go-libp2p-loggables v0.1.0/go.mod h1:EyumB2Y6PrYjr55Q3/tiJ/o3xoDasoRYM7nOzEpoa90= @@ -1322,8 +1325,8 @@ github.com/multiformats/go-multibase v0.1.1 h1:3ASCDsuLX8+j4kx58qnJ4YFq/JWTJpCyD github.com/multiformats/go-multibase v0.1.1/go.mod h1:ZEjHE+IsUrgp5mhlEAYjMtZwK1k4haNkcaPg9aoe1a8= github.com/multiformats/go-multicodec v0.3.0/go.mod h1:qGGaQmioCDh+TeFOnxrbU0DaIPw8yFgAZgFG0V7p1qQ= github.com/multiformats/go-multicodec v0.3.1-0.20210902112759-1539a079fd61/go.mod h1:1Hj/eHRaVWSXiSNNfcEPcwZleTmdNP81xlxDLnWU9GQ= -github.com/multiformats/go-multicodec v0.8.0 h1:evBmgkbSQux+Ds2IgfhkO38Dl2GDtRW8/Rp6YiSHX/Q= -github.com/multiformats/go-multicodec v0.8.0/go.mod h1:GUC8upxSBE4oG+q3kWZRw/+6yC1BqO550bjhWsJbZlw= +github.com/multiformats/go-multicodec v0.8.1 h1:ycepHwavHafh3grIbR1jIXnKCsFm0fqsfEOsJ8NtKE8= +github.com/multiformats/go-multicodec v0.8.1/go.mod h1:L3QTQvMIaVBkXOXXtVmYE+LI16i14xuaojr/H7Ai54k= github.com/multiformats/go-multihash v0.0.1/go.mod h1:w/5tugSrLEbWqlcgJabL3oHFKTwfvkofsjW2Qa1ct4U= github.com/multiformats/go-multihash v0.0.5/go.mod h1:lt/HCbqlQwlPBz7lv0sQCdtfcMtlJvakRUn/0Ual8po= github.com/multiformats/go-multihash v0.0.8/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew= @@ -1596,8 +1599,9 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ 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 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= @@ -1729,8 +1733,8 @@ go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo= go.opentelemetry.io/otel v1.2.0/go.mod h1:aT17Fk0Z1Nor9e0uisf98LrntPGMnk4frBO9+dkf69I= -go.opentelemetry.io/otel v1.12.0 h1:IgfC7kqQrRccIKuB7Cl+SRUmsKbEwSGPr0Eu+/ht1SQ= -go.opentelemetry.io/otel v1.12.0/go.mod h1:geaoz0L0r1BEOR81k7/n9W4TCXYCJ7bPO7K374jQHG0= +go.opentelemetry.io/otel v1.14.0 h1:/79Huy8wbf5DnIPhemGB+zEPVwnN6fuQybr/SRXa6hM= +go.opentelemetry.io/otel v1.14.0/go.mod h1:o4buv+dJzx8rohcUeRmWUZhqupFvzWis188WlggnNeU= go.opentelemetry.io/otel/bridge/opencensus v0.33.0 h1:DnSFYr/VxUVwkHL0UoaMcxx74Jugb1HO0B08cYBmi0c= go.opentelemetry.io/otel/bridge/opencensus v0.33.0/go.mod h1:gylOY4P2e7kPYc6T9M8XfQ5+RK4+evGorTOOy+gO4Nc= go.opentelemetry.io/otel/exporters/jaeger v1.2.0 h1:C/5Egj3MJBXRJi22cSl07suqPqtZLnLFmH//OxETUEc= @@ -1747,8 +1751,8 @@ go.opentelemetry.io/otel/sdk/metric v0.33.0 h1:oTqyWfksgKoJmbrs2q7O7ahkJzt+Ipeki go.opentelemetry.io/otel/sdk/metric v0.33.0/go.mod h1:xdypMeA21JBOvjjzDUtD0kzIcHO/SPez+a8HOzJPGp0= go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw= go.opentelemetry.io/otel/trace v1.2.0/go.mod h1:N5FLswTubnxKxOJHM7XZC074qpeEdLy3CgAVsdMucK0= -go.opentelemetry.io/otel/trace v1.12.0 h1:p28in++7Kd0r2d8gSt931O57fdjUyWxkVbESuILAeUc= -go.opentelemetry.io/otel/trace v1.12.0/go.mod h1:pHlgBynn6s25qJ2szD+Bv+iwKJttjHSI3lUAyf0GNuQ= +go.opentelemetry.io/otel/trace v1.14.0 h1:wp2Mmvj41tDsyAJXiWDWpfNsOiIyd38fy85pyKcFq/M= +go.opentelemetry.io/otel/trace v1.14.0/go.mod h1:8avnQLK+CG77yNLUae4ea2JDQ6iT+gozhnZjy/rw9G8= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= @@ -1823,8 +1827,8 @@ golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5 golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211209193657-4570a0811e8b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE= -golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU= +golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc= +golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= 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= @@ -1838,8 +1842,8 @@ golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EH golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/exp v0.0.0-20210615023648-acb5c1269671/go.mod h1:DVyR6MI7P4kEQgvZJSj1fQGrWIi2RzIrfYWycwheUAc= golang.org/x/exp v0.0.0-20210714144626-1041f73d31d8/go.mod h1:DVyR6MI7P4kEQgvZJSj1fQGrWIi2RzIrfYWycwheUAc= -golang.org/x/exp v0.0.0-20230129154200-a960b3787bd2 h1:5sPMf9HJXrvBWIamTw+rTST0bZ3Mho2n1p58M0+W99c= -golang.org/x/exp v0.0.0-20230129154200-a960b3787bd2/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= +golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb h1:PaBZQdo+iSDyHT053FjUCgZQ/9uqVwPOcl7KSWhKn6w= +golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -2051,8 +2055,8 @@ golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/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-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 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= diff --git a/itests/deals_partial_retrieval_dm-level_test.go b/itests/deals_partial_retrieval_dm-level_test.go index e3414c19171..4e59b163bc6 100644 --- a/itests/deals_partial_retrieval_dm-level_test.go +++ b/itests/deals_partial_retrieval_dm-level_test.go @@ -9,8 +9,8 @@ import ( "testing" "time" + blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" - blocks "github.com/ipfs/go-libipfs/blocks" "github.com/ipld/go-car" textselector "github.com/ipld/go-ipld-selector-text-lite" "github.com/stretchr/testify/require" diff --git a/itests/deals_partial_retrieval_test.go b/itests/deals_partial_retrieval_test.go index e2dae9aff19..0bbf23da054 100644 --- a/itests/deals_partial_retrieval_test.go +++ b/itests/deals_partial_retrieval_test.go @@ -9,8 +9,8 @@ import ( "testing" "time" + blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" - blocks "github.com/ipfs/go-libipfs/blocks" "github.com/ipld/go-car" "github.com/stretchr/testify/require" "golang.org/x/xerrors" diff --git a/itests/kit/deals.go b/itests/kit/deals.go index 39b7857d75f..f4c55152658 100644 --- a/itests/kit/deals.go +++ b/itests/kit/deals.go @@ -9,9 +9,9 @@ import ( "testing" "time" + "github.com/ipfs/boxo/files" "github.com/ipfs/go-cid" ipld "github.com/ipfs/go-ipld-format" - "github.com/ipfs/go-libipfs/files" dag "github.com/ipfs/go-merkledag" dstest "github.com/ipfs/go-merkledag/test" unixfile "github.com/ipfs/go-unixfs/file" diff --git a/itests/kit/files.go b/itests/kit/files.go index dbf7d2cd782..063dcf7e046 100644 --- a/itests/kit/files.go +++ b/itests/kit/files.go @@ -9,6 +9,7 @@ import ( "os" "testing" + "github.com/ipfs/boxo/files" "github.com/ipfs/go-blockservice" "github.com/ipfs/go-cid" "github.com/ipfs/go-cidutil" @@ -18,7 +19,6 @@ import ( chunk "github.com/ipfs/go-ipfs-chunker" offline "github.com/ipfs/go-ipfs-exchange-offline" ipldformat "github.com/ipfs/go-ipld-format" - "github.com/ipfs/go-libipfs/files" "github.com/ipfs/go-merkledag" "github.com/ipfs/go-unixfs/importer/balanced" ihelper "github.com/ipfs/go-unixfs/importer/helpers" diff --git a/lib/unixfs/filestore.go b/lib/unixfs/filestore.go index 0a0b61c4ca7..678520b2796 100644 --- a/lib/unixfs/filestore.go +++ b/lib/unixfs/filestore.go @@ -6,6 +6,7 @@ import ( "io" "os" + "github.com/ipfs/boxo/files" "github.com/ipfs/go-blockservice" "github.com/ipfs/go-cid" "github.com/ipfs/go-cidutil" @@ -13,7 +14,6 @@ import ( chunker "github.com/ipfs/go-ipfs-chunker" offline "github.com/ipfs/go-ipfs-exchange-offline" ipld "github.com/ipfs/go-ipld-format" - "github.com/ipfs/go-libipfs/files" "github.com/ipfs/go-merkledag" "github.com/ipfs/go-unixfs/importer/balanced" ihelper "github.com/ipfs/go-unixfs/importer/helpers" diff --git a/lib/unixfs/filestore_test.go b/lib/unixfs/filestore_test.go index 67d380701bd..2f5e2d93990 100644 --- a/lib/unixfs/filestore_test.go +++ b/lib/unixfs/filestore_test.go @@ -9,10 +9,10 @@ import ( "strings" "testing" + "github.com/ipfs/boxo/files" "github.com/ipfs/go-blockservice" "github.com/ipfs/go-cid" offline "github.com/ipfs/go-ipfs-exchange-offline" - "github.com/ipfs/go-libipfs/files" "github.com/ipfs/go-merkledag" unixfile "github.com/ipfs/go-unixfs/file" carv2 "github.com/ipld/go-car/v2" diff --git a/markets/dagstore/blockstore.go b/markets/dagstore/blockstore.go index 593204ec8e5..317cb08b9fc 100644 --- a/markets/dagstore/blockstore.go +++ b/markets/dagstore/blockstore.go @@ -4,9 +4,9 @@ import ( "context" "io" + blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" bstore "github.com/ipfs/go-ipfs-blockstore" - blocks "github.com/ipfs/go-libipfs/blocks" "golang.org/x/xerrors" "github.com/filecoin-project/dagstore" diff --git a/markets/storageadapter/api.go b/markets/storageadapter/api.go index 3105f2261c0..b93ffdfbb16 100644 --- a/markets/storageadapter/api.go +++ b/markets/storageadapter/api.go @@ -3,9 +3,9 @@ package storageadapter import ( "context" + blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" cbor "github.com/ipfs/go-ipld-cbor" - blocks "github.com/ipfs/go-libipfs/blocks" "golang.org/x/xerrors" "github.com/filecoin-project/go-address" diff --git a/markets/storageadapter/ondealsectorcommitted_test.go b/markets/storageadapter/ondealsectorcommitted_test.go index 592c22db7d5..1d7519ff9f9 100644 --- a/markets/storageadapter/ondealsectorcommitted_test.go +++ b/markets/storageadapter/ondealsectorcommitted_test.go @@ -10,8 +10,8 @@ import ( "testing" "time" + blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" - blocks "github.com/ipfs/go-libipfs/blocks" "github.com/stretchr/testify/require" "golang.org/x/xerrors" diff --git a/node/impl/client/client.go b/node/impl/client/client.go index e45d537e27a..860b50d8497 100644 --- a/node/impl/client/client.go +++ b/node/impl/client/client.go @@ -13,12 +13,12 @@ import ( "sync" "time" + "github.com/ipfs/boxo/files" "github.com/ipfs/go-blockservice" "github.com/ipfs/go-cid" bstore "github.com/ipfs/go-ipfs-blockstore" offline "github.com/ipfs/go-ipfs-exchange-offline" format "github.com/ipfs/go-ipld-format" - "github.com/ipfs/go-libipfs/files" logging "github.com/ipfs/go-log/v2" "github.com/ipfs/go-merkledag" unixfile "github.com/ipfs/go-unixfs/file" diff --git a/node/impl/client/client_test.go b/node/impl/client/client_test.go index 032fef55a4c..3feaac096ac 100644 --- a/node/impl/client/client_test.go +++ b/node/impl/client/client_test.go @@ -10,13 +10,13 @@ import ( "strings" "testing" + "github.com/ipfs/boxo/files" "github.com/ipfs/go-blockservice" "github.com/ipfs/go-cid" "github.com/ipfs/go-datastore" dssync "github.com/ipfs/go-datastore/sync" blockstore "github.com/ipfs/go-ipfs-blockstore" offline "github.com/ipfs/go-ipfs-exchange-offline" - "github.com/ipfs/go-libipfs/files" "github.com/ipfs/go-merkledag" unixfile "github.com/ipfs/go-unixfs/file" "github.com/ipld/go-car" diff --git a/node/impl/full/chain.go b/node/impl/full/chain.go index d0f36ce488c..addca2b9737 100644 --- a/node/impl/full/chain.go +++ b/node/impl/full/chain.go @@ -15,12 +15,12 @@ import ( "sync" "time" + blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-blockservice" "github.com/ipfs/go-cid" offline "github.com/ipfs/go-ipfs-exchange-offline" cbor "github.com/ipfs/go-ipld-cbor" ipld "github.com/ipfs/go-ipld-format" - blocks "github.com/ipfs/go-libipfs/blocks" logging "github.com/ipfs/go-log/v2" "github.com/ipfs/go-merkledag" mh "github.com/multiformats/go-multihash" diff --git a/node/modules/chain.go b/node/modules/chain.go index 762c77e4bf3..c4f6d644e9c 100644 --- a/node/modules/chain.go +++ b/node/modules/chain.go @@ -4,9 +4,9 @@ import ( "context" "time" + "github.com/ipfs/boxo/bitswap" + "github.com/ipfs/boxo/bitswap/network" "github.com/ipfs/go-blockservice" - "github.com/ipfs/go-libipfs/bitswap" - "github.com/ipfs/go-libipfs/bitswap/network" "github.com/libp2p/go-libp2p/core/host" "github.com/libp2p/go-libp2p/core/routing" "go.uber.org/fx" From 141c020b4e6adff675f4628be7d138a84f364c7b Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 18 Apr 2023 15:44:56 -0700 Subject: [PATCH 173/243] fix: remove pointless panic Technically, the block validator caught this panic. But it's pointless because we have a _real_ mechanism to return the validation reason, which we should have been using. In general, panicing like this is a very bad idea because it's non-obvious and, in this case, completely undocumented. --- chain/consensus/iface.go | 8 -------- 1 file changed, 8 deletions(-) diff --git a/chain/consensus/iface.go b/chain/consensus/iface.go index 10c3ead74df..ff6c337f9d0 100644 --- a/chain/consensus/iface.go +++ b/chain/consensus/iface.go @@ -65,15 +65,9 @@ func ValidateBlockPubsub(ctx context.Context, cns Consensus, self bool, msg *pub stats.Record(ctx, metrics.BlockReceived.M(1)) - recordFailureFlagPeer := func(what string) { - // bv.Validate will flag the peer in that case - panic(what) - } - blk, what, err := decodeAndCheckBlock(msg) if err != nil { log.Error("got invalid block over pubsub: ", err) - recordFailureFlagPeer(what) return pubsub.ValidationReject, what } @@ -81,7 +75,6 @@ func ValidateBlockPubsub(ctx context.Context, cns Consensus, self bool, msg *pub err = validateMsgMeta(ctx, blk) if err != nil { log.Warnf("error validating message metadata: %s", err) - recordFailureFlagPeer("invalid_block_meta") return pubsub.ValidationReject, "invalid_block_meta" } @@ -91,7 +84,6 @@ func ValidateBlockPubsub(ctx context.Context, cns Consensus, self bool, msg *pub log.Warn("ignoring block msg: ", err) return pubsub.ValidationIgnore, reject } - recordFailureFlagPeer(reject) return pubsub.ValidationReject, reject } From 4bdb6b34b31be33f05739b478547b69e3bc59a5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 19 Apr 2023 15:43:50 +0200 Subject: [PATCH 174/243] fix: sealing pipeline: Allow nil message in TerminateWait --- storage/pipeline/states_proving.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/storage/pipeline/states_proving.go b/storage/pipeline/states_proving.go index bed61a452a1..500dcab48e6 100644 --- a/storage/pipeline/states_proving.go +++ b/storage/pipeline/states_proving.go @@ -81,7 +81,12 @@ func (m *Sealing) handleTerminating(ctx statemachine.Context, sector SectorInfo) func (m *Sealing) handleTerminateWait(ctx statemachine.Context, sector SectorInfo) error { if sector.TerminateMessage == nil { - return xerrors.New("entered TerminateWait with nil TerminateMessage") + ts, err := m.Api.ChainHead(ctx.Context()) + if err != nil { + return ctx.Send(SectorTerminateFailed{xerrors.Errorf("getting chain head: %w", err)}) + } + + return ctx.Send(SectorTerminated{TerminatedAt: ts.Height()}) } mw, err := m.Api.StateWaitMsg(ctx.Context(), *sector.TerminateMessage, build.MessageConfidence, api.LookbackNoLimit, true) From 0c83781a7f1b4518ffb993198e1fa8f1cbd91bb7 Mon Sep 17 00:00:00 2001 From: Shrenuj Bansal Date: Wed, 19 Apr 2023 18:44:32 -0400 Subject: [PATCH 175/243] Add tests for PCB/PCA batch splitting --- node/impl/full/gas.go | 2 - storage/pipeline/commit_batch.go | 5 --- storage/pipeline/commit_batch_test.go | 53 +++++++++++++++++++++++- storage/pipeline/precommit_batch_test.go | 53 +++++++++++++++++------- 4 files changed, 89 insertions(+), 24 deletions(-) diff --git a/node/impl/full/gas.go b/node/impl/full/gas.go index 33d0478734f..c5b22354a52 100644 --- a/node/impl/full/gas.go +++ b/node/impl/full/gas.go @@ -384,13 +384,11 @@ func gasEstimateGasLimit( func (m *GasModule) GasEstimateMessageGas(ctx context.Context, msg *types.Message, spec *api.MessageSendSpec, _ types.TipSetKey) (*types.Message, error) { if msg.GasLimit == 0 { gasLimit, err := m.GasEstimateGasLimit(ctx, msg, types.EmptyTSK) - log.Errorf("GasEstimateMessageGas GasLimit: %f", gasLimit) if err != nil { return nil, err } msg.GasLimit = int64(float64(gasLimit) * m.Mpool.GetConfig().GasLimitOverestimation) - log.Errorf("GasEstimateMessageGas GasLimit: %f, GasLimitWithOverestimation", gasLimit, msg.GasLimit) // Gas overestimation can cause us to exceed the block gas limit, cap it. if msg.GasLimit > build.BlockGasLimit { msg.GasLimit = build.BlockGasLimit diff --git a/storage/pipeline/commit_batch.go b/storage/pipeline/commit_batch.go index 09f1357d722..afcd2a12e77 100644 --- a/storage/pipeline/commit_batch.go +++ b/storage/pipeline/commit_batch.go @@ -290,11 +290,6 @@ func (b *CommitBatcher) processBatch(cfg sealiface.Config, sectors []abi.SectorN collateral := big.Zero() for _, sector := range sectors { - if len(infos) >= cfg.MaxCommitBatch { - log.Infow("commit batch full") - break - } - res.Sectors = append(res.Sectors, sector) sc, err := b.getSectorCollateral(sector, ts.Key()) diff --git a/storage/pipeline/commit_batch_test.go b/storage/pipeline/commit_batch_test.go index ef45b6b7144..15c2100cb85 100644 --- a/storage/pipeline/commit_batch_test.go +++ b/storage/pipeline/commit_batch_test.go @@ -201,8 +201,7 @@ func TestCommitBatcher(t *testing.T) { 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: 1000000}, nil) - //s.EXPECT().ChainBaseFee(gomock.Any(), gomock.Any()).Return(basefee, 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 { @@ -225,6 +224,48 @@ func TestCommitBatcher(t *testing.T) { } } + 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) @@ -310,6 +351,14 @@ func TestCommitBatcher(t *testing.T) { 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), diff --git a/storage/pipeline/precommit_batch_test.go b/storage/pipeline/precommit_batch_test.go index b9c02530fa1..6951faad73f 100644 --- a/storage/pipeline/precommit_batch_test.go +++ b/storage/pipeline/precommit_batch_test.go @@ -156,22 +156,34 @@ func TestPrecommitBatcher(t *testing.T) { } //stm: @CHAIN_STATE_MINER_INFO_001, @CHAIN_STATE_NETWORK_VERSION_001 - expectSend := func(expect []abi.SectorNumber) action { + 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) s.EXPECT().StateNetworkVersion(gomock.Any(), gomock.Any()).Return(network.Version14, nil) - - s.EXPECT().StateMinerInfo(gomock.Any(), gomock.Any(), gomock.Any()).Return(api.MinerInfo{Owner: t0123, Worker: t0123}, 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) - 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 } } @@ -199,7 +211,8 @@ func TestPrecommitBatcher(t *testing.T) { flush := func(expect []abi.SectorNumber) action { return func(t *testing.T, s *mocks.MockPreCommitBatcherApi, pcb *pipeline.PreCommitBatcher) promise { - _ = expectSend(expect)(t, s, pcb) + _ = expectInitialCalls()(t, s, pcb) + _ = expectSend(expect, false)(t, s, pcb) r, err := pcb.Flush(ctx) require.NoError(t, err) @@ -241,7 +254,17 @@ func TestPrecommitBatcher(t *testing.T) { }, "addMax": { actions: []action{ - expectSend(getSectors(maxBatch)), + 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), }, }, From d1f3380850b3b8d8bcc7a764b3b4f2e1fca63fac Mon Sep 17 00:00:00 2001 From: Shrenuj Bansal Date: Thu, 20 Apr 2023 12:15:51 -0400 Subject: [PATCH 176/243] change comment --- storage/pipeline/commit_batch.go | 2 +- storage/pipeline/precommit_batch.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/storage/pipeline/commit_batch.go b/storage/pipeline/commit_batch.go index afcd2a12e77..9948b5432dc 100644 --- a/storage/pipeline/commit_batch.go +++ b/storage/pipeline/commit_batch.go @@ -392,7 +392,7 @@ func (b *CommitBatcher) processBatch(cfg sealiface.Config, sectors []abi.SectorN return []sealiface.CommitBatchRes{res}, xerrors.Errorf("simulating CommitBatch message failed: %w", err) } - // If we're out of gas, split the batch in half and try again + // If we're out of gas, split the batch in half and evaluate again 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 diff --git a/storage/pipeline/precommit_batch.go b/storage/pipeline/precommit_batch.go index 869c4feb57b..63e26366296 100644 --- a/storage/pipeline/precommit_batch.go +++ b/storage/pipeline/precommit_batch.go @@ -374,7 +374,7 @@ func (b *PreCommitBatcher) processPreCommitBatch(cfg sealiface.Config, bf abi.To return []sealiface.PreCommitBatchRes{res}, xerrors.Errorf("simulating PreCommitBatch message failed: %w", err) } - // If we're out of gas, split the batch in half and try again + // If we're out of gas, split the batch in half and evaluate again if api.ErrorIsIn(err, []error{&api.ErrOutOfGas{}}) { log.Warnf("PreCommitBatch out of gas, splitting batch in half and trying again") mid := len(entries) / 2 From 00a025449b1736e7ee1848ae5fcd33d98d847f55 Mon Sep 17 00:00:00 2001 From: jennijuju Date: Fri, 21 Apr 2023 00:17:15 +0800 Subject: [PATCH 177/243] bump matser version to v1.23.1-dev --- build/openrpc/full.json.gz | Bin 33857 -> 33857 bytes build/openrpc/gateway.json.gz | Bin 9538 -> 9538 bytes build/openrpc/miner.json.gz | Bin 15921 -> 15921 bytes build/openrpc/worker.json.gz | Bin 5224 -> 5224 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 e56fd0fdae6e539bd29666c71d040c2998b06652..e684d154ae5abe05b743037f879658d356b05c53 100644 GIT binary patch delta 26 icmX@u!E~^LX+j%I&$eyx8@tvwbFe?$Qnu(68v_8QmJ9&^ delta 26 icmX@u!E~^LX+j%IPCjqk#;*0v911eGTNa&SV*mh>rV4og diff --git a/build/openrpc/gateway.json.gz b/build/openrpc/gateway.json.gz index 0b48141945082f10adaa1bd4aecd41da3751600b..9a3332019ce090a79095033ecc92dcb2b73c0239 100644 GIT binary patch delta 24 gcmX@)b;xT%155IT%helOwyAJ5e~*xTrNqPl0F#*tq5uE@ delta 24 gcmX@)b;xT%1Iyy=muok+Y*XR*{N~*0S4vC_0HMMRbN~PV diff --git a/build/openrpc/miner.json.gz b/build/openrpc/miner.json.gz index 1dd7d16852fac86ec600d2ad3adabad96f67ef22..728e5a82c0a741ab0e38bda8ff2561f3f8b0aed2 100644 GIT binary patch delta 23 fcmdm3v$1AE6Z6G?2^(9N*m7hRhuo4EV`cyVj&2G7 delta 23 fcmdm3v$1AE6Z65k`i-qiY&qV~51S@0#>@Z!i@XW{ diff --git a/build/openrpc/worker.json.gz b/build/openrpc/worker.json.gz index ea2e3066f8858fa4e6e8f0a096f8d5827571a996..2468be53acb51956481c83a0f1aa59459f6d923e 100644 GIT binary patch delta 23 fcmaE%@j_!lBlCsbSsPpK2y<+1cqzG7kC6cYj4ujj delta 23 fcmaE%@j_!lBXhuRuZ=BtggG|czR$B(kC6cYgjWg( diff --git a/build/version.go b/build/version.go index 9c950395c44..f00783bcee8 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.21.0-dev" +const BuildVersion = "1.23.1-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 d304dc11da9..687e0ae42e7 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.21.0-dev + 1.23.1-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 7b52a1c4f3b..8f6114f8a27 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.21.0-dev + 1.23.1-dev COMMANDS: run Start lotus worker diff --git a/documentation/en/cli-lotus.md b/documentation/en/cli-lotus.md index 325b2738d63..8b422e9dca2 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.21.0-dev + 1.23.1-dev COMMANDS: daemon Start a lotus daemon process From 7b6f6843a743c33f21ae051634072ca3a904caab Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 20 Apr 2023 13:27:44 -0700 Subject: [PATCH 178/243] fix: tvx: correctly lookup actor codes in extract-many --- cmd/tvx/extract_many.go | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/cmd/tvx/extract_many.go b/cmd/tvx/extract_many.go index 96ce2473b23..7c8d306d8c0 100644 --- a/cmd/tvx/extract_many.go +++ b/cmd/tvx/extract_many.go @@ -7,18 +7,20 @@ import ( "log" "os" "path/filepath" + "regexp" "strconv" "strings" "github.com/fatih/color" "github.com/hashicorp/go-multierror" "github.com/ipfs/go-cid" - "github.com/multiformats/go-multihash" "github.com/urfave/cli/v2" "github.com/filecoin-project/go-state-types/abi" + actorstypes "github.com/filecoin-project/go-state-types/actors" "github.com/filecoin-project/go-state-types/exitcode" + "github.com/filecoin-project/lotus/chain/actors" "github.com/filecoin-project/lotus/chain/consensus" ) @@ -67,6 +69,8 @@ var extractManyCmd = &cli.Command{ }, } +var actorCodeRegex = regexp.MustCompile(`^fil/(?P\d+)/(?P\w+)$`) + func runExtractMany(c *cli.Context) error { // LOTUS_DISABLE_VM_BUF disables what's called "VM state tree buffering", // which stashes write operations in a BufferedBlockstore @@ -114,8 +118,6 @@ func runExtractMany(c *cli.Context) error { log.Println(color.GreenString("csv sanity check succeeded; header contains fields: %v", header)) } - codeCidBuilder := cid.V1Builder{Codec: cid.Raw, MhType: multihash.IDENTITY} - var ( generated []string merr = new(multierror.Error) @@ -153,9 +155,21 @@ func runExtractMany(c *cli.Context) error { return fmt.Errorf("invalid method number: %s", methodnumstr) } - codeCid, err := codeCidBuilder.Sum([]byte(actorcode)) - if err != nil { - return fmt.Errorf("failed to compute actor code CID") + // Lookup the code CID. + var codeCid cid.Cid + if matches := actorCodeRegex.FindStringSubmatch(actorcode); len(matches) == 3 { + av, err := strconv.Atoi(matches[1]) + if err != nil { + return fmt.Errorf("invalid actor version %q in actor code %q", matches[1], actorcode) + } + an := matches[2] + if k, ok := actors.GetActorCodeID(actorstypes.Version(av), an); ok { + codeCid = k + } else { + return fmt.Errorf("unknown actor code %q", actorcode) + } + } else { + return fmt.Errorf("invalid actor code %q", actorcode) } // Lookup the method in actor method table. From 54d8ddf3c1f723e071fb65fe874ca2a7cbd0fa2b Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 20 Apr 2023 13:50:16 -0700 Subject: [PATCH 179/243] fix: tvx: fvm vm.Flush --- conformance/driver.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/conformance/driver.go b/conformance/driver.go index e0d56d07410..eb5973f72b3 100644 --- a/conformance/driver.go +++ b/conformance/driver.go @@ -297,12 +297,12 @@ func (d *Driver) ExecuteMessage(bs blockstore.Blockstore, params ExecuteMessageP } var root cid.Cid - if d.vmFlush { + if lvm, ok := vmi.(*vm.LegacyVM); ok && !d.vmFlush { + root, err = lvm.StateTree().(*state.StateTree).Flush(d.ctx) + } else { // flush the VM, committing the state tree changes and forcing a // recursive copy from the temporary blockstore to the real blockstore. root, err = vmi.Flush(d.ctx) - } else { - root, err = vmi.(*vm.LegacyVM).StateTree().(*state.StateTree).Flush(d.ctx) } return ret, root, err From c84c07eb74e1c55b0c5b193b3a50432dbb301d89 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 20 Apr 2023 14:44:16 -0700 Subject: [PATCH 180/243] fix: events: don't set GC confidence to 1 The function/parameter were poorly named and should never have been exposed. "GC" confidence should always be the same, this parameter doesn't let us actually set the _confidence_, just the point before which we no longer support reverts. fixes #10706 --- chain/events/events.go | 4 ++-- node/modules/actorevent.go | 2 +- node/modules/ethmodule.go | 4 +--- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/chain/events/events.go b/chain/events/events.go index 86aded64d45..c68b62a64e0 100644 --- a/chain/events/events.go +++ b/chain/events/events.go @@ -47,7 +47,7 @@ type Events struct { *hcEvents } -func NewEventsWithConfidence(ctx context.Context, api EventAPI, gcConfidence abi.ChainEpoch) (*Events, error) { +func newEventsWithGCConfidence(ctx context.Context, api EventAPI, gcConfidence abi.ChainEpoch) (*Events, error) { cache := newCache(api, gcConfidence) ob := newObserver(cache, gcConfidence) @@ -63,5 +63,5 @@ func NewEventsWithConfidence(ctx context.Context, api EventAPI, gcConfidence abi func NewEvents(ctx context.Context, api EventAPI) (*Events, error) { gcConfidence := 2 * build.ForkLengthThreshold - return NewEventsWithConfidence(ctx, api, gcConfidence) + return newEventsWithGCConfidence(ctx, api, gcConfidence) } diff --git a/node/modules/actorevent.go b/node/modules/actorevent.go index 55a79a59a97..68a6990ce61 100644 --- a/node/modules/actorevent.go +++ b/node/modules/actorevent.go @@ -130,7 +130,7 @@ func EthEventAPI(cfg config.FevmConfig) func(helpers.MetricsCtx, repo.LockedRepo lc.Append(fx.Hook{ OnStart: func(context.Context) error { - ev, err := events.NewEventsWithConfidence(ctx, &evapi, ChainHeadConfidence) + ev, err := events.NewEvents(ctx, &evapi) if err != nil { return err } diff --git a/node/modules/ethmodule.go b/node/modules/ethmodule.go index eba6c54d1dd..074e911e23f 100644 --- a/node/modules/ethmodule.go +++ b/node/modules/ethmodule.go @@ -54,12 +54,10 @@ func EthModuleAPI(cfg config.FevmConfig) func(helpers.MetricsCtx, repo.LockedRep } } - const ChainHeadConfidence = 1 - ctx := helpers.LifecycleCtx(mctx, lc) lc.Append(fx.Hook{ OnStart: func(context.Context) error { - ev, err := events.NewEventsWithConfidence(ctx, &evapi, ChainHeadConfidence) + ev, err := events.NewEvents(ctx, &evapi) if err != nil { return err } From 953d56e216a50b04e937a847aedc52da536b20b5 Mon Sep 17 00:00:00 2001 From: Fridrik Asmundsson Date: Wed, 19 Apr 2023 18:46:51 +0000 Subject: [PATCH 181/243] perf: Address performance of EthGetTransactionCount We have observed that EthGetTransactionCount is one of the hotspots on Glif production notes, and we are seeing regular 10-20 second latencies when calling this rpc method. I tracked the high latency spikes and they were correlated when we were running ExecuteTipSet while following the chain. To address this, we should not rely on tipset computation to get nounce and instead look at the parent tipset and then count the messages sent from the 'addr'. --- chain/messagepool/messagepool.go | 84 ++++++++++++++++++--------- chain/messagepool/messagepool_test.go | 16 +++++ chain/messagepool/provider.go | 38 ++++++++---- 3 files changed, 100 insertions(+), 38 deletions(-) diff --git a/chain/messagepool/messagepool.go b/chain/messagepool/messagepool.go index b0e7b7e2b73..03025d7d7ee 100644 --- a/chain/messagepool/messagepool.go +++ b/chain/messagepool/messagepool.go @@ -169,13 +169,13 @@ type MessagePool struct { sigValCache *lru.TwoQueueCache[string, struct{}] - nonceCache *lru.Cache[nonceCacheKey, uint64] + stateNonceCache *lru.Cache[stateNonceCacheKey, uint64] evtTypes [3]journal.EventType journal journal.Journal } -type nonceCacheKey struct { +type stateNonceCacheKey struct { tsk types.TipSetKey addr address.Address } @@ -371,7 +371,7 @@ func (ms *msgSet) toSlice() []*types.SignedMessage { func New(ctx context.Context, api Provider, ds dtypes.MetadataDS, us stmgr.UpgradeSchedule, netName dtypes.NetworkName, j journal.Journal) (*MessagePool, error) { cache, _ := lru.New2Q[cid.Cid, crypto.Signature](build.BlsSignatureCacheSize) verifcache, _ := lru.New2Q[string, struct{}](build.VerifSigCacheSize) - noncecache, _ := lru.New[nonceCacheKey, uint64](256) + stateNonceCache, _ := lru.New[stateNonceCacheKey, uint64](256) keycache, _ := lru.New[address.Address, address.Address](1_000_000) cfg, err := loadConfig(ctx, ds) @@ -384,26 +384,26 @@ func New(ctx context.Context, api Provider, ds dtypes.MetadataDS, us stmgr.Upgra } mp := &MessagePool{ - ds: ds, - addSema: make(chan struct{}, 1), - closer: make(chan struct{}), - repubTk: build.Clock.Ticker(RepublishInterval), - repubTrigger: make(chan struct{}, 1), - localAddrs: make(map[address.Address]struct{}), - pending: make(map[address.Address]*msgSet), - keyCache: keycache, - minGasPrice: types.NewInt(0), - getNtwkVersion: us.GetNtwkVersion, - pruneTrigger: make(chan struct{}, 1), - pruneCooldown: make(chan struct{}, 1), - blsSigCache: cache, - sigValCache: verifcache, - nonceCache: noncecache, - changes: lps.New(50), - localMsgs: namespace.Wrap(ds, datastore.NewKey(localMsgsDs)), - api: api, - netName: netName, - cfg: cfg, + ds: ds, + addSema: make(chan struct{}, 1), + closer: make(chan struct{}), + repubTk: build.Clock.Ticker(RepublishInterval), + repubTrigger: make(chan struct{}, 1), + localAddrs: make(map[address.Address]struct{}), + pending: make(map[address.Address]*msgSet), + keyCache: keycache, + minGasPrice: types.NewInt(0), + getNtwkVersion: us.GetNtwkVersion, + pruneTrigger: make(chan struct{}, 1), + pruneCooldown: make(chan struct{}, 1), + blsSigCache: cache, + sigValCache: verifcache, + stateNonceCache: stateNonceCache, + changes: lps.New(50), + localMsgs: namespace.Wrap(ds, datastore.NewKey(localMsgsDs)), + api: api, + netName: netName, + cfg: cfg, evtTypes: [...]journal.EventType{ evtTypeMpoolAdd: j.RegisterEventType("mpool", "add"), evtTypeMpoolRemove: j.RegisterEventType("mpool", "remove"), @@ -1068,24 +1068,52 @@ func (mp *MessagePool) getStateNonce(ctx context.Context, addr address.Address, done := metrics.Timer(ctx, metrics.MpoolGetNonceDuration) defer done() - nk := nonceCacheKey{ + nk := stateNonceCacheKey{ tsk: ts.Key(), addr: addr, } - n, ok := mp.nonceCache.Get(nk) + n, ok := mp.stateNonceCache.Get(nk) if ok { return n, nil } - act, err := mp.api.GetActorAfter(addr, ts) + raddr, err := mp.resolveToKey(ctx, addr) + if err != nil { + return 0, err + } + + // get the nonce from the actor before ts + actor, err := mp.api.GetActorBefore(addr, ts) + if err != nil { + return 0, err + } + nextNonce := actor.Nonce + + // loop over all messages sent by 'addr' and find the highest nonce + messages, err := mp.api.MessagesForTipset(ctx, ts) if err != nil { return 0, err } + for _, message := range messages { + msg := message.VMMessage() + + maddr, err := mp.resolveToKey(ctx, msg.From) + if err != nil { + log.Warnf("failed to resolve message from address: %s", err) + continue + } + + if maddr == raddr { + if n := msg.Nonce + 1; n > nextNonce { + nextNonce = n + } + } + } - mp.nonceCache.Add(nk, act.Nonce) + mp.stateNonceCache.Add(nk, nextNonce) - return act.Nonce, nil + return nextNonce, nil } func (mp *MessagePool) getStateBalance(ctx context.Context, addr address.Address, ts *types.TipSet) (types.BigInt, error) { diff --git a/chain/messagepool/messagepool_test.go b/chain/messagepool/messagepool_test.go index 20da2317e9b..a781b50748c 100644 --- a/chain/messagepool/messagepool_test.go +++ b/chain/messagepool/messagepool_test.go @@ -120,6 +120,22 @@ func (tma *testMpoolAPI) PubSubPublish(string, []byte) error { return nil } +func (tma *testMpoolAPI) GetActorBefore(addr address.Address, ts *types.TipSet) (*types.Actor, error) { + balance, ok := tma.balance[addr] + if !ok { + balance = types.NewInt(1000e6) + tma.balance[addr] = balance + } + + nonce := tma.statenonce[addr] + + return &types.Actor{ + Code: builtin2.AccountActorCodeID, + Nonce: nonce, + Balance: balance, + }, nil +} + func (tma *testMpoolAPI) GetActorAfter(addr address.Address, ts *types.TipSet) (*types.Actor, error) { // regression check for load bug if ts == nil { diff --git a/chain/messagepool/provider.go b/chain/messagepool/provider.go index 123a2607ea0..764e6c13a92 100644 --- a/chain/messagepool/provider.go +++ b/chain/messagepool/provider.go @@ -2,6 +2,7 @@ package messagepool import ( "context" + "errors" "time" "github.com/ipfs/go-cid" @@ -27,6 +28,7 @@ type Provider interface { SubscribeHeadChanges(func(rev, app []*types.TipSet) error) *types.TipSet PutMessage(ctx context.Context, m types.ChainMsg) (cid.Cid, error) PubSubPublish(string, []byte) error + GetActorBefore(address.Address, *types.TipSet) (*types.Actor, error) GetActorAfter(address.Address, *types.TipSet) (*types.Actor, error) StateDeterministicAddressAtFinality(context.Context, address.Address, *types.TipSet) (address.Address, error) StateNetworkVersion(context.Context, abi.ChainEpoch) network.Version @@ -58,6 +60,23 @@ func (mpp *mpoolProvider) IsLite() bool { return mpp.lite != nil } +func (mpp *mpoolProvider) getActorLite(addr address.Address, ts *types.TipSet) (*types.Actor, error) { + if !mpp.IsLite() { + return nil, errors.New("should not use getActorLite on non lite Provider") + } + + n, err := mpp.lite.GetNonce(context.TODO(), addr, ts.Key()) + if err != nil { + return nil, xerrors.Errorf("getting nonce over lite: %w", err) + } + a, err := mpp.lite.GetActor(context.TODO(), addr, ts.Key()) + if err != nil { + return nil, xerrors.Errorf("getting actor over lite: %w", err) + } + a.Nonce = n + return a, nil +} + func (mpp *mpoolProvider) SubscribeHeadChanges(cb func(rev, app []*types.TipSet) error) *types.TipSet { mpp.sm.ChainStore().SubscribeHeadChanges( store.WrapHeadChangeCoalescer( @@ -77,18 +96,17 @@ func (mpp *mpoolProvider) PubSubPublish(k string, v []byte) error { return mpp.ps.Publish(k, v) // nolint } +func (mpp *mpoolProvider) GetActorBefore(addr address.Address, ts *types.TipSet) (*types.Actor, error) { + if mpp.IsLite() { + return mpp.getActorLite(addr, ts) + } + + return mpp.sm.LoadActor(context.TODO(), addr, ts) +} + func (mpp *mpoolProvider) GetActorAfter(addr address.Address, ts *types.TipSet) (*types.Actor, error) { if mpp.IsLite() { - n, err := mpp.lite.GetNonce(context.TODO(), addr, ts.Key()) - if err != nil { - return nil, xerrors.Errorf("getting nonce over lite: %w", err) - } - a, err := mpp.lite.GetActor(context.TODO(), addr, ts.Key()) - if err != nil { - return nil, xerrors.Errorf("getting actor over lite: %w", err) - } - a.Nonce = n - return a, nil + return mpp.getActorLite(addr, ts) } stcid, _, err := mpp.sm.TipSetState(context.TODO(), ts) From 553da395e4abd7409884c7f460f679a895709325 Mon Sep 17 00:00:00 2001 From: Fridrik Asmundsson Date: Fri, 21 Apr 2023 11:56:05 +0000 Subject: [PATCH 182/243] perf: Increase noncecache in MessagePool Bumped from 256 to 32k entries which should be about 6MB of cached entries given average nonceCacheKey of 200 bytes --- chain/messagepool/messagepool.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chain/messagepool/messagepool.go b/chain/messagepool/messagepool.go index 03025d7d7ee..54be7446364 100644 --- a/chain/messagepool/messagepool.go +++ b/chain/messagepool/messagepool.go @@ -371,7 +371,7 @@ func (ms *msgSet) toSlice() []*types.SignedMessage { func New(ctx context.Context, api Provider, ds dtypes.MetadataDS, us stmgr.UpgradeSchedule, netName dtypes.NetworkName, j journal.Journal) (*MessagePool, error) { cache, _ := lru.New2Q[cid.Cid, crypto.Signature](build.BlsSignatureCacheSize) verifcache, _ := lru.New2Q[string, struct{}](build.VerifSigCacheSize) - stateNonceCache, _ := lru.New[stateNonceCacheKey, uint64](256) + stateNonceCache, _ := lru.New[stateNonceCacheKey, uint64](32768) // 32k * ~200 bytes = 6MB keycache, _ := lru.New[address.Address, address.Address](1_000_000) cfg, err := loadConfig(ctx, ds) From 4028c05feabee46968377810e603927b76064d88 Mon Sep 17 00:00:00 2001 From: Fridrik Asmundsson Date: Fri, 21 Apr 2023 20:03:13 +0000 Subject: [PATCH 183/243] address review comment --- chain/messagepool/messagepool.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/chain/messagepool/messagepool.go b/chain/messagepool/messagepool.go index 54be7446364..6c3e776c09f 100644 --- a/chain/messagepool/messagepool.go +++ b/chain/messagepool/messagepool.go @@ -1078,17 +1078,17 @@ func (mp *MessagePool) getStateNonce(ctx context.Context, addr address.Address, return n, nil } - raddr, err := mp.resolveToKey(ctx, addr) + // get the nonce from the actor before ts + actor, err := mp.api.GetActorBefore(addr, ts) if err != nil { return 0, err } + nextNonce := actor.Nonce - // get the nonce from the actor before ts - actor, err := mp.api.GetActorBefore(addr, ts) + raddr, err := mp.resolveToKey(ctx, addr) if err != nil { return 0, err } - nextNonce := actor.Nonce // loop over all messages sent by 'addr' and find the highest nonce messages, err := mp.api.MessagesForTipset(ctx, ts) From d39fbb277f5f6c1bd7799c6f14fc539832122009 Mon Sep 17 00:00:00 2001 From: Shrenuj Bansal Date: Fri, 21 Apr 2023 16:35:31 -0400 Subject: [PATCH 184/243] Set default for MaxSectorProveCommitsSubmittedPerEpoch --- documentation/en/default-lotus-miner-config.toml | 2 +- node/config/def.go | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/documentation/en/default-lotus-miner-config.toml b/documentation/en/default-lotus-miner-config.toml index f75131af4e5..3b46ad7d1c0 100644 --- a/documentation/en/default-lotus-miner-config.toml +++ b/documentation/en/default-lotus-miner-config.toml @@ -665,7 +665,7 @@ # # type: uint64 # env var: LOTUS_SEALING_MAXSECTORPROVECOMMITSSUBMITTEDPEREPOCH - #MaxSectorProveCommitsSubmittedPerEpoch = 0 + #MaxSectorProveCommitsSubmittedPerEpoch = 20 # type: uint64 # env var: LOTUS_SEALING_TERMINATEBATCHMAX diff --git a/node/config/def.go b/node/config/def.go index 4020e4ca615..54d8963f14b 100644 --- a/node/config/def.go +++ b/node/config/def.go @@ -151,9 +151,10 @@ func DefaultStorageMiner() *StorageMiner { BatchPreCommitAboveBaseFee: types.FIL(types.BigMul(types.PicoFil, types.NewInt(320))), // 0.32 nFIL AggregateAboveBaseFee: types.FIL(types.BigMul(types.PicoFil, types.NewInt(320))), // 0.32 nFIL - TerminateBatchMin: 1, - TerminateBatchMax: 100, - TerminateBatchWait: Duration(5 * time.Minute), + TerminateBatchMin: 1, + TerminateBatchMax: 100, + TerminateBatchWait: Duration(5 * time.Minute), + MaxSectorProveCommitsSubmittedPerEpoch: 20, }, Proving: ProvingConfig{ From 875c09840b13bbf4c45679727cfbe6bf83e04d29 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Sat, 22 Apr 2023 00:16:26 +0200 Subject: [PATCH 185/243] chainstore: Fix raw blocks getting scanned for links during snapshots (#10684) We have to save raw blocks to the snapshot, but we should not be scanning them for additional links as if they were CBOR blocks. This cleans the logic a bit (we were checking that the parent was a CBOR block before queueing up the children, but then scanning the children... it was weird). Additionally, more verbose logging is added for the next time ScanForLinks fails (currently very little info was given). Our ScanForLinks callback should only enqueue CBOR for further processing. --- chain/store/snapshot.go | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/chain/store/snapshot.go b/chain/store/snapshot.go index 66d23675ef3..307bd356c1c 100644 --- a/chain/store/snapshot.go +++ b/chain/store/snapshot.go @@ -371,6 +371,9 @@ func (s *walkScheduler) enqueueIfNew(task walkTask) { //log.Infow("ignored", "cid", todo.c.String()) return } + + // This lets through RAW and CBOR blocks, the only two types that we + // end up writing to the exported CAR. if task.c.Prefix().Codec != cid.Raw && task.c.Prefix().Codec != cid.DagCBOR { //log.Infow("ignored", "cid", todo.c.String()) return @@ -442,10 +445,19 @@ func (s *walkScheduler) processTask(t walkTask, workerN int) error { b: blk, } + // We exported the ipld block. If it wasn't a CBOR block, there's nothing + // else to do and we can bail out early as it won't have any links + // etc. + if t.c.Prefix().Codec != cid.DagCBOR || t.c.Prefix().MhType == mh.IDENTITY { + return nil + } + + rawData := blk.RawData() + // extract relevant dags to walk from the block if t.taskType == blockTask { var b types.BlockHeader - if err := b.UnmarshalCBOR(bytes.NewBuffer(blk.RawData())); err != nil { + if err := b.UnmarshalCBOR(bytes.NewBuffer(rawData)); err != nil { return xerrors.Errorf("unmarshalling block header (cid=%s): %w", blk, err) } if b.Height%1_000 == 0 { @@ -521,11 +533,7 @@ func (s *walkScheduler) processTask(t walkTask, workerN int) error { } // Not a chain-block: we scan for CIDs in the raw block-data - return cbg.ScanForLinks(bytes.NewReader(blk.RawData()), func(c cid.Cid) { - if t.c.Prefix().Codec != cid.DagCBOR || t.c.Prefix().MhType == mh.IDENTITY { - return - } - + err = cbg.ScanForLinks(bytes.NewReader(rawData), func(c cid.Cid) { s.enqueueIfNew(walkTask{ c: c, taskType: dagTask, @@ -534,6 +542,13 @@ func (s *walkScheduler) processTask(t walkTask, workerN int) error { epoch: t.epoch, }) }) + + if err != nil { + return xerrors.Errorf( + "ScanForLinks(%s). Task: %s. Block: %s (%s). Epoch: %d. Err: %w", + t.c, t.taskType, t.topLevelTaskType, t.blockCid, t.epoch, err) + } + return nil } func (cs *ChainStore) ExportRange( From 784214ae050669ff83fb2fccdc5edb2fdbb71caa Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Sat, 22 Apr 2023 10:15:31 -0700 Subject: [PATCH 186/243] feat: sync: validate (early) that blocks fall within range (#10691) This will reject blocks in pubsub validation if they're either: 1. Too far into the future (5 blocks beyond the expected head). 2. Too far into the past (before finality with respect to our current head). Specifically: 1. We were previously rejecting future blocks in the sync logic, but not in pubsub itself. 2. We never used to check if a block was too _old_. Motivation: Blocks that are too new/too old can cause us to perform quite a bit of unnecessary work. --- chain/consensus/filcns/filecoin.go | 12 ++++++++++-- chain/consensus/iface.go | 15 ++++++++++++--- chain/sync.go | 2 +- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/chain/consensus/filcns/filecoin.go b/chain/consensus/filcns/filecoin.go index 2e3baa4db29..509eb8a5e85 100644 --- a/chain/consensus/filcns/filecoin.go +++ b/chain/consensus/filcns/filecoin.go @@ -382,13 +382,21 @@ func (filec *FilecoinEC) VerifyWinningPoStProof(ctx context.Context, nv network. return nil } -func (filec *FilecoinEC) IsEpochBeyondCurrMax(epoch abi.ChainEpoch) bool { +func (filec *FilecoinEC) IsEpochInConsensusRange(epoch abi.ChainEpoch) bool { if filec.genesis == nil { + return true + } + + // Don't try to sync anything before finality. Don't propagate such blocks either. + // + // We use _our_ current head, not the expected head, because the network's head can lag on + // catch-up (after a network outage). + if epoch < filec.store.GetHeaviestTipSet().Height()-build.Finality { return false } now := uint64(build.Clock.Now().Unix()) - return epoch > (abi.ChainEpoch((now-filec.genesis.MinTimestamp())/build.BlockDelaySecs) + MaxHeightDrift) + return epoch <= (abi.ChainEpoch((now-filec.genesis.MinTimestamp())/build.BlockDelaySecs) + MaxHeightDrift) } func (filec *FilecoinEC) minerIsValid(ctx context.Context, maddr address.Address, baseTs *types.TipSet) error { diff --git a/chain/consensus/iface.go b/chain/consensus/iface.go index ff6c337f9d0..9449cb5a47a 100644 --- a/chain/consensus/iface.go +++ b/chain/consensus/iface.go @@ -32,9 +32,10 @@ type Consensus interface { // the block (signature verifications, VRF checks, message validity, etc.) ValidateBlock(ctx context.Context, b *types.FullBlock) (err error) - // IsEpochBeyondCurrMax is used to configure the fork rules for longest-chain - // consensus protocols. - IsEpochBeyondCurrMax(epoch abi.ChainEpoch) bool + // IsEpochInConsensusRange returns true if the epoch is "in range" for consensus. That is: + // - It's not before finality. + // - It's not too far in the future. + IsEpochInConsensusRange(epoch abi.ChainEpoch) bool // CreateBlock implements all the logic required to propose and assemble a new Filecoin block. // @@ -71,6 +72,14 @@ func ValidateBlockPubsub(ctx context.Context, cns Consensus, self bool, msg *pub return pubsub.ValidationReject, what } + if !cns.IsEpochInConsensusRange(blk.Header.Height) { + // We ignore these blocks instead of rejecting to avoid breaking the network if + // we're recovering from an outage (e.g., where nobody agrees on where "head" is + // currently). + log.Warnf("received block outside of consensus range (%d)", blk.Header.Height) + return pubsub.ValidationIgnore, "invalid_block_height" + } + // validate the block meta: the Message CID in the header must match the included messages err = validateMsgMeta(ctx, blk) if err != nil { diff --git a/chain/sync.go b/chain/sync.go index b0c8b50ff96..d38b3723ac4 100644 --- a/chain/sync.go +++ b/chain/sync.go @@ -208,7 +208,7 @@ func (syncer *Syncer) InformNewHead(from peer.ID, fts *store.FullTipSet) bool { return false } - if syncer.consensus.IsEpochBeyondCurrMax(fts.TipSet().Height()) { + if !syncer.consensus.IsEpochInConsensusRange(fts.TipSet().Height()) { log.Errorf("Received block with impossibly large height %d", fts.TipSet().Height()) return false } From c8e78f7911ac8b53b02bc324d949fce571b4fe4b Mon Sep 17 00:00:00 2001 From: zenground0 Date: Sun, 23 Apr 2023 15:43:53 -0600 Subject: [PATCH 187/243] Fix lint after merge --- cmd/lotus-shed/miner.go | 2 +- go.mod | 2 +- go.sum | 17 +++++++++++++---- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/cmd/lotus-shed/miner.go b/cmd/lotus-shed/miner.go index 1772ced6338..a8bb9374422 100644 --- a/cmd/lotus-shed/miner.go +++ b/cmd/lotus-shed/miner.go @@ -12,9 +12,9 @@ import ( "strings" "github.com/docker/go-units" + block "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" ipldcbor "github.com/ipfs/go-ipld-cbor" - block "github.com/ipfs/go-libipfs/blocks" "github.com/mitchellh/go-homedir" "github.com/urfave/cli/v2" cbg "github.com/whyrusleeping/cbor-gen" diff --git a/go.mod b/go.mod index c9427987c76..d190323f286 100644 --- a/go.mod +++ b/go.mod @@ -99,7 +99,6 @@ require ( github.com/ipfs/go-ipfs-util v0.0.2 github.com/ipfs/go-ipld-cbor v0.0.6 github.com/ipfs/go-ipld-format v0.4.0 - github.com/ipfs/go-libipfs v0.7.0 github.com/ipfs/go-log/v2 v2.5.1 github.com/ipfs/go-merkledag v0.10.0 github.com/ipfs/go-metrics-interface v0.0.1 @@ -244,6 +243,7 @@ require ( github.com/ipfs/go-ipfs-pq v0.0.3 // indirect github.com/ipfs/go-ipld-legacy v0.1.1 // indirect github.com/ipfs/go-ipns v0.3.0 // indirect + github.com/ipfs/go-libipfs v0.7.0 // indirect github.com/ipfs/go-log v1.0.5 // indirect github.com/ipfs/go-path v0.3.1 // indirect github.com/ipfs/go-peertaskqueue v0.8.1 // indirect diff --git a/go.sum b/go.sum index a0c1148845c..ecfcbf0f1bd 100644 --- a/go.sum +++ b/go.sum @@ -290,7 +290,6 @@ github.com/filecoin-project/dagstore v0.5.2 h1:Nd6oXdnolbbVhpMpkYT5PJHOjQp4OBSnt 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= github.com/filecoin-project/go-address v0.0.5/go.mod h1:jr8JxKsYx+lQlQZmF5i2U0Z+cGQ59wMIps/8YW/lDj8= -github.com/filecoin-project/go-address v0.0.6/go.mod h1:7B0/5DA13n6nHkB8bbGx1gWzG/dbTsZ0fgOJVGsM3TE= github.com/filecoin-project/go-address v1.1.0 h1:ofdtUtEsNxkIxkDw67ecSmvtzaVSdcea4boAmLbnHfE= github.com/filecoin-project/go-address v1.1.0/go.mod h1:5t3z6qPmIADZBtuE9EIzi0EwzcRy2nVhpo0I/c1r0OA= github.com/filecoin-project/go-amt-ipld/v2 v2.1.0 h1:t6qDiuGYYngDqaLc2ZUvdtAg4UNxPeOYaXhBWSNsVaM= @@ -345,8 +344,8 @@ github.com/filecoin-project/go-state-types v0.0.0-20200928172055-2df22083d8ab/go github.com/filecoin-project/go-state-types v0.0.0-20201102161440-c8033295a1fc/go.mod h1:ezYnPf0bNkTsDibL/psSz5dy4B5awOJ/E7P2Saeep8g= 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.8/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.0-rc2/go.mod h1:SyNPwTsU7I22gL2r0OAPcImvLoTVfgRwdK/Y5rR1zz8= github.com/filecoin-project/go-state-types v0.11.1 h1:GDtCN9V18bYVwXDZe+vJXc6Ck+qY9OUaQqpoVlp1FAk= github.com/filecoin-project/go-state-types v0.11.1/go.mod h1:SyNPwTsU7I22gL2r0OAPcImvLoTVfgRwdK/Y5rR1zz8= github.com/filecoin-project/go-statemachine v0.0.0-20200925024713-05bd7c71fbfe/go.mod h1:FGwQgZAt2Gh5mjlwJUlVB62JeYdo+if0xWxSEfBD9ig= @@ -1707,6 +1706,7 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 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= @@ -1831,8 +1831,9 @@ golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20211209193657-4570a0811e8b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +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.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1875,6 +1876,7 @@ golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hM 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.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA= golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1936,6 +1938,8 @@ golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= 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.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -2052,21 +2056,24 @@ golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBc 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-20211209171907-798191bca915/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= 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.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 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 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -2077,6 +2084,7 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 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 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -2145,6 +2153,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.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.3.0 h1:SrNbZl6ECOS1qFzgTdQfWXZM9XBkiA6tkFrH9YSTPHM= golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From c5f2e99e7975396d7333dbe16f2d071108dbd6e3 Mon Sep 17 00:00:00 2001 From: zenground0 Date: Sun, 23 Apr 2023 15:51:34 -0600 Subject: [PATCH 188/243] go mod tidy --- go.sum | 1 - 1 file changed, 1 deletion(-) diff --git a/go.sum b/go.sum index ecfcbf0f1bd..80dcf1433bc 100644 --- a/go.sum +++ b/go.sum @@ -345,7 +345,6 @@ 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.0-rc2/go.mod h1:SyNPwTsU7I22gL2r0OAPcImvLoTVfgRwdK/Y5rR1zz8= github.com/filecoin-project/go-state-types v0.11.1 h1:GDtCN9V18bYVwXDZe+vJXc6Ck+qY9OUaQqpoVlp1FAk= github.com/filecoin-project/go-state-types v0.11.1/go.mod h1:SyNPwTsU7I22gL2r0OAPcImvLoTVfgRwdK/Y5rR1zz8= github.com/filecoin-project/go-statemachine v0.0.0-20200925024713-05bd7c71fbfe/go.mod h1:FGwQgZAt2Gh5mjlwJUlVB62JeYdo+if0xWxSEfBD9ig= From fd7a0e1922b78a40b7685bc4792fdc0ccf35ea28 Mon Sep 17 00:00:00 2001 From: Aayush Date: Mon, 24 Apr 2023 11:02:43 -0400 Subject: [PATCH 189/243] feat: shed: refactor market cron-state command --- cmd/lotus-shed/market.go | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/cmd/lotus-shed/market.go b/cmd/lotus-shed/market.go index 98e3d36b7c8..4436e3c404a 100644 --- a/cmd/lotus-shed/market.go +++ b/cmd/lotus-shed/market.go @@ -7,7 +7,6 @@ import ( "os" "path" - "github.com/ipfs/go-cid" "github.com/ipfs/go-datastore" dsq "github.com/ipfs/go-datastore/query" levelds "github.com/ipfs/go-ds-leveldb" @@ -65,26 +64,22 @@ var marketCronStateCmd = &cli.Command{ if err != nil { return err } - a, err := api.StateReadState(ctx, builtin.StorageMarketActorAddr, ts.Key()) + + bs := ReadOnlyAPIBlockstore{api} + adtStore := adt.WrapStore(ctx, ipldcbor.NewCborStore(&bs)) + + mAct, err := api.StateGetActor(ctx, builtin.StorageMarketActorAddr, ts.Key()) if err != nil { return err } - st, ok := a.State.(map[string]interface{}) - if !ok { - return xerrors.Errorf("failed to cast state map to expected form") - } - dealOpsRaw, ok := st["DealOpsByEpoch"].(map[string]interface{}) - if !ok { - return xerrors.Errorf("failed to read sectors root from state") - } - // string is of the form "/:bafy.." so [2:] to drop the path - dealOpsRoot, err := cid.Parse(dealOpsRaw["/"]) + + var mSt market11.State + err = adtStore.Get(ctx, mAct.Head, &mSt) if err != nil { return err } - bs := ReadOnlyAPIBlockstore{api} - adtStore := adt.WrapStore(ctx, ipldcbor.NewCborStore(&bs)) - dealOpsEpochSet, err := adt.AsMap(adtStore, dealOpsRoot, builtin.DefaultHamtBitwidth) + + dealOpsEpochSet, err := adt.AsMap(adtStore, mSt.DealOpsByEpoch, builtin.DefaultHamtBitwidth) if err != nil { return err } @@ -100,7 +95,7 @@ var marketCronStateCmd = &cli.Command{ return err } - dealOpsMultiMap, err := market11.AsSetMultimap(adtStore, dealOpsRoot, builtin.DefaultHamtBitwidth, builtin.DefaultHamtBitwidth) + dealOpsMultiMap, err := market11.AsSetMultimap(adtStore, mSt.DealOpsByEpoch, builtin.DefaultHamtBitwidth, builtin.DefaultHamtBitwidth) if err != nil { return err } From 9ae48022ff64f34fa5a1c2702a920c6f780bc754 Mon Sep 17 00:00:00 2001 From: ychiao Date: Mon, 24 Apr 2023 11:03:04 -0400 Subject: [PATCH 190/243] fix: Eth JSON-RPC api: handle messages with gasFeeCap less than baseFee (#10614) --- chain/types/message.go | 9 +++++++-- node/impl/full/eth_test.go | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/chain/types/message.go b/chain/types/message.go index 84f7a19a062..473289ead45 100644 --- a/chain/types/message.go +++ b/chain/types/message.go @@ -220,12 +220,17 @@ func (m *Message) ValidForBlockInclusion(minGas int64, version network.Version) } // EffectiveGasPremium returns the effective gas premium claimable by the miner -// given the supplied base fee. +// given the supplied base fee. This method is not used anywhere except the Eth API. // // Filecoin clamps the gas premium at GasFeeCap - BaseFee, if lower than the -// specified premium. +// specified premium. Returns 0 if GasFeeCap is less than BaseFee. func (m *Message) EffectiveGasPremium(baseFee abi.TokenAmount) abi.TokenAmount { available := big.Sub(m.GasFeeCap, baseFee) + // It's possible that storage providers may include messages with gasFeeCap less than the baseFee + // In such cases, their reward should be viewed as zero + if available.LessThan(big.NewInt(0)) { + available = big.NewInt(0) + } if big.Cmp(m.GasPremium, available) <= 0 { return m.GasPremium } diff --git a/node/impl/full/eth_test.go b/node/impl/full/eth_test.go index 87c0852fbd4..63dbb447eb2 100644 --- a/node/impl/full/eth_test.go +++ b/node/impl/full/eth_test.go @@ -114,7 +114,7 @@ func TestReward(t *testing.T) { {maxFeePerGas: big.NewInt(600), maxPriorityFeePerGas: big.NewInt(500), answer: big.NewInt(500)}, {maxFeePerGas: big.NewInt(600), maxPriorityFeePerGas: big.NewInt(600), answer: big.NewInt(500)}, {maxFeePerGas: big.NewInt(600), maxPriorityFeePerGas: big.NewInt(1000), answer: big.NewInt(500)}, - {maxFeePerGas: big.NewInt(50), maxPriorityFeePerGas: big.NewInt(200), answer: big.NewInt(-50)}, + {maxFeePerGas: big.NewInt(50), maxPriorityFeePerGas: big.NewInt(200), answer: big.NewInt(0)}, } for _, tc := range testcases { msg := &types.Message{GasFeeCap: tc.maxFeePerGas, GasPremium: tc.maxPriorityFeePerGas} From 71f184f5cbaf8f8359f684df570bb0d462c03cb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 7 Apr 2023 17:44:19 +0200 Subject: [PATCH 191/243] feat: daemon: Auto-resume interrupted snapshot imports --- cmd/lotus/daemon.go | 15 ++--- lib/httpreader/resumable.go | 112 ++++++++++++++++++++++++++++++++++++ 2 files changed, 117 insertions(+), 10 deletions(-) create mode 100644 lib/httpreader/resumable.go diff --git a/cmd/lotus/daemon.go b/cmd/lotus/daemon.go index c02200f26a0..fbb9dfd9a50 100644 --- a/cmd/lotus/daemon.go +++ b/cmd/lotus/daemon.go @@ -10,7 +10,6 @@ import ( "encoding/json" "fmt" "io" - "net/http" "os" "path" "runtime/pprof" @@ -43,6 +42,7 @@ import ( lcli "github.com/filecoin-project/lotus/cli" "github.com/filecoin-project/lotus/journal" "github.com/filecoin-project/lotus/journal/fsjournal" + "github.com/filecoin-project/lotus/lib/httpreader" "github.com/filecoin-project/lotus/lib/peermgr" "github.com/filecoin-project/lotus/lib/ulimit" "github.com/filecoin-project/lotus/metrics" @@ -434,18 +434,13 @@ func ImportChain(ctx context.Context, r repo.Repo, fname string, snapshot bool) var rd io.Reader var l int64 if strings.HasPrefix(fname, "http://") || strings.HasPrefix(fname, "https://") { - resp, err := http.Get(fname) //nolint:gosec + rrd, err := httpreader.NewResumableReader(ctx, fname) if err != nil { - return err - } - defer resp.Body.Close() //nolint:errcheck - - if resp.StatusCode != http.StatusOK { - return xerrors.Errorf("fetching chain CAR failed with non-200 response: %d", resp.StatusCode) + return xerrors.Errorf("fetching chain CAR failed: setting up resumable reader: %w", err) } - rd = resp.Body - l = resp.ContentLength + rd = rrd + l = rrd.ContentLength() } else { fname, err = homedir.Expand(fname) if err != nil { diff --git a/lib/httpreader/resumable.go b/lib/httpreader/resumable.go new file mode 100644 index 00000000000..c154299b8ba --- /dev/null +++ b/lib/httpreader/resumable.go @@ -0,0 +1,112 @@ +package httpreader + +import ( + "context" + "fmt" + "io" + "net/http" + "strconv" + + "golang.org/x/xerrors" +) + +type ResumableReader struct { + ctx context.Context + initialURL string + finalURL *string + position int64 + contentLength int64 + client *http.Client + reader io.ReadCloser +} + +func NewResumableReader(ctx context.Context, url string) (*ResumableReader, error) { + finalURL := "" + + client := &http.Client{ + CheckRedirect: func(req *http.Request, via []*http.Request) error { + finalURL = req.URL.String() + if len(via) >= 10 { + return xerrors.New("stopped after 10 redirects") + } + return nil + }, + } + + r := &ResumableReader{ + ctx: ctx, + initialURL: url, + finalURL: &finalURL, + position: 0, + client: client, + } + + req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + if err != nil { + return nil, err + } + + resp, err := r.client.Do(req) + if err != nil { + return nil, err + } + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("failed to fetch resource, status code: %d", resp.StatusCode) + } + + contentLength, err := strconv.ParseInt(resp.Header.Get("Content-Length"), 10, 64) + if err != nil { + resp.Body.Close() + return nil, err + } + + r.contentLength = contentLength + r.reader = resp.Body + + return r, nil +} + +func (r *ResumableReader) ContentLength() int64 { + return r.contentLength +} + +func (r *ResumableReader) Read(p []byte) (n int, err error) { + for { + if r.reader == nil { + reqURL := r.initialURL + if *r.finalURL != "" { + reqURL = *r.finalURL + } + + req, err := http.NewRequestWithContext(r.ctx, "GET", reqURL, nil) + if err != nil { + return 0, err + } + req.Header.Set("Range", fmt.Sprintf("bytes=%d-", r.position)) + resp, err := r.client.Do(req) + if err != nil { + return 0, err + } + + if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusPartialContent { + return 0, fmt.Errorf("non-resumable status code: %d", resp.StatusCode) + } + r.reader = resp.Body + } + + n, err = r.reader.Read(p) + r.position += int64(n) + + if err == io.EOF { + if r.position == r.contentLength { + r.reader.Close() + return n, err + } + r.reader.Close() + r.reader = nil + } else { + return n, err + } + } +} From a7d29c9564efc8065f89d1e53d32e3b1006511e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sat, 15 Apr 2023 14:02:50 +0200 Subject: [PATCH 192/243] httpreader: also resume on UnexpectedEOF --- lib/httpreader/resumable.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/httpreader/resumable.go b/lib/httpreader/resumable.go index c154299b8ba..a0ff7859630 100644 --- a/lib/httpreader/resumable.go +++ b/lib/httpreader/resumable.go @@ -98,7 +98,7 @@ func (r *ResumableReader) Read(p []byte) (n int, err error) { n, err = r.reader.Read(p) r.position += int64(n) - if err == io.EOF { + if err == io.EOF || err == io.ErrUnexpectedEOF { if r.position == r.contentLength { r.reader.Close() return n, err From 24945a906a484868e89c71d24828ef3fe43d62c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 25 Apr 2023 09:34:57 +0200 Subject: [PATCH 193/243] httpreader: Make linter happy --- lib/httpreader/resumable.go | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/lib/httpreader/resumable.go b/lib/httpreader/resumable.go index a0ff7859630..dacefeccc9a 100644 --- a/lib/httpreader/resumable.go +++ b/lib/httpreader/resumable.go @@ -7,9 +7,13 @@ import ( "net/http" "strconv" + logging "github.com/ipfs/go-log/v2" + "go.uber.org/multierr" "golang.org/x/xerrors" ) +var log = logging.Logger("httpreader") + type ResumableReader struct { ctx context.Context initialURL string @@ -57,7 +61,9 @@ func NewResumableReader(ctx context.Context, url string) (*ResumableReader, erro contentLength, err := strconv.ParseInt(resp.Header.Get("Content-Length"), 10, 64) if err != nil { - resp.Body.Close() + if err = resp.Body.Close(); err != nil { + err = multierr.Append(err, err) + } return nil, err } @@ -100,10 +106,14 @@ func (r *ResumableReader) Read(p []byte) (n int, err error) { if err == io.EOF || err == io.ErrUnexpectedEOF { if r.position == r.contentLength { - r.reader.Close() - return n, err + if err := r.reader.Close(); err != nil { + log.Warnf("error closing reader: %+v", err) + } + return n, io.EOF + } + if err := r.reader.Close(); err != nil { + log.Warnf("error closing reader: %+v", err) } - r.reader.Close() r.reader = nil } else { return n, err From 7162c656ccf246be20ded423cd0b889dafbd24be Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 25 Apr 2023 11:56:26 -0700 Subject: [PATCH 194/243] fix: chain: record heaviest tipset before notifying (#10694) Clearly this hasn't caused any issues, but I'm pretty sure we should be updating the current head _before_ notifying about it. --- chain/store/store.go | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/chain/store/store.go b/chain/store/store.go index b34ddb2665d..dc9dc6484c5 100644 --- a/chain/store/store.go +++ b/chain/store/store.go @@ -639,22 +639,10 @@ func (cs *ChainStore) reorgWorker(ctx context.Context, initialNotifees []ReorgNo func (cs *ChainStore) takeHeaviestTipSet(ctx context.Context, ts *types.TipSet) error { _, span := trace.StartSpan(ctx, "takeHeaviestTipSet") defer span.End() - - if cs.heaviest != nil { // buf - if len(cs.reorgCh) > 0 { - log.Warnf("Reorg channel running behind, %d reorgs buffered", len(cs.reorgCh)) - } - cs.reorgCh <- reorg{ - old: cs.heaviest, - new: ts, - } - } else { - log.Warnf("no heaviest tipset found, using %s", ts.Cids()) - } - span.AddAttributes(trace.BoolAttribute("newHead", true)) log.Infof("New heaviest tipset! %s (height=%d)", ts.Cids(), ts.Height()) + prevHeaviest := cs.heaviest cs.heaviest = ts if err := cs.writeHead(ctx, ts); err != nil { @@ -662,6 +650,18 @@ func (cs *ChainStore) takeHeaviestTipSet(ctx context.Context, ts *types.TipSet) return err } + if prevHeaviest != nil { // buf + if len(cs.reorgCh) > 0 { + log.Warnf("Reorg channel running behind, %d reorgs buffered", len(cs.reorgCh)) + } + cs.reorgCh <- reorg{ + old: prevHeaviest, + new: ts, + } + } else { + log.Warnf("no previous heaviest tipset found, using %s", ts.Cids()) + } + return nil } From a97dae8f4631d6ac9283dafbf298779a6355d661 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 25 Apr 2023 14:34:13 -0700 Subject: [PATCH 195/243] fix: sync: reduce log from error to info And fix the message to account for the fact that we now reject _old_ blocks along with new ones. We frequently receive "out of date" blocks in hello messages from syncing and/or out of sync nodes. This isn't an error. --- chain/sync.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chain/sync.go b/chain/sync.go index d38b3723ac4..c6808a0c85a 100644 --- a/chain/sync.go +++ b/chain/sync.go @@ -209,7 +209,7 @@ func (syncer *Syncer) InformNewHead(from peer.ID, fts *store.FullTipSet) bool { } if !syncer.consensus.IsEpochInConsensusRange(fts.TipSet().Height()) { - log.Errorf("Received block with impossibly large height %d", fts.TipSet().Height()) + log.Infof("received block outside of consensus range at height %d", fts.TipSet().Height()) return false } From 33ca0f1f79aeccee71a5c9223b102e63fabc4a1b Mon Sep 17 00:00:00 2001 From: Aayush Date: Wed, 26 Apr 2023 09:50:16 -0400 Subject: [PATCH 196/243] shed: migrations: add reminder about continuity testing tool --- cmd/lotus-shed/migrations.go | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd/lotus-shed/migrations.go b/cmd/lotus-shed/migrations.go index 0d09fe6b885..82a1afddf4a 100644 --- a/cmd/lotus-shed/migrations.go +++ b/cmd/lotus-shed/migrations.go @@ -73,6 +73,7 @@ var migrationsCmd = &cli.Command{ }, }, Action: func(cctx *cli.Context) error { + fmt.Println("REMINDER: If you are running this, you likely want to ALSO run the continuity testing tool!") ctx := context.TODO() if cctx.NArg() != 2 { From d2c3e84d54ea446bf3200c0b680fc03afb18a8df Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 26 Apr 2023 14:12:45 -0700 Subject: [PATCH 197/243] feat: sync: harden chain sync (#10756) * fix: sync: fail sync instead of logging if we sync the wrong chain * fix: sync: write headers in the correct order Just in case. This shouldn't be necessary, but we might as well. * fix: minus minus * fix: do put the tipset Put != Persist --- chain/sync.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/chain/sync.go b/chain/sync.go index c6808a0c85a..de603520644 100644 --- a/chain/sync.go +++ b/chain/sync.go @@ -1193,12 +1193,14 @@ func (syncer *Syncer) collectChain(ctx context.Context, ts *types.TipSet, hts *t span.AddAttributes(trace.Int64Attribute("syncChainLength", int64(len(headers)))) if !headers[0].Equals(ts) { - log.Errorf("collectChain headers[0] should be equal to sync target. Its not: %s != %s", headers[0].Cids(), ts.Cids()) + return xerrors.Errorf("collectChain synced %s, wanted to sync %s", headers[0].Cids(), ts.Cids()) } ss.SetStage(api.StagePersistHeaders) - for _, ts := range headers { + // Write tipsets from oldest to newest. + for i := len(headers) - 1; i >= 0; i-- { + ts := headers[i] if err := syncer.store.PersistTipset(ctx, ts); err != nil { err = xerrors.Errorf("failed to persist synced tipset to the chainstore: %w", err) ss.Error(err) From e351d77ff8a008a9bbba5ace86704ee699ade106 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 26 Apr 2023 15:10:43 -0700 Subject: [PATCH 198/243] test: eth: deflake multiblock lookup test (#10769) - Increase epoch times to give the miners a chance to see each other's blocks. - Wait longer for a multi-block tipset. - Reduce the initial wait (we're increasing the block times and I don't really feel like waiting around). --- itests/eth_block_hash_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/itests/eth_block_hash_test.go b/itests/eth_block_hash_test.go index db21375c58c..b582c84e346 100644 --- a/itests/eth_block_hash_test.go +++ b/itests/eth_block_hash_test.go @@ -24,7 +24,7 @@ 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 := 100 * time.Millisecond + blocktime := 250 * time.Millisecond n1, m1, m2, ens := kit.EnsembleOneTwo(t, kit.MockProofs(), kit.ThroughRPC(), @@ -32,14 +32,14 @@ func TestEthBlockHashesCorrect_MultiBlockTipset(t *testing.T) { ens.InterconnectAll().BeginMining(blocktime) ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - n1.WaitTillChain(ctx, kit.HeightAtLeast(abi.ChainEpoch(25))) + n1.WaitTillChain(ctx, kit.HeightAtLeast(abi.ChainEpoch(5))) defer 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(), 1*time.Minute) + ctx, cancel = context.WithTimeout(context.Background(), 5*time.Minute) n2.WaitTillChain(ctx, kit.BlocksMinedByAll(m1.ActorAddr, m2.ActorAddr)) defer cancel() From 9d2d53b58e6665b57dac0a2762e0914e608bb4a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 27 Apr 2023 09:26:12 +0200 Subject: [PATCH 199/243] fix: prover: Propagate skipped sectors in local PoSt --- storage/sealer/manager_post.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/sealer/manager_post.go b/storage/sealer/manager_post.go index a6f43903c86..27a71ef8caf 100644 --- a/storage/sealer/manager_post.go +++ b/storage/sealer/manager_post.go @@ -85,7 +85,7 @@ func (m *Manager) GenerateWindowPoSt(ctx context.Context, minerID abi.ActorID, p log.Info("GenerateWindowPoSt run at lotus-miner") p, s, err := m.localProver.GenerateWindowPoSt(ctx, minerID, postProofType, sectorInfo, randomness) if err != nil { - return nil, nil, xerrors.Errorf("local prover: %w", err) + return p, s, xerrors.Errorf("local prover: %w", err) } return p, s, nil From e91bb642e74a472b147278db6142bafca3d35c64 Mon Sep 17 00:00:00 2001 From: Aayush Date: Wed, 26 Apr 2023 10:31:06 -0400 Subject: [PATCH 200/243] fix: deflake: use 2 miners for flaky tests --- itests/sector_finalize_early_test.go | 3 ++- itests/sector_import_full_test.go | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/itests/sector_finalize_early_test.go b/itests/sector_finalize_early_test.go index fb7d9d94d4c..87e1384a27f 100644 --- a/itests/sector_finalize_early_test.go +++ b/itests/sector_finalize_early_test.go @@ -30,7 +30,8 @@ func TestDealsWithFinalizeEarly(t *testing.T) { var blockTime = 50 * time.Millisecond - client, miner, ens := kit.EnsembleMinimal(t, kit.ThroughRPC(), kit.MutateSealingConfig(func(sc *config.SealingConfig) { sc.FinalizeEarly = true })) // no mock proofs. + // 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(), kit.MutateSealingConfig(func(sc *config.SealingConfig) { sc.FinalizeEarly = true })) // no mock proofs. ens.InterconnectAll().BeginMining(blockTime) dh := kit.NewDealHarness(t, client, miner, miner) diff --git a/itests/sector_import_full_test.go b/itests/sector_import_full_test.go index 35fc3e6232a..e4ec5e141f2 100644 --- a/itests/sector_import_full_test.go +++ b/itests/sector_import_full_test.go @@ -65,7 +65,8 @@ func TestSectorImport(t *testing.T) { //////// // Start a miner node - client, miner, ens := kit.EnsembleMinimal(t, kit.ThroughRPC()) + // 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) ctx := context.Background() From bb5ba64cca40c726cc4b2a2faa470ff9cb6b185b Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 27 Apr 2023 12:18:02 -0700 Subject: [PATCH 201/243] Revert "Merge pull request #9858 from adlrocha/adlrocha/consistent-bcast" This reverts commit 8b2208fd9a3d70fa4ef419c12a3953337d45c807, reversing changes made to 2db6b12b78baf6e73cfa16a86ae7e267fd967421. Unfortunately, this is rather tricky code. We've found several issues so far and, while we've fixed a few, there are outstanding issues that would require complex fixes we don't have time to tackle right now. Luckily, this code isn't actually needed by the main Filecoin chain which relies on consensus fault reporting to handle equivocation. So we can just try again later. --- build/params_2k.go | 5 - build/params_butterfly.go | 7 - build/params_calibnet.go | 6 - build/params_interop.go | 6 - build/params_mainnet.go | 6 - chain/sub/bcast/consistent.go | 203 -------------------------- chain/sub/bcast/consistent_test.go | 223 ----------------------------- chain/sub/incoming.go | 21 +-- chain/sync_test.go | 5 - itests/kit/init.go | 5 - node/modules/services.go | 2 +- 11 files changed, 2 insertions(+), 487 deletions(-) delete mode 100644 chain/sub/bcast/consistent.go delete mode 100644 chain/sub/bcast/consistent_test.go diff --git a/build/params_2k.go b/build/params_2k.go index 5d51ec90afc..c3199e2d63a 100644 --- a/build/params_2k.go +++ b/build/params_2k.go @@ -6,7 +6,6 @@ package build import ( "os" "strconv" - "time" "github.com/ipfs/go-cid" @@ -146,7 +145,3 @@ const BootstrapPeerThreshold = 1 const Eip155ChainId = 31415926 var WhitelistedBlock = cid.Undef - -// Reducing the delivery delay for equivocation of -// consistent broadcast to just half a second. -var CBDeliveryDelay = 500 * time.Millisecond diff --git a/build/params_butterfly.go b/build/params_butterfly.go index d0e07d471d3..e26fb4ad194 100644 --- a/build/params_butterfly.go +++ b/build/params_butterfly.go @@ -4,8 +4,6 @@ package build import ( - "time" - "github.com/ipfs/go-cid" "github.com/filecoin-project/go-address" @@ -93,8 +91,3 @@ const BootstrapPeerThreshold = 2 const Eip155ChainId = 3141592 var WhitelistedBlock = cid.Undef - -// CBDeliveryDelay is the delay before deliver in the synchronous consistent broadcast. -// This determines the wait time for the detection of potential equivocations. -// It is a variable instead of a constant so it can be conveniently configured in tests -var CBDeliveryDelay = 2 * time.Second diff --git a/build/params_calibnet.go b/build/params_calibnet.go index fe52feb33b9..e8b1c9d7ebd 100644 --- a/build/params_calibnet.go +++ b/build/params_calibnet.go @@ -6,7 +6,6 @@ package build import ( "os" "strconv" - "time" "github.com/ipfs/go-cid" @@ -129,8 +128,3 @@ const BootstrapPeerThreshold = 4 const Eip155ChainId = 314159 var WhitelistedBlock = cid.Undef - -// CBDeliveryDelay is the delay before deliver in the synchronous consistent broadcast. -// This determines the wait time for the detection of potential equivocations. -// It is a variable instead of a constant so it can be conveniently configured in tests -var CBDeliveryDelay = 2 * time.Second diff --git a/build/params_interop.go b/build/params_interop.go index c6d3faa3cf1..2d8d366e9ca 100644 --- a/build/params_interop.go +++ b/build/params_interop.go @@ -6,7 +6,6 @@ package build import ( "os" "strconv" - "time" "github.com/ipfs/go-cid" @@ -135,8 +134,3 @@ const BootstrapPeerThreshold = 2 const Eip155ChainId = 3141592 var WhitelistedBlock = cid.Undef - -// CBDeliveryDelay is the delay before deliver in the synchronous consistent broadcast. -// This determines the wait time for the detection of potential equivocations. -// It is a variable instead of a constant so it can be conveniently configured in tests -var CBDeliveryDelay = 2 * time.Second diff --git a/build/params_mainnet.go b/build/params_mainnet.go index 100639bfcd5..53eeb209167 100644 --- a/build/params_mainnet.go +++ b/build/params_mainnet.go @@ -7,7 +7,6 @@ import ( "math" "os" "strconv" - "time" "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/abi" @@ -148,8 +147,3 @@ const Eip155ChainId = 314 // we skip checks on message validity in this block to sidestep the zero-bls signature var WhitelistedBlock = MustParseCid("bafy2bzaceapyg2uyzk7vueh3xccxkuwbz3nxewjyguoxvhx77malc2lzn2ybi") - -// CBDeliveryDelay is the delay before deliver in the synchronous consistent broadcast. -// This determines the wait time for the detection of potential equivocations. -// It is a variable instead of a constant so it can be conveniently configured in tests -var CBDeliveryDelay = 2 * time.Second diff --git a/chain/sub/bcast/consistent.go b/chain/sub/bcast/consistent.go deleted file mode 100644 index 4d1a14db845..00000000000 --- a/chain/sub/bcast/consistent.go +++ /dev/null @@ -1,203 +0,0 @@ -package bcast - -import ( - "context" - "sync" - "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/chain/types" -) - -var log = logging.Logger("sub-cb") - -const ( - // GcSanityCheck determines the number of epochs in the past - // that will be garbage collected from the current epoch. - GcSanityCheck = 100 - // GcLookback determines the number of epochs kept in the consistent - // broadcast cache. - GcLookback = 5 - // GcDeepCheck determines the number of epochs in the past that we - // we try cleaning in the deep garbage collection round. - GcDeepCheck = 2880 // (24h*60m*60s)/30s per epoch - // GcDeepInterval determines after the number of epochs for which - // we are going to start a deeper garbage collection round. - GcDeepInterval = 1000 -) - -type blksInfo struct { - ctx context.Context - cancel context.CancelFunc - blks []cid.Cid -} - -type bcastDict struct { - m map[string]*blksInfo -} - -func (bd *bcastDict) load(key []byte) (*blksInfo, bool) { - v, ok := bd.m[string(key)] - if !ok { - return nil, ok - } - return v, ok -} - -func (bd *bcastDict) blkLen(key []byte) int { - return len(bd.m[string(key)].blks) -} - -func (bd *bcastDict) store(key []byte, d *blksInfo) { - bd.m[string(key)] = d -} - -// ConsistentBCast tracks recent information about the -// blocks and tickets received at different epochs -type ConsistentBCast struct { - lk sync.RWMutex - delay time.Duration - m map[abi.ChainEpoch]*bcastDict - lastDeepGc abi.ChainEpoch -} - -func newBcastDict() *bcastDict { - return &bcastDict{m: make(map[string]*blksInfo)} -} - -func BCastKey(bh *types.BlockHeader) []byte { - return bh.Ticket.VRFProof -} - -func NewConsistentBCast(delay time.Duration) *ConsistentBCast { - return &ConsistentBCast{ - delay: delay, - m: make(map[abi.ChainEpoch]*bcastDict), - } -} - -func cidExists(cids []cid.Cid, c cid.Cid) bool { - for _, v := range cids { - if v == c { - return true - } - } - return false -} - -func (bInfo *blksInfo) eqErr() error { - bInfo.cancel() - return xerrors.Errorf("different blocks with the same ticket already seen") -} - -func (cb *ConsistentBCast) Len() int { - cb.lk.RLock() - defer cb.lk.RUnlock() - return len(cb.m) -} - -// RcvBlock is called every time a new block is received through the network. -// -// This function keeps track of all the blocks with a specific VRFProof received -// for the same height. Every time a new block with a VRFProof not seen at certain -// height is received, a new timer is triggered to wait for the delay time determined by -// the consistent broadcast before informing the syncer. During this time, if a new -// block with the same VRFProof for that height is received, it means a miner is -// trying to equivocate, and both blocks are discarded. -// -// The delay time should be set to a value high enough to allow any block sent for -// certain epoch to be propagated to a large amount of miners in the network. -func (cb *ConsistentBCast) RcvBlock(ctx context.Context, blk *types.BlockMsg) { - cb.lk.Lock() - defer cb.lk.Unlock() - bcastDict, ok := cb.m[blk.Header.Height] - if !ok { - bcastDict = newBcastDict() - cb.m[blk.Header.Height] = bcastDict - } - - key := BCastKey(blk.Header) - blkCid := blk.Cid() - - bInfo, ok := bcastDict.load(key) - if ok { - if len(bInfo.blks) > 1 { - log.Errorf("equivocation detected for height %d: %s", blk.Header.Height, bInfo.eqErr()) - return - } - - if !cidExists(bInfo.blks, blkCid) { - bcastDict.store(key, &blksInfo{bInfo.ctx, bInfo.cancel, append(bInfo.blks, blkCid)}) - // By calling bInfo.eqErr() inside this log we cancel the context for all blocks waiting for - // the epoch-ticket combination making them to fail and not be sent to the syncer, as - // a potential equivocation is detected. - log.Errorf("equivocation detected for height %d: %s", blk.Header.Height, bInfo.eqErr()) - return - } - return - } - - ctx, cancel := context.WithTimeout(ctx, cb.delay) - bcastDict.store(key, &blksInfo{ctx, cancel, []cid.Cid{blkCid}}) -} - -// WaitForDelivery is called before informing the syncer about a new block -// to check if the consistent broadcast delay triggered or if the block should -// be held off for a bit more time. -func (cb *ConsistentBCast) WaitForDelivery(bh *types.BlockHeader) error { - cb.lk.RLock() - defer cb.lk.RUnlock() - - bcastDict, ok := cb.m[bh.Height] - if !ok { - return xerrors.Errorf("block at height %d garbage collected before it could be processed", bh.Height) - } - key := BCastKey(bh) - bInfo, ok := bcastDict.load(key) - if !ok { - return xerrors.Errorf("something went wrong, unknown block with Epoch + VRFProof (cid=%s) in consistent broadcast storage", key) - } - - // Wait for the timeout - cb.lk.RUnlock() - <-bInfo.ctx.Done() - cb.lk.RLock() - if bcastDict.blkLen(key) > 1 { - return xerrors.Errorf("equivocation detected for epoch %d. Two blocks being broadcast with same VRFProof", bh.Height) - } - return nil -} - -// GarbageCollect cleans the consistent broadcast cache periodically. -// -// A light garbage collection is triggered before every block delivery -// while a deeper one is triggered once every GcDeepCheck to ensure -// that nothing was left behind. -func (cb *ConsistentBCast) GarbageCollect(currEpoch abi.ChainEpoch) { - cb.lk.Lock() - defer cb.lk.Unlock() - - // perform a deeper sanity check every now and then - gcRange := GcSanityCheck - if cb.lastDeepGc+GcDeepInterval > currEpoch { - gcRange = GcDeepCheck - cb.lastDeepGc = currEpoch - } - - // keep currEpoch-gcRange and delete a few more in the past - // as a sanity-check - // Garbage collection is triggered before block delivery, - // and we use the sanity-check in case there were a few rounds - // without delivery, and the garbage collection wasn't triggered - // for a few epochs. - for i := 0; i < gcRange; i++ { - if currEpoch > GcLookback { - delete(cb.m, currEpoch-abi.ChainEpoch(GcLookback+i)) - } - } -} diff --git a/chain/sub/bcast/consistent_test.go b/chain/sub/bcast/consistent_test.go deleted file mode 100644 index 8beb0574f36..00000000000 --- a/chain/sub/bcast/consistent_test.go +++ /dev/null @@ -1,223 +0,0 @@ -package bcast_test - -import ( - "context" - "crypto/rand" - "fmt" - mrand "math/rand" - "strconv" - "sync" - "testing" - "time" - - "github.com/ipfs/go-cid" - "github.com/multiformats/go-multihash" - "github.com/stretchr/testify/require" - - "github.com/filecoin-project/go-address" - "github.com/filecoin-project/go-state-types/abi" - - "github.com/filecoin-project/lotus/chain/sub/bcast" - "github.com/filecoin-project/lotus/chain/types" -) - -const TEST_DELAY = 1 * time.Second - -func TestSimpleDelivery(t *testing.T) { - cb := bcast.NewConsistentBCast(TEST_DELAY) - // Check that we wait for delivery. - start := time.Now() - testSimpleDelivery(t, cb, 100, 5) - since := time.Since(start) - require.GreaterOrEqual(t, since, TEST_DELAY) -} - -func testSimpleDelivery(t *testing.T, cb *bcast.ConsistentBCast, epoch abi.ChainEpoch, numBlocks int) { - ctx := context.Background() - - wg := new(sync.WaitGroup) - errs := make([]error, 0) - wg.Add(numBlocks) - for i := 0; i < numBlocks; i++ { - go func(i int) { - defer wg.Done() - // Add a random delay in block reception - r := mrand.Intn(200) - time.Sleep(time.Duration(r) * time.Millisecond) - blk := newBlock(t, epoch, randomProof(t), []byte("test"+strconv.Itoa(i))) - cb.RcvBlock(ctx, blk) - err := cb.WaitForDelivery(blk.Header) - if err != nil { - errs = append(errs, err) - } - }(i) - } - wg.Wait() - - for _, v := range errs { - t.Fatalf("error in delivery: %s", v) - } -} - -func TestSeveralEpochs(t *testing.T) { - cb := bcast.NewConsistentBCast(TEST_DELAY) - numEpochs := 6 - wg := new(sync.WaitGroup) - wg.Add(numEpochs) - for i := 0; i < numEpochs; i++ { - go func(i int) { - defer wg.Done() - // Add a random delay between epochs - r := mrand.Intn(500) - time.Sleep(time.Duration(i)*TEST_DELAY + time.Duration(r)*time.Millisecond) - rNumBlocks := mrand.Intn(5) - flip, err := flipCoin(0.7) - require.NoError(t, err) - t.Logf("Running epoch %d with %d with equivocation=%v", i, rNumBlocks, !flip) - if flip { - testSimpleDelivery(t, cb, abi.ChainEpoch(i), rNumBlocks) - } else { - testEquivocation(t, cb, abi.ChainEpoch(i), rNumBlocks) - } - cb.GarbageCollect(abi.ChainEpoch(i)) - }(i) - } - wg.Wait() - require.Equal(t, cb.Len(), numEpochs) -} - -// bias is expected to be 0-1 -func flipCoin(bias float32) (bool, error) { - if bias > 1 || bias < 0 { - return false, fmt.Errorf("wrong bias. expected (0,1)") - } - r := mrand.Intn(100) - return r < int(bias*100), nil -} - -func testEquivocation(t *testing.T, cb *bcast.ConsistentBCast, epoch abi.ChainEpoch, numBlocks int) { - ctx := context.Background() - - wg := new(sync.WaitGroup) - errs := make([]error, 0) - wg.Add(numBlocks + 1) - for i := 0; i < numBlocks; i++ { - proof := randomProof(t) - // Valid blocks - go func(i int, proof []byte) { - defer wg.Done() - r := mrand.Intn(200) - time.Sleep(time.Duration(r) * time.Millisecond) - blk := newBlock(t, epoch, proof, []byte("valid"+strconv.Itoa(i))) - cb.RcvBlock(ctx, blk) - err := cb.WaitForDelivery(blk.Header) - if err != nil { - errs = append(errs, err) - } - }(i, proof) - - // Equivocation for the last block - if i == numBlocks-1 { - // Attempting equivocation - go func(i int, proof []byte) { - defer wg.Done() - // Use the same proof and the same epoch - blk := newBlock(t, epoch, proof, []byte("invalid"+strconv.Itoa(i))) - cb.RcvBlock(ctx, blk) - err := cb.WaitForDelivery(blk.Header) - // Equivocation detected - require.Error(t, err) - }(i, proof) - } - } - wg.Wait() - - // The equivocated block arrived too late, so - // we delivered all the valid blocks. - require.Len(t, errs, 1) -} - -func TestEquivocation(t *testing.T) { - cb := bcast.NewConsistentBCast(TEST_DELAY) - testEquivocation(t, cb, 100, 5) -} - -func TestFailedEquivocation(t *testing.T) { - cb := bcast.NewConsistentBCast(TEST_DELAY) - ctx := context.Background() - numBlocks := 5 - - wg := new(sync.WaitGroup) - errs := make([]error, 0) - wg.Add(numBlocks + 1) - for i := 0; i < numBlocks; i++ { - proof := randomProof(t) - // Valid blocks - go func(i int, proof []byte) { - defer wg.Done() - r := mrand.Intn(200) - time.Sleep(time.Duration(r) * time.Millisecond) - blk := newBlock(t, 100, proof, []byte("valid"+strconv.Itoa(i))) - cb.RcvBlock(ctx, blk) - err := cb.WaitForDelivery(blk.Header) - if err != nil { - errs = append(errs, err) - } - }(i, proof) - - // Equivocation for the last block - if i == numBlocks-1 { - // Attempting equivocation - go func(i int, proof []byte) { - defer wg.Done() - // The equivocated block arrives late - time.Sleep(2 * TEST_DELAY) - // Use the same proof and the same epoch - blk := newBlock(t, 100, proof, []byte("invalid"+strconv.Itoa(i))) - cb.RcvBlock(ctx, blk) - err := cb.WaitForDelivery(blk.Header) - // Equivocation detected - require.Error(t, err) - }(i, proof) - } - } - wg.Wait() - - // The equivocated block arrived too late, so - // we delivered all the valid blocks. - require.Len(t, errs, 0) -} - -func randomProof(t *testing.T) []byte { - proof := make([]byte, 10) - _, err := rand.Read(proof) - if err != nil { - t.Fatal(err) - } - return proof -} - -func newBlock(t *testing.T, epoch abi.ChainEpoch, proof []byte, mCidSeed []byte) *types.BlockMsg { - h, err := multihash.Sum(mCidSeed, multihash.SHA2_256, -1) - if err != nil { - t.Fatal(err) - } - testCid := cid.NewCidV0(h) - addr, err := address.NewIDAddress(10) - if err != nil { - t.Fatal(err) - } - bh := &types.BlockHeader{ - Miner: addr, - ParentStateRoot: testCid, - ParentMessageReceipts: testCid, - Ticket: &types.Ticket{ - VRFProof: proof, - }, - Height: epoch, - Messages: testCid, - } - return &types.BlockMsg{ - Header: bh, - } -} diff --git a/chain/sub/incoming.go b/chain/sub/incoming.go index f98724361bf..533314a4fd1 100644 --- a/chain/sub/incoming.go +++ b/chain/sub/incoming.go @@ -27,7 +27,6 @@ import ( "github.com/filecoin-project/lotus/chain/consensus" "github.com/filecoin-project/lotus/chain/messagepool" "github.com/filecoin-project/lotus/chain/store" - "github.com/filecoin-project/lotus/chain/sub/bcast" "github.com/filecoin-project/lotus/chain/sub/ratelimit" "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/metrics" @@ -44,11 +43,10 @@ var msgCidPrefix = cid.Prefix{ MhLength: 32, } -func HandleIncomingBlocks(ctx context.Context, bsub *pubsub.Subscription, self peer.ID, s *chain.Syncer, bs bserv.BlockService, cmgr connmgr.ConnManager) { +func HandleIncomingBlocks(ctx context.Context, bsub *pubsub.Subscription, s *chain.Syncer, bs bserv.BlockService, cmgr connmgr.ConnManager) { // Timeout after (block time + propagation delay). This is useless at // this point. timeout := time.Duration(build.BlockDelaySecs+build.PropagationDelaySecs) * time.Second - cb := bcast.NewConsistentBCast(build.CBDeliveryDelay) for { msg, err := bsub.Next(ctx) @@ -69,9 +67,6 @@ func HandleIncomingBlocks(ctx context.Context, bsub *pubsub.Subscription, self p src := msg.GetFrom() - // Notify consistent broadcast about a new block - cb.RcvBlock(ctx, blk) - go func() { ctx, cancel := context.WithTimeout(ctx, timeout) defer cancel() @@ -107,20 +102,6 @@ func HandleIncomingBlocks(ctx context.Context, bsub *pubsub.Subscription, self p log.Warnw("received block with large delay from miner", "block", blk.Cid(), "delay", delay, "miner", blk.Header.Miner) } - // When we propose a new block ourselves, the proposed block also gets here through SyncSubmitBlock. - // If we are the block proposers we don't need to wait for delivery, we know the blocks are - // honest. - if src != self { - log.Debugf("Waiting for consistent broadcast of block in height: %v", blk.Header.Height) - if err := cb.WaitForDelivery(blk.Header); err != nil { - log.Errorf("not informing syncer about new block, potential equivocation detected (cid: %s, source: %s): %s; ", blk.Header.Cid(), src, err) - return - } - } - // Garbage collect the broadcast state - cb.GarbageCollect(blk.Header.Height) - log.Debugf("Block in height %v delivered successfully (cid=%s)", blk.Header.Height, blk.Cid()) - if s.InformNewBlock(msg.ReceivedFrom, &types.FullBlock{ Header: blk.Header, BlsMessages: bmsgs, diff --git a/chain/sync_test.go b/chain/sync_test.go index 1f32d96ec9f..a86d42f17e6 100644 --- a/chain/sync_test.go +++ b/chain/sync_test.go @@ -45,11 +45,6 @@ func init() { policy.SetSupportedProofTypes(abi.RegisteredSealProof_StackedDrg2KiBV1) policy.SetConsensusMinerMinPower(abi.NewStoragePower(2048)) policy.SetMinVerifiedDealSize(abi.NewStoragePower(256)) - - // these tests assume really fast block times. disabling - // the consistent broadcast delay to avoid them from adding - // an unnecessary overhead. - build.CBDeliveryDelay = 2 * time.Millisecond } const source = 0 diff --git a/itests/kit/init.go b/itests/kit/init.go index dbcb49aae31..9397c53a218 100644 --- a/itests/kit/init.go +++ b/itests/kit/init.go @@ -40,11 +40,6 @@ func init() { build.InsecurePoStValidation = true - // Disabling consistent broadcast in itests. Many tests use really fast - // block times, and adding this additional delay for block delivery - // would make these tests to fail. - build.CBDeliveryDelay = 0 - if err := os.Setenv("BELLMAN_NO_GPU", "1"); err != nil { panic(fmt.Sprintf("failed to set BELLMAN_NO_GPU env variable: %s", err)) } diff --git a/node/modules/services.go b/node/modules/services.go index 750d22fbaff..9acebd07105 100644 --- a/node/modules/services.go +++ b/node/modules/services.go @@ -167,7 +167,7 @@ func HandleIncomingBlocks(mctx helpers.MetricsCtx, panic(err) } - go sub.HandleIncomingBlocks(ctx, blocksub, h.ID(), s, bserv, h.ConnManager()) + go sub.HandleIncomingBlocks(ctx, blocksub, s, bserv, h.ConnManager()) } func HandleIncomingMessages(mctx helpers.MetricsCtx, lc fx.Lifecycle, ps *pubsub.PubSub, stmgr *stmgr.StateManager, mpool *messagepool.MessagePool, h host.Host, nn dtypes.NetworkName, bootstrapper dtypes.Bootstrapper) { From 0d8a3cbaf848d15169f25bff0920d3c804b1b186 Mon Sep 17 00:00:00 2001 From: Aayush Date: Wed, 26 Apr 2023 10:07:52 -0400 Subject: [PATCH 202/243] feat: shed tool to report on any consensus mismatches in history --- cmd/lotus-shed/main.go | 1 + cmd/lotus-shed/mismatches.go | 53 ++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 cmd/lotus-shed/mismatches.go diff --git a/cmd/lotus-shed/main.go b/cmd/lotus-shed/main.go index 1f4d953f990..9cb1cd32ccb 100644 --- a/cmd/lotus-shed/main.go +++ b/cmd/lotus-shed/main.go @@ -86,6 +86,7 @@ func main() { replayOfflineCmd, msgindexCmd, FevmAnalyticsCmd, + mismatchesCmd, } app := &cli.App{ diff --git a/cmd/lotus-shed/mismatches.go b/cmd/lotus-shed/mismatches.go new file mode 100644 index 00000000000..8dd1be35233 --- /dev/null +++ b/cmd/lotus-shed/mismatches.go @@ -0,0 +1,53 @@ +package main + +import ( + "fmt" + + lcli "github.com/filecoin-project/lotus/cli" + "github.com/urfave/cli/v2" +) + +var mismatchesCmd = &cli.Command{ + Name: "mismatches", + Description: "Walk up the chain, recomputing state, and reporting any mismatches", + Action: func(cctx *cli.Context) error { + srv, err := lcli.GetFullNodeServices(cctx) + if err != nil { + return err + } + defer srv.Close() //nolint:errcheck + + api := srv.FullNodeAPI() + ctx := lcli.ReqContext(cctx) + + checkTs, err := api.ChainHead(ctx) + if err != nil { + return err + } + + for checkTs.Height() != 0 { + if checkTs.Height()%10000 == 0 { + fmt.Println("Reached height ", checkTs.Height()) + } + + execTsk := checkTs.Parents() + execTs, err := api.ChainGetTipSet(ctx, execTsk) + if err != nil { + return err + } + + st, err := api.StateCompute(ctx, execTs.Height(), nil, execTsk) + if err != nil { + return err + } + + if st.Root != checkTs.ParentState() { + fmt.Println("consensus mismatch found at height ", execTs.Height()) + } + + checkTs = execTs + } + + return nil + }, +} From b4c2c249db5022789405a0de4eb9dc72a0521a30 Mon Sep 17 00:00:00 2001 From: Aayush Date: Fri, 28 Apr 2023 13:48:05 -0400 Subject: [PATCH 203/243] chore: deps: update to FVM 3.3.1 --- cmd/lotus-shed/mismatches.go | 3 ++- extern/filecoin-ffi | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/cmd/lotus-shed/mismatches.go b/cmd/lotus-shed/mismatches.go index 8dd1be35233..223158e0fe0 100644 --- a/cmd/lotus-shed/mismatches.go +++ b/cmd/lotus-shed/mismatches.go @@ -3,8 +3,9 @@ package main import ( "fmt" - lcli "github.com/filecoin-project/lotus/cli" "github.com/urfave/cli/v2" + + lcli "github.com/filecoin-project/lotus/cli" ) var mismatchesCmd = &cli.Command{ diff --git a/extern/filecoin-ffi b/extern/filecoin-ffi index 86698251ab8..de34caff946 160000 --- a/extern/filecoin-ffi +++ b/extern/filecoin-ffi @@ -1 +1 @@ -Subproject commit 86698251ab87eec6944320a01998623155c01276 +Subproject commit de34caff946d598edb299566d951b44b9b7f7dd4 From ff0fa3471f1489bb32b06ccae53041539a4725e3 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Tue, 2 May 2023 14:22:02 -0400 Subject: [PATCH 204/243] chore: refactor: drop unused IsTicketWinner (#10801) --- chain/types/blockheader.go | 32 -------------------------------- chain/types/electionproof.go | 1 + 2 files changed, 1 insertion(+), 32 deletions(-) diff --git a/chain/types/blockheader.go b/chain/types/blockheader.go index e0b9e6b30bb..3117e312247 100644 --- a/chain/types/blockheader.go +++ b/chain/types/blockheader.go @@ -13,8 +13,6 @@ import ( "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/crypto" "github.com/filecoin-project/go-state-types/proof" - - "github.com/filecoin-project/lotus/build" ) type Ticket struct { @@ -195,36 +193,6 @@ func CidArrsContains(a []cid.Cid, b cid.Cid) bool { return false } -var blocksPerEpoch = NewInt(build.BlocksPerEpoch) - -const sha256bits = 256 - -func IsTicketWinner(vrfTicket []byte, mypow BigInt, totpow BigInt) bool { - /* - Need to check that - (h(vrfout) + 1) / (max(h) + 1) <= e * myPower / totalPower - max(h) == 2^256-1 - which in terms of integer math means: - (h(vrfout) + 1) * totalPower <= e * myPower * 2^256 - in 2^256 space, it is equivalent to: - h(vrfout) * totalPower < e * myPower * 2^256 - - */ - - h := blake2b.Sum256(vrfTicket) - - lhs := BigFromBytes(h[:]).Int - lhs = lhs.Mul(lhs, totpow.Int) - - // rhs = sectorSize * 2^256 - // rhs = sectorSize << 256 - rhs := new(big.Int).Lsh(mypow.Int, sha256bits) - rhs = rhs.Mul(rhs, blocksPerEpoch.Int) - - // h(vrfout) * totalPower < e * sectorSize * 2^256? - return lhs.Cmp(rhs) < 0 -} - func (t *Ticket) Equals(ot *Ticket) bool { return bytes.Equal(t.VRFProof, ot.VRFProof) } diff --git a/chain/types/electionproof.go b/chain/types/electionproof.go index 6f59c7713de..f3168becb8f 100644 --- a/chain/types/electionproof.go +++ b/chain/types/electionproof.go @@ -100,6 +100,7 @@ func polyval(p []*big.Int, x *big.Int) *big.Int { // computes lambda in Q.256 func lambda(power, totalPower *big.Int) *big.Int { + blocksPerEpoch := NewInt(build.BlocksPerEpoch) lam := new(big.Int).Mul(power, blocksPerEpoch.Int) // Q.0 lam = lam.Lsh(lam, precision) // Q.256 lam = lam.Div(lam /* Q.256 */, totalPower /* Q.0 */) // Q.256 From 3390e3536d0858b13e616bafa920531896cb282b Mon Sep 17 00:00:00 2001 From: Jiaying Wang <42981373+jennijuju@users.noreply.github.com> Date: Mon, 24 Apr 2023 15:29:32 +0800 Subject: [PATCH 205/243] Update CHANGELOG.md --- CHANGELOG.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 650becb25c2..500f4161995 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -606,10 +606,6 @@ verifiedregistry bafk2bzacedej3dnr62g2je2abmyjg3xqv4otvh6e26du5fcrhvw7zgcaaez3a ### Dependencies github.com/filecoin-project/go-state-types (v0.11.0-rc1 -> v0.11.1): -<<<<<<< HEAD -======= - ->>>>>>> releases # v1.20.4 / 2023-03-17 This is a patch release intended to alleviate performance issues reported by some users since the nv18 upgrade. From 49f58252961d08a7465278012abcd76f8a3edfba Mon Sep 17 00:00:00 2001 From: Aayush Date: Tue, 2 May 2023 10:08:18 -0400 Subject: [PATCH 206/243] feat: chainstore: batch writes of tipsets --- chain/store/snapshot.go | 10 ++++++---- chain/store/store.go | 25 ++++++++++++++++--------- chain/sync.go | 13 +++++-------- cmd/lotus-sim/simulation/block.go | 2 +- 4 files changed, 28 insertions(+), 22 deletions(-) diff --git a/chain/store/snapshot.go b/chain/store/snapshot.go index 307bd356c1c..92bc238a685 100644 --- a/chain/store/snapshot.go +++ b/chain/store/snapshot.go @@ -123,11 +123,9 @@ func (cs *ChainStore) Import(ctx context.Context, r io.Reader) (*types.TipSet, e } ts := root + tssToPersist := make([]*types.TipSet, 0, TipsetkeyBackfillRange) for i := 0; i < int(TipsetkeyBackfillRange); i++ { - err = cs.PersistTipset(ctx, ts) - if err != nil { - return nil, err - } + tssToPersist = append(tssToPersist, ts) parentTsKey := ts.Parents() ts, err = cs.LoadTipSet(ctx, parentTsKey) if ts == nil || err != nil { @@ -136,6 +134,10 @@ func (cs *ChainStore) Import(ctx context.Context, r io.Reader) (*types.TipSet, e } } + if err := cs.PersistTipsets(ctx, tssToPersist); err != nil { + return nil, xerrors.Errorf("failed to persist tipsets: %w", err) + } + return root, nil } diff --git a/chain/store/store.go b/chain/store/store.go index dc9dc6484c5..7343bdf9d16 100644 --- a/chain/store/store.go +++ b/chain/store/store.go @@ -378,7 +378,7 @@ func (cs *ChainStore) SetGenesis(ctx context.Context, b *types.BlockHeader) erro } func (cs *ChainStore) PutTipSet(ctx context.Context, ts *types.TipSet) error { - if err := cs.PersistTipset(ctx, ts); err != nil { + if err := cs.PersistTipsets(ctx, []*types.TipSet{ts}); err != nil { return xerrors.Errorf("failed to persist tipset: %w", err) } @@ -970,18 +970,25 @@ func (cs *ChainStore) AddToTipSetTracker(ctx context.Context, b *types.BlockHead return nil } -func (cs *ChainStore) PersistTipset(ctx context.Context, ts *types.TipSet) error { - if err := cs.persistBlockHeaders(ctx, ts.Blocks()...); err != nil { - return xerrors.Errorf("failed to persist block headers: %w", err) +func (cs *ChainStore) PersistTipsets(ctx context.Context, tipsets []*types.TipSet) error { + toPersist := make([]*types.BlockHeader, 0, len(tipsets)*int(build.BlocksPerEpoch)) + tsBlks := make([]block.Block, 0, len(tipsets)) + for _, ts := range tipsets { + toPersist = append(toPersist, ts.Blocks()...) + tsBlk, err := ts.Key().ToStorageBlock() + if err != nil { + return xerrors.Errorf("failed to get tipset key block: %w", err) + } + + tsBlks = append(tsBlks, tsBlk) } - tsBlk, err := ts.Key().ToStorageBlock() - if err != nil { - return xerrors.Errorf("failed to get tipset key block: %w", err) + if err := cs.persistBlockHeaders(ctx, toPersist...); err != nil { + return xerrors.Errorf("failed to persist block headers: %w", err) } - if err = cs.chainLocalBlockstore.Put(ctx, tsBlk); err != nil { - return xerrors.Errorf("failed to put tipset key block: %w", err) + if err := cs.chainLocalBlockstore.PutMany(ctx, tsBlks); err != nil { + return xerrors.Errorf("failed to put tipset key blocks: %w", err) } return nil diff --git a/chain/sync.go b/chain/sync.go index de603520644..7830a977112 100644 --- a/chain/sync.go +++ b/chain/sync.go @@ -228,7 +228,7 @@ func (syncer *Syncer) InformNewHead(from peer.ID, fts *store.FullTipSet) bool { // TODO: IMPORTANT(GARBAGE) this needs to be put in the 'temporary' side of // the blockstore - if err := syncer.store.PersistTipset(ctx, fts.TipSet()); err != nil { + if err := syncer.store.PersistTipsets(ctx, []*types.TipSet{fts.TipSet()}); err != nil { log.Warn("failed to persist incoming block header: ", err) return false } @@ -1199,13 +1199,10 @@ func (syncer *Syncer) collectChain(ctx context.Context, ts *types.TipSet, hts *t ss.SetStage(api.StagePersistHeaders) // Write tipsets from oldest to newest. - for i := len(headers) - 1; i >= 0; i-- { - ts := headers[i] - if err := syncer.store.PersistTipset(ctx, ts); err != nil { - err = xerrors.Errorf("failed to persist synced tipset to the chainstore: %w", err) - ss.Error(err) - return err - } + if err := syncer.store.PersistTipsets(ctx, headers); err != nil { + err = xerrors.Errorf("failed to persist synced tipset to the chainstore: %w", err) + ss.Error(err) + return err } ss.SetStage(api.StageMessages) diff --git a/cmd/lotus-sim/simulation/block.go b/cmd/lotus-sim/simulation/block.go index 4dddba921ac..7cf5a6be66b 100644 --- a/cmd/lotus-sim/simulation/block.go +++ b/cmd/lotus-sim/simulation/block.go @@ -80,7 +80,7 @@ func (sim *Simulation) makeTipSet(ctx context.Context, messages []*types.Message return nil, xerrors.Errorf("failed to create new tipset: %w", err) } - err = sim.Node.Chainstore.PersistTipset(ctx, newTipSet) + err = sim.Node.Chainstore.PersistTipsets(ctx, []*types.TipSet{newTipSet}) if err != nil { return nil, xerrors.Errorf("failed to persist block headers: %w", err) } From 3ff8a091831106a6db0410f1647697c6bb4ceac4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 3 May 2023 16:59:41 +0200 Subject: [PATCH 207/243] feat: worker: Ensure tempdir exists (#10433) --- cmd/lotus-worker/main.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/cmd/lotus-worker/main.go b/cmd/lotus-worker/main.go index e0a9312747c..070fe601fd1 100644 --- a/cmd/lotus-worker/main.go +++ b/cmd/lotus-worker/main.go @@ -295,6 +295,13 @@ var runCmd = &cli.Command{ } } + // ensure tmpdir exists + td := os.TempDir() + if err := os.MkdirAll(td, 0755); err != nil { + return xerrors.Errorf("ensuring temp dir %s exists: %w", td, err) + } + + // Check file descriptor limit limit, _, err := ulimit.GetLimit() switch { case err == ulimit.ErrUnsupported: From 17c43caacfc9b419badab9a6116f4116a1f5fd52 Mon Sep 17 00:00:00 2001 From: Aayush Date: Wed, 3 May 2023 11:51:42 -0400 Subject: [PATCH 208/243] chore: drop flaky TestBatchDealInput subcase --- itests/batch_deal_test.go | 7 ------- 1 file changed, 7 deletions(-) diff --git a/itests/batch_deal_test.go b/itests/batch_deal_test.go index 9df6155bad6..68b276a0c36 100644 --- a/itests/batch_deal_test.go +++ b/itests/batch_deal_test.go @@ -145,11 +145,4 @@ func TestBatchDealInput(t *testing.T) { t.Run("4-p1600B", run(1600, 4, 4)) t.Run("4-p513B", run(513, 4, 2)) - if !testing.Short() { - t.Run("32-p257B", run(257, 32, 8)) - - // fixme: this appears to break data-transfer / markets in some really creative ways - //t.Run("32-p10B", run(10, 32, 2)) - // t.Run("128-p10B", run(10, 128, 8)) - } } From bca0023756704e431a4913ffe5bf57e41d2a3190 Mon Sep 17 00:00:00 2001 From: zenground0 Date: Wed, 3 May 2023 09:55:22 -0600 Subject: [PATCH 209/243] Don't block when potentially holding txnLk as writer --- blockstore/splitstore/splitstore_compact.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockstore/splitstore/splitstore_compact.go b/blockstore/splitstore/splitstore_compact.go index ee5f9302a96..f96f9d37033 100644 --- a/blockstore/splitstore/splitstore_compact.go +++ b/blockstore/splitstore/splitstore_compact.go @@ -455,7 +455,7 @@ func (s *SplitStore) protectTxnRefs(markSet MarkSet) error { // transactionally protect a reference by walking the object and marking. // concurrent markings are short circuited by checking the markset. func (s *SplitStore) doTxnProtect(root cid.Cid, markSet MarkSet) (int64, error) { - if err := s.checkYield(); err != nil { + if err := s.checkClosing(); err != nil { return 0, err } From f094e61b4a1c417701bc72da36f576d73eb64a4f Mon Sep 17 00:00:00 2001 From: Phi Date: Wed, 3 May 2023 17:56:34 +0200 Subject: [PATCH 210/243] Remove args check Remove args check in `lotus chain set` --- cli/chain.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/cli/chain.go b/cli/chain.go index 4344b077398..f3305a6674f 100644 --- a/cli/chain.go +++ b/cli/chain.go @@ -388,10 +388,6 @@ var ChainSetHeadCmd = &cli.Command{ defer closer() ctx := ReqContext(cctx) - if cctx.NArg() != 1 { - return IncorrectNumArgs(cctx) - } - var ts *types.TipSet if cctx.Bool("genesis") { From 78800a4e773442e4fce77e8235ed47fd758beb29 Mon Sep 17 00:00:00 2001 From: Shrenuj Bansal <108157875+shrenujbansal@users.noreply.github.com> Date: Wed, 3 May 2023 12:28:03 -0400 Subject: [PATCH 211/243] fix: sealing: Make lotus-worker report GPU usage to miner during ReplicaUpdate task (#10806) * Make lotus-worker report GPU usage to miner during ReplicaUpdate task * make gen --------- Co-authored-by: Jacob Crowther --- build/openrpc/miner.json.gz | Bin 15923 -> 15944 bytes build/openrpc/worker.json.gz | Bin 5224 -> 5245 bytes documentation/en/api-v0-methods-miner.md | 56 +++++++++++----------- documentation/en/api-v0-methods-worker.md | 56 +++++++++++----------- storage/sealer/storiface/resources.go | 11 ++++- 5 files changed, 65 insertions(+), 58 deletions(-) diff --git a/build/openrpc/miner.json.gz b/build/openrpc/miner.json.gz index 6cd2c7e85a59e84d5e10fcd824630462706b470e..d253366ce093ce6bea888fbabb523274d0f51db3 100644 GIT binary patch delta 325 zcmV-L0lNOPe8_yT4LyHW4o|UD=!7?vbVGx<0%hb{yO0{B&}C-Jf<7Me5j8V zWw>ipXAXK%DbE}ZB`lg8+;#C7L;YL!!=O}bXH~(|1!nUQO1!Q!Bcfca%6xqATZYxf zGl-_EgC~mKDqSQMRK2aySoM~uSfO3TtEyrZbBl?u8@f#KoZ@mMSUiUiQw*zkl~)}k z(n6*FH$v(teO5hZdQ_^xH+zy4Vl~K4$Uv3$aq(A5UmaBY&l{gF&(#^dyNfJ$wPyV2 Xz<7N8^7#J(009607^JnJ9~J`u5M-dN delta 304 zcmV-00nh%(e6xJ84LyH8NO-25!jBjQv_mO0`Y1ssjy+MR&kj+2LJh<=30eziEugi4 z)&g1!{9syOLp=WubuIpNG?9)b(!yUy6TRqYqQ^(Svan|yk&SKljCTai?WioUU)%H} z_Hpok!h7-agR_y9O#40KSo|&i7gTRq%9y**t_2uj|Z+C>N_TA0PadVYTrLqUq}3iK4ek7fA(GZ)-GGy(KDE zXjk#7s#wL`V&dzDE>k?GxEu)<&mqJV!zy0oRR@W*P^tfokUC1A)tMfZYVggTB!yTF zvJ)~;rF~rdmC`6z2i5-b#^=j(bw=;*BFkN^89zEO9v{Cv{=WbK0RR6D@lQM-76Sl@ CdXZZI diff --git a/build/openrpc/worker.json.gz b/build/openrpc/worker.json.gz index 2468be53acb51956481c83a0f1aa59459f6d923e..f18be8337a5bbb775079f4bc768368a9099acb03 100644 GIT binary patch delta 5236 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 ufXs=B#+1^$#D_!TvIxg)UMX; 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 diff --git a/documentation/en/api-v0-methods-miner.md b/documentation/en/api-v0-methods-miner.md index 420e7ccd86c..4761a3eed7b 100644 --- a/documentation/en/api-v0-methods-miner.md +++ b/documentation/en/api-v0-methods-miner.md @@ -5208,7 +5208,7 @@ Response: "0": { "MinMemory": 2048, "MaxMemory": 2048, - "GPUUtilization": 0, + "GPUUtilization": 1, "MaxParallelism": 1, "MaxParallelismGPU": 0, "BaseMinMemory": 2048, @@ -5217,7 +5217,7 @@ Response: "1": { "MinMemory": 8388608, "MaxMemory": 8388608, - "GPUUtilization": 0, + "GPUUtilization": 1, "MaxParallelism": 1, "MaxParallelismGPU": 0, "BaseMinMemory": 8388608, @@ -5226,7 +5226,7 @@ Response: "2": { "MinMemory": 1073741824, "MaxMemory": 1073741824, - "GPUUtilization": 0, + "GPUUtilization": 1, "MaxParallelism": 1, "MaxParallelismGPU": 0, "BaseMinMemory": 1073741824, @@ -5235,25 +5235,25 @@ Response: "3": { "MinMemory": 4294967296, "MaxMemory": 4294967296, - "GPUUtilization": 0, + "GPUUtilization": 1, "MaxParallelism": 1, - "MaxParallelismGPU": 0, + "MaxParallelismGPU": 6, "BaseMinMemory": 1073741824, "MaxConcurrent": 0 }, "4": { "MinMemory": 8589934592, "MaxMemory": 8589934592, - "GPUUtilization": 0, + "GPUUtilization": 1, "MaxParallelism": 1, - "MaxParallelismGPU": 0, + "MaxParallelismGPU": 6, "BaseMinMemory": 1073741824, "MaxConcurrent": 0 }, "5": { "MinMemory": 2048, "MaxMemory": 2048, - "GPUUtilization": 0, + "GPUUtilization": 1, "MaxParallelism": 1, "MaxParallelismGPU": 0, "BaseMinMemory": 2048, @@ -5262,7 +5262,7 @@ Response: "6": { "MinMemory": 8388608, "MaxMemory": 8388608, - "GPUUtilization": 0, + "GPUUtilization": 1, "MaxParallelism": 1, "MaxParallelismGPU": 0, "BaseMinMemory": 8388608, @@ -5271,7 +5271,7 @@ Response: "7": { "MinMemory": 1073741824, "MaxMemory": 1073741824, - "GPUUtilization": 0, + "GPUUtilization": 1, "MaxParallelism": 1, "MaxParallelismGPU": 0, "BaseMinMemory": 1073741824, @@ -5280,18 +5280,18 @@ Response: "8": { "MinMemory": 4294967296, "MaxMemory": 4294967296, - "GPUUtilization": 0, + "GPUUtilization": 1, "MaxParallelism": 1, - "MaxParallelismGPU": 0, + "MaxParallelismGPU": 6, "BaseMinMemory": 1073741824, "MaxConcurrent": 0 }, "9": { "MinMemory": 8589934592, "MaxMemory": 8589934592, - "GPUUtilization": 0, + "GPUUtilization": 1, "MaxParallelism": 1, - "MaxParallelismGPU": 0, + "MaxParallelismGPU": 6, "BaseMinMemory": 1073741824, "MaxConcurrent": 0 } @@ -5300,7 +5300,7 @@ Response: "0": { "MinMemory": 2048, "MaxMemory": 2048, - "GPUUtilization": 0, + "GPUUtilization": 1, "MaxParallelism": 1, "MaxParallelismGPU": 0, "BaseMinMemory": 2048, @@ -5309,7 +5309,7 @@ Response: "1": { "MinMemory": 8388608, "MaxMemory": 8388608, - "GPUUtilization": 0, + "GPUUtilization": 1, "MaxParallelism": 1, "MaxParallelismGPU": 0, "BaseMinMemory": 8388608, @@ -5318,7 +5318,7 @@ Response: "2": { "MinMemory": 1073741824, "MaxMemory": 1073741824, - "GPUUtilization": 0, + "GPUUtilization": 1, "MaxParallelism": 1, "MaxParallelismGPU": 0, "BaseMinMemory": 1073741824, @@ -5327,25 +5327,25 @@ Response: "3": { "MinMemory": 4294967296, "MaxMemory": 4294967296, - "GPUUtilization": 0, + "GPUUtilization": 1, "MaxParallelism": 1, - "MaxParallelismGPU": 0, + "MaxParallelismGPU": 6, "BaseMinMemory": 1073741824, "MaxConcurrent": 0 }, "4": { "MinMemory": 8589934592, "MaxMemory": 8589934592, - "GPUUtilization": 0, + "GPUUtilization": 1, "MaxParallelism": 1, - "MaxParallelismGPU": 0, + "MaxParallelismGPU": 6, "BaseMinMemory": 1073741824, "MaxConcurrent": 0 }, "5": { "MinMemory": 2048, "MaxMemory": 2048, - "GPUUtilization": 0, + "GPUUtilization": 1, "MaxParallelism": 1, "MaxParallelismGPU": 0, "BaseMinMemory": 2048, @@ -5354,7 +5354,7 @@ Response: "6": { "MinMemory": 8388608, "MaxMemory": 8388608, - "GPUUtilization": 0, + "GPUUtilization": 1, "MaxParallelism": 1, "MaxParallelismGPU": 0, "BaseMinMemory": 8388608, @@ -5363,7 +5363,7 @@ Response: "7": { "MinMemory": 1073741824, "MaxMemory": 1073741824, - "GPUUtilization": 0, + "GPUUtilization": 1, "MaxParallelism": 1, "MaxParallelismGPU": 0, "BaseMinMemory": 1073741824, @@ -5372,18 +5372,18 @@ Response: "8": { "MinMemory": 4294967296, "MaxMemory": 4294967296, - "GPUUtilization": 0, + "GPUUtilization": 1, "MaxParallelism": 1, - "MaxParallelismGPU": 0, + "MaxParallelismGPU": 6, "BaseMinMemory": 1073741824, "MaxConcurrent": 0 }, "9": { "MinMemory": 8589934592, "MaxMemory": 8589934592, - "GPUUtilization": 0, + "GPUUtilization": 1, "MaxParallelism": 1, - "MaxParallelismGPU": 0, + "MaxParallelismGPU": 6, "BaseMinMemory": 1073741824, "MaxConcurrent": 0 } diff --git a/documentation/en/api-v0-methods-worker.md b/documentation/en/api-v0-methods-worker.md index dab251a7c7e..d7d0f092e06 100644 --- a/documentation/en/api-v0-methods-worker.md +++ b/documentation/en/api-v0-methods-worker.md @@ -1135,7 +1135,7 @@ Response: "0": { "MinMemory": 2048, "MaxMemory": 2048, - "GPUUtilization": 0, + "GPUUtilization": 1, "MaxParallelism": 1, "MaxParallelismGPU": 0, "BaseMinMemory": 2048, @@ -1144,7 +1144,7 @@ Response: "1": { "MinMemory": 8388608, "MaxMemory": 8388608, - "GPUUtilization": 0, + "GPUUtilization": 1, "MaxParallelism": 1, "MaxParallelismGPU": 0, "BaseMinMemory": 8388608, @@ -1153,7 +1153,7 @@ Response: "2": { "MinMemory": 1073741824, "MaxMemory": 1073741824, - "GPUUtilization": 0, + "GPUUtilization": 1, "MaxParallelism": 1, "MaxParallelismGPU": 0, "BaseMinMemory": 1073741824, @@ -1162,25 +1162,25 @@ Response: "3": { "MinMemory": 4294967296, "MaxMemory": 4294967296, - "GPUUtilization": 0, + "GPUUtilization": 1, "MaxParallelism": 1, - "MaxParallelismGPU": 0, + "MaxParallelismGPU": 6, "BaseMinMemory": 1073741824, "MaxConcurrent": 0 }, "4": { "MinMemory": 8589934592, "MaxMemory": 8589934592, - "GPUUtilization": 0, + "GPUUtilization": 1, "MaxParallelism": 1, - "MaxParallelismGPU": 0, + "MaxParallelismGPU": 6, "BaseMinMemory": 1073741824, "MaxConcurrent": 0 }, "5": { "MinMemory": 2048, "MaxMemory": 2048, - "GPUUtilization": 0, + "GPUUtilization": 1, "MaxParallelism": 1, "MaxParallelismGPU": 0, "BaseMinMemory": 2048, @@ -1189,7 +1189,7 @@ Response: "6": { "MinMemory": 8388608, "MaxMemory": 8388608, - "GPUUtilization": 0, + "GPUUtilization": 1, "MaxParallelism": 1, "MaxParallelismGPU": 0, "BaseMinMemory": 8388608, @@ -1198,7 +1198,7 @@ Response: "7": { "MinMemory": 1073741824, "MaxMemory": 1073741824, - "GPUUtilization": 0, + "GPUUtilization": 1, "MaxParallelism": 1, "MaxParallelismGPU": 0, "BaseMinMemory": 1073741824, @@ -1207,18 +1207,18 @@ Response: "8": { "MinMemory": 4294967296, "MaxMemory": 4294967296, - "GPUUtilization": 0, + "GPUUtilization": 1, "MaxParallelism": 1, - "MaxParallelismGPU": 0, + "MaxParallelismGPU": 6, "BaseMinMemory": 1073741824, "MaxConcurrent": 0 }, "9": { "MinMemory": 8589934592, "MaxMemory": 8589934592, - "GPUUtilization": 0, + "GPUUtilization": 1, "MaxParallelism": 1, - "MaxParallelismGPU": 0, + "MaxParallelismGPU": 6, "BaseMinMemory": 1073741824, "MaxConcurrent": 0 } @@ -1227,7 +1227,7 @@ Response: "0": { "MinMemory": 2048, "MaxMemory": 2048, - "GPUUtilization": 0, + "GPUUtilization": 1, "MaxParallelism": 1, "MaxParallelismGPU": 0, "BaseMinMemory": 2048, @@ -1236,7 +1236,7 @@ Response: "1": { "MinMemory": 8388608, "MaxMemory": 8388608, - "GPUUtilization": 0, + "GPUUtilization": 1, "MaxParallelism": 1, "MaxParallelismGPU": 0, "BaseMinMemory": 8388608, @@ -1245,7 +1245,7 @@ Response: "2": { "MinMemory": 1073741824, "MaxMemory": 1073741824, - "GPUUtilization": 0, + "GPUUtilization": 1, "MaxParallelism": 1, "MaxParallelismGPU": 0, "BaseMinMemory": 1073741824, @@ -1254,25 +1254,25 @@ Response: "3": { "MinMemory": 4294967296, "MaxMemory": 4294967296, - "GPUUtilization": 0, + "GPUUtilization": 1, "MaxParallelism": 1, - "MaxParallelismGPU": 0, + "MaxParallelismGPU": 6, "BaseMinMemory": 1073741824, "MaxConcurrent": 0 }, "4": { "MinMemory": 8589934592, "MaxMemory": 8589934592, - "GPUUtilization": 0, + "GPUUtilization": 1, "MaxParallelism": 1, - "MaxParallelismGPU": 0, + "MaxParallelismGPU": 6, "BaseMinMemory": 1073741824, "MaxConcurrent": 0 }, "5": { "MinMemory": 2048, "MaxMemory": 2048, - "GPUUtilization": 0, + "GPUUtilization": 1, "MaxParallelism": 1, "MaxParallelismGPU": 0, "BaseMinMemory": 2048, @@ -1281,7 +1281,7 @@ Response: "6": { "MinMemory": 8388608, "MaxMemory": 8388608, - "GPUUtilization": 0, + "GPUUtilization": 1, "MaxParallelism": 1, "MaxParallelismGPU": 0, "BaseMinMemory": 8388608, @@ -1290,7 +1290,7 @@ Response: "7": { "MinMemory": 1073741824, "MaxMemory": 1073741824, - "GPUUtilization": 0, + "GPUUtilization": 1, "MaxParallelism": 1, "MaxParallelismGPU": 0, "BaseMinMemory": 1073741824, @@ -1299,18 +1299,18 @@ Response: "8": { "MinMemory": 4294967296, "MaxMemory": 4294967296, - "GPUUtilization": 0, + "GPUUtilization": 1, "MaxParallelism": 1, - "MaxParallelismGPU": 0, + "MaxParallelismGPU": 6, "BaseMinMemory": 1073741824, "MaxConcurrent": 0 }, "9": { "MinMemory": 8589934592, "MaxMemory": 8589934592, - "GPUUtilization": 0, + "GPUUtilization": 1, "MaxParallelism": 1, - "MaxParallelismGPU": 0, + "MaxParallelismGPU": 6, "BaseMinMemory": 1073741824, "MaxConcurrent": 0 } diff --git a/storage/sealer/storiface/resources.go b/storage/sealer/storiface/resources.go index be5c34d0f0d..0fd80d79ab0 100644 --- a/storage/sealer/storiface/resources.go +++ b/storage/sealer/storiface/resources.go @@ -342,7 +342,9 @@ var ResourceTable = map[sealtasks.TaskType]map[abi.RegisteredSealProof]Resources MaxMemory: 8 << 30, MinMemory: 8 << 30, - MaxParallelism: 1, + MaxParallelism: 1, + MaxParallelismGPU: 6, + GPUUtilization: 1.0, BaseMinMemory: 1 << 30, }, @@ -350,7 +352,9 @@ var ResourceTable = map[sealtasks.TaskType]map[abi.RegisteredSealProof]Resources MaxMemory: 4 << 30, MinMemory: 4 << 30, - MaxParallelism: 1, + MaxParallelism: 1, + MaxParallelismGPU: 6, + GPUUtilization: 1.0, BaseMinMemory: 1 << 30, }, @@ -359,6 +363,7 @@ var ResourceTable = map[sealtasks.TaskType]map[abi.RegisteredSealProof]Resources MinMemory: 1 << 30, MaxParallelism: 1, + GPUUtilization: 1.0, BaseMinMemory: 1 << 30, }, @@ -367,6 +372,7 @@ var ResourceTable = map[sealtasks.TaskType]map[abi.RegisteredSealProof]Resources MinMemory: 2 << 10, MaxParallelism: 1, + GPUUtilization: 1.0, BaseMinMemory: 2 << 10, }, @@ -375,6 +381,7 @@ var ResourceTable = map[sealtasks.TaskType]map[abi.RegisteredSealProof]Resources MinMemory: 8 << 20, MaxParallelism: 1, + GPUUtilization: 1.0, BaseMinMemory: 8 << 20, }, From 034888c58e1228c71114d65cda558f4864e86a8d Mon Sep 17 00:00:00 2001 From: Phi Date: Wed, 3 May 2023 19:03:54 +0200 Subject: [PATCH 212/243] Changing to if args.present Changing to if args.present --- cli/chain.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cli/chain.go b/cli/chain.go index f3305a6674f..a6ae521f581 100644 --- a/cli/chain.go +++ b/cli/chain.go @@ -388,6 +388,10 @@ var ChainSetHeadCmd = &cli.Command{ defer closer() ctx := ReqContext(cctx) + if cctx.Args().Present() { + return IncorrectNumArgs(cctx) + } + var ts *types.TipSet if cctx.Bool("genesis") { From 7b729689688d19e51e230ca39a3aed7fb0c0816e Mon Sep 17 00:00:00 2001 From: Phi Date: Wed, 3 May 2023 19:37:08 +0200 Subject: [PATCH 213/243] Update args Update args --- cli/chain.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/chain.go b/cli/chain.go index a6ae521f581..c0d54fd6382 100644 --- a/cli/chain.go +++ b/cli/chain.go @@ -388,7 +388,7 @@ var ChainSetHeadCmd = &cli.Command{ defer closer() ctx := ReqContext(cctx) - if cctx.Args().Present() { + if !cctx.Bool("genesis") && !cctx.IsSet("epoch") && cctx.NArg() != 1 { return IncorrectNumArgs(cctx) } From 742062f84c8e549789cdfb707927e06452f13cd8 Mon Sep 17 00:00:00 2001 From: Mikers Date: Wed, 3 May 2023 10:31:39 -1000 Subject: [PATCH 214/243] perf: mempool: lower priority optimizations (#10693) * release the read lock earlier as it is not needed for chaincomputebasefee * chain/messagepool/selection.go change to read lock in SelectMessages * tighten up locks in chain/messagepool/repub.go and two questions on whether curTsLks are needed as comments * include suggestion from @Jorropo to preallocate our msgs array so that we only need to make a single allocation * mp.pending should not be accessed directly but through the getter * from @arajasek: just check whether the sender is a robust address (anything except an ID address is robust) here, and return if so. That will: be faster reduce the size of this cache by half, because we can drop mp.keyCache.Add(ka, ka) on line 491. * do not need curTslk and clean up code comments --- chain/messagepool/check.go | 19 ++++++++++++++++--- chain/messagepool/messagepool.go | 11 +++++++++-- chain/messagepool/repub.go | 13 +++++++------ chain/messagepool/selection.go | 9 ++++----- 4 files changed, 36 insertions(+), 16 deletions(-) diff --git a/chain/messagepool/check.go b/chain/messagepool/check.go index a1097e7d1f2..07a278f6dbb 100644 --- a/chain/messagepool/check.go +++ b/chain/messagepool/check.go @@ -33,8 +33,13 @@ func (mp *MessagePool) CheckMessages(ctx context.Context, protos []*api.MessageP func (mp *MessagePool) CheckPendingMessages(ctx context.Context, from address.Address) ([][]api.MessageCheckStatus, error) { var msgs []*types.Message mp.lk.RLock() - mset, ok := mp.pending[from] + mset, ok, err := mp.getPendingMset(ctx, from) + if err != nil { + log.Warnf("errored while getting pending mset: %w", err) + return nil, err + } if ok { + msgs = make([]*types.Message, 0, len(mset.msgs)) for _, sm := range mset.msgs { msgs = append(msgs, &sm.Message) } @@ -64,7 +69,11 @@ func (mp *MessagePool) CheckReplaceMessages(ctx context.Context, replace []*type if !ok { mmap = make(map[uint64]*types.Message) msgMap[m.From] = mmap - mset, ok := mp.pending[m.From] + mset, ok, err := mp.getPendingMset(ctx, m.From) + if err != nil { + log.Warnf("errored while getting pending mset: %w", err) + return nil, err + } if ok { count += len(mset.msgs) for _, sm := range mset.msgs { @@ -144,7 +153,11 @@ func (mp *MessagePool) checkMessages(ctx context.Context, msgs []*types.Message, st, ok := state[m.From] if !ok { mp.lk.RLock() - mset, ok := mp.pending[m.From] + mset, ok, err := mp.getPendingMset(ctx, m.From) + if err != nil { + log.Warnf("errored while getting pending mset: %w", err) + return nil, err + } if ok && !interned { st = &actorState{nextNonce: mset.nextNonce, requiredFunds: mset.requiredFunds} for _, m := range mset.msgs { diff --git a/chain/messagepool/messagepool.go b/chain/messagepool/messagepool.go index 6c3e776c09f..4dcb6eb9b75 100644 --- a/chain/messagepool/messagepool.go +++ b/chain/messagepool/messagepool.go @@ -474,6 +474,15 @@ func (mp *MessagePool) TryForEachPendingMessage(f func(cid.Cid) error) error { } func (mp *MessagePool) resolveToKey(ctx context.Context, addr address.Address) (address.Address, error) { + //if addr is not an ID addr, then it is already resolved to a key + if addr.Protocol() != address.ID { + return addr, nil + } + return mp.resolveToKeyFromID(ctx, addr) +} + +func (mp *MessagePool) resolveToKeyFromID(ctx context.Context, addr address.Address) (address.Address, error) { + // check the cache a, ok := mp.keyCache.Get(addr) if ok { @@ -488,8 +497,6 @@ func (mp *MessagePool) resolveToKey(ctx context.Context, addr address.Address) ( // place both entries in the cache (may both be key addresses, which is fine) mp.keyCache.Add(addr, ka) - mp.keyCache.Add(ka, ka) - return ka, nil } diff --git a/chain/messagepool/repub.go b/chain/messagepool/repub.go index 70467643914..a87d5e08a84 100644 --- a/chain/messagepool/repub.go +++ b/chain/messagepool/repub.go @@ -22,18 +22,21 @@ var RepublishBatchDelay = 100 * time.Millisecond func (mp *MessagePool) republishPendingMessages(ctx context.Context) error { mp.curTsLk.RLock() ts := mp.curTs + mp.curTsLk.RUnlock() baseFee, err := mp.api.ChainComputeBaseFee(context.TODO(), ts) - mp.curTsLk.RUnlock() if err != nil { return xerrors.Errorf("computing basefee: %w", err) } baseFeeLowerBound := getBaseFeeLowerBound(baseFee, baseFeeLowerBoundFactor) pending := make(map[address.Address]map[uint64]*types.SignedMessage) - mp.curTsLk.Lock() + mp.lk.Lock() mp.republished = nil // clear this to avoid races triggering an early republish + mp.lk.Unlock() + + mp.lk.RLock() mp.forEachLocal(ctx, func(ctx context.Context, actor address.Address) { mset, ok, err := mp.getPendingMset(ctx, actor) if err != nil { @@ -54,9 +57,7 @@ func (mp *MessagePool) republishPendingMessages(ctx context.Context) error { } pending[actor] = pend }) - - mp.lk.Unlock() - mp.curTsLk.Unlock() + mp.lk.RUnlock() if len(pending) == 0 { return nil @@ -177,8 +178,8 @@ loop: republished[m.Cid()] = struct{}{} } - mp.lk.Lock() // update the republished set so that we can trigger early republish from head changes + mp.lk.Lock() mp.republished = republished mp.lk.Unlock() diff --git a/chain/messagepool/selection.go b/chain/messagepool/selection.go index e42cc781275..163bd76f985 100644 --- a/chain/messagepool/selection.go +++ b/chain/messagepool/selection.go @@ -40,12 +40,11 @@ type msgChain struct { } func (mp *MessagePool) SelectMessages(ctx context.Context, ts *types.TipSet, tq float64) ([]*types.SignedMessage, error) { - mp.curTsLk.Lock() - defer mp.curTsLk.Unlock() + mp.curTsLk.RLock() + defer mp.curTsLk.RUnlock() - //TODO confirm if we can switch to RLock here for performance - mp.lk.Lock() - defer mp.lk.Unlock() + mp.lk.RLock() + defer mp.lk.RUnlock() // See if we need to prune before selection; excessive buildup can lead to slow selection, // so prune if we have too many messages (ignoring the cooldown). From c7bdf61fb1a431e63034a0984cf4975f69c6312f Mon Sep 17 00:00:00 2001 From: Shrenuj Bansal <108157875+shrenujbansal@users.noreply.github.com> Date: Wed, 3 May 2023 16:42:23 -0400 Subject: [PATCH 215/243] Disable lotus markets by default (#10809) --- documentation/en/default-lotus-miner-config.toml | 2 +- node/config/def.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/documentation/en/default-lotus-miner-config.toml b/documentation/en/default-lotus-miner-config.toml index 3b46ad7d1c0..0c8ef441183 100644 --- a/documentation/en/default-lotus-miner-config.toml +++ b/documentation/en/default-lotus-miner-config.toml @@ -143,7 +143,7 @@ # type: bool # env var: LOTUS_SUBSYSTEMS_ENABLEMARKETS - #EnableMarkets = true + #EnableMarkets = false # type: string # env var: LOTUS_SUBSYSTEMS_SEALERAPIINFO diff --git a/node/config/def.go b/node/config/def.go index 54d8963f14b..70328828808 100644 --- a/node/config/def.go +++ b/node/config/def.go @@ -230,7 +230,7 @@ func DefaultStorageMiner() *StorageMiner { EnableMining: true, EnableSealing: true, EnableSectorStorage: true, - EnableMarkets: true, + EnableMarkets: false, }, Fees: MinerFeeConfig{ From fadc94f2538258233269cdf9aebbd75676fcee58 Mon Sep 17 00:00:00 2001 From: Shrenuj Bansal Date: Wed, 3 May 2023 17:20:57 -0400 Subject: [PATCH 216/243] Update build version for release/v1.23.1 --- build/openrpc/full.json.gz | Bin 33882 -> 33878 bytes build/openrpc/gateway.json.gz | Bin 9539 -> 9535 bytes build/openrpc/miner.json.gz | Bin 15944 -> 15941 bytes build/openrpc/worker.json.gz | Bin 5245 -> 5242 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 c5dde5e213b798f5cecb634f3faa9654f5e99421..cb20e40cdc0013936788641a16f75cefa4d8de98 100644 GIT binary patch literal 33878 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 literal 33882 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 diff --git a/build/openrpc/gateway.json.gz b/build/openrpc/gateway.json.gz index 7accc2e6727e972e67645c15a060f16fc974d179..938060588cfaa440fb62dfbe68f4afd35cc344f4 100644 GIT binary patch delta 9447 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 P=VunL*w{G*6vY1lLDrTf diff --git a/build/openrpc/miner.json.gz b/build/openrpc/miner.json.gz index d253366ce093ce6bea888fbabb523274d0f51db3..dfbcc7890a79f59b858ab5aa015caad12ea5e246 100644 GIT binary patch delta 15940 zcmV-KKD)ule8qf_gnz(z>=|psR@Cy0qtD+A0@wVr_t-`z#bp1D$*B9rvRveOM$fR2 zXS˺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 delta 15943 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 diff --git a/build/openrpc/worker.json.gz b/build/openrpc/worker.json.gz index f18be8337a5bbb775079f4bc768368a9099acb03..182c07d52edea771cb27000b3f6c6c396e54c0ef 100644 GIT binary patch delta 5158 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ℑwf%dkA;KK(%upK73p&p`3kAPnYddL*_UIxE<-fs@Q zT)M8~QDm_^YxYl+EvNiJ=&Daks%Ud zr_&@9M3!hEz~=XQC*sp-*V`w~r;3H6qU4TtL? z6H@jhXxv)fjhB`guL9MtG-lVTq8YchvVH^nV+rzJM9DqzuFVj&lCl{3q4&6p=bVR#M(4J;Vb3I139@2eP!lB=##x9BTPm zjDkhWG^f<{PmYggL6V;7 zERLrUYdCT}hskwpGX`=zv7O2B0{Rqr$I!*cu7BtJSpDA&k>1StudjoV)@N)`oH&jh%o`Wby-p`$f-<&$n||@tF(DI>*ZpV2 zZGR03@;W9yu@sZaj@Wt^CY8(xDh&`TJqETcAA+Qb`CcvatEf~Od&2VA+o8U?_!{$Y zQR=|O`3TyW%zWhCV-p?wL52RYe>7E8O+QjK-RMiQ^iKpW7olMGrseL z67_!J)VgiCF>K_>{x7r&v?x>>pL&?gn17D_3GF4yd6TD)X{3yLZku;A>g%W*D->E+ zYXun+Wazb#A!C?NhJGb*Vk6#pY?Ld9_Phu0LUL2gN21L6BqZ!H4$iR?Wp8qkXZI0E z9R#^Ls!IJ)zAF6#QFG3zNI@Q6_FRP60i)i%Jco21J&td~d^CfO|K?bTlSl``wtvN) zH$5TtVFYEausW2~{Pxzl8d&7Y((bH{R?<2tV#!iWsEGYFx@i*g5Y^;bIQOWwP=aj= zw)xuF=3rbv5io%^{)sM-Yhx2$xfZ0n;TySSa8L+d(9f%`H4;$JHr5tF1Uh1@&qM?S zE(lzBZMZN}%5fnYi+X?%W^Nw|!hig2L%_B*!uNN`AcgN2zF+wMTE2g*=KKCHh#(Kr z$l?79t^VOTiyFJ~h2`)rFPhluonng<9SW;&3nEC`>cKWEL76p#WNzhkPGd0B=j{fqyf~-1^k< z7RUmV6@cK+@cR!LKyd0<$OP{m=GdGAOf1Zr$m&=uZHi6y_Q-|i9kRByu+Dl9Ah7@h z-&m?S^yj_gQs$#{%=8>iSK8z!G!fJvd}}^f=};fgiSFN_h5NVj<3H|l9;#<$n{1E)YFH^s%CqBl=&MSkA+_b4hDL`L2Mni0KP7!%RkG zv6O+I4RD;j`^dC`0k^Az>sUn3x#QDNRZi#7wh@_y+k}{~a{EDER@wz5ma`zp_Y-tr zC)h<`)K!SVE>9&!kglQ@H%N3~dS{U}v{iyoFgUGkR*hPT$d0K|i+|zdiqgtX+Y>rp z?E@}`D1jGsrqao1<72mQA_uRLbsGBqCQw$Eb|Ri!8``ZbMSBHdNPF^gG7e84E4FEr z$|BLW&#C}Rq7O@%SSHLaIUZFi+jM3&x-Ji+_W#l@yW5HhZO@uCy#ZtD=X!!9C{)`X z{`BzSgVdQ1Aj_F(8h>_5IB8DOD(rNN1A*!7P=R@t;IB_B%92X|Lu}=LFF(yq^51jx z?++gWRlmco%i3~XNN#UA8EaCWIvK0kT)b_M(gImH=$i>9&|CdVOvhs8%zUSh21d-I zlts;M`=egq?z7E)lg{zA5n)w-k_Zz_X8SSWO$pCMg>xzJ^?$9)3hl*;uf%*e!k19r zOwn~2Z>*!9HJDD&T|svR-4%2X>ocIYyaHQOOLsxM6J@IT(#=K;=5hhcqLf z51o;z!%2Wxmi*65(#Hv~lx1v$y|1?Nwk?i}fkcwF@)b&+aAh;Ic%mwEvVS^0-B!ia z6x;`&?#a^GjDI*Dxz zZ%@y!{P3?Y!C%)&0d7-OxsFed@0H_+VD9N|%vOmeGvCm{VNKN(Vl;H& zNs6cF%5bcyeN{0k33!w*;UFWSZs`5-P#@`o{Qfgi5)KSQGy2NdFj5j~qmeOI)X`|9 zG%Mj?G=Egb#&8gfO^$^9?ij$Jg9b3{v;mAdZvf+t8^BtQBLl%?CZM4nG_>D6yl_xt ztFgz2#DZrQPXIxqKyY|odO(0>(e06DXmXKK!>zG1^}kBSYUdrHVXimM~4Q0sscSf092KX6976TTm$64 zJ>i-IfW5W8X#i+K>)Qc z_wOSb?~dYqRb=N@_C$@$CRi(e59)F3#BXdn>{)D2J&(Lje3?qx&+<$!^R40@KCf`|<2aA1Up z3=)w+A~Gn12oKE=32)lI3oo!1;D6BeOra6msDuqQ;tDDYxAziKjZbLW6HXvP+cSms zw7!8*pY{~1fe**p5-tG)u}OkJ0f7Pn1q2ER6nJAOuq5m+5lzJH{yP&{A)<*yG?9oV z>X6QNJDO;{=Da6jcSh=C_Dt%`F{#<3rc)=G@|@xwoQzuKaN;jf%I#6#8Gk|3$95;q zZmm+q_BdCL#NlzT6*E*%oIDv8Qi?osp6I#WFHgMU7=@H>RUD3lDnI5lIH{PG`ewhIqA-N3J-9l|< zKdIyGfA7(z*t}`ju9CdB(Tzmuc~h(nId3bIrw*?C&de1i`&GGz0D8=(NJ2sl&l5VRWjVSX9RqI>PE zD51Rq4|7DKbT`Be>wg12g&y^9$ex~L+vv>j#NSl7xT+FcXysz7r}6)FnAu=dXlD25 zBHYvtFFwo8T%NSGbP2rkMiX1TQ)~s7P450SHh*(YA9Dn1YZ{r~_Gi6EQcNpf*0eIx z>8EFrxz`&e%R0pt@L2UeB9ys0AKBBRFwKM!Co?JoT9$`=AAeZ@Z%gZeGs+y~)bSR` z0+SVh;7{a*Kmx(3V<8i~dzfQ$4luDWYa**-v9u{R+1n!*ns>ifB~>*MW+W>Bh)p@52-$bHI@it4m^a>9wsc4aZg4a2CLM*|63y z8AM;0gZPvz!i{qfz_ z3rJ?XK~t&f+amDUR*K-Mj+tv02zy4Gt40m{KArlAvVW1%3|YFwKT-I1{oVadiB-Bi ztnXR(;pVNes@r3&$@U4(+70{f7-voBaaG2;;i$SM zOF`z-u#=lueyY!Uj&rOjZ9#srx5N&!0RK|(&ss>e z$n{STA3n4bnO-aUylkts)Y@NoYqQ&*g3TR@%~jm{+<`|$$GF{>X=peYA8N8ZPH()c zoNq+q-N2Kx$we1I&ILJt$>dx&o+9VGai6iA^-s-+`0DRVTANR|7P*;hpjbfbn1yzM zLx1Nf@=N5E0`Uamy*9)%3eJgQZvEUt^;e{JnHyS^+HX54DN>tYm}q!>eMw+qM{KwY znD_?PmBdPm*f$eIMG%!{h)TcU;I9Pa)|s8}GS{D=?=ZjK9DOIlvoI4eQ}3X`c7c@? zX!J{@CISNm2EH~792A_1SMM;q{ZGMb>woyWr_R0mitkYBEI%1cmK1Cc#3ND2ckGlq zW(5}Z;lyTjixckN#tC;rWms^+-6f*Gl0X}{?Pim)h%inZ$1eHkU{}DtA08w`Df#$d zSK$JBcRrwVw6O~40~EeH-dINu{N_+V=aBt_ec&TXF`3hBGI#Ox z+{0v6rbx+h_SUD2SjC>Vv3Yc`ntxb{Q6$dBZt`%qH>cHV!RLyR0(?dkn)8o2ol;4y zx7j(pVk&n-+8mdrmDA)V)T$Wst*HGATSB=3a3LOnK;sRO7w4t|;RV2$Oa{9l-$2{1ldTKc$uk?m+ zAS3d#Y&#{opc*Qu`gjbXVjO7$t$$<~6ZL2UE#qinDZ@#BJkd1MsErn-doE&Gq1tvvnpMqtYS&@!KSErl7 zZsV~gDA_(5J}8KiU}F+40oPFQFk@r-9TwMwdT#|2+WjW9dswjlxfv7Q3jnwIN~Ri_ zfoC*&Y0}4a*_2~j@>BDT;^MjH9r2bFEOJLFz|qZ8QFH{t+vfIO>wkWt75TH0()3kk zCmXwBTDmcxt#8A31kLTxeOy`n00xj9BQ$ z_5g~_o1lVA{1XKSPvm|a(64zTSgZwY#oZ-4NgOvJ0`MvZ;AITs=>yw&B0}Q#ODl$= zkBqvvOh0+HM8=R?Y=4Q?L^HC7M!dSOHL?dVh-(4FU0NboRRpUxMmWa>F+`CqbCS&{ zSN=q8>mG`!UX-MS=0z#E*xgR5R(5)dx@z~lB^=e&k)hN^(rzlGrWOP#{RJ`o_Yxz@ zsgm1#d;uKWZ2PW&#p};kzg<-m+cEEaup|`QfX Date: Wed, 3 May 2023 18:32:10 -0400 Subject: [PATCH 217/243] Update changelog.md for release/v1.23.1 --- CHANGELOG.md | 110 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 500f4161995..071ee34b44b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,115 @@ # Lotus changelog +# v1.23.1 / 2023-05-03 + + - Update build version for release/v1.23.1 + - Disable lotus markets by default (#10809) ([filecoin-project/lotus#10809](https://github.com/filecoin-project/lotus/pull/10809)) + - perf: mempool: lower priority optimizations (#10693) ([filecoin-project/lotus#10693](https://github.com/filecoin-project/lotus/pull/10693)) + - Change args check ([filecoin-project/lotus#10812](https://github.com/filecoin-project/lotus/pull/10812)) + - chore: drop flaky TestBatchDealInput subcase ([filecoin-project/lotus#10810](https://github.com/filecoin-project/lotus/pull/10810)) + - fix: sealing: Make lotus-worker report GPU usage to miner during ReplicaUpdate task (#10806) ([filecoin-project/lotus#10806](https://github.com/filecoin-project/lotus/pull/10806)) + - fix:splitstore:Don't block when potentially holding txnLk as writer ([filecoin-project/lotus#10811](https://github.com/filecoin-project/lotus/pull/10811)) + - feat: worker: Ensure tempdir exists (#10433) ([filecoin-project/lotus#10433](https://github.com/filecoin-project/lotus/pull/10433)) + - feat: chainstore: batch writes of tipsets ([filecoin-project/lotus#10800](https://github.com/filecoin-project/lotus/pull/10800)) + - chore: changelog clean up ([filecoin-project/lotus#10744](https://github.com/filecoin-project/lotus/pull/10744)) + - chore: refactor: drop unused IsTicketWinner (#10801) ([filecoin-project/lotus#10801](https://github.com/filecoin-project/lotus/pull/10801)) + - Revert #9858 (consistent broadcast changes) ([filecoin-project/lotus#10777](https://github.com/filecoin-project/lotus/pull/10777)) + - chore: deps: update to FVM 3.3.1 ([filecoin-project/lotus#10786](https://github.com/filecoin-project/lotus/pull/10786)) + - Set default for MaxSectorProveCommitsSubmittedPerEpoch ([filecoin-project/lotus#10728](https://github.com/filecoin-project/lotus/pull/10728)) + - fix: deflake: use 2 miners for flaky tests ([filecoin-project/lotus#10764](https://github.com/filecoin-project/lotus/pull/10764)) + - fix: prover: Propagate skipped sectors in local PoSt + - test: eth: deflake multiblock lookup test (#10769) ([filecoin-project/lotus#10769](https://github.com/filecoin-project/lotus/pull/10769)) + - feat: sync: harden chain sync (#10756) ([filecoin-project/lotus#10756](https://github.com/filecoin-project/lotus/pull/10756)) + - fix: tvx: make extract-multiple support the FVM ([filecoin-project/lotus#10714](https://github.com/filecoin-project/lotus/pull/10714)) + - feat: badger: add a has check before writing to reduce duplicates ([filecoin-project/lotus#10680](https://github.com/filecoin-project/lotus/pull/10680)) + - fix: events: don't set GC confidence to 1 ([filecoin-project/lotus#10713](https://github.com/filecoin-project/lotus/pull/10713)) + - fix: sync: reduce log from error to info ([filecoin-project/lotus#10759](https://github.com/filecoin-project/lotus/pull/10759)) + - shed: migrations: add reminder about continuity testing tool ([filecoin-project/lotus#10762](https://github.com/filecoin-project/lotus/pull/10762)) + - fix: chain: record heaviest tipset before notifying (#10694) ([filecoin-project/lotus#10694](https://github.com/filecoin-project/lotus/pull/10694)) + - fix: sealing pipeline: Allow nil message in TerminateWait ([filecoin-project/lotus#10696](https://github.com/filecoin-project/lotus/pull/10696)) + - fix: storage: Remove temp fetching files after failed fetch ([filecoin-project/lotus#10661](https://github.com/filecoin-project/lotus/pull/10661)) + - feat: daemon: Auto-resume interrupted snapshot imports ([filecoin-project/lotus#10636](https://github.com/filecoin-project/lotus/pull/10636)) + - feat: shed: refactor market cron-state command ([filecoin-project/lotus#10746](https://github.com/filecoin-project/lotus/pull/10746)) + - fix: Eth JSON-RPC api: handle messages with gasFeeCap less than baseFee (#10614) ([filecoin-project/lotus#10614](https://github.com/filecoin-project/lotus/pull/10614)) + - chore: merge releases into master ([filecoin-project/lotus#10742](https://github.com/filecoin-project/lotus/pull/10742)) + - feat: sync: validate (early) that blocks fall within range (#10691) ([filecoin-project/lotus#10691](https://github.com/filecoin-project/lotus/pull/10691)) + - chainstore: Fix raw blocks getting scanned for links during snapshots (#10684) ([filecoin-project/lotus#10684](https://github.com/filecoin-project/lotus/pull/10684)) + - perf: Address performance of EthGetTransactionCount ([filecoin-project/lotus#10700](https://github.com/filecoin-project/lotus/pull/10700)) + - feat: sealing: Split PCA/PCB batches if gas used exceeds block limit ([filecoin-project/lotus#10647](https://github.com/filecoin-project/lotus/pull/10647)) + - fix: remove pointless panic ([filecoin-project/lotus#10690](https://github.com/filecoin-project/lotus/pull/10690)) + - chore: build: bump matser version to v1.23.1-dev ([filecoin-project/lotus#10709](https://github.com/filecoin-project/lotus/pull/10709)) + - chore: boxo: migrate from go-libipfs to boxo ([filecoin-project/lotus#10562](https://github.com/filecoin-project/lotus/pull/10562)) + - fix: unseal: check if sealed/update sector exists ([filecoin-project/lotus#10639](https://github.com/filecoin-project/lotus/pull/10639)) + - fix: check for nil bcastDict (#10646) ([filecoin-project/lotus#10646](https://github.com/filecoin-project/lotus/pull/10646)) + - Add API and CLI to unseal sector (#10626) ([filecoin-project/lotus#10626](https://github.com/filecoin-project/lotus/pull/10626)) + - test: events: fix race when recording tipsets (#10665) ([filecoin-project/lotus#10665](https://github.com/filecoin-project/lotus/pull/10665)) + - fix:mpool: prune excess messages before selection ([filecoin-project/lotus#10648](https://github.com/filecoin-project/lotus/pull/10648)) + - Update config default value (#10605) ([filecoin-project/lotus#10605](https://github.com/filecoin-project/lotus/pull/10605)) + - fix: cap the message gas limit at the block gas limit (#10637) ([filecoin-project/lotus#10637](https://github.com/filecoin-project/lotus/pull/10637)) + - fix: build: add CBDeliveryDelay to testground ([filecoin-project/lotus#10613](https://github.com/filecoin-project/lotus/pull/10613)) + - feat:splitstore:limit moving gc threads ([filecoin-project/lotus#10621](https://github.com/filecoin-project/lotus/pull/10621)) + - feat: chainstore: optimize BlockMsgsForTipset ([filecoin-project/lotus#10552](https://github.com/filecoin-project/lotus/pull/10552)) + - fix: make state compute --html work with unknown methods ([filecoin-project/lotus#10619](https://github.com/filecoin-project/lotus/pull/10619)) + - fix: cli: Check if the sectorID exists before removing ([filecoin-project/lotus#10611](https://github.com/filecoin-project/lotus/pull/10611)) + - Fixed incorrect words that could not be compiled ([filecoin-project/lotus#10610](https://github.com/filecoin-project/lotus/pull/10610)) + - Add feature to stagger sector prove commit submission (#10543) ([filecoin-project/lotus#10543](https://github.com/filecoin-project/lotus/pull/10543)) + - shed: get balances of evm accounts ([filecoin-project/lotus#10489](https://github.com/filecoin-project/lotus/pull/10489)) + - chore: deps: update to go-state-types v0.11.0-alpha-3 ([filecoin-project/lotus#10606](https://github.com/filecoin-project/lotus/pull/10606)) + - fix: cli: Make `net connect` to miner address work ([filecoin-project/lotus#10599](https://github.com/filecoin-project/lotus/pull/10599)) + - feat: VM Execution Lanes ([filecoin-project/lotus#10551](https://github.com/filecoin-project/lotus/pull/10551)) + - fix: log: Stop logging `file does not exists` ([filecoin-project/lotus#10588](https://github.com/filecoin-project/lotus/pull/10588)) + - perf: message pool: change locks to RWMutexes for performance ([filecoin-project/lotus#10561](https://github.com/filecoin-project/lotus/pull/10561)) + - fix: miner: correctly count sector extensions ([filecoin-project/lotus#10544](https://github.com/filecoin-project/lotus/pull/10544)) + - feat:networking: (Synchronous) Consistent Broadcast for Filecoin EC ([filecoin-project/lotus#9858](https://github.com/filecoin-project/lotus/pull/9858)) + - refactor: stop using deprecated io/ioutil ([filecoin-project/lotus#10596](https://github.com/filecoin-project/lotus/pull/10596)) + - feat: Use MessageIndex in WaitForMessage ([filecoin-project/lotus#10587](https://github.com/filecoin-project/lotus/pull/10587)) + - fix: searchForIndexedMsg always returns an error ([filecoin-project/lotus#10586](https://github.com/filecoin-project/lotus/pull/10586)) + - build: docker: Update GO-version ([filecoin-project/lotus#10581](https://github.com/filecoin-project/lotus/pull/10581)) + - fix: itests: Don't call t.Error in MineBlocks goroutine ([filecoin-project/lotus#10572](https://github.com/filecoin-project/lotus/pull/10572)) + - Fix: export-range: Ignore ipld Blocks not found in Receipts. ([filecoin-project/lotus#10535](https://github.com/filecoin-project/lotus/pull/10535)) + - feat: populate the index on snapshot import ([filecoin-project/lotus#10556](https://github.com/filecoin-project/lotus/pull/10556)) + - feat: Add small cache to execution traces ([filecoin-project/lotus#10517](https://github.com/filecoin-project/lotus/pull/10517)) + - docs: api: clarify MpoolClear params ([filecoin-project/lotus#10550](https://github.com/filecoin-project/lotus/pull/10550)) + - fix: proving: Initialize slice with with same length as partition ([filecoin-project/lotus#10569](https://github.com/filecoin-project/lotus/pull/10569)) + - perf: eth: gas estimate set applyTsMessages false (#10546) ([filecoin-project/lotus#10546](https://github.com/filecoin-project/lotus/pull/10546)) + - feat: shed: incoming block-sub chainwatch tool ([filecoin-project/lotus#10513](https://github.com/filecoin-project/lotus/pull/10513)) + - feat: stmgr: speed up calculation of genesis circ supply ([filecoin-project/lotus#10553](https://github.com/filecoin-project/lotus/pull/10553)) + - fix: gas estimation: don't special case paych collects ([filecoin-project/lotus#10549](https://github.com/filecoin-project/lotus/pull/10549)) + - fix: tracer: emit raw peer ids for compatibility with libp2p tracer ([filecoin-project/lotus#10271](https://github.com/filecoin-project/lotus/pull/10271)) + - fix: state: lotus-miner info should show deals info without admin permission ([filecoin-project/lotus#10323](https://github.com/filecoin-project/lotus/pull/10323)) + - Merge branch 'feat/new-gw-methods' + - chore: bump go-libipfs ([filecoin-project/lotus#10531](https://github.com/filecoin-project/lotus/pull/10531)) + - feat:chain: Message Index ([filecoin-project/lotus#10452](https://github.com/filecoin-project/lotus/pull/10452)) + +Contributors + +| Contributor | Commits | Lines ± | Files Changed | +|-------------|---------|---------|---------------| +| vyzo | 70 | +1990/-429 | 135 | +| Alfonso de la Rocha | 25 | +814/-299 | 56 | +| Steven Allen | 14 | +125/-539 | 28 | +| Shrenuj Bansal | 13 | +482/-138 | 52 | +| Aayush | 17 | +317/-301 | 90 | +| Łukasz Magiera | 13 | +564/-26 | 16 | +| Jennifer Wang | 7 | +401/-140 | 10 | +| Fridrik Asmundsson | 14 | +315/-84 | 20 | +| Jorropo | 2 | +139/-137 | 74 | +| Mikers | 6 | +114/-43 | 14 | +| Hector Sanjuan | 5 | +92/-44 | 5 | +| Ales Dumikau | 1 | +117/-0 | 10 | +| Mike Seiler | 4 | +51/-51 | 6 | +| zenground0 | 6 | +33/-25 | 8 | +| Phi | 8 | +32/-10 | 10 | +| Aayush Rajasekaran | 1 | +1/-32 | 2 | +| Ian Davis | 2 | +7/-10 | 3 | +| Marcel Telka | 1 | +5/-7 | 1 | +| ychiao | 1 | +8/-3 | 2 | +| jennijuju | 1 | +4/-4 | 8 | +| adlrocha | 2 | +2/-2 | 2 | +| Jiaying Wang | 1 | +0/-4 | 1 | +| ZenGround0 | 1 | +2/-1 | 2 | +| Zeng Li | 1 | +1/-1 | 1 | + # v1.23.0 / 2023-04-21 This is the stable feature release for the upcoming MANDATORY network upgrade at `2023-04-27T13:00:00Z`, epoch `2809800`. This feature release delivers the nv19 Lighting and nv20 Thunder network upgrade for mainnet, and includes numerous improvements and enhancements for node operators, ETH RPC-providers and storage providers. From e8dcf9f838d4417d27be6cb65599ab91acc79d55 Mon Sep 17 00:00:00 2001 From: Shrenuj Bansal <108157875+shrenujbansal@users.noreply.github.com> Date: Wed, 3 May 2023 19:07:15 -0400 Subject: [PATCH 218/243] update cl (#10819) --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 071ee34b44b..4657fcd8c25 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ # v1.23.1 / 2023-05-03 +# What's changed - Update build version for release/v1.23.1 - Disable lotus markets by default (#10809) ([filecoin-project/lotus#10809](https://github.com/filecoin-project/lotus/pull/10809)) - perf: mempool: lower priority optimizations (#10693) ([filecoin-project/lotus#10693](https://github.com/filecoin-project/lotus/pull/10693)) From 779975c3a93ddb43ab0f10fc556d5e696310df3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 25 Apr 2023 09:54:26 +0200 Subject: [PATCH 219/243] ci: Debugging m1 build --- .circleci/config.yml | 13 +++++++------ .circleci/template.yml | 13 +++++++------ 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index e3eb1c089b5..0efc8490c32 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -63,7 +63,7 @@ commands: name: Install Rust command: | curl https://sh.rustup.rs -sSf | sh -s -- -y - - run: make deps lotus + - run: make deps download-params: steps: - restore_cache: @@ -304,9 +304,7 @@ jobs: darwin: true darwin-architecture: arm64 - run: | - export CPATH=$(brew --prefix)/include - export LIBRARY_PATH=$(brew --prefix)/lib - make lotus lotus-miner lotus-worker + export CPATH=$(brew --prefix)/include && export LIBRARY_PATH=$(brew --prefix)/lib && make lotus lotus-miner lotus-worker - run: otool -hv lotus - run: name: check tag and version output match @@ -1069,6 +1067,7 @@ workflows: branches: only: - /^release\/v\d+\.\d+\.\d+(-rc\d+)?$/ + - /^ci\/.*$/ tags: only: - /^v\d+\.\d+\.\d+(-rc\d+)?$/ @@ -1078,6 +1077,7 @@ workflows: branches: only: - /^release\/v\d+\.\d+\.\d+(-rc\d+)?$/ + - /^ci\/.*$/ tags: only: - /^v\d+\.\d+\.\d+(-rc\d+)?$/ @@ -1087,6 +1087,7 @@ workflows: branches: only: - /^release\/v\d+\.\d+\.\d+(-rc\d+)?$/ + - /^ci\/.*$/ tags: only: - /^v\d+\.\d+\.\d+(-rc\d+)?$/ @@ -1098,8 +1099,8 @@ workflows: - "Build ( darwin / arm64 )" filters: branches: - ignore: - - /.*/ + only: + - /^ci\/.*$/ tags: only: - /^v\d+\.\d+\.\d+(-rc\d+)?$/ diff --git a/.circleci/template.yml b/.circleci/template.yml index 382965615ba..8393eec5be7 100644 --- a/.circleci/template.yml +++ b/.circleci/template.yml @@ -63,7 +63,7 @@ commands: name: Install Rust command: | curl https://sh.rustup.rs -sSf | sh -s -- -y - - run: make deps lotus + - run: make deps download-params: steps: - restore_cache: @@ -304,9 +304,7 @@ jobs: darwin: true darwin-architecture: arm64 - run: | - export CPATH=$(brew --prefix)/include - export LIBRARY_PATH=$(brew --prefix)/lib - make lotus lotus-miner lotus-worker + export CPATH=$(brew --prefix)/include && export LIBRARY_PATH=$(brew --prefix)/lib && make lotus lotus-miner lotus-worker - run: otool -hv lotus - run: name: check tag and version output match @@ -583,6 +581,7 @@ workflows: branches: only: - /^release\/v\d+\.\d+\.\d+(-rc\d+)?$/ + - /^ci\/.*$/ tags: only: - /^v\d+\.\d+\.\d+(-rc\d+)?$/ @@ -592,6 +591,7 @@ workflows: branches: only: - /^release\/v\d+\.\d+\.\d+(-rc\d+)?$/ + - /^ci\/.*$/ tags: only: - /^v\d+\.\d+\.\d+(-rc\d+)?$/ @@ -601,6 +601,7 @@ workflows: branches: only: - /^release\/v\d+\.\d+\.\d+(-rc\d+)?$/ + - /^ci\/.*$/ tags: only: - /^v\d+\.\d+\.\d+(-rc\d+)?$/ @@ -612,8 +613,8 @@ workflows: - "Build ( darwin / arm64 )" filters: branches: - ignore: - - /.*/ + only: + - /^ci\/.*$/ tags: only: - /^v\d+\.\d+\.\d+(-rc\d+)?$/ From 9558df7cdaafaab0779a71f55d9b0a4352429b42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 25 Apr 2023 10:24:18 +0200 Subject: [PATCH 220/243] temp drop m1 from releases --- .circleci/config.yml | 1 - .circleci/template.yml | 1 - 2 files changed, 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 0efc8490c32..a94329d81b9 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1096,7 +1096,6 @@ workflows: requires: - "Build ( darwin / amd64 )" - "Build ( linux / amd64 )" - - "Build ( darwin / arm64 )" filters: branches: only: diff --git a/.circleci/template.yml b/.circleci/template.yml index 8393eec5be7..ed5ba485060 100644 --- a/.circleci/template.yml +++ b/.circleci/template.yml @@ -610,7 +610,6 @@ workflows: requires: - "Build ( darwin / amd64 )" - "Build ( linux / amd64 )" - - "Build ( darwin / arm64 )" filters: branches: only: From 0cfb69b95a583138328d18b917acf1de713b2b0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 25 Apr 2023 10:25:40 +0200 Subject: [PATCH 221/243] ci: run release on ci/ branches, include m1 --- .circleci/config.yml | 6 ++++-- .circleci/template.yml | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index a94329d81b9..9038fdb8ff9 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1096,10 +1096,11 @@ workflows: requires: - "Build ( darwin / amd64 )" - "Build ( linux / amd64 )" + - "Build ( darwin / arm64 )" filters: branches: - only: - - /^ci\/.*$/ + ignore: + - /^.*$/ tags: only: - /^v\d+\.\d+\.\d+(-rc\d+)?$/ @@ -1114,6 +1115,7 @@ workflows: branches: only: - /^release\/v\d+\.\d+\.\d+(-rc\d+)?$/ + - /^ci\/.*$/ - build-docker: name: "Docker push (lotus-all-in-one / stable / mainnet)" image: lotus-all-in-one diff --git a/.circleci/template.yml b/.circleci/template.yml index ed5ba485060..cd8aeb663c9 100644 --- a/.circleci/template.yml +++ b/.circleci/template.yml @@ -610,10 +610,11 @@ workflows: requires: - "Build ( darwin / amd64 )" - "Build ( linux / amd64 )" + - "Build ( darwin / arm64 )" filters: branches: - only: - - /^ci\/.*$/ + ignore: + - /^.*$/ tags: only: - /^v\d+\.\d+\.\d+(-rc\d+)?$/ @@ -628,6 +629,7 @@ workflows: branches: only: - /^release\/v\d+\.\d+\.\d+(-rc\d+)?$/ + - /^ci\/.*$/ [[- range .Networks]] - build-docker: name: "Docker push (lotus-all-in-one / stable / [[.]])" From 4e49b8b671a16112a6bc1346a1588c62c209a9d2 Mon Sep 17 00:00:00 2001 From: Phi Date: Mon, 8 May 2023 13:13:18 +0200 Subject: [PATCH 222/243] Hide lotus-miner legacy markets cmds Hide lotus-miner legacy markets cmds --- cmd/lotus-miner/main.go | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/cmd/lotus-miner/main.go b/cmd/lotus-miner/main.go index 3cc796168c8..911e98e260a 100644 --- a/cmd/lotus-miner/main.go +++ b/cmd/lotus-miner/main.go @@ -43,16 +43,16 @@ func main() { backupCmd, lcli.WithCategory("chain", actorCmd), lcli.WithCategory("chain", infoCmd), - lcli.WithCategory("market", storageDealsCmd), - lcli.WithCategory("market", retrievalDealsCmd), - lcli.WithCategory("market", dataTransfersCmd), - lcli.WithCategory("market", dagstoreCmd), - lcli.WithCategory("market", indexProvCmd), + lcli.WithCategory("market", setHidden(storageDealsCmd)), + lcli.WithCategory("market", setHidden(retrievalDealsCmd)), + lcli.WithCategory("market", setHidden(dataTransfersCmd)), + lcli.WithCategory("market", setHidden(dagstoreCmd)), + lcli.WithCategory("market", setHidden(indexProvCmd)), lcli.WithCategory("storage", sectorsCmd), lcli.WithCategory("storage", provingCmd), lcli.WithCategory("storage", storageCmd), lcli.WithCategory("storage", sealingCmd), - lcli.WithCategory("retrieval", piecesCmd), + lcli.WithCategory("retrieval", setHidden(piecesCmd)), } jaeger := tracing.SetupJaegerTracing("lotus") @@ -86,6 +86,7 @@ func main() { // adapt the Net* commands to always hit the node running the markets // subsystem, as that is the only one that runs a libp2p node. netCmd := *lcli.NetCmd // make a copy. + netCmd.Hidden = true prev := netCmd.Before netCmd.Before = func(c *cli.Context) error { if prev != nil { @@ -137,11 +138,12 @@ func main() { &cli.StringFlag{ Name: FlagMarketsRepo, EnvVars: []string{"LOTUS_MARKETS_PATH"}, - Usage: fmt.Sprintf("Markets repo path"), + Hidden: true, }, &cli.BoolFlag{ - Name: "call-on-markets", - Usage: "(experimental; may be removed) call this command against a markets node; use only with common commands like net, auth, pprof, etc. whose target may be ambiguous", + Name: "call-on-markets", + Usage: "(experimental; may be removed) call this command against a markets node; use only with common commands like net, auth, pprof, etc. whose target may be ambiguous", + Hidden: true, }, cliutil.FlagVeryVerbose, }, @@ -190,3 +192,8 @@ func getActorAddress(ctx context.Context, cctx *cli.Context) (maddr address.Addr return maddr, nil } + +func setHidden(cmd *cli.Command) *cli.Command { + cmd.Hidden = true + return cmd +} From f03c06964f8381fd6c3356182fc59c1dcfe7338a Mon Sep 17 00:00:00 2001 From: Phi Date: Mon, 8 May 2023 13:42:53 +0200 Subject: [PATCH 223/243] Make docsgen-cli Make docsgen-cli --- documentation/en/cli-lotus-miner.md | 1047 --------------------------- 1 file changed, 1047 deletions(-) diff --git a/documentation/en/cli-lotus-miner.md b/documentation/en/cli-lotus-miner.md index cc194306510..c728c684ac7 100644 --- a/documentation/en/cli-lotus-miner.md +++ b/documentation/en/cli-lotus-miner.md @@ -25,16 +25,6 @@ COMMANDS: log Manage logging wait-api Wait for lotus api to come online fetch-params Fetch proving parameters - MARKET: - storage-deals Manage storage deals and related configuration - retrieval-deals Manage retrieval deals and related configuration - data-transfers Manage data transfers - dagstore Manage the dagstore on the markets subsystem - index Manage the index provider on the markets subsystem - NETWORK: - net Manage P2P Network - RETRIEVAL: - pieces interact with the piecestore STORAGE: sectors interact with sector store proving View proving information @@ -43,10 +33,8 @@ COMMANDS: GLOBAL OPTIONS: --actor value, -a value specify other actor to query / manipulate - --call-on-markets (experimental; may be removed) call this command against a markets node; use only with common commands like net, auth, pprof, etc. whose target may be ambiguous (default: false) --color use color in display output (default: depends on output being a TTY) --help, -h show help (default: false) - --markets-repo value Markets repo path [$LOTUS_MARKETS_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] --version, -v print the version (default: false) --vv enables very verbose mode, useful for debugging the CLI (default: false) @@ -607,1041 +595,6 @@ OPTIONS: ``` -## lotus-miner storage-deals -``` -NAME: - lotus-miner storage-deals - Manage storage deals and related configuration - -USAGE: - lotus-miner storage-deals command [command options] [arguments...] - -COMMANDS: - import-data Manually import data for a deal - list List all deals for this miner - selection Configure acceptance criteria for storage deal proposals - set-ask Configure the miner's ask - get-ask Print the miner's ask - set-blocklist Set the miner's list of blocklisted piece CIDs - get-blocklist List the contents of the miner's piece CID blocklist - reset-blocklist Remove all entries from the miner's piece CID blocklist - set-seal-duration Set the expected time, in minutes, that you expect sealing sectors to take. Deals that start before this duration will be rejected. - pending-publish list deals waiting in publish queue - retry-publish retry publishing a deal - help, h Shows a list of commands or help for one command - -OPTIONS: - --help, -h show help (default: false) - -``` - -### lotus-miner storage-deals import-data -``` -NAME: - lotus-miner storage-deals import-data - Manually import data for a deal - -USAGE: - lotus-miner storage-deals import-data [command options] - -OPTIONS: - --help, -h show help (default: false) - -``` - -### lotus-miner storage-deals list -``` -NAME: - lotus-miner storage-deals list - List all deals for this miner - -USAGE: - lotus-miner storage-deals list [command options] [arguments...] - -OPTIONS: - --format value output format of data, supported: table, json (default: "table") - --verbose, -v (default: false) - --watch watch deal updates in real-time, rather than a one time list (default: false) - -``` - -### lotus-miner storage-deals selection -``` -NAME: - lotus-miner storage-deals selection - Configure acceptance criteria for storage deal proposals - -USAGE: - lotus-miner storage-deals selection command [command options] [arguments...] - -COMMANDS: - list List storage deal proposal selection criteria - reset Reset storage deal proposal selection criteria to default values - reject Configure criteria which necessitate automatic rejection - help, h Shows a list of commands or help for one command - -OPTIONS: - --help, -h show help (default: false) - -``` - -#### lotus-miner storage-deals selection list -``` -NAME: - lotus-miner storage-deals selection list - List storage deal proposal selection criteria - -USAGE: - lotus-miner storage-deals selection list [command options] [arguments...] - -OPTIONS: - --help, -h show help (default: false) - -``` - -#### lotus-miner storage-deals selection reset -``` -NAME: - lotus-miner storage-deals selection reset - Reset storage deal proposal selection criteria to default values - -USAGE: - lotus-miner storage-deals selection reset [command options] [arguments...] - -OPTIONS: - --help, -h show help (default: false) - -``` - -#### lotus-miner storage-deals selection reject -``` -NAME: - lotus-miner storage-deals selection reject - Configure criteria which necessitate automatic rejection - -USAGE: - lotus-miner storage-deals selection reject [command options] [arguments...] - -OPTIONS: - --offline (default: false) - --online (default: false) - --unverified (default: false) - --verified (default: false) - -``` - -### lotus-miner storage-deals set-ask -``` -NAME: - lotus-miner storage-deals set-ask - Configure the miner's ask - -USAGE: - lotus-miner storage-deals set-ask [command options] [arguments...] - -OPTIONS: - --max-piece-size SIZE Set maximum piece size (w/bit-padding, in bytes) in ask to SIZE (default: miner sector size) - --min-piece-size SIZE Set minimum piece size (w/bit-padding, in bytes) in ask to SIZE (default: 256B) - --price PRICE Set the price of the ask for unverified deals (specified as FIL / GiB / Epoch) to PRICE. - --verified-price PRICE Set the price of the ask for verified deals (specified as FIL / GiB / Epoch) to PRICE - -``` - -### lotus-miner storage-deals get-ask -``` -NAME: - lotus-miner storage-deals get-ask - Print the miner's ask - -USAGE: - lotus-miner storage-deals get-ask [command options] [arguments...] - -OPTIONS: - --help, -h show help (default: false) - -``` - -### lotus-miner storage-deals set-blocklist -``` -NAME: - lotus-miner storage-deals set-blocklist - Set the miner's list of blocklisted piece CIDs - -USAGE: - lotus-miner storage-deals set-blocklist [command options] [ (optional, will read from stdin if omitted)] - -OPTIONS: - --help, -h show help (default: false) - -``` - -### lotus-miner storage-deals get-blocklist -``` -NAME: - lotus-miner storage-deals get-blocklist - List the contents of the miner's piece CID blocklist - -USAGE: - lotus-miner storage-deals get-blocklist [command options] [arguments...] - -OPTIONS: - -``` - -### lotus-miner storage-deals reset-blocklist -``` -NAME: - lotus-miner storage-deals reset-blocklist - Remove all entries from the miner's piece CID blocklist - -USAGE: - lotus-miner storage-deals reset-blocklist [command options] [arguments...] - -OPTIONS: - --help, -h show help (default: false) - -``` - -### lotus-miner storage-deals set-seal-duration -``` -NAME: - lotus-miner storage-deals set-seal-duration - Set the expected time, in minutes, that you expect sealing sectors to take. Deals that start before this duration will be rejected. - -USAGE: - lotus-miner storage-deals set-seal-duration [command options] - -OPTIONS: - --help, -h show help (default: false) - -``` - -### lotus-miner storage-deals pending-publish -``` -NAME: - lotus-miner storage-deals pending-publish - list deals waiting in publish queue - -USAGE: - lotus-miner storage-deals pending-publish [command options] [arguments...] - -OPTIONS: - --publish-now send a publish message now (default: false) - -``` - -### lotus-miner storage-deals retry-publish -``` -NAME: - lotus-miner storage-deals retry-publish - retry publishing a deal - -USAGE: - lotus-miner storage-deals retry-publish [command options] - -OPTIONS: - --help, -h show help (default: false) - -``` - -## lotus-miner retrieval-deals -``` -NAME: - lotus-miner retrieval-deals - Manage retrieval deals and related configuration - -USAGE: - lotus-miner retrieval-deals command [command options] [arguments...] - -COMMANDS: - selection Configure acceptance criteria for retrieval deal proposals - set-ask Configure the provider's retrieval ask - get-ask Get the provider's current retrieval ask configured by the provider in the ask-store using the set-ask CLI command - help, h Shows a list of commands or help for one command - -OPTIONS: - --help, -h show help (default: false) - -``` - -### lotus-miner retrieval-deals selection -``` -NAME: - lotus-miner retrieval-deals selection - Configure acceptance criteria for retrieval deal proposals - -USAGE: - lotus-miner retrieval-deals selection command [command options] [arguments...] - -COMMANDS: - list List retrieval deal proposal selection criteria - reset Reset retrieval deal proposal selection criteria to default values - reject Configure criteria which necessitate automatic rejection - help, h Shows a list of commands or help for one command - -OPTIONS: - --help, -h show help (default: false) - -``` - -#### lotus-miner retrieval-deals selection list -``` -NAME: - lotus-miner retrieval-deals selection list - List retrieval deal proposal selection criteria - -USAGE: - lotus-miner retrieval-deals selection list [command options] [arguments...] - -OPTIONS: - --help, -h show help (default: false) - -``` - -#### lotus-miner retrieval-deals selection reset -``` -NAME: - lotus-miner retrieval-deals selection reset - Reset retrieval deal proposal selection criteria to default values - -USAGE: - lotus-miner retrieval-deals selection reset [command options] [arguments...] - -OPTIONS: - --help, -h show help (default: false) - -``` - -#### lotus-miner retrieval-deals selection reject -``` -NAME: - lotus-miner retrieval-deals selection reject - Configure criteria which necessitate automatic rejection - -USAGE: - lotus-miner retrieval-deals selection reject [command options] [arguments...] - -OPTIONS: - --offline (default: false) - --online (default: false) - -``` - -### lotus-miner retrieval-deals set-ask -``` -NAME: - lotus-miner retrieval-deals set-ask - Configure the provider's retrieval ask - -USAGE: - lotus-miner retrieval-deals set-ask [command options] [arguments...] - -OPTIONS: - --payment-interval value Set the payment interval (in bytes) for retrieval (default: 1MiB) - --payment-interval-increase value Set the payment interval increase (in bytes) for retrieval (default: 1MiB) - --price value Set the price of the ask for retrievals (FIL/GiB) - --unseal-price value Set the price to unseal - -``` - -### lotus-miner retrieval-deals get-ask -``` -NAME: - lotus-miner retrieval-deals get-ask - Get the provider's current retrieval ask configured by the provider in the ask-store using the set-ask CLI command - -USAGE: - lotus-miner retrieval-deals get-ask [command options] [arguments...] - -OPTIONS: - --help, -h show help (default: false) - -``` - -## lotus-miner data-transfers -``` -NAME: - lotus-miner data-transfers - Manage data transfers - -USAGE: - lotus-miner data-transfers command [command options] [arguments...] - -COMMANDS: - list List ongoing data transfers for this miner - restart Force restart a stalled data transfer - cancel Force cancel a data transfer - diagnostics Get detailed diagnostics on active transfers with a specific peer - help, h Shows a list of commands or help for one command - -OPTIONS: - --help, -h show help (default: false) - -``` - -### lotus-miner data-transfers list -``` -NAME: - lotus-miner data-transfers list - List ongoing data transfers for this miner - -USAGE: - lotus-miner data-transfers list [command options] [arguments...] - -OPTIONS: - --completed show completed data transfers (default: false) - --show-failed show failed/cancelled transfers (default: false) - --verbose, -v print verbose transfer details (default: false) - --watch watch deal updates in real-time, rather than a one time list (default: false) - -``` - -### lotus-miner data-transfers restart -``` -NAME: - lotus-miner data-transfers restart - Force restart a stalled data transfer - -USAGE: - lotus-miner data-transfers restart [command options] [arguments...] - -OPTIONS: - --initiator specify only transfers where peer is/is not initiator (default: false) - --peerid value narrow to transfer with specific peer - -``` - -### lotus-miner data-transfers cancel -``` -NAME: - lotus-miner data-transfers cancel - Force cancel a data transfer - -USAGE: - lotus-miner data-transfers cancel [command options] [arguments...] - -OPTIONS: - --cancel-timeout value time to wait for cancel to be sent to client (default: 5s) - --initiator specify only transfers where peer is/is not initiator (default: false) - --peerid value narrow to transfer with specific peer - -``` - -### lotus-miner data-transfers diagnostics -``` -NAME: - lotus-miner data-transfers diagnostics - Get detailed diagnostics on active transfers with a specific peer - -USAGE: - lotus-miner data-transfers diagnostics [command options] [arguments...] - -OPTIONS: - --help, -h show help (default: false) - -``` - -## lotus-miner dagstore -``` -NAME: - lotus-miner dagstore - Manage the dagstore on the markets subsystem - -USAGE: - lotus-miner dagstore command [command options] [arguments...] - -COMMANDS: - list-shards List all shards known to the dagstore, with their current status - register-shard Register a shard - initialize-shard Initialize the specified shard - recover-shard Attempt to recover a shard in errored state - initialize-all Initialize all uninitialized shards, streaming results as they're produced; only shards for unsealed pieces are initialized by default - gc Garbage collect the dagstore - lookup-pieces Lookup pieces that a given CID belongs to - help, h Shows a list of commands or help for one command - -OPTIONS: - --help, -h show help (default: false) - -``` - -### lotus-miner dagstore list-shards -``` -NAME: - lotus-miner dagstore list-shards - List all shards known to the dagstore, with their current status - -USAGE: - lotus-miner dagstore list-shards [command options] [arguments...] - -OPTIONS: - --help, -h show help (default: false) - -``` - -### lotus-miner dagstore register-shard -``` -NAME: - lotus-miner dagstore register-shard - Register a shard - -USAGE: - lotus-miner dagstore register-shard [command options] [key] - -OPTIONS: - --help, -h show help (default: false) - -``` - -### lotus-miner dagstore initialize-shard -``` -NAME: - lotus-miner dagstore initialize-shard - Initialize the specified shard - -USAGE: - lotus-miner dagstore initialize-shard [command options] [key] - -OPTIONS: - --help, -h show help (default: false) - -``` - -### lotus-miner dagstore recover-shard -``` -NAME: - lotus-miner dagstore recover-shard - Attempt to recover a shard in errored state - -USAGE: - lotus-miner dagstore recover-shard [command options] [key] - -OPTIONS: - --help, -h show help (default: false) - -``` - -### lotus-miner dagstore initialize-all -``` -NAME: - lotus-miner dagstore initialize-all - Initialize all uninitialized shards, streaming results as they're produced; only shards for unsealed pieces are initialized by default - -USAGE: - lotus-miner dagstore initialize-all [command options] [arguments...] - -OPTIONS: - --concurrency value maximum shards to initialize concurrently at a time; use 0 for unlimited (default: 0) - --include-sealed initialize sealed pieces as well (default: false) - -``` - -### lotus-miner dagstore gc -``` -NAME: - lotus-miner dagstore gc - Garbage collect the dagstore - -USAGE: - lotus-miner dagstore gc [command options] [arguments...] - -OPTIONS: - --help, -h show help (default: false) - -``` - -### lotus-miner dagstore lookup-pieces -``` -NAME: - lotus-miner dagstore lookup-pieces - Lookup pieces that a given CID belongs to - -USAGE: - lotus-miner dagstore lookup-pieces [command options] - -OPTIONS: - --help, -h show help (default: false) - -``` - -## lotus-miner index -``` -NAME: - lotus-miner index - Manage the index provider on the markets subsystem - -USAGE: - lotus-miner index command [command options] [arguments...] - -COMMANDS: - announce Announce a deal to indexers so they can download its index - announce-all Announce all active deals to indexers so they can download the indices - help, h Shows a list of commands or help for one command - -OPTIONS: - --help, -h show help (default: false) - -``` - -### lotus-miner index announce -``` -NAME: - lotus-miner index announce - Announce a deal to indexers so they can download its index - -USAGE: - lotus-miner index announce [command options] - -OPTIONS: - --help, -h show help (default: false) - -``` - -### lotus-miner index announce-all -``` -NAME: - lotus-miner index announce-all - Announce all active deals to indexers so they can download the indices - -USAGE: - lotus-miner index announce-all [command options] [arguments...] - -OPTIONS: - --help, -h show help (default: false) - -``` - -## lotus-miner net -``` -NAME: - lotus-miner net - Manage P2P Network - -USAGE: - lotus-miner net command [command options] [arguments...] - -COMMANDS: - peers Print peers - ping Ping peers - connect Connect to a peer - disconnect Disconnect from a peer - listen List listen addresses - id Get node identity - find-peer, findpeer Find the addresses of a given peerID - scores Print peers' pubsub scores - reachability Print information about reachability from the internet - bandwidth Print bandwidth usage information - block Manage network connection gating rules - stat Report resource usage for a scope - limit Get or set resource limits for a scope - protect Add one or more peer IDs to the list of protected peer connections - unprotect Remove one or more peer IDs from the list of protected peer connections. - list-protected List the peer IDs with protected connection. - help, h Shows a list of commands or help for one command - -OPTIONS: - --help, -h show help (default: false) - -``` - -### lotus-miner net peers -``` -NAME: - lotus-miner net peers - Print peers - -USAGE: - lotus-miner net peers [command options] [arguments...] - -OPTIONS: - --agent, -a Print agent name (default: false) - --extended, -x Print extended peer information in json (default: false) - -``` - -### lotus-miner net ping -``` -NAME: - lotus-miner net ping - Ping peers - -USAGE: - lotus-miner net ping [command options] [peerMultiaddr] - -OPTIONS: - --count value, -c value specify the number of times it should ping (default: 10) - --interval value, -i value minimum time between pings (default: 1s) - -``` - -### lotus-miner net connect -``` -NAME: - lotus-miner net connect - Connect to a peer - -USAGE: - lotus-miner net connect [command options] [peerMultiaddr|minerActorAddress] - -OPTIONS: - --help, -h show help (default: false) - -``` - -### lotus-miner net disconnect -``` -NAME: - lotus-miner net disconnect - Disconnect from a peer - -USAGE: - lotus-miner net disconnect [command options] [peerID] - -OPTIONS: - --help, -h show help (default: false) - -``` - -### lotus-miner net listen -``` -NAME: - lotus-miner net listen - List listen addresses - -USAGE: - lotus-miner net listen [command options] [arguments...] - -OPTIONS: - --help, -h show help (default: false) - -``` - -### lotus-miner net id -``` -NAME: - lotus-miner net id - Get node identity - -USAGE: - lotus-miner net id [command options] [arguments...] - -OPTIONS: - --help, -h show help (default: false) - -``` - -#### lotus-miner net find-peer, findpeer -``` -``` - -### lotus-miner net scores -``` -NAME: - lotus-miner net scores - Print peers' pubsub scores - -USAGE: - lotus-miner net scores [command options] [arguments...] - -OPTIONS: - --extended, -x print extended peer scores in json (default: false) - -``` - -### lotus-miner net reachability -``` -NAME: - lotus-miner net reachability - Print information about reachability from the internet - -USAGE: - lotus-miner net reachability [command options] [arguments...] - -OPTIONS: - --help, -h show help (default: false) - -``` - -### lotus-miner net bandwidth -``` -NAME: - lotus-miner net bandwidth - Print bandwidth usage information - -USAGE: - lotus-miner net bandwidth [command options] [arguments...] - -OPTIONS: - --by-peer list bandwidth usage by peer (default: false) - --by-protocol list bandwidth usage by protocol (default: false) - -``` - -### lotus-miner net block -``` -NAME: - lotus-miner net block - Manage network connection gating rules - -USAGE: - lotus-miner net block command [command options] [arguments...] - -COMMANDS: - add Add connection gating rules - remove Remove connection gating rules - list list connection gating rules - help, h Shows a list of commands or help for one command - -OPTIONS: - --help, -h show help (default: false) - -``` - -#### lotus-miner net block add -``` -NAME: - lotus-miner net block add - Add connection gating rules - -USAGE: - lotus-miner net block add command [command options] [arguments...] - -COMMANDS: - peer Block a peer - ip Block an IP address - subnet Block an IP subnet - help, h Shows a list of commands or help for one command - -OPTIONS: - --help, -h show help (default: false) - -``` - -##### lotus-miner net block add peer -``` -NAME: - lotus-miner net block add peer - Block a peer - -USAGE: - lotus-miner net block add peer [command options] ... - -OPTIONS: - --help, -h show help (default: false) - -``` - -##### lotus-miner net block add ip -``` -NAME: - lotus-miner net block add ip - Block an IP address - -USAGE: - lotus-miner net block add ip [command options] ... - -OPTIONS: - --help, -h show help (default: false) - -``` - -##### lotus-miner net block add subnet -``` -NAME: - lotus-miner net block add subnet - Block an IP subnet - -USAGE: - lotus-miner net block add subnet [command options] ... - -OPTIONS: - --help, -h show help (default: false) - -``` - -#### lotus-miner net block remove -``` -NAME: - lotus-miner net block remove - Remove connection gating rules - -USAGE: - lotus-miner net block remove command [command options] [arguments...] - -COMMANDS: - peer Unblock a peer - ip Unblock an IP address - subnet Unblock an IP subnet - help, h Shows a list of commands or help for one command - -OPTIONS: - --help, -h show help (default: false) - -``` - -##### lotus-miner net block remove peer -``` -NAME: - lotus-miner net block remove peer - Unblock a peer - -USAGE: - lotus-miner net block remove peer [command options] ... - -OPTIONS: - --help, -h show help (default: false) - -``` - -##### lotus-miner net block remove ip -``` -NAME: - lotus-miner net block remove ip - Unblock an IP address - -USAGE: - lotus-miner net block remove ip [command options] ... - -OPTIONS: - --help, -h show help (default: false) - -``` - -##### lotus-miner net block remove subnet -``` -NAME: - lotus-miner net block remove subnet - Unblock an IP subnet - -USAGE: - lotus-miner net block remove subnet [command options] ... - -OPTIONS: - --help, -h show help (default: false) - -``` - -#### lotus-miner net block list -``` -NAME: - lotus-miner net block list - list connection gating rules - -USAGE: - lotus-miner net block list [command options] [arguments...] - -OPTIONS: - --help, -h show help (default: false) - -``` - -### lotus-miner net stat -``` -NAME: - lotus-miner net stat - Report resource usage for a scope - -USAGE: - lotus-miner net stat [command options] scope - -DESCRIPTION: - Report resource usage for a scope. - - The scope can be one of the following: - - system -- reports the system aggregate resource usage. - - transient -- reports the transient resource usage. - - svc: -- reports the resource usage of a specific service. - - proto: -- reports the resource usage of a specific protocol. - - peer: -- reports the resource usage of a specific peer. - - all -- reports the resource usage for all currently active scopes. - - -OPTIONS: - --json (default: false) - -``` - -### lotus-miner net limit -``` -NAME: - lotus-miner net limit - Get or set resource limits for a scope - -USAGE: - lotus-miner net limit [command options] scope [limit] - -DESCRIPTION: - Get or set resource limits for a scope. - - The scope can be one of the following: - - system -- reports the system aggregate resource usage. - - transient -- reports the transient resource usage. - - svc: -- reports the resource usage of a specific service. - - proto: -- reports the resource usage of a specific protocol. - - peer: -- reports the resource usage of a specific peer. - - The limit is json-formatted, with the same structure as the limits file. - - -OPTIONS: - --set set the limit for a scope (default: false) - -``` - -### lotus-miner net protect -``` -NAME: - lotus-miner net protect - Add one or more peer IDs to the list of protected peer connections - -USAGE: - lotus-miner net protect [command options] [...] - -OPTIONS: - --help, -h show help (default: false) - -``` - -### lotus-miner net unprotect -``` -NAME: - lotus-miner net unprotect - Remove one or more peer IDs from the list of protected peer connections. - -USAGE: - lotus-miner net unprotect [command options] [...] - -OPTIONS: - --help, -h show help (default: false) - -``` - -### lotus-miner net list-protected -``` -NAME: - lotus-miner net list-protected - List the peer IDs with protected connection. - -USAGE: - lotus-miner net list-protected [command options] [arguments...] - -OPTIONS: - --help, -h show help (default: false) - -``` - -## lotus-miner pieces -``` -NAME: - lotus-miner pieces - interact with the piecestore - -USAGE: - lotus-miner pieces command [command options] [arguments...] - -DESCRIPTION: - The piecestore is a database that tracks and manages data that is made available to the retrieval market - -COMMANDS: - list-pieces list registered pieces - list-cids list registered payload CIDs - piece-info get registered information for a given piece CID - cid-info get registered information for a given payload CID - help, h Shows a list of commands or help for one command - -OPTIONS: - --help, -h show help (default: false) - -``` - -### lotus-miner pieces list-pieces -``` -NAME: - lotus-miner pieces list-pieces - list registered pieces - -USAGE: - lotus-miner pieces list-pieces [command options] [arguments...] - -OPTIONS: - --help, -h show help (default: false) - -``` - -### lotus-miner pieces list-cids -``` -NAME: - lotus-miner pieces list-cids - list registered payload CIDs - -USAGE: - lotus-miner pieces list-cids [command options] [arguments...] - -OPTIONS: - --verbose, -v (default: false) - -``` - -### lotus-miner pieces piece-info -``` -NAME: - lotus-miner pieces piece-info - get registered information for a given piece CID - -USAGE: - lotus-miner pieces piece-info [command options] [arguments...] - -OPTIONS: - --help, -h show help (default: false) - -``` - -### lotus-miner pieces cid-info -``` -NAME: - lotus-miner pieces cid-info - get registered information for a given payload CID - -USAGE: - lotus-miner pieces cid-info [command options] [arguments...] - -OPTIONS: - --help, -h show help (default: false) - -``` - ## lotus-miner sectors ``` NAME: From 60d576241c7efa28f83c67cf5b96d6753ed5ea3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 10 May 2023 21:43:42 +0200 Subject: [PATCH 224/243] fix: sched: Address GET_32G_MAX_CONCURRENT regression (#10850) * Fix 1.21 regression: GET_32G_MAX_CONCURRENT + mixed prepared/executing leads to stuck scheduler If you have 12 GET tasks and GET_32G_MAX_CONCURRENT=1, sealing jobs will only show assigned tasks for GET of the miner and is stuck. I believe this to be a regression of 1.21 unifying the counters, in the case of GETs where PrepType and TaskType both being seal/v0/fetch leading to a state where tasks are blocked since already counted towards the limit. * itests: Repro issue from PR #10633 * make counters int (non-working) * fix: worker sched: Send taskDone notifs after tasks are done * itests: Make TestPledgeMaxConcurrentGet actually reproduce the issue * make the linter happy --------- Co-authored-by: Steffen Butzer --- .circleci/config.yml | 6 ++ itests/sealing_resources_test.go | 64 +++++++++++++++++++ storage/sealer/sched_assigner_common.go | 2 +- storage/sealer/sched_assigner_darts.go | 4 +- storage/sealer/sched_assigner_spread.go | 4 +- storage/sealer/sched_assigner_spread_tasks.go | 4 +- storage/sealer/sched_assigner_utilization.go | 4 +- storage/sealer/sched_post.go | 5 +- storage/sealer/sched_resources.go | 61 +++++++++++------- storage/sealer/sched_test.go | 4 +- storage/sealer/sched_worker.go | 29 +++++---- 11 files changed, 141 insertions(+), 46 deletions(-) create mode 100644 itests/sealing_resources_test.go diff --git a/.circleci/config.yml b/.circleci/config.yml index 9038fdb8ff9..5fcb831454c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -882,6 +882,12 @@ workflows: - build suite: itest-sdr_upgrade target: "./itests/sdr_upgrade_test.go" + - test: + name: test-itest-sealing_resources + requires: + - build + suite: itest-sealing_resources + target: "./itests/sealing_resources_test.go" - test: name: test-itest-sector_finalize_early requires: diff --git a/itests/sealing_resources_test.go b/itests/sealing_resources_test.go new file mode 100644 index 00000000000..85779fd88e7 --- /dev/null +++ b/itests/sealing_resources_test.go @@ -0,0 +1,64 @@ +package itests + +import ( + "context" + "os" + "testing" + "time" + + "github.com/stretchr/testify/require" + + "github.com/filecoin-project/lotus/itests/kit" + "github.com/filecoin-project/lotus/storage/sealer/storiface" +) + +// Regression check for a fix introduced in https://github.com/filecoin-project/lotus/pull/10633 +func TestPledgeMaxConcurrentGet(t *testing.T) { + require.NoError(t, os.Setenv("GET_2K_MAX_CONCURRENT", "1")) + t.Cleanup(func() { + require.NoError(t, os.Unsetenv("GET_2K_MAX_CONCURRENT")) + }) + + kit.QuietMiningLogs() + + blockTime := 50 * time.Millisecond + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + _, miner, ens := kit.EnsembleMinimal(t, kit.NoStorage()) // no mock proofs + ens.InterconnectAll().BeginMiningMustPost(blockTime) + + // separate sealed and storage paths so that finalize move needs to happen + miner.AddStorage(ctx, t, func(meta *storiface.LocalStorageMeta) { + meta.CanSeal = true + }) + miner.AddStorage(ctx, t, func(meta *storiface.LocalStorageMeta) { + meta.CanStore = true + }) + + // NOTE: This test only repros the issue when Fetch tasks take ~10s, there's + // no great way to do that in a non-horribly-hacky way + + /* The horribly hacky way: + + diff --git a/storage/sealer/sched_worker.go b/storage/sealer/sched_worker.go + index 35acd755d..76faec859 100644 + --- a/storage/sealer/sched_worker.go + +++ b/storage/sealer/sched_worker.go + @@ -513,6 +513,10 @@ func (sw *schedWorker) startProcessingTask(req *WorkerRequest) error { + tw.start() + err = <-werr + + + if req.TaskType == sealtasks.TTFetch { + + time.Sleep(10 * time.Second) + + } + + + select { + case req.ret <- workerResponse{err: err}: + case <-req.Ctx.Done(): + + */ + + miner.PledgeSectors(ctx, 3, 0, nil) +} diff --git a/storage/sealer/sched_assigner_common.go b/storage/sealer/sched_assigner_common.go index d676d410df5..ffc21b0dd63 100644 --- a/storage/sealer/sched_assigner_common.go +++ b/storage/sealer/sched_assigner_common.go @@ -103,7 +103,7 @@ func (a *AssignerCommon) TrySched(sh *Scheduler) { needRes := worker.Info.Resources.ResourceSpec(task.Sector.ProofType, task.TaskType) // TODO: allow bigger windows - if !windows[wnd].Allocated.CanHandleRequest(task.SealTask(), needRes, windowRequest.Worker, "schedAcceptable", worker.Info) { + if !windows[wnd].Allocated.CanHandleRequest(task.SchedId, task.SealTask(), needRes, windowRequest.Worker, "schedAcceptable", worker.Info) { continue } diff --git a/storage/sealer/sched_assigner_darts.go b/storage/sealer/sched_assigner_darts.go index e28b70e78a8..134698fbf0b 100644 --- a/storage/sealer/sched_assigner_darts.go +++ b/storage/sealer/sched_assigner_darts.go @@ -37,7 +37,7 @@ func RandomWS(sh *Scheduler, queueLen int, acceptableWindows [][]int, windows [] log.Debugf("SCHED try assign sqi:%d sector %d to window %d (awi:%d)", sqi, task.Sector.ID.Number, wnd, i) - if !windows[wnd].Allocated.CanHandleRequest(task.SealTask(), res, wid, "schedAssign", w.Info) { + if !windows[wnd].Allocated.CanHandleRequest(task.SchedId, task.SealTask(), res, wid, "schedAssign", w.Info) { continue } @@ -71,7 +71,7 @@ func RandomWS(sh *Scheduler, queueLen int, acceptableWindows [][]int, windows [] "worker", bestWid, "choices", len(choices)) - windows[selectedWindow].Allocated.Add(task.SealTask(), info.Resources, needRes) + windows[selectedWindow].Allocated.Add(task.SchedId, task.SealTask(), info.Resources, needRes) windows[selectedWindow].Todo = append(windows[selectedWindow].Todo, task) rmQueue = append(rmQueue, sqi) diff --git a/storage/sealer/sched_assigner_spread.go b/storage/sealer/sched_assigner_spread.go index 0a62b7406a9..b1ac4c8e9a8 100644 --- a/storage/sealer/sched_assigner_spread.go +++ b/storage/sealer/sched_assigner_spread.go @@ -35,7 +35,7 @@ func SpreadWS(queued bool) func(sh *Scheduler, queueLen int, acceptableWindows [ log.Debugf("SCHED try assign sqi:%d sector %d to window %d (awi:%d)", sqi, task.Sector.ID.Number, wnd, i) - if !windows[wnd].Allocated.CanHandleRequest(task.SealTask(), res, wid, "schedAssign", w.Info) { + if !windows[wnd].Allocated.CanHandleRequest(task.SchedId, task.SealTask(), res, wid, "schedAssign", w.Info) { continue } @@ -71,7 +71,7 @@ func SpreadWS(queued bool) func(sh *Scheduler, queueLen int, acceptableWindows [ "assigned", bestAssigned) workerAssigned[bestWid]++ - windows[selectedWindow].Allocated.Add(task.SealTask(), info.Resources, needRes) + windows[selectedWindow].Allocated.Add(task.SchedId, task.SealTask(), info.Resources, needRes) windows[selectedWindow].Todo = append(windows[selectedWindow].Todo, task) rmQueue = append(rmQueue, sqi) diff --git a/storage/sealer/sched_assigner_spread_tasks.go b/storage/sealer/sched_assigner_spread_tasks.go index 09cf9804602..f98e7b7456d 100644 --- a/storage/sealer/sched_assigner_spread_tasks.go +++ b/storage/sealer/sched_assigner_spread_tasks.go @@ -41,7 +41,7 @@ func SpreadTasksWS(queued bool) func(sh *Scheduler, queueLen int, acceptableWind log.Debugf("SCHED try assign sqi:%d sector %d to window %d (awi:%d)", sqi, task.Sector.ID.Number, wnd, i) - if !windows[wnd].Allocated.CanHandleRequest(task.SealTask(), res, wid, "schedAssign", w.Info) { + if !windows[wnd].Allocated.CanHandleRequest(task.SchedId, task.SealTask(), res, wid, "schedAssign", w.Info) { continue } @@ -80,7 +80,7 @@ func SpreadTasksWS(queued bool) func(sh *Scheduler, queueLen int, acceptableWind "assigned", bestAssigned) workerAssigned[bestWid]++ - windows[selectedWindow].Allocated.Add(task.SealTask(), info.Resources, needRes) + windows[selectedWindow].Allocated.Add(task.SchedId, task.SealTask(), info.Resources, needRes) windows[selectedWindow].Todo = append(windows[selectedWindow].Todo, task) rmQueue = append(rmQueue, sqi) diff --git a/storage/sealer/sched_assigner_utilization.go b/storage/sealer/sched_assigner_utilization.go index 1e75d904a2c..c81c9f18745 100644 --- a/storage/sealer/sched_assigner_utilization.go +++ b/storage/sealer/sched_assigner_utilization.go @@ -35,7 +35,7 @@ func LowestUtilizationWS(sh *Scheduler, queueLen int, acceptableWindows [][]int, log.Debugf("SCHED try assign sqi:%d sector %d to window %d (awi:%d)", sqi, task.Sector.ID.Number, wnd, i) // TODO: allow bigger windows - if !windows[wnd].Allocated.CanHandleRequest(task.SealTask(), res, wid, "schedAssign", w.Info) { + if !windows[wnd].Allocated.CanHandleRequest(task.SchedId, task.SealTask(), res, wid, "schedAssign", w.Info) { continue } @@ -82,7 +82,7 @@ func LowestUtilizationWS(sh *Scheduler, queueLen int, acceptableWindows [][]int, "worker", bestWid, "utilization", bestUtilization) - workerUtil[bestWid] += windows[selectedWindow].Allocated.Add(task.SealTask(), info.Resources, needRes) + workerUtil[bestWid] += windows[selectedWindow].Allocated.Add(task.SchedId, task.SealTask(), info.Resources, needRes) windows[selectedWindow].Todo = append(windows[selectedWindow].Todo, task) rmQueue = append(rmQueue, sqi) diff --git a/storage/sealer/sched_post.go b/storage/sealer/sched_post.go index 0e0c397688a..c6bd8182976 100644 --- a/storage/sealer/sched_post.go +++ b/storage/sealer/sched_post.go @@ -7,6 +7,7 @@ import ( "sync" "time" + "github.com/google/uuid" "github.com/hashicorp/go-multierror" "golang.org/x/xerrors" @@ -110,7 +111,7 @@ func (ps *poStScheduler) Schedule(ctx context.Context, primary bool, spt abi.Reg for i, selected := range candidates { worker := ps.workers[selected.id] - err := worker.active.withResources(selected.id, worker.Info, ps.postType.SealTask(spt), selected.res, &ps.lk, func() error { + err := worker.active.withResources(uuid.UUID{}, selected.id, worker.Info, ps.postType.SealTask(spt), selected.res, &ps.lk, func() error { ps.lk.Unlock() defer ps.lk.Lock() @@ -148,7 +149,7 @@ func (ps *poStScheduler) readyWorkers(spt abi.RegisteredSealProof) (bool, []cand continue } - if !wr.active.CanHandleRequest(ps.postType.SealTask(spt), needRes, wid, "post-readyWorkers", wr.Info) { + if !wr.active.CanHandleRequest(uuid.UUID{}, ps.postType.SealTask(spt), needRes, wid, "post-readyWorkers", wr.Info) { continue } diff --git a/storage/sealer/sched_resources.go b/storage/sealer/sched_resources.go index 597f36dbee5..a423def9fb8 100644 --- a/storage/sealer/sched_resources.go +++ b/storage/sealer/sched_resources.go @@ -3,6 +3,8 @@ package sealer import ( "sync" + "github.com/google/uuid" + "github.com/filecoin-project/lotus/storage/sealer/sealtasks" "github.com/filecoin-project/lotus/storage/sealer/storiface" ) @@ -20,7 +22,7 @@ type ActiveResources struct { } type taskCounter struct { - taskCounters map[sealtasks.SealTaskType]int + taskCounters map[sealtasks.SealTaskType]map[uuid.UUID]int // this lock is technically redundant, as ActiveResources is always accessed // with the worker lock, but let's not panic if we ever change that @@ -29,26 +31,40 @@ type taskCounter struct { func newTaskCounter() *taskCounter { return &taskCounter{ - taskCounters: map[sealtasks.SealTaskType]int{}, + taskCounters: make(map[sealtasks.SealTaskType]map[uuid.UUID]int), } } -func (tc *taskCounter) Add(tt sealtasks.SealTaskType) { +func (tc *taskCounter) Add(tt sealtasks.SealTaskType, schedID uuid.UUID) { tc.lk.Lock() defer tc.lk.Unlock() - tc.taskCounters[tt]++ + tc.getUnlocked(tt)[schedID]++ } -func (tc *taskCounter) Free(tt sealtasks.SealTaskType) { +func (tc *taskCounter) Free(tt sealtasks.SealTaskType, schedID uuid.UUID) { tc.lk.Lock() defer tc.lk.Unlock() - tc.taskCounters[tt]-- + m := tc.getUnlocked(tt) + if m[schedID] <= 1 { + delete(m, schedID) + } else { + m[schedID]-- + } } -func (tc *taskCounter) Get(tt sealtasks.SealTaskType) int { +func (tc *taskCounter) getUnlocked(tt sealtasks.SealTaskType) map[uuid.UUID]int { + if tc.taskCounters[tt] == nil { + tc.taskCounters[tt] = make(map[uuid.UUID]int) + } + + return tc.taskCounters[tt] +} + +func (tc *taskCounter) Get(tt sealtasks.SealTaskType) map[uuid.UUID]int { tc.lk.Lock() defer tc.lk.Unlock() - return tc.taskCounters[tt] + + return tc.getUnlocked(tt) } func (tc *taskCounter) Sum() int { @@ -56,7 +72,7 @@ func (tc *taskCounter) Sum() int { defer tc.lk.Unlock() sum := 0 for _, v := range tc.taskCounters { - sum += v + sum += len(v) } return sum } @@ -64,8 +80,8 @@ func (tc *taskCounter) Sum() int { func (tc *taskCounter) ForEach(cb func(tt sealtasks.SealTaskType, count int)) { tc.lk.Lock() defer tc.lk.Unlock() - for tt, count := range tc.taskCounters { - cb(tt, count) + for tt, v := range tc.taskCounters { + cb(tt, len(v)) } } @@ -75,8 +91,8 @@ func NewActiveResources(tc *taskCounter) *ActiveResources { } } -func (a *ActiveResources) withResources(id storiface.WorkerID, wr storiface.WorkerInfo, tt sealtasks.SealTaskType, r storiface.Resources, locker sync.Locker, cb func() error) error { - for !a.CanHandleRequest(tt, r, id, "withResources", wr) { +func (a *ActiveResources) withResources(schedID uuid.UUID, id storiface.WorkerID, wr storiface.WorkerInfo, tt sealtasks.SealTaskType, r storiface.Resources, locker sync.Locker, cb func() error) error { + for !a.CanHandleRequest(schedID, tt, r, id, "withResources", wr) { if a.cond == nil { a.cond = sync.NewCond(locker) } @@ -85,11 +101,11 @@ func (a *ActiveResources) withResources(id storiface.WorkerID, wr storiface.Work a.waiting-- } - a.Add(tt, wr.Resources, r) + a.Add(schedID, tt, wr.Resources, r) err := cb() - a.Free(tt, wr.Resources, r) + a.Free(schedID, tt, wr.Resources, r) return err } @@ -100,7 +116,7 @@ func (a *ActiveResources) hasWorkWaiting() bool { } // add task resources to ActiveResources and return utilization difference -func (a *ActiveResources) Add(tt sealtasks.SealTaskType, wr storiface.WorkerResources, r storiface.Resources) float64 { +func (a *ActiveResources) Add(schedID uuid.UUID, tt sealtasks.SealTaskType, wr storiface.WorkerResources, r storiface.Resources) float64 { startUtil := a.utilization(wr) if r.GPUUtilization > 0 { @@ -109,19 +125,19 @@ func (a *ActiveResources) Add(tt sealtasks.SealTaskType, wr storiface.WorkerReso a.cpuUse += r.Threads(wr.CPUs, len(wr.GPUs)) a.memUsedMin += r.MinMemory a.memUsedMax += r.MaxMemory - a.taskCounters.Add(tt) + a.taskCounters.Add(tt, schedID) return a.utilization(wr) - startUtil } -func (a *ActiveResources) Free(tt sealtasks.SealTaskType, wr storiface.WorkerResources, r storiface.Resources) { +func (a *ActiveResources) Free(schedID uuid.UUID, tt sealtasks.SealTaskType, wr storiface.WorkerResources, r storiface.Resources) { if r.GPUUtilization > 0 { a.gpuUsed -= r.GPUUtilization } a.cpuUse -= r.Threads(wr.CPUs, len(wr.GPUs)) a.memUsedMin -= r.MinMemory a.memUsedMax -= r.MaxMemory - a.taskCounters.Free(tt) + a.taskCounters.Free(tt, schedID) if a.cond != nil { a.cond.Broadcast() @@ -130,9 +146,10 @@ func (a *ActiveResources) Free(tt sealtasks.SealTaskType, wr storiface.WorkerRes // CanHandleRequest evaluates if the worker has enough available resources to // handle the request. -func (a *ActiveResources) CanHandleRequest(tt sealtasks.SealTaskType, needRes storiface.Resources, wid storiface.WorkerID, caller string, info storiface.WorkerInfo) bool { +func (a *ActiveResources) CanHandleRequest(schedID uuid.UUID, tt sealtasks.SealTaskType, needRes storiface.Resources, wid storiface.WorkerID, caller string, info storiface.WorkerInfo) bool { if needRes.MaxConcurrent > 0 { - if a.taskCounters.Get(tt) >= needRes.MaxConcurrent { + tasks := a.taskCounters.Get(tt) + if len(tasks) >= needRes.MaxConcurrent && (schedID == uuid.UUID{} || tasks[schedID] == 0) { log.Debugf("sched: not scheduling on worker %s for %s; at task limit tt=%s, curcount=%d", wid, caller, tt, a.taskCounters.Get(tt)) return false } @@ -226,7 +243,7 @@ func (a *ActiveResources) taskCount(tt *sealtasks.SealTaskType) int { return a.taskCounters.Sum() } - return a.taskCounters.Get(*tt) + return len(a.taskCounters.Get(*tt)) } func (wh *WorkerHandle) Utilization() float64 { diff --git a/storage/sealer/sched_test.go b/storage/sealer/sched_test.go index 07731e934c6..2e2b05ab2c3 100644 --- a/storage/sealer/sched_test.go +++ b/storage/sealer/sched_test.go @@ -698,7 +698,7 @@ func TestWindowCompact(t *testing.T) { TaskType: task, Sector: storiface.SectorRef{ProofType: spt}, }) - window.Allocated.Add(task.SealTask(spt), wh.Info.Resources, storiface.ResourceTable[task][spt]) + window.Allocated.Add(uuid.UUID{}, task.SealTask(spt), wh.Info.Resources, storiface.ResourceTable[task][spt]) } wh.activeWindows = append(wh.activeWindows, window) @@ -717,7 +717,7 @@ func TestWindowCompact(t *testing.T) { for ti, task := range tasks { require.Equal(t, task, wh.activeWindows[wi].Todo[ti].TaskType, "%d, %d", wi, ti) - expectRes.Add(task.SealTask(spt), wh.Info.Resources, storiface.ResourceTable[task][spt]) + expectRes.Add(uuid.UUID{}, task.SealTask(spt), wh.Info.Resources, storiface.ResourceTable[task][spt]) } require.Equal(t, expectRes.cpuUse, wh.activeWindows[wi].Allocated.cpuUse, "%d", wi) diff --git a/storage/sealer/sched_worker.go b/storage/sealer/sched_worker.go index b6efc851ac5..35acd755d0e 100644 --- a/storage/sealer/sched_worker.go +++ b/storage/sealer/sched_worker.go @@ -294,14 +294,14 @@ func (sw *schedWorker) workerCompactWindows() { for ti, todo := range window.Todo { needRes := worker.Info.Resources.ResourceSpec(todo.Sector.ProofType, todo.TaskType) - if !lower.Allocated.CanHandleRequest(todo.SealTask(), needRes, sw.wid, "compactWindows", worker.Info) { + if !lower.Allocated.CanHandleRequest(todo.SchedId, todo.SealTask(), needRes, sw.wid, "compactWindows", worker.Info) { continue } moved = append(moved, ti) lower.Todo = append(lower.Todo, todo) - lower.Allocated.Add(todo.SealTask(), worker.Info.Resources, needRes) - window.Allocated.Free(todo.SealTask(), worker.Info.Resources, needRes) + lower.Allocated.Add(todo.SchedId, todo.SealTask(), worker.Info.Resources, needRes) + window.Allocated.Free(todo.SchedId, todo.SealTask(), worker.Info.Resources, needRes) } if len(moved) > 0 { @@ -355,7 +355,7 @@ assignLoop: worker.lk.Lock() for t, todo := range firstWindow.Todo { needResPrep := worker.Info.Resources.PrepResourceSpec(todo.Sector.ProofType, todo.TaskType, todo.prepare.PrepType) - if worker.preparing.CanHandleRequest(todo.PrepSealTask(), needResPrep, sw.wid, "startPreparing", worker.Info) { + if worker.preparing.CanHandleRequest(todo.SchedId, todo.PrepSealTask(), needResPrep, sw.wid, "startPreparing", worker.Info) { tidx = t break } @@ -416,7 +416,7 @@ assignLoop: } needRes := worker.Info.Resources.ResourceSpec(todo.Sector.ProofType, todo.TaskType) - if worker.active.CanHandleRequest(todo.SealTask(), needRes, sw.wid, "startPreparing", worker.Info) { + if worker.active.CanHandleRequest(todo.SchedId, todo.SealTask(), needRes, sw.wid, "startPreparing", worker.Info) { tidx = t break } @@ -457,7 +457,7 @@ func (sw *schedWorker) startProcessingTask(req *WorkerRequest) error { needResPrep := w.Info.Resources.PrepResourceSpec(req.Sector.ProofType, req.TaskType, req.prepare.PrepType) w.lk.Lock() - w.preparing.Add(req.PrepSealTask(), w.Info.Resources, needResPrep) + w.preparing.Add(req.SchedId, req.PrepSealTask(), w.Info.Resources, needResPrep) w.lk.Unlock() go func() { @@ -468,7 +468,7 @@ func (sw *schedWorker) startProcessingTask(req *WorkerRequest) error { w.lk.Lock() if err != nil { - w.preparing.Free(req.PrepSealTask(), w.Info.Resources, needResPrep) + w.preparing.Free(req.SchedId, req.PrepSealTask(), w.Info.Resources, needResPrep) w.lk.Unlock() select { @@ -497,11 +497,12 @@ func (sw *schedWorker) startProcessingTask(req *WorkerRequest) error { }() // wait (if needed) for resources in the 'active' window - err = w.active.withResources(sw.wid, w.Info, req.SealTask(), needRes, &w.lk, func() error { - w.preparing.Free(req.PrepSealTask(), w.Info.Resources, needResPrep) + err = w.active.withResources(req.SchedId, sw.wid, w.Info, req.SealTask(), needRes, &w.lk, func() error { + w.preparing.Free(req.SchedId, req.PrepSealTask(), w.Info.Resources, needResPrep) w.lk.Unlock() defer w.lk.Lock() // we MUST return locked from this function + // make sure the worker loop sees that the prepare task has finished select { case sw.taskDone <- struct{}{}: case <-sh.closing: @@ -525,6 +526,12 @@ func (sw *schedWorker) startProcessingTask(req *WorkerRequest) error { w.lk.Unlock() + // make sure the worker loop sees that the task has finished + select { + case sw.taskDone <- struct{}{}: + default: // there is a notification pending already + } + // This error should always be nil, since nothing is setting it, but just to be safe: if err != nil { log.Errorf("error executing worker (withResources): %+v", err) @@ -539,7 +546,7 @@ func (sw *schedWorker) startProcessingReadyTask(req *WorkerRequest) error { needRes := w.Info.Resources.ResourceSpec(req.Sector.ProofType, req.TaskType) - w.active.Add(req.SealTask(), w.Info.Resources, needRes) + w.active.Add(req.SchedId, req.SealTask(), w.Info.Resources, needRes) go func() { // Do the work! @@ -557,7 +564,7 @@ func (sw *schedWorker) startProcessingReadyTask(req *WorkerRequest) error { w.lk.Lock() - w.active.Free(req.SealTask(), w.Info.Resources, needRes) + w.active.Free(req.SchedId, req.SealTask(), w.Info.Resources, needRes) select { case sw.taskDone <- struct{}{}: From 67df60f26f68fe6274a801b08b303e7b98620a16 Mon Sep 17 00:00:00 2001 From: Shrenuj Bansal Date: Wed, 10 May 2023 16:14:42 -0400 Subject: [PATCH 225/243] Changelog and build version for 1.23.1-rc2 --- CHANGELOG.md | 3 +++ build/openrpc/full.json.gz | Bin 33878 -> 33882 bytes build/openrpc/gateway.json.gz | Bin 9535 -> 9539 bytes build/openrpc/miner.json.gz | Bin 15941 -> 15944 bytes build/openrpc/worker.json.gz | Bin 5242 -> 5245 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 +- 9 files changed, 7 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4657fcd8c25..6f383f1c8a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,9 @@ # v1.23.1 / 2023-05-03 # What's changed + - fix: sched: Address GET_32G_MAX_CONCURRENT regression + - fix: cli: Hide legacy markets cmds + - fix: ci: Debugging m1 build - Update build version for release/v1.23.1 - Disable lotus markets by default (#10809) ([filecoin-project/lotus#10809](https://github.com/filecoin-project/lotus/pull/10809)) - perf: mempool: lower priority optimizations (#10693) ([filecoin-project/lotus#10693](https://github.com/filecoin-project/lotus/pull/10693)) diff --git a/build/openrpc/full.json.gz b/build/openrpc/full.json.gz index cb20e40cdc0013936788641a16f75cefa4d8de98..98df5d36bd3f8a3053a10576f9d0431e85f97ebc 100644 GIT binary patch literal 33882 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 literal 33878 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 diff --git a/build/openrpc/gateway.json.gz b/build/openrpc/gateway.json.gz index 938060588cfaa440fb62dfbe68f4afd35cc344f4..1857fae869bcadfe7a1fc01359a4d7474412d355 100644 GIT binary patch delta 9473 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 P=jRRC7{)mT6vY1lK+l#X delta 9447 zcmVs3ZazWZ z2UFY!;UO1OSpWR<&(!&DPZqYJz3{bHX8^-Hu)4_NQn?BGd9JN3K^2VXB-*YPkkg9i7(|FJezkYBjnV5%>_+xOq?fByML@96$wJ_p`P zAL*ZG(1RNApk_N*TS2S=d!-r9d=72wYZJ%Q@C^AH-Sr*4qr1QZbGlaxFWCL@>b2|m zh-@x3%*fF7@y||kpW2Pb7 zLi0i7 zdN7zi`lkEXL-thvao^EB`0oOF(9}oRTfktmTn`4*Hucd2SUwE;dC*^2IM`_Rjb4>g zOy+gLXH!e8@yPO{9>MRwLt-BGJNjowAL)~BuRj>-9sRxM%=zCJj%`5N@NZx(f*E%8 zj(!IGk7$mtJ~|{n-a~i{-1z0C2j^%pXAeFRl+{PQj(*9IQj^XU7k_&4i;Tyv38%N- z01XYthj)0k@~6AYo2C2j#O~isj9+{6+x~3s+~B*hxx4|(p|9TuTYC)+_a?D!_9GbN z8EipEzeZCVP~Pxc|4cBn-_cJ126?03H~s~Vk=Y=T1rUL067~kHOV4qkhf(lp2{m`E9-N_Ptx zK=rcjbGow!!1KV0P!2(L4$U5?Xe^cXna`EpgcQfEM|2#MR)3_bNOgTuO-Mlrl%jI7 z?)H!ebtYWRZSG@rlQj`jH*=l^$}qK9P8=hLZ=E4LTZ}ai&K(Rj3)we%M_+iBKGJ6x zyZ-3Sn<;6sV>t6S6K4Irc0DJU*BdLaBHxTHXZ&Ukdpe(;3!B-Ad)|DPerCdr*$?p$x2v@vOUmr-Xh?)?Nht zS<^<*Kc24gjy${y*lzDg0%41oQ~X+lJ3RwefSl#5bbo23OM6dF&Fu9K%Z2VG@Su&? zJ*!AUsTRTCen?2)B=CG8{K|%RG1QlJ1af8#N-it8Tq>6%e9LSv|E5|RQ)3}3_v)>% z^UOT{_dkAgLZ90=DPITMbUF85Jx|@5LzQ_n zZ@#oR1O693nDSvqKfMh;?3Lr-rsQRP`H`H99B+>oFKGSI9S~_8Ze=aI>=}raVXsQ8 z;8}fPf@eh{g!du;0aqQ(?Wdnn@$vxz^LPeqQGfFY&&owf46mjDA!`cO5X`wvq9e6^ zqMWA@H9Moex&wcPJ$pE~*`E#;wg=5$`;(TLzS~Fcayj#EjoB^K z3G>;&5kS0YeYI)piNctA@&sVwt3ai)6$5IF5^{u z27iKLw_h$O3JwbeMFs6GC@kt6+R~Pd9$H0nZkr9Ec$)Nak~-?%9Ou{@h;WW~6AV4J zLu)Y+4p0$40b@i*KXn~rR-k*AA0sl|l3*^#V-5Kl(4rhu@c$hDti>j2*XlE|vEx{ke2)UlXLfWeW!YUcy|$U7>PV|)M_K`bW_Ff6tmZhu zNZf3Keh}D?oM#;FmGKM+%z7LH0<(#vg8)u*dJ&tIN;G&5(y-qxORc$F;JVU~c7H9B zhGVkzJLC(zr089U-4s$jeoLZuG^w`9Kz={OFu52@0}QOq`!5z&miM!VoJ5Xn1j~Bl z_}?#!`MrX`tV;l_uSks4c!jG^uNl13)9V0`{IPXB)3%X?QVmi`JC&xD_`P(J`eeW$pCdOrZ_E0E^%DyZ!F#Uhj3k|G9fG>i0*({_F1E zsM}2z)B4fL)aMGQ)de`%_{&5X!_KSXUp}-ylr`#kB&YE%X12}-|gsgWOKjL z!9Y(dKB&J}5Fd1kXT<8c$baA3YD(mK=My83IbV>C_l6ae$U!V2#w=J_N=(kY4aAsT zQ|5!@HrdRHR6x0U?Gve%(!qW~w96@;9fKU(yP5!kZbeom|Fwd4K19` z-qtA%P}?vliw$5FzmVr35e43)S0lr?O36QLAuwY?W(l3LDNXVzsfGsyAxr_QodIN@ zOP{XAJsVO98MKY6gMaail`8lwRKdf7K$KHFJM}S|0}RhV9p?nsTU0TuLloRF8zqe7 za@uxAp)^CK87j@Nu>eRVcQ!3|2Hk>Src*q758(&oW5-+75Ib?_BB}G!cWlo!cCNr3 zt$33XJ28X`(Gxcmlss_90MP|hwu`5f$Bv~xL~KIw5Y@tz{t@@-+KL>0h#t4@XSDimXw&OVMOboXT?QU zLUGgNz*b7v#Ab-dxNz; z_7a$~oRyn%+kY%xx}b75P}oGw52Qb*sM0~dpnP@k)ge9t7O?A~dDL0Nu#FDh3lBYn zXdxPNv(&&Zt?fr3GAHm-2k@KFNn}plpP)lqv9h&T*`Odmn2~ItFdeNv0AIQcRtA6w zOAFc&el~WTw9}4&5@2TWh@uRQgz4AdXNk47ou{#HD1Sr)gZ;h3t}%hep3xgmhQ?rg z*grTh_Q9^Xe=vZD2m6B_JnW9UU}*IA_l-Un><^87a5(Jm4|aFqrWxI!&ojEK>SlrT5(oCr5!BAelKIEn)rOZRlgx3!qbRI+;h#*A7O=2PhhxP$!Y7eB_Ph7F8~~Z4uupVnJn;w2n>f7Z-Oa zCa|@=lSLp!SQXb0cnTxlb70y zSJ~%R1b?deUt-usE+fcaPMg)6a3_^bR(Z`)5u#-;c_?Wqa*b5=-A&13BnrGS-+$c` zb4U%l#WiapV*Th$*Wz74|7|m_V9dt37F&p7S&C&Tmi1sPt5;m^m6kG9D!wa|_30)qw}; z-{iufhd)*4K37zugju-c1h(uQ{eLofyW5u2$fh`}5=T!+9Q8};zd-y6`aYP#V`Lt! zI46JoBi#~!Lc)uk>AqrDF;z>ck^vhOUggVbwMmTB#xDDDS0pA zQ#05l98r2(lsXptYBHIU^GY=ckOdm%WUigtCR|1@wo{TnS#_WJI_LzD?JjVf@y8eM zpMzyKA#VYo+V1B_7fQ28(2% zMARzM_z~Ty@bIaMj664t)pxmW$P$g=jq!z`=!Mo(ka1f z+c*SDBPo=kdDZE`H=RnIv)DFTH|m`GQ6^Wagz=4V!GqFT6RB9hqg7rAnJg!aAQf># z7Lt~B=1`xH1lNlO+JB5IhhInAPJ2~U-vdVVRr*(QV?NKt&!_tZy9J{!pB@fqT zpA3#f1+iDeWyktSaZuK0GyF4NNZYulqC{!-#-mu4wWZ#QxQ=luU8DK3*-01fRvgIN z@e_LumhSd3z>C>D7&tVXBxhzU<_im8bPLZ?MZ42Qyno?j0u9!X-r^j3jdO~L z2agESfO^w16Q#k!T)3tvI*>oNF+o|*G>LhID7hgL*aD`-MX&lDEFv$4V~dR?7Xy>{jSVBIMOkj&ykQN=1_N zgC9C(gvEnSu1Ch1$9-k)@>Z`m@E*J{c&K`bRabA#A=l%?KJgQ`Cw30D+bu1jm$lcM z3i?Zele(=v8}zrXhhw8Lf0>oG{a8%-Jf26)pOc;u7L%_L1%D`~&11p7=j*iT6&DWw zlo!S?@2trKljEdG*WE(XbGym|lLuO~02hHT8=PFQmTkLVDhnNv#g$h&3Y&Np7x0c0zoi9RYmh?{Ks6zRmEbE#AqiHH2%<{;l4OusL)!>|E1$1w>_3U{ z$lXD4eclUrw|@>f7E)qG$@qFyx-9~YR@JtcNL5IFBPw`1-2$qRd=n{4N}N48aW*V2 zjC=v_E}?BAd&(XCt|Q=zpf#y{7LBoaL07UmrIso>-Y^~S78kg^^c?IMj>WRnYS3;9 zLN&b#Wpt;YZZw-ayMngSq)G?g2wm(K7l6Blws{5aIDdv+gEX>0L;}BP+5IHV2xJ3i z2NNV3k~b$wGPx^jn-TXUil$ewhTQC072ft3+0j99jh5@hm?WUrxzIl9Tf}^R`i||n z#?BSEyCig#Vm=H28L?8$jz$yYK1txxe280H+t^QLTg9mG=%2=<%*#ex( z%#b~--nw8Uj?IQ)S&y0n>~)JPetfb0HPjNWw+Q8@^aMfr_sg}BdbIDiSm{s#S5be1 z)ZZ&empcRgsgKbdU>b!D=V&pPWdgZCig>|-d4CPvepq>HC$1ei(89MvjiUc&UeG7i!CJZVDKTSsxKf5o(Zc!55`(q0iLop;>t7u9YdaT9lhu-_BE|zJ-Cln% z+)y-G*=2;A-QvRrb~x*UPLZ47z2J>htm~w~jNO4h!=61H-0V*W3)_R{ul>o=9!`Jm z4u1ybz!?tkzjoj4BX_x+dAG*w7V6bkls9%<&NGaHjg$&`d{zOEd;NkaV2+!zo4sqq z0OtZJ47Z}+iZvuM=@t8N4Q=zI2{n?jU9{A;Ue%l2Gr*UxrE#eej7a zSs(Q}I&=1o^#9sc_kY(%GsFJR4F2`@t=`d(&rgD>rr6flHR1Hu8=#@#`0x(TR{nH% zd9!r?o!I@miScW1e%qhTof~{NHkUVGIrQ~=$w;vGEagouQI1=8jjn9}L{c004S$s# zDcpOJaW2FQ_J`l`w?2m^A}BUSHt<%bw&9q_o|2E~!@VIFWEIDTeRkwm@^_t5^_>{m zH<%hyre&p>pCFBXA{RJ>1msV@ED*`A-lzY)&iTLaKiPcUa42ci3TL&sc)s2qZ_ft5 znu*sd)ijmXCQ&#dEq|1FQsPO8r+;>drm8-HZ+-j@KSrhZmXyy!~k@O_c^>bM4BMkl-ov(dAlWQNU+wUXQ5bi^nYNF0Nv@| z`e#C<{f>S@OGITxzkf)*j{7u98&uk0O>L0tp1^$xlsaLheClnFY~hWy?-9(?1mbIs z)b&TLx&ElF>dtOK?%pRKP0t{HcMFgO#uhvR7N}!`tnb^8R{=OKp-O-OvXM*pUKVT} z?ia66F+Iifw#W3c*qhnT+$KUh8kod~X&(Xa2oN;w-?^?w3v$L-MfNBTf|W2+VrZh} zUNaU_y8H*V{i{t#8O!vsn45X#SItbQw#v4Hf+S|fyZ?rMjr%JSoqyMmYz1LZl8Qc^ z#s<~gZo= z%Q0?F79OBOk;jt9f`93;WQ_rSS|avJdd|--5^29%kjZ}m@kiuiXxDIn@qP2_epMyu z@(>zI(M?UN4W+zPVrd2)dj)Cb7lBl*Rf;a|nU9Lp`%r<5rRC)&-rOl((4Ip%;# zZ*25fKrf(ewOithd17a#`~8A!=W7fwd}kQW!e%;&6lSKnuYacsXId5Gb86fYJvbdK zDudmI z+XbUFk|fK|y^Z`#WgV%kBNb=*&{;=HhBuUA$?^Cst4qEVN(k_$$24oVk?joNJ)7i$ zPyY1EARp&+iGT31V?uq@?dVrTMCMN*y1w_Yqo3XeV(*GwP1(fuQ$~2~_&AkN(9E~x zuKCtG`VsKqdkBx{T7xHl-a?Pn%Rr9Jz4(M|=v_h^Sa`)h`i#O4dIcv7JB0k%!N8)c z(7VCFr%Pxo0+^pYK-n)tvotQP9il?tk?`vPCF^};`hSiG5`2{z@TQFDE_f#c^AL1& zJSAvh(0_dT561dvuN(a@;0f1@u>mYAqF=QU?7pV}Qv#(o3o&+Qr$ET0MM${Bqlx6) zgp@^QiAnJLSMQcs(RM;Ap2&s5j21So)owBTD?x@L<841)ELj7D zu?UH85Px$Mk@bb_rsjSoI8x2Ds)*~v1Xz4^Skj^iMYF{AB*bhDBPzT*Ulzq9-Jvuk zDzm{!Q7=7gq!q~a%+oz&VT4MEY(Gw4X@rKh!o-wm{!~r#Br-AUPLhgiG4g~xBR8O& z5a8I_h9yRGX{v4YWe*GLt`N8G2_)bB7MajHc7H4jVCVts$XVE?blu2GfQ6@hW3+P# zOcR=y2paGj{h|(V8ZnU@H609-24CqBFPmG*iD2GX^2dS|8`Iio>+HALSfQ0(cd(h# z$6JaoQ6bIExIps=F0-Kpjf7mHDDqo@C3Qhbmr$&w`ZkfW^O!}^Zb984#vl#Hgwgd- zb$>)p3_vEZBw%q=54~8-iM;vtW4yw!poBgO*!lz*@b|rd#EGZW+*xdDp=~ae-@b^0 z(h~VRCTvd3H6Y`%0*2zO*al}jxwWI$R5|=+L>|Xf@~%SQ-U$e}VrfRF^rU3UUO{me z!YR)nerI4u`bM@y=k%83*o^aGnCh%}8Q!2Ak znN|AEW8-=U-GZtfguFii?*`(UwO+Fj)~x2igR10yiPmMcJot9<3^2Vu#RU}?++rn< zQu<846>%M$`iAG+F_ZkGW8AP1cYjF>JOm?aM~5Rm>$AUuUO~|fj^kWo#{*LmDX|7$ z=Ae`XoCNRBPl}wj6jabgkSTMcHs`#}TTm;RakQTsJ2cxJZYVZHv1SRMZcG+A7hm%4 zMA&jLQ^yBZ@PY&vpmmV_UCG^-!B7Cv|VC)5*Na`^y?u3b7^cou2@p6X$2z>m-HG@x= znuZ1ZS@Kc11;-XZbA8m6%@O9mm_)a5m!%!hnN5*}5PutyKvMRdWe4GLSI+Cr;iynl zbA+4HQ_R7;P1hOACxaUC(SID(Mp;c^WL|y(M@TdhIGQCRp*3cxQm7Y4nIi2|WV_9~ zciO5=_6ureQMC!#&^xy$PCYF$?7gOCAA+e?Aj6hr7Jb;JhBeQvr!ty z{2LKf@y|Ousgiwlyqn~c3wW1(7?7>!SPZq&s6*Qy&wxFJNk!=rLVxev)JJ-+e=_*P zalW4YTK@XufB(IOAN~)!XM68_@5_IVjIYZNNAIuuAN*72?DNw5^zrtO|HBi#qm%o& z0>my1xBH50)498JuJNVkI1_rS+Cd~yh)ysV4t5Xs2M2?_gG2}5Ve<88Tb~9V96PoT zZGYjv2MY_ITEHa~7WFWfH(f)9cKSPhmmcJGbxjQk?O27Ef&X?N~&{F1^J1L?kIsBjEO%xEA@8+14G>rkeGG8MMzSuQ5Q-aME^Vyn_Q3tH?l z0n38+G=4(-2*(TTOeWE@uf#MWgM?(nTwGt7(G*Y^ZIJ8~!KWrx!OM@1b2A3UO>z<^ z(69hCK@;Fl?@W{uO2>rA)3}C4VR8+LkckEMR$7oSc0dt~<-FD)U)# zn+Wh`f-O(&{jKUf|@OF6h^>H$HQ&6R!~KtK3}cwsUEx?cFQVsTtU|Y z^)A_^T?G|O!s!iU?i?di_MS;PP?P+fO}vfFU9rm=GKL{mwGx=iHIlH7k*RsX@ILv> zK=YwBc}?4lkv*l@ZsSmwfu@EcXgk_D4u5pXiQ~{m0Xyn!y?Zw>;88Z#p zCJL4qj4@wW7#YFa*Df$_pc#!4Y}QK+*rol)bVjzM+Dpt@sF z-7%={7*ux*syhY+vPsk(gX)e!b;qE(V^G~OsO}h4cMPgK2Gt#d)lSS_S;5U~2!A|d z7O#Co~$H0*x+53y>1g1ePyrJ+RkN``AM1~zs$+emoqyqaDKBAP5jCo|MaX?!rA+47oH3fooTbMY`K)Ub zANI>?6VsseGl*+fluJO6y_uADrsAgHGVE7#WJ8a&{L?1VO(-EYF|j5!+vIv%d$;wY ziquLY!^TvX!;Rces>}!pMXP|IvuamyLlGq`flh%EbWJa*cP6YB9k8I;Xn$2^S|yw- z=uNV6l7-)LYQY8FzA`<796<7AVG}RTZG;i9E-h$IVO+Kj8W@@DaHuU->p0>KqRk`bAsH5$qNF;#L#8G) z)g+DO72wI^H_uyV9+XvJjw@CpXyf3~qTUsjd4Sf;ULblDqj{lHYk6~MPFXHkVH<;A zAD6zY6ix|+Gffm(O*5y3z}N3StBdCjhhgb@5u-eCDr?uiJ5aon`D4!8hjn z9X4^vo;q|_hwkdoT^+isLw9xPUaT(e6;yq!SyidHlG01<#kbGx+CL;`#Xf%5RANHjhFwiPRD zo%<<|v+%oL;F5}7D*s8EE)NPy4t|B>0X-e6DH<~X@`WVjcQ~a~mzFm`Rh8MuwKP3#g1lkE~z(4;92!FO^vuoxews8sgnV`Eg z#GiWLo$LNa+2zJ@sLPPWKY8T;=YLhu#Ve=((=~a#CV&0)m(er4&3X;p2jj>vdWHjC zxR!(5xO~dy4|@*vu$UVEHJ{QiJ;O!b#%6qF{Mk+6mzme>`$X5Cxk78m;h}rDc5KA^ zF&6#7a5Of)^ndv00u5-NyVOB0!@R`;+8*i|?7>02Ii3F(naud|$npDogI#19N6g(I zj*R0X6EglpeD#>~tY@HmIb|o*^f)rfF?_&mwV4AKttmsm#^lE685`F&j*Jy!j(7C> z^-?t1H|hHI!l$=A$E6&v*EXl!*K?cBU$3FZkoy`s_qOv42P3(&&FPFhgtcMuBGomVFE#K-Yy2{CD2yx&xj&HQ$1Eb4qOlNvvghqVk$% zm$@^1vxLwQ=*Jf(JrAcP((@9D|HofZbI}>W$dQ{p;0!=oQ-fDhq&tJ%*Z#RCOkzs2V@SU)7=J=ws*=rmj@%5G-^62pJpe)2BsT2P zcr-aY7|ll0*_UW=e#`HgIY<4266X99vz6t-ySLDW#B9N#DL{p6I>-B0^ahbPYq}xa zxXLxbxqDx&Lb6(luD~83T}a!yC~% z@qbe?<~6x=kck%$0E$@w&;!>-|Jq1^TWoAQfVmH@R@eh_zXjxQIBBnu=fNcc$EPRP z1QH8a6nTJ92JRqcU_srm3A#t-hOhE9=}O!r*H%s3^q)@B_;iuMJ9=IrTLd8!22L$R z7+&BUIdhEpayTyX5Mg{pJY+BSWScoAOMl4}e2L}KBMaaV+CDgCTtqFXyG9n6KL7~+ zkX!8@fZ&)~$ONzMR@httOf1YtELMGEGi>s=dk!>jkQMgA=k6!K2?2tSywwVNtNokStV!}(WeA~?G9tkv{k1V>XgA9*)u?fjSC|LtrZ=-T=3e7!&qi_v(2M}M1# z>1Kn*!`rd(7?YYFb1_+d=TdVp%a@vO5kU)VV(30}Av=q~bt*xNyXjbtmX4=bnwH=f z2-Xz>^QaY|3r_e#sH(_ZEn>BZ)gpGcMC|Y|U&J1-ka=_N(pxyUn@QCKgfc;TftHx# zhpcmY$K+v>KDIZm$F_!>K9t=lZ%R%xF2B0bM1ulXXu&H??B)0<@XU_A)%IyCf^J)4W zzBK3HXxuLl^K|WO7&?Ir=9vsiay|i~OwfNK4zw&}onvGo=?`>?NqjLe$nQ1R|dZTOUs>-mFdrYF8shrxqi(L-0Vp(lDoR;S{osTmv_yozO;z`UlUiqB({Pa9a(?TzA6^_XRF zHCNKxyMO-ENUT^B1Aic+?%NM>lsm*oSZ&dQ8T1Dom@wnK0i;Ch)&?=`HyuHq!BZ%r zD@CD#(;z7UqYt!5W|M}-Xjpm{z){Ba$iKFq&SR)Gy z%;%Uu_u(Besl}xOmz^G(j$_dwWjny8EV83k&~#X81^tPMMSt)3ytgrhA}B@n&;}DW z9SfU>&#t$Bg`G>penk($j1dP2`LB`RT?|7lh#Aqc&o)Qb)%Qu%d z*m3w6K2LfBAu@$1_QOGIsfwNrqGd8@#9|Xw zY_Q4>azaD>RZvobu~kxynoRk*<-#f%_rb!~3(L&&VSlGb9Z*oSF+7{)P%HHGh1nJd zqXOH)MUbJl(7f3=&CH4zfFg^+KdqCdL9S&G1CnnP)KKO#C>qJLf0e=g%E_{0k(ond?J=*Pexl{54ambFj3{M+Z{HmM`sw^XZ{J+L`_K8Ct6u;jx7ej*&G*r_(8c0()I$s~3Ou?-z=3Sl z6KCtZuWQ0PVa)al?Izp)&Y62Q&S&luxbzrx(SL{IMywruo}ZZW`Ag8^as^%MLaamC z1|R}hYWe3tt!3^f@XqJ`7$)w9cwhuAdbY)ek~@+w-s|n zmSZ61w^FhSg+tDm!zG*|IQ9_y8<}hN^#H@0(a7=^-U?5L2XOv%jK|aI#9C~=x}(1r z-hX($hSt=&A7Q+-{x*8X<;FCT7pd@fd{K=f<9gHYk4&+|;!o7V#`9rj4tsk>;8)DJ z4g;*RNiRQu4nHi&#|!A?lwgLTjlW9DX~pv?SdBG%0lc+=|5kW3w91bJ^b4y?9c?Y??tDpn%=9(}H7SO~tW{{H{WXJ_B zVlGCv(DuZauVq}lBXigIp8*vEk8WHOd17QDrjs+K92xciCi3`&e_~QDUxYm(dw&XZ+XseF@Ef>(ypsq+6f?Y_EmZ)E;%N@p*!I5KlZQ5DFbtxnl~QU5Qgpa1T8SZ-hWEap0+x_ zUv)Gd6y#6^a`p^+Ob|%3(gs;DN>*r9kWv<`x-D8eTq$=9<=QERR}f#1$%49T$vkjQ zHw-}A4&>qup?8EcyO0r)wF-VG_5*;o5Zhu4Nr~9oMmtf?&ui%3Am&9{sBu|c} zYw6eiXh^0JjEcijzm`mucPBGwBtmIc-@<#hMpI!=$ih?&dd!Avh zSpswTbx|DH&wn39I1_JIsTVn%(nyRLuzE2VF)X*D^%AJ%9Kk2ws#OedsY5+zr;8VL zQy(R&nB|ve3Ty;{qEFn%Hb#UQM?tB5k@t7YEsFTFGdM@K9LptxZYl}&9kG%Rd@4G3 zv5C%+>%TPmG3dwC7NOG|6Y)YKl6o9Jv-b<{XS+Xx9)J7fSezMCXxPwOB_9P-aBM@o zHjer&YQDr5uTFxkKjC#R?a0ngT?o-Ksic4UOJ28=<~6utXT`06mv+hE{r%C zDkTNyazx!;lMGyvi<87u@=pyK&2p4lRiiY6Dt#4WQkC{84sKE$^yP>9OvXjwK3q#m z9>vyMn}3=&b}O?W1FFbIP^b%^53tl&Xi^ov5brHn4MkQOA!oJkc*rWTs2j>A*x_^;+#+zz}Bz8dKwp*-U**gCI@9vK3Q7m z#+6(Sw_6se_9wSykQDNoJzfrb%*x`3;*`*7RDVVV;I0@%IXI&QhvcKLS>)xg$YEL3 zQic?|S0E}OAkEohtLh^Ub@2 zEFvpL4Bf`GQ~>UXP1HkMnlniO`kF;v4vP%SV!kdB>(E=B2d(&alCb(fB}XVat?RQv z9)Gr5r+w|1-et$MmKkT0>8K#wPLydz?i)hrhM4G$-NI#204w)^t58A|0BI{9igC-A z3`JNZU|Vsefug!KOCiw3qrC|E0P;i)PPDkoXToCs?^GX zbxf%~e#JU04~2kAlN2hd;Xpvpoghwd2Y)@_A`{_TWc99zCn|G2022~m(K}*OXaUT; zpp2sqLH+(iP_=|Pn@lGKp&l|E@fZ>l*(Z>}D;E-PkwbPWVx|aKk*u*GgUoNUA2dEa z5y9yWLhio!dsMaMEb6wcR4N%x{QA9o5mktkDIRgA5;bUp@rSkN{X!Dq^V1W-U4N9V zkSk`TGpSTC{ad>bXS9|3aC|~hiy|KkPe#8|`satQ_g{bg_y66ZU;dXlAEs|S_tU@L znt$H^^7j4Z@E7kL{cv^femuMV^?%u-akP2qWpCPd@MN;@pdcE?55OLOq7f zX1jq>h(%J+C*YsJtDkeeDvO0H?SC#``Vnl`34^N|P_cB#w=oie(gUKL*3yN}%6lMYXX0>o1xX$k=g~@LNCk~lZfw2~MlvSL07UTk?+x;p z2;F|{1wVtbeu*TVo^k3O%N0CcK|+x2w+^!k0fN?|MmHs@tdU1RQD%2Xc?FJjg~gZset)nh z{&&^yABq3{+vpj7pm}+tGk?02Se(&iwjTu$k6#|YJU;e}IJ;zW&p12s_0YVbc|-F? znm20R#-FEVnq6F%;Fvm>_MNDul*A}6r)X@9zlsaT#uK$tOjHBm7+2NHol0;|HZLg7 zOL=cY2Fe+JjEC|o>iXPJ=eG3lqej#aXPmkjR0)>M8IRONh#g;!asp;7-RQ7p%7B?em5^mDuP#RMj z)vRTs#jzI0S{!R}ti|z@i{rc;1{h`*mio{1%MG_`wL+OjeoGO0NtTKjJ$lAp)lAxvgjlXlj1x|c z1(u}M=o)x*J|qFDKT9HJtSm)Jqexx zuT)W}sC%_?M1S#7*Oj1&32G?iUWkYo7G%sw9`S2TB4{@*rl&b=@*ua#1|lRg#yflkF6mj~5{2m0_pA09l};eWx4E#-MIF35F^Db~h0jwu)| z3w)=bZKce27GquPTNnG*#lF)Jbg^$;>{}Q6{vnEeA502z%p+ZSN=(XC5o=Lds1RC_ z)sQ@P7O>PUh*Te?wnRZk4eg5T=(c2G6ri1BZ4|fEmPbdHhkRZ$uAmoA?J>5xo6$Fx z^?pMvuYXgZ=bO2Wy;Y|$gCLHEUZjvH8h-Cg! z_`?{qAcN(ta?Hs0(2EXc1A;E}u8EbqKpt{$xt0(_cYzj0ES=^mZViPWUQo(9=E`mf zD9-1@anHDbccLzpd?w;@-rPcL!?}&#Q#UhZTz?*L!7~|iDIr`xn38fYUdly#D#o*0 zNmJ4ik{R;G(P7UxC3CtV*72K*^ddLkf22f0J!89IRr0)t(S{`hB}YQ;egI9H&g%OZ zp{?rHO38`5=}ifTy4b`iD`$nuT)#=_m)9T&8Oz_R6@I68#(HP`9qf!#hFZ-vN!U*H zzJK*~e;r>_Om%OSc9IHQ=?mXRsfSk5?okSsPb$@IxE7+&edY6tgb}jiiBCU(+hbZ< z_=<1kJGi)_2w1VxgMat!QO(Pta!W<%nb(%gjt?->ZaL%T+tlF23!#__#Fi&A)TxVrmJjiyan;w#L!4AO5Fi2)n$Kxy)Ggv>WX*3FX@J<%S+Ym{B*mzDu?Sei;4&VN%k zc1spoy#nurV7kft$j5IhCd!W%&p+!IT!JWP{sMWN2|KEsoCEDiN+;*^+YMpn4@k9m zavoZcF}|Dh0HeT_?{y_*T}fG2QhpL)I4I4`zCg@<@O4q+t19wAHNUMQA6{4DEraFO z?_JXQmij7*=M@6Of{Q&RPh7Sw;(v2lRK!OxJfBBz|1Dmg5+;nD)PoU#bG(1Nf|!WC zUJp29@#q{{7P7*E4mEBOaf~^3Ta_a5SyR*ZeWS`V)ogoN zW{~V6mRb8FNpM@U`Gl{t1G{gWJAf1$Y;hE$VF!ble7A$pD&%Z38AD1zz|KMgcO3gN9Al)8w|OE2Q1Mb6n|HZH2mfe9wdw|w;*$(iu;g7RU~ zhPlGdC1Rz=*&(2AiiO4T+D?VA_{g$SCG}QCoE&4zGmbc|^$Zbm7XJDL;pK`MN7cup zFX@b9HYrHz78Z{80kPxLlYiVm0Ts$k1F3Yey!zP@pD^m8egC5}icWXX30>!4tz9lZ zn#(1J5VHa(w~dk*>O3e$2(vS4iWGz3t|0sO#lk~O+CzyHnO^6?Ikm54X&y+B9&5AE zl$sI8&4V9e>(DG3rJzX43(-K+f~4V442wV7>!1)!JuMV|rlg0$)_>hXu=zl~YClWDUpbE>Vo0pY7GCM3tABiYh{?(F==^ti4TFBa-!sCRBQ=fosDIQ$rFpELf&Ib^*uz0V zbod1_=`C{KL%z^W*d`93$S3moOGF!T2~^c!9D3&}Fok-tOC*7X2?>}Zu;CJ(QD7m{ zhAslGY=e;p){tzVZ9fR9?;&D&U@dEzc-Mqc5%Dc5H329@ga`w-5QEtE09^#2kOe$3 zWuB1pL5?#2`hWQJgf|SJijG#4@TP2quE`qSpF@`k4)V^C>kr_;FR20+E?o;iCM648 zPcD=>eXlz;5IZM0ElB-J@N9m%rv>?oQv zz6E+zXxzwBYexS-{&z3u|APM-H7z0Orwe_TD)O?@WLm@fQweY|?HOxKV!zVf!f5O} zW4^V&Sgb7WH^D5b1QIJe(Eaggy^yrVr;(~Bo*m43HjX(@AgLaiQwteK{lbEmbx)rK zIHfC03xA8*>0r4QI&@T!;vv<>31l$OUQ<`0K7{AcpnligD9~se)%r6+_D|~G@XZ5< z(AgeA>s75+wO-YFRqNFq)~n-!ycb`a&^uyNXtnUR=K-pvWt-{LM1LKr*7O*jGGoy4T+4GU&$T??C3!w8$P<$y>;odmh0GUWzoLitE?tZ337U)c zLVuXsm-2;OcO&r2My6%Imi=1xYuUd$vVYp|7o-+SY4(xcB43KPkoGYs+n4XL&bpE4 zN$j-@*D_qoa4o}kNrn#!vP-4>I(O-9(yg2oIO?)I2U;ze9zFDd`~eBJZFufdy6~C@ zp61}yjrh+WuukaEs$geSK{-#UrT1m8U4Kla>UEXZreis^PE*in!@|_>e4(7>iZ(iJ z*iNTOAcFn6)M&$PX|$o%Xj-G~v_^|@l-gA#Z|}GY+m+0Gt@X6lQ_*^(((L*R1Y4)q zhcIesxH+YEb4?cZ*^T~+4?$}!t+jSWYdy_XnOc`+DL4-_TuXDLzkilEk3ul_yCd|0Fj;=slX&=L-6K zTAJa`3I3R_*O;}5*oZ-CsLTDYTYqWs7#7{=@Z_+x2G<&V2Q_%aIr`K}Yq6KA#SThy z|06-&CPpI$rJ)vU8IBP{plb!D!!NV~+ffCkBQCV=deORTR$9w|%jk0#b+ZYJ05#TN zsH?451gbk_mKdMbXIh`_q&`D>Pp0+S%hzj%r8ONA;=1j&ECSR}uSt1yjDM{b`Cmyy zwxHDXCQU1~9aU-(|0vQ)*JpWr_Mh==u<5~|J{8oudk1y*U{G330YjnApdk>yqoklV|4JH5Xclk}&sPxY2 zG{lmNt5aa>U0iZ;4dySqncYEp?iavm#)U}oF1qh<+xEn#Q=Y9-^{7o?6UYc z099M5#3YJ?YikXqP%FL+UK>H$2@6vk_OP-yX|s z^EA|arX|>}V~FRVs(;3{0t0KacDQ{lfrY%vDaP8^$ORsQtUH!{n#}q<{)jD62CL*d z1w*LVbde}E^GNI{qfHBP6{SHWOx91%(uGztH)a-05wpQL_B)l5w^T)@fwotWIcSzK zS_T0V5-{ig*fz2NCBPGHV*6gP-9^B0sfj!f&;^(y&W#%h!G8jb0w}f{n03aI=~*r2 zfF|lN#x;jua^Bd6PhI}>0Hq_;dXZ zHy9Tk{#%=xH()~;w!xaZ=$Zt*1yV+SThC!uB_=Y9zBO4f=nuzD#f9QwyMOaZYi-~D z#5lnsik_JB&D_S`>KJ!;_AgL4F^(qV(NO;4W2GLnd4C@Q%VX(<>88JkjE)9<-sK<2 z#S4rq-gPjS$2LZU8AryVKNyb2M$b5R=`FU98-I2N=g5{5cFCa2-Z|7FO7Z-`Z2X_$dJo15icYnsmK1TUHay-2x6%0|FLKMijWgUEeJBI z=M4+iN`J+(lwQd?$4~z3SA3LmN99dcT?o;fsSMFy|GJ&TX}Vs>NsM+?@QY}+Hi|j) zSQkc|Bvr7@XOFtQCb>C0l?=9WId04>Gu0r~dIVE%O7c)mvT$~Iy_Af8iom2iOF3>D zWmHe7$*)_ZXe4Q%=3c*=I?apdrNjVufqvLAqlh{X+4`5vrg zV1=~f12Y>gmo8dzdI1aS`c3z*$%TKk&3BvtFQSj3NB+S$=z2T%JdQ4&t_&HJHdQ#; z4u9BP*yO`eZy?O__Ym923g3J)2E&nYWSEc-5t@SAfQz67_%=lZr+CZm>_!=mlZmTI zxbEvH4hb$3Hj^_>2Qp4;=ZWw4e=du;iD?Xf66zTmvEI8q`%Ip1=1I;M7FTdskRccs z2Iq)aGJd0-IQ8Ka3Dn5vc6y>JM17E@Vt+Iax3TDqaIY4g0a8Ik4+J*$7@!N__-}k6 z8u~gZm?I$u18*lm_bd{pfxzrQNX924M=+*dD+$AbsE5X)XS?A6FP4>u{eq+@xt<>o zdw1_3lX2M%?URiQeTr$y)CCap*_+`t+7Fskb9Vvk5jMPc*$;cZliuf&YjOuM^91Ltsf&PxpzVPwCsoVbGJPAnk=sdg(r1A_s3BQ|D^bF2VlK8Xx z2i-exDTO|3KbsE6g%RkIdYQTMH$LJRz`LTqJBS23^)|d*3OQF>R{$TO>3(O5;#3jI0i4SNIObbl{7F#M5K(5y6= zIV6~tTF3`du85>3B+GoxNXr z@4l3kb~!znL_92w^oscVm~KeZEaV(W3bJv737d|80B_PWJnZdVkens?RFv2BooWA$_)wR852GCRjBMb(`_3IU`V#Y1L7yk!-67ToqDI z2d(O$)oR8bl}6WHAQRzM9xoq4(U$H)49-8WM+{bawtRTqxV0kWwqDyVJy6xWHU=4o zC=xDXB^-^iYIj$HK4*SDsGpiGDmov1GvcYuU1v`R>3`Z}K}B-5P6vNs>EK-YU19SE zu|I^RcO>tx2_7aPCU3(DoAPHJE)WNI(cyXrNC!kLuj!z4Avh`#s&{l4Tusm^$#IYu zFij4U@HsPc(*M#k-rYOc4}=Zc1gq5{$Hk}Q1PQ4Z5~2S%R^z$jHMqjgC1OH;bpkmJ zX6}r<6@PQdcyx2H9Bqh;tgi=)doo`BcQP7TBRU?jzsb-UV(0#T<=&dBTUjgn5+N(a z0sK*N#eB0)(vIf~D zapgi2Wm}ch2|s}h##7ACs2NGZPZwudV80+}A%9oupF6BIN3AN6DRn1{xN1!X8OlppIM9yRGKpp-TgDM6yp#zO@NVs}2S8o$C&cVTaG|&j&ATQd z-w`jD$PKSdL(sJ!wBh<=vEi(=nn1WWYQcjcjLsM^gxC%D#SyGxy{PMOuYZ}?ng*Lr z@QQB>$&#x(Mgfd=l9c!%*9+`=?57_8np_jOVRQ`{=6w7RZhA3GY<4ws{#eX;SXyNu zq{X&5V@W{ySrk8;*xEnF3MsQmXdT@Unv13qqZX%sMc$&y^sgBAK2ZW#FyEPFjlHN~ z+1wL~B(P&_0hg0L$5~bjvVRwQvdvsr7d{MVSuXNCWC0vP6RtNb?EE#d!2AI~@CR}| zQS%mpV`?E2ypqm0Oe}2rS;^~-&9KSe?m5uBK~~rcXWgHK^9_QJywwVNtNo-z=_c~A zac1vsN{Vs6H2XeehW0sEX;9tdT&1CIGv_L21WJOWI_E0zHATWgXMbJktgE)HJt$2a zmnYsW7+M18%r=+cx-rZ{)T*p+E=g3qF$<jWY5s(4rN`^Y1Im!g6YbHnJ|z zLV>qANVPJsF}O|m+Q)q>W&QK9EGyQUY^IXze>m&+`?2e6Pk&FuRxN&|?RYjhnD%>N z7QguYSXI@vNdim#yKq(WR+`74s}$b(o_Tv(8gb-t<*;eR-4a5B`*>dYOH3A2f#YR2 zB|INNYr^N@D9S4-en3nf<4A1i0zk0Dwm+ycl8 z#9U$Xh;kS-xcttifD7c<*o2=P3o_JwsSfC|cW$PD;?`?;B#YX-F&Vy1R+7mnF2hOf zPlT7Npa4vqVG=J+31%4D=QgrL_x#!Kgd-inhpzn)5PwU}+k9f!s+gP4Qpci6waWNB z)=eq%^~2Uv5#_R}Oncp|#T3!hoq&*&a&og?aFK$5t(210bwLAN&_EY7&;<>2L4(=_ z4H_x*cIBvwEYEs4gx2j=DhJeZ`=Q!ZlWOqElO$5-CaYJZgulQc$2LiM;v{ch@I&7$ zHa;lLRe$p?eaBWC$F2lXd~y>zUi^XyQ*q)-kQFC4DY%W?xgqYweq8?-z+^$A%lW#w zi7z#T+%uhQ8x?P4;CMV54Eq&FG##++OXHeZX*QWBZ5nB9wBVc=nBseu%!mQu7eM5GOiWW~Y*6 zbbloB*>rqZnqce6Eqg1YL_nw&r;!N4tTZ14{H`pA0NL_z3>{`c;@SR+VV+Txr)Jp3 z3_Ix;CEZFEd}CR%8g?U=O~NR@yta%rlWN>=_^7y^n;79L>>xW){XuMP>8v`GoJVKF@H`R zxjNs46P4%Yj;1Z|ZEVDKdJN{i&Nu!6OeR7hw(3fCmIWo%={R!kKZ^?@Azu_(>lixF z#Oy)DhrS7JIv2m*UnYP1HH2CsN zza-##Crdd4`7zAj%)I4g=w(`{q#NG3n4v8t8}2H*jfzsvC4Id`9^kAS zl1o^}ga)Ht@0NCy(hK@KuSw1V)x29*~fhsBAp2dHZyS9*6H7cG#Bh>7;E z$+-lK*-92fy%WZe@ZE~v|3B|x;UeAzG!cL<@&UNW+P@~hQHGAd)e3uHjo6A>Va4+W zwi$AL%;(UPB_Jak_H}4mc7IoryokoOcLK5%?^==@wuby@JenLHjAoX*ZWT(=RyM6xns0f3yghHzf=hb)`%<>QFU(S!6cr?qYjA>08@m4e_ie1D1fXh* z>GULJC5Z>4GMj4br#x5rD-)P>p&(YJZAe5!d3{ceOk@ zPgd}*LmT2dvgon_Ezg&h{=@l}rbU%y0@>8|AW1?{7TT&OrV?yt5?JZr=m!+JJK^5? z;h6HEs3c6BTw4b(#K5$TUxI@mmfP`ckaC~!vDdsmDR zA5OKe^as>gdYkZawtop)OrA|AhebJ2QX;&^#A+`lBPi7o$f2!X=ua7C&!_?yOY+>v zkBqHzHWgv2dN`Htq{$IgVt&+IG+BJh4yuv|Q;B0MbdNo@e@(8*=|UV+-MvT5TycK$ z5bLGDG3sBG8@tZPKjK;-c@>Jd^%mNmxYIx)CC;bzfPcazzJFiA+tm4)P35&luPjRb z!GOiXn7$HkujZ_1!_?%ECa1|$F$cj$Gs*c5yy`+%~amxbEg$GU-GHJuH z(KP`AQ*mDCiDSKRsd@nMov1iT47xBW@vuBX#Kgk4*xK;%_53W44Z)O%+Jv?(7mari z-fj$_Y0~wYihpo@0X*zG>>;*joyXW~wON(^w(DxMGVATTJ1^ZOCt;~>DL*64Y5im; zrEj5!R8CPVPEF5UEb)~;pY1%f{y}3j0lR%5T5z}-PJCWh`XsZBi9PKXCHF~I{eajR zHE$Z0VNSxxFV~Tl5u&MrF&scGF@woDP7YJI$(dS`r#2@tfUmojbGx$g}tH>w2@4EcY8s48u zfRmoF#w7ZYmKxE#?Tm>RE0N$Y*mF(K4_CZ?Iw;Sv%r%H=$V~pdiq|$NKx=sucr!KO z+0K994}awePBM1NQ(GbDOUOhg)9GY79ZmQ`>wHaz=ORTgS8%MYH5Fz7)$U>|Pw#lF#pc*h~{H_kZz!8a5B3UMiToMs$oSOB=5($lz-S zvXxQ6t|=}0(pX|#o^E(X&6}1SansF-Zu1}*jzdS`FaB_zEbv92iv1v`V&Bg&bWF_I zbUH0hJ-R@a%&KW^Q6wQ1Nz$J)F@$RQL`mo!$;b@KPZHMtbA8X#V03s0VSl_goDL^@ z7>t7JGS*tvgIGh^UCcy*w zp#-ABwozxDF>L~WorzM2Yka9xZwO^j@L*S!pn68m1%5RxyrPTC0$g>eDb%jiRO_2X zwD>-oPG{x0NPZc%#$ua)uXw_jDE+43wtwNxO1w77N|g-)7Fl{D=^MI7O(zB#X3QYnRR#whkMi6bh0-d%ntWviv`>}T%bjNG3!qe zLa}S^s$$jqeLsfHIGFZlgE{Q)O@9Y-Yi~S(lfAjwpY6d#f3g^u!zr3r$tdm;h*!|N z;nD&@`}J-AHRmaDE%Iw_e_U+HpAa+SsPA7_Z5$cH{;wAi_&kI@#sX_e)>3`iETu98{ zj0JVqkdw6q8QNobjqroI@AdHnOp|&Uy6V{F6|?Z=r|sdVe^k#N47YLy3=c-rPBDvm>*r!O zN?|t|4hO^GWPC8}C(-MV`Ui)@!DP@MDZ*exUX49|PF?WC=xIgJsUmoOa!(8Y9V7R&xGiX^3MkZ<@^833c@>?L( z#crTvNn2o+>a9xpkB5ij!|B2BaGF3a{`l#TOUwS*WOjHs8h=j?hsgzvJ$^dm8cP0c zP<<=Oza^?y-2yxd)u)xfPEmbY1?&*j#}t5nZZhar0c0LMw*pW_=;;-JDrPkmz)m68 zf$_gRY8D^Dxz)GjNnCw246>dZ^RT6vJ9}EvCmsrzE)1$-iq|&;O*Zc z`DZNt9V7p=QrIo>PiO^ti(KT`*n}I$f(+?spbnLTjt0`vK)az7bTrU&BKLhn1LY_K z9U1f_I|UsXw8McBIx_$7w!Kr>9@Tdc>a#t<>L77!TgWBUKx~tswSd+FS_^0`ptZmcrUf>{^Y2jC z(L_3$NJkUtXd)d=^rEAQ9v}V6!k%$NHn!a}-Vr#rqq4w$ZPSm~$HD&z@5Rp#&PG-; z?SJ=-WAV56lLI`a8^V03j}>LOYgA_rdQmCQ91bNcnjGA9@fbt>TlT}CRBUHe!P5n1 z^AJkBt}`Q|T&&7`eDGU_)y6Z3rmKS|iry++Bo$P>tF zO!1uJawJ$hhY(W?t9X@H9VF61rT#ZU>Od%cR%d!ts=+sVk`!V!$WF*WmG*J*S4v+U pRQt~xpD)kV8NIuUEO)hL{OG`VeEjnG{{jF2|Nm(%e>)!*0|2l<@sI!j delta 15940 zcmV-KKD)ule8qf_gnz(z>=|psR@Cy0qtD+A0@wVr_t-`z#bp1D$*B9rvRveOM$fR2 zXS˺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 diff --git a/build/openrpc/worker.json.gz b/build/openrpc/worker.json.gz index 182c07d52edea771cb27000b3f6c6c396e54c0ef..dcc7a526669f009c673bb7f29b2a0bea32b096f6 100644 GIT binary patch delta 5161 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 delta 5158 zcmV+>6xr+jDEcUngMX^DmZb%vbI0Rlp}I^G4E%D~mY^(~y>-W747WudT$OZ*ebz|}#~8zWZQXMafHd+=`e;1t^S*=HFb z&vU%@;PHmsP&@^G?~G975om8)2tIrO1lwVf8|opt^a%KcpodIx?`80-=l$l;%cbi& z9z_<*vu6K9*>cK%=PIn}teLDiOz`(NWPL-vfB!DYlD}Lmptq7vBuSQB=)pzUmrIY0 z$7FTnIzA5iK7V?h7%x}N3{XxNr>pVJIa)xbF6akbxHbyQJp0TZeZvHK(ut}liY)!M zTuj)n@miM7J;#~;v2u}gGFsot611#9Fle89j*C2sSvgZ^`$(4D*rQF!92p`ZcKS?0 zL1c*r0&IS-cOpKWcD;S#O#VhDm6DC(DByZi!tGj?Jb(0`CH9adoltLy*l@TWG9hJ8 zg2t`o-FRu4@hVXLN@I4dDw=V7D@&N7NHH>PNY)ni7QjA$Al1z4O`cmex|?Uv^WciV zoqDEb&Q{iGN33Upj>%~mXETfv^I|@ynq?JR3lpJ|Ot&nNzqBbUJCZK#o=RlhCaB^E zLcdY!3xAkFOUf|(?JjBCVje{-Lfel;M76d>B6f}1aEN^Fu&X5O6jqggLmHwKFVb~qkyf~F;kt$EZp3x# zg96v>JP_M~RyY=snQ?jXOaZ)8$%*6G!Mt$+-RpEBCMaY3x9JyO9TPGEdEI|T+}4mF zuYY6W6H76v?1-&*VN%JApwa-b(qmxD@*zl?nD5mxzlutwu_r8#y&dYSi?1;c7o`qd zoR6T5$;?OIJvPy?A5`cc`$tnn)$}7((~Z6)OaDaBLY^<3kfm*}nJ&7JGUGc>C{gbh zPOaOP8^cD9?EgZmK#M}9@u`Q&jOo~)(0^W{oHu#;m`2K|=eBt_qrQ&1u|lDBwN{WJ zL55x%88U|XWaw7{CpO}p$40qwXwQ4_E+jX_d?d=8PeQ^T#Q;3nkd5V4JUv zZ4Sl-6af=xBYb~{3{v=h;roT}ujTv4YQFFPf(Y^;jU3*; z(CQzav#7BvUsw+B@}h~Y-YK>?(V?*Vwt%9nz2$*e0ykbOZj>$_Mtc49T47)$gn}~* z5sIfcXWj4=Tfk%0`-o8HpnYUdkHRz)OlG0M5elGXdC2#X1@N}C9yp`St$$A)Z-Fc@ zSpf+C48Q%50R*Rxg-r17VUEo?z{J9=iL8#r(x%vCZ;xDP-XUvS3+t@+01^v8@QtOK zLx0{&E@eJS$4t-RbfryxLK8v#!MEmvl@9d*o#_4@TDX5ZKmOw`SI)xyd$O3K)l@g8 zSYNIN%Ox_jdqY}_RX%~}0)Np1L?0_!IimlCiRC<;JD0R3lO>Cvxx_S*M}zZvtgyX(!^zwV~b0QnXhPhO{R?C*$z+v0|G>sVow0 z`>YDEB>J$FiDkmz1}|NihH zQ1v_Px~whNh2-{@ld&e{sgtpq&BfdHC@qkMgT9$y0=?C*#B?lX&dhiEXkf%VN?FwG zwm<3x?mpY>H|ZQ-8xdCZCy6k@WVRm@-jwiMR5+IcU*Ec{(0^X6_)5%oBYX+<%@kdi z@y0spS%c{W-4%3K&|N`y^XTsDrH&c5onw^Q6qU@7iyPLqk%Q5=22`%2d`L6$`Oq1e zI-CTEWy$}{Bz>F!OIgN7*!yZLZ`7Fc|&4}Zn3xDK0OV8XKDIiO+2anP>v^bxyd>nkRXJ|5eaOE?VjDB1`KsWJY_xAMs z$`Akg68v?Y6yP>hmFxKQ_+B}F2GLM(NlDg#v;>Vrll+tsGzN~fo!(=|=iw7xOal%#ly zt_;VT+E*2$l7L6~5)LvF>W1DQ5A~5g$nQTRCE>s@G^4MK4I?F?HX0dYMIDVsO0yCU zMniRM41Wi~*yKpq?~VZsI%ojHP8-0e^9C^PxB;x?I5H4SW&#@OK|}l9!wUyRwiDI02wj!Zkqt+Y_!i z0N7jWn+AX;w7wkxXlU#jm<6;aTmy4}wuH+9fF>4w-pYQ!Ry?KQ#m1)g12n}@RAp=o z`hNqlAJ8QOcqRJ*2Qz@G`vHeCfEX-TkU|q%d8p8-Symb+Iwf2hFghjNi&`@3lyGU3 z=#+42+~|~WX#nYza4%{#sYAlWP-7aLWt)Q;s$z`#!$E4|6FqBi)|1Z+(jakIX|mIZ zLrRlfI69;>*_EP0N|Sc(A^>1LczeV-WF#e9Ie;O3JrTz(2 zaPE;8o}9l7zW5cQ0S;}?6n`4AjY`;1Bd(ybaCdi*v^`U3PwN{9 z^=VJB8u)OmE#VR{5St_j6c8vNP(Yx7K!G=g0!zaF646B5?!PmU6(X8QL=%Z#v0NE{ybS}{ZQ#L1IkA*IM8=ZT){{qn>sj!{VIR>k2+sPbb@gOiF`sgHJ+Q!#nM zc=+=K)>SDRPxqAEk5})>E4fTW@8=(${mr>Ym(=lKaE}4c8QQU&ym#TIpEGIx2rk>? zx*RtqL0i}1CL1X(!0p@>K!1n70Jlh)+q>ta?j}$g{V~~$)=a9~QPDBgx zGvEk_(%*CQpm&o@+o#wLmMCN?c zUoNrLySgf;qRE0vtbSV>pPi@W%s2QjBSSVXxDg7lihwf(4?(MeALf_AEV|duiW1r@ z@GwUtN_Rutus+~Z=zmfFhV1D{wvEmVPy9`Vi>oTJg;p-MdK&*2 znR~rqvaC~V0gqMhBSM+0^N~G03e!v&aWbPapk;Z;_mKtgwtuu9IHSx#P91N7EHGIC z2>wJ~2qX}kIuVd=p5PV~)=Fp$_ zl9SwKuE30OpomsAa2==^nQn|s_C8E8G6x(PvAQHymtNcI(r{dr4QBzImknzTvtb#y z{aD_pu0J8cwto!+e&g>=n6>94%vug%W=#~Nxg)kONb?&f0D?5XfRPcTxvL}7FF~I? z@UFJMlD*SDx&4umy1`5Q1^XB5|0T2kv69dJFOZF(kFJ8?eq?bn|5R>X)*s((y?|uK z8#I-wzAXZuZKVjF>X^B9fv{(^xoXtF@6)M|C>trwkbk91{1b(L*WcaWlvt(P!}^|e zUtR#L0NB?ButP;rivV`v-1EjGhnb4t6i#pi31^?*hwzw>j;xS;P~-u>NEB9eKHhEO zmXE>jbdEf(5Q{PZjJLC*wHMeppV_u+0i61t=PQ3(+ICL(NANH<*WHGG*!BxIE>$Zs zuGp$7w||Yr^71oRrkv*(ndpp69d6zltGYebnrxrotlhBxj&at69#>_o8;+`LvJ_-K z4LiAs<)`|rH!ex(LSH62Rd8UzfdvQN8>TfqAEqx6v`>TU2zUc6V>x*tI%JQ~z6hd% z3G)45cbPp7FcO*+-=jIDf~Q(iY?=drRyv3-B)m|Ez^ni(LQo z@Zm!{k?FOf&&#%IORfEdw>G=|DcIbh*j&ZE&mDMFbd1}5nTCdg@u4Qmq_z2UYmuAD28so=j#+3IICP#O zzkfttDG*N}-fKfVqu`t<=GMVMq3ulNq7&hnGNWJ$sHKs*wKe8*0?V^&~c zA5Lsmw>aVMZJcm7RE7m7++8C2D+#oL+io@)iwNVyaqNt5 zcjp5-M;ohvK0x8Ss6W-x5@|nNh(xIG6J!wVVfTq=~ZS zLQS!mM7j1hl3nfj=JAJzXEWphKT5baI=nS~QI&!(r=zc)K3Z0A@-eU4By$%}&pk|L zWr~z6XK#JVh*j))8=FT5tBI8uMStRK>?RL)dvjW?7JRN4DZpn$p*jDU($t>jIp8`RVU#eU$utoMW3>SI)kTl+2`AE+)9@zVuhBhRTPxWw{nq`zZFkfAZMnhi_=wE)2wtu$A5`*Y(WZ- zT}bD_!ez-Nc@2dfchwk-qR`P$BQB7IOdERpSVgjoBB%dLe9VJ=s;8DC@=9+A2Qnf* z%eGUZ3#y@ls*lGID#npE(E3M)F;R~u&@zrDmNK05#}iF6joN5Yy5}O6HJ;5z`vt*n ze?b54bGZ1$X_>K{g(M6?n}0(8&JU^yuRScZ#aMa!T3}MN_9^2^2 zf|Bi{;e&!G2{tC-5^xO#4>LBV-(hi0sP|Sdq1|snyN3n)pPMn^y#R2VuVku`8F)sc zmnMB&mrXggB|kOaC@!9B-Vtv}!6J8*0vz2e6-7rNylrmpweBZck$*ocDNSExcCxW6 zrllLxI(b|_F$F1&Py%0sWdcg2h_UR@_~(lf-coA^@*q0A9v0o<6XhCn6+%zqDc~`pBqz z%k-0HOJoeW#g=GIG=C#|XvC}gS|fV^gSZwz+@&RgRYkCBV}x^D5JME%GAG%La^+9d zw(g;r>P1OPXkL_pi{0&{YGtRVsH=9*Tf$LY9T`e(B<-d`YHC4{(q9nMe=jkjoGQ7^ z#}~k{&9?6fSiJsx_1jf7u^scy2TMY+4d@(s2tW@35(aDfz#m%@5`=h)C=WiR(o|`^ UzFq%+00030|4-66SE()m0D&F Date: Wed, 10 May 2023 16:25:05 -0400 Subject: [PATCH 226/243] update changelog --- CHANGELOG.md | 181 +++++++++++++++++++++++++++------------------------ 1 file changed, 97 insertions(+), 84 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f383f1c8a2..146b7cb626e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,89 +1,102 @@ # Lotus changelog -# v1.23.1 / 2023-05-03 - -# What's changed - - fix: sched: Address GET_32G_MAX_CONCURRENT regression - - fix: cli: Hide legacy markets cmds - - fix: ci: Debugging m1 build - - Update build version for release/v1.23.1 - - Disable lotus markets by default (#10809) ([filecoin-project/lotus#10809](https://github.com/filecoin-project/lotus/pull/10809)) - - perf: mempool: lower priority optimizations (#10693) ([filecoin-project/lotus#10693](https://github.com/filecoin-project/lotus/pull/10693)) - - Change args check ([filecoin-project/lotus#10812](https://github.com/filecoin-project/lotus/pull/10812)) - - chore: drop flaky TestBatchDealInput subcase ([filecoin-project/lotus#10810](https://github.com/filecoin-project/lotus/pull/10810)) - - fix: sealing: Make lotus-worker report GPU usage to miner during ReplicaUpdate task (#10806) ([filecoin-project/lotus#10806](https://github.com/filecoin-project/lotus/pull/10806)) - - fix:splitstore:Don't block when potentially holding txnLk as writer ([filecoin-project/lotus#10811](https://github.com/filecoin-project/lotus/pull/10811)) - - feat: worker: Ensure tempdir exists (#10433) ([filecoin-project/lotus#10433](https://github.com/filecoin-project/lotus/pull/10433)) - - feat: chainstore: batch writes of tipsets ([filecoin-project/lotus#10800](https://github.com/filecoin-project/lotus/pull/10800)) - - chore: changelog clean up ([filecoin-project/lotus#10744](https://github.com/filecoin-project/lotus/pull/10744)) - - chore: refactor: drop unused IsTicketWinner (#10801) ([filecoin-project/lotus#10801](https://github.com/filecoin-project/lotus/pull/10801)) - - Revert #9858 (consistent broadcast changes) ([filecoin-project/lotus#10777](https://github.com/filecoin-project/lotus/pull/10777)) - - chore: deps: update to FVM 3.3.1 ([filecoin-project/lotus#10786](https://github.com/filecoin-project/lotus/pull/10786)) - - Set default for MaxSectorProveCommitsSubmittedPerEpoch ([filecoin-project/lotus#10728](https://github.com/filecoin-project/lotus/pull/10728)) - - fix: deflake: use 2 miners for flaky tests ([filecoin-project/lotus#10764](https://github.com/filecoin-project/lotus/pull/10764)) - - fix: prover: Propagate skipped sectors in local PoSt - - test: eth: deflake multiblock lookup test (#10769) ([filecoin-project/lotus#10769](https://github.com/filecoin-project/lotus/pull/10769)) - - feat: sync: harden chain sync (#10756) ([filecoin-project/lotus#10756](https://github.com/filecoin-project/lotus/pull/10756)) - - fix: tvx: make extract-multiple support the FVM ([filecoin-project/lotus#10714](https://github.com/filecoin-project/lotus/pull/10714)) - - feat: badger: add a has check before writing to reduce duplicates ([filecoin-project/lotus#10680](https://github.com/filecoin-project/lotus/pull/10680)) - - fix: events: don't set GC confidence to 1 ([filecoin-project/lotus#10713](https://github.com/filecoin-project/lotus/pull/10713)) - - fix: sync: reduce log from error to info ([filecoin-project/lotus#10759](https://github.com/filecoin-project/lotus/pull/10759)) - - shed: migrations: add reminder about continuity testing tool ([filecoin-project/lotus#10762](https://github.com/filecoin-project/lotus/pull/10762)) - - fix: chain: record heaviest tipset before notifying (#10694) ([filecoin-project/lotus#10694](https://github.com/filecoin-project/lotus/pull/10694)) - - fix: sealing pipeline: Allow nil message in TerminateWait ([filecoin-project/lotus#10696](https://github.com/filecoin-project/lotus/pull/10696)) - - fix: storage: Remove temp fetching files after failed fetch ([filecoin-project/lotus#10661](https://github.com/filecoin-project/lotus/pull/10661)) - - feat: daemon: Auto-resume interrupted snapshot imports ([filecoin-project/lotus#10636](https://github.com/filecoin-project/lotus/pull/10636)) - - feat: shed: refactor market cron-state command ([filecoin-project/lotus#10746](https://github.com/filecoin-project/lotus/pull/10746)) - - fix: Eth JSON-RPC api: handle messages with gasFeeCap less than baseFee (#10614) ([filecoin-project/lotus#10614](https://github.com/filecoin-project/lotus/pull/10614)) - - chore: merge releases into master ([filecoin-project/lotus#10742](https://github.com/filecoin-project/lotus/pull/10742)) - - feat: sync: validate (early) that blocks fall within range (#10691) ([filecoin-project/lotus#10691](https://github.com/filecoin-project/lotus/pull/10691)) - - chainstore: Fix raw blocks getting scanned for links during snapshots (#10684) ([filecoin-project/lotus#10684](https://github.com/filecoin-project/lotus/pull/10684)) - - perf: Address performance of EthGetTransactionCount ([filecoin-project/lotus#10700](https://github.com/filecoin-project/lotus/pull/10700)) - - feat: sealing: Split PCA/PCB batches if gas used exceeds block limit ([filecoin-project/lotus#10647](https://github.com/filecoin-project/lotus/pull/10647)) - - fix: remove pointless panic ([filecoin-project/lotus#10690](https://github.com/filecoin-project/lotus/pull/10690)) - - chore: build: bump matser version to v1.23.1-dev ([filecoin-project/lotus#10709](https://github.com/filecoin-project/lotus/pull/10709)) - - chore: boxo: migrate from go-libipfs to boxo ([filecoin-project/lotus#10562](https://github.com/filecoin-project/lotus/pull/10562)) - - fix: unseal: check if sealed/update sector exists ([filecoin-project/lotus#10639](https://github.com/filecoin-project/lotus/pull/10639)) - - fix: check for nil bcastDict (#10646) ([filecoin-project/lotus#10646](https://github.com/filecoin-project/lotus/pull/10646)) - - Add API and CLI to unseal sector (#10626) ([filecoin-project/lotus#10626](https://github.com/filecoin-project/lotus/pull/10626)) - - test: events: fix race when recording tipsets (#10665) ([filecoin-project/lotus#10665](https://github.com/filecoin-project/lotus/pull/10665)) - - fix:mpool: prune excess messages before selection ([filecoin-project/lotus#10648](https://github.com/filecoin-project/lotus/pull/10648)) - - Update config default value (#10605) ([filecoin-project/lotus#10605](https://github.com/filecoin-project/lotus/pull/10605)) - - fix: cap the message gas limit at the block gas limit (#10637) ([filecoin-project/lotus#10637](https://github.com/filecoin-project/lotus/pull/10637)) - - fix: build: add CBDeliveryDelay to testground ([filecoin-project/lotus#10613](https://github.com/filecoin-project/lotus/pull/10613)) - - feat:splitstore:limit moving gc threads ([filecoin-project/lotus#10621](https://github.com/filecoin-project/lotus/pull/10621)) - - feat: chainstore: optimize BlockMsgsForTipset ([filecoin-project/lotus#10552](https://github.com/filecoin-project/lotus/pull/10552)) - - fix: make state compute --html work with unknown methods ([filecoin-project/lotus#10619](https://github.com/filecoin-project/lotus/pull/10619)) - - fix: cli: Check if the sectorID exists before removing ([filecoin-project/lotus#10611](https://github.com/filecoin-project/lotus/pull/10611)) - - Fixed incorrect words that could not be compiled ([filecoin-project/lotus#10610](https://github.com/filecoin-project/lotus/pull/10610)) - - Add feature to stagger sector prove commit submission (#10543) ([filecoin-project/lotus#10543](https://github.com/filecoin-project/lotus/pull/10543)) - - shed: get balances of evm accounts ([filecoin-project/lotus#10489](https://github.com/filecoin-project/lotus/pull/10489)) - - chore: deps: update to go-state-types v0.11.0-alpha-3 ([filecoin-project/lotus#10606](https://github.com/filecoin-project/lotus/pull/10606)) - - fix: cli: Make `net connect` to miner address work ([filecoin-project/lotus#10599](https://github.com/filecoin-project/lotus/pull/10599)) - - feat: VM Execution Lanes ([filecoin-project/lotus#10551](https://github.com/filecoin-project/lotus/pull/10551)) - - fix: log: Stop logging `file does not exists` ([filecoin-project/lotus#10588](https://github.com/filecoin-project/lotus/pull/10588)) - - perf: message pool: change locks to RWMutexes for performance ([filecoin-project/lotus#10561](https://github.com/filecoin-project/lotus/pull/10561)) - - fix: miner: correctly count sector extensions ([filecoin-project/lotus#10544](https://github.com/filecoin-project/lotus/pull/10544)) - - feat:networking: (Synchronous) Consistent Broadcast for Filecoin EC ([filecoin-project/lotus#9858](https://github.com/filecoin-project/lotus/pull/9858)) - - refactor: stop using deprecated io/ioutil ([filecoin-project/lotus#10596](https://github.com/filecoin-project/lotus/pull/10596)) - - feat: Use MessageIndex in WaitForMessage ([filecoin-project/lotus#10587](https://github.com/filecoin-project/lotus/pull/10587)) - - fix: searchForIndexedMsg always returns an error ([filecoin-project/lotus#10586](https://github.com/filecoin-project/lotus/pull/10586)) - - build: docker: Update GO-version ([filecoin-project/lotus#10581](https://github.com/filecoin-project/lotus/pull/10581)) - - fix: itests: Don't call t.Error in MineBlocks goroutine ([filecoin-project/lotus#10572](https://github.com/filecoin-project/lotus/pull/10572)) - - Fix: export-range: Ignore ipld Blocks not found in Receipts. ([filecoin-project/lotus#10535](https://github.com/filecoin-project/lotus/pull/10535)) - - feat: populate the index on snapshot import ([filecoin-project/lotus#10556](https://github.com/filecoin-project/lotus/pull/10556)) - - feat: Add small cache to execution traces ([filecoin-project/lotus#10517](https://github.com/filecoin-project/lotus/pull/10517)) - - docs: api: clarify MpoolClear params ([filecoin-project/lotus#10550](https://github.com/filecoin-project/lotus/pull/10550)) - - fix: proving: Initialize slice with with same length as partition ([filecoin-project/lotus#10569](https://github.com/filecoin-project/lotus/pull/10569)) - - perf: eth: gas estimate set applyTsMessages false (#10546) ([filecoin-project/lotus#10546](https://github.com/filecoin-project/lotus/pull/10546)) - - feat: shed: incoming block-sub chainwatch tool ([filecoin-project/lotus#10513](https://github.com/filecoin-project/lotus/pull/10513)) - - feat: stmgr: speed up calculation of genesis circ supply ([filecoin-project/lotus#10553](https://github.com/filecoin-project/lotus/pull/10553)) - - fix: gas estimation: don't special case paych collects ([filecoin-project/lotus#10549](https://github.com/filecoin-project/lotus/pull/10549)) - - fix: tracer: emit raw peer ids for compatibility with libp2p tracer ([filecoin-project/lotus#10271](https://github.com/filecoin-project/lotus/pull/10271)) - - fix: state: lotus-miner info should show deals info without admin permission ([filecoin-project/lotus#10323](https://github.com/filecoin-project/lotus/pull/10323)) - - Merge branch 'feat/new-gw-methods' - - chore: bump go-libipfs ([filecoin-project/lotus#10531](https://github.com/filecoin-project/lotus/pull/10531)) - - feat:chain: Message Index ([filecoin-project/lotus#10452](https://github.com/filecoin-project/lotus/pull/10452)) +# v1.23.1-rc2 / 2023-05-10 + +This is the first release candidate of the upcoming optional feature release of Lotus v1.23.1. + +**☢️ Upgrade Warnings ☢️** + +- If you are upgrading to this release candidate from Lotus v1.22.1, please make sure to read the upgrade warnings section in the [v1.23.0 release first.](https://github.com/filecoin-project/lotus/releases/tag/v1.23.0) + +## New features + +- feat: daemon: Auto-resume interrupted snapshot imports ([filecoin-project/lotus#10636](https://github.com/filecoin-project/lotus/pull/10636)) +- feat: VM Execution Lanes ([filecoin-project/lotus#10551](https://github.com/filecoin-project/lotus/pull/10551)) +- feat: chainstore: batch writes of tipsets ([filecoin-project/lotus#10800](https://github.com/filecoin-project/lotus/pull/10800)) +- Add API and CLI to unseal sector (#10626) ([filecoin-project/lotus#10626](https://github.com/filecoin-project/lotus/pull/10626)) +- feat: sealing: Split PCA/PCB batches if gas used exceeds block limit ([filecoin-project/lotus#10647](https://github.com/filecoin-project/lotus/pull/10647)) +- Add feature to stagger sector prove commit submission (#10543) ([filecoin-project/lotus#10543](https://github.com/filecoin-project/lotus/pull/10543)) +- Set default for MaxSectorProveCommitsSubmittedPerEpoch ([filecoin-project/lotus#10728](https://github.com/filecoin-project/lotus/pull/10728)) +- fix: storage: Remove temp fetching files after failed fetch ([filecoin-project/lotus#10661](https://github.com/filecoin-project/lotus/pull/10661)) +- feat: worker: Ensure tempdir exists (#10433) ([filecoin-project/lotus#10433](https://github.com/filecoin-project/lotus/pull/10433)) +- feat: sync: harden chain sync (#10756) ([filecoin-project/lotus#10756](https://github.com/filecoin-project/lotus/pull/10756)) +- feat: populate the index on snapshot import ([filecoin-project/lotus#10556](https://github.com/filecoin-project/lotus/pull/10556)) +- feat:chain: Message Index ([filecoin-project/lotus#10452](https://github.com/filecoin-project/lotus/pull/10452)) +- feat: Add small cache to execution traces ([filecoin-project/lotus#10517](https://github.com/filecoin-project/lotus/pull/10517)) +- feat: shed: incoming block-sub chainwatch tool ([filecoin-project/lotus#10513](https://github.com/filecoin-project/lotus/pull/10513)) + +## Improvements +- fix: sched: Address GET_32G_MAX_CONCURRENT regression +- fix: cli: Hide legacy markets cmds +- fix: ci: Debugging m1 build +- Disable lotus markets by default (#10809) ([filecoin-project/lotus#10809](https://github.com/filecoin-project/lotus/pull/10809)) +- perf: mempool: lower priority optimizations (#10693) ([filecoin-project/lotus#10693](https://github.com/filecoin-project/lotus/pull/10693)) +- perf: message pool: change locks to RWMutexes for performance ([filecoin-project/lotus#10561](https://github.com/filecoin-project/lotus/pull/10561)) +- perf: eth: gas estimate set applyTsMessages false (#10546) ([filecoin-project/lotus#10546](https://github.com/filecoin-project/lotus/pull/10546)) +- Change args check ([filecoin-project/lotus#10812](https://github.com/filecoin-project/lotus/pull/10812)) +- fix: sealing: Make lotus-worker report GPU usage to miner during ReplicaUpdate task (#10806) ([filecoin-project/lotus#10806](https://github.com/filecoin-project/lotus/pull/10806)) +- fix:splitstore:Don't block when potentially holding txnLk as writer ([filecoin-project/lotus#10811](https://github.com/filecoin-project/lotus/pull/10811)) +- fix: prover: Propagate skipped sectors in local PoSt +- fix: unseal: check if sealed/update sector exists ([filecoin-project/lotus#10639](https://github.com/filecoin-project/lotus/pull/10639)) +- fix: sealing pipeline: Allow nil message in TerminateWait ([filecoin-project/lotus#10696](https://github.com/filecoin-project/lotus/pull/10696)) +- fix: cli: Check if the sectorID exists before removing ([filecoin-project/lotus#10611](https://github.com/filecoin-project/lotus/pull/10611)) +- feat:splitstore:limit moving gc threads ([filecoin-project/lotus#10621](https://github.com/filecoin-project/lotus/pull/10621)) +- fix: cli: Make `net connect` to miner address work ([filecoin-project/lotus#10599](https://github.com/filecoin-project/lotus/pull/10599)) +- fix: log: Stop logging `file does not exists` ([filecoin-project/lotus#10588](https://github.com/filecoin-project/lotus/pull/10588)) +- Update config default value (#10605) ([filecoin-project/lotus#10605](https://github.com/filecoin-project/lotus/pull/10605)) +- fix: cap the message gas limit at the block gas limit (#10637) ([filecoin-project/lotus#10637](https://github.com/filecoin-project/lotus/pull/10637)) +- fix: miner: correctly count sector extensions ([filecoin-project/lotus#10544](https://github.com/filecoin-project/lotus/pull/10544)) +- fix:mpool: prune excess messages before selection ([filecoin-project/lotus#10648](https://github.com/filecoin-project/lotus/pull/10648)) +- fix: proving: Initialize slice with with same length as partition ([filecoin-project/lotus#10569](https://github.com/filecoin-project/lotus/pull/10569)) +- perf: Address performance of EthGetTransactionCount ([filecoin-project/lotus#10700](https://github.com/filecoin-project/lotus/pull/10700)) +- fix: sync: reduce log from error to info ([filecoin-project/lotus#10759](https://github.com/filecoin-project/lotus/pull/10759)) +- fix: state: lotus-miner info should show deals info without admin permission ([filecoin-project/lotus#10323](https://github.com/filecoin-project/lotus/pull/10323)) +- fix: tvx: make extract-multiple support the FVM ([filecoin-project/lotus#10714](https://github.com/filecoin-project/lotus/pull/10714)) +- feat: badger: add a has check before writing to reduce duplicates ([filecoin-project/lotus#10680](https://github.com/filecoin-project/lotus/pull/10680)) +- fix: chain: record heaviest tipset before notifying (#10694) ([filecoin-project/lotus#10694](https://github.com/filecoin-project/lotus/pull/10694)) +- fix: Eth JSON-RPC api: handle messages with gasFeeCap less than baseFee (#10614) ([filecoin-project/lotus#10614](https://github.com/filecoin-project/lotus/pull/10614)) +- feat: chainstore: optimize BlockMsgsForTipset ([filecoin-project/lotus#10552](https://github.com/filecoin-project/lotus/pull/10552)) +- refactor: stop using deprecated io/ioutil ([filecoin-project/lotus#10596](https://github.com/filecoin-project/lotus/pull/10596)) +- feat: shed: refactor market cron-state command ([filecoin-project/lotus#10746](https://github.com/filecoin-project/lotus/pull/10746)) +- fix: events: don't set GC confidence to 1 ([filecoin-project/lotus#10713](https://github.com/filecoin-project/lotus/pull/10713)) +- feat: sync: validate (early) that blocks fall within range (#10691) ([filecoin-project/lotus#10691](https://github.com/filecoin-project/lotus/pull/10691)) +- chainstore: Fix raw blocks getting scanned for links during snapshots (#10684) ([filecoin-project/lotus#10684](https://github.com/filecoin-project/lotus/pull/10684)) +- fix: remove pointless panic ([filecoin-project/lotus#10690](https://github.com/filecoin-project/lotus/pull/10690)) +- fix: check for nil bcastDict (#10646) ([filecoin-project/lotus#10646](https://github.com/filecoin-project/lotus/pull/10646)) +- fix: make state compute --html work with unknown methods ([filecoin-project/lotus#10619](https://github.com/filecoin-project/lotus/pull/10619)) +- shed: get balances of evm accounts ([filecoin-project/lotus#10489](https://github.com/filecoin-project/lotus/pull/10489)) +- feat: Use MessageIndex in WaitForMessage ([filecoin-project/lotus#10587](https://github.com/filecoin-project/lotus/pull/10587)) +- fix: searchForIndexedMsg always returns an error ([filecoin-project/lotus#10586](https://github.com/filecoin-project/lotus/pull/10586)) +- Fix: export-range: Ignore ipld Blocks not found in Receipts. ([filecoin-project/lotus#10535](https://github.com/filecoin-project/lotus/pull/10535)) +- feat: stmgr: speed up calculation of genesis circ supply ([filecoin-project/lotus#10553](https://github.com/filecoin-project/lotus/pull/10553)) +- fix: gas estimation: don't special case paych collects ([filecoin-project/lotus#10549](https://github.com/filecoin-project/lotus/pull/10549)) +- fix: tracer: emit raw peer ids for compatibility with libp2p tracer ([filecoin-project/lotus#10271](https://github.com/filecoin-project/lotus/pull/10271)) +- Merge branch 'feat/new-gw-methods' + +## Dependencies +- chore: deps: update to FVM 3.3.1 ([filecoin-project/lotus#10786](https://github.com/filecoin-project/lotus/pull/10786)) +- chore: boxo: migrate from go-libipfs to boxo ([filecoin-project/lotus#10562](https://github.com/filecoin-project/lotus/pull/10562)) +- chore: deps: update to go-state-types v0.11.0-alpha-3 ([filecoin-project/lotus#10606](https://github.com/filecoin-project/lotus/pull/10606)) +- chore: bump go-libipfs ([filecoin-project/lotus#10531](https://github.com/filecoin-project/lotus/pull/10531)) + +## Others +- feat:networking: (Synchronous) Consistent Broadcast for Filecoin EC ([filecoin-project/lotus#9858](https://github.com/filecoin-project/lotus/pull/9858)) +- Revert #9858 (consistent broadcast changes) ([filecoin-project/lotus#10777](https://github.com/filecoin-project/lotus/pull/10777)) +- Update build version for release/v1.23.1 +- chore: drop flaky TestBatchDealInput subcase ([filecoin-project/lotus#10810](https://github.com/filecoin-project/lotus/pull/10810)) +- chore: changelog clean up ([filecoin-project/lotus#10744](https://github.com/filecoin-project/lotus/pull/10744)) +- chore: refactor: drop unused IsTicketWinner (#10801) ([filecoin-project/lotus#10801](https://github.com/filecoin-project/lotus/pull/10801)) +- chore: build: bump matser version to v1.23.1-dev ([filecoin-project/lotus#10709](https://github.com/filecoin-project/lotus/pull/10709)) +- fix: deflake: use 2 miners for flaky tests ([filecoin-project/lotus#10764](https://github.com/filecoin-project/lotus/pull/10764)) +- test: eth: deflake multiblock lookup test (#10769) ([filecoin-project/lotus#10769](https://github.com/filecoin-project/lotus/pull/10769)) +- shed: migrations: add reminder about continuity testing tool ([filecoin-project/lotus#10762](https://github.com/filecoin-project/lotus/pull/10762)) +- chore: merge releases into master ([filecoin-project/lotus#10742](https://github.com/filecoin-project/lotus/pull/10742)) +- test: events: fix race when recording tipsets (#10665) ([filecoin-project/lotus#10665](https://github.com/filecoin-project/lotus/pull/10665)) +- fix: build: add CBDeliveryDelay to testground ([filecoin-project/lotus#10613](https://github.com/filecoin-project/lotus/pull/10613)) +- fix: build: Fixed incorrect words that could not be compiled ([filecoin-project/lotus#10610](https://github.com/filecoin-project/lotus/pull/10610)) +- build: docker: Update GO-version ([filecoin-project/lotus#10581](https://github.com/filecoin-project/lotus/pull/10581)) +- fix: itests: Don't call t.Error in MineBlocks goroutine ([filecoin-project/lotus#10572](https://github.com/filecoin-project/lotus/pull/10572)) +- docs: api: clarify MpoolClear params ([filecoin-project/lotus#10550](https://github.com/filecoin-project/lotus/pull/10550)) Contributors From a509ca23ecd1e6141e7e0685171c9f12d3ebf4bc Mon Sep 17 00:00:00 2001 From: Shrenuj Bansal Date: Tue, 16 May 2023 21:19:03 -0400 Subject: [PATCH 227/243] Check if epoch is negative in GetTipsetByHeight --- chain/store/store.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/chain/store/store.go b/chain/store/store.go index d7188a7bfd1..eba212198f8 100644 --- a/chain/store/store.go +++ b/chain/store/store.go @@ -1149,6 +1149,10 @@ func (cs *ChainStore) TryFillTipSet(ctx context.Context, ts *types.TipSet) (*Ful // selects the tipset before the null round if true, and the tipset following // the null round if false. func (cs *ChainStore) GetTipsetByHeight(ctx context.Context, h abi.ChainEpoch, ts *types.TipSet, prev bool) (*types.TipSet, error) { + if h < 0 { + return nil, xerrors.Errorf("height %d is negative", h) + } + if ts == nil { ts = cs.GetHeaviestTipSet() } From b65c93b2b465e1c96ffd6a4554996acccf468ce1 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Fri, 19 May 2023 19:43:56 +0200 Subject: [PATCH 228/243] feat: chainstore: sharded mutex for filling chain height index This PR introduces as sharded mutex within the ChainIndex#GetTipsetByHeight. It also replaces a go map with xsync.Map which doesn't require locking. The lock is taken when it appears that ChainIndex filling work should be started. After claiming the lock, the status of the cache is rechecked, if the entry is still missing, the fillCache is started. Thanks to @snissn and @arajasek for debugging and taking initial stabs at this. Supersedes #10866 and 10885 Signed-off-by: Jakub Sztandera --- chain/store/index.go | 44 ++++--- go.mod | 1 + go.sum | 2 + lib/shardedmutex/shardedmutex.go | 75 ++++++++++++ lib/shardedmutex/shardedmutex_test.go | 159 ++++++++++++++++++++++++++ 5 files changed, 266 insertions(+), 15 deletions(-) create mode 100644 lib/shardedmutex/shardedmutex.go create mode 100644 lib/shardedmutex/shardedmutex_test.go diff --git a/chain/store/index.go b/chain/store/index.go index 620cb2deeb5..5807a2705ea 100644 --- a/chain/store/index.go +++ b/chain/store/index.go @@ -2,18 +2,21 @@ package store import ( "context" + "hash/maphash" "os" "strconv" - "sync" + "github.com/puzpuzpuz/xsync/v2" "golang.org/x/xerrors" "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/lib/shardedmutex" ) -var DefaultChainIndexCacheSize = 32 << 15 +// DefaultChainIndexCacheSize no longer sets the maximum size, just the inital size of the map. +var DefaultChainIndexCacheSize = 1 << 15 func init() { if s := os.Getenv("LOTUS_CHAIN_INDEX_CACHE"); s != "" { @@ -27,8 +30,9 @@ func init() { } type ChainIndex struct { - indexCacheLk sync.Mutex - indexCache map[types.TipSetKey]*lbEntry + indexCache *xsync.MapOf[types.TipSetKey, *lbEntry] + + fillCacheLock shardedmutex.ShardedMutexFor[types.TipSetKey] loadTipSet loadTipSetFunc @@ -36,11 +40,16 @@ type ChainIndex struct { } type loadTipSetFunc func(context.Context, types.TipSetKey) (*types.TipSet, error) +func maphashTSK(s maphash.Seed, tsk types.TipSetKey) uint64 { + return maphash.Bytes(s, tsk.Bytes()) +} + func NewChainIndex(lts loadTipSetFunc) *ChainIndex { return &ChainIndex{ - indexCache: make(map[types.TipSetKey]*lbEntry, DefaultChainIndexCacheSize), - loadTipSet: lts, - skipLength: 20, + indexCache: xsync.NewTypedMapOfPresized[types.TipSetKey, *lbEntry](maphashTSK, DefaultChainIndexCacheSize), + fillCacheLock: shardedmutex.NewFor(maphashTSK, 32), + loadTipSet: lts, + skipLength: 20, } } @@ -59,17 +68,23 @@ func (ci *ChainIndex) GetTipsetByHeight(ctx context.Context, from *types.TipSet, return nil, xerrors.Errorf("failed to round down: %w", err) } - ci.indexCacheLk.Lock() - defer ci.indexCacheLk.Unlock() cur := rounded.Key() for { - lbe, ok := ci.indexCache[cur] + lbe, ok := ci.indexCache.Load(cur) // check the cache if !ok { - fc, err := ci.fillCache(ctx, cur) - if err != nil { - return nil, xerrors.Errorf("failed to fill cache: %w", err) + lk := ci.fillCacheLock.GetLock(cur) + lk.Lock() // if entry is missing, take the lock + lbe, ok = ci.indexCache.Load(cur) // check if someone else added it while we waited for lock + if !ok { + fc, err := ci.fillCache(ctx, cur) + if err != nil { + lk.Unlock() + return nil, xerrors.Errorf("failed to fill cache: %w", err) + } + lbe = fc + ci.indexCache.Store(cur, lbe) } - lbe = fc + lk.Unlock() } if to == lbe.targetHeight { @@ -137,7 +152,6 @@ func (ci *ChainIndex) fillCache(ctx context.Context, tsk types.TipSetKey) (*lbEn targetHeight: skipTarget.Height(), target: skipTarget.Key(), } - ci.indexCache[tsk] = lbe return lbe, nil } diff --git a/go.mod b/go.mod index d190323f286..f19c6d3caa9 100644 --- a/go.mod +++ b/go.mod @@ -139,6 +139,7 @@ require ( github.com/open-rpc/meta-schema v0.0.0-20201029221707-1b72ef2ea333 github.com/polydawn/refmt v0.89.0 github.com/prometheus/client_golang v1.14.0 + github.com/puzpuzpuz/xsync/v2 v2.4.0 github.com/raulk/clock v1.1.0 github.com/raulk/go-watchdog v1.3.0 github.com/stretchr/testify v1.8.2 diff --git a/go.sum b/go.sum index 80dcf1433bc..0a423f8a97e 100644 --- a/go.sum +++ b/go.sum @@ -1484,6 +1484,8 @@ github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5 github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= github.com/prometheus/statsd_exporter v0.21.0 h1:hA05Q5RFeIjgwKIYEdFd59xu5Wwaznf33yKI+pyX6T8= github.com/prometheus/statsd_exporter v0.21.0/go.mod h1:rbT83sZq2V+p73lHhPZfMc3MLCHmSHelCh9hSGYNLTQ= +github.com/puzpuzpuz/xsync/v2 v2.4.0 h1:5sXAMHrtx1bg9nbRZTOn8T4MkWe5V+o8yKRH02Eznag= +github.com/puzpuzpuz/xsync/v2 v2.4.0/go.mod h1:gD2H2krq/w52MfPLE+Uy64TzJDVY7lP2znR9qmR35kU= github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= github.com/quic-go/qtls-go1-19 v0.2.1 h1:aJcKNMkH5ASEJB9FXNeZCyTEIHU1J7MmHyz1Q1TSG1A= diff --git a/lib/shardedmutex/shardedmutex.go b/lib/shardedmutex/shardedmutex.go new file mode 100644 index 00000000000..47b677f511b --- /dev/null +++ b/lib/shardedmutex/shardedmutex.go @@ -0,0 +1,75 @@ +package shardedmutex + +import ( + "hash/maphash" + "sync" +) + +const cacheline = 64 + +// padding a mutex to a cacheline improves performance as the cachelines are not contested +// name old time/op new time/op delta +// Locks-8 74.6ns ± 7% 12.3ns ± 2% -83.54% (p=0.000 n=20+18) +type paddedMutex struct { + mt sync.Mutex + pad [cacheline - 8]uint8 +} + +type ShardedMutex struct { + shards []paddedMutex +} + +// New creates a new ShardedMutex with N shards +func New(n_shards int) ShardedMutex { + if n_shards < 1 { + panic("n_shards cannot be less than 1") + } + return ShardedMutex{ + shards: make([]paddedMutex, n_shards), + } +} + +func (sm ShardedMutex) Shards() int { + return len(sm.shards) +} + +func (sm ShardedMutex) Lock(shard int) { + sm.shards[shard].mt.Lock() +} + +func (sm ShardedMutex) Unlock(shard int) { + sm.shards[shard].mt.Unlock() +} + +func (sm ShardedMutex) GetLock(shard int) sync.Locker { + return &sm.shards[shard].mt +} + +type ShardedMutexFor[K any] struct { + inner ShardedMutex + + hasher func(maphash.Seed, K) uint64 + seed maphash.Seed +} + +func NewFor[K any](hasher func(maphash.Seed, K) uint64, n_shards int) ShardedMutexFor[K] { + return ShardedMutexFor[K]{ + inner: New(n_shards), + hasher: hasher, + seed: maphash.MakeSeed(), + } +} + +func (sm ShardedMutexFor[K]) shardFor(key K) int { + return int(sm.hasher(sm.seed, key) % uint64(len(sm.inner.shards))) +} + +func (sm ShardedMutexFor[K]) Lock(key K) { + sm.inner.Lock(sm.shardFor(key)) +} +func (sm ShardedMutexFor[K]) Unlock(key K) { + sm.inner.Unlock(sm.shardFor(key)) +} +func (sm ShardedMutexFor[K]) GetLock(key K) sync.Locker { + return sm.inner.GetLock(sm.shardFor(key)) +} diff --git a/lib/shardedmutex/shardedmutex_test.go b/lib/shardedmutex/shardedmutex_test.go new file mode 100644 index 00000000000..a7d5f7d1dfe --- /dev/null +++ b/lib/shardedmutex/shardedmutex_test.go @@ -0,0 +1,159 @@ +package shardedmutex + +import ( + "fmt" + "hash/maphash" + "runtime" + "sync" + "sync/atomic" + "testing" + "time" +) + +func TestLockingDifferentShardsDoesNotBlock(t *testing.T) { + shards := 16 + sm := New(shards) + done := make(chan struct{}) + go func() { + select { + case <-done: + return + case <-time.After(5 * time.Second): + panic("test locked up") + } + }() + for i := 0; i < shards; i++ { + sm.Lock(i) + } + + close(done) +} +func TestLockingSameShardsBlocks(t *testing.T) { + shards := 16 + sm := New(shards) + wg := sync.WaitGroup{} + wg.Add(shards) + ch := make(chan int, shards) + + for i := 0; i < shards; i++ { + go func(i int) { + if i != 15 { + sm.Lock(i) + } + wg.Done() + wg.Wait() + sm.Lock((15 + i) % shards) + ch <- i + sm.Unlock(i) + }(i) + } + + wg.Wait() + for i := 0; i < 2*shards; i++ { + runtime.Gosched() + } + for i := 0; i < shards; i++ { + if a := <-ch; a != i { + t.Errorf("got %d instead of %d", a, i) + } + } +} + +func TestShardedByString(t *testing.T) { + shards := 16 + sm := NewFor(maphash.String, shards) + + wg1 := sync.WaitGroup{} + wg1.Add(shards * 20) + wg2 := sync.WaitGroup{} + wg2.Add(shards * 20) + + active := atomic.Int32{} + max := atomic.Int32{} + + for i := 0; i < shards*20; i++ { + go func(i int) { + wg1.Done() + wg1.Wait() + sm.Lock(fmt.Sprintf("goroutine %d", i)) + activeNew := active.Add(1) + for { + curMax := max.Load() + if curMax >= activeNew { + break + } + if max.CompareAndSwap(curMax, activeNew) { + break + } + } + for j := 0; j < 100; j++ { + runtime.Gosched() + } + active.Add(-1) + sm.Unlock(fmt.Sprintf("goroutine %d", i)) + wg2.Done() + }(i) + } + + wg2.Wait() + + if max.Load() != 16 { + t.Fatal("max load not achieved", max.Load()) + } + +} + +func BenchmarkShardedMutex(b *testing.B) { + shards := 16 + sm := New(shards) + + done := atomic.Int32{} + go func() { + for { + sm.Lock(0) + sm.Unlock(0) + if done.Load() != 0 { + return + } + } + }() + for i := 0; i < 100; i++ { + runtime.Gosched() + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + sm.Lock(1) + sm.Unlock(1) + } + done.Add(1) +} + +func BenchmarkShardedMutexOf(b *testing.B) { + shards := 16 + sm := NewFor(maphash.String, shards) + + str1 := "string1" + str2 := "string2" + + done := atomic.Int32{} + go func() { + for { + sm.Lock(str1) + sm.Unlock(str1) + if done.Load() != 0 { + return + } + } + }() + for i := 0; i < 100; i++ { + runtime.Gosched() + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + sm.Lock(str2) + sm.Unlock(str2) + } + done.Add(1) +} From 80aa96ec25ef59a0cbd045e6be055a8598fd38e1 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Fri, 19 May 2023 20:02:47 +0200 Subject: [PATCH 229/243] Appease the linter Signed-off-by: Jakub Sztandera --- chain/store/index.go | 2 +- lib/shardedmutex/shardedmutex.go | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/chain/store/index.go b/chain/store/index.go index 5807a2705ea..8361f4db9eb 100644 --- a/chain/store/index.go +++ b/chain/store/index.go @@ -15,7 +15,7 @@ import ( "github.com/filecoin-project/lotus/lib/shardedmutex" ) -// DefaultChainIndexCacheSize no longer sets the maximum size, just the inital size of the map. +// DefaultChainIndexCacheSize no longer sets the maximum size, just the initial size of the map. var DefaultChainIndexCacheSize = 1 << 15 func init() { diff --git a/lib/shardedmutex/shardedmutex.go b/lib/shardedmutex/shardedmutex.go index 47b677f511b..922ac399404 100644 --- a/lib/shardedmutex/shardedmutex.go +++ b/lib/shardedmutex/shardedmutex.go @@ -20,12 +20,12 @@ type ShardedMutex struct { } // New creates a new ShardedMutex with N shards -func New(n_shards int) ShardedMutex { - if n_shards < 1 { +func New(nShards int) ShardedMutex { + if nShards < 1 { panic("n_shards cannot be less than 1") } return ShardedMutex{ - shards: make([]paddedMutex, n_shards), + shards: make([]paddedMutex, nShards), } } @@ -52,9 +52,9 @@ type ShardedMutexFor[K any] struct { seed maphash.Seed } -func NewFor[K any](hasher func(maphash.Seed, K) uint64, n_shards int) ShardedMutexFor[K] { +func NewFor[K any](hasher func(maphash.Seed, K) uint64, nShards int) ShardedMutexFor[K] { return ShardedMutexFor[K]{ - inner: New(n_shards), + inner: New(nShards), hasher: hasher, seed: maphash.MakeSeed(), } From 80d39f66163241ed457aadb25aa4cf01cdf9de24 Mon Sep 17 00:00:00 2001 From: Marten Seemann Date: Fri, 14 Apr 2023 14:57:21 +0200 Subject: [PATCH 230/243] chore: update go-libp2p to v0.27.1 --- api/api_net.go | 1 - cli/net.go | 3 - documentation/en/api-v0-methods-miner.md | 3 +- documentation/en/api-v0-methods.md | 3 +- documentation/en/api-v1-unstable-methods.md | 3 +- go.mod | 68 ++++----- go.sum | 157 +++++++++----------- node/impl/net/net.go | 14 +- 8 files changed, 111 insertions(+), 141 deletions(-) diff --git a/api/api_net.go b/api/api_net.go index 404c707b426..c1a8f8514c9 100644 --- a/api/api_net.go +++ b/api/api_net.go @@ -73,5 +73,4 @@ type CommonNet interface { type NatInfo struct { Reachability network.Reachability - PublicAddr string } diff --git a/cli/net.go b/cli/net.go index 2649791e701..1266bb923ee 100644 --- a/cli/net.go +++ b/cli/net.go @@ -445,9 +445,6 @@ var NetReachability = &cli.Command{ } fmt.Println("AutoNAT status: ", i.Reachability.String()) - if i.PublicAddr != "" { - fmt.Println("Public address: ", i.PublicAddr) - } return nil }, } diff --git a/documentation/en/api-v0-methods-miner.md b/documentation/en/api-v0-methods-miner.md index 4761a3eed7b..2be9e5891ba 100644 --- a/documentation/en/api-v0-methods-miner.md +++ b/documentation/en/api-v0-methods-miner.md @@ -1701,8 +1701,7 @@ Inputs: `null` Response: ```json { - "Reachability": 1, - "PublicAddr": "string value" + "Reachability": 1 } ``` diff --git a/documentation/en/api-v0-methods.md b/documentation/en/api-v0-methods.md index 95678782a7d..8c4db7f8f0e 100644 --- a/documentation/en/api-v0-methods.md +++ b/documentation/en/api-v0-methods.md @@ -3709,8 +3709,7 @@ Inputs: `null` Response: ```json { - "Reachability": 1, - "PublicAddr": "string value" + "Reachability": 1 } ``` diff --git a/documentation/en/api-v1-unstable-methods.md b/documentation/en/api-v1-unstable-methods.md index 73871ce5087..bc158ea11c1 100644 --- a/documentation/en/api-v1-unstable-methods.md +++ b/documentation/en/api-v1-unstable-methods.md @@ -5021,8 +5021,7 @@ Inputs: `null` Response: ```json { - "Reachability": 1, - "PublicAddr": "string value" + "Reachability": 1 } ``` diff --git a/go.mod b/go.mod index f19c6d3caa9..c84c6b70efe 100644 --- a/go.mod +++ b/go.mod @@ -16,8 +16,8 @@ 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/chzyer/readline v1.5.0 - github.com/containerd/cgroups v1.0.4 + github.com/chzyer/readline v1.5.1 + github.com/containerd/cgroups v1.1.0 github.com/coreos/go-systemd/v22 v22.5.0 github.com/detailyang/go-fallocate v0.0.0-20180908115635-432fa640bd2e github.com/dgraph-io/badger/v2 v2.2007.4 @@ -116,7 +116,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.26.3 + github.com/libp2p/go-libp2p v0.27.1 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.21.1 @@ -126,14 +126,14 @@ require ( github.com/libp2p/go-libp2p-routing-helpers v0.4.0 github.com/libp2p/go-maddr-filter v0.1.0 github.com/libp2p/go-msgio v0.3.0 - github.com/mattn/go-isatty v0.0.17 + github.com/mattn/go-isatty v0.0.18 github.com/mattn/go-sqlite3 v1.14.16 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.8.0 + github.com/multiformats/go-multiaddr v0.9.0 github.com/multiformats/go-multiaddr-dns v0.3.1 - github.com/multiformats/go-multibase v0.1.1 + github.com/multiformats/go-multibase v0.2.0 github.com/multiformats/go-multihash v0.2.1 github.com/multiformats/go-varint v0.0.7 github.com/open-rpc/meta-schema v0.0.0-20201029221707-1b72ef2ea333 @@ -158,16 +158,16 @@ require ( go.opentelemetry.io/otel/exporters/jaeger v1.2.0 go.opentelemetry.io/otel/sdk v1.11.1 go.uber.org/atomic v1.10.0 - go.uber.org/fx v1.18.2 - go.uber.org/multierr v1.9.0 + go.uber.org/fx v1.19.2 + go.uber.org/multierr v1.11.0 go.uber.org/zap v1.24.0 - golang.org/x/crypto v0.6.0 - golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb - golang.org/x/net v0.7.0 + golang.org/x/crypto v0.7.0 + golang.org/x/exp v0.0.0-20230321023759-10a507213a29 + golang.org/x/net v0.8.0 golang.org/x/sync v0.1.0 - golang.org/x/sys v0.6.0 + golang.org/x/sys v0.7.0 golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 - golang.org/x/tools v0.3.0 + golang.org/x/tools v0.7.0 golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 gopkg.in/cheggaaa/pb.v1 v1.0.28 gotest.tools v2.2.0+incompatible @@ -179,7 +179,7 @@ require ( github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect github.com/StackExchange/wmi v1.2.1 // indirect github.com/akavel/rsrc v0.8.0 // indirect - github.com/alecthomas/units v0.0.0-20210927113745-59d0afb8317a // 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.0 // indirect github.com/beorn7/perks v1.0.1 // indirect @@ -187,7 +187,7 @@ require ( 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.4.0 // indirect + github.com/cilium/ebpf v0.9.1 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect github.com/crackcomm/go-gitignore v0.0.0-20170627025303-887ab5e44cc3 // indirect github.com/cskr/pubsub v1.0.2 // indirect @@ -218,22 +218,22 @@ require ( github.com/go-openapi/jsonpointer v0.19.3 // indirect github.com/go-openapi/jsonreference v0.19.4 // indirect github.com/go-openapi/swag v0.19.11 // indirect - github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect + 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.0.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/golang/protobuf v1.5.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/go-cmp v0.5.9 // indirect github.com/google/gopacket v1.1.19 // indirect - github.com/google/pprof v0.0.0-20221203041831-ce31453925ec // indirect + github.com/google/pprof v0.0.0-20230405160723-4a4c7d95572b // 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.0.3 // indirect + github.com/huin/goupnp v1.1.0 // indirect github.com/iancoleman/orderedmap v0.1.0 // indirect github.com/ipfs/go-bitfield v1.1.0 // indirect github.com/ipfs/go-filestore v1.2.0 // indirect @@ -260,12 +260,12 @@ require ( github.com/josharian/intern v1.0.0 // indirect github.com/jpillora/backoff v1.0.0 // indirect github.com/kilic/bls12-381 v0.1.0 // indirect - github.com/klauspost/compress v1.15.12 // indirect - github.com/klauspost/cpuid/v2 v2.2.3 // indirect - github.com/koron/go-ssdp v0.0.3 // indirect + github.com/klauspost/compress v1.16.4 // indirect + github.com/klauspost/cpuid/v2 v2.2.4 // 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 - github.com/libp2p/go-libp2p-asn-util v0.2.0 // indirect + github.com/libp2p/go-libp2p-asn-util v0.3.0 // indirect github.com/libp2p/go-libp2p-gostream v0.6.0 // indirect github.com/libp2p/go-libp2p-kbucket v0.5.0 // indirect github.com/libp2p/go-nat v0.1.0 // indirect @@ -279,7 +279,7 @@ require ( github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-runewidth v0.0.10 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect - github.com/miekg/dns v1.1.50 // indirect + github.com/miekg/dns v1.1.53 // 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.0 // indirect @@ -290,7 +290,7 @@ require ( github.com/multiformats/go-multistream v0.4.1 // indirect github.com/nikkolasg/hexjson v0.1.0 // indirect github.com/nkovacs/streamquote v1.0.0 // indirect - github.com/onsi/ginkgo/v2 v2.5.1 // indirect + github.com/onsi/ginkgo/v2 v2.9.2 // indirect github.com/opencontainers/runtime-spec v1.0.2 // indirect github.com/opentracing/opentracing-go v1.2.0 // indirect github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect @@ -298,12 +298,12 @@ require ( github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_model v0.3.0 // indirect - github.com/prometheus/common v0.37.0 // indirect - github.com/prometheus/procfs v0.8.0 // indirect + github.com/prometheus/common v0.42.0 // indirect + github.com/prometheus/procfs v0.9.0 // indirect github.com/prometheus/statsd_exporter v0.21.0 // indirect github.com/quic-go/qpack v0.4.0 // indirect - github.com/quic-go/qtls-go1-19 v0.2.1 // indirect - github.com/quic-go/qtls-go1-20 v0.1.1 // indirect + github.com/quic-go/qtls-go1-19 v0.3.2 // indirect + github.com/quic-go/qtls-go1-20 v0.2.2 // indirect github.com/quic-go/quic-go v0.33.0 // indirect github.com/quic-go/webtransport-go v0.5.2 // indirect github.com/rivo/uniseg v0.1.0 // indirect @@ -327,14 +327,14 @@ require ( go.opentelemetry.io/otel/metric v0.33.0 // indirect go.opentelemetry.io/otel/sdk/metric v0.33.0 // indirect go.opentelemetry.io/otel/trace v1.14.0 // indirect - go.uber.org/dig v1.15.0 // indirect + go.uber.org/dig v1.16.1 // indirect go4.org v0.0.0-20200411211856-f5505b9728dd // indirect - golang.org/x/mod v0.7.0 // indirect - golang.org/x/term v0.5.0 // indirect - golang.org/x/text v0.7.0 // indirect + golang.org/x/mod v0.10.0 // indirect + golang.org/x/term v0.6.0 // indirect + golang.org/x/text v0.8.0 // indirect google.golang.org/genproto v0.0.0-20210917145530-b395a37504d4 // indirect google.golang.org/grpc v1.45.0 // indirect - google.golang.org/protobuf v1.28.1 // indirect + google.golang.org/protobuf v1.30.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect howett.net/plist v0.0.0-20181124034731-591f970eefbb // indirect diff --git a/go.sum b/go.sum index 0a423f8a97e..078aed73a89 100644 --- a/go.sum +++ b/go.sum @@ -91,8 +91,8 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/alecthomas/units v0.0.0-20210927113745-59d0afb8317a h1:E/8AP5dFtMhl5KPJz66Kt9G0n+7Sn41Fy1wv9/jHOrc= -github.com/alecthomas/units v0.0.0-20210927113745-59d0afb8317a/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= +github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAuRjVTiNNhvNRfY2Wxp9nhfyel4rklc= +github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= @@ -148,22 +148,21 @@ github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -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/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.0 h1:+eqR0HfOetur4tgnC8ftU5imRnhi4te+BadWS95c5AM= -github.com/chzyer/logex v1.2.0/go.mod h1:9+9sk7u7pGNWYMkh0hdiL++6OeibzJccyQU4p4MedaY= +github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM= +github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/readline v1.5.0 h1:lSwwFrbNviGePhkewF1az4oLmcwqCZijQ2/Wi3BGHAI= -github.com/chzyer/readline v1.5.0/go.mod h1:x22KAscuvRqlLoK9CsoYsmxoXZMMFVyOl86cAH8qUic= +github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI= +github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/chzyer/test v0.0.0-20210722231415-061457976a23 h1:dZ0/VyGgQdVGAss6Ju0dt5P0QltE0SFY5Woh6hbIfiQ= -github.com/chzyer/test v0.0.0-20210722231415-061457976a23/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/chzyer/test v1.0.0 h1:p3BQDXSxOhOG0P9z6/hGnII4LGiEPOYBhs8asl/fC04= +github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8= github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs= -github.com/cilium/ebpf v0.4.0 h1:QlHdikaxALkqWasW8hAC1mfR0jdmvbfaBdBPFmRSglA= -github.com/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= +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= @@ -179,8 +178,8 @@ github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:z 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= github.com/containerd/cgroups v0.0.0-20201119153540-4cbc285b3327/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE= -github.com/containerd/cgroups v1.0.4 h1:jN/mbWBEaz+T1pi5OFtnkQ+8qnmEbAr1Oo1FRm5B0dA= -github.com/containerd/cgroups v1.0.4/go.mod h1:nLNQtsF7Sl2HxNebu77i1R0oDlhiTG+kO4JTrUzo6IA= +github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM= +github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= @@ -413,7 +412,6 @@ github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2 github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= -github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= @@ -454,8 +452,8 @@ github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1 github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0= github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= github.com/gobuffalo/logger v1.0.0/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs= @@ -517,8 +515,9 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= @@ -556,8 +555,8 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20221203041831-ce31453925ec h1:fR20TYVVwhK4O7r7y+McjRYyaTH6/vjwJOajE+XhlzM= -github.com/google/pprof v0.0.0-20221203041831-ce31453925ec/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo= +github.com/google/pprof v0.0.0-20230405160723-4a4c7d95572b h1:Qcx5LM0fSiks9uCyFZwDBUasd3lxd1RM0GYpL+Li5o4= +github.com/google/pprof v0.0.0-20230405160723-4a4c7d95572b/go.mod h1:79YE0hCXdHag9sBkw2o+N/YnZtTkXi0UT9Nnixa5eYk= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -650,8 +649,8 @@ github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/J github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= github.com/huin/goupnp v1.0.0/go.mod h1:n9v9KO1tAxYH82qOn+UTIFQDmx5n1Zxd/ClZDMX7Bnc= -github.com/huin/goupnp v1.0.3 h1:N8No57ls+MnjlB+JPiCVSOyy/ot7MJTqlo7rn+NYSqQ= -github.com/huin/goupnp v1.0.3/go.mod h1:ZxNlw5WqJj6wSsRK5+YfflQGXYfccj5VgQsMNixHM7Y= +github.com/huin/goupnp v1.1.0 h1:gEe0Dp/lZmPZiDFzJJaOfUpOvv2MKUkoBX8lDrn9vKU= +github.com/huin/goupnp v1.1.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o= github.com/iancoleman/orderedmap v0.0.0-20190318233801-ac98e3ecb4b0/go.mod h1:N0Wam8K1arqPXNWjMo21EXnBPOPp36vB07FNRdD2geA= github.com/iancoleman/orderedmap v0.1.0 h1:2orAxZBJsvimgEBmMWfXaFlzSG2fbQil5qzP3F6cCkg= @@ -929,7 +928,6 @@ github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/u 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 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= @@ -953,13 +951,13 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= -github.com/klauspost/compress v1.15.12 h1:YClS/PImqYbn+UILDnqxQCZ3RehC9N318SU3kElDUEM= -github.com/klauspost/compress v1.15.12/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= +github.com/klauspost/compress v1.16.4 h1:91KN02FnsOYhuunwU4ssRe8lc2JosWmizWa91B5v1PU= +github.com/klauspost/compress v1.16.4/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= 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 h1:sxCkb+qR91z4vsqw4vGGZlDgPz3G7gjaLyK3V8y70BU= -github.com/klauspost/cpuid/v2 v2.2.3/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= +github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= +github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= 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= @@ -967,8 +965,8 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxv github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/koron/go-ssdp v0.0.0-20180514024734-4a0ed625a78b/go.mod h1:5Ky9EC2xfoUKUor0Hjgi2BJhCSXJfMOFlmyYrVKGQMk= github.com/koron/go-ssdp v0.0.0-20191105050749-2e1c40ed0b5d/go.mod h1:5Ky9EC2xfoUKUor0Hjgi2BJhCSXJfMOFlmyYrVKGQMk= -github.com/koron/go-ssdp v0.0.3 h1:JivLMY45N76b4p/vsWGOKewBQu6uf39y8l+AQ7sDKx8= -github.com/koron/go-ssdp v0.0.3/go.mod h1:b2MxI6yh02pKrsyNoQUsk4+YNikaGhe4894J+Q5lDvA= +github.com/koron/go-ssdp v0.0.4 h1:1IDwrghSKYM7yLf7XCzbByg2sJ/JcNOZRXS2jczTwz0= +github.com/koron/go-ssdp v0.0.4/go.mod h1:oDXq+E5IL5q0U8uSBcoAXzTzInwy5lEgC91HoKtbmZk= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= @@ -1007,10 +1005,10 @@ 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.26.3 h1:6g/psubqwdaBqNNoidbRKSTBEYgaOuKBhHl8Q5tO+PM= -github.com/libp2p/go-libp2p v0.26.3/go.mod h1:x75BN32YbwuY0Awm2Uix4d4KOz+/4piInkp4Wr3yOo8= -github.com/libp2p/go-libp2p-asn-util v0.2.0 h1:rg3+Os8jbnO5DxkC7K/Utdi+DkY3q/d1/1q+8WeNAsw= -github.com/libp2p/go-libp2p-asn-util v0.2.0/go.mod h1:WoaWxbHKBymSN41hWSq/lGKJEca7TNm58+gGJi2WsLI= +github.com/libp2p/go-libp2p v0.27.1 h1:k1u6RHsX3hqKnslDjsSgLNURxJ3O1atIZCY4gpMbbus= +github.com/libp2p/go-libp2p v0.27.1/go.mod h1:FAvvfQa/YOShUYdiSS03IR9OXzkcJXwcNA2FUCh9ImE= +github.com/libp2p/go-libp2p-asn-util v0.3.0 h1:gMDcMyYiZKkocGXDQ5nsUQyquC9+H+iLEQHwOCZ7s8s= +github.com/libp2p/go-libp2p-asn-util v0.3.0/go.mod h1:B1mcOrKUE35Xq/ASTmQ4tN3LNzVVaMNmq2NACuqyB9w= github.com/libp2p/go-libp2p-autonat v0.1.0/go.mod h1:1tLf2yXxiE/oKGtDwPYWTSYG3PtvYlJmg7NeVtPRqH8= github.com/libp2p/go-libp2p-autonat v0.1.1/go.mod h1:OXqkeGOY2xJVWKAGV2inNF5aKN/djNA3fdpCWloIudE= github.com/libp2p/go-libp2p-autonat v0.2.0/go.mod h1:DX+9teU4pEEoZUqR1PiMlqliONQdNbfzE1C718tcViI= @@ -1229,8 +1227,8 @@ github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Ky 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= -github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= -github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98= +github.com/mattn/go-isatty v0.0.18/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= @@ -1246,8 +1244,8 @@ github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3N github.com/miekg/dns v1.1.12/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.28/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= -github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA= -github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= +github.com/miekg/dns v1.1.53 h1:ZBkuHr5dxHtB1caEOlZTLPo7D3L3TWckgUUs/RHfDxw= +github.com/miekg/dns v1.1.53/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY= github.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c h1:bzE/A84HN25pxAuk9Eej1Kz9OUelF97nAc82bDquQI8= github.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c/go.mod h1:0SQS9kMwD2VsyFEB++InYyBJroV/FRmBgcydeSUcJms= github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b h1:z78hV3sbSMAUoyUMM0I83AUIT6Hu17AWfgjzIbtrYFc= @@ -1278,7 +1276,6 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8= github.com/mr-tron/base58 v1.1.1/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8= github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= @@ -1303,8 +1300,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.8.0 h1:aqjksEcqK+iD/Foe1RRFsGZh8+XFiGo7FgUCZlpv3LU= -github.com/multiformats/go-multiaddr v0.8.0/go.mod h1:Fs50eBDWvZu+l3/9S6xAE7ZYj6yhxlvaVZjakWN7xRs= +github.com/multiformats/go-multiaddr v0.9.0 h1:3h4V1LHIk5w4hJHekMKWALPXErDfz/sggzwC/NcqbDQ= +github.com/multiformats/go-multiaddr v0.9.0/go.mod h1:mI67Lb1EeTOYb8GQfL/7wpIZwc46ElrvzhYnoJOmTT0= 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= @@ -1323,8 +1320,8 @@ github.com/multiformats/go-multiaddr-net v0.1.5/go.mod h1:ilNnaM9HbmVFqsb/qcNysj github.com/multiformats/go-multiaddr-net v0.2.0/go.mod h1:gGdH3UXny6U3cKKYCvpXI5rnK7YaOIEOPVDI9tsJbEA= github.com/multiformats/go-multibase v0.0.1/go.mod h1:bja2MqRZ3ggyXtZSEDKpl0uO/gviWFaSteVbWT51qgs= github.com/multiformats/go-multibase v0.0.3/go.mod h1:5+1R4eQrT3PkYZ24C3W2Ue2tPwIdYQD509ZjSb5y9Oc= -github.com/multiformats/go-multibase v0.1.1 h1:3ASCDsuLX8+j4kx58qnJ4YFq/JWTJpCyDW27ztsVTOI= -github.com/multiformats/go-multibase v0.1.1/go.mod h1:ZEjHE+IsUrgp5mhlEAYjMtZwK1k4haNkcaPg9aoe1a8= +github.com/multiformats/go-multibase v0.2.0 h1:isdYCVLvksgWlMW9OZRYJEa9pZETFivncJHmHnnd87g= +github.com/multiformats/go-multibase v0.2.0/go.mod h1:bFBZX4lKCA/2lyOFSAoKH5SS6oPyjtnzK/XTFDPkNuk= github.com/multiformats/go-multicodec v0.3.0/go.mod h1:qGGaQmioCDh+TeFOnxrbU0DaIPw8yFgAZgFG0V7p1qQ= github.com/multiformats/go-multicodec v0.3.1-0.20210902112759-1539a079fd61/go.mod h1:1Hj/eHRaVWSXiSNNfcEPcwZleTmdNP81xlxDLnWU9GQ= github.com/multiformats/go-multicodec v0.6.0/go.mod h1:GUC8upxSBE4oG+q3kWZRw/+6yC1BqO550bjhWsJbZlw= @@ -1381,15 +1378,15 @@ 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/v2 v2.5.1 h1:auzK7OI497k6x4OvWq+TKAcpcSAlod0doAH72oIN0Jw= -github.com/onsi/ginkgo/v2 v2.5.1/go.mod h1:63DOGlLAH8+REH8jUGdL3YpCpu7JODesutUjdENfUAc= +github.com/onsi/ginkgo/v2 v2.9.2 h1:BA2GMJOtfGAfagzYtrAlufIP0lq6QERkFmHLMLPwFSU= +github.com/onsi/ginkgo/v2 v2.9.2/go.mod h1:WHcJJG2dIlcCqVfBAwUCrJxSPFb6v4azBwgxeMeDuts= github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= 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.24.0 h1:+0glovB9Jd6z3VR+ScSwQqXVTIfJcGA9UBM8yzQxhqg= +github.com/onsi/gomega v1.27.4 h1:Z2AnStgsdSayCMDiCU42qIz+HLqEPcgiOCXjAU/w+8E= 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= @@ -1446,7 +1443,6 @@ github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3O 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= -github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= @@ -1467,9 +1463,8 @@ github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB8 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= github.com/prometheus/common v0.28.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE= -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/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= @@ -1479,19 +1474,18 @@ github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsT github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= -github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= +github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= +github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= github.com/prometheus/statsd_exporter v0.21.0 h1:hA05Q5RFeIjgwKIYEdFd59xu5Wwaznf33yKI+pyX6T8= github.com/prometheus/statsd_exporter v0.21.0/go.mod h1:rbT83sZq2V+p73lHhPZfMc3MLCHmSHelCh9hSGYNLTQ= github.com/puzpuzpuz/xsync/v2 v2.4.0 h1:5sXAMHrtx1bg9nbRZTOn8T4MkWe5V+o8yKRH02Eznag= github.com/puzpuzpuz/xsync/v2 v2.4.0/go.mod h1:gD2H2krq/w52MfPLE+Uy64TzJDVY7lP2znR9qmR35kU= github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= -github.com/quic-go/qtls-go1-19 v0.2.1 h1:aJcKNMkH5ASEJB9FXNeZCyTEIHU1J7MmHyz1Q1TSG1A= -github.com/quic-go/qtls-go1-19 v0.2.1/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI= -github.com/quic-go/qtls-go1-20 v0.1.1 h1:KbChDlg82d3IHqaj2bn6GfKRj84Per2VGf5XV3wSwQk= -github.com/quic-go/qtls-go1-20 v0.1.1/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM= +github.com/quic-go/qtls-go1-19 v0.3.2 h1:tFxjCFcTQzK+oMxG6Zcvp4Dq8dx4yD3dDiIiyc86Z5U= +github.com/quic-go/qtls-go1-19 v0.3.2/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI= +github.com/quic-go/qtls-go1-20 v0.2.2 h1:WLOPx6OY/hxtTxKV1Zrq20FtXtDEkeY00CGQm8GEa3E= +github.com/quic-go/qtls-go1-20 v0.2.2/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM= github.com/quic-go/quic-go v0.33.0 h1:ItNoTDN/Fm/zBlq769lLJc8ECe9gYaW40veHCCco7y0= github.com/quic-go/quic-go v0.33.0/go.mod h1:YMuhaAV9/jIu0XclDXwZPAsP/2Kgr5yMYhe9oxhhOFA= github.com/quic-go/webtransport-go v0.5.2 h1:GA6Bl6oZY+g/flt00Pnu0XtivSD8vukOu3lYhJjnGEk= @@ -1768,10 +1762,10 @@ go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -go.uber.org/dig v1.15.0 h1:vq3YWr8zRj1eFGC7Gvf907hE0eRjPTZ1d3xHadD6liE= -go.uber.org/dig v1.15.0/go.mod h1:pKHs0wMynzL6brANhB2hLMro+zalv1osARTviTcqHLM= -go.uber.org/fx v1.18.2 h1:bUNI6oShr+OVFQeU8cDNbnN7VFsu+SsjHzUF51V/GAU= -go.uber.org/fx v1.18.2/go.mod h1:g0V1KMQ66zIRk8bLu3Ea5Jt2w/cHlOIp4wdRsgh0JaY= +go.uber.org/dig v1.16.1 h1:+alNIBsl0qfY0j6epRubp/9obgtrObRAc5aD+6jbWY8= +go.uber.org/dig v1.16.1/go.mod h1:557JTAUZT5bUK0SvCwikmLPPtdQhfvLYtO5tJgQSbnk= +go.uber.org/fx v1.19.2 h1:SyFgYQFr1Wl0AYstE8vyYIzP4bFz2URrScjwC4cwUvY= +go.uber.org/fx v1.19.2/go.mod h1:43G1VcqSzbIv77y00p1DRAsyZS8WdzuYdhZXmEUkMyQ= 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.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= @@ -1781,8 +1775,8 @@ go.uber.org/multierr v1.4.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+ go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= -go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= -go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= @@ -1835,8 +1829,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.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc= -golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= +golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= +golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= 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= @@ -1850,8 +1844,8 @@ golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EH golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/exp v0.0.0-20210615023648-acb5c1269671/go.mod h1:DVyR6MI7P4kEQgvZJSj1fQGrWIi2RzIrfYWycwheUAc= golang.org/x/exp v0.0.0-20210714144626-1041f73d31d8/go.mod h1:DVyR6MI7P4kEQgvZJSj1fQGrWIi2RzIrfYWycwheUAc= -golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb h1:PaBZQdo+iSDyHT053FjUCgZQ/9uqVwPOcl7KSWhKn6w= -golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= +golang.org/x/exp v0.0.0-20230321023759-10a507213a29 h1:ooxPy7fPvB4kwsA2h+iBNHkAbp/4JxTSwCmvdjEYmug= +golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -1878,8 +1872,8 @@ 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.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA= -golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= +golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1935,14 +1929,11 @@ golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLd golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210423184538-5f58ad60dda6/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -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.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= -golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -1951,7 +1942,6 @@ golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4Iltr golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -2058,7 +2048,6 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc 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= @@ -2068,15 +2057,16 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc 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.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= +golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 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 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw= +golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= 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= @@ -2086,8 +2076,8 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 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 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= 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= @@ -2153,10 +2143,9 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f 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.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.3.0 h1:SrNbZl6ECOS1qFzgTdQfWXZM9XBkiA6tkFrH9YSTPHM= -golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= +golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= +golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -2268,8 +2257,8 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= -google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= +google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/node/impl/net/net.go b/node/impl/net/net.go index 271c56ea537..5a7382eccf4 100644 --- a/node/impl/net/net.go +++ b/node/impl/net/net.go @@ -143,19 +143,7 @@ func (a *NetAPI) NetAutoNatStatus(ctx context.Context) (i api.NatInfo, err error }, nil } - var maddr string - if autonat.Status() == network.ReachabilityPublic { - pa, err := autonat.PublicAddr() - if err != nil { - return api.NatInfo{}, err - } - maddr = pa.String() - } - - return api.NatInfo{ - Reachability: autonat.Status(), - PublicAddr: maddr, - }, nil + return api.NatInfo{Reachability: autonat.Status()}, nil } func (a *NetAPI) NetAgentVersion(ctx context.Context, p peer.ID) (string, error) { From 48365f4f08b3f4126ec40cdfa8a83073cc16d0bc Mon Sep 17 00:00:00 2001 From: Marten Seemann Date: Wed, 10 May 2023 11:52:55 +0300 Subject: [PATCH 231/243] return all the public addresses in NatInfo --- api/api_net.go | 1 + cli/net.go | 3 +++ documentation/en/api-v0-methods-miner.md | 5 ++++- documentation/en/api-v0-methods.md | 5 ++++- documentation/en/api-v1-unstable-methods.md | 5 ++++- node/impl/net/net.go | 17 +++++++++++++++-- 6 files changed, 31 insertions(+), 5 deletions(-) diff --git a/api/api_net.go b/api/api_net.go index c1a8f8514c9..cfcd8d87e06 100644 --- a/api/api_net.go +++ b/api/api_net.go @@ -73,4 +73,5 @@ type CommonNet interface { type NatInfo struct { Reachability network.Reachability + PublicAddrs []string } diff --git a/cli/net.go b/cli/net.go index 1266bb923ee..516b44b7f75 100644 --- a/cli/net.go +++ b/cli/net.go @@ -445,6 +445,9 @@ var NetReachability = &cli.Command{ } fmt.Println("AutoNAT status: ", i.Reachability.String()) + if len(i.PublicAddrs) > 0 { + fmt.Println("Public address:", i.PublicAddrs) + } return nil }, } diff --git a/documentation/en/api-v0-methods-miner.md b/documentation/en/api-v0-methods-miner.md index 2be9e5891ba..997778069b5 100644 --- a/documentation/en/api-v0-methods-miner.md +++ b/documentation/en/api-v0-methods-miner.md @@ -1701,7 +1701,10 @@ Inputs: `null` Response: ```json { - "Reachability": 1 + "Reachability": 1, + "PublicAddrs": [ + "string value" + ] } ``` diff --git a/documentation/en/api-v0-methods.md b/documentation/en/api-v0-methods.md index 8c4db7f8f0e..522e1a6de45 100644 --- a/documentation/en/api-v0-methods.md +++ b/documentation/en/api-v0-methods.md @@ -3709,7 +3709,10 @@ Inputs: `null` Response: ```json { - "Reachability": 1 + "Reachability": 1, + "PublicAddrs": [ + "string value" + ] } ``` diff --git a/documentation/en/api-v1-unstable-methods.md b/documentation/en/api-v1-unstable-methods.md index bc158ea11c1..305592c6e98 100644 --- a/documentation/en/api-v1-unstable-methods.md +++ b/documentation/en/api-v1-unstable-methods.md @@ -5021,7 +5021,10 @@ Inputs: `null` Response: ```json { - "Reachability": 1 + "Reachability": 1, + "PublicAddrs": [ + "string value" + ] } ``` diff --git a/node/impl/net/net.go b/node/impl/net/net.go index 5a7382eccf4..5341092ce28 100644 --- a/node/impl/net/net.go +++ b/node/impl/net/net.go @@ -16,6 +16,7 @@ import ( "github.com/libp2p/go-libp2p/p2p/net/swarm" "github.com/libp2p/go-libp2p/p2p/protocol/ping" ma "github.com/multiformats/go-multiaddr" + manet "github.com/multiformats/go-multiaddr/net" "go.uber.org/fx" "golang.org/x/xerrors" @@ -134,7 +135,7 @@ func (a *NetAPI) NetFindPeer(ctx context.Context, p peer.ID) (peer.AddrInfo, err return a.Router.FindPeer(ctx, p) } -func (a *NetAPI) NetAutoNatStatus(ctx context.Context) (i api.NatInfo, err error) { +func (a *NetAPI) NetAutoNatStatus(context.Context) (i api.NatInfo, err error) { autonat := a.RawHost.(*basichost.BasicHost).GetAutoNat() if autonat == nil { @@ -143,7 +144,19 @@ func (a *NetAPI) NetAutoNatStatus(ctx context.Context) (i api.NatInfo, err error }, nil } - return api.NatInfo{Reachability: autonat.Status()}, nil + var addrs []string + if autonat.Status() == network.ReachabilityPublic { + for _, addr := range a.Host.Addrs() { + if manet.IsPublicAddr(addr) { + addrs = append(addrs, addr.String()) + } + } + } + + return api.NatInfo{ + Reachability: autonat.Status(), + PublicAddrs: addrs, + }, nil } func (a *NetAPI) NetAgentVersion(ctx context.Context, p peer.ID) (string, error) { From 3a7b52a66f2924f456144f15081dd0705c171065 Mon Sep 17 00:00:00 2001 From: Marten Seemann Date: Wed, 10 May 2023 11:53:30 +0300 Subject: [PATCH 232/243] update go-libp2p to v0.27.3 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index c84c6b70efe..c842cb3d64b 100644 --- a/go.mod +++ b/go.mod @@ -116,7 +116,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.27.1 + github.com/libp2p/go-libp2p v0.27.3 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.21.1 diff --git a/go.sum b/go.sum index 078aed73a89..e3aa2e68c5e 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.27.1 h1:k1u6RHsX3hqKnslDjsSgLNURxJ3O1atIZCY4gpMbbus= -github.com/libp2p/go-libp2p v0.27.1/go.mod h1:FAvvfQa/YOShUYdiSS03IR9OXzkcJXwcNA2FUCh9ImE= +github.com/libp2p/go-libp2p v0.27.3 h1:tkV/zm3KCZ4R5er9Xcs2pt0YNB4JH0iBfGAtHJdLHRs= +github.com/libp2p/go-libp2p v0.27.3/go.mod h1:FAvvfQa/YOShUYdiSS03IR9OXzkcJXwcNA2FUCh9ImE= github.com/libp2p/go-libp2p-asn-util v0.3.0 h1:gMDcMyYiZKkocGXDQ5nsUQyquC9+H+iLEQHwOCZ7s8s= github.com/libp2p/go-libp2p-asn-util v0.3.0/go.mod h1:B1mcOrKUE35Xq/ASTmQ4tN3LNzVVaMNmq2NACuqyB9w= github.com/libp2p/go-libp2p-autonat v0.1.0/go.mod h1:1tLf2yXxiE/oKGtDwPYWTSYG3PtvYlJmg7NeVtPRqH8= From 7831ec64d78eca0ba4a0ab4fb8a5274e33486aff Mon Sep 17 00:00:00 2001 From: jennijuju Date: Wed, 31 May 2023 14:31:02 -0400 Subject: [PATCH 233/243] update libp2p --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index c842cb3d64b..f741698abee 100644 --- a/go.mod +++ b/go.mod @@ -116,7 +116,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.27.3 + github.com/libp2p/go-libp2p v0.27.4 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.21.1 diff --git a/go.sum b/go.sum index e3aa2e68c5e..438f74e4898 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.27.3 h1:tkV/zm3KCZ4R5er9Xcs2pt0YNB4JH0iBfGAtHJdLHRs= -github.com/libp2p/go-libp2p v0.27.3/go.mod h1:FAvvfQa/YOShUYdiSS03IR9OXzkcJXwcNA2FUCh9ImE= +github.com/libp2p/go-libp2p v0.27.4 h1:zliwN9xuzCBqCtWe0XjLKJGK6EIZTkp9L1e15wBpiOU= +github.com/libp2p/go-libp2p v0.27.4/go.mod h1:oMfQGTb9CHnrOuSM6yMmyK2lXz3qIhnkn2+oK3B1Y2g= github.com/libp2p/go-libp2p-asn-util v0.3.0 h1:gMDcMyYiZKkocGXDQ5nsUQyquC9+H+iLEQHwOCZ7s8s= github.com/libp2p/go-libp2p-asn-util v0.3.0/go.mod h1:B1mcOrKUE35Xq/ASTmQ4tN3LNzVVaMNmq2NACuqyB9w= github.com/libp2p/go-libp2p-autonat v0.1.0/go.mod h1:1tLf2yXxiE/oKGtDwPYWTSYG3PtvYlJmg7NeVtPRqH8= From a750f29a301bbca14857c43c598960fcf293e989 Mon Sep 17 00:00:00 2001 From: Shrenuj Bansal Date: Wed, 31 May 2023 16:23:48 -0400 Subject: [PATCH 234/243] Update changelog and build version --- CHANGELOG.md | 7 +++++-- build/openrpc/full.json.gz | Bin 33882 -> 33882 bytes build/openrpc/gateway.json.gz | Bin 9539 -> 9539 bytes build/openrpc/miner.json.gz | Bin 15944 -> 15944 bytes build/openrpc/worker.json.gz | Bin 5245 -> 5245 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 +- 9 files changed, 9 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 146b7cb626e..5133d8807f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,8 @@ # Lotus changelog -# v1.23.1-rc2 / 2023-05-10 +# v1.23.1-rc3 / 2023-05-31 -This is the first release candidate of the upcoming optional feature release of Lotus v1.23.1. +This is the 3rd release candidate of the upcoming optional feature release of Lotus v1.23.1. **☢️ Upgrade Warnings ☢️** @@ -26,6 +26,8 @@ This is the first release candidate of the upcoming optional feature release of - feat: shed: incoming block-sub chainwatch tool ([filecoin-project/lotus#10513](https://github.com/filecoin-project/lotus/pull/10513)) ## Improvements +- feat: chainstore: sharded mutex for filling chain height index +- Check if epoch is negative in GetTipsetByHeight - fix: sched: Address GET_32G_MAX_CONCURRENT regression - fix: cli: Hide legacy markets cmds - fix: ci: Debugging m1 build @@ -74,6 +76,7 @@ This is the first release candidate of the upcoming optional feature release of - Merge branch 'feat/new-gw-methods' ## Dependencies +- devs: update libp2p #10937 - chore: deps: update to FVM 3.3.1 ([filecoin-project/lotus#10786](https://github.com/filecoin-project/lotus/pull/10786)) - chore: boxo: migrate from go-libipfs to boxo ([filecoin-project/lotus#10562](https://github.com/filecoin-project/lotus/pull/10562)) - chore: deps: update to go-state-types v0.11.0-alpha-3 ([filecoin-project/lotus#10606](https://github.com/filecoin-project/lotus/pull/10606)) diff --git a/build/openrpc/full.json.gz b/build/openrpc/full.json.gz index 98df5d36bd3f8a3053a10576f9d0431e85f97ebc..cea86aa2e113b2164cba60ebe09e89e111599397 100644 GIT binary patch delta 23 fcmccB!E~#GX+jUn0{W_$}d&Bg!#g&7Ki delta 23 fcmccB!E~#GX+jUPn>juS0041<2^# Date: Sat, 3 Jun 2023 10:09:17 -0400 Subject: [PATCH 235/243] chore: deps: update to go-libp2p 0.27.5 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index f741698abee..9a42946dfb9 100644 --- a/go.mod +++ b/go.mod @@ -116,7 +116,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.27.4 + github.com/libp2p/go-libp2p v0.27.5 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.21.1 diff --git a/go.sum b/go.sum index 438f74e4898..f794dab7f39 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.27.4 h1:zliwN9xuzCBqCtWe0XjLKJGK6EIZTkp9L1e15wBpiOU= -github.com/libp2p/go-libp2p v0.27.4/go.mod h1:oMfQGTb9CHnrOuSM6yMmyK2lXz3qIhnkn2+oK3B1Y2g= +github.com/libp2p/go-libp2p v0.27.5 h1:KwA7pXKXpz8hG6Cr1fMA7UkgleogcwQj0sxl5qquWRg= +github.com/libp2p/go-libp2p v0.27.5/go.mod h1:oMfQGTb9CHnrOuSM6yMmyK2lXz3qIhnkn2+oK3B1Y2g= github.com/libp2p/go-libp2p-asn-util v0.3.0 h1:gMDcMyYiZKkocGXDQ5nsUQyquC9+H+iLEQHwOCZ7s8s= github.com/libp2p/go-libp2p-asn-util v0.3.0/go.mod h1:B1mcOrKUE35Xq/ASTmQ4tN3LNzVVaMNmq2NACuqyB9w= github.com/libp2p/go-libp2p-autonat v0.1.0/go.mod h1:1tLf2yXxiE/oKGtDwPYWTSYG3PtvYlJmg7NeVtPRqH8= From aa0237949e6914bbe5eae933d17644749e5c41af Mon Sep 17 00:00:00 2001 From: Shrenuj Bansal Date: Mon, 5 Jun 2023 16:18:23 -0500 Subject: [PATCH 236/243] chore: update build version and changelog for 1.23.1-rc4 --- CHANGELOG.md | 5 +++-- build/openrpc/full.json.gz | Bin 33882 -> 33882 bytes build/openrpc/gateway.json.gz | Bin 9539 -> 9538 bytes build/openrpc/miner.json.gz | Bin 15944 -> 15944 bytes build/openrpc/worker.json.gz | Bin 5245 -> 5245 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 +- 9 files changed, 7 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5133d8807f3..3105756a75e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -76,11 +76,12 @@ This is the 3rd release candidate of the upcoming optional feature release of Lo - Merge branch 'feat/new-gw-methods' ## Dependencies +- chore: deps: update to go-libp2p 0.27.5 - devs: update libp2p #10937 - chore: deps: update to FVM 3.3.1 ([filecoin-project/lotus#10786](https://github.com/filecoin-project/lotus/pull/10786)) -- chore: boxo: migrate from go-libipfs to boxo ([filecoin-project/lotus#10562](https://github.com/filecoin-project/lotus/pull/10562)) +- chore: boxo: migrate from go-libipfs to boxo ([filecoin-project/lotus#10562](https://github.com/filecoin-project/lotus/pull/10562)) - chore: deps: update to go-state-types v0.11.0-alpha-3 ([filecoin-project/lotus#10606](https://github.com/filecoin-project/lotus/pull/10606)) -- chore: bump go-libipfs ([filecoin-project/lotus#10531](https://github.com/filecoin-project/lotus/pull/10531)) +- chore: bump go-libipfs ([filecoin-project/lotus#10531](https://github.com/filecoin-project/lotus/pull/10531)) ## Others - feat:networking: (Synchronous) Consistent Broadcast for Filecoin EC ([filecoin-project/lotus#9858](https://github.com/filecoin-project/lotus/pull/9858)) diff --git a/build/openrpc/full.json.gz b/build/openrpc/full.json.gz index cea86aa2e113b2164cba60ebe09e89e111599397..bc5faec9254b3eec98e6aefcf7a2c7620c2d20c7 100644 GIT binary patch literal 33882 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

9pYMvypNTO11jxj;} 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%0A!ZR!V5 zbG!9KHrH1**(=rZ_yF6gn;)PnfS-fuC?(mE#mHuHorDmrR@;a2=Ry3uSD4Fo!-xg$iY2DzvHku$!ez zS4uC#6VdPOTk`e};z$BWi}SJ^l|wL!*bp!jEOuX$I};aCpXaJEXTgtYFX}upf+n2_ z>|vIe`AYx5wAgC#oW{kUvlk|&Uta(CcKBb#yW1o;W|qG^c;S7k?8(NvQuDP;udlyc z-8k)$g7Sr_M=ciL=)G!j{Nb*x`KIe+)@tclKK(DG8#jH)lEYU@`}2~%w>|m%ckW)P z|4O#mA6mA=+E+N$Y_j_Ev2@F$q~gD(9pT)5+t(YnJSe@rTI1G2$H>k1=IlC{HS;0o zM)4^xIG8zuHyAx#!s}n^XsYyW)%(}SQeKAKdCC?uEB>i*oh#?}8~5V=0{In6SwM}%QSy{;sOZ7{qGt9U#*h`T;yxz_tCMZOVHAtG4bp zPmfESEi}5$ua{EB*?H@8V11L;hgYXm{*W?IApPg@><3}rZkZchdQ}d*FZqwm#u=9x zzviCo+3RinE7|(!kC*Rnl*uprXM6GU`Q5W-zTw}VZF;Bdxk};PSGr=KT4vX?ovySv zaYfCYqyN3S?;8Khrx(PrZJpv+ma^IQyZqW!|6LA#`CcApwkvG5!-BIltGPp7@iD&o zyk)13hRKHo;_Oe)tua;++i9FQW9DuilkaR?3j>{3w`wd{_~~Fr&z z>O6O~W4YjWM(y#cmol2o%f4+~`K8YrcvIix+uNTU<2t~hnPjx_k;uK74DX&la{T_| z%%Wb;Q&q3i-;3Uy`lT*Zd|88A^>IPB7U|mC(JkVdA=Ub8{c}Td sBfabf>(JEX?Mv(={XT!GsWtfVeE$0XfBrKtF#P{NG4#aekkf1o08A`BxBvhE literal 33882 zcmZ^qLzE`n6XxHtZQHhO+qUhhE*o98ZFJe{vTfV8r@#MV&TJ+Z_r$%6Tx8r65&4TJ z2%;eY|9gHeyM0_X+7cn&)f7AYvq?)lO~>j{cDUVW{7CO&lRWKaOg#=R@gqV=W2gec z0bl8=Js)(uuYQollsc(QCyOy+z`wyEU4J^}EI%)m2om}{yg$0HYW9}x>vjfAg?&B* zd~jV5o}PWmlEY!M^YDUREGWADZot7{RSK z>~bPnLA}i`1O2~I5HBm{HFyPiVIe)mX6?Yf(tI6pK@BK-A${|}aoY=z8SWVL+I#vj zzITt%-rU5#pX&Ud-ucj{PZ-;7u3pB|RZy`EU1R$&nJ)o%Ye8J7!BD&DVYs(gzz1#H zXlGA8{II*aaF&=646Hr=S1Msg6ti_h7@u=DU=gAC7$`#illS%*{3d4g#S;7BEFvzH zjpW7Il z^LGZLd))@Tg%mqQ5(0ja(_cJ;K+Z+d^!Ff#g7mqv`6IJ+i56e@9V$CF5!{Jcwlf08 z{4gVgf-d7ow~X!APd-Mw|9r(V?c4IR;h>^rI#KYjL+)+Y^$bG&`aXqnaoJigG15Cl zbU^tE_x{Qukni#G!y#sBBmI-NaFw$cd#W-7&67=Z!ks~Vm8uyld4`=NQ{hZ@M48M` z5*A%Dsy1ak#KIa}aNV5r1ku z$bh*s(Lm&+w7X@(45m4Gz(uS}|F@4~`0Xk1nKz3PywKLN5uIc3YRBmdMUl@P? z^y0P7y^*_CpE>WNZwDOSy%oN`)6#y#l{;g}sumQ3&4t@)HN~Gvusm8YfSgirP|Exe zb%zibULh7+uFrGv>*_DyqM#ym4@outK#G&o32DzWNJ%l@EkX}5Vr>L*4_2@*p2MqV zJZ6a6nHBD|1IQS*9Wb)k(gy{R4pg767Ebi4(x1Q|up8eo3dCSaueG6)95=-xKpE^1r4 zo&-$B9@n!WA$}S5xdD>v31Qw#VDv9hT_OxhAXPuP(3-g;1-L|+7r+3 zA-P+dNy7RdVLt#`!_vEvlOTeM|9IIxjJ=fo@n*qpC+T3>{~;IgkNE2B)N(wSv{$@Y zi6i)ez7T@xbRQOGwiMy9oPw}Z4T17%x3U!UgJG0_*fR|Zg8Ae~95(HJASu#a z_j4>%U$>wmAtrx1aG?`(j$;TxvND?;o{=K>DsBW*4vO8G;SjN#Ork9ZQ+t_7W-Q0kfnlU~3GCT4-gQE)kP; z9kY;CoZYyQ1K${!lds$99GqrLtoiZ!-lNTTo!_|m;_5Xgz6HdhU z8AYnOU>5M%VdN?%+Ux%E_!3J&Ky;<#BIoVPOmM+QZf)&lxqOKw3@>@|dlVzFcnq>x z-|>G|?%jU&xvvZMp^r#K;S3H;kJ9tA6uZayyMXmCrh@-V6iMIpe5?4j5@g1D<1 z?VyN)cm!<2RL{8lKgWL0j1n%-@tYTH+p)f10C1qLv@I#Q;4;QfFt4?MGttZzQ{IHB ztMr)~B9vmhNWr8AES~~+Jyh<}r#6kX{1nxUTtJrEu+fb)1%2#f(7pTyds89NmiaqTCI(FCJWCEFWXp{ef6I@fRnGS`Jn9%VvR$XBTIs8So10N7>s73-BpW-7og)G!@hx)(N2wA2k{fiBj4VkT!>-Fwt^cereo-}7XubYAv%*5eMPMiPJJb)zpy-p3qp)A!1hU?r|0mTg_-D% zApN>|w;|H<io0u1M3Xre{gVdg9YeggALEJ+NW_ls z4=?v$@1s8Z6}wQGJ2IXEl^Gi}xJ7|ESs~+-U?Zao87Mj}z9@0dB`VUILs!VZlR)Cg zcAq|Gw)2#B^BGwftkxUviSi#FZy1cvDueum8AR9JZjl;Pct^&6-y=03K;j7tczL1c zbHSgy_gi4auWo3_gL6?ui#zsucsi>#?Twi1h%ICphIrtP&(h^>HJ)X4Uhw?vpfGpk zaQfd&JA^fIbsx8yyj){%U;Wu;U~0k`y{lvSS{5K>UjHOIeJN%tHVw|eC_G?rjM#$5 z&{DaP@Sbl4$+aeDZPrwFDG*cRE0Ybd_fBN9qsojUzd8jo4C#K_w7=-cpYkbOF+H$O z0Gtlb6_viTv?{qEw{8{6Kv{LFUMcJORhT#JzVjI6<-P1}G|coduPs<7OMYu^6OVF?o&0KTEiXP z!3L}O>$ar&NOp#0519vsn}MwMe37xTD*VPwA#tSJaWGedN|IuV_x$?NlwqfS=P%WS z?wY0OS_41edo}LkMbR=#>m2Sn6!;4^$whbVP*JCuak#EerOL?wRWZmR2f1vW@q6Ne zle4SmSB<829?es(RZ`9;b01BCJraUP)_p(eB(IDDD_0<>g-hAr0f8 zSC4O7jRq4JPiNGd09_=v5JIjhOQ7Xp;OfiA+C;N48x*5w*vb*+&Q*f=F=4@s9HJPwr$)VWX{E>Y=slj>!ZYv(;oYHSg3UQQ#z zU>wx#=94G&Uys$BPD&-p3l1^E3cDq>2a|bY*G@hLG5C*RL=83WzNxmA(;q$U@CizK z`DMVUJi@k9uSw5W?hKsK*jb>o>AaT~S*A6f%**J@6o^Rcn*pPVOlxj70G7r<4RB@{ zUrkshm+Ym%k9S5eMxY$ZUL_{r)sEln3wEY^HKDEO+qLpD-4zpxXfo%oyqnJ6^u#8G zCk^mMdJDQ2VscB?-Sh0Jvw;=g8RvcJGm;SE75RsAiJC;ImVarK-ObQL6pp;LNq zEztQ~!rL(yw!iatc+yxJbZ2}+E)mEz{Z?)&%4_K#m}rqqllDq7)TOx{+G+~L`-&Y| zlP5w)ZEJ-$w{(gT-)y1SMlEED%~hlBP=c=aV3Acw;%ClN42A1T&Xatjl$q(3y?>!s zaTEEM$YJE;px*A}-}t-;Rug$y#{mV!S2cjjBc&OEXo#LxA10HLCeJ9&LY#9Jp%Q*H zywLzJ3SZU++s7D?e2<=RfxkE^==; zaC{mbAfFeV#y?;Jo<9u;rWE?OFAx>IJH4NG44quYNm#Wz`D&D}qNK?cPm?-DPF=E9 z37nHp3a3_`c1DugK5V!Ibd*qNb{$YZu!}p}L5F^51reptifSA?>JGR_mD27VKS&Q< ztECGyWg^1OwUwjy7%xlYoxZS276&#O^orZ`lBtE_FJB==f^oCX4P;^!(vy8m%VR^n z=abX{?lNOZ)_!2Zlp9Q3U!g_ZsJ3Q~UZ)qS!CbQnNdkfb!)vNr z8rH|p%gCx)FW{a0f~ueFOe?SVuQr%UwuK|BB2uEY*iz-d>?XH?W%OEU8D>lX3IN%} zYY^9Ptp+!1g>t^E-*QY=FeFt3^yngmC8!*8zT^rz@vs;0BYwiOc2wesuAQ7f^rx;6 z3#=M$<}|?$=@`2Il*r(DJIt1NdKlz>Pkt9@Up9M+68u@(xPP(o*AT2ENniTjdlm z?#|)*gEPc6lsK+b#sSC(D4_Z=1w3N?GPie(9VYJL?NQTT{(-XMS-P~*6uY@$^Yf5c zEcikLUjAG#h*?TAS62=Qt>RzXSkR1a^(a%TGmqlKd`)e*G?m)! zacc9jYa6H<0-fBwR&{0ETiaU|Jbzr!LyC`_@U)t()h5!&tF+NCP1PhmX*HKB`=Q`) z2LnV-_>(o%Bc3i5XVjv#jGL)ohaIB^O%S-U)v9l4kQto*6r$nBm7e{Ua7OdvjUm6_ z+DAn3f)a7~(W@e(i%6l{fVJ|O+mzP0x7V}1*BaHys;{(P(pDeN?eMhOb52d}(3(L~ ziAHa-o<}GtD7#c=YXy*JmpY_gxGI%~583LeGw@beU2!w&Lio55MV>%*)L;bF+9JmZ z^qfQ=z_E3&dx{8^d*vJbJqtQQs?SeEHrjDH3VoQQrAg=k<-U87oWdMA9?&6Ny}7kE zz5vr^VTjMnCo^(*ukYVG6pr`S`Ce8d?5=@9?iB3mwvg0dhexbhna2a?ShCm1LCd2gu0Ne#dyIKX1(;4Ud0|M92 z6@kT;Pl4h10{_u&>$o}4sL@lZBkSn6J*f9PJbA=6Pv&-FlR<|B`Z0$Jb|6~jHCR(+ z96Q6_$xI`0I(O*cEMAT~W4+y8H~Rn;+1iZP#(xlCg@p^e9NWje0`#Tj;_oV|?t6%5Kvw`=a2r+zKIfxA^Q86sB?O$yrc=({HrlPT zzvIkGi7Eb021LbU)<>Ra*k_=!j~apGFG9fy?)=+lb^GP$vOzYoJUWj7B5?xQx?H|! z;2ic`yv9LLWZGcT#WNvD>b-N@pd{Og#H0r6w=U#anmY- zdA)?yvo^p)6-~HA1HviZ1C*-^me42(ImDit=RTw|Bm#!(uwznP_4QD4{g7X{KlRfG zbU4}D4ZbDHJk7mD&4Yq@3e4w;xU4UkgaB$o#FaqwOQB(<+_&lVZ7hLR*wh(_i#lhl zIBL&8YxcvE4CuzYFg|1?NjB^BP|2i@~F$f{oerTQdK}kTVo#@*>F4&h9a

>Z?PQ-Vn_%M?9cDQ+gsV$K(}RwN*PWz1Q?KPUkl^6oPM7;VU0D# zn|D4g2@(){)!j6nx z_O$iFL*U5nkhW!92IM6s5-KUYd`P?@<#p&oIrPz#`8FPfOv72|4!3f+7XC}(6SSGh%F!UIZ6=fmnTZ#sy1iT0RdKM_N;!#r*kL6cB&c&B3+ z)YB;{M4H!F5)bw{w|A?2QE4BiUlL;K^e%@);S2e#buHumIQBo!Pz`Gi=PT2S-{((k zGkHpmR|Sm$^HR>7mm-2EWyY8AJ9!u?*wzmoKXM{m`Kz`b1MV#rWEt=>-YUH74X@7^ z#UA2mUKYa@Ayw=fkTqc1ISeWc7A9{q7DG3Fe@!`bvI!U2WDpR?OQA(9W>D!0gtyf~ z>1F!gfKW7O%TlFDEA5fLU#WfD_60;xH~n*D75`u=OZ;BMZ;*s+30SJIPq)lUFTiwV zZa4c2$Pl1Q& z25C>Za$8UVX`GLjNIrm2Mq5bey^!*47nTU>bh~OS(~m(>v(tA8?G-w$jf8|!qI#^H zqlaAgw`rC2oF~Vr;HOn4d%2Nog-2tkK@)5@MrpyC?>mXUBufca#KGUWJ9Pv=4jsm94A4g!Y5 z{^tyuI$0)OZQg76H-3zZ7If2EF~U=q#^xRPs%h<0F(M8#`O%OTbVR1mAs_X+!op~} zOVlm+;L^rT`t>^Lhxlnu3XfXOK2c4o`%F^bV@7GPed1HCPA`++jT$L7ln1Ie?6-***DQ4|oT&@%+)^@1<^_%$5}r9bq$Mjb~40F40KO zXmbnJ#RU)*<)5=3(ZE6TIqly)bN|Z9pvT-%sD50L=I+ILRso60GSR8usidF`MF!U5 zLl)4=3(`FrFl?sx$Y&s<9becKv}~x>ee$bd%3sR8Kg9(sB4qXj(iL|pGsH06SF@rb zgrT+R(0Wn@Zb?jX1Fa3wyA}#YFm9Sa-N*Y@kx)5_T5`nr%ET6-2?k#MCaOg#6ht(2 zRaY6w6U4pjIIsrR_3S1rjg%9Y^`?=j-Ew+k`h+>!nPd)0V$1niQYGv>`x#HGi+Raf zXJlVWGEI0U&T471leCwq{H2+`K4kOw9a{~^3F}N+u~xsAin~y6$kB|+Oj|5*Km|;R zuFWSuh!@?^1}WW z$}*JwEk;i2lawVZm6nTHF&_->!CNXdnWbl~aZYt+qQ+K}A4Wv!>3l%$QrsC@?JqA+ z?Zm9}*63d5A5Pa3VJI>u&6%M!UC)CI6xq5i6k5`(x%{;|{bvFs=&q%oZR7M2>GSR$W*QVo%6)x%C@E`?m3_5!5pQ=f zy`$;T)9q48sQSFU2$hobh8)hTdIW|9#?T+FQMt=y96c08y}rk9G+@kNp6MPe*GGluZ3fiyCZLoe)c%b zp|^@piGhtZXj?g5Y_V>TdYCDLslwZ^@T^&kBy&ZKQj`gqDBhU1w2!owmL;4%sy0iR z;u=rN?Cm2kX*v}sp1dG{9es8fcwJKv2gdSu%x_labsa2r+SxEL?ciDLkY&Oqqrm@d z*Z=gLprE&D`PF&^uQwT#rJrp*n(^vo3a8Y`bhAIWS}8a8(5sb}l+ogEW;jKtc}#-u+Q_sX$5=di_=qqzkeUt8{W%c%92`yYI%36f9=)CH(% zj4yvFL~Vvt!klHOQuh24@}=+8t}+l3h1F``;6XJ@Z^)c!f3_zj>XgMiE^>IzuX?pY zQCdi8w3*zrO`qVbb$zKIykr#e3sS-kNgY)1$TLiI(lYr#Cei zdE6>ZBGp|u{PhaueEJ;u?k_Rw_*=Z5SFSECe&af%o9vXX?tG$t{D!|<>w3q_fMT!v zRvD}MNf!E%x5LM&fZcAqNVOEG)CYU;4GF4AB7wSbF?=BNygLtC0LahzxOZc&OUSu- zV)GAmOO<}52ZJ`T@RE&!NeT3k=09v=c|~q_f?RpaPW5@y;58<8rY}z~Q}Vgx{Td@4 zo$6xY_F(A7kVY(Hl0aoh@*{7u_};&oxvhPrMPQ@9!)1a$>7o1wiBpwaG#yL*_4EDx z3*JRTx`O%hv$+>(q=*(DjmK!4lNnkGJ05Bm7iVp=2Ypqt+D~z(&fv7Emn1VqQx2Ch zcv|FR(e?Eb$ycVp$hp8V$Z*CO5l0EQk0Q>+EDBk$l2gc_d{|DIrB+=!mk+kC%l)nx z;e%z3!C^$kzF)}7fl{j*7Zj$#5k z1EdIzLAS0STif#8gvmns<;{;OOI4!!xFXMNj!J~IY(#{MBt?T}N+5XHHdMJ72T)tw z>RHi}X7`Z?52nm{rwxXw4U3OYPpXd{Ji&B4nQ@7YRVF{yMIYBF7S)@vwa?wyr& zdbgeYS`yWa(IK)(VWqKii9cQRZyJvZb!Tq4-jmqj52pNH?H?=LPI>7l94uSP$DKf; z+LH^o+c}g$8=YU---dKvnbRo}jwET`CCuxsr|ILp>~joG?osIc1dXe? zaxNELHB)#vMzIsS`Zy{SA)!f7?t>c$L{1y{EsXiLm}Vx1IrO%bG&W1c3Z?XURu1bZ=Y5apvRm;m&ZG#!v+W&=pC&hE(Gzr~S zkvxga12Gs{tDQ6`BvZ{S8b28dx=U{yZUN2GHWcSK$A`ulUlhWkZ5|>P{X_RaGOScN zG7LOO&f;7ME@)$hDEW6}#~dCJhc~}~3X-b33a;)8S`Edlkk`4g#*EfXv+Z$T5|rb4 zl%k5c%#YWSve~GG$&NrWYARwG9?eGBrGPxwqS()VKmk3l$x}?K?ivM%%@Dvoa0092Uk}hiwHkqo3Uilb z)NP*{pLti9wM>*YfRI#_HkI=9CMw&n`}1J`2Y#F2`+H>V2d}G_^Sih4B`ps?%)yy2 zdR@5L2NmKz$ST5*M<0B^mxbx`IX86`Vn#Gk=fFI20T?k0DZV z;{mZk@B^uH`^trf;6ZDLihaB6KA4SflFZAf)JZ9VzstZBCExf5O;vUHsG^#o&ZS4$ zrCH*Sx_;dmOe+Q)wvZO!Zj!(gwnM<@%#E0CR?jLTq=7F@tx zZ*!W2WRG=vTwCtPN+j*!uwr+)Wba*1o3^oF#zAy;@&wPhGi0Swzg0~g%NeyEOK`iI zaY}~QMi{(>5aOhem~wimIO#IJWv8)vYqG(}E}MbXP=D#PibyusOyJd4HZ{`QcD2OY|>-E7vrC-`22&zJyw&qX`x5#wPjb97gJKI5q_E|zSmz#Vher(&PK zEUx>LL&TfoBuD|h3_}wbjKKoxz-atQ);9PJrp?b`SD)8ROiJI7DG5vD=3RquQ+>9- zTE&U)F=-M0XV92Y6Rj{eavAODFr#%8b$Ze!quZt58VMCPEs}J_HRUvSk>JQ6kbaTG zVSg+S)OCBqp%C$dPd;loUAVNeyw}9yZ7YA}hDwL+{Yd(!>9v04kuxU`R`vznrAg^B?-rh=9;E1PTJOfwE2g=uCDOP8sTK${i@1N63Pd=M@qo3> zp-3`uUCsNDL0oE=N9W|0Jr+^ce>&tZuvsE1>mr#{Xz4rYLRCst(qG67Ef9#-pU;zb zO^|4G_bJvYTJu8HiWB1Dk_%>aACu8ph>&ST!rcVs>_JI6qK{yr7hGpqpO2l^a2=Ad zXF6EjKxdruK3$elj|5aSL8X)8Roc>_s^zqde#SX%r5%;uu8WbEH&fh`e4E>~oL26W z76c@Ov%;C;ZWuZl5T4$`)()4l2`o>U(wTB*W>F_88yQgp-X7AbgOhpp)$AiJ;b<1% zgBGHn0Ppyw>^7$>aGqPX4IuGG9($MO2cE5Fh#g$dWt=00$t9c61?9h1sz}*YEBOF6 zh!<`uO&XvSMMfm$!t7*4Y9 zY&{Df-Zdxj1CN1LhaA4w;VmB?;hXL2-z{^U{hk1i?5l1*3rGnco@xAFtt^{3-Cn`! zbRFUYO{d7c+!%UsX%LN!E9XNg^hU`?>_Q zHiubexX#7$LT&@0vSu)NcUPKet{{hmmk;t*-mc^Dyp_JY!F)_lx28ckvj;ErSJPDC z+e&xs`qfl3&s4c4xqgH($072GEBXRL9=rh(RB5fR5gvNe%)(NKHaipFb*IU*^q%Ah zGh)1DJ*Aqe#!%tj%kCuHX-|%<4^YJdb&(vzPoe7Fjq#HNA}2kWy5ylLq`Em0z0yKt zIWjd(YHulGP8N2qcjkn6aa6%Jk{Czj~$9qd=5z{3kvV2h97uE56;Oq^o0 z9lUj7%s@CLv+UCSxx=2QLM<9OMaAB5bTD8aR@s8(cN%J^dC zH4pEN`$tNH6C3BW3v({)tUMkCEbt+4SY&Vj1QrDUQ&$bmJ@NGCFC4?z zfViM`di0NUQ1V1KRT01z5e4cF%%<@ShzaS1v#5S8D2dq4G`2s?!Uy?kypSz3JgGgmGrDoEa@YE(OSdP+Bm%92AbCB$hW4|8E> z>wleq)hZhQdYhyClwfuE+CbdRT6x!dq&qTF!<~s(5xuA~+l7A;NDhJ5*fbmtFqmkw1f_Zg65G zPbD5Ta@V)ItA^p{RXsX13Ug|h#4rU{OTV72d*F`_%h}oHz{-HK3tzAN&M&YR<>qWcN>;*)PlB5>YNR3t%l<=Zgs00Q!j7L=rTrp1p9|-7RT1* z@Miw%C!-n!8(+nYC$PD}aKXM{2L3P&X2cHdn{%jr5hcxR~KN;6x*mP@N3~4hgd)F^d z9P2RLSTD#Qvy{_2G)pq)6c@2hnPfTqTB@*-;kH3h%hV#DVNxoQE9-=AXUAS7FPbDH z;cR2zx!=}o87i(k%;Fcx^Jt^&l?CBK=V-$Akm(?x*)eJM0>fvj>k}rBw7Lv@is6$= zXw%EGV{ZPQSrSv4fDx4|@9(CmD>jf@u^~lq%UO~fEGD5z3wO6;L)SdxQbAR3ZzYG< z$(1=@{CX}mz%3~uN;S5$hEQoz}cq-M1fvyG>(EJdMXACYS*;^&xK=pQBP&LMjSWaI`E zyjmpjXy#R}aQAZJFRW=aEXL3m)75sC@^?4ij%2-PLdAu0Vg7bWI9xvv z-Nd#76>Z?2F2>#x>?KS&FLHQ7OCBm>UNJ7)(^!j@x-;QlWSi+PGKj|(VrbMcUop2i zvzq3A@U4a0@WqC+LFvVSyPXa?X?Vmw!lVJ8p}T0*w7S$SC}R&_E%ISvXK|nIFsjBG z{{Jn<6_Y(D7hzax1|E;?w75N?A0;vJksD*FO-r8D!^5^?*CV~6Yb5>#rGlh5%zIJuyuwx z&lO7Q#p!g@UgC8qSmpFo^aLNmbm&<}s@G0~Y5Z-`X-82vTgv&E1IuD0IbT>6)2T)$yD_(TSWOf%QJ^`zI-5-yOl)tl?ROEeZbkBq7S zH7;&GbPH{0hQP0|VsU{A6&3~6dN<7%Uv_SpD~$op=ZXq>Og4?fE~MynHbpE8T$MC@ z40@OSZLo)2En(65 zO%@2=vzKG~MIDy)X}Jc?{ZZD`0tWV!yf~|2ePM0L@rM_UT(R{ z=*&uneWrguW&OC$7T|=Az>`V=duU3`D)N8`D3C}Q$We`mBEc9C=YG0lmbE_;Wfe*t z#Az&`xCqe@t*Z}$0l1{EApv)Qey)6!_>a<)h___M;)WPTMC4dLrBj5z`!hjUj`=4< ztvnvOM#@=G{lOdm6ohAzKbQLzOCX_z>Lk_O;;XgOclQwe+tsFXI(Rl| z--$B*s!oQC?;;~x2J^DWgXYfpVd^Q0vsr(2Iy9#RV^p-%VZt)m;oyeK&&U&tddilJ zi(_@d6LC--|B0HDa;^dyVJLz<6@^q|Rg{P;Rx<|2d|_CaSg>pnBzUTz!gWkbRHsz3 zW?iO2=j6p!FC|7}u|`}*=O9Ah3wAU?(I{2LX)CbqwY&rm-wsdRZxfuiZ1s$PfbVwZ zzGQBN`LuA=bS!(tAxo^-6Zy?@V&ExYz9-BS64;9UnHfW2Wf6hlA3LLU#$)~%wJA51 z{ZDyGZ{;9>%=onp;r7k){Uv4wjRE!(W6ys2B*t-+aJBu-^CqHD{CcDnaLk!~0g;E4 zr}R&;xpiaZFzt8K8_DsS7ag1-@@*3KPwx41`Q#H$ArZ7>J3Mx`>M{37j=6^y&g?p2 z@nZXaeEmNC^AZB`rSNT= zgxH-Y`;3O4UCzVUyFRt@l+4Tci&dU^_D!F z(^RJiQX$5M3y?<2rHzrNvigIY0jmw1pvrOnP@L?P2uAE&W%(;_tsrO+tkM^pai@cr z0#}ys8M7~`E#AA2O3-5zitC&ae8U(k+Ke_;%Y@d%DUgevGVVY}gSD;n*06QIT1~xI z4A_h-AiqLYD1`A|7xv*>^=v9)``YzjMeW>ia@G?w`0Jvis}pJgw--L=>Od2#UlLNo z9YE|2kUXxO-xQUC|LzAg>&w?$(@e!#||5Y`g3fFB};fLWG|+ zG~XVVaN^#oW_e{~gP`N%nRB(*rOB#Wly`*IHwSFIARtWp5OrPg)BPV@>@CfpE-J-V zsEMdr%CXtmmb>YRHrUG6Y30Ge1L_F+u0J2Eq^QbWmf@?Z;CiUtH+{3f)Ft`ec09OhMjzhdTRy%pYh(YEL~J1{Wjku^j}+VlO6vfIdr(01nA%gh=?e+XqoJg`8E< zE!QNX^GThdv9Oh<0k1s6E0z}&>z&ZtaDps{d=7NqY%g1PX(^g}OY*JKz9Y!Rb_K#J zg%PQo0Le8v?fjJ=ALC6lb$b)c1~H@oJi1Y~`WIC9egWqfBUsS>Z|NDI{X|1er}1u1 zJsp{#kUqE#$IPVX?=lL;lcc!x)+>)9mBU&kIWWv^z7R@C6=(kX%#gDI^RN9aU$`iZY2YQj zbV-;#TnQ<@uyJp}h*(wM*uJf%Dcb81nPWq0wwQN9FbynCVV3pP``&G0(EYjg5jI8y z7YR+Ssdxbp3P)~7tM>Mn7ul}_gp#-kIMlDl`6)n1p6)HqA%DyeqZaOT_Nf3bC4a4T zCNtOAw{gb}Dz+xyOk4~O!Df&Q?`>+ByuqOajXlQbJAUo;{#=&$bzi+48c_c@o}JiX zUM1v+T-Cbr&9^4DMET9Sv}8@tKka6M+L6lC3MnLktu?fc-P^D1nVz+Ls8%f5iv-z9 zDo(`bt3JhW`ch^T47%fl$%RCKW6D7=hY`uqkW=pMkt(fm7P}^lp)#CTx)W20^ck9! zJQ`T&-H=bWlAE#xJ*hEORlJa6zM=%xW)^|5+QxG6kv@dwHHYVpgg~@mJ%5IcF`t(D zG!Z0yhTXajv$1{^`&*`$O6xj~&b9X(cA&_Jpe$>@;c`KKNQbzJ@WWwr6nHv%-}!OF z&8lMBhCy;eI3MZu_NgqK6IeRZEyYl1v=A1AT>+R;@pKTYy=YC=Tv>VO;|~lx6xyid z6+CM1AqQ3N6qY(Rq0jm2@=?xKOaU$^9$)p+LgV~y7qs2Cd4F#)SPxYiP+gQ4K()oB zXNqmLG1+vcWcn{O4_mAB3oWyl41ht`5ns{cTy1a;v}^*(Y?$pD+LPU3OobL2b8B(j z6VIozqFASu|M-h4i|55d*|mD4639%5RC6wO}EFFKN=0zeXG5H7nh*>waze>Cm>ERp zGtSjG-#-w#KU@RLK-<@KeW3Jz{TLzpWm^@Ie{z;C(*n45LNgc2wB(2HYqbSTS*)&@ z*0|!OZ@45}e4p$j4BLMZ$_Zuv?(-gnte%9EXLlYJpI#l!Fi&8in%dZ7TdYr{M5=`$ zdF|wg@=w-(Y!RXWg`WGQ>pkiKuIi5Bz0#^yOp&U^>X}zX|6ASh*^KO#QUK`!SxU1= zT(8`>f}cvpXArNMpFD@F2s8FiMjA$Vg+u||zvrCI&dC4|1x8TBFDu=<1WH#q)|Jp*mxj5oxE$KG~G|Jt=oc54gDq14YI zC4eO6f(aWO{`~fux@xvYxD|Sf+V!8ov)M7Q0+f9PUuiFZ7_jRYmQAYJiAFyWUmj@M zF{&lVrABZt^g^)Xe>##l4i?U93e&GuI;5?uF5bf>Iq~8M#6Qt+QY0vA49?3v1pgR7 zqR%1-6@cA3_bU8wJ%5@C+a_joM0u%jYNXLv2MkNc+%RfM7q8@PjXuDu0WUf}Xc1Xl zL1|8C+w+6USkRWbF)Rh6JG(*f=wsj?_wJ1gdN-oMVgCHyt)Q7x8)EXW3gg3tvY~7| z)uHp>N-<|jjWR205)+Eg1cM4KXW>5-w#hN`T=!PvvswbE6>KcBq86=rzuRGEuh(`6 zkg9)KL_lIK0#%MHG50E%Vi~5`JQm!l)z^_T(#+VRttUc_@*%ud{)%Z$7J&_->Jd8h z(&slG>Oy-^pFA1*{By3Hq4}6ivJ(t{E;xBYnE{?eM|I*-!=IeZ;Jz$6eUedAUkO9} zjsAcIi5kd7ya}GX7Qk!+D}ZFOn%RQ>pgH-#m~Nr!=9{WIfFCp@htCpEQmI~m&2;RR zb}$5*+Jnk4HBLN9gyTB+uRA_P>A*AxbWVYy_M8k&8H5#YfZrv+Cs%~{4~rE;6Sr2U zW~$9K0(dv0B+>M4_!u1E|b zP-W~Ie`^*sV`wmF`HwB3SkF&5!~70UH|Fqix|dz99xpZ%3lGFVjnY8C3$?O< zOuThVV`(G`G&c`Ui~eZ^)Gj3!TM;Yln!|i~)TilH{EZ5_y+dW~#vF~d7Pp;QR<+$V z1_IMwCl5KXJTiTnV=((Yov|y>ZG=v0nk9 znYGtjl_3GnnN+)*_D&b$YBpII%%9D?B2^n+0Y!~cap;hN5hajEq~H;M*%GW~w=$2| zA#(WE%}Ms`y3*X7HY7cLn&`kBpU)ebniXc%)3R#zZ!^#4n!%{nvIM>3!X=r#xLp~<08{UAm8AJ7`tsvBLdZ% zf6yuM83rYsFatnDzxz@A@6p#&v^%Et;z&Dinn{Y|x3T?u1$W+1?-n01MG2t;A#a z@vM){Vz)vj)hU>p9#zwqSZRYfMA^NC5+~f#xxinj#P$XN`t7s>+qIopL0OChjKBha zT72k=2KV2HWK<}CF~Mkt8QJ?%>12JFx0ds`emSpy@}`hw%g8!pP+-bsH0gW z4t_@px!v6wq^N|`S5D%JgLLlIxhfF@bYzVU-9GHXR*3J>2obZtSg&ZfC zlIjl{c|Acsj;zS>76_3Zr3k&}O~-sJRO0Ht;T+B`ap`4+@{>6oBS9LZZiL#u{f38< z6i5RLoMk1RQSa|H+&sA5DCTYXC0al(>{IA*?UC%<^0BUgG>KG5$s7{1HS5$+4KofF z5i{}JtJUf~hGg2F9nc`Q;vzF(CZLQ?z@S*pXgO1Sjk-1=bu6t#6t1P*dmWz0zj8)i zr;_&FJ$@_0q-;eh;{RE`Tn;%o;h@&fH00o}0XezD<(zVm_8f=&ky9IH45j-gNKl+R zW&(Y!e2%*NLe|v3TOmu1^U)1Ev{C9h9!mJpf`F~#v;x_qlWZm23VK8fa)t4syKMfm z@>d_mXXEeoR;6wtbW9Fa%mVt$`S}F!&VFN#*=+4`rA%9vD{GvGO}+hKWLTo?qiTRU z9yoz$y*84q9?|#`ZLwjkSUvhsEnBPDqxD_>P%t`zHm~KKp$>1GMvAW{Al0l8H#3}b z@Ol~;U>o|;0k1*%TdiEk$E_c#4#mJ2bc?ub{}ulhfDDeuw7z>-jslC>npQ*yBgUyD zG%NdM5w(^Z#Bc*;$N`g0>)6mY23#fwD(_I$fKAcyb07OF$Zj47L(&zbh}t7jZT1Z) zHy4q9Kr}k^8M(uu|ApYdCpJ~ktV2gHxY4w!a$ibJK@c z#`%v-9z$*E4rEWp7K4nF|0E#aQe^q^zFW#*iJayV?I4(qC;Xx3kCLF7{z%HVY}q~oiZG9 z)`kjnktYlFtqHjTA@Qhl4csu0SvDicuU=w;Iffn>Lr>TrMmaN7{|bi66f)ok*y-A+ zcnWZ*Xj~LlhgnMX7*(3*`Ys5ywyBM4t&YsHUfIXk9&8V`SCag~8Y0~rB5O0I7Uo-+ zZ(+WL`I`vyM^%~q8J>Pb{QV8Y9vpk|xoNVdRNAva5F!T^Cu=q2Z!+`gc6 z4{ovN0YZUC$rJ@w1o}RL0hl3q&dJJHD`OX3n3r2CWtZg@ix`clFv#BJO~kYvXlU8# zBa3c&R<*wvE}_=hy*kSBTx_j0F@m0oWwZJBjJ#kj=wVUpf-kiQ%haEJ-;QGlu2&8c6;VZZ_M zsWZ#IK*UWywyM{vUej#`J80G>d>BQxTXQFb7Py#9FPs^2L$9@0Ek|ZK8&S~I1P!%V zs_7bamZIBMn`QfGWF_YLOY629^$xg*L+ml-SIb(8)}pf#a*>2gP3*!rX5X1?`<wg4CY)zTCX-_x_Oa7DqbVLO*V}XO?fPsriy-%f!OU^Srt%N7q#!?A0Vo zUnWy}M`_Y?F;wNbo#NoE-6hV2be%!HI(_=$P^w}M_0^pESjmBoV%BwgqQmB2UrUU> zIT?qZLa<;lkjkEout8HcXvzjn*`TS8(EQt)RQKm-PH&L9VzL#aPZLyOfWbq3#a zoi)D#DZ@<+;00tApZ*4V%B!Q3+DrmE&s>M3_9&p^AfL0$8v%mn@EWPZ2s22g2vE{b zqu~ULb9zMtJXaKSdQ#DtDxVYsW6c zD^t-gzGc2XalXWwCAW9xZ}qgWa_zXacgjD8cX*w-eFAyuEy}kj-=chr@;4CWzZ=x# zQx_<4iJa<5^@qjyZSj1^IoLDZFGKV7NWLZ)Xc><0G%loBrq0E1-U4?ty zg)hMiF`jkMElr^ux2Yj)fZTFSmBX|KV5Uqf=&-!Uh}B!y3AR&_=<*BV(L!x8y>!*` z$)%*ug6X9xluxd!-c_&R+)6lF$;{G{G(S}FV*`k!$^v=pvGwtyb)9B!Dv|-Jc>{UK znoqO1YL#uPL;90~wR0{mRg<(KDby%uLxxqsTKOEjoS^{8OnZcKaE~~EL}}0N0!~=#VXf){LCv8SblL&99?1mXHOr(7jXLk%`kDr-vT8e6eOj@FLp`K z!m)?h>@%CP-v0J*)X%**@ThaGUj6F#WIq0chI)C1f=lc(#7`mP+k=59yfx~Vz5Rd* z^f13yMSeAe)4A3g)U0CkXAw)*a$P822GX+Q+JnhCqc zX(EaoZW_kjt;lY72JfBOd(WeS<5NhQ`#=j6485Q)!Ha0qzgl4wh?nb)K@&w$#&EU1 z*i_eEnXZWUeV@v9@R}~Ry^0K6YUV}h)<1;AZH~xFXqYd!SBEo52=cP^7cam}@lN&- z%DZ8gq4cYkex;&wwW^p2l+$&4`#qi6a_;kcwVhGoS3^q^V!Tf zmmR;RwcELE^WURs{qzTJTQ~FBTN267kVyAPxkorSamC9CPNTfU1Dl!&P1|qfye*q( z%O_rb%Bt~gMKZDyeQ}#Da#L06)7rGQ0KftO z3jiztcrF0oT}A#PsmTth=ZP-1()hZvxi#!yck2+l3-z}qT(f1NDK*-!$nk7VU%|H? z$O7}p^94SrQZbOFb%k|oH86v$b>EwH=_OSj04@9#rLr!ZBG$rfCj01-hFsK z7jxB7-2h|6ZxJGKD6J21uysW)X9zGKIbx-_(KRme?y|AW2!>>8;3&D7AjD?@4W~0e z$!li@F_9O+%T3rxC`26ynGLU9KW^7J<8PfkC6-^M6{TM`vz=m-XcdObLzyGXK1ns&i&XK;qWh>6j}5YIRJY zEnKNVLWZ`$i9FS11aX>A2bqOkOd=&dUP zUW0ElL;&?U+xj}0$S@Lt73CUGQAIK>zP`67WpsOLZ|fB7CbPz=Br0Ure#zTRR!qal}CS0G;)AMl1m z&7o)nWRq$FAa@IV5lc|s%!Y0d0k@*xvV&Bv3(@QNBc;2pZ;Bp!RAS)qeSGFUVUz(X z5C3F(c?+@3QYt=dJoZTyn_qPOxLsqAP>@C6tk9Am#A5OI_CS2rb0!~su3myg3ZN==5{3~XA10<+|$!$ zwkFfn{GRs0%-yJa#7BcS6^Fg#NB)9t&y=0pZb~Qhm-Q!n|DiW0FMQ;@1TS7`x<>wk zgpKap2$`-ec2`?-Z_T(ZntPckW+qR5_TEd6jt@y}8 zh)eXNpB&%uKvIA2a2lj-l+*$LBgkRd@%~@G8pyM(`4O?-)cT0|ZPJ$NTGOUfuUnpB zH$PclcIqefolMT}WIlDH3b^%6uPwDvsllBRsYQc3`*Mv8?l^2E)2a|TnTRmMrFnsz z0P#W&_Kadk8BAM@kKB2lt86he)cZq ztQgixjfe78Bp>K!B&HrjN@eDP6>()EPE92fVHM54cAwC^9peNiW!=#alqmdFvY0wf zv62=SEx%u8)ixN>r6!G%4(-PH!PH#MceUQn#Dd$c$fF?56pde6S#PRt_W_EE?keIF zV~%Zjq76@6Q`hXeijlp#Blj)yv83uYIQ5&VwOE$aq-Sa4)-g!Q@|+~dtGchBdue17 zQo;jTu+ENffE-12yQxu za5V|-{vF?QBm>EEvC7#%vjx&3;i?9GWEyBJKr$USi!$3(fl})mF8kEY981>4W8mUx zBulNI#rj#SpXJ&4S-Ras6N=hQ_Y~w+Ym=%jK5}iMILq*Lj=44Ed|eUjySnTg8dDt3 zkaKW0k$XJMsh%`uNK0Ov$yqnxZuJr(_ir!D)FR6L14DS?H>)_8n(wX+qO-f5cDK{Q8w+nNym=Dv#s<-? zE{M)%X|P!ujDh)^irlm3aKcX!bW6VdB0!#0EzdsdBNS|%9Mz%dghUN`)^r-7=L~Nv z$u+CFDvVn4+(15T506H_Q~K@W-*SHS%03icH?~<%eSrJK!4K$2X=?bpzNG-yi}`j^gJN8UThIU)l>}j{&Gi zvS)gUR&J)4?gMv{YdKG5?2p)t2V`e-y*C|&BtY)pdy_k|GyUt$XylIQ&WQh;4E-VY z@9t*7jWfGJJ-N^NFgxt_&XbNL=G%(wfO?H{8XQs=9iALDPZC%pQ7Jdam>_;jgX=Ag z5MF{8bLf9pTKI>-eowKn8ev%^-9}z!J60X}Qo>qVV#)_}0-)!~-{TN_91{RV4J9QL z@u`Oi+^AvdPEr=Wz{-M|hl4JInTwyYz))!s1Jg>hXAH=miX-kBCsf{kSCJSoS)iXG z!4t&zVTt3&T->FoG?I9Y#9XS8j(4`JI$|Q>(p=?)T!aI2@@=kbSV3oZ6YOq6HdY$4;qO0QNh*uQUZ&0Z&}Lbth1AP; z%ft}QvK2`^C@~>al$~}dW{M2D7_v-=Ncd}NI7$vW1k`3rSwaE zkd;Y38Q#)X_*omdyC`%y>B{2HqZdY_C#QEhE=tWF6~)v(xLQ*&ijh$qG;=p^emLiF zyCNx#6ikP(ynWR8svP z4EQh*K!=G+Ti|kru1HiAGe9WUR9ryGYfk;wS~=)K3}B+Xu26un*O*)pMSH6=m6844 zrbmFIeV(16+Fwd!huYptey3t4^`?>xnM^k2-eorTqL=X=oC>f^0yU$}u(C)`Vb&FH zryGxb7fo-15#DhqL$`c(&!%tguJ8PRPss3g;{3fkzZuTv^qSv}-Medex5FAoTG~xb zyQ$et2%g16OWk{tQcOirmwjm>=svTlFupsx^71pA3S!Sde#*X{JM!Zk-(h}8UDVqj z^n2%0?Upm}5wbs+_`>h{z2h51I6J45cQyP<5FS#-bI~i^eA}3LIphbBp$`ZhsJ@CP zUvE%w%s8GyF&|SeB>Fu=1bX~F{pb=$AM_keLK0EFq#Syx7rlXsxJOPXXkG?TiC;a< z@^dLW)6&z)aDu3J($ynRFtqx}>Rp zxd9XD@oRV9LJfHdU(VF5XUwo1H!Wub@r=%NDArbvYMTJYB~f5z85$j_cqrIddM$Y@ zBAR$nVrgR1zewD)9M6nIY8k^9OPyo^7A9j^#guApFgzJG+ro%a8oT)vkFi6O4cEM? zZ`^E*3Yyj|!yZj(L?EPTy%b*QWDVJPV{4js6K|YYcMVr@D?y&IXPPzGObMjK+KM&C z#z!0vhZR{+l^<9-hFuvM(Q>0TL!!zj9|w;3!|3m90G)CXNg|8g@#z-Lg6LA@oA0^i zduZah&#;)@apf}<1(w{-C_~D%&a{gjj1j*@Ncqh9E!9qcSw_a;U6ISLM_>2B#f*lY z3-}B(z~Ob|u?N@ao&ii4N6`KMI&-nx5RS(T;iz-1+As5nUuHqLXG-YQg?wg)?a4c+-46YKpAhWOQ=Ibq9zYMuAJRh=g}m?guEN1!r-Q+0=`vb0-Zo05Jl2%|0XmyV-FchKR$Ny-4G3egFOz6^l85J9$e zMJ@%H(NP7V&@PbN-_B5g;vj;6a_UeIU@lZsUn&R8043hNjLwZFqxdo`oXaoVU7&&8 z#%yr>DLXMs59&aM6i}?(o<{z@hWG5HKUy;|~ltMRK zU-8ffG!SA`zH`Dr#&J2~tAZJVF2EBhXro$!R75+80MnMI4(iIaAbU>z_-!gw?1}*J z8oVG>Qj;CT3^0y-70)(C6B;0JGgp0;Bd_DgbOaiTcF|rcT54BKo68{drvY@4=maG9 zY8Zvga6?b%4K<)h&0ZP^{Xs&{6O;fLd(^pRK&*J=3I&yPS2cAQ+38L|^(he1RY75K z(EP65Mnfkslni%Bg^fQu&+B)gP5)Wf!_ zN9Kyi_R0oGy`cG;(Yh2fHakF9BqSWG&lPu7tZ>l@#j1?3Oa>-eL2~fLB#4IZiX;t= z)fk5i1yY)PNi9bKsxZgU%dT`siU6oaMhAFKCdlD2xhqogFc{z_t6I4LP4nd&EF!j#)*+j#^zY@P8gOQT6< zWH`oK=V*!r@5sGCa+cDGzV%JKJwnhsIg)q36*t38v*l$OTt%+D!Vs8U16RhK-ko6} zdN2v-T*iuWKqr8b*J_X1MbLY$d6FyU&B}Im-gdC7Q+9DuGQ-XM!Ebg(!|aAfn~oKF zvb?{$cn6d9ciY5{PdfH|UDf$s#a%q=))*xUZ?KDkL+W{uqX2pbG$d}z(?@j_>%!a4 zpz9*{3?m0!;J;OturZ}xMO>R`CQp8$Ua#Y=M$4DA)m-H-7#PFIxY{nH!+z(O$mKGJ zcfg4YignCs0H<;6Sa3xgNC0LmL~_}3c&)?g1Y!UT|E+SH-Jn24aWD>f$N*$XumHym z>VD70&b8JKstqw^-_~93bA$xa(7vITx-Oy6)QnZGt}o#{e`R@5*I}VYCeT4Ye;KH_ zgr)8RxN{wCV60~Nft;hcuVM>b`63&|tQ15aj3G;+D+A{F_rCF!VMN-?D= zm}Gz`Dx|*{C0pn4R&NZ-r29%mEl@z|rfU=K3u?`+s9etst)>i9X)T0EQ45K#YgH5- zeNY<+(z9w)IBbNoW>XbvZrz$`RypGXcpDuR>8*{+xcrg=jTzPfO01`T84c?0t@?;~ zc@7LC-4fMa?ncvBP4WvxO?GF{q&i`cJSc8I?O=;Up+4jH52ns0sjEpJG zsWFWcw<;q;RShFG7?j-6 zYVQyVEYCr#{f&3=tQT=@Ei0uez%tSQp?DN2ZpV!`*;_EH^8H2!$>iTF_No z8bAjbiK6aK<-;v-u4{{q;>i_>-liz&v?&uB61Uqxl|`|bOKNKwzPXC{6sxeU z!nO+AD(uHn*rU1>_!(W%asxC=V>ws#9Qxm@O|>80XzoR6^II^`d~>xhYE3me%PaGo zQfC>h?nt@tT&;hrgPY}WZ?OVOlf1X1V;I2FO`>$GFgsJ2n;P0W+NsO2{t@v51RY9_ z2@hH(SkKZh%S@bX?53)kD8f|nPOa8#4;JK6&+(B~Wn=jqAo(In+N!iz^!-%Q%Pnv+ z(K#?>*%?&E=RM+Jj1U2SK;6(mZa>brsq>1;T(U{lQCal=67ikvs7;~A;iuwlB>l?k*0T;YVq7IdkNL~DW02suv?dC{UhRs9>nw3Vola= zzsD!5n{MyP!X^?>7VK2^Mcp*t&r>|4<(?y-RPExaw{`@f?8S_XURmkDpv18IrXBZtYxw4J{SjkEdbPWWL1nLj`+viD@GB)!t#K zs(0-x)LP-kGO(bTbFikxRxZJKT+FFQfk&O|BjmyR3nXUe&af}!2SbE~OlD*K zjL8|j6>>sG=Enmp6Q*4X390j&odxKS&gYmP&Yo60?qo{jc$U9C#S=~^lk%7E8AJ22cVGVY98E&xmA|`$eg!+f zhx31jke9za3$TMuG2>sU&vHdnA3s8N3Fkg0(g~UbK7JoG~fb$E`M7dQh(544fN>^kP-YciANiQlE zVuPw#(a@k`b{#dTn4gZaFOgL;|y*{EKAoi(VOm#ht{=A>YQnmK{psAg`VHmF;W zWDV+Og;A4=8G+oOVouCAsad9g8r09~#0C{JvbRaSlr(QrEsZ4_)JyC2Ce^Y^ph?ZN zc5PBGty|mW1$R?-U9=)r}Q}z+@b4Xk|Cy22R z0iCOT?bc>n8L`SFF~Pmh>DCdQLrgA^uwq1Omv9>Ajmg|gy&#jB%)=ZvZ(Myvmjmp)WS$GQZDmK^I#yX#~ zlBGPueEPM)M?Sb!A9g)b?X0P8+!@rRN-K!kOYB@D-rlEKO6`egN6qa+s>fu2meG$9 z1X)zD7|K244rEkkRM4Z=y_2tR|ak|p8*KwVxTaY3Uo~aeM?*;HbUGR0~uP(RDXBb=~9$f{}O5SEjEGKC* zq!!BAOz9d4n<>{^zUKR9AX&4VDW7%dOVN4EoPgd+pUf$h&KiApQaL=0Y~>4miMgU| z#itdY3&dyr!p^@n*_co>S09s9bGOaa-VvFm+Y(WW&-#Mzows!%%b(xps8QO#Q1B z(pKy>OPNueFQvILN{g8YLOvMoj&?`lx#$XI(35{-jOd83aagzBWd3FlN$Wy(NX(QDBV4U!txY-AKCd(srXDHAwrR0yOA5zbQ90h_C z`9d+0d;F`fmXsJ2m1Co%`BTXFAHFNTZH^5OvRU>~Gz2PZ$^PKiukMnPq`zjXTqX_g z(JKsCQwqsp(;QV>;@R8jg+*>Jy|W6S zS*Gh6XF+ohfprT!zFVM~SK6BX*7Ucg|C2WTHw0vE*QI}u!PoEqfA+q;sf}ET_p7M< z&pyg<2oRF+Uw9qaPVZ(qrvwo>Uwwmy18HBx8G3IX@pfp zb#xgVz7hL@>>V8s4qvNZDC|_LRi`ZLKqQw~9wxYym|0c(qOk&{zG-i(xff>ya}1-8 z5eg?rKRdl&pn#+B1g@^o62@WSx!LLMI#*~B!2pG7h~7?6%8PkZq^3&#hE|;p*fkd4 z>)&rRIIfsV|9C`P^f^W?&BE843YpCZ#zF0XAxEx5zKO-uhJa*76!ETUe`#IE?7d13@f2Qqj5BghHd% zy2+ZnnMZ#Wsm%nbdUAv(XM{V7&_eqSRsoxMTRT9xx6bml$Y?HxipKJyijDzPa`Pu< zyaJSMAbW4I)M#(*sl>7D$0(Yj8w+7rKKAk&u)-+bp;1xCHxZvqLCOZx=D zFv6rQtXH1KrWRI1$W45v%{E$7O{tbr)9#zAxXc){!MmiaRJKqMrX|KSwnCz$`0S-( zFBR7sO3fUrFpOHSoJQ~?W&AoB%`sz`5^0_szW6#EoM5&{xY+ikx#O57gbxn)ciWrb zswq0%o)&rRjSdA-|FQ{wMT(_&Fo{~b&1M8P6Y?#s4XWt|L0_WVQr@S1bQSiRvkJq5 z_Scg5j8g>0qTnjMHOqy<_Hw_M`z~^Sy`7<@nL$YWXQde*Gy%29gh~ODM~g$1eh`{1*7hdt5}rWR5%Uf9a($1*ULSpQ2*KmX}@vtTm5}W^8TS;Xgte5 zxkStIL$Q2g8of2BKl!Pn${$Z5nV`Ja=0}9$vv6=Yc(Hr3_Zy{O-v4#~*Khy*zjx@v zfB544!CMx8`aeg(m-`P#@2+=0uv7Z}=03hSzx(Y!d^{KqB&MmD*tIz_zF-p4rNe9Kf{k@lOUhTc!J9wR|0Ant{p7FHGCy$n@&1nP|One=a=ok?+#sO9X zosN(7_y)z&(e2*}da(j{8fG<%7bHrY$GtZ<-syAbDY z6dtW|RMZ5_US3imj*#{<@J#}2Q}(@yuIRE)42Gmbc%q7ClwT zL2H+qk3&uMvZ`_$s90oG#ft0Px5tI$?=qV*0kZj|wk57h+f9PlvwkM|ohr7Rh!xwM zhd=WZIXS#;1RGQ8h~?U3dF^j>CDYu%>U6Q#>*PAKhRKJ$)vO;G$J`r@ ztlRgTSd{?_SqKDKDPzND3lvE2sPr0C8GD}T0jGAxIjZVKf7}Xp-Hsh}1<5N|3<)c_ z?N+Fmp9oXz;IJ!pJQdJy=C10y!?y63w7$VNak2Dqk}P~_pXNG9B2&(X(wh-_XM%5| z_`GQZ<`cp`Rbw}~Wy*;7s5c2F{9wOEm-*vsO4Lo2$#`h=El7*CR9{%jY6 zMfTX_E=*Mt<8ts!l@T>R9t`1NzYBbWUbt`O6Opcp3a1GBozob;jiU50t{ozzt!h<# zdW8adhr|wFd~lOyX8-QmMzAJ{j_Xo-E)(1O{ zXlim3kzqa?Y}BIuhQTKpA;*nbCe-RS;>7gB^=eb`kavL=Pqarzf4kZ5T)xOW^~*8; z&c$qERXF^6a0Qn~D~|MUc}e!~1V)&z-iH4#dn%Knau~I!_W`mCOmZQ*UWSWY{i<7m ze1|+Jml*cmXL5tE`2|6Kh=1IkJTa60!HY)JJ30M~{qh7!x&4lAzJ1n^d|XETIxZj9 zs^2g+U7PjAe3VUrP_@;Y^LulCZ_e+{`Mo*+vFpvfIe!zSXLl_M+ea0xRvT*MqwC}- zZD=%*G`8J6qYJHfZ9XXV&DoGp;46SpkVG=&z0g05#U4No7@Z@4gbbDH2}~mi7bwOw zyd{u?Ikm=A-ilEqjS+@8riAraY`t(@Ym~@VS=lI&d*`D39Vc;&2rv3Zi@7TSIMZim zBt-Yh5Bc{6$~3a$h_VcZ?05>J2oVdIOq!#Q6HdqD^w}5b8Ydgc!ki`PFWPBNEFo1O zUn!-pTMv-!C;nW;sgZ|R2o%|A5R6)>Y|fZtQsGuqPcJ3QX@CG&J`*K!VMTq z&H$I9YKAz)kJ?ks$wji}OZ(R~Q^>hyX~w$O%+Kg&=bL%3w-$NJTpeu$c#skKF2S%_ z^WPvKHrH2I&Bo$zePC>y-sbhevAxF92giPYF}lANg-c^*b^v2opyVS^;RtWr7M~iU z_lQ!5^e`2?R7+R&nOe!xa$kL{*+uSxDGFu`;4y%CR6~F(aSfmAsjoOq9?S{sP45Ht zy|(~L%V9S9;rdY%Em@z?TtWZXvi#**2r#ua6d2E@a$HjB0QNxpb@_;G+4MR91WmW4 zE}ZY9t1@h}XPfA>u%_cw?Zw1`P}CxwAuiJ`8iOC}Fn{8YCC7U~=n0rmmp`TO(#3vv zj_;5%ohg@7^|$f?RhVF2#w|m{*_O0&D|_xp^{LrC4%UKyZJtY~MHZbZZ1I`^jm?tl ze226;<%Bx!I(%h1or1w5-2vD(I8)}3F^)KjEjtQRw^kXfIRh|80Fxj}!sn~-KQ^rG zUc2wp1NG^F`t(43dLZ}V?b8GG>4BbTWY()D=*dgOmo%PTig&Gab&EiSs{RZ3IaDf# zRc@`0ZIx|MvCD-0RqH(W4cfHLQqHI%9_Z^sqRObhX^4a;*vi-5={hKD>Bt1r-QOv3Wczve6rcrlLEuTK&0_Q%ICg8d(bn%9j zQPV){<&pJJ^l1GmF&yR=ie4Od`W>`Spd2r{v@`aH9QQ*`&0HxT$!p6!%;nbZPV3vP z$0?6q?KI(;Tp<{$ROGEhQCfXNSLe4nMLA!;NvBPA=ihAi!r`_f;~4hNweRgyiG!Z*SOO!;|mn)UfY)=+kW0 zG^W1I(+>81^`Z$FLmv89ZDqcw>!@~0fgj*-`xu8p(4Ubz8eGW*;2`NmLtTn_&A5|{ zmu<)LAC{&4M~uM<8Un~zGRI^BFb7M>(z^(Cw>^qzFe_tIDRp!EmfVuFF@X9vn4y)> zN94_Sh{B<~2ZyTdK(wJ-l@}qDgE37=2q*!;6k;;GC8Cu%;!_%eC5}woFMvdiFko<{ zoKUs$Yv?T@BR^?+nkhnSxe_(uLagf;i_K4$!wf`}&QJ)Fg<>6#4NGY=U;^1zj21Bt z3@J*sB>!14!TJd4F-IZT0+AkC{)9~0vDgIZ z0NE{JNf02$#z_>dQbyB(9)jf*2UA%|d@&H=83GW5G#N!`i!Bfd<=lK*J@ugVrTA1- z#N#oFg)}}sI}x9L1?U*OhYZ}oC_zllhFg*qV|YTKP=JPDlyFTZiU8$P6szxqxXM<{ zgQ;l#XGW7aK>rF6hP8jN&+-g71$6U9&G>8AL1TZacR`{w1i)ODy!VT^R&d0 z5uySyBvvQ{3`OHDRc0h6MWAVDg;)sPl-f2plOkzM<2ee!Xayih?HmvYbs_*itA$`R zb~&|^h~q%K-CDpvNHf!l7_48kNS46)Q^=;<`9otjpTDgktWu@4O$0G>nwM@iH`Gpf@h+zGTnY>-6-ZI=vk)&@zkLtcIi6!K?=Ig%_70(AxX509 zjL{q?bN%2#?vjJU7sJ6vEey)ZMuvmR>$}lt5u(XmyodJ#%FvQeS8Vd~es;h3b4+%Z zz?apv;CAYF_2Hr#VNoI<8GaQ`W ziFI&ADenxUK{jwqnWT83q;6-{RJ5Gq9EMbOJFBC-6soe4~B{wlthYdZ=igPJH)9mTwI6>4qDNUqXSgVh$ z-q}RNSDC_iq8HtzTFXips7+Ozb|!LK1nM8A{^5LZc(9ZHCuG8PG7_pzx_UC$k3=_V~G`jmc9`LtepGFmdY{b1cVQ z%Q;1~R>6@KJq`3)@E8oWYwxG7C>`8M?G57QSzPw0PJYA1)ISUg@8V zG;!&l5jQQzGbfSS&ALqLWDBq~8LKKc-e zJI7Ro?&0q-qm&TedX~P@$r}15Ld;UBTU6Dmxyy>Rb@hjowE7(J4TSPHld;3>9Mw)% zWv4sORoqID=gu>J4f<^YmRNh>9plMkhrZfv!VbMgFb<|dB(=IxNUOKFRFIJtaD=y| z=d%QR8G^+=7*QHY|GI4u=@IuAdiAvgfznr2NnM({~GQK6Za5g?Y| z!XM1NPX_RGfM2oyo!}R$^D3A(|H*(aoB2IFRNmba^f<_1ZDIzr>R{V=B;Q$hGpW+~qX3i#%^3QRlg) zW$dxJzV~MO?VP!5mU~$@t1sxHsr^oF@H`7iKZS9$YNsjas!mA6y3uC`J@ab63369` z!uN<*f;4z+M4H4y>_TmUXL5{ny#;0c_1b?7H+K9@>Q5}OX4WM)+6Gs;E=i&YV3N5Q z>rG3N!M206GKr=h$fCfuwG+IFSEe)&#Muxe3{|TgfKxC*Jgs<3 zQ1(u4t^LN?Z=6qj<8%eR*$DEcN4)t_5pVpF_j;I{9_FTpxp{0bH$UbGVY$t2Fh>a*891#WQ6R6D@Cp8|Cl^Wj`PX2uts%tg_i+6>$cZC`KTl^92+K zkYVbHnCD>$E0ekaOaP5T<+v83FbNP~_^%WLFD-aW3`Dd+N0uIO#U6`h?6FavUin1n zQ|X?!5c1)D-)(PQeJ6uf-7yUsNJ?t~*4+rTWo**e#w2>`+Z1d@7Q}jxI2RrDE5uNIhfXjXC9%Oz@!aLJrhR38k*gNl>WkMuVLL81FWXRo;jMB3- zc=Q{rhsB_{?8+8`iV_ZAf9HWd{Nt3nuXmafMi_~3g(jHscy&doo|qem-U2a?6cR;w z>?mgo4L0SF$hXkqa8cXGbq9+YUTyKKxijYpQw5khKQRzfD#3Gmo&S4el_+gCk9{+G zq%!G6Q_=`M?c)x#!1%lBfAIrpesK$(FYdLyUfX-bqP5rdx{<8AO?er| zvNQus+64(F{9wO3>?GZEcY-wQiwAC!;i4^S(zZ^4pd)^U0CSbffq{f@9OaTHz&OTt zh*=Owkc41@6bO)%u31W;fRZr|5eX0o(SlF6dx)hTVyTB%dgKsGJzCJ?Knp4>ZfgH} zFmI&&-3M_;>M@RbjH4dosK+?!F^+nSBXbFTpibpW?KMq=z*Bl3b0Y9tH z+jz#a=vANbZU?smuR~s*!v@p5-LzDl8NlN-$6nrDd;~+2f=*-F4vjrcrKF1yWR!Gg z?&>wfN0#?W6DW>t`q;r9mCFh9#FEp$-mimHGs|p2F)3#Za~3$Vy7PUaid$(CIFeqq z$Y~*L40jIVMl~!%3p2uo2@w;dFHty@@h@p=O(Ah8`H2H`hhni3X8 zfZxMxeVIgMuU!4m64U8XDT;pZ75=ggsA@%m3mCW7Cv@Gl>O|w3)26?tJySJ zUdmEiXjs9c6<{k8$mp+6OxwE9J&%z@bdu)FUX^&Vs>HLC9Ixs^idQ74PEPM4H)PEU zsqqW=`PcOQy2FPo)XnXZ#ZtY{Q0>`Q@j((R!?n7l*S8jOV5O7*Y{fX5`^ke!OIQ7H z=U%y3x+Dq`61CLtFEpN^`0PYQ>K{QSO$^64)VUFdgCi*&%`anL*%8Ne7*Eu3-L3pJ zm)4dS1q+XS5li3rot+n3^8cHiokRKme+R?CsmkwJ{W3M;4~GH@`wph3&og(kfSY*=cvjo)YjTnicsUw8U9FpD_0J)IpQ}A*}Uu~ z!=p79o?YzP>(_O$YeIM=qdDes(Y6!jS^YXi@K<#@k!3E}WQ{^-5$r-K%=6c*7}`w- zkbn^aiO_^N1x&sI5D>JCRv?Ixyv0{0Cv_*heM<=9OL55K)0ifcsk+@IH0%!Kx-gcG zQvU;jp^80~c@lH^esM5U3-t5VI~_t>zMT+)DMEo@A`P?sg2^#Wh|06Qrt%Sj0VSt| z$5^v;MPsoIaTU)wkAIyb&w%BEMbrijCHf`3gm#4S}}sz^U@{%y)oH z$^CXqvTBi8nvKh%$t%C@#CeW@1A?NZPP-iwLXa+#>g?pmPsQTh$7)@ACk>38DGTVQzI>d`IR8^j{UcaS46 zLlUsUT2LEo5I_8K%74;2ue9v7i&NT-&Mp}-nb1NGyEp*XICA;id0E6Z^#iE6-FhOM z>#LgVm1=o>fNj;y56~6B&%tz*lI+N0WV5(VLWowY?L+zVAb#HRp*PHwme%W<*cmmi z&gL6z$g1)hcWW(-+nnnZ{RchIrCnIJ05vZ$3XlX>gCNx_%;iQ9C82y{?cF-)Ush&7 z=F-#1U0tuwe(&p1dB2~Oc~cYGp%%|MW^HARs9J3%`s1s&KoP_m#7T)_3xT5fZ4xDt ziwH9=vq;+JAhgl$T{Xc=T`GNssuB)2Kj|$0Iy6pFe>k}XS`*&dYn9K@kGux-;1VsH?>~P-s~dS~?!U!eqUD3`uZs6|o2%eLg)%V}+Ejhm&C;bSrI+D} z==b(5d3y(OBmtzwd0CFiAs9t$2p9?$yD#HU(_dr1T}i|*pvJqzUd2Sy^O?pm#vY%q z;t8kM?d9Hf;Ns7%7dC8udHv(t)&C~s-R`-uW%rka7vB%{Jvorq>pykY>+3J~I(QdO z5PWeeX-?pqV_9<^7u>zIT`IgUOmzC3PxYN*>%D_6CBE9_ZoaYF`P0vT=i++nC(bji zaJscl-bT?bQuf~utEj?_JO4=?S8Gqm|1kGZTYSRQi9c2zn3O&D$#mZ#H@FA{U^!&i&}mg*86sRUd)?Yt(0}JZgKmR zA4}Mz7VnL@EK+l1_BQF4vuTKTv=vo|U*7rm_oPV-E1Tx4YV;k_ssIlbwBG(8x2c>%_OR=>Psu&c zEM~rD7dP77;_~+A3VTPu%tE$#s1ZXI?h^+Lm(c z-cs4W8|Nnd`BI*t}TC=8lc}CbxOf{)6IFm`@^o*FG~3J+ge{bcD1jr8NOEDiV+i$ zs<_bIT$FxIQmFgROoKCLV%cU@Gq+w?vGA&s$c2EP3C2n1(`Ic8e!gvsdfB=o$6VJ`JSdpTV&PV>DQKK5`R9Qzh3{Zo`Hek|9_{V8Q(%qvoQbw{ZkTm diff --git a/build/openrpc/gateway.json.gz b/build/openrpc/gateway.json.gz index c384524ade906144bcbf6ea841dd434e462b4517..f407b1f0ae00e817ee989bc7d2f5cd99fece7a04 100644 GIT binary patch delta 8647 zcmV;&AvoT{O2SHzh5j=XAgkqffb<~ zg6bTaJxLRj$4oDI3}$~RgvoYq?(X|5-3IGWZmr{59&;~n%msR>LzO< zrf%ju3zT7Mv79(Y4&ORMc(xd89-KQEY8J9@^p3vpEPbTUFn0aXn>SN`(qzYQ=5Hp< z`g`qqPB5=GR$xiK8C%Zy%^dg`dT)S>-ngE#T>bx3Xzxs&|L^0ld$=F0sn~`*gEsV0 zZR?SB&#@Z=TQS&4f9ErDuOSvO6&bWfjl_6{+cphzEf9W7vIa=%t%&_Wg`>T&td36X zm+03Ah^8~FkGhr0-BZGU_+Iy*9OFY7SQFz}cL7fc{g|x12>P?8jiP@%UF98lconeS z-jM{t7BQ#zwFq~52Ce`(%US8tN|*MYoSNC|9hM8-OW;8puX|RJgij>n`8kAgCa=BD4NBEZ6Uj9wBG^WNvR_@hXVdt5DdHnBx{OE)}w{KFu z4z}rX?!S7Tx;2L?^O$ggibHPdHF?~!YtR*uU02Y6$i-{Qt`Lfd>`E%!tf#mn3x_I| zWVvIkA=kL|DY>TPT7_K8#l9RVSJX~O=AAAP4)#E`oaXyibM$SL;eG< zI-1*0KcnL10|e&r4A`RP5uTNck{Di10YcUktRa|ln?y%y`$RcUBWiX=eRT)^414x) zaI-%hENl;&zxF3fdpP~MI~bS)XE?zB+I_c=+~soS-5RrhTc{J}vwi7)7BG( zG4%3W^Ha zTTockIkcrM8$Gm&=G-}Y=_ojA{?M1egejbj(+Mo z#;id1tjS`3xZyXzZIX*?qZXRn+iE{XWV$86T#&~a@-?7EO*lI1b$i_&Ct_I}r4cIb z`Lwv_{!oN_9<7>SogtKm;G5yt8(^B;vJ}Tu98+=3r_C`Bc1t+s2jKrX{#lDn(yrBK zVq?d#D)}A-me1_yR?4!wXnJikN7a#5%Z{`H1kLP!EPGhZae|S!*#!L{upc?kINU4a z84#HDI0gh}6G;aFoaXc*HY=5A@EoLJzgw1CbGg8Er6KKFBn`)8>vzZ(cuCQ_5W6X) zeEgO~?PyYMlY#txh+%RumIfGDoA+NVtSs+m4>^e(*$9^P$nn2l7V~=rfmxRTSYMGC zsqqSbSD#)pc%`S;0U-Hf>v*PZBMYS(woEmoD3)`!%N|!Vu58Rw-TAeZqYMioF;9?h zIJY^NS;0-he)*ara1m95{&b;zb$NW^7>j@=%lJtMv4octftBJe+|1jcO%|;w>v1b? zD57IZRm0i7QI{eXBzQ7C9j1xgu3 zau7=ed=6MCicHSD4Mdr{rW^s2-%7xdtaq2Y#u!3la(HMCCgylB>F>fp_s}rbTZ3e)Jz5eGZoA6llNO(dWqKex-wfo>qKNf3F}u=oHV0 z)pL=*wbhi!_0A_o9&^4R8}AJ(D3OC$LX26kvXq#dc^il^yQa(s$!)Tk6RCi5_1Y&= zEv19~f@qghJUa$Cws$oF1l@|PO#W+s1?|Y0EY-48lyS7e<&`iQ1VM@*;A9Dct)z{I zu<4ByR0StAZ%{%6b#&~=_Lu^ncA?qZOH=Y66O_AJ@c&lY|?6LE(OT4X98lbjeP!=11z$|_t z&p{#zyh*P{hH;gWf7n7`#)QlgI%QLup@|j6!LKN;6cNVPgT1 zO73i0?hLvG!Az%k_8!6y$j6R)=gPEn^Nzs9RVf4 z%;FJ685#-Guffj}Yim1CW8YAS1_t|khh1X=jXk3`o(zq__^^L)VC;ikbN^rf4-fVS zJ$Tq1cfru;?e80XFxVd&``~cc-yiJm!c8-}L7!)I!LP;{-F6ax!Ya8N%a|x(ZsIu= z`Yb?!Q4`;n;2*T{hXx_~GcfpK8)UN0K%65Rda^!c9|H_&A;)HBfjwaRzzDvb-y~d* zm2qtnrsYiiBg*aIh=t3LRDh|4Ci%%-KS3H0QDS@#G#I`Gji7|)senqi{Sf?;hltaDvoi=20Y z|GXl85$+3Z`b1ku=5fMM+mfPWdCYzeV z0Y8I-oCz@gD1xE9e0|7GOG=rCoC&WVJm@@#I1u@UoGS55kQ3&_tjMf0(p{)iLM&GL z%5zh5a%P@UogQ)@7g^+cz>oT*PZ4$tk_;1uckP^tG>9V41a(w339Z@BF>V|?3mdN;jF1mdG&rG7B2oFs8_g}MTy)za zzE#A6$|z|ao7yif?ov!(YkMb)K#H&`#w{9UR$C|;Be3)~Gpsh#mSP*1V1 zdh2T~4iWThCPrMKFb*4UleKZ;CAIO)LdAwZ&e(8O>}1>gf9|lj*vT2h?~#R}NAF0j zQ~4ncLWBd-4(BH?wHL3l&#wsnRP(>Yu#H?skiVQZt2g0JDx0kGnxi5_%U<$O(o*Ca zsp`9%lE+9Ccw@f1C+3hEc8hD)M8x{hnXbjVg8tiPT)~))b1k+I#j+I3QY`DiSXQsN z+{qcl^_f)}f1Aodfom5U>v5`vc~nceQ^D3rl+_9=dh`d~UccYf2{93qRNCOgj*AR- zMU-SbN;Kvcj>)P657NKMg+mX2s?L3`s7491aLEa5**p4W@^-f^r;$x@Rwa&}kT~j> z)PI5a6ZCyBg~!M|T5(SP`bWAY0EL7XJJWr|u41Z|e^Mm_HYmKxm(^;SrcP~z_*;DU zS86ZSrC<+uDOg*%>Y%jX&^2~EFoo}=@4ruVmpxF*IY)8_YaKI1Q`zuY6Tjr}z$ut~ zo4BevbP_pqdK#xW@d^j(SWO+PW%&QFv`Q1FA+8f1nE@lELG#Z(FRNL7X0!&?=e(6D zrTOtIf6Yl8E8kM`Uc{$nuuC|i^tLEsL zrq@c>T}wQq2@Dp=K#8bTr12xVQ{mxL6&ZPMe>_bwLF%%3tob}a7D|*NC{PDn9(tV` z;SYQ-MpkGDWrL9iA}w#_w`9AQxV2R8-z%+yl2LO>9yP3UpI8YrVJm?6guoc>kgL8< zUB_s~Fkti8qsJ;h`>Z|mx%tj}inXS|TMaZg2w((H{#u`Fv#y%ljC z<5apv^JTM>F5Im+khkL}_8KhR?PGu!u`@Z~I36%?XgEpE%vj797QpBho~4R*r;B*Q z$pjj#A-%;p^cv?B6AvB{qyhD&WhP34hq-V~QFI`GZexP7oM{sC3Q=-HB(Mcce~(qC z=%-BW@yF^vPZP&Ox*c;!O{frimhNP|i>){r5MME69-`OnS7#+}eYKC3T+FSM`;XbJ z(2+#QtD_w0@aC0@BnnSL~iGAWH zZcpqSY`0rlLN9BtHx=}k1SfS{e|t9QZ(R?^Mq~amD{cF+nDTi%kC;E@v{6nQ<+M>w z8|Ac7P8;R4QBE7>v{6nQ<+M>wo5zBE&(~?wD=r-VDKCs)-dU3eCdWyWuDgY%=XR9` zCJ(e|0WJbxHaNLpE!%d#R2DiSiz}~m6gCknb);A6@K1^;`o*QGFW?<1e|}2~w$>nr zB!Fr{2rI!?kU|oyAQ42B`X$LAvxc@209QU=)!2U$;gP$8;`+Q7@NOM)ETqJWlJWJZ zbXx=(t*UJ?k*bjVMpW>2x&>4r`6g19lsJ2G;%r!482JL;T|(PL_LMvNT}QwbL2FX^ zEE;3;g05tBN-b4%ykR=te=RO>d+9mYF&v9!snwv}6ohJe70T#NLEUIJcXkDBqe+zx zyb-$CFD?Lg4Q=xZ+;I%M25Dr0hy;GovinJz5y%G44kk!6ByUcVWO7&5HY4sy6iu&U z4Y}F1D!lD6vZI6I8ZFn0F-btLbD@3Iw}|=t^c~xCjh!oScS-0ff5m(l05W2wnjMWM z$bFK)rTGxIwzjdK%(jYA;nCgOw~vSYBtKs%Bg!;YEUEP@>9Dw9$`>2iJ_eRm+v-n2 zki52e>^N2hFG>xN&ynb6%dws8%-hC%l(AlivjsSnnIU^vy>-Dz9GeZpvK}=D*y|Qo z{P<$~Yp5k$ZxPB*f9VN=^zWBzBlT$CZ?V#$1g@g~2C2VSkS=!y{8JyJIlwdu8_v;U zF3SXRffVtA1@juZ{jl=ZPFy>3poMRT8b$xl$i0U654d`%y`WF3gSB$!Q)0&OaHR~F zqJ{I9B?fD06JuFy*1tIH*LE(JCaWb;MT`edy1o8jxS?pWf3nL6H@n4$4eW5%2c04} z!F$0QsaV%ZgBiO6e}+AKIJnuL4i>ft&0qVIr9GVf+#L+efioQ7f9<~8NA7Yt^KOmV zE!3;8C~xezoM#vX8z~j?_^bjR_xc4CUj(ku4k8rtTy z3k@QavZ5&6e^W>p#S>~IW4manZM~{Dxo3hQSmAl*XxQ)QpB;S^a`le>o@Tg4zb|MF z_n_h5;hhtDM?V8T$zj(=hvdh52#HoE@?*FcjW`_Nr z8T{+*TfL(npPvL%O|h-9Yr^TRH$X$f@!=hwt^DckfAVJO{yVYzcN62+-u$*dn>#o7 zZfq`Zz;fv8_mYud?^(*5T%sJe?iyX${)wbE@Ea;SQn>db<6MXr><_==Z+#9;L{Myu zY~ZaN_#AZ!k5aOv_3$KS3J(L@sa$3CN#* zSs;>Kf4xutd!6%t;eWFEy5UgLs1?p?bMbt=J>H%Tel-)XSE^|$txckEL|XnR@ub9) z5>M?CPc5}s1_eo4pIpbW&LF1WThdlZbS|<{Ql5{O+KWYH++6~2zOB)fu{6>XZJV>a zb>5*o@w3Q}{cEp3uBD_nEXd6wNP4-*iJ}rAf5V_-g`40SO>Jm0{otkcg56e8Nr(aH z67O?(Z-_KOvMINX81r^Z)R17UN6$jB>gd590lL$__0NPz`yKs+mWaxXe*chq9rtOJ zHmJ10n%W@OJ%RfYD0RY0`PADU*}@xZ-y@i(3B=bNsq2qgbNx|U)t%jf+`Ugenw~-Y zf9@6_3ydvz1T0X;2FV8_XpN8~lllks1%q$msJ?ieZ7gfsI)kRvZLFM->gaeo%y6$D z0g_QS%g(LI16l9&Sl_oFuL5vfLX`jmWFwdGy)4)|+%I0CVtR_{ZI9_?u{X1wxlM$2 zG%$$|(>?;;5g=&VzjIxW7UYbvitJGwe*`OGro_-h%e`hSq;&ZYYWr84kTRC(V=*`L z%&(f6P;HfM2L(yYjCcPH{TlaIBs#Al*$Tp-Bo%!+jSZ^z(K_221Epl#Q;=~gBtuz4 zEsz+81$pKiGeq|E+L%FeVb%2vrJ$^59R)BMKtXZA4P7EPIJkAiMskhHK2ow{e|v0@ zL*L9kCf|viqMq9bBVdINVQK_jW8h(W)6O!xZ* z+0NG(VEE23oQ2JF5-H3~bze^v&a^7V=hV0*dT=^eR0hklt5Zs_Qi8Xq1ZTf&+rNBE zx!<55zm^dAF|e{Kj0oQgI!ffx=A_J?+qaH!MLv5aw+lvVBuSQ^dmH(gf66*iSw||) z^r5qklnieu#ggOkSyq>PDU=Z4PmgKVZX??nz4RMpSrrzATDIxdPJ$)LkKN z-4jT@`zAI1X01Hq1#%Sjfm?ktY5j5a6`b8b!G-4t*YC0Gu z4ZhMNUN*Oq6T!T(5Ye2?j1q{Vmu?^06 za%)GgsdD(ue~3JespMUSz`YX?aK+M$PU%U>l)ZxDE`(E_LHy3Zj`WReiO%Z3^;f3iu?&>ZH@?(tld#Nu_*1X6La*1J1f^qf7rMC)J&rNf^s~Bd4EJcCMPEK z45GMukqFC*t8YJKqw3>PN{jET9ao=NP1`EAD#;*I%BCt#J+FnpenyIcc*U6~UJ+&g zu%IFer6|X_S-3e@RSPi})_J=rJmILDOyJ>Grc`F7GOP5R$Hw&zx&>7|2zh@3-VMYx zYrSS6f2>)}g9lZ~{SvLqYI*SO zZO(a{x1d%s<7huOc4)Rc+)!+YV$Bjh-Iy$Le=fe{--)p0V5W`_tl$L+F3L^bs_T=D z!<9z8?x0U1Eztx)lgyAU``ALz#(XAC7^1~L`v}I+qD#rC0d;Dd$p?&HFFja+zAuE=ruI3=)F`qG}Vep?7XioO)Vh z*n3UOJ_J*(K!z>LEc&ob4QrlTPb-y2e`U!(QFXDH4*54Cs^Xt_c2Xt#>UcNFCl~N8 z`!FC|&#@S4rBR2rKb`@53X_V`C4}C&sgLwt|77ro<9t2)wfyzR|NeUmKl~qd&-UK= z-k1Lz8DEzlj^1DQKlrE4+2^JA>ErDm|A!}fM<@4l1&Cc3Zub@0rgL}cT;og6e{m-C zR<(mjq7a>6FdXb2?hg(Idk2XQz{BM0(Y8JfJUDi2AKL!He-9QGKDB^L5@^_hj-U-E z$Uta(7~&m45M|$^SlGx+q!dG}tO~AxaLU3wT#B>;=5z!Ct_- z4CMGSiKA5l3Pr&9<%KgxHiYzrf2=~AfGizYkzKM>R&2at5hZ|anCv9EPq=>SVgZhc zOg~GBja}-&6B`G#R_0$CHpf+@6ILnDl2+9j>&Z#rFDA7bx2mMbDyv!N5?NleG@Z+? zI7R+Ksh|-9Ss$r+jw>>D86%eLA9H@8jx`Z6N6bCDIIDv}Sv^yl)9&2mfA}SZEe6tk z%~0VOkeJaxnl|We=GUQ21!XF1)3aPmg2{J+34URR(Qo|<+#RhjWWR~V`PTwi2(R9m zKjWuiorfCzJ^=p{ve_e|hsvp;iT-&aHn|`P$e9UCm&{0P!urxSxy6rnB~ML04$CUu zQuOnjTVzj9ATTXt*NmLWe*v_n%p@>sy^(VLc@nYAf7s^sVv7;b zO?(TH9*NBr?vS$)B7GSKh%}`bB6Q%%qneP3T=1pk`-Uha+%to*;ysfX{rqh6-LeWr zM66N=%dxY_x#Lh+@8EzmaJ0vKK4MA-H+0n@AA?)-oG-aXrF$sdW6Q^Vp(`}^#4mm+ zqJ4Zsw=Ajloq?mje~FM>1Y4fk{bvXJWo1R!9@V^^ zF9(Epel~DX;yW<_qR3x*sUJRqx+y9z3ngwq?y+&M<3>^+lo zpeFe{n|K?Uf4gFrHDnA!tZF4Nmun{FqVi8m>%onG+bJo#Pom zHVK_2mpY9d$0GT5{*)H`t#oomvuMuG$uF$US2!t`r*e5dE|+KNJy}V1u)(`5d)+23 z`pRCbf3=;*0 zXgb67QeMKoB5G7^i;(-eN}0^DIb$@FIZKZ*@>$m=KJ1s(CZ<8_XAsw}D3^dBdowBR zOvO#XW!SIg$c7$k`KL{!n@~b*Vq#5dw#oIjfA((cM-{1+Muv^4E{7YrpH!I<5{gy< zL1)#j;)WthSOT2_CFq)7QtwPyEjnO9v(c)|v`RQv(3@oCBn!Xg)Pf7TePwzEIe_HL z!X{pv+Xy3IU0Tqb!nkZ5G%z$z!0HO#0nbc54=dI3eMl6N0ZqC^@3@@h@J$*TZTSaW ze>J*c*C_a0f4!?;T!i0Y5$z=UMBLG!00R*?qf4E%@!w9muygj4)&-IJ`Zd5vl`cym zsQmFl2K(xQ;80tv)^WreM4Ly*BSv`OIG(U$^7J`SJ=z4L~V_5QfZ(0TIa`@I6!n4I#^WfaUP_vMIqj&U$XXzt-hOz69-n^NAk|sNbGk-H- z*57N_bAoxju>wo-&De6rZ|1RLVIWG{C^*Z-NXH0O~p3k8ML8~ zYFm%2dyd^0*owhU`a7SIdkwLOsmP!;Y9z)p+_q_$Yk}}vk~Kh5Z$<16Dje;FWp#97 zzeK-2Ks23UeblW~?w%5V#`n4hEY!>fSp z_KqYFwum{!uSK}iGjIjSSco#!`Sw|pe)}Z9FlFOxXIl{Nh_VRD4r7<-YvU0E93Omn#%;SIm<3}g-xqXxJ zb+ApBbN|)z)U7#Gna6|^R2*_sugT+#|vi!BMO)fXmsRwP1rAMzh? z)zRF3`WY23A0RM~XTTOUkMOKql*I6A3J|iUU=6{X+ax+t+b7C-8d0+|>Z?2OXV|lc zgPZ;7U}1aE{Ix$>+QaG3-NC>dIKu({*Y3N0-B+32BFH0QS25Q?WsA1A4!?#*$Iy@3elcsIe&V>`4K6X5_A@e?pcbo5i# zF=hq2XH6D=!wtUyZj)SG8@15n-d6iDBGWAi=7K!dkgowPYQoW3uiNYPI1$U*D2-5Y z&!@#b_lF|f^Jvus>kOeh1m6tD-T>3&mZdnR;+TqKK5dS9uv@}0KLG#F@y}Xpl6I{= z6B|2@Rmt}#uzY4mw^EkfMbm4WIjWAdT6UxrAZTWPXW7GQjuVW;%_isvf&IvN#^GKW z&w#+J$1xxQD-CJaB561#TfalTz)OnWh1g9Y z<>R*`YDbf5n+)XlLkyFPu{6NI+Pwc_VP$zgd&o)T$VRZNM~?sfvY6j12+XdvpN9A#J#iFty2 z!@14D%nEK2_RH54fs3db^rs8$tIOjP$5;e3S;kL7h$XzF2&@!$;bz_jZL(-hS&v(B zLlGTQs#?}g@68n2&CjB?(U7c-DEMXADv8%u7Fxy zfP;;{OoTD)yebY>pp+#LtMHh~akn6X?G(@68HTg4<)fjwuK7gy?FYm&ib6qKDp1NO zl7m<(;B&xAQDk!FZ6M0rHRTAH{8j>vWWBrGHO3Gclfy%EFfqr2Nq-j(x`&3*-y2SU z`or#c+}+z7b_YE@87vh4RmZiS@uUCf=yR|&qKu;zF0X{iAP7(g3v$gRgA(fCp+o(Di z&seE~&q5VEEC@t7#j{f%qdCCv4AgN>aJ@wp!#YI44YN_gNG_*sXB0{^RGOjE3>yo8 zRB~t2a%a#j2xdCPv-c2wKt6VVyj2ac6L&6>IzN5K_FQA<3f$3(Hz~0bL#PlvaYI4L z6L*l6K8aPeP55LsRLm|px})c=?XXL#*gy$V7B&+R!H#7@pWeR`3CS_=;d=-#p%