+
+ var clearCacheFuncs []func()
+ // save main chain blockHeaders
+ for _, blockHeader := range mainBlockHeaders {
+ bh := blockHeader
+ blockHash := bh.Hash()
+ binaryBlockHash, err := blockHash.MarshalText()
+ if err != nil {
+ return errors.Wrap(err, "Marshal block hash")
+ }
+
+ batch.Set(calcMainChainIndexPrefix(bh.Height), binaryBlockHash)
+ clearCacheFuncs = append(clearCacheFuncs, func() {
+ s.cache.removeMainChainHash(bh.Height)
+ })
+ }
+ batch.Write()
+ for _, clearCacheFunc := range clearCacheFuncs {
+ clearCacheFunc()
+ }
+
+ return nil
+}
+
+func calcCheckpointKey(height uint64, hash *bc.Hash) []byte {
+ buf := make([]byte, 8)
+ binary.BigEndian.PutUint64(buf, height)
+ key := append(CheckpointPrefix, buf...)
+ if hash != nil {
+ key = append(key, hash.Bytes()...)
+ }
+ return key
+}
+
+func (s *Store) GetCheckpoint(hash *bc.Hash) (*state.Checkpoint, error) {
+ header, err := s.GetBlockHeader(hash)
+ if err != nil {
+ return nil, err
+ }
+
+ data := s.db.Get(calcCheckpointKey(header.Height, hash))
+ checkpoint := &state.Checkpoint{}
+ if err := json.Unmarshal(data, checkpoint); err != nil {
+ return nil, err
+ }
+
+ checkpoint.SupLinks = append(checkpoint.SupLinks, header.SupLinks...)
+ return checkpoint, nil
+}
+
+// GetCheckpointsByHeight return all checkpoints of specified block height
+func (s *Store) GetCheckpointsByHeight(height uint64) ([]*state.Checkpoint, error) {
+ iter := s.db.IteratorPrefix(calcCheckpointKey(height, nil))
+ defer iter.Release()
+ return s.loadCheckpointsFromIter(iter)
+}
+
+// CheckpointsFromNode return all checkpoints from specified block height and hash
+func (s *Store) CheckpointsFromNode(height uint64, hash *bc.Hash) ([]*state.Checkpoint, error) {
+ startKey := calcCheckpointKey(height, hash)
+ iter := s.db.IteratorPrefixWithStart(CheckpointPrefix, startKey, false)
+
+ firstCheckpoint := &state.Checkpoint{}
+ if err := json.Unmarshal(iter.Value(), firstCheckpoint); err != nil {
+ return nil, err
+ }
+
+ checkpoints := []*state.Checkpoint{firstCheckpoint}
+ subs, err := s.loadCheckpointsFromIter(iter)
+ if err != nil {
+ return nil, err
+ }
+
+ checkpoints = append(checkpoints, subs...)
+ return checkpoints, nil
+}
+
+func (s *Store) loadCheckpointsFromIter(iter dbm.Iterator) ([]*state.Checkpoint, error) {
+ var checkpoints []*state.Checkpoint
+ defer iter.Release()
+ for iter.Next() {
+ checkpoint := &state.Checkpoint{}
+ if err := json.Unmarshal(iter.Value(), checkpoint); err != nil {
+ return nil, err
+ }
+
+ header, err := s.GetBlockHeader(&checkpoint.Hash)
+ if err != nil {
+ return nil, err
+ }
+
+ checkpoint.SupLinks = append(checkpoint.SupLinks, header.SupLinks...)
+ checkpoints = append(checkpoints, checkpoint)
+ }
+ return checkpoints, nil
+}
+
+// SaveCheckpoints bulk save multiple checkpoint
+func (s *Store) SaveCheckpoints(checkpoints []*state.Checkpoint) error {
+ batch := s.db.NewBatch()
+
+ if err := s.saveCheckpoints(batch, checkpoints); err != nil {
+ return err
+ }
+