Unverified Commit 28c5a8a5 authored by gary rong's avatar gary rong Committed by GitHub

les: implement new les fetcher (#20692)

* cmd, consensus, eth, les: implement light fetcher

* les: address comment

* les: address comment

* les: address comments

* les: check td after delivery

* les: add linearExpiredValue for error counter

* les: fix import

* les: fix dead lock

* les: order announces by td

* les: encapsulate invalid counter

* les: address comment

* les: add more checks during the delivery

* les: fix log

* eth, les: fix lint

* eth/fetcher: address comment
parent 93da0cf8
...@@ -200,11 +200,11 @@ func (e *NoRewardEngine) Author(header *types.Header) (common.Address, error) { ...@@ -200,11 +200,11 @@ func (e *NoRewardEngine) Author(header *types.Header) (common.Address, error) {
return e.inner.Author(header) return e.inner.Author(header)
} }
func (e *NoRewardEngine) VerifyHeader(chain consensus.ChainReader, header *types.Header, seal bool) error { func (e *NoRewardEngine) VerifyHeader(chain consensus.ChainHeaderReader, header *types.Header, seal bool) error {
return e.inner.VerifyHeader(chain, header, seal) return e.inner.VerifyHeader(chain, header, seal)
} }
func (e *NoRewardEngine) VerifyHeaders(chain consensus.ChainReader, headers []*types.Header, seals []bool) (chan<- struct{}, <-chan error) { func (e *NoRewardEngine) VerifyHeaders(chain consensus.ChainHeaderReader, headers []*types.Header, seals []bool) (chan<- struct{}, <-chan error) {
return e.inner.VerifyHeaders(chain, headers, seals) return e.inner.VerifyHeaders(chain, headers, seals)
} }
...@@ -212,11 +212,11 @@ func (e *NoRewardEngine) VerifyUncles(chain consensus.ChainReader, block *types. ...@@ -212,11 +212,11 @@ func (e *NoRewardEngine) VerifyUncles(chain consensus.ChainReader, block *types.
return e.inner.VerifyUncles(chain, block) return e.inner.VerifyUncles(chain, block)
} }
func (e *NoRewardEngine) VerifySeal(chain consensus.ChainReader, header *types.Header) error { func (e *NoRewardEngine) VerifySeal(chain consensus.ChainHeaderReader, header *types.Header) error {
return e.inner.VerifySeal(chain, header) return e.inner.VerifySeal(chain, header)
} }
func (e *NoRewardEngine) Prepare(chain consensus.ChainReader, header *types.Header) error { func (e *NoRewardEngine) Prepare(chain consensus.ChainHeaderReader, header *types.Header) error {
return e.inner.Prepare(chain, header) return e.inner.Prepare(chain, header)
} }
...@@ -229,7 +229,7 @@ func (e *NoRewardEngine) accumulateRewards(config *params.ChainConfig, state *st ...@@ -229,7 +229,7 @@ func (e *NoRewardEngine) accumulateRewards(config *params.ChainConfig, state *st
state.AddBalance(header.Coinbase, reward) state.AddBalance(header.Coinbase, reward)
} }
func (e *NoRewardEngine) Finalize(chain consensus.ChainReader, header *types.Header, statedb *state.StateDB, txs []*types.Transaction, func (e *NoRewardEngine) Finalize(chain consensus.ChainHeaderReader, header *types.Header, statedb *state.StateDB, txs []*types.Transaction,
uncles []*types.Header) { uncles []*types.Header) {
if e.rewardsOn { if e.rewardsOn {
e.inner.Finalize(chain, header, statedb, txs, uncles) e.inner.Finalize(chain, header, statedb, txs, uncles)
...@@ -239,7 +239,7 @@ func (e *NoRewardEngine) Finalize(chain consensus.ChainReader, header *types.Hea ...@@ -239,7 +239,7 @@ func (e *NoRewardEngine) Finalize(chain consensus.ChainReader, header *types.Hea
} }
} }
func (e *NoRewardEngine) FinalizeAndAssemble(chain consensus.ChainReader, header *types.Header, statedb *state.StateDB, txs []*types.Transaction, func (e *NoRewardEngine) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, statedb *state.StateDB, txs []*types.Transaction,
uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) { uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) {
if e.rewardsOn { if e.rewardsOn {
return e.inner.FinalizeAndAssemble(chain, header, statedb, txs, uncles, receipts) return e.inner.FinalizeAndAssemble(chain, header, statedb, txs, uncles, receipts)
...@@ -252,7 +252,7 @@ func (e *NoRewardEngine) FinalizeAndAssemble(chain consensus.ChainReader, header ...@@ -252,7 +252,7 @@ func (e *NoRewardEngine) FinalizeAndAssemble(chain consensus.ChainReader, header
} }
} }
func (e *NoRewardEngine) Seal(chain consensus.ChainReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error { func (e *NoRewardEngine) Seal(chain consensus.ChainHeaderReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error {
return e.inner.Seal(chain, block, results, stop) return e.inner.Seal(chain, block, results, stop)
} }
...@@ -260,11 +260,11 @@ func (e *NoRewardEngine) SealHash(header *types.Header) common.Hash { ...@@ -260,11 +260,11 @@ func (e *NoRewardEngine) SealHash(header *types.Header) common.Hash {
return e.inner.SealHash(header) return e.inner.SealHash(header)
} }
func (e *NoRewardEngine) CalcDifficulty(chain consensus.ChainReader, time uint64, parent *types.Header) *big.Int { func (e *NoRewardEngine) CalcDifficulty(chain consensus.ChainHeaderReader, time uint64, parent *types.Header) *big.Int {
return e.inner.CalcDifficulty(chain, time, parent) return e.inner.CalcDifficulty(chain, time, parent)
} }
func (e *NoRewardEngine) APIs(chain consensus.ChainReader) []rpc.API { func (e *NoRewardEngine) APIs(chain consensus.ChainHeaderReader) []rpc.API {
return e.inner.APIs(chain) return e.inner.APIs(chain)
} }
......
...@@ -28,7 +28,7 @@ import ( ...@@ -28,7 +28,7 @@ import (
// API is a user facing RPC API to allow controlling the signer and voting // API is a user facing RPC API to allow controlling the signer and voting
// mechanisms of the proof-of-authority scheme. // mechanisms of the proof-of-authority scheme.
type API struct { type API struct {
chain consensus.ChainReader chain consensus.ChainHeaderReader
clique *Clique clique *Clique
} }
......
...@@ -213,14 +213,14 @@ func (c *Clique) Author(header *types.Header) (common.Address, error) { ...@@ -213,14 +213,14 @@ func (c *Clique) Author(header *types.Header) (common.Address, error) {
} }
// VerifyHeader checks whether a header conforms to the consensus rules. // VerifyHeader checks whether a header conforms to the consensus rules.
func (c *Clique) VerifyHeader(chain consensus.ChainReader, header *types.Header, seal bool) error { func (c *Clique) VerifyHeader(chain consensus.ChainHeaderReader, header *types.Header, seal bool) error {
return c.verifyHeader(chain, header, nil) return c.verifyHeader(chain, header, nil)
} }
// VerifyHeaders is similar to VerifyHeader, but verifies a batch of headers. The // VerifyHeaders is similar to VerifyHeader, but verifies a batch of headers. The
// method returns a quit channel to abort the operations and a results channel to // method returns a quit channel to abort the operations and a results channel to
// retrieve the async verifications (the order is that of the input slice). // retrieve the async verifications (the order is that of the input slice).
func (c *Clique) VerifyHeaders(chain consensus.ChainReader, headers []*types.Header, seals []bool) (chan<- struct{}, <-chan error) { func (c *Clique) VerifyHeaders(chain consensus.ChainHeaderReader, headers []*types.Header, seals []bool) (chan<- struct{}, <-chan error) {
abort := make(chan struct{}) abort := make(chan struct{})
results := make(chan error, len(headers)) results := make(chan error, len(headers))
...@@ -242,7 +242,7 @@ func (c *Clique) VerifyHeaders(chain consensus.ChainReader, headers []*types.Hea ...@@ -242,7 +242,7 @@ func (c *Clique) VerifyHeaders(chain consensus.ChainReader, headers []*types.Hea
// caller may optionally pass in a batch of parents (ascending order) to avoid // caller may optionally pass in a batch of parents (ascending order) to avoid
// looking those up from the database. This is useful for concurrently verifying // looking those up from the database. This is useful for concurrently verifying
// a batch of new headers. // a batch of new headers.
func (c *Clique) verifyHeader(chain consensus.ChainReader, header *types.Header, parents []*types.Header) error { func (c *Clique) verifyHeader(chain consensus.ChainHeaderReader, header *types.Header, parents []*types.Header) error {
if header.Number == nil { if header.Number == nil {
return errUnknownBlock return errUnknownBlock
} }
...@@ -305,7 +305,7 @@ func (c *Clique) verifyHeader(chain consensus.ChainReader, header *types.Header, ...@@ -305,7 +305,7 @@ func (c *Clique) verifyHeader(chain consensus.ChainReader, header *types.Header,
// rather depend on a batch of previous headers. The caller may optionally pass // 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 // in a batch of parents (ascending order) to avoid looking those up from the
// database. This is useful for concurrently verifying a batch of new headers. // database. This is useful for concurrently verifying a batch of new headers.
func (c *Clique) verifyCascadingFields(chain consensus.ChainReader, header *types.Header, parents []*types.Header) error { func (c *Clique) verifyCascadingFields(chain consensus.ChainHeaderReader, header *types.Header, parents []*types.Header) error {
// The genesis block is the always valid dead-end // The genesis block is the always valid dead-end
number := header.Number.Uint64() number := header.Number.Uint64()
if number == 0 { if number == 0 {
...@@ -345,7 +345,7 @@ func (c *Clique) verifyCascadingFields(chain consensus.ChainReader, header *type ...@@ -345,7 +345,7 @@ func (c *Clique) verifyCascadingFields(chain consensus.ChainReader, header *type
} }
// snapshot retrieves the authorization snapshot at a given point in time. // snapshot retrieves the authorization snapshot at a given point in time.
func (c *Clique) snapshot(chain consensus.ChainReader, number uint64, hash common.Hash, parents []*types.Header) (*Snapshot, error) { func (c *Clique) snapshot(chain consensus.ChainHeaderReader, number uint64, hash common.Hash, parents []*types.Header) (*Snapshot, error) {
// Search for a snapshot in memory or on disk for checkpoints // Search for a snapshot in memory or on disk for checkpoints
var ( var (
headers []*types.Header headers []*types.Header
...@@ -436,7 +436,7 @@ func (c *Clique) VerifyUncles(chain consensus.ChainReader, block *types.Block) e ...@@ -436,7 +436,7 @@ func (c *Clique) VerifyUncles(chain consensus.ChainReader, block *types.Block) e
// VerifySeal implements consensus.Engine, checking whether the signature contained // VerifySeal implements consensus.Engine, checking whether the signature contained
// in the header satisfies the consensus protocol requirements. // in the header satisfies the consensus protocol requirements.
func (c *Clique) VerifySeal(chain consensus.ChainReader, header *types.Header) error { func (c *Clique) VerifySeal(chain consensus.ChainHeaderReader, header *types.Header) error {
return c.verifySeal(chain, header, nil) return c.verifySeal(chain, header, nil)
} }
...@@ -444,7 +444,7 @@ func (c *Clique) VerifySeal(chain consensus.ChainReader, header *types.Header) e ...@@ -444,7 +444,7 @@ func (c *Clique) VerifySeal(chain consensus.ChainReader, header *types.Header) e
// consensus protocol requirements. The method accepts an optional list of parent // consensus protocol requirements. The method accepts an optional list of parent
// headers that aren't yet part of the local blockchain to generate the snapshots // headers that aren't yet part of the local blockchain to generate the snapshots
// from. // from.
func (c *Clique) verifySeal(chain consensus.ChainReader, header *types.Header, parents []*types.Header) error { func (c *Clique) verifySeal(chain consensus.ChainHeaderReader, header *types.Header, parents []*types.Header) error {
// Verifying the genesis block is not supported // Verifying the genesis block is not supported
number := header.Number.Uint64() number := header.Number.Uint64()
if number == 0 { if number == 0 {
...@@ -487,7 +487,7 @@ func (c *Clique) verifySeal(chain consensus.ChainReader, header *types.Header, p ...@@ -487,7 +487,7 @@ func (c *Clique) verifySeal(chain consensus.ChainReader, header *types.Header, p
// Prepare implements consensus.Engine, preparing all the consensus fields of the // Prepare implements consensus.Engine, preparing all the consensus fields of the
// header for running the transactions on top. // header for running the transactions on top.
func (c *Clique) Prepare(chain consensus.ChainReader, header *types.Header) error { func (c *Clique) Prepare(chain consensus.ChainHeaderReader, header *types.Header) error {
// If the block isn't a checkpoint, cast a random vote (good enough for now) // If the block isn't a checkpoint, cast a random vote (good enough for now)
header.Coinbase = common.Address{} header.Coinbase = common.Address{}
header.Nonce = types.BlockNonce{} header.Nonce = types.BlockNonce{}
...@@ -552,7 +552,7 @@ func (c *Clique) Prepare(chain consensus.ChainReader, header *types.Header) erro ...@@ -552,7 +552,7 @@ func (c *Clique) Prepare(chain consensus.ChainReader, header *types.Header) erro
// Finalize implements consensus.Engine, ensuring no uncles are set, nor block // Finalize implements consensus.Engine, ensuring no uncles are set, nor block
// rewards given. // rewards given.
func (c *Clique) Finalize(chain consensus.ChainReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header) { func (c *Clique) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header) {
// No block rewards in PoA, so the state remains as is and uncles are dropped // No block rewards in PoA, so the state remains as is and uncles are dropped
header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number))
header.UncleHash = types.CalcUncleHash(nil) header.UncleHash = types.CalcUncleHash(nil)
...@@ -560,7 +560,7 @@ func (c *Clique) Finalize(chain consensus.ChainReader, header *types.Header, sta ...@@ -560,7 +560,7 @@ func (c *Clique) Finalize(chain consensus.ChainReader, header *types.Header, sta
// FinalizeAndAssemble implements consensus.Engine, ensuring no uncles are set, // FinalizeAndAssemble implements consensus.Engine, ensuring no uncles are set,
// nor block rewards given, and returns the final block. // nor block rewards given, and returns the final block.
func (c *Clique) FinalizeAndAssemble(chain consensus.ChainReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) { func (c *Clique) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) {
// No block rewards in PoA, so the state remains as is and uncles are dropped // No block rewards in PoA, so the state remains as is and uncles are dropped
header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number))
header.UncleHash = types.CalcUncleHash(nil) header.UncleHash = types.CalcUncleHash(nil)
...@@ -581,7 +581,7 @@ func (c *Clique) Authorize(signer common.Address, signFn SignerFn) { ...@@ -581,7 +581,7 @@ func (c *Clique) Authorize(signer common.Address, signFn SignerFn) {
// Seal implements consensus.Engine, attempting to create a sealed block using // Seal implements consensus.Engine, attempting to create a sealed block using
// the local signing credentials. // the local signing credentials.
func (c *Clique) Seal(chain consensus.ChainReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error { func (c *Clique) Seal(chain consensus.ChainHeaderReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error {
header := block.Header() header := block.Header()
// Sealing the genesis block is not supported // Sealing the genesis block is not supported
...@@ -654,7 +654,7 @@ func (c *Clique) Seal(chain consensus.ChainReader, block *types.Block, results c ...@@ -654,7 +654,7 @@ func (c *Clique) Seal(chain consensus.ChainReader, block *types.Block, results c
// CalcDifficulty is the difficulty adjustment algorithm. It returns the difficulty // 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 // that a new block should have based on the previous blocks in the chain and the
// current signer. // current signer.
func (c *Clique) CalcDifficulty(chain consensus.ChainReader, time uint64, parent *types.Header) *big.Int { func (c *Clique) CalcDifficulty(chain consensus.ChainHeaderReader, time uint64, parent *types.Header) *big.Int {
snap, err := c.snapshot(chain, parent.Number.Uint64(), parent.Hash(), nil) snap, err := c.snapshot(chain, parent.Number.Uint64(), parent.Hash(), nil)
if err != nil { if err != nil {
return nil return nil
...@@ -684,7 +684,7 @@ func (c *Clique) Close() error { ...@@ -684,7 +684,7 @@ func (c *Clique) Close() error {
// APIs implements consensus.Engine, returning the user facing RPC API to allow // APIs implements consensus.Engine, returning the user facing RPC API to allow
// controlling the signer voting. // controlling the signer voting.
func (c *Clique) APIs(chain consensus.ChainReader) []rpc.API { func (c *Clique) APIs(chain consensus.ChainHeaderReader) []rpc.API {
return []rpc.API{{ return []rpc.API{{
Namespace: "clique", Namespace: "clique",
Version: "1.0", Version: "1.0",
......
...@@ -27,9 +27,9 @@ import ( ...@@ -27,9 +27,9 @@ import (
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
) )
// ChainReader defines a small collection of methods needed to access the local // ChainHeaderReader defines a small collection of methods needed to access the local
// blockchain during header and/or uncle verification. // blockchain during header verification.
type ChainReader interface { type ChainHeaderReader interface {
// Config retrieves the blockchain's chain configuration. // Config retrieves the blockchain's chain configuration.
Config() *params.ChainConfig Config() *params.ChainConfig
...@@ -44,6 +44,12 @@ type ChainReader interface { ...@@ -44,6 +44,12 @@ type ChainReader interface {
// GetHeaderByHash retrieves a block header from the database by its hash. // GetHeaderByHash retrieves a block header from the database by its hash.
GetHeaderByHash(hash common.Hash) *types.Header GetHeaderByHash(hash common.Hash) *types.Header
}
// ChainReader defines a small collection of methods needed to access the local
// blockchain during header and/or uncle verification.
type ChainReader interface {
ChainHeaderReader
// GetBlock retrieves a block from the database by hash and number. // GetBlock retrieves a block from the database by hash and number.
GetBlock(hash common.Hash, number uint64) *types.Block GetBlock(hash common.Hash, number uint64) *types.Block
...@@ -59,13 +65,13 @@ type Engine interface { ...@@ -59,13 +65,13 @@ type Engine interface {
// VerifyHeader checks whether a header conforms to the consensus rules of a // VerifyHeader checks whether a header conforms to the consensus rules of a
// given engine. Verifying the seal may be done optionally here, or explicitly // given engine. Verifying the seal may be done optionally here, or explicitly
// via the VerifySeal method. // via the VerifySeal method.
VerifyHeader(chain ChainReader, header *types.Header, seal bool) error VerifyHeader(chain ChainHeaderReader, header *types.Header, seal bool) error
// VerifyHeaders is similar to VerifyHeader, but verifies a batch of headers // VerifyHeaders is similar to VerifyHeader, but verifies a batch of headers
// concurrently. The method returns a quit channel to abort the operations and // concurrently. The method returns a quit channel to abort the operations and
// a results channel to retrieve the async verifications (the order is that of // a results channel to retrieve the async verifications (the order is that of
// the input slice). // the input slice).
VerifyHeaders(chain ChainReader, headers []*types.Header, seals []bool) (chan<- struct{}, <-chan error) VerifyHeaders(chain ChainHeaderReader, headers []*types.Header, seals []bool) (chan<- struct{}, <-chan error)
// VerifyUncles verifies that the given block's uncles conform to the consensus // VerifyUncles verifies that the given block's uncles conform to the consensus
// rules of a given engine. // rules of a given engine.
...@@ -73,18 +79,18 @@ type Engine interface { ...@@ -73,18 +79,18 @@ type Engine interface {
// VerifySeal checks whether the crypto seal on a header is valid according to // VerifySeal checks whether the crypto seal on a header is valid according to
// the consensus rules of the given engine. // the consensus rules of the given engine.
VerifySeal(chain ChainReader, header *types.Header) error VerifySeal(chain ChainHeaderReader, header *types.Header) error
// Prepare initializes the consensus fields of a block header according to the // Prepare initializes the consensus fields of a block header according to the
// rules of a particular engine. The changes are executed inline. // rules of a particular engine. The changes are executed inline.
Prepare(chain ChainReader, header *types.Header) error Prepare(chain ChainHeaderReader, header *types.Header) error
// Finalize runs any post-transaction state modifications (e.g. block rewards) // Finalize runs any post-transaction state modifications (e.g. block rewards)
// but does not assemble the block. // but does not assemble the block.
// //
// Note: The block header and state database might be updated to reflect any // Note: The block header and state database might be updated to reflect any
// consensus rules that happen at finalization (e.g. block rewards). // consensus rules that happen at finalization (e.g. block rewards).
Finalize(chain ChainReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, Finalize(chain ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction,
uncles []*types.Header) uncles []*types.Header)
// FinalizeAndAssemble runs any post-transaction state modifications (e.g. block // FinalizeAndAssemble runs any post-transaction state modifications (e.g. block
...@@ -92,7 +98,7 @@ type Engine interface { ...@@ -92,7 +98,7 @@ type Engine interface {
// //
// Note: The block header and state database might be updated to reflect any // Note: The block header and state database might be updated to reflect any
// consensus rules that happen at finalization (e.g. block rewards). // consensus rules that happen at finalization (e.g. block rewards).
FinalizeAndAssemble(chain ChainReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, FinalizeAndAssemble(chain ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction,
uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error)
// Seal generates a new sealing request for the given input block and pushes // Seal generates a new sealing request for the given input block and pushes
...@@ -100,17 +106,17 @@ type Engine interface { ...@@ -100,17 +106,17 @@ type Engine interface {
// //
// Note, the method returns immediately and will send the result async. More // Note, the method returns immediately and will send the result async. More
// than one result may also be returned depending on the consensus algorithm. // than one result may also be returned depending on the consensus algorithm.
Seal(chain ChainReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error Seal(chain ChainHeaderReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error
// SealHash returns the hash of a block prior to it being sealed. // SealHash returns the hash of a block prior to it being sealed.
SealHash(header *types.Header) common.Hash SealHash(header *types.Header) common.Hash
// CalcDifficulty is the difficulty adjustment algorithm. It returns the difficulty // CalcDifficulty is the difficulty adjustment algorithm. It returns the difficulty
// that a new block should have. // that a new block should have.
CalcDifficulty(chain ChainReader, time uint64, parent *types.Header) *big.Int CalcDifficulty(chain ChainHeaderReader, time uint64, parent *types.Header) *big.Int
// APIs returns the RPC APIs this consensus engine provides. // APIs returns the RPC APIs this consensus engine provides.
APIs(chain ChainReader) []rpc.API APIs(chain ChainHeaderReader) []rpc.API
// Close terminates any background threads maintained by the consensus engine. // Close terminates any background threads maintained by the consensus engine.
Close() error Close() error
......
...@@ -86,7 +86,7 @@ func (ethash *Ethash) Author(header *types.Header) (common.Address, error) { ...@@ -86,7 +86,7 @@ func (ethash *Ethash) Author(header *types.Header) (common.Address, error) {
// VerifyHeader checks whether a header conforms to the consensus rules of the // VerifyHeader checks whether a header conforms to the consensus rules of the
// stock Ethereum ethash engine. // stock Ethereum ethash engine.
func (ethash *Ethash) VerifyHeader(chain consensus.ChainReader, header *types.Header, seal bool) error { func (ethash *Ethash) VerifyHeader(chain consensus.ChainHeaderReader, header *types.Header, seal bool) error {
// If we're running a full engine faking, accept any input as valid // If we're running a full engine faking, accept any input as valid
if ethash.config.PowMode == ModeFullFake { if ethash.config.PowMode == ModeFullFake {
return nil return nil
...@@ -107,7 +107,7 @@ func (ethash *Ethash) VerifyHeader(chain consensus.ChainReader, header *types.He ...@@ -107,7 +107,7 @@ func (ethash *Ethash) VerifyHeader(chain consensus.ChainReader, header *types.He
// VerifyHeaders is similar to VerifyHeader, but verifies a batch of headers // VerifyHeaders is similar to VerifyHeader, but verifies a batch of headers
// concurrently. The method returns a quit channel to abort the operations and // concurrently. The method returns a quit channel to abort the operations and
// a results channel to retrieve the async verifications. // a results channel to retrieve the async verifications.
func (ethash *Ethash) VerifyHeaders(chain consensus.ChainReader, headers []*types.Header, seals []bool) (chan<- struct{}, <-chan error) { func (ethash *Ethash) VerifyHeaders(chain consensus.ChainHeaderReader, headers []*types.Header, seals []bool) (chan<- struct{}, <-chan error) {
// If we're running a full engine faking, accept any input as valid // If we're running a full engine faking, accept any input as valid
if ethash.config.PowMode == ModeFullFake || len(headers) == 0 { if ethash.config.PowMode == ModeFullFake || len(headers) == 0 {
abort, results := make(chan struct{}), make(chan error, len(headers)) abort, results := make(chan struct{}), make(chan error, len(headers))
...@@ -169,7 +169,7 @@ func (ethash *Ethash) VerifyHeaders(chain consensus.ChainReader, headers []*type ...@@ -169,7 +169,7 @@ func (ethash *Ethash) VerifyHeaders(chain consensus.ChainReader, headers []*type
return abort, errorsOut return abort, errorsOut
} }
func (ethash *Ethash) verifyHeaderWorker(chain consensus.ChainReader, headers []*types.Header, seals []bool, index int) error { func (ethash *Ethash) verifyHeaderWorker(chain consensus.ChainHeaderReader, headers []*types.Header, seals []bool, index int) error {
var parent *types.Header var parent *types.Header
if index == 0 { if index == 0 {
parent = chain.GetHeader(headers[0].ParentHash, headers[0].Number.Uint64()-1) parent = chain.GetHeader(headers[0].ParentHash, headers[0].Number.Uint64()-1)
...@@ -243,7 +243,7 @@ func (ethash *Ethash) VerifyUncles(chain consensus.ChainReader, block *types.Blo ...@@ -243,7 +243,7 @@ func (ethash *Ethash) VerifyUncles(chain consensus.ChainReader, block *types.Blo
// verifyHeader checks whether a header conforms to the consensus rules of the // verifyHeader checks whether a header conforms to the consensus rules of the
// stock Ethereum ethash engine. // stock Ethereum ethash engine.
// See YP section 4.3.4. "Block Header Validity" // See YP section 4.3.4. "Block Header Validity"
func (ethash *Ethash) verifyHeader(chain consensus.ChainReader, header, parent *types.Header, uncle bool, seal bool) error { func (ethash *Ethash) verifyHeader(chain consensus.ChainHeaderReader, header, parent *types.Header, uncle bool, seal bool) error {
// Ensure that the header's extra-data section is of a reasonable size // Ensure that the header's extra-data section is of a reasonable size
if uint64(len(header.Extra)) > params.MaximumExtraDataSize { if uint64(len(header.Extra)) > params.MaximumExtraDataSize {
return fmt.Errorf("extra-data too long: %d > %d", len(header.Extra), params.MaximumExtraDataSize) return fmt.Errorf("extra-data too long: %d > %d", len(header.Extra), params.MaximumExtraDataSize)
...@@ -306,7 +306,7 @@ func (ethash *Ethash) verifyHeader(chain consensus.ChainReader, header, parent * ...@@ -306,7 +306,7 @@ func (ethash *Ethash) verifyHeader(chain consensus.ChainReader, header, parent *
// CalcDifficulty is the difficulty adjustment algorithm. It returns // CalcDifficulty is the difficulty adjustment algorithm. It returns
// the difficulty that a new block should have when created at time // the difficulty that a new block should have when created at time
// given the parent block's time and difficulty. // given the parent block's time and difficulty.
func (ethash *Ethash) CalcDifficulty(chain consensus.ChainReader, time uint64, parent *types.Header) *big.Int { func (ethash *Ethash) CalcDifficulty(chain consensus.ChainHeaderReader, time uint64, parent *types.Header) *big.Int {
return CalcDifficulty(chain.Config(), time, parent) return CalcDifficulty(chain.Config(), time, parent)
} }
...@@ -486,14 +486,14 @@ func calcDifficultyFrontier(time uint64, parent *types.Header) *big.Int { ...@@ -486,14 +486,14 @@ func calcDifficultyFrontier(time uint64, parent *types.Header) *big.Int {
// VerifySeal implements consensus.Engine, checking whether the given block satisfies // VerifySeal implements consensus.Engine, checking whether the given block satisfies
// the PoW difficulty requirements. // the PoW difficulty requirements.
func (ethash *Ethash) VerifySeal(chain consensus.ChainReader, header *types.Header) error { func (ethash *Ethash) VerifySeal(chain consensus.ChainHeaderReader, header *types.Header) error {
return ethash.verifySeal(chain, header, false) return ethash.verifySeal(chain, header, false)
} }
// verifySeal checks whether a block satisfies the PoW difficulty requirements, // verifySeal checks whether a block satisfies the PoW difficulty requirements,
// either using the usual ethash cache for it, or alternatively using a full DAG // either using the usual ethash cache for it, or alternatively using a full DAG
// to make remote mining fast. // to make remote mining fast.
func (ethash *Ethash) verifySeal(chain consensus.ChainReader, header *types.Header, fulldag bool) error { func (ethash *Ethash) verifySeal(chain consensus.ChainHeaderReader, header *types.Header, fulldag bool) error {
// If we're running a fake PoW, accept any seal as valid // If we're running a fake PoW, accept any seal as valid
if ethash.config.PowMode == ModeFake || ethash.config.PowMode == ModeFullFake { if ethash.config.PowMode == ModeFake || ethash.config.PowMode == ModeFullFake {
time.Sleep(ethash.fakeDelay) time.Sleep(ethash.fakeDelay)
...@@ -558,7 +558,7 @@ func (ethash *Ethash) verifySeal(chain consensus.ChainReader, header *types.Head ...@@ -558,7 +558,7 @@ func (ethash *Ethash) verifySeal(chain consensus.ChainReader, header *types.Head
// Prepare implements consensus.Engine, initializing the difficulty field of a // Prepare implements consensus.Engine, initializing the difficulty field of a
// header to conform to the ethash protocol. The changes are done inline. // header to conform to the ethash protocol. The changes are done inline.
func (ethash *Ethash) Prepare(chain consensus.ChainReader, header *types.Header) error { func (ethash *Ethash) Prepare(chain consensus.ChainHeaderReader, header *types.Header) error {
parent := chain.GetHeader(header.ParentHash, header.Number.Uint64()-1) parent := chain.GetHeader(header.ParentHash, header.Number.Uint64()-1)
if parent == nil { if parent == nil {
return consensus.ErrUnknownAncestor return consensus.ErrUnknownAncestor
...@@ -569,7 +569,7 @@ func (ethash *Ethash) Prepare(chain consensus.ChainReader, header *types.Header) ...@@ -569,7 +569,7 @@ func (ethash *Ethash) Prepare(chain consensus.ChainReader, header *types.Header)
// Finalize implements consensus.Engine, accumulating the block and uncle rewards, // Finalize implements consensus.Engine, accumulating the block and uncle rewards,
// setting the final state on the header // setting the final state on the header
func (ethash *Ethash) Finalize(chain consensus.ChainReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header) { func (ethash *Ethash) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header) {
// Accumulate any block and uncle rewards and commit the final state root // Accumulate any block and uncle rewards and commit the final state root
accumulateRewards(chain.Config(), state, header, uncles) accumulateRewards(chain.Config(), state, header, uncles)
header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number))
...@@ -577,7 +577,7 @@ func (ethash *Ethash) Finalize(chain consensus.ChainReader, header *types.Header ...@@ -577,7 +577,7 @@ func (ethash *Ethash) Finalize(chain consensus.ChainReader, header *types.Header
// FinalizeAndAssemble implements consensus.Engine, accumulating the block and // FinalizeAndAssemble implements consensus.Engine, accumulating the block and
// uncle rewards, setting the final state and assembling the block. // uncle rewards, setting the final state and assembling the block.
func (ethash *Ethash) FinalizeAndAssemble(chain consensus.ChainReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) { func (ethash *Ethash) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) {
// Accumulate any block and uncle rewards and commit the final state root // Accumulate any block and uncle rewards and commit the final state root
accumulateRewards(chain.Config(), state, header, uncles) accumulateRewards(chain.Config(), state, header, uncles)
header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number))
......
...@@ -656,7 +656,7 @@ func (ethash *Ethash) Hashrate() float64 { ...@@ -656,7 +656,7 @@ func (ethash *Ethash) Hashrate() float64 {
} }
// APIs implements consensus.Engine, returning the user facing RPC APIs. // APIs implements consensus.Engine, returning the user facing RPC APIs.
func (ethash *Ethash) APIs(chain consensus.ChainReader) []rpc.API { func (ethash *Ethash) APIs(chain consensus.ChainHeaderReader) []rpc.API {
// In order to ensure backward compatibility, we exposes ethash RPC APIs // In order to ensure backward compatibility, we exposes ethash RPC APIs
// to both eth and ethash namespaces. // to both eth and ethash namespaces.
return []rpc.API{ return []rpc.API{
......
...@@ -48,7 +48,7 @@ var ( ...@@ -48,7 +48,7 @@ var (
// Seal implements consensus.Engine, attempting to find a nonce that satisfies // Seal implements consensus.Engine, attempting to find a nonce that satisfies
// the block's difficulty requirements. // the block's difficulty requirements.
func (ethash *Ethash) Seal(chain consensus.ChainReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error { func (ethash *Ethash) Seal(chain consensus.ChainHeaderReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error {
// If we're running a fake PoW, simply return a 0 nonce immediately // If we're running a fake PoW, simply return a 0 nonce immediately
if ethash.config.PowMode == ModeFake || ethash.config.PowMode == ModeFullFake { if ethash.config.PowMode == ModeFake || ethash.config.PowMode == ModeFullFake {
header := block.Header() header := block.Header()
......
This diff is collapsed.
This diff is collapsed.
...@@ -188,7 +188,7 @@ func NewProtocolManager(config *params.ChainConfig, checkpoint *params.TrustedCh ...@@ -188,7 +188,7 @@ func NewProtocolManager(config *params.ChainConfig, checkpoint *params.TrustedCh
} }
return n, err return n, err
} }
manager.blockFetcher = fetcher.NewBlockFetcher(blockchain.GetBlockByHash, validator, manager.BroadcastBlock, heighter, inserter, manager.removePeer) manager.blockFetcher = fetcher.NewBlockFetcher(false, nil, blockchain.GetBlockByHash, validator, manager.BroadcastBlock, heighter, nil, inserter, manager.removePeer)
fetchTx := func(peer string, hashes []common.Hash) error { fetchTx := func(peer string, hashes []common.Hash) error {
p := manager.peers.Peer(peer) p := manager.peers.Peer(peer)
......
...@@ -269,7 +269,7 @@ func (s *LightEthereum) EventMux() *event.TypeMux { return s.eventMux ...@@ -269,7 +269,7 @@ func (s *LightEthereum) EventMux() *event.TypeMux { return s.eventMux
// network protocols to start. // network protocols to start.
func (s *LightEthereum) Protocols() []p2p.Protocol { func (s *LightEthereum) Protocols() []p2p.Protocol {
return s.makeProtocols(ClientProtocolVersions, s.handler.runPeer, func(id enode.ID) interface{} { return s.makeProtocols(ClientProtocolVersions, s.handler.runPeer, func(id enode.ID) interface{} {
if p := s.peers.peer(peerIdToString(id)); p != nil { if p := s.peers.peer(id.String()); p != nil {
return p.Info() return p.Info()
} }
return nil return nil
...@@ -285,6 +285,7 @@ func (s *LightEthereum) Start(srvr *p2p.Server) error { ...@@ -285,6 +285,7 @@ func (s *LightEthereum) Start(srvr *p2p.Server) error {
// Start bloom request workers. // Start bloom request workers.
s.wg.Add(bloomServiceThreads) s.wg.Add(bloomServiceThreads)
s.startBloomHandlers(params.BloomBitsBlocksClient) s.startBloomHandlers(params.BloomBitsBlocksClient)
s.handler.start()
s.netRPCService = ethapi.NewPublicNetAPI(srvr, s.config.NetworkId) s.netRPCService = ethapi.NewPublicNetAPI(srvr, s.config.NetworkId)
return nil return nil
......
...@@ -64,16 +64,20 @@ func newClientHandler(ulcServers []string, ulcFraction int, checkpoint *params.T ...@@ -64,16 +64,20 @@ func newClientHandler(ulcServers []string, ulcFraction int, checkpoint *params.T
if checkpoint != nil { if checkpoint != nil {
height = (checkpoint.SectionIndex+1)*params.CHTFrequency - 1 height = (checkpoint.SectionIndex+1)*params.CHTFrequency - 1
} }
handler.fetcher = newLightFetcher(handler, backend.serverPool.getTimeout) handler.fetcher = newLightFetcher(backend.blockchain, backend.engine, backend.peers, handler.ulc, backend.chainDb, backend.reqDist, handler.synchronise)
handler.downloader = downloader.New(height, backend.chainDb, nil, backend.eventMux, nil, backend.blockchain, handler.removePeer) handler.downloader = downloader.New(height, backend.chainDb, nil, backend.eventMux, nil, backend.blockchain, handler.removePeer)
handler.backend.peers.subscribe((*downloaderPeerNotify)(handler)) handler.backend.peers.subscribe((*downloaderPeerNotify)(handler))
return handler return handler
} }
func (h *clientHandler) start() {
h.fetcher.start()
}
func (h *clientHandler) stop() { func (h *clientHandler) stop() {
close(h.closeCh) close(h.closeCh)
h.downloader.Terminate() h.downloader.Terminate()
h.fetcher.close() h.fetcher.stop()
h.wg.Wait() h.wg.Wait()
} }
...@@ -121,7 +125,6 @@ func (h *clientHandler) handle(p *serverPeer) error { ...@@ -121,7 +125,6 @@ func (h *clientHandler) handle(p *serverPeer) error {
connectionTimer.Update(time.Duration(mclock.Now() - connectedAt)) connectionTimer.Update(time.Duration(mclock.Now() - connectedAt))
serverConnectionGauge.Update(int64(h.backend.peers.len())) serverConnectionGauge.Update(int64(h.backend.peers.len()))
}() }()
h.fetcher.announce(p, &announceData{Hash: p.headInfo.Hash, Number: p.headInfo.Number, Td: p.headInfo.Td}) h.fetcher.announce(p, &announceData{Hash: p.headInfo.Hash, Number: p.headInfo.Number, Td: p.headInfo.Td})
// Mark the peer starts to be served. // Mark the peer starts to be served.
...@@ -185,6 +188,9 @@ func (h *clientHandler) handleMsg(p *serverPeer) error { ...@@ -185,6 +188,9 @@ func (h *clientHandler) handleMsg(p *serverPeer) error {
p.Log().Trace("Valid announcement signature") p.Log().Trace("Valid announcement signature")
} }
p.Log().Trace("Announce message content", "number", req.Number, "hash", req.Hash, "td", req.Td, "reorg", req.ReorgDepth) p.Log().Trace("Announce message content", "number", req.Number, "hash", req.Hash, "td", req.Td, "reorg", req.ReorgDepth)
// Update peer head information first and then notify the announcement
p.updateHead(req.Hash, req.Number, req.Td)
h.fetcher.announce(p, &req) h.fetcher.announce(p, &req)
} }
case BlockHeadersMsg: case BlockHeadersMsg:
...@@ -196,12 +202,17 @@ func (h *clientHandler) handleMsg(p *serverPeer) error { ...@@ -196,12 +202,17 @@ func (h *clientHandler) handleMsg(p *serverPeer) error {
if err := msg.Decode(&resp); err != nil { if err := msg.Decode(&resp); err != nil {
return errResp(ErrDecode, "msg %v: %v", msg, err) return errResp(ErrDecode, "msg %v: %v", msg, err)
} }
headers := resp.Headers
p.fcServer.ReceivedReply(resp.ReqID, resp.BV) p.fcServer.ReceivedReply(resp.ReqID, resp.BV)
p.answeredRequest(resp.ReqID) p.answeredRequest(resp.ReqID)
if h.fetcher.requestedID(resp.ReqID) {
h.fetcher.deliverHeaders(p, resp.ReqID, resp.Headers) // Filter out any explicitly requested headers, deliver the rest to the downloader
} else { filter := len(headers) == 1
if err := h.downloader.DeliverHeaders(p.id, resp.Headers); err != nil { if filter {
headers = h.fetcher.deliverHeaders(p, resp.ReqID, resp.Headers)
}
if len(headers) != 0 || !filter {
if err := h.downloader.DeliverHeaders(p.id, headers); err != nil {
log.Debug("Failed to deliver headers", "err", err) log.Debug("Failed to deliver headers", "err", err)
} }
} }
...@@ -320,8 +331,7 @@ func (h *clientHandler) handleMsg(p *serverPeer) error { ...@@ -320,8 +331,7 @@ func (h *clientHandler) handleMsg(p *serverPeer) error {
// Deliver the received response to retriever. // Deliver the received response to retriever.
if deliverMsg != nil { if deliverMsg != nil {
if err := h.backend.retriever.deliver(p, deliverMsg); err != nil { if err := h.backend.retriever.deliver(p, deliverMsg); err != nil {
p.errCount++ if val := p.errCount.Add(1, mclock.Now()); val > maxResponseErrors {
if p.errCount > maxResponseErrors {
return err return err
} }
} }
......
...@@ -212,7 +212,7 @@ func (f *clientPool) connect(peer clientPoolPeer, capacity uint64) bool { ...@@ -212,7 +212,7 @@ func (f *clientPool) connect(peer clientPoolPeer, capacity uint64) bool {
id, freeID := peer.ID(), peer.freeClientId() id, freeID := peer.ID(), peer.freeClientId()
if _, ok := f.connectedMap[id]; ok { if _, ok := f.connectedMap[id]; ok {
clientRejectedMeter.Mark(1) clientRejectedMeter.Mark(1)
log.Debug("Client already connected", "address", freeID, "id", peerIdToString(id)) log.Debug("Client already connected", "address", freeID, "id", id.String())
return false return false
} }
// Create a clientInfo but do not add it yet // Create a clientInfo but do not add it yet
...@@ -277,7 +277,7 @@ func (f *clientPool) connect(peer clientPoolPeer, capacity uint64) bool { ...@@ -277,7 +277,7 @@ func (f *clientPool) connect(peer clientPoolPeer, capacity uint64) bool {
f.connectedQueue.Push(c) f.connectedQueue.Push(c)
} }
clientRejectedMeter.Mark(1) clientRejectedMeter.Mark(1)
log.Debug("Client rejected", "address", freeID, "id", peerIdToString(id)) log.Debug("Client rejected", "address", freeID, "id", id.String())
return false return false
} }
// accept new client, drop old ones // accept new client, drop old ones
...@@ -322,7 +322,7 @@ func (f *clientPool) disconnect(p clientPoolPeer) { ...@@ -322,7 +322,7 @@ func (f *clientPool) disconnect(p clientPoolPeer) {
// Short circuit if the peer hasn't been registered. // Short circuit if the peer hasn't been registered.
e := f.connectedMap[p.ID()] e := f.connectedMap[p.ID()]
if e == nil { if e == nil {
log.Debug("Client not connected", "address", p.freeClientId(), "id", peerIdToString(p.ID())) log.Debug("Client not connected", "address", p.freeClientId(), "id", p.ID().String())
return return
} }
f.dropClient(e, f.clock.Now(), false) f.dropClient(e, f.clock.Now(), false)
......
This diff is collapsed.
// Copyright 2020 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package les
import (
"math/big"
"testing"
"time"
"github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/p2p/enode"
)
// verifyImportEvent verifies that one single event arrive on an import channel.
func verifyImportEvent(t *testing.T, imported chan interface{}, arrive bool) {
if arrive {
select {
case <-imported:
case <-time.After(time.Second):
t.Fatalf("import timeout")
}
} else {
select {
case <-imported:
t.Fatalf("import invoked")
case <-time.After(20 * time.Millisecond):
}
}
}
// verifyImportDone verifies that no more events are arriving on an import channel.
func verifyImportDone(t *testing.T, imported chan interface{}) {
select {
case <-imported:
t.Fatalf("extra block imported")
case <-time.After(50 * time.Millisecond):
}
}
// verifyChainHeight verifies the chain height is as expected.
func verifyChainHeight(t *testing.T, fetcher *lightFetcher, height uint64) {
local := fetcher.chain.CurrentHeader().Number.Uint64()
if local != height {
t.Fatalf("chain height mismatch, got %d, want %d", local, height)
}
}
func TestSequentialAnnouncementsLes2(t *testing.T) { testSequentialAnnouncements(t, 2) }
func TestSequentialAnnouncementsLes3(t *testing.T) { testSequentialAnnouncements(t, 3) }
func testSequentialAnnouncements(t *testing.T, protocol int) {
s, c, teardown := newClientServerEnv(t, 4, protocol, nil, nil, 0, false, false)
defer teardown()
// Create connected peer pair.
c.handler.fetcher.noAnnounce = true // Ignore the first announce from peer which can trigger a resync.
p1, _, err := newTestPeerPair("peer", protocol, s.handler, c.handler)
if err != nil {
t.Fatalf("Failed to create peer pair %v", err)
}
c.handler.fetcher.noAnnounce = false
importCh := make(chan interface{})
c.handler.fetcher.newHeadHook = func(header *types.Header) {
importCh <- header
}
for i := uint64(1); i <= s.backend.Blockchain().CurrentHeader().Number.Uint64(); i++ {
header := s.backend.Blockchain().GetHeaderByNumber(i)
hash, number := header.Hash(), header.Number.Uint64()
td := rawdb.ReadTd(s.db, hash, number)
announce := announceData{hash, number, td, 0, nil}
if p1.cpeer.announceType == announceTypeSigned {
announce.sign(s.handler.server.privateKey)
}
p1.cpeer.sendAnnounce(announce)
verifyImportEvent(t, importCh, true)
}
verifyImportDone(t, importCh)
verifyChainHeight(t, c.handler.fetcher, 4)
}
func TestGappedAnnouncementsLes2(t *testing.T) { testGappedAnnouncements(t, 2) }
func TestGappedAnnouncementsLes3(t *testing.T) { testGappedAnnouncements(t, 3) }
func testGappedAnnouncements(t *testing.T, protocol int) {
s, c, teardown := newClientServerEnv(t, 4, protocol, nil, nil, 0, false, false)
defer teardown()
// Create connected peer pair.
c.handler.fetcher.noAnnounce = true // Ignore the first announce from peer which can trigger a resync.
peer, _, err := newTestPeerPair("peer", protocol, s.handler, c.handler)
if err != nil {
t.Fatalf("Failed to create peer pair %v", err)
}
c.handler.fetcher.noAnnounce = false
done := make(chan *types.Header, 1)
c.handler.fetcher.newHeadHook = func(header *types.Header) { done <- header }
// Prepare announcement by latest header.
latest := s.backend.Blockchain().CurrentHeader()
hash, number := latest.Hash(), latest.Number.Uint64()
td := rawdb.ReadTd(s.db, hash, number)
// Sign the announcement if necessary.
announce := announceData{hash, number, td, 0, nil}
if peer.cpeer.announceType == announceTypeSigned {
announce.sign(s.handler.server.privateKey)
}
peer.cpeer.sendAnnounce(announce)
<-done // Wait syncing
verifyChainHeight(t, c.handler.fetcher, 4)
// Send a reorged announcement
var newAnno = make(chan struct{}, 1)
c.handler.fetcher.noAnnounce = true
c.handler.fetcher.newAnnounce = func(*serverPeer, *announceData) {
newAnno <- struct{}{}
}
blocks, _ := core.GenerateChain(rawdb.ReadChainConfig(s.db, s.backend.Blockchain().Genesis().Hash()), s.backend.Blockchain().GetBlockByNumber(3),
ethash.NewFaker(), s.db, 2, func(i int, gen *core.BlockGen) {
gen.OffsetTime(-9) // higher block difficulty
})
s.backend.Blockchain().InsertChain(blocks)
<-newAnno
c.handler.fetcher.noAnnounce = false
c.handler.fetcher.newAnnounce = nil
latest = blocks[len(blocks)-1].Header()
hash, number = latest.Hash(), latest.Number.Uint64()
td = rawdb.ReadTd(s.db, hash, number)
announce = announceData{hash, number, td, 1, nil}
if peer.cpeer.announceType == announceTypeSigned {
announce.sign(s.handler.server.privateKey)
}
peer.cpeer.sendAnnounce(announce)
<-done // Wait syncing
verifyChainHeight(t, c.handler.fetcher, 5)
}
func TestTrustedAnnouncementsLes2(t *testing.T) { testTrustedAnnouncement(t, 2) }
func TestTrustedAnnouncementsLes3(t *testing.T) { testTrustedAnnouncement(t, 3) }
func testTrustedAnnouncement(t *testing.T, protocol int) {
var (
servers []*testServer
teardowns []func()
nodes []*enode.Node
ids []string
cpeers []*clientPeer
speers []*serverPeer
)
for i := 0; i < 10; i++ {
s, n, teardown := newTestServerPeer(t, 10, protocol)
servers = append(servers, s)
nodes = append(nodes, n)
teardowns = append(teardowns, teardown)
// A half of them are trusted servers.
if i < 5 {
ids = append(ids, n.String())
}
}
_, c, teardown := newClientServerEnv(t, 0, protocol, nil, ids, 60, false, false)
defer teardown()
defer func() {
for i := 0; i < len(teardowns); i++ {
teardowns[i]()
}
}()
c.handler.fetcher.noAnnounce = true // Ignore the first announce from peer which can trigger a resync.
// Connect all server instances.
for i := 0; i < len(servers); i++ {
sp, cp, err := connect(servers[i].handler, nodes[i].ID(), c.handler, protocol)
if err != nil {
t.Fatalf("connect server and client failed, err %s", err)
}
cpeers = append(cpeers, cp)
speers = append(speers, sp)
}
c.handler.fetcher.noAnnounce = false
newHead := make(chan *types.Header, 1)
c.handler.fetcher.newHeadHook = func(header *types.Header) { newHead <- header }
check := func(height []uint64, expected uint64, callback func()) {
for i := 0; i < len(height); i++ {
for j := 0; j < len(servers); j++ {
h := servers[j].backend.Blockchain().GetHeaderByNumber(height[i])
hash, number := h.Hash(), h.Number.Uint64()
td := rawdb.ReadTd(servers[j].db, hash, number)
// Sign the announcement if necessary.
announce := announceData{hash, number, td, 0, nil}
p := cpeers[j]
if p.announceType == announceTypeSigned {
announce.sign(servers[j].handler.server.privateKey)
}
p.sendAnnounce(announce)
}
}
if callback != nil {
callback()
}
verifyChainHeight(t, c.handler.fetcher, expected)
}
check([]uint64{1}, 1, func() { <-newHead }) // Sequential announcements
check([]uint64{4}, 4, func() { <-newHead }) // ULC-style light syncing, rollback untrusted headers
check([]uint64{10}, 10, func() { <-newHead }) // Sync the whole chain.
}
func TestInvalidAnnounces(t *testing.T) {
s, c, teardown := newClientServerEnv(t, 4, lpv3, nil, nil, 0, false, false)
defer teardown()
// Create connected peer pair.
c.handler.fetcher.noAnnounce = true // Ignore the first announce from peer which can trigger a resync.
peer, _, err := newTestPeerPair("peer", lpv3, s.handler, c.handler)
if err != nil {
t.Fatalf("Failed to create peer pair %v", err)
}
c.handler.fetcher.noAnnounce = false
done := make(chan *types.Header, 1)
c.handler.fetcher.newHeadHook = func(header *types.Header) { done <- header }
// Prepare announcement by latest header.
headerOne := s.backend.Blockchain().GetHeaderByNumber(1)
hash, number := headerOne.Hash(), headerOne.Number.Uint64()
td := big.NewInt(200) // bad td
// Sign the announcement if necessary.
announce := announceData{hash, number, td, 0, nil}
if peer.cpeer.announceType == announceTypeSigned {
announce.sign(s.handler.server.privateKey)
}
peer.cpeer.sendAnnounce(announce)
<-done // Wait syncing
// Ensure the bad peer is evicited
if c.handler.backend.peers.len() != 0 {
t.Fatalf("Failed to evict invalid peer")
}
}
...@@ -222,13 +222,13 @@ func testOdr(t *testing.T, protocol int, expFail uint64, checkCached bool, fn od ...@@ -222,13 +222,13 @@ func testOdr(t *testing.T, protocol int, expFail uint64, checkCached bool, fn od
// expect retrievals to fail (except genesis block) without a les peer // expect retrievals to fail (except genesis block) without a les peer
client.handler.backend.peers.lock.Lock() client.handler.backend.peers.lock.Lock()
client.peer.speer.hasBlock = func(common.Hash, uint64, bool) bool { return false } client.peer.speer.hasBlockHook = func(common.Hash, uint64, bool) bool { return false }
client.handler.backend.peers.lock.Unlock() client.handler.backend.peers.lock.Unlock()
test(expFail) test(expFail)
// expect all retrievals to pass // expect all retrievals to pass
client.handler.backend.peers.lock.Lock() client.handler.backend.peers.lock.Lock()
client.peer.speer.hasBlock = func(common.Hash, uint64, bool) bool { return true } client.peer.speer.hasBlockHook = func(common.Hash, uint64, bool) bool { return true }
client.handler.backend.peers.lock.Unlock() client.handler.backend.peers.lock.Unlock()
test(5) test(5)
......
...@@ -36,7 +36,6 @@ import ( ...@@ -36,7 +36,6 @@ import (
"github.com/ethereum/go-ethereum/les/utils" "github.com/ethereum/go-ethereum/les/utils"
"github.com/ethereum/go-ethereum/light" "github.com/ethereum/go-ethereum/light"
"github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
) )
...@@ -115,11 +114,6 @@ func (m keyValueMap) get(key string, val interface{}) error { ...@@ -115,11 +114,6 @@ func (m keyValueMap) get(key string, val interface{}) error {
return rlp.DecodeBytes(enc, val) return rlp.DecodeBytes(enc, val)
} }
// peerIdToString converts enode.ID to a string form
func peerIdToString(id enode.ID) string {
return fmt.Sprintf("%x", id.Bytes())
}
// peerCommons contains fields needed by both server peer and client peer. // peerCommons contains fields needed by both server peer and client peer.
type peerCommons struct { type peerCommons struct {
*p2p.Peer *p2p.Peer
...@@ -343,12 +337,12 @@ type serverPeer struct { ...@@ -343,12 +337,12 @@ type serverPeer struct {
sentReqs map[uint64]sentReqEntry sentReqs map[uint64]sentReqEntry
// Statistics // Statistics
errCount int // Counter the invalid responses server has replied errCount utils.LinearExpiredValue // Counter the invalid responses server has replied
updateCount uint64 updateCount uint64
updateTime mclock.AbsTime updateTime mclock.AbsTime
// Callbacks // Test callback hooks
hasBlock func(common.Hash, uint64, bool) bool // Used to determine whether the server has the specified block. hasBlockHook func(common.Hash, uint64, bool) bool // Used to determine whether the server has the specified block.
} }
func newServerPeer(version int, network uint64, trusted bool, p *p2p.Peer, rw p2p.MsgReadWriter) *serverPeer { func newServerPeer(version int, network uint64, trusted bool, p *p2p.Peer, rw p2p.MsgReadWriter) *serverPeer {
...@@ -356,13 +350,14 @@ func newServerPeer(version int, network uint64, trusted bool, p *p2p.Peer, rw p2 ...@@ -356,13 +350,14 @@ func newServerPeer(version int, network uint64, trusted bool, p *p2p.Peer, rw p2
peerCommons: peerCommons{ peerCommons: peerCommons{
Peer: p, Peer: p,
rw: rw, rw: rw,
id: peerIdToString(p.ID()), id: p.ID().String(),
version: version, version: version,
network: network, network: network,
sendQueue: utils.NewExecQueue(100), sendQueue: utils.NewExecQueue(100),
closeCh: make(chan struct{}), closeCh: make(chan struct{}),
}, },
trusted: trusted, trusted: trusted,
errCount: utils.LinearExpiredValue{Rate: mclock.AbsTime(time.Hour)},
} }
} }
...@@ -524,7 +519,11 @@ func (p *serverPeer) getTxRelayCost(amount, size int) uint64 { ...@@ -524,7 +519,11 @@ func (p *serverPeer) getTxRelayCost(amount, size int) uint64 {
// HasBlock checks if the peer has a given block // HasBlock checks if the peer has a given block
func (p *serverPeer) HasBlock(hash common.Hash, number uint64, hasState bool) bool { func (p *serverPeer) HasBlock(hash common.Hash, number uint64, hasState bool) bool {
p.lock.RLock() p.lock.RLock()
defer p.lock.RUnlock()
if p.hasBlockHook != nil {
return p.hasBlockHook(hash, number, hasState)
}
head := p.headInfo.Number head := p.headInfo.Number
var since, recent uint64 var since, recent uint64
if hasState { if hasState {
...@@ -534,10 +533,7 @@ func (p *serverPeer) HasBlock(hash common.Hash, number uint64, hasState bool) bo ...@@ -534,10 +533,7 @@ func (p *serverPeer) HasBlock(hash common.Hash, number uint64, hasState bool) bo
since = p.chainSince since = p.chainSince
recent = p.chainRecent recent = p.chainRecent
} }
hasBlock := p.hasBlock return head >= number && number >= since && (recent == 0 || number+recent+4 > head)
p.lock.RUnlock()
return head >= number && number >= since && (recent == 0 || number+recent+4 > head) && hasBlock != nil && hasBlock(hash, number, hasState)
} }
// updateFlowControl updates the flow control parameters belonging to the server // updateFlowControl updates the flow control parameters belonging to the server
...@@ -562,6 +558,15 @@ func (p *serverPeer) updateFlowControl(update keyValueMap) { ...@@ -562,6 +558,15 @@ func (p *serverPeer) updateFlowControl(update keyValueMap) {
} }
} }
// updateHead updates the head information based on the announcement from
// the peer.
func (p *serverPeer) updateHead(hash common.Hash, number uint64, td *big.Int) {
p.lock.Lock()
defer p.lock.Unlock()
p.headInfo = blockInfo{Hash: hash, Number: number, Td: td}
}
// Handshake executes the les protocol handshake, negotiating version number, // Handshake executes the les protocol handshake, negotiating version number,
// network IDs, difficulties, head and genesis blocks. // network IDs, difficulties, head and genesis blocks.
func (p *serverPeer) Handshake(td *big.Int, head common.Hash, headNum uint64, genesis common.Hash, server *LesServer) error { func (p *serverPeer) Handshake(td *big.Int, head common.Hash, headNum uint64, genesis common.Hash, server *LesServer) error {
...@@ -712,9 +717,13 @@ type clientPeer struct { ...@@ -712,9 +717,13 @@ type clientPeer struct {
// responseLock ensures that responses are queued in the same order as // responseLock ensures that responses are queued in the same order as
// RequestProcessed is called // RequestProcessed is called
responseLock sync.Mutex responseLock sync.Mutex
server bool
invalidCount uint32 // Counter the invalid request the client peer has made.
responseCount uint64 // Counter to generate an unique id for request processing. responseCount uint64 // Counter to generate an unique id for request processing.
// invalidLock is used for protecting invalidCount.
invalidLock sync.RWMutex
invalidCount utils.LinearExpiredValue // Counter the invalid request the client peer has made.
server bool
errCh chan error errCh chan error
fcClient *flowcontrol.ClientNode // Server side mirror token bucket. fcClient *flowcontrol.ClientNode // Server side mirror token bucket.
} }
...@@ -724,12 +733,13 @@ func newClientPeer(version int, network uint64, p *p2p.Peer, rw p2p.MsgReadWrite ...@@ -724,12 +733,13 @@ func newClientPeer(version int, network uint64, p *p2p.Peer, rw p2p.MsgReadWrite
peerCommons: peerCommons{ peerCommons: peerCommons{
Peer: p, Peer: p,
rw: rw, rw: rw,
id: peerIdToString(p.ID()), id: p.ID().String(),
version: version, version: version,
network: network, network: network,
sendQueue: utils.NewExecQueue(100), sendQueue: utils.NewExecQueue(100),
closeCh: make(chan struct{}), closeCh: make(chan struct{}),
}, },
invalidCount: utils.LinearExpiredValue{Rate: mclock.AbsTime(time.Hour)},
errCh: make(chan error, 1), errCh: make(chan error, 1),
} }
} }
...@@ -970,6 +980,18 @@ func (p *clientPeer) Handshake(td *big.Int, head common.Hash, headNum uint64, ge ...@@ -970,6 +980,18 @@ func (p *clientPeer) Handshake(td *big.Int, head common.Hash, headNum uint64, ge
}) })
} }
func (p *clientPeer) bumpInvalid() {
p.invalidLock.Lock()
p.invalidCount.Add(1, mclock.Now())
p.invalidLock.Unlock()
}
func (p *clientPeer) getInvalid() uint64 {
p.invalidLock.RLock()
defer p.invalidLock.RUnlock()
return p.invalidCount.Value(mclock.Now())
}
// serverPeerSubscriber is an interface to notify services about added or // serverPeerSubscriber is an interface to notify services about added or
// removed server peers // removed server peers
type serverPeerSubscriber interface { type serverPeerSubscriber interface {
......
...@@ -116,7 +116,7 @@ func NewLesServer(e *eth.Ethereum, config *eth.Config) (*LesServer, error) { ...@@ -116,7 +116,7 @@ func NewLesServer(e *eth.Ethereum, config *eth.Config) (*LesServer, error) {
srv.maxCapacity = totalRecharge srv.maxCapacity = totalRecharge
} }
srv.fcManager.SetCapacityLimits(srv.freeCapacity, srv.maxCapacity, srv.freeCapacity*2) srv.fcManager.SetCapacityLimits(srv.freeCapacity, srv.maxCapacity, srv.freeCapacity*2)
srv.clientPool = newClientPool(srv.chainDb, srv.freeCapacity, mclock.System{}, func(id enode.ID) { go srv.peers.unregister(peerIdToString(id)) }) srv.clientPool = newClientPool(srv.chainDb, srv.freeCapacity, mclock.System{}, func(id enode.ID) { go srv.peers.unregister(id.String()) })
srv.clientPool.setDefaultFactors(priceFactors{0, 1, 1}, priceFactors{0, 1, 1}) srv.clientPool.setDefaultFactors(priceFactors{0, 1, 1}, priceFactors{0, 1, 1})
checkpoint := srv.latestLocalCheckpoint() checkpoint := srv.latestLocalCheckpoint()
...@@ -153,7 +153,7 @@ func (s *LesServer) APIs() []rpc.API { ...@@ -153,7 +153,7 @@ func (s *LesServer) APIs() []rpc.API {
func (s *LesServer) Protocols() []p2p.Protocol { func (s *LesServer) Protocols() []p2p.Protocol {
ps := s.makeProtocols(ServerProtocolVersions, s.handler.runPeer, func(id enode.ID) interface{} { ps := s.makeProtocols(ServerProtocolVersions, s.handler.runPeer, func(id enode.ID) interface{} {
if p := s.peers.peer(peerIdToString(id)); p != nil { if p := s.peers.peer(id.String()); p != nil {
return p.Info() return p.Info()
} }
return nil return nil
......
...@@ -322,7 +322,7 @@ func (h *serverHandler) handleMsg(p *clientPeer, wg *sync.WaitGroup) error { ...@@ -322,7 +322,7 @@ func (h *serverHandler) handleMsg(p *clientPeer, wg *sync.WaitGroup) error {
origin = h.blockchain.GetHeaderByNumber(query.Origin.Number) origin = h.blockchain.GetHeaderByNumber(query.Origin.Number)
} }
if origin == nil { if origin == nil {
atomic.AddUint32(&p.invalidCount, 1) p.bumpInvalid()
break break
} }
headers = append(headers, origin) headers = append(headers, origin)
...@@ -419,7 +419,7 @@ func (h *serverHandler) handleMsg(p *clientPeer, wg *sync.WaitGroup) error { ...@@ -419,7 +419,7 @@ func (h *serverHandler) handleMsg(p *clientPeer, wg *sync.WaitGroup) error {
} }
body := h.blockchain.GetBodyRLP(hash) body := h.blockchain.GetBodyRLP(hash)
if body == nil { if body == nil {
atomic.AddUint32(&p.invalidCount, 1) p.bumpInvalid()
continue continue
} }
bodies = append(bodies, body) bodies = append(bodies, body)
...@@ -467,7 +467,7 @@ func (h *serverHandler) handleMsg(p *clientPeer, wg *sync.WaitGroup) error { ...@@ -467,7 +467,7 @@ func (h *serverHandler) handleMsg(p *clientPeer, wg *sync.WaitGroup) error {
header := h.blockchain.GetHeaderByHash(request.BHash) header := h.blockchain.GetHeaderByHash(request.BHash)
if header == nil { if header == nil {
p.Log().Warn("Failed to retrieve associate header for code", "hash", request.BHash) p.Log().Warn("Failed to retrieve associate header for code", "hash", request.BHash)
atomic.AddUint32(&p.invalidCount, 1) p.bumpInvalid()
continue continue
} }
// Refuse to search stale state data in the database since looking for // Refuse to search stale state data in the database since looking for
...@@ -475,7 +475,7 @@ func (h *serverHandler) handleMsg(p *clientPeer, wg *sync.WaitGroup) error { ...@@ -475,7 +475,7 @@ func (h *serverHandler) handleMsg(p *clientPeer, wg *sync.WaitGroup) error {
local := h.blockchain.CurrentHeader().Number.Uint64() local := h.blockchain.CurrentHeader().Number.Uint64()
if !h.server.archiveMode && header.Number.Uint64()+core.TriesInMemory <= local { if !h.server.archiveMode && header.Number.Uint64()+core.TriesInMemory <= local {
p.Log().Debug("Reject stale code request", "number", header.Number.Uint64(), "head", local) p.Log().Debug("Reject stale code request", "number", header.Number.Uint64(), "head", local)
atomic.AddUint32(&p.invalidCount, 1) p.bumpInvalid()
continue continue
} }
triedb := h.blockchain.StateCache().TrieDB() triedb := h.blockchain.StateCache().TrieDB()
...@@ -483,7 +483,7 @@ func (h *serverHandler) handleMsg(p *clientPeer, wg *sync.WaitGroup) error { ...@@ -483,7 +483,7 @@ func (h *serverHandler) handleMsg(p *clientPeer, wg *sync.WaitGroup) error {
account, err := h.getAccount(triedb, header.Root, common.BytesToHash(request.AccKey)) account, err := h.getAccount(triedb, header.Root, common.BytesToHash(request.AccKey))
if err != nil { if err != nil {
p.Log().Warn("Failed to retrieve account for code", "block", header.Number, "hash", header.Hash(), "account", common.BytesToHash(request.AccKey), "err", err) p.Log().Warn("Failed to retrieve account for code", "block", header.Number, "hash", header.Hash(), "account", common.BytesToHash(request.AccKey), "err", err)
atomic.AddUint32(&p.invalidCount, 1) p.bumpInvalid()
continue continue
} }
code, err := triedb.Node(common.BytesToHash(account.CodeHash)) code, err := triedb.Node(common.BytesToHash(account.CodeHash))
...@@ -542,7 +542,7 @@ func (h *serverHandler) handleMsg(p *clientPeer, wg *sync.WaitGroup) error { ...@@ -542,7 +542,7 @@ func (h *serverHandler) handleMsg(p *clientPeer, wg *sync.WaitGroup) error {
results := h.blockchain.GetReceiptsByHash(hash) results := h.blockchain.GetReceiptsByHash(hash)
if results == nil { if results == nil {
if header := h.blockchain.GetHeaderByHash(hash); header == nil || header.ReceiptHash != types.EmptyRootHash { if header := h.blockchain.GetHeaderByHash(hash); header == nil || header.ReceiptHash != types.EmptyRootHash {
atomic.AddUint32(&p.invalidCount, 1) p.bumpInvalid()
continue continue
} }
} }
...@@ -605,7 +605,7 @@ func (h *serverHandler) handleMsg(p *clientPeer, wg *sync.WaitGroup) error { ...@@ -605,7 +605,7 @@ func (h *serverHandler) handleMsg(p *clientPeer, wg *sync.WaitGroup) error {
if header = h.blockchain.GetHeaderByHash(request.BHash); header == nil { if header = h.blockchain.GetHeaderByHash(request.BHash); header == nil {
p.Log().Warn("Failed to retrieve header for proof", "hash", request.BHash) p.Log().Warn("Failed to retrieve header for proof", "hash", request.BHash)
atomic.AddUint32(&p.invalidCount, 1) p.bumpInvalid()
continue continue
} }
// Refuse to search stale state data in the database since looking for // Refuse to search stale state data in the database since looking for
...@@ -613,14 +613,14 @@ func (h *serverHandler) handleMsg(p *clientPeer, wg *sync.WaitGroup) error { ...@@ -613,14 +613,14 @@ func (h *serverHandler) handleMsg(p *clientPeer, wg *sync.WaitGroup) error {
local := h.blockchain.CurrentHeader().Number.Uint64() local := h.blockchain.CurrentHeader().Number.Uint64()
if !h.server.archiveMode && header.Number.Uint64()+core.TriesInMemory <= local { if !h.server.archiveMode && header.Number.Uint64()+core.TriesInMemory <= local {
p.Log().Debug("Reject stale trie request", "number", header.Number.Uint64(), "head", local) p.Log().Debug("Reject stale trie request", "number", header.Number.Uint64(), "head", local)
atomic.AddUint32(&p.invalidCount, 1) p.bumpInvalid()
continue continue
} }
root = header.Root root = header.Root
} }
// If a header lookup failed (non existent), ignore subsequent requests for the same header // If a header lookup failed (non existent), ignore subsequent requests for the same header
if root == (common.Hash{}) { if root == (common.Hash{}) {
atomic.AddUint32(&p.invalidCount, 1) p.bumpInvalid()
continue continue
} }
// Open the account or storage trie for the request // Open the account or storage trie for the request
...@@ -639,7 +639,7 @@ func (h *serverHandler) handleMsg(p *clientPeer, wg *sync.WaitGroup) error { ...@@ -639,7 +639,7 @@ func (h *serverHandler) handleMsg(p *clientPeer, wg *sync.WaitGroup) error {
account, err := h.getAccount(statedb.TrieDB(), root, common.BytesToHash(request.AccKey)) account, err := h.getAccount(statedb.TrieDB(), root, common.BytesToHash(request.AccKey))
if err != nil { if err != nil {
p.Log().Warn("Failed to retrieve account for proof", "block", header.Number, "hash", header.Hash(), "account", common.BytesToHash(request.AccKey), "err", err) p.Log().Warn("Failed to retrieve account for proof", "block", header.Number, "hash", header.Hash(), "account", common.BytesToHash(request.AccKey), "err", err)
atomic.AddUint32(&p.invalidCount, 1) p.bumpInvalid()
continue continue
} }
trie, err = statedb.OpenStorageTrie(common.BytesToHash(request.AccKey), account.Root) trie, err = statedb.OpenStorageTrie(common.BytesToHash(request.AccKey), account.Root)
...@@ -833,9 +833,9 @@ func (h *serverHandler) handleMsg(p *clientPeer, wg *sync.WaitGroup) error { ...@@ -833,9 +833,9 @@ func (h *serverHandler) handleMsg(p *clientPeer, wg *sync.WaitGroup) error {
clientErrorMeter.Mark(1) clientErrorMeter.Mark(1)
return errResp(ErrInvalidMsgCode, "%v", msg.Code) return errResp(ErrInvalidMsgCode, "%v", msg.Code)
} }
// If the client has made too much invalid request(e.g. request a non-exist data), // If the client has made too much invalid request(e.g. request a non-existent data),
// reject them to prevent SPAM attack. // reject them to prevent SPAM attack.
if atomic.LoadUint32(&p.invalidCount) > maxRequestErrors { if p.getInvalid() > maxRequestErrors {
clientErrorMeter.Mark(1) clientErrorMeter.Mark(1)
return errTooManyInvalidRequest return errTooManyInvalidRequest
} }
......
...@@ -223,6 +223,7 @@ func newTestClientHandler(backend *backends.SimulatedBackend, odr *LesOdr, index ...@@ -223,6 +223,7 @@ func newTestClientHandler(backend *backends.SimulatedBackend, odr *LesOdr, index
if client.oracle != nil { if client.oracle != nil {
client.oracle.Start(backend) client.oracle.Start(backend)
} }
client.handler.start()
return client.handler return client.handler
} }
......
...@@ -124,6 +124,50 @@ func (e *ExpiredValue) SubExp(a ExpiredValue) { ...@@ -124,6 +124,50 @@ func (e *ExpiredValue) SubExp(a ExpiredValue) {
} }
} }
// LinearExpiredValue is very similar with the expiredValue which the value
// will continuously expired. But the different part is it's expired linearly.
type LinearExpiredValue struct {
Offset uint64 // The latest time offset
Val uint64 // The remaining value, can never be negative
Rate mclock.AbsTime `rlp:"-"` // Expiration rate(by nanosecond), will ignored by RLP
}
// value calculates the value at the given moment. This function always has the
// assumption that the given timestamp shouldn't less than the recorded one.
func (e LinearExpiredValue) Value(now mclock.AbsTime) uint64 {
offset := uint64(now / e.Rate)
if e.Offset < offset {
diff := offset - e.Offset
if e.Val >= diff {
e.Val -= diff
} else {
e.Val = 0
}
}
return e.Val
}
// add adds a signed value at the given moment. This function always has the
// assumption that the given timestamp shouldn't less than the recorded one.
func (e *LinearExpiredValue) Add(amount int64, now mclock.AbsTime) uint64 {
offset := uint64(now / e.Rate)
if e.Offset < offset {
diff := offset - e.Offset
if e.Val >= diff {
e.Val -= diff
} else {
e.Val = 0
}
e.Offset = offset
}
if amount < 0 && uint64(-amount) > e.Val {
e.Val = 0
} else {
e.Val = uint64(int64(e.Val) + amount)
}
return e.Val
}
// Expirer changes logOffset with a linear rate which can be changed during operation. // Expirer changes logOffset with a linear rate which can be changed during operation.
// It is not thread safe, if access by multiple goroutines is needed then it should be // It is not thread safe, if access by multiple goroutines is needed then it should be
// encapsulated into a locked structure. // encapsulated into a locked structure.
......
...@@ -18,6 +18,8 @@ package utils ...@@ -18,6 +18,8 @@ package utils
import ( import (
"testing" "testing"
"github.com/ethereum/go-ethereum/common/mclock"
) )
func TestValueExpiration(t *testing.T) { func TestValueExpiration(t *testing.T) {
...@@ -116,3 +118,78 @@ func TestExpiredValueSubtraction(t *testing.T) { ...@@ -116,3 +118,78 @@ func TestExpiredValueSubtraction(t *testing.T) {
} }
} }
} }
func TestLinearExpiredValue(t *testing.T) {
var cases = []struct {
value LinearExpiredValue
now mclock.AbsTime
expect uint64
}{
{LinearExpiredValue{
Offset: 0,
Val: 0,
Rate: mclock.AbsTime(1),
}, 0, 0},
{LinearExpiredValue{
Offset: 1,
Val: 1,
Rate: mclock.AbsTime(1),
}, 0, 1},
{LinearExpiredValue{
Offset: 1,
Val: 1,
Rate: mclock.AbsTime(1),
}, mclock.AbsTime(2), 0},
{LinearExpiredValue{
Offset: 1,
Val: 1,
Rate: mclock.AbsTime(1),
}, mclock.AbsTime(3), 0},
}
for _, c := range cases {
if value := c.value.Value(c.now); value != c.expect {
t.Fatalf("Value mismatch, want=%d, got=%d", c.expect, value)
}
}
}
func TestLinearExpiredAddition(t *testing.T) {
var cases = []struct {
value LinearExpiredValue
amount int64
now mclock.AbsTime
expect uint64
}{
{LinearExpiredValue{
Offset: 0,
Val: 0,
Rate: mclock.AbsTime(1),
}, -1, 0, 0},
{LinearExpiredValue{
Offset: 1,
Val: 1,
Rate: mclock.AbsTime(1),
}, -1, 0, 0},
{LinearExpiredValue{
Offset: 1,
Val: 2,
Rate: mclock.AbsTime(1),
}, -1, mclock.AbsTime(2), 0},
{LinearExpiredValue{
Offset: 1,
Val: 2,
Rate: mclock.AbsTime(1),
}, -2, mclock.AbsTime(2), 0},
}
for _, c := range cases {
if value := c.value.Add(c.amount, c.now); value != c.expect {
t.Fatalf("Value mismatch, want=%d, got=%d", c.expect, value)
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment