From 5e7702e5093d9ae001e076c468bf766c5ccea32e Mon Sep 17 00:00:00 2001 From: Mike Carson Date: Fri, 6 Dec 2024 18:05:55 +0000 Subject: [PATCH] changes for mainnet; to schnorr for signing, to spaceHash --- README.md | 13 +++---------- bin/common.ts | 2 +- constants.ts | 12 +++++++++--- index.ts | 4 +++- schnorr.ts | 48 +++++++++++++++++++++++++----------------------- spaces.ts | 2 +- types.ts | 9 +++++++++ utils.ts | 9 ++++----- 8 files changed, 55 insertions(+), 44 deletions(-) create mode 100644 types.ts diff --git a/README.md b/README.md index 0fbb812..5b7bbee 100644 --- a/README.md +++ b/README.md @@ -7,12 +7,10 @@ Fabric is a trustless, distributed DNS resolver built on top of [hyperdht](https://github.com/holepunchto/hyperdht), extending its capabilities to allow publishing signed zone files using [spaces](https://spacesprotocol.org) as keys authenticated by Bitcoin. [Spaces](https://spacesprotocol.org) are sovereign Bitcoin identities and serve as a trust anchor, while Fabric DHT enables publishing records off-chain without adding any unnecessary on-chain bloat. -**Note:** Fabric currently defaults to Bitcoin testnet4 since spaces are not yet on Bitcoin mainnet. - ## Prerequisites To use `fabric` and `beam`, you need to: -- Run Bitcoin Core on testnet4 +- Run Bitcoin Core on mainnet - Install and sync spaces You may use [this guide](https://docs.spacesprotocol.org/getting-started/installation) to set these up. @@ -36,9 +34,6 @@ beam @onion TXT Space @now also has TXT records published. -**Note**: `beam` will automatically connect to a locally run spaces node using its default port for `testnet4` to verify answers from the DHT. - - ## How to publish records for a Space? 1. Create a zone file (e.g., example.zone) with an SOA record and the records you want to publish: @@ -55,13 +50,13 @@ Space @now also has TXT records published. 2. Find the space's private key using `space-cli` ```shell -space-cli --chain testnet4 exportwallet | grep '"spaces_descriptor"' | sed -E 's/.*(tprv[^\/]*).*/\1/' +space-cli --chain mainnet exportwallet | grep '"descriptor"' | sed -E 's/.*(xprv[^\/]*).*/\1/' ``` it should look something like this: ``` -tprv8ZgxMBicQKsPeUUxV746bQ9JmsytoyEeioAd9b962bQxcq7PfK8vRbFkSR7JD7ySoBoyswHX5vQvnhS95dHKUxW2maG2Tt7bJcCHsY66gNF +xprv8ZgxMBicQKsPeUUxV746bQ9JmsytoyEeioAd9b962bQxcq7PfK8vRbFkSR7JD7ySoBoyswHX5vQvnhS95dHKUxW2maG2Tt7bJcCHsY66gNF ``` @@ -83,8 +78,6 @@ You can either: Run a node if you want to publish your own zones and also contribute to the network. Specify a reachable ip/port: -**Note**: Fabric will automatically connect to a locally run spaces node using its default port for testnet4. - ``` fabric --host --port ``` diff --git a/bin/common.ts b/bin/common.ts index d83aefb..99879bb 100644 --- a/bin/common.ts +++ b/bin/common.ts @@ -20,7 +20,7 @@ export function defineMainOptions() { .version('0.1.0') .option('--seeds ', 'Connect to the following bootstrap nodes') .option('--peers ', 'Include the following known peers') - .option('--chain ', 'Bitcoin network', 'testnet4') + .option('--chain ', 'Bitcoin network', 'mainnet') .option('--spaces-rpc-url ', 'Specify a spaces rpc url (default based on chain)'); } diff --git a/constants.ts b/constants.ts index 4adeb1e..1818fcd 100644 --- a/constants.ts +++ b/constants.ts @@ -1,8 +1,14 @@ import * as crypto from 'hypercore-crypto'; +import { BootstrapNode, BootstrapNodes } from './types'; -export const BOOTSTRAP_NODES = [ - '107.152.45.120@testnet4.fabric.buffrr.dev:22253', -] +export const BOOTSTRAP_NODES = { + mainnet: [ + { host: '44.209.201.250', port: 40357 } + ], + testnet4: [ + { host: '107.152.45.120', port: 22253 } + ] +}; // Extend the existing COMMANDS from hyperdht/lib/constants export const COMMANDS = { diff --git a/index.ts b/index.ts index ebb7f62..0bbf51f 100644 --- a/index.ts +++ b/index.ts @@ -7,6 +7,7 @@ import b4a from 'b4a'; import {Spaces} from './spaces'; import * as m from './messages'; import {DHT} from "dht-rpc"; +import { BootstrapNode, BootstrapNodes } from './types'; const defaultMaxSize = 32768; const defaultMaxAge = 48 * 60 * 60 * 1000; // 48 hours @@ -52,7 +53,8 @@ export class Fabric extends HyperDHT { public spaces: Spaces; constructor(opts: FabricOptions = {}) { - opts.bootstrap = opts.bootstrap || BOOTSTRAP_NODES + const chain = (opts.spaces?.resolver?.chain || 'mainnet') as keyof BootstrapNodes; + opts.bootstrap = opts.bootstrap || BOOTSTRAP_NODES[chain].map((n: BootstrapNode) => `${n.host}:${n.port}`); super(opts); this.once('persistent', () => { this._zones = new Cache(opts.zones || { diff --git a/schnorr.ts b/schnorr.ts index 54a0a22..961b275 100644 --- a/schnorr.ts +++ b/schnorr.ts @@ -14,31 +14,32 @@ interface TweakedKeyPair { // Finds the tweaked private key from an extended private key matching a given address export function findTweakedPair(xprv: string, derivedAddress: string): TweakedKeyPair | null { - const path = `${prefix}'/86'/1'/0'/0/*`; - const network = xprv.startsWith('tprv') ? bitcoin.networks.testnet : bitcoin.networks.bitcoin; - const node: BIP32Interface = bip32.fromBase58(xprv, network); + const network = xprv.startsWith('tprv') ? bitcoin.networks.testnet : bitcoin.networks.bitcoin; + const path = `86'/0'/0'/0/*`; + + const rootNode: BIP32Interface = bip32.fromBase58(xprv, network); + const pathComponents = path.split('/').filter(component => component !== '*'); + const baseNode: BIP32Interface = pathComponents.reduce((acc: BIP32Interface, component: string) => { + const isHardened = component.endsWith('\''); + const index = parseInt(component, 10); - const pathComponents = path.split('/').filter(component => component !== '*'); - const baseNode: BIP32Interface = pathComponents.reduce((acc: BIP32Interface, component: string) => { - const isHardened = component.endsWith('\''); - const index = parseInt(component, 10); - return isHardened ? acc.deriveHardened(index) : acc.derive(index); - }, node); + return isHardened ? acc.deriveHardened(index) : acc.derive(index); + }, rootNode); - const maxDerivations = 1000; - for (let i = 0; i < maxDerivations; i++) { - const child = baseNode.derive(i); - const pubkey = child.publicKey.slice(1, 33); - const {address} = bitcoin.payments.p2tr({ - internalPubkey: pubkey, - network, - }); - if (!address) continue; + const MAX_DERIVATIONS = 1000; + for (let i = 0; i < MAX_DERIVATIONS; i++) { + const child = baseNode.derive(i); + const pubkey = child.publicKey.slice(1, 33); + const {address} = bitcoin.payments.p2tr({ + internalPubkey: pubkey, + network, + }); + if (!address) continue; const tweakHash = bitcoin.crypto.taggedHash('TapTweak', pubkey); - const tweakedPriv = child.tweak(tweakHash) as BIP32Interface; + const tweakedPriv = child.tweak(tweakHash) as BIP32Interface; if (address === derivedAddress || spaceAddress(address) === derivedAddress) { - return { + return { privateKey: tweakedPriv.privateKey!, publicKey: tweakedPriv.publicKey!.slice(1), }; @@ -65,10 +66,11 @@ export function scriptPubKeyToAddress(scriptPubKey: string, network: string = 'm // Adjusts address prefix for the space network function spaceAddress(address: string): string { const decoded = bech32m.decode(address); - if (decoded.prefix !== 'tb' && decoded.prefix !== 'bc') { - throw new Error('Invalid address prefix'); + // Handle bc -> bcs for spaces + if (decoded.prefix === 'bc') { + return bech32m.encode('bcs', decoded.words); } - return bech32m.encode(`${decoded.prefix}s`, decoded.words); + return address; } // Schnorr signing diff --git a/spaces.ts b/spaces.ts index f5dda20..25e7105 100644 --- a/spaces.ts +++ b/spaces.ts @@ -86,7 +86,7 @@ export class Resolver { rpcUrl: URL; constructor(opts: ResolverOptions = {}) { - this.chain = opts.chain || 'testnet4'; + this.chain = opts.chain || 'mainnet'; this.rpcUrl = new URL(opts.rpcUrl || `http://localhost:${this.default_rpc_port()}`); } diff --git a/types.ts b/types.ts new file mode 100644 index 0000000..ea6b4ac --- /dev/null +++ b/types.ts @@ -0,0 +1,9 @@ +export interface BootstrapNode { + host: string; + port: number; +} + +export interface BootstrapNodes { + mainnet: BootstrapNode[]; + testnet4: BootstrapNode[]; +} diff --git a/utils.ts b/utils.ts index a4c50f9..cc4ed09 100644 --- a/utils.ts +++ b/utils.ts @@ -1,12 +1,11 @@ import crypto from 'crypto'; export function spaceHash(spaceName: string): Buffer { - const byteArray = Buffer.from(spaceName, 'utf8'); + const label = spaceName.startsWith('@') ? spaceName.slice(1) : spaceName; + const byteArray = Buffer.from(label, 'utf8'); const lengthPrefix = Buffer.from([byteArray.length]); - const lengthPrefixedByteArray = Buffer.concat([lengthPrefix, byteArray]); - const finalByteArray = Buffer.concat([lengthPrefixedByteArray, Buffer.from([0])]); - + const finalByteArray = Buffer.concat([lengthPrefix, byteArray]); const base = Buffer.from(crypto.createHash('sha256').update(finalByteArray).digest('hex'), 'hex'); base[0] &= 0x7f; return base; -} \ No newline at end of file +}