The network is robust because it is centralized right now. When the network is under heavy load network propagation delays cause minority witnesses to miss their slots.
We have been studying this problem and have concluded that the current P2P algorithm is not well suited to 1 second blocks.
Assuming 2 nodes are across the globe and have a 250 ms latency, then the following handshake takes 0.75 seconds with 0 data and CPU delays:
notify inventory
request item
receive item
That is barely good enough for two peers to keep in sync if directly connected. But when you have 2 peers connected through a middle man then we are talking 1.5 seconds which breaks down with 1 second blocks.
This is the one part of the code that we have been reusing from BTS 1.0 and it appears it is not up to the task. So today Ben and I came up with a new, simple, protocol that should dramatically improve network performance.
# Network Protocol 2
Building a low-latency network requires P2P nodes that have low-latency
connections and a protocol designed to minimize latency. for the purpose
of this document we will assume that two nodes are located on opposite
sides of the globe with a ping time of 250ms.
## Announce, Request, Send Protocol
Under the prior network archtiecture, transactions and blocks were broadcast
in a manner similar to the Bitcoin protocol: inventory messages notify peers of
transactions and blocks, then peers fetch the transaction or block from one
peer. After validating the item a node will broadcast an inventory message to
its peers.
Under this model it will take 0.75 seconds for a peer to communicate a transaction
or block to another peer even if their size was 0 and there was no processing overhead.
This level of performance is unacceptable for a network attempting to produce one block
every second.
This prior protocol also sent every transaction twice: initial broadcast, and again as
part of a block.
## Push Protocol
To minimize latency each node needs to immediately broadcast the data it receives
to its peers after validating it. Given the average transaction size is less than
100 bytes, it is almost as effecient to send the transaction as it is to send
the notice (assuming a 20 byte transaction id)
Each node implements the following protocol:
onReceiveTransaction( from_peer, transaction )
if( isKnown( transaction.id() ) )
return
markKnown( transaction.id() )
if( !validate( transaction ) )
return
for( peer : peers )
if( peer != from_peer )
send( peer, transaction )
onReceiveBlock( from_peer, block_summary )
if( isKnown( block_summary )
return
full_block = reconstructFullBlcok( from_peer, block_summary )
if( !full_block ) disconnect from_peer
markKnown( block_summary )
if( !pushBlock( full_block ) ) disconnect from_peer
for( peer : peers )
if( peer != from_peer )
send( peer, block_summary )
onConnect( new_peer, new_peer_head_block_num )
if( peers.size() >= max_peers )
send( new_peer, peers )
disconnect( new_peer )
return
while( new_peer_head_block_num < our_head_block_num )
sendFullBlock( new_peer, ++new_peer_head_block_num )
new_peer.synced = true
for( peer : peers )
send( peer, new_peer )
onReceivePeers( from_peer, peers )
addToPotentialPeers( peers )
onUpdateConnectionsTimer
if( peers.size() < desired_peers )
connect( random_potential_peer )
onFullBlock( from_peer, full_block )
if( !pushBlock( full_block ) ) disconnect from_peer
onStartup
init_potential_peers from config
start onUpdateConnectionsTimer