From 187317a00461fb6fac67ac4f4cf5c3dfb748e969 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 23 Oct 2019 16:45:03 +0200 Subject: [PATCH 1/4] sync: Do some things in parallel in ValidateBlock --- chain/sync.go | 134 +++++++++++++++++++++++++++++++++----------------- go.mod | 1 + go.sum | 2 + 3 files changed, 92 insertions(+), 45 deletions(-) diff --git a/chain/sync.go b/chain/sync.go index def95c25e..67ffe1ba4 100644 --- a/chain/sync.go +++ b/chain/sync.go @@ -16,6 +16,7 @@ import ( "github.com/filecoin-project/lotus/chain/store" "github.com/filecoin-project/lotus/chain/types" + "github.com/Gurpartap/async" amt "github.com/filecoin-project/go-amt-ipld" "github.com/ipfs/go-cid" dstore "github.com/ipfs/go-datastore" @@ -456,19 +457,7 @@ func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock) err return xerrors.Errorf("load parent tipset failed (%s): %w", h.Parents, err) } - stateroot, precp, err := syncer.sm.TipSetState(ctx, baseTs) - if err != nil { - return xerrors.Errorf("get tipsetstate(%d, %s) failed: %w", h.Height, h.Parents, err) - } - - if stateroot != h.ParentStateRoot { - return xerrors.Errorf("parent state root did not match computed state (%s != %s)", stateroot, h.ParentStateRoot) - } - - if precp != h.ParentMessageReceipts { - return xerrors.Errorf("parent receipts root did not match computed value (%s != %s)", precp, h.ParentMessageReceipts) - } - + // fast checks first if h.Timestamp > uint64(time.Now().Unix()+build.AllowableClockDrift) { return xerrors.Errorf("block was from the future") } @@ -478,43 +467,97 @@ func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock) err return xerrors.Errorf("block was generated too soon (h.ts:%d < base.mints:%d + BLOCK_DELAY:%d * tkts.len:%d)", h.Timestamp, baseTs.MinTimestamp(), build.BlockDelay, len(h.Tickets)) } - if err := syncer.minerIsValid(ctx, h.Miner, baseTs); err != nil { - return xerrors.Errorf("minerIsValid failed: %w", err) - } + waddrPromise := async.Any(func() (interface{}, error) { + stateroot, precp, err := syncer.sm.TipSetState(ctx, baseTs) + if err != nil { + return nil, xerrors.Errorf("get tipsetstate(%d, %s) failed: %w", h.Height, h.Parents, err) + } - waddr, err := stmgr.GetMinerWorker(ctx, syncer.sm, stateroot, h.Miner) + if stateroot != h.ParentStateRoot { + return nil, xerrors.Errorf("parent state root did not match computed state (%s != %s)", stateroot, h.ParentStateRoot) + } + + if precp != h.ParentMessageReceipts { + return nil, xerrors.Errorf("parent receipts root did not match computed value (%s != %s)", precp, h.ParentMessageReceipts) + } + + waddr, err := stmgr.GetMinerWorker(ctx, syncer.sm, stateroot, h.Miner) + if err != nil { + return nil, xerrors.Errorf("GetMinerWorker failed: %w", err) + } + return waddr, nil + }) + + minerCheck := async.Err(func() error { + if err := syncer.minerIsValid(ctx, h.Miner, baseTs); err != nil { + return xerrors.Errorf("minerIsValid failed: %w", err) + } + return nil + }) + + waddri, err := waddrPromise.AwaitContext(ctx) if err != nil { - return xerrors.Errorf("GetMinerWorker failed: %w", err) + return err } + waddr := waddri.(address.Address) - if err := h.CheckBlockSignature(ctx, waddr); err != nil { - return xerrors.Errorf("check block signature failed: %w", err) + blockSigCheck := async.Err(func() error { + if err := h.CheckBlockSignature(ctx, waddr); err != nil { + return xerrors.Errorf("check block signature failed: %w", err) + } + return nil + }) + + tktsCheck := async.Err(func() error { + if err := syncer.validateTickets(ctx, waddr, h.Tickets, baseTs); err != nil { + return xerrors.Errorf("validating block tickets failed: %w", err) + } + return nil + }) + + eproofCheck := async.Err(func() error { + rand, err := syncer.sm.ChainStore().GetRandomness(ctx, baseTs.Cids(), h.Tickets, build.RandomnessLookback) + if err != nil { + return xerrors.Errorf("failed to get randomness for verifying election proof: %w", err) + } + + if err := VerifyElectionProof(ctx, h.ElectionProof, rand, waddr); err != nil { + return xerrors.Errorf("checking eproof failed: %w", err) + } + return nil + }) + + winnerCheck := async.Err(func() error { + mpow, tpow, err := stmgr.GetPower(ctx, syncer.sm, baseTs, h.Miner) + if err != nil { + return xerrors.Errorf("failed getting power: %w", err) + } + + if !types.PowerCmp(h.ElectionProof, mpow, tpow) { + return xerrors.Errorf("miner created a block but was not a winner") + } + return nil + }) + + msgsCheck := async.Err(func() error { + if err := syncer.checkBlockMessages(ctx, b, baseTs); err != nil { + return xerrors.Errorf("block had invalid messages: %w", err) + } + return nil + }) + + await := []async.ErrorFuture{ + minerCheck, + tktsCheck, + blockSigCheck, + eproofCheck, + winnerCheck, + msgsCheck, } - - if err := syncer.validateTickets(ctx, waddr, h.Tickets, baseTs); err != nil { - return xerrors.Errorf("validating block tickets failed: %w", err) - } - - rand, err := syncer.sm.ChainStore().GetRandomness(ctx, baseTs.Cids(), h.Tickets, build.RandomnessLookback) - if err != nil { - return xerrors.Errorf("failed to get randomness for verifying election proof: %w", err) - } - - if err := VerifyElectionProof(ctx, h.ElectionProof, rand, waddr); err != nil { - return xerrors.Errorf("checking eproof failed: %w", err) - } - - mpow, tpow, err := stmgr.GetPower(ctx, syncer.sm, baseTs, h.Miner) - if err != nil { - return xerrors.Errorf("failed getting power: %w", err) - } - - if !types.PowerCmp(h.ElectionProof, mpow, tpow) { - return xerrors.Errorf("miner created a block but was not a winner") - } - - if err := syncer.checkBlockMessages(ctx, b, baseTs); err != nil { - return xerrors.Errorf("block had invalid messages: %w", err) + for _, fut := range await { + if err := fut.AwaitContext(ctx); err != nil { + return err + } } return nil @@ -776,6 +819,7 @@ func (syncer *Syncer) syncFork(ctx context.Context, from *types.TipSet, to *type func (syncer *Syncer) syncMessagesAndCheckState(ctx context.Context, headers []*types.TipSet) error { syncer.syncState.SetHeight(0) + return syncer.iterFullTipsets(ctx, headers, func(ctx context.Context, fts *store.FullTipSet) error { log.Debugw("validating tipset", "height", fts.TipSet().Height(), "size", len(fts.TipSet().Cids())) if err := syncer.ValidateTipSet(ctx, fts); err != nil { diff --git a/go.mod b/go.mod index 48098edda..71426bae1 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( contrib.go.opencensus.io/exporter/jaeger v0.1.0 github.com/BurntSushi/toml v0.3.1 github.com/GeertJohan/go.rice v1.0.0 + github.com/Gurpartap/async v0.0.0-20180927173644-4f7f499dd9ee github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect github.com/fatih/color v1.7.0 // indirect github.com/filecoin-project/go-amt-ipld v0.0.0-20190919045431-3650716fff16 diff --git a/go.sum b/go.sum index be3b0270d..1a3b55548 100644 --- a/go.sum +++ b/go.sum @@ -12,6 +12,8 @@ github.com/GeertJohan/go.incremental v1.0.0 h1:7AH+pY1XUgQE4Y1HcXYaMqAI0m9yrFqo/ github.com/GeertJohan/go.incremental v1.0.0/go.mod h1:6fAjUhbVuX1KcMD3c8TEgVUqmo4seqhv0i0kdATSkM0= github.com/GeertJohan/go.rice v1.0.0 h1:KkI6O9uMaQU3VEKaj01ulavtF7o1fWT7+pk/4voiMLQ= github.com/GeertJohan/go.rice v1.0.0/go.mod h1:eH6gbSOAUv07dQuZVnBmoDP8mgsM1rtixis4Tib9if0= +github.com/Gurpartap/async v0.0.0-20180927173644-4f7f499dd9ee h1:8doiS7ib3zi6/K172oDhSKU0dJ/miJramo9NITOMyZQ= +github.com/Gurpartap/async v0.0.0-20180927173644-4f7f499dd9ee/go.mod h1:W0GbEAA4uFNYOGG2cJpmFJ04E6SD1NLELPYZB57/7AY= github.com/Kubuxu/go-os-helper v0.0.1/go.mod h1:N8B+I7vPCT80IcP58r50u4+gEEcsZETFUpAzWW2ep1Y= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= From d9fe9a6ea451cfdb58242aea5ffcb27f2f5eb788 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 11 Nov 2019 20:26:14 +0100 Subject: [PATCH 2/4] sync: Post merge fixes --- chain/sync.go | 98 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 59 insertions(+), 39 deletions(-) diff --git a/chain/sync.go b/chain/sync.go index 6fd03f98e..68e059e5f 100644 --- a/chain/sync.go +++ b/chain/sync.go @@ -483,6 +483,43 @@ func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock) err return xerrors.Errorf("load parent tipset failed (%s): %w", h.Parents, err) } + // fast checks first + if h.Timestamp > uint64(time.Now().Unix()+build.AllowableClockDrift) { + return xerrors.Errorf("block was from the future") + } + + if h.Timestamp < baseTs.MinTimestamp()+uint64(build.BlockDelay*len(h.Tickets)) { + log.Warn("timestamp funtimes: ", h.Timestamp, baseTs.MinTimestamp(), len(h.Tickets)) + return xerrors.Errorf("block was generated too soon (h.ts:%d < base.mints:%d + BLOCK_DELAY:%d * tkts.len:%d)", h.Timestamp, baseTs.MinTimestamp(), build.BlockDelay, len(h.Tickets)) + } + + winnerCheck := async.Err(func() error { + mpow, tpow, err := stmgr.GetPower(ctx, syncer.sm, baseTs, h.Miner) + if err != nil { + return xerrors.Errorf("failed getting power: %w", err) + } + + if !types.PowerCmp(h.ElectionProof, mpow, tpow) { + return xerrors.Errorf("miner created a block but was not a winner") + } + return nil + }) + + msgsCheck := async.Err(func() error { + if err := syncer.checkBlockMessages(ctx, b, baseTs); err != nil { + return xerrors.Errorf("block had invalid messages: %w", err) + } + return nil + }) + + minerCheck := async.Err(func() error { + if err := syncer.minerIsValid(ctx, h.Miner, baseTs); err != nil { + return xerrors.Errorf("minerIsValid failed: %w", err) + } + return nil + }) + + // Stuff that needs stateroot / worker address stateroot, precp, err := syncer.sm.TipSetState(ctx, baseTs) if err != nil { return xerrors.Errorf("get tipsetstate(%d, %s) failed: %w", h.Height, h.Parents, err) @@ -507,53 +544,36 @@ func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock) err return xerrors.Errorf("parent receipts root did not match computed value (%s != %s)", precp, h.ParentMessageReceipts) } - if h.Timestamp > uint64(time.Now().Unix()+build.AllowableClockDrift) { - return xerrors.Errorf("block was from the future: %w", ErrTemporal) - } - - if h.Timestamp < baseTs.MinTimestamp()+uint64(build.BlockDelay*len(h.Tickets)) { - log.Warn("timestamp funtimes: ", h.Timestamp, baseTs.MinTimestamp(), len(h.Tickets)) - return xerrors.Errorf("block was generated too soon (h.ts:%d < base.mints:%d + BLOCK_DELAY:%d * tkts.len:%d)", h.Timestamp, baseTs.MinTimestamp(), build.BlockDelay, len(h.Tickets)) - } - - if err := syncer.minerIsValid(ctx, h.Miner, baseTs); err != nil { - return xerrors.Errorf("minerIsValid failed: %w", err) - } - waddr, err := stmgr.GetMinerWorkerRaw(ctx, syncer.sm, stateroot, h.Miner) if err != nil { return xerrors.Errorf("GetMinerWorkerRaw failed: %w", err) } - if err := h.CheckBlockSignature(ctx, waddr); err != nil { - return xerrors.Errorf("check block signature failed: %w", err) - } + blockSigCheck := async.Err(func() error { + if err := h.CheckBlockSignature(ctx, waddr); err != nil { + return xerrors.Errorf("check block signature failed: %w", err) + } + return nil + }) - if err := syncer.validateTickets(ctx, waddr, h.Tickets, baseTs); err != nil { - return xerrors.Errorf("validating block tickets failed: %w", err) - } + tktsCheck := async.Err(func() error { + if err := syncer.validateTickets(ctx, waddr, h.Tickets, baseTs); err != nil { + return xerrors.Errorf("validating block tickets failed: %w", err) + } + return nil + }) - rand, err := syncer.sm.ChainStore().GetRandomness(ctx, baseTs.Cids(), h.Tickets, build.EcRandomnessLookback) - if err != nil { - return xerrors.Errorf("failed to get randomness for verifying election proof: %w", err) - } + eproofCheck := async.Err(func() error { + rand, err := syncer.sm.ChainStore().GetRandomness(ctx, baseTs.Cids(), h.Tickets, build.EcRandomnessLookback) + if err != nil { + return xerrors.Errorf("failed to get randomness for verifying election proof: %w", err) + } - if err := VerifyElectionProof(ctx, h.ElectionProof, rand, waddr); err != nil { - return xerrors.Errorf("checking eproof failed: %w", err) - } - - mpow, tpow, err := stmgr.GetPower(ctx, syncer.sm, baseTs, h.Miner) - if err != nil { - return xerrors.Errorf("failed getting power: %w", err) - } - - if !types.PowerCmp(h.ElectionProof, mpow, tpow) { - return xerrors.Errorf("miner created a block but was not a winner") - } - - if err := syncer.checkBlockMessages(ctx, b, baseTs); err != nil { - return xerrors.Errorf("block had invalid messages: %w", err) - } + if err := VerifyElectionProof(ctx, h.ElectionProof, rand, waddr); err != nil { + return xerrors.Errorf("checking eproof failed: %w", err) + } + return nil + }) await := []async.ErrorFuture{ minerCheck, From eaddb8abe80ccb93ae67c690a52558c39fefff31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 11 Nov 2019 20:30:49 +0100 Subject: [PATCH 3/4] sync: use muliterror in ValidateBlock --- chain/sync.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/chain/sync.go b/chain/sync.go index 68e059e5f..cc3f99205 100644 --- a/chain/sync.go +++ b/chain/sync.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "github.com/hashicorp/go-multierror" "sync" "time" @@ -583,13 +584,15 @@ func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock) err winnerCheck, msgsCheck, } + + var merr error for _, fut := range await { if err := fut.AwaitContext(ctx); err != nil { - return err + err = multierror.Append(merr, err) } } - return nil + return merr } func (syncer *Syncer) checkBlockMessages(ctx context.Context, b *types.FullBlock, baseTs *types.TipSet) error { From b72463e6fc0a2b9ce4efed9c0e0c2d9b1b05e7ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 11 Nov 2019 20:32:30 +0100 Subject: [PATCH 4/4] mod tidy --- chain/sync.go | 24 ++++++++++++------------ go.mod | 1 + 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/chain/sync.go b/chain/sync.go index cc3f99205..8e62eefa3 100644 --- a/chain/sync.go +++ b/chain/sync.go @@ -4,23 +4,13 @@ import ( "context" "errors" "fmt" - "github.com/hashicorp/go-multierror" "sync" "time" - "github.com/filecoin-project/go-bls-sigs" - "github.com/filecoin-project/lotus/api" - "github.com/filecoin-project/lotus/build" - "github.com/filecoin-project/lotus/chain/actors" - "github.com/filecoin-project/lotus/chain/address" - "github.com/filecoin-project/lotus/chain/blocksync" - "github.com/filecoin-project/lotus/chain/state" - "github.com/filecoin-project/lotus/chain/stmgr" - "github.com/filecoin-project/lotus/chain/store" - "github.com/filecoin-project/lotus/chain/types" - "github.com/Gurpartap/async" amt "github.com/filecoin-project/go-amt-ipld" + "github.com/filecoin-project/go-bls-sigs" + "github.com/hashicorp/go-multierror" "github.com/ipfs/go-cid" dstore "github.com/ipfs/go-datastore" hamt "github.com/ipfs/go-hamt-ipld" @@ -30,6 +20,16 @@ import ( cbg "github.com/whyrusleeping/cbor-gen" "go.opencensus.io/trace" "golang.org/x/xerrors" + + "github.com/filecoin-project/lotus/api" + "github.com/filecoin-project/lotus/build" + "github.com/filecoin-project/lotus/chain/actors" + "github.com/filecoin-project/lotus/chain/address" + "github.com/filecoin-project/lotus/chain/blocksync" + "github.com/filecoin-project/lotus/chain/state" + "github.com/filecoin-project/lotus/chain/stmgr" + "github.com/filecoin-project/lotus/chain/store" + "github.com/filecoin-project/lotus/chain/types" ) var log = logging.Logger("chain") diff --git a/go.mod b/go.mod index 6a7d97b64..1e9c0b32d 100644 --- a/go.mod +++ b/go.mod @@ -16,6 +16,7 @@ require ( github.com/gbrlsnchs/jwt/v3 v3.0.0-beta.1 github.com/go-ole/go-ole v1.2.4 // indirect github.com/gorilla/websocket v1.4.0 + github.com/hashicorp/go-multierror v1.0.0 github.com/hashicorp/golang-lru v0.5.3 github.com/influxdata/influxdb1-client v0.0.0-20190809212627-fc22c7df067e github.com/ipfs/go-bitswap v0.1.8