github.com/aergoio/aergo@v1.3.1/state/chainstatedb.go (about) 1 package state 2 3 import ( 4 "fmt" 5 "math/big" 6 "sync" 7 8 "github.com/aergoio/aergo-lib/db" 9 "github.com/aergoio/aergo/internal/common" 10 "github.com/aergoio/aergo/internal/enc" 11 "github.com/aergoio/aergo/types" 12 ) 13 14 // ChainStateDB manages statedb and additional informations about blocks like a state root hash 15 type ChainStateDB struct { 16 sync.RWMutex 17 states *StateDB 18 store db.DB 19 testmode bool 20 } 21 22 // NewChainStateDB creates instance of ChainStateDB 23 func NewChainStateDB() *ChainStateDB { 24 return &ChainStateDB{} 25 } 26 27 // Init initialize database and load statedb of latest block 28 func (sdb *ChainStateDB) Clone() *ChainStateDB { 29 sdb.Lock() 30 defer sdb.Unlock() 31 32 newSdb := &ChainStateDB{ 33 store: sdb.store, 34 states: sdb.GetStateDB().Clone(), 35 } 36 return newSdb 37 } 38 39 // Init initialize database and load statedb of latest block 40 func (sdb *ChainStateDB) Init(dbType string, dataDir string, bestBlock *types.Block, test bool) error { 41 sdb.Lock() 42 defer sdb.Unlock() 43 44 sdb.testmode = test 45 // init db 46 if sdb.store == nil { 47 dbPath := common.PathMkdirAll(dataDir, stateName) 48 sdb.store = db.NewDB(db.ImplType(dbType), dbPath) 49 } 50 51 // init trie 52 if sdb.states == nil { 53 var sroot []byte 54 if bestBlock != nil { 55 sroot = bestBlock.GetHeader().GetBlocksRootHash() 56 } 57 58 sdb.states = NewStateDB(sdb.store, sroot, sdb.testmode) 59 } 60 return nil 61 } 62 63 // Close saves latest block information of the chain 64 func (sdb *ChainStateDB) Close() error { 65 sdb.Lock() 66 defer sdb.Unlock() 67 68 // close db 69 if sdb.store != nil { 70 sdb.store.Close() 71 } 72 return nil 73 } 74 75 // GetStateDB returns statedb stores account states 76 func (sdb *ChainStateDB) GetStateDB() *StateDB { 77 return sdb.states 78 } 79 80 // GetSystemAccountState returns the state of the aergo system account. 81 func (sdb *ChainStateDB) GetSystemAccountState() (*ContractState, error) { 82 return sdb.GetStateDB().GetSystemAccountState() 83 } 84 85 // OpenNewStateDB returns new instance of statedb given state root hash 86 func (sdb *ChainStateDB) OpenNewStateDB(root []byte) *StateDB { 87 return NewStateDB(sdb.store, root, sdb.testmode) 88 } 89 90 func (sdb *ChainStateDB) SetGenesis(genesis *types.Genesis, bpInit func(*StateDB, *types.Genesis) error) error { 91 block := genesis.Block() 92 stateDB := sdb.OpenNewStateDB(sdb.GetRoot()) 93 94 // create state of genesis block 95 gbState := sdb.NewBlockState(stateDB.GetRoot()) 96 97 if len(genesis.BPs) > 0 && bpInit != nil { 98 // To avoid cyclic dedendency, BP initilization is called via function 99 // pointer. 100 if err := bpInit(stateDB, genesis); err != nil { 101 return err 102 } 103 104 aid := types.ToAccountID([]byte(types.AergoSystem)) 105 scs, err := stateDB.OpenContractStateAccount(aid) 106 if err != nil { 107 return err 108 } 109 110 if err := gbState.PutState(aid, scs.State); err != nil { 111 return err 112 } 113 } 114 115 for address, balance := range genesis.Balance { 116 bytes := types.ToAddress(address) 117 id := types.ToAccountID(bytes) 118 if v, ok := new(big.Int).SetString(balance, 10); ok { 119 if err := gbState.PutState(id, &types.State{Balance: v.Bytes()}); err != nil { 120 return err 121 } 122 genesis.AddBalance(v) 123 } else { 124 return fmt.Errorf("balance conversion failed for %s (address: %s)", balance, address) 125 } 126 } 127 128 // save state of genesis block 129 // FIXME don't use chainstate API 130 if err := sdb.Apply(gbState); err != nil { 131 return err 132 } 133 134 block.SetBlocksRootHash(sdb.GetRoot()) 135 136 return nil 137 } 138 139 // Apply specific blockstate to statedb of main chain 140 func (sdb *ChainStateDB) Apply(bstate *BlockState) error { 141 sdb.Lock() 142 defer sdb.Unlock() 143 144 // // rollback and revert trie requires state root before apply 145 // if bstate.Undo.StateRoot == emptyHashID { 146 // bstate.Undo.StateRoot = types.ToHashID(sdb.states.trie.Root) 147 // } 148 149 // apply blockState to trie 150 if err := bstate.Update(); err != nil { 151 return err 152 } 153 if err := bstate.Commit(); err != nil { 154 return err 155 } 156 157 if err := sdb.UpdateRoot(bstate); err != nil { 158 return err 159 } 160 161 return nil 162 } 163 164 func (sdb *ChainStateDB) UpdateRoot(bstate *BlockState) error { 165 // // check state root 166 // if bstate.BlockInfo.StateRoot != types.ToHashID(bstate.GetRoot()) { 167 // // TODO: if validation failed, than revert statedb. 168 // bstate.BlockInfo.StateRoot = types.ToHashID(sdb.GetRoot()) 169 // } 170 171 logger.Debug().Str("before", enc.ToString(sdb.states.GetRoot())). 172 Str("stateRoot", enc.ToString(bstate.GetRoot())).Msg("apply block state") 173 174 if err := sdb.states.SetRoot(bstate.GetRoot()); err != nil { 175 return err 176 } 177 178 return nil 179 } 180 181 func (sdb *ChainStateDB) SetRoot(targetBlockRoot []byte) error { 182 sdb.Lock() 183 defer sdb.Unlock() 184 185 logger.Debug().Str("before", enc.ToString(sdb.states.GetRoot())). 186 Str("target", enc.ToString(targetBlockRoot)).Msg("rollback state") 187 188 sdb.states.SetRoot(targetBlockRoot) 189 return nil 190 } 191 192 // GetRoot returns state root hash 193 func (sdb *ChainStateDB) GetRoot() []byte { 194 return sdb.states.GetRoot() 195 } 196 197 func (sdb *ChainStateDB) IsExistState(hash []byte) bool { 198 //TODO : StateRootValidation 199 return false 200 } 201 202 func (sdb *ChainStateDB) NewBlockState(root []byte) *BlockState { 203 bState := NewBlockState(sdb.OpenNewStateDB(root)) 204 205 return bState 206 }