diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go index 6f46fc1495..406ac5917b 100644 --- a/accounts/abi/bind/backends/simulated.go +++ b/accounts/abi/bind/backends/simulated.go @@ -35,8 +35,8 @@ import ( "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/database" "github.com/ethereum/go-ethereum/eth/filters" - "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" @@ -51,8 +51,8 @@ var errGasEstimationFailed = errors.New("gas required exceeds allowance or alway // SimulatedBackend implements bind.ContractBackend, simulating a blockchain in // the background. Its main purpose is to allow easily testing contract bindings. type SimulatedBackend struct { - database ethdb.Database // In memory database to store our testing data - blockchain *core.BlockChain // Ethereum blockchain to handle the consensus + database database.Database // In memory database to store our testing data + blockchain *core.BlockChain // Ethereum blockchain to handle the consensus mu sync.Mutex pendingBlock *types.Block // Currently pending block that will be imported on request @@ -66,7 +66,7 @@ type SimulatedBackend struct { // NewSimulatedBackend creates a new binding backend using a simulated blockchain // for testing purposes. func NewSimulatedBackend(alloc core.GenesisAlloc, gasLimit uint64) *SimulatedBackend { - database := ethdb.NewMemDatabase() + database := rawdb.NewMemoryDatabase() genesis := core.Genesis{Config: params.AllEthashProtocolChanges, GasLimit: gasLimit, Alloc: alloc} genesis.MustCommit(database) blockchain, _ := core.NewBlockChain(database, nil, genesis.Config, ethash.NewFaker(), vm.Config{}, nil) @@ -422,12 +422,12 @@ func (m callmsg) Data() []byte { return m.CallMsg.Data } // filterBackend implements filters.Backend to support filtering for logs without // taking bloom-bits acceleration structures into account. type filterBackend struct { - db ethdb.Database + db database.Database bc *core.BlockChain } -func (fb *filterBackend) ChainDb() ethdb.Database { return fb.db } -func (fb *filterBackend) EventMux() *event.TypeMux { panic("not supported") } +func (fb *filterBackend) ChainDb() database.Database { return fb.db } +func (fb *filterBackend) EventMux() *event.TypeMux { panic("not supported") } func (fb *filterBackend) HeaderByNumber(ctx context.Context, block rpc.BlockNumber) (*types.Header, error) { if block == rpc.LatestBlockNumber { diff --git a/cmd/evm/runner.go b/cmd/evm/runner.go index c732c8b574..bc5d00cfbe 100644 --- a/cmd/evm/runner.go +++ b/cmd/evm/runner.go @@ -31,10 +31,10 @@ import ( "github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm/runtime" - "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" cli "gopkg.in/urfave/cli.v1" @@ -99,12 +99,12 @@ func runCmd(ctx *cli.Context) error { if ctx.GlobalString(GenesisFlag.Name) != "" { gen := readGenesis(ctx.GlobalString(GenesisFlag.Name)) genesisConfig = gen - db := ethdb.NewMemDatabase() + db := rawdb.NewMemoryDatabase() genesis := gen.ToBlock(db) statedb, _ = state.New(genesis.Root(), state.NewDatabase(db)) chainConfig = gen.Config } else { - statedb, _ = state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) + statedb, _ = state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase())) genesisConfig = new(core.Genesis) } if ctx.GlobalString(SenderFlag.Name) != "" { diff --git a/cmd/geth/chaincmd.go b/cmd/geth/chaincmd.go index 6d41261f80..545250378c 100644 --- a/cmd/geth/chaincmd.go +++ b/cmd/geth/chaincmd.go @@ -29,14 +29,13 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/console" "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/eth/downloader" - "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/trie" - "github.com/syndtr/goleveldb/leveldb/util" "gopkg.in/urfave/cli.v1" ) @@ -193,7 +192,7 @@ func initGenesis(ctx *cli.Context) error { defer stack.Close() for _, name := range []string{"chaindata", "lightchaindata"} { - chaindb, err := stack.OpenDatabase(name, 0, 0) + chaindb, err := stack.OpenDatabase(name, 0, 0, "") if err != nil { utils.Fatalf("Failed to open database: %v", err) } @@ -201,6 +200,7 @@ func initGenesis(ctx *cli.Context) error { if err != nil { utils.Fatalf("Failed to write genesis block: %v", err) } + chaindb.Close() log.Info("Successfully wrote genesis state", "database", name, "hash", hash) } return nil @@ -213,8 +213,8 @@ func importChain(ctx *cli.Context) error { stack := makeFullNode(ctx) defer stack.Close() - chain, chainDb := utils.MakeChain(ctx, stack) - defer chainDb.Close() + chain, db := utils.MakeChain(ctx, stack) + defer db.Close() // Start periodically gathering memory profiles var peakMemAlloc, peakMemSys uint64 @@ -249,15 +249,13 @@ func importChain(ctx *cli.Context) error { fmt.Printf("Import done in %v.\n\n", time.Since(start)) // Output pre-compaction stats mostly to see the import trashing - db := chainDb.(*ethdb.LDBDatabase) - - stats, err := db.LDB().GetProperty("leveldb.stats") + stats, err := db.Stat("leveldb.stats") if err != nil { utils.Fatalf("Failed to read database stats: %v", err) } fmt.Println(stats) - ioStats, err := db.LDB().GetProperty("leveldb.iostats") + ioStats, err := db.Stat("leveldb.iostats") if err != nil { utils.Fatalf("Failed to read database iostats: %v", err) } @@ -280,24 +278,24 @@ func importChain(ctx *cli.Context) error { } // Compact the entire database to more accurately measure disk io and print the stats - start = time.Now() + /*start = time.Now() fmt.Println("Compacting entire database...") if err = db.LDB().CompactRange(util.Range{}); err != nil { utils.Fatalf("Compaction failed: %v", err) } fmt.Printf("Compaction done in %v.\n\n", time.Since(start)) - stats, err = db.LDB().GetProperty("leveldb.stats") + stats, err = db.Stat("leveldb.stats") if err != nil { utils.Fatalf("Failed to read database stats: %v", err) } fmt.Println(stats) - ioStats, err = db.LDB().GetProperty("leveldb.iostats") + ioStats, err = db.Stat("leveldb.iostats") if err != nil { utils.Fatalf("Failed to read database iostats: %v", err) } - fmt.Println(ioStats) + fmt.Println(ioStats)*/ return nil } @@ -344,10 +342,10 @@ func importPreimages(ctx *cli.Context) error { stack := makeFullNode(ctx) defer stack.Close() - diskdb := utils.MakeChainDatabase(ctx, stack).(*ethdb.LDBDatabase) + db := utils.MakeChainDatabase(ctx, stack) start := time.Now() - if err := utils.ImportPreimages(diskdb, ctx.Args().First()); err != nil { + if err := utils.ImportPreimages(db, ctx.Args().First()); err != nil { utils.Fatalf("Import error: %v\n", err) } fmt.Printf("Import done in %v\n", time.Since(start)) @@ -362,10 +360,10 @@ func exportPreimages(ctx *cli.Context) error { stack := makeFullNode(ctx) defer stack.Close() - diskdb := utils.MakeChainDatabase(ctx, stack).(*ethdb.LDBDatabase) + db := utils.MakeChainDatabase(ctx, stack) start := time.Now() - if err := utils.ExportPreimages(diskdb, ctx.Args().First()); err != nil { + if err := utils.ExportPreimages(db, ctx.Args().First()); err != nil { utils.Fatalf("Export error: %v\n", err) } fmt.Printf("Export done in %v\n", time.Since(start)) @@ -374,9 +372,12 @@ func exportPreimages(ctx *cli.Context) error { func copyDb(ctx *cli.Context) error { // Ensure we have a source chain directory to copy - if len(ctx.Args()) != 1 { + if len(ctx.Args()) < 1 { utils.Fatalf("Source chaindata directory path argument missing") } + if len(ctx.Args()) < 2 { + utils.Fatalf("Source ancient chain directory path argument missing") + } // Initialize a new chain for the running node to sync into stack := makeFullNode(ctx) defer stack.Close() @@ -386,7 +387,7 @@ func copyDb(ctx *cli.Context) error { dl := downloader.New(syncmode, chainDb, new(event.TypeMux), chain, nil, nil) // Create a source peer to satisfy downloader requests from - db, err := ethdb.NewLDBDatabase(ctx.Args().First(), ctx.GlobalInt(utils.CacheFlag.Name), 256) + db, err := rawdb.NewLeveldbDatabaseWithFreezer(ctx.Args().First(), ctx.GlobalInt(utils.CacheFlag.Name), 256, ctx.Args().Get(1), "") if err != nil { return err } @@ -411,12 +412,12 @@ func copyDb(ctx *cli.Context) error { fmt.Printf("Database copy done in %v\n", time.Since(start)) // Compact the entire database to remove any sync overhead - start = time.Now() + /*start = time.Now() fmt.Println("Compacting entire database...") if err = chainDb.(*ethdb.LDBDatabase).LDB().CompactRange(util.Range{}); err != nil { utils.Fatalf("Compaction failed: %v", err) } - fmt.Printf("Compaction done in %v.\n\n", time.Since(start)) + fmt.Printf("Compaction done in %v.\n\n", time.Since(start))*/ return nil } diff --git a/cmd/geth/dao_test.go b/cmd/geth/dao_test.go index 52983ff2af..dc75732806 100644 --- a/cmd/geth/dao_test.go +++ b/cmd/geth/dao_test.go @@ -25,7 +25,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/rawdb" - "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/params" ) @@ -121,7 +120,7 @@ func testDAOForkBlockNewChain(t *testing.T, test int, genesis string, expectBloc } // Retrieve the DAO config flag from the database path := filepath.Join(datadir, "geth", "chaindata") - db, err := ethdb.NewLDBDatabase(path, 0, 0) + db, err := rawdb.NewLeveldbDatabase(path, 0, 0, "") if err != nil { t.Fatalf("test %d: failed to open test database: %v", test, err) } diff --git a/cmd/geth/main.go b/cmd/geth/main.go index 17bf438e2a..2cee8cbc4e 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -61,6 +61,7 @@ var ( utils.BootnodesV4Flag, utils.BootnodesV5Flag, utils.DataDirFlag, + utils.AncientFlag, utils.KeyStoreDirFlag, utils.ExternalSignerFlag, utils.NoUSBFlag, diff --git a/cmd/geth/usage.go b/cmd/geth/usage.go index 0b5c6fd905..b003ebd2c5 100644 --- a/cmd/geth/usage.go +++ b/cmd/geth/usage.go @@ -69,6 +69,7 @@ var AppHelpFlagGroups = []flagGroup{ Flags: []cli.Flag{ configFileFlag, utils.DataDirFlag, + utils.AncientFlag, utils.KeyStoreDirFlag, utils.NoUSBFlag, utils.NetworkIdFlag, diff --git a/cmd/utils/cmd.go b/cmd/utils/cmd.go index f23aa5775b..f0383a275d 100644 --- a/cmd/utils/cmd.go +++ b/cmd/utils/cmd.go @@ -32,7 +32,7 @@ import ( "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/database" "github.com/ethereum/go-ethereum/internal/debug" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/node" @@ -238,7 +238,7 @@ func ExportAppendChain(blockchain *core.BlockChain, fn string, first uint64, las } // ImportPreimages imports a batch of exported hash preimages into the database. -func ImportPreimages(db *ethdb.LDBDatabase, fn string) error { +func ImportPreimages(db database.Database, fn string) error { log.Info("Importing preimages", "file", fn) // Open the file handle and potentially unwrap the gzip stream @@ -285,7 +285,7 @@ func ImportPreimages(db *ethdb.LDBDatabase, fn string) error { // ExportPreimages exports all known hash preimages into the specified file, // truncating any data already present in the file. -func ExportPreimages(db *ethdb.LDBDatabase, fn string) error { +func ExportPreimages(db database.Database, fn string) error { log.Info("Exporting preimages", "file", fn) // Open the file handle and potentially wrap with a gzip stream diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 66f533102e..482d3dcfe3 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -41,10 +41,10 @@ import ( "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/dashboard" + "github.com/ethereum/go-ethereum/database" "github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/eth/gasprice" - "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethstats" "github.com/ethereum/go-ethereum/les" "github.com/ethereum/go-ethereum/log" @@ -120,6 +120,10 @@ var ( Usage: "Data directory for the databases and keystore", Value: DirectoryString{node.DefaultDataDir()}, } + AncientFlag = DirectoryFlag{ + Name: "datadir.ancient", + Usage: "Data directory for ancient chain segments (default = inside chaindata)", + } KeyStoreDirFlag = DirectoryFlag{ Name: "keystore", Usage: "Directory for the keystore (default = inside the datadir)", @@ -1317,6 +1321,9 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) { cfg.DatabaseCache = ctx.GlobalInt(CacheFlag.Name) * ctx.GlobalInt(CacheDatabaseFlag.Name) / 100 } cfg.DatabaseHandles = makeDatabaseHandles() + if ctx.GlobalIsSet(AncientFlag.Name) { + cfg.DatabaseFreezer = ctx.GlobalString(AncientFlag.Name) + } if gcmode := ctx.GlobalString(GCModeFlag.Name); gcmode != "full" && gcmode != "archive" { Fatalf("--%s must be either 'full' or 'archive'", GCModeFlag.Name) @@ -1526,7 +1533,7 @@ func SplitTagsFlag(tagsFlag string) map[string]string { } // MakeChainDatabase open an LevelDB using the flags passed to the client and will hard crash if it fails. -func MakeChainDatabase(ctx *cli.Context, stack *node.Node) ethdb.Database { +func MakeChainDatabase(ctx *cli.Context, stack *node.Node) database.Database { var ( cache = ctx.GlobalInt(CacheFlag.Name) * ctx.GlobalInt(CacheDatabaseFlag.Name) / 100 handles = makeDatabaseHandles() @@ -1535,7 +1542,7 @@ func MakeChainDatabase(ctx *cli.Context, stack *node.Node) ethdb.Database { if ctx.GlobalString(SyncModeFlag.Name) == "light" { name = "lightchaindata" } - chainDb, err := stack.OpenDatabase(name, cache, handles) + chainDb, err := stack.OpenDatabase(name, cache, handles, "") if err != nil { Fatalf("Could not open database: %v", err) } @@ -1558,7 +1565,7 @@ func MakeGenesis(ctx *cli.Context) *core.Genesis { } // MakeChain creates a chain manager from set command line flags. -func MakeChain(ctx *cli.Context, stack *node.Node) (chain *core.BlockChain, chainDb ethdb.Database) { +func MakeChain(ctx *cli.Context, stack *node.Node) (chain *core.BlockChain, chainDb database.Database) { var err error chainDb = MakeChainDatabase(ctx, stack) config, _, err := core.SetupGenesisBlock(chainDb, MakeGenesis(ctx)) diff --git a/consensus/clique/clique.go b/consensus/clique/clique.go index 967a843dec..2c60ff3107 100644 --- a/consensus/clique/clique.go +++ b/consensus/clique/clique.go @@ -34,7 +34,7 @@ import ( "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/database" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" @@ -170,7 +170,7 @@ func ecrecover(header *types.Header, sigcache *lru.ARCCache) (common.Address, er // Ethereum testnet following the Ropsten attacks. type Clique struct { config *params.CliqueConfig // Consensus engine configuration parameters - db ethdb.Database // Database to store and retrieve snapshot checkpoints + db database.Database // Database to store and retrieve snapshot checkpoints recents *lru.ARCCache // Snapshots for recent block to speed up reorgs signatures *lru.ARCCache // Signatures of recent blocks to speed up mining @@ -187,7 +187,7 @@ type Clique struct { // New creates a Clique proof-of-authority consensus engine with the initial // signers set to the ones provided by the user. -func New(config *params.CliqueConfig, db ethdb.Database) *Clique { +func New(config *params.CliqueConfig, db database.Database) *Clique { // Set any missing consensus parameters to their defaults conf := *config if conf.Epoch == 0 { diff --git a/consensus/clique/snapshot.go b/consensus/clique/snapshot.go index 54d4555f3b..5270f8e390 100644 --- a/consensus/clique/snapshot.go +++ b/consensus/clique/snapshot.go @@ -23,7 +23,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/database" "github.com/ethereum/go-ethereum/params" lru "github.com/hashicorp/golang-lru" ) @@ -84,7 +84,7 @@ func newSnapshot(config *params.CliqueConfig, sigcache *lru.ARCCache, number uin } // loadSnapshot loads an existing snapshot from the database. -func loadSnapshot(config *params.CliqueConfig, sigcache *lru.ARCCache, db ethdb.Database, hash common.Hash) (*Snapshot, error) { +func loadSnapshot(config *params.CliqueConfig, sigcache *lru.ARCCache, db database.Database, hash common.Hash) (*Snapshot, error) { blob, err := db.Get(append([]byte("clique-"), hash[:]...)) if err != nil { return nil, err @@ -100,7 +100,7 @@ func loadSnapshot(config *params.CliqueConfig, sigcache *lru.ARCCache, db ethdb. } // store inserts the snapshot into the database. -func (s *Snapshot) store(db ethdb.Database) error { +func (s *Snapshot) store(db database.Database) error { blob, err := json.Marshal(s) if err != nil { return err diff --git a/consensus/clique/snapshot_test.go b/consensus/clique/snapshot_test.go index b920312a89..47c25aa040 100644 --- a/consensus/clique/snapshot_test.go +++ b/consensus/clique/snapshot_test.go @@ -24,10 +24,10 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/rawdb" "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/ethdb" "github.com/ethereum/go-ethereum/params" ) @@ -400,7 +400,7 @@ func TestClique(t *testing.T) { copy(genesis.ExtraData[extraVanity+j*common.AddressLength:], signer[:]) } // Create a pristine blockchain with the genesis injected - db := ethdb.NewMemDatabase() + db := rawdb.NewMemoryDatabase() genesis.Commit(db) // Assemble a chain of headers from the cast votes diff --git a/core/bench_test.go b/core/bench_test.go index 53cba05170..6defa98efc 100644 --- a/core/bench_test.go +++ b/core/bench_test.go @@ -30,7 +30,7 @@ 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/ethdb" + "github.com/ethereum/go-ethereum/database" "github.com/ethereum/go-ethereum/params" ) @@ -148,16 +148,16 @@ func genUncles(i int, gen *BlockGen) { func benchInsertChain(b *testing.B, disk bool, gen func(int, *BlockGen)) { // Create the database in memory or in a temporary directory. - var db ethdb.Database + var db database.Database if !disk { - db = ethdb.NewMemDatabase() + db = rawdb.NewMemoryDatabase() } else { dir, err := ioutil.TempDir("", "eth-core-bench") if err != nil { b.Fatalf("cannot create temporary directory: %v", err) } defer os.RemoveAll(dir) - db, err = ethdb.NewLDBDatabase(dir, 128, 128) + db, err = rawdb.NewLeveldbDatabase(dir, 128, 128, "") if err != nil { b.Fatalf("cannot create temporary database: %v", err) } @@ -223,7 +223,7 @@ func BenchmarkChainWrite_full_500k(b *testing.B) { // makeChainForBench writes a given number of headers or empty blocks/receipts // into a database. -func makeChainForBench(db ethdb.Database, full bool, count uint64) { +func makeChainForBench(db database.Database, full bool, count uint64) { var hash common.Hash for n := uint64(0); n < count; n++ { header := &types.Header{ @@ -255,7 +255,7 @@ func benchWriteChain(b *testing.B, full bool, count uint64) { if err != nil { b.Fatalf("cannot create temporary directory: %v", err) } - db, err := ethdb.NewLDBDatabase(dir, 128, 1024) + db, err := rawdb.NewLeveldbDatabase(dir, 128, 1024, "") if err != nil { b.Fatalf("error opening database at %v: %v", dir, err) } @@ -272,7 +272,7 @@ func benchReadChain(b *testing.B, full bool, count uint64) { } defer os.RemoveAll(dir) - db, err := ethdb.NewLDBDatabase(dir, 128, 1024) + db, err := rawdb.NewLeveldbDatabase(dir, 128, 1024, "") if err != nil { b.Fatalf("error opening database at %v: %v", dir, err) } @@ -283,7 +283,7 @@ func benchReadChain(b *testing.B, full bool, count uint64) { b.ResetTimer() for i := 0; i < b.N; i++ { - db, err := ethdb.NewLDBDatabase(dir, 128, 1024) + db, err := rawdb.NewLeveldbDatabase(dir, 128, 1024, "") if err != nil { b.Fatalf("error opening database at %v: %v", dir, err) } diff --git a/core/block_validator_test.go b/core/block_validator_test.go index 9319a7835d..06e2ba1a4f 100644 --- a/core/block_validator_test.go +++ b/core/block_validator_test.go @@ -22,9 +22,9 @@ import ( "time" "github.com/ethereum/go-ethereum/consensus/ethash" + "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/params" ) @@ -32,7 +32,7 @@ import ( func TestHeaderVerification(t *testing.T) { // Create a simple chain to verify var ( - testdb = ethdb.NewMemDatabase() + testdb = rawdb.NewMemoryDatabase() gspec = &Genesis{Config: params.TestChainConfig} genesis = gspec.MustCommit(testdb) blocks, _ = GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), testdb, 8, nil) @@ -84,7 +84,7 @@ func TestHeaderConcurrentVerification32(t *testing.T) { testHeaderConcurrentVeri func testHeaderConcurrentVerification(t *testing.T, threads int) { // Create a simple chain to verify var ( - testdb = ethdb.NewMemDatabase() + testdb = rawdb.NewMemoryDatabase() gspec = &Genesis{Config: params.TestChainConfig} genesis = gspec.MustCommit(testdb) blocks, _ = GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), testdb, 8, nil) @@ -156,7 +156,7 @@ func TestHeaderConcurrentAbortion32(t *testing.T) { testHeaderConcurrentAbortion func testHeaderConcurrentAbortion(t *testing.T, threads int) { // Create a simple chain to verify var ( - testdb = ethdb.NewMemDatabase() + testdb = rawdb.NewMemoryDatabase() gspec = &Genesis{Config: params.TestChainConfig} genesis = gspec.MustCommit(testdb) blocks, _ = GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), testdb, 1024, nil) diff --git a/core/blockchain.go b/core/blockchain.go index feb9fb9426..bf1c8dd182 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -36,7 +36,7 @@ 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/ethdb" + "github.com/ethereum/go-ethereum/database" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" @@ -95,9 +95,9 @@ type BlockChain struct { chainConfig *params.ChainConfig // Chain & network configuration cacheConfig *CacheConfig // Cache configuration for pruning - db ethdb.Database // Low level persistent database to store final content in - triegc *prque.Prque // Priority queue mapping block numbers to tries to gc - gcproc time.Duration // Accumulates canonical block processing for trie dumping + db database.Database // Low level persistent database to store final content in + triegc *prque.Prque // Priority queue mapping block numbers to tries to gc + gcproc time.Duration // Accumulates canonical block processing for trie dumping hc *HeaderChain rmLogsFeed event.Feed @@ -140,7 +140,7 @@ type BlockChain struct { // NewBlockChain returns a fully initialised block chain using information // available in the database. It initialises the default Ethereum Validator and // Processor. -func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *params.ChainConfig, engine consensus.Engine, vmConfig vm.Config, shouldPreserve func(block *types.Block) bool) (*BlockChain, error) { +func NewBlockChain(db database.Database, cacheConfig *CacheConfig, chainConfig *params.ChainConfig, engine consensus.Engine, vmConfig vm.Config, shouldPreserve func(block *types.Block) bool) (*BlockChain, error) { if cacheConfig == nil { cacheConfig = &CacheConfig{ TrieCleanLimit: 256, @@ -284,7 +284,7 @@ func (bc *BlockChain) SetHead(head uint64) error { defer bc.chainmu.Unlock() // Rewind the header chain, deleting all block bodies until then - delFn := func(db rawdb.DatabaseDeleter, hash common.Hash, num uint64) { + delFn := func(db database.Deleter, hash common.Hash, num uint64) { rawdb.DeleteBody(db, hash, num) } bc.hc.SetHead(head, delFn) @@ -866,7 +866,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [ stats.processed++ - if batch.ValueSize() >= ethdb.IdealBatchSize { + if batch.ValueSize() >= database.IdealBatchSize { if err := batch.Write(); err != nil { return 0, err } @@ -976,7 +976,7 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types. limit = common.StorageSize(bc.cacheConfig.TrieDirtyLimit) * 1024 * 1024 ) if nodes > limit || imgs > 4*1024*1024 { - triedb.Cap(limit - ethdb.IdealBatchSize) + triedb.Cap(limit - database.IdealBatchSize) } // Find the next state trie we need to commit chosen := current - triesInMemory diff --git a/core/blockchain_test.go b/core/blockchain_test.go index fa68c2894a..9fc7c344da 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -32,7 +32,7 @@ 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/ethdb" + "github.com/ethereum/go-ethereum/database" "github.com/ethereum/go-ethereum/params" ) @@ -45,9 +45,9 @@ var ( // newCanonical creates a chain database, and injects a deterministic canonical // chain. Depending on the full flag, if creates either a full block chain or a // header only chain. -func newCanonical(engine consensus.Engine, n int, full bool) (ethdb.Database, *BlockChain, error) { +func newCanonical(engine consensus.Engine, n int, full bool) (database.Database, *BlockChain, error) { var ( - db = ethdb.NewMemDatabase() + db = rawdb.NewMemoryDatabase() genesis = new(Genesis).MustCommit(db) ) @@ -603,7 +603,7 @@ func testInsertNonceError(t *testing.T, full bool) { func TestFastVsFullChains(t *testing.T) { // Configure and generate a sample block chain var ( - gendb = ethdb.NewMemDatabase() + gendb = rawdb.NewMemoryDatabase() key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") address = crypto.PubkeyToAddress(key.PublicKey) funds = big.NewInt(1000000000) @@ -633,7 +633,7 @@ func TestFastVsFullChains(t *testing.T) { } }) // Import the chain as an archive node for the comparison baseline - archiveDb := ethdb.NewMemDatabase() + archiveDb := rawdb.NewMemoryDatabase() gspec.MustCommit(archiveDb) archive, _ := NewBlockChain(archiveDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil) defer archive.Stop() @@ -642,7 +642,7 @@ func TestFastVsFullChains(t *testing.T) { t.Fatalf("failed to process block %d: %v", n, err) } // Fast import the chain as a non-archive node to test - fastDb := ethdb.NewMemDatabase() + fastDb := rawdb.NewMemoryDatabase() gspec.MustCommit(fastDb) fast, _ := NewBlockChain(fastDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil) defer fast.Stop() @@ -691,7 +691,7 @@ func TestFastVsFullChains(t *testing.T) { func TestLightVsFastVsFullChainHeads(t *testing.T) { // Configure and generate a sample block chain var ( - gendb = ethdb.NewMemDatabase() + gendb = rawdb.NewMemoryDatabase() key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") address = crypto.PubkeyToAddress(key.PublicKey) funds = big.NewInt(1000000000) @@ -719,7 +719,7 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) { } } // Import the chain as an archive node and ensure all pointers are updated - archiveDb := ethdb.NewMemDatabase() + archiveDb := rawdb.NewMemoryDatabase() gspec.MustCommit(archiveDb) archive, _ := NewBlockChain(archiveDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil) @@ -733,7 +733,7 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) { assert(t, "archive", archive, height/2, height/2, height/2) // Import the chain as a non-archive node and ensure all pointers are updated - fastDb := ethdb.NewMemDatabase() + fastDb := rawdb.NewMemoryDatabase() gspec.MustCommit(fastDb) fast, _ := NewBlockChain(fastDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil) defer fast.Stop() @@ -753,7 +753,7 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) { assert(t, "fast", fast, height/2, height/2, 0) // Import the chain as a light node and ensure all pointers are updated - lightDb := ethdb.NewMemDatabase() + lightDb := rawdb.NewMemoryDatabase() gspec.MustCommit(lightDb) light, _ := NewBlockChain(lightDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil) @@ -776,7 +776,7 @@ func TestChainTxReorgs(t *testing.T) { addr1 = crypto.PubkeyToAddress(key1.PublicKey) addr2 = crypto.PubkeyToAddress(key2.PublicKey) addr3 = crypto.PubkeyToAddress(key3.PublicKey) - db = ethdb.NewMemDatabase() + db = rawdb.NewMemoryDatabase() gspec = &Genesis{ Config: params.TestChainConfig, GasLimit: 3141592, @@ -888,7 +888,7 @@ func TestLogReorgs(t *testing.T) { var ( key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") addr1 = crypto.PubkeyToAddress(key1.PublicKey) - db = ethdb.NewMemDatabase() + db = rawdb.NewMemoryDatabase() // this code generates a log code = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00") gspec = &Genesis{Config: params.TestChainConfig, Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000)}}} @@ -932,7 +932,7 @@ func TestLogReorgs(t *testing.T) { func TestReorgSideEvent(t *testing.T) { var ( - db = ethdb.NewMemDatabase() + db = rawdb.NewMemoryDatabase() key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") addr1 = crypto.PubkeyToAddress(key1.PublicKey) gspec = &Genesis{ @@ -1060,7 +1060,7 @@ func TestCanonicalBlockRetrieval(t *testing.T) { func TestEIP155Transition(t *testing.T) { // Configure and generate a sample block chain var ( - db = ethdb.NewMemDatabase() + db = rawdb.NewMemoryDatabase() key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") address = crypto.PubkeyToAddress(key.PublicKey) funds = big.NewInt(1000000000) @@ -1163,7 +1163,7 @@ func TestEIP155Transition(t *testing.T) { func TestEIP161AccountRemoval(t *testing.T) { // Configure and generate a sample block chain var ( - db = ethdb.NewMemDatabase() + db = rawdb.NewMemoryDatabase() key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") address = crypto.PubkeyToAddress(key.PublicKey) funds = big.NewInt(1000000000) @@ -1235,7 +1235,7 @@ func TestBlockchainHeaderchainReorgConsistency(t *testing.T) { // Generate a canonical chain to act as the main dataset engine := ethash.NewFaker() - db := ethdb.NewMemDatabase() + db := rawdb.NewMemoryDatabase() genesis := new(Genesis).MustCommit(db) blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 64, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) }) @@ -1251,7 +1251,7 @@ func TestBlockchainHeaderchainReorgConsistency(t *testing.T) { } // Import the canonical and fork chain side by side, verifying the current block // and current header consistency - diskdb := ethdb.NewMemDatabase() + diskdb := rawdb.NewMemoryDatabase() new(Genesis).MustCommit(diskdb) chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil) @@ -1280,7 +1280,7 @@ func TestTrieForkGC(t *testing.T) { // Generate a canonical chain to act as the main dataset engine := ethash.NewFaker() - db := ethdb.NewMemDatabase() + db := rawdb.NewMemoryDatabase() genesis := new(Genesis).MustCommit(db) blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 2*triesInMemory, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) }) @@ -1295,7 +1295,7 @@ func TestTrieForkGC(t *testing.T) { forks[i] = fork[0] } // Import the canonical and fork chain side by side, forcing the trie cache to cache both - diskdb := ethdb.NewMemDatabase() + diskdb := rawdb.NewMemoryDatabase() new(Genesis).MustCommit(diskdb) chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil) @@ -1326,7 +1326,7 @@ func TestLargeReorgTrieGC(t *testing.T) { // Generate the original common chain segment and the two competing forks engine := ethash.NewFaker() - db := ethdb.NewMemDatabase() + db := rawdb.NewMemoryDatabase() genesis := new(Genesis).MustCommit(db) shared, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 64, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) }) @@ -1334,7 +1334,7 @@ func TestLargeReorgTrieGC(t *testing.T) { competitor, _ := GenerateChain(params.TestChainConfig, shared[len(shared)-1], engine, db, 2*triesInMemory+1, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{3}) }) // Import the shared chain and the original canonical one - diskdb := ethdb.NewMemDatabase() + diskdb := rawdb.NewMemoryDatabase() new(Genesis).MustCommit(diskdb) chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil) @@ -1394,7 +1394,7 @@ func benchmarkLargeNumberOfValueToNonexisting(b *testing.B, numTxs, numBlocks in ) // Generate the original common chain segment and the two competing forks engine := ethash.NewFaker() - db := ethdb.NewMemDatabase() + db := rawdb.NewMemoryDatabase() genesis := gspec.MustCommit(db) blockGenerator := func(i int, block *BlockGen) { @@ -1416,7 +1416,7 @@ func benchmarkLargeNumberOfValueToNonexisting(b *testing.B, numTxs, numBlocks in b.ResetTimer() for i := 0; i < b.N; i++ { // Import the shared chain and the original canonical one - diskdb := ethdb.NewMemDatabase() + diskdb := rawdb.NewMemoryDatabase() gspec.MustCommit(diskdb) chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil) @@ -1494,7 +1494,7 @@ func BenchmarkBlockChain_1x1000Executions(b *testing.B) { func TestLowDiffLongChain(t *testing.T) { // Generate a canonical chain to act as the main dataset engine := ethash.NewFaker() - db := ethdb.NewMemDatabase() + db := rawdb.NewMemoryDatabase() genesis := new(Genesis).MustCommit(db) // We must use a pretty long chain to ensure that the fork doesn't overtake us @@ -1505,7 +1505,7 @@ func TestLowDiffLongChain(t *testing.T) { }) // Import the canonical chain - diskdb := ethdb.NewMemDatabase() + diskdb := rawdb.NewMemoryDatabase() new(Genesis).MustCommit(diskdb) chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil) diff --git a/core/chain_indexer.go b/core/chain_indexer.go index 1adde1fcb4..2234dd9e81 100644 --- a/core/chain_indexer.go +++ b/core/chain_indexer.go @@ -27,7 +27,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/database" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/log" ) @@ -67,8 +67,8 @@ type ChainIndexerChain interface { // after an entire section has been finished or in case of rollbacks that might // affect already finished sections. type ChainIndexer struct { - chainDb ethdb.Database // Chain database to index the data from - indexDb ethdb.Database // Prefixed table-view of the db to write index metadata into + chainDb database.Database // Chain database to index the data from + indexDb database.Database // Prefixed table-view of the db to write index metadata into backend ChainIndexerBackend // Background processor generating the index data content children []*ChainIndexer // Child indexers to cascade chain updates to @@ -97,7 +97,7 @@ type ChainIndexer struct { // NewChainIndexer creates a new chain indexer to do background processing on // chain segments of a given size after certain number of confirmations passed. // The throttling parameter might be used to prevent database thrashing. -func NewChainIndexer(chainDb, indexDb ethdb.Database, backend ChainIndexerBackend, section, confirm uint64, throttling time.Duration, kind string) *ChainIndexer { +func NewChainIndexer(chainDb database.Database, indexDb database.Database, backend ChainIndexerBackend, section, confirm uint64, throttling time.Duration, kind string) *ChainIndexer { c := &ChainIndexer{ chainDb: chainDb, indexDb: indexDb, diff --git a/core/chain_indexer_test.go b/core/chain_indexer_test.go index a029dec626..abf5b3cc14 100644 --- a/core/chain_indexer_test.go +++ b/core/chain_indexer_test.go @@ -27,7 +27,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/ethdb" ) // Runs multiple tests with randomized parameters. @@ -49,7 +48,7 @@ func TestChainIndexerWithChildren(t *testing.T) { // multiple backends. The section size and required confirmation count parameters // are randomized. func testChainIndexer(t *testing.T, count int) { - db := ethdb.NewMemDatabase() + db := rawdb.NewMemoryDatabase() defer db.Close() // Create a chain of indexers and ensure they all report empty @@ -60,7 +59,7 @@ func testChainIndexer(t *testing.T, count int) { confirmsReq = uint64(rand.Intn(10)) ) backends[i] = &testChainIndexBackend{t: t, processCh: make(chan uint64)} - backends[i].indexer = NewChainIndexer(db, ethdb.NewTable(db, string([]byte{byte(i)})), backends[i], sectionSize, confirmsReq, 0, fmt.Sprintf("indexer-%d", i)) + backends[i].indexer = NewChainIndexer(db, rawdb.NewTable(db, string([]byte{byte(i)})), backends[i], sectionSize, confirmsReq, 0, fmt.Sprintf("indexer-%d", i)) if sections, _, _ := backends[i].indexer.Sections(); sections != 0 { t.Fatalf("Canonical section count mismatch: have %v, want %v", sections, 0) diff --git a/core/chain_makers.go b/core/chain_makers.go index 0b5a3d1843..48a215bb83 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -26,7 +26,7 @@ import ( "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/database" "github.com/ethereum/go-ethereum/params" ) @@ -169,7 +169,7 @@ func (b *BlockGen) OffsetTime(seconds int64) { // Blocks created by GenerateChain do not contain valid proof of work // values. Inserting them into BlockChain requires use of FakePow or // a similar non-validating proof of work implementation. -func GenerateChain(config *params.ChainConfig, parent *types.Block, engine consensus.Engine, db ethdb.Database, n int, gen func(int, *BlockGen)) ([]*types.Block, []types.Receipts) { +func GenerateChain(config *params.ChainConfig, parent *types.Block, engine consensus.Engine, db database.Database, n int, gen func(int, *BlockGen)) ([]*types.Block, []types.Receipts) { if config == nil { config = params.TestChainConfig } @@ -249,7 +249,7 @@ func makeHeader(chain consensus.ChainReader, parent *types.Block, state *state.S } // makeHeaderChain creates a deterministic chain of headers rooted at parent. -func makeHeaderChain(parent *types.Header, n int, engine consensus.Engine, db ethdb.Database, seed int) []*types.Header { +func makeHeaderChain(parent *types.Header, n int, engine consensus.Engine, db database.Database, seed int) []*types.Header { blocks := makeBlockChain(types.NewBlockWithHeader(parent), n, engine, db, seed) headers := make([]*types.Header, len(blocks)) for i, block := range blocks { @@ -259,7 +259,7 @@ func makeHeaderChain(parent *types.Header, n int, engine consensus.Engine, db et } // makeBlockChain creates a deterministic chain of blocks rooted at parent. -func makeBlockChain(parent *types.Block, n int, engine consensus.Engine, db ethdb.Database, seed int) []*types.Block { +func makeBlockChain(parent *types.Block, n int, engine consensus.Engine, db database.Database, seed int) []*types.Block { blocks, _ := GenerateChain(params.TestChainConfig, parent, engine, db, n, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{0: byte(seed), 19: byte(i)}) }) diff --git a/core/chain_makers_test.go b/core/chain_makers_test.go index 64b64fd6a3..32e3888d55 100644 --- a/core/chain_makers_test.go +++ b/core/chain_makers_test.go @@ -21,10 +21,10 @@ import ( "math/big" "github.com/ethereum/go-ethereum/consensus/ethash" + "github.com/ethereum/go-ethereum/core/rawdb" "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/ethdb" "github.com/ethereum/go-ethereum/params" ) @@ -36,7 +36,7 @@ func ExampleGenerateChain() { addr1 = crypto.PubkeyToAddress(key1.PublicKey) addr2 = crypto.PubkeyToAddress(key2.PublicKey) addr3 = crypto.PubkeyToAddress(key3.PublicKey) - db = ethdb.NewMemDatabase() + db = rawdb.NewMemoryDatabase() ) // Ensure that key1 has some funds in the genesis block. diff --git a/core/dao_test.go b/core/dao_test.go index 966139bce3..4e8dba9e84 100644 --- a/core/dao_test.go +++ b/core/dao_test.go @@ -21,8 +21,8 @@ import ( "testing" "github.com/ethereum/go-ethereum/consensus/ethash" + "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/params" ) @@ -32,13 +32,13 @@ func TestDAOForkRangeExtradata(t *testing.T) { forkBlock := big.NewInt(32) // Generate a common prefix for both pro-forkers and non-forkers - db := ethdb.NewMemDatabase() + db := rawdb.NewMemoryDatabase() gspec := new(Genesis) genesis := gspec.MustCommit(db) prefix, _ := GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, int(forkBlock.Int64()-1), func(i int, gen *BlockGen) {}) // Create the concurrent, conflicting two nodes - proDb := ethdb.NewMemDatabase() + proDb := rawdb.NewMemoryDatabase() gspec.MustCommit(proDb) proConf := *params.TestChainConfig @@ -48,7 +48,7 @@ func TestDAOForkRangeExtradata(t *testing.T) { proBc, _ := NewBlockChain(proDb, nil, &proConf, ethash.NewFaker(), vm.Config{}, nil) defer proBc.Stop() - conDb := ethdb.NewMemDatabase() + conDb := rawdb.NewMemoryDatabase() gspec.MustCommit(conDb) conConf := *params.TestChainConfig @@ -67,7 +67,7 @@ func TestDAOForkRangeExtradata(t *testing.T) { // Try to expand both pro-fork and non-fork chains iteratively with other camp's blocks for i := int64(0); i < params.DAOForkExtraRange.Int64(); i++ { // Create a pro-fork block, and try to feed into the no-fork chain - db = ethdb.NewMemDatabase() + db = rawdb.NewMemoryDatabase() gspec.MustCommit(db) bc, _ := NewBlockChain(db, nil, &conConf, ethash.NewFaker(), vm.Config{}, nil) defer bc.Stop() @@ -92,7 +92,7 @@ func TestDAOForkRangeExtradata(t *testing.T) { t.Fatalf("contra-fork chain didn't accepted no-fork block: %v", err) } // Create a no-fork block, and try to feed into the pro-fork chain - db = ethdb.NewMemDatabase() + db = rawdb.NewMemoryDatabase() gspec.MustCommit(db) bc, _ = NewBlockChain(db, nil, &proConf, ethash.NewFaker(), vm.Config{}, nil) defer bc.Stop() @@ -118,7 +118,7 @@ func TestDAOForkRangeExtradata(t *testing.T) { } } // Verify that contra-forkers accept pro-fork extra-datas after forking finishes - db = ethdb.NewMemDatabase() + db = rawdb.NewMemoryDatabase() gspec.MustCommit(db) bc, _ := NewBlockChain(db, nil, &conConf, ethash.NewFaker(), vm.Config{}, nil) defer bc.Stop() @@ -138,7 +138,7 @@ func TestDAOForkRangeExtradata(t *testing.T) { t.Fatalf("contra-fork chain didn't accept pro-fork block post-fork: %v", err) } // Verify that pro-forkers accept contra-fork extra-datas after forking finishes - db = ethdb.NewMemDatabase() + db = rawdb.NewMemoryDatabase() gspec.MustCommit(db) bc, _ = NewBlockChain(db, nil, &proConf, ethash.NewFaker(), vm.Config{}, nil) defer bc.Stop() diff --git a/core/genesis.go b/core/genesis.go index cbb6eecd28..c5576bcf91 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -31,7 +31,7 @@ import ( "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/database" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" @@ -134,7 +134,7 @@ type GenesisMismatchError struct { } func (e *GenesisMismatchError) Error() string { - return fmt.Sprintf("database already contains an incompatible genesis block (have %x, new %x)", e.Stored[:8], e.New[:8]) + return fmt.Sprintf("database contains incompatible genesis (have %x, new %x)", e.Stored, e.New) } // SetupGenesisBlock writes or updates the genesis block in db. @@ -150,10 +150,10 @@ func (e *GenesisMismatchError) Error() string { // error is a *params.ConfigCompatError and the new, unwritten config is returned. // // The returned chain configuration is never nil. -func SetupGenesisBlock(db ethdb.Database, genesis *Genesis) (*params.ChainConfig, common.Hash, error) { +func SetupGenesisBlock(db database.Database, genesis *Genesis) (*params.ChainConfig, common.Hash, error) { return SetupGenesisBlockWithOverride(db, genesis, nil) } -func SetupGenesisBlockWithOverride(db ethdb.Database, genesis *Genesis, constantinopleOverride *big.Int) (*params.ChainConfig, common.Hash, error) { +func SetupGenesisBlockWithOverride(db database.Database, genesis *Genesis, constantinopleOverride *big.Int) (*params.ChainConfig, common.Hash, error) { if genesis != nil && genesis.Config == nil { return params.AllEthashProtocolChanges, common.Hash{}, errGenesisNoConfig } @@ -226,9 +226,9 @@ func (g *Genesis) configOrDefault(ghash common.Hash) *params.ChainConfig { // ToBlock creates the genesis block and writes state of a genesis specification // to the given database (or discards it if nil). -func (g *Genesis) ToBlock(db ethdb.Database) *types.Block { +func (g *Genesis) ToBlock(db database.Database) *types.Block { if db == nil { - db = ethdb.NewMemDatabase() + db = rawdb.NewMemoryDatabase() } statedb, _ := state.New(common.Hash{}, state.NewDatabase(db)) for addr, account := range g.Alloc { @@ -267,7 +267,7 @@ func (g *Genesis) ToBlock(db ethdb.Database) *types.Block { // Commit writes the block and state of a genesis specification to the database. // The block is committed as the canonical head block. -func (g *Genesis) Commit(db ethdb.Database) (*types.Block, error) { +func (g *Genesis) Commit(db database.Database) (*types.Block, error) { block := g.ToBlock(db) if block.Number().Sign() != 0 { return nil, fmt.Errorf("can't commit genesis block with number > 0") @@ -289,7 +289,7 @@ func (g *Genesis) Commit(db ethdb.Database) (*types.Block, error) { // MustCommit writes the genesis block and state to db, panicking on error. // The block is committed as the canonical head block. -func (g *Genesis) MustCommit(db ethdb.Database) *types.Block { +func (g *Genesis) MustCommit(db database.Database) *types.Block { block, err := g.Commit(db) if err != nil { panic(err) @@ -298,7 +298,7 @@ func (g *Genesis) MustCommit(db ethdb.Database) *types.Block { } // GenesisBlockForTesting creates and writes a block in which addr has the given wei balance. -func GenesisBlockForTesting(db ethdb.Database, addr common.Address, balance *big.Int) *types.Block { +func GenesisBlockForTesting(db database.Database, addr common.Address, balance *big.Int) *types.Block { g := Genesis{Alloc: GenesisAlloc{addr: {Balance: balance}}} return g.MustCommit(db) } diff --git a/core/genesis_test.go b/core/genesis_test.go index c7d54f2057..aa5ecb4bcb 100644 --- a/core/genesis_test.go +++ b/core/genesis_test.go @@ -26,7 +26,7 @@ import ( "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/database" "github.com/ethereum/go-ethereum/params" ) @@ -55,14 +55,14 @@ func TestSetupGenesis(t *testing.T) { oldcustomg.Config = ¶ms.ChainConfig{HomesteadBlock: big.NewInt(2)} tests := []struct { name string - fn func(ethdb.Database) (*params.ChainConfig, common.Hash, error) + fn func(database.Database) (*params.ChainConfig, common.Hash, error) wantConfig *params.ChainConfig wantHash common.Hash wantErr error }{ { name: "genesis without ChainConfig", - fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) { + fn: func(db database.Database) (*params.ChainConfig, common.Hash, error) { return SetupGenesisBlock(db, new(Genesis)) }, wantErr: errGenesisNoConfig, @@ -70,7 +70,7 @@ func TestSetupGenesis(t *testing.T) { }, { name: "no block in DB, genesis == nil", - fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) { + fn: func(db database.Database) (*params.ChainConfig, common.Hash, error) { return SetupGenesisBlock(db, nil) }, wantHash: params.MainnetGenesisHash, @@ -78,7 +78,7 @@ func TestSetupGenesis(t *testing.T) { }, { name: "mainnet block in DB, genesis == nil", - fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) { + fn: func(db database.Database) (*params.ChainConfig, common.Hash, error) { DefaultGenesisBlock().MustCommit(db) return SetupGenesisBlock(db, nil) }, @@ -87,7 +87,7 @@ func TestSetupGenesis(t *testing.T) { }, { name: "custom block in DB, genesis == nil", - fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) { + fn: func(db database.Database) (*params.ChainConfig, common.Hash, error) { customg.MustCommit(db) return SetupGenesisBlock(db, nil) }, @@ -96,7 +96,7 @@ func TestSetupGenesis(t *testing.T) { }, { name: "custom block in DB, genesis == testnet", - fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) { + fn: func(db database.Database) (*params.ChainConfig, common.Hash, error) { customg.MustCommit(db) return SetupGenesisBlock(db, DefaultTestnetGenesisBlock()) }, @@ -106,7 +106,7 @@ func TestSetupGenesis(t *testing.T) { }, { name: "compatible config in DB", - fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) { + fn: func(db database.Database) (*params.ChainConfig, common.Hash, error) { oldcustomg.MustCommit(db) return SetupGenesisBlock(db, &customg) }, @@ -115,7 +115,7 @@ func TestSetupGenesis(t *testing.T) { }, { name: "incompatible config in DB", - fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) { + fn: func(db database.Database) (*params.ChainConfig, common.Hash, error) { // Commit the 'old' genesis block with Homestead transition at #2. // Advance to block #4, past the homestead transition block of customg. genesis := oldcustomg.MustCommit(db) @@ -141,7 +141,7 @@ func TestSetupGenesis(t *testing.T) { } for _, test := range tests { - db := ethdb.NewMemDatabase() + db := rawdb.NewMemoryDatabase() config, hash, err := test.fn(db) // Check the return values. if !reflect.DeepEqual(err, test.wantErr) { diff --git a/core/headerchain.go b/core/headerchain.go index 8904dd887b..4909aa471e 100644 --- a/core/headerchain.go +++ b/core/headerchain.go @@ -30,7 +30,7 @@ import ( "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/database" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" "github.com/hashicorp/golang-lru" @@ -50,7 +50,7 @@ const ( type HeaderChain struct { config *params.ChainConfig - chainDb ethdb.Database + chainDb database.Database genesisHeader *types.Header currentHeader atomic.Value // Current head of the header chain (may be above the block chain!) @@ -70,7 +70,7 @@ type HeaderChain struct { // getValidator should return the parent's validator // procInterrupt points to the parent's interrupt semaphore // wg points to the parent's shutdown wait group -func NewHeaderChain(chainDb ethdb.Database, config *params.ChainConfig, engine consensus.Engine, procInterrupt func() bool) (*HeaderChain, error) { +func NewHeaderChain(chainDb database.Database, config *params.ChainConfig, engine consensus.Engine, procInterrupt func() bool) (*HeaderChain, error) { headerCache, _ := lru.New(headerCacheLimit) tdCache, _ := lru.New(tdCacheLimit) numberCache, _ := lru.New(numberCacheLimit) @@ -455,7 +455,7 @@ func (hc *HeaderChain) SetCurrentHeader(head *types.Header) { // DeleteCallback is a callback function that is called by SetHead before // each header is deleted. -type DeleteCallback func(rawdb.DatabaseDeleter, common.Hash, uint64) +type DeleteCallback func(database.Deleter, common.Hash, uint64) // SetHead rewinds the local chain to a new head. Everything above the new head // will be deleted and the new one set. diff --git a/core/helper_test.go b/core/helper_test.go index 051384d854..e55609fc3b 100644 --- a/core/helper_test.go +++ b/core/helper_test.go @@ -19,8 +19,9 @@ package core import ( "container/list" + "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/database" "github.com/ethereum/go-ethereum/event" ) @@ -29,7 +30,7 @@ type TestManager struct { // stateManager *StateManager eventMux *event.TypeMux - db ethdb.Database + db database.Database txPool *TxPool blockChain *BlockChain Blocks []*types.Block @@ -71,14 +72,14 @@ func (tm *TestManager) EventMux() *event.TypeMux { // return nil // } -func (tm *TestManager) Db() ethdb.Database { +func (tm *TestManager) Db() database.Database { return tm.db } func NewTestManager() *TestManager { testManager := &TestManager{} testManager.eventMux = new(event.TypeMux) - testManager.db = ethdb.NewMemDatabase() + testManager.db = rawdb.NewMemoryDatabase() // testManager.txPool = NewTxPool(testManager) // testManager.blockChain = NewBlockChain(testManager) // testManager.stateManager = NewStateManager(testManager) diff --git a/core/rawdb/accessors_chain.go b/core/rawdb/accessors_chain.go index 491a125c65..0a362dd09a 100644 --- a/core/rawdb/accessors_chain.go +++ b/core/rawdb/accessors_chain.go @@ -23,13 +23,17 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/database" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/rlp" ) // ReadCanonicalHash retrieves the hash assigned to a canonical block number. -func ReadCanonicalHash(db DatabaseReader, number uint64) common.Hash { - data, _ := db.Get(headerHashKey(number)) +func ReadCanonicalHash(db database.AncientReader, number uint64) common.Hash { + data, _ := db.Ancient("hashes", number) + if len(data) == 0 { + data, _ = db.Get(headerHashKey(number)) + } if len(data) == 0 { return common.Hash{} } @@ -37,21 +41,39 @@ func ReadCanonicalHash(db DatabaseReader, number uint64) common.Hash { } // WriteCanonicalHash stores the hash assigned to a canonical block number. -func WriteCanonicalHash(db DatabaseWriter, hash common.Hash, number uint64) { +func WriteCanonicalHash(db database.Writer, hash common.Hash, number uint64) { if err := db.Put(headerHashKey(number), hash.Bytes()); err != nil { log.Crit("Failed to store number to hash mapping", "err", err) } } // DeleteCanonicalHash removes the number to hash canonical mapping. -func DeleteCanonicalHash(db DatabaseDeleter, number uint64) { +func DeleteCanonicalHash(db database.Deleter, number uint64) { if err := db.Delete(headerHashKey(number)); err != nil { log.Crit("Failed to delete number to hash mapping", "err", err) } } +// readAllHashes retrieves all the hashes assigned to blocks at a certain heights, +// both canonical and reorged forks included. +// +// This method is a helper for the chain reader. It should never be exposed to the +// outside world. +func readAllHashes(db database.Iteratorer, number uint64) []common.Hash { + prefix := headerKeyPrefix(number) + + hashes := make([]common.Hash, 0, 1) + it := db.NewIteratorWithPrefix(prefix) + for it.Next() { + if key := it.Key(); len(key) == len(prefix)+32 { + hashes = append(hashes, common.BytesToHash(key[len(key)-32:])) + } + } + return hashes +} + // ReadHeaderNumber returns the header number assigned to a hash. -func ReadHeaderNumber(db DatabaseReader, hash common.Hash) *uint64 { +func ReadHeaderNumber(db database.Reader, hash common.Hash) *uint64 { data, _ := db.Get(headerNumberKey(hash)) if len(data) != 8 { return nil @@ -61,7 +83,7 @@ func ReadHeaderNumber(db DatabaseReader, hash common.Hash) *uint64 { } // ReadHeadHeaderHash retrieves the hash of the current canonical head header. -func ReadHeadHeaderHash(db DatabaseReader) common.Hash { +func ReadHeadHeaderHash(db database.Reader) common.Hash { data, _ := db.Get(headHeaderKey) if len(data) == 0 { return common.Hash{} @@ -70,14 +92,14 @@ func ReadHeadHeaderHash(db DatabaseReader) common.Hash { } // WriteHeadHeaderHash stores the hash of the current canonical head header. -func WriteHeadHeaderHash(db DatabaseWriter, hash common.Hash) { +func WriteHeadHeaderHash(db database.Writer, hash common.Hash) { if err := db.Put(headHeaderKey, hash.Bytes()); err != nil { log.Crit("Failed to store last header's hash", "err", err) } } // ReadHeadBlockHash retrieves the hash of the current canonical head block. -func ReadHeadBlockHash(db DatabaseReader) common.Hash { +func ReadHeadBlockHash(db database.Reader) common.Hash { data, _ := db.Get(headBlockKey) if len(data) == 0 { return common.Hash{} @@ -86,14 +108,14 @@ func ReadHeadBlockHash(db DatabaseReader) common.Hash { } // WriteHeadBlockHash stores the head block's hash. -func WriteHeadBlockHash(db DatabaseWriter, hash common.Hash) { +func WriteHeadBlockHash(db database.Writer, hash common.Hash) { if err := db.Put(headBlockKey, hash.Bytes()); err != nil { log.Crit("Failed to store last block's hash", "err", err) } } // ReadHeadFastBlockHash retrieves the hash of the current fast-sync head block. -func ReadHeadFastBlockHash(db DatabaseReader) common.Hash { +func ReadHeadFastBlockHash(db database.Reader) common.Hash { data, _ := db.Get(headFastBlockKey) if len(data) == 0 { return common.Hash{} @@ -102,7 +124,7 @@ func ReadHeadFastBlockHash(db DatabaseReader) common.Hash { } // WriteHeadFastBlockHash stores the hash of the current fast-sync head block. -func WriteHeadFastBlockHash(db DatabaseWriter, hash common.Hash) { +func WriteHeadFastBlockHash(db database.Writer, hash common.Hash) { if err := db.Put(headFastBlockKey, hash.Bytes()); err != nil { log.Crit("Failed to store last fast block's hash", "err", err) } @@ -110,7 +132,7 @@ func WriteHeadFastBlockHash(db DatabaseWriter, hash common.Hash) { // ReadFastTrieProgress retrieves the number of tries nodes fast synced to allow // reporting correct numbers across restarts. -func ReadFastTrieProgress(db DatabaseReader) uint64 { +func ReadFastTrieProgress(db database.Reader) uint64 { data, _ := db.Get(fastTrieProgressKey) if len(data) == 0 { return 0 @@ -120,20 +142,26 @@ func ReadFastTrieProgress(db DatabaseReader) uint64 { // WriteFastTrieProgress stores the fast sync trie process counter to support // retrieving it across restarts. -func WriteFastTrieProgress(db DatabaseWriter, count uint64) { +func WriteFastTrieProgress(db database.Writer, count uint64) { if err := db.Put(fastTrieProgressKey, new(big.Int).SetUint64(count).Bytes()); err != nil { log.Crit("Failed to store fast sync trie progress", "err", err) } } // ReadHeaderRLP retrieves a block header in its raw RLP database encoding. -func ReadHeaderRLP(db DatabaseReader, hash common.Hash, number uint64) rlp.RawValue { - data, _ := db.Get(headerKey(number, hash)) +func ReadHeaderRLP(db database.AncientReader, hash common.Hash, number uint64) rlp.RawValue { + data, _ := db.Ancient("headers", number) + if len(data) == 0 { + data, _ = db.Get(headerKey(number, hash)) + } return data } // HasHeader verifies the existence of a block header corresponding to the hash. -func HasHeader(db DatabaseReader, hash common.Hash, number uint64) bool { +func HasHeader(db database.AncientReader, hash common.Hash, number uint64) bool { + if has, err := db.Ancient("hashes", number); err == nil && common.BytesToHash(has) == hash { + return true + } if has, err := db.Has(headerKey(number, hash)); !has || err != nil { return false } @@ -141,7 +169,7 @@ func HasHeader(db DatabaseReader, hash common.Hash, number uint64) bool { } // ReadHeader retrieves the block header corresponding to the hash. -func ReadHeader(db DatabaseReader, hash common.Hash, number uint64) *types.Header { +func ReadHeader(db database.AncientReader, hash common.Hash, number uint64) *types.Header { data := ReadHeaderRLP(db, hash, number) if len(data) == 0 { return nil @@ -156,7 +184,7 @@ func ReadHeader(db DatabaseReader, hash common.Hash, number uint64) *types.Heade // WriteHeader stores a block header into the database and also stores the hash- // to-number mapping. -func WriteHeader(db DatabaseWriter, header *types.Header) { +func WriteHeader(db database.Writer, header *types.Header) { // Write the hash -> number mapping var ( hash = header.Hash() @@ -179,30 +207,42 @@ func WriteHeader(db DatabaseWriter, header *types.Header) { } // DeleteHeader removes all block header data associated with a hash. -func DeleteHeader(db DatabaseDeleter, hash common.Hash, number uint64) { - if err := db.Delete(headerKey(number, hash)); err != nil { - log.Crit("Failed to delete header", "err", err) - } +func DeleteHeader(db database.Deleter, hash common.Hash, number uint64) { + deleteHeaderWithoutNumber(db, hash, number) if err := db.Delete(headerNumberKey(hash)); err != nil { log.Crit("Failed to delete hash to number mapping", "err", err) } } +// deleteHeaderWithoutNumber removes only the block header but does not remove +// the hash to number mapping. +func deleteHeaderWithoutNumber(db database.Deleter, hash common.Hash, number uint64) { + if err := db.Delete(headerKey(number, hash)); err != nil { + log.Crit("Failed to delete header", "err", err) + } +} + // ReadBodyRLP retrieves the block body (transactions and uncles) in RLP encoding. -func ReadBodyRLP(db DatabaseReader, hash common.Hash, number uint64) rlp.RawValue { - data, _ := db.Get(blockBodyKey(number, hash)) +func ReadBodyRLP(db database.AncientReader, hash common.Hash, number uint64) rlp.RawValue { + data, _ := db.Ancient("bodies", number) + if len(data) == 0 { + data, _ = db.Get(blockBodyKey(number, hash)) + } return data } // WriteBodyRLP stores an RLP encoded block body into the database. -func WriteBodyRLP(db DatabaseWriter, hash common.Hash, number uint64, rlp rlp.RawValue) { +func WriteBodyRLP(db database.Writer, hash common.Hash, number uint64, rlp rlp.RawValue) { if err := db.Put(blockBodyKey(number, hash), rlp); err != nil { log.Crit("Failed to store block body", "err", err) } } // HasBody verifies the existence of a block body corresponding to the hash. -func HasBody(db DatabaseReader, hash common.Hash, number uint64) bool { +func HasBody(db database.AncientReader, hash common.Hash, number uint64) bool { + if has, err := db.Ancient("hashes", number); err == nil && common.BytesToHash(has) == hash { + return true + } if has, err := db.Has(blockBodyKey(number, hash)); !has || err != nil { return false } @@ -210,7 +250,7 @@ func HasBody(db DatabaseReader, hash common.Hash, number uint64) bool { } // ReadBody retrieves the block body corresponding to the hash. -func ReadBody(db DatabaseReader, hash common.Hash, number uint64) *types.Body { +func ReadBody(db database.AncientReader, hash common.Hash, number uint64) *types.Body { data := ReadBodyRLP(db, hash, number) if len(data) == 0 { return nil @@ -224,7 +264,7 @@ func ReadBody(db DatabaseReader, hash common.Hash, number uint64) *types.Body { } // WriteBody storea a block body into the database. -func WriteBody(db DatabaseWriter, hash common.Hash, number uint64, body *types.Body) { +func WriteBody(db database.Writer, hash common.Hash, number uint64, body *types.Body) { data, err := rlp.EncodeToBytes(body) if err != nil { log.Crit("Failed to RLP encode body", "err", err) @@ -233,15 +273,24 @@ func WriteBody(db DatabaseWriter, hash common.Hash, number uint64, body *types.B } // DeleteBody removes all block body data associated with a hash. -func DeleteBody(db DatabaseDeleter, hash common.Hash, number uint64) { +func DeleteBody(db database.Deleter, hash common.Hash, number uint64) { if err := db.Delete(blockBodyKey(number, hash)); err != nil { log.Crit("Failed to delete block body", "err", err) } } +// ReadTdRLP retrieves a block's total difficulty corresponding to the hash in RLP encoding. +func ReadTdRLP(db database.AncientReader, hash common.Hash, number uint64) rlp.RawValue { + data, _ := db.Ancient("diffs", number) + if len(data) == 0 { + data, _ = db.Get(headerTDKey(number, hash)) + } + return data +} + // ReadTd retrieves a block's total difficulty corresponding to the hash. -func ReadTd(db DatabaseReader, hash common.Hash, number uint64) *big.Int { - data, _ := db.Get(headerTDKey(number, hash)) +func ReadTd(db database.AncientReader, hash common.Hash, number uint64) *big.Int { + data := ReadTdRLP(db, hash, number) if len(data) == 0 { return nil } @@ -254,7 +303,7 @@ func ReadTd(db DatabaseReader, hash common.Hash, number uint64) *big.Int { } // WriteTd stores the total difficulty of a block into the database. -func WriteTd(db DatabaseWriter, hash common.Hash, number uint64, td *big.Int) { +func WriteTd(db database.Writer, hash common.Hash, number uint64, td *big.Int) { data, err := rlp.EncodeToBytes(td) if err != nil { log.Crit("Failed to RLP encode block total difficulty", "err", err) @@ -265,7 +314,7 @@ func WriteTd(db DatabaseWriter, hash common.Hash, number uint64, td *big.Int) { } // DeleteTd removes all block total difficulty data associated with a hash. -func DeleteTd(db DatabaseDeleter, hash common.Hash, number uint64) { +func DeleteTd(db database.Deleter, hash common.Hash, number uint64) { if err := db.Delete(headerTDKey(number, hash)); err != nil { log.Crit("Failed to delete block total difficulty", "err", err) } @@ -273,17 +322,29 @@ func DeleteTd(db DatabaseDeleter, hash common.Hash, number uint64) { // HasReceipts verifies the existence of all the transaction receipts belonging // to a block. -func HasReceipts(db DatabaseReader, hash common.Hash, number uint64) bool { +func HasReceipts(db database.AncientReader, hash common.Hash, number uint64) bool { + if has, err := db.Ancient("hashes", number); err == nil && common.BytesToHash(has) == hash { + return true + } if has, err := db.Has(blockReceiptsKey(number, hash)); !has || err != nil { return false } return true } +// ReadReceiptsRLP retrieves all the transaction receipts belonging to a block in RLP encoding. +func ReadReceiptsRLP(db database.AncientReader, hash common.Hash, number uint64) rlp.RawValue { + data, _ := db.Ancient("receipts", number) + if len(data) == 0 { + data, _ = db.Get(blockReceiptsKey(number, hash)) + } + return data +} + // ReadReceipts retrieves all the transaction receipts belonging to a block. -func ReadReceipts(db DatabaseReader, hash common.Hash, number uint64) types.Receipts { +func ReadReceipts(db database.AncientReader, hash common.Hash, number uint64) types.Receipts { // Retrieve the flattened receipt slice - data, _ := db.Get(blockReceiptsKey(number, hash)) + data := ReadReceiptsRLP(db, hash, number) if len(data) == 0 { return nil } @@ -301,7 +362,7 @@ func ReadReceipts(db DatabaseReader, hash common.Hash, number uint64) types.Rece } // WriteReceipts stores all the transaction receipts belonging to a block. -func WriteReceipts(db DatabaseWriter, hash common.Hash, number uint64, receipts types.Receipts) { +func WriteReceipts(db database.Writer, hash common.Hash, number uint64, receipts types.Receipts) { // Convert the receipts into their storage form and serialize them storageReceipts := make([]*types.ReceiptForStorage, len(receipts)) for i, receipt := range receipts { @@ -318,7 +379,7 @@ func WriteReceipts(db DatabaseWriter, hash common.Hash, number uint64, receipts } // DeleteReceipts removes all receipt data associated with a block hash. -func DeleteReceipts(db DatabaseDeleter, hash common.Hash, number uint64) { +func DeleteReceipts(db database.Deleter, hash common.Hash, number uint64) { if err := db.Delete(blockReceiptsKey(number, hash)); err != nil { log.Crit("Failed to delete block receipts", "err", err) } @@ -330,7 +391,7 @@ func DeleteReceipts(db DatabaseDeleter, hash common.Hash, number uint64) { // // Note, due to concurrent download of header and block body the header and thus // canonical hash can be stored in the database but the body data not (yet). -func ReadBlock(db DatabaseReader, hash common.Hash, number uint64) *types.Block { +func ReadBlock(db database.AncientReader, hash common.Hash, number uint64) *types.Block { header := ReadHeader(db, hash, number) if header == nil { return nil @@ -343,21 +404,30 @@ func ReadBlock(db DatabaseReader, hash common.Hash, number uint64) *types.Block } // WriteBlock serializes a block into the database, header and body separately. -func WriteBlock(db DatabaseWriter, block *types.Block) { +func WriteBlock(db database.Writer, block *types.Block) { WriteBody(db, block.Hash(), block.NumberU64(), block.Body()) WriteHeader(db, block.Header()) } // DeleteBlock removes all block data associated with a hash. -func DeleteBlock(db DatabaseDeleter, hash common.Hash, number uint64) { +func DeleteBlock(db database.Deleter, hash common.Hash, number uint64) { DeleteReceipts(db, hash, number) DeleteHeader(db, hash, number) DeleteBody(db, hash, number) DeleteTd(db, hash, number) } +// deleteBlockWithoutNumber removes all block data associated with a hash, except +// the hash to number mapping. +func deleteBlockWithoutNumber(db database.Deleter, hash common.Hash, number uint64) { + DeleteReceipts(db, hash, number) + deleteHeaderWithoutNumber(db, hash, number) + DeleteBody(db, hash, number) + DeleteTd(db, hash, number) +} + // FindCommonAncestor returns the last common ancestor of two block headers -func FindCommonAncestor(db DatabaseReader, a, b *types.Header) *types.Header { +func FindCommonAncestor(db database.AncientReader, a, b *types.Header) *types.Header { for bn := b.Number.Uint64(); a.Number.Uint64() > bn; { a = ReadHeader(db, a.ParentHash, a.Number.Uint64()-1) if a == nil { diff --git a/core/rawdb/accessors_chain_test.go b/core/rawdb/accessors_chain_test.go index fcc36dc2b4..c3b51c7ecc 100644 --- a/core/rawdb/accessors_chain_test.go +++ b/core/rawdb/accessors_chain_test.go @@ -23,14 +23,13 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/rlp" "golang.org/x/crypto/sha3" ) // Tests block header storage and retrieval operations. func TestHeaderStorage(t *testing.T) { - db := ethdb.NewMemDatabase() + db := NewMemoryDatabase() // Create a test header to move around the database and make sure it's really new header := &types.Header{Number: big.NewInt(42), Extra: []byte("test header")} @@ -63,7 +62,7 @@ func TestHeaderStorage(t *testing.T) { // Tests block body storage and retrieval operations. func TestBodyStorage(t *testing.T) { - db := ethdb.NewMemDatabase() + db := NewMemoryDatabase() // Create a test body to move around the database and make sure it's really new body := &types.Body{Uncles: []*types.Header{{Extra: []byte("test header")}}} @@ -101,7 +100,7 @@ func TestBodyStorage(t *testing.T) { // Tests block storage and retrieval operations. func TestBlockStorage(t *testing.T) { - db := ethdb.NewMemDatabase() + db := NewMemoryDatabase() // Create a test block to move around the database and make sure it's really new block := types.NewBlockWithHeader(&types.Header{ @@ -151,7 +150,7 @@ func TestBlockStorage(t *testing.T) { // Tests that partial block contents don't get reassembled into full blocks. func TestPartialBlockStorage(t *testing.T) { - db := ethdb.NewMemDatabase() + db := NewMemoryDatabase() block := types.NewBlockWithHeader(&types.Header{ Extra: []byte("test block"), UncleHash: types.EmptyUncleHash, @@ -185,7 +184,7 @@ func TestPartialBlockStorage(t *testing.T) { // Tests block total difficulty storage and retrieval operations. func TestTdStorage(t *testing.T) { - db := ethdb.NewMemDatabase() + db := NewMemoryDatabase() // Create a test TD to move around the database and make sure it's really new hash, td := common.Hash{}, big.NewInt(314) @@ -208,7 +207,7 @@ func TestTdStorage(t *testing.T) { // Tests that canonical numbers can be mapped to hashes and retrieved. func TestCanonicalMappingStorage(t *testing.T) { - db := ethdb.NewMemDatabase() + db := NewMemoryDatabase() // Create a test canonical number and assinged hash to move around hash, number := common.Hash{0: 0xff}, uint64(314) @@ -231,7 +230,7 @@ func TestCanonicalMappingStorage(t *testing.T) { // Tests that head headers and head blocks can be assigned, individually. func TestHeadStorage(t *testing.T) { - db := ethdb.NewMemDatabase() + db := NewMemoryDatabase() blockHead := types.NewBlockWithHeader(&types.Header{Extra: []byte("test block header")}) blockFull := types.NewBlockWithHeader(&types.Header{Extra: []byte("test block full")}) @@ -266,7 +265,7 @@ func TestHeadStorage(t *testing.T) { // Tests that receipts associated with a single block can be stored and retrieved. func TestBlockReceiptStorage(t *testing.T) { - db := ethdb.NewMemDatabase() + db := NewMemoryDatabase() receipt1 := &types.Receipt{ Status: types.ReceiptStatusFailed, diff --git a/core/rawdb/accessors_indexes.go b/core/rawdb/accessors_indexes.go index 4ff7e5bd35..5f767a8383 100644 --- a/core/rawdb/accessors_indexes.go +++ b/core/rawdb/accessors_indexes.go @@ -19,13 +19,14 @@ package rawdb import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/database" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/rlp" ) // ReadTxLookupEntry retrieves the positional metadata associated with a transaction // hash to allow retrieving the transaction or receipt by hash. -func ReadTxLookupEntry(db DatabaseReader, hash common.Hash) (common.Hash, uint64, uint64) { +func ReadTxLookupEntry(db database.Reader, hash common.Hash) (common.Hash, uint64, uint64) { data, _ := db.Get(txLookupKey(hash)) if len(data) == 0 { return common.Hash{}, 0, 0 @@ -40,7 +41,7 @@ func ReadTxLookupEntry(db DatabaseReader, hash common.Hash) (common.Hash, uint64 // WriteTxLookupEntries stores a positional metadata for every transaction from // a block, enabling hash based transaction and receipt lookups. -func WriteTxLookupEntries(db DatabaseWriter, block *types.Block) { +func WriteTxLookupEntries(db database.Writer, block *types.Block) { for i, tx := range block.Transactions() { entry := TxLookupEntry{ BlockHash: block.Hash(), @@ -58,13 +59,13 @@ func WriteTxLookupEntries(db DatabaseWriter, block *types.Block) { } // DeleteTxLookupEntry removes all transaction data associated with a hash. -func DeleteTxLookupEntry(db DatabaseDeleter, hash common.Hash) { +func DeleteTxLookupEntry(db database.Deleter, hash common.Hash) { db.Delete(txLookupKey(hash)) } // ReadTransaction retrieves a specific transaction from the database, along with // its added positional metadata. -func ReadTransaction(db DatabaseReader, hash common.Hash) (*types.Transaction, common.Hash, uint64, uint64) { +func ReadTransaction(db database.AncientReader, hash common.Hash) (*types.Transaction, common.Hash, uint64, uint64) { blockHash, blockNumber, txIndex := ReadTxLookupEntry(db, hash) if blockHash == (common.Hash{}) { return nil, common.Hash{}, 0, 0 @@ -79,7 +80,7 @@ func ReadTransaction(db DatabaseReader, hash common.Hash) (*types.Transaction, c // ReadReceipt retrieves a specific transaction receipt from the database, along with // its added positional metadata. -func ReadReceipt(db DatabaseReader, hash common.Hash) (*types.Receipt, common.Hash, uint64, uint64) { +func ReadReceipt(db database.AncientReader, hash common.Hash) (*types.Receipt, common.Hash, uint64, uint64) { blockHash, blockNumber, receiptIndex := ReadTxLookupEntry(db, hash) if blockHash == (common.Hash{}) { return nil, common.Hash{}, 0, 0 @@ -94,13 +95,13 @@ func ReadReceipt(db DatabaseReader, hash common.Hash) (*types.Receipt, common.Ha // ReadBloomBits retrieves the compressed bloom bit vector belonging to the given // section and bit index from the. -func ReadBloomBits(db DatabaseReader, bit uint, section uint64, head common.Hash) ([]byte, error) { +func ReadBloomBits(db database.Reader, bit uint, section uint64, head common.Hash) ([]byte, error) { return db.Get(bloomBitsKey(bit, section, head)) } // WriteBloomBits stores the compressed bloom bits vector belonging to the given // section and bit index. -func WriteBloomBits(db DatabaseWriter, bit uint, section uint64, head common.Hash, bits []byte) { +func WriteBloomBits(db database.Writer, bit uint, section uint64, head common.Hash, bits []byte) { if err := db.Put(bloomBitsKey(bit, section, head), bits); err != nil { log.Crit("Failed to store bloom bits", "err", err) } diff --git a/core/rawdb/accessors_indexes_test.go b/core/rawdb/accessors_indexes_test.go index d9c10e1490..3acb628a9a 100644 --- a/core/rawdb/accessors_indexes_test.go +++ b/core/rawdb/accessors_indexes_test.go @@ -22,12 +22,11 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/ethdb" ) // Tests that positional lookup metadata can be stored and retrieved. func TestLookupStorage(t *testing.T) { - db := ethdb.NewMemDatabase() + db := NewMemoryDatabase() tx1 := types.NewTransaction(1, common.BytesToAddress([]byte{0x11}), big.NewInt(111), 1111, big.NewInt(11111), []byte{0x11, 0x11, 0x11}) tx2 := types.NewTransaction(2, common.BytesToAddress([]byte{0x22}), big.NewInt(222), 2222, big.NewInt(22222), []byte{0x22, 0x22, 0x22}) diff --git a/core/rawdb/accessors_metadata.go b/core/rawdb/accessors_metadata.go index 82e4bf0455..7b7a5a2836 100644 --- a/core/rawdb/accessors_metadata.go +++ b/core/rawdb/accessors_metadata.go @@ -20,13 +20,14 @@ import ( "encoding/json" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/database" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" ) // ReadDatabaseVersion retrieves the version number of the database. -func ReadDatabaseVersion(db DatabaseReader) *uint64 { +func ReadDatabaseVersion(db database.Reader) *uint64 { var version uint64 enc, _ := db.Get(databaseVerisionKey) @@ -41,7 +42,7 @@ func ReadDatabaseVersion(db DatabaseReader) *uint64 { } // WriteDatabaseVersion stores the version number of the database -func WriteDatabaseVersion(db DatabaseWriter, version uint64) { +func WriteDatabaseVersion(db database.Writer, version uint64) { enc, err := rlp.EncodeToBytes(version) if err != nil { log.Crit("Failed to encode database version", "err", err) @@ -52,7 +53,7 @@ func WriteDatabaseVersion(db DatabaseWriter, version uint64) { } // ReadChainConfig retrieves the consensus settings based on the given genesis hash. -func ReadChainConfig(db DatabaseReader, hash common.Hash) *params.ChainConfig { +func ReadChainConfig(db database.Reader, hash common.Hash) *params.ChainConfig { data, _ := db.Get(configKey(hash)) if len(data) == 0 { return nil @@ -66,7 +67,7 @@ func ReadChainConfig(db DatabaseReader, hash common.Hash) *params.ChainConfig { } // WriteChainConfig writes the chain config settings to the database. -func WriteChainConfig(db DatabaseWriter, hash common.Hash, cfg *params.ChainConfig) { +func WriteChainConfig(db database.Writer, hash common.Hash, cfg *params.ChainConfig) { if cfg == nil { return } @@ -80,13 +81,13 @@ func WriteChainConfig(db DatabaseWriter, hash common.Hash, cfg *params.ChainConf } // ReadPreimage retrieves a single preimage of the provided hash. -func ReadPreimage(db DatabaseReader, hash common.Hash) []byte { +func ReadPreimage(db database.Reader, hash common.Hash) []byte { data, _ := db.Get(preimageKey(hash)) return data } // WritePreimages writes the provided set of preimages to the database. -func WritePreimages(db DatabaseWriter, preimages map[common.Hash][]byte) { +func WritePreimages(db database.Writer, preimages map[common.Hash][]byte) { for hash, preimage := range preimages { if err := db.Put(preimageKey(hash), preimage); err != nil { log.Crit("Failed to store trie preimage", "err", err) diff --git a/core/rawdb/database.go b/core/rawdb/database.go new file mode 100644 index 0000000000..42ac61b38f --- /dev/null +++ b/core/rawdb/database.go @@ -0,0 +1,100 @@ +// Copyright 2018 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package rawdb + +import ( + "github.com/ethereum/go-ethereum/database" + "github.com/ethereum/go-ethereum/internal/keyvalue" +) + +// freezerdb is a databse wrapper that enabled freezer data retrievals. +type freezerdb struct { + database.KeyValueStore + database.Ancienter +} + +// nofreezedb is a database wrapper that disables freezer data retrievals. +type nofreezedb struct { + database.KeyValueStore +} + +// Frozen returns nil as we don't have a backing chain freezer. +func (db *nofreezedb) Ancient(kind string, number uint64) ([]byte, error) { + return nil, errOutOfBounds +} + +// NewDatabase creates a high level database on top of a given key-value data +// store without a freezer moving immutable chain segments into cold storage. +func NewDatabase(db database.KeyValueStore) database.Database { + return &nofreezedb{ + KeyValueStore: db, + } +} + +// NewDatabaseWithFreezer creates a high level database on top of a given key- +// value data store with a freezer moving immutable chain segments into cold +// storage. +func NewDatabaseWithFreezer(db database.KeyValueStore, freezer string, namespace string) (database.Database, error) { + frdb, err := newFreezer(freezer, namespace) + if err != nil { + return nil, err + } + go frdb.freeze(db) + + return &freezerdb{ + KeyValueStore: db, + Ancienter: frdb, + }, nil +} + +// NewMemoryDatabase creates an ephemeral in-memory key-value database without a +// freezer moving immutable chain segments into cold storage. +func NewMemoryDatabase() database.Database { + return NewDatabase(keyvalue.NewMemoryDatabase()) +} + +// NewMemoryDatabaseWithCap creates an ephemeral in-memory key-value database with +// an initial starting capacity, but without a freezer moving immutable chain +// segments into cold storage. +func NewMemoryDatabaseWithCap(size int) database.Database { + return NewDatabase(keyvalue.NewMemoryDatabaseWithCap(size)) +} + +// NewLeveldbDatabase creates a persistent key-value database without a freezer +// moving immutable chain segments into cold storage. +func NewLeveldbDatabase(file string, cache int, handles int, namespace string) (database.Database, error) { + db, err := keyvalue.NewLeveldbDatabase(file, cache, handles, namespace) + if err != nil { + return nil, err + } + return NewDatabase(db), nil +} + +// NewLeveldbDatabaseWithFreezer creates a persistent key-value database with a freezer +// moving immutable chain segments into cold storage. +func NewLeveldbDatabaseWithFreezer(file string, cache int, handles int, freezer string, namespace string) (database.Database, error) { + kvdb, err := keyvalue.NewLeveldbDatabase(file, cache, handles, namespace) + if err != nil { + return nil, err + } + frdb, err := NewDatabaseWithFreezer(kvdb, freezer, namespace) + if err != nil { + kvdb.Close() + return nil, err + } + return frdb, nil +} diff --git a/core/rawdb/freezer.go b/core/rawdb/freezer.go new file mode 100644 index 0000000000..eb7b84637c --- /dev/null +++ b/core/rawdb/freezer.go @@ -0,0 +1,276 @@ +// Copyright 2018 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package rawdb + +import ( + "errors" + "fmt" + "math" + "sync/atomic" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/database" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/metrics" +) + +// errUnknownTable is returned if the user attempts to read from a table that is +// not tracked by the freezer. +var errUnknownTable = errors.New("unknown table") + +const ( + // freezerRecheckInterval is the frequency to check the key-value database for + // chain progression that might permit new blocks to be frozen into immutable + // storage. + freezerRecheckInterval = time.Minute + + // freezerBlockGraduation is the number of confirmations a block must achieve + // before it becomes elligible for chain freezing. This must exceed any chain + // reorg depth, since the freezer also deletes all block siblings. + freezerBlockGraduation = 60000 + + // freezerBatchLimit is the maximum number of blocks to freeze in one batch + // before doing an fsync and deleting it from the key-value store. + freezerBatchLimit = 30000 +) + +// freezer is an memory mapped append-only database to store immutable chain data +// into flat files: +// +// - The append only nature ensures that disk writes are minimized. +// - The memory mapping ensures we can max out system memory for caching without +// reserving it for go-ethereum. This would also reduce the memory requirements +// of Geth, and thus also GC overhead. +type freezer struct { + tables map[string]*freezerTable // Data tables for storing everything + frozen uint64 // Number of blocks already frozen +} + +// newFreezer creates a chain freezer that moves ancient chain data into +// append-only flat file containers. +func newFreezer(datadir string, namespace string) (*freezer, error) { + // Create the initial freezer object + var ( + readMeter = metrics.NewRegisteredMeter(namespace+"ancient/read", nil) + writeMeter = metrics.NewRegisteredMeter(namespace+"ancient/write", nil) + ) + // Open all the supported data tables + freezer := &freezer{ + tables: make(map[string]*freezerTable), + } + for _, name := range []string{"hashes", "headers", "bodies", "receipts", "diffs"} { + table, err := newTable(datadir, name, readMeter, writeMeter) + if err != nil { + for _, table := range freezer.tables { + table.Close() + } + return nil, err + } + freezer.tables[name] = table + } + // Truncate all data tables to the same length + freezer.frozen = math.MaxUint64 + for _, table := range freezer.tables { + if freezer.frozen > table.items { + freezer.frozen = table.items + } + } + for _, table := range freezer.tables { + if err := table.truncate(freezer.frozen); err != nil { + for _, table := range freezer.tables { + table.Close() + } + return nil, err + } + } + return freezer, nil +} + +// Close terminates the chain freezer, unmapping all the data files. +func (f *freezer) Close() error { + var errs []error + for _, table := range f.tables { + if err := table.Close(); err != nil { + errs = append(errs, err) + } + } + if errs != nil { + return fmt.Errorf("%v", errs) + } + return nil +} + +// sync flushes all data tables to disk. +func (f *freezer) sync() error { + var errs []error + for _, table := range f.tables { + if err := table.Sync(); err != nil { + errs = append(errs, err) + } + } + if errs != nil { + return fmt.Errorf("%v", errs) + } + return nil +} + +// Ancient retrieves an ancient binary blob from the append-only immutable files. +func (f *freezer) Ancient(kind string, number uint64) ([]byte, error) { + if table := f.tables[kind]; table != nil { + return table.Retrieve(number) + } + return nil, errUnknownTable +} + +// freeze is a background thread that periodically checks the blockchain for any +// import progress and moves ancient data from the fast database into the freezer. +// +// This functionality is deliberately broken off from block importing to avoid +// incurring additional data shuffling delays on block propagation. +func (f *freezer) freeze(db database.KeyValueStore) { + nfdb := &nofreezedb{KeyValueStore: db} + + for { + // Retrieve the freezing threshold. In theory we're interested only in full + // blocks post-sync, but that would keep the live database enormous during + // dast sync. By picking the fast block, we still get to deep freeze all the + // final immutable data without having to wait for sync to finish. + hash := ReadHeadFastBlockHash(nfdb) + if hash == (common.Hash{}) { + log.Debug("Current fast block hash unavailable") // new chain, empty database + time.Sleep(freezerRecheckInterval) + continue + } + number := ReadHeaderNumber(nfdb, hash) + switch { + case number == nil: + log.Error("Current fast block number unavailable", "hash", hash) + time.Sleep(freezerRecheckInterval) + continue + + case *number < freezerBlockGraduation: + log.Debug("Current fast block not old enough", "number", *number, "hash", hash, "delay", freezerBlockGraduation) + time.Sleep(freezerRecheckInterval) + continue + + case *number-freezerBlockGraduation <= f.frozen: + log.Debug("Ancient blocks frozen already", "number", *number, "hash", hash, "frozen", f.frozen) + time.Sleep(freezerRecheckInterval) + continue + } + head := ReadHeader(nfdb, hash, *number) + if head == nil { + log.Error("Current fast block unavailable", "number", *number, "hash", hash) + time.Sleep(freezerRecheckInterval) + continue + } + // Seems we have data ready to be frozen, process in usable batches + limit := *number - freezerBlockGraduation + if limit-f.frozen > freezerBatchLimit { + limit = f.frozen + freezerBatchLimit + } + var ( + start = time.Now() + first = f.frozen + ancients = make([]common.Hash, 0, limit) + ) + for f.frozen < limit { + // Retrieves all the components of the canonical block + hash := ReadCanonicalHash(nfdb, f.frozen) + if hash == (common.Hash{}) { + log.Error("Canonical hash missing, can't freeze", "number", f.frozen) + break + } + header := ReadHeaderRLP(nfdb, hash, f.frozen) + if len(header) == 0 { + log.Error("Block header missing, can't freeze", "number", f.frozen, "hash", hash) + break + } + body := ReadBodyRLP(nfdb, hash, f.frozen) + if len(body) == 0 { + log.Error("Block body missing, can't freeze", "number", f.frozen, "hash", hash) + break + } + receipts := ReadReceiptsRLP(nfdb, hash, f.frozen) + if len(receipts) == 0 { + log.Error("Block receipts missing, can't freeze", "number", f.frozen, "hash", hash) + break + } + td := ReadTdRLP(nfdb, hash, f.frozen) + if len(td) == 0 { + log.Error("Total difficulty missing, can't freeze", "number", f.frozen, "hash", hash) + break + } + // Inject all the components into the relevant data tables + if err := f.tables["hashes"].Append(f.frozen, hash[:]); err != nil { + log.Error("Failed to deep freeze hash", "number", f.frozen, "hash", hash, "err", err) + break + } + if err := f.tables["headers"].Append(f.frozen, header); err != nil { + log.Error("Failed to deep freeze header", "number", f.frozen, "hash", hash, "err", err) + break + } + if err := f.tables["bodies"].Append(f.frozen, body); err != nil { + log.Error("Failed to deep freeze body", "number", f.frozen, "hash", hash, "err", err) + break + } + if err := f.tables["receipts"].Append(f.frozen, receipts); err != nil { + log.Error("Failed to deep freeze receipts", "number", f.frozen, "hash", hash, "err", err) + break + } + if err := f.tables["diffs"].Append(f.frozen, td); err != nil { + log.Error("Failed to deep freeze difficulty", "number", f.frozen, "hash", hash, "err", err) + break + } + log.Trace("Deep froze ancient block", "number", f.frozen, "hash", hash) + atomic.AddUint64(&f.frozen, 1) // Only modify atomically + ancients = append(ancients, hash) + } + // Batch of blocks have been frozen, flush them before wiping from leveldb + if err := f.sync(); err != nil { + log.Crit("Failed to flush frozen tables", "err", err) + } + // Wipe out all data from the active database + batch := db.NewBatch() + for number := first; number < f.frozen; number++ { + for _, hash := range readAllHashes(db, number) { + if hash == ancients[number-first] { + deleteBlockWithoutNumber(batch, hash, number) + } else { + DeleteBlock(batch, hash, number) + } + } + } + if err := batch.Write(); err != nil { + log.Crit("Failed to delete frozen items", "err", err) + } + // Log something friendly for the user + context := []interface{}{ + "blocks", f.frozen - first, "elapsed", common.PrettyDuration(time.Since(start)), "number", f.frozen - 1, + } + if n := len(ancients); n > 0 { + context = append(context, []interface{}{"hash", ancients[n-1]}...) + } + log.Info("Deep froze chain segment", context...) + + // Avoid database thrashing with tiny writes + if f.frozen-first < freezerBatchLimit { + time.Sleep(freezerRecheckInterval) + } + } +} diff --git a/core/rawdb/freezer_table.go b/core/rawdb/freezer_table.go new file mode 100644 index 0000000000..546db0c656 --- /dev/null +++ b/core/rawdb/freezer_table.go @@ -0,0 +1,284 @@ +// Copyright 2018 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package rawdb + +import ( + "encoding/binary" + "errors" + "fmt" + "os" + "path/filepath" + "sync" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/metrics" + "github.com/golang/snappy" +) + +var ( + // errClosed is returned if an operation attempts to read from or write to the + // freezer table after it has already been closed. + errClosed = errors.New("closed") + + // errOutOfBounds is returned if the item requested is not contained within the + // freezer table. + errOutOfBounds = errors.New("out of bounds") +) + +// freezerTable represents a single chained data table within the freezer (e.g. blocks). +// It consists of a data file (snappy encoded arbitrary data blobs) and an index +// file (uncompressed 64 bit indices into the data file). +type freezerTable struct { + content *os.File // File descriptor for the data content of the table + offsets *os.File // File descriptor for the index file of the table + + items uint64 // Number of items stored in the table + bytes uint64 // Number of content bytes stored in the table + + readMeter metrics.Meter // Meter for measuring the effective amount of data read + writeMeter metrics.Meter // Meter for measuring the effective amount of data written + + logger log.Logger // Logger with database path and table name ambedded + lock sync.RWMutex // Mutex protecting the data file descriptors +} + +// newTable opens a freezer table, creating the data and index files if they are +// non existent. Both files are truncated to the shortest common length to ensure +// they don't go out of sync. +func newTable(path string, name string, readMeter metrics.Meter, writeMeter metrics.Meter) (*freezerTable, error) { + // Ensure the containing directory exists and open the two data files + if err := os.MkdirAll(path, 0755); err != nil { + return nil, err + } + content, err := os.OpenFile(filepath.Join(path, name+".dat"), os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644) + if err != nil { + return nil, err + } + offsets, err := os.OpenFile(filepath.Join(path, name+".idx"), os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644) + if err != nil { + content.Close() + return nil, err + } + // Create the table and repair any past inconsistency + tab := &freezerTable{ + content: content, + offsets: offsets, + readMeter: readMeter, + writeMeter: writeMeter, + logger: log.New("database", path, "table", name), + } + if err := tab.repair(); err != nil { + offsets.Close() + content.Close() + return nil, err + } + return tab, nil +} + +// repair cross checks the content and the offsets file and truncates them to +// be in sync with each other after a potential crash / data loss. +func (t *freezerTable) repair() error { + // Create a temporary offset buffer to init files with and read offsts into + offset := make([]byte, 8) + + // If we've just created the files, initialize the offsets with the 0 index + stat, err := t.offsets.Stat() + if err != nil { + return err + } + if stat.Size() == 0 { + if _, err := t.offsets.Write(offset); err != nil { + return err + } + } + // Ensure the offsets are a multiple of 8 bytes + if overflow := stat.Size() % 8; overflow != 0 { + t.offsets.Truncate(stat.Size() - overflow) // New file can't trigger this path + } + // Retrieve the file sizes and prepare for truncation + if stat, err = t.offsets.Stat(); err != nil { + return err + } + offsetsSize := stat.Size() + + if stat, err = t.content.Stat(); err != nil { + return err + } + contentSize := stat.Size() + + // Keep truncating both files until they come in sync + t.offsets.ReadAt(offset, offsetsSize-8) + contentExp := int64(binary.LittleEndian.Uint64(offset)) + + for contentExp != contentSize { + // Truncate the content file to the last offset pointer + if contentExp < contentSize { + t.logger.Warn("Truncating dangling content", "indexed", common.StorageSize(contentExp), "stored", common.StorageSize(contentSize)) + if err := t.content.Truncate(contentExp); err != nil { + return err + } + contentSize = contentExp + } + // Truncate the offsets to point within the content file + if contentExp > contentSize { + t.logger.Warn("Truncating dangling offsets", "indexed", common.StorageSize(contentExp), "stored", common.StorageSize(contentSize)) + if err := t.offsets.Truncate(offsetsSize - 8); err != nil { + return err + } + offsetsSize -= 8 + + t.offsets.ReadAt(offset, offsetsSize-8) + contentExp = int64(binary.LittleEndian.Uint64(offset)) + } + } + // Ensure all reparation changes have been written to disk + if err := t.offsets.Sync(); err != nil { + return err + } + if err := t.content.Sync(); err != nil { + return err + } + // Update the item and byte counters and return + t.items = uint64(offsetsSize/8 - 1) // last index points to the end of the data file + t.bytes = uint64(contentSize) + + t.logger.Debug("Chain freezer table opened", "items", t.items, "size", common.StorageSize(t.bytes)) + return nil +} + +// truncate discards any recent data above the provided threashold number. +func (t *freezerTable) truncate(items uint64) error { + // If out item count is corrent, don't do anything + if t.items <= items { + return nil + } + // Something's out of sync, truncate the table's offset index + t.logger.Warn("Truncating freezer table", "items", t.items, "limit", items) + if err := t.offsets.Truncate(int64(items+1) * 8); err != nil { + return err + } + // Calculate the new expected size of the data file and truncate it + offset := make([]byte, 8) + t.offsets.ReadAt(offset, int64(items)*8) + expected := binary.LittleEndian.Uint64(offset) + + if err := t.content.Truncate(int64(expected)); err != nil { + return err + } + // All data files truncated, set internal counters and return + t.items, t.bytes = items, expected + return nil +} + +// Close unmaps all active memory mapped regions. +func (t *freezerTable) Close() error { + t.lock.Lock() + defer t.lock.Unlock() + + var errs []error + if err := t.offsets.Close(); err != nil { + errs = append(errs, err) + } + t.offsets = nil + + if err := t.content.Close(); err != nil { + errs = append(errs, err) + } + t.content = nil + + if errs != nil { + return fmt.Errorf("%v", errs) + } + return nil +} + +// Append injects a binary blob at the end of the freezer table. The item index +// is a precautionary parameter to ensure data correctness, but the table will +// reject already existing data. +// +// Note, this method will *not* flush any data to disk so be sure to explicitly +// fsync before irreversibly deleting data from the database. +func (t *freezerTable) Append(item uint64, blob []byte) error { + // Ensure the table is still accessible + if t.offsets == nil || t.content == nil { + return errClosed + } + // Ensure only the next item can be written, nothing else + if t.items != item { + panic(fmt.Sprintf("appending unexpected item: want %d, have %d", t.items, item)) + } + // Encode the blob and write it into the data file + blob = snappy.Encode(nil, blob) + if _, err := t.content.Write(blob); err != nil { + return err + } + t.bytes += uint64(len(blob)) + + offset := make([]byte, 8) + binary.LittleEndian.PutUint64(offset, t.bytes) + if _, err := t.offsets.Write(offset); err != nil { + return err + } + t.items++ + + t.writeMeter.Mark(int64(len(blob) + 8)) // 8 = 1 x 8 byte offset + return nil +} + +// Retrieve looks up the data offset of an item with the given index and retrieves +// the raw binary blob from the data file. +func (t *freezerTable) Retrieve(item uint64) ([]byte, error) { + t.lock.RLock() + defer t.lock.RUnlock() + + // Ensure the table and the item is accessible + if t.offsets == nil || t.content == nil { + return nil, errClosed + } + if t.items <= item { + return nil, errOutOfBounds + } + // Item reachable, retrieve the data content boundaries + offset := make([]byte, 8) + if _, err := t.offsets.ReadAt(offset, int64(item*8)); err != nil { + return nil, err + } + start := binary.LittleEndian.Uint64(offset) + + if _, err := t.offsets.ReadAt(offset, int64((item+1)*8)); err != nil { + return nil, err + } + end := binary.LittleEndian.Uint64(offset) + + // Retrieve the data itself, decompress and return + blob := make([]byte, end-start) + if _, err := t.content.ReadAt(blob, int64(start)); err != nil { + return nil, err + } + t.readMeter.Mark(int64(len(blob) + 16)) // 16 = 2 x 8 byte offset + return snappy.Decode(nil, blob) +} + +// Sync pushes any pending data from memory out to disk. This is an expensive +// operation, so use it with care. +func (t *freezerTable) Sync() error { + if err := t.offsets.Sync(); err != nil { + return err + } + return t.content.Sync() +} diff --git a/core/rawdb/interfaces.go b/core/rawdb/interfaces.go deleted file mode 100644 index 3bdf55124a..0000000000 --- a/core/rawdb/interfaces.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2018 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package rawdb - -// DatabaseReader wraps the Has and Get method of a backing data store. -type DatabaseReader interface { - Has(key []byte) (bool, error) - Get(key []byte) ([]byte, error) -} - -// DatabaseWriter wraps the Put method of a backing data store. -type DatabaseWriter interface { - Put(key []byte, value []byte) error -} - -// DatabaseDeleter wraps the Delete method of a backing data store. -type DatabaseDeleter interface { - Delete(key []byte) error -} diff --git a/core/rawdb/schema.go b/core/rawdb/schema.go index 8a9921ef40..795920e689 100644 --- a/core/rawdb/schema.go +++ b/core/rawdb/schema.go @@ -78,6 +78,11 @@ func encodeBlockNumber(number uint64) []byte { return enc } +// headerKeyPrefix = headerPrefix + num (uint64 big endian) +func headerKeyPrefix(number uint64) []byte { + return append(headerPrefix, encodeBlockNumber(number)...) +} + // headerKey = headerPrefix + num (uint64 big endian) + hash func headerKey(number uint64, hash common.Hash) []byte { return append(append(headerPrefix, encodeBlockNumber(number)...), hash.Bytes()...) diff --git a/core/rawdb/table.go b/core/rawdb/table.go new file mode 100644 index 0000000000..bc2d028600 --- /dev/null +++ b/core/rawdb/table.go @@ -0,0 +1,124 @@ +// Copyright 2018 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package rawdb + +import ( + "github.com/ethereum/go-ethereum/database" +) + +// table is a wrapper around a database that prefixes each key access with a pre- +// configured string. +type table struct { + db database.Database + prefix string +} + +// NewTable returns a database object that prefixes all keys with a given string. +func NewTable(db database.Database, prefix string) database.Database { + return &table{ + db: db, + prefix: prefix, + } +} + +// Close is a noop to implement the Database interface. +func (t *table) Close() error { + return nil +} + +// Has retrieves if a prefixed version of a key is present in the database. +func (t *table) Has(key []byte) (bool, error) { + return t.db.Has(append([]byte(t.prefix), key...)) +} + +// Get retrieves the given prefixed key if it's present in the database. +func (t *table) Get(key []byte) ([]byte, error) { + return t.db.Get(append([]byte(t.prefix), key...)) +} + +// Ancient is a noop passthrough that just forwards the request to the underlying +// database. +func (t *table) Ancient(kind string, number uint64) ([]byte, error) { + return t.db.Ancient(kind, number) +} + +// Put inserts the given value into the database at a prefixed version of the +// provided key. +func (t *table) Put(key []byte, value []byte) error { + return t.db.Put(append([]byte(t.prefix), key...), value) +} + +// Delete removes the given prefixed key from the database. +func (t *table) Delete(key []byte) error { + return t.db.Delete(append([]byte(t.prefix), key...)) +} + +// NewIterator creates a binary-alphabetical iterator over the entire keyspace +// contained within the database. +func (t *table) NewIterator() database.Iterator { + return t.NewIteratorWithPrefix(nil) +} + +// NewIteratorWithPrefix creates a binary-alphabetical iterator over a subset +// of database content with a particular key prefix. +func (t *table) NewIteratorWithPrefix(prefix []byte) database.Iterator { + return t.db.NewIteratorWithPrefix(append([]byte(t.prefix), prefix...)) +} + +// Stat returns a particular internal stat of the database. +func (t *table) Stat(property string) (string, error) { + return t.db.Stat(property) +} + +// NewBatch creates a write-only database that buffers changes to its host db +// until a final write is called, each operation prefixing all keys with the +// pre-configured string. +func (t *table) NewBatch() database.Batch { + return &tableBatch{t.db.NewBatch(), t.prefix} +} + +// tableBatch is a wrapper around a database batch that prefixes each key access +// with a pre-configured string. +type tableBatch struct { + batch database.Batch + prefix string +} + +// Put inserts the given value into the batch for later committing. +func (b *tableBatch) Put(key, value []byte) error { + return b.batch.Put(append([]byte(b.prefix), key...), value) +} + +// Delete inserts the a key removal into the batch for later committing. +func (b *tableBatch) Delete(key []byte) error { + return b.batch.Delete(append([]byte(b.prefix), key...)) +} + +// ValueSize retrieves the amount of data queued up for writing. +func (b *tableBatch) ValueSize() int { + return b.batch.ValueSize() +} + +// Write flushes any accumulated data to disk. +func (b *tableBatch) Write() error { + return b.batch.Write() +} + +// Reset resets the batch for reuse. +func (b *tableBatch) Reset() { + b.batch.Reset() +} diff --git a/core/state/database.go b/core/state/database.go index f6ea144b9b..bfcfef6ea8 100644 --- a/core/state/database.go +++ b/core/state/database.go @@ -21,7 +21,7 @@ import ( "sync" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/database" "github.com/ethereum/go-ethereum/trie" lru "github.com/hashicorp/golang-lru" ) @@ -68,20 +68,20 @@ type Trie interface { Hash() common.Hash NodeIterator(startKey []byte) trie.NodeIterator GetKey([]byte) []byte // TODO(fjl): remove this when SecureTrie is removed - Prove(key []byte, fromLevel uint, proofDb ethdb.Putter) error + Prove(key []byte, fromLevel uint, proofDb database.Writer) error } // NewDatabase creates a backing store for state. The returned database is safe for // concurrent use and retains a few recent expanded trie nodes in memory. To keep // more historical state in memory, use the NewDatabaseWithCache constructor. -func NewDatabase(db ethdb.Database) Database { +func NewDatabase(db database.Database) Database { return NewDatabaseWithCache(db, 0) } // NewDatabase creates a backing store for state. The returned database is safe for // concurrent use and retains both a few recent expanded trie nodes in memory, as // well as a lot of collapsed RLP trie nodes in a large memory cache. -func NewDatabaseWithCache(db ethdb.Database, cache int) Database { +func NewDatabaseWithCache(db database.Database, cache int) Database { csc, _ := lru.New(codeSizeCacheSize) return &cachingDB{ db: trie.NewDatabaseWithCache(db, cache), @@ -179,6 +179,6 @@ func (m cachedTrie) Commit(onleaf trie.LeafCallback) (common.Hash, error) { return root, err } -func (m cachedTrie) Prove(key []byte, fromLevel uint, proofDb ethdb.Putter) error { +func (m cachedTrie) Prove(key []byte, fromLevel uint, proofDb database.Writer) error { return m.SecureTrie.Prove(key, fromLevel, proofDb) } diff --git a/core/state/iterator_test.go b/core/state/iterator_test.go index 9e46c851cd..561f9abae6 100644 --- a/core/state/iterator_test.go +++ b/core/state/iterator_test.go @@ -21,7 +21,7 @@ import ( "testing" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/database" ) // Tests that the node iterator indeed walks over the entire database contents. @@ -51,7 +51,9 @@ func TestNodeIteratorCoverage(t *testing.T) { t.Errorf("state entry not reported %x", hash) } } - for _, key := range db.TrieDB().DiskDB().(*ethdb.MemDatabase).Keys() { + it := db.TrieDB().DiskDB().(database.Database).NewIterator() + for it.Next() { + key := it.Key() if bytes.HasPrefix(key, []byte("secure-key-")) { continue } @@ -59,4 +61,5 @@ func TestNodeIteratorCoverage(t *testing.T) { t.Errorf("state entry not reported %x", key) } } + it.Release() } diff --git a/core/state/managed_state_test.go b/core/state/managed_state_test.go index 3d9c4e8676..fdfde96adf 100644 --- a/core/state/managed_state_test.go +++ b/core/state/managed_state_test.go @@ -20,13 +20,13 @@ import ( "testing" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/core/rawdb" ) var addr = common.BytesToAddress([]byte("test")) func create() (*ManagedState, *account) { - statedb, _ := New(common.Hash{}, NewDatabase(ethdb.NewMemDatabase())) + statedb, _ := New(common.Hash{}, NewDatabase(rawdb.NewMemoryDatabase())) ms := ManageState(statedb) ms.StateDB.SetNonce(addr, 100) ms.accounts[addr] = newAccount(ms.StateDB.getStateObject(addr)) diff --git a/core/state/state_test.go b/core/state/state_test.go index a09273f3b1..92a622de00 100644 --- a/core/state/state_test.go +++ b/core/state/state_test.go @@ -22,13 +22,14 @@ import ( "testing" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/database" checker "gopkg.in/check.v1" ) type StateSuite struct { - db *ethdb.MemDatabase + db database.Database state *StateDB } @@ -87,7 +88,7 @@ func (s *StateSuite) TestDump(c *checker.C) { } func (s *StateSuite) SetUpTest(c *checker.C) { - s.db = ethdb.NewMemDatabase() + s.db = rawdb.NewMemoryDatabase() s.state, _ = New(common.Hash{}, NewDatabase(s.db)) } @@ -141,7 +142,7 @@ func (s *StateSuite) TestSnapshotEmpty(c *checker.C) { // use testing instead of checker because checker does not support // printing/logging in tests (-check.vv does not work) func TestSnapshot2(t *testing.T) { - state, _ := New(common.Hash{}, NewDatabase(ethdb.NewMemDatabase())) + state, _ := New(common.Hash{}, NewDatabase(rawdb.NewMemoryDatabase())) stateobjaddr0 := toAddr([]byte("so0")) stateobjaddr1 := toAddr([]byte("so1")) diff --git a/core/state/statedb_test.go b/core/state/statedb_test.go index 69392d972e..c2d2b2f692 100644 --- a/core/state/statedb_test.go +++ b/core/state/statedb_test.go @@ -31,15 +31,15 @@ import ( check "gopkg.in/check.v1" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/ethdb" ) // Tests that updating a state trie does not leak any database writes prior to // actually committing the state. func TestUpdateLeaks(t *testing.T) { // Create an empty state database - db := ethdb.NewMemDatabase() + db := rawdb.NewMemoryDatabase() state, _ := New(common.Hash{}, NewDatabase(db)) // Update it with some accounts @@ -56,18 +56,19 @@ func TestUpdateLeaks(t *testing.T) { state.IntermediateRoot(false) } // Ensure that no data was leaked into the database - for _, key := range db.Keys() { - value, _ := db.Get(key) - t.Errorf("State leaked into database: %x -> %x", key, value) + it := db.NewIterator() + for it.Next() { + t.Errorf("State leaked into database: %x -> %x", it.Key(), it.Value()) } + it.Release() } // Tests that no intermediate state of an object is stored into the database, // only the one right before the commit. func TestIntermediateLeaks(t *testing.T) { // Create two state databases, one transitioning to the final state, the other final from the beginning - transDb := ethdb.NewMemDatabase() - finalDb := ethdb.NewMemDatabase() + transDb := rawdb.NewMemoryDatabase() + finalDb := rawdb.NewMemoryDatabase() transState, _ := New(common.Hash{}, NewDatabase(transDb)) finalState, _ := New(common.Hash{}, NewDatabase(finalDb)) @@ -103,16 +104,20 @@ func TestIntermediateLeaks(t *testing.T) { if _, err := finalState.Commit(false); err != nil { t.Fatalf("failed to commit final state: %v", err) } - for _, key := range finalDb.Keys() { + it := finalDb.NewIterator() + for it.Next() { + key := it.Key() if _, err := transDb.Get(key); err != nil { - val, _ := finalDb.Get(key) - t.Errorf("entry missing from the transition database: %x -> %x", key, val) + t.Errorf("entry missing from the transition database: %x -> %x", key, it.Value()) } } - for _, key := range transDb.Keys() { + it.Release() + + it = transDb.NewIterator() + for it.Next() { + key := it.Key() if _, err := finalDb.Get(key); err != nil { - val, _ := transDb.Get(key) - t.Errorf("extra entry in the transition database: %x -> %x", key, val) + t.Errorf("extra entry in the transition database: %x -> %x", key, it.Value()) } } } @@ -122,7 +127,7 @@ func TestIntermediateLeaks(t *testing.T) { // https://github.com/ethereum/go-ethereum/pull/15549. func TestCopy(t *testing.T) { // Create a random state test to copy and modify "independently" - orig, _ := New(common.Hash{}, NewDatabase(ethdb.NewMemDatabase())) + orig, _ := New(common.Hash{}, NewDatabase(rawdb.NewMemoryDatabase())) for i := byte(0); i < 255; i++ { obj := orig.GetOrNewStateObject(common.BytesToAddress([]byte{i})) @@ -342,7 +347,7 @@ func (test *snapshotTest) String() string { func (test *snapshotTest) run() bool { // Run all actions and create snapshots. var ( - state, _ = New(common.Hash{}, NewDatabase(ethdb.NewMemDatabase())) + state, _ = New(common.Hash{}, NewDatabase(rawdb.NewMemoryDatabase())) snapshotRevs = make([]int, len(test.snapshots)) sindex = 0 ) @@ -433,7 +438,7 @@ func (s *StateSuite) TestTouchDelete(c *check.C) { // TestCopyOfCopy tests that modified objects are carried over to the copy, and the copy of the copy. // See https://github.com/ethereum/go-ethereum/pull/15225#issuecomment-380191512 func TestCopyOfCopy(t *testing.T) { - sdb, _ := New(common.Hash{}, NewDatabase(ethdb.NewMemDatabase())) + sdb, _ := New(common.Hash{}, NewDatabase(rawdb.NewMemoryDatabase())) addr := common.HexToAddress("aaaa") sdb.SetBalance(addr, big.NewInt(42)) diff --git a/core/state/sync.go b/core/state/sync.go index c566e79073..8873c71a36 100644 --- a/core/state/sync.go +++ b/core/state/sync.go @@ -20,12 +20,13 @@ import ( "bytes" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/database" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie" ) // NewStateSync create a new state trie download scheduler. -func NewStateSync(root common.Hash, database trie.DatabaseReader) *trie.Sync { +func NewStateSync(root common.Hash, database database.Reader) *trie.Sync { var syncer *trie.Sync callback := func(leaf []byte, parent common.Hash) error { var obj Account diff --git a/core/state/sync_test.go b/core/state/sync_test.go index 3177401608..475534568f 100644 --- a/core/state/sync_test.go +++ b/core/state/sync_test.go @@ -22,8 +22,9 @@ import ( "testing" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/database" "github.com/ethereum/go-ethereum/trie" ) @@ -38,7 +39,7 @@ type testAccount struct { // makeTestState create a sample test state to test node-wise reconstruction. func makeTestState() (Database, common.Hash, []*testAccount) { // Create an empty state - db := NewDatabase(ethdb.NewMemDatabase()) + db := NewDatabase(rawdb.NewMemoryDatabase()) state, _ := New(common.Hash{}, db) // Fill it with some arbitrary data @@ -68,7 +69,7 @@ func makeTestState() (Database, common.Hash, []*testAccount) { // checkStateAccounts cross references a reconstructed state with an expected // account array. -func checkStateAccounts(t *testing.T, db ethdb.Database, root common.Hash, accounts []*testAccount) { +func checkStateAccounts(t *testing.T, db database.Database, root common.Hash, accounts []*testAccount) { // Check root availability and state contents state, err := New(root, NewDatabase(db)) if err != nil { @@ -91,7 +92,7 @@ func checkStateAccounts(t *testing.T, db ethdb.Database, root common.Hash, accou } // checkTrieConsistency checks that all nodes in a (sub-)trie are indeed present. -func checkTrieConsistency(db ethdb.Database, root common.Hash) error { +func checkTrieConsistency(db database.Database, root common.Hash) error { if v, _ := db.Get(root[:]); v == nil { return nil // Consider a non existent state consistent. } @@ -106,7 +107,7 @@ func checkTrieConsistency(db ethdb.Database, root common.Hash) error { } // checkStateConsistency checks that all data of a state root is present. -func checkStateConsistency(db ethdb.Database, root common.Hash) error { +func checkStateConsistency(db database.Database, root common.Hash) error { // Create and iterate a state trie rooted in a sub-node if _, err := db.Get(root.Bytes()); err != nil { return nil // Consider a non existent state consistent. @@ -124,7 +125,7 @@ func checkStateConsistency(db ethdb.Database, root common.Hash) error { // Tests that an empty state is not scheduled for syncing. func TestEmptyStateSync(t *testing.T) { empty := common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421") - if req := NewStateSync(empty, ethdb.NewMemDatabase()).Missing(1); len(req) != 0 { + if req := NewStateSync(empty, rawdb.NewMemoryDatabase()).Missing(1); len(req) != 0 { t.Errorf("content requested for empty state: %v", req) } } @@ -139,7 +140,7 @@ func testIterativeStateSync(t *testing.T, batch int) { srcDb, srcRoot, srcAccounts := makeTestState() // Create a destination state and sync with the scheduler - dstDb := ethdb.NewMemDatabase() + dstDb := rawdb.NewMemoryDatabase() sched := NewStateSync(srcRoot, dstDb) queue := append([]common.Hash{}, sched.Missing(batch)...) @@ -171,7 +172,7 @@ func TestIterativeDelayedStateSync(t *testing.T) { srcDb, srcRoot, srcAccounts := makeTestState() // Create a destination state and sync with the scheduler - dstDb := ethdb.NewMemDatabase() + dstDb := rawdb.NewMemoryDatabase() sched := NewStateSync(srcRoot, dstDb) queue := append([]common.Hash{}, sched.Missing(0)...) @@ -208,7 +209,7 @@ func testIterativeRandomStateSync(t *testing.T, batch int) { srcDb, srcRoot, srcAccounts := makeTestState() // Create a destination state and sync with the scheduler - dstDb := ethdb.NewMemDatabase() + dstDb := rawdb.NewMemoryDatabase() sched := NewStateSync(srcRoot, dstDb) queue := make(map[common.Hash]struct{}) @@ -248,7 +249,7 @@ func TestIterativeRandomDelayedStateSync(t *testing.T) { srcDb, srcRoot, srcAccounts := makeTestState() // Create a destination state and sync with the scheduler - dstDb := ethdb.NewMemDatabase() + dstDb := rawdb.NewMemoryDatabase() sched := NewStateSync(srcRoot, dstDb) queue := make(map[common.Hash]struct{}) @@ -292,10 +293,10 @@ func TestIncompleteStateSync(t *testing.T) { // Create a random state to copy srcDb, srcRoot, srcAccounts := makeTestState() - checkTrieConsistency(srcDb.TrieDB().DiskDB().(ethdb.Database), srcRoot) + checkTrieConsistency(srcDb.TrieDB().DiskDB().(database.Database), srcRoot) // Create a destination state and sync with the scheduler - dstDb := ethdb.NewMemDatabase() + dstDb := rawdb.NewMemoryDatabase() sched := NewStateSync(srcRoot, dstDb) added := []common.Hash{} diff --git a/core/tx_pool_test.go b/core/tx_pool_test.go index 6d3bd7a5a5..b93cbd4836 100644 --- a/core/tx_pool_test.go +++ b/core/tx_pool_test.go @@ -27,10 +27,10 @@ import ( "time" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/params" ) @@ -78,7 +78,7 @@ func pricedTransaction(nonce uint64, gaslimit uint64, gasprice *big.Int, key *ec } func setupTxPool() (*TxPool, *ecdsa.PrivateKey) { - statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) + statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase())) blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)} key, _ := crypto.GenerateKey() @@ -163,7 +163,7 @@ func (c *testChain) State() (*state.StateDB, error) { // a state change between those fetches. stdb := c.statedb if *c.trigger { - c.statedb, _ = state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) + c.statedb, _ = state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase())) // simulate that the new head block included tx0 and tx1 c.statedb.SetNonce(c.address, 2) c.statedb.SetBalance(c.address, new(big.Int).SetUint64(params.Ether)) @@ -181,7 +181,7 @@ func TestStateChangeDuringTransactionPoolReset(t *testing.T) { var ( key, _ = crypto.GenerateKey() address = crypto.PubkeyToAddress(key.PublicKey) - statedb, _ = state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) + statedb, _ = state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase())) trigger = false ) @@ -335,7 +335,7 @@ func TestTransactionChainFork(t *testing.T) { addr := crypto.PubkeyToAddress(key.PublicKey) resetState := func() { - statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) + statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase())) statedb.AddBalance(addr, big.NewInt(100000000000000)) pool.chain = &testBlockChain{statedb, 1000000, new(event.Feed)} @@ -364,7 +364,7 @@ func TestTransactionDoubleNonce(t *testing.T) { addr := crypto.PubkeyToAddress(key.PublicKey) resetState := func() { - statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) + statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase())) statedb.AddBalance(addr, big.NewInt(100000000000000)) pool.chain = &testBlockChain{statedb, 1000000, new(event.Feed)} @@ -554,7 +554,7 @@ func TestTransactionPostponing(t *testing.T) { t.Parallel() // Create the pool to test the postponing with - statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) + statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase())) blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)} pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain) @@ -769,7 +769,7 @@ func testTransactionQueueGlobalLimiting(t *testing.T, nolocals bool) { t.Parallel() // Create the pool to test the limit enforcement with - statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) + statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase())) blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)} config := testTxPoolConfig @@ -857,7 +857,7 @@ func testTransactionQueueTimeLimiting(t *testing.T, nolocals bool) { evictionInterval = time.Second // Create the pool to test the non-expiration enforcement - statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) + statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase())) blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)} config := testTxPoolConfig @@ -1011,7 +1011,7 @@ func TestTransactionPendingGlobalLimiting(t *testing.T) { t.Parallel() // Create the pool to test the limit enforcement with - statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) + statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase())) blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)} config := testTxPoolConfig @@ -1057,7 +1057,7 @@ func TestTransactionCapClearsFromAll(t *testing.T) { t.Parallel() // Create the pool to test the limit enforcement with - statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) + statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase())) blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)} config := testTxPoolConfig @@ -1091,7 +1091,7 @@ func TestTransactionPendingMinimumAllowance(t *testing.T) { t.Parallel() // Create the pool to test the limit enforcement with - statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) + statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase())) blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)} config := testTxPoolConfig @@ -1139,7 +1139,7 @@ func TestTransactionPoolRepricing(t *testing.T) { t.Parallel() // Create the pool to test the pricing enforcement with - statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) + statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase())) blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)} pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain) @@ -1260,7 +1260,7 @@ func TestTransactionPoolRepricingKeepsLocals(t *testing.T) { t.Parallel() // Create the pool to test the pricing enforcement with - statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) + statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase())) blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)} pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain) @@ -1322,7 +1322,7 @@ func TestTransactionPoolUnderpricing(t *testing.T) { t.Parallel() // Create the pool to test the pricing enforcement with - statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) + statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase())) blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)} config := testTxPoolConfig @@ -1428,7 +1428,7 @@ func TestTransactionPoolStableUnderpricing(t *testing.T) { t.Parallel() // Create the pool to test the pricing enforcement with - statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) + statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase())) blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)} config := testTxPoolConfig @@ -1494,7 +1494,7 @@ func TestTransactionReplacement(t *testing.T) { t.Parallel() // Create the pool to test the pricing enforcement with - statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) + statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase())) blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)} pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain) @@ -1588,7 +1588,7 @@ func testTransactionJournaling(t *testing.T, nolocals bool) { os.Remove(journal) // Create the original pool to inject transaction into the journal - statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) + statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase())) blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)} config := testTxPoolConfig @@ -1686,7 +1686,7 @@ func TestTransactionStatusCheck(t *testing.T) { t.Parallel() // Create the pool to test the status retrievals with - statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) + statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase())) blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)} pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain) diff --git a/core/vm/runtime/runtime.go b/core/vm/runtime/runtime.go index cda49a34b4..db1f6f3822 100644 --- a/core/vm/runtime/runtime.go +++ b/core/vm/runtime/runtime.go @@ -22,10 +22,10 @@ import ( "time" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/params" ) @@ -99,7 +99,7 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) { setDefaults(cfg) if cfg.State == nil { - cfg.State, _ = state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) + cfg.State, _ = state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase())) } var ( address = common.BytesToAddress([]byte("contract")) @@ -129,7 +129,7 @@ func Create(input []byte, cfg *Config) ([]byte, common.Address, uint64, error) { setDefaults(cfg) if cfg.State == nil { - cfg.State, _ = state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) + cfg.State, _ = state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase())) } var ( vmenv = NewEnv(cfg) diff --git a/core/vm/runtime/runtime_test.go b/core/vm/runtime/runtime_test.go index bac06e524b..15f545ddca 100644 --- a/core/vm/runtime/runtime_test.go +++ b/core/vm/runtime/runtime_test.go @@ -23,9 +23,9 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/params" ) @@ -95,7 +95,7 @@ func TestExecute(t *testing.T) { } func TestCall(t *testing.T) { - state, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) + state, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase())) address := common.HexToAddress("0x0a") state.SetCode(address, []byte{ byte(vm.PUSH1), 10, @@ -151,7 +151,7 @@ func BenchmarkCall(b *testing.B) { } func benchmarkEVM_Create(bench *testing.B, code string) { var ( - statedb, _ = state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) + statedb, _ = state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase())) sender = common.BytesToAddress([]byte("sender")) receiver = common.BytesToAddress([]byte("receiver")) ) diff --git a/ethdb/interface.go b/database/batch.go similarity index 55% rename from ethdb/interface.go rename to database/batch.go index af13557798..43de4720e5 100644 --- a/ethdb/interface.go +++ b/database/batch.go @@ -1,4 +1,4 @@ -// Copyright 2014 The go-ethereum Authors +// Copyright 2018 The go-ethereum Authors // This file is part of the go-ethereum library. // // The go-ethereum library is free software: you can redistribute it and/or modify @@ -14,39 +14,31 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -package ethdb +package database -// Code using batches should try to add this much data to the batch. -// The value was determined empirically. +// IdealBatchSize defines the size of the data batches should ideally add in one +// write. const IdealBatchSize = 100 * 1024 -// Putter wraps the database write operation supported by both batches and regular databases. -type Putter interface { - Put(key []byte, value []byte) error -} - -// Deleter wraps the database delete operation supported by both batches and regular databases. -type Deleter interface { - Delete(key []byte) error -} - -// Database wraps all database operations. All methods are safe for concurrent use. -type Database interface { - Putter - Deleter - Get(key []byte) ([]byte, error) - Has(key []byte) (bool, error) - Close() - NewBatch() Batch -} - // Batch is a write-only database that commits changes to its host database -// when Write is called. Batch cannot be used concurrently. +// when Write is called. A batch cannot be used concurrently. type Batch interface { - Putter + Writer Deleter - ValueSize() int // amount of data in the batch + + // ValueSize retrieves the amount of data queued up for writing. + ValueSize() int + + // Write flushes any accumulated data to disk. Write() error + // Reset resets the batch for reuse Reset() } + +// Batcher wraps the NewBatch method of a backing data store. +type Batcher interface { + // NewBatch creates a write-only database that buffers changes to its host db + // until a final write is called. + NewBatch() Batch +} diff --git a/database/database.go b/database/database.go new file mode 100644 index 0000000000..08aea43d08 --- /dev/null +++ b/database/database.go @@ -0,0 +1,84 @@ +// Copyright 2018 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package database defines the interfaces for an Ethereum data store. +package database + +import "io" + +// Reader wraps the Has and Get method of a backing data store. +type Reader interface { + // Has retrieves if a key is present in the key-value data store. + Has(key []byte) (bool, error) + + // Get retrieves the given key if it's present in the key-value data store. + Get(key []byte) ([]byte, error) +} + +// Writer wraps the Put method of a backing data store. +type Writer interface { + // Put inserts the given value into the key-value data store. + Put(key []byte, value []byte) error +} + +// Deleter wraps the Delete method of a backing data store. +type Deleter interface { + // Delete removes the key from the key-value data store. + Delete(key []byte) error +} + +// Stater wraps the Stat method of a backing data store. +type Stater interface { + // Stat returns a particular internal stat of the database. + Stat(property string) (string, error) +} + +// KeyValueStore contains all the methods required to allow handling different +// key-value data stores backing the high level database. +type KeyValueStore interface { + Reader + Writer + Deleter + Batcher + Iteratorer + Stater + io.Closer +} + +// Ancienter wraps the Ancient method for a backing immutable chain data store. +type Ancienter interface { + // Ancient retrieves an ancient binary blob from the append-only immutable files. + Ancient(kind string, number uint64) ([]byte, error) +} + +// AncientReader contains the methods required to access both key-value as well as +// immutable ancient data. +type AncientReader interface { + Reader + Ancienter +} + +// Database contains all the methods required by the high level database to not +// only access +type Database interface { + AncientReader + Writer + Deleter + Batcher + Iteratorer + Stater + io.Closer +} diff --git a/database/iterator.go b/database/iterator.go new file mode 100644 index 0000000000..c0b2909743 --- /dev/null +++ b/database/iterator.go @@ -0,0 +1,57 @@ +// Copyright 2018 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package database + +// Iterator iterates over a databases key/value pairs in key order. +// +// When it encounters an error any seek will return false and will yield no key/ +// value pairs. The error can be queried by calling the Error method. Calling +// Release is still necessary. +// +// An iterator must be released after use, but it is not necessary to read an +// iterator until exhaustion. An iterator is not safe for concurrent use, but it +// is safe to use multiple iterators concurrently. +type Iterator interface { + // Next moves the iterator to the next key/value pair. It returns whether the + // iterator is exhausted. + Next() bool + + // Key returns the key of the current key/value pair, or nil if done. The caller + // should not modify the contents of the returned slice, and its contents may + // change on the next call to Next. + Key() []byte + + // Value returns the value of the current key/value pair, or nil if done. The + // caller should not modify the contents of the returned slice, and its contents + // may change on the next call to Next. + Value() []byte + + // Release releases associated resources. Release should always succeed and can + // be called multiple times without causing error. + Release() +} + +// Iteratorer wraps the NewIterator methods of a backing data store. +type Iteratorer interface { + // NewIterator creates a binary-alphabetical iterator over the entire keyspace + // contained within the key-value database. + NewIterator() Iterator + + // NewIteratorWithPrefix creates a binary-alphabetical iterator over a subset + // of database content with a particular key prefix. + NewIteratorWithPrefix(prefix []byte) Iterator +} diff --git a/eth/api_backend.go b/eth/api_backend.go index a48815e0db..15e72b682c 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -28,9 +28,9 @@ import ( "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/database" "github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/eth/gasprice" - "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" @@ -201,7 +201,7 @@ func (b *EthAPIBackend) SuggestPrice(ctx context.Context) (*big.Int, error) { return b.gpo.SuggestPrice(ctx) } -func (b *EthAPIBackend) ChainDb() ethdb.Database { +func (b *EthAPIBackend) ChainDb() database.Database { return b.eth.ChainDb() } diff --git a/eth/api_test.go b/eth/api_test.go index 47b062a40c..cdd5bb8e34 100644 --- a/eth/api_test.go +++ b/eth/api_test.go @@ -22,8 +22,8 @@ import ( "github.com/davecgh/go-spew/spew" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" - "github.com/ethereum/go-ethereum/ethdb" ) var dumper = spew.ConfigState{Indent: " "} @@ -31,7 +31,7 @@ var dumper = spew.ConfigState{Indent: " "} func TestStorageRangeAt(t *testing.T) { // Create a state where account 0x010000... has a few storage entries. var ( - state, _ = state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) + state, _ = state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase())) addr = common.Address{0x01} keys = []common.Hash{ // hashes of Keys of storage common.HexToHash("340dd630ad21bf010b4e676dbfa9ba9a02175262d1fa356232cfde6cb5b47ef2"), diff --git a/eth/backend.go b/eth/backend.go index deed195563..08e555f868 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -24,7 +24,6 @@ import ( "runtime" "sync" "sync/atomic" - "time" "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/common" @@ -37,12 +36,11 @@ import ( "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/database" "github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/eth/filters" "github.com/ethereum/go-ethereum/eth/gasprice" - "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" - "github.com/ethereum/go-ethereum/freezer" "github.com/ethereum/go-ethereum/internal/ethapi" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/miner" @@ -71,12 +69,11 @@ type Ethereum struct { // Handlers txPool *core.TxPool blockchain *core.BlockChain - freezer *freezer.Freezer protocolManager *ProtocolManager lesServer LesServer // DB interfaces - chainDb ethdb.Database // Block chain database + chainDb database.Database // Block chain database eventMux *event.TypeMux engine consensus.Engine @@ -123,7 +120,7 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { log.Info("Allocated trie memory caches", "clean", common.StorageSize(config.TrieCleanCache)*1024*1024, "dirty", common.StorageSize(config.TrieDirtyCache)*1024*1024) // Assemble the Ethereum object - chainDb, err := CreateDB(ctx, config, "chaindata") + chainDb, err := ctx.OpenDatabaseWithFreezer("chaindata", config.DatabaseCache, config.DatabaseHandles, config.DatabaseFreezer, "eth/db/chaindata/") if err != nil { return nil, err } @@ -171,12 +168,6 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { if err != nil { return nil, err } - eth.freezer, err = freezer.New(ctx.ResolvePath("freezer")) - if err != nil { - return nil, err - } - go eth.freezer.Freeze(eth.blockchain, chainDb, time.Minute, 60000) - // Rewind the chain in case of an incompatible config upgrade. if compat, ok := genesisErr.(*params.ConfigCompatError); ok { log.Warn("Rewinding chain to upgrade configuration", "err", compat) @@ -224,20 +215,8 @@ func makeExtraData(extra []byte) []byte { return extra } -// CreateDB creates the chain database. -func CreateDB(ctx *node.ServiceContext, config *Config, name string) (ethdb.Database, error) { - db, err := ctx.OpenDatabase(name, config.DatabaseCache, config.DatabaseHandles) - if err != nil { - return nil, err - } - if db, ok := db.(*ethdb.LDBDatabase); ok { - db.Meter("eth/db/chaindata/") - } - return db, nil -} - // CreateConsensusEngine creates the required type of consensus engine instance for an Ethereum service -func CreateConsensusEngine(ctx *node.ServiceContext, chainConfig *params.ChainConfig, config *ethash.Config, notify []string, noverify bool, db ethdb.Database) consensus.Engine { +func CreateConsensusEngine(ctx *node.ServiceContext, chainConfig *params.ChainConfig, config *ethash.Config, notify []string, noverify bool, db database.Database) consensus.Engine { // If proof-of-authority is requested, set it up if chainConfig.Clique != nil { return clique.New(chainConfig.Clique, db) @@ -482,7 +461,7 @@ func (s *Ethereum) BlockChain() *core.BlockChain { return s.blockchain } func (s *Ethereum) TxPool() *core.TxPool { return s.txPool } func (s *Ethereum) EventMux() *event.TypeMux { return s.eventMux } func (s *Ethereum) Engine() consensus.Engine { return s.engine } -func (s *Ethereum) ChainDb() ethdb.Database { return s.chainDb } +func (s *Ethereum) ChainDb() database.Database { return s.chainDb } func (s *Ethereum) IsListening() bool { return true } // Always listening func (s *Ethereum) EthVersion() int { return int(s.protocolManager.SubProtocols[0].Version) } func (s *Ethereum) NetVersion() uint64 { return s.networkID } diff --git a/eth/bloombits.go b/eth/bloombits.go index c7bb561402..272115ad52 100644 --- a/eth/bloombits.go +++ b/eth/bloombits.go @@ -26,7 +26,7 @@ import ( "github.com/ethereum/go-ethereum/core/bloombits" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/database" ) const ( @@ -89,7 +89,7 @@ const ( // for the Ethereum header bloom filters, permitting blazing fast filtering. type BloomIndexer struct { size uint64 // section size to generate bloombits for - db ethdb.Database // database instance to write index data and metadata into + db database.Database // database instance to write index data and metadata into gen *bloombits.Generator // generator to rotate the bloom bits crating the bloom index section uint64 // Section is the section number being processed currently head common.Hash // Head is the hash of the last header processed @@ -97,12 +97,12 @@ type BloomIndexer struct { // NewBloomIndexer returns a chain indexer that generates bloom bits data for the // canonical chain for fast logs filtering. -func NewBloomIndexer(db ethdb.Database, size, confirms uint64) *core.ChainIndexer { +func NewBloomIndexer(db database.Database, size, confirms uint64) *core.ChainIndexer { backend := &BloomIndexer{ db: db, size: size, } - table := ethdb.NewTable(db, string(rawdb.BloomBitsIndexPrefix)) + table := rawdb.NewTable(db, string(rawdb.BloomBitsIndexPrefix)) return core.NewChainIndexer(db, table, backend, size, confirms, bloomThrottling, "bloombits") } diff --git a/eth/config.go b/eth/config.go index f71b8dfee4..f2d0235019 100644 --- a/eth/config.go +++ b/eth/config.go @@ -102,9 +102,11 @@ type Config struct { SkipBcVersionCheck bool `toml:"-"` DatabaseHandles int `toml:"-"` DatabaseCache int - TrieCleanCache int - TrieDirtyCache int - TrieTimeout time.Duration + DatabaseFreezer string + + TrieCleanCache int + TrieDirtyCache int + TrieTimeout time.Duration // Mining-related options Etherbase common.Address `toml:",omitempty"` diff --git a/eth/downloader/downloader.go b/eth/downloader/downloader.go index 58d19254c2..2f9b131c5b 100644 --- a/eth/downloader/downloader.go +++ b/eth/downloader/downloader.go @@ -29,7 +29,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/database" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" @@ -102,7 +102,7 @@ type Downloader struct { genesis uint64 // Genesis block number to limit sync to (e.g. light client CHT) queue *queue // Scheduler for selecting the hashes to download peers *peerSet // Set of active peers from which download can proceed - stateDB ethdb.Database + stateDB database.Database rttEstimate uint64 // Round trip time to target for download requests rttConfidence uint64 // Confidence in the estimated RTT (unit: millionths to allow atomic ops) @@ -205,7 +205,7 @@ type BlockChain interface { } // New creates a new downloader to fetch hashes and blocks from remote peers. -func New(mode SyncMode, stateDb ethdb.Database, mux *event.TypeMux, chain BlockChain, lightchain LightChain, dropPeer peerDropFn) *Downloader { +func New(mode SyncMode, stateDb database.Database, mux *event.TypeMux, chain BlockChain, lightchain LightChain, dropPeer peerDropFn) *Downloader { if lightchain == nil { lightchain = chain } diff --git a/eth/downloader/downloader_test.go b/eth/downloader/downloader_test.go index 1a42965d39..cc246e64cf 100644 --- a/eth/downloader/downloader_test.go +++ b/eth/downloader/downloader_test.go @@ -28,8 +28,9 @@ import ( ethereum "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/database" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/trie" ) @@ -45,9 +46,9 @@ func init() { type downloadTester struct { downloader *Downloader - genesis *types.Block // Genesis blocks used by the tester and peers - stateDb ethdb.Database // Database used by the tester for syncing from peers - peerDb ethdb.Database // Database of the peers containing all data + genesis *types.Block // Genesis blocks used by the tester and peers + stateDb database.Database // Database used by the tester for syncing from peers + peerDb database.Database // Database of the peers containing all data peers map[string]*downloadTesterPeer ownHashes []common.Hash // Hash chain belonging to the tester @@ -71,8 +72,9 @@ func newTester() *downloadTester { ownReceipts: map[common.Hash]types.Receipts{testGenesis.Hash(): nil}, ownChainTd: map[common.Hash]*big.Int{testGenesis.Hash(): testGenesis.Difficulty()}, } - tester.stateDb = ethdb.NewMemDatabase() + tester.stateDb = rawdb.NewMemoryDatabase() tester.stateDb.Put(testGenesis.Root().Bytes(), []byte{0x00}) + tester.downloader = New(FullSync, tester.stateDb, new(event.TypeMux), tester, nil, tester.dropPeer) return tester } diff --git a/eth/downloader/fakepeer.go b/eth/downloader/fakepeer.go index 59832facaa..00e4e8f63d 100644 --- a/eth/downloader/fakepeer.go +++ b/eth/downloader/fakepeer.go @@ -23,7 +23,7 @@ import ( "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/database" ) // FakePeer is a mock downloader peer that operates on a local database instance @@ -31,13 +31,13 @@ import ( // sync commands from an existing local database. type FakePeer struct { id string - db ethdb.Database + db database.Database hc *core.HeaderChain dl *Downloader } // NewFakePeer creates a new mock downloader peer with the given data sources. -func NewFakePeer(id string, db ethdb.Database, hc *core.HeaderChain, dl *Downloader) *FakePeer { +func NewFakePeer(id string, db database.Database, hc *core.HeaderChain, dl *Downloader) *FakePeer { return &FakePeer{id: id, db: db, hc: hc, dl: dl} } diff --git a/eth/downloader/statesync.go b/eth/downloader/statesync.go index 0675a91cdc..f4bd6a0138 100644 --- a/eth/downloader/statesync.go +++ b/eth/downloader/statesync.go @@ -25,7 +25,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" - "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/database" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/trie" "golang.org/x/crypto/sha3" @@ -325,7 +325,7 @@ func (s *stateSync) loop() (err error) { } func (s *stateSync) commit(force bool) error { - if !force && s.bytesUncommitted < ethdb.IdealBatchSize { + if !force && s.bytesUncommitted < database.IdealBatchSize { return nil } start := time.Now() diff --git a/eth/downloader/testchain_test.go b/eth/downloader/testchain_test.go index 0b5a214257..4ae342dc63 100644 --- a/eth/downloader/testchain_test.go +++ b/eth/downloader/testchain_test.go @@ -24,9 +24,9 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/params" ) @@ -34,7 +34,7 @@ import ( var ( testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") testAddress = crypto.PubkeyToAddress(testKey.PublicKey) - testDB = ethdb.NewMemDatabase() + testDB = rawdb.NewMemoryDatabase() testGenesis = core.GenesisBlockForTesting(testDB, testAddress, big.NewInt(1000000000)) ) diff --git a/eth/fetcher/fetcher_test.go b/eth/fetcher/fetcher_test.go index a86e773e3b..83172c5348 100644 --- a/eth/fetcher/fetcher_test.go +++ b/eth/fetcher/fetcher_test.go @@ -27,14 +27,14 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/params" ) var ( - testdb = ethdb.NewMemDatabase() + testdb = rawdb.NewMemoryDatabase() testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") testAddress = crypto.PubkeyToAddress(testKey.PublicKey) genesis = core.GenesisBlockForTesting(testdb, testAddress, big.NewInt(1000000000)) diff --git a/eth/filters/api.go b/eth/filters/api.go index 5ed80a8875..e2cf19ddc5 100644 --- a/eth/filters/api.go +++ b/eth/filters/api.go @@ -29,7 +29,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/database" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/rpc" ) @@ -55,7 +55,7 @@ type PublicFilterAPI struct { backend Backend mux *event.TypeMux quit chan struct{} - chainDb ethdb.Database + chainDb database.Database events *EventSystem filtersMu sync.Mutex filters map[rpc.ID]*filter diff --git a/eth/filters/bench_test.go b/eth/filters/bench_test.go index c5f681e024..ee498acd10 100644 --- a/eth/filters/bench_test.go +++ b/eth/filters/bench_test.go @@ -17,7 +17,6 @@ package filters import ( - "bytes" "context" "fmt" "testing" @@ -28,7 +27,7 @@ import ( "github.com/ethereum/go-ethereum/core/bloombits" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/database" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/node" ) @@ -67,7 +66,7 @@ func benchmarkBloomBits(b *testing.B, sectionSize uint64) { benchDataDir := node.DefaultDataDir() + "/geth/chaindata" fmt.Println("Running bloombits benchmark section size:", sectionSize) - db, err := ethdb.NewLDBDatabase(benchDataDir, 128, 1024) + db, err := rawdb.NewLeveldbDatabase(benchDataDir, 128, 1024, "") if err != nil { b.Fatalf("error opening database at %v: %v", benchDataDir, err) } @@ -129,7 +128,7 @@ func benchmarkBloomBits(b *testing.B, sectionSize uint64) { for i := 0; i < benchFilterCnt; i++ { if i%20 == 0 { db.Close() - db, _ = ethdb.NewLDBDatabase(benchDataDir, 128, 1024) + db, _ = rawdb.NewLeveldbDatabase(benchDataDir, 128, 1024, "") backend = &testBackend{mux, db, cnt, new(event.Feed), new(event.Feed), new(event.Feed), new(event.Feed)} } var addr common.Address @@ -146,37 +145,21 @@ func benchmarkBloomBits(b *testing.B, sectionSize uint64) { db.Close() } -func forEachKey(db ethdb.Database, startPrefix, endPrefix []byte, fn func(key []byte)) { - it := db.(*ethdb.LDBDatabase).NewIterator() - it.Seek(startPrefix) - for it.Valid() { - key := it.Key() - cmpLen := len(key) - if len(endPrefix) < cmpLen { - cmpLen = len(endPrefix) - } - if bytes.Compare(key[:cmpLen], endPrefix) == 1 { - break - } - fn(common.CopyBytes(key)) - it.Next() - } - it.Release() -} - var bloomBitsPrefix = []byte("bloomBits-") -func clearBloomBits(db ethdb.Database) { +func clearBloomBits(db database.Database) { fmt.Println("Clearing bloombits data...") - forEachKey(db, bloomBitsPrefix, bloomBitsPrefix, func(key []byte) { - db.Delete(key) - }) + it := db.NewIteratorWithPrefix(bloomBitsPrefix) + for it.Next() { + db.Delete(it.Key()) + } + it.Release() } func BenchmarkNoBloomBits(b *testing.B) { benchDataDir := node.DefaultDataDir() + "/geth/chaindata" fmt.Println("Running benchmark without bloombits") - db, err := ethdb.NewLDBDatabase(benchDataDir, 128, 1024) + db, err := rawdb.NewLeveldbDatabase(benchDataDir, 128, 1024, "") if err != nil { b.Fatalf("error opening database at %v: %v", benchDataDir, err) } diff --git a/eth/filters/filter.go b/eth/filters/filter.go index 071613ad7a..1962ec3697 100644 --- a/eth/filters/filter.go +++ b/eth/filters/filter.go @@ -25,13 +25,13 @@ import ( "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/bloombits" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/database" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/rpc" ) type Backend interface { - ChainDb() ethdb.Database + ChainDb() database.Database EventMux() *event.TypeMux HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error) HeaderByHash(ctx context.Context, blockHash common.Hash) (*types.Header, error) @@ -51,7 +51,7 @@ type Backend interface { type Filter struct { backend Backend - db ethdb.Database + db database.Database addresses []common.Address topics [][]common.Hash diff --git a/eth/filters/filter_system_test.go b/eth/filters/filter_system_test.go index e71080b1ab..8a78978d9e 100644 --- a/eth/filters/filter_system_test.go +++ b/eth/filters/filter_system_test.go @@ -32,7 +32,7 @@ import ( "github.com/ethereum/go-ethereum/core/bloombits" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/database" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" @@ -40,7 +40,7 @@ import ( type testBackend struct { mux *event.TypeMux - db ethdb.Database + db database.Database sections uint64 txFeed *event.Feed rmLogsFeed *event.Feed @@ -48,7 +48,7 @@ type testBackend struct { chainFeed *event.Feed } -func (b *testBackend) ChainDb() ethdb.Database { +func (b *testBackend) ChainDb() database.Database { return b.db } @@ -161,7 +161,7 @@ func TestBlockSubscription(t *testing.T) { var ( mux = new(event.TypeMux) - db = ethdb.NewMemDatabase() + db = rawdb.NewMemoryDatabase() txFeed = new(event.Feed) rmLogsFeed = new(event.Feed) logsFeed = new(event.Feed) @@ -218,7 +218,7 @@ func TestPendingTxFilter(t *testing.T) { var ( mux = new(event.TypeMux) - db = ethdb.NewMemDatabase() + db = rawdb.NewMemoryDatabase() txFeed = new(event.Feed) rmLogsFeed = new(event.Feed) logsFeed = new(event.Feed) @@ -278,7 +278,7 @@ func TestPendingTxFilter(t *testing.T) { func TestLogFilterCreation(t *testing.T) { var ( mux = new(event.TypeMux) - db = ethdb.NewMemDatabase() + db = rawdb.NewMemoryDatabase() txFeed = new(event.Feed) rmLogsFeed = new(event.Feed) logsFeed = new(event.Feed) @@ -327,7 +327,7 @@ func TestInvalidLogFilterCreation(t *testing.T) { var ( mux = new(event.TypeMux) - db = ethdb.NewMemDatabase() + db = rawdb.NewMemoryDatabase() txFeed = new(event.Feed) rmLogsFeed = new(event.Feed) logsFeed = new(event.Feed) @@ -354,7 +354,7 @@ func TestInvalidLogFilterCreation(t *testing.T) { func TestInvalidGetLogsRequest(t *testing.T) { var ( mux = new(event.TypeMux) - db = ethdb.NewMemDatabase() + db = rawdb.NewMemoryDatabase() txFeed = new(event.Feed) rmLogsFeed = new(event.Feed) logsFeed = new(event.Feed) @@ -384,7 +384,7 @@ func TestLogFilter(t *testing.T) { var ( mux = new(event.TypeMux) - db = ethdb.NewMemDatabase() + db = rawdb.NewMemoryDatabase() txFeed = new(event.Feed) rmLogsFeed = new(event.Feed) logsFeed = new(event.Feed) @@ -503,7 +503,7 @@ func TestPendingLogsSubscription(t *testing.T) { var ( mux = new(event.TypeMux) - db = ethdb.NewMemDatabase() + db = rawdb.NewMemoryDatabase() txFeed = new(event.Feed) rmLogsFeed = new(event.Feed) logsFeed = new(event.Feed) diff --git a/eth/filters/filter_test.go b/eth/filters/filter_test.go index 396a03d611..970293e6bc 100644 --- a/eth/filters/filter_test.go +++ b/eth/filters/filter_test.go @@ -29,7 +29,6 @@ import ( "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/params" ) @@ -51,7 +50,7 @@ func BenchmarkFilters(b *testing.B) { defer os.RemoveAll(dir) var ( - db, _ = ethdb.NewLDBDatabase(dir, 0, 0) + db, _ = rawdb.NewLeveldbDatabase(dir, 0, 0, "") mux = new(event.TypeMux) txFeed = new(event.Feed) rmLogsFeed = new(event.Feed) @@ -110,7 +109,7 @@ func TestFilters(t *testing.T) { defer os.RemoveAll(dir) var ( - db, _ = ethdb.NewLDBDatabase(dir, 0, 0) + db, _ = rawdb.NewLeveldbDatabase(dir, 0, 0, "") mux = new(event.TypeMux) txFeed = new(event.Feed) rmLogsFeed = new(event.Feed) diff --git a/eth/handler.go b/eth/handler.go index b42612a566..d5fafffec7 100644 --- a/eth/handler.go +++ b/eth/handler.go @@ -31,9 +31,9 @@ import ( "github.com/ethereum/go-ethereum/consensus/misc" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/database" "github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/eth/fetcher" - "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/p2p" @@ -103,7 +103,7 @@ type ProtocolManager struct { // NewProtocolManager returns a new Ethereum sub protocol manager. The Ethereum sub protocol manages peers capable // with the Ethereum network. -func NewProtocolManager(config *params.ChainConfig, mode downloader.SyncMode, networkID uint64, mux *event.TypeMux, txpool txPool, engine consensus.Engine, blockchain *core.BlockChain, chaindb ethdb.Database, whitelist map[uint64]common.Hash) (*ProtocolManager, error) { +func NewProtocolManager(config *params.ChainConfig, mode downloader.SyncMode, networkID uint64, mux *event.TypeMux, txpool txPool, engine consensus.Engine, blockchain *core.BlockChain, chaindb database.Database, whitelist map[uint64]common.Hash) (*ProtocolManager, error) { // Create the protocol manager with the base fields manager := &ProtocolManager{ networkID: networkID, diff --git a/eth/handler_test.go b/eth/handler_test.go index 9fffd95811..7028d8d08e 100644 --- a/eth/handler_test.go +++ b/eth/handler_test.go @@ -27,12 +27,12 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" "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/eth/downloader" - "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/params" @@ -344,11 +344,15 @@ func testGetNodeData(t *testing.T, protocol int) { // Fetch for now the entire chain db hashes := []common.Hash{} - for _, key := range db.Keys() { - if len(key) == len(common.Hash{}) { + + it := db.NewIterator() + for it.Next() { + if key := it.Key(); len(key) == common.HashLength { hashes = append(hashes, common.BytesToHash(key)) } } + it.Release() + p2p.Send(peer.app, 0x0d, hashes) msg, err := peer.app.ReadMsg() if err != nil { @@ -367,7 +371,7 @@ func testGetNodeData(t *testing.T, protocol int) { t.Errorf("data hash mismatch: have %x, want %x", hash, want) } } - statedb := ethdb.NewMemDatabase() + statedb := rawdb.NewMemoryDatabase() for i := 0; i < len(data); i++ { statedb.Put(hashes[i].Bytes(), data[i]) } @@ -469,7 +473,7 @@ func testDAOChallenge(t *testing.T, localForked, remoteForked bool, timeout bool var ( evmux = new(event.TypeMux) pow = ethash.NewFaker() - db = ethdb.NewMemDatabase() + db = rawdb.NewMemoryDatabase() config = ¶ms.ChainConfig{DAOForkBlock: big.NewInt(1), DAOForkSupport: localForked} gspec = &core.Genesis{Config: config} genesis = gspec.MustCommit(db) @@ -550,7 +554,7 @@ func testBroadcastBlock(t *testing.T, totalPeers, broadcastExpected int) { var ( evmux = new(event.TypeMux) pow = ethash.NewFaker() - db = ethdb.NewMemDatabase() + db = rawdb.NewMemoryDatabase() config = ¶ms.ChainConfig{} gspec = &core.Genesis{Config: config} genesis = gspec.MustCommit(db) diff --git a/eth/helper_test.go b/eth/helper_test.go index b18a02baf9..1383720957 100644 --- a/eth/helper_test.go +++ b/eth/helper_test.go @@ -30,11 +30,12 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/rawdb" "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/database" "github.com/ethereum/go-ethereum/eth/downloader" - "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/enode" @@ -49,11 +50,11 @@ var ( // newTestProtocolManager creates a new protocol manager for testing purposes, // with the given number of blocks already known, and potential notification // channels for different events. -func newTestProtocolManager(mode downloader.SyncMode, blocks int, generator func(int, *core.BlockGen), newtx chan<- []*types.Transaction) (*ProtocolManager, *ethdb.MemDatabase, error) { +func newTestProtocolManager(mode downloader.SyncMode, blocks int, generator func(int, *core.BlockGen), newtx chan<- []*types.Transaction) (*ProtocolManager, database.Database, error) { var ( evmux = new(event.TypeMux) engine = ethash.NewFaker() - db = ethdb.NewMemDatabase() + db = rawdb.NewMemoryDatabase() gspec = &core.Genesis{ Config: params.TestChainConfig, Alloc: core.GenesisAlloc{testBank: {Balance: big.NewInt(1000000)}}, @@ -78,7 +79,7 @@ func newTestProtocolManager(mode downloader.SyncMode, blocks int, generator func // with the given number of blocks already known, and potential notification // channels for different events. In case of an error, the constructor force- // fails the test. -func newTestProtocolManagerMust(t *testing.T, mode downloader.SyncMode, blocks int, generator func(int, *core.BlockGen), newtx chan<- []*types.Transaction) (*ProtocolManager, *ethdb.MemDatabase) { +func newTestProtocolManagerMust(t *testing.T, mode downloader.SyncMode, blocks int, generator func(int, *core.BlockGen), newtx chan<- []*types.Transaction) (*ProtocolManager, database.Database) { pm, db, err := newTestProtocolManager(mode, blocks, generator, newtx) if err != nil { t.Fatalf("Failed to create protocol manager: %v", err) diff --git a/eth/tracers/tracers_test.go b/eth/tracers/tracers_test.go index b435b1694b..972e056e73 100644 --- a/eth/tracers/tracers_test.go +++ b/eth/tracers/tracers_test.go @@ -31,10 +31,10 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/rawdb" "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/ethdb" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/tests" @@ -155,6 +155,7 @@ func TestPrestateTracerCreate2(t *testing.T) { GasPrice: big.NewInt(1), } alloc := core.GenesisAlloc{} + // The code pushes 'deadbeef' into memory, then the other params, and calls CREATE2, then returns // the address alloc[common.HexToAddress("0x00000000000000000000000000000000deadbeef")] = core.GenesisAccount{ @@ -167,7 +168,8 @@ func TestPrestateTracerCreate2(t *testing.T) { Code: []byte{}, Balance: big.NewInt(500000000000000), } - statedb := tests.MakePreState(ethdb.NewMemDatabase(), alloc) + statedb := tests.MakePreState(rawdb.NewMemoryDatabase(), alloc) + // Create the tracer, the EVM environment and run it tracer, err := New("prestateTracer") if err != nil { @@ -240,7 +242,7 @@ func TestCallTracer(t *testing.T) { GasLimit: uint64(test.Context.GasLimit), GasPrice: tx.GasPrice(), } - statedb := tests.MakePreState(ethdb.NewMemDatabase(), test.Genesis.Alloc) + statedb := tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc) // Create the tracer, the EVM environment and run it tracer, err := New("callTracer") diff --git a/ethdb/.gitignore b/ethdb/.gitignore deleted file mode 100644 index f725d58d14..0000000000 --- a/ethdb/.gitignore +++ /dev/null @@ -1,12 +0,0 @@ -# See http://help.github.com/ignore-files/ for more about ignoring files. -# -# If you find yourself ignoring temporary files generated by your text editor -# or operating system, you probably want to add a global ignore instead: -# git config --global core.excludesfile ~/.gitignore_global - -/tmp -*/**/*un~ -*un~ -.DS_Store -*/**/.DS_Store - diff --git a/ethdb/database_js.go b/ethdb/database_js.go deleted file mode 100644 index ba6eeb5a23..0000000000 --- a/ethdb/database_js.go +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2014 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// +build js - -package ethdb - -import ( - "errors" -) - -var errNotSupported = errors.New("ethdb: not supported") - -type LDBDatabase struct { -} - -// NewLDBDatabase returns a LevelDB wrapped object. -func NewLDBDatabase(file string, cache int, handles int) (*LDBDatabase, error) { - return nil, errNotSupported -} - -// Path returns the path to the database directory. -func (db *LDBDatabase) Path() string { - return "" -} - -// Put puts the given key / value to the queue -func (db *LDBDatabase) Put(key []byte, value []byte) error { - return errNotSupported -} - -func (db *LDBDatabase) Has(key []byte) (bool, error) { - return false, errNotSupported -} - -// Get returns the given key if it's present. -func (db *LDBDatabase) Get(key []byte) ([]byte, error) { - return nil, errNotSupported -} - -// Delete deletes the key from the queue and database -func (db *LDBDatabase) Delete(key []byte) error { - return errNotSupported -} - -func (db *LDBDatabase) Close() { -} - -// Meter configures the database metrics collectors and -func (db *LDBDatabase) Meter(prefix string) { -} - -func (db *LDBDatabase) NewBatch() Batch { - return nil -} diff --git a/ethdb/database_js_test.go b/ethdb/database_js_test.go deleted file mode 100644 index b4c12ae0bc..0000000000 --- a/ethdb/database_js_test.go +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2014 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// +build js - -package ethdb_test - -import ( - "github.com/ethereum/go-ethereum/ethdb" -) - -var _ ethdb.Database = ðdb.LDBDatabase{} diff --git a/ethdb/database_test.go b/ethdb/database_test.go deleted file mode 100644 index 382fedbf9e..0000000000 --- a/ethdb/database_test.go +++ /dev/null @@ -1,214 +0,0 @@ -// Copyright 2014 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// +build !js - -package ethdb_test - -import ( - "bytes" - "fmt" - "io/ioutil" - "os" - "strconv" - "sync" - "testing" - - "github.com/ethereum/go-ethereum/ethdb" -) - -func newTestLDB() (*ethdb.LDBDatabase, func()) { - dirname, err := ioutil.TempDir(os.TempDir(), "ethdb_test_") - if err != nil { - panic("failed to create test file: " + err.Error()) - } - db, err := ethdb.NewLDBDatabase(dirname, 0, 0) - if err != nil { - panic("failed to create test database: " + err.Error()) - } - - return db, func() { - db.Close() - os.RemoveAll(dirname) - } -} - -var test_values = []string{"", "a", "1251", "\x00123\x00"} - -func TestLDB_PutGet(t *testing.T) { - db, remove := newTestLDB() - defer remove() - testPutGet(db, t) -} - -func TestMemoryDB_PutGet(t *testing.T) { - testPutGet(ethdb.NewMemDatabase(), t) -} - -func testPutGet(db ethdb.Database, t *testing.T) { - t.Parallel() - - for _, k := range test_values { - err := db.Put([]byte(k), nil) - if err != nil { - t.Fatalf("put failed: %v", err) - } - } - - for _, k := range test_values { - data, err := db.Get([]byte(k)) - if err != nil { - t.Fatalf("get failed: %v", err) - } - if len(data) != 0 { - t.Fatalf("get returned wrong result, got %q expected nil", string(data)) - } - } - - _, err := db.Get([]byte("non-exist-key")) - if err == nil { - t.Fatalf("expect to return a not found error") - } - - for _, v := range test_values { - err := db.Put([]byte(v), []byte(v)) - if err != nil { - t.Fatalf("put failed: %v", err) - } - } - - for _, v := range test_values { - data, err := db.Get([]byte(v)) - if err != nil { - t.Fatalf("get failed: %v", err) - } - if !bytes.Equal(data, []byte(v)) { - t.Fatalf("get returned wrong result, got %q expected %q", string(data), v) - } - } - - for _, v := range test_values { - err := db.Put([]byte(v), []byte("?")) - if err != nil { - t.Fatalf("put override failed: %v", err) - } - } - - for _, v := range test_values { - data, err := db.Get([]byte(v)) - if err != nil { - t.Fatalf("get failed: %v", err) - } - if !bytes.Equal(data, []byte("?")) { - t.Fatalf("get returned wrong result, got %q expected ?", string(data)) - } - } - - for _, v := range test_values { - orig, err := db.Get([]byte(v)) - if err != nil { - t.Fatalf("get failed: %v", err) - } - orig[0] = byte(0xff) - data, err := db.Get([]byte(v)) - if err != nil { - t.Fatalf("get failed: %v", err) - } - if !bytes.Equal(data, []byte("?")) { - t.Fatalf("get returned wrong result, got %q expected ?", string(data)) - } - } - - for _, v := range test_values { - err := db.Delete([]byte(v)) - if err != nil { - t.Fatalf("delete %q failed: %v", v, err) - } - } - - for _, v := range test_values { - _, err := db.Get([]byte(v)) - if err == nil { - t.Fatalf("got deleted value %q", v) - } - } -} - -func TestLDB_ParallelPutGet(t *testing.T) { - db, remove := newTestLDB() - defer remove() - testParallelPutGet(db, t) -} - -func TestMemoryDB_ParallelPutGet(t *testing.T) { - testParallelPutGet(ethdb.NewMemDatabase(), t) -} - -func testParallelPutGet(db ethdb.Database, t *testing.T) { - const n = 8 - var pending sync.WaitGroup - - pending.Add(n) - for i := 0; i < n; i++ { - go func(key string) { - defer pending.Done() - err := db.Put([]byte(key), []byte("v"+key)) - if err != nil { - panic("put failed: " + err.Error()) - } - }(strconv.Itoa(i)) - } - pending.Wait() - - pending.Add(n) - for i := 0; i < n; i++ { - go func(key string) { - defer pending.Done() - data, err := db.Get([]byte(key)) - if err != nil { - panic("get failed: " + err.Error()) - } - if !bytes.Equal(data, []byte("v"+key)) { - panic(fmt.Sprintf("get failed, got %q expected %q", []byte(data), []byte("v"+key))) - } - }(strconv.Itoa(i)) - } - pending.Wait() - - pending.Add(n) - for i := 0; i < n; i++ { - go func(key string) { - defer pending.Done() - err := db.Delete([]byte(key)) - if err != nil { - panic("delete failed: " + err.Error()) - } - }(strconv.Itoa(i)) - } - pending.Wait() - - pending.Add(n) - for i := 0; i < n; i++ { - go func(key string) { - defer pending.Done() - _, err := db.Get([]byte(key)) - if err == nil { - panic("get succeeded") - } - }(strconv.Itoa(i)) - } - pending.Wait() -} diff --git a/ethdb/memory_database.go b/ethdb/memory_database.go deleted file mode 100644 index 727f2f7ca3..0000000000 --- a/ethdb/memory_database.go +++ /dev/null @@ -1,143 +0,0 @@ -// Copyright 2014 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package ethdb - -import ( - "errors" - "sync" - - "github.com/ethereum/go-ethereum/common" -) - -/* - * This is a test memory database. Do not use for any production it does not get persisted - */ -type MemDatabase struct { - db map[string][]byte - lock sync.RWMutex -} - -func NewMemDatabase() *MemDatabase { - return &MemDatabase{ - db: make(map[string][]byte), - } -} - -func NewMemDatabaseWithCap(size int) *MemDatabase { - return &MemDatabase{ - db: make(map[string][]byte, size), - } -} - -func (db *MemDatabase) Put(key []byte, value []byte) error { - db.lock.Lock() - defer db.lock.Unlock() - - db.db[string(key)] = common.CopyBytes(value) - return nil -} - -func (db *MemDatabase) Has(key []byte) (bool, error) { - db.lock.RLock() - defer db.lock.RUnlock() - - _, ok := db.db[string(key)] - return ok, nil -} - -func (db *MemDatabase) Get(key []byte) ([]byte, error) { - db.lock.RLock() - defer db.lock.RUnlock() - - if entry, ok := db.db[string(key)]; ok { - return common.CopyBytes(entry), nil - } - return nil, errors.New("not found") -} - -func (db *MemDatabase) Keys() [][]byte { - db.lock.RLock() - defer db.lock.RUnlock() - - keys := [][]byte{} - for key := range db.db { - keys = append(keys, []byte(key)) - } - return keys -} - -func (db *MemDatabase) Delete(key []byte) error { - db.lock.Lock() - defer db.lock.Unlock() - - delete(db.db, string(key)) - return nil -} - -func (db *MemDatabase) Close() {} - -func (db *MemDatabase) NewBatch() Batch { - return &memBatch{db: db} -} - -func (db *MemDatabase) Len() int { return len(db.db) } - -type kv struct { - k, v []byte - del bool -} - -type memBatch struct { - db *MemDatabase - writes []kv - size int -} - -func (b *memBatch) Put(key, value []byte) error { - b.writes = append(b.writes, kv{common.CopyBytes(key), common.CopyBytes(value), false}) - b.size += len(value) - return nil -} - -func (b *memBatch) Delete(key []byte) error { - b.writes = append(b.writes, kv{common.CopyBytes(key), nil, true}) - b.size += 1 - return nil -} - -func (b *memBatch) Write() error { - b.db.lock.Lock() - defer b.db.lock.Unlock() - - for _, kv := range b.writes { - if kv.del { - delete(b.db.db, string(kv.k)) - continue - } - b.db.db[string(kv.k)] = kv.v - } - return nil -} - -func (b *memBatch) ValueSize() int { - return b.size -} - -func (b *memBatch) Reset() { - b.writes = b.writes[:0] - b.size = 0 -} diff --git a/ethdb/table.go b/ethdb/table.go deleted file mode 100644 index 28069c078e..0000000000 --- a/ethdb/table.go +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2014 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package ethdb - -type table struct { - db Database - prefix string -} - -// NewTable returns a Database object that prefixes all keys with a given -// string. -func NewTable(db Database, prefix string) Database { - return &table{ - db: db, - prefix: prefix, - } -} - -func (dt *table) Put(key []byte, value []byte) error { - return dt.db.Put(append([]byte(dt.prefix), key...), value) -} - -func (dt *table) Has(key []byte) (bool, error) { - return dt.db.Has(append([]byte(dt.prefix), key...)) -} - -func (dt *table) Get(key []byte) ([]byte, error) { - return dt.db.Get(append([]byte(dt.prefix), key...)) -} - -func (dt *table) Delete(key []byte) error { - return dt.db.Delete(append([]byte(dt.prefix), key...)) -} - -func (dt *table) Close() { - // Do nothing; don't close the underlying DB. -} diff --git a/ethdb/table_batch.go b/ethdb/table_batch.go deleted file mode 100644 index ae83e79ced..0000000000 --- a/ethdb/table_batch.go +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2014 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package ethdb - -type tableBatch struct { - batch Batch - prefix string -} - -// NewTableBatch returns a Batch object which prefixes all keys with a given string. -func NewTableBatch(db Database, prefix string) Batch { - return &tableBatch{db.NewBatch(), prefix} -} - -func (dt *table) NewBatch() Batch { - return &tableBatch{dt.db.NewBatch(), dt.prefix} -} - -func (tb *tableBatch) Put(key, value []byte) error { - return tb.batch.Put(append([]byte(tb.prefix), key...), value) -} - -func (tb *tableBatch) Delete(key []byte) error { - return tb.batch.Delete(append([]byte(tb.prefix), key...)) -} - -func (tb *tableBatch) Write() error { - return tb.batch.Write() -} - -func (tb *tableBatch) ValueSize() int { - return tb.batch.ValueSize() -} - -func (tb *tableBatch) Reset() { - tb.batch.Reset() -} diff --git a/freezer/freezer.go b/freezer/freezer.go deleted file mode 100644 index 0c6951c37a..0000000000 --- a/freezer/freezer.go +++ /dev/null @@ -1,213 +0,0 @@ -// Copyright 2018 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// Package freezer implements an append-only immutable mmap chain database. -package freezer - -import ( - "time" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/core/rawdb" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/ethdb" - "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/metrics" - "github.com/ethereum/go-ethereum/rlp" -) - -// Freezer is an memory mapped append-only database to store immutable chain data -// into flat files: -// -// - The append only nature ensures that disk writes are minimized. -// - The memory mapping ensures we can max out system memory for caching without -// reserving it for go-ethereum. This would also reduce the memory requirements -// of Geth, and thus also GC overhead. -type Freezer struct { - frozen uint64 // Number of blocks already frozen - - headers *table // Data table for storing the block headers - bodies *table // Data table for storing the block bodies - receipts *table // Data table for storing the block receipts - diffs *table // Data table for storing the block tds - - logger log.Logger // Contextual logger for the freezer database -} - -// New creates a chain freezer that moves ancient chain data into immutable flat -// file containers. -func New(datadir string) (*Freezer, error) { - // Create the initial freezer object - var ( - freezer = &Freezer{ - logger: log.New("path", datadir), - } - readMeter = metrics.NewRegisteredMeter("eth/db/freezer/read", nil) - writeMeter = metrics.NewRegisteredMeter("eth/db/freezer/write", nil) - err error - ) - // Open all the supported data tables - if freezer.headers, err = newTable(datadir, "headers", readMeter, writeMeter); err != nil { - return nil, err - } - if freezer.bodies, err = newTable(datadir, "bodies", readMeter, writeMeter); err != nil { - freezer.headers.Close() - return nil, err - } - if freezer.receipts, err = newTable(datadir, "receipts", readMeter, writeMeter); err != nil { - freezer.bodies.Close() - freezer.headers.Close() - return nil, err - } - if freezer.diffs, err = newTable(datadir, "diffs", readMeter, writeMeter); err != nil { - freezer.receipts.Close() - freezer.bodies.Close() - freezer.headers.Close() - return nil, err - } - return freezer, nil -} - -// Close terminates the chain freezer, unmapping all the data files. -func (f *Freezer) Close() error { - f.diffs.Close() - f.receipts.Close() - f.bodies.Close() - f.headers.Close() - - return nil -} - -// Freeze is a background thread that periodically checks the blockchain for any -// import progress and moves ancient data from the fast database into the freezer. -// -// This functionality is deliberately broken off from block importing to avoid -// incurring additional data shuffling delays on block propagation. -func (f *Freezer) Freeze(chain *core.BlockChain, db ethdb.Database, recheck time.Duration, delay uint64) { - for { - // Retrieve the freezing threshold. In theory we're interested only in full - // blocks post-sync, but that would keep the live database enormous during - // dast sync. By picking the fast block, we still get to deep freeze all the - // final immutable data without having to wait for sync to finish. - head := chain.CurrentFastBlock() - if head == nil { - log.Error("Current fast block is nil") - time.Sleep(recheck) - continue - } - if head.NumberU64() < delay { - log.Debug("Current block not old enough", "number", head.Number(), "hash", head.Hash(), "age", common.PrettyAge(time.Unix(head.Time().Int64(), 0)), "delay", delay) - time.Sleep(recheck) - continue - } - limit := head.NumberU64() - delay - if limit <= f.frozen { - log.Debug("Ancient blocks frozen already") - time.Sleep(recheck) - continue - } - // Seems we have data ready to be frozen, process in usable batches - if limit-f.frozen > 30000 { - limit = f.frozen + 30000 - } - var ( - start = time.Now() - first = f.frozen - last *types.Block - ) - for f.frozen < limit { - // Deep freeze the next canonical block if it's available - if block := chain.GetBlockByNumber(f.frozen); block != nil { - // Deep freeze the block header and body - blob, _ := rlp.EncodeToBytes(block.Header()) - if err := f.headers.Append(f.frozen, blob); err != nil { - log.Error("Failed to deep freeze header", "number", block.Number(), "hash", block.Hash(), "age", common.PrettyAge(time.Unix(block.Time().Int64(), 0)), "err", err) - break - } - blob, _ = rlp.EncodeToBytes(block.Body()) - if err := f.bodies.Append(f.frozen, blob); err != nil { - log.Error("Failed to deep freeze body", "number", block.Number(), "hash", block.Hash(), "age", common.PrettyAge(time.Unix(block.Time().Int64(), 0)), "err", err) - break - } - // Deep freeze the block receipts and total difficulty - if receipts := chain.GetReceiptsByHash(block.Hash()); receipts != nil { - blob, _ = rlp.EncodeToBytes(receipts) - if err := f.receipts.Append(f.frozen, blob); err != nil { - log.Error("Failed to deep freeze receipts", "number", block.Number(), "hash", block.Hash(), "age", common.PrettyAge(time.Unix(block.Time().Int64(), 0)), "err", err) - break - } - } - if td := chain.GetTd(block.Hash(), block.NumberU64()); td != nil { - blob, _ = rlp.EncodeToBytes(td) - if err := f.diffs.Append(f.frozen, blob); err != nil { - log.Error("Failed to deep freeze difficulty", "number", block.Number(), "hash", block.Hash(), "age", common.PrettyAge(time.Unix(block.Time().Int64(), 0)), "err", err) - break - } - } - log.Trace("Deep froze ancient block", "number", block.Number(), "hash", block.Hash(), "age", common.PrettyAge(time.Unix(block.Time().Int64(), 0))) - f.frozen++ - - // If it's the last block, save for reporting - if f.frozen == limit-1 { - last = block - } - } - } - // Batch of blocks have been frozen, flush them before wiping from leveldb - if err := f.headers.Flush(); err != nil { - f.logger.Error("Failed to flush frozen headers", "err", err) - time.Sleep(recheck) - continue - } - if err := f.bodies.Flush(); err != nil { - f.logger.Error("Failed to flush frozen bodies", "err", err) - time.Sleep(recheck) - continue - } - if err := f.receipts.Flush(); err != nil { - f.logger.Error("Failed to flush frozen receipts", "err", err) - time.Sleep(recheck) - continue - } - if err := f.diffs.Flush(); err != nil { - f.logger.Error("Failed to flush frozen diffs", "err", err) - time.Sleep(recheck) - continue - } - // Wipe out all data from the active database - for number := first; number < f.frozen; number++ { - if number == 0 { - // Skip deleting the genesis for the PoC - continue - } - rawdb.DeleteBlock(db, rawdb.ReadCanonicalHash(db, number), number) - } - // Log something friendly for the user - context := []interface{}{ - "count", f.frozen - first, "elapsed", common.PrettyDuration(time.Since(start)), "number", f.frozen - 1, - } - if last != nil { - context = append(context, []interface{}{"hash", last.Hash(), "age", common.PrettyAge(time.Unix(last.Time().Int64(), 0))}...) - } - log.Info("Deep froze chain segment", context...) - - // Avoid database thrashing with tiny writes - if f.frozen-first < 30000 { - time.Sleep(recheck) - } - } -} diff --git a/freezer/mmap.go b/freezer/mmap.go deleted file mode 100644 index 40d3836fe8..0000000000 --- a/freezer/mmap.go +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright 2018 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package freezer - -import ( - "os" - "reflect" - "unsafe" - - "github.com/edsrzf/mmap-go" -) - -// openWithSize opens a file and ensures it is at least the provided bytes in -// size, growing it if necessary. -func openWithSize(path string, size uint64) (*os.File, error) { - // Open the file for writing, potentially creating it - file, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0644) - if err != nil { - return nil, err - } - // Ensure the file's size is at least as much as requested - info, err := file.Stat() - if err != nil { - file.Close() - return nil, err - } - if info.Size() < int64(size) { - if err := file.Truncate(int64(size)); err != nil { - file.Close() - return nil, err - } - } - // File is correctly open and of the correct size - return file, err -} - -// mmapBytes tries to memory map a file, creating it if it's non existent. -func mmapBytes(path string, size uint64) (*os.File, mmap.MMap, []byte, error) { - // Open the file to memory map and ensure it's large enough - file, err := openWithSize(path, size) - if err != nil { - return nil, nil, nil, err - } - // Memory map the file, cast to a byte slice and return - mem, err := mmap.Map(file, mmap.RDWR, 0) - if err != nil { - file.Close() - return nil, nil, nil, err - } - header := *(*reflect.SliceHeader)(unsafe.Pointer(&mem)) - return file, mem, *(*[]byte)(unsafe.Pointer(&header)), nil -} - -// mmapUints tries to memory map a file, creating it if it's non existent. -func mmapUints(path string, size uint64) (*os.File, mmap.MMap, []uint64, error) { - // Open the file to memory map and ensure it's large enough - file, err := openWithSize(path, size) - if err != nil { - return nil, nil, nil, err - } - // Memory map the file, cast to an uint64 slice and return - mem, err := mmap.Map(file, mmap.RDWR, 0) - if err != nil { - file.Close() - return nil, nil, nil, err - } - header := *(*reflect.SliceHeader)(unsafe.Pointer(&mem)) - header.Len /= 8 - header.Cap /= 8 - - return file, mem, *(*[]uint64)(unsafe.Pointer(&header)), nil -} diff --git a/freezer/table.go b/freezer/table.go deleted file mode 100644 index 81aa673c94..0000000000 --- a/freezer/table.go +++ /dev/null @@ -1,290 +0,0 @@ -// Copyright 2018 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package freezer - -import ( - "errors" - "os" - "path/filepath" - "sync" - - "github.com/edsrzf/mmap-go" - "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/metrics" - "github.com/golang/snappy" -) - -const ( - growthIndex uint64 = 1024 * 1024 // Growth rate of the index file when remapping - growthData uint64 = 128 * 1024 * 1024 // Growth rate of the data file when remapping -) - -var ( - // errAlreadyExists is returned if the user attempts to append an item to the - // freezer table that already exists (i.e. it's offset is lower or equal to - // the head of the immutable file). - errAlreadyExists = errors.New("item already exists") - - // errGappedWrite is returned if the user attempts to append an item to the - // freezer table that would produce a data gap (i.e. it's offset is larger than - // the head of the immutable file). - errGappedWrite = errors.New("item produces data gap") - - // errTableInaccessible is returned if a previously well functioning freezer - // table becomes non-accessible after a memory remap (resize). - errTableInaccessible = errors.New("table not accessible") - - // errOutOfBounds is returned if the item requested is not contained within the - // freezer table. - errOutOfBounds = errors.New("out of bounds") -) - -// table represents a single chained data table within the freezer (e.g. blocks). -// It consists of a data file (snappy encoded arbitrary data blobs) and an index -// file (uncompressed 64 bit indices into the data file). In addition a counter -// (binary) file is also created which simply contains the number of items. -type table struct { - path string // Database folder to store the files into - name string // Table name to multiplex multiple tables into the same folder - - fileData *os.File // File descriptor for the data region - fileIndex *os.File // File descriptor for the offset region - fileCounter *os.File // File descriptor for the counter region - - mmapData mmap.MMap // Memory mapping for the data region - mmapIndex mmap.MMap // Memory mapping for the offset region - mmapCounter mmap.MMap // Memory mapping for the counter region - - rawData []byte // Direct memory region for the data file - rawIndex []uint64 // Direct memory region for the offset file - rawCounter []uint64 // Direct memory region for the counter (single item) - - readMeter metrics.Meter // Meter for measuring the effective amount of data read - writeMeter metrics.Meter // Meter for measuring the effective amount of data written - - logger log.Logger // Logger with database path and table name ambedded - lock sync.RWMutex // Lock protecting the reads from remaps -} - -// newTable attempts to new freezer table by memory mapping the composing files. -// If no file exists, it will create new ones. -func newTable(path string, name string, readMeter metrics.Meter, writeMeter metrics.Meter) (*table, error) { - if err := os.MkdirAll(path, 0755); err != nil { - return nil, err - } - tab := &table{ - path: path, - name: name, - readMeter: readMeter, - writeMeter: writeMeter, - logger: log.New("path", path, "table", name), - } - if err := tab.ensure(); err != nil { - return nil, err - } - return tab, nil -} - -// ensure checks all the memory region limits and ensures that they conform to -// the required data counts. If anything is off, this method will recreate and -// remap accordingly. -func (t *table) ensure() error { - // Figure out if we need to remap or not - // If the regions are already open and large enough, leave as is - if t.rawCounter != nil { - // Ensure the data file is large enough, unmap it otherwise - wantData := (t.rawIndex[t.rawCounter[0]]/growthData + 1) * growthData - if uint64(len(t.rawData)) < wantData { - if err := t.mmapData.Unmap(); err != nil { - t.logger.Error("Failed to unmap freezer datastore", "err", err) - } - if err := t.fileData.Close(); err != nil { - t.logger.Error("Failed to close freezer datastore", "err", err) - } - t.fileData, t.mmapData, t.rawData = nil, nil, nil - } - // Ensure the index file is large enough, unmap it otherwise - wantIndex := (((t.rawCounter[0]+1)*8)/growthIndex + 1) * growthIndex - if uint64(len(t.rawIndex)) < wantIndex { - if err := t.mmapIndex.Unmap(); err != nil { - t.logger.Error("Failed to unmap freezer index", "err", err) - } - if err := t.fileIndex.Close(); err != nil { - t.logger.Error("Failed to close freezer index", "err", err) - } - t.fileIndex, t.mmapIndex, t.rawIndex = nil, nil, nil - } - } - // Memory map the counter and retrieve the size of the offset file - var err error - - if t.rawCounter == nil { - if t.fileCounter, t.mmapCounter, t.rawCounter, err = mmapUints(filepath.Join(t.path, t.name+".len"), 8); err != nil { - return err - } - } - // Memory map the index file and retrieve the size of the data file - if t.rawIndex == nil { - size := (((t.rawCounter[0]+1)*8)/growthIndex + 1) * growthIndex - if t.fileIndex, t.mmapIndex, t.rawIndex, err = mmapUints(filepath.Join(t.path, t.name+".idx"), size); err != nil { - if err := t.mmapCounter.Unmap(); err != nil { - t.logger.Error("Failed to unmap freezer counter", "err", err) - } - if err := t.fileCounter.Close(); err != nil { - t.logger.Error("Failed to close freezer counter", "err", err) - } - t.fileCounter, t.mmapCounter, t.rawCounter = nil, nil, nil - return err - } - } - // Memory map the data file and return the final freezer table - if t.rawData == nil { - size := growthData - if t.rawCounter[0] > 0 { - size = (t.rawIndex[t.rawCounter[0]]/growthData + 1) * growthData - } - if t.fileData, t.mmapData, t.rawData, err = mmapBytes(filepath.Join(t.path, t.name+".dat"), size); err != nil { - if err := t.mmapIndex.Unmap(); err != nil { - t.logger.Error("Failed to unmap freezer index", "err", err) - } - if err := t.fileIndex.Close(); err != nil { - t.logger.Error("Failed to close freezer index", "err", err) - } - t.fileIndex, t.mmapIndex, t.rawIndex = nil, nil, nil - - if err := t.mmapCounter.Unmap(); err != nil { - t.logger.Error("Failed to unmap freezer counter", "err", err) - } - if err := t.fileCounter.Close(); err != nil { - t.logger.Error("Failed to close freezer counter", "err", err) - } - t.fileCounter, t.mmapCounter, t.rawCounter = nil, nil, nil - - return err - } - } - return nil -} - -// Close unmaps all active memory mapped regions. -func (t *table) Close() error { - t.lock.Lock() - defer t.lock.Unlock() - - if t.mmapData != nil { - if err := t.mmapData.Unmap(); err != nil { - t.logger.Error("Failed to unmap freezer datastore", "err", err) - } - if err := t.fileData.Close(); err != nil { - t.logger.Error("Failed to close freezer datastore", "err", err) - } - t.fileData, t.mmapData, t.rawData = nil, nil, nil - } - if t.mmapIndex != nil { - if err := t.mmapIndex.Unmap(); err != nil { - t.logger.Error("Failed to unmap freezer index", "err", err) - } - if err := t.fileIndex.Close(); err != nil { - t.logger.Error("Failed to close freezer index", "err", err) - } - t.fileIndex, t.mmapIndex, t.rawIndex = nil, nil, nil - } - if t.mmapCounter != nil { - if err := t.mmapCounter.Unmap(); err != nil { - t.logger.Error("Failed to unmap freezer counter", "err", err) - } - if err := t.fileCounter.Close(); err != nil { - t.logger.Error("Failed to close freezer counter", "err", err) - } - t.fileCounter, t.mmapCounter, t.rawCounter = nil, nil, nil - } - return nil -} - -// Append injects a binary blob at the end of the freezer table. The item index -// is a precautionary parameter to ensure data correctness, but the table will -// reject already existing data. -// -// Note, this method will *not* flush any data to disk (unless the files need to -// be resized). Be sure to explicitly flush before irreversibly deleting data -// from the fast chain database. -func (t *table) Append(item uint64, blob []byte) error { - t.lock.Lock() - defer t.lock.Unlock() - - // Ensure the table is still accessible - if t.rawCounter == nil { - if err := t.ensure(); err != nil { - t.logger.Error("Failed to re-access table", "err", err) - return errTableInaccessible - } - } - // Ensure only the next item can be written, nothing else - items := t.rawCounter[0] - if items > item { - return errAlreadyExists - } - if items < item { - return errGappedWrite - } - // Encode the blob and write it into the data file - blob = snappy.Encode(nil, blob) - - t.rawIndex[items+1] = t.rawIndex[items] + uint64(len(blob)) - copy(t.rawData[t.rawIndex[items]:], blob) - t.rawCounter[0]++ - - t.writeMeter.Mark(int64(len(blob))) - - // Ensure we have enough space for future appends - return t.ensure() -} - -// Retrieve looks up the data offset of an item with the given index and retrieves -// the raw binary blob from the data file. -func (t *table) Retrieve(item uint64) ([]byte, error) { - t.lock.RLock() - defer t.lock.RUnlock() - - // Ensure the table and the item is accessible - if t.rawCounter == nil { - return nil, errTableInaccessible - } - if t.rawCounter[0] <= item { - return nil, errOutOfBounds - } - // Item reachable, retrive and return to the user - blob := t.rawData[t.rawIndex[item]:t.rawIndex[item+1]] - t.writeMeter.Mark(int64(len(blob))) - - return snappy.Decode(nil, blob) -} - -// Flush pushes any pending data from memory out to disk. This is an expensive -// operation, so use it with care. -func (t *table) Flush() error { - if err := t.mmapData.Flush(); err != nil { - return err - } - if err := t.mmapIndex.Flush(); err != nil { - return err - } - if err := t.mmapCounter.Flush(); err != nil { - return err - } - return nil -} diff --git a/internal/ethapi/backend.go b/internal/ethapi/backend.go index e23ee03b15..78223f362a 100644 --- a/internal/ethapi/backend.go +++ b/internal/ethapi/backend.go @@ -27,8 +27,8 @@ import ( "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/database" "github.com/ethereum/go-ethereum/eth/downloader" - "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" @@ -41,7 +41,7 @@ type Backend interface { Downloader() *downloader.Downloader ProtocolVersion() int SuggestPrice(ctx context.Context) (*big.Int, error) - ChainDb() ethdb.Database + ChainDb() database.Database EventMux() *event.TypeMux AccountManager() *accounts.Manager diff --git a/ethdb/database.go b/internal/keyvalue/leveldb.go similarity index 65% rename from ethdb/database.go rename to internal/keyvalue/leveldb.go index 17f1478e5b..ac20582ce6 100644 --- a/ethdb/database.go +++ b/internal/keyvalue/leveldb.go @@ -16,7 +16,7 @@ // +build !js -package ethdb +package keyvalue import ( "fmt" @@ -26,23 +26,38 @@ import ( "time" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/database" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" "github.com/syndtr/goleveldb/leveldb" "github.com/syndtr/goleveldb/leveldb/errors" "github.com/syndtr/goleveldb/leveldb/filter" - "github.com/syndtr/goleveldb/leveldb/iterator" "github.com/syndtr/goleveldb/leveldb/opt" "github.com/syndtr/goleveldb/leveldb/util" ) const ( - writePauseWarningThrottler = 1 * time.Minute + // leveldbDegradationWarnInterval specifies how often warning should be printed + // if the leveldb database cannot keep up with requested writes. + leveldbDegradationWarnInterval = time.Minute + + // leveldbMinCache is the minimum amount of memory in megabytes to allocate to + // leveldb read and write caching, split half and half. + leveldbMinCache = 16 + + // leveldbMinHandles is the minimum number of files handles to allocate to the + // open database files. + leveldbMinHandles = 16 + + // metricsGatheringInterval specifies the interval to retrieve leveldb database + // compaction, io and pause stats to report to the user. + metricsGatheringInterval = 3 * time.Second ) -var OpenFileLimit = 64 - -type LDBDatabase struct { +// LeveldbDatabase is a persistent key-value store. Apart from basic data storage +// functionality it also supports batch writes and iterating over the keyspace in +// binary-alphabetical order. +type LeveldbDatabase struct { fn string // filename for reporting db *leveldb.DB // LevelDB instance @@ -60,17 +75,16 @@ type LDBDatabase struct { log log.Logger // Contextual logger tracking the database path } -// NewLDBDatabase returns a LevelDB wrapped object. -func NewLDBDatabase(file string, cache int, handles int) (*LDBDatabase, error) { - logger := log.New("database", file) - +// NewLeveldbDatabase returns a wrapped LevelDB object. +func NewLeveldbDatabase(file string, cache int, handles int, namespace string) (database.KeyValueStore, error) { // Ensure we have some minimal caching and file guarantees - if cache < 16 { - cache = 16 + if cache < leveldbMinCache { + cache = leveldbMinCache } - if handles < 16 { - handles = 16 + if handles < leveldbMinHandles { + handles = leveldbMinHandles } + logger := log.New("database", file) logger.Info("Allocated cache and file handles", "cache", common.StorageSize(cache*1024*1024), "handles", handles) // Open the db and recover any potential corruptions @@ -83,56 +97,32 @@ func NewLDBDatabase(file string, cache int, handles int) (*LDBDatabase, error) { if _, corrupted := err.(*errors.ErrCorrupted); corrupted { db, err = leveldb.RecoverFile(file, nil) } - // (Re)check for errors and abort if opening of the db failed if err != nil { return nil, err } - return &LDBDatabase{ - fn: file, - db: db, - log: logger, - }, nil -} - -// Path returns the path to the database directory. -func (db *LDBDatabase) Path() string { - return db.fn -} - -// Put puts the given key / value to the queue -func (db *LDBDatabase) Put(key []byte, value []byte) error { - return db.db.Put(key, value, nil) -} - -func (db *LDBDatabase) Has(key []byte) (bool, error) { - return db.db.Has(key, nil) -} - -// Get returns the given key if it's present. -func (db *LDBDatabase) Get(key []byte) ([]byte, error) { - dat, err := db.db.Get(key, nil) - if err != nil { - return nil, err + // Assemble the wrapper with all the registered metrics + ldb := &LeveldbDatabase{ + fn: file, + db: db, + log: logger, + quitChan: make(chan chan error), } - return dat, nil + ldb.compTimeMeter = metrics.NewRegisteredMeter(namespace+"compact/time", nil) + ldb.compReadMeter = metrics.NewRegisteredMeter(namespace+"compact/input", nil) + ldb.compWriteMeter = metrics.NewRegisteredMeter(namespace+"compact/output", nil) + ldb.diskReadMeter = metrics.NewRegisteredMeter(namespace+"disk/read", nil) + ldb.diskWriteMeter = metrics.NewRegisteredMeter(namespace+"disk/write", nil) + ldb.writeDelayMeter = metrics.NewRegisteredMeter(namespace+"compact/writedelay/duration", nil) + ldb.writeDelayNMeter = metrics.NewRegisteredMeter(namespace+"compact/writedelay/counter", nil) + + // Start up the metrics gathering and return + go ldb.meter(metricsGatheringInterval) + return ldb, nil } -// Delete deletes the key from the queue and database -func (db *LDBDatabase) Delete(key []byte) error { - return db.db.Delete(key, nil) -} - -func (db *LDBDatabase) NewIterator() iterator.Iterator { - return db.db.NewIterator(nil, nil) -} - -// NewIteratorWithPrefix returns a iterator to iterate over subset of database content with a particular prefix. -func (db *LDBDatabase) NewIteratorWithPrefix(prefix []byte) iterator.Iterator { - return db.db.NewIterator(util.BytesPrefix(prefix), nil) -} - -func (db *LDBDatabase) Close() { - // Stop the metrics collection to avoid internal database races +// Close stops th emetrics collection, flushes any pending data to disk and closes +// all io access to the underlying key-value store. +func (db *LeveldbDatabase) Close() error { db.quitLock.Lock() defer db.quitLock.Unlock() @@ -144,41 +134,68 @@ func (db *LDBDatabase) Close() { } db.quitChan = nil } - err := db.db.Close() - if err == nil { - db.log.Info("Database closed") - } else { - db.log.Error("Failed to close database", "err", err) + return db.db.Close() +} + +// Has retrieves if a key is present in the key-value store. +func (db *LeveldbDatabase) Has(key []byte) (bool, error) { + return db.db.Has(key, nil) +} + +// Get retrieves the given key if it's present in the key-value store. +func (db *LeveldbDatabase) Get(key []byte) ([]byte, error) { + dat, err := db.db.Get(key, nil) + if err != nil { + return nil, err + } + return dat, nil +} + +// Put inserts the given value into the key-value store. +func (db *LeveldbDatabase) Put(key []byte, value []byte) error { + return db.db.Put(key, value, nil) +} + +// Delete removes the key from the key-value store. +func (db *LeveldbDatabase) Delete(key []byte) error { + return db.db.Delete(key, nil) +} + +// NewBatch creates a write-only key-value store that buffers changes to its host +// database until a final write is called. +func (db *LeveldbDatabase) NewBatch() database.Batch { + return &leveldbBatch{ + db: db.db, + b: new(leveldb.Batch), } } -func (db *LDBDatabase) LDB() *leveldb.DB { - return db.db +// NewIterator creates a binary-alphabetical iterator over the entire keyspace +// contained within the leveldb database. +func (db *LeveldbDatabase) NewIterator() database.Iterator { + return db.NewIteratorWithPrefix(nil) } -// Meter configures the database metrics collectors and -func (db *LDBDatabase) Meter(prefix string) { - // Initialize all the metrics collector at the requested prefix - db.compTimeMeter = metrics.NewRegisteredMeter(prefix+"compact/time", nil) - db.compReadMeter = metrics.NewRegisteredMeter(prefix+"compact/input", nil) - db.compWriteMeter = metrics.NewRegisteredMeter(prefix+"compact/output", nil) - db.diskReadMeter = metrics.NewRegisteredMeter(prefix+"disk/read", nil) - db.diskWriteMeter = metrics.NewRegisteredMeter(prefix+"disk/write", nil) - db.writeDelayMeter = metrics.NewRegisteredMeter(prefix+"compact/writedelay/duration", nil) - db.writeDelayNMeter = metrics.NewRegisteredMeter(prefix+"compact/writedelay/counter", nil) +// NewIteratorWithPrefix creates a binary-alphabetical iterator over a subset +// of database content with a particular key prefix. +func (db *LeveldbDatabase) NewIteratorWithPrefix(prefix []byte) database.Iterator { + return db.db.NewIterator(util.BytesPrefix(prefix), nil) +} - // Create a quit channel for the periodic collector and run it - db.quitLock.Lock() - db.quitChan = make(chan chan error) - db.quitLock.Unlock() +// Stat returns a particular internal stat of the database. +func (db *LeveldbDatabase) Stat(property string) (string, error) { + return db.db.GetProperty(property) +} - go db.meter(3 * time.Second) +// Path returns the path to the database directory. +func (db *LeveldbDatabase) Path() string { + return db.fn } // meter periodically retrieves internal leveldb counters and reports them to // the metrics subsystem. // -// This is how a stats table look like (currently): +// This is how a stats leveldbTable look like (currently): // Compactions // Level | Tables | Size(MB) | Time(sec) | Read(MB) | Write(MB) // -------+------------+---------------+---------------+---------------+--------------- @@ -192,7 +209,7 @@ func (db *LDBDatabase) Meter(prefix string) { // // This is how the iostats look like (currently): // Read(MB):3895.04860 Write(MB):3654.64712 -func (db *LDBDatabase) meter(refresh time.Duration) { +func (db *LeveldbDatabase) meter(refresh time.Duration) { // Create the counters to store current and previous compaction values compactions := make([][]float64, 2) for i := 0; i < 2; i++ { @@ -221,19 +238,19 @@ func (db *LDBDatabase) meter(refresh time.Duration) { merr = err continue } - // Find the compaction table, skip the header + // Find the compaction leveldbTable, skip the header lines := strings.Split(stats, "\n") for len(lines) > 0 && strings.TrimSpace(lines[0]) != "Compactions" { lines = lines[1:] } if len(lines) <= 3 { - db.log.Error("Compaction table not found") - merr = errors.New("compaction table not found") + db.log.Error("Compaction leveldbTable not found") + merr = errors.New("compaction leveldbTable not found") continue } lines = lines[3:] - // Iterate over all the table rows, and accumulate the entries + // Iterate over all the leveldbTable rows, and accumulate the entries for j := 0; j < len(compactions[i%2]); j++ { compactions[i%2][j] = 0 } @@ -296,7 +313,7 @@ func (db *LDBDatabase) meter(refresh time.Duration) { // If a warning that db is performing compaction has been displayed, any subsequent // warnings will be withheld for one minute not to overwhelm the user. if paused && delayN-delaystats[0] == 0 && duration.Nanoseconds()-delaystats[1] == 0 && - time.Now().After(lastWritePaused.Add(writePauseWarningThrottler)) { + time.Now().After(lastWritePaused.Add(leveldbDegradationWarnInterval)) { db.log.Warn("Database compacting, degraded performance") lastWritePaused = time.Now() } @@ -349,37 +366,40 @@ func (db *LDBDatabase) meter(refresh time.Duration) { errc <- merr } -func (db *LDBDatabase) NewBatch() Batch { - return &ldbBatch{db: db.db, b: new(leveldb.Batch)} -} - -type ldbBatch struct { +// leveldbBatch is a write-only leveldb batch that commits changes to its host +// database when Write is called. A batch cannot be used concurrently. +type leveldbBatch struct { db *leveldb.DB b *leveldb.Batch size int } -func (b *ldbBatch) Put(key, value []byte) error { +// Put inserts the given value into the batch for later committing. +func (b *leveldbBatch) Put(key, value []byte) error { b.b.Put(key, value) b.size += len(value) return nil } -func (b *ldbBatch) Delete(key []byte) error { +// Delete inserts the a key removal into the batch for later committing. +func (b *leveldbBatch) Delete(key []byte) error { b.b.Delete(key) b.size += 1 return nil } -func (b *ldbBatch) Write() error { - return b.db.Write(b.b, nil) -} - -func (b *ldbBatch) ValueSize() int { +// ValueSize retrieves the amount of data queued up for writing. +func (b *leveldbBatch) ValueSize() int { return b.size } -func (b *ldbBatch) Reset() { +// Write flushes any accumulated data to disk. +func (b *leveldbBatch) Write() error { + return b.db.Write(b.b, nil) +} + +// Reset resets the batch for reuse. +func (b *leveldbBatch) Reset() { b.b.Reset() b.size = 0 } diff --git a/internal/keyvalue/memorydb.go b/internal/keyvalue/memorydb.go new file mode 100644 index 0000000000..eca99d017b --- /dev/null +++ b/internal/keyvalue/memorydb.go @@ -0,0 +1,295 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package keyvalue + +import ( + "errors" + "sort" + "strings" + "sync" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/database" +) + +var ( + // errMemorydbClosed is returned if a memory database was already closed at the + // invocation of a data access operation. + errMemorydbClosed = errors.New("database closed") + + // errMemorydbNotFound is returned if a key is requested that is not found in + // the provided memory database. + errMemorydbNotFound = errors.New("not found") +) + +// MemoryDatabase is an ephemeral key-value store. Apart from basic data storage +// functionality it also supports batch writes and iterating over the keyspace in +// binary-alphabetical order. +type MemoryDatabase struct { + db map[string][]byte + lock sync.RWMutex +} + +// NewMemoryDatabase returns a wrapped map with all the required database interface +// methods implemented. +func NewMemoryDatabase() *MemoryDatabase { + return &MemoryDatabase{ + db: make(map[string][]byte), + } +} + +// NewMemoryDatabaseWithCap returns a wrapped map pre-allocated to the provided +// capcity with all the required database interface methods implemented. +func NewMemoryDatabaseWithCap(size int) database.KeyValueStore { + return &MemoryDatabase{ + db: make(map[string][]byte, size), + } +} + +// Close deallocates the internal map and ensures any consecutive data access op +// failes with an error. +func (db *MemoryDatabase) Close() error { + db.lock.Lock() + defer db.lock.Unlock() + + db.db = nil + return nil +} + +// Has retrieves if a key is present in the key-value store. +func (db *MemoryDatabase) Has(key []byte) (bool, error) { + db.lock.RLock() + defer db.lock.RUnlock() + + if db.db == nil { + return false, errMemorydbClosed + } + _, ok := db.db[string(key)] + return ok, nil +} + +// Get retrieves the given key if it's present in the key-value store. +func (db *MemoryDatabase) Get(key []byte) ([]byte, error) { + db.lock.RLock() + defer db.lock.RUnlock() + + if db.db == nil { + return nil, errMemorydbClosed + } + if entry, ok := db.db[string(key)]; ok { + return common.CopyBytes(entry), nil + } + return nil, errMemorydbNotFound +} + +// Put inserts the given value into the key-value store. +func (db *MemoryDatabase) Put(key []byte, value []byte) error { + db.lock.Lock() + defer db.lock.Unlock() + + if db.db == nil { + return errMemorydbClosed + } + db.db[string(key)] = common.CopyBytes(value) + return nil +} + +// Delete removes the key from the key-value store. +func (db *MemoryDatabase) Delete(key []byte) error { + db.lock.Lock() + defer db.lock.Unlock() + + if db.db == nil { + return errMemorydbClosed + } + delete(db.db, string(key)) + return nil +} + +// NewBatch creates a write-only key-value store that buffers changes to its host +// database until a final write is called. +func (db *MemoryDatabase) NewBatch() database.Batch { + return &memoryBatch{ + db: db, + } +} + +// NewIterator creates a binary-alphabetical iterator over the entire keyspace +// contained within the memory database. +func (db *MemoryDatabase) NewIterator() database.Iterator { + return db.NewIteratorWithPrefix(nil) +} + +// NewIteratorWithPrefix creates a binary-alphabetical iterator over a subset +// of database content with a particular key prefix. +func (db *MemoryDatabase) NewIteratorWithPrefix(prefix []byte) database.Iterator { + db.lock.RLock() + defer db.lock.RUnlock() + + var ( + pr = string(prefix) + keys = []string{"FAKE KEY TO SIMPLIFY NEXT"} + values = [][]byte{[]byte("FAKE VAL TO SIMPLIFY NEXT")} + ) + // Collect the keys from the memory database corresponding to the given prefix + for key, _ := range db.db { + if strings.HasPrefix(key, pr) { + keys = append(keys, key) + } + } + // Sort the items and retrieve the associated values + sort.Strings(keys[1:]) + for _, key := range keys[1:] { + values = append(values, db.db[key]) + } + return &memoryIterator{ + keys: keys, + values: values, + } +} + +// Stat returns a particular internal stat of the database. +func (db *MemoryDatabase) Stat(property string) (string, error) { + return "", errors.New("unknown property") +} + +// Len returns the number of entries currently present in the memory database. +// +// Note, this method is only used for testing (i.e. not public in general) and +// does not have explicit checks for closed-ness to allow simpler testing code. +func (db *MemoryDatabase) Len() int { + db.lock.RLock() + defer db.lock.RUnlock() + + return len(db.db) +} + +/* +// Keys retrieves a copy of all the keys present in the database. +// +// Note, this method is only used for testing (i.e. not public in general) and +// does not have explicit checks for closed-ness to allow simpler testing code. +func (db *MemoryDatabase) Keys() [][]byte { + db.lock.RLock() + defer db.lock.RUnlock() + + keys := [][]byte{} + for key := range db.db { + keys = append(keys, []byte(key)) + } + return keys +}*/ + +// keyvalue is a key-value tuple tagged with a deletion field to allow creating +// memory-database write batches. +type keyvalue struct { + key []byte + value []byte + delete bool +} + +// memoryBatch is a write-only memory batch that commits changes to its host +// database when Write is called. A batch cannot be used concurrently. +type memoryBatch struct { + db *MemoryDatabase + writes []keyvalue + size int +} + +// Put inserts the given value into the batch for later committing. +func (b *memoryBatch) Put(key, value []byte) error { + b.writes = append(b.writes, keyvalue{common.CopyBytes(key), common.CopyBytes(value), false}) + b.size += len(value) + return nil +} + +// Delete inserts the a key removal into the batch for later committing. +func (b *memoryBatch) Delete(key []byte) error { + b.writes = append(b.writes, keyvalue{common.CopyBytes(key), nil, true}) + b.size += 1 + return nil +} + +// ValueSize retrieves the amount of data queued up for writing. +func (b *memoryBatch) ValueSize() int { + return b.size +} + +// Write flushes any accumulated data to the memory database. +func (b *memoryBatch) Write() error { + b.db.lock.Lock() + defer b.db.lock.Unlock() + + for _, keyvalue := range b.writes { + if keyvalue.delete { + delete(b.db.db, string(keyvalue.key)) + continue + } + b.db.db[string(keyvalue.key)] = keyvalue.value + } + return nil +} + +// Reset resets the batch for reuse. +func (b *memoryBatch) Reset() { + b.writes = b.writes[:0] + b.size = 0 +} + +// memoryIterator can walk over the (potentially partial) keyspace of a memory +// key value store. Internally it is a deep copy of the entire iterated state, +// sorted by keys. +type memoryIterator struct { + keys []string + values [][]byte +} + +// Next moves the iterator to the next key/value pair. It returns whether the +// iterator is exhausted. +func (it *memoryIterator) Next() bool { + if len(it.keys) > 0 { + it.keys = it.keys[1:] + it.values = it.values[1:] + } + return len(it.keys) > 0 +} + +// Key returns the key of the current key/value pair, or nil if done. The caller +// should not modify the contents of the returned slice, and its contents may +// change on the next call to Next. +func (it *memoryIterator) Key() []byte { + if len(it.keys) > 0 { + return []byte(it.keys[0]) + } + return nil +} + +// Value returns the value of the current key/value pair, or nil if done. The +// caller should not modify the contents of the returned slice, and its contents +// may change on the next call to Next. +func (it *memoryIterator) Value() []byte { + if len(it.values) > 0 { + return it.values[0] + } + return nil +} + +// Release releases associated resources. Release should always succeed and can +// be called multiple times without causing error. +func (it *memoryIterator) Release() { + it.keys, it.values = nil, nil +} diff --git a/les/api_backend.go b/les/api_backend.go index 7531396235..6f79487904 100644 --- a/les/api_backend.go +++ b/les/api_backend.go @@ -29,9 +29,9 @@ import ( "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/database" "github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/eth/gasprice" - "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/light" "github.com/ethereum/go-ethereum/params" @@ -175,7 +175,7 @@ func (b *LesApiBackend) SuggestPrice(ctx context.Context) (*big.Int, error) { return b.gpo.SuggestPrice(ctx) } -func (b *LesApiBackend) ChainDb() ethdb.Database { +func (b *LesApiBackend) ChainDb() database.Database { return b.eth.chainDb } diff --git a/les/backend.go b/les/backend.go index cd99f8f813..d4707821c1 100644 --- a/les/backend.go +++ b/les/backend.go @@ -78,7 +78,7 @@ type LightEthereum struct { } func New(ctx *node.ServiceContext, config *eth.Config) (*LightEthereum, error) { - chainDb, err := eth.CreateDB(ctx, config, "lightchaindata") + chainDb, err := ctx.OpenDatabase("lightchaindata", config.DatabaseCache, config.DatabaseHandles, "eth/db/chaindata/") if err != nil { return nil, err } diff --git a/les/commons.go b/les/commons.go index 21fb25714a..f857eb2a98 100644 --- a/les/commons.go +++ b/les/commons.go @@ -22,8 +22,8 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/database" "github.com/ethereum/go-ethereum/eth" - "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/light" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/enode" @@ -34,7 +34,7 @@ import ( type lesCommons struct { config *eth.Config iConfig *light.IndexerConfig - chainDb ethdb.Database + chainDb database.Database protocolManager *ProtocolManager chtIndexer, bloomTrieIndexer *core.ChainIndexer } diff --git a/les/freeclient.go b/les/freeclient.go index 5ee607be8f..9e93a25a85 100644 --- a/les/freeclient.go +++ b/les/freeclient.go @@ -25,7 +25,7 @@ import ( "github.com/ethereum/go-ethereum/common/mclock" "github.com/ethereum/go-ethereum/common/prque" - "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/database" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/rlp" ) @@ -44,7 +44,7 @@ import ( // value for the client. Currently the LES protocol manager uses IP addresses // (without port address) to identify clients. type freeClientPool struct { - db ethdb.Database + db database.Database lock sync.Mutex clock mclock.Clock closed bool @@ -64,7 +64,7 @@ const ( ) // newFreeClientPool creates a new free client pool -func newFreeClientPool(db ethdb.Database, connectedLimit, totalLimit int, clock mclock.Clock) *freeClientPool { +func newFreeClientPool(db database.Database, connectedLimit, totalLimit int, clock mclock.Clock) *freeClientPool { pool := &freeClientPool{ db: db, clock: clock, diff --git a/les/freeclient_test.go b/les/freeclient_test.go index e95abc7aad..51c4de39e9 100644 --- a/les/freeclient_test.go +++ b/les/freeclient_test.go @@ -25,7 +25,7 @@ import ( "time" "github.com/ethereum/go-ethereum/common/mclock" - "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/core/rawdb" ) func TestFreeClientPoolL10C100(t *testing.T) { @@ -45,7 +45,7 @@ const testFreeClientPoolTicks = 500000 func testFreeClientPool(t *testing.T, connLimit, clientCount int) { var ( clock mclock.Simulated - db = ethdb.NewMemDatabase() + db = rawdb.NewMemoryDatabase() pool = newFreeClientPool(db, connLimit, 10000, &clock) connected = make([]bool, clientCount) connTicks = make([]int, clientCount) diff --git a/les/handler.go b/les/handler.go index 46a1ed2d7b..d12066693c 100644 --- a/les/handler.go +++ b/les/handler.go @@ -33,9 +33,9 @@ import ( "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/database" "github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/eth/downloader" - "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/light" "github.com/ethereum/go-ethereum/log" @@ -97,7 +97,7 @@ type ProtocolManager struct { chainConfig *params.ChainConfig iConfig *light.IndexerConfig blockchain BlockChain - chainDb ethdb.Database + chainDb database.Database odr *LesOdr server *LesServer serverPool *serverPool @@ -136,7 +136,7 @@ func NewProtocolManager( peers *peerSet, blockchain BlockChain, txpool txPool, - chainDb ethdb.Database, + chainDb database.Database, odr *LesOdr, txrelay *LesTxRelay, serverPool *serverPool, @@ -907,7 +907,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { if reject(uint64(reqCnt), MaxHelperTrieProofsFetch) { return errResp(ErrRequestRejected, "") } - trieDb := trie.NewDatabase(ethdb.NewTable(pm.chainDb, light.ChtTablePrefix)) + trieDb := trie.NewDatabase(rawdb.NewTable(pm.chainDb, light.ChtTablePrefix)) for _, req := range req.Reqs { if header := pm.blockchain.GetHeaderByNumber(req.BlockNum); header != nil { sectionHead := rawdb.ReadCanonicalHash(pm.chainDb, req.ChtNum*pm.iConfig.ChtSize-1) @@ -966,7 +966,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { var prefix string if root, prefix = pm.getHelperTrie(req.Type, req.TrieIdx); root != (common.Hash{}) { - auxTrie, _ = trie.New(root, trie.NewDatabase(ethdb.NewTable(pm.chainDb, prefix))) + auxTrie, _ = trie.New(root, trie.NewDatabase(rawdb.NewTable(pm.chainDb, prefix))) } } if req.AuxReq == auxRoot { diff --git a/les/handler_test.go b/les/handler_test.go index 72ba266b3a..91f55e3b67 100644 --- a/les/handler_test.go +++ b/les/handler_test.go @@ -30,7 +30,6 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/eth/downloader" - "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/light" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/params" @@ -405,7 +404,7 @@ func testGetCHTProofs(t *testing.T, protocol int) { switch protocol { case 1: root := light.GetChtRoot(server.db, 0, bc.GetHeaderByNumber(frequency-1).Hash()) - trie, _ := trie.New(root, trie.NewDatabase(ethdb.NewTable(server.db, light.ChtTablePrefix))) + trie, _ := trie.New(root, trie.NewDatabase(rawdb.NewTable(server.db, light.ChtTablePrefix))) var proof light.NodeList trie.Prove(key, 0, &proof) @@ -413,7 +412,7 @@ func testGetCHTProofs(t *testing.T, protocol int) { case 2: root := light.GetChtRoot(server.db, (frequency/config.ChtSize)-1, bc.GetHeaderByNumber(frequency-1).Hash()) - trie, _ := trie.New(root, trie.NewDatabase(ethdb.NewTable(server.db, light.ChtTablePrefix))) + trie, _ := trie.New(root, trie.NewDatabase(rawdb.NewTable(server.db, light.ChtTablePrefix))) trie.Prove(key, 0, &proofsV2.Proofs) } // Assemble the requests for the different protocols @@ -480,7 +479,7 @@ func TestGetBloombitsProofs(t *testing.T) { var proofs HelperTrieResps root := light.GetBloomTrieRoot(server.db, 0, bc.GetHeaderByNumber(config.BloomTrieSize-1).Hash()) - trie, _ := trie.New(root, trie.NewDatabase(ethdb.NewTable(server.db, light.BloomTrieTablePrefix))) + trie, _ := trie.New(root, trie.NewDatabase(rawdb.NewTable(server.db, light.BloomTrieTablePrefix))) trie.Prove(key, 0, &proofs.Proofs) // Send the proof request and verify the response @@ -493,7 +492,7 @@ func TestGetBloombitsProofs(t *testing.T) { } func TestTransactionStatusLes2(t *testing.T) { - db := ethdb.NewMemDatabase() + db := rawdb.NewMemoryDatabase() pm := newTestProtocolManagerMust(t, false, 0, nil, nil, nil, db, nil) chain := pm.blockchain.(*core.BlockChain) config := core.DefaultTxPoolConfig diff --git a/les/helper_test.go b/les/helper_test.go index 02b1668c84..73ca283ded 100644 --- a/les/helper_test.go +++ b/les/helper_test.go @@ -29,11 +29,12 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/rawdb" "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/database" "github.com/ethereum/go-ethereum/eth" - "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/les/flowcontrol" "github.com/ethereum/go-ethereum/light" @@ -125,7 +126,7 @@ func testChainGen(i int, block *core.BlockGen) { } // testIndexers creates a set of indexers with specified params for testing purpose. -func testIndexers(db ethdb.Database, odr light.OdrBackend, iConfig *light.IndexerConfig) (*core.ChainIndexer, *core.ChainIndexer, *core.ChainIndexer) { +func testIndexers(db database.Database, odr light.OdrBackend, iConfig *light.IndexerConfig) (*core.ChainIndexer, *core.ChainIndexer, *core.ChainIndexer) { chtIndexer := light.NewChtIndexer(db, odr, iConfig.ChtSize, iConfig.ChtConfirms) bloomIndexer := eth.NewBloomIndexer(db, iConfig.BloomSize, iConfig.BloomConfirms) bloomTrieIndexer := light.NewBloomTrieIndexer(db, odr, iConfig.BloomSize, iConfig.BloomTrieSize) @@ -146,7 +147,7 @@ func testRCL() RequestCostList { // newTestProtocolManager creates a new protocol manager for testing purposes, // with the given number of blocks already known, potential notification // channels for different events and relative chain indexers array. -func newTestProtocolManager(lightSync bool, blocks int, generator func(int, *core.BlockGen), odr *LesOdr, peers *peerSet, db ethdb.Database, ulcConfig *eth.ULCConfig) (*ProtocolManager, error) { +func newTestProtocolManager(lightSync bool, blocks int, generator func(int, *core.BlockGen), odr *LesOdr, peers *peerSet, db database.Database, ulcConfig *eth.ULCConfig) (*ProtocolManager, error) { var ( evmux = new(event.TypeMux) engine = ethash.NewFaker() @@ -200,7 +201,7 @@ func newTestProtocolManager(lightSync bool, blocks int, generator func(int, *cor // with the given number of blocks already known, potential notification // channels for different events and relative chain indexers array. In case of an error, the constructor force- // fails the test. -func newTestProtocolManagerMust(t *testing.T, lightSync bool, blocks int, generator func(int, *core.BlockGen), odr *LesOdr, peers *peerSet, db ethdb.Database, ulcConfig *eth.ULCConfig) *ProtocolManager { +func newTestProtocolManagerMust(t *testing.T, lightSync bool, blocks int, generator func(int, *core.BlockGen), odr *LesOdr, peers *peerSet, db database.Database, ulcConfig *eth.ULCConfig) *ProtocolManager { pm, err := newTestProtocolManager(lightSync, blocks, generator, odr, peers, db, ulcConfig) if err != nil { t.Fatalf("Failed to create protocol manager: %v", err) @@ -327,7 +328,7 @@ func (p *testPeer) close() { // TestEntity represents a network entity for testing with necessary auxiliary fields. type TestEntity struct { - db ethdb.Database + db database.Database rPeer *peer tPeer *testPeer peers *peerSet @@ -340,7 +341,7 @@ type TestEntity struct { // newServerEnv creates a server testing environment with a connected test peer for testing purpose. func newServerEnv(t *testing.T, blocks int, protocol int, waitIndexers func(*core.ChainIndexer, *core.ChainIndexer, *core.ChainIndexer)) (*TestEntity, func()) { - db := ethdb.NewMemDatabase() + db := rawdb.NewMemoryDatabase() cIndexer, bIndexer, btIndexer := testIndexers(db, nil, light.TestServerIndexerConfig) pm := newTestProtocolManagerMust(t, false, blocks, testChainGen, nil, nil, db, nil) @@ -372,7 +373,7 @@ func newServerEnv(t *testing.T, blocks int, protocol int, waitIndexers func(*cor // newClientServerEnv creates a client/server arch environment with a connected les server and light client pair // for testing purpose. func newClientServerEnv(t *testing.T, blocks int, protocol int, waitIndexers func(*core.ChainIndexer, *core.ChainIndexer, *core.ChainIndexer), newPeer bool) (*TestEntity, *TestEntity, func()) { - db, ldb := ethdb.NewMemDatabase(), ethdb.NewMemDatabase() + db, ldb := rawdb.NewMemoryDatabase(), rawdb.NewMemoryDatabase() peers, lPeers := newPeerSet(), newPeerSet() dist := newRequestDistributor(lPeers, make(chan struct{})) diff --git a/les/odr.go b/les/odr.go index f7592354de..5ee4fdfd9a 100644 --- a/les/odr.go +++ b/les/odr.go @@ -20,21 +20,21 @@ import ( "context" "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/database" "github.com/ethereum/go-ethereum/light" "github.com/ethereum/go-ethereum/log" ) // LesOdr implements light.OdrBackend type LesOdr struct { - db ethdb.Database + db database.Database indexerConfig *light.IndexerConfig chtIndexer, bloomTrieIndexer, bloomIndexer *core.ChainIndexer retriever *retrieveManager stop chan struct{} } -func NewLesOdr(db ethdb.Database, config *light.IndexerConfig, retriever *retrieveManager) *LesOdr { +func NewLesOdr(db database.Database, config *light.IndexerConfig, retriever *retrieveManager) *LesOdr { return &LesOdr{ db: db, indexerConfig: config, @@ -49,7 +49,7 @@ func (odr *LesOdr) Stop() { } // Database returns the backing database -func (odr *LesOdr) Database() ethdb.Database { +func (odr *LesOdr) Database() database.Database { return odr.db } diff --git a/les/odr_requests.go b/les/odr_requests.go index 0f2e5dd9ec..45060ee97a 100644 --- a/les/odr_requests.go +++ b/les/odr_requests.go @@ -27,7 +27,7 @@ import ( "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/database" "github.com/ethereum/go-ethereum/light" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/rlp" @@ -51,7 +51,7 @@ type LesOdrRequest interface { GetCost(*peer) uint64 CanSend(*peer) bool Request(uint64, *peer) error - Validate(ethdb.Database, *Msg) error + Validate(database.Database, *Msg) error } func LesRequest(req light.OdrRequest) LesOdrRequest { @@ -96,7 +96,7 @@ func (r *BlockRequest) Request(reqID uint64, peer *peer) error { // Valid processes an ODR request reply message from the LES network // returns true and stores results in memory if the message was a valid reply // to the request (implementation of LesOdrRequest) -func (r *BlockRequest) Validate(db ethdb.Database, msg *Msg) error { +func (r *BlockRequest) Validate(db database.Database, msg *Msg) error { log.Debug("Validating block body", "hash", r.Hash) // Ensure we have a correct message with a single block body @@ -152,7 +152,7 @@ func (r *ReceiptsRequest) Request(reqID uint64, peer *peer) error { // Valid processes an ODR request reply message from the LES network // returns true and stores results in memory if the message was a valid reply // to the request (implementation of LesOdrRequest) -func (r *ReceiptsRequest) Validate(db ethdb.Database, msg *Msg) error { +func (r *ReceiptsRequest) Validate(db database.Database, msg *Msg) error { log.Debug("Validating block receipts", "hash", r.Hash) // Ensure we have a correct message with a single block receipt @@ -219,7 +219,7 @@ func (r *TrieRequest) Request(reqID uint64, peer *peer) error { // Valid processes an ODR request reply message from the LES network // returns true and stores results in memory if the message was a valid reply // to the request (implementation of LesOdrRequest) -func (r *TrieRequest) Validate(db ethdb.Database, msg *Msg) error { +func (r *TrieRequest) Validate(db database.Database, msg *Msg) error { log.Debug("Validating trie proof", "root", r.Id.Root, "key", r.Key) switch msg.MsgType { @@ -288,7 +288,7 @@ func (r *CodeRequest) Request(reqID uint64, peer *peer) error { // Valid processes an ODR request reply message from the LES network // returns true and stores results in memory if the message was a valid reply // to the request (implementation of LesOdrRequest) -func (r *CodeRequest) Validate(db ethdb.Database, msg *Msg) error { +func (r *CodeRequest) Validate(db database.Database, msg *Msg) error { log.Debug("Validating code data", "hash", r.Hash) // Ensure we have a correct message with a single code element @@ -399,7 +399,7 @@ func (r *ChtRequest) Request(reqID uint64, peer *peer) error { // Valid processes an ODR request reply message from the LES network // returns true and stores results in memory if the message was a valid reply // to the request (implementation of LesOdrRequest) -func (r *ChtRequest) Validate(db ethdb.Database, msg *Msg) error { +func (r *ChtRequest) Validate(db database.Database, msg *Msg) error { log.Debug("Validating CHT", "cht", r.ChtNum, "block", r.BlockNum) switch msg.MsgType { @@ -523,7 +523,7 @@ func (r *BloomRequest) Request(reqID uint64, peer *peer) error { // Valid processes an ODR request reply message from the LES network // returns true and stores results in memory if the message was a valid reply // to the request (implementation of LesOdrRequest) -func (r *BloomRequest) Validate(db ethdb.Database, msg *Msg) error { +func (r *BloomRequest) Validate(db database.Database, msg *Msg) error { log.Debug("Validating BloomBits", "bloomTrie", r.BloomTrieNum, "bitIdx", r.BitIdx, "sections", r.SectionIndexList) // Ensure we have a correct message with a single proof element @@ -560,7 +560,7 @@ func (r *BloomRequest) Validate(db ethdb.Database, msg *Msg) error { // readTraceDB stores the keys of database reads. We use this to check that received node // sets contain only the trie nodes necessary to make proofs pass. type readTraceDB struct { - db trie.DatabaseReader + db database.Reader reads map[string]struct{} } diff --git a/les/odr_test.go b/les/odr_test.go index ac81fbcf02..861cda0c7b 100644 --- a/les/odr_test.go +++ b/les/odr_test.go @@ -30,19 +30,19 @@ import ( "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/database" "github.com/ethereum/go-ethereum/light" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" ) -type odrTestFn func(ctx context.Context, db ethdb.Database, config *params.ChainConfig, bc *core.BlockChain, lc *light.LightChain, bhash common.Hash) []byte +type odrTestFn func(ctx context.Context, db database.Database, config *params.ChainConfig, bc *core.BlockChain, lc *light.LightChain, bhash common.Hash) []byte func TestOdrGetBlockLes1(t *testing.T) { testOdr(t, 1, 1, odrGetBlock) } func TestOdrGetBlockLes2(t *testing.T) { testOdr(t, 2, 1, odrGetBlock) } -func odrGetBlock(ctx context.Context, db ethdb.Database, config *params.ChainConfig, bc *core.BlockChain, lc *light.LightChain, bhash common.Hash) []byte { +func odrGetBlock(ctx context.Context, db database.Database, config *params.ChainConfig, bc *core.BlockChain, lc *light.LightChain, bhash common.Hash) []byte { var block *types.Block if bc != nil { block = bc.GetBlockByHash(bhash) @@ -60,7 +60,7 @@ func TestOdrGetReceiptsLes1(t *testing.T) { testOdr(t, 1, 1, odrGetReceipts) } func TestOdrGetReceiptsLes2(t *testing.T) { testOdr(t, 2, 1, odrGetReceipts) } -func odrGetReceipts(ctx context.Context, db ethdb.Database, config *params.ChainConfig, bc *core.BlockChain, lc *light.LightChain, bhash common.Hash) []byte { +func odrGetReceipts(ctx context.Context, db database.Database, config *params.ChainConfig, bc *core.BlockChain, lc *light.LightChain, bhash common.Hash) []byte { var receipts types.Receipts if bc != nil { if number := rawdb.ReadHeaderNumber(db, bhash); number != nil { @@ -82,7 +82,7 @@ func TestOdrAccountsLes1(t *testing.T) { testOdr(t, 1, 1, odrAccounts) } func TestOdrAccountsLes2(t *testing.T) { testOdr(t, 2, 1, odrAccounts) } -func odrAccounts(ctx context.Context, db ethdb.Database, config *params.ChainConfig, bc *core.BlockChain, lc *light.LightChain, bhash common.Hash) []byte { +func odrAccounts(ctx context.Context, db database.Database, config *params.ChainConfig, bc *core.BlockChain, lc *light.LightChain, bhash common.Hash) []byte { dummyAddr := common.HexToAddress("1234567812345678123456781234567812345678") acc := []common.Address{testBankAddress, acc1Addr, acc2Addr, dummyAddr} @@ -118,7 +118,7 @@ type callmsg struct { func (callmsg) CheckNonce() bool { return false } -func odrContractCall(ctx context.Context, db ethdb.Database, config *params.ChainConfig, bc *core.BlockChain, lc *light.LightChain, bhash common.Hash) []byte { +func odrContractCall(ctx context.Context, db database.Database, config *params.ChainConfig, bc *core.BlockChain, lc *light.LightChain, bhash common.Hash) []byte { data := common.Hex2Bytes("60CD26850000000000000000000000000000000000000000000000000000000000000000") var res []byte diff --git a/les/request_test.go b/les/request_test.go index c9c1851982..594511cbad 100644 --- a/les/request_test.go +++ b/les/request_test.go @@ -24,7 +24,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/database" "github.com/ethereum/go-ethereum/light" ) @@ -34,13 +34,13 @@ func secAddr(addr common.Address) []byte { return crypto.Keccak256(addr[:]) } -type accessTestFn func(db ethdb.Database, bhash common.Hash, number uint64) light.OdrRequest +type accessTestFn func(db database.Database, bhash common.Hash, number uint64) light.OdrRequest func TestBlockAccessLes1(t *testing.T) { testAccess(t, 1, tfBlockAccess) } func TestBlockAccessLes2(t *testing.T) { testAccess(t, 2, tfBlockAccess) } -func tfBlockAccess(db ethdb.Database, bhash common.Hash, number uint64) light.OdrRequest { +func tfBlockAccess(db database.Database, bhash common.Hash, number uint64) light.OdrRequest { return &light.BlockRequest{Hash: bhash, Number: number} } @@ -48,7 +48,7 @@ func TestReceiptsAccessLes1(t *testing.T) { testAccess(t, 1, tfReceiptsAccess) } func TestReceiptsAccessLes2(t *testing.T) { testAccess(t, 2, tfReceiptsAccess) } -func tfReceiptsAccess(db ethdb.Database, bhash common.Hash, number uint64) light.OdrRequest { +func tfReceiptsAccess(db database.Database, bhash common.Hash, number uint64) light.OdrRequest { return &light.ReceiptsRequest{Hash: bhash, Number: number} } @@ -56,7 +56,7 @@ func TestTrieEntryAccessLes1(t *testing.T) { testAccess(t, 1, tfTrieEntryAccess) func TestTrieEntryAccessLes2(t *testing.T) { testAccess(t, 2, tfTrieEntryAccess) } -func tfTrieEntryAccess(db ethdb.Database, bhash common.Hash, number uint64) light.OdrRequest { +func tfTrieEntryAccess(db database.Database, bhash common.Hash, number uint64) light.OdrRequest { if number := rawdb.ReadHeaderNumber(db, bhash); number != nil { return &light.TrieRequest{Id: light.StateTrieID(rawdb.ReadHeader(db, bhash, *number)), Key: testBankSecureTrieKey} } @@ -67,7 +67,7 @@ func TestCodeAccessLes1(t *testing.T) { testAccess(t, 1, tfCodeAccess) } func TestCodeAccessLes2(t *testing.T) { testAccess(t, 2, tfCodeAccess) } -func tfCodeAccess(db ethdb.Database, bhash common.Hash, num uint64) light.OdrRequest { +func tfCodeAccess(db database.Database, bhash common.Hash, num uint64) light.OdrRequest { number := rawdb.ReadHeaderNumber(db, bhash) if number != nil { return nil diff --git a/les/server.go b/les/server.go index 2ded3c184b..8d0c536a24 100644 --- a/les/server.go +++ b/les/server.go @@ -27,8 +27,8 @@ import ( "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/database" "github.com/ethereum/go-ethereum/eth" - "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/les/flowcontrol" "github.com/ethereum/go-ethereum/light" "github.com/ethereum/go-ethereum/log" @@ -247,7 +247,7 @@ func linRegFromBytes(data []byte) *linReg { type requestCostStats struct { lock sync.RWMutex - db ethdb.Database + db database.Database stats map[uint64]*linReg } @@ -258,7 +258,7 @@ type requestCostStatsRlp []struct { var rcStatsKey = []byte("_requestCostStats") -func newCostStats(db ethdb.Database) *requestCostStats { +func newCostStats(db database.Database) *requestCostStats { stats := make(map[uint64]*linReg) for _, code := range reqList { stats[code] = &linReg{cnt: 100} diff --git a/les/serverpool.go b/les/serverpool.go index 3f4d0a1d9b..0406db18a6 100644 --- a/les/serverpool.go +++ b/les/serverpool.go @@ -30,7 +30,7 @@ import ( "github.com/ethereum/go-ethereum/common/mclock" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/database" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/discv5" @@ -113,7 +113,7 @@ type registerReq struct { // known light server nodes. It received discovered nodes, stores statistics about // known nodes and takes care of always having enough good quality servers connected. type serverPool struct { - db ethdb.Database + db database.Database dbKey []byte server *p2p.Server quit chan struct{} @@ -141,7 +141,7 @@ type serverPool struct { } // newServerPool creates a new serverPool instance -func newServerPool(db ethdb.Database, quit chan struct{}, wg *sync.WaitGroup, trustedNodes []string) *serverPool { +func newServerPool(db database.Database, quit chan struct{}, wg *sync.WaitGroup, trustedNodes []string) *serverPool { pool := &serverPool{ db: db, quit: quit, diff --git a/les/ulc_test.go b/les/ulc_test.go index 3b95e6368e..d82de29234 100644 --- a/les/ulc_test.go +++ b/les/ulc_test.go @@ -1,20 +1,18 @@ package les import ( + "crypto/ecdsa" "fmt" + "math/big" + "net" "reflect" "testing" "time" - "net" - - "crypto/ecdsa" - "math/big" - "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/eth" - "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/light" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/enode" @@ -197,7 +195,7 @@ func connectPeers(full, light pairPeer, version int) (*peer, *peer, error) { // newFullPeerPair creates node with full sync mode func newFullPeerPair(t *testing.T, index int, numberOfblocks int, chainGen func(int, *core.BlockGen)) pairPeer { - db := ethdb.NewMemDatabase() + db := rawdb.NewMemoryDatabase() pmFull := newTestProtocolManagerMust(t, false, numberOfblocks, chainGen, nil, nil, db, nil) @@ -219,7 +217,7 @@ func newLightPeer(t *testing.T, ulcConfig *eth.ULCConfig) pairPeer { peers := newPeerSet() dist := newRequestDistributor(peers, make(chan struct{})) rm := newRetrieveManager(peers, dist, nil) - ldb := ethdb.NewMemDatabase() + ldb := rawdb.NewMemoryDatabase() odr := NewLesOdr(ldb, light.DefaultClientIndexerConfig, rm) diff --git a/light/lightchain.go b/light/lightchain.go index 5019622c79..9caf00ad5c 100644 --- a/light/lightchain.go +++ b/light/lightchain.go @@ -30,7 +30,7 @@ import ( "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/database" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" @@ -49,7 +49,7 @@ var ( type LightChain struct { hc *core.HeaderChain indexerConfig *IndexerConfig - chainDb ethdb.Database + chainDb database.Database engine consensus.Engine odr OdrBackend chainFeed event.Feed diff --git a/light/lightchain_test.go b/light/lightchain_test.go index 374cb53198..b3f1de45b4 100644 --- a/light/lightchain_test.go +++ b/light/lightchain_test.go @@ -26,7 +26,7 @@ import ( "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/database" "github.com/ethereum/go-ethereum/params" ) @@ -37,7 +37,7 @@ var ( ) // makeHeaderChain creates a deterministic chain of headers rooted at parent. -func makeHeaderChain(parent *types.Header, n int, db ethdb.Database, seed int) []*types.Header { +func makeHeaderChain(parent *types.Header, n int, db database.Database, seed int) []*types.Header { blocks, _ := core.GenerateChain(params.TestChainConfig, types.NewBlockWithHeader(parent), ethash.NewFaker(), db, n, func(i int, b *core.BlockGen) { b.SetCoinbase(common.Address{0: byte(seed), 19: byte(i)}) }) @@ -51,8 +51,8 @@ func makeHeaderChain(parent *types.Header, n int, db ethdb.Database, seed int) [ // newCanonical creates a chain database, and injects a deterministic canonical // chain. Depending on the full flag, if creates either a full block chain or a // header only chain. -func newCanonical(n int) (ethdb.Database, *LightChain, error) { - db := ethdb.NewMemDatabase() +func newCanonical(n int) (database.Database, *LightChain, error) { + db := rawdb.NewMemoryDatabase() gspec := core.Genesis{Config: params.TestChainConfig} genesis := gspec.MustCommit(db) blockchain, _ := NewLightChain(&dummyOdr{db: db, indexerConfig: TestClientIndexerConfig}, gspec.Config, ethash.NewFaker()) @@ -69,7 +69,7 @@ func newCanonical(n int) (ethdb.Database, *LightChain, error) { // newTestLightChain creates a LightChain that doesn't validate anything. func newTestLightChain() *LightChain { - db := ethdb.NewMemDatabase() + db := rawdb.NewMemoryDatabase() gspec := &core.Genesis{ Difficulty: big.NewInt(1), Config: params.TestChainConfig, @@ -265,11 +265,11 @@ func makeHeaderChainWithDiff(genesis *types.Block, d []int, seed byte) []*types. type dummyOdr struct { OdrBackend - db ethdb.Database + db database.Database indexerConfig *IndexerConfig } -func (odr *dummyOdr) Database() ethdb.Database { +func (odr *dummyOdr) Database() database.Database { return odr.db } diff --git a/light/nodeset.go b/light/nodeset.go index 6f25219c13..301b289661 100644 --- a/light/nodeset.go +++ b/light/nodeset.go @@ -22,7 +22,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/database" "github.com/ethereum/go-ethereum/rlp" ) @@ -106,7 +106,7 @@ func (db *NodeSet) NodeList() NodeList { } // Store writes the contents of the set to the given database -func (db *NodeSet) Store(target ethdb.Putter) { +func (db *NodeSet) Store(target database.Writer) { db.lock.RLock() defer db.lock.RUnlock() @@ -115,11 +115,11 @@ func (db *NodeSet) Store(target ethdb.Putter) { } } -// NodeList stores an ordered list of trie nodes. It implements ethdb.Putter. +// NodeList stores an ordered list of trie nodes. It implements database.Writer. type NodeList []rlp.RawValue // Store writes the contents of the list to the given database -func (n NodeList) Store(db ethdb.Putter) { +func (n NodeList) Store(db database.Writer) { for _, node := range n { db.Put(crypto.Keccak256(node), node) } diff --git a/light/odr.go b/light/odr.go index 900be05440..3cf922aa84 100644 --- a/light/odr.go +++ b/light/odr.go @@ -27,7 +27,7 @@ import ( "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/database" ) // NoOdr is the default context passed to an ODR capable function when the ODR @@ -39,7 +39,7 @@ var ErrNoPeers = errors.New("no suitable peers available") // OdrBackend is an interface to a backend service that handles ODR retrievals type type OdrBackend interface { - Database() ethdb.Database + Database() database.Database ChtIndexer() *core.ChainIndexer BloomTrieIndexer() *core.ChainIndexer BloomIndexer() *core.ChainIndexer @@ -49,7 +49,7 @@ type OdrBackend interface { // OdrRequest is an interface for retrieval requests type OdrRequest interface { - StoreResult(db ethdb.Database) + StoreResult(db database.Database) } // TrieID identifies a state or account storage trie @@ -91,7 +91,7 @@ type TrieRequest struct { } // StoreResult stores the retrieved data in local database -func (req *TrieRequest) StoreResult(db ethdb.Database) { +func (req *TrieRequest) StoreResult(db database.Database) { req.Proof.Store(db) } @@ -104,7 +104,7 @@ type CodeRequest struct { } // StoreResult stores the retrieved data in local database -func (req *CodeRequest) StoreResult(db ethdb.Database) { +func (req *CodeRequest) StoreResult(db database.Database) { db.Put(req.Hash[:], req.Data) } @@ -117,7 +117,7 @@ type BlockRequest struct { } // StoreResult stores the retrieved data in local database -func (req *BlockRequest) StoreResult(db ethdb.Database) { +func (req *BlockRequest) StoreResult(db database.Database) { rawdb.WriteBodyRLP(db, req.Hash, req.Number, req.Rlp) } @@ -130,7 +130,7 @@ type ReceiptsRequest struct { } // StoreResult stores the retrieved data in local database -func (req *ReceiptsRequest) StoreResult(db ethdb.Database) { +func (req *ReceiptsRequest) StoreResult(db database.Database) { rawdb.WriteReceipts(db, req.Hash, req.Number, req.Receipts) } @@ -146,7 +146,7 @@ type ChtRequest struct { } // StoreResult stores the retrieved data in local database -func (req *ChtRequest) StoreResult(db ethdb.Database) { +func (req *ChtRequest) StoreResult(db database.Database) { hash, num := req.Header.Hash(), req.Header.Number.Uint64() rawdb.WriteHeader(db, req.Header) @@ -167,7 +167,7 @@ type BloomRequest struct { } // StoreResult stores the retrieved data in local database -func (req *BloomRequest) StoreResult(db ethdb.Database) { +func (req *BloomRequest) StoreResult(db database.Database) { for i, sectionIdx := range req.SectionIndexList { sectionHead := rawdb.ReadCanonicalHash(db, (sectionIdx+1)*req.Config.BloomTrieSize-1) // if we don't have the canonical hash stored for this section head number, we'll still store it under diff --git a/light/odr_test.go b/light/odr_test.go index 3da7b3055e..a10408e1a6 100644 --- a/light/odr_test.go +++ b/light/odr_test.go @@ -33,7 +33,7 @@ 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/ethdb" + "github.com/ethereum/go-ethereum/database" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie" @@ -56,11 +56,11 @@ var ( type testOdr struct { OdrBackend indexerConfig *IndexerConfig - sdb, ldb ethdb.Database + sdb, ldb database.Database disable bool } -func (odr *testOdr) Database() ethdb.Database { +func (odr *testOdr) Database() database.Database { return odr.ldb } @@ -97,11 +97,11 @@ func (odr *testOdr) IndexerConfig() *IndexerConfig { return odr.indexerConfig } -type odrTestFn func(ctx context.Context, db ethdb.Database, bc *core.BlockChain, lc *LightChain, bhash common.Hash) ([]byte, error) +type odrTestFn func(ctx context.Context, db database.Database, bc *core.BlockChain, lc *LightChain, bhash common.Hash) ([]byte, error) func TestOdrGetBlockLes1(t *testing.T) { testChainOdr(t, 1, odrGetBlock) } -func odrGetBlock(ctx context.Context, db ethdb.Database, bc *core.BlockChain, lc *LightChain, bhash common.Hash) ([]byte, error) { +func odrGetBlock(ctx context.Context, db database.Database, bc *core.BlockChain, lc *LightChain, bhash common.Hash) ([]byte, error) { var block *types.Block if bc != nil { block = bc.GetBlockByHash(bhash) @@ -117,7 +117,7 @@ func odrGetBlock(ctx context.Context, db ethdb.Database, bc *core.BlockChain, lc func TestOdrGetReceiptsLes1(t *testing.T) { testChainOdr(t, 1, odrGetReceipts) } -func odrGetReceipts(ctx context.Context, db ethdb.Database, bc *core.BlockChain, lc *LightChain, bhash common.Hash) ([]byte, error) { +func odrGetReceipts(ctx context.Context, db database.Database, bc *core.BlockChain, lc *LightChain, bhash common.Hash) ([]byte, error) { var receipts types.Receipts if bc != nil { number := rawdb.ReadHeaderNumber(db, bhash) @@ -139,7 +139,7 @@ func odrGetReceipts(ctx context.Context, db ethdb.Database, bc *core.BlockChain, func TestOdrAccountsLes1(t *testing.T) { testChainOdr(t, 1, odrAccounts) } -func odrAccounts(ctx context.Context, db ethdb.Database, bc *core.BlockChain, lc *LightChain, bhash common.Hash) ([]byte, error) { +func odrAccounts(ctx context.Context, db database.Database, bc *core.BlockChain, lc *LightChain, bhash common.Hash) ([]byte, error) { dummyAddr := common.HexToAddress("1234567812345678123456781234567812345678") acc := []common.Address{testBankAddress, acc1Addr, acc2Addr, dummyAddr} @@ -169,7 +169,7 @@ type callmsg struct { func (callmsg) CheckNonce() bool { return false } -func odrContractCall(ctx context.Context, db ethdb.Database, bc *core.BlockChain, lc *LightChain, bhash common.Hash) ([]byte, error) { +func odrContractCall(ctx context.Context, db database.Database, bc *core.BlockChain, lc *LightChain, bhash common.Hash) ([]byte, error) { data := common.Hex2Bytes("60CD26850000000000000000000000000000000000000000000000000000000000000000") config := params.TestChainConfig @@ -250,8 +250,8 @@ func testChainGen(i int, block *core.BlockGen) { func testChainOdr(t *testing.T, protocol int, fn odrTestFn) { var ( - sdb = ethdb.NewMemDatabase() - ldb = ethdb.NewMemDatabase() + sdb = rawdb.NewMemoryDatabase() + ldb = rawdb.NewMemoryDatabase() gspec = core.Genesis{Alloc: core.GenesisAlloc{testBankAddress: {Balance: testBankFunds}}} genesis = gspec.MustCommit(sdb) ) diff --git a/light/postprocess.go b/light/postprocess.go index dd1b74a7be..4b98832b9a 100644 --- a/light/postprocess.go +++ b/light/postprocess.go @@ -29,7 +29,7 @@ import ( "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/database" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" @@ -127,7 +127,7 @@ type ChtNode struct { // GetChtRoot reads the CHT root associated to the given section from the database // Note that sectionIdx is specified according to LES/1 CHT section size. -func GetChtRoot(db ethdb.Database, sectionIdx uint64, sectionHead common.Hash) common.Hash { +func GetChtRoot(db database.Database, sectionIdx uint64, sectionHead common.Hash) common.Hash { var encNumber [8]byte binary.BigEndian.PutUint64(encNumber[:], sectionIdx) data, _ := db.Get(append(append(chtPrefix, encNumber[:]...), sectionHead.Bytes()...)) @@ -136,7 +136,7 @@ func GetChtRoot(db ethdb.Database, sectionIdx uint64, sectionHead common.Hash) c // StoreChtRoot writes the CHT root associated to the given section into the database // Note that sectionIdx is specified according to LES/1 CHT section size. -func StoreChtRoot(db ethdb.Database, sectionIdx uint64, sectionHead, root common.Hash) { +func StoreChtRoot(db database.Database, sectionIdx uint64, sectionHead, root common.Hash) { var encNumber [8]byte binary.BigEndian.PutUint64(encNumber[:], sectionIdx) db.Put(append(append(chtPrefix, encNumber[:]...), sectionHead.Bytes()...), root.Bytes()) @@ -144,7 +144,7 @@ func StoreChtRoot(db ethdb.Database, sectionIdx uint64, sectionHead, root common // ChtIndexerBackend implements core.ChainIndexerBackend. type ChtIndexerBackend struct { - diskdb, trieTable ethdb.Database + diskdb, trieTable database.Database odr OdrBackend triedb *trie.Database section, sectionSize uint64 @@ -153,8 +153,8 @@ type ChtIndexerBackend struct { } // NewChtIndexer creates a Cht chain indexer -func NewChtIndexer(db ethdb.Database, odr OdrBackend, size, confirms uint64) *core.ChainIndexer { - trieTable := ethdb.NewTable(db, ChtTablePrefix) +func NewChtIndexer(db database.Database, odr OdrBackend, size, confirms uint64) *core.ChainIndexer { + trieTable := rawdb.NewTable(db, ChtTablePrefix) backend := &ChtIndexerBackend{ diskdb: db, odr: odr, @@ -162,7 +162,7 @@ func NewChtIndexer(db ethdb.Database, odr OdrBackend, size, confirms uint64) *co triedb: trie.NewDatabaseWithCache(trieTable, 1), // Use a tiny cache only to keep memory down sectionSize: size, } - return core.NewChainIndexer(db, ethdb.NewTable(db, "chtIndex-"), backend, size, confirms, time.Millisecond*100, "cht") + return core.NewChainIndexer(db, rawdb.NewTable(db, "chtIndex-"), backend, size, confirms, time.Millisecond*100, "cht") } // fetchMissingNodes tries to retrieve the last entry of the latest trusted CHT from the @@ -247,7 +247,7 @@ var ( ) // GetBloomTrieRoot reads the BloomTrie root assoctiated to the given section from the database -func GetBloomTrieRoot(db ethdb.Database, sectionIdx uint64, sectionHead common.Hash) common.Hash { +func GetBloomTrieRoot(db database.Database, sectionIdx uint64, sectionHead common.Hash) common.Hash { var encNumber [8]byte binary.BigEndian.PutUint64(encNumber[:], sectionIdx) data, _ := db.Get(append(append(bloomTriePrefix, encNumber[:]...), sectionHead.Bytes()...)) @@ -255,7 +255,7 @@ func GetBloomTrieRoot(db ethdb.Database, sectionIdx uint64, sectionHead common.H } // StoreBloomTrieRoot writes the BloomTrie root assoctiated to the given section into the database -func StoreBloomTrieRoot(db ethdb.Database, sectionIdx uint64, sectionHead, root common.Hash) { +func StoreBloomTrieRoot(db database.Database, sectionIdx uint64, sectionHead, root common.Hash) { var encNumber [8]byte binary.BigEndian.PutUint64(encNumber[:], sectionIdx) db.Put(append(append(bloomTriePrefix, encNumber[:]...), sectionHead.Bytes()...), root.Bytes()) @@ -263,7 +263,7 @@ func StoreBloomTrieRoot(db ethdb.Database, sectionIdx uint64, sectionHead, root // BloomTrieIndexerBackend implements core.ChainIndexerBackend type BloomTrieIndexerBackend struct { - diskdb, trieTable ethdb.Database + diskdb, trieTable database.Database triedb *trie.Database odr OdrBackend section uint64 @@ -275,8 +275,8 @@ type BloomTrieIndexerBackend struct { } // NewBloomTrieIndexer creates a BloomTrie chain indexer -func NewBloomTrieIndexer(db ethdb.Database, odr OdrBackend, parentSize, size uint64) *core.ChainIndexer { - trieTable := ethdb.NewTable(db, BloomTrieTablePrefix) +func NewBloomTrieIndexer(db database.Database, odr OdrBackend, parentSize, size uint64) *core.ChainIndexer { + trieTable := rawdb.NewTable(db, BloomTrieTablePrefix) backend := &BloomTrieIndexerBackend{ diskdb: db, odr: odr, @@ -287,7 +287,7 @@ func NewBloomTrieIndexer(db ethdb.Database, odr OdrBackend, parentSize, size uin } backend.bloomTrieRatio = size / parentSize backend.sectionHeads = make([]common.Hash, backend.bloomTrieRatio) - return core.NewChainIndexer(db, ethdb.NewTable(db, "bltIndex-"), backend, size, 0, time.Millisecond*100, "bloomtrie") + return core.NewChainIndexer(db, rawdb.NewTable(db, "bltIndex-"), backend, size, 0, time.Millisecond*100, "bloomtrie") } // fetchMissingNodes tries to retrieve the last entries of the latest trusted bloom trie from the diff --git a/light/trie.go b/light/trie.go index ab4e18b43e..2102f14c89 100644 --- a/light/trie.go +++ b/light/trie.go @@ -25,7 +25,7 @@ import ( "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/database" "github.com/ethereum/go-ethereum/trie" ) @@ -141,7 +141,7 @@ func (t *odrTrie) GetKey(sha []byte) []byte { return nil } -func (t *odrTrie) Prove(key []byte, fromLevel uint, proofDb ethdb.Putter) error { +func (t *odrTrie) Prove(key []byte, fromLevel uint, proofDb database.Writer) error { return errors.New("not implemented, needs client/server interface split") } diff --git a/light/trie_test.go b/light/trie_test.go index 5b5fce31d9..4919f89641 100644 --- a/light/trie_test.go +++ b/light/trie_test.go @@ -25,17 +25,17 @@ import ( "github.com/davecgh/go-spew/spew" "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/trie" ) func TestNodeIterator(t *testing.T) { var ( - fulldb = ethdb.NewMemDatabase() - lightdb = ethdb.NewMemDatabase() + fulldb = rawdb.NewMemoryDatabase() + lightdb = rawdb.NewMemoryDatabase() gspec = core.Genesis{Alloc: core.GenesisAlloc{testBankAddress: {Balance: testBankFunds}}} genesis = gspec.MustCommit(fulldb) ) diff --git a/light/txpool.go b/light/txpool.go index e945ef2ec1..0e73587f44 100644 --- a/light/txpool.go +++ b/light/txpool.go @@ -27,7 +27,7 @@ import ( "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/database" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" @@ -59,7 +59,7 @@ type TxPool struct { mu sync.RWMutex chain *LightChain odr OdrBackend - chainDb ethdb.Database + chainDb database.Database relay TxRelayBackend head common.Hash nonce map[common.Address]uint64 // "pending" nonce diff --git a/light/txpool_test.go b/light/txpool_test.go index ce77573efd..4f446c6ca2 100644 --- a/light/txpool_test.go +++ b/light/txpool_test.go @@ -26,9 +26,9 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/params" ) @@ -81,8 +81,8 @@ func TestTxPool(t *testing.T) { } var ( - sdb = ethdb.NewMemDatabase() - ldb = ethdb.NewMemDatabase() + sdb = rawdb.NewMemoryDatabase() + ldb = rawdb.NewMemoryDatabase() gspec = core.Genesis{Alloc: core.GenesisAlloc{testBankAddress: {Balance: testBankFunds}}} genesis = gspec.MustCommit(sdb) ) diff --git a/miner/worker_test.go b/miner/worker_test.go index bfa9cbd21d..62fa6e2a63 100644 --- a/miner/worker_test.go +++ b/miner/worker_test.go @@ -26,10 +26,11 @@ import ( "github.com/ethereum/go-ethereum/consensus/clique" "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/rawdb" "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/ethdb" + "github.com/ethereum/go-ethereum/database" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/params" ) @@ -70,7 +71,7 @@ func init() { // testWorkerBackend implements worker.Backend interfaces and wraps all information needed during the testing. type testWorkerBackend struct { - db ethdb.Database + db database.Database txPool *core.TxPool chain *core.BlockChain testTxFeed event.Feed @@ -79,7 +80,7 @@ type testWorkerBackend struct { func newTestWorkerBackend(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine, n int) *testWorkerBackend { var ( - db = ethdb.NewMemDatabase() + db = rawdb.NewMemoryDatabase() gspec = core.Genesis{ Config: chainConfig, Alloc: core.GenesisAlloc{testBankAddress: {Balance: testBankFunds}}, @@ -142,7 +143,7 @@ func TestPendingStateAndBlockEthash(t *testing.T) { testPendingStateAndBlock(t, ethashChainConfig, ethash.NewFaker()) } func TestPendingStateAndBlockClique(t *testing.T) { - testPendingStateAndBlock(t, cliqueChainConfig, clique.New(cliqueChainConfig.Clique, ethdb.NewMemDatabase())) + testPendingStateAndBlock(t, cliqueChainConfig, clique.New(cliqueChainConfig.Clique, rawdb.NewMemoryDatabase())) } func testPendingStateAndBlock(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine) { @@ -174,7 +175,7 @@ func TestEmptyWorkEthash(t *testing.T) { testEmptyWork(t, ethashChainConfig, ethash.NewFaker()) } func TestEmptyWorkClique(t *testing.T) { - testEmptyWork(t, cliqueChainConfig, clique.New(cliqueChainConfig.Clique, ethdb.NewMemDatabase())) + testEmptyWork(t, cliqueChainConfig, clique.New(cliqueChainConfig.Clique, rawdb.NewMemoryDatabase())) } func testEmptyWork(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine) { @@ -291,7 +292,7 @@ func TestRegenerateMiningBlockEthash(t *testing.T) { } func TestRegenerateMiningBlockClique(t *testing.T) { - testRegenerateMiningBlock(t, cliqueChainConfig, clique.New(cliqueChainConfig.Clique, ethdb.NewMemDatabase())) + testRegenerateMiningBlock(t, cliqueChainConfig, clique.New(cliqueChainConfig.Clique, rawdb.NewMemoryDatabase())) } func testRegenerateMiningBlock(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine) { @@ -356,7 +357,7 @@ func TestAdjustIntervalEthash(t *testing.T) { } func TestAdjustIntervalClique(t *testing.T) { - testAdjustInterval(t, cliqueChainConfig, clique.New(cliqueChainConfig.Clique, ethdb.NewMemDatabase())) + testAdjustInterval(t, cliqueChainConfig, clique.New(cliqueChainConfig.Clique, rawdb.NewMemoryDatabase())) } func testAdjustInterval(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine) { diff --git a/node/node.go b/node/node.go index f267cdc461..e1f65f2d83 100644 --- a/node/node.go +++ b/node/node.go @@ -27,7 +27,8 @@ import ( "sync" "github.com/ethereum/go-ethereum/accounts" - "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/database" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/internal/debug" "github.com/ethereum/go-ethereum/log" @@ -601,11 +602,11 @@ func (n *Node) EventMux() *event.TypeMux { // OpenDatabase opens an existing database with the given name (or creates one if no // previous can be found) from within the node's instance directory. If the node is // ephemeral, a memory database is returned. -func (n *Node) OpenDatabase(name string, cache, handles int) (ethdb.Database, error) { +func (n *Node) OpenDatabase(name string, cache, handles int, namespace string) (database.Database, error) { if n.config.DataDir == "" { - return ethdb.NewMemDatabase(), nil + return rawdb.NewMemoryDatabase(), nil } - return ethdb.NewLDBDatabase(n.config.ResolvePath(name), cache, handles) + return rawdb.NewLeveldbDatabase(n.config.ResolvePath(name), cache, handles, namespace) } // ResolvePath returns the absolute path of a resource in the instance directory. diff --git a/node/service.go b/node/service.go index 6a96d9b1e1..a081e128c9 100644 --- a/node/service.go +++ b/node/service.go @@ -17,10 +17,12 @@ package node import ( + "path/filepath" "reflect" "github.com/ethereum/go-ethereum/accounts" - "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/database" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/rpc" @@ -39,11 +41,35 @@ type ServiceContext struct { // OpenDatabase opens an existing database with the given name (or creates one // if no previous can be found) from within the node's data directory. If the // node is an ephemeral one, a memory database is returned. -func (ctx *ServiceContext) OpenDatabase(name string, cache int, handles int) (ethdb.Database, error) { +func (ctx *ServiceContext) OpenDatabase(name string, cache int, handles int, namespace string) (database.Database, error) { if ctx.config.DataDir == "" { - return ethdb.NewMemDatabase(), nil + return rawdb.NewMemoryDatabase(), nil } - db, err := ethdb.NewLDBDatabase(ctx.config.ResolvePath(name), cache, handles) + db, err := rawdb.NewLeveldbDatabase(ctx.config.ResolvePath(name), cache, handles, namespace) + if err != nil { + return nil, err + } + return db, nil +} + +// OpenDatabaseWithFreezer opens an existing database with the given name (or +// creates one if no previous can be found) from within the node's data directory, +// also attaching a chain freezer to it that moves ancient chain data from the +// database to immutable append-only files. If the node is an ephemeral one, a +// memory database is returned. +func (ctx *ServiceContext) OpenDatabaseWithFreezer(name string, cache int, handles int, freezer string, namespace string) (database.Database, error) { + if ctx.config.DataDir == "" { + return rawdb.NewMemoryDatabase(), nil + } + root := ctx.config.ResolvePath(name) + + switch { + case freezer == "": + freezer = filepath.Join(root, "ancient") + case !filepath.IsAbs(freezer): + freezer = ctx.config.ResolvePath(freezer) + } + db, err := rawdb.NewLeveldbDatabaseWithFreezer(root, cache, handles, freezer, namespace) if err != nil { return nil, err } diff --git a/node/service_test.go b/node/service_test.go index 73c51eccbb..63004a51ab 100644 --- a/node/service_test.go +++ b/node/service_test.go @@ -39,7 +39,7 @@ func TestContextDatabases(t *testing.T) { } // Request the opening/creation of a database and ensure it persists to disk ctx := &ServiceContext{config: &Config{Name: "unit-test", DataDir: dir}} - db, err := ctx.OpenDatabase("persistent", 0, 0) + db, err := ctx.OpenDatabase("persistent", 0, 0, "") if err != nil { t.Fatalf("failed to open persistent database: %v", err) } @@ -50,7 +50,7 @@ func TestContextDatabases(t *testing.T) { } // Request th opening/creation of an ephemeral database and ensure it's not persisted ctx = &ServiceContext{config: &Config{DataDir: ""}} - db, err = ctx.OpenDatabase("ephemeral", 0, 0) + db, err = ctx.OpenDatabase("ephemeral", 0, 0, "") if err != nil { t.Fatalf("failed to open ephemeral database: %v", err) } diff --git a/tests/block_test_util.go b/tests/block_test_util.go index 9fa69bf4ed..d40a3726c0 100644 --- a/tests/block_test_util.go +++ b/tests/block_test_util.go @@ -30,10 +30,10 @@ import ( "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" ) @@ -101,7 +101,7 @@ func (t *BlockTest) Run() error { } // import pre accounts & construct test genesis block & state root - db := ethdb.NewMemDatabase() + db := rawdb.NewMemoryDatabase() gblock, err := t.genesis(config).Commit(db) if err != nil { return err diff --git a/tests/state_test_util.go b/tests/state_test_util.go index 436284196d..ff275f8afd 100644 --- a/tests/state_test_util.go +++ b/tests/state_test_util.go @@ -27,11 +27,12 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" "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/ethdb" + "github.com/ethereum/go-ethereum/database" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" "golang.org/x/crypto/sha3" @@ -126,7 +127,7 @@ func (t *StateTest) Run(subtest StateSubtest, vmconfig vm.Config) (*state.StateD return nil, UnsupportedForkError{subtest.Fork} } block := t.genesis(config).ToBlock(nil) - statedb := MakePreState(ethdb.NewMemDatabase(), t.json.Pre) + statedb := MakePreState(rawdb.NewMemoryDatabase(), t.json.Pre) post := t.json.Post[subtest.Fork][subtest.Index] msg, err := t.json.Tx.toMessage(post) @@ -168,7 +169,7 @@ func (t *StateTest) gasLimit(subtest StateSubtest) uint64 { return t.json.Tx.GasLimit[t.json.Post[subtest.Fork][subtest.Index].Indexes.Gas] } -func MakePreState(db ethdb.Database, accounts core.GenesisAlloc) *state.StateDB { +func MakePreState(db database.Database, accounts core.GenesisAlloc) *state.StateDB { sdb := state.NewDatabase(db) statedb, _ := state.New(common.Hash{}, sdb) for addr, a := range accounts { diff --git a/tests/vm_test_util.go b/tests/vm_test_util.go index cb81c5b94e..91566c47e3 100644 --- a/tests/vm_test_util.go +++ b/tests/vm_test_util.go @@ -26,10 +26,10 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/params" ) @@ -79,7 +79,7 @@ type vmExecMarshaling struct { } func (t *VMTest) Run(vmconfig vm.Config) error { - statedb := MakePreState(ethdb.NewMemDatabase(), t.json.Pre) + statedb := MakePreState(rawdb.NewMemoryDatabase(), t.json.Pre) ret, gasRemaining, err := t.exec(statedb, vmconfig) if t.json.GasRemaining == nil { diff --git a/trie/database.go b/trie/database.go index aba5943f5a..45aeedee51 100644 --- a/trie/database.go +++ b/trie/database.go @@ -24,7 +24,7 @@ import ( "github.com/allegro/bigcache" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/database" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/rlp" @@ -55,20 +55,11 @@ var secureKeyPrefix = []byte("secure-key-") // secureKeyLength is the length of the above prefix + 32byte hash. const secureKeyLength = 11 + 32 -// DatabaseReader wraps the Get and Has method of a backing store for the trie. -type DatabaseReader interface { - // Get retrieves the value associated with key from the database. - Get(key []byte) (value []byte, err error) - - // Has retrieves whether a key is present in the database. - Has(key []byte) (bool, error) -} - // Database is an intermediate write layer between the trie data structures and // the disk database. The aim is to accumulate trie writes in-memory and only // periodically flush a couple tries to disk, garbage collecting the remainder. type Database struct { - diskdb ethdb.Database // Persistent storage for matured trie nodes + diskdb database.KeyValueStore // Persistent storage for matured trie nodes cleans *bigcache.BigCache // GC friendly memory cache of clean node RLPs dirties map[common.Hash]*cachedNode // Data and references relationships of dirty nodes @@ -271,14 +262,14 @@ func expandNode(hash hashNode, n node, cachegen uint16) node { // NewDatabase creates a new trie database to store ephemeral trie content before // its written out to disk or garbage collected. No read cache is created, so all // data retrievals will hit the underlying disk database. -func NewDatabase(diskdb ethdb.Database) *Database { +func NewDatabase(diskdb database.KeyValueStore) *Database { return NewDatabaseWithCache(diskdb, 0) } // NewDatabaseWithCache creates a new trie database to store ephemeral trie content // before its written out to disk or garbage collected. It also acts as a read cache // for nodes loaded from disk. -func NewDatabaseWithCache(diskdb ethdb.Database, cache int) *Database { +func NewDatabaseWithCache(diskdb database.KeyValueStore, cache int) *Database { var cleans *bigcache.BigCache if cache > 0 { cleans, _ = bigcache.NewBigCache(bigcache.Config{ @@ -298,7 +289,7 @@ func NewDatabaseWithCache(diskdb ethdb.Database, cache int) *Database { } // DiskDB retrieves the persistent storage backing the trie database. -func (db *Database) DiskDB() DatabaseReader { +func (db *Database) DiskDB() database.Reader { return db.diskdb } @@ -582,7 +573,7 @@ func (db *Database) Cap(limit common.StorageSize) error { db.lock.RUnlock() return err } - if batch.ValueSize() > ethdb.IdealBatchSize { + if batch.ValueSize() > database.IdealBatchSize { if err := batch.Write(); err != nil { db.lock.RUnlock() return err @@ -601,7 +592,7 @@ func (db *Database) Cap(limit common.StorageSize) error { return err } // If we exceeded the ideal batch size, commit and reset - if batch.ValueSize() >= ethdb.IdealBatchSize { + if batch.ValueSize() >= database.IdealBatchSize { if err := batch.Write(); err != nil { log.Error("Failed to write flush list to disk", "err", err) db.lock.RUnlock() @@ -677,7 +668,7 @@ func (db *Database) Commit(node common.Hash, report bool) error { db.lock.RUnlock() return err } - if batch.ValueSize() > ethdb.IdealBatchSize { + if batch.ValueSize() > database.IdealBatchSize { if err := batch.Write(); err != nil { db.lock.RUnlock() return err @@ -728,7 +719,7 @@ func (db *Database) Commit(node common.Hash, report bool) error { } // commit is the private locked version of Commit. -func (db *Database) commit(hash common.Hash, batch ethdb.Batch) error { +func (db *Database) commit(hash common.Hash, batch database.Batch) error { // If the node does not exist, it's a previously committed node node, ok := db.dirties[hash] if !ok { @@ -743,7 +734,7 @@ func (db *Database) commit(hash common.Hash, batch ethdb.Batch) error { return err } // If we've reached an optimal batch size, commit and start over - if batch.ValueSize() >= ethdb.IdealBatchSize { + if batch.ValueSize() >= database.IdealBatchSize { if err := batch.Write(); err != nil { return err } diff --git a/trie/iterator_test.go b/trie/iterator_test.go index 4f633b195f..7883f87af0 100644 --- a/trie/iterator_test.go +++ b/trie/iterator_test.go @@ -23,7 +23,7 @@ import ( "testing" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/internal/keyvalue" ) func TestIterator(t *testing.T) { @@ -120,11 +120,14 @@ func TestNodeIteratorCoverage(t *testing.T) { } } } - for _, key := range db.diskdb.(*ethdb.MemDatabase).Keys() { + it := db.diskdb.NewIterator() + for it.Next() { + key := it.Key() if _, ok := hashes[common.BytesToHash(key)]; !ok { t.Errorf("state entry not reported %x", key) } } + it.Release() } type kvs struct{ k, v string } @@ -289,7 +292,7 @@ func TestIteratorContinueAfterErrorDisk(t *testing.T) { testIteratorContinueA func TestIteratorContinueAfterErrorMemonly(t *testing.T) { testIteratorContinueAfterError(t, true) } func testIteratorContinueAfterError(t *testing.T, memonly bool) { - diskdb := ethdb.NewMemDatabase() + diskdb := keyvalue.NewMemoryDatabase() triedb := NewDatabase(diskdb) tr, _ := New(common.Hash{}, triedb) @@ -309,7 +312,11 @@ func testIteratorContinueAfterError(t *testing.T, memonly bool) { if memonly { memKeys = triedb.Nodes() } else { - diskKeys = diskdb.Keys() + it := diskdb.NewIterator() + for it.Next() { + diskKeys = append(diskKeys, it.Key()) + } + it.Release() } for i := 0; i < 20; i++ { // Create trie that will load all nodes from DB. @@ -376,7 +383,7 @@ func TestIteratorContinueAfterSeekErrorMemonly(t *testing.T) { func testIteratorContinueAfterSeekError(t *testing.T, memonly bool) { // Commit test trie to db, then remove the node containing "bars". - diskdb := ethdb.NewMemDatabase() + diskdb := keyvalue.NewMemoryDatabase() triedb := NewDatabase(diskdb) ctr, _ := New(common.Hash{}, triedb) diff --git a/trie/proof.go b/trie/proof.go index f90ecd7d88..b2401c31c8 100644 --- a/trie/proof.go +++ b/trie/proof.go @@ -22,7 +22,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/database" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/rlp" ) @@ -34,7 +34,7 @@ import ( // If the trie does not contain a value for key, the returned proof contains all // nodes of the longest existing prefix of the key (at least the root node), ending // with the node that proves the absence of the key. -func (t *Trie) Prove(key []byte, fromLevel uint, proofDb ethdb.Putter) error { +func (t *Trie) Prove(key []byte, fromLevel uint, proofDb database.Writer) error { // Collect all nodes on the path to key. key = keybytesToHex(key) nodes := []node{} @@ -97,14 +97,14 @@ func (t *Trie) Prove(key []byte, fromLevel uint, proofDb ethdb.Putter) error { // If the trie does not contain a value for key, the returned proof contains all // nodes of the longest existing prefix of the key (at least the root node), ending // with the node that proves the absence of the key. -func (t *SecureTrie) Prove(key []byte, fromLevel uint, proofDb ethdb.Putter) error { +func (t *SecureTrie) Prove(key []byte, fromLevel uint, proofDb database.Writer) error { return t.trie.Prove(key, fromLevel, proofDb) } // VerifyProof checks merkle proofs. The given proof must contain the value for // key in a trie with the given root hash. VerifyProof returns an error if the // proof contains invalid trie nodes or the wrong value. -func VerifyProof(rootHash common.Hash, key []byte, proofDb DatabaseReader) (value []byte, nodes int, err error) { +func VerifyProof(rootHash common.Hash, key []byte, proofDb database.Reader) (value []byte, nodes int, err error) { key = keybytesToHex(key) wantHash := rootHash for i := 0; ; i++ { diff --git a/trie/proof_test.go b/trie/proof_test.go index 996f874781..24222c6400 100644 --- a/trie/proof_test.go +++ b/trie/proof_test.go @@ -25,7 +25,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/internal/keyvalue" ) func init() { @@ -34,18 +34,18 @@ func init() { // makeProvers creates Merkle trie provers based on different implementations to // test all variations. -func makeProvers(trie *Trie) []func(key []byte) *ethdb.MemDatabase { - var provers []func(key []byte) *ethdb.MemDatabase +func makeProvers(trie *Trie) []func(key []byte) *keyvalue.MemoryDatabase { + var provers []func(key []byte) *keyvalue.MemoryDatabase // Create a direct trie based Merkle prover - provers = append(provers, func(key []byte) *ethdb.MemDatabase { - proof := ethdb.NewMemDatabase() + provers = append(provers, func(key []byte) *keyvalue.MemoryDatabase { + proof := keyvalue.NewMemoryDatabase() trie.Prove(key, 0, proof) return proof }) // Create a leaf iterator based Merkle prover - provers = append(provers, func(key []byte) *ethdb.MemDatabase { - proof := ethdb.NewMemDatabase() + provers = append(provers, func(key []byte) *keyvalue.MemoryDatabase { + proof := keyvalue.NewMemoryDatabase() if it := NewIterator(trie.NodeIterator(key)); it.Next() && bytes.Equal(key, it.Key) { for _, p := range it.Prove() { proof.Put(crypto.Keccak256(p), p) @@ -106,9 +106,14 @@ func TestBadProof(t *testing.T) { if proof == nil { t.Fatalf("prover %d: nil proof", i) } - key := proof.Keys()[mrand.Intn(proof.Len())] + it := proof.NewIterator() + for i, d := 0, mrand.Intn(proof.Len()); i <= d; i++ { + it.Next() + } + key := it.Key() val, _ := proof.Get(key) proof.Delete(key) + it.Release() mutateByte(val) proof.Put(crypto.Keccak256(val), val) @@ -127,7 +132,7 @@ func TestMissingKeyProof(t *testing.T) { updateString(trie, "k", "v") for i, key := range []string{"a", "j", "l", "z"} { - proof := ethdb.NewMemDatabase() + proof := keyvalue.NewMemoryDatabase() trie.Prove([]byte(key), 0, proof) if proof.Len() != 1 { @@ -164,8 +169,8 @@ func BenchmarkProve(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { kv := vals[keys[i%len(keys)]] - proofs := ethdb.NewMemDatabase() - if trie.Prove(kv.k, 0, proofs); len(proofs.Keys()) == 0 { + proofs := keyvalue.NewMemoryDatabase() + if trie.Prove(kv.k, 0, proofs); proofs.Len() == 0 { b.Fatalf("zero length proof for %x", kv.k) } } @@ -175,10 +180,10 @@ func BenchmarkVerifyProof(b *testing.B) { trie, vals := randomTrie(100) root := trie.Hash() var keys []string - var proofs []*ethdb.MemDatabase + var proofs []*keyvalue.MemoryDatabase for k := range vals { keys = append(keys, k) - proof := ethdb.NewMemDatabase() + proof := keyvalue.NewMemoryDatabase() trie.Prove([]byte(k), 0, proof) proofs = append(proofs, proof) } diff --git a/trie/secure_trie_test.go b/trie/secure_trie_test.go index d16d999684..52ee7c9d31 100644 --- a/trie/secure_trie_test.go +++ b/trie/secure_trie_test.go @@ -24,18 +24,18 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/internal/keyvalue" ) func newEmptySecure() *SecureTrie { - trie, _ := NewSecure(common.Hash{}, NewDatabase(ethdb.NewMemDatabase()), 0) + trie, _ := NewSecure(common.Hash{}, NewDatabase(keyvalue.NewMemoryDatabase()), 0) return trie } // makeTestSecureTrie creates a large enough secure trie for testing. func makeTestSecureTrie() (*Database, *SecureTrie, map[string][]byte) { // Create an empty trie - triedb := NewDatabase(ethdb.NewMemDatabase()) + triedb := NewDatabase(keyvalue.NewMemoryDatabase()) trie, _ := NewSecure(common.Hash{}, triedb, 0) diff --git a/trie/sync.go b/trie/sync.go index 67dff5a8b6..d6f4eace5a 100644 --- a/trie/sync.go +++ b/trie/sync.go @@ -22,7 +22,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/prque" - "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/database" ) // ErrNotRequested is returned by the trie sync when it's requested to process a @@ -72,14 +72,14 @@ func newSyncMemBatch() *syncMemBatch { // unknown trie hashes to retrieve, accepts node data associated with said hashes // and reconstructs the trie step by step until all is done. type Sync struct { - database DatabaseReader // Persistent database to check for existing entries + database database.Reader // Persistent database to check for existing entries membatch *syncMemBatch // Memory buffer to avoid frequent database writes requests map[common.Hash]*request // Pending requests pertaining to a key hash queue *prque.Prque // Priority queue with the pending requests } // NewSync creates a new trie data download scheduler. -func NewSync(root common.Hash, database DatabaseReader, callback LeafCallback) *Sync { +func NewSync(root common.Hash, database database.Reader, callback LeafCallback) *Sync { ts := &Sync{ database: database, membatch: newSyncMemBatch(), @@ -213,7 +213,7 @@ func (s *Sync) Process(results []SyncResult) (bool, int, error) { // Commit flushes the data stored in the internal membatch out to persistent // storage, returning the number of items written and any occurred error. -func (s *Sync) Commit(dbw ethdb.Putter) (int, error) { +func (s *Sync) Commit(dbw database.Writer) (int, error) { // Dump the membatch into a database dbw for i, key := range s.membatch.order { if err := dbw.Put(key[:], s.membatch.batch[key]); err != nil { diff --git a/trie/sync_test.go b/trie/sync_test.go index c76779e5c7..86dbe3d243 100644 --- a/trie/sync_test.go +++ b/trie/sync_test.go @@ -21,13 +21,13 @@ import ( "testing" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/internal/keyvalue" ) // makeTestTrie create a sample test trie to test node-wise reconstruction. func makeTestTrie() (*Database, *Trie, map[string][]byte) { // Create an empty trie - triedb := NewDatabase(ethdb.NewMemDatabase()) + triedb := NewDatabase(keyvalue.NewMemoryDatabase()) trie, _ := New(common.Hash{}, triedb) // Fill it with some arbitrary data @@ -88,13 +88,13 @@ func checkTrieConsistency(db *Database, root common.Hash) error { // Tests that an empty trie is not scheduled for syncing. func TestEmptySync(t *testing.T) { - dbA := NewDatabase(ethdb.NewMemDatabase()) - dbB := NewDatabase(ethdb.NewMemDatabase()) + dbA := NewDatabase(keyvalue.NewMemoryDatabase()) + dbB := NewDatabase(keyvalue.NewMemoryDatabase()) emptyA, _ := New(common.Hash{}, dbA) emptyB, _ := New(emptyRoot, dbB) for i, trie := range []*Trie{emptyA, emptyB} { - if req := NewSync(trie.Hash(), ethdb.NewMemDatabase(), nil).Missing(1); len(req) != 0 { + if req := NewSync(trie.Hash(), keyvalue.NewMemoryDatabase(), nil).Missing(1); len(req) != 0 { t.Errorf("test %d: content requested for empty trie: %v", i, req) } } @@ -110,7 +110,7 @@ func testIterativeSync(t *testing.T, batch int) { srcDb, srcTrie, srcData := makeTestTrie() // Create a destination trie and sync with the scheduler - diskdb := ethdb.NewMemDatabase() + diskdb := keyvalue.NewMemoryDatabase() triedb := NewDatabase(diskdb) sched := NewSync(srcTrie.Hash(), diskdb, nil) @@ -143,7 +143,7 @@ func TestIterativeDelayedSync(t *testing.T) { srcDb, srcTrie, srcData := makeTestTrie() // Create a destination trie and sync with the scheduler - diskdb := ethdb.NewMemDatabase() + diskdb := keyvalue.NewMemoryDatabase() triedb := NewDatabase(diskdb) sched := NewSync(srcTrie.Hash(), diskdb, nil) @@ -181,7 +181,7 @@ func testIterativeRandomSync(t *testing.T, batch int) { srcDb, srcTrie, srcData := makeTestTrie() // Create a destination trie and sync with the scheduler - diskdb := ethdb.NewMemDatabase() + diskdb := keyvalue.NewMemoryDatabase() triedb := NewDatabase(diskdb) sched := NewSync(srcTrie.Hash(), diskdb, nil) @@ -222,7 +222,7 @@ func TestIterativeRandomDelayedSync(t *testing.T) { srcDb, srcTrie, srcData := makeTestTrie() // Create a destination trie and sync with the scheduler - diskdb := ethdb.NewMemDatabase() + diskdb := keyvalue.NewMemoryDatabase() triedb := NewDatabase(diskdb) sched := NewSync(srcTrie.Hash(), diskdb, nil) @@ -269,7 +269,7 @@ func TestDuplicateAvoidanceSync(t *testing.T) { srcDb, srcTrie, srcData := makeTestTrie() // Create a destination trie and sync with the scheduler - diskdb := ethdb.NewMemDatabase() + diskdb := keyvalue.NewMemoryDatabase() triedb := NewDatabase(diskdb) sched := NewSync(srcTrie.Hash(), diskdb, nil) @@ -309,7 +309,7 @@ func TestIncompleteSync(t *testing.T) { srcDb, srcTrie, _ := makeTestTrie() // Create a destination trie and sync with the scheduler - diskdb := ethdb.NewMemDatabase() + diskdb := keyvalue.NewMemoryDatabase() triedb := NewDatabase(diskdb) sched := NewSync(srcTrie.Hash(), diskdb, nil) diff --git a/trie/trie_test.go b/trie/trie_test.go index f9d6029c99..4203f5b823 100644 --- a/trie/trie_test.go +++ b/trie/trie_test.go @@ -32,7 +32,8 @@ import ( "github.com/davecgh/go-spew/spew" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/database" + "github.com/ethereum/go-ethereum/internal/keyvalue" "github.com/ethereum/go-ethereum/rlp" ) @@ -43,7 +44,7 @@ func init() { // Used for testing func newEmpty() *Trie { - trie, _ := New(common.Hash{}, NewDatabase(ethdb.NewMemDatabase())) + trie, _ := New(common.Hash{}, NewDatabase(keyvalue.NewMemoryDatabase())) return trie } @@ -67,7 +68,7 @@ func TestNull(t *testing.T) { } func TestMissingRoot(t *testing.T) { - trie, err := New(common.HexToHash("0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33"), NewDatabase(ethdb.NewMemDatabase())) + trie, err := New(common.HexToHash("0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33"), NewDatabase(keyvalue.NewMemoryDatabase())) if trie != nil { t.Error("New returned non-nil trie for invalid root") } @@ -80,7 +81,7 @@ func TestMissingNodeDisk(t *testing.T) { testMissingNode(t, false) } func TestMissingNodeMemonly(t *testing.T) { testMissingNode(t, true) } func testMissingNode(t *testing.T, memonly bool) { - diskdb := ethdb.NewMemDatabase() + diskdb := keyvalue.NewMemoryDatabase() triedb := NewDatabase(diskdb) trie, _ := New(common.Hash{}, triedb) @@ -317,13 +318,13 @@ func TestLargeValue(t *testing.T) { } type countingDB struct { - ethdb.Database + database.KeyValueStore gets map[string]int } func (db *countingDB) Get(key []byte) ([]byte, error) { db.gets[string(key)]++ - return db.Database.Get(key) + return db.KeyValueStore.Get(key) } // TestCacheUnload checks that decoded nodes are unloaded after a @@ -342,7 +343,7 @@ func TestCacheUnload(t *testing.T) { // Commit the trie repeatedly and access key1. // The branch containing it is loaded from DB exactly two times: // in the 0th and 6th iteration. - diskdb := &countingDB{Database: trie.db.diskdb, gets: make(map[string]int)} + diskdb := &countingDB{KeyValueStore: trie.db.diskdb, gets: make(map[string]int)} triedb := NewDatabase(diskdb) trie, _ = New(root, triedb) trie.SetCacheLimit(5) @@ -412,7 +413,7 @@ func (randTest) Generate(r *rand.Rand, size int) reflect.Value { } func runRandTest(rt randTest) bool { - triedb := NewDatabase(ethdb.NewMemDatabase()) + triedb := NewDatabase(keyvalue.NewMemoryDatabase()) tr, _ := New(common.Hash{}, triedb) values := make(map[string]string) // tracks content of the trie @@ -540,7 +541,7 @@ func benchGet(b *testing.B, commit bool) { b.StopTimer() if commit { - ldb := trie.db.diskdb.(*ethdb.LDBDatabase) + ldb := trie.db.diskdb.(*keyvalue.LeveldbDatabase) ldb.Close() os.RemoveAll(ldb.Path()) } @@ -596,7 +597,7 @@ func tempDB() (string, *Database) { if err != nil { panic(fmt.Sprintf("can't create temporary directory: %v", err)) } - diskdb, err := ethdb.NewLDBDatabase(dir, 256, 0) + diskdb, err := keyvalue.NewLeveldbDatabase(dir, 256, 0, "") if err != nil { panic(fmt.Sprintf("can't create temporary database: %v", err)) }