Skip to content

Bitswap

Pub Version Dart CI

A Dart implementation of the Bitswap protocol for the libp2p networking stack. This package enables Dart applications to exchange content-addressed blocks of data with other peers in the libp2p network using Bitswap.

This implementation targets Bitswap Protocol Version 1.2.0.

  • Implements Bitswap version 1.2.0 (/ipfs/bitswap/1.2.0).
  • Requesting blocks from peers using wantlists.
  • Responding to peer requests with blocks from a local block store.
  • Sending and receiving HAVE / DONT_HAVE messages for blocks.
  • Sending CANCEL messages for blocks no longer needed.
  • Management of local and per-peer wantlists.
  • Automatic handling of varint-prefixed message framing.
  • Reuse of outgoing streams for efficient communication.
  • Basic peer disconnection handling to clean up resources.
  • A working Dart environment.
  • An initialized and running dart_libp2p Host instance. This package relies on an existing libp2p host for network communication, peer discovery, and stream management.
  • Familiarity with CIDs (Content Identifiers) and block-based data storage.

If this package is published to pub.dev, add it to your pubspec.yaml:

dependencies:
dart_libp2p_bitswap: ^latest_version # Replace with the actual version
dart_libp2p: any # Your dart_libp2p version
dart_cid: any # For CID objects
# other necessary dependencies

If you are using it as a local path dependency:

dependencies:
dart_libp2p_bitswap:
path: ../path/to/dart_libp2p_bitswap # Adjust path as needed
# ... other dependencies

Then run dart pub get or flutter pub get.

The Bitswap service requires a block store to manage blocks. You need to provide an implementation of the IBlockStore interface. This interface defines how Bitswap interacts with your application’s data storage.

import 'package:dart_libp2p_bitswap/dart_libp2p_bitswap.dart';
import 'package:dart_cid/dart_cid.dart' as dart_cid;
import 'dart:typed_data';
// A simple in-memory block store for example purposes
class MyBlockStore implements IBlockStore {
final Map<String, Uint8List> _storage = {};
@override
Future<Uint8List?> get(dart_cid.CID cid) async {
print('[MyBlockStore] GET ${cid.toString()}');
return _storage[cid.toString()];
}
@override
Future<bool> has(dart_cid.CID cid) async {
final present = _storage.containsKey(cid.toString());
print('[MyBlockStore] HAS ${cid.toString()}: $present');
return present;
}
@override
Future<void> put(dart_cid.CID cid, Uint8List data) async {
print('[MyBlockStore] PUT ${cid.toString()} (data length: ${data.length})');
_storage[cid.toString()] = data;
// In a real application, you might notify Bitswap that new blocks are available,
// or Bitswap might re-query peers based on updated wantlists.
}
}

Instantiate the Bitswap class with your configured libp2p Host and your IBlockStore implementation.

import 'package:dart_libp2p_bitswap/dart_libp2p_bitswap.dart';
import 'package:dart_libp2p/dart_libp2p.dart' as libp2p; // Assuming this is your libp2p import
// Assuming 'host' is your initialized and started libp2p.Host instance
// libp2p.Host host = ...;
// await host.start();
// You need to obtain the peer connectedness event stream from your host.
// The exact way to get this stream depends on your libp2p.Host implementation.
// For example, it might be:
// Stream<libp2p.EvtPeerConnectednessChanged> peerEvents = host.eventBus.subscribe(libp2p.EvtPeerConnectednessChanged).stream;
// Or if your host provides it directly:
// Stream<libp2p.EvtPeerConnectednessChanged> peerEvents = host.getPeerEventsStream(); // Placeholder
// For this example, let's assume you have a StreamController for it:
final peerConnectednessController = StreamController<libp2p.EvtPeerConnectednessChanged>.broadcast();
// In a real app, this controller would be fed by actual peer connection events from the libp2p Host.
final myBlockStore = MyBlockStore();
final bitswap = Bitswap(
host: host,
blockStore: myBlockStore,
peerEventsStream: peerConnectednessController.stream, // Provide the stream
);
print('Bitswap service initialized.');
// Don't forget to close the controller when done if you created it
// await peerConnectednessController.close();

To request blocks from the network, use the wantBlocks method.

import 'package:dart_cid/dart_cid.dart' as dart_cid;
// Example CIDs you want to fetch
final cid1 = dart_cid.CID.fromString('bafkreibm6jg3ux5qumhcn2b3flc3tyu6dmlb4xa7u5bf44yegnrjhc4yeq'); // Replace with actual CIDs, using fromString
// final cid2 = dart_cid.CID.fromString('...'); // Using fromString
await bitswap.wantBlocks([cid1 /*, cid2 */]);
print('Requested CIDs: ${cid1.toString()}');
// Bitswap will now attempt to find peers with these blocks and retrieve them.
// Received blocks will be put into your IBlockStore via the `put` method.

When other peers request blocks that are present in your IBlockStore (checked via the has and get methods), the Bitswap service will automatically send them. Ensure your IBlockStore.put method correctly stores blocks that your node wishes to share.

When your application is shutting down, or you no longer need the Bitswap service, call the stop method to clean up resources, such as unregistering protocol handlers and closing active streams.

await bitswap.stop();
print('Bitswap service stopped.');

A more detailed, runnable example can be found in the /example directory of this package. It includes a placeholder for libp2p host setup.

// (Simplified snippet from example/dart_libp2p_bitswap_example.dart)
// ... (Host and BlockStore setup as above) ...
// Bitswap bitswap = Bitswap(host: host, blockStore: blockStore);
// final exampleData = Uint8List.fromList('Hello Bitswap!'.codeUnits);
// final exampleCid = dart_cid.CID.fromData(dart_cid.CID.V1, 'raw', exampleData); // Example of creating CID from data
// await myBlockStore.put(exampleCid, exampleData);
// final cidToWant = dart_cid.CID.fromString('bafkreibm6jg3ux5qumhcn2b3flc3tyu6dmlb4xa7u5bf44yegnrjhc4yeq'); // Using fromString
// await bitswap.wantBlocks([cidToWant]);
// ... (await bitswap.stop()) ...
  • Bitswap: The main public class for interacting with the Bitswap service.
  • IBlockStore: An interface you must implement to provide block storage capabilities to the Bitswap service.
  • BitswapNetworkService: (Internal) Handles the libp2p stream and message framing logic.
  • WantManager: (Internal) Manages local and peer wantlists.
  • Protobuf Messages: The package exports the generated Dart classes for Bitswap messages (e.g., Message, Wantlist, Block) from src/proto/bitswap_message.pb.dart.

This library implements version 1.2.0 of the Bitswap protocol, identified by the libp2p protocol ID /ipfs/bitswap/1.2.0. Key features of 1.2.0 supported include:

  • Block and Have want types.
  • sendDontHave requests.
  • HAVE / DONT_HAVE block presence messages.
  • CIDv1 support via prefix and data in block payloads.

For full specification details, please refer to the official Bitswap spec. You can also find a copy of the spec used during development in bitswap_spec.txt within this repository.

Contributions are welcome! Please feel free to open an issue or submit a pull request on the GitHub repository.

If you encounter any bugs or have feature requests, please file an issue on the GitHub issue tracker.

This package is licensed under the [Your License] license.