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); } }