Merge branch 'primitive_fixes' into 'development'

Misc Primitive Fixes

See merge request GeneralProtocols/xo/stack!18
This commit is contained in:
Jim Hamill
2025-01-15 03:05:54 +00:00
15 changed files with 119 additions and 109 deletions
+2
View File
@@ -41,6 +41,8 @@ function toPascalCase(str: string) {
} }
function convertNavToPascalCase(nav: Array<any>) { function convertNavToPascalCase(nav: Array<any>) {
console.log(nav);
return nav.map((item) => { return nav.map((item) => {
const result = { ...item }; const result = { ...item };
@@ -28,8 +28,8 @@ body {
} }
.vp-doc h4 { .vp-doc h4 {
font-size: 14px; font-size: 18px;
opacity: 0.75; opacity: 1;
color: var(--primary); color: var(--primary);
padding-top: 3em; padding-top: 3em;
border-top: 1px solid var(--vp-c-text-1); border-top: 1px solid var(--vp-c-text-1);
+2
View File
@@ -19,12 +19,14 @@
"enumMembersFormat": "table", "enumMembersFormat": "table",
"useCodeBlocks": true, "useCodeBlocks": true,
"sort": ["static-first"], "sort": ["static-first"],
"groupOrder": ["Methods", "Properties", "*"],
"exclude": "**/*+(index|.spec|.e2e|.browser|.node|.types).ts", "exclude": "**/*+(index|.spec|.e2e|.browser|.node|.types).ts",
"excludeInternal": true, "excludeInternal": true,
"excludeProtected": true, "excludeProtected": true,
"excludePrivate": true, "excludePrivate": true,
"excludeReferences": true, "excludeReferences": true,
"outputFileStrategy": "modules", "outputFileStrategy": "modules",
"hidePageTitle": true,
"hideGroupHeadings": true "hideGroupHeadings": true
/* /*
+3 -3
View File
@@ -29,7 +29,7 @@ export const DEFAULT_ADDRESS_PREFIXES = [
* Address Entity. * Address Entity.
*/ */
export class Address { export class Address {
public readonly addressContents: KnownAddressTypeContents; protected readonly addressContents: KnownAddressTypeContents;
protected constructor(addressContents: KnownAddressTypeContents) { protected constructor(addressContents: KnownAddressTypeContents) {
this.addressContents = addressContents; this.addressContents = addressContents;
@@ -102,7 +102,7 @@ export class Address {
} }
// Return new instance of address. // Return new instance of address.
return new Address({ payload: decodeResult.payload, type: encodeType }); return new this({ payload: decodeResult.payload, type: encodeType });
} }
/** /**
@@ -140,7 +140,7 @@ export class Address {
throw new Error(`Unsupported address type: ${decodeResult.version}`); throw new Error(`Unsupported address type: ${decodeResult.version}`);
} }
return new Address({ payload: decodeResult.payload, type: encodeType }); return new this({ payload: decodeResult.payload, type: encodeType });
} }
/** /**
+5 -5
View File
@@ -47,8 +47,8 @@ export class BlockHeader {
*/ */
public static from(blockHeader: Uint8Array | string) { public static from(blockHeader: Uint8Array | string) {
return blockHeader instanceof Uint8Array return blockHeader instanceof Uint8Array
? BlockHeader.fromBytes(blockHeader) ? this.fromBytes(blockHeader)
: BlockHeader.fromHex(blockHeader); : this.fromHex(blockHeader);
} }
/** /**
@@ -85,7 +85,7 @@ export class BlockHeader {
throw new Error('The given string is not valid hexadecimal.'); throw new Error('The given string is not valid hexadecimal.');
} }
return BlockHeader.fromBytes(hexToBin(hex)); return this.fromBytes(hexToBin(hex));
} }
/** /**
@@ -97,7 +97,7 @@ export class BlockHeader {
* @returns A new BlockHeader instance * @returns A new BlockHeader instance
*/ */
static fromRaw(obj: BlockHeaderRaw): BlockHeader { static fromRaw(obj: BlockHeaderRaw): BlockHeader {
return new BlockHeader( return new this(
obj.version, obj.version,
obj.previousBlockHash, obj.previousBlockHash,
obj.merkleRoot, obj.merkleRoot,
@@ -116,7 +116,7 @@ export class BlockHeader {
*/ */
public static isValid(blockHeader: Uint8Array | string): boolean { public static isValid(blockHeader: Uint8Array | string): boolean {
try { try {
BlockHeader.from(blockHeader); this.from(blockHeader);
} catch (_error) { } catch (_error) {
return false; return false;
} }
+1 -1
View File
@@ -16,7 +16,7 @@ import {
* Bytes Primitive. * Bytes Primitive.
*/ */
export class Bytes { export class Bytes {
public readonly bytes: Uint8Array; protected readonly bytes: Uint8Array;
/** /**
* Instantiate Bytes from a Uint8Array. * Instantiate Bytes from a Uint8Array.
+1 -1
View File
@@ -24,7 +24,7 @@ import type {
*/ */
export class HDPrivateNode { export class HDPrivateNode {
// The HD Private Node. // The HD Private Node.
public readonly node: HdPrivateNodeValid; protected readonly node: HdPrivateNodeValid;
/** /**
* Construct a new HD Private Node. * Construct a new HD Private Node.
+1 -1
View File
@@ -17,7 +17,7 @@ import type {
*/ */
export class HDPublicNode { export class HDPublicNode {
// The HD Public Node. // The HD Public Node.
public readonly node: HdPublicNodeValid; protected readonly node: HdPublicNodeValid;
/** /**
* Construct a new HD Public Node. * Construct a new HD Public Node.
+4 -7
View File
@@ -14,7 +14,7 @@ export type MnemonicRaw = {
* Supports 12, 15, 18, 21, and 24 word phrases with optional passphrase. * Supports 12, 15, 18, 21, and 24 word phrases with optional passphrase.
*/ */
export class Mnemonic { export class Mnemonic {
public static VALID_WORD_COUNTS = new Set([12, 15, 18, 21, 24]); protected static VALID_WORD_COUNTS = new Set([12, 15, 18, 21, 24]);
/** /**
* Instantiate Mnemonic from a phrase string. * Instantiate Mnemonic from a phrase string.
@@ -40,12 +40,9 @@ export class Mnemonic {
phraseOrWords: string | Array<string>, phraseOrWords: string | Array<string>,
wordList: Array<string> = bip39WordListEnglish, wordList: Array<string> = bip39WordListEnglish,
) { ) {
// If this is a list of words... return Array.isArray(phraseOrWords)
if (Array.isArray(phraseOrWords)) { ? this.fromWords(phraseOrWords, wordList)
return this.fromWords(phraseOrWords, wordList); : this.fromPhrase(phraseOrWords, wordList);
}
return this.fromPhrase(phraseOrWords, wordList);
} }
/** /**
+39 -30
View File
@@ -1,3 +1,4 @@
import { Bytes } from './bytes.js';
import { PublicKey } from './public-key.js'; import { PublicKey } from './public-key.js';
import { import {
@@ -5,9 +6,6 @@ import {
decodePrivateKeyWif, decodePrivateKeyWif,
encodePrivateKeyWif, encodePrivateKeyWif,
generatePrivateKey, generatePrivateKey,
hexToBin,
isHex,
utf8ToBin,
bigIntToCompactUint, bigIntToCompactUint,
secp256k1, secp256k1,
sha256, sha256,
@@ -23,7 +21,7 @@ import type {
*/ */
export class PrivateKey { export class PrivateKey {
// The raw bytes representing the Private Key. // The raw bytes representing the Private Key.
public readonly bytes: Uint8Array; protected readonly bytes: Uint8Array;
/** /**
* Construct a new Private Key. * Construct a new Private Key.
@@ -54,14 +52,14 @@ export class PrivateKey {
*/ */
public static from(privateKey: Uint8Array | string) { public static from(privateKey: Uint8Array | string) {
if (privateKey instanceof Uint8Array) { if (privateKey instanceof Uint8Array) {
return PrivateKey.fromBytes(privateKey); return this.fromBytes(privateKey);
} }
if (PrivateKey.isWIF(privateKey)) { if (this.isWIF(privateKey)) {
return PrivateKey.fromWIF(privateKey); return this.fromWIF(privateKey);
} }
return PrivateKey.fromHex(privateKey); return this.fromHex(privateKey);
} }
/** /**
@@ -74,7 +72,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: Uint8Array): PrivateKey {
return new PrivateKey(privateKeyBytes); return new this(privateKeyBytes);
} }
/** /**
@@ -87,18 +85,11 @@ export class PrivateKey {
* @returns {PrivateKey} The created Private Key Entity. * @returns {PrivateKey} The created Private Key Entity.
*/ */
public static fromHex(privateKeyHex: string): PrivateKey { public static fromHex(privateKeyHex: string): PrivateKey {
// Ensure that the given string is hex.
if (!isHex(privateKeyHex)) {
throw new Error(
`The given string (${privateKeyHex}) is not valid hexadecimal.`,
);
}
// Convert the hex to binary (Uint8Array). // Convert the hex to binary (Uint8Array).
const privateKeyBin = hexToBin(privateKeyHex); const privateKeyBin = Bytes.fromHex(privateKeyHex).toBytes();
// Create a Private Key Entity from the binary. // Create a Private Key Entity from the binary.
return new PrivateKey(privateKeyBin); return new this(privateKeyBin);
} }
/** /**
@@ -142,7 +133,7 @@ export class PrivateKey {
} }
// Return a new Private Key entity. // Return a new Private Key entity.
return new PrivateKey(decodeResult.privateKey); return new this(decodeResult.privateKey);
} }
/** /**
@@ -166,7 +157,7 @@ export class PrivateKey {
public static generateRandom(): PrivateKey { public static generateRandom(): PrivateKey {
const bytes = generatePrivateKey(); const bytes = generatePrivateKey();
return new PrivateKey(bytes); return new this(bytes);
} }
public static isWIF(wif: string, removePrefixes = ['bitcoincash:', 'wif:']) { public static isWIF(wif: string, removePrefixes = ['bitcoincash:', 'wif:']) {
@@ -261,7 +252,7 @@ export class PrivateKey {
*/ */
public signMessageRecoverableCompact(message: Uint8Array | string) { public signMessageRecoverableCompact(message: Uint8Array | string) {
if (typeof message === 'string') { if (typeof message === 'string') {
message = utf8ToBin(message); message = Bytes.fromUtf8(message).toBytes();
} }
const messageHash = sha256.hash(message); const messageHash = sha256.hash(message);
@@ -281,7 +272,7 @@ export class PrivateKey {
*/ */
public signMessageCompact(message: Uint8Array | string) { public signMessageCompact(message: Uint8Array | string) {
if (typeof message === 'string') { if (typeof message === 'string') {
message = utf8ToBin(message); message = Bytes.fromUtf8(message).toBytes();
} }
const messageHash = sha256.hash(message); const messageHash = sha256.hash(message);
@@ -301,7 +292,7 @@ export class PrivateKey {
*/ */
public signMessageSchnorr(message: Uint8Array | string) { public signMessageSchnorr(message: Uint8Array | string) {
if (typeof message === 'string') { if (typeof message === 'string') {
message = utf8ToBin(message); message = Bytes.fromUtf8(message).toBytes();
} }
const messageHash = sha256.hash(message); const messageHash = sha256.hash(message);
@@ -321,7 +312,7 @@ export class PrivateKey {
*/ */
public signMessageDER(message: Uint8Array | string) { public signMessageDER(message: Uint8Array | string) {
if (typeof message === 'string') { if (typeof message === 'string') {
message = utf8ToBin(message); message = Bytes.fromUtf8(message).toBytes();
} }
const messageHash = sha256.hash(message); const messageHash = sha256.hash(message);
@@ -344,8 +335,10 @@ export class PrivateKey {
*/ */
public signMessageElectron(message: string): Uint8Array { public signMessageElectron(message: string): Uint8Array {
// Compile the message. // Compile the message.
const magicBytes = utf8ToBin('\x18Bitcoin Signed Message:\n'); const magicBytes = Bytes.fromUtf8(
const messageBytes = utf8ToBin(message); '\x18Bitcoin Signed Message:\n',
).toBytes();
const messageBytes = Bytes.fromUtf8(message).toBytes();
const messageLengthBytes = bigIntToCompactUint(BigInt(messageBytes.length)); // hexToBin(messageBytes.length.toString(16)); // const messageLengthBytes = bigIntToCompactUint(BigInt(messageBytes.length)); // hexToBin(messageBytes.length.toString(16)); //
const messageFinal = new Uint8Array([ const messageFinal = new Uint8Array([
...magicBytes, ...magicBytes,
@@ -385,7 +378,11 @@ export class PrivateKey {
// If a string was given, convert it from hex to bytes. // If a string was given, convert it from hex to bytes.
// TODO: Consider using Bytes class - it ensures valid hex. // TODO: Consider using Bytes class - it ensures valid hex.
if (typeof messageHash === 'string') { if (typeof messageHash === 'string') {
messageHash = hexToBin(messageHash); messageHash = Bytes.fromHex(messageHash).toBytes();
}
if (messageHash.length !== 32) {
throw new Error('messageHash must be a hash of length 32 bytes');
} }
// Attempt to sign the message. // Attempt to sign the message.
@@ -416,7 +413,11 @@ export class PrivateKey {
// If a string was given, convert it from hex to bytes. // If a string was given, convert it from hex to bytes.
// TODO: Consider using Bytes class - it ensures valid hex. // TODO: Consider using Bytes class - it ensures valid hex.
if (typeof messageHash === 'string') { if (typeof messageHash === 'string') {
messageHash = hexToBin(messageHash); messageHash = Bytes.fromHex(messageHash).toBytes();
}
if (messageHash.length !== 32) {
throw new Error('messageHash must be a hash of length 32 bytes');
} }
// Attempt to sign the message. // Attempt to sign the message.
@@ -447,7 +448,11 @@ export class PrivateKey {
// If a string was given, convert it from hex to bytes. // If a string was given, convert it from hex to bytes.
// TODO: Consider using Bytes class - it ensures valid hex. // TODO: Consider using Bytes class - it ensures valid hex.
if (typeof messageHash === 'string') { if (typeof messageHash === 'string') {
messageHash = hexToBin(messageHash); messageHash = Bytes.fromHex(messageHash).toBytes();
}
if (messageHash.length !== 32) {
throw new Error('messageHash must be a hash of length 32 bytes');
} }
// Attempt to sign the message. // Attempt to sign the message.
@@ -478,7 +483,11 @@ export class PrivateKey {
// If a string was given, convert it from hex to bytes. // If a string was given, convert it from hex to bytes.
// TODO: Consider using Bytes class - it ensures valid hex. // TODO: Consider using Bytes class - it ensures valid hex.
if (typeof messageHash === 'string') { if (typeof messageHash === 'string') {
messageHash = hexToBin(messageHash); messageHash = Bytes.fromHex(messageHash).toBytes();
}
if (messageHash.length !== 32) {
throw new Error('messageHash must be a hash of length 32 bytes');
} }
const signResult = secp256k1.signMessageHashDER(this.bytes, messageHash); const signResult = secp256k1.signMessageHashDER(this.bytes, messageHash);
+5 -5
View File
@@ -15,7 +15,7 @@ import {
*/ */
export class PublicKey { export class PublicKey {
// The raw bytes representing the Public Key. // The raw bytes representing the Public Key.
public readonly bytes: Uint8Array; protected readonly bytes: Uint8Array;
/** /**
* Construct a new Public Key. * Construct a new Public Key.
@@ -37,8 +37,8 @@ export class PublicKey {
*/ */
public static from(publicKey: Uint8Array | string) { public static from(publicKey: Uint8Array | string) {
return publicKey instanceof Uint8Array return publicKey instanceof Uint8Array
? PublicKey.fromBytes(publicKey) ? this.fromBytes(publicKey)
: PublicKey.fromHex(publicKey); : this.fromHex(publicKey);
} }
/** /**
@@ -56,7 +56,7 @@ export class PublicKey {
throw new Error(`Invalid Public Key.`); throw new Error(`Invalid Public Key.`);
} }
return new PublicKey(publicKeyBytes); return new this(publicKeyBytes);
} }
/** /**
@@ -80,7 +80,7 @@ export class PublicKey {
const publicKey = hexToBin(publicKeyHex); const publicKey = hexToBin(publicKeyHex);
// Create a Public Key Entity from the binary. // Create a Public Key Entity from the binary.
return new PublicKey(publicKey); return new this(publicKey);
} }
/** /**
+6 -6
View File
@@ -4,7 +4,7 @@ export type BCHUnits = ['sats', 'bits', 'mBCH', 'BCH'];
* Satoshis Primitive * Satoshis Primitive
*/ */
export class Satoshis { export class Satoshis {
public readonly satoshis: bigint; protected readonly satoshis: bigint;
/** /**
* Construct a new instance of Satoshis. * Construct a new instance of Satoshis.
@@ -26,7 +26,7 @@ export class Satoshis {
* @returns {Satoshis} New instance of Satoshis. * @returns {Satoshis} New instance of Satoshis.
*/ */
public static fromSats(satoshis: bigint | number): Satoshis { public static fromSats(satoshis: bigint | number): Satoshis {
return new Satoshis(satoshis); return new this(satoshis);
} }
/** /**
@@ -39,7 +39,7 @@ export class Satoshis {
* @returns {Satoshis} New instance of Satoshis. * @returns {Satoshis} New instance of Satoshis.
*/ */
public static fromSatoshis(satoshis: bigint | number): Satoshis { public static fromSatoshis(satoshis: bigint | number): Satoshis {
return new Satoshis(satoshis); return new this(satoshis);
} }
/** /**
@@ -52,7 +52,7 @@ export class Satoshis {
public static fromBits(bits: number): Satoshis { public static fromBits(bits: number): Satoshis {
const satoshis = Satoshis.roundToDigits(bits * 100, 0); const satoshis = Satoshis.roundToDigits(bits * 100, 0);
return new Satoshis(satoshis); return new this(satoshis);
} }
/** /**
@@ -65,7 +65,7 @@ export class Satoshis {
public static fromMBCH(mBCH: number): Satoshis { public static fromMBCH(mBCH: number): Satoshis {
const satoshis = Satoshis.roundToDigits(mBCH * 100_000, 0); const satoshis = Satoshis.roundToDigits(mBCH * 100_000, 0);
return new Satoshis(satoshis); return new this(satoshis);
} }
/** /**
@@ -78,7 +78,7 @@ export class Satoshis {
public static fromBCH(bch: number): Satoshis { public static fromBCH(bch: number): Satoshis {
const satoshis = Satoshis.roundToDigits(bch * 100_000_000, 0); const satoshis = Satoshis.roundToDigits(bch * 100_000_000, 0);
return new Satoshis(satoshis); return new this(satoshis);
} }
/** /**
+6 -6
View File
@@ -10,7 +10,7 @@ import {
* Script Entity. * Script Entity.
*/ */
export class Script { export class Script {
public readonly bytes: Uint8Array; protected readonly bytes: Uint8Array;
/** /**
* Construct a new Script * Construct a new Script
@@ -32,8 +32,8 @@ export class Script {
*/ */
public static from(script: Uint8Array | string) { public static from(script: Uint8Array | string) {
return script instanceof Uint8Array return script instanceof Uint8Array
? Script.fromBytes(script) ? this.fromBytes(script)
: Script.fromHex(script); : this.fromHex(script);
} }
/** /**
@@ -46,7 +46,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: Uint8Array): Script {
return new Script(bytes); return new this(bytes);
} }
/** /**
@@ -73,7 +73,7 @@ export class Script {
} }
// Return new Script Entity from the assembled bytecode. // Return new Script Entity from the assembled bytecode.
return new Script(assembleResult.bytecode); return new this(assembleResult.bytecode);
} }
/** /**
@@ -95,7 +95,7 @@ export class Script {
const scriptBin = hexToBin(hex); const scriptBin = hexToBin(hex);
// Return new Script entity constructed from the binary. // Return new Script entity constructed from the binary.
return new Script(scriptBin); return new this(scriptBin);
} }
/** /**
+8 -8
View File
@@ -4,7 +4,7 @@ import { binToHex, hexToBin, hashTransaction, isHex } from '@bitauth/libauth';
* Transaction Hash Primitive. * Transaction Hash Primitive.
*/ */
export class TransactionHash { export class TransactionHash {
public readonly bytes: Uint8Array; protected readonly bytes: Uint8Array;
/** /**
* Instantiates TransactionHash from a Transaction Hash's bytes. * Instantiates TransactionHash from a Transaction Hash's bytes.
@@ -26,8 +26,8 @@ export class TransactionHash {
*/ */
public static from(hash: Uint8Array | string) { public static from(hash: Uint8Array | string) {
return hash instanceof Uint8Array return hash instanceof Uint8Array
? TransactionHash.fromBytes(hash) ? this.fromBytes(hash)
: TransactionHash.fromHex(hash); : this.fromHex(hash);
} }
/** /**
@@ -47,7 +47,7 @@ export class TransactionHash {
); );
} }
return new TransactionHash(bytes); return new this(bytes);
} }
/** /**
@@ -80,8 +80,8 @@ export class TransactionHash {
*/ */
public static fromTransaction(transaction: Uint8Array | string) { public static fromTransaction(transaction: Uint8Array | string) {
return transaction instanceof Uint8Array return transaction instanceof Uint8Array
? TransactionHash.fromTransactionBytes(transaction) ? this.fromTransactionBytes(transaction)
: TransactionHash.fromTransactionHex(transaction); : this.fromTransactionHex(transaction);
} }
/** /**
@@ -96,7 +96,7 @@ export class TransactionHash {
): TransactionHash { ): TransactionHash {
const transactionHash = hashTransaction(transactionBytes); const transactionHash = hashTransaction(transactionBytes);
return new TransactionHash(hexToBin(transactionHash)); return new this(hexToBin(transactionHash));
} }
/** /**
@@ -115,7 +115,7 @@ export class TransactionHash {
const bytes = hexToBin(transactionHex); const bytes = hexToBin(transactionHex);
return TransactionHash.fromTransactionBytes(bytes); return this.fromTransactionBytes(bytes);
} }
/** /**
+34 -34
View File
@@ -29,8 +29,8 @@ export class Transaction {
*/ */
public static from(transaction: Uint8Array | string) { public static from(transaction: Uint8Array | string) {
return transaction instanceof Uint8Array return transaction instanceof Uint8Array
? Transaction.fromBytes(transaction) ? this.fromBytes(transaction)
: Transaction.fromHex(transaction); : this.fromHex(transaction);
} }
/** /**
@@ -52,7 +52,7 @@ export class Transaction {
} }
// Return a new instance of the Transaction Primitive created from the decode result. // Return a new instance of the Transaction Primitive created from the decode result.
return new Transaction(decodeResult); return new this(decodeResult);
} }
/** /**
@@ -74,7 +74,7 @@ export class Transaction {
const bytes = hexToBin(hex); const bytes = hexToBin(hex);
// Return the transaction constructed from our bytes. // Return the transaction constructed from our bytes.
return Transaction.fromBytes(bytes); return this.fromBytes(bytes);
} }
/** /**
@@ -99,7 +99,7 @@ export class Transaction {
*/ */
public static isValid(transaction: Uint8Array | string): boolean { public static isValid(transaction: Uint8Array | string): boolean {
try { try {
Transaction.from(transaction); this.from(transaction);
} catch (_error) { } catch (_error) {
return false; return false;
} }
@@ -159,35 +159,6 @@ export class Transaction {
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) {
return false;
}
const input = this.getInputs()[0];
// Check if the outpoint transaction hash is all zeros (32 bytes of 0x00)
const isOutpointZero = input.outpointTransactionHash.every(
(byte) => byte === 0x00,
);
// Check if the outpoint index is 0xFFFFFFFF (or -1 in signed 32-bit integer representation)
const isOutpointIndexMax = input.outpointIndex === 0xffffffff;
// Both conditions must be true for a coinbase transaction
return isOutpointZero && isOutpointIndexMax;
}
/** /**
* Gets the version for this transaction. * Gets the version for this transaction.
* *
@@ -231,4 +202,33 @@ export class Transaction {
public getLocktime() { public getLocktime() {
return this.transaction.locktime; return this.transaction.locktime;
} }
/**
* 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) {
return false;
}
const input = this.getInputs()[0];
// Check if the outpoint transaction hash is all zeros (32 bytes of 0x00)
const isOutpointZero = input.outpointTransactionHash.every(
(byte) => byte === 0x00,
);
// Check if the outpoint index is 0xFFFFFFFF (or -1 in signed 32-bit integer representation)
const isOutpointIndexMax = input.outpointIndex === 0xffffffff;
// Both conditions must be true for a coinbase transaction
return isOutpointZero && isOutpointIndexMax;
}
} }