From 162378f768681dd02a1d07130a6b76107b87e1da Mon Sep 17 00:00:00 2001 From: Alejo Acosta Date: Sun, 19 May 2024 17:36:56 -0300 Subject: [PATCH 1/8] move QuaiHDWallet methods to parent class HDWallet --- src.ts/wallet/hdwallet.ts | 87 ++-- src.ts/wallet/index.ts | 9 +- src.ts/wallet/quai-hdwallet.ts | 799 ++------------------------------- 3 files changed, 82 insertions(+), 813 deletions(-) diff --git a/src.ts/wallet/hdwallet.ts b/src.ts/wallet/hdwallet.ts index 42d56ad5..41c8620d 100644 --- a/src.ts/wallet/hdwallet.ts +++ b/src.ts/wallet/hdwallet.ts @@ -159,53 +159,54 @@ export abstract class HDWallet extends BaseWallet implements HDNodeLike this)(...params); } - #account(): KeystoreAccount { - const account: KeystoreAccount = { - address: this.address, - privateKey: this.privateKey, - }; - const m = this.mnemonic; - if (this.path && m && m.wordlist.locale === 'en' && m.password === '') { - account.mnemonic = { - path: this.path, - locale: 'en', - entropy: m.entropy, - }; - } + protected account(): KeystoreAccount { + const account: KeystoreAccount = { + address: this.address, + privateKey: this.privateKey, + }; + const m = this.mnemonic; + if (this.path && m && m.wordlist.locale === "en" && m.password === "") { + account.mnemonic = { + path: this.path, + locale: "en", + entropy: m.entropy, + }; + } return account; } - /** - * Resolves to a [JSON Keystore Wallet](json-wallets) encrypted with `password`. - * - * If `progressCallback` is specified, it will receive periodic updates as the encryption process progreses. - * - * @param {Uint8Array | string} password - The password to encrypt the wallet with. - * @param {ProgressCallback} [progressCallback] - An optional callback to receive progress updates. - * - * @returns {Promise} The encrypted JSON Keystore Wallet. - */ - async encrypt(password: Uint8Array | string, progressCallback?: ProgressCallback): Promise { - return await encryptKeystoreJson(this.#account(), password, { progressCallback }); - } - - /** - * Returns a [JSON Keystore Wallet](json-wallets) encryped with `password`. - * - * It is preferred to use the [async version](encrypt) instead, which allows a - * {@link ProgressCallback | **ProgressCallback**} to keep the user informed. - * - * This method will block the event loop (freezing all UI) until it is complete, which may be a non-trivial - * duration. - * - * @param {Uint8Array | string} password - The password to encrypt the wallet with. - * - * @returns {string} The encrypted JSON Keystore Wallet. - */ - encryptSync(password: Uint8Array | string): string { - return encryptKeystoreJsonSync(this.#account(), password); - } + /** + * Resolves to a [JSON Keystore Wallet](json-wallets) encrypted with + * `password`. + * + * If `progressCallback` is specified, it will receive periodic + * updates as the encryption process progreses. + * + * @param {Uint8Array | string} password - The password to encrypt the wallet with. + * @param {ProgressCallback} [progressCallback] - An optional callback to receive progress updates. + * @returns {Promise} The encrypted JSON Keystore Wallet. + */ + async encrypt(password: Uint8Array | string,progressCallback?: ProgressCallback): Promise { + return await encryptKeystoreJson(this.account(), password, {progressCallback}); + } + + /** + * Returns a [JSON Keystore Wallet](json-wallets) encryped with + * `password`. + * + * It is preferred to use the [async version](encrypt) instead, + * which allows a {@link ProgressCallback | **ProgressCallback**} to keep the user informed. + * + * This method will block the event loop (freezing all UI) until + * it is complete, which may be a non-trivial duration. + * + * @param {Uint8Array | string} password - The password to encrypt the wallet with. + * @returns {string} The encrypted JSON Keystore Wallet. + */ + encryptSync(password: Uint8Array | string): string { + return encryptKeystoreJsonSync(this.account(), password); + } /** * The extended key. diff --git a/src.ts/wallet/index.ts b/src.ts/wallet/index.ts index 33058293..77694768 100644 --- a/src.ts/wallet/index.ts +++ b/src.ts/wallet/index.ts @@ -15,14 +15,7 @@ export { BaseWallet } from './base-wallet.js'; -export { - getAccountPath, - getIndexedAccountPath, - quaiHDAccountPath, - qiHDAccountPath, - QuaiHDWallet, - HDNodeVoidWallet, -} from './quai-hdwallet.js'; +export {QuaiHDWallet} from "./quai-hdwallet.js"; export { isCrowdsaleJson, decryptCrowdsaleJson } from './json-crowdsale.js'; diff --git a/src.ts/wallet/quai-hdwallet.ts b/src.ts/wallet/quai-hdwallet.ts index 5b95bf85..05ae5e41 100644 --- a/src.ts/wallet/quai-hdwallet.ts +++ b/src.ts/wallet/quai-hdwallet.ts @@ -3,38 +3,17 @@ * * @section api/wallet:HD Wallets [hd-wallets] */ -import { computeHmac, randomBytes, ripemd160, SigningKey, sha256 } from '../crypto/index.js'; -import { VoidSigner } from '../providers/index.js'; -import { computeAddress } from '../transaction/index.js'; -import { - concat, - dataSlice, - decodeBase58, - defineProperties, - getBytes, - hexlify, - isBytesLike, - getNumber, - toBeArray, - toBigInt, - toBeHex, - assertPrivate, - assert, - assertArgument, -} from '../utils/index.js'; -import { BaseWallet } from './base-wallet.js'; -import { Mnemonic } from './mnemonic.js'; -import { encryptKeystoreJson, encryptKeystoreJsonSync } from './json-keystore.js'; -import { N, ShardData } from '../constants/index.js'; -import { getShardForAddress, isUTXOAddress } from '../utils/index.js'; -import type { ProgressCallback } from '../crypto/index.js'; -import type { Provider } from '../providers/index.js'; -import type { BytesLike, Numeric } from '../utils/index.js'; -import type { Wordlist } from '../wordlists/index.js'; -import type { KeystoreAccount } from './json-keystore.js'; -import { encodeBase58Check, zpad, HardenedBit, ser_I, derivePath, MasterSecret } from './utils.js'; +import { SigningKey } from "../crypto/index.js"; +import { Mnemonic } from "./mnemonic.js"; +import type { Provider } from "../providers/index.js"; +import { HDWallet, AddressInfo} from "./hdwallet.js"; -const _guard = {}; +const QUAI_COINT_TYPE = 994; + +// keeps track of the addresses and outpoints for a given shard (zone) +type ShardWalletData = { + addressesInfo: AddressInfo[]; +} /** * An **QuaiHDWallet** is a [Signer](../interfaces/Signer) backed by the private key derived from an HD Node using the @@ -45,749 +24,45 @@ const _guard = {}; * * @category Wallet */ -export class QuaiHDWallet extends BaseWallet { - /** - * The compressed public key. - */ - readonly #publicKey!: string; - - /** - * The fingerprint. - * - * A fingerprint allows quick qay to detect parent and child nodes, but developers should be prepared to deal with - * collisions as it is only 4 bytes. - */ - readonly fingerprint!: string; - - /** - * The parent fingerprint. - */ - readonly accountFingerprint!: string; - - /** - * The mnemonic used to create this HD Node, if available. - * - * Sources such as extended keys do not encode the mnemonic, in which case this will be `null`. - */ - readonly mnemonic!: null | Mnemonic; - - /** - * The chaincode, which is effectively a public key used to derive children. - */ - readonly chainCode!: string; - - /** - * The derivation path of this wallet. - * - * Since extended keys do not provider full path details, this may be `null`, if instantiated from a source that - * does not enocde it. - */ - readonly path!: null | string; - - /** - * The child index of this wallet. Values over `2 *\* 31` indicate the node is hardened. - */ - readonly index!: number; - - /** - * The depth of this wallet, which is the number of components in its path. - */ - readonly depth!: number; - - coinType?: number; - - /** - * @private - */ - constructor( - guard: any, - signingKey: SigningKey, - accountFingerprint: string, - chainCode: string, - path: null | string, - index: number, - depth: number, - mnemonic: null | Mnemonic, - provider: null | Provider, - ) { - super(signingKey, provider); - assertPrivate(guard, _guard); - - this.#publicKey = signingKey.compressedPublicKey; - - const fingerprint = dataSlice(ripemd160(sha256(this.#publicKey)), 0, 4); - defineProperties(this, { - accountFingerprint, - fingerprint, - chainCode, - path, - index, - depth, - }); - defineProperties(this, { mnemonic }); - } - - connect(provider: null | Provider): QuaiHDWallet { - return new QuaiHDWallet( - _guard, - this.signingKey, - this.accountFingerprint, - this.chainCode, - this.path, - this.index, - this.depth, - this.mnemonic, - provider, - ); - } - - #account(): KeystoreAccount { - const account: KeystoreAccount = { address: this.address, privateKey: this.privateKey }; - const m = this.mnemonic; - if (this.path && m && m.wordlist.locale === 'en' && m.password === '') { - account.mnemonic = { - path: this.path, - locale: 'en', - entropy: m.entropy, - }; - } - - return account; - } - - /** - * Resolves to a [JSON Keystore Wallet](json-wallets) encrypted with `password`. - * - * If `progressCallback` is specified, it will receive periodic updates as the encryption process progreses. - * - * @param {Uint8Array | string} password - The password to encrypt the wallet with. - * @param {ProgressCallback} [progressCallback] - An optional callback to receive progress updates. - * - * @returns {Promise} The encrypted JSON Keystore Wallet. - */ - async encrypt(password: Uint8Array | string, progressCallback?: ProgressCallback): Promise { - return await encryptKeystoreJson(this.#account(), password, { progressCallback }); - } - - /** - * Returns a [JSON Keystore Wallet](json-wallets) encryped with `password`. - * - * It is preferred to use the [async version](encrypt) instead, which allows a - * {@link ProgressCallback | **ProgressCallback**} to keep the user informed. - * - * This method will block the event loop (freezing all UI) until it is complete, which may be a non-trivial - * duration. - * - * @param {Uint8Array | string} password - The password to encrypt the wallet with. - * - * @returns {string} The encrypted JSON Keystore Wallet. - */ - encryptSync(password: Uint8Array | string): string { - return encryptKeystoreJsonSync(this.#account(), password); - } - - /** - * The extended key. - * - * This key will begin with the prefix `xpriv` and can be used to reconstruct this HD Node to derive its children. - */ - get extendedKey(): string { - // We only support the mainnet values for now, but if anyone needs - // testnet values, let me know. I believe current sentiment is that - // we should always use mainnet, and use BIP-44 to derive the network - // - Mainnet: public=0x0488B21E, private=0x0488ADE4 - // - Testnet: public=0x043587CF, private=0x04358394 - - assert(this.depth < 256, 'Depth too deep', 'UNSUPPORTED_OPERATION', { operation: 'extendedKey' }); - - return encodeBase58Check( - concat([ - '0x0488ADE4', - zpad(this.depth, 1), - this.accountFingerprint ?? '', - zpad(this.index, 4), - this.chainCode, - concat(['0x00', this.privateKey]), - ]), - ); - } - - /** - * Gets the current publicKey - */ - get publicKey(): string { - return this.#publicKey; - } - - /** - * Returns true if this wallet has a path, providing a Type Guard that the path is non-null. - * - * @returns {boolean} True if the path is non-null. - */ - hasPath(): this is { path: string } { - return this.path != null; - } - - /** - * Returns a neutered HD Node, which removes the private details of an HD Node. - * - * A neutered node has no private key, but can be used to derive child addresses and other public data about the HD - * Node. - * - * @returns {HDNodeVoidWallet} A neutered HD Node. - */ - neuter(): HDNodeVoidWallet { - return new HDNodeVoidWallet( - _guard, - this.address, - this.#publicKey, - this.accountFingerprint ?? '', - this.chainCode, - this.path ?? '', - this.index, - this.depth, - this.provider, - ); - } - - /** - * Return the child for `index`. - * - * @param {Numeric} _index - The index of the child to derive. - * - * @returns {QuaiHDWallet} The derived child HD Node. - */ - deriveChild(_index: Numeric): QuaiHDWallet { - const index = getNumber(_index, 'index'); - assertArgument(index <= 0xffffffff, 'invalid index', 'index', index); - - // Base path - let newDepth = this.depth + 1; - let path = this.path; - if (path) { - const pathFields = path.split('/'); - if (pathFields.length == 6) { - pathFields.pop(); - path = pathFields.join('/'); - newDepth--; - } - - path += '/' + (index & ~HardenedBit); - if (index & HardenedBit) { - path += "'"; - } - } - - const { IR, IL } = ser_I(index, this.chainCode, this.#publicKey, this.privateKey); - const ki = new SigningKey(toBeHex((toBigInt(IL) + BigInt(this.privateKey)) % N, 32)); - - //BIP44 if we are at the account depth get that fingerprint, otherwise continue with the current one - const newFingerprint = this.depth == 3 ? this.fingerprint : this.accountFingerprint; - - return new QuaiHDWallet( - _guard, - ki, - newFingerprint, - hexlify(IR), - path, - index, - newDepth, - this.mnemonic, - this.provider, - ); - } - - /** - * Return the HDNode for `path` from this node. - * - * @param {string} path - The path to derive. - * - * @returns {QuaiHDWallet} The derived HD Node. - */ - derivePath(path: string): QuaiHDWallet { - return derivePath(this, path); - } - - setCoinType(): void { - this.coinType = Number(this.path?.split("/")[2]?.replace("'", "")); - } - - static #fromSeed(_seed: BytesLike, mnemonic: null | Mnemonic): QuaiHDWallet { - assertArgument(isBytesLike(_seed), 'invalid seed', 'seed', '[REDACTED]'); - - const seed = getBytes(_seed, 'seed'); - assertArgument(seed.length >= 16 && seed.length <= 64, 'invalid seed', 'seed', '[REDACTED]'); - - const I = getBytes(computeHmac('sha512', MasterSecret, seed)); - const signingKey = new SigningKey(hexlify(I.slice(0, 32))); - - const result = new QuaiHDWallet( - _guard, - signingKey, - '0x00000000', - hexlify(I.slice(32)), - 'm', - 0, - 0, - mnemonic, - null, - ); - return result; - } - - /** - * Creates a new HD Node from `extendedKey`. - * - * If the `extendedKey` will either have a prefix or `xpub` or `xpriv`, returning a neutered HD Node - * ({@link HDNodeVoidWallet | **HDNodeVoidWallet**}) or full HD Node ({@link QuaiHDWallet | **QuaiHDWallet**}) - * respectively. - * - * @param {string} extendedKey - The extended key to create the HD Node from. - * - * @returns {QuaiHDWallet | HDNodeVoidWallet} The HD Node created from the extended key. - */ - static fromExtendedKey(extendedKey: string): QuaiHDWallet | HDNodeVoidWallet { - const bytes = toBeArray(decodeBase58(extendedKey)); // @TODO: redact - - assertArgument( - bytes.length === 82 || encodeBase58Check(bytes.slice(0, 78)) === extendedKey, - 'invalid extended key', - 'extendedKey', - '[ REDACTED ]', - ); - - const depth = bytes[4]; - const accountFingerprint = hexlify(bytes.slice(5, 9)); - const index = parseInt(hexlify(bytes.slice(9, 13)).substring(2), 16); - const chainCode = hexlify(bytes.slice(13, 45)); - const key = bytes.slice(45, 78); - - switch (hexlify(bytes.slice(0, 4))) { - // Public Key - case '0x0488b21e': - case '0x043587cf': { - const publicKey = hexlify(key); - return new HDNodeVoidWallet( - _guard, - computeAddress(publicKey), - publicKey, - accountFingerprint, - chainCode, - null, - index, - depth, - null, - ); - } - - // Private Key - case '0x0488ade4': - case '0x04358394 ': - if (key[0] !== 0) { - break; - } - return new QuaiHDWallet( - _guard, - new SigningKey(key.slice(1)), - accountFingerprint, - chainCode, - null, - index, - depth, - null, - null, - ); - } - - assertArgument(false, 'invalid extended key prefix', 'extendedKey', '[ REDACTED ]'); - } - - /** - * Creates a new random HDNode. - * - * @param {string} path - The BIP44 path to derive the HD Node from. - * @param {string} [password] - The password to use for the mnemonic. - * @param {Wordlist} [wordlist] - The wordlist to use for the mnemonic. - * - * @returns {QuaiHDWallet} The new HD Node. - */ - static createRandom( path: string, password?: string, wordlist?: Wordlist): QuaiHDWallet { - if (path == null) { throw new Error('Path is null') } -// if (path == null || !this.isValidPath(path)) { throw new Error('Invalid path: ' + path)} - const mnemonic = Mnemonic.fromEntropy(randomBytes(16), password, wordlist) - return QuaiHDWallet.#fromSeed(mnemonic.computeSeed(), mnemonic).derivePath(path); - } - - /** - * Create an HD Node from `mnemonic`. - * - * @param {Mnemonic} mnemonic - The mnemonic to create the HD Node from. - * @param {string} path - The BIP44 path to derive the HD Node from. - * - * @returns {QuaiHDWallet} The new HD Node Wallet. - */ - static fromMnemonic(mnemonic: Mnemonic, path: string): QuaiHDWallet { - if (path == null) { throw new Error('Path is null') } -// if (path == null || !this.isValidPath(path)) { throw new Error('Invalid path: ' + path)} - return QuaiHDWallet.#fromSeed(mnemonic.computeSeed(), mnemonic).derivePath(path); - } - - /** - * Creates an HD Node from a mnemonic `phrase`. - * - * @param {string} phrase - The mnemonic phrase to create the HD Node from. - * @param {string} path - The BIP44 path to derive the HD Node from. - * @param {string} [password] - The password to use for the mnemonic. - * @param {Wordlist} [wordlist] - The wordlist to use for the mnemonic. - * - * @returns {QuaiHDWallet} The new HD Node Wallet. - */ - static fromPhrase(phrase: string, path: string, password?: string, wordlist?: Wordlist): QuaiHDWallet { - if (path == null) { throw new Error('Path is null') } -// if (path == null || !this.isValidPath(path)) { throw new Error('Invalid path: ' + path)} - const mnemonic = Mnemonic.fromPhrase(phrase, password, wordlist) - return QuaiHDWallet.#fromSeed(mnemonic.computeSeed(), mnemonic).derivePath(path); - } - - /** - * Checks if the provided BIP44 path is valid and limited to the change level. - * - * @param {string} path - The BIP44 path to validate. - * - * @returns {boolean} True if the path is valid and does not include the address_index; false otherwise. - */ - static isValidPath(path: string): boolean { - // BIP44 path regex pattern for up to the 'change' level, excluding 'address_index' - // This pattern matches paths like "m/44'/0'/0'/0" and "m/44'/60'/0'/1", but not "m/44'/60'/0'/0/0" - const pathRegex = /^m\/44'\/\d+'\/\d+'\/[01]$/; - return pathRegex.test(path); - } - - /** - * Creates an HD Node from a `seed`. - * - * @param {BytesLike} seed - The seed to create the HD Node from. - * - * @returns {QuaiHDWallet} The new HD Node Wallet. - */ - static fromSeed(seed: BytesLike): QuaiHDWallet { - return QuaiHDWallet.#fromSeed(seed, null); - } - - /** - * Derives address by incrementing address_index according to BIP44 - * - * @param {number} index - The index of the address to derive. - * @param {string} [zone] - The zone of the address to derive. - * - * @returns {QuaiHDWallet} The derived HD Node. - * @throws {Error} If the path is missing or the zone is invalid. - */ - deriveAddress(index: number, zone?: string): QuaiHDWallet { - if (!this.path) throw new Error('Missing Path'); - - //Case for a non quai/qi wallet where zone is not needed - if (!zone) { - if (this.coinType == 994 || this.coinType == 969) { - //Zone not provided but wallet cointype is quai/qi - throw new Error('No zone provided for a Quai / Qi wallet'); - } - //Return address for any other cointype with no - return this.derivePath(this.path + '/' + index.toString()); - } - zone = zone.toLowerCase(); - // Check if zone is valid - const shard = ShardData.find( - (shard) => - shard.name.toLowerCase() === zone || - shard.nickname.toLowerCase() === zone || - shard.byte.toLowerCase() === zone, - ); - if (!shard) { - throw new Error('Invalid zone'); - } - - let newWallet: QuaiHDWallet; - let addrIndex: number = 0; - let zoneIndex: number = index + 1; - do { - newWallet = this.derivePath(addrIndex.toString()); - if ( - getShardForAddress(newWallet.address) == shard && - (newWallet.coinType == 969) == isUTXOAddress(newWallet.address) - ) - zoneIndex--; - addrIndex++; - } while (zoneIndex > 0); - - return newWallet; - } -} - -// // In crements the address_ index according to BIP-44 -// function incrementPathIndex(path: string): string { -// const parts = path.split('/'); -// const lastIndex = parseInt(parts[parts.length - 1], 10); -// parts[parts.length - 1] = (lastIndex + 1).toString(); -// return parts.join('/'); -// } - -/** - * A **HDNodeVoidWallet** cannot sign, but provides access to the children nodes of a - * [BIP-32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki) HD wallet addresses. - * - * The can be created by using an extended `xpub` key to - * {@link QuaiHDWallet.fromExtendedKey | **QuaiHDWallet.fromExtendedKey**} or by [nuetering](QuaiHDWallet-neuter) a - * {@link QuaiHDWallet | **QuaiHDWallet**}. - * - * @category Wallet - */ -export class HDNodeVoidWallet extends VoidSigner { - /** - * The compressed public key. - */ - readonly publicKey!: string; - - /** - * The fingerprint. - * - * A fingerprint allows quick qay to detect parent and child nodes, but developers should be prepared to deal with - * collisions as it is only 4 bytes. - */ - readonly fingerprint!: string; - - /** - * The parent node fingerprint. - */ - readonly accountFingerprint!: string; - - /** - * The chaincode, which is effectively a public key used to derive children. - */ - readonly chainCode!: string; - - /** - * The derivation path of this wallet. - * - * Since extended keys do not provider full path details, this may be `null`, if instantiated from a source that - * does not enocde it. - */ - readonly path!: null | string; - - /** - * The child index of this wallet. Values over `2 *\* 31` indicate the node is hardened. - */ - readonly index!: number; +export class QuaiHDWallet extends HDWallet { /** - * The depth of this wallet, which is the number of components in its path. + * The Quai cointype. */ - readonly depth!: number; + readonly coinType: number = QUAI_COINT_TYPE; /** - * @private + * Map of shard name (zone) to shardWalletData + * shardWalletData contains the private keys, addresses and derive indexes for the shard + * that are known to the wallet */ - constructor( - guard: any, - address: string, - publicKey: string, - accountFingerprint: string, - chainCode: string, - path: null | string, - index: number, - depth: number, - provider: null | Provider, - ) { - super(address, provider); - assertPrivate(guard, _guard, 'HDNodeVoidWallet'); + #shardWalletsMap: Map = new Map(); - defineProperties(this, { publicKey }); - - const fingerprint = dataSlice(ripemd160(sha256(publicKey)), 0, 4); - defineProperties(this, { - publicKey, - fingerprint, - accountFingerprint, - chainCode, - path, - index, - depth, - }); + get shardWalletsMap(): Map { + return this.#shardWalletsMap; } - connect(provider: null | Provider): HDNodeVoidWallet { - return new HDNodeVoidWallet( - _guard, - this.address, - this.publicKey, - this.accountFingerprint ?? '', - this.chainCode, - this.path, - this.index, - this.depth, - provider, - ); + set shardWallets(shardWallets: Map) { + this.#shardWalletsMap = shardWallets; + } + + constructor(guard: any, signingKey: SigningKey, accountFingerprint: string, chainCode: string, path: null | string, index: number, depth: number, mnemonic: null | Mnemonic, provider: null | Provider) { + super(guard, signingKey, accountFingerprint, chainCode, path, index, depth, mnemonic, provider); } - /** - * The extended key. - * - * This key will begin with the prefix `xpub` and can be used to reconstruct this neutered key to derive its - * children addresses. - */ - get extendedKey(): string { - // We only support the mainnet values for now, but if anyone needs - // testnet values, let me know. I believe current sentiment is that - // we should always use mainnet, and use BIP-44 to derive the network - // - Mainnet: public=0x0488B21E, private=0x0488ADE4 - // - Testnet: public=0x043587CF, private=0x04358394 - - assert(this.depth < 256, 'Depth too deep', 'UNSUPPORTED_OPERATION', { operation: 'extendedKey' }); - - return encodeBase58Check( - concat([ - '0x0488B21E', - zpad(this.depth, 1), - this.accountFingerprint ?? '', - zpad(this.index, 4), - this.chainCode, - this.publicKey, - ]), - ); - } - - /** - * Returns true if this wallet has a path, providing a Type Guard that the path is non-null. - * - * @returns {boolean} True if the path is non-null. - */ - hasPath(): this is { path: string } { - return this.path != null; - } - - /** - * Return the child for `index`. - * - * @param {Numeric} _index - The index of the child to derive. - * - * @returns {HDNodeVoidWallet} The derived child HD Node. - */ - deriveChild(_index: Numeric): HDNodeVoidWallet { - const index = getNumber(_index, 'index'); - assertArgument(index <= 0xffffffff, 'invalid index', 'index', index); - - // Base path - let path = this.path; - if (path) { - path += '/' + (index & ~HardenedBit); - if (index & HardenedBit) { - path += "'"; - } + async getAddress(zone: string): Promise { + let index = 0; + let shardWalletData: ShardWalletData | undefined = this.#shardWalletsMap.get(zone); + if (shardWalletData) { + const pos = shardWalletData.addressesInfo.length; + index = shardWalletData!.addressesInfo[pos-1].index + 1; + } else { + shardWalletData = {addressesInfo: []}; + this.#shardWalletsMap.set(zone, shardWalletData); } - const { IR, IL } = ser_I(index, this.chainCode, this.publicKey, null); - const Ki = SigningKey.addPoints(IL, this.publicKey, true); - - const address = computeAddress(Ki); - - return new HDNodeVoidWallet( - _guard, - address, - Ki, - this.fingerprint, - hexlify(IR), - path, - index, - this.depth + 1, - this.provider, - ); - } - - /** - * Return the signer for `path` from this node. - * - * @param {string} path - The path to derive. - * - * @returns {HDNodeVoidWallet} The derived HD Node. - */ - derivePath(path: string): HDNodeVoidWallet { - return derivePath(this, path); - } -} - -/* -export class QuaiHDWalletManager { - #root: QuaiHDWallet; - constructor(phrase: string, password?: null | string, path?: null | string, locale?: null | Wordlist) { - if (password == null) { password = ""; } - if (path == null) { path = "m/44'/60'/0'/0"; } - if (locale == null) { locale = LangEn.wordlist(); } - this.#root = QuaiHDWallet.fromPhrase(phrase, password, path, locale); + const addressInfo = this.deriveAddress(index, zone); + shardWalletData.addressesInfo.push(addressInfo); + return addressInfo.address; } - - getSigner(index?: number): QuaiHDWallet { - return this.#root.deriveChild((index == null) ? 0: index); - } -} -*/ - -/** - * Returns the [BIP-32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki) path for the account at `index`. - * - * This is the pattern used by wallets like Ledger. - * - * There is also an [alternate pattern](getIndexedAccountPath) used by some software. - * - * @category Wallet - * @param {Numeric} _index - The account index. - * - * @returns {string} The BIP44 derivation path for the specified account. - */ -export function getAccountPath(_index: Numeric): string { - const index = getNumber(_index, 'index'); - assertArgument(index >= 0 && index < HardenedBit, 'invalid account index', 'index', index); - return `m/44'/60'/${index}'/0/0`; -} - -/** - * Returns the path using an alternative pattern for deriving accounts, at `index`. - * - * This derivation path uses the //index// component rather than the //account// component to derive sequential - * accounts. - * - * This is the pattern used by wallets like MetaMask. - * - * @category Wallet - * @param {Numeric} _index - The account index. - * - * @returns {string} The BIP44 derivation path for the specified account. - */ -export function getIndexedAccountPath(_index: Numeric): string { - const index = getNumber(_index, 'index'); - assertArgument(index >= 0 && index < HardenedBit, 'invalid account index', 'index', index); - return `m/44'/60'/0'/0/${index}`; -} - -/** - * Returns a derivation path for a Qi blockchain account. - * - * @category Wallet - * @param {number} account - The account index (defaults to 0). - * - * @returns {string} The BIP44 derivation path for the specified account on the Qi blockchain. - */ -export function qiHDAccountPath(account: number = 0, change: boolean = false): string { - return `m/44'/969'/${account}'/${change ? 1 : 0}`; -} - -/** - * Returns a derivation path for a Quai blockchain account. - * - * @category Wallet - * @param {number} account - The account index (defaults to 0). - * - * @returns {string} The BIP44 derivation path for the specified account on the Quai blockchain. - */ -export function quaiHDAccountPath(account: number = 0): string { - return `m/44'/994'/${account}'/0`; } From 6f7a821c41def2606e3449b1e6765e25a9bc9560 Mon Sep 17 00:00:00 2001 From: Alejo Acosta Date: Mon, 20 May 2024 16:28:08 -0300 Subject: [PATCH 2/8] add new constants for Qi and Quai coin types --- src.ts/constants/coins.ts | 8 ++++++++ src.ts/constants/index.ts | 17 ++++++++++++----- src.ts/wallet/qi-hdwallet.ts | 14 +++++++------- src.ts/wallet/quai-hdwallet.ts | 4 ++-- 4 files changed, 29 insertions(+), 14 deletions(-) create mode 100644 src.ts/constants/coins.ts diff --git a/src.ts/constants/coins.ts b/src.ts/constants/coins.ts new file mode 100644 index 00000000..43f70747 --- /dev/null +++ b/src.ts/constants/coins.ts @@ -0,0 +1,8 @@ +/** + * Constants that define the coin type for Qi and Quai + * + * @category Constants + */ + +export const QI_COIN_TYPE = 969; +export const QUAI_COIN_TYPE = 994; \ No newline at end of file diff --git a/src.ts/constants/index.ts b/src.ts/constants/index.ts index bc29f028..dc26531b 100644 --- a/src.ts/constants/index.ts +++ b/src.ts/constants/index.ts @@ -4,8 +4,15 @@ * @_section: api/constants: Constants [about-constants] */ -export { ZeroAddress } from './addresses.js'; -export { ZeroHash } from './hashes.js'; -export { N, WeiPerEther, MaxUint256, MinInt256, MaxInt256 } from './numbers.js'; -export { quaisymbol, MessagePrefix } from './strings.js'; -export { ShardData } from './shards.js'; +export { ZeroAddress } from "./addresses.js"; +export { ZeroHash } from "./hashes.js"; +export { + N, + WeiPerEther, + MaxUint256, + MinInt256, + MaxInt256 +} from "./numbers.js"; +export { quaisymbol, MessagePrefix } from "./strings.js"; +export { ShardData } from "./shards.js"; +export { QI_COIN_TYPE, QUAI_COIN_TYPE } from "./coins.js"; diff --git a/src.ts/wallet/qi-hdwallet.ts b/src.ts/wallet/qi-hdwallet.ts index 04ae6a7d..0c9138ed 100644 --- a/src.ts/wallet/qi-hdwallet.ts +++ b/src.ts/wallet/qi-hdwallet.ts @@ -4,12 +4,13 @@ import { getBytes, getShardForAddress, hexlify } from '../utils/index.js'; import { Provider, QiTransactionRequest } from '../providers/index.js'; import { TransactionLike, computeAddress, QiTransaction, TxInput } from '../transaction/index.js'; import { Mnemonic } from './mnemonic.js'; -import { HDWallet, AddressInfo } from './hdwallet.js'; -import { MuSigFactory } from '@brandonblack/musig'; -import { nobleCrypto } from './musig-crypto.js'; -import { schnorr } from '@noble/curves/secp256k1'; -import { keccak_256 } from '@noble/hashes/sha3'; -import { getAddress } from '../address/index.js'; +import { HDWallet, AddressInfo } from "./hdwallet.js"; +import { MuSigFactory } from "@brandonblack/musig" +import { nobleCrypto } from "./musig-crypto.js"; +import { schnorr } from "@noble/curves/secp256k1"; +import { keccak_256 } from "@noble/hashes/sha3"; +import { getAddress } from "../address/index.js"; +import {QI_COIN_TYPE} from '../constants/index.js'; type Outpoint = { Txhash: string; @@ -24,7 +25,6 @@ type ShardWalletData = { }; const GAP = 20; -const QI_COIN_TYPE = 969; /** * @category Wallet diff --git a/src.ts/wallet/quai-hdwallet.ts b/src.ts/wallet/quai-hdwallet.ts index 05ae5e41..c72c4f2b 100644 --- a/src.ts/wallet/quai-hdwallet.ts +++ b/src.ts/wallet/quai-hdwallet.ts @@ -7,8 +7,8 @@ import { SigningKey } from "../crypto/index.js"; import { Mnemonic } from "./mnemonic.js"; import type { Provider } from "../providers/index.js"; import { HDWallet, AddressInfo} from "./hdwallet.js"; +import { QUAI_COIN_TYPE } from '../constants/index.js'; -const QUAI_COINT_TYPE = 994; // keeps track of the addresses and outpoints for a given shard (zone) type ShardWalletData = { @@ -29,7 +29,7 @@ export class QuaiHDWallet extends HDWallet { /** * The Quai cointype. */ - readonly coinType: number = QUAI_COINT_TYPE; + readonly coinType: number = QUAI_COIN_TYPE; /** * Map of shard name (zone) to shardWalletData From 53fdb6042030090f09d139c4cfe078c6e60dd464 Mon Sep 17 00:00:00 2001 From: Alejo Acosta Date: Tue, 21 May 2024 12:58:53 -0300 Subject: [PATCH 3/8] rename 'isUTXOAddress' to 'isQiAddress' --- src.ts/contract/factory.ts | 10 +++---- src.ts/providers/abstract-signer.ts | 19 ++++--------- src.ts/quais.ts | 2 +- src.ts/transaction/qi-transaction.ts | 8 +++--- src.ts/transaction/quai-transaction.ts | 6 ++-- src.ts/utils/index.ts | 2 +- src.ts/utils/shards.ts | 37 ++++++++++++------------- src.ts/wallet/hdwallet.ts | 38 ++++++++++++++++++++++++++ 8 files changed, 75 insertions(+), 47 deletions(-) diff --git a/src.ts/contract/factory.ts b/src.ts/contract/factory.ts index 631f2077..6cbd083d 100644 --- a/src.ts/contract/factory.ts +++ b/src.ts/contract/factory.ts @@ -7,7 +7,7 @@ import type { InterfaceAbi } from '../abi/index.js'; import type { Addressable } from '../address/index.js'; import type { ContractRunner } from '../providers/index.js'; import type { BytesLike } from '../utils/index.js'; -import { getShardForAddress, isUTXOAddress } from '../utils/index.js'; +import { getShardForAddress, isQiAddress } from '../utils/index.js'; import type { ContractInterface, ContractMethodArgs, ContractDeployTransaction } from './types.js'; import type { ContractTransactionResponse } from './wrappers.js'; import { Wallet, randomBytes } from '../quais.js'; @@ -221,10 +221,10 @@ export class ContractFactory = Array, I = BaseContract let i = 0; const startingData = tx.data; while (i < 10000) { - const contractAddress = getContractAddress(sender, BigInt(tx.nonce || 0), tx.data || ''); - const contractShard = getShardForAddress(contractAddress); - console.log('contractAddress ', contractAddress); - const utxo = isUTXOAddress(contractAddress); + var contractAddress = getContractAddress(sender, BigInt(tx.nonce || 0), tx.data || ''); + var contractShard = getShardForAddress(contractAddress); + console.log("contractAddress ", contractAddress); + var utxo = isQiAddress(contractAddress); if (contractShard === toShard && !utxo) { return tx; } diff --git a/src.ts/providers/abstract-signer.ts b/src.ts/providers/abstract-signer.ts index 14a3c8bf..de4c190a 100644 --- a/src.ts/providers/abstract-signer.ts +++ b/src.ts/providers/abstract-signer.ts @@ -6,19 +6,10 @@ */ import { AddressLike, resolveAddress } from '../address/index.js'; import { - defineProperties, - getBigInt, - resolveProperties, - assert, - assertArgument, - isUTXOAddress, -} from '../utils/index.js'; -import { - addressFromTransactionRequest, - copyRequest, - QiTransactionRequest, - QuaiTransactionRequest, -} from './provider.js'; + defineProperties, getBigInt, resolveProperties, + assert, assertArgument, isQiAddress +} from "../utils/index.js"; +import {addressFromTransactionRequest, copyRequest, QiTransactionRequest, QuaiTransactionRequest} from "./provider.js"; import type { TypedDataDomain, TypedDataField } from '../hash/index.js'; import type { TransactionLike } from '../transaction/index.js'; @@ -191,7 +182,7 @@ export abstract class AbstractSigner

implements QiTran throw new Error('Transaction must have at least one input and one output'); } - const destUtxo = isUTXOAddress(hexlify(this.txOutputs[0].address) || ''); + const destUtxo = isQiAddress(hexlify(this.txOutputs[0].address) || ""); const pubKey = hexlify(this.txInputs[0].pub_key); - const senderAddr = computeAddress(pubKey || ''); - const originUtxo = isUTXOAddress(senderAddr); + const senderAddr = computeAddress(pubKey || ""); + const originUtxo = isQiAddress(senderAddr); if (!this.destShard || !this.originShard) { throw new Error( diff --git a/src.ts/transaction/quai-transaction.ts b/src.ts/transaction/quai-transaction.ts index 7d946542..b0cb9459 100644 --- a/src.ts/transaction/quai-transaction.ts +++ b/src.ts/transaction/quai-transaction.ts @@ -11,7 +11,7 @@ import { getBytes, getNumber, getShardForAddress, - hexlify, isUTXOAddress, + hexlify, isQiAddress, toBeArray, toBigInt, zeroPadValue } from "../utils/index.js"; import {getAddress} from "../address/index.js"; @@ -123,8 +123,8 @@ export class QuaiTransaction extends AbstractTransaction implements Q return this.unsignedHash; } get unsignedHash(): string { - const destUtxo = isUTXOAddress(this.to || ''); - const originUtxo = isUTXOAddress(this.from); + const destUtxo = isQiAddress(this.to || ""); + const originUtxo = isQiAddress(this.from); if (!this.originShard) { throw new Error("Invalid Shard for from or to address"); diff --git a/src.ts/utils/index.ts b/src.ts/utils/index.ts index 00c26285..68a536ef 100644 --- a/src.ts/utils/index.ts +++ b/src.ts/utils/index.ts @@ -65,7 +65,7 @@ export { toUtf8Bytes, toUtf8CodePoints, toUtf8String, Utf8ErrorFuncs } from './u export { uuidV4 } from './uuid.js'; -export { getTxType, getShardForAddress, getAddressDetails, isUTXOAddress } from './shards.js'; +export { getTxType, getShardForAddress, getAddressDetails, isQiAddress } from './shards.js'; ///////////////////////////// // Types diff --git a/src.ts/utils/shards.ts b/src.ts/utils/shards.ts index 6a87c65c..e4a3761a 100644 --- a/src.ts/utils/shards.ts +++ b/src.ts/utils/shards.ts @@ -1,5 +1,4 @@ -import { ShardData } from '../constants/shards.js'; - +import { ShardData } from "../constants/shards.js"; /** * Retrieves the shard information for a given address based on its byte prefix. The function parses the address to * extract its byte prefix, then filters the ShardData to find a matching shard entry. If no matching shard is found, it @@ -64,9 +63,9 @@ export function getAddressDetails(address: string): { shard: any; isUTXO: boolea * @returns {number} The transaction type based on the addresses. */ export function getTxType(from: string | null, to: string | null): number { - if (from === null || to === null) return 0; - const fromUTXO = isUTXOAddress(from); - const toUTXO = isUTXOAddress(to); + if (from === null || to === null) return 0; + const fromUTXO = isQiAddress(from); + const toUTXO = isQiAddress(to); switch (true) { case fromUTXO && toUTXO: @@ -79,19 +78,19 @@ export function getTxType(from: string | null, to: string | null): number { } /** - * Checks whether a given blockchain address is a UTXO address based on the 9th bit of the address. This function - * extracts the second byte of the address and checks its first bit to determine the UTXO status. - * - * @category Utils - * @param {string} address - The blockchain address to be analyzed, expected to start with "0x" followed by its - * hexadecimal representation. - * - * @returns {boolean} True if the address is a UTXO address, false otherwise. - */ -export function isUTXOAddress(address: string): boolean { - const secondByte = address.substring(4, 6); - const binaryString = parseInt(secondByte, 16).toString(2).padStart(8, '0'); - const isUTXO = binaryString[0] === '1'; + * Checks whether a given blockchain address is a UTXO address based on the 9th bit of + * the address. This function extracts the second byte of the address and checks its + * first bit to determine the UTXO status. + * + * @param {string} address - The blockchain address to be analyzed, expected to start with "0x" followed by its hexadecimal representation. + * @returns {boolean} True if the address is a UTXO address, false otherwise. + * + * @category Utils + */ +export function isQiAddress(address: string): boolean { + const secondByte = address.substring(4, 6); + const binaryString = parseInt(secondByte, 16).toString(2).padStart(8, '0'); + const isUTXO = binaryString[0] === '1'; - return isUTXO; + return isUTXO; } diff --git a/src.ts/wallet/hdwallet.ts b/src.ts/wallet/hdwallet.ts index 41c8620d..9246c36e 100644 --- a/src.ts/wallet/hdwallet.ts +++ b/src.ts/wallet/hdwallet.ts @@ -7,6 +7,7 @@ import { computeHmac, randomBytes, SigningKey, sha256, ripemd160 } from '../cryp import { VoidSigner, Provider } from '../providers/index.js'; import { computeAddress } from '../transaction/index.js'; import { +<<<<<<< HEAD concat, decodeBase58, isBytesLike, @@ -34,6 +35,34 @@ import type { ProgressCallback } from '../crypto/index.js'; import type { Wordlist } from '../wordlists/index.js'; import type { KeystoreAccount } from './json-keystore.js'; import { encodeBase58Check, zpad, HardenedBit, ser_I, derivePath, MasterSecret, HDNodeLike } from './utils.js'; +======= + computeHmac, randomBytes, SigningKey, + sha256, ripemd160, +} from "../crypto/index.js"; +import { VoidSigner, Provider } from "../providers/index.js"; +import {computeAddress} from '../transaction/index.js'; +import { + concat, decodeBase58, isBytesLike, getNumber, + toBeArray, toBigInt, toBeHex, assertPrivate, + assert, assertArgument, hexlify, getShardForAddress, + isQiAddress, BytesLike, Numeric, defineProperties, + getBytes, dataSlice, +} from "../utils/index.js"; +import { BaseWallet } from "./base-wallet.js"; +import { Mnemonic } from "./mnemonic.js"; +import { + encryptKeystoreJson, + encryptKeystoreJsonSync, +} from "./json-keystore.js"; +import { N } from "../constants/index.js"; +import type { ProgressCallback } from "../crypto/index.js"; +import type { Wordlist } from "../wordlists/index.js"; +import type { KeystoreAccount } from "./json-keystore.js"; +import { + encodeBase58Check,zpad, HardenedBit, + ser_I,derivePath,MasterSecret,HDNodeLike, +} from "./utils.js"; +>>>>>>> da8321b5 (rename 'isUTXOAddress' to 'isQiAddress') const _guard = {}; // Constant to represent the maximum attempt to derive an address @@ -509,6 +538,7 @@ export abstract class HDWallet extends BaseWallet implements HDNodeLike { return ( @@ -517,6 +547,14 @@ export abstract class HDWallet extends BaseWallet implements HDNodeLike { + return (getShardForAddress(address)?.nickname.toLowerCase() === zone && + newWallet.coinType == this.coinType && + isQiAddress(address) == true); + } +>>>>>>> da8321b5 (rename 'isUTXOAddress' to 'isQiAddress') let addrIndex: number = startingIndex; do { From e0403ad8a3c44acc1bc800a4211dc1fbb65ba10e Mon Sep 17 00:00:00 2001 From: Alejo Acosta Date: Tue, 21 May 2024 18:55:23 -0300 Subject: [PATCH 4/8] modify 'deriveAddress' method to support both Qi and Quai --- src.ts/wallet/hdwallet.ts | 68 +++++++++++++++++----------------- src.ts/wallet/qi-hdwallet.ts | 2 +- src.ts/wallet/quai-hdwallet.ts | 2 +- 3 files changed, 37 insertions(+), 35 deletions(-) diff --git a/src.ts/wallet/hdwallet.ts b/src.ts/wallet/hdwallet.ts index 9246c36e..1c0f9d6a 100644 --- a/src.ts/wallet/hdwallet.ts +++ b/src.ts/wallet/hdwallet.ts @@ -7,7 +7,6 @@ import { computeHmac, randomBytes, SigningKey, sha256, ripemd160 } from '../cryp import { VoidSigner, Provider } from '../providers/index.js'; import { computeAddress } from '../transaction/index.js'; import { -<<<<<<< HEAD concat, decodeBase58, isBytesLike, @@ -20,7 +19,7 @@ import { assertArgument, hexlify, getShardForAddress, - isUTXOAddress, + isQiAddress, BytesLike, Numeric, defineProperties, @@ -35,34 +34,6 @@ import type { ProgressCallback } from '../crypto/index.js'; import type { Wordlist } from '../wordlists/index.js'; import type { KeystoreAccount } from './json-keystore.js'; import { encodeBase58Check, zpad, HardenedBit, ser_I, derivePath, MasterSecret, HDNodeLike } from './utils.js'; -======= - computeHmac, randomBytes, SigningKey, - sha256, ripemd160, -} from "../crypto/index.js"; -import { VoidSigner, Provider } from "../providers/index.js"; -import {computeAddress} from '../transaction/index.js'; -import { - concat, decodeBase58, isBytesLike, getNumber, - toBeArray, toBigInt, toBeHex, assertPrivate, - assert, assertArgument, hexlify, getShardForAddress, - isQiAddress, BytesLike, Numeric, defineProperties, - getBytes, dataSlice, -} from "../utils/index.js"; -import { BaseWallet } from "./base-wallet.js"; -import { Mnemonic } from "./mnemonic.js"; -import { - encryptKeystoreJson, - encryptKeystoreJsonSync, -} from "./json-keystore.js"; -import { N } from "../constants/index.js"; -import type { ProgressCallback } from "../crypto/index.js"; -import type { Wordlist } from "../wordlists/index.js"; -import type { KeystoreAccount } from "./json-keystore.js"; -import { - encodeBase58Check,zpad, HardenedBit, - ser_I,derivePath,MasterSecret,HDNodeLike, -} from "./utils.js"; ->>>>>>> da8321b5 (rename 'isUTXOAddress' to 'isQiAddress') const _guard = {}; // Constant to represent the maximum attempt to derive an address @@ -524,6 +495,7 @@ export abstract class HDWallet extends BaseWallet implements HDNodeLike>>>>>> 78eadd47 (modify 'deriveAddress' method to support both Qi and Quai) let newWallet: this; +<<<<<<< HEAD <<<<<<< HEAD // helper function to check if the generated address is valid for the specified zone const isValidAddressForZone = (address: string) => { @@ -549,11 +534,17 @@ export abstract class HDWallet extends BaseWallet implements HDNodeLike>>>>>> 78eadd47 (modify 'deriveAddress' method to support both Qi and Quai) const isValidAddressForZone = (address: string) => { - return (getShardForAddress(address)?.nickname.toLowerCase() === zone && - newWallet.coinType == this.coinType && - isQiAddress(address) == true); + const shardNickname = getShardForAddress(address)?.nickname.toLowerCase(); + const isCorrectShard = shardNickname === zone; + const isCorrectCoinType = newWallet.coinType === this.coinType; + const isCorrectLedger = (ledgerType === 'Qi') ? isQiAddress(address) : !isQiAddress(address); + + return isCorrectShard && isCorrectCoinType && isCorrectLedger; } +<<<<<<< HEAD >>>>>>> da8321b5 (rename 'isUTXOAddress' to 'isQiAddress') let addrIndex: number = startingIndex; @@ -567,6 +558,17 @@ export abstract class HDWallet extends BaseWallet implements HDNodeLike MAX_ADDRESS_DERIVATION_ATTEMPTS) { + throw new Error(`Failed to derive a valid address for the zone ${zone} after MAX_ADDRESS_DERIVATION_ATTEMPTS attempts.`); + } + } while (!isValidAddressForZone(newWallet.address)); +>>>>>>> 78eadd47 (modify 'deriveAddress' method to support both Qi and Quai) const addresInfo = { address: newWallet.address, privKey: newWallet.privateKey, index: addrIndex - 1 }; diff --git a/src.ts/wallet/qi-hdwallet.ts b/src.ts/wallet/qi-hdwallet.ts index 0c9138ed..32e86334 100644 --- a/src.ts/wallet/qi-hdwallet.ts +++ b/src.ts/wallet/qi-hdwallet.ts @@ -108,7 +108,7 @@ export class QiHDWallet extends HDWallet { let derivationIndex = 0; while (nakedCount < GAP) { - const addressInfo = this.deriveAddress(derivationIndex, zone); + const addressInfo = this.deriveAddress(derivationIndex, zone, "Qi"); // store the address, private key and index shardWalletData.addressesInfo.push(addressInfo); // query the network node for the outpoints of the address and update the balance diff --git a/src.ts/wallet/quai-hdwallet.ts b/src.ts/wallet/quai-hdwallet.ts index c72c4f2b..9df7a1e6 100644 --- a/src.ts/wallet/quai-hdwallet.ts +++ b/src.ts/wallet/quai-hdwallet.ts @@ -61,7 +61,7 @@ export class QuaiHDWallet extends HDWallet { this.#shardWalletsMap.set(zone, shardWalletData); } - const addressInfo = this.deriveAddress(index, zone); + const addressInfo = this.deriveAddress(index, zone, "Quai"); shardWalletData.addressesInfo.push(addressInfo); return addressInfo.address; } From 075091c7ed8394ea257ade5e19e2cda3b2920296 Mon Sep 17 00:00:00 2001 From: Alejo Acosta Date: Tue, 21 May 2024 19:06:45 -0300 Subject: [PATCH 5/8] remove unused methods and features from 'Wallet' class --- src.ts/wallet/wallet.ts | 82 ++++++++--------------------------------- 1 file changed, 15 insertions(+), 67 deletions(-) diff --git a/src.ts/wallet/wallet.ts b/src.ts/wallet/wallet.ts index 561ef844..6ec977c9 100644 --- a/src.ts/wallet/wallet.ts +++ b/src.ts/wallet/wallet.ts @@ -5,26 +5,15 @@ import { BaseWallet } from './base-wallet.js'; import { QuaiHDWallet } from './quai-hdwallet.js'; import { decryptCrowdsaleJson, isCrowdsaleJson } from './json-crowdsale.js'; import { - decryptKeystoreJson, - decryptKeystoreJsonSync, - encryptKeystoreJson, - encryptKeystoreJsonSync, - isKeystoreJson, -} from './json-keystore.js'; -import { Mnemonic } from './mnemonic.js'; + decryptKeystoreJson, decryptKeystoreJsonSync, + encryptKeystoreJson, encryptKeystoreJsonSync, + isKeystoreJson +} from "./json-keystore.js"; -import type { ProgressCallback } from '../crypto/index.js'; -import type { Provider } from '../providers/index.js'; -import type { CrowdsaleAccount } from './json-crowdsale.js'; -import type { KeystoreAccount } from './json-keystore.js'; - -function stall(duration: number): Promise { - return new Promise((resolve) => { - setTimeout(() => { - resolve(); - }, duration); - }); -} +import type { ProgressCallback } from "../crypto/index.js"; +import type { Provider } from "../providers/index.js"; +import type { CrowdsaleAccount } from "./json-crowdsale.js"; +import type { KeystoreAccount } from "./json-keystore.js"; /** * A **Wallet** manages a single private key which is used to sign transactions, messages and other common payloads. @@ -85,17 +74,8 @@ export class Wallet extends BaseWallet { return encryptKeystoreJsonSync(account, password); } - static #fromAccount(account: null | CrowdsaleAccount | KeystoreAccount): QuaiHDWallet | Wallet { - assertArgument(account, 'invalid JSON wallet', 'json', '[ REDACTED ]'); - - if ('mnemonic' in account && account.mnemonic && account.mnemonic.locale === 'en') { - const mnemonic = Mnemonic.fromEntropy(account.mnemonic.entropy); - const wallet = QuaiHDWallet.fromMnemonic(mnemonic, account.mnemonic.path || ''); // Add a check for undefined path - if (wallet.address === account.address && wallet.privateKey === account.privateKey) { - return wallet; - } - console.log('WARNING: JSON mismatch address/privateKey != mnemonic; fallback onto private key'); - } + static #fromAccount(account: KeystoreAccount): Wallet { + assertArgument(account, "invalid JSON wallet", "json", "[ REDACTED ]"); const wallet = new Wallet(account.privateKey); @@ -115,27 +95,14 @@ export class Wallet extends BaseWallet { * * @returns {Promise} The decrypted wallet. */ - static async fromEncryptedJson( - json: string, - password: Uint8Array | string, - progress?: ProgressCallback, - ): Promise { - let account: null | CrowdsaleAccount | KeystoreAccount = null; + static async fromEncryptedJson(json: string, password: Uint8Array | string, progress?: ProgressCallback): Promise { + let account: KeystoreAccount; if (isKeystoreJson(json)) { account = await decryptKeystoreJson(json, password, progress); - } else if (isCrowdsaleJson(json)) { - if (progress) { - progress(0); - await stall(0); - } - account = decryptCrowdsaleJson(json, password); - if (progress) { - progress(1); - await stall(0); - } - } + return Wallet.#fromAccount(account); + } + throw new Error("invalid JSON wallet"); - return Wallet.#fromAccount(account); } /** @@ -161,23 +128,4 @@ export class Wallet extends BaseWallet { return Wallet.#fromAccount(account); } - - /** - * Creates a new random {@link QuaiHDWallet | **QuaiHDWallet**} using the available [cryptographic random - * source](randomBytes). - * - * If there is no crytographic random source, this will throw. - * - * @param {string} path - The derivation path for the wallet. - * @param {Provider} [provider] - An optional provider to connect the wallet to. - * - * @returns {QuaiHDWallet} The new wallet. - */ - static createRandom(path: string, provider?: null | Provider): QuaiHDWallet { - const wallet = QuaiHDWallet.createRandom(path); - if (provider) { - return wallet.connect(provider); - } - return wallet; - } } From 0f1d7558aba5fef3c405dd8cf18a10a3859b695d Mon Sep 17 00:00:00 2001 From: Alejo Acosta Date: Tue, 21 May 2024 19:31:35 -0300 Subject: [PATCH 6/8] update index files --- src.ts/quais.ts | 5 ----- src.ts/wallet/index.ts | 4 +++- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src.ts/quais.ts b/src.ts/quais.ts index bac1aee5..7455dc64 100644 --- a/src.ts/quais.ts +++ b/src.ts/quais.ts @@ -120,15 +120,10 @@ export { Mnemonic, BaseWallet, QuaiHDWallet, HDNodeVoidWallet, QiHDWallet, Wallet, - - - getAccountPath, getIndexedAccountPath, isCrowdsaleJson, isKeystoreJson, - decryptCrowdsaleJson, decryptKeystoreJsonSync, decryptKeystoreJson, encryptKeystoreJson, encryptKeystoreJsonSync, - quaiHDAccountPath, qiHDAccountPath, nobleCrypto, } from "./wallet/index.js"; diff --git a/src.ts/wallet/index.ts b/src.ts/wallet/index.ts index 77694768..b591edca 100644 --- a/src.ts/wallet/index.ts +++ b/src.ts/wallet/index.ts @@ -36,4 +36,6 @@ export type { KeystoreAccount, EncryptOptions } from './json-keystore.js'; export { QiHDWallet } from './qi-hdwallet.js'; -export { nobleCrypto } from './musig-crypto.js'; +export { HDNodeVoidWallet } from "./hdwallet.js"; + +export { nobleCrypto } from "./musig-crypto.js"; From ca2803af02bb96bd7853cd1fb1a1e2788ef42520 Mon Sep 17 00:00:00 2001 From: Alejo Acosta Date: Wed, 22 May 2024 09:59:43 -0300 Subject: [PATCH 7/8] update tests to allow compilation --- src.ts/_tests/test-providers-errors.ts | 10 ++- src.ts/_tests/test-wallet-hd.ts | 104 ++++++++++++------------- src.ts/_tests/test-wallet-json.ts | 11 ++- src.ts/_tests/test-wallet.ts | 62 ++++++++------- src.ts/wallet/hdwallet.ts | 55 ++----------- src.ts/wallet/index.ts | 2 + 6 files changed, 107 insertions(+), 137 deletions(-) diff --git a/src.ts/_tests/test-providers-errors.ts b/src.ts/_tests/test-providers-errors.ts index 76b72ba0..10319580 100644 --- a/src.ts/_tests/test-providers-errors.ts +++ b/src.ts/_tests/test-providers-errors.ts @@ -5,8 +5,11 @@ import { concat, dataSlice, id, toBeArray, zeroPadValue, isCallException, isErro import { getProvider, setupProviders, providerNames } from './create-provider.js'; import { stall } from './utils.js'; -import dotenv from 'dotenv'; -import { QuaiTransactionResponse } from '../providers/provider.js'; +import { HDWallet } from "../wallet/hdwallet.js"; +import type { HDWalletStatic } from "../wallet/hdwallet.js"; + +import dotenv from "dotenv"; +import { QuaiTransactionResponse } from "../providers/provider.js"; dotenv.config(); type TestCustomError = { @@ -242,7 +245,8 @@ describe("Test Provider Blockchain Errors", function() { it(`tests insufficient funds: ${providerName}`, async function () { this.timeout(60000); - const w = Wallet.createRandom("m/44'/60'/0'/0/0").connect(provider); + const WalletClass = HDWallet as typeof HDWallet & HDWalletStatic; + const w = WalletClass.createRandom("m/44'/60'/0'/0/0").connect(provider); await assert.rejects( async function () { diff --git a/src.ts/_tests/test-wallet-hd.ts b/src.ts/_tests/test-wallet-hd.ts index 56b72475..fd2c9c72 100644 --- a/src.ts/_tests/test-wallet-hd.ts +++ b/src.ts/_tests/test-wallet-hd.ts @@ -2,11 +2,19 @@ import assert from 'assert'; import { loadTests } from './utils.js'; -import { getBytes, wordlists, QuaiHDWallet, HDNodeVoidWallet, Mnemonic } from '../index.js'; -import type { Wordlist } from '../wordlists/index.js'; +import { + getBytes, wordlists, + QuaiHDWallet, Mnemonic, + QiHDWallet, +} from "../index.js"; + +import type { Wordlist } from "../wordlists/index.js"; + +import type { TestCaseMnemonic, TestCaseMnemonicNode } from "./types.js"; + +import type { HDWalletStatic } from "../wallet/hdwallet.js"; -import type { TestCaseMnemonic, TestCaseMnemonicNode } from './types.js'; const decoder = new TextDecoder(); function fromHex(hex: string): string { @@ -23,21 +31,21 @@ type Test = { test: TestCaseMnemonic; }; -describe('Test HDWallets', function () { - function checkWallet(wallet: QuaiHDWallet | HDNodeVoidWallet, test: TestCaseMnemonicNode): void { -// assert.equal(wallet.chainCode, test.chainCode, "chainCode"); +describe("Test HDWallets", function() { + function checkWallet(wallet: QuaiHDWallet | QiHDWallet, test: TestCaseMnemonicNode): void { + assert.equal(wallet.chainCode, test.chainCode, "chainCode"); assert.equal(wallet.depth, test.depth, "depth"); assert.equal(wallet.index, test.index, "index"); assert.equal(wallet.fingerprint, test.fingerprint, "fingerprint"); -// assert.equal(wallet.accountFingerprint, test.parentFingerprint, "parentFingerprint"); + assert.equal(wallet.accountFingerprint, test.parentFingerprint, "parentFingerprint"); assert.equal(wallet.publicKey, test.publicKey, "publicKey"); if (wallet instanceof QuaiHDWallet) { -// assert.equal(wallet.extendedKey, test.xpriv, "xpriv"); + assert.equal(wallet.extendedKey, test.xpriv, "xpriv"); assert.equal(wallet.privateKey, test.privateKey, "privateKey"); -// assert.equal(wallet.neuter().extendedKey, test.xpub, "xpub"); - } else if (wallet instanceof HDNodeVoidWallet) { -// assert.equal(wallet.extendedKey, test.xpub, "xpub"); + assert.equal(wallet.neuter().extendedKey, test.xpub, "xpub"); + } else if (wallet instanceof QiHDWallet) { + assert.equal(wallet.extendedKey, test.xpub, "xpub"); } } @@ -80,7 +88,8 @@ describe('Test HDWallets', function () { for (const { test, checkMnemonic, phrase, password, wordlist } of checks) { it(`computes the HD keys by mnemonic: ${test.name}`, function () { for (const subtest of test.nodes) { - const w = QuaiHDWallet.fromPhrase(phrase, subtest.path, password, wordlist); + const WalletClass = QuaiHDWallet as typeof QuaiHDWallet & HDWalletStatic; + const w = WalletClass.fromPhrase(phrase, password, subtest.path, wordlist); assert.ok(w instanceof QuaiHDWallet, "instanceof QuaiHDWallet"); assert.equal(w.path, subtest.path, "path") checkWallet(w, subtest); @@ -91,8 +100,9 @@ describe('Test HDWallets', function () { } for (const { test } of checks) { - it(`computes the HD keys by entropy: ${test.name}`, function () { - const seedRoot = QuaiHDWallet.fromSeed(test.seed); + it(`computes the HD keys by entropy: ${ test.name }`, function() { + const WalletClass = QuaiHDWallet as typeof QuaiHDWallet & HDWalletStatic; + const seedRoot = WalletClass.fromSeed(test.seed); for (const subtest of test.nodes) { const w = seedRoot.derivePath(subtest.path); assert.ok(w instanceof QuaiHDWallet, 'instanceof QuaiHDWallet'); @@ -113,42 +123,32 @@ describe('Test HDWallets', function () { } }); } - - for (const { test, phrase, password, wordlist } of checks) { - it(`computes the neutered HD keys by paths: ${ test.name }`, function() { - const root = QuaiHDWallet.fromPhrase(phrase, "m", password, wordlist).neuter(); - for (const subtest of test.nodes) { - if (subtest.path.indexOf("'") >= 0) { - assert.throws( - () => { - const w = root.derivePath(subtest.path); - console.log(w); - }, - (error: any) => { - return ( - error.code === 'UNSUPPORTED_OPERATION' && - error.message.match(/^cannot derive child of neutered node/) && - error.operation === 'deriveChild' - ); - }, - ); - } else { - const w = root.derivePath(subtest.path); - assert.ok(w instanceof HDNodeVoidWallet, 'instanceof HDNodeVoidWallet'); - assert.equal(w.path, subtest.path, 'path'); - checkWallet(w, subtest); - } - } - }); - } - - for (const { test } of checks) { - it(`computes the neutered HD keys by enxtended public key: ${test.name}`, function () { - for (const subtest of test.nodes) { - const w = QuaiHDWallet.fromExtendedKey(subtest.xpub); - assert.ok(w instanceof HDNodeVoidWallet, 'instanceof HDNodeVoidWallet'); - checkWallet(w, subtest); - } - }); - } + // ! TODO: Fix this test + // for (const { test, phrase, password, wordlist } of checks) { + // it(`computes the neutered HD keys by paths: ${ test.name }`, function() { + // const root = QuaiHDWallet.fromPhrase(phrase, "m", password, wordlist).neuter(); + // for (const subtest of test.nodes) { + // if (subtest.path.indexOf("'") >= 0) { + // assert.throws( + // () => { + // const w = root.derivePath(subtest.path); + // console.log(w); + // }, + // (error: any) => { + // return ( + // error.code === 'UNSUPPORTED_OPERATION' && + // error.message.match(/^cannot derive child of neutered node/) && + // error.operation === 'deriveChild' + // ); + // }, + // ); + // } else { + // const w = root.derivePath(subtest.path); + // assert.ok(w instanceof HDNodeVoidWallet, 'instanceof HDNodeVoidWallet'); + // assert.equal(w.path, subtest.path, 'path'); + // checkWallet(w, subtest); + // } + // } + // }); + // } }); diff --git a/src.ts/_tests/test-wallet-json.ts b/src.ts/_tests/test-wallet-json.ts index 203b84c9..8a8133a8 100644 --- a/src.ts/_tests/test-wallet-json.ts +++ b/src.ts/_tests/test-wallet-json.ts @@ -17,6 +17,9 @@ import { Wallet, } from '../index.js'; +import type { HDWalletStatic } from "../wallet/hdwallet.js"; +import { HDWallet } from "../wallet/hdwallet.js"; + describe('Tests JSON Wallet Formats', function () { const tests = loadTests('wallets'); tests.forEach((test) => { @@ -78,11 +81,11 @@ describe('Tests JSON Wallet Formats', function () { it('tests encrypting wallet with mnemonic', function () { this.timeout(20000); - const wallet = QuaiHDWallet.createRandom("m/44'/60'/0'/0/0"); + const WalletClass = QuaiHDWallet as typeof QuaiHDWallet & HDWalletStatic; + const wallet = WalletClass.createRandom("m/44'/60'/0'/0/0"); assert.ok(wallet.mnemonic, 'mnemonic'); const phrase = wallet.mnemonic.phrase; const json = wallet.encryptSync('foobar'); - const wallet2 = Wallet.fromEncryptedJsonSync(json, 'foobar'); assert.ok(wallet2 instanceof QuaiHDWallet && wallet2.mnemonic); @@ -165,8 +168,8 @@ describe('Tests Extra JSON Wallet Functions', function () { error: 'invalid scrypt p parameter', }, ]; - - const wallet = Wallet.createRandom("m/44'/994'/0'/0"); + const WalletClass = HDWallet as typeof HDWallet & HDWalletStatic; + const wallet = WalletClass.createRandom("m/44'/994'/0'/0"); const account = { address: wallet.address, privateKey: wallet.privateKey }; const password = 'foobar'; diff --git a/src.ts/_tests/test-wallet.ts b/src.ts/_tests/test-wallet.ts index dba2e183..d1289316 100644 --- a/src.ts/_tests/test-wallet.ts +++ b/src.ts/_tests/test-wallet.ts @@ -4,9 +4,13 @@ import { loadTests } from './utils.js'; import type { TestCaseAccount, TestCaseTypedData, TestCaseTransaction } from './types.js'; -import { hexlify, randomBytes, Wallet } from '../index.js'; +import { + // hexlify, + // randomBytes, + Wallet, +} from "../index.js"; -import type { QuaiHDWallet } from '../index.js'; +// import type { QuaiHDWallet } from "../index.js"; describe('Test Private Key Wallet', function () { const tests = loadTests('accounts'); @@ -60,30 +64,30 @@ describe('Test Typed-Data Signing (EIP-712)', function () { } }); -describe('Test Wallet Encryption', function () { - const password = 'foobar'; - - // Loop: - // 1 : random wallet (uses QuaiHDWallet under the hood) - // 2 : Wallet using private key (uses Wallet explicitly) - - for (let i = 0; i < 2; i++) { - let wallet: Wallet | QuaiHDWallet = Wallet.createRandom("m/44'/994'/0'/0"); - - it('encrypts a random wallet: sync', function () { - this.timeout(30000); - const json = wallet.encryptSync(password); - const decrypted = Wallet.fromEncryptedJsonSync(json, password); - assert.equal(decrypted.address, wallet.address, 'address'); - }); - - it('encrypts a random wallet: async', async function () { - this.timeout(30000); - const json = await wallet.encrypt(password); - const decrypted = await Wallet.fromEncryptedJson(json, password); - assert.equal(decrypted.address, wallet.address, 'address'); - }); - - wallet = new Wallet(hexlify(randomBytes(32))); - } -}); +// describe("Test Wallet Encryption", function() { +// const password = "foobar"; + +// // Loop: +// // 1 : random wallet (uses QuaiHDWallet under the hood) +// // 2 : Wallet using private key (uses Wallet explicitly) + +// for (let i = 0; i < 2; i++) { +// let wallet: Wallet | QuaiHDWallet = Wallet.createRandom("m/44'/994'/0'/0"); + +// it("encrypts a random wallet: sync", function() { +// this.timeout(30000); +// const json = wallet.encryptSync(password); +// const decrypted = Wallet.fromEncryptedJsonSync(json, password); +// assert.equal(decrypted.address, wallet.address, "address"); +// }); + +// it("encrypts a random wallet: async", async function() { +// this.timeout(30000); +// const json = await wallet.encrypt(password); +// const decrypted = await Wallet.fromEncryptedJson(json, password); +// assert.equal(decrypted.address, wallet.address, "address"); +// }); + +// wallet = new Wallet(hexlify(randomBytes(32))); +// } +// }); diff --git a/src.ts/wallet/hdwallet.ts b/src.ts/wallet/hdwallet.ts index 1c0f9d6a..7c1329d6 100644 --- a/src.ts/wallet/hdwallet.ts +++ b/src.ts/wallet/hdwallet.ts @@ -40,11 +40,11 @@ const _guard = {}; const MAX_ADDRESS_DERIVATION_ATTEMPTS = 10000000; // Used to type the instantiation of a child wallet class from static methods -interface HDWalletStatic { - new (...args: any[]): T; - _fromSeed(_seed: BytesLike, mnemonic: null | Mnemonic): T; - isValidPath(path: string): boolean; - derivePath(path: string): T; +export interface HDWalletStatic { + new(...args: any[]): T; + _fromSeed(_seed: BytesLike, mnemonic: null | Mnemonic): T; + isValidPath(path: string): boolean; + derivePath(path: string): T; } export type AddressInfo = { @@ -495,19 +495,6 @@ export abstract class HDWallet extends BaseWallet implements HDNodeLike>>>>>> 78eadd47 (modify 'deriveAddress' method to support both Qi and Quai) let newWallet: this; -<<<<<<< HEAD -<<<<<<< HEAD - // helper function to check if the generated address is valid for the specified zone - const isValidAddressForZone = (address: string) => { - return ( - getShardForAddress(address)?.nickname.toLowerCase() === zone && - newWallet.coinType == this.coinType && - isUTXOAddress(address) == true - ); - }; -======= - // helper function to check if the generated address is valid for the specified zone -======= ->>>>>>> 78eadd47 (modify 'deriveAddress' method to support both Qi and Quai) const isValidAddressForZone = (address: string) => { const shardNickname = getShardForAddress(address)?.nickname.toLowerCase(); const isCorrectShard = shardNickname === zone; @@ -544,21 +516,7 @@ export abstract class HDWallet extends BaseWallet implements HDNodeLike>>>>>> da8321b5 (rename 'isUTXOAddress' to 'isQiAddress') - - let addrIndex: number = startingIndex; - do { - newWallet = this.derivePath(addrIndex.toString()); - addrIndex++; - // put a hard limit on the number of addresses to derive - if (addrIndex - startingIndex > MAX_ADDRESS_DERIVATION_ATTEMPTS) { - throw new Error( - `Failed to derive a valid address for the zone ${zone} after MAX_ADDRESS_DERIVATION_ATTEMPTS attempts.`, - ); - } - } while (!isValidAddressForZone(newWallet.address)); -======= + let addrIndex: number = startingIndex; do { newWallet = this.derivePath(addrIndex.toString()); @@ -568,7 +526,6 @@ export abstract class HDWallet extends BaseWallet implements HDNodeLike>>>>>> 78eadd47 (modify 'deriveAddress' method to support both Qi and Quai) const addresInfo = { address: newWallet.address, privKey: newWallet.privateKey, index: addrIndex - 1 }; diff --git a/src.ts/wallet/index.ts b/src.ts/wallet/index.ts index b591edca..24675379 100644 --- a/src.ts/wallet/index.ts +++ b/src.ts/wallet/index.ts @@ -38,4 +38,6 @@ export { QiHDWallet } from './qi-hdwallet.js'; export { HDNodeVoidWallet } from "./hdwallet.js"; +export type { HDWalletStatic } from "./hdwallet.js"; + export { nobleCrypto } from "./musig-crypto.js"; From 0060cc6a11729ef2559d4178224068b05be55bf9 Mon Sep 17 00:00:00 2001 From: Alejo Acosta Date: Wed, 22 May 2024 11:11:19 -0300 Subject: [PATCH 8/8] change access modifier to 'protected' for 'startBlock' --- src.ts/providers/provider.ts | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src.ts/providers/provider.ts b/src.ts/providers/provider.ts index c1e06b24..945d6718 100644 --- a/src.ts/providers/provider.ts +++ b/src.ts/providers/provider.ts @@ -1547,9 +1547,7 @@ export class QuaiTransactionResponse implements QuaiTransactionLike, QuaiTransac */ readonly accessList!: null | AccessList; - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - #startBlock: number; + protected startBlock: number; /** * @ignore @@ -1580,7 +1578,7 @@ export class QuaiTransactionResponse implements QuaiTransactionLike, QuaiTransac this.signature = tx.signature; this.accessList = tx.accessList != null ? tx.accessList : null; - this.#startBlock = -1; + this.startBlock = -1; } /** @@ -1696,7 +1694,7 @@ export class QuaiTransactionResponse implements QuaiTransactionLike, QuaiTransac const confirms = _confirms == null ? 1 : _confirms; const timeout = _timeout == null ? 0 : _timeout; - let startBlock = this.#startBlock; + let startBlock = this.startBlock; let nextScan = -1; let stopScanning = startBlock === -1 ? true : false; const shard = shardFromHash(this.hash); @@ -1731,8 +1729,8 @@ export class QuaiTransactionResponse implements QuaiTransactionLike, QuaiTransac // Starting to scan; look back a few extra blocks for safety if (nextScan === -1) { nextScan = startBlock - 3; - if (nextScan < this.#startBlock) { - nextScan = this.#startBlock; + if (nextScan < this.startBlock) { + nextScan = this.startBlock; } } @@ -1967,7 +1965,7 @@ export class QuaiTransactionResponse implements QuaiTransactionLike, QuaiTransac replaceableTransaction(startBlock: number): QuaiTransactionResponse { assertArgument(Number.isInteger(startBlock) && startBlock >= 0, 'invalid startBlock', 'startBlock', startBlock); const tx = new QuaiTransactionResponse(this, this.provider); - tx.#startBlock = startBlock; + tx.startBlock = startBlock; return tx; } } @@ -2027,8 +2025,7 @@ export class QiTransactionResponse implements QiTransactionLike, QiTransactionRe readonly txOutputs?: Array; - // @ts-expect-error: #startBlock not used. Consider removing it. - #startBlock: number; + protected startBlock: number; /** * @ignore */ @@ -2046,7 +2043,7 @@ export class QiTransactionResponse implements QiTransactionLike, QiTransactionRe this.chainId = tx.chainId; this.signature = tx.signature; - this.#startBlock = -1; + this.startBlock = -1; this.txInputs = tx.txInputs; this.txOutputs = tx.txOutputs; @@ -2382,7 +2379,7 @@ export class QiTransactionResponse implements QiTransactionLike, QiTransactionRe replaceableTransaction(startBlock: number): QiTransactionResponse { assertArgument(Number.isInteger(startBlock) && startBlock >= 0, 'invalid startBlock', 'startBlock', startBlock); const tx = new QiTransactionResponse(this, this.provider); - tx.#startBlock = startBlock; + tx.startBlock = startBlock; return tx; } }