Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/product-sdk-keys-migration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"playground-cli": patch
---

Internal: bump `@parity/product-sdk-*` packages and `bulletin-deploy` to current latest, and consume `deriveProductAccountPublicKey` from `@parity/product-sdk-keys` instead of a local mirror. No user-visible behaviour change; output is byte-identical for production inputs.
19 changes: 9 additions & 10 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,27 +27,26 @@
"@dotdm/contracts": "^2.0.3",
"@parity/dotns-cli": "0.6.1",
"@parity/product-sdk-address": "^0.1.1",
"@parity/product-sdk-bulletin": "^0.4.0",
"@parity/product-sdk-chain-client": "^0.4.0",
"@parity/product-sdk-contracts": "^0.5.0",
"@parity/product-sdk-descriptors": "^0.4.0",
"@parity/product-sdk-host": "^0.3.0",
"@parity/product-sdk-keys": "^0.2.3",
"@parity/product-sdk-bulletin": "^0.4.2",
"@parity/product-sdk-chain-client": "^0.4.2",
"@parity/product-sdk-contracts": "^0.5.1",
"@parity/product-sdk-descriptors": "^0.4.1",
"@parity/product-sdk-host": "^0.4.0",
"@parity/product-sdk-keys": "^0.3.0",
"@parity/product-sdk-logger": "^0.1.1",
"@parity/product-sdk-storage": "^0.1.4",
"@parity/product-sdk-storage": "^0.1.5",
"@parity/product-sdk-terminal": "^0.2.1",
"@parity/product-sdk-tx": "^0.2.3",
"@parity/product-sdk-tx": "^0.2.4",
"@parity/product-sdk-utils": "^0.1.1",
"@polkadot-api/sdk-ink": "^0.7.0",
"@scure/sr25519": "^2.2.0",
"@sentry/node": "^9.47.1",
"bulletin-deploy": "0.7.20",
"bulletin-deploy": "0.7.24",
"commander": "^12.0.0",
"ink": "^5.2.1",
"polkadot-api": "^2.1.3",
"react": "^18.3.1",
"react-devtools-core": "file:stubs/react-devtools-core",
"scale-ts": "^1.6.1",
"tar": "^7.5.13"
},
"devDependencies": {
Expand Down
173 changes: 77 additions & 96 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

7 changes: 1 addition & 6 deletions src/commands/init/identityLine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,11 @@
* `.tsx` per the repo convention "pure logic that lives inside a `.tsx`
* component should be lifted into a sibling `.ts` file" (see
* `completion.ts` next to `InitScreen.tsx` for the same pattern).
*
* The product-account derivation here uses the local
* `src/utils/productAccountDerivation.ts` mirror; Phase 3 of the
* product-account unification migration will swap that for the canonical
* `@parity/product-sdk-keys` export. Output is byte-identical either way.
*/

import { deriveH160, ss58Decode, ss58Encode, truncateAddress } from "@parity/product-sdk-address";
import { deriveProductAccountPublicKey } from "@parity/product-sdk-keys";
import { PLAYGROUND_PRODUCT_ID } from "../../config.js";
import { deriveProductAccountPublicKey } from "../../utils/productAccountDerivation.js";

export interface ProductAccountAddresses {
ss58: string;
Expand Down
33 changes: 33 additions & 0 deletions src/utils/productAccount.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import { describe, expect, it } from "vitest";
import { ss58Encode } from "@parity/product-sdk-address";
import { deriveProductAccountPublicKey } from "@parity/product-sdk-keys";
import { PLAYGROUND_PRODUCT_ID } from "../config.js";

describe("product account integration", () => {
it("derives a stable SS58 address for playground.dot / index 0 from a fixed root", () => {
const root = new Uint8Array(32).fill(0);
const pubkey = deriveProductAccountPublicKey(root, PLAYGROUND_PRODUCT_ID, 0);
const address = ss58Encode(pubkey);
// The algorithm is locked by upstream frozen vectors in @parity/product-sdk-keys;
// here we only assert that wiring (config + sdk import + ss58Encode) does
// not silently produce an empty/malformed address.
expect(typeof address).toBe("string");
expect(address.length).toBeGreaterThan(40);
expect(pubkey.length).toBe(32);
});
});
86 changes: 0 additions & 86 deletions src/utils/productAccountDerivation.ts

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -18,58 +18,10 @@ import { ss58Encode } from "@parity/product-sdk-address";
import { seedToAccount } from "@parity/product-sdk-keys";
import type { UserSession } from "@parity/product-sdk-terminal";
import { PLAYGROUND_PRODUCT_ID } from "../config.js";
import { deriveProductAccountPublicKey } from "./productAccountDerivation.js";
import { createPlaygroundSessionSigner } from "./sessionSigner.js";

const DEV_PHRASE = "bottom drive obey lake curtain smoke basket hold race lonely fit walk";

function toHex(bytes: Uint8Array): string {
return (
"0x" +
Array.from(bytes)
.map((b) => b.toString(16).padStart(2, "0"))
.join("")
);
}

describe("deriveProductAccountPublicKey", () => {
// The mobile wallet derives the product account by applying derivation
// path "/product/{productId}/{derivationIndex}" to the user's mnemonic
// (substrate-sdk-android, EncryptionType.SR25519). For sr25519 soft
// derivation, applying that path to the mnemonic and applying it as
// public-soft to the bare mnemonic keypair's public key produce the
// same final public key. This test pins that property.
test("matches mnemonic+derivation-path keypair (single-junction productId)", () => {
// Empty path → bare master keypair from mnemonic. Same as Android's
// `deriveRootAccount()` / `derivationPath = null`. Public key here is
// what arrives in `session.rootAccountId` over SSO.
const root = seedToAccount(DEV_PHRASE, "");
const product = seedToAccount(DEV_PHRASE, "/product/playground.dot/0");

const local = deriveProductAccountPublicKey(root.publicKey, "playground.dot", 0);

expect(toHex(local)).toEqual(toHex(product.publicKey));
});

test("matches for a multi-segment productId and non-zero index", () => {
const root = seedToAccount(DEV_PHRASE, "");
const product = seedToAccount(DEV_PHRASE, "/product/some-app.dot/7");

const local = deriveProductAccountPublicKey(root.publicKey, "some-app.dot", 7);

expect(toHex(local)).toEqual(toHex(product.publicKey));
});

test("different productIds produce different accounts", () => {
const root = seedToAccount(DEV_PHRASE, "");

const a = deriveProductAccountPublicKey(root.publicKey, "playground.dot", 0);
const b = deriveProductAccountPublicKey(root.publicKey, "playground42.dot", 0);

expect(toHex(a)).not.toEqual(toHex(b));
});
});

// ────────────────────────────────────────────────────────────────────────────
// Init / deploy / playground-app equivalence
//
Expand All @@ -89,7 +41,7 @@ describe("deriveProductAccountPublicKey", () => {
// "/product/{dotNsId}/0").publicKey` and SS58-encodes it.
//
// As long as all three pin to the same `(rootPubKey, productId, 0)` triple,
// they yield byte-identical SS58 strings. This test is the regression guard.
// they yield byte-identical SS58 strings. These tests are the regression guard.
// ────────────────────────────────────────────────────────────────────────────
describe("init / deploy / playground-app account equivalence", () => {
// Build a stand-in for the mobile's SSO handshake response. Mirrors what
Expand Down
5 changes: 3 additions & 2 deletions src/utils/sessionSigner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ import { fromHex, toHex } from "polkadot-api/utils";
import { ss58Encode } from "@parity/product-sdk-address";
import type { UserSession } from "@parity/product-sdk-terminal";
import type { PolkadotSigner } from "polkadot-api";
import { deriveProductAccountPublicKey } from "./productAccountDerivation.js";
import { deriveProductAccountPublicKey } from "@parity/product-sdk-keys";

export interface ProductAccountRef {
productId: string;
Expand Down Expand Up @@ -111,7 +111,8 @@ export function createPlaygroundSessionSigner(
// `derivationPath = null`). Sr25519 soft derivation is composable on
// public keys alone, so deriving from it locally produces the SAME public
// key the mobile derives privately via `mnemonic + "/product/...{idx}"`.
// See `productAccountDerivation.test.ts` for the proof-of-equivalence.
// Algorithm parity with mobile/desktop is locked by the frozen vectors in
// `@parity/product-sdk-keys`'s `product-account.test.ts`.
const publicKey = deriveProductAccountPublicKey(
new Uint8Array(session.rootAccountId),
ref.productId,
Expand Down
Loading