diff options
Diffstat (limited to 'eth')
-rw-r--r-- | eth/api.go | 109 | ||||
-rw-r--r-- | eth/api_test.go | 5 | ||||
-rw-r--r-- | eth/backend.go | 31 | ||||
-rw-r--r-- | eth/config.go | 39 | ||||
-rw-r--r-- | eth/downloader/downloader_test.go | 61 | ||||
-rw-r--r-- | eth/downloader/peer.go | 2 | ||||
-rw-r--r-- | eth/gen_config.go | 51 |
7 files changed, 216 insertions, 82 deletions
diff --git a/eth/api.go b/eth/api.go index e91f51bb9..c748f75de 100644 --- a/eth/api.go +++ b/eth/api.go @@ -452,7 +452,12 @@ func (api *PrivateDebugAPI) traceBlock(block *types.Block, logConfig *vm.LogConf } statedb, err := blockchain.StateAt(blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1).Root()) if err != nil { - return false, structLogger.StructLogs(), err + switch err.(type) { + case *trie.MissingNodeError: + return false, structLogger.StructLogs(), fmt.Errorf("required historical state unavailable") + default: + return false, structLogger.StructLogs(), err + } } receipts, _, usedGas, err := processor.Process(block, statedb, config) @@ -518,7 +523,12 @@ func (api *PrivateDebugAPI) TraceTransaction(ctx context.Context, txHash common. } msg, context, statedb, err := api.computeTxEnv(blockHash, int(txIndex)) if err != nil { - return nil, err + switch err.(type) { + case *trie.MissingNodeError: + return nil, fmt.Errorf("required historical state unavailable") + default: + return nil, err + } } // Run the transaction with tracing enabled. @@ -615,14 +625,18 @@ func (api *PrivateDebugAPI) StorageRangeAt(ctx context.Context, blockHash common if st == nil { return StorageRangeResult{}, fmt.Errorf("account %x doesn't exist", contractAddress) } - return storageRangeAt(st, keyStart, maxResult), nil + return storageRangeAt(st, keyStart, maxResult) } -func storageRangeAt(st state.Trie, start []byte, maxResult int) StorageRangeResult { +func storageRangeAt(st state.Trie, start []byte, maxResult int) (StorageRangeResult, error) { it := trie.NewIterator(st.NodeIterator(start)) result := StorageRangeResult{Storage: storageMap{}} for i := 0; i < maxResult && it.Next(); i++ { - e := storageEntry{Value: common.BytesToHash(it.Value)} + _, content, _, err := rlp.Split(it.Value) + if err != nil { + return StorageRangeResult{}, err + } + e := storageEntry{Value: common.BytesToHash(content)} if preimage := st.GetKey(it.Key); preimage != nil { preimage := common.BytesToHash(preimage) e.Key = &preimage @@ -634,5 +648,88 @@ func storageRangeAt(st state.Trie, start []byte, maxResult int) StorageRangeResu next := common.BytesToHash(it.Key) result.NextKey = &next } - return result + return result, nil +} + +// GetModifiedAccountsByumber returns all accounts that have changed between the +// two blocks specified. A change is defined as a difference in nonce, balance, +// code hash, or storage hash. +// +// With one parameter, returns the list of accounts modified in the specified block. +func (api *PrivateDebugAPI) GetModifiedAccountsByNumber(startNum uint64, endNum *uint64) ([]common.Address, error) { + var startBlock, endBlock *types.Block + + startBlock = api.eth.blockchain.GetBlockByNumber(startNum) + if startBlock == nil { + return nil, fmt.Errorf("start block %x not found", startNum) + } + + if endNum == nil { + endBlock = startBlock + startBlock = api.eth.blockchain.GetBlockByHash(startBlock.ParentHash()) + if startBlock == nil { + return nil, fmt.Errorf("block %x has no parent", endBlock.Number()) + } + } else { + endBlock = api.eth.blockchain.GetBlockByNumber(*endNum) + if endBlock == nil { + return nil, fmt.Errorf("end block %d not found", *endNum) + } + } + return api.getModifiedAccounts(startBlock, endBlock) +} + +// GetModifiedAccountsByHash returns all accounts that have changed between the +// two blocks specified. A change is defined as a difference in nonce, balance, +// code hash, or storage hash. +// +// With one parameter, returns the list of accounts modified in the specified block. +func (api *PrivateDebugAPI) GetModifiedAccountsByHash(startHash common.Hash, endHash *common.Hash) ([]common.Address, error) { + var startBlock, endBlock *types.Block + startBlock = api.eth.blockchain.GetBlockByHash(startHash) + if startBlock == nil { + return nil, fmt.Errorf("start block %x not found", startHash) + } + + if endHash == nil { + endBlock = startBlock + startBlock = api.eth.blockchain.GetBlockByHash(startBlock.ParentHash()) + if startBlock == nil { + return nil, fmt.Errorf("block %x has no parent", endBlock.Number()) + } + } else { + endBlock = api.eth.blockchain.GetBlockByHash(*endHash) + if endBlock == nil { + return nil, fmt.Errorf("end block %x not found", *endHash) + } + } + return api.getModifiedAccounts(startBlock, endBlock) +} + +func (api *PrivateDebugAPI) getModifiedAccounts(startBlock, endBlock *types.Block) ([]common.Address, error) { + if startBlock.Number().Uint64() >= endBlock.Number().Uint64() { + return nil, fmt.Errorf("start block height (%d) must be less than end block height (%d)", startBlock.Number().Uint64(), endBlock.Number().Uint64()) + } + + oldTrie, err := trie.NewSecure(startBlock.Root(), api.eth.chainDb, 0) + if err != nil { + return nil, err + } + newTrie, err := trie.NewSecure(endBlock.Root(), api.eth.chainDb, 0) + if err != nil { + return nil, err + } + + diff, _ := trie.NewDifferenceIterator(oldTrie.NodeIterator([]byte{}), newTrie.NodeIterator([]byte{})) + iter := trie.NewIterator(diff) + + var dirty []common.Address + for iter.Next() { + key := newTrie.GetKey(iter.Key) + if key == nil { + return nil, fmt.Errorf("no preimage found for hash %x", iter.Key) + } + dirty = append(dirty, common.BytesToAddress(key)) + } + return dirty, nil } diff --git a/eth/api_test.go b/eth/api_test.go index 49ce38688..248bc3ab6 100644 --- a/eth/api_test.go +++ b/eth/api_test.go @@ -79,7 +79,10 @@ func TestStorageRangeAt(t *testing.T) { }, } for _, test := range tests { - result := storageRangeAt(state.StorageTrie(addr), test.start, test.limit) + result, err := storageRangeAt(state.StorageTrie(addr), test.start, test.limit) + if err != nil { + t.Error(err) + } if !reflect.DeepEqual(result, test.want) { t.Fatalf("wrong result for range 0x%x.., limit %d:\ngot %s\nwant %s", test.start, test.limit, dumper.Sdump(result), dumper.Sdump(&test.want)) diff --git a/eth/backend.go b/eth/backend.go index 1cd9e8fff..c39974a2c 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -125,7 +125,7 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { chainConfig: chainConfig, eventMux: ctx.EventMux, accountManager: ctx.AccountManager, - engine: CreateConsensusEngine(ctx, config, chainConfig, chainDb), + engine: CreateConsensusEngine(ctx, &config.Ethash, chainConfig, chainDb), shutdownChan: make(chan bool), stopDbUpgrade: stopDbUpgrade, networkId: config.NetworkId, @@ -209,25 +209,31 @@ func CreateDB(ctx *node.ServiceContext, config *Config, name string) (ethdb.Data } // CreateConsensusEngine creates the required type of consensus engine instance for an Ethereum service -func CreateConsensusEngine(ctx *node.ServiceContext, config *Config, chainConfig *params.ChainConfig, db ethdb.Database) consensus.Engine { +func CreateConsensusEngine(ctx *node.ServiceContext, config *ethash.Config, chainConfig *params.ChainConfig, db ethdb.Database) consensus.Engine { // If proof-of-authority is requested, set it up if chainConfig.Clique != nil { return clique.New(chainConfig.Clique, db) } // Otherwise assume proof-of-work switch { - case config.PowFake: + case config.PowMode == ethash.ModeFake: log.Warn("Ethash used in fake mode") return ethash.NewFaker() - case config.PowTest: + case config.PowMode == ethash.ModeTest: log.Warn("Ethash used in test mode") return ethash.NewTester() - case config.PowShared: + case config.PowMode == ethash.ModeShared: log.Warn("Ethash used in shared mode") return ethash.NewShared() default: - engine := ethash.New(ctx.ResolvePath(config.EthashCacheDir), config.EthashCachesInMem, config.EthashCachesOnDisk, - config.EthashDatasetDir, config.EthashDatasetsInMem, config.EthashDatasetsOnDisk) + engine := ethash.New(ethash.Config{ + CacheDir: ctx.ResolvePath(config.CacheDir), + CachesInMem: config.CachesInMem, + CachesOnDisk: config.CachesOnDisk, + DatasetDir: config.DatasetDir, + DatasetsInMem: config.DatasetsInMem, + DatasetsOnDisk: config.DatasetsOnDisk, + }) engine.SetThreads(-1) // Disable CPU mining return engine } @@ -304,10 +310,17 @@ func (s *Ethereum) Etherbase() (eb common.Address, err error) { } if wallets := s.AccountManager().Wallets(); len(wallets) > 0 { if accounts := wallets[0].Accounts(); len(accounts) > 0 { - return accounts[0].Address, nil + etherbase := accounts[0].Address + + s.lock.Lock() + s.etherbase = etherbase + s.lock.Unlock() + + log.Info("Etherbase automatically configured", "address", etherbase) + return etherbase, nil } } - return common.Address{}, fmt.Errorf("etherbase address must be explicitly specified") + return common.Address{}, fmt.Errorf("etherbase must be explicitly specified") } // set in js console via admin interface or wrapper from cli flags diff --git a/eth/config.go b/eth/config.go index 7bcfd403e..383cd6783 100644 --- a/eth/config.go +++ b/eth/config.go @@ -25,6 +25,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/eth/gasprice" @@ -33,16 +34,18 @@ import ( // DefaultConfig contains default settings for use on the Ethereum main net. var DefaultConfig = Config{ - SyncMode: downloader.FastSync, - EthashCacheDir: "ethash", - EthashCachesInMem: 2, - EthashCachesOnDisk: 3, - EthashDatasetsInMem: 1, - EthashDatasetsOnDisk: 2, - NetworkId: 1, - LightPeers: 20, - DatabaseCache: 128, - GasPrice: big.NewInt(18 * params.Shannon), + SyncMode: downloader.FastSync, + Ethash: ethash.Config{ + CacheDir: "ethash", + CachesInMem: 2, + CachesOnDisk: 3, + DatasetsInMem: 1, + DatasetsOnDisk: 2, + }, + NetworkId: 1, + LightPeers: 20, + DatabaseCache: 128, + GasPrice: big.NewInt(18 * params.Shannon), TxPool: core.DefaultTxPoolConfig, GPO: gasprice.Config{ @@ -59,9 +62,9 @@ func init() { } } if runtime.GOOS == "windows" { - DefaultConfig.EthashDatasetDir = filepath.Join(home, "AppData", "Ethash") + DefaultConfig.Ethash.DatasetDir = filepath.Join(home, "AppData", "Ethash") } else { - DefaultConfig.EthashDatasetDir = filepath.Join(home, ".ethash") + DefaultConfig.Ethash.DatasetDir = filepath.Join(home, ".ethash") } } @@ -92,12 +95,7 @@ type Config struct { GasPrice *big.Int // Ethash options - EthashCacheDir string - EthashCachesInMem int - EthashCachesOnDisk int - EthashDatasetDir string - EthashDatasetsInMem int - EthashDatasetsOnDisk int + Ethash ethash.Config // Transaction pool options TxPool core.TxPoolConfig @@ -109,10 +107,7 @@ type Config struct { EnablePreimageRecording bool // Miscellaneous options - DocRoot string `toml:"-"` - PowFake bool `toml:"-"` - PowTest bool `toml:"-"` - PowShared bool `toml:"-"` + DocRoot string `toml:"-"` } type configMarshaling struct { diff --git a/eth/downloader/downloader_test.go b/eth/downloader/downloader_test.go index 58f6e9a62..7d1cc8c34 100644 --- a/eth/downloader/downloader_test.go +++ b/eth/downloader/downloader_test.go @@ -704,6 +704,7 @@ func TestThrottling64Full(t *testing.T) { testThrottling(t, 64, FullSync) } func TestThrottling64Fast(t *testing.T) { testThrottling(t, 64, FastSync) } func testThrottling(t *testing.T, protocol int, mode SyncMode) { + t.Parallel() tester := newTester() defer tester.terminate() @@ -1166,6 +1167,8 @@ func TestShiftedHeaderAttack64Fast(t *testing.T) { testShiftedHeaderAttack(t, 6 func TestShiftedHeaderAttack64Light(t *testing.T) { testShiftedHeaderAttack(t, 64, LightSync) } func testShiftedHeaderAttack(t *testing.T, protocol int, mode SyncMode) { + t.Parallel() + tester := newTester() defer tester.terminate() @@ -1198,6 +1201,8 @@ func TestInvalidHeaderRollback64Fast(t *testing.T) { testInvalidHeaderRollback( func TestInvalidHeaderRollback64Light(t *testing.T) { testInvalidHeaderRollback(t, 64, LightSync) } func testInvalidHeaderRollback(t *testing.T, protocol int, mode SyncMode) { + t.Parallel() + tester := newTester() defer tester.terminate() @@ -1310,6 +1315,8 @@ func TestBlockHeaderAttackerDropping63(t *testing.T) { testBlockHeaderAttackerDr func TestBlockHeaderAttackerDropping64(t *testing.T) { testBlockHeaderAttackerDropping(t, 64) } func testBlockHeaderAttackerDropping(t *testing.T, protocol int) { + t.Parallel() + // Define the disconnection requirement for individual hash fetch errors tests := []struct { result error @@ -1665,12 +1672,26 @@ func testFakedSyncProgress(t *testing.T, protocol int, mode SyncMode) { // This test reproduces an issue where unexpected deliveries would // block indefinitely if they arrived at the right time. -func TestDeliverHeadersHang62(t *testing.T) { testDeliverHeadersHang(t, 62, FullSync) } -func TestDeliverHeadersHang63Full(t *testing.T) { testDeliverHeadersHang(t, 63, FullSync) } -func TestDeliverHeadersHang63Fast(t *testing.T) { testDeliverHeadersHang(t, 63, FastSync) } -func TestDeliverHeadersHang64Full(t *testing.T) { testDeliverHeadersHang(t, 64, FullSync) } -func TestDeliverHeadersHang64Fast(t *testing.T) { testDeliverHeadersHang(t, 64, FastSync) } -func TestDeliverHeadersHang64Light(t *testing.T) { testDeliverHeadersHang(t, 64, LightSync) } +// We use data driven subtests to manage this so that it will be parallel on its own +// and not with the other tests, avoiding intermittent failures. +func TestDeliverHeadersHang(t *testing.T) { + testCases := []struct { + protocol int + syncMode SyncMode + }{ + {62, FullSync}, + {63, FullSync}, + {63, FastSync}, + {64, FullSync}, + {64, FastSync}, + {64, LightSync}, + } + for _, tc := range testCases { + t.Run(fmt.Sprintf("protocol %d mode %v", tc.protocol, tc.syncMode), func(t *testing.T) { + testDeliverHeadersHang(t, tc.protocol, tc.syncMode) + }) + } +} type floodingTestPeer struct { peer Peer @@ -1703,7 +1724,7 @@ func (ftp *floodingTestPeer) RequestHeadersByNumber(from uint64, count, skip int // Deliver the actual requested headers. go ftp.peer.RequestHeadersByNumber(from, count, skip, reverse) // None of the extra deliveries should block. - timeout := time.After(15 * time.Second) + timeout := time.After(60 * time.Second) for i := 0; i < cap(deliveriesDone); i++ { select { case <-deliveriesDone: @@ -1732,7 +1753,6 @@ func testDeliverHeadersHang(t *testing.T, protocol int, mode SyncMode) { tester.downloader.peers.peers["peer"].peer, tester, } - if err := tester.sync("peer", nil, mode); err != nil { t.Errorf("sync failed: %v", err) } @@ -1742,12 +1762,28 @@ func testDeliverHeadersHang(t *testing.T, protocol int, mode SyncMode) { // Tests that if fast sync aborts in the critical section, it can restart a few // times before giving up. -func TestFastCriticalRestartsFail63(t *testing.T) { testFastCriticalRestarts(t, 63, false) } -func TestFastCriticalRestartsFail64(t *testing.T) { testFastCriticalRestarts(t, 64, false) } -func TestFastCriticalRestartsCont63(t *testing.T) { testFastCriticalRestarts(t, 63, true) } -func TestFastCriticalRestartsCont64(t *testing.T) { testFastCriticalRestarts(t, 64, true) } +// We use data driven subtests to manage this so that it will be parallel on its own +// and not with the other tests, avoiding intermittent failures. +func TestFastCriticalRestarts(t *testing.T) { + testCases := []struct { + protocol int + progress bool + }{ + {63, false}, + {64, false}, + {63, true}, + {64, true}, + } + for _, tc := range testCases { + t.Run(fmt.Sprintf("protocol %d progress %v", tc.protocol, tc.progress), func(t *testing.T) { + testFastCriticalRestarts(t, tc.protocol, tc.progress) + }) + } +} func testFastCriticalRestarts(t *testing.T, protocol int, progress bool) { + t.Parallel() + tester := newTester() defer tester.terminate() @@ -1776,6 +1812,7 @@ func testFastCriticalRestarts(t *testing.T, protocol int, progress bool) { // If it's the first failure, pivot should be locked => reenable all others to detect pivot changes if i == 0 { + time.Sleep(150 * time.Millisecond) // Make sure no in-flight requests remain if tester.downloader.fsPivotLock == nil { time.Sleep(400 * time.Millisecond) // Make sure the first huge timeout expires too t.Fatalf("pivot block not locked in after critical section failure") diff --git a/eth/downloader/peer.go b/eth/downloader/peer.go index e638744ea..a4aa86114 100644 --- a/eth/downloader/peer.go +++ b/eth/downloader/peer.go @@ -548,7 +548,7 @@ func (ps *peerSet) idlePeers(minProtocol, maxProtocol int, idleCheck func(*peerC return idle, total } -// medianRTT returns the median RTT of te peerset, considering only the tuning +// medianRTT returns the median RTT of the peerset, considering only the tuning // peers if there are more peers available. func (ps *peerSet) medianRTT() time.Duration { // Gather all the currnetly measured round trip times diff --git a/eth/gen_config.go b/eth/gen_config.go index 4a4cd7b9c..e2d50e1f6 100644 --- a/eth/gen_config.go +++ b/eth/gen_config.go @@ -7,6 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/eth/gasprice" @@ -36,10 +37,8 @@ func (c Config) MarshalTOML() (interface{}, error) { TxPool core.TxPoolConfig GPO gasprice.Config EnablePreimageRecording bool - DocRoot string `toml:"-"` - PowFake bool `toml:"-"` - PowTest bool `toml:"-"` - PowShared bool `toml:"-"` + DocRoot string `toml:"-"` + PowMode ethash.Mode `toml:"-"` } var enc Config enc.Genesis = c.Genesis @@ -54,19 +53,17 @@ func (c Config) MarshalTOML() (interface{}, error) { enc.MinerThreads = c.MinerThreads enc.ExtraData = c.ExtraData enc.GasPrice = c.GasPrice - enc.EthashCacheDir = c.EthashCacheDir - enc.EthashCachesInMem = c.EthashCachesInMem - enc.EthashCachesOnDisk = c.EthashCachesOnDisk - enc.EthashDatasetDir = c.EthashDatasetDir - enc.EthashDatasetsInMem = c.EthashDatasetsInMem - enc.EthashDatasetsOnDisk = c.EthashDatasetsOnDisk + enc.EthashCacheDir = c.Ethash.CacheDir + enc.EthashCachesInMem = c.Ethash.CachesInMem + enc.EthashCachesOnDisk = c.Ethash.CachesOnDisk + enc.EthashDatasetDir = c.Ethash.DatasetDir + enc.EthashDatasetsInMem = c.Ethash.DatasetsInMem + enc.EthashDatasetsOnDisk = c.Ethash.DatasetsOnDisk enc.TxPool = c.TxPool enc.GPO = c.GPO enc.EnablePreimageRecording = c.EnablePreimageRecording enc.DocRoot = c.DocRoot - enc.PowFake = c.PowFake - enc.PowTest = c.PowTest - enc.PowShared = c.PowShared + enc.PowMode = c.Ethash.PowMode return &enc, nil } @@ -94,10 +91,8 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error { TxPool *core.TxPoolConfig GPO *gasprice.Config EnablePreimageRecording *bool - DocRoot *string `toml:"-"` - PowFake *bool `toml:"-"` - PowTest *bool `toml:"-"` - PowShared *bool `toml:"-"` + DocRoot *string `toml:"-"` + PowMode *ethash.Mode `toml:"-"` } var dec Config if err := unmarshal(&dec); err != nil { @@ -140,22 +135,22 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error { c.GasPrice = dec.GasPrice } if dec.EthashCacheDir != nil { - c.EthashCacheDir = *dec.EthashCacheDir + c.Ethash.CacheDir = *dec.EthashCacheDir } if dec.EthashCachesInMem != nil { - c.EthashCachesInMem = *dec.EthashCachesInMem + c.Ethash.CachesInMem = *dec.EthashCachesInMem } if dec.EthashCachesOnDisk != nil { - c.EthashCachesOnDisk = *dec.EthashCachesOnDisk + c.Ethash.CachesOnDisk = *dec.EthashCachesOnDisk } if dec.EthashDatasetDir != nil { - c.EthashDatasetDir = *dec.EthashDatasetDir + c.Ethash.DatasetDir = *dec.EthashDatasetDir } if dec.EthashDatasetsInMem != nil { - c.EthashDatasetsInMem = *dec.EthashDatasetsInMem + c.Ethash.DatasetsInMem = *dec.EthashDatasetsInMem } if dec.EthashDatasetsOnDisk != nil { - c.EthashDatasetsOnDisk = *dec.EthashDatasetsOnDisk + c.Ethash.DatasetsOnDisk = *dec.EthashDatasetsOnDisk } if dec.TxPool != nil { c.TxPool = *dec.TxPool @@ -169,14 +164,8 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error { if dec.DocRoot != nil { c.DocRoot = *dec.DocRoot } - if dec.PowFake != nil { - c.PowFake = *dec.PowFake - } - if dec.PowTest != nil { - c.PowTest = *dec.PowTest - } - if dec.PowShared != nil { - c.PowShared = *dec.PowShared + if dec.PowMode != nil { + c.Ethash.PowMode = *dec.PowMode } return nil } |