Refactor ValidateHeader and CalcDifficulty to accept a *BlockChain

parameter
This commit is contained in:
Julian Yap 2016-12-02 23:03:09 -10:00
parent 7e15bf9013
commit 399ce66e5b
6 changed files with 136 additions and 14 deletions

View file

@ -82,7 +82,7 @@ func (v *BlockValidator) ValidateBlock(block *types.Block) error {
header := block.Header() header := block.Header()
// validate the block header // validate the block header
if err := ValidateHeader(v.config, v.Pow, header, parent.Header(), false, false); err != nil { if err := ValidateHeader(v.config, v.Pow, header, parent.Header(), false, false, v.bc); err != nil {
return err return err
} }
// verify the uncles are correctly rewarded // verify the uncles are correctly rewarded
@ -177,7 +177,7 @@ func (v *BlockValidator) VerifyUncles(block, parent *types.Block) error {
return UncleError("uncle[%d](%x)'s parent is not ancestor (%x)", i, hash[:4], uncle.ParentHash[0:4]) return UncleError("uncle[%d](%x)'s parent is not ancestor (%x)", i, hash[:4], uncle.ParentHash[0:4])
} }
if err := ValidateHeader(v.config, v.Pow, uncle, ancestors[uncle.ParentHash].Header(), true, true); err != nil { if err := ValidateHeader(v.config, v.Pow, uncle, ancestors[uncle.ParentHash].Header(), true, true, v.bc); err != nil {
return ValidationError(fmt.Sprintf("uncle[%d](%x) header invalid: %v", i, hash[:4], err)) return ValidationError(fmt.Sprintf("uncle[%d](%x) header invalid: %v", i, hash[:4], err))
} }
} }
@ -197,13 +197,13 @@ func (v *BlockValidator) ValidateHeader(header, parent *types.Header, checkPow b
if v.bc.HasHeader(header.Hash()) { if v.bc.HasHeader(header.Hash()) {
return nil return nil
} }
return ValidateHeader(v.config, v.Pow, header, parent, checkPow, false) return ValidateHeader(v.config, v.Pow, header, parent, checkPow, false, v.bc)
} }
// Validates a header. Returns an error if the header is invalid. // Validates a header. Returns an error if the header is invalid.
// //
// See YP section 4.3.4. "Block Header Validity" // See YP section 4.3.4. "Block Header Validity"
func ValidateHeader(config *params.ChainConfig, pow pow.PoW, header *types.Header, parent *types.Header, checkPow, uncle bool) error { func ValidateHeader(config *params.ChainConfig, pow pow.PoW, header *types.Header, parent *types.Header, checkPow, uncle bool, bc *BlockChain) error {
if big.NewInt(int64(len(header.Extra))).Cmp(params.MaximumExtraDataSize) == 1 { if big.NewInt(int64(len(header.Extra))).Cmp(params.MaximumExtraDataSize) == 1 {
return fmt.Errorf("Header extra data too long (%d)", len(header.Extra)) return fmt.Errorf("Header extra data too long (%d)", len(header.Extra))
} }
@ -221,7 +221,59 @@ func ValidateHeader(config *params.ChainConfig, pow pow.PoW, header *types.Heade
return BlockEqualTSErr return BlockEqualTSErr
} }
expd := CalcDifficulty(config, header.Time.Uint64(), parent.Time.Uint64(), parent.Number, parent.Difficulty) expd := CalcDifficulty(config, header.Time.Uint64(), parent.Time.Uint64(), parent.Number, parent.Difficulty, bc)
if expd.Cmp(header.Difficulty) != 0 {
return fmt.Errorf("Difficulty check failed for header (remote: %v local: %v)", header.Difficulty, expd)
}
a := new(big.Int).Set(parent.GasLimit)
a = a.Sub(a, header.GasLimit)
a.Abs(a)
b := new(big.Int).Set(parent.GasLimit)
b = b.Div(b, params.GasLimitBoundDivisor)
if !(a.Cmp(b) < 0) || (header.GasLimit.Cmp(params.MinGasLimit) == -1) {
return fmt.Errorf("GasLimit check failed for header (remote: %v local_max: %v)", header.GasLimit, b)
}
num := new(big.Int).Set(parent.Number)
num.Sub(header.Number, num)
if num.Cmp(big.NewInt(1)) != 0 {
return BlockNumberErr
}
if checkPow {
// Verify the nonce of the header. Return an error if it's not valid
if !pow.Verify(types.NewBlockWithHeader(header)) {
return &BlockNonceErr{header.Number, header.Hash(), header.Nonce.Uint64()}
}
}
if !uncle && config.EIP150Block != nil && config.EIP150Block.Cmp(header.Number) == 0 {
if config.EIP150Hash != (common.Hash{}) && config.EIP150Hash != header.Hash() {
return ValidationError("Homestead gas reprice fork hash mismatch: have 0x%x, want 0x%x", header.Hash(), config.EIP150Hash)
}
}
return nil
}
func ValidateHeaderHeaderChain(config *params.ChainConfig, pow pow.PoW, header *types.Header, parent *types.Header, checkPow, uncle bool, hc *HeaderChain) error {
if big.NewInt(int64(len(header.Extra))).Cmp(params.MaximumExtraDataSize) == 1 {
return fmt.Errorf("Header extra data too long (%d)", len(header.Extra))
}
if uncle {
if header.Time.Cmp(common.MaxBig) == 1 {
return BlockTSTooBigErr
}
} else {
if header.Time.Cmp(big.NewInt(time.Now().Unix())) == 1 {
return BlockFutureErr
}
}
if header.Time.Cmp(parent.Time) != 1 {
return BlockEqualTSErr
}
expd := CalcDifficultyHeaderChain(config, header.Time.Uint64(), parent.Time.Uint64(), parent.Number, parent.Difficulty, hc)
if expd.Cmp(header.Difficulty) != 0 { if expd.Cmp(header.Difficulty) != 0 {
return fmt.Errorf("Difficulty check failed for header (remote: %v local: %v)", header.Difficulty, expd) return fmt.Errorf("Difficulty check failed for header (remote: %v local: %v)", header.Difficulty, expd)
} }
@ -258,7 +310,7 @@ func ValidateHeader(config *params.ChainConfig, pow pow.PoW, header *types.Heade
// 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 CalcDifficulty(config *params.ChainConfig, time, parentTime uint64, parentNumber, parentDiff *big.Int) *big.Int { func CalcDifficulty(config *params.ChainConfig, time, parentTime uint64, parentNumber, parentDiff *big.Int, bc *BlockChain) *big.Int {
// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-2.mediawiki // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-2.mediawiki
// algorithm: // algorithm:
// diff = (parent_diff + // diff = (parent_diff +
@ -307,6 +359,76 @@ func CalcDifficulty(config *params.ChainConfig, time, parentTime uint64, parentN
return x return x
} }
func CalcDifficultyHeaderChain(config *params.ChainConfig, time, parentTime uint64, parentNumber, parentDiff *big.Int, hc *HeaderChain) *big.Int {
bigTime := new(big.Int).SetUint64(time)
bigParentTime := new(big.Int).SetUint64(parentTime)
x := new(big.Int)
y := new(big.Int)
x.Sub(bigTime, bigParentTime)
x.Div(x, big10)
x.Sub(common.Big1, x)
if x.Cmp(bigMinus99) < 0 {
x.Set(bigMinus99)
}
y.Div(parentDiff, params.DifficultyBoundDivisor)
x.Mul(y, x)
x.Add(parentDiff, x)
if x.Cmp(params.MinimumDifficulty) < 0 {
x.Set(params.MinimumDifficulty)
}
periodCount := new(big.Int).Add(parentNumber, common.Big1)
periodCount.Div(periodCount, ExpDiffPeriod)
if periodCount.Cmp(common.Big1) > 0 {
y.Sub(periodCount, common.Big2)
y.Exp(common.Big2, y, nil)
x.Add(x, y)
}
return x
}
func CalcDifficultyLegacy(config *params.ChainConfig, time, parentTime uint64, parentNumber, parentDiff *big.Int) *big.Int {
bigTime := new(big.Int).SetUint64(time)
bigParentTime := new(big.Int).SetUint64(parentTime)
x := new(big.Int)
y := new(big.Int)
x.Sub(bigTime, bigParentTime)
x.Div(x, big10)
x.Sub(common.Big1, x)
if x.Cmp(bigMinus99) < 0 {
x.Set(bigMinus99)
}
y.Div(parentDiff, params.DifficultyBoundDivisor)
x.Mul(y, x)
x.Add(parentDiff, x)
if x.Cmp(params.MinimumDifficulty) < 0 {
x.Set(params.MinimumDifficulty)
}
periodCount := new(big.Int).Add(parentNumber, common.Big1)
periodCount.Div(periodCount, ExpDiffPeriod)
if periodCount.Cmp(common.Big1) > 0 {
y.Sub(periodCount, common.Big2)
y.Exp(common.Big2, y, nil)
x.Add(x, y)
}
return x
}
// CalcGasLimit computes the gas limit of the next block after parent. // CalcGasLimit computes the gas limit of the next block after parent.
// The result may be modified by the caller. // The result may be modified by the caller.
// This is miner strategy, not consensus protocol. // This is miner strategy, not consensus protocol.

View file

@ -163,7 +163,7 @@ func (b *BlockGen) OffsetTime(seconds int64) {
if b.header.Time.Cmp(b.parent.Header().Time) <= 0 { if b.header.Time.Cmp(b.parent.Header().Time) <= 0 {
panic("block time out of range") panic("block time out of range")
} }
b.header.Difficulty = CalcDifficulty(MakeChainConfig(), b.header.Time.Uint64(), b.parent.Time().Uint64(), b.parent.Number(), b.parent.Difficulty()) b.header.Difficulty = CalcDifficultyLegacy(MakeChainConfig(), b.header.Time.Uint64(), b.parent.Time().Uint64(), b.parent.Number(), b.parent.Difficulty())
} }
// GenerateChain creates a chain of n blocks. The first block's // GenerateChain creates a chain of n blocks. The first block's
@ -224,7 +224,7 @@ func makeHeader(config *params.ChainConfig, parent *types.Block, state *state.St
Root: state.IntermediateRoot(config.IsEIP158(parent.Number())), Root: state.IntermediateRoot(config.IsEIP158(parent.Number())),
ParentHash: parent.Hash(), ParentHash: parent.Hash(),
Coinbase: parent.Coinbase(), Coinbase: parent.Coinbase(),
Difficulty: CalcDifficulty(MakeChainConfig(), time.Uint64(), new(big.Int).Sub(time, big.NewInt(10)).Uint64(), parent.Number(), parent.Difficulty()), Difficulty: CalcDifficultyLegacy(MakeChainConfig(), time.Uint64(), new(big.Int).Sub(time, big.NewInt(10)).Uint64(), parent.Number(), parent.Difficulty()),
GasLimit: CalcGasLimit(parent), GasLimit: CalcGasLimit(parent),
GasUsed: new(big.Int), GasUsed: new(big.Int),
Number: new(big.Int).Add(parent.Number(), common.Big1), Number: new(big.Int).Add(parent.Number(), common.Big1),

View file

@ -27,6 +27,7 @@ import (
"sync/atomic" "sync/atomic"
"time" "time"
"github.com/hashicorp/golang-lru"
"github.com/ubiq/go-ubiq/common" "github.com/ubiq/go-ubiq/common"
"github.com/ubiq/go-ubiq/core/types" "github.com/ubiq/go-ubiq/core/types"
"github.com/ubiq/go-ubiq/ethdb" "github.com/ubiq/go-ubiq/ethdb"
@ -34,7 +35,6 @@ import (
"github.com/ubiq/go-ubiq/logger/glog" "github.com/ubiq/go-ubiq/logger/glog"
"github.com/ubiq/go-ubiq/params" "github.com/ubiq/go-ubiq/params"
"github.com/ubiq/go-ubiq/pow" "github.com/ubiq/go-ubiq/pow"
"github.com/hashicorp/golang-lru"
) )
const ( const (
@ -517,5 +517,5 @@ func (v *headerValidator) ValidateHeader(header, parent *types.Header, checkPow
if v.hc.HasHeader(header.Hash()) { if v.hc.HasHeader(header.Hash()) {
return nil return nil
} }
return ValidateHeader(v.config, v.Pow, header, parent, checkPow, false) return ValidateHeaderHeaderChain(v.config, v.Pow, header, parent, checkPow, false, v.hc)
} }

View file

@ -404,7 +404,7 @@ func (api *PrivateDebugAPI) traceBlock(block *types.Block, logConfig *vm.LogConf
Tracer: structLogger, Tracer: structLogger,
} }
if err := core.ValidateHeader(api.config, blockchain.AuxValidator(), block.Header(), blockchain.GetHeader(block.ParentHash(), block.NumberU64()-1), true, false); err != nil { if err := core.ValidateHeader(api.config, blockchain.AuxValidator(), block.Header(), blockchain.GetHeader(block.ParentHash(), block.NumberU64()-1), true, false, blockchain); err != nil {
return false, structLogger.StructLogs(), err return false, structLogger.StructLogs(), err
} }
statedb, err := blockchain.StateAt(blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1).Root()) statedb, err := blockchain.StateAt(blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1).Root())

View file

@ -161,7 +161,7 @@ func NewProtocolManager(config *params.ChainConfig, fastSync bool, networkId int
manager.removePeer) manager.removePeer)
validator := func(block *types.Block, parent *types.Block) error { validator := func(block *types.Block, parent *types.Block) error {
return core.ValidateHeader(config, pow, block.Header(), parent.Header(), true, false) return core.ValidateHeader(config, pow, block.Header(), parent.Header(), true, false, blockchain)
} }
heighter := func() uint64 { heighter := func() uint64 {
return blockchain.CurrentBlock().NumberU64() return blockchain.CurrentBlock().NumberU64()

View file

@ -301,7 +301,7 @@ func (self *worker) wait() {
} }
auxValidator := self.eth.BlockChain().AuxValidator() auxValidator := self.eth.BlockChain().AuxValidator()
if err := core.ValidateHeader(self.config, auxValidator, block.Header(), parent.Header(), true, false); err != nil && err != core.BlockFutureErr { if err := core.ValidateHeader(self.config, auxValidator, block.Header(), parent.Header(), true, false, self.eth.BlockChain()); err != nil && err != core.BlockFutureErr {
glog.V(logger.Error).Infoln("Invalid header on mined block:", err) glog.V(logger.Error).Infoln("Invalid header on mined block:", err)
continue continue
} }
@ -486,7 +486,7 @@ func (self *worker) commitNewWork() {
header := &types.Header{ header := &types.Header{
ParentHash: parent.Hash(), ParentHash: parent.Hash(),
Number: num.Add(num, common.Big1), Number: num.Add(num, common.Big1),
Difficulty: core.CalcDifficulty(self.config, uint64(tstamp), parent.Time().Uint64(), parent.Number(), parent.Difficulty()), Difficulty: core.CalcDifficulty(self.config, uint64(tstamp), parent.Time().Uint64(), parent.Number(), parent.Difficulty(), self.chain),
GasLimit: core.CalcGasLimit(parent), GasLimit: core.CalcGasLimit(parent),
GasUsed: new(big.Int), GasUsed: new(big.Int),
Coinbase: self.coinbase, Coinbase: self.coinbase,