github.com/aergoio/aergo@v1.3.1/chain/chaindb.go (about)

     1  /**
     2   *  @file
     3   *  @copyright defined in aergo/LICENSE.txt
     4   */
     5  
     6  package chain
     7  
     8  import (
     9  	"bytes"
    10  	"encoding/binary"
    11  	"encoding/gob"
    12  	"encoding/json"
    13  	"errors"
    14  	"fmt"
    15  	"sync/atomic"
    16  
    17  	"github.com/aergoio/aergo-lib/db"
    18  	"github.com/aergoio/aergo/consensus"
    19  	"github.com/aergoio/aergo/internal/common"
    20  	"github.com/aergoio/aergo/internal/enc"
    21  	"github.com/aergoio/aergo/types"
    22  	"github.com/gogo/protobuf/proto"
    23  )
    24  
    25  const (
    26  	chainDBName       = "chain"
    27  	genesisKey        = chainDBName + ".genesisInfo"
    28  	genesisBalanceKey = chainDBName + ".genesisBalance"
    29  )
    30  
    31  var (
    32  	// ErrNoChainDB reports chaindb is not prepared.
    33  	ErrNoChainDB           = fmt.Errorf("chaindb not prepared")
    34  	ErrorLoadBestBlock     = errors.New("failed to load latest block from DB")
    35  	ErrCantDropGenesis     = errors.New("can't drop genesis block")
    36  	ErrTooBigResetHeight   = errors.New("reset height is too big")
    37  	ErrWalNoHardState      = errors.New("not exist hard state")
    38  	ErrInvalidHardState    = errors.New("invalid hard state")
    39  	ErrInvalidRaftSnapshot = errors.New("invalid raft snapshot")
    40  	ErrInvalidCCProgress   = errors.New("invalid conf change progress")
    41  
    42  	latestKey      = []byte(chainDBName + ".latest")
    43  	receiptsPrefix = []byte("r")
    44  
    45  	raftIdentityKey              = []byte("r_identity")
    46  	raftStateKey                 = []byte("r_state")
    47  	raftSnapKey                  = []byte("r_snap")
    48  	raftEntryLastIdxKey          = []byte("r_last")
    49  	raftEntryPrefix              = []byte("r_entry.")
    50  	raftEntryInvertPrefix        = []byte("r_inv.")
    51  	raftConfChangeProgressPrefix = []byte("r_ccstatus.")
    52  )
    53  
    54  // ErrNoBlock reports there is no such a block with id (hash or block number).
    55  type ErrNoBlock struct {
    56  	id interface{}
    57  }
    58  
    59  func (e ErrNoBlock) Error() string {
    60  	var idStr string
    61  
    62  	switch id := e.id.(type) {
    63  	case []byte:
    64  		idStr = fmt.Sprintf("blockHash=%v", enc.ToString(id))
    65  	default:
    66  		idStr = fmt.Sprintf("blockNo=%v", id)
    67  	}
    68  
    69  	return fmt.Sprintf("block not found: %s", idStr)
    70  }
    71  
    72  type ChainDB struct {
    73  	cc consensus.ChainConsensus
    74  
    75  	latest    atomic.Value //types.BlockNo
    76  	bestBlock atomic.Value // *types.Block
    77  	//	blocks []*types.Block
    78  	store db.DB
    79  }
    80  
    81  func NewChainDB() *ChainDB {
    82  	// logger.SetLevel("debug")
    83  	cdb := &ChainDB{
    84  		//blocks: []*types.Block{},
    85  	}
    86  	cdb.latest.Store(types.BlockNo(0))
    87  
    88  	return cdb
    89  }
    90  
    91  // NewTx returns a new chain DB Transaction.
    92  func (cdb *ChainDB) NewTx() db.Transaction {
    93  	return cdb.store.NewTx()
    94  }
    95  
    96  func (cdb *ChainDB) Init(dbType string, dataDir string) error {
    97  	if cdb.store == nil {
    98  		logger.Info().Str("datadir", dataDir).Msg("chain database initialized")
    99  		dbPath := common.PathMkdirAll(dataDir, chainDBName)
   100  		cdb.store = db.NewDB(db.ImplType(dbType), dbPath)
   101  	}
   102  
   103  	// load data
   104  	if err := cdb.loadChainData(); err != nil {
   105  		return err
   106  	}
   107  
   108  	// recover from reorg marker
   109  	if err := cdb.recover(); err != nil {
   110  		logger.Error().Err(err).Msg("failed to recover chain database from crash")
   111  		return err
   112  	}
   113  
   114  	// // if empty then create new genesis block
   115  	// // if cdb.getBestBlockNo() == 0 && len(cdb.blocks) == 0 {
   116  	// blockIdx := types.BlockNoToBytes(0)
   117  	// blockHash := cdb.store.Get(blockIdx)
   118  	// if cdb.getBestBlockNo() == 0 && (blockHash == nil || len(blockHash) == 0) {
   119  	// 	cdb.generateGenesisBlock(seed)
   120  	// }
   121  	return nil
   122  }
   123  
   124  func (cdb *ChainDB) recover() error {
   125  	marker, err := cdb.getReorgMarker()
   126  	if err != nil {
   127  		return err
   128  	}
   129  
   130  	if marker == nil {
   131  		return nil
   132  	}
   133  
   134  	if err := marker.RecoverChainMapping(cdb); err != nil {
   135  		return err
   136  	}
   137  
   138  	return nil
   139  }
   140  
   141  // ResetBest reset best block of chain db manually remove blocks from original
   142  // best to resetNo.
   143  //
   144  // *Caution*: This API is dangerous. It must be used for test blockchain only.
   145  func (cdb *ChainDB) ResetBest(resetNo types.BlockNo) error {
   146  	logger.Info().Uint64("reset height", resetNo).Msg("reset best block")
   147  
   148  	best := cdb.getBestBlockNo()
   149  	if best <= resetNo {
   150  		logger.Error().Uint64("best", best).Uint64("reset", resetNo).Msg("too big reset height")
   151  		return ErrTooBigResetHeight
   152  	}
   153  
   154  	for curNo := best; curNo > resetNo; curNo-- {
   155  		if err := cdb.dropBlock(curNo); err != nil {
   156  			logger.Error().Err(err).Uint64("no", curNo).Msg("failed to drop block")
   157  			return err
   158  		}
   159  	}
   160  
   161  	logger.Info().Msg("succeed to reset best block")
   162  
   163  	return nil
   164  }
   165  
   166  type ErrDropBlock struct {
   167  	pos int
   168  }
   169  
   170  func (err *ErrDropBlock) Error() string {
   171  	return fmt.Sprintf("failed to drop block: pos=%d", err.pos)
   172  }
   173  
   174  func (cdb *ChainDB) checkBlockDropped(dropBlock *types.Block) error {
   175  	no := dropBlock.GetHeader().GetBlockNo()
   176  	hash := dropBlock.GetHash()
   177  	txLen := len(dropBlock.GetBody().GetTxs())
   178  
   179  	//check receipt
   180  	var err error
   181  
   182  	if txLen > 0 {
   183  		if _, err = cdb.getReceipt(hash, no, 0); err == nil {
   184  			return &ErrDropBlock{pos: 0}
   185  		}
   186  		if _, err = cdb.getReceipt(hash, no, int32(txLen-1)); err == nil {
   187  			return &ErrDropBlock{pos: 1}
   188  		}
   189  	}
   190  
   191  	//check tx
   192  	for _, tx := range dropBlock.GetBody().GetTxs() {
   193  		if _, _, err = cdb.getTx(tx.GetHash()); err == nil {
   194  			return &ErrDropBlock{pos: 2}
   195  		}
   196  	}
   197  
   198  	//check hash/block
   199  	if _, err = cdb.getBlock(hash); err == nil {
   200  		return &ErrDropBlock{pos: 3}
   201  	}
   202  
   203  	//check no/hash
   204  	if _, err = cdb.getHashByNo(no); err == nil {
   205  		return &ErrDropBlock{pos: 4}
   206  	}
   207  
   208  	return nil
   209  }
   210  
   211  func (cdb *ChainDB) Close() {
   212  	if cdb.store != nil {
   213  		cdb.store.Close()
   214  	}
   215  	return
   216  }
   217  
   218  // Get returns the value corresponding to key from the chain DB.
   219  func (cdb *ChainDB) Get(key []byte) []byte {
   220  	return cdb.store.Get(key)
   221  }
   222  
   223  func (cdb *ChainDB) GetBestBlock() (*types.Block, error) {
   224  	//logger.Debug().Uint64("blockno", blockNo).Msg("get best block")
   225  	var block *types.Block
   226  
   227  	aopv := cdb.bestBlock.Load()
   228  
   229  	if aopv != nil {
   230  		block = aopv.(*types.Block)
   231  	}
   232  
   233  	return block, nil
   234  }
   235  
   236  func (cdb *ChainDB) loadChainData() error {
   237  	latestBytes := cdb.store.Get(latestKey)
   238  	if latestBytes == nil || len(latestBytes) == 0 {
   239  		return nil
   240  	}
   241  	latestNo := types.BlockNoFromBytes(latestBytes)
   242  	/* TODO: just checking DB
   243  	cdb.blocks = make([]*types.Block, latestNo+1)
   244  	for i := uint32(0); i <= latestNo; i++ {
   245  		blockIdx := types.BlockNoToBytes(i)
   246  		buf := types.Block{}
   247  		err := cdb.loadData(blockIdx, &buf)
   248  		if err != nil {
   249  			return err
   250  		}
   251  		bHash := buf.CalculateBlockHash()
   252  		if buf.Hash == nil {
   253  			buf.Hash = bHash
   254  		} else if !bytes.Equal(buf.Hash, bHash) {
   255  			return fmt.Errorf("invalid Block Hash: hash=%s, check=%s",
   256  				enc.ToString(buf.Hash), enc.ToString(bHash))
   257  		}
   258  		for _, v := range buf.Body.Txs {
   259  			tHash := v.CalculateTxHash()
   260  			if v.Hash == nil {
   261  				v.Hash = tHash
   262  			} else if !bytes.Equal(v.Hash, tHash) {
   263  				return fmt.Errorf("invalid Transaction Hash: hash=%s, check=%s",
   264  					enc.ToString(v.Hash), enc.ToString(tHash))
   265  			}
   266  		}
   267  		cdb.blocks[i] = &buf
   268  	}
   269  	*/
   270  	latestBlock, err := cdb.GetBlockByNo(latestNo)
   271  	if err != nil {
   272  		return ErrorLoadBestBlock
   273  	}
   274  	cdb.setLatest(latestBlock)
   275  
   276  	// skips := true
   277  	// for i, _ := range cdb.blocks {
   278  	// 	if i > 3 && i+3 <= cdb.getBestBlockNo() {
   279  	// 		if skips {
   280  	// 			skips = false
   281  	// 			//logger.Info("  ...")
   282  	// 		}
   283  	// 		continue
   284  	// 	}
   285  	// 	//logger.Info("- loaded:", i, ToJSON(v))
   286  	// }
   287  	return nil
   288  }
   289  
   290  func (cdb *ChainDB) loadData(key []byte, pb proto.Message) error {
   291  	buf := cdb.store.Get(key)
   292  	if buf == nil || len(buf) == 0 {
   293  		return fmt.Errorf("failed to load data: key=%v", key)
   294  	}
   295  	//logger.Debugf("  loadData: key=%d, len=%d, val=%s\n", Btoi(key), len(buf), enc.ToString(buf))
   296  	err := proto.Unmarshal(buf, pb)
   297  	if err != nil {
   298  		return fmt.Errorf("failed to unmarshal: key=%v, len=%d", key, len(buf))
   299  	}
   300  	//logger.Debug("  loaded: ", ToJSON(pb))
   301  	return nil
   302  }
   303  
   304  func (cdb *ChainDB) addGenesisBlock(genesis *types.Genesis) error {
   305  	block := genesis.Block()
   306  
   307  	tx := cdb.store.NewTx()
   308  
   309  	if len(block.Hash) == 0 {
   310  		block.BlockID()
   311  	}
   312  
   313  	cdb.connectToChain(tx, block, false)
   314  	tx.Set([]byte(genesisKey), genesis.Bytes())
   315  	if totalBalance := genesis.TotalBalance(); totalBalance != nil {
   316  		tx.Set([]byte(genesisBalanceKey), totalBalance.Bytes())
   317  	}
   318  
   319  	tx.Commit()
   320  
   321  	logger.Info().Str("chain id", genesis.ID.ToJSON()).Str("hash", block.ID()).Msg("Genesis Block Added")
   322  
   323  	//logger.Info().Str("chain id", genesis.ID.ToJSON()).Str("chain id (raw)",
   324  	//	enc.ToString(block.GetHeader().GetChainID())).Msg("Genesis Block Added")
   325  	return nil
   326  }
   327  
   328  // GetGenesisInfo returns Genesis info, which is read from cdb.
   329  func (cdb *ChainDB) GetGenesisInfo() *types.Genesis {
   330  	if b := cdb.Get([]byte(genesisKey)); len(b) != 0 {
   331  		genesis := types.GetGenesisFromBytes(b)
   332  		if block, err := cdb.GetBlockByNo(0); err == nil {
   333  			genesis.SetBlock(block)
   334  
   335  			// genesis.ID is overwritten by the genesis block's chain
   336  			// id. Prefer the latter since it is sort of protected the block
   337  			// chain system (all the chaild blocks connected to the genesis
   338  			// block).
   339  			rawCid := genesis.Block().GetHeader().GetChainID()
   340  			if len(rawCid) > 0 {
   341  				cid := types.NewChainID()
   342  				if err := cid.Read(rawCid); err == nil {
   343  					genesis.ID = *cid
   344  				}
   345  			}
   346  
   347  		}
   348  
   349  		if v := cdb.Get([]byte(genesisBalanceKey)); len(v) != 0 {
   350  			genesis.SetTotalBalance(v)
   351  		}
   352  
   353  		return genesis
   354  	}
   355  	return nil
   356  }
   357  
   358  func (cdb *ChainDB) setLatest(newBestBlock *types.Block) (oldLatest types.BlockNo) {
   359  	oldLatest = cdb.getBestBlockNo()
   360  
   361  	newLatest := types.BlockNo(newBestBlock.GetHeader().GetBlockNo())
   362  	cdb.latest.Store(newLatest)
   363  	cdb.bestBlock.Store(newBestBlock)
   364  
   365  	logger.Debug().Uint64("old", oldLatest).Uint64("new", newLatest).Msg("update latest block")
   366  
   367  	return
   368  }
   369  
   370  func (cdb *ChainDB) connectToChain(dbtx db.Transaction, block *types.Block, skipAdd bool) (oldLatest types.BlockNo) {
   371  	blockNo := block.GetHeader().GetBlockNo()
   372  	blockIdx := types.BlockNoToBytes(blockNo)
   373  
   374  	if !skipAdd {
   375  		if err := cdb.addBlock(dbtx, block); err != nil {
   376  			return 0
   377  		}
   378  	}
   379  
   380  	// Update best block hash
   381  	dbtx.Set(latestKey, blockIdx)
   382  	dbtx.Set(blockIdx, block.BlockHash())
   383  
   384  	// Save the last consensus status.
   385  	if cdb.cc != nil {
   386  		if err := cdb.cc.Save(dbtx); err != nil {
   387  			logger.Error().Err(err).Msg("failed to save DPoS status")
   388  		}
   389  	}
   390  
   391  	oldLatest = cdb.setLatest(block)
   392  
   393  	logger.Debug().Msg("connected block to mainchain")
   394  
   395  	return
   396  }
   397  
   398  func (cdb *ChainDB) swapChainMapping(newBlocks []*types.Block) error {
   399  	oldNo := cdb.getBestBlockNo()
   400  	newNo := newBlocks[0].GetHeader().GetBlockNo()
   401  
   402  	if oldNo >= newNo {
   403  		logger.Error().Uint64("old", oldNo).Uint64("new", newNo).
   404  			Msg("New chain is not longger than old chain")
   405  		return ErrInvalidSwapChain
   406  	}
   407  
   408  	var blockIdx []byte
   409  
   410  	bulk := cdb.store.NewBulk()
   411  	defer bulk.DiscardLast()
   412  
   413  	//make newTx because of batchsize limit of DB
   414  	for i := len(newBlocks) - 1; i >= 0; i-- {
   415  		block := newBlocks[i]
   416  		blockIdx = types.BlockNoToBytes(block.GetHeader().GetBlockNo())
   417  
   418  		bulk.Set(blockIdx, block.BlockHash())
   419  	}
   420  
   421  	bulk.Set(latestKey, blockIdx)
   422  
   423  	// Save the last consensus status.
   424  	cdb.cc.Save(bulk)
   425  
   426  	bulk.Flush()
   427  
   428  	cdb.setLatest(newBlocks[0])
   429  
   430  	return nil
   431  }
   432  
   433  func (cdb *ChainDB) isMainChain(block *types.Block) (bool, error) {
   434  	blockNo := block.GetHeader().GetBlockNo()
   435  	bestNo := cdb.getBestBlockNo()
   436  	if blockNo > 0 && blockNo != bestNo+1 {
   437  		logger.Debug().Uint64("no", blockNo).Uint64("latest", bestNo).Msg("block is branch")
   438  
   439  		return false, nil
   440  	}
   441  
   442  	prevHash := block.GetHeader().GetPrevBlockHash()
   443  	latestHash, err := cdb.getHashByNo(cdb.getBestBlockNo())
   444  	if err != nil { //need assertion
   445  		return false, fmt.Errorf("failed to getting block hash by no(%v)", cdb.getBestBlockNo())
   446  	}
   447  
   448  	isMainChain := bytes.Equal(prevHash, latestHash)
   449  
   450  	logger.Debug().Bool("isMainChain", isMainChain).Msg("check if block is in main chain")
   451  
   452  	return isMainChain, nil
   453  }
   454  
   455  type txInfo struct {
   456  	blockHash []byte
   457  	idx       int
   458  }
   459  
   460  func (cdb *ChainDB) addTxsOfBlock(dbTx *db.Transaction, txs []*types.Tx, blockHash []byte) error {
   461  	if err := TestDebugger.Check(DEBUG_CHAIN_STOP, 4, nil); err != nil {
   462  		return err
   463  	}
   464  
   465  	for i, txEntry := range txs {
   466  		if err := cdb.addTx(dbTx, txEntry, blockHash, i); err != nil {
   467  			logger.Error().Err(err).Str("hash", enc.ToString(blockHash)).Int("txidx", i).
   468  				Msg("failed to add tx")
   469  
   470  			return err
   471  		}
   472  	}
   473  
   474  	return nil
   475  }
   476  
   477  // stor tx info to DB
   478  func (cdb *ChainDB) addTx(dbtx *db.Transaction, tx *types.Tx, blockHash []byte, idx int) error {
   479  	txidx := types.TxIdx{
   480  		BlockHash: blockHash,
   481  		Idx:       int32(idx),
   482  	}
   483  	txidxbytes, err := proto.Marshal(&txidx)
   484  	if err != nil {
   485  		return err
   486  	}
   487  	(*dbtx).Set(tx.Hash, txidxbytes)
   488  	return nil
   489  }
   490  
   491  func (cdb *ChainDB) deleteTx(dbtx *db.Transaction, tx *types.Tx) {
   492  	(*dbtx).Delete(tx.Hash)
   493  }
   494  
   495  // store block info to DB
   496  func (cdb *ChainDB) addBlock(dbtx db.Transaction, block *types.Block) error {
   497  	blockNo := block.GetHeader().GetBlockNo()
   498  
   499  	// TODO: Is it possible?
   500  	// if blockNo != 0 && isMainChain && cdb.getBestBlockNo()+1 != blockNo {
   501  	// 	return fmt.Errorf("failed to add block(%d,%v). blkno != latestNo(%d) + 1", blockNo,
   502  	// 		block.BlockHash(), cdb.getBestBlockNo())
   503  	// }
   504  	// FIXME: blockNo 0 exception handling
   505  	// assumption: not an orphan
   506  	// fork can be here
   507  	logger.Debug().Uint64("blockNo", blockNo).Msg("add block to db")
   508  	blockBytes, err := proto.Marshal(block)
   509  	if err != nil {
   510  		logger.Error().Err(err).Uint64("no", blockNo).Str("hash", block.ID()).Msg("failed to add block")
   511  		return err
   512  	}
   513  
   514  	//add block
   515  	dbtx.Set(block.BlockHash(), blockBytes)
   516  
   517  	return nil
   518  }
   519  
   520  // drop block from DB
   521  func (cdb *ChainDB) dropBlock(dropNo types.BlockNo) error {
   522  	logger.Info().Uint64("no", dropNo).Msg("drop block")
   523  
   524  	dbTx := cdb.NewTx()
   525  	defer dbTx.Discard()
   526  
   527  	if dropNo <= 0 {
   528  		return ErrCantDropGenesis
   529  	}
   530  
   531  	dropBlock, err := cdb.GetBlockByNo(dropNo)
   532  	if err != nil {
   533  		return err
   534  	}
   535  
   536  	// remove tx mapping
   537  	for _, tx := range dropBlock.GetBody().GetTxs() {
   538  		cdb.deleteTx(&dbTx, tx)
   539  	}
   540  
   541  	// remove receipt
   542  	cdb.deleteReceipts(&dbTx, dropBlock.BlockHash(), dropBlock.BlockNo())
   543  
   544  	// remove (hash/block)
   545  	dbTx.Delete(dropBlock.BlockHash())
   546  
   547  	// remove (no/hash)
   548  	dropIdx := types.BlockNoToBytes(dropNo)
   549  	newLatestIdx := types.BlockNoToBytes(dropNo - 1)
   550  	dbTx.Delete(dropIdx)
   551  
   552  	// update latest
   553  	dbTx.Set(latestKey, newLatestIdx)
   554  
   555  	dbTx.Commit()
   556  
   557  	prevBlock, err := cdb.GetBlockByNo(dropNo - 1)
   558  	if err != nil {
   559  		return err
   560  	}
   561  
   562  	cdb.setLatest(prevBlock)
   563  
   564  	if err = cdb.checkBlockDropped(dropBlock); err != nil {
   565  		logger.Error().Err(err).Msg("block meta is not dropped")
   566  		return err
   567  	}
   568  	return nil
   569  }
   570  
   571  func (cdb *ChainDB) getBestBlockNo() (latestNo types.BlockNo) {
   572  	aopv := cdb.latest.Load()
   573  	if aopv != nil {
   574  		latestNo = aopv.(types.BlockNo)
   575  	} else {
   576  		panic("ChainDB:latest is nil")
   577  	}
   578  	return latestNo
   579  }
   580  
   581  // GetBlockByNo returns the block with its block number as blockNo.
   582  func (cdb *ChainDB) GetBlockByNo(blockNo types.BlockNo) (*types.Block, error) {
   583  	blockHash, err := cdb.getHashByNo(blockNo)
   584  	if err != nil {
   585  		return nil, err
   586  	}
   587  	//logger.Debugf("getblockbyNo No=%d Hash=%v", blockNo, enc.ToString(blockHash))
   588  	return cdb.getBlock(blockHash)
   589  }
   590  
   591  func (cdb *ChainDB) GetBlock(blockHash []byte) (*types.Block, error) {
   592  	return cdb.getBlock(blockHash)
   593  }
   594  
   595  func (cdb *ChainDB) GetHashByNo(blockNo types.BlockNo) ([]byte, error) {
   596  	return cdb.getHashByNo(blockNo)
   597  }
   598  
   599  func (cdb *ChainDB) getBlock(blockHash []byte) (*types.Block, error) {
   600  	if blockHash == nil {
   601  		return nil, fmt.Errorf("block hash invalid(nil)")
   602  	}
   603  	buf := types.Block{}
   604  	err := cdb.loadData(blockHash, &buf)
   605  	if err != nil || !bytes.Equal(buf.Hash, blockHash) {
   606  		return nil, &ErrNoBlock{id: blockHash}
   607  	}
   608  
   609  	//logger.Debugf("getblockbyHash Hash=%v", enc.ToString(blockHash))
   610  	return &buf, nil
   611  }
   612  
   613  func (cdb *ChainDB) getHashByNo(blockNo types.BlockNo) ([]byte, error) {
   614  	blockIdx := types.BlockNoToBytes(blockNo)
   615  	if cdb.store == nil {
   616  		return nil, ErrNoChainDB
   617  	}
   618  	blockHash := cdb.store.Get(blockIdx)
   619  	if len(blockHash) == 0 {
   620  		return nil, &ErrNoBlock{id: blockNo}
   621  	}
   622  	return blockHash, nil
   623  }
   624  
   625  func (cdb *ChainDB) getTx(txHash []byte) (*types.Tx, *types.TxIdx, error) {
   626  	txIdx := &types.TxIdx{}
   627  
   628  	err := cdb.loadData(txHash, txIdx)
   629  	if err != nil {
   630  		return nil, nil, fmt.Errorf("tx not found: txHash=%v", enc.ToString(txHash))
   631  	}
   632  	block, err := cdb.getBlock(txIdx.BlockHash)
   633  	if err != nil {
   634  		return nil, nil, &ErrNoBlock{txIdx.BlockHash}
   635  	}
   636  	txs := block.GetBody().GetTxs()
   637  	if txIdx.Idx >= int32(len(txs)) {
   638  		return nil, nil, fmt.Errorf("wrong tx idx: %d", txIdx.Idx)
   639  	}
   640  	tx := txs[txIdx.Idx]
   641  	logger.Debug().Str("hash", enc.ToString(txHash)).Msg("getTx")
   642  
   643  	return tx, txIdx, nil
   644  }
   645  
   646  func (cdb *ChainDB) getReceipt(blockHash []byte, blockNo types.BlockNo, idx int32) (*types.Receipt, error) {
   647  	storedReceipts, err := cdb.getReceipts(blockHash, blockNo)
   648  	if err != nil {
   649  		return nil, err
   650  	}
   651  	receipts := storedReceipts.Get()
   652  
   653  	if idx < 0 || idx > int32(len(receipts)) {
   654  		return nil, fmt.Errorf("cannot find a receipt: invalid index (%d)", idx)
   655  	}
   656  	r := receipts[idx]
   657  	r.SetMemoryInfo(blockHash, blockNo, idx)
   658  	return receipts[idx], nil
   659  }
   660  
   661  func (cdb *ChainDB) getReceipts(blockHash []byte, blockNo types.BlockNo) (*types.Receipts, error) {
   662  	data := cdb.store.Get(receiptsKey(blockHash, blockNo))
   663  	if len(data) == 0 {
   664  		return nil, errors.New("cannot find a receipt")
   665  	}
   666  	var b bytes.Buffer
   667  	b.Write(data)
   668  	var receipts types.Receipts
   669  	decoder := gob.NewDecoder(&b)
   670  	decoder.Decode(&receipts)
   671  
   672  	return &receipts, nil
   673  }
   674  
   675  type ChainTree struct {
   676  	Tree []ChainInfo
   677  }
   678  type ChainInfo struct {
   679  	Height types.BlockNo
   680  	Hash   string
   681  }
   682  
   683  func (cdb *ChainDB) GetChainTree() ([]byte, error) {
   684  	tree := make([]ChainInfo, 0)
   685  	var i uint64
   686  	for i = 0; i < cdb.getBestBlockNo(); i++ {
   687  		hash, _ := cdb.getHashByNo(i)
   688  		tree = append(tree, ChainInfo{
   689  			Height: i,
   690  			Hash:   enc.ToString(hash),
   691  		})
   692  		logger.Info().Str("hash", enc.ToString(hash)).Msg("GetChainTree")
   693  	}
   694  	jsonBytes, err := json.Marshal(tree)
   695  	if err != nil {
   696  		logger.Info().Msg("GetChainTree failed")
   697  	}
   698  	return jsonBytes, nil
   699  }
   700  
   701  func (cdb *ChainDB) writeReceipts(blockHash []byte, blockNo types.BlockNo, receipts *types.Receipts) {
   702  	dbTx := cdb.store.NewTx()
   703  	defer dbTx.Discard()
   704  
   705  	var val bytes.Buffer
   706  	gob := gob.NewEncoder(&val)
   707  	gob.Encode(receipts)
   708  
   709  	dbTx.Set(receiptsKey(blockHash, blockNo), val.Bytes())
   710  
   711  	dbTx.Commit()
   712  }
   713  
   714  func (cdb *ChainDB) deleteReceipts(dbTx *db.Transaction, blockHash []byte, blockNo types.BlockNo) {
   715  	(*dbTx).Delete(receiptsKey(blockHash, blockNo))
   716  }
   717  
   718  func receiptsKey(blockHash []byte, blockNo types.BlockNo) []byte {
   719  	var key bytes.Buffer
   720  	key.Write(receiptsPrefix)
   721  	key.Write(blockHash)
   722  	l := make([]byte, 8)
   723  	binary.LittleEndian.PutUint64(l[:], blockNo)
   724  	key.Write(l)
   725  	return key.Bytes()
   726  }
   727  
   728  func (cdb *ChainDB) writeReorgMarker(marker *ReorgMarker) error {
   729  	dbTx := cdb.store.NewTx()
   730  	defer dbTx.Discard()
   731  
   732  	val, err := marker.toBytes()
   733  	if err != nil {
   734  		logger.Error().Err(err).Msg("failed to serialize reorg marker")
   735  		return err
   736  	}
   737  
   738  	dbTx.Set(reorgKey, val)
   739  
   740  	dbTx.Commit()
   741  	return nil
   742  }
   743  
   744  func (cdb *ChainDB) deleteReorgMarker() {
   745  	dbTx := cdb.store.NewTx()
   746  	defer dbTx.Discard()
   747  
   748  	dbTx.Delete(reorgKey)
   749  
   750  	dbTx.Commit()
   751  }
   752  
   753  func (cdb *ChainDB) getReorgMarker() (*ReorgMarker, error) {
   754  	data := cdb.store.Get(reorgKey)
   755  	if len(data) == 0 {
   756  		return nil, nil
   757  	}
   758  
   759  	var marker ReorgMarker
   760  	var b bytes.Buffer
   761  	b.Write(data)
   762  	decoder := gob.NewDecoder(&b)
   763  	err := decoder.Decode(&marker)
   764  
   765  	return &marker, err
   766  }
   767  
   768  // implement ChainWAL interface
   769  func (cdb *ChainDB) IsNew() bool {
   770  	//TODO
   771  	return true
   772  }