Add MultiAddress pattern matching procedures (go-multiaddr-fmt) with tests.

Add some comments.
This commit is contained in:
cheatfate 2019-03-20 11:41:37 +02:00
parent 4fa5ee3c93
commit f8dc3abe36
No known key found for this signature in database
GPG Key ID: 46ADD633A7201F95
4 changed files with 220 additions and 1 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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