Skip to content

feat(evm): TokenState contract, PP updates and deploy script#1894

Open
atharrva01 wants to merge 12 commits into
LFDT-Panurus:feature/evm-network-driverfrom
atharrva01:evm-tokenstate
Open

feat(evm): TokenState contract, PP updates and deploy script#1894
atharrva01 wants to merge 12 commits into
LFDT-Panurus:feature/evm-network-driverfrom
atharrva01:evm-tokenstate

Conversation

@atharrva01

Copy link
Copy Markdown
Contributor

Second half of week 2, stacked on #1879. This adds TokenState, the contract that holds token state and applies the endorsed deltas.

applyStateDelta verifies the endorser quorum over the digest it computes, checks the public params version, checks spent and existence (per the graphHiding flag), then writes everything atomically. Anything off reverts.

Worth calling out:

  • graph-revealing spends reference the content-bound marker, so forged bytes at a real position get rejected. Proven on chain in the tests.
  • public params updates go through an endorsed setup delta that bumps the version, and older deltas go stale.
  • each TMS gets its own TokenState as an EIP-1167 clone of a shared, locked implementation. Deploy.s.sol sets it up.

40 forge tests cover the happy paths and the failure cases: issue, transfer, double spend, forged content, tampering, stale params, replay, the setup flow, queries, and graph hiding.

Endorsers are simulated with forge vm.sign for now. The Go signer cross-check is week 3, and the end to end run on Besu is week 6.

Stacked on #1879, so please review that one first.

adecaro and others added 12 commits June 30, 2026 15:46
Signed-off-by: Angelo De Caro <adc@zurich.ibm.com>
…/hash types (LFDT-Panurus#1850)

Signed-off-by: atharrva01 <atharvaborade568@gmail.com>
…s#1853)

Signed-off-by: atharrva01 <atharvaborade568@gmail.com>
Working branch was rebuilt on the merged upstream (post LFDT-Panurus#1850/LFDT-Panurus#1853). Restores our finalized design doc, implementation plan (Week 2 updated for the snMarker fix and new digest) and plan.md tracker, none of which are meant to be upstreamed.

Signed-off-by: atharrva01 <atharvaborade568@gmail.com>
Foundry project under x/token/services/network/evm/contracts, kept out of
the Go module so forge never links into the driver.

- StateDelta.sol: frozen structs, same field order as the Go/EIP-712 type
- EIP712.sol: domain separator, type hashes, hashStruct, digest, mirroring
  the Go eip712 package
- EndorsementVerifier.sol: ecrecover, low-s, v check, unique signers and
  threshold over a digest; endorser set seeded at deploy

21 forge tests pass. Solidity reproduces both golden digests from the Go
fixture (transfer 0xc9326b72, setup 0xdca9a011), also checked with ethers v6.

Also tightens StateDelta.Validate (rejects setup params on non-setup deltas
and unsorted or duplicate metadata keys) and adds the setup digest vector on
the Go side. verify takes the final digest, since TokenState is the per-TMS
clone that owns the domain separator.

Signed-off-by: atharrva01 <atharvaborade568@gmail.com>
- Besu is the acceptance backend, fabric-x-evm is a stretch
- working rules R1-R7, each from an actual slip on this branch
- module stays isolated under x/.../evm, core must not import it
- snMarker query-surface decision noted for PR 2b

Signed-off-by: atharrva01 <atharvaborade568@gmail.com>
- remove em dashes from code comments in the contracts and Go files
- document configNetworkResolver.IsEVMNetwork

Signed-off-by: atharrva01 <atharvaborade568@gmail.com>
TokenState stores per-TMS token state and applies endorsed StateDeltas.
applyStateDelta verifies the endorser quorum over the EIP-712 digest it
computes, checks the public params version, enforces spent and existence
per the graphHiding flag, then applies the transition atomically. Failures
revert with typed reasons.

- content-bound spend markers: a graph-revealing spend references the
  snMarker recorded at output creation, so forged bytes at a real
  (anchor, index) are rejected on chain
- option (a) query surface: a tokenID to marker mapping so isSpent(tokenID)
  is a single call without the caller holding the token content
- setup delta path is present; the full PP-update flow lands in phase B

11 forge tests: issue, spend, double spend, forged content, tampered
delta, stale params, replay, insufficient sigs, setup guard, init guards.

Signed-off-by: atharrva01 <atharvaborade568@gmail.com>
Completes the TokenState side of week 2.

- public params update: an endorsed setup delta installs new params and
  bumps the version (0 at initialize, then 1, 2, ...). Both delta kinds
  assert the current params, so a setup also orders updates and a delta
  validated against an old version is stale. The check is hoisted so it
  guards setup and transfer alike.
- EIP-1167 clones (Clones.sol): each TMS gets a cheap TokenState clone of
  a shared implementation. The implementation is locked in its constructor
  so only clones can be initialized.
- Deploy.s.sol: deploys the verifier, the implementation and an
  initialized clone from environment inputs (endorsers, threshold, PP v0,
  graphHiding). Dry-run deploys end to end.

Tests grow to 18 for TokenState (39 total): setup version bump, stale
after setup, stale second setup, metadata, areTokensSpent batch,
graph-hiding spend by serial, implementation lock. All use the clone path.

Signed-off-by: atharrva01 <atharvaborade568@gmail.com>
Quality and consistency review before raising the PR.

- add NatSpec to the remaining TokenState query functions so every
  external function is documented
- reject a setup delta that carries metadata: it is digest-covered and
  would otherwise be signed but ignored (rule R6); add a test for it
- drop em dashes from code comments across the evm contracts and Go
- document configNetworkResolver.IsEVMNetwork

No behaviour change beyond the setup guard. Go and forge suites green.

Signed-off-by: atharrva01 <atharvaborade568@gmail.com>
Traced the plan and design claims against the actual SDK and FSC source
before building on them. Six claims verified correct (version keeper
semantics, translator counter rules, TxID and FinalityListener shapes,
AreTokensSpent meta, metadata key parity, driver.Ledger). Four findings
folded into the docs:

- ComputeTxID is a mutating contract: FSC generates a random nonce when
  id.Nonce is empty and writes it back, and callers depend on that. A pure
  implementation would reuse the same anchor per creator and the second
  transaction would revert with AnchorAlreadyProcessed. Week 5 now
  specifies the generate-and-write-back behaviour plus tests.
- explicit mapping from receipt status to the SDK ValidationCode values
  (Valid=1, Invalid=2, Busy=3, Unknown=4); zero is not a code.
- a recipient can never observe Invalid from chain data alone, since a
  failed applyStateDelta reverts and emits nothing; the finality timeout
  is the only failure signal for anchor-only listeners. Documented.
- clone then initialize is two transactions, so initialize can be
  front-run. Week 6 gets a deploy-hardening task (atomic factory) and the
  script must assert post-initialize state. Also fixed the stale
  initialize signature in the design.

Signed-off-by: atharrva01 <atharvaborade568@gmail.com>
Pre-week-3 pass over the translator and signer seams.

- TokenState: metadata keys are now write-once. The Fabric translator
  enforces StateMustNotExist on every issue/transfer metadata key, so a
  reused key (say an htlc claim key seen twice) is a validation failure,
  never an overwrite. Without MVCC the contract must re-check this, same
  as the spent/existence checks; the apply list in the design missed it.
  New MetadataKeyOccupied error plus a test; the original value stays
  untouched when the second write reverts.
- design/plan: pinned the signer byte formats for week 3. decred
  SignCompact returns the recovery byte first, so reorder to {r,s,v};
  sign uncompressed so v stays in {27,28}; the address input is the
  64-byte pubkey without the 0x04 prefix; assert low-s regardless.
- verified against the source and noted: translator counter rules
  (translator.go:359/421) and the responder call sequence
  (fsc/responder.go:277-285) match the planned week 3 surface.

Signed-off-by: atharrva01 <atharvaborade568@gmail.com>
@adecaro adecaro force-pushed the feature/evm-network-driver branch from 5a6fa6f to 259b560 Compare July 13, 2026 11:12
Comment thread integration/go.mod
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.1 // indirect
github.com/dgraph-io/badger/v4 v4.9.2 // indirect

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.

why this?

@adecaro adecaro self-requested a review July 13, 2026 11:17
@adecaro adecaro self-assigned this Jul 13, 2026
@adecaro adecaro added this to the EVM-based Network Driver milestone Jul 13, 2026
@adecaro

adecaro commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

@atharrva01 , please rebase 🙏

@adecaro adecaro 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.

LGTM

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants