Skip to content

[pull] master from bnb-chain:master - #13

Open
pull[bot] wants to merge 3110 commits into
Ramyromel:masterfrom
bnb-chain:master
Open

pull[bot] wants to merge 3110 commits into
Ramyromel:masterfrom
bnb-chain:master

Conversation

@pull

@pull pull Bot commented Jun 2, 2025 •

Copy link
Copy Markdown

See Commits and Changes for more details.


Created by pull[bot] (v2.0.0-alpha.4)

Can you help keep this open source service alive? 💖 Please sponsor : )

@pull pull Bot added the ⤵️ pull label Jun 2, 2025
@changeset-bot

changeset-bot Bot commented Jun 2, 2025 •

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 54afaff

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@pull pull Bot added the merge-conflict Resolve conflicts manually label Jun 2, 2025
@snyk-io

snyk-io Bot commented Jun 2, 2025 •

Copy link
Copy Markdown

⛔ Snyk checks have failed. 23 issues have been found so far.

Status Scan Engine Critical High Medium Low Total (23)
⛔ Open Source Security 0 12 11 0 23 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

conorpp and others added 26 commits April 13, 2026 15:30
This fixes a truncation bug that results in an invalid serialization of
empty EIP712.

For example:

```json
{
    "method": "eth_signTypedData_v4",
    "request": {
        "types": {
            "EIP712Domain": [
                {
                    "name": "version",
                    "type": "string"
                }
            ],
            "Empty": []
        },
        "primaryType": "Empty",
        "domain": {
            "version": "0"
        },
        "message": {}
    }
}
``` 

When calculating the type-hash for the stuct-hash, it will incorrectly
use `Empty)` instead of `Empty()`
runtime.setDefaults was unconditionally assigning cfg.Random =
&common.Hash{}, which silently overwrote any caller-provided Random
value. This made it impossible to simulate a specific PREVRANDAO and
also forced post-merge rules whenever London was active, regardless of
the intended environment.

This change only initializes cfg.Random when it is nil, matching how
other fields in Config are defaulted. Existing callers that did not set
Random keep the same behavior (a non-nil zero hash still enables
post-merge semantics), while callers that explicitly set Random now get
their value respected.
Fixes #34108

The UDPv5 test harness (`newUDPV5Test`) uses the default `PingInterval`
of 3 seconds. When tests like `TestUDPv5_findnodeHandling` insert nodes
into the routing table via `fillTable`, the table's revalidation loop
may schedule PING packets for those nodes. Under the race detector or on
slow CI runners, the test runs long enough for revalidation to fire,
causing background pings to be written to the test pipe. The `close()`
method then finds these as unmatched packets and fails.

The fix sets `PingInterval` to a very large value in the test harness so
revalidation never fires during tests.

Verified locally: 100 iterations with `-race -count=100` pass reliably,
where previously the test would fail within ~50 iterations.
… (#34043)

This fixes the remaining Hive discv5/FindnodeResults failures in the
cmd/devp2p/internal/v5test fixture.

The issue was in the simulator-side bystander behavior, not in
production discovery logic. The existing fixture could get bystanders
inserted into the remote table, but under current geth behavior they
were not stable enough to remain valid FINDNODE results. In
particular, the fixture still had a few protocol/behavior mismatches:

- incomplete WHOAREYOU recovery
- replies not consistently following the UDP envelope source
- incorrect endpoint echoing in PONG
- fixture-originated PING using the wrong ENR sequence
- bystanders answering background FINDNODE with empty NODES

That last point was important because current lookup accounting can
treat repeatedly unhelpful FINDNODE interactions as failures. As a
result, a bystander could become live via PING/PONG and still later be
dropped from the table before the final FindnodeResults assertion.
This change updates the fixture so that bystanders behave more like
stable discv5 peers:

- perform one explicit initial handshake, then switch to passive response handling
- resend the exact challenged packet when handling WHOAREYOU
- reply to the actual UDP packet source and mirror that source in PONG.ToIP / PONG.ToPort
- use the bystander’s own ENR sequence in fixture-originated PING
- prefill each bystander with the bystander ENR set and answer FINDNODE from that set

The result is that the fixture now forms a small self-consistent lookup
environment instead of a set of peers that are live but systematically
poor lookup participants.
This is meant to be run daily, in order to verify the FreeBSD build
wasn't broken like last time.
Adds config to add Prague prune point for the hoodi testnet.
Optimizes the transient storage. Turns it from a map of maps into a single map keyed by <account,slot>.
This PR simplifies the implementation of EIP-7610 by eliminating the
need to check storage emptiness during contract deployment.

EIP-7610 specifies that contract creation must be rejected if the
destination account has a non-zero nonce, non-empty runtime code, or 
**non-empty storage**.

After EIP-161, all newly deployed contracts are initialized with a nonce
of one. As a result, such accounts are no longer eligible as deployment 
targets unless they are explicitly cleared.

However, prior to EIP-161, contracts were initialized with a nonce of
zero. This made it possible to end up with accounts that have:

- zero nonce
- empty runtime code
- non-empty storage (created during constructor execution)
- non-zero balance

These edge-case accounts complicate the storage emptiness check.

In practice, contract addresses are derived using one of the following
formulas:
- `Keccak256(rlp({sender, nonce}))[12:]`
- `Keccak256([]byte{0xff}, sender, salt[:], initHash)[12:]`

As such, an existing address is not selected as a deployment target
unless a collision occurs, which is extremely unlikely.

---

Previously, verifying storage emptiness relied on GetStorageRoot.
However, with the transition to the block-based access list (BAL), 
the storage root is no longer available, as computing it would require 
reconstructing the full storage trie from all mutations of preceding 
transactions.

To address this, this PR introduces a simplified approach: it hardcodes
the set of known accounts that have zero nonce, empty runtime code, 
but non-empty storage and non-zero balance. During contract deployment, 
if the destination address belongs to this set, the deployment is
rejected.

This check is applied retroactively back to genesis. Since no address
collision events have occurred in Ethereum’s history, this change does
not
alter existing behavior. Instead, it serves as a safeguard for future
state
transitions.
… `testing_buildBlockV1` (#34722)

This is a copy of #34721 but against `master` (rather than
`bal-devnet-3`), as requested by @jwasinger, since the slotnum logic now
exists on `master` as well.
…y (#34723)

StateDB.Commit first commits all storage changes into the storage trie,
then updates the account metadata with the new storage root into the 
account trie.

Within StateDB.Commit, the new storage trie root has already been
computed and applied as the storage root. This PR explicitly skips the 
redundant storage trie root assignment for readability.
Auto-enable logic for `StatelessSelfValidation` was reading CLI flag
directly via `ctx.Bool()`, bypassing the merged `cfg.EnableWitnessStats`
value. Now uses `cfg.EnableWitnessStats` so config file settings trigger
the same auto-enable behavior as CLI flags.
Changes the log handler to check for vmodule level overrides
even for messages above the current level. This enables the user to selectively
hide messages from certain packages, among other things.

Also fixes a bug where handler instances created by WithAttr would not follow
the level setting anymore. The WithAttrs method is calledd by slog.Logger.With,
which we also use in go-ethereum to create context specific loggers with
pre-filled attributes. Under the previous implementation of WithAttrs, if the
application created a long-lived logger (for example, for a specific peer), then
that logger would not be affected by later level changes done on the top-level
logger, leading to potentially missed events.

Closes: #30717

---------

Co-authored-by: Marius van der Wijden <m.vanderwijden@live.de>
Co-authored-by: Felix Lange <fjl@twurst.com>
`trace.noreturndata` is documented as "enable return data output" but
the flag name/value imply it disables return data. This is confusing for
users and likely inverted wording. Update the Usage string to reflect
the actual behavior (disable return data output).
…34700)

This Pr implements some prerequisite changes for #34004 : split the
`CachingDB` into a `MerkleDB` and a `UBTDB`, so that very different
behaviors don't clash as much.

The transition isn't handled by this PR, but after talking to Gary we
agreed that `UBTDB` should receive another `triedb`, which will only be
loaded if the `Ended` flag is set to false in the conversion contract.
If this is too hard to achieve, it makes sense to load it regardless,
and then loading can be prevented at a later stage by adding a
`UBTTransitionFinalizationTime` in `ChainConfig`.

---------

Co-authored-by: Gary Rong <garyrong0905@gmail.com>
Adds a 'code' exporter to 'geth db export' that iterates over all
contract bytecode entries (CodePrefix + code_hash -> bytecode).

Usage: geth --datadir <dir> db export code code.rlp

This enables exporting contract bytecode.
…tlptracehttp from 1.34.0 to 1.43.0 (#3637)

Co-authored-by: allformless <213398294+allformless@users.noreply.github.com>
merge: develop to master for v1.7.3
…entity (#34733)

`timedExec` compares errors by direct interface inequality (haveErr !=
err). If execFunc returns newly constructed errors with the same message
each run, this will panic even though behavior is equivalent.
… amplification (#34754)

## Problem

`BinaryTrie.Commit` unconditionally walked every resolved in-memory node
and flushed it into the `NodeSet`, producing one Pebble write per
resolved internal + stem node on every block — even when the node's
on-disk blob was bitwise identical to the previous commit. On a warm
400M-state workload this meant tens of thousands of redundant 65-byte
writes per block, compounding Pebble compaction pressure on every
commit.

The existing `mustRecompute` flag tracks *hash* staleness, not
*disk-blob* staleness: after `Hash()` completes, `mustRecompute` is
cleared even though the fresh blob has not been persisted. It is
therefore insufficient for a skip-flush optimization.

## Fix

Mirror the MPT committer pattern (`trie/committer.go:51-56`) by adding a
`dirty` flag on `InternalNode` and `StemNode` with the semantics *the
on-disk blob is stale*. The flag is:

- set to `true` wherever the node is created or structurally modified
(the same call sites that already set `mustRecompute = true`);
- set to `false` only after the node has been passed to the `flushfn`
inside `CollectNodes`;
- left `false` on nodes produced by `DeserializeNodeWithHash`, matching
the *loaded from disk, already persisted* semantics.

`CollectNodes` short-circuits on `!dirty` subtrees. The propagation
invariant (an ancestor of any dirty node is itself dirty) is already
maintained by the existing `InsertValuesAtStem` / `Insert` paths, which
now mirror every `mustRecompute = true` setter with a `dirty = true`
setter.

## Benchmark

New `BenchmarkCollectNodes_SparseWrite` measures commit cost when only
one leaf changes between blocks — the common case for state updates.
10,000-stem trie, one-leaf modification + Commit per iteration, Apple M4
Pro:

| | before | after | delta |
|---|---|---|---|
| time / op | 12,653,000 ns | 7,336 ns | **~1,725×** |
| bytes / op | 107,224,740 B | 37,774 B | **~2,839×** |
| allocs / op | 80,953 | 134 | **~604×** |

End-to-end impact on a real workload depends on the
resolved-footprint-to-dirty-path ratio; the new
`TestBinaryTrieCommitIncremental` provides a structural regression guard
(asserts that a Commit following a single-leaf modification flushes a
root-to-leaf path, not the whole tree).

---

Found all of this stuff while bloating my #34706 DB to make some
benchmarks. And saw we were spending A LOT OF TIME on hashing.
Hope this helps the perf a bit. Will rebase the flat-state PR on top of
this once merged.
joey0612 and others added 30 commits August 27, 2026 15:41
…e Amsterdam

BlockAccessListHash (EIP-7928) and SlotNumber (EIP-7843) are trailing
rlp:"optional" header fields that are covered by the block hash but not
by the Parlia seal preimage (EncodeSigHeader stops after RequestsHash).
Parlia's header verification never validated them, so before Amsterdam an
unprivileged peer could append a crafted BlockAccessListHash to any
validator-signed block: RLP-valid, seal preimage and signature unchanged,
but a different block hash. Every node accepted the mutated block as
validly sealed, letting a peer with no stake or key grind the block hash
of an honest validator's block (e.g. to bias BLOCKHASH-based consumers or
win a same-height lower-hash fork-choice tie).

Validate these fields in VerifyUnsealedHeader, which is on the peer-import
path (VerifyHeader -> verifyHeader -> VerifyUnsealedHeader, before
verifySeal) as well as the bid pre-seal path, mirroring the existing
RequestsHash/ParentBeaconRoot handling: require them absent before
Amsterdam and present from Amsterdam on. This closes the live pre-Amsterdam
malleability; from Amsterdam the fields are legitimately present and their
content is bound by state validation and the slot rules.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* miner: persist BidBlock revokes across restarts

* miner: journal BidBlock revokes under datadir instead of chaindata

* miner: version the BidBlock revoke journal and pin its keys
* internal/ethapi: introduce gRPC endpoint for mev send bidblock request

* internal/ethapi: expose MEV protobuf definitions

* internal/ethapi: preserve MEV error details over gRPC

* eth, miner: configure MEV gRPC by port
…e at Jenner

verifyDoubleSignEvidence charges a flat DoubleSignEvidenceVerifyGas regardless of
input size, while Run computes SealHash over both RLP-decoded headers whose Extra
fields are not size-bounded, so the metered cost does not track the hashing work.

Add a Jenner-gated variant (verifyDoubleSignEvidenceJenner) pricing per input
byte (DoubleSignEvidenceVerifyPerByteGas = 16), mirroring 0x67's
cometBFTLightBlockValidatePasteur. Verification logic inherited unchanged;
pre-Jenner tables (including Pasteur) keep the flat-gas contract.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Expose Miner.Config.DelayLeftOver via mev_params so builders can
compute BidMustBefore per validator instead of hardcoding 15ms.
…r-nil

consensus/parlia: validate BlockAccessListHash and SlotNumber in header verification
core/vm: price the 0x68 double-sign-evidence precompile per input byte at Pasteur
* params: add the Jenner fork and the CAS20 governance constant

Jenner is the fork BEP-702 ships in. IsOnJenner marks the single block that
crosses into it, and it counts block 1 as that block on a chain whose genesis is
already Jenner-active — such a chain has no false-to-true transition to find, and
the fork's state changes have to be applied somewhere or the feature can never be
bootstrapped.

CAS20GovHubAddress duplicates GovHub's fixed system address, which
core/systemcontracts cannot supply because it imports core/vm and so cannot be
imported back. It is the one address a governance proposal can reach the
activation registry through, and it is the same on every network — which is why
the switch can be a constant rather than configuration.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* core/vm: add the stateful-precompile host and the CAS20 token family

Two layers, in one commit because they do not compile apart: the host's AddLog
meters through a charge helper the family defines.

**The host.** A StatefulPrecompiledContract receives a PrecompileContext
carrying the StateDB, the caller, the call-form guards and dynamic gas
accounting, so a precompile can read and write consensus state. RequiredGas
reports nothing, because a stateful cost depends on state not yet read — whether
a slot is cold, whether a write creates or rewrites — so metering happens inside
the handler as the work is performed. Existing precompiles and
RunPrecompiledContract are untouched.

Gas accounting is frame-scoped and sticky. A refused charge marks the frame and
every later charge on it refuses too, so one failure ends the call rather than
letting the rest of a handler run on an exhausted budget; the exit turns that
mark into ErrOutOfGas whatever the handler returned. Charges mirror the
interpreter's own cost functions — EIP-2929 warm/cold, EIP-2200 net metering with
EIP-3529 refunds, the EIP-2200 reentrancy sentry, LOG and KECCAK256 prices — so
nothing a CAS20 call does is cheaper than the same work through bytecode.

**The family.** A factory at a fixed address derives a token's address from
(variant, creator, salt) and plants a one-byte 0xEF sentinel there; the token
holds no executable code, is routed by its address prefix, and keeps its state
under that address in ERC-7201 namespaces. Two variants — Asset, with the
ERC-8056 scaled multiplier and disclosure announcements, and Stablecoin — share
the shared ERC-20 surface, role-based access control, a pause mask, EIP-2612
permit, and transfer policies resolved through a singleton PolicyRegistry. An
ActivationRegistry holds the per-feature switch that gates creation and policy
writes, and never reaches a token that already exists.

Every state write is metered before it happens and refused in a read-only frame
by the metering layer itself, so a handler that forgot its own guard cannot write
during a STATICCALL. Malformed calldata and unknown selectors revert with empty
returndata rather than a selector echo, keeping one client's internal strings out
of consensus.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* core/systemcontracts: plant the CAS20 registry sentinels at the fork

The two singleton registries hold storage and no code, so EIP-161 would clear
them and their storage with them. They are precompiles rather than upgradeable
contracts, so the sentinels are planted here instead of through an Upgrade
config, and nothing else is written: no feature is opened and there is no admin
to seed, the activation admin being governance's to appoint afterwards.

The sentinels are also what make that appointment possible. GovHub refuses a
proposal target carrying no code and reports the refusal in an event while
leaving the proposal itself successful, so a registry without one could never
receive its first parameter change and the failure would read as a success.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* core/vm: answer as an equivalent Solidity contract does

Nine places where the precompiles answered differently from the contract they
stand in for. None was reachable from a test the suite had, because the checks
that would have caught them compared our own artifacts against each other.

Argument decoding. A malformed narrow argument is a decode failure, and
Solidity's external decoder reverts with empty returndata for every one of them:
an integer outside an enum's range, a bool that is neither 0 nor 1, a uint8 or
address carrying padding a caller could not have produced, a bytes4 with nonzero
trailing bytes. Measured against solc 0.8.16 and 0.8.19, which agree. Panic(0x21)
comes from an internal uint-to-enum cast and never from argument handling, so
seven sites raising it were reporting a failure Solidity does not report there;
supportsInterface answered false for an encoding it should have refused. The six
Panic(0x11) sites are arithmetic overflow and stay.

PolicyRegistry. The registry's absence error carries no id — the caller named the
policy — and the parameterized form belongs to a token reporting an id it could
not find; two composite paths used the token's. createCompositePolicy checked the
type before the zero-admin guard, where the two simple constructors check the
admin first; all three now agree, and the order is observable through which error
a caller receives. Child validation ran one interleaved pass per child, so a set
naming both an ineligible child and a missing one reported whichever came first;
it now runs existence over the whole set before eligibility over the whole set,
and a set holding both reports the absence. A sentinel was accepted as a child,
though it stores no membership for a composite to consult. And an INTERSECT over
no children answered false, alone among the four types in not taking its own
empty-set semantics: an AND over nothing is vacuously true, which makes a
never-created INTERSECT as permissive as a never-created BLOCKLIST already was.
Binding checks existence, so no token can reference either.

Storage. A string length word in the long form encodes 32 bytes or more; a
shorter one is as non-canonical as an oversized one and now reads as empty. The
word has two readers, and the second feeds setStringAt's release loop, so leaving
it out would have released chunks for a string the reader reports as absent.

Every change is witnessed: reverting any one of them fails a test.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* core/vm: open the token space at 0xCA52

The reserved prefix is two bytes, but the eight zero bytes that follow it are
part of the layout, so the first five hex digits of every token address are the
marker plus a zero the padding supplies for free. Spending the second byte on
0x52 makes those digits spell the standard's name; 0x50 spelled it without its
number.

Nothing structural moves. The factory keeps 0xCA5F, the registries keep 0x7020,
and the reserved-space audit was made against the whole 0xCA5 namespace rather
than the token prefix alone, so it covers this choice without being redone.

The one vector that names the prefix in full is the padding case, which has to
carry a valid marker to reach the check it is named for. It does, and deleting
the padding check still fails it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* core/vm: drop comments the identifier already makes

A doc comment that restates its own name costs a reader the time to find that
out. Eleven of them go; what stays is the half a name cannot carry.

Deleted outright where the signature was the whole story: eventTopic, revCAS20,
revCAS20Bytes, the policy precompile type, the core storage root. Trimmed to the
part that is not derivable elsewhere: the delegate-call error keeps why it is
refused rather than what it is, cas20ErrorSel keeps that it registers as well as
computes, and two exported accessors keep their one line and lose the examples of
who calls them.

Left alone: anything naming a consensus rule, a check order, or a distinction the
identifier does not draw — that a policy view is metered where the same type has
an unmetered constructor, which two of the four policy types count as composite,
that an empty metadata value clears rather than writes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* core/vm: plant the registry sentinels only at the fork

Remove the lazy ensureSentinel path that every registry write ran. On a
conforming chain it was unreachable: the fork transition seeds both
registries before any transaction, and it could not rescue the case it
existed for, since GovHub tests extcodesize before forwarding a
parameter change and would have refused the registry before the write
was ever attempted. What it did cost was a code-hash read per write.

With the admin appointed through governance rather than taken from
configuration, the fork hook is the only path that can plant the byte,
and it now stands alone. The sentinel tests that only exercised the lazy path go
with it; the seeded case gains a negative control showing a bare
account with the same storage is reaped by EIP-161 clearing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* core/vm: decode announce and batchMint arguments before the state checks

Solidity's dispatcher decodes a function's arguments before any
modifier runs, so calldata that does not decode is reported as such
whoever sent it and whatever the token's state. announce and batchMint
checked the pause, the role and the reentrancy flag first, answering a
business error for a payload that never named a valid call.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* core/vm: keep only the comments a name cannot carry

Delete a comment when the identifier or the body already says it, or
when a test pins the behaviour; keep, compressed, the why a name cannot
express and no test guards: check order, storage layout, deliberate
dead code, the gas and sentinel invariants. Test files lose the step
comments that restated their own assertion messages.

Dead code met on the way goes too: the unused cas20PendingMulBits, an
activate() call in a test whose result was discarded, updateMembers'
second identical bool check, and finalizeUpdateAdmin's zero-caller
clause already implied by the non-zero pending admin. readU64 now
delegates to u64FromWord instead of repeating its loop.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* core/vm: bound getStringAt's allocation by paid reads; log a foreign registry code

getStringAt allocated the stored length before any tail read was
charged, so a call forwarding too little gas could still trigger a
large allocation before running out. The buffer now grows only behind
reads that were paid for.

seedCAS20Sentinel finding code on a registry address can only mean an
address collision, so say so in the log instead of returning silently.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* params: write IsOnJenner the way IsOnPasteur is written

Drop the arm that treated block 1 as the fork boundary on a chain born
Jenner-active. CAS20MarkerCode is exported so such a chain carries the
registry sentinels in its genesis allocation, as the other system
accounts do; the boundary predicate then needs no special case and
reads like the other IsOn* helpers. Also restore UBTTime to its place
in the aligned field block.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* core/vm: answer newUIMultiplier with the current multiplier when nothing is scheduled

It returned the raw pending lane, which is zero for a new token, after an
instant update and after a cancellation. ERC-8056 answers with
the effective multiplier whenever no schedule is live.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* core/vm, internal/ethapi: add cas20_getTokenInfo

A wallet reading one CAS20 token today issues a dozen eth_calls against
the same address, and reconstructing a consistent view of the scheduled
multiplier from multiplier, newUIMultiplier and effectiveAt across
separate calls is racy across blocks. cas20_getTokenInfo(address, block)
answers the whole configuration from one state, variant-tagged, with the
effective multiplier already folded and a pending schedule reported only
while it is live; cas20_getTokenInfoBatch does the same over up to twenty
addresses, answering null for one that holds no token.

The facade reads through the unmetered storage accessors and is bounded
against state the precompile could not have written: a string longer than
256 KiB is refused rather than walked. The namespace is not in the default
module list.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* internal/ethapi: serve the CAS20 token info from the eth namespace

eth_getCAS20TokenInfo and eth_getCAS20TokenInfoBatch, beside the other
BSC additions to eth_, so no extra module has to be enabled.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* core/vm: decode before the static-frame guard in every token method

pause, batchMint and announce refused a static frame before decoding
their arguments, while every other token method decodes first, so the
same malformed calldata under STATICCALL reported StaticCallNotAllowed
from three methods and an empty revert from the rest. The order is
observable and therefore consensus; every token method now shares one
prefix. The factory and the registries keep their guard before decoding.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* core/vm: refuse a code write in a static frame at the metering layer

chargeStorageWrite and AddLog already mark the frame write-protected, so a
handler missing its own guard fails closed; chargeCodeWrite did not, and
the factory's SetCode was covered only by the entry-point guard. Now every
mutation the family performs is refused at the point of mutation as well.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* core/vm: name the standard, not another implementation, in comments

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* core/vm: charge each internal dispatch what the reference contract pays

The bytes[] decoder accepts entries whose offsets alias one another, so an
announce bundle or an initCalls array of size O(N+M) could name N×M
dispatches, and a constant getter reads no state, so each one ran for
nothing: 12.5M dispatches fit in a 15M-gas transaction and took 746 ms.

Every entry now pays a warm CALL plus the copy of its own bytes before it
runs, which is what the reference contract pays to route it through a call
of its own. Aliasing stays accepted; it is no longer free.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* core/systemcontracts: pin the CAS20 GovHub constant to GovHubContract

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* core/vm: make the dispatch-charge test run out of gas inside the loop

The negative case's budget could not pay for the announcement itself, so it
never reached the loop it claimed to test. It now pays for the announcement
and exactly 200 dispatches, then for a hundred fewer.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* core/tracing, core/vm, eth/tracers: announce native storage access to tracers

The prestate and access-list tracers discover storage through the SLOAD and
SSTORE opcodes, and a CAS20 operation executes none: a traced transfer
showed no token storage, diffMode missed the balance change, and
eth_createAccessList returned an empty list. A new OnStorageRead hook fires
for every slot a stateful precompile reads or is about to write; the two
tracers consume it and see the same slots bytecode would have shown them.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* core/vm, internal/ethapi: let a code override reach a CAS20 address

StateOverride.Apply removed an overridden address from the static precompile
map, but a CAS20 address is routed by prefix, so the call still reached
native code and reverted against the replaced marker. An override on such
an address now leaves a DisabledPrecompile marker in the map, which dispatch
reads as "ordinary account"; on-chain routing is untouched.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* core/tracing, eth/tracers, internal/ethapi: finish the native-access tracing

Three gaps in the previous two commits. An account read had no hook, so a
token appeared in a prestate only once a slot was touched, and createCAS20's
new account was captured after its sentinel was written: the prestate showed
the code as pre-existing and the diff missed its creation. OnAccountRead now
fires before every native account read, from the existence check and the
factory's occupancy check alike. The mux tracer did not forward the new
hooks, so a prestate tracer requested beside a call tracer lost every CAS20
slot again. And eth_createAccessList applied overrides against a nil
precompile map, which a CAS20 code override then wrote to; it now builds the
set, applies the overrides to it and hands it to the EVM it simulates with.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* core/vm: let a memo transfer declare the format its payload follows

A memo is a 32-byte hash, and a hash says nothing about how the payload
behind it is encoded: an indexer or a receiving institution cannot tell
which message format a transfer claims to follow, or parse the payload it
receives out of band, without a side channel.

    transferWithMemoFormat(to, amount, memo, formatId)
    transferFromWithMemoFormat(from, to, amount, memo, formatId)

run the ordinary memo transfer of their own entry point — the transferFrom
form is transferFrom whoever calls it, so a holder moving its own balance
spends its self-approval as through transferFromWithMemo — and emit
MemoFormatDeclared(declarer, memo, formatId) right after Memo, so an indexer
ties the declaration to its transfer by log position. The formatId is one
bytes32 naming a format definition maintained off chain: composing several
standards is the definition's business, not the chain's. The chain records
the declaration and nothing more — it never sees the payload, so it never
verifies the claim, and it keeps no registry of formats. A zero formatId is
refused; the plain memo methods are how a transfer declares nothing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* core/vm: consolidate cas20 abi helpers

* core/tracing, core/vm, eth/tracers: clarify native access hook contracts

* core/vm: simplify gas accounting and compare native access costs

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…BEP-714) (#3822)

* consensus/parlia, core/systemcontracts: implement BEP-714 client changes for Jenner
…er, drop dead StateOverride copy (#3826)

* core/vm: emit CAS20Created before the admin grant and initCalls

CAS20Created is the token's identity record. Emit it as soon as that
identity is written, ahead of the initial RoleGranted and whatever the
initCalls log, so a creation transaction's logs read in the order the
state was built. The event test pins the order.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* core/vm: run the bootstrap window as the factory

initCalls are calls the factory makes into the new token, so the token
should see the factory as msg.sender, as it would for any contract the
factory called. Running them as the creator let the factory act in the
creator's name: approve, transfer, burn and the renounce methods inside
the window operated on the creator's balances and roles, a power no
ordinary caller of the token has. The initial admin's RoleGranted names
the factory as sender for the same reason.

No test relied on the old caller; the event test now pins the new one.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* internal/ethapi: drop the unused StateOverride copy

internal/ethapi/override owns OverrideAccount, StateOverride and Apply,
and every RPC path uses that package. The copy left in api.go was
reachable only from a test that the override package already carries,
and it lacked the DisabledPrecompile handling the live Apply gained, so a
future caller pointed at it would silently lose CAS20 override routing.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
#3825)

* eth/protocols/eth: fix deadlock when re-requesting partial receipts (#35537)

When an eth/70 receipts response arrives with LastBlockIncomplete set,
requestPartialReceipts re-requested the remainder while holding
receiptBufferLock across the dispatch. That dispatch blocks until the
dispatcher loop accepts the request. If the loop is concurrently
cancelling a request for that peer, its cleanup takes the same
receiptBufferLock. The dispatcher then waits for the lock the re-request
holds while the re-request waits on a channel only the dispatcher reads.

The fix releases the lock before sending and routes the follow-up through
the dispatcher as a new resend request.

---------

Co-authored-by: Gary Rong <garyrong0905@gmail.com>
(cherry picked from commit c57bbe4)

* eth/protocols/eth, eth/downloader: reject empty partial receipt responses (#35593)

eth/70 can complete a receipts request over multiple round trips.
Current implementation has two issues in requester side.

1. A peer could keep a request loop alive with incomplete responses that
deliver no receipt. An incomplete response must now add at least one
receipt. A trailing empty list after other blocks is still accepted. We
already have a grace period and peer-dropping logic covering this, but
an additional guard is nice to have.

2. Response.Time is recorded across all rounds, making the peer's
roundtrip estimate incorrect. The dispatcher now counts the rounds
made under a request id, and the downloader divides the delivered
items and elapsed time to one round trip.

(cherry picked from commit 596096a)

* eth/protocols/eth: add regression tests for partial receipt cancellation and continuation

---------

Co-authored-by: 0xSHKWON <kwonsanghyeon3245@naver.com>
Co-authored-by: Gary Rong <garyrong0905@gmail.com>
Co-authored-by: Bosul Mun <bosul.mun@ethereum.org>
…3847)

* params: set bsc testnet jenner hardfork to 2026-10-29 02:30:00 AM UTC

* chore: bump 1.8.1 for bsc testnet jenner hf
all: merge develop to master
params, version: prepare v1.8.2 for Mainnet Jenner hardfork
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

⤵️ pull merge-conflict Resolve conflicts manually

Projects

None yet

Development

Successfully merging this pull request may close these issues.