From ae6e1ea305b491d3c8b43fa22a94aeda8bcab063 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Wed, 12 Jan 2022 18:32:13 +0100 Subject: [PATCH 1/4] add wallet methods to legacy client --- legacy/types/src/constants/jsonrpc.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/legacy/types/src/constants/jsonrpc.ts b/legacy/types/src/constants/jsonrpc.ts index b88830ec1..19d5cdf96 100644 --- a/legacy/types/src/constants/jsonrpc.ts +++ b/legacy/types/src/constants/jsonrpc.ts @@ -6,7 +6,7 @@ export const WALLET_METHODS = [ "wallet_registerOnboarding", "wallet_watchAsset", "wallet_scanQRCode", -] +]; export const SIGNING_METHODS = [ "eth_sendTransaction", @@ -18,7 +18,7 @@ export const SIGNING_METHODS = [ "eth_signTypedData_v3", "eth_signTypedData_v4", "personal_sign", - ...WALLET_METHODS + ...WALLET_METHODS, ]; // backwards-compatibility alias From 9d9e30052c5f9323f63fa3f194d9148c11fb8712 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Fri, 21 Jan 2022 10:54:55 +0000 Subject: [PATCH 2/4] add extend expiry method --- packages/client/src/client.ts | 17 ++++- packages/client/src/constants/client.ts | 2 + packages/client/src/constants/pairing.ts | 2 + packages/client/src/constants/session.ts | 2 + packages/client/src/controllers/engine.ts | 72 ++++++++++++++++++++++ packages/client/src/controllers/pairing.ts | 32 +++++++--- packages/client/src/controllers/session.ts | 28 ++++++--- packages/types/src/client.ts | 13 ++-- packages/types/src/engine.ts | 18 ++++-- packages/types/src/pairing.ts | 20 ++++-- packages/types/src/sequence.ts | 33 +++++++--- packages/types/src/session.ts | 22 ++++--- packages/utils/src/error.ts | 27 +++++++- 13 files changed, 237 insertions(+), 51 deletions(-) diff --git a/packages/client/src/client.ts b/packages/client/src/client.ts index a046459b3..2d6d197a2 100644 --- a/packages/client/src/client.ts +++ b/packages/client/src/client.ts @@ -261,12 +261,16 @@ export class Client extends IClient { this.logger.trace({ type: "method", method: "reject", pending }); } + public async update(params: ClientTypes.UpdateParams): Promise { + await this.session.update(params); + } + public async upgrade(params: ClientTypes.UpgradeParams): Promise { await this.session.upgrade(params); } - public async update(params: ClientTypes.UpdateParams): Promise { - await this.session.update(params); + public async extend(params: ClientTypes.ExtendParams): Promise { + await this.session.extend(params); } public async request(params: ClientTypes.RequestParams): Promise { @@ -427,6 +431,15 @@ export class Client extends IClient { this.events.emit(eventName, session, upgrade); }, ); + this.session.on( + SESSION_EVENTS.extended, + (session: SessionTypes.Settled, extension: Partial) => { + const eventName = CLIENT_EVENTS.session.extended; + this.logger.info(`Emitting ${eventName}`); + this.logger.debug({ type: "event", event: eventName, data: session, extension }); + this.events.emit(eventName, session, extension); + }, + ); this.session.on( SESSION_EVENTS.deleted, (session: SessionTypes.Settled, reason: ErrorResponse) => { diff --git a/packages/client/src/constants/client.ts b/packages/client/src/constants/client.ts index 6214da3f1..3bd234d86 100644 --- a/packages/client/src/constants/client.ts +++ b/packages/client/src/constants/client.ts @@ -14,6 +14,7 @@ export const CLIENT_EVENTS = { proposal: "pairing_proposal", updated: "pairing_updated", upgraded: "pairing_upgraded", + extended: "pairing_extended", created: "pairing_created", deleted: "pairing_deleted", sync: "pairing_sync", @@ -22,6 +23,7 @@ export const CLIENT_EVENTS = { proposal: "session_proposal", updated: "session_updated", upgraded: "session_upgraded", + extended: "session_extended", created: "session_created", deleted: "session_deleted", notification: "session_notification", diff --git a/packages/client/src/constants/pairing.ts b/packages/client/src/constants/pairing.ts index 752d5c7c3..cc29c938f 100644 --- a/packages/client/src/constants/pairing.ts +++ b/packages/client/src/constants/pairing.ts @@ -7,6 +7,7 @@ export const PAIRING_JSONRPC: PairingTypes.JsonRpc = { reject: "wc_pairingReject", update: "wc_pairingUpdate", upgrade: "wc_pairingUpgrade", + extend: "wc_pairingExtend", delete: "wc_pairingDelete", payload: "wc_pairingPayload", ping: "wc_pairingPing", @@ -32,6 +33,7 @@ export const PAIRING_EVENTS: PairingTypes.Events = { settled: "pairing_settled", updated: "pairing_updated", upgraded: "pairing_upgraded", + extended: "pairing_extended", deleted: "pairing_deleted", request: "pairing_request", response: "pairing_response", diff --git a/packages/client/src/constants/session.ts b/packages/client/src/constants/session.ts index cc6487d86..483474115 100644 --- a/packages/client/src/constants/session.ts +++ b/packages/client/src/constants/session.ts @@ -8,6 +8,7 @@ export const SESSION_JSONRPC: SessionTypes.JsonRpc = { reject: "wc_sessionReject", update: "wc_sessionUpdate", upgrade: "wc_sessionUpgrade", + extend: "wc_sessionExtend", delete: "wc_sessionDelete", payload: "wc_sessionPayload", ping: "wc_sessionPing", @@ -33,6 +34,7 @@ export const SESSION_EVENTS: SessionTypes.Events = { settled: "session_settled", updated: "session_updated", upgraded: "session_upgraded", + extended: "session_extended", deleted: "session_deleted", request: "session_request", response: "session_response", diff --git a/packages/client/src/controllers/engine.ts b/packages/client/src/controllers/engine.ts index 27ca67407..7f3230440 100644 --- a/packages/client/src/controllers/engine.ts +++ b/packages/client/src/controllers/engine.ts @@ -291,6 +291,23 @@ export class Engine extends IEngine { return settled; } + public async extend(params: SequenceTypes.ExtendParams): Promise { + this.sequence.logger.debug(`Extend ${this.sequence.context}`); + this.sequence.logger.trace({ type: "method", method: "extend", params }); + const settled = await this.sequence.settled.get(params.topic); + const participant: SequenceTypes.Participant = { publicKey: settled.self.publicKey }; + if (params.ttl > (await this.sequence.getDefaultTTL())) { + const error = ERROR.INVALID_EXTEND_REQUEST.format({ context: this.sequence.name }); + this.sequence.logger.error(error.message); + throw new Error(error.message); + } + const extension = { expiry: calcExpiry(params.ttl) }; + const upgrade = await this.handleExtension(params.topic, extension, participant); + const request = formatJsonRpcRequest(this.sequence.config.jsonrpc.upgrade, upgrade); + await this.send(settled.topic, request); + return settled; + } + public async request(params: SequenceTypes.RequestParams): Promise { return new Promise(async (resolve, reject) => { try { @@ -516,6 +533,9 @@ export class Engine extends IEngine { case this.sequence.config.jsonrpc.upgrade: await this.onUpgrade(payloadEvent); break; + case this.sequence.config.jsonrpc.extend: + await this.onExtension(payloadEvent); + break; case this.sequence.config.jsonrpc.notification: await this.onNotification(payloadEvent); break; @@ -600,6 +620,24 @@ export class Engine extends IEngine { } } + public async onExtension(payloadEvent: RelayerTypes.PayloadEvent): Promise { + const { topic, payload } = payloadEvent; + this.sequence.logger.debug(`Receiving ${this.sequence.context} extension`); + this.sequence.logger.trace({ type: "method", method: "onExtension", topic, payload }); + const request = payloadEvent.payload as JsonRpcRequest; + const settled = await this.sequence.settled.get(payloadEvent.topic); + try { + const participant: SequenceTypes.Participant = { publicKey: settled.peer.publicKey }; + await this.handleExtension(topic, request.params, participant); + const response = formatJsonRpcResult(request.id, true); + await this.send(settled.topic, response); + } catch (e) { + this.sequence.logger.error(e as any); + const response = formatJsonRpcError(request.id, (e as any).message); + await this.send(settled.topic, response); + } + } + protected async onNotification(payloadEvent: RelayerTypes.PayloadEvent) { const { params: notification } = payloadEvent.payload as JsonRpcRequest< SessionTypes.Notification @@ -670,6 +708,29 @@ export class Engine extends IEngine { await this.sequence.settled.update(settled.topic, { permissions }); return upgrade; } + + public async handleExtension( + topic: string, + extension: SequenceTypes.Extension, + participant: SequenceTypes.Participant, + ): Promise { + if (typeof extension.expiry === "undefined") { + const error = ERROR.INVALID_EXTEND_REQUEST.format({ context: this.sequence.name }); + this.sequence.logger.error(error.message); + throw new Error(error.message); + } + const settled = await this.sequence.settled.get(topic); + if (participant.publicKey !== settled.permissions.controller.publicKey) { + const error = ERROR.UNAUTHORIZED_EXTEND_REQUEST.format({ + context: this.sequence.name, + }); + this.sequence.logger.error(error.message); + throw new Error(error.message); + } + await this.sequence.mergeExtension(topic, extension); + await this.sequence.settled.update(settled.topic, extension); + return extension; + } // ---------- Private ----------------------------------------------- // private async isJsonRpcAuthorized( @@ -904,6 +965,17 @@ export class Engine extends IEngine { upgrade, }); this.sequence.events.emit(eventName, settled, upgrade); + } else if (typeof update.expiry !== "undefined") { + const eventName = this.sequence.config.events.extended; + const extension = update; + this.sequence.logger.info(`Emitting ${eventName}`); + this.sequence.logger.debug({ + type: "event", + event: eventName, + sequence: settled, + extension, + }); + this.sequence.events.emit(eventName, settled, extension); } }, ); diff --git a/packages/client/src/controllers/pairing.ts b/packages/client/src/controllers/pairing.ts index 55e3ff2cb..010aeb5dc 100644 --- a/packages/client/src/controllers/pairing.ts +++ b/packages/client/src/controllers/pairing.ts @@ -3,7 +3,7 @@ import { Logger } from "pino"; import { generateChildLogger, getLoggerContext } from "@walletconnect/logger"; import { PairingTypes, IClient, IPairing } from "@walletconnect/types"; import { JsonRpcPayload } from "@walletconnect/jsonrpc-utils"; -import { formatUri, mergeArrays } from "@walletconnect/utils"; +import { ERROR, formatUri, mergeArrays } from "@walletconnect/utils"; import { Store } from "./store"; import { Engine } from "./engine"; @@ -88,31 +88,35 @@ export class Pairing extends IPairing { } public create(params?: PairingTypes.CreateParams): Promise { - return this.engine.create(params); + return this.engine.create(params as any) as any; } public respond(params: PairingTypes.RespondParams): Promise { - return this.engine.respond(params); + return this.engine.respond(params as any) as any; + } + + public update(params: PairingTypes.UpdateParams): Promise { + return this.engine.update(params as any) as any; } public upgrade(params: PairingTypes.UpgradeParams): Promise { - return this.engine.upgrade(params); + return this.engine.upgrade(params as any) as any; } - public update(params: PairingTypes.UpdateParams): Promise { - return this.engine.update(params); + public extend(params: PairingTypes.ExtendParams): Promise { + return this.engine.extend(params as any) as any; } public request(params: PairingTypes.RequestParams): Promise { - return this.engine.request(params); + return this.engine.request(params as any) as any; } public delete(params: PairingTypes.DeleteParams): Promise { - return this.engine.delete(params); + return this.engine.delete(params as any) as any; } public notify(params: PairingTypes.NotificationEvent): Promise { - return this.engine.notify(params); + return this.engine.notify(params as any) as any; } public on(event: string, listener: any): void { @@ -159,6 +163,16 @@ export class Pairing extends IPairing { return permissions; } + public async mergeExtension(topic: string, extension: PairingTypes.Extension) { + const settled = await this.settled.get(topic); + if (settled.expiry >= extension.expiry) { + const error = ERROR.INVALID_EXTEND_REQUEST.format({ context: this.name }); + this.logger.error(error.message); + throw new Error(error.message); + } + return extension; + } + public async validateRespond(params?: PairingTypes.RespondParams) { // nothing to validate } diff --git a/packages/client/src/controllers/session.ts b/packages/client/src/controllers/session.ts index 53603f6fe..1bdbd03f3 100644 --- a/packages/client/src/controllers/session.ts +++ b/packages/client/src/controllers/session.ts @@ -1,7 +1,7 @@ import { EventEmitter } from "events"; import { Logger } from "pino"; import { generateChildLogger, getLoggerContext } from "@walletconnect/logger"; -import { IClient, ISession, SessionTypes } from "@walletconnect/types"; +import { IClient, ISession, SequenceTypes, SessionTypes } from "@walletconnect/types"; import { JsonRpcPayload } from "@walletconnect/jsonrpc-utils"; import { validateSessionProposeParams, @@ -100,26 +100,28 @@ export class Session extends ISession { return this.engine.respond(params as any) as any; } + public update(params: SessionTypes.UpdateParams): Promise { + return this.engine.update(params as any) as any; + } + public upgrade(params: SessionTypes.UpgradeParams): Promise { - // TODO: fix type casting as any return this.engine.upgrade(params as any) as any; } - public update(params: SessionTypes.UpdateParams): Promise { - // TODO: fix type casting as any - return this.engine.update(params as any) as any; + public extend(params: SessionTypes.ExtendParams): Promise { + return this.engine.extend(params as any) as any; } public request(params: SessionTypes.RequestParams): Promise { - return this.engine.request(params); + return this.engine.request(params as any) as any; } public delete(params: SessionTypes.DeleteParams): Promise { - return this.engine.delete(params); + return this.engine.delete(params as any) as any; } public notify(params: SessionTypes.NotificationEvent): Promise { - return this.engine.notify(params); + return this.engine.notify(params as any) as any; } public on(event: string, listener: any): void { @@ -172,6 +174,16 @@ export class Session extends ISession { return permissions; } + public async mergeExtension(topic: string, extension: SessionTypes.Extension) { + const settled = await this.settled.get(topic); + if (settled.expiry >= extension.expiry) { + const error = ERROR.INVALID_EXTEND_REQUEST.format({ context: this.name }); + this.logger.error(error.message); + throw new Error(error.message); + } + return extension; + } + public async validateRespond(params?: SessionTypes.RespondParams) { if (typeof params === "undefined") { const error = ERROR.MISSING_OR_INVALID.format({ name: "respond params" }); diff --git a/packages/types/src/client.ts b/packages/types/src/client.ts index 933dd4c38..630d4898f 100644 --- a/packages/types/src/client.ts +++ b/packages/types/src/client.ts @@ -62,10 +62,13 @@ export abstract class IClient extends IEvents { public abstract approve(params: ClientTypes.ApproveParams): Promise; // for responder to reject a session proposal public abstract reject(params: ClientTypes.RejectParams): Promise; - // for responder to upgrade session permissions - public abstract upgrade(params: ClientTypes.UpgradeParams): Promise; - // for responder to update session state + + // for controller to update session state public abstract update(params: ClientTypes.UpdateParams): Promise; + // for controller to upgrade session permissions + public abstract upgrade(params: ClientTypes.UpgradeParams): Promise; + // for controller to extend session expiry + public abstract extend(params: ClientTypes.ExtendParams): Promise; // for proposer to request JSON-RPC public abstract request(params: ClientTypes.RequestParams): Promise; @@ -106,9 +109,11 @@ export declare namespace ClientTypes { reason?: Reason; } + export type UpdateParams = SessionTypes.UpdateParams; + export type UpgradeParams = SessionTypes.UpgradeParams; - export type UpdateParams = SessionTypes.UpdateParams; + export type ExtendParams = SessionTypes.ExtendParams; export type RequestParams = SessionTypes.RequestParams; diff --git a/packages/types/src/engine.ts b/packages/types/src/engine.ts index 9f52d6a13..97e9dffef 100644 --- a/packages/types/src/engine.ts +++ b/packages/types/src/engine.ts @@ -6,13 +6,15 @@ import { ISequence, SequenceTypes } from "./sequence"; export abstract class IEngine< Pending = SequenceTypes.Pending, Settled = SequenceTypes.Settled, - Upgrade = SequenceTypes.Upgrade, Update = SequenceTypes.Update, + Upgrade = SequenceTypes.Upgrade, + Extension = SequenceTypes.Extension, CreateParams = SequenceTypes.CreateParams, RespondParams = SequenceTypes.RespondParams, RequestParams = SequenceTypes.RequestParams, - UpgradeParams = SequenceTypes.UpgradeParams, UpdateParams = SequenceTypes.UpdateParams, + UpgradeParams = SequenceTypes.UpgradeParams, + ExtendParams = SequenceTypes.ExtendParams, DeleteParams = SequenceTypes.DeleteParams, ProposeParams = SequenceTypes.ProposeParams, SettleParams = SequenceTypes.SettleParams, @@ -27,8 +29,9 @@ export abstract class IEngine< public abstract send(topic: string, payload: JsonRpcPayload, chainId?: string): Promise; public abstract create(params?: CreateParams): Promise; public abstract respond(params: RespondParams): Promise; - public abstract upgrade(params: UpgradeParams): Promise; public abstract update(params: UpdateParams): Promise; + public abstract upgrade(params: UpgradeParams): Promise; + public abstract extend(params: ExtendParams): Promise; public abstract request(params: RequestParams): Promise; public abstract delete(params: DeleteParams): Promise; public abstract notify(params: NotifyParams): Promise; @@ -45,12 +48,17 @@ export abstract class IEngine< protected abstract handleUpdate( topic: string, - params: Update, + update: Update, participant: Participant, ): Promise; protected abstract handleUpgrade( topic: string, - params: Upgrade, + upgrade: Upgrade, participant: Participant, ): Promise; + protected abstract handleExtension( + topic: string, + extension: Extension, + participant: Participant, + ): Promise; } diff --git a/packages/types/src/pairing.ts b/packages/types/src/pairing.ts index ad6ceba68..a0d1ecea9 100644 --- a/packages/types/src/pairing.ts +++ b/packages/types/src/pairing.ts @@ -52,15 +52,19 @@ export declare namespace PairingTypes { export type SettleParams = SequenceTypes.SettleParams; + export type UpdateParams = SequenceTypes.UpdateParams; + export type UpgradeParams = SequenceTypes.UpgradeParams; - export type UpdateParams = SequenceTypes.UpdateParams; + export type ExtendParams = SequenceTypes.ExtendParams; export type RequestParams = SequenceTypes.RequestParams; + export type Update = SequenceTypes.Update; + export type Upgrade = SequenceTypes.Upgrade; - export type Update = SequenceTypes.Update; + export type Extension = SequenceTypes.Extension; export type Request = SequenceTypes.Request; @@ -103,13 +107,15 @@ export declare namespace PairingTypes { export type Engine = IEngine< Pending, Settled, - Upgrade, Update, + Upgrade, + Extension, CreateParams, RespondParams, RequestParams, - UpgradeParams, UpdateParams, + UpgradeParams, + ExtendParams, DeleteParams, ProposeParams, SettleParams, @@ -124,15 +130,17 @@ export abstract class IPairing extends ISequence< PairingTypes.Config, PairingTypes.Pending, PairingTypes.Settled, - PairingTypes.Upgrade, PairingTypes.Update, + PairingTypes.Upgrade, + PairingTypes.Extension, PairingTypes.State, PairingTypes.Permissions, PairingTypes.CreateParams, PairingTypes.RespondParams, PairingTypes.RequestParams, - PairingTypes.UpgradeParams, PairingTypes.UpdateParams, + PairingTypes.UpgradeParams, + PairingTypes.ExtendParams, PairingTypes.DeleteParams, PairingTypes.ProposeParams, PairingTypes.SettleParams, diff --git a/packages/types/src/sequence.ts b/packages/types/src/sequence.ts index a05c69fae..10fd335b7 100644 --- a/packages/types/src/sequence.ts +++ b/packages/types/src/sequence.ts @@ -36,6 +36,7 @@ export declare namespace SequenceTypes { settled: string; updated: string; upgraded: string; + extended: string; deleted: string; request: string; response: string; @@ -48,6 +49,7 @@ export declare namespace SequenceTypes { reject: string; update: string; upgrade: string; + extend: string; delete: string; payload: string; ping: string; @@ -152,12 +154,17 @@ export declare namespace SequenceTypes { expiry: number; } + export interface UpdateParams extends Update { + topic: string; + } + export interface UpgradeParams extends Upgrade { topic: string; } - export interface UpdateParams extends Update { + export interface ExtendParams { topic: string; + ttl: number; } export interface RequestParams { @@ -167,12 +174,16 @@ export declare namespace SequenceTypes { chainId?: string; } + export interface Update { + state: Partial; + } + export interface Upgrade { permissions: Partial; } - export interface Update { - state: Partial; + export interface Extension { + expiry: number; } export interface Request { @@ -266,15 +277,17 @@ export abstract class ISequence< Config = SequenceTypes.Config, Pending = SequenceTypes.Pending, Settled = SequenceTypes.Settled, - Upgrade = SequenceTypes.Upgrade, Update = SequenceTypes.Update, + Upgrade = SequenceTypes.Upgrade, + Extension = SequenceTypes.Extension, State = SequenceTypes.State, Permissions = SequenceTypes.Permissions, CreateParams = SequenceTypes.CreateParams, RespondParams = SequenceTypes.RespondParams, RequestParams = SequenceTypes.RequestParams, - UpgradeParams = SequenceTypes.UpgradeParams, UpdateParams = SequenceTypes.UpdateParams, + UpgradeParams = SequenceTypes.UpgradeParams, + ExtendParams = SequenceTypes.ExtendParams, DeleteParams = SequenceTypes.DeleteParams, ProposeParams = SequenceTypes.ProposeParams, SettleParams = SequenceTypes.SettleParams, @@ -337,11 +350,14 @@ export abstract class ISequence< // called by proposer to request JSON-RPC public abstract request(params: RequestParams): Promise; - // called by responder to upgrade permissions - public abstract upgrade(params: UpgradeParams): Promise; - // called by either to update state + // called by controller to update state public abstract update(params: UpdateParams): Promise; + // called by controller to upgrade permissions + public abstract upgrade(params: UpgradeParams): Promise; + // called by controller to extend expiry + public abstract extend(params: ExtendParams): Promise; + // called by either to terminate public abstract delete(params: DeleteParams): Promise; // called by either to notify @@ -350,6 +366,7 @@ export abstract class ISequence< // merge callbacks for sequence engine public abstract mergeUpdate(topic: string, update: Update): Promise; public abstract mergeUpgrade(topic: string, upgrade: Upgrade): Promise; + public abstract mergeExtension(topic: string, extension: Extension): Promise; // validator callbacks for sequence engine public abstract validateRespond(params?: RespondParams): Promise; diff --git a/packages/types/src/session.ts b/packages/types/src/session.ts index 17ac25fea..1345c5237 100644 --- a/packages/types/src/session.ts +++ b/packages/types/src/session.ts @@ -74,21 +74,25 @@ export declare namespace SessionTypes { export type SettleParams = SequenceTypes.SettleParams; - export interface UpgradeParams extends Upgrade { + export interface UpdateParams extends Update { topic: string; } - export interface UpdateParams extends Update { + export interface UpgradeParams extends Upgrade { topic: string; } + export type ExtendParams = SequenceTypes.ExtendParams; + export interface RequestParams extends SequenceTypes.RequestParams { chainId?: string; } + export type Update = SequenceTypes.Update; + export type Upgrade = SequenceTypes.Upgrade; - export type Update = SequenceTypes.Update; + export type Extension = SequenceTypes.Extension; export interface Request extends SequenceTypes.Request { chainId?: string; @@ -142,13 +146,15 @@ export declare namespace SessionTypes { export type Engine = IEngine< Pending, Settled, - Upgrade, Update, + Upgrade, + Extension, CreateParams, RespondParams, RequestParams, - UpgradeParams, UpdateParams, + UpgradeParams, + ExtendParams, DeleteParams, ProposeParams, SettleParams, @@ -163,15 +169,17 @@ export abstract class ISession extends ISequence< SessionTypes.Config, SessionTypes.Pending, SessionTypes.Settled, - SessionTypes.Upgrade, SessionTypes.Update, + SessionTypes.Upgrade, + SessionTypes.Extension, SessionTypes.State, SessionTypes.Permissions, SessionTypes.CreateParams, SessionTypes.RespondParams, SessionTypes.RequestParams, - SessionTypes.UpgradeParams, SessionTypes.UpdateParams, + SessionTypes.UpgradeParams, + SessionTypes.ExtendParams, SessionTypes.DeleteParams, SessionTypes.ProposeParams, SessionTypes.SettleParams, diff --git a/packages/utils/src/error.ts b/packages/utils/src/error.ts index a02f2ea49..049e9187d 100644 --- a/packages/utils/src/error.ts +++ b/packages/utils/src/error.ts @@ -11,6 +11,7 @@ export const ERROR_TYPE = enumify({ MISSING_DECRYPT_PARAMS: "MISSING_DECRYPT_PARAMS", INVALID_UPDATE_REQUEST: "INVALID_UPDATE_REQUEST", INVALID_UPGRADE_REQUEST: "INVALID_UPGRADE_REQUEST", + INVALID_EXTEND_REQUEST: "INVALID_EXTEND_REQUEST", INVALID_STORAGE_KEY_NAME: "INVALID_STORAGE_KEY_NAME", RECORD_ALREADY_EXISTS: "RECORD_ALREADY_EXISTS", RESTORE_WILL_OVERRIDE: "RESTORE_WILL_OVERRIDE", @@ -37,6 +38,7 @@ export const ERROR_TYPE = enumify({ UNAUTHORIZED_NOTIFICATION_TYPE: "UNAUTHORIZED_NOTIFICATION_TYPE", UNAUTHORIZED_UPDATE_REQUEST: "UNAUTHORIZED_UPDATE_REQUEST", UNAUTHORIZED_UPGRADE_REQUEST: "UNAUTHORIZED_UPGRADE_REQUEST", + UNAUTHORIZED_EXTEND_REQUEST: "UNAUTHORIZED_EXTEND_REQUEST", UNAUTHORIZED_MATCHING_CONTROLLER: "UNAUTHORIZED_MATCHING_CONTROLLER", // 4000 (EIP-1193) JSONRPC_REQUEST_METHOD_REJECTED: "JSONRPC_REQUEST_METHOD_REJECTED", @@ -139,9 +141,19 @@ export const ERROR: Record = { message: ERROR[ERROR_TYPE.INVALID_UPGRADE_REQUEST].stringify(params), }), }, + [ERROR_TYPE.INVALID_EXTEND_REQUEST]: { + type: ERROR_TYPE.INVALID_EXTEND_REQUEST, + code: 1005, + stringify: (params?: any) => + `Invalid ${params?.context || defaultParams.context} extend request`, + format: (params?: any) => ({ + code: ERROR[ERROR_TYPE.INVALID_EXTEND_REQUEST].code, + message: ERROR[ERROR_TYPE.INVALID_EXTEND_REQUEST].stringify(params), + }), + }, [ERROR_TYPE.INVALID_STORAGE_KEY_NAME]: { type: ERROR_TYPE.INVALID_STORAGE_KEY_NAME, - code: 1005, + code: 1020, stringify: (params?: any) => `Invalid storage key name: ${params?.name || defaultParams.name}`, format: (params?: any) => ({ code: ERROR[ERROR_TYPE.INVALID_STORAGE_KEY_NAME].code, @@ -378,9 +390,20 @@ export const ERROR: Record = { message: ERROR[ERROR_TYPE.UNAUTHORIZED_UPGRADE_REQUEST].stringify(params), }), }, + [ERROR_TYPE.UNAUTHORIZED_EXTEND_REQUEST]: { + type: ERROR_TYPE.UNAUTHORIZED_EXTEND_REQUEST, + code: 3005, + stringify: (params?: any) => + `Unauthorized ${params?.context || defaultParams.context} extend request`, + format: (params?: any) => ({ + code: ERROR[ERROR_TYPE.UNAUTHORIZED_EXTEND_REQUEST].code, + message: ERROR[ERROR_TYPE.UNAUTHORIZED_EXTEND_REQUEST].stringify(params), + }), + }, + [ERROR_TYPE.UNAUTHORIZED_MATCHING_CONTROLLER]: { type: ERROR_TYPE.UNAUTHORIZED_MATCHING_CONTROLLER, - code: 3005, + code: 3100, stringify: (params?: any) => `Unauthorized: peer is also ${params?.controller ? "" : "not "}controller`, format: (params?: any) => ({ From dd3be34fc9ccff8072e8c9f42dcf802e0b282e50 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Thu, 3 Feb 2022 13:55:40 +0100 Subject: [PATCH 3/4] fix mismatched version --- package-lock.json | 2 +- packages/utils/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index eb5a40b31..45f4d86ab 100644 --- a/package-lock.json +++ b/package-lock.json @@ -34,7 +34,7 @@ "@walletconnect/jsonrpc-utils": "^1.0.0", "@walletconnect/jsonrpc-ws-connection": "^1.0.0", "@walletconnect/logger": "^1.0.0", - "@walletconnect/relay-api": "^1.0.0", + "@walletconnect/relay-api": "^1.0.2", "@walletconnect/safe-json": "^1.0.0", "@walletconnect/timestamp": "^1.0.0", "@walletconnect/window-getters": "^1.0.0", diff --git a/packages/utils/package.json b/packages/utils/package.json index 9bd05b57c..0cec55a18 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -32,7 +32,7 @@ "@walletconnect/encoding": "^1.0.0", "@walletconnect/jsonrpc-utils": "^1.0.0", "@walletconnect/logger": "^1.0.0", - "@walletconnect/relay-api": "^1.0.0", + "@walletconnect/relay-api": "^1.0.2", "@walletconnect/safe-json": "^1.0.0", "@walletconnect/types": "^2.0.0-beta.22", "@walletconnect/window-getters": "^1.0.0", From 3ddd4e0ea6e6a09a74f82248a7fc0b3bfbcfbc9f Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Tue, 8 Feb 2022 14:43:47 +0100 Subject: [PATCH 4/4] write tests and fix rpc method --- packages/client/src/controllers/engine.ts | 8 +-- packages/client/src/controllers/pairing.ts | 2 +- packages/client/src/controllers/session.ts | 2 +- packages/client/test/session.spec.ts | 60 +++++++++++++++++++++- 4 files changed, 65 insertions(+), 7 deletions(-) diff --git a/packages/client/src/controllers/engine.ts b/packages/client/src/controllers/engine.ts index d02646790..8908ad54c 100644 --- a/packages/client/src/controllers/engine.ts +++ b/packages/client/src/controllers/engine.ts @@ -304,9 +304,9 @@ export class Engine extends IEngine { this.sequence.logger.error(error.message); throw new Error(error.message); } - const extension = { expiry: calcExpiry(params.ttl) }; - const upgrade = await this.handleExtension(params.topic, extension, participant); - const request = formatJsonRpcRequest(this.sequence.config.jsonrpc.upgrade, upgrade); + let extension = { expiry: calcExpiry(params.ttl) }; + extension = await this.handleExtension(params.topic, extension, participant); + const request = formatJsonRpcRequest(this.sequence.config.jsonrpc.extend, extension); await this.send(settled.topic, request); return settled; } @@ -730,7 +730,7 @@ export class Engine extends IEngine { this.sequence.logger.error(error.message); throw new Error(error.message); } - await this.sequence.mergeExtension(topic, extension); + extension = await this.sequence.mergeExtension(topic, extension); await this.sequence.settled.update(settled.topic, extension); return extension; } diff --git a/packages/client/src/controllers/pairing.ts b/packages/client/src/controllers/pairing.ts index 010aeb5dc..ad2ce5f09 100644 --- a/packages/client/src/controllers/pairing.ts +++ b/packages/client/src/controllers/pairing.ts @@ -165,7 +165,7 @@ export class Pairing extends IPairing { public async mergeExtension(topic: string, extension: PairingTypes.Extension) { const settled = await this.settled.get(topic); - if (settled.expiry >= extension.expiry) { + if (extension.expiry <= settled.expiry) { const error = ERROR.INVALID_EXTEND_REQUEST.format({ context: this.name }); this.logger.error(error.message); throw new Error(error.message); diff --git a/packages/client/src/controllers/session.ts b/packages/client/src/controllers/session.ts index 1bdbd03f3..d614f5e72 100644 --- a/packages/client/src/controllers/session.ts +++ b/packages/client/src/controllers/session.ts @@ -176,7 +176,7 @@ export class Session extends ISession { public async mergeExtension(topic: string, extension: SessionTypes.Extension) { const settled = await this.settled.get(topic); - if (settled.expiry >= extension.expiry) { + if (extension.expiry <= settled.expiry) { const error = ERROR.INVALID_EXTEND_REQUEST.format({ context: this.name }); this.logger.error(error.message); throw new Error(error.message); diff --git a/packages/client/test/session.spec.ts b/packages/client/test/session.spec.ts index fa1837136..b3fac7ef9 100644 --- a/packages/client/test/session.spec.ts +++ b/packages/client/test/session.spec.ts @@ -15,9 +15,11 @@ import { TEST_TIMEOUT_DURATION, testJsonRpcRequest, TEST_SESSION_TTL, + TEST_TIMEOUT_SAFEGUARD, } from "./shared"; -import { CLIENT_EVENTS } from "../src"; +import { CLIENT_EVENTS, ONE_DAY, SEVEN_DAYS, THIRTY_DAYS } from "../src"; import { ErrorResponse, formatJsonRpcResult } from "@walletconnect/jsonrpc-utils"; +import { delay } from "@walletconnect/timestamp"; describe("Session", function() { it("A proposes session and B approves", async () => { @@ -340,4 +342,60 @@ describe("Session (with timeout)", function() { }); // clock.tick(TEST_TIMEOUT_DURATION); }); + it("B extends expiry and A receives event", async () => { + const ttl = SEVEN_DAYS; + const { setup, clients } = await setupClientsForTesting(); + const topic = await testApproveSession(setup, clients); + const { expiry } = await clients.a.session.get(topic); + await Promise.all([ + new Promise(async (resolve, reject) => { + clients.a.on(CLIENT_EVENTS.session.extended, async (session: SessionTypes.Settled) => { + if (session.expiry <= expiry) { + return reject(new Error(`Upgraded session expiry missing new value: ${expiry}`)); + } + const savedSession = await clients.a.session.get(session.topic); + if (savedSession.expiry <= expiry) { + return reject(new Error(`Saved session expiry missing new value: ${expiry}`)); + } + resolve(); + }); + }), + new Promise(async (resolve, reject) => { + try { + clock.tick(TEST_TIMEOUT_SAFEGUARD); + await clients.b.extend({ topic, ttl }); + resolve(); + } catch (e) { + reject(e); + } + }), + ]); + }); + it("B fails to extend expiry if higher than default ttl", async () => { + const { setup, clients } = await setupClientsForTesting(); + const topic = await testApproveSession(setup, clients); + const ttl = THIRTY_DAYS; + clock.tick(TEST_TIMEOUT_SAFEGUARD); + await expect(clients.b.extend({ topic, ttl })).to.eventually.be.rejectedWith( + `Invalid session extend request`, + ); + }); + it("B fails to extend expiry if smaller than current expiry", async () => { + const { setup, clients } = await setupClientsForTesting(); + const topic = await testApproveSession(setup, clients); + const ttl = ONE_DAY; + clock.tick(TEST_TIMEOUT_SAFEGUARD); + await expect(clients.b.extend({ topic, ttl })).to.eventually.be.rejectedWith( + `Invalid session extend request`, + ); + }); + it("A fails to extend expiry as non-controller", async () => { + const { setup, clients } = await setupClientsForTesting(); + const topic = await testApproveSession(setup, clients); + const ttl = SEVEN_DAYS; + clock.tick(TEST_TIMEOUT_SAFEGUARD); + await expect(clients.a.extend({ topic, ttl })).to.eventually.be.rejectedWith( + `Unauthorized session extend request`, + ); + }); });