Skip to content

twostack/spiffynode4j

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

7 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

SpiffyNode4J

A Java library for connecting to the BitcoinSV P2P network. Handles peer connections, the Bitcoin handshake, message serialization, health monitoring, automatic reconnection, and SPV chain tip tracking.

Ported from SpiffyNode (Dart).

Requirements

  • Java 21+
  • Gradle 8.x

Installation

Gradle (Kotlin DSL):

dependencies {
    implementation("org.twostack:spiffynode4j:0.1.0-SNAPSHOT")
}

Gradle (Groovy):

implementation 'org.twostack:spiffynode4j:0.1.0-SNAPSHOT'

Quick Start

Connect to a Peer

import org.twostack.spiffynode4j.api.*;
import org.twostack.spiffynode4j.core.*;
import org.twostack.spiffynode4j.config.*;
import org.twostack.spiffynode4j.model.*;
import org.twostack.spiffynode4j.protocol.messages.*;

// 1. Register message types (once at startup)
MsgVersion.registerWithFactory();
MsgVerAck.registerWithFactory();
MsgPing.registerWithFactory();
MsgPong.registerWithFactory();
MsgInv.registerWithFactory();
MsgGetData.registerWithFactory();
MsgTx.registerWithFactory();
MsgBlock.registerWithFactory();
MsgHeaders.registerWithFactory();

// 2. Define your handler
PeerHandler handler = new PeerHandler.Default() {
    @Override
    public CompletableFuture<Void> handleTransaction(WireMessage msg, Peer peer) {
        MsgTx tx = (MsgTx) msg;
        System.out.println("Received TX: " + tx.getTxId());
        return CompletableFuture.completedFuture(null);
    }

    @Override
    public CompletableFuture<Void> handleBlockAnnouncement(InvVect item, Peer peer) {
        System.out.println("New block: " + item.hash());
        return CompletableFuture.completedFuture(null);
    }
};

// 3. Create the manager and connect
PeerManagerConfig config = PeerManagerConfig.builder()
    .maxPeers(8)
    .enableReconnection(true)
    .build();

try (PeerManager manager = new DefaultPeerManager(BitcoinNetwork.MAINNET, config, handler)) {
    Peer peer = manager.addPeer("seed.bitcoinsv.io", 8333).join();
    System.out.println("Connected: " + peer.getHost() + " (height unknown until version)");

    // Keep running...
    Thread.sleep(60_000);
}

Monitor Chain Tip (SPV)

import org.twostack.spiffynode4j.spv.*;
import org.twostack.spiffynode4j.config.ChainTipTrackerConfig;

ChainTipTracker tracker = new ChainTipTracker(
    ChainTipTrackerConfig.builder()
        .minPeersForConsensus(3)
        .consensusThreshold(0.6)
        .build()
);

tracker.addListener(event -> {
    System.out.println(event.getType() + ": height " + event.getNewTip().getHeight()
        + " (confidence " + event.getNewTip().getConfidence() + ")");
    if (event.isReorganization()) {
        System.out.println("REORG detected! " + event.getDescription());
    }
});

// Wire it into the handler
PeerHandler spvHandler = new PeerHandler.Default() {
    @Override
    public CompletableFuture<Void> handleVersion(WireMessage msg, Peer peer) {
        String peerId = peer.getHost() + ":" + peer.getPort();
        tracker.registerPeer(peerId);
        tracker.handleVersion(peerId, (MsgVersion) msg);
        return CompletableFuture.completedFuture(null);
    }

    @Override
    public CompletableFuture<Void> handleBlockAnnouncement(InvVect item, Peer peer) {
        tracker.handleBlockAnnouncement(peer.getHost() + ":" + peer.getPort(), item.hash());
        return CompletableFuture.completedFuture(null);
    }

    @Override
    public CompletableFuture<Void> handleHeaders(WireMessage msg, Peer peer) {
        tracker.handleHeaders(peer.getHost() + ":" + peer.getPort(), (MsgHeaders) msg);
        return CompletableFuture.completedFuture(null);
    }
};

Or use the built-in ChainTipHandler which does the above automatically:

ChainTipHandler chainTipHandler = new ChainTipHandler(tracker);
CompositeHandler compositeHandler = new CompositeHandler(myHandler, chainTipHandler);

Check Statistics

// Per-peer
for (Peer peer : manager.getPeers()) {
    PeerStatistics stats = peer.getStatistics();
    System.out.printf("%s:%d — %s, %d msgs sent, %d received%n",
        stats.host(), stats.port(), stats.state(),
        stats.messagesSent(), stats.messagesReceived());
}

// Aggregate
ManagerStatistics stats = ((DefaultPeerManager) manager).getStatistics();
System.out.printf("Peers: %d connected, %d healthy, %d reconnecting%n",
    stats.connectedPeers(), stats.healthyPeers(), stats.peersReconnecting());

Documentation

Building

./gradlew clean test

376 tests across all 8 implementation phases.

License

See LICENSE for details.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages