@ftptech/canton-agent-wallet 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/LICENSE +201 -0
- package/README.md +134 -0
- package/dist/canton-hash.d.ts +61 -0
- package/dist/canton-hash.d.ts.map +1 -0
- package/dist/canton-hash.js +108 -0
- package/dist/canton-hash.js.map +1 -0
- package/dist/cli-args.d.ts +31 -0
- package/dist/cli-args.d.ts.map +1 -0
- package/dist/cli-args.js +56 -0
- package/dist/cli-args.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +123 -0
- package/dist/cli.js.map +1 -0
- package/dist/hash-binding.d.ts +40 -0
- package/dist/hash-binding.d.ts.map +1 -0
- package/dist/hash-binding.js +20 -0
- package/dist/hash-binding.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/keys.d.ts +26 -0
- package/dist/keys.d.ts.map +1 -0
- package/dist/keys.js +38 -0
- package/dist/keys.js.map +1 -0
- package/dist/onboard.d.ts +12 -0
- package/dist/onboard.d.ts.map +1 -0
- package/dist/onboard.js +152 -0
- package/dist/onboard.js.map +1 -0
- package/dist/pay.d.ts +16 -0
- package/dist/pay.d.ts.map +1 -0
- package/dist/pay.js +19 -0
- package/dist/pay.js.map +1 -0
- package/dist/relay-client.d.ts +128 -0
- package/dist/relay-client.d.ts.map +1 -0
- package/dist/relay-client.js +67 -0
- package/dist/relay-client.js.map +1 -0
- package/dist/relay-signer.d.ts +33 -0
- package/dist/relay-signer.d.ts.map +1 -0
- package/dist/relay-signer.js +44 -0
- package/dist/relay-signer.js.map +1 -0
- package/dist/store.d.ts +15 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +33 -0
- package/dist/store.js.map +1 -0
- package/dist/trusted-dso.d.ts +33 -0
- package/dist/trusted-dso.d.ts.map +1 -0
- package/dist/trusted-dso.js +36 -0
- package/dist/trusted-dso.js.map +1 -0
- package/dist/tx.d.ts +102 -0
- package/dist/tx.d.ts.map +1 -0
- package/dist/tx.js +328 -0
- package/dist/tx.js.map +1 -0
- package/dist/verify-prepared.d.ts +361 -0
- package/dist/verify-prepared.d.ts.map +1 -0
- package/dist/verify-prepared.js +2235 -0
- package/dist/verify-prepared.js.map +1 -0
- package/dist/withdraw.d.ts +18 -0
- package/dist/withdraw.d.ts.map +1 -0
- package/dist/withdraw.js +31 -0
- package/dist/withdraw.js.map +1 -0
- package/package.json +33 -0
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
interface DecodedExercise {
|
|
2
|
+
choiceId: string;
|
|
3
|
+
chosenValue: Uint8Array;
|
|
4
|
+
/** The exercised contract id (Exercise.contract_id, field 2), UTF-8 decoded.
|
|
5
|
+
* Pinned by the arms ONLY when the caller supplies an expected value
|
|
6
|
+
* (defense-in-depth: closes the resolve→prepare TOCTOU where a relay points
|
|
7
|
+
* the choice at a contract other than the one it resolved to the agent).
|
|
8
|
+
* undefined if the node carried no decodable contract_id. */
|
|
9
|
+
contractId?: string | undefined;
|
|
10
|
+
/** The exercised contract's template, as a `module:entity` qualified name (the
|
|
11
|
+
* package id is dropped because it changes across package upgrades). Used to
|
|
12
|
+
* pin WHICH template the validated choice runs against, so a relay cannot
|
|
13
|
+
* point a same-named choice at a confusable/attacker template. undefined if
|
|
14
|
+
* the node carried no decodable template_id. */
|
|
15
|
+
templateQualifiedName?: string | undefined;
|
|
16
|
+
}
|
|
17
|
+
/** The recognized `transaction.v1.Node` node-type oneof members. Anything not
|
|
18
|
+
* in this set is treated as PRESENT-AND-UNVALIDATED (reject), never skipped. */
|
|
19
|
+
type NodeKind = "create" | "fetch" | "exercise" | "rollback" | "query_by_key";
|
|
20
|
+
/**
|
|
21
|
+
* One fully-accounted node of the prepared transaction. EVERY entry in
|
|
22
|
+
* DamlTransaction.nodes produces exactly one of these — a node we cannot fully
|
|
23
|
+
* recognize is recorded with `recognized=false` rather than silently dropped, so
|
|
24
|
+
* the invariant layer can fail closed on it (this is the fix for the bypass
|
|
25
|
+
* where a sibling node carried under a non-1000 node version, or as a
|
|
26
|
+
* non-exercise node type, was invisible to verify).
|
|
27
|
+
*/
|
|
28
|
+
interface DecodedNode {
|
|
29
|
+
/** DamlTransaction.Node.node_id (the id `roots`/`children` reference). */
|
|
30
|
+
nodeId: string;
|
|
31
|
+
/** False ⇒ the node version was not the known `v1` (1000) oneof member, or it
|
|
32
|
+
* set an unknown/extra versioned_node member. Such a node is opaque: a
|
|
33
|
+
* participant understanding that version could execute it under our signature,
|
|
34
|
+
* so we must refuse to sign. */
|
|
35
|
+
recognizedVersion: boolean;
|
|
36
|
+
/** The v1 node-type oneof member, or undefined if !recognizedVersion or the
|
|
37
|
+
* inner node set no/unknown node_type member. undefined ⇒ reject. */
|
|
38
|
+
kind?: NodeKind | undefined;
|
|
39
|
+
/** For exercise nodes: the choice id + chosen value (money-critical args). */
|
|
40
|
+
exercise?: DecodedExercise | undefined;
|
|
41
|
+
/** node_id references this node declares as its children (Exercise.children /
|
|
42
|
+
* Rollback.children). Used to prove reachability from the single root so no
|
|
43
|
+
* orphan (extra-leg) node can hide in DamlTransaction.nodes. */
|
|
44
|
+
children: string[];
|
|
45
|
+
/** Every `Value`-typed payload this node introduces (exercise chosen_value &
|
|
46
|
+
* exercise_result, create argument). The foreign-party backstop scans the
|
|
47
|
+
* UNION of these across ALL nodes, not just the matched root exercise — so a
|
|
48
|
+
* party/recipient introduced by ANY sibling/consequence node surfaces. */
|
|
49
|
+
values: Uint8Array[];
|
|
50
|
+
/** Node-level `repeated string party` fields that are NOT carried inside a
|
|
51
|
+
* Daml `Value` tree: Create.signatories/stakeholders,
|
|
52
|
+
* Exercise.signatories/stakeholders/acting_parties/choice_observers,
|
|
53
|
+
* Fetch.signatories/stakeholders/acting_parties, and a QueryByKey's key
|
|
54
|
+
* maintainers. The foreign-party backstop scans these too, so an attacker
|
|
55
|
+
* party placed as e.g. a consequence contract's stakeholder/observer (a
|
|
56
|
+
* position invisible to the Value-leaf walk) still surfaces. */
|
|
57
|
+
partyMeta: string[];
|
|
58
|
+
}
|
|
59
|
+
interface DecodedPrepared {
|
|
60
|
+
/** `act_as` parties from Metadata.submitter_info (authoritative submitter). */
|
|
61
|
+
actAs: string[];
|
|
62
|
+
/** DamlTransaction.roots — node_ids declared as transaction roots. */
|
|
63
|
+
roots: string[];
|
|
64
|
+
/** EVERY node in DamlTransaction.nodes, fully accounted (recognized or not). */
|
|
65
|
+
nodes: DecodedNode[];
|
|
66
|
+
/** Convenience view: every recognized Exercise node's choice id + chosen value
|
|
67
|
+
* (backwards-compatible with callers that only inspected exercises). */
|
|
68
|
+
exercises: DecodedExercise[];
|
|
69
|
+
/** Metadata.synchronizer_id (proto field 3, in the SIGNED block). undefined if
|
|
70
|
+
* absent. Pinned to caller intent by the arms when supplied. */
|
|
71
|
+
synchronizerId?: string | undefined;
|
|
72
|
+
/** SIGNED Metadata timing fields, microseconds since the Unix epoch, as
|
|
73
|
+
* BigInts (undefined if absent). All in the proto's "needs to be signed"
|
|
74
|
+
* block, so the agent's signature covers them; the arms sanity-bound them. */
|
|
75
|
+
preparationTime?: bigint | undefined;
|
|
76
|
+
minLedgerEffectiveTime?: bigint | undefined;
|
|
77
|
+
maxLedgerEffectiveTime?: bigint | undefined;
|
|
78
|
+
maxRecordTime?: bigint | undefined;
|
|
79
|
+
/** Every Create `argument` Value carried in Metadata.input_contracts (proto
|
|
80
|
+
* field 7, SIGNED): the authenticated input-contract set. Scanned by the
|
|
81
|
+
* foreign-party backstop so a party that appears ONLY inside an input
|
|
82
|
+
* contract's payload (never in a transaction node) still surfaces. */
|
|
83
|
+
inputContractValues: Uint8Array[];
|
|
84
|
+
/** The node-level `repeated string party` fields of each Metadata.input_contracts
|
|
85
|
+
* Create — signatories (field 6) + stakeholders (field 7) + any contract-key
|
|
86
|
+
* parties (field 8). The V2 metadata hasher binds disclosed/input contracts via
|
|
87
|
+
* `hashNode(toCreateNode)` → addCreateNode, which hashes argument + signatories
|
|
88
|
+
* + stakeholders, so these party fields ARE covered by the agent's signature;
|
|
89
|
+
* the backstop scans them so a foreign party placed ONLY as an input-contract
|
|
90
|
+
* signatory/stakeholder (not in its argument) still surfaces. */
|
|
91
|
+
inputContractPartyMeta: string[];
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Decode a base64 `PreparedTransaction` into the bits we validate.
|
|
95
|
+
*
|
|
96
|
+
* Enumerates EVERY node in DamlTransaction.nodes — every node-version member and
|
|
97
|
+
* every node-type oneof member — recording unrecognized ones as
|
|
98
|
+
* present-but-unvalidated (never silently skipping them). The single allowed
|
|
99
|
+
* root exercise, no-orphan reachability, and the all-nodes value backstop are
|
|
100
|
+
* enforced by the per-arm invariant (`assertSingleAllowedRootExercise`).
|
|
101
|
+
*/
|
|
102
|
+
export declare function decodePrepared(preparedTransactionB64: string): DecodedPrepared;
|
|
103
|
+
interface ExtractedTransfer {
|
|
104
|
+
sender: string;
|
|
105
|
+
receiver: string;
|
|
106
|
+
amount: string;
|
|
107
|
+
instrumentAdmin: string;
|
|
108
|
+
instrumentId: string;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Extract the transfer body (sender/receiver/amount/instrument) from a
|
|
112
|
+
* TransferFactory_Transfer choice argument, BY TYPE at its structural position.
|
|
113
|
+
*/
|
|
114
|
+
export declare function extractTransfer(chosenValue: Uint8Array): ExtractedTransfer;
|
|
115
|
+
export interface PreparedTransferExpectation {
|
|
116
|
+
/** The agent's own party — must be the transfer sender (caller intent). */
|
|
117
|
+
sender: string;
|
|
118
|
+
/** The intended recipient (merchant payTo / withdraw target) — caller intent. */
|
|
119
|
+
receiver: string;
|
|
120
|
+
/** The EXACT amount string the agent asked to transfer — caller intent. */
|
|
121
|
+
amount: string;
|
|
122
|
+
/**
|
|
123
|
+
* The instrument id the agent expects (e.g. "Amulet"). Caller intent — the
|
|
124
|
+
* agent chooses what asset to pay in. The instrument ADMIN (DSO party) is
|
|
125
|
+
* resolved by the relay and is deliberately NOT used as a trust anchor: we
|
|
126
|
+
* never whitelist a relay-supplied party. Pass it only if you have an
|
|
127
|
+
* independently-trusted value to additionally pin.
|
|
128
|
+
*/
|
|
129
|
+
instrumentId: string;
|
|
130
|
+
/** Optional, independently-trusted instrument admin (the DSO party) to pin. When
|
|
131
|
+
* supplied it is pinned by exact equality AND becomes the ONLY admin/dso value
|
|
132
|
+
* the foreign-party backstop excludes outside its root position — closing the
|
|
133
|
+
* "alias the unpinned admin to the attacker and inject it as a consequence
|
|
134
|
+
* recipient" neutralization. Pass it whenever you have an out-of-band DSO. */
|
|
135
|
+
instrumentAdmin?: string;
|
|
136
|
+
/** Optional, caller-intent synchronizer id (the merchant-advertised domain).
|
|
137
|
+
* When supplied, the SIGNED Metadata.synchronizer_id is pinned to it (fail-
|
|
138
|
+
* closed if absent/different) so a relay cannot land the agent's signature on a
|
|
139
|
+
* domain of its choosing. */
|
|
140
|
+
synchronizerId?: string;
|
|
141
|
+
/** Optional, caller-intent template `module:entity` qualified name of the
|
|
142
|
+
* TransferFactory the choice runs against (e.g. the token-standard transfer
|
|
143
|
+
* factory). When supplied, the exercise's template_id is pinned, closing the
|
|
144
|
+
* template/contract-confusion surface. */
|
|
145
|
+
templateQualifiedName?: string;
|
|
146
|
+
/** Optional, caller-intent contract id of the factory the exercise targets.
|
|
147
|
+
* When supplied, Exercise.contract_id is pinned by exact equality (fail-closed
|
|
148
|
+
* on divergence) — defense-in-depth closing the resolve→prepare TOCTOU. No-op
|
|
149
|
+
* when omitted (the all-nodes party backstop still contains any redirect). */
|
|
150
|
+
expectedContractId?: string;
|
|
151
|
+
/** Inject Date.now() for testability of the timing sanity checks. */
|
|
152
|
+
nowMs?: number;
|
|
153
|
+
}
|
|
154
|
+
export declare class PreparedDecodeError extends Error {
|
|
155
|
+
constructor(message: string);
|
|
156
|
+
}
|
|
157
|
+
export declare class PreparedTransferMismatchError extends Error {
|
|
158
|
+
constructor(message: string);
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Assert the relay-returned `preparedTransaction` encodes EXACTLY the transfer
|
|
162
|
+
* the agent intended. Throws `PreparedTransferMismatchError` on any mismatch
|
|
163
|
+
* and `PreparedDecodeError` if the bytes are not a decodable PreparedTransaction
|
|
164
|
+
* carrying a single transfer exercise. Call this BEFORE signing `hash`.
|
|
165
|
+
*
|
|
166
|
+
* Fail-closed: anything we cannot positively prove matches the intent throws.
|
|
167
|
+
*/
|
|
168
|
+
export declare function assertPreparedTransferMatches(preparedTransactionB64: string, expect: PreparedTransferExpectation): void;
|
|
169
|
+
interface ExtractedCreateTransferCommand {
|
|
170
|
+
sender: string;
|
|
171
|
+
receiver: string;
|
|
172
|
+
delegate: string;
|
|
173
|
+
amount: string;
|
|
174
|
+
nonce: string;
|
|
175
|
+
/** Decimal-string of the Value.timestamp varint, or undefined if not present
|
|
176
|
+
* as a timestamp leaf (some encodings serialize Time differently); the
|
|
177
|
+
* caller only sanity-checks it, never pins it to money. */
|
|
178
|
+
expiresAt?: string;
|
|
179
|
+
/** The expectedDso party AS it appears at its own position, used ONLY to
|
|
180
|
+
* exclude it from the foreign-recipient backstop (never as an allowlist). */
|
|
181
|
+
expectedDso?: string;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Extract the v1 create-command fields from its (flat) choice-argument record,
|
|
185
|
+
* BY TYPE at its DAML DECLARATION-ORDER POSITION (what the participant binds),
|
|
186
|
+
* NOT by label — and fail closed on any label/position divergence (the
|
|
187
|
+
* amount-inflation / receiver-swap vector). Declaration order:
|
|
188
|
+
* [0] sender:Party [1] receiver:Party [2] delegate:Party [3] amount:Numeric
|
|
189
|
+
* [4] expiresAt:Time [5] nonce:Int [6] description:Optional Text [7] expectedDso:Party
|
|
190
|
+
* Honest encodings — fully labelled in order OR label-free (normalized) — both
|
|
191
|
+
* pass; a record whose labels disagree with declaration order (a decoy field
|
|
192
|
+
* re-using a money-critical label, or a mislabeled value at a money position) is
|
|
193
|
+
* rejected. Fails closed if a money-critical field is the wrong type / missing.
|
|
194
|
+
*/
|
|
195
|
+
export declare function extractCreateTransferCommand(chosenValue: Uint8Array): ExtractedCreateTransferCommand;
|
|
196
|
+
/** Caller-intent expectation for a v1 `CreateTransferCommand`. Every field is
|
|
197
|
+
* CALLER INTENT — none is taken from the relay's resolve response. */
|
|
198
|
+
export interface PreparedCreateTransferCommandExpectation {
|
|
199
|
+
/** The agent's own party — must be the command sender. */
|
|
200
|
+
sender: string;
|
|
201
|
+
/** The intended recipient (merchant payTo from the 402). */
|
|
202
|
+
receiver: string;
|
|
203
|
+
/** The facilitator/delegate party (from the 402 `extra.facilitatorParty`). */
|
|
204
|
+
delegate: string;
|
|
205
|
+
/** The EXACT amount string the agent asked to pay. */
|
|
206
|
+
amount: string;
|
|
207
|
+
/** Optional: the exact nonce the agent built the command with. When supplied
|
|
208
|
+
* it is pinned by exact equality; always sanity-checked (>= 0) regardless. */
|
|
209
|
+
nonce?: string;
|
|
210
|
+
/** Optional, independently-trusted DSO party. When supplied it is pinned by
|
|
211
|
+
* exact equality to the command's expectedDso AND becomes the ONLY dso value
|
|
212
|
+
* the foreign-party backstop excludes outside its root position — closing the
|
|
213
|
+
* "alias the unpinned expectedDso to the attacker and inject it as a
|
|
214
|
+
* consequence recipient" neutralization. Pass it whenever an out-of-band DSO
|
|
215
|
+
* is available; without it the backstop still closes the root-extra-leaf and
|
|
216
|
+
* money-role aliasing vectors. */
|
|
217
|
+
expectedDso?: string;
|
|
218
|
+
/** Optional, caller-intent synchronizer id (merchant-advertised domain). When
|
|
219
|
+
* supplied, the SIGNED Metadata.synchronizer_id is pinned to it (fail-closed
|
|
220
|
+
* if absent/different) so a relay cannot land the signature on its own domain. */
|
|
221
|
+
synchronizerId?: string;
|
|
222
|
+
/** Optional, caller-intent template `module:entity` of the ExternalPartyAmulet-
|
|
223
|
+
* Rules contract the choice runs against. When supplied, the exercise's
|
|
224
|
+
* template_id is pinned (template/contract-confusion). */
|
|
225
|
+
templateQualifiedName?: string;
|
|
226
|
+
/** Optional, caller-intent contract id of the ExternalPartyAmuletRules contract
|
|
227
|
+
* the exercise targets. When supplied, Exercise.contract_id is pinned by exact
|
|
228
|
+
* equality (fail-closed on divergence) — defense-in-depth closing the
|
|
229
|
+
* resolve→prepare TOCTOU. No-op when omitted. */
|
|
230
|
+
expectedContractId?: string;
|
|
231
|
+
/** Inject Date.now() for testability of the expiresAt / timing sanity checks. */
|
|
232
|
+
nowMs?: number;
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Assert the relay-returned `preparedTransaction` encodes EXACTLY the v1
|
|
236
|
+
* `ExternalPartyAmuletRules_CreateTransferCommand` the agent intended. Throws
|
|
237
|
+
* `PreparedTransferMismatchError` on any mismatch and `PreparedDecodeError` if
|
|
238
|
+
* the bytes are not a decodable PreparedTransaction carrying a single
|
|
239
|
+
* create-command exercise. Call BEFORE signing the hash. Fail-closed: anything
|
|
240
|
+
* not positively proven to match the intent throws.
|
|
241
|
+
*/
|
|
242
|
+
export declare function assertPreparedCreateTransferCommandMatches(preparedTransactionB64: string, expect: PreparedCreateTransferCommandExpectation): void;
|
|
243
|
+
/** Caller-intent expectation for a `TransferInstruction_Accept` (claim) prepare. */
|
|
244
|
+
export interface PreparedAcceptExpectation {
|
|
245
|
+
/** The agent's own party — must be the authoritative submitter (act_as). */
|
|
246
|
+
selfParty: string;
|
|
247
|
+
/** Inject Date.now() for testability of the timing sanity checks. */
|
|
248
|
+
nowMs?: number;
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Assert the relay-returned `preparedTransaction` for the claim path encodes
|
|
252
|
+
* EXACTLY a single `TransferInstruction_Accept` exercise submitted by the agent —
|
|
253
|
+
* and is NOT an outbound transfer/create-transfer-command drain. Throws
|
|
254
|
+
* `PreparedTransferMismatchError` on any mismatch and `PreparedDecodeError` if
|
|
255
|
+
* the bytes are not a decodable PreparedTransaction. Call BEFORE signing the
|
|
256
|
+
* hash. Fail-closed: anything not positively proven to be an inbound accept by
|
|
257
|
+
* the agent throws.
|
|
258
|
+
*/
|
|
259
|
+
export declare function assertPreparedAcceptMatches(preparedTransactionB64: string, expect: PreparedAcceptExpectation): void;
|
|
260
|
+
/**
|
|
261
|
+
* How to bind the relay-returned `hash` to the `preparedTransaction` bytes the
|
|
262
|
+
* agent just structurally validated. Exactly one of these must hold for a
|
|
263
|
+
* value-moving transfer, or we refuse to sign (fail-closed).
|
|
264
|
+
*/
|
|
265
|
+
export interface HashBindingOptions {
|
|
266
|
+
/**
|
|
267
|
+
* A function that recomputes the signing hash from the prepared-transaction
|
|
268
|
+
* bytes EXACTLY as Canton's participant does on `execute`
|
|
269
|
+
* (HASHING_SCHEME_VERSION_V2), returning it base64-encoded. When supplied,
|
|
270
|
+
* `assertHashBinding` requires `recomputeHash(prepared) === hash`
|
|
271
|
+
* (constant-time) and throws otherwise — this is the real cryptographic
|
|
272
|
+
* binding and the only thing that defeats a relay returning honest bytes with
|
|
273
|
+
* the hash of a DIFFERENT (tampered) transaction.
|
|
274
|
+
*
|
|
275
|
+
* It MUST be a deterministic function of the bytes alone and MUST match the
|
|
276
|
+
* participant's algorithm; if it does not, honest transfers fail closed
|
|
277
|
+
* (refused), never silently mis-bound.
|
|
278
|
+
*
|
|
279
|
+
* May be async: the conformant implementation (`recomputeHash` in
|
|
280
|
+
* `canton-hash.ts`, backed by `@canton-network/core-tx-visualizer`) uses
|
|
281
|
+
* WebCrypto and returns a Promise. `assertHashBinding` awaits the result, so a
|
|
282
|
+
* sync `=> string` recompute is also accepted.
|
|
283
|
+
*/
|
|
284
|
+
recomputeHash?: (preparedTransactionB64: string) => Promise<string> | string;
|
|
285
|
+
/**
|
|
286
|
+
* DANGEROUS, OFF BY DEFAULT. Sign the relay-returned hash WITHOUT recomputing
|
|
287
|
+
* it. The Canton Ledger API proto is explicit: "clients MUST recompute the
|
|
288
|
+
* hash from the raw transaction if the preparing participant is not trusted"
|
|
289
|
+
* and the hash field is "provided for convenience [and] may be removed". A
|
|
290
|
+
* compromised relay can return structurally-honest bytes paired with the hash
|
|
291
|
+
* of a tampered transaction; if it then swaps the bytes it forwards to the
|
|
292
|
+
* participant, the agent's signature authorizes the attacker's transfer.
|
|
293
|
+
*
|
|
294
|
+
* Only set this true if you independently display the decoded transfer to a
|
|
295
|
+
* human for approval before signing, OR you fully trust the relay. Never the
|
|
296
|
+
* default for autonomous agents.
|
|
297
|
+
*/
|
|
298
|
+
trustRelayHash?: boolean;
|
|
299
|
+
}
|
|
300
|
+
/** Thrown when a supplied `recomputeHash` cannot faithfully encode the bytes. */
|
|
301
|
+
export declare class PreparedHashUnavailableError extends Error {
|
|
302
|
+
constructor(message: string);
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Bind the `hash` the relay told us to sign to the `preparedTransaction` bytes
|
|
306
|
+
* we just structurally validated. Call this BEFORE signing `hash`.
|
|
307
|
+
*
|
|
308
|
+
* THE BINDING (why blind-signing breaks self-custody)
|
|
309
|
+
* ---------------------------------------------------
|
|
310
|
+
* The signed artifact is `hash`. The participant, on `execute`, recomputes the
|
|
311
|
+
* V2 hash from whatever bytes reach it and accepts the signature only if it
|
|
312
|
+
* matches. A compromised relay (the participant proxy) can therefore return
|
|
313
|
+
* structurally-HONEST `preparedTransaction` bytes (which sail through
|
|
314
|
+
* `assertPreparedTransferMatches`) paired with `hash = V2(PT_evil)` for a
|
|
315
|
+
* DIFFERENT transfer, then forward `PT_evil` instead of the honest bytes to the
|
|
316
|
+
* participant: V2(PT_evil) == hash, signature valid, attacker paid. Validating
|
|
317
|
+
* the bytes is therefore necessary but NOT sufficient — the agent must also
|
|
318
|
+
* prove the hash it signs is the hash OF THOSE validated bytes. That requires
|
|
319
|
+
* recomputing the hash locally; the relay-supplied hash is untrusted input.
|
|
320
|
+
*
|
|
321
|
+
* This function enforces, fail-closed:
|
|
322
|
+
* 1. shape: `hash` is a present, well-formed, non-empty base64 digest, and the
|
|
323
|
+
* `preparedTransaction` is a decodable PreparedTransaction with exactly the
|
|
324
|
+
* one transfer exercise (so we are signing a transfer we understood);
|
|
325
|
+
* 2. binding: EITHER `opts.recomputeHash` is supplied and
|
|
326
|
+
* `recomputeHash(prepared) === hash` (constant-time) — the real binding —
|
|
327
|
+
* OR `opts.trustRelayHash === true` is explicitly set (the documented,
|
|
328
|
+
* off-by-default escape hatch for human-in-the-loop / trusted-relay use).
|
|
329
|
+
* If neither holds, we REFUSE to sign rather than blind-sign an unbound,
|
|
330
|
+
* relay-chosen hash.
|
|
331
|
+
*/
|
|
332
|
+
export declare function assertHashBinding(preparedTransactionB64: string, hashB64: string, opts?: HashBindingOptions): Promise<void>;
|
|
333
|
+
/**
|
|
334
|
+
* Returns true iff `hash` is the plain SHA-256 of the `preparedTransaction`
|
|
335
|
+
* bytes. NOTE: Canton V2 does NOT hash this way (see `assertHashBinding`), so
|
|
336
|
+
* this is advisory only and is NEVER used to accept or reject a submission.
|
|
337
|
+
* Retained for diagnostics / future schemes.
|
|
338
|
+
*/
|
|
339
|
+
export declare function hashMatchesPreparedPlain(preparedTransactionB64: string, hashB64: string): boolean;
|
|
340
|
+
/** Thrown when relay-returned onboarding topology cannot be proven to bind the
|
|
341
|
+
* agent's own key + namespace. Onboarding refuses to sign the multiHash. */
|
|
342
|
+
export declare class OnboardingTopologyMismatchError extends Error {
|
|
343
|
+
constructor(message: string);
|
|
344
|
+
}
|
|
345
|
+
/** What the agent independently knows/expects about its onboarding. */
|
|
346
|
+
export interface OnboardingTopologyExpectation {
|
|
347
|
+
/** The agent's OWN freshly-generated public key, base64 SPKI/DER (keys.ts). */
|
|
348
|
+
publicKeySpkiB64: string;
|
|
349
|
+
/** The relay-returned party id, expected `name::fingerprint`. */
|
|
350
|
+
party: string;
|
|
351
|
+
/** The relay-returned canonical key fingerprint (the party's namespace). */
|
|
352
|
+
publicKeyFingerprint: string;
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Assert the relay-returned onboarding topology binds the agent's OWN key and
|
|
356
|
+
* namespace. Call this BEFORE signing the onboarding `multiHash`. Fail-closed:
|
|
357
|
+
* anything not positively proven throws `OnboardingTopologyMismatchError`.
|
|
358
|
+
*/
|
|
359
|
+
export declare function assertOnboardingTopologyBindsKey(onboardingTransactionsB64: string[], expect: OnboardingTopologyExpectation): void;
|
|
360
|
+
export {};
|
|
361
|
+
//# sourceMappingURL=verify-prepared.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verify-prepared.d.ts","sourceRoot":"","sources":["../src/verify-prepared.ts"],"names":[],"mappings":"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"}
|