Proficient in Filecoin: Hello protocol of Filecoin source code

When the start method of the Filecoin full node is called to start the full node, the New method of the hello protocol is called. The processing of this method is as follows:

  1. Generate a Hello object.
     hello := &Handler{    host:              h,    genesis:           gen,    chainSyncCB:       syncCallback,    getHeaviestTipSet: getHeaviestTipSet,    net:               net,    commitSha:         commitSha, } 

    The host object is the Host object provided by the underlying libp2; genesis is the CID of the genesis block; chainSyncCB is the syncCallBack function of the full node object for synchronizing blocks from remote nodes; getHeaviestTipSet is the ChainHead method of the porcelain.API object Inherited from plumbing.API object, the ChainHead method is defined in the latter), used to return the tipset of the blockchain head; net represents the current network environment, such as the test network, the official network;

  2. Call the SetStreamHandler method of the host object and set its own handleNewStream method as the handler for the /fil/hello/1.0.0 protocol.
     h.SetStreamHandler(protocol, hello.handleNewStream) 

    When the connection is established, the notifier registered later calls the sayHello method, in which a hello protocol stream is opened and a hello message is sent.

  3. Call the Notify method of the host object's network object (that is, the swarm object), and register itself as the notified party on the network object.
     h.Network().Notify((*helloNotify)(hello)) 

    In this step, by calling the Network method of the host object, the underlying swarm object is returned, then the hello object is converted into a helloNotify object, and finally the Notify method of the swarm object is called, so that the helloNotify object will be notified when any event occurs on the underlying swarm object (Ie the hello object).

In the Hello protocol, we only care about the connection establishment event, so the helloNotify type only implements this method, and the other methods are implemented as empty, as follows:

 type helloNotify Handler 

func (hn * helloNotify) hello () * Handler {
return (* Handler) (hn)
}

const helloTimeout = time.Second * 10

func (hn * helloNotify) Connected (n net.Network, c net.Conn) {
go func () {
ctx, cancel: = context.WithTimeout (context.Background (), helloTimeout)
defer cancel ()
p: = c.RemotePeer ()
if err: = hn.hello (). sayHello (ctx, p); err! = nil {
log.Warningf ("failed to send hello handshake to peer% s:% s", p, err)
}
} ()
}
When a node acts as a client and dials up to connect to a remote peer, the underlying swarm object will call its notifyAll method to notify all Notify objects that connections have been opened, that is, call the Connected methods of all Notify objects, including the ones we registered earlier Notification object. When the Connected method of the helloNotify object is called, this method internally calls its own hello method, which returns itself and is coerced to the Handler type, and then calls its sayHello method to say hello to the remote we are currently connected to.

At the same time, when a remote node acts as a server and receives a connection request from us to generate a connection, its swarm object will also notify all its Notify objects, and thus also its previously registered notification objects, which call the server vendor's Connected method, thus calling its sayHello method to send us the block situation; because in the second step, we registered the handleNewStream method of the Hello object as the processor of the Hello protocol, so when the node receives the remote node to send the block situation This method will be called for processing, and this method will call the syncCallBack method of the full node for block synchronization processing. In general, the Hello protocol handleNewStream block synchronization through sayHello and handleNewStream . The former sends its own block situation to a remote node, and the latter handles the block situation sent by the remote node.

sayHello method is processed as follows:

  1. Call the NewStream Host object to generate a stream object that handles the Hello protocol.
     s, err := h.host.NewStream(ctx, p, protocol) if err != nil {    return err } defer s.Close() // nolint: errcheck 
  2. Call your own getOurHelloMessage method to get the top information of your own getOurHelloMessage .
     msg := h.getOurHelloMessage() 

    The internal execution flow of this method is as follows:

    • Call your own getHeaviestTipSet method to get the top information of the ChainHead . This method is a reference to the plumbing.API object ChainHead method.
    • Using the obtained blockchain information, a message object Message generated and returned.
  3. Send block information to remote nodes via streaming.

handleNewStream method is processed as follows:

  1. Generate a message object Message and read from the stream the content sent by the remote peer node into the message object.
     var hello Message if err := cbu.NewMsgReader(s).ReadMsg(&hello); err != nil {    log.Debugf("bad hello message from peer %s: %s", from, err)    helloMsgErrCt.Inc(context.TODO(), 1)    s.Conn().Close() // nolint: errcheck    return } 
  2. Call its own processHelloMessage method to process messages sent by remote nodes. The method code is as follows:
     func (h *Handler) processHelloMessage(from peer.ID, msg *Message) error {    if !msg.GenesisHash.Equals(h.genesis) {        return ErrBadGenesis    }    if (h.net == "devnet-test" || h.net == "devnet-user") && msg.CommitSha != h.commitSha {        return ErrWrongVersion    } 
     h.chainSyncCB(from, msg.HeaviestTipSetCids, msg.HeaviestTipSetHeight) return nil 
     } 

    Its processing logic is relatively simple:

    • First, check if the genesis block hash sent by the remote node is equal to its genesis block hash. If not, return an error directly.
    • Then, check the network type.
    • Finally, it calls its own chainSyncCB method to process the block information sent by the remote node. This synchronization callback method object is generated in the start method Start of the full node. Its main generation is to generate a type types/SortedCidSet object based on the top-level information of the types/SortedCidSet sent by the remote node, and then call the HandleNewTipset method in chain/syncer.go to process the remotely sent block information.
  3. Different processing is performed according to the result of the previous processing message.
     switch err := h.processHelloMessage(from, &hello); err { case ErrBadGenesis:    log.Debugf("genesis cid: %s does not match: %s, disconnecting from peer: %s", &hello.GenesisHash, h.genesis, from)    genesisErrCt.Inc(context.TODO(), 1)    s.Conn().Close() // nolint: errcheck    return case ErrWrongVersion:    log.Debugf("code not at same version: peer has version %s, daemon has version %s, disconnecting from peer: %s", hello.CommitSha, h.commitSha, from)    versionErrCt.Inc(context.TODO(), 1)    s.Conn().Close() // nolint: errcheck    return case nil: // ok, noop default:    log.Error(err) }