Skip to content

Commit a0cc73a

Browse files
committed
[release/1.4.10] cmd, core, miner: add extradata validation to consensus rules
(cherry picked from commit a87089f)
1 parent 682c453 commit a0cc73a

File tree

7 files changed

+275
-216
lines changed

7 files changed

+275
-216
lines changed

cmd/geth/dao_test.go

Lines changed: 110 additions & 184 deletions
Large diffs are not rendered by default.

cmd/geth/genesis_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,8 @@ var customGenesisTests = []struct {
7575
"timestamp" : "0x00",
7676
"config" : {
7777
"homesteadBlock" : 314,
78-
"daoForkBlock" : 141
78+
"daoForkBlock" : 141,
79+
"daoForkSupport" : true
7980
},
8081
}`,
8182
query: "eth.getBlock(0).nonce",

cmd/utils/flags.go

Lines changed: 26 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -791,43 +791,42 @@ func MustMakeChainConfig(ctx *cli.Context) *core.ChainConfig {
791791
// MustMakeChainConfigFromDb reads the chain configuration from the given database.
792792
func MustMakeChainConfigFromDb(ctx *cli.Context, db ethdb.Database) *core.ChainConfig {
793793
// If the chain is already initialized, use any existing chain configs
794+
config := new(core.ChainConfig)
795+
794796
if genesis := core.GetBlock(db, core.GetCanonicalHash(db, 0)); genesis != nil {
795797
storedConfig, err := core.GetChainConfig(db, genesis.Hash())
796-
if err == nil {
797-
// Force override any existing configs if explicitly requested
798-
switch {
799-
case storedConfig.DAOForkBlock == nil && ctx.GlobalBool(SupportDAOFork.Name) && ctx.GlobalBool(TestNetFlag.Name):
800-
storedConfig.DAOForkBlock = params.TestNetDAOForkBlock
801-
case storedConfig.DAOForkBlock == nil && ctx.GlobalBool(SupportDAOFork.Name):
802-
storedConfig.DAOForkBlock = params.MainNetDAOForkBlock
803-
case ctx.GlobalBool(OpposeDAOFork.Name):
804-
storedConfig.DAOForkBlock = nil
805-
}
806-
return storedConfig
807-
} else if err != core.ChainConfigNotFoundErr {
798+
switch err {
799+
case nil:
800+
config = storedConfig
801+
case core.ChainConfigNotFoundErr:
802+
// No configs found, use empty, will populate below
803+
default:
808804
Fatalf("Could not make chain configuration: %v", err)
809805
}
810806
}
811-
// If the chain is uninitialized nor no configs are present, create one
812-
var homesteadBlock *big.Int
813-
if ctx.GlobalBool(TestNetFlag.Name) {
814-
homesteadBlock = params.TestNetHomesteadBlock
815-
} else {
816-
homesteadBlock = params.MainNetHomesteadBlock
807+
// Set any missing fields due to them being unset or system upgrade
808+
if config.HomesteadBlock == nil {
809+
if ctx.GlobalBool(TestNetFlag.Name) {
810+
config.HomesteadBlock = new(big.Int).Set(params.TestNetHomesteadBlock)
811+
} else {
812+
config.HomesteadBlock = new(big.Int).Set(params.MainNetHomesteadBlock)
813+
}
817814
}
818-
var daoForkBlock *big.Int
815+
if config.DAOForkBlock == nil {
816+
if ctx.GlobalBool(TestNetFlag.Name) {
817+
config.DAOForkBlock = new(big.Int).Set(params.TestNetDAOForkBlock)
818+
} else {
819+
config.DAOForkBlock = new(big.Int).Set(params.MainNetDAOForkBlock)
820+
}
821+
}
822+
// Force override any existing configs if explicitly requested
819823
switch {
820-
case ctx.GlobalBool(SupportDAOFork.Name) && ctx.GlobalBool(TestNetFlag.Name):
821-
daoForkBlock = params.TestNetDAOForkBlock
822824
case ctx.GlobalBool(SupportDAOFork.Name):
823-
daoForkBlock = params.MainNetDAOForkBlock
825+
config.DAOForkSupport = true
824826
case ctx.GlobalBool(OpposeDAOFork.Name):
825-
daoForkBlock = nil
826-
}
827-
return &core.ChainConfig{
828-
HomesteadBlock: homesteadBlock,
829-
DAOForkBlock: daoForkBlock,
827+
config.DAOForkSupport = false
830828
}
829+
return config
831830
}
832831

833832
// MakeChainDatabase open an LevelDB using the flags passed to the client and will hard crash if it fails.

core/block_validator.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package core
1818

1919
import (
20+
"bytes"
2021
"fmt"
2122
"math/big"
2223
"time"
@@ -247,6 +248,26 @@ func ValidateHeader(config *ChainConfig, pow pow.PoW, header *types.Header, pare
247248
return &BlockNonceErr{header.Number, header.Hash(), header.Nonce.Uint64()}
248249
}
249250
}
251+
// DAO hard-fork extension to the header validity: a) if the node is no-fork,
252+
// do not accept blocks in the [fork, fork+10) range with the fork specific
253+
// extra-data set; b) if the node is pro-fork, require blocks in the specific
254+
// range to have the unique extra-data set.
255+
if daoBlock := config.DAOForkBlock; daoBlock != nil {
256+
// Check whether the block is among the fork extra-override range
257+
limit := new(big.Int).Add(daoBlock, params.DAOForkExtraRange)
258+
if daoBlock.Cmp(header.Number) <= 0 && header.Number.Cmp(limit) < 0 {
259+
// Depending whether we support or oppose the fork, verrift the extra-data contents
260+
if config.DAOForkSupport {
261+
if bytes.Compare(header.Extra, params.DAOForkBlockExtra) != 0 {
262+
return ValidationError("DAO pro-fork bad block extra-data: 0x%x", header.Extra)
263+
}
264+
} else {
265+
if bytes.Compare(header.Extra, params.DAOForkBlockExtra) == 0 {
266+
return ValidationError("DAO no-fork bad block extra-data: 0x%x", header.Extra)
267+
}
268+
}
269+
}
270+
}
250271
return nil
251272
}
252273

core/block_validator_test.go

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727
"github.com/ethereum/go-ethereum/core/vm"
2828
"github.com/ethereum/go-ethereum/ethdb"
2929
"github.com/ethereum/go-ethereum/event"
30+
"github.com/ethereum/go-ethereum/params"
3031
"github.com/ethereum/go-ethereum/pow/ezp"
3132
)
3233

@@ -92,3 +93,107 @@ func TestPutReceipt(t *testing.T) {
9293
t.Error("expected to get 1 receipt, got none.")
9394
}
9495
}
96+
97+
// Tests that DAO-fork enabled clients can properly filter out fork-commencing
98+
// blocks based on their extradata fields.
99+
func TestDAOForkRangeExtradata(t *testing.T) {
100+
forkBlock := big.NewInt(32)
101+
102+
// Generate a common prefix for both pro-forkers and non-forkers
103+
db, _ := ethdb.NewMemDatabase()
104+
genesis := WriteGenesisBlockForTesting(db)
105+
prefix, _ := GenerateChain(genesis, db, int(forkBlock.Int64()-1), func(i int, gen *BlockGen) {})
106+
107+
// Create the concurrent, conflicting two nodes
108+
proDb, _ := ethdb.NewMemDatabase()
109+
WriteGenesisBlockForTesting(proDb)
110+
proBc, _ := NewBlockChain(proDb, &ChainConfig{HomesteadBlock: big.NewInt(0), DAOForkBlock: forkBlock, DAOForkSupport: true}, new(FakePow), new(event.TypeMux))
111+
112+
conDb, _ := ethdb.NewMemDatabase()
113+
WriteGenesisBlockForTesting(conDb)
114+
conBc, _ := NewBlockChain(conDb, &ChainConfig{HomesteadBlock: big.NewInt(0), DAOForkBlock: forkBlock, DAOForkSupport: false}, new(FakePow), new(event.TypeMux))
115+
116+
if _, err := proBc.InsertChain(prefix); err != nil {
117+
t.Fatalf("pro-fork: failed to import chain prefix: %v", err)
118+
}
119+
if _, err := conBc.InsertChain(prefix); err != nil {
120+
t.Fatalf("con-fork: failed to import chain prefix: %v", err)
121+
}
122+
// Try to expand both pro-fork and non-fork chains iteratively with other camp's blocks
123+
for i := int64(0); i < params.DAOForkExtraRange.Int64(); i++ {
124+
// Create a pro-fork block, and try to feed into the no-fork chain
125+
db, _ = ethdb.NewMemDatabase()
126+
WriteGenesisBlockForTesting(db)
127+
bc, _ := NewBlockChain(db, &ChainConfig{HomesteadBlock: big.NewInt(0)}, new(FakePow), new(event.TypeMux))
128+
129+
blocks := conBc.GetBlocksFromHash(conBc.CurrentBlock().Hash(), int(conBc.CurrentBlock().NumberU64()+1))
130+
for j := 0; j < len(blocks)/2; j++ {
131+
blocks[j], blocks[len(blocks)-1-j] = blocks[len(blocks)-1-j], blocks[j]
132+
}
133+
if _, err := bc.InsertChain(blocks); err != nil {
134+
t.Fatalf("failed to import contra-fork chain for expansion: %v", err)
135+
}
136+
blocks, _ = GenerateChain(conBc.CurrentBlock(), db, 1, func(i int, gen *BlockGen) { gen.SetExtra(params.DAOForkBlockExtra) })
137+
if _, err := conBc.InsertChain(blocks); err == nil {
138+
t.Fatalf("contra-fork chain accepted pro-fork block: %v", blocks[0])
139+
}
140+
// Create a proper no-fork block for the contra-forker
141+
blocks, _ = GenerateChain(conBc.CurrentBlock(), db, 1, func(i int, gen *BlockGen) {})
142+
if _, err := conBc.InsertChain(blocks); err != nil {
143+
t.Fatalf("contra-fork chain didn't accepted no-fork block: %v", err)
144+
}
145+
// Create a no-fork block, and try to feed into the pro-fork chain
146+
db, _ = ethdb.NewMemDatabase()
147+
WriteGenesisBlockForTesting(db)
148+
bc, _ = NewBlockChain(db, &ChainConfig{HomesteadBlock: big.NewInt(0)}, new(FakePow), new(event.TypeMux))
149+
150+
blocks = proBc.GetBlocksFromHash(proBc.CurrentBlock().Hash(), int(proBc.CurrentBlock().NumberU64()+1))
151+
for j := 0; j < len(blocks)/2; j++ {
152+
blocks[j], blocks[len(blocks)-1-j] = blocks[len(blocks)-1-j], blocks[j]
153+
}
154+
if _, err := bc.InsertChain(blocks); err != nil {
155+
t.Fatalf("failed to import pro-fork chain for expansion: %v", err)
156+
}
157+
blocks, _ = GenerateChain(proBc.CurrentBlock(), db, 1, func(i int, gen *BlockGen) {})
158+
if _, err := proBc.InsertChain(blocks); err == nil {
159+
t.Fatalf("pro-fork chain accepted contra-fork block: %v", blocks[0])
160+
}
161+
// Create a proper pro-fork block for the pro-forker
162+
blocks, _ = GenerateChain(proBc.CurrentBlock(), db, 1, func(i int, gen *BlockGen) { gen.SetExtra(params.DAOForkBlockExtra) })
163+
if _, err := proBc.InsertChain(blocks); err != nil {
164+
t.Fatalf("pro-fork chain didn't accepted pro-fork block: %v", err)
165+
}
166+
}
167+
// Verify that contra-forkers accept pro-fork extra-datas after forking finishes
168+
db, _ = ethdb.NewMemDatabase()
169+
WriteGenesisBlockForTesting(db)
170+
bc, _ := NewBlockChain(db, &ChainConfig{HomesteadBlock: big.NewInt(0)}, new(FakePow), new(event.TypeMux))
171+
172+
blocks := conBc.GetBlocksFromHash(conBc.CurrentBlock().Hash(), int(conBc.CurrentBlock().NumberU64()+1))
173+
for j := 0; j < len(blocks)/2; j++ {
174+
blocks[j], blocks[len(blocks)-1-j] = blocks[len(blocks)-1-j], blocks[j]
175+
}
176+
if _, err := bc.InsertChain(blocks); err != nil {
177+
t.Fatalf("failed to import contra-fork chain for expansion: %v", err)
178+
}
179+
blocks, _ = GenerateChain(conBc.CurrentBlock(), db, 1, func(i int, gen *BlockGen) { gen.SetExtra(params.DAOForkBlockExtra) })
180+
if _, err := conBc.InsertChain(blocks); err != nil {
181+
t.Fatalf("contra-fork chain didn't accept pro-fork block post-fork: %v", err)
182+
}
183+
// Verify that pro-forkers accept contra-fork extra-datas after forking finishes
184+
db, _ = ethdb.NewMemDatabase()
185+
WriteGenesisBlockForTesting(db)
186+
bc, _ = NewBlockChain(db, &ChainConfig{HomesteadBlock: big.NewInt(0)}, new(FakePow), new(event.TypeMux))
187+
188+
blocks = proBc.GetBlocksFromHash(proBc.CurrentBlock().Hash(), int(proBc.CurrentBlock().NumberU64()+1))
189+
for j := 0; j < len(blocks)/2; j++ {
190+
blocks[j], blocks[len(blocks)-1-j] = blocks[len(blocks)-1-j], blocks[j]
191+
}
192+
if _, err := bc.InsertChain(blocks); err != nil {
193+
t.Fatalf("failed to import pro-fork chain for expansion: %v", err)
194+
}
195+
blocks, _ = GenerateChain(proBc.CurrentBlock(), db, 1, func(i int, gen *BlockGen) {})
196+
if _, err := proBc.InsertChain(blocks); err != nil {
197+
t.Fatalf("pro-fork chain didn't accept contra-fork block post-fork: %v", err)
198+
}
199+
}

core/config.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,9 @@ var ChainConfigNotFoundErr = errors.New("ChainConfig not found") // general conf
3131
// that any network, identified by its genesis block, can have its own
3232
// set of configuration options.
3333
type ChainConfig struct {
34-
HomesteadBlock *big.Int `json:"homesteadBlock"` // homestead switch block (0 = already homestead)
35-
DAOForkBlock *big.Int `json:"daoForkBlock"` // TheDAO hard-fork block (nil = no fork)
34+
HomesteadBlock *big.Int `json:"homesteadBlock"` // Homestead switch block (nil = no fork, 0 = already homestead)
35+
DAOForkBlock *big.Int `json:"daoForkBlock"` // TheDAO hard-fork switch block (nil = no fork)
36+
DAOForkSupport bool `json:"daoForkSupport"` // Whether the nodes supports or opposes the DAO hard-fork
3637

3738
VmConfig vm.Config `json:"-"`
3839
}

miner/worker.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package miner
1818

1919
import (
20+
"bytes"
2021
"fmt"
2122
"math/big"
2223
"sync"
@@ -469,12 +470,17 @@ func (self *worker) commitNewWork() {
469470
Extra: self.extra,
470471
Time: big.NewInt(tstamp),
471472
}
472-
// If we are doing a DAO hard-fork check whether to override the extra-data or not
473+
// If we are care about TheDAO hard-fork check whether to override the extra-data or not
473474
if daoBlock := self.config.DAOForkBlock; daoBlock != nil {
474475
// Check whether the block is among the fork extra-override range
475476
limit := new(big.Int).Add(daoBlock, params.DAOForkExtraRange)
476477
if daoBlock.Cmp(header.Number) <= 0 && header.Number.Cmp(limit) < 0 {
477-
header.Extra = common.CopyBytes(params.DAOForkBlockExtra)
478+
// Depending whether we support or oppose the fork, override differently
479+
if self.config.DAOForkSupport {
480+
header.Extra = common.CopyBytes(params.DAOForkBlockExtra)
481+
} else if bytes.Compare(header.Extra, params.DAOForkBlockExtra) == 0 {
482+
header.Extra = []byte{} // If miner opposes, don't let it use the reserved extra-data
483+
}
478484
}
479485
}
480486
previous := self.current

0 commit comments

Comments
 (0)