mirror of https://github.com/vacp2p/nim-libp2p.git
Add MultiAddress pattern matching procedures (go-multiaddr-fmt) with tests.
Add some comments.
This commit is contained in:
parent
4fa5ee3c93
commit
f8dc3abe36
|
@ -6,6 +6,9 @@
|
||||||
## at your option.
|
## at your option.
|
||||||
## This file may not be copied, modified, or distributed except according to
|
## This file may not be copied, modified, or distributed except according to
|
||||||
## those terms.
|
## those terms.
|
||||||
|
|
||||||
|
## This module implements cross-platform network interfaces list.
|
||||||
|
## Currently supported OSes are Windows, Linux, MacOS, BSD(not tested).
|
||||||
import algorithm
|
import algorithm
|
||||||
from strutils import toHex
|
from strutils import toHex
|
||||||
import ipnet
|
import ipnet
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
## This file may not be copied, modified, or distributed except according to
|
## This file may not be copied, modified, or distributed except according to
|
||||||
## those terms.
|
## those terms.
|
||||||
|
|
||||||
|
## This module implements various IP network utility procedures.
|
||||||
import endians, strutils
|
import endians, strutils
|
||||||
import chronos
|
import chronos
|
||||||
export chronos
|
export chronos
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
## This module implements MultiAddress.
|
## This module implements MultiAddress.
|
||||||
import tables, strutils, net
|
import tables, strutils, net
|
||||||
import multicodec, multihash, multibase, transcoder, base58, base32, vbuffer
|
import multicodec, multihash, multibase, transcoder, base58, base32, vbuffer
|
||||||
import peer
|
from peer import PeerID
|
||||||
|
|
||||||
{.deadCodeElim:on.}
|
{.deadCodeElim:on.}
|
||||||
|
|
||||||
|
@ -27,6 +27,18 @@ type
|
||||||
MultiAddress* = object
|
MultiAddress* = object
|
||||||
data*: VBuffer
|
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
|
MultiAddressError* = object of Exception
|
||||||
|
|
||||||
proc ip4StB(s: string, vb: var VBuffer): bool =
|
proc ip4StB(s: string, vb: var VBuffer): bool =
|
||||||
|
@ -219,6 +231,21 @@ proc dnsVB(vb: var VBuffer): bool =
|
||||||
if s.find('/') == -1:
|
if s.find('/') == -1:
|
||||||
result = true
|
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
|
const
|
||||||
TranscoderIP4* = Transcoder(
|
TranscoderIP4* = Transcoder(
|
||||||
stringToBuffer: ip4StB,
|
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,
|
proc initMultiAddressCodeTable(): Table[MultiCodec,
|
||||||
MAProtocol] {.compileTime.} =
|
MAProtocol] {.compileTime.} =
|
||||||
result = initTable[MultiCodec, MAProtocol]()
|
result = initTable[MultiCodec, MAProtocol]()
|
||||||
|
@ -527,6 +588,12 @@ proc `$`*(value: MultiAddress): string =
|
||||||
if len(parts) > 0:
|
if len(parts) > 0:
|
||||||
result = "/" & parts.join("/")
|
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 =
|
proc hex*(value: MultiAddress): string =
|
||||||
## Return hexadecimal string representation of MultiAddress ``value``.
|
## Return hexadecimal string representation of MultiAddress ``value``.
|
||||||
result = $(value.data)
|
result = $(value.data)
|
||||||
|
@ -715,3 +782,54 @@ proc isWire*(ma: MultiAddress): bool =
|
||||||
break
|
break
|
||||||
except:
|
except:
|
||||||
result = false
|
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..<len(pat.args):
|
||||||
|
let res = pat.args[i].matchPart(pcs)
|
||||||
|
if not res.flag:
|
||||||
|
return MaPatResult(flag: false, rem: res.rem)
|
||||||
|
pcs = res.rem
|
||||||
|
result = MaPatResult(flag: true, rem: pcs)
|
||||||
|
elif pat.operator == Eq:
|
||||||
|
if len(pcs) == 0:
|
||||||
|
return MaPatResult(flag: false, rem: empty)
|
||||||
|
if pcs[0] == pat.value:
|
||||||
|
return MaPatResult(flag: true, rem: pcs[1..^1])
|
||||||
|
result = MaPatResult(flag: false, rem: empty)
|
||||||
|
|
||||||
|
proc match*(pat: MaPattern, address: MultiAddress): bool =
|
||||||
|
## Match full ``address`` using pattern ``pat`` and return ``true`` if
|
||||||
|
## ``address`` satisfies pattern.
|
||||||
|
var protos = address.protocols()
|
||||||
|
let res = matchPart(pat, protos)
|
||||||
|
result = res.flag and (len(res.rem) == 0)
|
||||||
|
|
||||||
|
proc matchPartial*(pat: MaPattern, address: MultiAddress): bool =
|
||||||
|
## Match prefix part of ``address`` using pattern ``pat`` and return
|
||||||
|
## ``true`` if ``address`` starts with pattern.
|
||||||
|
var protos = address.protocols()
|
||||||
|
let res = matchPart(pat, protos)
|
||||||
|
result = res.flag
|
||||||
|
|
||||||
|
proc `$`*(pat: MaPattern): string =
|
||||||
|
## Return pattern ``pat`` as string.
|
||||||
|
var sub = newSeq[string]()
|
||||||
|
for a in pat.args:
|
||||||
|
sub.add($a)
|
||||||
|
if pat.operator == And:
|
||||||
|
result = sub.join("/")
|
||||||
|
elif pat.operator == Or:
|
||||||
|
result = "(" & sub.join("|") & ")"
|
||||||
|
elif pat.operator == Eq:
|
||||||
|
result = $pat.value
|
||||||
|
|
|
@ -1,6 +1,12 @@
|
||||||
import unittest
|
import unittest
|
||||||
import ../libp2p/multiaddress
|
import ../libp2p/multiaddress
|
||||||
|
|
||||||
|
type
|
||||||
|
PatternVector = object
|
||||||
|
pattern: MaPattern
|
||||||
|
good: seq[string]
|
||||||
|
bad: seq[string]
|
||||||
|
|
||||||
const
|
const
|
||||||
SuccessVectors = [
|
SuccessVectors = [
|
||||||
"/ip4/1.2.3.4",
|
"/ip4/1.2.3.4",
|
||||||
|
@ -203,6 +209,88 @@ const
|
||||||
"/ip4/127.0.0.1/udp/1234/quic"
|
"/ip4/127.0.0.1/udp/1234/quic"
|
||||||
]
|
]
|
||||||
|
|
||||||
|
PatternVectors = [
|
||||||
|
PatternVector(pattern: IP,
|
||||||
|
good: @["/ip4/0.0.0.0", "/ip6/fc00::"],
|
||||||
|
bad: @["/ip4/0.0.0.0/tcp/555", "/udp/789/ip6/fc00::"]
|
||||||
|
),
|
||||||
|
PatternVector(pattern: TCP,
|
||||||
|
good: @["/ip4/0.0.7.6/tcp/1234", "/ip6/::/tcp/0"],
|
||||||
|
bad: @["/tcp/12345", "/ip6/fc00::/udp/5523/tcp/9543"]
|
||||||
|
),
|
||||||
|
PatternVector(pattern: UDP,
|
||||||
|
good: @["/ip4/0.0.7.6/udp/1234", "/ip6/::/udp/0"],
|
||||||
|
bad: @["/udp/12345", "/ip6/fc00::/tcp/5523/udp/9543"],
|
||||||
|
),
|
||||||
|
PatternVector(pattern: UTP,
|
||||||
|
good: @["/ip4/1.2.3.4/udp/3456/utp", "/ip6/::/udp/0/utp"],
|
||||||
|
bad: @[
|
||||||
|
"/ip4/0.0.0.0/tcp/12345/utp",
|
||||||
|
"/ip6/fc00::/ip4/0.0.0.0/udp/1234/utp",
|
||||||
|
"/utp"
|
||||||
|
]
|
||||||
|
),
|
||||||
|
PatternVector(pattern: QUIC,
|
||||||
|
good: @["/ip4/1.2.3.4/udp/1234/quic", "/ip6/::/udp/1234/quic"],
|
||||||
|
bad: @[
|
||||||
|
"/ip4/0.0.0.0/tcp/12345/quic",
|
||||||
|
"/ip6/fc00::/ip4/0.0.0.0/udp/1234/quic",
|
||||||
|
"/quic"
|
||||||
|
]
|
||||||
|
),
|
||||||
|
PatternVector(pattern: IPFS,
|
||||||
|
good: @[
|
||||||
|
"/ip4/1.2.3.4/tcp/1234/ipfs/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ",
|
||||||
|
"/ip6/::/tcp/1234/ipfs/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ",
|
||||||
|
"/ip6/::/udp/1234/utp/ipfs/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ",
|
||||||
|
"/ip4/0.0.0.0/udp/1234/utp/ipfs/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ",
|
||||||
|
],
|
||||||
|
bad: @[
|
||||||
|
"/ip4/1.2.3.4/ipfs/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ",
|
||||||
|
"/ip6/::/ipfs/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ",
|
||||||
|
"/tcp/123/ipfs/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ",
|
||||||
|
"/ip6/::/udp/1234/ipfs/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ",
|
||||||
|
"/ip6/::/utp/ipfs/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ",
|
||||||
|
"/ipfs/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ",
|
||||||
|
]
|
||||||
|
),
|
||||||
|
PatternVector(pattern: DNS,
|
||||||
|
good: @["/dnsaddr/example.io", "/dns4/example.io", "/dns6/example.io"],
|
||||||
|
bad: @["/ip4/127.0.0.1"],
|
||||||
|
),
|
||||||
|
PatternVector(pattern: WebRTCDirect,
|
||||||
|
good: @[
|
||||||
|
"/ip4/1.2.3.4/tcp/3456/http/p2p-webrtc-direct",
|
||||||
|
"/ip6/::/tcp/0/http/p2p-webrtc-direct"
|
||||||
|
],
|
||||||
|
bad: @[
|
||||||
|
"/ip4/0.0.0.0", "/ip6/fc00::", "/udp/12345",
|
||||||
|
"/ip6/fc00::/tcp/5523/udp/9543"
|
||||||
|
]
|
||||||
|
),
|
||||||
|
PatternVector(pattern: HTTP,
|
||||||
|
good: @[
|
||||||
|
"/ip4/1.2.3.4/http", "/dns4/example.io/http",
|
||||||
|
"/dns6/::/tcp/7011/http", "/dnsaddr/example.io/http",
|
||||||
|
"/ip6/fc00::/http"
|
||||||
|
],
|
||||||
|
bad: @[
|
||||||
|
"/ip4/1.2.3.4/https", "/ip4/0.0.0.0/tcp/12345/quic",
|
||||||
|
"/ip6/fc00::/tcp/5523"
|
||||||
|
]
|
||||||
|
),
|
||||||
|
PatternVector(pattern: HTTPS,
|
||||||
|
good: @[
|
||||||
|
"/ip4/1.2.3.4/https", "/dns4/example.io/https",
|
||||||
|
"/dns6/::/tcp/7011/https", "/ip6/fc00::/https"
|
||||||
|
],
|
||||||
|
bad: @[
|
||||||
|
"/ip4/1.2.3.4/http", "/ip4/0.0.0.0/tcp/12345/quic",
|
||||||
|
"/ip6/fc00::/tcp/5523"
|
||||||
|
]
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
suite "MultiAddress test suite":
|
suite "MultiAddress test suite":
|
||||||
|
|
||||||
test "go-multiaddr success test vectors":
|
test "go-multiaddr success test vectors":
|
||||||
|
@ -264,3 +352,12 @@ suite "MultiAddress test suite":
|
||||||
check:
|
check:
|
||||||
hex(a) == PathExpects[i]
|
hex(a) == PathExpects[i]
|
||||||
$a == PathVectors[i]
|
$a == PathVectors[i]
|
||||||
|
|
||||||
|
test "MultiAddress pattern matching test vectors":
|
||||||
|
for item in PatternVectors:
|
||||||
|
for gitem in item.good:
|
||||||
|
var a = MultiAddress.init(gitem)
|
||||||
|
check item.pattern.match(a) == true
|
||||||
|
for bitem in item.bad:
|
||||||
|
var a = MultiAddress.init(bitem)
|
||||||
|
check item.pattern.match(a) == false
|
||||||
|
|
Loading…
Reference in New Issue