diff --git a/core/block_validator.go b/core/block_validator.go index 6eac3d5133..42372b834b 100644 --- a/core/block_validator.go +++ b/core/block_validator.go @@ -82,7 +82,7 @@ func (v *BlockValidator) ValidateBlock(block *types.Block) error { header := 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 } // 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]) } - 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)) } } @@ -197,13 +197,13 @@ func (v *BlockValidator) ValidateHeader(header, parent *types.Header, checkPow b if v.bc.HasHeader(header.Hash()) { 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. // // 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 { 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 } - 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 { 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 // the difficulty that a new block should have when created at time // 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 // algorithm: // diff = (parent_diff + @@ -307,6 +359,76 @@ func CalcDifficulty(config *params.ChainConfig, time, parentTime uint64, parentN 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. // The result may be modified by the caller. // This is miner strategy, not consensus protocol. diff --git a/core/chain_makers.go b/core/chain_makers.go index caf5d80697..63dbce3d3f 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -163,7 +163,7 @@ func (b *BlockGen) OffsetTime(seconds int64) { if b.header.Time.Cmp(b.parent.Header().Time) <= 0 { 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 @@ -224,7 +224,7 @@ func makeHeader(config *params.ChainConfig, parent *types.Block, state *state.St Root: state.IntermediateRoot(config.IsEIP158(parent.Number())), ParentHash: parent.Hash(), 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), GasUsed: new(big.Int), Number: new(big.Int).Add(parent.Number(), common.Big1), diff --git a/core/headerchain.go b/core/headerchain.go index 765eb3ac83..c89624971f 100644 --- a/core/headerchain.go +++ b/core/headerchain.go @@ -27,6 +27,7 @@ import ( "sync/atomic" "time" + "github.com/hashicorp/golang-lru" "github.com/ubiq/go-ubiq/common" "github.com/ubiq/go-ubiq/core/types" "github.com/ubiq/go-ubiq/ethdb" @@ -34,7 +35,6 @@ import ( "github.com/ubiq/go-ubiq/logger/glog" "github.com/ubiq/go-ubiq/params" "github.com/ubiq/go-ubiq/pow" - "github.com/hashicorp/golang-lru" ) const ( @@ -517,5 +517,5 @@ func (v *headerValidator) ValidateHeader(header, parent *types.Header, checkPow if v.hc.HasHeader(header.Hash()) { return nil } - return ValidateHeader(v.config, v.Pow, header, parent, checkPow, false) + return ValidateHeaderHeaderChain(v.config, v.Pow, header, parent, checkPow, false, v.hc) } diff --git a/eth/api.go b/eth/api.go index 69421e9c7d..b4e8fbb049 100644 --- a/eth/api.go +++ b/eth/api.go @@ -404,7 +404,7 @@ func (api *PrivateDebugAPI) traceBlock(block *types.Block, logConfig *vm.LogConf 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 } statedb, err := blockchain.StateAt(blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1).Root()) diff --git a/eth/handler.go b/eth/handler.go index 2f1e66e305..32d8b60698 100644 --- a/eth/handler.go +++ b/eth/handler.go @@ -161,7 +161,7 @@ func NewProtocolManager(config *params.ChainConfig, fastSync bool, networkId int manager.removePeer) 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 { return blockchain.CurrentBlock().NumberU64() diff --git a/miner/worker.go b/miner/worker.go index 1b4d6ea263..3dc2e9f544 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -301,7 +301,7 @@ func (self *worker) wait() { } 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) continue } @@ -486,7 +486,7 @@ func (self *worker) commitNewWork() { header := &types.Header{ ParentHash: parent.Hash(), 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), GasUsed: new(big.Int), Coinbase: self.coinbase,