From ef82c8c496b9c66c29d01705c5590ac3bae0e6a2 Mon Sep 17 00:00:00 2001 From: Tomasz Slabon Date: Tue, 3 Oct 2023 16:58:19 +0200 Subject: [PATCH 01/12] Used bitcoinjs-lib for encodeToBitcoinAddress --- typescript/src/bitcoin.ts | 31 +++++-------------------------- typescript/test/bitcoin.test.ts | 20 ++++++++++++++++---- 2 files changed, 21 insertions(+), 30 deletions(-) diff --git a/typescript/src/bitcoin.ts b/typescript/src/bitcoin.ts index d7c04a2cf..2e269683e 100644 --- a/typescript/src/bitcoin.ts +++ b/typescript/src/bitcoin.ts @@ -1,5 +1,4 @@ import bcoin, { TX, Script } from "bcoin" -import wif from "wif" import bufio from "bufio" import { BigNumber, utils } from "ethers" import { Hex } from "./hex" @@ -502,26 +501,6 @@ export function compressPublicKey(publicKey: string | Hex): string { return `${prefix}${publicKeyX}` } -/** - * Creates a Bitcoin key ring based on the given private key. - * @param privateKey Private key that should be used to create the key ring - * @param witness Flag indicating whether the key ring will create witness - * or non-witness addresses - * @returns Bitcoin key ring. - */ -export function createKeyRing( - privateKey: string, - witness: boolean = true -): any { - const decodedPrivateKey = wif.decode(privateKey) - - return new bcoin.KeyRing({ - witness: witness, - privateKey: decodedPrivateKey.privateKey, - compressed: decodedPrivateKey.compressed, - }) -} - /** * Computes the HASH160 for the given text. * @param text - Text the HASH160 is computed for. @@ -580,13 +559,13 @@ export function hashLEToBigNumber(hash: Hex): BigNumber { export function encodeToBitcoinAddress( publicKeyHash: string, witness: boolean, - network: BitcoinNetwork + bitcoinNetwork: BitcoinNetwork ): string { - const buffer = Buffer.from(publicKeyHash, "hex") - const bcoinNetwork = toBcoinNetwork(network) + const hash = Buffer.from(publicKeyHash, "hex") + const network = toBitcoinJsLibNetwork(bitcoinNetwork) return witness - ? bcoin.Address.fromWitnessPubkeyhash(buffer).toString(bcoinNetwork) - : bcoin.Address.fromPubkeyhash(buffer).toString(bcoinNetwork) + ? payments.p2wpkh({ hash, network }).address! + : payments.p2pkh({ hash, network }).address! } /** diff --git a/typescript/test/bitcoin.test.ts b/typescript/test/bitcoin.test.ts index 2c57ed006..3e43be062 100644 --- a/typescript/test/bitcoin.test.ts +++ b/typescript/test/bitcoin.test.ts @@ -146,7 +146,10 @@ describe("Bitcoin", () => { true, BitcoinNetwork.Mainnet ) - ).to.throw("P2WPKH must be 20 bytes") + ).to.throw( + 'Expected property "hash" of type Buffer(Length: 20), got ' + + "Buffer(Length: 21)" + ) }) }) }) @@ -174,7 +177,10 @@ describe("Bitcoin", () => { false, BitcoinNetwork.Mainnet ) - ).to.throw("P2PKH must be 20 bytes") + ).to.throw( + 'Expected property "hash" of type Buffer(Length: 20), got ' + + "Buffer(Length: 21)" + ) }) }) }) @@ -204,7 +210,10 @@ describe("Bitcoin", () => { true, BitcoinNetwork.Testnet ) - ).to.throw("P2WPKH must be 20 bytes") + ).to.throw( + 'Expected property "hash" of type Buffer(Length: 20), got ' + + "Buffer(Length: 21)" + ) }) }) }) @@ -232,7 +241,10 @@ describe("Bitcoin", () => { false, BitcoinNetwork.Testnet ) - ).to.throw("P2PKH must be 20 bytes") + ).to.throw( + 'Expected property "hash" of type Buffer(Length: 20), got ' + + "Buffer(Length: 21)" + ) }) }) }) From 4c28279a59ab83e990216cb54d7c2aba295107ec Mon Sep 17 00:00:00 2001 From: Tomasz Slabon Date: Tue, 3 Oct 2023 17:28:54 +0200 Subject: [PATCH 02/12] Used bitcoinjs-lib for createAddressFromOutputScript --- typescript/src/bitcoin-network.ts | 21 ----------------- typescript/src/bitcoin.ts | 21 +++++++---------- typescript/test/bitcoin-network.test.ts | 31 ++----------------------- 3 files changed, 11 insertions(+), 62 deletions(-) diff --git a/typescript/src/bitcoin-network.ts b/typescript/src/bitcoin-network.ts index c59a134f2..c54c5da38 100644 --- a/typescript/src/bitcoin-network.ts +++ b/typescript/src/bitcoin-network.ts @@ -45,27 +45,6 @@ export namespace BitcoinNetwork { } } -/** - * Converts enumerated {@link BitcoinNetwork} to a string expected by the - * {@link https://github.com/keep-network/bcoin/blob/aba6841e43546e8a485e96dc0019d1e788eab2ee/lib/protocol/networks.js#L33| `bcoin` library} - * @param bitcoinNetwork Bitcoin network. - * @returns String representing the given network in bcoin library. - * @throws An error if the network is not supported by bcoin. - */ -export function toBcoinNetwork(bitcoinNetwork: BitcoinNetwork): string { - switch (bitcoinNetwork) { - case BitcoinNetwork.Mainnet: { - return "main" - } - case BitcoinNetwork.Testnet: { - return "testnet" - } - default: { - throw new Error(`network not supported`) - } - } -} - /** * Converts the provided {@link BitcoinNetwork} enumeration to a format expected * by the `bitcoinjs-lib` library. diff --git a/typescript/src/bitcoin.ts b/typescript/src/bitcoin.ts index 2e269683e..78f99870b 100644 --- a/typescript/src/bitcoin.ts +++ b/typescript/src/bitcoin.ts @@ -2,12 +2,8 @@ import bcoin, { TX, Script } from "bcoin" import bufio from "bufio" import { BigNumber, utils } from "ethers" import { Hex } from "./hex" -import { - BitcoinNetwork, - toBcoinNetwork, - toBitcoinJsLibNetwork, -} from "./bitcoin-network" -import { payments } from "bitcoinjs-lib" +import { BitcoinNetwork, toBitcoinJsLibNetwork } from "./bitcoin-network" +import { address, payments } from "bitcoinjs-lib" /** * Represents a transaction hash (or transaction ID) as an un-prefixed hex @@ -552,7 +548,7 @@ export function hashLEToBigNumber(hash: Hex): BigNumber { * unprefixed hex string (without 0x prefix). * @param witness - If true, a witness public key hash will be encoded and * P2WPKH address will be returned. Returns P2PKH address otherwise - * @param network - Network the address should be encoded for. + * @param bitcoinNetwork - Network the address should be encoded for. * @returns P2PKH or P2WPKH address encoded from the given public key hash * @throws Throws an error if network is not supported. */ @@ -626,16 +622,17 @@ export function createOutputScriptFromAddress(address: string): Hex { /** * Creates the Bitcoin address from the output script. * @param script The unprefixed and not prepended with length output script. - * @param network Bitcoin network. + * @param bitcoinNetwork Bitcoin network. * @returns The Bitcoin address. */ export function createAddressFromOutputScript( script: Hex, - network: BitcoinNetwork = BitcoinNetwork.Mainnet + bitcoinNetwork: BitcoinNetwork = BitcoinNetwork.Mainnet ): string { - return Script.fromRaw(script.toString(), "hex") - .getAddress() - ?.toString(toBcoinNetwork(network)) + return address.fromOutputScript( + script.toBuffer(), + toBitcoinJsLibNetwork(bitcoinNetwork) + ) } /** diff --git a/typescript/test/bitcoin-network.test.ts b/typescript/test/bitcoin-network.test.ts index e610c2ee9..b283c5441 100644 --- a/typescript/test/bitcoin-network.test.ts +++ b/typescript/test/bitcoin-network.test.ts @@ -1,9 +1,5 @@ import { expect } from "chai" -import { - BitcoinNetwork, - toBcoinNetwork, - toBitcoinJsLibNetwork, -} from "../src/bitcoin-network" +import { BitcoinNetwork, toBitcoinJsLibNetwork } from "../src/bitcoin-network" import { TransactionHash } from "../src/bitcoin" import { networks } from "bitcoinjs-lib" @@ -14,7 +10,6 @@ describe("BitcoinNetwork", () => { enumValue: "unknown", // any value that doesn't match other supported networks genesisHash: TransactionHash.from("0x00010203"), - expectedToBcoinResult: new Error("network not supported"), expectedToBitcoinJsLibResult: new Error("network not supported"), }, { @@ -23,7 +18,6 @@ describe("BitcoinNetwork", () => { genesisHash: TransactionHash.from( "0x000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943" ), - expectedToBcoinResult: "testnet", expectedToBitcoinJsLibResult: networks.testnet, }, { @@ -32,19 +26,12 @@ describe("BitcoinNetwork", () => { genesisHash: TransactionHash.from( "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f" ), - expectedToBcoinResult: "main", expectedToBitcoinJsLibResult: networks.bitcoin, }, ] testData.forEach( - ({ - enumKey, - enumValue, - genesisHash, - expectedToBcoinResult, - expectedToBitcoinJsLibResult, - }) => { + ({ enumKey, enumValue, genesisHash, expectedToBitcoinJsLibResult }) => { context(enumKey, async () => { describe(`toString`, async () => { it(`should return correct value`, async () => { @@ -60,20 +47,6 @@ describe("BitcoinNetwork", () => { }) }) - describe(`toBcoinNetwork`, async () => { - if (expectedToBcoinResult instanceof Error) { - it(`should throw an error`, async () => { - expect(() => toBcoinNetwork(enumKey)).to.throw( - expectedToBcoinResult.message - ) - }) - } else { - it(`should return ${expectedToBcoinResult}`, async () => { - expect(toBcoinNetwork(enumKey)).to.be.equal(expectedToBcoinResult) - }) - } - }) - describe(`toBitcoinJsLibNetwork`, async () => { if (expectedToBitcoinJsLibResult instanceof Error) { it(`should throw an error`, async () => { From a21fc6f80adc75b09a443a5c1235a5dd8dbbe138 Mon Sep 17 00:00:00 2001 From: Tomasz Slabon Date: Tue, 3 Oct 2023 18:03:58 +0200 Subject: [PATCH 03/12] Used bitcoinjs-lib for createOutputScriptFromAddress --- typescript/src/bitcoin.ts | 17 +++++++++++++---- typescript/src/deposit-refund.ts | 5 ++++- typescript/src/deposit-sweep.ts | 5 ++++- typescript/src/electrum.ts | 12 ++++++++++-- typescript/src/wallet.ts | 5 ++++- typescript/test/bitcoin.test.ts | 8 ++++++-- typescript/test/data/redemption.ts | 8 +++++--- 7 files changed, 46 insertions(+), 14 deletions(-) diff --git a/typescript/src/bitcoin.ts b/typescript/src/bitcoin.ts index 78f99870b..33df0dd4b 100644 --- a/typescript/src/bitcoin.ts +++ b/typescript/src/bitcoin.ts @@ -1,4 +1,4 @@ -import bcoin, { TX, Script } from "bcoin" +import bcoin, { TX } from "bcoin" import bufio from "bufio" import { BigNumber, utils } from "ethers" import { Hex } from "./hex" @@ -612,11 +612,20 @@ export function locktimeToNumber(locktimeLE: Buffer | string): number { /** * Creates the output script from the BTC address. - * @param address BTC address. + * @param bitcoinAddress Bitcoin address. + * @param bitcoinNetwork Bitcoin network. * @returns The un-prefixed and not prepended with length output script. */ -export function createOutputScriptFromAddress(address: string): Hex { - return Hex.from(Script.fromAddress(address).toRaw().toString("hex")) +export function createOutputScriptFromAddress( + bitcoinAddress: string, + bitcoinNetwork: BitcoinNetwork = BitcoinNetwork.Mainnet +): Hex { + return Hex.from( + address.toOutputScript( + bitcoinAddress, + toBitcoinJsLibNetwork(bitcoinNetwork) + ) + ) } /** diff --git a/typescript/src/deposit-refund.ts b/typescript/src/deposit-refund.ts index 3b68f81a9..2f6b9ee88 100644 --- a/typescript/src/deposit-refund.ts +++ b/typescript/src/deposit-refund.ts @@ -122,7 +122,10 @@ export async function assembleDepositRefundTransaction( utxo.outputIndex ) - const outputScript = createOutputScriptFromAddress(refunderAddress) + const outputScript = createOutputScriptFromAddress( + refunderAddress, + bitcoinNetwork + ) transaction.addOutput(outputScript.toBuffer(), outputValue.toNumber()) // In order to be able to spend the UTXO being refunded the transaction's diff --git a/typescript/src/deposit-sweep.ts b/typescript/src/deposit-sweep.ts index 7c68008eb..03cfc5e13 100644 --- a/typescript/src/deposit-sweep.ts +++ b/typescript/src/deposit-sweep.ts @@ -182,7 +182,10 @@ export async function assembleDepositSweepTransaction( } outputValue = outputValue.sub(fee) - const outputScript = createOutputScriptFromAddress(walletAddress) + const outputScript = createOutputScriptFromAddress( + walletAddress, + bitcoinNetwork + ) transaction.addOutput(outputScript.toBuffer(), outputValue.toNumber()) // Sign the main UTXO input if there is main UTXO. diff --git a/typescript/src/electrum.ts b/typescript/src/electrum.ts index 70d47d256..ba6380576 100644 --- a/typescript/src/electrum.ts +++ b/typescript/src/electrum.ts @@ -232,7 +232,11 @@ export class Client implements BitcoinClient { ): Promise { return this.withElectrum( async (electrum: Electrum) => { - const script = createOutputScriptFromAddress(address).toString() + const bitcoinNetwork = await this.getNetwork() + const script = createOutputScriptFromAddress( + address, + bitcoinNetwork + ).toString() // eslint-disable-next-line camelcase type UnspentOutput = { tx_pos: number; value: number; tx_hash: string } @@ -262,7 +266,11 @@ export class Client implements BitcoinClient { limit?: number ): Promise { return this.withElectrum(async (electrum: Electrum) => { - const script = createOutputScriptFromAddress(address).toString() + const bitcoinNetwork = await this.getNetwork() + const script = createOutputScriptFromAddress( + address, + bitcoinNetwork + ).toString() // eslint-disable-next-line camelcase type HistoryItem = { height: number; tx_hash: string } diff --git a/typescript/src/wallet.ts b/typescript/src/wallet.ts index 5c9a46ae1..947b95436 100644 --- a/typescript/src/wallet.ts +++ b/typescript/src/wallet.ts @@ -283,7 +283,10 @@ export async function determineWalletMainUtxo( // Get the wallet script based on the wallet address. This is required // to find transaction outputs that lock funds on the wallet. - const walletScript = createOutputScriptFromAddress(walletAddress) + const walletScript = createOutputScriptFromAddress( + walletAddress, + bitcoinNetwork + ) const isWalletOutput = (output: TransactionOutput) => walletScript.equals(output.scriptPubKey) diff --git a/typescript/test/bitcoin.test.ts b/typescript/test/bitcoin.test.ts index 3e43be062..65fa8198f 100644 --- a/typescript/test/bitcoin.test.ts +++ b/typescript/test/bitcoin.test.ts @@ -537,7 +537,11 @@ describe("Bitcoin", () => { ).forEach( ([addressType, { address, scriptPubKey: expectedOutputScript }]) => { it(`should create correct output script for ${addressType} address type`, () => { - const result = createOutputScriptFromAddress(address) + const network = + bitcoinNetwork === "mainnet" + ? BitcoinNetwork.Mainnet + : BitcoinNetwork.Testnet + const result = createOutputScriptFromAddress(address, network) expect(result.toString()).to.eq(expectedOutputScript.toString()) }) @@ -547,7 +551,7 @@ describe("Bitcoin", () => { }) }) - describe("getAddressFromScriptPubKey", () => { + describe("createAddressFromOutputScript", () => { Object.keys(btcAddresses).forEach((bitcoinNetwork) => { context(`with ${bitcoinNetwork} addresses`, () => { Object.entries( diff --git a/typescript/test/data/redemption.ts b/typescript/test/data/redemption.ts index 958b9dcc2..d38780f78 100644 --- a/typescript/test/data/redemption.ts +++ b/typescript/test/data/redemption.ts @@ -11,7 +11,7 @@ import { } from "../../src/bitcoin" import { RedemptionRequest } from "../../src/redemption" import { Address } from "../../src/ethereum" -import { BitcoinTransaction, Hex } from "../../src" +import { BitcoinNetwork, BitcoinTransaction, Hex } from "../../src" import { WalletState } from "../../src/wallet" /** @@ -723,7 +723,8 @@ export const findWalletForRedemptionData: { outputIndex: 0, value: BigNumber.from("791613461"), scriptPubKey: createOutputScriptFromAddress( - "tb1qqwm566yn44rdlhgph8sw8vecta8uutg79afuja" + "tb1qqwm566yn44rdlhgph8sw8vecta8uutg79afuja", + BitcoinNetwork.Testnet ), }, ], @@ -848,7 +849,8 @@ export const findWalletForRedemptionData: { outputIndex: 0, value: BigNumber.from("3370000"), // 0.0337 BTC scriptPubKey: createOutputScriptFromAddress( - "tb1qx2xejtjltdcau5dpks8ucszkhxdg3fj88404lh" + "tb1qx2xejtjltdcau5dpks8ucszkhxdg3fj88404lh", + BitcoinNetwork.Testnet ), }, ], From ba404322d840dd0935d0a9ba0548aa3e35bd580d Mon Sep 17 00:00:00 2001 From: Tomasz Slabon Date: Wed, 4 Oct 2023 12:44:31 +0200 Subject: [PATCH 04/12] Updated transaction decomposition --- typescript/src/bitcoin.ts | 49 ++++++++++++++++++++++----------------- 1 file changed, 28 insertions(+), 21 deletions(-) diff --git a/typescript/src/bitcoin.ts b/typescript/src/bitcoin.ts index 33df0dd4b..6f7e3c074 100644 --- a/typescript/src/bitcoin.ts +++ b/typescript/src/bitcoin.ts @@ -1,9 +1,9 @@ -import bcoin, { TX } from "bcoin" +import bcoin from "bcoin" import bufio from "bufio" import { BigNumber, utils } from "ethers" import { Hex } from "./hex" import { BitcoinNetwork, toBitcoinJsLibNetwork } from "./bitcoin-network" -import { address, payments } from "bitcoinjs-lib" +import { Transaction as Tx, address, payments } from "bitcoinjs-lib" /** * Represents a transaction hash (or transaction ID) as an un-prefixed hex @@ -407,46 +407,53 @@ export interface Client { export function decomposeRawTransaction( rawTransaction: RawTransaction ): DecomposedRawTransaction { - const toHex = (bufferWriter: any) => { + const toHex = (bufferWriter: any): string => { return bufferWriter.render().toString("hex") } - const vectorToRaw = (elements: any[]) => { + const getTxInputVector = (tx: Tx): string => { const buffer = bufio.write() - buffer.writeVarint(elements.length) - for (const element of elements) { - element.toWriter(buffer) - } + buffer.writeVarint(tx.ins.length) + tx.ins.forEach((input) => { + buffer.writeHash(input.hash) + buffer.writeU32(input.index) + buffer.writeVarBytes(input.script) + buffer.writeU32(input.sequence) + }) return toHex(buffer) } - const getTxInputVector = (tx: any) => { - return vectorToRaw(tx.inputs) - } - - const getTxOutputVector = (tx: any) => { - return vectorToRaw(tx.outputs) + const getTxOutputVector = (tx: Tx): string => { + const buffer = bufio.write() + buffer.writeVarint(tx.outs.length) + tx.outs.forEach((output) => { + buffer.writeI64(output.value) + buffer.writeVarBytes(output.script) + }) + return toHex(buffer) } - const getTxVersion = (tx: any) => { + const getTxVersion = (tx: Tx): string => { const buffer = bufio.write() buffer.writeU32(tx.version) return toHex(buffer) } - const getTxLocktime = (tx: any) => { + const getTxLocktime = (tx: Tx): string => { const buffer = bufio.write() buffer.writeU32(tx.locktime) return toHex(buffer) } - const tx = TX.fromRaw(Buffer.from(rawTransaction.transactionHex, "hex"), null) + const transaction = Tx.fromBuffer( + Buffer.from(rawTransaction.transactionHex, "hex") + ) return { - version: getTxVersion(tx), - inputs: getTxInputVector(tx), - outputs: getTxOutputVector(tx), - locktime: getTxLocktime(tx), + version: getTxVersion(transaction), + inputs: getTxInputVector(transaction), + outputs: getTxOutputVector(transaction), + locktime: getTxLocktime(transaction), } } From ddfc0f5de0de7bc5ec273b114e99a0d7cc2d5861 Mon Sep 17 00:00:00 2001 From: Tomasz Slabon Date: Wed, 4 Oct 2023 13:38:44 +0200 Subject: [PATCH 05/12] Used bitcoinjs-lib for decodeBitcoinAddress --- typescript/src/bitcoin.ts | 31 +++++++---- typescript/test/bitcoin.test.ts | 96 ++++++++++++++++++++++++--------- 2 files changed, 91 insertions(+), 36 deletions(-) diff --git a/typescript/src/bitcoin.ts b/typescript/src/bitcoin.ts index 6f7e3c074..3649327a6 100644 --- a/typescript/src/bitcoin.ts +++ b/typescript/src/bitcoin.ts @@ -1,4 +1,3 @@ -import bcoin from "bcoin" import bufio from "bufio" import { BigNumber, utils } from "ethers" import { Hex } from "./hex" @@ -574,20 +573,30 @@ export function encodeToBitcoinAddress( /** * Decodes P2PKH or P2WPKH address into a public key hash. Throws if the * provided address is not PKH-based. - * @param address - P2PKH or P2WPKH address that will be decoded. + * @param bitcoinAddress - P2PKH or P2WPKH address that will be decoded. + * @param bitcoinNetwork - Bitcoin network. * @returns Public key hash decoded from the address. This will be an unprefixed * hex string (without 0x prefix). */ -export function decodeBitcoinAddress(address: string): string { - const addressObject = new bcoin.Address(address) +export function decodeBitcoinAddress( + bitcoinAddress: string, + bitcoinNetwork: BitcoinNetwork +): string { + const network = toBitcoinJsLibNetwork(bitcoinNetwork) - const isPKH = - addressObject.isPubkeyhash() || addressObject.isWitnessPubkeyhash() - if (!isPKH) { - throw new Error("Address must be P2PKH or P2WPKH") - } + try { + // Try extracting hash from P2PKH address. + const hash = payments.p2pkh({ address: bitcoinAddress, network }).hash! + return hash.toString("hex") + } catch (err) {} + + try { + // Try extracting hash from P2WPKH address. + const hash = payments.p2wpkh({ address: bitcoinAddress, network }).hash! + return hash.toString("hex") + } catch (err) {} - return addressObject.getHash("hex") + throw new Error("Address must be P2PKH or P2WPKH valid for given network") } /** @@ -625,7 +634,7 @@ export function locktimeToNumber(locktimeLE: Buffer | string): number { */ export function createOutputScriptFromAddress( bitcoinAddress: string, - bitcoinNetwork: BitcoinNetwork = BitcoinNetwork.Mainnet + bitcoinNetwork: BitcoinNetwork ): Hex { return Hex.from( address.toOutputScript( diff --git a/typescript/test/bitcoin.test.ts b/typescript/test/bitcoin.test.ts index 65fa8198f..a6ea1c1ce 100644 --- a/typescript/test/bitcoin.test.ts +++ b/typescript/test/bitcoin.test.ts @@ -259,21 +259,21 @@ describe("Bitcoin", () => { }) }) - describe("decodeAddress", () => { + describe("decodeBitcoinAddress", () => { context("when network is mainnet", () => { context("when proper P2WPKH address is provided", () => { it("should decode P2WPKH adress correctly", () => { - expect(decodeBitcoinAddress(P2WPKHAddress)).to.be.equal( - publicKeyHash - ) + expect( + decodeBitcoinAddress(P2WPKHAddress, BitcoinNetwork.Mainnet) + ).to.be.equal(publicKeyHash) }) }) context("when proper P2PKH address is provided", () => { it("should decode P2PKH address correctly", () => { - expect(decodeBitcoinAddress(P2PKHAddress)).to.be.equal( - publicKeyHash - ) + expect( + decodeBitcoinAddress(P2PKHAddress, BitcoinNetwork.Mainnet) + ).to.be.equal(publicKeyHash) }) }) @@ -281,8 +281,10 @@ describe("Bitcoin", () => { it("should throw", () => { const bitcoinAddress = "123" + P2PKHAddress - expect(() => decodeBitcoinAddress(bitcoinAddress)).to.throw( - "Address is too long" + expect(() => + decodeBitcoinAddress(bitcoinAddress, BitcoinNetwork.Mainnet) + ).to.throw( + "Address must be P2PKH or P2WPKH valid for given network" ) }) }) @@ -290,8 +292,13 @@ describe("Bitcoin", () => { context("when unsupported P2SH address is provided", () => { it("should throw", () => { expect(() => - decodeBitcoinAddress("3EktnHQD7RiAE6uzMj2ZifT9YgRrkSgzQX") - ).to.throw("Address must be P2PKH or P2WPKH") + decodeBitcoinAddress( + "3EktnHQD7RiAE6uzMj2ZifT9YgRrkSgzQX", + BitcoinNetwork.Mainnet + ) + ).to.throw( + "Address must be P2PKH or P2WPKH valid for given network" + ) }) }) @@ -299,9 +306,25 @@ describe("Bitcoin", () => { it("should throw", () => { expect(() => decodeBitcoinAddress( - "bc1qma629cu92skg0t86lftyaf9uflzwhp7jk63h6mpmv3ezh6puvdhsdxuv4m" + "bc1qma629cu92skg0t86lftyaf9uflzwhp7jk63h6mpmv3ezh6puvdhsdxuv4m", + BitcoinNetwork.Mainnet ) - ).to.throw("Address must be P2PKH or P2WPKH") + ).to.throw( + "Address must be P2PKH or P2WPKH valid for given network" + ) + }) + }) + + context("when address from testnet network is provided", () => { + it("should throw", () => { + expect(() => + decodeBitcoinAddress( + "mkpoZkRvtd3SDGWgUDuXK1aEXZfHRM2gKw", + BitcoinNetwork.Mainnet + ) + ).to.throw( + "Address must be P2PKH or P2WPKH valid for given network" + ) }) }) }) @@ -309,17 +332,17 @@ describe("Bitcoin", () => { context("when network is testnet", () => { context("when proper P2WPKH address is provided", () => { it("should decode P2WPKH adress correctly", () => { - expect(decodeBitcoinAddress(P2WPKHAddressTestnet)).to.be.equal( - publicKeyHash - ) + expect( + decodeBitcoinAddress(P2WPKHAddressTestnet, BitcoinNetwork.Testnet) + ).to.be.equal(publicKeyHash) }) }) context("when proper P2PKH address is provided", () => { it("should decode P2PKH address correctly", () => { - expect(decodeBitcoinAddress(P2PKHAddressTestnet)).to.be.equal( - publicKeyHash - ) + expect( + decodeBitcoinAddress(P2PKHAddressTestnet, BitcoinNetwork.Testnet) + ).to.be.equal(publicKeyHash) }) }) @@ -327,8 +350,10 @@ describe("Bitcoin", () => { it("should throw", () => { const bitcoinAddress = "123" + P2PKHAddressTestnet - expect(() => decodeBitcoinAddress(bitcoinAddress)).to.throw( - "Address is too long" + expect(() => + decodeBitcoinAddress(bitcoinAddress, BitcoinNetwork.Testnet) + ).to.throw( + "Address must be P2PKH or P2WPKH valid for given network" ) }) }) @@ -336,8 +361,13 @@ describe("Bitcoin", () => { context("when unsupported P2SH address is provided", () => { it("should throw", () => { expect(() => - decodeBitcoinAddress("2MyxShnGQ5NifGb8CHYrtmzosRySxZ9pZo5") - ).to.throw("Address must be P2PKH or P2WPKH") + decodeBitcoinAddress( + "2MyxShnGQ5NifGb8CHYrtmzosRySxZ9pZo5", + BitcoinNetwork.Testnet + ) + ).to.throw( + "Address must be P2PKH or P2WPKH valid for given network" + ) }) }) @@ -345,9 +375,25 @@ describe("Bitcoin", () => { it("should throw", () => { expect(() => decodeBitcoinAddress( - "tb1qma629cu92skg0t86lftyaf9uflzwhp7jk63h6mpmv3ezh6puvdhs6w2r05" + "tb1qma629cu92skg0t86lftyaf9uflzwhp7jk63h6mpmv3ezh6puvdhs6w2r05", + BitcoinNetwork.Testnet ) - ).to.throw("Address must be P2PKH or P2WPKH") + ).to.throw( + "Address must be P2PKH or P2WPKH valid for given network" + ) + }) + }) + + context("when address from mainnet network is provided", () => { + it("should throw", () => { + expect(() => + decodeBitcoinAddress( + "bc1q8gudgnt2pjxshwzwqgevccet0eyvwtswt03nuy", + BitcoinNetwork.Testnet + ) + ).to.throw( + "Address must be P2PKH or P2WPKH valid for given network" + ) }) }) }) From 3dcdbf98c988171ab2489d9400a2bad59845b201 Mon Sep 17 00:00:00 2001 From: Tomasz Slabon Date: Wed, 4 Oct 2023 14:34:03 +0200 Subject: [PATCH 06/12] Used bitcoinjs-lib for electrum functionalities --- typescript/src/electrum.ts | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/typescript/src/electrum.ts b/typescript/src/electrum.ts index ba6380576..de21961ff 100644 --- a/typescript/src/electrum.ts +++ b/typescript/src/electrum.ts @@ -1,7 +1,8 @@ -import bcoin from "bcoin" +import { Transaction as Tx, TxInput, TxOutput } from "bitcoinjs-lib" import pTimeout from "p-timeout" import { Client as BitcoinClient, + computeSha256, createOutputScriptFromAddress, RawTransaction, Transaction, @@ -337,26 +338,26 @@ export class Client implements BitcoinClient { } // Decode the raw transaction. - const transaction = bcoin.TX.fromRaw(rawTransaction, "hex") + const transaction = Tx.fromHex(rawTransaction) - const inputs = transaction.inputs.map( - (input: any): TransactionInput => ({ - transactionHash: TransactionHash.from(input.prevout.hash).reverse(), - outputIndex: input.prevout.index, - scriptSig: Hex.from(input.script.toRaw()), + const inputs = transaction.ins.map( + (input: TxInput): TransactionInput => ({ + transactionHash: TransactionHash.from(input.hash).reverse(), + outputIndex: input.index, + scriptSig: Hex.from(input.script), }) ) - const outputs = transaction.outputs.map( - (output: any, i: number): TransactionOutput => ({ + const outputs = transaction.outs.map( + (output: TxOutput, i: number): TransactionOutput => ({ outputIndex: i, value: BigNumber.from(output.value), - scriptPubKey: Hex.from(output.script.toRaw()), + scriptPubKey: Hex.from(output.script), }) ) return { - transactionHash: TransactionHash.from(transaction.hash()).reverse(), + transactionHash: TransactionHash.from(transaction.getId()), inputs: inputs, outputs: outputs, } @@ -407,7 +408,7 @@ export class Client implements BitcoinClient { ) // Decode the raw transaction. - const transaction = bcoin.TX.fromRaw(rawTransaction, "hex") + const transaction = Tx.fromHex(rawTransaction) // As a workaround for the problem described in https://github.com/Blockstream/electrs/pull/36 // we need to calculate the number of confirmations based on the latest @@ -425,8 +426,10 @@ export class Client implements BitcoinClient { // If a transaction is unconfirmed (is still in the mempool) the height will // have a value of `0` or `-1`. let txBlockHeight: number = Math.min() - for (const output of transaction.outputs) { - const scriptHash: Buffer = output.script.sha256() + for (const output of transaction.outs) { + const scriptHash: Buffer = computeSha256( + Hex.from(output.script) + ).toBuffer() type HistoryEntry = { // eslint-disable-next-line camelcase From 23de326ed1e932c64c04b54dbf9d3bcc3f476c48 Mon Sep 17 00:00:00 2001 From: Tomasz Slabon Date: Thu, 5 Oct 2023 12:15:53 +0200 Subject: [PATCH 07/12] Replaced bcoin with custom function for unit tests --- typescript/src/bitcoin.ts | 46 +++++++++++ typescript/test/data/deposit.ts | 12 --- typescript/test/deposit-sweep.test.ts | 110 +++++++++----------------- typescript/test/deposit.test.ts | 28 +++---- typescript/test/redemption.test.ts | 88 ++++++++++----------- 5 files changed, 140 insertions(+), 144 deletions(-) diff --git a/typescript/src/bitcoin.ts b/typescript/src/bitcoin.ts index 3649327a6..3745050da 100644 --- a/typescript/src/bitcoin.ts +++ b/typescript/src/bitcoin.ts @@ -780,3 +780,49 @@ export function isP2WSHScript(script: Buffer): boolean { return false } } + +interface TxJSON { + hash: string + version: number + locktime: number + inputs: { + hash: string + index: number + sequence: number + script: string + witness: string[] + }[] + outputs: { + value: number + script: string + address: string + }[] +} + +export function txToJSON( + rawTransaction: string, + bitcoinNetwork: BitcoinNetwork +): TxJSON { + const transaction = Tx.fromHex(rawTransaction) + const network = toBitcoinJsLibNetwork(bitcoinNetwork) + + const txJSON: TxJSON = { + hash: transaction.getId(), + version: transaction.version, + locktime: transaction.locktime, + inputs: transaction.ins.map((input) => ({ + hash: Hex.from(input.hash).reverse().toString(), + index: input.index, + sequence: input.sequence, + script: input.script.toString("hex"), + witness: input.witness.map((w) => w.toString("hex")), + })), + outputs: transaction.outs.map((output) => ({ + value: output.value, + script: output.script.toString("hex"), + address: address.fromOutputScript(output.script, network), + })), + } + + return txJSON +} diff --git a/typescript/test/data/deposit.ts b/typescript/test/data/deposit.ts index 425c87204..61a9e7b46 100644 --- a/typescript/test/data/deposit.ts +++ b/typescript/test/data/deposit.ts @@ -60,15 +60,3 @@ export const testnetWalletPrivateKey = * Address corresponding to testnetWalletPrivateKey. */ export const testnetWalletAddress = "tb1q3k6sadfqv04fmx9naty3fzdfpaecnphkfm3cf3" - -/** - * Address generated from deposit script hash during deposit creation - */ -export const testnetDepositScripthashAddress = - "2Mxy76sc1qAxiJ1fXMXDXqHvVcPLh6Lf12C" - -/** - * Address generated from deposit witness script hash during deposit creation - */ -export const testnetDepositWitnessScripthashAddress = - "tb1qs63s8nwjut4tr5t8nudgzwp4m3dpkefjzpmumn90pruce0cye2tq2jkq0y" diff --git a/typescript/test/deposit-sweep.test.ts b/typescript/test/deposit-sweep.test.ts index be41c9a81..103d86607 100644 --- a/typescript/test/deposit-sweep.test.ts +++ b/typescript/test/deposit-sweep.test.ts @@ -4,13 +4,9 @@ import { TransactionHash, UnspentTransactionOutput, Transaction, + txToJSON, } from "../src/bitcoin" -import { - testnetDepositScripthashAddress, - testnetDepositWitnessScripthashAddress, - testnetWalletAddress, - testnetWalletPrivateKey, -} from "./data/deposit" +import { testnetWalletAddress, testnetWalletPrivateKey } from "./data/deposit" import { depositSweepWithWitnessMainUtxoAndWitnessOutput, depositSweepWithNoMainUtxoAndWitnessOutput, @@ -21,7 +17,6 @@ import { } from "./data/deposit-sweep" import { MockBitcoinClient } from "./utils/mock-bitcoin-client" import { MockBridge } from "./utils/mock-bridge" -import bcoin from "bcoin" import * as chai from "chai" import chaiAsPromised from "chai-as-promised" chai.use(chaiAsPromised) @@ -394,8 +389,10 @@ describe("Sweep", () => { ) // Convert raw transaction to JSON to make detailed comparison. - const buffer = Buffer.from(transaction.transactionHex, "hex") - const txJSON = bcoin.TX.fromRaw(buffer).getJSON("testnet") + const txJSON = txToJSON( + transaction.transactionHex, + BitcoinNetwork.Testnet + ) expect(txJSON.hash).to.be.equal( depositSweepWithNoMainUtxoAndWitnessOutput.expectedSweep.transactionHash.toString() @@ -406,26 +403,23 @@ describe("Sweep", () => { expect(txJSON.inputs.length).to.be.equal(2) const p2shInput = txJSON.inputs[0] - expect(p2shInput.prevout.hash).to.be.equal( + expect(p2shInput.hash).to.be.equal( depositSweepWithNoMainUtxoAndWitnessOutput.deposits[0].utxo.transactionHash.toString() ) - expect(p2shInput.prevout.index).to.be.equal( + expect(p2shInput.index).to.be.equal( depositSweepWithNoMainUtxoAndWitnessOutput.deposits[0].utxo .outputIndex ) // Transaction should be signed. As it's not SegWit input, the `witness` // field should be empty, while the `script` field should be filled. - expect(p2shInput.witness).to.be.equal("00") + expect(p2shInput.witness).to.be.empty expect(p2shInput.script.length).to.be.greaterThan(0) - // Input's address should be set to the address generated from deposit - // script hash - expect(p2shInput.address).to.be.equal(testnetDepositScripthashAddress) const p2wshInput = txJSON.inputs[1] - expect(p2wshInput.prevout.hash).to.be.equal( + expect(p2wshInput.hash).to.be.equal( depositSweepWithNoMainUtxoAndWitnessOutput.deposits[1].utxo.transactionHash.toString() ) - expect(p2wshInput.prevout.index).to.be.equal( + expect(p2wshInput.index).to.be.equal( depositSweepWithNoMainUtxoAndWitnessOutput.deposits[1].utxo .outputIndex ) @@ -433,11 +427,6 @@ describe("Sweep", () => { // field should be filled, while the `script` field should be empty. expect(p2wshInput.witness.length).to.be.greaterThan(0) expect(p2wshInput.script.length).to.be.equal(0) - // Input's address should be set to the address generated from deposit - // witness script hash - expect(p2wshInput.address).to.be.equal( - testnetDepositWitnessScripthashAddress - ) // Validate outputs. expect(txJSON.outputs.length).to.be.equal(1) @@ -526,8 +515,10 @@ describe("Sweep", () => { ) // Convert raw transaction to JSON to make detailed comparison. - const buffer = Buffer.from(transaction.transactionHex, "hex") - const txJSON = bcoin.TX.fromRaw(buffer).getJSON("testnet") + const txJSON = txToJSON( + transaction.transactionHex, + BitcoinNetwork.Testnet + ) expect(txJSON.hash).to.be.equal( depositSweepWithWitnessMainUtxoAndWitnessOutput.expectedSweep.transactionHash.toString() @@ -538,10 +529,10 @@ describe("Sweep", () => { expect(txJSON.inputs.length).to.be.equal(3) const p2wkhInput = txJSON.inputs[0] - expect(p2wkhInput.prevout.hash).to.be.equal( + expect(p2wkhInput.hash).to.be.equal( depositSweepWithWitnessMainUtxoAndWitnessOutput.mainUtxo.transactionHash.toString() ) - expect(p2wkhInput.prevout.index).to.be.equal( + expect(p2wkhInput.index).to.be.equal( depositSweepWithWitnessMainUtxoAndWitnessOutput.mainUtxo .outputIndex ) @@ -549,33 +540,25 @@ describe("Sweep", () => { // field should be filled, while the `script` field should be empty. expect(p2wkhInput.witness.length).to.be.greaterThan(0) expect(p2wkhInput.script.length).to.be.equal(0) - // The input comes from the main UTXO so the input should be the - // wallet's address - expect(p2wkhInput.address).to.be.equal(testnetWalletAddress) const p2shInput = txJSON.inputs[1] - expect(p2shInput.prevout.hash).to.be.equal( + expect(p2shInput.hash).to.be.equal( depositSweepWithWitnessMainUtxoAndWitnessOutput.deposits[0].utxo.transactionHash.toString() ) - expect(p2shInput.prevout.index).to.be.equal( + expect(p2shInput.index).to.be.equal( depositSweepWithWitnessMainUtxoAndWitnessOutput.deposits[0].utxo .outputIndex ) // Transaction should be signed. As it's not SegWit input, the `witness` // field should be empty, while the `script` field should be filled. - expect(p2shInput.witness).to.be.equal("00") + expect(p2shInput.witness).to.be.empty expect(p2shInput.script.length).to.be.greaterThan(0) - // Input's address should be set to the address generated from deposit - // script hash - expect(p2shInput.address).to.be.equal( - testnetDepositScripthashAddress - ) const p2wshInput = txJSON.inputs[2] - expect(p2wshInput.prevout.hash).to.be.equal( + expect(p2wshInput.hash).to.be.equal( depositSweepWithWitnessMainUtxoAndWitnessOutput.deposits[1].utxo.transactionHash.toString() ) - expect(p2wshInput.prevout.index).to.be.equal( + expect(p2wshInput.index).to.be.equal( depositSweepWithWitnessMainUtxoAndWitnessOutput.deposits[1].utxo .outputIndex ) @@ -583,11 +566,6 @@ describe("Sweep", () => { // field should be filled, while the `script` field should be empty. expect(p2wshInput.witness.length).to.be.greaterThan(0) expect(p2wshInput.script.length).to.be.equal(0) - // Input's address should be set to the address generated from deposit - // witness script hash - expect(p2wshInput.address).to.be.equal( - testnetDepositWitnessScripthashAddress - ) // Validate outputs. expect(txJSON.outputs.length).to.be.equal(1) @@ -677,8 +655,10 @@ describe("Sweep", () => { ) // Convert raw transaction to JSON to make detailed comparison. - const buffer = Buffer.from(transaction.transactionHex, "hex") - const txJSON = bcoin.TX.fromRaw(buffer).getJSON("testnet") + const txJSON = txToJSON( + transaction.transactionHex, + BitcoinNetwork.Testnet + ) expect(txJSON.hash).to.be.equal( depositSweepWithNonWitnessMainUtxoAndWitnessOutput.expectedSweep.transactionHash.toString() @@ -689,28 +669,23 @@ describe("Sweep", () => { expect(txJSON.inputs.length).to.be.equal(2) const p2pkhInput = txJSON.inputs[0] // main UTXO - expect(p2pkhInput.prevout.hash).to.be.equal( + expect(p2pkhInput.hash).to.be.equal( depositSweepWithNonWitnessMainUtxoAndWitnessOutput.mainUtxo.transactionHash.toString() ) - expect(p2pkhInput.prevout.index).to.be.equal( + expect(p2pkhInput.index).to.be.equal( depositSweepWithNonWitnessMainUtxoAndWitnessOutput.mainUtxo .outputIndex ) // Transaction should be signed. As it's not SegWit input, the `witness` // field should be empty, while the `script` field should be filled. - expect(p2pkhInput.witness).to.be.equal("00") + expect(p2pkhInput.witness).to.be.empty expect(p2pkhInput.script.length).to.be.greaterThan(0) - // The input comes from the main UTXO so the input should be the - // wallet's address - expect(p2pkhInput.address).to.be.equal( - "mtSEUCE7G8om9zJttG9twtjoiSsUz7QnY9" - ) const p2wshInput = txJSON.inputs[1] - expect(p2wshInput.prevout.hash).to.be.equal( + expect(p2wshInput.hash).to.be.equal( depositSweepWithNonWitnessMainUtxoAndWitnessOutput.deposits[0].utxo.transactionHash.toString() ) - expect(p2wshInput.prevout.index).to.be.equal( + expect(p2wshInput.index).to.be.equal( depositSweepWithNonWitnessMainUtxoAndWitnessOutput.deposits[0] .utxo.outputIndex ) @@ -718,11 +693,6 @@ describe("Sweep", () => { // field should be filled, while the `script` field should be empty. expect(p2wshInput.witness.length).to.be.greaterThan(0) expect(p2wshInput.script.length).to.be.equal(0) - // Input's address should be set to the address generated from deposit - // script hash - expect(p2wshInput.address).to.be.equal( - "tb1qk8urugnf08wfle6wslmdxq7mkz9z0gw8e6gkvspn7dx87tfpfntshdm7qr" - ) // Validate outputs. expect(txJSON.outputs.length).to.be.equal(1) @@ -810,9 +780,10 @@ describe("Sweep", () => { ) // Convert raw transaction to JSON to make detailed comparison. - const buffer = Buffer.from(transaction.transactionHex, "hex") - const txJSON = bcoin.TX.fromRaw(buffer).getJSON("testnet") - + const txJSON = txToJSON( + transaction.transactionHex, + BitcoinNetwork.Testnet + ) expect(txJSON.hash).to.be.equal( depositSweepWithNoMainUtxoAndNonWitnessOutput.expectedSweep.transactionHash.toString() ) @@ -822,22 +793,17 @@ describe("Sweep", () => { expect(txJSON.inputs.length).to.be.equal(1) const p2shInput = txJSON.inputs[0] - expect(p2shInput.prevout.hash).to.be.equal( + expect(p2shInput.hash).to.be.equal( depositSweepWithNoMainUtxoAndNonWitnessOutput.deposits[0].utxo.transactionHash.toString() ) - expect(p2shInput.prevout.index).to.be.equal( + expect(p2shInput.index).to.be.equal( depositSweepWithNoMainUtxoAndNonWitnessOutput.deposits[0].utxo .outputIndex ) // Transaction should be signed. As it's not SegWit input, the `witness` // field should be empty, while the `script` field should be filled. - expect(p2shInput.witness).to.be.equal("00") + expect(p2shInput.witness).to.be.empty expect(p2shInput.script.length).to.be.greaterThan(0) - // Input's address should be set to the address generated from deposit - // script hash - expect(p2shInput.address).to.be.equal( - "2N8iF1pRndihBzgLDna9MfRhmqktwTdHejA" - ) // Validate outputs. expect(txJSON.outputs.length).to.be.equal(1) diff --git a/typescript/test/deposit.test.ts b/typescript/test/deposit.test.ts index 27bb5eadd..79f098230 100644 --- a/typescript/test/deposit.test.ts +++ b/typescript/test/deposit.test.ts @@ -12,9 +12,9 @@ import { RawTransaction, TransactionHash, UnspentTransactionOutput, + txToJSON, } from "../src/bitcoin" import { MockBitcoinClient } from "./utils/mock-bitcoin-client" -import bcoin from "bcoin" import { assembleDepositScript, assembleDepositTransaction, @@ -342,8 +342,10 @@ describe("Deposit", () => { expect(transaction).to.be.eql(expectedP2WSHDeposit.transaction) // Convert raw transaction to JSON to make detailed comparison. - const buffer = Buffer.from(transaction.transactionHex, "hex") - const txJSON = bcoin.TX.fromRaw(buffer).getJSON("testnet") + const txJSON = txToJSON( + transaction.transactionHex, + BitcoinNetwork.Testnet + ) expect(txJSON.hash).to.be.equal( expectedP2WSHDeposit.transactionHash.toString() @@ -355,15 +357,12 @@ describe("Deposit", () => { const input = txJSON.inputs[0] - expect(input.prevout.hash).to.be.equal( - testnetUTXO.transactionHash.toString() - ) - expect(input.prevout.index).to.be.equal(testnetUTXO.outputIndex) + expect(input.hash).to.be.equal(testnetUTXO.transactionHash.toString()) + expect(input.index).to.be.equal(testnetUTXO.outputIndex) // Transaction should be signed but this is SegWit input so the `script` // field should be empty and the `witness` field should be filled instead. expect(input.script.length).to.be.equal(0) expect(input.witness.length).to.be.greaterThan(0) - expect(input.address).to.be.equal(testnetAddress) // Validate outputs. expect(txJSON.outputs.length).to.be.equal(2) @@ -442,8 +441,10 @@ describe("Deposit", () => { expect(transaction).to.be.eql(expectedP2SHDeposit.transaction) // Convert raw transaction to JSON to make detailed comparison. - const buffer = Buffer.from(transaction.transactionHex, "hex") - const txJSON = bcoin.TX.fromRaw(buffer).getJSON("testnet") + const txJSON = txToJSON( + transaction.transactionHex, + BitcoinNetwork.Testnet + ) expect(txJSON.hash).to.be.equal( expectedP2SHDeposit.transactionHash.toString() @@ -455,15 +456,12 @@ describe("Deposit", () => { const input = txJSON.inputs[0] - expect(input.prevout.hash).to.be.equal( - testnetUTXO.transactionHash.toString() - ) - expect(input.prevout.index).to.be.equal(testnetUTXO.outputIndex) + expect(input.hash).to.be.equal(testnetUTXO.transactionHash.toString()) + expect(input.index).to.be.equal(testnetUTXO.outputIndex) // Transaction should be signed but this is SegWit input so the `script` // field should be empty and the `witness` field should be filled instead. expect(input.script.length).to.be.equal(0) expect(input.witness.length).to.be.greaterThan(0) - expect(input.address).to.be.equal(testnetAddress) // Validate outputs. expect(txJSON.outputs.length).to.be.equal(2) diff --git a/typescript/test/redemption.test.ts b/typescript/test/redemption.test.ts index 874d0fd26..acbafa2bc 100644 --- a/typescript/test/redemption.test.ts +++ b/typescript/test/redemption.test.ts @@ -3,8 +3,8 @@ import { RawTransaction, TransactionHash, UnspentTransactionOutput, + txToJSON, } from "../src/bitcoin" -import bcoin from "bcoin" import { MockBitcoinClient } from "./utils/mock-bitcoin-client" import { walletPrivateKey, @@ -510,8 +510,10 @@ describe("Redemption", () => { ) // Convert raw transaction to JSON to make detailed comparison. - const buffer = Buffer.from(transaction.transactionHex, "hex") - const txJSON = bcoin.TX.fromRaw(buffer).getJSON("testnet") + const txJSON = txToJSON( + transaction.transactionHex, + BitcoinNetwork.Testnet + ) expect(txJSON.hash).to.be.equal( data.expectedRedemption.transactionHash.toString() @@ -523,17 +525,14 @@ describe("Redemption", () => { const input = txJSON.inputs[0] - expect(input.prevout.hash).to.be.equal( + expect(input.hash).to.be.equal( data.mainUtxo.transactionHash.toString() ) - expect(input.prevout.index).to.be.equal( - data.mainUtxo.outputIndex - ) + expect(input.index).to.be.equal(data.mainUtxo.outputIndex) // Transaction should be signed but this is SegWit input so the `script` // field should be empty and the `witness` field should be filled instead. expect(input.script.length).to.be.equal(0) expect(input.witness.length).to.be.greaterThan(0) - expect(input.address).to.be.equal(p2wpkhWalletAddress) // Validate outputs. expect(txJSON.outputs.length).to.be.equal(2) @@ -624,8 +623,10 @@ describe("Redemption", () => { ) // Convert raw transaction to JSON to make detailed comparison. - const buffer = Buffer.from(transaction.transactionHex, "hex") - const txJSON = bcoin.TX.fromRaw(buffer).getJSON("testnet") + const txJSON = txToJSON( + transaction.transactionHex, + BitcoinNetwork.Testnet + ) expect(txJSON.hash).to.be.equal( data.expectedRedemption.transactionHash.toString() @@ -637,17 +638,14 @@ describe("Redemption", () => { const input = txJSON.inputs[0] - expect(input.prevout.hash).to.be.equal( + expect(input.hash).to.be.equal( data.mainUtxo.transactionHash.toString() ) - expect(input.prevout.index).to.be.equal( - data.mainUtxo.outputIndex - ) + expect(input.index).to.be.equal(data.mainUtxo.outputIndex) // Transaction should be signed but this is SegWit input so the `script` // field should be empty and the `witness` field should be filled instead. expect(input.script.length).to.be.equal(0) expect(input.witness.length).to.be.greaterThan(0) - expect(input.address).to.be.equal(p2wpkhWalletAddress) // Validate outputs. expect(txJSON.outputs.length).to.be.equal(2) @@ -737,8 +735,10 @@ describe("Redemption", () => { ) // Convert raw transaction to JSON to make detailed comparison. - const buffer = Buffer.from(transaction.transactionHex, "hex") - const txJSON = bcoin.TX.fromRaw(buffer).getJSON("testnet") + const txJSON = txToJSON( + transaction.transactionHex, + BitcoinNetwork.Testnet + ) expect(txJSON.hash).to.be.equal( data.expectedRedemption.transactionHash.toString() @@ -750,17 +750,14 @@ describe("Redemption", () => { const input = txJSON.inputs[0] - expect(input.prevout.hash).to.be.equal( + expect(input.hash).to.be.equal( data.mainUtxo.transactionHash.toString() ) - expect(input.prevout.index).to.be.equal( - data.mainUtxo.outputIndex - ) + expect(input.index).to.be.equal(data.mainUtxo.outputIndex) // Transaction should be signed but this is SegWit input so the `script` // field should be empty and the `witness` field should be filled instead. expect(input.script.length).to.be.equal(0) expect(input.witness.length).to.be.greaterThan(0) - expect(input.address).to.be.equal(p2wpkhWalletAddress) // Validate outputs. expect(txJSON.outputs.length).to.be.equal(2) @@ -850,8 +847,10 @@ describe("Redemption", () => { ) // Convert raw transaction to JSON to make detailed comparison. - const buffer = Buffer.from(transaction.transactionHex, "hex") - const txJSON = bcoin.TX.fromRaw(buffer).getJSON("testnet") + const txJSON = txToJSON( + transaction.transactionHex, + BitcoinNetwork.Testnet + ) expect(txJSON.hash).to.be.equal( data.expectedRedemption.transactionHash.toString() @@ -863,17 +862,14 @@ describe("Redemption", () => { const input = txJSON.inputs[0] - expect(input.prevout.hash).to.be.equal( + expect(input.hash).to.be.equal( data.mainUtxo.transactionHash.toString() ) - expect(input.prevout.index).to.be.equal( - data.mainUtxo.outputIndex - ) + expect(input.index).to.be.equal(data.mainUtxo.outputIndex) // Transaction should be signed but this is SegWit input so the `script` // field should be empty and the `witness` field should be filled instead. expect(input.script.length).to.be.equal(0) expect(input.witness.length).to.be.greaterThan(0) - expect(input.address).to.be.equal(p2wpkhWalletAddress) // Validate outputs. expect(txJSON.outputs.length).to.be.equal(2) @@ -960,8 +956,10 @@ describe("Redemption", () => { expect(transaction).to.be.eql(data.expectedRedemption.transaction) // Convert raw transaction to JSON to make detailed comparison. - const buffer = Buffer.from(transaction.transactionHex, "hex") - const txJSON = bcoin.TX.fromRaw(buffer).getJSON("testnet") + const txJSON = txToJSON( + transaction.transactionHex, + BitcoinNetwork.Testnet + ) expect(txJSON.hash).to.be.equal( data.expectedRedemption.transactionHash.toString() @@ -973,15 +971,14 @@ describe("Redemption", () => { const input = txJSON.inputs[0] - expect(input.prevout.hash).to.be.equal( + expect(input.hash).to.be.equal( data.mainUtxo.transactionHash.toString() ) - expect(input.prevout.index).to.be.equal(data.mainUtxo.outputIndex) + expect(input.index).to.be.equal(data.mainUtxo.outputIndex) // Transaction should be signed but this is SegWit input so the `script` // field should be empty and the `witness` field should be filled instead. expect(input.script.length).to.be.equal(0) expect(input.witness.length).to.be.greaterThan(0) - expect(input.address).to.be.equal(p2wpkhWalletAddress) // Validate outputs. expect(txJSON.outputs.length).to.be.equal(5) @@ -1118,8 +1115,10 @@ describe("Redemption", () => { expect(transaction).to.be.eql(data.expectedRedemption.transaction) // Convert raw transaction to JSON to make detailed comparison. - const buffer = Buffer.from(transaction.transactionHex, "hex") - const txJSON = bcoin.TX.fromRaw(buffer).getJSON("testnet") + const txJSON = txToJSON( + transaction.transactionHex, + BitcoinNetwork.Testnet + ) expect(txJSON.hash).to.be.equal( data.expectedRedemption.transactionHash.toString() @@ -1131,15 +1130,14 @@ describe("Redemption", () => { const input = txJSON.inputs[0] - expect(input.prevout.hash).to.be.equal( + expect(input.hash).to.be.equal( data.mainUtxo.transactionHash.toString() ) - expect(input.prevout.index).to.be.equal(data.mainUtxo.outputIndex) + expect(input.index).to.be.equal(data.mainUtxo.outputIndex) // Transaction should be signed but this is SegWit input so the `script` // field should be empty and the `witness` field should be filled instead. expect(input.script.length).to.be.equal(0) expect(input.witness.length).to.be.greaterThan(0) - expect(input.address).to.be.equal(p2wpkhWalletAddress) // Validate outputs. expect(txJSON.outputs.length).to.be.equal(2) @@ -1225,9 +1223,10 @@ describe("Redemption", () => { expect(transaction).to.be.eql(data.expectedRedemption.transaction) // Convert raw transaction to JSON to make detailed comparison. - const buffer = Buffer.from(transaction.transactionHex, "hex") - const txJSON = bcoin.TX.fromRaw(buffer).getJSON("testnet") - + const txJSON = txToJSON( + transaction.transactionHex, + BitcoinNetwork.Testnet + ) expect(txJSON.hash).to.be.equal( data.expectedRedemption.transactionHash.toString() ) @@ -1238,15 +1237,14 @@ describe("Redemption", () => { const input = txJSON.inputs[0] - expect(input.prevout.hash).to.be.equal( + expect(input.hash).to.be.equal( data.mainUtxo.transactionHash.toString() ) - expect(input.prevout.index).to.be.equal(data.mainUtxo.outputIndex) + expect(input.index).to.be.equal(data.mainUtxo.outputIndex) // Transaction should be signed but this is SegWit input so the `script` // field should be empty and the `witness` field should be filled instead. expect(input.script.length).to.be.equal(0) expect(input.witness.length).to.be.greaterThan(0) - expect(input.address).to.be.equal(p2wpkhWalletAddress) // Validate outputs. expect(txJSON.outputs.length).to.be.equal(2) From 5be11800ef6f440a08974a86eb101f8860113455 Mon Sep 17 00:00:00 2001 From: Tomasz Slabon Date: Thu, 5 Oct 2023 12:27:19 +0200 Subject: [PATCH 08/12] REmoved bcoin dependency --- typescript/package.json | 7 +- typescript/typings.d.ts | 1 - typescript/yarn.lock | 195 +--------------------------------------- 3 files changed, 2 insertions(+), 201 deletions(-) diff --git a/typescript/package.json b/typescript/package.json index 806339a53..21d9a4fc9 100644 --- a/typescript/package.json +++ b/typescript/package.json @@ -13,8 +13,7 @@ "test": "mocha --exit --recursive 'test/**/*.test.ts'", "typechain": "rm -rf ./typechain && for i in $npm_package_config_contracts; do typechain --target ethers-v5 --out-dir ./typechain $i; done && rm ./typechain/index.ts", "build": "npm run typechain && tsc --project tsconfig.build.json", - "dev": "tsc --project tsconfig.build.json --watch", - "postinstall": "npm rebuild bcrypto" + "dev": "tsc --project tsconfig.build.json --watch" }, "files": [ "dist/", @@ -27,7 +26,6 @@ "dependencies": { "@keep-network/ecdsa": "development", "@keep-network/tbtc-v2": "development", - "bcoin": "git+https://github.com/keep-network/bcoin.git#5accd32c63e6025a0d35d67739c4a6e84095a1f8", "bitcoinjs-lib": "6.0.2", "bufio": "^1.0.6", "ecpair": "^2.1.0", @@ -61,8 +59,5 @@ }, "engines": { "node": ">=14 <15" - }, - "browser": { - "bcoin": "bcoin/lib/bcoin-browser" } } diff --git a/typescript/typings.d.ts b/typescript/typings.d.ts index 2558e6a79..abe66d9f4 100644 --- a/typescript/typings.d.ts +++ b/typescript/typings.d.ts @@ -2,7 +2,6 @@ * Manually declare modules for imported third-party libraries that * don't provide their own typings. */ -declare module "bcoin" declare module "bufio" declare module "electrum-client-js" declare module "wif" diff --git a/typescript/yarn.lock b/typescript/yarn.lock index 05897068f..ba01c6262 100644 --- a/typescript/yarn.lock +++ b/typescript/yarn.lock @@ -2496,45 +2496,6 @@ base64-js@^1.3.1: resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== -"bcfg@git+https://github.com/bcoin-org/bcfg.git#semver:~0.1.7": - version "0.1.7" - resolved "git+https://github.com/bcoin-org/bcfg.git#05122154b35baa82cd01dc9478ebee7346386ba1" - dependencies: - bsert "~0.0.10" - -"bcoin@git+https://github.com/keep-network/bcoin.git#5accd32c63e6025a0d35d67739c4a6e84095a1f8": - version "2.2.0" - resolved "git+https://github.com/keep-network/bcoin.git#5accd32c63e6025a0d35d67739c4a6e84095a1f8" - dependencies: - bcfg "git+https://github.com/bcoin-org/bcfg.git#semver:~0.1.7" - bcrypto "git+https://github.com/bcoin-org/bcrypto.git#semver:~5.5.0" - bcurl "git+https://github.com/bcoin-org/bcurl.git#semver:^0.1.6" - bdb "git+https://github.com/bcoin-org/bdb.git#semver:~1.2.1" - bdns "git+https://github.com/bcoin-org/bdns.git#semver:~0.1.5" - bevent "git+https://github.com/bcoin-org/bevent.git#semver:~0.1.5" - bfile "git+https://github.com/bcoin-org/bfile.git#semver:~0.2.1" - bfilter "git+https://github.com/bcoin-org/bfilter.git#semver:~2.3.0" - bheep "git+https://github.com/bcoin-org/bheep.git#semver:~0.1.5" - binet "git+https://github.com/bcoin-org/binet.git#semver:~0.3.5" - blgr "git+https://github.com/bcoin-org/blgr.git#semver:~0.2.0" - blru "git+https://github.com/bcoin-org/blru.git#semver:~0.1.6" - blst "git+https://github.com/bcoin-org/blst.git#semver:~0.1.5" - bmutex "git+https://github.com/bcoin-org/bmutex.git#semver:~0.1.6" - brq "git+https://github.com/bcoin-org/brq.git#semver:~0.1.7" - bs32 "git+https://github.com/bcoin-org/bs32.git#semver:=0.1.6" - bsert "git+https://github.com/chjj/bsert.git#semver:~0.0.10" - bsock "git+https://github.com/bcoin-org/bsock.git#semver:~0.1.9" - bsocks "git+https://github.com/bcoin-org/bsocks.git#semver:~0.2.6" - btcp "git+https://github.com/bcoin-org/btcp.git#semver:~0.1.5" - buffer-map "git+https://github.com/chjj/buffer-map.git#semver:~0.0.7" - bufio "git+https://github.com/bcoin-org/bufio.git#semver:~1.0.6" - bupnp "git+https://github.com/bcoin-org/bupnp.git#semver:~0.2.6" - bval "git+https://github.com/bcoin-org/bval.git#semver:~0.1.6" - bweb "git+https://github.com/bcoin-org/bweb.git#semver:=0.1.9" - loady "git+https://github.com/chjj/loady.git#semver:~0.0.1" - n64 "git+https://github.com/chjj/n64.git#semver:~0.2.10" - nan "git+https://github.com/braydonf/nan.git#semver:=2.14.0" - bcrypt-pbkdf@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" @@ -2542,34 +2503,6 @@ bcrypt-pbkdf@^1.0.0: dependencies: tweetnacl "^0.14.3" -"bcrypto@git+https://github.com/bcoin-org/bcrypto.git#semver:~5.5.0": - version "5.5.0" - resolved "git+https://github.com/bcoin-org/bcrypto.git#34738cf15033e3bce91a4f6f41ec1ebee3c2fdc8" - dependencies: - bufio "~1.0.7" - loady "~0.0.5" - -"bcurl@git+https://github.com/bcoin-org/bcurl.git#semver:^0.1.6": - version "0.1.10" - resolved "git+https://github.com/bcoin-org/bcurl.git#d7e088fad4c284fb5d6fd7205c6b903bd3e6bf83" - dependencies: - brq "~0.1.8" - bsert "~0.0.10" - bsock "~0.1.9" - -"bdb@git+https://github.com/bcoin-org/bdb.git#semver:~1.2.1": - version "1.2.2" - resolved "git+https://github.com/bcoin-org/bdb.git#2c8d48c8adca4b11260263472766cd4b7ae74ef7" - dependencies: - bsert "~0.0.10" - loady "~0.0.1" - -"bdns@git+https://github.com/bcoin-org/bdns.git#semver:~0.1.5": - version "0.1.5" - resolved "git+https://github.com/bcoin-org/bdns.git#cb0b62a0075f7e1259fc50fa723ba644e9a07d14" - dependencies: - bsert "~0.0.10" - bech32@1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/bech32/-/bech32-1.1.4.tgz#e38c9f37bf179b8eb16ae3a772b40c356d4832e9" @@ -2580,31 +2513,6 @@ bech32@^2.0.0: resolved "https://registry.yarnpkg.com/bech32/-/bech32-2.0.0.tgz#078d3686535075c8c79709f054b1b226a133b355" integrity sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg== -"bevent@git+https://github.com/bcoin-org/bevent.git#semver:~0.1.5": - version "0.1.5" - resolved "git+https://github.com/bcoin-org/bevent.git#60fb503de3ea1292d29ce438bfba80f0bc5ccb60" - dependencies: - bsert "~0.0.10" - -"bfile@git+https://github.com/bcoin-org/bfile.git#semver:~0.2.1": - version "0.2.2" - resolved "git+https://github.com/bcoin-org/bfile.git#c3075133a02830dc384f8353d8275d4499b8bff9" - -"bfilter@git+https://github.com/bcoin-org/bfilter.git#semver:~2.3.0": - version "2.3.0" - resolved "git+https://github.com/bcoin-org/bfilter.git#70e42125f877191d340e8838a1a90fabb750e680" - dependencies: - bcrypto "git+https://github.com/bcoin-org/bcrypto.git#semver:~5.5.0" - bsert "git+https://github.com/chjj/bsert.git#semver:~0.0.10" - bufio "git+https://github.com/bcoin-org/bufio.git#semver:~1.0.6" - loady "git+https://github.com/chjj/loady.git#semver:~0.0.1" - -"bheep@git+https://github.com/bcoin-org/bheep.git#semver:~0.1.5": - version "0.1.5" - resolved "git+https://github.com/bcoin-org/bheep.git#e59329d0a776ae71b2fb7a2876ee5b9fd3030fa2" - dependencies: - bsert "~0.0.10" - big-integer@^1.6.44: version "1.6.51" resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.51.tgz#0df92a5d9880560d3ff2d5fd20245c889d130686" @@ -2637,13 +2545,6 @@ bindings@^1.3.0: dependencies: file-uri-to-path "1.0.0" -"binet@git+https://github.com/bcoin-org/binet.git#semver:~0.3.5", binet@~0.3.5: - version "0.3.6" - resolved "git+https://github.com/bcoin-org/binet.git#d3decfb7a7257abdfb03c3a9c091499b2ebff0e1" - dependencies: - bs32 "~0.1.5" - bsert "~0.0.10" - bip174@^2.0.1: version "2.1.1" resolved "https://registry.yarnpkg.com/bip174/-/bip174-2.1.1.tgz#ef3e968cf76de234a546962bcf572cc150982f9f" @@ -2718,18 +2619,6 @@ blakejs@^1.1.0: resolved "https://registry.yarnpkg.com/blakejs/-/blakejs-1.2.1.tgz#5057e4206eadb4a97f7c0b6e197a505042fc3814" integrity sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ== -"blgr@git+https://github.com/bcoin-org/blgr.git#semver:~0.2.0": - version "0.2.0" - resolved "git+https://github.com/bcoin-org/blgr.git#050cbb587a1654a078468dbb92606330fdc4d120" - dependencies: - bsert "~0.0.10" - -"blru@git+https://github.com/bcoin-org/blru.git#semver:~0.1.6": - version "0.1.6" - resolved "git+https://github.com/bcoin-org/blru.git#c2c093e9475439333dfb87bfb2fdc3be6c98b080" - dependencies: - bsert "~0.0.10" - "bls12377js@https://github.com/celo-org/bls12377js#400bcaeec9e7620b040bfad833268f5289699cac": version "0.1.0" resolved "https://github.com/celo-org/bls12377js#400bcaeec9e7620b040bfad833268f5289699cac" @@ -2754,23 +2643,11 @@ blakejs@^1.1.0: ts-node "^8.4.1" typescript "^3.6.4" -"blst@git+https://github.com/bcoin-org/blst.git#semver:~0.1.5": - version "0.1.5" - resolved "git+https://github.com/bcoin-org/blst.git#d588403edb18e628899e05aeba8c3a98a5cdedff" - dependencies: - bsert "~0.0.10" - bluebird@^3.5.0, bluebird@^3.5.2: version "3.7.2" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== -"bmutex@git+https://github.com/bcoin-org/bmutex.git#semver:~0.1.6": - version "0.1.6" - resolved "git+https://github.com/bcoin-org/bmutex.git#e50782323932a4946ecc05a74c6d45861adc2c25" - dependencies: - bsert "~0.0.10" - bn.js@4.11.6: version "4.11.6" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.6.tgz#53344adb14617a13f6e8dd2ce28905d1c0ba3215" @@ -2893,18 +2770,6 @@ browserify-sign@^4.0.0: readable-stream "^3.6.0" safe-buffer "^5.2.0" -"brq@git+https://github.com/bcoin-org/brq.git#semver:~0.1.7", brq@~0.1.7, brq@~0.1.8: - version "0.1.8" - resolved "git+https://github.com/bcoin-org/brq.git#534bb2c83fb366ba40ad80bc3de796a174503294" - dependencies: - bsert "~0.0.10" - -"bs32@git+https://github.com/bcoin-org/bs32.git#semver:=0.1.6", bs32@~0.1.5: - version "0.1.6" - resolved "git+https://github.com/bcoin-org/bs32.git#21cf9c724659dc15df722d2410548828c142f265" - dependencies: - bsert "~0.0.10" - bs58@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/bs58/-/bs58-4.0.1.tgz#be161e76c354f6f788ae4071f63f34e8c4f0a42a" @@ -2921,27 +2786,6 @@ bs58check@<3.0.0, bs58check@^2.1.1, bs58check@^2.1.2: create-hash "^1.1.0" safe-buffer "^5.1.2" -"bsert@git+https://github.com/chjj/bsert.git#semver:~0.0.10", bsert@~0.0.10: - version "0.0.10" - resolved "git+https://github.com/chjj/bsert.git#bd09d49eab8644bca08ae8259a3d8756e7d453fc" - -"bsock@git+https://github.com/bcoin-org/bsock.git#semver:~0.1.9", bsock@~0.1.8, bsock@~0.1.9: - version "0.1.9" - resolved "git+https://github.com/bcoin-org/bsock.git#7cf76b3021ae7929c023d1170f789811e91ae528" - dependencies: - bsert "~0.0.10" - -"bsocks@git+https://github.com/bcoin-org/bsocks.git#semver:~0.2.6": - version "0.2.6" - resolved "git+https://github.com/bcoin-org/bsocks.git#6a8eb764dc4408e7f47da4f84e1afb1b393117e8" - dependencies: - binet "~0.3.5" - bsert "~0.0.10" - -"btcp@git+https://github.com/bcoin-org/btcp.git#semver:~0.1.5": - version "0.1.5" - resolved "git+https://github.com/bcoin-org/btcp.git#4ea7e1ce5a43cd5348152c007aff76a419190a3a" - buffer-alloc-unsafe@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz#bd7dc26ae2972d0eda253be061dba992349c19f0" @@ -2970,10 +2814,6 @@ buffer-from@^1.0.0: resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== -"buffer-map@git+https://github.com/chjj/buffer-map.git#semver:~0.0.7": - version "0.0.7" - resolved "git+https://github.com/chjj/buffer-map.git#bad5863af9a520701937a17fc8fa2bd8ca8e73f3" - buffer-reverse@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/buffer-reverse/-/buffer-reverse-1.0.1.tgz#49283c8efa6f901bc01fa3304d06027971ae2f60" @@ -3026,31 +2866,10 @@ bufferutil@^4.0.1: dependencies: node-gyp-build "^4.3.0" -bufio@^1.0.6, "bufio@git+https://github.com/bcoin-org/bufio.git#semver:~1.0.6", bufio@~1.0.7: +bufio@^1.0.6: version "1.0.7" resolved "git+https://github.com/bcoin-org/bufio.git#91ae6c93899ff9fad7d7cee9afd2a1c4933ca984" -"bupnp@git+https://github.com/bcoin-org/bupnp.git#semver:~0.2.6": - version "0.2.6" - resolved "git+https://github.com/bcoin-org/bupnp.git#c44fa7356aa297c9de96e8ad094a6816939cd688" - dependencies: - binet "~0.3.5" - brq "~0.1.7" - bsert "~0.0.10" - -"bval@git+https://github.com/bcoin-org/bval.git#semver:~0.1.6": - version "0.1.6" - resolved "git+https://github.com/bcoin-org/bval.git#c8cd14419ca46f63610dc48b797b076835e86f48" - dependencies: - bsert "~0.0.10" - -"bweb@git+https://github.com/bcoin-org/bweb.git#semver:=0.1.9": - version "0.1.9" - resolved "git+https://github.com/bcoin-org/bweb.git#31ae94ec9e97079610394e91928fe070d312c39d" - dependencies: - bsert "~0.0.10" - bsock "~0.1.8" - bytes@3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" @@ -5726,10 +5545,6 @@ load-json-file@^1.0.0: pinkie-promise "^2.0.0" strip-bom "^2.0.0" -"loady@git+https://github.com/chjj/loady.git#semver:~0.0.1", loady@~0.0.1, loady@~0.0.5: - version "0.0.5" - resolved "git+https://github.com/chjj/loady.git#b94958b7ee061518f4b85ea6da380e7ee93222d5" - locate-path@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" @@ -6148,19 +5963,11 @@ multihashes@^0.4.15, multihashes@~0.4.15: multibase "^0.7.0" varint "^5.0.0" -"n64@git+https://github.com/chjj/n64.git#semver:~0.2.10": - version "0.2.10" - resolved "git+https://github.com/chjj/n64.git#34f981f1441f569821d97a31f8cf21a3fc11b8f6" - nan@^2.13.2, nan@^2.14.0: version "2.15.0" resolved "https://registry.yarnpkg.com/nan/-/nan-2.15.0.tgz#3f34a473ff18e15c1b5626b62903b5ad6e665fee" integrity sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ== -"nan@git+https://github.com/braydonf/nan.git#semver:=2.14.0": - version "2.14.0" - resolved "git+https://github.com/braydonf/nan.git#1dcc61bd06d84e389bfd5311b2b1492a14c74201" - nano-json-stream-parser@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/nano-json-stream-parser/-/nano-json-stream-parser-0.1.2.tgz#0cc8f6d0e2b622b479c40d499c46d64b755c6f5f" From 6d15b27499fd2ac3f22e2202d3c6b5fa25200c31 Mon Sep 17 00:00:00 2001 From: Tomasz Slabon Date: Thu, 5 Oct 2023 12:38:33 +0200 Subject: [PATCH 09/12] Added docstrings for transaction to JSON functionalities --- typescript/src/bitcoin.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/typescript/src/bitcoin.ts b/typescript/src/bitcoin.ts index 3745050da..aea3d403f 100644 --- a/typescript/src/bitcoin.ts +++ b/typescript/src/bitcoin.ts @@ -781,6 +781,11 @@ export function isP2WSHScript(script: Buffer): boolean { } } +/** + * Represents a structured JSON format for a Bitcoin transaction. It includes + * detailed information about its inputs and outputs, as well as the transaction + * itself. + */ interface TxJSON { hash: string version: number @@ -799,6 +804,12 @@ interface TxJSON { }[] } +/** + * Converts a raw Bitcoin transaction into a structured JSON format. + * @param rawTransaction - A raw Bitcoin transaction in hexadecimal string format. + * @param bitcoinNetwork - Bitcoin network. + * @returns A structured JSON object representing the transaction. + */ export function txToJSON( rawTransaction: string, bitcoinNetwork: BitcoinNetwork From 9305eb0896791bb69cb126e4bbdfad9ffe5756c8 Mon Sep 17 00:00:00 2001 From: Tomasz Slabon Date: Thu, 5 Oct 2023 13:27:19 +0200 Subject: [PATCH 10/12] Added unit tests for transaction to JSON functionalities --- typescript/test/bitcoin.test.ts | 94 ++++++++++++++++++++++++++++++++- typescript/test/data/bitcoin.ts | 25 +++++++++ 2 files changed, 118 insertions(+), 1 deletion(-) diff --git a/typescript/test/bitcoin.test.ts b/typescript/test/bitcoin.test.ts index a6ea1c1ce..eb8521e00 100644 --- a/typescript/test/bitcoin.test.ts +++ b/typescript/test/bitcoin.test.ts @@ -22,12 +22,18 @@ import { isP2WPKHScript, isP2SHScript, isP2WSHScript, + txToJSON, } from "../src/bitcoin" import { calculateDepositRefundLocktime } from "../src/deposit" import { BitcoinNetwork } from "../src/bitcoin-network" import { Hex } from "../src/hex" import { BigNumber } from "ethers" -import { btcAddresses, btcAddressFromPublicKey } from "./data/bitcoin" +import { + btcAddresses, + btcAddressFromPublicKey, + mainnetTransaction, + testnetTransaction, +} from "./data/bitcoin" describe("Bitcoin", () => { describe("compressPublicKey", () => { @@ -732,3 +738,89 @@ describe("Bitcoin", () => { }) }) }) + +describe("txToJSON", () => { + context("when network is mainnet", () => { + it("should return correct transaction JSON", () => { + const txJSON = txToJSON(mainnetTransaction, BitcoinNetwork.Mainnet) + + expect(txJSON.hash).to.be.equal( + "bb20b27fef136ab1e5ee866a73bc9b33a038c3e258162e6c03e94f6e22941e0e" + ) + expect(txJSON.version).to.be.equal(1) + expect(txJSON.locktime).to.be.equal(0) + + expect(txJSON.inputs.length).to.be.equal(1) + expect(txJSON.inputs[0].hash).to.be.equal( + "a4082d137ab5c5264efb9f616ca4ac1673015c1e0817cd5cdc1b0379161be95e" + ) + expect(txJSON.inputs[0].index).to.be.equal(5) + expect(txJSON.inputs[0].sequence).to.be.equal(4294967295) + expect(txJSON.inputs[0].script).to.be.equal("") + expect(txJSON.inputs[0].witness).to.deep.equal([ + "", + "3044022022c7d7546fc0bb96a26c04823d97f0aa4bbe5d9af54acc8f4bd898e88" + + "b86956002206b126720f42b2f200434c6ae770b78aded9b32da4f020aba37f099" + + "d804eab02701", + "304402202b60c2ef3ba68eb473b65564e0fd038884407dc684c98309e3141bb53" + + "233dfd7022078d14fb2e433c71c6c62bd2019dd83859173a3b6973c62444930c1" + + "5d86d4bd1601", + "52210375e00eb72e29da82b89367947f29ef34afb75e8654f6ea368e0acdfd929" + + "76b7c2103a1b26313f430c4b15bb1fdce663207659d8cac749a0e53d70eff0187" + + "4496feff2103c96d495bfdd5ba4145e3e046fee45e84a8a48ad05bd8dbb395c01" + + "1a32cf9f88053ae", + ]) + + expect(txJSON.outputs.length).to.be.equal(2) + expect(txJSON.outputs[0].value).to.be.equal(11991850) + expect(txJSON.outputs[0].script).to.be.equal( + "76a914ee4b7569e9063064323332ad07dd18bc32402a0c88ac" + ) + expect(txJSON.outputs[0].address).to.be.equal( + "1NizDcdk2mWE45yZr98JJ2dyi2W2zeZUn5" + ) + expect(txJSON.outputs[1].value).to.be.equal(1805173) + expect(txJSON.outputs[1].script).to.be.equal( + "0020701a8d401c84fb13e6baf169d59684e17abd9fa216c8cc5b9fc63d622ff8c58d" + ) + expect(txJSON.outputs[1].address).to.be.equal( + "bc1qwqdg6squsna38e46795at95yu9atm8azzmyvckulcc7kytlcckxswvvzej" + ) + }) + }) + + context("when network is testnet", () => { + it("should return correct transaction JSON", () => { + const txJSON = txToJSON(testnetTransaction, BitcoinNetwork.Testnet) + + expect(txJSON.hash).to.be.equal( + "873effe868161e09ab65e1a23c7cecdc2792995c90ec94973f2fdbc59728ba89" + ) + expect(txJSON.version).to.be.equal(1) + expect(txJSON.locktime).to.be.equal(0) + + expect(txJSON.inputs.length).to.be.equal(1) + expect(txJSON.inputs[0].hash).to.be.equal( + "c0a5ed42f574b4b969ef0df16a70edb60d4a464739c5011bc051a8dedbaab730" + ) + expect(txJSON.inputs[0].index).to.be.equal(0) + expect(txJSON.inputs[0].sequence).to.be.equal(4294967295) + expect(txJSON.inputs[0].script).to.be.equal( + "4830450221009ab9ba3a4c9d81c4ac4431c05eac57388c8332bb191507926a3424" + + "ec697ac23802203369c91742a7d5168ba3af429aed4f2d1022749a4ba5052b172b" + + "b6776d9a07c1012103548c7fe1d7a66f8e705a4299153b87f4874c80aaed2cf828" + + "cd552d6975a01b80" + ) + expect(txJSON.inputs[0].witness).to.deep.equal([]) + + expect(txJSON.outputs.length).to.be.equal(1) + expect(txJSON.outputs[0].value).to.be.equal(270150) + expect(txJSON.outputs[0].script).to.be.equal( + "76a914819850140920deeacfee3a63193807daea8fc5d288ac" + ) + expect(txJSON.outputs[0].address).to.be.equal( + "msLBvgMp45BN9CaQCoZ4ewjm71Fix7RgB2" + ) + }) + }) +}) diff --git a/typescript/test/data/bitcoin.ts b/typescript/test/data/bitcoin.ts index b04baad81..03fb6d8de 100644 --- a/typescript/test/data/bitcoin.ts +++ b/typescript/test/data/bitcoin.ts @@ -104,3 +104,28 @@ export const btcAddressFromPublicKey: Record< }, }, } + +// An arbitrary Bitcoin mainnet transaction: +// https://live.blockcypher.com/btc/tx/bb20b27fef136ab1e5ee866a73bc9b33a038c3e258162e6c03e94f6e22941e0e/ +export const mainnetTransaction = + "010000000001015ee91b1679031bdc5ccd17081e5c017316aca46c619ffb4e26c5b57a13" + + "2d08a40500000000ffffffff022afbb600000000001976a914ee4b7569e9063064323332" + + "ad07dd18bc32402a0c88ac758b1b0000000000220020701a8d401c84fb13e6baf169d596" + + "84e17abd9fa216c8cc5b9fc63d622ff8c58d0400473044022022c7d7546fc0bb96a26c04" + + "823d97f0aa4bbe5d9af54acc8f4bd898e88b86956002206b126720f42b2f200434c6ae77" + + "0b78aded9b32da4f020aba37f099d804eab0270147304402202b60c2ef3ba68eb473b655" + + "64e0fd038884407dc684c98309e3141bb53233dfd7022078d14fb2e433c71c6c62bd2019" + + "dd83859173a3b6973c62444930c15d86d4bd16016952210375e00eb72e29da82b8936794" + + "7f29ef34afb75e8654f6ea368e0acdfd92976b7c2103a1b26313f430c4b15bb1fdce6632" + + "07659d8cac749a0e53d70eff01874496feff2103c96d495bfdd5ba4145e3e046fee45e84" + + "a8a48ad05bd8dbb395c011a32cf9f88053ae00000000" + +// An arbitrary Bitcoin testnet transaction: +// https://live.blockcypher.com/btc-testnet/tx/873effe868161e09ab65e1a23c7cecdc2792995c90ec94973f2fdbc59728ba89/ +export const testnetTransaction = + "010000000130b7aadbdea851c01b01c53947464a0db6ed706af10def69b9b474f542eda5" + + "c0000000006b4830450221009ab9ba3a4c9d81c4ac4431c05eac57388c8332bb19150792" + + "6a3424ec697ac23802203369c91742a7d5168ba3af429aed4f2d1022749a4ba5052b172b" + + "b6776d9a07c1012103548c7fe1d7a66f8e705a4299153b87f4874c80aaed2cf828cd552d" + + "6975a01b80ffffffff01461f0400000000001976a914819850140920deeacfee3a631938" + + "07daea8fc5d288ac00000000" From 38e970bd5ad75d9ca07fb78e064894ceef195910 Mon Sep 17 00:00:00 2001 From: Tomasz Slabon Date: Thu, 5 Oct 2023 17:48:51 +0200 Subject: [PATCH 11/12] Added missing unit test for decomposeRawTransaction --- typescript/test/bitcoin.test.ts | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/typescript/test/bitcoin.test.ts b/typescript/test/bitcoin.test.ts index eb8521e00..791127c1e 100644 --- a/typescript/test/bitcoin.test.ts +++ b/typescript/test/bitcoin.test.ts @@ -23,6 +23,7 @@ import { isP2SHScript, isP2WSHScript, txToJSON, + decomposeRawTransaction, } from "../src/bitcoin" import { calculateDepositRefundLocktime } from "../src/deposit" import { BitcoinNetwork } from "../src/bitcoin-network" @@ -34,6 +35,7 @@ import { mainnetTransaction, testnetTransaction, } from "./data/bitcoin" +import { depositSweepWithNoMainUtxoAndWitnessOutput } from "./data/deposit-sweep" describe("Bitcoin", () => { describe("compressPublicKey", () => { @@ -824,3 +826,28 @@ describe("txToJSON", () => { }) }) }) + +describe("decomposeRawTransaction", () => { + it("should return correctly decomposed transaction", () => { + const rawTransaction = + depositSweepWithNoMainUtxoAndWitnessOutput.expectedSweep.transaction + const decomposedTransaction = decomposeRawTransaction(rawTransaction) + + expect(decomposedTransaction.version).to.be.equal("01000000") + expect(decomposedTransaction.inputs).to.be.equal( + "02bc187be612bc3db8cfcdec56b75e9bc0262ab6eacfe27cc1a699bacd53e3d07400" + + "000000c948304502210089a89aaf3fec97ac9ffa91cdff59829f0cb3ef852a468153" + + "e2c0e2b473466d2e022072902bb923ef016ac52e941ced78f816bf27991c2b73211e" + + "227db27ec200bc0a012103989d253b17a6a0f41838b84ff0d20e8898f9d7b1a98f25" + + "64da4cc29dcf8581d94c5c14934b98637ca318a4d6e7ca6ffd1690b8e77df6377508" + + "f9f0c90d000395237576a9148db50eb52063ea9d98b3eac91489a90f738986f68763" + + "ac6776a914e257eccafbc07c381642ce6e7e55120fb077fbed8804e0250162b175ac" + + "68ffffffffdc557e737b6688c5712649b86f7757a722dc3d42786f23b2fa826394df" + + "ec545c0000000000ffffffff" + ) + expect(decomposedTransaction.outputs).to.be.equal( + "01488a0000000000001600148db50eb52063ea9d98b3eac91489a90f738986f6" + ) + expect(decomposedTransaction.locktime).to.be.equal("00000000") + }) +}) From 881e19de9fc41da0d3f8eaa0c5fb479870c49c04 Mon Sep 17 00:00:00 2001 From: Tomasz Slabon Date: Thu, 5 Oct 2023 18:24:18 +0200 Subject: [PATCH 12/12] Moved txToJSON to test/utils/helpers --- typescript/src/bitcoin.ts | 57 ---------------- typescript/test/bitcoin.test.ts | 94 +-------------------------- typescript/test/data/bitcoin.ts | 25 ------- typescript/test/deposit-sweep.test.ts | 2 +- typescript/test/deposit.test.ts | 2 +- typescript/test/redemption.test.ts | 2 +- typescript/test/utils/helpers.ts | 63 ++++++++++++++++++ 7 files changed, 67 insertions(+), 178 deletions(-) create mode 100644 typescript/test/utils/helpers.ts diff --git a/typescript/src/bitcoin.ts b/typescript/src/bitcoin.ts index aea3d403f..3649327a6 100644 --- a/typescript/src/bitcoin.ts +++ b/typescript/src/bitcoin.ts @@ -780,60 +780,3 @@ export function isP2WSHScript(script: Buffer): boolean { return false } } - -/** - * Represents a structured JSON format for a Bitcoin transaction. It includes - * detailed information about its inputs and outputs, as well as the transaction - * itself. - */ -interface TxJSON { - hash: string - version: number - locktime: number - inputs: { - hash: string - index: number - sequence: number - script: string - witness: string[] - }[] - outputs: { - value: number - script: string - address: string - }[] -} - -/** - * Converts a raw Bitcoin transaction into a structured JSON format. - * @param rawTransaction - A raw Bitcoin transaction in hexadecimal string format. - * @param bitcoinNetwork - Bitcoin network. - * @returns A structured JSON object representing the transaction. - */ -export function txToJSON( - rawTransaction: string, - bitcoinNetwork: BitcoinNetwork -): TxJSON { - const transaction = Tx.fromHex(rawTransaction) - const network = toBitcoinJsLibNetwork(bitcoinNetwork) - - const txJSON: TxJSON = { - hash: transaction.getId(), - version: transaction.version, - locktime: transaction.locktime, - inputs: transaction.ins.map((input) => ({ - hash: Hex.from(input.hash).reverse().toString(), - index: input.index, - sequence: input.sequence, - script: input.script.toString("hex"), - witness: input.witness.map((w) => w.toString("hex")), - })), - outputs: transaction.outs.map((output) => ({ - value: output.value, - script: output.script.toString("hex"), - address: address.fromOutputScript(output.script, network), - })), - } - - return txJSON -} diff --git a/typescript/test/bitcoin.test.ts b/typescript/test/bitcoin.test.ts index 791127c1e..03ab74305 100644 --- a/typescript/test/bitcoin.test.ts +++ b/typescript/test/bitcoin.test.ts @@ -22,19 +22,13 @@ import { isP2WPKHScript, isP2SHScript, isP2WSHScript, - txToJSON, decomposeRawTransaction, } from "../src/bitcoin" import { calculateDepositRefundLocktime } from "../src/deposit" import { BitcoinNetwork } from "../src/bitcoin-network" import { Hex } from "../src/hex" import { BigNumber } from "ethers" -import { - btcAddresses, - btcAddressFromPublicKey, - mainnetTransaction, - testnetTransaction, -} from "./data/bitcoin" +import { btcAddresses, btcAddressFromPublicKey } from "./data/bitcoin" import { depositSweepWithNoMainUtxoAndWitnessOutput } from "./data/deposit-sweep" describe("Bitcoin", () => { @@ -741,92 +735,6 @@ describe("Bitcoin", () => { }) }) -describe("txToJSON", () => { - context("when network is mainnet", () => { - it("should return correct transaction JSON", () => { - const txJSON = txToJSON(mainnetTransaction, BitcoinNetwork.Mainnet) - - expect(txJSON.hash).to.be.equal( - "bb20b27fef136ab1e5ee866a73bc9b33a038c3e258162e6c03e94f6e22941e0e" - ) - expect(txJSON.version).to.be.equal(1) - expect(txJSON.locktime).to.be.equal(0) - - expect(txJSON.inputs.length).to.be.equal(1) - expect(txJSON.inputs[0].hash).to.be.equal( - "a4082d137ab5c5264efb9f616ca4ac1673015c1e0817cd5cdc1b0379161be95e" - ) - expect(txJSON.inputs[0].index).to.be.equal(5) - expect(txJSON.inputs[0].sequence).to.be.equal(4294967295) - expect(txJSON.inputs[0].script).to.be.equal("") - expect(txJSON.inputs[0].witness).to.deep.equal([ - "", - "3044022022c7d7546fc0bb96a26c04823d97f0aa4bbe5d9af54acc8f4bd898e88" + - "b86956002206b126720f42b2f200434c6ae770b78aded9b32da4f020aba37f099" + - "d804eab02701", - "304402202b60c2ef3ba68eb473b65564e0fd038884407dc684c98309e3141bb53" + - "233dfd7022078d14fb2e433c71c6c62bd2019dd83859173a3b6973c62444930c1" + - "5d86d4bd1601", - "52210375e00eb72e29da82b89367947f29ef34afb75e8654f6ea368e0acdfd929" + - "76b7c2103a1b26313f430c4b15bb1fdce663207659d8cac749a0e53d70eff0187" + - "4496feff2103c96d495bfdd5ba4145e3e046fee45e84a8a48ad05bd8dbb395c01" + - "1a32cf9f88053ae", - ]) - - expect(txJSON.outputs.length).to.be.equal(2) - expect(txJSON.outputs[0].value).to.be.equal(11991850) - expect(txJSON.outputs[0].script).to.be.equal( - "76a914ee4b7569e9063064323332ad07dd18bc32402a0c88ac" - ) - expect(txJSON.outputs[0].address).to.be.equal( - "1NizDcdk2mWE45yZr98JJ2dyi2W2zeZUn5" - ) - expect(txJSON.outputs[1].value).to.be.equal(1805173) - expect(txJSON.outputs[1].script).to.be.equal( - "0020701a8d401c84fb13e6baf169d59684e17abd9fa216c8cc5b9fc63d622ff8c58d" - ) - expect(txJSON.outputs[1].address).to.be.equal( - "bc1qwqdg6squsna38e46795at95yu9atm8azzmyvckulcc7kytlcckxswvvzej" - ) - }) - }) - - context("when network is testnet", () => { - it("should return correct transaction JSON", () => { - const txJSON = txToJSON(testnetTransaction, BitcoinNetwork.Testnet) - - expect(txJSON.hash).to.be.equal( - "873effe868161e09ab65e1a23c7cecdc2792995c90ec94973f2fdbc59728ba89" - ) - expect(txJSON.version).to.be.equal(1) - expect(txJSON.locktime).to.be.equal(0) - - expect(txJSON.inputs.length).to.be.equal(1) - expect(txJSON.inputs[0].hash).to.be.equal( - "c0a5ed42f574b4b969ef0df16a70edb60d4a464739c5011bc051a8dedbaab730" - ) - expect(txJSON.inputs[0].index).to.be.equal(0) - expect(txJSON.inputs[0].sequence).to.be.equal(4294967295) - expect(txJSON.inputs[0].script).to.be.equal( - "4830450221009ab9ba3a4c9d81c4ac4431c05eac57388c8332bb191507926a3424" + - "ec697ac23802203369c91742a7d5168ba3af429aed4f2d1022749a4ba5052b172b" + - "b6776d9a07c1012103548c7fe1d7a66f8e705a4299153b87f4874c80aaed2cf828" + - "cd552d6975a01b80" - ) - expect(txJSON.inputs[0].witness).to.deep.equal([]) - - expect(txJSON.outputs.length).to.be.equal(1) - expect(txJSON.outputs[0].value).to.be.equal(270150) - expect(txJSON.outputs[0].script).to.be.equal( - "76a914819850140920deeacfee3a63193807daea8fc5d288ac" - ) - expect(txJSON.outputs[0].address).to.be.equal( - "msLBvgMp45BN9CaQCoZ4ewjm71Fix7RgB2" - ) - }) - }) -}) - describe("decomposeRawTransaction", () => { it("should return correctly decomposed transaction", () => { const rawTransaction = diff --git a/typescript/test/data/bitcoin.ts b/typescript/test/data/bitcoin.ts index 03fb6d8de..b04baad81 100644 --- a/typescript/test/data/bitcoin.ts +++ b/typescript/test/data/bitcoin.ts @@ -104,28 +104,3 @@ export const btcAddressFromPublicKey: Record< }, }, } - -// An arbitrary Bitcoin mainnet transaction: -// https://live.blockcypher.com/btc/tx/bb20b27fef136ab1e5ee866a73bc9b33a038c3e258162e6c03e94f6e22941e0e/ -export const mainnetTransaction = - "010000000001015ee91b1679031bdc5ccd17081e5c017316aca46c619ffb4e26c5b57a13" + - "2d08a40500000000ffffffff022afbb600000000001976a914ee4b7569e9063064323332" + - "ad07dd18bc32402a0c88ac758b1b0000000000220020701a8d401c84fb13e6baf169d596" + - "84e17abd9fa216c8cc5b9fc63d622ff8c58d0400473044022022c7d7546fc0bb96a26c04" + - "823d97f0aa4bbe5d9af54acc8f4bd898e88b86956002206b126720f42b2f200434c6ae77" + - "0b78aded9b32da4f020aba37f099d804eab0270147304402202b60c2ef3ba68eb473b655" + - "64e0fd038884407dc684c98309e3141bb53233dfd7022078d14fb2e433c71c6c62bd2019" + - "dd83859173a3b6973c62444930c15d86d4bd16016952210375e00eb72e29da82b8936794" + - "7f29ef34afb75e8654f6ea368e0acdfd92976b7c2103a1b26313f430c4b15bb1fdce6632" + - "07659d8cac749a0e53d70eff01874496feff2103c96d495bfdd5ba4145e3e046fee45e84" + - "a8a48ad05bd8dbb395c011a32cf9f88053ae00000000" - -// An arbitrary Bitcoin testnet transaction: -// https://live.blockcypher.com/btc-testnet/tx/873effe868161e09ab65e1a23c7cecdc2792995c90ec94973f2fdbc59728ba89/ -export const testnetTransaction = - "010000000130b7aadbdea851c01b01c53947464a0db6ed706af10def69b9b474f542eda5" + - "c0000000006b4830450221009ab9ba3a4c9d81c4ac4431c05eac57388c8332bb19150792" + - "6a3424ec697ac23802203369c91742a7d5168ba3af429aed4f2d1022749a4ba5052b172b" + - "b6776d9a07c1012103548c7fe1d7a66f8e705a4299153b87f4874c80aaed2cf828cd552d" + - "6975a01b80ffffffff01461f0400000000001976a914819850140920deeacfee3a631938" + - "07daea8fc5d288ac00000000" diff --git a/typescript/test/deposit-sweep.test.ts b/typescript/test/deposit-sweep.test.ts index 103d86607..f37a961c6 100644 --- a/typescript/test/deposit-sweep.test.ts +++ b/typescript/test/deposit-sweep.test.ts @@ -4,7 +4,6 @@ import { TransactionHash, UnspentTransactionOutput, Transaction, - txToJSON, } from "../src/bitcoin" import { testnetWalletAddress, testnetWalletPrivateKey } from "./data/deposit" import { @@ -17,6 +16,7 @@ import { } from "./data/deposit-sweep" import { MockBitcoinClient } from "./utils/mock-bitcoin-client" import { MockBridge } from "./utils/mock-bridge" +import { txToJSON } from "./utils/helpers" import * as chai from "chai" import chaiAsPromised from "chai-as-promised" chai.use(chaiAsPromised) diff --git a/typescript/test/deposit.test.ts b/typescript/test/deposit.test.ts index 79f098230..a1b2c705b 100644 --- a/typescript/test/deposit.test.ts +++ b/typescript/test/deposit.test.ts @@ -12,7 +12,6 @@ import { RawTransaction, TransactionHash, UnspentTransactionOutput, - txToJSON, } from "../src/bitcoin" import { MockBitcoinClient } from "./utils/mock-bitcoin-client" import { @@ -30,6 +29,7 @@ import { suggestDepositWallet, } from "../src/deposit" import { MockBridge } from "./utils/mock-bridge" +import { txToJSON } from "./utils/helpers" import { Address } from "../src/ethereum" import { BitcoinNetwork } from "../src" diff --git a/typescript/test/redemption.test.ts b/typescript/test/redemption.test.ts index acbafa2bc..802942f7b 100644 --- a/typescript/test/redemption.test.ts +++ b/typescript/test/redemption.test.ts @@ -3,7 +3,6 @@ import { RawTransaction, TransactionHash, UnspentTransactionOutput, - txToJSON, } from "../src/bitcoin" import { MockBitcoinClient } from "./utils/mock-bitcoin-client" import { @@ -31,6 +30,7 @@ import { submitRedemptionProof, submitRedemptionTransaction, } from "../src/redemption" +import { txToJSON } from "./utils/helpers" import { MockBridge } from "./utils/mock-bridge" import * as chai from "chai" import chaiAsPromised from "chai-as-promised" diff --git a/typescript/test/utils/helpers.ts b/typescript/test/utils/helpers.ts new file mode 100644 index 000000000..59634d35b --- /dev/null +++ b/typescript/test/utils/helpers.ts @@ -0,0 +1,63 @@ +import { Hex } from "../../src/hex" +import { + BitcoinNetwork, + toBitcoinJsLibNetwork, +} from "../../src/bitcoin-network" +import { Transaction, address } from "bitcoinjs-lib" + +/** + * Represents a structured JSON format for a Bitcoin transaction. It includes + * detailed information about its inputs and outputs, as well as the transaction + * itself. + */ +interface TxJSON { + hash: string + version: number + locktime: number + inputs: { + hash: string + index: number + sequence: number + script: string + witness: string[] + }[] + outputs: { + value: number + script: string + address: string + }[] +} + +/** + * Converts a raw Bitcoin transaction into a structured JSON format. + * @param rawTransaction - A raw Bitcoin transaction in hexadecimal string format. + * @param bitcoinNetwork - Bitcoin network. + * @returns A structured JSON object representing the transaction. + */ +export function txToJSON( + rawTransaction: string, + bitcoinNetwork: BitcoinNetwork +): TxJSON { + const transaction = Transaction.fromHex(rawTransaction) + const network = toBitcoinJsLibNetwork(bitcoinNetwork) + + const txJSON: TxJSON = { + hash: transaction.getId(), + version: transaction.version, + locktime: transaction.locktime, + inputs: transaction.ins.map((input) => ({ + hash: Hex.from(input.hash).reverse().toString(), + index: input.index, + sequence: input.sequence, + script: input.script.toString("hex"), + witness: input.witness.map((w) => w.toString("hex")), + })), + outputs: transaction.outs.map((output) => ({ + value: output.value, + script: output.script.toString("hex"), + address: address.fromOutputScript(output.script, network), + })), + } + + return txJSON +}