@psavelis/enterprise-blockchain 0.1.0 → 1.1.1
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 +15 -2
- package/dist/aid-settlement/application/reconciler.d.ts +13 -0
- package/dist/aid-settlement/application/reconciler.d.ts.map +1 -0
- package/dist/aid-settlement/application/reconciler.js +77 -0
- package/dist/aid-settlement/domain/entities.d.ts +24 -0
- package/dist/aid-settlement/domain/entities.d.ts.map +1 -0
- package/dist/aid-settlement/domain/entities.js +1 -0
- package/dist/aid-settlement/domain/ports.d.ts +10 -0
- package/dist/aid-settlement/domain/ports.d.ts.map +1 -0
- package/dist/aid-settlement/domain/ports.js +1 -0
- package/dist/aid-settlement/index.d.ts +19 -0
- package/dist/aid-settlement/index.d.ts.map +1 -0
- package/dist/aid-settlement/index.js +23 -0
- package/dist/aid-settlement/infrastructure/in-memory-store.d.ts +12 -0
- package/dist/aid-settlement/infrastructure/in-memory-store.d.ts.map +1 -0
- package/dist/aid-settlement/infrastructure/in-memory-store.js +17 -0
- package/dist/credentialing/application/clearance-evaluator.d.ts +10 -0
- package/dist/credentialing/application/clearance-evaluator.d.ts.map +1 -0
- package/dist/credentialing/application/clearance-evaluator.js +63 -0
- package/dist/credentialing/domain/entities.d.ts +28 -0
- package/dist/credentialing/domain/entities.d.ts.map +1 -0
- package/dist/credentialing/domain/entities.js +1 -0
- package/dist/credentialing/domain/ports.d.ts +9 -0
- package/dist/credentialing/domain/ports.d.ts.map +1 -0
- package/dist/credentialing/domain/ports.js +1 -0
- package/dist/credentialing/index.d.ts +19 -0
- package/dist/credentialing/index.d.ts.map +1 -0
- package/dist/credentialing/index.js +23 -0
- package/dist/credentialing/infrastructure/in-memory-store.d.ts +11 -0
- package/dist/credentialing/infrastructure/in-memory-store.d.ts.map +1 -0
- package/dist/credentialing/infrastructure/in-memory-store.js +14 -0
- package/dist/hsm/application/asymmetric-key-service.d.ts +23 -0
- package/dist/hsm/application/asymmetric-key-service.d.ts.map +1 -0
- package/dist/hsm/application/asymmetric-key-service.js +109 -0
- package/dist/hsm/application/envelope-encryption-service.d.ts +18 -0
- package/dist/hsm/application/envelope-encryption-service.d.ts.map +1 -0
- package/dist/hsm/application/envelope-encryption-service.js +59 -0
- package/dist/hsm/application/symmetric-key-service.d.ts +34 -0
- package/dist/hsm/application/symmetric-key-service.d.ts.map +1 -0
- package/dist/hsm/application/symmetric-key-service.js +107 -0
- package/dist/hsm/domain/entities.d.ts +104 -0
- package/dist/hsm/domain/entities.d.ts.map +1 -0
- package/dist/hsm/domain/entities.js +10 -0
- package/dist/hsm/domain/ports.d.ts +20 -0
- package/dist/hsm/domain/ports.d.ts.map +1 -0
- package/dist/hsm/domain/ports.js +1 -0
- package/dist/hsm/index.d.ts +48 -0
- package/dist/hsm/index.d.ts.map +1 -0
- package/dist/hsm/index.js +97 -0
- package/dist/hsm/infrastructure/audit-log-factory.d.ts +59 -0
- package/dist/hsm/infrastructure/audit-log-factory.d.ts.map +1 -0
- package/dist/hsm/infrastructure/audit-log-factory.js +95 -0
- package/dist/hsm/infrastructure/audit-log.d.ts +8 -0
- package/dist/hsm/infrastructure/audit-log.d.ts.map +1 -0
- package/dist/hsm/infrastructure/audit-log.js +18 -0
- package/dist/hsm/infrastructure/file-audit-log.d.ts +55 -0
- package/dist/hsm/infrastructure/file-audit-log.d.ts.map +1 -0
- package/dist/hsm/infrastructure/file-audit-log.js +128 -0
- package/dist/hsm/infrastructure/key-store.d.ts +9 -0
- package/dist/hsm/infrastructure/key-store.d.ts.map +1 -0
- package/dist/hsm/infrastructure/key-store.js +12 -0
- package/dist/hsm/infrastructure/syslog-audit-log.d.ts +64 -0
- package/dist/hsm/infrastructure/syslog-audit-log.d.ts.map +1 -0
- package/dist/hsm/infrastructure/syslog-audit-log.js +167 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +37 -0
- package/dist/integrations/besu-client/error-mapper.d.ts +9 -0
- package/dist/integrations/besu-client/error-mapper.d.ts.map +1 -0
- package/dist/integrations/besu-client/error-mapper.js +22 -0
- package/dist/integrations/besu-client/index.d.ts +65 -0
- package/dist/integrations/besu-client/index.d.ts.map +1 -0
- package/dist/integrations/besu-client/index.js +276 -0
- package/dist/integrations/besu-client/ports.d.ts +44 -0
- package/dist/integrations/besu-client/ports.d.ts.map +1 -0
- package/dist/integrations/besu-client/ports.js +1 -0
- package/dist/integrations/corda-gateway/index.d.ts +37 -0
- package/dist/integrations/corda-gateway/index.d.ts.map +1 -0
- package/dist/integrations/corda-gateway/index.js +234 -0
- package/dist/integrations/corda-gateway/ports.d.ts +33 -0
- package/dist/integrations/corda-gateway/ports.d.ts.map +1 -0
- package/dist/integrations/corda-gateway/ports.js +1 -0
- package/dist/integrations/fabric-gateway/index.d.ts +78 -0
- package/dist/integrations/fabric-gateway/index.d.ts.map +1 -0
- package/dist/integrations/fabric-gateway/index.js +214 -0
- package/dist/integrations/fabric-gateway/ports.d.ts +50 -0
- package/dist/integrations/fabric-gateway/ports.d.ts.map +1 -0
- package/dist/integrations/fabric-gateway/ports.js +1 -0
- package/dist/integrations/index.d.ts +19 -0
- package/dist/integrations/index.d.ts.map +1 -0
- package/dist/integrations/index.js +19 -0
- package/dist/integrations/shared/env.d.ts +4 -0
- package/dist/integrations/shared/env.d.ts.map +1 -0
- package/dist/integrations/shared/env.js +24 -0
- package/dist/integrations/shared/retry.d.ts +79 -0
- package/dist/integrations/shared/retry.d.ts.map +1 -0
- package/dist/integrations/shared/retry.js +315 -0
- package/dist/mpc/adapters.d.ts +36 -0
- package/dist/mpc/adapters.d.ts.map +1 -0
- package/dist/mpc/adapters.js +46 -0
- package/dist/mpc/crypto.d.ts +2 -0
- package/dist/mpc/crypto.d.ts.map +1 -0
- package/dist/mpc/crypto.js +2 -0
- package/dist/mpc/dsa.d.ts +134 -0
- package/dist/mpc/dsa.d.ts.map +1 -0
- package/dist/mpc/dsa.js +127 -0
- package/dist/mpc/field.d.ts +127 -0
- package/dist/mpc/field.d.ts.map +1 -0
- package/dist/mpc/field.js +209 -0
- package/dist/mpc/hybrid-kem.d.ts +96 -0
- package/dist/mpc/hybrid-kem.d.ts.map +1 -0
- package/dist/mpc/hybrid-kem.js +136 -0
- package/dist/mpc/index.d.ts +135 -0
- package/dist/mpc/index.d.ts.map +1 -0
- package/dist/mpc/index.js +348 -0
- package/dist/mpc/kyber.d.ts +134 -0
- package/dist/mpc/kyber.d.ts.map +1 -0
- package/dist/mpc/kyber.js +143 -0
- package/dist/mpc/ports.d.ts +67 -0
- package/dist/mpc/ports.d.ts.map +1 -0
- package/dist/mpc/ports.js +9 -0
- package/dist/mpc/quantum.d.ts +80 -0
- package/dist/mpc/quantum.d.ts.map +1 -0
- package/dist/mpc/quantum.js +180 -0
- package/dist/p2mr/adapters.d.ts +31 -0
- package/dist/p2mr/adapters.d.ts.map +1 -0
- package/dist/p2mr/adapters.js +35 -0
- package/dist/p2mr/index.d.ts +63 -0
- package/dist/p2mr/index.d.ts.map +1 -0
- package/dist/p2mr/index.js +59 -0
- package/dist/p2mr/merkle-tree.d.ts +109 -0
- package/dist/p2mr/merkle-tree.d.ts.map +1 -0
- package/dist/p2mr/merkle-tree.js +239 -0
- package/dist/p2mr/p2mr-output.d.ts +142 -0
- package/dist/p2mr/p2mr-output.d.ts.map +1 -0
- package/dist/p2mr/p2mr-output.js +150 -0
- package/dist/p2mr/ports.d.ts +52 -0
- package/dist/p2mr/ports.d.ts.map +1 -0
- package/dist/p2mr/ports.js +9 -0
- package/dist/p2mr/script-interpreter.d.ts +92 -0
- package/dist/p2mr/script-interpreter.d.ts.map +1 -0
- package/dist/p2mr/script-interpreter.js +535 -0
- package/dist/p2mr/script-leaf.d.ts +70 -0
- package/dist/p2mr/script-leaf.d.ts.map +1 -0
- package/dist/p2mr/script-leaf.js +203 -0
- package/dist/p2mr/spend-proof.d.ts +95 -0
- package/dist/p2mr/spend-proof.d.ts.map +1 -0
- package/dist/p2mr/spend-proof.js +358 -0
- package/dist/p2mr/types.d.ts +209 -0
- package/dist/p2mr/types.d.ts.map +1 -0
- package/dist/p2mr/types.js +9 -0
- package/dist/privacy/application/view-projector.d.ts +13 -0
- package/dist/privacy/application/view-projector.d.ts.map +1 -0
- package/dist/privacy/application/view-projector.js +85 -0
- package/dist/privacy/domain/entities.d.ts +26 -0
- package/dist/privacy/domain/entities.d.ts.map +1 -0
- package/dist/privacy/domain/entities.js +1 -0
- package/dist/privacy/domain/ports.d.ts +7 -0
- package/dist/privacy/domain/ports.d.ts.map +1 -0
- package/dist/privacy/domain/ports.js +1 -0
- package/dist/privacy/index.d.ts +21 -0
- package/dist/privacy/index.d.ts.map +1 -0
- package/dist/privacy/index.js +25 -0
- package/dist/privacy/infrastructure/in-memory-store.d.ts +8 -0
- package/dist/privacy/infrastructure/in-memory-store.d.ts.map +1 -0
- package/dist/privacy/infrastructure/in-memory-store.js +7 -0
- package/dist/protocols/besu-port.d.ts +80 -0
- package/dist/protocols/besu-port.d.ts.map +1 -0
- package/dist/protocols/besu-port.js +1 -0
- package/dist/protocols/corda-port.d.ts +103 -0
- package/dist/protocols/corda-port.d.ts.map +1 -0
- package/dist/protocols/corda-port.js +9 -0
- package/dist/protocols/credentialing-port.d.ts +11 -0
- package/dist/protocols/credentialing-port.d.ts.map +1 -0
- package/dist/protocols/credentialing-port.js +1 -0
- package/dist/protocols/fabric-port.d.ts +89 -0
- package/dist/protocols/fabric-port.d.ts.map +1 -0
- package/dist/protocols/fabric-port.js +9 -0
- package/dist/protocols/index.d.ts +14 -0
- package/dist/protocols/index.d.ts.map +1 -0
- package/dist/protocols/index.js +7 -0
- package/dist/protocols/p2mr-port.d.ts +159 -0
- package/dist/protocols/p2mr-port.d.ts.map +1 -0
- package/dist/protocols/p2mr-port.js +12 -0
- package/dist/protocols/privacy-port.d.ts +9 -0
- package/dist/protocols/privacy-port.d.ts.map +1 -0
- package/dist/protocols/privacy-port.js +1 -0
- package/dist/protocols/traceability-port.d.ts +12 -0
- package/dist/protocols/traceability-port.d.ts.map +1 -0
- package/dist/protocols/traceability-port.js +1 -0
- package/dist/shared/collection-store.d.ts +12 -0
- package/dist/shared/collection-store.d.ts.map +1 -0
- package/dist/shared/collection-store.js +26 -0
- package/dist/shared/commit.d.ts +24 -0
- package/dist/shared/commit.d.ts.map +1 -0
- package/dist/shared/commit.js +50 -0
- package/dist/shared/crypto.d.ts +2 -0
- package/dist/shared/crypto.d.ts.map +1 -0
- package/dist/shared/crypto.js +4 -0
- package/dist/shared/date.d.ts +2 -0
- package/dist/shared/date.d.ts.map +1 -0
- package/dist/shared/date.js +3 -0
- package/dist/shared/index.d.ts +9 -0
- package/dist/shared/index.d.ts.map +1 -0
- package/dist/shared/index.js +11 -0
- package/dist/shared/logger.d.ts +37 -0
- package/dist/shared/logger.d.ts.map +1 -0
- package/dist/shared/logger.js +45 -0
- package/dist/shared/store.d.ts +25 -0
- package/dist/shared/store.d.ts.map +1 -0
- package/dist/shared/store.js +18 -0
- package/dist/shared/telemetry-sdk.d.ts +26 -0
- package/dist/shared/telemetry-sdk.d.ts.map +1 -0
- package/dist/shared/telemetry-sdk.js +97 -0
- package/dist/shared/telemetry.d.ts +86 -0
- package/dist/shared/telemetry.d.ts.map +1 -0
- package/dist/shared/telemetry.js +137 -0
- package/dist/stark-settlement/application/aggregator-service.d.ts +112 -0
- package/dist/stark-settlement/application/aggregator-service.d.ts.map +1 -0
- package/dist/stark-settlement/application/aggregator-service.js +256 -0
- package/dist/stark-settlement/application/ledger-service.d.ts +114 -0
- package/dist/stark-settlement/application/ledger-service.d.ts.map +1 -0
- package/dist/stark-settlement/application/ledger-service.js +318 -0
- package/dist/stark-settlement/application/settlement-service.d.ts +104 -0
- package/dist/stark-settlement/application/settlement-service.d.ts.map +1 -0
- package/dist/stark-settlement/application/settlement-service.js +251 -0
- package/dist/stark-settlement/domain/entities.d.ts +365 -0
- package/dist/stark-settlement/domain/entities.d.ts.map +1 -0
- package/dist/stark-settlement/domain/entities.js +29 -0
- package/dist/stark-settlement/domain/ports.d.ts +485 -0
- package/dist/stark-settlement/domain/ports.d.ts.map +1 -0
- package/dist/stark-settlement/domain/ports.js +14 -0
- package/dist/stark-settlement/domain/value-objects.d.ts +268 -0
- package/dist/stark-settlement/domain/value-objects.d.ts.map +1 -0
- package/dist/stark-settlement/domain/value-objects.js +500 -0
- package/dist/stark-settlement/index.d.ts +172 -0
- package/dist/stark-settlement/index.d.ts.map +1 -0
- package/dist/stark-settlement/index.js +193 -0
- package/dist/stark-settlement/infrastructure/adapters/audit-adapter.d.ts +52 -0
- package/dist/stark-settlement/infrastructure/adapters/audit-adapter.d.ts.map +1 -0
- package/dist/stark-settlement/infrastructure/adapters/audit-adapter.js +154 -0
- package/dist/stark-settlement/infrastructure/adapters/bitcoin-adapter.d.ts +88 -0
- package/dist/stark-settlement/infrastructure/adapters/bitcoin-adapter.d.ts.map +1 -0
- package/dist/stark-settlement/infrastructure/adapters/bitcoin-adapter.js +187 -0
- package/dist/stark-settlement/infrastructure/adapters/clock-adapter.d.ts +59 -0
- package/dist/stark-settlement/infrastructure/adapters/clock-adapter.d.ts.map +1 -0
- package/dist/stark-settlement/infrastructure/adapters/clock-adapter.js +85 -0
- package/dist/stark-settlement/infrastructure/adapters/dilithium-adapter.d.ts +60 -0
- package/dist/stark-settlement/infrastructure/adapters/dilithium-adapter.d.ts.map +1 -0
- package/dist/stark-settlement/infrastructure/adapters/dilithium-adapter.js +104 -0
- package/dist/stark-settlement/infrastructure/adapters/event-emitter-adapter.d.ts +115 -0
- package/dist/stark-settlement/infrastructure/adapters/event-emitter-adapter.d.ts.map +1 -0
- package/dist/stark-settlement/infrastructure/adapters/event-emitter-adapter.js +191 -0
- package/dist/stark-settlement/infrastructure/adapters/fiat-adapter.d.ts +65 -0
- package/dist/stark-settlement/infrastructure/adapters/fiat-adapter.d.ts.map +1 -0
- package/dist/stark-settlement/infrastructure/adapters/fiat-adapter.js +207 -0
- package/dist/stark-settlement/infrastructure/adapters/mock-stark-adapter.d.ts +73 -0
- package/dist/stark-settlement/infrastructure/adapters/mock-stark-adapter.d.ts.map +1 -0
- package/dist/stark-settlement/infrastructure/adapters/mock-stark-adapter.js +287 -0
- package/dist/stark-settlement/infrastructure/adapters/solana-adapter.d.ts +78 -0
- package/dist/stark-settlement/infrastructure/adapters/solana-adapter.d.ts.map +1 -0
- package/dist/stark-settlement/infrastructure/adapters/solana-adapter.js +172 -0
- package/dist/stark-settlement/infrastructure/adapters/starknet-proof-adapter.d.ts +56 -0
- package/dist/stark-settlement/infrastructure/adapters/starknet-proof-adapter.d.ts.map +1 -0
- package/dist/stark-settlement/infrastructure/adapters/starknet-proof-adapter.js +261 -0
- package/dist/stark-settlement/infrastructure/adapters/stone-proof-adapter.d.ts +125 -0
- package/dist/stark-settlement/infrastructure/adapters/stone-proof-adapter.d.ts.map +1 -0
- package/dist/stark-settlement/infrastructure/adapters/stone-proof-adapter.js +416 -0
- package/dist/stark-settlement/infrastructure/persistence/ledger-store.d.ts +68 -0
- package/dist/stark-settlement/infrastructure/persistence/ledger-store.d.ts.map +1 -0
- package/dist/stark-settlement/infrastructure/persistence/ledger-store.js +238 -0
- package/dist/stark-settlement/infrastructure/persistence/offset-store.d.ts +30 -0
- package/dist/stark-settlement/infrastructure/persistence/offset-store.d.ts.map +1 -0
- package/dist/stark-settlement/infrastructure/persistence/offset-store.js +57 -0
- package/dist/stark-settlement/infrastructure/persistence/outbox-store.d.ts +45 -0
- package/dist/stark-settlement/infrastructure/persistence/outbox-store.d.ts.map +1 -0
- package/dist/stark-settlement/infrastructure/persistence/outbox-store.js +171 -0
- package/dist/traceability/application/recall-assessor.d.ts +13 -0
- package/dist/traceability/application/recall-assessor.d.ts.map +1 -0
- package/dist/traceability/application/recall-assessor.js +74 -0
- package/dist/traceability/domain/entities.d.ts +23 -0
- package/dist/traceability/domain/entities.d.ts.map +1 -0
- package/dist/traceability/domain/entities.js +1 -0
- package/dist/traceability/domain/ports.d.ts +23 -0
- package/dist/traceability/domain/ports.d.ts.map +1 -0
- package/dist/traceability/domain/ports.js +1 -0
- package/dist/traceability/domain/recall.d.ts +12 -0
- package/dist/traceability/domain/recall.d.ts.map +1 -0
- package/dist/traceability/domain/recall.js +1 -0
- package/dist/traceability/index.d.ts +22 -0
- package/dist/traceability/index.d.ts.map +1 -0
- package/dist/traceability/index.js +26 -0
- package/dist/traceability/infrastructure/in-memory-store.d.ts +13 -0
- package/dist/traceability/infrastructure/in-memory-store.d.ts.map +1 -0
- package/dist/traceability/infrastructure/in-memory-store.js +24 -0
- package/package.json +12 -9
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ledger Service
|
|
3
|
+
*
|
|
4
|
+
* Orchestrates the ZKP ledger operations:
|
|
5
|
+
* - Account management (create, query, update)
|
|
6
|
+
* - Transaction submission with ML-DSA-65 signing
|
|
7
|
+
* - State transition proof generation
|
|
8
|
+
* - Audit logging
|
|
9
|
+
*
|
|
10
|
+
* All transactions are signed with post-quantum ML-DSA-65 signatures.
|
|
11
|
+
*
|
|
12
|
+
* @see domain/ports.ts for port interfaces
|
|
13
|
+
*/
|
|
14
|
+
import { createHash } from "node:crypto";
|
|
15
|
+
import { StateRoot } from "../domain/value-objects.js";
|
|
16
|
+
/**
|
|
17
|
+
* Ledger service for managing accounts and transactions.
|
|
18
|
+
*/
|
|
19
|
+
export class LedgerService {
|
|
20
|
+
ctx;
|
|
21
|
+
currentStateRoot;
|
|
22
|
+
constructor(ctx) {
|
|
23
|
+
this.ctx = ctx;
|
|
24
|
+
this.currentStateRoot = StateRoot.genesis();
|
|
25
|
+
}
|
|
26
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
27
|
+
// Account Management
|
|
28
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
29
|
+
/**
|
|
30
|
+
* Create a new mirror account.
|
|
31
|
+
*/
|
|
32
|
+
async createAccount(options) {
|
|
33
|
+
const now = this.ctx.clock.now();
|
|
34
|
+
const id = this.ctx.clock.uuid();
|
|
35
|
+
const account = {
|
|
36
|
+
id,
|
|
37
|
+
externalAddress: options.externalAddress,
|
|
38
|
+
assetType: options.assetType,
|
|
39
|
+
balance: options.initialBalance ?? 0n,
|
|
40
|
+
lastProofRoot: this.currentStateRoot.toString().replace("0x", ""),
|
|
41
|
+
createdAt: now,
|
|
42
|
+
updatedAt: now,
|
|
43
|
+
isActive: true,
|
|
44
|
+
metadata: options.metadata ?? {},
|
|
45
|
+
};
|
|
46
|
+
await this.ctx.ledgerStore.createAccount(account);
|
|
47
|
+
// Update state root to reflect new account
|
|
48
|
+
this.currentStateRoot = this.currentStateRoot.hash(new TextEncoder().encode(serializeWithBigInt(account)));
|
|
49
|
+
// Emit event
|
|
50
|
+
this.ctx.events.emit({
|
|
51
|
+
type: "transaction:submitted",
|
|
52
|
+
tx: {
|
|
53
|
+
txId: `account-creation-${id}`,
|
|
54
|
+
type: "deposit",
|
|
55
|
+
fromAccountId: null,
|
|
56
|
+
toAccountId: id,
|
|
57
|
+
assetType: options.assetType,
|
|
58
|
+
amount: options.initialBalance ?? 0n,
|
|
59
|
+
idempotencyKey: `account-creation-${id}`,
|
|
60
|
+
mlDsaSignature: new Uint8Array(0),
|
|
61
|
+
mlDsaPublicKeyHash: "",
|
|
62
|
+
status: "finalized",
|
|
63
|
+
metadata: { accountCreation: "true" },
|
|
64
|
+
createdAt: now,
|
|
65
|
+
updatedAt: now,
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
return account;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Get an account by ID.
|
|
72
|
+
*/
|
|
73
|
+
async getAccount(accountId) {
|
|
74
|
+
return this.ctx.ledgerStore.getAccount(accountId);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Get an account by external address and asset type.
|
|
78
|
+
*/
|
|
79
|
+
async getAccountByAddress(externalAddress, assetType) {
|
|
80
|
+
return this.ctx.ledgerStore.getAccountByAddress(externalAddress, assetType);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Get all accounts for an asset type.
|
|
84
|
+
*/
|
|
85
|
+
async getAccountsByAssetType(assetType) {
|
|
86
|
+
return this.ctx.ledgerStore.getAccountsByAssetType(assetType);
|
|
87
|
+
}
|
|
88
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
89
|
+
// Transaction Submission
|
|
90
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
91
|
+
/**
|
|
92
|
+
* Submit a transaction to the ledger.
|
|
93
|
+
*
|
|
94
|
+
* The transaction is:
|
|
95
|
+
* 1. Validated (balances, account existence)
|
|
96
|
+
* 2. Signed with ML-DSA-65
|
|
97
|
+
* 3. Recorded in the ledger
|
|
98
|
+
* 4. A base STARK proof is generated
|
|
99
|
+
* 5. Account balances are updated
|
|
100
|
+
*
|
|
101
|
+
* @returns The transaction and its base proof
|
|
102
|
+
* @throws If validation fails
|
|
103
|
+
*/
|
|
104
|
+
async submitTransaction(options) {
|
|
105
|
+
const now = this.ctx.clock.now();
|
|
106
|
+
const txId = this.ctx.clock.uuid();
|
|
107
|
+
// Generate idempotency key if not provided
|
|
108
|
+
const idempotencyKey = options.idempotencyKey ??
|
|
109
|
+
createHash("sha256").update(`${txId}:${now}`).digest("hex");
|
|
110
|
+
// Check for duplicate (idempotent replay)
|
|
111
|
+
const existingTxs = await this.ctx.ledgerStore.getTransactionsByIdempotencyKey(idempotencyKey);
|
|
112
|
+
if (existingTxs.length > 0) {
|
|
113
|
+
const existingTx = existingTxs[0];
|
|
114
|
+
// Look up proof using the proofId stored on the transaction (if present)
|
|
115
|
+
// or fall back to the deterministic proof ID pattern for backward compatibility
|
|
116
|
+
const txWithProofId = existingTx;
|
|
117
|
+
const proofId = txWithProofId.proofId ?? `proof-${existingTx.txId}`;
|
|
118
|
+
const existingProof = await this.ctx.ledgerStore.getBaseProof(proofId);
|
|
119
|
+
if (existingProof) {
|
|
120
|
+
return {
|
|
121
|
+
transaction: existingTx,
|
|
122
|
+
baseProof: existingProof,
|
|
123
|
+
isDuplicate: true,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
// Transaction exists but proof not found - refuse to create duplicate
|
|
127
|
+
throw new Error(`Idempotency violation: transaction with key "${idempotencyKey}" already exists (txId: ${existingTx.txId}), but proof could not be found. Refusing to create duplicate.`);
|
|
128
|
+
}
|
|
129
|
+
// Validate transaction
|
|
130
|
+
await this.validateTransaction(options);
|
|
131
|
+
// Create transaction payload for signing
|
|
132
|
+
const payload = {
|
|
133
|
+
txId,
|
|
134
|
+
type: options.type,
|
|
135
|
+
fromAccountId: options.fromAccountId ?? null,
|
|
136
|
+
toAccountId: options.toAccountId ?? null,
|
|
137
|
+
assetType: options.assetType,
|
|
138
|
+
amount: options.amount.toString(),
|
|
139
|
+
idempotencyKey,
|
|
140
|
+
createdAt: now,
|
|
141
|
+
};
|
|
142
|
+
// Sign with ML-DSA-65
|
|
143
|
+
const signature = this.ctx.transactionSigning.signPayload(payload, options.signerSecretKey);
|
|
144
|
+
const publicKeyHash = this.ctx.dilithium.hashPublicKey(options.signerPublicKey);
|
|
145
|
+
// Create transaction record
|
|
146
|
+
const transaction = {
|
|
147
|
+
txId,
|
|
148
|
+
type: options.type,
|
|
149
|
+
fromAccountId: options.fromAccountId ?? null,
|
|
150
|
+
toAccountId: options.toAccountId ?? null,
|
|
151
|
+
assetType: options.assetType,
|
|
152
|
+
amount: options.amount,
|
|
153
|
+
idempotencyKey,
|
|
154
|
+
mlDsaSignature: signature,
|
|
155
|
+
mlDsaPublicKeyHash: publicKeyHash,
|
|
156
|
+
status: "pending",
|
|
157
|
+
metadata: options.metadata ?? {},
|
|
158
|
+
createdAt: now,
|
|
159
|
+
updatedAt: now,
|
|
160
|
+
};
|
|
161
|
+
// Save transaction
|
|
162
|
+
await this.ctx.ledgerStore.appendTransaction(transaction);
|
|
163
|
+
// Compute state transition
|
|
164
|
+
const preStateRoot = this.currentStateRoot.toString().replace("0x", "");
|
|
165
|
+
await this.applyTransaction(transaction);
|
|
166
|
+
const postStateRoot = this.currentStateRoot.toString().replace("0x", "");
|
|
167
|
+
// Generate base STARK proof
|
|
168
|
+
const baseProof = await this.ctx.starkProver.generateBaseProof(transaction, preStateRoot, postStateRoot);
|
|
169
|
+
// Save base proof
|
|
170
|
+
await this.ctx.ledgerStore.saveBaseProof(baseProof);
|
|
171
|
+
// Update transaction status
|
|
172
|
+
await this.ctx.ledgerStore.updateTransactionStatus(txId, "proved");
|
|
173
|
+
// Create updated transaction with correct status for return/events
|
|
174
|
+
const provedTransaction = {
|
|
175
|
+
...transaction,
|
|
176
|
+
status: "proved",
|
|
177
|
+
updatedAt: this.ctx.clock.now(),
|
|
178
|
+
};
|
|
179
|
+
// Log audit record
|
|
180
|
+
await this.ctx.auditLog.append({
|
|
181
|
+
eventType: "transaction_submitted",
|
|
182
|
+
entityId: txId,
|
|
183
|
+
entityType: "transaction",
|
|
184
|
+
actor: publicKeyHash,
|
|
185
|
+
timestamp: now,
|
|
186
|
+
data: {
|
|
187
|
+
type: options.type,
|
|
188
|
+
assetType: options.assetType,
|
|
189
|
+
amount: options.amount.toString(),
|
|
190
|
+
preStateRoot,
|
|
191
|
+
postStateRoot,
|
|
192
|
+
},
|
|
193
|
+
});
|
|
194
|
+
// Emit event with updated transaction
|
|
195
|
+
this.ctx.events.emit({
|
|
196
|
+
type: "transaction:submitted",
|
|
197
|
+
tx: provedTransaction,
|
|
198
|
+
});
|
|
199
|
+
this.ctx.events.emit({ type: "proof:base:generated", proof: baseProof });
|
|
200
|
+
return {
|
|
201
|
+
transaction: provedTransaction,
|
|
202
|
+
baseProof,
|
|
203
|
+
isDuplicate: false,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Get pending transactions that need proof generation.
|
|
208
|
+
*/
|
|
209
|
+
async getPendingTransactions(limit = 1000) {
|
|
210
|
+
return this.ctx.ledgerStore.getPendingTransactions(limit);
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Get the current state root.
|
|
214
|
+
*/
|
|
215
|
+
getCurrentStateRoot() {
|
|
216
|
+
return this.currentStateRoot;
|
|
217
|
+
}
|
|
218
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
219
|
+
// Private Helpers
|
|
220
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
221
|
+
async validateTransaction(options) {
|
|
222
|
+
// Validate based on transaction type
|
|
223
|
+
switch (options.type) {
|
|
224
|
+
case "deposit":
|
|
225
|
+
if (!options.toAccountId) {
|
|
226
|
+
throw new Error("Deposit requires toAccountId");
|
|
227
|
+
}
|
|
228
|
+
await this.validateAccountExists(options.toAccountId, options.assetType);
|
|
229
|
+
break;
|
|
230
|
+
case "withdrawal":
|
|
231
|
+
if (!options.fromAccountId) {
|
|
232
|
+
throw new Error("Withdrawal requires fromAccountId");
|
|
233
|
+
}
|
|
234
|
+
await this.validateAccountExists(options.fromAccountId, options.assetType);
|
|
235
|
+
await this.validateSufficientBalance(options.fromAccountId, options.amount);
|
|
236
|
+
break;
|
|
237
|
+
case "transfer":
|
|
238
|
+
if (!options.fromAccountId || !options.toAccountId) {
|
|
239
|
+
throw new Error("Transfer requires both fromAccountId and toAccountId");
|
|
240
|
+
}
|
|
241
|
+
await this.validateAccountExists(options.fromAccountId, options.assetType);
|
|
242
|
+
await this.validateAccountExists(options.toAccountId, options.assetType);
|
|
243
|
+
await this.validateSufficientBalance(options.fromAccountId, options.amount);
|
|
244
|
+
break;
|
|
245
|
+
}
|
|
246
|
+
// Validate amount is positive
|
|
247
|
+
if (options.amount <= 0n) {
|
|
248
|
+
throw new Error("Amount must be positive");
|
|
249
|
+
}
|
|
250
|
+
// Verify signature is valid
|
|
251
|
+
const payload = {
|
|
252
|
+
txId: "validation-check",
|
|
253
|
+
type: options.type,
|
|
254
|
+
fromAccountId: options.fromAccountId ?? null,
|
|
255
|
+
toAccountId: options.toAccountId ?? null,
|
|
256
|
+
assetType: options.assetType,
|
|
257
|
+
amount: options.amount.toString(),
|
|
258
|
+
idempotencyKey: "validation-check",
|
|
259
|
+
createdAt: this.ctx.clock.now(),
|
|
260
|
+
};
|
|
261
|
+
const testSignature = this.ctx.transactionSigning.signPayload(payload, options.signerSecretKey);
|
|
262
|
+
const isValid = this.ctx.transactionSigning.verifyPayload(payload, testSignature, options.signerPublicKey);
|
|
263
|
+
if (!isValid) {
|
|
264
|
+
throw new Error("Invalid signature");
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
async validateAccountExists(accountId, expectedAssetType) {
|
|
268
|
+
const account = await this.ctx.ledgerStore.getAccount(accountId);
|
|
269
|
+
if (!account) {
|
|
270
|
+
throw new Error(`Account ${accountId} not found`);
|
|
271
|
+
}
|
|
272
|
+
if (account.assetType !== expectedAssetType) {
|
|
273
|
+
throw new Error(`Account ${accountId} has asset type ${account.assetType}, expected ${expectedAssetType}`);
|
|
274
|
+
}
|
|
275
|
+
if (!account.isActive) {
|
|
276
|
+
throw new Error(`Account ${accountId} is not active`);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
async validateSufficientBalance(accountId, amount) {
|
|
280
|
+
const account = await this.ctx.ledgerStore.getAccount(accountId);
|
|
281
|
+
if (!account) {
|
|
282
|
+
throw new Error(`Account ${accountId} not found`);
|
|
283
|
+
}
|
|
284
|
+
if (account.balance < amount) {
|
|
285
|
+
throw new Error(`Insufficient balance: ${account.balance} < ${amount}`);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
async applyTransaction(tx) {
|
|
289
|
+
// First, advance the state root to reflect the new transaction
|
|
290
|
+
this.currentStateRoot = this.currentStateRoot.hash(new TextEncoder().encode(serializeWithBigInt(tx)));
|
|
291
|
+
// Use the post-transaction state root for account updates
|
|
292
|
+
const postTxStateRoot = this.currentStateRoot.toString().replace("0x", "");
|
|
293
|
+
switch (tx.type) {
|
|
294
|
+
case "deposit":
|
|
295
|
+
if (tx.toAccountId) {
|
|
296
|
+
await this.ctx.ledgerStore.updateAccountBalance(tx.toAccountId, tx.amount, postTxStateRoot);
|
|
297
|
+
}
|
|
298
|
+
break;
|
|
299
|
+
case "withdrawal":
|
|
300
|
+
if (tx.fromAccountId) {
|
|
301
|
+
await this.ctx.ledgerStore.updateAccountBalance(tx.fromAccountId, -tx.amount, postTxStateRoot);
|
|
302
|
+
}
|
|
303
|
+
break;
|
|
304
|
+
case "transfer":
|
|
305
|
+
if (tx.fromAccountId && tx.toAccountId) {
|
|
306
|
+
await this.ctx.ledgerStore.updateAccountBalance(tx.fromAccountId, -tx.amount, postTxStateRoot);
|
|
307
|
+
await this.ctx.ledgerStore.updateAccountBalance(tx.toAccountId, tx.amount, postTxStateRoot);
|
|
308
|
+
}
|
|
309
|
+
break;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Serialize an object to JSON, converting BigInt values to strings.
|
|
315
|
+
*/
|
|
316
|
+
function serializeWithBigInt(obj) {
|
|
317
|
+
return JSON.stringify(obj, (_key, value) => typeof value === "bigint" ? value.toString() : value);
|
|
318
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Settlement Service
|
|
3
|
+
*
|
|
4
|
+
* Orchestrates outbound settlement to external chains:
|
|
5
|
+
* - Solana (devnet): VersionedTransaction with lookup tables
|
|
6
|
+
* - Bitcoin (testnet): PSBT batched UTXO spends
|
|
7
|
+
* - Fiat (mock): ISO 20022 pain.001 credit transfers
|
|
8
|
+
*
|
|
9
|
+
* Uses idempotency keys and outbox entry state transitions to coordinate
|
|
10
|
+
* settlement attempts and reduce duplicate processing.
|
|
11
|
+
*
|
|
12
|
+
* @see domain/ports.ts for settlement port interfaces
|
|
13
|
+
*/
|
|
14
|
+
import type { AssetType, Tier2BlockProof, OutboxEntry, SolanaSettlementResult, BitcoinSettlementResult, FiatSettlementResult } from "../domain/entities.js";
|
|
15
|
+
import type { SolanaSettlementPort, BitcoinSettlementPort, FiatSettlementPort } from "../domain/ports.js";
|
|
16
|
+
import type { SettlementContext } from "../index.js";
|
|
17
|
+
/**
|
|
18
|
+
* Result of settling a single rail.
|
|
19
|
+
*/
|
|
20
|
+
export type SettlementRailResult = {
|
|
21
|
+
success: true;
|
|
22
|
+
assetType: AssetType;
|
|
23
|
+
result: SolanaSettlementResult | BitcoinSettlementResult | FiatSettlementResult;
|
|
24
|
+
} | {
|
|
25
|
+
success: false;
|
|
26
|
+
assetType: AssetType;
|
|
27
|
+
error: string;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Result of settling all rails.
|
|
31
|
+
*/
|
|
32
|
+
export interface SettleAllRailsResult {
|
|
33
|
+
blockProofId: string;
|
|
34
|
+
results: SettlementRailResult[];
|
|
35
|
+
allSucceeded: boolean;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Settlement service configuration.
|
|
39
|
+
*/
|
|
40
|
+
export interface SettlementServiceConfig {
|
|
41
|
+
/** Delay between retries in ms (default: 1000) */
|
|
42
|
+
retryDelayMs?: number;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Settlement service for external chain settlement.
|
|
46
|
+
*/
|
|
47
|
+
export declare class SettlementService {
|
|
48
|
+
private readonly ctx;
|
|
49
|
+
private readonly config;
|
|
50
|
+
private solanaAdapter;
|
|
51
|
+
private bitcoinAdapter;
|
|
52
|
+
private fiatAdapter;
|
|
53
|
+
constructor(ctx: SettlementContext, config?: SettlementServiceConfig);
|
|
54
|
+
/**
|
|
55
|
+
* Set the Solana settlement adapter.
|
|
56
|
+
*/
|
|
57
|
+
setSolanaAdapter(adapter: SolanaSettlementPort): void;
|
|
58
|
+
/**
|
|
59
|
+
* Set the Bitcoin settlement adapter.
|
|
60
|
+
*/
|
|
61
|
+
setBitcoinAdapter(adapter: BitcoinSettlementPort): void;
|
|
62
|
+
/**
|
|
63
|
+
* Set the Fiat settlement adapter.
|
|
64
|
+
*/
|
|
65
|
+
setFiatAdapter(adapter: FiatSettlementPort): void;
|
|
66
|
+
/**
|
|
67
|
+
* Settle all pending outbox entries for a block proof.
|
|
68
|
+
*/
|
|
69
|
+
settleAllRails(blockProof: Tier2BlockProof): Promise<SettleAllRailsResult>;
|
|
70
|
+
/**
|
|
71
|
+
* Settle a single outbox entry.
|
|
72
|
+
*/
|
|
73
|
+
settleEntry(entry: OutboxEntry, blockProof: Tier2BlockProof): Promise<SettlementRailResult>;
|
|
74
|
+
/**
|
|
75
|
+
* Process all retryable entries.
|
|
76
|
+
*/
|
|
77
|
+
processRetries(): Promise<SettlementRailResult[]>;
|
|
78
|
+
/**
|
|
79
|
+
* Get health status of all settlement rails.
|
|
80
|
+
*/
|
|
81
|
+
getHealth(): Promise<{
|
|
82
|
+
solana: {
|
|
83
|
+
available: boolean;
|
|
84
|
+
healthy: boolean;
|
|
85
|
+
details?: unknown;
|
|
86
|
+
};
|
|
87
|
+
bitcoin: {
|
|
88
|
+
available: boolean;
|
|
89
|
+
healthy: boolean;
|
|
90
|
+
details?: unknown;
|
|
91
|
+
};
|
|
92
|
+
fiat: {
|
|
93
|
+
available: boolean;
|
|
94
|
+
healthy: boolean;
|
|
95
|
+
details?: unknown;
|
|
96
|
+
};
|
|
97
|
+
}>;
|
|
98
|
+
private executeSettlement;
|
|
99
|
+
private settleSolana;
|
|
100
|
+
private settleBitcoin;
|
|
101
|
+
private settleFiat;
|
|
102
|
+
private getSettlementTxId;
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=settlement-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"settlement-service.d.ts","sourceRoot":"","sources":["../../../src/stark-settlement/application/settlement-service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EACV,SAAS,EACT,eAAe,EACf,WAAW,EAEX,sBAAsB,EACtB,uBAAuB,EACvB,oBAAoB,EACrB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EACV,oBAAoB,EACpB,qBAAqB,EACrB,kBAAkB,EACnB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAErD;;GAEG;AACH,MAAM,MAAM,oBAAoB,GAC5B;IACE,OAAO,EAAE,IAAI,CAAC;IACd,SAAS,EAAE,SAAS,CAAC;IACrB,MAAM,EACF,sBAAsB,GACtB,uBAAuB,GACvB,oBAAoB,CAAC;CAC1B,GACD;IAAE,OAAO,EAAE,KAAK,CAAC;IAAC,SAAS,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAE5D;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,oBAAoB,EAAE,CAAC;IAChC,YAAY,EAAE,OAAO,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC,kDAAkD;IAClD,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;GAEG;AACH,qBAAa,iBAAiB;IAO1B,OAAO,CAAC,QAAQ,CAAC,GAAG;IANtB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAoC;IAC3D,OAAO,CAAC,aAAa,CAAqC;IAC1D,OAAO,CAAC,cAAc,CAAsC;IAC5D,OAAO,CAAC,WAAW,CAAmC;gBAGnC,GAAG,EAAE,iBAAiB,EACvC,MAAM,CAAC,EAAE,uBAAuB;IAOlC;;OAEG;IACH,gBAAgB,CAAC,OAAO,EAAE,oBAAoB,GAAG,IAAI;IAIrD;;OAEG;IACH,iBAAiB,CAAC,OAAO,EAAE,qBAAqB,GAAG,IAAI;IAIvD;;OAEG;IACH,cAAc,CAAC,OAAO,EAAE,kBAAkB,GAAG,IAAI;IAQjD;;OAEG;IACG,cAAc,CAClB,UAAU,EAAE,eAAe,GAC1B,OAAO,CAAC,oBAAoB,CAAC;IA0BhC;;OAEG;IACG,WAAW,CACf,KAAK,EAAE,WAAW,EAClB,UAAU,EAAE,eAAe,GAC1B,OAAO,CAAC,oBAAoB,CAAC;IAoFhC;;OAEG;IACG,cAAc,IAAI,OAAO,CAAC,oBAAoB,EAAE,CAAC;IAkCvD;;OAEG;IACG,SAAS,IAAI,OAAO,CAAC;QACzB,MAAM,EAAE;YAAE,SAAS,EAAE,OAAO,CAAC;YAAC,OAAO,EAAE,OAAO,CAAC;YAAC,OAAO,CAAC,EAAE,OAAO,CAAA;SAAE,CAAC;QACpE,OAAO,EAAE;YAAE,SAAS,EAAE,OAAO,CAAC;YAAC,OAAO,EAAE,OAAO,CAAC;YAAC,OAAO,CAAC,EAAE,OAAO,CAAA;SAAE,CAAC;QACrE,IAAI,EAAE;YAAE,SAAS,EAAE,OAAO,CAAC;YAAC,OAAO,EAAE,OAAO,CAAC;YAAC,OAAO,CAAC,EAAE,OAAO,CAAA;SAAE,CAAC;KACnE,CAAC;YA0DY,iBAAiB;YAgBjB,YAAY;YAUZ,aAAa;YAUb,UAAU;IAUxB,OAAO,CAAC,iBAAiB;CAc1B"}
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Settlement Service
|
|
3
|
+
*
|
|
4
|
+
* Orchestrates outbound settlement to external chains:
|
|
5
|
+
* - Solana (devnet): VersionedTransaction with lookup tables
|
|
6
|
+
* - Bitcoin (testnet): PSBT batched UTXO spends
|
|
7
|
+
* - Fiat (mock): ISO 20022 pain.001 credit transfers
|
|
8
|
+
*
|
|
9
|
+
* Uses idempotency keys and outbox entry state transitions to coordinate
|
|
10
|
+
* settlement attempts and reduce duplicate processing.
|
|
11
|
+
*
|
|
12
|
+
* @see domain/ports.ts for settlement port interfaces
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* Settlement service for external chain settlement.
|
|
16
|
+
*/
|
|
17
|
+
export class SettlementService {
|
|
18
|
+
ctx;
|
|
19
|
+
config;
|
|
20
|
+
solanaAdapter = null;
|
|
21
|
+
bitcoinAdapter = null;
|
|
22
|
+
fiatAdapter = null;
|
|
23
|
+
constructor(ctx, config) {
|
|
24
|
+
this.ctx = ctx;
|
|
25
|
+
this.config = {
|
|
26
|
+
retryDelayMs: config?.retryDelayMs ?? 1000,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Set the Solana settlement adapter.
|
|
31
|
+
*/
|
|
32
|
+
setSolanaAdapter(adapter) {
|
|
33
|
+
this.solanaAdapter = adapter;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Set the Bitcoin settlement adapter.
|
|
37
|
+
*/
|
|
38
|
+
setBitcoinAdapter(adapter) {
|
|
39
|
+
this.bitcoinAdapter = adapter;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Set the Fiat settlement adapter.
|
|
43
|
+
*/
|
|
44
|
+
setFiatAdapter(adapter) {
|
|
45
|
+
this.fiatAdapter = adapter;
|
|
46
|
+
}
|
|
47
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
48
|
+
// Settlement Operations
|
|
49
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
50
|
+
/**
|
|
51
|
+
* Settle all pending outbox entries for a block proof.
|
|
52
|
+
*/
|
|
53
|
+
async settleAllRails(blockProof) {
|
|
54
|
+
const results = [];
|
|
55
|
+
// Get all pending entries for this block
|
|
56
|
+
for (const assetType of ["SOL", "BTC", "USD"]) {
|
|
57
|
+
const entries = await this.ctx.outboxStore.getPendingEntries(assetType, 100);
|
|
58
|
+
const blockEntries = entries.filter((e) => e.blockProofId === blockProof.blockProofId);
|
|
59
|
+
for (const entry of blockEntries) {
|
|
60
|
+
const result = await this.settleEntry(entry, blockProof);
|
|
61
|
+
results.push(result);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
blockProofId: blockProof.blockProofId,
|
|
66
|
+
results,
|
|
67
|
+
allSucceeded: results.every((r) => r.success),
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Settle a single outbox entry.
|
|
72
|
+
*/
|
|
73
|
+
async settleEntry(entry, blockProof) {
|
|
74
|
+
// Mark as processing
|
|
75
|
+
await this.ctx.outboxStore.markProcessing(entry.entryId);
|
|
76
|
+
try {
|
|
77
|
+
const result = await this.executeSettlement(entry, blockProof);
|
|
78
|
+
// Mark as settled
|
|
79
|
+
await this.ctx.outboxStore.markSettled(entry.entryId, this.getSettlementTxId(result));
|
|
80
|
+
// Log audit record
|
|
81
|
+
await this.ctx.auditLog.append({
|
|
82
|
+
eventType: "settlement_confirmed",
|
|
83
|
+
entityId: entry.entryId,
|
|
84
|
+
entityType: "outbox_entry",
|
|
85
|
+
actor: "settlement-service",
|
|
86
|
+
timestamp: this.ctx.clock.now(),
|
|
87
|
+
data: {
|
|
88
|
+
assetType: entry.assetType,
|
|
89
|
+
blockProofId: blockProof.blockProofId,
|
|
90
|
+
settlementTxId: this.getSettlementTxId(result),
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
// Emit event with updated entry status
|
|
94
|
+
const settledEntry = {
|
|
95
|
+
...entry,
|
|
96
|
+
status: "settled",
|
|
97
|
+
settledAt: this.ctx.clock.now(),
|
|
98
|
+
settlementTxId: this.getSettlementTxId(result),
|
|
99
|
+
};
|
|
100
|
+
this.ctx.events.emit({
|
|
101
|
+
type: "settlement:completed",
|
|
102
|
+
entry: settledEntry,
|
|
103
|
+
result,
|
|
104
|
+
});
|
|
105
|
+
return { success: true, assetType: entry.assetType, result };
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
109
|
+
// Mark as failed
|
|
110
|
+
await this.ctx.outboxStore.markFailed(entry.entryId, errorMessage);
|
|
111
|
+
// Log audit record
|
|
112
|
+
await this.ctx.auditLog.append({
|
|
113
|
+
eventType: "settlement_failed",
|
|
114
|
+
entityId: entry.entryId,
|
|
115
|
+
entityType: "outbox_entry",
|
|
116
|
+
actor: "settlement-service",
|
|
117
|
+
timestamp: this.ctx.clock.now(),
|
|
118
|
+
data: {
|
|
119
|
+
assetType: entry.assetType,
|
|
120
|
+
blockProofId: blockProof.blockProofId,
|
|
121
|
+
error: errorMessage,
|
|
122
|
+
retryCount: entry.retryCount + 1,
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
// Emit event with updated entry status
|
|
126
|
+
const failedEntry = {
|
|
127
|
+
...entry,
|
|
128
|
+
status: "failed",
|
|
129
|
+
retryCount: entry.retryCount + 1,
|
|
130
|
+
errorMessage,
|
|
131
|
+
};
|
|
132
|
+
this.ctx.events.emit({
|
|
133
|
+
type: "settlement:failed",
|
|
134
|
+
entry: failedEntry,
|
|
135
|
+
error: errorMessage,
|
|
136
|
+
});
|
|
137
|
+
return {
|
|
138
|
+
success: false,
|
|
139
|
+
assetType: entry.assetType,
|
|
140
|
+
error: errorMessage,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Process all retryable entries.
|
|
146
|
+
*/
|
|
147
|
+
async processRetries() {
|
|
148
|
+
const results = [];
|
|
149
|
+
for (const assetType of ["SOL", "BTC", "USD"]) {
|
|
150
|
+
const entries = await this.ctx.outboxStore.getRetryableEntries(assetType, 10);
|
|
151
|
+
for (const entry of entries) {
|
|
152
|
+
// Get the block proof for this entry
|
|
153
|
+
const blockProof = await this.ctx.ledgerStore.getTier2Proof(entry.blockProofId);
|
|
154
|
+
if (!blockProof) {
|
|
155
|
+
console.warn(`Block proof ${entry.blockProofId} not found for retry`);
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
const result = await this.settleEntry(entry, blockProof);
|
|
159
|
+
results.push(result);
|
|
160
|
+
// Delay between retries
|
|
161
|
+
if (this.config.retryDelayMs > 0) {
|
|
162
|
+
await new Promise((resolve) => setTimeout(resolve, this.config.retryDelayMs));
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return results;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Get health status of all settlement rails.
|
|
170
|
+
*/
|
|
171
|
+
async getHealth() {
|
|
172
|
+
const result = {
|
|
173
|
+
solana: { available: false, healthy: false },
|
|
174
|
+
bitcoin: { available: false, healthy: false },
|
|
175
|
+
fiat: { available: false, healthy: false },
|
|
176
|
+
};
|
|
177
|
+
if (this.solanaAdapter) {
|
|
178
|
+
result.solana.available = true;
|
|
179
|
+
try {
|
|
180
|
+
const health = await this.solanaAdapter.getHealth();
|
|
181
|
+
result.solana.healthy = health.healthy;
|
|
182
|
+
result.solana.details = health;
|
|
183
|
+
}
|
|
184
|
+
catch {
|
|
185
|
+
result.solana.healthy = false;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
if (this.bitcoinAdapter) {
|
|
189
|
+
result.bitcoin.available = true;
|
|
190
|
+
try {
|
|
191
|
+
const health = await this.bitcoinAdapter.getHealth();
|
|
192
|
+
result.bitcoin.healthy = health.healthy;
|
|
193
|
+
result.bitcoin.details = health;
|
|
194
|
+
}
|
|
195
|
+
catch {
|
|
196
|
+
result.bitcoin.healthy = false;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
if (this.fiatAdapter) {
|
|
200
|
+
result.fiat.available = true;
|
|
201
|
+
try {
|
|
202
|
+
const health = await this.fiatAdapter.getHealth();
|
|
203
|
+
result.fiat.healthy = health.healthy;
|
|
204
|
+
}
|
|
205
|
+
catch {
|
|
206
|
+
result.fiat.healthy = false;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return result;
|
|
210
|
+
}
|
|
211
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
212
|
+
// Private Helpers
|
|
213
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
214
|
+
async executeSettlement(entry, blockProof) {
|
|
215
|
+
switch (entry.assetType) {
|
|
216
|
+
case "SOL":
|
|
217
|
+
return this.settleSolana(entry.netTransfers, blockProof);
|
|
218
|
+
case "BTC":
|
|
219
|
+
return this.settleBitcoin(entry.netTransfers, blockProof);
|
|
220
|
+
case "USD":
|
|
221
|
+
return this.settleFiat(entry.netTransfers, blockProof);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
async settleSolana(transfers, blockProof) {
|
|
225
|
+
if (!this.solanaAdapter) {
|
|
226
|
+
throw new Error("Solana adapter not configured");
|
|
227
|
+
}
|
|
228
|
+
return this.solanaAdapter.executeBatchedTransfer(transfers, blockProof);
|
|
229
|
+
}
|
|
230
|
+
async settleBitcoin(transfers, blockProof) {
|
|
231
|
+
if (!this.bitcoinAdapter) {
|
|
232
|
+
throw new Error("Bitcoin adapter not configured");
|
|
233
|
+
}
|
|
234
|
+
return this.bitcoinAdapter.executeBatchedSpend(transfers, blockProof);
|
|
235
|
+
}
|
|
236
|
+
async settleFiat(transfers, blockProof) {
|
|
237
|
+
if (!this.fiatAdapter) {
|
|
238
|
+
throw new Error("Fiat adapter not configured");
|
|
239
|
+
}
|
|
240
|
+
return this.fiatAdapter.executeTransfer(transfers, blockProof);
|
|
241
|
+
}
|
|
242
|
+
getSettlementTxId(result) {
|
|
243
|
+
if ("signature" in result) {
|
|
244
|
+
return result.signature; // Solana
|
|
245
|
+
}
|
|
246
|
+
if ("txid" in result) {
|
|
247
|
+
return result.txid; // Bitcoin
|
|
248
|
+
}
|
|
249
|
+
return result.iso20022MessageId; // Fiat
|
|
250
|
+
}
|
|
251
|
+
}
|