Skip to content

Commit c96b754

Browse files
authored
Merge pull request #6746 from onflow/leo/cmd-add-verify-execution-result
[Util] Add verify execution result cmd
2 parents fb77044 + df4891d commit c96b754

File tree

9 files changed

+473
-117
lines changed

9 files changed

+473
-117
lines changed

cmd/scaffold.go

Lines changed: 4 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,7 @@ import (
3333
"github.com/onflow/flow-go/cmd/build"
3434
"github.com/onflow/flow-go/config"
3535
"github.com/onflow/flow-go/consensus/hotstuff/persister"
36-
"github.com/onflow/flow-go/fvm"
37-
"github.com/onflow/flow-go/fvm/environment"
36+
"github.com/onflow/flow-go/fvm/initialize"
3837
"github.com/onflow/flow-go/model/flow"
3938
"github.com/onflow/flow-go/model/flow/filter"
4039
"github.com/onflow/flow-go/module"
@@ -1522,32 +1521,9 @@ func (fnb *FlowNodeBuilder) initLocal() error {
15221521
}
15231522

15241523
func (fnb *FlowNodeBuilder) initFvmOptions() {
1525-
blockFinder := environment.NewBlockFinder(fnb.Storage.Headers)
1526-
vmOpts := []fvm.Option{
1527-
fvm.WithChain(fnb.RootChainID.Chain()),
1528-
fvm.WithBlocks(blockFinder),
1529-
fvm.WithAccountStorageLimit(true),
1530-
}
1531-
switch fnb.RootChainID {
1532-
case flow.Testnet,
1533-
flow.Sandboxnet,
1534-
flow.Previewnet,
1535-
flow.Mainnet:
1536-
vmOpts = append(vmOpts,
1537-
fvm.WithTransactionFeesEnabled(true),
1538-
)
1539-
}
1540-
switch fnb.RootChainID {
1541-
case flow.Testnet,
1542-
flow.Sandboxnet,
1543-
flow.Previewnet,
1544-
flow.Localnet,
1545-
flow.Benchnet:
1546-
vmOpts = append(vmOpts,
1547-
fvm.WithContractDeploymentRestricted(false),
1548-
)
1549-
}
1550-
fnb.FvmOptions = vmOpts
1524+
fnb.FvmOptions = initialize.InitFvmOptions(
1525+
fnb.RootChainID, fnb.Storage.Headers,
1526+
)
15511527
}
15521528

15531529
// handleModules initializes the given module.

cmd/util/cmd/root.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import (
4141
"github.com/onflow/flow-go/cmd/util/cmd/snapshot"
4242
system_addresses "github.com/onflow/flow-go/cmd/util/cmd/system-addresses"
4343
truncate_database "github.com/onflow/flow-go/cmd/util/cmd/truncate-database"
44+
verify_execution_result "github.com/onflow/flow-go/cmd/util/cmd/verify_execution_result"
4445
"github.com/onflow/flow-go/cmd/util/cmd/version"
4546
"github.com/onflow/flow-go/module/profiler"
4647
)
@@ -126,6 +127,7 @@ func addCommands() {
126127
rootCmd.AddCommand(debug_script.Cmd)
127128
rootCmd.AddCommand(generate_authorization_fixes.Cmd)
128129
rootCmd.AddCommand(evm_state_exporter.Cmd)
130+
rootCmd.AddCommand(verify_execution_result.Cmd)
129131
}
130132

131133
func initConfig() {
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
package verify
2+
3+
import (
4+
"fmt"
5+
"strconv"
6+
"strings"
7+
8+
"github.com/rs/zerolog/log"
9+
"github.com/spf13/cobra"
10+
11+
"github.com/onflow/flow-go/engine/verification/verifier"
12+
"github.com/onflow/flow-go/model/flow"
13+
)
14+
15+
var (
16+
flagLastK uint64
17+
flagDatadir string
18+
flagChunkDataPackDir string
19+
flagChain string
20+
flagFromTo string
21+
)
22+
23+
// # verify the last 100 sealed blocks
24+
// ./util verify_execution_result --chain flow-testnet --datadir /var/flow/data/protocol --chunk_data_pack_dir /var/flow/data/chunk_data_pack --lastk 100
25+
// # verify the blocks from height 2000 to 3000
26+
// ./util verify_execution_result --chain flow-testnet --datadir /var/flow/data/protocol --chunk_data_pack_dir /var/flow/data/chunk_data_pack --from_to 2000-3000
27+
var Cmd = &cobra.Command{
28+
Use: "verify-execution-result",
29+
Short: "verify block execution by verifying all chunks in the result",
30+
Run: run,
31+
}
32+
33+
func init() {
34+
Cmd.Flags().StringVar(&flagChain, "chain", "", "Chain name")
35+
_ = Cmd.MarkFlagRequired("chain")
36+
37+
Cmd.Flags().StringVar(&flagDatadir, "datadir", "/var/flow/data/protocol",
38+
"directory that stores the protocol state")
39+
_ = Cmd.MarkFlagRequired("datadir")
40+
41+
Cmd.Flags().StringVar(&flagChunkDataPackDir, "chunk_data_pack_dir", "/var/flow/data/chunk_data_pack",
42+
"directory that stores the protocol state")
43+
_ = Cmd.MarkFlagRequired("chunk_data_pack_dir")
44+
45+
Cmd.Flags().Uint64Var(&flagLastK, "lastk", 1,
46+
"last k sealed blocks to verify")
47+
48+
Cmd.Flags().StringVar(&flagFromTo, "from_to", "",
49+
"the height range to verify blocks (inclusive), i.e, 1-1000, 1000-2000, 2000-3000, etc.")
50+
}
51+
52+
func run(*cobra.Command, []string) {
53+
chainID := flow.ChainID(flagChain)
54+
_ = chainID.Chain()
55+
56+
if flagFromTo != "" {
57+
from, to, err := parseFromTo(flagFromTo)
58+
if err != nil {
59+
log.Fatal().Err(err).Msg("could not parse from_to")
60+
}
61+
62+
log.Info().Msgf("verifying range from %d to %d", from, to)
63+
err = verifier.VerifyRange(from, to, chainID, flagDatadir, flagChunkDataPackDir)
64+
if err != nil {
65+
log.Fatal().Err(err).Msgf("could not verify range from %d to %d", from, to)
66+
}
67+
log.Info().Msgf("successfully verified range from %d to %d", from, to)
68+
69+
} else {
70+
log.Info().Msgf("verifying last %d sealed blocks", flagLastK)
71+
err := verifier.VerifyLastKHeight(flagLastK, chainID, flagDatadir, flagChunkDataPackDir)
72+
if err != nil {
73+
log.Fatal().Err(err).Msg("could not verify last k height")
74+
}
75+
76+
log.Info().Msgf("successfully verified last %d sealed blocks", flagLastK)
77+
}
78+
}
79+
80+
func parseFromTo(fromTo string) (from, to uint64, err error) {
81+
parts := strings.Split(fromTo, "-")
82+
if len(parts) != 2 {
83+
return 0, 0, fmt.Errorf("invalid format: expected 'from-to', got '%s'", fromTo)
84+
}
85+
86+
from, err = strconv.ParseUint(strings.TrimSpace(parts[0]), 10, 64)
87+
if err != nil {
88+
return 0, 0, fmt.Errorf("invalid 'from' value: %w", err)
89+
}
90+
91+
to, err = strconv.ParseUint(strings.TrimSpace(parts[1]), 10, 64)
92+
if err != nil {
93+
return 0, 0, fmt.Errorf("invalid 'to' value: %w", err)
94+
}
95+
96+
if from > to {
97+
return 0, 0, fmt.Errorf("'from' value (%d) must be less than or equal to 'to' value (%d)", from, to)
98+
}
99+
100+
return from, to, nil
101+
}

engine/execution/computation/execution_verification_test.go

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ import (
2525
"github.com/onflow/flow-go/engine/execution/testutil"
2626
"github.com/onflow/flow-go/engine/execution/utils"
2727
"github.com/onflow/flow-go/engine/testutil/mocklocal"
28-
"github.com/onflow/flow-go/engine/verification/fetcher"
2928
"github.com/onflow/flow-go/fvm"
3029
"github.com/onflow/flow-go/fvm/blueprints"
3130
"github.com/onflow/flow-go/fvm/environment"
@@ -36,6 +35,7 @@ import (
3635
"github.com/onflow/flow-go/ledger/complete/wal/fixtures"
3736
"github.com/onflow/flow-go/model/flow"
3837
"github.com/onflow/flow-go/model/verification"
38+
"github.com/onflow/flow-go/model/verification/convert"
3939
"github.com/onflow/flow-go/module/chunks"
4040
"github.com/onflow/flow-go/module/executiondatasync/execution_data"
4141
exedataprovider "github.com/onflow/flow-go/module/executiondatasync/provider"
@@ -69,7 +69,7 @@ func Test_ExecutionMatchesVerification(t *testing.T) {
6969
`access(all) contract Foo {
7070
access(all) event FooEvent(x: Int, y: Int)
7171
72-
access(all) fun emitEvent() {
72+
access(all) fun emitEvent() {
7373
emit FooEvent(x: 2, y: 1)
7474
}
7575
}`), "Foo")
@@ -113,7 +113,7 @@ func Test_ExecutionMatchesVerification(t *testing.T) {
113113
`access(all) contract Foo {
114114
access(all) event FooEvent(x: Int, y: Int)
115115
116-
access(all) fun emitEvent() {
116+
access(all) fun emitEvent() {
117117
emit FooEvent(x: 2, y: 1)
118118
}
119119
}`), "Foo")
@@ -585,34 +585,34 @@ func TestTransactionFeeDeduction(t *testing.T) {
585585
//
586586
// The withdraw amount and the account from getAccount
587587
// would be the parameters to the transaction
588-
588+
589589
import FungibleToken from 0x%s
590590
import FlowToken from 0x%s
591-
591+
592592
transaction(amount: UFix64, to: Address) {
593-
593+
594594
// The Vault resource that holds the tokens that are being transferred
595595
let sentVault: @{FungibleToken.Vault}
596-
596+
597597
prepare(signer: auth(BorrowValue) &Account) {
598-
598+
599599
// Get a reference to the signer's stored vault
600600
let vaultRef = signer.storage.borrow<auth(FungibleToken.Withdraw) &FlowToken.Vault>(from: /storage/flowTokenVault)
601601
?? panic("Could not borrow reference to the owner's Vault!")
602-
602+
603603
// Withdraw tokens from the signer's stored vault
604604
self.sentVault <- vaultRef.withdraw(amount: amount)
605605
}
606-
606+
607607
execute {
608-
608+
609609
// Get the recipient's public account object
610610
let recipient = getAccount(to)
611-
611+
612612
// Get a reference to the recipient's Receiver
613613
let receiverRef = recipient.capabilities.borrow<&{FungibleToken.Receiver}>(/public/flowTokenReceiver)
614614
?? panic("Could not borrow receiver reference to the recipient's Vault")
615-
615+
616616
// Deposit the withdrawn tokens in the recipient's receiver
617617
receiverRef.deposit(from: <-self.sentVault)
618618
}
@@ -840,7 +840,7 @@ func executeBlockAndVerifyWithParameters(t *testing.T,
840840

841841
for i, chunk := range er.Chunks {
842842
isSystemChunk := i == er.Chunks.Len()-1
843-
offsetForChunk, err := fetcher.TransactionOffsetForChunk(er.Chunks, chunk.Index)
843+
offsetForChunk, err := convert.TransactionOffsetForChunk(er.Chunks, chunk.Index)
844844
require.NoError(t, err)
845845

846846
vcds[i] = &verification.VerifiableChunkData{

engine/verification/fetcher/engine.go

Lines changed: 10 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"github.com/onflow/flow-go/model/flow"
1313
"github.com/onflow/flow-go/model/flow/filter"
1414
"github.com/onflow/flow-go/model/verification"
15+
"github.com/onflow/flow-go/model/verification/convert"
1516
"github.com/onflow/flow-go/module"
1617
"github.com/onflow/flow-go/module/mempool"
1718
"github.com/onflow/flow-go/module/trace"
@@ -259,7 +260,7 @@ func (e *Engine) HandleChunkDataPack(originID flow.Identifier, response *verific
259260
Uint64("block_height", status.BlockHeight).
260261
Hex("result_id", logging.ID(resultID)).
261262
Uint64("chunk_index", status.ChunkIndex).
262-
Bool("system_chunk", IsSystemChunk(status.ChunkIndex, status.ExecutionResult)).
263+
Bool("system_chunk", convert.IsSystemChunk(status.ChunkIndex, status.ExecutionResult)).
263264
Logger()
264265

265266
span, ctx := e.tracer.StartBlockSpan(context.Background(), status.ExecutionResult.BlockID, trace.VERFetcherHandleChunkDataPack)
@@ -413,7 +414,7 @@ func (e Engine) validateCollectionID(
413414
result *flow.ExecutionResult,
414415
chunk *flow.Chunk) error {
415416

416-
if IsSystemChunk(chunk.Index, result) {
417+
if convert.IsSystemChunk(chunk.Index, result) {
417418
return e.validateSystemChunkCollection(chunkDataPack)
418419
}
419420

@@ -550,29 +551,13 @@ func (e *Engine) makeVerifiableChunkData(chunk *flow.Chunk,
550551
chunkDataPack *flow.ChunkDataPack,
551552
) (*verification.VerifiableChunkData, error) {
552553

553-
// system chunk is the last chunk
554-
isSystemChunk := IsSystemChunk(chunk.Index, result)
555-
556-
endState, err := EndStateCommitment(result, chunk.Index, isSystemChunk)
557-
if err != nil {
558-
return nil, fmt.Errorf("could not compute end state of chunk: %w", err)
559-
}
560-
561-
transactionOffset, err := TransactionOffsetForChunk(result.Chunks, chunk.Index)
562-
if err != nil {
563-
return nil, fmt.Errorf("cannot compute transaction offset for chunk: %w", err)
564-
}
565-
566-
return &verification.VerifiableChunkData{
567-
IsSystemChunk: isSystemChunk,
568-
Chunk: chunk,
569-
Header: header,
570-
Snapshot: snapshot,
571-
Result: result,
572-
ChunkDataPack: chunkDataPack,
573-
EndState: endState,
574-
TransactionOffset: transactionOffset,
575-
}, nil
554+
return convert.FromChunkDataPack(
555+
chunk,
556+
chunkDataPack,
557+
header,
558+
snapshot,
559+
result,
560+
)
576561
}
577562

578563
// requestChunkDataPack creates and dispatches a chunk data pack request to the requester engine.
@@ -661,42 +646,3 @@ func executorsOf(receipts []*flow.ExecutionReceipt, resultID flow.Identifier) (f
661646

662647
return agrees, disagrees
663648
}
664-
665-
// EndStateCommitment computes the end state of the given chunk.
666-
func EndStateCommitment(result *flow.ExecutionResult, chunkIndex uint64, systemChunk bool) (flow.StateCommitment, error) {
667-
var endState flow.StateCommitment
668-
if systemChunk {
669-
var err error
670-
// last chunk in a result is the system chunk and takes final state commitment
671-
endState, err = result.FinalStateCommitment()
672-
if err != nil {
673-
return flow.DummyStateCommitment, fmt.Errorf("can not read final state commitment, likely a bug:%w", err)
674-
}
675-
} else {
676-
// any chunk except last takes the subsequent chunk's start state
677-
endState = result.Chunks[chunkIndex+1].StartState
678-
}
679-
680-
return endState, nil
681-
}
682-
683-
// TransactionOffsetForChunk calculates transaction offset for a given chunk which is the index of the first
684-
// transaction of this chunk within the whole block
685-
func TransactionOffsetForChunk(chunks flow.ChunkList, chunkIndex uint64) (uint32, error) {
686-
if int(chunkIndex) > len(chunks)-1 {
687-
return 0, fmt.Errorf("chunk list out of bounds, len %d asked for chunk %d", len(chunks), chunkIndex)
688-
}
689-
var offset uint32 = 0
690-
for i := 0; i < int(chunkIndex); i++ {
691-
offset += uint32(chunks[i].NumberOfTransactions)
692-
}
693-
return offset, nil
694-
}
695-
696-
// IsSystemChunk returns true if `chunkIndex` points to a system chunk in `result`.
697-
// Otherwise, it returns false.
698-
// In the current version, a chunk is a system chunk if it is the last chunk of the
699-
// execution result.
700-
func IsSystemChunk(chunkIndex uint64, result *flow.ExecutionResult) bool {
701-
return chunkIndex == uint64(len(result.Chunks)-1)
702-
}

0 commit comments

Comments
 (0)