Skip to content

feat: TON support for foreign tx validation requests in mpc contract#3244

Open
DSharifi wants to merge 15 commits into
mainfrom
3241-ton-support-in-mpc-contract
Open

feat: TON support for foreign tx validation requests in mpc contract#3244
DSharifi wants to merge 15 commits into
mainfrom
3241-ton-support-in-mpc-contract

Conversation

@DSharifi
Copy link
Copy Markdown
Contributor

closes #3241

co-authored with: @frolvanya

@DSharifi DSharifi linked an issue May 14, 2026 that may be closed by this pull request
@DSharifi DSharifi changed the title feat: ton support in mpc contract feat: TON support for foreign tx validation requests in mpc contract May 14, 2026
@DSharifi DSharifi force-pushed the 3241-ton-support-in-mpc-contract branch from 410752e to 9297612 Compare May 14, 2026 22:01
@DSharifi DSharifi marked this pull request as draft May 14, 2026 22:03
@DSharifi
Copy link
Copy Markdown
Contributor Author

@claude review

@near near deleted a comment from claude Bot May 15, 2026
@DSharifi DSharifi marked this pull request as ready for review June 1, 2026 13:19
@DSharifi
Copy link
Copy Markdown
Contributor Author

DSharifi commented Jun 1, 2026

@claude review

@DSharifi DSharifi self-assigned this Jun 1, 2026
@claude
Copy link
Copy Markdown

claude Bot commented Jun 1, 2026

Pull request overview

Re-review after the May 15 round. The prior blocking items have been addressed: TonCellRefs is now EmptyBoundedVec<Hash256, TON_CELL_MAX_REFS> (refs are 32-byte cell-hash commitments, max 4 → 128 B total, no inner unboundedness), and TonCellBody now carries an explicit bit_length: u16 validated against bits.len() via ceiling-division — with both the Deserialize and BorshDeserialize impls routed through a single TonCellBodyRepr → TryFrom choke point, plus unit/property tests confirming the bit/byte mismatch + max-bit-length checks reject invalid input on both wire formats. The doc comment on ForeignTxSignPayload ("Never reorder variants/fields") is honored — Ton is appended at the tail of ForeignChainRpcRequest, ExtractedValue, and ForeignChain, preserving existing Borsh discriminants.

Changes:

  • TonCellRefs re-typed as EmptyBoundedVec<Hash256, TON_CELL_MAX_REFS> (was unbounded inner Vec<u8>).
  • TonCellBody introduced as { bits, bit_length } with constructor-only invariants; manual Deserialize/BorshDeserialize enforce the same invariants on the wire.
  • Hash-distinguishability test (ton_log_compute_msg_hash__should_differ_for_same_bytes_but_different_bit_length) pins the previously-flagged 7-vs-8-bit collision case.

Reviewed changes

Per-file summary
File Description
crates/near-mpc-contract-interface/src/types/foreign_chain.rs Adds Ton DTOs, TonCellBody with validated bit-length, error type, and a battery of constructor/round-trip tests.
crates/near-mpc-contract-interface/src/types/snapshots/...ton__should_have_consistent_hash.snap Pinned canonical-payload hash for the Ton case.
crates/contract/tests/sandbox/common.rs ton_request() / bogus_ton_log_extracted_value() helpers.
crates/contract/tests/sandbox/foreign_chain_request.rs Ton case added to three rstest matrices.
crates/contract/tests/snapshots/abi__abi_has_not_changed.snap ABI snapshot extended with Ton variants + TonRpcRequest/TonAddress/TonExtractor/TonFinality/TonTxId.

Findings

Non-blocking (nits, follow-ups, suggestions):

  • crates/near-mpc-contract-interface/src/types/foreign_chain.rs:336TonCellBody::new validates bits.len() == ⌈bit_length / 8⌉ but does not validate that the unused low bits in the final byte are zero, even though the doc comment two lines above promises "the final byte's unused low bits zeroed." Concretely, TonCellBody { bits: [0xF0], bit_length: 4 } and TonCellBody { bits: [0xFF], bit_length: 4 } are both accepted today but Borsh-serialize to different bytes and therefore produce different compute_msg_hash outputs. Because this hash drives MPC quorum, two inspectors that read the same on-chain message but pack the trailing padding differently would silently disagree and never reach quorum. The simplest fix is a 2-line guard:
    let trailing = bit_length % 8;
    if trailing != 0 {
        let mask: u8 = 0xFF >> trailing; // low (8 - trailing) bits
        if let Some(last) = bits.as_ref().last() {
            if last & mask != 0 {
                return Err(TonCellBodyError::NonCanonicalTrailingBits { last: *last, bit_length });
            }
        }
    }
    Cheaper to add now than after V1 payloads ship, since both Deserialize and BorshDeserialize already route through new. Not strictly a contract-side concern (only the hash flows on-chain) but it's a real footgun for the upcoming inspector PR.
  • crates/near-mpc-contract-interface/src/types/foreign_chain.rs:507 — still no negative test that ForeignChainRpcRequest::Ton(...) paired with a non-Ton ExtractedValue (e.g. EvmExtractedValue::BlockHash) hashes to something different than the Ton-Ton pairing — mirroring the existing foreign_tx_sign_payload_v1__should_produce_different_hashes_for_different_requests. The previous review's nit; carrying it forward since the snapshot test alone doesn't guard variant-reordering against cross-chain payload collisions.
  • crates/node/src/providers/verify_foreign_tx/sign.rs:319 — the catchall _ => bail!(\"unsupported foreign chain request\") still rejects every Ton request at runtime, and crates/node-config/src/foreign_chains.rs has no Ton entry. Intentional (this PR is contract/DTO-only) — please call that out in the PR description so deployers don't infer end-to-end TON support from this merge alone.

✅ Approved

@near near deleted a comment from claude Bot Jun 1, 2026
@near near deleted a comment from claude Bot Jun 1, 2026
anodar
anodar previously approved these changes Jun 4, 2026
Copy link
Copy Markdown
Collaborator

@anodar anodar left a comment

Choose a reason for hiding this comment

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

Thank you!

Comment thread crates/near-mpc-contract-interface/src/types/foreign_chain.rs Outdated
Comment thread crates/near-mpc-contract-interface/src/types/foreign_chain.rs Outdated
# above the post-feature size without leaving the contract free to creep up
# to the protocol boundary.
HARD_LIMIT=1450000
HARD_LIMIT=1500000
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

optional nit: consider adding a comment here so that we know in the future how we kept bumping it up.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I'll skip this, as the other PR I have allows us to bump this to 2MiB

Copy link
Copy Markdown
Contributor

@SimonRastikian SimonRastikian left a comment

Choose a reason for hiding this comment

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

Overall seems good with a couple of questions

Comment thread crates/contract/tests/sandbox/common.rs
Comment thread crates/contract/tests/sandbox/common.rs
Comment thread crates/near-mpc-contract-interface/src/types/foreign_chain.rs Outdated
Comment thread crates/near-mpc-contract-interface/src/types/foreign_chain.rs
anodar
anodar previously approved these changes Jun 4, 2026
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.

TON support in MPC contract

3 participants