From e78cc744e5e8d2c14534bb9ae2fe4f347997b4d6 Mon Sep 17 00:00:00 2001 From: janniks Date: Tue, 22 Oct 2024 21:14:41 +0200 Subject: [PATCH] refactor: Minor refactoring to match common theme --- .github/MIGRATION.md | 1 + packages/bns/src/utils.ts | 2 ++ packages/encryption/src/cryptoRandom.ts | 2 +- packages/encryption/src/ec.ts | 4 +-- packages/encryption/src/messageSignature.ts | 1 + packages/encryption/src/varuint.ts | 3 ++ packages/stacking/src/utils.ts | 29 ++++++++++++++------ packages/stacking/tests/stacking-2.4.test.ts | 6 ++-- packages/stacking/tests/stacking.test.ts | 4 +-- packages/stacking/tests/utils.test.ts | 10 +++---- packages/transactions/src/utils.ts | 2 +- packages/wallet-sdk/src/encryption.ts | 21 -------------- packages/wallet-sdk/src/generate.ts | 10 +++---- packages/wallet-sdk/src/index.ts | 1 - 14 files changed, 47 insertions(+), 49 deletions(-) delete mode 100644 packages/wallet-sdk/src/encryption.ts diff --git a/.github/MIGRATION.md b/.github/MIGRATION.md index eeb205f77..887cb71d4 100644 --- a/.github/MIGRATION.md +++ b/.github/MIGRATION.md @@ -326,6 +326,7 @@ This likely was a misunderstood and unused feature. - `makeRandomPrivKey` was renamed to `randomPrivateKey` and now returns a compressed private key. - `generateSecretKey` was renamed to `randomSeedPhrase`. - `nextYear`, `nextMonth`, `nextHour`, `makeUUID4`, `updateQueryStringParameter`, `getAesCbcOutputLength`, `getAPIUsageErrorMessage`, `isSameOriginAbsoluteUrl`, `isLaterVersion`, `getBase64OutputLength`, were marked as deprecated. +- `encrypt` and `decrypt` in `@stacks/wallet-sdk` (aliases of `encryptMnemonic` and `decryptMnemonic` in the `@stacks/encryption` package respectively) were removed. ## Stacks.js (<=4.x.x) → (5.x.x) diff --git a/packages/bns/src/utils.ts b/packages/bns/src/utils.ts index a41876ae2..797160fc1 100644 --- a/packages/bns/src/utils.ts +++ b/packages/bns/src/utils.ts @@ -1,6 +1,7 @@ import { utf8ToBytes } from '@stacks/common'; import { hash160 } from '@stacks/transactions'; +/** @ignore */ export function decodeFQN(fqdn: string): { name: string; namespace: string; @@ -20,4 +21,5 @@ export function decodeFQN(fqdn: string): { }; } +/** @ignore */ export const getZonefileHash = (zonefile: string) => hash160(utf8ToBytes(zonefile)); diff --git a/packages/encryption/src/cryptoRandom.ts b/packages/encryption/src/cryptoRandom.ts index fd4eefbb0..a098ebf13 100644 --- a/packages/encryption/src/cryptoRandom.ts +++ b/packages/encryption/src/cryptoRandom.ts @@ -8,5 +8,5 @@ import { utils } from '@noble/secp256k1'; */ export const randomBytes = (bytesLength: number = 32): Uint8Array => utils.randomBytes(bytesLength); -/** Optional function to generate cryptographically secure random bytes */ +/** @deprecated @ignore */ export type GetRandomBytes = (count: number) => Uint8Array; diff --git a/packages/encryption/src/ec.ts b/packages/encryption/src/ec.ts index bb4547a20..e52654c6b 100644 --- a/packages/encryption/src/ec.ts +++ b/packages/encryption/src/ec.ts @@ -203,7 +203,7 @@ function isValidPublicKey(pub: string): { /** * Hex encodes a 32-byte bigint instance. * The result string is zero padded and always 64 characters in length. - * @ignore @internal + * @ignore @internal @deprecated */ export function getHexFromBN(bnInput: bigint): string { const hexOut = bnInput.toString(16); @@ -221,7 +221,7 @@ export function getHexFromBN(bnInput: bigint): string { /** * Converts to zero padded 32 bytes - * @ignore + * @ignore @deprecated */ export function getBytesFromBN(bnInput: bigint): Uint8Array { // todo: remove method? diff --git a/packages/encryption/src/messageSignature.ts b/packages/encryption/src/messageSignature.ts index 5f328a7af..69484a045 100644 --- a/packages/encryption/src/messageSignature.ts +++ b/packages/encryption/src/messageSignature.ts @@ -11,6 +11,7 @@ export function hashMessage(message: string, prefix: string = chainPrefix): Uint } export function encodeMessage( + /** UTF-8 string or Uint8Array (bytes) */ message: string | Uint8Array, prefix: string = chainPrefix ): Uint8Array { diff --git a/packages/encryption/src/varuint.ts b/packages/encryption/src/varuint.ts index c57668b34..86e172d86 100644 --- a/packages/encryption/src/varuint.ts +++ b/packages/encryption/src/varuint.ts @@ -29,6 +29,7 @@ function ensureUInt53(n: number) { if (n < 0 || n > MAX_SAFE_INTEGER || n % 1 !== 0) throw new RangeError('value out of range'); } +/** @ignore */ export function encode(number: number, bytes?: Uint8Array, offset: number = 0) { ensureUInt53(number); if (!bytes) bytes = new Uint8Array(encodingLength(number)); @@ -57,6 +58,7 @@ export function encode(number: number, bytes?: Uint8Array, offset: number = 0) { return bytes; } +/** @ignore */ export function decode(bytes: Uint8Array, offset: number = 0) { const first = readUInt8(bytes, offset); @@ -83,6 +85,7 @@ export function decode(bytes: Uint8Array, offset: number = 0) { } } +/** @ignore */ export function encodingLength(number: number) { ensureUInt53(number); diff --git a/packages/stacking/src/utils.ts b/packages/stacking/src/utils.ts index 95ec521ae..b12e9eba1 100644 --- a/packages/stacking/src/utils.ts +++ b/packages/stacking/src/utils.ts @@ -1,6 +1,6 @@ import { sha256 } from '@noble/hashes/sha256'; import { bech32, bech32m } from '@scure/base'; -import { IntegerType, PrivateKey, bigIntToBytes, hexToBytes } from '@stacks/common'; +import { IntegerType, PrivateKey, bigIntToBytes, bytesToHex, hexToBytes } from '@stacks/common'; import { base58CheckDecode, base58CheckEncode, @@ -79,6 +79,7 @@ function nativeAddressToSegwitVersion( ); } +/** @ignore */ function bech32Decode(btcAddress: string) { const { words: bech32Words } = bech32.decode(btcAddress); const witnessVersion = bech32Words[0]; @@ -92,6 +93,7 @@ function bech32Decode(btcAddress: string) { }; } +/** @ignore */ function bech32MDecode(btcAddress: string) { const { words: bech32MWords } = bech32m.decode(btcAddress); const witnessVersion = bech32MWords[0]; @@ -105,6 +107,7 @@ function bech32MDecode(btcAddress: string) { }; } +/** @ignore */ function decodeNativeSegwitBtcAddress(btcAddress: string): { witnessVersion: number; data: Uint8Array; @@ -117,6 +120,14 @@ function decodeNativeSegwitBtcAddress(btcAddress: string): { } export function decodeBtcAddress(btcAddress: string): { + version: PoXAddressVersion; + data: string; +} { + const { version, data } = decodeBtcAddressBytes(btcAddress); + return { version, data: bytesToHex(data) }; +} + +export function decodeBtcAddressBytes(btcAddress: string): { version: PoXAddressVersion; data: Uint8Array; } { @@ -233,7 +244,7 @@ export function getErrorString(error: StackingErrors): string { * @returns The converted PoX address as a tuple of version and hashbytes. */ export function poxAddressToTuple(poxAddress: string) { - const { version, data } = decodeBtcAddress(poxAddress); + const { version, data } = decodeBtcAddressBytes(poxAddress); const versionBuff = bufferCV(bigIntToBytes(BigInt(version), 1)); const hashBuff = bufferCV(data); return tupleCV({ @@ -261,26 +272,28 @@ function legacyHashModeToBtcAddressVersion( function _poxAddressToBtcAddress_Values( version: number, - hashBytes: Uint8Array, + hash: string | Uint8Array, network: StacksNetworkName ): string { if (!StacksNetworks.includes(network)) throw new Error('Invalid network.'); + hash = typeof hash === 'string' ? hexToBytes(hash) : hash; + switch (version) { case PoXAddressVersion.P2PKH: case PoXAddressVersion.P2SH: case PoXAddressVersion.P2SHP2WPKH: case PoXAddressVersion.P2SHP2WSH: { const btcAddrVersion = legacyHashModeToBtcAddressVersion(version, network); - return base58CheckEncode(btcAddrVersion, hashBytes); + return base58CheckEncode(btcAddrVersion, hash); } case PoXAddressVersion.P2WPKH: case PoXAddressVersion.P2WSH: { - const words = bech32.toWords(hashBytes); + const words = bech32.toWords(hash); return bech32.encode(SegwitPrefix[network], [SEGWIT_V0, ...words]); } case PoXAddressVersion.P2TR: { - const words = bech32m.toWords(hashBytes); + const words = bech32m.toWords(hash); return bech32m.encode(SegwitPrefix[network], [SEGWIT_V1, ...words]); } } @@ -299,13 +312,13 @@ function _poxAddressToBtcAddress_ClarityValue( * Converts a PoX address to a Bitcoin address. * * @param version - The version of the PoX address (as a single number, not a Uint8array). - * @param hashBytes - The hash bytes of the PoX address. + * @param hash - The hash bytes of the PoX address. * @param network - The network the PoX address is on. * @returns The corresponding Bitcoin address. */ export function poxAddressToBtcAddress( version: number, - hashBytes: Uint8Array, + hash: string | Uint8Array, network: StacksNetworkName // todo: allow NetworkParam in the future (minor) ): string; /** diff --git a/packages/stacking/tests/stacking-2.4.test.ts b/packages/stacking/tests/stacking-2.4.test.ts index 749934668..6f4d360e8 100644 --- a/packages/stacking/tests/stacking-2.4.test.ts +++ b/packages/stacking/tests/stacking-2.4.test.ts @@ -13,7 +13,7 @@ import { } from '@stacks/internal'; import { STACKS_MAINNET, STACKS_TESTNET } from '@stacks/network'; import { ContractCallPayload, deserializeTransaction, fetchNonce } from '@stacks/transactions'; -import { StackingClient, decodeBtcAddress } from '../src'; +import { StackingClient, decodeBtcAddress, decodeBtcAddressBytes } from '../src'; import { PoxOperationPeriod } from '../src/constants'; import { BTC_ADDRESS_CASES } from './utils.test'; @@ -817,7 +817,7 @@ describe('delegated stacking', () => { expect(rewardSet).toBeDefined(); expect(rewardSet?.total_ustx).toBe(FULL_AMOUNT); expect(rewardSet?.pox_address.version[0]).toEqual(decodeBtcAddress(poolPoxAddress).version); - expect(rewardSet?.pox_address.hashbytes).toEqual(decodeBtcAddress(poolPoxAddress).data); + expect(rewardSet?.pox_address.hashbytes).toEqual(decodeBtcAddressBytes(poolPoxAddress).data); }); test('delegator stacks for multiple stackers in a pool, then increases commitment (requires >= pox-2)', async () => { @@ -1007,7 +1007,7 @@ describe('delegated stacking', () => { expect(rewardSet).toBeDefined(); expect(rewardSet?.total_ustx).toBe(AMOUNT_75 * 2n); // 1.5x the FULL_AMOUNT (aka everything the stackers stacked together) expect(rewardSet?.pox_address.version[0]).toEqual(decodeBtcAddress(poolPoxAddress).version); - expect(rewardSet?.pox_address.hashbytes).toEqual(decodeBtcAddress(poolPoxAddress).data); + expect(rewardSet?.pox_address.hashbytes).toEqual(decodeBtcAddressBytes(poolPoxAddress).data); }); }); diff --git a/packages/stacking/tests/stacking.test.ts b/packages/stacking/tests/stacking.test.ts index ed564cbc0..03ad4b415 100644 --- a/packages/stacking/tests/stacking.test.ts +++ b/packages/stacking/tests/stacking.test.ts @@ -1184,7 +1184,7 @@ test('pox address to btc address', () => { expect(btcAddress).toBe(item.expectedBtcAddr); const decodedAddress = decodeBtcAddress(btcAddress); expect(decodedAddress.version).toBe(item.version); - expect(bytesToHex(decodedAddress.data)).toBe(bytesToHex(item.hashBytes)); + expect(decodedAddress.data).toBe(bytesToHex(item.hashBytes)); }); vectors.forEach(item => { @@ -1205,7 +1205,7 @@ test('pox address to btc address', () => { expect(btcAddress).toBe(item.expectedBtcAddr); const decodedAddress = decodeBtcAddress(btcAddress); expect(decodedAddress.version).toBe(item.version); - expect(bytesToHex(decodedAddress.data)).toBe(bytesToHex(item.hashBytes)); + expect(decodedAddress.data).toBe(bytesToHex(item.hashBytes)); }); }); diff --git a/packages/stacking/tests/utils.test.ts b/packages/stacking/tests/utils.test.ts index 642da164f..bd0f41d87 100644 --- a/packages/stacking/tests/utils.test.ts +++ b/packages/stacking/tests/utils.test.ts @@ -1,4 +1,4 @@ -import { bytesToHex, hexToBytes } from '@stacks/common'; +import { hexToBytes } from '@stacks/common'; import { PoXAddressVersion } from '../src/constants'; import { decodeBtcAddress, poxAddressToBtcAddress, poxAddressToTuple } from '../src/utils'; @@ -68,8 +68,8 @@ test.each(BTC_ADDRESS_CASES)( ({ address, expectedVersion, expectedHash, expectedLength }) => { const decoded = decodeBtcAddress(address); expect(decoded.version).toBe(expectedVersion); - expect(decoded.data).toEqual(hexToBytes(expectedHash)); - expect(decoded.data).toHaveLength(expectedLength); + expect(decoded.data).toEqual(expectedHash); + expect(decoded.data).toHaveLength(expectedLength * 2); const tuple = poxAddressToTuple(address); expect(hexToBytes(tuple.value['version'].value)).toHaveLength(1); @@ -265,7 +265,7 @@ test.each(BTC_ADDRESS_CASES_API)( 'decoding and encoding btc address $format', ({ address, hash, format, network }) => { const decoded = decodeBtcAddress(address); - expect(bytesToHex(decoded.data)).toBe(hash); + expect(decoded.data).toBe(hash); expect(decoded.version).toBe(FORMAT_TO_VERSION[format]); const encoded1 = poxAddressToBtcAddress(decoded.version, decoded.data, network); @@ -310,7 +310,7 @@ const BTC_ADDRESS_CASES_HASH = [ test.each(BTC_ADDRESS_CASES_HASH)('decoding btc address hash', ({ address, hash }) => { const decoded = decodeBtcAddress(address); - expect(bytesToHex(decoded.data)).toBe(hash); + expect(decoded.data).toBe(hash); }); const BTC_ADDRESS_CASES_INVALID_POX_ADDRESS = [ diff --git a/packages/transactions/src/utils.ts b/packages/transactions/src/utils.ts index 2efa8d788..227bad981 100644 --- a/packages/transactions/src/utils.ts +++ b/packages/transactions/src/utils.ts @@ -30,7 +30,7 @@ export const rightPadHexToLength = (hexString: string, length: number): string = export const exceedsMaxLengthBytes = (string: string, maxLengthBytes: number): boolean => string ? utf8ToBytes(string).length > maxLengthBytes : false; -/** @internal */ +/** @internal @deprecated */ export function cloneDeep(obj: T): T { return lodashCloneDeep(obj); } diff --git a/packages/wallet-sdk/src/encryption.ts b/packages/wallet-sdk/src/encryption.ts deleted file mode 100644 index 4dc39959f..000000000 --- a/packages/wallet-sdk/src/encryption.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { decryptMnemonic, encryptMnemonic } from '@stacks/encryption'; - -/** - * Decrypt an encrypted mnemonic phrase with a password. - * @param data - Uint8Array or hex-encoded string of the encrypted mnemonic - * @param password - Password for data - * @return the raw mnemonic phrase - */ -export function decrypt(dataBytes: Uint8Array | string, password: string): Promise { - return decryptMnemonic(dataBytes, password); -} - -/** - * Encrypt a raw mnemonic phrase to be password protected - * @param phrase - Raw mnemonic phrase - * @param password - Password to encrypt mnemonic with - * @return The encrypted phrase - * */ -export function encrypt(phrase: string, password: string): Promise { - return encryptMnemonic(phrase, password); -} diff --git a/packages/wallet-sdk/src/generate.ts b/packages/wallet-sdk/src/generate.ts index 64d7d4dd3..01c942f65 100644 --- a/packages/wallet-sdk/src/generate.ts +++ b/packages/wallet-sdk/src/generate.ts @@ -11,11 +11,11 @@ import { wordlist } from '@scure/bip39/wordlists/english'; // https://github.com/paulmillr/scure-bip32 // Secure, audited & minimal implementation of BIP32 hierarchical deterministic (HD) wallets. import { HDKey } from '@scure/bip32'; -import { Wallet, getRootNode } from './models/common'; -import { encrypt } from './encryption'; -import { deriveAccount, deriveWalletKeys } from './derive'; -import { DerivationType } from '.'; import { bytesToHex } from '@stacks/common'; +import { encryptMnemonic } from '@stacks/encryption'; +import { DerivationType } from '.'; +import { deriveAccount, deriveWalletKeys } from './derive'; +import { Wallet, getRootNode } from './models/common'; export type AllowedKeyEntropyBits = 128 | 256; @@ -47,7 +47,7 @@ export const generateWallet = async ({ secretKey: string; password: string; }): Promise => { - const ciphertextBytes = await encrypt(secretKey, password); + const ciphertextBytes = await encryptMnemonic(secretKey, password); const encryptedSecretKey = bytesToHex(ciphertextBytes); const rootPrivateKey = await mnemonicToSeed(secretKey); diff --git a/packages/wallet-sdk/src/index.ts b/packages/wallet-sdk/src/index.ts index 8cf569f59..bcecef608 100644 --- a/packages/wallet-sdk/src/index.ts +++ b/packages/wallet-sdk/src/index.ts @@ -6,5 +6,4 @@ export * from './models/common'; export * from './models/profile'; export * from './models/wallet-config'; export * from './models/legacy-wallet-config'; -export * from './encryption'; export * from './utils';