Solana nodes communicate with each other and share data using the gossip protocol. They send messages in a binary form which need to be deserialized. There are 6 types of messages:
- pull request
- pull response
- push message
- prune message
- ping
- pong
Each message contains data specific to its type: values that nodes share between them, filters, pruned nodes, etc. Nodes keep their data in Cluster Replicated Data Store (crds
), which is synchronized between nodes via pull requests, push messages and pull responses.
Tip
Naming conventions used in this document
- Node - a validator running the gossip
- Peer - a node sending or receiving messages from the current node we're talking about
- Entrypoint - the gossip address of the peer the node will initially connect to
- Origin - node, the original creator of the message
- Cluster - a network of validators with a leader that produces blocks
- Leader - node, the leader of the cluster in a given slot
- Shred - is the smallest portion of block produced by a leader
- Shred version - a cluster identification value
- Fork - a fork occures when two different blocks got chained to the same parent block (e.g. next block is created before the previous one was completed)
- Epoch - it is a length of certain amount of blocks (slots) in which the validator schedule is defined
- Slot - the period of time for which each leader ingests transactions and produces a block
Each message is sent in a binary form with a maximum size of 1232 bytes (1280 is a minimum IPv6 TPU
, 40 bytes is the size of IPv6
header and 8 bytes is the size of the fragment header).
Data sent in the message is serialized from a Protocol
type, which can be one of:
Enum ID | Message | Data | Description |
---|---|---|---|
0 | pull request | CrdsFilter , CrdsValue |
sent by node to ask for new information |
1 | pull response | Pubkey , CrdsValuesList |
response to a pull request |
2 | push message | Pubkey , CrdsValuesList |
sent by node to share its data |
3 | prune message | Pubkey , PruneData |
sent to peers with a list of origin nodes that should be pruned |
4 | ping message | Ping |
a ping message |
5 | pong message | Pong |
response to a ping |
block-beta
block
columns 3
block
columns 1
b["Pull request"]
c["Pull response"]
d["Push message"]
e["Prune message"]
f["Ping message"]
g["Pong message"]
end
right<["Serialize"]>(right)
a["Packet\n(1232 bytes)"]
end
Solana client Rust implementation
enum Protocol {
PullRequest(CrdsFilter, CrdsValue),
PullResponse(Pubkey, Vec<CrdsValue>),
PushMessage(Pubkey, Vec<CrdsValue>),
PruneMessage(Pubkey, PruneData),
PingMessage(Ping),
PongMessage(Pong)
}
Fields described in the tables below have their types specified using Rust notation:
u8
- 1 byte of unsigned data (8-bit unsigned integer)u16
- 16-bit unsigned integeru32
- 32-bit unsigned integer, and so on...[u8]
- dynamic size array of 1-byte elements[u8; 32]
- fixed size array of 32 elements, with each element being 1 byte[[u8; 64]]
- a two-dimensional array containing arrays of 64 1-byte elementsb[u8]
- a bit vector containing 1-byte elementsu32 | None
- an option type, meaning an element is eitheru32
(in this case) orNone
(u32, [u8, 16])
- a tuple that contains two elements - one is a 32-bit integer, the second one is a 16-element array of bytesMyStruct
- a complex type (either defined as a struct or a Rust enum), consisting of many elements of different basic types
The Size column in tables contains the size of data in bytes. The size of dynamic arrays contains an additional plus (+
) sign, e.g. 32+
, which means the array has at least 32 bytes. Empty dynamic arrays always have 8 bytes which is the size of the array header containing array length.
In case the size of a particular complex data is unknown it is marked with ?
. The limit, however, is always 1232 bytes for the whole data packet (payload within the UDP packet).
In the Rust implementation of the Solana node, the data is serialized into a binary form using a bincode
crate as follows:
- basic types, e.g.
u8
,u16
,u64
, etc. - are serialized as they are present in the memory, e.g.u8
type is serialized as 1 byte,u16
as 2 bytes, and so on, - array elements are serialized as above, e.g.
[u8; 32]
array is serialized as 32 bytes,[u16; 32]
will be serialized as 32 16-bit elements which are equal to 64 bytes, - dynamically sized arrays have always an 8-byte header containing array length plus bytes of data, therefore empty arrays take 8 bytes,
- bit vectors are serialized similar to dynamic arrays - their header contains 1-byte which tells whether there is any data in the vector, followed by 8-byte array length and the data,
- enum types contain a header with a 4-byte discriminant (tells which enum variant is selected) + additional data,
- option types are serialized using a 1-byte discriminant followed by the bytes of data. If a value is
None
discriminant is set to 0 and the data part is empty, otherwise it is set to 1 with data serialized according to its type, - struct fields are serialized one by one using the rules above,
- tuples are serialized like structs.
Enum types in Rust are more advanced than in other languages. Apart from classic enum types, e.g.:
enum CompressionType {
Gzip,
Bzip2
}
it is also possible to create an enum which contains data fields, e.g.:
enum SomeEnum {
Variant1(u64),
Variant2(SomeType)
}
struct SomeType {
x: u32,
y: u16,
}
In the first case, the serialized object of the CompressionType
enum will only contain a 4-byte header with the discriminant value set to the selected variant (0 = GZip
, 1 = Bzip2
). In the latter case apart from the header the serialized data will contain additional bytes according to which variant was selected:
Variant1
: 8 bytesVariant2
: 6 bytes (the sum ofx
andy
fields ofSomeType
struct)
Special care needs to be taken when deserializing such enum as according to the selected variant number of following data bytes may be different.
It is sent by nodes who want to share information with others. Nodes gather data from their crds
and send push messages to their peers periodically.
A node receiving a set of push messages will:
- check for duplicate
CrdsValue
s and drop them - insert new
CrdsValue
s into thecrds
- transmit newly inserted
CrdsValue
s to their peers via push message.
Data | Type | Size | Description |
---|---|---|---|
Pubkey |
[u8; 32] |
32 | a public key of the origin |
CrdsValuesList |
[CrdsValue] |
8+ | a list of values to share |
Solana client Rust implementation
enum Protocol {
//...
PushMessage(Pubkey, Vec<CrdsValue>),
//...
}
A node sends a pull request to ask the cluster for new information. It creates a list of bloom filters for its crds
values and sends different bloom filters to different peers. The recipients of pull requests check what info the sender is missing using the received bloom filter and then construct a pull response packed with missing CrdsValue
s data for the pull request origin.
Data | Type | Size | Description |
---|---|---|---|
CrdsFilter |
CrdsFilter |
37+ | a bloom filter representing things node already has |
CrdsValue |
CrdsValue |
? | a value, usually a LegacyContactInfo of the node that sends the pull request containing node socket addresses for different protocols (gossip, tvu, tpu, rpc, etc.) |
Data | Type | Size | Description |
---|---|---|---|
filter |
Bloom |
25+ | a bloom filter |
mask |
u64 |
8 | filter mask which defines the data stored in the bloom filter |
mask_bits |
u32 |
4 | number of mask bits, also defines a number of bloom filters as 2^mask_bits |
Data | Type | Size | Description |
---|---|---|---|
keys |
[u64] |
8+ | keys |
bits |
b[u64] |
9+ | bits |
num_bits_set |
u64 |
8 | number of bits |
Solana client Rust implementation
enum Protocol {
PullRequest(CrdsFilter, CrdsValue),
//...
}
struct CrdsFilter {
filter: Bloom,
mask: u64,
mask_bits: u32,
}
struct Bloom {
keys: Vec<u64>,
bits: BitVec<u64>,
num_bits_set: u64,
}
These are sent in response to a pull request. They contain filtered values from the node's crds
which pull request origin is missing.
Data | Type | Size | Description |
---|---|---|---|
Pubkey |
[u8; 32] |
32 | a public key of the origin |
CrdsValuesList |
[CrdsValue] |
8+ | a list of new values |
Solana client Rust implementation
enum Protocol {
//...
PullResponse(Pubkey, Vec<CrdsValue>),
//...
}
It is sent to peers with a list of origin nodes that should be pruned. No more push messages from pruned origin nodes should be sent by the recipient of this prune message to its sender.
Data | Type | Size | Description |
---|---|---|---|
Pubkey |
[u8, 32] |
32 | a public key of the origin |
PruneData |
PruneData |
144+ | a structure which contains prune details |
Data | Type | Size | Description |
---|---|---|---|
pubkey |
[u8, 32] |
32 | public key of the origin of this message |
prunes |
[[u8, 32]] |
8+ | public keys of origin nodes that should be pruned |
signature |
[u8, 64] |
64 | signature of this message |
destination |
[u8, 32] |
32 | a public key of the destination node for this message |
wallclock |
u64 |
8 | wallclock of the node that generated the message |
Solana client Rust implementation
enum Protocol {
//...
PruneMessage(Pubkey, PruneData),
//...
}
struct PruneData {
pubkey: Pubkey,
prunes: Vec<Pubkey,
signature: Signature,
destination: Pubkey,
wallclock: u64,
}
Note: for signing purposes, before serializing, the PruneData
struct is prefixed with the byte array [0xff, 'S', 'O', 'L', 'A', 'N', 'A', '_', 'P', 'R', 'U', 'N', 'E', '_', 'D', 'A', 'T', 'A']
.
Solana client Rust implementation
#[derive(Serialize)]
struct SignDataWithPrefix<'a> {
prefix: &'a [u8], // Should be a b"\xffSOLANA_PRUNE_DATA"
pubkey: &'a Pubkey,
prunes: &'a [Pubkey],
destination: &'a Pubkey,
wallclock: u64,
}
Nodes send ping messages frequently to their peers to check whether they are active. The node receiving the ping message should respond with a pong message.
Data | Type | Size | Description |
---|---|---|---|
from |
[u8, 32] |
32 | public key of the origin |
token |
[u8, 32] |
32 | 32 bytes token |
signature |
[u8, 64] |
64 | signature of the message |
Solana client Rust implementation
enum Protocol {
//...
PingMessage(Ping),
//...
}
struct Ping {
from: Pubkey,
token: [u8, 32],
signature: Signature,
}
Sent by node as a response to the ping message.
Data | Type | Size | Description |
---|---|---|---|
from |
[u8, 32] |
32 | public key of the origin |
hash |
[u8, 32] |
32 | hash of the received ping token prefixed by the "SOLANA_PING_PONG" string |
signature |
[u8, 64] |
64 | signature of the message |
Solana client Rust implementation
enum Protocol {
//...
PongMessage(Pong)
}
struct Pong {
from: Pubkey,
hash: Hash,
signature: Signature,
}
The CrdsValue
values that are sent in push messages, pull requests & pull responses contain the signature and the actual shared data:
Data | Type | Size | Description |
---|---|---|---|
signature |
[u8; 64] |
64 | signature of the origin |
data |
CrdsData |
? | data |
Solana client Rust implementation
struct CrdsValue {
signature: Signature,
data: CrdsData,
}
The CrdsData
is an enum and can be one of:
Enum ID | Type |
---|---|
0 | LegacyContactInfo |
1 | Vote |
2 | LowestSlot |
3 | LegacySnapshotHashes (deprecated) |
4 | AccountsHashes (deprecated) |
5 | EpochSlots |
6 | LegacyVersion |
7 | Version |
8 | NodeInstance |
9 | DuplicateShred |
10 | SnapshotHashes |
11 | ContactInfo |
12 | RestartLastVotedForkSlots |
13 | RestartHeaviestFork |
Solana client Rust implementation
enum CrdsData {
LegacyContactInfo(LegacyContactInfo),
Vote(VoteIndex, Vote),
LowestSlot(LowestSlotIndex, LowestSlot),
LegacySnapshotHashes(LegacySnapshotHashes),
AccountsHashes(AccountsHashes),
EpochSlots(EpochSlotsIndex, EpochSlots),
LegacyVersion(LegacyVersion),
Version(Version),
NodeInstance(NodeInstance),
DuplicateShred(DuplicateShredIndex, DuplicateShred),
SnapshotHashes(SnapshotHashes),
ContactInfo(ContactInfo),
RestartLastVotedForkSlots(RestartLastVotedForkSlots),
RestartHeaviestFork(RestartHeaviestFork),
}
Basic info about the node. Nodes send this message to introduce themselves to the cluster and provide all addresses and ports that their peers can use to communicate with them.
Data | Type | Size | Description |
---|---|---|---|
id |
[u8; 32] |
32 | public key of the origin |
gossip |
SocketAddr |
10 or 22 | gossip protocol address |
tvu |
SocketAddr |
10 or 22 | address to connect to for replication |
tvu_quic |
SocketAddr |
10 or 22 | TVU over QUIC protocol |
serve_repair_quic |
SocketAddr |
10 or 22 | repair service for QUIC protocol |
tpu |
SocketAddr |
10 or 22 | transactions address |
tpu_forwards |
SocketAddr |
10 or 22 | address to forward unprocessed transactions |
tpu_vote |
SocketAddr |
10 or 22 | address for sending votes |
rpc |
SocketAddr |
10 or 22 | address for JSON-RPC requests |
rpc_pubsub |
SocketAddr |
10 or 22 | websocket for JSON-RPC push notifications |
serve_repair |
SocketAddr |
10 or 22 | address for sending repair requests |
wallclock |
u64 |
8 | wallclock of the node that generated the message |
shred_version |
u16 |
2 | the shred version node has been configured to use |
An enum, can be either V4 or V6 socket address.
Enum ID | Data | Type | Size | Description |
---|---|---|---|---|
0 | V4 |
SocketAddrV4 |
10 | V4 socket address |
1 | V6 |
SocketAddrV6 |
22 | V6 socket address |
Data | Type | Size | Description |
---|---|---|---|
ip |
[u8; 4] |
4 | ip address |
port |
u16 |
2 | port |
Data | Type | Size | Description |
---|---|---|---|
ip |
[u8; 16] |
16 | ip address |
port |
u16 |
2 | port |
Solana client Rust implementation
struct LegacyContactInfo {
id: Pubkey,
gossip: SocketAddr,
tvu: SocketAddr,
tvu_quic: SocketAddr,
serve_repair_quic: SocketAddr,
tpu: SocketAddr,
tpu_forwards: SocketAddr,
tpu_vote: SocketAddr,
rpc: SocketAddr,
rpc_pubsub: SocketAddr,
serve_repair: SocketAddr,
wallclock: u64,
shred_version: u16,
}
enum SocketAddr {
V4(SocketAddrV4),
V6(SocketAddrV6)
}
struct SocketAddrV4 {
ip: Ipv4Addr,
port: u16,
}
struct SocketAddrV6 {
ip: Ipv6Addr,
port: u16
}
struct Ipv4Addr {
octets: [u8; 4]
}
struct Ipv6Addr {
octets: [u8; 16]
}
It's a validator's vote on a fork. Contains a one-byte index from the vote tower (range 0 to 31) and vote transaction to be executed by the leader.
Data | Type | Size | Description |
---|---|---|---|
index |
u8 |
1 | vote tower index |
from |
[u8; 32] |
32 | public key of the origin |
transaction |
Transaction |
59+ | a vote transaction, an atomically-committed sequence of instructions |
wallclock |
u64 |
8 | wallclock of the node that generated the message |
slot |
u64 |
8 | slot in which the vote was created |
Contains a signature and a message with a sequence of instructions.
Data | Type | Size | Description |
---|---|---|---|
signature |
[[u8; 64]] |
8+ | list of signatures equal to num_required_signatures for the message |
message |
Message |
51+ | transaction message containing instructions to invoke |
Data | Type | Size | Description |
---|---|---|---|
header |
MessageHeader |
3 | message header |
account_keys |
[[u8; 32]] |
8+ | all account keys used by this transaction |
recent_blockhash |
[u8; 32] |
32 | hash of a recent ledger entry |
instructions |
[CompiledInstruction] |
8+ | list of compiled instructions to execute |
Data | Type | Size | Description |
---|---|---|---|
num_required_signatures |
u8 |
1 | number of signatures required for this message to be considered valid |
num_readonly_signed_accounts |
u8 |
1 | last num_readonly_signed_accounts of the signed keys are read-only accounts |
num_readonly_unsigned_accounts |
u8 |
1 | last num_readonly_unsigned_accounts of the unsigned keys are read-only accounts |
Data | Type | Size | Description |
---|---|---|---|
program_id_index |
u8 |
1 | index of the transaction keys array indicating the program account ID that executes the program |
accounts |
[u8] |
8+ | indices of the transaction keys array indicating the accounts that are passed to a program |
data |
[u8] |
8+ | program input data |
Solana client Rust implementation
enum CrdsData {
//...
Vote(VoteIndex, Vote),
//...
}
type VoteIndex = u8;
struct Vote {
from: Pubkey,
transaction: Transaction,
wallclock: u64,
slot: Option<Slot>,
}
type Slot = u64
struct Transaction {
signature: Vec<Signature>,
message: Message
}
struct Message {
header: MessageHeader,
account_keys: Vec<Pubkey>,
recent_blockhash: Hash,
instructions: Vec<CompiledInstruction>,
}
struct MessageHeader {
num_required_signatures: u8,
num_readonly_signed_accounts: u8,
num_readonly_unsigned_accounts: u8,
}
struct CompiledInstruction {
program_id_index: u8,
accounts: Vec<u8>,
data: Vec<u8>,
}
It is the first available slot in Solana blockstore that contains any data. Contains a one-byte index (deprecated) and the lowest slot number.
Data | Type | Size | Description |
---|---|---|---|
index |
u8 |
1 | deprecated |
from |
[u8; 32] |
32 | public key of the origin |
root |
u64 |
8 | deprecated |
lowest |
u64 |
8 | the actual slot |
slots |
[u64] |
8+ | deprecated |
stash |
[EpochIncompleteSlots] |
8+ | deprecated |
wallclock |
u64 |
8 | wallclock of the node that generated the message |
Data | Type | Size | Description |
---|---|---|---|
first |
u64 |
8 | first slot number |
compression |
CompressionType |
4 | compression type |
compressed_list |
[u8] |
8+ | compressed slots list |
Compression type enum.
Enum ID | Data | Description |
---|---|---|
0 | Uncompressed |
uncompressed |
1 | GZip |
gzip |
2 | BZip2 |
bzip2 |
Solana client Rust implementation
enum CrdsData {
//...
LowestSlot(LowestSlotIndex, LowestSlot),
//...
}
type LowestSlotIndex = u8;
struct LowestSlot {
from: Pubkey,
root: Slot,
lowest: Slot,
slots: BTreeSet<Slot>,
stash: Vec<EpochIncompleteSlots>,
wallclock: u64,
}
struct EpochIncompleteSlots {
first: Slot,
compression: CompressionType,
compressed_list: Vec<u8>,
}
enum CompressionType {
Uncompressed,
GZip,
BZip2,
}
Deprecated
Data | Type | Size | Description |
---|---|---|---|
from |
[u8, 32] |
32 | public key of the origin |
hashes |
[(u64, [u8, 32])] |
8+ | a list of hashes grouped by slots |
wallclock |
u64 |
8 | wallclock of the node that generated the message |
Solana client Rust implementation
struct AccountsHashes {
from: Pubkey,
hashes: Vec<(Slot, Hash)>,
wallclock: u64,
}
type LegacySnapshotHashes = AccountsHashes;
Contains a one-byte index and list of all slots from an epoch (epoch consists of around 432000 slots). There can be 256 epoch slots in total.
Data | Type | Size | Description |
---|---|---|---|
index |
u8 |
1 | index |
from |
[u8, 32] |
32 | public key of the origin |
slots |
[CompressedSlots] |
8+ | list of slots |
wallclock |
u64 |
8 | wallclock of the node that generated the message |
EnumID | Data | Type | Size | Description |
---|---|---|---|---|
0 | Flate2 |
Flate2 |
24+ | Flate 2 compression |
1 | Uncompressed |
Uncompressed |
25+ | no compression |
Data | Type | Size | Description |
---|---|---|---|
first_slot |
u64 |
8 | first slot number |
num |
u64 |
8 | number of slots |
compressed |
[u8] |
8+ | bytes array of compressed slots |
Data | Type | Size | Description |
---|---|---|---|
first_slot |
u64 |
8 | first slot number |
num |
u64 |
8 | number of slots |
slots |
b[u8] |
9+ | bits array of slots |
Solana client Rust implementation
enum CrdsData {
//...
EpochSlots(EpochSlotsIndex, EpochSlots),
//...
}
type EpochSlotsIndex = u8;
struct EpochSlots {
from: Pubkey,
slots: Vec<CompressedSlots>,
wallclock: u64,
}
enum CompressedSlots {
Flate2(Flate2),
Uncompressed(Uncompressed),
}
struct Flate2 {
first_slot: Slot,
num: usize,
compressed: Vec<u8>
}
struct Uncompressed {
first_slot: Slot,
num: usize,
slots: BitVec<u8>,
}
The older version of the Solana client the node is using.
Data | Type | Size | Description |
---|---|---|---|
from |
[u8, 32] |
32 | public key of origin |
wallclock |
u64 |
8 | wallclock of the node that generated the message |
version |
LegacyVersion1 |
7 or 11 | older version of the Solana used in 1.3.x and earlier releases |
Data | Type | Size | Description |
---|---|---|---|
major |
u16 |
2 | major part of version |
minor |
u16 |
2 | minor part of version |
patch |
u16 |
2 | patch |
commit |
u32 | None |
5 or 1 | commit |
Solana client Rust implementation
struct LegacyVersion {
from: Pubkey,
wallclock: u64,
version: LegacyVersion1,
}
struct LegacyVersion1 {
major: u16,
minor: u16,
patch: u16,
commit: Option<u32>
}
The version of the Solana client the node is using.
Data | Type | Size | Description |
---|---|---|---|
from |
[u8, 32] |
32 | public key of origin |
wallclock |
u64 |
8 | wallclock of the node that generated the message |
version |
LegacyVersion2 |
11 or 15 | version of the Solana |
Data | Type | Size | Description |
---|---|---|---|
major |
u16 |
2 | major part of version |
minor |
u16 |
2 | minor part of version |
patch |
u16 |
2 | patch |
commit |
u32 | None |
5 or 1 | commit |
feature_set |
u32 |
4 | feature set |
Solana client Rust implementation
struct Version {
from: Pubkey,
wallclock: u64,
version: LegacyVersion2,
}
struct LegacyVersion2 {
major: u16,
minor: u16,
patch: u16,
commit: Option<u32>,
feature_set: u32
}
Contains node creation timestamp and randomly generated token.
Data | Type | Size | Description |
---|---|---|---|
from |
[u8, 32] |
32 | public key of origin |
wallclock |
u64 |
8 | wallclock of the node that generated the message |
timestamp |
u64 |
8 | timestamp when the instance was created |
token |
u64 |
8 | randomly generated value at node instantiation |
Solana client Rust implementation
struct NodeInstance {
from: Pubkey,
wallclock: u64,
timestamp: u64,
token: u64,
}
A duplicated shred proof. Contains a 2-byte index followed by other data:
Data | Type | Size | Description |
---|---|---|---|
index |
u16 |
2 | index |
from |
[u8, 32] |
32 | public key of origin |
wallclock |
u64 |
8 | wallclock of the node that generated the message |
slot |
u64 |
8 | slot when shreds where created |
_unused |
u32 |
4 | unused |
_unused_shred_type |
ShredType |
1 | unused |
num_chunks |
u8 |
1 | number of chunks available |
chunk_index |
u8 |
1 | index of the chunk |
chunk |
[u8] |
8+ | shred data |
This enum is serialized as 1-byte data.
Enum ID | Data | Description |
---|---|---|
0b10100101 |
Data |
data shred |
0b01011010 |
Code |
coding shred |
Solana client Rust implementation
enum CrdsData {
//...
DuplicateShred(DuplicateShredIndex, DuplicateShred),
//...
}
type DuplicateShredIndex = u16;
struct DuplicateShred {
from: Pubkey,
wallclock: u64,
slot: Slot,
_unused: u32,
_unused_shred_type: ShredType,
num_chunks: u8,
chunk_index: u8,
chunk: Vec<u8>,
}
#[serde(into = "u8", try_from = "u8")]
enum ShredType {
Data = 0b1010_0101,
Code = 0b0101_1010,
}
Contains hashes of full and incremental snapshots.
Data | Type | Size | Description |
---|---|---|---|
from |
[u8, 32] |
32 | public key of origin |
full |
(u64, [u8, 32]) |
40 | hash and slot number of the full snapshot |
incremental |
[(u64, [u8, 32])] |
8+ | list of hashes and slot numbers of incremental snapshots |
wallclock |
u64 |
8 | wallclock of the node that generated the message |
Solana client Rust implementation
struct SnapshotHashes {
from: Pubkey,
full: (Slot, Hash),
incremental: Vec<(Slot, Hash)>,
wallclock: u64,
}
Contact info of the node.
Data | Type | Size | Description |
---|---|---|---|
pubkey |
[u8, 32] |
32 | public key of origin |
wallclock |
u64 |
8 | wallclock of the node that generated the message |
outset |
u64 |
8 | timestamp when node instance was first created |
shred_version |
u16 |
2 | the shred version the node has been configured to use |
version |
Version |
13+ | Solana version |
addrs |
[IpAddr] |
8+ | list of unique IP addresses |
sockets |
[SocketEntry] |
8+ | list of unique sockets |
extensions |
[Extension] |
8+ | future additions to ContactInfo will be added to Extensions instead of modifying ContactInfo, currently unused |
cache |
[SocketAddr, 12] |
120 or 264 | cache of nodes socket addresses |
Data | Type | Size | Description |
---|---|---|---|
major |
u16 |
2 | major part of version |
minor |
u16 |
2 | minor part of version |
patch |
u16 |
2 | patch |
commit |
u32 | None |
5 or 1 | commit |
feature_set |
u32 |
4 | feature set |
client |
u16 |
2 | client |
Enum ID | Data | Type | Size | Description |
---|---|---|---|---|
0 | V4 |
[u8; 4] |
4 | IP v4 addr |
1 | V6 |
[u8, 16] |
16 | IP v6 addr |
Data | Type | Size | Description |
---|---|---|---|
key |
u8 |
1 | protocol identifier (tvu, tpu, etc.) |
index |
u8 |
1 | IpAddr index in the addrs list |
offset |
u16 |
2 | port offset in respect to previous entry |
Currently empty
Solana client Rust implementation
struct ContactInfo {
pubkey: Pubkey,
wallclock: u64,
outset: u64,
shred_version: u16,
version: Version,
addrs: Vec<IpAddr>,
sockets: Vec<SocketEntry>,
extensions: Vec<Extension>,
cache: [SocketAddr; 12],
}
enum Extension {}
enum IpAddr {
V4(Ipv4Addr),
V6(Ipv4Addr)
}
struct Ipv4Addr {
octets: [u8; 4]
}
struct Ipv6Addr {
octets: [u8; 16]
}
struct SocketEntry {
key: u8,
index: u8,
offset: u16
}
struct Version {
major: u16,
minor: u16,
patch: u16,
commit: Option<u32>,
feature_set: u32,
client: u16
}
Contains a list of last-voted fork slots.
Data | Type | Size | Description |
---|---|---|---|
from |
[u8, 32] |
32 | public key of origin |
wallclock |
u64 |
8 | wallclock of the node that generated the message |
offsets |
SlotsOffsets |
12+ | list of slots |
last_voted_slot |
u64 |
8 | last voted slot |
last_voted_hash |
[u8, 32] |
32 | |
shred_version |
u16 |
2 | the shred version node has been configured to use |
Offsets are stored either in binary form (RawOffsets
) or encoded as numbers of consecutive 1's and 0's, e.g. 110001110 is [2, 3, 3, 1].
Enum ID | Data | Type | Size | Description |
---|---|---|---|---|
0 | RunLengthEncoding |
[u16] |
8+ | encoded offsets |
1 | RawOffsets |
b[u8] |
9+ | raw offsets |
Solana client Rust implementation
struct RestartLastVotedForkSlots {
from: Pubkey,
wallclock: u64,
offsets: SlotsOffsets,
last_voted_slot: Slot,
last_voted_hash: Hash,
shred_version: u16,
}
enum SlotsOffsets {
RunLengthEncoding(RunLengthEncoding),
RawOffsets(RawOffsets),
}
struct RunLengthEncoding(Vec<u16>);
struct RawOffsets(BitVec<u8>);
Contains the heaviest fork.
Data | Type | Size | Description |
---|---|---|---|
from |
[u8, 32] |
32 | public key of origin |
wallclock |
u64 |
8 | wallclock of the node that generated the message |
last_slot |
u64 |
8 | last slot of the fork |
last_hash |
[u8, 32] |
32 | hash of the last slot |
observed_stake |
u64 |
8 | |
shred_version |
u16 |
2 | the shred version node has been configured to use |
Solana client Rust implementation
struct RestartHeaviestFork {
from: Pubkey,
wallclock: u64,
last_slot: Slot,
last_slot_hash: Hash,
observed_stake: u64,
shred_version: u16,
}
The IP Echo Server is a server running on the TCP socket on the same gossip address. (e.g. if the node is running the gossip service on UDP socket 192.0.0.1:9000, then the IP server port is running on the same TCP socket 192.0.0.1:9000).
Before the node starts the gossip service, the node first needs to:
- find out the shred version of the cluster,
- discover its public IP address,
- check TCP/UDP port reachability.
All of these are discovered via the IP echo server running on one of the provided entrypoint nodes. Note: all validators run an IP Echo Server.
The node should create sockets with ports that need to be checked and then send an IP echo server request message to one of the entrypoint nodes.
The entrypoint node will then check reachability for all ports listed in the request and then it will respond with an IP echo server response containing the shred_version
and public IP of the node.
IP echo server message request containing a list of ports whose reachability should be checked by the server:
- UDP ports - the server should send a single byte
[0x00]
packet to the socket. - TCP ports - the server should establish a TCP connection with every TCP socket.
Data | Type | Size | Description |
---|---|---|---|
tcp_ports |
[u16, 4] |
64 | TCP ports that should be checked |
udp_ports |
[u16, 4] |
64 | UDP ports that should be checked |
Solana client Rust implementation
/// Echo server message request.
pub struct IpEchoServerMessage {
pub tcp_ports: [u16; 4],
pub udp_ports: [u16; 4],
}
IP echo server message response.
Data | Type | Size | Description |
---|---|---|---|
address |
IpAddr |
4 or 16 | public IP of the node that sent the request |
shred_version |
u16 |
2 | shred verion of the cluster |
Solana client Rust implementation
/// Echo server response.
pub struct IpEchoServerResponse {
/// Public IP address of request echoed back to the node.
pub address: IpAddr,
/// Cluster shred-version of the node running the server.
pub shred_version: Option<u16>,
}