From f8dc3abe36615ed71eec42f39ab1b4a31e5d31f3 Mon Sep 17 00:00:00 2001 From: cheatfate Date: Wed, 20 Mar 2019 11:41:37 +0200 Subject: [PATCH] Add MultiAddress pattern matching procedures (go-multiaddr-fmt) with tests. Add some comments. --- libp2p/ipnet/iface.nim | 3 + libp2p/ipnet/ipnet.nim | 1 + libp2p/multiaddress.nim | 120 ++++++++++++++++++++++++++++++++++++- tests/testmultiaddress.nim | 97 ++++++++++++++++++++++++++++++ 4 files changed, 220 insertions(+), 1 deletion(-) diff --git a/libp2p/ipnet/iface.nim b/libp2p/ipnet/iface.nim index 0eb328a4a..d1b831ce4 100644 --- a/libp2p/ipnet/iface.nim +++ b/libp2p/ipnet/iface.nim @@ -6,6 +6,9 @@ ## at your option. ## This file may not be copied, modified, or distributed except according to ## those terms. + +## This module implements cross-platform network interfaces list. +## Currently supported OSes are Windows, Linux, MacOS, BSD(not tested). import algorithm from strutils import toHex import ipnet diff --git a/libp2p/ipnet/ipnet.nim b/libp2p/ipnet/ipnet.nim index cd781db72..2a1206822 100644 --- a/libp2p/ipnet/ipnet.nim +++ b/libp2p/ipnet/ipnet.nim @@ -7,6 +7,7 @@ ## This file may not be copied, modified, or distributed except according to ## those terms. +## This module implements various IP network utility procedures. import endians, strutils import chronos export chronos diff --git a/libp2p/multiaddress.nim b/libp2p/multiaddress.nim index 3132b74b7..96e9211ca 100644 --- a/libp2p/multiaddress.nim +++ b/libp2p/multiaddress.nim @@ -10,7 +10,7 @@ ## This module implements MultiAddress. import tables, strutils, net import multicodec, multihash, multibase, transcoder, base58, base32, vbuffer -import peer +from peer import PeerID {.deadCodeElim:on.} @@ -27,6 +27,18 @@ type MultiAddress* = object data*: VBuffer + MaPatternOp* = enum + Eq, Or, And + + MaPattern* = object + operator*: MaPatternOp + args*: seq[MaPattern] + value*: MultiCodec + + MaPatResult* = object + flag*: bool + rem*: seq[MultiCodec] + MultiAddressError* = object of Exception proc ip4StB(s: string, vb: var VBuffer): bool = @@ -219,6 +231,21 @@ proc dnsVB(vb: var VBuffer): bool = if s.find('/') == -1: result = true +proc pEq(codec: string): MaPattern = + ## ``Equal`` operator for pattern + result.operator = Eq + result.value = multiCodec(codec) + +proc pOr(args: varargs[MaPattern]): MaPattern = + ## ``Or`` operator for pattern + result.operator = Or + result.args = @args + +proc pAnd(args: varargs[MaPattern]): MaPattern = + ## ``And`` operator for pattern + result.operator = And + result.args = @args + const TranscoderIP4* = Transcoder( stringToBuffer: ip4StB, @@ -349,6 +376,40 @@ const ) ] + DNS4* = pEq("dns4") + DNS6* = pEq("dns6") + IP4* = pEq("ip4") + IP6* = pEq("ip6") + DNS* = pOr(pEq("dnsaddr"), DNS4, DNS6) + IP* = pOr(IP4, IP6) + TCP* = pOr(pAnd(DNS, pEq("tcp")), pAnd(IP, pEq("tcp"))) + UDP* = pOr(pAnd(DNS, pEq("udp")), pAnd(IP, pEq("udp"))) + UTP* = pAnd(UDP, pEq("utp")) + QUIC* = pAnd(UDP, pEq("quic")) + + Unreliable* = pOr(UDP) + + Reliable* = pOr(TCP, UTP, QUIC) + + IPFS* = pAnd(Reliable, pEq("p2p")) + + HTTP* = pOr( + pAnd(TCP, pEq("http")), + pAnd(IP, pEq("http")), + pAnd(DNS, pEq("http")) + ) + + HTTPS* = pOr( + pAnd(TCP, pEq("https")), + pAnd(IP, pEq("https")), + pAnd(DNS, pEq("https")) + ) + + WebRTCDirect* = pOr( + pAnd(HTTP, pEq("p2p-webrtc-direct")), + pAnd(HTTPS, pEq("p2p-webrtc-direct")) + ) + proc initMultiAddressCodeTable(): Table[MultiCodec, MAProtocol] {.compileTime.} = result = initTable[MultiCodec, MAProtocol]() @@ -527,6 +588,12 @@ proc `$`*(value: MultiAddress): string = if len(parts) > 0: result = "/" & parts.join("/") +proc protocols*(value: MultiAddress): seq[MultiCodec] = + ## Returns list of protocol codecs inside of MultiAddress ``value``. + result = newSeq[MultiCodec]() + for item in value.items(): + result.add(item.protoCode()) + proc hex*(value: MultiAddress): string = ## Return hexadecimal string representation of MultiAddress ``value``. result = $(value.data) @@ -715,3 +782,54 @@ proc isWire*(ma: MultiAddress): bool = break except: result = false + +proc matchPart(pat: MaPattern, protos: seq[MultiCodec]): MaPatResult = + var empty: seq[MultiCodec] + var pcs = protos + if pat.operator == Or: + for a in pat.args: + let res = a.matchPart(pcs) + if res.flag: + return MaPatResult(flag: true, rem: res.rem) + result = MaPatResult(flag: false, rem: empty) + elif pat.operator == And: + if len(pcs) < len(pat.args): + return MaPatResult(flag: false, rem: empty) + for i in 0..