diff --git a/consensus/cuckoo/consensus.go b/consensus/cuckoo/consensus.go index aac4dcc888..933d94d8ff 100644 --- a/consensus/cuckoo/consensus.go +++ b/consensus/cuckoo/consensus.go @@ -19,7 +19,7 @@ package cuckoo import ( "encoding/binary" // "encoding/hex" - // "bytes" + "bytes" "errors" "fmt" "math/big" @@ -278,7 +278,8 @@ func (cuckoo *Cuckoo) verifyHeader(chain consensus.ChainHeaderReader, header, pa return fmt.Errorf("invalid gasUsed: have %d, gasLimit %d", header.GasUsed, header.GasLimit) } - validate := checkGasLimit(parent.GasUsed, parent.GasLimit, header.GasLimit) + //validate := checkGasLimit(parent.GasUsed, parent.GasLimit, header.GasLimit, chain.Config(), header.Number) + validate := gasCheck(chain, parent, header) if !validate { return fmt.Errorf("invalid gas limit trend: have %d, want %d used %d", header.GasLimit, parent.GasLimit, parent.GasUsed) } @@ -369,7 +370,10 @@ func CalcDifficulty(config *params.ChainConfig, time uint64, parent *types.Heade } // important add gas limit to consensus -func checkGasLimit(gasUsed, gasLimit, currentGasLimit uint64) bool { +func gasCheck(chain consensus.ChainHeaderReader, parent, header *types.Header) bool { + return checkGasLimit(parent.GasUsed, parent.GasLimit, header.GasLimit, chain.Config(), header.Number) +} +func checkGasLimit(gasUsed, gasLimit, currentGasLimit uint64, config *params.ChainConfig, num *big.Int) bool { contrib := (gasUsed + gasUsed/2) / params.GasLimitBoundDivisor decay := gasLimit/params.GasLimitBoundDivisor - 1 @@ -391,7 +395,10 @@ func checkGasLimit(gasUsed, gasLimit, currentGasLimit uint64) bool { } } - // TODO + if config != nil && config.IsMercury(num) { + return limit <= currentGasLimit + } + return limit == currentGasLimit } @@ -677,6 +684,15 @@ func (cuckoo *Cuckoo) VerifySeal(chain consensus.ChainHeaderReader, header *type return errInvalidDifficulty } + // Verify the calculated values against the ones provided in the header + if chain.Config().IsMercury(header.Number) { + log.Trace("Mix digest verify", "number", header.Number, "mix", header.Solution.Hash(), "header", header.MixDigest) + digest := header.Solution.Hash().Bytes() + if !bytes.Equal(header.MixDigest[:], digest) { + return errInvalidMixDigest + } + } + var ( result = header.Solution nonce uint64 = uint64(header.Nonce.Uint64()) diff --git a/consensus/cuckoo/consensus_test.go b/consensus/cuckoo/consensus_test.go index cdce422c8c..0c2ca9aa38 100644 --- a/consensus/cuckoo/consensus_test.go +++ b/consensus/cuckoo/consensus_test.go @@ -1,6 +1,7 @@ package cuckoo import ( + "bytes" "encoding/binary" "math/big" "math/rand" @@ -71,23 +72,33 @@ func randSlice(min, max uint32) []byte { } func TestGasLimitCheck(t *testing.T) { - if validate := checkGasLimit(0, 7992189, 8000000); !validate { + if validate := checkGasLimit(0, 7992189, 8000000, nil, nil); !validate { t.Fatalf("failed") } - if validate := checkGasLimit(7992189, 7992189, 8000000); !validate { + if validate := checkGasLimit(7992189, 7992189, 8000000, nil, nil); !validate { t.Fatalf("failed") } - if validate := checkGasLimit(0, 8000000, 7992189); validate { + if validate := checkGasLimit(0, 8000000, 7992189, nil, nil); validate { t.Fatalf("failed") } - if validate := checkGasLimit(7980000, 8000000, 8003878); !validate { + if validate := checkGasLimit(7980000, 8000000, 8003878, nil, nil); !validate { t.Fatalf("failed") } - if validate := checkGasLimit(7980000, 8000000, 8003879); validate { + if validate := checkGasLimit(7980000, 8000000, 8003879, nil, nil); validate { + t.Fatalf("failed") + } +} + +func TestMixDigest(t *testing.T) { + sol := &types.BlockSolution{36552774, 57703112, 69439022, 71282429, 117293109, 186862362, 271815245, 283711993, 301666096, 321610391, 345111432, 376920806, 469608283, 480776867, 488279241, 490162501, 496690146, 516943318, 548320772, 575755973, 584516346, 590932588, 594672482, 596315856, 611820183, 626014278, 668956240, 724642732, 737723908, 761956873, 772529653, 781328833, 782613854, 804339063, 838293864, 852149620, 875286932, 888462575, 905024013, 993378264, 1039286741, 1068794681} + mix := sol.Hash() + + t.Log("sol:", sol, "mix:", mix) + if !bytes.Equal(mix[:], sol.Hash().Bytes()) { t.Fatalf("failed") } } diff --git a/consensus/cuckoo/sealer.go b/consensus/cuckoo/sealer.go index 55d7378aca..c154cb291f 100644 --- a/consensus/cuckoo/sealer.go +++ b/consensus/cuckoo/sealer.go @@ -207,7 +207,7 @@ func (cuckoo *Cuckoo) remote() { // Verify the correctness of submitted result. header := block.Header() header.Nonce = nonce - //header.MixDigest = mixDigest + header.MixDigest = sol.Hash() header.Solution = sol if err := cuckoo.VerifySeal(nil, header); err != nil { log.Warn("Invalid proof-of-work submitted", "hash", hash, "err", err) diff --git a/core/cvm.go b/core/cvm.go index 7097b79a7f..938be6ffe1 100644 --- a/core/cvm.go +++ b/core/cvm.go @@ -24,6 +24,7 @@ import ( "github.com/CortexFoundation/CortexTheseus/core/types" "github.com/CortexFoundation/CortexTheseus/core/vm" "github.com/CortexFoundation/CortexTheseus/log" + "github.com/CortexFoundation/CortexTheseus/params" ) // ChainContext supports retrieving headers and consensus parameters from the @@ -48,9 +49,19 @@ func NewCVMBlockContext(header *types.Header, chain ChainContext, author *common } else { beneficiary = *author } + + log.Trace("Random", "number", header.Number, "mix", header.MixDigest, "parent", header.ParentHash, "mercury", params.MERCURY_MAINNET) + + // TODO auto invoked by PoS (IsMerge Version) op random, set when mining work commit, it will not happen by PoW if header.Difficulty.Cmp(common.Big0) == 0 { random = &header.MixDigest + } else { + // TODO fake random only for random op pass, used carefully, check block number + if params.MERCURY_MAINNET != nil && header.Number.Cmp(params.MERCURY_MAINNET) >= 0 { + random = &header.ParentHash + } } + return vm.BlockContext{ CanTransfer: CanTransfer, Transfer: Transfer, diff --git a/core/types/block.go b/core/types/block.go index 2af680bf2d..3a81fb0a56 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -63,6 +63,14 @@ func (n *BlockNonce) UnmarshalText(input []byte) error { return hexutil.UnmarshalFixedText("BlockNonce", input, n[:]) } +func (s *BlockSolution) Hash() common.Hash { + b, e := s.MarshalText() + if e != nil { + return common.EmptyHash + } + return common.BytesToHash(b) +} + func (s *BlockSolution) Uint32() []uint32 { return s[:] } func (s *BlockSolution) MarshalText() ([]byte, error) { diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 7058dfb69d..aae74741f7 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -89,6 +89,8 @@ func NewCVMInterpreter(cvm *CVM) *CVMInterpreter { // If jump table was not initialised we set the default one. var table *JumpTable switch { + case cvm.chainRules.IsMercury: + table = &mercuryInstructionSet case cvm.chainRules.IsMerge: table = &mergeInstructionSet case cvm.chainRules.IsNeo: diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go index 315392dd03..6ee5cfbe9f 100644 --- a/core/vm/jump_table.go +++ b/core/vm/jump_table.go @@ -53,6 +53,8 @@ var ( neoInstructionSet = newNeoInstructionSet() mergeInstructionSet = newMergeInstructionSet() + + mercuryInstructionSet = newMercuryInstructionSet() ) // JumpTable contains the CVM opcodes supported at a given fork. @@ -67,6 +69,13 @@ func validate(jt JumpTable) JumpTable { return jt } +func newMercuryInstructionSet() JumpTable { + instructionSet := newMergeInstructionSet() + enable5656(&instructionSet) // EIP-5656 (MCOPY opcode) + + return validate(instructionSet) +} + func newMergeInstructionSet() JumpTable { instructionSet := newNeoInstructionSet() instructionSet[RANDOM] = &operation{ @@ -74,7 +83,6 @@ func newMergeInstructionSet() JumpTable { gasCost: constGasFunc(GasQuickStep), validateStack: makeStackFunc(0, 1), } - enable5656(&instructionSet) // EIP-5656 (MCOPY opcode) return validate(instructionSet) } diff --git a/params/config.go b/params/config.go index 2bd1c85d48..fa09e0542e 100644 --- a/params/config.go +++ b/params/config.go @@ -131,6 +131,7 @@ var ( PetersburgBlock: big.NewInt(0), IstanbulBlock: big.NewInt(3_230_000), NeoBlock: big.NewInt(4_650_000), + MercuryBlock: MERCURY_MAINNET, TerminalTotalDifficulty: nil, TerminalTotalDifficultyPassed: false, Cuckoo: new(CuckooConfig), @@ -151,6 +152,7 @@ var ( PetersburgBlock: big.NewInt(0), IstanbulBlock: big.NewInt(0), NeoBlock: big.NewInt(5_000_000), + MercuryBlock: nil, TerminalTotalDifficulty: nil, TerminalTotalDifficultyPassed: false, Clique: &CliqueConfig{ @@ -174,6 +176,7 @@ var ( PetersburgBlock: big.NewInt(0), IstanbulBlock: big.NewInt(0), NeoBlock: big.NewInt(0), + MercuryBlock: nil, TerminalTotalDifficulty: nil, TerminalTotalDifficultyPassed: false, Cuckoo: new(CuckooConfig), @@ -194,6 +197,7 @@ var ( PetersburgBlock: big.NewInt(0), IstanbulBlock: big.NewInt(0), NeoBlock: big.NewInt(0), + MercuryBlock: nil, TerminalTotalDifficulty: nil, TerminalTotalDifficultyPassed: false, Clique: &CliqueConfig{ @@ -221,6 +225,7 @@ var ( PetersburgBlock: big.NewInt(0), IstanbulBlock: nil, NeoBlock: nil, + MercuryBlock: nil, TerminalTotalDifficulty: nil, TerminalTotalDifficultyPassed: false, Cuckoo: new(CuckooConfig), @@ -231,9 +236,9 @@ var ( // // This configuration is intentionally not using keyed fields to force anyone // adding flags to the config to also have to set these fields. - AllCliqueProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, nil, false, nil, &CliqueConfig{Period: 0, Epoch: 30000}} + AllCliqueProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, nil, nil, false, nil, &CliqueConfig{Period: 0, Epoch: 30000}} - TestChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, nil, false, new(CuckooConfig), nil} + TestChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, nil, nil, false, new(CuckooConfig), nil} TestRules = TestChainConfig.Rules(new(big.Int), false, 0) ) @@ -269,6 +274,7 @@ type ChainConfig struct { PetersburgBlock *big.Int `json:"petersburgBlock,omitempty"` // Petersburg switch block (nil = same as Constantinople) IstanbulBlock *big.Int `json:"istanbulBlock,omitempty"` // Istanbul switch block (nil = no fork, 0 = already on istanbul) NeoBlock *big.Int `json:"neoBlock,omitempty"` + MercuryBlock *big.Int `json:"mercuryBlock,omitempty"` // TerminalTotalDifficulty is the amount of total difficulty reached by // the network that triggers the consensus upgrade. TerminalTotalDifficulty *big.Int `json:"terminalTotalDifficulty,omitempty"` @@ -311,7 +317,7 @@ func (c *ChainConfig) String() string { default: engine = "unknown" } - return fmt.Sprintf("{ChainID: %v Homestead: %v DAO: %v DAOSupport: %v TangerineWhistle(EIP150): %v SpuriousDragon(EIP155): %v SpuriousDragon(EIP158): %v Byzantium: %v Constantinople: %v Petersburg: %v Istanbul: %v Neo:%v Engine: %v}", + return fmt.Sprintf("{ChainID: %v Homestead: %v DAO: %v DAOSupport: %v TangerineWhistle(EIP150): %v SpuriousDragon(EIP155): %v SpuriousDragon(EIP158): %v Byzantium: %v Constantinople: %v Petersburg: %v Istanbul: %v Neo:%v Mercury:%v Engine: %v}", c.ChainID, c.HomesteadBlock, c.DAOForkBlock, @@ -324,6 +330,7 @@ func (c *ChainConfig) String() string { c.PetersburgBlock, c.IstanbulBlock, c.NeoBlock, + c.MercuryBlock, engine, ) } @@ -378,6 +385,10 @@ func (c *ChainConfig) IsNeo(num *big.Int) bool { return isForked(c.NeoBlock, num) } +func (c *ChainConfig) IsMercury(num *big.Int) bool { + return isForked(c.MercuryBlock, num) +} + // IsTerminalPoWBlock returns whether the given block is the last block of PoW stage. func (c *ChainConfig) IsTerminalPoWBlock(parentTotalDiff *big.Int, totalDiff *big.Int) bool { if c.TerminalTotalDifficulty == nil { @@ -442,6 +453,7 @@ func (c *ChainConfig) CheckConfigForkOrder() error { {name: "petersburgBlock", block: c.PetersburgBlock}, {name: "istanbulBlock", block: c.IstanbulBlock}, {name: "neoBlock", block: c.NeoBlock}, + {name: "mercuryBlock", block: c.MercuryBlock}, } { if lastFork.name != "" { // Next one must be higher number @@ -505,6 +517,10 @@ func (c *ChainConfig) checkCompatible(newcfg *ChainConfig, head *big.Int) *Confi if isForkIncompatible(c.NeoBlock, newcfg.NeoBlock, head) { return newCompatError("Neo fork block", c.NeoBlock, newcfg.NeoBlock) } + + if isForkIncompatible(c.MercuryBlock, newcfg.MercuryBlock, head) { + return newCompatError("Mercury fork block", c.MercuryBlock, newcfg.MercuryBlock) + } return nil } @@ -622,10 +638,10 @@ func (err *ConfigCompatError) Error() string { // Rules is a one time interface meaning that it shouldn't be used in between transition // phases. type Rules struct { - ChainID *big.Int - IsHomestead, IsEIP150, IsEIP155, IsEIP158 bool - IsByzantium, IsConstantinople, IsPetersburg, IsIstanbul, IsNeo bool - IsMerge bool + ChainID *big.Int + IsHomestead, IsEIP150, IsEIP155, IsEIP158 bool + IsByzantium, IsConstantinople, IsPetersburg, IsIstanbul, IsNeo, IsMercury bool + IsMerge bool } // Rules ensures c's ChainID is not nil. @@ -634,7 +650,7 @@ func (c *ChainConfig) Rules(num *big.Int, isMerge bool, timestamp uint64) Rules if chainID == nil { chainID = new(big.Int) } - return Rules{ChainID: new(big.Int).Set(chainID), IsHomestead: c.IsHomestead(num), IsEIP150: c.IsEIP150(num), IsEIP155: c.IsEIP155(num), IsEIP158: c.IsEIP158(num), IsByzantium: c.IsByzantium(num), IsPetersburg: c.IsPetersburg(num), IsIstanbul: c.IsIstanbul(num), IsNeo: c.IsNeo(num), IsMerge: isMerge} + return Rules{ChainID: new(big.Int).Set(chainID), IsHomestead: c.IsHomestead(num), IsEIP150: c.IsEIP150(num), IsEIP155: c.IsEIP155(num), IsEIP158: c.IsEIP158(num), IsByzantium: c.IsByzantium(num), IsPetersburg: c.IsPetersburg(num), IsIstanbul: c.IsIstanbul(num), IsNeo: c.IsNeo(num), IsMercury: c.IsMercury(num), IsMerge: isMerge} } // Get Mature Block diff --git a/params/protocol_params.go b/params/protocol_params.go index 7cd9efcdf7..a4684a318b 100644 --- a/params/protocol_params.go +++ b/params/protocol_params.go @@ -190,6 +190,8 @@ var ( CTXC_F1 = big.NewInt(0).Mul(big.NewInt(20_486_540), big.NewInt(1000000000000000000)) CTXC_F2 = big.NewInt(0).Mul(big.NewInt(21_285_544), big.NewInt(1000000000000000000)) + + MERCURY_MAINNET *big.Int = nil //big.NewInt(0) ) const (