github.com/ethereumproject/go-ethereum@v5.5.2+incompatible/core/database_util.go (about)

     1  // Copyright 2015 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package core
    18  
    19  import (
    20  	"bytes"
    21  	"encoding/binary"
    22  	"fmt"
    23  	"github.com/ethereumproject/go-ethereum/common"
    24  	"github.com/ethereumproject/go-ethereum/core/types"
    25  	"github.com/ethereumproject/go-ethereum/ethdb"
    26  	"github.com/ethereumproject/go-ethereum/logger"
    27  	"github.com/ethereumproject/go-ethereum/logger/glog"
    28  	"github.com/ethereumproject/go-ethereum/rlp"
    29  	"math/big"
    30  )
    31  
    32  var (
    33  	headHeaderKey = []byte("LastHeader")
    34  	headBlockKey  = []byte("LastBlock")
    35  	headFastKey   = []byte("LastFast")
    36  
    37  	blockPrefix    = []byte("block-")
    38  	blockNumPrefix = []byte("block-num-")
    39  
    40  	headerSuffix = []byte("-header")
    41  	bodySuffix   = []byte("-body")
    42  	tdSuffix     = []byte("-td")
    43  
    44  	txMetaSuffix        = []byte{0x01}
    45  	receiptsPrefix      = []byte("receipts-")
    46  	blockReceiptsPrefix = []byte("receipts-block-")
    47  
    48  	mipmapPre    = []byte("mipmap-log-bloom-")
    49  	MIPMapLevels = []uint64{1000000, 500000, 100000, 50000, 1000}
    50  
    51  	blockHashPrefix = []byte("block-hash-") // [deprecated by the header/block split, remove eventually]
    52  
    53  	preimagePrefix = "secure-key-" // preimagePrefix + hash -> preimage
    54  	lookupPrefix   = []byte("l")   // lookupPrefix + hash -> transaction/receipt lookup metadata
    55  )
    56  
    57  // TxLookupEntry is a positional metadata to help looking up the data content of
    58  // a transaction or receipt given only its hash.
    59  type TxLookupEntry struct {
    60  	BlockHash  common.Hash
    61  	BlockIndex uint64
    62  	Index      uint64
    63  }
    64  
    65  // GetCanonicalHash retrieves a hash assigned to a canonical block number.
    66  func GetCanonicalHash(db ethdb.Database, number uint64) common.Hash {
    67  	data, _ := db.Get(append(blockNumPrefix, big.NewInt(int64(number)).Bytes()...))
    68  	if len(data) == 0 {
    69  		return common.Hash{}
    70  	}
    71  	return common.BytesToHash(data)
    72  }
    73  
    74  // GetHeadHeaderHash retrieves the hash of the current canonical head block's
    75  // header. The difference between this and GetHeadBlockHash is that whereas the
    76  // last block hash is only updated upon a full block import, the last header
    77  // hash is updated already at header import, allowing head tracking for the
    78  // light synchronization mechanism.
    79  func GetHeadHeaderHash(db ethdb.Database) common.Hash {
    80  	data, _ := db.Get(headHeaderKey)
    81  	if len(data) == 0 {
    82  		return common.Hash{}
    83  	}
    84  	return common.BytesToHash(data)
    85  }
    86  
    87  // GetHeadBlockHash retrieves the hash of the current canonical head block.
    88  func GetHeadBlockHash(db ethdb.Database) common.Hash {
    89  	data, _ := db.Get(headBlockKey)
    90  	if len(data) == 0 {
    91  		return common.Hash{}
    92  	}
    93  	return common.BytesToHash(data)
    94  }
    95  
    96  // GetHeadFastBlockHash retrieves the hash of the current canonical head block during
    97  // fast synchronization. The difference between this and GetHeadBlockHash is that
    98  // whereas the last block hash is only updated upon a full block import, the last
    99  // fast hash is updated when importing pre-processed blocks.
   100  func GetHeadFastBlockHash(db ethdb.Database) common.Hash {
   101  	data, _ := db.Get(headFastKey)
   102  	if len(data) == 0 {
   103  		return common.Hash{}
   104  	}
   105  	return common.BytesToHash(data)
   106  }
   107  
   108  // GetHeaderRLP retrieves a block header in its raw RLP database encoding, or nil
   109  // if the header's not found.
   110  func GetHeaderRLP(db ethdb.Database, hash common.Hash) rlp.RawValue {
   111  	data, _ := db.Get(append(append(blockPrefix, hash[:]...), headerSuffix...))
   112  	return data
   113  }
   114  
   115  // GetHeader retrieves the block header corresponding to the hash, nil if none
   116  // found.
   117  func GetHeader(db ethdb.Database, hash common.Hash) *types.Header {
   118  	data := GetHeaderRLP(db, hash)
   119  	if len(data) == 0 {
   120  		return nil
   121  	}
   122  	header := new(types.Header)
   123  	if err := rlp.Decode(bytes.NewReader(data), header); err != nil {
   124  		glog.V(logger.Error).Infof("invalid block header RLP for hash %x: %v", hash, err)
   125  		return nil
   126  	}
   127  	return header
   128  }
   129  
   130  // GetBodyRLP retrieves the block body (transactions and uncles) in RLP encoding.
   131  func GetBodyRLP(db ethdb.Database, hash common.Hash) rlp.RawValue {
   132  	data, _ := db.Get(append(append(blockPrefix, hash[:]...), bodySuffix...))
   133  	return data
   134  }
   135  
   136  // GetBody retrieves the block body (transactons, uncles) corresponding to the
   137  // hash, nil if none found.
   138  func GetBody(db ethdb.Database, hash common.Hash) *types.Body {
   139  	data := GetBodyRLP(db, hash)
   140  	if len(data) == 0 {
   141  		return nil
   142  	}
   143  	body := new(types.Body)
   144  	if err := rlp.Decode(bytes.NewReader(data), body); err != nil {
   145  		glog.V(logger.Error).Infof("invalid block body RLP for hash %x: %v", hash, err)
   146  		return nil
   147  	}
   148  	return body
   149  }
   150  
   151  // GetTd retrieves a block's total difficulty corresponding to the hash, nil if
   152  // none found.
   153  func GetTd(db ethdb.Database, hash common.Hash) *big.Int {
   154  	data, _ := db.Get(append(append(blockPrefix, hash.Bytes()...), tdSuffix...))
   155  	if len(data) == 0 {
   156  		return nil
   157  	}
   158  	td := new(big.Int)
   159  	if err := rlp.Decode(bytes.NewReader(data), td); err != nil {
   160  		glog.V(logger.Error).Infof("invalid block total difficulty RLP for hash %x: %v", hash, err)
   161  		return nil
   162  	}
   163  	return td
   164  }
   165  
   166  // GetBlock retrieves an entire block corresponding to the hash, assembling it
   167  // back from the stored header and body. If either the header or body could not
   168  // be retrieved nil is returned.
   169  //
   170  // Note, due to concurrent download of header and block body the header and thus
   171  // canonical hash can be stored in the database but the body data not (yet).
   172  func GetBlock(db ethdb.Database, hash common.Hash) *types.Block {
   173  	// Retrieve the block header and body contents
   174  	header := GetHeader(db, hash)
   175  	if header == nil {
   176  		return nil
   177  	}
   178  	body := GetBody(db, hash)
   179  	if body == nil {
   180  		return nil
   181  	}
   182  	// Reassemble the block and return
   183  	return types.NewBlockWithHeader(header).WithBody(body.Transactions, body.Uncles)
   184  }
   185  
   186  // GetBlockReceipts retrieves the receipts generated by the transactions included
   187  // in a block given by its hash.
   188  func GetBlockReceipts(db ethdb.Database, hash common.Hash) types.Receipts {
   189  	data, _ := db.Get(append(blockReceiptsPrefix, hash[:]...))
   190  	if len(data) == 0 {
   191  		return nil
   192  	}
   193  	storageReceipts := []*types.ReceiptForStorage{}
   194  	if err := rlp.DecodeBytes(data, &storageReceipts); err != nil {
   195  		glog.V(logger.Error).Infof("invalid receipt array RLP for hash %x: %v", hash, err)
   196  		return nil
   197  	}
   198  	receipts := make(types.Receipts, len(storageReceipts))
   199  	for i, receipt := range storageReceipts {
   200  		receipts[i] = (*types.Receipt)(receipt)
   201  	}
   202  	return receipts
   203  }
   204  
   205  // GetTransaction retrieves a specific transaction from the database, along with
   206  // its added positional metadata.
   207  func GetTransaction(db ethdb.Database, hash common.Hash) (*types.Transaction, common.Hash, uint64, uint64) {
   208  	// Retrieve the transaction itself from the database
   209  	data, _ := db.Get(hash.Bytes())
   210  	if len(data) == 0 {
   211  		return nil, common.Hash{}, 0, 0
   212  	}
   213  	var tx types.Transaction
   214  	if err := rlp.DecodeBytes(data, &tx); err != nil {
   215  		return nil, common.Hash{}, 0, 0
   216  	}
   217  	// Retrieve the blockchain positional metadata
   218  	data, _ = db.Get(append(hash.Bytes(), txMetaSuffix...))
   219  	if len(data) == 0 {
   220  		return nil, common.Hash{}, 0, 0
   221  	}
   222  	var meta struct {
   223  		BlockHash  common.Hash
   224  		BlockIndex uint64
   225  		Index      uint64
   226  	}
   227  	if err := rlp.DecodeBytes(data, &meta); err != nil {
   228  		return nil, common.Hash{}, 0, 0
   229  	}
   230  	return &tx, meta.BlockHash, meta.BlockIndex, meta.Index
   231  }
   232  
   233  // GetReceipt returns a receipt by hash
   234  func GetReceipt(db ethdb.Database, txHash common.Hash) *types.Receipt {
   235  	data, _ := db.Get(append(receiptsPrefix, txHash[:]...))
   236  	if len(data) == 0 {
   237  		return nil
   238  	}
   239  	var receipt types.ReceiptForStorage
   240  	err := rlp.DecodeBytes(data, &receipt)
   241  	if err != nil {
   242  		glog.V(logger.Core).Errorln("GetReceipt err:", err)
   243  	}
   244  	return (*types.Receipt)(&receipt)
   245  }
   246  
   247  // WriteCanonicalHash stores the canonical hash for the given block number.
   248  func WriteCanonicalHash(db ethdb.Database, hash common.Hash, number uint64) error {
   249  	key := append(blockNumPrefix, big.NewInt(int64(number)).Bytes()...)
   250  	if err := db.Put(key, hash.Bytes()); err != nil {
   251  		glog.Fatalf("failed to store number to hash mapping into database: %v", err)
   252  		return err
   253  	}
   254  	return nil
   255  }
   256  
   257  // WriteHeadHeaderHash stores the head header's hash.
   258  func WriteHeadHeaderHash(db ethdb.Database, hash common.Hash) error {
   259  	if err := db.Put(headHeaderKey, hash.Bytes()); err != nil {
   260  		glog.Fatalf("failed to store last header's hash into database: %v", err)
   261  		return err
   262  	}
   263  	return nil
   264  }
   265  
   266  // WriteHeadBlockHash stores the head block's hash.
   267  func WriteHeadBlockHash(db ethdb.Database, hash common.Hash) error {
   268  	if err := db.Put(headBlockKey, hash.Bytes()); err != nil {
   269  		glog.Fatalf("failed to store last block's hash into database: %v", err)
   270  		return err
   271  	}
   272  	return nil
   273  }
   274  
   275  // WriteHeadFastBlockHash stores the fast head block's hash.
   276  func WriteHeadFastBlockHash(db ethdb.Database, hash common.Hash) error {
   277  	if err := db.Put(headFastKey, hash.Bytes()); err != nil {
   278  		glog.Fatalf("failed to store last fast block's hash into database: %v", err)
   279  		return err
   280  	}
   281  	return nil
   282  }
   283  
   284  // WriteHeader serializes a block header into the database.
   285  func WriteHeader(db ethdb.Database, header *types.Header) error {
   286  	data, err := rlp.EncodeToBytes(header)
   287  	if err != nil {
   288  		return err
   289  	}
   290  	key := append(append(blockPrefix, header.Hash().Bytes()...), headerSuffix...)
   291  	if err := db.Put(key, data); err != nil {
   292  		glog.Fatalf("failed to store header into database: %v", err)
   293  		return err
   294  	}
   295  	glog.V(logger.Detail).Infof("stored header #%v [%x…]", header.Number, header.Hash().Bytes()[:4])
   296  	return nil
   297  }
   298  
   299  // WriteBody serializes the body of a block into the database.
   300  func WriteBody(db ethdb.Database, hash common.Hash, body *types.Body) error {
   301  	data, err := rlp.EncodeToBytes(body)
   302  	if err != nil {
   303  		return err
   304  	}
   305  	key := append(append(blockPrefix, hash.Bytes()...), bodySuffix...)
   306  	if err := db.Put(key, data); err != nil {
   307  		glog.Fatalf("failed to store block body into database: %v", err)
   308  		return err
   309  	}
   310  	glog.V(logger.Detail).Infof("stored block body [%x…]", hash.Bytes()[:4])
   311  	return nil
   312  }
   313  
   314  // WriteTd serializes the total difficulty of a block into the database.
   315  func WriteTd(db ethdb.Database, hash common.Hash, td *big.Int) error {
   316  	data, err := rlp.EncodeToBytes(td)
   317  	if err != nil {
   318  		return err
   319  	}
   320  	key := append(append(blockPrefix, hash.Bytes()...), tdSuffix...)
   321  	if err := db.Put(key, data); err != nil {
   322  		glog.Fatalf("failed to store block total difficulty into database: %v", err)
   323  		return err
   324  	}
   325  	glog.V(logger.Detail).Infof("stored block total difficulty [%x…]: %v", hash.Bytes()[:4], td)
   326  	return nil
   327  }
   328  
   329  // WriteBlock serializes a block into the database, header and body separately.
   330  func WriteBlock(db ethdb.Database, block *types.Block) error {
   331  	// Store the body first to retain database consistency
   332  	if err := WriteBody(db, block.Hash(), block.Body()); err != nil {
   333  		return err
   334  	}
   335  
   336  	// Store the header too, signaling full block ownership
   337  	if err := WriteHeader(db, block.Header()); err != nil {
   338  		return err
   339  	}
   340  	return nil
   341  }
   342  
   343  // WriteBlockReceipts stores all the transaction receipts belonging to a block
   344  // as a single receipt slice. This is used during chain reorganisations for
   345  // rescheduling dropped transactions.
   346  func WriteBlockReceipts(db ethdb.Database, hash common.Hash, receipts types.Receipts) error {
   347  	// Convert the receipts into their storage form and serialize them
   348  	storageReceipts := make([]*types.ReceiptForStorage, len(receipts))
   349  	for i, receipt := range receipts {
   350  		storageReceipts[i] = (*types.ReceiptForStorage)(receipt)
   351  	}
   352  	bytes, err := rlp.EncodeToBytes(storageReceipts)
   353  	if err != nil {
   354  		return err
   355  	}
   356  	// Store the flattened receipt slice
   357  	if err := db.Put(append(blockReceiptsPrefix, hash.Bytes()...), bytes); err != nil {
   358  		glog.Fatalf("failed to store block receipts into database: %v", err)
   359  		return err
   360  	}
   361  	glog.V(logger.Detail).Infof("stored block receipts [%x…]", hash.Bytes()[:4])
   362  	return nil
   363  }
   364  
   365  // WriteTransactions stores the transactions associated with a specific block
   366  // into the given database. Beside writing the transaction, the function also
   367  // stores a metadata entry along with the transaction, detailing the position
   368  // of this within the blockchain.
   369  func WriteTransactions(db ethdb.Database, block *types.Block) error {
   370  	batch := db.NewBatch()
   371  
   372  	// Iterate over each transaction and encode it with its metadata
   373  	for i, tx := range block.Transactions() {
   374  		// Encode and queue up the transaction for storage
   375  		data, err := rlp.EncodeToBytes(tx)
   376  		if err != nil {
   377  			return err
   378  		}
   379  		if err := batch.Put(tx.Hash().Bytes(), data); err != nil {
   380  			return err
   381  		}
   382  		// Encode and queue up the transaction metadata for storage
   383  		meta := struct {
   384  			BlockHash  common.Hash
   385  			BlockIndex uint64
   386  			Index      uint64
   387  		}{
   388  			BlockHash:  block.Hash(),
   389  			BlockIndex: block.NumberU64(),
   390  			Index:      uint64(i),
   391  		}
   392  		data, err = rlp.EncodeToBytes(meta)
   393  		if err != nil {
   394  			return err
   395  		}
   396  		if err := batch.Put(append(tx.Hash().Bytes(), txMetaSuffix...), data); err != nil {
   397  			return err
   398  		}
   399  	}
   400  	// Write the scheduled data into the database
   401  	if err := batch.Write(); err != nil {
   402  		glog.Fatalf("failed to store transactions into database: %v", err)
   403  		return err
   404  	}
   405  	return nil
   406  }
   407  
   408  // WriteReceipts stores a batch of transaction receipts into the database.
   409  func WriteReceipts(db ethdb.Database, receipts types.Receipts) error {
   410  	batch := db.NewBatch()
   411  
   412  	// Iterate over all the receipts and queue them for database injection
   413  	for _, receipt := range receipts {
   414  		storageReceipt := (*types.ReceiptForStorage)(receipt)
   415  		data, err := rlp.EncodeToBytes(storageReceipt)
   416  		if err != nil {
   417  			return err
   418  		}
   419  		if err := batch.Put(append(receiptsPrefix, receipt.TxHash.Bytes()...), data); err != nil {
   420  			return err
   421  		}
   422  	}
   423  	// Write the scheduled data into the database
   424  	if err := batch.Write(); err != nil {
   425  		glog.Fatalf("failed to store receipts into database: %v", err)
   426  		return err
   427  	}
   428  	return nil
   429  }
   430  
   431  // DeleteCanonicalHash removes the number to hash canonical mapping.
   432  func DeleteCanonicalHash(db ethdb.Database, number uint64) {
   433  	db.Delete(append(blockNumPrefix, big.NewInt(int64(number)).Bytes()...))
   434  }
   435  
   436  // DeleteHeader removes all block header data associated with a hash.
   437  func DeleteHeader(db ethdb.Database, hash common.Hash) {
   438  	db.Delete(append(append(blockPrefix, hash.Bytes()...), headerSuffix...))
   439  }
   440  
   441  // DeleteBody removes all block body data associated with a hash.
   442  func DeleteBody(db ethdb.Database, hash common.Hash) {
   443  	db.Delete(append(append(blockPrefix, hash.Bytes()...), bodySuffix...))
   444  }
   445  
   446  // DeleteTd removes all block total difficulty data associated with a hash.
   447  func DeleteTd(db ethdb.Database, hash common.Hash) {
   448  	db.Delete(append(append(blockPrefix, hash.Bytes()...), tdSuffix...))
   449  }
   450  
   451  // DeleteBlock removes all block data associated with a hash.
   452  func DeleteBlock(db ethdb.Database, hash common.Hash) {
   453  	DeleteBlockReceipts(db, hash)
   454  	DeleteHeader(db, hash)
   455  	DeleteBody(db, hash)
   456  	DeleteTd(db, hash)
   457  }
   458  
   459  // DeleteBlockReceipts removes all receipt data associated with a block hash.
   460  func DeleteBlockReceipts(db ethdb.Database, hash common.Hash) {
   461  	db.Delete(append(blockReceiptsPrefix, hash.Bytes()...))
   462  }
   463  
   464  // DeleteTransaction removes all transaction data associated with a hash.
   465  func DeleteTransaction(db ethdb.Database, hash common.Hash) {
   466  	db.Delete(hash.Bytes())
   467  	db.Delete(append(hash.Bytes(), txMetaSuffix...))
   468  }
   469  
   470  // DeleteReceipt removes all receipt data associated with a transaction hash.
   471  func DeleteReceipt(db ethdb.Database, hash common.Hash) {
   472  	db.Delete(append(receiptsPrefix, hash.Bytes()...))
   473  }
   474  
   475  // PreimageTable returns a Database instance with the key prefix for preimage entries.
   476  func PreimageTable(db ethdb.Database) ethdb.Database {
   477  	return ethdb.NewTable(db, preimagePrefix)
   478  }
   479  
   480  // WritePreimages writes the provided set of preimages to the database. `number` is the
   481  // current block number, and is used for debug messages only.
   482  func WritePreimages(db ethdb.Database, number uint64, preimages map[common.Hash][]byte) error {
   483  	table := PreimageTable(db)
   484  	batch := table.NewBatch()
   485  	hitCount := 0
   486  	for hash, preimage := range preimages {
   487  		if _, err := table.Get(hash.Bytes()); err != nil {
   488  			batch.Put(hash.Bytes(), preimage)
   489  			hitCount++
   490  		}
   491  	}
   492  	//preimageCounter.Inc(int64(len(preimages)))
   493  	//preimageHitCounter.Inc(int64(hitCount))
   494  	if hitCount > 0 {
   495  		if err := batch.Write(); err != nil {
   496  			return fmt.Errorf("preimage write fail for block %d: %v", number, err)
   497  		}
   498  	}
   499  	return nil
   500  }
   501  
   502  // WriteTxLookupEntries stores a positional metadata for every transaction from
   503  // a block, enabling hash based transaction and receipt lookups.
   504  func WriteTxLookupEntries(db ethdb.Putter, block *types.Block) error {
   505  	// Iterate over each transaction and encode its metadata
   506  	for i, tx := range block.Transactions() {
   507  		entry := TxLookupEntry{
   508  			BlockHash:  block.Hash(),
   509  			BlockIndex: block.NumberU64(),
   510  			Index:      uint64(i),
   511  		}
   512  		data, err := rlp.EncodeToBytes(entry)
   513  		if err != nil {
   514  			return err
   515  		}
   516  		if err := db.Put(append(lookupPrefix, tx.Hash().Bytes()...), data); err != nil {
   517  			return err
   518  		}
   519  	}
   520  	return nil
   521  }
   522  
   523  // [deprecated by the header/block split, remove eventually]
   524  // GetBlockByHashOld returns the old combined block corresponding to the hash
   525  // or nil if not found. This method is only used by the upgrade mechanism to
   526  // access the old combined block representation. It will be dropped after the
   527  // network transitions to eth/63.
   528  func GetBlockByHashOld(db ethdb.Database, hash common.Hash) *types.Block {
   529  	data, _ := db.Get(append(blockHashPrefix, hash[:]...))
   530  	if len(data) == 0 {
   531  		return nil
   532  	}
   533  	var block types.StorageBlock
   534  	if err := rlp.Decode(bytes.NewReader(data), &block); err != nil {
   535  		glog.V(logger.Error).Infof("invalid block RLP for hash %x: %v", hash, err)
   536  		return nil
   537  	}
   538  	return (*types.Block)(&block)
   539  }
   540  
   541  // returns a formatted MIP mapped key by adding prefix, canonical number and level
   542  //
   543  // ex. fn(98, 1000) = (prefix || 1000 || 0)
   544  func mipmapKey(num, level uint64) []byte {
   545  	lkey := make([]byte, 8)
   546  	binary.BigEndian.PutUint64(lkey, level)
   547  	key := new(big.Int).SetUint64(num / level * level)
   548  
   549  	return append(mipmapPre, append(lkey, key.Bytes()...)...)
   550  }
   551  
   552  // WriteMapmapBloom writes each address included in the receipts' logs to the
   553  // MIP bloom bin.
   554  func WriteMipmapBloom(db ethdb.Database, number uint64, receipts types.Receipts) error {
   555  	batch := db.NewBatch()
   556  	for _, level := range MIPMapLevels {
   557  		key := mipmapKey(number, level)
   558  		bloomDat, _ := db.Get(key)
   559  		bloom := types.BytesToBloom(bloomDat)
   560  		for _, receipt := range receipts {
   561  			for _, log := range receipt.Logs {
   562  				bloom.Add(log.Address.Big())
   563  			}
   564  		}
   565  		batch.Put(key, bloom.Bytes())
   566  	}
   567  	if err := batch.Write(); err != nil {
   568  		return fmt.Errorf("mipmap write fail for: %d: %v", number, err)
   569  	}
   570  	return nil
   571  }
   572  
   573  // GetMipmapBloom returns a bloom filter using the number and level as input
   574  // parameters. For available levels see MIPMapLevels.
   575  func GetMipmapBloom(db ethdb.Database, number, level uint64) types.Bloom {
   576  	bloomDat, _ := db.Get(mipmapKey(number, level))
   577  	return types.BytesToBloom(bloomDat)
   578  }
   579  
   580  // GetBlockChainVersion reads the version number from db.
   581  func GetBlockChainVersion(db ethdb.Database) int {
   582  	var vsn uint
   583  	enc, _ := db.Get([]byte("BlockchainVersion"))
   584  	rlp.DecodeBytes(enc, &vsn)
   585  	return int(vsn)
   586  }
   587  
   588  // WriteBlockChainVersion writes vsn as the version number to db.
   589  func WriteBlockChainVersion(db ethdb.Database, vsn int) {
   590  	enc, _ := rlp.EncodeToBytes(uint(vsn))
   591  	db.Put([]byte("BlockchainVersion"), enc)
   592  }