lotus/chain/checkpoint.go

95 lines
2.2 KiB
Go
Raw Normal View History

package chain
import (
"context"
"encoding/json"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/node/modules/dtypes"
"github.com/ipfs/go-datastore"
"golang.org/x/xerrors"
)
var CheckpointKey = datastore.NewKey("/chain/checks")
func loadCheckpoint(ds dtypes.MetadataDS) (types.TipSetKey, error) {
haveChks, err := ds.Has(CheckpointKey)
if err != nil {
return types.EmptyTSK, err
}
if !haveChks {
return types.EmptyTSK, nil
}
tskBytes, err := ds.Get(CheckpointKey)
if err != nil {
return types.EmptyTSK, err
}
var tsk types.TipSetKey
err = json.Unmarshal(tskBytes, &tsk)
if err != nil {
return types.EmptyTSK, err
}
return tsk, err
}
func (syncer *Syncer) SyncCheckpoint(ctx context.Context, tsk types.TipSetKey) error {
if tsk == types.EmptyTSK {
return xerrors.Errorf("called with empty tsk")
}
ts, err := syncer.ChainStore().LoadTipSet(tsk)
if err != nil {
tss, err := syncer.Exchange.GetBlocks(ctx, tsk, 1)
if err != nil {
return xerrors.Errorf("failed to fetch tipset: %w", err)
} else if len(tss) != 1 {
return xerrors.Errorf("expected 1 tipset, got %d", len(tss))
}
ts = tss[0]
}
hts := syncer.ChainStore().GetHeaviestTipSet()
anc, err := syncer.ChainStore().IsAncestorOf(ts, hts)
if err != nil {
return xerrors.Errorf("cannot determine whether checkpoint tipset is in main-chain: %w", err)
}
if !hts.Equals(ts) && !anc {
if err := syncer.collectChain(ctx, ts, hts); err != nil {
return xerrors.Errorf("failed to collect chain for checkpoint: %w", err)
}
if err := syncer.ChainStore().SetHead(ts); err != nil {
return xerrors.Errorf("failed to set the chain head: %w", err)
}
}
syncer.checkptLk.Lock()
defer syncer.checkptLk.Unlock()
tskBytes, err := json.Marshal(tsk)
if err != nil {
return err
}
err = syncer.ds.Put(CheckpointKey, tskBytes)
if err != nil {
return err
}
// TODO: This is racy. as there may be a concurrent sync in progress.
// The only real solution is to checkpoint inside the chainstore, not here.
syncer.checkpt = tsk
return nil
}
func (syncer *Syncer) GetCheckpoint() types.TipSetKey {
syncer.checkptLk.Lock()
defer syncer.checkptLk.Unlock()
return syncer.checkpt
}