Network, ARC and data sources
This page shows how to point libspiffy at a network: which chain it runs on, where it broadcasts
transactions, what fee it pays, which peers it takes block headers from, and where it reads chain
data when you import a wallet. All of it is set through LibSpiffyActorSystem.initialize().
// Sketch: the network options of initialize(); storage options are left out.import 'package:libspiffy/libspiffy.dart';
final libspiffy = LibSpiffyActorSystem();await libspiffy.initialize( networkType: 'main', arcConfig: ArcServiceConfig.gorillaPoolMainnet( minimumFeeRate: const FeeRate(satoshis: 1, bytes: 1000), ), enableP2P: true, peerAddresses: null, // null: the network's DNS seeds blockchainDataSource: WhatsOnChainDataSource(networkType: 'main'),);Network type
Section titled “Network type”networkType is a string. It defaults to 'test'.
| Value | Network | Addresses and keys |
|---|---|---|
'main' (also 'mainnet', 'livenet') |
BSV mainnet | mainnet encoding |
'test' (also 'testnet'; anything unrecognised) |
BSV testnet | testnet encoding |
'regtest' |
a local regtest chain | testnet encoding |
The spellings are normalised by NetworkName (lib/src/utils/network_name.dart). Read models store
the canonical form: 'mainnet', 'testnet' or 'regtest'. Regtest has its own genesis header and
proof-of-work limit (NetworkParams), and no DNS seeds, so on regtest you must name peers with
peerAddresses.
ARC and Arcade
Section titled “ARC and Arcade”libspiffy broadcasts every transaction through an ARC server, reads ARC’s fee policy, and follows
each transaction’s status until it is mined. You choose the server with an ArcServiceConfig
(lib/src/services/arc_service_config.dart).
Presets
Section titled “Presets”| Preset | Base URL | API key |
|---|---|---|
ArcServiceConfig.taalMainnet() |
https://arc.taal.com/v1 |
optional apiKey: |
ArcServiceConfig.taalTestnet() |
https://arc-test.taal.com/v1 |
optional apiKey: |
ArcServiceConfig.gorillaPoolMainnet() |
https://arc.gorillapool.io/v1 |
none needed |
ArcServiceConfig.gorillaPoolTestnet() |
https://testnet.arc.gorillapool.io/v1 |
none needed |
ArcServiceConfig.bsvaArcadeTestnet() |
https://arcade-v2-testnet-us-1.bsvblockchain.tech |
none needed |
Every preset takes apiKey and minimumFeeRate as named parameters.
Default when you pass no arcConfig
Section titled “Default when you pass no arcConfig”initialize() picks a server from networkType (see lib/src/actors/libspiffy_actor_system.dart):
- mainnet:
ArcServiceConfig.gorillaPoolMainnet() - anything else, regtest included:
ArcServiceConfig.taalTestnet()
A regtest node needs an explicit arcConfig that points at your local ARC or Arcade.
Your own server
Section titled “Your own server”Use the constructor, or ArcServiceConfig.custom():
final arc = ArcServiceConfig( baseUrl: 'https://arc.example.com/v1', apiKey: 'your-api-key', // sent as Authorization: Bearer <apiKey> requestTimeout: const Duration(seconds: 30), minimumFeeRate: const FeeRate(satoshis: 1, bytes: 1000),);| Field | Default | Meaning |
|---|---|---|
baseUrl |
required | Root of the ARC API. libspiffy appends /tx, /tx/{txid}, /policy and /health to it. |
apiKey |
null |
Sent as Authorization: Bearer <apiKey> when set. |
requestTimeout |
30 s | Upper bound on any single HTTP request to ARC. |
minimumFeeRate |
null |
Your fee floor; see below. |
ArcServiceConfig.custom() takes baseUrl, apiKey, defaultCallbackUrl and minimumFeeRate, but
not requestTimeout. Use the constructor when you need a different timeout.
Arcade has no /v1
Section titled “Arcade has no /v1”Arcade is the Teranode-era successor to ARC. It serves the same API at its root, so its base URL has
no /v1:
ArcServiceConfig(baseUrl: 'http://127.0.0.1:23011'); // local ArcadeArcServiceConfig.bsvaArcadeTestnet(); // BSV Association testnet ArcadeArcade answers every submission with 202 RECEIVED and takes it to the network afterwards.
libspiffy treats that as in flight and asks Arcade again after each delay in
ARCActor.defaultInFlightFollowDelays (1, 2, 4, 8 and 15 seconds), stopping at the first status that
is no longer in flight. You can change the delays with initialize(arcInFlightFollowDelays: ...).
Arcade also requires Extended Format; libspiffy submits it.
ArcService.submitBatchTransactions does not work with Arcade, whose /txs takes a different body.
Nothing in libspiffy calls it.
Fees and minimumFeeRate
Section titled “Fees and minimumFeeRate”Every transaction the wallet builds pays ARC’s published policy rate (GET /policy, miningFee) on
its signed size. libspiffy has no fee rate of its own. If ARC’s policy cannot be read, the fee
quote fails; libspiffy does not guess a rate.
Since 4.7.0 you can set a floor with minimumFeeRate. The wallet pays the published rate, or your
floor when the published rate is lower. Some ARC servers publish a rate no miner mines at:
GorillaPool’s testnet ARC publishes 0 sat/kB.
ArcServiceConfig.gorillaPoolTestnet( minimumFeeRate: const FeeRate(satoshis: 1, bytes: 1000), // 1 sat/kB);FeeRate is satoshis per bytes, as ARC publishes it. FeeRate.feeFor(sizeBytes) rounds up, and
FeeRate.isAbove(other) compares two rates exactly.
Retries
Section titled “Retries”With the Isar backend, a broadcast that fails is put on a durable retry queue (duraq, queue name
arc_broadcast_retry) with exponential backoff from 10 seconds to 5 minutes, at most 10 attempts.
The coordinator emits BroadcastFailureEvent (txid, error, willRetry). The queue is stored in
Isar, so it is disabled when libspiffy has no Isar instance (for example with the PostgreSQL
backend); a warning is logged.
P2P block headers
Section titled “P2P block headers”libspiffy verifies merkle proofs against block headers it holds itself. After the optional CDN header sync, it takes the rest from Bitcoin SV peers over the wire protocol.
initialize() parameter |
Default | Meaning |
|---|---|---|
enableP2P |
true |
Connect to peers and sync headers. |
peerAddresses |
the network’s DNS seeds | Peers as host:port. Each name is resolved to every address it holds and all are dialled in parallel. |
userAgent |
LibSpiffyActorSystem.defaultUserAgent (/LibSpiffy-BSV:1.0/) |
The user agent sent in the version handshake. |
startHeight |
none | The height this node reports to peers in its handshake. Header sync does not start from it; it continues from the tip of the stored header chain. |
The DNS seeds come from NetworkParams (lib/src/spv/network_params.dart):
- mainnet:
seed.bitcoinsv.io:8333,seed.satoshisvision.network:8333,seed.bitcoinseed.directory:8333 - testnet:
testnet-seed.bitcoinsv.io:18333,testnet-seed.bitcoincloud.net:18333,testnet-seed.bitcoinseed.directory:18333,seed.gorillapool.io:18333 - regtest: none
Keep BSV in the user agent
Section titled “Keep BSV in the user agent”Teranode’s wire-protocol service accepts a peer only if its user agent contains BSV or
Bitcoin SV. It bans the IP of any other peer for 24 hours. Every client behind the same address
is then locked out. Up to 4.6.x the default was /LibSpiffy:1.0/, which Teranode refuses; 4.7.0
changed the default to /LibSpiffy-BSV:1.0/. If you set your own userAgent, keep BSV in it.
When no peer can be reached
Section titled “When no peer can be reached”If no peer connects, initialize() throws a StateError (“P2P initialization failed …
LibSpiffy will fall back to API-only mode”). By then the actors are already running and
libspiffy.ready has completed, so the system is still usable for everything that does not need
new headers; isInitialized stays true. Catch the error and carry on.
While P2P runs, libspiffy checks every 30 seconds (LibSpiffyActorSystem.peerUpkeepInterval)
whether any peer is left. If none is, it resolves the peers again, dials them, and restarts header
sync once one connects. To watch progress, see Monitoring.
Blockchain data sources
Section titled “Blockchain data sources”A blockchain data source lets libspiffy read chain data it did not receive itself: the history and
UTXOs of addresses when you import a wallet, and a fallback for deferred-payment checks. Pass one as
blockchainDataSource. Without it there is no import actor: the coordinator refuses an
ImportWalletCommand with a failed ImportCompleteEvent, and the LibSpiffyActorSystem import
methods throw StateError('ImportActor not available. ...').
libspiffy 5.0.0 ships one data source, WhatsOnChainDataSource. NodeRpcDataSource is removed in
5.0.0: it read proofs with gettxoutproof, which only an SV Node serves, and Teranode does not.
WhatsOnChainDataSource
Section titled “WhatsOnChainDataSource”final source = WhatsOnChainDataSource( networkType: 'main', // 'main' or 'test' maxRetries: 3, initialBackoffMs: 1000, requestsPerSecondLimit: 3, requestTimeout: const Duration(seconds: 30),);It calls https://api.whatsonchain.com/v1/bsv/main or .../test and reads merkle proofs in TSC
format. The values shown are the defaults. It supports mainnet and testnet only: a 'regtest'
network throws ArgumentError.
Your own source
Section titled “Your own source”Implement BlockchainDataSource (lib/src/services/blockchain_data_source.dart) to read from
another service, such as a regtest node or your own indexer. See the
API reference for its members.