Documentation ¶
Overview ¶
Package noise is an opinionated, easy-to-use P2P network stack for decentralized applications, and cryptographic protocols written in Go.
noise is made to be minimal, robust, developer-friendly, performant, secure, and cross-platform across multitudes of devices by making use of a small amount of well-tested, production-grade dependencies.
Example (CodecMessaging) ¶
This example demonstrates messaging with registering Go types to be serialized/deserialized on-the-wire provided marshal/unmarshal functions, how to decode serialized messages received from a peer, and how to send serialized messages.
package main import ( "context" "fmt" "github.com/muirglacier/noise" "strings" "sync" ) // ChatMessage is an example struct that is registered on example nodes, and serialized/deserialized on-the-fly. type ChatMessage struct { content string } // Marshal serializes a chat message into bytes. func (m ChatMessage) Marshal() []byte { return []byte(m.content) } // Unmarshal deserializes a slice of bytes into a chat message, and returns an error should deserialization // fail, or the slice of bytes be malformed. func UnmarshalChatMessage(buf []byte) (ChatMessage, error) { return ChatMessage{content: strings.ToValidUTF8(string(buf), "")}, nil } // This example demonstrates messaging with registering Go types to be serialized/deserialized on-the-wire provided // marshal/unmarshal functions, how to decode serialized messages received from a peer, and how to send serialized // messages. func main() { // Let there be Alice and Bob. alice, err := noise.NewNode() if err != nil { panic(err) } bob, err := noise.NewNode() if err != nil { panic(err) } // Gracefully release resources for Alice and Bob at the end of the example. defer alice.Close() defer bob.Close() // Register the ChatMessage type to Alice and Bob so they know how to serialize/deserialize // them. alice.RegisterMessage(ChatMessage{}, UnmarshalChatMessage) bob.RegisterMessage(ChatMessage{}, UnmarshalChatMessage) var wg sync.WaitGroup // When Alice gets a ChatMessage from Bob, print it out. alice.Handle(func(ctx noise.HandlerContext) error { obj, err := ctx.DecodeMessage() if err != nil { return nil } msg, ok := obj.(ChatMessage) if !ok { return nil } fmt.Printf("Got a message from Bob: '%s'\n", msg.content) wg.Done() return nil }) // When Bob gets a message from Alice, print it out. bob.Handle(func(ctx noise.HandlerContext) error { obj, err := ctx.DecodeMessage() if err != nil { return nil } msg, ok := obj.(ChatMessage) if !ok { return nil } fmt.Printf("Got a message from Alice: '%s'\n", msg.content) wg.Done() return nil }) // Have Alice and Bob start listening for new peers. if err := alice.Listen(); err != nil { panic(err) } if err := bob.Listen(); err != nil { panic(err) } // Have Alice send Bob a ChatMessage with 'Hi Bob!' if err := alice.SendMessage(context.TODO(), bob.Addr(), ChatMessage{content: "Hi Bob!"}); err != nil { panic(err) } // Wait until Bob receives the message from Alice. wg.Add(1) wg.Wait() // Have Bob send Alice a ChatMessage with 'Hi Alice!' if err := bob.SendMessage(context.TODO(), alice.Addr(), ChatMessage{content: "Hi Alice!"}); err != nil { panic(err) } // Wait until Alice receives the message from Bob. wg.Add(1) wg.Wait() }
Output: Got a message from Alice: 'Hi Bob!' Got a message from Bob: 'Hi Alice!'
Example (CodecRPC) ¶
This example demonstrates sending registered serialized Go types as requests, decoding registered serialized Go types from peers, and sending registered serialized Go types as responses.
// Let there be Alice and Bob. alice, err := noise.NewNode() if err != nil { panic(err) } bob, err := noise.NewNode() if err != nil { panic(err) } // Gracefully release resources for Alice and Bob at the end of the example. defer alice.Close() defer bob.Close() // Register the ChatMessage type to Alice and Bob so they know how to serialize/deserialize // them. alice.RegisterMessage(ChatMessage{}, UnmarshalChatMessage) bob.RegisterMessage(ChatMessage{}, UnmarshalChatMessage) // When Bob gets a request from Alice, print it out and respond to Alice with 'Hi Alice!'. bob.Handle(func(ctx noise.HandlerContext) error { if !ctx.IsRequest() { return nil } req, err := ctx.DecodeMessage() if err != nil { return nil } fmt.Printf("Got a message from Alice: '%s'\n", req.(ChatMessage).content) return ctx.SendMessage(ChatMessage{content: "Hi Alice!"}) }) // Have Alice and Bob start listening for new peers. if err := alice.Listen(); err != nil { panic(err) } if err := bob.Listen(); err != nil { panic(err) } // Have Alice send Bob a ChatMessage request with the message 'Hi Bob!' res, err := alice.RequestMessage(context.TODO(), bob.Addr(), ChatMessage{content: "Hi Bob!"}) if err != nil { panic(err) } // Print out the ChatMessage response Bob got from Alice. fmt.Printf("Got a message from Bob: '%s'\n", res.(ChatMessage).content)
Output: Got a message from Alice: 'Hi Bob!' Got a message from Bob: 'Hi Alice!'
Example (Messaging) ¶
This example demonstrates how to send and receive raw bytes across peers, how to listen for incoming peers, and how to cleanup node instances after you are done using them.
package main import ( "context" "fmt" "github.com/muirglacier/noise" "sync" ) func main() { // Let there be nodes Alice and Bob. alice, err := noise.NewNode() if err != nil { panic(err) } bob, err := noise.NewNode() if err != nil { panic(err) } // Gracefully release resources for Alice and Bob at the end of the example. defer alice.Close() defer bob.Close() var wg sync.WaitGroup // When Alice gets a message from Bob, print it out. alice.Handle(func(ctx noise.HandlerContext) error { fmt.Printf("Got a message from Bob: '%s'\n", string(ctx.Data())) wg.Done() return nil }) // When Bob gets a message from Alice, print it out. bob.Handle(func(ctx noise.HandlerContext) error { fmt.Printf("Got a message from Alice: '%s'\n", string(ctx.Data())) wg.Done() return nil }) // Have Alice and Bob start listening for new peers. if err := alice.Listen(); err != nil { panic(err) } if err := bob.Listen(); err != nil { panic(err) } // Have Alice send Bob 'Hi Bob!' if err := alice.Send(context.TODO(), bob.Addr(), []byte("Hi Bob!")); err != nil { panic(err) } // Wait until Bob receives the message from Alice. wg.Add(1) wg.Wait() // Have Bob send Alice 'Hi Alice!' if err := bob.Send(context.TODO(), alice.Addr(), []byte("Hi Alice!")); err != nil { panic(err) } // Wait until Alice receives the message from Bob. wg.Add(1) wg.Wait() }
Output: Got a message from Alice: 'Hi Bob!' Got a message from Bob: 'Hi Alice!'
Example (PeerDiscovery) ¶
This example demonstrates how to use Kademlia to have three peers Alice, Charlie and bob discover each other in an open, trustless network.
package main import ( "context" "fmt" "github.com/muirglacier/noise" "github.com/muirglacier/noise/kademlia" ) func main() { // Let there be Alice, Bob, and Charlie. alice, err := noise.NewNode() if err != nil { panic(err) } bob, err := noise.NewNode() if err != nil { panic(err) } charlie, err := noise.NewNode() if err != nil { panic(err) } // Alice, Bob, and Charlie are following an overlay network protocol called Kademlia to discover, interact, and // manage each others peer connections. ka, kb, kc := kademlia.New(), kademlia.New(), kademlia.New() alice.Bind(ka.Protocol()) bob.Bind(kb.Protocol()) charlie.Bind(kc.Protocol()) if err := alice.Listen(); err != nil { panic(err) } if err := bob.Listen(); err != nil { panic(err) } if err := charlie.Listen(); err != nil { panic(err) } // Have Bob and Charlie learn about Alice. Bob and Charlie do not yet know of each other. if _, err := bob.Ping(context.TODO(), alice.Addr()); err != nil { panic(err) } if _, err := charlie.Ping(context.TODO(), bob.Addr()); err != nil { panic(err) } // Using Kademlia, Bob and Charlie will learn of each other. Alice, Bob, and Charlie should // learn about each other once they run (*kademlia.Protocol).Discover(). fmt.Printf("Alice discovered %d peer(s).\n", len(ka.Discover())) fmt.Printf("Bob discovered %d peer(s).\n", len(kb.Discover())) fmt.Printf("Charlie discovered %d peer(s).\n", len(kc.Discover())) }
Output: Alice discovered 2 peer(s). Bob discovered 2 peer(s). Charlie discovered 2 peer(s).
Example (RPC) ¶
This example demonstrates how to send/handle RPC requests across peers, how to listen for incoming peers, how to check if a message received is a request or not, how to reply to a RPC request, and how to cleanup node instances after you are done using them.
package main import ( "context" "fmt" "github.com/muirglacier/noise" ) func main() { // Let there be nodes Alice and Bob. alice, err := noise.NewNode() if err != nil { panic(err) } bob, err := noise.NewNode() if err != nil { panic(err) } // Gracefully release resources for Alice and Bob at the end of the example. defer alice.Close() defer bob.Close() // When Bob gets a message from Alice, print it out and respond to Alice with 'Hi Alice!' bob.Handle(func(ctx noise.HandlerContext) error { if !ctx.IsRequest() { return nil } fmt.Printf("Got a message from Alice: '%s'\n", string(ctx.Data())) return ctx.Send([]byte("Hi Alice!")) }) // Have Alice and Bob start listening for new peers. if err := alice.Listen(); err != nil { panic(err) } if err := bob.Listen(); err != nil { panic(err) } // Have Alice send Bob a request with the message 'Hi Bob!' res, err := alice.Request(context.TODO(), bob.Addr(), []byte("Hi Bob!")) if err != nil { panic(err) } // Print out the response Bob got from Alice. fmt.Printf("Got a message from Bob: '%s'\n", string(res)) }
Output: Got a message from Alice: 'Hi Bob!' Got a message from Bob: 'Hi Alice!'
Index ¶
- Constants
- Variables
- func ECDH(ourPrivateKey PrivateKey, peerPublicKey PublicKey) ([]byte, error)
- func GenerateKeys(rand io.Reader) (publicKey PublicKey, privateKey PrivateKey, err error)
- func ResolveAddress(address string) (string, error)
- type Client
- type Handler
- type HandlerContext
- func (ctx *HandlerContext) Data() []byte
- func (ctx *HandlerContext) DecodeMessage() (Serializable, error)
- func (ctx *HandlerContext) ID() ID
- func (ctx *HandlerContext) IsRequest() bool
- func (ctx *HandlerContext) Logger() *zap.Logger
- func (ctx *HandlerContext) Send(data []byte) error
- func (ctx *HandlerContext) SendMessage(msg Serializable) error
- type ID
- type Node
- func (n *Node) Addr() string
- func (n *Node) Bind(protocols ...Protocol)
- func (n *Node) Close() error
- func (n *Node) DecodeMessage(data []byte) (Serializable, error)
- func (n *Node) EncodeMessage(msg Serializable) ([]byte, error)
- func (n *Node) Handle(handlers ...Handler)
- func (n *Node) ID() ID
- func (n *Node) Inbound() []*Client
- func (n *Node) Listen() error
- func (n *Node) Logger() *zap.Logger
- func (n *Node) Outbound() []*Client
- func (n *Node) Ping(ctx context.Context, addr string) (*Client, error)
- func (n *Node) RegisterMessage(ser Serializable, de interface{}) uint16
- func (n *Node) Request(ctx context.Context, addr string, data []byte) ([]byte, error)
- func (n *Node) RequestMessage(ctx context.Context, addr string, req Serializable) (Serializable, error)
- func (n *Node) Send(ctx context.Context, addr string, data []byte) error
- func (n *Node) SendMessage(ctx context.Context, addr string, msg Serializable) error
- func (n *Node) Sign(data []byte) Signature
- type NodeOption
- func WithNodeAddress(addr string) NodeOption
- func WithNodeBindHost(host net.IP) NodeOption
- func WithNodeBindPort(port uint16) NodeOption
- func WithNodeID(id ID) NodeOption
- func WithNodeIdleTimeout(idleTimeout time.Duration) NodeOption
- func WithNodeLogger(logger *zap.Logger) NodeOption
- func WithNodeMaxDialAttempts(maxDialAttempts uint) NodeOption
- func WithNodeMaxInboundConnections(maxInboundConnections uint) NodeOption
- func WithNodeMaxOutboundConnections(maxOutboundConnections uint) NodeOption
- func WithNodeMaxRecvMessageSize(maxRecvMessageSize uint32) NodeOption
- func WithNodeNumWorkers(numWorkers uint) NodeOption
- func WithNodePrivateKey(privateKey PrivateKey) NodeOption
- type PrivateKey
- type Protocol
- type PublicKey
- type Serializable
- type Signature
Examples ¶
Constants ¶
const ( // SizePublicKey is the size in bytes of a nodes/peers public key. SizePublicKey = ed25519.PublicKeySize // SizePrivateKey is the size in bytes of a nodes/peers private key. SizePrivateKey = ed25519.PrivateKeySize // SizeSignature is the size in bytes of a cryptographic signature. SizeSignature = ed25519.SignatureSize )
Variables ¶
var ( // ZeroPublicKey is the zero-value for a node/peer public key. ZeroPublicKey PublicKey // ZeroPrivateKey is the zero-value for a node/peer private key. ZeroPrivateKey PrivateKey // ZeroSignature is the zero-value for a cryptographic signature. ZeroSignature Signature )
var ( // ErrMessageTooLarge is reported by a client when it receives a message from a peer that exceeds the max // receivable message size limit configured on a node. ErrMessageTooLarge = errors.New("msg from peer is too large") )
Functions ¶
func ECDH ¶
func ECDH(ourPrivateKey PrivateKey, peerPublicKey PublicKey) ([]byte, error)
ECDH transform all Ed25519 points to Curve25519 points and performs a Diffie-Hellman handshake to derive a shared key. It throws an error should the Ed25519 points be invalid.
func GenerateKeys ¶
func GenerateKeys(rand io.Reader) (publicKey PublicKey, privateKey PrivateKey, err error)
GenerateKeys randomly generates a new pair of cryptographic keys. Nil may be passed to rand in order to use crypto/rand by default. It returns an error if rand is invalid.
func ResolveAddress ¶
ResolveAddress resolves an address using net.ResolveTCPAddress("tcp", (*net.Conn).RemoteAddr()) and nullifies the IP if the IP is unspecified or is a loopback address. It then returns the string representation of the address, or an error if the resolution of the address fails.
Types ¶
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client represents an pooled inbound/outbound connection under some node. Should a client successfully undergo noise's protocol handshake, information about the peer representative of this client, such as its ID is available.
A clients connection may be closed through (*Client).Close, through the result of a failed handshake, through exceeding the max inbound/outbound connection count configured on the clients associated node, through a node gracefully being stopped, through a Handler configured on the node returning an error upon recipient of some data, or through receiving unexpected/suspicious data.
The lifecycle of a client may be controlled through (*Client).WaitUntilReady, and (*Client).WaitUntilClosed. It provably has been useful in writing unit tests where a client instance is used under high concurrency scenarios.
A client in total has two goroutines associated to it: a goroutine responsible for handling writing messages, and a goroutine responsible for handling the recipient of messages.
func (*Client) Close ¶
func (c *Client) Close()
Close asynchronously kills the underlying connection and signals all goroutines to stop underlying this client.
Close may be called concurrently.
func (*Client) Error ¶
Error returns the very first error that has caused this clients connection to have dropped.
Error may be called concurrently.
func (*Client) ID ¶
ID returns an immutable copy of the ID of this client, which is established once the client has successfully completed the handshake protocol configured from this clients associated node.
ID may be called concurrently.
func (*Client) Logger ¶
Logger returns the underlying logger associated to this client. It may optionally be set via (*Client).SetLogger.
Logger may be called concurrently.
func (*Client) SetLogger ¶
SetLogger updates the logger instance of this client.
SetLogger may be called concurrently.
func (*Client) WaitUntilClosed ¶
func (c *Client) WaitUntilClosed()
WaitUntilClosed pauses the goroutine to which it was called within until all goroutines associated to this client has been closed. The goroutines associated to this client would only close should:
1) handshaking failed/succeeded, 2) the connection was dropped, or 3) (*Client).Close was called.
WaitUntilReady may be called concurrently.
func (*Client) WaitUntilReady ¶
func (c *Client) WaitUntilReady()
WaitUntilReady pauses the goroutine to which it was called within until/unless the client has successfully completed/failed the handshake protocol configured under the node instance to which this peer was derived from.
It pauses the goroutine by reading from a channel that is closed when the client has successfully completed/failed the aforementioned handshake protocol.
WaitUntilReady may be called concurrently.
type Handler ¶
type Handler func(ctx HandlerContext) error
Handler is called whenever a node receives data from either an inbound/outbound peer connection. Multiple handlers may be registered to a node by (*Node).Handle before the node starts listening for new peers.
Returning an error in a handler closes the connection and marks the connection to have closed unexpectedly or due to error. Should you intend to wish to skip a handler from processing some given data, return a nil error.
type HandlerContext ¶
type HandlerContext struct {
// contains filtered or unexported fields
}
HandlerContext provides contextual information upon the recipient of data from an inbound/outbound connection. It provides the option of responding to a request should the data received be of a request.
func (*HandlerContext) Data ¶
func (ctx *HandlerContext) Data() []byte
Data returns the raw bytes that some peer has sent to you.
Data may be called concurrently.
func (*HandlerContext) DecodeMessage ¶
func (ctx *HandlerContext) DecodeMessage() (Serializable, error)
DecodeMessage decodes the raw bytes that some peer has sent you into a Go type. The Go type must have previously been registered to the node to which the handler this context is under was registered on. An error is thrown otherwise.
It is highly recommended that should you choose to have your application utilize noise's serialization/ deserialization framework for data over-the-wire, that all handlers use them by default.
DecodeMessage may be called concurrently.
func (*HandlerContext) ID ¶
func (ctx *HandlerContext) ID() ID
ID returns the ID of the inbound/outbound peer that sent you the data that is currently being handled.
func (*HandlerContext) IsRequest ¶
func (ctx *HandlerContext) IsRequest() bool
IsRequest marks whether or not the data received was intended to be of a request.
IsRequest may be called concurrently.
func (*HandlerContext) Logger ¶
func (ctx *HandlerContext) Logger() *zap.Logger
Logger returns the logger instance associated to the inbound/outbound peer being handled.
func (*HandlerContext) Send ¶
func (ctx *HandlerContext) Send(data []byte) error
Send sends data back to the peer that has sent you data. Should the data the peer send you be of a request, Send will send data back as a response. It returns an error if multiple responses attempt to be sent to a single request, or if an error occurred while attempting to send the peer a message.
Send may be called concurrently.
func (*HandlerContext) SendMessage ¶
func (ctx *HandlerContext) SendMessage(msg Serializable) error
SendMessage encodes and serializes a Go type into a byte slice, and sends data back to the peer that has sent you data as either a response or message. Refer to (*HandlerContext).Send for more details. An error is thrown if the Go type passed in has not been registered to the node to which the handler this context is under was registered on.
It is highly recommended that should you choose to have your application utilize noise's serialization/deserialization framework for data over-the-wire, that all handlers use them by default.
SendMessage may be called concurrently.
type ID ¶
type ID struct { // The Ed25519 public key of the bearer of this ID. ID PublicKey `json:"public_key"` // Public host of the bearer of this ID. Host net.IP `json:"address"` // Public port of the bearer of this ID. Port uint16 // 'host:port' Address string }
ID represents a peer ID. It comprises of a cryptographic public key, and a public, reachable network address specified by a IPv4/IPv6 host and 16-bit port number. The size of an ID in terms of its byte representation is static, with its contents being deterministic.
func UnmarshalID ¶
UnmarshalID deserializes buf, representing a slice of bytes, ID instance. It throws io.ErrUnexpectedEOF if the contents of buf is malformed.
type Node ¶
type Node struct {
// contains filtered or unexported fields
}
Node keeps track of a users ID, all of a users outgoing/incoming connections to/from peers as *Client instances under a bounded connection pool whose bounds may be configured, the TCP listener which accepts new incoming peer connections, and all Go types that may be serialized/deserialized at will on-the-wire or through a Handler.
A node at most will only have one goroutine + num configured worker goroutines associated to it which represents the listener looking to accept new incoming peer connections, and workers responsible for handling incoming peer messages. A node once closed or once started (as in, (*Node).Listen was called) should not be reused.
func NewNode ¶
func NewNode(opts ...NodeOption) (*Node, error)
NewNode instantiates a new node instance, and pre-configures the node with provided options. Default values for some non-specified options are instantiated as well, which may yield an error.
func (*Node) Addr ¶
Addr returns the public address of this node. The public address, should it not be configured through the WithNodeAddress functional option when calling NewNode, is initialized to 'host:port' after a successful call to (*Node).Listen.
Addr may be called concurrently.
func (*Node) Bind ¶
Bind registers a Protocol to this node, which implements callbacks for all events this node can emit throughout its lifecycle. For more information on how to implement Protocol, refer to the documentation for Protocol. Bind only registers Protocol's should the node not yet be listening for new connections. If the node is already listening for new peers, Bind silently returns and does nothing.
Bind may be called concurrently.
func (*Node) Close ¶
Close gracefully stops all live inbound/outbound peer connections registered on this node, and stops the node from handling/accepting new incoming peer connections. It returns an error if an error occurs closing the nodes listener. Nodes that are closed should not ever be re-used.
Close may be called concurrently.
func (*Node) DecodeMessage ¶
func (n *Node) DecodeMessage(data []byte) (Serializable, error)
DecodeMessage decodes data into its registered Go type T should it be well-formed. It throws an error if the opcode at the head of data has yet to be registered/associated to a Go type via (*Node).RegisterMessage. For more details, refer to (*Node).RegisterMessage.
DecodeMessage may be called concurrently.
func (*Node) EncodeMessage ¶
func (n *Node) EncodeMessage(msg Serializable) ([]byte, error)
EncodeMessage encodes msg which must be a registered Go type T into its wire representation. It throws an error if the Go type of msg has not yet been registered through (*Node).RegisterMessage. For more details, refer to (*Node).RegisterMessage.
EncodeMessage may be called concurrently.
func (*Node) Handle ¶
Handle registers a Handler to this node, which is executed every time this node receives a message from an inbound/outbound connection. For more information on how to write a Handler, refer to the documentation for Handler. Handle only registers Handler's should the node not yet be listening for new connections. If the node is already listening for new peers, Handle silently returns and does nothing.
Handle may be called concurrently.
func (*Node) ID ¶
ID returns an immutable copy of the ID of this node. The ID of the node is set after a successful call to (*Node).Listen, or otherwise passed through the WithNodeID functional option when calling NewNode.
ID may be called concurrently.
func (*Node) Inbound ¶
Inbound returns a cloned slice of all inbound connections to this node as Client instances. It is useful while writing unit tests where you would want to block the current goroutine via (*Client).WaitUntilReady and (*Client).WaitUntilClosed to test scenarios where you want to be sure some inbound client is open/closed.
func (*Node) Listen ¶
Listen has the node start listening for new peers. If an error occurs while starting the listener due to misconfigured options or resource exhaustion, an error is returned. If the node is already listening for new connections, an error is thrown.
Listen must not be called concurrently, and should only ever be called once per node instance.
func (*Node) Logger ¶
Logger returns the underlying logger associated to this node. The logger, should it not be configured through the WithNodeLogger functional option when calling NewNode, is by default zap.NewNop().
Logger may be called concurrently.
func (*Node) Outbound ¶
Outbound returns a cloned slice of all outbound connections to this node as Client instances. It is useful while writing unit tests where you would want to block the current goroutine via (*Client).WaitUntilClosed to test scenarios where you want to be sure some outbound client has resources associated to it completely released.
func (*Node) Ping ¶
Ping takes an available connection from this nodes connection pool if the peer at addr has never been connected to before, connects to it, handshakes with the peer, and returns a *Client instance should the entire process be successful.
If there already exists a live connection to the peer at addr, no new connection is established and the *Client instance associated to the peer is returned. An error is returned if connecting to the peer should it not have been connected to before fails, or if ctx was canceled/expired, or if handshaking fails.
If there is no available connection from this nodes connection pool, the connection that is at the tail of the pool is closed and evicted and used to ping addr.
It is safe to call Ping concurrently.
func (*Node) RegisterMessage ¶
func (n *Node) RegisterMessage(ser Serializable, de interface{}) uint16
RegisterMessage registers a Go type T that implements the Serializable interface with an associated deserialize function whose signature comprises of func([]byte) (T, error). RegisterMessage should be called in the following manner:
RegisterMessage(T{}, func([]byte) (T, error) { ... })
It returns a 16-bit unsigned integer (opcode) that is associated to the type T on-the-wire. Once a Go type has been registered, it may be used in a Handler, or via (*Node).EncodeMessage, (*Node).DecodeMessage, (*Node).SendMessage, and (*Node).RequestMessage.
The wire format of a type registered comprises of append([]byte{16-bit big-endian integer (opcode)}, ser.Marshal()...).
RegisterMessage may be called concurrently, though is discouraged.
func (*Node) Request ¶
Request takes an available connection from this nodes connection pool if the peer at addr has never been connected to before, connects to it, handshakes with the peer, and sends it a request should the entire process be successful.
Once the request has been sent, the current goroutine Request was called in will block until either a response has been received which will be subsequently returned, ctx was canceled/expired, or the connection was dropped.
If there already exists a live connection to the peer at addr, no new connection is established and the request will follow through. An error is returned if connecting to the peer should it not have been connected to before fails, or if handshaking fails.
If there is no available connection from this nodes connection pool, the connection that is at the tail of the pool is closed and evicted and used to send a request to addr.
func (*Node) RequestMessage ¶
func (n *Node) RequestMessage(ctx context.Context, addr string, req Serializable) (Serializable, error)
RequestMessage encodes msg which is a Go type registered via (*Node).RegisterMessage, and sends it as a request to addr, and returns a decoded response from the peer at addr. For more details, refer to (*Node).Request and (*Node).RegisterMessage.
func (*Node) Send ¶
Send takes an available connection from this nodes connection pool if the peer at addr has never been connected to before, connects to it, handshakes with the peer, and sends it data.
If there already exists a live connection to the peer at addr, no new connection is established and data will be sent through. An error is returned if connecting to the peer should it not have been connected to before fails, or if handshaking fails, or if the connection is closed.
If there is no available connection from this nodes connection pool, the connection that is at the tail of the pool is closed and evicted and used to send data to addr.
func (*Node) SendMessage ¶
SendMessage encodes msg which is a Go type registered via (*Node).RegisterMessage, and sends it to addr. For more details, refer to (*Node).Send and (*Node).RegisterMessage.
type NodeOption ¶
type NodeOption func(n *Node)
NodeOption represents a functional option that may be passed to NewNode for instantiating a new node instance with configured values.
func WithNodeAddress ¶
func WithNodeAddress(addr string) NodeOption
WithNodeAddress sets the public address of this node which is advertised on the ID sent to peers during a handshake protocol which is performed when interacting with peers this node has had no live connection to beforehand. By default, it is left blank, and initialized to 'binding host:binding port' upon calling (*Node).Listen.
func WithNodeBindHost ¶
func WithNodeBindHost(host net.IP) NodeOption
WithNodeBindHost sets the TCP host IP address which the node binds itself to and listens for new incoming peer connections on. By default, it is unspecified (0.0.0.0).
func WithNodeBindPort ¶
func WithNodeBindPort(port uint16) NodeOption
WithNodeBindPort sets the TCP port which the node binds itself to and listens for new incoming peer connections on. By default, a random port is assigned by the operating system.
func WithNodeID ¶
func WithNodeID(id ID) NodeOption
WithNodeID sets the nodes ID, and public address. By default, the ID is set with an address that is set to the binding host and port upon calling (*Node).Listen should the address not be configured.
func WithNodeIdleTimeout ¶
func WithNodeIdleTimeout(idleTimeout time.Duration) NodeOption
WithNodeIdleTimeout sets the duration in which should there be no subsequent reads/writes on a connection, the connection shall timeout and have resources related to it released. By default, the timeout is set to be 3 seconds. If an idle timeout of 0 is specified, idle timeouts will be disabled.
func WithNodeLogger ¶
func WithNodeLogger(logger *zap.Logger) NodeOption
WithNodeLogger sets the logger implementation that the node shall use. By default, zap.NewNop() is assigned which disables any logs.
func WithNodeMaxDialAttempts ¶
func WithNodeMaxDialAttempts(maxDialAttempts uint) NodeOption
WithNodeMaxDialAttempts sets the max number of attempts a connection is dialed before it is determined to have failed. By default, the max number of attempts a connection is dialed is 3.
func WithNodeMaxInboundConnections ¶
func WithNodeMaxInboundConnections(maxInboundConnections uint) NodeOption
WithNodeMaxInboundConnections sets the max number of inbound connections the connection pool a node maintains allows at any given moment in time. By default, the max number of inbound connections is 128. Exceeding the max number causes the connection pool to release the oldest inbound connection in the pool.
func WithNodeMaxOutboundConnections ¶
func WithNodeMaxOutboundConnections(maxOutboundConnections uint) NodeOption
WithNodeMaxOutboundConnections sets the max number of outbound connections the connection pool a node maintains allows at any given moment in time. By default, the maximum number of outbound connections is 128. Exceeding the max number causes the connection pool to release the oldest outbound connection in the pool.
func WithNodeMaxRecvMessageSize ¶
func WithNodeMaxRecvMessageSize(maxRecvMessageSize uint32) NodeOption
WithNodeMaxRecvMessageSize sets the max number of bytes a node is willing to receive from a peer. If the limit is ever exceeded, the peer is disconnected with an error. Setting this option to zero will disable the limit. By default, the max number of bytes a node is willing to receive from a peer is set to 4MB.
func WithNodeNumWorkers ¶
func WithNodeNumWorkers(numWorkers uint) NodeOption
WithNodeNumWorkers sets the max number of workers a node will spawn to handle incoming peer messages. By default, the max number of workers a node will spawn is the number of CPUs available to the Go runtime specified by runtime.NumCPU(). The minimum number of workers which need to be spawned is 1.
func WithNodePrivateKey ¶
func WithNodePrivateKey(privateKey PrivateKey) NodeOption
WithNodePrivateKey sets the private key of the node. By default, a random private key is generated using GenerateKeys should no private key be configured.
type PrivateKey ¶
type PrivateKey [SizePrivateKey]byte
PrivateKey is the default node/peer private key type.
func LoadKeysFromHex ¶
func LoadKeysFromHex(secretHex string) (PrivateKey, error)
LoadKeysFromHex loads a private key from a hex string. It returns an error if secretHex is not hex-encoded or is an invalid number of bytes. In the case of the latter error, the error is wrapped as io.ErrUnexpectedEOF. Calling this function performs 1 allocation.
func (PrivateKey) MarshalJSON ¶
func (k PrivateKey) MarshalJSON() ([]byte, error)
MarshalJSON returns the hexadecimal representation of this private key in JSON. It should never throw an error.
func (PrivateKey) Public ¶
func (k PrivateKey) Public() PublicKey
Public returns the public key associated to this private key.
func (PrivateKey) Sign ¶
func (k PrivateKey) Sign(data []byte) Signature
Sign uses this private key to sign data and return its cryptographic signature as a slice of bytes.
func (PrivateKey) String ¶
func (k PrivateKey) String() string
String returns the hexadecimal representation of this private key.
type Protocol ¶
type Protocol struct {
// VersionMajor, VersionMinor, and VersionPatch mark the version of this protocol with respect to semantic
// versioning.
VersionMajor, VersionMinor, VersionPatch uint
// Bind is called when the node has successfully started listening for new peers. Important node information
// such as the nodes binding host, binding port, public address, and ID are not initialized until after
// (*Node).Listen has successfully been called. Bind gets called the very moment such information has successfully
// been initialized.
//
// Errors returned from implementations of Bind will propagate back up to (*Node).Listen as a returned error.
Bind func(node *Node) error
// OnPeerConnected is called when a node successfully receives an incoming peer/connects to an outgoing peer, and
// completes noise's protocol handshake.
OnPeerConnected func(client *Client)
// OnPeerDisconnected is called whenever any inbound/outbound connection that has successfully connected to a node
// has been terminated.
OnPeerDisconnected func(client *Client)
// OnPingFailed is called whenever any attempt by a node to dial a peer at addr fails.
OnPingFailed func(addr string, err error)
// OnMessageSent is called whenever bytes of a message or request or response have been flushed/sent to a peer.
OnMessageSent func(client *Client)
// OnMessageRecv is called whenever a message or response is received from a peer.
OnMessageRecv func(client *Client)
}
Protocol is an interface that may be implemented by libraries and projects built on top of Noise to hook callbacks onto a series of events that are emitted throughout a nodes lifecycle. They may be registered to a node by (*Node).Bind before the node starts listening for new peers.
type PublicKey ¶
type PublicKey [SizePublicKey]byte
PublicKey is the default node/peer public key type.
func (PublicKey) MarshalJSON ¶
MarshalJSON returns the hexadecimal representation of this public key in JSON. It should never throw an error.
type Serializable ¶
type Serializable interface { // Marshal converts this type into it's byte representation as a slice. Marshal() []byte }
Serializable attributes whether or not a type has a byte representation that it may be serialized into.
type Signature ¶
type Signature [SizeSignature]byte
Signature is the default node/peer cryptographic signature type.
func UnmarshalSignature ¶
UnmarshalSignature decodes data into a Signature instance. It panics if data is not of expected length by instilling a bound check hint to the compiler. It uses unsafe hackery to zero-alloc convert data into a Signature.
func (Signature) MarshalJSON ¶
MarshalJSON returns the hexadecimal representation of this signature in JSON. It should never throw an error.
Source Files ¶
Directories ¶
Path | Synopsis |
---|---|
cmd
|
|
Package gossip is a simple implementation of a gossip protocol for noise.
|
Package gossip is a simple implementation of a gossip protocol for noise. |
Package kademlia is a noise implementation of the routing and discovery portion of the Kademlia protocol, with minor improvements suggested by the S/Kademlia paper.
|
Package kademlia is a noise implementation of the routing and discovery portion of the Kademlia protocol, with minor improvements suggested by the S/Kademlia paper. |