Skip to content

Commit

Permalink
Merge pull request #4825 from onflow/leo/validate-checkpoint-root-hash
Browse files Browse the repository at this point in the history
Validate checkpoint root hash
  • Loading branch information
zhangchiqing authored Oct 18, 2023
2 parents 29cd27f + ade2d8a commit 14e7bdd
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 11 deletions.
5 changes: 2 additions & 3 deletions cmd/bootstrap/run/execution_state.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,11 @@ import (
ledger "github.com/onflow/flow-go/ledger/complete"
"github.com/onflow/flow-go/ledger/complete/mtrie/trie"
"github.com/onflow/flow-go/ledger/complete/wal"
bootstrapFilenames "github.com/onflow/flow-go/model/bootstrap"
"github.com/onflow/flow-go/model/flow"
"github.com/onflow/flow-go/module/metrics"
)

const bootstrapCheckpointFile = "bootstrap-checkpoint"

func GenerateExecutionState(
dbDir string,
accountKey flow.AccountPublicKey,
Expand Down Expand Up @@ -75,7 +74,7 @@ func GenerateExecutionState(
return flow.DummyStateCommitment, fmt.Errorf("bootstraping failed to produce a checkpoint for trie %v", stateCommitment)
}

err = wal.StoreCheckpointV6([]*trie.MTrie{matchTrie}, dbDir, bootstrapCheckpointFile, zerolog.Nop(), 1)
err = wal.StoreCheckpointV6([]*trie.MTrie{matchTrie}, dbDir, bootstrapFilenames.FilenameWALRootCheckpoint, zerolog.Nop(), 1)
if err != nil {
return flow.DummyStateCommitment, fmt.Errorf("failed to store bootstrap checkpoint: %w", err)
}
Expand Down
13 changes: 11 additions & 2 deletions cmd/execution_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ import (
"github.com/onflow/flow-go/fvm"
"github.com/onflow/flow-go/fvm/storage/snapshot"
"github.com/onflow/flow-go/fvm/systemcontracts"
ledgerpkg "github.com/onflow/flow-go/ledger"
"github.com/onflow/flow-go/ledger/common/pathfinder"
ledger "github.com/onflow/flow-go/ledger/complete"
"github.com/onflow/flow-go/ledger/complete/wal"
Expand Down Expand Up @@ -1126,15 +1127,23 @@ func (exeNode *ExecutionNode) LoadBootstrapper(node *NodeConfig) error {

// if the execution database does not exist, then we need to bootstrap the execution database.
if !bootstrapped {
err := wal.CheckpointHasRootHash(
node.Logger,
path.Join(node.BootstrapDir, bootstrapFilenames.DirnameExecutionState),
bootstrapFilenames.FilenameWALRootCheckpoint,
ledgerpkg.RootHash(node.RootSeal.FinalState),
)
if err != nil {
return err
}

// when bootstrapping, the bootstrap folder must have a checkpoint file
// we need to cover this file to the trie folder to restore the trie to restore the execution state.
err = copyBootstrapState(node.BootstrapDir, exeNode.exeConf.triedir)
if err != nil {
return fmt.Errorf("could not load bootstrap state from checkpoint file: %w", err)
}

// TODO: check that the checkpoint file contains the root block's statecommit hash

err = bootstrapper.BootstrapExecutionDatabase(node.DB, node.RootSeal)
if err != nil {
return fmt.Errorf("could not bootstrap execution database: %w", err)
Expand Down
24 changes: 24 additions & 0 deletions ledger/complete/wal/checkpoint_v6_reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
var ErrEOFNotReached = errors.New("expect to reach EOF, but actually didn't")

var ReadTriesRootHash = readTriesRootHash
var CheckpointHasRootHash = checkpointHasRootHash

// readCheckpointV6 reads checkpoint file from a main file and 17 file parts.
// the main file stores:
Expand Down Expand Up @@ -662,6 +663,29 @@ func readTriesRootHash(logger zerolog.Logger, dir string, fileName string) (
return trieRootsToReturn, errToReturn
}

// checkpointHasRootHash check if the given checkpoint file contains the expected root hash
func checkpointHasRootHash(logger zerolog.Logger, bootstrapDir, filename string, expectedRootHash ledger.RootHash) error {
roots, err := ReadTriesRootHash(logger, bootstrapDir, filename)
if err != nil {
return fmt.Errorf("could not read checkpoint root hash: %w", err)
}

if len(roots) == 0 {
return fmt.Errorf("no root hash found in checkpoint file")
}

for i, root := range roots {
if root == expectedRootHash {
logger.Info().Msgf("found matching checkpoint root hash at index: %v, checkpoint total trie roots: %v",
i, len(roots))
// found the expected commit
return nil
}
}

return fmt.Errorf("could not find expected root hash %v in checkpoint file which contains: %v ", expectedRootHash, roots)
}

func readFileHeader(reader io.Reader) (uint16, uint16, error) {
bytes := make([]byte, encMagicSize+encVersionSize)
_, err := io.ReadFull(reader, bytes)
Expand Down
30 changes: 24 additions & 6 deletions ledger/complete/wal/checkpoint_v6_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -243,10 +243,10 @@ func TestGetNodesByIndex(t *testing.T) {
ns[i] = randomNode()
}
subtrieNodes := [][]*node.Node{
[]*node.Node{ns[0], ns[1]},
[]*node.Node{ns[2]},
[]*node.Node{},
[]*node.Node{},
{ns[0], ns[1]},
{ns[2]},
{},
{},
}
topLevelNodes := []*node.Node{nil, ns[3]}
totalSubTrieNodeCount := computeTotalSubTrieNodeCount(subtrieNodes)
Expand Down Expand Up @@ -598,7 +598,7 @@ func TestReadCheckpointRootHash(t *testing.T) {
logger := unittest.Logger()
require.NoErrorf(t, StoreCheckpointV6Concurrently(tries, dir, fileName, logger), "fail to store checkpoint")

trieRoots, err := readTriesRootHash(logger, dir, fileName)
trieRoots, err := ReadTriesRootHash(logger, dir, fileName)
require.NoError(t, err)
for i, root := range trieRoots {
expectedHash := tries[i].RootHash()
Expand All @@ -615,7 +615,7 @@ func TestReadCheckpointRootHashMulti(t *testing.T) {
logger := unittest.Logger()
require.NoErrorf(t, StoreCheckpointV6Concurrently(tries, dir, fileName, logger), "fail to store checkpoint")

trieRoots, err := readTriesRootHash(logger, dir, fileName)
trieRoots, err := ReadTriesRootHash(logger, dir, fileName)
require.NoError(t, err)
for i, root := range trieRoots {
expectedHash := tries[i].RootHash()
Expand All @@ -624,3 +624,21 @@ func TestReadCheckpointRootHashMulti(t *testing.T) {
require.Equal(t, len(tries), len(trieRoots))
})
}

func TestCheckpointHasRootHash(t *testing.T) {
unittest.RunWithTempDir(t, func(dir string) {
tries := createMultipleRandomTries(t)
fileName := "checkpoint"
logger := unittest.Logger()
require.NoErrorf(t, StoreCheckpointV6Concurrently(tries, dir, fileName, logger), "fail to store checkpoint")

trieRoots, err := ReadTriesRootHash(logger, dir, fileName)
require.NoError(t, err)
for _, root := range trieRoots {
require.NoError(t, CheckpointHasRootHash(logger, dir, fileName, root))
}

nonExist := ledger.RootHash(unittest.StateCommitmentFixture())
require.Error(t, CheckpointHasRootHash(logger, dir, fileName, nonExist))
})
}

0 comments on commit 14e7bdd

Please sign in to comment.