feat: add signet support, closes #3771

This commit is contained in:
kyranjamie
2023-05-29 17:17:39 +02:00
committed by Anastasios
parent 8c2bad3948
commit 2800623fac
11 changed files with 59 additions and 32 deletions

View File

@@ -291,14 +291,8 @@ export function whenNetwork(mode: NetworkModes) {
return <T>(networkMap: NetworkMap<T>): T => networkMap[mode];
}
const bitcoinNetworkToCoreNetworkMap: Record<BitcoinNetworkModes, NetworkModes> = {
mainnet: 'mainnet',
testnet: 'testnet',
regtest: 'testnet',
signet: 'testnet',
};
export function bitcoinNetworkModeToCoreNetworkMode(mode: BitcoinNetworkModes) {
return bitcoinNetworkToCoreNetworkMap[mode];
export function whenBitcoinNetwork(mode: BitcoinNetworkModes) {
return <T>(networkMap: Record<BitcoinNetworkModes, T>): T => networkMap[mode];
}
export function logAndThrow(msg: string, args: any[] = []) {

View File

@@ -2,6 +2,7 @@ import { AddressType, Network, getAddressInfo, validate } from 'bitcoin-address-
import * as yup from 'yup';
import { BitcoinNetworkModes, NetworkConfiguration } from '@shared/constants';
import { bitcoinNetworkModeToCoreNetworkMode } from '@shared/crypto/bitcoin/bitcoin.utils';
import { isString } from '@shared/utils';
import { FormErrorMessages } from '@app/common/error-messages';
@@ -46,7 +47,7 @@ export function btcTaprootAddressValidator() {
function btcAddressNetworkValidatorFactory(network: BitcoinNetworkModes) {
return (value?: string) => {
if (!isString(value)) return false;
return validate(value, network as Network);
return validate(value, bitcoinNetworkModeToCoreNetworkMode(network) as Network);
};
}

View File

@@ -1,8 +1,9 @@
import { useSelector } from 'react-redux';
import { bitcoinNetworkModeToCoreNetworkMode } from '@shared/crypto/bitcoin/bitcoin.utils';
import { getNativeSegWitPaymentFromAddressIndex } from '@shared/crypto/bitcoin/p2wpkh-address-gen';
import { bitcoinNetworkModeToCoreNetworkMode, whenNetwork } from '@app/common/utils';
import { whenNetwork } from '@app/common/utils';
import { useCurrentNetwork } from '@app/store/networks/networks.selectors';
import { useCurrentAccountIndex } from '../../account';

View File

@@ -1,9 +1,10 @@
import { useSelector } from 'react-redux';
import { BitcoinNetworkModes } from '@shared/constants';
import { bitcoinNetworkModeToCoreNetworkMode } from '@shared/crypto/bitcoin/bitcoin.utils';
import { getTaprootPaymentFromAddressIndex } from '@shared/crypto/bitcoin/p2tr-address-gen';
import { bitcoinNetworkModeToCoreNetworkMode, whenNetwork } from '@app/common/utils';
import { whenNetwork } from '@app/common/utils';
import { useCurrentNetwork } from '@app/store/networks/networks.selectors';
import { useCurrentAccountIndex } from '../../account';

View File

@@ -4,12 +4,13 @@ import { ChainID } from '@stacks/transactions';
import {
BITCOIN_API_BASE_URL_MAINNET,
BITCOIN_API_BASE_URL_SIGNET,
BITCOIN_API_BASE_URL_TESTNET,
HIRO_API_BASE_URL_MAINNET,
HIRO_API_BASE_URL_TESTNET,
} from '@shared/constants';
import { whenStacksChainId } from '@app/common/utils';
import { whenBitcoinNetwork, whenStacksChainId } from '@app/common/utils';
import { BitcoinClient } from '@app/query/bitcoin/bitcoin-client';
import { StacksClient } from '@app/query/stacks/stacks-client';
import { TokenMetadataClient } from '@app/query/stacks/token-metadata-client';
@@ -24,9 +25,11 @@ import { useCurrentNetworkState } from '../networks/networks.hooks';
export function useBitcoinClient() {
const network = useCurrentNetworkState();
const baseUrl = whenStacksChainId(network.chain.stacks.chainId)({
[ChainID.Mainnet]: BITCOIN_API_BASE_URL_MAINNET,
[ChainID.Testnet]: BITCOIN_API_BASE_URL_TESTNET,
const baseUrl = whenBitcoinNetwork(network.chain.bitcoin.network)({
mainnet: BITCOIN_API_BASE_URL_MAINNET,
testnet: BITCOIN_API_BASE_URL_TESTNET,
regtest: BITCOIN_API_BASE_URL_TESTNET,
signet: BITCOIN_API_BASE_URL_SIGNET,
});
return new BitcoinClient(baseUrl);

View File

@@ -32,6 +32,7 @@ export const GITHUB_REPO = 'wallet';
export enum WalletDefaultNetworkConfigurationIds {
mainnet = 'mainnet',
testnet = 'testnet',
signet = 'signet',
devnet = 'devnet',
}
@@ -73,6 +74,7 @@ export const HIRO_API_BASE_URL_TESTNET = 'https://api.testnet.hiro.so';
export const BITCOIN_API_BASE_URL_MAINNET = 'https://blockstream.info/api';
export const BITCOIN_API_BASE_URL_TESTNET = 'https://blockstream.info/testnet/api';
export const BITCOIN_API_BASE_URL_SIGNET = 'https://mempool.space/signet/api';
const networkMainnet: NetworkConfiguration = {
id: WalletDefaultNetworkConfigurationIds.mainnet,
@@ -108,6 +110,23 @@ const networkTestnet: NetworkConfiguration = {
},
};
const networkSignet: NetworkConfiguration = {
id: WalletDefaultNetworkConfigurationIds.signet,
name: 'Signet',
chain: {
stacks: {
blockchain: 'stacks',
chainId: ChainID.Testnet,
url: HIRO_API_BASE_URL_TESTNET,
},
bitcoin: {
blockchain: 'bitcoin',
network: 'signet',
url: BITCOIN_API_BASE_URL_SIGNET,
},
},
};
const networkDevnet: NetworkConfiguration = {
id: WalletDefaultNetworkConfigurationIds.devnet,
name: 'Devnet',
@@ -133,6 +152,7 @@ export const defaultNetworksKeyedById: Record<
> = {
[WalletDefaultNetworkConfigurationIds.mainnet]: networkMainnet,
[WalletDefaultNetworkConfigurationIds.testnet]: networkTestnet,
[WalletDefaultNetworkConfigurationIds.signet]: networkSignet,
[WalletDefaultNetworkConfigurationIds.devnet]: networkDevnet,
};

View File

@@ -32,18 +32,13 @@ const bitcoinRegtest: BtcSignerNetwork = {
wif: 0xef,
};
const bitcoinSignet: BtcSignerNetwork = {
bech32: 'sb',
pubKeyHash: 0x3f,
scriptHash: 0x7f,
wif: 0x80,
};
const btcSignerLibNetworks: Record<BitcoinNetworkModes, BtcSignerNetwork> = {
mainnet: bitcoinMainnet,
testnet: bitcoinTestnet,
regtest: bitcoinRegtest,
signet: bitcoinSignet,
// Signet originally was going to have its own prefix but authors decided to
// copy testnet
signet: bitcoinTestnet,
};
export function getBtcSignerLibNetworkConfigByMode(network: BitcoinNetworkModes) {
@@ -54,8 +49,7 @@ const bitcoinJsLibNetworks: Record<BitcoinNetworkModes, bitcoinJs.Network> = {
mainnet: bitcoinJs.networks.bitcoin,
testnet: bitcoinJs.networks.testnet,
regtest: bitcoinJs.networks.regtest,
// Signet doesn't exist in bitcoinjs-lib
signet: bitcoinJs.networks.regtest,
signet: bitcoinJs.networks.testnet,
};
export function getBitcoinJsLibNetworkConfigByMode(network: BitcoinNetworkModes) {

View File

@@ -9,13 +9,23 @@ import { logger } from '@shared/logger';
import { DerivationPathDepth } from '../derivation-path.utils';
import { BtcSignerNetwork, getBtcSignerLibNetworkConfigByMode } from './bitcoin.network';
const bitcoinNetworkToCoreNetworkMap: Record<BitcoinNetworkModes, NetworkModes> = {
mainnet: 'mainnet',
testnet: 'testnet',
regtest: 'testnet',
signet: 'testnet',
};
export function bitcoinNetworkModeToCoreNetworkMode(mode: BitcoinNetworkModes) {
return bitcoinNetworkToCoreNetworkMap[mode];
}
const coinTypeMap: Record<NetworkModes, 0 | 1> = {
mainnet: 0,
testnet: 1,
};
export function getBitcoinCoinTypeIndexByNetwork(network: NetworkModes) {
return coinTypeMap[network];
export function getBitcoinCoinTypeIndexByNetwork(network: BitcoinNetworkModes) {
return coinTypeMap[bitcoinNetworkModeToCoreNetworkMode(network)];
}
export function deriveAddressIndexKeychainFromAccount(keychain: HDKey) {

View File

@@ -1,17 +1,20 @@
import { HDKey } from '@scure/bip32';
import * as btc from '@scure/btc-signer';
import { BitcoinNetworkModes, NetworkModes } from '@shared/constants';
import { BitcoinNetworkModes } from '@shared/constants';
import { DerivationPathDepth } from '../derivation-path.utils';
import { getBtcSignerLibNetworkConfigByMode } from './bitcoin.network';
import { ecdsaPublicKeyToSchnorr, getBitcoinCoinTypeIndexByNetwork } from './bitcoin.utils';
function getTaprootAccountDerivationPath(network: NetworkModes, accountIndex: number) {
function getTaprootAccountDerivationPath(network: BitcoinNetworkModes, accountIndex: number) {
return `m/86'/${getBitcoinCoinTypeIndexByNetwork(network)}'/${accountIndex}'`;
}
export function deriveTaprootAccountFromRootKeychain(keychain: HDKey, network: NetworkModes) {
export function deriveTaprootAccountFromRootKeychain(
keychain: HDKey,
network: BitcoinNetworkModes
) {
if (keychain.depth !== DerivationPathDepth.Root)
throw new Error('Keychain passed is not an account');

View File

@@ -10,7 +10,7 @@ import {
getBitcoinCoinTypeIndexByNetwork,
} from './bitcoin.utils';
function getNativeSegWitAccountDerivationPath(network: NetworkModes, accountIndex: number) {
function getNativeSegWitAccountDerivationPath(network: BitcoinNetworkModes, accountIndex: number) {
return `m/84'/${getBitcoinCoinTypeIndexByNetwork(network)}'/${accountIndex}'`;
}

View File

@@ -75,6 +75,6 @@ describe(`Settings integration tests`, () => {
const networkListItems = await wallet.page.$$(
createTestSelector(SettingsSelectors.NetworkListItem)
);
expect(networkListItems).toHaveLength(3);
expect(networkListItems).toHaveLength(4);
});
});