Skip to content

Commit

Permalink
Add getAddressFromScriptPubKey fn
Browse files Browse the repository at this point in the history
Add the new function that returns the Bitcoin address based on the
script pub key placed on the output of a Bitcoin transaction.
  • Loading branch information
r-czajkowski committed Jul 6, 2023
1 parent 5c3aba9 commit ea33c65
Show file tree
Hide file tree
Showing 3 changed files with 128 additions and 101 deletions.
19 changes: 18 additions & 1 deletion typescript/src/bitcoin.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import bcoin, { TX, Script } from "bcoin"
import bcoin, { TX, Script, Address } from "bcoin"
import wif from "wif"
import bufio from "bufio"
import hash160 from "bcrypto/lib/hash160"
Expand Down Expand Up @@ -612,3 +612,20 @@ export function locktimeToNumber(locktimeLE: Buffer | string): number {
export function createOutputScriptFromAddress(address: string): Hex {
return Hex.from(Script.fromAddress(address).toRaw().toString("hex"))
}

/**
* Returns the Bitcoin address based on the script pub key placed on the output
* of a Bitcoin transaction.
* @param scriptPubKey Scirpt pub key placed on the output of a Bitcoin
* transaction.
* @param network Bitcoin network.
* @returns The Bitcoin address.
*/
export function getAddressFromScriptPubKey(
scriptPubKey: string,
network: BitcoinNetwork = BitcoinNetwork.Mainnet
): string {
return Script.fromRaw(scriptPubKey.toString(), "hex")
.getAddress()
?.toString(toBcoinNetwork(network))
}
145 changes: 45 additions & 100 deletions typescript/test/bitcoin.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,13 @@ import {
bitsToTarget,
targetToDifficulty,
createOutputScriptFromAddress,
getAddressFromScriptPubKey,
} 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 } from "./data/bitcoin"

describe("Bitcoin", () => {
describe("compressPublicKey", () => {
Expand Down Expand Up @@ -467,110 +469,53 @@ describe("Bitcoin", () => {
})

describe("createOutputScriptFromAddress", () => {
context("with testnet addresses", () => {
const btcAddresses = {
P2PKH: {
address: "mjc2zGWypwpNyDi4ZxGbBNnUA84bfgiwYc",
redeemerOutputScript:
"0x1976a9142cd680318747b720d67bf4246eb7403b476adb3488ac",
outputScript: "76a9142cd680318747b720d67bf4246eb7403b476adb3488ac",
},
P2WPKH: {
address: "tb1qumuaw3exkxdhtut0u85latkqfz4ylgwstkdzsx",
redeemerOutputScript:
"0x160014e6f9d74726b19b75f16fe1e9feaec048aa4fa1d0",
outputScript: "0014e6f9d74726b19b75f16fe1e9feaec048aa4fa1d0",
},
P2SH: {
address: "2MsM67NLa71fHvTUBqNENW15P68nHB2vVXb",
redeemerOutputScript:
"0x17a914011beb6fb8499e075a57027fb0a58384f2d3f78487",
outputScript: "a914011beb6fb8499e075a57027fb0a58384f2d3f78487",
},
P2WSH: {
address:
"tb1qau95mxzh2249aa3y8exx76ltc2sq0e7kw8hj04936rdcmnynhswqqz02vv",
redeemerOutputScript:
"0x220020ef0b4d985752aa5ef6243e4c6f6bebc2a007e7d671ef27d4b1d0db8dcc93bc1c",
outputScript:
"0020ef0b4d985752aa5ef6243e4c6f6bebc2a007e7d671ef27d4b1d0db8dcc93bc1c",
},
}

Object.entries(btcAddresses).forEach(
([
addressType,
{
address,
redeemerOutputScript: expectedRedeemerOutputScript,
outputScript: expectedOutputScript,
},
]) => {
it(`should create correct output script for ${addressType} address type`, () => {
const result = createOutputScriptFromAddress(address)

expect(result.toString()).to.eq(expectedOutputScript)
// Check if we can build the prefixed raw redeemer output script based
// on the result.
expect(buildRawPrefixedOutputScript(result.toString())).to.eq(
expectedRedeemerOutputScript
)
})
}
)
Object.keys(btcAddresses).forEach((bitcoinNetwork) => {
context(`with ${bitcoinNetwork} addresses`, () => {
Object.entries(
btcAddresses[bitcoinNetwork as keyof typeof btcAddresses]
).forEach(
([
addressType,
{
address,
redeemerOutputScript: expectedRedeemerOutputScript,
scriptPubKey: expectedOutputScript,
},
]) => {
it(`should create correct output script for ${addressType} address type`, () => {
const result = createOutputScriptFromAddress(address)

expect(result.toString()).to.eq(expectedOutputScript)
// Check if we can build the prefixed raw redeemer output script based
// on the result.
expect(buildRawPrefixedOutputScript(result.toString())).to.eq(
expectedRedeemerOutputScript
)
})
}
)
})
})
})

context("with mainnet addresses", () => {
const btcAddresses = {
P2PKH: {
address: "12higDjoCCNXSA95xZMWUdPvXNmkAduhWv",
redeemerOutputScript:
"0x1976a91412ab8dc588ca9d5787dde7eb29569da63c3a238c88ac",
outputScript: "76a91412ab8dc588ca9d5787dde7eb29569da63c3a238c88ac",
},
P2WPKH: {
address: "bc1q34aq5drpuwy3wgl9lhup9892qp6svr8ldzyy7c",
redeemerOutputScript:
"0x1600148d7a0a3461e3891723e5fdf8129caa0075060cff",
outputScript: "00148d7a0a3461e3891723e5fdf8129caa0075060cff",
},
P2SH: {
address: "342ftSRCvFHfCeFFBuz4xwbeqnDw6BGUey",
redeemerOutputScript:
"0x17a91419a7d869032368fd1f1e26e5e73a4ad0e474960e87",
outputScript: "a91419a7d869032368fd1f1e26e5e73a4ad0e474960e87",
},
P2WSH: {
address:
"bc1qeklep85ntjz4605drds6aww9u0qr46qzrv5xswd35uhjuj8ahfcqgf6hak",
redeemerOutputScript:
"0x220020cdbf909e935c855d3e8d1b61aeb9c5e3c03ae8021b286839b1a72f2e48fdba70",
outputScript:
"0020cdbf909e935c855d3e8d1b61aeb9c5e3c03ae8021b286839b1a72f2e48fdba70",
},
}

Object.entries(btcAddresses).forEach(
([
addressType,
{
address,
redeemerOutputScript: expectedRedeemerOutputScript,
outputScript: expectedOutputScript,
},
]) => {
it(`should create correct output script for ${addressType} address type`, () => {
const result = createOutputScriptFromAddress(address)

expect(result.toString()).to.eq(expectedOutputScript)
// Check if we can build the prefixed raw redeemer output script based
// on the result.
expect(buildRawPrefixedOutputScript(result.toString())).to.eq(
expectedRedeemerOutputScript
describe("getAddressFromScriptPubKey", () => {
Object.keys(btcAddresses).forEach((bitcoinNetwork) => {
context(`with ${bitcoinNetwork} addresses`, () => {
Object.entries(
btcAddresses[bitcoinNetwork as keyof typeof btcAddresses]
).forEach(([addressType, { address, scriptPubKey }]) => {
it(`should return correct ${addressType} address`, () => {
const result = getAddressFromScriptPubKey(
scriptPubKey,
bitcoinNetwork === "mainnet"
? BitcoinNetwork.Mainnet
: BitcoinNetwork.Testnet
)

expect(result.toString()).to.eq(address)
})
}
)
})
})
})
})
})
Expand Down
65 changes: 65 additions & 0 deletions typescript/test/data/bitcoin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { BitcoinNetwork } from "../../src/bitcoin-network"

export const btcAddresses: Record<
Exclude<BitcoinNetwork, BitcoinNetwork.Unknown>,
{
[addressType: string]: {
address: string
redeemerOutputScript: string
scriptPubKey: string
}
}
> = {
testnet: {
P2PKH: {
address: "mjc2zGWypwpNyDi4ZxGbBNnUA84bfgiwYc",
redeemerOutputScript:
"0x1976a9142cd680318747b720d67bf4246eb7403b476adb3488ac",
scriptPubKey: "76a9142cd680318747b720d67bf4246eb7403b476adb3488ac",
},
P2WPKH: {
address: "tb1qumuaw3exkxdhtut0u85latkqfz4ylgwstkdzsx",
redeemerOutputScript: "0x160014e6f9d74726b19b75f16fe1e9feaec048aa4fa1d0",
scriptPubKey: "0014e6f9d74726b19b75f16fe1e9feaec048aa4fa1d0",
},
P2SH: {
address: "2MsM67NLa71fHvTUBqNENW15P68nHB2vVXb",
redeemerOutputScript:
"0x17a914011beb6fb8499e075a57027fb0a58384f2d3f78487",
scriptPubKey: "a914011beb6fb8499e075a57027fb0a58384f2d3f78487",
},
P2WSH: {
address: "tb1qau95mxzh2249aa3y8exx76ltc2sq0e7kw8hj04936rdcmnynhswqqz02vv",
redeemerOutputScript:
"0x220020ef0b4d985752aa5ef6243e4c6f6bebc2a007e7d671ef27d4b1d0db8dcc93bc1c",
scriptPubKey:
"0020ef0b4d985752aa5ef6243e4c6f6bebc2a007e7d671ef27d4b1d0db8dcc93bc1c",
},
},
mainnet: {
P2PKH: {
address: "12higDjoCCNXSA95xZMWUdPvXNmkAduhWv",
redeemerOutputScript:
"0x1976a91412ab8dc588ca9d5787dde7eb29569da63c3a238c88ac",
scriptPubKey: "76a91412ab8dc588ca9d5787dde7eb29569da63c3a238c88ac",
},
P2WPKH: {
address: "bc1q34aq5drpuwy3wgl9lhup9892qp6svr8ldzyy7c",
redeemerOutputScript: "0x1600148d7a0a3461e3891723e5fdf8129caa0075060cff",
scriptPubKey: "00148d7a0a3461e3891723e5fdf8129caa0075060cff",
},
P2SH: {
address: "342ftSRCvFHfCeFFBuz4xwbeqnDw6BGUey",
redeemerOutputScript:
"0x17a91419a7d869032368fd1f1e26e5e73a4ad0e474960e87",
scriptPubKey: "a91419a7d869032368fd1f1e26e5e73a4ad0e474960e87",
},
P2WSH: {
address: "bc1qeklep85ntjz4605drds6aww9u0qr46qzrv5xswd35uhjuj8ahfcqgf6hak",
redeemerOutputScript:
"0x220020cdbf909e935c855d3e8d1b61aeb9c5e3c03ae8021b286839b1a72f2e48fdba70",
scriptPubKey:
"0020cdbf909e935c855d3e8d1b61aeb9c5e3c03ae8021b286839b1a72f2e48fdba70",
},
},
}

0 comments on commit ea33c65

Please sign in to comment.