From 51c37558ee7654de30e7804d36942a0977c30acc Mon Sep 17 00:00:00 2001 From: Michael Standen Date: Wed, 30 Apr 2025 07:32:55 +1200 Subject: [PATCH] Add allowMessages flag to permissions --- .../core/src/signers/session/explicit.ts | 4 ++-- .../wallet/core/test/session-manager.test.ts | 3 +++ .../src/subcommands/devTools.ts | 6 ++++-- packages/wallet/primitives/src/permission.ts | 19 ++++++++++++++----- .../wallet/primitives/src/session-config.ts | 14 +++++++------- 5 files changed, 30 insertions(+), 16 deletions(-) diff --git a/packages/wallet/core/src/signers/session/explicit.ts b/packages/wallet/core/src/signers/session/explicit.ts index 546594734..4c905432c 100644 --- a/packages/wallet/core/src/signers/session/explicit.ts +++ b/packages/wallet/core/src/signers/session/explicit.ts @@ -1,7 +1,7 @@ import { Payload, Permission, SessionSignature, Utils } from '@0xsequence/wallet-primitives' -import { AbiParameters, Address, Bytes, Hash, Hex, Provider, Secp256k1 } from 'ox' -import { SignerInterface } from './session.js' +import { AbiParameters, Address, Bytes, Hash, Hex, Provider } from 'ox' import { MemoryPkStore, PkStore } from '../pk/index.js' +import { SignerInterface } from './session.js' export type ExplicitParams = Omit diff --git a/packages/wallet/core/test/session-manager.test.ts b/packages/wallet/core/test/session-manager.test.ts index 465ca0ed9..0208fb62d 100644 --- a/packages/wallet/core/test/session-manager.test.ts +++ b/packages/wallet/core/test/session-manager.test.ts @@ -49,6 +49,7 @@ describe('SessionManager', () => { let topology = SessionConfig.emptySessionsTopology(identityAddress) // Add random signer to the topology const sessionPermission: Signers.Session.ExplicitParams = { + allowMessages: true, valueLimit: 1000000000000000000n, deadline: BigInt(Math.floor(Date.now() / 1000) + 3600), // 1 hour from now permissions: [ @@ -213,6 +214,7 @@ describe('SessionManager', () => { // Create explicit signer const explicitPrivateKey = Secp256k1.randomPrivateKey() const explicitPermissions: Signers.Session.ExplicitParams = { + allowMessages: true, valueLimit: 1000000000000000000n, // 1 ETH deadline: BigInt(Math.floor(Date.now() / 1000) + 3600), // 1 hour from now permissions: [ @@ -446,6 +448,7 @@ describe('SessionManager', () => { // Create explicit signer const explicitPrivateKey = Secp256k1.randomPrivateKey() const sessionPermission: Signers.Session.ExplicitParams = { + allowMessages: true, valueLimit: 1000000000000000000n, // 1 ETH deadline: BigInt(Math.floor(Date.now() / 1000) + 3600), // 1 hour from now permissions: [{ target: EMITTER_ADDRESS, rules: [] }], diff --git a/packages/wallet/primitives-cli/src/subcommands/devTools.ts b/packages/wallet/primitives-cli/src/subcommands/devTools.ts index 6dfd7b149..6769299a0 100644 --- a/packages/wallet/primitives-cli/src/subcommands/devTools.ts +++ b/packages/wallet/primitives-cli/src/subcommands/devTools.ts @@ -105,16 +105,18 @@ async function generateSessionsTopology( depth: number, options?: RandomOptions, ): Promise { - const isLeaf = (options?.seededRandom ?? Math.random)() * 2 > 1 + const rand = options?.seededRandom ?? Math.random + const isLeaf = rand() * 2 > 1 if (isLeaf || depth <= 1) { - const permissionsCount = Math.floor((options?.seededRandom ?? Math.random)() * (options?.maxPermissions ?? 5)) + 1 + const permissionsCount = Math.floor(rand() * (options?.maxPermissions ?? 5)) + 1 const permissions = await Promise.all( Array.from({ length: permissionsCount }, () => generateRandomPermission(options)), ) return { type: 'session-permissions', signer: randomAddress(options), + allowMessages: rand() * 2 > 1, valueLimit: randomBigInt(100n, options), deadline: randomBigInt(1000n, options), permissions: permissions as [Permission.Permission, ...Permission.Permission[]], diff --git a/packages/wallet/primitives/src/permission.ts b/packages/wallet/primitives/src/permission.ts index 03f6497ec..f9b2edb0f 100644 --- a/packages/wallet/primitives/src/permission.ts +++ b/packages/wallet/primitives/src/permission.ts @@ -1,4 +1,5 @@ import { AbiParameters, Address, Bytes } from 'ox' +import { SESSIONS_FLAG_PERMISSIONS } from './session-config.js' export enum ParameterOperation { EQUAL = 0, @@ -22,6 +23,7 @@ export type Permission = { export type SessionPermissions = { signer: Address.Address + allowMessages: boolean valueLimit: bigint deadline: bigint permissions: [Permission, ...Permission[]] @@ -39,7 +41,10 @@ export function encodeSessionPermissions(sessionPermissions: SessionPermissions) const encodedPermissions = sessionPermissions.permissions.map(encodePermission) + const flag = (SESSIONS_FLAG_PERMISSIONS << 4) | (sessionPermissions.allowMessages ? 1 : 0) + return Bytes.concat( + Bytes.fromNumber(flag, { size: 1 }), Bytes.padLeft(Bytes.fromHex(sessionPermissions.signer), 20), Bytes.padLeft(Bytes.fromNumber(sessionPermissions.valueLimit), 32), Bytes.padLeft(Bytes.fromNumber(sessionPermissions.deadline), 32), @@ -77,12 +82,13 @@ function encodeParameterRule(rule: ParameterRule): Bytes.Bytes { // Decoding export function decodeSessionPermissions(bytes: Bytes.Bytes): SessionPermissions { - const signer = Bytes.toHex(bytes.slice(0, 20)) - const valueLimit = Bytes.toBigInt(bytes.slice(20, 52)) - const deadline = Bytes.toBigInt(bytes.slice(52, 84)) - const permissionsLength = Number(bytes[84]!) + const allowMessages = (bytes[0]! & 0x01) === 1 + const signer = Bytes.toHex(bytes.slice(1, 21)) + const valueLimit = Bytes.toBigInt(bytes.slice(21, 53)) + const deadline = Bytes.toBigInt(bytes.slice(53, 85)) + const permissionsLength = Number(bytes[85]!) const permissions = [] - let pointer = 85 + let pointer = 86 for (let i = 0; i < permissionsLength; i++) { // Pass the remaining bytes instead of a fixed slice length const { permission, consumed } = decodePermission(bytes.slice(pointer)) @@ -94,6 +100,7 @@ export function decodeSessionPermissions(bytes: Bytes.Bytes): SessionPermissions } return { signer, + allowMessages, valueLimit, deadline, permissions: permissions as [Permission, ...Permission[]], @@ -190,6 +197,7 @@ export function sessionPermissionsToJson(sessionPermissions: SessionPermissions) export function encodeSessionPermissionsForJson(sessionPermissions: SessionPermissions): any { return { signer: sessionPermissions.signer.toString(), + allowMessages: sessionPermissions.allowMessages, valueLimit: sessionPermissions.valueLimit.toString(), deadline: sessionPermissions.deadline.toString(), permissions: sessionPermissions.permissions.map(encodePermissionForJson), @@ -228,6 +236,7 @@ export function sessionPermissionsFromJson(json: string): SessionPermissions { export function sessionPermissionsFromParsed(parsed: any): SessionPermissions { return { signer: Address.from(parsed.signer), + allowMessages: parsed.allowMessages, valueLimit: BigInt(parsed.valueLimit), deadline: BigInt(parsed.deadline), permissions: parsed.permissions.map(permissionFromParsed), diff --git a/packages/wallet/primitives/src/session-config.ts b/packages/wallet/primitives/src/session-config.ts index 679bbe621..0002e0dc4 100644 --- a/packages/wallet/primitives/src/session-config.ts +++ b/packages/wallet/primitives/src/session-config.ts @@ -211,14 +211,14 @@ export function encodeLeafToGeneric(leaf: SessionLeaf): GenericTree.Leaf { if (isSessionPermissions(leaf)) { return { type: 'leaf', - value: Bytes.concat(Bytes.fromNumber(SESSIONS_FLAG_PERMISSIONS), encodeSessionPermissions(leaf)), + value: encodeSessionPermissions(leaf), } } if (isImplicitBlacklist(leaf)) { return { type: 'leaf', value: Bytes.concat( - Bytes.fromNumber(SESSIONS_FLAG_BLACKLIST), + Bytes.fromNumber(SESSIONS_FLAG_BLACKLIST << 4), Bytes.concat(...leaf.blacklist.map((b) => Bytes.padLeft(Bytes.fromHex(b), 20))), ), } @@ -227,7 +227,7 @@ export function encodeLeafToGeneric(leaf: SessionLeaf): GenericTree.Leaf { return { type: 'leaf', value: Bytes.concat( - Bytes.fromNumber(SESSIONS_FLAG_IDENTITY_SIGNER), + Bytes.fromNumber(SESSIONS_FLAG_IDENTITY_SIGNER << 4), Bytes.padLeft(Bytes.fromHex(leaf.identitySigner), 20), ), } @@ -237,7 +237,7 @@ export function encodeLeafToGeneric(leaf: SessionLeaf): GenericTree.Leaf { } export function decodeLeafFromBytes(bytes: Bytes.Bytes): SessionLeaf { - const flag = bytes[0]! + const flag = (bytes[0]! & 0xf0) >> 4 if (flag === SESSIONS_FLAG_BLACKLIST) { const blacklist: `0x${string}`[] = [] for (let i = 1; i < bytes.length; i += 20) { @@ -249,7 +249,7 @@ export function decodeLeafFromBytes(bytes: Bytes.Bytes): SessionLeaf { return { type: 'identity-signer', identitySigner: Bytes.toHex(bytes.slice(1, 21)) } } if (flag === SESSIONS_FLAG_PERMISSIONS) { - return { type: 'session-permissions', ...decodeSessionPermissions(bytes.slice(1)) } + return { type: 'session-permissions', ...decodeSessionPermissions(bytes) } } throw new Error('Invalid leaf') } @@ -307,9 +307,9 @@ export function encodeSessionsTopology(topology: SessionsTopology): Bytes.Bytes } if (isSessionPermissions(topology)) { - const flagByte = SESSIONS_FLAG_PERMISSIONS << 4 + // Encoding includes the flag const encodedLeaf = encodeSessionPermissions(topology) - return Bytes.concat(Bytes.fromNumber(flagByte), encodedLeaf) + return Bytes.concat(encodedLeaf) } if (isSessionsNode(topology)) {