Resolve "WIP: UTXO Selection and Storage"
This commit is contained in:
@@ -7,7 +7,6 @@ import {
|
||||
generatePrivateKey,
|
||||
hexToBin,
|
||||
isHex,
|
||||
binToUtf8,
|
||||
utf8ToBin,
|
||||
bigIntToCompactUint,
|
||||
secp256k1,
|
||||
@@ -37,7 +36,7 @@ export class PrivateKey {
|
||||
// Ensure that the Private Key is valid.
|
||||
if (!secp256k1.validatePrivateKey(bytes)) {
|
||||
throw new Error(
|
||||
`Private Key is invalid: Must be >= 0x01 and <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364140.`
|
||||
`Private Key is invalid: Must be >= 0x01 and <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364140.`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -70,7 +69,7 @@ export class PrivateKey {
|
||||
// Ensure that the given string is hex.
|
||||
if (!isHex(privateKeyHex)) {
|
||||
throw new Error(
|
||||
`The given string (${privateKeyHex}) is not valid hexadecimal.`
|
||||
`The given string (${privateKeyHex}) is not valid hexadecimal.`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -90,7 +89,7 @@ export class PrivateKey {
|
||||
*/
|
||||
public static fromWIF(
|
||||
wif: string,
|
||||
removePrefixes = ['bitcoincash:', 'wif:']
|
||||
removePrefixes = ['bitcoincash:', 'wif:'],
|
||||
): PrivateKey {
|
||||
// Remove the prefixes from the WIF (if they exist).
|
||||
const wifWithoutPrefixes = removePrefixes.reduce((result, prefix) => {
|
||||
@@ -105,6 +104,22 @@ export class PrivateKey {
|
||||
throw new Error(decodeResult);
|
||||
}
|
||||
|
||||
// Ensure the Private Key is valid by checking the length (32 bytes = uncompressed private key, 33 bytes = compressed private key).
|
||||
// NOTE: This is to work around a LibAuth bug where decodePrivateKeyWif does not verify the version of the Base58 string.
|
||||
// This means that we will get a false positive on Base58/Legacy addresses as being valid WIFs.
|
||||
// Once the below PR is merged into LibAuth (and we've updated to the new version), we can remove the following code.
|
||||
// https://github.com/bitauth/libauth/pull/147
|
||||
const isValidPrivateKey =
|
||||
decodeResult.privateKey.length === 32 ||
|
||||
decodeResult.privateKey.length === 33;
|
||||
|
||||
// If it is not a valid private key, then it is not a WIF,
|
||||
if (!isValidPrivateKey) {
|
||||
throw new Error(
|
||||
`Expected 32 or 33 bytes WIF, received ${decodeResult.privateKey.length}`,
|
||||
);
|
||||
}
|
||||
|
||||
// Return a new Private Key entity.
|
||||
return new PrivateKey(decodeResult.privateKey);
|
||||
}
|
||||
@@ -120,6 +135,15 @@ export class PrivateKey {
|
||||
return new PrivateKey(bytes);
|
||||
}
|
||||
|
||||
public static isWIF(wif: string, removePrefixes = ['bitcoincash:', 'wif:']) {
|
||||
try {
|
||||
this.fromWIF(wif, removePrefixes);
|
||||
return true;
|
||||
} catch (_error) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Derive the public key from this private key.
|
||||
*
|
||||
@@ -141,101 +165,83 @@ export class PrivateKey {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sign a message hash with this Private Key using a Recoverable Compact Signature.
|
||||
* Sign a message using a recoverable compact signature format.
|
||||
*
|
||||
* @param messageHash {Uint8Array} The hash of the message.
|
||||
* @param message The message to sign, either as a Uint8Array or string.
|
||||
* If a string is provided, it will be converted to UTF-8 bytes.
|
||||
*
|
||||
* @throws {Error} If message hash cannot be signed.
|
||||
* @throws {Error} If message cannot be signed.
|
||||
*
|
||||
* @returns {Uint8Array} The generated signature.
|
||||
* @returns The generated recoverable compact signature.
|
||||
*/
|
||||
public signMessageHashRecoverableCompact(
|
||||
messageHash: Uint8Array
|
||||
): RecoverableSignature {
|
||||
// Attempt to sign the message.
|
||||
const signResult = secp256k1.signMessageHashRecoverableCompact(
|
||||
this.bytes,
|
||||
messageHash
|
||||
);
|
||||
|
||||
// If a string is returned, this indicates an error...
|
||||
if (typeof signResult === 'string') {
|
||||
throw new Error(signResult);
|
||||
public signMessageRecoverableCompact(message: Uint8Array | string) {
|
||||
if (typeof message === 'string') {
|
||||
message = utf8ToBin(message);
|
||||
}
|
||||
|
||||
// Return the signature.
|
||||
return signResult;
|
||||
const messageHash = sha256.hash(message);
|
||||
|
||||
return this.signMessageHashRecoverableCompact(messageHash);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sign a message hash with this Private Key using a Compact Signature.
|
||||
* Sign a message using a compact signature format.
|
||||
*
|
||||
* @param messageHash {Uint8Array} The hash of the message.
|
||||
* @param message The message to sign, either as a Uint8Array or string.
|
||||
* If a string is provided, it will be converted to UTF-8 bytes.
|
||||
*
|
||||
* @throws {Error} If message hash cannot be signed.
|
||||
* @throws {Error} If message cannot be signed.
|
||||
*
|
||||
* @returns {Uint8Array} The generated signature.
|
||||
* @returns The generated compact signature.
|
||||
*/
|
||||
public signMessageHashCompact(messageHash: Uint8Array): Uint8Array {
|
||||
// Attempt to sign the message.
|
||||
const signResult = secp256k1.signMessageHashCompact(
|
||||
this.bytes,
|
||||
messageHash
|
||||
);
|
||||
|
||||
// If a string is returned, this indicates an error...
|
||||
if (typeof signResult === 'string') {
|
||||
throw new Error(signResult);
|
||||
public signMessageCompact(message: Uint8Array | string) {
|
||||
if (typeof message === 'string') {
|
||||
message = utf8ToBin(message);
|
||||
}
|
||||
|
||||
// Return the signature.
|
||||
return signResult;
|
||||
const messageHash = sha256.hash(message);
|
||||
|
||||
return this.signMessageHashCompact(messageHash);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sign a message hash with this Private Key using a Schnorr Signature.
|
||||
* Sign a message using the Schnorr signature scheme.
|
||||
*
|
||||
* @param messageHash {Uint8Array} The hash of the message.
|
||||
* @param message The message to sign, either as a Uint8Array or string.
|
||||
* If a string is provided, it will be converted to UTF-8 bytes.
|
||||
*
|
||||
* @throws {Error} If message hash cannot be signed.
|
||||
* @throws {Error} If message cannot be signed.
|
||||
*
|
||||
* @returns {Uint8Array} The generated signature.
|
||||
* @returns The generated Schnorr signature.
|
||||
*/
|
||||
public signMessageHashSchnorr(messageHash: Uint8Array): Uint8Array {
|
||||
// Attempt to sign the message.
|
||||
const signResult = secp256k1.signMessageHashSchnorr(
|
||||
this.bytes,
|
||||
messageHash
|
||||
);
|
||||
|
||||
// If a string is returned, this indicates an error...
|
||||
if (typeof signResult === 'string') {
|
||||
throw new Error(signResult);
|
||||
public signMessageSchnorr(message: Uint8Array | string) {
|
||||
if (typeof message === 'string') {
|
||||
message = utf8ToBin(message);
|
||||
}
|
||||
|
||||
// Return the signature.
|
||||
return signResult;
|
||||
const messageHash = sha256.hash(message);
|
||||
|
||||
return this.signMessageHashSchnorr(messageHash);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sign a message hash with this Private Key using a DER Signature.
|
||||
* Sign a message using DER (Distinguished Encoding Rules) format.
|
||||
*
|
||||
* @param messageHash {Uint8Array} The hash of the message.
|
||||
* @param message The message to sign, either as a Uint8Array or string.
|
||||
* If a string is provided, it will be converted to UTF-8 bytes.
|
||||
*
|
||||
* @throws {Error} If message hash cannot be signed.
|
||||
* @throws {Error} If message cannot be signed.
|
||||
*
|
||||
* @returns {Uint8Array} The generated signature.
|
||||
* @returns The generated DER signature.
|
||||
*/
|
||||
public signMessageHashDER(messageHash: Uint8Array): Uint8Array {
|
||||
const signResult = secp256k1.signMessageHashDER(this.bytes, messageHash);
|
||||
|
||||
// If a string is returned, this indicates an error...
|
||||
if (typeof signResult === 'string') {
|
||||
throw new Error(signResult);
|
||||
public signMessageDER(message: Uint8Array | string) {
|
||||
if (typeof message === 'string') {
|
||||
message = utf8ToBin(message);
|
||||
}
|
||||
|
||||
// Return the signature.
|
||||
return signResult;
|
||||
const messageHash = sha256.hash(message);
|
||||
|
||||
return this.signMessageHashDER(messageHash);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -279,6 +285,128 @@ export class PrivateKey {
|
||||
return electronEncoding;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sign a message hash with this Private Key using a Recoverable Compact Signature.
|
||||
*
|
||||
* @param messageHash {Uint8Array} The hash of the message.
|
||||
*
|
||||
* @throws {Error} If message hash cannot be signed.
|
||||
*
|
||||
* @returns {Uint8Array} The generated signature.
|
||||
*/
|
||||
public signMessageHashRecoverableCompact(
|
||||
messageHash: Uint8Array | string,
|
||||
): RecoverableSignature {
|
||||
// If a string was given, convert it from hex to bytes.
|
||||
// TODO: Consider using Bytes class - it ensures valid hex.
|
||||
if (typeof messageHash === 'string') {
|
||||
messageHash = hexToBin(messageHash);
|
||||
}
|
||||
|
||||
// Attempt to sign the message.
|
||||
const signResult = secp256k1.signMessageHashRecoverableCompact(
|
||||
this.bytes,
|
||||
messageHash,
|
||||
);
|
||||
|
||||
// If a string is returned, this indicates an error...
|
||||
if (typeof signResult === 'string') {
|
||||
throw new Error(signResult);
|
||||
}
|
||||
|
||||
// Return the signature.
|
||||
return signResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sign a message hash with this Private Key using a Compact Signature.
|
||||
*
|
||||
* @param messageHash {Uint8Array} The hash of the message.
|
||||
*
|
||||
* @throws {Error} If message hash cannot be signed.
|
||||
*
|
||||
* @returns {Uint8Array} The generated signature.
|
||||
*/
|
||||
public signMessageHashCompact(messageHash: Uint8Array | string): Uint8Array {
|
||||
// If a string was given, convert it from hex to bytes.
|
||||
// TODO: Consider using Bytes class - it ensures valid hex.
|
||||
if (typeof messageHash === 'string') {
|
||||
messageHash = hexToBin(messageHash);
|
||||
}
|
||||
|
||||
// Attempt to sign the message.
|
||||
const signResult = secp256k1.signMessageHashCompact(
|
||||
this.bytes,
|
||||
messageHash,
|
||||
);
|
||||
|
||||
// If a string is returned, this indicates an error...
|
||||
if (typeof signResult === 'string') {
|
||||
throw new Error(signResult);
|
||||
}
|
||||
|
||||
// Return the signature.
|
||||
return signResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sign a message hash with this Private Key using a Schnorr Signature.
|
||||
*
|
||||
* @param messageHash {Uint8Array} The hash of the message.
|
||||
*
|
||||
* @throws {Error} If message hash cannot be signed.
|
||||
*
|
||||
* @returns {Uint8Array} The generated signature.
|
||||
*/
|
||||
public signMessageHashSchnorr(messageHash: Uint8Array | string): Uint8Array {
|
||||
// If a string was given, convert it from hex to bytes.
|
||||
// TODO: Consider using Bytes class - it ensures valid hex.
|
||||
if (typeof messageHash === 'string') {
|
||||
messageHash = hexToBin(messageHash);
|
||||
}
|
||||
|
||||
// Attempt to sign the message.
|
||||
const signResult = secp256k1.signMessageHashSchnorr(
|
||||
this.bytes,
|
||||
messageHash,
|
||||
);
|
||||
|
||||
// If a string is returned, this indicates an error...
|
||||
if (typeof signResult === 'string') {
|
||||
throw new Error(signResult);
|
||||
}
|
||||
|
||||
// Return the signature.
|
||||
return signResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sign a message hash with this Private Key using a DER Signature.
|
||||
*
|
||||
* @param messageHash {Uint8Array} The hash of the message.
|
||||
*
|
||||
* @throws {Error} If message hash cannot be signed.
|
||||
*
|
||||
* @returns {Uint8Array} The generated signature.
|
||||
*/
|
||||
public signMessageHashDER(messageHash: Uint8Array | string): Uint8Array {
|
||||
// If a string was given, convert it from hex to bytes.
|
||||
// TODO: Consider using Bytes class - it ensures valid hex.
|
||||
if (typeof messageHash === 'string') {
|
||||
messageHash = hexToBin(messageHash);
|
||||
}
|
||||
|
||||
const signResult = secp256k1.signMessageHashDER(this.bytes, messageHash);
|
||||
|
||||
// If a string is returned, this indicates an error...
|
||||
if (typeof signResult === 'string') {
|
||||
throw new Error(signResult);
|
||||
}
|
||||
|
||||
// Return the signature.
|
||||
return signResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the raw bytes of this Private Key to hexadecimal encoding.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user