Skip to content

chore(immutable-x): Add console.logs for debugging issues during starkex gen #2596

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
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
1 change: 1 addition & 0 deletions packages/internal/toolkit/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"bugs": "https://github.com/immutable/ts-immutable-sdk/issues",
"dependencies": {
"@imtbl/x-client": "workspace:*",
"@imtbl/metrics": "workspace:*",
"@magic-ext/oidc": "12.0.2",
"@metamask/detect-provider": "^2.0.0",
"axios": "^1.6.5",
Expand Down
68 changes: 66 additions & 2 deletions packages/internal/toolkit/src/crypto.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import BN from 'bn.js';
import * as encUtils from 'enc-utils';
import { Signer } from 'ethers';
import { Signer, toUtf8Bytes } from 'ethers';
import { track } from '@imtbl/metrics';

type SignatureOptions = {
r: BN;
Expand Down Expand Up @@ -42,7 +43,70 @@ export async function signRaw(
payload: string,
signer: Signer,
): Promise<string> {
const signature = deserializeSignature(await signer.signMessage(payload));
const address = await signer.getAddress();
track('xProvider', 'log', {
address,
param: 'signRaw.payload',
val: payload,
});
track('xProvider', 'log', {
address,
param: 'signRaw.toUtf8Bytes',
val: toUtf8Bytes(payload).toString(),
});
track('xProvider', 'log', {
address,
param: 'signRaw.payload.normalize() === payload',
val: payload === payload.normalize(),
});

// prevent utf-8 encoding issues
const encoder = new TextEncoder();
const buffer = encoder.encode(payload);
// use this message to sign
const message = new TextDecoder('utf-8').decode(buffer);

const buffer2 = Buffer.from(payload, 'utf8');
const message2 = new TextDecoder('utf-8').decode(buffer2);

// compare message utf8 bytes with payload.normalize()
track('xProvider', 'log', {
address,
param: 'signRaw.message === payload.normalize()',
val: message === payload.normalize(),
});
track('xProvider', 'log', {
address,
param: 'signRaw.message2 === payload.normalize()',
val: message2 === payload.normalize(),
});

// output utf8 bytes
track('xProvider', 'log', {
address,
param: 'signRaw.message',
val: message,
bytes: toUtf8Bytes(message).toString(),
});
track('xProvider', 'log', {
address,
param: 'signRaw.message2',
val: message2,
bytes: toUtf8Bytes(message2).toString(),
});
// compare utf8 bytes output
track('xProvider', 'log', {
address,
param: 'signRaw.toUtf8Bytes === toUtf8Bytes(message)',
val: toUtf8Bytes(payload).toString() === toUtf8Bytes(message).toString(),
});
track('xProvider', 'log', {
address,
param: 'signRaw.toUtf8Bytes === toUtf8Bytes(message2)',
val: toUtf8Bytes(payload).toString() === toUtf8Bytes(message2).toString(),
});

const signature = deserializeSignature(await signer.signMessage(toUtf8Bytes(message)));
return serializeEthSignature(signature);
}

Expand Down
4 changes: 2 additions & 2 deletions packages/passport/sdk/jest.setup.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { TextEncoder } from 'util';
import { TextEncoder, TextDecoder } from 'util';

global.TextEncoder = TextEncoder;

global.TextDecoder = TextDecoder;
/**
* Required for ethers v6
* @see https://github.com/ethers-io/ethers.js/issues/4365
Expand Down
13 changes: 12 additions & 1 deletion packages/x-client/src/utils/crypto/crypto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,18 @@ export async function signRaw(
payload: string,
signer: Signer,
): Promise<string> {
const signature = deserializeSignature(await signer.signMessage(payload));
// prevent utf-8 encoding issues
const encoder = new TextEncoder();
const buffer = encoder.encode(payload);
const message = new TextDecoder('utf-8').decode(buffer);

const buffer2 = Buffer.from(payload, 'utf8');
const message2 = new TextDecoder('utf-8').decode(buffer2);

console.log('signRaw.message', { message });
console.log('signRaw.message2', { message2 });

const signature = deserializeSignature(await signer.signMessage(message));
return serializeEthSignature(signature);
}

Expand Down
13 changes: 12 additions & 1 deletion packages/x-client/src/utils/stark/legacy/crypto/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,21 @@ import BN from 'bn.js';
import elliptic from 'elliptic';
import hashJS from 'hash.js';

import { toUtf8String } from 'ethers';
import { constantPointsHex } from './points';
import { Instruction, InstructionWithFee } from './types';

const DEFAULT_ACCOUNT_MAPPING_KEY = 'STARKWARE_ACCOUNT_MAPPING';
const DEFAULT_SIGNATURE_MESSAGE = 'Only sign this request if you’ve initiated an action with Immutable X.';
// const DEFAULT_SIGNATURE_MESSAGE = 'Only sign this request if you’ve initiated an action with Immutable X.';
// non-english systems may not encode this correctly
const DEFAULT_SIGNATURE_BYTES = new Uint8Array([
79, 110, 108, 121, 32, 115, 105, 103, 110, 32, 116, 104, 105, 115, 32, 114,
101, 113, 117, 101, 115, 116, 32, 105, 102, 32, 121, 111, 117, 226, 128, 153,
118, 101, 32, 105, 110, 105, 116, 105, 97, 116, 101, 100, 32, 97, 110, 32,
97, 99, 116, 105, 111, 110, 32, 119, 105, 116, 104, 32, 73, 109, 109, 117,
116, 97, 98, 108, 101, 32, 88, 46,
]);
const DEFAULT_SIGNATURE_MESSAGE = toUtf8String(DEFAULT_SIGNATURE_BYTES);

const DEFAULT_ACCOUNT_LAYER = 'starkex';
const DEFAULT_ACCOUNT_APPLICATION = 'immutablex';
Expand Down Expand Up @@ -95,6 +105,7 @@ export {
DEFAULT_ACCOUNT_INDEX,
DEFAULT_ACCOUNT_LAYER,
DEFAULT_ACCOUNT_MAPPING_KEY,
DEFAULT_SIGNATURE_BYTES,
DEFAULT_SIGNATURE_MESSAGE,
instructionEncodingMap,
MAX_ECDSA_BN,
Expand Down
4 changes: 2 additions & 2 deletions packages/x-client/src/utils/stark/starkCurve.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import * as encUtils from 'enc-utils';
// eslint-disable-next-line @typescript-eslint/naming-convention
import BN from 'bn.js';
import { hdkey } from '@ethereumjs/wallet';
import { Signature, Signer, toUtf8Bytes } from 'ethers';
import { Signature, Signer } from 'ethers';
import { createStarkSigner } from './starkSigner';
import * as legacy from './legacy/crypto';
import { getStarkPublicKeyFromImx } from './getStarkPublicKeyFromImx';
Expand Down Expand Up @@ -286,7 +286,7 @@ export async function generateLegacyStarkPrivateKey(
signer: Signer,
): Promise<string> {
const address = (await signer.getAddress()).toLowerCase();
const signature = await signer.signMessage(toUtf8Bytes(legacy.DEFAULT_SIGNATURE_MESSAGE));
const signature = await signer.signMessage(legacy.DEFAULT_SIGNATURE_BYTES);
const seed = Signature.from(signature).s;
const path = legacy.getAccountPath(
legacy.DEFAULT_ACCOUNT_LAYER,
Expand Down
1 change: 1 addition & 0 deletions packages/x-provider/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"@imtbl/generated-clients": "workspace:*",
"@imtbl/toolkit": "workspace:*",
"@imtbl/x-client": "workspace:*",
"@imtbl/metrics": "workspace:*",
"@magic-ext/oidc": "12.0.2",
"@metamask/detect-provider": "^2.0.0",
"axios": "^1.6.5",
Expand Down
87 changes: 84 additions & 3 deletions packages/x-provider/src/imx-wallet/imxWallet.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { Environment } from '@imtbl/config';
import { BrowserProvider, toUtf8Bytes } from 'ethers';
import {
BrowserProvider,
toUtf8Bytes,
toUtf8String,
} from 'ethers';
import { track } from '@imtbl/metrics';
import {
ConnectRequest,
ConnectResponse,
Expand All @@ -16,16 +21,92 @@ import { messageResponseListener } from './messageResponseListener';
import { ImxSigner } from './ImxSigner';
import { getOrSetupIFrame } from './imxWalletIFrame';

const DEFAULT_CONNECTION_MESSAGE = 'Only sign this request if you’ve initiated an action with Immutable X.';
// "Only sign this request if you've initiated an action with Immutable X."
const DEFAULT_CONNECTION_BYTES = new Uint8Array([
79, 110, 108, 121, 32, 115, 105, 103, 110, 32, 116, 104, 105, 115, 32, 114,
101, 113, 117, 101, 115, 116, 32, 105, 102, 32, 121, 111, 117, 226, 128, 153,
118, 101, 32, 105, 110, 105, 116, 105, 97, 116, 101, 100, 32, 97, 110, 32,
97, 99, 116, 105, 111, 110, 32, 119, 105, 116, 104, 32, 73, 109, 109, 117,
116, 97, 98, 108, 101, 32, 88, 46,
]);
const DEFAULT_CONNECTION_STRING_1 = 'Only sign this request if you’ve initiated an action with Immutable X.';
const DEFAULT_CONNECTION_STRING_2 = Buffer.from(DEFAULT_CONNECTION_STRING_1, 'utf8').toString('utf8');
const CONNECTION_FAILED_ERROR = 'The L2 IMX Wallet connection has failed';

function trackConnectionDetails(address: string) {
// track language and charset
track('xProvider', 'log', { address, param: 'navigator.language', val: navigator?.language });
track('xProvider', 'log', { address, param: 'navigator.languages', val: navigator?.languages?.join(',') });
track('xProvider', 'log', { address, param: 'document.characterSet', val: document?.characterSet });

// track connection encoding details
track('xProvider', 'log', {
address,
param: 'DEFAULT_CONNECTION_STRING_2',
val: DEFAULT_CONNECTION_STRING_2,
});
track('xProvider', 'log', {
address,
param: 'DEFAULT_CONNECTION_BYTES',
val: DEFAULT_CONNECTION_BYTES.toString(),
});
track('xProvider', 'log', {
address,
param: 'DEFAULT_CONNECTION_STRING_1',
val: toUtf8Bytes(DEFAULT_CONNECTION_STRING_1).toString(),
});
track('xProvider', 'log', {
address,
param: 'DEFAULT_CONNECTION_STRING_2',
val: toUtf8Bytes(DEFAULT_CONNECTION_STRING_2).toString(),
});
track('xProvider', 'log', {
address,
param: 'DEFAULT_CONNECTION_STRING_1.normalize()',
val: toUtf8Bytes(DEFAULT_CONNECTION_STRING_1.normalize()).toString(),
});
track('xProvider', 'log', {
address,
param: 'DEFAULT_CONNECTION_STRING_2.normalize()',
val: toUtf8Bytes(DEFAULT_CONNECTION_STRING_2.normalize()).toString(),
});
track('xProvider', 'log', {
address,
param: 'Buffer.from(DEFAULT_CONNECTION_STRING_1, utf8).toString()',
val: Buffer.from(DEFAULT_CONNECTION_STRING_1, 'utf8').toString(),
});
track('xProvider', 'log', {
address,
param: 'DEFAULT_CONNECTION_BYTES === toUtf8Bytes(DEFAULT_CONNECTION_STRING_1)',
val: DEFAULT_CONNECTION_BYTES.toString() === toUtf8Bytes(DEFAULT_CONNECTION_STRING_1).toString(),
});
track('xProvider', 'log', {
address,
param: 'DEFAULT_CONNECTION_BYTES === toUtf8Bytes(DEFAULT_CONNECTION_STRING_2)',
val: DEFAULT_CONNECTION_BYTES.toString() === toUtf8Bytes(DEFAULT_CONNECTION_STRING_2).toString(),
});
track('xProvider', 'log', {
address,
param: 'DEFAULT_CONNECTION_BYTES',
val: DEFAULT_CONNECTION_BYTES.toString(),
});
track('xProvider', 'log', {
address,
param: 'DEFAULT_CONNECTION_BYTES.toUtf8String()',
val: toUtf8String(DEFAULT_CONNECTION_BYTES),
});
}

export async function connect(
l1Provider: BrowserProvider,
env: Environment,
): Promise<ImxSigner> {
const l1Signer = await l1Provider.getSigner();
const address = await l1Signer.getAddress();
const signature = await l1Signer.signMessage(toUtf8Bytes(DEFAULT_CONNECTION_MESSAGE));

trackConnectionDetails(address);

const signature = await l1Signer.signMessage(DEFAULT_CONNECTION_BYTES);
const iframe = await getOrSetupIFrame(env);

return new Promise((resolve, reject) => {
Expand Down
Loading
Loading