GossipSub
Gossip-based message propagation
XE uses GossipSub for broadcasting messages to all peers in the network. Each category of data has its own topic, and each topic has a dedicated gossip type with Publish() and Subscribe() methods.
#Topics
| Topic | Constant | Data Type | Channel Buffer |
|---|---|---|---|
xe/blocks | BlockTopic | BlockMsg (wraps core.Block) | 256 |
xe/votes | VoteTopic | VoteMsg (wraps core.Vote) | 256 |
xe/marketplace | MarketplaceTopic | MarketplaceMsg | 256 |
xe/statechain | StateChainTopic | StateChainMsg (wraps statechain.Block) | 16 |
xe/directory | DirectoryTopic | directory.Registration | 256 |
xe/certificates | CertificateTopic | perf.Certificate | 64 |
#Message Size Limit
const MaxGossipMessageSize = 262144 // 256 KBThe 256 KB limit accommodates state chain blocks which can carry large key-value data. Block lattice messages are much smaller but share the same PubSub instance.
#Gossip Types
All gossip types share a common PubSub instance created with NewPubSub():
func NewPubSub(ctx context.Context, h host.Host) (*pubsub.PubSub, error)Each gossip type joins its respective topic and provides Publish() and Subscribe() methods.
#Block Gossip (Gossip)
Broadcasts and receives block lattice blocks.
type Gossip struct { /* ... */ } func NewGossip(ctx context.Context, h host.Host, ps ...*pubsub.PubSub) (*Gossip, error)func (g *Gossip) Publish(ctx context.Context, b *core.Block) errorfunc (g *Gossip) Subscribe(ctx context.Context) <-chan *core.Blockfunc (g *Gossip) PubSub() *pubsub.PubSubThe PubSub() accessor returns the underlying PubSub instance for sharing with other gossip types.
#Vote Gossip (VoteGossip)
Broadcasts and receives conflict resolution votes.
type VoteGossip struct { /* ... */ } func NewVoteGossip(ps *pubsub.PubSub) (*VoteGossip, error)func (vg *VoteGossip) Publish(ctx context.Context, v *core.Vote) errorfunc (vg *VoteGossip) Subscribe(ctx context.Context) <-chan *core.Vote#State Chain Gossip (StateChainGossip)
Broadcasts and receives state chain blocks (governance operations).
type StateChainGossip struct { /* ... */ } func NewStateChainGossip(ps *pubsub.PubSub) (*StateChainGossip, error)func (sg *StateChainGossip) Publish(ctx context.Context, b *statechain.Block) errorfunc (sg *StateChainGossip) Subscribe(ctx context.Context) <-chan *statechain.Block#Directory Gossip (DirectoryGossip)
Broadcasts and receives account directory registrations (human-readable name mappings).
type DirectoryGossip struct { /* ... */ } func NewDirectoryGossip(ps *pubsub.PubSub) (*DirectoryGossip, error)func (dg *DirectoryGossip) Publish(ctx context.Context, reg *directory.Registration) errorfunc (dg *DirectoryGossip) Subscribe(ctx context.Context) <-chan *directory.Registration#Marketplace Gossip (MarketplaceGossip)
Broadcasts and receives marketplace messages (resource advertisements, requests, and offers).
type MarketplaceGossip struct { /* ... */ } func NewMarketplaceGossip(ps *pubsub.PubSub) (*MarketplaceGossip, error)func (mg *MarketplaceGossip) Publish(ctx context.Context, msg *MarketplaceMsg) errorfunc (mg *MarketplaceGossip) Subscribe(ctx context.Context) <-chan *MarketplaceMsg#Pre-Validation
Messages are validated before being passed to consumers. This rejects malformed data before expensive cryptographic verification.
#Block Validation
// Hash and Account are 64 hex chars (32 bytes), Signature is 128 hex chars (64-byte ed25519)if len(b.Hash) != 64 || len(b.Signature) != 128 || len(b.Account) != 64 { continue // drop}| Field | Expected Length | Encoding |
|---|---|---|
Hash | 64 chars | Hex (32 bytes) |
Signature | 128 chars | Hex (64 bytes) |
Account | 64 chars | Hex (32 bytes) |
#Vote Validation
// RepPubKey and BlockHash are 64 hex chars, Signature is 64 raw bytes (ed25519)if len(v.RepPubKey) != 64 || len(v.BlockHash) != 64 || len(v.Signature) != 64 { continue // drop}#State Chain Block Validation
// Hash must be 64 hex chars, at least one signature requiredif len(scm.Block.Hash) != 64 || len(scm.Block.Signatures) == 0 { continue // drop}#Directory Validation
// Account and Signature must be non-emptyif reg.Account == "" || reg.Signature == "" { continue // drop}#Marketplace Validation
// Type field must be non-emptyif mm.Type == "" { continue // drop}#Message Flow
Publisher Node Subscriber Node───────────── ─────────────── │ │ │ Publish(ctx, block) │ │ │ │ │ ▼ │ │ JSON marshal │ │ │ │ │ ▼ │ │ topic.Publish() ──── GossipSub ────▶ sub.Next() │ │ │ ▼ │ JSON decode │ │ │ ▼ │ Pre-validate │ │ │ ┌────┴────┐ │ │ Valid? │ │ └────┬────┘ │ yes │ no │ │ └─▶ drop │ ▼ │ ch <- block │ (or drop if full)#Backpressure
Each Subscribe method creates a buffered channel. If the consumer is slow and the channel fills up, incoming messages are dropped with a log warning:
gossip: block channel full, dropping block a1b2c3d4e5f6...#JSON Encoding
All gossip messages use JSON encoding with encoding/json. Block and vote decoders use DisallowUnknownFields() for strict parsing. State chain and marketplace decoders use standard json.Unmarshal.
#Shared PubSub Instance
All gossip types share a single PubSub instance. The typical initialization order is:
// 1. Create shared PubSubps, err := xenet.NewPubSub(ctx, host) // 2. Create block gossip (can also create PubSub internally)blockGossip, err := xenet.NewGossip(ctx, host, ps) // 3. Create other gossip types sharing the same PubSubvoteGossip, err := xenet.NewVoteGossip(ps)stateChainGossip, err := xenet.NewStateChainGossip(ps)directoryGossip, err := xenet.NewDirectoryGossip(ps)marketplaceGossip, err := xenet.NewMarketplaceGossip(ps)