nim-eth-p2p/README.md

134 lines
5.3 KiB
Markdown

# 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)
[Nim] Ethereum P2P protocol implementation
## 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 proc `rlpxConnect` 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.
By default, `nextMsg` will still automatically dispatch all messages different
from the awaited one, but you can prevent this behavior by specifying the extra
flag `discardOthers = true`.
### 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
Licensed and distributed under either of
* MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT).
* Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0).
at your option. This file may not be copied, modified, or distributed except according to those terms.