From f64cf45df534cdf265ac2903f1c9e5d1b66fcad0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1?= <47506558+MegaRedHand@users.noreply.github.com> Date: Thu, 27 Jul 2023 15:48:38 -0300 Subject: [PATCH 01/43] Add example calling Go functions from Elixir --- .gitignore | 9 +++++++++ Makefile | 25 +++++++++++++++++++++++-- lib/libp2p.ex | 37 +++++++++++++++++++++++++++++++++++++ libp2p/go.mod | 3 +++ libp2p/libp2p.c | 25 +++++++++++++++++++++++++ libp2p/main.go | 12 ++++++++++++ test/libp2p_test.exs | 12 ++++++++++++ 7 files changed, 121 insertions(+), 2 deletions(-) create mode 100644 lib/libp2p.ex create mode 100644 libp2p/go.mod create mode 100644 libp2p/libp2p.c create mode 100644 libp2p/main.go create mode 100644 test/libp2p_test.exs diff --git a/.gitignore b/.gitignore index dd2cb66e7..6d6201dff 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,12 @@ lambda_ethereum_consensus-*.tar *.beam /config/*.secret.exs .elixir_ls/ + +# Compiled artifacts. +*.o +*.a +*.h +*.so + +# VSCode configuration dir. +.vscode/ diff --git a/Makefile b/Makefile index 32f899108..ea61674c8 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,25 @@ -.PHONY: iex deps test +.PHONY: iex deps test clean compile + +BREW_PREFIX := $(shell brew --prefix) +ERLANG_INCLUDES = $(BREW_PREFIX)/Cellar/erlang/26.0.2/lib/erlang/usr/include/ + +GO_SOURCES = libp2p/main.go +GO_ARCHIVES := $(patsubst %.go,%.a,$(GO_SOURCES)) +GO_HEADERS := $(patsubst %.go,%.h,$(GO_SOURCES)) + + +%.a: %.go + go build -buildmode=c-archive -o $@ $< + +libp2p.so: libp2p/libp2p.c $(GO_ARCHIVES) + gcc -Wall -Werror -dynamiclib -undefined dynamic_lookup -I $(ERLANG_INCLUDES) -o libp2p.so \ + libp2p/libp2p.c $(GO_ARCHIVES) + +clean: + -rm $(GO_ARCHIVES) $(GO_HEADERS) libp2p.so + +# Compile C and Go artifacts. +compile: libp2p.so # Run an interactive terminal with the main supervisor setup. iex: @@ -9,5 +30,5 @@ deps: mix deps.get # Run tests -test: +test: compile mix test diff --git a/lib/libp2p.ex b/lib/libp2p.ex new file mode 100644 index 000000000..cb14ce7e9 --- /dev/null +++ b/lib/libp2p.ex @@ -0,0 +1,37 @@ +defmodule Libp2p do + @moduledoc """ + Documentation for `Libp2p`. + """ + + @on_load :load_nifs + + def load_nifs do + :erlang.load_nif(~c"./libp2p", 0) + end + + @doc """ + Hello world. + + ## Examples + + iex> Libp2p.hello() + :world + + """ + def hello do + raise "NIF hello not implemented" + end + + @doc """ + Hello world. + + ## Examples + + iex> Libp2p.my_function(2, 3) + 8 + + """ + def my_function(_a, _b) do + raise "NIF my_function not implemented" + end +end diff --git a/libp2p/go.mod b/libp2p/go.mod new file mode 100644 index 000000000..b070389db --- /dev/null +++ b/libp2p/go.mod @@ -0,0 +1,3 @@ +module lambdaclass.com/libp2p + +go 1.20 diff --git a/libp2p/libp2p.c b/libp2p/libp2p.c new file mode 100644 index 000000000..dda9e708d --- /dev/null +++ b/libp2p/libp2p.c @@ -0,0 +1,25 @@ +#include + +extern int MyFunction(int a, int b); + +static ERL_NIF_TERM hello(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) +{ + return enif_make_atom(env, "world"); +} + +static ERL_NIF_TERM my_function(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) +{ + int a, b; + enif_get_int(env, argv[0], &a); + enif_get_int(env, argv[1], &b); + + int result = MyFunction(a, b); + + return enif_make_int(env, result); +} + +static ErlNifFunc nif_funcs[] = { + {"hello", 0, hello}, + {"my_function", 2, my_function}}; + +ERL_NIF_INIT(Elixir.Libp2p, nif_funcs, NULL, NULL, NULL, NULL) diff --git a/libp2p/main.go b/libp2p/main.go new file mode 100644 index 000000000..a2ff0c907 --- /dev/null +++ b/libp2p/main.go @@ -0,0 +1,12 @@ +// NOTE: the package **must** be named main +package main + +import "C" + +//export MyFunction +func MyFunction(a, b int) int { + return a + 2*b +} + +// NOTE: this is needed to build it as an archive (.a) +func main() {} diff --git a/test/libp2p_test.exs b/test/libp2p_test.exs new file mode 100644 index 000000000..7aa971e0e --- /dev/null +++ b/test/libp2p_test.exs @@ -0,0 +1,12 @@ +defmodule Libp2pTest do + use ExUnit.Case + doctest Libp2p + + test "greets the world" do + assert Libp2p.hello() == :world + end + + test "my_function is a + 2 * b" do + assert Libp2p.my_function(5, 124) == 5 + 2 * 124 + end +end From 00fd683a2d768b656c4f6e2f98cb1c733b6e7a73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1?= <47506558+MegaRedHand@users.noreply.github.com> Date: Thu, 27 Jul 2023 17:20:28 -0300 Subject: [PATCH 02/43] Add simple libp2p.New example --- Makefile | 4 +- lib/libp2p.ex | 13 + libp2p/go.mod | 89 ++++++ libp2p/go.sum | 800 ++++++++++++++++++++++++++++++++++++++++++++++++ libp2p/libp2p.c | 16 +- libp2p/main.go | 17 + 6 files changed, 936 insertions(+), 3 deletions(-) create mode 100644 libp2p/go.sum diff --git a/Makefile b/Makefile index ea61674c8..339608807 100644 --- a/Makefile +++ b/Makefile @@ -8,8 +8,8 @@ GO_ARCHIVES := $(patsubst %.go,%.a,$(GO_SOURCES)) GO_HEADERS := $(patsubst %.go,%.h,$(GO_SOURCES)) -%.a: %.go - go build -buildmode=c-archive -o $@ $< +libp2p/%.a: libp2p/%.go + cd libp2p; go build -buildmode=c-archive $*.go libp2p.so: libp2p/libp2p.c $(GO_ARCHIVES) gcc -Wall -Werror -dynamiclib -undefined dynamic_lookup -I $(ERLANG_INCLUDES) -o libp2p.so \ diff --git a/lib/libp2p.ex b/lib/libp2p.ex index cb14ce7e9..8d45c13b5 100644 --- a/lib/libp2p.ex +++ b/lib/libp2p.ex @@ -34,4 +34,17 @@ defmodule Libp2p do def my_function(_a, _b) do raise "NIF my_function not implemented" end + + @doc """ + Hello world. + + ## Examples + + iex> Libp2p.host_new() + :ok + + """ + def host_new() do + raise "NIF host_new not implemented" + end end diff --git a/libp2p/go.mod b/libp2p/go.mod index b070389db..47ddd3428 100644 --- a/libp2p/go.mod +++ b/libp2p/go.mod @@ -1,3 +1,92 @@ module lambdaclass.com/libp2p go 1.20 + +require github.com/libp2p/go-libp2p v0.29.0 + +require ( + github.com/benbjohnson/clock v1.3.5 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/containerd/cgroups v1.1.0 // indirect + github.com/coreos/go-systemd/v22 v22.5.0 // indirect + github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect + github.com/docker/go-units v0.5.0 // indirect + github.com/elastic/gosigar v0.14.2 // indirect + github.com/flynn/noise v1.0.0 // indirect + github.com/francoispqt/gojay v1.2.13 // indirect + github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect + github.com/godbus/dbus/v5 v5.1.0 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + 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-20230705174524-200ffdc848b8 // indirect + github.com/gorilla/websocket v1.5.0 // indirect + github.com/huin/goupnp v1.2.0 // indirect + github.com/ipfs/go-cid v0.4.1 // 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.7 // indirect + github.com/klauspost/cpuid/v2 v2.2.5 // indirect + github.com/koron/go-ssdp v0.0.4 // 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-msgio v0.3.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.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.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 + github.com/mr-tron/base58 v1.2.0 // indirect + github.com/multiformats/go-base32 v0.1.0 // indirect + github.com/multiformats/go-base36 v0.2.0 // indirect + github.com/multiformats/go-multiaddr v0.10.1 // indirect + github.com/multiformats/go-multiaddr-dns v0.3.1 // indirect + github.com/multiformats/go-multiaddr-fmt v0.1.0 // indirect + github.com/multiformats/go-multibase v0.2.0 // indirect + github.com/multiformats/go-multicodec v0.9.0 // indirect + github.com/multiformats/go-multihash v0.2.3 // 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.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/prometheus/client_golang v1.14.0 // indirect + github.com/prometheus/client_model v0.4.0 // indirect + github.com/prometheus/common v0.37.0 // indirect + github.com/prometheus/procfs v0.8.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.36.2 // indirect + github.com/quic-go/webtransport-go v0.5.3 // indirect + github.com/raulk/go-watchdog v1.3.0 // indirect + github.com/spaolacci/murmur3 v1.1.0 // indirect + go.uber.org/atomic v1.11.0 // indirect + go.uber.org/dig v1.17.0 // indirect + go.uber.org/fx v1.20.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.24.0 // indirect + golang.org/x/crypto v0.11.0 // indirect + golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 // indirect + golang.org/x/mod v0.12.0 // indirect + golang.org/x/net v0.12.0 // indirect + golang.org/x/sync v0.3.0 // indirect + golang.org/x/sys v0.10.0 // indirect + golang.org/x/text v0.11.0 // indirect + golang.org/x/tools v0.11.0 // indirect + google.golang.org/protobuf v1.30.0 // indirect + lukechampine.com/blake3 v1.2.1 // indirect +) diff --git a/libp2p/go.sum b/libp2p/go.sum new file mode 100644 index 000000000..c05dd50e5 --- /dev/null +++ b/libp2p/go.sum @@ -0,0 +1,800 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU= +dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4= +dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU= +git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +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/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.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= +github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/containerd/cgroups v0.0.0-20201119153540-4cbc285b3327/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE= +github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM= +github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw= +github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= +github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c h1:pFUpOrbxDR6AkioZ1ySsx5yxlDQZ8stG2b88gTPxgJU= +github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c/go.mod h1:6UhI8N9EjYm1c2odKpFpAYeR8dsBeM7PtzQhRgxRr9U= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= +github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/elastic/gosigar v0.12.0/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0WKyPWoMs= +github.com/elastic/gosigar v0.14.2 h1:Dg80n8cr90OZ7x+bAax/QjoW/XqTI11RmA79ZwIm9/4= +github.com/elastic/gosigar v0.14.2/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0WKyPWoMs= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= +github.com/flynn/noise v1.0.0 h1:DlTHqmzmvcEiKj+4RYo/imoswx/4r6iBlCMfVtrMXpQ= +github.com/flynn/noise v1.0.0/go.mod h1:xbMo+0i6+IGbYdJhF31t2eR1BIU0CYc12+BNAKwUTag= +github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk= +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/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-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +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/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= +github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +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.2/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.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +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/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/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.4/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-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.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/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20230705174524-200ffdc848b8 h1:n6vlPhxsA+BW/XsS5+uqi7GyzaLa5MH7qlSLBZtRdiA= +github.com/google/pprof v0.0.0-20230705174524-200ffdc848b8/go.mod h1:Jh3hGz2jkYak8qXPD19ryItVnUgpgeqzdkY/D0EaeuA= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +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/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +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/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/huin/goupnp v1.2.0 h1:uOKW26NG1hsSSbXIZ1IR7XP9Gjd1U8pnLaCMgntmkmY= +github.com/huin/goupnp v1.2.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +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/go.mod h1:8BNT7shDZPo99Q74BpGMK+4D8Mn4j46UU0LZ723meps= +github.com/ipfs/go-log/v2 v2.5.1 h1:1XdUzF7048prq4aBjDQQ4SL5RxftpRGdXhNRwKSAlcY= +github.com/ipfs/go-log/v2 v2.5.1/go.mod h1:prSpmC1Gpllc9UYWxDiZDreBYw7zp4Iqp1kOLU9U5UI= +github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= +github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= +github.com/jbenet/go-temp-err-catcher v0.1.0 h1:zpb3ZH6wIE8Shj2sKS+khgRvf7T7RABoLk/+KKHggpk= +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/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +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.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/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +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/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +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/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/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.29.0 h1:QduJ2XQr/Crg4EnloueWDL0Jj86N3Ezhyyj7XH+XwHI= +github.com/libp2p/go-libp2p v0.29.0/go.mod h1:iNKL7mEnZ9wAss+03IjAwM9ZAQXfVUAPUUmOACQfQ/g= +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-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.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.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.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= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +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.55 h1:GoQ4hpsj0nFLYe+bWiCToyrBEJXkQfOOIvFGFy0lEgo= +github.com/miekg/dns v1.1.55/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY= +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= +github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b/go.mod h1:lxPUiZwKoFL8DUUmalo2yJJUCxbPKtm8OKfqr2/FTNU= +github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc h1:PTfri+PuQmWDqERdnNMiD9ZejrlswWrCpBEZgWOiTrc= +github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc/go.mod h1:cGKTAVKx4SxOuR/czcZ/E2RSJ3sfHs8FpHhQ5CWMf9s= +github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ= +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/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/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +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= +github.com/multiformats/go-base32 v0.1.0 h1:pVx9xoSPqEIQG8o+UbAe7DNi51oej1NtK+aGkbLYxPE= +github.com/multiformats/go-base32 v0.1.0/go.mod h1:Kj3tFY6zNr+ABYMqeUNeGvkIC/UYgtWibDcT0rExnbI= +github.com/multiformats/go-base36 v0.2.0 h1:lFsAbNOGeKtuKozrtBsAkSVhv1p9D0/qedU9rQyccr0= +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.10.1 h1:HghtFrWyZEPrpTvgAMFJi6gFdgHfs2cb0pyfDsk+lqU= +github.com/multiformats/go-multiaddr v0.10.1/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= +github.com/multiformats/go-multiaddr-fmt v0.1.0/go.mod h1:hGtDIW4PU4BqJ50gW2quDuPVjyWNZxToGUh/HwTZYJo= +github.com/multiformats/go-multibase v0.2.0 h1:isdYCVLvksgWlMW9OZRYJEa9pZETFivncJHmHnnd87g= +github.com/multiformats/go-multibase v0.2.0/go.mod h1:bFBZX4lKCA/2lyOFSAoKH5SS6oPyjtnzK/XTFDPkNuk= +github.com/multiformats/go-multicodec v0.9.0 h1:pb/dlPnzee/Sxv/j4PmkDRxCOi3hXTz3IbPKOXWJkmg= +github.com/multiformats/go-multicodec v0.9.0/go.mod h1:L3QTQvMIaVBkXOXXtVmYE+LI16i14xuaojr/H7Ai54k= +github.com/multiformats/go-multihash v0.0.8/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew= +github.com/multiformats/go-multihash v0.2.3 h1:7Lyc8XfX/IY2jWb/gI7JP+o7JEq9hOa7BFvVU9RSh+U= +github.com/multiformats/go-multihash v0.2.3/go.mod h1:dXgKXCXjBzdscBLk9JkjINiEsCKRVch90MdaGiKsvSM= +github.com/multiformats/go-multistream v0.4.1 h1:rFy0Iiyn3YT0asivDUIR05leAdwZq3de4741sbiSdfo= +github.com/multiformats/go-multistream v0.4.1/go.mod h1:Mz5eykRVAjJWckE2U78c6xqdtyNUEhKSM0Lwar2p77Q= +github.com/multiformats/go-varint v0.0.1/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= +github.com/multiformats/go-varint v0.0.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/nEGOHFS8= +github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +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 v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +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/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/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +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/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 v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= +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_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +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.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= +github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= +github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE= +github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= +github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= +github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= +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/quic-go v0.36.2 h1:ZX/UNQ4gvpCv2RmwdbA6lrRjF6EBm5yZ7TMoT4NQVrA= +github.com/quic-go/quic-go v0.36.2/go.mod h1:zPetvwDlILVxt15n3hr3Gf/I3mDf7LpLKPhR4Ez0AZQ= +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.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +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= +github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY= +github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM= +github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0= +github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= +github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ= +github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw= +github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI= +github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU= +github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag= +github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg= +github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw= +github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y= +github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= +github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q= +github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ= +github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I= +github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0= +github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ= +github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk= +github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4= +github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= +github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= +github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= +github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= +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= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +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.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/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= +go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= +go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= +go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= +go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= +golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +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.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA= +golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 h1:MGwJjxBy0HJshjDNfLsYO8xppfqWlA5ZT9OhtUUhTNw= +golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +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= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +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.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= +golang.org/x/mod v0.12.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-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-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +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.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50= +golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= +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= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/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-20201207232520-09787c993a3a/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.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-20180905080454-ebe1bf3edb33/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-20181116152217-5ac8a444bdc5/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-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/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.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= +golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +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.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= +golang.org/x/text v0.11.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-20190308202827-9d24e82272b4/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= +golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +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.11.0 h1:EMCa6U9S2LtZXLAMoWiR/R8dAQFRqbAitmbJ2UKhoi8= +golang.org/x/tools v0.11.0/go.mod h1:anzJrxPjNtfgiYQYirP2CPGzGLxrH2u2QBhn6Bf3qY8= +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= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= +google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= +google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= +google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= +google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +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= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +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-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +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.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +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= +grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= +honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +lukechampine.com/blake3 v1.2.1 h1:YuqqRuaqsGV71BV/nm9xlI0MKUv4QC54jQnBChWbGnI= +lukechampine.com/blake3 v1.2.1/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +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/libp2p/libp2p.c b/libp2p/libp2p.c index dda9e708d..44aa055f2 100644 --- a/libp2p/libp2p.c +++ b/libp2p/libp2p.c @@ -2,6 +2,8 @@ extern int MyFunction(int a, int b); +extern int New(); + static ERL_NIF_TERM hello(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { return enif_make_atom(env, "world"); @@ -18,8 +20,20 @@ static ERL_NIF_TERM my_function(ErlNifEnv *env, int argc, const ERL_NIF_TERM arg return enif_make_int(env, result); } +static ERL_NIF_TERM host_new(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) +{ + int result = New(); + if (result != 0) + { + return enif_make_atom(env, "error"); + } + return enif_make_atom(env, "ok"); +} + static ErlNifFunc nif_funcs[] = { {"hello", 0, hello}, - {"my_function", 2, my_function}}; + {"my_function", 2, my_function}, + {"host_new", 0, host_new}, +}; ERL_NIF_INIT(Elixir.Libp2p, nif_funcs, NULL, NULL, NULL, NULL) diff --git a/libp2p/main.go b/libp2p/main.go index a2ff0c907..1c4429397 100644 --- a/libp2p/main.go +++ b/libp2p/main.go @@ -3,10 +3,27 @@ package main import "C" +import ( + "github.com/libp2p/go-libp2p" + "github.com/libp2p/go-libp2p/core/host" +) + +var h host.Host + //export MyFunction func MyFunction(a, b int) int { return a + 2*b } +//export New +func New() int { + var err error + h, err = libp2p.New() + if err != nil { + return 1 + } + return 0 +} + // NOTE: this is needed to build it as an archive (.a) func main() {} From 64ba7115c81947d0bbaa73c996bb6212f3cad1ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1?= <47506558+MegaRedHand@users.noreply.github.com> Date: Thu, 27 Jul 2023 20:06:08 -0300 Subject: [PATCH 03/43] Add host_close, and use cgo handles --- Makefile | 4 ++-- lib/libp2p.ex | 10 +++++++--- libp2p/libp2p.c | 18 +++++++++++++----- libp2p/main.go | 22 ++++++++++++++-------- test/libp2p_test.exs | 6 ++++++ 5 files changed, 42 insertions(+), 18 deletions(-) diff --git a/Makefile b/Makefile index 339608807..c49b1e63e 100644 --- a/Makefile +++ b/Makefile @@ -8,10 +8,10 @@ GO_ARCHIVES := $(patsubst %.go,%.a,$(GO_SOURCES)) GO_HEADERS := $(patsubst %.go,%.h,$(GO_SOURCES)) -libp2p/%.a: libp2p/%.go +libp2p/%.a libp2p/%.h: libp2p/%.go cd libp2p; go build -buildmode=c-archive $*.go -libp2p.so: libp2p/libp2p.c $(GO_ARCHIVES) +libp2p.so: libp2p/libp2p.c $(GO_ARCHIVES) $(GO_HEADERS) gcc -Wall -Werror -dynamiclib -undefined dynamic_lookup -I $(ERLANG_INCLUDES) -o libp2p.so \ libp2p/libp2p.c $(GO_ARCHIVES) diff --git a/lib/libp2p.ex b/lib/libp2p.ex index 8d45c13b5..bb492c382 100644 --- a/lib/libp2p.ex +++ b/lib/libp2p.ex @@ -23,7 +23,7 @@ defmodule Libp2p do end @doc """ - Hello world. + Test function. ## Examples @@ -36,15 +36,19 @@ defmodule Libp2p do end @doc """ - Hello world. + Creates a new Host. ## Examples iex> Libp2p.host_new() - :ok + {:ok, 1} """ def host_new() do raise "NIF host_new not implemented" end + + def host_close(_host) do + raise "NIF host_close not implemented" + end end diff --git a/libp2p/libp2p.c b/libp2p/libp2p.c index 44aa055f2..b1d44b96b 100644 --- a/libp2p/libp2p.c +++ b/libp2p/libp2p.c @@ -1,9 +1,6 @@ +#include "main.h" #include -extern int MyFunction(int a, int b); - -extern int New(); - static ERL_NIF_TERM hello(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { return enif_make_atom(env, "world"); @@ -23,16 +20,27 @@ static ERL_NIF_TERM my_function(ErlNifEnv *env, int argc, const ERL_NIF_TERM arg static ERL_NIF_TERM host_new(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { int result = New(); - if (result != 0) + if (result == 0) { return enif_make_atom(env, "error"); } + return enif_make_tuple2(env, enif_make_atom(env, "ok"), enif_make_uint64(env, result)); +} + +static ERL_NIF_TERM host_close(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) +{ + uintptr_t handle; + enif_get_uint64(env, argv[0], &handle); + + Close(handle); + return enif_make_atom(env, "ok"); } static ErlNifFunc nif_funcs[] = { {"hello", 0, hello}, {"my_function", 2, my_function}, + {"host_close", 1, host_close}, {"host_new", 0, host_new}, }; diff --git a/libp2p/main.go b/libp2p/main.go index 1c4429397..dc09b2bb4 100644 --- a/libp2p/main.go +++ b/libp2p/main.go @@ -1,28 +1,34 @@ // NOTE: the package **must** be named main package main +/* +#include // for uintptr_t +*/ import "C" - import ( + "runtime/cgo" + "github.com/libp2p/go-libp2p" "github.com/libp2p/go-libp2p/core/host" ) -var h host.Host - //export MyFunction func MyFunction(a, b int) int { return a + 2*b } //export New -func New() int { - var err error - h, err = libp2p.New() +func New() C.uintptr_t { + h, err := libp2p.New() if err != nil { - return 1 + return 0 } - return 0 + return C.uintptr_t(cgo.NewHandle(h)) +} + +//export Close +func (h C.uintptr_t) Close() { + cgo.Handle(h).Value().(host.Host).Close() } // NOTE: this is needed to build it as an archive (.a) diff --git a/test/libp2p_test.exs b/test/libp2p_test.exs index 7aa971e0e..b8e3550ea 100644 --- a/test/libp2p_test.exs +++ b/test/libp2p_test.exs @@ -9,4 +9,10 @@ defmodule Libp2pTest do test "my_function is a + 2 * b" do assert Libp2p.my_function(5, 124) == 5 + 2 * 124 end + + test "Create and destroy host" do + {:ok, host} = Libp2p.host_new() + assert host != 0 + :ok = Libp2p.host_close(host) + end end From 99ea9c252da4c6591da63c26b7afcd9f497aad0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1?= <47506558+MegaRedHand@users.noreply.github.com> Date: Fri, 28 Jul 2023 11:13:21 -0300 Subject: [PATCH 04/43] Use macro for C function signature --- libp2p/libp2p.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/libp2p/libp2p.c b/libp2p/libp2p.c index b1d44b96b..e326d2d39 100644 --- a/libp2p/libp2p.c +++ b/libp2p/libp2p.c @@ -1,12 +1,14 @@ #include "main.h" #include -static ERL_NIF_TERM hello(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) +#define ERL_FUNCTION(FUNCTION_NAME) static ERL_NIF_TERM FUNCTION_NAME(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) + +ERL_FUNCTION(hello) { return enif_make_atom(env, "world"); } -static ERL_NIF_TERM my_function(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) +ERL_FUNCTION(my_function) { int a, b; enif_get_int(env, argv[0], &a); @@ -17,7 +19,7 @@ static ERL_NIF_TERM my_function(ErlNifEnv *env, int argc, const ERL_NIF_TERM arg return enif_make_int(env, result); } -static ERL_NIF_TERM host_new(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) +ERL_FUNCTION(host_new) { int result = New(); if (result == 0) @@ -27,7 +29,7 @@ static ERL_NIF_TERM host_new(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[] return enif_make_tuple2(env, enif_make_atom(env, "ok"), enif_make_uint64(env, result)); } -static ERL_NIF_TERM host_close(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) +ERL_FUNCTION(host_close) { uintptr_t handle; enif_get_uint64(env, argv[0], &handle); @@ -40,8 +42,8 @@ static ERL_NIF_TERM host_close(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv static ErlNifFunc nif_funcs[] = { {"hello", 0, hello}, {"my_function", 2, my_function}, - {"host_close", 1, host_close}, {"host_new", 0, host_new}, + {"host_close", 1, host_close}, }; ERL_NIF_INIT(Elixir.Libp2p, nif_funcs, NULL, NULL, NULL, NULL) From b3b7abb8aa421063ef1e0477ba0eb5b6507bdf55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1?= <47506558+MegaRedHand@users.noreply.github.com> Date: Fri, 28 Jul 2023 11:17:37 -0300 Subject: [PATCH 05/43] Fix host_close and tests --- lib/libp2p.ex | 9 +++------ libp2p/main.go | 4 +++- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/lib/libp2p.ex b/lib/libp2p.ex index bb492c382..9f7552f7d 100644 --- a/lib/libp2p.ex +++ b/lib/libp2p.ex @@ -37,17 +37,14 @@ defmodule Libp2p do @doc """ Creates a new Host. - - ## Examples - - iex> Libp2p.host_new() - {:ok, 1} - """ def host_new() do raise "NIF host_new not implemented" end + @doc """ + Deletes a Host. + """ def host_close(_host) do raise "NIF host_close not implemented" end diff --git a/libp2p/main.go b/libp2p/main.go index dc09b2bb4..593a80141 100644 --- a/libp2p/main.go +++ b/libp2p/main.go @@ -28,7 +28,9 @@ func New() C.uintptr_t { //export Close func (h C.uintptr_t) Close() { - cgo.Handle(h).Value().(host.Host).Close() + handle := cgo.Handle(h) + defer handle.Delete() + handle.Value().(host.Host).Close() } // NOTE: this is needed to build it as an archive (.a) From 6e0166f735e5f1720fef4b81d1eede46c1ead61e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1?= <47506558+MegaRedHand@users.noreply.github.com> Date: Fri, 28 Jul 2023 15:17:48 -0300 Subject: [PATCH 06/43] Add Host.SetStreamHandler binding --- .gitignore | 2 ++ Makefile | 4 ++-- lib/libp2p.ex | 7 +++++++ libp2p/libp2p.c | 25 +++++++++++++++++++++++++ libp2p/main.go | 16 +++++++++++++++- libp2p/utils.c | 18 ++++++++++++++++++ libp2p/utils.h | 5 +++++ test/libp2p_test.exs | 7 +++++++ 8 files changed, 81 insertions(+), 3 deletions(-) create mode 100644 libp2p/utils.c create mode 100644 libp2p/utils.h diff --git a/.gitignore b/.gitignore index 6d6201dff..7d7df4cfc 100644 --- a/.gitignore +++ b/.gitignore @@ -41,3 +41,5 @@ lambda_ethereum_consensus-*.tar # VSCode configuration dir. .vscode/ + +!libp2p/utils.h diff --git a/Makefile b/Makefile index c49b1e63e..5c19802c3 100644 --- a/Makefile +++ b/Makefile @@ -11,9 +11,9 @@ GO_HEADERS := $(patsubst %.go,%.h,$(GO_SOURCES)) libp2p/%.a libp2p/%.h: libp2p/%.go cd libp2p; go build -buildmode=c-archive $*.go -libp2p.so: libp2p/libp2p.c $(GO_ARCHIVES) $(GO_HEADERS) +libp2p.so: $(GO_ARCHIVES) $(GO_HEADERS) libp2p/libp2p.c libp2p/utils.c gcc -Wall -Werror -dynamiclib -undefined dynamic_lookup -I $(ERLANG_INCLUDES) -o libp2p.so \ - libp2p/libp2p.c $(GO_ARCHIVES) + libp2p/libp2p.c libp2p/utils.c $(GO_ARCHIVES) clean: -rm $(GO_ARCHIVES) $(GO_HEADERS) libp2p.so diff --git a/lib/libp2p.ex b/lib/libp2p.ex index 9f7552f7d..979e50ebb 100644 --- a/lib/libp2p.ex +++ b/lib/libp2p.ex @@ -48,4 +48,11 @@ defmodule Libp2p do def host_close(_host) do raise "NIF host_close not implemented" end + + @doc """ + Sets the stream handler associated to a protocol id. + """ + def host_set_stream_handler(_host, _protocol_id) do + raise "NIF host_set_stream_handler not implemented" + end end diff --git a/libp2p/libp2p.c b/libp2p/libp2p.c index e326d2d39..f2cfca8b2 100644 --- a/libp2p/libp2p.c +++ b/libp2p/libp2p.c @@ -1,8 +1,11 @@ #include "main.h" +#include "utils.h" #include #define ERL_FUNCTION(FUNCTION_NAME) static ERL_NIF_TERM FUNCTION_NAME(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) +const uint64_t PID_LENGTH = 1024; + ERL_FUNCTION(hello) { return enif_make_atom(env, "world"); @@ -39,11 +42,33 @@ ERL_FUNCTION(host_close) return enif_make_atom(env, "ok"); } +ERL_FUNCTION(host_set_stream_handler) +{ + uintptr_t handle; + enif_get_uint64(env, argv[0], &handle); + + char proto_id[PID_LENGTH]; + enif_get_string(env, argv[1], proto_id, PID_LENGTH, ERL_NIF_UTF8); + + // TODO: This is a memory leak. + ErlNifPid *pid = malloc(sizeof(ErlNifPid)); + + if (!enif_self(env, pid)) + { + return enif_make_atom(env, "error"); + } + + SetStreamHandler(handle, proto_id, (void *)pid); + + return enif_make_atom(env, "ok"); +} + static ErlNifFunc nif_funcs[] = { {"hello", 0, hello}, {"my_function", 2, my_function}, {"host_new", 0, host_new}, {"host_close", 1, host_close}, + {"host_set_stream_handler", 2, host_set_stream_handler}, }; ERL_NIF_INIT(Elixir.Libp2p, nif_funcs, NULL, NULL, NULL, NULL) diff --git a/libp2p/main.go b/libp2p/main.go index 593a80141..f2765fdb7 100644 --- a/libp2p/main.go +++ b/libp2p/main.go @@ -2,14 +2,18 @@ package main /* -#include // for uintptr_t +#include "utils.h" */ import "C" + import ( "runtime/cgo" + "unsafe" "github.com/libp2p/go-libp2p" "github.com/libp2p/go-libp2p/core/host" + "github.com/libp2p/go-libp2p/core/network" + "github.com/libp2p/go-libp2p/core/protocol" ) //export MyFunction @@ -33,5 +37,15 @@ func (h C.uintptr_t) Close() { handle.Value().(host.Host).Close() } +//export SetStreamHandler +func (h C.uintptr_t) SetStreamHandler(proto_id *C.char, proc_id unsafe.Pointer) { + handle := cgo.Handle(h) + host := handle.Value().(host.Host) + handler := func(stream network.Stream) { + C.send_message(proc_id, C.uintptr_t(cgo.NewHandle(stream))) + } + host.SetStreamHandler(protocol.ID(C.GoString(proto_id)), handler) +} + // NOTE: this is needed to build it as an archive (.a) func main() {} diff --git a/libp2p/utils.c b/libp2p/utils.c new file mode 100644 index 000000000..43a21ffbf --- /dev/null +++ b/libp2p/utils.c @@ -0,0 +1,18 @@ +#include "utils.h" +#include + +void send_message(void *_pid, uintptr_t stream_handle) +{ + // Passed as void* to avoid including erl_nif.h in the header. + ErlNifPid *pid = (ErlNifPid *)_pid; + ErlNifEnv *env = enif_alloc_env(); + + ERL_NIF_TERM message = enif_make_tuple2(env, enif_make_atom(env, "ok"), enif_make_uint64(env, stream_handle)); + + int result = enif_send(NULL, pid, env, message); + // On error, the env isn't freed by the function. + if (!result) + { + enif_free_env(env); + } +} diff --git a/libp2p/utils.h b/libp2p/utils.h new file mode 100644 index 000000000..17c855b5f --- /dev/null +++ b/libp2p/utils.h @@ -0,0 +1,5 @@ +#pragma once + +#include // for uintptr_t + +void send_message(void *pid, uintptr_t stream); diff --git a/test/libp2p_test.exs b/test/libp2p_test.exs index b8e3550ea..24e6168dd 100644 --- a/test/libp2p_test.exs +++ b/test/libp2p_test.exs @@ -15,4 +15,11 @@ defmodule Libp2pTest do assert host != 0 :ok = Libp2p.host_close(host) end + + test "Set stream handler" do + {:ok, host} = Libp2p.host_new() + assert host != 0 + :ok = Libp2p.host_set_stream_handler(host, "/my-app/amazing-protocol/1.0.1") + :ok = Libp2p.host_close(host) + end end From 4b6497a8fc8ced9618f229fbe45f35b57baa0151 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1?= <47506558+MegaRedHand@users.noreply.github.com> Date: Fri, 28 Jul 2023 16:59:50 -0300 Subject: [PATCH 07/43] Add PoC for message sending Go -> Elixir --- lib/libp2p.ex | 7 +++++++ libp2p/libp2p.c | 16 ++++++++++++++++ libp2p/main.go | 13 +++++++++++-- libp2p/utils.c | 25 +++++++++++++++++++++++-- libp2p/utils.h | 6 +++++- test/libp2p_test.exs | 10 ++++++++++ 6 files changed, 72 insertions(+), 5 deletions(-) diff --git a/lib/libp2p.ex b/lib/libp2p.ex index 979e50ebb..a54f86bff 100644 --- a/lib/libp2p.ex +++ b/lib/libp2p.ex @@ -35,6 +35,13 @@ defmodule Libp2p do raise "NIF my_function not implemented" end + @doc """ + Test function that sends a message asynchronously. + """ + def test_send_message() do + raise "NIF test_send_message not implemented" + end + @doc """ Creates a new Host. """ diff --git a/libp2p/libp2p.c b/libp2p/libp2p.c index f2cfca8b2..a624ca686 100644 --- a/libp2p/libp2p.c +++ b/libp2p/libp2p.c @@ -22,6 +22,21 @@ ERL_FUNCTION(my_function) return enif_make_int(env, result); } +ERL_FUNCTION(test_send_message) +{ + // TODO: This is a memory leak. + ErlNifPid *pid = malloc(sizeof(ErlNifPid)); + + if (!enif_self(env, pid)) + { + return enif_make_atom(env, "error"); + } + + TestSendMessage(pid); + + return enif_make_atom(env, "ok"); +} + ERL_FUNCTION(host_new) { int result = New(); @@ -69,6 +84,7 @@ static ErlNifFunc nif_funcs[] = { {"host_new", 0, host_new}, {"host_close", 1, host_close}, {"host_set_stream_handler", 2, host_set_stream_handler}, + {"test_send_message", 0, test_send_message}, }; ERL_NIF_INIT(Elixir.Libp2p, nif_funcs, NULL, NULL, NULL, NULL) diff --git a/libp2p/main.go b/libp2p/main.go index f2765fdb7..9f2f7f152 100644 --- a/libp2p/main.go +++ b/libp2p/main.go @@ -8,7 +8,7 @@ import "C" import ( "runtime/cgo" - "unsafe" + "time" "github.com/libp2p/go-libp2p" "github.com/libp2p/go-libp2p/core/host" @@ -21,6 +21,15 @@ func MyFunction(a, b int) int { return a + 2*b } +//export TestSendMessage +func TestSendMessage(proc_id C.erl_pid_t) { + go func() { + // wait for 500 ms + time.Sleep(500 * time.Millisecond) + C.go_test_send_message(proc_id) + }() +} + //export New func New() C.uintptr_t { h, err := libp2p.New() @@ -38,7 +47,7 @@ func (h C.uintptr_t) Close() { } //export SetStreamHandler -func (h C.uintptr_t) SetStreamHandler(proto_id *C.char, proc_id unsafe.Pointer) { +func (h C.uintptr_t) SetStreamHandler(proto_id *C.char, proc_id C.erl_pid_t) { handle := cgo.Handle(h) host := handle.Value().(host.Host) handler := func(stream network.Stream) { diff --git a/libp2p/utils.c b/libp2p/utils.c index 43a21ffbf..860e331a4 100644 --- a/libp2p/utils.c +++ b/libp2p/utils.c @@ -1,10 +1,15 @@ #include "utils.h" #include -void send_message(void *_pid, uintptr_t stream_handle) +ErlNifPid *get_pid(erl_pid_t _pid) +{ + return (ErlNifPid *)_pid; +} + +void send_message(erl_pid_t _pid, uintptr_t stream_handle) { // Passed as void* to avoid including erl_nif.h in the header. - ErlNifPid *pid = (ErlNifPid *)_pid; + ErlNifPid *pid = get_pid(_pid); ErlNifEnv *env = enif_alloc_env(); ERL_NIF_TERM message = enif_make_tuple2(env, enif_make_atom(env, "ok"), enif_make_uint64(env, stream_handle)); @@ -16,3 +21,19 @@ void send_message(void *_pid, uintptr_t stream_handle) enif_free_env(env); } } + +void go_test_send_message(erl_pid_t _pid) +{ + // Passed as void* to avoid including erl_nif.h in the header. + ErlNifPid *pid = get_pid(_pid); + ErlNifEnv *env = enif_alloc_env(); + + ERL_NIF_TERM message = enif_make_tuple2(env, enif_make_atom(env, "ok"), enif_make_uint64(env, 5353)); + + int result = enif_send(NULL, pid, env, message); + // On error, the env isn't freed by the function. + if (!result) + { + enif_free_env(env); + } +} diff --git a/libp2p/utils.h b/libp2p/utils.h index 17c855b5f..8d2a77554 100644 --- a/libp2p/utils.h +++ b/libp2p/utils.h @@ -2,4 +2,8 @@ #include // for uintptr_t -void send_message(void *pid, uintptr_t stream); +typedef void *erl_pid_t; + +void send_message(erl_pid_t pid, uintptr_t stream); + +void go_test_send_message(erl_pid_t pid); diff --git a/test/libp2p_test.exs b/test/libp2p_test.exs index 24e6168dd..8e352b1d8 100644 --- a/test/libp2p_test.exs +++ b/test/libp2p_test.exs @@ -10,6 +10,16 @@ defmodule Libp2pTest do assert Libp2p.my_function(5, 124) == 5 + 2 * 124 end + test "test_send_message sends a message" do + :ok = Libp2p.test_send_message() + + receive do + msg -> {:ok, 5353} = msg + after + 1_000 -> :timeout + end + end + test "Create and destroy host" do {:ok, host} = Libp2p.host_new() assert host != 0 From 120e8d209bf205c46ad22be759a84dcae88920d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1?= <47506558+MegaRedHand@users.noreply.github.com> Date: Fri, 28 Jul 2023 17:01:15 -0300 Subject: [PATCH 08/43] Fix test --- libp2p/libp2p.c | 3 +-- libp2p/utils.c | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libp2p/libp2p.c b/libp2p/libp2p.c index a624ca686..570a25fd9 100644 --- a/libp2p/libp2p.c +++ b/libp2p/libp2p.c @@ -24,7 +24,6 @@ ERL_FUNCTION(my_function) ERL_FUNCTION(test_send_message) { - // TODO: This is a memory leak. ErlNifPid *pid = malloc(sizeof(ErlNifPid)); if (!enif_self(env, pid)) @@ -81,10 +80,10 @@ ERL_FUNCTION(host_set_stream_handler) static ErlNifFunc nif_funcs[] = { {"hello", 0, hello}, {"my_function", 2, my_function}, + {"test_send_message", 0, test_send_message}, {"host_new", 0, host_new}, {"host_close", 1, host_close}, {"host_set_stream_handler", 2, host_set_stream_handler}, - {"test_send_message", 0, test_send_message}, }; ERL_NIF_INIT(Elixir.Libp2p, nif_funcs, NULL, NULL, NULL, NULL) diff --git a/libp2p/utils.c b/libp2p/utils.c index 860e331a4..1c1861266 100644 --- a/libp2p/utils.c +++ b/libp2p/utils.c @@ -36,4 +36,5 @@ void go_test_send_message(erl_pid_t _pid) { enif_free_env(env); } + free(pid); } From d2ff73a70ffa8c887476a9a771be1576436edbe6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1?= <47506558+MegaRedHand@users.noreply.github.com> Date: Mon, 31 Jul 2023 11:43:27 -0300 Subject: [PATCH 09/43] Divide main.go in sections, and add more methods --- libp2p/libp2p.c | 2 +- libp2p/main.go | 56 +++++++++++++++++++++++++++++++++++++++----- test/libp2p_test.exs | 12 ++++++++++ 3 files changed, 63 insertions(+), 7 deletions(-) diff --git a/libp2p/libp2p.c b/libp2p/libp2p.c index 570a25fd9..104f51f71 100644 --- a/libp2p/libp2p.c +++ b/libp2p/libp2p.c @@ -38,7 +38,7 @@ ERL_FUNCTION(test_send_message) ERL_FUNCTION(host_new) { - int result = New(); + int result = New(0, NULL); if (result == 0) { return enif_make_atom(env, "error"); diff --git a/libp2p/main.go b/libp2p/main.go index 9f2f7f152..b9e9bb322 100644 --- a/libp2p/main.go +++ b/libp2p/main.go @@ -13,25 +13,50 @@ import ( "github.com/libp2p/go-libp2p" "github.com/libp2p/go-libp2p/core/host" "github.com/libp2p/go-libp2p/core/network" + "github.com/libp2p/go-libp2p/core/peer" + "github.com/libp2p/go-libp2p/core/peerstore" "github.com/libp2p/go-libp2p/core/protocol" + "github.com/multiformats/go-multiaddr" ) +//export PermanentAddrTTL +const PermanentAddrTTL = peerstore.PermanentAddrTTL + +/*********/ +/* Tests */ +/*********/ + //export MyFunction func MyFunction(a, b int) int { return a + 2*b } //export TestSendMessage -func TestSendMessage(proc_id C.erl_pid_t) { +func TestSendMessage(procId C.erl_pid_t) { go func() { // wait for 500 ms time.Sleep(500 * time.Millisecond) - C.go_test_send_message(proc_id) + C.go_test_send_message(procId) }() } +/*********/ +/* Utils */ +/*********/ + +//export ListenAddrStrings +func ListenAddrStrings(listenAddr *C.char) C.uintptr_t { + addr := libp2p.ListenAddrStrings(C.GoString(listenAddr)) + return C.uintptr_t(cgo.NewHandle(addr)) +} + +/****************/ +/* Host methods */ +/****************/ + //export New -func New() C.uintptr_t { +func New(len uint, options *C.uintptr_t) C.uintptr_t { + // TODO: pass options h, err := libp2p.New() if err != nil { return 0 @@ -47,13 +72,32 @@ func (h C.uintptr_t) Close() { } //export SetStreamHandler -func (h C.uintptr_t) SetStreamHandler(proto_id *C.char, proc_id C.erl_pid_t) { +func (h C.uintptr_t) SetStreamHandler(protoId *C.char, procId C.erl_pid_t) { handle := cgo.Handle(h) host := handle.Value().(host.Host) handler := func(stream network.Stream) { - C.send_message(proc_id, C.uintptr_t(cgo.NewHandle(stream))) + // NOTE: the stream handle should be deleted when calling Stream.Close() + C.send_message(procId, C.uintptr_t(cgo.NewHandle(stream))) } - host.SetStreamHandler(protocol.ID(C.GoString(proto_id)), handler) + host.SetStreamHandler(protocol.ID(C.GoString(protoId)), handler) +} + +//export Peerstore +func (h C.uintptr_t) Peerstore() C.uintptr_t { + host := cgo.Handle(h).Value().(host.Host) + return C.uintptr_t(cgo.NewHandle(host.Peerstore())) +} + +/*********************/ +/* Peerstore methods */ +/*********************/ + +//export AddAddrs +func (ps C.uintptr_t) AddAddrs(id, addrs C.uintptr_t, ttl uint64) { + psv := cgo.Handle(ps).Value().(peerstore.Peerstore) + idv := cgo.Handle(id).Value().(peer.ID) + addrsv := cgo.Handle(addrs).Value().([]multiaddr.Multiaddr) + psv.AddAddrs(idv, addrsv, time.Duration(ttl)) } // NOTE: this is needed to build it as an archive (.a) diff --git a/test/libp2p_test.exs b/test/libp2p_test.exs index 8e352b1d8..339d5d820 100644 --- a/test/libp2p_test.exs +++ b/test/libp2p_test.exs @@ -32,4 +32,16 @@ defmodule Libp2pTest do :ok = Libp2p.host_set_stream_handler(host, "/my-app/amazing-protocol/1.0.1") :ok = Libp2p.host_close(host) end + + test "Start two hosts, and play one round of ping-pong" do + # Setup sender + {:ok, sender} = Libp2p.host_new() + # Setup receiver + {:ok, recver} = Libp2p.host_new() + + :ok = Libp2p.host_set_stream_handler(recver, "/pong") + + :ok = Libp2p.host_close(host1) + :ok = Libp2p.host_close(host2) + end end From 01dcace0afa0f40cfd83406e27439ab1ee66ad60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1?= <47506558+MegaRedHand@users.noreply.github.com> Date: Mon, 31 Jul 2023 12:47:13 -0300 Subject: [PATCH 10/43] Add Read+Write+Close methods for Stream --- libp2p/libp2p.c | 2 +- libp2p/main.go | 53 +++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 52 insertions(+), 3 deletions(-) diff --git a/libp2p/libp2p.c b/libp2p/libp2p.c index 104f51f71..c7389f3ae 100644 --- a/libp2p/libp2p.c +++ b/libp2p/libp2p.c @@ -51,7 +51,7 @@ ERL_FUNCTION(host_close) uintptr_t handle; enif_get_uint64(env, argv[0], &handle); - Close(handle); + HostClose(handle); return enif_make_atom(env, "ok"); } diff --git a/libp2p/main.go b/libp2p/main.go index b9e9bb322..8db7e48bf 100644 --- a/libp2p/main.go +++ b/libp2p/main.go @@ -2,13 +2,16 @@ package main /* +#include #include "utils.h" */ import "C" import ( + "context" "runtime/cgo" "time" + "unsafe" "github.com/libp2p/go-libp2p" "github.com/libp2p/go-libp2p/core/host" @@ -64,8 +67,8 @@ func New(len uint, options *C.uintptr_t) C.uintptr_t { return C.uintptr_t(cgo.NewHandle(h)) } -//export Close -func (h C.uintptr_t) Close() { +//export HostClose +func (h C.uintptr_t) HostClose() { handle := cgo.Handle(h) defer handle.Delete() handle.Value().(host.Host).Close() @@ -88,6 +91,17 @@ func (h C.uintptr_t) Peerstore() C.uintptr_t { return C.uintptr_t(cgo.NewHandle(host.Peerstore())) } +//export NewStream +func (h C.uintptr_t) NewStream(pid C.uintptr_t, protoId *C.char) C.uintptr_t { + host := cgo.Handle(h).Value().(host.Host) + peerId := cgo.Handle(pid).Value().(peer.ID) + stream, err := host.NewStream(context.TODO(), peerId, protocol.ID(C.GoString(protoId))) + if err != nil { + return 0 + } + return C.uintptr_t(cgo.NewHandle(stream)) +} + /*********************/ /* Peerstore methods */ /*********************/ @@ -100,5 +114,40 @@ func (ps C.uintptr_t) AddAddrs(id, addrs C.uintptr_t, ttl uint64) { psv.AddAddrs(idv, addrsv, time.Duration(ttl)) } +/******************/ +/* Stream methods */ +/******************/ + +//export Read +func (s C.uintptr_t) Read(len uint, buffer *C.char) int { + stream := cgo.Handle(s).Value().(network.Stream) + goBuffer := make([]byte, len) + n, err := stream.Read(goBuffer) + C.memcpy(unsafe.Pointer(buffer), unsafe.Pointer(&goBuffer[0]), C.size_t(n)) + if err != nil { + return -1 + } + return n +} + +//export Write +func (s C.uintptr_t) Write(len uint, buffer *C.char) int { + stream := cgo.Handle(s).Value().(network.Stream) + goBuffer := C.GoBytes(unsafe.Pointer(buffer), C.int(len)) + n, err := stream.Write(goBuffer) + if err != nil { + return -1 + } + return n +} + +//export StreamClose +func (s C.uintptr_t) StreamClose() { + handle := cgo.Handle(s) + defer handle.Delete() + stream := handle.Value().(network.Stream) + stream.Close() +} + // NOTE: this is needed to build it as an archive (.a) func main() {} From 7309f5f1e9258236b84ac5e5b8262b18f6afca28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1?= <47506558+MegaRedHand@users.noreply.github.com> Date: Mon, 31 Jul 2023 12:55:10 -0300 Subject: [PATCH 11/43] Add getters for Host --- libp2p/main.go | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/libp2p/main.go b/libp2p/main.go index 8db7e48bf..04d89358e 100644 --- a/libp2p/main.go +++ b/libp2p/main.go @@ -43,6 +43,16 @@ func TestSendMessage(procId C.erl_pid_t) { }() } +/***********/ +/* Helpers */ +/***********/ + +func callGetter[T any, R any](h C.uintptr_t, g func(T) R) C.uintptr_t { + recver := cgo.Handle(h).Value().(T) + prop := g(recver) + return C.uintptr_t(cgo.NewHandle(prop)) +} + /*********/ /* Utils */ /*********/ @@ -87,8 +97,17 @@ func (h C.uintptr_t) SetStreamHandler(protoId *C.char, procId C.erl_pid_t) { //export Peerstore func (h C.uintptr_t) Peerstore() C.uintptr_t { - host := cgo.Handle(h).Value().(host.Host) - return C.uintptr_t(cgo.NewHandle(host.Peerstore())) + return callGetter(h, host.Host.Peerstore) +} + +//export ID +func (h C.uintptr_t) ID() C.uintptr_t { + return callGetter(h, host.Host.ID) +} + +//export Addrs +func (h C.uintptr_t) Addrs() C.uintptr_t { + return callGetter(h, host.Host.Addrs) } //export NewStream From c67f8eb1abcd3a0d5d6243c65d7fa1a8f8f614cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1?= <47506558+MegaRedHand@users.noreply.github.com> Date: Mon, 31 Jul 2023 15:30:19 -0300 Subject: [PATCH 12/43] Port ListenAddrStrings and getters --- lib/libp2p.ex | 31 ++++++++++++++ libp2p/libp2p.c | 99 +++++++++++++++++++++++++++++++++++++------- libp2p/main.go | 33 ++++++++------- test/libp2p_test.exs | 18 ++++---- 4 files changed, 141 insertions(+), 40 deletions(-) diff --git a/lib/libp2p.ex b/lib/libp2p.ex index a54f86bff..7f31137ef 100644 --- a/lib/libp2p.ex +++ b/lib/libp2p.ex @@ -62,4 +62,35 @@ defmodule Libp2p do def host_set_stream_handler(_host, _protocol_id) do raise "NIF host_set_stream_handler not implemented" end + + @doc """ + `listen_addr_strings` configures libp2p to listen on the + given (unparsed) addresses. + Returns an `Option` that can be passed to `host_new` + as an argument. + """ + def listen_addr_strings(_addr) do + raise "NIF listen_addr_strings not implemented" + end + + @doc """ + host_peerstore gets the `Peerstore` of the given `Host`. + """ + def host_peerstore(_host) do + raise "NIF host_peerstore not implemented" + end + + @doc """ + host_id gets the `ID` of the given `Host`. + """ + def host_id(_host) do + raise "NIF host_id not implemented" + end + + @doc """ + host_id gets the addresses of the given `Host`. + """ + def host_addrs(_host) do + raise "NIF host_addrs not implemented" + end end diff --git a/libp2p/libp2p.c b/libp2p/libp2p.c index c7389f3ae..1d9ea08da 100644 --- a/libp2p/libp2p.c +++ b/libp2p/libp2p.c @@ -4,8 +4,45 @@ #define ERL_FUNCTION(FUNCTION_NAME) static ERL_NIF_TERM FUNCTION_NAME(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) +#define ERL_FUNCTION_GETTER(NAME, GETTER) \ + ERL_FUNCTION(NAME) \ + { \ + uintptr_t host = get_handle_from_term(env, argv[0]); \ + uintptr_t host_id = GETTER(host); \ + return get_handle_result(env, host_id); \ + } + +#define NIF_ENTRY(FUNCTION_NAME, ARITY) \ + { \ + #FUNCTION_NAME, ARITY, FUNCTION_NAME \ + } + const uint64_t PID_LENGTH = 1024; +/***********/ +/* Helpers */ +/***********/ + +static uintptr_t get_handle_from_term(ErlNifEnv *env, ERL_NIF_TERM term) +{ + uintptr_t handle; + enif_get_uint64(env, term, &handle); + return handle; +} + +static ERL_NIF_TERM get_handle_result(ErlNifEnv *env, uintptr_t handle) +{ + if (handle == 0) + { + return enif_make_atom(env, "error"); + } + return enif_make_tuple2(env, enif_make_atom(env, "ok"), enif_make_uint64(env, handle)); +} + +/*********/ +/* Tests */ +/*********/ + ERL_FUNCTION(hello) { return enif_make_atom(env, "world"); @@ -36,20 +73,37 @@ ERL_FUNCTION(test_send_message) return enif_make_atom(env, "ok"); } +/*********/ +/* Utils */ +/*********/ + +ERL_FUNCTION(listen_addr_strings) +{ + uint32_t len; + enif_get_string_length(env, argv[0], &len, ERL_NIF_UTF8); + char addr_string[len]; + enif_get_string(env, argv[0], addr_string, len, ERL_NIF_UTF8); + + GoString go_listenAddr = {addr_string, len}; + + ListenAddrStrings(go_listenAddr); + + return enif_make_atom(env, "ok"); +} + +/****************/ +/* Host methods */ +/****************/ + ERL_FUNCTION(host_new) { int result = New(0, NULL); - if (result == 0) - { - return enif_make_atom(env, "error"); - } - return enif_make_tuple2(env, enif_make_atom(env, "ok"), enif_make_uint64(env, result)); + return get_handle_result(env, result); } ERL_FUNCTION(host_close) { - uintptr_t handle; - enif_get_uint64(env, argv[0], &handle); + uintptr_t handle = get_handle_from_term(env, argv[0]); HostClose(handle); @@ -58,8 +112,7 @@ ERL_FUNCTION(host_close) ERL_FUNCTION(host_set_stream_handler) { - uintptr_t handle; - enif_get_uint64(env, argv[0], &handle); + uintptr_t handle = get_handle_from_term(env, argv[0]); char proto_id[PID_LENGTH]; enif_get_string(env, argv[1], proto_id, PID_LENGTH, ERL_NIF_UTF8); @@ -77,13 +130,29 @@ ERL_FUNCTION(host_set_stream_handler) return enif_make_atom(env, "ok"); } +ERL_FUNCTION_GETTER(host_peerstore, Peerstore) +ERL_FUNCTION_GETTER(host_id, ID) +ERL_FUNCTION_GETTER(host_addrs, Addrs) + +/* Functions left to port +- NewStream +- AddAddrs +- StreamRead +- StreamWrite +- StreamClose +*/ + static ErlNifFunc nif_funcs[] = { - {"hello", 0, hello}, - {"my_function", 2, my_function}, - {"test_send_message", 0, test_send_message}, - {"host_new", 0, host_new}, - {"host_close", 1, host_close}, - {"host_set_stream_handler", 2, host_set_stream_handler}, + NIF_ENTRY(hello, 0), + NIF_ENTRY(my_function, 2), + NIF_ENTRY(test_send_message, 0), + NIF_ENTRY(listen_addr_strings, 1), + NIF_ENTRY(host_new, 0), + NIF_ENTRY(host_close, 1), + NIF_ENTRY(host_set_stream_handler, 2), + NIF_ENTRY(host_peerstore, 1), + NIF_ENTRY(host_id, 1), + NIF_ENTRY(host_addrs, 1), }; ERL_NIF_INIT(Elixir.Libp2p, nif_funcs, NULL, NULL, NULL, NULL) diff --git a/libp2p/main.go b/libp2p/main.go index 04d89358e..b2c7128b1 100644 --- a/libp2p/main.go +++ b/libp2p/main.go @@ -25,6 +25,16 @@ import ( //export PermanentAddrTTL const PermanentAddrTTL = peerstore.PermanentAddrTTL +/***********/ +/* Helpers */ +/***********/ + +func callGetter[T any, R any](h C.uintptr_t, g func(T) R) C.uintptr_t { + recver := cgo.Handle(h).Value().(T) + prop := g(recver) + return C.uintptr_t(cgo.NewHandle(prop)) +} + /*********/ /* Tests */ /*********/ @@ -43,23 +53,14 @@ func TestSendMessage(procId C.erl_pid_t) { }() } -/***********/ -/* Helpers */ -/***********/ - -func callGetter[T any, R any](h C.uintptr_t, g func(T) R) C.uintptr_t { - recver := cgo.Handle(h).Value().(T) - prop := g(recver) - return C.uintptr_t(cgo.NewHandle(prop)) -} - /*********/ /* Utils */ /*********/ //export ListenAddrStrings -func ListenAddrStrings(listenAddr *C.char) C.uintptr_t { - addr := libp2p.ListenAddrStrings(C.GoString(listenAddr)) +func ListenAddrStrings(listenAddr string) C.uintptr_t { + // TODO: this function is variadic + addr := libp2p.ListenAddrStrings(listenAddr) return C.uintptr_t(cgo.NewHandle(addr)) } @@ -137,8 +138,8 @@ func (ps C.uintptr_t) AddAddrs(id, addrs C.uintptr_t, ttl uint64) { /* Stream methods */ /******************/ -//export Read -func (s C.uintptr_t) Read(len uint, buffer *C.char) int { +//export StreamRead +func (s C.uintptr_t) StreamRead(len uint, buffer *C.char) int { stream := cgo.Handle(s).Value().(network.Stream) goBuffer := make([]byte, len) n, err := stream.Read(goBuffer) @@ -149,8 +150,8 @@ func (s C.uintptr_t) Read(len uint, buffer *C.char) int { return n } -//export Write -func (s C.uintptr_t) Write(len uint, buffer *C.char) int { +//export StreamWrite +func (s C.uintptr_t) StreamWrite(len uint, buffer *C.char) int { stream := cgo.Handle(s).Value().(network.Stream) goBuffer := C.GoBytes(unsafe.Pointer(buffer), C.int(len)) n, err := stream.Write(goBuffer) diff --git a/test/libp2p_test.exs b/test/libp2p_test.exs index 339d5d820..c6ed7f93a 100644 --- a/test/libp2p_test.exs +++ b/test/libp2p_test.exs @@ -33,15 +33,15 @@ defmodule Libp2pTest do :ok = Libp2p.host_close(host) end - test "Start two hosts, and play one round of ping-pong" do - # Setup sender - {:ok, sender} = Libp2p.host_new() - # Setup receiver - {:ok, recver} = Libp2p.host_new() + # test "Start two hosts, and play one round of ping-pong" do + # # Setup sender + # {:ok, sender} = Libp2p.host_new() + # # Setup receiver + # {:ok, recver} = Libp2p.host_new() - :ok = Libp2p.host_set_stream_handler(recver, "/pong") + # :ok = Libp2p.host_set_stream_handler(recver, "/pong") - :ok = Libp2p.host_close(host1) - :ok = Libp2p.host_close(host2) - end + # :ok = Libp2p.host_close(host1) + # :ok = Libp2p.host_close(host2) + # end end From 685653104fc546225fbad570acdc3c2b53b2675e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1?= <47506558+MegaRedHand@users.noreply.github.com> Date: Mon, 31 Jul 2023 16:03:06 -0300 Subject: [PATCH 13/43] Port NewStream method --- lib/libp2p.ex | 8 ++++++++ libp2p/libp2p.c | 16 ++++++++++++++-- libp2p/main.go | 23 ++++++++++++----------- 3 files changed, 34 insertions(+), 13 deletions(-) diff --git a/lib/libp2p.ex b/lib/libp2p.ex index 7f31137ef..23b3e9429 100644 --- a/lib/libp2p.ex +++ b/lib/libp2p.ex @@ -73,6 +73,14 @@ defmodule Libp2p do raise "NIF listen_addr_strings not implemented" end + @doc """ + host_new_stream creates a new `Stream` connected to the + peer with the given id, using the protocol with given id. + """ + def host_new_stream(_host, _peer_id, _protocol_id) do + raise "NIF host_new_stream not implemented" + end + @doc """ host_peerstore gets the `Peerstore` of the given `Host`. """ diff --git a/libp2p/libp2p.c b/libp2p/libp2p.c index 1d9ea08da..2e732d277 100644 --- a/libp2p/libp2p.c +++ b/libp2p/libp2p.c @@ -97,7 +97,7 @@ ERL_FUNCTION(listen_addr_strings) ERL_FUNCTION(host_new) { - int result = New(0, NULL); + uintptr_t result = New(0, NULL); return get_handle_result(env, result); } @@ -130,12 +130,23 @@ ERL_FUNCTION(host_set_stream_handler) return enif_make_atom(env, "ok"); } +ERL_FUNCTION(host_new_stream) +{ + uintptr_t handle = get_handle_from_term(env, argv[0]); + uintptr_t id = get_handle_from_term(env, argv[1]); + + char proto_id[PID_LENGTH]; + enif_get_string(env, argv[1], proto_id, PID_LENGTH, ERL_NIF_UTF8); + + int result = NewStream(handle, id, proto_id); + return get_handle_result(env, result); +} + ERL_FUNCTION_GETTER(host_peerstore, Peerstore) ERL_FUNCTION_GETTER(host_id, ID) ERL_FUNCTION_GETTER(host_addrs, Addrs) /* Functions left to port -- NewStream - AddAddrs - StreamRead - StreamWrite @@ -150,6 +161,7 @@ static ErlNifFunc nif_funcs[] = { NIF_ENTRY(host_new, 0), NIF_ENTRY(host_close, 1), NIF_ENTRY(host_set_stream_handler, 2), + NIF_ENTRY(host_new_stream, 3), NIF_ENTRY(host_peerstore, 1), NIF_ENTRY(host_id, 1), NIF_ENTRY(host_addrs, 1), diff --git a/libp2p/main.go b/libp2p/main.go index b2c7128b1..3ca1f40d9 100644 --- a/libp2p/main.go +++ b/libp2p/main.go @@ -96,6 +96,18 @@ func (h C.uintptr_t) SetStreamHandler(protoId *C.char, procId C.erl_pid_t) { host.SetStreamHandler(protocol.ID(C.GoString(protoId)), handler) } +//export NewStream +func (h C.uintptr_t) NewStream(pid C.uintptr_t, protoId *C.char) C.uintptr_t { + host := cgo.Handle(h).Value().(host.Host) + peerId := cgo.Handle(pid).Value().(peer.ID) + // TODO: revisit context.TODO() and add multi-protocol support + stream, err := host.NewStream(context.TODO(), peerId, protocol.ID(C.GoString(protoId))) + if err != nil { + return 0 + } + return C.uintptr_t(cgo.NewHandle(stream)) +} + //export Peerstore func (h C.uintptr_t) Peerstore() C.uintptr_t { return callGetter(h, host.Host.Peerstore) @@ -111,17 +123,6 @@ func (h C.uintptr_t) Addrs() C.uintptr_t { return callGetter(h, host.Host.Addrs) } -//export NewStream -func (h C.uintptr_t) NewStream(pid C.uintptr_t, protoId *C.char) C.uintptr_t { - host := cgo.Handle(h).Value().(host.Host) - peerId := cgo.Handle(pid).Value().(peer.ID) - stream, err := host.NewStream(context.TODO(), peerId, protocol.ID(C.GoString(protoId))) - if err != nil { - return 0 - } - return C.uintptr_t(cgo.NewHandle(stream)) -} - /*********************/ /* Peerstore methods */ /*********************/ From 350b96099213124376af16a4729fa7c1887fc4ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1?= <47506558+MegaRedHand@users.noreply.github.com> Date: Mon, 31 Jul 2023 16:18:13 -0300 Subject: [PATCH 14/43] Port Peerstore.AddAddrs --- lib/libp2p.ex | 22 +++++++++++++++------- libp2p/libp2p.c | 22 +++++++++++++++++++--- test/libp2p_test.exs | 4 ++-- 3 files changed, 36 insertions(+), 12 deletions(-) diff --git a/lib/libp2p.ex b/lib/libp2p.ex index 23b3e9429..b0d0f7b90 100644 --- a/lib/libp2p.ex +++ b/lib/libp2p.ex @@ -64,17 +64,16 @@ defmodule Libp2p do end @doc """ - `listen_addr_strings` configures libp2p to listen on the - given (unparsed) addresses. Returns an `Option` that can be passed to `host_new` - as an argument. + as an argument to configures libp2p to listen on the + given (unparsed) addresses. """ def listen_addr_strings(_addr) do raise "NIF listen_addr_strings not implemented" end @doc """ - host_new_stream creates a new `Stream` connected to the + Creates a new `Stream` connected to the peer with the given id, using the protocol with given id. """ def host_new_stream(_host, _peer_id, _protocol_id) do @@ -82,23 +81,32 @@ defmodule Libp2p do end @doc """ - host_peerstore gets the `Peerstore` of the given `Host`. + Gets the `Peerstore` of the given `Host`. """ def host_peerstore(_host) do raise "NIF host_peerstore not implemented" end @doc """ - host_id gets the `ID` of the given `Host`. + Gets the `ID` of the given `Host`. """ def host_id(_host) do raise "NIF host_id not implemented" end @doc """ - host_id gets the addresses of the given `Host`. + Gets the addresses of the given `Host`. """ def host_addrs(_host) do raise "NIF host_addrs not implemented" end + + @doc """ + Adds the addresses of the peer with the given ID to + the `Peerstore`. The addresses are valid for the given + TTL. + """ + def peerstore_add_addrs(_peerstore, _peer_id, _addrs, _ttl) do + raise "NIF peerstore_add_addrs not implemented" + end end diff --git a/libp2p/libp2p.c b/libp2p/libp2p.c index 2e732d277..390f4e440 100644 --- a/libp2p/libp2p.c +++ b/libp2p/libp2p.c @@ -88,7 +88,7 @@ ERL_FUNCTION(listen_addr_strings) ListenAddrStrings(go_listenAddr); - return enif_make_atom(env, "ok"); + return enif_make_atom(env, "nil"); } /****************/ @@ -107,7 +107,7 @@ ERL_FUNCTION(host_close) HostClose(handle); - return enif_make_atom(env, "ok"); + return enif_make_atom(env, "nil"); } ERL_FUNCTION(host_set_stream_handler) @@ -146,8 +146,23 @@ ERL_FUNCTION_GETTER(host_peerstore, Peerstore) ERL_FUNCTION_GETTER(host_id, ID) ERL_FUNCTION_GETTER(host_addrs, Addrs) +/*********************/ +/* Peerstore methods */ +/*********************/ + +ERL_FUNCTION(peerstore_add_addrs) +{ + uintptr_t ps = get_handle_from_term(env, argv[0]); + uintptr_t id = get_handle_from_term(env, argv[1]); + uintptr_t addrs = get_handle_from_term(env, argv[2]); + u_long ttl; + enif_get_uint64(env, argv[3], &ttl); + + AddAddrs(ps, id, addrs, ttl); + return enif_make_atom(env, "nil"); +} + /* Functions left to port -- AddAddrs - StreamRead - StreamWrite - StreamClose @@ -165,6 +180,7 @@ static ErlNifFunc nif_funcs[] = { NIF_ENTRY(host_peerstore, 1), NIF_ENTRY(host_id, 1), NIF_ENTRY(host_addrs, 1), + NIF_ENTRY(peerstore_add_addrs, 4), }; ERL_NIF_INIT(Elixir.Libp2p, nif_funcs, NULL, NULL, NULL, NULL) diff --git a/test/libp2p_test.exs b/test/libp2p_test.exs index c6ed7f93a..602e33143 100644 --- a/test/libp2p_test.exs +++ b/test/libp2p_test.exs @@ -23,14 +23,14 @@ defmodule Libp2pTest do test "Create and destroy host" do {:ok, host} = Libp2p.host_new() assert host != 0 - :ok = Libp2p.host_close(host) + Libp2p.host_close(host) end test "Set stream handler" do {:ok, host} = Libp2p.host_new() assert host != 0 :ok = Libp2p.host_set_stream_handler(host, "/my-app/amazing-protocol/1.0.1") - :ok = Libp2p.host_close(host) + Libp2p.host_close(host) end # test "Start two hosts, and play one round of ping-pong" do From b1678e40b0c9adafd18415fae2d129e55c4b3721 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1?= <47506558+MegaRedHand@users.noreply.github.com> Date: Mon, 31 Jul 2023 16:59:36 -0300 Subject: [PATCH 15/43] Port Stream methods --- lib/libp2p.ex | 21 +++++++++++++++++++++ libp2p/libp2p.c | 49 ++++++++++++++++++++++++++++++++++++++++++++----- libp2p/main.go | 12 ++++-------- 3 files changed, 69 insertions(+), 13 deletions(-) diff --git a/lib/libp2p.ex b/lib/libp2p.ex index b0d0f7b90..0a0ee1c74 100644 --- a/lib/libp2p.ex +++ b/lib/libp2p.ex @@ -109,4 +109,25 @@ defmodule Libp2p do def peerstore_add_addrs(_peerstore, _peer_id, _addrs, _ttl) do raise "NIF peerstore_add_addrs not implemented" end + + @doc """ + Reads bytes from the stream (up to a predefined maximum). + """ + def stream_read(_stream) do + raise "NIF stream_read not implemented" + end + + @doc """ + Writes data into the stream. + """ + def stream_write(_stream, _data) do + raise "NIF stream_write not implemented" + end + + @doc """ + Closes the stream. + """ + def stream_close(_stream) do + raise "NIF stream_close not implemented" + end end diff --git a/libp2p/libp2p.c b/libp2p/libp2p.c index 390f4e440..a4c3d1fc4 100644 --- a/libp2p/libp2p.c +++ b/libp2p/libp2p.c @@ -162,11 +162,47 @@ ERL_FUNCTION(peerstore_add_addrs) return enif_make_atom(env, "nil"); } -/* Functions left to port -- StreamRead -- StreamWrite -- StreamClose -*/ +/******************/ +/* Stream methods */ +/******************/ + +ERL_FUNCTION(stream_read) +{ + uintptr_t stream = get_handle_from_term(env, argv[0]); + + uint64_t len = 4096; + + char buffer[len]; + GoSlice go_buffer = {buffer, len, len}; + + uint64_t read = StreamRead(stream, go_buffer); + + return get_handle_result(env, read); +} + +ERL_FUNCTION(stream_write) +{ + uintptr_t stream = get_handle_from_term(env, argv[0]); + + uint32_t len; + enif_get_string_length(env, argv[1], &len, ERL_NIF_UTF8); + char data[len]; + enif_get_string(env, argv[1], data, len, ERL_NIF_UTF8); + GoSlice go_data = {data, len, len}; + + uint64_t read = StreamWrite(stream, go_data); + + return get_handle_result(env, read); +} + +ERL_FUNCTION(stream_close) +{ + uintptr_t stream = get_handle_from_term(env, argv[0]); + + StreamClose(stream); + + return enif_make_atom(env, "nil"); +} static ErlNifFunc nif_funcs[] = { NIF_ENTRY(hello, 0), @@ -181,6 +217,9 @@ static ErlNifFunc nif_funcs[] = { NIF_ENTRY(host_id, 1), NIF_ENTRY(host_addrs, 1), NIF_ENTRY(peerstore_add_addrs, 4), + NIF_ENTRY(stream_read, 1), + NIF_ENTRY(stream_write, 2), + NIF_ENTRY(stream_close, 1), }; ERL_NIF_INIT(Elixir.Libp2p, nif_funcs, NULL, NULL, NULL, NULL) diff --git a/libp2p/main.go b/libp2p/main.go index 3ca1f40d9..1cea804e3 100644 --- a/libp2p/main.go +++ b/libp2p/main.go @@ -11,7 +11,6 @@ import ( "context" "runtime/cgo" "time" - "unsafe" "github.com/libp2p/go-libp2p" "github.com/libp2p/go-libp2p/core/host" @@ -140,11 +139,9 @@ func (ps C.uintptr_t) AddAddrs(id, addrs C.uintptr_t, ttl uint64) { /******************/ //export StreamRead -func (s C.uintptr_t) StreamRead(len uint, buffer *C.char) int { +func (s C.uintptr_t) StreamRead(buffer []byte) int { stream := cgo.Handle(s).Value().(network.Stream) - goBuffer := make([]byte, len) - n, err := stream.Read(goBuffer) - C.memcpy(unsafe.Pointer(buffer), unsafe.Pointer(&goBuffer[0]), C.size_t(n)) + n, err := stream.Read(buffer) if err != nil { return -1 } @@ -152,10 +149,9 @@ func (s C.uintptr_t) StreamRead(len uint, buffer *C.char) int { } //export StreamWrite -func (s C.uintptr_t) StreamWrite(len uint, buffer *C.char) int { +func (s C.uintptr_t) StreamWrite(data []byte) int { stream := cgo.Handle(s).Value().(network.Stream) - goBuffer := C.GoBytes(unsafe.Pointer(buffer), C.int(len)) - n, err := stream.Write(goBuffer) + n, err := stream.Write(data) if err != nil { return -1 } From 34fac42e970ffaa0743a84e6413539b20160db6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1?= <47506558+MegaRedHand@users.noreply.github.com> Date: Mon, 31 Jul 2023 17:27:30 -0300 Subject: [PATCH 16/43] Add integration test for bindings --- test/libp2p_test.exs | 59 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 50 insertions(+), 9 deletions(-) diff --git a/test/libp2p_test.exs b/test/libp2p_test.exs index 602e33143..00238a2d6 100644 --- a/test/libp2p_test.exs +++ b/test/libp2p_test.exs @@ -33,15 +33,56 @@ defmodule Libp2pTest do Libp2p.host_close(host) end - # test "Start two hosts, and play one round of ping-pong" do - # # Setup sender - # {:ok, sender} = Libp2p.host_new() - # # Setup receiver - # {:ok, recver} = Libp2p.host_new() + test "Start two hosts, and play one round of ping-pong" do + # Setup sender + {:ok, sender} = Libp2p.host_new() + # Setup receiver + {:ok, recver} = Libp2p.host_new() - # :ok = Libp2p.host_set_stream_handler(recver, "/pong") + # (recver) Set stream handler + :ok = Libp2p.host_set_stream_handler(recver, "/pong") - # :ok = Libp2p.host_close(host1) - # :ok = Libp2p.host_close(host2) - # end + # (sender) Add recver address to peerstore + {:ok, peerstore} = Libp2p.host_peerstore(sender) + {:ok, id} = Libp2p.host_id(recver) + {:ok, addrs} = Libp2p.host_addrs(recver) + + Libp2p.peerstore_add_addrs( + peerstore, + id, + addrs, + 2_512_512 + ) + + # (sender) Create stream sender -> recver + {:ok, send} = Libp2p.host_new_stream(sender, id, "/pong") + + # (sender) Write "ping" to stream + {:ok, 4} = Libp2p.stream_write(send, "ping") + + # (recver) Receive the stream via the configured stream handler + {:ok, recv} = + receive do + msg -> msg + after + 1_000 -> :timeout + end + + # (recver) Read the "ping" message from the stream + {:ok, "ping"} = Libp2p.stream_read(recv) + + # (recver) Write "pong" to the stream + {:ok, 4} = Libp2p.stream_write(recv, "pong") + + # (sender) Read the "pong" message from the stream + {:ok, "pong"} = Libp2p.stream_read(send) + + # Close both streams + Libp2p.stream_close(send) + Libp2p.stream_close(recv) + + # Close both hosts + Libp2p.host_close(sender) + Libp2p.host_close(recver) + end end From 8cf9b178ea0c0c23b5d38553a0b12ce2eed58a49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1?= <47506558+MegaRedHand@users.noreply.github.com> Date: Mon, 31 Jul 2023 17:59:46 -0300 Subject: [PATCH 17/43] Fix test --- libp2p/libp2p.c | 48 +++++++++++++++++++++++++++++++------------- libp2p/main.go | 9 +++++++++ test/libp2p_test.exs | 10 ++++----- 3 files changed, 48 insertions(+), 19 deletions(-) diff --git a/libp2p/libp2p.c b/libp2p/libp2p.c index a4c3d1fc4..7bc949ae0 100644 --- a/libp2p/libp2p.c +++ b/libp2p/libp2p.c @@ -18,6 +18,7 @@ } const uint64_t PID_LENGTH = 1024; +const uint64_t BUFFER_SIZE = 4096; /***********/ /* Helpers */ @@ -39,6 +40,11 @@ static ERL_NIF_TERM get_handle_result(ErlNifEnv *env, uintptr_t handle) return enif_make_tuple2(env, enif_make_atom(env, "ok"), enif_make_uint64(env, handle)); } +static ERL_NIF_TERM make_error_msg(ErlNifEnv *env, const char *msg) +{ + return enif_make_tuple2(env, enif_make_atom(env, "error"), enif_make_string(env, msg, ERL_NIF_UTF8)); +} + /*********/ /* Tests */ /*********/ @@ -65,7 +71,7 @@ ERL_FUNCTION(test_send_message) if (!enif_self(env, pid)) { - return enif_make_atom(env, "error"); + return make_error_msg(env, "failed to get pid"); } TestSendMessage(pid); @@ -115,6 +121,7 @@ ERL_FUNCTION(host_set_stream_handler) uintptr_t handle = get_handle_from_term(env, argv[0]); char proto_id[PID_LENGTH]; + // TODO: use GoString enif_get_string(env, argv[1], proto_id, PID_LENGTH, ERL_NIF_UTF8); // TODO: This is a memory leak. @@ -122,7 +129,7 @@ ERL_FUNCTION(host_set_stream_handler) if (!enif_self(env, pid)) { - return enif_make_atom(env, "error"); + return make_error_msg(env, "failed to get pid"); } SetStreamHandler(handle, proto_id, (void *)pid); @@ -136,6 +143,7 @@ ERL_FUNCTION(host_new_stream) uintptr_t id = get_handle_from_term(env, argv[1]); char proto_id[PID_LENGTH]; + // TODO: use GoString enif_get_string(env, argv[1], proto_id, PID_LENGTH, ERL_NIF_UTF8); int result = NewStream(handle, id, proto_id); @@ -170,29 +178,41 @@ ERL_FUNCTION(stream_read) { uintptr_t stream = get_handle_from_term(env, argv[0]); - uint64_t len = 4096; - - char buffer[len]; - GoSlice go_buffer = {buffer, len, len}; + char buffer[BUFFER_SIZE]; + GoSlice go_buffer = {buffer, BUFFER_SIZE, BUFFER_SIZE}; uint64_t read = StreamRead(stream, go_buffer); - return get_handle_result(env, read); + if (read == -1) + { + return make_error_msg(env, "failed to read"); + } + + ERL_NIF_TERM msg = enif_make_string_len(env, buffer, read, ERL_NIF_UTF8); + + return enif_make_tuple2(env, enif_make_atom(env, "ok"), msg); } ERL_FUNCTION(stream_write) { uintptr_t stream = get_handle_from_term(env, argv[0]); - uint32_t len; - enif_get_string_length(env, argv[1], &len, ERL_NIF_UTF8); - char data[len]; - enif_get_string(env, argv[1], data, len, ERL_NIF_UTF8); - GoSlice go_data = {data, len, len}; + char data[BUFFER_SIZE]; + uint64_t len = enif_get_string(env, argv[1], data, BUFFER_SIZE, ERL_NIF_UTF8); + if (len <= 0) + { + return make_error_msg(env, "invalid string"); + } + GoSlice go_data = {data, len - 1, len - 1}; + + uint64_t written = StreamWrite(stream, go_data); - uint64_t read = StreamWrite(stream, go_data); + if (written == -1) + { + return make_error_msg(env, "failed to write"); + } - return get_handle_result(env, read); + return enif_make_tuple2(env, enif_make_atom(env, "ok"), enif_make_uint64(env, written)); } ERL_FUNCTION(stream_close) diff --git a/libp2p/main.go b/libp2p/main.go index 1cea804e3..34bf4abce 100644 --- a/libp2p/main.go +++ b/libp2p/main.go @@ -9,6 +9,7 @@ import "C" import ( "context" + "fmt" "runtime/cgo" "time" @@ -72,6 +73,8 @@ func New(len uint, options *C.uintptr_t) C.uintptr_t { // TODO: pass options h, err := libp2p.New() if err != nil { + // TODO: handle in better way + fmt.Println(err) return 0 } return C.uintptr_t(cgo.NewHandle(h)) @@ -102,6 +105,8 @@ func (h C.uintptr_t) NewStream(pid C.uintptr_t, protoId *C.char) C.uintptr_t { // TODO: revisit context.TODO() and add multi-protocol support stream, err := host.NewStream(context.TODO(), peerId, protocol.ID(C.GoString(protoId))) if err != nil { + // TODO: handle in better way + fmt.Println(err) return 0 } return C.uintptr_t(cgo.NewHandle(stream)) @@ -143,6 +148,8 @@ func (s C.uintptr_t) StreamRead(buffer []byte) int { stream := cgo.Handle(s).Value().(network.Stream) n, err := stream.Read(buffer) if err != nil { + // TODO: handle in better way + fmt.Println(err) return -1 } return n @@ -153,6 +160,8 @@ func (s C.uintptr_t) StreamWrite(data []byte) int { stream := cgo.Handle(s).Value().(network.Stream) n, err := stream.Write(data) if err != nil { + // TODO: handle in better way + fmt.Println(err) return -1 } return n diff --git a/test/libp2p_test.exs b/test/libp2p_test.exs index 00238a2d6..56b9982d3 100644 --- a/test/libp2p_test.exs +++ b/test/libp2p_test.exs @@ -55,10 +55,10 @@ defmodule Libp2pTest do ) # (sender) Create stream sender -> recver - {:ok, send} = Libp2p.host_new_stream(sender, id, "/pong") + {:ok, send} = Libp2p.host_new_stream(sender, id, ~c"/pong") # (sender) Write "ping" to stream - {:ok, 4} = Libp2p.stream_write(send, "ping") + {:ok, 4} = Libp2p.stream_write(send, ~c"ping") # (recver) Receive the stream via the configured stream handler {:ok, recv} = @@ -69,13 +69,13 @@ defmodule Libp2pTest do end # (recver) Read the "ping" message from the stream - {:ok, "ping"} = Libp2p.stream_read(recv) + {:ok, ~c"ping"} = Libp2p.stream_read(recv) # (recver) Write "pong" to the stream - {:ok, 4} = Libp2p.stream_write(recv, "pong") + {:ok, 4} = Libp2p.stream_write(recv, ~c"pong") # (sender) Read the "pong" message from the stream - {:ok, "pong"} = Libp2p.stream_read(send) + {:ok, ~c"pong"} = Libp2p.stream_read(send) # Close both streams Libp2p.stream_close(send) From aa75af31ed0a57301c3a5aae45d3820bd5dd94bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1?= <47506558+MegaRedHand@users.noreply.github.com> Date: Mon, 31 Jul 2023 18:00:38 -0300 Subject: [PATCH 18/43] Add disclaimers to docs --- lib/libp2p.ex | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/libp2p.ex b/lib/libp2p.ex index 0a0ee1c74..15dc21a79 100644 --- a/lib/libp2p.ex +++ b/lib/libp2p.ex @@ -112,13 +112,17 @@ defmodule Libp2p do @doc """ Reads bytes from the stream (up to a predefined maximum). + Note that the data is returned as a charlist. + TODO: return a binary. """ def stream_read(_stream) do raise "NIF stream_read not implemented" end @doc """ - Writes data into the stream. + Writes data into the stream. Note that the data must be + a charlist. + TODO: make it work with binaries. """ def stream_write(_stream, _data) do raise "NIF stream_write not implemented" From 545e14c462596ccfb0afd27a668bb232d8de57cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1?= <47506558+MegaRedHand@users.noreply.github.com> Date: Mon, 31 Jul 2023 18:09:59 -0300 Subject: [PATCH 19/43] Remove dummy tests --- lib/libp2p.ex | 33 --------------------------------- libp2p/libp2p.c | 37 ------------------------------------- libp2p/main.go | 18 ------------------ libp2p/utils.c | 17 ----------------- libp2p/utils.h | 2 -- test/libp2p_test.exs | 18 ------------------ 6 files changed, 125 deletions(-) diff --git a/lib/libp2p.ex b/lib/libp2p.ex index 15dc21a79..bcfef5dec 100644 --- a/lib/libp2p.ex +++ b/lib/libp2p.ex @@ -9,39 +9,6 @@ defmodule Libp2p do :erlang.load_nif(~c"./libp2p", 0) end - @doc """ - Hello world. - - ## Examples - - iex> Libp2p.hello() - :world - - """ - def hello do - raise "NIF hello not implemented" - end - - @doc """ - Test function. - - ## Examples - - iex> Libp2p.my_function(2, 3) - 8 - - """ - def my_function(_a, _b) do - raise "NIF my_function not implemented" - end - - @doc """ - Test function that sends a message asynchronously. - """ - def test_send_message() do - raise "NIF test_send_message not implemented" - end - @doc """ Creates a new Host. """ diff --git a/libp2p/libp2p.c b/libp2p/libp2p.c index 7bc949ae0..8b3c6c0ce 100644 --- a/libp2p/libp2p.c +++ b/libp2p/libp2p.c @@ -45,40 +45,6 @@ static ERL_NIF_TERM make_error_msg(ErlNifEnv *env, const char *msg) return enif_make_tuple2(env, enif_make_atom(env, "error"), enif_make_string(env, msg, ERL_NIF_UTF8)); } -/*********/ -/* Tests */ -/*********/ - -ERL_FUNCTION(hello) -{ - return enif_make_atom(env, "world"); -} - -ERL_FUNCTION(my_function) -{ - int a, b; - enif_get_int(env, argv[0], &a); - enif_get_int(env, argv[1], &b); - - int result = MyFunction(a, b); - - return enif_make_int(env, result); -} - -ERL_FUNCTION(test_send_message) -{ - ErlNifPid *pid = malloc(sizeof(ErlNifPid)); - - if (!enif_self(env, pid)) - { - return make_error_msg(env, "failed to get pid"); - } - - TestSendMessage(pid); - - return enif_make_atom(env, "ok"); -} - /*********/ /* Utils */ /*********/ @@ -225,9 +191,6 @@ ERL_FUNCTION(stream_close) } static ErlNifFunc nif_funcs[] = { - NIF_ENTRY(hello, 0), - NIF_ENTRY(my_function, 2), - NIF_ENTRY(test_send_message, 0), NIF_ENTRY(listen_addr_strings, 1), NIF_ENTRY(host_new, 0), NIF_ENTRY(host_close, 1), diff --git a/libp2p/main.go b/libp2p/main.go index 34bf4abce..a6b812f81 100644 --- a/libp2p/main.go +++ b/libp2p/main.go @@ -35,24 +35,6 @@ func callGetter[T any, R any](h C.uintptr_t, g func(T) R) C.uintptr_t { return C.uintptr_t(cgo.NewHandle(prop)) } -/*********/ -/* Tests */ -/*********/ - -//export MyFunction -func MyFunction(a, b int) int { - return a + 2*b -} - -//export TestSendMessage -func TestSendMessage(procId C.erl_pid_t) { - go func() { - // wait for 500 ms - time.Sleep(500 * time.Millisecond) - C.go_test_send_message(procId) - }() -} - /*********/ /* Utils */ /*********/ diff --git a/libp2p/utils.c b/libp2p/utils.c index 1c1861266..e4a4b9ec1 100644 --- a/libp2p/utils.c +++ b/libp2p/utils.c @@ -21,20 +21,3 @@ void send_message(erl_pid_t _pid, uintptr_t stream_handle) enif_free_env(env); } } - -void go_test_send_message(erl_pid_t _pid) -{ - // Passed as void* to avoid including erl_nif.h in the header. - ErlNifPid *pid = get_pid(_pid); - ErlNifEnv *env = enif_alloc_env(); - - ERL_NIF_TERM message = enif_make_tuple2(env, enif_make_atom(env, "ok"), enif_make_uint64(env, 5353)); - - int result = enif_send(NULL, pid, env, message); - // On error, the env isn't freed by the function. - if (!result) - { - enif_free_env(env); - } - free(pid); -} diff --git a/libp2p/utils.h b/libp2p/utils.h index 8d2a77554..5285f2b3d 100644 --- a/libp2p/utils.h +++ b/libp2p/utils.h @@ -5,5 +5,3 @@ typedef void *erl_pid_t; void send_message(erl_pid_t pid, uintptr_t stream); - -void go_test_send_message(erl_pid_t pid); diff --git a/test/libp2p_test.exs b/test/libp2p_test.exs index 56b9982d3..ff3f6143a 100644 --- a/test/libp2p_test.exs +++ b/test/libp2p_test.exs @@ -2,24 +2,6 @@ defmodule Libp2pTest do use ExUnit.Case doctest Libp2p - test "greets the world" do - assert Libp2p.hello() == :world - end - - test "my_function is a + 2 * b" do - assert Libp2p.my_function(5, 124) == 5 + 2 * 124 - end - - test "test_send_message sends a message" do - :ok = Libp2p.test_send_message() - - receive do - msg -> {:ok, 5353} = msg - after - 1_000 -> :timeout - end - end - test "Create and destroy host" do {:ok, host} = Libp2p.host_new() assert host != 0 From 26bfe1f452a3a40409152cc7c5c577aa8a175027 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1?= <47506558+MegaRedHand@users.noreply.github.com> Date: Mon, 31 Jul 2023 18:11:32 -0300 Subject: [PATCH 20/43] Add disclaimer --- lib/libp2p.ex | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/libp2p.ex b/lib/libp2p.ex index bcfef5dec..8747babd5 100644 --- a/lib/libp2p.ex +++ b/lib/libp2p.ex @@ -34,6 +34,8 @@ defmodule Libp2p do Returns an `Option` that can be passed to `host_new` as an argument to configures libp2p to listen on the given (unparsed) addresses. + Note that the address must be a charlist. + TODO: make it work with binaries. """ def listen_addr_strings(_addr) do raise "NIF listen_addr_strings not implemented" From 9c3546bda04746bb228dfdd954eefd0406e797e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1?= <47506558+MegaRedHand@users.noreply.github.com> Date: Mon, 31 Jul 2023 18:50:28 -0300 Subject: [PATCH 21/43] Add constant, and clean up --- lib/libp2p.ex | 5 ++++ libp2p/libp2p.c | 65 ++++++++++++++++++++++++-------------------- libp2p/main.go | 3 +- test/libp2p_test.exs | 7 +---- 4 files changed, 42 insertions(+), 38 deletions(-) diff --git a/lib/libp2p.ex b/lib/libp2p.ex index 8747babd5..6af25e125 100644 --- a/lib/libp2p.ex +++ b/lib/libp2p.ex @@ -9,6 +9,11 @@ defmodule Libp2p do :erlang.load_nif(~c"./libp2p", 0) end + @doc """ + The ttl for a "permanent address" (e.g. bootstrap nodes). + """ + def ttl_permanent_addr, do: 2 ** 63 - 1 + @doc """ Creates a new Host. """ diff --git a/libp2p/libp2p.c b/libp2p/libp2p.c index 8b3c6c0ce..a9127d309 100644 --- a/libp2p/libp2p.c +++ b/libp2p/libp2p.c @@ -4,14 +4,17 @@ #define ERL_FUNCTION(FUNCTION_NAME) static ERL_NIF_TERM FUNCTION_NAME(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) -#define ERL_FUNCTION_GETTER(NAME, GETTER) \ - ERL_FUNCTION(NAME) \ - { \ - uintptr_t host = get_handle_from_term(env, argv[0]); \ - uintptr_t host_id = GETTER(host); \ - return get_handle_result(env, host_id); \ +#define ERL_FUNCTION_GETTER(NAME, GETTER) \ + ERL_FUNCTION(NAME) \ + { \ + uintptr_t _handle = get_handle_from_term(env, argv[0]); \ + uintptr_t _res = GETTER(_handle); \ + return get_handle_result(env, _res); \ } +#define ERL_CONSTANT_UINT(NAME, VALUE) \ + ERL_FUNCTION(NAME) { return enif_make_uint64(env, (VALUE)); } + #define NIF_ENTRY(FUNCTION_NAME, ARITY) \ { \ #FUNCTION_NAME, ARITY, FUNCTION_NAME \ @@ -31,18 +34,23 @@ static uintptr_t get_handle_from_term(ErlNifEnv *env, ERL_NIF_TERM term) return handle; } +static ERL_NIF_TERM make_error_msg(ErlNifEnv *env, const char *msg) +{ + return enif_make_tuple2(env, enif_make_atom(env, "error"), enif_make_string(env, msg, ERL_NIF_UTF8)); +} + +static ERL_NIF_TERM make_ok_tuple2(ErlNifEnv *env, ERL_NIF_TERM term) +{ + return enif_make_tuple2(env, enif_make_atom(env, "ok"), term); +} + static ERL_NIF_TERM get_handle_result(ErlNifEnv *env, uintptr_t handle) { if (handle == 0) { - return enif_make_atom(env, "error"); + return make_error_msg(env, "invalid handle returned"); } - return enif_make_tuple2(env, enif_make_atom(env, "ok"), enif_make_uint64(env, handle)); -} - -static ERL_NIF_TERM make_error_msg(ErlNifEnv *env, const char *msg) -{ - return enif_make_tuple2(env, enif_make_atom(env, "error"), enif_make_string(env, msg, ERL_NIF_UTF8)); + return make_ok_tuple2(env, enif_make_uint64(env, handle)); } /*********/ @@ -51,16 +59,18 @@ static ERL_NIF_TERM make_error_msg(ErlNifEnv *env, const char *msg) ERL_FUNCTION(listen_addr_strings) { - uint32_t len; - enif_get_string_length(env, argv[0], &len, ERL_NIF_UTF8); - char addr_string[len]; - enif_get_string(env, argv[0], addr_string, len, ERL_NIF_UTF8); + char addr_string[PID_LENGTH]; + uint64_t len = enif_get_string(env, argv[0], addr_string, PID_LENGTH, ERL_NIF_UTF8); + if (len <= 0) + { + return make_error_msg(env, "invalid string"); + } - GoString go_listenAddr = {addr_string, len}; + GoString go_listenAddr = {addr_string, len - 1}; - ListenAddrStrings(go_listenAddr); + uintptr_t handle = ListenAddrStrings(go_listenAddr); - return enif_make_atom(env, "nil"); + return get_handle_result(env, handle); } /****************/ @@ -69,6 +79,7 @@ ERL_FUNCTION(listen_addr_strings) ERL_FUNCTION(host_new) { + // TODO: add option passing uintptr_t result = New(0, NULL); return get_handle_result(env, result); } @@ -153,10 +164,7 @@ ERL_FUNCTION(stream_read) { return make_error_msg(env, "failed to read"); } - - ERL_NIF_TERM msg = enif_make_string_len(env, buffer, read, ERL_NIF_UTF8); - - return enif_make_tuple2(env, enif_make_atom(env, "ok"), msg); + return make_ok_tuple2(env, enif_make_string_len(env, buffer, read, ERL_NIF_UTF8)); } ERL_FUNCTION(stream_write) @@ -165,6 +173,7 @@ ERL_FUNCTION(stream_write) char data[BUFFER_SIZE]; uint64_t len = enif_get_string(env, argv[1], data, BUFFER_SIZE, ERL_NIF_UTF8); + if (len <= 0) { return make_error_msg(env, "invalid string"); @@ -177,16 +186,12 @@ ERL_FUNCTION(stream_write) { return make_error_msg(env, "failed to write"); } - - return enif_make_tuple2(env, enif_make_atom(env, "ok"), enif_make_uint64(env, written)); + return make_ok_tuple2(env, enif_make_uint64(env, written)); } ERL_FUNCTION(stream_close) { - uintptr_t stream = get_handle_from_term(env, argv[0]); - - StreamClose(stream); - + StreamClose(get_handle_from_term(env, argv[0])); return enif_make_atom(env, "nil"); } diff --git a/libp2p/main.go b/libp2p/main.go index a6b812f81..dbe5b5cfa 100644 --- a/libp2p/main.go +++ b/libp2p/main.go @@ -153,8 +153,7 @@ func (s C.uintptr_t) StreamWrite(data []byte) int { func (s C.uintptr_t) StreamClose() { handle := cgo.Handle(s) defer handle.Delete() - stream := handle.Value().(network.Stream) - stream.Close() + handle.Value().(network.Stream).Close() } // NOTE: this is needed to build it as an archive (.a) diff --git a/test/libp2p_test.exs b/test/libp2p_test.exs index ff3f6143a..e8afd2635 100644 --- a/test/libp2p_test.exs +++ b/test/libp2p_test.exs @@ -29,12 +29,7 @@ defmodule Libp2pTest do {:ok, id} = Libp2p.host_id(recver) {:ok, addrs} = Libp2p.host_addrs(recver) - Libp2p.peerstore_add_addrs( - peerstore, - id, - addrs, - 2_512_512 - ) + Libp2p.peerstore_add_addrs(peerstore, id, addrs, Libp2p.ttl_permanent_addr()) # (sender) Create stream sender -> recver {:ok, send} = Libp2p.host_new_stream(sender, id, ~c"/pong") From b1e32bf1585eba9e4b4f47f1fb22def629ee5434 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1?= <47506558+MegaRedHand@users.noreply.github.com> Date: Mon, 31 Jul 2023 18:50:59 -0300 Subject: [PATCH 22/43] Remove unneeded macro --- libp2p/libp2p.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/libp2p/libp2p.c b/libp2p/libp2p.c index a9127d309..77752fb60 100644 --- a/libp2p/libp2p.c +++ b/libp2p/libp2p.c @@ -12,9 +12,6 @@ return get_handle_result(env, _res); \ } -#define ERL_CONSTANT_UINT(NAME, VALUE) \ - ERL_FUNCTION(NAME) { return enif_make_uint64(env, (VALUE)); } - #define NIF_ENTRY(FUNCTION_NAME, ARITY) \ { \ #FUNCTION_NAME, ARITY, FUNCTION_NAME \ From 6e7bd49c39aa8ad3956245e7f9fa3b971fa252b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1?= <47506558+MegaRedHand@users.noreply.github.com> Date: Tue, 1 Aug 2023 11:51:01 -0300 Subject: [PATCH 23/43] Add specs --- lib/libp2p.ex | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/lib/libp2p.ex b/lib/libp2p.ex index 6af25e125..8bcc0bf9b 100644 --- a/lib/libp2p.ex +++ b/lib/libp2p.ex @@ -9,14 +9,56 @@ defmodule Libp2p do :erlang.load_nif(~c"./libp2p", 0) end + @typedoc """ + A handle to a Go resource. + """ + @type handle :: integer + + @typedoc """ + A handle to a host.Host. + """ + @type host :: handle + + @typedoc """ + A handle to a peerstore.Peerstore. + """ + @type peerstore :: handle + + @typedoc """ + A handle to a peer.ID. + """ + @type peer_id :: handle + + @typedoc """ + A handle to a []multiaddr.MultiAddr. + """ + @type addrs :: handle + + @typedoc """ + A handle to a stream. + """ + @type stream :: handle + + @typedoc """ + A handle to an Option. + """ + @type option :: handle + + @typedoc """ + An error returned by this module. + """ + @type error :: {:error, charlist} + @doc """ The ttl for a "permanent address" (e.g. bootstrap nodes). """ + @spec ttl_permanent_addr :: integer def ttl_permanent_addr, do: 2 ** 63 - 1 @doc """ Creates a new Host. """ + @spec host_new :: {:ok, host} | error def host_new() do raise "NIF host_new not implemented" end @@ -24,6 +66,7 @@ defmodule Libp2p do @doc """ Deletes a Host. """ + @spec host_close(host) :: nil def host_close(_host) do raise "NIF host_close not implemented" end @@ -31,6 +74,7 @@ defmodule Libp2p do @doc """ Sets the stream handler associated to a protocol id. """ + @spec host_set_stream_handler(host, charlist) :: :ok | error def host_set_stream_handler(_host, _protocol_id) do raise "NIF host_set_stream_handler not implemented" end @@ -42,6 +86,7 @@ defmodule Libp2p do Note that the address must be a charlist. TODO: make it work with binaries. """ + @spec listen_addr_strings(charlist) :: {:ok, option} | error def listen_addr_strings(_addr) do raise "NIF listen_addr_strings not implemented" end @@ -50,6 +95,7 @@ defmodule Libp2p do Creates a new `Stream` connected to the peer with the given id, using the protocol with given id. """ + @spec host_new_stream(host, peer_id, charlist) :: {:ok, stream} | error def host_new_stream(_host, _peer_id, _protocol_id) do raise "NIF host_new_stream not implemented" end @@ -57,6 +103,7 @@ defmodule Libp2p do @doc """ Gets the `Peerstore` of the given `Host`. """ + @spec host_peerstore(host) :: {:ok, peerstore} | error def host_peerstore(_host) do raise "NIF host_peerstore not implemented" end @@ -64,6 +111,7 @@ defmodule Libp2p do @doc """ Gets the `ID` of the given `Host`. """ + @spec host_id(host) :: {:ok, peer_id} | error def host_id(_host) do raise "NIF host_id not implemented" end @@ -71,6 +119,7 @@ defmodule Libp2p do @doc """ Gets the addresses of the given `Host`. """ + @spec host_addrs(host) :: {:ok, addrs} | error def host_addrs(_host) do raise "NIF host_addrs not implemented" end @@ -80,6 +129,7 @@ defmodule Libp2p do the `Peerstore`. The addresses are valid for the given TTL. """ + @spec peerstore_add_addrs(peerstore, peer_id, addrs, integer) :: nil def peerstore_add_addrs(_peerstore, _peer_id, _addrs, _ttl) do raise "NIF peerstore_add_addrs not implemented" end @@ -89,6 +139,7 @@ defmodule Libp2p do Note that the data is returned as a charlist. TODO: return a binary. """ + @spec stream_read(stream) :: {:ok, charlist} | error def stream_read(_stream) do raise "NIF stream_read not implemented" end @@ -98,6 +149,7 @@ defmodule Libp2p do a charlist. TODO: make it work with binaries. """ + @spec stream_write(stream, charlist) :: :ok | error def stream_write(_stream, _data) do raise "NIF stream_write not implemented" end @@ -105,6 +157,7 @@ defmodule Libp2p do @doc """ Closes the stream. """ + @spec stream_close(stream) :: nil def stream_close(_stream) do raise "NIF stream_close not implemented" end From 582cb50899491a56b354f84e211074272364e92a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1?= <47506558+MegaRedHand@users.noreply.github.com> Date: Tue, 1 Aug 2023 12:10:35 -0300 Subject: [PATCH 24/43] Use charlists and GoStrings --- libp2p/libp2p.c | 24 +++++++++++++++++------- libp2p/main.go | 16 ++++++++-------- test/libp2p_test.exs | 10 +++++----- 3 files changed, 30 insertions(+), 20 deletions(-) diff --git a/libp2p/libp2p.c b/libp2p/libp2p.c index 77752fb60..7a860c757 100644 --- a/libp2p/libp2p.c +++ b/libp2p/libp2p.c @@ -95,8 +95,13 @@ ERL_FUNCTION(host_set_stream_handler) uintptr_t handle = get_handle_from_term(env, argv[0]); char proto_id[PID_LENGTH]; - // TODO: use GoString - enif_get_string(env, argv[1], proto_id, PID_LENGTH, ERL_NIF_UTF8); + uint64_t len = enif_get_string(env, argv[1], proto_id, PID_LENGTH, ERL_NIF_UTF8); + + if (len <= 0) + { + return make_error_msg(env, "invalid string"); + } + GoString go_protoId = {proto_id, len - 1}; // TODO: This is a memory leak. ErlNifPid *pid = malloc(sizeof(ErlNifPid)); @@ -106,7 +111,7 @@ ERL_FUNCTION(host_set_stream_handler) return make_error_msg(env, "failed to get pid"); } - SetStreamHandler(handle, proto_id, (void *)pid); + SetStreamHandler(handle, go_protoId, (void *)pid); return enif_make_atom(env, "ok"); } @@ -117,10 +122,15 @@ ERL_FUNCTION(host_new_stream) uintptr_t id = get_handle_from_term(env, argv[1]); char proto_id[PID_LENGTH]; - // TODO: use GoString - enif_get_string(env, argv[1], proto_id, PID_LENGTH, ERL_NIF_UTF8); + uint64_t len = enif_get_string(env, argv[2], proto_id, PID_LENGTH, ERL_NIF_UTF8); - int result = NewStream(handle, id, proto_id); + if (len <= 0) + { + return make_error_msg(env, "invalid string"); + } + GoString go_protoId = {proto_id, len - 1}; + + int result = NewStream(handle, id, go_protoId); return get_handle_result(env, result); } @@ -183,7 +193,7 @@ ERL_FUNCTION(stream_write) { return make_error_msg(env, "failed to write"); } - return make_ok_tuple2(env, enif_make_uint64(env, written)); + return enif_make_atom(env, "ok"); } ERL_FUNCTION(stream_close) diff --git a/libp2p/main.go b/libp2p/main.go index dbe5b5cfa..419c92743 100644 --- a/libp2p/main.go +++ b/libp2p/main.go @@ -56,7 +56,7 @@ func New(len uint, options *C.uintptr_t) C.uintptr_t { h, err := libp2p.New() if err != nil { // TODO: handle in better way - fmt.Println(err) + fmt.Errorf("%s\n", err) return 0 } return C.uintptr_t(cgo.NewHandle(h)) @@ -70,25 +70,25 @@ func (h C.uintptr_t) HostClose() { } //export SetStreamHandler -func (h C.uintptr_t) SetStreamHandler(protoId *C.char, procId C.erl_pid_t) { +func (h C.uintptr_t) SetStreamHandler(protoId string, procId C.erl_pid_t) { handle := cgo.Handle(h) host := handle.Value().(host.Host) handler := func(stream network.Stream) { // NOTE: the stream handle should be deleted when calling Stream.Close() C.send_message(procId, C.uintptr_t(cgo.NewHandle(stream))) } - host.SetStreamHandler(protocol.ID(C.GoString(protoId)), handler) + host.SetStreamHandler(protocol.ID(protoId), handler) } //export NewStream -func (h C.uintptr_t) NewStream(pid C.uintptr_t, protoId *C.char) C.uintptr_t { +func (h C.uintptr_t) NewStream(pid C.uintptr_t, protoId string) C.uintptr_t { host := cgo.Handle(h).Value().(host.Host) peerId := cgo.Handle(pid).Value().(peer.ID) // TODO: revisit context.TODO() and add multi-protocol support - stream, err := host.NewStream(context.TODO(), peerId, protocol.ID(C.GoString(protoId))) + stream, err := host.NewStream(context.TODO(), peerId, protocol.ID(protoId)) if err != nil { // TODO: handle in better way - fmt.Println(err) + fmt.Errorf("%s\n", err) return 0 } return C.uintptr_t(cgo.NewHandle(stream)) @@ -131,7 +131,7 @@ func (s C.uintptr_t) StreamRead(buffer []byte) int { n, err := stream.Read(buffer) if err != nil { // TODO: handle in better way - fmt.Println(err) + fmt.Errorf("%s\n", err) return -1 } return n @@ -143,7 +143,7 @@ func (s C.uintptr_t) StreamWrite(data []byte) int { n, err := stream.Write(data) if err != nil { // TODO: handle in better way - fmt.Println(err) + fmt.Errorf("%s\n", err) return -1 } return n diff --git a/test/libp2p_test.exs b/test/libp2p_test.exs index e8afd2635..b5a83a5ca 100644 --- a/test/libp2p_test.exs +++ b/test/libp2p_test.exs @@ -11,7 +11,7 @@ defmodule Libp2pTest do test "Set stream handler" do {:ok, host} = Libp2p.host_new() assert host != 0 - :ok = Libp2p.host_set_stream_handler(host, "/my-app/amazing-protocol/1.0.1") + :ok = Libp2p.host_set_stream_handler(host, ~c"/my-app/amazing-protocol/1.0.1") Libp2p.host_close(host) end @@ -22,7 +22,7 @@ defmodule Libp2pTest do {:ok, recver} = Libp2p.host_new() # (recver) Set stream handler - :ok = Libp2p.host_set_stream_handler(recver, "/pong") + :ok = Libp2p.host_set_stream_handler(recver, ~c"/pong") # (sender) Add recver address to peerstore {:ok, peerstore} = Libp2p.host_peerstore(sender) @@ -35,21 +35,21 @@ defmodule Libp2pTest do {:ok, send} = Libp2p.host_new_stream(sender, id, ~c"/pong") # (sender) Write "ping" to stream - {:ok, 4} = Libp2p.stream_write(send, ~c"ping") + :ok = Libp2p.stream_write(send, ~c"ping") # (recver) Receive the stream via the configured stream handler {:ok, recv} = receive do msg -> msg after - 1_000 -> :timeout + 5_000 -> :timeout end # (recver) Read the "ping" message from the stream {:ok, ~c"ping"} = Libp2p.stream_read(recv) # (recver) Write "pong" to the stream - {:ok, 4} = Libp2p.stream_write(recv, ~c"pong") + :ok = Libp2p.stream_write(recv, ~c"pong") # (sender) Read the "pong" message from the stream {:ok, ~c"pong"} = Libp2p.stream_read(send) From 2176af495796b3213d98d7f275334b7c81a9cb72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1?= <47506558+MegaRedHand@users.noreply.github.com> Date: Tue, 1 Aug 2023 13:29:56 -0300 Subject: [PATCH 25/43] Allow receiving Options in host_new --- lib/libp2p.ex | 5 +++++ libp2p/libp2p.c | 22 +++++++++++++++++++--- libp2p/main.go | 11 +++++++---- test/libp2p_test.exs | 11 +++++++++-- 4 files changed, 40 insertions(+), 9 deletions(-) diff --git a/lib/libp2p.ex b/lib/libp2p.ex index 8bcc0bf9b..19ac68a7f 100644 --- a/lib/libp2p.ex +++ b/lib/libp2p.ex @@ -60,6 +60,11 @@ defmodule Libp2p do """ @spec host_new :: {:ok, host} | error def host_new() do + host_new([]) + end + + @spec host_new(list(option)) :: {:ok, host} | error + def host_new(_option_list) do raise "NIF host_new not implemented" end diff --git a/libp2p/libp2p.c b/libp2p/libp2p.c index 7a860c757..839dd08e2 100644 --- a/libp2p/libp2p.c +++ b/libp2p/libp2p.c @@ -76,8 +76,24 @@ ERL_FUNCTION(listen_addr_strings) ERL_FUNCTION(host_new) { - // TODO: add option passing - uintptr_t result = New(0, NULL); + const int MAX_OPTIONS = 256; + uintptr_t options[MAX_OPTIONS]; + int i = 0; + if (argc == 1) + { + if (!enif_is_list(env, argv[0])) + { + return make_error_msg(env, "options is not a list"); + } + ERL_NIF_TERM head, tail = argv[0]; + while (!enif_is_empty_list(env, tail) && i < MAX_OPTIONS) + { + enif_get_list_cell(env, tail, &head, &tail); + options[i++] = get_handle_from_term(env, head); + } + } + GoSlice go_options = {options, i, MAX_OPTIONS}; + uintptr_t result = HostNew(go_options); return get_handle_result(env, result); } @@ -204,7 +220,7 @@ ERL_FUNCTION(stream_close) static ErlNifFunc nif_funcs[] = { NIF_ENTRY(listen_addr_strings, 1), - NIF_ENTRY(host_new, 0), + NIF_ENTRY(host_new, 1), NIF_ENTRY(host_close, 1), NIF_ENTRY(host_set_stream_handler, 2), NIF_ENTRY(host_new_stream, 3), diff --git a/libp2p/main.go b/libp2p/main.go index 419c92743..d5ed850ca 100644 --- a/libp2p/main.go +++ b/libp2p/main.go @@ -50,10 +50,13 @@ func ListenAddrStrings(listenAddr string) C.uintptr_t { /* Host methods */ /****************/ -//export New -func New(len uint, options *C.uintptr_t) C.uintptr_t { - // TODO: pass options - h, err := libp2p.New() +//export HostNew +func HostNew(options []C.uintptr_t) C.uintptr_t { + optionsSlice := make([]libp2p.Option, len(options)) + for i := 0; i < len(options); i++ { + optionsSlice[i] = cgo.Handle(options[i]).Value().(libp2p.Option) + } + h, err := libp2p.New(optionsSlice...) if err != nil { // TODO: handle in better way fmt.Errorf("%s\n", err) diff --git a/test/libp2p_test.exs b/test/libp2p_test.exs index b5a83a5ca..bf12eabeb 100644 --- a/test/libp2p_test.exs +++ b/test/libp2p_test.exs @@ -15,11 +15,18 @@ defmodule Libp2pTest do Libp2p.host_close(host) end + test "listen_addr_strings parsing" do + {:ok, option} = Libp2p.listen_addr_strings(~c"/ip4/127.0.0.1/tcp/48787") + assert option != 0 + end + test "Start two hosts, and play one round of ping-pong" do # Setup sender - {:ok, sender} = Libp2p.host_new() + {:ok, addr} = Libp2p.listen_addr_strings(~c"/ip4/127.0.0.1/tcp/48787") + {:ok, sender} = Libp2p.host_new([addr]) # Setup receiver - {:ok, recver} = Libp2p.host_new() + {:ok, addr} = Libp2p.listen_addr_strings(~c"/ip4/127.0.0.1/tcp/48789") + {:ok, recver} = Libp2p.host_new([addr]) # (recver) Set stream handler :ok = Libp2p.host_set_stream_handler(recver, ~c"/pong") From 403050e181cd48000825d5cb242cabb8932f53a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1?= <47506558+MegaRedHand@users.noreply.github.com> Date: Tue, 1 Aug 2023 15:08:36 -0300 Subject: [PATCH 26/43] Revert CString -> GoString --- libp2p/libp2p.c | 10 +++------- libp2p/main.go | 15 +++++++++------ test/libp2p_test.exs | 2 +- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/libp2p/libp2p.c b/libp2p/libp2p.c index 839dd08e2..cf86ee934 100644 --- a/libp2p/libp2p.c +++ b/libp2p/libp2p.c @@ -63,9 +63,7 @@ ERL_FUNCTION(listen_addr_strings) return make_error_msg(env, "invalid string"); } - GoString go_listenAddr = {addr_string, len - 1}; - - uintptr_t handle = ListenAddrStrings(go_listenAddr); + uintptr_t handle = ListenAddrStrings(addr_string); return get_handle_result(env, handle); } @@ -117,7 +115,6 @@ ERL_FUNCTION(host_set_stream_handler) { return make_error_msg(env, "invalid string"); } - GoString go_protoId = {proto_id, len - 1}; // TODO: This is a memory leak. ErlNifPid *pid = malloc(sizeof(ErlNifPid)); @@ -127,7 +124,7 @@ ERL_FUNCTION(host_set_stream_handler) return make_error_msg(env, "failed to get pid"); } - SetStreamHandler(handle, go_protoId, (void *)pid); + SetStreamHandler(handle, proto_id, (void *)pid); return enif_make_atom(env, "ok"); } @@ -144,9 +141,8 @@ ERL_FUNCTION(host_new_stream) { return make_error_msg(env, "invalid string"); } - GoString go_protoId = {proto_id, len - 1}; - int result = NewStream(handle, id, go_protoId); + int result = NewStream(handle, id, proto_id); return get_handle_result(env, result); } diff --git a/libp2p/main.go b/libp2p/main.go index d5ed850ca..baae29dd2 100644 --- a/libp2p/main.go +++ b/libp2p/main.go @@ -40,9 +40,10 @@ func callGetter[T any, R any](h C.uintptr_t, g func(T) R) C.uintptr_t { /*********/ //export ListenAddrStrings -func ListenAddrStrings(listenAddr string) C.uintptr_t { +func ListenAddrStrings(listenAddr *C.char) C.uintptr_t { // TODO: this function is variadic - addr := libp2p.ListenAddrStrings(listenAddr) + goListenAddr := C.GoString(listenAddr) + addr := libp2p.ListenAddrStrings(goListenAddr) return C.uintptr_t(cgo.NewHandle(addr)) } @@ -73,22 +74,24 @@ func (h C.uintptr_t) HostClose() { } //export SetStreamHandler -func (h C.uintptr_t) SetStreamHandler(protoId string, procId C.erl_pid_t) { +func (h C.uintptr_t) SetStreamHandler(protoId *C.char, procId C.erl_pid_t) { handle := cgo.Handle(h) host := handle.Value().(host.Host) + goProtoId := protocol.ID(C.GoString(protoId)) handler := func(stream network.Stream) { // NOTE: the stream handle should be deleted when calling Stream.Close() C.send_message(procId, C.uintptr_t(cgo.NewHandle(stream))) } - host.SetStreamHandler(protocol.ID(protoId), handler) + host.SetStreamHandler(protocol.ID(goProtoId), handler) } //export NewStream -func (h C.uintptr_t) NewStream(pid C.uintptr_t, protoId string) C.uintptr_t { +func (h C.uintptr_t) NewStream(pid C.uintptr_t, protoId *C.char) C.uintptr_t { host := cgo.Handle(h).Value().(host.Host) peerId := cgo.Handle(pid).Value().(peer.ID) + goProtoId := protocol.ID(C.GoString(protoId)) // TODO: revisit context.TODO() and add multi-protocol support - stream, err := host.NewStream(context.TODO(), peerId, protocol.ID(protoId)) + stream, err := host.NewStream(context.TODO(), peerId, goProtoId) if err != nil { // TODO: handle in better way fmt.Errorf("%s\n", err) diff --git a/test/libp2p_test.exs b/test/libp2p_test.exs index bf12eabeb..26af85f14 100644 --- a/test/libp2p_test.exs +++ b/test/libp2p_test.exs @@ -49,7 +49,7 @@ defmodule Libp2pTest do receive do msg -> msg after - 5_000 -> :timeout + 1000 -> :timeout end # (recver) Read the "ping" message from the stream From b6deb3c4b4c21c567b330c9c5285c381f5c10a87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1?= <47506558+MegaRedHand@users.noreply.github.com> Date: Tue, 1 Aug 2023 16:24:02 -0300 Subject: [PATCH 27/43] Use macros to reduce C boilerplate --- lib/libp2p.ex | 6 +-- libp2p/libp2p.c | 110 ++++++++++++++++++------------------------- libp2p/main.go | 11 +++-- test/libp2p_test.exs | 19 ++++---- 4 files changed, 65 insertions(+), 81 deletions(-) diff --git a/lib/libp2p.ex b/lib/libp2p.ex index 19ac68a7f..78422f6ad 100644 --- a/lib/libp2p.ex +++ b/lib/libp2p.ex @@ -71,7 +71,7 @@ defmodule Libp2p do @doc """ Deletes a Host. """ - @spec host_close(host) :: nil + @spec host_close(host) :: :ok | error def host_close(_host) do raise "NIF host_close not implemented" end @@ -134,7 +134,7 @@ defmodule Libp2p do the `Peerstore`. The addresses are valid for the given TTL. """ - @spec peerstore_add_addrs(peerstore, peer_id, addrs, integer) :: nil + @spec peerstore_add_addrs(peerstore, peer_id, addrs, integer) :: :ok | error def peerstore_add_addrs(_peerstore, _peer_id, _addrs, _ttl) do raise "NIF peerstore_add_addrs not implemented" end @@ -162,7 +162,7 @@ defmodule Libp2p do @doc """ Closes the stream. """ - @spec stream_close(stream) :: nil + @spec stream_close(stream) :: :ok | error def stream_close(_stream) do raise "NIF stream_close not implemented" end diff --git a/libp2p/libp2p.c b/libp2p/libp2p.c index cf86ee934..71131ca58 100644 --- a/libp2p/libp2p.c +++ b/libp2p/libp2p.c @@ -8,10 +8,24 @@ ERL_FUNCTION(NAME) \ { \ uintptr_t _handle = get_handle_from_term(env, argv[0]); \ + OR_ERROR(_handle == 0, "invalid first argument"); \ uintptr_t _res = GETTER(_handle); \ return get_handle_result(env, _res); \ } +#define OR_ERROR(COND, MSG) \ + if (COND) \ + { \ + return make_error_msg(env, (MSG)); \ + } + +#define GET_HANDLE(TERM, NAME) \ + ({ \ + uintptr_t _handle = get_handle_from_term(env, (TERM)); \ + OR_ERROR(_handle == 0, "invalid " NAME); \ + _handle; \ + }) + #define NIF_ENTRY(FUNCTION_NAME, ARITY) \ { \ #FUNCTION_NAME, ARITY, FUNCTION_NAME \ @@ -27,8 +41,7 @@ const uint64_t BUFFER_SIZE = 4096; static uintptr_t get_handle_from_term(ErlNifEnv *env, ERL_NIF_TERM term) { uintptr_t handle; - enif_get_uint64(env, term, &handle); - return handle; + return enif_get_uint64(env, term, &handle) ? handle : 0; } static ERL_NIF_TERM make_error_msg(ErlNifEnv *env, const char *msg) @@ -43,10 +56,7 @@ static ERL_NIF_TERM make_ok_tuple2(ErlNifEnv *env, ERL_NIF_TERM term) static ERL_NIF_TERM get_handle_result(ErlNifEnv *env, uintptr_t handle) { - if (handle == 0) - { - return make_error_msg(env, "invalid handle returned"); - } + OR_ERROR(handle == 0, "invalid handle returned"); return make_ok_tuple2(env, enif_make_uint64(env, handle)); } @@ -58,10 +68,7 @@ ERL_FUNCTION(listen_addr_strings) { char addr_string[PID_LENGTH]; uint64_t len = enif_get_string(env, argv[0], addr_string, PID_LENGTH, ERL_NIF_UTF8); - if (len <= 0) - { - return make_error_msg(env, "invalid string"); - } + OR_ERROR(len <= 0, "invalid string"); uintptr_t handle = ListenAddrStrings(addr_string); @@ -74,21 +81,15 @@ ERL_FUNCTION(listen_addr_strings) ERL_FUNCTION(host_new) { + OR_ERROR(!enif_is_list(env, argv[0]), "options is not a list"); const int MAX_OPTIONS = 256; uintptr_t options[MAX_OPTIONS]; int i = 0; - if (argc == 1) + ERL_NIF_TERM head, tail = argv[0]; + while (!enif_is_empty_list(env, tail) && i < MAX_OPTIONS) { - if (!enif_is_list(env, argv[0])) - { - return make_error_msg(env, "options is not a list"); - } - ERL_NIF_TERM head, tail = argv[0]; - while (!enif_is_empty_list(env, tail) && i < MAX_OPTIONS) - { - enif_get_list_cell(env, tail, &head, &tail); - options[i++] = get_handle_from_term(env, head); - } + enif_get_list_cell(env, tail, &head, &tail); + options[i++] = GET_HANDLE(head, "option"); } GoSlice go_options = {options, i, MAX_OPTIONS}; uintptr_t result = HostNew(go_options); @@ -97,52 +98,41 @@ ERL_FUNCTION(host_new) ERL_FUNCTION(host_close) { - uintptr_t handle = get_handle_from_term(env, argv[0]); - - HostClose(handle); - - return enif_make_atom(env, "nil"); + uintptr_t host = GET_HANDLE(argv[0], "host"); + HostClose(host); + return enif_make_atom(env, "ok"); } ERL_FUNCTION(host_set_stream_handler) { - uintptr_t handle = get_handle_from_term(env, argv[0]); + uintptr_t host = GET_HANDLE(argv[0], "host"); char proto_id[PID_LENGTH]; uint64_t len = enif_get_string(env, argv[1], proto_id, PID_LENGTH, ERL_NIF_UTF8); - if (len <= 0) - { - return make_error_msg(env, "invalid string"); - } + OR_ERROR(len <= 0, "invalid string"); // TODO: This is a memory leak. ErlNifPid *pid = malloc(sizeof(ErlNifPid)); - if (!enif_self(env, pid)) - { - return make_error_msg(env, "failed to get pid"); - } + OR_ERROR(!enif_self(env, pid), "failed to get pid"); - SetStreamHandler(handle, proto_id, (void *)pid); + SetStreamHandler(host, proto_id, (void *)pid); return enif_make_atom(env, "ok"); } ERL_FUNCTION(host_new_stream) { - uintptr_t handle = get_handle_from_term(env, argv[0]); - uintptr_t id = get_handle_from_term(env, argv[1]); + uintptr_t host = GET_HANDLE(argv[0], "host"); + uintptr_t id = GET_HANDLE(argv[1], "peer id"); char proto_id[PID_LENGTH]; uint64_t len = enif_get_string(env, argv[2], proto_id, PID_LENGTH, ERL_NIF_UTF8); - if (len <= 0) - { - return make_error_msg(env, "invalid string"); - } + OR_ERROR(len <= 0, "invalid string"); - int result = NewStream(handle, id, proto_id); + int result = NewStream(host, id, proto_id); return get_handle_result(env, result); } @@ -156,14 +146,14 @@ ERL_FUNCTION_GETTER(host_addrs, Addrs) ERL_FUNCTION(peerstore_add_addrs) { - uintptr_t ps = get_handle_from_term(env, argv[0]); - uintptr_t id = get_handle_from_term(env, argv[1]); - uintptr_t addrs = get_handle_from_term(env, argv[2]); + uintptr_t ps = GET_HANDLE(argv[0], "peerstore"); + uintptr_t id = GET_HANDLE(argv[1], "peer id"); + uintptr_t addrs = GET_HANDLE(argv[2], "addrs"); u_long ttl; - enif_get_uint64(env, argv[3], &ttl); + OR_ERROR(!enif_get_uint64(env, argv[3], &ttl), "invalid TTL"); AddAddrs(ps, id, addrs, ttl); - return enif_make_atom(env, "nil"); + return enif_make_atom(env, "ok"); } /******************/ @@ -172,46 +162,38 @@ ERL_FUNCTION(peerstore_add_addrs) ERL_FUNCTION(stream_read) { - uintptr_t stream = get_handle_from_term(env, argv[0]); + uintptr_t stream = GET_HANDLE(argv[0], "stream"); char buffer[BUFFER_SIZE]; GoSlice go_buffer = {buffer, BUFFER_SIZE, BUFFER_SIZE}; uint64_t read = StreamRead(stream, go_buffer); + OR_ERROR(read == -1, "failed to read"); - if (read == -1) - { - return make_error_msg(env, "failed to read"); - } return make_ok_tuple2(env, enif_make_string_len(env, buffer, read, ERL_NIF_UTF8)); } ERL_FUNCTION(stream_write) { - uintptr_t stream = get_handle_from_term(env, argv[0]); + uintptr_t stream = GET_HANDLE(argv[0], "stream"); char data[BUFFER_SIZE]; uint64_t len = enif_get_string(env, argv[1], data, BUFFER_SIZE, ERL_NIF_UTF8); + OR_ERROR(len <= 0, "invalid string"); - if (len <= 0) - { - return make_error_msg(env, "invalid string"); - } GoSlice go_data = {data, len - 1, len - 1}; uint64_t written = StreamWrite(stream, go_data); + OR_ERROR(written == -1, "failed to write"); - if (written == -1) - { - return make_error_msg(env, "failed to write"); - } return enif_make_atom(env, "ok"); } ERL_FUNCTION(stream_close) { - StreamClose(get_handle_from_term(env, argv[0])); - return enif_make_atom(env, "nil"); + uintptr_t stream = GET_HANDLE(argv[0], "stream"); + StreamClose(stream); + return enif_make_atom(env, "ok"); } static ErlNifFunc nif_funcs[] = { diff --git a/libp2p/main.go b/libp2p/main.go index baae29dd2..b590088b7 100644 --- a/libp2p/main.go +++ b/libp2p/main.go @@ -10,6 +10,7 @@ import "C" import ( "context" "fmt" + "os" "runtime/cgo" "time" @@ -60,7 +61,7 @@ func HostNew(options []C.uintptr_t) C.uintptr_t { h, err := libp2p.New(optionsSlice...) if err != nil { // TODO: handle in better way - fmt.Errorf("%s\n", err) + fmt.Fprintf(os.Stderr, "%s\n", err) return 0 } return C.uintptr_t(cgo.NewHandle(h)) @@ -79,7 +80,7 @@ func (h C.uintptr_t) SetStreamHandler(protoId *C.char, procId C.erl_pid_t) { host := handle.Value().(host.Host) goProtoId := protocol.ID(C.GoString(protoId)) handler := func(stream network.Stream) { - // NOTE: the stream handle should be deleted when calling Stream.Close() + // NOTE: the stream handle should be deleted by calling Stream.Close() C.send_message(procId, C.uintptr_t(cgo.NewHandle(stream))) } host.SetStreamHandler(protocol.ID(goProtoId), handler) @@ -94,7 +95,7 @@ func (h C.uintptr_t) NewStream(pid C.uintptr_t, protoId *C.char) C.uintptr_t { stream, err := host.NewStream(context.TODO(), peerId, goProtoId) if err != nil { // TODO: handle in better way - fmt.Errorf("%s\n", err) + fmt.Fprintf(os.Stderr, "%s\n", err) return 0 } return C.uintptr_t(cgo.NewHandle(stream)) @@ -137,7 +138,7 @@ func (s C.uintptr_t) StreamRead(buffer []byte) int { n, err := stream.Read(buffer) if err != nil { // TODO: handle in better way - fmt.Errorf("%s\n", err) + fmt.Fprintf(os.Stderr, "%s\n", err) return -1 } return n @@ -149,7 +150,7 @@ func (s C.uintptr_t) StreamWrite(data []byte) int { n, err := stream.Write(data) if err != nil { // TODO: handle in better way - fmt.Errorf("%s\n", err) + fmt.Fprintf(os.Stderr, "%s\n", err) return -1 } return n diff --git a/test/libp2p_test.exs b/test/libp2p_test.exs index 26af85f14..237fa0a42 100644 --- a/test/libp2p_test.exs +++ b/test/libp2p_test.exs @@ -5,14 +5,14 @@ defmodule Libp2pTest do test "Create and destroy host" do {:ok, host} = Libp2p.host_new() assert host != 0 - Libp2p.host_close(host) + :ok = Libp2p.host_close(host) end test "Set stream handler" do {:ok, host} = Libp2p.host_new() assert host != 0 :ok = Libp2p.host_set_stream_handler(host, ~c"/my-app/amazing-protocol/1.0.1") - Libp2p.host_close(host) + :ok = Libp2p.host_close(host) end test "listen_addr_strings parsing" do @@ -21,6 +21,7 @@ defmodule Libp2pTest do end test "Start two hosts, and play one round of ping-pong" do + protocol_id = ~c"/pong" # Setup sender {:ok, addr} = Libp2p.listen_addr_strings(~c"/ip4/127.0.0.1/tcp/48787") {:ok, sender} = Libp2p.host_new([addr]) @@ -29,17 +30,17 @@ defmodule Libp2pTest do {:ok, recver} = Libp2p.host_new([addr]) # (recver) Set stream handler - :ok = Libp2p.host_set_stream_handler(recver, ~c"/pong") + :ok = Libp2p.host_set_stream_handler(recver, protocol_id) # (sender) Add recver address to peerstore {:ok, peerstore} = Libp2p.host_peerstore(sender) {:ok, id} = Libp2p.host_id(recver) {:ok, addrs} = Libp2p.host_addrs(recver) - Libp2p.peerstore_add_addrs(peerstore, id, addrs, Libp2p.ttl_permanent_addr()) + :ok = Libp2p.peerstore_add_addrs(peerstore, id, addrs, Libp2p.ttl_permanent_addr()) # (sender) Create stream sender -> recver - {:ok, send} = Libp2p.host_new_stream(sender, id, ~c"/pong") + {:ok, send} = Libp2p.host_new_stream(sender, id, protocol_id) # (sender) Write "ping" to stream :ok = Libp2p.stream_write(send, ~c"ping") @@ -62,11 +63,11 @@ defmodule Libp2pTest do {:ok, ~c"pong"} = Libp2p.stream_read(send) # Close both streams - Libp2p.stream_close(send) - Libp2p.stream_close(recv) + :ok = Libp2p.stream_close(send) + :ok = Libp2p.stream_close(recv) # Close both hosts - Libp2p.host_close(sender) - Libp2p.host_close(recver) + :ok = Libp2p.host_close(sender) + :ok = Libp2p.host_close(recver) end end From 9e54b4d45bbefa9de7859ca40c1fbfcffb5c4e26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1?= <47506558+MegaRedHand@users.noreply.github.com> Date: Tue, 1 Aug 2023 17:25:13 -0300 Subject: [PATCH 28/43] Simplify errors raised in stubs --- lib/libp2p.ex | 64 ++++++++++++++++++++------------------------------- 1 file changed, 25 insertions(+), 39 deletions(-) diff --git a/lib/libp2p.ex b/lib/libp2p.ex index 78422f6ad..7d3e0d885 100644 --- a/lib/libp2p.ex +++ b/lib/libp2p.ex @@ -59,30 +59,25 @@ defmodule Libp2p do Creates a new Host. """ @spec host_new :: {:ok, host} | error - def host_new() do - host_new([]) - end + def host_new(), do: host_new([]) @spec host_new(list(option)) :: {:ok, host} | error - def host_new(_option_list) do - raise "NIF host_new not implemented" - end + def host_new(_option_list), + do: :erlang.nif_error(:not_implemented) @doc """ Deletes a Host. """ @spec host_close(host) :: :ok | error - def host_close(_host) do - raise "NIF host_close not implemented" - end + def host_close(_host), + do: :erlang.nif_error(:not_implemented) @doc """ Sets the stream handler associated to a protocol id. """ @spec host_set_stream_handler(host, charlist) :: :ok | error - def host_set_stream_handler(_host, _protocol_id) do - raise "NIF host_set_stream_handler not implemented" - end + def host_set_stream_handler(_host, _protocol_id), + do: :erlang.nif_error(:not_implemented) @doc """ Returns an `Option` that can be passed to `host_new` @@ -92,42 +87,37 @@ defmodule Libp2p do TODO: make it work with binaries. """ @spec listen_addr_strings(charlist) :: {:ok, option} | error - def listen_addr_strings(_addr) do - raise "NIF listen_addr_strings not implemented" - end + def listen_addr_strings(_addr), + do: :erlang.nif_error(:not_implemented) @doc """ Creates a new `Stream` connected to the peer with the given id, using the protocol with given id. """ @spec host_new_stream(host, peer_id, charlist) :: {:ok, stream} | error - def host_new_stream(_host, _peer_id, _protocol_id) do - raise "NIF host_new_stream not implemented" - end + def host_new_stream(_host, _peer_id, _protocol_id), + do: :erlang.nif_error(:not_implemented) @doc """ Gets the `Peerstore` of the given `Host`. """ @spec host_peerstore(host) :: {:ok, peerstore} | error - def host_peerstore(_host) do - raise "NIF host_peerstore not implemented" - end + def host_peerstore(_host), + do: :erlang.nif_error(:not_implemented) @doc """ Gets the `ID` of the given `Host`. """ @spec host_id(host) :: {:ok, peer_id} | error - def host_id(_host) do - raise "NIF host_id not implemented" - end + def host_id(_host), + do: :erlang.nif_error(:not_implemented) @doc """ Gets the addresses of the given `Host`. """ @spec host_addrs(host) :: {:ok, addrs} | error - def host_addrs(_host) do - raise "NIF host_addrs not implemented" - end + def host_addrs(_host), + do: :erlang.nif_error(:not_implemented) @doc """ Adds the addresses of the peer with the given ID to @@ -135,9 +125,8 @@ defmodule Libp2p do TTL. """ @spec peerstore_add_addrs(peerstore, peer_id, addrs, integer) :: :ok | error - def peerstore_add_addrs(_peerstore, _peer_id, _addrs, _ttl) do - raise "NIF peerstore_add_addrs not implemented" - end + def peerstore_add_addrs(_peerstore, _peer_id, _addrs, _ttl), + do: :erlang.nif_error(:not_implemented) @doc """ Reads bytes from the stream (up to a predefined maximum). @@ -145,9 +134,8 @@ defmodule Libp2p do TODO: return a binary. """ @spec stream_read(stream) :: {:ok, charlist} | error - def stream_read(_stream) do - raise "NIF stream_read not implemented" - end + def stream_read(_stream), + do: :erlang.nif_error(:not_implemented) @doc """ Writes data into the stream. Note that the data must be @@ -155,15 +143,13 @@ defmodule Libp2p do TODO: make it work with binaries. """ @spec stream_write(stream, charlist) :: :ok | error - def stream_write(_stream, _data) do - raise "NIF stream_write not implemented" - end + def stream_write(_stream, _data), + do: :erlang.nif_error(:not_implemented) @doc """ Closes the stream. """ @spec stream_close(stream) :: :ok | error - def stream_close(_stream) do - raise "NIF stream_close not implemented" - end + def stream_close(_stream), + do: :erlang.nif_error(:not_implemented) end From 623191eb514461a8562e6398941f17ef5fee2499 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1?= <47506558+MegaRedHand@users.noreply.github.com> Date: Tue, 1 Aug 2023 18:12:08 -0300 Subject: [PATCH 29/43] Use binaries instead of charlists --- lib/libp2p.ex | 20 +++++--------- libp2p/libp2p.c | 66 +++++++++++++++++++++++++------------------- libp2p/main.go | 16 +++++++---- test/libp2p_test.exs | 19 +++++++------ 4 files changed, 64 insertions(+), 57 deletions(-) diff --git a/lib/libp2p.ex b/lib/libp2p.ex index 7d3e0d885..80f2543ae 100644 --- a/lib/libp2p.ex +++ b/lib/libp2p.ex @@ -47,7 +47,7 @@ defmodule Libp2p do @typedoc """ An error returned by this module. """ - @type error :: {:error, charlist} + @type error :: {:error, binary} @doc """ The ttl for a "permanent address" (e.g. bootstrap nodes). @@ -75,7 +75,7 @@ defmodule Libp2p do @doc """ Sets the stream handler associated to a protocol id. """ - @spec host_set_stream_handler(host, charlist) :: :ok | error + @spec host_set_stream_handler(host, binary) :: :ok | error def host_set_stream_handler(_host, _protocol_id), do: :erlang.nif_error(:not_implemented) @@ -83,10 +83,8 @@ defmodule Libp2p do Returns an `Option` that can be passed to `host_new` as an argument to configures libp2p to listen on the given (unparsed) addresses. - Note that the address must be a charlist. - TODO: make it work with binaries. """ - @spec listen_addr_strings(charlist) :: {:ok, option} | error + @spec listen_addr_strings(binary) :: {:ok, option} | error def listen_addr_strings(_addr), do: :erlang.nif_error(:not_implemented) @@ -94,7 +92,7 @@ defmodule Libp2p do Creates a new `Stream` connected to the peer with the given id, using the protocol with given id. """ - @spec host_new_stream(host, peer_id, charlist) :: {:ok, stream} | error + @spec host_new_stream(host, peer_id, binary) :: {:ok, stream} | error def host_new_stream(_host, _peer_id, _protocol_id), do: :erlang.nif_error(:not_implemented) @@ -130,19 +128,15 @@ defmodule Libp2p do @doc """ Reads bytes from the stream (up to a predefined maximum). - Note that the data is returned as a charlist. - TODO: return a binary. """ - @spec stream_read(stream) :: {:ok, charlist} | error + @spec stream_read(stream) :: {:ok, binary} | error def stream_read(_stream), do: :erlang.nif_error(:not_implemented) @doc """ - Writes data into the stream. Note that the data must be - a charlist. - TODO: make it work with binaries. + Writes data into the stream. """ - @spec stream_write(stream, charlist) :: :ok | error + @spec stream_write(stream, binary) :: :ok | error def stream_write(_stream, _data), do: :erlang.nif_error(:not_implemented) diff --git a/libp2p/libp2p.c b/libp2p/libp2p.c index 71131ca58..8c4196201 100644 --- a/libp2p/libp2p.c +++ b/libp2p/libp2p.c @@ -8,12 +8,12 @@ ERL_FUNCTION(NAME) \ { \ uintptr_t _handle = get_handle_from_term(env, argv[0]); \ - OR_ERROR(_handle == 0, "invalid first argument"); \ + IF_ERROR(_handle == 0, "invalid first argument"); \ uintptr_t _res = GETTER(_handle); \ return get_handle_result(env, _res); \ } -#define OR_ERROR(COND, MSG) \ +#define IF_ERROR(COND, MSG) \ if (COND) \ { \ return make_error_msg(env, (MSG)); \ @@ -22,7 +22,7 @@ #define GET_HANDLE(TERM, NAME) \ ({ \ uintptr_t _handle = get_handle_from_term(env, (TERM)); \ - OR_ERROR(_handle == 0, "invalid " NAME); \ + IF_ERROR(_handle == 0, "invalid " NAME); \ _handle; \ }) @@ -44,9 +44,17 @@ static uintptr_t get_handle_from_term(ErlNifEnv *env, ERL_NIF_TERM term) return enif_get_uint64(env, term, &handle) ? handle : 0; } -static ERL_NIF_TERM make_error_msg(ErlNifEnv *env, const char *msg) +static ERL_NIF_TERM _make_error_msg(ErlNifEnv *env, uint len, const char *msg) { - return enif_make_tuple2(env, enif_make_atom(env, "error"), enif_make_string(env, msg, ERL_NIF_UTF8)); + ERL_NIF_TERM msg_term; + u_char *buffer = enif_make_new_binary(env, len, &msg_term); + memcpy(buffer, msg, len); + return enif_make_tuple2(env, enif_make_atom(env, "error"), msg_term); +} + +static inline ERL_NIF_TERM make_error_msg(ErlNifEnv *env, const char *msg) +{ + return _make_error_msg(env, strlen(msg), msg); } static ERL_NIF_TERM make_ok_tuple2(ErlNifEnv *env, ERL_NIF_TERM term) @@ -56,7 +64,7 @@ static ERL_NIF_TERM make_ok_tuple2(ErlNifEnv *env, ERL_NIF_TERM term) static ERL_NIF_TERM get_handle_result(ErlNifEnv *env, uintptr_t handle) { - OR_ERROR(handle == 0, "invalid handle returned"); + IF_ERROR(handle == 0, "invalid handle returned"); return make_ok_tuple2(env, enif_make_uint64(env, handle)); } @@ -66,11 +74,11 @@ static ERL_NIF_TERM get_handle_result(ErlNifEnv *env, uintptr_t handle) ERL_FUNCTION(listen_addr_strings) { - char addr_string[PID_LENGTH]; - uint64_t len = enif_get_string(env, argv[0], addr_string, PID_LENGTH, ERL_NIF_UTF8); - OR_ERROR(len <= 0, "invalid string"); + ErlNifBinary bin; + IF_ERROR(!enif_inspect_binary(env, argv[0], &bin), "invalid address"); + GoString listen_addr = {(const char *)bin.data, bin.size}; - uintptr_t handle = ListenAddrStrings(addr_string); + uintptr_t handle = ListenAddrStrings(listen_addr); return get_handle_result(env, handle); } @@ -81,7 +89,7 @@ ERL_FUNCTION(listen_addr_strings) ERL_FUNCTION(host_new) { - OR_ERROR(!enif_is_list(env, argv[0]), "options is not a list"); + IF_ERROR(!enif_is_list(env, argv[0]), "options is not a list"); const int MAX_OPTIONS = 256; uintptr_t options[MAX_OPTIONS]; int i = 0; @@ -107,15 +115,14 @@ ERL_FUNCTION(host_set_stream_handler) { uintptr_t host = GET_HANDLE(argv[0], "host"); - char proto_id[PID_LENGTH]; - uint64_t len = enif_get_string(env, argv[1], proto_id, PID_LENGTH, ERL_NIF_UTF8); - - OR_ERROR(len <= 0, "invalid string"); + ErlNifBinary bin; + IF_ERROR(!enif_inspect_binary(env, argv[1], &bin), "invalid protocol ID"); + GoString proto_id = {(const char *)bin.data, bin.size}; // TODO: This is a memory leak. ErlNifPid *pid = malloc(sizeof(ErlNifPid)); - OR_ERROR(!enif_self(env, pid), "failed to get pid"); + IF_ERROR(!enif_self(env, pid), "failed to get pid"); SetStreamHandler(host, proto_id, (void *)pid); @@ -127,10 +134,9 @@ ERL_FUNCTION(host_new_stream) uintptr_t host = GET_HANDLE(argv[0], "host"); uintptr_t id = GET_HANDLE(argv[1], "peer id"); - char proto_id[PID_LENGTH]; - uint64_t len = enif_get_string(env, argv[2], proto_id, PID_LENGTH, ERL_NIF_UTF8); - - OR_ERROR(len <= 0, "invalid string"); + ErlNifBinary bin; + IF_ERROR(!enif_inspect_binary(env, argv[2], &bin), "invalid protocol ID"); + GoString proto_id = {(const char *)bin.data, bin.size}; int result = NewStream(host, id, proto_id); return get_handle_result(env, result); @@ -150,7 +156,7 @@ ERL_FUNCTION(peerstore_add_addrs) uintptr_t id = GET_HANDLE(argv[1], "peer id"); uintptr_t addrs = GET_HANDLE(argv[2], "addrs"); u_long ttl; - OR_ERROR(!enif_get_uint64(env, argv[3], &ttl), "invalid TTL"); + IF_ERROR(!enif_get_uint64(env, argv[3], &ttl), "invalid TTL"); AddAddrs(ps, id, addrs, ttl); return enif_make_atom(env, "ok"); @@ -168,23 +174,25 @@ ERL_FUNCTION(stream_read) GoSlice go_buffer = {buffer, BUFFER_SIZE, BUFFER_SIZE}; uint64_t read = StreamRead(stream, go_buffer); - OR_ERROR(read == -1, "failed to read"); + IF_ERROR(read == -1, "failed to read"); + + ERL_NIF_TERM bin_term; + u_char *bin_data = enif_make_new_binary(env, read, &bin_term); + memcpy(bin_data, buffer, read); - return make_ok_tuple2(env, enif_make_string_len(env, buffer, read, ERL_NIF_UTF8)); + return make_ok_tuple2(env, bin_term); } ERL_FUNCTION(stream_write) { uintptr_t stream = GET_HANDLE(argv[0], "stream"); - char data[BUFFER_SIZE]; - uint64_t len = enif_get_string(env, argv[1], data, BUFFER_SIZE, ERL_NIF_UTF8); - OR_ERROR(len <= 0, "invalid string"); - - GoSlice go_data = {data, len - 1, len - 1}; + ErlNifBinary bin; + IF_ERROR(!enif_inspect_binary(env, argv[1], &bin), "invalid data"); + GoSlice go_data = {bin.data, bin.size, bin.size}; uint64_t written = StreamWrite(stream, go_data); - OR_ERROR(written == -1, "failed to write"); + IF_ERROR(written == -1, "failed to write"); return enif_make_atom(env, "ok"); } diff --git a/libp2p/main.go b/libp2p/main.go index b590088b7..33290b3cd 100644 --- a/libp2p/main.go +++ b/libp2p/main.go @@ -12,6 +12,7 @@ import ( "fmt" "os" "runtime/cgo" + "strings" "time" "github.com/libp2p/go-libp2p" @@ -41,9 +42,10 @@ func callGetter[T any, R any](h C.uintptr_t, g func(T) R) C.uintptr_t { /*********/ //export ListenAddrStrings -func ListenAddrStrings(listenAddr *C.char) C.uintptr_t { +func ListenAddrStrings(listenAddr string) C.uintptr_t { // TODO: this function is variadic - goListenAddr := C.GoString(listenAddr) + // WARN: we clone the string because the underlying buffer is owned by Elixir + goListenAddr := strings.Clone(listenAddr) addr := libp2p.ListenAddrStrings(goListenAddr) return C.uintptr_t(cgo.NewHandle(addr)) } @@ -75,10 +77,11 @@ func (h C.uintptr_t) HostClose() { } //export SetStreamHandler -func (h C.uintptr_t) SetStreamHandler(protoId *C.char, procId C.erl_pid_t) { +func (h C.uintptr_t) SetStreamHandler(protoId string, procId C.erl_pid_t) { handle := cgo.Handle(h) host := handle.Value().(host.Host) - goProtoId := protocol.ID(C.GoString(protoId)) + // WARN: we clone the string because the underlying buffer is owned by Elixir + goProtoId := protocol.ID(strings.Clone(protoId)) handler := func(stream network.Stream) { // NOTE: the stream handle should be deleted by calling Stream.Close() C.send_message(procId, C.uintptr_t(cgo.NewHandle(stream))) @@ -87,10 +90,11 @@ func (h C.uintptr_t) SetStreamHandler(protoId *C.char, procId C.erl_pid_t) { } //export NewStream -func (h C.uintptr_t) NewStream(pid C.uintptr_t, protoId *C.char) C.uintptr_t { +func (h C.uintptr_t) NewStream(pid C.uintptr_t, protoId string) C.uintptr_t { host := cgo.Handle(h).Value().(host.Host) peerId := cgo.Handle(pid).Value().(peer.ID) - goProtoId := protocol.ID(C.GoString(protoId)) + // WARN: we clone the string because the underlying buffer is owned by Elixir + goProtoId := protocol.ID(strings.Clone(protoId)) // TODO: revisit context.TODO() and add multi-protocol support stream, err := host.NewStream(context.TODO(), peerId, goProtoId) if err != nil { diff --git a/test/libp2p_test.exs b/test/libp2p_test.exs index 237fa0a42..43c310493 100644 --- a/test/libp2p_test.exs +++ b/test/libp2p_test.exs @@ -11,24 +11,25 @@ defmodule Libp2pTest do test "Set stream handler" do {:ok, host} = Libp2p.host_new() assert host != 0 - :ok = Libp2p.host_set_stream_handler(host, ~c"/my-app/amazing-protocol/1.0.1") + :ok = Libp2p.host_set_stream_handler(host, "/my-app/amazing-protocol/1.0.1") :ok = Libp2p.host_close(host) end test "listen_addr_strings parsing" do - {:ok, option} = Libp2p.listen_addr_strings(~c"/ip4/127.0.0.1/tcp/48787") + {:ok, option} = Libp2p.listen_addr_strings("/ip4/127.0.0.1/tcp/48787") assert option != 0 end test "Start two hosts, and play one round of ping-pong" do - protocol_id = ~c"/pong" # Setup sender - {:ok, addr} = Libp2p.listen_addr_strings(~c"/ip4/127.0.0.1/tcp/48787") + {:ok, addr} = Libp2p.listen_addr_strings("/ip4/127.0.0.1/tcp/48787") {:ok, sender} = Libp2p.host_new([addr]) # Setup receiver - {:ok, addr} = Libp2p.listen_addr_strings(~c"/ip4/127.0.0.1/tcp/48789") + {:ok, addr} = Libp2p.listen_addr_strings("/ip4/127.0.0.1/tcp/48789") {:ok, recver} = Libp2p.host_new([addr]) + protocol_id = "/pong" + # (recver) Set stream handler :ok = Libp2p.host_set_stream_handler(recver, protocol_id) @@ -43,7 +44,7 @@ defmodule Libp2pTest do {:ok, send} = Libp2p.host_new_stream(sender, id, protocol_id) # (sender) Write "ping" to stream - :ok = Libp2p.stream_write(send, ~c"ping") + :ok = Libp2p.stream_write(send, "ping") # (recver) Receive the stream via the configured stream handler {:ok, recv} = @@ -54,13 +55,13 @@ defmodule Libp2pTest do end # (recver) Read the "ping" message from the stream - {:ok, ~c"ping"} = Libp2p.stream_read(recv) + {:ok, "ping"} = Libp2p.stream_read(recv) # (recver) Write "pong" to the stream - :ok = Libp2p.stream_write(recv, ~c"pong") + :ok = Libp2p.stream_write(recv, "pong") # (sender) Read the "pong" message from the stream - {:ok, ~c"pong"} = Libp2p.stream_read(send) + {:ok, "pong"} = Libp2p.stream_read(send) # Close both streams :ok = Libp2p.stream_close(send) From 7efb0cc457306df7f568355c59a8d53701e526ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1?= <47506558+MegaRedHand@users.noreply.github.com> Date: Wed, 2 Aug 2023 11:26:42 -0300 Subject: [PATCH 30/43] Change target name to `compile-native` --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 5c19802c3..167c05202 100644 --- a/Makefile +++ b/Makefile @@ -19,7 +19,7 @@ clean: -rm $(GO_ARCHIVES) $(GO_HEADERS) libp2p.so # Compile C and Go artifacts. -compile: libp2p.so +compile-native: libp2p.so # Run an interactive terminal with the main supervisor setup. iex: From ac314524fbfe165007b46e469fa39676b4bf68a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1?= <47506558+MegaRedHand@users.noreply.github.com> Date: Wed, 2 Aug 2023 12:03:28 -0300 Subject: [PATCH 31/43] Fix makefile --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 167c05202..83727c084 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: iex deps test clean compile +.PHONY: iex deps test clean compile-native BREW_PREFIX := $(shell brew --prefix) ERLANG_INCLUDES = $(BREW_PREFIX)/Cellar/erlang/26.0.2/lib/erlang/usr/include/ @@ -30,5 +30,5 @@ deps: mix deps.get # Run tests -test: compile +test: compile-native mix test From 315776bad5a9f81578cf147e2bbb1b0e505796a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1?= <47506558+MegaRedHand@users.noreply.github.com> Date: Thu, 3 Aug 2023 11:25:55 -0300 Subject: [PATCH 32/43] Move files to mimic rustler's skeleton --- Makefile | 19 +++++++++++-------- lib/{ => lambda_ethereum_consensus}/libp2p.ex | 11 +++-------- {libp2p => native/libp2p_nif}/go.mod | 0 {libp2p => native/libp2p_nif}/go.sum | 0 {libp2p => native/libp2p_nif}/libp2p.c | 0 {libp2p => native/libp2p_nif}/main.go | 0 {libp2p => native/libp2p_nif}/utils.c | 0 {libp2p => native/libp2p_nif}/utils.h | 0 8 files changed, 14 insertions(+), 16 deletions(-) rename lib/{ => lambda_ethereum_consensus}/libp2p.ex (94%) rename {libp2p => native/libp2p_nif}/go.mod (100%) rename {libp2p => native/libp2p_nif}/go.sum (100%) rename {libp2p => native/libp2p_nif}/libp2p.c (100%) rename {libp2p => native/libp2p_nif}/main.go (100%) rename {libp2p => native/libp2p_nif}/utils.c (100%) rename {libp2p => native/libp2p_nif}/utils.h (100%) diff --git a/Makefile b/Makefile index 83727c084..78afa2b44 100644 --- a/Makefile +++ b/Makefile @@ -3,23 +3,26 @@ BREW_PREFIX := $(shell brew --prefix) ERLANG_INCLUDES = $(BREW_PREFIX)/Cellar/erlang/26.0.2/lib/erlang/usr/include/ -GO_SOURCES = libp2p/main.go +LIBP2P_DIR = native/libp2p_nif +OUTPUT_DIR = priv/native + +GO_SOURCES = $(LIBP2P_DIR)/main.go GO_ARCHIVES := $(patsubst %.go,%.a,$(GO_SOURCES)) GO_HEADERS := $(patsubst %.go,%.h,$(GO_SOURCES)) -libp2p/%.a libp2p/%.h: libp2p/%.go - cd libp2p; go build -buildmode=c-archive $*.go +$(LIBP2P_DIR)/%.a $(LIBP2P_DIR)/%.h: $(LIBP2P_DIR)/%.go + cd $(LIBP2P_DIR); go build -buildmode=c-archive $*.go -libp2p.so: $(GO_ARCHIVES) $(GO_HEADERS) libp2p/libp2p.c libp2p/utils.c - gcc -Wall -Werror -dynamiclib -undefined dynamic_lookup -I $(ERLANG_INCLUDES) -o libp2p.so \ - libp2p/libp2p.c libp2p/utils.c $(GO_ARCHIVES) +$(OUTPUT_DIR)/libp2p_nif.so: $(GO_ARCHIVES) $(GO_HEADERS) $(LIBP2P_DIR)/libp2p.c $(LIBP2P_DIR)/utils.c + gcc -Wall -Werror -dynamiclib -undefined dynamic_lookup -I $(ERLANG_INCLUDES) -o $(OUTPUT_DIR)/libp2p_nif.so \ + $(LIBP2P_DIR)/libp2p.c $(LIBP2P_DIR)/utils.c $(GO_ARCHIVES) clean: - -rm $(GO_ARCHIVES) $(GO_HEADERS) libp2p.so + -rm $(GO_ARCHIVES) $(GO_HEADERS) $(OUTPUT_DIR)/* # Compile C and Go artifacts. -compile-native: libp2p.so +compile-native: $(OUTPUT_DIR)/libp2p_nif.so # Run an interactive terminal with the main supervisor setup. iex: diff --git a/lib/libp2p.ex b/lib/lambda_ethereum_consensus/libp2p.ex similarity index 94% rename from lib/libp2p.ex rename to lib/lambda_ethereum_consensus/libp2p.ex index 80f2543ae..8fd5fdf3a 100644 --- a/lib/libp2p.ex +++ b/lib/lambda_ethereum_consensus/libp2p.ex @@ -6,7 +6,8 @@ defmodule Libp2p do @on_load :load_nifs def load_nifs do - :erlang.load_nif(~c"./libp2p", 0) + dir = :code.priv_dir(:lambda_ethereum_consensus) + :erlang.load_nif(dir ++ ~c"/native/libp2p_nif", 0) end @typedoc """ @@ -55,14 +56,8 @@ defmodule Libp2p do @spec ttl_permanent_addr :: integer def ttl_permanent_addr, do: 2 ** 63 - 1 - @doc """ - Creates a new Host. - """ - @spec host_new :: {:ok, host} | error - def host_new(), do: host_new([]) - @spec host_new(list(option)) :: {:ok, host} | error - def host_new(_option_list), + def host_new(_option_list \\ []), do: :erlang.nif_error(:not_implemented) @doc """ diff --git a/libp2p/go.mod b/native/libp2p_nif/go.mod similarity index 100% rename from libp2p/go.mod rename to native/libp2p_nif/go.mod diff --git a/libp2p/go.sum b/native/libp2p_nif/go.sum similarity index 100% rename from libp2p/go.sum rename to native/libp2p_nif/go.sum diff --git a/libp2p/libp2p.c b/native/libp2p_nif/libp2p.c similarity index 100% rename from libp2p/libp2p.c rename to native/libp2p_nif/libp2p.c diff --git a/libp2p/main.go b/native/libp2p_nif/main.go similarity index 100% rename from libp2p/main.go rename to native/libp2p_nif/main.go diff --git a/libp2p/utils.c b/native/libp2p_nif/utils.c similarity index 100% rename from libp2p/utils.c rename to native/libp2p_nif/utils.c diff --git a/libp2p/utils.h b/native/libp2p_nif/utils.h similarity index 100% rename from libp2p/utils.h rename to native/libp2p_nif/utils.h From c478f181f5a531a9b1c103daed9cfa0c0b6d43ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1?= <47506558+MegaRedHand@users.noreply.github.com> Date: Thu, 3 Aug 2023 11:47:19 -0300 Subject: [PATCH 33/43] Fix path to erlang headers --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 78afa2b44..06dd55f7c 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ .PHONY: iex deps test clean compile-native -BREW_PREFIX := $(shell brew --prefix) -ERLANG_INCLUDES = $(BREW_PREFIX)/Cellar/erlang/26.0.2/lib/erlang/usr/include/ +ERLANG_DIR := $(shell asdf where erlang) +ERLANG_INCLUDES = $(ERLANG_DIR)/usr/include/ LIBP2P_DIR = native/libp2p_nif OUTPUT_DIR = priv/native From f32afe75218105737a9696fa5f6b55de80ad7483 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1?= <47506558+MegaRedHand@users.noreply.github.com> Date: Thu, 3 Aug 2023 13:11:04 -0300 Subject: [PATCH 34/43] Add native compilation to CI --- .github/workflows/ci.yml | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 126c10e94..b7de1b228 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,8 +13,29 @@ permissions: contents: read jobs: + compile-native: + name: Build native libraries + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Set up Go + # NOTE: this action comes with caching by default + uses: actions/setup-go@v4 + with: + go-version: 1.20 + - name: Cache output artifacts + id: output-cache + uses: actions/cache@v3 + with: + path: priv/native/*.so + key: ${{ runner.os }}-native-${{ hashFiles('native/**') }} + - name: Compile native code + if: steps.output-cache.outputs.cache-hit != 'true' + run: make compile-native + build: name: Build + needs: compile-native runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 @@ -24,7 +45,14 @@ jobs: version-type: strict version-file: .tool-versions env: - ImageOS: ubuntu20 + ImageOS: ubuntu20 + - name: Fetch native libraries + id: output-cache + uses: actions/cache/restore@v3 + with: + path: priv/native/*.so + key: ${{ runner.os }}-native-${{ hashFiles('native/**') }} + fail-on-cache-miss: true - name: Restore dependencies cache uses: actions/cache@v3 with: @@ -46,8 +74,10 @@ jobs: mix dialyzer --plt - name: Run dialyzer run: mix dialyzer --no-check + test: name: Test + needs: compile-native runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 @@ -58,6 +88,13 @@ jobs: version-file: .tool-versions env: ImageOS: ubuntu20 + - name: Fetch native libraries + id: output-cache + uses: actions/cache/restore@v3 + with: + path: priv/native/*.so + key: ${{ runner.os }}-native-${{ hashFiles('native/**') }} + fail-on-cache-miss: true - name: Restore dependencies cache uses: actions/cache@v3 with: From ede47634086f13ffbd6b5159c05f9945a51f1216 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1?= <47506558+MegaRedHand@users.noreply.github.com> Date: Thu, 3 Aug 2023 14:22:23 -0300 Subject: [PATCH 35/43] Install elixir for building step --- .github/workflows/ci.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b7de1b228..003a8678f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,6 +18,14 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 + - name: Set up Elixir + # NOTE: this is needed for the NIF header files + uses: erlef/setup-beam@v1 + with: + version-type: strict + version-file: .tool-versions + env: + ImageOS: ubuntu20 - name: Set up Go # NOTE: this action comes with caching by default uses: actions/setup-go@v4 @@ -29,6 +37,11 @@ jobs: with: path: priv/native/*.so key: ${{ runner.os }}-native-${{ hashFiles('native/**') }} + - name: DEBUG print + run: | + which erl || true + which iex || true + which mix || true - name: Compile native code if: steps.output-cache.outputs.cache-hit != 'true' run: make compile-native From 62a5d865903ed5f3faa702d3c9e196445b9efabe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1?= <47506558+MegaRedHand@users.noreply.github.com> Date: Thu, 3 Aug 2023 14:54:58 -0300 Subject: [PATCH 36/43] Add go install step to makefile --- Makefile | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 06dd55f7c..b1b2342e3 100644 --- a/Makefile +++ b/Makefile @@ -12,10 +12,12 @@ GO_HEADERS := $(patsubst %.go,%.h,$(GO_SOURCES)) $(LIBP2P_DIR)/%.a $(LIBP2P_DIR)/%.h: $(LIBP2P_DIR)/%.go - cd $(LIBP2P_DIR); go build -buildmode=c-archive $*.go + cd $(LIBP2P_DIR); \ + go install; \ + go build -buildmode=c-archive -tags only_go $*.go $(OUTPUT_DIR)/libp2p_nif.so: $(GO_ARCHIVES) $(GO_HEADERS) $(LIBP2P_DIR)/libp2p.c $(LIBP2P_DIR)/utils.c - gcc -Wall -Werror -dynamiclib -undefined dynamic_lookup -I $(ERLANG_INCLUDES) -o $(OUTPUT_DIR)/libp2p_nif.so \ + gcc -Wall -Werror -dynamiclib -undefined dynamic_lookup -I $(ERLANG_INCLUDES) -I $(LIBP2P_DIR) -o $(OUTPUT_DIR)/libp2p_nif.so \ $(LIBP2P_DIR)/libp2p.c $(LIBP2P_DIR)/utils.c $(GO_ARCHIVES) clean: From f4617cd2ff6c589af9d0938f140d5aae2d0d160e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1?= <47506558+MegaRedHand@users.noreply.github.com> Date: Thu, 3 Aug 2023 14:57:00 -0300 Subject: [PATCH 37/43] Fix: yaml eating trailing 0 --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 003a8678f..84acfe2e9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,7 +30,7 @@ jobs: # NOTE: this action comes with caching by default uses: actions/setup-go@v4 with: - go-version: 1.20 + go-version: "1.20" - name: Cache output artifacts id: output-cache uses: actions/cache@v3 From e03d58f688866b7d0622140a1316e01a794d9c73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1?= <47506558+MegaRedHand@users.noreply.github.com> Date: Thu, 3 Aug 2023 15:16:23 -0300 Subject: [PATCH 38/43] Find the Erlang includes dynamically --- Makefile | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index b1b2342e3..733b92bc4 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,9 @@ .PHONY: iex deps test clean compile-native -ERLANG_DIR := $(shell asdf where erlang) -ERLANG_INCLUDES = $(ERLANG_DIR)/usr/include/ +# magic from sym_num https://elixirforum.com/t/where-is-erl-nif-h-header-file-required-for-nif/27142/5 +ERLANG_INCLUDES := $(shell erl -eval 'io:format("~s", \ + [lists:concat([code:root_dir(), "/erts-", erlang:system_info(version), "/include"])] \ + )' -s init stop -noshell) LIBP2P_DIR = native/libp2p_nif OUTPUT_DIR = priv/native From d8b800109749db03fdf46d4e3de7190d08b26f26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1?= <47506558+MegaRedHand@users.noreply.github.com> Date: Thu, 3 Aug 2023 15:33:47 -0300 Subject: [PATCH 39/43] Change C flags used --- Makefile | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 733b92bc4..1a589f039 100644 --- a/Makefile +++ b/Makefile @@ -12,6 +12,9 @@ GO_SOURCES = $(LIBP2P_DIR)/main.go GO_ARCHIVES := $(patsubst %.go,%.a,$(GO_SOURCES)) GO_HEADERS := $(patsubst %.go,%.h,$(GO_SOURCES)) +CFLAGS = -Wall -Werror +CFLAGS += -Wl,-undefined -Wl,dynamic_lookup -fPIC -shared +CFLAGS += -I$(ERLANG_INCLUDES) $(LIBP2P_DIR)/%.a $(LIBP2P_DIR)/%.h: $(LIBP2P_DIR)/%.go cd $(LIBP2P_DIR); \ @@ -19,7 +22,7 @@ $(LIBP2P_DIR)/%.a $(LIBP2P_DIR)/%.h: $(LIBP2P_DIR)/%.go go build -buildmode=c-archive -tags only_go $*.go $(OUTPUT_DIR)/libp2p_nif.so: $(GO_ARCHIVES) $(GO_HEADERS) $(LIBP2P_DIR)/libp2p.c $(LIBP2P_DIR)/utils.c - gcc -Wall -Werror -dynamiclib -undefined dynamic_lookup -I $(ERLANG_INCLUDES) -I $(LIBP2P_DIR) -o $(OUTPUT_DIR)/libp2p_nif.so \ + gcc $(CFLAGS) -o $@ \ $(LIBP2P_DIR)/libp2p.c $(LIBP2P_DIR)/utils.c $(GO_ARCHIVES) clean: From b652cf83f8ac39438755b6de4f50a2336f4eb4f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1?= <47506558+MegaRedHand@users.noreply.github.com> Date: Thu, 3 Aug 2023 15:46:17 -0300 Subject: [PATCH 40/43] Create dir if it doesn't exist --- Makefile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Makefile b/Makefile index 1a589f039..7617e2fae 100644 --- a/Makefile +++ b/Makefile @@ -8,6 +8,9 @@ ERLANG_INCLUDES := $(shell erl -eval 'io:format("~s", \ LIBP2P_DIR = native/libp2p_nif OUTPUT_DIR = priv/native +# create dir if it doesn't exist +dir_guard=@mkdir -p $(@D) + GO_SOURCES = $(LIBP2P_DIR)/main.go GO_ARCHIVES := $(patsubst %.go,%.a,$(GO_SOURCES)) GO_HEADERS := $(patsubst %.go,%.h,$(GO_SOURCES)) @@ -22,6 +25,7 @@ $(LIBP2P_DIR)/%.a $(LIBP2P_DIR)/%.h: $(LIBP2P_DIR)/%.go go build -buildmode=c-archive -tags only_go $*.go $(OUTPUT_DIR)/libp2p_nif.so: $(GO_ARCHIVES) $(GO_HEADERS) $(LIBP2P_DIR)/libp2p.c $(LIBP2P_DIR)/utils.c + $(dir_guard) gcc $(CFLAGS) -o $@ \ $(LIBP2P_DIR)/libp2p.c $(LIBP2P_DIR)/utils.c $(GO_ARCHIVES) From 09298c5066f2f872bbb8553e3c847538fa26945d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1?= <47506558+MegaRedHand@users.noreply.github.com> Date: Thu, 3 Aug 2023 15:49:56 -0300 Subject: [PATCH 41/43] Create output dir outside targets --- Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 7617e2fae..b1e75d1fa 100644 --- a/Makefile +++ b/Makefile @@ -8,8 +8,9 @@ ERLANG_INCLUDES := $(shell erl -eval 'io:format("~s", \ LIBP2P_DIR = native/libp2p_nif OUTPUT_DIR = priv/native -# create dir if it doesn't exist -dir_guard=@mkdir -p $(@D) +# create directories if they don't exist +DIRS=$(OUTPUT_DIR) +$(info $(shell mkdir -p $(DIRS))) GO_SOURCES = $(LIBP2P_DIR)/main.go GO_ARCHIVES := $(patsubst %.go,%.a,$(GO_SOURCES)) @@ -25,7 +26,6 @@ $(LIBP2P_DIR)/%.a $(LIBP2P_DIR)/%.h: $(LIBP2P_DIR)/%.go go build -buildmode=c-archive -tags only_go $*.go $(OUTPUT_DIR)/libp2p_nif.so: $(GO_ARCHIVES) $(GO_HEADERS) $(LIBP2P_DIR)/libp2p.c $(LIBP2P_DIR)/utils.c - $(dir_guard) gcc $(CFLAGS) -o $@ \ $(LIBP2P_DIR)/libp2p.c $(LIBP2P_DIR)/utils.c $(GO_ARCHIVES) From 09c2afff0440d45e87b56ac25b3f7f9dbbe7df4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1?= <47506558+MegaRedHand@users.noreply.github.com> Date: Thu, 3 Aug 2023 15:51:58 -0300 Subject: [PATCH 42/43] Remove print from CI --- .github/workflows/ci.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 84acfe2e9..ad969698b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,11 +37,6 @@ jobs: with: path: priv/native/*.so key: ${{ runner.os }}-native-${{ hashFiles('native/**') }} - - name: DEBUG print - run: | - which erl || true - which iex || true - which mix || true - name: Compile native code if: steps.output-cache.outputs.cache-hit != 'true' run: make compile-native From 6b5c9ca3eee32b7a2605810c8f3df3d82781a242 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1?= <47506558+MegaRedHand@users.noreply.github.com> Date: Thu, 3 Aug 2023 15:55:18 -0300 Subject: [PATCH 43/43] Add dependency path for go action --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ad969698b..1ad653ccb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,6 +31,7 @@ jobs: uses: actions/setup-go@v4 with: go-version: "1.20" + cache-dependency-path: native/libp2p_nif/go.sum - name: Cache output artifacts id: output-cache uses: actions/cache@v3