Skip to content

Commit

Permalink
feat: function to convert pox address details to a btc address
Browse files Browse the repository at this point in the history
  • Loading branch information
zone117x committed May 17, 2021
1 parent f605ec8 commit b6ae9ef
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 5 deletions.
52 changes: 48 additions & 4 deletions packages/stacking/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,49 @@ export class InvalidAddressError extends Error {
}
}

export const BitcoinNetworkVersion = {
mainnet: {
P2PKH: 0x00, // 0
P2SH: 0x05, // 5
},
testnet: {
P2PKH: 0x6f, // 111
P2SH: 0xc4, // 196
},
} as const;

export function btcAddressVersionToHashMode(btcAddressVersion: number): AddressHashMode {
switch (btcAddressVersion) {
case 0: // btc mainnet P2PKH
case BitcoinNetworkVersion.mainnet.P2PKH:
return AddressHashMode.SerializeP2PKH;
case 111: // btc mainnet P2PKH
case BitcoinNetworkVersion.testnet.P2PKH:
return AddressHashMode.SerializeP2PKH;
case 5: // btc mainnet P2SH
case BitcoinNetworkVersion.mainnet.P2SH:
return AddressHashMode.SerializeP2SH;
case 196: // btc testnet P2SH
case BitcoinNetworkVersion.testnet.P2SH:
return AddressHashMode.SerializeP2SH;
default:
throw new Error('Invalid pox address version');
}
}

export function hashModeToBtcAddressVersion(
hashMode: AddressHashMode,
network: 'mainnet' | 'testnet'
): number {
if (!['mainnet', 'testnet'].includes(network)) {
throw new Error(`Invalid network argument: ${network}`);
}
switch (hashMode) {
case AddressHashMode.SerializeP2PKH:
return BitcoinNetworkVersion[network].P2PKH;
case AddressHashMode.SerializeP2SH:
return BitcoinNetworkVersion[network].P2SH;
default:
throw new Error(`Invalid pox address hash mode: ${hashMode}`);
}
}

export function getAddressHashMode(btcAddress: string) {
try {
const { version } = address.fromBase58Check(btcAddress);
Expand All @@ -57,6 +85,22 @@ export function decodeBtcAddress(btcAddress: string) {
};
}

export function poxAddressToBtcAddress(
version: Buffer,
hashBytes: Buffer,
network: 'mainnet' | 'testnet'
) {
if (version.byteLength !== 1) {
throw new Error(`Invalid byte length for version buffer: ${version.toString('hex')}`);
}
if (hashBytes.byteLength !== 20) {
throw new Error(`Invalid byte length for hashBytes: ${hashBytes.toString('hex')}`);
}
const btcNetworkVersion = hashModeToBtcAddressVersion(version[0], network);
const btcAddress = address.toBase58Check(hashBytes, btcNetworkVersion);
return btcAddress;
}

export function getBTCAddress(version: Buffer, checksum: Buffer) {
const btcAddress = address.toBase58Check(checksum, new BN(version).toNumber());
return btcAddress;
Expand Down
51 changes: 50 additions & 1 deletion packages/stacking/tests/stacking.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
intCV,
} from '@stacks/transactions';
import { address as btcAddress } from 'bitcoinjs-lib';
import { decodeBtcAddress, getAddressHashMode, InvalidAddressError } from '../src/utils';
import { decodeBtcAddress, getAddressHashMode, InvalidAddressError, poxAddressToBtcAddress } from '../src/utils';

beforeEach(() => {
fetchMock.resetMocks();
Expand Down Expand Up @@ -893,3 +893,52 @@ test('pox address hash mode', async () => {
expect(() => decodeBtcAddress(p2wsh)).toThrowError(InvalidAddressError);
expect(() => decodeBtcAddress(p2wshTestnet)).toThrowError(InvalidAddressError);
})


test('pox address to btc address', () => {
const vectors: {
version: Buffer;
hashBytes: Buffer;
network: 'mainnet' | 'testnet';
expectedBtcAddr: string;
}[] = [
{
version: Buffer.from([0x01]),
hashBytes: Buffer.from('07366658d1e5f0f75c585a17b618b776f4f10a6b', 'hex'),
network: 'mainnet',
expectedBtcAddr: '32M9pegJxqXBoxXSKBN1s7HJUR2YMkMaFg'
},
{
version: Buffer.from([0x01]),
hashBytes: Buffer.from('9b24b88b1334b0a17a99c09470c4df06ffd3ea22', 'hex'),
network: 'mainnet',
expectedBtcAddr: '3FqLegt1Lo1JuhiBAQQiM5WwDdmefTo5zd',
},
{
version: Buffer.from([0x00]),
hashBytes: Buffer.from('fde9c82d7bc43f55e9054438470c3ca8d6e7237f', 'hex'),
network: 'mainnet',
expectedBtcAddr: '1Q9a1zGPfJ4oH5Xaz5wc7BdvWV21fSNkkr',
},
{
version: Buffer.from([0x00]),
hashBytes: Buffer.from('5dc795522f81dcb7eb774a0b8e84b612e3edc141', 'hex'),
network: 'testnet',
expectedBtcAddr: 'mp4pEBdJiMh6aL5Uhs6nZX1XhyZ4V2xrzg',
},
{
version: Buffer.from([0x01]),
hashBytes: Buffer.from('3149c3eba2d21cfdeea56894866b8f4cd11b72ad', 'hex'),
network: 'testnet',
expectedBtcAddr: '2MwjqTzEJodSaoehcxRSqfWrvJMGZHq4tdC',
}
];

vectors.forEach(item => {
const btcAddress = poxAddressToBtcAddress(item.version, item.hashBytes, item.network);
expect(btcAddress).toBe(item.expectedBtcAddr);
const decodedAddress = decodeBtcAddress(btcAddress);
expect(decodedAddress.hashMode).toBe(item.version[0]);
expect(decodedAddress.data.toString('hex')).toBe(item.hashBytes.toString('hex'));
});
});

0 comments on commit b6ae9ef

Please sign in to comment.