ADCNet is a proof of concept Go implementation of an anonymous distributed communication network using XOR-based message blinding and auction-based message scheduling. It provides anonymous broadcast requiring all servers to participate in message recovery.
The protocol is mostly based on ZIPNet: Low-bandwidth anonymous broadcast from (dis)Trusted Execution Environments.
For an overview of the protocol, see protocol/protocol.md.
Warning
This repository is a work in progress. A lot of it is vibe-coded, a lof of the cryptography is hand-rolled. Do not use for any production use cases! Do use this repository if you want to familiarize yourself with DCNet/ZIPNet and/or experiment with the protocol đź’–
ADCNet enables participants to broadcast messages anonymously. Message sender identity remains hidden as long as one server is honest. The protocol uses an Invertible Bloom Filter (IBF) based auction system for fair and efficient message scheduling.
ADCNet consists of three main components operating in a round-based protocol:
Clients prepare messages for anonymous broadcast by:
- XOR-blinding messages with one-time pads derived from shared secrets with all servers
- Participating in auctions for message slots by encoding bids into IBF chunks
- Encoding messages at auction-determined offsets if they won slots in previous rounds
Aggregators reduce bandwidth requirements by:
- Collecting and verifying client message signatures
- XORing client message vectors together
- Adding client auction vectors in the finite field
- Supporting hierarchical aggregation to further reduce server load
Servers collaborate to reconstruct messages:
- Each server removes its XOR blinding factors from aggregated messages
- All servers must contribute their blinding vectors
- Combined unblinding recovers the original message vector
- The reconstructed IBF is inverted to determine next round's message scheduling
- XOR-Based Blinding: Messages blinded with one-time pads from all server shared secrets
- Anytrust Server Group: Anonymity preserved as long as a single server is honest
- Finite Field Arithmetic: 384-bit field for auction IBF operations
- IBF-based Scheduling: Distributed auction mechanism using Invertible Bloom Filters
- Dynamic Message Sizing: Variable-length messages allocated through auction weights
- TEE Attestation: Optional TDX attestation for service verification
go get github.com/flashbots/adcnetServices can be configured via YAML files or command-line flags. Config files allow storing credentials securely and simplify deployment.
Example server configuration (server.yaml):
http_addr: ":8081"
registry_url: "http://localhost:8080"
admin_token: "admin:secret"
keys:
signing_key: "" # Hex-encoded, generates if empty
exchange_key: "" # Hex-encoded, generates if empty
attestation:
use_tdx: false
measurements_url: ""
server:
is_leader: trueExample registry configuration (registry.yaml):
http_addr: ":8080"
admin_token: "admin:secret"
attestation:
use_tdx: false
measurements_url: ""
protocol:
round_duration: 10s
message_length: 512000
auction_slots: 10
min_clients: 1The demo orchestrator runs a complete local deployment:
go run ./services/demo \
--clients=10 \
--aggregators=2 \
--servers=5 \
--round=10s \
--msg-length=512000 \
--admin-token="admin:secret"For production deployments, run services independently using the unified service command.
go run ./cmd/registry \
--addr=:8080 \
--admin-token="admin:secret" \
--measurements-url="https://example.com/measurements.json" \
--round=10s \
--msg-length=512000# Leader server
go run ./cmd/service \
--service-type=server \
--addr=:8081 \
--registry=http://localhost:8080 \
--admin-token="admin:secret" \
--leader
# Additional servers
go run ./cmd/service \
--service-type=server \
--addr=:8082 \
--registry=http://localhost:8080 \
--admin-token="admin:secret"go run ./cmd/service \
--service-type=aggregator \
--addr=:8083 \
--registry=http://localhost:8080 \
--admin-token="admin:secret"go run ./cmd/service \
--service-type=client \
--addr=:8084 \
--registry=http://localhost:8080Create a YAML config for each service:
# server.yaml
service_type: "server"
http_addr: ":8081"
registry_url: "http://localhost:8080"
admin_token: "admin:secret"
keys:
signing_key: "" # Generated if empty
exchange_key: "" # Generated if empty
attestation:
use_tdx: true
measurements_url: "https://example.com/measurements.json"
server:
is_leader: truego run ./cmd/service --config=server.yamlThe unified service command enables building a single binary for TEE VM images:
# Build single binary
go build -o adcnet-service ./cmd/service
# Run with different configurations
./adcnet-service --config=/etc/adcnet/server.yaml
./adcnet-service --config=/etc/adcnet/client.yamlEnable TDX attestation:
go run ./cmd/service \
--service-type=server \
--tdx \
--tdx-url=http://attestation-service:8080 \
--registry=http://localhost:8080All services self-register on startup:
| Service Type | Endpoint | Auth Required |
|---|---|---|
| Client | POST /register/client |
No |
| Server | POST /admin/register/server |
Yes (Basic Auth) |
| Aggregator | POST /admin/register/aggregator |
Yes (Basic Auth) |
Servers and aggregators include admin_token in their config for authentication.
Enable TDX attestation for production deployments:
# In config file:
attestation:
use_tdx: true
tdx_remote_url: "http://attestation-service:8080" # Optional
measurements_url: "https://example.com/measurements.json"Or via flags:
go run ./cmd/server \
--tdx \
--tdx-url=http://attestation-service:8080 \
--measurements-url=https://example.com/measurements.json \
--registry=http://localhost:8080import (
"github.com/flashbots/adcnet/protocol"
)
config := &protocol.ADCNetConfig{
AuctionSlots: 100,
MessageLength: 1000,
MinClients: 3,
RoundDuration: 10 * time.Second,
}
client := protocol.NewClientService(config, signingKey, exchangeKey)
client.RegisterServer(serverID, serverExchangePubkey)
client.ScheduleMessageForNextRound([]byte("hello"), 10)Main protocol implementation including:
ClientMessager: XOR blinding and message preparationServerMessager: Unblinding and message reconstructionAggregatorMessager: Message aggregation operations
Distributed auction mechanism featuring:
IBFVector: Multi-level Invertible Bloom Filter implementationAuctionEngine: Knapsack-based slot allocation
Cryptographic primitives providing:
- Field arithmetic in finite fields
- Key management (Ed25519 signing, P-256 key exchange)
- XOR and field-element blinding vector derivation
HTTP service implementations with:
- Central registry for service discovery
- TEE attestation verification
- Signed message authentication
Standalone CLI commands:
cmd/registry: Central registry servicecmd/server: ADCNet servercmd/aggregator: Message aggregatorcmd/client: ADCNet client
- Privacy: Message content hidden unless all servers collude
- Anonymity: Sender identity protected through XOR blinding with all servers
- Unlinkability: Fresh blinding prevents correlation between rounds
- Availability: System requires all servers to participate
- Integrity: Digital signatures authenticate all protocol messages
- Attestation: Optional TEE verification for service identity
- Field Order: 384-bit prime field (48-byte IBF chunks)
- Message Blinding: XOR with PRF-derived one-time pads unique per server/round
- Auction Blinding: Field addition with server-specific blinding vectors
- IBF Structure: 4-level filter with 0.75 shrink factor between levels
- Signatures: Ed25519 for all protocol messages
- Key Exchange: ECDH P-256 for shared secret derivation
go test ./...Performance benchmarks:
go test -bench=. ./protocol- Servers must have pre-established shared secrets with all authorized clients
- All authorized clients should participate (real or dummy messages) for anonymity
- Message padding required to prevent traffic analysis
- Synchronous rounds assumed
- Not all cryptographic operations are constant-time (field arithmetic)
- All servers must be online for message recovery
- Admin credentials should be securely managed in production
MIT License - see the LICENSE file for details.