**nim-eth-p2p** [![Build Status](https://travis-ci.org/status-im/nim-eth-p2p.svg?branch=master)](https://travis-ci.org/status-im/nim-eth-p2p) [![Build status](https://ci.appveyor.com/api/projects/status/i4txsa2pdyaahmn0/branch/master?svg=true)](https://ci.appveyor.com/project/cheatfate/nim-eth-p2p/branch/master)[![License: Apache](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)![Stability: experimental](https://img.shields.io/badge/stability-experimental-orange.svg) ## Introduction This library is a Nim re-implementation of the Ethereum DevP2P networking protocol. ## Installation ``` $ nimble install eth_p2p ``` ## RLPx [RLPx](https://github.com/ethereum/devp2p/blob/master/rlpx.md) is the high-level protocol for exchanging messages between peers in the Ethereum network. Most of the client code of this library should not be concerned with the implementation details of the underlying protocols and should use the high-level APIs described in this section. To obtain a RLPx connection, use the `rlpxConnect` proc supplying the id of another node in the network. On success, the proc will return a `Peer` object representing the connection. Each of the RLPx sub-protocols consists of a set of strongly-typed messages, which are represented by this library as regular Nim procs that can be executed over the `Peer` object (more on this later). ### Defining RLPx sub-protocols The sub-protocols are defined with the `rlpxProtocol` macro. It will accept a 3-letter identifier for the protocol and the current protocol version: Here is how the [DevP2P wire protocol](https://github.com/ethereum/wiki/wiki/%C3%90%CE%9EVp2p-Wire-Protocol) might look like: ``` nim rlpxProtocol p2p, 0: proc hello(peer: Peer, version: uint, clientId: string, capabilities: openarray[Capability], listenPort: uint, nodeId: P2PNodeId) = peer.id = nodeId peer.dispatcher = getDispatcher(capabilities) proc disconnect(peer: Peer, reason: DisconnectionReason) proc ping(peer: Peer) proc pong(peer: Peer) = echo "received pong from ", peer.id ``` #### Sending messages To send a particular message to a particular peer, just call the corresponding proc over the `Peer` object: ``` nim peer.hello(4, "Nimbus 1.0", ...) peer.ping() ``` #### Receiving messages Once a connection is established, incoming messages in RLPx may appear in arbitrary order, because the sub-protocols may be multiplexed over a single underlying connection. For this reason, the library assumes that the incoming messages will be dispatched automatically to their corresponding handlers, appearing in the protocol definition. The protocol implementations are expected to maintain a state and to act like a state machine handling the incoming messages. To achieve this, each protocol may define a `State` object that can be accessed as a `state` field of the `Peer` object: ``` nim rlpxProtocol abc, 1: type State = object receivedMsgsCount: int proc incomingMessage(p: Peer) = p.state.receivedMsgsCount += 1 ``` Sometimes, you'll need to access the state of another protocol. To do this, specify the protocol identifier to the `state` accessor: ``` nim echo "ABC protocol messages: ", peer.state(abc).receivedMsgCount ``` While the state machine approach is the recommended way of implementing sub-protocols, sometimes in imperative code it may be easier to wait for a particular response message after sending a certain request. This is enabled by the helper proc `nextMsg`: ``` nim proc handshakeExample(peer: Peer) {.async.} = ... # send a hello message peer.hello(...) # wait for a matching hello response let response = await peer.nextMsg(p2p.hello) echo response.clientId # print the name of the Ethereum client # used by the other peer (Geth, Parity, Nimbus, etc) ``` There are few things to note in the above example: 1. The `rlpxProtocol` definition created a pseudo-variable named after the protocol holding various properties of the protocol. 2. Each message defined in the protocol received a corresponding type name, matching the message name (e.g. `p2p.hello`). This type will have fields matching the parameter names of the message. If the messages has `openarray` params, these will be remapped to `seq` types. The future returned by `nextMsg` will be resolved only after the handler of the designated message has been fully executed (so you can count on any side effects produced by the handler to have taken place). If there are multiple outstanding calls to `nextMsg`, they will complete together. Any other messages received in the meantime will still be dispatched to their respective handlers. ### Checking the other peer's supported sub-protocols Upon establishing a connection, RLPx will automatically negotiate the list of mutually supported protocols by the peers. To check whether a particular peer supports a particular sub-protocol, use the following code: ``` nim if peer.supports(les): # `les` is the identifier of the light clients sub-protocol peer.getReceipts(nextReqId(), neededReceipts()) ``` ## License Distributed under one of the following: * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) This file may not be copied, modified, or distributed except according to those terms.