github.1485827954.workers.dev/ethereum/go-ethereum@v1.14.3/triedb/pathdb/history.go (about)

     1  // Copyright 2022 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 pathdb
    18  
    19  import (
    20  	"bytes"
    21  	"encoding/binary"
    22  	"errors"
    23  	"fmt"
    24  	"slices"
    25  	"time"
    26  
    27  	"github.com/ethereum/go-ethereum/common"
    28  	"github.com/ethereum/go-ethereum/core/rawdb"
    29  	"github.com/ethereum/go-ethereum/ethdb"
    30  	"github.com/ethereum/go-ethereum/log"
    31  	"github.com/ethereum/go-ethereum/trie/triestate"
    32  )
    33  
    34  // State history records the state changes involved in executing a block. The
    35  // state can be reverted to the previous version by applying the associated
    36  // history object (state reverse diff). State history objects are kept to
    37  // guarantee that the system can perform state rollbacks in case of deep reorg.
    38  //
    39  // Each state transition will generate a state history object. Note that not
    40  // every block has a corresponding state history object. If a block performs
    41  // no state changes whatsoever, no state is created for it. Each state history
    42  // will have a sequentially increasing number acting as its unique identifier.
    43  //
    44  // The state history is written to disk (ancient store) when the corresponding
    45  // diff layer is merged into the disk layer. At the same time, system can prune
    46  // the oldest histories according to config.
    47  //
    48  //                                                        Disk State
    49  //                                                            ^
    50  //                                                            |
    51  //   +------------+     +---------+     +---------+     +---------+
    52  //   | Init State |---->| State 1 |---->|   ...   |---->| State n |
    53  //   +------------+     +---------+     +---------+     +---------+
    54  //
    55  //                     +-----------+      +------+     +-----------+
    56  //                     | History 1 |----> | ...  |---->| History n |
    57  //                     +-----------+      +------+     +-----------+
    58  //
    59  // # Rollback
    60  //
    61  // If the system wants to roll back to a previous state n, it needs to ensure
    62  // all history objects from n+1 up to the current disk layer are existent. The
    63  // history objects are applied to the state in reverse order, starting from the
    64  // current disk layer.
    65  
    66  const (
    67  	accountIndexSize = common.AddressLength + 13 // The length of encoded account index
    68  	slotIndexSize    = common.HashLength + 5     // The length of encoded slot index
    69  	historyMetaSize  = 9 + 2*common.HashLength   // The length of encoded history meta
    70  
    71  	stateHistoryVersion = uint8(0) // initial version of state history structure.
    72  )
    73  
    74  // Each state history entry is consisted of five elements:
    75  //
    76  // # metadata
    77  //  This object contains a few meta fields, such as the associated state root,
    78  //  block number, version tag and so on. This object may contain an extra
    79  //  accountHash list which means the storage changes belong to these accounts
    80  //  are not complete due to large contract destruction. The incomplete history
    81  //  can not be used for rollback and serving archive state request.
    82  //
    83  // # account index
    84  //  This object contains some index information of account. For example, offset
    85  //  and length indicate the location of the data belonging to the account. Besides,
    86  //  storageOffset and storageSlots indicate the storage modification location
    87  //  belonging to the account.
    88  //
    89  //  The size of each account index is *fixed*, and all indexes are sorted
    90  //  lexicographically. Thus binary search can be performed to quickly locate a
    91  //  specific account.
    92  //
    93  // # account data
    94  //  Account data is a concatenated byte stream composed of all account data.
    95  //  The account data can be solved by the offset and length info indicated
    96  //  by corresponding account index.
    97  //
    98  //            fixed size
    99  //         ^             ^
   100  //        /               \
   101  //        +-----------------+-----------------+----------------+-----------------+
   102  //        | Account index 1 | Account index 2 |       ...      | Account index N |
   103  //        +-----------------+-----------------+----------------+-----------------+
   104  //        |
   105  //        |     length
   106  // offset |----------------+
   107  //        v                v
   108  //        +----------------+----------------+----------------+----------------+
   109  //        | Account data 1 | Account data 2 |       ...      | Account data N |
   110  //        +----------------+----------------+----------------+----------------+
   111  //
   112  // # storage index
   113  //  This object is similar with account index. It's also fixed size and contains
   114  //  the location info of storage slot data.
   115  //
   116  // # storage data
   117  //  Storage data is a concatenated byte stream composed of all storage slot data.
   118  //  The storage slot data can be solved by the location info indicated by
   119  //  corresponding account index and storage slot index.
   120  //
   121  //                    fixed size
   122  //                 ^             ^
   123  //                /               \
   124  //                +-----------------+-----------------+----------------+-----------------+
   125  //                | Account index 1 | Account index 2 |       ...      | Account index N |
   126  //                +-----------------+-----------------+----------------+-----------------+
   127  //                |
   128  //                |                    storage slots
   129  // storage offset |-----------------------------------------------------+
   130  //                v                                                     v
   131  //                +-----------------+-----------------+-----------------+
   132  //                | storage index 1 | storage index 2 | storage index 3 |
   133  //                +-----------------+-----------------+-----------------+
   134  //                |     length
   135  //         offset |-------------+
   136  //                v             v
   137  //                +-------------+
   138  //                | slot data 1 |
   139  //                +-------------+
   140  
   141  // accountIndex describes the metadata belonging to an account.
   142  type accountIndex struct {
   143  	address       common.Address // The address of account
   144  	length        uint8          // The length of account data, size limited by 255
   145  	offset        uint32         // The offset of item in account data table
   146  	storageOffset uint32         // The offset of storage index in storage index table
   147  	storageSlots  uint32         // The number of mutated storage slots belonging to the account
   148  }
   149  
   150  // encode packs account index into byte stream.
   151  func (i *accountIndex) encode() []byte {
   152  	var buf [accountIndexSize]byte
   153  	copy(buf[:], i.address.Bytes())
   154  	buf[common.AddressLength] = i.length
   155  	binary.BigEndian.PutUint32(buf[common.AddressLength+1:], i.offset)
   156  	binary.BigEndian.PutUint32(buf[common.AddressLength+5:], i.storageOffset)
   157  	binary.BigEndian.PutUint32(buf[common.AddressLength+9:], i.storageSlots)
   158  	return buf[:]
   159  }
   160  
   161  // decode unpacks account index from byte stream.
   162  func (i *accountIndex) decode(blob []byte) {
   163  	i.address = common.BytesToAddress(blob[:common.AddressLength])
   164  	i.length = blob[common.AddressLength]
   165  	i.offset = binary.BigEndian.Uint32(blob[common.AddressLength+1:])
   166  	i.storageOffset = binary.BigEndian.Uint32(blob[common.AddressLength+5:])
   167  	i.storageSlots = binary.BigEndian.Uint32(blob[common.AddressLength+9:])
   168  }
   169  
   170  // slotIndex describes the metadata belonging to a storage slot.
   171  type slotIndex struct {
   172  	hash   common.Hash // The hash of slot key
   173  	length uint8       // The length of storage slot, up to 32 bytes defined in protocol
   174  	offset uint32      // The offset of item in storage slot data table
   175  }
   176  
   177  // encode packs slot index into byte stream.
   178  func (i *slotIndex) encode() []byte {
   179  	var buf [slotIndexSize]byte
   180  	copy(buf[:common.HashLength], i.hash.Bytes())
   181  	buf[common.HashLength] = i.length
   182  	binary.BigEndian.PutUint32(buf[common.HashLength+1:], i.offset)
   183  	return buf[:]
   184  }
   185  
   186  // decode unpack slot index from the byte stream.
   187  func (i *slotIndex) decode(blob []byte) {
   188  	i.hash = common.BytesToHash(blob[:common.HashLength])
   189  	i.length = blob[common.HashLength]
   190  	i.offset = binary.BigEndian.Uint32(blob[common.HashLength+1:])
   191  }
   192  
   193  // meta describes the meta data of state history object.
   194  type meta struct {
   195  	version uint8       // version tag of history object
   196  	parent  common.Hash // prev-state root before the state transition
   197  	root    common.Hash // post-state root after the state transition
   198  	block   uint64      // associated block number
   199  }
   200  
   201  // encode packs the meta object into byte stream.
   202  func (m *meta) encode() []byte {
   203  	buf := make([]byte, historyMetaSize)
   204  	buf[0] = m.version
   205  	copy(buf[1:1+common.HashLength], m.parent.Bytes())
   206  	copy(buf[1+common.HashLength:1+2*common.HashLength], m.root.Bytes())
   207  	binary.BigEndian.PutUint64(buf[1+2*common.HashLength:historyMetaSize], m.block)
   208  	return buf[:]
   209  }
   210  
   211  // decode unpacks the meta object from byte stream.
   212  func (m *meta) decode(blob []byte) error {
   213  	if len(blob) < 1 {
   214  		return errors.New("no version tag")
   215  	}
   216  	switch blob[0] {
   217  	case stateHistoryVersion:
   218  		if len(blob) != historyMetaSize {
   219  			return fmt.Errorf("invalid state history meta, len: %d", len(blob))
   220  		}
   221  		m.version = blob[0]
   222  		m.parent = common.BytesToHash(blob[1 : 1+common.HashLength])
   223  		m.root = common.BytesToHash(blob[1+common.HashLength : 1+2*common.HashLength])
   224  		m.block = binary.BigEndian.Uint64(blob[1+2*common.HashLength : historyMetaSize])
   225  		return nil
   226  	default:
   227  		return fmt.Errorf("unknown version %d", blob[0])
   228  	}
   229  }
   230  
   231  // history represents a set of state changes belong to a block along with
   232  // the metadata including the state roots involved in the state transition.
   233  // State history objects in disk are linked with each other by a unique id
   234  // (8-bytes integer), the oldest state history object can be pruned on demand
   235  // in order to control the storage size.
   236  type history struct {
   237  	meta        *meta                                     // Meta data of history
   238  	accounts    map[common.Address][]byte                 // Account data keyed by its address hash
   239  	accountList []common.Address                          // Sorted account hash list
   240  	storages    map[common.Address]map[common.Hash][]byte // Storage data keyed by its address hash and slot hash
   241  	storageList map[common.Address][]common.Hash          // Sorted slot hash list
   242  }
   243  
   244  // newHistory constructs the state history object with provided state change set.
   245  func newHistory(root common.Hash, parent common.Hash, block uint64, states *triestate.Set) *history {
   246  	var (
   247  		accountList []common.Address
   248  		storageList = make(map[common.Address][]common.Hash)
   249  	)
   250  	for addr := range states.Accounts {
   251  		accountList = append(accountList, addr)
   252  	}
   253  	slices.SortFunc(accountList, common.Address.Cmp)
   254  
   255  	for addr, slots := range states.Storages {
   256  		slist := make([]common.Hash, 0, len(slots))
   257  		for slotHash := range slots {
   258  			slist = append(slist, slotHash)
   259  		}
   260  		slices.SortFunc(slist, common.Hash.Cmp)
   261  		storageList[addr] = slist
   262  	}
   263  	return &history{
   264  		meta: &meta{
   265  			version: stateHistoryVersion,
   266  			parent:  parent,
   267  			root:    root,
   268  			block:   block,
   269  		},
   270  		accounts:    states.Accounts,
   271  		accountList: accountList,
   272  		storages:    states.Storages,
   273  		storageList: storageList,
   274  	}
   275  }
   276  
   277  // encode serializes the state history and returns four byte streams represent
   278  // concatenated account/storage data, account/storage indexes respectively.
   279  func (h *history) encode() ([]byte, []byte, []byte, []byte) {
   280  	var (
   281  		slotNumber     uint32 // the number of processed slots
   282  		accountData    []byte // the buffer for concatenated account data
   283  		storageData    []byte // the buffer for concatenated storage data
   284  		accountIndexes []byte // the buffer for concatenated account index
   285  		storageIndexes []byte // the buffer for concatenated storage index
   286  	)
   287  	for _, addr := range h.accountList {
   288  		accIndex := accountIndex{
   289  			address: addr,
   290  			length:  uint8(len(h.accounts[addr])),
   291  			offset:  uint32(len(accountData)),
   292  		}
   293  		slots, exist := h.storages[addr]
   294  		if exist {
   295  			// Encode storage slots in order
   296  			for _, slotHash := range h.storageList[addr] {
   297  				sIndex := slotIndex{
   298  					hash:   slotHash,
   299  					length: uint8(len(slots[slotHash])),
   300  					offset: uint32(len(storageData)),
   301  				}
   302  				storageData = append(storageData, slots[slotHash]...)
   303  				storageIndexes = append(storageIndexes, sIndex.encode()...)
   304  			}
   305  			// Fill up the storage meta in account index
   306  			accIndex.storageOffset = slotNumber
   307  			accIndex.storageSlots = uint32(len(slots))
   308  			slotNumber += uint32(len(slots))
   309  		}
   310  		accountData = append(accountData, h.accounts[addr]...)
   311  		accountIndexes = append(accountIndexes, accIndex.encode()...)
   312  	}
   313  	return accountData, storageData, accountIndexes, storageIndexes
   314  }
   315  
   316  // decoder wraps the byte streams for decoding with extra meta fields.
   317  type decoder struct {
   318  	accountData    []byte // the buffer for concatenated account data
   319  	storageData    []byte // the buffer for concatenated storage data
   320  	accountIndexes []byte // the buffer for concatenated account index
   321  	storageIndexes []byte // the buffer for concatenated storage index
   322  
   323  	lastAccount       *common.Address // the address of last resolved account
   324  	lastAccountRead   uint32          // the read-cursor position of account data
   325  	lastSlotIndexRead uint32          // the read-cursor position of storage slot index
   326  	lastSlotDataRead  uint32          // the read-cursor position of storage slot data
   327  }
   328  
   329  // verify validates the provided byte streams for decoding state history. A few
   330  // checks will be performed to quickly detect data corruption. The byte stream
   331  // is regarded as corrupted if:
   332  //
   333  // - account indexes buffer is empty(empty state set is invalid)
   334  // - account indexes/storage indexer buffer is not aligned
   335  //
   336  // note, these situations are allowed:
   337  //
   338  // - empty account data: all accounts were not present
   339  // - empty storage set: no slots are modified
   340  func (r *decoder) verify() error {
   341  	if len(r.accountIndexes)%accountIndexSize != 0 || len(r.accountIndexes) == 0 {
   342  		return fmt.Errorf("invalid account index, len: %d", len(r.accountIndexes))
   343  	}
   344  	if len(r.storageIndexes)%slotIndexSize != 0 {
   345  		return fmt.Errorf("invalid storage index, len: %d", len(r.storageIndexes))
   346  	}
   347  	return nil
   348  }
   349  
   350  // readAccount parses the account from the byte stream with specified position.
   351  func (r *decoder) readAccount(pos int) (accountIndex, []byte, error) {
   352  	// Decode account index from the index byte stream.
   353  	var index accountIndex
   354  	if (pos+1)*accountIndexSize > len(r.accountIndexes) {
   355  		return accountIndex{}, nil, errors.New("account data buffer is corrupted")
   356  	}
   357  	index.decode(r.accountIndexes[pos*accountIndexSize : (pos+1)*accountIndexSize])
   358  
   359  	// Perform validation before parsing account data, ensure
   360  	// - account is sorted in order in byte stream
   361  	// - account data is strictly encoded with no gap inside
   362  	// - account data is not out-of-slice
   363  	if r.lastAccount != nil { // zero address is possible
   364  		if bytes.Compare(r.lastAccount.Bytes(), index.address.Bytes()) >= 0 {
   365  			return accountIndex{}, nil, errors.New("account is not in order")
   366  		}
   367  	}
   368  	if index.offset != r.lastAccountRead {
   369  		return accountIndex{}, nil, errors.New("account data buffer is gaped")
   370  	}
   371  	last := index.offset + uint32(index.length)
   372  	if uint32(len(r.accountData)) < last {
   373  		return accountIndex{}, nil, errors.New("account data buffer is corrupted")
   374  	}
   375  	data := r.accountData[index.offset:last]
   376  
   377  	r.lastAccount = &index.address
   378  	r.lastAccountRead = last
   379  
   380  	return index, data, nil
   381  }
   382  
   383  // readStorage parses the storage slots from the byte stream with specified account.
   384  func (r *decoder) readStorage(accIndex accountIndex) ([]common.Hash, map[common.Hash][]byte, error) {
   385  	var (
   386  		last    common.Hash
   387  		list    []common.Hash
   388  		storage = make(map[common.Hash][]byte)
   389  	)
   390  	for j := 0; j < int(accIndex.storageSlots); j++ {
   391  		var (
   392  			index slotIndex
   393  			start = (accIndex.storageOffset + uint32(j)) * uint32(slotIndexSize)
   394  			end   = (accIndex.storageOffset + uint32(j+1)) * uint32(slotIndexSize)
   395  		)
   396  		// Perform validation before parsing storage slot data, ensure
   397  		// - slot index is not out-of-slice
   398  		// - slot data is not out-of-slice
   399  		// - slot is sorted in order in byte stream
   400  		// - slot indexes is strictly encoded with no gap inside
   401  		// - slot data is strictly encoded with no gap inside
   402  		if start != r.lastSlotIndexRead {
   403  			return nil, nil, errors.New("storage index buffer is gapped")
   404  		}
   405  		if uint32(len(r.storageIndexes)) < end {
   406  			return nil, nil, errors.New("storage index buffer is corrupted")
   407  		}
   408  		index.decode(r.storageIndexes[start:end])
   409  
   410  		if bytes.Compare(last.Bytes(), index.hash.Bytes()) >= 0 {
   411  			return nil, nil, errors.New("storage slot is not in order")
   412  		}
   413  		if index.offset != r.lastSlotDataRead {
   414  			return nil, nil, errors.New("storage data buffer is gapped")
   415  		}
   416  		sEnd := index.offset + uint32(index.length)
   417  		if uint32(len(r.storageData)) < sEnd {
   418  			return nil, nil, errors.New("storage data buffer is corrupted")
   419  		}
   420  		storage[index.hash] = r.storageData[r.lastSlotDataRead:sEnd]
   421  		list = append(list, index.hash)
   422  
   423  		last = index.hash
   424  		r.lastSlotIndexRead = end
   425  		r.lastSlotDataRead = sEnd
   426  	}
   427  	return list, storage, nil
   428  }
   429  
   430  // decode deserializes the account and storage data from the provided byte stream.
   431  func (h *history) decode(accountData, storageData, accountIndexes, storageIndexes []byte) error {
   432  	var (
   433  		accounts    = make(map[common.Address][]byte)
   434  		storages    = make(map[common.Address]map[common.Hash][]byte)
   435  		accountList []common.Address
   436  		storageList = make(map[common.Address][]common.Hash)
   437  
   438  		r = &decoder{
   439  			accountData:    accountData,
   440  			storageData:    storageData,
   441  			accountIndexes: accountIndexes,
   442  			storageIndexes: storageIndexes,
   443  		}
   444  	)
   445  	if err := r.verify(); err != nil {
   446  		return err
   447  	}
   448  	for i := 0; i < len(accountIndexes)/accountIndexSize; i++ {
   449  		// Resolve account first
   450  		accIndex, accData, err := r.readAccount(i)
   451  		if err != nil {
   452  			return err
   453  		}
   454  		accounts[accIndex.address] = accData
   455  		accountList = append(accountList, accIndex.address)
   456  
   457  		// Resolve storage slots
   458  		slotList, slotData, err := r.readStorage(accIndex)
   459  		if err != nil {
   460  			return err
   461  		}
   462  		if len(slotList) > 0 {
   463  			storageList[accIndex.address] = slotList
   464  			storages[accIndex.address] = slotData
   465  		}
   466  	}
   467  	h.accounts = accounts
   468  	h.accountList = accountList
   469  	h.storages = storages
   470  	h.storageList = storageList
   471  	return nil
   472  }
   473  
   474  // readHistory reads and decodes the state history object by the given id.
   475  func readHistory(reader ethdb.AncientReader, id uint64) (*history, error) {
   476  	blob := rawdb.ReadStateHistoryMeta(reader, id)
   477  	if len(blob) == 0 {
   478  		return nil, fmt.Errorf("state history not found %d", id)
   479  	}
   480  	var m meta
   481  	if err := m.decode(blob); err != nil {
   482  		return nil, err
   483  	}
   484  	var (
   485  		dec            = history{meta: &m}
   486  		accountData    = rawdb.ReadStateAccountHistory(reader, id)
   487  		storageData    = rawdb.ReadStateStorageHistory(reader, id)
   488  		accountIndexes = rawdb.ReadStateAccountIndex(reader, id)
   489  		storageIndexes = rawdb.ReadStateStorageIndex(reader, id)
   490  	)
   491  	if err := dec.decode(accountData, storageData, accountIndexes, storageIndexes); err != nil {
   492  		return nil, err
   493  	}
   494  	return &dec, nil
   495  }
   496  
   497  // writeHistory persists the state history with the provided state set.
   498  func writeHistory(writer ethdb.AncientWriter, dl *diffLayer) error {
   499  	// Short circuit if state set is not available.
   500  	if dl.states == nil {
   501  		return errors.New("state change set is not available")
   502  	}
   503  	var (
   504  		start   = time.Now()
   505  		history = newHistory(dl.rootHash(), dl.parentLayer().rootHash(), dl.block, dl.states)
   506  	)
   507  	accountData, storageData, accountIndex, storageIndex := history.encode()
   508  	dataSize := common.StorageSize(len(accountData) + len(storageData))
   509  	indexSize := common.StorageSize(len(accountIndex) + len(storageIndex))
   510  
   511  	// Write history data into five freezer table respectively.
   512  	rawdb.WriteStateHistory(writer, dl.stateID(), history.meta.encode(), accountIndex, storageIndex, accountData, storageData)
   513  
   514  	historyDataBytesMeter.Mark(int64(dataSize))
   515  	historyIndexBytesMeter.Mark(int64(indexSize))
   516  	historyBuildTimeMeter.UpdateSince(start)
   517  	log.Debug("Stored state history", "id", dl.stateID(), "block", dl.block, "data", dataSize, "index", indexSize, "elapsed", common.PrettyDuration(time.Since(start)))
   518  
   519  	return nil
   520  }
   521  
   522  // checkHistories retrieves a batch of meta objects with the specified range
   523  // and performs the callback on each item.
   524  func checkHistories(reader ethdb.AncientReader, start, count uint64, check func(*meta) error) error {
   525  	for count > 0 {
   526  		number := count
   527  		if number > 10000 {
   528  			number = 10000 // split the big read into small chunks
   529  		}
   530  		blobs, err := rawdb.ReadStateHistoryMetaList(reader, start, number)
   531  		if err != nil {
   532  			return err
   533  		}
   534  		for _, blob := range blobs {
   535  			var dec meta
   536  			if err := dec.decode(blob); err != nil {
   537  				return err
   538  			}
   539  			if err := check(&dec); err != nil {
   540  				return err
   541  			}
   542  		}
   543  		count -= uint64(len(blobs))
   544  		start += uint64(len(blobs))
   545  	}
   546  	return nil
   547  }
   548  
   549  // truncateFromHead removes the extra state histories from the head with the given
   550  // parameters. It returns the number of items removed from the head.
   551  func truncateFromHead(db ethdb.Batcher, store ethdb.AncientStore, nhead uint64) (int, error) {
   552  	ohead, err := store.Ancients()
   553  	if err != nil {
   554  		return 0, err
   555  	}
   556  	otail, err := store.Tail()
   557  	if err != nil {
   558  		return 0, err
   559  	}
   560  	// Ensure that the truncation target falls within the specified range.
   561  	if ohead < nhead || nhead < otail {
   562  		return 0, fmt.Errorf("out of range, tail: %d, head: %d, target: %d", otail, ohead, nhead)
   563  	}
   564  	// Short circuit if nothing to truncate.
   565  	if ohead == nhead {
   566  		return 0, nil
   567  	}
   568  	// Load the meta objects in range [nhead+1, ohead]
   569  	blobs, err := rawdb.ReadStateHistoryMetaList(store, nhead+1, ohead-nhead)
   570  	if err != nil {
   571  		return 0, err
   572  	}
   573  	batch := db.NewBatch()
   574  	for _, blob := range blobs {
   575  		var m meta
   576  		if err := m.decode(blob); err != nil {
   577  			return 0, err
   578  		}
   579  		rawdb.DeleteStateID(batch, m.root)
   580  	}
   581  	if err := batch.Write(); err != nil {
   582  		return 0, err
   583  	}
   584  	ohead, err = store.TruncateHead(nhead)
   585  	if err != nil {
   586  		return 0, err
   587  	}
   588  	return int(ohead - nhead), nil
   589  }
   590  
   591  // truncateFromTail removes the extra state histories from the tail with the given
   592  // parameters. It returns the number of items removed from the tail.
   593  func truncateFromTail(db ethdb.Batcher, store ethdb.AncientStore, ntail uint64) (int, error) {
   594  	ohead, err := store.Ancients()
   595  	if err != nil {
   596  		return 0, err
   597  	}
   598  	otail, err := store.Tail()
   599  	if err != nil {
   600  		return 0, err
   601  	}
   602  	// Ensure that the truncation target falls within the specified range.
   603  	if otail > ntail || ntail > ohead {
   604  		return 0, fmt.Errorf("out of range, tail: %d, head: %d, target: %d", otail, ohead, ntail)
   605  	}
   606  	// Short circuit if nothing to truncate.
   607  	if otail == ntail {
   608  		return 0, nil
   609  	}
   610  	// Load the meta objects in range [otail+1, ntail]
   611  	blobs, err := rawdb.ReadStateHistoryMetaList(store, otail+1, ntail-otail)
   612  	if err != nil {
   613  		return 0, err
   614  	}
   615  	batch := db.NewBatch()
   616  	for _, blob := range blobs {
   617  		var m meta
   618  		if err := m.decode(blob); err != nil {
   619  			return 0, err
   620  		}
   621  		rawdb.DeleteStateID(batch, m.root)
   622  	}
   623  	if err := batch.Write(); err != nil {
   624  		return 0, err
   625  	}
   626  	otail, err = store.TruncateTail(ntail)
   627  	if err != nil {
   628  		return 0, err
   629  	}
   630  	return int(ntail - otail), nil
   631  }