diff --git a/main.go b/main.go index 3ee8760d..52363409 100644 --- a/main.go +++ b/main.go @@ -1,7 +1,7 @@ package main import ( - "bytes" + "encoding/binary" "fmt" "io" "os" @@ -30,25 +30,23 @@ var ( CodeKey = types.NewKVStoreKey("code") ) -// This is what stored in the lookupDb -type LookupValue struct { +type CommitHashPreimage struct { VersionId int64 + Prefix []byte } // Implementation of eth_state.Database type OurDatabase struct { stateStore store.CommitMultiStore // For the history of accounts // Also, for the history of contract data (effects of SSTORE instruction) - lookupDb dbm.DB // Maping [trie_root_hash] => . - // This mapping exists so that we can implement OpenTrie and OpenStorageTrie functions - // of the state.Database interface + accountsCache store.CacheKVStore + storageCache store.CacheKVStore codeDb dbm.DB // Mapping [codeHash] -> - addrPreimageDb dbm.DB // Mapping [contract_address_hash] -> cdc *amino.Codec // Amino codec to encode the values forthe lookupDb tracing bool } -func OurNewDatabase(stateDb, lookupDb, addrPreimageDb, codeDb dbm.DB) (*OurDatabase, error) { +func OurNewDatabase(stateDb, codeDb dbm.DB) (*OurDatabase, error) { od := &OurDatabase{} od.stateStore = store.NewCommitMultiStore(stateDb) od.stateStore.MountStoreWithDB(AccountsKey, types.StoreTypeIAVL, nil) @@ -56,57 +54,45 @@ func OurNewDatabase(stateDb, lookupDb, addrPreimageDb, codeDb dbm.DB) (*OurDatab if err := od.stateStore.LoadLatestVersion(); err != nil { return nil, err } - od.lookupDb = lookupDb - od.addrPreimageDb = addrPreimageDb od.codeDb = codeDb od.cdc = amino.NewCodec() return od, nil } +// root is not interpreted as a hash, but as an encoding of version func (od *OurDatabase) OpenTrie(root eth_common.Hash) (eth_state.Trie, error) { // Look up version id to use hasData := root != (eth_common.Hash{}) + var versionId int64 if hasData { - val := od.lookupDb.Get(root[:]) - if val == nil { - return nil, fmt.Errorf("Could not find version with root hash %x", root[:]) - } - var versionId int64 - _, err := od.cdc.UnmarshalBinaryReader(bytes.NewBuffer(val), &versionId, 0) - if err != nil { - return nil, err - } - if od.tracing { - fmt.Printf("Loading version %d\n", versionId) - } + // First 8 bytes encode version + versionId = int64(binary.BigEndian.Uint64(root[:8])) if err := od.stateStore.LoadVersion(versionId); err != nil { return nil, err } } - st := od.stateStore.GetCommitKVStore(AccountsKey) - return &OurTrie{od: od, st: st, prefix: nil, hasData: hasData}, nil + if od.accountsCache == nil { + od.accountsCache = store.NewCacheKVStore(od.stateStore.GetCommitKVStore(AccountsKey)) + } + return &OurTrie{od: od, versionId: versionId, st: od.accountsCache, prefix: nil, hasData: hasData}, nil } func (od *OurDatabase) OpenStorageTrie(addrHash, root eth_common.Hash) (eth_state.Trie, error) { hasData := root != (eth_common.Hash{}) + var versionId int64 if hasData { - val := od.lookupDb.Get(root[:]) - if val == nil { - return nil, fmt.Errorf("Could not find version with root hash %x", root[:]) - } - var versionId int64 - _, err := od.cdc.UnmarshalBinaryReader(bytes.NewBuffer(val), &versionId, 0) - if err != nil { - return nil, err - } + // First 8 bytes encode version + versionId = int64(binary.BigEndian.Uint64(root[:8])) // This might not be required, // we just need to check that accounts and storage are consistent if err := od.stateStore.LoadVersion(versionId); err != nil { return nil, err } } - st := od.stateStore.GetCommitKVStore(StorageKey) - return &OurTrie{od:od, st: st, prefix: addrHash[:], hasData: hasData}, nil + if od.storageCache == nil { + od.storageCache = store.NewCacheKVStore(od.stateStore.GetCommitKVStore(StorageKey)) + } + return &OurTrie{od:od, versionId: versionId, st: od.storageCache, prefix: addrHash[:], hasData: hasData}, nil } func (od *OurDatabase) CopyTrie(eth_state.Trie) eth_state.Trie { @@ -130,8 +116,9 @@ func (od *OurDatabase) TrieDB() *eth_trie.Database { // Implementation of eth_state.Trie type OurTrie struct { od *OurDatabase + versionId int64 // This is essentially part of the KVStore for a specific prefix - st store.CommitKVStore + st store.KVStore prefix []byte hasData bool } @@ -173,16 +160,18 @@ func (ot *OurTrie) Commit(onleaf eth_trie.LeafCallback) (eth_common.Hash, error) if !ot.hasData { return eth_common.Hash{}, nil } - commitId := ot.st.Commit() - fmt.Printf("Committed version %d\n", commitId.Version) - var hash eth_common.Hash - copy(hash[:], commitId.Hash) - b, err := ot.od.cdc.MarshalBinary(commitId.Version) - if err != nil { - return hash, err + var commitHash eth_common.Hash + // We assume here that the next committed version will be ot.versionId+1 + binary.BigEndian.PutUint64(commitHash[:8], uint64(ot.versionId+1)) + if ot.od.accountsCache != nil { + ot.od.accountsCache.Write() + ot.od.accountsCache = nil } - ot.od.lookupDb.Set(hash[:], b) - return hash, nil + if ot.od.storageCache != nil { + ot.od.storageCache.Write() + ot.od.storageCache = nil + } + return commitHash, nil } func (ot *OurTrie) Hash() eth_common.Hash { @@ -204,10 +193,8 @@ func (ot *OurTrie) Prove(key []byte, fromLevel uint, proofDb eth_ethdb.Putter) e func main() { fmt.Printf("Instantiating state.Database\n") stateDb := dbm.NewDB("state" /* name */, dbm.MemDBBackend, "" /* dir */) - lookupDb := dbm.NewDB("lookup" /* name */, dbm.MemDBBackend, "" /* dir */) - addrPreimageDb := dbm.NewDB("addrPreimage" /* name */, dbm.MemDBBackend, "" /* dir */) codeDb := dbm.NewDB("code" /* name */, dbm.MemDBBackend, "" /* dir */) - d, err := OurNewDatabase(stateDb, lookupDb, addrPreimageDb, codeDb) + d, err := OurNewDatabase(stateDb, codeDb) if err != nil { panic(err) } @@ -234,6 +221,8 @@ func main() { if err != nil { panic(err) } + commitID := d.stateStore.Commit() + fmt.Printf("CommitID after genesis: %v\n", commitID) fmt.Printf("Genesis state root hash: %x\n", genesis_root[:]) // File with blockchain data exported from geth by using "geth expordb" command input, err := os.Open("/Users/alexeyakhunov/mygit/blockchain") @@ -269,14 +258,17 @@ func main() { // Apply mining rewards to the statedb accumulateRewards(chainConfig, statedb, header, block.Uncles()) // Commit block - prev_root, err := statedb.Commit(chainConfig.IsEIP158(block.Number()) /* deleteEmptyObjects */) + prev_root, err = statedb.Commit(chainConfig.IsEIP158(block.Number()) /* deleteEmptyObjects */) if err != nil { panic(fmt.Errorf("at block %d: %v", n, err)) } + //fmt.Printf("State root after block %d: %x\n", block.NumberU64(), prev_root) + d.stateStore.Commit() + //fmt.Printf("CommitID after block %d: %v\n", block.NumberU64(), commitID) switch block.NumberU64() { - case 1: + case 500: root500 = prev_root - case 502: + case 501: root501 = prev_root } n++ @@ -290,6 +282,7 @@ func main() { fmt.Printf("Balance of one of the genesis investors: %s\n", genesis_state.GetBalance(eth_common.HexToAddress("0x756F45E3FA69347A9A973A725E3C98bC4db0b5a0"))) miner501 := eth_common.HexToAddress("0x35e8e5dC5FBd97c5b421A80B596C030a2Be2A04D") // Miner of the block 501 // Try to create a new statedb from root of the block 500 + fmt.Printf("root500: %x\n", root500[:]) state500, err := eth_state.New(root500, d) if err != nil { panic(err)