Skip to content

Check minting account during token inspection#8590

Open
janezpodhostnik wants to merge 1 commit into
masterfrom
janez/check-minting-account
Open

Check minting account during token inspection#8590
janezpodhostnik wants to merge 1 commit into
masterfrom
janez/check-minting-account

Conversation

@janezpodhostnik

@janezpodhostnik janezpodhostnik commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

The FVM token-changes inspector accounts for FlowToken mints/burns via events, but did not check who triggered a mint. This PR adds an optional per-event minter allow-list: a mint observed in a transaction not signed by any allow-listed account is recorded as a violation and logged at error level.

By default, FlowToken minting is restricted to the service account (e.g. the system transaction paying epoch staking rewards); any other minter is flagged.

Summary by CodeRabbit

  • New Features

    • Transaction inspection now uses signer information when available, improving detection of token-related issues.
    • Token monitoring now reports allow-list violations alongside normal balance changes.
  • Bug Fixes

    • Minting checks now better distinguish authorized vs. unauthorized token events.
    • Transaction signer handling now consistently deduplicates and orders required signers, including edge cases with empty or missing signers.

@janezpodhostnik janezpodhostnik self-assigned this Jun 26, 2026
@janezpodhostnik janezpodhostnik requested a review from a team as a code owner June 26, 2026 18:34
@github-actions

Copy link
Copy Markdown
Contributor

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Scanned Files

None

@coderabbitai

coderabbitai Bot commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Transaction bodies now produce deduplicated signer lists, and inspector entry points pass those signers through inspection. Token diff inspection uses signer-aware source/sink definitions, records mint allow-list violations, updates default token mappings, and adds tests for the new result fields.

Changes

Signer-aware inspection

Layer / File(s) Summary
Transaction signer source
model/flow/transaction.go, model/flow/transaction_body_builder.go, model/flow/transaction_test.go
TransactionBody.Signers() returns proposer, payer, and authorizers in deduplicated order; the builder derives signer indexes from that order, and tests cover ordering and nil handling.
Inspector signer plumbing
fvm/inspection/inspector.go, fvm/fvm.go, cmd/util/cmd/inspect-token-movements/storage.go
The inspector interface accepts signer lists, inspectProcedureResults forwards context.TxBody.Signers(), and token-movement inspection passes transaction signers into the inspector.
Token allow-list accounting
fvm/inspection/token_changes.go, fvm/fvm_test.go
SourceSink now pairs amount decoding with optional mint allow-lists, TokenChanges.Inspect records MintAllowlistViolation entries and logs them, default token mappings use the new model, and tests assert the updated result fields.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • onflow/flow-go#8551: Directly adjacent changes in fvm/inspection/token_changes.go around mint-event token wiring.

Suggested reviewers

  • j1010001
  • zhangchiqing
  • peterargue

Poem

I hopped through signer trails at dawn,
And counted paws in tidy rows;
The minting clover wore a rule,
No sneaky nibble left unknown—
🐇 Hooray, the burrow’s logs now glow.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: token inspection now checks the minting account during inspection.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch janez/check-minting-account

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codecov-commenter

codecov-commenter commented Jun 26, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 27.27273% with 56 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
fvm/inspection/token_changes.go 0.00% 55 Missing ⚠️
cmd/util/cmd/inspect-token-movements/storage.go 0.00% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
fvm/inspection/token_changes.go (1)

833-863: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Restore the default FlowToken.TokensBurned mapping.

DefaultTokenDiffSearchTokens() now registers TokensMinted plus the EVM bridge events, but not FlowToken.TokensBurned. A normal burn will therefore miss findSourcesSinks() and show up as a negative unaccounted supply delta under the default configuration.

Proposed fix
 func DefaultTokenDiffSearchTokens(chain flow.Chain) TokenChangesSearchTokens {
 	sc := systemcontracts.SystemContractsForChain(chain.ChainID())
 	flowTokenID := fmt.Sprintf("A.%s.FlowToken.Vault", sc.FlowToken.Address.Hex())
 	flowTokenMintedEventID := fmt.Sprintf("A.%s.FlowToken.TokensMinted", sc.FlowToken.Address.Hex())
+	flowTokenBurnedEventID := fmt.Sprintf("A.%s.FlowToken.TokensBurned", sc.FlowToken.Address.Hex())
@@
 	searchTokens[flowTokenID].SinksSources[flowTokenMintedEventID] = SourceSink{
 		Amount: decodeFlowEventAmount(flowAmountSource),
 		MinterAllowlist: map[flow.Address]struct{}{
 			chain.ServiceAddress(): {},
 		},
 	}
+	searchTokens[flowTokenID].SinksSources[flowTokenBurnedEventID] = SourceSink{
+		Amount: decodeFlowEventAmount(flowAmountSink),
+	}

This is based on the PR objective that FlowToken mint and burn events remain accounted for while adding mint allow-list enforcement.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@fvm/inspection/token_changes.go` around lines 833 - 863, The default token
search setup in DefaultTokenDiffSearchTokens is missing the FlowToken burn event
mapping, so normal burns are not being accounted for. Update the SearchToken
configuration for flowTokenID in token_changes.go to also register
FlowToken.TokensBurned alongside TokensMinted and the EVM bridge events, using
the same burn amount source/sink handling as before. Keep the mint allow-list
enforcement on TokensMinted intact while restoring burn detection so
findSourcesSinks can classify standard burn transactions correctly.
🧹 Nitpick comments (1)
fvm/fvm_test.go (1)

4547-4623: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a burn-path case for the default token definitions.

The new assertions only exercise mint flows. A transaction that emits FlowToken.TokensBurned and asserts zero unaccounted tokens here would lock in the default burn accounting path and catch regressions in DefaultTokenDiffSearchTokens().

This is based on the PR objective that burn accounting remains part of the inspector behavior.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@fvm/fvm_test.go` around lines 4547 - 4623, Add a burn-path test case
alongside the existing mint cases in the TokenDiff inspector table, using the
default token definitions from DefaultTokenDiffSearchTokens and a transaction
that emits FlowToken.TokensBurned. Reuse the same txBody pattern with
templates.GenerateMintFlowScript-style setup replaced by the burn flow, and in
resultChecker assert zero unaccounted tokens and the expected token
changes/violations so the default burn accounting path is covered by this test.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@fvm/inspection/inspector.go`:
- Around line 18-20: Update the signer contract docs in Inspector to match
TransactionBody.Signers() and the inspector inputs: the current comment
under-documents the returned slice by omitting the proposer. Revise the doc
comment near the inspector logic to state that signers are the proposer, payer,
and authorizers, deduplicated, and keep it aligned with
model/flow/transaction.go and TransactionBody.Signers().

---

Outside diff comments:
In `@fvm/inspection/token_changes.go`:
- Around line 833-863: The default token search setup in
DefaultTokenDiffSearchTokens is missing the FlowToken burn event mapping, so
normal burns are not being accounted for. Update the SearchToken configuration
for flowTokenID in token_changes.go to also register FlowToken.TokensBurned
alongside TokensMinted and the EVM bridge events, using the same burn amount
source/sink handling as before. Keep the mint allow-list enforcement on
TokensMinted intact while restoring burn detection so findSourcesSinks can
classify standard burn transactions correctly.

---

Nitpick comments:
In `@fvm/fvm_test.go`:
- Around line 4547-4623: Add a burn-path test case alongside the existing mint
cases in the TokenDiff inspector table, using the default token definitions from
DefaultTokenDiffSearchTokens and a transaction that emits
FlowToken.TokensBurned. Reuse the same txBody pattern with
templates.GenerateMintFlowScript-style setup replaced by the burn flow, and in
resultChecker assert zero unaccounted tokens and the expected token
changes/violations so the default burn accounting path is covered by this test.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: df3a1512-9e77-4bbf-9e22-d6e3183d021b

📥 Commits

Reviewing files that changed from the base of the PR and between 4c61355 and d431089.

📒 Files selected for processing (8)
  • cmd/util/cmd/inspect-token-movements/storage.go
  • fvm/fvm.go
  • fvm/fvm_test.go
  • fvm/inspection/inspector.go
  • fvm/inspection/token_changes.go
  • model/flow/transaction.go
  • model/flow/transaction_body_builder.go
  • model/flow/transaction_test.go

Comment on lines +18 to +20
// - signers are the accounts that signed the procedure's transaction (its
// authorizers and payer), deduplicated. It is empty for procedures that are
// not transactions (e.g. scripts).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Include the proposer in the signer contract docs.

TransactionBody.Signers() returns proposer, payer, then authorizers, so this comment currently under-documents the slice that inspectors actually receive. Cross-file, this mismatches model/flow/transaction.go.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@fvm/inspection/inspector.go` around lines 18 - 20, Update the signer contract
docs in Inspector to match TransactionBody.Signers() and the inspector inputs:
the current comment under-documents the returned slice by omitting the proposer.
Revise the doc comment near the inspector logic to state that signers are the
proposer, payer, and authorizers, deduplicated, and keep it aligned with
model/flow/transaction.go and TransactionBody.Signers().


// If an allow-list is configured for this event, the transaction must be
// signed by at least one allow-listed account; otherwise record a violation.
if ts.ss.MinterAllowlist != nil && !anySignerAllowed(signers, ts.ss.MinterAllowlist) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question 1:
Is the MinterAllowlist deterministic (not configurable by node, because node might use different config)?

Question 2:
Is the MinterAllowlist currently only Service Account? If we track other non-Flow token, does the allow list for those tokens be different from service account?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants