@bsv/teranode-listener
v1.0.1
Published
An npm package to subscribe to Teranode P2P topics in a private DHT network and log messages
Readme
@bsv/teranode-listener
BSV BLOCKCHAIN | A TypeScript library for subscribing to Teranode P2P topics in a private DHT network
A robust npm package that enables subscription to Teranode P2P topics using libp2p with private network support, DHT, and gossipsub messaging.
Table of Contents
- Overview
- Installation
- Getting Started
- API Reference
- Configuration
- Examples
- Development
- Contributing
- Support & Contacts
Overview
The @bsv/teranode-listener package provides a simple yet powerful interface for connecting to Teranode's private P2P network. It handles:
- Private Network Access: Secure connections using pre-shared keys (PSK)
- DHT Integration: Distributed hash table for peer discovery
- Topic Subscription: Subscribe to specific topics and receive real-time messages
- Peer Management: Automatic peer discovery and connection management
- Message Logging: Built-in logging for network events and messages
Installation
npm install @bsv/teranode-listenerRequirements
- Node.js: Version 18.0.0 or higher
- ES Modules: This package is published as an ES module. Ensure your project supports ES modules by either:
- Adding
"type": "module"to yourpackage.json, or - Using
.mjsfile extensions for your JavaScript files
- Adding
Getting Started
Callback-Based API (Recommended)
The easiest way to use the library is with the TeranodeListener class, which provides topic-specific callbacks:
import { TeranodeListener } from '@bsv/teranode-listener';
// Define callback functions for different topics
const blockCallback = (data: Uint8Array, topic: string, from: string) => {
console.log(`New block received from ${from}:`, data);
// Process block data here
};
const subtreeCallback = (data: Uint8Array, topic: string, from: string) => {
console.log(`Subtree update from ${from}:`, data);
// Process subtree data here
};
// Create listener with topic callbacks
const listener = new TeranodeListener({
'bitcoin/mainnet-block': blockCallback,
'bitcoin/mainnet-subtree': subtreeCallback
});
// The listener starts automatically and connects to Teranode mainnet
console.log('Listener started and waiting for messages...');Function-Based API
Alternatively, you can use the original function-based API:
import { startSubscriber } from '@bsv/teranode-listener';
// Start with default configuration (connects to Teranode mainnet)
const { node, stop } = await startSubscriber({
onMessage: (data, topic, from) => {
console.log(`Message on ${topic} from ${from}:`, data);
}
});
console.log('Subscriber started and listening for messages...');Both approaches automatically:
- Connect to the official Teranode bootstrap peer
- Use the mainnet shared key
- Listen on
127.0.0.1:9901 - Connect to known active Teranode peers
Custom Configuration
import { startSubscriber } from '@bsv/teranode-listener';
const config = {
topics: ['teranode/blocks'], // Only subscribe to blocks
listenAddresses: ['/ip4/0.0.0.0/tcp/4000'] // Listen on a different port
};
// Start with custom topics and port
await startSubscriber(config);
console.log('Subscriber started with custom configuration...');Complete Custom Setup
import { startSubscriber } from '@bsv/teranode-listener';
const config = {
bootstrapPeers: [
'/ip4/127.0.0.1/tcp/4001/p2p/12D3KooWExample1'
],
staticPeers: [
'/ip4/192.168.1.100/tcp/4003/p2p/12D3KooWStatic1',
'/ip4/192.168.1.101/tcp/4003/p2p/12D3KooWStatic2'
],
sharedKey: 'your-custom-hex-shared-key-here',
topics: ['custom/topic'],
listenAddresses: ['/ip4/0.0.0.0/tcp/4000'],
dhtProtocolID: '/custom-protocol'
};
await startSubscriber(config);For more detailed examples, check our Examples section.
API Reference
TeranodeListener Class (Recommended)
The primary API for subscribing to Teranode P2P topics with callback functions.
Constructor
new TeranodeListener(topicCallbacks: TopicCallbacks, config?: TeranodeListenerConfig)Parameters:
topicCallbacks- Object mapping topic names to callback functionsconfig- Optional configuration (uses Teranode mainnet defaults)
Example:
const listener = new TeranodeListener({
'bitcoin/mainnet-block': (data, topic, from) => {
console.log('Block received:', data);
},
'bitcoin/mainnet-subtree': (data, topic, from) => {
console.log('Subtree update:', data);
}
});Methods
addTopicCallback(topic: Topic, callback: MessageCallback): void- Add a new topic subscriptionremoveTopicCallback(topic: Topic): void- Remove a topic subscriptionstop(): Promise<void>- Stop the listenergetNode(): Libp2p | null- Get the underlying libp2p nodegetConnectedPeerCount(): number- Get number of connected peers
Types
// Supported Teranode P2P topics
export type Topic =
'bitcoin/mainnet-bestblock' | // Best block message
'bitcoin/mainnet-block' | // When miners find a block solution
'bitcoin/mainnet-subtree' | // When a subtree is created
'bitcoin/mainnet-mining_on' | // When mining is enabled
'bitcoin/mainnet-handshake' | // When a peer connects to the network
'bitcoin/mainnet-rejected_tx'; // When a transaction is rejected
type MessageCallback = (data: Uint8Array, topic: Topic, from: string) => void;
type TopicCallbacks = Partial<Record<Topic, MessageCallback>>;
interface TeranodeListenerConfig {
bootstrapPeers?: string[]; // Bootstrap peer multiaddrs (default: Teranode mainnet bootstrap)
staticPeers?: string[]; // Static peer multiaddrs (default: Known Teranode mainnet peers)
sharedKey?: string; // Hex string of PSK (default: Teranode mainnet key)
dhtProtocolID?: string; // DHT protocol prefix (default: '/teranode')
listenAddresses?: string[]; // Listen addresses (default: ['/ip4/127.0.0.1/tcp/9901'])
usePrivateDHT?: boolean; // Whether to use private DHT (default: true)
}startSubscriber(config?: SubscriberConfig): Promise<void>
Legacy function-based API for subscribing to topics.
Parameters
config- Optional configuration object for the subscriber. If not provided, uses mainnet defaults.
Returns
A Promise that resolves when the subscriber is successfully started.
SubscriberConfig
Configuration interface for the function-based API. All parameters are optional:
interface SubscriberConfig {
bootstrapPeers?: string[]; // Bootstrap peer multiaddrs (default: Teranode mainnet bootstrap)
staticPeers?: string[]; // Static peer multiaddrs (default: Known Teranode mainnet peers)
sharedKey?: string; // Hex string of PSK (default: Teranode mainnet key)
dhtProtocolID?: string; // DHT protocol prefix (default: '/teranode')
topics?: Topic[]; // Topics to subscribe to (default: all Teranode topics)
listenAddresses?: string[]; // Listen addresses (default: ['/ip4/127.0.0.1/tcp/9901'])
usePrivateDHT?: boolean; // Whether to use private DHT (default: true)
}Configuration
Default Configuration
The package comes with production-ready defaults for Teranode mainnet:
bootstrapPeers:['/dns4/teranode-bootstrap.bsvb.tech/tcp/9901/p2p/12D3KooWESmhNAN8s6NPdGNvJH3zJ4wMKDxapXKNUe2DzkAwKYqK']staticPeers: Array of known active Teranode mainnet peers (TAAL, BSVB, etc.)sharedKey: Teranode mainnet pre-shared keytopics:['teranode/blocks', 'teranode/transactions']listenAddresses:['/ip4/127.0.0.1/tcp/9901']dhtProtocolID:/teranodeusePrivateDHT:true
Customizable Parameters
All parameters are optional and can be overridden:
bootstrapPeers: Array of multiaddr strings for initial peer discoverystaticPeers: Additional peers to maintain persistent connections withsharedKey: Hexadecimal string representing the pre-shared key for network accesstopics: Array of topic strings to subscribe tolistenAddresses: Network addresses to listen ondhtProtocolID: Custom DHT protocol identifierusePrivateDHT: Whether to use private DHT networking
Pre-Shared Key Format
The sharedKey should be provided as a hexadecimal string without the PSK headers. The library automatically formats it as:
/key/swarm/psk/1.0.0/
/base16/
<your-hex-key>Examples
Example 1: Basic TeranodeListener Usage
import { TeranodeListener, type Topic } from '@bsv/teranode-listener';
// Simple callback-based listener
const listener = new TeranodeListener({
'bitcoin/mainnet-block': (data: Uint8Array, topic: Topic, from: string) => {
console.log(`New block from ${from}:`, data.length, 'bytes');
// Process block data
},
'bitcoin/mainnet-subtree': (data: Uint8Array, topic: Topic, from: string) => {
console.log(`Subtree update from ${from}:`, data.length, 'bytes');
// Process subtree data
}
});
console.log('Listener started, waiting for messages...');Example 2: Advanced TeranodeListener with Custom Configuration
import { TeranodeListener } from '@bsv/teranode-listener';
// Create a listener with topic-specific callbacks
const listener = new TeranodeListener({
'bitcoin/mainnet-block': (data, topic, from) => {
console.log(`Received block from ${from}:`, data);
},
'bitcoin/mainnet-subtree': (data, topic, from) => {
console.log(`Received subtree from ${from}:`, data);
}
});
// The listener starts automatically
console.log('Connected peers:', listener.getConnectedPeerCount());
// Add more topics dynamically
listener.addTopicCallback('bitcoin/mainnet-transaction', (data, topic, from) => {
console.log(`Received transaction from ${from}:`, data);
});
// Monitor connection status
setInterval(() => {
console.log('Connected peers:', listener.getConnectedPeerCount());
}, 30000);Example 3: Function-Based API (Legacy)
import { startSubscriber } from '@bsv/teranode-listener';
// Connect to Teranode mainnet with all defaults
startSubscriber()
.then(() => console.log('Connected to Teranode mainnet!'))
.catch(console.error);Example 4: Custom Port and Multiple Topics (Function API)
import { startSubscriber } from '@bsv/teranode-listener';
// Use a different port and subscribe to multiple topics
const config = {
topics: [
'teranode/blocks',
'teranode/transactions',
'teranode/mempool'
],
listenAddresses: ['/ip4/0.0.0.0/tcp/4000']
};
await startSubscriber(config);
console.log('Listening on port 4000 for blocks, transactions, and mempool...');Example 5: Environment-Based Configuration
import { startSubscriber } from '@bsv/teranode-listener';
const config = {
topics: process.env.TOPICS?.split(',') || undefined, // Use defaults if not set
listenAddresses: process.env.LISTEN_ADDRESS ? [process.env.LISTEN_ADDRESS] : undefined,
sharedKey: process.env.CUSTOM_SHARED_KEY || undefined // Use default mainnet key if not set
};
// Start with environment overrides, falling back to defaults
await startSubscriber(config);
console.log('Started with environment configuration...');Example 6: Complete Custom Network
import { startSubscriber } from '@bsv/teranode-listener';
// Connect to a custom private network
const config = {
bootstrapPeers: [
'/ip4/10.0.0.1/tcp/4001/p2p/12D3KooWBootstrap1',
'/ip4/10.0.0.2/tcp/4001/p2p/12D3KooWBootstrap2'
],
staticPeers: [
'/ip4/10.0.0.10/tcp/4003/p2p/12D3KooWStatic1'
],
sharedKey: 'your-custom-private-network-key',
dhtProtocolID: '/custom-network',
topics: ['custom/blocks', 'custom/transactions'],
listenAddresses: ['/ip4/0.0.0.0/tcp/4000'],
usePrivateDHT: true
};
await startSubscriber(config);
console.log('Connected to custom private network...');Development
Building from Source
# Clone the repository
git clone https://github.com/bitcoin-sv/ts-p2p.git
cd ts-p2p
# Install dependencies
npm install
# Build the project
npm run buildProject Structure
ts-p2p/
├── src/
│ └── index.ts # Main library code
├── dist/ # Compiled JavaScript output
├── package.json # Package configuration
├── tsconfig.json # TypeScript configuration
└── README.md # This fileDependencies
This package relies on several key libp2p modules:
- libp2p: Core P2P networking library
- @chainsafe/libp2p-gossipsub: Gossip-based pub/sub messaging
- @libp2p/kad-dht: Kademlia DHT for peer discovery
- @libp2p/pnet: Private network support with PSK
- @chainsafe/libp2p-noise: Noise protocol for secure connections
Contributing
We welcome contributions to improve the @bsv/teranode-listener package. Whether it's bug reports, feature requests, or pull requests - all contributions are appreciated.
How to Contribute
- Fork the repository - Start by forking the project repository to your GitHub account
- Clone the repository - Clone the forked repository to your local machine
- Create a new branch - Create a new branch for your feature or bug fix
- Make your changes - Implement your changes with appropriate tests
- Build and test - Ensure the project builds and all tests pass
- Submit a pull request - Submit a pull request with a clear description
Development Guidelines
- Follow TypeScript best practices
- Maintain backward compatibility when possible
- Add tests for new features
- Update documentation as needed
- Follow the existing code style and conventions
Support & Contacts
Project Maintainers:
For questions, bug reports, or feature requests:
- Open an issue on GitHub
- Check existing documentation
License
This project is licensed under the MIT License. See the LICENSE file for details.
Thank you for being a part of the BSV Blockchain ecosystem. Let's build the future of BSV Blockchain together!
