diff --git a/packages/docs/src/.vitepress/config.ts b/packages/docs/src/.vitepress/config.ts
index ee86dd8..b090876 100644
--- a/packages/docs/src/.vitepress/config.ts
+++ b/packages/docs/src/.vitepress/config.ts
@@ -129,7 +129,6 @@ export default defineConfig({
link: '/tools/primitives#block-header',
},
{ text: 'Bytes', link: '/tools/primitives#bytes' },
- { text: 'Hasher', link: '/tools/primitives#hasher' },
{
text: 'HD Private Node',
link: '/tools/primitives#hd-private-node',
@@ -164,18 +163,35 @@ export default defineConfig({
},
],
},
- /*
{
text: 'Stack',
collapsed: false,
items: [
+ /*
{
text: 'Blockchain',
- items: [{ text: 'Blockchain Electrum' }],
+ link: '/tools/stack/blockchain',
+ items: [
+ {
+ text: 'Electrum',
+ link: '/tools/stack/blockchain#encryption',
+ },
+ ],
+ },
+ */
+ {
+ text: 'Utils',
+ link: '/tools/stack/utils',
+ items: [
+ {
+ text: 'Encrypter',
+ link: '/tools/stack/utils#encrypter',
+ },
+ { text: 'Hasher', link: '/tools/stack/utils#hasher' },
+ ],
},
],
},
- */
],
},
],
diff --git a/packages/docs/src/.vitepress/theme/custom.css b/packages/docs/src/.vitepress/theme/custom.css
index fe51e07..c659d16 100644
--- a/packages/docs/src/.vitepress/theme/custom.css
+++ b/packages/docs/src/.vitepress/theme/custom.css
@@ -30,6 +30,10 @@ body {
.vp-doc h4 {
font-size: 14px;
opacity: 0.75;
+ color: var(--primary);
+ padding-top: 3em;
+ border-top: 1px solid var(--vp-c-text-1);
+ margin-top: 3em;
}
.VPSidebarItem .text {
diff --git a/packages/docs/src/tools/primitive-functions.ts b/packages/docs/src/tools/primitive-functions.ts
index 4941d39..5d5ee0b 100644
--- a/packages/docs/src/tools/primitive-functions.ts
+++ b/packages/docs/src/tools/primitive-functions.ts
@@ -4,7 +4,8 @@ import {
Bytes,
encodeExtendedJson,
decodeExtendedJson,
- Hasher,
+ // Encrypter,
+ // Hasher,
HDPrivateNode,
HDPublicNode,
Mnemonic,
@@ -116,10 +117,10 @@ export const address = {
export const blockHeader = {
from: [
{
- label: 'From Hex',
- inputs: ['UTF-8'],
+ label: 'From',
+ inputs: ['Hex'],
models: [''],
- callback: (args: [string]) => BlockHeader.fromHex(...args),
+ callback: (args: [string]) => BlockHeader.from(...args),
},
],
to: [
@@ -193,67 +194,30 @@ export const bytes = {
],
};
-export const hasher = {
- from: [
- {
- label: 'From Hex',
- inputs: ['Hex'],
- models: [''],
- callback: (args: [string]) => Hasher.fromHex(...args),
- },
- {
- label: 'From Base64',
- inputs: ['Base64'],
- models: [''],
- callback: (args: [string]) => Hasher.fromBase64(...args),
- },
- {
- label: 'From UTF-8',
- inputs: ['UTF-8'],
- models: [''],
- callback: (args: [string]) => Hasher.fromUTF8(...args),
- },
- ],
- to: [
- {
- label: 'To Sha256 Hex',
- callback: (hasher: Hasher) => hasher.toSHA256Hex(),
- },
- {
- label: 'To RipeMD160 Hex',
- callback: (hasher: Hasher) => hasher.toRIPEMD160Hex(),
- },
- {
- label: 'To Hash160 Hex',
- callback: (hasher: Hasher) => hasher.toHash160Hex(),
- },
- {
- label: 'To Hash256 Hex',
- callback: (hasher: Hasher) => hasher.toHash256Hex(),
- },
- ],
-};
-
export const hdPrivateNode = {
from: [
{
label: 'Generate Random',
- inputs: ['Derivation Path'],
- models: ["m/44'/145'/0'/0/1"],
callback: () => HDPrivateNode.generateRandom(),
},
{
- label: 'From Mnemonic',
- inputs: ['Mnemonic', 'Derivation Path'],
- models: ['', "m/44'/145'/0'/0/1"],
+ label: 'From Mnemonic Phrase',
+ inputs: ['Mnemonic'],
+ models: [''],
callback: (args: [string]) => HDPrivateNode.fromMnemonic(...args),
},
{
label: 'From XPriv Key',
- inputs: ['XPriv Key', 'Derivation Path'],
- models: ['', "m/44'/145'/0'/0/1"],
+ inputs: ['XPriv Key'],
+ models: [''],
callback: (args: [string]) => HDPrivateNode.fromXPriv(...args),
},
+ {
+ label: 'From Seed',
+ inputs: ['Seed (Hex)'],
+ models: [''],
+ callback: (args: [string]) => HDPrivateNode.fromSeed(...args),
+ },
{
label: 'From Raw',
inputs: ['Raw (LibAuth Format/ExtJSON Encoded)'],
@@ -283,6 +247,18 @@ export const hdPrivateNode = {
hdPrivateNode.deriveHDPublicNode().toXPub(),
},
],
+ methods: [
+ {
+ label: 'Derive Path',
+ inputs: ['BIP32 Path'],
+ models: ["m/44'/145'/0'/0/0"],
+ callback: (hdPrivateNode: HDPrivateNode, args: [string]) =>
+ hdPrivateNode
+ .derivePath(...args)
+ .toPrivateKey()
+ .toWif(),
+ },
+ ],
};
export const hdPublicNode = {
@@ -317,30 +293,50 @@ export const hdPublicNode = {
encodeExtendedJson(hdPublicNode.toRaw(), 2),
},
],
+ methods: [
+ {
+ label: 'Derive Path',
+ inputs: ['BIP32 Public Key Path'],
+ models: ['M/0/0'],
+ callback: (hdPublicNode: HDPublicNode, args: [string]) =>
+ hdPublicNode
+ .derivePath(...args)
+ .toPublicKey()
+ .deriveAddress()
+ .toCashAddr(),
+ },
+ ],
};
export const mnemonic = {
from: [
{
label: 'Generate Random',
- callback: () => Mnemonic.generateRandom(),
+ inputs: ['Passphrase (Optional)'],
+ models: [undefined],
+ callback: (args: [string?]) => Mnemonic.generateRandom(...args),
},
{
- label: 'From String',
- inputs: ['String'],
+ label: 'From Phrase',
+ inputs: ['Mnemonic Phrase'],
models: [''],
- callback: (args: [string]) => Mnemonic.fromString(...args),
+ callback: (args: [string]) => Mnemonic.fromPhrase(...args),
extendedInput: true,
},
],
to: [
{
- label: 'To String (without passphrase)',
- callback: (mnemonic: Mnemonic) => mnemonic.toString(false),
+ label: 'To Seed',
+ callback: (mnemonic: Mnemonic) =>
+ Bytes.fromBytes(mnemonic.toSeed()).toHex(),
},
{
- label: 'To String (with passphrase)',
- callback: (mnemonic: Mnemonic) => mnemonic.toString(true),
+ label: 'To Phrase (without passphrase)',
+ callback: (mnemonic: Mnemonic) => mnemonic.toPhrase(false),
+ },
+ {
+ label: 'To Phrase (with passphrase)',
+ callback: (mnemonic: Mnemonic) => mnemonic.toPhrase(true),
},
{
label: 'Words',
@@ -361,16 +357,10 @@ export const privateKey = {
callback: () => PrivateKey.generateRandom(),
},
{
- label: 'From Hex',
- inputs: ['Hex'],
+ label: 'From Hex or WIF',
+ inputs: ['Hex or WIF'],
models: [''],
- callback: (args: [string]) => PrivateKey.fromHex(...args),
- },
- {
- label: 'From WIF',
- inputs: ['WIF'],
- models: [''],
- callback: (args: [string]) => PrivateKey.fromWIF(...args),
+ callback: (args: [string]) => PrivateKey.from(...args),
},
],
to: [
diff --git a/packages/docs/src/tools/primitives.md b/packages/docs/src/tools/primitives.md
index 23f27de..f7746f1 100644
--- a/packages/docs/src/tools/primitives.md
+++ b/packages/docs/src/tools/primitives.md
@@ -6,7 +6,6 @@ import {
address,
blockHeader,
bytes,
- hasher,
hdPrivateNode,
hdPublicNode,
mnemonic,
@@ -58,18 +57,6 @@ import {
/>
-## Hasher
-
-
-
## HD Private Node
@@ -90,6 +78,7 @@ import {
diff --git a/packages/docs/src/tools/stack/utils-functions.ts b/packages/docs/src/tools/stack/utils-functions.ts
index d6f3a5f..94419a2 100644
--- a/packages/docs/src/tools/stack/utils-functions.ts
+++ b/packages/docs/src/tools/stack/utils-functions.ts
@@ -1,14 +1,13 @@
import { Bytes } from '@xocash/primitives';
+import { Encrypter, Hasher } from '@xocash/stack';
-import { Encryption } from '@xocash/stack';
-
-export const encryption = {
+export const encrypter = {
from: [
{
label: 'From Passphrase',
inputs: ['Passphrase'],
models: [''],
- callback: (args: [string]) => Encryption.fromPassphrase(...args),
+ callback: (args: [string]) => Encrypter.fromPassphrase(...args),
extendedInput: true,
},
],
@@ -18,17 +17,58 @@ export const encryption = {
label: 'Encrypt',
inputs: ['UTF8'],
models: ['Your text here'],
- callback: async (encryption: Encryption, args: [string]) =>
- Bytes.fromBytes(await encryption.encrypt(...args)).toHex(),
+ callback: async (encrypter: Encrypter, args: [string]) =>
+ Bytes.fromBytes(await encrypter.encrypt(...args)).toHex(),
},
{
label: 'Decrypt',
inputs: ['Hex'],
models: ['Encrypted Data (hex)'],
- callback: async (encryption: Encryption, args: [string]) =>
+ callback: async (encrypter: Encrypter, args: [string]) =>
Bytes.fromBytes(
- await encryption.decrypt(Bytes.fromHex(...args).toBytes()),
+ await encrypter.decrypt(Bytes.fromHex(...args).toBytes()),
).toUtf8(),
},
],
};
+
+export const hasher = {
+ from: [
+ {
+ label: 'From Hex',
+ inputs: ['Hex'],
+ models: [''],
+ callback: (args: [string]) => Hasher.fromHex(...args),
+ },
+ {
+ label: 'From Base64',
+ inputs: ['Base64'],
+ models: [''],
+ callback: (args: [string]) => Hasher.fromBase64(...args),
+ },
+ {
+ label: 'From UTF-8',
+ inputs: ['UTF-8'],
+ models: [''],
+ callback: (args: [string]) => Hasher.fromUTF8(...args),
+ },
+ ],
+ to: [
+ {
+ label: 'To Sha256 Hex',
+ callback: (hasher: Hasher) => hasher.toSHA256Hex(),
+ },
+ {
+ label: 'To RipeMD160 Hex',
+ callback: (hasher: Hasher) => hasher.toRIPEMD160Hex(),
+ },
+ {
+ label: 'To Hash160 Hex',
+ callback: (hasher: Hasher) => hasher.toHash160Hex(),
+ },
+ {
+ label: 'To Hash256 Hex',
+ callback: (hasher: Hasher) => hasher.toHash256Hex(),
+ },
+ ],
+};
diff --git a/packages/docs/src/tools/stack/utils.md b/packages/docs/src/tools/stack/utils.md
index d3777f7..71af8b7 100644
--- a/packages/docs/src/tools/stack/utils.md
+++ b/packages/docs/src/tools/stack/utils.md
@@ -3,7 +3,8 @@ import PrimitiveToolVue, { type FromFunction, type ToFunction } from '@theme/com
// Import our tool functions
import {
- encryption
+ encrypter,
+ hasher,
} from './utils-functions.ts'
@@ -11,15 +12,27 @@ import {
# Utils
-## Encryption
+## Encrypter
+
+## Hasher
+
+
diff --git a/packages/primitives/README.md b/packages/primitives/README.md
index f959a5e..c816fc5 100644
--- a/packages/primitives/README.md
+++ b/packages/primitives/README.md
@@ -2,4 +2,4 @@
**NOTE: This package is EXTREMELY UNSTABLE and WILL BREAK frequently. DO NOT USE.**
-A collection of useful BCH Primitives.
+A collection of immutable primitives for use with BCH.
diff --git a/packages/primitives/src/address.ts b/packages/primitives/src/address.ts
index 644e2df..7b573a2 100644
--- a/packages/primitives/src/address.ts
+++ b/packages/primitives/src/address.ts
@@ -18,13 +18,20 @@ import type { KnownAddressTypeContents } from '@bitauth/libauth';
export type AddressType = 'P2PK' | 'P2PKH' | 'P2SH20' | 'P2SH32';
+export const DEFAULT_ADDRESS_PREFIXES = [
+ '',
+ 'bitcoincash',
+ 'bchtest',
+ 'bchreg',
+];
+
/**
* Address Entity.
*/
export class Address {
public readonly addressContents: KnownAddressTypeContents;
- constructor(addressContents: KnownAddressTypeContents) {
+ protected constructor(addressContents: KnownAddressTypeContents) {
this.addressContents = addressContents;
}
@@ -37,13 +44,16 @@ export class Address {
*
* @returns {Address} Instance of Address.
*/
- public static from(address: string): Address {
+ public static from(
+ address: string,
+ tryPrefixes = DEFAULT_ADDRESS_PREFIXES,
+ ): Address {
// Attempt to decode as a Cash Address.
try {
- return Address.fromCashAddr(address);
+ return this.fromCashAddr(address, tryPrefixes);
} catch (_error) {
// And if that fails, attempt to decode as a Base58 Address.
- return Address.fromLegacy(address);
+ return this.fromLegacy(address);
}
}
@@ -61,7 +71,7 @@ export class Address {
*/
public static fromCashAddr(
cashAddr: string,
- tryPrefixes: Array = ['', 'bitcoincash', 'bchtest', 'bchreg'],
+ tryPrefixes: Array = DEFAULT_ADDRESS_PREFIXES,
): Address {
// Try all prefixes (including empty string) until we find a valid decode
const decodeResult = tryPrefixes
@@ -158,7 +168,7 @@ export class Address {
}
// Return a new instance of address.
- return new Address({ payload, type });
+ return this.fromRaw({ payload, type });
}
/**
@@ -181,7 +191,7 @@ export class Address {
const contractBytecode = hexToBin(hex);
// Return a new instance of address.
- return Address.fromRedeemScript(contractBytecode, type);
+ return this.fromRedeemScript(contractBytecode, type);
}
/**
@@ -201,7 +211,7 @@ export class Address {
throw new Error(`Invalid type (${addressContents.type})`);
}
- return new Address(addressContents);
+ return this.fromRaw(addressContents);
}
/**
@@ -220,7 +230,7 @@ export class Address {
const lockScriptBytes = hexToBin(hex);
- return Address.fromLockscriptBytes(lockScriptBytes);
+ return this.fromLockscriptBytes(lockScriptBytes);
}
/**
@@ -244,7 +254,7 @@ export class Address {
);
}
- return new Address({ payload: hash160, type });
+ return this.fromRaw({ payload: hash160, type });
}
/**
@@ -267,16 +277,34 @@ export class Address {
const hash160Bytes = hexToBin(hash160Hex);
- return Address.fromHash160(hash160Bytes, type);
+ return this.fromHash160(hash160Bytes, type);
}
+ /**
+ * Creates an Address instance from a raw object representation (LibAuth's KnownAddressTypeContents).
+ *
+ * @remarks This method is UNSAFE and MUST only be used where input is already verified or trusted.
+ *
+ * @param obj - LibAuth Object of type KnownAddressTypeContents
+ * @returns A new Address instance
+ */
public static fromRaw(addressContents: KnownAddressTypeContents) {
return new this(addressContents);
}
- public static isValid(address: string): boolean {
+ /**
+ * Checks whether the given address is a valid Legacy or CashAddr.
+ *
+ * @param address The address.
+ *
+ * @returns boolean indicating whether the address is a valid Legacy Address or CashAddr.
+ */
+ public static isValid(
+ address: string,
+ tryPrefixes = DEFAULT_ADDRESS_PREFIXES,
+ ): boolean {
try {
- Address.from(address);
+ Address.from(address, tryPrefixes);
} catch (_error) {
return false;
}
@@ -284,6 +312,13 @@ export class Address {
return true;
}
+ /**
+ * Checks whether the given address is a valid Legacy Address.
+ *
+ * @param address The address.
+ *
+ * @returns boolean indicating whether the address is a valid Legacy Address.
+ */
public static isValidLegacy(address: string): boolean {
try {
Address.fromLegacy(address);
@@ -294,9 +329,19 @@ export class Address {
return true;
}
- public static isValidCashAddr(address: string): boolean {
+ /**
+ * Checks whether the given address is a valid CashAddr.
+ *
+ * @param address The address.
+ *
+ * @returns boolean indicating whether the address is a valid CashAddr.
+ */
+ public static isValidCashAddr(
+ address: string,
+ tryPrefixes = DEFAULT_ADDRESS_PREFIXES,
+ ): boolean {
try {
- Address.fromCashAddr(address);
+ Address.fromCashAddr(address, tryPrefixes);
} catch (_error) {
return false;
}
@@ -467,6 +512,11 @@ export class Address {
return lockscriptAsm;
}
+ /**
+ * Converts the address to its raw LibAuth representation (KnownAddressTypeContents).
+ *
+ * @returns A LibAuth KnownAddressTypeContents object.
+ */
public toRaw() {
return { ...this.addressContents };
}
diff --git a/packages/primitives/src/block-header.ts b/packages/primitives/src/block-header.ts
index 7558995..582174a 100644
--- a/packages/primitives/src/block-header.ts
+++ b/packages/primitives/src/block-header.ts
@@ -24,7 +24,7 @@ export class BlockHeader {
* @param bits - Compact representation of the target threshold
* @param nonce - Value modified during mining to make the block hash valid
*/
- constructor(
+ protected constructor(
private readonly version: number,
private readonly previousBlockHash: Uint8Array,
private readonly merkleRoot: Uint8Array,
@@ -33,6 +33,24 @@ export class BlockHeader {
private readonly nonce: number,
) {}
+ /**
+ * Instantiates a blockheader from either:
+ *
+ * 1. Transaction bytecode
+ * 2. Transaction bytecode (in hexadecimal format)
+ *
+ * @param blockHeader The block header in one of the above formats.
+ *
+ * @throws {Error} If the block header cannot be decoded.
+ *
+ * @returns The created BlockHeader Primitive.
+ */
+ public static from(blockHeader: Uint8Array | string) {
+ return blockHeader instanceof Uint8Array
+ ? BlockHeader.fromBytes(blockHeader)
+ : BlockHeader.fromHex(blockHeader);
+ }
+
/**
* Creates a BlockHeader instance from a byte array.
* @param bytes - 80-byte array containing the serialized block header
@@ -72,6 +90,9 @@ export class BlockHeader {
/**
* Creates a BlockHeader instance from a raw object representation.
+ *
+ * @remarks This method is UNSAFE and MUST only be used where input is already verified or trusted.
+ *
* @param obj - Object containing the block header fields
* @returns A new BlockHeader instance
*/
@@ -86,6 +107,23 @@ export class BlockHeader {
);
}
+ /**
+ * Checks whether the given BlochHeader is of a valid structure.
+ *
+ * @param blockHeader The blockheader as hex or a Uint8Array.
+ *
+ * @returns boolean indicating whether the blockheader is of a valid structure.
+ */
+ public static isValid(blockHeader: Uint8Array | string): boolean {
+ try {
+ BlockHeader.from(blockHeader);
+ } catch (_error) {
+ return false;
+ }
+
+ return true;
+ }
+
/**
* Serializes the block header to a byte array.
* @returns 80-byte array containing the serialized block header
@@ -106,7 +144,17 @@ export class BlockHeader {
}
/**
- * Converts the block header to a raw object representation.
+ * Converts the block header to a hex string.
+ *
+ * @returns 160-character hex string representing the block header
+ */
+ toHex(): string {
+ return binToHex(this.toBytes());
+ }
+
+ /**
+ * Converts the block header to a serializable raw object representation.
+ *
* @returns Object containing the block header fields
*/
toRaw(): BlockHeaderRaw {
@@ -120,14 +168,6 @@ export class BlockHeader {
};
}
- /**
- * Converts the block header to a hex string.
- * @returns 160-character hex string representing the block header
- */
- toHex(): string {
- return binToHex(this.toBytes());
- }
-
// Getters
getVersion(): number {
return this.version;
diff --git a/packages/primitives/src/bytes.ts b/packages/primitives/src/bytes.ts
index 3f859ac..2366a65 100644
--- a/packages/primitives/src/bytes.ts
+++ b/packages/primitives/src/bytes.ts
@@ -23,7 +23,7 @@ export class Bytes {
*
* @param bytes {Uint8Array} The bytes to use.
*/
- constructor(bytes: Uint8Array) {
+ protected constructor(bytes: Uint8Array) {
this.bytes = bytes;
}
diff --git a/packages/primitives/src/ext-json.ts b/packages/primitives/src/ext-json.ts
index 4775982..7170460 100644
--- a/packages/primitives/src/ext-json.ts
+++ b/packages/primitives/src/ext-json.ts
@@ -1,6 +1,8 @@
-// TODO: These are intended as temporary stand-ins until this functionality has been implemented directly in LibAuth.
-// We are doing this so that we may better standardize with the rest of the BCH eco-system in future.
-// See: https://github.com/bitauth/libauth/pull/108
+/**
+ * TODO: These are intended as temporary stand-ins until this functionality has been implemented directly in LibAuth.
+ * We are doing this so that we may better standardize with the rest of the BCH eco-system in future.
+ * See: https://github.com/bitauth/libauth/pull/108
+ */
import { binToHex, hexToBin } from '@bitauth/libauth';
diff --git a/packages/primitives/src/hd-private-node.ts b/packages/primitives/src/hd-private-node.ts
index 060eb19..e806ed3 100644
--- a/packages/primitives/src/hd-private-node.ts
+++ b/packages/primitives/src/hd-private-node.ts
@@ -1,4 +1,6 @@
+import { Bytes } from './bytes.js';
import { HDPublicNode } from './hd-public-node.js';
+import { Mnemonic } from './mnemonic.js';
import { PrivateKey } from './private-key.js';
import {
@@ -8,7 +10,6 @@ import {
deriveHdPathRelative,
deriveHdPublicNode,
deriveHdPrivateNodeFromSeed,
- deriveSeedFromBip39Mnemonic,
generateRandomBytes,
} from '@bitauth/libauth';
@@ -30,7 +31,7 @@ export class HDPrivateNode {
*
* @param node The HD Private Node.
*/
- constructor(node: HdPrivateNodeValid) {
+ protected constructor(node: HdPrivateNodeValid) {
this.node = node;
}
@@ -43,27 +44,18 @@ export class HDPrivateNode {
*
* @returns The created HD Private Node.
*/
- public static fromBytes(seed: Uint8Array): HDPrivateNode {
- // Return a new HD Private Node from the given seed.
- return this.fromSeed(seed);
- }
+ public static fromSeed(seed: Uint8Array | string): HDPrivateNode {
+ // Cast from hex if a string was provided.
+ if (typeof seed === 'string') {
+ seed = Bytes.fromHex(seed).toBytes();
+ }
- /**
- * Creates a HD Private Node from a raw seed.
- *
- * @param seed The raw seed bytes.
- *
- * @throws {Error} If HD Private Node cannot be created.
- *
- * @returns The created HD Private Node.
- */
- public static fromSeed(seed: Uint8Array): HDPrivateNode {
- // If the seed is not 32 bytes, throw an error.
- /*if (seed.length !== 32) {
+ // If the seed is not 64 bytes, throw an error.
+ if (seed.length !== 64) {
throw new Error(
- `Failed to derive Private Node from seed: Seed must be 32 bytes (${seed.length} given)`
+ `Failed to derive Private Node from seed: Seed must be 64 bytes (${seed.length} given)`,
);
- }*/
+ }
// Attempt to derive HD Private Node from the given seed.
const deriveResult = deriveHdPrivateNodeFromSeed(seed);
@@ -80,15 +72,15 @@ export class HDPrivateNode {
/**
* Creates a HD Private Node from the given mnemonic.
*
- * @param mnemonic The mnemonic.
+ * @param phrase The mnemonic phrase as a string or array of words.
*
* @throws {Error} If HD Private Node cannot be created.
*
* @returns The created HD Private Node.
*/
- public static fromMnemonic(mnemonic: string): HDPrivateNode {
+ public static fromMnemonic(phrase: string | Array): HDPrivateNode {
// Derive the seed from the mnemonic.
- const seed = deriveSeedFromBip39Mnemonic(mnemonic);
+ const seed = Mnemonic.from(phrase).toSeed();
// Return a new HDPrivateNode from the given seed.
return this.fromSeed(seed);
@@ -163,14 +155,74 @@ export class HDPrivateNode {
}
}
+ /**
+ * Returns the Private Key of this node (as a Uint8Array).
+ *
+ * @returns The Node's Private Key (as a Uint8Array).
+ */
+ public toBytes(): Uint8Array {
+ // Encode the XPriv from our node info.
+ return this.node.privateKey;
+ }
+
/**
* Gets the Private Key entity for this node.
*
* @returns The Private Key Entity for this node.
*/
- public privateKey(): PrivateKey {
+ public toPrivateKey(): PrivateKey {
// Return a Private Key Entity from the private key of our node.
- return new PrivateKey(this.node.privateKey);
+ return PrivateKey.fromRaw(this.node.privateKey);
+ }
+
+ /**
+ * Converts this node to an XPriv Key for export.
+ *
+ * @param network {HdKeyNetwork} The network to encode for.
+ *
+ * @returns The XPriv Key.
+ */
+ public toXPriv(network: HdKeyNetwork = 'mainnet'): string {
+ // Create our node info structure.
+ const nodeInfo: DecodedHdKey = {
+ network: network,
+ node: this.node,
+ };
+
+ // Encode the XPriv from our node info.
+ return encodeHdPrivateKey(nodeInfo).hdPrivateKey;
+ }
+
+ /**
+ * Returns the XPriv string for this node.
+ *
+ * @returns The XPriv string for this node.
+ */
+ public toString(): string {
+ // Return the Mainnet XPriv.
+ return this.toXPriv();
+ }
+
+ /**
+ * Returns the raw node data.
+ *
+ * @returns The raw node data.
+ */
+ public toRaw(): HdPrivateNodeValid {
+ return this.node;
+ }
+
+ /**
+ * Derives a HD Public Node from this Private Node.
+ *
+ * @returns The derived HD Public Node
+ */
+ public deriveHDPublicNode(): HDPublicNode {
+ // Derive a HD Public Node from this HD Private Node.
+ const publicNode = deriveHdPublicNode(this.node);
+
+ // Return a new HD Public Node entity from our derived Public Node.
+ return HDPublicNode.fromRaw(publicNode);
}
/**
@@ -216,64 +268,4 @@ export class HDPrivateNode {
// Return a new HD Private Node Entity derived from the given path.
return new HDPrivateNode(derivePathResult);
}
-
- /**
- * Derives a HD Public Node from this Private Node.
- *
- * @returns The derived HD Public Node
- */
- public deriveHDPublicNode(): HDPublicNode {
- // Derive a HD Public Node from this HD Private Node.
- const publicNode = deriveHdPublicNode(this.node);
-
- // Return a new HD Public Node entity from our derived Public Node.
- return new HDPublicNode(publicNode);
- }
-
- /**
- * Returns the Private Key of this node (as a Uint8Array).
- *
- * @returns The Node's Private Key (as a Uint8Array).
- */
- public toBytes(): Uint8Array {
- // Encode the XPriv from our node info.
- return this.node.privateKey;
- }
-
- /**
- * Converts this node to an XPriv Key for export.
- *
- * @param network {HdKeyNetwork} The network to encode for.
- *
- * @returns The XPriv Key.
- */
- public toXPriv(network: HdKeyNetwork = 'mainnet'): string {
- // Create our node info structure.
- const nodeInfo: DecodedHdKey = {
- network: network,
- node: this.node,
- };
-
- // Encode the XPriv from our node info.
- return encodeHdPrivateKey(nodeInfo).hdPrivateKey;
- }
-
- /**
- * Returns the XPriv string for this node.
- *
- * @returns The XPriv string for this node.
- */
- public toString(): string {
- // Return the Mainnet XPriv.
- return this.toXPriv();
- }
-
- /**
- * Returns the raw node data.
- *
- * @returns The raw node data.
- */
- public toRaw(): HdPrivateNodeValid {
- return this.node;
- }
}
diff --git a/packages/primitives/src/hd-public-node.ts b/packages/primitives/src/hd-public-node.ts
index bebeeb2..baac5c6 100644
--- a/packages/primitives/src/hd-public-node.ts
+++ b/packages/primitives/src/hd-public-node.ts
@@ -24,7 +24,7 @@ export class HDPublicNode {
*
* @param node {HdPublicNode} The HD Public Node.
*/
- constructor(node: HdPublicNodeValid) {
+ protected constructor(node: HdPublicNodeValid) {
this.node = node;
}
@@ -50,6 +50,14 @@ export class HDPublicNode {
return new HDPublicNode(decodeResult.node);
}
+ /**
+ * Creates a HDPublicNode instance from a raw object representation (LibAuth's HdPublicNodeValid).
+ *
+ * @remarks This method is UNSAFE and MUST only be used where input is already verified or trusted.
+ *
+ * @param obj - LibAuth Object of type HdPublicNodeValid
+ * @returns A new HDPublicNode instance
+ */
public static fromRaw(node: HdPublicNodeValid) {
return new this(node);
}
@@ -68,9 +76,46 @@ export class HDPublicNode {
*
* @returns The Public Key Entity for this node.
*/
- public publicKey(): PublicKey {
+ public toPublicKey(): PublicKey {
// Return a Public Key Entity from the public key of our node.
- return new PublicKey(this.node.publicKey);
+ return PublicKey.fromRaw(this.node.publicKey);
+ }
+
+ /**
+ * Converts this node to an XPub Key for export.
+ *
+ * @param network The network to encode for.
+ *
+ * @returns The XPub Key.
+ */
+ public toXPub(network: HdKeyNetwork = 'mainnet'): string {
+ // Create our node info structure.
+ const nodeInfo = {
+ network: network,
+ node: this.node,
+ } as DecodedHdKey;
+
+ // Encode the XPub from our node info.
+ return encodeHdPublicKey(nodeInfo).hdPublicKey;
+ }
+
+ /**
+ * Returns the XPub string for this node.
+ *
+ * @returns {string} The XPub string for this node.
+ */
+ public toString(): string {
+ // Return the Mainnet XPub.
+ return this.toXPub();
+ }
+
+ /**
+ * Converts the HDPublicKey to its raw Libauth representation (HDPublicNodeValid).
+ *
+ * @returns A LibAuth HDPublicNodeValid object
+ */
+ public toRaw(): HdPublicNodeValid {
+ return { ...this.node };
}
/**
@@ -94,36 +139,4 @@ export class HDPublicNode {
// Return a new HD Public Node Entity derived from the given path.
return new HDPublicNode(derivePathResult);
}
-
- /**
- * Converts this node to an XPub Key for export.
- *
- * @param network The network to encode for.
- *
- * @returns The XPub Key.
- */
- public toXPub(network: HdKeyNetwork = 'mainnet'): string {
- // Create our node info structure.
- const nodeInfo = {
- network: network,
- node: this.node,
- } as DecodedHdKey;
-
- // Encode the XPub from our node info.
- return encodeHdPublicKey(nodeInfo).hdPublicKey;
- }
-
- public toRaw(): HdPublicNodeValid {
- return this.node;
- }
-
- /**
- * Returns the XPub string for this node.
- *
- * @returns {string} The XPub string for this node.
- */
- public toString(): string {
- // Return the Mainnet XPub.
- return this.toXPub();
- }
}
diff --git a/packages/primitives/src/index.ts b/packages/primitives/src/index.ts
index a8b858b..7b5a2c2 100644
--- a/packages/primitives/src/index.ts
+++ b/packages/primitives/src/index.ts
@@ -2,7 +2,6 @@ export * from './address.js';
export * from './block-header.js';
export * from './bytes.js';
export * from './ext-json.js';
-export * from './hasher.js';
export * from './hd-private-node.js';
export * from './hd-public-node.js';
export * from './mnemonic.js';
diff --git a/packages/primitives/src/mnemonic.ts b/packages/primitives/src/mnemonic.ts
index f7cfbb1..a356bca 100644
--- a/packages/primitives/src/mnemonic.ts
+++ b/packages/primitives/src/mnemonic.ts
@@ -1,6 +1,7 @@
import {
- generateBip39MnemonicNonStandard,
bip39WordListEnglish,
+ deriveSeedFromBip39Mnemonic,
+ generateBip39MnemonicNonStandard,
} from '@bitauth/libauth';
export type MnemonicRaw = {
@@ -20,16 +21,19 @@ export class Mnemonic {
*
* @param phrase The complete phrase (mnemonic words + optional passphrase).
* @param wordList Optional wordlist (defaults to English BIP-39 wordlist).
+ *
* @throws {Error} If the mnemonic is invalid.
*/
- constructor(private mnemonic: MnemonicRaw) {}
+ protected constructor(private mnemonic: MnemonicRaw) {}
/**
* Instantiates a mnemonic from a string or array of words
*
* @param phraseOrWords The mnemonic phrase as a string or array of words
- * @param dictionary Optional wordlist (defaults to English BIP-39 wordlist).
+ * @param wordList Optional wordlist (defaults to English BIP-39 wordlist).
+ *
* @throws {Error} If mnemonic could not be generated.
+ *
* @returns Instance of Mnemonic.
*/
public static from(
@@ -41,18 +45,20 @@ export class Mnemonic {
return this.fromWords(phraseOrWords, wordList);
}
- return this.fromString(phraseOrWords, wordList);
+ return this.fromPhrase(phraseOrWords, wordList);
}
/**
* Instantiates a mnemonic from a string
*
* @param phrase The mnemonic phrase as a string
- * @param dictionary Optional wordlist (defaults to English BIP-39 wordlist).
+ * @param wordList Optional wordlist (defaults to English BIP-39 wordlist).
+ *
* @throws {Error} If mnemonic could not be generated.
+ *
* @returns Instance of Mnemonic.
*/
- public static fromString(
+ public static fromPhrase(
phrase: string,
wordList: Array = bip39WordListEnglish,
) {
@@ -69,8 +75,10 @@ export class Mnemonic {
* Instantiates a mnemonic from an array of words.
*
* @param words Array of words (strings) that represent the mnemonic
- * @param dictionary Optional wordlist (defaults to English BIP-39 wordlist).
+ * @param wordList Optional wordlist (defaults to English BIP-39 wordlist).
+ *
* @throws {Error} If mnemonic could not be generated.
+ *
* @returns Instance of Mnemonic.
*/
public static fromWords(
@@ -133,6 +141,7 @@ export class Mnemonic {
*
* @param mnemonicRaw The raw menmonic data.
* @throws {Error} If mnemonic could not be generated.
+ *
* @returns Instance of Mnemonic.
*/
public static fromRaw(mnemonicRaw: MnemonicRaw) {
@@ -144,6 +153,7 @@ export class Mnemonic {
*
* @param dictionary Optional wordlist (defaults to English BIP-39 wordlist).
* @throws {Error} If mnemonic could not be generated.
+ *
* @returns Instance of Mnemonic.
*/
public static generateRandom(
@@ -173,6 +183,7 @@ export class Mnemonic {
*
* @param phraseOrWords The mnemonic phrase or words to validate.
* @param wordList Optional wordlist (defaults to English BIP-39 wordlist).
+ *
* @returns boolean indicating if the phrase is valid.
*/
public static isValid(
@@ -192,6 +203,7 @@ export class Mnemonic {
*
* @param word Word to check.
* @param dictionary Optional wordlist (defaults to English BIP-39 wordlist).
+ *
* @returns boolean indicating if the word is valid.
*/
public static isValidWord(
@@ -202,21 +214,22 @@ export class Mnemonic {
}
/**
- * Gets the passphrase if one exists.
+ * Returns the resulting seed for this mnemonic.
*
- * @returns The passphrase or undefined if none exists.
+ * @returns The seed (for use with HDPrivateNodes).
*/
- public getPassphrase(): string | undefined {
- return this.mnemonic.passphrase;
+ public toSeed(): Uint8Array {
+ return deriveSeedFromBip39Mnemonic(this.toPhrase());
}
/**
* Returns the complete phrase as a string.
*
* @param includePassphrase Whether to include the passphrase in the output.
+ *
* @returns Complete phrase as a string.
*/
- public toString(includePassphrase: boolean = true): string {
+ public toPhrase(includePassphrase: boolean = true): string {
// Join the menmonic words, separating them using a space character.
const mnemonicWords = this.mnemonic.words.join(' ');
@@ -252,4 +265,13 @@ export class Mnemonic {
public toRaw(): MnemonicRaw {
return { ...this.mnemonic };
}
+
+ /**
+ * Gets the passphrase if one exists.
+ *
+ * @returns The passphrase or undefined if none exists.
+ */
+ public getPassphrase(): string | undefined {
+ return this.mnemonic.passphrase;
+ }
}
diff --git a/packages/primitives/src/private-key.ts b/packages/primitives/src/private-key.ts
index 5ca508c..d48fbd1 100644
--- a/packages/primitives/src/private-key.ts
+++ b/packages/primitives/src/private-key.ts
@@ -32,7 +32,7 @@ export class PrivateKey {
*
* @param bytes {Uint8Array} The raw bytes (32) for the Private Key.
*/
- constructor(bytes: Uint8Array) {
+ protected constructor(bytes: Uint8Array) {
// Ensure that the Private Key is valid.
if (!secp256k1.validatePrivateKey(bytes)) {
throw new Error(
@@ -43,6 +43,27 @@ export class PrivateKey {
this.bytes = bytes;
}
+ /**
+ * Instantiates a Private Key from either Bytes, Hex or WIF.
+ *
+ * @param privateKey The private key as either Bytes, Hex or WIF.
+ *
+ * @throws {Error} If the private key cannot be decoded.
+ *
+ * @returns The created Private Key Primitive.
+ */
+ public static from(privateKey: Uint8Array | string) {
+ if (privateKey instanceof Uint8Array) {
+ return PrivateKey.fromBytes(privateKey);
+ }
+
+ if (PrivateKey.isWIF(privateKey)) {
+ return PrivateKey.fromWIF(privateKey);
+ }
+
+ return PrivateKey.fromHex(privateKey);
+ }
+
/**
* Create a Private Key Entity from the given bytes.
*
@@ -124,6 +145,19 @@ export class PrivateKey {
return new PrivateKey(decodeResult.privateKey);
}
+ /**
+ * Creates an instance from serialized data.
+ *
+ * @remarks This method is UNSAFE and MUST only be used where input is already verified or trusted.
+ *
+ * @param bytes - Serialized Primitive Data
+ *
+ * @returns A new instance of this primitive.
+ */
+ public static fromRaw(bytes: Uint8Array) {
+ return new this(bytes);
+ }
+
/**
* Generate a random Private Key.
*
@@ -144,6 +178,57 @@ export class PrivateKey {
}
}
+ /**
+ * Convert the raw bytes of this Private Key to hexadecimal encoding.
+ *
+ * @returns {string} The raw bytes of the Private Key as a hexadecimal string.
+ */
+ public toHex(): string {
+ // Return the hexadecimal encoded bytes of this Private Key.
+ return binToHex(this.bytes);
+ }
+
+ /**
+ * Convert this Private Key to a raw 32 byte representation.
+ *
+ * @returns {Uint8Array} The raw Private Key as a Uint8Array.
+ */
+ public toBytes(): Uint8Array {
+ // Return the raw bytes for this Private Key.
+ return this.bytes;
+ }
+
+ /**
+ * Convert this Private Key to a WIF format.
+ *
+ * @param type {WalletImportFormatType} The type of WIF to create.
+ *
+ * @returns {string} The Private Key in WIF format.
+ */
+ public toWif(type: WalletImportFormatType = 'mainnet'): string {
+ // Return the encoded Private Key WIF.
+ return encodePrivateKeyWif(this.bytes, type);
+ }
+
+ /**
+ * Returns the Private Key as a mainnet WIF.
+ *
+ * @returns {string} The Private Key as a mainnet WIF.
+ */
+ public toString(): string {
+ // Return the encoded Mainnet Private Key WIF.
+ return this.toWif();
+ }
+
+ /**
+ * Converts this primitive to a serializable raw object representation.
+ *
+ * @returns Serialzied primitive data.
+ */
+ toRaw() {
+ return this.bytes;
+ }
+
/**
* Derive the public key from this private key.
*
@@ -161,7 +246,7 @@ export class PrivateKey {
}
// Return the derived Public Key.
- return new PublicKey(deriveResult);
+ return PublicKey.fromRaw(deriveResult);
}
/**
@@ -406,46 +491,4 @@ export class PrivateKey {
// Return the signature.
return signResult;
}
-
- /**
- * Convert the raw bytes of this Private Key to hexadecimal encoding.
- *
- * @returns {string} The raw bytes of the Private Key as a hexadecimal string.
- */
- public toHex(): string {
- // Return the hexadecimal encoded bytes of this Private Key.
- return binToHex(this.bytes);
- }
-
- /**
- * Convert this Private Key to a raw 32 byte representation.
- *
- * @returns {Uint8Array} The raw Private Key as a Uint8Array.
- */
- public toBytes(): Uint8Array {
- // Return the raw bytes for this Private Key.
- return this.bytes;
- }
-
- /**
- * Convert this Private Key to a WIF format.
- *
- * @param type {WalletImportFormatType} The type of WIF to create.
- *
- * @returns {string} The Private Key in WIF format.
- */
- public toWif(type: WalletImportFormatType = 'mainnet'): string {
- // Return the encoded Private Key WIF.
- return encodePrivateKeyWif(this.bytes, type);
- }
-
- /**
- * Returns the Private Key as a mainnet WIF.
- *
- * @returns {string} The Private Key as a mainnet WIF.
- */
- public toString(): string {
- // Return the encoded Mainnet Private Key WIF.
- return this.toWif();
- }
}
diff --git a/packages/primitives/src/public-key.ts b/packages/primitives/src/public-key.ts
index 899de7b..706683f 100644
--- a/packages/primitives/src/public-key.ts
+++ b/packages/primitives/src/public-key.ts
@@ -22,15 +22,25 @@ export class PublicKey {
*
* @param bytes The raw bytes (33) for the Public Key.
*/
- constructor(bytes: Uint8Array) {
- // Ensure that exactly 33 Bytes are given.
- if (!secp256k1.validatePublicKey(bytes)) {
- throw new Error(`Invalid Public Key.`);
- }
-
+ protected constructor(bytes: Uint8Array) {
this.bytes = bytes;
}
+ /**
+ * Instantiates a Public Key from either Public Key Hex or Public Key Bytes.
+ *
+ * @param publicKey The public key in either hex or bytes.
+ *
+ * @throws {Error} If the public key cannot be decoded.
+ *
+ * @returns The created Public Key Primitive.
+ */
+ public static from(publicKey: Uint8Array | string) {
+ return publicKey instanceof Uint8Array
+ ? PublicKey.fromBytes(publicKey)
+ : PublicKey.fromHex(publicKey);
+ }
+
/**
* Create a Public Key entity from the given hexadecimal string.
*
@@ -41,6 +51,11 @@ export class PublicKey {
* @returns The created Public Key entity.
*/
public static fromBytes(publicKeyBytes: Uint8Array): PublicKey {
+ // Ensure that exactly 33 Bytes are given.
+ if (!secp256k1.validatePublicKey(publicKeyBytes)) {
+ throw new Error(`Invalid Public Key.`);
+ }
+
return new PublicKey(publicKeyBytes);
}
@@ -68,6 +83,57 @@ export class PublicKey {
return new PublicKey(publicKey);
}
+ /**
+ * Creates an instance from serialized data.
+ *
+ * @remarks This method is UNSAFE and MUST only be used where input is already verified or trusted.
+ *
+ * @param bytes - Serialized Primitive Data
+ *
+ * @returns A new instance of this primitive.
+ */
+ public static fromRaw(bytes: Uint8Array) {
+ return new this(bytes);
+ }
+
+ /**
+ * Encode the raw bytes of this Public Key to a hexadecimal representation.
+ *
+ * @returns The raw bytes Public Key encoded as Hexadecimal.
+ */
+ public toHex(): string {
+ return binToHex(this.bytes);
+ }
+
+ /**
+ * Get the raw bytes of this Public Key.
+ *
+ * @returns The raw bytes of the Public Key.
+ */
+ public toBytes(): Uint8Array {
+ // Return the raw bytes for this Public Key.
+ return this.bytes;
+ }
+
+ /**
+ * Return the hexadecimal representation of the raw bytes of the Public Key.
+ *
+ * @returns The raw bytes of the Public Key as hexadecimal.
+ */
+ public toString(): string {
+ // Return the raw Public Key byes encoded as a hexadecimal string.
+ return this.toHex();
+ }
+
+ /**
+ * Converts this primitive to a serializable representation.
+ *
+ * @returns Serialzied primitive data.
+ */
+ toRaw() {
+ return this.bytes;
+ }
+
/**
* Derive the Hash160 for this Public Key.
*
@@ -212,33 +278,4 @@ export class PublicKey {
return validity;
}
-
- /**
- * Encode the raw bytes of this Public Key to a hexadecimal representation.
- *
- * @returns The raw bytes Public Key encoded as Hexadecimal.
- */
- public toHex(): string {
- return binToHex(this.bytes);
- }
-
- /**
- * Get the raw bytes of this Public Key.
- *
- * @returns The raw bytes of the Public Key.
- */
- public toBytes(): Uint8Array {
- // Return the raw bytes for this Public Key.
- return this.bytes;
- }
-
- /**
- * Return the hexadecimal representation of the raw bytes of the Public Key.
- *
- * @returns The raw bytes of the Public Key as hexadecimal.
- */
- public toString(): string {
- // Return the raw Public Key byes encoded as a hexadecimal string.
- return this.toHex();
- }
}
diff --git a/packages/primitives/src/satoshis.ts b/packages/primitives/src/satoshis.ts
index 28f14ef..6f626d6 100644
--- a/packages/primitives/src/satoshis.ts
+++ b/packages/primitives/src/satoshis.ts
@@ -1,3 +1,5 @@
+export type BCHUnits = ['sats', 'bits', 'mBCH', 'BCH'];
+
/**
* Satoshis Primitive
*/
@@ -11,7 +13,7 @@ export class Satoshis {
*
* @param satoshis {number} The satoshi amount.
*/
- constructor(satoshis: bigint | number) {
+ protected constructor(satoshis: bigint | number) {
// Set the satoshi amount.
this.satoshis = BigInt(satoshis);
}
diff --git a/packages/primitives/src/script.ts b/packages/primitives/src/script.ts
index 0d6e725..996bcfb 100644
--- a/packages/primitives/src/script.ts
+++ b/packages/primitives/src/script.ts
@@ -1,12 +1,9 @@
import {
assembleBytecodeBCH,
disassembleBytecodeBCH,
- base58AddressToLockingBytecode,
- cashAddressToLockingBytecode,
binToHex,
hexToBin,
isHex,
- parseScript,
} from '@bitauth/libauth';
/**
@@ -20,14 +17,29 @@ export class Script {
*
* @param bytes {Uint8Array} The raw bytes of the Script.
*/
- constructor(bytes: Uint8Array) {
+ protected constructor(bytes: Uint8Array) {
this.bytes = bytes;
}
/**
- * Create a Script Entity from the given Assembly String.
+ * Instantiates a Script from either Bytes or Hex.
*
- * @param asm {string} String of assembly code.
+ * @param script The script as either Bytes or Hex.
+ *
+ * @throws {Error} If the script cannot be decoded.
+ *
+ * @returns The created Script Primitive.
+ */
+ public static from(script: Uint8Array | string) {
+ return script instanceof Uint8Array
+ ? Script.fromBytes(script)
+ : Script.fromHex(script);
+ }
+
+ /**
+ * Create a Script Entity from bytes.
+ *
+ * @param bytes {string} Script Bytecode
*
* @throws {Error} If script cannot be decoded.
*
@@ -86,44 +98,6 @@ export class Script {
return new Script(scriptBin);
}
- /**
- * Create a Script Entity from the given CashAddr.
- *
- * @param cashAddr {string} The Address in CashAddr format.
- *
- * @throws {Error} If address cannot be decoded.
- *
- * @returns {Script} The created Script Entity.
- */
- public static fromCashAddr(cashAddr: string): Script {
- const decodeResult = cashAddressToLockingBytecode(cashAddr);
-
- if (typeof decodeResult === 'string') {
- throw new Error(decodeResult);
- }
-
- return new Script(decodeResult.bytecode);
- }
-
- /**
- * Create a Script Entity from the given Legacy Address.
- *
- * @param legacyAddress {string} The Address in Legacy format.
- *
- * @throws {Error} If address cannot be decoded.
- *
- * @returns {Script} The created Script Entity.
- */
- public static fromLegacyAddress(legacyAddress: string): Script {
- const decodeResult = base58AddressToLockingBytecode(legacyAddress);
-
- if (typeof decodeResult === 'string') {
- throw new Error(decodeResult);
- }
-
- return new Script(decodeResult.bytecode);
- }
-
/**
* Convert this script to a human-readable string of assembly.
*
diff --git a/packages/primitives/src/transaction-hash.ts b/packages/primitives/src/transaction-hash.ts
index 483fc45..3a715af 100644
--- a/packages/primitives/src/transaction-hash.ts
+++ b/packages/primitives/src/transaction-hash.ts
@@ -11,7 +11,35 @@ export class TransactionHash {
*
* @param bytes {Uint8Array} The Transaction Hash's bytes.
*/
- constructor(bytes: Uint8Array) {
+ protected constructor(bytes: Uint8Array) {
+ this.bytes = bytes;
+ }
+
+ /**
+ * Instantiates a TransactionHash from either the Hash Hex or Hash Bytes.
+ *
+ * @param hash The Transaction Hash as either hex or bytes.
+ *
+ * @throws {Error} If the hash is invalid.
+ *
+ * @returns The created Public Key Primitive.
+ */
+ public static from(hash: Uint8Array | string) {
+ return hash instanceof Uint8Array
+ ? TransactionHash.fromBytes(hash)
+ : TransactionHash.fromHex(hash);
+ }
+
+ /**
+ * Instantiates TransactionHash from Hash Bytes.
+ *
+ * @param bytes The hash as bytes.
+ *
+ * @throws {Error} If hash is invalid.
+ *
+ * @returns {TransactionHash} Instance of TransactionHash.
+ */
+ public static fromBytes(bytes: Uint8Array): TransactionHash {
// If bytes is not exactly 32, then this is an invalid transaction hash...
if (bytes.length !== 32) {
throw new Error(
@@ -19,7 +47,7 @@ export class TransactionHash {
);
}
- this.bytes = bytes;
+ return new TransactionHash(bytes);
}
/**
@@ -38,7 +66,22 @@ export class TransactionHash {
const bytes = hexToBin(hex);
- return new TransactionHash(bytes);
+ return this.fromBytes(bytes);
+ }
+
+ /**
+ * Instantiates a TransactionHash from either the Transaction Hex or Transaction Hash.
+ *
+ * @param hash The Transaction as either hex or bytes.
+ *
+ * @throws {Error} If the public key cannot be decoded.
+ *
+ * @returns The created Public Key Primitive.
+ */
+ public static fromTransaction(transaction: Uint8Array | string) {
+ return transaction instanceof Uint8Array
+ ? TransactionHash.fromTransactionBytes(transaction)
+ : TransactionHash.fromTransactionHex(transaction);
}
/**
@@ -75,6 +118,19 @@ export class TransactionHash {
return TransactionHash.fromTransactionBytes(bytes);
}
+ /**
+ * Creates an instance from serialized data.
+ *
+ * @remarks This method is UNSAFE and MUST only be used where input is already verified or trusted.
+ *
+ * @param bytes - Serialized Primitive Data
+ *
+ * @returns A new instance of this primitive.
+ */
+ public static fromRaw(bytes: Uint8Array) {
+ return new this(bytes);
+ }
+
/**
* Returns transaction hash as a hex string.
*
@@ -92,4 +148,13 @@ export class TransactionHash {
public toBytes(): Uint8Array {
return this.bytes;
}
+
+ /**
+ * Converts this primitive to a serializable representation.
+ *
+ * @returns Serialzied primitive data.
+ */
+ toRaw() {
+ return this.bytes;
+ }
}
diff --git a/packages/primitives/src/transaction.ts b/packages/primitives/src/transaction.ts
index 5aeed95..3c0f410 100644
--- a/packages/primitives/src/transaction.ts
+++ b/packages/primitives/src/transaction.ts
@@ -13,7 +13,7 @@ import type { TransactionBCH } from '@bitauth/libauth';
* Transaction Primitive.
*/
export class Transaction {
- constructor(public transaction: TransactionBCH) {}
+ protected constructor(public transaction: TransactionBCH) {}
/**
* Instantiates a transaction from either:
@@ -28,19 +28,9 @@ export class Transaction {
* @returns The created Transaction Primitive.
*/
public static from(transaction: Uint8Array | string) {
- // Handle string (hex) case
- if (typeof transaction === 'string') {
- return Transaction.fromHex(transaction);
- }
-
- // Handle Uint8Array case
- if (transaction instanceof Uint8Array) {
- return Transaction.fromBytes(transaction);
- }
-
- throw new TypeError(
- 'Invalid transaction input type. Expected Uint8Array, or hex string',
- );
+ return transaction instanceof Uint8Array
+ ? Transaction.fromBytes(transaction)
+ : Transaction.fromHex(transaction);
}
/**
@@ -90,7 +80,7 @@ export class Transaction {
/**
* Create a Transaction Primitive from LibAuth's TransactionBCH type.
*
- * @remarks This method is UNSAFE and should only be used where input is trusted.
+ * @remarks This method is UNSAFE and MUST only be used where input is already verified or trusted.
*
* @param transaction The transaction object (as per LibAuth).
*
@@ -104,6 +94,7 @@ export class Transaction {
* Validates the structure of a transaction.
*
* @param transaction The transaction (as hex or bytes).
+ *
* @returns boolean indicating whether the transaction structure is valid.
*/
public static isValid(transaction: Uint8Array | string): boolean {
@@ -116,34 +107,67 @@ export class Transaction {
return true;
}
+ /**
+ * Serializes the transaction to a byte array.
+ * @returns The transaction serialized as a Uint8Array
+ */
public toBytes(): Uint8Array {
const bytes = encodeTransaction(this.transaction);
return bytes;
}
+ /**
+ * Serializes the transaction to a byte array (represented as a hex string).
+ * @returns The transaction serialized in hex
+ */
public toHex(): string {
const bytes = this.toBytes();
return binToHex(bytes);
}
+ /**
+ * Hashes the transaction and returns a TransactionHash primitive.
+ * @returns The transaction hash primitive.
+ */
public toHash(): TransactionHash {
return TransactionHash.fromTransactionBytes(this.toBytes());
}
+ /**
+ * Hashes the transaction and returns the hash as hexadecimal.
+ * @returns The transaction hash hex.
+ */
public toHashHex(): string {
return this.toHash().toHex();
}
+ /**
+ * Hashes the transaction and returns the hash as a Uint8Array.
+ * @returns The transaction hash bytes.
+ */
public toHashBytes(): Uint8Array {
return this.toHash().toBytes();
}
+ /**
+ * Converts transaction to a raw object representation.
+ * @returns A TransactionBCH LibAuth object.
+ */
public toRaw(): TransactionBCH {
- return this.transaction;
+ return { ...this.transaction };
}
+ /**
+ * Checks whether this transaction is a Coinbase Transaction.
+ *
+ * @remarks UNTESTED
+ *
+ * @experimental
+ *
+ * @returns True if this is a Coinbase Transaction
+ */
public isCoinbase(): boolean {
// A coinbase transaction MUST only have one input
if (this.getInputs().length !== 1) {
@@ -164,18 +188,46 @@ export class Transaction {
return isOutpointZero && isOutpointIndexMax;
}
+ /**
+ * Gets the version for this transaction.
+ *
+ * @experimental
+ *
+ * @returns The version of this transaction
+ */
public getVersion() {
return this.transaction.version;
}
+ /**
+ * Gets the inputs in this transaction.
+ *
+ * @experimental
+ *
+ * @returns The inputs in this transaction
+ */
public getInputs() {
return this.transaction.inputs;
}
+ /**
+ * Gets the outputs in this transaction.
+ *
+ * @experimental
+ *
+ * @returns The outputs in this transaction
+ */
public getOutputs() {
return this.transaction.outputs;
}
+ /**
+ * Gets the locktime for this transaction.
+ *
+ * @experimental
+ *
+ * @returns The locktime of this transaction
+ */
public getLocktime() {
return this.transaction.locktime;
}
diff --git a/packages/primitives/tests/mnemonic.spec.ts b/packages/primitives/tests/mnemonic.spec.ts
index 5156a95..51daee7 100644
--- a/packages/primitives/tests/mnemonic.spec.ts
+++ b/packages/primitives/tests/mnemonic.spec.ts
@@ -1,21 +1,23 @@
import { describe, it, expect } from 'vitest';
+import { Bytes } from '~/src/bytes';
import { Mnemonic, MnemonicRaw } from '~/src/mnemonic';
describe('Success cases', () => {
// Simple array of cases
const testCases: Array<{
- fromString: string;
+ fromPhrase: string;
fromWords: Array;
passphrase?: string;
raw: MnemonicRaw;
- toString: string;
- toStringWithoutPassphrase: string;
+ toSeed: Uint8Array;
+ toPhrase: string;
+ toPhraseWithoutPassphrase: string;
toWords: Array;
toWordsWithoutPassphrase: Array;
}> = [
// No pass-phrase
{
- fromString:
+ fromPhrase:
'goose just fan kidney barrel album wave crowd movie flame cousin exhaust',
fromWords: [
'goose',
@@ -47,9 +49,12 @@ describe('Success cases', () => {
'exhaust',
],
},
- toString:
+ toSeed: Bytes.fromHex(
+ '7066f43a742829f0edce7d8e3c31651df320f13913995a6500f4e515db372b35202227f923ff181defc35cea02f8468df39f8fa9d97a202d252e61562e4bbfb5',
+ ).toBytes(),
+ toPhrase:
'goose just fan kidney barrel album wave crowd movie flame cousin exhaust',
- toStringWithoutPassphrase:
+ toPhraseWithoutPassphrase:
'goose just fan kidney barrel album wave crowd movie flame cousin exhaust',
toWords: [
'goose',
@@ -82,7 +87,7 @@ describe('Success cases', () => {
},
// Passphrase
{
- fromString:
+ fromPhrase:
'goose just fan kidney barrel album wave crowd movie flame cousin exhaust GeneralProtocols!',
fromWords: [
'goose',
@@ -117,9 +122,12 @@ describe('Success cases', () => {
],
passphrase: 'GeneralProtocols!',
},
- toString:
+ toSeed: Bytes.fromHex(
+ '2f706756ab2083f7ce07c0c0fcde8f14f062da5de839f42763262b25e0d871f03dd2b561d55eaacc471c94da656ee7b0007fb48a64e31ec91a1a2ba706b46b89',
+ ).toBytes(),
+ toPhrase:
'goose just fan kidney barrel album wave crowd movie flame cousin exhaust GeneralProtocols!',
- toStringWithoutPassphrase:
+ toPhraseWithoutPassphrase:
'goose just fan kidney barrel album wave crowd movie flame cousin exhaust',
toWords: [
'goose',
@@ -153,44 +161,46 @@ describe('Success cases', () => {
},
];
- it.each(testCases)(
- 'from(string)',
- ({ fromString: phrase, raw: expected }) => {
- const mnemonic = Mnemonic.from(phrase);
- expect(mnemonic.toRaw()).toStrictEqual(expected);
- },
- );
+ it.each(testCases)('from(string)', ({ fromPhrase: phrase, raw }) => {
+ const mnemonic = Mnemonic.from(phrase);
+ expect(mnemonic.toRaw()).toStrictEqual(raw);
+ });
- it.each(testCases)('from(words)', ({ fromWords, raw: expected }) => {
+ it.each(testCases)('from(words)', ({ fromWords, raw }) => {
const mnemonic = Mnemonic.from(fromWords);
- expect(mnemonic.toRaw()).toStrictEqual(expected);
+ expect(mnemonic.toRaw()).toStrictEqual(raw);
});
- it.each(testCases)('fromString', ({ fromString, raw: expected }) => {
- const mnemonic = Mnemonic.fromString(fromString);
- expect(mnemonic.toRaw()).toStrictEqual(expected);
+ it.each(testCases)('fromPhrase', ({ fromPhrase, raw }) => {
+ const mnemonic = Mnemonic.fromPhrase(fromPhrase);
+ expect(mnemonic.toRaw()).toStrictEqual(raw);
});
- it.each(testCases)('fromWords', ({ fromWords, raw: expected }) => {
+ it.each(testCases)('fromWords', ({ fromWords, raw }) => {
const mnemonic = Mnemonic.fromWords(fromWords);
- expect(mnemonic.toRaw()).toStrictEqual(expected);
+ expect(mnemonic.toRaw()).toStrictEqual(raw);
});
- it.each(testCases)('fromWords', ({ fromWords, raw: expected }) => {
+ it.each(testCases)('fromWords', ({ fromWords, raw }) => {
const mnemonic = Mnemonic.fromWords(fromWords);
- expect(mnemonic.toRaw()).toStrictEqual(expected);
+ expect(mnemonic.toRaw()).toStrictEqual(raw);
});
- it.each(testCases)('toString', ({ toString, raw }) => {
+ it.each(testCases)('toSeed', ({ toSeed, raw }) => {
const mnemonic = Mnemonic.fromRaw(raw);
- expect(mnemonic.toString()).toStrictEqual(toString);
+ expect(mnemonic.toSeed()).toStrictEqual(toSeed);
+ });
+
+ it.each(testCases)('toPhrase', ({ toPhrase, raw }) => {
+ const mnemonic = Mnemonic.fromRaw(raw);
+ expect(mnemonic.toPhrase()).toStrictEqual(toPhrase);
});
it.each(testCases)(
- 'toString(withoutPassphrase)',
- ({ toStringWithoutPassphrase, raw }) => {
+ 'toPhrase(withoutPassphrase)',
+ ({ toPhraseWithoutPassphrase, raw }) => {
const mnemonic = Mnemonic.fromRaw(raw);
- expect(mnemonic.toString(false)).toStrictEqual(toStringWithoutPassphrase);
+ expect(mnemonic.toPhrase(false)).toStrictEqual(toPhraseWithoutPassphrase);
},
);
@@ -254,10 +264,10 @@ describe('Validations', () => {
});
describe('Fail cases', () => {
- it('fromString should fail if invalid number of words', () => {
+ it('fromPhrase should fail if invalid number of words', () => {
expect(() => {
// 11 words.
- Mnemonic.fromString(
+ Mnemonic.fromPhrase(
'goose just fan kidney barrel album wave crowd movie flame cousin',
);
}).toThrow();
@@ -289,10 +299,10 @@ describe('Fail cases', () => {
}).toThrow();
});
- it('fromString should fail if word not in dictionary', () => {
+ it('fromPhrase should fail if word not in dictionary', () => {
expect(() => {
// Invalid word "fnord".
- Mnemonic.fromString(
+ Mnemonic.fromPhrase(
'fnord just fan kidney barrel album wave crowd movie flame cousin exhaust',
);
}).toThrow();
diff --git a/packages/stack/src/storage/base-store.ts b/packages/stack/src/storage/base-store.ts
index c6419ca..5eef6fe 100644
--- a/packages/stack/src/storage/base-store.ts
+++ b/packages/stack/src/storage/base-store.ts
@@ -1,8 +1,8 @@
-import { Encryption, EventEmitter } from '../utils';
+import { Encrypter, EventEmitter } from '../utils';
export type StorageOpts = {
syncInMemory?: boolean;
- encryption?: Encryption;
+ encryption?: Encrypter;
};
export type StoreEvents = {
diff --git a/packages/stack/src/storage/store-indexeddb.ts b/packages/stack/src/storage/store-indexeddb.ts
index f323998..e6267bd 100644
--- a/packages/stack/src/storage/store-indexeddb.ts
+++ b/packages/stack/src/storage/store-indexeddb.ts
@@ -1,7 +1,7 @@
import { BaseStorage, BaseStore, StorageOpts } from './base-store.js';
import { StoreInMemorySynced } from './store-inmemory-synced.js';
-import { Encryption } from '../utils';
+import { Encrypter } from '../utils';
import {
encodeExtendedJsonObject,
@@ -113,7 +113,7 @@ export class StoreIndexedDB extends BaseStore {
constructor(
public db: IDBPDatabase,
public storeName: string,
- public encryption?: Encryption,
+ public encryption?: Encrypter,
) {
super();
@@ -143,7 +143,7 @@ export class StoreIndexedDB extends BaseStore {
static async create(
db: IDBPDatabase,
storeName: string,
- encryption?: Encryption,
+ encryption?: Encrypter,
) {
return new this(db, storeName, encryption);
}
diff --git a/packages/stack/src/utils/encryption.ts b/packages/stack/src/utils/encrypter.ts
similarity index 91%
rename from packages/stack/src/utils/encryption.ts
rename to packages/stack/src/utils/encrypter.ts
index 1fb0480..fb82ac8 100644
--- a/packages/stack/src/utils/encryption.ts
+++ b/packages/stack/src/utils/encrypter.ts
@@ -9,7 +9,7 @@ import { utf8ToBin } from '@bitauth/libauth';
export const DEFAULT_XO_SALT =
'cash.xo.default_salt_for_encryption_aabbccddeeff00112233445566778899';
-export type EncryptionOpts = {
+export type EncrypterOpts = {
algorithm: string;
keyLength: number;
salt: Uint8Array;
@@ -17,13 +17,13 @@ export type EncryptionOpts = {
iterations: number;
};
-export class Encryption {
+export class Encrypter {
constructor(
private key: CryptoKey,
- public opts: EncryptionOpts,
+ public opts: EncrypterOpts,
) {}
- static async fromBytes(key: Uint8Array, opts: Partial = {}) {
+ static async fromBytes(key: Uint8Array, opts: Partial = {}) {
// Merge the provided options into our default options.
const encryptionOpts = {
algorithm: 'AES-GCM',
@@ -60,12 +60,12 @@ export class Encryption {
['encrypt', 'decrypt'],
);
- return new Encryption(derivedKey, encryptionOpts);
+ return new Encrypter(derivedKey, encryptionOpts);
}
static async fromPassphrase(
passphrase: string,
- opts: Partial = {},
+ opts: Partial = {},
) {
const key = utf8ToBin(passphrase);
diff --git a/packages/primitives/src/hasher.ts b/packages/stack/src/utils/hasher.ts
similarity index 100%
rename from packages/primitives/src/hasher.ts
rename to packages/stack/src/utils/hasher.ts
diff --git a/packages/stack/src/utils/index.ts b/packages/stack/src/utils/index.ts
index b42cf82..b85daf1 100644
--- a/packages/stack/src/utils/index.ts
+++ b/packages/stack/src/utils/index.ts
@@ -1,6 +1,7 @@
-export * from './encryption.js';
+export * from './encrypter.js';
export * from './event-emitter.js';
export * from './ext-error.js';
+export * from './hasher.js';
export * from './memoizer.js';
export * from './misc.js';
export * from './uuid.js';
diff --git a/packages/stack/src/wallet/wallet-hd.ts b/packages/stack/src/wallet/wallet-hd.ts
index efddd9e..8f75163 100644
--- a/packages/stack/src/wallet/wallet-hd.ts
+++ b/packages/stack/src/wallet/wallet-hd.ts
@@ -418,7 +418,7 @@ export class WalletHD<
// Derive the private key at the given path.
const privateKeyHex = await this.cache.getOrSet(derivationPath, () => {
const childNode = this.hdPrivateNode.derivePath(derivationPath);
- return childNode.privateKey().toHex();
+ return childNode.toPrivateKey().toHex();
});
// Create a child P2PKH wallet for this Private Key.
diff --git a/packages/stack/tests/utils/encryption.spec.ts b/packages/stack/tests/utils/encryption.spec.ts
index 22e5961..71f79e8 100644
--- a/packages/stack/tests/utils/encryption.spec.ts
+++ b/packages/stack/tests/utils/encryption.spec.ts
@@ -1,25 +1,25 @@
import { describe, it, expect, beforeEach } from 'vitest';
-import { Encryption } from '~/src/utils/encryption';
+import { Encrypter } from '~/src/utils/encrypter';
import { binToUtf8, utf8ToBin } from '@bitauth/libauth';
-describe('Encryption', () => {
- let encryption: Encryption;
+describe('Encrypter', () => {
+ let encryption: Encrypter;
const testKey = 'test-secret-key-123';
beforeEach(async () => {
- encryption = await Encryption.fromPassphrase(testKey);
+ encryption = await Encrypter.fromPassphrase(testKey);
});
describe('constructor', () => {
it('should create instance with string key', async () => {
- const enc = await Encryption.fromPassphrase('test-key');
- expect(enc).toBeInstanceOf(Encryption);
+ const enc = await Encrypter.fromPassphrase('test-key');
+ expect(enc).toBeInstanceOf(Encrypter);
});
it('should create instance with Uint8Array key', async () => {
- const enc = await Encryption.fromBytes(utf8ToBin('test-key'));
- expect(enc).toBeInstanceOf(Encryption);
+ const enc = await Encrypter.fromBytes(utf8ToBin('test-key'));
+ expect(enc).toBeInstanceOf(Encrypter);
});
});
@@ -77,8 +77,8 @@ describe('Encryption', () => {
describe('cross-instance encryption', () => {
it('should decrypt data encrypted with different instance using same key', async () => {
- const enc1 = await Encryption.fromPassphrase(testKey);
- const enc2 = await Encryption.fromPassphrase(testKey);
+ const enc1 = await Encrypter.fromPassphrase(testKey);
+ const enc2 = await Encrypter.fromPassphrase(testKey);
const originalData = 'Hello, World!';
const encrypted = await enc1.encrypt(originalData);