Skip to content

Commit

Permalink
Add getAddressFromScriptPubKey function (#646)
Browse files Browse the repository at this point in the history
~Depends on: #632~

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
lukasz-zimnoch committed Jul 12, 2023
2 parents ec34e17 + 1c9b91b commit b43f6a5
Show file tree
Hide file tree
Showing 3 changed files with 120 additions and 60 deletions.
15 changes: 15 additions & 0 deletions typescript/src/bitcoin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -624,3 +624,18 @@ export function locktimeToNumber(locktimeLE: Buffer | string): number {
export function createOutputScriptFromAddress(address: string): Hex {
return Hex.from(Script.fromAddress(address).toRaw().toString("hex"))
}

/**
* Creates the Bitcoin address from the output script.
* @param script The unprefixed and not prepended with length output script.
* @param network Bitcoin network.
* @returns The Bitcoin address.
*/
export function createAddressFromOutputScript(
script: Hex,
network: BitcoinNetwork = BitcoinNetwork.Mainnet
): string {
return Script.fromRaw(script.toString(), "hex")
.getAddress()
?.toString(toBcoinNetwork(network))
}
93 changes: 33 additions & 60 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,
createAddressFromOutputScript,
} 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,70 +469,41 @@ describe("Bitcoin", () => {
})

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

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

expect(result.toString()).to.eq(expectedOutputScript)
})
}
)
Object.keys(btcAddresses).forEach((bitcoinNetwork) => {
context(`with ${bitcoinNetwork} addresses`, () => {
Object.entries(
btcAddresses[bitcoinNetwork as keyof typeof btcAddresses]
).forEach(
([addressType, { address, scriptPubKey: expectedOutputScript }]) => {
it(`should create correct output script for ${addressType} address type`, () => {
const result = createOutputScriptFromAddress(address)

expect(result.toString()).to.eq(expectedOutputScript.toString())
})
}
)
})
})
})

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

Object.entries(btcAddresses).forEach(
([addressType, { address, outputScript: expectedOutputScript }]) => {
it(`should create correct output script for ${addressType} address type`, () => {
const result = createOutputScriptFromAddress(address)
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 = createAddressFromOutputScript(
scriptPubKey,
bitcoinNetwork === "mainnet"
? BitcoinNetwork.Mainnet
: BitcoinNetwork.Testnet
)

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

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

0 comments on commit b43f6a5

Please sign in to comment.