Documentation
¶
Overview ¶
internal/paygate/auth_brc31.go — BRC-31 mutual auth session.
V1 simplification: the BRC-31 header is computed as
Brc31 session=<sid>; nonce=<hex>; pk=<hex>; sig=<hex>
where sig = secp256k1 ECDSA over the canonical request preamble. The full BRC-31 handshake (key exchange, session establishment via the /v1/auth path) is wired by the stub server in Task 13 and by the live integration tests; v1 does not implement the registration dance because PRD 2 § 7.1 inherits it from proof-generation-architecture.md without extending the contract.
internal/paygate/client.go — top-level Client + canonical 11-step submit lifecycle from PRD P1.8 "Submit lifecycle".
internal/paygate/credit_token_ledger.go — single-use credit_token store.
Schema:
CREATE TABLE credit_tokens ( token TEXT PRIMARY KEY, issued_at TIMESTAMP NOT NULL, expires_at TIMESTAMP NOT NULL, used_at TIMESTAMP );
Take performs the single-use enforcement atomically:
- Load token, check expiry → ErrCreditTokenExpired
- Check used_at IS NULL → ErrCreditTokenInvalid (already used)
- UPDATE used_at = now() → only one caller wins
Package paygate is the konareef runtime client for the PayGate ZK API (PRD 2). It implements the six PRD 4 § 2.1 / PRD P1.8 obligations and counter-signs the eight named acceptance checks M-1..M-8 plus the konareef-added M-6b first-fetch-window extension.
Public surface:
- Client + Session
- Open, SubmitFold, PollStatus, FetchResult
- IdempotencyKey (PRD 2 § 6.5 errata-locked preimage)
- Typed error model (Category + Code + IsRetryable)
All rejection paths return wrapped sentinel errors. Branch with errors.Is(err, paygate.ErrCircuitPinMismatch) for the canonical PRD P1.8 § 2 (Obligation 6) State C halt code; see errors.go for the closed enumeration of categories and codes inherited from PRD 2 § 6.4.
internal/paygate/errors.go — typed PRD 2 § 6.4 error model.
Every Err* sentinel is a *PaygateError; wrapped errors compare equal via errors.Is via the default pointer-equality unwrap. Code(err) and CategoryOf(err) extract the stable identifier strings for logging, telemetry, and golden-vector comparison.
internal/paygate/idempotency.go — PRD 2 § 6.5 errata-locked preimage.
preimage = ULEB128(len(circuit_id_bytes)) ‖ circuit_id_bytes
‖ be_u64(step_index) (8 bytes, fixed) ‖ h_p (32 bytes, fixed)
Idempotency-Key = hex_lower( SHA-256(preimage) )
circuit_id_bytes is the UTF-8 NFC bytes of the circuit_id tstr. Per the PRD P1.8 normative-encoding rationale, ULEB128 length prefix + fixed-width big-endian step_index removes all delimiter ambiguity the upstream PRD 2 § 6.5 erratum (2026-06-06) flagged.
internal/paygate/idempotency_cache.go — PRD P1.8 WI-4 normative cache.
Schema:
CREATE TABLE idempotency_cache ( brc31_session_id TEXT NOT NULL, idempotency_key TEXT NOT NULL, job_id TEXT NOT NULL, submitted_at TIMESTAMP NOT NULL, expires_at TIMESTAMP NOT NULL, PRIMARY KEY (brc31_session_id, idempotency_key) );
Lookup is filter-on-now < expires_at; older rows count as cache miss for dedupe purposes (a resubmission past expiry is a billable fresh submission). Single-argument lookup is NOT a supported API per PRD P1.8 § "Idempotency cache schema (WI-4 normative)".
internal/paygate/jobs.go — async job lifecycle (submit / poll / fetch).
internal/paygate/manifest.go — discovery manifest (PRD 2 § 3.4).
internal/paygate/manifest_cache.go — discovery manifest refresh cadence per PRD P1.8 Obligation 2. Hard floor 60s, soft ceiling 1h (matches the result-retention window per PRD 2 § 4.4).
internal/paygate/payment_brc29.go — PRD 2 § 5.1 + PRD P1.8 Obligation 3.
retail_sats = ceil(compute_cost_usd * margin_multiplier * (100_000_000 / bsv_usd_rate))
BEEFs constructed after schedule_effective_after has elapsed are non-refundable per PRD P1.8 Obligation 3. BuildBEEF returns ErrStaleScheduleBEEF in that case so the runtime bears the cost observably rather than silently absorbing it.
internal/paygate/pin_check.go — PRD P1.8 Obligation 6 State A/B/C state machine. Auto-refresh applies ONLY to State A; State C is a terminal halt with no in-session auto-adoption of the new vkey.
internal/paygate/pipelining.go — PRD P1.8 Obligation 1 durable-binding gate. Preparatory work for step i+1 MAY proceed in parallel, but a nova-fold submission for step i+1 MUST NOT precede durable binding of step i's accumulator+result.
internal/paygate/retry.go — PRD 2 § 6.6 retry semantics + Retry-After.
internal/paygate/session.go — per-publish-session state.
Index ¶
- Constants
- Variables
- func BackoffStep(attempt int) time.Duration
- func Code(err error) string
- func CreditTokenFrom(err error) string
- func CreditTokenValidityFrom(err error) (issuedAt, expiresAt time.Time, ok bool)
- func IdempotencyKey(circuitID string, stepIndex uint64, hP [32]byte) string
- func IdempotencyPreimage(circuitID string, stepIndex uint64, hP [32]byte) []byte
- func IsRetryable(err error) bool
- func ParseRetryAfter(h http.Header, now time.Time) time.Duration
- func RetailSats(computeUSD float64, marginMultiplier int, bsvUSDRate float64) uint64
- func Sha256HexFor(body []byte) string
- type BEEF
- type BRC31Identity
- type BRC31Session
- type Category
- type Circuit
- type Client
- func (c *Client) FetchResult(ctx context.Context, sess *Session, jobID string) (*ResultBody, error)
- func (c *Client) Open(ctx context.Context, in OpenSessionInput) (*Session, error)
- func (c *Client) PollStatus(ctx context.Context, sess *Session, jobID string) (*StatusResp, error)
- func (c *Client) SubmitFold(ctx context.Context, sess *Session, in FoldStepInput) (*ResultBody, error)
- func (c *Client) SubmitFoldWithCredit(ctx context.Context, sess *Session, in FoldStepInput, creditToken string) (*ResultBody, error)
- type DisclosurePolicy
- type FoldStep
- type FoldStepInput
- type IdempCache
- type LedgerEntry
- type Manifest
- type ManifestCache
- type OpenSessionInput
- type PRDError
- type PaygateError
- type PinCheck
- type PinChecker
- type PinState
- type PipeliningGate
- type ResultBody
- type Schedule
- type Session
- type StatusResp
- type SubmitResp
- type TokenLedger
Constants ¶
const IdempotencyTTL = time.Hour
IdempotencyTTL is the default server-mirrored cache window (1h per PRD 2 § 6.5).
Variables ¶
var ( ErrAuthMissingBRC31 = &PaygateError{CodeStr: "AUTH_MISSING_BRC31", Cat: CategoryAuth, Retryabl: true} ErrAuthInvalidBRC31 = &PaygateError{CodeStr: "AUTH_INVALID_BRC31", Cat: CategoryAuth, Retryabl: true} ErrAuthSessionExpired = &PaygateError{CodeStr: "AUTH_SESSION_EXPIRED", Cat: CategoryAuth, Retryabl: true} ErrAuthScopeMismatch = &PaygateError{CodeStr: "AUTH_SCOPE_MISMATCH", Cat: CategoryAuth, Retryabl: false} )
auth sentinels.
var ( ErrPaymentBEEFInvalid = &PaygateError{CodeStr: "PAYMENT_BEEF_INVALID", Cat: CategoryPayment, Retryabl: true} ErrPaymentAmountInsufficient = &PaygateError{CodeStr: "PAYMENT_AMOUNT_INSUFFICIENT", Cat: CategoryPayment, Retryabl: true} ErrPaymentDoubleSpend = &PaygateError{CodeStr: "PAYMENT_DOUBLE_SPEND", Cat: CategoryPayment, Retryabl: false} ErrCreditTokenInvalid = &PaygateError{CodeStr: "PAYMENT_CREDIT_TOKEN_INVALID", Cat: CategoryPayment, Retryabl: false} ErrCreditTokenExpired = &PaygateError{CodeStr: "PAYMENT_CREDIT_TOKEN_EXPIRED", Cat: CategoryPayment, Retryabl: false} // Non-PRD-2 but normative per PRD P1.8 Obligation 3: ErrStaleScheduleBEEF = &PaygateError{CodeStr: "ERR_STALE_SCHEDULE_BEEF", Cat: CategoryPayment, Retryabl: false} )
payment sentinels.
var ( ErrValidationSchema = &PaygateError{CodeStr: "VALIDATION_SCHEMA", Cat: CategoryValidation, Retryabl: false} ErrValidationInputCapViolation = &PaygateError{CodeStr: "VALIDATION_INPUT_CAP_VIOLATION", Cat: CategoryValidation, Retryabl: false} ErrValidationUnknownCircuitID = &PaygateError{CodeStr: "VALIDATION_UNKNOWN_CIRCUIT_ID", Cat: CategoryValidation, Retryabl: false} ErrValidationWitnessShape = &PaygateError{CodeStr: "VALIDATION_WITNESS_SHAPE", Cat: CategoryValidation, Retryabl: true} ErrValidationOperationNotSupp = &PaygateError{CodeStr: "VALIDATION_OPERATION_NOT_SUPPORTED", Cat: CategoryValidation, Retryabl: false} // Non-PRD-2 but normative per PRD P1.8 Obligation 6: ErrCircuitPinMismatch = &PaygateError{CodeStr: "ERR_CIRCUIT_PIN_MISMATCH", Cat: CategoryValidation, Retryabl: false} )
validation sentinels.
var ( ErrComputeWorkerCrash = &PaygateError{CodeStr: "COMPUTE_WORKER_CRASH", Cat: CategoryCompute, Retryabl: true} ErrComputeTimeout = &PaygateError{CodeStr: "COMPUTE_TIMEOUT", Cat: CategoryCompute, Retryabl: true} ErrComputeResourceExhausted = &PaygateError{CodeStr: "COMPUTE_RESOURCE_EXHAUSTED", Cat: CategoryCompute, Retryabl: true} )
compute sentinels.
var ( ErrProofSelfVerifyFailed = &PaygateError{CodeStr: "PROOF_SELF_VERIFY_FAILED", Cat: CategoryLogical, Retryabl: false} ErrProofCircuitDeprecated = &PaygateError{CodeStr: "PROOF_CIRCUIT_DEPRECATED", Cat: CategoryLogical, Retryabl: false} )
logical sentinels.
var ( ErrServiceQueueSaturated = &PaygateError{CodeStr: "SERVICE_QUEUE_SATURATED", Cat: CategoryService, Retryabl: true} ErrResultExpired = &PaygateError{CodeStr: "RESULT_EXPIRED", Cat: CategoryService, Retryabl: false} )
service sentinels.
var ErrCircuitNotInRefreshedManifest = errors.New("paygate: refreshed manifest no longer advertises session circuit")
ErrCircuitNotInRefreshedManifest is returned by refreshManifestAndRevalidatePin when the freshly-fetched manifest no longer advertises the session's circuit. Surfacing this instead of silently allowing submission ensures a manifest that drops a circuit mid-session is treated as a hard failure for both the primary and credit-token retry paths.
var ErrIdempCacheMiss = errors.New("paygate: idempotency cache miss")
ErrIdempCacheMiss signals "no live cache row" — distinct from a PaygateError because it is internal to the client's bookkeeping.
var ErrSpeculativeFold = errors.New("paygate: speculative fold rejected (PRD P1.8 Obligation 1)")
ErrSpeculativeFold signals that the caller tried to submit step i+1 before step i's result was durably bound (PRD P1.8 Obligation 1).
Functions ¶
func BackoffStep ¶
BackoffStep returns the exponential-backoff delay for retry attempt number attempt (0-indexed). 1s, 2s, 4s, …, capped at 60s.
func Code ¶
Code extracts the stable code string from err (or any wrapper). Returns "" for nil or non-PaygateError.
func CreditTokenFrom ¶
CreditTokenFrom walks the error chain and returns the credit_token from the innermost *codedErr it finds. Returns "" if none.
func CreditTokenValidityFrom ¶
CreditTokenValidityFrom walks the error chain and returns the server-declared (issued_at, expires_at, ok) for the credit_token, where ok is true only when BOTH timestamps are present on the failure envelope. Callers MUST treat ok == false as "no validity declared" and refuse to persist the token (round-2 B3 fail-closed).
func IdempotencyKey ¶
IdempotencyKey returns hex_lower(SHA-256(preimage)) — 64 lowercase hex ASCII characters, suitable for the `Idempotency-Key` request header.
func IdempotencyPreimage ¶
IdempotencyPreimage returns the byte-exact preimage from PRD 2 § 6.5 errata. circuitID is NFC-normalised internally before the byte sequence is computed, so canonically-equivalent Unicode forms (composed vs decomposed) always produce the same preimage and the same idempotency key. Callers SHOULD pass NFC-normalised circuit IDs already, but the function is defensive: an upstream component that forgets to normalise cannot produce a divergent key and double-bill.
func IsRetryable ¶
IsRetryable returns the per-code retry hint from PRD 2 § 6.6. Caller MAY override based on other signals (e.g. Retry-After header).
func ParseRetryAfter ¶
ParseRetryAfter reads a `Retry-After` header per RFC 9110 §10.2.3 (delta-seconds OR HTTP-date). Returns 0 if absent or unparseable.
func RetailSats ¶
RetailSats computes ceil(usd * margin * 100_000_000 / rate).
func Sha256HexFor ¶
Sha256HexFor returns lowercase-hex SHA-256(body); exported for tests.
Types ¶
type BEEF ¶
BEEF is the constructed BRC-29 payment payload.
Wire-byte construction is delegated to the existing konareef secp256k1 publisher signing code (internal/identity/); BEEF.WireBytes is the finalised CBOR-ish payload to be hex-encoded into the X-Payment header. V1 ships a stub WireBytes that satisfies the conformance stub server; full BSV transaction graph is added by the live BEEF builder ticket (out of scope for v1 stub).
type BRC31Identity ¶
type BRC31Identity struct {
// contains filtered or unexported fields
}
BRC31Identity is the persistent secp256k1 keypair used to authenticate to PayGate ZK. Distinct from the publisher identity per PRD P1.8 § 1.
func LoadOrCreateBRC31Identity ¶
func LoadOrCreateBRC31Identity(path string) (*BRC31Identity, error)
LoadOrCreateBRC31Identity reads the secp256k1 private key from path, or generates and persists a new one if path does not exist. Mode 0600.
func (*BRC31Identity) PubKeyHex ¶
func (id *BRC31Identity) PubKeyHex() string
PubKeyHex returns the compressed-SEC pubkey as lowercase hex.
func (*BRC31Identity) Sign ¶
func (id *BRC31Identity) Sign(msg []byte) []byte
Sign returns the DER-encoded ECDSA signature of msg under the identity key.
type BRC31Session ¶
type BRC31Session struct {
ID string
Identity *BRC31Identity
}
BRC31Session is one BRC-31-scoped session with its server-issued ID.
func (*BRC31Session) AuthorizationHeader ¶
func (s *BRC31Session) AuthorizationHeader(preamble []byte) string
AuthorizationHeader produces the per-request Brc31 header value for the given canonical preamble (e.g. method+path bytes per the wire spec). Nonce is fresh per call.
type Category ¶
type Category string
Category is the closed enumeration from PRD 2 § 6.4.
func CategoryOf ¶
CategoryOf returns the closed-enumeration category for err.
type Circuit ¶
type Circuit struct {
VkeySha256 string `json:"vkey_sha256"`
VkeyURL string `json:"vkey_url"`
Pricing map[string]Schedule `json:"pricing"`
InputCaps map[string]interface{} `json:"input_caps"`
DeprecatedAfter *time.Time `json:"deprecated_after"`
}
Circuit is the per-circuit block from PRD 2 § 3.4.
type Client ¶
type Client struct {
BaseURL string
HTTP *http.Client
IdentityPath string
StateDBPath string
ManifestCache *ManifestCache
Resolver *vkeystore.Resolver
SaltBackend saltstore.Backend
PinChecker PinCheck // interface; defaults to *PinChecker in production wiring
IdempCache *IdempCache
TokenLedger *TokenLedger
// SubmitMaxRetries bounds the SubmitFold Retry-After loop.
// Zero means default (2). One initial attempt PLUS up to N retries.
SubmitMaxRetries int
}
Client is the konareef runtime's PayGate ZK API consumer.
func (*Client) FetchResult ¶
FetchResult issues GET /v1/jobs/{job_id}/result. A 410 Gone surfaces ErrResultExpired per PRD 2 § 4.4.
func (*Client) Open ¶
Open performs the pre-session ritual: refresh manifest, pin-check (State A/B/C), create BRC-31 session (v1-simplified — within-process only; see Non-goals), load Type-D salt if applicable, prepare the pipelining gate. Returns the ready Session.
func (*Client) PollStatus ¶
PollStatus issues GET /v1/jobs/{job_id}.
func (*Client) SubmitFold ¶
func (c *Client) SubmitFold(ctx context.Context, sess *Session, in FoldStepInput) (*ResultBody, error)
SubmitFold executes the canonical 11-step lifecycle from PRD P1.8 "Submit lifecycle":
- ensureFreshManifest
- assertNotSpeculative
- (witness/public-inputs supplied by caller)
- idempKey
- constructBEEF (or take credit_token)
- submit
- handle 202 / 4xx / 5xx
- on 5xx with credit_token: ledger.Persist
- poll status loop
- fetch result
- durableBind
func (*Client) SubmitFoldWithCredit ¶
func (c *Client) SubmitFoldWithCredit(ctx context.Context, sess *Session, in FoldStepInput, creditToken string) (*ResultBody, error)
SubmitFoldWithCredit retries a previously-failed step using the supplied credit_token. M-4 / M-8 exercise this path.
Round-3 B1 + B2 contract: the credit-token retry path MUST be no weaker than the primary path. That means:
- the manifest is re-fetched AND the State A/B/C pin check re-runs on a rotated vkey_sha256 (shared refreshManifestAndRevalidatePin helper) — a credit-retry MUST fail closed across a mid-session vkey rotation, exactly like SubmitFold;
- the post-submission IdempotencyCache.Insert error is fatal — swallowing it would leave the runtime reporting success while no durable (brc31_session_id, idempotency_key) → job_id binding exists, which the pipelining gate would then permit a later replay to slip past as a fresh BEEF for the same logical fold (duplicate-payment risk that round-2 B5 was meant to close).
type DisclosurePolicy ¶
type DisclosurePolicy string
DisclosurePolicy selects whether the runtime needs to look up a Type-D session salt from saltstore (Type-D) or skip salt lookup entirely because the bundle discloses the salt itself (Type-C). Required field — Open returns an error if it is the zero value.
const ( // DisclosureTypeC indicates the bundle discloses salt; no saltstore lookup. DisclosureTypeC DisclosurePolicy = "C" // DisclosureTypeD indicates the runtime MUST load salt from saltstore; missing is fatal. DisclosureTypeD DisclosurePolicy = "D" )
type FoldStepInput ¶
type FoldStepInput struct {
StepIndex uint64
PublicInputs map[string]interface{}
Witness map[string]interface{}
HP [32]byte // SHA-256 of canonical public-input vector
BSVUSDRate float64 // for BEEF sizing
}
FoldStepInput is the runtime-side input to one fold submission.
type IdempCache ¶
type IdempCache struct {
// contains filtered or unexported fields
}
IdempCache is the SQLite-backed `(brc31_session_id, idempotency_key)` → job_id store.
func OpenIdempCache ¶
func OpenIdempCache(dbPath string) (*IdempCache, error)
OpenIdempCache opens or creates the SQLite database at dbPath and migrates the schema.
func (*IdempCache) Close ¶
func (c *IdempCache) Close() error
Close releases the underlying database handle.
func (*IdempCache) Insert ¶
func (c *IdempCache) Insert(sessionID, idemKey, jobID string, submittedAt time.Time) error
Insert records (sessionID, idemKey) → jobID with submitted_at and a derived expires_at = submitted_at + IdempotencyTTL.
func (*IdempCache) Lookup ¶
func (c *IdempCache) Lookup(sessionID, idemKey string) (string, error)
Lookup returns the cached job_id for (sessionID, idemKey) if a row exists AND now < expires_at. Otherwise ErrIdempCacheMiss.
Single-argument lookup is intentionally NOT supported per PRD P1.8 WI-4: the server-side cache key is two-tuple, so the client mirror MUST be two-tuple. A future caller cannot accidentally collapse two distinct sessions presenting identical idempotency keys.
type LedgerEntry ¶
LedgerEntry is one durable credit_token record.
type Manifest ¶
type Manifest struct {
Service string `json:"service"`
Version string `json:"version"`
Operations []string `json:"operations"`
Circuits map[string]Circuit `json:"circuits"`
MarginMultiplier int `json:"margin_multiplier"`
ScheduleRevision int `json:"schedule_revision"`
ScheduleEffectiveAfter *time.Time `json:"schedule_effective_after"`
Signature map[string]string `json:"signature"`
}
Manifest mirrors PRD 2 § 3.4. Unknown fields are preserved as raw JSON in RawExtra so a future PRD 2 field bump does not require a client recompile to ignore.
func ParseManifest ¶
ParseManifest parses the JSON body of GET /v1/.well-known/x402-info.
func (*Manifest) ActiveSchedule ¶
ActiveSchedule returns the pricing row for (circuitID, op). Returns an error if either lookup fails.
func (*Manifest) IsScheduleStale ¶
IsScheduleStale returns true when ScheduleEffectiveAfter is non-nil and now >= ScheduleEffectiveAfter; PRD 2 § 5.3 calls this the transition-in-progress condition and PRD P1.8 Obligation 3 makes BEEFs built after this point non-refundable.
type ManifestCache ¶
type ManifestCache struct {
BaseURL string // base URL (no trailing slash) of the PayGate ZK service
HTTP *http.Client
Cadence time.Duration
// contains filtered or unexported fields
}
ManifestCache caches the discovery manifest in memory with refresh cadence. Safe for concurrent use.
func (*ManifestCache) EffectiveCadence ¶
func (mc *ManifestCache) EffectiveCadence() time.Duration
EffectiveCadence returns the configured cadence clamped to [60s, 1h]. An unset / zero Cadence resolves to defaultRefreshCadence (15 min).
type OpenSessionInput ¶
type OpenSessionInput struct {
CircuitID string
LineageID [16]byte // Type-D only; from pod manifest; used for saltstore lookup
DisclosurePolicy DisclosurePolicy // REQUIRED: "C" or "D"
}
OpenSessionInput drives Open().
DisclosurePolicy is REQUIRED. For Type-D, LineageID MUST be set and saltstore.ErrSaltNotFound is FATAL — Open returns the wrapped error and does NOT open the session. For Type-C, LineageID is ignored and saltstore is not consulted (the bundle carries the salt).
type PRDError ¶
type PRDError struct {
Code string `json:"code"`
Category Category `json:"category"`
Message string `json:"message"`
Details map[string]interface{} `json:"details"`
Retryable bool `json:"retryable"`
CreditToken string `json:"credit_token"`
CreditTokenIssuedAt *time.Time `json:"credit_token_issued_at,omitempty"`
CreditTokenExpiresAt *time.Time `json:"credit_token_expires_at,omitempty"`
RetryAfterSeconds int `json:"retry_after_seconds"`
}
PRDError mirrors PRD 2 § 6.3 envelope.
CreditTokenIssuedAt / CreditTokenExpiresAt carry the server-declared validity window for `credit_token` (round-2 B3). When the server omits either, the client treats the token as un-trustworthy and does NOT persist a synthesised "now + 1h" placeholder — that placeholder would let the runtime keep a credit_token alive past server-side expiry or reject a still-valid one early. The fields are zero-valued for non-payment failures.
type PaygateError ¶
PaygateError is the concrete sentinel type.
func (*PaygateError) Error ¶
func (e *PaygateError) Error() string
Error implements the error interface and returns the stable code string.
type PinCheck ¶
type PinCheck interface {
Check(ctx context.Context, circuitID, manifestPin string) (PinState, error)
}
PinCheck is the minimal interface SubmitFold's Open path consumes. Default production wiring is *PinChecker (vkeystore-backed). Tests can supply a noopPinChecker via newSmokeClient or Options.PinChecker.
type PinChecker ¶
type PinChecker struct {
Resolver *vkeystore.Resolver
Domain string // discovery manifest's paygate-zk service domain
}
PinChecker runs the pre-session pin check against the discovery manifest's vkey_sha256 commitment.
func (*PinChecker) Check ¶
Check applies the State A/B/C machine to (circuitID, manifestPin). Returns the state reached and any halt error.
- State B: cached vkey bytes hash-verify against manifestPin. Proceed. Round-2 B2 fix: this goes through LocalCache.Lookup so the cached vkey.bin is hashed and compared, NOT a bare path-existence check. A corrupted/tampered vkey.bin under the expected directory now surfaces ErrCircuitPinMismatch (or ErrVkeyCorrupt for malformed metadata) instead of silently proceeding to fold submission.
- State C: cached vkey hash diverges from manifestPin. Terminal halt with ErrCircuitPinMismatch; cache is NOT updated.
- State A: no local cache. Resolver is invoked; if the resolved vkey's hash matches manifestPin, cache it and report State A. If the resolved vkey hashes DIFFERENTLY from manifestPin, vkeystore returns ErrCircuitPinMismatch and we propagate it.
type PipeliningGate ¶
type PipeliningGate struct {
// contains filtered or unexported fields
}
PipeliningGate tracks the last durably-bound step index.
func (*PipeliningGate) AssertNotSpeculative ¶
func (g *PipeliningGate) AssertNotSpeculative(stepIndex uint64) error
AssertNotSpeculative checks that stepIndex is the very-next step after the last durably-bound step OR the last durably-bound step itself (idempotent replay). Step 0 is always allowed (genesis); thereafter the request MUST be exactly lastStep or lastStep+1.
func (*PipeliningGate) DurablyBind ¶
func (g *PipeliningGate) DurablyBind(stepIndex uint64, jobID string, accumulator []byte)
DurablyBind records the receipt of step stepIndex's /result. Only after this call may stepIndex+1 be submitted.
type ResultBody ¶
type ResultBody struct {
AccumulatorOut []byte `json:"accumulator_out"`
StepProof []byte `json:"step_proof"`
PublicInputsEchoed map[string]interface{} `json:"public_inputs_echoed"`
CostMeta map[string]interface{} `json:"cost_meta"`
JobID string `json:"job_id"`
}
ResultBody mirrors PRD 2 § 3.3 for nova-fold.
JobID is a konareef-local extension (PRD 2 § 3.3 erratum recommendation — the published spec returns it only in the 202 Accepted submit response, but tracking it on the result envelope makes M-3 / M-4 / M-6b assertions substantially simpler. The stub server echoes it; live PayGate servers MAY omit it and callers should fall back to the Idempotency-Key cache.
type Schedule ¶
type Schedule struct {
ComputeUSD float64 `json:"compute_usd"`
RetailUSD float64 `json:"retail_usd"`
AvailableAfter string `json:"available_after,omitempty"`
}
Schedule is one (compute_usd, retail_usd) pricing row.
type Session ¶
type Session struct {
BRC31Session *BRC31Session
DiscoveryManifest *Manifest
ManifestFetchedAt time.Time
CircuitID string
PinnedVkeyHash string // hex, mirrors manifest's vkey_sha256
CreditTokens *TokenLedger
IdempotencyCache *IdempCache
LastDurableStep *FoldStep
Gate *PipeliningGate
LineageID [16]byte
TypeDSalt [32]byte
}
Session is the per-publish-session bag of mutable state.
type StatusResp ¶
type StatusResp struct {
JobID string `json:"job_id"`
Status string `json:"status"`
ProofType string `json:"proof_type"`
CircuitID string `json:"circuit_id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
Error *PRDError `json:"error"`
RetryAfter int `json:"-"` // populated from header
}
StatusResp mirrors PRD 2 § 3.2.
type SubmitResp ¶
SubmitResp mirrors PRD 2 § 3.1 202 Accepted body.
type TokenLedger ¶
type TokenLedger struct {
// contains filtered or unexported fields
}
TokenLedger persists credit_tokens issued in failed-job response bodies.
func OpenTokenLedger ¶
func OpenTokenLedger(dbPath string) (*TokenLedger, error)
OpenTokenLedger opens or creates the SQLite database at dbPath and migrates the schema.
func (*TokenLedger) Close ¶
func (l *TokenLedger) Close() error
Close releases the underlying database handle.
func (*TokenLedger) Persist ¶
func (l *TokenLedger) Persist(token string, issuedAt, expiresAt time.Time) error
Persist records a freshly-issued credit_token. issuedAt + expiresAt are read from the server's failure-body envelope.
func (*TokenLedger) Take ¶
func (l *TokenLedger) Take(token string) (*LedgerEntry, error)
Take consumes a credit_token atomically. Returns ErrCreditTokenInvalid for unknown OR already-used tokens; ErrCreditTokenExpired for expired tokens. The single-use enforcement is the UPDATE … WHERE used_at IS NULL row count.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package stubserver — handlers + writers for the in-process stub PayGate.
|
Package stubserver — handlers + writers for the in-process stub PayGate. |