Documentation
¶
Index ¶
- Constants
- func BurnReceiptDigest(v *core.BurnReceipt) []byte
- func MarshalSignerPayload(signers []common.Address, threshold int) ([]byte, error)
- func MintReceiptDigest(v *core.MintReceipt) []byte
- func ParseSignerPayload(b []byte) ([]common.Address, int, error)
- func RunReceiptVerifierRefresh(ctx context.Context, rv *ReceiptVerifier, interval time.Duration, ...)
- type ChecksumSource
- type PayloadStore
- type ReceiptVerifier
- func (rv *ReceiptVerifier) Refresh(ctx context.Context) error
- func (rv *ReceiptVerifier) SetSignersForTest(signers []common.Address, threshold int)
- func (rv *ReceiptVerifier) SignerCount() int
- func (rv *ReceiptVerifier) Threshold() int
- func (rv *ReceiptVerifier) VerifyBurnReceipt(v *core.BurnReceipt) error
- func (rv *ReceiptVerifier) VerifyMintReceipt(v *core.MintReceipt) error
- type RegistrySignerSource
- type SignerSource
- type StaticSignerSource
Constants ¶
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
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
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
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
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.