Skip to content
Open
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
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Changelog

## 0.9.1

### Changed

- **Test host now matches signing accounts by SS58 address only.** `getPairByAddress` previously had a fallback that matched a raw hex public key (`0x…`) against keypairs, which silently masked a real bug: `@novasamatech/host-api-wrapper`'s `getLegacyAccountSigner` was sending the `signer` field as `toHex(publicKey)` instead of an SS58 address. Real wallets match by SS58 only, so the fallback made the test host more lenient than production. Removing it makes the legacy sign-raw/sign-payload paths fail here exactly as they do against a real wallet, guarding against regressions of [paritytech/product-sdk#156](https://github.com/paritytech/product-sdk/issues/156).
- **`test/test-product.ts` `trySignRaw` now exercises the real signer path** (`getLegacyAccountSigner(account).signBytes(...)`) instead of hand-building the wire request, so the integration test covers the actual SDK code path. Requires the fixed wrapper (`@novasamatech/host-api-wrapper` ≥ 0.8.1) to pass.

## 0.9.0

### Changed
Expand Down
6 changes: 6 additions & 0 deletions forum-post.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
# host-api-test-sdk 0.9.1

## Stricter (real-wallet-like) account matching

The test host now matches signing accounts by **SS58 address only**. Previously it also accepted a raw hex public key as the `signer`, which was more lenient than a real wallet and masked a bug in `getLegacyAccountSigner` (it sent the public key as hex instead of SS58). With this change, legacy-account `signRaw`/`signPayload` fail in the test host exactly as they do against a real wallet — so the test suite catches that class of regression. Use a wrapper that sends SS58 signers (`@novasamatech/host-api-wrapper` ≥ 0.8.1).

# host-api-test-sdk 0.9.0

Tracks upstream `@novasamatech/*@^0.8.0` ([triangle-js-sdks#179](https://github.com/paritytech/triangle-js-sdks/pull/179)). v0.8 is **wire-incompatible** with v0.7 — there is no compatibility shim, so your product side must be on `@novasamatech/host-api@^0.8.0` too. The [v0.8 migration guide](https://github.com/paritytech/triangle-js-sdks/blob/release/0.8/docs/migration/v0.8.md) lists all the product-side touchpoints; most products that use `createPapiProvider` for chain access and `@novasamatech/product-react-renderer` for custom chat don't need code changes.
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@parity/host-api-test-sdk",
"version": "0.9.0",
"version": "0.9.1",
"description": "Lightweight test host for Spektr product E2E testing — embeds dapps with auto-signing dev accounts, no Docker needed",
"license": "MIT",
"repository": {
Expand Down
11 changes: 5 additions & 6 deletions src/browser/host-runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -245,12 +245,11 @@ function getPairByAddress(address: string): KeyringPair | undefined {
for (const pair of pairsByUri.values()) {
if (pair.address === address) return pair;
}
// Try matching by public key hex (product-sdk sends 0x + hex(publicKey))
const normalized = address.toLowerCase();
for (const pair of pairsByUri.values()) {
if (u8aToHex(pair.publicKey).toLowerCase() === normalized) return pair;
}
// Try matching by SS58 re-encoding (address might be in different SS58 format)
// Match by SS58 re-encoding (address might be in a different SS58 prefix).
// NOTE: a raw hex-pubkey fallback was removed intentionally — real wallets
// match by SS58 address only, so the signer field must be a valid SS58
// address. Keeping the hex fallback here masked the getLegacyAccountSigner
// bug where `signer` was sent as toHex(publicKey).
for (const pair of pairsByUri.values()) {
try {
if (keyring.encodeAddress(pair.publicKey) === address) return pair;
Expand Down
22 changes: 13 additions & 9 deletions test/test-product.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ async function init() {

// Fetch legacy (root) accounts
let firstRootAddress: string | null = null;
let firstRootAccount: { publicKey: Uint8Array; name: string | undefined } | null = null;
const rootResult = await accountsProvider.getLegacyAccounts();

rootResult.match(
Expand All @@ -126,6 +127,7 @@ async function init() {
rootEl.textContent = JSON.stringify(keys);
rootEl.dataset.ready = 'true';
if (keys.length > 0) firstRootAddress = keys[0];
if (accounts.length > 0) firstRootAccount = accounts[0];
},
() => {},
);
Expand All @@ -136,15 +138,17 @@ async function init() {

async trySignRaw(): Promise<TestResult> {
try {
const r = await hostApi.signRawWithLegacyAccount(enumValue('v1', {
signer: firstRootAddress ?? '',
payload: { tag: 'Bytes' as const, value: new TextEncoder().encode('test-payload') },
}));
if (r.isOk()) {
const val = r.value;
return { ok: true, signature: val.value.signature };
}
return { ok: false, error: extractError(r.error) };
if (!firstRootAccount) return { ok: false, error: 'no legacy account' };
// Go through the real signer code path (getLegacyAccountSigner ->
// PolkadotSigner.signBytes), which is where the SS58/hex signer
// encoding is decided — rather than hand-building the wire request.
const signer = accountsProvider.getLegacyAccountSigner({
publicKey: firstRootAccount.publicKey,
dotNsIdentifier: '',
derivationIndex: 0,
});
const signature = await signer.signBytes(new TextEncoder().encode('test-payload'));
return { ok: true, signature: u8aToHex(signature) };
} catch (err) {
return { ok: false, error: extractError(err) };
}
Expand Down
Loading