Bitswap

The Bitswap protocol enables content discovery and file sharing in py-libp2p networks.

Subpackages

Overview

Bitswap is a message-based protocol for exchanging content-addressed blocks between peers. It is a core component of IPFS (InterPlanetary File System) and enables efficient peer-to-peer file sharing.

Key Features

  • Content-addressed block storage and retrieval

  • Wantlist-based block exchange

  • Priority-based request handling

  • Bidirectional peer communication

  • Cancellation support for requests

Protocol Version

This implementation supports Bitswap 1.0.0 (/ipfs/bitswap/1.0.0).

Quick Start

Basic Usage

import trio
from libp2p import new_host
from libp2p.bitswap import BitswapClient
import multiaddr

async def main():
    # Create a host
    listen_addr = multiaddr.Multiaddr("/ip4/0.0.0.0/tcp/0")
    host = new_host()

    async with host.run([listen_addr]), trio.open_nursery() as nursery:
        # Create Bitswap client
        bitswap = BitswapClient(host)
        bitswap.set_nursery(nursery)
        await bitswap.start()

        # Add a block
        cid = compute_cid(b"Hello, Bitswap!")
        await bitswap.add_block(cid, b"Hello, Bitswap!")

        # Request a block from a peer
        data = await bitswap.get_block(cid, peer_id, timeout=30)
        print(f"Received: {data}")

        await bitswap.stop()

trio.run(main)

Running a Provider

async def run_provider():
    host = new_host()
    listen_addr = multiaddr.Multiaddr("/ip4/0.0.0.0/tcp/4001")

    async with host.run([listen_addr]), trio.open_nursery() as nursery:
        bitswap = BitswapClient(host)
        bitswap.set_nursery(nursery)
        await bitswap.start()

        # Add blocks to serve
        blocks = [b"Block 1", b"Block 2", b"Block 3"]
        for data in blocks:
            cid = compute_cid(data)
            await bitswap.add_block(cid, data)

        print(f"Provider listening on {host.get_addrs()}")
        await trio.sleep_forever()

Running a Client

async def run_client(provider_multiaddr):
    host = new_host()
    listen_addr = multiaddr.Multiaddr("/ip4/0.0.0.0/tcp/0")

    # Parse provider address
    provider_info = info_from_p2p_addr(provider_multiaddr)

    async with host.run([listen_addr]), trio.open_nursery() as nursery:
        bitswap = BitswapClient(host)
        bitswap.set_nursery(nursery)
        await bitswap.start()

        # Connect to provider
        await host.connect(provider_info)

        # Request blocks
        wanted_cids = [...]  # List of CIDs to request
        for cid in wanted_cids:
            try:
                data = await bitswap.get_block(cid, provider_info.peer_id)
                print(f"Got block: {data}")
            except Exception as e:
                print(f"Failed to get block: {e}")

API Reference

BitswapClient

class libp2p.bitswap.BitswapClient(host: IHost, block_store: BlockStore | None = None, protocol_version: str = '/ipfs/bitswap/1.2.0')

Bases: object

Bitswap client for exchanging blocks with other peers.

Supports Bitswap protocol versions 1.0.0, 1.1.0, and 1.2.0 for content discovery and file sharing in a peer-to-peer network.

async add_block(cid: bytes, data: bytes) None

Add a block to the local store.

Args:

cid: The CID of the block data: The block data

Raises:

BlockTooLargeError: If the block exceeds maximum size

async cancel_want(cid: bytes) None

Cancel a previous want for a block.

Args:

cid: The CID to cancel

async get_block(cid: bytes, peer_id: ID | None = None, timeout: float = 30) bytes

Get a block, fetching from peers if not available locally.

Args:

cid: The CID of the block to fetch peer_id: Optional specific peer to request from timeout: Timeout in seconds

Returns:

The block data

Raises:

BlockNotFoundError: If the block cannot be found BitswapTimeoutError: If the request times out

async have_block(cid: bytes, peer_id: ID | None = None) bool

Check if a peer has a block (v1.2.0 feature).

Args:

cid: The CID of the block to check peer_id: Optional specific peer to query

Returns:

True if peer has the block, False otherwise

set_nursery(nursery: Nursery) None

Set the nursery for background tasks.

async start() None

Start the Bitswap client.

async stop() None

Stop the Bitswap client.

async want_block(cid: bytes, priority: int = 1, want_type: int = 0, send_dont_have: bool = False) None

Add a block to the wantlist without blocking.

Args:

cid: The CID of the block to want priority: Priority of the request want_type: 0 for Block (full block), 1 for Have (just check) - v1.2.0 send_dont_have: Whether to request DontHave response if not found - v1.2.0

BlockStore

class libp2p.bitswap.BlockStore

Bases: ABC

Abstract interface for storing and retrieving blocks.

Implementations should provide persistent or in-memory storage for content-addressed blocks.

abstract async delete_block(cid: bytes) None

Delete a block.

Args:

cid: The CID of the block to delete

abstract get_all_cids() list[bytes]

Get all CIDs in the block store.

Returns:

List of all CIDs

abstract async get_block(cid: bytes) bytes | None

Get a block by its CID.

Args:

cid: The CID of the block to retrieve

Returns:

The block data if found, None otherwise

abstract async has_block(cid: bytes) bool

Check if a block exists.

Args:

cid: The CID of the block

Returns:

True if the block exists, False otherwise

abstract async put_block(cid: bytes, data: bytes) None

Store a block.

Args:

cid: The CID of the block data: The block data

MemoryBlockStore

class libp2p.bitswap.MemoryBlockStore

Bases: BlockStore

In-memory block store implementation.

async delete_block(cid: bytes) None

Delete a block.

get_all_cids() list[bytes]

Get all CIDs in the store.

async get_block(cid: bytes) bytes | None

Get a block by its CID.

async has_block(cid: bytes) bool

Check if a block exists.

async put_block(cid: bytes, data: bytes) None

Store a block.

size() int

Get the number of blocks in the store.

Configuration

Bitswap configuration constants and defaults.

Errors

Bitswap protocol errors.

exception libp2p.bitswap.errors.BitswapError

Bases: Exception

Base exception for Bitswap errors.

exception libp2p.bitswap.errors.BlockNotFoundError

Bases: BitswapError

Raised when a requested block is not found.

exception libp2p.bitswap.errors.BlockTooLargeError

Bases: BitswapError

Raised when a block exceeds the maximum size.

exception libp2p.bitswap.errors.BlockUnavailableError

Bases: BitswapError

Raised when a peer confirms they don’t have a requested block (DontHave).

exception libp2p.bitswap.errors.InvalidBlockError

Bases: BitswapError

Raised when a block is invalid or malformed.

exception libp2p.bitswap.errors.InvalidCIDError

Bases: BitswapError

Raised when a CID is invalid.

exception libp2p.bitswap.errors.MessageTooLargeError

Bases: BitswapError

Raised when a message exceeds the maximum size.

exception libp2p.bitswap.errors.TimeoutError

Bases: BitswapError

Raised when an operation times out.

Protocol Details

Message Format

Bitswap messages use Protocol Buffers with the following structure:

message Message {
  message Wantlist {
    message Entry {
      bytes block = 1;      // CID of the block
      int32 priority = 2;   // Priority (higher = more important)
      bool cancel = 3;      // Cancel a previous request
    }
    repeated Entry entries = 1;
    bool full = 2;          // Full wantlist or update
  }

  Wantlist wantlist = 1;
  repeated bytes blocks = 2;  // Block data
}

Wire Protocol

Messages are sent as length-prefixed protobuf messages:

  1. Message is serialized to protobuf bytes

  2. Length is encoded as unsigned varint

  3. Format: [varint length][protobuf message]

Maximum message size: 4 MiB

Maximum block size: 2 MiB

Block Exchange Flow

  1. Client requests block:

    • Check local block store

    • If not found, add to wantlist

    • Send wantlist to peer(s)

    • Wait for block response

  2. Server receives wantlist:

    • Parse wantlist entries

    • Check local block store for requested blocks

    • Send available blocks to client

  3. Client receives blocks:

    • Store blocks locally

    • Notify pending requests

    • Send cancel messages for received blocks

Wantlist Management

Bitswap maintains two types of wantlists:

  • Local wantlist: Blocks this peer wants

  • Peer wantlists: Blocks each connected peer wants

Wantlist updates can be:

  • Full (full = true): Replace entire wantlist

  • Incremental (full = false): Apply specific changes

Advanced Usage

Custom Block Store

Implement a custom block store by subclassing BlockStore:

from libp2p.bitswap import BlockStore
from pathlib import Path
from typing import Optional

class FileBlockStore(BlockStore):
    def __init__(self, base_path: str):
        self.base_path = Path(base_path)
        self.base_path.mkdir(exist_ok=True)

    async def get_block(self, cid: bytes) -> Optional[bytes]:
        file_path = self.base_path / cid.hex()
        if file_path.exists():
            return file_path.read_bytes()
        return None

    async def put_block(self, cid: bytes, data: bytes) -> None:
        file_path = self.base_path / cid.hex()
        file_path.write_bytes(data)

    async def has_block(self, cid: bytes) -> bool:
        file_path = self.base_path / cid.hex()
        return file_path.exists()

    async def delete_block(self, cid: bytes) -> None:
        file_path = self.base_path / cid.hex()
        if file_path.exists():
            file_path.unlink()

# Use the custom store
bitswap = BitswapClient(host, FileBlockStore("/path/to/blocks"))

Priority-Based Requests

Request blocks with different priorities:

# High priority block
await bitswap.want_block(critical_cid, priority=10)

# Normal priority
await bitswap.want_block(normal_cid, priority=1)

# Low priority
await bitswap.want_block(optional_cid, priority=0)

Blocks with higher priority values are served first.

Cancel Requests

Cancel a block request:

# Add to wantlist
await bitswap.want_block(cid)

# Later, if no longer needed
await bitswap.cancel_want(cid)

This sends a cancel message to all peers that haven’t responded yet.

Examples

The examples/bitswap directory contains complete examples:

  • Provider mode: Serve blocks to other peers

  • Client mode: Request blocks from providers

  • Demo mode: Automated demonstration

Run examples:

# Provider
python examples/bitswap/bitswap.py --mode provider

# Provider with file
python examples/bitswap/bitswap.py --mode provider --file myfile.txt

# Client
python examples/bitswap/bitswap.py --mode client --provider <multiaddr>

# Demo
python examples/bitswap/bitswap.py --mode demo

Limitations

Current Version Limitations

  • CID Encoding: Uses simplified SHA-256 hashing instead of proper CIDv0/CIDv1

  • Protocol Version: Only supports Bitswap 1.0.0

  • Block Store: Default implementation is in-memory only

  • No Peer Scoring: No accounting or peer reputation system

Future Enhancements

Planned improvements:

  • Support for Bitswap 1.1.0 (CIDv1 with prefix encoding)

  • Support for Bitswap 1.2.0 (Have/DontHave responses)

  • Proper CID library integration

  • Persistent block storage

  • Peer scoring and accounting

  • Content routing integration with DHT

See Also