-
Notifications
You must be signed in to change notification settings - Fork 207
[FVM] Add token movements inspection #8424
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
janezpodhostnik
wants to merge
42
commits into
master
Choose a base branch
from
janez/transaction-result-inspection
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+2,094
−17
Open
Changes from 32 commits
Commits
Show all changes
42 commits
Select commit
Hold shift + click to select a range
5917d36
Add token movements inspection
janezpodhostnik 946dd3f
cleanup
janezpodhostnik 6a67aeb
fix overflow todo
zhangchiqing 709c710
Use updated storageMap.ReadOnlyLoadedValueIterator()
fxamacker 907bffc
Merge branch 'master' into janez/transaction-result-inspection
fxamacker 915ce3b
Apply suggestions from code review
zhangchiqing 1ce426f
Merge branch 'master' into janez/transaction-result-inspection
janezpodhostnik 0b331f5
add token movements inspect
zhangchiqing ed80ade
fix lint issue
zhangchiqing 2c2fb83
add subcommand for inspect token movements
zhangchiqing 5b2ea7e
update logger
zhangchiqing cb418d2
Add log with trace to debug token diff error
fxamacker 488bfc6
Workaround for unloaded domain storage map in token change
fxamacker f090e09
Log account token diff
fxamacker a806372
merge and cadence upgrade fixes
janezpodhostnik d0ebab5
print token changes
zhangchiqing 07d9985
add logging
zhangchiqing 264a159
fix logging
zhangchiqing d74e24c
add logging
zhangchiqing 481a195
log tx execution in token movements
zhangchiqing 129999d
update util comments
zhangchiqing 12d55f8
improve logging for token movements execution
zhangchiqing d0d5a6e
fix lint
zhangchiqing bd3df91
debug with info level log
zhangchiqing bc9fc8f
add more logs
zhangchiqing 3f91465
fix token tracking enable flag
zhangchiqing a9b7524
log inspection results
zhangchiqing 80efae5
add inspection to fvm
zhangchiqing fa64f08
fix tests
zhangchiqing 125041e
fix panic
zhangchiqing 8d03f46
fix mutation
zhangchiqing 6d1ac95
fix merge
janezpodhostnik 0992f0b
Merge branch 'master' into janez/transaction-result-inspection
janezpodhostnik 1186591
log-level fixes + cleanup
janezpodhostnik a321b81
inspection fixes
janezpodhostnik 36fcbe2
cleanup PR
janezpodhostnik ea0b48a
code cleanup
janezpodhostnik 0e9b03f
system chunk in inspect-token-movements
janezpodhostnik db0a61b
handle EVM flow transitions
janezpodhostnik 51f87f7
Merge branch 'master' into janez/transaction-result-inspection
janezpodhostnik cc2f6c6
merge fix
janezpodhostnik ab216e4
address review comments
janezpodhostnik File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,227 @@ | ||
| package inspect | ||
|
|
||
| import ( | ||
| "errors" | ||
| "fmt" | ||
| "strconv" | ||
| "strings" | ||
|
|
||
| "github.com/rs/zerolog" | ||
| "github.com/rs/zerolog/log" | ||
| "github.com/spf13/cobra" | ||
|
|
||
| "github.com/onflow/flow-go/cmd/util/cmd/common" | ||
| "github.com/onflow/flow-go/fvm/inspection" | ||
| "github.com/onflow/flow-go/model/flow" | ||
| "github.com/onflow/flow-go/state/protocol" | ||
| "github.com/onflow/flow-go/storage" | ||
| ) | ||
|
|
||
| var ( | ||
| flagDatadir string | ||
| flagChunkDataPackDir string | ||
| flagChain string | ||
| flagFromTo string | ||
| flagLastK uint64 | ||
| ) | ||
|
|
||
| // Cmd is the command for inspecting token movements in executed blocks | ||
| // by reading chunk data packs and running the token changes inspector. | ||
| // | ||
| // # inspect the last 100 sealed blocks | ||
| // ./util inspect-token-movements --chain flow-mainnet --datadir /var/flow/data/protocol --chunk_data_pack_dir /var/flow/data/chunk_data_packs --lastk 100 | ||
| // # inspect the blocks from height 2000 to 3000 | ||
| // ./util inspect-token-movements --chain flow-mainnet --datadir /var/flow/data/protocol --chunk_data_pack_dir /var/flow/data/chunk_data_packs --from_to 2000_3000 | ||
| var Cmd = &cobra.Command{ | ||
| Use: "inspect-token-movements", | ||
| Short: "inspect token movements by analyzing chunk data packs for unaccounted token mints/burns", | ||
| Run: run, | ||
| } | ||
|
|
||
| func init() { | ||
| Cmd.Flags().StringVar(&flagChain, "chain", "", "Chain name") | ||
| _ = Cmd.MarkFlagRequired("chain") | ||
|
|
||
| common.InitDataDirFlag(Cmd, &flagDatadir) | ||
|
|
||
| Cmd.Flags().StringVar(&flagChunkDataPackDir, "chunk_data_pack_dir", "/var/flow/data/chunk_data_packs", | ||
| "directory that stores the chunk data packs") | ||
| _ = Cmd.MarkFlagRequired("chunk_data_pack_dir") | ||
|
|
||
| Cmd.Flags().Uint64Var(&flagLastK, "lastk", 1, | ||
| "last k sealed blocks to inspect") | ||
|
|
||
| Cmd.Flags().StringVar(&flagFromTo, "from_to", "", | ||
| "the height range to inspect blocks (inclusive), i.e, 1_1000, 1000_2000, 2000_3000, etc.") | ||
| } | ||
|
|
||
| func run(*cobra.Command, []string) { | ||
| lockManager := storage.MakeSingletonLockManager() | ||
| chainID := flow.ChainID(flagChain) | ||
| chain := chainID.Chain() | ||
|
|
||
| lg := log.With(). | ||
| Str("chain", string(chainID)). | ||
| Str("datadir", flagDatadir). | ||
| Str("chunk_data_pack_dir", flagChunkDataPackDir). | ||
| Uint64("lastk", flagLastK). | ||
| Str("from_to", flagFromTo). | ||
| Logger() | ||
|
|
||
| lg.Info().Msg("initializing token movements inspector") | ||
|
|
||
| closer, storages, chunkDataPacks, state, err := initStorages(lockManager, flagDatadir, flagChunkDataPackDir) | ||
| if err != nil { | ||
| lg.Fatal().Err(err).Msg("could not init storages") | ||
| } | ||
| defer func() { | ||
| if closeErr := closer(); closeErr != nil { | ||
| lg.Warn().Err(closeErr).Msg("error closing storages") | ||
| } | ||
| }() | ||
|
|
||
| // Create the token changes inspector with default search tokens for this chain | ||
| inspector := inspection.NewTokenChangesInspector(inspection.DefaultTokenDiffSearchTokens(chain)) | ||
|
|
||
| var from, to uint64 | ||
|
|
||
| if flagFromTo != "" { | ||
| from, to, err = parseFromTo(flagFromTo) | ||
| if err != nil { | ||
| lg.Fatal().Err(err).Msg("could not parse from_to") | ||
| } | ||
| } else { | ||
| lastSealed, err := state.Sealed().Head() | ||
| if err != nil { | ||
| lg.Fatal().Err(err).Msg("could not get last sealed height") | ||
| } | ||
|
|
||
| root := state.Params().SealedRoot().Height | ||
|
|
||
| // preventing overflow | ||
| if flagLastK > lastSealed.Height+1 { | ||
| lg.Fatal().Msgf("k is greater than the number of sealed blocks, k: %d, last sealed height: %d", flagLastK, lastSealed.Height) | ||
| } | ||
|
|
||
| from = lastSealed.Height - flagLastK + 1 | ||
|
|
||
| // root block is not verifiable, because it's sealed already. | ||
| // the first verifiable is the next block of the root block | ||
| firstVerifiable := root + 1 | ||
|
|
||
| if from < firstVerifiable { | ||
| from = firstVerifiable | ||
| } | ||
| to = lastSealed.Height | ||
| } | ||
|
|
||
| root := state.Params().SealedRoot().Height | ||
| if from <= root { | ||
| lg.Fatal().Msgf("cannot inspect blocks before the root block, from: %d, root: %d", from, root) | ||
| } | ||
|
|
||
| lg.Info().Msgf("inspecting token movements for blocks from %d to %d", from, to) | ||
|
|
||
| for height := from; height <= to; height++ { | ||
| err := inspectHeight( | ||
| lg, | ||
| chainID, | ||
| height, | ||
| storages.Headers, | ||
| chunkDataPacks, | ||
| storages.Results, | ||
| state, | ||
| inspector, | ||
| ) | ||
| if err != nil { | ||
| lg.Error().Err(err).Uint64("height", height).Msg("error inspecting height") | ||
| } | ||
| } | ||
|
|
||
| lg.Info().Msgf("finished inspecting token movements for blocks from %d to %d", from, to) | ||
| } | ||
|
|
||
| func inspectHeight( | ||
| lg zerolog.Logger, | ||
| chainID flow.ChainID, | ||
| height uint64, | ||
| headers storage.Headers, | ||
| chunkDataPacks storage.ChunkDataPacks, | ||
| results storage.ExecutionResults, | ||
| protocolState protocol.State, | ||
| inspector *inspection.TokenChanges, | ||
| ) error { | ||
| header, err := headers.ByHeight(height) | ||
| if err != nil { | ||
| return fmt.Errorf("could not get block header by height %d: %w", height, err) | ||
| } | ||
|
|
||
| blockID := header.ID() | ||
|
|
||
| result, err := results.ByBlockID(blockID) | ||
| if err != nil { | ||
| if errors.Is(err, storage.ErrNotFound) { | ||
| lg.Warn().Uint64("height", height).Hex("block_id", blockID[:]).Msg("execution result not found") | ||
| return nil | ||
| } | ||
| return fmt.Errorf("could not get execution result by block ID %s: %w", blockID, err) | ||
| } | ||
|
|
||
| heightLg := lg.With(). | ||
| Uint64("height", height). | ||
| Hex("block_id", blockID[:]). | ||
| Logger() | ||
|
|
||
| heightLg.Info().Int("num_chunks", len(result.Chunks)).Msg("inspecting block") | ||
|
|
||
| for _, chunk := range result.Chunks { | ||
| chunkDataPack, err := chunkDataPacks.ByChunkID(chunk.ID()) | ||
| if err != nil { | ||
| return fmt.Errorf("could not get chunk data pack by chunk ID %s: %w", chunk.ID(), err) | ||
| } | ||
|
|
||
| chunkLg := heightLg.With(). | ||
| Uint64("chunk_index", chunk.Index). | ||
| Logger() | ||
|
|
||
| err = inspectChunkFromDataPack( | ||
| chunkLg, | ||
| chainID, | ||
| header, | ||
| chunk, | ||
| chunkDataPack, | ||
| result, | ||
| protocolState, | ||
| headers, | ||
| inspector, | ||
| ) | ||
| if err != nil { | ||
| chunkLg.Error().Err(err).Msg("error inspecting chunk") | ||
| } | ||
| } | ||
|
|
||
| return nil | ||
| } | ||
|
|
||
| func parseFromTo(fromTo string) (from, to uint64, err error) { | ||
| parts := strings.Split(fromTo, "_") | ||
| if len(parts) != 2 { | ||
| return 0, 0, fmt.Errorf("invalid format: expected 'from_to', got '%s'", fromTo) | ||
| } | ||
|
|
||
| from, err = strconv.ParseUint(strings.TrimSpace(parts[0]), 10, 64) | ||
| if err != nil { | ||
| return 0, 0, fmt.Errorf("invalid 'from' value: %w", err) | ||
| } | ||
|
|
||
| to, err = strconv.ParseUint(strings.TrimSpace(parts[1]), 10, 64) | ||
| if err != nil { | ||
| return 0, 0, fmt.Errorf("invalid 'to' value: %w", err) | ||
| } | ||
|
|
||
| if from > to { | ||
| return 0, 0, fmt.Errorf("'from' value (%d) must be less than or equal to 'to' value (%d)", from, to) | ||
| } | ||
|
|
||
| return from, to, nil | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.