Skip to content

Commit db60920

Browse files
Merge pull request bnb-chain#4 from Loverush/develop
[WIP]Fast Finality: p2p protocol
2 parents 21a3b11 + 9ae0983 commit db60920

12 files changed

+328
-29
lines changed

core/events.go

+4-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import (
2121
"github.com/ethereum/go-ethereum/core/types"
2222
)
2323

24-
// NewTxsEvent is posted when a batch of transactions enter the transaction pool.
24+
// NewTxsEvent is posted when a batch of transactions enters the transaction pool.
2525
type NewTxsEvent struct{ Txs []*types.Transaction }
2626

2727
// ReannoTxsEvent is posted when a batch of local pending transactions exceed a specified duration.
@@ -33,6 +33,9 @@ type NewMinedBlockEvent struct{ Block *types.Block }
3333
// RemovedLogsEvent is posted when a reorg happens
3434
type RemovedLogsEvent struct{ Logs []*types.Log }
3535

36+
// NewVotesEvent is posted when a batch of votes enters the vote pool.
37+
type NewVotesEvent struct{ Votes []*types.VoteEnvelope }
38+
3639
type ChainEvent struct {
3740
Block *types.Block
3841
Hash common.Hash

core/types/vote.go

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package types
2+
3+
import (
4+
"sync/atomic"
5+
6+
"github.com/ethereum/go-ethereum/common"
7+
)
8+
9+
const (
10+
BLSPublicKeyLength = 48
11+
BLSSignatureLength = 96
12+
)
13+
14+
type BLSPublicKey [BLSPublicKeyLength]byte
15+
type BLSSignature [BLSSignatureLength]byte
16+
17+
type VoteData struct {
18+
BlockNumber uint64
19+
BlockHash common.Hash
20+
}
21+
22+
type VoteEnvelope struct {
23+
VoteAddress BLSPublicKey
24+
Signature BLSSignature
25+
Data VoteData
26+
27+
// caches
28+
hash atomic.Value
29+
}
30+
31+
type VoteEnvelopes []*VoteEnvelope
32+
33+
// Hash returns the vote hash.
34+
func (v *VoteEnvelope) Hash() common.Hash {
35+
if hash := v.hash.Load(); hash != nil {
36+
return hash.(common.Hash)
37+
}
38+
39+
h := v.calcVoteHash()
40+
v.hash.Store(h)
41+
return h
42+
}
43+
44+
func (v *VoteEnvelope) calcVoteHash() common.Hash {
45+
voteData := struct {
46+
VoteAddress BLSPublicKey
47+
Signature BLSSignature
48+
Data VoteData
49+
}{v.VoteAddress, v.Signature, v.Data}
50+
return rlpHash(voteData)
51+
}

eth/fetcher/tx_fetcher.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,7 @@ func (f *TxFetcher) Notify(peer string, hashes []common.Hash) error {
260260
// Enqueue imports a batch of received transaction into the transaction pool
261261
// and the fetcher. This method may be called by both transaction broadcasts and
262262
// direct request replies. The differentiation is important so the fetcher can
263-
// re-shedule missing transactions as soon as possible.
263+
// re-schedule missing transactions as soon as possible.
264264
func (f *TxFetcher) Enqueue(peer string, txs []*types.Transaction, direct bool) error {
265265
// Keep track of all the propagated transactions
266266
if direct {

eth/handler.go

+68-3
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ import (
4545
const (
4646
// txChanSize is the size of channel listening to NewTxsEvent.
4747
// The number is referenced from the size of tx pool.
48-
txChanSize = 4096
48+
txChanSize = 256
49+
voteChanSize = 4096
4950
)
5051

5152
var (
@@ -79,12 +80,25 @@ type txPool interface {
7980
SubscribeReannoTxsEvent(chan<- core.ReannoTxsEvent) event.Subscription
8081
}
8182

83+
// votePool defines the methods needed from a votes pool implementation to
84+
// support all the operations needed by the Ethereum chain protocols.
85+
type votePool interface {
86+
PutVote(vote *types.VoteEnvelope) error
87+
Get(hash common.Hash) *types.VoteEnvelope
88+
GetVotes() types.VoteEnvelopes
89+
90+
// SubscribeNewVotesEvent should return an event subscription of
91+
// NewVotesEvent and send events to the given channel.
92+
SubscribeNewVotesEvent(chan<- core.NewVotesEvent) event.Subscription
93+
}
94+
8295
// handlerConfig is the collection of initialization parameters to create a full
8396
// node network handler.
8497
type handlerConfig struct {
8598
Database ethdb.Database // Database for direct sync insertions
8699
Chain *core.BlockChain // Blockchain to serve data from
87100
TxPool txPool // Transaction pool to propagate from
101+
VotePool votePool // Votes pool to propagate from
88102
Network uint64 // Network identifier to adfvertise
89103
Sync downloader.SyncMode // Whether to fast or full sync
90104
DiffSync bool // Whether to diff sync
@@ -112,6 +126,7 @@ type handler struct {
112126

113127
database ethdb.Database
114128
txpool txPool
129+
votepool votePool
115130
chain *core.BlockChain
116131
maxPeers int
117132

@@ -127,6 +142,8 @@ type handler struct {
127142
reannoTxsCh chan core.ReannoTxsEvent
128143
reannoTxsSub event.Subscription
129144
minedBlockSub *event.TypeMuxSubscription
145+
votesCh chan core.NewVotesEvent
146+
votesSub event.Subscription
130147

131148
whitelist map[uint64]common.Hash
132149

@@ -152,6 +169,7 @@ func newHandler(config *handlerConfig) (*handler, error) {
152169
eventMux: config.EventMux,
153170
database: config.Database,
154171
txpool: config.TxPool,
172+
votepool: config.VotePool,
155173
chain: config.Chain,
156174
peers: newPeerSet(),
157175
whitelist: config.Whitelist,
@@ -332,9 +350,10 @@ func (h *handler) runEthPeer(peer *eth.Peer, handler eth.Handler) error {
332350
}
333351
h.chainSync.handlePeerEvent(peer)
334352

335-
// Propagate existing transactions. new transactions appearing
353+
// Propagate existing transactions and votes. new transactions and votes appearing
336354
// after this will be sent via broadcasts.
337355
h.syncTransactions(peer)
356+
h.syncVotes(peer)
338357

339358
// If we have a trusted CHT, reject all peers below that (avoid fast sync eclipse)
340359
if h.checkpointHash != (common.Hash{}) {
@@ -438,6 +457,12 @@ func (h *handler) Start(maxPeers int) {
438457
h.txsSub = h.txpool.SubscribeNewTxsEvent(h.txsCh)
439458
go h.txBroadcastLoop()
440459

460+
// broadcast votes
461+
h.wg.Add(1)
462+
h.votesCh = make(chan core.NewVotesEvent, voteChanSize)
463+
h.votesSub = h.votepool.SubscribeNewVotesEvent(h.votesCh)
464+
go h.voteBroadcastLoop()
465+
441466
// announce local pending transactions again
442467
h.wg.Add(1)
443468
h.reannoTxsCh = make(chan core.ReannoTxsEvent, txChanSize)
@@ -510,7 +535,7 @@ func (h *handler) BroadcastBlock(block *types.Block, propagate bool) {
510535
log.Trace("Propagated block", "hash", hash, "recipients", len(transfer), "duration", common.PrettyDuration(time.Since(block.ReceivedAt)))
511536
return
512537
}
513-
// Otherwise if the block is indeed in out own chain, announce it
538+
// Otherwise if the block is indeed in our own chain, announce it
514539
if h.chain.HasBlock(hash, block.NumberU64()) {
515540
for _, peer := range peers {
516541
peer.AsyncSendNewBlockHash(block)
@@ -580,6 +605,33 @@ func (h *handler) ReannounceTransactions(txs types.Transactions) {
580605
"announce packs", peersCount, "announced hashes", peersCount*uint(len(hashes)))
581606
}
582607

608+
// BroadcastVotes will propagate a batch of votes to all peers
609+
// which are not known to already have the given vote.
610+
func (h *handler) BroadcastVotes(votes types.VoteEnvelopes) {
611+
var (
612+
directCount int // Count of announcements made
613+
directPeers int
614+
615+
voteset = make(map[*ethPeer][]*types.VoteEnvelope) // Set peer->hash to transfer directly
616+
)
617+
618+
// Broadcast votes to a batch of peers not knowing about it
619+
for _, vote := range votes {
620+
peers := h.peers.peersWithoutVote(vote.Hash())
621+
for _, peer := range peers {
622+
voteset[peer] = append(voteset[peer], vote)
623+
}
624+
}
625+
626+
for peer, _votes := range voteset {
627+
directPeers++
628+
directCount += len(_votes)
629+
peer.AsyncSendVotes(_votes)
630+
}
631+
log.Debug("Vote broadcast", "votes", len(votes),
632+
"vote packs", directPeers, "broadcast votes", directCount)
633+
}
634+
583635
// minedBroadcastLoop sends mined blocks to connected peers.
584636
func (h *handler) minedBroadcastLoop() {
585637
defer h.wg.Done()
@@ -617,3 +669,16 @@ func (h *handler) txReannounceLoop() {
617669
}
618670
}
619671
}
672+
673+
// voteBroadcastLoop announces new transactions to connected peers.
674+
func (h *handler) voteBroadcastLoop() {
675+
defer h.wg.Done()
676+
for {
677+
select {
678+
case event := <-h.votesCh:
679+
h.BroadcastVotes(event.Votes)
680+
case <-h.votesSub.Err():
681+
return
682+
}
683+
}
684+
}

eth/handler_eth.go

+16
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ type ethHandler handler
4040
func (h *ethHandler) Chain() *core.BlockChain { return h.chain }
4141
func (h *ethHandler) StateBloom() *trie.SyncBloom { return h.stateBloom }
4242
func (h *ethHandler) TxPool() eth.TxPool { return h.txpool }
43+
func (h *ethHandler) VotePool() eth.VotePool { return h.votepool }
4344

4445
// RunPeer is invoked when a peer joins on the `eth` protocol.
4546
func (h *ethHandler) RunPeer(peer *eth.Peer, hand eth.Handler) error {
@@ -99,6 +100,9 @@ func (h *ethHandler) Handle(peer *eth.Peer, packet eth.Packet) error {
99100

100101
case *eth.PooledTransactionsPacket:
101102
return h.txFetcher.Enqueue(peer.ID(), *packet, true)
103+
104+
case *eth.VotesPacket:
105+
return h.handleVotesBroadcast(peer, *packet)
102106
default:
103107
return fmt.Errorf("unexpected eth packet type: %T", packet)
104108
}
@@ -225,3 +229,15 @@ func (h *ethHandler) handleBlockBroadcast(peer *eth.Peer, block *types.Block, td
225229
}
226230
return nil
227231
}
232+
233+
// handleVotesBroadcast is invoked from a peer's message handler when it transmits a
234+
// votes broadcast for the local node to process.
235+
func (h *ethHandler) handleVotesBroadcast(peer *eth.Peer, votes []*types.VoteEnvelope) error {
236+
// Try to put votes into votepool
237+
for _, vote := range votes {
238+
if err := h.votepool.PutVote(vote); err != nil {
239+
return err
240+
}
241+
}
242+
return nil
243+
}

eth/peerset.go

+16-1
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,7 @@ func (ps *peerSet) headPeers(num uint) []*ethPeer {
311311
}
312312

313313
// peersWithoutBlock retrieves a list of peers that do not have a given block in
314-
// their set of known hashes so it might be propagated to them.
314+
// their set of known hashes, so it might be propagated to them.
315315
func (ps *peerSet) peersWithoutBlock(hash common.Hash) []*ethPeer {
316316
ps.lock.RLock()
317317
defer ps.lock.RUnlock()
@@ -340,6 +340,21 @@ func (ps *peerSet) peersWithoutTransaction(hash common.Hash) []*ethPeer {
340340
return list
341341
}
342342

343+
// peersWithoutVote retrieves a list of peers that do not have a given
344+
// vote in their set of known hashes.
345+
func (ps *peerSet) peersWithoutVote(hash common.Hash) []*ethPeer {
346+
ps.lock.RLock()
347+
defer ps.lock.RUnlock()
348+
349+
list := make([]*ethPeer, 0, len(ps.peers))
350+
for _, p := range ps.peers {
351+
if !p.KnownVote(hash) {
352+
list = append(list, p)
353+
}
354+
}
355+
return list
356+
}
357+
343358
// len returns if the current number of `eth` peers in the set. Since the `snap`
344359
// peers are tied to the existence of an `eth` connection, that will always be a
345360
// subset of `eth`.

eth/protocols/eth/broadcast.go

+21
Original file line numberDiff line numberDiff line change
@@ -200,3 +200,24 @@ func (p *Peer) announceTransactions() {
200200
}
201201
}
202202
}
203+
204+
// broadcastVotes is a write loop that schedules votes broadcasts
205+
// to the remote peer. The goal is to have an async writer that does not lock up
206+
// node internals and at the same time rate limits queued data.
207+
func (p *Peer) broadcastVotes() {
208+
for {
209+
select {
210+
case votes := <-p.voteBroadcast:
211+
if err := p.SendVotes(votes); err != nil {
212+
return
213+
}
214+
p.Log().Trace("Sent votes", "count", len(votes))
215+
216+
case <-p.voteTerm:
217+
return
218+
219+
case <-p.term:
220+
return
221+
}
222+
}
223+
}

eth/protocols/eth/handler.go

+33-3
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,9 @@ type Backend interface {
7979
// or if inbound transactions should simply be dropped.
8080
AcceptTxs() bool
8181

82+
// VotePool retrieves the votes pool object to serve data.
83+
VotePool() VotePool
84+
8285
// RunPeer is invoked when a peer joins on the `eth` protocol. The handler
8386
// should do any peer maintenance work, handshakes and validations. If all
8487
// is passed, control should be given back to the `handler` to process the
@@ -96,10 +99,15 @@ type Backend interface {
9699

97100
// TxPool defines the methods needed by the protocol handler to serve transactions.
98101
type TxPool interface {
99-
// Get retrieves the the transaction from the local txpool with the given hash.
102+
// Get retrieves the transaction from the local txpool with the given hash.
100103
Get(hash common.Hash) *types.Transaction
101104
}
102105

106+
type VotePool interface {
107+
// Get retrieves the vote from the local votepool with the given hash.
108+
Get(hash common.Hash) *types.VoteEnvelope
109+
}
110+
103111
// MakeProtocols constructs the P2P protocol definitions for `eth`.
104112
func MakeProtocols(backend Backend, network uint64, dnsdisc enode.Iterator) []p2p.Protocol {
105113
protocols := make([]p2p.Protocol, len(ProtocolVersions))
@@ -111,7 +119,7 @@ func MakeProtocols(backend Backend, network uint64, dnsdisc enode.Iterator) []p2
111119
Version: version,
112120
Length: protocolLengths[version],
113121
Run: func(p *p2p.Peer, rw p2p.MsgReadWriter) error {
114-
peer := NewPeer(version, p, rw, backend.TxPool())
122+
peer := NewPeer(version, p, rw, backend.TxPool(), backend.VotePool())
115123
defer peer.Close()
116124

117125
return backend.RunPeer(peer, func(peer *Peer) error {
@@ -206,6 +214,26 @@ var eth66 = map[uint64]msgHandler{
206214
PooledTransactionsMsg: handlePooledTransactions66,
207215
}
208216

217+
var eth68 = map[uint64]msgHandler{
218+
NewBlockHashesMsg: handleNewBlockhashes,
219+
NewBlockMsg: handleNewBlock,
220+
TransactionsMsg: handleTransactions,
221+
NewPooledTransactionHashesMsg: handleNewPooledTransactionHashes,
222+
// eth66 messages with request-id
223+
GetBlockHeadersMsg: handleGetBlockHeaders66,
224+
BlockHeadersMsg: handleBlockHeaders66,
225+
GetBlockBodiesMsg: handleGetBlockBodies66,
226+
BlockBodiesMsg: handleBlockBodies66,
227+
GetNodeDataMsg: handleGetNodeData66,
228+
NodeDataMsg: handleNodeData66,
229+
GetReceiptsMsg: handleGetReceipts66,
230+
ReceiptsMsg: handleReceipts66,
231+
GetPooledTransactionsMsg: handleGetPooledTransactions66,
232+
PooledTransactionsMsg: handlePooledTransactions66,
233+
// eth68 messages
234+
VotesMsg: handleVotes,
235+
}
236+
209237
// handleMessage is invoked whenever an inbound message is received from a remote
210238
// peer. The remote connection is torn down upon returning any error.
211239
func handleMessage(backend Backend, peer *Peer) error {
@@ -220,7 +248,9 @@ func handleMessage(backend Backend, peer *Peer) error {
220248
defer msg.Discard()
221249

222250
var handlers = eth65
223-
if peer.Version() >= ETH66 {
251+
if peer.Version() >= ETH68 {
252+
handlers = eth68
253+
} else if peer.Version() >= ETH66 {
224254
handlers = eth66
225255
}
226256
// Track the amount of time it takes to serve the request and run the handler

0 commit comments

Comments
 (0)