From 8887cbf276914b48919ecb947bdb37b81fd5932f Mon Sep 17 00:00:00 2001 From: valentunn Date: Sun, 14 Jun 2026 19:30:02 +0300 Subject: [PATCH] Legacy sign requests --- .../host-papp/__tests__/userSession.spec.ts | 30 ++++++++ packages/host-papp/src/index.ts | 3 + .../sessionManager/scale/createTransaction.ts | 9 ++- .../sso/sessionManager/scale/remoteMessage.ts | 16 ++++- .../src/sso/sessionManager/scale/signing.ts | 18 ++++- .../src/sso/sessionManager/userSession.ts | 72 ++++++++++++++++++- 6 files changed, 142 insertions(+), 6 deletions(-) diff --git a/packages/host-papp/__tests__/userSession.spec.ts b/packages/host-papp/__tests__/userSession.spec.ts index d4f48a72..073a33de 100644 --- a/packages/host-papp/__tests__/userSession.spec.ts +++ b/packages/host-papp/__tests__/userSession.spec.ts @@ -167,6 +167,36 @@ describe('createUserSession debug emits', () => { } }); + it('signRawLegacy emits host_action_sent with actionKind SignRawLegacyRequest and resolves with the signature', async () => { + mocks.request.mockReturnValue(okAsync(undefined)); + const signature = new Uint8Array([1, 2, 3]); + mocks.waitForRequestMessage.mockReturnValue(okAsync({ success: true, value: signature })); + + const session = buildSession(); + const { events, unsubscribe } = captureEvents(); + try { + const result = await session.signRawLegacy({} as any); + expect(result.isOk()).toBe(true); + expect(result._unsafeUnwrap()).toBe(signature); + expect(events.find(e => e.event === 'host_action_sent')?.payload).toMatchObject({ + actionKind: 'SignRawLegacyRequest', + }); + } finally { + unsubscribe(); + } + }); + + it('createTransactionLegacy resolves with the signed transaction from a CreateTransactionResponse', async () => { + mocks.request.mockReturnValue(okAsync(undefined)); + const signedTransaction = new Uint8Array([4, 5, 6]); + mocks.waitForRequestMessage.mockReturnValue(okAsync({ success: true, value: signedTransaction })); + + const session = buildSession(); + const result = await session.createTransactionLegacy({} as any); + expect(result.isOk()).toBe(true); + expect(result._unsafeUnwrap()).toBe(signedTransaction); + }); + it('getRingVrfAlias emits host_action_sent with actionKind RingVrfAliasRequest', async () => { mocks.request.mockReturnValue(okAsync(undefined)); mocks.waitForRequestMessage.mockReturnValue(okAsync({ success: true, value: new Uint8Array() as any })); diff --git a/packages/host-papp/src/index.ts b/packages/host-papp/src/index.ts index 824b1502..0da4a2ab 100644 --- a/packages/host-papp/src/index.ts +++ b/packages/host-papp/src/index.ts @@ -15,6 +15,8 @@ export type { StoredUserSession } from './sso/userSessionRepository.js'; export type { Identity } from './identity/types.js'; export type { + SignRawLegacyRequest, + SignRawLegacyResponse, SigningPayloadRequest, SigningPayloadResponse, SigningRawRequest, @@ -22,6 +24,7 @@ export type { } from './sso/sessionManager/scale/signing.js'; export type { RingVrfAliasRequest, RingVrfAliasResponse } from './sso/sessionManager/scale/ringVrf.js'; export type { + CreateTransactionLegacyRequest, CreateTransactionRequest, CreateTransactionResponse, } from './sso/sessionManager/scale/createTransaction.js'; diff --git a/packages/host-papp/src/sso/sessionManager/scale/createTransaction.ts b/packages/host-papp/src/sso/sessionManager/scale/createTransaction.ts index 728faf68..be0ba58a 100644 --- a/packages/host-papp/src/sso/sessionManager/scale/createTransaction.ts +++ b/packages/host-papp/src/sso/sessionManager/scale/createTransaction.ts @@ -1,4 +1,4 @@ -import { ProductAccountTransaction } from '@novasamatech/host-api'; +import { LegacyTransaction, ProductAccountTransaction } from '@novasamatech/host-api'; import { Enum } from '@novasamatech/scale'; import type { CodecType } from 'scale-ts'; import { Bytes, Result, Struct, str } from 'scale-ts'; @@ -10,6 +10,13 @@ export const CreateTransactionRequestCodec = Struct({ }), }); +export type CreateTransactionLegacyRequest = CodecType; +export const CreateTransactionLegacyRequestCodec = Struct({ + payload: Enum({ + v1: LegacyTransaction, + }), +}); + export type CreateTransactionResponse = CodecType; export const CreateTransactionResponseCodec = Struct({ // referencing to RemoteMessage.messageId diff --git a/packages/host-papp/src/sso/sessionManager/scale/remoteMessage.ts b/packages/host-papp/src/sso/sessionManager/scale/remoteMessage.ts index fde669bf..c87cc221 100644 --- a/packages/host-papp/src/sso/sessionManager/scale/remoteMessage.ts +++ b/packages/host-papp/src/sso/sessionManager/scale/remoteMessage.ts @@ -1,10 +1,19 @@ import type { CodecType } from 'scale-ts'; import { Enum, Struct, _void, str } from 'scale-ts'; -import { CreateTransactionRequestCodec, CreateTransactionResponseCodec } from './createTransaction.js'; +import { + CreateTransactionLegacyRequestCodec, + CreateTransactionRequestCodec, + CreateTransactionResponseCodec, +} from './createTransaction.js'; import { ResourceAllocationRequestCodec, ResourceAllocationResponseCodec } from './resourceAllocation.js'; import { RingVrfAliasRequestCodec, RingVrfAliasResponseCodec } from './ringVrf.js'; -import { SigningRequestCodec, SigningResponseCodec } from './signing.js'; +import { + SignRawLegacyRequestCodec, + SignRawLegacyResponseCodec, + SigningRequestCodec, + SigningResponseCodec, +} from './signing.js'; export type RemoteMessage = CodecType; export const RemoteMessageCodec = Struct({ @@ -20,6 +29,9 @@ export const RemoteMessageCodec = Struct({ ResourceAllocationResponse: ResourceAllocationResponseCodec, CreateTransactionRequest: CreateTransactionRequestCodec, CreateTransactionResponse: CreateTransactionResponseCodec, + CreateTransactionLegacyRequest: CreateTransactionLegacyRequestCodec, + SignRawLegacyRequest: SignRawLegacyRequestCodec, + SignRawLegacyResponse: SignRawLegacyResponseCodec, }), }), }); diff --git a/packages/host-papp/src/sso/sessionManager/scale/signing.ts b/packages/host-papp/src/sso/sessionManager/scale/signing.ts index 8915f70d..6573d36e 100644 --- a/packages/host-papp/src/sso/sessionManager/scale/signing.ts +++ b/packages/host-papp/src/sso/sessionManager/scale/signing.ts @@ -1,4 +1,4 @@ -import { ProductAccountId } from '@novasamatech/host-api'; +import { AccountId, ProductAccountId } from '@novasamatech/host-api'; import { Enum, Hex, OptionBool } from '@novasamatech/scale'; import type { CodecType } from 'scale-ts'; import { Bytes, Option, Result, Struct, Vector, str, u32 } from 'scale-ts'; @@ -32,6 +32,22 @@ export const SigningRawRequestCodec = Struct({ }), }); +export type SignRawLegacyRequest = CodecType; +export const SignRawLegacyRequestCodec = Struct({ + account: AccountId, + data: Enum({ + Bytes: Bytes(), + Payload: str, + }), +}); + +export type SignRawLegacyResponse = CodecType; +export const SignRawLegacyResponseCodec = Struct({ + // referencing to RemoteMessage.messageId + respondingTo: str, + signature: Result(Bytes(), str), +}); + export type SigningRequest = CodecType; export const SigningRequestCodec = Enum({ Payload: SigningPayloadRequestCodec, diff --git a/packages/host-papp/src/sso/sessionManager/userSession.ts b/packages/host-papp/src/sso/sessionManager/userSession.ts index b67df5c3..3db1872c 100644 --- a/packages/host-papp/src/sso/sessionManager/userSession.ts +++ b/packages/host-papp/src/sso/sessionManager/userSession.ts @@ -15,11 +15,16 @@ import { toError } from '../../helpers/utils.js'; import type { Callback } from '../../types.js'; import type { StoredUserSession } from '../userSessionRepository.js'; -import type { CreateTransactionRequest } from './scale/createTransaction.js'; +import type { CreateTransactionLegacyRequest, CreateTransactionRequest } from './scale/createTransaction.js'; import type { RemoteMessage } from './scale/remoteMessage.js'; import { RemoteMessageCodec } from './scale/remoteMessage.js'; import type { ApAllocationOutcome, ResourceAllocationRequest } from './scale/resourceAllocation.js'; -import type { SigningPayloadRequest, SigningPayloadResponseData, SigningRawRequest } from './scale/signing.js'; +import type { + SignRawLegacyRequest, + SigningPayloadRequest, + SigningPayloadResponseData, + SigningRawRequest, +} from './scale/signing.js'; // Timeout for the inner queue task. Without it the queue wedges forever when // the remote signer doesn't respond — e.g. the request @@ -111,7 +116,9 @@ export type UserSession = StoredUserSession & { abortPendingRequests(): ResultAsync; signPayload(payload: SigningPayloadRequest): ResultAsync; signRaw(payload: SigningRawRequest): ResultAsync; + signRawLegacy(payload: SignRawLegacyRequest): ResultAsync; createTransaction(payload: CreateTransactionRequest): ResultAsync; + createTransactionLegacy(payload: CreateTransactionLegacyRequest): ResultAsync; getRingVrfAlias( productAccountId: CodecType, productId: string, @@ -231,6 +238,37 @@ export function createUserSession({ }); }, + signRawLegacy(payload) { + return enqueue(() => { + const messageId = nanoid(); + const data = enumValue('v1', enumValue('SignRawLegacyRequest', payload)); + emitHostAction(messageId, actionKindFromMessageData(data), userSession.id); + + const responseFilter = (message: RemoteMessage) => { + if ( + message.data.tag === 'v1' && + message.data.value.tag === 'SignRawLegacyResponse' && + message.data.value.value.respondingTo === messageId + ) { + return message.data.value.value.signature; + } + }; + + const request = session.request(RemoteMessageCodec, { messageId, data }); + const reply = session.waitForRequestMessage(RemoteMessageCodec, responseFilter); + + const inner = awaitReplyOrAckFailure(request, reply).andThen(message => { + if (message.success) { + return ok(message.value); + } else { + return err(new Error(message.value)); + } + }); + + return withHostActionTrace(withQueueTimeout(inner, 'signRawLegacy'), messageId, userSession.id); + }); + }, + createTransaction(payload) { return enqueue(() => { const messageId = nanoid(); @@ -261,6 +299,36 @@ export function createUserSession({ }); }, + createTransactionLegacy(payload) { + return enqueue(() => { + const messageId = nanoid(); + const data = enumValue('v1', enumValue('CreateTransactionLegacyRequest', payload)); + + const responseFilter = (message: RemoteMessage) => { + if ( + message.data.tag === 'v1' && + message.data.value.tag === 'CreateTransactionResponse' && + message.data.value.value.respondingTo === messageId + ) { + return message.data.value.value.signedTransaction; + } + }; + + const request = session.request(RemoteMessageCodec, { messageId, data }); + const reply = session.waitForRequestMessage(RemoteMessageCodec, responseFilter); + + const inner = awaitReplyOrAckFailure(request, reply).andThen(message => { + if (message.success) { + return ok(message.value); + } else { + return err(new Error(message.value)); + } + }); + + return withQueueTimeout(inner, 'createTransactionLegacy'); + }); + }, + getRingVrfAlias(productAccountId, productId) { return enqueue(() => { const messageId = nanoid();