From b872961ec82ec88a7ac6ef331cfb3eb685ce2c00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Thu, 24 Aug 2017 12:26:06 +0300 Subject: [PATCH] consensus, core, tests: implement Metropolis EIP 649 --- consensus/ethash/consensus.go | 25 +++++++++++--- core/chain_makers.go | 2 +- core/vm/logger.go | 8 ++--- tests/gen_stlog.go | 61 ----------------------------------- tests/state_test_util.go | 56 ++++++-------------------------- tests/testdata | 2 +- tests/vm_test.go | 4 +++ tests/vm_test_util.go | 21 ++++++------ 8 files changed, 50 insertions(+), 129 deletions(-) delete mode 100644 tests/gen_stlog.go diff --git a/consensus/ethash/consensus.go b/consensus/ethash/consensus.go index 01d97a470..b71420445 100644 --- a/consensus/ethash/consensus.go +++ b/consensus/ethash/consensus.go @@ -36,8 +36,9 @@ import ( // Ethash proof-of-work protocol constants. var ( - blockReward *big.Int = big.NewInt(5e+18) // Block reward in wei for successfully mining a block - maxUncles = 2 // Maximum number of uncles allowed in a single block + frontierBlockReward *big.Int = big.NewInt(5e+18) // Block reward in wei for successfully mining a block + metropolisBlockReward *big.Int = big.NewInt(3e+18) // Block reward in wei for successfully mining a block upward from Metropolis + maxUncles = 2 // Maximum number of uncles allowed in a single block ) // Various error messages to mark blocks invalid. These should be private to @@ -306,6 +307,7 @@ var ( big9 = big.NewInt(9) big10 = big.NewInt(10) bigMinus99 = big.NewInt(-99) + big2999999 = big.NewInt(2999999) ) // calcDifficultyMetropolis is the difficulty adjustment algorithm. It returns @@ -346,8 +348,15 @@ func calcDifficultyMetropolis(time uint64, parent *types.Header) *big.Int { if x.Cmp(params.MinimumDifficulty) < 0 { x.Set(params.MinimumDifficulty) } + // calculate a fake block numer for the ice-age delay: + // https://github.com/ethereum/EIPs/pull/669 + // fake_block_number = min(0, block.number - 3_000_000 + fakeBlockNumber := new(big.Int) + if parent.Number.Cmp(big2999999) >= 0 { + fakeBlockNumber = fakeBlockNumber.Sub(parent.Number, big2999999) // Note, parent is 1 less than the actual block number + } // for the exponential factor - periodCount := new(big.Int).Add(parent.Number, big1) + periodCount := fakeBlockNumber periodCount.Div(periodCount, expDiffPeriod) // the exponential factor, commonly referred to as "the bomb" @@ -501,7 +510,7 @@ func (ethash *Ethash) Prepare(chain consensus.ChainReader, header *types.Header) // setting the final state and assembling the block. func (ethash *Ethash) Finalize(chain consensus.ChainReader, 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 - AccumulateRewards(state, header, uncles) + AccumulateRewards(chain.Config(), state, header, uncles) header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) // Header seems complete, assemble into a block and return @@ -518,7 +527,13 @@ var ( // reward. The total reward consists of the static block reward and rewards for // included uncles. The coinbase of each uncle block is also rewarded. // TODO (karalabe): Move the chain maker into this package and make this private! -func AccumulateRewards(state *state.StateDB, header *types.Header, uncles []*types.Header) { +func AccumulateRewards(config *params.ChainConfig, state *state.StateDB, header *types.Header, uncles []*types.Header) { + // Select the correct block reward based on chain progression + blockReward := frontierBlockReward + if config.IsMetropolis(header.Number) { + blockReward = metropolisBlockReward + } + // Accumulate the rewards for the miner and any included uncles reward := new(big.Int).Set(blockReward) r := new(big.Int) for _, uncle := range uncles { diff --git a/core/chain_makers.go b/core/chain_makers.go index cb5825d18..dd3e2fb19 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -179,7 +179,7 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, db ethdb.Dat if gen != nil { gen(i, b) } - ethash.AccumulateRewards(statedb, h, b.uncles) + ethash.AccumulateRewards(config, statedb, h, b.uncles) root, err := statedb.CommitTo(db, config.IsEIP158(h.Number)) if err != nil { panic(fmt.Sprintf("state write error: %v", err)) diff --git a/core/vm/logger.go b/core/vm/logger.go index 5ada310f0..623c0d563 100644 --- a/core/vm/logger.go +++ b/core/vm/logger.go @@ -128,18 +128,14 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui } // capture SSTORE opcodes and determine the changed value and store - // it in the local storage container. NOTE: we do not need to do any - // range checks here because that's already handler prior to calling - // this function. - switch op { - case SSTORE: + // it in the local storage container. + if op == SSTORE && stack.len() >= 2 { var ( value = common.BigToHash(stack.data[stack.len()-2]) address = common.BigToHash(stack.data[stack.len()-1]) ) l.changedValues[contract.Address()][address] = value } - // copy a snapstot of the current memory state to a new buffer var mem []byte if !l.cfg.DisableMemory { diff --git a/tests/gen_stlog.go b/tests/gen_stlog.go deleted file mode 100644 index 4f7ebc966..000000000 --- a/tests/gen_stlog.go +++ /dev/null @@ -1,61 +0,0 @@ -// Code generated by github.com/fjl/gencodec. DO NOT EDIT. - -package tests - -import ( - "encoding/json" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" -) - -var _ = (*stLogMarshaling)(nil) - -func (s stLog) MarshalJSON() ([]byte, error) { - type stLog struct { - Address common.UnprefixedAddress `json:"address"` - Data hexutil.Bytes `json:"data"` - Topics []common.UnprefixedHash `json:"topics"` - Bloom string `json:"bloom"` - } - var enc stLog - enc.Address = common.UnprefixedAddress(s.Address) - enc.Data = s.Data - if s.Topics != nil { - enc.Topics = make([]common.UnprefixedHash, len(s.Topics)) - for k, v := range s.Topics { - enc.Topics[k] = common.UnprefixedHash(v) - } - } - enc.Bloom = s.Bloom - return json.Marshal(&enc) -} - -func (s *stLog) UnmarshalJSON(input []byte) error { - type stLog struct { - Address *common.UnprefixedAddress `json:"address"` - Data hexutil.Bytes `json:"data"` - Topics []common.UnprefixedHash `json:"topics"` - Bloom *string `json:"bloom"` - } - var dec stLog - if err := json.Unmarshal(input, &dec); err != nil { - return err - } - if dec.Address != nil { - s.Address = common.Address(*dec.Address) - } - if dec.Data != nil { - s.Data = dec.Data - } - if dec.Topics != nil { - s.Topics = make([]common.Hash, len(dec.Topics)) - for k, v := range dec.Topics { - s.Topics[k] = common.Hash(v) - } - } - if dec.Bloom != nil { - s.Bloom = *dec.Bloom - } - return nil -} diff --git a/tests/state_test_util.go b/tests/state_test_util.go index 0eb85ab28..ecaa6c668 100644 --- a/tests/state_test_util.go +++ b/tests/state_test_util.go @@ -17,12 +17,10 @@ package tests import ( - "bytes" "encoding/hex" "encoding/json" "fmt" "math/big" - "reflect" "strings" "github.com/ethereum/go-ethereum/common" @@ -33,8 +31,10 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/crypto/sha3" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/rlp" ) // StateTest checks transaction processing without block context. @@ -63,7 +63,7 @@ type stJSON struct { type stPostState struct { Root common.UnprefixedHash `json:"hash"` - Logs *[]stLog `json:"logs"` + Logs common.UnprefixedHash `json:"logs"` Indexes struct { Data int `json:"data"` Gas int `json:"gas"` @@ -108,21 +108,6 @@ type stTransactionMarshaling struct { PrivateKey hexutil.Bytes } -//go:generate gencodec -type stLog -field-override stLogMarshaling -out gen_stlog.go - -type stLog struct { - Address common.Address `json:"address"` - Data []byte `json:"data"` - Topics []common.Hash `json:"topics"` - Bloom string `json:"bloom"` -} - -type stLogMarshaling struct { - Address common.UnprefixedAddress - Data hexutil.Bytes - Topics []common.UnprefixedHash -} - // Subtests returns all valid subtests of the test. func (t *StateTest) Subtests() []StateSubtest { var sub []StateSubtest @@ -159,10 +144,8 @@ func (t *StateTest) Run(subtest StateSubtest, vmconfig vm.Config) error { if _, _, _, err := core.ApplyMessage(evm, msg, gaspool); err != nil { statedb.RevertToSnapshot(snapshot) } - if post.Logs != nil { - if err := checkLogs(statedb.Logs(), *post.Logs); err != nil { - return err - } + if logs := rlpHash(statedb.Logs()); logs != common.Hash(post.Logs) { + return fmt.Errorf("post state logs hash mismatch: got %x, want %x", logs, post.Logs) } root, _ := statedb.CommitTo(db, config.IsEIP158(block.Number())) if root != common.Hash(post.Root) { @@ -254,28 +237,9 @@ func (tx *stTransaction) toMessage(ps stPostState) (core.Message, error) { return msg, nil } -func checkLogs(have []*types.Log, want []stLog) error { - if len(have) != len(want) { - return fmt.Errorf("logs length mismatch: got %d, want %d", len(have), len(want)) - } - for i := range have { - if have[i].Address != want[i].Address { - return fmt.Errorf("log address %d: got %x, want %x", i, have[i].Address, want[i].Address) - } - if !bytes.Equal(have[i].Data, want[i].Data) { - return fmt.Errorf("log data %d: got %x, want %x", i, have[i].Data, want[i].Data) - } - if !reflect.DeepEqual(have[i].Topics, want[i].Topics) { - return fmt.Errorf("log topics %d:\ngot %x\nwant %x", i, have[i].Topics, want[i].Topics) - } - genBloom := math.PaddedBigBytes(types.LogsBloom([]*types.Log{have[i]}), 256) - var wantBloom types.Bloom - if err := hexutil.UnmarshalFixedUnprefixedText("Bloom", []byte(want[i].Bloom), wantBloom[:]); err != nil { - return fmt.Errorf("test log %d has invalid bloom: %v", i, err) - } - if !bytes.Equal(genBloom, wantBloom[:]) { - return fmt.Errorf("bloom mismatch") - } - } - return nil +func rlpHash(x interface{}) (h common.Hash) { + hw := sha3.NewKeccak256() + rlp.Encode(hw, x) + hw.Sum(h[:0]) + return h } diff --git a/tests/testdata b/tests/testdata index 70e5862eb..cd2c3f1b3 160000 --- a/tests/testdata +++ b/tests/testdata @@ -1 +1 @@ -Subproject commit 70e5862eb267226ca89fb9f395c97be1fdf6923a +Subproject commit cd2c3f1b3acb98c0d1501b06a4a54629d8794d79 diff --git a/tests/vm_test.go b/tests/vm_test.go index 5289ba355..f35abeb11 100644 --- a/tests/vm_test.go +++ b/tests/vm_test.go @@ -26,6 +26,10 @@ func TestVM(t *testing.T) { t.Parallel() vmt := new(testMatcher) vmt.fails("^vmSystemOperationsTest.json/createNameRegistrator$", "fails without parallel execution") + + vmt.skipLoad(`^vmPerformanceTest.json`) // log format broken + vmt.skipLoad(`^vmInputLimits(Light)?.json`) // log format broken + vmt.skipShortMode("^vmPerformanceTest.json") vmt.skipShortMode("^vmInputLimits(Light)?.json") diff --git a/tests/vm_test_util.go b/tests/vm_test_util.go index afdd896c3..0aa37955c 100644 --- a/tests/vm_test_util.go +++ b/tests/vm_test_util.go @@ -44,14 +44,14 @@ func (t *VMTest) UnmarshalJSON(data []byte) error { } type vmJSON struct { - Env stEnv `json:"env"` - Exec vmExec `json:"exec"` - Logs []stLog `json:"logs"` - GasRemaining *math.HexOrDecimal64 `json:"gas"` - Out hexutil.Bytes `json:"out"` - Pre core.GenesisAlloc `json:"pre"` - Post core.GenesisAlloc `json:"post"` - PostStateRoot common.Hash `json:"postStateRoot"` + Env stEnv `json:"env"` + Exec vmExec `json:"exec"` + Logs common.UnprefixedHash `json:"logs"` + GasRemaining *math.HexOrDecimal64 `json:"gas"` + Out hexutil.Bytes `json:"out"` + Pre core.GenesisAlloc `json:"pre"` + Post core.GenesisAlloc `json:"post"` + PostStateRoot common.Hash `json:"postStateRoot"` } //go:generate gencodec -type vmExec -field-override vmExecMarshaling -out gen_vmexec.go @@ -109,7 +109,10 @@ func (t *VMTest) Run(vmconfig vm.Config) error { // if root := statedb.IntermediateRoot(false); root != t.json.PostStateRoot { // return fmt.Errorf("post state root mismatch, got %x, want %x", root, t.json.PostStateRoot) // } - return checkLogs(statedb.Logs(), t.json.Logs) + if logs := rlpHash(statedb.Logs()); logs != common.Hash(t.json.Logs) { + return fmt.Errorf("post state logs hash mismatch: got %x, want %x", logs, t.json.Logs) + } + return nil } func (t *VMTest) exec(statedb *state.StateDB, vmconfig vm.Config) ([]byte, uint64, error) {