nim-libp2p-experimental/libp2p/protocols/identify.nim

219 lines
6.8 KiB
Nim
Raw Normal View History

2019-08-28 02:30:53 +00:00
## Nim-LibP2P
2019-09-24 17:48:23 +00:00
## Copyright (c) 2019 Status Research & Development GmbH
2019-08-28 02:30:53 +00:00
## Licensed under either of
## * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
## * MIT license ([LICENSE-MIT](LICENSE-MIT))
## at your option.
## This file may not be copied, modified, or distributed except according to
## those terms.
{.push raises: [Defect].}
import options
2019-09-09 17:33:32 +00:00
import chronos, chronicles
import ../protobuf/minprotobuf,
2019-09-08 07:43:33 +00:00
../peerinfo,
../connmanager,
../stream/connection,
../peerid,
../crypto/crypto,
2019-09-08 07:43:33 +00:00
../multiaddress,
../protocols/protocol,
../utility,
../errors
2019-08-28 02:30:53 +00:00
2019-09-10 02:15:52 +00:00
logScope:
2020-12-01 17:34:27 +00:00
topics = "libp2p identify"
2019-09-10 02:15:52 +00:00
2019-10-04 16:04:21 +00:00
const
IdentifyCodec* = "/ipfs/id/1.0.0"
IdentifyPushCodec* = "/ipfs/id/push/1.0.0"
ProtoVersion* = "ipfs/0.1.0"
AgentVersion* = "nim-libp2p/0.0.1"
2019-08-28 02:30:53 +00:00
type
IdentifyError* = object of LPError
IdentityNoMatchError* = object of IdentifyError
IdentityInvalidMsgError* = object of IdentifyError
IdentifyNoPubKeyError* = object of IdentifyError
2019-08-28 02:30:53 +00:00
2019-08-30 05:16:30 +00:00
IdentifyInfo* = object
2019-09-11 16:14:20 +00:00
pubKey*: Option[PublicKey]
2019-08-30 05:16:30 +00:00
addrs*: seq[MultiAddress]
2019-09-11 16:14:20 +00:00
observedAddr*: Option[MultiAddress]
protoVersion*: Option[string]
agentVersion*: Option[string]
protos*: seq[string]
2019-08-28 02:30:53 +00:00
2019-08-30 05:16:30 +00:00
Identify* = ref object of LPProtocol
2019-09-03 20:39:28 +00:00
peerInfo*: PeerInfo
2019-08-28 02:30:53 +00:00
IdentifyPush* = ref object of LPProtocol
connManager: ConnManager
proc encodeMsg*(peerInfo: PeerInfo, observedAddr: Multiaddress): ProtoBuffer
{.raises: [Defect, IdentifyNoPubKeyError].} =
2019-08-30 05:16:30 +00:00
result = initProtoBuffer()
if peerInfo.publicKey.isNone:
raise newException(IdentifyNoPubKeyError, "No public key found for peer!")
result.write(1, peerInfo.publicKey.get().getBytes().get())
2019-08-30 05:16:30 +00:00
for ma in peerInfo.addrs:
result.write(2, ma.data.buffer)
for proto in peerInfo.protocols:
result.write(3, proto)
result.write(4, observedAddr.data.buffer)
let protoVersion = ProtoVersion
result.write(5, protoVersion)
let agentVersion = if peerInfo.agentVersion.len <= 0:
AgentVersion
else:
peerInfo.agentVersion
result.write(6, agentVersion)
result.finish()
proc decodeMsg*(buf: seq[byte]): Option[IdentifyInfo] =
var
iinfo: IdentifyInfo
pubKey: PublicKey
oaddr: MultiAddress
protoVersion: string
agentVersion: string
var pb = initProtoBuffer(buf)
let r1 = pb.getField(1, pubKey)
let r2 = pb.getRepeatedField(2, iinfo.addrs)
let r3 = pb.getRepeatedField(3, iinfo.protos)
let r4 = pb.getField(4, oaddr)
let r5 = pb.getField(5, protoVersion)
let r6 = pb.getField(6, agentVersion)
let res = r1.isOk() and r2.isOk() and r3.isOk() and
r4.isOk() and r5.isOk() and r6.isOk()
if res:
if r1.get():
iinfo.pubKey = some(pubKey)
if r4.get():
iinfo.observedAddr = some(oaddr)
if r5.get():
iinfo.protoVersion = some(protoVersion)
if r6.get():
iinfo.agentVersion = some(agentVersion)
Gossip one one (#240) * allow multiple codecs per protocol (without breaking things) * add 1.1 protocol to gossip * explicit peering part 1 * explicit peering part 2 * explicit peering part 3 * PeerInfo and ControlPrune protocols * fix encodePrune * validated always, even explicit peers * prune by score (score is stub still) * add a way to pass parameters to gossip * standard setup fixes * take into account explicit direct peers in publish * add floodPublish logic * small fixes, publish still half broken * make sure to waitsub in sparse test * use var semantics to optimize table access * wip... lvalues don't work properly sadly... * big publish refactor, replenish and balance * fix internal tests * use g.peers for fanout (todo: don't include flood peers) * exclude non gossip from fanout * internal test fixes * fix flood tests * fix test's trypublish * test interop fixes * make sure to not remove peers from gossip table * restore old replenishFanout * cleanups * restore utility module import * restore trace vs debug in gossip * improve fanout replenish behavior further * triage publish nil peers (issue is on master too but just hidden behind a if/in) * getGossipPeers fixes * remove topics from pubsubpeer (was unused) * simplify rebalanceMesh (following spec) and make it finally reach D_high * better diagnostics * merge new pubsubpeer, copy 1.1 to new module * fix up merge * conditional enable gossip11 module * add back topics in peers, re-enable flood publish * add more heartbeat locking to prevent races * actually lock the heartbeat * minor fixes * with sugar * merge 1.0 * remove assertion in publish * fix multistream 1.1 multi proto * Fix merge oops * wip * fix gossip 11 upstream * gossipsub11 -> gossipsub * support interop testing * tests fixing * fix directchat build * control prune updates (pb) * wip parameters * gossip internal tests fixes * parameters wip * finishup with params * cleanups/wip * small sugar * grafted and pruned procs * wip updateScores * wip * fix logging issue * pubsubpeer, chronicles explicit override * fix internal gossip tests * wip * tables troubleshooting * score wip * score wip * fixes * fix test utils generateNodes * don't delete while iterating in score update * fix grafted defect * add a handleConnect in subscribeTopic * pruning improvements * wip * score fixes * post merge - builds gossip tests * further merge fixes * rebalance improvements and opportunistic grafting * fix test for now * restore explicit peering * implement peer exchange graft message * add an hard cap to PX * backoff time management * IWANT cap/budget * Adaptive gossip dissemination * outbound mesh quota, internal tests fixing * oversub prune score based, finish outbound quota * finishup with score and ihave budget * use go daemon 0.3.0 * import fixes * byScore cleanup score sorting * remove pointless scaling in `/` Duration operator * revert using libp2p org for daemon * interop fixes * fixes and cleanup * remove heartbeat assertion, minor debug fixes * logging improvements and cleaning up * (to revert) add some traces * add explicit topic to gossip rpcs * pubsub merge fixes and type fix in switch * Revert "(to revert) add some traces" This reverts commit 4663eaab6cc336c81cee50bc54025cf0b7bcbd99. * cleanup some now irrelevant todo * shuffle peers anyway as score might be disabled * add missing shuffle * old merge fix * more merge fixes * debug improvements * re-enable gossip internal tests * add gossip10 fallback (dormant but tested) * split gossipsub internal tests into 1.0 and 1.1 Co-authored-by: Dmitriy Ryajov <dryajov@gmail.com>
2020-09-21 09:16:29 +00:00
debug "decodeMsg: decoded message", pubkey = ($pubKey).shortLog,
addresses = $iinfo.addrs, protocols = $iinfo.protos,
observable_address = $iinfo.observedAddr,
proto_version = $iinfo.protoVersion,
agent_version = $iinfo.agentVersion
some(iinfo)
else:
trace "decodeMsg: failed to decode received message"
none[IdentifyInfo]()
proc new*(T: typedesc[Identify], peerInfo: PeerInfo): T =
let identify = T(peerInfo: peerInfo)
identify.init()
identify
proc newIdentify*(peerInfo: PeerInfo): Identify {.deprecated: "use Identify.new".} =
Identify.new(peerInfo)
2019-09-03 20:39:28 +00:00
method init*(p: Identify) =
2019-09-02 20:45:52 +00:00
proc handle(conn: Connection, proto: string) {.async, gcsafe, closure.} =
try:
trace "handling identify request", conn
var pb = encodeMsg(p.peerInfo, conn.observedAddr)
await conn.writeLp(pb.buffer)
except CancelledError as exc:
raise exc
except CatchableError as exc:
trace "exception in identify handler", exc = exc.msg, conn
finally:
trace "exiting identify handler", conn
await conn.closeWithEOF()
2019-08-30 23:45:57 +00:00
p.handler = handle
p.codec = IdentifyCodec
2019-08-30 23:45:57 +00:00
proc identify*(p: Identify,
conn: Connection,
remotePeerInfo: PeerInfo): Future[IdentifyInfo] {.async, gcsafe.} =
trace "initiating identify", conn
2020-05-08 20:58:23 +00:00
var message = await conn.readLp(64*1024)
if len(message) == 0:
trace "identify: Empty message received!", conn
raise newException(IdentityInvalidMsgError, "Empty message received!")
let infoOpt = decodeMsg(message)
if infoOpt.isNone():
raise newException(IdentityInvalidMsgError, "Incorrect message received!")
result = infoOpt.get()
2019-09-11 16:14:20 +00:00
if not isNil(remotePeerInfo) and result.pubKey.isSome:
let peer = PeerID.init(result.pubKey.get())
if peer.isErr:
raise newException(IdentityInvalidMsgError, $peer.error)
else:
if peer.get() != remotePeerInfo.peerId:
trace "Peer ids don't match",
remote = peer,
local = remotePeerInfo.peerId
raise newException(IdentityNoMatchError, "Peer ids don't match")
proc new*(T: typedesc[IdentifyPush], connManager: ConnManager): T =
let identifypush = T(connManager: connManager)
identifypush.init()
identifypush
proc init*(p: IdentifyPush) =
proc handle(conn: Connection, proto: string) {.async, gcsafe, closure.} =
trace "handling identify push", conn
try:
var message = await conn.readLp(64*1024)
let infoOpt = decodeMsg(message)
if infoOpt.isNone():
raise newException(IdentityInvalidMsgError, "Incorrect message received!")
let indentInfo = infoOpt.get()
if isNil(conn.peerInfo):
raise newException(IdentityInvalidMsgError, "Connection got no peerInfo")
if indentInfo.pubKey.isSome:
let receivedPeerId = PeerID.init(indentInfo.pubKey.get()).tryGet()
if receivedPeerId != conn.peerInfo.peerId:
raise newException(IdentityNoMatchError, "Peer ids don't match")
if indentInfo.addrs.len > 0:
conn.peerInfo.addrs = indentInfo.addrs
if indentInfo.agentVersion.isSome:
conn.peerInfo.agentVersion = indentInfo.agentVersion.get()
if indentInfo.protoVersion.isSome:
conn.peerInfo.protoVersion = indentInfo.protoVersion.get()
if indentInfo.protos.len > 0:
conn.peerInfo.protocols = indentInfo.protos
trace "triggering peer event", peerInfo = conn.peerInfo
await p.connManager.triggerPeerEvents(conn.peerInfo, PeerEvent(kind: PeerEventKind.Identified))
except CancelledError as exc:
raise exc
except CatchableError as exc:
info "exception in identify push handler", exc = exc.msg, conn
finally:
trace "exiting identify push handler", conn
await conn.closeWithEOF()
p.handler = handle
p.codec = IdentifyPushCodec
proc push*(p: IdentifyPush, peerInfo: PeerInfo, conn: Connection) {.async.} =
var pb = encodeMsg(peerInfo, conn.observedAddr)
2019-08-30 05:16:30 +00:00
await conn.writeLp(pb.buffer)