refactor: move brother_node development artifact to dev/test-nodes subdirectory

Development Artifact Cleanup:
 BROTHER_NODE REORGANIZATION: Moved development test node to appropriate location
- dev/test-nodes/brother_node/: Moved from root directory for better organization
- Contains development configuration, test logs, and test chain data
- No impact on production systems - purely development/testing artifact

 DEVELOPMENT ARTIFACTS IDENTIFIED:
- Chain ID: aitbc-brother-chain (test/development chain)
- Ports: 8010 (P2P) and 8011 (RPC) - different from production
- Environment: .env file with test configuration
- Logs: rpc.log and node.log from development testing session (March 15, 2026)

 ROOT DIRECTORY CLEANUP: Removed development clutter from production directory
- brother_node/ moved to dev/test-nodes/brother_node/
- Root directory now contains only production-ready components
- Development artifacts properly organized in dev/ subdirectory

DIRECTORY STRUCTURE IMPROVEMENT:
📁 dev/test-nodes/: Development and testing node configurations
🏗️ Root Directory: Clean production structure with only essential components
🧪 Development Isolation: Test environments separated from production

BENEFITS:
 Clean Production Directory: No development artifacts in root
 Better Organization: Development nodes grouped in dev/ subdirectory
 Clear Separation: Production vs development environments clearly distinguished
 Maintainability: Easier to identify and manage development components

RESULT: Successfully moved brother_node development artifact to dev/test-nodes/ subdirectory, cleaning up the root directory while preserving development testing environment for future use.
This commit is contained in:
2026-03-30 17:09:06 +02:00
parent bf730dcb4a
commit 816e258d4c
11734 changed files with 2001707 additions and 0 deletions

160
dev/env/node_modules/ethers/src.ts/wallet/base-wallet.ts generated vendored Executable file
View File

@@ -0,0 +1,160 @@
import { getAddress, resolveAddress } from "../address/index.js";
import {
hashAuthorization, hashMessage, TypedDataEncoder
} from "../hash/index.js";
import { AbstractSigner, copyRequest } from "../providers/index.js";
import { computeAddress, Transaction } from "../transaction/index.js";
import {
defineProperties, getBigInt, resolveProperties, assert, assertArgument
} from "../utils/index.js";
import type { SigningKey } from "../crypto/index.js";
import type {
AuthorizationRequest, TypedDataDomain, TypedDataField
} from "../hash/index.js";
import type { Provider, TransactionRequest } from "../providers/index.js";
import type { Authorization, TransactionLike } from "../transaction/index.js";
/**
* The **BaseWallet** is a stream-lined implementation of a
* [[Signer]] that operates with a private key.
*
* It is preferred to use the [[Wallet]] class, as it offers
* additional functionality and simplifies loading a variety
* of JSON formats, Mnemonic Phrases, etc.
*
* This class may be of use for those attempting to implement
* a minimal Signer.
*/
export class BaseWallet extends AbstractSigner {
/**
* The wallet address.
*/
readonly address!: string;
readonly #signingKey: SigningKey;
/**
* Creates a new BaseWallet for %%privateKey%%, optionally
* connected to %%provider%%.
*
* If %%provider%% is not specified, only offline methods can
* be used.
*/
constructor(privateKey: SigningKey, provider?: null | Provider) {
super(provider);
assertArgument(privateKey && typeof(privateKey.sign) === "function", "invalid private key", "privateKey", "[ REDACTED ]");
this.#signingKey = privateKey;
const address = computeAddress(this.signingKey.publicKey);
defineProperties<BaseWallet>(this, { address });
}
// Store private values behind getters to reduce visibility
// in console.log
/**
* The [[SigningKey]] used for signing payloads.
*/
get signingKey(): SigningKey { return this.#signingKey; }
/**
* The private key for this wallet.
*/
get privateKey(): string { return this.signingKey.privateKey; }
async getAddress(): Promise<string> { return this.address; }
connect(provider: null | Provider): BaseWallet {
return new BaseWallet(this.#signingKey, provider);
}
async signTransaction(tx: TransactionRequest): Promise<string> {
tx = copyRequest(tx);
// Replace any Addressable or ENS name with an address
const { to, from } = await resolveProperties({
to: (tx.to ? resolveAddress(tx.to, this): undefined),
from: (tx.from ? resolveAddress(tx.from, this): undefined)
});
if (to != null) { tx.to = to; }
if (from != null) { tx.from = from; }
if (tx.from != null) {
assertArgument(getAddress(<string>(tx.from)) === this.address,
"transaction from address mismatch", "tx.from", tx.from);
delete tx.from;
}
// Build the transaction
const btx = Transaction.from(<TransactionLike<string>>tx);
btx.signature = this.signingKey.sign(btx.unsignedHash);
return btx.serialized;
}
async signMessage(message: string | Uint8Array): Promise<string> {
return this.signMessageSync(message);
}
// @TODO: Add a secialized signTx and signTyped sync that enforces
// all parameters are known?
/**
* Returns the signature for %%message%% signed with this wallet.
*/
signMessageSync(message: string | Uint8Array): string {
return this.signingKey.sign(hashMessage(message)).serialized;
}
/**
* Returns the Authorization for %%auth%%.
*/
authorizeSync(auth: AuthorizationRequest): Authorization {
assertArgument(typeof(auth.address) === "string",
"invalid address for authorizeSync", "auth.address", auth);
const signature = this.signingKey.sign(hashAuthorization(auth));
return Object.assign({ }, {
address: getAddress(auth.address),
nonce: getBigInt(auth.nonce || 0),
chainId: getBigInt(auth.chainId || 0),
}, { signature });
}
/**
* Resolves to the Authorization for %%auth%%.
*/
async authorize(auth: AuthorizationRequest): Promise<Authorization> {
auth = Object.assign({ }, auth, {
address: await resolveAddress(auth.address, this)
});
return this.authorizeSync(await this.populateAuthorization(auth));
}
async signTypedData(domain: TypedDataDomain, types: Record<string, Array<TypedDataField>>, value: Record<string, any>): Promise<string> {
// Populate any ENS names
const populated = await TypedDataEncoder.resolveNames(domain, types, value, async (name: string) => {
// @TODO: this should use resolveName; addresses don't
// need a provider
assert(this.provider != null, "cannot resolve ENS names without a provider", "UNSUPPORTED_OPERATION", {
operation: "resolveName",
info: { name }
});
const address = await this.provider.resolveName(name);
assert(address != null, "unconfigured ENS name", "UNCONFIGURED_NAME", {
value: name
});
return address;
});
return this.signingKey.sign(TypedDataEncoder.hash(populated.domain, types, populated.value)).serialized;
}
}

586
dev/env/node_modules/ethers/src.ts/wallet/hdwallet.ts generated vendored Executable file
View File

@@ -0,0 +1,586 @@
/**
* Explain HD Wallets..
*
* @_subsection: 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, encodeBase58,
getBytes, hexlify, isBytesLike,
getNumber, toBeArray, toBigInt, toBeHex,
assertPrivate, assert, assertArgument
} from "../utils/index.js";
import { LangEn } from "../wordlists/lang-en.js";
import { BaseWallet } from "./base-wallet.js";
import { Mnemonic } from "./mnemonic.js";
import {
encryptKeystoreJson, encryptKeystoreJsonSync,
} from "./json-keystore.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";
/**
* The default derivation path for Ethereum HD Nodes. (i.e. ``"m/44'/60'/0'/0/0"``)
*/
export const defaultPath: string = "m/44'/60'/0'/0/0";
// "Bitcoin seed"
const MasterSecret = new Uint8Array([ 66, 105, 116, 99, 111, 105, 110, 32, 115, 101, 101, 100 ]);
const HardenedBit = 0x80000000;
const N = BigInt("0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141");
const Nibbles = "0123456789abcdef";
function zpad(value: number, length: number): string {
let result = "";
while (value) {
result = Nibbles[value % 16] + result;
value = Math.trunc(value / 16);
}
while (result.length < length * 2) { result = "0" + result; }
return "0x" + result;
}
function encodeBase58Check(_value: BytesLike): string {
const value = getBytes(_value);
const check = dataSlice(sha256(sha256(value)), 0, 4);
const bytes = concat([ value, check ]);
return encodeBase58(bytes);
}
const _guard = { };
function ser_I(index: number, chainCode: string, publicKey: string, privateKey: null | string): { IL: Uint8Array, IR: Uint8Array } {
const data = new Uint8Array(37);
if (index & HardenedBit) {
assert(privateKey != null, "cannot derive child of neutered node", "UNSUPPORTED_OPERATION", {
operation: "deriveChild"
});
// Data = 0x00 || ser_256(k_par)
data.set(getBytes(privateKey), 1);
} else {
// Data = ser_p(point(k_par))
data.set(getBytes(publicKey));
}
// Data += ser_32(i)
for (let i = 24; i >= 0; i -= 8) { data[33 + (i >> 3)] = ((index >> (24 - i)) & 0xff); }
const I = getBytes(computeHmac("sha512", chainCode, data));
return { IL: I.slice(0, 32), IR: I.slice(32) };
}
type HDNodeLike<T> = { depth: number, deriveChild: (i: number) => T };
function derivePath<T extends HDNodeLike<T>>(node: T, path: string): T {
const components = path.split("/");
assertArgument(components.length > 0, "invalid path", "path", path);
if (components[0] === "m") {
assertArgument(node.depth === 0, `cannot derive root path (i.e. path starting with "m/") for a node at non-zero depth ${ node.depth }`, "path", path);
components.shift();
}
let result: T = node;
for (let i = 0; i < components.length; i++) {
const component = components[i];
if (component.match(/^[0-9]+'$/)) {
const index = parseInt(component.substring(0, component.length - 1));
assertArgument(index < HardenedBit, "invalid path index", `path[${ i }]`, component);
result = result.deriveChild(HardenedBit + index);
} else if (component.match(/^[0-9]+$/)) {
const index = parseInt(component);
assertArgument(index < HardenedBit, "invalid path index", `path[${ i }]`, component);
result = result.deriveChild(index);
} else {
assertArgument(false, "invalid path component", `path[${ i }]`, component);
}
}
return result;
}
/**
* An **HDNodeWallet** is a [[Signer]] backed by the private key derived
* from an HD Node using the [[link-bip-32]] stantard.
*
* An HD Node forms a hierarchal structure with each HD Node having a
* private key and the ability to derive child HD Nodes, defined by
* a path indicating the index of each child.
*/
export class HDNodeWallet 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 parentFingerprint!: 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 provide full path details, this
* may be ``null``, if instantiated from a source that does not
* encode 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;
/**
* @private
*/
constructor(guard: any, signingKey: SigningKey, parentFingerprint: string, chainCode: string, path: null | string, index: number, depth: number, mnemonic: null | Mnemonic, provider: null | Provider) {
super(signingKey, provider);
assertPrivate(guard, _guard, "HDNodeWallet");
defineProperties<HDNodeWallet>(this, { publicKey: signingKey.compressedPublicKey });
const fingerprint = dataSlice(ripemd160(sha256(this.publicKey)), 0, 4);
defineProperties<HDNodeWallet>(this, {
parentFingerprint, fingerprint,
chainCode, path, index, depth
});
defineProperties<HDNodeWallet>(this, { mnemonic });
}
connect(provider: null | Provider): HDNodeWallet {
return new HDNodeWallet(_guard, this.signingKey, this.parentFingerprint,
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.
*/
async encrypt(password: Uint8Array | string, progressCallback?: ProgressCallback): Promise<string> {
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 [[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.
*/
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.parentFingerprint,
zpad(this.index, 4), this.chainCode,
concat([ "0x00", this.privateKey ])
]));
}
/**
* Returns true if this wallet has a path, providing a Type Guard
* that 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.
*/
neuter(): HDNodeVoidWallet {
return new HDNodeVoidWallet(_guard, this.address, this.publicKey,
this.parentFingerprint, this.chainCode, this.path, this.index,
this.depth, this.provider);
}
/**
* Return the child for %%index%%.
*/
deriveChild(_index: Numeric): HDNodeWallet {
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 += "'"; }
}
const { IR, IL } = ser_I(index, this.chainCode, this.publicKey, this.privateKey);
const ki = new SigningKey(toBeHex((toBigInt(IL) + BigInt(this.privateKey)) % N, 32));
return new HDNodeWallet(_guard, ki, this.fingerprint, hexlify(IR),
path, index, this.depth + 1, this.mnemonic, this.provider);
}
/**
* Return the HDNode for %%path%% from this node.
*/
derivePath(path: string): HDNodeWallet {
return derivePath<HDNodeWallet>(this, path);
}
static #fromSeed(_seed: BytesLike, mnemonic: null | Mnemonic): HDNodeWallet {
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)));
return new HDNodeWallet(_guard, signingKey, "0x00000000", hexlify(I.slice(32)),
"m", 0, 0, mnemonic, null);
}
/**
* Creates a new HD Node from %%extendedKey%%.
*
* If the %%extendedKey%% will either have a prefix or ``xpub`` or
* ``xpriv``, returning a neutered HD Node ([[HDNodeVoidWallet]])
* or full HD Node ([[HDNodeWallet) respectively.
*/
static fromExtendedKey(extendedKey: string): HDNodeWallet | 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 parentFingerprint = 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,
parentFingerprint, chainCode, null, index, depth, null);
}
// Private Key
case "0x0488ade4": case "0x04358394 ":
if (key[0] !== 0) { break; }
return new HDNodeWallet(_guard, new SigningKey(key.slice(1)),
parentFingerprint, chainCode, null, index, depth, null, null);
}
assertArgument(false, "invalid extended key prefix", "extendedKey", "[ REDACTED ]");
}
/**
* Creates a new random HDNode.
*/
static createRandom(password?: string, path?: string, wordlist?: Wordlist): HDNodeWallet {
if (password == null) { password = ""; }
if (path == null) { path = defaultPath; }
if (wordlist == null) { wordlist = LangEn.wordlist(); }
const mnemonic = Mnemonic.fromEntropy(randomBytes(16), password, wordlist)
return HDNodeWallet.#fromSeed(mnemonic.computeSeed(), mnemonic).derivePath(path);
}
/**
* Create an HD Node from %%mnemonic%%.
*/
static fromMnemonic(mnemonic: Mnemonic, path?: string): HDNodeWallet {
if (!path) { path = defaultPath; }
return HDNodeWallet.#fromSeed(mnemonic.computeSeed(), mnemonic).derivePath(path);
}
/**
* Creates an HD Node from a mnemonic %%phrase%%.
*/
static fromPhrase(phrase: string, password?: string, path?: string, wordlist?: Wordlist): HDNodeWallet {
if (password == null) { password = ""; }
if (path == null) { path = defaultPath; }
if (wordlist == null) { wordlist = LangEn.wordlist(); }
const mnemonic = Mnemonic.fromPhrase(phrase, password, wordlist)
return HDNodeWallet.#fromSeed(mnemonic.computeSeed(), mnemonic).derivePath(path);
}
/**
* Creates an HD Node from a %%seed%%.
*/
static fromSeed(seed: BytesLike): HDNodeWallet {
return HDNodeWallet.#fromSeed(seed, null);
}
}
/**
* A **HDNodeVoidWallet** cannot sign, but provides access to
* the children nodes of a [[link-bip-32]] HD wallet addresses.
*
* The can be created by using an extended ``xpub`` key to
* [[HDNodeWallet_fromExtendedKey]] or by
* [nuetering](HDNodeWallet-neuter) a [[HDNodeWallet]].
*/
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 parentFingerprint!: 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;
/**
* The depth of this wallet, which is the number of components
* in its path.
*/
readonly depth!: number;
/**
* @private
*/
constructor(guard: any, address: string, publicKey: string, parentFingerprint: string, chainCode: string, path: null | string, index: number, depth: number, provider: null | Provider) {
super(address, provider);
assertPrivate(guard, _guard, "HDNodeVoidWallet");
defineProperties<HDNodeVoidWallet>(this, { publicKey });
const fingerprint = dataSlice(ripemd160(sha256(publicKey)), 0, 4);
defineProperties<HDNodeVoidWallet>(this, {
publicKey, fingerprint, parentFingerprint, chainCode, path, index, depth
});
}
connect(provider: null | Provider): HDNodeVoidWallet {
return new HDNodeVoidWallet(_guard, this.address, this.publicKey,
this.parentFingerprint, this.chainCode, this.path, this.index, this.depth, 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.parentFingerprint,
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.
*/
hasPath(): this is { path: string } { return (this.path != null); }
/**
* Return the child for %%index%%.
*/
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 += "'"; }
}
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.
*/
derivePath(path: string): HDNodeVoidWallet {
return derivePath<HDNodeVoidWallet>(this, path);
}
}
/*
export class HDNodeWalletManager {
#root: HDNodeWallet;
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 = HDNodeWallet.fromPhrase(phrase, password, path, locale);
}
getSigner(index?: number): HDNodeWallet {
return this.#root.deriveChild((index == null) ? 0: index);
}
}
*/
/**
* Returns the [[link-bip-32]] 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.
*/
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.
*/
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}`;
}

47
dev/env/node_modules/ethers/src.ts/wallet/index.ts generated vendored Executable file
View File

@@ -0,0 +1,47 @@
/**
* When interacting with Ethereum, it is necessary to use a private
* key authenticate actions by signing a payload.
*
* Wallets are the simplest way to expose the concept of an
* //Externally Owner Account// (EOA) as it wraps a private key
* and supports high-level methods to sign common types of interaction
* and send transactions.
*
* The class most developers will want to use is [[Wallet]], which
* can load a private key directly or from any common wallet format.
*
* The [[HDNodeWallet]] can be used when it is necessary to access
* low-level details of how an HD wallets are derived, exported
* or imported.
*
* @_section: api/wallet:Wallets [about-wallets]
*/
export { BaseWallet } from "./base-wallet.js";
export {
defaultPath,
getAccountPath, getIndexedAccountPath,
HDNodeWallet,
HDNodeVoidWallet,
} from "./hdwallet.js";
export { isCrowdsaleJson, decryptCrowdsaleJson } from "./json-crowdsale.js";
export {
isKeystoreJson,
decryptKeystoreJsonSync, decryptKeystoreJson,
encryptKeystoreJson, encryptKeystoreJsonSync
} from "./json-keystore.js";
export { Mnemonic } from "./mnemonic.js";
export { Wallet } from "./wallet.js";
export type { CrowdsaleAccount } from "./json-crowdsale.js";
export type {
KeystoreAccount, EncryptOptions
} from "./json-keystore.js"

74
dev/env/node_modules/ethers/src.ts/wallet/json-crowdsale.ts generated vendored Executable file
View File

@@ -0,0 +1,74 @@
/**
* @_subsection: api/wallet:JSON Wallets [json-wallets]
*/
import { CBC, pkcs7Strip } from "aes-js";
import { getAddress } from "../address/index.js";
import { pbkdf2 } from "../crypto/index.js";
import { id } from "../hash/index.js";
import { getBytes, assertArgument } from "../utils/index.js";
import { getPassword, looseArrayify, spelunk } from "./utils.js";
/**
* The data stored within a JSON Crowdsale wallet is fairly
* minimal.
*/
export type CrowdsaleAccount = {
privateKey: string;
address: string;
}
/**
* Returns true if %%json%% is a valid JSON Crowdsale wallet.
*/
export function isCrowdsaleJson(json: string): boolean {
try {
const data = JSON.parse(json);
if (data.encseed) { return true; }
} catch (error) { }
return false;
}
// See: https://github.com/ethereum/pyethsaletool
/**
* Before Ethereum launched, it was necessary to create a wallet
* format for backers to use, which would be used to receive ether
* as a reward for contributing to the project.
*
* The [[link-crowdsale]] format is now obsolete, but it is still
* useful to support and the additional code is fairly trivial as
* all the primitives required are used through core portions of
* the library.
*/
export function decryptCrowdsaleJson(json: string, _password: string | Uint8Array): CrowdsaleAccount {
const data = JSON.parse(json);
const password = getPassword(_password);
// Ethereum Address
const address = getAddress(spelunk(data, "ethaddr:string!"));
// Encrypted Seed
const encseed = looseArrayify(spelunk(data, "encseed:string!"));
assertArgument(encseed && (encseed.length % 16) === 0, "invalid encseed", "json", json);
const key = getBytes(pbkdf2(password, password, 2000, 32, "sha256")).slice(0, 16);
const iv = encseed.slice(0, 16);
const encryptedSeed = encseed.slice(16);
// Decrypt the seed
const aesCbc = new CBC(key, iv);
const seed = pkcs7Strip(getBytes(aesCbc.decrypt(encryptedSeed)));
// This wallet format is weird... Convert the binary encoded hex to a string.
let seedHex = "";
for (let i = 0; i < seed.length; i++) {
seedHex += String.fromCharCode(seed[i]);
}
return { address, privateKey: id(seedHex) };
}

389
dev/env/node_modules/ethers/src.ts/wallet/json-keystore.ts generated vendored Executable file
View File

@@ -0,0 +1,389 @@
/**
* The JSON Wallet formats allow a simple way to store the private
* keys needed in Ethereum along with related information and allows
* for extensible forms of encryption.
*
* These utilities facilitate decrypting and encrypting the most common
* JSON Wallet formats.
*
* @_subsection: api/wallet:JSON Wallets [json-wallets]
*/
import { CTR } from "aes-js";
import { getAddress } from "../address/index.js";
import { keccak256, pbkdf2, randomBytes, scrypt, scryptSync } from "../crypto/index.js";
import { computeAddress } from "../transaction/index.js";
import {
concat, getBytes, hexlify, uuidV4, assert, assertArgument
} from "../utils/index.js";
import { getPassword, spelunk, zpad } from "./utils.js";
import type { ProgressCallback } from "../crypto/index.js";
import type { BytesLike } from "../utils/index.js";
import { version } from "../_version.js";
const defaultPath = "m/44'/60'/0'/0/0";
/**
* The contents of a JSON Keystore Wallet.
*/
export type KeystoreAccount = {
address: string;
privateKey: string;
mnemonic?: {
path?: string;
locale?: string;
entropy: string;
}
};
/**
* The parameters to use when encrypting a JSON Keystore Wallet.
*/
export type EncryptOptions = {
progressCallback?: ProgressCallback;
iv?: BytesLike;
entropy?: BytesLike;
client?: string;
salt?: BytesLike;
uuid?: string;
scrypt?: {
N?: number;
r?: number;
p?: number;
}
}
/**
* Returns true if %%json%% is a valid JSON Keystore Wallet.
*/
export function isKeystoreJson(json: string): boolean {
try {
const data = JSON.parse(json);
const version = ((data.version != null) ? parseInt(data.version): 0);
if (version === 3) { return true; }
} catch (error) { }
return false;
}
function decrypt(data: any, key: Uint8Array, ciphertext: Uint8Array): string {
const cipher = spelunk<string>(data, "crypto.cipher:string");
if (cipher === "aes-128-ctr") {
const iv = spelunk<Uint8Array>(data, "crypto.cipherparams.iv:data!")
const aesCtr = new CTR(key, iv);
return hexlify(aesCtr.decrypt(ciphertext));
}
assert(false, "unsupported cipher", "UNSUPPORTED_OPERATION", {
operation: "decrypt"
});
}
function getAccount(data: any, _key: string): KeystoreAccount {
const key = getBytes(_key);
const ciphertext = spelunk<Uint8Array>(data, "crypto.ciphertext:data!");
const computedMAC = hexlify(keccak256(concat([ key.slice(16, 32), ciphertext ]))).substring(2);
assertArgument(computedMAC === spelunk<string>(data, "crypto.mac:string!").toLowerCase(),
"incorrect password", "password", "[ REDACTED ]");
const privateKey = decrypt(data, key.slice(0, 16), ciphertext);
const address = computeAddress(privateKey);
if (data.address) {
let check = data.address.toLowerCase();
if (!check.startsWith("0x")) { check = "0x" + check; }
assertArgument(getAddress(check) === address, "keystore address/privateKey mismatch", "address", data.address);
}
const account: KeystoreAccount = { address, privateKey };
// Version 0.1 x-ethers metadata must contain an encrypted mnemonic phrase
const version = spelunk(data, "x-ethers.version:string");
if (version === "0.1") {
const mnemonicKey = key.slice(32, 64);
const mnemonicCiphertext = spelunk<Uint8Array>(data, "x-ethers.mnemonicCiphertext:data!");
const mnemonicIv = spelunk<Uint8Array>(data, "x-ethers.mnemonicCounter:data!");
const mnemonicAesCtr = new CTR(mnemonicKey, mnemonicIv);
account.mnemonic = {
path: (spelunk<null | string>(data, "x-ethers.path:string") || defaultPath),
locale: (spelunk<null | string>(data, "x-ethers.locale:string") || "en"),
entropy: hexlify(getBytes(mnemonicAesCtr.decrypt(mnemonicCiphertext)))
};
}
return account;
}
type ScryptParams = {
name: "scrypt";
salt: Uint8Array;
N: number;
r: number;
p: number;
dkLen: number;
};
type KdfParams = ScryptParams | {
name: "pbkdf2";
salt: Uint8Array;
count: number;
dkLen: number;
algorithm: "sha256" | "sha512";
};
function getDecryptKdfParams<T>(data: any): KdfParams {
const kdf = spelunk(data, "crypto.kdf:string");
if (kdf && typeof(kdf) === "string") {
if (kdf.toLowerCase() === "scrypt") {
const salt = spelunk<Uint8Array>(data, "crypto.kdfparams.salt:data!");
const N = spelunk<number>(data, "crypto.kdfparams.n:int!");
const r = spelunk<number>(data, "crypto.kdfparams.r:int!");
const p = spelunk<number>(data, "crypto.kdfparams.p:int!");
// Make sure N is a power of 2
assertArgument(N > 0 && (N & (N - 1)) === 0, "invalid kdf.N", "kdf.N", N);
assertArgument(r > 0 && p > 0, "invalid kdf", "kdf", kdf);
const dkLen = spelunk<number>(data, "crypto.kdfparams.dklen:int!");
assertArgument(dkLen === 32, "invalid kdf.dklen", "kdf.dflen", dkLen);
return { name: "scrypt", salt, N, r, p, dkLen: 64 };
} else if (kdf.toLowerCase() === "pbkdf2") {
const salt = spelunk<Uint8Array>(data, "crypto.kdfparams.salt:data!");
const prf = spelunk<string>(data, "crypto.kdfparams.prf:string!");
const algorithm = prf.split("-").pop();
assertArgument(algorithm === "sha256" || algorithm === "sha512", "invalid kdf.pdf", "kdf.pdf", prf);
const count = spelunk<number>(data, "crypto.kdfparams.c:int!");
const dkLen = spelunk<number>(data, "crypto.kdfparams.dklen:int!");
assertArgument(dkLen === 32, "invalid kdf.dklen", "kdf.dklen", dkLen);
return { name: "pbkdf2", salt, count, dkLen, algorithm };
}
}
assertArgument(false, "unsupported key-derivation function", "kdf", kdf);
}
/**
* Returns the account details for the JSON Keystore Wallet %%json%%
* using %%password%%.
*
* It is preferred to use the [async version](decryptKeystoreJson)
* instead, which allows a [[ProgressCallback]] to keep the user informed
* as to the decryption status.
*
* This method will block the event loop (freezing all UI) until decryption
* is complete, which can take quite some time, depending on the wallet
* paramters and platform.
*/
export function decryptKeystoreJsonSync(json: string, _password: string | Uint8Array): KeystoreAccount {
const data = JSON.parse(json);
const password = getPassword(_password);
const params = getDecryptKdfParams(data);
if (params.name === "pbkdf2") {
const { salt, count, dkLen, algorithm } = params;
const key = pbkdf2(password, salt, count, dkLen, algorithm);
return getAccount(data, key);
}
assert(params.name === "scrypt", "cannot be reached", "UNKNOWN_ERROR", { params })
const { salt, N, r, p, dkLen } = params;
const key = scryptSync(password, salt, N, r, p, dkLen);
return getAccount(data, key);
}
function stall(duration: number): Promise<void> {
return new Promise((resolve) => { setTimeout(() => { resolve(); }, duration); });
}
/**
* Resolves to the decrypted JSON Keystore Wallet %%json%% using the
* %%password%%.
*
* If provided, %%progress%% will be called periodically during the
* decrpytion to provide feedback, and if the function returns
* ``false`` will halt decryption.
*
* The %%progressCallback%% will **always** receive ``0`` before
* decryption begins and ``1`` when complete.
*/
export async function decryptKeystoreJson(json: string, _password: string | Uint8Array, progress?: ProgressCallback): Promise<KeystoreAccount> {
const data = JSON.parse(json);
const password = getPassword(_password);
const params = getDecryptKdfParams(data);
if (params.name === "pbkdf2") {
if (progress) {
progress(0);
await stall(0);
}
const { salt, count, dkLen, algorithm } = params;
const key = pbkdf2(password, salt, count, dkLen, algorithm);
if (progress) {
progress(1);
await stall(0);
}
return getAccount(data, key);
}
assert(params.name === "scrypt", "cannot be reached", "UNKNOWN_ERROR", { params })
const { salt, N, r, p, dkLen } = params;
const key = await scrypt(password, salt, N, r, p, dkLen, progress);
return getAccount(data, key);
}
function getEncryptKdfParams(options: EncryptOptions): ScryptParams {
// Check/generate the salt
const salt = (options.salt != null) ? getBytes(options.salt, "options.salt"): randomBytes(32);
// Override the scrypt password-based key derivation function parameters
let N = (1 << 17), r = 8, p = 1;
if (options.scrypt) {
if (options.scrypt.N) { N = options.scrypt.N; }
if (options.scrypt.r) { r = options.scrypt.r; }
if (options.scrypt.p) { p = options.scrypt.p; }
}
assertArgument(typeof(N) === "number" && N > 0 && Number.isSafeInteger(N) && (BigInt(N) & BigInt(N - 1)) === BigInt(0), "invalid scrypt N parameter", "options.N", N);
assertArgument(typeof(r) === "number" && r > 0 && Number.isSafeInteger(r), "invalid scrypt r parameter", "options.r", r);
assertArgument(typeof(p) === "number" && p > 0 && Number.isSafeInteger(p), "invalid scrypt p parameter", "options.p", p);
return { name: "scrypt", dkLen: 32, salt, N, r, p };
}
function _encryptKeystore(key: Uint8Array, kdf: ScryptParams, account: KeystoreAccount, options: EncryptOptions): any {
const privateKey = getBytes(account.privateKey, "privateKey");
// Override initialization vector
const iv = (options.iv != null) ? getBytes(options.iv, "options.iv"): randomBytes(16);
assertArgument(iv.length === 16, "invalid options.iv length", "options.iv", options.iv);
// Override the uuid
const uuidRandom = (options.uuid != null) ? getBytes(options.uuid, "options.uuid"): randomBytes(16);
assertArgument(uuidRandom.length === 16, "invalid options.uuid length", "options.uuid", options.iv);
// This will be used to encrypt the wallet (as per Web3 secret storage)
// - 32 bytes As normal for the Web3 secret storage (derivedKey, macPrefix)
// - 32 bytes AES key to encrypt mnemonic with (required here to be Ethers Wallet)
const derivedKey = key.slice(0, 16);
const macPrefix = key.slice(16, 32);
// Encrypt the private key
const aesCtr = new CTR(derivedKey, iv);
const ciphertext = getBytes(aesCtr.encrypt(privateKey));
// Compute the message authentication code, used to check the password
const mac = keccak256(concat([ macPrefix, ciphertext ]))
// See: https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition
const data: { [key: string]: any } = {
address: account.address.substring(2).toLowerCase(),
id: uuidV4(uuidRandom),
version: 3,
Crypto: {
cipher: "aes-128-ctr",
cipherparams: {
iv: hexlify(iv).substring(2),
},
ciphertext: hexlify(ciphertext).substring(2),
kdf: "scrypt",
kdfparams: {
salt: hexlify(kdf.salt).substring(2),
n: kdf.N,
dklen: 32,
p: kdf.p,
r: kdf.r
},
mac: mac.substring(2)
}
};
// If we have a mnemonic, encrypt it into the JSON wallet
if (account.mnemonic) {
const client = (options.client != null) ? options.client: `ethers/${ version }`;
const path = account.mnemonic.path || defaultPath;
const locale = account.mnemonic.locale || "en";
const mnemonicKey = key.slice(32, 64);
const entropy = getBytes(account.mnemonic.entropy, "account.mnemonic.entropy");
const mnemonicIv = randomBytes(16);
const mnemonicAesCtr = new CTR(mnemonicKey, mnemonicIv);
const mnemonicCiphertext = getBytes(mnemonicAesCtr.encrypt(entropy));
const now = new Date();
const timestamp = (now.getUTCFullYear() + "-" +
zpad(now.getUTCMonth() + 1, 2) + "-" +
zpad(now.getUTCDate(), 2) + "T" +
zpad(now.getUTCHours(), 2) + "-" +
zpad(now.getUTCMinutes(), 2) + "-" +
zpad(now.getUTCSeconds(), 2) + ".0Z");
const gethFilename = ("UTC--" + timestamp + "--" + data.address);
data["x-ethers"] = {
client, gethFilename, path, locale,
mnemonicCounter: hexlify(mnemonicIv).substring(2),
mnemonicCiphertext: hexlify(mnemonicCiphertext).substring(2),
version: "0.1"
};
}
return JSON.stringify(data);
}
/**
* Return the JSON Keystore Wallet for %%account%% encrypted with
* %%password%%.
*
* The %%options%% can be used to tune the password-based key
* derivation function parameters, explicitly set the random values
* used. Any provided [[ProgressCallback]] is ignord.
*/
export function encryptKeystoreJsonSync(account: KeystoreAccount, password: string | Uint8Array, options?: EncryptOptions): string {
if (options == null) { options = { }; }
const passwordBytes = getPassword(password);
const kdf = getEncryptKdfParams(options);
const key = scryptSync(passwordBytes, kdf.salt, kdf.N, kdf.r, kdf.p, 64);
return _encryptKeystore(getBytes(key), kdf, account, options);
}
/**
* Resolved to the JSON Keystore Wallet for %%account%% encrypted
* with %%password%%.
*
* The %%options%% can be used to tune the password-based key
* derivation function parameters, explicitly set the random values
* used and provide a [[ProgressCallback]] to receive periodic updates
* on the completion status..
*/
export async function encryptKeystoreJson(account: KeystoreAccount, password: string | Uint8Array, options?: EncryptOptions): Promise<string> {
if (options == null) { options = { }; }
const passwordBytes = getPassword(password);
const kdf = getEncryptKdfParams(options);
const key = await scrypt(passwordBytes, kdf.salt, kdf.N, kdf.r, kdf.p, 64, options.progressCallback);
return _encryptKeystore(getBytes(key), kdf, account, options);
}

203
dev/env/node_modules/ethers/src.ts/wallet/mnemonic.ts generated vendored Executable file
View File

@@ -0,0 +1,203 @@
import { pbkdf2, sha256 } from "../crypto/index.js";
import {
defineProperties, getBytes, hexlify, assertNormalize, assertPrivate, assertArgument, toUtf8Bytes
} from "../utils/index.js";
import { LangEn } from "../wordlists/lang-en.js";
import type { BytesLike } from "../utils/index.js";
import type { Wordlist } from "../wordlists/index.js";
// Returns a byte with the MSB bits set
function getUpperMask(bits: number): number {
return ((1 << bits) - 1) << (8 - bits) & 0xff;
}
// Returns a byte with the LSB bits set
function getLowerMask(bits: number): number {
return ((1 << bits) - 1) & 0xff;
}
function mnemonicToEntropy(mnemonic: string, wordlist?: null | Wordlist): string {
assertNormalize("NFKD");
if (wordlist == null) { wordlist = LangEn.wordlist(); }
const words = wordlist.split(mnemonic);
assertArgument((words.length % 3) === 0 && words.length >= 12 && words.length <= 24,
"invalid mnemonic length", "mnemonic", "[ REDACTED ]");
const entropy = new Uint8Array(Math.ceil(11 * words.length / 8));
let offset = 0;
for (let i = 0; i < words.length; i++) {
let index = wordlist.getWordIndex(words[i].normalize("NFKD"));
assertArgument(index >= 0, `invalid mnemonic word at index ${ i }`, "mnemonic", "[ REDACTED ]");
for (let bit = 0; bit < 11; bit++) {
if (index & (1 << (10 - bit))) {
entropy[offset >> 3] |= (1 << (7 - (offset % 8)));
}
offset++;
}
}
const entropyBits = 32 * words.length / 3;
const checksumBits = words.length / 3;
const checksumMask = getUpperMask(checksumBits);
const checksum = getBytes(sha256(entropy.slice(0, entropyBits / 8)))[0] & checksumMask;
assertArgument(checksum === (entropy[entropy.length - 1] & checksumMask),
"invalid mnemonic checksum", "mnemonic", "[ REDACTED ]");
return hexlify(entropy.slice(0, entropyBits / 8));
}
function entropyToMnemonic(entropy: Uint8Array, wordlist?: null | Wordlist): string {
assertArgument((entropy.length % 4) === 0 && entropy.length >= 16 && entropy.length <= 32,
"invalid entropy size", "entropy", "[ REDACTED ]");
if (wordlist == null) { wordlist = LangEn.wordlist(); }
const indices: Array<number> = [ 0 ];
let remainingBits = 11;
for (let i = 0; i < entropy.length; i++) {
// Consume the whole byte (with still more to go)
if (remainingBits > 8) {
indices[indices.length - 1] <<= 8;
indices[indices.length - 1] |= entropy[i];
remainingBits -= 8;
// This byte will complete an 11-bit index
} else {
indices[indices.length - 1] <<= remainingBits;
indices[indices.length - 1] |= entropy[i] >> (8 - remainingBits);
// Start the next word
indices.push(entropy[i] & getLowerMask(8 - remainingBits));
remainingBits += 3;
}
}
// Compute the checksum bits
const checksumBits = entropy.length / 4;
const checksum = parseInt(sha256(entropy).substring(2, 4), 16) & getUpperMask(checksumBits);
// Shift the checksum into the word indices
indices[indices.length - 1] <<= checksumBits;
indices[indices.length - 1] |= (checksum >> (8 - checksumBits));
return wordlist.join(indices.map((index) => (<Wordlist>wordlist).getWord(index)));
}
const _guard = { };
/**
* A **Mnemonic** wraps all properties required to compute [[link-bip-39]]
* seeds and convert between phrases and entropy.
*/
export class Mnemonic {
/**
* The mnemonic phrase of 12, 15, 18, 21 or 24 words.
*
* Use the [[wordlist]] ``split`` method to get the individual words.
*/
readonly phrase!: string;
/**
* The password used for this mnemonic. If no password is used this
* is the empty string (i.e. ``""``) as per the specification.
*/
readonly password!: string;
/**
* The wordlist for this mnemonic.
*/
readonly wordlist!: Wordlist;
/**
* The underlying entropy which the mnemonic encodes.
*/
readonly entropy!: string;
/**
* @private
*/
constructor(guard: any, entropy: string, phrase: string, password?: null | string, wordlist?: null | Wordlist) {
if (password == null) { password = ""; }
if (wordlist == null) { wordlist = LangEn.wordlist(); }
assertPrivate(guard, _guard, "Mnemonic");
defineProperties<Mnemonic>(this, { phrase, password, wordlist, entropy });
}
/**
* Returns the seed for the mnemonic.
*/
computeSeed(): string {
const salt = toUtf8Bytes("mnemonic" + this.password, "NFKD");
return pbkdf2(toUtf8Bytes(this.phrase, "NFKD"), salt, 2048, 64, "sha512");
}
/**
* Creates a new Mnemonic for the %%phrase%%.
*
* The default %%password%% is the empty string and the default
* wordlist is the [English wordlists](LangEn).
*/
static fromPhrase(phrase: string, password?: null | string, wordlist?: null | Wordlist): Mnemonic {
// Normalize the case and space; throws if invalid
const entropy = mnemonicToEntropy(phrase, wordlist);
phrase = entropyToMnemonic(getBytes(entropy), wordlist);
return new Mnemonic(_guard, entropy, phrase, password, wordlist);
}
/**
* Create a new **Mnemonic** from the %%entropy%%.
*
* The default %%password%% is the empty string and the default
* wordlist is the [English wordlists](LangEn).
*/
static fromEntropy(_entropy: BytesLike, password?: null | string, wordlist?: null | Wordlist): Mnemonic {
const entropy = getBytes(_entropy, "entropy");
const phrase = entropyToMnemonic(entropy, wordlist);
return new Mnemonic(_guard, hexlify(entropy), phrase, password, wordlist);
}
/**
* Returns the phrase for %%mnemonic%%.
*/
static entropyToPhrase(_entropy: BytesLike, wordlist?: null | Wordlist): string {
const entropy = getBytes(_entropy, "entropy");
return entropyToMnemonic(entropy, wordlist);
}
/**
* Returns the entropy for %%phrase%%.
*/
static phraseToEntropy(phrase: string, wordlist?: null | Wordlist): string {
return mnemonicToEntropy(phrase, wordlist);
}
/**
* Returns true if %%phrase%% is a valid [[link-bip-39]] phrase.
*
* This checks all the provided words belong to the %%wordlist%%,
* that the length is valid and the checksum is correct.
*/
static isValidMnemonic(phrase: string, wordlist?: null | Wordlist): boolean {
try {
mnemonicToEntropy(phrase, wordlist);
return true;
} catch (error) { }
return false;
}
}

147
dev/env/node_modules/ethers/src.ts/wallet/utils.ts generated vendored Executable file
View File

@@ -0,0 +1,147 @@
/**
* @_ignore
*/
import {
getBytesCopy, assertArgument, toUtf8Bytes
} from "../utils/index.js";
export function looseArrayify(hexString: string): Uint8Array {
if (typeof(hexString) === "string" && !hexString.startsWith("0x")) {
hexString = "0x" + hexString;
}
return getBytesCopy(hexString);
}
export function zpad(value: String | number, length: number): String {
value = String(value);
while (value.length < length) { value = '0' + value; }
return value;
}
export function getPassword(password: string | Uint8Array): Uint8Array {
if (typeof(password) === 'string') {
return toUtf8Bytes(password, "NFKC");
}
return getBytesCopy(password);
}
export function spelunk<T>(object: any, _path: string): T {
const match = _path.match(/^([a-z0-9$_.-]*)(:([a-z]+))?(!)?$/i);
assertArgument(match != null, "invalid path", "path", _path);
const path = match[1];
const type = match[3];
const reqd = (match[4] === "!");
let cur = object;
for (const comp of path.toLowerCase().split('.')) {
// Search for a child object with a case-insensitive matching key
if (Array.isArray(cur)) {
if (!comp.match(/^[0-9]+$/)) { break; }
cur = cur[parseInt(comp)];
} else if (typeof(cur) === "object") {
let found: any = null;
for (const key in cur) {
if (key.toLowerCase() === comp) {
found = cur[key];
break;
}
}
cur = found;
} else {
cur = null;
}
if (cur == null) { break; }
}
assertArgument(!reqd || cur != null, "missing required value", "path", path);
if (type && cur != null) {
if (type === "int") {
if (typeof(cur) === "string" && cur.match(/^-?[0-9]+$/)) {
return <T><unknown>parseInt(cur);
} else if (Number.isSafeInteger(cur)) {
return cur;
}
}
if (type === "number") {
if (typeof(cur) === "string" && cur.match(/^-?[0-9.]*$/)) {
return <T><unknown>parseFloat(cur);
}
}
if (type === "data") {
if (typeof(cur) === "string") { return <T><unknown>looseArrayify(cur); }
}
if (type === "array" && Array.isArray(cur)) { return <T><unknown>cur; }
if (type === typeof(cur)) { return cur; }
assertArgument(false, `wrong type found for ${ type } `, "path", path);
}
return cur;
}
/*
export function follow(object: any, path: string): null | string {
let currentChild = object;
for (const comp of path.toLowerCase().split('/')) {
// Search for a child object with a case-insensitive matching key
let matchingChild = null;
for (const key in currentChild) {
if (key.toLowerCase() === comp) {
matchingChild = currentChild[key];
break;
}
}
if (matchingChild === null) { return null; }
currentChild = matchingChild;
}
return currentChild;
}
// "path/to/something:type!"
export function followRequired(data: any, path: string): string {
const value = follow(data, path);
if (value != null) { return value; }
return logger.throwArgumentError("invalid value", `data:${ path }`,
JSON.stringify(data));
}
*/
// See: https://www.ietf.org/rfc/rfc4122.txt (Section 4.4)
/*
export function uuidV4(randomBytes: BytesLike): string {
const bytes = getBytes(randomBytes, "randomBytes");
// Section: 4.1.3:
// - time_hi_and_version[12:16] = 0b0100
bytes[6] = (bytes[6] & 0x0f) | 0x40;
// Section 4.4
// - clock_seq_hi_and_reserved[6] = 0b0
// - clock_seq_hi_and_reserved[7] = 0b1
bytes[8] = (bytes[8] & 0x3f) | 0x80;
const value = hexlify(bytes);
return [
value.substring(2, 10),
value.substring(10, 14),
value.substring(14, 18),
value.substring(18, 22),
value.substring(22, 34),
].join("-");
}
*/

163
dev/env/node_modules/ethers/src.ts/wallet/wallet.ts generated vendored Executable file
View File

@@ -0,0 +1,163 @@
import { SigningKey } from "../crypto/index.js";
import { assertArgument } from "../utils/index.js";
import { BaseWallet } from "./base-wallet.js";
import { HDNodeWallet } from "./hdwallet.js";
import { decryptCrowdsaleJson, isCrowdsaleJson } from "./json-crowdsale.js";
import {
decryptKeystoreJson, decryptKeystoreJsonSync,
encryptKeystoreJson, encryptKeystoreJsonSync,
isKeystoreJson
} from "./json-keystore.js";
import { Mnemonic } from "./mnemonic.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<void> {
return new Promise((resolve) => { setTimeout(() => { resolve(); }, duration); });
}
/**
* A **Wallet** manages a single private key which is used to sign
* transactions, messages and other common payloads.
*
* This class is generally the main entry point for developers
* that wish to use a private key directly, as it can create
* instances from a large variety of common sources, including
* raw private key, [[link-bip-39]] mnemonics and encrypte JSON
* wallets.
*/
export class Wallet extends BaseWallet {
/**
* Create a new wallet for the private %%key%%, optionally connected
* to %%provider%%.
*/
constructor(key: string | SigningKey, provider?: null | Provider) {
if (typeof(key) === "string" && !key.startsWith("0x")) {
key = "0x" + key;
}
let signingKey = (typeof(key) === "string") ? new SigningKey(key): key;
super(signingKey, provider);
}
connect(provider: null | Provider): Wallet {
return new Wallet(this.signingKey, provider);
}
/**
* 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.
*/
async encrypt(password: Uint8Array | string, progressCallback?: ProgressCallback): Promise<string> {
const account = { address: this.address, privateKey: this.privateKey };
return await encryptKeystoreJson(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 [[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.
*/
encryptSync(password: Uint8Array | string): string {
const account = { address: this.address, privateKey: this.privateKey };
return encryptKeystoreJsonSync(account, password);
}
static #fromAccount(account: null | CrowdsaleAccount | KeystoreAccount): HDNodeWallet | 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 = HDNodeWallet.fromMnemonic(mnemonic, account.mnemonic.path);
if (wallet.address === account.address && wallet.privateKey === account.privateKey) {
return wallet;
}
console.log("WARNING: JSON mismatch address/privateKey != mnemonic; fallback onto private key");
}
const wallet = new Wallet(account.privateKey);
assertArgument(wallet.address === account.address,
"address/privateKey mismatch", "json", "[ REDACTED ]");
return wallet;
}
/**
* Creates (asynchronously) a **Wallet** by decrypting the %%json%%
* with %%password%%.
*
* If %%progress%% is provided, it is called periodically during
* decryption so that any UI can be updated.
*/
static async fromEncryptedJson(json: string, password: Uint8Array | string, progress?: ProgressCallback): Promise<HDNodeWallet | Wallet> {
let account: null | CrowdsaleAccount | KeystoreAccount = null;
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);
}
/**
* Creates a **Wallet** by decrypting the %%json%% with %%password%%.
*
* The [[fromEncryptedJson]] method is preferred, as this method
* will lock up and freeze the UI during decryption, which may take
* some time.
*/
static fromEncryptedJsonSync(json: string, password: Uint8Array | string): HDNodeWallet | Wallet {
let account: null | CrowdsaleAccount | KeystoreAccount = null;
if (isKeystoreJson(json)) {
account = decryptKeystoreJsonSync(json, password);
} else if (isCrowdsaleJson(json)) {
account = decryptCrowdsaleJson(json, password);
} else {
assertArgument(false, "invalid JSON wallet", "json", "[ REDACTED ]");
}
return Wallet.#fromAccount(account);
}
/**
* Creates a new random [[HDNodeWallet]] using the available
* [cryptographic random source](randomBytes).
*
* If there is no crytographic random source, this will throw.
*/
static createRandom(provider?: null | Provider): HDNodeWallet {
const wallet = HDNodeWallet.createRandom();
if (provider) { return wallet.connect(provider); }
return wallet;
}
/**
* Creates a [[HDNodeWallet]] for %%phrase%%.
*/
static fromPhrase(phrase: string, provider?: Provider): HDNodeWallet {
const wallet = HDNodeWallet.fromPhrase(phrase);
if (provider) { return wallet.connect(provider); }
return wallet;
}
}