diff --git a/accounts/scwallet/hub.go b/accounts/scwallet/hub.go index dfcc5ac4bf16735e3d20b8b1cfcd0e07b59074f5..66005b04e986024055d5332190a9dd6a71b37002 100644 --- a/accounts/scwallet/hub.go +++ b/accounts/scwallet/hub.go @@ -41,11 +41,11 @@ import ( "sync" "time" + pcsc "github.com/gballet/go-libpcsclite" "github.com/maticnetwork/bor/accounts" "github.com/maticnetwork/bor/common" "github.com/maticnetwork/bor/event" "github.com/maticnetwork/bor/log" - pcsc "github.com/gballet/go-libpcsclite" ) // Scheme is the URI prefix for smartcard wallets. diff --git a/accounts/scwallet/securechannel.go b/accounts/scwallet/securechannel.go index 3d0be4728ea2161a4b24c8174efcb77c84c7feef..7f0a8df4ac9a7fb4f636bcff734ed50f86d58b94 100644 --- a/accounts/scwallet/securechannel.go +++ b/accounts/scwallet/securechannel.go @@ -25,8 +25,8 @@ import ( "crypto/sha512" "fmt" - "github.com/maticnetwork/bor/crypto" pcsc "github.com/gballet/go-libpcsclite" + "github.com/maticnetwork/bor/crypto" "github.com/wsddn/go-ecdh" "golang.org/x/crypto/pbkdf2" "golang.org/x/text/unicode/norm" diff --git a/accounts/scwallet/wallet.go b/accounts/scwallet/wallet.go index 540bcc1a60dffe9e528545c62529387c1c523d70..6b986ff946c34a4d3b447a91111944947ae21f28 100644 --- a/accounts/scwallet/wallet.go +++ b/accounts/scwallet/wallet.go @@ -33,13 +33,13 @@ import ( "sync" "time" + pcsc "github.com/gballet/go-libpcsclite" ethereum "github.com/maticnetwork/bor" "github.com/maticnetwork/bor/accounts" "github.com/maticnetwork/bor/common" "github.com/maticnetwork/bor/core/types" "github.com/maticnetwork/bor/crypto" "github.com/maticnetwork/bor/log" - pcsc "github.com/gballet/go-libpcsclite" "github.com/status-im/keycard-go/derivationpath" ) diff --git a/accounts/usbwallet/hub.go b/accounts/usbwallet/hub.go index a80eff5a93a95e5b96a4792f46de5b9419701f5a..e742e49fe01b3818d5f62d3baef15f706e94b3bf 100644 --- a/accounts/usbwallet/hub.go +++ b/accounts/usbwallet/hub.go @@ -23,10 +23,10 @@ import ( "sync/atomic" "time" + "github.com/karalabe/usb" "github.com/maticnetwork/bor/accounts" "github.com/maticnetwork/bor/event" "github.com/maticnetwork/bor/log" - "github.com/karalabe/usb" ) // LedgerScheme is the protocol scheme prefixing account and wallet URLs. diff --git a/accounts/usbwallet/trezor.go b/accounts/usbwallet/trezor.go index f88501e32f503c3f471f5917fe228486daf784ac..49c71cb0283b1550bf035c57c0c3a4f4e5afc9f7 100644 --- a/accounts/usbwallet/trezor.go +++ b/accounts/usbwallet/trezor.go @@ -27,13 +27,13 @@ import ( "io" "math/big" + "github.com/golang/protobuf/proto" "github.com/maticnetwork/bor/accounts" "github.com/maticnetwork/bor/accounts/usbwallet/trezor" "github.com/maticnetwork/bor/common" "github.com/maticnetwork/bor/common/hexutil" "github.com/maticnetwork/bor/core/types" "github.com/maticnetwork/bor/log" - "github.com/golang/protobuf/proto" ) // ErrTrezorPINNeeded is returned if opening the trezor requires a PIN code. In diff --git a/accounts/usbwallet/wallet.go b/accounts/usbwallet/wallet.go index 09a140a756441555a170fa8a9b3b62f1cab23a2f..29f7cf661e9c8291665584b1b3cf273abb8e8d26 100644 --- a/accounts/usbwallet/wallet.go +++ b/accounts/usbwallet/wallet.go @@ -25,13 +25,13 @@ import ( "sync" "time" + "github.com/karalabe/usb" ethereum "github.com/maticnetwork/bor" "github.com/maticnetwork/bor/accounts" "github.com/maticnetwork/bor/common" "github.com/maticnetwork/bor/core/types" "github.com/maticnetwork/bor/crypto" "github.com/maticnetwork/bor/log" - "github.com/karalabe/usb" ) // Maximum time between wallet health checks to detect USB unplugs. diff --git a/consensus/bor/bor.go b/consensus/bor/bor.go index a33681c1d827e07649646c737f4c9566289f78a8..84fabf3336685fc8a33671fea37bfe9e94f6a3a0 100644 --- a/consensus/bor/bor.go +++ b/consensus/bor/bor.go @@ -201,8 +201,8 @@ func encodeSigHeader(w io.Writer, header *types.Header) { // CalcDifficulty is the difficulty adjustment algorithm. It returns the difficulty // that a new block should have based on the previous blocks in the chain and the // current signer. -func CalcDifficulty(snap *Snapshot, signer common.Address, epoch uint64) *big.Int { - return big.NewInt(0).SetUint64(snap.inturn(snap.Number+1, signer, epoch)) +func CalcDifficulty(snap *Snapshot, signer common.Address, sprint uint64) *big.Int { + 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 @@ -346,12 +346,9 @@ func (c *Bor) verifyHeader(chain consensus.ChainReader, header *types.Header, pa if header.Time > uint64(time.Now().Unix()) { return consensus.ErrFutureBlock } - // Check that the extra-data contains both the vanity and signature - if len(header.Extra) < extraVanity { - return errMissingVanity - } - if len(header.Extra) < extraVanity+extraSeal { - return errMissingSignature + + if err := validateHeaderExtraField(header.Extra); err != nil { + return err } // check extr adata @@ -387,6 +384,18 @@ func (c *Bor) verifyHeader(chain consensus.ChainReader, header *types.Header, pa return c.verifyCascadingFields(chain, header, parents) } +// validateHeaderExtraField validates that the extra-data contains both the vanity and signature. +// header.Extra = header.Vanity + header.ProducerBytes (optional) + header.Seal +func validateHeaderExtraField(extraBytes []byte) error { + if len(extraBytes) < extraVanity { + return errMissingVanity + } + if len(extraBytes) < extraVanity+extraSeal { + return errMissingSignature + } + return nil +} + // verifyCascadingFields verifies all the header fields that are not standalone, // rather depend on a batch of previous headers. The caller may optionally pass // in a batch of parents (ascending order) to avoid looking those up from the @@ -420,8 +429,9 @@ func (c *Bor) verifyCascadingFields(chain consensus.ChainReader, header *types.H return err } - // If the block is a sprint end block, verify the validator list - if number%c.config.Sprint == 0 { + isSprintEnd := (number+1)%c.config.Sprint == 0 + // verify the validator list in the last sprint block + if isSprintEnd { validatorsBytes := make([]byte, len(snap.ValidatorSet.Validators)*validatorHeaderBytesLength) currentValidators := snap.ValidatorSet.Copy().Validators @@ -430,14 +440,9 @@ func (c *Bor) verifyCascadingFields(chain consensus.ChainReader, header *types.H for i, validator := range currentValidators { copy(validatorsBytes[i*validatorHeaderBytesLength:], validator.HeaderBytes()) } - - extraSuffix := len(header.Extra) - extraSeal - - // fmt.Println("validatorsBytes ==> verify seal ==> ", hex.EncodeToString(validatorsBytes)) - // fmt.Println("header.Extra ==> verify seal ==> ", hex.EncodeToString(header.Extra[extraVanity:extraSuffix])) - - if !bytes.Equal(header.Extra[extraVanity:extraSuffix], validatorsBytes) { - // return errMismatchingSprintValidators + // len(header.Extra) >= extraVanity+extraSeal has already been validated in validateHeaderExtraField, so this won't result in a panic + if !bytes.Equal(header.Extra[extraVanity : len(header.Extra)-extraSeal], validatorsBytes) { + return errMismatchingSprintValidators } } @@ -474,7 +479,7 @@ func (c *Bor) snapshot(chain consensus.ChainReader, number uint64, hash common.H // up more headers than allowed to be reorged (chain reinit from a freezer), // consider the checkpoint trusted and snapshot it. // TODO fix this - if number == 0 /* || (number%c.config.Sprint == 0 && (len(headers) > params.ImmutabilityThreshold || chain.GetHeaderByNumber(number-1) == nil)) */ { + if number == 0 { checkpoint := chain.GetHeaderByNumber(number) if checkpoint != nil { // get checkpoint data @@ -582,27 +587,8 @@ func (c *Bor) verifySeal(chain consensus.ChainReader, header *types.Header, pare return errUnauthorizedSigner } - // check if signer is correct - validators := snap.ValidatorSet.Validators - // proposer will be the last signer if block is not epoch block - proposer := snap.ValidatorSet.GetProposer().Address - if number%c.config.Sprint != 0 { - // proposer = snap.Recents[number-1] - } - proposerIndex, _ := snap.ValidatorSet.GetByAddress(proposer) - signerIndex, _ := snap.ValidatorSet.GetByAddress(signer) - limit := len(validators)/2 + 1 - - // temp index - tempIndex := signerIndex - if proposerIndex != tempIndex && limit > 0 { - if tempIndex < proposerIndex { - tempIndex = tempIndex + len(validators) - } - - if tempIndex-proposerIndex > limit { - return errRecentlySigned - } + if _, err = snap.GetSignerSuccessionNumber(signer); err != nil { + return err } // Ensure that the difficulty corresponds to the turn-ness of the signer @@ -766,35 +752,18 @@ func (c *Bor) Seal(chain consensus.ChainReader, block *types.Block, results chan return errUnauthorizedSigner } - validators := snap.ValidatorSet.Validators - // proposer will be the last signer if block is not epoch block - proposer := snap.ValidatorSet.GetProposer().Address - if number%c.config.Sprint != 0 { - // proposer = snap.Recents[number-1] - } - - proposerIndex, _ := snap.ValidatorSet.GetByAddress(proposer) - signerIndex, _ := snap.ValidatorSet.GetByAddress(signer) - limit := len(validators)/2 + 1 - - // temp index - tempIndex := signerIndex - if tempIndex < proposerIndex { - tempIndex = tempIndex + len(validators) - } - - if limit > 0 && tempIndex-proposerIndex > limit { - log.Info("Signed recently, must wait for others") - return nil + successionNumber, err := snap.GetSignerSuccessionNumber(signer) + if err != nil { + return err } // 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(tempIndex-proposerIndex) + wiggle := time.Duration(2*c.config.Period) * time.Second * time.Duration(successionNumber) delay += wiggle log.Info("Out-of-turn signing requested", "wiggle", common.PrettyDuration(wiggle)) - log.Info("Sealing block with", "number", number, "delay", delay, "headerDifficulty", header.Difficulty, "signer", signer.Hex(), "proposer", proposer.Hex()) + log.Info("Sealing block with", "number", number, "delay", delay, "headerDifficulty", header.Difficulty, "signer", signer.Hex(), "proposer", snap.ValidatorSet.GetProposer().Address.Hex()) // Sign all the things! sighash, err := signFn(accounts.Account{Address: signer}, accounts.MimetypeBor, BorRLP(header)) @@ -854,7 +823,7 @@ func (c *Bor) Close() error { return nil } -// Checks if new span is pending +// Checks if "force" proposeSpan has been set func (c *Bor) isSpanPending(snapshotNumber uint64) (bool, error) { blockNr := rpc.BlockNumber(snapshotNumber) method := "spanProposalPending" @@ -866,14 +835,10 @@ func (c *Bor) isSpanPending(snapshotNumber uint64) (bool, error) { return false, err } - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() // cancel when we are finished consuming integers - - // call msgData := (hexutil.Bytes)(data) toAddress := common.HexToAddress(c.config.ValidatorContract) gas := (hexutil.Uint64)(uint64(math.MaxUint64 / 2)) - result, err := c.ethAPI.Call(ctx, ethapi.CallArgs{ + result, err := c.ethAPI.Call(context.Background(), ethapi.CallArgs{ Gas: &gas, To: &toAddress, Data: &msgData, @@ -904,14 +869,10 @@ func (c *Bor) GetCurrentSpan(snapshotNumber uint64) (*Span, error) { return nil, err } - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() // cancel when we are finished consuming integers - - // call msgData := (hexutil.Bytes)(data) toAddress := common.HexToAddress(c.config.ValidatorContract) gas := (hexutil.Uint64)(uint64(math.MaxUint64 / 2)) - result, err := c.ethAPI.Call(ctx, ethapi.CallArgs{ + result, err := c.ethAPI.Call(context.Background(), ethapi.CallArgs{ Gas: &gas, To: &toAddress, Data: &msgData, @@ -954,14 +915,11 @@ func (c *Bor) GetCurrentValidators(snapshotNumber uint64, blockNumber uint64) ([ return nil, err } - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() // cancel when we are finished consuming integers - // call msgData := (hexutil.Bytes)(data) toAddress := common.HexToAddress(c.config.ValidatorContract) gas := (hexutil.Uint64)(uint64(math.MaxUint64 / 2)) - result, err := c.ethAPI.Call(ctx, ethapi.CallArgs{ + result, err := c.ethAPI.Call(context.Background(), ethapi.CallArgs{ Gas: &gas, To: &toAddress, Data: &msgData, @@ -1144,13 +1102,10 @@ func (c *Bor) GetPendingStateProposals(snapshotNumber uint64) ([]*big.Int, error return nil, err } - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() // cancel when we are finished consuming integers - msgData := (hexutil.Bytes)(data) toAddress := common.HexToAddress(c.config.StateReceiverContract) gas := (hexutil.Uint64)(uint64(math.MaxUint64 / 2)) - result, err := c.ethAPI.Call(ctx, ethapi.CallArgs{ + result, err := c.ethAPI.Call(context.Background(), ethapi.CallArgs{ Gas: &gas, To: &toAddress, Data: &msgData, diff --git a/consensus/bor/bor_test/helper.go b/consensus/bor/bor_test/helper.go index 9a5c0facc561a073075ff4150a590103ce8f3908..1ead54ed88a9d02c3bdc2a5c8176700dfd6a2ab2 100644 --- a/consensus/bor/bor_test/helper.go +++ b/consensus/bor/bor_test/helper.go @@ -105,7 +105,16 @@ func buildMinimalNextHeader(t *testing.T, block *types.Block, borConfig *params. 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.Extra = make([]byte, 97) // vanity (32) + extraSeal (65) + isSprintEnd := (header.Number.Uint64()+1)%borConfig.Sprint == 0 + if isSprintEnd { + header.Extra = make([]byte, 32 + 40 + 65) // vanity + validatorBytes + extraSeal + // the genesis file was initialized with a validator 0x71562b71999873db5b286df957af199ec94617f7 with power 10 + // So, if you change ./genesis.json, do change the following as well + validatorBytes, _ := hex.DecodeString("71562b71999873db5b286df957af199ec94617f7000000000000000000000000000000000000000a") + copy(header.Extra[32:72], validatorBytes) + } else { + header.Extra = make([]byte, 32 + 65) // vanity + extraSeal + } _key, _ := hex.DecodeString(privKey) sig, err := secp256k1.Sign(crypto.Keccak256(bor.BorRLP(header)), _key) if err != nil { diff --git a/consensus/bor/bor_test/snapshot_test.go b/consensus/bor/bor_test/snapshot_test.go new file mode 100644 index 0000000000000000000000000000000000000000..264c63295299f6967f16c4fd124bd4266a2f3a63 --- /dev/null +++ b/consensus/bor/bor_test/snapshot_test.go @@ -0,0 +1,125 @@ +package bortest + +import ( + "math/rand" + "sort" + "testing" + "time" + + "github.com/stretchr/testify/assert" + + "github.com/maticnetwork/bor/common" + "github.com/maticnetwork/bor/consensus/bor" +) + +const ( + numVals = 100 +) + +func TestGetSignerSuccessionNumber_ProposerIsSigner(t *testing.T) { + validators := buildRandomValidatorSet(numVals) + validatorSet := bor.NewValidatorSet(validators) + snap := bor.Snapshot{ + ValidatorSet: validatorSet, + } + + // proposer is signer + signer := validatorSet.Proposer.Address + successionNumber, err := snap.GetSignerSuccessionNumber(signer) + if err != nil { + t.Fatalf("%s", err) + } + assert.Equal(t, 0, successionNumber) +} + +func TestGetSignerSuccessionNumber_SignerIndexIsLarger(t *testing.T) { + validators := buildRandomValidatorSet(numVals) + + // sort validators by address, which is what bor.NewValidatorSet also does + sort.Sort(bor.ValidatorsByAddress(validators)) + proposerIndex := 32 + signerIndex := 56 + // give highest ProposerPriority to a particular val, so that they become the proposer + validators[proposerIndex].VotingPower = 200 + snap := bor.Snapshot{ + ValidatorSet: bor.NewValidatorSet(validators), + } + + // choose a signer at an index greater than proposer index + signer := snap.ValidatorSet.Validators[signerIndex].Address + successionNumber, err := snap.GetSignerSuccessionNumber(signer) + if err != nil { + t.Fatalf("%s", err) + } + assert.Equal(t, signerIndex-proposerIndex, successionNumber) +} + +func TestGetSignerSuccessionNumber_SignerIndexIsSmaller(t *testing.T) { + validators := buildRandomValidatorSet(numVals) + proposerIndex := 98 + signerIndex := 11 + // give highest ProposerPriority to a particular val, so that they become the proposer + validators[proposerIndex].VotingPower = 200 + snap := bor.Snapshot{ + ValidatorSet: bor.NewValidatorSet(validators), + } + + // choose a signer at an index greater than proposer index + signer := snap.ValidatorSet.Validators[signerIndex].Address + successionNumber, err := snap.GetSignerSuccessionNumber(signer) + if err != nil { + t.Fatalf("%s", err) + } + assert.Equal(t, signerIndex+numVals-proposerIndex, successionNumber) +} + +func TestGetSignerSuccessionNumber_ProposerNotFound(t *testing.T) { + validators := buildRandomValidatorSet(numVals) + snap := bor.Snapshot{ + ValidatorSet: bor.NewValidatorSet(validators), + } + dummyProposerAddress := randomAddress() + snap.ValidatorSet.Proposer = &bor.Validator{Address: dummyProposerAddress} + // choose any signer + signer := snap.ValidatorSet.Validators[3].Address + _, err := snap.GetSignerSuccessionNumber(signer) + assert.NotNil(t, err) + e, ok := err.(*bor.ProposerNotFoundError) + assert.True(t, ok) + assert.Equal(t, dummyProposerAddress, e.Address) +} + +func TestGetSignerSuccessionNumber_SignerNotFound(t *testing.T) { + validators := buildRandomValidatorSet(numVals) + snap := bor.Snapshot{ + ValidatorSet: bor.NewValidatorSet(validators), + } + dummySignerAddress := randomAddress() + _, err := snap.GetSignerSuccessionNumber(dummySignerAddress) + assert.NotNil(t, err) + e, ok := err.(*bor.SignerNotFoundError) + assert.True(t, ok) + assert.Equal(t, dummySignerAddress, e.Address) +} + +func buildRandomValidatorSet(numVals int) []*bor.Validator { + rand.Seed(time.Now().Unix()) + validators := make([]*bor.Validator, numVals) + for i := 0; i < numVals; i++ { + validators[i] = &bor.Validator{ + Address: randomAddress(), + // cannot process validators with voting power 0, hence +1 + VotingPower: int64(rand.Intn(99) + 1), + } + } + + // sort validators by address, which is what bor.NewValidatorSet also does + sort.Sort(bor.ValidatorsByAddress(validators)) + return validators +} + +func randomAddress() common.Address { + bytes := make([]byte, 32) + rand.Read(bytes) + return common.BytesToAddress(bytes) +} diff --git a/consensus/bor/errors.go b/consensus/bor/errors.go new file mode 100644 index 0000000000000000000000000000000000000000..a3b0dc656d08352a5a8ad3f5f1dddfca3aee52b3 --- /dev/null +++ b/consensus/bor/errors.go @@ -0,0 +1,42 @@ +package bor + +import ( + "fmt" + + "github.com/maticnetwork/bor/common" +) + +// Will include any new bor consensus errors here in an attempt to make error messages more descriptive + +// ProposerNotFoundError is returned if the given proposer address is not present in the validator set +type ProposerNotFoundError struct { + Address common.Address +} + +func (e *ProposerNotFoundError) Error() string { + return fmt.Sprintf("Proposer: %s not found", e.Address.Hex()) +} + +// SignerNotFoundError is returned when the signer address is not present in the validator set +type SignerNotFoundError struct { + Address common.Address +} + +func (e *SignerNotFoundError) Error() string { + return fmt.Sprintf("Signer: %s not found", e.Address.Hex()) +} + +// TotalVotingPowerExceededError is returned when the maximum allowed total voting power is exceeded +type TotalVotingPowerExceededError struct { + Sum int64 + Validators []*Validator +} + +func (e *TotalVotingPowerExceededError) Error() string { + return fmt.Sprintf( + "Total voting power should be guarded to not exceed %v; got: %v; for validator set: %v", + MaxTotalVotingPower, + e.Sum, + e.Validators, + ) +} diff --git a/consensus/bor/snapshot.go b/consensus/bor/snapshot.go index d2f0e82ff49c047cede16743d2d4cfdae2d33250..08088d791103647145acc7e72744aa5a09863cec 100644 --- a/consensus/bor/snapshot.go +++ b/consensus/bor/snapshot.go @@ -87,7 +87,9 @@ func loadSnapshot(config *params.BorConfig, sigcache *lru.ARCCache, db ethdb.Dat snap.ethAPI = ethAPI // update total voting power - snap.ValidatorSet.updateTotalVotingPower() + if err := snap.ValidatorSet.updateTotalVotingPower(); err != nil { + return nil, err + } return snap, nil } @@ -156,28 +158,8 @@ func (s *Snapshot) apply(headers []*types.Header) (*Snapshot, error) { return nil, errUnauthorizedSigner } - // - // Check validator - // - - validators := snap.ValidatorSet.Validators - // proposer will be the last signer if block is not epoch block - proposer := snap.ValidatorSet.GetProposer().Address - proposerIndex, _ := snap.ValidatorSet.GetByAddress(proposer) - signerIndex, _ := snap.ValidatorSet.GetByAddress(signer) - limit := len(validators)/2 + 1 - - // temp index - tempIndex := signerIndex - if proposerIndex != tempIndex && limit > 0 { - if tempIndex < proposerIndex { - tempIndex = tempIndex + len(validators) - } - - if tempIndex-proposerIndex > limit { - log.Info("Invalid signer: error while applying headers", "proposerIndex", validators[proposerIndex].Address.Hex(), "signerIndex", validators[signerIndex].Address.Hex()) - return nil, errRecentlySigned - } + if _, err = snap.GetSignerSuccessionNumber(signer); err != nil { + return nil, err } // add recents @@ -185,6 +167,9 @@ func (s *Snapshot) apply(headers []*types.Header) (*Snapshot, error) { // change validator set and change proposer if number > 0 && (number+1)%s.config.Sprint == 0 { + if err := validateHeaderExtraField(header.Extra); err != nil { + return nil, err + } validatorBytes := header.Extra[extraVanity : len(header.Extra)-extraSeal] // get validators from headers and use that for new validator set @@ -200,6 +185,34 @@ func (s *Snapshot) apply(headers []*types.Header) (*Snapshot, error) { return snap, nil } +// GetSignerSuccessionNumber returns the relative position of signer in terms of the in-turn proposer +func (s *Snapshot) GetSignerSuccessionNumber(signer common.Address) (int, error) { + validators := s.ValidatorSet.Validators + proposer := s.ValidatorSet.GetProposer().Address + proposerIndex, _ := s.ValidatorSet.GetByAddress(proposer) + if proposerIndex == -1 { + return -1, &ProposerNotFoundError{proposer} + } + signerIndex, _ := s.ValidatorSet.GetByAddress(signer) + if signerIndex == -1 { + return -1, &SignerNotFoundError{signer} + } + limit := len(validators)/2 + 1 + + tempIndex := signerIndex + if proposerIndex != tempIndex && limit > 0 { + if tempIndex < proposerIndex { + tempIndex = tempIndex + len(validators) + } + + if tempIndex-proposerIndex > limit { + log.Info("errRecentlySigned", "proposerIndex", validators[proposerIndex].Address.Hex(), "signerIndex", validators[signerIndex].Address.Hex()) + return -1, errRecentlySigned + } + } + return tempIndex - proposerIndex, nil +} + // signers retrieves the list of authorized signers in ascending order. func (s *Snapshot) signers() []common.Address { sigs := make([]common.Address, 0, len(s.ValidatorSet.Validators)) diff --git a/consensus/bor/validator.go b/consensus/bor/validator.go index 0592ab3b138a279d9bcc43d28feabd25913b5112..dab50e7b2c7eb9efa83d5fbb535d08b36c3e9ced 100644 --- a/consensus/bor/validator.go +++ b/consensus/bor/validator.go @@ -2,7 +2,7 @@ package bor import ( "bytes" - "encoding/json" + // "encoding/json" "errors" "fmt" "math/big" @@ -13,8 +13,6 @@ import ( ) // Validator represets Volatile state for each Validator -// NOTE: The ProposerPriority is not included in Validator.Hash(); -// make sure to update that method if changes are made here type Validator struct { ID uint64 `json:"ID"` Address common.Address `json:"signer"` @@ -31,18 +29,23 @@ func NewValidator(address common.Address, votingPower int64) *Validator { } } -// Creates a new copy of the validator so we can mutate ProposerPriority. +// Copy creates a new copy of the validator so we can mutate ProposerPriority. // Panics if the validator is nil. func (v *Validator) Copy() *Validator { vCopy := *v return &vCopy } -// Returns the one with higher ProposerPriority. -func (v *Validator) CompareProposerPriority(other *Validator) *Validator { +// Cmp returns the one validator with a higher ProposerPriority. +// If ProposerPriority is same, it returns the validator with lexicographically smaller address +func (v *Validator) Cmp(other *Validator) *Validator { + // if both of v and other are nil, nil will be returned and that could possibly lead to nil pointer dereference bubbling up the stack if v == nil { return other } + if other == nil { + return v + } if v.ProposerPriority > other.ProposerPriority { return v } else if v.ProposerPriority < other.ProposerPriority { @@ -55,7 +58,6 @@ func (v *Validator) CompareProposerPriority(other *Validator) *Validator { return other } else { panic("Cannot compare identical validators") - return nil } } } @@ -80,18 +82,6 @@ func ValidatorListString(vals []*Validator) string { return strings.Join(chunks, ",") } -// Bytes computes the unique encoding of a validator with a given voting power. -// These are the bytes that gets hashed in consensus. It excludes address -// as its redundant with the pubkey. This also excludes ProposerPriority -// which changes every round. -func (v *Validator) Bytes() []byte { - b, err := json.Marshal(v) - if err != nil { - return b - } - return nil -} - // HeaderBytes return header bytes func (v *Validator) HeaderBytes() []byte { result := make([]byte, 40) diff --git a/consensus/bor/validator_set.go b/consensus/bor/validator_set.go index 2f22d5b59f389460c62a95a93b44ba0aba0bee7f..dbe987ce780dedc3fe1b96d0b5a86d0b757409f7 100644 --- a/consensus/bor/validator_set.go +++ b/consensus/bor/validator_set.go @@ -11,6 +11,7 @@ import ( "strings" "github.com/maticnetwork/bor/common" + "github.com/maticnetwork/bor/log" ) // MaxTotalVotingPower - the maximum allowed total voting power. @@ -182,7 +183,7 @@ func computeMaxMinPriorityDiff(vals *ValidatorSet) int64 { func (vals *ValidatorSet) getValWithMostPriority() *Validator { var res *Validator for _, val := range vals.Validators { - res = res.CompareProposerPriority(val) + res = res.Cmp(val) } return res } @@ -256,28 +257,29 @@ func (vals *ValidatorSet) Size() int { } // Force recalculation of the set's total voting power. -func (vals *ValidatorSet) updateTotalVotingPower() { +func (vals *ValidatorSet) updateTotalVotingPower() error { sum := int64(0) for _, val := range vals.Validators { // mind overflow sum = safeAddClip(sum, val.VotingPower) if sum > MaxTotalVotingPower { - panic(fmt.Sprintf( - "Total voting power should be guarded to not exceed %v; got: %v", - MaxTotalVotingPower, - sum)) + return &TotalVotingPowerExceededError{sum, vals.Validators} } } - vals.totalVotingPower = sum + return nil } // TotalVotingPower returns the sum of the voting powers of all validators. // It recomputes the total voting power if required. func (vals *ValidatorSet) TotalVotingPower() int64 { if vals.totalVotingPower == 0 { - vals.updateTotalVotingPower() + log.Info("invoking updateTotalVotingPower before returning it") + if err := vals.updateTotalVotingPower(); err != nil { + // Can/should we do better? + panic(err) + } } return vals.totalVotingPower } @@ -298,7 +300,7 @@ func (vals *ValidatorSet) findProposer() *Validator { var proposer *Validator for _, val := range vals.Validators { if proposer == nil || !bytes.Equal(val.Address.Bytes(), proposer.Address.Bytes()) { - proposer = proposer.CompareProposerPriority(val) + proposer = proposer.Cmp(val) } } return proposer @@ -562,7 +564,9 @@ func (vals *ValidatorSet) updateWithChangeSet(changes []*Validator, allowDeletes vals.applyUpdates(updates) vals.applyRemovals(deletes) - vals.updateTotalVotingPower() + if err := vals.updateTotalVotingPower(); err != nil { + return err + } // Scale and center. vals.RescalePriorities(PriorityWindowSizeFactor * vals.TotalVotingPower()) diff --git a/consensus/clique/clique.go b/consensus/clique/clique.go index b90f61176647554ead76da13b30215d89416a91d..799ee16b2dac4521c6459dab1269fc4158ed4a5f 100644 --- a/consensus/clique/clique.go +++ b/consensus/clique/clique.go @@ -26,6 +26,7 @@ import ( "sync" "time" + lru "github.com/hashicorp/golang-lru" "github.com/maticnetwork/bor/accounts" "github.com/maticnetwork/bor/common" "github.com/maticnetwork/bor/common/hexutil" @@ -39,7 +40,6 @@ import ( "github.com/maticnetwork/bor/params" "github.com/maticnetwork/bor/rlp" "github.com/maticnetwork/bor/rpc" - lru "github.com/hashicorp/golang-lru" "golang.org/x/crypto/sha3" ) diff --git a/consensus/clique/snapshot.go b/consensus/clique/snapshot.go index 593dfe8b997b17c9c8b8100e02d309cdb47bde53..a4063e351695ac59dd7d1c1706b0a5810299f951 100644 --- a/consensus/clique/snapshot.go +++ b/consensus/clique/snapshot.go @@ -22,12 +22,12 @@ import ( "sort" "time" + lru "github.com/hashicorp/golang-lru" "github.com/maticnetwork/bor/common" "github.com/maticnetwork/bor/core/types" "github.com/maticnetwork/bor/ethdb" "github.com/maticnetwork/bor/log" "github.com/maticnetwork/bor/params" - lru "github.com/hashicorp/golang-lru" ) // Vote represents a single vote that an authorized signer made to modify the diff --git a/consensus/ethash/ethash.go b/consensus/ethash/ethash.go index 999d92012ac5532558521468dc95b4e960d895e0..08a80ae8195d11a1d6628018e7d11be26c3d2ed3 100644 --- a/consensus/ethash/ethash.go +++ b/consensus/ethash/ethash.go @@ -34,13 +34,13 @@ import ( "unsafe" mmap "github.com/edsrzf/mmap-go" + "github.com/hashicorp/golang-lru/simplelru" "github.com/maticnetwork/bor/common" "github.com/maticnetwork/bor/consensus" "github.com/maticnetwork/bor/core/types" "github.com/maticnetwork/bor/log" "github.com/maticnetwork/bor/metrics" "github.com/maticnetwork/bor/rpc" - "github.com/hashicorp/golang-lru/simplelru" ) var ErrInvalidDumpMagic = errors.New("invalid dump magic") diff --git a/core/headerchain.go b/core/headerchain.go index 95634891fe7d3d2a523aeb5a6a1b55b48c9e907e..07775a228ac43cf27ce384004574773f93dd7547 100644 --- a/core/headerchain.go +++ b/core/headerchain.go @@ -26,6 +26,7 @@ import ( "sync/atomic" "time" + lru "github.com/hashicorp/golang-lru" "github.com/maticnetwork/bor/common" "github.com/maticnetwork/bor/consensus" "github.com/maticnetwork/bor/core/rawdb" @@ -33,7 +34,6 @@ import ( "github.com/maticnetwork/bor/ethdb" "github.com/maticnetwork/bor/log" "github.com/maticnetwork/bor/params" - lru "github.com/hashicorp/golang-lru" ) const ( diff --git a/core/rawdb/freezer_table.go b/core/rawdb/freezer_table.go index 868d81f6a518bf704629931f52c6a4538a736ab8..33c1ca8b34a2b7278a5a0b6ce9bb78cd0aafc818 100644 --- a/core/rawdb/freezer_table.go +++ b/core/rawdb/freezer_table.go @@ -26,10 +26,10 @@ import ( "sync" "sync/atomic" + "github.com/golang/snappy" "github.com/maticnetwork/bor/common" "github.com/maticnetwork/bor/log" "github.com/maticnetwork/bor/metrics" - "github.com/golang/snappy" ) var ( diff --git a/core/state/database.go b/core/state/database.go index 834e8ad96b45145932884a9387800cb6f5a8b347..33ab998c611e1277799b2a6fec8ca3059df54777 100644 --- a/core/state/database.go +++ b/core/state/database.go @@ -19,10 +19,10 @@ package state import ( "fmt" + lru "github.com/hashicorp/golang-lru" "github.com/maticnetwork/bor/common" "github.com/maticnetwork/bor/ethdb" "github.com/maticnetwork/bor/trie" - lru "github.com/hashicorp/golang-lru" ) const ( diff --git a/graphql/service.go b/graphql/service.go index 473e01b250aacd39715a0f96093252d4dedf10d2..6b2626d927000140133a5705220402a4de58a81a 100644 --- a/graphql/service.go +++ b/graphql/service.go @@ -21,12 +21,12 @@ import ( "net" "net/http" + "github.com/graph-gophers/graphql-go" + "github.com/graph-gophers/graphql-go/relay" "github.com/maticnetwork/bor/internal/ethapi" "github.com/maticnetwork/bor/log" "github.com/maticnetwork/bor/p2p" "github.com/maticnetwork/bor/rpc" - "github.com/graph-gophers/graphql-go" - "github.com/graph-gophers/graphql-go/relay" ) // Service encapsulates a GraphQL service. diff --git a/internal/debug/flags.go b/internal/debug/flags.go index 8bd64b9bb939dbd1ef1033df4915e680ea12652c..469dd8dff2a8adbb9d4e6782963913f17505a110 100644 --- a/internal/debug/flags.go +++ b/internal/debug/flags.go @@ -24,10 +24,10 @@ import ( "os" "runtime" + "github.com/fjl/memsize/memsizeui" "github.com/maticnetwork/bor/log" "github.com/maticnetwork/bor/metrics" "github.com/maticnetwork/bor/metrics/exp" - "github.com/fjl/memsize/memsizeui" colorable "github.com/mattn/go-colorable" "github.com/mattn/go-isatty" "gopkg.in/urfave/cli.v1" diff --git a/light/lightchain.go b/light/lightchain.go index cfa684e84624bd81221bcb0b5651d21d02660094..99f14ee3caa60140b78404fd3b3c9a865a570838 100644 --- a/light/lightchain.go +++ b/light/lightchain.go @@ -26,6 +26,7 @@ import ( "sync/atomic" "time" + lru "github.com/hashicorp/golang-lru" "github.com/maticnetwork/bor/common" "github.com/maticnetwork/bor/consensus" "github.com/maticnetwork/bor/core" @@ -37,7 +38,6 @@ import ( "github.com/maticnetwork/bor/log" "github.com/maticnetwork/bor/params" "github.com/maticnetwork/bor/rlp" - lru "github.com/hashicorp/golang-lru" ) var ( diff --git a/metrics/influxdb/influxdb.go b/metrics/influxdb/influxdb.go index 5971c50aa3b525b54582897c4f89d3070534d717..d4452bc6d35fcc63080976900c415a5fe7a6ee6d 100644 --- a/metrics/influxdb/influxdb.go +++ b/metrics/influxdb/influxdb.go @@ -5,9 +5,9 @@ import ( uurl "net/url" "time" + "github.com/influxdata/influxdb/client" "github.com/maticnetwork/bor/log" "github.com/maticnetwork/bor/metrics" - "github.com/influxdata/influxdb/client" ) type reporter struct { diff --git a/p2p/nat/nat.go b/p2p/nat/nat.go index 08b7a39ddaf91ea2305f64cff4e53055e66191ba..0f5cd07c050515a47794c2bf9d8127a0fae6484b 100644 --- a/p2p/nat/nat.go +++ b/p2p/nat/nat.go @@ -25,8 +25,8 @@ import ( "sync" "time" - "github.com/maticnetwork/bor/log" "github.com/jackpal/go-nat-pmp" + "github.com/maticnetwork/bor/log" ) // An implementation of nat.Interface can map local ports to ports diff --git a/p2p/rlpx.go b/p2p/rlpx.go index 1513ed9d09fb95883a75fc1e737cb439990d6d85..36f845332fb5ea144d93e5cf8cc080b4427a2731 100644 --- a/p2p/rlpx.go +++ b/p2p/rlpx.go @@ -35,11 +35,11 @@ import ( "sync" "time" + "github.com/golang/snappy" "github.com/maticnetwork/bor/common/bitutil" "github.com/maticnetwork/bor/crypto" "github.com/maticnetwork/bor/crypto/ecies" "github.com/maticnetwork/bor/rlp" - "github.com/golang/snappy" "golang.org/x/crypto/sha3" ) diff --git a/p2p/simulations/http.go b/p2p/simulations/http.go index c42f2e8a0c67f37df7aa08b166ae1844ae843814..55119851f289e29d0be3c2f8106f0d1bdca45592 100644 --- a/p2p/simulations/http.go +++ b/p2p/simulations/http.go @@ -29,12 +29,12 @@ import ( "strings" "sync" + "github.com/julienschmidt/httprouter" "github.com/maticnetwork/bor/event" "github.com/maticnetwork/bor/p2p" "github.com/maticnetwork/bor/p2p/enode" "github.com/maticnetwork/bor/p2p/simulations/adapters" "github.com/maticnetwork/bor/rpc" - "github.com/julienschmidt/httprouter" "golang.org/x/net/websocket" )