diff --git a/go.mod b/go.mod index ca84034..2265d69 100644 --- a/go.mod +++ b/go.mod @@ -3,11 +3,11 @@ module github.com/plprobelab/go-kademlia go 1.20 require ( - github.com/benbjohnson/clock v1.3.0 + github.com/benbjohnson/clock v1.3.5 github.com/ipfs/go-cid v0.4.1 - github.com/libp2p/go-libp2p v0.27.6 + github.com/libp2p/go-libp2p v0.28.1 github.com/libp2p/go-msgio v0.3.0 - github.com/multiformats/go-multiaddr v0.9.0 + github.com/multiformats/go-multiaddr v0.10.0 github.com/multiformats/go-multibase v0.2.0 github.com/multiformats/go-multicodec v0.9.0 github.com/multiformats/go-multihash v0.2.3 @@ -16,7 +16,7 @@ require ( go.opentelemetry.io/otel/exporters/jaeger v1.16.0 go.opentelemetry.io/otel/sdk v1.16.0 go.opentelemetry.io/otel/trace v1.16.0 - google.golang.org/protobuf v1.30.0 + google.golang.org/protobuf v1.31.0 ) require ( @@ -39,27 +39,28 @@ require ( github.com/golang/mock v1.6.0 // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/google/gopacket v1.1.19 // indirect - github.com/google/pprof v0.0.0-20230405160723-4a4c7d95572b // indirect - github.com/huin/goupnp v1.1.0 // indirect + github.com/google/pprof v0.0.0-20230602150820-91b7bce49751 // indirect + github.com/gorilla/websocket v1.5.0 // indirect + github.com/huin/goupnp v1.2.0 // indirect github.com/ipfs/go-log/v2 v2.5.1 // indirect github.com/jackpal/go-nat-pmp v1.0.2 // indirect github.com/jbenet/go-temp-err-catcher v0.1.0 // indirect - github.com/klauspost/compress v1.16.4 // indirect + github.com/klauspost/compress v1.16.7 // indirect github.com/klauspost/cpuid/v2 v2.2.5 // indirect github.com/koron/go-ssdp v0.0.4 // indirect - github.com/kr/pretty v0.3.1 // indirect + github.com/kr/text v0.2.0 // indirect github.com/libp2p/go-buffer-pool v0.1.0 // indirect github.com/libp2p/go-cidranger v1.1.0 // indirect github.com/libp2p/go-flow-metrics v0.1.0 // indirect github.com/libp2p/go-libp2p-asn-util v0.3.0 // indirect - github.com/libp2p/go-nat v0.1.0 // indirect + github.com/libp2p/go-nat v0.2.0 // indirect github.com/libp2p/go-netroute v0.2.1 // indirect - github.com/libp2p/go-reuseport v0.2.0 // indirect - github.com/libp2p/go-yamux/v4 v4.0.0 // indirect + github.com/libp2p/go-reuseport v0.3.0 // indirect + github.com/libp2p/go-yamux/v4 v4.0.1 // indirect github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd // indirect github.com/mattn/go-isatty v0.0.19 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect - github.com/miekg/dns v1.1.53 // indirect + github.com/miekg/dns v1.1.55 // indirect github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b // indirect github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc // indirect github.com/minio/sha256-simd v1.0.1 // indirect @@ -70,38 +71,43 @@ require ( github.com/multiformats/go-multiaddr-fmt v0.1.0 // indirect github.com/multiformats/go-multistream v0.4.1 // indirect github.com/multiformats/go-varint v0.0.7 // indirect - github.com/onsi/ginkgo/v2 v2.9.2 // indirect + github.com/onsi/ginkgo/v2 v2.11.0 // indirect github.com/opencontainers/runtime-spec v1.0.2 // indirect github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_golang v1.14.0 // indirect - github.com/prometheus/client_model v0.3.0 // indirect - github.com/prometheus/common v0.42.0 // indirect - github.com/prometheus/procfs v0.9.0 // indirect + github.com/prometheus/client_golang v1.16.0 // indirect + github.com/prometheus/client_model v0.4.0 // indirect + github.com/prometheus/common v0.44.0 // indirect + github.com/prometheus/procfs v0.11.0 // indirect github.com/quic-go/qpack v0.4.0 // indirect github.com/quic-go/qtls-go1-19 v0.3.2 // indirect - github.com/quic-go/qtls-go1-20 v0.2.2 // indirect - github.com/quic-go/quic-go v0.33.0 // indirect - github.com/quic-go/webtransport-go v0.5.2 // indirect + github.com/quic-go/qtls-go1-20 v0.3.0 // indirect + github.com/quic-go/quic-go v0.36.1 // indirect + github.com/quic-go/webtransport-go v0.5.3 // indirect github.com/raulk/go-watchdog v1.3.0 // indirect - github.com/rogpeppe/go-internal v1.10.0 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect go.opentelemetry.io/otel/metric v1.16.0 // indirect go.uber.org/atomic v1.11.0 // indirect - go.uber.org/dig v1.16.1 // indirect - go.uber.org/fx v1.19.2 // indirect + go.uber.org/dig v1.17.0 // indirect + go.uber.org/fx v1.20.0 // indirect + go.uber.org/goleak v1.2.1 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.24.0 // indirect - golang.org/x/crypto v0.9.0 // indirect - golang.org/x/exp v0.0.0-20230321023759-10a507213a29 // indirect - golang.org/x/mod v0.10.0 // indirect - golang.org/x/net v0.10.0 // indirect - golang.org/x/sync v0.1.0 // indirect - golang.org/x/sys v0.8.0 // indirect - golang.org/x/text v0.9.0 // indirect - golang.org/x/tools v0.7.0 // indirect + golang.org/x/crypto v0.10.0 // indirect + golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df // indirect + golang.org/x/mod v0.11.0 // indirect + golang.org/x/net v0.11.0 // indirect + golang.org/x/sync v0.3.0 // indirect + golang.org/x/sys v0.9.0 // indirect + golang.org/x/text v0.10.0 // indirect + golang.org/x/tools v0.10.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect lukechampine.com/blake3 v1.2.1 // indirect - nhooyr.io/websocket v1.8.7 // indirect +) + +replace ( + github.com/quic-go/qtls-go1-19 => github.com/quic-go/qtls-go1-19 v0.2.1 + github.com/quic-go/qtls-go1-20 => github.com/quic-go/qtls-go1-20 v0.1.1 + github.com/quic-go/quic-go => github.com/quic-go/quic-go v0.33.0 ) diff --git a/go.sum b/go.sum index 45df414..0b056fa 100644 --- a/go.sum +++ b/go.sum @@ -10,8 +10,9 @@ git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGy github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= -github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o= +github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= @@ -53,10 +54,6 @@ github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJn github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= -github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= -github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14= -github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= @@ -64,21 +61,8 @@ github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= -github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= -github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= -github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= -github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= -github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY= -github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= -github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0= -github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= -github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8= -github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= -github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo= -github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= @@ -95,38 +79,31 @@ github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gopacket v1.1.17/go.mod h1:UdDNZ1OO62aGYVnPhxT1U6aI7ukYtA/kB8vaU0diBUM= github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20230405160723-4a4c7d95572b h1:Qcx5LM0fSiks9uCyFZwDBUasd3lxd1RM0GYpL+Li5o4= -github.com/google/pprof v0.0.0-20230405160723-4a4c7d95572b/go.mod h1:79YE0hCXdHag9sBkw2o+N/YnZtTkXi0UT9Nnixa5eYk= +github.com/google/pprof v0.0.0-20230602150820-91b7bce49751 h1:hR7/MlvK23p6+lIw9SN1TigNLn9ZnF3W4SYRKq2gAHs= +github.com/google/pprof v0.0.0-20230602150820-91b7bce49751/go.mod h1:Jh3hGz2jkYak8qXPD19ryItVnUgpgeqzdkY/D0EaeuA= github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM= -github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= -github.com/huin/goupnp v1.0.0/go.mod h1:n9v9KO1tAxYH82qOn+UTIFQDmx5n1Zxd/ClZDMX7Bnc= -github.com/huin/goupnp v1.1.0 h1:gEe0Dp/lZmPZiDFzJJaOfUpOvv2MKUkoBX8lDrn9vKU= -github.com/huin/goupnp v1.1.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= -github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o= +github.com/huin/goupnp v1.2.0 h1:uOKW26NG1hsSSbXIZ1IR7XP9Gjd1U8pnLaCMgntmkmY= +github.com/huin/goupnp v1.2.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= github.com/ipfs/go-cid v0.4.1 h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s= github.com/ipfs/go-cid v0.4.1/go.mod h1:uQHwDeX4c6CtyrFwdqyhpNcxVewur1M7l7fNU7LKwZk= github.com/ipfs/go-detect-race v0.0.1 h1:qX/xay2W3E4Q1U7d9lNs1sU9nvguX0a7319XbyQ6cOk= @@ -139,59 +116,49 @@ github.com/jbenet/go-temp-err-catcher v0.1.0 h1:zpb3ZH6wIE8Shj2sKS+khgRvf7T7RABo github.com/jbenet/go-temp-err-catcher v0.1.0/go.mod h1:0kJRvmDZXNMIiJirNPEYfhpPwbGVtZVWC34vc5WLsDk= github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= -github.com/klauspost/compress v1.16.4 h1:91KN02FnsOYhuunwU4ssRe8lc2JosWmizWa91B5v1PU= -github.com/klauspost/compress v1.16.4/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I= +github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= -github.com/koron/go-ssdp v0.0.0-20191105050749-2e1c40ed0b5d/go.mod h1:5Ky9EC2xfoUKUor0Hjgi2BJhCSXJfMOFlmyYrVKGQMk= github.com/koron/go-ssdp v0.0.4 h1:1IDwrghSKYM7yLf7XCzbByg2sJ/JcNOZRXS2jczTwz0= github.com/koron/go-ssdp v0.0.4/go.mod h1:oDXq+E5IL5q0U8uSBcoAXzTzInwy5lEgC91HoKtbmZk= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= -github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8= github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg= github.com/libp2p/go-cidranger v1.1.0 h1:ewPN8EZ0dd1LSnrtuwd4709PXVcITVeuwbag38yPW7c= github.com/libp2p/go-cidranger v1.1.0/go.mod h1:KWZTfSr+r9qEo9OkI9/SIEeAtw+NNoU0dXIXt15Okic= github.com/libp2p/go-flow-metrics v0.1.0 h1:0iPhMI8PskQwzh57jB9WxIuIOQ0r+15PChFGkx3Q3WM= github.com/libp2p/go-flow-metrics v0.1.0/go.mod h1:4Xi8MX8wj5aWNDAZttg6UPmc0ZrnFNsMtpsYUClFtro= -github.com/libp2p/go-libp2p v0.27.6 h1:KmGU5kskCaaerm53heqzfGOlrW2z8icZ+fnyqgrZs38= -github.com/libp2p/go-libp2p v0.27.6/go.mod h1:oMfQGTb9CHnrOuSM6yMmyK2lXz3qIhnkn2+oK3B1Y2g= +github.com/libp2p/go-libp2p v0.28.1 h1:YurK+ZAI6cKfASLJBVFkpVBdl3wGhFi6fusOt725ii8= +github.com/libp2p/go-libp2p v0.28.1/go.mod h1:s3Xabc9LSwOcnv9UD4nORnXKTsWkPMkIMB/JIGXVnzk= github.com/libp2p/go-libp2p-asn-util v0.3.0 h1:gMDcMyYiZKkocGXDQ5nsUQyquC9+H+iLEQHwOCZ7s8s= github.com/libp2p/go-libp2p-asn-util v0.3.0/go.mod h1:B1mcOrKUE35Xq/ASTmQ4tN3LNzVVaMNmq2NACuqyB9w= github.com/libp2p/go-libp2p-testing v0.12.0 h1:EPvBb4kKMWO29qP4mZGyhVzUyR25dvfUIK5WDu6iPUA= github.com/libp2p/go-msgio v0.3.0 h1:mf3Z8B1xcFN314sWX+2vOTShIE0Mmn2TXn3YCUQGNj0= github.com/libp2p/go-msgio v0.3.0/go.mod h1:nyRM819GmVaF9LX3l03RMh10QdOroF++NBbxAb0mmDM= -github.com/libp2p/go-nat v0.1.0 h1:MfVsH6DLcpa04Xr+p8hmVRG4juse0s3J8HyNWYHffXg= -github.com/libp2p/go-nat v0.1.0/go.mod h1:X7teVkwRHNInVNWQiO/tAiAVRwSr5zoRz4YSTC3uRBM= -github.com/libp2p/go-netroute v0.1.2/go.mod h1:jZLDV+1PE8y5XxBySEBgbuVAXbhtuHSdmLPL2n9MKbk= +github.com/libp2p/go-nat v0.2.0 h1:Tyz+bUFAYqGyJ/ppPPymMGbIgNRH+WqC5QrT5fKrrGk= +github.com/libp2p/go-nat v0.2.0/go.mod h1:3MJr+GRpRkyT65EpVPBstXLvOlAPzUVlG6Pwg9ohLJk= github.com/libp2p/go-netroute v0.2.1 h1:V8kVrpD8GK0Riv15/7VN6RbUQ3URNZVosw7H2v9tksU= github.com/libp2p/go-netroute v0.2.1/go.mod h1:hraioZr0fhBjG0ZRXJJ6Zj2IVEVNx6tDTFQfSmcq7mQ= -github.com/libp2p/go-reuseport v0.2.0 h1:18PRvIMlpY6ZK85nIAicSBuXXvrYoSw3dsBAR7zc560= -github.com/libp2p/go-reuseport v0.2.0/go.mod h1:bvVho6eLMm6Bz5hmU0LYN3ixd3nPPvtIlaURZZgOY4k= -github.com/libp2p/go-sockaddr v0.0.2/go.mod h1:syPvOmNs24S3dFVGJA1/mrqdeijPxLV2Le3BRLKd68k= -github.com/libp2p/go-yamux/v4 v4.0.0 h1:+Y80dV2Yx/kv7Y7JKu0LECyVdMXm1VUoko+VQ9rBfZQ= -github.com/libp2p/go-yamux/v4 v4.0.0/go.mod h1:NWjl8ZTLOGlozrXSOZ/HlfG++39iKNnM5wwmtQP1YB4= +github.com/libp2p/go-reuseport v0.3.0 h1:iiZslO5byUYZEg9iCwJGf5h+sf1Agmqx2V2FDjPyvUw= +github.com/libp2p/go-reuseport v0.3.0/go.mod h1:laea40AimhtfEqysZ71UpYj4S+R9VpH8PgqLo7L+SwI= +github.com/libp2p/go-yamux/v4 v4.0.1 h1:FfDR4S1wj6Bw2Pqbc8Uz7pCxeRBPbwsBbEdfwiCypkQ= +github.com/libp2p/go-yamux/v4 v4.0.1/go.mod h1:NWjl8ZTLOGlozrXSOZ/HlfG++39iKNnM5wwmtQP1YB4= github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd h1:br0buuQ854V8u83wA0rVZ8ttrq5CpaPZdvrK0LP2lOk= github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd/go.mod h1:QuCEs1Nt24+FYQEqAAncTDPJIuGs+LxK1MCiFL25pMU= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= @@ -200,8 +167,8 @@ github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zk github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= -github.com/miekg/dns v1.1.53 h1:ZBkuHr5dxHtB1caEOlZTLPo7D3L3TWckgUUs/RHfDxw= -github.com/miekg/dns v1.1.53/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY= +github.com/miekg/dns v1.1.55 h1:GoQ4hpsj0nFLYe+bWiCToyrBEJXkQfOOIvFGFy0lEgo= +github.com/miekg/dns v1.1.55/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY= github.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c h1:bzE/A84HN25pxAuk9Eej1Kz9OUelF97nAc82bDquQI8= github.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c/go.mod h1:0SQS9kMwD2VsyFEB++InYyBJroV/FRmBgcydeSUcJms= github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b h1:z78hV3sbSMAUoyUMM0I83AUIT6Hu17AWfgjzIbtrYFc= @@ -212,12 +179,8 @@ github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8Rv github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= @@ -227,8 +190,8 @@ github.com/multiformats/go-base36 v0.2.0 h1:lFsAbNOGeKtuKozrtBsAkSVhv1p9D0/qedU9 github.com/multiformats/go-base36 v0.2.0/go.mod h1:qvnKE++v+2MWCfePClUEjE78Z7P2a1UV0xHgWc0hkp4= github.com/multiformats/go-multiaddr v0.1.1/go.mod h1:aMKBKNEYmzmDmxfX88/vz+J5IU55txyt0p4aiWVohjo= github.com/multiformats/go-multiaddr v0.2.0/go.mod h1:0nO36NvPpyV4QzvTLi/lafl2y95ncPj0vFwVF6k6wJ4= -github.com/multiformats/go-multiaddr v0.9.0 h1:3h4V1LHIk5w4hJHekMKWALPXErDfz/sggzwC/NcqbDQ= -github.com/multiformats/go-multiaddr v0.9.0/go.mod h1:mI67Lb1EeTOYb8GQfL/7wpIZwc46ElrvzhYnoJOmTT0= +github.com/multiformats/go-multiaddr v0.10.0 h1:onErs0qLf3P2oRqrYk1fAMcfrWaJb58pYY43SkEF78w= +github.com/multiformats/go-multiaddr v0.10.0/go.mod h1:jLEZsA61rwWNZQTHHnqq2HNa+4os/Hz54eqiRnsRqYQ= github.com/multiformats/go-multiaddr-dns v0.3.1 h1:QgQgR+LQVt3NPTjbrLLpsaT2ufAA2y0Mkk+QRVJbW3A= github.com/multiformats/go-multiaddr-dns v0.3.1/go.mod h1:G/245BRQ6FJGmryJCrOuTdB37AMA5AMOVuO6NY3JwTk= github.com/multiformats/go-multiaddr-fmt v0.1.0 h1:WLEFClPycPkp4fnIzoFoV9FVd49/eQsuaL3/CWe167E= @@ -247,47 +210,44 @@ github.com/multiformats/go-varint v0.0.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/n github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU= github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= -github.com/onsi/ginkgo/v2 v2.9.2 h1:BA2GMJOtfGAfagzYtrAlufIP0lq6QERkFmHLMLPwFSU= -github.com/onsi/ginkgo/v2 v2.9.2/go.mod h1:WHcJJG2dIlcCqVfBAwUCrJxSPFb6v4azBwgxeMeDuts= -github.com/onsi/gomega v1.27.4 h1:Z2AnStgsdSayCMDiCU42qIz+HLqEPcgiOCXjAU/w+8E= +github.com/onsi/ginkgo/v2 v2.11.0 h1:WgqUCUt/lT6yXoQ8Wef0fsNn5cAuMK7+KT9UFRz2tcU= +github.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7kR0iZvM= +github.com/onsi/gomega v1.27.8 h1:gegWiwZjBsf2DgiSbf5hpokZ98JVDMcWkUiigk6/KXc= github.com/opencontainers/runtime-spec v1.0.2 h1:UfAcuLBJB9Coz72x1hgl8O5RVzTdNiaglX6v2DM6FI0= github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0= github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= -github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= +github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= +github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= -github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= +github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= +github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= -github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= +github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY= +github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= -github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= +github.com/prometheus/procfs v0.11.0 h1:5EAgkfkMl659uZPbe9AS2N68a7Cc1TJbPEuGzFuRbyk= +github.com/prometheus/procfs v0.11.0/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= -github.com/quic-go/qtls-go1-19 v0.3.2 h1:tFxjCFcTQzK+oMxG6Zcvp4Dq8dx4yD3dDiIiyc86Z5U= -github.com/quic-go/qtls-go1-19 v0.3.2/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI= -github.com/quic-go/qtls-go1-20 v0.2.2 h1:WLOPx6OY/hxtTxKV1Zrq20FtXtDEkeY00CGQm8GEa3E= -github.com/quic-go/qtls-go1-20 v0.2.2/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM= +github.com/quic-go/qtls-go1-19 v0.2.1 h1:aJcKNMkH5ASEJB9FXNeZCyTEIHU1J7MmHyz1Q1TSG1A= +github.com/quic-go/qtls-go1-19 v0.2.1/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI= +github.com/quic-go/qtls-go1-20 v0.1.1 h1:KbChDlg82d3IHqaj2bn6GfKRj84Per2VGf5XV3wSwQk= +github.com/quic-go/qtls-go1-20 v0.1.1/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM= github.com/quic-go/quic-go v0.33.0 h1:ItNoTDN/Fm/zBlq769lLJc8ECe9gYaW40veHCCco7y0= github.com/quic-go/quic-go v0.33.0/go.mod h1:YMuhaAV9/jIu0XclDXwZPAsP/2Kgr5yMYhe9oxhhOFA= -github.com/quic-go/webtransport-go v0.5.2 h1:GA6Bl6oZY+g/flt00Pnu0XtivSD8vukOu3lYhJjnGEk= -github.com/quic-go/webtransport-go v0.5.2/go.mod h1:OhmmgJIzTTqXK5xvtuX0oBpLV2GkLWNDA+UeTGJXErU= +github.com/quic-go/webtransport-go v0.5.3 h1:5XMlzemqB4qmOlgIus5zB45AcZ2kCgCy2EptUrfOPWU= +github.com/quic-go/webtransport-go v0.5.3/go.mod h1:OhmmgJIzTTqXK5xvtuX0oBpLV2GkLWNDA+UeTGJXErU= github.com/raulk/go-watchdog v1.3.0 h1:oUmdlHxdkXRJlwfG0O9omj8ukerm8MEQavSiDTEtBsk= github.com/raulk/go-watchdog v1.3.0/go.mod h1:fIvOnLbF0b0ZwkB9YU4mOW9Did//4vPZtDqv66NfsMU= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= -github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= @@ -329,10 +289,6 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= -github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= -github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= -github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= -github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU= github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= @@ -353,12 +309,13 @@ go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLk go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -go.uber.org/dig v1.16.1 h1:+alNIBsl0qfY0j6epRubp/9obgtrObRAc5aD+6jbWY8= -go.uber.org/dig v1.16.1/go.mod h1:557JTAUZT5bUK0SvCwikmLPPtdQhfvLYtO5tJgQSbnk= -go.uber.org/fx v1.19.2 h1:SyFgYQFr1Wl0AYstE8vyYIzP4bFz2URrScjwC4cwUvY= -go.uber.org/fx v1.19.2/go.mod h1:43G1VcqSzbIv77y00p1DRAsyZS8WdzuYdhZXmEUkMyQ= +go.uber.org/dig v1.17.0 h1:5Chju+tUvcC+N7N6EV08BJz41UZuO3BmHcN4A287ZLI= +go.uber.org/dig v1.17.0/go.mod h1:rTxpf7l5I0eBTlE6/9RL+lDybC7WFwY2QH55ZSjy1mU= +go.uber.org/fx v1.20.0 h1:ZMC/pnRvhsthOZh9MZjMq5U8Or3mA9zBSPaLnzs3ihQ= +go.uber.org/fx v1.20.0/go.mod h1:qCUj0btiR3/JnanEr1TYEePfSw6o/4qYJscgvzQ5Ub0= go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= -go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= +go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= +go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= @@ -375,11 +332,11 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200602180216-279210d13fed/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= -golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= +golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM= +golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20230321023759-10a507213a29 h1:ooxPy7fPvB4kwsA2h+iBNHkAbp/4JxTSwCmvdjEYmug= -golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= +golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df h1:UA2aFVmmsIlefxMk29Dp2juaUSth8Pyn3Tq5Y5mJGME= +golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= @@ -389,12 +346,11 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= -golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU= +golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -408,8 +364,8 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= -golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.11.0 h1:Gi2tvZIJyBtO9SDr1q9h5hEQCp/4L2RQ+ar0qjx2oNU= +golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -423,19 +379,16 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20180810173357-98c5dad5d1a0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190405154228-4b34438f7a67/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -446,18 +399,16 @@ golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s= +golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58= +golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -471,8 +422,8 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= -golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= +golang.org/x/tools v0.10.0 h1:tvDr/iQoUqNdohiYm0LmmKcBk+q86lb9EprIUFhHHGg= +golang.org/x/tools v0.10.0/go.mod h1:UJwyiVBsOA2uwvK/e5OY3GTpDUJriEd+/YlqAwLPmyM= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -495,8 +446,8 @@ google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3 google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= -google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= @@ -505,7 +456,6 @@ gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= @@ -516,7 +466,5 @@ honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= lukechampine.com/blake3 v1.2.1 h1:YuqqRuaqsGV71BV/nm9xlI0MKUv4QC54jQnBChWbGnI= lukechampine.com/blake3 v1.2.1/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= -nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g= -nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck= sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0= diff --git a/internal/testutil/bench.go b/internal/testutil/bench.go new file mode 100644 index 0000000..842bbfa --- /dev/null +++ b/internal/testutil/bench.go @@ -0,0 +1,11 @@ +//go:build go1.20 + +package testutil + +import "testing" + +// ReportTimePerItemMetric adds a custom metric to a benchmark that reports the number of nanoseconds taken per item. +func ReportTimePerItemMetric(b *testing.B, n int, name string) { + // b.Elapsed was added in Go 1.20 + b.ReportMetric(float64(b.Elapsed().Nanoseconds())/float64(n), "ns/"+name) +} diff --git a/internal/testutil/bench_pre120.go b/internal/testutil/bench_pre120.go new file mode 100644 index 0000000..b869c41 --- /dev/null +++ b/internal/testutil/bench_pre120.go @@ -0,0 +1,10 @@ +//go:build !go1.20 + +package testutil + +import "testing" + +// ReportTimePerItemMetric is a no-op on versions of Go before 1.20 +func ReportTimePerItemMetric(b *testing.B, n int, name string) { + // no-op +} diff --git a/internal/testutil/rand.go b/internal/testutil/rand.go new file mode 100644 index 0000000..21dda68 --- /dev/null +++ b/internal/testutil/rand.go @@ -0,0 +1,56 @@ +package testutil + +import ( + "encoding/binary" + "math/rand" + "strconv" + + "github.com/plprobelab/go-kademlia/key" +) + +var rng = rand.New(rand.NewSource(299792458)) + +// Random returns a KadKey with the specificed number of bits populated with random data. +func Random(bits int) key.KadKey { + buf := make([]byte, (bits+7)/8) + rng.Read(buf) + return buf +} + +// RandomWithPrefix returns a KadKey with the specificed number of bits having a prefix equal to the bit pattern held in s. +// A prefix of up to 64 bits is supported. +func RandomWithPrefix(s string, bits int) key.KadKey { + kk := Random(bits) + if s == "" { + return kk + } + + prefixbits := len(s) + if prefixbits > 64 { + panic("RandomWithPrefix: prefix too long") + } else if prefixbits > bits { + panic("RandomWithPrefix: prefix longer than key length") + } + n, err := strconv.ParseInt(s, 2, 64) + if err != nil { + panic("RandomWithPrefix: " + err.Error()) + } + prefix := uint64(n) << (64 - prefixbits) + + // sizes are in bytes + keySize := (bits + 7) / 8 + bufSize := keySize + if bufSize < 8 { + bufSize = 8 + } + + buf := make([]byte, bufSize) + rng.Read(buf) + + lead := binary.BigEndian.Uint64(buf) + lead <<= prefixbits + lead >>= prefixbits + lead |= prefix + binary.BigEndian.PutUint64(buf, lead) + return key.KadKey(buf[:keySize]) +} diff --git a/key/errors.go b/key/errors.go new file mode 100644 index 0000000..6fb9a39 --- /dev/null +++ b/key/errors.go @@ -0,0 +1,9 @@ +package key + +import ( + "fmt" +) + +func ErrInvalidKey(l int) error { + return fmt.Errorf("invalid key: should be %d bytes long", l) +} diff --git a/key/key.go b/key/key.go index b4c3fd5..5c14e71 100644 --- a/key/key.go +++ b/key/key.go @@ -2,7 +2,9 @@ package key import ( "encoding/hex" + "fmt" "math" + "strings" ) type KadKey []byte @@ -26,6 +28,15 @@ func shortLong(a, b KadKey) (min, max KadKey) { return b, a } +// BitString returns a bit representation of the key, in descending order of significance. +func (k KadKey) BitString() string { + sb := new(strings.Builder) + for _, b := range k { + sb.WriteString(fmt.Sprintf("%08b", b)) + } + return sb.String() +} + func (a KadKey) Xor(b KadKey) KadKey { short, long := shortLong(a, b) xored := make([]byte, len(long)) @@ -73,5 +84,25 @@ func (a KadKey) Compare(b KadKey) int { } func (a KadKey) Equal(b KadKey) bool { + if a.Size() != b.Size() { + return false + } return a.Compare(b) == 0 } + +// BitLen returns the length of the key in bits +func (k KadKey) BitLen() int { + return len(k) * 8 +} + +// BitAt returns the value of the i'th bit of the key from most significant to least. It is equivalent to (key>>(bitlen-i-1))&1. +func (k KadKey) BitAt(i int) int { + if i < 0 || i > k.BitLen()-1 { + panic("BitAt: index out of range") + } + if k[i/8]&(byte(1)<<(7-i%8)) == 0 { + return 0 + } else { + return 1 + } +} diff --git a/key/key_test.go b/key/key_test.go index ab927f0..fe3a423 100644 --- a/key/key_test.go +++ b/key/key_test.go @@ -111,3 +111,20 @@ func TestCompare(t *testing.T) { require.Equal(t, 0, key.Compare([]byte{0x80, 0x00, 0x00, 0x00})) // a == b -> 0 require.Equal(t, -1, key.Compare([]byte{0x80, 0x00, 0x00, 0x00, 0x00})) // a is prefix of b -> -1 } + +func TestBitAt(t *testing.T) { + kk := KadKey([]byte{0b10010011, 0b11110100}) + require.Equal(t, 1, kk.BitAt(0)) + require.Equal(t, 0, kk.BitAt(1)) + require.Equal(t, 0, kk.BitAt(2)) + require.Equal(t, 1, kk.BitAt(3)) + require.Equal(t, 0, kk.BitAt(4)) + require.Equal(t, 0, kk.BitAt(5)) + require.Equal(t, 1, kk.BitAt(6)) + require.Equal(t, 1, kk.BitAt(7)) + require.Equal(t, 1, kk.BitAt(8)) + require.Equal(t, 0, kk.BitAt(15)) + + require.Panics(t, func() { kk.BitAt(-1) }) + require.Panics(t, func() { kk.BitAt(16) }) +} diff --git a/key/trie/doc.go b/key/trie/doc.go new file mode 100644 index 0000000..49df19f --- /dev/null +++ b/key/trie/doc.go @@ -0,0 +1,2 @@ +// Package trie provides an implementation of a XOR Trie. +package trie diff --git a/key/trie/trie.go b/key/trie/trie.go new file mode 100644 index 0000000..85d1985 --- /dev/null +++ b/key/trie/trie.go @@ -0,0 +1,323 @@ +// Package trie provides an implementation of a XOR Trie +package trie + +import ( + "errors" + + "github.com/plprobelab/go-kademlia/key" +) + +var ErrMismatchedKeyLength = errors.New("key length does not match existing keys") + +// Trie is a trie for equal-length bit vectors, which stores values only in the leaves. +// A node may optionally hold data of type T +// Trie node invariants: +// (1) Either both branches are nil, or both are non-nil. +// (2) If branches are non-nil, key must be nil. +// (3) If both branches are leaves, then they are both non-empty (have keys). +type Trie[T any] struct { + branch [2]*Trie[T] + key key.KadKey + data T +} + +func New[T any]() *Trie[T] { + return &Trie[T]{} +} + +func (tr *Trie[T]) Key() key.KadKey { + return tr.key +} + +func (tr *Trie[T]) Data() T { + return tr.data +} + +func (tr *Trie[T]) Branch(dir int) *Trie[T] { + return tr.branch[dir] +} + +// Size returns the number of keys added to the trie. +func (tr *Trie[T]) Size() int { + return tr.sizeAtDepth(0) +} + +// Size returns the number of keys added to the trie at or beyond depth d. +func (tr *Trie[T]) sizeAtDepth(d int) int { + if tr.IsLeaf() { + if !tr.HasKey() { + return 0 + } else { + return 1 + } + } else { + return tr.branch[0].sizeAtDepth(d+1) + tr.branch[1].sizeAtDepth(d+1) + } +} + +// HasKey reports whether the Trie node holds a key. +func (tr *Trie[T]) HasKey() bool { + return tr.key != nil +} + +// IsLeaf reports whether the Trie is a leaf node. A leaf node has no child branches but may hold a key and data. +func (tr *Trie[T]) IsLeaf() bool { + return tr.branch[0] == nil && tr.branch[1] == nil +} + +// IsEmptyLeaf reports whether the Trie is a leaf node without branches that also has no key. +func (tr *Trie[T]) IsEmptyLeaf() bool { + return !tr.HasKey() && tr.IsLeaf() +} + +// IsEmptyLeaf reports whether the Trie is a leaf node without branches but has a key. +func (tr *Trie[T]) IsNonEmptyLeaf() bool { + return tr.HasKey() && tr.IsLeaf() +} + +func (tr *Trie[T]) Copy() *Trie[T] { + if tr.IsLeaf() { + return &Trie[T]{key: tr.key, data: tr.data} + } + + return &Trie[T]{branch: [2]*Trie[T]{ + tr.branch[0].Copy(), + tr.branch[1].Copy(), + }} +} + +func (tr *Trie[T]) shrink() { + b0, b1 := tr.branch[0], tr.branch[1] + switch { + case b0.IsEmptyLeaf() && b1.IsEmptyLeaf(): + tr.branch[0], tr.branch[1] = nil, nil + case b0.IsEmptyLeaf() && b1.IsNonEmptyLeaf(): + tr.key = b1.key + tr.branch[0], tr.branch[1] = nil, nil + case b0.IsNonEmptyLeaf() && b1.IsEmptyLeaf(): + tr.key = b0.key + tr.branch[0], tr.branch[1] = nil, nil + } +} + +func (tr *Trie[T]) firstNonEmptyLeaf() *Trie[T] { + if tr.IsLeaf() { + if tr.HasKey() { + return tr + } + return nil + } + f := tr.branch[0].firstNonEmptyLeaf() + if f != nil { + return f + } + return tr.branch[1].firstNonEmptyLeaf() +} + +// Add attempts to add a key to the trie, mutating the trie. +// Returns true if the key was added, false otherwise. +func (tr *Trie[T]) Add(kk key.KadKey, data T) (bool, error) { + f := tr.firstNonEmptyLeaf() + if f != nil { + if f.key.Size() != kk.Size() { + return false, ErrMismatchedKeyLength + } + } + return tr.addAtDepth(0, kk, data), nil +} + +func (tr *Trie[T]) addAtDepth(depth int, kk key.KadKey, data T) bool { + switch { + case tr.IsEmptyLeaf(): + tr.key = kk + tr.data = data + return true + case tr.IsNonEmptyLeaf(): + if tr.key.Equal(kk) { + return false + } else { + p := tr.key + d := tr.data + tr.key = nil + var v T + tr.data = v + // both branches are nil + tr.branch[0], tr.branch[1] = &Trie[T]{}, &Trie[T]{} + tr.branch[p.BitAt(depth)].key = p + tr.branch[p.BitAt(depth)].data = d + return tr.branch[kk.BitAt(depth)].addAtDepth(depth+1, kk, data) + } + default: + return tr.branch[kk.BitAt(depth)].addAtDepth(depth+1, kk, data) + } +} + +// Add adds the key to trie, returning a new trie. +// Add is immutable/non-destructive: the original trie remains unchanged. +func Add[T any](tr *Trie[T], kk key.KadKey, data T) (*Trie[T], error) { + f := tr.firstNonEmptyLeaf() + if f != nil { + if f.key.Size() != kk.Size() { + return tr, ErrMismatchedKeyLength + } + } + return addAtDepth(0, tr, kk, data), nil +} + +func addAtDepth[T any](depth int, tr *Trie[T], kk key.KadKey, data T) *Trie[T] { + switch { + case tr.IsEmptyLeaf(): + return &Trie[T]{key: kk, data: data} + case tr.IsNonEmptyLeaf(): + eq := tr.key.Equal(kk) + if eq { + return tr + } + return trieForTwo(depth, tr.key, tr.data, kk, data) + + default: + dir := kk.BitAt(depth) + s := &Trie[T]{} + s.branch[dir] = addAtDepth(depth+1, tr.branch[dir], kk, data) + s.branch[1-dir] = tr.branch[1-dir] + return s + } +} + +func trieForTwo[T any](depth int, p key.KadKey, pdata T, q key.KadKey, qdata T) *Trie[T] { + pDir, qDir := p.BitAt(depth), q.BitAt(depth) + if qDir == pDir { + s := &Trie[T]{} + s.branch[pDir] = trieForTwo(depth+1, p, pdata, q, qdata) + s.branch[1-pDir] = &Trie[T]{} + return s + } else { + s := &Trie[T]{} + s.branch[pDir] = &Trie[T]{key: p, data: pdata} + s.branch[qDir] = &Trie[T]{key: q, data: qdata} + return s + } +} + +// Remove attempts to remove a key from the trie, mutating the trie. +// Returns true if the key was removed, false otherwise. +func (tr *Trie[T]) Remove(kk key.KadKey) (bool, error) { + f := tr.firstNonEmptyLeaf() + if f != nil { + if f.key.Size() != kk.Size() { + return false, ErrMismatchedKeyLength + } + } + return tr.removeAtDepth(0, kk), nil +} + +func (tr *Trie[T]) removeAtDepth(depth int, kk key.KadKey) bool { + switch { + case tr.IsEmptyLeaf(): + return false + case tr.IsNonEmptyLeaf(): + eq := tr.key.Equal(kk) + if !eq { + return false + } + tr.key = nil + var v T + tr.data = v + return true + default: + if tr.branch[kk.BitAt(depth)].removeAtDepth(depth+1, kk) { + tr.shrink() + return true + } + return false + } +} + +// Remove removes the key from the trie. +// Remove is immutable/non-destructive: the original trie remains unchanged. +// If the key did not exist in the trie then the original trie is returned. +func Remove[T any](tr *Trie[T], kk key.KadKey) (*Trie[T], error) { + f := tr.firstNonEmptyLeaf() + if f != nil { + if f.key.Size() != kk.Size() { + return tr, ErrMismatchedKeyLength + } + } + return removeAtDepth(0, tr, kk), nil +} + +func removeAtDepth[T any](depth int, tr *Trie[T], kk key.KadKey) *Trie[T] { + switch { + case tr.IsEmptyLeaf(): + return tr + case tr.IsNonEmptyLeaf(): + eq := tr.key.Equal(kk) + if !eq { + return tr + } + return &Trie[T]{} + + default: + dir := kk.BitAt(depth) + afterDelete := removeAtDepth(depth+1, tr.branch[dir], kk) + if afterDelete == tr.branch[dir] { + return tr + } + copy := &Trie[T]{} + copy.branch[dir] = afterDelete + copy.branch[1-dir] = tr.branch[1-dir] + copy.shrink() + return copy + } +} + +func Equal[T any](a, b *Trie[T]) bool { + switch { + case a.IsLeaf() && b.IsLeaf(): + eq := a.key.Equal(b.key) + if !eq { + return false + } + return true + case !a.IsLeaf() && !b.IsLeaf(): + return Equal(a.branch[0], b.branch[0]) && Equal(a.branch[1], b.branch[1]) + } + return false +} + +// Find looks for a key in the trie. +// It reports whether the key was found along with data value held with the key. +func Find[T any](tr *Trie[T], kk key.KadKey) (bool, T) { + f, _ := findFromDepth(tr, 0, kk) + if f == nil { + var v T + return false, v + } + return true, f.data +} + +// Locate looks for the position of a key in the trie. +// It reports whether the key was found along with the depth of the leaf reached along the path +// of the key, regardless of whether the key was found in that leaf. +func Locate[T any](tr *Trie[T], kk key.KadKey) (bool, int) { + f, depth := findFromDepth(tr, 0, kk) + if f == nil { + return false, depth + } + return true, depth +} + +func findFromDepth[T any](tr *Trie[T], depth int, kk key.KadKey) (*Trie[T], int) { + switch { + case tr.IsEmptyLeaf(): + return nil, depth + case tr.IsNonEmptyLeaf(): + eq := tr.key.Equal(kk) + if !eq { + return nil, depth + } + return tr, depth + default: + return findFromDepth(tr.branch[kk.BitAt(depth)], depth+1, kk) + } +} diff --git a/key/trie/trie_test.go b/key/trie/trie_test.go new file mode 100644 index 0000000..a575cb7 --- /dev/null +++ b/key/trie/trie_test.go @@ -0,0 +1,882 @@ +package trie + +import ( + "math/rand" + "testing" + + "github.com/plprobelab/go-kademlia/internal/testutil" + "github.com/plprobelab/go-kademlia/key" + "github.com/stretchr/testify/require" +) + +func trieFromKeys[T any](kks []key.KadKey) (*Trie[T], error) { + t := New[T]() + var err error + for _, kk := range kks { + var v T + t, err = Add(t, kk, v) + if err != nil { + return nil, err + } + } + return t, nil +} + +func TestRepeatedAddRemove(t *testing.T) { + r := New[any]() + testSeq(t, r) + testSeq(t, r) +} + +func testSeq[T any](t *testing.T, tr *Trie[T]) { + t.Helper() + var err error + for _, s := range testInsertSeq { + var v T + tr, err = Add(tr, s.key, v) + if err != nil { + t.Fatalf("unexpected error during add: %v", err) + } + found, depth := Locate(tr, s.key) + if !found { + t.Fatalf("key not found: %v", s.key) + } + if depth != s.insertedDepth { + t.Errorf("inserting expected depth %d, got %d", s.insertedDepth, depth) + } + if d := CheckInvariant(tr); d != nil { + t.Fatalf("trie invariant discrepancy: %v", d) + } + } + for _, s := range testRemoveSeq { + tr, err = Remove(tr, s.key) + if err != nil { + t.Fatalf("unexpected error during remove: %v", err) + } + found, _ := Locate(tr, s.key) + if found { + t.Fatalf("key unexpectedly found: %v", s.key) + } + if d := CheckInvariant(tr); d != nil { + t.Fatalf("trie invariant discrepancy: %v", d) + } + } +} + +func TestCopy(t *testing.T) { + tr, err := trieFromKeys[any](sampleKeySet.Keys) + if err != nil { + t.Fatalf("unexpected error during from keys: %v", err) + } + copy := tr.Copy() + if d := CheckInvariant(copy); d != nil { + t.Fatalf("trie invariant discrepancy: %v", d) + } + if tr == copy { + t.Errorf("Expected trie copy not to be the same reference as original") + } + if !Equal(tr, copy) { + t.Errorf("Expected tries to be equal, original: %v\n, copy: %v\n", tr, copy) + } +} + +var testInsertSeq = []struct { + key key.KadKey + insertedDepth int +}{ + {key: key.KadKey([]byte{0x00}), insertedDepth: 0}, + {key: key.KadKey([]byte{0x80}), insertedDepth: 1}, + {key: key.KadKey([]byte{0x10}), insertedDepth: 4}, + {key: key.KadKey([]byte{0xc0}), insertedDepth: 2}, + {key: key.KadKey([]byte{0x20}), insertedDepth: 3}, +} + +var testRemoveSeq = []struct { + key key.KadKey + reachedDepth int +}{ + {key: key.KadKey([]byte{0x00}), reachedDepth: 4}, + {key: key.KadKey([]byte{0x10}), reachedDepth: 3}, + {key: key.KadKey([]byte{0x20}), reachedDepth: 1}, + {key: key.KadKey([]byte{0x80}), reachedDepth: 2}, + {key: key.KadKey([]byte{0xc0}), reachedDepth: 0}, +} + +func TestAddIsOrderIndependent(t *testing.T) { + for _, s := range newKeySetList(100) { + base := New[any]() + for _, k := range s.Keys { + base.Add(k, nil) + } + if d := CheckInvariant(base); d != nil { + t.Fatalf("base trie invariant discrepancy: %v", d) + } + for j := 0; j < 100; j++ { + perm := rand.Perm(len(s.Keys)) + reordered := New[any]() + for i := range s.Keys { + reordered.Add(s.Keys[perm[i]], nil) + } + if d := CheckInvariant(reordered); d != nil { + t.Fatalf("reordered trie invariant discrepancy: %v", d) + } + if !Equal(base, reordered) { + t.Errorf("trie %v differs from trie %v", base, reordered) + } + } + } +} + +func TestImmutableAddIsOrderIndependent(t *testing.T) { + for _, s := range newKeySetList(100) { + base := New[any]() + for _, k := range s.Keys { + base, _ = Add(base, k, nil) + } + if d := CheckInvariant(base); d != nil { + t.Fatalf("base trie invariant discrepancy: %v", d) + } + for j := 0; j < 100; j++ { + perm := rand.Perm(len(s.Keys)) + reordered := New[any]() + for i := range s.Keys { + reordered, _ = Add(reordered, s.Keys[perm[i]], nil) + } + if d := CheckInvariant(reordered); d != nil { + t.Fatalf("reordered trie invariant discrepancy: %v", d) + } + if !Equal(base, reordered) { + t.Errorf("trie %v differs from trie %v", base, reordered) + } + } + } +} + +func TestSize(t *testing.T) { + tr := New[any]() + require.Equal(t, 0, tr.Size()) + + var err error + for _, kk := range sampleKeySet.Keys { + tr, err = Add(tr, kk, nil) + require.NoError(t, err) + } + + require.Equal(t, len(sampleKeySet.Keys), tr.Size()) +} + +func TestAddIgnoresDuplicates(t *testing.T) { + tr := New[any]() + for _, kk := range sampleKeySet.Keys { + added, err := tr.Add(kk, nil) + require.NoError(t, err) + require.True(t, added) + } + require.Equal(t, len(sampleKeySet.Keys), tr.Size()) + + for _, kk := range sampleKeySet.Keys { + added, err := tr.Add(kk, nil) + require.NoError(t, err) + require.False(t, added) + } + require.Equal(t, len(sampleKeySet.Keys), tr.Size()) + + if d := CheckInvariant(tr); d != nil { + t.Fatalf("reordered trie invariant discrepancy: %v", d) + } +} + +func TestImmutableAddIgnoresDuplicates(t *testing.T) { + tr := New[any]() + var err error + for _, kk := range sampleKeySet.Keys { + tr, err = Add(tr, kk, nil) + require.NoError(t, err) + } + require.Equal(t, len(sampleKeySet.Keys), tr.Size()) + + for _, kk := range sampleKeySet.Keys { + tr, err = Add(tr, kk, nil) + require.NoError(t, err) + } + require.Equal(t, len(sampleKeySet.Keys), tr.Size()) + + if d := CheckInvariant(tr); d != nil { + t.Fatalf("reordered trie invariant discrepancy: %v", d) + } +} + +func TestAddRejectsMismatchedKeyLength(t *testing.T) { + tr, err := trieFromKeys[any](sampleKeySet.Keys) + if err != nil { + t.Fatalf("unexpected error during from keys: %v", err) + } + + added, err := tr.Add(testutil.Random(40), nil) + require.ErrorIs(t, err, ErrMismatchedKeyLength) + require.False(t, added) + + if d := CheckInvariant(tr); d != nil { + t.Fatalf("reordered trie invariant discrepancy: %v", d) + } +} + +func TestImmutableAddRejectsMismatchedKeyLength(t *testing.T) { + tr, err := trieFromKeys[any](sampleKeySet.Keys) + if err != nil { + t.Fatalf("unexpected error during from keys: %v", err) + } + + trNext, err := Add(tr, testutil.Random(40), nil) + require.ErrorIs(t, err, ErrMismatchedKeyLength) + + // trie has not been changed + require.Same(t, tr, trNext) + + if d := CheckInvariant(trNext); d != nil { + t.Fatalf("reordered trie invariant discrepancy: %v", d) + } +} + +func TestAddWithData(t *testing.T) { + tr := New[int]() + for i, kk := range sampleKeySet.Keys { + added, err := tr.Add(kk, i) + require.NoError(t, err) + require.True(t, added) + } + require.Equal(t, len(sampleKeySet.Keys), tr.Size()) + + for i, kk := range sampleKeySet.Keys { + found, v := Find(tr, kk) + require.True(t, found) + require.Equal(t, i, v) + } +} + +func TestImmutableAddWithData(t *testing.T) { + tr := New[int]() + var err error + for i, kk := range sampleKeySet.Keys { + tr, err = Add(tr, kk, i) + require.NoError(t, err) + } + require.Equal(t, len(sampleKeySet.Keys), tr.Size()) + + for i, kk := range sampleKeySet.Keys { + found, v := Find(tr, kk) + require.True(t, found) + require.Equal(t, i, v) + } +} + +func TestRemove(t *testing.T) { + tr, err := trieFromKeys[any](sampleKeySet.Keys) + if err != nil { + t.Fatalf("unexpected error during from keys: %v", err) + } + require.Equal(t, len(sampleKeySet.Keys), tr.Size()) + + removed, err := tr.Remove(sampleKeySet.Keys[0]) + require.NoError(t, err) + require.True(t, removed) + require.Equal(t, len(sampleKeySet.Keys)-1, tr.Size()) + + if d := CheckInvariant(tr); d != nil { + t.Fatalf("reordered trie invariant discrepancy: %v", d) + } +} + +func TestImmutableRemove(t *testing.T) { + tr, err := trieFromKeys[any](sampleKeySet.Keys) + if err != nil { + t.Fatalf("unexpected error during from keys: %v", err) + } + require.Equal(t, len(sampleKeySet.Keys), tr.Size()) + + trNext, err := Remove(tr, sampleKeySet.Keys[0]) + require.NoError(t, err) + require.Equal(t, len(sampleKeySet.Keys)-1, trNext.Size()) + + if d := CheckInvariant(tr); d != nil { + t.Fatalf("reordered trie invariant discrepancy: %v", d) + } +} + +func TestRemoveFromEmpty(t *testing.T) { + tr := New[any]() + removed, err := tr.Remove(sampleKeySet.Keys[0]) + require.NoError(t, err) + require.False(t, removed) + require.Equal(t, 0, tr.Size()) + + if d := CheckInvariant(tr); d != nil { + t.Fatalf("reordered trie invariant discrepancy: %v", d) + } +} + +func TestImmutableRemoveFromEmpty(t *testing.T) { + tr := New[any]() + trNext, err := Remove(tr, sampleKeySet.Keys[0]) + require.NoError(t, err) + require.Equal(t, 0, tr.Size()) + + // trie has not been changed + require.Same(t, tr, trNext) +} + +func TestRemoveRejectsMismatchedKeyLength(t *testing.T) { + tr, err := trieFromKeys[any](sampleKeySet.Keys) + if err != nil { + t.Fatalf("unexpected error during from keys: %v", err) + } + + removed, err := tr.Remove(testutil.Random(40)) + require.ErrorIs(t, err, ErrMismatchedKeyLength) + require.False(t, removed) + + if d := CheckInvariant(tr); d != nil { + t.Fatalf("reordered trie invariant discrepancy: %v", d) + } +} + +func TestImmutableRemoveRejectsMismatchedKeyLength(t *testing.T) { + tr, err := trieFromKeys[any](sampleKeySet.Keys) + if err != nil { + t.Fatalf("unexpected error during from keys: %v", err) + } + + trNext, err := Remove(tr, testutil.Random(40)) + require.ErrorIs(t, err, ErrMismatchedKeyLength) + + // trie has not been changed + require.Same(t, tr, trNext) + + if d := CheckInvariant(trNext); d != nil { + t.Fatalf("reordered trie invariant discrepancy: %v", d) + } +} + +func TestRemoveUnknown(t *testing.T) { + tr, err := trieFromKeys[any](sampleKeySet.Keys) + if err != nil { + t.Fatalf("unexpected error during from keys: %v", err) + } + require.Equal(t, len(sampleKeySet.Keys), tr.Size()) + + unknown := newKeyNotInSet(sampleKeySet.Keys[0].BitLen(), sampleKeySet) + + removed, err := tr.Remove(unknown) + require.NoError(t, err) + require.False(t, removed) + require.Equal(t, len(sampleKeySet.Keys), tr.Size()) + + if d := CheckInvariant(tr); d != nil { + t.Fatalf("reordered trie invariant discrepancy: %v", d) + } +} + +func TestImmutableRemoveUnknown(t *testing.T) { + tr, err := trieFromKeys[any](sampleKeySet.Keys) + if err != nil { + t.Fatalf("unexpected error during from keys: %v", err) + } + require.Equal(t, len(sampleKeySet.Keys), tr.Size()) + + unknown := newKeyNotInSet(sampleKeySet.Keys[0].BitLen(), sampleKeySet) + + trNext, err := Remove(tr, unknown) + require.NoError(t, err) + + // trie has not been changed + require.Same(t, tr, trNext) + + if d := CheckInvariant(trNext); d != nil { + t.Fatalf("reordered trie invariant discrepancy: %v", d) + } +} + +func TestEqual(t *testing.T) { + a, err := trieFromKeys[any](sampleKeySet.Keys) + if err != nil { + t.Fatalf("unexpected error during from keys: %v", err) + } + b, err := trieFromKeys[any](sampleKeySet.Keys) + if err != nil { + t.Fatalf("unexpected error during from keys: %v", err) + } + require.True(t, Equal(a, b)) + + sampleKeySet2 := newKeySetOfLength(12, 64) + c, err := trieFromKeys[any](sampleKeySet2.Keys) + if err != nil { + t.Fatalf("unexpected error during from keys: %v", err) + } + require.False(t, Equal(a, c)) +} + +func TestEqualRejectsMismatchedKeyLength(t *testing.T) { + a, err := trieFromKeys[any](sampleKeySet.Keys) + if err != nil { + t.Fatalf("unexpected error during from keys: %v", err) + } + b, err := trieFromKeys[any](sampleKeySet.Keys) + if err != nil { + t.Fatalf("unexpected error during from keys: %v", err) + } + require.True(t, Equal(a, b)) +} + +func TestFindNoData(t *testing.T) { + tr, err := trieFromKeys[any](sampleKeySet.Keys) + if err != nil { + t.Fatalf("unexpected error during from keys: %v", err) + } + + for _, kk := range sampleKeySet.Keys { + found, _ := Find(tr, kk) + require.True(t, found) + } +} + +func TestFindNotFound(t *testing.T) { + tr, err := trieFromKeys[any](sampleKeySet.Keys[1:]) + if err != nil { + t.Fatalf("unexpected error during from keys: %v", err) + } + + found, _ := Find(tr, sampleKeySet.Keys[0]) + require.False(t, found) +} + +func TestFindMismatchedKeyLength(t *testing.T) { + tr, err := trieFromKeys[any](sampleKeySet.Keys) + if err != nil { + t.Fatalf("unexpected error during from keys: %v", err) + } + + found, _ := Find(tr, testutil.Random(40)) + require.False(t, found) +} + +func TestFindWithData(t *testing.T) { + tr := New[int]() + + var err error + for i, kk := range sampleKeySet.Keys { + tr, err = Add(tr, kk, i) + require.NoError(t, err) + } + + for i, kk := range sampleKeySet.Keys { + found, v := Find(tr, kk) + require.True(t, found) + require.Equal(t, i, v) + } +} + +func BenchmarkBuildTrieMutable(b *testing.B) { + b.Run("1000", benchmarkBuildTrieMutable(1000)) + b.Run("10000", benchmarkBuildTrieMutable(10000)) + b.Run("100000", benchmarkBuildTrieMutable(100000)) +} + +func BenchmarkBuildTrieImmutable(b *testing.B) { + b.Run("1000", benchmarkBuildTrieImmutable(1000)) + b.Run("10000", benchmarkBuildTrieImmutable(10000)) + b.Run("100000", benchmarkBuildTrieImmutable(100000)) +} + +func BenchmarkAddMutable(b *testing.B) { + b.Run("1000", benchmarkAddMutable(1000)) + b.Run("10000", benchmarkAddMutable(10000)) + b.Run("100000", benchmarkAddMutable(100000)) +} + +func BenchmarkAddImmutable(b *testing.B) { + b.Run("1000", benchmarkAddImmutable(1000)) + b.Run("10000", benchmarkAddImmutable(10000)) + b.Run("100000", benchmarkAddImmutable(100000)) +} + +func BenchmarkRemoveMutable(b *testing.B) { + b.Run("1000", benchmarkRemoveMutable(1000)) + b.Run("10000", benchmarkRemoveMutable(10000)) + b.Run("100000", benchmarkRemoveMutable(100000)) +} + +func BenchmarkRemoveImmutable(b *testing.B) { + b.Run("1000", benchmarkRemoveImmutable(1000)) + b.Run("10000", benchmarkRemoveImmutable(10000)) + b.Run("100000", benchmarkRemoveImmutable(100000)) +} + +func BenchmarkFindPositive(b *testing.B) { + b.Run("1000", benchmarkFindPositive(1000)) + b.Run("10000", benchmarkFindPositive(10000)) + b.Run("100000", benchmarkFindPositive(100000)) +} + +func BenchmarkFindNegative(b *testing.B) { + b.Run("1000", benchmarkFindNegative(1000)) + b.Run("10000", benchmarkFindNegative(10000)) + b.Run("100000", benchmarkFindNegative(100000)) +} + +func benchmarkBuildTrieMutable(n int) func(b *testing.B) { + return func(b *testing.B) { + keys := make([]key.KadKey, n) + for i := 0; i < n; i++ { + keys[i] = testutil.Random(256) + } + b.ResetTimer() + b.ReportAllocs() + for i := 0; i < b.N; i++ { + tr := New[any]() + for _, kk := range keys { + tr.Add(kk, nil) + } + } + testutil.ReportTimePerItemMetric(b, n, "key") + } +} + +func benchmarkBuildTrieImmutable(n int) func(b *testing.B) { + return func(b *testing.B) { + keys := make([]key.KadKey, n) + for i := 0; i < n; i++ { + keys[i] = testutil.Random(256) + } + b.ResetTimer() + b.ReportAllocs() + for i := 0; i < b.N; i++ { + tr := New[any]() + for _, kk := range keys { + tr, _ = Add(tr, kk, nil) + } + } + testutil.ReportTimePerItemMetric(b, n, "key") + } +} + +func benchmarkAddMutable(n int) func(b *testing.B) { + return func(b *testing.B) { + keys := make([]key.KadKey, n) + for i := 0; i < n; i++ { + keys[i] = testutil.Random(256) + } + tr := New[any]() + for _, kk := range keys { + tr, _ = Add(tr, kk, nil) + } + + // number of additions has to be large enough so that benchmarking takes + // more time than cloning the trie + // see https://github.com/golang/go/issues/27217 + additions := make([]key.KadKey, n/4) + for i := range additions { + additions[i] = testutil.Random(256) + } + b.ResetTimer() + b.ReportAllocs() + for i := 0; i < b.N; i++ { + b.StopTimer() + trclone := tr.Copy() + b.StartTimer() + for _, kk := range additions { + trclone.Add(kk, nil) + } + } + testutil.ReportTimePerItemMetric(b, len(additions), "key") + } +} + +func benchmarkAddImmutable(n int) func(b *testing.B) { + return func(b *testing.B) { + keys := make([]key.KadKey, n) + for i := 0; i < n; i++ { + keys[i] = testutil.Random(256) + } + trBase := New[any]() + for _, kk := range keys { + trBase, _ = Add(trBase, kk, nil) + } + + additions := make([]key.KadKey, n/4) + for i := range additions { + additions[i] = testutil.Random(256) + } + b.ResetTimer() + b.ReportAllocs() + for i := 0; i < b.N; i++ { + tr := trBase + for _, kk := range additions { + tr, _ = Add(tr, kk, nil) + } + } + testutil.ReportTimePerItemMetric(b, len(additions), "key") + } +} + +func benchmarkRemoveMutable(n int) func(b *testing.B) { + return func(b *testing.B) { + keys := make([]key.KadKey, n) + for i := 0; i < n; i++ { + keys[i] = testutil.Random(256) + } + tr := New[any]() + for _, kk := range keys { + tr, _ = Add(tr, kk, nil) + } + + // number of removals has to be large enough so that benchmarking takes + // more time than cloning the trie + // see https://github.com/golang/go/issues/27217 + removals := make([]key.KadKey, n/4) + for i := range removals { + removals[i] = keys[i*4] // every 4th key + } + b.ResetTimer() + b.ReportAllocs() + for i := 0; i < b.N; i++ { + b.StopTimer() + trclone := tr.Copy() + b.StartTimer() + for _, kk := range removals { + trclone.Remove(kk) + } + } + testutil.ReportTimePerItemMetric(b, len(removals), "key") + } +} + +func benchmarkRemoveImmutable(n int) func(b *testing.B) { + return func(b *testing.B) { + keys := make([]key.KadKey, n) + for i := 0; i < n; i++ { + keys[i] = testutil.Random(256) + } + trBase := New[any]() + for _, kk := range keys { + trBase, _ = Add(trBase, kk, nil) + } + + removals := make([]key.KadKey, n/4) + for i := range removals { + removals[i] = keys[i*4] // every 4th key + } + b.ResetTimer() + b.ReportAllocs() + for i := 0; i < b.N; i++ { + tr := trBase + for _, kk := range removals { + tr, _ = Remove(tr, kk) + } + } + testutil.ReportTimePerItemMetric(b, len(removals), "key") + } +} + +func benchmarkFindPositive(n int) func(b *testing.B) { + return func(b *testing.B) { + keys := make([]key.KadKey, n) + for i := 0; i < n; i++ { + keys[i] = testutil.Random(256) + } + tr := New[any]() + for _, kk := range keys { + tr, _ = Add(tr, kk, nil) + } + b.ResetTimer() + b.ReportAllocs() + for i := 0; i < b.N; i++ { + Find(tr, keys[i%len(keys)]) + } + } +} + +func benchmarkFindNegative(n int) func(b *testing.B) { + return func(b *testing.B) { + keys := make([]key.KadKey, n) + for i := 0; i < n; i++ { + keys[i] = testutil.Random(256) + } + tr := New[any]() + for _, kk := range keys { + tr, _ = Add(tr, kk, nil) + } + unknown := make([]key.KadKey, n) + for i := 0; i < n; i++ { + kk := testutil.Random(256) + if found, _ := Find(tr, kk); found { + continue + } + unknown[i] = kk + } + + b.ResetTimer() + b.ReportAllocs() + for i := 0; i < b.N; i++ { + Find(tr, unknown[i%len(unknown)]) + } + } +} + +type keySet struct { + Keys []key.KadKey +} + +var sampleKeySet = newKeySetOfLength(12, 64) + +func newKeySetList(n int) []*keySet { + s := make([]*keySet, n) + for i := range s { + s[i] = newKeySetOfLength(31, 32) + } + return s +} + +func newKeySetOfLength(n int, bits int) *keySet { + set := make([]key.KadKey, 0, n) + seen := make(map[string]bool) + for len(set) < n { + kk := testutil.Random(bits) + if seen[kk.String()] { + continue + } + seen[kk.String()] = true + set = append(set, kk) + } + return &keySet{ + Keys: set, + } +} + +func newKeyNotInSet(bits int, ks *keySet) key.KadKey { + seen := make(map[string]bool) + for i := range ks.Keys { + seen[ks.Keys[i].String()] = true + } + + kk := testutil.Random(bits) + for seen[kk.String()] { + kk = testutil.Random(bits) + } + + return kk +} + +type InvariantDiscrepancy struct { + Reason string + PathToDiscrepancy string + KeyAtDiscrepancy string +} + +// CheckInvariant panics of the trie does not meet its invariant. +func CheckInvariant[T any](tr *Trie[T]) *InvariantDiscrepancy { + return checkInvariant(tr, 0, nil) +} + +func checkInvariant[T any](tr *Trie[T], depth int, pathSoFar *triePath) *InvariantDiscrepancy { + switch { + case tr.IsEmptyLeaf(): + return nil + case tr.IsNonEmptyLeaf(): + if !pathSoFar.matchesKey(tr.key) { + return &InvariantDiscrepancy{ + Reason: "key found at invalid location in trie", + PathToDiscrepancy: pathSoFar.BitString(), + KeyAtDiscrepancy: tr.key.BitString(), + } + } + return nil + default: + if !tr.HasKey() { + b0, b1 := tr.branch[0], tr.branch[1] + if d0 := checkInvariant(b0, depth+1, pathSoFar.Push(0)); d0 != nil { + return d0 + } + if d1 := checkInvariant(b1, depth+1, pathSoFar.Push(1)); d1 != nil { + return d1 + } + switch { + case b0.IsEmptyLeaf() && b1.IsEmptyLeaf(): + return &InvariantDiscrepancy{ + Reason: "intermediate node with two empty leaves", + PathToDiscrepancy: pathSoFar.BitString(), + KeyAtDiscrepancy: "none", + } + case b0.IsEmptyLeaf() && b1.IsNonEmptyLeaf(): + return &InvariantDiscrepancy{ + Reason: "intermediate node with one empty leaf/0", + PathToDiscrepancy: pathSoFar.BitString(), + KeyAtDiscrepancy: "none", + } + case b0.IsNonEmptyLeaf() && b1.IsEmptyLeaf(): + return &InvariantDiscrepancy{ + Reason: "intermediate node with one empty leaf/1", + PathToDiscrepancy: pathSoFar.BitString(), + KeyAtDiscrepancy: "none", + } + default: + return nil + } + } else { + return &InvariantDiscrepancy{ + Reason: "intermediate node with a key", + PathToDiscrepancy: pathSoFar.BitString(), + KeyAtDiscrepancy: tr.key.BitString(), + } + } + } +} + +type triePath struct { + parent *triePath + bit byte +} + +func (p *triePath) Push(bit byte) *triePath { + return &triePath{parent: p, bit: bit} +} + +func (p *triePath) RootPath() []byte { + if p == nil { + return nil + } else { + return append(p.parent.RootPath(), p.bit) + } +} + +func (p *triePath) matchesKey(k key.KadKey) bool { + ok, _ := p.walk(k, 0) + return ok +} + +func (p *triePath) walk(k key.KadKey, depthToLeaf int) (ok bool, depthToRoot int) { + if p == nil { + return true, 0 + } else { + parOk, parDepthToRoot := p.parent.walk(k, depthToLeaf+1) + return k.BitAt(parDepthToRoot) == int(p.bit) && parOk, parDepthToRoot + 1 + } +} + +func (p *triePath) BitString() string { + return p.bitString(0) +} + +func (p *triePath) bitString(depthToLeaf int) string { + if p == nil { + return "" + } else { + switch { + case p.bit == 0: + return p.parent.bitString(depthToLeaf+1) + "0" + case p.bit == 1: + return p.parent.bitString(depthToLeaf+1) + "1" + default: + panic("bit digit > 1") + } + } +} diff --git a/routing/triert/config.go b/routing/triert/config.go new file mode 100644 index 0000000..5c02128 --- /dev/null +++ b/routing/triert/config.go @@ -0,0 +1,15 @@ +package triert + +// Config holds configuration options for a TrieRT. +type Config struct { + // KeyFilter defines the filter that is applied before a key is added to the table. + // If nil, no filter is applied. + KeyFilter KeyFilterFunc +} + +// DefaultConfig returns a default configuration for a TrieRT. +func DefaultConfig() *Config { + return &Config{ + KeyFilter: nil, + } +} diff --git a/routing/triert/doc.go b/routing/triert/doc.go new file mode 100644 index 0000000..ca4ac87 --- /dev/null +++ b/routing/triert/doc.go @@ -0,0 +1,2 @@ +// Package triert provides a routing table implemented using a XOR trie. +package triert diff --git a/routing/triert/filter.go b/routing/triert/filter.go new file mode 100644 index 0000000..bcd7310 --- /dev/null +++ b/routing/triert/filter.go @@ -0,0 +1,13 @@ +package triert + +import "github.com/plprobelab/go-kademlia/key" + +// KeyFilterFunc is a function that is applied before a key is added to the table. +// Return false to prevent the key from being added. +type KeyFilterFunc func(rt *TrieRT, kk key.KadKey) bool + +// BucketLimit20 is a filter function that limits the occupancy of buckets in the table to 20 keys. +func BucketLimit20(rt *TrieRT, kk key.KadKey) bool { + cpl := rt.Cpl(kk) + return rt.CplSize(cpl) < 20 +} diff --git a/routing/triert/filter_test.go b/routing/triert/filter_test.go new file mode 100644 index 0000000..47b0d96 --- /dev/null +++ b/routing/triert/filter_test.go @@ -0,0 +1,55 @@ +package triert + +import ( + "context" + "fmt" + "testing" + + "github.com/plprobelab/go-kademlia/internal/testutil" + "github.com/plprobelab/go-kademlia/network/address" + "github.com/stretchr/testify/require" +) + +func TestBucketLimit20(t *testing.T) { + ctx := context.Background() + + cfg := DefaultConfig() + cfg.KeyFilter = BucketLimit20 + rt, err := New(key0, cfg) + require.NoError(t, err) + + nodes := make([]address.NodeID, 21) + for i := range nodes { + kk := testutil.RandomWithPrefix("000100", 256) + nodes[i] = NewNode(fmt.Sprintf("QmPeer%d", i), kk) + } + + // Add 20 peers with cpl 3 + for i := 0; i < 20; i++ { + success, err := rt.AddPeer(ctx, nodes[i]) + require.NoError(t, err) + require.True(t, success) + } + + // cannot add 21st + success, err := rt.AddPeer(ctx, nodes[20]) + require.NoError(t, err) + require.False(t, success) + + // add peer with different cpl + kk := testutil.RandomWithPrefix("0000100", 256) + node22 := NewNode("QmPeer22", kk) + success, err = rt.AddPeer(ctx, node22) + require.NoError(t, err) + require.True(t, success) + + // make space for another cpl 3 key + success, err = rt.RemoveKey(ctx, nodes[0].Key()) + require.NoError(t, err) + require.True(t, success) + + // now can add cpl 3 key + success, err = rt.AddPeer(ctx, nodes[20]) + require.NoError(t, err) + require.True(t, success) +} diff --git a/routing/triert/table.go b/routing/triert/table.go new file mode 100644 index 0000000..eae263e --- /dev/null +++ b/routing/triert/table.go @@ -0,0 +1,180 @@ +package triert + +import ( + "context" + "fmt" + + "github.com/plprobelab/go-kademlia/key" + "github.com/plprobelab/go-kademlia/key/trie" + "github.com/plprobelab/go-kademlia/network/address" +) + +// TrieRT is a routing table backed by a XOR Trie which offers good scalablity and performance +// for large networks. +type TrieRT struct { + self key.KadKey + keyFilter KeyFilterFunc + + keys *nodeTrie +} + +type nodeTrie = trie.Trie[address.NodeID] + +// New creates a new TrieRT using the supplied key as the local node's Kademlia key. +// If cfg is nil, the default config is used. +func New(self key.KadKey, cfg *Config) (*TrieRT, error) { + rt := &TrieRT{ + self: self, + keys: &nodeTrie{}, + } + if err := rt.apply(cfg); err != nil { + return nil, fmt.Errorf("apply config: %w", err) + } + return rt, nil +} + +func (rt *TrieRT) apply(cfg *Config) error { + if cfg == nil { + cfg = DefaultConfig() + } + + rt.keyFilter = cfg.KeyFilter + + return nil +} + +// Self returns the local node's Kademlia key. +func (rt *TrieRT) Self() key.KadKey { + return rt.self +} + +// AddPeer tries to add a peer to the routing table. +func (rt *TrieRT) AddPeer(ctx context.Context, node address.NodeID) (bool, error) { + kk := node.Key() + if kk.Size() != rt.self.Size() { + return false, trie.ErrMismatchedKeyLength + } + + if rt.keyFilter != nil && !rt.keyFilter(rt, kk) { + return false, nil + } + + return rt.keys.Add(kk, node) +} + +// RemoveKey tries to remove a peer identified by its Kademlia key from the +// routing table. It returns true if the key was found to be present in the table and was removed. +func (rt *TrieRT) RemoveKey(ctx context.Context, kk key.KadKey) (bool, error) { + if kk.Size() != rt.self.Size() { + return false, trie.ErrMismatchedKeyLength + } + return rt.keys.Remove(kk) +} + +// NearestPeers returns the n closest peers to a given key. +func (rt *TrieRT) NearestPeers(ctx context.Context, kk key.KadKey, n int) ([]address.NodeID, error) { + if kk.Size() != rt.self.Size() { + return nil, trie.ErrMismatchedKeyLength + } + + closestEntries := closestAtDepth(kk, rt.keys, 0, n) + if len(closestEntries) == 0 { + return []address.NodeID{}, nil + } + + nodes := make([]address.NodeID, 0, len(closestEntries)) + for _, c := range closestEntries { + nodes = append(nodes, c.data) + } + + return nodes, nil +} + +type entry struct { + key key.KadKey + data address.NodeID +} + +func closestAtDepth(kk key.KadKey, t *nodeTrie, depth int, n int) []entry { + if t.IsLeaf() { + if t.HasKey() { + // We've found a leaf + return []entry{ + {key: t.Key(), data: t.Data()}, + } + } + // We've found an empty node? + return nil + } + + if depth > kk.BitLen() { + return nil + } + + // Find the closest direction. + dir := kk.BitAt(depth) + // Add peers from the closest direction first + found := closestAtDepth(kk, t.Branch(dir), depth+1, n) + if len(found) == n { + return found + } + // Didn't find enough peers in the closest direction, try the other direction. + return append(found, closestAtDepth(kk, t.Branch(1-dir), depth+1, n-len(found))...) +} + +func (rt *TrieRT) Find(ctx context.Context, kk key.KadKey) (address.NodeID, error) { + if kk.Size() != rt.self.Size() { + return nil, trie.ErrMismatchedKeyLength + } + + found, node := trie.Find(rt.keys, kk) + if found { + return node, nil + } + + return nil, nil +} + +// Size returns the number of peers contained in the table. +func (rt *TrieRT) Size() int { + return rt.keys.Size() +} + +// Cpl returns the longest common prefix length the supplied key shares with the table's key. +func (rt *TrieRT) Cpl(kk key.KadKey) int { + return rt.self.CommonPrefixLength(kk) +} + +// CplSize returns the number of peers in the table whose longest common prefix with the table's key is of length cpl. +func (rt *TrieRT) CplSize(cpl int) int { + n, err := countCpl(rt.keys, rt.self, cpl, 0) + if err != nil { + return 0 + } + return n +} + +func countCpl(t *nodeTrie, kk key.KadKey, cpl int, depth int) (int, error) { + // special cases for very small tables where keys may be placed higher in the trie due to low population + if t.IsLeaf() { + if t.HasKey() { + return 0, nil + } + keyCpl := kk.CommonPrefixLength(key.KadKey(t.Key())) + if keyCpl == cpl { + return 1, nil + } + return 0, nil + } + + if depth > kk.BitLen() { + return 0, nil + } + + if depth == cpl { + // return the number of entries that do not share the next bit with kk + return t.Branch(1 - kk.BitAt(depth)).Size(), nil + } + + return countCpl(t.Branch(kk.BitAt(depth)), kk, cpl, depth+1) +} diff --git a/routing/triert/table_test.go b/routing/triert/table_test.go new file mode 100644 index 0000000..06e95b6 --- /dev/null +++ b/routing/triert/table_test.go @@ -0,0 +1,555 @@ +package triert + +import ( + "context" + "math/rand" + "testing" + + "github.com/plprobelab/go-kademlia/internal/testutil" + "github.com/plprobelab/go-kademlia/key" + "github.com/plprobelab/go-kademlia/key/trie" + "github.com/plprobelab/go-kademlia/network/address" + "github.com/plprobelab/go-kademlia/network/address/kadid" + "github.com/stretchr/testify/require" +) + +var ( + key0 = key.KadKey(make([]byte, 32)) // 000000...000 + + key1 = testutil.RandomWithPrefix("010000", 256) + key2 = testutil.RandomWithPrefix("100000", 256) + key3 = testutil.RandomWithPrefix("110000", 256) + key4 = testutil.RandomWithPrefix("111000", 256) + key5 = testutil.RandomWithPrefix("011000", 256) + key6 = testutil.RandomWithPrefix("011100", 256) + key7 = testutil.RandomWithPrefix("000110", 256) + key8 = testutil.RandomWithPrefix("000101", 256) + key9 = testutil.RandomWithPrefix("000100", 256) + key10 = testutil.RandomWithPrefix("001000", 256) + key11 = testutil.RandomWithPrefix("001100", 256) + + node1 = NewNode("QmPeer1", key1) + node2 = NewNode("QmPeer2", key2) + node3 = NewNode("QmPeer3", key3) + node4 = NewNode("QmPeer4", key4) + node5 = NewNode("QmPeer5", key5) + node6 = NewNode("QmPeer6", key6) + node7 = NewNode("QmPeer7", key7) + node8 = NewNode("QmPeer8", key8) + node9 = NewNode("QmPeer9", key9) + node10 = NewNode("QmPeer10", key10) + node11 = NewNode("QmPeer11", key11) +) + +func TestAddPeer(t *testing.T) { + t.Run("one", func(t *testing.T) { + rt, err := New(key0, nil) + require.NoError(t, err) + success, err := rt.AddPeer(context.Background(), node1) + require.NoError(t, err) + require.True(t, success) + require.Equal(t, 1, rt.Size()) + }) + + t.Run("ignore duplicate", func(t *testing.T) { + rt, err := New(key0, nil) + require.NoError(t, err) + success, err := rt.AddPeer(context.Background(), node1) + require.NoError(t, err) + require.True(t, success) + require.Equal(t, 1, rt.Size()) + + success, err = rt.AddPeer(context.Background(), node1) + require.NoError(t, err) + require.False(t, success) + require.Equal(t, 1, rt.Size()) + }) + + t.Run("many", func(t *testing.T) { + rt, err := New(key0, nil) + require.NoError(t, err) + success, err := rt.AddPeer(context.Background(), node1) + require.NoError(t, err) + require.True(t, success) + + success, err = rt.AddPeer(context.Background(), node2) + require.NoError(t, err) + require.True(t, success) + + success, err = rt.AddPeer(context.Background(), node3) + require.NoError(t, err) + require.True(t, success) + + success, err = rt.AddPeer(context.Background(), node4) + require.NoError(t, err) + require.True(t, success) + + success, err = rt.AddPeer(context.Background(), node5) + require.NoError(t, err) + require.True(t, success) + + success, err = rt.AddPeer(context.Background(), node6) + require.NoError(t, err) + require.True(t, success) + + success, err = rt.AddPeer(context.Background(), node7) + require.NoError(t, err) + require.True(t, success) + + success, err = rt.AddPeer(context.Background(), node8) + require.NoError(t, err) + require.True(t, success) + + require.Equal(t, 8, rt.Size()) + }) +} + +func TestRemovePeer(t *testing.T) { + rt, err := New(key0, nil) + require.NoError(t, err) + success, err := rt.AddPeer(context.Background(), node1) + require.NoError(t, err) + require.True(t, success) + + t.Run("unknown peer", func(t *testing.T) { + success, err := rt.RemoveKey(context.Background(), key2) + require.NoError(t, err) + require.False(t, success) + }) + + t.Run("known peer", func(t *testing.T) { + success, err := rt.RemoveKey(context.Background(), key1) + require.NoError(t, err) + require.True(t, success) + }) +} + +func TestFindPeer(t *testing.T) { + t.Run("known peer", func(t *testing.T) { + rt, err := New(key0, nil) + require.NoError(t, err) + success, err := rt.AddPeer(context.Background(), node1) + require.NoError(t, err) + require.True(t, success) + + want := node1 + got, err := rt.Find(context.Background(), key1) + require.NoError(t, err) + require.Equal(t, want, got) + }) + + t.Run("unknown peer", func(t *testing.T) { + rt, err := New(key0, nil) + require.NoError(t, err) + got, err := rt.Find(context.Background(), key2) + require.NoError(t, err) + require.Nil(t, got) + }) + + t.Run("removed peer", func(t *testing.T) { + rt, err := New(key0, nil) + require.NoError(t, err) + success, err := rt.AddPeer(context.Background(), node1) + require.NoError(t, err) + require.True(t, success) + + want := node1 + got, err := rt.Find(context.Background(), key1) + require.NoError(t, err) + require.Equal(t, want, got) + + success, err = rt.RemoveKey(context.Background(), key1) + require.NoError(t, err) + require.True(t, success) + + got, err = rt.Find(context.Background(), key1) + require.NoError(t, err) + require.Nil(t, got) + }) +} + +func TestNearestPeers(t *testing.T) { + ctx := context.Background() + + rt, err := New(key0, nil) + require.NoError(t, err) + rt.AddPeer(ctx, node1) + rt.AddPeer(ctx, node2) + rt.AddPeer(ctx, node3) + rt.AddPeer(ctx, node4) + rt.AddPeer(ctx, node5) + rt.AddPeer(ctx, node6) + rt.AddPeer(ctx, node7) + rt.AddPeer(ctx, node8) + rt.AddPeer(ctx, node9) + rt.AddPeer(ctx, node10) + rt.AddPeer(ctx, node11) + + // find the 5 nearest peers to key0 + peers, err := rt.NearestPeers(ctx, key0, 5) + require.NoError(t, err) + require.Equal(t, 5, len(peers)) + + expectedOrder := []address.NodeID{node9, node8, node7, node10, node11} + require.Equal(t, expectedOrder, peers) + + peers, err = rt.NearestPeers(ctx, node11.Key(), 2) + require.NoError(t, err) + require.Equal(t, 2, len(peers)) +} + +func TestInvalidKeys(t *testing.T) { + ctx := context.Background() + incompatKey := key.KadKey(make([]byte, 4)) // key is shorter (4 bytes only) + incompatNode := NewNode("inv", incompatKey) + + rt, err := New(key0, nil) + require.NoError(t, err) + + t.Run("add peer", func(t *testing.T) { + success, err := rt.AddPeer(ctx, incompatNode) + require.ErrorIs(t, err, trie.ErrMismatchedKeyLength) + require.False(t, success) + }) + + t.Run("remove key", func(t *testing.T) { + success, err := rt.RemoveKey(ctx, incompatKey) + require.ErrorIs(t, err, trie.ErrMismatchedKeyLength) + require.False(t, success) + }) + + t.Run("find", func(t *testing.T) { + nodeID, err := rt.Find(ctx, incompatKey) + require.ErrorIs(t, err, trie.ErrMismatchedKeyLength) + require.Nil(t, nodeID) + }) + + t.Run("nearest peers", func(t *testing.T) { + nodeIDs, err := rt.NearestPeers(ctx, incompatKey, 2) + require.ErrorIs(t, err, trie.ErrMismatchedKeyLength) + require.Nil(t, nodeIDs) + }) +} + +func TestCplSize(t *testing.T) { + t.Run("empty", func(t *testing.T) { + rt, err := New(key0, nil) + require.NoError(t, err) + require.Equal(t, 0, rt.Size()) + require.Equal(t, 0, rt.CplSize(1)) + require.Equal(t, 0, rt.CplSize(2)) + require.Equal(t, 0, rt.CplSize(3)) + require.Equal(t, 0, rt.CplSize(4)) + }) + + t.Run("cpl 1", func(t *testing.T) { + ctx := context.Background() + rt, err := New(key0, nil) + require.NoError(t, err) + + success, err := rt.AddPeer(ctx, NewNode("cpl1a", testutil.RandomWithPrefix("01", 256))) + require.NoError(t, err) + require.True(t, success) + success, err = rt.AddPeer(ctx, NewNode("cpl1b", testutil.RandomWithPrefix("01", 256))) + require.NoError(t, err) + require.True(t, success) + require.Equal(t, 2, rt.Size()) + require.Equal(t, 2, rt.CplSize(1)) + + require.Equal(t, 0, rt.CplSize(2)) + require.Equal(t, 0, rt.CplSize(3)) + }) + + t.Run("cpl 2", func(t *testing.T) { + ctx := context.Background() + rt, err := New(key0, nil) + require.NoError(t, err) + + success, err := rt.AddPeer(ctx, NewNode("cpl2a", testutil.RandomWithPrefix("001", 256))) + require.NoError(t, err) + require.True(t, success) + success, err = rt.AddPeer(ctx, NewNode("cpl2b", testutil.RandomWithPrefix("001", 256))) + require.NoError(t, err) + require.True(t, success) + + require.Equal(t, 2, rt.Size()) + require.Equal(t, 2, rt.CplSize(2)) + + require.Equal(t, 0, rt.CplSize(1)) + require.Equal(t, 0, rt.CplSize(3)) + }) + + t.Run("cpl 3", func(t *testing.T) { + ctx := context.Background() + rt, err := New(key0, nil) + require.NoError(t, err) + + success, err := rt.AddPeer(ctx, NewNode("cpl3a", testutil.RandomWithPrefix("0001", 256))) + require.NoError(t, err) + require.True(t, success) + success, err = rt.AddPeer(ctx, NewNode("cpl3b", testutil.RandomWithPrefix("0001", 256))) + require.NoError(t, err) + require.True(t, success) + success, err = rt.AddPeer(ctx, NewNode("cpl3c", testutil.RandomWithPrefix("0001", 256))) + require.NoError(t, err) + require.True(t, success) + success, err = rt.AddPeer(ctx, NewNode("cpl3d", testutil.RandomWithPrefix("0001", 256))) + require.NoError(t, err) + require.True(t, success) + + require.Equal(t, 4, rt.Size()) + require.Equal(t, 4, rt.CplSize(3)) + + require.Equal(t, 0, rt.CplSize(1)) + require.Equal(t, 0, rt.CplSize(2)) + }) + + t.Run("cpl mixed", func(t *testing.T) { + ctx := context.Background() + rt, err := New(key0, nil) + require.NoError(t, err) + + success, err := rt.AddPeer(ctx, NewNode("cpl1a", testutil.RandomWithPrefix("01", 256))) + require.NoError(t, err) + require.True(t, success) + success, err = rt.AddPeer(ctx, NewNode("cpl1b", testutil.RandomWithPrefix("01", 256))) + require.NoError(t, err) + require.True(t, success) + + success, err = rt.AddPeer(ctx, NewNode("cpl2a", testutil.RandomWithPrefix("001", 256))) + require.NoError(t, err) + require.True(t, success) + success, err = rt.AddPeer(ctx, NewNode("cpl2b", testutil.RandomWithPrefix("001", 256))) + require.NoError(t, err) + require.True(t, success) + + success, err = rt.AddPeer(ctx, NewNode("cpl3a", testutil.RandomWithPrefix("0001", 256))) + require.NoError(t, err) + require.True(t, success) + success, err = rt.AddPeer(ctx, NewNode("cpl3b", testutil.RandomWithPrefix("0001", 256))) + require.NoError(t, err) + require.True(t, success) + success, err = rt.AddPeer(ctx, NewNode("cpl3c", testutil.RandomWithPrefix("0001", 256))) + require.NoError(t, err) + require.True(t, success) + success, err = rt.AddPeer(ctx, NewNode("cpl3d", testutil.RandomWithPrefix("0001", 256))) + require.NoError(t, err) + require.True(t, success) + + require.Equal(t, 8, rt.Size()) + require.Equal(t, 2, rt.CplSize(1)) + require.Equal(t, 2, rt.CplSize(2)) + require.Equal(t, 4, rt.CplSize(3)) + }) +} + +func TestKeyFilter(t *testing.T) { + ctx := context.Background() + cfg := DefaultConfig() + cfg.KeyFilter = func(rt *TrieRT, kk key.KadKey) bool { + return !kk.Equal(key2) // don't allow key2 to be added + } + rt, err := New(key0, cfg) + require.NoError(t, err) + + // can't add key2 + success, err := rt.AddPeer(ctx, node2) + require.NoError(t, err) + require.False(t, success) + + got, err := rt.Find(ctx, key2) + require.NoError(t, err) + require.Nil(t, got) + + // can add other key + success, err = rt.AddPeer(ctx, node1) + require.NoError(t, err) + require.True(t, success) + + want := node1 + got, err = rt.Find(ctx, key1) + require.NoError(t, err) + require.Equal(t, want, got) +} + +func BenchmarkBuildTable(b *testing.B) { + b.Run("1000", benchmarkBuildTable(1000)) + b.Run("10000", benchmarkBuildTable(10000)) + b.Run("100000", benchmarkBuildTable(100000)) +} + +func BenchmarkFindPositive(b *testing.B) { + b.Run("1000", benchmarkFindPositive(1000)) + b.Run("10000", benchmarkFindPositive(10000)) + b.Run("100000", benchmarkFindPositive(100000)) +} + +func BenchmarkFindNegative(b *testing.B) { + b.Run("1000", benchmarkFindNegative(1000)) + b.Run("10000", benchmarkFindNegative(10000)) + b.Run("100000", benchmarkFindNegative(100000)) +} + +func BenchmarkNearestPeers(b *testing.B) { + b.Run("1000", benchmarkNearestPeers(1000)) + b.Run("10000", benchmarkNearestPeers(10000)) + b.Run("100000", benchmarkNearestPeers(100000)) +} + +func BenchmarkChurn(b *testing.B) { + b.Run("1000", benchmarkChurn(1000)) + b.Run("10000", benchmarkChurn(10000)) + b.Run("100000", benchmarkChurn(100000)) +} + +func benchmarkBuildTable(n int) func(b *testing.B) { + return func(b *testing.B) { + nodes := make([]address.NodeID, n) + for i := 0; i < n; i++ { + nodes[i] = kadid.NewKadID(testutil.Random(256)) + } + b.ResetTimer() + b.ReportAllocs() + for i := 0; i < b.N; i++ { + rt, err := New(key0, nil) + if err != nil { + b.Fatalf("unexpected error creating table: %v", err) + } + for _, node := range nodes { + rt.AddPeer(context.Background(), node) + } + } + testutil.ReportTimePerItemMetric(b, len(nodes), "node") + } +} + +func benchmarkFindPositive(n int) func(b *testing.B) { + return func(b *testing.B) { + keys := make([]key.KadKey, n) + for i := 0; i < n; i++ { + keys[i] = testutil.Random(256) + } + rt, err := New(key0, nil) + if err != nil { + b.Fatalf("unexpected error creating table: %v", err) + } + for _, kk := range keys { + rt.AddPeer(context.Background(), kadid.NewKadID(kk)) + } + b.ResetTimer() + b.ReportAllocs() + for i := 0; i < b.N; i++ { + rt.Find(context.Background(), keys[i%len(keys)]) + } + } +} + +func benchmarkFindNegative(n int) func(b *testing.B) { + return func(b *testing.B) { + keys := make([]key.KadKey, n) + for i := 0; i < n; i++ { + keys[i] = testutil.Random(256) + } + rt, err := New(key0, nil) + if err != nil { + b.Fatalf("unexpected error creating table: %v", err) + } + for _, kk := range keys { + rt.AddPeer(context.Background(), kadid.NewKadID(kk)) + } + + unknown := make([]key.KadKey, n) + for i := 0; i < n; i++ { + kk := testutil.Random(256) + if found, _ := rt.Find(context.Background(), kk); found != nil { + continue + } + unknown[i] = kk + } + + b.ResetTimer() + b.ReportAllocs() + for i := 0; i < b.N; i++ { + rt.Find(context.Background(), unknown[i%len(unknown)]) + } + } +} + +func benchmarkNearestPeers(n int) func(b *testing.B) { + return func(b *testing.B) { + keys := make([]key.KadKey, n) + for i := 0; i < n; i++ { + keys[i] = testutil.Random(256) + } + rt, err := New(key0, nil) + if err != nil { + b.Fatalf("unexpected error creating table: %v", err) + } + for _, kk := range keys { + rt.AddPeer(context.Background(), kadid.NewKadID(kk)) + } + b.ResetTimer() + b.ReportAllocs() + for i := 0; i < b.N; i++ { + rt.NearestPeers(context.Background(), testutil.Random(256), 20) + } + } +} + +func benchmarkChurn(n int) func(b *testing.B) { + return func(b *testing.B) { + universe := make([]address.NodeID, n) + for i := 0; i < n; i++ { + universe[i] = kadid.NewKadID(testutil.Random(256)) + } + rt, err := New(key0, nil) + if err != nil { + b.Fatalf("unexpected error creating table: %v", err) + } + // Add a portion of the universe to the routing table + for i := 0; i < len(universe)/4; i++ { + rt.AddPeer(context.Background(), universe[i]) + } + rand.Shuffle(len(universe), func(i, j int) { universe[i], universe[j] = universe[j], universe[i] }) + + b.ResetTimer() + b.ReportAllocs() + for i := 0; i < b.N; i++ { + node := universe[i%len(universe)] + found, _ := rt.Find(context.Background(), node.Key()) + if found == nil { + // add new peer + rt.AddPeer(context.Background(), universe[i%len(universe)]) + } else { + // remove it + rt.RemoveKey(context.Background(), node.Key()) + } + } + } +} + +var _ address.NodeID = (*node)(nil) + +type node struct { + id string + key key.KadKey +} + +func NewNode(id string, k key.KadKey) *node { + return &node{ + id: id, + key: k, + } +} + +func (n node) String() string { + return n.id +} + +func (n node) Key() key.KadKey { + return n.key +} + +func (n node) NodeID() address.NodeID { + return &n +}