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.
|
||||
## 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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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..<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 ../libp2p/multiaddress
|
||||
|
||||
type
|
||||
PatternVector = object
|
||||
pattern: MaPattern
|
||||
good: seq[string]
|
||||
bad: seq[string]
|
||||
|
||||
const
|
||||
SuccessVectors = [
|
||||
"/ip4/1.2.3.4",
|
||||
|
@ -203,6 +209,88 @@ const
|
|||
"/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":
|
||||
|
||||
test "go-multiaddr success test vectors":
|
||||
|
@ -264,3 +352,12 @@ suite "MultiAddress test suite":
|
|||
check:
|
||||
hex(a) == PathExpects[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