package explorer import ( "github.com/ethereum/go-ethereum/common" "github.com/syndtr/goleveldb/leveldb" "github.com/syndtr/goleveldb/leveldb/filter" "github.com/syndtr/goleveldb/leveldb/opt" levelutil "github.com/syndtr/goleveldb/leveldb/util" ) // database is an adapter for *leveldb.DB type database interface { databaseWriter databaseReader NewBatch() batch } type databaseWriter interface { Put(key, val []byte) error } type databaseReader interface { Get(key []byte) ([]byte, error) Has(key []byte) (bool, error) NewPrefixIterator(prefix []byte) iterator NewSizedIterator(start []byte, size int) iterator } type batch interface { databaseWriter Write() error ValueSize() int } // lvlDB is the adapter for leveldb.Database type lvlDB struct { db *leveldb.DB } func newLvlDB(dbPath string) (database, error) { // https://github.com/ethereum/go-ethereum/blob/master/ethdb/leveldb/leveldb.go#L98 options. // We had 0 for handles and cache params before, so set 0s for all of them. Filter opt is the same. options := &opt.Options{ OpenFilesCacheCapacity: 500, BlockCacheCapacity: 8 * 1024 * 1024, // 8 MiB WriteBuffer: 4 * 1024 * 1024, // 4 MiB Filter: filter.NewBloomFilter(10), } db, err := leveldb.OpenFile(dbPath, options) if err != nil { return nil, err } return &lvlDB{db}, nil } func (db *lvlDB) Put(key, val []byte) error { return db.db.Put(key, val, nil) } func (db *lvlDB) Get(key []byte) ([]byte, error) { return db.db.Get(key, nil) } func (db *lvlDB) Has(key []byte) (bool, error) { return db.db.Has(key, nil) } func (db *lvlDB) NewBatch() batch { batch := new(leveldb.Batch) return &lvlBatch{ batch: batch, db: db.db, } } func (db *lvlDB) NewPrefixIterator(prefix []byte) iterator { rng := levelutil.BytesPrefix(prefix) it := db.db.NewIterator(rng, nil) return it } func (db *lvlDB) NewSizedIterator(start []byte, size int) iterator { return db.newSizedIterator(start, size) } type sizedIterator struct { it iterator curIndex int sizeLimit int } func (db *lvlDB) newSizedIterator(start []byte, size int) *sizedIterator { rng := &levelutil.Range{Start: start, Limit: nil} it := db.db.NewIterator(rng, nil) return &sizedIterator{ it: it, curIndex: 0, sizeLimit: size, } } func (it *sizedIterator) Next() bool { if it.curIndex >= it.sizeLimit-1 { return false } it.curIndex++ return it.it.Next() } func (it *sizedIterator) Key() []byte { return it.it.Key() } func (it *sizedIterator) Value() []byte { return it.it.Value() } func (it *sizedIterator) Release() { it.it.Release() } func (it *sizedIterator) Error() error { return it.it.Error() } // Note: lvlBatch is not thread safe type lvlBatch struct { batch *leveldb.Batch db *leveldb.DB valueSize int } func (b *lvlBatch) Put(key, val []byte) error { b.batch.Put(key, val) b.valueSize += len(val) return nil } func (b *lvlBatch) Write() error { if err := b.db.Write(b.batch, nil); err != nil { return err } b.valueSize = 0 return nil } func (b *lvlBatch) ValueSize() int { return b.valueSize } type iterator interface { Next() bool Key() []byte Value() []byte Release() Error() error } // blockChainTxIndexer is the interface to check the loop up entry for transaction. // Implemented by core.BlockChain type blockChainTxIndexer interface { ReadTxLookupEntry(txID common.Hash) (common.Hash, uint64, uint64) }