|
| 1 | +/* |
| 2 | +Package p2p provides header exchange utilities that enable requesting and serving headers over direct peer-to-peer streams. It primarily provides: |
| 3 | +
|
| 4 | +# Components |
| 5 | +
|
| 6 | +Exchange Server, Exchange Client and GossipSub Subscriber for header exchange. |
| 7 | +
|
| 8 | + - Exchange Server: |
| 9 | + The exchange server provides handlers for getting latest or head headers, or getting headers by hash or range. |
| 10 | + On start, the server it registers a streamHandler on the protocolID ("/${networkID}/header-ex/v0.0.1"), where networkID can be |
| 11 | + any available network. |
| 12 | + The server is powered by a header.Store[H] implementation that is used to store and retrieve headers. |
| 13 | +
|
| 14 | + - Exchange Client: |
| 15 | + The exchange client provides functionality to retrieve latest/head headers, headers by hash or range. |
| 16 | + On start the client connects to trusted peers (bootstrappers) as well as kicks off tracking and garbage collection routines for peer connections. |
| 17 | + The getters it defines are several and are used to get headers by height, range, and hash. |
| 18 | + The exchange client uses bi-directional read-write libp2p streams to request and receive headers from peers. |
| 19 | +
|
| 20 | + - Subscriber: |
| 21 | + The subscriber is an abstraction over GossipSub subscriptions that tracks the pubsub object + topic. |
| 22 | + On start, the subscriber joins the GossipSub topic: "${networkID}/header-ex/v0.0.3" to listen for incoming headers. |
| 23 | + The subscriber provides methods for adding incomning messages' validators* as well as methods that subscribe to the gossipsub topic. |
| 24 | + It also defines a broadcast method to publish headers to other subscribers. |
| 25 | + *) Validators are functions that are used to validate incoming messages before |
| 26 | + they are processed by the subscriber. They either return reject or accept the incoming message. |
| 27 | +
|
| 28 | +For more information, see the documentation for each component. |
| 29 | +
|
| 30 | +# Usage Examples |
| 31 | +
|
| 32 | + - Exchange Server Usage |
| 33 | + To use the exchange server, first create a new instance of the server and start it: |
| 34 | +
|
| 35 | +```go |
| 36 | +s, err:= p2p.NewExchangeServer[H]( |
| 37 | +
|
| 38 | + libp2pHost, |
| 39 | + store, |
| 40 | + WithNetworkID[ServerParameters](networkID), |
| 41 | +
|
| 42 | +) |
| 43 | +err = s.Start() |
| 44 | +// ... |
| 45 | +err = s.Stop() |
| 46 | +``` |
| 47 | +
|
| 48 | + - Exchange Client Usage |
| 49 | +
|
| 50 | + To use the exchange client, first create a new instance of the client and start it: |
| 51 | +
|
| 52 | +```go |
| 53 | +c, err := p2p.NewExchange[H]( |
| 54 | +
|
| 55 | + libp2pHost, |
| 56 | + trustedPeers, |
| 57 | + libp2pConnGater, |
| 58 | + WithChainId(chainID), |
| 59 | +
|
| 60 | +) |
| 61 | +err = c.Start() |
| 62 | +``` |
| 63 | +
|
| 64 | + Then, you can use the various getters to get headers: |
| 65 | +
|
| 66 | +```go |
| 67 | +ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) |
| 68 | +// Get the best head from trusted peers |
| 69 | +head, err := c.Head(ctx) |
| 70 | +
|
| 71 | +// Get a header by height |
| 72 | +header, err := c.GetByHeight(ctx, height) |
| 73 | +
|
| 74 | +// Get a range of headers by height |
| 75 | +headers, err := c.GetRangeByHeight(ctx, start, end) |
| 76 | +
|
| 77 | +// Get a range of verified headers by height |
| 78 | +headers, err := c.GetVerifiedRange(ctx, header, amount) |
| 79 | +
|
| 80 | +// Get a header by hash |
| 81 | +header, err := c.Get(ctx, header.hash) |
| 82 | +``` |
| 83 | +
|
| 84 | + - Subscriber Usage |
| 85 | + To use the subscriber, first create a new instance of the subscriber and start it: |
| 86 | +
|
| 87 | +```go |
| 88 | +sub := p2p.NewSubscriber[H]( |
| 89 | +
|
| 90 | + pubsub, // pubsub.PubSub from libp2p |
| 91 | + msdIDFn, // message ID signing function |
| 92 | + networkID, // network ID |
| 93 | +
|
| 94 | +) |
| 95 | +err := sub.Start() |
| 96 | +``` |
| 97 | +
|
| 98 | + Then, you can add validators and subscribe to headers: |
| 99 | +
|
| 100 | +```go |
| 101 | +// Add a validator |
| 102 | +
|
| 103 | + err := sub.AddValidator(func(ctx context.Context, header H) pubsub.ValidationResult { |
| 104 | + if msg.ValidatorData != nil { |
| 105 | + return true |
| 106 | + } |
| 107 | + return false |
| 108 | + }) |
| 109 | +
|
| 110 | +// Subscribe to headers |
| 111 | +subscription, err := sub.Subscribe(ctx) |
| 112 | +
|
| 113 | +// Keep listening for new headers |
| 114 | +
|
| 115 | + go func() { |
| 116 | + for { |
| 117 | + select { |
| 118 | + case <-ctx.Done(): |
| 119 | + subscription.Cancel() |
| 120 | + return |
| 121 | + case header := subscription.NextHeader(ctx): |
| 122 | + // Do something with the header |
| 123 | + } |
| 124 | + } |
| 125 | + }() |
| 126 | +
|
| 127 | +// Broadcast a header |
| 128 | +err := sub.Broadcast(header) |
| 129 | +``` |
| 130 | +*/ |
| 131 | +package p2p |
0 commit comments