diff --git a/.changeset/brave-dragons-retire.md b/.changeset/brave-dragons-retire.md deleted file mode 100644 index 79315fe54..000000000 --- a/.changeset/brave-dragons-retire.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@ckb-lumos/common-scripts": minor ---- - -feat: support create omnilock address from btc testnet address diff --git a/.changeset/cyan-geese-relate.md b/.changeset/cyan-geese-relate.md deleted file mode 100644 index e86d32e1d..000000000 --- a/.changeset/cyan-geese-relate.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@ckb-lumos/common-scripts": minor ---- - -feat: deprecated omnilock btc auth with p2sh diff --git a/.changeset/olive-waves-relax.md b/.changeset/olive-waves-relax.md new file mode 100644 index 000000000..1be1702ea --- /dev/null +++ b/.changeset/olive-waves-relax.md @@ -0,0 +1,5 @@ +--- +"@ckb-lumos/common-scripts": minor +--- + +**BREAKING CHANGE**: revert #682 that deprecate omnilock p2sh and supports btc testnet addr diff --git a/.eslintrc.next.js b/.eslintrc.next.js index 7589d0f34..240fa6c24 100644 --- a/.eslintrc.next.js +++ b/.eslintrc.next.js @@ -23,7 +23,6 @@ module.exports = { -1, // index -1 is not found 0, // first element of an array 1, // common for i + 1 in a loop - 2, // many .slice(2) since the '0x' prefix should be removed while calling 3rd-party library 16, // toString(16) 1000, // second to millisecond ], diff --git a/packages/common-scripts/src/omnilock-bitcoin.ts b/packages/common-scripts/src/omnilock-bitcoin.ts index 8097079e7..b6b537afb 100644 --- a/packages/common-scripts/src/omnilock-bitcoin.ts +++ b/packages/common-scripts/src/omnilock-bitcoin.ts @@ -1,3 +1,6 @@ +// TODO the magic number eslint will be resolved in 0.24 by recovering https://github.com/ckb-js/lumos/pull/682 +/*eslint-disable @typescript-eslint/no-magic-numbers*/ + import { bytes, BytesLike } from "@ckb-lumos/codec"; import { bech32 } from "bech32"; import bs58 from "bs58"; @@ -7,14 +10,10 @@ const BTC_PREFIX = "CKB (Bitcoin Layer) transaction: 0x"; /** * Decode bitcoin address to public key hash in bytes - * @deprecated please migrate to {@link parseAddressToPublicKeyHash} * @see https://en.bitcoin.it/wiki/List_of_address_prefixes * @param address */ export function decodeAddress(address: string): ArrayLike { - const btcAddressFlagSize = 1; - const hashSize = 20; - try { // Bech32 if (address.startsWith("bc1q")) { @@ -23,16 +22,12 @@ export function decodeAddress(address: string): ArrayLike { // P2PKH if (address.startsWith("1")) { - return bs58 - .decode(address) - .slice(btcAddressFlagSize, btcAddressFlagSize + hashSize); + return bs58.decode(address).slice(1, 21); } // P2SH if (address.startsWith("3")) { - return bs58 - .decode(address) - .slice(btcAddressFlagSize, btcAddressFlagSize + hashSize); + return bs58.decode(address).slice(1, 21); } } catch { // https://bitcoin.design/guide/glossary/address/#taproot-address---p2tr @@ -46,32 +41,6 @@ export function decodeAddress(address: string): ArrayLike { ); } -export function parseAddressToPublicKeyHash( - address: string -): ArrayLike { - try { - // Bech32 - if (isP2wpkhAddress(address)) { - return bech32.fromWords(bech32.decode(address).words.slice(1)); - } - - // P2PKH - if (isP2pkhAddress(address)) { - const networkSize = 1; - const pubkeyHashSize = 20; - // public key hash - // a P2PKH address is composed of network(1 byte) + pubkey hash(20 bytes) - return bs58 - .decode(address) - .slice(networkSize, networkSize + pubkeyHashSize); - } - } catch { - // do nothing here, throw an error below - } - - throw new Error("Only supports Native Segwit(P2WPKH) and Legacy(P2PKH)"); -} - export interface Provider { requestAccounts(): Promise; signMessage(message: string, type?: "ecdsa"): Promise; @@ -113,25 +82,21 @@ export async function signMessage( const signature = bytes.bytify(base64ToHex(signatureBase64)); const address = accounts[0]; - /* eslint-disable @typescript-eslint/no-magic-numbers */ - // a secp256k1 private key can be used to sign various types of messages // the first byte of signature used as a recovery id to identify the type of message // https://github.com/XuJiandong/omnilock/blob/4e9fdb6ca78637651c8145bb7c5b82b4591332fb/c/ckb_identity.h#L249-L266 - if (isP2wpkhAddress(address)) { + if (address.startsWith("bc1q")) { signature[0] = 39 + ((signature[0] - 27) % 4); - } else if (isP2shAddress(address)) { + } else if (address.startsWith("3")) { signature[0] = 35 + ((signature[0] - 27) % 4); - } else if (isP2pkhAddress(address)) { + } else if (address.startsWith("1")) { signature[0] = 31 + ((signature[0] - 27) % 4); } else { throw new Error( - `Unsupported bitcoin address ${address}. Only supports SegWit, P2SH-P2WPKH, P2PKH` + `Unsupported bitcoin address ${address}, only 1...(P2PKH) 3...(P2SH), and bc1...(Bech32) are supported.` ); } - /* eslint-enable @typescript-eslint/no-magic-numbers */ - return bytes.hexify(signature); } @@ -139,32 +104,8 @@ function base64ToHex(str: string) { const raw = atob(str); let result = ""; for (let i = 0; i < raw.length; i++) { - // eslint-disable-next-line @typescript-eslint/no-magic-numbers - result += raw.charCodeAt(i).toString(16).padStart(2, "0"); + const hex = raw.charCodeAt(i).toString(16); + result += hex.length === 2 ? hex : "0" + hex; } return "0x" + result; } - -/* https://en.bitcoin.it/wiki/List_of_address_prefixes */ - -function isP2wpkhAddress(address: string): boolean { - return ( - address.startsWith("bc1") || // mainnet - address.startsWith("tb1") // testnet - ); -} - -function isP2shAddress(address: string): boolean { - return ( - address.startsWith("3") || // mainnet - address.startsWith("2") // testnet - ); -} - -function isP2pkhAddress(address: string): boolean { - return ( - address.startsWith("1") || // mainnet - address.startsWith("m") || // testnet - address.startsWith("n") // testnet - ); -} diff --git a/packages/common-scripts/src/omnilock.ts b/packages/common-scripts/src/omnilock.ts index a88ff4330..756629f8e 100644 --- a/packages/common-scripts/src/omnilock.ts +++ b/packages/common-scripts/src/omnilock.ts @@ -110,9 +110,8 @@ const SECP256K1_SIGNATURE_PLACEHOLDER_LENGTH = 65; const ED25519_SIGNATURE_PLACEHOLDER_LENGTH = 96; /** - * Create an Omnilock script based on other networks' wallet - * @deprecated please migrate to {@link createSimplePublicKeyBasedOmnilockScript} - * @see https://github.com/nervosnetwork/rfcs/blob/master/rfcs/0042-omnilock/0042-omnilock.md + * only support ETHEREUM and SECP256K1_BLAKE160 mode currently + * refer to: @link https://github.com/nervosnetwork/rfcs/blob/master/rfcs/0042-omnilock/0042-omnilock.md omnilock * @param omnilockInfo * @param options * @returns @@ -137,58 +136,6 @@ export function createOmnilockScript( ): Script { const config = options?.config || getConfig(); const omnilockConfig = config.SCRIPTS.OMNILOCK; - - if (!omnilockConfig) { - throw new Error("OMNILOCK script config not found."); - } - - const defaultOmnilockArgs = 0b00000000; - const omnilockArgs = [defaultOmnilockArgs]; - - if (omnilockInfo.auth.flag === "BITCOIN") { - return { - codeHash: omnilockConfig.CODE_HASH, - hashType: omnilockConfig.HASH_TYPE, - args: bytes.hexify( - bytes.concat( - [IdentityFlagsType.IdentityFlagsBitcoin], - bitcoin.decodeAddress(omnilockInfo.auth.content), - omnilockArgs - ) - ), - }; - } - - return createSimplePublicKeyBasedOmnilockScript(omnilockInfo, options); -} - -/** - * Create an Omnilock script based on other networks' wallet - * @see https://github.com/nervosnetwork/rfcs/blob/master/rfcs/0042-omnilock/0042-omnilock.md - * @param omnilockInfo - * @param options - * @returns - * @example - * // create an omnilock to work with MetaMask wallet - * createOmnilockScript({ - * auth: { - * flag: "ETHEREUM", - * content: "an ethereum address here", - * }, { config }) - * // or we can create an omnilock to work with UniSat wallet - * createOmnilockScript({ - * auth: { - * flag: "BITCOIN", - * content: "a bitcoin address here", - * } - * }, {config}) - */ -export function createSimplePublicKeyBasedOmnilockScript( - omnilockInfo: OmnilockInfo, - options?: Options -): Script { - const config = options?.config || getConfig(); - const omnilockConfig = config.SCRIPTS.OMNILOCK; if (!omnilockConfig) { throw new Error("OMNILOCK script config not found."); } @@ -229,7 +176,7 @@ export function createSimplePublicKeyBasedOmnilockScript( return bytes.hexify( bytes.concat( [IdentityFlagsType.IdentityFlagsBitcoin], - bitcoin.parseAddressToPublicKeyHash(omnilockInfo.auth.content), + bitcoin.decodeAddress(omnilockInfo.auth.content), omnilockArgs ) ); @@ -506,5 +453,4 @@ export default { CellCollector, OmnilockWitnessLock, createOmnilockScript, - createSimplePublicKeyBasedOmnilockScript, }; diff --git a/packages/common-scripts/tests/omnilock-bitcoin.test.ts b/packages/common-scripts/tests/omnilock-bitcoin.test.ts index 1c9e3c4eb..c6772357e 100644 --- a/packages/common-scripts/tests/omnilock-bitcoin.test.ts +++ b/packages/common-scripts/tests/omnilock-bitcoin.test.ts @@ -9,10 +9,7 @@ import { blockchain, utils } from "@ckb-lumos/base"; import { bytes } from "@ckb-lumos/codec"; import { common } from "../src"; import { mockOutPoint } from "@ckb-lumos/debugger/lib/context"; -import { - createSimplePublicKeyBasedOmnilockScript, - OmnilockWitnessLock, -} from "../src/omnilock"; +import { createOmnilockScript, OmnilockWitnessLock } from "../src/omnilock"; import { address, AddressType, core, keyring } from "@unisat/wallet-sdk"; import { NetworkType } from "@unisat/wallet-sdk/lib/network"; import { Provider, signMessage } from "../src/omnilock-bitcoin"; @@ -35,12 +32,6 @@ test.serial("Omnilock#Bitcoin P2PKH", async (t) => { t.is(result.code, 0, result.message); }); -test.serial("Omnilock#Bitcoin P2PKH Testnet", async (t) => { - const { provider } = makeProvider(AddressType.P2PKH, NetworkType.TESTNET); - const result = await execute(provider); - t.is(result.code, 0, result.message); -}); - test.serial("Omnilock#Bitcoin P2WPKH", async (t) => { const { provider } = makeProvider(AddressType.P2WPKH); const result = await execute(provider); @@ -48,8 +39,8 @@ test.serial("Omnilock#Bitcoin P2WPKH", async (t) => { t.is(result.code, 0, result.message); }); -test.serial("Omnilock#Bitcoin P2WPKH Testnet", async (t) => { - const { provider } = makeProvider(AddressType.P2WPKH, NetworkType.TESTNET); +test.serial("Omnilock#Bitcoin P2SH_P2WPKH", async (t) => { + const { provider } = makeProvider(AddressType.P2SH_P2WPKH); const result = await execute(provider); t.is(result.code, 0, result.message); @@ -79,10 +70,7 @@ async function execute(provider: Provider) { }); } -function makeProvider( - addressType: AddressType, - network: NetworkType = NetworkType.MAINNET -): { +function makeProvider(addressType: AddressType): { provider: Provider; pair: core.ECPairInterface; keyring: SimpleKeyring; @@ -90,7 +78,11 @@ function makeProvider( const pair = core.ECPair.makeRandom(); const ring = new keyring.SimpleKeyring([pair.privateKey!.toString("hex")]); const publicKey = pair.publicKey.toString("hex"); - const addr = address.publicKeyToAddress(publicKey, addressType, network); + const addr = address.publicKeyToAddress( + publicKey, + addressType, + NetworkType.MAINNET + ); return { pair, @@ -106,7 +98,7 @@ function makeProvider( async function setupTxSkeleton(addr: string) { const txSkeleton = TransactionSkeleton().asMutable(); - const lock = createSimplePublicKeyBasedOmnilockScript( + const lock = createOmnilockScript( { auth: { flag: "BITCOIN", content: addr } }, { config: managerConfig } );