Files
precompile/dex/native_seam_parity_test.go
zeekayandHanzo Dev 0b433110ac dex: the funding rail carries value, and nothing else
Move once, trade many. Value crosses the C<->D boundary twice in its life; in
between, D owns it and every order, match and trade is a local balance movement.
So the crossing has exactly one job — move ownership exactly once — and the
object shrinks to what that job needs:

    beneficiary(20) | asset(32) | amount(8)   = 60 bytes

Out of the wire: the 49-byte market/side/limit/size tail, the `spent` execution
witness, and the lane byte selecting a trading pot. Out of the precompile: the
per-order escrow with its principal cap and deadline, the taker-authenticated
price floor, the deadline reclaim and its selector, the shared-memory discovery
trait, and the .v2 domain rotation that existed only because the tail had
changed the object's width.

ONE CUSTODY POT. seamReserve and committedPositions were the swap lane and the
LP lane of one quantity — "value C holds because D owns it" — and the lane byte
was the only thing keeping them apart. Splitting them is what created the
cross-lane drain the byte then had to defend against. One number per asset, one
credit path, nothing to cross.

WHAT REPLACES THE PER-ORDER CAP AND THE PRICE FLOOR: D refuses an over-debit on
export, C refuses a credit custody cannot back, a claim is consumable exactly
once, and Block.Verify proves the carried bytes are the recorded object. Those
are properties of the value. The cap and the floor were properties of an order,
which is why the order had to ride the object for them to be checkable at all.

The claim id is one derivation for both directions and both repos:
SHA-256(domain | source | dest | sourceTx | index). It replaces DeriveOrderID,
DerivePositionCommitID and deriveSeamOutputID, which were three spellings of the
same fact.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-07 17:33:14 -07:00

136 lines
5.4 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Copyright (C) 2025-2026, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package dex
import (
"encoding/hex"
"testing"
"github.com/luxfi/geth/common"
"github.com/luxfi/ids"
)
// native_seam_parity_test.go pins the C<->D funding wire BYTE-FOR-BYTE against
// dex/pkg/dchain. The two repos cannot import each other, so this golden vector IS
// the cross-repo contract: a one-sided change to the layout is a SILENT CONSENSUS
// BREAK — D would write a claim C cannot decode (every import reverts, nobody is
// ever credited) or, worse, a width both sides accept and read differently.
// dex/pkg/dchain/atomic_wire_test.go pins the IDENTICAL strings, so if either side
// drifts one of the two tests fails before the bytes reach consensus.
//
// HOW THE GOLDEN WAS PRODUCED (reproducible). encodeClaim on:
//
// beneficiary = 0x112233445566778899aabbccddeeff0102030405 (20 bytes)
// asset = 0xa0a1a2a3...bebf (32 bytes, ascending from 0xA0)
// amount = 0x0102030405060708
//
// yields the 60 bytes below. Any change to claimSize or the field order breaks it —
// intentionally.
const claimGoldenHex = "112233445566778899aabbccddeeff0102030405" + // beneficiary (20)
"a0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebf" + // asset (32)
"0102030405060708" // amount (8, big-endian)
// claimIDGoldenHex pins DeriveClaimID over a fixed corridor and source transaction.
// dex/pkg/dchain derives the SAME id from the SAME inputs; if it did not, C would
// write a claim under one key and D would look for it under another, and every
// crossing would silently vanish.
//
// source = 0x0101..01 (32 × 0x01)
// dest = 0x0202..02 (32 × 0x02)
// sourceTx = 0x0303..03 (32 × 0x03)
// index = 7
const claimIDGoldenHex = "0a5e96264cb3e4649c30aba257749a203119d0363677b1be3ead32ebf989f430"
func parityBeneficiary() common.Address {
return common.HexToAddress("0x112233445566778899aabbccddeeff0102030405")
}
func parityAsset() [32]byte {
var a [32]byte
for i := range a {
a[i] = byte(0xA0 + i)
}
return a
}
func repeatID(b byte) ids.ID {
var id ids.ID
for i := range id {
id[i] = b
}
return id
}
// TestClaimWire_GoldenMatchesDChain pins the encoder against the cross-repo golden
// and pins the canonical width.
func TestClaimWire_GoldenMatchesDChain(t *testing.T) {
want, err := hex.DecodeString(claimGoldenHex)
if err != nil {
t.Fatalf("bad golden hex: %v", err)
}
if len(want) != claimSize {
t.Fatalf("golden width %d != claimSize %d — the wire changed on ONE side only "+
"(consensus break: D writes a width C cannot decode). Update BOTH repos in lockstep.",
len(want), claimSize)
}
got := encodeClaim(parityBeneficiary(), parityAsset(), 0x0102030405060708)
if hex.EncodeToString(got) != claimGoldenHex {
t.Fatalf("funding wire DIVERGED from the D-Chain golden:\n got=%s\nwant=%s\n"+
"the C<->D claim is no longer byte-identical — every crossing would fail to "+
"decode. Re-align native_wire.go with dex/pkg/dchain/atomic.go.",
hex.EncodeToString(got), claimGoldenHex)
}
}
// TestClaimID_GoldenMatchesDChain pins the id derivation across the repos. A drift
// here does not corrupt value, it LOSES it: C writes under one key, D reads another.
func TestClaimID_GoldenMatchesDChain(t *testing.T) {
got := DeriveClaimID(repeatID(0x01), repeatID(0x02), repeatID(0x03), 7)
if hex.EncodeToString(got[:]) != claimIDGoldenHex {
t.Fatalf("claim id DIVERGED from the D-Chain golden:\n got=%s\nwant=%s",
hex.EncodeToString(got[:]), claimIDGoldenHex)
}
}
// TestClaimID_BindsItsCorridor pins that a claim id is scoped to one direction: the
// same source transaction and index on the reversed corridor derives a DIFFERENT id,
// so an object minted for C->D can never be presented as D->C.
func TestClaimID_BindsItsCorridor(t *testing.T) {
src, dst, tx := repeatID(0x01), repeatID(0x02), repeatID(0x03)
if DeriveClaimID(src, dst, tx, 0) == DeriveClaimID(dst, src, tx, 0) {
t.Fatal("a claim id is identical in both directions: an object could be replayed " +
"back across the corridor it just came from")
}
if DeriveClaimID(src, dst, tx, 0) == DeriveClaimID(src, dst, tx, 1) {
t.Fatal("two claims from one transaction collide: a second crossing in the same tx " +
"would overwrite the first")
}
}
// TestClaimWire_RoundTrip pins that decode is the exact inverse of encode.
func TestClaimWire_RoundTrip(t *testing.T) {
beneficiary, asset := parityBeneficiary(), parityAsset()
const amount uint64 = 9_000_000
gotB, gotA, gotAmt, ok := decodeClaim(encodeClaim(beneficiary, asset, amount))
if !ok {
t.Fatal("decode of a canonical claim must succeed")
}
if gotB != beneficiary || gotA != asset || gotAmt != amount {
t.Fatalf("round-trip mismatch: beneficiary=%x asset=%x amount=%d", gotB, gotA, gotAmt)
}
}
// TestClaimWire_WrongWidthRejected pins that a non-canonical width never decodes into
// a credit. In particular it pins the widths of the OLD braided object — 69 (value +
// lane byte + spent witness) and 118 (that, plus a market/side/limit/size tail) — so
// a stale object of either generation is refused rather than reinterpreted.
func TestClaimWire_WrongWidthRejected(t *testing.T) {
for _, n := range []int{0, 59, 61, 69, 80, 118} {
if _, _, _, ok := decodeClaim(make([]byte, n)); ok {
t.Fatalf("a %d-byte object must NOT decode (only the canonical %d is valid)", n, claimSize)
}
}
}