From c27b8b78012f64723e730b6e98b3eb1298a41543 Mon Sep 17 00:00:00 2001 From: atvanguard <93arpit@gmail.com> Date: Thu, 14 May 2020 12:19:47 +0530 Subject: [PATCH] new: Add wiggle to block time --- cmd/puppeth/wizard_genesis.go | 1 + consensus/bor/bor.go | 49 +++++++++++++++++------------ consensus/bor/bor_test/bor_test.go | 11 +++---- consensus/bor/bor_test/genesis.json | 1 + consensus/bor/bor_test/helper.go | 2 +- consensus/bor/errors.go | 13 ++++++++ params/config.go | 1 + 7 files changed, 51 insertions(+), 27 deletions(-) diff --git a/cmd/puppeth/wizard_genesis.go b/cmd/puppeth/wizard_genesis.go index 695005f09..e431c17dd 100644 --- a/cmd/puppeth/wizard_genesis.go +++ b/cmd/puppeth/wizard_genesis.go @@ -111,6 +111,7 @@ func (w *wizard) makeGenesis() { Period: 1, ProducerDelay: 5, Sprint: 60, + BackupMultiplier: 1, ValidatorContract: "0x0000000000000000000000000000000000001000", StateReceiverContract: "0x0000000000000000000000000000000000001001", } diff --git a/consensus/bor/bor.go b/consensus/bor/bor.go index 204c560eb..3151e6e54 100644 --- a/consensus/bor/bor.go +++ b/consensus/bor/bor.go @@ -207,14 +207,18 @@ func CalcDifficulty(snap *Snapshot, signer common.Address, sprint uint64) *big.I return big.NewInt(0).SetUint64(snap.inturn(snap.Number+1, signer, sprint)) } -// 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 { +// CalcProducerDelay is the block delay algorithm based on block time, period, producerDelay and turn-ness of a signer +func CalcProducerDelay(number uint64, succession int, c *params.BorConfig) 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 + delay := c.Period + if number%c.Sprint == 0 { + delay = c.ProducerDelay } - return period + if succession > 0 { + delay += uint64(succession) * c.BackupMultiplier + } + return delay } // BorRLP returns the rlp bytes which needs to be signed for the bor @@ -334,17 +338,6 @@ 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 @@ -590,10 +583,22 @@ func (c *Bor) verifySeal(chain consensus.ChainReader, header *types.Header, pare return errUnauthorizedSigner } - if _, err = snap.GetSignerSuccessionNumber(signer); err != nil { + succession, err := snap.GetSignerSuccessionNumber(signer) + if err != nil { return err } + 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, succession, c.config) { + return &BlockTooSoonError{number, succession} + } + // Ensure that the difficulty corresponds to the turn-ness of the signer if !c.fakeDiff { difficulty := snap.inturn(header.Number.Uint64(), signer, c.config.Sprint) @@ -654,7 +659,11 @@ func (c *Bor) Prepare(chain consensus.ChainReader, header *types.Header) error { return consensus.ErrUnknownAncestor } - header.Time = parent.Time + CalcProducerDelay(number, c.config.Period, c.config.Sprint, c.config.ProducerDelay) + succession, err := snap.GetSignerSuccessionNumber(c.signer) + if err != nil { + return err + } + header.Time = parent.Time + CalcProducerDelay(number, succession, c.config) if header.Time < uint64(time.Now().Unix()) { header.Time = uint64(time.Now().Unix()) } @@ -765,8 +774,8 @@ func (c *Bor) Seal(chain consensus.ChainReader, block *types.Block, results chan // Sweet, the protocol permits us to sign the block, wait for our time delay := time.Unix(int64(header.Time), 0).Sub(time.Now()) // nolint: gosimple - wiggle := time.Duration(2*c.config.Period) * time.Second * time.Duration(successionNumber) - delay += wiggle + // wiggle was already accounted for in header.Time, this is just for logging + wiggle := time.Duration(successionNumber) * time.Duration(c.config.BackupMultiplier) * time.Second // Sign all the things! sighash, err := signFn(accounts.Account{Address: signer}, accounts.MimetypeBor, BorRLP(header)) diff --git a/consensus/bor/bor_test/bor_test.go b/consensus/bor/bor_test/bor_test.go index b7cf4659b..f478175b2 100644 --- a/consensus/bor/bor_test/bor_test.go +++ b/consensus/bor/bor_test/bor_test.go @@ -24,11 +24,7 @@ func TestCommitSpan(t *testing.T) { // Mock HeimdallClient.FetchWithRetry to return span data from span.json res, heimdallSpan := loadSpanFromFile(t) h := &mocks.IHeimdallClient{} - // FetchWithRetry is invoked 3 times - // 1. bor.FinalizeAndAssemble to prepare a new block when calling insertNewBlock - // 2. bor.Finalize via(bc.insertChain => bc.processor.Process) - // 3. bor.FinalizeAndAssemble via worker.commit - h.On("FetchWithRetry", "bor", "span", "1").Return(res, nil).Times(3) + h.On("FetchWithRetry", "bor", "span", "1").Return(res, nil) _bor.SetHeimdallClient(h) db := init.ethereum.ChainDb() @@ -45,7 +41,10 @@ func TestCommitSpan(t *testing.T) { block = insertNewBlock(t, _bor, chain, header, statedb, _key) } - assert.True(t, h.AssertNumberOfCalls(t, "FetchWithRetry", 3)) + // FetchWithRetry is invoked 2 times + // 1. bor.FinalizeAndAssemble to prepare a new block when calling insertNewBlock + // 2. bor.Finalize via(bc.insertChain => bc.processor.Process) + assert.True(t, h.AssertNumberOfCalls(t, "FetchWithRetry", 2)) validators, err := _bor.GetCurrentValidators(sprintSize, 256) // new span starts at 256 if err != nil { t.Fatalf("%s", err) diff --git a/consensus/bor/bor_test/genesis.json b/consensus/bor/bor_test/genesis.json index 5525732b9..5113190b1 100644 --- a/consensus/bor/bor_test/genesis.json +++ b/consensus/bor/bor_test/genesis.json @@ -11,6 +11,7 @@ "bor": { "period": 1, "producerDelay": 4, + "backupMultiplier": 1, "sprint": 4, "validatorContract": "0x0000000000000000000000000000000000001000", "stateReceiverContract": "0x0000000000000000000000000000000000001001" diff --git a/consensus/bor/bor_test/helper.go b/consensus/bor/bor_test/helper.go index f812599dc..2a050eff8 100644 --- a/consensus/bor/bor_test/helper.go +++ b/consensus/bor/bor_test/helper.go @@ -111,7 +111,7 @@ func buildMinimalNextHeader(t *testing.T, block *types.Block, borConfig *params. header := block.Header() header.Number.Add(header.Number, big.NewInt(1)) header.ParentHash = block.Hash() - header.Time += bor.CalcProducerDelay(header.Number.Uint64(), borConfig.Period, borConfig.Sprint, borConfig.ProducerDelay) + header.Time += bor.CalcProducerDelay(header.Number.Uint64(), 0, borConfig) header.Extra = make([]byte, 32+65) // vanity + extraSeal currentValidators := []*bor.Validator{bor.NewValidator(addr, 10)} diff --git a/consensus/bor/errors.go b/consensus/bor/errors.go index b058adc7f..7e7ee1791 100644 --- a/consensus/bor/errors.go +++ b/consensus/bor/errors.go @@ -96,3 +96,16 @@ func (e *MismatchingValidatorsError) Error() string { e.ValidatorSetHeader, ) } + +type BlockTooSoonError struct { + Number uint64 + Succession int +} + +func (e *BlockTooSoonError) Error() string { + return fmt.Sprintf( + "Block %d was created too soon. Signer turn-ness number is %d\n", + e.Number, + e.Succession, + ) +} diff --git a/params/config.go b/params/config.go index e56336fe2..3af6bb064 100644 --- a/params/config.go +++ b/params/config.go @@ -319,6 +319,7 @@ type BorConfig struct { Period uint64 `json:"period"` // Number of seconds between blocks to enforce ProducerDelay uint64 `json:"producerDelay"` // Number of seconds delay between two producer interval Sprint uint64 `json:"sprint"` // Epoch length to proposer + BackupMultiplier uint64 `json:"backupMultiplier"` // Backup multiplier to determine the wiggle time ValidatorContract string `json:"validatorContract"` // Validator set contract StateReceiverContract string `json:"stateReceiverContract"` // State receiver contract } -- GitLab