dedoopair
Version:
Blockchain Agnostic ECPair library for Bitcoin-like cryptocurrencies
167 lines (166 loc) • 5.43 kB
JavaScript
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
exports.ECPairFactory = void 0;
const types = require('./types');
const wif = require('wif');
const utils_1 = require('@noble/hashes/utils');
const isOptions = types.typeforce.maybe(
types.typeforce.compile({
compressed: types.maybe(types.Boolean),
network: types.maybe(types.Network),
}),
);
const toXOnly = (pubKey) =>
pubKey.length === 32 ? pubKey : pubKey.slice(1, 33);
function ECPairFactory(ecc) {
function isPoint(maybePoint) {
return ecc.isPoint(maybePoint);
}
function fromPrivateKey(buffer, options) {
types.typeforce(types.Buffer256bit, buffer);
if (!ecc.isPrivate(buffer))
throw new TypeError('Private key not in range [1, n)');
types.typeforce(isOptions, options);
return new ECPair(buffer, undefined, options);
}
function fromPublicKey(buffer, options) {
types.typeforce(ecc.isPoint, buffer);
types.typeforce(isOptions, options);
return new ECPair(undefined, buffer, options);
}
function fromWIF(wifString, network) {
const decoded = wif.decode(wifString);
const version = decoded.version;
// list of networks?
if (types.Array(network)) {
network = network
.filter((x) => {
return version === x.wif;
})
.pop();
if (!network) throw new Error('Unknown network version');
// otherwise, require a network object
} else {
if (!network) {
throw new Error(
'Network parameter is required when decoding WIF. Use networks.get("networkId") to get a network.',
);
}
if (version !== network.wif) throw new Error('Invalid network version');
}
return fromPrivateKey(Buffer.from(decoded.privateKey), {
compressed: decoded.compressed,
network: network,
});
}
function makeRandom(options) {
types.typeforce(isOptions, options);
if (options === undefined) options = {};
const rng =
options.rng || ((v) => Buffer.from((0, utils_1.randomBytes)(v)));
let d;
do {
d = rng(32);
types.typeforce(types.Buffer256bit, d);
} while (!ecc.isPrivate(d));
return fromPrivateKey(d, options);
}
class ECPair {
__D;
__Q;
compressed;
network;
lowR;
constructor(__D, __Q, options) {
this.__D = __D;
this.__Q = __Q;
this.lowR = false;
if (options === undefined) options = {};
this.compressed =
options.compressed === undefined ? true : options.compressed;
if (!options.network) {
throw new Error(
'Network parameter is required in ECPair options. Use networks.get("networkId") to get a network.',
);
}
this.network = options.network;
if (__Q !== undefined)
this.__Q = Buffer.from(ecc.pointCompress(__Q, this.compressed));
}
get privateKey() {
return this.__D;
}
get publicKey() {
if (!this.__Q) {
const p = ecc.pointFromScalar(this.__D, this.compressed);
this.__Q = Buffer.from(p);
}
return this.__Q;
}
toWIF() {
if (!this.__D) throw new Error('Missing private key');
return wif.encode(this.network.wif, this.__D, this.compressed);
}
tweak(t) {
if (this.privateKey) return this.tweakFromPrivateKey(t);
return this.tweakFromPublicKey(t);
}
sign(hash, _lowR) {
if (!this.__D) throw new Error('Missing private key');
return Buffer.from(
ecc.sign(hash, this.__D, (0, utils_1.randomBytes)(32)),
);
}
signSchnorr(hash) {
if (!this.privateKey) throw new Error('Missing private key');
if (!ecc.signSchnorr)
throw new Error('signSchnorr not supported by ecc library');
return Buffer.from(
ecc.signSchnorr(hash, this.privateKey, (0, utils_1.randomBytes)(32)),
);
}
verify(hash, signature) {
return ecc.verify(hash, this.publicKey, signature);
}
verifySchnorr(hash, signature) {
if (!ecc.verifySchnorr)
throw new Error('verifySchnorr not supported by ecc library');
return ecc.verifySchnorr(hash, this.publicKey.subarray(1, 33), signature);
}
tweakFromPublicKey(t) {
const xOnlyPubKey = toXOnly(this.publicKey);
const tweakedPublicKey = ecc.xOnlyPointAddTweak(xOnlyPubKey, t);
if (!tweakedPublicKey || tweakedPublicKey.xOnlyPubkey === null)
throw new Error('Cannot tweak public key!');
const parityByte = Buffer.from([
tweakedPublicKey.parity === 0 ? 0x02 : 0x03,
]);
return fromPublicKey(
Buffer.concat([parityByte, tweakedPublicKey.xOnlyPubkey]),
{ network: this.network, compressed: this.compressed },
);
}
tweakFromPrivateKey(t) {
const hasOddY =
this.publicKey[0] === 3 ||
(this.publicKey[0] === 4 && (this.publicKey[64] & 1) === 1);
const privateKey = hasOddY
? ecc.privateNegate(this.privateKey)
: this.privateKey;
const tweakedPrivateKey = ecc.privateAdd(privateKey, t);
if (!tweakedPrivateKey) throw new Error('Invalid tweaked private key!');
return fromPrivateKey(Buffer.from(tweakedPrivateKey), {
network: this.network,
compressed: this.compressed,
});
}
}
return {
isPoint,
fromPrivateKey,
fromPublicKey,
fromWIF,
makeRandom,
};
}
exports.ECPairFactory = ECPairFactory;