Skip to content

Commit

Permalink
show multisig ckb address
Browse files Browse the repository at this point in the history
  • Loading branch information
doitian committed Feb 26, 2024
1 parent 252b0f3 commit 36a8ac3
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 56 deletions.
18 changes: 18 additions & 0 deletions src/AddressPage.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,30 @@
import DeleteButton from "./components/DeleteButton.js";
import { Button } from "flowbite-react";
import { generateMultisigAddress } from "./lib/ckb-address.js";

export default function AddressPage({ address, deleteAddress, navigate }) {
return (
<section>
<h2 className="text-lg mb-4">
Multisig <code className="break-all">{address.args}</code>
</h2>

<dl className="mb-4">
<dt>Testnet Address</dt>
<dd className="mb-4">
<code className="break-all">
{generateMultisigAddress(address, "ckt")}
</code>
</dd>

<dt>Mainnet Address</dt>
<dd className="mb-4">
<code className="break-all">
{generateMultisigAddress(address, "ckb")}
</code>
</dd>
</dl>

<p className="mb-4">
Requiring {address.threshold}{" "}
{address.threshold === 1 ? "signature" : "signatures"} from{" "}
Expand Down
80 changes: 80 additions & 0 deletions src/lib/ckb-address.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { utils as lumosBaseUtils } from "@ckb-lumos/base";
import * as lumosHelpers from "@ckb-lumos/helpers";

const { CKBHasher } = lumosBaseUtils;

export const SECP256K1_CODE_HASH =
"0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8";

export const SECP256K1_MULTISIG_CODE_HASH =
"0x5c5069eb0857efc65e1bca0c07df34c31663b3622fd3876c876320fc9634e2a8";

export function checkSecp256k1Address(address) {
try {
const script = addressToScript(address);
return (
script.codeHash === SECP256K1_CODE_HASH &&
script.hashType === "type" &&
script.args.length === 42
);
} catch (error) {
return false;
}
}

export function addressToScript(address, options) {
if (!options) {
const prefix = address.startsWith("ckb")
? "ckb"
: address.startsWith("ckt")
? "ckt"
: null;
if (prefix === null) {
throw new Error(`Invalid CKB Address: ${address}`);
}

options = { config: { PREFIX: prefix } };
}

return lumosHelpers.addressToScript(address, options);
}

export function scriptToAddress(script, options) {
if (typeof options === "string") {
options = { config: { PREFIX: options } };
}
return lumosHelpers.encodeToAddress(script, options);
}

// multisig_script: S | R | M | N | PubKeyHash1 | PubKeyHash2 | ...
//
// +-------------+------------------------------------+-------+
// | | Description | Bytes |
// +-------------+------------------------------------+-------+
// | S | reserved field, must be zero | 1 |
// | R | first nth public keys must match | 1 |
// | M | threshold | 1 |
// | N | total public keys | 1 |
// | PubkeyHashN | blake160 hash of compressed pubkey | 20 |
export function generateMultisigArgs(config) {
const hasher = new CKBHasher();
hasher.update(
Uint8Array.of(0, config.required, config.threshold, config.signers.length),
);
for (const address of config.signers) {
const script = addressToScript(address);
hasher.update(script.args);
}
// first 20 bytes
return hasher.digestHex().substring(0, 42);
}

export function generateMultisigAddress(config, prefix) {
const script = {
codeHash: SECP256K1_MULTISIG_CODE_HASH,
hashType: "type",
args: generateMultisigArgs(config),
};

return scriptToAddress(script, prefix);
}
57 changes: 1 addition & 56 deletions src/schemas.js
Original file line number Diff line number Diff line change
@@ -1,60 +1,5 @@
import { z } from "zod";
import { addressToScript } from "@ckb-lumos/helpers";
import { utils as lumosBaseUtils } from "@ckb-lumos/base";

const { CKBHasher } = lumosBaseUtils;

const SECP256K1_CODE_HASH =
"0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8";

function checkSecp256k1Address(address) {
try {
const script = parseAddress(address);
return (
script.codeHash === SECP256K1_CODE_HASH &&
script.hashType === "type" &&
script.args.length == 42
);
} catch (error) {
return false;
}
}

function parseAddress(address) {
const prefix = address.startsWith("ckb")
? "ckb"
: address.startsWith("ckt")
? "ckt"
: null;
if (prefix === null) {
throw new Error(`Invalid CKB Address: ${address}`);
}

return addressToScript(address, { config: { PREFIX: prefix } });
}

// multisig_script: S | R | M | N | PubKeyHash1 | PubKeyHash2 | ...
//
// +-------------+------------------------------------+-------+
// | | Description | Bytes |
// +-------------+------------------------------------+-------+
// | S | reserved field, must be zero | 1 |
// | R | first nth public keys must match | 1 |
// | M | threshold | 1 |
// | N | total public keys | 1 |
// | PubkeyHashN | blake160 hash of compressed pubkey | 20 |
function generateMultisigArgs(config) {
const hasher = new CKBHasher();
hasher.update(
Uint8Array.of(0, config.required, config.threshold, config.signers.length),
);
for (const address of config.signers) {
const script = parseAddress(address);
hasher.update(script.args);
}
// first 20 bytes
return hasher.digestHex().substring(0, 42);
}
import { generateMultisigArgs, checkSecp256k1Address } from "./lib/ckb-address";

export const Address = z.string().refine(checkSecp256k1Address, {
message: "invalid CKB secp256k1 address",
Expand Down

0 comments on commit 36a8ac3

Please sign in to comment.