PHP WebShell
Текущая директория: /opt/BitGoJS/modules/utxo-bin/node_modules/bitcoinjs-lib/src/payments
Просмотр файла: p2tr.js
'use strict';
// SegWit version 1 P2TR output type for Taproot defined in
// https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki
Object.defineProperty(exports, '__esModule', { value: true });
exports.p2tr = void 0;
const networks_1 = require('../networks');
const bscript = require('../script');
const taproot = require('../taproot');
const lazy = require('./lazy');
const typef = require('typeforce');
const OPS = bscript.OPS;
const { bech32m } = require('bech32');
/**
* A secp256k1 x coordinate with unknown discrete logarithm used for eliminating
* keypath spends, equal to SHA256(uncompressedDER(SECP256K1_GENERATOR_POINT)).
*/
const H = Buffer.from(
'50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0',
'hex',
);
const EMPTY_BUFFER = Buffer.alloc(0);
// output: OP_1 {witnessProgram}
function p2tr(a, opts) {
if (
!a.address &&
!a.pubkey &&
!a.pubkeys &&
!(a.redeems && a.redeems.length) &&
!a.output &&
!a.witness
)
throw new TypeError('Not enough data');
opts = Object.assign({ validate: true }, opts || {});
if (!opts.eccLib) throw new Error('ECC Library is required for p2tr.');
const ecc = opts.eccLib;
typef(
{
network: typef.maybe(typef.Object),
address: typef.maybe(typef.String),
// the output script should be a fixed 34 bytes.
// 1 byte for OP_1 indicating segwit version 1, one byte for 0x20 to push
// the next 32 bytes, followed by the 32 byte witness program
output: typef.maybe(typef.BufferN(34)),
// a single pubkey
pubkey: typef.maybe(ecc.isXOnlyPoint),
// the pub key(s) used for keypath signing.
// aggregated with MuSig2* if > 1
pubkeys: typef.maybe(typef.arrayOf(ecc.isXOnlyPoint)),
redeems: typef.maybe(
typef.arrayOf({
network: typef.maybe(typef.Object),
output: typef.maybe(typef.Buffer),
weight: typef.maybe(typef.Number),
depth: typef.maybe(typef.Number),
witness: typef.maybe(typef.arrayOf(typef.Buffer)),
}),
),
redeemIndex: typef.maybe(typef.Number),
signature: typef.maybe(bscript.isCanonicalSchnorrSignature),
controlBlock: typef.maybe(typef.Buffer),
annex: typef.maybe(typef.Buffer),
},
a,
);
const _address = lazy.value(() => {
if (!a.address) return undefined;
const result = bech32m.decode(a.address);
const version = result.words.shift();
const data = bech32m.fromWords(result.words);
return {
version,
prefix: result.prefix,
data: Buffer.from(data),
};
});
const _outputPubkey = lazy.value(() => {
// we remove the first two bytes (OP_1 0x20) from the output script to
// extract the 32 byte taproot pubkey (aka witness program)
return a.output && a.output.slice(2);
});
const network = a.network || networks_1.bitcoin;
const o = { network };
const _taprootPaths = lazy.value(() => {
if (!a.redeems) return;
if (o.tapTree) {
return taproot.getDepthFirstTaptree(o.tapTree);
}
const outputs = a.redeems.map(({ output }) => output);
if (!outputs.every(output => output)) return;
return taproot.getHuffmanTaptree(
outputs,
a.redeems.map(({ weight }) => weight),
);
});
const _parsedWitness = lazy.value(() => {
if (!a.witness) return;
return taproot.parseTaprootWitness(a.witness);
});
const _parsedControlBlock = lazy.value(() => {
// Can't use o.controlBlock, because it could be circular
if (a.controlBlock) return taproot.parseControlBlock(ecc, a.controlBlock);
const parsedWitness = _parsedWitness();
if (parsedWitness && parsedWitness.spendType === 'Script')
return taproot.parseControlBlock(ecc, parsedWitness.controlBlock);
});
lazy.prop(o, 'internalPubkey', () => {
if (a.pubkey) {
// single pubkey
return a.pubkey;
} else if (a.pubkeys && a.pubkeys.length === 1) {
return a.pubkeys[0];
} else if (a.pubkeys && a.pubkeys.length > 1) {
// multiple pubkeys
return Buffer.from(taproot.aggregateMuSigPubkeys(ecc, a.pubkeys));
} else if (_parsedControlBlock()) {
return _parsedControlBlock().internalPubkey;
} else {
// If there is no key path spending condition, we use an internal key with unknown secret key.
// TODO: In order to avoid leaking the information that key path spending is not possible it
// is recommended to pick a fresh integer r in the range 0...n-1 uniformly at random and use
// H + rG as internal key. It is possible to prove that this internal key does not have a
// known discrete logarithm with respect to G by revealing r to a verifier who can then
// reconstruct how the internal key was created.
return H;
}
});
const _taprootPubkey = lazy.value(() => {
const parsedControlBlock = _parsedControlBlock();
const parsedWitness = _parsedWitness();
// Refuse to create an unspendable key
if (
!a.pubkey &&
!(a.pubkeys && a.pubkeys.length) &&
!a.redeems &&
!parsedControlBlock
)
return;
let taptreeRoot;
// Prefer to get the root via the control block because not all redeems may
// be available
if (parsedControlBlock) {
let tapscript;
if (parsedWitness && parsedWitness.spendType === 'Script') {
tapscript = parsedWitness.tapscript;
} else if (o.redeem && o.redeem.output) {
tapscript = o.redeem.output;
}
if (tapscript)
taptreeRoot = taproot.getTaptreeRoot(
ecc,
parsedControlBlock,
tapscript,
);
}
if (!taptreeRoot && _taprootPaths()) taptreeRoot = _taprootPaths().root;
return taproot.tapTweakPubkey(ecc, o.internalPubkey, taptreeRoot);
});
lazy.prop(o, 'tapTree', () => {
if (!a.redeems) return;
if (a.redeems.find(({ depth }) => depth === undefined)) {
console.warn(
'Deprecation Warning: Weight-based tap tree construction will be removed in the future. ' +
'Please use depth-first coding as specified in BIP-0371.',
);
return;
}
if (!a.redeems.every(({ output }) => output)) return;
return {
leaves: a.redeems.map(({ output, depth }) => {
return {
script: output,
leafVersion: taproot.INITIAL_TAPSCRIPT_VERSION,
depth,
};
}),
};
});
lazy.prop(o, 'address', () => {
const pubkey =
_outputPubkey() || (_taprootPubkey() && _taprootPubkey().xOnlyPubkey);
// only encode the 32 byte witness program as bech32m
const words = bech32m.toWords(pubkey);
words.unshift(0x01);
return bech32m.encode(network.bech32, words);
});
lazy.prop(o, 'controlBlock', () => {
const parsedWitness = _parsedWitness();
if (parsedWitness && parsedWitness.spendType === 'Script')
return parsedWitness.controlBlock;
const taprootPubkey = _taprootPubkey();
const taprootPaths = _taprootPaths();
if (!taprootPaths || !taprootPubkey || a.redeemIndex === undefined) return;
return taproot.getControlBlock(
taprootPubkey.parity,
o.internalPubkey,
taprootPaths.paths[a.redeemIndex],
);
});
lazy.prop(o, 'signature', () => {
const parsedWitness = _parsedWitness();
if (parsedWitness && parsedWitness.spendType === 'Key')
return parsedWitness.signature;
});
lazy.prop(o, 'annex', () => {
if (!_parsedWitness()) return;
return _parsedWitness().annex;
});
lazy.prop(o, 'output', () => {
if (a.address) {
const { data } = _address();
return bscript.compile([OPS.OP_1, data]);
}
const taprootPubkey = _taprootPubkey();
if (!taprootPubkey) return;
// OP_1 indicates segwit version 1
return bscript.compile([OPS.OP_1, Buffer.from(taprootPubkey.xOnlyPubkey)]);
});
lazy.prop(o, 'witness', () => {
if (!a.redeems) {
if (a.signature) return [a.signature]; // Keypath spend
return;
} else if (!o.redeem) {
return; // No chosen redeem script, can't make witness
} else if (!o.controlBlock) {
return;
}
let redeemWitness;
// some callers may provide witness elements in the input script
if (
o.redeem.input &&
o.redeem.input.length > 0 &&
o.redeem.output &&
o.redeem.output.length > 0
) {
// transform redeem input to witness stack
redeemWitness = bscript.toStack(bscript.decompile(o.redeem.input));
// assigns a new object to o.redeem
o.redeems[a.redeemIndex] = Object.assign(
{ witness: redeemWitness },
o.redeem,
);
o.redeem.input = EMPTY_BUFFER;
} else if (
o.redeem.output &&
o.redeem.output.length > 0 &&
o.redeem.witness &&
o.redeem.witness.length > 0
) {
redeemWitness = o.redeem.witness;
} else {
return;
}
const witness = [...redeemWitness, o.redeem.output, o.controlBlock];
if (a.annex) {
witness.push(a.annex);
}
return witness;
});
lazy.prop(o, 'name', () => {
const nameParts = ['p2tr'];
return nameParts.join('-');
});
lazy.prop(o, 'redeem', () => {
if (a.redeems) {
if (a.redeemIndex === undefined) return;
return a.redeems[a.redeemIndex];
}
const parsedWitness = _parsedWitness();
if (parsedWitness && parsedWitness.spendType === 'Script')
return {
witness: parsedWitness.scriptSig,
output: parsedWitness.tapscript,
};
});
// extended validation
if (opts.validate) {
const taprootPubkey = _taprootPubkey();
if (a.output) {
if (a.output[0] !== OPS.OP_1 || a.output[1] !== 0x20)
throw new TypeError('Output is invalid');
// if we're passed both an output script and an address, ensure they match
if (a.address && !_outputPubkey().equals(_address().data))
throw new TypeError('mismatch between address & output');
if (taprootPubkey && !_outputPubkey().equals(taprootPubkey.xOnlyPubkey))
throw new TypeError('mismatch between output and taproot pubkey');
}
if (a.address)
if (taprootPubkey && !_address().data.equals(taprootPubkey.xOnlyPubkey))
throw new TypeError('mismatch between address and taproot pubkey');
const parsedControlBlock = _parsedControlBlock();
if (parsedControlBlock) {
if (!parsedControlBlock.internalPubkey.equals(o.internalPubkey))
throw new TypeError('Internal pubkey mismatch');
if (taprootPubkey && parsedControlBlock.parity !== taprootPubkey.parity)
throw new TypeError('Parity mismatch');
}
if (a.redeems) {
if (!a.redeems.length) throw new TypeError('Empty redeems');
if (
a.redeemIndex !== undefined &&
(a.redeemIndex < 0 || a.redeemIndex >= a.redeems.length)
) {
throw new TypeError('invalid redeem index');
}
a.redeems.forEach(redeem => {
if (redeem.network && redeem.network !== network)
throw new TypeError('Network mismatch');
});
}
const chosenRedeem =
a.redeems && a.redeemIndex !== undefined && a.redeems[a.redeemIndex];
const parsedWitness = _parsedWitness();
if (parsedWitness && parsedWitness.spendType === 'Key') {
if (a.controlBlock)
throw new TypeError('unexpected control block for key path');
if (a.signature && !a.signature.equals(parsedWitness.signature))
throw new TypeError('mismatch between witness & signature');
}
if (parsedWitness && parsedWitness.spendType === 'Script') {
if (a.signature)
throw new TypeError('unexpected signature with script path witness');
if (a.controlBlock && !a.controlBlock.equals(parsedWitness.controlBlock))
throw new TypeError('control block mismatch');
if (
a.annex &&
parsedWitness.annex &&
!a.annex.equals(parsedWitness.annex)
)
throw new TypeError('annex mismatch');
if (
chosenRedeem &&
chosenRedeem.output &&
!chosenRedeem.output.equals(parsedWitness.tapscript)
)
throw new TypeError('tapscript mismatch');
}
}
return Object.assign(o, a);
}
exports.p2tr = p2tr;
Выполнить команду
Для локальной разработки. Не используйте в интернете!