Skip to content

feat: WASM executor without COI #201

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 1 commit into
base: master
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
15 changes: 13 additions & 2 deletions packages/core/src/signer/btc/verify.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
import { secp256k1 } from "@noble/curves/secp256k1";
import { ripemd160 } from "@noble/hashes/ripemd160";
import { sha256 } from "@noble/hashes/sha256";
import { magicHash } from "bitcoinjs-message";
import bs58check from "bs58check";
import { Bytes, BytesLike, bytesConcat, bytesFrom } from "../../bytes/index.js";
import { Hex, hexFrom } from "../../hex/index.js";

let magicHash: ((message: string, messagePrefix: string) => Uint8Array) | null =
null;

if (typeof window !== "undefined") {
void import("bitcoinjs-message").then((mod) => {
magicHash = mod.magicHash;
});
}
/**
* @public
*/
Expand Down Expand Up @@ -45,5 +52,9 @@ export function verifyMessageBtcEcdsa(

const rawSign = bytesFrom(signature, "base64").slice(1);

return secp256k1.verify(bytesFrom(rawSign), magicHash(challenge), publicKey);
return secp256k1.verify(
bytesFrom(rawSign),
magicHash!(challenge, "\x19Bitcoin Signed Message:\n"),
publicKey,
);
}
13 changes: 11 additions & 2 deletions packages/core/src/signer/doge/signerDogePrivateKey.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"use client";

import { secp256k1 } from "@noble/curves/secp256k1";
import { magicHash } from "bitcoinjs-message";
import {
Bytes,
bytesConcat,
Expand All @@ -12,6 +13,14 @@ import { Hex, hexFrom } from "../../hex/index.js";
import { btcP2pkhAddressFromPublicKey } from "../btc/verify.js";
import { SignerDoge } from "./signerDoge.js";

let magicHash: ((message: string, messagePrefix: string) => Uint8Array) | null =
null;

if (typeof window !== "undefined") {
void import("bitcoinjs-message").then((mod) => {
magicHash = mod.magicHash;
});
}
/**
* A class extending SignerDoge that provides access to a Doge address.
* @public
Expand Down Expand Up @@ -85,7 +94,7 @@ export class SignerDogePrivateKey extends SignerDoge {
const challenge = typeof msg === "string" ? msg : hexFrom(msg).slice(2);

const signature = secp256k1.sign(
magicHash(challenge, "\x19Dogecoin Signed Message:\n"),
magicHash!(challenge, "\x19Dogecoin Signed Message:\n"),
this.privateKey,
);

Expand Down
12 changes: 10 additions & 2 deletions packages/core/src/signer/doge/verify.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
import { secp256k1 } from "@noble/curves/secp256k1";
import { magicHash } from "bitcoinjs-message";
import { bytesFrom, BytesLike } from "../../bytes/index.js";
import { hexFrom } from "../../hex/index.js";
import {
btcEcdsaPublicKeyHash,
btcPublicKeyFromP2pkhAddress,
} from "../btc/verify.js";

let magicHash: ((message: string, messagePrefix: string) => Uint8Array) | null =
null;

if (typeof window !== "undefined") {
void import("bitcoinjs-message").then((mod) => {
magicHash = mod.magicHash;
});
}

/**
* @public
*/
Expand All @@ -31,7 +39,7 @@ export function verifyMessageDogeEcdsa(
btcEcdsaPublicKeyHash(
sig
.recoverPublicKey(
magicHash(challenge, "\x19Dogecoin Signed Message:\n"),
magicHash!(challenge, "\x19Dogecoin Signed Message:\n"),
)
.toHex(),
),
Expand Down
2 changes: 1 addition & 1 deletion packages/demo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"@lit/react": "^1.0.5",
"@uiw/react-json-view": "2.0.0-alpha.30",
"lucide-react": "^0.427.0",
"next": "14.2.10",
"next": "15.1.7",
"react": "^18",
"react-dom": "^18"
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
"use client";

import React from "react";
import { TextInput } from "@/src/components/Input";
import { Button } from "@/src/components/Button";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
"use client";

import React, { useState, useEffect } from "react";
import { Button } from "@/src/components/Button";
import { TextInput } from "@/src/components/Input";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
"use client";

import React from "react";
import { Button } from "@/src/components/Button";
import { ccc } from "@ckb-ccc/connector-react";
Expand Down
46 changes: 10 additions & 36 deletions packages/demo/src/app/connected/(tools)/SSRI/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,12 @@ import { TextInput } from "@/src/components/Input";
import { useApp } from "@/src/context";
import { ButtonsPanel } from "@/src/components/ButtonsPanel";
import { Dropdown } from "@/src/components/Dropdown";
import {
ScriptAmountArrayInput,
ScriptAmountInput,
ScriptAmountType,
} from "@/src/app/connected/(tools)/SSRI/components/ScriptAmountInput";
import { ScriptAmountType } from "@/src/app/connected/(tools)/SSRI/components/ScriptAmountInput";
import { ssri } from "@ckb-ccc/ssri";
import { ccc } from "@ckb-ccc/connector-react";
import JsonView from "@uiw/react-json-view";
import { darkTheme } from "@uiw/react-json-view/dark";
import Image from "next/image";
import {
HexArrayInput,
HexInput,
} from "@/src/app/connected/(tools)/SSRI/components/HexArrayInput";
import { Icon } from "@/src/components/Icon";
import { MethodParamType, PARAM_TYPE_OPTIONS } from "./types";
import { ParamValue } from "./types";
Expand All @@ -35,12 +27,9 @@ const METHODS_OPTIONS = [
];

export default function SSRI() {
const { signer, createSender } = useApp();
const { signer, createSender, ssriExecutor } = useApp();
const { log, error } = createSender("SSRI");

const [SSRIExecutorURL, setSSRIExecutorURL] = useState<string>(
"http://localhost:9090",
);
const [contractOutPointTx, setContractOutPointTx] = useState<string>("");
const [contractOutPointIndex, setContractOutPointIndex] =
useState<string>("0");
Expand Down Expand Up @@ -132,14 +121,16 @@ export default function SSRI() {
]);

const makeSSRICall = async () => {
if (!ssriExecutor) {
return;
}
await ssriExecutor.confirmStarted();
if (!signer) return;

setIsLoading(true);
setMethodResult(undefined);
setIconDataURL("");

const testSSRIExecutor = new ssri.ExecutorJsonRpc(SSRIExecutorURL);

let contract: ssri.Trait | undefined;
try {
const targetOutPoint = {
Expand All @@ -155,7 +146,7 @@ export default function SSRI() {
if (!scriptCell.cellOutput.type?.hash()) {
throw new Error("Script cell type hash not found");
}
contract = new ssri.Trait(scriptCell.outPoint, testSSRIExecutor);
contract = new ssri.Trait(scriptCell.outPoint, ssriExecutor);

if (!contract) {
throw new Error("Contract not initialized");
Expand Down Expand Up @@ -226,11 +217,11 @@ export default function SSRI() {
e instanceof Error
? e.message
: typeof e === "object"
? "Check your SSRI server"
? "Unexpected error. Please retry or post an issue on GitHub."
: String(e) || "Unknown error";
if (String(errorMessage).length < 3) {
errorMessage =
"Check your SSRI server or URL. Run `docker run -p 9090:9090 hanssen0/ckb-ssri-server` to start a local SSRI server.";
"Unexpected error. Please retry or post an issue on GitHub.";
}
setMethodResult(`Error: ${errorMessage}`);
error(`Error: ${errorMessage}`);
Expand Down Expand Up @@ -327,18 +318,6 @@ export default function SSRI() {
<span className="flex h-6 w-6 shrink-0 items-center justify-center rounded-full bg-blue-500 text-sm font-semibold text-white">
1
</span>
<div className="flex-1 text-gray-800 dark:text-gray-100">
<code className="rounded bg-blue-50 px-2 py-1 font-mono text-sm text-blue-900 dark:bg-blue-900 dark:text-blue-100">
docker run -p 9090:9090 hanssen0/ckb-ssri-server
</code>
<span className="ml-2">to start a local SSRI server.</span>
</div>
</div>

<div className="flex items-start gap-3">
<span className="flex h-6 w-6 shrink-0 items-center justify-center rounded-full bg-blue-500 text-sm font-semibold text-white">
2
</span>
<div className="flex-1 text-gray-800 dark:text-gray-100">
The default parameters are prepared to just work. Just click{" "}
<span className="font-semibold text-blue-600 dark:text-blue-400">
Expand All @@ -354,7 +333,7 @@ export default function SSRI() {

<div className="flex items-start gap-3">
<span className="flex h-6 w-6 shrink-0 items-center justify-center rounded-full bg-blue-500 text-sm font-semibold text-white">
3
2
</span>
<div className="flex-1 text-gray-800 dark:text-gray-100">
All Done! You called an SSRI method! Try playing with other
Expand All @@ -373,11 +352,6 @@ export default function SSRI() {
</div>
</div>
<>
<TextInput
label="SSRI Executor URL"
placeholder="URL of the SSRI executor"
state={[SSRIExecutorURL, setSSRIExecutorURL]}
/>
<div className="flex flex-row items-center gap-2">
<TextInput
label="Script Cell Type ID Args (Optional)"
Expand Down
2 changes: 2 additions & 0 deletions packages/demo/src/app/connected/(tools)/SSRI/types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
"use client";

import { ScriptAmountType } from "@/src/app/connected/(tools)/SSRI/components/ScriptAmountInput";
import { ccc } from "@ckb-ccc/connector-react";
export type ParamValue =
Expand Down
1 change: 1 addition & 0 deletions packages/demo/src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import "./globals.css";
const inter = Inter({ subsets: ["latin"] });

export const metadata: Metadata = {
metadataBase: new URL("https://app.ckbccc.com/"),
title: "CCC App",
description: "An app based on the CCC library",
icons: "/favicon.svg",
Expand Down
2 changes: 1 addition & 1 deletion packages/demo/src/components/Background.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { APP_CONTEXT } from "../context";

export class Background extends Component {
static contextType = APP_CONTEXT;
context: React.ContextType<typeof APP_CONTEXT>;
declare context: React.ContextType<typeof APP_CONTEXT>;

refBg: RefObject<HTMLDivElement> = createRef();
ref0: RefObject<RandomWalk> = createRef();
Expand Down
2 changes: 1 addition & 1 deletion packages/demo/src/components/RandomWalk.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export type RandomWalkProps = React.ComponentPropsWithoutRef<"div"> & {

export class RandomWalk extends Component<RandomWalkProps> {
static contextType = APP_CONTEXT;
context: React.ContextType<typeof APP_CONTEXT>;
declare context: React.ContextType<typeof APP_CONTEXT>;

// position, speed, force
physics: number[] = [0, 0, 0, 0, 0, 0];
Expand Down
15 changes: 14 additions & 1 deletion packages/demo/src/context.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"use client";

import React, { createContext, ReactNode, useEffect, useState } from "react";
import { ccc } from "@ckb-ccc/connector-react";
import { ccc, ssri } from "@ckb-ccc/connector-react";
import { Notifications } from "./components/Notifications";
import { formatString, useGetExplorerLink } from "./utils";
import { Key } from "lucide-react";
Expand Down Expand Up @@ -49,6 +49,7 @@ export const APP_CONTEXT = createContext<
warn: (...msgs: ReactNode[]) => void;
error: (...msgs: ReactNode[]) => void;
};
ssriExecutor: ssri.ExecutorWASM | undefined;
}
| undefined
>(undefined);
Expand All @@ -61,6 +62,9 @@ export function AppProvider({ children }: { children: React.ReactNode }) {

const [enabledAnimate, setAnimate] = useState(true);
const [backgroundLifted, setBackgroundLifted] = useState(false);
const [ssriExecutorWASM, setSsriExecutorWASM] = useState<
ssri.ExecutorWASM | undefined
>(undefined);

const {
wallet,
Expand All @@ -73,6 +77,14 @@ export function AppProvider({ children }: { children: React.ReactNode }) {

const { explorerAddress } = useGetExplorerLink();

useEffect(() => {
if (!ssriExecutorWASM) {
setSsriExecutorWASM(
new ssri.ExecutorWASM("https://testnet.ckb.dev/", true, 10, 300),
);
}
}, [ssriExecutorWASM]);

useEffect(() => {
if (
!privateKeySigner ||
Expand Down Expand Up @@ -175,6 +187,7 @@ export function AppProvider({ children }: { children: React.ReactNode }) {
warn: (...msgs) => sendMessage("warn", title, msgs),
error: (...msgs) => sendMessage("error", title, msgs),
}),
ssriExecutor: ssriExecutorWASM,
}}
>
{children}
Expand Down
19 changes: 15 additions & 4 deletions packages/demo/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
{
"compilerOptions": {
"lib": ["dom", "dom.iterable", "esnext"],
"lib": [
"dom",
"dom.iterable",
"esnext",
"ES2024",
"WebWorker",
],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
Expand All @@ -18,10 +24,13 @@
}
],
"paths": {
"@/*": ["./*"]
"@/*": [
"./*"
]
},
"emitDecoratorMetadata": true,
"experimentalDecorators": true
"experimentalDecorators": true,
"target": "ES2024"
},
"include": [
"next-env.d.ts",
Expand All @@ -30,5 +39,7 @@
".next/types/**/*.ts",
"src/app/connected/(tools)/IssueXUdtSus"
],
"exclude": ["node_modules"]
"exclude": [
"node_modules"
]
}
6 changes: 6 additions & 0 deletions packages/ssri-executor-wasm/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/dist
/package-lock.json
/yarn.lock
/node_modules
/pkg
/target
Loading