diff --git a/package-lock.json b/package-lock.json index 662825247..a0aeaaeb9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,7 +17,7 @@ "@scure/bip39": "^1.0.0", "@stacks/blockchain-api-client": "^0.34.1", "@stacks/stacks-blockchain-api-types": "^0.61.0", - "@types/bn.js": "^4.11.6", + "@types/bn.js": "^5.1.0", "@types/common-tags": "^1.8.0", "@types/cors": "^2.8.5", "@types/elliptic": "^6.4.12", @@ -573,6 +573,14 @@ "smart-buffer": "^4.1.0" } }, + "node_modules/@blockstack/stacks-transactions/node_modules/@types/bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@blockstack/stacks-transactions/node_modules/bn.js": { "version": "4.12.0", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", @@ -4098,9 +4106,9 @@ } }, "node_modules/@types/bn.js": { - "version": "4.11.6", - "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz", - "integrity": "sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.0.tgz", + "integrity": "sha512-QSSVYj7pYFN49kW77o2s9xTCwZ8F2xLbjLLSEVh8D2F4JUhZtPAGOFLTD+ffqksBx/u4cE/KImFjyhqCjn/LIA==", "dependencies": { "@types/node": "*" } @@ -5859,6 +5867,14 @@ "npm": ">=6" } }, + "node_modules/blockstack/node_modules/@types/bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/blockstack/node_modules/ajv": { "version": "4.11.8", "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", @@ -21518,9 +21534,7 @@ "dependencies": { "@stacks/common": "^4.0.1", "@stacks/network": "^4.0.1", - "@stacks/transactions": "^4.0.0", - "@types/bn.js": "^4.11.6", - "bn.js": "^5.2.0" + "@stacks/transactions": "^4.0.0" }, "devDependencies": { "@types/jest": "^26.0.22", @@ -21555,8 +21569,6 @@ "bip32": "^2.0.6", "bip39": "^3.0.2", "bitcoinjs-lib": "^5.2.0", - "blockstack": "^19.2.2", - "bn.js": "^5.2.0", "c32check": "^1.1.3", "cors": "^2.8.4", "cross-fetch": "^3.1.4", @@ -21581,6 +21593,7 @@ "@types/jest": "^26.0.22", "@types/node-fetch": "^2.5.0", "@types/ripemd160": "^2.0.0", + "blockstack": "^19.2.2", "jest": "^26.6.3", "jest-fetch-mock": "^3.0.3", "jest-module-name-mapper": "^0.1.5", @@ -21595,14 +21608,15 @@ "version": "4.0.1", "license": "GPL-3.0-or-later", "dependencies": { + "@types/bn.js": "^5.1.0", "@types/node": "^14.14.43", - "bn.js": "^5.2.0", "buffer": "^6.0.3", "cross-fetch": "^3.1.4" }, "devDependencies": { "@rollup/plugin-typescript": "^8.3.0", "@types/jest": "^26.0.22", + "bn.js": "^5.2.0", "jest": "^26.6.3", "jest-fetch-mock": "^3.0.3", "jest-module-name-mapper": "^0.1.5", @@ -21625,9 +21639,7 @@ "@noble/secp256k1": "^1.5.5", "@scure/bip39": "^1.0.0", "@stacks/common": "^4.0.1", - "@types/bn.js": "^4.11.6", "@types/node": "^14.14.43", - "bn.js": "^5.2.0", "bs58": "^5.0.0", "ripemd160-min": "^0.0.6", "sha.js": "^2.4.11", @@ -21673,13 +21685,11 @@ "@stacks/profile": "^4.0.0", "@stacks/storage": "^4.0.0", "@stacks/transactions": "^4.0.0", - "@types/bn.js": "^4.11.6", "@types/node": "^14.14.43", "@types/triplesec": "^3.0.0", "bip32": "^2.0.6", "bip39": "^3.0.2", "bitcoinjs-lib": "^5.2.0", - "bn.js": "^5.2.0", "c32check": "^1.1.3", "jsontokens": "^3.0.0", "randombytes": "^2.1.0", @@ -21763,9 +21773,7 @@ "@stacks/network": "^4.0.1", "@stacks/stacks-blockchain-api-types": "^0.61.0", "@stacks/transactions": "^4.0.0", - "@types/bn.js": "^4.11.6", - "bitcoinjs-lib": "^5.2.0", - "bn.js": "^5.2.0" + "bitcoinjs-lib": "^5.2.0" }, "devDependencies": { "@types/jest": "^26.0.22", @@ -21791,7 +21799,6 @@ "@stacks/auth": "^4.0.0", "@stacks/common": "^4.0.1", "@stacks/encryption": "^4.0.1", - "bitcoinjs-lib": "^5.2.0", "jsontokens": "^3.0.0" }, "devDependencies": { @@ -21821,10 +21828,8 @@ "@noble/secp256k1": "^1.5.5", "@stacks/common": "^4.0.1", "@stacks/network": "^4.0.1", - "@types/bn.js": "^4.11.6", "@types/node": "^14.14.43", "@types/sha.js": "^2.4.0", - "bn.js": "^5.2.0", "c32check": "^1.1.3", "cross-fetch": "^3.1.4", "lodash.clonedeep": "^4.5.0", @@ -21867,7 +21872,6 @@ "@stacks/storage": "^4.0.0", "@stacks/transactions": "^4.0.0", "bitcoinjs-lib": "^5.2.0", - "bn.js": "^5.2.0", "c32check": "^1.1.3", "jsontokens": "^3.0.0", "triplesec": "^4.0.3", @@ -22229,6 +22233,14 @@ "smart-buffer": "^4.1.0" }, "dependencies": { + "@types/bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==", + "requires": { + "@types/node": "*" + } + }, "bn.js": { "version": "4.12.0", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", @@ -24559,9 +24571,7 @@ "@stacks/common": "^4.0.1", "@stacks/network": "^4.0.1", "@stacks/transactions": "^4.0.0", - "@types/bn.js": "^4.11.6", "@types/jest": "^26.0.22", - "bn.js": "^5.2.0", "jest": "^26.6.3", "jest-fetch-mock": "^3.0.3", "jest-module-name-mapper": "^0.1.5", @@ -24599,7 +24609,6 @@ "bip39": "^3.0.2", "bitcoinjs-lib": "^5.2.0", "blockstack": "^19.2.2", - "bn.js": "^5.2.0", "c32check": "^1.1.3", "cors": "^2.8.4", "cross-fetch": "^3.1.4", @@ -24624,6 +24633,7 @@ "version": "file:packages/common", "requires": { "@rollup/plugin-typescript": "^8.3.0", + "@types/bn.js": "^5.1.0", "@types/jest": "^26.0.22", "@types/node": "^14.14.43", "bn.js": "^5.2.0", @@ -24650,7 +24660,6 @@ "@peculiar/webcrypto": "^1.1.6", "@scure/bip39": "^1.0.0", "@stacks/common": "^4.0.1", - "@types/bn.js": "^4.11.6", "@types/bs58check": "^2.1.0", "@types/elliptic": "^6.4.12", "@types/jest": "^26.0.22", @@ -24658,7 +24667,6 @@ "@types/sha.js": "^2.4.0", "@types/triplesec": "^3.0.0", "bitcoinjs-lib": "^5.2.0", - "bn.js": "^5.2.0", "bs58": "^5.0.0", "bs58check": "^2.1.2", "crypto-browserify": "^3.12.0", @@ -24993,14 +25001,12 @@ "@stacks/profile": "^4.0.0", "@stacks/storage": "^4.0.0", "@stacks/transactions": "^4.0.0", - "@types/bn.js": "^4.11.6", "@types/jest": "^26.0.22", "@types/node": "^14.14.43", "@types/triplesec": "^3.0.0", "bip32": "^2.0.6", "bip39": "^3.0.2", "bitcoinjs-lib": "^5.2.0", - "bn.js": "^5.2.0", "c32check": "^1.1.3", "crypto-browserify": "^3.12.0", "jest": "^26.6.3", @@ -25088,10 +25094,8 @@ "@stacks/network": "^4.0.1", "@stacks/stacks-blockchain-api-types": "^0.61.0", "@stacks/transactions": "^4.0.0", - "@types/bn.js": "^4.11.6", "@types/jest": "^26.0.22", "bitcoinjs-lib": "^5.2.0", - "bn.js": "^5.2.0", "jest": "^26.6.3", "jest-fetch-mock": "^3.0.3", "jest-module-name-mapper": "^0.1.5", @@ -25119,7 +25123,6 @@ "@stacks/encryption": "^4.0.1", "@types/jest": "^26.0.22", "@types/jsdom": "^16.2.10", - "bitcoinjs-lib": "^5.2.0", "jest": "^26.6.3", "jest-fetch-mock": "^3.0.3", "jest-module-name-mapper": "^0.1.5", @@ -25143,14 +25146,12 @@ "@noble/secp256k1": "^1.5.5", "@stacks/common": "^4.0.1", "@stacks/network": "^4.0.1", - "@types/bn.js": "^4.11.6", "@types/common-tags": "^1.8.0", "@types/elliptic": "^6.4.12", "@types/jest": "^26.0.22", "@types/lodash.clonedeep": "^4.5.6", "@types/node": "^14.14.43", "@types/sha.js": "^2.4.0", - "bn.js": "^5.2.0", "c32check": "^1.1.3", "common-tags": "^1.8.0", "cross-fetch": "^3.1.4", @@ -25189,7 +25190,6 @@ "assert": "^2.0.0", "bip32": "^2.0.6", "bitcoinjs-lib": "^5.2.0", - "bn.js": "^5.2.0", "c32check": "^1.1.3", "crypto-browserify": "^3.12.0", "jest": "^26.6.3", @@ -25250,9 +25250,9 @@ } }, "@types/bn.js": { - "version": "4.11.6", - "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz", - "integrity": "sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.0.tgz", + "integrity": "sha512-QSSVYj7pYFN49kW77o2s9xTCwZ8F2xLbjLLSEVh8D2F4JUhZtPAGOFLTD+ffqksBx/u4cE/KImFjyhqCjn/LIA==", "requires": { "@types/node": "*" } @@ -26655,6 +26655,14 @@ "zone-file": "^1.0.0" }, "dependencies": { + "@types/bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==", + "requires": { + "@types/node": "*" + } + }, "ajv": { "version": "4.11.8", "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", diff --git a/packages/bns/package.json b/packages/bns/package.json index 102b6262b..b1c4d326c 100755 --- a/packages/bns/package.json +++ b/packages/bns/package.json @@ -42,9 +42,7 @@ "dependencies": { "@stacks/common": "^4.0.1", "@stacks/network": "^4.0.1", - "@stacks/transactions": "^4.0.0", - "@types/bn.js": "^4.11.6", - "bn.js": "^5.2.0" + "@stacks/transactions": "^4.0.0" }, "devDependencies": { "@types/jest": "^26.0.22", diff --git a/packages/bns/src/utils.ts b/packages/bns/src/utils.ts index f04995144..56bb230d1 100644 --- a/packages/bns/src/utils.ts +++ b/packages/bns/src/utils.ts @@ -1,8 +1,6 @@ import { Buffer } from '@stacks/common'; import { bufferCV, uintCV, hash160 } from '@stacks/transactions'; -import BN from 'bn.js'; - export function decodeFQN(fqdn: string): { name: string; namespace: string; @@ -30,6 +28,6 @@ export function decodeFQN(fqdn: string): { export const bufferCVFromString = (string: string) => bufferCV(Buffer.from(string)); -export const uintCVFromBN = (int: BN) => uintCV(int.toString(10)); +export const uintCVFromBN = (int: bigint) => uintCV(int.toString(10)); export const getZonefileHash = (zonefile: string) => hash160(Buffer.from(zonefile)); diff --git a/packages/bns/tests/bns.test.ts b/packages/bns/tests/bns.test.ts index 4ed3f7764..b154dc5c0 100644 --- a/packages/bns/tests/bns.test.ts +++ b/packages/bns/tests/bns.test.ts @@ -30,8 +30,7 @@ import { import {bufferCVFromString, decodeFQN, getZonefileHash, uintCVFromBN} from "../src/utils"; -import BN from "bn.js"; -import { ChainID } from "@stacks/common"; +import { ChainID } from '@stacks/common'; beforeEach(() => { fetchMock.resetMocks(); @@ -356,7 +355,7 @@ test('getNamePrice error', async () => { test('preorderNamespace', async () => { const namespace = 'id'; const salt = 'salt'; - const stxToBurn = new BN(10); + const stxToBurn = BigInt(10); const publicKey = '03ef788b3830c00abe8f64f62dc32fc863bc0b2cafeb073b6c8e1c7657d9c2c3ab'; const network = new StacksTestnet(); @@ -416,29 +415,29 @@ test('revealNamespace', async () => { const publicKey = '03ef788b3830c00abe8f64f62dc32fc863bc0b2cafeb073b6c8e1c7657d9c2c3ab'; const priceFunction: PriceFunction = { - base: new BN(10), - coefficient: new BN(1), - b1: new BN(1), - b2: new BN(2), - b3: new BN(3), - b4: new BN(4), - b5: new BN(5), - b6: new BN(6), - b7: new BN(7), - b8: new BN(8), - b9: new BN(9), - b10: new BN(10), - b11: new BN(11), - b12: new BN(12), - b13: new BN(13), - b14: new BN(14), - b15: new BN(15), - b16: new BN(16), - nonAlphaDiscount: new BN(0), - noVowelDiscount: new BN(0), + base: BigInt(10), + coefficient: BigInt(1), + b1: BigInt(1), + b2: BigInt(2), + b3: BigInt(3), + b4: BigInt(4), + b5: BigInt(5), + b6: BigInt(6), + b7: BigInt(7), + b8: BigInt(8), + b9: BigInt(9), + b10: BigInt(10), + b11: BigInt(11), + b12: BigInt(12), + b13: BigInt(13), + b14: BigInt(14), + b15: BigInt(15), + b16: BigInt(16), + nonAlphaDiscount: BigInt(0), + noVowelDiscount: BigInt(0), } - const lifetime = new BN(10000); + const lifetime = BigInt(10000); const namespaceImportAddress = 'SPF0324DSC4K505TP6A8C7GAK4R95E38TGNZP7RE'; const makeUnsignedContractCall = jest.fn().mockResolvedValue({}); @@ -602,7 +601,7 @@ test('readyNamespace', async () => { test('preorderName', async () => { const fullyQualifiedName = 'test.id'; const salt = 'salt'; - const stxToBurn = new BN(10); + const stxToBurn = BigInt(10); const publicKey = '03ef788b3830c00abe8f64f62dc32fc863bc0b2cafeb073b6c8e1c7657d9c2c3ab'; const makeUnsignedContractCall = jest.fn().mockResolvedValue({}); @@ -957,7 +956,7 @@ test('revokeName', async () => { test('renewName', async () => { const fullyQualifiedName = 'test.id'; - const stxToBurn = new BN(10); + const stxToBurn = BigInt(10); const newOwnerAddress = 'SPF0324DSC4K505TP6A8C7GAK4R95E38TGNZP7RE'; const zonefile = 'zonefile'; const publicKey = '03ef788b3830c00abe8f64f62dc32fc863bc0b2cafeb073b6c8e1c7657d9c2c3ab'; @@ -1023,7 +1022,7 @@ test('renewName', async () => { test('renewName optionalArguments', async () => { const fullyQualifiedName = 'test.id'; - const stxToBurn = new BN(10); + const stxToBurn = BigInt(10); const newOwnerAddress = undefined; const zonefile = undefined; const publicKey = '03ef788b3830c00abe8f64f62dc32fc863bc0b2cafeb073b6c8e1c7657d9c2c3ab'; diff --git a/packages/cli/package.json b/packages/cli/package.json index a493121cb..724016aba 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -80,6 +80,7 @@ "@types/jest": "^26.0.22", "@types/node-fetch": "^2.5.0", "@types/ripemd160": "^2.0.0", + "blockstack": "^19.2.2", "jest": "^26.6.3", "jest-fetch-mock": "^3.0.3", "jest-module-name-mapper": "^0.1.5", @@ -102,8 +103,6 @@ "bip32": "^2.0.6", "bip39": "^3.0.2", "bitcoinjs-lib": "^5.2.0", - "blockstack": "^19.2.2", - "bn.js": "^5.2.0", "c32check": "^1.1.3", "cors": "^2.8.4", "cross-fetch": "^3.1.4", diff --git a/packages/cli/src/cli.ts b/packages/cli/src/cli.ts index 61e1e732c..3968b4379 100644 --- a/packages/cli/src/cli.ts +++ b/packages/cli/src/cli.ts @@ -6,7 +6,6 @@ import * as fs from 'fs'; import * as winston from 'winston'; import cors from 'cors'; -import BN from 'bn.js'; import * as crypto from 'crypto'; import * as bip39 from 'bip39'; import express from 'express'; @@ -372,21 +371,10 @@ function balance(network: CLINetworkAdapter, args: string[]): Promise { return response.json(); }) .then(response => { - let balanceHex = response.balance; - if (response.balance.startsWith('0x')) { - balanceHex = response.balance.substr(2); - } - let lockedHex = response.locked; - if (response.locked.startsWith('0x')) { - lockedHex = response.locked.substr(2); - } - const unlockHeight = response.unlock_height; - const balance = new BN(balanceHex, 16); - const locked = new BN(lockedHex, 16); const res = { - balance: balance.toString(10), - locked: locked.toString(10), - unlock_height: unlockHeight, + balance: BigInt(response.balance).toString(10), + locked: BigInt(response.locked).toString(10), + unlock_height: response.unlock_height, nonce: response.nonce, }; return Promise.resolve(JSONStringify(res)); @@ -539,9 +527,9 @@ function getAccountHistory(network: CLINetworkAdapter, args: string[]): Promise< */ async function sendTokens(network: CLINetworkAdapter, args: string[]): Promise { const recipientAddress = args[0]; - const tokenAmount = new BN(args[1]); - const fee = new BN(args[2]); - const nonce = new BN(args[3]); + const tokenAmount = BigInt(args[1]); + const fee = BigInt(args[2]); + const nonce = BigInt(args[3]); const privateKey = args[4]; let memo = ''; @@ -605,8 +593,8 @@ async function sendTokens(network: CLINetworkAdapter, args: string[]): Promise { const sourceFile = args[0]; const contractName = args[1]; - const fee = new BN(args[2]); - const nonce = new BN(args[3]); + const fee = BigInt(args[2]); + const nonce = BigInt(args[3]); const privateKey = args[4]; const source = fs.readFileSync(sourceFile).toString(); @@ -668,8 +656,8 @@ async function contractFunctionCall(network: CLINetworkAdapter, args: string[]): const contractAddress = args[0]; const contractName = args[1]; const functionName = args[2]; - const fee = new BN(args[3]); - const nonce = new BN(args[4]); + const fee = BigInt(args[3]); + const nonce = BigInt(args[4]); const privateKey = args[5]; // temporary hack to use network config from stacks-transactions lib @@ -1517,7 +1505,7 @@ async function stackingStatus(network: CLINetworkAdapter, args: string[]): Promi } async function canStack(network: CLINetworkAdapter, args: string[]): Promise { - const amount = new BN(args[0]); + const amount = BigInt(args[0]); const cycles = Number(args[1]); const poxAddress = args[2]; const stxAddress = args[3]; @@ -1542,16 +1530,16 @@ async function canStack(network: CLINetworkAdapter, args: string[]): Promise { - const minAmount = new BN(poxInfo.min_amount_ustx); - const balanceBN = new BN(balance.stx.balance); + const minAmount = BigInt(poxInfo.min_amount_ustx); + const balanceBN = BigInt(balance.stx.balance); - if (minAmount.gt(amount)) { + if (minAmount > amount) { throw new Error( `Stacking amount less than required minimum of ${minAmount.toString()} microstacks` ); } - if (amount.gt(balanceBN)) { + if (amount > balanceBN) { throw new Error( `Stacking amount greater than account balance of ${balanceBN.toString()} microstacks` ); @@ -1569,7 +1557,7 @@ async function canStack(network: CLINetworkAdapter, args: string[]): Promise { - const amount = new BN(args[0]); + const amount = BigInt(args[0]); const cycles = Number(args[1]); const poxAddress = args[2]; const privateKey = args[3]; @@ -1610,18 +1598,18 @@ async function stack(network: CLINetworkAdapter, args: string[]): Promise { - const minAmount = new BN(poxInfo.min_amount_ustx); - const balanceBN = new BN(balance.stx.balance); + const minAmount = BigInt(poxInfo.min_amount_ustx); + const balanceBN = BigInt(balance.stx.balance); const burnChainBlockHeight = coreInfo.burn_block_height; const startBurnBlock = burnChainBlockHeight + 3; - if (minAmount.gt(amount)) { + if (minAmount > amount) { throw new Error( `Stacking amount less than required minimum of ${minAmount.toString()} microstacks` ); } - if (amount.gt(balanceBN)) { + if (amount > balanceBN) { throw new Error( `Stacking amount greater than account balance of ${balanceBN.toString()} microstacks` ); diff --git a/packages/cli/src/network.ts b/packages/cli/src/network.ts index 14940f6a3..c7001aea5 100644 --- a/packages/cli/src/network.ts +++ b/packages/cli/src/network.ts @@ -1,6 +1,5 @@ import blockstack from 'blockstack'; import * as bitcoin from 'bitcoinjs-lib'; -import BN from 'bn.js'; import fetch from 'node-fetch'; import { CLI_CONFIG_TYPE } from './argparse'; @@ -22,7 +21,7 @@ export interface CLI_NETWORK_OPTS { export interface PriceType { units: 'BTC' | 'STACKS'; - amount: import('bn.js'); + amount: bigint; } export type NameInfoType = { @@ -144,7 +143,7 @@ export class CLINetworkAdapter { return new Promise((resolve: any) => resolve({ units: String(this.priceUnits), - amount: new BN(this.priceToPay as string), + amount: BigInt(this.priceToPay || 0), } as PriceType) ); } @@ -154,7 +153,7 @@ export class CLINetworkAdapter { if (!priceInfo.units) { priceInfo = { units: 'BTC', - amount: new BN(String(priceInfo)), + amount: BigInt(priceInfo.amount), }; } return priceInfo; @@ -167,7 +166,7 @@ export class CLINetworkAdapter { return new Promise((resolve: any) => resolve({ units: String(this.priceUnits), - amount: new BN(String(this.priceToPay)), + amount: BigInt(this.priceToPay || 0), } as PriceType) ); } @@ -177,7 +176,7 @@ export class CLINetworkAdapter { if (!priceInfo.units) { priceInfo = { units: 'BTC', - amount: new BN(String(priceInfo)), + amount: BigInt(priceInfo.amount), } as PriceType; } return priceInfo; diff --git a/packages/common/package.json b/packages/common/package.json index ca496263c..28e3b7862 100644 --- a/packages/common/package.json +++ b/packages/common/package.json @@ -35,14 +35,15 @@ "url": "https://github.com/blockstack/blockstack.js/issues" }, "dependencies": { + "@types/bn.js": "^5.1.0", "@types/node": "^14.14.43", - "bn.js": "^5.2.0", "buffer": "^6.0.3", "cross-fetch": "^3.1.4" }, "devDependencies": { "@rollup/plugin-typescript": "^8.3.0", "@types/jest": "^26.0.22", + "bn.js": "^5.2.0", "jest": "^26.6.3", "jest-fetch-mock": "^3.0.3", "jest-module-name-mapper": "^0.1.5", diff --git a/packages/common/src/utils.ts b/packages/common/src/utils.ts index 06c0fdf26..99dca70a0 100644 --- a/packages/common/src/utils.ts +++ b/packages/common/src/utils.ts @@ -9,8 +9,6 @@ import { Buffer as BufferPolyfill } from 'buffer/'; // so export using the type definition from NodeJS (@types/node). import type { Buffer as NodeJSBuffer } from 'buffer'; -import BN from 'bn.js'; - const AvailableBufferModule: typeof NodeJSBuffer = // eslint-disable-next-line node/prefer-global/buffer typeof Buffer !== 'undefined' ? Buffer : (BufferPolyfill as any); @@ -330,17 +328,13 @@ export function getGlobalObjects>( } return result; } - +// After removing bn.js library provide backward compatibility for users passing bn.js instance +type BN = import('bn.js'); // Type only import from @types/bn.js export type IntegerType = number | string | bigint | Uint8Array | BN; // eslint-disable-next-line node/prefer-global/buffer export function intToBytes(value: IntegerType, signed: boolean, byteLength: number): Buffer { - return intToBN(value, signed).toArrayLike(AvailableBufferModule, 'be', byteLength); -} - -export function intToBN(value: IntegerType, signed: boolean): BN { - const bigInt = intToBigInt(value, signed); - return new BN(bigInt.toString()); + return toBuffer(intToBigInt(value, signed), byteLength); } export function intToBigInt(value: IntegerType, signed: boolean): bigint { @@ -380,17 +374,26 @@ export function intToBigInt(value: IntegerType, signed: boolean): bigint { // Allow byte arrays smaller than 128-bits to be passed. // This allows positive signed ints like `0x08` (8) or negative signed // ints like `0xf8` (-8) to be passed without having to pad to 16 bytes. - const bn = new BN(parsedValue, 'be').fromTwos(parsedValue.byteLength * 8); + const bn = fromTwos( + BigInt(`0x${bytesToHex(parsedValue)}`), + BigInt(parsedValue.byteLength * 8) + ); return BigInt(bn.toString()); } else { - return BigInt(new BN(parsedValue, 'be').toString()); + return BigInt(`0x${bytesToHex(parsedValue)}`); } } - if (parsedValue instanceof BN || BN.isBN(parsedValue)) { + // After removing bn.js library provide backward compatibility for users passing bn.js instance + // For backward compatibility with bn.js check if it's a bn.js instance + if ( + parsedValue != null && + typeof parsedValue === 'object' && + parsedValue.constructor.name === 'BN' + ) { return BigInt(parsedValue.toString()); } throw new TypeError( - `Invalid value type. Must be a number, bigint, integer-string, hex-string, BN.js instance, or Buffer.` + `Invalid value type. Must be a number, bigint, integer-string, hex-string, or Buffer.` ); } @@ -402,14 +405,159 @@ export function with0x(value: string): string { * Converts hex input string to bigint * @param hex - hex input string without 0x prefix and in big endian format * @example "6c7cde4d702830c1db34ef7c19e2776f59107afef39084776fc88bc78dbb9656" + * @ignore */ export function hexToBigInt(hex: string): bigint { if (typeof hex !== 'string') - throw new TypeError('hexToNumber: expected string, got ' + typeof hex); + throw new TypeError(`hexToBigInt: expected string, got ${typeof hex}`); // Big Endian return BigInt(`0x${hex}`); } -export function utf8ToBytes(content: string) { - return new TextEncoder().encode(content); +/** + * Converts IntegerType to hex string + * @ignore + */ +export function intToHex(integer: IntegerType, lengthBytes = 8): string { + const value = typeof integer === 'bigint' ? integer : intToBigInt(integer, false); + return value.toString(16).padStart(lengthBytes * 2, '0'); +} + +/** + * Converts hex string to integer + * @ignore + */ +export function hexToInt(hex: string): number { + return parseInt(hex, 16); +} + +/** + * Converts bigint to buffer type + * @param {value} bigint value to be converted into buffer + * @param {length} buffer optional length + * @return {Buffer} buffer instance in big endian format + */ +export function toBuffer(value: bigint, length: number = 16) { + const hex = intToHex(value, length); + // buffer instance in big endian format + return AvailableBufferModule.from(hexToBytes(hex)); +} + +/** + * Converts from signed number to two's complement + * MIN_VALUE = -(1 << (width - 1)) + * MAX_VALUE = (1 << (width - 1)) - 1 + * @ignore + */ +export function toTwos(value: bigint, width: bigint): bigint { + if ( + value < -(BigInt(1) << (width - BigInt(1))) || + (BigInt(1) << (width - BigInt(1))) - BigInt(1) < value + ) { + throw `Unable to represent integer in width: ${width}`; + } + if (value >= BigInt(0)) { + return BigInt(value); + } + return value + (BigInt(1) << width); +} + +/** + * Returns nth bit (right-to-left, zero-indexed) + */ +function nthBit(value: bigint, n: bigint) { + return value & (BigInt(1) << n); +} + +/** + * Converts from two's complement to signed number + * @ignore + */ +export function fromTwos(value: bigint, width: bigint) { + if (nthBit(value, width - BigInt(1))) { + return value - (BigInt(1) << width); + } + return value; +} + +// The following methods are based on `@noble/hashes` implementation +// https://github.com/paulmillr/noble-hashes +// Copyright (c) 2022 Paul Miller (https://paulmillr.com) +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the “Software”), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +const hexes = Array.from({ length: 256 }, (_, i) => i.toString(16).padStart(2, '0')); +/** + * @example bytesToHex(Uint8Array.from([0xde, 0xad, 0xbe, 0xef])) + * @ignore + */ +export function bytesToHex(uint8a: Uint8Array): string { + // pre-caching improves the speed 6x + if (!(uint8a instanceof Uint8Array)) throw new Error('Uint8Array expected'); + let hex = ''; + for (let i = 0; i < uint8a.length; i++) { + hex += hexes[uint8a[i]]; + } + return hex; +} + +/** + * @example hexToBytes('deadbeef') + * @ignore + */ +export function hexToBytes(hex: string): Uint8Array { + if (typeof hex !== 'string') { + throw new TypeError(`hexToBytes: expected string, got ${typeof hex}`); + } + if (hex.length % 2) throw new Error('hexToBytes: received invalid unpadded hex'); + const array = new Uint8Array(hex.length / 2); + for (let i = 0; i < array.length; i++) { + const j = i * 2; + const hexByte = hex.slice(j, j + 2); + const byte = Number.parseInt(hexByte, 16); + if (Number.isNaN(byte) || byte < 0) throw new Error('Invalid byte sequence'); + array[i] = byte; + } + return array; +} + +declare const TextEncoder: any; + +/** @ignore */ +export function utf8ToBytes(str: string): Uint8Array { + if (typeof str !== 'string') { + throw new TypeError(`utf8ToBytes expected string, got ${typeof str}`); + } + return new TextEncoder().encode(str); +} + +/** @ignore */ +export function toBytes(data: Uint8Array | string): Uint8Array { + if (typeof data === 'string') data = utf8ToBytes(data); + if (!(data instanceof Uint8Array)) + throw new TypeError(`Expected input type is Uint8Array (got ${typeof data})`); + return data; +} + +/** + * Concats Uint8Array-s into one; like `Buffer.concat([buf1, buf2])` + * @example concatBytes(buf1, buf2) + * @ignore + */ +export function concatBytes(...arrays: Uint8Array[]): Uint8Array { + if (!arrays.every(a => a instanceof Uint8Array)) throw new Error('Uint8Array list expected'); + if (arrays.length === 1) return arrays[0]; + const length = arrays.reduce((a, arr) => a + arr.length, 0); + const result = new Uint8Array(length); + for (let i = 0, pad = 0; i < arrays.length; i++) { + const arr = arrays[i]; + result.set(arr, pad); + pad += arr.length; + } + return result; } diff --git a/packages/common/tests/utils.test.ts b/packages/common/tests/utils.test.ts index 3d67f5a1c..da72cdb9b 100644 --- a/packages/common/tests/utils.test.ts +++ b/packages/common/tests/utils.test.ts @@ -1,4 +1,15 @@ -import { isSameOriginAbsoluteUrl, isLaterVersion } from '../src' +import { + isSameOriginAbsoluteUrl, + isLaterVersion, + intToHex, + hexToBytes, + bytesToHex, + fromTwos, + toTwos, + toBuffer, + intToBigInt, +} from '../src' +import BN from 'bn.js'; test('isLaterVersion', () => { expect(isLaterVersion('', '1.1.0')).toEqual(false) @@ -21,3 +32,90 @@ test('isSameOriginAbsoluteUrl', () => { expect(isSameOriginAbsoluteUrl('http://example.com', 'http://example.com:1234')).toEqual(false) expect(isSameOriginAbsoluteUrl('http://app.example.com', 'https://example.com/manifest.json')).toEqual(false) }) + +test('intToHex', () => { + const expected = '0000000000000010'; + + expect(intToHex(BigInt(16))).toEqual(expected); + expect(intToHex(16)).toEqual(expected); +}); + +test('hexToBytes & bytesToHex', () => { + const hex = 'ff'; + const bytes = Uint8Array.of(255); + + expect(hexToBytes(hex)).toEqual(bytes); + expect(bytesToHex(bytes)).toEqual(hex); +}); + +test('should return proper buffer', () => { + const n = BigInt('0x123456'); + + expect(toBuffer(n, 5).toString('hex')).toBe('0000123456'); + + const s = '211e1566be78319bb949470577c2d4'; + + for (let i = 1; i <= s.length; i++) { + const slice = (i % 2 === 0 ? '' : '0') + s.slice(0, i); + const bn = BigInt(`0x${slice}`); + expect(toBuffer(bn).toString('hex')).toBe(slice.padStart(32, '0')) + } +}); + +test('fromTwos', () => { + expect(Number(fromTwos(BigInt('0x00000000'),BigInt(32)))).toBe(0); + expect(Number(fromTwos(BigInt('0x00000001'),BigInt(32)))).toBe(1); + expect(Number(fromTwos(BigInt('0x7fffffff'),BigInt(32)))).toBe(2147483647); + expect(Number(fromTwos(BigInt('0x80000000'),BigInt(32)))).toBe(-2147483648); + expect(Number(fromTwos(BigInt('0xf0000000'),BigInt(32)))).toBe(-268435456); + expect(Number(fromTwos(BigInt('0xf1234567'),BigInt(32)))).toBe(-249346713); + expect(Number(fromTwos(BigInt('0xffffffff'),BigInt(32)))).toBe(-1); + expect(Number(fromTwos(BigInt('0xfffffffe'),BigInt(32)))).toBe(-2); + expect(Number(fromTwos(BigInt('0xfffffffffffffffffffffffffffffffe'),BigInt(128)))).toBe(-2); + expect(Number(fromTwos(BigInt('0xffffffffffffffffffffffffffffffff' + + 'fffffffffffffffffffffffffffffffe'),BigInt(256)))).toBe(-2); + expect(Number(fromTwos(BigInt('0xffffffffffffffffffffffffffffffff' + + 'ffffffffffffffffffffffffffffffff'),BigInt(256)))).toBe(-1); + expect( + fromTwos(BigInt('0x7fffffffffffffffffffffffffffffff' + + 'ffffffffffffffffffffffffffffffff'),BigInt(256)).toString(10)) + .toEqual(BigInt('5789604461865809771178549250434395392663499' + + '2332820282019728792003956564819967').toString(10)); + expect( + fromTwos(BigInt('0x80000000000000000000000000000000' + + '00000000000000000000000000000000'),BigInt(256)).toString(10)) + .toEqual(BigInt('-578960446186580977117854925043439539266349' + + '92332820282019728792003956564819968').toString(10)); +}); + +test('toTwos', () => { + expect(toTwos(BigInt(0), BigInt(32)).toString(16)).toEqual('0'); + expect(toTwos(BigInt(1), BigInt(32)).toString(16)).toEqual('1'); + expect(toTwos(BigInt(2147483647), BigInt(32)).toString(16)).toEqual('7fffffff'); + expect(toTwos(BigInt(-2147483648), BigInt(32)).toString(16)).toEqual('80000000'); + expect(toTwos(BigInt(-268435456), BigInt(32)).toString(16)).toEqual('f0000000'); + expect(toTwos(BigInt(-249346713), BigInt(32)).toString(16)).toEqual('f1234567'); + expect(toTwos(BigInt(-1), BigInt(32)).toString(16)).toEqual('ffffffff'); + expect(toTwos(BigInt(-2), BigInt(32)).toString(16)).toEqual('fffffffe'); + expect(toTwos(BigInt(-2), BigInt(128)).toString(16)).toEqual('fffffffffffffffffffffffffffffffe'); + expect(toTwos(BigInt(-2), BigInt(256)).toString(16)) + .toEqual('fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe'); + expect(toTwos(BigInt(-1), BigInt(256)).toString(16)) + .toEqual('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'); + expect(toTwos(BigInt('5789604461865809771178549250434395392663' + + '4992332820282019728792003956564819967'), BigInt(256)).toString(16)) + .toEqual('7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'); + expect(toTwos(BigInt('-578960446186580977117854925043439539266' + + '34992332820282019728792003956564819968'), BigInt(256)).toString(16)) + .toEqual('8000000000000000000000000000000000000000000000000000000000000000'); +}); + +test('Should accept bn.js instance', () => { + const value = '123456'; + const bn = new BN(value); + // After removing bn.js library verify backward compatibility for users passing bn.js instance + // Should not break if bn.js instance is passed + const nativeBigInt = intToBigInt(bn, false); + + expect(nativeBigInt.toString()).toEqual(value); +}); diff --git a/packages/encryption/package.json b/packages/encryption/package.json index 280d1c82b..35ad085bc 100644 --- a/packages/encryption/package.json +++ b/packages/encryption/package.json @@ -36,9 +36,7 @@ "@noble/secp256k1": "^1.5.5", "@scure/bip39": "^1.0.0", "@stacks/common": "^4.0.1", - "@types/bn.js": "^4.11.6", "@types/node": "^14.14.43", - "bn.js": "^5.2.0", "bs58": "^5.0.0", "ripemd160-min": "^0.0.6", "sha.js": "^2.4.11", diff --git a/packages/encryption/src/ec.ts b/packages/encryption/src/ec.ts index 59675e76a..26e87abd2 100644 --- a/packages/encryption/src/ec.ts +++ b/packages/encryption/src/ec.ts @@ -1,9 +1,14 @@ import { hmac } from '@noble/hashes/hmac'; import { sha256 } from '@noble/hashes/sha256'; -import { concatBytes, hexToBytes } from '@noble/hashes/utils'; import { getPublicKey, getSharedSecret, Point, signSync, utils, verify } from '@noble/secp256k1'; -import { Buffer, FailedDecryptionError } from '@stacks/common'; -import * as BN from 'bn.js'; +import { + Buffer, + toBuffer, + FailedDecryptionError, + concatBytes, + hexToBytes, + bytesToHex, +} from '@stacks/common'; import { createCipher } from './aesCipher'; import { createHmacSha256 } from './hmacSha256'; import { getPublicKeyFromPrivate } from './keys'; @@ -179,12 +184,12 @@ function isValidPublicKey(pub: string): { } /** - * Hex encodes a 32-byte BN.js instance. + * Hex encodes a 32-byte bigint instance. * The result string is zero padded and always 64 characters in length. * @ignore */ -export function getHexFromBN(bnInput: BN): string { - const hexOut = bnInput.toString('hex', 64); +export function getHexFromBN(bnInput: bigint): string { + const hexOut = bnInput.toString(16); if (hexOut.length === 64) { return hexOut; } else if (hexOut.length < 64) { @@ -198,14 +203,14 @@ export function getHexFromBN(bnInput: BN): string { } /** - * Returns a big-endian encoded 32-byte BN.js instance. + * Returns a big-endian encoded 32-byte buffer instance. * The result Buffer is zero padded and always 32 bytes in length. * @ignore */ -export function getBufferFromBN(bnInput: BN): Buffer { - const result = bnInput.toArrayLike(Buffer, 'be', 32); +export function getBufferFromBN(bnInput: bigint): Buffer { + const result = toBuffer(bnInput, 32); if (result.byteLength !== 32) { - throw new Error('Failed to generate a 32-byte BN'); + throw new Error('Failed to generate a 32-byte buffer instance'); } return result; } @@ -362,7 +367,7 @@ export async function encryptECIES( let cipherTextString: string; if (!cipherTextEncoding || cipherTextEncoding === 'hex') { - cipherTextString = utils.bytesToHex(cipherText); + cipherTextString = bytesToHex(cipherText); } else if (cipherTextEncoding === 'base64') { cipherTextString = cipherText.toString('base64'); } else { @@ -370,10 +375,10 @@ export async function encryptECIES( } const result: CipherObject = { - iv: utils.bytesToHex(initializationVector), - ephemeralPK: utils.bytesToHex(ephemeralPublicKey), + iv: bytesToHex(initializationVector), + ephemeralPK: bytesToHex(ephemeralPublicKey), cipherText: cipherTextString, - mac: utils.bytesToHex(mac), + mac: bytesToHex(mac), wasString, }; if (cipherTextEncoding && cipherTextEncoding !== 'hex') { @@ -465,7 +470,7 @@ export function signECDSA( const signature = signSync(contentHash, privateKey); return { - signature: utils.bytesToHex(signature), + signature: bytesToHex(signature), publicKey, }; } diff --git a/packages/encryption/tests/encryption.test.ts b/packages/encryption/tests/encryption.test.ts index c0f1a3534..5b9a48b50 100644 --- a/packages/encryption/tests/encryption.test.ts +++ b/packages/encryption/tests/encryption.test.ts @@ -1,17 +1,21 @@ -import * as triplesec from 'triplesec' -import * as elliptic from 'elliptic' -import * as webCryptoPolyfill from '@peculiar/webcrypto' +import * as triplesec from 'triplesec'; +import * as elliptic from 'elliptic'; +import * as webCryptoPolyfill from '@peculiar/webcrypto'; import { - encryptECIES, decryptECIES, getHexFromBN, signECDSA, - verifyECDSA, encryptMnemonic, decryptMnemonic, -} from '../src' -import { ERROR_CODES, getGlobalScope, utf8ToBytes } from '@stacks/common'; -import * as pbkdf2 from '../src/pbkdf2' -import * as aesCipher from '../src/aesCipher' -import * as sha2Hash from '../src/sha2Hash' -import * as hmacSha256 from '../src/hmacSha256' -import * as ripemd160 from '../src/hashRipemd160' -import BN from 'bn.js' + encryptECIES, + decryptECIES, + getHexFromBN, + signECDSA, + verifyECDSA, + encryptMnemonic, + decryptMnemonic, +} from '../src'; +import { bytesToHex, ERROR_CODES, getGlobalScope, utf8ToBytes } from '@stacks/common'; +import * as pbkdf2 from '../src/pbkdf2'; +import * as aesCipher from '../src/aesCipher'; +import * as sha2Hash from '../src/sha2Hash'; +import * as hmacSha256 from '../src/hmacSha256'; +import * as ripemd160 from '../src/hashRipemd160'; // https://github.com/paulmillr/scure-bip39 // Secure, audited & minimal implementation of BIP39 mnemonic phrases. import { validateMnemonic, mnemonicToEntropy, entropyToMnemonic } from '@scure/bip39'; @@ -21,17 +25,18 @@ import { validateMnemonic, mnemonicToEntropy, entropyToMnemonic } from '@scure/b // Very small in size as compared to bitcoinjs/bip39 wordlist // Reference: https://github.com/paulmillr/scure-bip39 import { wordlist } from '@scure/bip39/wordlists/english'; -import { getBufferFromBN } from '../src/ec' +import { getBufferFromBN } from '../src/ec'; import { getPublicKey as nobleGetPublicKey, getSharedSecret, signSync as nobleSecp256k1Sign, utils, - verify as nobleSecp256k1Verify + verify as nobleSecp256k1Verify, } from '@noble/secp256k1'; +import { Buffer } from '@stacks/common'; -const privateKey = 'a5c61c6ca7b3e7e55edee68566aeab22e4da26baa285c7bd10e8d2218aa3b229' -const publicKey = '027d28f9951ce46538951e3697c62588a87f1f1f295de4a14fdd4c780fc52cfe69' +const privateKey = 'a5c61c6ca7b3e7e55edee68566aeab22e4da26baa285c7bd10e8d2218aa3b229'; +const publicKey = '027d28f9951ce46538951e3697c62588a87f1f1f295de4a14fdd4c780fc52cfe69'; test('ripemd160 digest tests', async () => { const vectors = [ @@ -40,299 +45,323 @@ test('ripemd160 digest tests', async () => { ['a', '0bdc9d2d256b3ee9daae347be6f4dc835a467ffe'], ['abc', '8eb208f7e05d987a9b044a8e98c6b087f15a0bfc'], ['message digest', '5d0689ef49d2fae572b881b123a85ffa21595f36'], - ['', '9c1185a5c5e9fc54612808977ee8f548b2258d31'] - ] - const nodeCryptoHasher = await ripemd160.createHashRipemd160() - expect(nodeCryptoHasher instanceof ripemd160.NodeCryptoRipemd160Digest).toEqual(true) + ['', '9c1185a5c5e9fc54612808977ee8f548b2258d31'], + ]; + const nodeCryptoHasher = await ripemd160.createHashRipemd160(); + expect(nodeCryptoHasher instanceof ripemd160.NodeCryptoRipemd160Digest).toEqual(true); for (const [input, expected] of vectors) { - const result = await nodeCryptoHasher.digest(Buffer.from(input)) - const resultHex = result.toString('hex') - expect(resultHex).toEqual(expected) + const result = await nodeCryptoHasher.digest(Buffer.from(input)); + const resultHex = result.toString('hex'); + expect(resultHex).toEqual(expected); } - const polyfillHasher = new ripemd160.Ripemd160PolyfillDigest() + const polyfillHasher = new ripemd160.Ripemd160PolyfillDigest(); for (const [input, expected] of vectors) { - const result = await polyfillHasher.digest(Buffer.from(input)) - const resultHex = result.toString('hex') - expect(resultHex).toEqual(expected) + const result = await polyfillHasher.digest(Buffer.from(input)); + const resultHex = result.toString('hex'); + expect(resultHex).toEqual(expected); } - const nodeCrypto = require('crypto') - const createHashOrig = nodeCrypto.createHash - nodeCrypto.createHash = () => { throw new Error('Artificial broken hash') } + const nodeCrypto = require('crypto'); + const createHashOrig = nodeCrypto.createHash; + nodeCrypto.createHash = () => { + throw new Error('Artificial broken hash'); + }; try { - await ripemd160.hashRipemd160(Buffer.from('acb')) + await ripemd160.hashRipemd160(Buffer.from('acb')); } finally { - nodeCrypto.createHash = createHashOrig + nodeCrypto.createHash = createHashOrig; } -}) +}); test('sha2 digest tests', async () => { - const globalScope = getGlobalScope() as any + const globalScope = getGlobalScope() as any; // Remove any existing global `crypto` variable for testing - const globalCryptoOrig = { defined: 'crypto' in globalScope, value: globalScope.crypto } + const globalCryptoOrig = { defined: 'crypto' in globalScope, value: globalScope.crypto }; // Set global web `crypto` polyfill for testing - const webCrypto = new webCryptoPolyfill.Crypto() - globalScope.crypto = webCrypto + const webCrypto = new webCryptoPolyfill.Crypto(); + globalScope.crypto = webCrypto; const vectors = [ - ['abc', - 'ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad', - 'ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f'], - ['', - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - 'cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e'], - ['abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq', - '248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1', - '204a8fc6dda82f0a0ced7beb8e08a41657c16ef468b228a8279be331a703c33596fd15c13b1b07f9aa1d3bea57789ca031ad85c7a71dd70354ec631238ca3445'] - ] + [ + 'abc', + 'ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad', + 'ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f', + ], + [ + '', + 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', + 'cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e', + ], + [ + 'abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq', + '248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1', + '204a8fc6dda82f0a0ced7beb8e08a41657c16ef468b228a8279be331a703c33596fd15c13b1b07f9aa1d3bea57789ca031ad85c7a71dd70354ec631238ca3445', + ], + ]; try { - const webCryptoHasher = await sha2Hash.createSha2Hash() - expect(webCryptoHasher instanceof sha2Hash.WebCryptoSha2Hash).toBe(true) + const webCryptoHasher = await sha2Hash.createSha2Hash(); + expect(webCryptoHasher instanceof sha2Hash.WebCryptoSha2Hash).toBe(true); for (const [input, expected256, expected512] of vectors) { - const result256 = await webCryptoHasher.digest(Buffer.from(input), 'sha256') - expect(result256.toString('hex')).toEqual(expected256) - const result512 = await webCryptoHasher.digest(Buffer.from(input), 'sha512') - expect(result512.toString('hex')).toEqual(expected512) + const result256 = await webCryptoHasher.digest(Buffer.from(input), 'sha256'); + expect(result256.toString('hex')).toEqual(expected256); + const result512 = await webCryptoHasher.digest(Buffer.from(input), 'sha512'); + expect(result512.toString('hex')).toEqual(expected512); } - const nodeCryptoHasher = new sha2Hash.NodeCryptoSha2Hash(require('crypto').createHash) + const nodeCryptoHasher = new sha2Hash.NodeCryptoSha2Hash(require('crypto').createHash); for (const [input, expected256, expected512] of vectors) { - const result256 = await nodeCryptoHasher.digest(Buffer.from(input), 'sha256') - expect(result256.toString('hex')).toEqual(expected256) - const result512 = await nodeCryptoHasher.digest(Buffer.from(input), 'sha512') - expect(result512.toString('hex')).toEqual(expected512) + const result256 = await nodeCryptoHasher.digest(Buffer.from(input), 'sha256'); + expect(result256.toString('hex')).toEqual(expected256); + const result512 = await nodeCryptoHasher.digest(Buffer.from(input), 'sha512'); + expect(result512.toString('hex')).toEqual(expected512); } } finally { // Restore previous `crypto` global var if (globalCryptoOrig.defined) { - globalScope.crypto = globalCryptoOrig.value + globalScope.crypto = globalCryptoOrig.value; } else { - delete globalScope.crypto + delete globalScope.crypto; } } -}) +}); test('sha2 native digest fallback tests', async () => { - const input = Buffer.from('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq') - const expectedOutput256 = '248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1' - const expectedOutput512 = '204a8fc6dda82f0a0ced7beb8e08a41657c16ef468b228a8279be331a703c33596fd15c13b1b07f9aa1d3bea57789ca031ad85c7a71dd70354ec631238ca3445' + const input = Buffer.from('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq'); + const expectedOutput256 = '248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1'; + const expectedOutput512 = + '204a8fc6dda82f0a0ced7beb8e08a41657c16ef468b228a8279be331a703c33596fd15c13b1b07f9aa1d3bea57789ca031ad85c7a71dd70354ec631238ca3445'; // Test WebCrypto fallback - const webCryptoSubtle = new webCryptoPolyfill.Crypto().subtle - webCryptoSubtle.digest = () => { throw new Error('Artificial broken hash') } - const nodeCryptoHasher = new sha2Hash.WebCryptoSha2Hash(webCryptoSubtle) - const result256 = await nodeCryptoHasher.digest(input, 'sha256') - expect(result256.toString('hex')).toEqual(expectedOutput256) - const result512 = await nodeCryptoHasher.digest(input, 'sha512') - expect(result512.toString('hex')).toEqual(expectedOutput512) + const webCryptoSubtle = new webCryptoPolyfill.Crypto().subtle; + webCryptoSubtle.digest = () => { + throw new Error('Artificial broken hash'); + }; + const nodeCryptoHasher = new sha2Hash.WebCryptoSha2Hash(webCryptoSubtle); + const result256 = await nodeCryptoHasher.digest(input, 'sha256'); + expect(result256.toString('hex')).toEqual(expectedOutput256); + const result512 = await nodeCryptoHasher.digest(input, 'sha512'); + expect(result512.toString('hex')).toEqual(expectedOutput512); // Test Node.js `crypto.createHash` fallback - const nodeCrypto = require('crypto') - const createHashOrig = nodeCrypto.createHash - nodeCrypto.createHash = () => { throw new Error('Artificial broken hash') } + const nodeCrypto = require('crypto'); + const createHashOrig = nodeCrypto.createHash; + nodeCrypto.createHash = () => { + throw new Error('Artificial broken hash'); + }; try { - const nodeCryptoHasher = new sha2Hash.NodeCryptoSha2Hash(require('crypto').createHash) - const result256 = await nodeCryptoHasher.digest(input, 'sha256') - expect(result256.toString('hex')).toEqual(expectedOutput256) - const result512 = await nodeCryptoHasher.digest(input, 'sha512') - expect(result512.toString('hex')).toEqual(expectedOutput512) + const nodeCryptoHasher = new sha2Hash.NodeCryptoSha2Hash(require('crypto').createHash); + const result256 = await nodeCryptoHasher.digest(input, 'sha256'); + expect(result256.toString('hex')).toEqual(expectedOutput256); + const result512 = await nodeCryptoHasher.digest(input, 'sha512'); + expect(result512.toString('hex')).toEqual(expectedOutput512); } finally { - nodeCrypto.createHash = createHashOrig + nodeCrypto.createHash = createHashOrig; } -}) +}); test('hmac-sha256 tests', async () => { - const key = Buffer.alloc(32, 0xf5) - const data = Buffer.alloc(100, 0x44) - const expected = 'fe44c2197eb8a5678daba87ff2aba891d8b12224d8219acd4cfa5cee4f9acc77' + const key = Buffer.alloc(32, 0xf5); + const data = Buffer.alloc(100, 0x44); + const expected = 'fe44c2197eb8a5678daba87ff2aba891d8b12224d8219acd4cfa5cee4f9acc77'; - const globalScope = getGlobalScope() as any + const globalScope = getGlobalScope() as any; // Remove any existing global `crypto` variable for testing - const globalCryptoOrig = { defined: 'crypto' in globalScope, value: globalScope.crypto } - delete globalScope.crypto + const globalCryptoOrig = { defined: 'crypto' in globalScope, value: globalScope.crypto }; + delete globalScope.crypto; try { - const nodeCryptoHmac = await hmacSha256.createHmacSha256() - expect(nodeCryptoHmac instanceof hmacSha256.NodeCryptoHmacSha256).toEqual(true) + const nodeCryptoHmac = await hmacSha256.createHmacSha256(); + expect(nodeCryptoHmac instanceof hmacSha256.NodeCryptoHmacSha256).toEqual(true); // Set global web `crypto` polyfill for testing - const webCrypto = new webCryptoPolyfill.Crypto() - globalScope.crypto = webCrypto - const webCryptoHmac = await hmacSha256.createHmacSha256() - expect(webCryptoHmac instanceof hmacSha256.WebCryptoHmacSha256).toEqual(true) + const webCrypto = new webCryptoPolyfill.Crypto(); + globalScope.crypto = webCrypto; + const webCryptoHmac = await hmacSha256.createHmacSha256(); + expect(webCryptoHmac instanceof hmacSha256.WebCryptoHmacSha256).toEqual(true); - const derivedNodeCrypto = (await nodeCryptoHmac.digest(key, data)).toString('hex') - const derivedWebCrypto = (await webCryptoHmac.digest(key, data)).toString('hex') + const derivedNodeCrypto = (await nodeCryptoHmac.digest(key, data)).toString('hex'); + const derivedWebCrypto = (await webCryptoHmac.digest(key, data)).toString('hex'); - expect(expected).toEqual(derivedNodeCrypto) - expect(expected).toEqual(derivedWebCrypto) + expect(expected).toEqual(derivedNodeCrypto); + expect(expected).toEqual(derivedWebCrypto); } finally { // Restore previous `crypto` global var if (globalCryptoOrig.defined) { - globalScope.crypto = globalCryptoOrig.value + globalScope.crypto = globalCryptoOrig.value; } else { - delete globalScope.crypto + delete globalScope.crypto; } } -}) +}); test('pbkdf2 digest tests', async () => { - const salt = Buffer.alloc(16, 0xf0) - const password = 'password123456' - const digestAlgo = 'sha512' - const iterations = 100000 - const keyLength = 48 + const salt = Buffer.alloc(16, 0xf0); + const password = 'password123456'; + const digestAlgo = 'sha512'; + const iterations = 100000; + const keyLength = 48; - const globalScope = getGlobalScope() as any + const globalScope = getGlobalScope() as any; // Remove any existing global `crypto` variable for testing - const globalCryptoOrig = { defined: 'crypto' in globalScope, value: globalScope.crypto } - delete globalScope.crypto + const globalCryptoOrig = { defined: 'crypto' in globalScope, value: globalScope.crypto }; + delete globalScope.crypto; try { - const nodeCryptoPbkdf2 = await pbkdf2.createPbkdf2() - expect(nodeCryptoPbkdf2 instanceof pbkdf2.NodeCryptoPbkdf2).toEqual(true) + const nodeCryptoPbkdf2 = await pbkdf2.createPbkdf2(); + expect(nodeCryptoPbkdf2 instanceof pbkdf2.NodeCryptoPbkdf2).toEqual(true); // Set global web `crypto` polyfill for testing - const webCrypto = new webCryptoPolyfill.Crypto() - globalScope.crypto = webCrypto - const webCryptoPbkdf2 = await pbkdf2.createPbkdf2() - expect(webCryptoPbkdf2 instanceof pbkdf2.WebCryptoPbkdf2).toEqual(true) - - const polyFillPbkdf2 = new pbkdf2.WebCryptoPartialPbkdf2(webCrypto.subtle) - - const derivedNodeCrypto = (await nodeCryptoPbkdf2 - .derive(password, salt, iterations, keyLength, digestAlgo)).toString('hex') - const derivedWebCrypto = (await webCryptoPbkdf2 - .derive(password, salt, iterations, keyLength, digestAlgo)).toString('hex') - const derivedPolyFill = (await polyFillPbkdf2 - .derive(password, salt, iterations, keyLength, digestAlgo)).toString('hex') - - const expected = '92f603459cc45a33eeb6ee06bb75d12bb8e61d9f679668392362bb104eab6d95027398e02f500c849a3dd1ccd63fb310' - expect(expected).toEqual(derivedNodeCrypto) - expect(expected).toEqual(derivedWebCrypto) - expect(expected).toEqual(derivedPolyFill) - + const webCrypto = new webCryptoPolyfill.Crypto(); + globalScope.crypto = webCrypto; + const webCryptoPbkdf2 = await pbkdf2.createPbkdf2(); + expect(webCryptoPbkdf2 instanceof pbkdf2.WebCryptoPbkdf2).toEqual(true); + + const polyFillPbkdf2 = new pbkdf2.WebCryptoPartialPbkdf2(webCrypto.subtle); + + const derivedNodeCrypto = ( + await nodeCryptoPbkdf2.derive(password, salt, iterations, keyLength, digestAlgo) + ).toString('hex'); + const derivedWebCrypto = ( + await webCryptoPbkdf2.derive(password, salt, iterations, keyLength, digestAlgo) + ).toString('hex'); + const derivedPolyFill = ( + await polyFillPbkdf2.derive(password, salt, iterations, keyLength, digestAlgo) + ).toString('hex'); + + const expected = + '92f603459cc45a33eeb6ee06bb75d12bb8e61d9f679668392362bb104eab6d95027398e02f500c849a3dd1ccd63fb310'; + expect(expected).toEqual(derivedNodeCrypto); + expect(expected).toEqual(derivedWebCrypto); + expect(expected).toEqual(derivedPolyFill); } finally { // Restore previous `crypto` global var if (globalCryptoOrig.defined) { - globalScope.crypto = globalCryptoOrig.value + globalScope.crypto = globalCryptoOrig.value; } else { - delete globalScope.crypto + delete globalScope.crypto; } } -}) +}); test('aes-cbc tests', async () => { - const globalScope = getGlobalScope() as any + const globalScope = getGlobalScope() as any; // Remove any existing global `crypto` variable for testing - const globalCryptoOrig = { defined: 'crypto' in globalScope, value: globalScope.crypto } - delete globalScope.crypto + const globalCryptoOrig = { defined: 'crypto' in globalScope, value: globalScope.crypto }; + delete globalScope.crypto; try { - const nodeCryptoAesCipher = await aesCipher.createCipher() - expect(nodeCryptoAesCipher instanceof aesCipher.NodeCryptoAesCipher).toEqual(true) + const nodeCryptoAesCipher = await aesCipher.createCipher(); + expect(nodeCryptoAesCipher instanceof aesCipher.NodeCryptoAesCipher).toEqual(true); // Set global web `crypto` polyfill for testing - const webCrypto = new webCryptoPolyfill.Crypto() - globalScope.crypto = webCrypto - const webCryptoAesCipher = await aesCipher.createCipher() - expect(webCryptoAesCipher instanceof aesCipher.WebCryptoAesCipher).toEqual(true) - - const key128 = Buffer.from('0f'.repeat(16), 'hex') - const key256 = Buffer.from('0f'.repeat(32), 'hex') - const iv = Buffer.from('f7'.repeat(16), 'hex') - const inputData = Buffer.from('TestData'.repeat(20)) - const inputDataHex = inputData.toString('hex') - - const expected128Cbc = '5aa1100a0a3133c9184dc661bc95c675a0fe5f02a67880f50702f8c88e7a445248d6dedfca80e72d00c3d277ea025eebde5940265fa00c1bfe80aebf3968b6eaf0564eda6ddd9e97548be1fa6d487e71353b11136193782d76d3b8d1895047e08a121c1706c083ceefdb9605a75a2310cccee1b0aaca632230f45f1172001cad96ae6d15db38ab9eed27b27b6f80353a5f30e3532a526a834a0f8273ffb2e9caaa92843b40c893e298f3b472fb26b11f' - const expected128CbcBuffer = Buffer.from(expected128Cbc, 'hex') - - const expected256Cbc = '66a21fa53680d8182a79c1b90cdc38d398fe34d85c7ca5d45b8381fea4a84536e38514b3bcdba06655314607534be7ea370952ed6f334af709efc6504e600ce0b7c20fe3b469c29b63a391983b74aa12f1d859b477092c61e7814bd6c8d143ec21d34f79468c74c97ae9763ec11695e1e9a3a3b33f12561ecef9fbae79ddf7f2701c97ba1531801862662a9ce87a880934318a9e46a3941367fa68da3340f83941211aba7ec741826ff35d4f880243db' - const expected256CbcBuffer = Buffer.from(expected256Cbc, 'hex') + const webCrypto = new webCryptoPolyfill.Crypto(); + globalScope.crypto = webCrypto; + const webCryptoAesCipher = await aesCipher.createCipher(); + expect(webCryptoAesCipher instanceof aesCipher.WebCryptoAesCipher).toEqual(true); + + const key128 = Buffer.from('0f'.repeat(16), 'hex'); + const key256 = Buffer.from('0f'.repeat(32), 'hex'); + const iv = Buffer.from('f7'.repeat(16), 'hex'); + const inputData = Buffer.from('TestData'.repeat(20)); + const inputDataHex = inputData.toString('hex'); + + const expected128Cbc = + '5aa1100a0a3133c9184dc661bc95c675a0fe5f02a67880f50702f8c88e7a445248d6dedfca80e72d00c3d277ea025eebde5940265fa00c1bfe80aebf3968b6eaf0564eda6ddd9e97548be1fa6d487e71353b11136193782d76d3b8d1895047e08a121c1706c083ceefdb9605a75a2310cccee1b0aaca632230f45f1172001cad96ae6d15db38ab9eed27b27b6f80353a5f30e3532a526a834a0f8273ffb2e9caaa92843b40c893e298f3b472fb26b11f'; + const expected128CbcBuffer = Buffer.from(expected128Cbc, 'hex'); + + const expected256Cbc = + '66a21fa53680d8182a79c1b90cdc38d398fe34d85c7ca5d45b8381fea4a84536e38514b3bcdba06655314607534be7ea370952ed6f334af709efc6504e600ce0b7c20fe3b469c29b63a391983b74aa12f1d859b477092c61e7814bd6c8d143ec21d34f79468c74c97ae9763ec11695e1e9a3a3b33f12561ecef9fbae79ddf7f2701c97ba1531801862662a9ce87a880934318a9e46a3941367fa68da3340f83941211aba7ec741826ff35d4f880243db'; + const expected256CbcBuffer = Buffer.from(expected256Cbc, 'hex'); // Test aes-256-cbc encrypt - const encrypted256NodeCrypto = (await nodeCryptoAesCipher - .encrypt('aes-256-cbc', key256, iv, inputData)).toString('hex') - const encrypted256WebCrypto = (await webCryptoAesCipher - .encrypt('aes-256-cbc', key256, iv, inputData)).toString('hex') + const encrypted256NodeCrypto = ( + await nodeCryptoAesCipher.encrypt('aes-256-cbc', key256, iv, inputData) + ).toString('hex'); + const encrypted256WebCrypto = ( + await webCryptoAesCipher.encrypt('aes-256-cbc', key256, iv, inputData) + ).toString('hex'); - expect(expected256Cbc).toEqual(encrypted256NodeCrypto) - expect(expected256Cbc).toEqual(encrypted256WebCrypto) + expect(expected256Cbc).toEqual(encrypted256NodeCrypto); + expect(expected256Cbc).toEqual(encrypted256WebCrypto); // Test aes-256-cbc decrypt - const decrypted256NodeCrypto = (await nodeCryptoAesCipher - .decrypt('aes-256-cbc', key256, iv, expected256CbcBuffer)).toString('hex') - const decrypted256WebCrypto = (await webCryptoAesCipher - .decrypt('aes-256-cbc', key256, iv, expected256CbcBuffer)).toString('hex') + const decrypted256NodeCrypto = ( + await nodeCryptoAesCipher.decrypt('aes-256-cbc', key256, iv, expected256CbcBuffer) + ).toString('hex'); + const decrypted256WebCrypto = ( + await webCryptoAesCipher.decrypt('aes-256-cbc', key256, iv, expected256CbcBuffer) + ).toString('hex'); - expect(inputDataHex).toEqual(decrypted256NodeCrypto) - expect(inputDataHex).toEqual(decrypted256WebCrypto) + expect(inputDataHex).toEqual(decrypted256NodeCrypto); + expect(inputDataHex).toEqual(decrypted256WebCrypto); // Test aes-128-cbc encrypt - const encrypted128NodeCrypto = (await nodeCryptoAesCipher - .encrypt('aes-128-cbc', key128, iv, inputData)).toString('hex') - const encrypted128WebCrypto = (await webCryptoAesCipher - .encrypt('aes-128-cbc', key128, iv, inputData)).toString('hex') + const encrypted128NodeCrypto = ( + await nodeCryptoAesCipher.encrypt('aes-128-cbc', key128, iv, inputData) + ).toString('hex'); + const encrypted128WebCrypto = ( + await webCryptoAesCipher.encrypt('aes-128-cbc', key128, iv, inputData) + ).toString('hex'); - expect(expected128Cbc).toEqual(encrypted128NodeCrypto) - expect(expected128Cbc).toEqual(encrypted128WebCrypto) + expect(expected128Cbc).toEqual(encrypted128NodeCrypto); + expect(expected128Cbc).toEqual(encrypted128WebCrypto); // Test aes-128-cbc decrypt - const decrypted128NodeCrypto = (await nodeCryptoAesCipher - .decrypt('aes-128-cbc', key128, iv, expected128CbcBuffer)).toString('hex') - const decrypted128WebCrypto = (await webCryptoAesCipher - .decrypt('aes-128-cbc', key128, iv, expected128CbcBuffer)).toString('hex') - - expect(inputDataHex).toEqual(decrypted128NodeCrypto) - expect(inputDataHex).toEqual(decrypted128WebCrypto) - + const decrypted128NodeCrypto = ( + await nodeCryptoAesCipher.decrypt('aes-128-cbc', key128, iv, expected128CbcBuffer) + ).toString('hex'); + const decrypted128WebCrypto = ( + await webCryptoAesCipher.decrypt('aes-128-cbc', key128, iv, expected128CbcBuffer) + ).toString('hex'); + + expect(inputDataHex).toEqual(decrypted128NodeCrypto); + expect(inputDataHex).toEqual(decrypted128WebCrypto); } finally { // Restore previous `crypto` global var if (globalCryptoOrig.defined) { - globalScope.crypto = globalCryptoOrig.value + globalScope.crypto = globalCryptoOrig.value; } else { - delete globalScope.crypto + delete globalScope.crypto; } } -}) +}); test('encrypt-to-decrypt works', async () => { - const testString = 'all work and no play makes jack a dull boy' - let cipherObj = await encryptECIES(publicKey, Buffer.from(testString), true) - let deciphered = await decryptECIES(privateKey, cipherObj) - expect(deciphered).toEqual(testString) - - const testBuffer = Buffer.from(testString) - cipherObj = await encryptECIES(publicKey, testBuffer, false) - deciphered = await decryptECIES(privateKey, cipherObj) - expect(deciphered.toString('hex')).toEqual(testBuffer.toString('hex')) -}) + const testString = 'all work and no play makes jack a dull boy'; + let cipherObj = await encryptECIES(publicKey, Buffer.from(testString), true); + let deciphered = await decryptECIES(privateKey, cipherObj); + expect(deciphered).toEqual(testString); + + const testBuffer = Buffer.from(testString); + cipherObj = await encryptECIES(publicKey, testBuffer, false); + deciphered = await decryptECIES(privateKey, cipherObj); + expect(deciphered.toString('hex')).toEqual(testBuffer.toString('hex')); +}); test('encrypt-to-decrypt fails on bad mac', async () => { - const testString = 'all work and no play makes jack a dull boy' - const cipherObj = await encryptECIES(publicKey, Buffer.from(testString), true) - const evilString = 'some work and some play makes jack a dull boy' - const evilObj = await encryptECIES(publicKey, Buffer.from(evilString), true) + const testString = 'all work and no play makes jack a dull boy'; + const cipherObj = await encryptECIES(publicKey, Buffer.from(testString), true); + const evilString = 'some work and some play makes jack a dull boy'; + const evilObj = await encryptECIES(publicKey, Buffer.from(evilString), true); - cipherObj.cipherText = evilObj.cipherText + cipherObj.cipherText = evilObj.cipherText; try { - await decryptECIES(privateKey, cipherObj) - expect(false).toEqual(true) + await decryptECIES(privateKey, cipherObj); + expect(false).toEqual(true); } catch (e: any) { - - expect(e.code).toEqual(ERROR_CODES.FAILED_DECRYPTION_ERROR) - expect(e.message.indexOf('failure in MAC check')).not.toEqual(-1) + expect(e.code).toEqual(ERROR_CODES.FAILED_DECRYPTION_ERROR); + expect(e.message.indexOf('failure in MAC check')).not.toEqual(-1); } -}) +}); test('Should be able to prevent a public key twist attack for secp256k1', async () => { const curve = new elliptic.ec('secp256k1'); @@ -350,14 +379,14 @@ test('Should be able to prevent a public key twist attack for secp256k1', async } catch (error: any) { expect(error.reason).toEqual('IsNotPoint'); } -}) +}); test('Should be able to accept public key with valid point on secp256k', async () => { const curve = new elliptic.ec('secp256k1'); // Pick a valid point on secp256k to generate a public key. const point = curve.keyFromPublic({ x: '0C6047F9441ED7D6D3045406E95C07CD85C778E4B8CEF3CA7ABAC09B95C709EE5', - y: '1AE168FEA63DC339A3C58419466CEAEEF7F632653266D0E1236431A950CFE52A' + y: '1AE168FEA63DC339A3C58419466CEAEEF7F632653266D0E1236431A950CFE52A', }); const goodPublicKey = point.getPublic('hex'); try { @@ -368,7 +397,7 @@ test('Should be able to accept public key with valid point on secp256k', async ( } catch (error: any) { expect(error.reason).toEqual('IsNotPoint'); } -}) +}); test('Should reject public key having invalid length', async () => { const invalidPublicKey = '0273d28f9951ce46538951e3697c62588a87f1f1f295de4a14fdd4c780fc52cfe69'; @@ -380,7 +409,7 @@ test('Should reject public key having invalid length', async () => { } catch (error: any) { expect(error.reason).toEqual('InvalidFormat'); } -}) +}); test('Should accept public key having valid length', async () => { const publicKey = '027d28f9951ce46538951e3697c62588a87f1f1f295de4a14fdd4c780fc52cfe69'; @@ -392,10 +421,11 @@ test('Should accept public key having valid length', async () => { } catch (error: any) { expect(error.reason).toEqual('InvalidFormat'); } -}) +}); test('Should reject invalid uncompressed public key', async () => { - const invalidPublicKey = '02ad90e5b6bc86b3ec7fac2c5fbda7423fc8ef0d58df594c773fa05e2c281b2bfe877677c668bd13603944e34f4818ee03cadd81a88542b8b4d5431264180e2c28'; + const invalidPublicKey = + '02ad90e5b6bc86b3ec7fac2c5fbda7423fc8ef0d58df594c773fa05e2c281b2bfe877677c668bd13603944e34f4818ee03cadd81a88542b8b4d5431264180e2c28'; try { const testString = 'all work and no play makes jack a dull boy'; await encryptECIES(invalidPublicKey, Buffer.from(testString), true); @@ -404,10 +434,11 @@ test('Should reject invalid uncompressed public key', async () => { } catch (error: any) { expect(error.reason).toEqual('InvalidFormat'); } -}) +}); test('Should accept valid uncompressed public key', async () => { - const publicKey = '04ad90e5b6bc86b3ec7fac2c5fbda7423fc8ef0d58df594c773fa05e2c281b2bfe877677c668bd13603944e34f4818ee03cadd81a88542b8b4d5431264180e2c28'; + const publicKey = + '04ad90e5b6bc86b3ec7fac2c5fbda7423fc8ef0d58df594c773fa05e2c281b2bfe877677c668bd13603944e34f4818ee03cadd81a88542b8b4d5431264180e2c28'; try { const testString = 'all work and no play makes jack a dull boy'; await encryptECIES(publicKey, Buffer.from(testString), true); @@ -416,7 +447,7 @@ test('Should accept valid uncompressed public key', async () => { } catch (error: any) { expect(error.reason).toEqual('InvalidFormat'); } -}) +}); test('Should reject invalid compressed public key', async () => { const invalidPublicKey = '017d28f9951ce46538951e3697c62588a87f1f1f295de4a14fdd4c780fc52cfe69'; @@ -428,7 +459,7 @@ test('Should reject invalid compressed public key', async () => { } catch (error: any) { expect(error.reason).toEqual('InvalidFormat'); } -}) +}); test('Should accept valid compressed public key', async () => { const publicKey = '027d28f9951ce46538951e3697c62588a87f1f1f295de4a14fdd4c780fc52cfe69'; @@ -440,132 +471,152 @@ test('Should accept valid compressed public key', async () => { } catch (error: any) { expect(error.reason).toEqual('InvalidFormat'); } -}) +}); test('sign-to-verify-works', async () => { - const testString = 'all work and no play makes jack a dull boy' - let sigObj = await signECDSA(privateKey, testString) - expect(await verifyECDSA(testString, sigObj.publicKey, sigObj.signature)).toEqual(true) + const testString = 'all work and no play makes jack a dull boy'; + let sigObj = await signECDSA(privateKey, testString); + expect(await verifyECDSA(testString, sigObj.publicKey, sigObj.signature)).toEqual(true); - const testBuffer = Buffer.from(testString) - sigObj = await signECDSA(privateKey, testBuffer) - expect(await verifyECDSA(testBuffer, sigObj.publicKey, sigObj.signature)).toEqual(true) -}) + const testBuffer = Buffer.from(testString); + sigObj = await signECDSA(privateKey, testBuffer); + expect(await verifyECDSA(testBuffer, sigObj.publicKey, sigObj.signature)).toEqual(true); +}); test('sign-to-verify-fails', async () => { - const testString = 'all work and no play makes jack a dull boy' - const failString = 'I should fail' + const testString = 'all work and no play makes jack a dull boy'; + const failString = 'I should fail'; - let sigObj = await signECDSA(privateKey, testString) - expect(await verifyECDSA(failString, sigObj.publicKey, sigObj.signature)).toEqual(false) + let sigObj = await signECDSA(privateKey, testString); + expect(await verifyECDSA(failString, sigObj.publicKey, sigObj.signature)).toEqual(false); - const testBuffer = Buffer.from(testString) - sigObj = await signECDSA(privateKey, testBuffer) - expect(await verifyECDSA(Buffer.from(failString), sigObj.publicKey, sigObj.signature)).toEqual(false) + const testBuffer = Buffer.from(testString); + sigObj = await signECDSA(privateKey, testBuffer); + expect(await verifyECDSA(Buffer.from(failString), sigObj.publicKey, sigObj.signature)).toEqual( + false + ); - const badPK = '0288580b020800f421d746f738b221d384f098e911b81939d8c94df89e74cba776' - sigObj = await signECDSA(privateKey, testBuffer) - expect(await verifyECDSA(Buffer.from(failString), badPK, sigObj.signature)).toEqual(false) -}) + const badPK = '0288580b020800f421d746f738b221d384f098e911b81939d8c94df89e74cba776'; + sigObj = await signECDSA(privateKey, testBuffer); + expect(await verifyECDSA(Buffer.from(failString), badPK, sigObj.signature)).toEqual(false); +}); test('bn-padded-to-64-bytes', () => { - const ecurve = new elliptic.ec('secp256k1') - - const evilHexes = ['ba40f85b152bea8c3812da187bcfcfb0dc6e15f9e27cb073633b1c787b19472f', - 'e346010f923f768138152d0bad063999ff1da5361a81e6e6f9106241692a0076'] - const results = evilHexes.map((hex) => { - const ephemeralSK = ecurve.keyFromPrivate(hex) - const ephemeralPK = ephemeralSK.getPublic() - const sharedSecret = ephemeralSK.derive(ephemeralPK) - return getHexFromBN(sharedSecret).length === 64 - }) + const ecurve = new elliptic.ec('secp256k1'); + + const evilHexes = [ + 'ba40f85b152bea8c3812da187bcfcfb0dc6e15f9e27cb073633b1c787b19472f', + 'e346010f923f768138152d0bad063999ff1da5361a81e6e6f9106241692a0076', + ]; + const results = evilHexes.map(hex => { + const ephemeralSK = ecurve.keyFromPrivate(hex); + const ephemeralPK = ephemeralSK.getPublic(); + const sharedSecret = ephemeralSK.derive(ephemeralPK); + return getHexFromBN(BigInt(sharedSecret.toString())).length === 64; + }); - expect(results.every(x => x)).toEqual(true) + expect(results.every(x => x)).toEqual(true); - const bnBuffer = getBufferFromBN(new BN(123)) + const bnBuffer = getBufferFromBN(BigInt(123)); - expect(bnBuffer.byteLength).toEqual(32) - expect(bnBuffer.toString('hex')).toEqual(getHexFromBN(new BN(123))) -}) + expect(bnBuffer.byteLength).toEqual(32); + expect(bnBuffer.toString('hex')).toEqual(getHexFromBN(BigInt(123))); +}); test('encryptMnemonic & decryptMnemonic', async () => { - - const rawPhrase = 'march eager husband pilot waste rely exclude taste ' - + 'twist donkey actress scene' - const rawPassword = 'testtest' - const encryptedPhrase = 'ffffffffffffffffffffffffffffffffca638cc39fc270e8be5c' - + 'bf98347e42a52ee955e287ab589c571af5f7c80269295b0039e32ae13adf11bc6506f5ec' - + '32dda2f79df4c44276359c6bac178ae393de' - - const preEncryptedPhrase = '7573f4f51089ba7ce2b95542552b7504de7305398637733' - + '0579649dfbc9e664073ba614fac180d3dc237b21eba57f9aee5702ba819fe17a0752c4dc7' - + '94884c9e75eb60da875f778bbc1aaca1bd373ea3' - - const legacyPhrase = 'vivid oxygen neutral wheat find thumb cigar wheel ' - + 'board kiwi portion business' - const legacyPassword = 'supersecret' - const legacyEncrypted = '1c94d7de0000000304d583f007c71e6e5fef354c046e8c64b1' - + 'adebd6904dcb007a1222f07313643873455ab2a3ab3819e99d518cc7d33c18bde02494aa' - + '74efc35a8970b2007b2fc715f6067cee27f5c92d020b1806b0444994aab80050a6732131' - + 'd2947a51bacb3952fb9286124b3c2b3196ff7edce66dee0dbd9eb59558e0044bddb3a78f' - + '48a66cf8d78bb46bb472bd2d5ec420c831fc384293252459524ee2d668869f33c586a944' - + '67d0ce8671260f4cc2e87140c873b6ca79fb86c6d77d134d7beb2018845a9e71e6c7ecde' - + 'dacd8a676f1f873c5f9c708cc6070642d44d2505aa9cdba26c50ad6f8d3e547fb0cba710' - + 'a7f7be54ff7ea7e98a809ddee5ef85f6f259b3a17a8d8dbaac618b80fe266a1e63ec19e4' - + '76bee9177b51894ee' + const rawPhrase = + 'march eager husband pilot waste rely exclude taste ' + 'twist donkey actress scene'; + const rawPassword = 'testtest'; + const encryptedPhrase = + 'ffffffffffffffffffffffffffffffffca638cc39fc270e8be5c' + + 'bf98347e42a52ee955e287ab589c571af5f7c80269295b0039e32ae13adf11bc6506f5ec' + + '32dda2f79df4c44276359c6bac178ae393de'; + + const preEncryptedPhrase = + '7573f4f51089ba7ce2b95542552b7504de7305398637733' + + '0579649dfbc9e664073ba614fac180d3dc237b21eba57f9aee5702ba819fe17a0752c4dc7' + + '94884c9e75eb60da875f778bbc1aaca1bd373ea3'; + + const legacyPhrase = + 'vivid oxygen neutral wheat find thumb cigar wheel ' + 'board kiwi portion business'; + const legacyPassword = 'supersecret'; + const legacyEncrypted = + '1c94d7de0000000304d583f007c71e6e5fef354c046e8c64b1' + + 'adebd6904dcb007a1222f07313643873455ab2a3ab3819e99d518cc7d33c18bde02494aa' + + '74efc35a8970b2007b2fc715f6067cee27f5c92d020b1806b0444994aab80050a6732131' + + 'd2947a51bacb3952fb9286124b3c2b3196ff7edce66dee0dbd9eb59558e0044bddb3a78f' + + '48a66cf8d78bb46bb472bd2d5ec420c831fc384293252459524ee2d668869f33c586a944' + + '67d0ce8671260f4cc2e87140c873b6ca79fb86c6d77d134d7beb2018845a9e71e6c7ecde' + + 'dacd8a676f1f873c5f9c708cc6070642d44d2505aa9cdba26c50ad6f8d3e547fb0cba710' + + 'a7f7be54ff7ea7e98a809ddee5ef85f6f259b3a17a8d8dbaac618b80fe266a1e63ec19e4' + + '76bee9177b51894ee'; // Test encryption -> decryption. Can't be done with hard-coded values // due to random salt. await encryptMnemonic(rawPhrase, rawPassword) - .then(encoded => decryptMnemonic(encoded.toString('hex'), rawPassword, triplesec.decrypt), - (err) => { - fail(`Should encrypt mnemonic phrase, instead errored: ${err}`) - }) - .then((decoded: string) => { - expect(decoded.toString() === rawPhrase).toEqual(true) - }, (err) => { - fail(`Should decrypt encrypted phrase, instead errored: ${err}`) - }) + .then( + encoded => decryptMnemonic(encoded.toString('hex'), rawPassword, triplesec.decrypt), + err => { + fail(`Should encrypt mnemonic phrase, instead errored: ${err}`); + } + ) + .then( + (decoded: string) => { + expect(decoded.toString() === rawPhrase).toEqual(true); + }, + err => { + fail(`Should decrypt encrypted phrase, instead errored: ${err}`); + } + ); // // Test encryption with mocked randomBytes generator to use same salt try { - const mockSalt = Buffer.from('ff'.repeat(16), 'hex') - const encoded = await encryptMnemonic(rawPhrase, rawPassword, {getRandomBytes: () => mockSalt}) - expect(encoded.toString('hex') === encryptedPhrase).toEqual(true) + const mockSalt = Buffer.from('ff'.repeat(16), 'hex'); + const encoded = await encryptMnemonic(rawPhrase, rawPassword, { + getRandomBytes: () => mockSalt, + }); + expect(encoded.toString('hex') === encryptedPhrase).toEqual(true); } catch (err) { - fail(`Should have encrypted phrase with deterministic salt, instead errored: ${err}`) + fail(`Should have encrypted phrase with deterministic salt, instead errored: ${err}`); } // // Test decryption with mocked randomBytes generator to use same salt try { - const decoded = await decryptMnemonic(Buffer.from(encryptedPhrase, 'hex'), rawPassword, triplesec.decrypt) - expect(decoded === rawPhrase).toEqual(true) + const decoded = await decryptMnemonic( + Buffer.from(encryptedPhrase, 'hex'), + rawPassword, + triplesec.decrypt + ); + expect(decoded === rawPhrase).toEqual(true); } catch (err) { - fail(`Should have decrypted phrase with deterministic salt, instead errored: ${err}`) + fail(`Should have decrypted phrase with deterministic salt, instead errored: ${err}`); } // // Test valid input (No salt, so it's the same every time) - await decryptMnemonic(legacyEncrypted, legacyPassword, triplesec.decrypt).then((decoded) => { - expect(decoded === legacyPhrase).toEqual(true) - }, (err) => { - fail(`Should decrypt legacy encrypted phrase, instead errored: ${err}`) - }) + await decryptMnemonic(legacyEncrypted, legacyPassword, triplesec.decrypt).then( + decoded => { + expect(decoded === legacyPhrase).toEqual(true); + }, + err => { + fail(`Should decrypt legacy encrypted phrase, instead errored: ${err}`); + } + ); - const errorCallback = jest.fn() + const errorCallback = jest.fn(); // // Invalid inputs await encryptMnemonic('not a mnemonic phrase', 'password').then(() => { - fail('Should have thrown on invalid mnemonic input') - }, errorCallback) + fail('Should have thrown on invalid mnemonic input'); + }, errorCallback); - expect(errorCallback).toHaveBeenCalledTimes(1) + expect(errorCallback).toHaveBeenCalledTimes(1); await decryptMnemonic(preEncryptedPhrase, 'incorrect password', triplesec.decrypt).then(() => { - fail('Should have thrown on incorrect password for decryption') - }, errorCallback) + fail('Should have thrown on incorrect password for decryption'); + }, errorCallback); - expect(errorCallback).toHaveBeenCalledTimes(2) -}) + expect(errorCallback).toHaveBeenCalledTimes(2); +}); test('Shared secret from a keypair should be same using elliptic or noble', () => { // Consider a privateKey, publicKey pair and get shared secret using noble and then elliptic @@ -576,7 +627,7 @@ test('Shared secret from a keypair should be same using elliptic or noble', () = const sharedSecretNobleBuffer = Buffer.from(sharedSecretNoble).slice(1); //Step 2: Get shared secret using elliptic library - const ecurve = new elliptic.ec('secp256k1') + const ecurve = new elliptic.ec('secp256k1'); const ecPK = ecurve.keyFromPublic(publicKey, 'hex').getPublic(); const keyPair = ecurve.keyFromPrivate(privateKey); @@ -600,7 +651,7 @@ test('Sign msg using elliptic/secp256k1 and verify signature using @noble/secp25 const keyPair = ec.genKeyPair(options); const msg = 'hello world'; - const msgHex = utils.bytesToHex(utf8ToBytes(msg)); + const msgHex = bytesToHex(utf8ToBytes(msg)); // Sign msg using elliptic/secp256k1 // input must be an array, or a hex-string @@ -629,7 +680,7 @@ test('Sign msg using @noble/secp256k1 and verify signature using elliptic/secp25 const privateKey = utils.randomPrivateKey(); const msg = 'hello world'; - const msgHex = utils.bytesToHex(utf8ToBytes(msg)); + const msgHex = bytesToHex(utf8ToBytes(msg)); // Sign msg using @noble/secp256k1 // input must be a hex-string @@ -661,9 +712,11 @@ test('Verify compatibility @scure/bip39 <=> bitcoinjs/bip39', () => { const entropyUint8Array = new Uint8Array(entropy.split('').map(Number)); // Based on Aaron comment do not import bitcoinjs/bip39 for these tests - const bitcoinjsBip39 = { // Consider it equivalent to bitcoinjs/bip39 (offloaded now) + const bitcoinjsBip39 = { + // Consider it equivalent to bitcoinjs/bip39 (offloaded now) // Using this map of required functions from bitcoinjs/bip39 and mocking the output for considered entropy - entropyToMnemonicBip39: (_: string) => 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about', + entropyToMnemonicBip39: (_: string) => + 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about', validateMnemonicBip39: (_: string) => true, mnemonicToEntropyBip39: (_: string) => '00000000000000000000000000000000', }; @@ -696,8 +749,7 @@ test('Verify compatibility @scure/bip39 <=> bitcoinjs/bip39', () => { // mnemonicToEntropyBip39 imported from bitcoinjs/bip39 const entropyString = bitcoinjsBip39.mnemonicToEntropyBip39(mnemonic); // Convert entropy to bytes - const entropyInBytes = new Uint8Array(entropyString.split('').map(Number)) + const entropyInBytes = new Uint8Array(entropyString.split('').map(Number)); // entropy should match with entropyUint8Array expect(entropyInBytes).toEqual(entropyUint8Array); }); - diff --git a/packages/keychain/package.json b/packages/keychain/package.json index 50436f483..dbf8e7daa 100644 --- a/packages/keychain/package.json +++ b/packages/keychain/package.json @@ -70,13 +70,11 @@ "@stacks/profile": "^4.0.0", "@stacks/storage": "^4.0.0", "@stacks/transactions": "^4.0.0", - "@types/bn.js": "^4.11.6", "@types/node": "^14.14.43", "@types/triplesec": "^3.0.0", "bip32": "^2.0.6", "bip39": "^3.0.2", "bitcoinjs-lib": "^5.2.0", - "bn.js": "^5.2.0", "c32check": "^1.1.3", "jsontokens": "^3.0.0", "randombytes": "^2.1.0", diff --git a/packages/keychain/src/wallet/signer.ts b/packages/keychain/src/wallet/signer.ts index 9393e8ac0..86e9a916e 100644 --- a/packages/keychain/src/wallet/signer.ts +++ b/packages/keychain/src/wallet/signer.ts @@ -14,7 +14,6 @@ import { import { StacksTestnet, StacksNetwork, StacksNetworkName } from '@stacks/network'; import RPCClient from '@blockstack/rpc-client'; -import BN from 'bn.js'; interface ContractCallOptions { contractName: string; @@ -120,7 +119,7 @@ export class WalletSigner { codeBody: codeBody, senderKey: this.getSTXPrivateKey().toString('hex'), network: this.getNetwork(), - nonce: new BN(nonce), + nonce: BigInt(nonce), postConditionMode, postConditions, anchorMode, @@ -139,11 +138,11 @@ export class WalletSigner { }: STXTransferOptions) { const tx = await makeSTXTokenTransfer({ recipient, - amount: new BN(amount), + amount: BigInt(amount), memo, senderKey: this.getSTXPrivateKey().toString('hex'), network: this.getNetwork(), - nonce: new BN(nonce), + nonce: BigInt(nonce), postConditionMode, postConditions, anchorMode, diff --git a/packages/stacking/package.json b/packages/stacking/package.json index 9ccff0f28..3aa17eb4f 100644 --- a/packages/stacking/package.json +++ b/packages/stacking/package.json @@ -40,9 +40,7 @@ "@stacks/network": "^4.0.1", "@stacks/stacks-blockchain-api-types": "^0.61.0", "@stacks/transactions": "^4.0.0", - "@types/bn.js": "^4.11.6", - "bitcoinjs-lib": "^5.2.0", - "bn.js": "^5.2.0" + "bitcoinjs-lib": "^5.2.0" }, "devDependencies": { "@types/jest": "^26.0.22", diff --git a/packages/stacking/src/index.ts b/packages/stacking/src/index.ts index 727bcee76..44ca7c77d 100644 --- a/packages/stacking/src/index.ts +++ b/packages/stacking/src/index.ts @@ -1,4 +1,5 @@ -import { Buffer, IntegerType, intToBigInt } from '@stacks/common'; +// @ts-ignore +import { Buffer, IntegerType, intToBigInt, toBuffer } from '@stacks/common'; import { makeContractCall, bufferCV, @@ -30,7 +31,6 @@ import { BurnchainRewardSlotHolderListResponse, } from '@stacks/stacks-blockchain-api-types'; import { StacksNetwork } from '@stacks/network'; -import BN from 'bn.js'; import { StackingErrors } from './constants'; import { fetchPrivate } from '@stacks/common'; import { decodeBtcAddress } from './utils'; @@ -326,7 +326,7 @@ export class StackingClient { return Promise.all([balancePromise, poxInfoPromise]) .then(([balance, poxInfo]) => { const { hashMode, data } = decodeBtcAddress(poxAddress); - const hashModeBuffer = bufferCV(new BN(hashMode, 10).toArrayLike(Buffer)); + const hashModeBuffer = bufferCV(toBuffer(BigInt(hashMode))); const hashbytes = bufferCV(data); const poxAddressCV = tupleCV({ hashbytes, @@ -528,7 +528,7 @@ export class StackingClient { burnBlockHeight: number; }) { const { hashMode, data } = decodeBtcAddress(poxAddress); - const hashModeBuffer = bufferCV(new BN(hashMode, 10).toArrayLike(Buffer)); + const hashModeBuffer = bufferCV(toBuffer(BigInt(hashMode))); const hashbytes = bufferCV(data); const address = tupleCV({ hashbytes, @@ -566,7 +566,7 @@ export class StackingClient { if (poxAddress) { const { hashMode, data } = decodeBtcAddress(poxAddress); - const hashModeBuffer = bufferCV(new BN(hashMode, 10).toArrayLike(Buffer)); + const hashModeBuffer = bufferCV(toBuffer(BigInt(hashMode))); const hashbytes = bufferCV(data); address = someCV( tupleCV({ @@ -613,7 +613,7 @@ export class StackingClient { nonce?: IntegerType; }) { const { hashMode, data } = decodeBtcAddress(poxAddress); - const hashModeBuffer = bufferCV(new BN(hashMode, 10).toArrayLike(Buffer)); + const hashModeBuffer = bufferCV(toBuffer(BigInt(hashMode))); const hashbytes = bufferCV(data); const address = tupleCV({ hashbytes, @@ -655,7 +655,7 @@ export class StackingClient { rewardCycle: number; }) { const { hashMode, data } = decodeBtcAddress(poxAddress); - const hashModeBuffer = bufferCV(new BN(hashMode, 10).toArrayLike(Buffer)); + const hashModeBuffer = bufferCV(toBuffer(BigInt(hashMode))); const hashbytes = bufferCV(data); const address = tupleCV({ hashbytes, diff --git a/packages/stacking/src/utils.ts b/packages/stacking/src/utils.ts index 890ecf58d..4a9c9e80b 100644 --- a/packages/stacking/src/utils.ts +++ b/packages/stacking/src/utils.ts @@ -8,7 +8,6 @@ import { TupleCV, } from '@stacks/transactions'; import { address } from 'bitcoinjs-lib'; -import BN from 'bn.js'; import { StackingErrors } from './constants'; export class InvalidAddressError extends Error { @@ -140,7 +139,7 @@ export function poxAddressToBtcAddress(...args: PoxAddressArgs): string { } export function getBTCAddress(version: Buffer, checksum: Buffer) { - const btcAddress = address.toBase58Check(checksum, new BN(version).toNumber()); + const btcAddress = address.toBase58Check(checksum, Number(version.toString())); return btcAddress; } diff --git a/packages/stacking/tests/stacking.test.ts b/packages/stacking/tests/stacking.test.ts index 3dfd8a855..dd7f792fa 100644 --- a/packages/stacking/tests/stacking.test.ts +++ b/packages/stacking/tests/stacking.test.ts @@ -1,6 +1,6 @@ import { StacksTestnet } from '@stacks/network'; +import { Buffer, toBuffer } from '@stacks/common'; import fetchMock from 'jest-fetch-mock'; -import BN from 'bn.js'; import { StackingErrors } from '../src/constants'; import { uintCV, @@ -223,7 +223,7 @@ test('stack stx', async () => { const address = 'ST3XKKN4RPV69NN1PHFDNX3TYKXT7XPC4N8KC1ARH'; const poxAddress = '1Xik14zRm29UsyS6DjhYg4iZeZqsDa8D3'; const network = new StacksTestnet(); - const amountMicroStx = new BN(100000000000); + const amountMicroStx = BigInt(100000000000); const cycles = 10; const privateKey = 'd48f215481c16cbe6426f8e557df9b78895661971d71735126545abddcd5377001'; const burnBlockHeight = 2000; @@ -263,7 +263,7 @@ test('stack stx', async () => { }); const { version, hash } = btcAddress.fromBase58Check(poxAddress); - const versionBuffer = bufferCV(new BN(version, 10).toBuffer()); + const versionBuffer = bufferCV(toBuffer(BigInt(version))); const hashbytes = bufferCV(hash); const poxAddressCV = tupleCV({ hashbytes, @@ -299,7 +299,7 @@ test('delegate stx', async () => { const delegateTo = 'ST2MCYPWTFMD2MGR5YY695EJG0G1R4J2BTJPRGM7H'; const poxAddress = '1Xik14zRm29UsyS6DjhYg4iZeZqsDa8D3'; const network = new StacksTestnet(); - const amountMicroStx = new BN(100000000000); + const amountMicroStx = BigInt(100000000000); const untilBurnBlockHeight = 2000; const privateKey = 'd48f215481c16cbe6426f8e557df9b78895661971d71735126545abddcd5377001'; @@ -340,7 +340,7 @@ test('delegate stx', async () => { }); const { version, hash } = btcAddress.fromBase58Check(poxAddress); - const versionBuffer = bufferCV(new BN(version, 10).toBuffer()); + const versionBuffer = bufferCV(toBuffer(BigInt(version))); const hashbytes = bufferCV(hash); const poxAddressCV = tupleCV({ hashbytes, @@ -375,7 +375,7 @@ test('delegate stx with empty optional parameters', async () => { const address = 'ST3XKKN4RPV69NN1PHFDNX3TYKXT7XPC4N8KC1ARH'; const delegateTo = 'ST2MCYPWTFMD2MGR5YY695EJG0G1R4J2BTJPRGM7H'; const network = new StacksTestnet(); - const amountMicroStx = new BN(100000000000); + const amountMicroStx = BigInt(100000000000); const privateKey = 'd48f215481c16cbe6426f8e557df9b78895661971d71735126545abddcd5377001'; const transaction = { serialize: () => 'mocktxhex' } @@ -445,7 +445,7 @@ test('delegate stack stx with one delegator', async () => { const address = 'ST2MCYPWTFMD2MGR5YY695EJG0G1R4J2BTJPRGM7H'; const poxAddress = '1Xik14zRm29UsyS6DjhYg4iZeZqsDa8D3'; const network = new StacksTestnet(); - const amountMicroStx = new BN(100000000000); + const amountMicroStx = BigInt(100000000000); const burnBlockHeight = 2000; const cycles = 10; const privateKey = 'd48f215481c16cbe6426f8e557df9b78895661971d71735126545abddcd5377001'; @@ -497,7 +497,7 @@ test('delegate stack stx with one delegator', async () => { }); const { version, hash } = btcAddress.fromBase58Check(poxAddress); - const versionBuffer = bufferCV(new BN(version, 10).toBuffer()); + const versionBuffer = bufferCV(toBuffer(BigInt(version))); const hashbytes = bufferCV(hash); const poxAddressCV = tupleCV({ hashbytes, @@ -534,11 +534,11 @@ test('delegate stack stx with set nonce', async () => { const address = 'ST2MCYPWTFMD2MGR5YY695EJG0G1R4J2BTJPRGM7H'; const poxAddress = '1Xik14zRm29UsyS6DjhYg4iZeZqsDa8D3'; const network = new StacksTestnet(); - const amountMicroStx = new BN(100000000000); + const amountMicroStx = BigInt(100000000000); const burnBlockHeight = 2000; const cycles = 10; const privateKey = 'd48f215481c16cbe6426f8e557df9b78895661971d71735126545abddcd5377001'; - const nonce = new BN(1); + const nonce = BigInt(1); const transaction = { serialize: () => 'mocktxhex' } const makeContractCall = jest.fn().mockResolvedValue(transaction); @@ -588,7 +588,7 @@ test('delegate stack stx with set nonce', async () => { }); const { version, hash } = btcAddress.fromBase58Check(poxAddress); - const versionBuffer = bufferCV(new BN(version, 10).toBuffer()); + const versionBuffer = bufferCV(toBuffer(BigInt(version))); const hashbytes = bufferCV(hash); const poxAddressCV = tupleCV({ hashbytes, @@ -662,7 +662,7 @@ test('delegator commit', async () => { }); const { version, hash } = btcAddress.fromBase58Check(poxAddress); - const versionBuffer = bufferCV(new BN(version, 10).toBuffer()); + const versionBuffer = bufferCV(toBuffer(BigInt(version))); const hashbytes = bufferCV(hash); const poxAddressCV = tupleCV({ hashbytes, @@ -953,8 +953,9 @@ test('get account balance', async () => { const responseBalanceInfo = await client.getAccountBalance(); expect(fetchMock.mock.calls[0][0]).toEqual(network.getAccountApiUrl(address)); - expect(responseBalanceInfo.toString()).toEqual(new BN(balanceInfo.balance.substr(2), 'hex').toString()); + expect(responseBalanceInfo.toString()).toEqual(BigInt(balanceInfo.balance).toString()); }) + test('get seconds until next cycle', async () => { const address = 'ST3XKKN4RPV69NN1PHFDNX3TYKXT7XPC4N8KC1ARH'; const network = new StacksTestnet(); diff --git a/packages/transactions/package.json b/packages/transactions/package.json index a8449aeaa..57e37c6d8 100644 --- a/packages/transactions/package.json +++ b/packages/transactions/package.json @@ -44,10 +44,8 @@ "@noble/secp256k1": "^1.5.5", "@stacks/common": "^4.0.1", "@stacks/network": "^4.0.1", - "@types/bn.js": "^4.11.6", "@types/node": "^14.14.43", "@types/sha.js": "^2.4.0", - "bn.js": "^5.2.0", "c32check": "^1.1.3", "cross-fetch": "^3.1.4", "lodash.clonedeep": "^4.5.0", diff --git a/packages/transactions/src/clarity/serialize.ts b/packages/transactions/src/clarity/serialize.ts index 9c1c3f5ee..5cfe96da1 100644 --- a/packages/transactions/src/clarity/serialize.ts +++ b/packages/transactions/src/clarity/serialize.ts @@ -1,4 +1,4 @@ -import { Buffer } from '@stacks/common'; +import { Buffer, toTwos, toBuffer } from '@stacks/common'; import { serializeAddress, serializeLPString } from '../types'; import { createLPString } from '../postcondition-types'; import { @@ -19,7 +19,6 @@ import { BufferArray } from '../utils'; import { SerializationError } from '../errors'; import { StringAsciiCV, StringUtf8CV } from './types/stringCV'; import { CLARITY_INT_BYTE_SIZE, CLARITY_INT_SIZE } from '../constants'; -import BN from 'bn.js'; function bufferWithTypeID(typeId: ClarityType, buffer: Buffer): Buffer { const id = Buffer.from([typeId]); @@ -45,14 +44,12 @@ function serializeBufferCV(cv: BufferCV): Buffer { } function serializeIntCV(cv: IntCV): Buffer { - const buffer = new BN(cv.value.toString()) - .toTwos(CLARITY_INT_SIZE) - .toArrayLike(Buffer, 'be', CLARITY_INT_BYTE_SIZE); + const buffer = toBuffer(toTwos(cv.value, BigInt(CLARITY_INT_SIZE)), CLARITY_INT_BYTE_SIZE); return bufferWithTypeID(cv.type, buffer); } function serializeUIntCV(cv: UIntCV): Buffer { - const buffer = new BN(cv.value.toString()).toArrayLike(Buffer, 'be', CLARITY_INT_BYTE_SIZE); + const buffer = toBuffer(cv.value, CLARITY_INT_BYTE_SIZE); return bufferWithTypeID(cv.type, buffer); } diff --git a/packages/transactions/src/keys.ts b/packages/transactions/src/keys.ts index f022e0880..c3204f3cc 100644 --- a/packages/transactions/src/keys.ts +++ b/packages/transactions/src/keys.ts @@ -9,7 +9,10 @@ import { } from '@noble/secp256k1'; import { Buffer, + bytesToHex, hexToBigInt, + hexToInt, + intToHex, privateKeyToBuffer, PRIVATE_KEY_COMPRESSED_LENGTH, } from '@stacks/common'; @@ -31,14 +34,7 @@ import { TransactionVersion, UNCOMPRESSED_PUBKEY_LENGTH_BYTES, } from './constants'; -import { - BufferArray, - hash160, - hashP2PKH, - hexStringToInt, - intToHexString, - leftPadHexToLength, -} from './utils'; +import { BufferArray, hash160, hashP2PKH, leftPadHexToLength } from './utils'; /** * To use secp256k1.signSync set utils.hmacSha256Sync to a function using noble-hashes @@ -121,11 +117,11 @@ export function serializePublicKey(key: StacksPublicKey): Buffer { export function pubKeyfromPrivKey(privateKey: string | Buffer): StacksPublicKey { const privKey = createStacksPrivateKey(privateKey); const publicKey = nobleGetPublicKey(privKey.data.slice(0, 32), privKey.compressed); - return createStacksPublicKey(utils.bytesToHex(publicKey)); + return createStacksPublicKey(bytesToHex(publicKey)); } export function compressPublicKey(publicKey: string | Buffer): StacksPublicKey { - const hex = typeof publicKey === 'string' ? publicKey : utils.bytesToHex(publicKey); + const hex = typeof publicKey === 'string' ? publicKey : bytesToHex(publicKey); const compressed = Point.fromHex(hex).toHex(true); return createStacksPublicKey(compressed); } @@ -153,7 +149,7 @@ export function createStacksPrivateKey(key: string | Buffer): StacksPrivateKey { } export function makeRandomPrivKey(): StacksPrivateKey { - return createStacksPrivateKey(utils.bytesToHex(utils.randomPrivateKey())); + return createStacksPrivateKey(bytesToHex(utils.randomPrivateKey())); } export function signWithKey(privateKey: StacksPrivateKey, input: string): MessageSignature { @@ -169,7 +165,7 @@ export function signWithKey(privateKey: StacksPrivateKey, input: string): Messag if (recoveryParam === undefined || recoveryParam === null) { throw new Error('"signature.recoveryParam" is not set'); } - const recoveryParamHex = intToHexString(recoveryParam, 1); + const recoveryParamHex = intToHex(recoveryParam, 1); const recoverableSignatureString = recoveryParamHex + r + s; return createMessageSignature(recoverableSignatureString); } @@ -180,7 +176,7 @@ export function getSignatureRecoveryParam(signature: string) { throw new Error('Invalid signature'); } const recoveryParamHex = signature.substr(0, 2); - return hexStringToInt(recoveryParamHex); + return hexToInt(recoveryParamHex); } export function parseRecoverableSignature(signature: string) { @@ -192,7 +188,7 @@ export function parseRecoverableSignature(signature: string) { const r = signature.substr(2, coordinateValueBytes * 2); const s = signature.substr(2 + coordinateValueBytes * 2, coordinateValueBytes * 2); return { - recoveryParam: hexStringToInt(recoveryParamHex), + recoveryParam: hexToInt(recoveryParamHex), r, s, }; diff --git a/packages/transactions/src/types.ts b/packages/transactions/src/types.ts index 1a22549c9..882bf8dbd 100644 --- a/packages/transactions/src/types.ts +++ b/packages/transactions/src/types.ts @@ -1,4 +1,4 @@ -import { Buffer, intToBytes } from '@stacks/common'; +import { Buffer, hexToInt, intToBytes, intToHex } from '@stacks/common'; import { MEMO_MAX_LENGTH_BYTES, AddressHashMode, @@ -15,8 +15,6 @@ import { StacksPublicKey, serializePublicKey, deserializePublicKey, isCompressed import { BufferArray, - intToHexString, - hexStringToInt, exceedsMaxLengthBytes, hashP2PKH, rightPadHexToLength, @@ -183,14 +181,14 @@ export function addressFromPublicKeys( export function serializeAddress(address: Address): Buffer { const bufferArray: BufferArray = new BufferArray(); - bufferArray.appendHexString(intToHexString(address.version, 1)); + bufferArray.appendHexString(intToHex(address.version, 1)); bufferArray.appendHexString(address.hash160); return bufferArray.concatBuffer(); } export function deserializeAddress(bufferReader: BufferReader): Address { - const version = hexStringToInt(bufferReader.readBuffer(1).toString('hex')); + const version = hexToInt(bufferReader.readBuffer(1).toString('hex')); const data = bufferReader.readBuffer(20).toString('hex'); return { type: StacksMessageType.Address, version, hash160: data }; @@ -227,7 +225,7 @@ export function serializeLPString(lps: LengthPrefixedString) { const bufferArray: BufferArray = new BufferArray(); const contentBuffer = Buffer.from(lps.content); const length = contentBuffer.byteLength; - bufferArray.appendHexString(intToHexString(length, lps.lengthPrefixBytes)); + bufferArray.appendHexString(intToHex(length, lps.lengthPrefixBytes)); bufferArray.push(contentBuffer); return bufferArray.concatBuffer(); } @@ -238,7 +236,7 @@ export function deserializeLPString( maxLength?: number ): LengthPrefixedString { prefixBytes = prefixBytes ? prefixBytes : 1; - const length = hexStringToInt(bufferReader.readBuffer(prefixBytes).toString('hex')); + const length = hexToInt(bufferReader.readBuffer(prefixBytes).toString('hex')); const content = bufferReader.readBuffer(length).toString(); return createLPString(content, prefixBytes, maxLength ?? 128); } @@ -312,7 +310,7 @@ export function createLPList( export function serializeLPList(lpList: LengthPrefixedList): Buffer { const list = lpList.values; const bufferArray: BufferArray = new BufferArray(); - bufferArray.appendHexString(intToHexString(list.length, lpList.lengthPrefixBytes)); + bufferArray.appendHexString(intToHex(list.length, lpList.lengthPrefixBytes)); for (let index = 0; index < list.length; index++) { bufferArray.push(serializeStacksMessage(list[index])); } @@ -324,7 +322,7 @@ export function deserializeLPList( type: StacksMessageType, lengthPrefixBytes?: number ): LengthPrefixedList { - const length = hexStringToInt(bufferReader.readBuffer(lengthPrefixBytes || 4).toString('hex')); + const length = hexToInt(bufferReader.readBuffer(lengthPrefixBytes || 4).toString('hex')); const l: StacksMessage[] = []; for (let index = 0; index < length; index++) { switch (type) { diff --git a/packages/transactions/src/utils.ts b/packages/transactions/src/utils.ts index fa9b91a29..388084175 100644 --- a/packages/transactions/src/utils.ts +++ b/packages/transactions/src/utils.ts @@ -6,8 +6,7 @@ import { utils } from '@noble/secp256k1'; import { deserializeCV } from './clarity'; import { c32addressDecode } from 'c32check'; import lodashCloneDeep from 'lodash.clonedeep'; -import { with0x } from '@stacks/common'; -import { bytesToHex } from '@noble/hashes/utils'; +import { bytesToHex, with0x } from '@stacks/common'; /** * Use utils.randomBytes to replace randombytes dependency @@ -17,6 +16,9 @@ import { bytesToHex } from '@noble/hashes/utils'; */ export const randomBytes = (bytesLength?: number) => Buffer.from(utils.randomBytes(bytesLength)); +/** + * @deprecated Import from `@stacks/common` instead + */ export { bytesToHex }; export class BufferArray { @@ -52,11 +54,6 @@ export const leftPadHexToLength = (hexString: string, length: number): string => export const rightPadHexToLength = (hexString: string, length: number): string => hexString.padEnd(length, '0'); -export const intToHexString = (integer: number, lengthBytes = 8): string => - integer.toString(16).padStart(lengthBytes * 2, '0'); - -export const hexStringToInt = (hexString: string): number => parseInt(hexString, 16); - export const exceedsMaxLengthBytes = (string: string, maxLengthBytes: number): boolean => string ? Buffer.from(string).length > maxLengthBytes : false; diff --git a/packages/transactions/tests/clarity.test.ts b/packages/transactions/tests/clarity.test.ts index 2be166497..f63cfd100 100644 --- a/packages/transactions/tests/clarity.test.ts +++ b/packages/transactions/tests/clarity.test.ts @@ -34,7 +34,6 @@ import { } from '../src/clarity'; import { BufferReader } from '../src/bufferReader'; import { cvToString, cvToJSON, cvToValue, getCVTypeString } from '../src/clarity/clarityValue'; -import BN from 'bn.js'; const ADDRESS = 'SP2JXKMSH007NPYAQHKJPQMAQYAD90NQGTVJVQ02B'; @@ -227,7 +226,7 @@ describe('Clarity Types', () => { ['-10', '-10', '0xfffffffffffffffffffffffffffffff6'], ['0xfff6', '-10', '0xfffffffffffffffffffffffffffffff6'], ['0xf6', '-10', '0xfffffffffffffffffffffffffffffff6'], - [new BN(-10), '-10', '0xfffffffffffffffffffffffffffffff6'], + [BigInt(-10), '-10', '0xfffffffffffffffffffffffffffffff6'], [Buffer.from([0xff, 0xf6]), '-10', '0xfffffffffffffffffffffffffffffff6'], [Buffer.from([0xf6]), '-10', '0xfffffffffffffffffffffffffffffff6'], [Buffer.from([0xff, 0xfe]), '-2', '0xfffffffffffffffffffffffffffffffe'], @@ -246,7 +245,7 @@ describe('Clarity Types', () => { ['0x000a', '10', '0x0000000000000000000000000000000a'], ['0xa', '10', '0x0000000000000000000000000000000a'], // hex string with odd padding ['0x00a', '10', '0x0000000000000000000000000000000a'], // hex string with odd padding - [new BN(10), '10', '0x0000000000000000000000000000000a'], + [BigInt(10), '10', '0x0000000000000000000000000000000a'], [Buffer.from([0x0a]), '10', '0x0000000000000000000000000000000a'], [Buffer.from([0x00, 0x0a]), '10', '0x0000000000000000000000000000000a'], [Uint8Array.of(0x0a), '10', '0x0000000000000000000000000000000a'], @@ -329,7 +328,7 @@ describe('Clarity Types', () => { [10n, '10', '0x0000000000000000000000000000000a'], ['10', '10', '0x0000000000000000000000000000000a'], ['0x0a', '10', '0x0000000000000000000000000000000a'], - [new BN(10), '10', '0x0000000000000000000000000000000a'], + [BigInt(10), '10', '0x0000000000000000000000000000000a'], [Buffer.from([0x0a]), '10', '0x0000000000000000000000000000000a'], [Buffer.from([0x00, 0x0a]), '10', '0x0000000000000000000000000000000a'] ] diff --git a/packages/transactions/tests/keys.test.ts b/packages/transactions/tests/keys.test.ts index 090163fe8..55860e0c9 100644 --- a/packages/transactions/tests/keys.test.ts +++ b/packages/transactions/tests/keys.test.ts @@ -1,3 +1,11 @@ +import { + getPublicKey as nobleGetPublicKey, + signSync as nobleSecp256k1Sign, + utils, + verify as nobleSecp256k1Verify, +} from '@noble/secp256k1'; +import { Buffer, bytesToHex, utf8ToBytes } from '@stacks/common'; +import { ec as EC } from 'elliptic'; import { compressPublicKey, createStacksPrivateKey, @@ -6,25 +14,17 @@ import { getPublicKey, makeRandomPrivKey, privateKeyToString, + PubKeyEncoding, pubKeyfromPrivKey, publicKeyFromSignature, publicKeyToString, signWithKey, - StacksPublicKey, - PubKeyEncoding, StacksMessageType, - TransactionVersion + StacksPublicKey, + TransactionVersion, } from '../src'; -import { utf8ToBytes } from '@stacks/common'; import { randomBytes } from '../src/utils'; -import { - utils, - verify as nobleSecp256k1Verify, - signSync as nobleSecp256k1Sign, - getPublicKey as nobleGetPublicKey -} from '@noble/secp256k1'; import { serializeDeserialize } from './macros'; -import { ec as EC } from 'elliptic'; // Create and initialize EC context // Better do it once and reuse it @@ -72,25 +72,37 @@ test('Stacks public key and private keys', () => { ).toBe('STZG6BAY4JVR9RNAB1HY92B7Q208ZYY4HZG8ZXFM'); const compressedPubKey = compressPublicKey(pubKey.data).data.toString('hex'); - expect(compressedPubKey).toBe('03ef788b3830c00abe8f64f62dc32fc863bc0b2cafeb073b6c8e1c7657d9c2c3ab'); + expect(compressedPubKey).toBe( + '03ef788b3830c00abe8f64f62dc32fc863bc0b2cafeb073b6c8e1c7657d9c2c3ab' + ); }); test('Retrieve public key from signature', () => { - const privKey = createStacksPrivateKey('edf9aee84d9b7abc145504dde6726c64f369d37ee34ded868fabd876c26570bc'); + const privKey = createStacksPrivateKey( + 'edf9aee84d9b7abc145504dde6726c64f369d37ee34ded868fabd876c26570bc' + ); const uncompressedPubKey = '04ef788b3830c00abe8f64f62dc32fc863bc0b2cafeb073b6c8e1c7657d9c2c3ab5b435d20ea91337cdd8c30dd7427bb098a5355e9c9bfad43797899b8137237cf'; const compressedPubKey = '03ef788b3830c00abe8f64f62dc32fc863bc0b2cafeb073b6c8e1c7657d9c2c3ab'; const message = 'hello world'; - const messageHex = utils.bytesToHex(utf8ToBytes(message)); + const messageHex = bytesToHex(utf8ToBytes(message)); const sig = signWithKey(privKey, messageHex); - const uncompressedPubKeyFromSig = publicKeyFromSignature(messageHex, sig, PubKeyEncoding.Uncompressed) - const compressedPubKeyFromSig = publicKeyFromSignature(messageHex, sig, PubKeyEncoding.Compressed) + const uncompressedPubKeyFromSig = publicKeyFromSignature( + messageHex, + sig, + PubKeyEncoding.Uncompressed + ); + const compressedPubKeyFromSig = publicKeyFromSignature( + messageHex, + sig, + PubKeyEncoding.Compressed + ); expect(uncompressedPubKeyFromSig).toBe(uncompressedPubKey); expect(compressedPubKeyFromSig).toBe(compressedPubKey); -}) +}); test('Sign msg using elliptic/secp256k1 and verify signature using @noble/secp256k1', () => { // Maximum keypairs to try if a keypairs is not accepted by @noble/secp256k1 @@ -104,7 +116,7 @@ test('Sign msg using elliptic/secp256k1 and verify signature using @noble/secp25 const keyPair = ec.genKeyPair(options); const msg = 'hello world'; - const msgHex = utils.bytesToHex(utf8ToBytes(msg)); + const msgHex = bytesToHex(utf8ToBytes(msg)); // Sign msg using elliptic/secp256k1 // input must be an array, or a hex-string @@ -126,14 +138,14 @@ test('Sign msg using elliptic/secp256k1 and verify signature using @noble/secp25 } // Verification result by @noble/secp256k1 should be true expect(nobleVerifyResult).toBeTruthy(); -}) +}); test('Sign msg using @noble/secp256k1 and verify signature using elliptic/secp256k1', () => { // Generate private key const privateKey = utils.randomPrivateKey(); const msg = 'hello world'; - const msgHex = utils.bytesToHex(utf8ToBytes(msg)); + const msgHex = bytesToHex(utf8ToBytes(msg)); // Sign msg using @noble/secp256k1 // input must be a hex-string @@ -155,4 +167,4 @@ test('Sign msg using @noble/secp256k1 and verify signature using elliptic/secp25 // Verification result by elliptic/secp256k1 should be true expect(ellipticVerifyResult).toBeTruthy(); -}) +}); diff --git a/packages/wallet-sdk/package.json b/packages/wallet-sdk/package.json index f88e45211..7e5d641b8 100644 --- a/packages/wallet-sdk/package.json +++ b/packages/wallet-sdk/package.json @@ -55,7 +55,6 @@ "@stacks/storage": "^4.0.0", "@stacks/transactions": "^4.0.0", "bitcoinjs-lib": "^5.2.0", - "bn.js": "^5.2.0", "c32check": "^1.1.3", "jsontokens": "^3.0.0", "triplesec": "^4.0.3", diff --git a/packages/wallet-sdk/tests/derive-keychain.test.ts b/packages/wallet-sdk/tests/derive-keychain.test.ts index 695edbe40..8551f52e4 100644 --- a/packages/wallet-sdk/tests/derive-keychain.test.ts +++ b/packages/wallet-sdk/tests/derive-keychain.test.ts @@ -12,9 +12,10 @@ import { import { mnemonicToSeed } from '@scure/bip39'; import { BIP32Interface, fromBase58 } from 'bip32'; import { HDKey } from '@scure/bip32'; -import { TransactionVersion, bytesToHex } from '@stacks/transactions'; +import { TransactionVersion } from '@stacks/transactions'; import { StacksMainnet } from '@stacks/network'; import fetchMock from 'jest-fetch-mock'; +import { bytesToHex } from '@stacks/common'; const SECRET_KEY = 'sound idle panel often situate develop unit text design antenna ' + @@ -242,19 +243,22 @@ test('fetch username defaults to mainnet', async () => { expect(fetchMock.mock.calls[0][0]).toContain('stacks-node-api.mainnet'); }); -test('Verify compatibility between @scure/bip32 and bip32 dependency',() => { +test('Verify compatibility between @scure/bip32 and bip32 dependency', () => { // Consider a root key in base58 format - const root = 'xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi'; + const root = + 'xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi'; const bip32Node: BIP32Interface = fromBase58(root); // Use same root key to create node using @scure/bip32 const keychainNode = HDKey.fromExtendedKey(root); if (bip32Node.privateKey && keychainNode.privateKey) { - expect(bip32Node.privateKey.toString('hex')).toEqual(bytesToHex(keychainNode.privateKey)) + expect(bip32Node.privateKey.toString('hex')).toEqual(bytesToHex(keychainNode.privateKey)); } else { // Reject test case - fail('No private keys: failed to verify compatibility between @scure/bip32 and bip32 dependency'); + fail( + 'No private keys: failed to verify compatibility between @scure/bip32 and bip32 dependency' + ); } const derivationPath = 'm/0/0'; @@ -266,9 +270,13 @@ test('Verify compatibility between @scure/bip32 and bip32 dependency',() => { const childKeychainNode = keychainNode.derive(derivationPath); if (childBip32Node.privateKey && childKeychainNode.privateKey) { - expect(childBip32Node.privateKey.toString('hex')).toEqual(bytesToHex(childKeychainNode.privateKey)) + expect(childBip32Node.privateKey.toString('hex')).toEqual( + bytesToHex(childKeychainNode.privateKey) + ); } else { // Reject test case - fail('No private keys: failed to verify compatibility between @scure/bip32 and bip32 dependency'); + fail( + 'No private keys: failed to verify compatibility between @scure/bip32 and bip32 dependency' + ); } });