bip322-js
Version:
A Javascript library that provides utility functions related to the BIP-322 signature scheme
132 lines • 7.16 kB
JavaScript
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
// Import dependencies
const BIP322_1 = __importDefault(require("./BIP322"));
const ecpair_1 = __importDefault(require("ecpair"));
const helpers_1 = require("./helpers");
const bitcoin = __importStar(require("bitcoinjs-lib"));
const secp256k1_1 = __importDefault(require("@bitcoinerlab/secp256k1"));
const bitcoinMessage = __importStar(require("bitcoinjs-message"));
/**
* Class that signs BIP-322 signature using a private key.
* Reference: https://github.com/LegReq/bip0322-signatures/blob/master/BIP0322_signing.ipynb
*/
class Signer {
/**
* Sign a BIP-322 signature from P2WPKH, P2SH-P2WPKH, and single-key-spend P2TR address and its corresponding private key.
* Network is automatically inferred from the given address.
*
* @param privateKey Private key used to sign the message
* @param address Address to be signing the message
* @param message message_challenge to be signed by the address
* @returns BIP-322 simple signature, encoded in base-64
*/
static sign(privateKey, address, message) {
// Initialize private key used to sign the transaction
const ECPair = (0, ecpair_1.default)(secp256k1_1.default);
let signer = ECPair.fromWIF(privateKey, [bitcoin.networks.bitcoin, bitcoin.networks.testnet, bitcoin.networks.regtest]);
// Check if the private key can sign message for the given address
if (!this.checkPubKeyCorrespondToAddress(signer.publicKey, address)) {
throw new Error(`Invalid private key provided for signing message for ${address}.`);
}
// Handle legacy P2PKH signature
if (helpers_1.Address.isP2PKH(address)) {
// For P2PKH address, sign a legacy signature
// Reference: https://github.com/bitcoinjs/bitcoinjs-message/blob/c43430f4c03c292c719e7801e425d887cbdf7464/README.md?plain=1#L21
return bitcoinMessage.sign(message, signer.privateKey, signer.compressed).toString('base64');
}
// Convert address into corresponding script pubkey
const scriptPubKey = helpers_1.Address.convertAdressToScriptPubkey(address);
// Draft corresponding toSpend using the message and script pubkey
const toSpendTx = BIP322_1.default.buildToSpendTx(message, scriptPubKey);
// Draft corresponding toSign transaction based on the address type
let toSignTx;
if (helpers_1.Address.isP2SH(address)) {
// P2SH-P2WPKH signing path
// Derive the P2SH-P2WPKH redeemScript from the corresponding hashed public key
const redeemScript = bitcoin.payments.p2wpkh({
hash: bitcoin.crypto.hash160(signer.publicKey),
network: helpers_1.Address.getNetworkFromAddess(address)
}).output;
toSignTx = BIP322_1.default.buildToSignTx(toSpendTx.getId(), redeemScript, true);
}
else if (helpers_1.Address.isP2WPKH(address)) {
// P2WPKH signing path
toSignTx = BIP322_1.default.buildToSignTx(toSpendTx.getId(), scriptPubKey);
}
else {
// P2TR signing path
// Extract the taproot internal public key
const internalPublicKey = helpers_1.Key.toXOnly(signer.publicKey);
// Tweak the private key for signing, since the output and address uses tweaked key
// Reference: https://github.com/bitcoinjs/bitcoinjs-lib/blob/1a9119b53bcea4b83a6aa8b948f0e6370209b1b4/test/integration/taproot.spec.ts#L55
signer = signer.tweak(bitcoin.crypto.taggedHash('TapTweak', internalPublicKey));
// Draft a toSign transaction that spends toSpend transaction
toSignTx = BIP322_1.default.buildToSignTx(toSpendTx.getId(), scriptPubKey, false, internalPublicKey);
// Set the sighashType to bitcoin.Transaction.SIGHASH_ALL since it defaults to SIGHASH_DEFAULT
toSignTx.updateInput(0, {
sighashType: bitcoin.Transaction.SIGHASH_ALL
});
}
// Sign the toSign transaction
const toSignTxSigned = toSignTx.signAllInputs(signer, [bitcoin.Transaction.SIGHASH_ALL]).finalizeAllInputs();
// Extract and return the signature
return BIP322_1.default.encodeWitness(toSignTxSigned);
}
/**
* Check if a given public key is the public key for a claimed address.
* @param publicKey Public key to be tested
* @param claimedAddress Address claimed to be derived based on the provided public key
* @returns True if the claimedAddress can be derived by the provided publicKey, false if otherwise
*/
static checkPubKeyCorrespondToAddress(publicKey, claimedAddress) {
// Derive the same address type from the provided public key
let derivedAddresses;
if (helpers_1.Address.isP2PKH(claimedAddress)) {
derivedAddresses = helpers_1.Address.convertPubKeyIntoAddress(publicKey, 'p2pkh');
}
else if (helpers_1.Address.isP2SH(claimedAddress)) {
derivedAddresses = helpers_1.Address.convertPubKeyIntoAddress(publicKey, 'p2sh-p2wpkh');
}
else if (helpers_1.Address.isP2WPKH(claimedAddress)) {
derivedAddresses = helpers_1.Address.convertPubKeyIntoAddress(publicKey, 'p2wpkh');
}
else if (helpers_1.Address.isP2TR(claimedAddress)) {
derivedAddresses = helpers_1.Address.convertPubKeyIntoAddress(publicKey, 'p2tr');
}
else {
throw new Error('Unable to sign BIP-322 message for unsupported address type.'); // Unsupported address type
}
// Check if the derived address correspond to the claimedAddress
return ((derivedAddresses.mainnet === claimedAddress) || (derivedAddresses.testnet === claimedAddress) ||
(derivedAddresses.regtest === claimedAddress));
}
}
exports.default = Signer;
//# sourceMappingURL=Signer.js.map
;