From 5dbbf50f624b046c8f0a21112e13b85506109b03 Mon Sep 17 00:00:00 2001 From: whyrusleeping Date: Tue, 4 Aug 2020 17:01:21 -0700 Subject: [PATCH 1/6] recheck best known tipset after waiting for random beacon entry to become available --- api/api_full.go | 8 +++++++ api/apistruct/struct.go | 6 ++++++ miner/miner.go | 48 ++++++++++++++++++++++++++--------------- 3 files changed, 45 insertions(+), 17 deletions(-) diff --git a/api/api_full.go b/api/api_full.go index b47d10d03..6e9b999de 100644 --- a/api/api_full.go +++ b/api/api_full.go @@ -107,6 +107,14 @@ type FullNode interface { // ChainExport returns a stream of bytes with CAR dump of chain data. ChainExport(context.Context, types.TipSetKey) (<-chan []byte, error) + // MethodGroup: Beacon + // The Beacon method group contains methods for interacting with the random beacon (DRAND) + + // BeaconGetEntry returns the beacon entry for the given filecoin epoch. If + // the entry has not yet been produced, the call will block until the entry + // becomes available + BeaconGetEntry(ctx context.Context, epoch abi.ChainEpoch) (*types.BeaconEntry, error) + // GasEstimateGasLimit estimates gas used by the message and returns it. // It fails if message fails to execute. GasEstimateGasLimit(context.Context, *types.Message, types.TipSetKey) (int64, error) diff --git a/api/apistruct/struct.go b/api/apistruct/struct.go index 6481223da..561783abc 100644 --- a/api/apistruct/struct.go +++ b/api/apistruct/struct.go @@ -85,6 +85,8 @@ type FullNodeStruct struct { ChainGetPath func(context.Context, types.TipSetKey, types.TipSetKey) ([]*api.HeadChange, error) `perm:"read"` ChainExport func(context.Context, types.TipSetKey) (<-chan []byte, error) `perm:"read"` + BeaconGetEntry func(ctx context.Context, epoch abi.ChainEpoch) (*types.BeaconEntry, error) `perm:"read"` + GasEstimateGasPrice func(context.Context, uint64, address.Address, int64, types.TipSetKey) (types.BigInt, error) `perm:"read"` GasEstimateGasLimit func(context.Context, *types.Message, types.TipSetKey) (int64, error) `perm:"read"` @@ -598,6 +600,10 @@ func (c *FullNodeStruct) ChainExport(ctx context.Context, tsk types.TipSetKey) ( return c.Internal.ChainExport(ctx, tsk) } +func (c *FullNodeStruct) BeaconGetEntry(ctx context.Context, epoch abi.ChainEpoch) (*types.BeaconEntry, error) { + return c.Internal.BeaconGetEntry(ctx, epoch) +} + func (c *FullNodeStruct) SyncState(ctx context.Context) (*api.SyncState, error) { return c.Internal.SyncState(ctx) } diff --git a/miner/miner.go b/miner/miner.go index 709424aba..61a5e1bb0 100644 --- a/miner/miner.go +++ b/miner/miner.go @@ -147,25 +147,39 @@ func (m *Miner) mine(ctx context.Context) { default: } - prebase, err := m.GetBestMiningCandidate(ctx) - if err != nil { - log.Errorf("failed to get best mining candidate: %s", err) - m.niceSleep(time.Second * 5) - continue + var base *MiningBase + var onDone func(bool, error) + var injectNulls abi.ChainEpoch + + for { + prebase, err := m.GetBestMiningCandidate(ctx) + if err != nil { + log.Errorf("failed to get best mining candidate: %s", err) + m.niceSleep(time.Second * 5) + continue + } + + if base != nil && base.TipSet.Height() == prebase.TipSet.Height() && base.NullRounds == prebase.NullRounds { + break + } + + // Wait until propagation delay period after block we plan to mine on + onDone, injectNulls, err = m.waitFunc(ctx, prebase.TipSet.MinTimestamp()) + if err != nil { + log.Error(err) + continue + } + + // just wait for the beacon entry to become available before we select our final mining base + _, err = m.api.BeaconGetEntry(ctx, prebase.TipSet.Height()+prebase.NullRounds+1) + if err != nil { + log.Errorf("failed getting beacon entry: %s", err) + continue + } + + base = prebase } - // Wait until propagation delay period after block we plan to mine on - onDone, injectNulls, err := m.waitFunc(ctx, prebase.TipSet.MinTimestamp()) - if err != nil { - log.Error(err) - continue - } - - base, err := m.GetBestMiningCandidate(ctx) - if err != nil { - log.Errorf("failed to get best mining candidate: %s", err) - continue - } if base.TipSet.Equals(lastBase.TipSet) && lastBase.NullRounds == base.NullRounds { log.Warnf("BestMiningCandidate from the previous round: %s (nulls:%d)", lastBase.TipSet.Cids(), lastBase.NullRounds) m.niceSleep(time.Duration(build.BlockDelaySecs) * time.Second) From 57d57e54a1f9f973c9286d01b24afdc8245ac40e Mon Sep 17 00:00:00 2001 From: whyrusleeping Date: Wed, 5 Aug 2020 18:44:55 -0700 Subject: [PATCH 2/6] fix api inclusion --- node/impl/full.go | 1 + 1 file changed, 1 insertion(+) diff --git a/node/impl/full.go b/node/impl/full.go index 46a706f2b..47f09bfd3 100644 --- a/node/impl/full.go +++ b/node/impl/full.go @@ -29,6 +29,7 @@ type FullNodeAPI struct { full.MsigAPI full.WalletAPI full.SyncAPI + full.BeaconAPI } // MpoolEstimateGasPrice estimates gas price From 32a5c52105abf360e7c2ef26c9716e64bd5fa723 Mon Sep 17 00:00:00 2001 From: whyrusleeping Date: Wed, 5 Aug 2020 18:52:43 -0700 Subject: [PATCH 3/6] add missing file --- node/impl/full/beacon.go | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 node/impl/full/beacon.go diff --git a/node/impl/full/beacon.go b/node/impl/full/beacon.go new file mode 100644 index 000000000..07037f6e1 --- /dev/null +++ b/node/impl/full/beacon.go @@ -0,0 +1,35 @@ +package full + +import ( + "context" + "fmt" + + "github.com/filecoin-project/lotus/chain/beacon" + "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/specs-actors/actors/abi" + "go.uber.org/fx" +) + +type BeaconAPI struct { + fx.In + + Beacon beacon.RandomBeacon +} + +func (a *BeaconAPI) BeaconGetEntry(ctx context.Context, epoch abi.ChainEpoch) (*types.BeaconEntry, error) { + rr := a.Beacon.MaxBeaconRoundForEpoch(epoch, types.BeaconEntry{}) + e := a.Beacon.Entry(ctx, rr) + + select { + case be, ok := <-e: + if !ok { + return nil, fmt.Errorf("beacon get returned no value") + } + if be.Err != nil { + return nil, be.Err + } + return &be.Entry, nil + case <-ctx.Done(): + return nil, ctx.Err() + } +} From 402b4b6c70ee63535f5db743be3692beacc3ed45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 7 Aug 2020 01:01:45 +0200 Subject: [PATCH 4/6] mpool: Fix SelectMessages hanging --- chain/messagepool/selection.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chain/messagepool/selection.go b/chain/messagepool/selection.go index 030981409..db83aa128 100644 --- a/chain/messagepool/selection.go +++ b/chain/messagepool/selection.go @@ -117,7 +117,7 @@ tailLoop: continue } // this chain needs to be trimmed - last = i + last += i continue tailLoop } From 41bc8f14a234156919dd7441ef82f45b1503ee31 Mon Sep 17 00:00:00 2001 From: whyrusleeping Date: Thu, 6 Aug 2020 17:05:35 -0700 Subject: [PATCH 5/6] fix tests --- api/test/mining.go | 53 +++++++++++++++++++++------------------------- miner/miner.go | 13 +++++++++++- 2 files changed, 36 insertions(+), 30 deletions(-) diff --git a/api/test/mining.go b/api/test/mining.go index 373266b0e..7667b3e61 100644 --- a/api/test/mining.go +++ b/api/test/mining.go @@ -132,21 +132,22 @@ func TestDealMining(t *testing.T, b APIBuilder, blocktime time.Duration, carExpo done := make(chan struct{}) minedTwo := make(chan struct{}) + m2addr, err := sn[1].ActorAddress(context.TODO()) + if err != nil { + t.Fatal(err) + } + go func() { - doneMinedTwo := false defer close(done) - prevExpect := 0 for atomic.LoadInt32(&mine) != 0 { - wait := make(chan int, 2) + wait := make(chan int) mdone := func(mined bool, err error) { - go func() { - n := 0 - if mined { - n = 1 - } - wait <- n - }() + n := 0 + if mined { + n = 1 + } + wait <- n } if err := sn[0].MineOne(ctx, miner.MineReq{Done: mdone}); err != nil { @@ -166,33 +167,27 @@ func TestDealMining(t *testing.T, b APIBuilder, blocktime time.Duration, carExpo continue } - for { - n := 0 - for i, node := range sn { - mb, err := node.MiningBase(ctx) - if err != nil { - t.Error(err) - return - } + var nodeOneMined bool + for _, node := range sn { + mb, err := node.MiningBase(ctx) + if err != nil { + t.Error(err) + return + } - if len(mb.Cids()) != expect { - log.Warnf("node %d mining base not complete (%d, want %d)", i, len(mb.Cids()), expect) - continue + for _, b := range mb.Blocks() { + if b.Miner == m2addr { + nodeOneMined = true + break } - n++ } - if n == len(sn) { - break - } - time.Sleep(blocktime) + } - if prevExpect == 2 && expect == 2 && !doneMinedTwo { + if nodeOneMined { close(minedTwo) - doneMinedTwo = true } - prevExpect = expect } }() diff --git a/miner/miner.go b/miner/miner.go index 61a5e1bb0..92fc7e2bc 100644 --- a/miner/miner.go +++ b/miner/miner.go @@ -6,10 +6,11 @@ import ( "crypto/rand" "encoding/binary" "fmt" - "github.com/filecoin-project/lotus/chain/gen/slashfilter" "sync" "time" + "github.com/filecoin-project/lotus/chain/gen/slashfilter" + "github.com/filecoin-project/go-address" "github.com/filecoin-project/specs-actors/actors/abi" "github.com/filecoin-project/specs-actors/actors/crypto" @@ -162,6 +163,16 @@ func (m *Miner) mine(ctx context.Context) { if base != nil && base.TipSet.Height() == prebase.TipSet.Height() && base.NullRounds == prebase.NullRounds { break } + if base != nil { + onDone(false, nil) + } + + // TODO: need to change the orchestration here. the problem is that + // we are waiting *after* we enter this loop and selecta mining + // candidate, which is almost certain to change in multiminer + // tests. Instead, we should block before entering the loop, so + // that when the test 'MineOne' function is triggered, we pull our + // best mining candidate at that time. // Wait until propagation delay period after block we plan to mine on onDone, injectNulls, err = m.waitFunc(ctx, prebase.TipSet.MinTimestamp()) From de86167a7b97cb42cd9f4e8cdb7b46b01d4f04cf Mon Sep 17 00:00:00 2001 From: whyrusleeping Date: Thu, 6 Aug 2020 17:26:28 -0700 Subject: [PATCH 6/6] dont double close channels --- api/test/mining.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/api/test/mining.go b/api/test/mining.go index 7667b3e61..581697440 100644 --- a/api/test/mining.go +++ b/api/test/mining.go @@ -140,6 +140,7 @@ func TestDealMining(t *testing.T, b APIBuilder, blocktime time.Duration, carExpo go func() { defer close(done) + complChan := minedTwo for atomic.LoadInt32(&mine) != 0 { wait := make(chan int) mdone := func(mined bool, err error) { @@ -184,8 +185,9 @@ func TestDealMining(t *testing.T, b APIBuilder, blocktime time.Duration, carExpo } - if nodeOneMined { - close(minedTwo) + if nodeOneMined && complChan != nil { + close(complChan) + complChan = nil } }