2018-06-13 09:56:01 +00:00
package main
import (
2018-06-24 21:58:28 +00:00
"encoding/binary"
2018-06-13 09:56:01 +00:00
"fmt"
2018-06-19 11:32:26 +00:00
"io"
"os"
2018-06-13 09:56:01 +00:00
eth_common "github.com/ethereum/go-ethereum/common"
2018-06-18 21:10:29 +00:00
eth_core "github.com/ethereum/go-ethereum/core"
2018-06-13 09:56:01 +00:00
eth_state "github.com/ethereum/go-ethereum/core/state"
2018-06-19 11:32:26 +00:00
eth_types "github.com/ethereum/go-ethereum/core/types"
eth_rlp "github.com/ethereum/go-ethereum/rlp"
2018-06-13 09:56:01 +00:00
eth_ethdb "github.com/ethereum/go-ethereum/ethdb"
2018-06-19 11:32:26 +00:00
eth_params "github.com/ethereum/go-ethereum/params"
2018-06-13 09:56:01 +00:00
eth_trie "github.com/ethereum/go-ethereum/trie"
dbm "github.com/tendermint/tmlibs/db"
2018-06-15 13:26:06 +00:00
"github.com/tendermint/go-amino"
2018-06-13 14:29:22 +00:00
"github.com/cosmos/cosmos-sdk/store"
2018-06-15 10:05:12 +00:00
"github.com/cosmos/cosmos-sdk/types"
)
var (
// Key for the sub-store with Ethereum accounts
2018-06-18 15:33:07 +00:00
AccountsKey = types . NewKVStoreKey ( "account" )
2018-06-15 10:05:12 +00:00
// Key for the sub-store with storage data of Ethereum contracts
StorageKey = types . NewKVStoreKey ( "storage" )
2018-06-18 15:33:07 +00:00
// Key for the sub-store with the code for contracts
CodeKey = types . NewKVStoreKey ( "code" )
2018-06-13 09:56:01 +00:00
)
2018-06-24 21:58:28 +00:00
type CommitHashPreimage struct {
2018-06-15 13:26:06 +00:00
VersionId int64
2018-06-24 21:58:28 +00:00
Prefix [ ] byte
2018-06-15 13:26:06 +00:00
}
2018-06-13 09:56:01 +00:00
// Implementation of eth_state.Database
type OurDatabase struct {
2018-06-15 10:05:12 +00:00
stateStore store . CommitMultiStore // For the history of accounts <balance, nonce, storage root hash, code hash>
// Also, for the history of contract data (effects of SSTORE instruction)
2018-06-24 21:58:28 +00:00
accountsCache store . CacheKVStore
storageCache store . CacheKVStore
2018-06-18 15:33:07 +00:00
codeDb dbm . DB // Mapping [codeHash] -> <code>
2018-06-15 13:26:06 +00:00
cdc * amino . Codec // Amino codec to encode the values forthe lookupDb
2018-06-20 04:49:52 +00:00
tracing bool
2018-06-13 09:56:01 +00:00
}
2018-06-24 21:58:28 +00:00
func OurNewDatabase ( stateDb , codeDb dbm . DB ) ( * OurDatabase , error ) {
2018-06-15 10:05:12 +00:00
od := & OurDatabase { }
od . stateStore = store . NewCommitMultiStore ( stateDb )
od . stateStore . MountStoreWithDB ( AccountsKey , types . StoreTypeIAVL , nil )
od . stateStore . MountStoreWithDB ( StorageKey , types . StoreTypeIAVL , nil )
2018-06-18 15:33:07 +00:00
if err := od . stateStore . LoadLatestVersion ( ) ; err != nil {
return nil , err
}
od . codeDb = codeDb
2018-06-15 13:26:06 +00:00
od . cdc = amino . NewCodec ( )
2018-06-18 15:33:07 +00:00
return od , nil
2018-06-13 09:56:01 +00:00
}
2018-06-24 21:58:28 +00:00
// root is not interpreted as a hash, but as an encoding of version
2018-06-13 09:56:01 +00:00
func ( od * OurDatabase ) OpenTrie ( root eth_common . Hash ) ( eth_state . Trie , error ) {
2018-06-15 13:26:06 +00:00
// Look up version id to use
2018-06-20 04:49:52 +00:00
hasData := root != ( eth_common . Hash { } )
2018-06-24 21:58:28 +00:00
var versionId int64
2018-06-20 04:49:52 +00:00
if hasData {
2018-06-24 21:58:28 +00:00
// First 8 bytes encode version
versionId = int64 ( binary . BigEndian . Uint64 ( root [ : 8 ] ) )
2018-06-20 04:49:52 +00:00
if err := od . stateStore . LoadVersion ( versionId ) ; err != nil {
return nil , err
}
2018-06-15 13:26:06 +00:00
}
2018-06-24 21:58:28 +00:00
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
2018-06-13 09:56:01 +00:00
}
func ( od * OurDatabase ) OpenStorageTrie ( addrHash , root eth_common . Hash ) ( eth_state . Trie , error ) {
2018-06-20 04:49:52 +00:00
hasData := root != ( eth_common . Hash { } )
2018-06-24 21:58:28 +00:00
var versionId int64
2018-06-20 04:49:52 +00:00
if hasData {
2018-06-24 21:58:28 +00:00
// First 8 bytes encode version
versionId = int64 ( binary . BigEndian . Uint64 ( root [ : 8 ] ) )
2018-06-20 04:49:52 +00:00
// 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
}
2018-06-15 13:26:06 +00:00
}
2018-06-24 21:58:28 +00:00
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
2018-06-13 09:56:01 +00:00
}
func ( od * OurDatabase ) CopyTrie ( eth_state . Trie ) eth_state . Trie {
return nil
}
func ( od * OurDatabase ) ContractCode ( addrHash , codeHash eth_common . Hash ) ( [ ] byte , error ) {
2018-06-18 15:33:07 +00:00
code := od . codeDb . Get ( codeHash [ : ] )
return code , nil
2018-06-13 09:56:01 +00:00
}
func ( od * OurDatabase ) ContractCodeSize ( addrHash , codeHash eth_common . Hash ) ( int , error ) {
2018-06-18 15:33:07 +00:00
code := od . codeDb . Get ( codeHash [ : ] )
return len ( code ) , nil
2018-06-13 09:56:01 +00:00
}
func ( od * OurDatabase ) TrieDB ( ) * eth_trie . Database {
return nil
}
// Implementation of eth_state.Trie
type OurTrie struct {
2018-06-15 21:32:35 +00:00
od * OurDatabase
2018-06-24 21:58:28 +00:00
versionId int64
2018-06-15 10:05:12 +00:00
// This is essentially part of the KVStore for a specific prefix
2018-06-24 21:58:28 +00:00
st store . KVStore
2018-06-15 10:05:12 +00:00
prefix [ ] byte
2018-06-20 04:49:52 +00:00
hasData bool
2018-06-13 09:56:01 +00:00
}
2018-06-15 20:51:55 +00:00
func ( ot * OurTrie ) makePrefix ( key [ ] byte ) [ ] byte {
kk := make ( [ ] byte , len ( ot . prefix ) + len ( key ) )
copy ( kk , ot . prefix )
copy ( kk [ len ( ot . prefix ) : ] , key )
return kk
}
2018-06-13 09:56:01 +00:00
func ( ot * OurTrie ) TryGet ( key [ ] byte ) ( [ ] byte , error ) {
2018-06-15 20:51:55 +00:00
if ot . prefix == nil {
return ot . st . Get ( key ) , nil
}
return ot . st . Get ( ot . makePrefix ( key ) ) , nil
2018-06-13 09:56:01 +00:00
}
func ( ot * OurTrie ) TryUpdate ( key , value [ ] byte ) error {
2018-06-20 04:49:52 +00:00
ot . hasData = true
2018-06-15 20:51:55 +00:00
if ot . prefix == nil {
ot . st . Set ( key , value )
return nil
}
ot . st . Set ( ot . makePrefix ( key ) , value )
2018-06-13 09:56:01 +00:00
return nil
}
func ( ot * OurTrie ) TryDelete ( key [ ] byte ) error {
2018-06-15 20:51:55 +00:00
if ot . prefix == nil {
ot . st . Delete ( key )
return nil
}
ot . st . Delete ( ot . makePrefix ( key ) )
2018-06-13 09:56:01 +00:00
return nil
}
func ( ot * OurTrie ) Commit ( onleaf eth_trie . LeafCallback ) ( eth_common . Hash , error ) {
2018-06-20 04:49:52 +00:00
if ! ot . hasData {
return eth_common . Hash { } , nil
}
2018-06-24 21:58:28 +00:00
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
}
if ot . od . storageCache != nil {
ot . od . storageCache . Write ( )
ot . od . storageCache = nil
2018-06-15 21:32:35 +00:00
}
2018-06-24 21:58:28 +00:00
return commitHash , nil
2018-06-13 09:56:01 +00:00
}
func ( ot * OurTrie ) Hash ( ) eth_common . Hash {
return eth_common . Hash { }
}
func ( ot * OurTrie ) NodeIterator ( startKey [ ] byte ) eth_trie . NodeIterator {
return nil
}
func ( ot * OurTrie ) GetKey ( [ ] byte ) [ ] byte {
return nil
}
func ( ot * OurTrie ) Prove ( key [ ] byte , fromLevel uint , proofDb eth_ethdb . Putter ) error {
return nil
}
func main ( ) {
fmt . Printf ( "Instantiating state.Database\n" )
2018-06-15 10:05:12 +00:00
stateDb := dbm . NewDB ( "state" /* name */ , dbm . MemDBBackend , "" /* dir */ )
2018-06-18 15:33:07 +00:00
codeDb := dbm . NewDB ( "code" /* name */ , dbm . MemDBBackend , "" /* dir */ )
2018-06-24 21:58:28 +00:00
d , err := OurNewDatabase ( stateDb , codeDb )
2018-06-18 15:33:07 +00:00
if err != nil {
panic ( err )
}
fmt . Printf ( "Instantiating state.StateDB\n" )
// With empty root hash, i.e. empty state
statedb , err := eth_state . New ( eth_common . Hash { } , d )
if err != nil {
panic ( err )
}
2018-06-18 21:10:29 +00:00
g := eth_core . DefaultGenesisBlock ( )
for addr , account := range g . Alloc {
statedb . AddBalance ( addr , account . Balance )
statedb . SetCode ( addr , account . Code )
statedb . SetNonce ( addr , account . Nonce )
for key , value := range account . Storage {
statedb . SetState ( addr , key , value )
}
}
// One of the genesis account having 200 ETH
b := statedb . GetBalance ( eth_common . HexToAddress ( "0x756F45E3FA69347A9A973A725E3C98bC4db0b5a0" ) )
2018-06-18 15:33:07 +00:00
fmt . Printf ( "Balance: %s\n" , b )
2018-06-19 11:32:26 +00:00
genesis_root , err := statedb . Commit ( false /* deleteEmptyObjects */ )
2018-06-18 21:10:29 +00:00
if err != nil {
panic ( err )
}
2018-06-24 21:58:28 +00:00
commitID := d . stateStore . Commit ( )
fmt . Printf ( "CommitID after genesis: %v\n" , commitID )
2018-06-19 11:32:26 +00:00
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" )
2018-06-18 21:10:29 +00:00
if err != nil {
panic ( err )
}
2018-06-19 11:32:26 +00:00
defer input . Close ( )
// Ethereum mainnet config
chainConfig := eth_params . MainnetChainConfig
stream := eth_rlp . NewStream ( input , 0 )
var block eth_types . Block
n := 0
var root500 eth_common . Hash // Root hash after block 500
var root501 eth_common . Hash // Root hash after block 501
2018-06-20 04:49:52 +00:00
prev_root := genesis_root
d . tracing = true
2018-06-19 11:32:26 +00:00
for {
if err = stream . Decode ( & block ) ; err == io . EOF {
err = nil // Clear it
break
} else if err != nil {
panic ( fmt . Errorf ( "at block %d: %v" , n , err ) )
}
// don't import first block
if block . NumberU64 ( ) == 0 {
continue
}
header := block . Header ( )
2018-06-20 04:49:52 +00:00
statedb , err = eth_state . New ( prev_root , d )
if err != nil {
panic ( fmt . Errorf ( "at block %d: %v" , n , err ) )
}
2018-06-19 11:32:26 +00:00
// Apply mining rewards to the statedb
accumulateRewards ( chainConfig , statedb , header , block . Uncles ( ) )
// Commit block
2018-06-24 21:58:28 +00:00
prev_root , err = statedb . Commit ( chainConfig . IsEIP158 ( block . Number ( ) ) /* deleteEmptyObjects */ )
2018-06-19 11:32:26 +00:00
if err != nil {
2018-06-20 04:49:52 +00:00
panic ( fmt . Errorf ( "at block %d: %v" , n , err ) )
2018-06-19 11:32:26 +00:00
}
2018-06-24 21:58:28 +00:00
//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)
2018-06-20 04:49:52 +00:00
switch block . NumberU64 ( ) {
2018-06-24 21:58:28 +00:00
case 500 :
2018-06-20 04:49:52 +00:00
root500 = prev_root
2018-06-24 21:58:28 +00:00
case 501 :
2018-06-20 04:49:52 +00:00
root501 = prev_root
2018-06-19 11:32:26 +00:00
}
n ++
if n >= 1000 {
break
}
}
fmt . Printf ( "Processed %d blocks\n" , n )
2018-06-20 04:49:52 +00:00
d . tracing = true
2018-06-19 11:32:26 +00:00
genesis_state , err := eth_state . New ( genesis_root , d )
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
2018-06-24 21:58:28 +00:00
fmt . Printf ( "root500: %x\n" , root500 [ : ] )
2018-06-19 11:32:26 +00:00
state500 , err := eth_state . New ( root500 , d )
if err != nil {
panic ( err )
}
miner501_balance_at_500 := state500 . GetBalance ( miner501 )
state501 , err := eth_state . New ( root501 , d )
if err != nil {
panic ( err )
}
miner501_balance_at_501 := state501 . GetBalance ( miner501 )
fmt . Printf ( "Miner of block 501's balance after block 500: %d\n" , miner501_balance_at_500 )
fmt . Printf ( "Miner of block 501's balance after block 501: %d\n" , miner501_balance_at_501 )
2018-06-13 09:56:01 +00:00
}