Skip to content

Commit

Permalink
Snapshot V4
Browse files Browse the repository at this point in the history
  • Loading branch information
weiribao committed Aug 16, 2021
1 parent dadc576 commit 0f75ecc
Show file tree
Hide file tree
Showing 3 changed files with 169 additions and 4 deletions.
6 changes: 5 additions & 1 deletion rpc/backup.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,13 @@ func (t *ThetaRPCService) BackupSnapshot(args *BackupSnapshotArgs, result *Backu
snapshotFile, err := snapshot.ExportSnapshotV2(db, consensus, chain, snapshotDir, args.Height)
result.SnapshotFile = snapshotFile
return err
} else if args.Version == 3 {
snapshotFile, err := snapshot.ExportSnapshotV3(db, consensus, chain, snapshotDir, args.Height)
result.SnapshotFile = snapshotFile
return err
}

snapshotFile, err := snapshot.ExportSnapshotV3(db, consensus, chain, snapshotDir, args.Height)
snapshotFile, err := snapshot.ExportSnapshotV4(db, consensus, chain, snapshotDir, args.Height)
result.SnapshotFile = snapshotFile
return err
}
Expand Down
127 changes: 127 additions & 0 deletions snapshot/snapshot_export.go
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,133 @@ func ExportSnapshotV3(db database.Database, consensus *cns.ConsensusEngine, chai
return filename, nil
}

func ExportSnapshotV4(db database.Database, consensus *cns.ConsensusEngine, chain *blockchain.Chain, snapshotDir string, height uint64) (string, error) {
var lastFinalizedBlock *core.ExtendedBlock
if height != 0 {
blocks := chain.FindBlocksByHeight(height)
for _, block := range blocks {
if block.Status.IsDirectlyFinalized() {
lastFinalizedBlock = block
break
}
}
if lastFinalizedBlock == nil {
return "", fmt.Errorf("Can't find finalized block at height %v", height)
}
} else {
stub := consensus.GetSummary()
var err error
lastFinalizedBlock, err = chain.FindBlock(stub.LastFinalizedBlock)
if err != nil {
logger.Errorf("Failed to get block %v, %v", stub.LastFinalizedBlock, err)
return "", err
}
}
sv := state.NewStoreView(lastFinalizedBlock.Height, lastFinalizedBlock.BlockHeader.StateHash, db)

currentTime := time.Now().UTC()
filename := "theta_snapshot-" + strconv.FormatUint(sv.Height(), 10) + "-" + sv.Hash().String() + "-" + currentTime.Format("2006-01-02")
snapshotPath := path.Join(snapshotDir, filename)
file, err := os.Create(snapshotPath)
if err != nil {
return "", err
}
defer file.Close()
writer := bufio.NewWriter(file)

// --------------- Export the Header Section --------------- //

snapshotHeader := &core.SnapshotHeader{
Magic: core.SnapshotHeaderMagic,
Version: 4,
}
err = core.WriteSnapshotHeader(writer, snapshotHeader)
if err != nil {
return "", err
}

// ------------ Export the Last Checkpoint Section ------------- //

lastFinalizedBlockHeight := lastFinalizedBlock.Height
lastCheckpointHeight := common.LastCheckPointHeight(lastFinalizedBlockHeight)
lastCheckpoint := &core.LastCheckpoint{}

currHeight := lastFinalizedBlockHeight
currBlock := lastFinalizedBlock
for currHeight > lastCheckpointHeight {
parentHash := currBlock.Parent
currBlock, err = chain.FindBlock(parentHash)
if err != nil {
logger.Errorf("Failed to get intermediate block %v, %v", parentHash.Hex(), err)
return "", err
}
lastCheckpoint.IntermediateHeaders = append(lastCheckpoint.IntermediateHeaders, currBlock.Block.BlockHeader)
currHeight = currBlock.Height
}

lastCheckpointBlock := currBlock

lastCheckpoint.CheckpointHeader = lastCheckpointBlock.BlockHeader

err = core.WriteLastCheckpoint(writer, lastCheckpoint)
if err != nil {
return "", err
}

// -------------- Export the Metadata Section -------------- //

metadata := &core.SnapshotMetadata{}

parentBlock, err := chain.FindBlock(lastFinalizedBlock.Parent)
if err != nil {
return "", fmt.Errorf("Failed to find last finalized block's parent, %v", err)
}
childBlock, err := getAtLeastCommittedChild(lastFinalizedBlock, chain)
if err != nil {
return "", fmt.Errorf("Failed to find last finalized block's committed child, %v", err)
}

if lastFinalizedBlock.HCC.BlockHash != parentBlock.Hash() {
return "", fmt.Errorf("Parent block hash mismatch: %v vs %v", lastFinalizedBlock.HCC.BlockHash, parentBlock.Hash())
}

if childBlock.HCC.BlockHash != lastFinalizedBlock.Hash() {
return "", fmt.Errorf("Finalized block hash mismatch: %v vs %v", childBlock.HCC.BlockHash, lastFinalizedBlock.Hash())
}

childVoteSet := chain.FindVotesByHash(childBlock.Hash())

vcpProof, err := proveVCP(parentBlock, db)
if err != nil {
return "", fmt.Errorf("Failed to get VCP Proof")
}
metadata.TailTrio = core.SnapshotBlockTrio{
First: core.SnapshotFirstBlock{Header: parentBlock.BlockHeader, Proof: *vcpProof},
Second: core.SnapshotSecondBlock{Header: lastFinalizedBlock.BlockHeader},
Third: core.SnapshotThirdBlock{Header: childBlock.BlockHeader, VoteSet: childVoteSet},
}

err = core.WriteMetadata(writer, metadata)
if err != nil {
return "", err
}

// -------------- Export the StoreView Section -------------- //
// Last checkpoint storeview
if lastFinalizedBlock.Height != lastCheckpointHeight {
lastCheckpointSV := state.NewStoreView(lastCheckpointBlock.Height, lastCheckpointBlock.StateHash, db)
writeStoreViewV3(lastCheckpointSV, false, writer, db, common.Hash{})
}

// Parent block storeview
parentSV := state.NewStoreView(parentBlock.Height, parentBlock.StateHash, db)
writeStoreViewV3(parentSV, false, writer, db, common.Hash{})

writeStoreViewV3(sv, true, writer, db, parentSV.Hash())

return filename, nil
}

func proveVCP(block *core.ExtendedBlock, db database.Database) (*core.VCPProof, error) {
sv := state.NewStoreView(block.Height, block.StateHash, db)
vcpKey := state.ValidatorCandidatePoolKey()
Expand Down
40 changes: 37 additions & 3 deletions snapshot/snapshot_import.go
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ func loadSnapshot(snapshotFilePath string, db database.Database, logStr string)
}

var sv *state.StoreView
if snapshotHeader.Version == 3 {
if snapshotHeader.Version >= 3 {
err = loadStateV3(snapshotFile, db, fileSize, logStr)
if err != nil {
return nil, nil, err
Expand All @@ -265,8 +265,14 @@ func loadSnapshot(snapshotFilePath string, db database.Database, logStr string)

// ----------------------------- Validity Checks -------------------------- //

if err = checkSnapshot(sv, &metadata, db); err != nil {
return nil, nil, fmt.Errorf("Snapshot state validation failed: %v", err)
if snapshotVersion >= 4 {
if err = checkSnapshotV4(sv, &metadata, db); err != nil {
return nil, nil, fmt.Errorf("Snapshot state validation failed: %v", err)
}
} else {
if err = checkSnapshot(sv, &metadata, db); err != nil {
return nil, nil, fmt.Errorf("Snapshot state validation failed: %v", err)
}
}

// --------------------- Save Proofs and Tail Blocks --------------------- //
Expand Down Expand Up @@ -791,6 +797,34 @@ func checkSnapshot(sv *state.StoreView, metadata *core.SnapshotMetadata, db data
return nil
}

func checkSnapshotV4(sv *state.StoreView, metadata *core.SnapshotMetadata, db database.Database) error {
tailTrio := &metadata.TailTrio
secondBlock := tailTrio.Second.Header
expectedStateHash := sv.Hash()
if bytes.Compare(expectedStateHash.Bytes(), secondBlock.StateHash.Bytes()) != 0 {
return fmt.Errorf("StateHash not matching: %v vs %s",
expectedStateHash.Hex(), secondBlock.StateHash.Hex())
}

var valSet *core.ValidatorSet
var err error

first := tailTrio.First
valSet, err = getValidatorSetFromVCPProof(first.Header.StateHash, &first.Proof)
if err != nil {
return fmt.Errorf("Failed to retrieve validator set from VCP proof: %v", err)
}

logger.Infof("Validators of snapshost: %v", valSet)

err = checkTailTrio(sv, valSet, tailTrio)
if err != nil {
return err
}

return nil
}

func checkProofTrios(proofTrios []core.SnapshotBlockTrio, db database.Database) (*core.ValidatorSet, error) {
logger.Debugf("Check validator set change proofs...")

Expand Down

0 comments on commit 0f75ecc

Please sign in to comment.