Skip to content

Commit 373bd67

Browse files
committed
add verify execution result cmd
1 parent fb77044 commit 373bd67

File tree

7 files changed

+421
-89
lines changed

7 files changed

+421
-89
lines changed

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: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
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+
40+
Cmd.Flags().StringVar(&flagChunkDataPackDir, "chunk_data_pack_dir", "/var/flow/data/chunk_data_pack",
41+
"directory that stores the protocol state")
42+
43+
Cmd.Flags().Uint64Var(&flagLastK, "lastk", 1,
44+
"last k sealed blocks to verify")
45+
46+
Cmd.Flags().StringVar(&flagFromTo, "from_to", "",
47+
"the height range to verify blocks, i.e, 1-1000, 1000-2000, 2000-3000, etc.")
48+
}
49+
50+
func run(*cobra.Command, []string) {
51+
_ = flow.ChainID(flagChain).Chain()
52+
53+
if flagFromTo != "" {
54+
from, to, err := parseFromTo(flagFromTo)
55+
if err != nil {
56+
log.Fatal().Err(err).Msg("could not parse from_to")
57+
}
58+
59+
log.Info().Msgf("verifying range from %d to %d", from, to)
60+
err = verifier.VerifyRange(from, to, flow.Testnet, flagDatadir, flagChunkDataPackDir)
61+
if err != nil {
62+
log.Fatal().Err(err).Msg("could not verify last k height")
63+
}
64+
log.Info().Msgf("successfully verified range from %d to %d", from, to)
65+
66+
} else {
67+
log.Info().Msgf("verifying last %d sealed blocks", flagLastK)
68+
err := verifier.VerifyLastKHeight(flagLastK, flow.Testnet, flagDatadir, flagChunkDataPackDir)
69+
if err != nil {
70+
log.Fatal().Err(err).Msg("could not verify last k height")
71+
}
72+
73+
log.Info().Msgf("successfully verified last %d sealed blocks", flagLastK)
74+
}
75+
}
76+
77+
func parseFromTo(fromTo string) (from, to uint64, err error) {
78+
parts := strings.Split(fromTo, "-")
79+
if len(parts) != 2 {
80+
return 0, 0, fmt.Errorf("invalid format: expected 'from-to', got '%s'", fromTo)
81+
}
82+
83+
from, err = strconv.ParseUint(strings.TrimSpace(parts[0]), 10, 64)
84+
if err != nil {
85+
return 0, 0, fmt.Errorf("invalid 'from' value: %w", err)
86+
}
87+
88+
to, err = strconv.ParseUint(strings.TrimSpace(parts[1]), 10, 64)
89+
if err != nil {
90+
return 0, 0, fmt.Errorf("invalid 'to' value: %w", err)
91+
}
92+
93+
if from > to {
94+
return 0, 0, fmt.Errorf("'from' value (%d) must be less than or equal to 'to' value (%d)", from, to)
95+
}
96+
97+
return from, to, nil
98+
}

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-
}

engine/verification/fetcher/engine_test.go

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"github.com/onflow/flow-go/model/chunks"
1919
"github.com/onflow/flow-go/model/flow"
2020
"github.com/onflow/flow-go/model/verification"
21+
"github.com/onflow/flow-go/model/verification/convert"
2122
mempool "github.com/onflow/flow-go/module/mempool/mock"
2223
module "github.com/onflow/flow-go/module/mock"
2324
"github.com/onflow/flow-go/module/trace"
@@ -757,10 +758,10 @@ func mockVerifierEngine(t *testing.T,
757758
require.Equal(t, expected.Result.ID(), vc.Result.ID())
758759
require.Equal(t, expected.Header.ID(), vc.Header.ID())
759760

760-
isSystemChunk := fetcher.IsSystemChunk(vc.Chunk.Index, vc.Result)
761+
isSystemChunk := convert.IsSystemChunk(vc.Chunk.Index, vc.Result)
761762
require.Equal(t, isSystemChunk, vc.IsSystemChunk)
762763

763-
endState, err := fetcher.EndStateCommitment(vc.Result, vc.Chunk.Index, isSystemChunk)
764+
endState, err := convert.EndStateCommitment(vc.Result, vc.Chunk.Index, isSystemChunk)
764765
require.NoError(t, err)
765766

766767
require.Equal(t, endState, vc.EndState)
@@ -872,7 +873,7 @@ func chunkDataPackResponseFixture(t *testing.T,
872873
collection *flow.Collection,
873874
result *flow.ExecutionResult) *verification.ChunkDataPackResponse {
874875

875-
require.Equal(t, collection != nil, !fetcher.IsSystemChunk(chunk.Index, result), "only non-system chunks must have a collection")
876+
require.Equal(t, collection != nil, !convert.IsSystemChunk(chunk.Index, result), "only non-system chunks must have a collection")
876877

877878
return &verification.ChunkDataPackResponse{
878879
Locator: chunks.Locator{
@@ -917,7 +918,7 @@ func verifiableChunkFixture(t *testing.T,
917918
result *flow.ExecutionResult,
918919
chunkDataPack *flow.ChunkDataPack) *verification.VerifiableChunkData {
919920

920-
offsetForChunk, err := fetcher.TransactionOffsetForChunk(result.Chunks, chunk.Index)
921+
offsetForChunk, err := convert.TransactionOffsetForChunk(result.Chunks, chunk.Index)
921922
require.NoError(t, err)
922923

923924
// TODO: add end state
@@ -1000,7 +1001,7 @@ func completeChunkStatusListFixture(t *testing.T, chunkCount int, statusCount in
10001001
locators := unittest.ChunkStatusListToChunkLocatorFixture(statuses)
10011002

10021003
for _, status := range statuses {
1003-
if fetcher.IsSystemChunk(status.ChunkIndex, result) {
1004+
if convert.IsSystemChunk(status.ChunkIndex, result) {
10041005
// system-chunk should have a nil collection
10051006
continue
10061007
}
@@ -1012,7 +1013,7 @@ func completeChunkStatusListFixture(t *testing.T, chunkCount int, statusCount in
10121013

10131014
func TestTransactionOffsetForChunk(t *testing.T) {
10141015
t.Run("first chunk index always returns zero offset", func(t *testing.T) {
1015-
offsetForChunk, err := fetcher.TransactionOffsetForChunk([]*flow.Chunk{nil}, 0)
1016+
offsetForChunk, err := convert.TransactionOffsetForChunk([]*flow.Chunk{nil}, 0)
10161017
require.NoError(t, err)
10171018
assert.Equal(t, uint32(0), offsetForChunk)
10181019
})
@@ -1042,19 +1043,19 @@ func TestTransactionOffsetForChunk(t *testing.T) {
10421043
},
10431044
}
10441045

1045-
offsetForChunk, err := fetcher.TransactionOffsetForChunk(chunksList, 0)
1046+
offsetForChunk, err := convert.TransactionOffsetForChunk(chunksList, 0)
10461047
require.NoError(t, err)
10471048
assert.Equal(t, uint32(0), offsetForChunk)
10481049

1049-
offsetForChunk, err = fetcher.TransactionOffsetForChunk(chunksList, 1)
1050+
offsetForChunk, err = convert.TransactionOffsetForChunk(chunksList, 1)
10501051
require.NoError(t, err)
10511052
assert.Equal(t, uint32(1), offsetForChunk)
10521053

1053-
offsetForChunk, err = fetcher.TransactionOffsetForChunk(chunksList, 2)
1054+
offsetForChunk, err = convert.TransactionOffsetForChunk(chunksList, 2)
10541055
require.NoError(t, err)
10551056
assert.Equal(t, uint32(3), offsetForChunk)
10561057

1057-
offsetForChunk, err = fetcher.TransactionOffsetForChunk(chunksList, 3)
1058+
offsetForChunk, err = convert.TransactionOffsetForChunk(chunksList, 3)
10581059
require.NoError(t, err)
10591060
assert.Equal(t, uint32(6), offsetForChunk)
10601061
})
@@ -1063,7 +1064,7 @@ func TestTransactionOffsetForChunk(t *testing.T) {
10631064

10641065
chunksList := make([]*flow.Chunk, 2)
10651066

1066-
_, err := fetcher.TransactionOffsetForChunk(chunksList, 2)
1067+
_, err := convert.TransactionOffsetForChunk(chunksList, 2)
10671068
require.Error(t, err)
10681069
})
10691070
}

0 commit comments

Comments
 (0)