@stellaris-lab/por-sdk 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +77 -0
- package/dist/audit.d.ts +53 -0
- package/dist/audit.js +48 -0
- package/dist/backend.d.ts +70 -0
- package/dist/backend.js +91 -0
- package/dist/codec.d.ts +50 -0
- package/dist/codec.js +110 -0
- package/dist/constants.d.ts +35 -0
- package/dist/constants.js +60 -0
- package/dist/domain.d.ts +199 -0
- package/dist/domain.js +57 -0
- package/dist/encoding.d.ts +67 -0
- package/dist/encoding.js +120 -0
- package/dist/errors.d.ts +25 -0
- package/dist/errors.js +54 -0
- package/dist/events.d.ts +78 -0
- package/dist/events.js +66 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +19 -0
- package/dist/manifest.d.ts +22 -0
- package/dist/manifest.js +54 -0
- package/dist/operations.d.ts +109 -0
- package/dist/operations.js +164 -0
- package/dist/persistence.d.ts +67 -0
- package/dist/persistence.js +154 -0
- package/dist/pipeline.d.ts +37 -0
- package/dist/pipeline.js +95 -0
- package/dist/policy.d.ts +27 -0
- package/dist/policy.js +60 -0
- package/dist/prove.d.ts +27 -0
- package/dist/prove.js +76 -0
- package/dist/reconciler.d.ts +65 -0
- package/dist/reconciler.js +152 -0
- package/dist/registry.d.ts +71 -0
- package/dist/registry.js +167 -0
- package/dist/signals.d.ts +36 -0
- package/dist/signals.js +194 -0
- package/dist/stellar.d.ts +129 -0
- package/dist/stellar.js +256 -0
- package/dist/transport.d.ts +70 -0
- package/dist/transport.js +94 -0
- package/package.json +50 -0
package/README.md
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# @stellaris-lab/por-sdk
|
|
2
|
+
|
|
3
|
+
TypeScript/JavaScript SDK for Stellaris proof-of-reserves and solvency-gated minting flows on Stellar Soroban.
|
|
4
|
+
|
|
5
|
+
The SDK provides deterministic protocol utilities, local proof verification helpers, public-signal parsing/encoding, and Soroban client abstractions for issuer, verifier, and operator applications.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @stellaris-lab/por-sdk
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Requirements
|
|
14
|
+
|
|
15
|
+
- Node.js 20+
|
|
16
|
+
- ESM-compatible runtime
|
|
17
|
+
- Circuit artifacts when generating proofs locally
|
|
18
|
+
|
|
19
|
+
## Public Signal Parsing
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
import { parsePublicSignalsV3, encodePublicSignalsV3 } from "@stellaris-lab/por-sdk";
|
|
23
|
+
|
|
24
|
+
const signals = [
|
|
25
|
+
"1",
|
|
26
|
+
"28184704509896798694171806401809735674629341338708097298840167762771603184872",
|
|
27
|
+
"38920107329329417419205137842949329405942847694151540866319542913651737474578",
|
|
28
|
+
"0",
|
|
29
|
+
"1",
|
|
30
|
+
"1",
|
|
31
|
+
"1",
|
|
32
|
+
"9",
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
const parsed = parsePublicSignalsV3(signals);
|
|
36
|
+
const encoded = encodePublicSignalsV3(parsed);
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Local Proof Verification
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
import { verifyLocal } from "@stellaris-lab/por-sdk";
|
|
43
|
+
|
|
44
|
+
const ok = await verifyLocal(verificationKey, proofBundle);
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Protocol Compatibility
|
|
48
|
+
|
|
49
|
+
This package is tested against the language-neutral vectors in:
|
|
50
|
+
|
|
51
|
+
```txt
|
|
52
|
+
../protocol-spec/test-vectors/public-signals.json
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
The same vectors are also used by the Rust SDK, which keeps JS, TS, and Rust implementations aligned on public-signal order and validation rules.
|
|
56
|
+
|
|
57
|
+
## Publish Checklist
|
|
58
|
+
|
|
59
|
+
Before publishing a release:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
npm run check
|
|
63
|
+
npm pack --dry-run
|
|
64
|
+
npm publish --access public
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
The package intentionally publishes only:
|
|
68
|
+
|
|
69
|
+
```txt
|
|
70
|
+
dist/
|
|
71
|
+
README.md
|
|
72
|
+
package.json
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Status
|
|
76
|
+
|
|
77
|
+
Version `0.1.0` is a developer-preview SDK for audited testnet/demo flows. Production users should pin exact versions and verify deployed contract IDs, WASM hashes, circuit artifacts, and verification keys.
|
package/dist/audit.d.ts
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Audit artifacts for prover/Stellaris workflows.
|
|
3
|
+
*
|
|
4
|
+
* These records are safe to persist in backend logs or attach to operational run
|
|
5
|
+
* books. They intentionally exclude reserve balances and salts.
|
|
6
|
+
*/
|
|
7
|
+
import { AttestationReceipt, ContractDeployment, PeriodId, PublicKey, PublicSignals } from "./domain.js";
|
|
8
|
+
import { PolicyReport } from "./policy.js";
|
|
9
|
+
export type AuditStepStatus = "started" | "accepted" | "rejected" | "failed" | "submitted";
|
|
10
|
+
export interface AuditStep {
|
|
11
|
+
readonly at: string;
|
|
12
|
+
readonly name: string;
|
|
13
|
+
readonly status: AuditStepStatus;
|
|
14
|
+
readonly message?: string;
|
|
15
|
+
readonly context?: Record<string, unknown>;
|
|
16
|
+
}
|
|
17
|
+
export interface AttestationAuditLog {
|
|
18
|
+
readonly schemaVersion: "stellaris.audit.v1";
|
|
19
|
+
readonly workflowId: string;
|
|
20
|
+
readonly issuer: PublicKey;
|
|
21
|
+
readonly periodId: PeriodId;
|
|
22
|
+
readonly deployment: ContractDeployment;
|
|
23
|
+
readonly publicSignals?: PublicSignals;
|
|
24
|
+
readonly policyReport?: PolicyReport;
|
|
25
|
+
readonly receipt?: AttestationReceipt;
|
|
26
|
+
readonly steps: readonly AuditStep[];
|
|
27
|
+
}
|
|
28
|
+
export interface AuditSink {
|
|
29
|
+
record(step: AuditStep): void | Promise<void>;
|
|
30
|
+
}
|
|
31
|
+
export declare class InMemoryAuditSink implements AuditSink {
|
|
32
|
+
private readonly entries;
|
|
33
|
+
record(step: AuditStep): void;
|
|
34
|
+
snapshot(): readonly AuditStep[];
|
|
35
|
+
}
|
|
36
|
+
export declare function makeWorkflowId(issuer: PublicKey, periodId: PeriodId): string;
|
|
37
|
+
export declare function redactPublicSignals(signals: PublicSignals): PublicSignals;
|
|
38
|
+
export declare function makeAuditStep(input: {
|
|
39
|
+
readonly name: string;
|
|
40
|
+
readonly status: AuditStepStatus;
|
|
41
|
+
readonly message?: string;
|
|
42
|
+
readonly context?: Record<string, unknown>;
|
|
43
|
+
}): AuditStep;
|
|
44
|
+
export declare function makeAuditLog(input: {
|
|
45
|
+
readonly workflowId: string;
|
|
46
|
+
readonly issuer: PublicKey;
|
|
47
|
+
readonly periodId: PeriodId;
|
|
48
|
+
readonly deployment: ContractDeployment;
|
|
49
|
+
readonly steps: readonly AuditStep[];
|
|
50
|
+
readonly publicSignals?: PublicSignals;
|
|
51
|
+
readonly policyReport?: PolicyReport;
|
|
52
|
+
readonly receipt?: AttestationReceipt;
|
|
53
|
+
}): AttestationAuditLog;
|
package/dist/audit.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Audit artifacts for prover/Stellaris workflows.
|
|
3
|
+
*
|
|
4
|
+
* These records are safe to persist in backend logs or attach to operational run
|
|
5
|
+
* books. They intentionally exclude reserve balances and salts.
|
|
6
|
+
*/
|
|
7
|
+
export class InMemoryAuditSink {
|
|
8
|
+
entries = [];
|
|
9
|
+
record(step) {
|
|
10
|
+
this.entries.push(step);
|
|
11
|
+
}
|
|
12
|
+
snapshot() {
|
|
13
|
+
return [...this.entries];
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
export function makeWorkflowId(issuer, periodId) {
|
|
17
|
+
return `stellaris:${issuer}:${periodId}:${Date.now()}`;
|
|
18
|
+
}
|
|
19
|
+
export function redactPublicSignals(signals) {
|
|
20
|
+
return {
|
|
21
|
+
solvent: signals.solvent,
|
|
22
|
+
commitment: signals.commitment,
|
|
23
|
+
liabilities: signals.liabilities,
|
|
24
|
+
periodId: signals.periodId,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
export function makeAuditStep(input) {
|
|
28
|
+
return {
|
|
29
|
+
at: new Date().toISOString(),
|
|
30
|
+
name: input.name,
|
|
31
|
+
status: input.status,
|
|
32
|
+
...(input.message === undefined ? {} : { message: input.message }),
|
|
33
|
+
...(input.context === undefined ? {} : { context: input.context }),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
export function makeAuditLog(input) {
|
|
37
|
+
return {
|
|
38
|
+
schemaVersion: "stellaris.audit.v1",
|
|
39
|
+
workflowId: input.workflowId,
|
|
40
|
+
issuer: input.issuer,
|
|
41
|
+
periodId: input.periodId,
|
|
42
|
+
deployment: input.deployment,
|
|
43
|
+
steps: [...input.steps],
|
|
44
|
+
...(input.publicSignals === undefined ? {} : { publicSignals: redactPublicSignals(input.publicSignals) }),
|
|
45
|
+
...(input.policyReport === undefined ? {} : { policyReport: input.policyReport }),
|
|
46
|
+
...(input.receipt === undefined ? {} : { receipt: input.receipt }),
|
|
47
|
+
};
|
|
48
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* backend.ts — swappable proving-backend seam (Milestone D1, SDK side).
|
|
3
|
+
*
|
|
4
|
+
* The contract abstracts verification behind a `VerifierBackend` trait
|
|
5
|
+
* (contracts/stellaris/src/verifier.rs); this is the symmetric SDK abstraction
|
|
6
|
+
* for PROOF GENERATION. `prove.ts` historically hardcoded `snarkjs.groth16`;
|
|
7
|
+
* this interface makes Groth16 ONE implementation, not a baked-in assumption, so
|
|
8
|
+
* a future PLONK / post-quantum backend can plug in without touching the attest
|
|
9
|
+
* pipeline, the public-signal ABI, or the byte-encoding boundary.
|
|
10
|
+
*
|
|
11
|
+
* Design (deliberately minimal — no speculative second backend is shipped):
|
|
12
|
+
* - `ProvingBackend` : interface { version, prove(witnessInput, artifacts),
|
|
13
|
+
* verify(vk, bundle) }
|
|
14
|
+
* - `Groth16Backend` : the real, only implementation (snarkjs groth16)
|
|
15
|
+
* - `ProvingVersion` : on-the-wire backend tag, mirrors the contract's
|
|
16
|
+
* `VerifierVersion` (Groth16 = 1; 0 is unset/invalid)
|
|
17
|
+
*
|
|
18
|
+
* The proof-shape (`SnarkJsGroth16Proof`) and `ProofBundle` are unchanged: only
|
|
19
|
+
* the call seam is abstracted. Witness construction stays in prove.ts because it
|
|
20
|
+
* is statement-specific (reserve vector), not backend-specific.
|
|
21
|
+
*/
|
|
22
|
+
import { ProofBundle, ProvingArtifacts } from "./domain.js";
|
|
23
|
+
/**
|
|
24
|
+
* On-the-wire backend selector. Mirrors the contract's `VerifierVersion`
|
|
25
|
+
* (verifier.rs): Groth16 is `1`, leaving `0` as an explicit "unset/invalid"
|
|
26
|
+
* sentinel. A manifest or SDK caller can request a specific backend by tag.
|
|
27
|
+
*/
|
|
28
|
+
export declare enum ProvingVersion {
|
|
29
|
+
Groth16 = 1
|
|
30
|
+
}
|
|
31
|
+
/** The witness input shape a backend feeds its prover (statement-specific). */
|
|
32
|
+
export interface WitnessInput {
|
|
33
|
+
readonly [signal: string]: string | string[];
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* A proof-generation/verification backend. Groth16 is the only backend today;
|
|
37
|
+
* a future backend implements this same interface so `generateProofFromInput`
|
|
38
|
+
* and the attest pipeline are backend-agnostic.
|
|
39
|
+
*/
|
|
40
|
+
export interface ProvingBackend {
|
|
41
|
+
/** The on-the-wire version tag this backend answers to. */
|
|
42
|
+
readonly version: ProvingVersion;
|
|
43
|
+
/** Generate a proof bundle from a witness input + proving artifacts. */
|
|
44
|
+
prove(witnessInput: WitnessInput, artifacts: ProvingArtifacts): Promise<ProofBundle>;
|
|
45
|
+
/** Verify a proof bundle locally against a verification key document. */
|
|
46
|
+
verify(verificationKey: Record<string, unknown>, bundle: ProofBundle): Promise<boolean>;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* The Groth16 backend — the real, only implementation. Wraps snarkjs groth16,
|
|
50
|
+
* the exact code path `prove.ts` used before the seam existed.
|
|
51
|
+
*/
|
|
52
|
+
export declare class Groth16Backend implements ProvingBackend {
|
|
53
|
+
readonly version = ProvingVersion.Groth16;
|
|
54
|
+
prove(witnessInput: WitnessInput, artifacts: ProvingArtifacts): Promise<ProofBundle>;
|
|
55
|
+
verify(verificationKey: Record<string, unknown>, bundle: ProofBundle): Promise<boolean>;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Resolve a backend by version tag. Today only Groth16 resolves. Unknown tags
|
|
59
|
+
* throw, mirroring the contract's `WrongVerifierVersion`.
|
|
60
|
+
*
|
|
61
|
+
* SCOPE (honest boundary): a new SDK backend is one more arm here, but that is
|
|
62
|
+
* only the proving half. A different proof shape also needs a contract entrypoint
|
|
63
|
+
* that accepts it (the Soroban `attest*` signatures bind the concrete proof type
|
|
64
|
+
* into XDR) plus its own on-chain verifier — NOT just a `dispatch_verify` arm.
|
|
65
|
+
* This resolver is the SDK-side version-routing seam, not a claim that a new
|
|
66
|
+
* proving system is free.
|
|
67
|
+
*/
|
|
68
|
+
export declare function backendFor(version: ProvingVersion): ProvingBackend;
|
|
69
|
+
/** The default backend the SDK uses when no explicit backend is requested. */
|
|
70
|
+
export declare const DEFAULT_PROVING_BACKEND: ProvingBackend;
|
package/dist/backend.js
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* backend.ts — swappable proving-backend seam (Milestone D1, SDK side).
|
|
3
|
+
*
|
|
4
|
+
* The contract abstracts verification behind a `VerifierBackend` trait
|
|
5
|
+
* (contracts/stellaris/src/verifier.rs); this is the symmetric SDK abstraction
|
|
6
|
+
* for PROOF GENERATION. `prove.ts` historically hardcoded `snarkjs.groth16`;
|
|
7
|
+
* this interface makes Groth16 ONE implementation, not a baked-in assumption, so
|
|
8
|
+
* a future PLONK / post-quantum backend can plug in without touching the attest
|
|
9
|
+
* pipeline, the public-signal ABI, or the byte-encoding boundary.
|
|
10
|
+
*
|
|
11
|
+
* Design (deliberately minimal — no speculative second backend is shipped):
|
|
12
|
+
* - `ProvingBackend` : interface { version, prove(witnessInput, artifacts),
|
|
13
|
+
* verify(vk, bundle) }
|
|
14
|
+
* - `Groth16Backend` : the real, only implementation (snarkjs groth16)
|
|
15
|
+
* - `ProvingVersion` : on-the-wire backend tag, mirrors the contract's
|
|
16
|
+
* `VerifierVersion` (Groth16 = 1; 0 is unset/invalid)
|
|
17
|
+
*
|
|
18
|
+
* The proof-shape (`SnarkJsGroth16Proof`) and `ProofBundle` are unchanged: only
|
|
19
|
+
* the call seam is abstracted. Witness construction stays in prove.ts because it
|
|
20
|
+
* is statement-specific (reserve vector), not backend-specific.
|
|
21
|
+
*/
|
|
22
|
+
import { StellarisError, wrapUnknown } from "./errors.js";
|
|
23
|
+
import { assertVerificationKeyShape } from "./manifest.js";
|
|
24
|
+
import { parsePublicSignals } from "./signals.js";
|
|
25
|
+
/**
|
|
26
|
+
* On-the-wire backend selector. Mirrors the contract's `VerifierVersion`
|
|
27
|
+
* (verifier.rs): Groth16 is `1`, leaving `0` as an explicit "unset/invalid"
|
|
28
|
+
* sentinel. A manifest or SDK caller can request a specific backend by tag.
|
|
29
|
+
*/
|
|
30
|
+
export var ProvingVersion;
|
|
31
|
+
(function (ProvingVersion) {
|
|
32
|
+
ProvingVersion[ProvingVersion["Groth16"] = 1] = "Groth16";
|
|
33
|
+
})(ProvingVersion || (ProvingVersion = {}));
|
|
34
|
+
/**
|
|
35
|
+
* The Groth16 backend — the real, only implementation. Wraps snarkjs groth16,
|
|
36
|
+
* the exact code path `prove.ts` used before the seam existed.
|
|
37
|
+
*/
|
|
38
|
+
export class Groth16Backend {
|
|
39
|
+
version = ProvingVersion.Groth16;
|
|
40
|
+
async prove(witnessInput, artifacts) {
|
|
41
|
+
if (artifacts.verificationKey) {
|
|
42
|
+
assertVerificationKeyShape(artifacts.verificationKey);
|
|
43
|
+
}
|
|
44
|
+
try {
|
|
45
|
+
const snarkjs = await import("snarkjs");
|
|
46
|
+
const { proof, publicSignals } = await snarkjs.groth16.fullProve(witnessInput, artifacts.wasmUrl, artifacts.zkeyUrl);
|
|
47
|
+
const parsed = parsePublicSignals(publicSignals);
|
|
48
|
+
return {
|
|
49
|
+
proof: proof,
|
|
50
|
+
publicSignals,
|
|
51
|
+
parsed,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
catch (cause) {
|
|
55
|
+
if (cause instanceof StellarisError) {
|
|
56
|
+
throw cause;
|
|
57
|
+
}
|
|
58
|
+
throw wrapUnknown("proving", "failed to generate Groth16 proof", cause);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
async verify(verificationKey, bundle) {
|
|
62
|
+
try {
|
|
63
|
+
const snarkjs = await import("snarkjs");
|
|
64
|
+
return await snarkjs.groth16.verify(verificationKey, bundle.publicSignals, bundle.proof);
|
|
65
|
+
}
|
|
66
|
+
catch (cause) {
|
|
67
|
+
throw wrapUnknown("verification", "failed to verify proof locally", cause);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Resolve a backend by version tag. Today only Groth16 resolves. Unknown tags
|
|
73
|
+
* throw, mirroring the contract's `WrongVerifierVersion`.
|
|
74
|
+
*
|
|
75
|
+
* SCOPE (honest boundary): a new SDK backend is one more arm here, but that is
|
|
76
|
+
* only the proving half. A different proof shape also needs a contract entrypoint
|
|
77
|
+
* that accepts it (the Soroban `attest*` signatures bind the concrete proof type
|
|
78
|
+
* into XDR) plus its own on-chain verifier — NOT just a `dispatch_verify` arm.
|
|
79
|
+
* This resolver is the SDK-side version-routing seam, not a claim that a new
|
|
80
|
+
* proving system is free.
|
|
81
|
+
*/
|
|
82
|
+
export function backendFor(version) {
|
|
83
|
+
switch (version) {
|
|
84
|
+
case ProvingVersion.Groth16:
|
|
85
|
+
return new Groth16Backend();
|
|
86
|
+
default:
|
|
87
|
+
throw StellarisError.validation(`unsupported proving backend version: ${version}`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/** The default backend the SDK uses when no explicit backend is requested. */
|
|
91
|
+
export const DEFAULT_PROVING_BACKEND = new Groth16Backend();
|
package/dist/codec.d.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Encoding boundary for contract arguments.
|
|
3
|
+
*
|
|
4
|
+
* Keep all low-level proof/public-signal encoding in one module. When Stellar SDK
|
|
5
|
+
* exposes first-class BLS12-381 XDR helpers, only this file should change.
|
|
6
|
+
*/
|
|
7
|
+
import { Attestation, AttestationV2, AttestationV3, ProofBundle, SnarkJsGroth16Proof } from "./domain.js";
|
|
8
|
+
import { ContractProofBytes } from "./encoding.js";
|
|
9
|
+
export interface ContractProofArgs {
|
|
10
|
+
readonly a: readonly [string, string];
|
|
11
|
+
readonly b: readonly [readonly [string, string], readonly [string, string]];
|
|
12
|
+
readonly c: readonly [string, string];
|
|
13
|
+
}
|
|
14
|
+
export interface ContractAttestArgs {
|
|
15
|
+
readonly proof: ContractProofArgs;
|
|
16
|
+
readonly publicSignals: readonly string[];
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Byte-exact `attest` arguments ready for ScVal wrapping by a transport.
|
|
20
|
+
* `proof` holds the G1/G2 uncompressed buffers; `publicSignals` are 32-byte
|
|
21
|
+
* big-endian U256 buffers in circuit declaration order. This is the single
|
|
22
|
+
* shared on-chain encoding — see `encoding.ts`.
|
|
23
|
+
*/
|
|
24
|
+
export interface ContractAttestBytes {
|
|
25
|
+
readonly proof: ContractProofBytes;
|
|
26
|
+
readonly publicSignals: readonly Uint8Array[];
|
|
27
|
+
}
|
|
28
|
+
export declare function proofToContractArgs(proof: SnarkJsGroth16Proof): ContractProofArgs;
|
|
29
|
+
export declare function bundleToContractArgs(bundle: ProofBundle): ContractAttestArgs;
|
|
30
|
+
/**
|
|
31
|
+
* Serialize a proof bundle to the byte-exact `attest` arguments the on-chain
|
|
32
|
+
* verifier consumes. This is the production encoding path: G1 points -> 96-byte
|
|
33
|
+
* uncompressed buffers, G2 -> 192-byte (with the c1||c0 Fp2 ordering), and each
|
|
34
|
+
* public signal -> a 32-byte big-endian U256 buffer. A transport only needs to
|
|
35
|
+
* wrap these in ScVal (ScBytes / ScU256) — no field math at the transport layer.
|
|
36
|
+
*/
|
|
37
|
+
export declare function bundleToContractBytes(bundle: ProofBundle): ContractAttestBytes;
|
|
38
|
+
export declare function decodeAttestation(raw: unknown): Attestation;
|
|
39
|
+
/**
|
|
40
|
+
* Decode a v2 attestation (solvency with SNARK-proven liabilities). `liabRoot`
|
|
41
|
+
* is a full-width field element (a hash) kept as a string; `liabTotal` is the
|
|
42
|
+
* proven total liabilities.
|
|
43
|
+
*/
|
|
44
|
+
export declare function decodeAttestationV2(raw: unknown): AttestationV2;
|
|
45
|
+
/**
|
|
46
|
+
* Decode a v3 multi-asset attestation. `assetSolvent` is the per-asset flag
|
|
47
|
+
* vector (Soroban returns it as an array of bools); `reserveCommitment` and
|
|
48
|
+
* `priceCommitment` are full-width field elements kept as strings.
|
|
49
|
+
*/
|
|
50
|
+
export declare function decodeAttestationV3(raw: unknown): AttestationV3;
|
package/dist/codec.js
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Encoding boundary for contract arguments.
|
|
3
|
+
*
|
|
4
|
+
* Keep all low-level proof/public-signal encoding in one module. When Stellar SDK
|
|
5
|
+
* exposes first-class BLS12-381 XDR helpers, only this file should change.
|
|
6
|
+
*/
|
|
7
|
+
import { proofToBytes, signalToBytes, } from "./encoding.js";
|
|
8
|
+
import { StellarisError } from "./errors.js";
|
|
9
|
+
import { parsePublicSignals } from "./signals.js";
|
|
10
|
+
export function proofToContractArgs(proof) {
|
|
11
|
+
if (!Array.isArray(proof.pi_a) || !Array.isArray(proof.pi_b) || !Array.isArray(proof.pi_c)) {
|
|
12
|
+
throw StellarisError.encoding("invalid snarkjs proof shape");
|
|
13
|
+
}
|
|
14
|
+
return {
|
|
15
|
+
a: [proof.pi_a[0], proof.pi_a[1]],
|
|
16
|
+
b: [
|
|
17
|
+
[proof.pi_b[0][0], proof.pi_b[0][1]],
|
|
18
|
+
[proof.pi_b[1][0], proof.pi_b[1][1]],
|
|
19
|
+
],
|
|
20
|
+
c: [proof.pi_c[0], proof.pi_c[1]],
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export function bundleToContractArgs(bundle) {
|
|
24
|
+
const parsed = parsePublicSignals(bundle.publicSignals);
|
|
25
|
+
if (parsed.commitment !== bundle.parsed.commitment) {
|
|
26
|
+
throw StellarisError.encoding("bundle parsed commitment differs from raw public signals");
|
|
27
|
+
}
|
|
28
|
+
return {
|
|
29
|
+
proof: proofToContractArgs(bundle.proof),
|
|
30
|
+
publicSignals: [...bundle.publicSignals],
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Serialize a proof bundle to the byte-exact `attest` arguments the on-chain
|
|
35
|
+
* verifier consumes. This is the production encoding path: G1 points -> 96-byte
|
|
36
|
+
* uncompressed buffers, G2 -> 192-byte (with the c1||c0 Fp2 ordering), and each
|
|
37
|
+
* public signal -> a 32-byte big-endian U256 buffer. A transport only needs to
|
|
38
|
+
* wrap these in ScVal (ScBytes / ScU256) — no field math at the transport layer.
|
|
39
|
+
*/
|
|
40
|
+
export function bundleToContractBytes(bundle) {
|
|
41
|
+
const parsed = parsePublicSignals(bundle.publicSignals);
|
|
42
|
+
if (parsed.commitment !== bundle.parsed.commitment) {
|
|
43
|
+
throw StellarisError.encoding("bundle parsed commitment differs from raw public signals");
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
proof: proofToBytes(bundle.proof),
|
|
47
|
+
publicSignals: bundle.publicSignals.map((s) => signalToBytes(s)),
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
export function decodeAttestation(raw) {
|
|
51
|
+
if (!raw || typeof raw !== "object") {
|
|
52
|
+
throw StellarisError.encoding("attestation response is not an object");
|
|
53
|
+
}
|
|
54
|
+
const value = raw;
|
|
55
|
+
return {
|
|
56
|
+
commitment: String(value.commitment),
|
|
57
|
+
liabilities: BigInt(String(value.liabilities)),
|
|
58
|
+
solvent: Boolean(value.solvent),
|
|
59
|
+
ledgerTs: BigInt(String(value.ledgerTs ?? value.ledger_ts ?? 0)),
|
|
60
|
+
periodId: BigInt(String(value.periodId ?? value.period_id)),
|
|
61
|
+
issuer: String(value.issuer),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Decode a v2 attestation (solvency with SNARK-proven liabilities). `liabRoot`
|
|
66
|
+
* is a full-width field element (a hash) kept as a string; `liabTotal` is the
|
|
67
|
+
* proven total liabilities.
|
|
68
|
+
*/
|
|
69
|
+
export function decodeAttestationV2(raw) {
|
|
70
|
+
if (!raw || typeof raw !== "object") {
|
|
71
|
+
throw StellarisError.encoding("v2 attestation response is not an object");
|
|
72
|
+
}
|
|
73
|
+
const value = raw;
|
|
74
|
+
return {
|
|
75
|
+
reserveCommitment: String(value.reserveCommitment ?? value.reserve_commitment),
|
|
76
|
+
liabRoot: String(value.liabRoot ?? value.liab_root),
|
|
77
|
+
liabTotal: BigInt(String(value.liabTotal ?? value.liab_total)),
|
|
78
|
+
solvent: Boolean(value.solvent),
|
|
79
|
+
ledgerTs: BigInt(String(value.ledgerTs ?? value.ledger_ts ?? 0)),
|
|
80
|
+
periodId: BigInt(String(value.periodId ?? value.period_id)),
|
|
81
|
+
issuer: String(value.issuer),
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Decode a v3 multi-asset attestation. `assetSolvent` is the per-asset flag
|
|
86
|
+
* vector (Soroban returns it as an array of bools); `reserveCommitment` and
|
|
87
|
+
* `priceCommitment` are full-width field elements kept as strings.
|
|
88
|
+
*/
|
|
89
|
+
export function decodeAttestationV3(raw) {
|
|
90
|
+
if (!raw || typeof raw !== "object") {
|
|
91
|
+
throw StellarisError.encoding("v3 attestation response is not an object");
|
|
92
|
+
}
|
|
93
|
+
const value = raw;
|
|
94
|
+
const rawFlags = value.assetSolvent ?? value.asset_solvent ?? [];
|
|
95
|
+
if (!Array.isArray(rawFlags)) {
|
|
96
|
+
throw StellarisError.encoding("v3 assetSolvent is not an array");
|
|
97
|
+
}
|
|
98
|
+
const assetSolvent = rawFlags.map((f) => Boolean(typeof f === "string" ? f === "1" || f === "true" : f));
|
|
99
|
+
return {
|
|
100
|
+
aggregateSolvent: Boolean(value.aggregateSolvent ?? value.aggregate_solvent),
|
|
101
|
+
reserveCommitment: String(value.reserveCommitment ?? value.reserve_commitment),
|
|
102
|
+
priceCommitment: String(value.priceCommitment ?? value.price_commitment),
|
|
103
|
+
assetSolvent,
|
|
104
|
+
oracleBound: Boolean(value.oracleBound ?? value.oracle_bound),
|
|
105
|
+
custodianBound: Boolean(value.custodianBound ?? value.custodian_bound),
|
|
106
|
+
ledgerTs: BigInt(String(value.ledgerTs ?? value.ledger_ts ?? 0)),
|
|
107
|
+
periodId: BigInt(String(value.periodId ?? value.period_id)),
|
|
108
|
+
issuer: String(value.issuer),
|
|
109
|
+
};
|
|
110
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* constants.ts — Public signal order, circuit parameters, and contract metadata.
|
|
3
|
+
*
|
|
4
|
+
* These must match:
|
|
5
|
+
* - `circuits/PUBLIC_SIGNALS.md` (generated from snarkjs)
|
|
6
|
+
* - `contracts/stellaris/src/types.rs` (SIG_* constants)
|
|
7
|
+
*
|
|
8
|
+
* If the circuit compilation changes the signal order, update ONLY
|
|
9
|
+
* `circuits/PUBLIC_SIGNALS.md`, then synchronize this file.
|
|
10
|
+
*/
|
|
11
|
+
export declare const SIGNAL_INDEX_SOLVENT = 0;
|
|
12
|
+
export declare const SIGNAL_INDEX_COMMITMENT = 1;
|
|
13
|
+
export declare const SIGNAL_INDEX_LIABILITIES = 2;
|
|
14
|
+
export declare const SIGNAL_INDEX_PERIOD = 3;
|
|
15
|
+
export declare const N_PUBLIC_SIGNALS = 4;
|
|
16
|
+
export declare const SIGNAL_INDEX_V2_SOLVENT = 0;
|
|
17
|
+
export declare const SIGNAL_INDEX_V2_RESERVE_COMMITMENT = 1;
|
|
18
|
+
export declare const SIGNAL_INDEX_V2_LIAB_ROOT = 2;
|
|
19
|
+
export declare const SIGNAL_INDEX_V2_LIAB_TOTAL = 3;
|
|
20
|
+
export declare const SIGNAL_INDEX_V2_PERIOD = 4;
|
|
21
|
+
export declare const N_PUBLIC_SIGNALS_V2 = 5;
|
|
22
|
+
export declare const SIGNAL_INDEX_V3_AGGREGATE_SOLVENT = 0;
|
|
23
|
+
export declare const SIGNAL_INDEX_V3_RESERVE_COMMITMENT = 1;
|
|
24
|
+
export declare const SIGNAL_INDEX_V3_PRICE_COMMITMENT = 2;
|
|
25
|
+
export declare const SIGNAL_INDEX_V3_ASSET_SOLVENT_BASE = 3;
|
|
26
|
+
export declare const N_ASSETS_V3 = 4;
|
|
27
|
+
export declare const SIGNAL_INDEX_V3_PERIOD: number;
|
|
28
|
+
export declare const N_PUBLIC_SIGNALS_V3: number;
|
|
29
|
+
/** Number of reserve balance slots in the proof-of-reserves circuit. */
|
|
30
|
+
export declare const N_RESERVES = 16;
|
|
31
|
+
/** Bit-width of each reserve balance (64-bit unsigned integers). */
|
|
32
|
+
export declare const N_BITS = 64;
|
|
33
|
+
/** Maximum reserve balance value (2^64 - 1). */
|
|
34
|
+
export declare const MAX_RESERVE: bigint;
|
|
35
|
+
export declare const CURVE = "bls12381";
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* constants.ts — Public signal order, circuit parameters, and contract metadata.
|
|
3
|
+
*
|
|
4
|
+
* These must match:
|
|
5
|
+
* - `circuits/PUBLIC_SIGNALS.md` (generated from snarkjs)
|
|
6
|
+
* - `contracts/stellaris/src/types.rs` (SIG_* constants)
|
|
7
|
+
*
|
|
8
|
+
* If the circuit compilation changes the signal order, update ONLY
|
|
9
|
+
* `circuits/PUBLIC_SIGNALS.md`, then synchronize this file.
|
|
10
|
+
*/
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
// Public signal indices (lock-step with circuits/PUBLIC_SIGNALS.md)
|
|
13
|
+
// Expected order from snarkjs: [ solvent, commitment, liabilities, period ]
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
export const SIGNAL_INDEX_SOLVENT = 0;
|
|
16
|
+
export const SIGNAL_INDEX_COMMITMENT = 1;
|
|
17
|
+
export const SIGNAL_INDEX_LIABILITIES = 2;
|
|
18
|
+
export const SIGNAL_INDEX_PERIOD = 3;
|
|
19
|
+
export const N_PUBLIC_SIGNALS = 4;
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
// v2 public signal indices (solvency with SNARK-proven liabilities).
|
|
22
|
+
// Expected order from snarkjs:
|
|
23
|
+
// [ solvent, reserveCommitment, liabRoot, liabTotal, period ]
|
|
24
|
+
// Lock-step with contracts/stellaris/src/types.rs (SIG2_* constants).
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
export const SIGNAL_INDEX_V2_SOLVENT = 0;
|
|
27
|
+
export const SIGNAL_INDEX_V2_RESERVE_COMMITMENT = 1;
|
|
28
|
+
export const SIGNAL_INDEX_V2_LIAB_ROOT = 2;
|
|
29
|
+
export const SIGNAL_INDEX_V2_LIAB_TOTAL = 3;
|
|
30
|
+
export const SIGNAL_INDEX_V2_PERIOD = 4;
|
|
31
|
+
export const N_PUBLIC_SIGNALS_V2 = 5;
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
// v3 public signal indices (multi-asset solvency with oracle-priced aggregate).
|
|
34
|
+
// Expected order from snarkjs:
|
|
35
|
+
// [ aggregateSolvent, reserveCommitment, priceCommitment,
|
|
36
|
+
// assetSolvent[0..N_ASSETS_V3-1], period ]
|
|
37
|
+
// Lock-step with contracts/stellaris/src/types.rs (SIG3_* constants) and
|
|
38
|
+
// circuits/PUBLIC_SIGNALS.md.
|
|
39
|
+
// ---------------------------------------------------------------------------
|
|
40
|
+
export const SIGNAL_INDEX_V3_AGGREGATE_SOLVENT = 0;
|
|
41
|
+
export const SIGNAL_INDEX_V3_RESERVE_COMMITMENT = 1;
|
|
42
|
+
export const SIGNAL_INDEX_V3_PRICE_COMMITMENT = 2;
|
|
43
|
+
export const SIGNAL_INDEX_V3_ASSET_SOLVENT_BASE = 3; // assetSolvent[a] at base + a
|
|
44
|
+
export const N_ASSETS_V3 = 4;
|
|
45
|
+
export const SIGNAL_INDEX_V3_PERIOD = SIGNAL_INDEX_V3_ASSET_SOLVENT_BASE + N_ASSETS_V3; // 7
|
|
46
|
+
export const N_PUBLIC_SIGNALS_V3 = SIGNAL_INDEX_V3_PERIOD + 1; // 8
|
|
47
|
+
// ---------------------------------------------------------------------------
|
|
48
|
+
// Circuit parameters
|
|
49
|
+
// ---------------------------------------------------------------------------
|
|
50
|
+
/** Number of reserve balance slots in the proof-of-reserves circuit. */
|
|
51
|
+
export const N_RESERVES = 16;
|
|
52
|
+
/** Bit-width of each reserve balance (64-bit unsigned integers). */
|
|
53
|
+
export const N_BITS = 64;
|
|
54
|
+
/** Maximum reserve balance value (2^64 - 1). */
|
|
55
|
+
export const MAX_RESERVE = (1n << BigInt(N_BITS)) - 1n;
|
|
56
|
+
// ---------------------------------------------------------------------------
|
|
57
|
+
// Curve identifier matching the compiled circuit and Soroban verifier.
|
|
58
|
+
// BLS12-381 per the official Stellar groth16_verifier example.
|
|
59
|
+
// ---------------------------------------------------------------------------
|
|
60
|
+
export const CURVE = "bls12381";
|