receipt

package
v0.5.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 17, 2026 License: MIT Imports: 14 Imported by: 0

Documentation

Index

Constants

View Source
const SignerPayloadVersion uint32 = 1

SignerPayloadVersion is bumped whenever the canonical signer-payload wire format changes in a way that would alter the checksum for an unchanged signer intent. It is part of the hashed payload, so a version skew between the producer (custody) and a consumer surfaces as a checksum mismatch rather than a silent disagreement.

Variables

This section is empty.

Functions

func BurnReceiptDigest

func BurnReceiptDigest(v *core.BurnReceipt) []byte

BurnReceiptDigest is the keccak256 digest custody providers sign over for withdrawal terminal attestations. Format: keccak256(

WithdrawalID || BlockHash || EntryIndex[uint64be] ||
len(TxID)[uint32be] || TxID || Status[byte]).

The trailing Status byte binds the terminal outcome (Executed vs Expired) into the signature, so a quorum can never be tricked into swapping an executed receipt for an expired one (which would authorize a re-credit). Exported so custody-side tooling and the custodytesting package can build matching signatures.

func MarshalSignerPayload added in v0.3.0

func MarshalSignerPayload(signers []common.Address, threshold int) ([]byte, error)

MarshalSignerPayload returns the deterministic byte encoding of (signers, threshold): JSON with stable field order, signers deduplicated, lowercased, and sorted ascending, so two callers agreeing on intent produce byte-identical output (and therefore an identical keccak256 checksum).

func MintReceiptDigest

func MintReceiptDigest(v *core.MintReceipt) []byte

MintReceiptDigest is the keccak256 digest custody providers sign over for deposit confirmation attestations. Exported so custody-side tooling can produce matching signatures.

Format: keccak256(

len(TxID)[uint32be]     || TxID ||
len(Account)[uint32be]  || Account ||
len(AssetURI)[uint32be] || AssetURI ||
len(Amount)[uint32be]   || canonical-CBOR(decimal.Decimal))

The length prefixes prevent boundary-shift collisions between variable-size receipt fields. Idempotency is keyed by (AssetURI, TxID), but the digest also binds the credited account and protocol amount.

func ParseSignerPayload added in v0.3.0

func ParseSignerPayload(b []byte) ([]common.Address, int, error)

ParseSignerPayload decodes bytes produced by MarshalSignerPayload back into a signer set and threshold, validating the version, the addresses, ascending order, and the threshold bound.

func RunReceiptVerifierRefresh

func RunReceiptVerifierRefresh(ctx context.Context, rv *ReceiptVerifier, interval time.Duration, onError func(error))

RunReceiptVerifierRefresh periodically refreshes the verifier's cached signer set. It returns when ctx is cancelled.

Types

type ChecksumSource added in v0.3.0

type ChecksumSource interface {
	LatestChecksum(key [32]byte) (checksum [32]byte, ok bool)
}

ChecksumSource yields the latest confirmed content checksum for a registry key. The EVM ConfigWatcher satisfies it via LatestChecksum; the interface keeps this package decoupled from the blockchain bindings.

type PayloadStore added in v0.3.0

type PayloadStore interface {
	Payload(ctx context.Context, checksum [32]byte) ([]byte, error)
}

PayloadStore resolves a content checksum to the bytes whose keccak256 equals it. For custody this is the local seeded config_versions store; for an external verifier it is wherever the published payload is mirrored. The RegistrySignerSource re-verifies the checksum, so a wrong or tampered store cannot inject an unauthorised signer set.

type ReceiptVerifier

type ReceiptVerifier struct {
	// contains filtered or unexported fields
}

ReceiptVerifier validates MintReceipts and BurnReceipts against a SignerSource. It caches signers and threshold and refreshes them periodically so verification is a fast in-memory check.

Verification dispatches on signature length:

  • 65 bytes → ECDSA secp256k1 (EVM custody chains; ADR-005 §11.1).
  • 64 bytes → ED25519 (XRPL/Solana custody; not yet implemented; gated on a per-chain ADR per ADR-005 §10).
  • other → ignored.

A signer's contribution is counted at most once across the receipt's signatures, so a duplicated signature does not satisfy the threshold.

func NewReceiptVerifier

func NewReceiptVerifier(source SignerSource, maxAge time.Duration) *ReceiptVerifier

NewReceiptVerifier returns a verifier backed by the given SignerSource. Refresh must be called before either Verify* method; production callers do this at startup and on a periodic ticker (see RunReceiptVerifierRefresh). maxAge bounds cache staleness; non-positive falls back to the package default (15 minutes).

func (*ReceiptVerifier) Refresh

func (rv *ReceiptVerifier) Refresh(ctx context.Context) error

Refresh reads the current signer set and threshold from the SignerSource and replaces the cached view atomically.

func (*ReceiptVerifier) SetSignersForTest

func (rv *ReceiptVerifier) SetSignersForTest(signers []common.Address, threshold int)

SetSignersForTest seeds the cache from an explicit signer list. Tests use this to drive the verifier without an on-chain reader.

func (*ReceiptVerifier) SignerCount

func (rv *ReceiptVerifier) SignerCount() int

SignerCount reports the cached signer-set size for diagnostics.

func (*ReceiptVerifier) Threshold

func (rv *ReceiptVerifier) Threshold() int

Threshold reports the cached signer threshold for diagnostics.

func (*ReceiptVerifier) VerifyBurnReceipt

func (rv *ReceiptVerifier) VerifyBurnReceipt(v *core.BurnReceipt) error

VerifyBurnReceipt checks that the receipt carries at least `threshold` distinct valid signatures from the cached signer set over BurnReceiptDigest.

func (*ReceiptVerifier) VerifyMintReceipt

func (rv *ReceiptVerifier) VerifyMintReceipt(v *core.MintReceipt) error

VerifyMintReceipt checks that the receipt carries at least `threshold` distinct valid signatures from the cached signer set over MintReceiptDigest.

type RegistrySignerSource added in v0.3.0

type RegistrySignerSource struct {
	// contains filtered or unexported fields
}

RegistrySignerSource is the on-chain-anchored SignerSource (ADR-017): the authoritative signer set is whatever payload hashes to the checksum the registry has committed under KEY_SIGNERS. It composes a ChecksumSource (the confirmed on-chain checksum) with a PayloadStore (the content-addressed bytes), verifies keccak256(payload) == checksum, and decodes (signers, threshold). It implements SignerSource, so swapping it in for StaticSignerSource needs no verifier-side change.

func NewRegistrySignerSource added in v0.3.0

func NewRegistrySignerSource(key [32]byte, checksums ChecksumSource, store PayloadStore) (*RegistrySignerSource, error)

NewRegistrySignerSource binds a source to the KEY_SIGNERS registry key.

func (*RegistrySignerSource) Load added in v0.3.0

Load resolves the current KEY_SIGNERS checksum, fetches and verifies the matching payload, and returns the decoded signer set and threshold. It errors (rather than returning a stale set) if no checksum is confirmed yet or the payload is missing — the verifier keeps its last good set until a refresh succeeds.

type SignerSource

type SignerSource interface {
	// Load returns the current signer set and threshold. Implementations
	// may be cached or pull from a remote source; the verifier calls Load
	// at startup and on a periodic ticker.
	Load(ctx context.Context) (signers []common.Address, threshold int, err error)
}

SignerSource supplies the custody signer set and threshold that the receipt verifier checks signatures against. It is chain-agnostic: a node can serve receipts produced by custody systems on any L1 as long as some SignerSource implementation knows the active signer set.

Implementations available today:

  • StaticSignerSource — manifest-driven; operator-managed list.

Planned: a Registry-backed source that reads a single canonical signer directory from the on-chain Registry once that contract grows the right method. The interface here is the seam that lets that swap land without touching the verifier or its callers. See the ADR-005 2026-05-12 receipt-model amendment.

type StaticSignerSource

type StaticSignerSource struct {
	// contains filtered or unexported fields
}

StaticSignerSource returns a fixed signer set and threshold supplied at construction time. It is the production SignerSource implementation for the receipt verifier today: operators publish the custody signer set in the manifest, and every node loads the same list.

When the on-chain Registry grows a custody-signers view, a RegistrySignerSource will replace this implementation at the call site; no verifier-side code changes are needed.

func NewStaticSignerSource

func NewStaticSignerSource(signers []common.Address, threshold int) (*StaticSignerSource, error)

NewStaticSignerSource validates the inputs and returns a source that hands the same (signers, threshold) pair to every Load call.

func (*StaticSignerSource) Load

Load returns the configured signers and threshold. The slice is copied so callers can mutate it without affecting the source.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL