Skip to content

Commit

Permalink
feat: sol sign off chain message OK-34534 OK-34533 (#6448)
Browse files Browse the repository at this point in the history
* feat: sol sign off chain message

* chore: fix lint

* chore: fix lint
  • Loading branch information
ByteZhang1024 authored Jan 7, 2025
1 parent 664dbc3 commit 9dc08d6
Show file tree
Hide file tree
Showing 11 changed files with 588 additions and 321 deletions.
22 changes: 11 additions & 11 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,17 +56,17 @@
"@glif/filecoin-rpc-client": "3.0.2",
"@mysten/sui": "1.13.0",
"@ngraveio/bc-ur": "^1.1.13",
"@onekeyfe/cross-inpage-provider-core": "2.2.7-alpha.0",
"@onekeyfe/cross-inpage-provider-errors": "2.2.7-alpha.0",
"@onekeyfe/cross-inpage-provider-injected": "2.2.7-alpha.0",
"@onekeyfe/cross-inpage-provider-types": "2.2.7-alpha.0",
"@onekeyfe/extension-bridge-hosted": "2.2.7-alpha.0",
"@onekeyfe/hd-ble-sdk": "1.0.17",
"@onekeyfe/hd-core": "1.0.17",
"@onekeyfe/hd-shared": "1.0.17",
"@onekeyfe/hd-transport": "1.0.17",
"@onekeyfe/hd-web-sdk": "1.0.17",
"@onekeyfe/onekey-cross-webview": "2.2.7-alpha.0",
"@onekeyfe/cross-inpage-provider-core": "2.2.7-alpha.2",
"@onekeyfe/cross-inpage-provider-errors": "2.2.7-alpha.2",
"@onekeyfe/cross-inpage-provider-injected": "2.2.7-alpha.2",
"@onekeyfe/cross-inpage-provider-types": "2.2.7-alpha.2",
"@onekeyfe/extension-bridge-hosted": "2.2.7-alpha.2",
"@onekeyfe/hd-ble-sdk": "1.0.18",
"@onekeyfe/hd-core": "1.0.18",
"@onekeyfe/hd-shared": "1.0.18",
"@onekeyfe/hd-transport": "1.0.18",
"@onekeyfe/hd-web-sdk": "1.0.18",
"@onekeyfe/onekey-cross-webview": "2.2.7-alpha.2",
"@polkadot/extension-inject": "0.46.6",
"@polkadot/types": "11.3.1",
"@polkadot/util-crypto": "12.6.2",
Expand Down
22 changes: 21 additions & 1 deletion packages/core/src/chains/sol/CoreChainSoftware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ import bs58 from 'bs58';

import { checkIsDefined } from '@onekeyhq/shared/src/utils/assertUtils';
import bufferUtils from '@onekeyhq/shared/src/utils/bufferUtils';
import {
EMessageTypesCommon,
EMessageTypesSolana,
} from '@onekeyhq/shared/types/message';

import { CoreChainApiBase } from '../../base/CoreChainApiBase';
import { decryptAsync } from '../../secret';
Expand All @@ -22,6 +26,8 @@ import {
type ISignedTxPro,
} from '../../types';

import { OffchainMessage } from './sdkSol/OffchainMessage';

import type { IEncodedTxSol, INativeTxSol } from './types';
import type { ISigner } from '../../base/ChainSigner';

Expand Down Expand Up @@ -152,7 +158,21 @@ export default class CoreChainSoftware extends CoreChainApiBase {
payload,
curve,
});
return signMessage(unsignedMsg.message, signer);

if (unsignedMsg.type === EMessageTypesCommon.SIGN_MESSAGE) {
return signMessage(unsignedMsg.message, signer);
}
if (unsignedMsg.type === EMessageTypesSolana.SIGN_OFFCHAIN_MESSAGE) {
const { message, payload: messagePayload } = unsignedMsg;
const offchainMessage = new OffchainMessage({
version: messagePayload?.version,
message: Buffer.from(message),
});
const [signature] = await signer.sign(offchainMessage.serialize());
return bs58.encode(signature);
}

throw new Error('signMessage not supported');
}

override async getAddressFromPrivate(
Expand Down
173 changes: 173 additions & 0 deletions packages/core/src/chains/sol/sdkSol/OffchainMessage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
/* eslint-disable @typescript-eslint/no-unnecessary-type-assertion */
/* eslint-disable no-bitwise */
/* eslint-disable spellcheck/spell-checker */

// Max off-chain message length supported by Ledger
const OFFCM_MAX_LEDGER_LEN = 1212;
// Max length of version 0 off-chain message
const OFFCM_MAX_V0_LEN = 65_515;

function isValidUTF8(data: Uint8Array): boolean {
const length = data.length;
let i = 0;

while (i < length) {
if (data[i] < 0x80) {
/* 0xxxxxxx */
// eslint-disable-next-line no-plusplus
++i;
} else if ((data[i] & 0xe0) === 0xc0) {
/* 110XXXXx 10xxxxxx */
if (
i + 1 >= length ||
(data[i + 1] & 0xc0) !== 0x80 ||
(data[i] & 0xfe) === 0xc0
) {
/* overlong? */ return false;
}
i += 2;
} else if ((data[i] & 0xf0) === 0xe0) {
/* 1110XXXX 10Xxxxxx 10xxxxxx */
if (
i + 2 >= length ||
(data[i + 1] & 0xc0) !== 0x80 ||
(data[i + 2] & 0xc0) !== 0x80 ||
(data[i] === 0xe0 && (data[i + 1] & 0xe0) === 0x80) /* overlong? */ ||
(data[i] === 0xed && (data[i + 1] & 0xe0) === 0xa0) /* surrogate? */ ||
(data[i] === 0xef &&
data[i + 1] === 0xbf &&
(data[i + 2] & 0xfe) === 0xbe)
) {
/* U+FFFE or U+FFFF? */ return false;
}
i += 3;
} else if ((data[i] & 0xf8) === 0xf0) {
/* 11110XXX 10XXxxxx 10xxxxxx 10xxxxxx */
if (
i + 3 >= length ||
(data[i + 1] & 0xc0) !== 0x80 ||
(data[i + 2] & 0xc0) !== 0x80 ||
(data[i + 3] & 0xc0) !== 0x80 ||
(data[i] === 0xf0 && (data[i + 1] & 0xf0) === 0x80) /* overlong? */ ||
(data[i] === 0xf4 && data[i + 1] > 0x8f) ||
data[i] > 0xf4
) {
/* > U+10FFFF? */ return false;
}
i += 4;
} else {
return false;
}
}
return true;
}

export class OffchainMessage {
version: number;

messageFormat: number | undefined;

message: Buffer | undefined;

/**
* Constructs a new OffchainMessage
* @param {version: number, messageFormat: number, message: string | Buffer} opts - Constructor parameters
*/
constructor(opts: {
version?: number;
messageFormat?: number;
message: Buffer;
}) {
this.version = 0;
this.messageFormat = undefined;
this.message = undefined;

if (!opts) {
return;
}
if (opts.version) {
this.version = opts.version;
}
if (opts.messageFormat) {
this.messageFormat = opts.messageFormat;
}
if (opts.message) {
this.message = Buffer.from(opts.message);
if (this.version === 0) {
if (!this.messageFormat) {
this.messageFormat = OffchainMessage.guessMessageFormat(this.message);
}
}
}
}

static guessMessageFormat(message: Buffer) {
if (Object.prototype.toString.call(message) !== '[object Uint8Array]') {
return undefined;
}
if (message.length <= OFFCM_MAX_LEDGER_LEN) {
if (OffchainMessage.isPrintableASCII(message)) {
return 0;
}
if (OffchainMessage.isUTF8(message)) {
return 1;
}
} else if (message.length <= OFFCM_MAX_V0_LEN) {
if (OffchainMessage.isUTF8(message)) {
return 2;
}
}
return undefined;
}

static isPrintableASCII(buffer: Buffer) {
return (
buffer && buffer.every((element) => element >= 0x20 && element <= 0x7e)
);
}

static isUTF8(buffer: Buffer) {
return buffer && isValidUTF8(buffer);
}

isValid() {
if (this.version !== 0) {
return false;
}
if (!this.message) {
return false;
}
const format = OffchainMessage.guessMessageFormat(this.message);
return format != null && format === this.messageFormat;
}

isLedgerSupported(allowBlindSigning: boolean) {
return (
this.isValid() &&
(this.messageFormat === 0 ||
(this.messageFormat === 1 && allowBlindSigning))
);
}

serialize() {
if (!this.isValid()) {
throw new Error(`Invalid OffchainMessage: ${JSON.stringify(this)}`);
}
const buffer = Buffer.alloc(4);
if (!this.message) {
throw new Error('message is null');
}
if (this.messageFormat === undefined) {
throw new Error('messageFormat is null');
}
let offset = buffer.writeUInt8(this.version);
offset = buffer.writeUInt8(this.messageFormat, offset);
offset = buffer.writeUInt16LE(this.message.length, offset);
return Buffer.concat([
Buffer.from([255]),
Buffer.from('solana offchain'),
buffer,
this.message,
]);
}
}
12 changes: 11 additions & 1 deletion packages/core/src/types/coreTypesMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import type {
EMessageTypesBtc,
EMessageTypesCommon,
EMessageTypesEth,
EMessageTypesSolana,
EMessageTypesTon,
} from '@onekeyhq/shared/types/message';

Expand Down Expand Up @@ -72,6 +73,14 @@ export type IUnsignedMessageAlph = {
payload?: any;
};

export type IUnsignedMessageSolana = {
type: EMessageTypesSolana;
message: string;
payload?: {
version?: number;
};
};

export type IUnsignedMessage =
| IUnsignedMessageCommon
| IUnsignedMessageEth
Expand All @@ -80,4 +89,5 @@ export type IUnsignedMessage =
| IUnsignedMessageTon
| IUnsignedMessageAda
| IUnsignedMessageAlph
| IUnsignedMessageCfx;
| IUnsignedMessageCfx
| IUnsignedMessageSolana;
22 changes: 22 additions & 0 deletions packages/kit-bg/src/providers/ProviderApiCosmos.ts
Original file line number Diff line number Diff line change
Expand Up @@ -606,6 +606,28 @@ class ProviderApiCosmos extends ProviderApiBase {
};
});
}

@providerApiMethod()
public async getChainInfoWithoutEndpoints(
request: IJsBridgeMessagePayload,
params: string,
) {
const { networks } =
await this.backgroundApi.serviceNetwork.getNetworksByImpls({
impls: ['cosmos'],
});

const network = networks.find((n) => n.chainId === params);
if (!network) throw new Error(`OneKey does not support ${params}`);

return {
chainId: network.chainId,
chainName: network.name,
bip44: { coinType: parseInt(COINTYPE_COSMOS, 10) },
currencies: [],
feeCurrencies: [],
};
}
}

export default ProviderApiCosmos;
40 changes: 39 additions & 1 deletion packages/kit-bg/src/providers/ProviderApiSolana.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ import {
providerApiMethod,
} from '@onekeyhq/shared/src/background/backgroundDecorators';
import { defaultLogger } from '@onekeyhq/shared/src/logger/logger';
import { EMessageTypesCommon } from '@onekeyhq/shared/types/message';
import {
EMessageTypesCommon,
EMessageTypesSolana,
} from '@onekeyhq/shared/types/message';

import ProviderApiBase from './ProviderApiBase';

Expand Down Expand Up @@ -222,6 +225,41 @@ class ProviderApiSolana extends ProviderApiBase {
return { signature, publicKey: address ?? '' };
}

@providerApiMethod()
public async solSignOffchainMessage(
request: IJsBridgeMessagePayload,
params: {
message: string;
version?: number;
},
) {
defaultLogger.discovery.dapp.dappRequest({ request });
const { message, version } = params;

const { accountInfo: { accountId, networkId, address } = {} } = (
await this.getAccountsInfo(request)
)[0];

console.log('solana signOffchainMessage', request, params);

const signature = await this.backgroundApi.serviceDApp.openSignMessageModal(
{
request,
unsignedMessage: {
type: EMessageTypesSolana.SIGN_OFFCHAIN_MESSAGE,
message: bs58.decode(message).toString(),
payload: {
version: version ?? 0,
},
},
networkId: networkId ?? '',
accountId: accountId ?? '',
},
);

return { signature, publicKey: address ?? '' };
}

@providerApiMethod()
public async connect(
request: IJsBridgeMessagePayload,
Expand Down
Loading

0 comments on commit 9dc08d6

Please sign in to comment.