@saturnbtcio/bip322-js
Version:
A Javascript library that provides utility functions related to the BIP-322 signature scheme
154 lines • 7.77 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 () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__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"));
const Network_1 = require("./helpers/Network");
/**
* 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,
Network_1.BITCOIN_NETWORK_JS_LIB_TESTNET4,
]);
// 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 ||
derivedAddresses.testnet4 === claimedAddress);
}
}
exports.default = Signer;
//# sourceMappingURL=Signer.js.map