mirror of
				https://github.com/libp2p/go-libp2p.git
				synced 2025-10-31 03:46:35 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			158 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			158 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package main
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"log"
 | |
| 	"time"
 | |
| 
 | |
| 	"github.com/libp2p/go-libp2p/core/crypto"
 | |
| 	"github.com/libp2p/go-libp2p/core/host"
 | |
| 	"github.com/libp2p/go-libp2p/core/peer"
 | |
| 	"github.com/libp2p/go-libp2p/core/protocol"
 | |
| 	p2p "github.com/libp2p/go-libp2p/examples/multipro/pb"
 | |
| 
 | |
| 	ggio "github.com/gogo/protobuf/io"
 | |
| 	"github.com/gogo/protobuf/proto"
 | |
| )
 | |
| 
 | |
| // node client version
 | |
| const clientVersion = "go-p2p-node/0.0.1"
 | |
| 
 | |
| // Node type - a p2p host implementing one or more p2p protocols
 | |
| type Node struct {
 | |
| 	host.Host     // lib-p2p host
 | |
| 	*PingProtocol // ping protocol impl
 | |
| 	*EchoProtocol // echo protocol impl
 | |
| 	// add other protocols here...
 | |
| }
 | |
| 
 | |
| // Create a new node with its implemented protocols
 | |
| func NewNode(host host.Host, done chan bool) *Node {
 | |
| 	node := &Node{Host: host}
 | |
| 	node.PingProtocol = NewPingProtocol(node, done)
 | |
| 	node.EchoProtocol = NewEchoProtocol(node, done)
 | |
| 	return node
 | |
| }
 | |
| 
 | |
| // Authenticate incoming p2p message
 | |
| // message: a protobufs go data object
 | |
| // data: common p2p message data
 | |
| func (n *Node) authenticateMessage(message proto.Message, data *p2p.MessageData) bool {
 | |
| 	// store a temp ref to signature and remove it from message data
 | |
| 	// sign is a string to allow easy reset to zero-value (empty string)
 | |
| 	sign := data.Sign
 | |
| 	data.Sign = nil
 | |
| 
 | |
| 	// marshall data without the signature to protobufs3 binary format
 | |
| 	bin, err := proto.Marshal(message)
 | |
| 	if err != nil {
 | |
| 		log.Println(err, "failed to marshal pb message")
 | |
| 		return false
 | |
| 	}
 | |
| 
 | |
| 	// restore sig in message data (for possible future use)
 | |
| 	data.Sign = sign
 | |
| 
 | |
| 	// restore peer id binary format from base58 encoded node id data
 | |
| 	peerId, err := peer.Decode(data.NodeId)
 | |
| 	if err != nil {
 | |
| 		log.Println(err, "Failed to decode node id from base58")
 | |
| 		return false
 | |
| 	}
 | |
| 
 | |
| 	// verify the data was authored by the signing peer identified by the public key
 | |
| 	// and signature included in the message
 | |
| 	return n.verifyData(bin, []byte(sign), peerId, data.NodePubKey)
 | |
| }
 | |
| 
 | |
| // sign an outgoing p2p message payload
 | |
| func (n *Node) signProtoMessage(message proto.Message) ([]byte, error) {
 | |
| 	data, err := proto.Marshal(message)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	return n.signData(data)
 | |
| }
 | |
| 
 | |
| // sign binary data using the local node's private key
 | |
| func (n *Node) signData(data []byte) ([]byte, error) {
 | |
| 	key := n.Peerstore().PrivKey(n.ID())
 | |
| 	res, err := key.Sign(data)
 | |
| 	return res, err
 | |
| }
 | |
| 
 | |
| // Verify incoming p2p message data integrity
 | |
| // data: data to verify
 | |
| // signature: author signature provided in the message payload
 | |
| // peerId: author peer id from the message payload
 | |
| // pubKeyData: author public key from the message payload
 | |
| func (n *Node) verifyData(data []byte, signature []byte, peerId peer.ID, pubKeyData []byte) bool {
 | |
| 	key, err := crypto.UnmarshalPublicKey(pubKeyData)
 | |
| 	if err != nil {
 | |
| 		log.Println(err, "Failed to extract key from message key data")
 | |
| 		return false
 | |
| 	}
 | |
| 
 | |
| 	// extract node id from the provided public key
 | |
| 	idFromKey, err := peer.IDFromPublicKey(key)
 | |
| 
 | |
| 	if err != nil {
 | |
| 		log.Println(err, "Failed to extract peer id from public key")
 | |
| 		return false
 | |
| 	}
 | |
| 
 | |
| 	// verify that message author node id matches the provided node public key
 | |
| 	if idFromKey != peerId {
 | |
| 		log.Println(err, "Node id and provided public key mismatch")
 | |
| 		return false
 | |
| 	}
 | |
| 
 | |
| 	res, err := key.Verify(data, signature)
 | |
| 	if err != nil {
 | |
| 		log.Println(err, "Error authenticating data")
 | |
| 		return false
 | |
| 	}
 | |
| 
 | |
| 	return res
 | |
| }
 | |
| 
 | |
| // helper method - generate message data shared between all node's p2p protocols
 | |
| // messageId: unique for requests, copied from request for responses
 | |
| func (n *Node) NewMessageData(messageId string, gossip bool) *p2p.MessageData {
 | |
| 	// Add protobufs bin data for message author public key
 | |
| 	// this is useful for authenticating  messages forwarded by a node authored by another node
 | |
| 	nodePubKey, err := crypto.MarshalPublicKey(n.Peerstore().PubKey(n.ID()))
 | |
| 
 | |
| 	if err != nil {
 | |
| 		panic("Failed to get public key for sender from local peer store.")
 | |
| 	}
 | |
| 
 | |
| 	return &p2p.MessageData{ClientVersion: clientVersion,
 | |
| 		NodeId:     n.ID().String(),
 | |
| 		NodePubKey: nodePubKey,
 | |
| 		Timestamp:  time.Now().Unix(),
 | |
| 		Id:         messageId,
 | |
| 		Gossip:     gossip}
 | |
| }
 | |
| 
 | |
| // helper method - writes a protobuf go data object to a network stream
 | |
| // data: reference of protobuf go data object to send (not the object itself)
 | |
| // s: network stream to write the data to
 | |
| func (n *Node) sendProtoMessage(id peer.ID, p protocol.ID, data proto.Message) bool {
 | |
| 	s, err := n.NewStream(context.Background(), id, p)
 | |
| 	if err != nil {
 | |
| 		log.Println(err)
 | |
| 		return false
 | |
| 	}
 | |
| 	defer s.Close()
 | |
| 
 | |
| 	writer := ggio.NewFullWriter(s)
 | |
| 	err = writer.WriteMsg(data)
 | |
| 	if err != nil {
 | |
| 		log.Println(err)
 | |
| 		s.Reset()
 | |
| 		return false
 | |
| 	}
 | |
| 	return true
 | |
| }
 | 
