diff --git a/consensus/bor/bor.go b/consensus/bor/bor.go index a59fecd2e8f3750ad9fe7fbbb21d808dacf7fc6d..a33681c1d827e07649646c737f4c9566289f78a8 100644 --- a/consensus/bor/bor.go +++ b/consensus/bor/bor.go @@ -205,13 +205,13 @@ func CalcDifficulty(snap *Snapshot, signer common.Address, epoch uint64) *big.In return big.NewInt(0).SetUint64(snap.inturn(snap.Number+1, signer, epoch)) } -// CalcProducerDelay is the producer delay algorithm based on block time. -func CalcProducerDelay(snap *Snapshot, signer common.Address, period uint64, epoch uint64, producerDelay uint64) uint64 { - // if block is epoch start block, proposer will be inturn signer - if (snap.Number+1)%epoch == 0 { +// CalcProducerDelay is the block delay algorithm based on block time and period / producerDelay values in genesis +func CalcProducerDelay(number uint64, period uint64, sprint uint64, producerDelay uint64) uint64 { + // When the block is the first block of the sprint, it is expected to be delayed by `producerDelay`. + // That is to allow time for block propagation in the last sprint + if number%sprint == 0 { return producerDelay } - return period } @@ -331,6 +331,17 @@ func (c *Bor) verifyHeader(chain consensus.ChainReader, header *types.Header, pa } number := header.Number.Uint64() + var parent *types.Header + if len(parents) > 0 { // if parents is nil, len(parents) is zero + parent = parents[len(parents)-1] + } else if number > 0 { + parent = chain.GetHeader(header.ParentHash, number-1) + } + + if parent != nil && header.Time < parent.Time+CalcProducerDelay(number, c.config.Period, c.config.Sprint, c.config.ProducerDelay) { + return consensus.ErrBlockTooSoon + } + // Don't waste time checking blocks from the future if header.Time > uint64(time.Now().Unix()) { return consensus.ErrFutureBlock @@ -654,7 +665,7 @@ func (c *Bor) Prepare(chain consensus.ChainReader, header *types.Header) error { return consensus.ErrUnknownAncestor } - header.Time = parent.Time + CalcProducerDelay(snap, c.signer, c.config.Period, c.config.Sprint, c.config.ProducerDelay) + header.Time = parent.Time + CalcProducerDelay(number, c.config.Period, c.config.Sprint, c.config.ProducerDelay) if header.Time < uint64(time.Now().Unix()) { header.Time = uint64(time.Now().Unix()) } @@ -1190,7 +1201,7 @@ func (c *Bor) CommitStates( } // check if chain id matches with event record - if eventRecord.ChainID != "" && eventRecord.ChainID != c.chainConfig.ChainID.String() { + if eventRecord.ChainID != c.chainConfig.ChainID.String() { return fmt.Errorf( "Chain id proposed state in span, %s, and bor chain id, %s, doesn't match", eventRecord.ChainID, diff --git a/consensus/bor/bor_test/bor_test.go b/consensus/bor/bor_test/bor_test.go index ce1585f6f2f8788c8b2c1d709b00a706685ee6fd..13fb4f2e6bed889a9580412953c6c38bba19f07a 100644 --- a/consensus/bor/bor_test/bor_test.go +++ b/consensus/bor/bor_test/bor_test.go @@ -32,7 +32,7 @@ func TestCommitSpan(t *testing.T) { db := init.ethereum.ChainDb() block := init.genesis.ToBlock(db) // Build 1st block's header - header := buildMinimalNextHeader(t, block, init.genesis.Config.Bor.Period) + header := buildMinimalNextHeader(t, block, init.genesis.Config.Bor) statedb, err := chain.State() if err != nil { @@ -89,7 +89,7 @@ func TestIsValidatorAction(t *testing.T) { db := init.ethereum.ChainDb() block := init.genesis.ToBlock(db) - header := buildMinimalNextHeader(t, block, init.genesis.Config.Bor.Period) + header := buildMinimalNextHeader(t, block, init.genesis.Config.Bor) statedb, err := chain.State() if err != nil { t.Fatalf("%s", err) @@ -101,7 +101,7 @@ func TestIsValidatorAction(t *testing.T) { var headers []*types.Header for i := int64(2); i <= 255; i++ { - header := buildMinimalNextHeader(t, block, init.genesis.Config.Bor.Period) + header := buildMinimalNextHeader(t, block, init.genesis.Config.Bor) headers = append(headers, header) block = types.NewBlockWithHeader(header) } diff --git a/consensus/bor/bor_test/helper.go b/consensus/bor/bor_test/helper.go index 54ab949983a9861a096f4182bca649563ac1790a..9a5c0facc561a073075ff4150a590103ce8f3908 100644 --- a/consensus/bor/bor_test/helper.go +++ b/consensus/bor/bor_test/helper.go @@ -16,6 +16,7 @@ import ( "github.com/maticnetwork/bor/eth" "github.com/maticnetwork/bor/ethdb" "github.com/maticnetwork/bor/node" + "github.com/maticnetwork/bor/params" ) var ( @@ -99,11 +100,11 @@ func insertNewBlock(t *testing.T, _bor *bor.Bor, chain *core.BlockChain, header } } -func buildMinimalNextHeader(t *testing.T, block *types.Block, period uint64) *types.Header { +func buildMinimalNextHeader(t *testing.T, block *types.Block, borConfig *params.BorConfig) *types.Header { header := block.Header() header.Number.Add(header.Number, big.NewInt(1)) header.ParentHash = block.Hash() - header.Time += (period + 1) + header.Time += bor.CalcProducerDelay(header.Number.Uint64(), borConfig.Period, borConfig.Sprint, borConfig.ProducerDelay) header.Extra = make([]byte, 97) // vanity (32) + extraSeal (65) _key, _ := hex.DecodeString(privKey) sig, err := secp256k1.Sign(crypto.Keccak256(bor.BorRLP(header)), _key) diff --git a/consensus/errors.go b/consensus/errors.go index a005c5f63de802cbb2bf54203257d23fe16a3109..04342bea8a7c8a6e9ef9959b4d215fa7a04955ad 100644 --- a/consensus/errors.go +++ b/consensus/errors.go @@ -31,6 +31,9 @@ var ( // to the current node. ErrFutureBlock = errors.New("block in the future") + // ErrBlockTooSoon is returned when the period / producerDelay values in the genesis were not respected + ErrBlockTooSoon = errors.New("block was produced sooner than expected") + // ErrInvalidNumber is returned if a block's number doesn't equal it's parent's // plus one. ErrInvalidNumber = errors.New("invalid block number")