Skip to content

Instantly share code, notes, and snippets.

@janniks
Last active November 17, 2022 14:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save janniks/b7ba98b1770228880ef05fc96ccd14cb to your computer and use it in GitHub Desktop.
Save janniks/b7ba98b1770228880ef05fc96ccd14cb to your computer and use it in GitHub Desktop.
Test Vectors for BTC addresses and their hash bytes
Using test vectors from various sources, we generate and verify addresses and the raw hash bytes of those addresses.
import { bech32m as scure } from "@scure/base";
import { bytesToHex, hexToBytes } from "@stacks/common";
import * as btc from "micro-btc-signer";
import { bech32m } from "bech32";
import * as taproot from "bip-schnorr/src/taproot.js";
import * as bitcoin from "bitcoinjs-lib";
import ecurve from "ecurve";
import * as assert from "assert";
const SECP = ecurve.getCurveByName("secp256k1");
const NETWORKS = {
mainnet: bitcoin.networks.bitcoin,
testnet: bitcoin.networks.testnet,
};
function generateTestVector(opts) {
const { format, privateKey, publicKey, address, network } = opts;
let payment;
let generatedAddress, generatedHashBytes; // out
switch (format) {
case "p2pkh": // ===========================================================
payment = bitcoin.payments.p2pkh({
pubkey: Buffer.from(publicKey, "hex"),
network: NETWORKS[network],
});
generatedAddress = btc.getAddress(
"pkh",
hexToBytes(privateKey.slice(0, 64)),
NETWORKS[network]
);
assert.equal(
generatedAddress,
address,
JSON.stringify(NETWORKS[network])
);
assert.equal(generatedAddress, payment.address);
generatedHashBytes = payment.hash.toString("hex");
break;
case "p2sh-p2pkh": // ======================================================
payment = bitcoin.payments.p2sh({
redeem: bitcoin.payments.p2pkh({
pubkey: Buffer.from(publicKey, "hex"),
network: NETWORKS[network],
}),
network: NETWORKS[network],
});
generatedAddress = payment.address;
assert.equal(generatedAddress, address);
generatedHashBytes = payment.hash.toString("hex");
break;
case "p2sh-p2wpkh": // =====================================================
payment = bitcoin.payments.p2sh({
redeem: bitcoin.payments.p2wpkh({
pubkey: Buffer.from(publicKey, "hex"),
network: NETWORKS[network],
}),
network: NETWORKS[network],
});
generatedAddress = payment.address;
assert.equal(generatedAddress, address);
generatedHashBytes = payment.hash.toString("hex");
break;
case "p2sh-p2wsh": // ======================================================
payment = bitcoin.payments.p2sh({
redeem: bitcoin.payments.p2wsh({
redeem: bitcoin.payments.p2ms({
m: 1,
pubkeys: [Buffer.from(publicKey, "hex")],
network: NETWORKS[network],
}),
}),
network: NETWORKS[network],
});
generatedAddress = payment.address;
assert.equal(generatedAddress, address);
generatedHashBytes = payment.hash.toString("hex");
break;
case "p2wpkh": // ==========================================================
payment = bitcoin.payments.p2wpkh({
pubkey: Buffer.from(publicKey, "hex"),
network: NETWORKS[network],
});
generatedAddress = btc.getAddress(
"wpkh",
hexToBytes(privateKey.slice(0, 64)),
NETWORKS[network]
);
assert.equal(generatedAddress, address);
assert.equal(payment.address, address);
generatedHashBytes = payment.hash.toString("hex");
break;
case "p2wsh": // ===========================================================
payment = bitcoin.payments.p2wsh({
redeem: bitcoin.payments.p2ms({
m: 1,
pubkeys: [Buffer.from(publicKey, "hex")],
network: NETWORKS[network],
}),
network: NETWORKS[network],
});
generatedAddress = payment.address;
assert.equal(generatedAddress, address);
generatedHashBytes = payment.hash.toString("hex");
break;
case "p2tr": // ============================================================
generatedAddress = btc.getAddress(
"tr",
hexToBytes(privateKey.slice(0, 64)),
NETWORKS[network]
);
assert.equal(generatedAddress, address);
// scure
const { words: words1 } = scure.decode(
btc.getAddress(
"tr",
hexToBytes(privateKey.slice(0, 64)),
NETWORKS[network]
)
);
generatedHashBytes = bytesToHex(
Uint8Array.from(scure.fromWords(words1.slice(1)))
);
// bip-schnorr
const point = ecurve.Point.decodeFrom(
SECP,
Buffer.from(publicKey, "hex")
);
const pubTr = taproot.taprootConstruct(point);
const words = bech32m.toWords(pubTr);
assert.equal(
bytesToHex(Uint8Array.from(bech32m.fromWords(words))),
generatedHashBytes
);
words.unshift(1);
assert.equal(bech32m.encode(NETWORKS[network].bech32, words), address);
break;
default:
throw new Error(format);
}
console.log(
`${JSON.stringify({
...opts,
generatedAddress,
generatedHashBytes,
})},`
);
}
// Test vectors taken from https://github.com/hirosystems/stacks-blockchain-api/blob/ae0eb65c4d6901db172f2b4ea751817d8ef8c05c/src/tests/helpers-tests.ts#L193-L530
const BTC_ADDRESS_CASES_API = [
{
comment:
"Test vector from https://github.com/bitcoinjs/bitcoinjs-lib/blob/54259d301960cefddc259d64012bb4a7c2366d48/test/fixtures/address.json#L3-L9",
privateKey:
"0000000000000000000000000000000000000000000000000000000000000001",
publicKey:
"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
address: "1BgGZ9tcN4rm9KBzDn7KprQz87SZ26SAMH",
network: "mainnet",
format: "p2pkh",
},
{
comment:
"Test vector from https://github.com/bitcoinjs/bitcoinjs-lib/blob/54259d301960cefddc259d64012bb4a7c2366d48/test/fixtures/address.json#L31-L37",
privateKey:
"0000000000000000000000000000000000000000000000000000000000000001",
publicKey:
"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
address: "mrCDrCybB6J1vRfbwM5hemdJz73FwDBC8r",
network: "testnet",
format: "p2pkh",
},
{
privateKey:
"cb3df38053d132895220b9ce471f6b676db5b9bf0b4adefb55f2118ece2478df01",
publicKey:
"03797dd653040d344fd048c1ad05d4cbcb2178b30c6a0c4276994795f3e833da41",
address: "mhYeZXrSEuyf2wbJ14qZ2apG7ofMLDj9Ss",
network: "testnet",
format: "p2pkh",
},
{
comment:
"Test vector from https://github.com/bitcoinjs/bitcoinjs-lib/blob/54259d301960cefddc259d64012bb4a7c2366d48/test/fixtures/address.json#L11-L15",
privateKey:
"0000000000000000000000000000000000000000000000000000000000000001",
publicKey:
"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
address: "3LRW7jeCvQCRdPF8S3yUCfRAx4eqXFmdcr",
network: "mainnet",
format: "p2sh-p2pkh",
},
{
comment:
"Test vector from https://github.com/trezor-graveyard/bitcoinjs-trezor/blob/13b1c0be67abfea0bddbf5360548630c82331ce9/test/fixtures/address.json#L39-L43",
privateKey:
"0000000000000000000000000000000000000000000000000000000000000001",
publicKey:
"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
address: "2NByiBUaEXrhmqAsg7BbLpcQSAQs1EDwt5w",
network: "testnet",
format: "p2sh-p2pkh",
},
{
privateKey:
"cb3df38053d132895220b9ce471f6b676db5b9bf0b4adefb55f2118ece2478df01",
publicKey:
"03797dd653040d344fd048c1ad05d4cbcb2178b30c6a0c4276994795f3e833da41",
address: "2MygMgDLGPjN9wfEW8gaS1CqAwnuzLdNheW",
network: "testnet",
format: "p2sh-p2pkh",
},
// Test vectors from https://github.com/bitcoinjs/bitcoinjs-message/blob/c43430f4c03c292c719e7801e425d887cbdf7464/test/fixtures.json#L117
{
privateKey:
"0000000000000000000000000000000000000000000000000000000000000001",
publicKey:
"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
address: "3JvL6Ymt8MVWiCNHC7oWU6nLeHNJKLZGLN",
network: "mainnet",
format: "p2sh-p2wpkh",
},
{
privateKey:
"0000000000000000000000000000000000000000000000000000000000000001",
publicKey:
"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
address: "2NAUYAHhujozruyzpsFRP63mbrdaU5wnEpN",
network: "testnet",
format: "p2sh-p2wpkh",
},
{
privateKey:
"cb3df38053d132895220b9ce471f6b676db5b9bf0b4adefb55f2118ece2478df01",
publicKey:
"03797dd653040d344fd048c1ad05d4cbcb2178b30c6a0c4276994795f3e833da41",
address: "2NEb2fNbJXdwi7EC6vKCjWUTA12PABNniQM",
network: "testnet",
format: "p2sh-p2wpkh",
},
{
privateKey:
"0000000000000000000000000000000000000000000000000000000000000001",
publicKey:
"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
address: "344YToRR99ER5CRo975kXTUAnYcBrVxQYm",
network: "mainnet",
format: "p2sh-p2wsh",
},
{
privateKey:
"0000000000000000000000000000000000000000000000000000000000000001",
publicKey:
"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
address: "2MuckXYMSkbjmGz4LpEhd9QTRztpMceVskG",
network: "testnet",
format: "p2sh-p2wsh",
},
{
privateKey:
"cb3df38053d132895220b9ce471f6b676db5b9bf0b4adefb55f2118ece2478df01",
publicKey:
"03797dd653040d344fd048c1ad05d4cbcb2178b30c6a0c4276994795f3e833da41",
address: "2MvggQUqkG6weTSxTcpwvqh5gSow4zKjkcL",
network: "testnet",
format: "p2sh-p2wsh",
},
// Test vectors from https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki#examples
{
privateKey:
"0000000000000000000000000000000000000000000000000000000000000001",
publicKey:
"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
address: "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4",
network: "mainnet",
format: "p2wpkh",
},
{
privateKey:
"0000000000000000000000000000000000000000000000000000000000000001",
publicKey:
"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
address: "tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx",
network: "testnet",
format: "p2wpkh",
},
{
privateKey:
"cb3df38053d132895220b9ce471f6b676db5b9bf0b4adefb55f2118ece2478df01",
publicKey:
"03797dd653040d344fd048c1ad05d4cbcb2178b30c6a0c4276994795f3e833da41",
address: "tb1qzepy04hjksj6c4m3ggawdjqvw48hzu4swvwmvt",
network: "testnet",
format: "p2wpkh",
},
{
privateKey:
"0000000000000000000000000000000000000000000000000000000000000001",
publicKey:
"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
address: "bc1q9qs9xv7mjghkd69fgx62xttxmeww5q7eekjxu0nxtzf4yu4ekf8s4plngs",
network: "mainnet",
format: "p2wsh",
},
{
privateKey:
"0000000000000000000000000000000000000000000000000000000000000001",
publicKey:
"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
address: "tb1q9qs9xv7mjghkd69fgx62xttxmeww5q7eekjxu0nxtzf4yu4ekf8szffujl",
network: "testnet",
format: "p2wsh",
},
{
privateKey:
"cb3df38053d132895220b9ce471f6b676db5b9bf0b4adefb55f2118ece2478df01",
publicKey:
"03797dd653040d344fd048c1ad05d4cbcb2178b30c6a0c4276994795f3e833da41",
address: "tb1qczxqnu5hx2zvxcqt3lmr6vjju4ysf7d649mvrzd8v7l3jez0dqzql2ek5y",
network: "testnet",
format: "p2wsh",
},
// {
// // Vector from https://github.com/bitcoin/bitcoin/blob/master/src/test/data/bip341_wallet_vectors.json
// privateKey: null, // how can we generate this?
// publicKey:
// "d6889cb081036e0faefa3a35157ad71086b123b2b144b649798b494c300a961d", // should be 33 bytes?
// address: "bc1p2wsldez5mud2yam29q22wgfh9439spgduvct83k3pm50fcxa5dps59h4z5",
// network: "mainnet",
// format: "p2tr",
// },
{
comment:
"Vector from https://github.com/chaintope/bitcoinrb/blob/c6d2cf564f069e37301b7ba5cd2ff8a25b94dbfe/spec/bitcoin/taproot/simple_builder_spec.rb#L31",
privateKey:
"0000000000000000000000000000000000000000000000000000000000000001",
publicKey:
"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
address: "bc1pmfr3p9j00pfxjh0zmgp99y8zftmd3s5pmedqhyptwy6lm87hf5sspknck9",
network: "mainnet",
format: "p2tr",
},
{
comment: "Vector from locally verified regtest/krypton accounts",
privateKey:
"cb3df38053d132895220b9ce471f6b676db5b9bf0b4adefb55f2118ece2478df01",
publicKey:
"03797dd653040d344fd048c1ad05d4cbcb2178b30c6a0c4276994795f3e833da41",
address: "tb1p8dlmzllfah294ntwatr8j5uuvcj7yg0dete94ck2krrk0ka2c9qqex96hv",
network: "testnet",
format: "p2tr",
},
];
for (const opts of BTC_ADDRESS_CASES_API) {
generateTestVector(opts);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment