Use Bytes Primitive

This commit is contained in:
Jim Hamill
2025-07-23 00:27:41 +00:00
parent 9af5dc0087
commit e72c678785
10 changed files with 62 additions and 72 deletions
+8 -5
View File
@@ -23,15 +23,18 @@ import { hash256 } from '@bitauth/libauth';
* ``` * ```
*/ */
export class BlockHeader { export class BlockHeader {
protected readonly bytes: Bytes;
// View of our data so that we can extract as numbers. // View of our data so that we can extract as numbers.
private view: DataView; protected readonly view: DataView;
/** /**
* Creates a new BlockHeader instance. * Creates a new BlockHeader instance.
* @param bytes The bytes representing our Block Header (Big Endian) * @param bytes The bytes representing our Block Header (Big Endian)
*/ */
protected constructor(private readonly bytes: Uint8Array) { protected constructor(bytes: Bytes | Uint8Array) {
this.view = new DataView(bytes.buffer); this.bytes = Bytes.from(bytes);
this.view = new DataView(this.bytes.buffer);
} }
/** /**
@@ -58,7 +61,7 @@ export class BlockHeader {
* @returns A new BlockHeader instance * @returns A new BlockHeader instance
* @throws Error if the byte array length is not 80 * @throws Error if the byte array length is not 80
*/ */
static fromBytes(bytes: Uint8Array): BlockHeader { static fromBytes(bytes: Bytes | Uint8Array): BlockHeader {
if (bytes.length !== 80) { if (bytes.length !== 80) {
throw new Error('Invalid block header length'); throw new Error('Invalid block header length');
} }
@@ -134,7 +137,7 @@ export class BlockHeader {
* Serializes the block header to a byte array. * Serializes the block header to a byte array.
* @returns 80-byte array containing the serialized block header * @returns 80-byte array containing the serialized block header
*/ */
toBytes(): Uint8Array { toBytes(): Bytes {
return this.bytes; return this.bytes;
} }
+2 -2
View File
@@ -176,9 +176,9 @@ export class HDPrivateNode {
* *
* @returns The Node's Private Key (as a Uint8Array). * @returns The Node's Private Key (as a Uint8Array).
*/ */
public toBytes(): Uint8Array { public toBytes(): Bytes {
// Encode the XPriv from our node info. // Encode the XPriv from our node info.
return this.node.privateKey; return Bytes.from(this.node.privateKey);
} }
/** /**
+8 -8
View File
@@ -33,16 +33,16 @@ import {
*/ */
export class PrivateKey { export class PrivateKey {
// The raw bytes representing the Private Key. // The raw bytes representing the Private Key.
protected readonly bytes: Uint8Array; protected readonly bytes: Bytes;
/** /**
* Construct a new Private Key. * Construct a new Private Key.
* *
* @throws {Error} If Private Key cannot be constructed. * @throws {Error} If Private Key cannot be constructed.
* *
* @param bytes {Uint8Array} The raw bytes (32) for the Private Key. * @param bytes The raw bytes (32) for the Private Key.
*/ */
protected constructor(bytes: Uint8Array) { protected constructor(bytes: Bytes | Uint8Array) {
// Ensure that the Private Key is valid. // Ensure that the Private Key is valid.
if (!secp256k1.validatePrivateKey(bytes)) { if (!secp256k1.validatePrivateKey(bytes)) {
throw new Error( throw new Error(
@@ -50,7 +50,7 @@ export class PrivateKey {
); );
} }
this.bytes = bytes; this.bytes = Bytes.from(bytes);
} }
/** /**
@@ -62,7 +62,7 @@ export class PrivateKey {
* *
* @returns The created Private Key Primitive. * @returns The created Private Key Primitive.
*/ */
public static from(privateKey: Uint8Array | string) { public static from(privateKey: Bytes | Uint8Array | string) {
if (privateKey instanceof Uint8Array) { if (privateKey instanceof Uint8Array) {
return this.fromBytes(privateKey); return this.fromBytes(privateKey);
} }
@@ -83,7 +83,7 @@ export class PrivateKey {
* *
* @returns {PrivateKey} The created Private Key Entity. * @returns {PrivateKey} The created Private Key Entity.
*/ */
public static fromBytes(privateKeyBytes: Uint8Array): PrivateKey { public static fromBytes(privateKeyBytes: Bytes | Uint8Array): PrivateKey {
return new this(privateKeyBytes); return new this(privateKeyBytes);
} }
@@ -194,9 +194,9 @@ export class PrivateKey {
/** /**
* Convert this Private Key to a raw 32 byte representation. * Convert this Private Key to a raw 32 byte representation.
* *
* @returns {Uint8Array} The raw Private Key as a Uint8Array. * @returns The raw Private Key as a Uint8Array.
*/ */
public toBytes(): Uint8Array { public toBytes(): Bytes {
// Return the raw bytes for this Private Key. // Return the raw bytes for this Private Key.
return this.bytes; return this.bytes;
} }
+7 -7
View File
@@ -24,15 +24,15 @@ import { secp256k1, sha256, ripemd160, utf8ToBin } from '@bitauth/libauth';
*/ */
export class PublicKey { export class PublicKey {
// The raw bytes representing the Public Key. // The raw bytes representing the Public Key.
protected readonly bytes: Uint8Array; protected readonly bytes: Bytes;
/** /**
* Construct a new Public Key. * Construct a new Public Key.
* *
* @param bytes The raw bytes (33) for the Public Key. * @param bytes The raw bytes (33) for the Public Key.
*/ */
protected constructor(bytes: Uint8Array) { protected constructor(bytes: Bytes | Uint8Array) {
this.bytes = bytes; this.bytes = Bytes.from(bytes);
} }
/** /**
@@ -44,7 +44,7 @@ export class PublicKey {
* *
* @returns The created Public Key Primitive. * @returns The created Public Key Primitive.
*/ */
public static from(publicKey: Uint8Array | string) { public static from(publicKey: Bytes | Uint8Array | string) {
return publicKey instanceof Uint8Array return publicKey instanceof Uint8Array
? this.fromBytes(publicKey) ? this.fromBytes(publicKey)
: this.fromHex(publicKey); : this.fromHex(publicKey);
@@ -59,7 +59,7 @@ export class PublicKey {
* *
* @returns The created Public Key entity. * @returns The created Public Key entity.
*/ */
public static fromBytes(publicKeyBytes: Uint8Array): PublicKey { public static fromBytes(publicKeyBytes: Bytes | Uint8Array): PublicKey {
// Ensure that exactly 33 Bytes are given. // Ensure that exactly 33 Bytes are given.
if (!secp256k1.validatePublicKey(publicKeyBytes)) { if (!secp256k1.validatePublicKey(publicKeyBytes)) {
throw new Error(`Invalid Public Key.`); throw new Error(`Invalid Public Key.`);
@@ -90,7 +90,7 @@ export class PublicKey {
* *
* @returns A new instance of this primitive. * @returns A new instance of this primitive.
*/ */
public static fromRaw(bytes: Uint8Array) { public static fromRaw(bytes: Bytes | Uint8Array) {
return new this(bytes); return new this(bytes);
} }
@@ -108,7 +108,7 @@ export class PublicKey {
* *
* @returns The raw bytes of the Public Key. * @returns The raw bytes of the Public Key.
*/ */
public toBytes(): Uint8Array { public toBytes(): Bytes {
// Return the raw bytes for this Public Key. // Return the raw bytes for this Public Key.
return this.bytes; return this.bytes;
} }
+6 -6
View File
@@ -28,15 +28,15 @@ import {
* ``` * ```
*/ */
export class Script { export class Script {
protected readonly bytes: Uint8Array; protected readonly bytes: Bytes;
/** /**
* Construct a new Script * Construct a new Script
* *
* @param bytes {Uint8Array} The raw bytes of the Script. * @param bytes {Uint8Array} The raw bytes of the Script.
*/ */
protected constructor(bytes: Uint8Array) { protected constructor(bytes: Bytes | Uint8Array) {
this.bytes = bytes; this.bytes = Bytes.from(bytes);
} }
/** /**
@@ -48,7 +48,7 @@ export class Script {
* *
* @returns The created Script Primitive. * @returns The created Script Primitive.
*/ */
public static from(script: Uint8Array | string) { public static from(script: Bytes | Uint8Array | string) {
return script instanceof Uint8Array return script instanceof Uint8Array
? this.fromBytes(script) ? this.fromBytes(script)
: this.fromHex(script); : this.fromHex(script);
@@ -63,7 +63,7 @@ export class Script {
* *
* @returns {Script} The created Script Entity. * @returns {Script} The created Script Entity.
*/ */
public static fromBytes(bytes: Uint8Array): Script { public static fromBytes(bytes: Bytes | Uint8Array): Script {
// Disassemble the bytecode. // Disassemble the bytecode.
const disassembledBytecode = disassembleBytecodeBCH(bytes); const disassembledBytecode = disassembleBytecodeBCH(bytes);
@@ -213,7 +213,7 @@ export class Script {
* *
* @returns {Uint8Array} The compiled bytes. * @returns {Uint8Array} The compiled bytes.
*/ */
public toBytes(): Uint8Array { public toBytes(): Bytes {
return this.bytes; return this.bytes;
} }
@@ -5,9 +5,9 @@ import { Bytes } from '../src/bytes';
describe('Success cases', () => { describe('Success cases', () => {
// Test cases with different block header representations // Test cases with different block header representations
type BlockHeaderCase = { type BlockHeaderCase = {
raw: Uint8Array; raw: Bytes;
representations: { representations: {
bytes: Uint8Array; bytes: Bytes;
hex: string; hex: string;
}; };
}; };
@@ -16,11 +16,11 @@ describe('Success cases', () => {
{ {
raw: Bytes.fromHex( raw: Bytes.fromHex(
'0000602093ea70b294aeef69db85981bd387285824fc5af732e487010000000000000000135576e1ba9cae5509d60b4fbb9e66612f35b5ee8db264cb12612d46cc8db60b5e34a164d46b021864abbb1d', '0000602093ea70b294aeef69db85981bd387285824fc5af732e487010000000000000000135576e1ba9cae5509d60b4fbb9e66612f35b5ee8db264cb12612d46cc8db60b5e34a164d46b021864abbb1d',
).toUint8Array(), ),
representations: { representations: {
bytes: Bytes.fromHex( bytes: Bytes.fromHex(
'0000602093ea70b294aeef69db85981bd387285824fc5af732e487010000000000000000135576e1ba9cae5509d60b4fbb9e66612f35b5ee8db264cb12612d46cc8db60b5e34a164d46b021864abbb1d', '0000602093ea70b294aeef69db85981bd387285824fc5af732e487010000000000000000135576e1ba9cae5509d60b4fbb9e66612f35b5ee8db264cb12612d46cc8db60b5e34a164d46b021864abbb1d',
).toUint8Array(), ),
hex: '0000602093ea70b294aeef69db85981bd387285824fc5af732e487010000000000000000135576e1ba9cae5509d60b4fbb9e66612f35b5ee8db264cb12612d46cc8db60b5e34a164d46b021864abbb1d', hex: '0000602093ea70b294aeef69db85981bd387285824fc5af732e487010000000000000000135576e1ba9cae5509d60b4fbb9e66612f35b5ee8db264cb12612d46cc8db60b5e34a164d46b021864abbb1d',
}, },
}, },
@@ -35,19 +35,19 @@ describe('Success cases', () => {
fromMethods.forEach(({ name, key }) => { fromMethods.forEach(({ name, key }) => {
it.each(testCases)(`${name}()`, ({ raw, representations }) => { it.each(testCases)(`${name}()`, ({ raw, representations }) => {
const blockHeader = BlockHeader[name](representations[key]); const blockHeader = BlockHeader[name](representations[key]);
expect(blockHeader.toRaw()).toStrictEqual(raw); expect(blockHeader.toRaw()).toEqual(raw);
}); });
}); });
// Test generic from method // Test generic from method
it.each(testCases)('from(Uint8Array)', ({ raw, representations }) => { it.each(testCases)('from(Uint8Array)', ({ raw, representations }) => {
const blockHeader = BlockHeader.from(representations.bytes); const blockHeader = BlockHeader.from(representations.bytes);
expect(blockHeader.toRaw()).toStrictEqual(raw); expect(blockHeader.toRaw()).toEqual(raw);
}); });
it.each(testCases)('from(string)', ({ raw, representations }) => { it.each(testCases)('from(string)', ({ raw, representations }) => {
const blockHeader = BlockHeader.from(representations.hex); const blockHeader = BlockHeader.from(representations.hex);
expect(blockHeader.toRaw()).toStrictEqual(raw); expect(blockHeader.toRaw()).toEqual(raw);
}); });
// Test to methods with their respective types // Test to methods with their respective types
+6 -8
View File
@@ -1,13 +1,13 @@
import { describe, it, expect } from 'vitest'; import { describe, it, expect } from 'vitest';
import { Bytes } from '~/src/bytes'; import { Bytes } from '../src/bytes';
import { Script } from '~/src/script'; import { Script } from '../src/script';
describe('Success cases', () => { describe('Success cases', () => {
// Test cases with different script representations // Test cases with different script representations
type ScriptCase = { type ScriptCase = {
raw: Uint8Array; raw: Bytes;
representations: { representations: {
bytes: Uint8Array; bytes: Bytes;
asm: string; asm: string;
hex: string; hex: string;
}; };
@@ -15,13 +15,11 @@ describe('Success cases', () => {
const testCases: ScriptCase[] = [ const testCases: ScriptCase[] = [
{ {
raw: Bytes.fromHex( raw: Bytes.fromHex('76a914876fbb82ec05caa6af7a3b5e5a983aae6c6cc6d688ac'),
'76a914876fbb82ec05caa6af7a3b5e5a983aae6c6cc6d688ac',
).toUint8Array(),
representations: { representations: {
bytes: Bytes.fromHex( bytes: Bytes.fromHex(
'76a914876fbb82ec05caa6af7a3b5e5a983aae6c6cc6d688ac', '76a914876fbb82ec05caa6af7a3b5e5a983aae6c6cc6d688ac',
).toUint8Array(), ),
asm: 'OP_DUP\nOP_HASH160\nOP_PUSHBYTES_20\n0x876fbb82ec05caa6af7a3b5e5a983aae6c6cc6d6\nOP_EQUALVERIFY\nOP_CHECKSIG', asm: 'OP_DUP\nOP_HASH160\nOP_PUSHBYTES_20\n0x876fbb82ec05caa6af7a3b5e5a983aae6c6cc6d6\nOP_EQUALVERIFY\nOP_CHECKSIG',
hex: '76a914876fbb82ec05caa6af7a3b5e5a983aae6c6cc6d688ac', hex: '76a914876fbb82ec05caa6af7a3b5e5a983aae6c6cc6d688ac',
}, },
@@ -1,4 +1,4 @@
import { type WalletUTXO } from './types.js'; import { type TransactionTemplate, type WalletUTXO } from './types.js';
import { type UTXOSet, ExtMap, Outpoint } from '@xocash/primitives'; import { type UTXOSet, ExtMap, Outpoint } from '@xocash/primitives';
@@ -28,13 +28,26 @@ import {
*/ */
export class TransactionBuilder { export class TransactionBuilder {
static build( static build(
transaction: TransactionTemplateFixed<CompilerBCH>, txTemplate: Partial<TransactionTemplate>,
unspents: Array<WalletUTXO> = [], unspents: Array<WalletUTXO> = [],
changeLockingBytecode: Uint8Array, changeLockingBytecode: Uint8Array,
sourceOutputs: UTXOSet = new ExtMap(), sourceOutputs: UTXOSet = new ExtMap(),
) { ) {
// Populate the final transaction template by merging with defaults.
// NOTE: This is just a convenience to make this utility easier to use.
const txTemplateFinal: TransactionTemplate = {
...{
version: 2,
locktime: 0,
inputs: [],
outputs: [],
},
...txTemplate,
};
// Build the template.
const txBuilder = new TransactionBuilder( const txBuilder = new TransactionBuilder(
transaction, txTemplateFinal,
unspents, unspents,
sourceOutputs, sourceOutputs,
); );
+1 -13
View File
@@ -305,18 +305,6 @@ export class WalletHD extends BaseWallet {
async signTransaction( async signTransaction(
txTemplate: Partial<TransactionTemplate>, txTemplate: Partial<TransactionTemplate>,
): Promise<TransactionBuilder> { ): Promise<TransactionBuilder> {
// Populate the final transaction template by merging with defaults.
// NOTE: This is just a convenience to make "send" easier to use.
const txTemplateFinal: TransactionTemplate = {
...{
version: 2,
locktime: 0,
inputs: [],
outputs: [],
},
...txTemplate,
};
// Get the receiving address of this wallet. // Get the receiving address of this wallet.
// NOTE: As per BIP44 spec, we set the change address to use the internal chain. // NOTE: As per BIP44 spec, we set the change address to use the internal chain.
const changeAddress = await this.getReceivingAddress(CHAIN_EXTERNAL); const changeAddress = await this.getReceivingAddress(CHAIN_EXTERNAL);
@@ -329,7 +317,7 @@ export class WalletHD extends BaseWallet {
// Build the transaction. // Build the transaction.
return TransactionBuilder.build( return TransactionBuilder.build(
txTemplateFinal, txTemplate,
unspents.toArray(), unspents.toArray(),
changeLockingBytecode, changeLockingBytecode,
); );
+1 -13
View File
@@ -338,18 +338,6 @@ export class WalletP2PKH extends BaseWallet {
} }
async signTransaction(txTemplate: Partial<TransactionTemplate>) { async signTransaction(txTemplate: Partial<TransactionTemplate>) {
// Populate the final transaction template by merging with defaults.
// NOTE: This is just a convenience to make "send" easier to use.
const txTemplateFinal: TransactionTemplate = {
...{
version: 2,
locktime: 0,
inputs: [],
outputs: [],
},
...txTemplate,
};
// Get the receiving address of this wallet. // Get the receiving address of this wallet.
const receivingAddress = await this.getReceivingAddress(); const receivingAddress = await this.getReceivingAddress();
@@ -361,7 +349,7 @@ export class WalletP2PKH extends BaseWallet {
// Build the transaction. // Build the transaction.
return TransactionBuilder.build( return TransactionBuilder.build(
txTemplateFinal, txTemplate,
unspents.toArray(), unspents.toArray(),
changeLockingBytecode, changeLockingBytecode,
); );