From dfb5ba5c3609905277956364ad081a40a46d3d04 Mon Sep 17 00:00:00 2001 From: Antoni Spaanderman <56turtle56@gmail.com> Date: Sun, 3 Apr 2022 21:41:12 +0200 Subject: [PATCH 1/3] Completely rewrote convertScriptSigAsm it now gives identical output to esplora, tested with the following TXs (testnet): 88710a9a6751827490f260e307757543f533c0f18bcd6865794713d263d5f5a4 446b2aad074de94efa28a1e82d2e6016dcb8a8ca38aca1a5a8eac6ef54e56a2e 4cfc410092e9514c14f48b61e20d2d3baf540ae7e981a821dd8c05dd4b7cd591 4b55dde38173174ab09e5571ebffffca798ba11143d28b9770600ff376dc778a --- backend/src/api/bitcoin/bitcoin-api.ts | 85 ++++++++++++++++---------- 1 file changed, 54 insertions(+), 31 deletions(-) diff --git a/backend/src/api/bitcoin/bitcoin-api.ts b/backend/src/api/bitcoin/bitcoin-api.ts index 061b3ced1..ec786593e 100644 --- a/backend/src/api/bitcoin/bitcoin-api.ts +++ b/backend/src/api/bitcoin/bitcoin-api.ts @@ -147,7 +147,7 @@ class BitcoinApi implements AbstractBitcoinApi { scriptpubkey: vout.scriptPubKey.hex, scriptpubkey_address: vout.scriptPubKey && vout.scriptPubKey.address ? vout.scriptPubKey.address : vout.scriptPubKey.addresses ? vout.scriptPubKey.addresses[0] : '', - scriptpubkey_asm: vout.scriptPubKey.asm ? this.convertScriptSigAsm(vout.scriptPubKey.asm) : '', + scriptpubkey_asm: vout.scriptPubKey.asm ? this.convertScriptSigAsm(vout.scriptPubKey.hex) : '', scriptpubkey_type: this.translateScriptPubKeyType(vout.scriptPubKey.type), }; }); @@ -157,7 +157,7 @@ class BitcoinApi implements AbstractBitcoinApi { is_coinbase: !!vin.coinbase, prevout: null, scriptsig: vin.scriptSig && vin.scriptSig.hex || vin.coinbase || '', - scriptsig_asm: vin.scriptSig && this.convertScriptSigAsm(vin.scriptSig.asm) || '', + scriptsig_asm: vin.scriptSig && this.convertScriptSigAsm(vin.scriptSig.hex) || '', sequence: vin.sequence, txid: vin.txid || '', vout: vin.vout || 0, @@ -290,38 +290,61 @@ class BitcoinApi implements AbstractBitcoinApi { return transaction; } - private convertScriptSigAsm(str: string): string { - const a = str.split(' '); + private convertScriptSigAsm(hex: string): string { + const buf = Buffer.from(hex, 'hex'); + const b: string[] = []; - a.forEach((chunk) => { - if (chunk.substr(0, 3) === 'OP_') { - chunk = chunk.replace(/^OP_(\d+)$/, 'OP_PUSHNUM_$1'); - chunk = chunk.replace('OP_CHECKSEQUENCEVERIFY', 'OP_CSV'); - chunk = chunk.replace('OP_CHECKLOCKTIMEVERIFY', 'OP_CLTV'); - b.push(chunk); + + let i = 0; + while (i < buf.length) { + const op = buf[i]; + if (op >= 0x01 && op <= 0x4e) { + i++; + let push: number; + if (op === 0x4c) { + push = buf.readUInt8(i); + b.push('OP_PUSHDATA1'); + i += 1; + } else if (op === 0x4d) { + push = buf.readUInt16LE(i); + b.push('OP_PUSHDATA2'); + i += 2; + } else if (op === 0x4e) { + push = buf.readUInt32LE(i); + b.push('OP_PUSHDATA4'); + i += 4; + } else { + push = op; + b.push('OP_PUSHBYTES_' + push); + } + + const data = buf.slice(i, i + push); + if (data.length !== push) { + break; + } + + b.push(data.toString('hex')); + i += data.length; } else { - chunk = chunk.replace('[ALL]', '01'); - if (chunk === '0') { - b.push('OP_0'); - } else if (chunk.match(/^[^0]\d*$/)) { - const chunkInt = parseInt(chunk, 10); - if (chunkInt < 0) { - b.push('OP_PUSHNUM_NEG' + -chunkInt); + const opcode = bitcoinjs.script.toASM([ op ]); + if (opcode && op < 0xfd) { + if (opcode === 'OP_1NEGATE') { + b.push('OP_PUSHNUM_NEG1'); + } else if (/^OP_(\d+)$/.test(opcode) && opcode !== 'OP_0') { + b.push(opcode.replace(/^OP_(\d+)$/, 'OP_PUSHNUM_$1')); } else { - b.push('OP_PUSHNUM_' + chunk); + b.push(opcode + .replace('OP_CHECKSEQUENCEVERIFY', 'OP_CSV') + .replace('OP_CHECKLOCKTIMEVERIFY', 'OP_CLTV') + ); } } else { - const dataLength = Math.round(chunk.length / 2); - if (dataLength > 255) { - b.push('OP_PUSHDATA2' + ' ' + chunk); - } else if (dataLength > 75) { - b.push('OP_PUSHDATA1' + ' ' + chunk); - } else { - b.push('OP_PUSHBYTES_' + dataLength + ' ' + chunk); - } + b.push('OP_RETURN_' + op); } + i += 1; } - }); + } + return b.join(' '); } @@ -332,21 +355,21 @@ class BitcoinApi implements AbstractBitcoinApi { if (vin.prevout.scriptpubkey_type === 'p2sh') { const redeemScript = vin.scriptsig_asm.split(' ').reverse()[0]; - vin.inner_redeemscript_asm = this.convertScriptSigAsm(bitcoinjs.script.toASM(Buffer.from(redeemScript, 'hex'))); + vin.inner_redeemscript_asm = this.convertScriptSigAsm(redeemScript); if (vin.witness && vin.witness.length > 2) { const witnessScript = vin.witness[vin.witness.length - 1]; - vin.inner_witnessscript_asm = this.convertScriptSigAsm(bitcoinjs.script.toASM(Buffer.from(witnessScript, 'hex'))); + vin.inner_witnessscript_asm = this.convertScriptSigAsm(witnessScript); } } if (vin.prevout.scriptpubkey_type === 'v0_p2wsh' && vin.witness) { const witnessScript = vin.witness[vin.witness.length - 1]; - vin.inner_witnessscript_asm = this.convertScriptSigAsm(bitcoinjs.script.toASM(Buffer.from(witnessScript, 'hex'))); + vin.inner_witnessscript_asm = this.convertScriptSigAsm(witnessScript); } if (vin.prevout.scriptpubkey_type === 'v1_p2tr' && vin.witness && vin.witness.length > 1) { const witnessScript = vin.witness[vin.witness.length - 2]; - vin.inner_witnessscript_asm = this.convertScriptSigAsm(bitcoinjs.script.toASM(Buffer.from(witnessScript, 'hex'))); + vin.inner_witnessscript_asm = this.convertScriptSigAsm(witnessScript); } } From 096f2172c6a93473ce55e94f93368f281db4ad5e Mon Sep 17 00:00:00 2001 From: Antoni Spaanderman <56turtle56@gmail.com> Date: Sun, 3 Apr 2022 21:58:53 +0200 Subject: [PATCH 2/3] more direct opcode comparison --- backend/src/api/bitcoin/bitcoin-api.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/backend/src/api/bitcoin/bitcoin-api.ts b/backend/src/api/bitcoin/bitcoin-api.ts index ec786593e..17808d8bc 100644 --- a/backend/src/api/bitcoin/bitcoin-api.ts +++ b/backend/src/api/bitcoin/bitcoin-api.ts @@ -328,15 +328,16 @@ class BitcoinApi implements AbstractBitcoinApi { } else { const opcode = bitcoinjs.script.toASM([ op ]); if (opcode && op < 0xfd) { - if (opcode === 'OP_1NEGATE') { + if (op === 0x4f) { b.push('OP_PUSHNUM_NEG1'); - } else if (/^OP_(\d+)$/.test(opcode) && opcode !== 'OP_0') { + } else if (/^OP_(\d+)$/.test(opcode) && op !== 0x00) { b.push(opcode.replace(/^OP_(\d+)$/, 'OP_PUSHNUM_$1')); + } else if (op === 0xb1) { + b.push('OP_CLTV'); + } else if (op === 0xb2) { + b.push('OP_CSV'); } else { - b.push(opcode - .replace('OP_CHECKSEQUENCEVERIFY', 'OP_CSV') - .replace('OP_CHECKLOCKTIMEVERIFY', 'OP_CLTV') - ); + b.push(opcode); } } else { b.push('OP_RETURN_' + op); From 53d68a3571e09fa777b651a03eb476b01d71b06e Mon Sep 17 00:00:00 2001 From: Antoni Spaanderman <56turtle56@gmail.com> Date: Mon, 4 Apr 2022 17:16:34 +0200 Subject: [PATCH 3/3] name tapscript by its name + OP_CHECKSIGADD tapscript opcode detection --- backend/src/api/bitcoin/bitcoin-api.ts | 34 +++++++++++-------- .../transactions-list.component.html | 5 ++- .../pipes/asm-styler/asm-styler.pipe.ts | 1 + 3 files changed, 25 insertions(+), 15 deletions(-) diff --git a/backend/src/api/bitcoin/bitcoin-api.ts b/backend/src/api/bitcoin/bitcoin-api.ts index 17808d8bc..7b1fc161d 100644 --- a/backend/src/api/bitcoin/bitcoin-api.ts +++ b/backend/src/api/bitcoin/bitcoin-api.ts @@ -326,21 +326,27 @@ class BitcoinApi implements AbstractBitcoinApi { b.push(data.toString('hex')); i += data.length; } else { - const opcode = bitcoinjs.script.toASM([ op ]); - if (opcode && op < 0xfd) { - if (op === 0x4f) { - b.push('OP_PUSHNUM_NEG1'); - } else if (/^OP_(\d+)$/.test(opcode) && op !== 0x00) { - b.push(opcode.replace(/^OP_(\d+)$/, 'OP_PUSHNUM_$1')); - } else if (op === 0xb1) { - b.push('OP_CLTV'); - } else if (op === 0xb2) { - b.push('OP_CSV'); - } else { - b.push(opcode); - } + if (op === 0x00) { + b.push('OP_0'); + } else if (op === 0x4f) { + b.push('OP_PUSHNUM_NEG1'); + } else if (op === 0xb1) { + b.push('OP_CLTV'); + } else if (op === 0xb2) { + b.push('OP_CSV'); + } else if (op === 0xba) { + b.push('OP_CHECKSIGADD'); } else { - b.push('OP_RETURN_' + op); + const opcode = bitcoinjs.script.toASM([ op ]); + if (opcode && op < 0xfd) { + if (/^OP_(\d+)$/.test(opcode)) { + b.push(opcode.replace(/^OP_(\d+)$/, 'OP_PUSHNUM_$1')); + } else { + b.push(opcode); + } + } else { + b.push('OP_RETURN_' + op); + } } i += 1; } diff --git a/frontend/src/app/components/transactions-list/transactions-list.component.html b/frontend/src/app/components/transactions-list/transactions-list.component.html index 72407a405..eded208bd 100644 --- a/frontend/src/app/components/transactions-list/transactions-list.component.html +++ b/frontend/src/app/components/transactions-list/transactions-list.component.html @@ -111,7 +111,10 @@ - P2WSH witness script + P2TR tapscript + + P2WSH witness script + diff --git a/frontend/src/app/shared/pipes/asm-styler/asm-styler.pipe.ts b/frontend/src/app/shared/pipes/asm-styler/asm-styler.pipe.ts index f6f1a59ad..54a02e405 100644 --- a/frontend/src/app/shared/pipes/asm-styler/asm-styler.pipe.ts +++ b/frontend/src/app/shared/pipes/asm-styler/asm-styler.pipe.ts @@ -281,6 +281,7 @@ export class AsmStylerPipe implements PipeTransform { case 'CHECKSIGVERIFY': case 'CHECKMULTISIG': case 'CHECKMULTISIGVERIFY': + case 'CHECKSIGADD': style = 'crypto'; break;