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  }