/ examples / multipro / node.go
node.go
  1  package main
  2  
  3  import (
  4  	"context"
  5  	"log"
  6  	"time"
  7  
  8  	"github.com/libp2p/go-libp2p/core/crypto"
  9  	"github.com/libp2p/go-libp2p/core/host"
 10  	"github.com/libp2p/go-libp2p/core/peer"
 11  	"github.com/libp2p/go-libp2p/core/protocol"
 12  	p2p "github.com/libp2p/go-libp2p/examples/multipro/pb"
 13  
 14  	ggio "github.com/gogo/protobuf/io"
 15  	"github.com/gogo/protobuf/proto"
 16  )
 17  
 18  // node client version
 19  const clientVersion = "go-p2p-node/0.0.1"
 20  
 21  // Node type - a p2p host implementing one or more p2p protocols
 22  type Node struct {
 23  	host.Host     // lib-p2p host
 24  	*PingProtocol // ping protocol impl
 25  	*EchoProtocol // echo protocol impl
 26  	// add other protocols here...
 27  }
 28  
 29  // Create a new node with its implemented protocols
 30  func NewNode(host host.Host, done chan bool) *Node {
 31  	node := &Node{Host: host}
 32  	node.PingProtocol = NewPingProtocol(node, done)
 33  	node.EchoProtocol = NewEchoProtocol(node, done)
 34  	return node
 35  }
 36  
 37  // Authenticate incoming p2p message
 38  // message: a protobufs go data object
 39  // data: common p2p message data
 40  func (n *Node) authenticateMessage(message proto.Message, data *p2p.MessageData) bool {
 41  	// store a temp ref to signature and remove it from message data
 42  	// sign is a string to allow easy reset to zero-value (empty string)
 43  	sign := data.Sign
 44  	data.Sign = nil
 45  
 46  	// marshall data without the signature to protobufs3 binary format
 47  	bin, err := proto.Marshal(message)
 48  	if err != nil {
 49  		log.Println(err, "failed to marshal pb message")
 50  		return false
 51  	}
 52  
 53  	// restore sig in message data (for possible future use)
 54  	data.Sign = sign
 55  
 56  	// restore peer id binary format from base58 encoded node id data
 57  	peerId, err := peer.Decode(data.NodeId)
 58  	if err != nil {
 59  		log.Println(err, "Failed to decode node id from base58")
 60  		return false
 61  	}
 62  
 63  	// verify the data was authored by the signing peer identified by the public key
 64  	// and signature included in the message
 65  	return n.verifyData(bin, []byte(sign), peerId, data.NodePubKey)
 66  }
 67  
 68  // sign an outgoing p2p message payload
 69  func (n *Node) signProtoMessage(message proto.Message) ([]byte, error) {
 70  	data, err := proto.Marshal(message)
 71  	if err != nil {
 72  		return nil, err
 73  	}
 74  	return n.signData(data)
 75  }
 76  
 77  // sign binary data using the local node's private key
 78  func (n *Node) signData(data []byte) ([]byte, error) {
 79  	key := n.Peerstore().PrivKey(n.ID())
 80  	res, err := key.Sign(data)
 81  	return res, err
 82  }
 83  
 84  // Verify incoming p2p message data integrity
 85  // data: data to verify
 86  // signature: author signature provided in the message payload
 87  // peerId: author peer id from the message payload
 88  // pubKeyData: author public key from the message payload
 89  func (n *Node) verifyData(data []byte, signature []byte, peerId peer.ID, pubKeyData []byte) bool {
 90  	key, err := crypto.UnmarshalPublicKey(pubKeyData)
 91  	if err != nil {
 92  		log.Println(err, "Failed to extract key from message key data")
 93  		return false
 94  	}
 95  
 96  	// extract node id from the provided public key
 97  	idFromKey, err := peer.IDFromPublicKey(key)
 98  
 99  	if err != nil {
100  		log.Println(err, "Failed to extract peer id from public key")
101  		return false
102  	}
103  
104  	// verify that message author node id matches the provided node public key
105  	if idFromKey != peerId {
106  		log.Println(err, "Node id and provided public key mismatch")
107  		return false
108  	}
109  
110  	res, err := key.Verify(data, signature)
111  	if err != nil {
112  		log.Println(err, "Error authenticating data")
113  		return false
114  	}
115  
116  	return res
117  }
118  
119  // helper method - generate message data shared between all node's p2p protocols
120  // messageId: unique for requests, copied from request for responses
121  func (n *Node) NewMessageData(messageId string, gossip bool) *p2p.MessageData {
122  	// Add protobufs bin data for message author public key
123  	// this is useful for authenticating  messages forwarded by a node authored by another node
124  	nodePubKey, err := crypto.MarshalPublicKey(n.Peerstore().PubKey(n.ID()))
125  
126  	if err != nil {
127  		panic("Failed to get public key for sender from local peer store.")
128  	}
129  
130  	return &p2p.MessageData{ClientVersion: clientVersion,
131  		NodeId:     n.ID().String(),
132  		NodePubKey: nodePubKey,
133  		Timestamp:  time.Now().Unix(),
134  		Id:         messageId,
135  		Gossip:     gossip}
136  }
137  
138  // helper method - writes a protobuf go data object to a network stream
139  // data: reference of protobuf go data object to send (not the object itself)
140  // s: network stream to write the data to
141  func (n *Node) sendProtoMessage(id peer.ID, p protocol.ID, data proto.Message) bool {
142  	s, err := n.NewStream(context.Background(), id, p)
143  	if err != nil {
144  		log.Println(err)
145  		return false
146  	}
147  	defer s.Close()
148  
149  	writer := ggio.NewFullWriter(s)
150  	err = writer.WriteMsg(data)
151  	if err != nil {
152  		log.Println(err)
153  		s.Reset()
154  		return false
155  	}
156  	return true
157  }