Skip to content

Forbid unprotected txs#3678

Merged
librelois merged 3 commits intomasterfrom
elois/forbid-unprotected-txs
Feb 21, 2026
Merged

Forbid unprotected txs#3678
librelois merged 3 commits intomasterfrom
elois/forbid-unprotected-txs

Conversation

@librelois
Copy link
Collaborator

@librelois librelois commented Feb 21, 2026

⚠️ Breaking Changes ⚠️

Ethereum transactions without chain ID are now rejected

What does it do?

Forbid ethereum transactions without chain ID

What important points should reviewers know?

Is there something left for follow-up PRs?

What alternative implementations were considered?

Are there relevant PRs or issues in other repositories (Substrate, Polkadot, Frontier, Cumulus)?

What value does it bring to the blockchain users?

@coderabbitai
Copy link

coderabbitai bot commented Feb 21, 2026

📝 Walkthrough

Walkthrough

This PR disables unprotected Ethereum transactions across all Moonbeam, Moonbase, and Moonriver runtime configurations by setting AllowUnprotectedTxs to false. It also updates test suites to use EIP-1559 transaction format instead of legacy transactions and adjusts an Eth module initialization parameter.

Changes

Cohort / File(s) Summary
Runtime Configuration
runtime/moonbase/src/lib.rs, runtime/moonbeam/src/lib.rs, runtime/moonriver/src/lib.rs
Changed AllowUnprotectedTxs constant from true to false in pallet_evm::Config parameter_types, disallowing unprotected transactions in all three runtimes.
Runtime Test Mocks
runtime/moonbase/tests/xcm_mock/parachain.rs, runtime/moonbeam/tests/xcm_mock/parachain.rs, runtime/moonriver/tests/xcm_mock/parachain.rs
Updated AllowUnprotectedTxs from true to false in PostBlockAndTxnHashes parameter_types for XCM test parachain configurations.
Pallet Test Mocks
pallets/ethereum-xcm/src/mock.rs, pallets/moonbeam-foreign-assets/src/mock.rs
Modified AllowUnprotectedTxs from true to false in pallet_evm::Config for Test runtime configurations.
RPC Service
node/service/src/rpc.rs
Changed boolean argument in Eth module initialization from true to false following fee_history_limit and fee_history_cache parameters.
Test Suite Updates
test/suites/dev/moonbase/test-balance/test-balance-transfer.ts, test/suites/dev/moonbase/test-eth-tx/test-test-tx-nonce.ts, test/suites/dev/moonbase/test-precompile/test-precompile-xtokens.ts
Converted transaction format from legacy gasPrice to EIP-1559 (maxFeePerGas and maxPriorityFeePerGas); updated fee calculations and balance assertions accordingly.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

Suggested labels

agent-review

Suggested reviewers

  • arturgontijo
  • manuelmauro
🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Forbid unprotected txs' is concise and directly reflects the main change across all modified files: setting AllowUnprotectedTxs from true to false throughout the codebase to disallow unprotected Ethereum transactions.
Description check ✅ Passed The description is clearly related to the changeset, explaining that it forbids Ethereum transactions without chain IDs and marks it as a breaking change, which aligns with the actual modifications throughout the codebase.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch elois/forbid-unprotected-txs

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 and usage tips.

@github-actions
Copy link
Contributor

github-actions bot commented Feb 21, 2026

Coverage Report

@@                       Coverage Diff                       @@
##           master   elois/forbid-unprotected-txs     +/-   ##
===============================================================
  Coverage   77.10%                         77.10%   0.00%     
  Files         389                            389             
  Lines       76943                          76943             
===============================================================
  Hits        59323                          59323             
  Misses      17620                          17620             
Files Changed Coverage

Coverage generated Sat Feb 21 15:16:52 UTC 2026

@librelois librelois added B7-runtimenoteworthy Changes should be noted in any runtime-upgrade release notes D3-trivial PR contains trivial changes in a runtime directory that do not require an audit breaking Needs to be mentioned in breaking changes labels Feb 21, 2026
@github-actions
Copy link
Contributor

WASM runtime size check:

Compared to target branch

Moonbase runtime: 2120 KB (no changes) ✅

Moonbeam runtime: 2240 KB (no changes) ✅

Moonriver runtime: 2240 KB (no changes) ✅

Compared to latest release (runtime-4201)

Moonbase runtime: 2120 KB (+188 KB compared to latest release) ⚠️

Moonbeam runtime: 2240 KB (+212 KB compared to latest release) ⚠️

Moonriver runtime: 2240 KB (+212 KB compared to latest release) ⚠️

@librelois librelois marked this pull request as ready for review February 21, 2026 15:46
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Caution

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

⚠️ Outside diff range comments (2)
test/suites/dev/moonbase/test-eth-tx/test-test-tx-nonce.ts (1)

143-144: ⚠️ Potential issue | 🟡 Minor

Duplicate test id: "T01" within suite D011304 — second test should be "T02".

Lines 135 and 143 both declare id: "T01" inside the same describeSuite. The commit message "fix test D011304T01" targeted this suite, but only updated txnType; the ID collision was left unresolved. Depending on the moonwall test runner, the duplicate ID may cause one test to be silently skipped or both results to be misreported.

🐛 Proposed fix
     it({
-      id: "T01",
+      id: "T02",
       title: "should increment to 1",
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/suites/dev/moonbase/test-eth-tx/test-test-tx-nonce.ts` around lines 143
- 144, Within the describeSuite for D011304 there are two tests using the same
id value "T01"; locate the second it({ id: "T01", ... }) inside that
describeSuite and change its id to "T02" so test IDs are unique (ensure the
entry inside the it call that sets id is updated; e.g., modify the second
occurrence of id: "T01" to id: "T02" in the test block that also updated
txnType).
runtime/moonriver/src/lib.rs (1)

217-226: ⚠️ Potential issue | 🟡 Minor

The spec_version was incremented for this breaking change, but not in the same PR.

AllowUnprotectedTxs = false is a breaking behavioral change (PR #3677, Feb 20), rejecting legacy Ethereum transactions that were previously valid. The spec_version was incremented from 4200 to 4300, but this occurred in a separate PR (#3657, Feb 5) for unrelated runtime changes (frontier upgrades, EIP support, XCM fixes). The AllowUnprotectedTxs change itself did not trigger a spec_version bump. While the final state correctly reflects version 4300, best practice would align the spec_version increment with the PR introducing this breaking behavioral change.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@runtime/moonriver/src/lib.rs` around lines 217 - 226, The spec_version bump
must be applied in the same PR that introduced the breaking change
AllowUnprotectedTxs (PR `#3677`); update the RuntimeVersion constant named VERSION
by incrementing the spec_version field (RuntimeVersion::spec_version) so that
the PR that introduced AllowUnprotectedTxs contains the version change (e.g.,
bump 4300 -> 4301) and include that updated VERSION in the same commit that
toggles AllowUnprotectedTxs to ensure the breaking change and version bump are
co-located.
🧹 Nitpick comments (5)
test/suites/dev/moonbase/test-precompile/test-precompile-xtokens.ts (1)

254-255: Nit: inline fee style diverges from the other three tests in the same file.

T01, T03, and T05 all use an intermediate gasPrice variable before computing fees. Keeping a consistent style makes the pattern easier to scan.

♻️ Optional style alignment
-        const fees = receipt.gasUsed * receipt.effectiveGasPrice;
+        const gasPrice = receipt.effectiveGasPrice;
+        const fees = receipt.gasUsed * gasPrice;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/suites/dev/moonbase/test-precompile/test-precompile-xtokens.ts` around
lines 254 - 255, The assertion computes fees inline; to match T01/T03/T05,
introduce an intermediate gasPrice (or effectiveGasPrice) variable from
receipt.effectiveGasPrice before computing fees and then use fees =
receipt.gasUsed * gasPrice, so update the lines around the receipt usage (the
`fees` calculation and subsequent expect) to use the intermediate `gasPrice`
variable for consistent style with the other tests.
test/suites/dev/moonbase/test-eth-tx/test-test-tx-nonce.ts (1)

151-165: txnType: "eip1559" change is correct; consider preserving receipt assertion.

The switch from "legacy" to "eip1559" correctly aligns the test with the new AllowUnprotectedTxs = false runtime policy.

The AI summary notes that the original code also validated the transaction receipt and asserted the block contained exactly one transaction. Dropping those checks means a silent EVM revert (e.g., out-of-gas on the incr() call with gasLimit: 21000) would still satisfy the nonce assertion, masking a real failure.

♻️ Suggested improvement: re-add receipt check
-        await context.createBlock(
+        const { result } = await context.createBlock(
           context.createTxn!({
             privateKey: BALTATHAR_PRIVATE_KEY,
             to: incrementorAddress,
             data,
             value: 0n,
             gasLimit: 21000,
             txnType: "eip1559",
           })
         );
+        expect(result?.successful, "transaction should succeed").toBe(true);
         expect(
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/suites/dev/moonbase/test-eth-tx/test-test-tx-nonce.ts` around lines 151
- 165, The nonce assertion was kept but the test dropped the transaction receipt
and block-transaction-count checks, which can mask failures; update the test
around context.createBlock(context.createTxn!({...})) to also fetch and assert
the transaction receipt (from the returned tx hash or block) and assert the
created block contains exactly one transaction (use the block/receipt from
context.createBlock or context.viem().getTransactionReceipt and
context.viem().getBlock) before checking getTransactionCount for
BALTATHAR_ADDRESS, ensuring the incr() call actually executed (refer to
context.createBlock, context.createTxn, BALTATHAR_PRIVATE_KEY,
incrementorAddress, and getTransactionCount).
runtime/moonbase/src/lib.rs (1)

694-697: Core change LGTM — ensure spec_version (and transaction_version) is bumped before on-chain deployment.

Rejecting previously-valid unprotected transactions is a consensus-breaking change. Without a spec_version increment, tooling cannot detect the semantic boundary, and any in-flight unprotected transactions in the mempool at upgrade time will be silently dropped rather than clearly rejected. Consider also bumping transaction_version to signal the change to signing clients. This applies equally to the moonbeam and moonriver runtimes.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@runtime/moonbase/src/lib.rs` around lines 694 - 697, Bump the runtime
`spec_version` (and also `transaction_version`) in the runtime metadata before
on-chain deployment to signal the consensus change caused by disallowing
unprotected transactions; update the constants where `spec_version` and
`transaction_version` are defined (e.g., the runtime version struct or constants
that return RuntimeVersion) so tooling and signing clients detect the breaking
change and in-flight mempool unprotected transactions are rejected rather than
silently dropped; apply the same change in both moonbeam and moonriver runtime
modules.
test/suites/dev/moonbase/test-balance/test-balance-transfer.ts (1)

94-96: Weakened fee assertion in T03 — consider asserting the exact deduction.

With maxPriorityFeePerGas: 0n and maxFeePerGas: GENESIS_BASE_FEE, the effective gas price equals GENESIS_BASE_FEE (no tip above base fee), so the total deduction is deterministic. toBeGreaterThan(512n) only proves the transfer occurred; it doesn't validate fee accounting. T08 uses an exact fee assertion for the same EIP-1559 configuration.

♻️ Suggested fix
-        expect(balanceBefore - balanceAfter).toBeGreaterThan(512n);
+        expect(balanceBefore - balanceAfter).toBe(512n + 21000n * GENESIS_BASE_FEE);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/suites/dev/moonbase/test-balance/test-balance-transfer.ts` around lines
94 - 96, The test currently only checks balanceBefore - balanceAfter > 512n,
which is too weak; instead compute the exact expected deduction using the
transaction's value and gas used times GENESIS_BASE_FEE and assert equality.
After sending the transfer, fetch the receipt (e.g., via
context.viem().getTransactionReceipt or from the send call), read
receipt.gasUsed, compute expectedDeduction = transferValue + receipt.gasUsed *
GENESIS_BASE_FEE (use the same transfer amount used in the test), then replace
the loose expect with expect(balanceBefore -
balanceAfter).toEqual(expectedDeduction) referencing balanceBefore,
balanceAfter, CHARLETH_ADDRESS, GENESIS_BASE_FEE and the receipt.gasUsed value.
runtime/moonriver/src/lib.rs (1)

695-698: AllowUnprotectedTxs = false — correct and aligns with PR intent.

This prevents legacy (pre-EIP-155, no chain ID) Ethereum transactions from passing validate_self_contained. XCM-originated Ethereum transactions (dispatched via pallet_ethereum_xcm) go through ValidatedTransaction::apply directly and are unaffected by this flag, so XCM flows remain intact.

Optionally, since other boolean configs in this file use ConstBool<N> directly (e.g. ConstBool<true> in pallet_async_backing::Config), the parameter_types! indirection could be replaced:

♻️ Optional: use ConstBool directly for consistency
-parameter_types! {
-    pub const PostBlockAndTxnHashes: PostLogContent = PostLogContent::BlockAndTxnHashes;
-    pub const AllowUnprotectedTxs: bool = false;
-}
+parameter_types! {
+    pub const PostBlockAndTxnHashes: PostLogContent = PostLogContent::BlockAndTxnHashes;
+}

 impl pallet_ethereum::Config for Runtime {
     type StateRoot = ...;
     type PostLogContent = PostBlockAndTxnHashes;
     type ExtraDataLength = ConstU32<30>;
-    type AllowUnprotectedTxs = AllowUnprotectedTxs;
+    type AllowUnprotectedTxs = ConstBool<false>;
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@runtime/moonriver/src/lib.rs` around lines 695 - 698, The current
parameter_types! block defines AllowUnprotectedTxs as a bool constant set to
false; to match the other boolean config patterns in this file (e.g. the
ConstBool<true> used in pallet_async_backing::Config) and remove the
indirection, replace usages of the AllowUnprotectedTxs parameter with the direct
ConstBool<false> type and remove the AllowUnprotectedTxs parameter from the
parameter_types! block (keeping PostBlockAndTxnHashes as-is); search for the
identifier AllowUnprotectedTxs in the crate and update the bound to
ConstBool<false> so the config types are consistent.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@runtime/moonriver/src/lib.rs`:
- Around line 217-226: The spec_version bump must be applied in the same PR that
introduced the breaking change AllowUnprotectedTxs (PR `#3677`); update the
RuntimeVersion constant named VERSION by incrementing the spec_version field
(RuntimeVersion::spec_version) so that the PR that introduced
AllowUnprotectedTxs contains the version change (e.g., bump 4300 -> 4301) and
include that updated VERSION in the same commit that toggles AllowUnprotectedTxs
to ensure the breaking change and version bump are co-located.

In `@test/suites/dev/moonbase/test-eth-tx/test-test-tx-nonce.ts`:
- Around line 143-144: Within the describeSuite for D011304 there are two tests
using the same id value "T01"; locate the second it({ id: "T01", ... }) inside
that describeSuite and change its id to "T02" so test IDs are unique (ensure the
entry inside the it call that sets id is updated; e.g., modify the second
occurrence of id: "T01" to id: "T02" in the test block that also updated
txnType).

---

Nitpick comments:
In `@runtime/moonbase/src/lib.rs`:
- Around line 694-697: Bump the runtime `spec_version` (and also
`transaction_version`) in the runtime metadata before on-chain deployment to
signal the consensus change caused by disallowing unprotected transactions;
update the constants where `spec_version` and `transaction_version` are defined
(e.g., the runtime version struct or constants that return RuntimeVersion) so
tooling and signing clients detect the breaking change and in-flight mempool
unprotected transactions are rejected rather than silently dropped; apply the
same change in both moonbeam and moonriver runtime modules.

In `@runtime/moonriver/src/lib.rs`:
- Around line 695-698: The current parameter_types! block defines
AllowUnprotectedTxs as a bool constant set to false; to match the other boolean
config patterns in this file (e.g. the ConstBool<true> used in
pallet_async_backing::Config) and remove the indirection, replace usages of the
AllowUnprotectedTxs parameter with the direct ConstBool<false> type and remove
the AllowUnprotectedTxs parameter from the parameter_types! block (keeping
PostBlockAndTxnHashes as-is); search for the identifier AllowUnprotectedTxs in
the crate and update the bound to ConstBool<false> so the config types are
consistent.

In `@test/suites/dev/moonbase/test-balance/test-balance-transfer.ts`:
- Around line 94-96: The test currently only checks balanceBefore - balanceAfter
> 512n, which is too weak; instead compute the exact expected deduction using
the transaction's value and gas used times GENESIS_BASE_FEE and assert equality.
After sending the transfer, fetch the receipt (e.g., via
context.viem().getTransactionReceipt or from the send call), read
receipt.gasUsed, compute expectedDeduction = transferValue + receipt.gasUsed *
GENESIS_BASE_FEE (use the same transfer amount used in the test), then replace
the loose expect with expect(balanceBefore -
balanceAfter).toEqual(expectedDeduction) referencing balanceBefore,
balanceAfter, CHARLETH_ADDRESS, GENESIS_BASE_FEE and the receipt.gasUsed value.

In `@test/suites/dev/moonbase/test-eth-tx/test-test-tx-nonce.ts`:
- Around line 151-165: The nonce assertion was kept but the test dropped the
transaction receipt and block-transaction-count checks, which can mask failures;
update the test around context.createBlock(context.createTxn!({...})) to also
fetch and assert the transaction receipt (from the returned tx hash or block)
and assert the created block contains exactly one transaction (use the
block/receipt from context.createBlock or context.viem().getTransactionReceipt
and context.viem().getBlock) before checking getTransactionCount for
BALTATHAR_ADDRESS, ensuring the incr() call actually executed (refer to
context.createBlock, context.createTxn, BALTATHAR_PRIVATE_KEY,
incrementorAddress, and getTransactionCount).

In `@test/suites/dev/moonbase/test-precompile/test-precompile-xtokens.ts`:
- Around line 254-255: The assertion computes fees inline; to match T01/T03/T05,
introduce an intermediate gasPrice (or effectiveGasPrice) variable from
receipt.effectiveGasPrice before computing fees and then use fees =
receipt.gasUsed * gasPrice, so update the lines around the receipt usage (the
`fees` calculation and subsequent expect) to use the intermediate `gasPrice`
variable for consistent style with the other tests.

@librelois librelois merged commit 05ce4c8 into master Feb 21, 2026
40 of 41 checks passed
@librelois librelois deleted the elois/forbid-unprotected-txs branch February 21, 2026 16:15
@arturgontijo arturgontijo mentioned this pull request Mar 5, 2026
17 tasks
arturgontijo pushed a commit that referenced this pull request Mar 5, 2026
* forbid unprotected txs

* Tests must not use legacy txs without chain id

* fix test D011304T01
@coderabbitai coderabbitai bot mentioned this pull request Mar 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

B7-runtimenoteworthy Changes should be noted in any runtime-upgrade release notes breaking Needs to be mentioned in breaking changes D3-trivial PR contains trivial changes in a runtime directory that do not require an audit

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant