Skip to content

Commit

Permalink
feat(common-scripts): support eth displaying for omnilock
Browse files Browse the repository at this point in the history
  • Loading branch information
homura committed Jun 24, 2024
1 parent f2f5666 commit a523cbd
Show file tree
Hide file tree
Showing 5 changed files with 229 additions and 2 deletions.
5 changes: 3 additions & 2 deletions packages/common-scripts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@
"@ckb-lumos/helpers": "0.23.0",
"@ckb-lumos/rpc": "0.23.0",
"@ckb-lumos/toolkit": "0.23.0",
"bs58": "^5.0.0",
"bech32": "^2.0.0",
"bs58": "^5.0.0",
"immutable": "^4.3.0"
},
"repository": {
Expand All @@ -37,7 +37,7 @@
"scripts": {
"fmt": "prettier --write \"{src,tests,examples}/**/*.ts\" package.json",
"lint": "eslint -c ../../.eslintrc.js \"{src,tests,examples}/**/*.ts\"",
"test": "ava **/*.test.ts --timeout=2m",
"test": "ava tests/omnilock-ethereum-displaying.test.ts --timeout=2m",
"build": "pnpm run build:types && pnpm run build:js",
"build:types": "tsc --declaration --emitDeclarationOnly",
"build:js": "babel --root-mode upward src --out-dir lib --extensions .ts -s",
Expand All @@ -57,6 +57,7 @@
"devDependencies": {
"@ckb-lumos/debugger": "0.23.0",
"@ckb-lumos/hd": "0.23.0",
"@ethereumjs/util": "^9.0.3",
"@types/keccak": "^3.0.1",
"@unisat/wallet-sdk": "^1.1.2",
"keccak": "^3.0.1",
Expand Down
51 changes: 51 additions & 0 deletions packages/common-scripts/src/omnilock-ethereum-displaying.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { BytesLike, bytes } from "@ckb-lumos/codec";
import { hexify } from "@ckb-lumos/codec/lib/bytes";

const COMMON_PREFIX = "CKB transaction: 0x";

export interface Provider {
request: {
(payload: {
method: "personal_sign";
params: [string /*from*/, string /*message*/];
}): Promise<string>;
};
}

export async function signMessage(
address: string,
digest: BytesLike,
provider?: Provider
): Promise<string> {
const internal: Provider = (() => {
if (provider) return provider;

/* c8 ignore start */
if (
typeof window !== "undefined" &&
"ethereum" in window &&
window.ethereum
) {
return window.ethereum as Provider;
}

throw new Error(
"No provider found, make sure you have installed MetaMask or the other EIP1193 compatible wallet"
);
/* c8 ignore stop */
})();

const sig = await internal.request({
method: "personal_sign",
params: [address, `${COMMON_PREFIX}${hexify(digest).slice(2)}`],
});

const signature = bytes.bytify(sig);

const [tweakedV] = signature.slice(-1);
// https://eips.ethereum.org/EIPS/eip-155
const PARITY_FLAG = 27;
const v = tweakedV > PARITY_FLAG ? tweakedV - PARITY_FLAG : tweakedV;
signature.set([v], signature.length - 1);
return bytes.hexify(signature);
}
16 changes: 16 additions & 0 deletions packages/common-scripts/src/omnilock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export type OmnilockInfo = {
export type OmnilockAuth =
| IdentityCkb
| IdentityEthereum
| IdentityEthereumDisplaying
| IdentityBitcoin
| IdentitySolana;

Expand All @@ -68,6 +69,11 @@ export type IdentityEthereum = {
content: BytesLike;
};

export type IdentityEthereumDisplaying = {
flag: "ETHEREUM-DISPLAYING";
content: BytesLike;
};

export type IdentityBitcoin = {
flag: "BITCOIN";
/**
Expand Down Expand Up @@ -173,6 +179,14 @@ export function createOmnilockScript(
omnilockArgs
)
);
case "ETHEREUM-DISPLAYING":
return bytes.hexify(
bytes.concat(
[IdentityFlagsType.IdentityFlagsEthereumDisplaying],
omnilockInfo.auth.content,
omnilockArgs
)
);
case "SECP256K1_BLAKE160":
return bytes.hexify(
bytes.concat(
Expand Down Expand Up @@ -408,6 +422,7 @@ export async function setupInputCell(

case IdentityFlagsType.IdentityFlagsCkb:
case IdentityFlagsType.IdentityFlagsEthereum:
case IdentityFlagsType.IdentityFlagsEthereumDisplaying:
case IdentityFlagsType.IdentityFlagsBitcoin: {
return SECP256K1_SIGNATURE_PLACEHOLDER_LENGTH;
}
Expand Down Expand Up @@ -456,6 +471,7 @@ export function prepareSigningEntries(
return _prepareSigningEntries(txSkeleton, config, "OMNILOCK");
}

export * as ethereumDisplaying from "./omnilock-ethereum-displaying";
export { bitcoin };
export { solana };

Expand Down
108 changes: 108 additions & 0 deletions packages/common-scripts/tests/omnilock-ethereum-displaying.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import test from "ava";
import { createOmnilockScript, OmnilockWitnessLock } from "../src/omnilock";
import { Provider, signMessage } from "../src/omnilock-ethereum-displaying";
import { BytesLike, bytes } from "@ckb-lumos/codec";
import { TransactionSkeleton } from "@ckb-lumos/helpers";
import {
CKBDebuggerDownloader,
createTestContext,
getDefaultConfig,
} from "@ckb-lumos/debugger";
import common from "../src/common";
import { mockOutPoint } from "@ckb-lumos/debugger/lib/context";
import { blockchain, utils } from "@ckb-lumos/base";
import {
ecsign,
fromSigned,
hashPersonalMessage,
privateToAddress,
toUnsigned,
} from "@ethereumjs/util";
import { Uint8 } from "@ckb-lumos/codec/lib/number";
import { randomBytes } from "node:crypto";

const context = createTestContext(getDefaultConfig());
const managerConfig = { PREFIX: "ckt", SCRIPTS: context.scriptConfigs };

test.before(async () => {
await new CKBDebuggerDownloader().downloadIfNotExists();
});

test("Omnilock with IdentityEthereumDisplayingFlag", async (t) => {
const privateKey = randomBytes(32);
const provider = makeProvider(privateKey);

const lock = createOmnilockScript(
{
auth: { flag: "ETHEREUM-DISPLAYING", content: provider.selectedAddress },
},
{ config: managerConfig }
);

const txSkeleton = TransactionSkeleton().asMutable();

await common.setupInputCell(
txSkeleton,
{
cellOutput: { lock, capacity: "0x1" },
data: "0x",
outPoint: mockOutPoint(),
},
undefined,
{ config: managerConfig }
);

common.prepareSigningEntries(txSkeleton, { config: managerConfig });

const signedMessage = await signMessage(
provider.selectedAddress,
txSkeleton.get("signingEntries").get(0)!.message,
provider
);

const signedWitness = bytes.hexify(
blockchain.WitnessArgs.pack({
lock: OmnilockWitnessLock.pack({ signature: signedMessage }),
})
);

txSkeleton.update("witnesses", (witnesses) =>
witnesses.set(0, signedWitness)
);

const result = await context.executor.execute(txSkeleton, {
scriptHash: utils.computeScriptHash(lock),
scriptGroupType: "lock",
});

t.is(result.code, 0);
});

function makeProvider(
privateKey: BytesLike
): Provider & { selectedAddress: string } {
const privKey = bytes.bytify(privateKey);
const selectedAddress = bytes.hexify(privateToAddress(privKey));

return {
selectedAddress,
request: async ({ params }) => {
const message = new TextEncoder().encode(params[1]);
const msgHash = hashPersonalMessage(message);
const sig = ecsign(msgHash, privKey);

const serialized = concatSig(Uint8.pack(sig.v), sig.r, sig.s);
return serialized;
},
};
}

function concatSig(v: Uint8Array, r: Uint8Array, s: Uint8Array): string {
const rSig = fromSigned(r);
const sSig = fromSigned(s);

const rStr = toUnsigned(rSig);
const sStr = toUnsigned(sSig);

return bytes.hexify(bytes.concat(rStr, sStr, v));
}
51 changes: 51 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit a523cbd

Please sign in to comment.