@parity/product-sdk-terminal 0.2.1 → 0.3.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 +42 -46
- package/dist/adapter-Dt3ryKnw.d.ts +72 -0
- package/dist/{chunk-5MCLSFKQ.js → chunk-GHOFKVUG.js} +1 -1
- package/dist/chunk-GHOFKVUG.js.map +1 -0
- package/dist/host.d.ts +113 -0
- package/dist/host.js +1414 -0
- package/dist/host.js.map +1 -0
- package/dist/index.d.ts +43 -84
- package/dist/index.js +170 -81
- package/dist/index.js.map +1 -1
- package/dist/testing.d.ts +1 -1
- package/dist/testing.js +12 -4
- package/dist/testing.js.map +1 -1
- package/package.json +15 -18
- package/dist/chunk-5MCLSFKQ.js.map +0 -1
- package/dist/loader.mjs +0 -76
- package/dist/register.d.ts +0 -2
- package/dist/register.js +0 -9
- package/dist/register.js.map +0 -1
- package/scripts/patch-wasm.sh +0 -27
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createNodeStorageAdapter
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-GHOFKVUG.js";
|
|
4
4
|
|
|
5
5
|
// src/adapter.ts
|
|
6
6
|
import {
|
|
@@ -26,7 +26,6 @@ function createTerminalAdapter(options) {
|
|
|
26
26
|
const statementStore = createPapiStatementStoreAdapter(trackedLazyClient);
|
|
27
27
|
const adapter = createPappAdapter({
|
|
28
28
|
appId: options.appId,
|
|
29
|
-
metadata: options.metadataUrl,
|
|
30
29
|
hostMetadata: options.hostMetadata,
|
|
31
30
|
adapters: {
|
|
32
31
|
storage,
|
|
@@ -38,6 +37,7 @@ function createTerminalAdapter(options) {
|
|
|
38
37
|
return {
|
|
39
38
|
...adapter,
|
|
40
39
|
appId: options.appId,
|
|
40
|
+
storageDir: options.storageDir,
|
|
41
41
|
destroy() {
|
|
42
42
|
if (destroyPromise) return destroyPromise;
|
|
43
43
|
destroyPromise = teardown(adapter.sessions, trackedLazyClient);
|
|
@@ -272,20 +272,57 @@ if (void 0) {
|
|
|
272
272
|
var fakeLazyClient2;
|
|
273
273
|
|
|
274
274
|
// src/signer.ts
|
|
275
|
-
import {
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
275
|
+
import { decAnyMetadata, unifyMetadata } from "@polkadot-api/substrate-bindings";
|
|
276
|
+
function buildCreateTransactionRequest(productAccountId, callData, signedExtensions, txExtVersion) {
|
|
277
|
+
const checkGenesis = signedExtensions.CheckGenesis;
|
|
278
|
+
if (!checkGenesis) {
|
|
279
|
+
throw new Error(
|
|
280
|
+
"Cannot build transaction: chain did not provide the CheckGenesis signed extension"
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
return {
|
|
284
|
+
payload: {
|
|
285
|
+
tag: "v1",
|
|
286
|
+
value: {
|
|
287
|
+
signer: productAccountId,
|
|
288
|
+
// CheckGenesis carries the genesis hash as its additionalSigned (implicit) data.
|
|
289
|
+
genesisHash: checkGenesis.additionalSigned,
|
|
290
|
+
callData,
|
|
291
|
+
extensions: Object.values(signedExtensions).map(
|
|
292
|
+
({ identifier, value, additionalSigned }) => ({
|
|
293
|
+
id: identifier,
|
|
294
|
+
extra: value,
|
|
295
|
+
additionalSigned
|
|
296
|
+
})
|
|
297
|
+
),
|
|
298
|
+
txExtVersion
|
|
299
|
+
}
|
|
285
300
|
}
|
|
286
|
-
return result.value.signature;
|
|
287
301
|
};
|
|
288
302
|
}
|
|
303
|
+
function txExtVersionFromMetadata(metadata) {
|
|
304
|
+
const decoded = unifyMetadata(decAnyMetadata(metadata));
|
|
305
|
+
const latestVersion = decoded.extrinsic.version.reduce((max, v) => Math.max(max, v), 0);
|
|
306
|
+
return latestVersion === 4 ? 0 : latestVersion;
|
|
307
|
+
}
|
|
308
|
+
async function requestSignedTransaction(session, request) {
|
|
309
|
+
const result = await session.createTransaction(request);
|
|
310
|
+
if (result.isErr()) {
|
|
311
|
+
throw new Error(`Mobile transaction signing rejected: ${result.error.message}`);
|
|
312
|
+
}
|
|
313
|
+
return result.value;
|
|
314
|
+
}
|
|
315
|
+
function makeTxSignTx(session, productAccountId) {
|
|
316
|
+
return async (callData, signedExtensions, metadata) => requestSignedTransaction(
|
|
317
|
+
session,
|
|
318
|
+
buildCreateTransactionRequest(
|
|
319
|
+
productAccountId,
|
|
320
|
+
callData,
|
|
321
|
+
signedExtensions,
|
|
322
|
+
txExtVersionFromMetadata(metadata)
|
|
323
|
+
)
|
|
324
|
+
);
|
|
325
|
+
}
|
|
289
326
|
function makeRawBytesSignCallback(session, productAccountId) {
|
|
290
327
|
return async (data) => {
|
|
291
328
|
const result = await session.signRaw({
|
|
@@ -300,19 +337,21 @@ function makeRawBytesSignCallback(session, productAccountId) {
|
|
|
300
337
|
}
|
|
301
338
|
function buildSessionSigner(session, ref) {
|
|
302
339
|
const productAccountId = [ref.productId, ref.derivationIndex];
|
|
303
|
-
const publicKey = new Uint8Array(session.remoteAccount.accountId);
|
|
304
|
-
const base = getPolkadotSigner(
|
|
305
|
-
publicKey,
|
|
306
|
-
"Sr25519",
|
|
307
|
-
makeTxSignCallback(session, productAccountId)
|
|
308
|
-
);
|
|
340
|
+
const publicKey = ref.publicKey ?? new Uint8Array(session.remoteAccount.accountId);
|
|
309
341
|
return {
|
|
310
|
-
|
|
342
|
+
publicKey,
|
|
343
|
+
// Transaction signing → host-papp `createTransaction`: the wallet builds
|
|
344
|
+
// and signs the extrinsic, so it can show a decoded tx (no `<Bytes>`
|
|
345
|
+
// envelope) and unknown signed extensions (e.g. `AsPgas`) survive.
|
|
346
|
+
signTx: makeTxSignTx(session, productAccountId),
|
|
347
|
+
// Arbitrary-byte signing keeps the `Bytes` tag so the wallet applies the
|
|
348
|
+
// `<Bytes>...</Bytes>` anti-phishing envelope exactly once on its side
|
|
349
|
+
// (correct for non-extrinsic user data).
|
|
311
350
|
signBytes: makeRawBytesSignCallback(session, productAccountId)
|
|
312
351
|
};
|
|
313
352
|
}
|
|
314
|
-
function createSessionSigner(session, adapter) {
|
|
315
|
-
return buildSessionSigner(session, { productId: adapter.appId, derivationIndex: 0 });
|
|
353
|
+
function createSessionSigner(session, adapter, publicKey) {
|
|
354
|
+
return buildSessionSigner(session, { productId: adapter.appId, derivationIndex: 0, publicKey });
|
|
316
355
|
}
|
|
317
356
|
function createSessionSignerForAccount(session, ref) {
|
|
318
357
|
return buildSessionSigner(session, ref);
|
|
@@ -331,17 +370,27 @@ if (void 0) {
|
|
|
331
370
|
opts.signRaw ?? (async () => {
|
|
332
371
|
throw new Error("signRaw not stubbed in this test");
|
|
333
372
|
})
|
|
373
|
+
),
|
|
374
|
+
createTransaction: vi.fn(
|
|
375
|
+
opts.createTransaction ?? (async () => {
|
|
376
|
+
throw new Error("createTransaction not stubbed in this test");
|
|
377
|
+
})
|
|
334
378
|
)
|
|
335
379
|
};
|
|
380
|
+
}, ext = function(identifier, value, additionalSigned) {
|
|
381
|
+
return {
|
|
382
|
+
identifier,
|
|
383
|
+
value: new Uint8Array(value),
|
|
384
|
+
additionalSigned: new Uint8Array(additionalSigned)
|
|
385
|
+
};
|
|
336
386
|
}, fakeAdapter = function(appId) {
|
|
337
387
|
return { appId };
|
|
338
388
|
};
|
|
339
|
-
makeSession2 = makeSession, fakeAdapter2 = fakeAdapter;
|
|
389
|
+
makeSession2 = makeSession, ext2 = ext, fakeAdapter2 = fakeAdapter;
|
|
340
390
|
const { describe, test, expect, vi } = void 0;
|
|
341
391
|
const { ok, err } = await null;
|
|
342
|
-
const { fromHex } = await null;
|
|
343
392
|
describe("createSessionSigner", () => {
|
|
344
|
-
test("
|
|
393
|
+
test("falls back to remoteAccount.accountId when no product key is passed", () => {
|
|
345
394
|
const bytes = Array.from({ length: 32 }, (_, i) => i);
|
|
346
395
|
const signer = createSessionSigner(
|
|
347
396
|
makeSession({ accountIdBytes: bytes }),
|
|
@@ -349,6 +398,17 @@ if (void 0) {
|
|
|
349
398
|
);
|
|
350
399
|
expect(signer.publicKey).toEqual(new Uint8Array(bytes));
|
|
351
400
|
});
|
|
401
|
+
test("uses the host-supplied product-account public key when provided", () => {
|
|
402
|
+
const walletBytes = Array.from({ length: 32 }, (_, i) => i);
|
|
403
|
+
const productKey = new Uint8Array(32).fill(171);
|
|
404
|
+
const signer = createSessionSigner(
|
|
405
|
+
makeSession({ accountIdBytes: walletBytes }),
|
|
406
|
+
fakeAdapter("test-app"),
|
|
407
|
+
productKey
|
|
408
|
+
);
|
|
409
|
+
expect(signer.publicKey).toEqual(productKey);
|
|
410
|
+
expect(signer.publicKey).not.toEqual(new Uint8Array(walletBytes));
|
|
411
|
+
});
|
|
352
412
|
test("signBytes routes through session.signRaw with the Bytes tag", async () => {
|
|
353
413
|
const sig = new Uint8Array([9, 8, 7, 6, 5, 4, 3, 2, 1]);
|
|
354
414
|
const captured = [];
|
|
@@ -387,75 +447,103 @@ if (void 0) {
|
|
|
387
447
|
);
|
|
388
448
|
});
|
|
389
449
|
});
|
|
390
|
-
describe("
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
const
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
450
|
+
describe("buildCreateTransactionRequest \u2014 tx payload (the AsPgas fix)", () => {
|
|
451
|
+
const checkGenesis = ext("CheckGenesis", [], [17, 34, 51]);
|
|
452
|
+
test("wraps the payload as v1 with signer, callData, and txExtVersion", () => {
|
|
453
|
+
const callData = new Uint8Array([202, 17]);
|
|
454
|
+
const req = buildCreateTransactionRequest(
|
|
455
|
+
["my-app", 3],
|
|
456
|
+
callData,
|
|
457
|
+
{ CheckGenesis: checkGenesis },
|
|
458
|
+
5
|
|
459
|
+
);
|
|
460
|
+
expect(req.payload.tag).toBe("v1");
|
|
461
|
+
expect(req.payload.value.signer).toEqual(["my-app", 3]);
|
|
462
|
+
expect(req.payload.value.callData).toEqual(callData);
|
|
463
|
+
expect(req.payload.value.txExtVersion).toBe(5);
|
|
464
|
+
});
|
|
465
|
+
test("takes the genesis hash from CheckGenesis.additionalSigned", () => {
|
|
466
|
+
const req = buildCreateTransactionRequest(
|
|
467
|
+
["my-app", 0],
|
|
468
|
+
new Uint8Array([0]),
|
|
469
|
+
{ CheckGenesis: checkGenesis },
|
|
470
|
+
0
|
|
471
|
+
);
|
|
472
|
+
expect(req.payload.value.genesisHash).toEqual(new Uint8Array([17, 34, 51]));
|
|
473
|
+
});
|
|
474
|
+
test("maps every signed extension to { id, extra, additionalSigned }", () => {
|
|
475
|
+
const req = buildCreateTransactionRequest(
|
|
476
|
+
["my-app", 0],
|
|
477
|
+
new Uint8Array([0]),
|
|
478
|
+
{ CheckGenesis: checkGenesis, CheckNonce: ext("CheckNonce", [7], []) },
|
|
479
|
+
0
|
|
480
|
+
);
|
|
481
|
+
expect(req.payload.value.extensions).toEqual([
|
|
482
|
+
{
|
|
483
|
+
id: "CheckGenesis",
|
|
484
|
+
extra: new Uint8Array([]),
|
|
485
|
+
additionalSigned: new Uint8Array([17, 34, 51])
|
|
486
|
+
},
|
|
487
|
+
{
|
|
488
|
+
id: "CheckNonce",
|
|
489
|
+
extra: new Uint8Array([7]),
|
|
490
|
+
additionalSigned: new Uint8Array([])
|
|
397
491
|
}
|
|
398
|
-
|
|
399
|
-
const callback = makeTxSignCallback(session, ["my-app", 0]);
|
|
400
|
-
await callback(new Uint8Array([222, 173, 190, 239]));
|
|
401
|
-
expect(captured).toHaveLength(1);
|
|
402
|
-
const req = captured[0];
|
|
403
|
-
expect(req.productAccountId).toEqual(["my-app", 0]);
|
|
404
|
-
expect(req.data.tag).toBe("Payload");
|
|
405
|
-
expect(req.data.value).toBe("0xdeadbeef");
|
|
492
|
+
]);
|
|
406
493
|
});
|
|
407
|
-
test("
|
|
408
|
-
const
|
|
409
|
-
|
|
494
|
+
test("preserves an unknown signed extension verbatim (e.g. AsPgas)", () => {
|
|
495
|
+
const req = buildCreateTransactionRequest(
|
|
496
|
+
["my-app", 0],
|
|
497
|
+
new Uint8Array([0]),
|
|
498
|
+
{ CheckGenesis: checkGenesis, AsPgas: ext("AsPgas", [222, 173], [190, 239]) },
|
|
499
|
+
0
|
|
500
|
+
);
|
|
501
|
+
expect(req.payload.value.extensions).toContainEqual({
|
|
502
|
+
id: "AsPgas",
|
|
503
|
+
extra: new Uint8Array([222, 173]),
|
|
504
|
+
additionalSigned: new Uint8Array([190, 239])
|
|
410
505
|
});
|
|
411
|
-
const callback = makeTxSignCallback(session, ["my-app", 0]);
|
|
412
|
-
await callback(new Uint8Array([1, 2, 3]));
|
|
413
|
-
const sessionWithSpies = session;
|
|
414
|
-
expect(sessionWithSpies.signPayload.mock.calls).toHaveLength(0);
|
|
415
|
-
expect(sessionWithSpies.signRaw.mock.calls).toHaveLength(1);
|
|
416
506
|
});
|
|
417
|
-
test("
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
});
|
|
422
|
-
const callback = makeTxSignCallback(session, ["my-app", 0]);
|
|
423
|
-
const out = await callback(new Uint8Array([0]));
|
|
424
|
-
expect(out).toBe(sig);
|
|
507
|
+
test("throws a clear error when CheckGenesis is absent", () => {
|
|
508
|
+
expect(
|
|
509
|
+
() => buildCreateTransactionRequest(["my-app", 0], new Uint8Array([0]), {}, 0)
|
|
510
|
+
).toThrow(/CheckGenesis/);
|
|
425
511
|
});
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
512
|
+
});
|
|
513
|
+
describe("requestSignedTransaction \u2014 SSO round-trip", () => {
|
|
514
|
+
const request = buildCreateTransactionRequest(
|
|
515
|
+
["my-app", 0],
|
|
516
|
+
new Uint8Array([0]),
|
|
517
|
+
{ CheckGenesis: ext("CheckGenesis", [], [1]) },
|
|
518
|
+
0
|
|
519
|
+
);
|
|
520
|
+
test("returns the signed extrinsic bytes from the mobile response", async () => {
|
|
521
|
+
const signed = new Uint8Array([171, 205, 239]);
|
|
522
|
+
const session = makeSession({ createTransaction: async () => ok(signed) });
|
|
523
|
+
const out = await requestSignedTransaction(session, request);
|
|
524
|
+
expect(out).toBe(signed);
|
|
525
|
+
});
|
|
526
|
+
test("calls session.createTransaction \u2014 never signRaw/signPayload \u2014 with the request", async () => {
|
|
440
527
|
const captured = [];
|
|
441
528
|
const session = makeSession({
|
|
442
|
-
|
|
443
|
-
captured.push(
|
|
444
|
-
return ok(
|
|
529
|
+
createTransaction: async (req) => {
|
|
530
|
+
captured.push(req);
|
|
531
|
+
return ok(new Uint8Array([1]));
|
|
445
532
|
}
|
|
446
533
|
});
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
const
|
|
450
|
-
expect(
|
|
534
|
+
await requestSignedTransaction(session, request);
|
|
535
|
+
expect(captured).toEqual([request]);
|
|
536
|
+
const spies = session;
|
|
537
|
+
expect(spies.createTransaction.mock.calls).toHaveLength(1);
|
|
538
|
+
expect(spies.signPayload.mock.calls).toHaveLength(0);
|
|
539
|
+
expect(spies.signRaw.mock.calls).toHaveLength(0);
|
|
451
540
|
});
|
|
452
541
|
test("throws a clear error when the mobile rejects", async () => {
|
|
453
542
|
const session = makeSession({
|
|
454
|
-
|
|
543
|
+
createTransaction: async () => err({ message: "user declined" })
|
|
455
544
|
});
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
"Mobile signing rejected: user declined"
|
|
545
|
+
await expect(requestSignedTransaction(session, request)).rejects.toThrow(
|
|
546
|
+
"Mobile transaction signing rejected: user declined"
|
|
459
547
|
);
|
|
460
548
|
});
|
|
461
549
|
});
|
|
@@ -525,6 +613,7 @@ if (void 0) {
|
|
|
525
613
|
});
|
|
526
614
|
}
|
|
527
615
|
var makeSession2;
|
|
616
|
+
var ext2;
|
|
528
617
|
var fakeAdapter2;
|
|
529
618
|
|
|
530
619
|
// src/sessions.ts
|