Closed saurabhburade closed 7 months ago
It is impossible for p2tr key spend witnesses. (Since it’s only a signature) but every other type of segwit address can be recovered by its witness stack.
is that ok?
To be specific:
If you give this a P2TR witness that you know is definitely a P2TR, then it will give you the address if it's a script spend.
Probably buggy though.
import * as bitcoin from 'bitcoinjs-lib';
import * as vint from 'varuint-bitcoin';
import * as ecc from 'tiny-secp256k1';
// Required
bitcoin.initEccLib(ecc);
function p2trScriptToAddress(witness: Buffer[]): string {
if (!Array.isArray(witness) || !witness.every(b => Buffer.isBuffer(b))) {
throw new Error('All witness elements must be Buffers');
}
const wlen = witness.length;
if (wlen < 2) {
throw new Error('Not a p2tr script spend witness');
}
let annexOffset = 0;
if (witness[wlen - 1][0] === 0x50) {
// has annex
annexOffset = 1;
}
if (wlen < 2 + annexOffset) {
throw new Error('Not a p2tr script spend witness');
}
const controlBlock = witness[wlen - annexOffset - 1];
if (controlBlock.length < 33 || ((controlBlock.length - 33) / 32) % 1 !== 0) {
throw new Error('Incorrect Control block length');
}
const script = witness[wlen - annexOffset - 2];
const leafVersion = controlBlock[0] & 0xfe;
const internalPubkey = controlBlock.subarray(1, 33);
// Get preimage for tapleaf hash
const preImageLeafHash = Buffer.allocUnsafe(
script.length + vint.encodingLength(script.length) + 1,
);
preImageLeafHash.writeUInt8(leafVersion, 0);
vint.encode(script.length, preImageLeafHash, 1);
script.copy(preImageLeafHash, 1 + vint.encodingLength(script.length));
// Get tapleaf hash
let tapLeafHash = bitcoin.crypto.taggedHash('TapLeaf', preImageLeafHash);
const loops = (controlBlock.length - 33) / 32;
for (let j = 0; j < loops; j++) {
const branch = controlBlock.subarray(33 + 32 * j, 65 + 32 * j);
if (Buffer.compare(tapLeafHash, branch) < 0) {
tapLeafHash = bitcoin.crypto.taggedHash(
'TapBranch',
Buffer.concat([tapLeafHash, branch]),
);
} else {
tapLeafHash = bitcoin.crypto.taggedHash(
'TapBranch',
Buffer.concat([branch, tapLeafHash]),
);
}
}
const final = bitcoin.crypto.taggedHash(
'TapTweak',
Buffer.concat([internalPubkey, tapLeafHash]),
);
if (!ecc.isPrivate(final)) {
throw new Error(
'Rare error. Final taptree hash was higher than curve order',
);
}
const result = ecc.xOnlyPointAddTweak(internalPubkey, final);
if (result === null) {
throw new Error('Error when tweaking');
}
return bitcoin.address.fromOutputScript(
Buffer.concat([Buffer.from([0x51, 0x20]), Buffer.from(result.xOnlyPubkey)]),
);
}
@junderw Unable to recover address from https://mempool.space/tx/1bc6814dfab83f468e7ba4a24694974f1e33744e6b6ea0518ec6e99c80ff1016
If you're not going to read my replies, posts, and comments, then I am wasting my time here.
Good luck with whatever it is you're doing.
https://github.com/bitcoinjs/bitcoinjs-lib/issues/2088#issuecomment-2079221481 (Read this reply to your issue)
bc1pkqndh95dk*******uleyx4pqq7x4tl