github.com/bigzoro/my_simplechain@v0.0.0-20240315012955-8ad0a2a29bb9/consensus/hotstuff/hotsptcl/handler.go (about)

     1  package hotstuffprotocol
     2  
     3  import (
     4  	"context"
     5  	"crypto/rand"
     6  	"errors"
     7  	"fmt"
     8  	"sync"
     9  	"time"
    10  
    11  	"github.com/bigzoro/my_simplechain/common"
    12  	"github.com/bigzoro/my_simplechain/consensus"
    13  	bls "github.com/bigzoro/my_simplechain/consensus/hotstuff/bls12-381"
    14  	hots "github.com/bigzoro/my_simplechain/consensus/hotstuff/common"
    15  	"github.com/bigzoro/my_simplechain/core/types"
    16  	"github.com/bigzoro/my_simplechain/log"
    17  	"github.com/bigzoro/my_simplechain/p2p"
    18  	"github.com/bigzoro/my_simplechain/p2p/enode"
    19  )
    20  
    21  var (
    22  	errNotLegalPeer      = errors.New("not a legal hotstuff peer")
    23  	errAlreadyRegistered = errors.New("replica is already registered")
    24  	errClosed            = errors.New("relica hub is closed")
    25  
    26  	handshakeTimeoutRTT = 5 * time.Second
    27  
    28  	// handshakeValidatorLen is the fixed length of the authentication random number.
    29  	// used during handshake
    30  	handshakeValidatorLen = 256
    31  )
    32  
    33  // handler handles Hotstuff protocol request messages.
    34  type handler interface {
    35  	Timeout(id hots.ID, view uint64) error
    36  
    37  	Vote(id hots.ID, view uint64, block []byte, sig *bls.PartialSignature) error
    38  }
    39  
    40  type replica struct {
    41  	id      hots.ID
    42  	handler handler
    43  
    44  	peer *p2p.Peer
    45  	rw   p2p.MsgReadWriter
    46  
    47  	queued chan *timeoutMsg
    48  	quit   chan struct{}
    49  }
    50  
    51  func newReplica(id hots.ID, peer *p2p.Peer, rw p2p.MsgReadWriter) *replica {
    52  	return &replica{
    53  		peer:   peer,
    54  		rw:     rw,
    55  		id:     id,
    56  		queued: make(chan *timeoutMsg),
    57  		quit:   make(chan struct{}),
    58  	}
    59  }
    60  
    61  func (rep *replica) handle() error {
    62  	msg, err := rep.rw.ReadMsg()
    63  	if err != nil {
    64  		return err
    65  	}
    66  	if msg.Size > protocolMaxMsgSize {
    67  		return fmt.Errorf("too large message,%d - %d", msg.Size, protocolMaxMsgSize)
    68  	}
    69  	defer msg.Discard()
    70  
    71  	switch msg.Code {
    72  	case vote:
    73  		var vote voteMsg
    74  		if err := msg.Decode(&vote); err != nil {
    75  			return fmt.Errorf("msg %v: %v", msg, err)
    76  		}
    77  		sig := new(bls.PartialSignature)
    78  		if err := sig.FromBytes(vote.Signature); err != nil {
    79  			return fmt.Errorf("signature deserialization:%v", err)
    80  		}
    81  		if err := rep.handler.Vote(rep.id, vote.View, vote.Hash.Bytes(), sig); err != nil {
    82  			rep.peer.Log().Error("handle vote request", "error", err)
    83  		}
    84  
    85  	case timeout:
    86  		var timeout timeoutMsg
    87  		if err := msg.Decode(&timeout); err != nil {
    88  			return fmt.Errorf("msg %v: %v", msg, err)
    89  		}
    90  		if err := rep.handler.Timeout(rep.id, timeout.View); err != nil {
    91  			rep.peer.Log().Error("handle timeout request", "error", err)
    92  		}
    93  
    94  	default:
    95  		return fmt.Errorf("invalid message code %d", msg.Code)
    96  	}
    97  	return nil
    98  }
    99  
   100  func (rep *replica) run(ctx context.Context) {
   101  	for {
   102  		select {
   103  		case msg := <-rep.queued:
   104  			p2p.Send(rep.rw, timeout, msg)
   105  
   106  		case <-ctx.Done():
   107  			return
   108  		case <-rep.quit:
   109  			return
   110  		}
   111  	}
   112  }
   113  
   114  func (rep *replica) close() error {
   115  	close(rep.quit)
   116  	return nil
   117  }
   118  
   119  type identifier interface {
   120  	Sign([]byte) (*bls.PartialSignature, error)
   121  
   122  	VerifyAt(*types.Header, *bls.PartialSignature, []byte) error
   123  }
   124  
   125  type engine interface {
   126  	identifier
   127  	handler
   128  }
   129  
   130  // Hub manages the lifecycle of all connected Hotstuff peers.
   131  type Hub struct {
   132  	closed bool
   133  
   134  	ctx    context.Context
   135  	cancel context.CancelFunc
   136  
   137  	mux    sync.RWMutex
   138  	peers  map[hots.ID]*replica
   139  	enodes map[hots.ID]enode.ID
   140  
   141  	engine engine
   142  
   143  	wg sync.WaitGroup
   144  }
   145  
   146  func NewHub(engine engine) *Hub {
   147  	ctx, cancel := context.WithCancel(context.Background())
   148  	return &Hub{
   149  		ctx:    ctx,
   150  		cancel: cancel,
   151  		engine: engine,
   152  		peers:  make(map[hots.ID]*replica),
   153  		enodes: make(map[hots.ID]enode.ID),
   154  	}
   155  }
   156  
   157  func (hub *Hub) Vote(remote hots.ID, view uint64, block common.Hash, sig *bls.PartialSignature) error {
   158  	hub.mux.RLock()
   159  	defer hub.mux.RUnlock()
   160  
   161  	if hub.closed {
   162  		return errClosed
   163  	}
   164  
   165  	peer, ok := hub.peers[remote]
   166  	if !ok {
   167  		return fmt.Errorf("unknown peer %v", remote)
   168  	}
   169  	sigbytes, _ := sig.ToBytes()
   170  	return p2p.Send(peer.rw, vote, &voteMsg{
   171  		View:      view,
   172  		Hash:      block,
   173  		Signature: sigbytes,
   174  	})
   175  }
   176  
   177  func (hub *Hub) GetEnode(ids []hots.ID) []enode.ID {
   178  	hub.mux.RLock()
   179  	defer hub.mux.RUnlock()
   180  
   181  	enodes := make([]enode.ID, 0, len(ids))
   182  	for i := range ids {
   183  		if enodeId, ok := hub.enodes[ids[i]]; ok {
   184  			enodes = append(enodes, enodeId)
   185  		}
   186  	}
   187  	return enodes
   188  }
   189  
   190  func (hub *Hub) Timeout(view uint64) error {
   191  	hub.mux.RLock()
   192  	defer hub.mux.RUnlock()
   193  
   194  	if hub.closed {
   195  		return errClosed
   196  	}
   197  
   198  	for _, peer := range hub.peers {
   199  		select {
   200  		case peer.queued <- &timeoutMsg{View: view}:
   201  		case <-peer.quit:
   202  		}
   203  	}
   204  
   205  	return nil
   206  }
   207  
   208  func (hub *Hub) Close() error {
   209  	hub.cancel()
   210  
   211  	hub.mux.Lock()
   212  	for _, replica := range hub.peers {
   213  		replica.peer.Disconnect(p2p.DiscQuitting)
   214  	}
   215  	hub.closed = true
   216  	hub.mux.Unlock()
   217  
   218  	hub.wg.Wait()
   219  	log.Info("Hotstuff protocol handler stop")
   220  
   221  	return nil
   222  }
   223  
   224  func (hub *Hub) Handle(peer *p2p.Peer, rw p2p.MsgReadWriter, chain consensus.ChainReader) error {
   225  	id, err := hub.handshake(peer, rw, chain)
   226  	if err != nil {
   227  		peer.Log().Debug("peer handshake failed", "error", err)
   228  		return err
   229  	}
   230  	replica := newReplica(id, peer, rw)
   231  	if err := hub.register(replica); err != nil {
   232  		return err
   233  	}
   234  	defer hub.unregister(replica.id)
   235  
   236  	hub.wg.Add(1)
   237  	defer hub.wg.Done()
   238  
   239  	peer.Log().Info("hotstuff peer connected", "peer", peer.Name())
   240  
   241  	// Handle incoming messages until the connection is torn down
   242  	for {
   243  		if err := replica.handle(); err != nil {
   244  			peer.Log().Debug("hotstuff message handling failed", "err", err)
   245  			return err
   246  		}
   247  	}
   248  }
   249  
   250  // Handshake executes the hotstuff protocol handshake
   251  func (hub *Hub) handshake(peer *p2p.Peer, rw p2p.MsgReadWriter, chain consensus.ChainReader) (id hots.ID, err error) {
   252  
   253  	// 1.exchange random seed
   254  	// 2.exchange authentication signature
   255  
   256  	errc := make(chan error)
   257  	idch := make(chan hots.ID)
   258  
   259  	go func() {
   260  		if id, err := hub.checkHandshake(rw, chain); err != nil {
   261  			errc <- err
   262  		} else {
   263  			idch <- id
   264  		}
   265  	}()
   266  
   267  	timeout := time.NewTimer(2 * handshakeTimeoutRTT)
   268  	defer timeout.Stop()
   269  
   270  	select {
   271  	case id := <-idch:
   272  		return id, nil
   273  	case err := <-errc:
   274  		return hots.ID{}, err
   275  	case <-timeout.C:
   276  		return hots.ID{}, p2p.DiscReadTimeout
   277  	}
   278  }
   279  
   280  func (hub *Hub) checkHandshake(rw p2p.MsgReadWriter, chain consensus.ChainReader) (hots.ID, error) {
   281  	seed := make([]byte, handshakeValidatorLen)
   282  	rand.Read(seed)
   283  	if err := p2p.Send(rw, status, &handshakeData{Random: seed}); err != nil {
   284  		return hots.ID{}, err
   285  	}
   286  
   287  	validator, err := readLegacy(rw)
   288  	if err != nil {
   289  		return hots.ID{}, err
   290  	}
   291  
   292  	if len(validator.Random) != handshakeValidatorLen {
   293  		return hots.ID{}, errors.New("invalid handshake params")
   294  	}
   295  
   296  	sig, _ := hub.engine.Sign(validator.Random)
   297  	sigbytes, _ := sig.ToBytes()
   298  	if err := p2p.Send(rw, status, &handshakeData{Signature: sigbytes}); err != nil {
   299  		return hots.ID{}, err
   300  	}
   301  
   302  	if validator, err = readLegacy(rw); err != nil {
   303  		return hots.ID{}, err
   304  	}
   305  	if err = sig.FromBytes(validator.Signature); err != nil {
   306  		return hots.ID{}, err
   307  	}
   308  
   309  	if err = hub.engine.VerifyAt(chain.CurrentHeader(), sig, seed); err != nil {
   310  		return hots.ID{}, err
   311  	}
   312  
   313  	log.Debug("Hotstuff handshake", "id", sig.ID())
   314  	return sig.ID(), nil
   315  }
   316  
   317  func (hub *Hub) register(rep *replica) error {
   318  	hub.mux.Lock()
   319  	defer hub.mux.Unlock()
   320  
   321  	if hub.closed {
   322  		return errClosed
   323  	}
   324  	if _, ok := hub.peers[rep.id]; ok {
   325  		return errAlreadyRegistered
   326  	}
   327  
   328  	hub.peers[rep.id] = rep
   329  	hub.enodes[rep.id] = rep.peer.ID()
   330  	rep.handler = hub.engine
   331  	go rep.run(hub.ctx)
   332  
   333  	return nil
   334  }
   335  
   336  func (hub *Hub) unregister(id hots.ID) error {
   337  	hub.mux.Lock()
   338  	defer hub.mux.Unlock()
   339  
   340  	if rep, ok := hub.peers[id]; ok {
   341  		delete(hub.peers, id)
   342  		delete(hub.enodes, id)
   343  		rep.close()
   344  	}
   345  
   346  	return nil
   347  }
   348  
   349  func readLegacy(rw p2p.MsgReadWriter) (*handshakeData, error) {
   350  	msg, err := rw.ReadMsg()
   351  	if err != nil {
   352  		return nil, err
   353  	}
   354  
   355  	if msg.Code != status {
   356  		return nil, fmt.Errorf("first msg has code %x (!= %x)", msg.Code, status)
   357  	}
   358  	if msg.Size > protocolMaxMsgSize {
   359  		return nil, fmt.Errorf("too large message,%d - %d", msg.Size, protocolMaxMsgSize)
   360  	}
   361  	var status handshakeData
   362  	return &status, msg.Decode(&status)
   363  }