diff --git a/.gitignore b/.gitignore index b7b0629ab25..cded78e334c 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,6 @@ _testmain.go *.prof .DS_Store + +wasm-demo/bee.wasm +wasm-demo/sw_bundle.js diff --git a/Makefile b/Makefile index 047f5d4f8be..1121b90cbb7 100644 --- a/Makefile +++ b/Makefile @@ -37,6 +37,12 @@ binary: dist FORCE $(GO) version $(GO) build -trimpath -ldflags "$(LDFLAGS)" -o dist/bee ./cmd/bee +.PHONY: wasm +wasm: export CGO_ENABLED=0 +wasm: dist FORCE + $(GO) version + GOARCH=wasm GOOS=js $(GO) build -trimpath -ldflags "$(LDFLAGS)" -o wasm-demo/bee.wasm ./cmd/bee + dist: mkdir $@ @@ -172,4 +178,4 @@ clean: $(GO) clean rm -rf dist/ -FORCE: \ No newline at end of file +FORCE: diff --git a/go.mod b/go.mod index e629193a4e4..13e89d342c5 100644 --- a/go.mod +++ b/go.mod @@ -25,32 +25,34 @@ require ( github.com/gorilla/websocket v1.5.3 github.com/hashicorp/go-multierror v1.1.1 github.com/hashicorp/golang-lru/v2 v2.0.7 - github.com/ipfs/go-cid v0.5.0 + github.com/ipfs/go-cid v0.6.0 github.com/ipshipyard/p2p-forge v0.6.1 github.com/kardianos/service v1.2.2 github.com/klauspost/reedsolomon v1.11.8 - github.com/libp2p/go-libp2p v0.44.0 - github.com/multiformats/go-multiaddr v0.16.0 + github.com/libp2p/go-libp2p v0.45.0 + github.com/multiformats/go-multiaddr v0.16.1 github.com/multiformats/go-multiaddr-dns v0.4.1 github.com/multiformats/go-multihash v0.2.3 github.com/multiformats/go-multistream v0.6.1 + github.com/nlepage/go-wasm-http-server/v2 v2.2.1 github.com/opentracing/opentracing-go v1.2.0 github.com/prometheus/client_golang v1.22.0 - github.com/spf13/afero v1.6.0 + github.com/spf13/afero v1.15.0 github.com/spf13/cobra v1.8.1 - github.com/spf13/viper v1.7.0 - github.com/stretchr/testify v1.10.0 + github.com/spf13/viper v1.21.0 + github.com/stretchr/testify v1.11.1 github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 github.com/uber/jaeger-client-go v2.24.0+incompatible + github.com/v1rtl/go-libp2p-wasmws v0.0.0-20251207155153-95835dee8ae4 github.com/vmihailenco/msgpack/v5 v5.3.4 github.com/wealdtech/go-ens/v3 v3.5.1 gitlab.com/nolash/go-mockbytes v0.0.7 go.uber.org/atomic v1.11.0 go.uber.org/goleak v1.3.0 - golang.org/x/crypto v0.41.0 - golang.org/x/sync v0.16.0 - golang.org/x/sys v0.35.0 - golang.org/x/term v0.34.0 + golang.org/x/crypto v0.45.0 + golang.org/x/sync v0.18.0 + golang.org/x/sys v0.38.0 + golang.org/x/term v0.37.0 golang.org/x/time v0.12.0 gopkg.in/yaml.v2 v2.4.0 resenje.org/feed v0.1.2 @@ -60,7 +62,18 @@ require ( ) require ( - github.com/BurntSushi/toml v1.1.0 // indirect + github.com/coder/websocket v1.8.14 // indirect + github.com/go-viper/mapstructure/v2 v2.4.0 // indirect + github.com/hack-pad/safejs v0.1.1 // indirect + github.com/nlepage/go-js-promise v1.0.0 // indirect + github.com/pelletier/go-toml/v2 v2.2.4 // indirect + github.com/sagikazarmark/locafero v0.11.0 // indirect + github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect + go.yaml.in/yaml/v3 v3.0.4 // indirect + golang.org/x/telemetry v0.0.0-20251111182119-bc8e575c7b54 // indirect +) + +require ( github.com/Microsoft/go-winio v0.6.2 // indirect github.com/StackExchange/wmi v1.2.1 // indirect github.com/benbjohnson/clock v1.3.5 // indirect @@ -81,8 +94,7 @@ require ( github.com/ethereum/go-verkle v0.2.2 // indirect github.com/felixge/fgprof v0.9.5 github.com/flynn/noise v1.1.0 // indirect - github.com/francoispqt/gojay v1.2.13 // indirect - github.com/fsnotify/fsnotify v1.6.0 // indirect + github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/go-kit/log v0.2.1 // indirect github.com/go-logfmt/logfmt v0.5.1 // indirect github.com/go-ole/go-ole v1.3.0 // indirect @@ -91,17 +103,14 @@ require ( github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect github.com/google/pprof v0.0.0-20250607225305-033d6d78b36a // indirect - github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00 // indirect github.com/hashicorp/errwrap v1.0.0 // indirect - github.com/hashicorp/hcl v1.0.0 // indirect github.com/holiman/uint256 v1.3.2 // indirect github.com/huin/goupnp v1.3.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/ipfs/go-log/v2 v2.6.0 // 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.18.0 // indirect - github.com/klauspost/cpuid/v2 v2.2.10 // indirect + github.com/klauspost/cpuid/v2 v2.3.0 // indirect github.com/koron/go-ssdp v0.0.6 // indirect github.com/leodido/go-urn v1.2.1 // indirect github.com/libdns/libdns v0.2.2 // indirect @@ -112,7 +121,6 @@ require ( github.com/libp2p/go-netroute v0.3.0 // indirect github.com/libp2p/go-reuseport v0.4.0 // indirect github.com/libp2p/go-yamux/v5 v5.0.1 // indirect - github.com/magiconair/properties v1.8.1 // indirect github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mholt/acmez/v3 v3.0.0 // indirect @@ -120,18 +128,15 @@ require ( 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/mitchellh/mapstructure v1.4.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-fmt v0.1.0 // indirect github.com/multiformats/go-multibase v0.2.0 // indirect - github.com/multiformats/go-multicodec v0.9.1 // indirect - github.com/multiformats/go-varint v0.0.7 + github.com/multiformats/go-multicodec v0.10.0 // indirect + github.com/multiformats/go-varint v0.1.0 github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - github.com/onsi/gomega v1.36.3 // indirect github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect - github.com/pelletier/go-toml v1.8.0 // indirect github.com/pion/datachannel v1.5.10 // indirect github.com/pion/dtls/v2 v2.2.12 // indirect github.com/pion/dtls/v3 v3.0.6 // indirect @@ -158,15 +163,13 @@ require ( github.com/prometheus/procfs v0.16.1 // indirect github.com/prometheus/statsd_exporter v0.22.7 // indirect github.com/quic-go/qpack v0.5.1 // indirect - github.com/quic-go/quic-go v0.55.0 // indirect + github.com/quic-go/quic-go v0.56.0 // indirect github.com/quic-go/webtransport-go v0.9.0 // indirect github.com/shirou/gopsutil v3.21.5+incompatible // indirect - github.com/smartystreets/assertions v1.1.1 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect - github.com/spf13/cast v1.3.0 // indirect - github.com/spf13/jwalterweatherman v1.0.0 // indirect - github.com/spf13/pflag v1.0.6 // indirect - github.com/subosito/gotenv v1.2.0 // indirect + github.com/spf13/cast v1.10.0 // indirect + github.com/spf13/pflag v1.0.10 // indirect + github.com/subosito/gotenv v1.6.0 // indirect github.com/supranational/blst v0.3.14 // indirect github.com/tklauser/go-sysconf v0.3.12 // indirect github.com/tklauser/numcpus v0.6.1 // indirect @@ -182,15 +185,18 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 go.uber.org/zap/exp v0.3.0 // indirect - golang.org/x/exp v0.0.0-20250606033433-dcc06ee1d476 // indirect - golang.org/x/mod v0.27.0 // indirect - golang.org/x/net v0.43.0 // indirect - golang.org/x/text v0.28.0 // indirect - golang.org/x/tools v0.36.0 // indirect - google.golang.org/protobuf v1.36.6 // indirect - gopkg.in/ini.v1 v1.57.0 // indirect + golang.org/x/exp v0.0.0-20251125195548-87e1e737ad39 // indirect + golang.org/x/mod v0.30.0 // indirect + golang.org/x/net v0.47.0 // indirect + golang.org/x/text v0.31.0 // indirect + golang.org/x/tools v0.39.0 // indirect + google.golang.org/protobuf v1.36.10 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect lukechampine.com/blake3 v1.4.1 // indirect ) replace github.com/codahale/hdrhistogram => github.com/HdrHistogram/hdrhistogram-go v0.0.0-20200919145931-8dac23c8dac1 + +replace github.com/syndtr/goleveldb => ../goleveldb + +replace github.com/libp2p/go-libp2p => ../go-libp2p diff --git a/go.sum b/go.sum index b2918edd460..de4665e5c20 100644 --- a/go.sum +++ b/go.sum @@ -26,7 +26,6 @@ cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM7 cloud.google.com/go/bigtable v1.2.0/go.mod h1:JcVAOl45lrTmQfLj7T6TxyMzIN/3FGGcFm+2xVAli2o= 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/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= 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= @@ -59,8 +58,6 @@ github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I= -github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ= @@ -91,9 +88,6 @@ github.com/allegro/bigcache v1.2.1/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2uc github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/apache/arrow/go/arrow v0.0.0-20191024131854-af6fa24be0db/go.mod h1:VTxUBvSJ3s3eHAg65PNgrsn5BtqCRPdmyXh6rAfdxN0= -github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= -github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= -github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/aws/aws-sdk-go-v2 v1.2.0/go.mod h1:zEQs02YRBw1DjK0PoJv3ygDYOFTre1ejlJWl8FwAuQo= @@ -111,10 +105,8 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24 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/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bits-and-blooms/bitset v1.20.0 h1:2F+rfL86jE2d/bmw7OhqUg2Sj/1rURkBn3MdfoPyRVU= github.com/bits-and-blooms/bitset v1.20.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= -github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/bmizerany/pat v0.0.0-20170815010413-6226ea591a40/go.mod h1:8rLXio+WjiTceGBHIoTvn60HIbs7Hm7bcHjyrSqYB9c= github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= @@ -175,17 +167,15 @@ github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwP github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo= github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ= +github.com/coder/websocket v1.8.14 h1:9L0p0iKiNOibykf283eHkKUHHrpG7f65OE3BhhO7v9g= +github.com/coder/websocket v1.8.14/go.mod h1:NX3SzP+inril6yawo5CQXx8+fk145lPDC6pumgx0mVg= github.com/consensys/bavard v0.1.8-0.20210406032232-f3452dc9b572/go.mod h1:Bpd0/3mZuaj6Sj+PqrmIquiOKy397AKGThQPaGzNXAQ= github.com/consensys/gnark-crypto v0.4.1-0.20210426202927-39ac3d4b3f1f/go.mod h1:815PAHg3wvysy0SyIqanF8gZ0Y1wjk/hrDHD/iT88+Q= github.com/consensys/gnark-crypto v0.18.1 h1:RyLV6UhPRoYYzaFnPQA4qK3DyuDgkTgskDdoGqFt3fI= github.com/consensys/gnark-crypto v0.18.1/go.mod h1:L3mXGFTe1ZN+RSJ+CLjUt9x7PNdx8ubaYfDROyp2Z8c= -github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= -github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= 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.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc= @@ -255,12 +245,13 @@ github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI github.com/flynn/noise v1.1.0 h1:KjPQoQCEFdZDiP03phOvGi11+SVVhBG2wOWAorLsstg= github.com/flynn/noise v1.1.0/go.mod h1:xbMo+0i6+IGbYdJhF31t2eR1BIU0CYc12+BNAKwUTag= github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= -github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk= github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= -github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= +github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 h1:f6D9Hr8xV8uYKlyuj8XIruxlh9WjVjdh1gIicAS7ays= github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= @@ -285,6 +276,11 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA= github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= +github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= @@ -300,6 +296,13 @@ github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4 github.com/go-sourcemap/sourcemap v2.1.2+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +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/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= +github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= +github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= +github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= github.com/gobwas/ws v1.2.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY= @@ -307,7 +310,6 @@ github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= 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= @@ -316,7 +318,6 @@ github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/geo v0.0.0-20190916061304-5b978397cfec/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 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= @@ -346,6 +347,7 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw 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/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= @@ -366,6 +368,8 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ 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-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= @@ -383,7 +387,13 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf 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-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= +github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= +github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo= +github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= +github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= +github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= github.com/google/pprof v0.0.0-20250607225305-033d6d78b36a h1://KbezygeMJZCSHH+HgUZiTeSoiuFspbMg1ge+eFj18= github.com/google/pprof v0.0.0-20250607225305-033d6d78b36a/go.mod h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= @@ -397,8 +407,6 @@ github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE0 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/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00 h1:l5lAOZEym3oK3SQ2HBHWsJUfbNBiTXJDeW2QDxw9AQ0= -github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/handlers v1.4.2 h1:0QniY0USkHQ1RGCLfKxeNHK9bkDHGRYGNDFBCS+YARg= github.com/gorilla/handlers v1.4.2/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= @@ -410,39 +418,20 @@ github.com/graph-gophers/graphql-go v0.0.0-20201113091052-beb923fada29/go.mod h1 github.com/graph-gophers/graphql-go v1.3.0 h1:Eb9x/q6MFpCLz7jBCiP/WTxjSDrYLR1QY41SORZyNJ0= github.com/graph-gophers/graphql-go v1.3.0/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= -github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= -github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hack-pad/safejs v0.1.1 h1:d5qPO0iQ7h2oVtpzGnLExE+Wn9AtytxIfltcS2b9KD8= +github.com/hack-pad/safejs v0.1.1/go.mod h1:HdS+bKF1NrE72VoXZeWzxFOVQVUSqZJAG0xNCnb+Tio= github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE= github.com/hashicorp/go-bexpr v0.1.10/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0= -github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= -github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= -github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= -github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= 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/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= -github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= -github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= -github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= -github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= -github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4 h1:X4egAf/gcS1zATw6wn4Ej8vjuVGxeHdan+bRb2ebyv4= github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4/go.mod h1:5GuXa7vkL8u9FkFuWdVvfR5ix8hRB7DbOAaYULamFpc= github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= @@ -456,7 +445,9 @@ github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc= github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20230524184225-eabc099b10ab/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw= +github.com/ianlancetaylor/demangle v0.0.0-20240312041847-bd984b5ce465/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= @@ -475,8 +466,8 @@ github.com/influxdata/roaring v0.4.13-0.20180809181101-fc520f41fab6/go.mod h1:bS github.com/influxdata/tdigest v0.0.0-20181121200506-bf2b5ad3c0a9/go.mod h1:Js0mqiSBE6Ffsg94weZZ2c+v/ciT8QRHFOap7EKDrR0= github.com/influxdata/usage-client v0.0.0-20160829180054-6d3895376368/go.mod h1:Wbbw6tYNvwa5dlB6304Sd+82Z3f7PmVZHVKU637d4po= github.com/ipfs/go-cid v0.0.7/go.mod h1:6Ux9z5e+HpkQdckYoX1PG/6xqKspzlEIR5SDmgqgC/I= -github.com/ipfs/go-cid v0.5.0 h1:goEKKhaGm0ul11IHA7I6p1GmKz8kEYniqFopaB5Otwg= -github.com/ipfs/go-cid v0.5.0/go.mod h1:0L7vmeNXpQpUS9vt+yEARkJ8rOg43DF3iPgn4GIN0mk= +github.com/ipfs/go-cid v0.6.0 h1:DlOReBV1xhHBhhfy/gBNNTSyfOM6rLiIx9J7A4DGf30= +github.com/ipfs/go-cid v0.6.0/go.mod h1:NC4kS1LZjzfhK40UGmpXv5/qD2kcMzACYJNntCUiDhQ= github.com/ipfs/go-log/v2 v2.6.0 h1:2Nu1KKQQ2ayonKp4MPo6pXCjqw1ULc9iohRqWV5EYqg= github.com/ipfs/go-log/v2 v2.6.0/go.mod h1:p+Efr3qaY5YXpx9TX7MoLCSEZX5boSWj9wh86P5HJa8= github.com/ipshipyard/p2p-forge v0.6.1 h1:987/hUC1YxI56CcMX6iTB+9BLjFV0d2SJnig9Z1pf8A= @@ -492,7 +483,6 @@ github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= -github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= @@ -504,7 +494,6 @@ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHm 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/jsternberg/zap-logfmt v1.0.0/go.mod h1:uvPs/4X51zdkcm5jXl5SYoN+4RK21K8mysFmDaM/h+o= -github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= @@ -515,7 +504,6 @@ github.com/karalabe/usb v0.0.0-20210518091819-4ea20957c210/go.mod h1:Od972xHfMJo github.com/kardianos/service v1.2.2 h1:ZvePhAHfvo0A7Mftk/tEzqEZ7Q4lgnR8sGz4xu1YX60= github.com/kardianos/service v1.2.2/go.mod h1:CIMRFEJVL+0DS1a3Nx06NaMn4Dz63Ng6O7dl0qH0zVM= github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= -github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= 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= @@ -526,8 +514,8 @@ github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYW github.com/klauspost/cpuid v0.0.0-20170728055534-ae7887de9fa5/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.6/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE= -github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= +github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= +github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= github.com/klauspost/crc32 v0.0.0-20161016154125-cb6bfca970f6/go.mod h1:+ZoRqAPRLkC4NPOvfYeR5KNOrY6TD+/sAC3HXPZgDYg= github.com/klauspost/pgzip v1.0.2-0.20170402124221-0bf5dcad4ada/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/klauspost/reedsolomon v1.11.8 h1:s8RpUW5TK4hjr+djiOpbZJB4ksx+TdYbRH7vHQpwPOY= @@ -536,7 +524,6 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxv 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.6 h1:Jb0h04599eq/CY7rB5YEqPS83HmRfHP2azkxMN2rFtU= github.com/koron/go-ssdp v0.0.6/go.mod h1:0R9LfRJGek1zWTjN3JUNlm5INCDYGpRDfAptnct63fI= -github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= 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= @@ -563,8 +550,6 @@ github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6 github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg= github.com/libp2p/go-flow-metrics v0.2.0 h1:EIZzjmeOE6c8Dav0sNv35vhZxATIXWZg6j/C08XmmDw= github.com/libp2p/go-flow-metrics v0.2.0/go.mod h1:st3qqfu8+pMfh+9Mzqb2GTiwrAGjIPszEjZmtksN8Jc= -github.com/libp2p/go-libp2p v0.44.0 h1:5Gtt8OrF8yiXmH+Mx4+/iBeFRMK1TY3a8OrEBDEqAvs= -github.com/libp2p/go-libp2p v0.44.0/go.mod h1:NovCojezAt4dnDd4fH048K7PKEqH0UFYYqJRjIIu8zc= github.com/libp2p/go-libp2p-asn-util v0.4.1 h1:xqL7++IKD9TBFMgnLPZR6/6iYhawHKHl950SO9L6n94= github.com/libp2p/go-libp2p-asn-util v0.4.1/go.mod h1:d/NI6XZ9qxw67b4e+NgpQexCIiFYJjErASrYW4PFDN8= github.com/libp2p/go-libp2p-testing v0.12.0 h1:EPvBb4kKMWO29qP4mZGyhVzUyR25dvfUIK5WDu6iPUA= @@ -580,8 +565,6 @@ github.com/libp2p/go-yamux/v5 v5.0.1 h1:f0WoX/bEF2E8SbE4c/k1Mo+/9z0O4oC/hWEA+nfY github.com/libp2p/go-yamux/v5 v5.0.1/go.mod h1:en+3cdX51U0ZslwRdRLrvQsdayFt3TSUKvBGErzpWbU= github.com/lucas-clemente/quic-go v0.15.2/go.mod h1:qxmO5Y4ZMhdNkunGfxuZnZXnJwYpW9vjQkyrZ7BsgUI= github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= -github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= -github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/marcopolo/simnet v0.0.1 h1:rSMslhPz6q9IvJeFWDoMGxMIrlsbXau3NkuIXHGJxfg= @@ -598,7 +581,6 @@ github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxec github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-ieproxy v0.0.0-20190610004146-91bb50d98149/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc= github.com/mattn/go-ieproxy v0.0.0-20190702010315-6dee0af9227d/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc= -github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.5-0.20180830101745-3fb116b82035/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= @@ -615,7 +597,6 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5 github.com/mholt/acmez/v3 v3.0.0 h1:r1NcjuWR0VaKP2BTjDK9LRFBw/WvURx3jlaEUl9Ht8E= github.com/mholt/acmez/v3 v3.0.0/go.mod h1:L1wOU06KKvq7tswuMDwKdcHeKpFFgkppZy/y0DFxagQ= github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= -github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.66 h1:FeZXOS3VCVsKnEAd+wBkjMC3D2K+ww66Cq3VnCINuJE= github.com/miekg/dns v1.1.66/go.mod h1:jGFzBsSNbJw6z1HYut1RKBKHA9PBdxeHrZG8J+gC2WE= github.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c h1:bzE/A84HN25pxAuk9Eej1Kz9OUelF97nAc82bDquQI8= @@ -629,13 +610,6 @@ github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771/go.mod h1:B5e1 github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM= 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/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= -github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= -github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= -github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/pointerstructure v1.2.0 h1:O+i9nHnXS3l/9Wu7r4NrEdwA2VFTicjUEN1uBnDo34A= @@ -660,8 +634,8 @@ github.com/multiformats/go-base36 v0.2.0/go.mod h1:qvnKE++v+2MWCfePClUEjE78Z7P2a github.com/multiformats/go-multiaddr v0.1.1/go.mod h1:aMKBKNEYmzmDmxfX88/vz+J5IU55txyt0p4aiWVohjo= github.com/multiformats/go-multiaddr v0.2.2/go.mod h1:NtfXiOtHvghW9KojvtySjH5y0u0xW5UouOmQQrn6a3Y= github.com/multiformats/go-multiaddr v0.3.2/go.mod h1:lCKNGP1EQ1eZ35Za2wlqnabm9xQkib3fyB+nZXHLag0= -github.com/multiformats/go-multiaddr v0.16.0 h1:oGWEVKioVQcdIOBlYM8BH1rZDWOGJSqr9/BKl6zQ4qc= -github.com/multiformats/go-multiaddr v0.16.0/go.mod h1:JSVUmXDjsVFiW7RjIFMP7+Ev+h1DTbiJgVeTV/tcmP0= +github.com/multiformats/go-multiaddr v0.16.1 h1:fgJ0Pitow+wWXzN9do+1b8Pyjmo8m5WhGfzpL82MpCw= +github.com/multiformats/go-multiaddr v0.16.1/go.mod h1:JSVUmXDjsVFiW7RjIFMP7+Ev+h1DTbiJgVeTV/tcmP0= github.com/multiformats/go-multiaddr-dns v0.4.1 h1:whi/uCLbDS3mSEUMb1MsoT4uzUeZB0N32yzufqS0i5M= github.com/multiformats/go-multiaddr-dns v0.4.1/go.mod h1:7hfthtB4E4pQwirrz+J0CcDUfbWzTqEzVyYKKIKpgkc= github.com/multiformats/go-multiaddr-fmt v0.1.0 h1:WLEFClPycPkp4fnIzoFoV9FVd49/eQsuaL3/CWe167E= @@ -669,8 +643,8 @@ github.com/multiformats/go-multiaddr-fmt v0.1.0/go.mod h1:hGtDIW4PU4BqJ50gW2quDu github.com/multiformats/go-multibase v0.0.3/go.mod h1:5+1R4eQrT3PkYZ24C3W2Ue2tPwIdYQD509ZjSb5y9Oc= 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.1 h1:x/Fuxr7ZuR4jJV4Os5g444F7xC4XmyUaT/FWtE+9Zjo= -github.com/multiformats/go-multicodec v0.9.1/go.mod h1:LLWNMtyV5ithSBUo3vFIMaeDy+h3EbkMTek1m+Fybbo= +github.com/multiformats/go-multicodec v0.10.0 h1:UpP223cig/Cx8J76jWt91njpK3GTAO1w02sdcjZDSuc= +github.com/multiformats/go-multicodec v0.10.0/go.mod h1:wg88pM+s2kZJEQfRCKBNU+g32F5aWBEjyFHXvZLTcLI= github.com/multiformats/go-multihash v0.0.8/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew= github.com/multiformats/go-multihash v0.0.13/go.mod h1:VdAWLKTwram9oKAatUcLxBNUjdtcVwxObEQBtRfuyjc= github.com/multiformats/go-multihash v0.0.14/go.mod h1:VdAWLKTwram9oKAatUcLxBNUjdtcVwxObEQBtRfuyjc= @@ -681,8 +655,8 @@ github.com/multiformats/go-multistream v0.6.1 h1:4aoX5v6T+yWmc2raBHsTvzmFhOI8WVO github.com/multiformats/go-multistream v0.6.1/go.mod h1:ksQf6kqHAb6zIsyw7Zm+gAuVo57Qbq84E27YlYqavqw= github.com/multiformats/go-varint v0.0.5/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= github.com/multiformats/go-varint v0.0.6/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/multiformats/go-varint v0.1.0 h1:i2wqFp4sdl3IcIxfAonHQV9qU5OsZ4Ts9IOoETFs5dI= +github.com/multiformats/go-varint v0.1.0/go.mod h1:5KVAVXegtfmNQQm/lCY+ATvDzvJJhSkUlGQV9wgObdI= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= @@ -691,8 +665,12 @@ github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E= 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/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= +github.com/nlepage/go-js-promise v1.0.0 h1:K7OmJ3+0BgWJ2LfXchg2sI6RDr7AW/KWR8182epFwGQ= +github.com/nlepage/go-js-promise v1.0.0/go.mod h1:bdOP0wObXu34euibyK39K1hoBCtlgTKXGc56AGflaRo= +github.com/nlepage/go-wasm-http-server/v2 v2.2.1 h1:4tzhSb3HKQ3Ykt2TPfqEnmcPfw8n1E8agv4OzAyckr8= +github.com/nlepage/go-wasm-http-server/v2 v2.2.1/go.mod h1:r8j7cEOeUqNp+c+C52sNuWaFTvvT/cNqIwBuEtA36HA= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= @@ -700,16 +678,60 @@ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= +github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= -github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= +github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= +github.com/onsi/ginkgo/v2 v2.1.4/go.mod h1:um6tUpWM/cxCK3/FK8BXqEiUMUwRgSM4JXG47RKZmLU= +github.com/onsi/ginkgo/v2 v2.1.6/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk= +github.com/onsi/ginkgo/v2 v2.3.0/go.mod h1:Eew0uilEqZmIEZr8JrvYlvOM7Rr6xzTmMV8AyFNU9d0= +github.com/onsi/ginkgo/v2 v2.4.0/go.mod h1:iHkDK1fKGcBoEHT5W7YBq4RFWaQulw+caOMkAt4OrFo= +github.com/onsi/ginkgo/v2 v2.5.0/go.mod h1:Luc4sArBICYCS8THh8v3i3i5CuSZO+RaQRaJoeNwomw= +github.com/onsi/ginkgo/v2 v2.7.0/go.mod h1:yjiuMwPokqY1XauOgju45q3sJt6VzQ/Fict1LFVcsAo= +github.com/onsi/ginkgo/v2 v2.8.1/go.mod h1:N1/NbDngAFcSLdyZ+/aYTYGSlq9qMCS/cNKGJjy+csc= +github.com/onsi/ginkgo/v2 v2.9.0/go.mod h1:4xkjoL/tZv4SMWeww56BU5kAt19mVB47gTWxmrTcxyk= +github.com/onsi/ginkgo/v2 v2.9.1/go.mod h1:FEcmzVcCHl+4o9bQZVab+4dC9+j+91t2FHSzmGAPfuo= +github.com/onsi/ginkgo/v2 v2.9.2/go.mod h1:WHcJJG2dIlcCqVfBAwUCrJxSPFb6v4azBwgxeMeDuts= +github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k= +github.com/onsi/ginkgo/v2 v2.9.7/go.mod h1:cxrmXWykAwTwhQsJOPfdIDiJ+l2RYq7U8hFU+M/1uw0= +github.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7kR0iZvM= +github.com/onsi/ginkgo/v2 v2.13.0/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o= +github.com/onsi/ginkgo/v2 v2.17.1/go.mod h1:llBI3WDLL9Z6taip6f33H76YcWtJv+7R3HigUjbIBOs= +github.com/onsi/ginkgo/v2 v2.17.2/go.mod h1:nP2DPOQoNsQmsVyv5rDA8JkXQoCs6goXIvr/PRJ1eCc= +github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To= +github.com/onsi/ginkgo/v2 v2.20.1/go.mod h1:lG9ey2Z29hR41WMVthyJBGUBcBhGOtoPF2VFMvBXFCI= +github.com/onsi/ginkgo/v2 v2.22.1/go.mod h1:S6aTpoRsSq2cZOd+pssHAlKW/Q/jZt6cPrPlnj4a1xM= +github.com/onsi/ginkgo/v2 v2.23.3/go.mod h1:zXTP6xIp3U8aVuXN8ENK9IXRaTjFnpVB9mGmaSRvxnM= +github.com/onsi/ginkgo/v2 v2.23.4 h1:ktYTpKJAVZnDT4VjxSbiBenUjmlL/5QkBEocaWXiQus= +github.com/onsi/ginkgo/v2 v2.23.4/go.mod h1:Bt66ApGPBFzHyR+JO10Zbt0Gsp4uWxu5mIOTusL46e8= github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.36.3 h1:hID7cr8t3Wp26+cYnfcjR6HpJ00fdogN6dqZ1t6IylU= +github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= +github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= +github.com/onsi/gomega v1.20.1/go.mod h1:DtrZpjmvpn2mPm4YWQa0/ALMDj9v4YxLgojwPeREyVo= +github.com/onsi/gomega v1.21.1/go.mod h1:iYAIXgPSaDHak0LCMA+AWBpIKBr8WZicMxnE8luStNc= +github.com/onsi/gomega v1.22.1/go.mod h1:x6n7VNe4hw0vkyYUM4mjIXx3JbLiPaBPNgB7PRQ1tuM= +github.com/onsi/gomega v1.24.0/go.mod h1:Z/NWtiqwBrwUt4/2loMmHL63EDLnYHmVbuBpDr2vQAg= +github.com/onsi/gomega v1.24.1/go.mod h1:3AOiACssS3/MajrniINInwbfOOtfZvplPzuRSmvt1jM= +github.com/onsi/gomega v1.26.0/go.mod h1:r+zV744Re+DiYCIPRlYOTxn0YkOLcAnW8k1xXdMPGhM= +github.com/onsi/gomega v1.27.1/go.mod h1:aHX5xOykVYzWOV4WqQy0sy8BQptgukenXpCXfadcIAw= +github.com/onsi/gomega v1.27.3/go.mod h1:5vG284IBtfDAmDyrK+eGyZmUgUlmi+Wngqo557cZ6Gw= +github.com/onsi/gomega v1.27.4/go.mod h1:riYq/GJKh8hhoM01HN6Vmuy93AarCXCBGpvFDK3q3fQ= +github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg= +github.com/onsi/gomega v1.27.7/go.mod h1:1p8OOlwo2iUUDsHnOrjE5UKYJ+e3W8eQ3qSlRahPmr4= +github.com/onsi/gomega v1.27.8/go.mod h1:2J8vzI/s+2shY9XHRApDkdgPo1TKT7P2u6fXeJKFnNQ= +github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= +github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= +github.com/onsi/gomega v1.33.0/go.mod h1:+925n5YtiFsLzzafLUHzVMBpvvRAzrydIBiSIxjX3wY= +github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0= +github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= +github.com/onsi/gomega v1.36.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= +github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY= github.com/onsi/gomega v1.36.3/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0= +github.com/onsi/gomega v1.37.0 h1:CdEG8g0S133B4OswTDC/5XPSzE1OeP29QOioj2PID2Y= +github.com/onsi/gomega v1.37.0/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0= github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.0.3-0.20180606204148-bd9c31933947/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= @@ -717,13 +739,11 @@ github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+ github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0= -github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/paulbellamy/ratecounter v0.2.0/go.mod h1:Hfx1hDpSGoqxkVVpBi/IlYD7kChlfo5C6hzIHwPqfFE= 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/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pelletier/go-toml v1.8.0 h1:Keo9qb7iRJs2voHvunFtuuYFsbWeOBh8/P9v/kVMFtw= -github.com/pelletier/go-toml v1.8.0/go.mod h1:D6yutnOGMveHEPV7VQOuvI/gXY61bv+9bAOTRnLElKs= +github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= +github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= github.com/peterh/liner v1.0.1-0.20180619022028-8c1271fcf47f/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc= github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0= github.com/peterh/liner v1.2.1 h1:O4BlKaq/LWu6VRWmol4ByWfzx6MfXc5Op5HETyIy5yg= @@ -779,15 +799,13 @@ github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE 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/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pkg/term v0.0.0-20180730021639-bffc007b7fd5/go.mod h1:eCbImbZ95eXtAUIbLAuAVnBnwf83mjf6QIVH8SHYwqQ= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= 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 v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.5.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= @@ -805,7 +823,6 @@ github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNw github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= @@ -818,7 +835,6 @@ github.com/prometheus/common v0.64.0 h1:pdZeA+g617P7oGv1CzdTzyeShxAGrTBsolKNOLQP github.com/prometheus/common v0.64.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8= 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.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= @@ -834,8 +850,8 @@ github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40T github.com/prometheus/tsdb v0.10.0/go.mod h1:oi49uRhEe9dPUTlS3JRZOwJuVi6tmh10QSgwXEyGCt4= github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI= github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg= -github.com/quic-go/quic-go v0.55.0 h1:zccPQIqYCXDt5NmcEabyYvOnomjs8Tlwl7tISjJh9Mk= -github.com/quic-go/quic-go v0.55.0/go.mod h1:DR51ilwU1uE164KuWXhinFcKWGlEjzys2l8zUl5Ss1U= +github.com/quic-go/quic-go v0.56.0 h1:q/TW+OLismmXAehgFLczhCDTYB3bFmua4D9lsNBWxvY= +github.com/quic-go/quic-go v0.56.0/go.mod h1:9gx5KsFQtw2oZ6GZTyh+7YEvOxWCL9WZAepnHxgAo6c= github.com/quic-go/webtransport-go v0.9.0 h1:jgys+7/wm6JarGDrW+lD/r9BGqBAmqY/ssklE09bA70= github.com/quic-go/webtransport-go v0.9.0/go.mod h1:4FUYIiUc75XSsF6HShcLeXXYZJ9AGwo/xh3L8M/P1ao= github.com/retailnext/hllpp v1.0.1-0.20180308014038-101a6d2f8b52/go.mod h1:RDpi1RftBQPUCDRw6SmxeaREsAaRKnOclghuzp/WRzc= @@ -843,7 +859,6 @@ github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rjeczalik/notify v0.9.1/go.mod h1:rKwnCoCGeuQnwBtTSPL9Dad03Vh2n40ePRrjvIXnJho= github.com/rjeczalik/notify v0.9.2/go.mod h1:aErll2f0sUX9PXZnVNyeiObbmTlk5jnMoCa4QEjJeqM= -github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= @@ -856,8 +871,8 @@ github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc= +github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik= github.com/segmentio/kafka-go v0.1.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo= github.com/segmentio/kafka-go v0.2.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= @@ -891,42 +906,40 @@ github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx 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/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/assertions v1.1.1 h1:T/YLemO5Yp7KPzS+lVtu+WsHn8yoSwTfItdAd1r3cck= -github.com/smartystreets/assertions v1.1.1/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= -github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= +github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw= +github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U= github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY= -github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= -github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= +github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I= +github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= +github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= -github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= -github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= -github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.7.0 h1:xVKxvI7ouOI5I+U9s2eeiUfMaWBVoXA3AWskkrqK0VM= -github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU= +github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY= github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q= github.com/status-im/keycard-go v0.0.0-20200402102358-957c09536969/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q= 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/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.0/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 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.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 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/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= @@ -934,16 +947,14 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/stvp/go-udp-testing v0.0.0-20201019212854-469649b16807/go.mod h1:7jxmlfBCDBXRzr0eAQJ48XC1hBu1np4CS5+cHEYfwpc= -github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= -github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/supranational/blst v0.3.14 h1:xNMoHRJOTwMn63ip6qoWJ2Ymgvj7E2b9jY2FAwY+qRo= github.com/supranational/blst v0.3.14/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= -github.com/syndtr/goleveldb v1.0.1-0.20210305035536-64b5b1c73954/go.mod h1:u2MKkTVTVJWe5D1rCvame8WqhBd88EuIwODJZ1VHCPM= -github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= -github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= github.com/tdewolff/minify/v2 v2.7.3/go.mod h1:BkDSm8aMMT0ALGmpt7j3Ra7nLUgZL0qhyrAHXwxcy5w= github.com/tdewolff/parse/v2 v2.4.2/go.mod h1:WzaJpRSbwq++EIQHYIRTpbYKNA3gn9it1Ik++q4zyho= @@ -956,7 +967,6 @@ github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0h github.com/tklauser/numcpus v0.2.2/go.mod h1:x3qojaO3uyYt0i56EW/VUYs7uBvdl2fkfZFu0T9wgjM= github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= -github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs= github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U= github.com/uber/jaeger-client-go v2.24.0+incompatible h1:CGchgJcHsDd2jWnaL4XngByMrXoGHh3n8oCqAKx0uMo= @@ -966,6 +976,8 @@ github.com/uber/jaeger-lib v2.2.0+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6 github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w= github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ= +github.com/v1rtl/go-libp2p-wasmws v0.0.0-20251207155153-95835dee8ae4 h1:1mvei7+YbH0cPd/eo+WCJmIM1FKDUbTAjD4LdPMYcqE= +github.com/v1rtl/go-libp2p-wasmws v0.0.0-20251207155153-95835dee8ae4/go.mod h1:FtW4JAL8wRRLZo6D77k6r7LxQBFTiaD9WCJnCXuhse4= 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/vmihailenco/msgpack/v5 v5.3.4 h1:qMKAwOV+meBw2Y8k9cVwAy7qErtYCwBzZ2ellBfvnqc= @@ -982,7 +994,6 @@ github.com/willf/bitset v1.1.3/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPyS github.com/wlynxg/anet v0.0.3/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA= github.com/wlynxg/anet v0.0.5 h1:J3VJGi1gvo0JwZ/P1/Yc/8p63SoW98B5dHkYDmpgvvU= github.com/wlynxg/anet v0.0.5/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA= -github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4= github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= @@ -990,6 +1001,7 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de 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.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/zeebo/assert v1.1.0 h1:hU1L1vLTHsnO8x8c9KAR5GmM5QscxHg5RNU5z5qbUWY= github.com/zeebo/assert v1.1.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= @@ -999,7 +1011,6 @@ github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo= github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4= gitlab.com/nolash/go-mockbytes v0.0.7 h1:9XVFpEfY67kGBVJve3uV19kzqORdlo7V+q09OE6Yo54= gitlab.com/nolash/go-mockbytes v0.0.7/go.mod h1:KKOpNTT39j2Eo+P6uUTOncntfeKY6AFh/2CxuD5MpgE= -go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= 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= @@ -1010,9 +1021,10 @@ go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 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/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= +go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= go.uber.org/dig v1.19.0 h1:BACLhebsYdpQ7IROQ1AGPjrXcP5dF80U3gKoFzbaq/4= go.uber.org/dig v1.19.0/go.mod h1:Us0rSJiThwCv2GteUN0Q7OKvU7n5J4dxZ9JKUXozFdE= go.uber.org/fx v1.24.0 h1:wE8mruvpg2kiiL1Vqd0CC+tr0/24XIB10Iwp2lLWzkg= @@ -1025,23 +1037,22 @@ go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/ 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.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= go.uber.org/zap/exp v0.3.0 h1:6JYzdifzYkGmTdRR59oYH+Ng7k49H9qVpWwNSsGJj3U= go.uber.org/zap/exp v0.3.0/go.mod h1:5I384qq7XGxYyByIhHm6jg5CHkGY0nsTfbDLgDDlgJQ= +go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= 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-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/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-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190909091759-094676da4a83/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -1056,11 +1067,27 @@ golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= +golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= -golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4= -golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= +golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= +golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= +golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= +golang.org/x/crypto v0.30.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= +golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= +golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= +golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc= +golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= +golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1074,8 +1101,9 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 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-20250606033433-dcc06ee1d476 h1:bsqhLWFR6G6xiQcb+JoGqdKdRU6WzPWmK8E0jxTjzo4= -golang.org/x/exp v0.0.0-20250606033433-dcc06ee1d476/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8= +golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= +golang.org/x/exp v0.0.0-20251125195548-87e1e737ad39 h1:DHNhtq3sNNzrvduZZIiFyXWOL9IWaDPHqTnLJp+rCBY= +golang.org/x/exp v0.0.0-20251125195548-87e1e737ad39/go.mod h1:46edojNIoXTNOhySWIWdix628clX9ODXwPsQuG6hsK0= golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= 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= @@ -1099,21 +1127,32 @@ golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= +golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ= -golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc= +golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= +golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk= +golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/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-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181220203305-927f97764cc3/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-20190228165749-92fc7df08ae7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1142,26 +1181,49 @@ golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/ 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-20200813134508-3edf25e44fcc/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-20201110031124-69a78807bb2b/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-20210220033124-5f55cee0dc0d/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/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.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= +golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= +golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= -golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= -golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= +golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= +golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= +golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs= +golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= +golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= +golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= +golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E= +golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= +golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= 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= @@ -1186,14 +1248,21 @@ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= -golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= -golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= +golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= 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-20180926160741-c2ed4eda69e7/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/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-20181031143558-9b800f95dbbc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1231,16 +1300,15 @@ golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7w 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-20200519105757-fe76b779f299/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-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1252,32 +1320,75 @@ golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/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.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220708085239-5a0f0661e09d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.4.0/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.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= -golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= +golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= +golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457/go.mod h1:pRgIJT+bRLFKnoM1ldnzKoxTIn14Yxz928LQRYYgIN0= +golang.org/x/telemetry v0.0.0-20251111182119-bc8e575c7b54 h1:E2/AqCUMZGgd73TQkxUMcMla25GB9i/5HOdLr+uH7Vo= +golang.org/x/telemetry v0.0.0-20251111182119-bc8e575c7b54/go.mod h1:hKdjCMrbv9skySur+Nek8Hd0uJ0GuxJIoIX2payrIdQ= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= 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/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= +golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= +golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o= golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= +golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= +golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= -golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4= -golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= +golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= +golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= +golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4= +golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk= +golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= +golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= +golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s= +golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g= +golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw= +golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU= +golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254= 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= @@ -1286,12 +1397,26 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/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.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= -golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= +golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= +golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= +golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= +golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= 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= @@ -1299,7 +1424,6 @@ golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= -golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 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= @@ -1321,7 +1445,6 @@ golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgw 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-20191112195655-aa38f8e97acc/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= @@ -1349,12 +1472,30 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY 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-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= +golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg= -golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s= +golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= +golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= +golang.org/x/tools v0.9.3/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= +golang.org/x/tools v0.12.0/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= +golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg= +golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI= +golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= +golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw= +golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY= +golang.org/x/tools v0.31.0/go.mod h1:naFTU+Cev749tSJRXJlna0T3WxKvb1kWEx15xA4SdmQ= +golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ= +golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ= 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= @@ -1457,8 +1598,13 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= -google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= +google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -1469,19 +1615,13 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EV gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.57.0 h1:9unxIsFcTt4I55uWluz+UmL95q4kdJ0buvQ1ZIqVQww= -gopkg.in/ini.v1 v1.57.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/mail.v2 v2.3.1/go.mod h1:htwXN1Qh09vZJ1NVKxQqHPBaCBbzKhp5GzuJEA4VJWw= gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c= gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200619000410-60c24ae608a6/go.mod h1:uAJfkITjFhyEEuUfm7bsmCZRbW5WRq8s9EY8HZ6hCns= -gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/urfave/cli.v1 v1.20.0/go.mod h1:vuBzUtMdQeixQj8LVd+/98pzhxNGQoyuPBlsXHOQNO0= -gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= 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.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/pkg/api/router.go b/pkg/api/router.go index 776def9457d..65a28882b0c 100644 --- a/pkg/api/router.go +++ b/pkg/api/router.go @@ -5,20 +5,16 @@ package api import ( - "expvar" "fmt" "net/http" - "net/http/pprof" "strings" "github.com/ethersphere/bee/v2/pkg/jsonhttp" "github.com/ethersphere/bee/v2/pkg/log/httpaccess" "github.com/ethersphere/bee/v2/pkg/swarm" "github.com/ethersphere/bee/v2/pkg/transaction/backendnoop" - "github.com/felixge/fgprof" "github.com/gorilla/handlers" "github.com/gorilla/mux" - "github.com/prometheus/client_golang/prometheus/promhttp" "resenje.org/web" ) @@ -105,80 +101,6 @@ func (s *Service) EnableFullAPI() { ) } -func (s *Service) mountTechnicalDebug() { - s.router.Handle("/node", jsonhttp.MethodHandler{ - "GET": http.HandlerFunc(s.nodeGetHandler), - }) - - s.router.Handle("/addresses", jsonhttp.MethodHandler{ - "GET": http.HandlerFunc(s.addressesHandler), - }) - - s.router.Handle("/chainstate", jsonhttp.MethodHandler{ - "GET": http.HandlerFunc(s.chainStateHandler), - }) - - s.router.Handle("/debugstore", jsonhttp.MethodHandler{ - "GET": web.ChainHandlers( - httpaccess.NewHTTPAccessSuppressLogHandler(), - web.FinalHandlerFunc(s.debugStorage), - ), - }) - - s.router.Path("/metrics").Handler(web.ChainHandlers( - httpaccess.NewHTTPAccessSuppressLogHandler(), - web.FinalHandler(promhttp.InstrumentMetricHandler( - s.metricsRegistry, - promhttp.HandlerFor(s.metricsRegistry, promhttp.HandlerOpts{}), - )), - )) - - s.router.Handle("/debug/pprof", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - u := r.URL - u.Path += "/" - http.Redirect(w, r, u.String(), http.StatusPermanentRedirect) - })) - - s.router.Handle("/debug/fgprof", fgprof.Handler()) - s.router.Handle("/debug/pprof/cmdline", http.HandlerFunc(pprof.Cmdline)) - s.router.Handle("/debug/pprof/profile", http.HandlerFunc(pprof.Profile)) - s.router.Handle("/debug/pprof/symbol", http.HandlerFunc(pprof.Symbol)) - s.router.Handle("/debug/pprof/trace", http.HandlerFunc(pprof.Trace)) - s.router.PathPrefix("/debug/pprof/").Handler(http.HandlerFunc(pprof.Index)) - s.router.Handle("/debug/vars", expvar.Handler()) - - s.router.Handle("/loggers", jsonhttp.MethodHandler{ - "GET": web.ChainHandlers( - httpaccess.NewHTTPAccessSuppressLogHandler(), - web.FinalHandlerFunc(s.loggerGetHandler), - ), - }) - - s.router.Handle("/loggers/{exp}", jsonhttp.MethodHandler{ - "GET": web.ChainHandlers( - httpaccess.NewHTTPAccessSuppressLogHandler(), - web.FinalHandlerFunc(s.loggerGetHandler), - ), - }) - - s.router.Handle("/loggers/{exp}/{verbosity}", jsonhttp.MethodHandler{ - "PUT": web.ChainHandlers( - httpaccess.NewHTTPAccessSuppressLogHandler(), - web.FinalHandlerFunc(s.loggerSetVerbosityHandler), - ), - }) - - s.router.Handle("/readiness", web.ChainHandlers( - httpaccess.NewHTTPAccessSuppressLogHandler(), - web.FinalHandlerFunc(s.readinessHandler), - )) - - s.router.Handle("/health", web.ChainHandlers( - httpaccess.NewHTTPAccessSuppressLogHandler(), - web.FinalHandlerFunc(s.healthHandler), - )) -} - func (s *Service) checkRouteAvailability(handler http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if !s.fullAPIEnabled { @@ -209,16 +131,6 @@ func (s *Service) checkChequebookAvailability(handler http.Handler) http.Handler }) } -func (s *Service) checkStorageIncentivesAvailability(handler http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if s.redistributionAgent == nil { - jsonhttp.Forbidden(w, "Storage incentives are disabled. This endpoint is unavailable.") - return - } - handler.ServeHTTP(w, r) - }) -} - func (s *Service) checkChainAvailability(handler http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if _, ok := s.chainBackend.(*backendnoop.Backend); ok { @@ -407,293 +319,3 @@ func (s *Service) mountAPI() { "PUT": http.HandlerFunc(s.stewardshipPutHandler), }) } - -func (s *Service) mountBusinessDebug() { - handle := func(path string, handler http.Handler) { - routeHandler := s.checkRouteAvailability(handler) - s.router.Handle(path, routeHandler) - s.router.Handle(rootPath+path, routeHandler) - } - - if s.transaction != nil { - handle("/transactions", jsonhttp.MethodHandler{ - "GET": http.HandlerFunc(s.transactionListHandler), - }) - - handle("/transactions/{hash}", jsonhttp.MethodHandler{ - "GET": http.HandlerFunc(s.transactionDetailHandler), - "POST": http.HandlerFunc(s.transactionResendHandler), - "DELETE": http.HandlerFunc(s.transactionCancelHandler), - }) - } - - handle("/peers", jsonhttp.MethodHandler{ - "GET": http.HandlerFunc(s.peersHandler), - }) - - handle("/pingpong/{address}", jsonhttp.MethodHandler{ - "POST": http.HandlerFunc(s.pingpongHandler), - }) - - handle("/reservestate", jsonhttp.MethodHandler{ - "GET": http.HandlerFunc(s.reserveStateHandler), - }) - - handle("/connect/{multi-address:.+}", jsonhttp.MethodHandler{ - "POST": http.HandlerFunc(s.peerConnectHandler), - }) - - handle("/blocklist", jsonhttp.MethodHandler{ - "GET": http.HandlerFunc(s.blocklistedPeersHandler), - }) - - handle("/peers/{address}", jsonhttp.MethodHandler{ - "DELETE": http.HandlerFunc(s.peerDisconnectHandler), - }) - - handle("/topology", jsonhttp.MethodHandler{ - "GET": http.HandlerFunc(s.topologyHandler), - }) - - handle("/welcome-message", jsonhttp.MethodHandler{ - "GET": http.HandlerFunc(s.getWelcomeMessageHandler), - "POST": web.ChainHandlers( - jsonhttp.NewMaxBodyBytesHandler(welcomeMessageMaxRequestSize), - web.FinalHandlerFunc(s.setWelcomeMessageHandler), - ), - }) - - handle("/balances", jsonhttp.MethodHandler{ - "GET": http.HandlerFunc(s.compensatedBalancesHandler), - }) - - handle("/balances/{peer}", jsonhttp.MethodHandler{ - "GET": http.HandlerFunc(s.compensatedPeerBalanceHandler), - }) - - handle("/consumed", jsonhttp.MethodHandler{ - "GET": http.HandlerFunc(s.balancesHandler), - }) - - handle("/consumed/{peer}", jsonhttp.MethodHandler{ - "GET": http.HandlerFunc(s.peerBalanceHandler), - }) - - handle("/timesettlements", jsonhttp.MethodHandler{ - "GET": http.HandlerFunc(s.settlementsHandlerPseudosettle), - }) - - handle("/settlements", web.ChainHandlers( - s.checkSwapAvailability, - web.FinalHandler(jsonhttp.MethodHandler{ - "GET": http.HandlerFunc(s.settlementsHandler), - }), - )) - - handle("/settlements/{peer}", web.ChainHandlers( - s.checkSwapAvailability, - web.FinalHandler(jsonhttp.MethodHandler{ - "GET": http.HandlerFunc(s.peerSettlementsHandler), - }), - )) - - handle("/chequebook/cheque/{peer}", web.ChainHandlers( - s.checkSwapAvailability, - web.FinalHandler(jsonhttp.MethodHandler{ - "GET": http.HandlerFunc(s.chequebookLastPeerHandler), - }), - )) - - handle("/chequebook/cheque", web.ChainHandlers( - s.checkSwapAvailability, - web.FinalHandler(jsonhttp.MethodHandler{ - "GET": http.HandlerFunc(s.chequebookAllLastHandler), - }), - )) - - handle("/chequebook/cashout/{peer}", web.ChainHandlers( - s.checkSwapAvailability, - web.FinalHandler(jsonhttp.MethodHandler{ - "GET": http.HandlerFunc(s.swapCashoutStatusHandler), - "POST": web.ChainHandlers( - s.gasConfigMiddleware("swap cashout"), - web.FinalHandlerFunc(s.swapCashoutHandler), - ), - }), - )) - - handle("/chequebook/balance", web.ChainHandlers( - s.checkChequebookAvailability, - web.FinalHandler(jsonhttp.MethodHandler{ - "GET": http.HandlerFunc(s.chequebookBalanceHandler), - }), - )) - - handle("/chequebook/address", web.ChainHandlers( - s.checkChequebookAvailability, - web.FinalHandler(jsonhttp.MethodHandler{ - "GET": http.HandlerFunc(s.chequebookAddressHandler), - }), - )) - - handle("/chequebook/deposit", web.ChainHandlers( - s.checkChequebookAvailability, - web.FinalHandler(jsonhttp.MethodHandler{ - "POST": web.ChainHandlers( - s.gasConfigMiddleware("chequebook deposit"), - web.FinalHandlerFunc(s.chequebookDepositHandler), - ), - }), - )) - - handle("/chequebook/withdraw", web.ChainHandlers( - s.checkChequebookAvailability, - web.FinalHandler(jsonhttp.MethodHandler{ - "POST": web.ChainHandlers( - s.gasConfigMiddleware("chequebook withdraw"), - web.FinalHandlerFunc(s.chequebookWithdrawHandler), - ), - }), - )) - - handle("/wallet", web.ChainHandlers( - s.checkChequebookAvailability, - s.checkSwapAvailability, - web.FinalHandler(jsonhttp.MethodHandler{ - "GET": http.HandlerFunc(s.walletHandler), - }), - )) - - handle("/wallet/withdraw/{coin}", web.ChainHandlers( - s.checkChequebookAvailability, - s.checkSwapAvailability, - web.FinalHandler(jsonhttp.MethodHandler{ - "POST": web.ChainHandlers( - s.gasConfigMiddleware("wallet withdraw"), - web.FinalHandlerFunc(s.walletWithdrawHandler), - ), - }), - )) - - handle("/stamps", web.ChainHandlers( - s.checkChainAvailability, - s.postageSyncStatusCheckHandler, - web.FinalHandler(jsonhttp.MethodHandler{ - "GET": http.HandlerFunc(s.postageGetStampsHandler), - })), - ) - - handle("/stamps/{batch_id}", web.ChainHandlers( - s.checkChainAvailability, - s.postageSyncStatusCheckHandler, - web.FinalHandler(jsonhttp.MethodHandler{ - "GET": http.HandlerFunc(s.postageGetStampHandler), - })), - ) - - handle("/stamps/{batch_id}/buckets", web.ChainHandlers( - s.checkChainAvailability, - s.postageSyncStatusCheckHandler, - web.FinalHandler(jsonhttp.MethodHandler{ - "GET": http.HandlerFunc(s.postageGetStampBucketsHandler), - })), - ) - - handle("/stamps/{amount}/{depth}", web.ChainHandlers( - s.checkChainAvailability, - s.postageAccessHandler, - s.postageSyncStatusCheckHandler, - s.gasConfigMiddleware("create batch"), - web.FinalHandler(jsonhttp.MethodHandler{ - "POST": http.HandlerFunc(s.postageCreateHandler), - })), - ) - - handle("/stamps/topup/{batch_id}/{amount}", web.ChainHandlers( - s.checkChainAvailability, - s.postageAccessHandler, - s.postageSyncStatusCheckHandler, - s.gasConfigMiddleware("topup batch"), - web.FinalHandler(jsonhttp.MethodHandler{ - "PATCH": http.HandlerFunc(s.postageTopUpHandler), - })), - ) - - handle("/stamps/dilute/{batch_id}/{depth}", web.ChainHandlers( - s.checkChainAvailability, - s.postageAccessHandler, - s.postageSyncStatusCheckHandler, - s.gasConfigMiddleware("dilute batch"), - web.FinalHandler(jsonhttp.MethodHandler{ - "PATCH": http.HandlerFunc(s.postageDiluteHandler), - })), - ) - - handle("/batches", jsonhttp.MethodHandler{ - "GET": http.HandlerFunc(s.postageGetAllBatchesHandler), - }) - - handle("/accounting", jsonhttp.MethodHandler{ - "GET": http.HandlerFunc(s.accountingInfoHandler), - }) - - handle("/stake/withdrawable", web.ChainHandlers( - s.stakingAccessHandler, - s.gasConfigMiddleware("get or withdraw withdrawable stake"), - web.FinalHandler(jsonhttp.MethodHandler{ - "GET": http.HandlerFunc(s.getWithdrawableStakeHandler), - "DELETE": http.HandlerFunc(s.withdrawStakeHandler), - })), - ) - - handle("/stake/{amount}", web.ChainHandlers( - s.stakingAccessHandler, - s.gasConfigMiddleware("deposit stake"), - web.FinalHandler(jsonhttp.MethodHandler{ - "POST": http.HandlerFunc(s.stakingDepositHandler), - }), - )) - - handle("/stake", web.ChainHandlers( - s.stakingAccessHandler, - s.gasConfigMiddleware("get or migrate stake"), - web.FinalHandler(jsonhttp.MethodHandler{ - "GET": http.HandlerFunc(s.getPotentialStake), - "DELETE": http.HandlerFunc(s.migrateStakeHandler), - })), - ) - - handle("/redistributionstate", web.ChainHandlers( - s.checkStorageIncentivesAvailability, - web.FinalHandler(jsonhttp.MethodHandler{ - "GET": http.HandlerFunc(s.redistributionStatusHandler), - })), - ) - - handle("/status", jsonhttp.MethodHandler{ - "GET": web.ChainHandlers( - httpaccess.NewHTTPAccessSuppressLogHandler(), - web.FinalHandlerFunc(s.statusGetHandler), - ), - }) - - handle("/status/peers", jsonhttp.MethodHandler{ - "GET": web.ChainHandlers( - httpaccess.NewHTTPAccessSuppressLogHandler(), - s.statusAccessHandler, - web.FinalHandlerFunc(s.statusGetPeersHandler), - ), - }) - - handle("/status/neighborhoods", jsonhttp.MethodHandler{ - "GET": web.ChainHandlers( - httpaccess.NewHTTPAccessSuppressLogHandler(), - s.statusAccessHandler, - web.FinalHandlerFunc(s.statusGetNeighborhoods), - ), - }) - - handle("/rchash/{depth}/{anchor1}/{anchor2}", jsonhttp.MethodHandler{ - "GET": http.HandlerFunc(s.rchash), - }) -} diff --git a/pkg/api/router_js.go b/pkg/api/router_js.go new file mode 100644 index 00000000000..439342a3036 --- /dev/null +++ b/pkg/api/router_js.go @@ -0,0 +1,188 @@ +//go:build js + +package api + +import ( + "net/http" + + "github.com/ethersphere/bee/v2/pkg/jsonhttp" + "github.com/ethersphere/bee/v2/pkg/log/httpaccess" + "resenje.org/web" +) + +func (s *Service) mountTechnicalDebug() { + +} + +func (s *Service) mountBusinessDebug() { + handle := func(path string, handler http.Handler) { + routeHandler := s.checkRouteAvailability(handler) + s.router.Handle(path, routeHandler) + s.router.Handle(rootPath+path, routeHandler) + } + + if s.transaction != nil { + handle("/transactions", jsonhttp.MethodHandler{ + "GET": http.HandlerFunc(s.transactionListHandler), + }) + + handle("/transactions/{hash}", jsonhttp.MethodHandler{ + "GET": http.HandlerFunc(s.transactionDetailHandler), + "POST": http.HandlerFunc(s.transactionResendHandler), + "DELETE": http.HandlerFunc(s.transactionCancelHandler), + }) + } + + handle("/peers", jsonhttp.MethodHandler{ + "GET": http.HandlerFunc(s.peersHandler), + }) + + handle("/pingpong/{address}", jsonhttp.MethodHandler{ + "POST": http.HandlerFunc(s.pingpongHandler), + }) + + handle("/reservestate", jsonhttp.MethodHandler{ + "GET": http.HandlerFunc(s.reserveStateHandler), + }) + + handle("/connect/{multi-address:.+}", jsonhttp.MethodHandler{ + "POST": http.HandlerFunc(s.peerConnectHandler), + }) + + handle("/blocklist", jsonhttp.MethodHandler{ + "GET": http.HandlerFunc(s.blocklistedPeersHandler), + }) + + handle("/peers/{address}", jsonhttp.MethodHandler{ + "DELETE": http.HandlerFunc(s.peerDisconnectHandler), + }) + + handle("/topology", jsonhttp.MethodHandler{ + "GET": http.HandlerFunc(s.topologyHandler), + }) + + handle("/welcome-message", jsonhttp.MethodHandler{ + "GET": http.HandlerFunc(s.getWelcomeMessageHandler), + "POST": web.ChainHandlers( + jsonhttp.NewMaxBodyBytesHandler(welcomeMessageMaxRequestSize), + web.FinalHandlerFunc(s.setWelcomeMessageHandler), + ), + }) + + handle("/balances", jsonhttp.MethodHandler{ + "GET": http.HandlerFunc(s.compensatedBalancesHandler), + }) + + handle("/balances/{peer}", jsonhttp.MethodHandler{ + "GET": http.HandlerFunc(s.compensatedPeerBalanceHandler), + }) + + handle("/consumed", jsonhttp.MethodHandler{ + "GET": http.HandlerFunc(s.balancesHandler), + }) + + handle("/consumed/{peer}", jsonhttp.MethodHandler{ + "GET": http.HandlerFunc(s.peerBalanceHandler), + }) + + handle("/timesettlements", jsonhttp.MethodHandler{ + "GET": http.HandlerFunc(s.settlementsHandlerPseudosettle), + }) + + handle("/settlements", web.ChainHandlers( + s.checkSwapAvailability, + web.FinalHandler(jsonhttp.MethodHandler{ + "GET": http.HandlerFunc(s.settlementsHandler), + }), + )) + + handle("/settlements/{peer}", web.ChainHandlers( + s.checkSwapAvailability, + web.FinalHandler(jsonhttp.MethodHandler{ + "GET": http.HandlerFunc(s.peerSettlementsHandler), + }), + )) + + handle("/chequebook/cheque/{peer}", web.ChainHandlers( + s.checkSwapAvailability, + web.FinalHandler(jsonhttp.MethodHandler{ + "GET": http.HandlerFunc(s.chequebookLastPeerHandler), + }), + )) + + handle("/chequebook/cheque", web.ChainHandlers( + s.checkSwapAvailability, + web.FinalHandler(jsonhttp.MethodHandler{ + "GET": http.HandlerFunc(s.chequebookAllLastHandler), + }), + )) + + handle("/chequebook/balance", web.ChainHandlers( + s.checkChequebookAvailability, + web.FinalHandler(jsonhttp.MethodHandler{ + "GET": http.HandlerFunc(s.chequebookBalanceHandler), + }), + )) + + handle("/stamps", web.ChainHandlers( + s.checkChainAvailability, + s.postageSyncStatusCheckHandler, + web.FinalHandler(jsonhttp.MethodHandler{ + "GET": http.HandlerFunc(s.postageGetStampsHandler), + })), + ) + + handle("/stamps/{batch_id}", web.ChainHandlers( + s.checkChainAvailability, + s.postageSyncStatusCheckHandler, + web.FinalHandler(jsonhttp.MethodHandler{ + "GET": http.HandlerFunc(s.postageGetStampHandler), + })), + ) + + handle("/stamps/{batch_id}/buckets", web.ChainHandlers( + s.checkChainAvailability, + s.postageSyncStatusCheckHandler, + web.FinalHandler(jsonhttp.MethodHandler{ + "GET": http.HandlerFunc(s.postageGetStampBucketsHandler), + })), + ) + + handle("/stamps/{amount}/{depth}", web.ChainHandlers( + s.checkChainAvailability, + s.postageAccessHandler, + s.postageSyncStatusCheckHandler, + s.gasConfigMiddleware("create batch"), + web.FinalHandler(jsonhttp.MethodHandler{ + "POST": http.HandlerFunc(s.postageCreateHandler), + })), + ) + + handle("/batches", jsonhttp.MethodHandler{ + "GET": http.HandlerFunc(s.postageGetAllBatchesHandler), + }) + + handle("/status", jsonhttp.MethodHandler{ + "GET": web.ChainHandlers( + httpaccess.NewHTTPAccessSuppressLogHandler(), + web.FinalHandlerFunc(s.statusGetHandler), + ), + }) + + handle("/status/peers", jsonhttp.MethodHandler{ + "GET": web.ChainHandlers( + httpaccess.NewHTTPAccessSuppressLogHandler(), + s.statusAccessHandler, + web.FinalHandlerFunc(s.statusGetPeersHandler), + ), + }) + + handle("/status/neighborhoods", jsonhttp.MethodHandler{ + "GET": web.ChainHandlers( + httpaccess.NewHTTPAccessSuppressLogHandler(), + s.statusAccessHandler, + web.FinalHandlerFunc(s.statusGetNeighborhoods), + ), + }) + +} diff --git a/pkg/api/router_native.go b/pkg/api/router_native.go new file mode 100644 index 00000000000..8aa7a86c50e --- /dev/null +++ b/pkg/api/router_native.go @@ -0,0 +1,389 @@ +//go:build !js + +package api + +import ( + "expvar" + "net/http" + "net/http/pprof" + + "github.com/ethersphere/bee/v2/pkg/jsonhttp" + "github.com/ethersphere/bee/v2/pkg/log/httpaccess" + "github.com/felixge/fgprof" + "github.com/prometheus/client_golang/prometheus/promhttp" + "resenje.org/web" +) + +func (s *Service) checkStorageIncentivesAvailability(handler http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if s.redistributionAgent == nil { + jsonhttp.Forbidden(w, "Storage incentives are disabled. This endpoint is unavailable.") + return + } + handler.ServeHTTP(w, r) + }) +} + +func (s *Service) mountTechnicalDebug() { + s.router.Handle("/node", jsonhttp.MethodHandler{ + "GET": http.HandlerFunc(s.nodeGetHandler), + }) + + s.router.Handle("/addresses", jsonhttp.MethodHandler{ + "GET": http.HandlerFunc(s.addressesHandler), + }) + + s.router.Handle("/chainstate", jsonhttp.MethodHandler{ + "GET": http.HandlerFunc(s.chainStateHandler), + }) + + s.router.Handle("/debugstore", jsonhttp.MethodHandler{ + "GET": web.ChainHandlers( + httpaccess.NewHTTPAccessSuppressLogHandler(), + web.FinalHandlerFunc(s.debugStorage), + ), + }) + + s.router.Path("/metrics").Handler(web.ChainHandlers( + httpaccess.NewHTTPAccessSuppressLogHandler(), + web.FinalHandler(promhttp.InstrumentMetricHandler( + s.metricsRegistry, + promhttp.HandlerFor(s.metricsRegistry, promhttp.HandlerOpts{}), + )), + )) + + s.router.Handle("/debug/pprof", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + u := r.URL + u.Path += "/" + http.Redirect(w, r, u.String(), http.StatusPermanentRedirect) + })) + + s.router.Handle("/debug/fgprof", fgprof.Handler()) + s.router.Handle("/debug/pprof/cmdline", http.HandlerFunc(pprof.Cmdline)) + s.router.Handle("/debug/pprof/profile", http.HandlerFunc(pprof.Profile)) + s.router.Handle("/debug/pprof/symbol", http.HandlerFunc(pprof.Symbol)) + s.router.Handle("/debug/pprof/trace", http.HandlerFunc(pprof.Trace)) + s.router.PathPrefix("/debug/pprof/").Handler(http.HandlerFunc(pprof.Index)) + s.router.Handle("/debug/vars", expvar.Handler()) + + s.router.Handle("/loggers", jsonhttp.MethodHandler{ + "GET": web.ChainHandlers( + httpaccess.NewHTTPAccessSuppressLogHandler(), + web.FinalHandlerFunc(s.loggerGetHandler), + ), + }) + + s.router.Handle("/loggers/{exp}", jsonhttp.MethodHandler{ + "GET": web.ChainHandlers( + httpaccess.NewHTTPAccessSuppressLogHandler(), + web.FinalHandlerFunc(s.loggerGetHandler), + ), + }) + + s.router.Handle("/loggers/{exp}/{verbosity}", jsonhttp.MethodHandler{ + "PUT": web.ChainHandlers( + httpaccess.NewHTTPAccessSuppressLogHandler(), + web.FinalHandlerFunc(s.loggerSetVerbosityHandler), + ), + }) + + s.router.Handle("/readiness", web.ChainHandlers( + httpaccess.NewHTTPAccessSuppressLogHandler(), + web.FinalHandlerFunc(s.readinessHandler), + )) + + s.router.Handle("/health", web.ChainHandlers( + httpaccess.NewHTTPAccessSuppressLogHandler(), + web.FinalHandlerFunc(s.healthHandler), + )) +} + +func (s *Service) mountBusinessDebug() { + handle := func(path string, handler http.Handler) { + routeHandler := s.checkRouteAvailability(handler) + s.router.Handle(path, routeHandler) + s.router.Handle(rootPath+path, routeHandler) + } + + if s.transaction != nil { + handle("/transactions", jsonhttp.MethodHandler{ + "GET": http.HandlerFunc(s.transactionListHandler), + }) + + handle("/transactions/{hash}", jsonhttp.MethodHandler{ + "GET": http.HandlerFunc(s.transactionDetailHandler), + "POST": http.HandlerFunc(s.transactionResendHandler), + "DELETE": http.HandlerFunc(s.transactionCancelHandler), + }) + } + + handle("/peers", jsonhttp.MethodHandler{ + "GET": http.HandlerFunc(s.peersHandler), + }) + + handle("/pingpong/{address}", jsonhttp.MethodHandler{ + "POST": http.HandlerFunc(s.pingpongHandler), + }) + + handle("/reservestate", jsonhttp.MethodHandler{ + "GET": http.HandlerFunc(s.reserveStateHandler), + }) + + handle("/connect/{multi-address:.+}", jsonhttp.MethodHandler{ + "POST": http.HandlerFunc(s.peerConnectHandler), + }) + + handle("/blocklist", jsonhttp.MethodHandler{ + "GET": http.HandlerFunc(s.blocklistedPeersHandler), + }) + + handle("/peers/{address}", jsonhttp.MethodHandler{ + "DELETE": http.HandlerFunc(s.peerDisconnectHandler), + }) + + handle("/topology", jsonhttp.MethodHandler{ + "GET": http.HandlerFunc(s.topologyHandler), + }) + + handle("/welcome-message", jsonhttp.MethodHandler{ + "GET": http.HandlerFunc(s.getWelcomeMessageHandler), + "POST": web.ChainHandlers( + jsonhttp.NewMaxBodyBytesHandler(welcomeMessageMaxRequestSize), + web.FinalHandlerFunc(s.setWelcomeMessageHandler), + ), + }) + + handle("/balances", jsonhttp.MethodHandler{ + "GET": http.HandlerFunc(s.compensatedBalancesHandler), + }) + + handle("/balances/{peer}", jsonhttp.MethodHandler{ + "GET": http.HandlerFunc(s.compensatedPeerBalanceHandler), + }) + + handle("/consumed", jsonhttp.MethodHandler{ + "GET": http.HandlerFunc(s.balancesHandler), + }) + + handle("/consumed/{peer}", jsonhttp.MethodHandler{ + "GET": http.HandlerFunc(s.peerBalanceHandler), + }) + + handle("/timesettlements", jsonhttp.MethodHandler{ + "GET": http.HandlerFunc(s.settlementsHandlerPseudosettle), + }) + + handle("/settlements", web.ChainHandlers( + s.checkSwapAvailability, + web.FinalHandler(jsonhttp.MethodHandler{ + "GET": http.HandlerFunc(s.settlementsHandler), + }), + )) + + handle("/settlements/{peer}", web.ChainHandlers( + s.checkSwapAvailability, + web.FinalHandler(jsonhttp.MethodHandler{ + "GET": http.HandlerFunc(s.peerSettlementsHandler), + }), + )) + + handle("/chequebook/cheque/{peer}", web.ChainHandlers( + s.checkSwapAvailability, + web.FinalHandler(jsonhttp.MethodHandler{ + "GET": http.HandlerFunc(s.chequebookLastPeerHandler), + }), + )) + + handle("/chequebook/cheque", web.ChainHandlers( + s.checkSwapAvailability, + web.FinalHandler(jsonhttp.MethodHandler{ + "GET": http.HandlerFunc(s.chequebookAllLastHandler), + }), + )) + + handle("/chequebook/cashout/{peer}", web.ChainHandlers( + s.checkSwapAvailability, + web.FinalHandler(jsonhttp.MethodHandler{ + "GET": http.HandlerFunc(s.swapCashoutStatusHandler), + "POST": web.ChainHandlers( + s.gasConfigMiddleware("swap cashout"), + web.FinalHandlerFunc(s.swapCashoutHandler), + ), + }), + )) + + handle("/chequebook/balance", web.ChainHandlers( + s.checkChequebookAvailability, + web.FinalHandler(jsonhttp.MethodHandler{ + "GET": http.HandlerFunc(s.chequebookBalanceHandler), + }), + )) + + handle("/chequebook/address", web.ChainHandlers( + s.checkChequebookAvailability, + web.FinalHandler(jsonhttp.MethodHandler{ + "GET": http.HandlerFunc(s.chequebookAddressHandler), + }), + )) + + handle("/chequebook/deposit", web.ChainHandlers( + s.checkChequebookAvailability, + web.FinalHandler(jsonhttp.MethodHandler{ + "POST": web.ChainHandlers( + s.gasConfigMiddleware("chequebook deposit"), + web.FinalHandlerFunc(s.chequebookDepositHandler), + ), + }), + )) + + handle("/chequebook/withdraw", web.ChainHandlers( + s.checkChequebookAvailability, + web.FinalHandler(jsonhttp.MethodHandler{ + "POST": web.ChainHandlers( + s.gasConfigMiddleware("chequebook withdraw"), + web.FinalHandlerFunc(s.chequebookWithdrawHandler), + ), + }), + )) + + handle("/wallet", web.ChainHandlers( + s.checkChequebookAvailability, + s.checkSwapAvailability, + web.FinalHandler(jsonhttp.MethodHandler{ + "GET": http.HandlerFunc(s.walletHandler), + }), + )) + + handle("/wallet/withdraw/{coin}", web.ChainHandlers( + s.checkChequebookAvailability, + s.checkSwapAvailability, + web.FinalHandler(jsonhttp.MethodHandler{ + "POST": web.ChainHandlers( + s.gasConfigMiddleware("wallet withdraw"), + web.FinalHandlerFunc(s.walletWithdrawHandler), + ), + }), + )) + + handle("/stamps", web.ChainHandlers( + s.checkChainAvailability, + s.postageSyncStatusCheckHandler, + web.FinalHandler(jsonhttp.MethodHandler{ + "GET": http.HandlerFunc(s.postageGetStampsHandler), + })), + ) + + handle("/stamps/{batch_id}", web.ChainHandlers( + s.checkChainAvailability, + s.postageSyncStatusCheckHandler, + web.FinalHandler(jsonhttp.MethodHandler{ + "GET": http.HandlerFunc(s.postageGetStampHandler), + })), + ) + + handle("/stamps/{batch_id}/buckets", web.ChainHandlers( + s.checkChainAvailability, + s.postageSyncStatusCheckHandler, + web.FinalHandler(jsonhttp.MethodHandler{ + "GET": http.HandlerFunc(s.postageGetStampBucketsHandler), + })), + ) + + handle("/stamps/{amount}/{depth}", web.ChainHandlers( + s.checkChainAvailability, + s.postageAccessHandler, + s.postageSyncStatusCheckHandler, + s.gasConfigMiddleware("create batch"), + web.FinalHandler(jsonhttp.MethodHandler{ + "POST": http.HandlerFunc(s.postageCreateHandler), + })), + ) + + handle("/stamps/topup/{batch_id}/{amount}", web.ChainHandlers( + s.checkChainAvailability, + s.postageAccessHandler, + s.postageSyncStatusCheckHandler, + s.gasConfigMiddleware("topup batch"), + web.FinalHandler(jsonhttp.MethodHandler{ + "PATCH": http.HandlerFunc(s.postageTopUpHandler), + })), + ) + + handle("/stamps/dilute/{batch_id}/{depth}", web.ChainHandlers( + s.checkChainAvailability, + s.postageAccessHandler, + s.postageSyncStatusCheckHandler, + s.gasConfigMiddleware("dilute batch"), + web.FinalHandler(jsonhttp.MethodHandler{ + "PATCH": http.HandlerFunc(s.postageDiluteHandler), + })), + ) + + handle("/batches", jsonhttp.MethodHandler{ + "GET": http.HandlerFunc(s.postageGetAllBatchesHandler), + }) + + handle("/accounting", jsonhttp.MethodHandler{ + "GET": http.HandlerFunc(s.accountingInfoHandler), + }) + + handle("/stake/withdrawable", web.ChainHandlers( + s.stakingAccessHandler, + s.gasConfigMiddleware("get or withdraw withdrawable stake"), + web.FinalHandler(jsonhttp.MethodHandler{ + "GET": http.HandlerFunc(s.getWithdrawableStakeHandler), + "DELETE": http.HandlerFunc(s.withdrawStakeHandler), + })), + ) + + handle("/stake/{amount}", web.ChainHandlers( + s.stakingAccessHandler, + s.gasConfigMiddleware("deposit stake"), + web.FinalHandler(jsonhttp.MethodHandler{ + "POST": http.HandlerFunc(s.stakingDepositHandler), + }), + )) + + handle("/stake", web.ChainHandlers( + s.stakingAccessHandler, + s.gasConfigMiddleware("get or migrate stake"), + web.FinalHandler(jsonhttp.MethodHandler{ + "GET": http.HandlerFunc(s.getPotentialStake), + "DELETE": http.HandlerFunc(s.migrateStakeHandler), + })), + ) + + handle("/redistributionstate", web.ChainHandlers( + s.checkStorageIncentivesAvailability, + web.FinalHandler(jsonhttp.MethodHandler{ + "GET": http.HandlerFunc(s.redistributionStatusHandler), + })), + ) + + handle("/status", jsonhttp.MethodHandler{ + "GET": web.ChainHandlers( + httpaccess.NewHTTPAccessSuppressLogHandler(), + web.FinalHandlerFunc(s.statusGetHandler), + ), + }) + + handle("/status/peers", jsonhttp.MethodHandler{ + "GET": web.ChainHandlers( + httpaccess.NewHTTPAccessSuppressLogHandler(), + s.statusAccessHandler, + web.FinalHandlerFunc(s.statusGetPeersHandler), + ), + }) + + handle("/status/neighborhoods", jsonhttp.MethodHandler{ + "GET": web.ChainHandlers( + httpaccess.NewHTTPAccessSuppressLogHandler(), + s.statusAccessHandler, + web.FinalHandlerFunc(s.statusGetNeighborhoods), + ), + }) + + handle("/rchash/{depth}/{anchor1}/{anchor2}", jsonhttp.MethodHandler{ + "GET": http.HandlerFunc(s.rchash), + }) +} diff --git a/pkg/fs/fs.go b/pkg/fs/fs.go new file mode 100644 index 00000000000..08a492a36e7 --- /dev/null +++ b/pkg/fs/fs.go @@ -0,0 +1,89 @@ +// Copyright (c) 2012, Suryandaru Triandana +// All rights reservefs. +// +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package fs + +import ( + "io" + "os" + "path/filepath" +) + +// osFile is an interface for interacting with a file. In Go, the underlying +// type of osFile is always simply *os.File. In JavaScript/Wasm, the underlying +// type is a wrapper type which mimics the functionality of os.File. +type OsFile interface { + // Stat returns the FileInfo structure describing file. If there is an error, + // it will be of type *PathError. + Stat() (os.FileInfo, error) + // Read reads up to len(b) bytes from the File. It returns the number of bytes + // read and any error encountered. At end of file, Read returns 0, io.EOF. + Read(b []byte) (n int, err error) + // ReadAt reads len(b) bytes from the File starting at byte offset off. It + // returns the number of bytes read and the error, if any. ReadAt always + // returns a non-nil error when n < len(b). At end of file, that error is + // io.EOF. + ReadAt(b []byte, off int64) (n int, err error) + // Write writes len(b) bytes to the File. It returns the number of bytes + // written and an error, if any. Write returns a non-nil error when n != + // len(b). + Write(b []byte) (n int, err error) + // Seek sets the offset for the next Read or Write on file to offset, + // interpreted according to whence: 0 means relative to the origin of the + // file, 1 means relative to the current offset, and 2 means relative to the + // end. It returns the new offset and an error, if any. The behavior of Seek + // on a file opened with O_APPEND is not specified. + Seek(offset int64, whence int) (ret int64, err error) + // Sync commits the current contents of the file to stable storage. Typically, + // this means flushing the file system's in-memory copy of recently written + // data to disk. + Sync() error + // Close closes the File, rendering it unusable for I/O. On files that support + // SetDeadline, any pending I/O operations will be canceled and return + // immediately with an error. + Close() error + + WriteAt([]byte, int64) (int, error) + Truncate(size int64) error + WriteString(s string) (n int, err error) +} + +func ReadFile(filename string) ([]byte, error) { + f, err := osOpen(filename) + if err != nil { + return nil, err + } + defer f.Close() + + return io.ReadAll(f) +} + +func MkdirAll(path string, perm os.FileMode) error { + return osMkdirAll(path, perm) +} + +func Open(name string) (OsFile, error) { + return osOpen(name) +} + +func OpenFile(name string, flag int, perm os.FileMode) (OsFile, error) { + return osOpenFile(name, flag, perm) +} + +func WriteFile(filename string, data []byte, perm os.FileMode) error { + if err := MkdirAll(filepath.Dir(filename), 0o755); err != nil { + return err + } + return writeFile(filename, data, perm) +} + +func Stat(name string) (os.FileInfo, error) { + return fsStat(name) +} + +func Remove(name string) error { + return osRemove(name) +} diff --git a/pkg/fs/fs_js.go b/pkg/fs/fs_js.go new file mode 100644 index 00000000000..c5ee8122caa --- /dev/null +++ b/pkg/fs/fs_js.go @@ -0,0 +1,572 @@ +//go:build js && wasm +// +build js,wasm + +// Copyright (c) 2012, Suryandaru Triandana +// All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package fs + +import ( + "fmt" + "io" + "os" + "path/filepath" + "reflect" + "strings" + "sync" + "syscall" + "syscall/js" + "time" +) + +var _ os.FileInfo = fsFileInfo{} + +// fsFileInfo is an implementation of os.FileInfo for JavaScript/Wasm. +// It's backed by fs. +type fsFileInfo struct { + js.Value + path string +} + +// Name returns the base name of the file. +func (fi fsFileInfo) Name() string { + return filepath.Base(fi.path) +} + +// Size returns the length of the file in bytes. +func (fi fsFileInfo) Size() int64 { + return int64(fi.Value.Get("size").Int()) +} + +// Mode returns the file mode bits. +func (fi fsFileInfo) Mode() os.FileMode { + return os.FileMode(fi.Value.Get("size").Int()) +} + +// ModTime returns the modification time. +func (fi fsFileInfo) ModTime() time.Time { + modifiedTimeString := fi.Value.Get("mtime").String() + modifiedTime, err := time.Parse(time.RFC3339, modifiedTimeString) + if err != nil { + panic(fmt.Errorf("could not convert string mtime (%q) to time.Time: %s", modifiedTimeString, err.Error())) + } + return modifiedTime +} + +// IsDir is an abbreviation for Mode().IsDir(). +func (fi fsFileInfo) IsDir() bool { + return fi.Value.Call("isDirectory").Bool() +} + +// underlying data source (always returns nil for wasm/js). +func (fi fsFileInfo) Sys() interface{} { + return nil +} + +var _ OsFile = &fsFile{} + +// fsFile is an implementation of osFile for JavaScript/Wasm. It's backed +// by fs. +type fsFile struct { + // name is the name of the file (including path) + path string + // fd is a file descriptor used as a reference to the file. + fd int + // currOffset is the current value of the offset used for reading or writing. + currOffset int64 +} + +// Stat returns the FileInfo structure describing file. If there is an error, +// it will be of type *PathError. +func (f fsFile) Stat() (os.FileInfo, error) { + return fsStat(f.path) +} + +// Read reads up to len(b) bytes from the File. It returns the number of bytes +// read and any error encountered. At end of file, Read returns 0, io.EOF. +func (f *fsFile) Read(b []byte) (n int, err error) { + bytesRead, err := f.read(b, f.currOffset) + if bytesRead == 0 { + return 0, io.EOF + } + f.currOffset += int64(bytesRead) + return bytesRead, nil +} + +// ReadAt reads len(b) bytes from the File starting at byte offset off. It +// returns the number of bytes read and the error, if any. ReadAt always +// returns a non-nil error when n < len(b). At end of file, that error is +// io.EOF. +func (f fsFile) ReadAt(b []byte, off int64) (n int, err error) { + bytesRead, err := f.read(b, off) + if bytesRead < len(b) { + return bytesRead, io.EOF + } + return bytesRead, nil +} + +func (f fsFile) read(b []byte, off int64) (n int, err error) { + defer func() { + if e := recover(); e != nil { + if jsErr, ok := e.(js.Error); ok { + err = convertJSError(jsErr) + } + } + }() + // JavaScript API expects a Uint8Array which we then convert into []byte. + buffer := js.Global().Get("Uint8Array").New(len(b)) + rawBytesRead := jsReadSync(f.fd, buffer, 0, len(b), int(off)) + bytesRead := rawBytesRead.Int() + for i := 0; i < bytesRead; i++ { + b[i] = byte(buffer.Index(i).Int()) + } + return bytesRead, nil +} + +// Write writes len(b) bytes to the File. It returns the number of bytes +// written and an error, if any. Write returns a non-nil error when n != +// len(b). +func (f *fsFile) Write(b []byte) (n int, err error) { + defer func() { + if e := recover(); e != nil { + if jsErr, ok := e.(js.Error); ok { + err = convertJSError(jsErr) + } + } + }() + uint8arr := js.Global().Get("Uint8Array").New(len(b)) + js.CopyBytesToJS(uint8arr, b) + rawBytesWritten := jsWriteSync(f.fd, uint8arr, 0, len(b), int(f.currOffset)) + bytesWritten := rawBytesWritten.Int() + f.currOffset += int64(bytesWritten) + if bytesWritten != len(b) { + return bytesWritten, io.ErrShortWrite + } + if err := f.Sync(); err != nil { + return bytesWritten, err + } + return bytesWritten, nil +} + +// Seek sets the offset for the next Read or Write on file to offset, +// interpreted according to whence: 0 means relative to the origin of the +// file, 1 means relative to the current offset, and 2 means relative to the +// end. It returns the new offset and an error, if any. The behavior of Seek +// on a file opened with O_APPEND is not specified. +func (f *fsFile) Seek(offset int64, whence int) (ret int64, err error) { + switch whence { + case io.SeekStart: + f.currOffset = offset + return f.currOffset, nil + case io.SeekCurrent: + f.currOffset += offset + return f.currOffset, nil + case io.SeekEnd: + f.currOffset = -offset + return f.currOffset, nil + } + return 0, fmt.Errorf("Seek: unexpected whence value: %d", whence) +} + +// Sync commits the current contents of the file to stable storage. Typically, +// this means flushing the file system's in-memory copy of recently written +// data to disk. +func (f fsFile) Sync() (err error) { + defer func() { + if e := recover(); e != nil { + if jsErr, ok := e.(js.Error); ok { + err = convertJSError(jsErr) + } + } + }() + jsFsyncSync(f.fd) + return nil +} + +// Close closes the File, rendering it unusable for I/O. On files that support +// SetDeadline, any pending I/O operations will be canceled and return +// immediately with an error. +func (f fsFile) Close() (err error) { + defer func() { + if e := recover(); e != nil { + if jsErr, ok := e.(js.Error); ok { + err = convertJSError(jsErr) + } + } + }() + jsCloseSync(f.fd) + return nil +} + +func fsStat(path string) (fileInfo os.FileInfo, err error) { + defer func() { + if e := recover(); e != nil { + if jsErr, ok := e.(js.Error); ok { + err = convertJSError(jsErr) + } + } + }() + rawFileInfo := jsStatSync(path) + return fsFileInfo{Value: rawFileInfo, path: path}, nil +} + +func osOpen(path string) (OsFile, error) { + if isfsSupported() { + return fsOpenFile(path, os.O_RDONLY, os.ModePerm) + } + return os.Open(path) +} + +func fsOpenFile(path string, flag int, perm os.FileMode) (file OsFile, err error) { + defer func() { + if e := recover(); e != nil { + if jsErr, ok := e.(js.Error); ok { + err = convertJSError(jsErr) + } + } + }() + jsFlag, err := toJSFlag(flag) + if err != nil { + return nil, err + } + rawFD := jsOpenSync(path, jsFlag, int(perm)) + return &fsFile{path: path, fd: rawFD.Int()}, nil +} + +func toJSFlag(flag int) (string, error) { + // O_APPEND takes precedence + if flag&os.O_APPEND != 0 { + return "a", nil + } + // O_CREATE + O_RDWR + if flag&os.O_CREATE != 0 && flag&os.O_RDWR != 0 { + return "w+", nil // create if not exist, read/write, truncate + } + // O_CREATE + O_WRONLY + if flag&os.O_CREATE != 0 && flag&os.O_WRONLY != 0 { + return "w", nil // create if not exist, write only, truncate + } + // O_RDWR (no create) + if flag&os.O_RDWR != 0 { + return "r+", nil // read/write, fail if not exist + } + // O_WRONLY (no create) + if flag&os.O_WRONLY != 0 { + return "w", nil // write only, truncate, fail if not exist + } + // O_RDONLY + return "r", nil // read only +} + +func Readdirnames(path string, n int) ([]string, error) { + if isfsSupported() { + return fsReaddirnames(path, n) + } + // In Go, this requires two steps. Open the dir, then call Readdirnames. + dir, err := os.Open(path) + if err != nil { + return nil, err + } + return dir.Readdirnames(n) +} + +func fsReaddirnames(path string, n int) ([]string, error) { + rawNames := jsReaddirSync(path) + length := rawNames.Get("length").Int() + if n != 0 && length > n { + // If n > 0, only return up to n names. + length = n + } + names := make([]string, length) + for i := 0; i < length; i++ { + names[i] = rawNames.Index(i).String() + } + return names, nil +} + +func osMkdirAll(path string, perm os.FileMode) error { + if isfsSupported() { + return fsMkdirAll(path, perm) + } + return os.MkdirAll(path, perm) +} + +func fsMkdirAll(path string, perm os.FileMode) (err error) { + defer func() { + if e := recover(); e != nil { + if jsErr, ok := e.(js.Error); ok { + err = convertJSError(jsErr) + } + } + }() + // Note: mkdirAll is not supported by fs so we have to manually create + // each directory. + names := strings.Split(path, string(os.PathSeparator)) + for i := range names { + partialPath := filepath.Join(names[:i+1]...) + if err := fsMkdir(partialPath, perm); err != nil { + if os.IsExist(err) { + // If the directory already exists, that's fine. + continue + } + } + } + return nil +} + +func fsMkdir(dir string, perm os.FileMode) (err error) { + defer func() { + if e := recover(); e != nil { + if jsErr, ok := e.(js.Error); ok { + err = convertJSError(jsErr) + } + } + }() + jsMkdirSync(dir, int(perm)) + return nil +} + +func fsRename(oldpath, newpath string) error { + jsRenameSync(oldpath, newpath) + return nil +} + +// isfsSupported returns true if fs is supported. It does this by +// checking for the global "fs" object. +func isfsSupported() bool { + return !reflect.DeepEqual(js.Global().Get("ZenFS"), js.Null()) && !reflect.DeepEqual(js.Global().Get("ZenFS"), js.Undefined()) +} + +// convertJSError converts an error returned by the fs API into a Go +// error. This is important because Go expects certain types of errors to be +// returned (e.g. ENOENT when a file doesn't exist) and programs often change +// their behavior depending on the type of error. +func convertJSError(err js.Error) error { + if reflect.DeepEqual(err.Value, js.Undefined()) || reflect.DeepEqual(err.Value, js.Null()) { + return nil // No error + } + // There is an error, check the code + if code := err.Value.Get("code"); !reflect.DeepEqual(code, js.Undefined()) && !reflect.DeepEqual(code, js.Null()) { + switch code.String() { + case "ENOENT": + return os.ErrNotExist + case "EISDIR": + return syscall.EISDIR + case "EEXIST": + return os.ErrExist + } + } + return err +} + +// Note: JavaScript doesn't have an flock syscall so we have to fake it. This +// won't work if another process tries to read/write to the same file. It only +// works in the context of this process, but is safe with multiple goroutines. + +// locksMu protects access to readLocks and writeLocks +var locksMu = sync.Mutex{} + +// readLocks is a map of path to the number of readers. +var readLocks = map[string]uint{} + +// writeLocks keeps track of files which are locked for writing. +var writeLocks = map[string]struct{}{} + +type fsFileLock struct { + path string + readOnly bool + file OsFile +} + +func isErrInvalid(err error) bool { + if err == os.ErrInvalid { + return true + } + // Go >= 1.8 returns *os.PathError instead + if patherr, ok := err.(*os.PathError); ok && patherr.Err == syscall.EINVAL { + return true + } + return false +} + +func jsReadSync(fd int, buffer js.Value, offset, length, position int) js.Value { + callback, resultsChan, errChan := makeAutoReleaseCallback() + js.Global().Get("ZenFS").Call("read", fd, buffer, offset, length, position, callback) + return waitForCallbackResults(resultsChan, errChan) +} + +func jsWriteSync(fd int, data js.Value, offset, length, position int) js.Value { + return js.Global().Get("ZenFS").Call("writeSync", fd, data, offset, length, position) +} + +func jsFsyncSync(fd int) { + callback, resultsChan, errChan := makeAutoReleaseCallback() + js.Global().Get("ZenFS").Call("fsync", fd, callback) + waitForCallbackResults(resultsChan, errChan) +} + +func jsCloseSync(fd int) { + callback, resultsChan, errChan := makeAutoReleaseCallback() + js.Global().Get("ZenFS").Call("close", fd, callback) + waitForCallbackResults(resultsChan, errChan) +} + +func jsStatSync(path string) js.Value { + callback, resultsChan, errChan := makeAutoReleaseCallback() + js.Global().Get("ZenFS").Call("stat", path, callback) + return waitForCallbackResults(resultsChan, errChan) +} + +func jsOpenSync(path string, flags string, mode int) js.Value { + callback, resultsChan, errChan := makeAutoReleaseCallback() + js.Global().Get("ZenFS").Call("open", path, flags, mode, callback) + return waitForCallbackResults(resultsChan, errChan) +} + +func jsUnlinkSync(path string) { + callback, resultsChan, errChan := makeAutoReleaseCallback() + js.Global().Get("ZenFS").Call("unlink", path, callback) + waitForCallbackResults(resultsChan, errChan) +} + +func jsReaddirSync(path string) js.Value { + callback, resultsChan, errChan := makeAutoReleaseCallback() + js.Global().Get("ZenFS").Call("readdir", path, callback) + return waitForCallbackResults(resultsChan, errChan) +} + +func jsMkdirSync(path string, mode int) { + callback, resultsChan, errChan := makeAutoReleaseCallback() + js.Global().Get("ZenFS").Call("mkdir", path, mode, callback) + waitForCallbackResults(resultsChan, errChan) +} + +func jsRenameSync(oldPath string, newPath string) { + callback, resultsChan, errChan := makeAutoReleaseCallback() + js.Global().Get("ZenFS").Call("rename", oldPath, newPath, callback) + waitForCallbackResults(resultsChan, errChan) +} + +func jsWriteFileSync(filename string, data []byte, perm os.FileMode) { + callback, resultsChan, errChan := makeAutoReleaseCallback() + uint8arr := js.Global().Get("Uint8Array").New(len(data)) + js.CopyBytesToJS(uint8arr, data) + opts := js.Global().Get("Object").New() + opts.Set("mode", int(perm)) + js.Global().Get("ZenFS").Call("writeFile", filename, uint8arr, opts, callback) + waitForCallbackResults(resultsChan, errChan) +} + +// makeAutoReleaseCallback creates and returns a js.Func that can be used as a +// callback. The callback will be released immediately after being called. The +// callback assumes the JavaScript callback convention and accepts two +// arguments: (err, result). If err is not null or undefined, it will send err +// through errChan. Otherwise, it will send result through resultsChan. +func makeAutoReleaseCallback() (callback js.Func, resultsChan chan js.Value, errChan chan error) { + resultsChan = make(chan js.Value, 1) + errChan = make(chan error, 1) + callback = js.FuncOf(func(this js.Value, args []js.Value) interface{} { + defer callback.Release() + go func() { + if len(args) == 0 { + resultsChan <- js.Undefined() + return + } + err := args[0] + if !reflect.DeepEqual(err, js.Undefined()) && !reflect.DeepEqual(err, js.Null()) { + errChan <- js.Error{Value: err} + return + } + if len(args) >= 2 { + resultsChan <- args[1] + } else { + resultsChan <- js.Undefined() + } + }() + return nil + }) + return callback, resultsChan, errChan +} + +// waitForCallbackResults blocks until receiving from either resultsChan or +// errChan. If it receives from resultsChan first, it will return the result. If +// it receives from errChan first, it will panic with the error. +func waitForCallbackResults(resultsChan chan js.Value, errChan chan error) js.Value { + select { + case result := <-resultsChan: + return result + case err := <-errChan: + // Expected to be recovered up the call stack. + panic(err) + } +} + +func writeFile(filename string, data []byte, perm os.FileMode) error { + jsWriteFileSync(filename, data, perm) + return nil +} + +func osOpenFile(name string, flag int, perm os.FileMode) (OsFile, error) { + if isfsSupported() { + return fsOpenFile(name, flag, perm) + } + return os.OpenFile(name, flag, perm) +} + +func (f *fsFile) WriteAt(b []byte, off int64) (n int, err error) { + defer func() { + if e := recover(); e != nil { + if jsErr, ok := e.(js.Error); ok { + err = convertJSError(jsErr) + } + } + }() + uint8arr := js.Global().Get("Uint8Array").New(len(b)) + js.CopyBytesToJS(uint8arr, b) + rawBytesWritten := jsWriteSync(f.fd, uint8arr, 0, len(b), int(off)) + bytesWritten := rawBytesWritten.Int() + if bytesWritten != len(b) { + return bytesWritten, io.ErrShortWrite + } + if err := f.Sync(); err != nil { + return bytesWritten, err + } + return bytesWritten, nil +} +func (f *fsFile) Truncate(size int64) (err error) { + defer func() { + if e := recover(); e != nil { + if jsErr, ok := e.(js.Error); ok { + err = convertJSError(jsErr) + } + } + }() + js.Global().Get("ZenFS").Call("ftruncateSync", f.fd, size) + return nil +} + +func osRemove(name string) error { + if isfsSupported() { + return fsRemove(name) + } + return os.Remove(name) +} + +func fsRemove(name string) (err error) { + defer func() { + if e := recover(); e != nil { + if jsErr, ok := e.(js.Error); ok { + err = convertJSError(jsErr) + } + } + }() + jsUnlinkSync(name) + return nil +} + +func (f *fsFile) WriteString(s string) (n int, err error) { + return f.Write([]byte(s)) +} diff --git a/pkg/fs/fs_native.go b/pkg/fs/fs_native.go new file mode 100644 index 00000000000..7eaebc80fb8 --- /dev/null +++ b/pkg/fs/fs_native.go @@ -0,0 +1,40 @@ +//go:build !js +// +build !js + +// Copyright (c) 2012, Suryandaru Triandana +// All rights reservefs. +// +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package fs + +import ( + "os" +) + +// osOpen calls os.Open. +func osOpen(name string) (OsFile, error) { + return os.Open(name) +} + +func osOpenFile(name string, flag int, perm os.FileMode) (OsFile, error) { + return os.OpenFile(name, flag, perm) +} + +// osMkdirAll calls os.MkdirAll. +func osMkdirAll(path string, perm os.FileMode) error { + return os.MkdirAll(path, perm) +} + +func writeFile(filename string, data []byte, perm os.FileMode) error { + return os.WriteFile(filename, data, perm) +} + +func fsStat(name string) (os.FileInfo, error) { + return os.Stat(name) +} + +func osRemove(name string) error { + return os.Remove(name) +} diff --git a/pkg/keystore/file/service.go b/pkg/keystore/file/service.go index 01a5db72aeb..a43c0cfcf5e 100644 --- a/pkg/keystore/file/service.go +++ b/pkg/keystore/file/service.go @@ -11,6 +11,8 @@ import ( "path/filepath" "github.com/ethersphere/bee/v2/pkg/keystore" + + fs "github.com/ethersphere/bee/v2/pkg/fs" ) // Service is the file-based keystore.Service implementation. @@ -29,7 +31,7 @@ func New(dir string) *Service { func (s *Service) Exists(name string) (bool, error) { filename := s.keyFilename(name) - data, err := os.ReadFile(filename) + data, err := fs.ReadFile(filename) if err != nil && !os.IsNotExist(err) { return false, fmt.Errorf("read private key: %w", err) } @@ -53,11 +55,11 @@ func (s *Service) SetKey(name, password string, edg keystore.EDG) (*ecdsa.Privat filename := s.keyFilename(name) - if err := os.MkdirAll(filepath.Dir(filename), 0700); err != nil { + if err := fs.MkdirAll(filepath.Dir(filename), 0700); err != nil { return nil, err } - if err := os.WriteFile(filename, d, 0600); err != nil { + if err := fs.WriteFile(filename, d, 0600); err != nil { return nil, err } @@ -67,7 +69,7 @@ func (s *Service) SetKey(name, password string, edg keystore.EDG) (*ecdsa.Privat func (s *Service) Key(name, password string, edg keystore.EDG) (pk *ecdsa.PrivateKey, created bool, err error) { filename := s.keyFilename(name) - data, err := os.ReadFile(filename) + data, err := fs.ReadFile(filename) if err != nil && !os.IsNotExist(err) { return nil, false, fmt.Errorf("read private key: %w", err) } diff --git a/pkg/node/node.go b/pkg/node/node.go index 5f2a108617b..8daae2a3559 100644 --- a/pkg/node/node.go +++ b/pkg/node/node.go @@ -9,78 +9,22 @@ package node import ( "context" - "crypto/ecdsa" - "encoding/hex" "errors" "fmt" "io" - stdlog "log" - "math/big" "net" "net/http" - "path/filepath" - "runtime" "strconv" "sync" - "sync/atomic" "time" - "github.com/ethereum/go-ethereum/common" - "github.com/ethersphere/bee/v2/pkg/accesscontrol" - "github.com/ethersphere/bee/v2/pkg/accounting" - "github.com/ethersphere/bee/v2/pkg/addressbook" - "github.com/ethersphere/bee/v2/pkg/api" - "github.com/ethersphere/bee/v2/pkg/config" - "github.com/ethersphere/bee/v2/pkg/crypto" - "github.com/ethersphere/bee/v2/pkg/feeds/factory" - "github.com/ethersphere/bee/v2/pkg/gsoc" - "github.com/ethersphere/bee/v2/pkg/hive" "github.com/ethersphere/bee/v2/pkg/log" - "github.com/ethersphere/bee/v2/pkg/metrics" "github.com/ethersphere/bee/v2/pkg/p2p" - "github.com/ethersphere/bee/v2/pkg/p2p/libp2p" - "github.com/ethersphere/bee/v2/pkg/pingpong" - "github.com/ethersphere/bee/v2/pkg/postage" - "github.com/ethersphere/bee/v2/pkg/postage/batchservice" - "github.com/ethersphere/bee/v2/pkg/postage/batchstore" - "github.com/ethersphere/bee/v2/pkg/postage/listener" - "github.com/ethersphere/bee/v2/pkg/postage/postagecontract" - "github.com/ethersphere/bee/v2/pkg/pricer" - "github.com/ethersphere/bee/v2/pkg/pricing" - "github.com/ethersphere/bee/v2/pkg/pss" - "github.com/ethersphere/bee/v2/pkg/puller" - "github.com/ethersphere/bee/v2/pkg/pullsync" - "github.com/ethersphere/bee/v2/pkg/pusher" - "github.com/ethersphere/bee/v2/pkg/pushsync" "github.com/ethersphere/bee/v2/pkg/resolver/multiresolver" - "github.com/ethersphere/bee/v2/pkg/retrieval" - "github.com/ethersphere/bee/v2/pkg/salud" - "github.com/ethersphere/bee/v2/pkg/settlement/pseudosettle" - "github.com/ethersphere/bee/v2/pkg/settlement/swap" - "github.com/ethersphere/bee/v2/pkg/settlement/swap/chequebook" - "github.com/ethersphere/bee/v2/pkg/settlement/swap/erc20" - "github.com/ethersphere/bee/v2/pkg/settlement/swap/priceoracle" - "github.com/ethersphere/bee/v2/pkg/stabilization" - "github.com/ethersphere/bee/v2/pkg/status" - "github.com/ethersphere/bee/v2/pkg/steward" - "github.com/ethersphere/bee/v2/pkg/storageincentives" - "github.com/ethersphere/bee/v2/pkg/storageincentives/redistribution" - "github.com/ethersphere/bee/v2/pkg/storageincentives/staking" - "github.com/ethersphere/bee/v2/pkg/storer" "github.com/ethersphere/bee/v2/pkg/swarm" "github.com/ethersphere/bee/v2/pkg/topology" - "github.com/ethersphere/bee/v2/pkg/topology/kademlia" - "github.com/ethersphere/bee/v2/pkg/topology/lightnode" - "github.com/ethersphere/bee/v2/pkg/tracing" - "github.com/ethersphere/bee/v2/pkg/transaction" - "github.com/ethersphere/bee/v2/pkg/util/abiutil" - "github.com/ethersphere/bee/v2/pkg/util/ioutil" - "github.com/ethersphere/bee/v2/pkg/util/nbhdutil" "github.com/ethersphere/bee/v2/pkg/util/syncutil" "github.com/hashicorp/go-multierror" - ma "github.com/multiformats/go-multiaddr" - "github.com/prometheus/client_golang/prometheus" - "golang.org/x/crypto/sha3" "golang.org/x/sync/errgroup" ) @@ -203,1143 +147,6 @@ const ( maxAllowedDoubling = 1 ) -func NewBee( - ctx context.Context, - addr string, - publicKey *ecdsa.PublicKey, - signer crypto.Signer, - networkID uint64, - logger log.Logger, - libp2pPrivateKey, - pssPrivateKey *ecdsa.PrivateKey, - session accesscontrol.Session, - o *Options, -) (b *Bee, err error) { - // start time for node warmup duration measurement - warmupStartTime := time.Now() - var pullSyncStartTime time.Time - - nodeMetrics := newMetrics() - - tracer, tracerCloser, err := tracing.NewTracer(&tracing.Options{ - Enabled: o.TracingEnabled, - Endpoint: o.TracingEndpoint, - ServiceName: o.TracingServiceName, - }) - if err != nil { - return nil, fmt.Errorf("tracer: %w", err) - } - - if err := validatePublicAddress(o.NATAddr); err != nil { - return nil, fmt.Errorf("invalid NAT address %s: %w", o.NATAddr, err) - } - - if err := validatePublicAddress(o.NATWSSAddr); err != nil { - return nil, fmt.Errorf("invalid NAT WSS address %s: %w", o.NATWSSAddr, err) - } - - ctx, ctxCancel := context.WithCancel(ctx) - defer func() { - // if there's been an error on this function - // we'd like to cancel the p2p context so that - // incoming connections will not be possible - if err != nil { - ctxCancel() - } - }() - - // light nodes have zero warmup time for pull/pushsync protocols - warmupTime := o.WarmupTime - if !o.FullNodeMode { - warmupTime = 0 - } - - sink := ioutil.WriterFunc(func(p []byte) (int, error) { - logger.Error(nil, string(p)) - return len(p), nil - }) - - b = &Bee{ - ctxCancel: ctxCancel, - errorLogWriter: sink, - tracerCloser: tracerCloser, - syncingStopped: syncutil.NewSignaler(), - } - - defer func(b *Bee) { - if err != nil { - logger.Error(err, "got error, shutting down...") - if err2 := b.Shutdown(); err2 != nil { - logger.Error(err2, "got error while shutting down") - } - } - }(b) - - if !o.FullNodeMode && o.ReserveCapacityDoubling != 0 { - return nil, fmt.Errorf("reserve capacity doubling is only allowed for full nodes") - } - - if o.ReserveCapacityDoubling < 0 || o.ReserveCapacityDoubling > maxAllowedDoubling { - return nil, fmt.Errorf("config reserve capacity doubling has to be between default: 0 and maximum: %d", maxAllowedDoubling) - } - shallowReceiptTolerance := maxAllowedDoubling - o.ReserveCapacityDoubling - - reserveCapacity := (1 << o.ReserveCapacityDoubling) * storer.DefaultReserveCapacity - - stateStore, stateStoreMetrics, err := InitStateStore(logger, o.DataDir, o.StatestoreCacheCapacity) - if err != nil { - return nil, fmt.Errorf("init state store: %w", err) - } - - pubKey, err := signer.PublicKey() - if err != nil { - return nil, fmt.Errorf("signer public key: %w", err) - } - - nonce, nonceExists, err := overlayNonceExists(stateStore) - if err != nil { - return nil, fmt.Errorf("check presence of nonce: %w", err) - } - - swarmAddress, err := crypto.NewOverlayAddress(*pubKey, networkID, nonce) - if err != nil { - return nil, fmt.Errorf("compute overlay address: %w", err) - } - - targetNeighborhood := o.TargetNeighborhood - if targetNeighborhood == "" && !nonceExists && o.NeighborhoodSuggester != "" { - logger.Info("fetching target neighborhood from suggester", "url", o.NeighborhoodSuggester) - targetNeighborhood, err = nbhdutil.FetchNeighborhood(&http.Client{}, o.NeighborhoodSuggester) - if err != nil { - return nil, fmt.Errorf("neighborhood suggestion: %w", err) - } - } - - var changedOverlay, resetReserve bool - if targetNeighborhood != "" { - neighborhood, err := swarm.ParseBitStrAddress(targetNeighborhood) - if err != nil { - return nil, fmt.Errorf("invalid neighborhood. %s", targetNeighborhood) - } - - if swarm.Proximity(swarmAddress.Bytes(), neighborhood.Bytes()) < uint8(len(targetNeighborhood)) { - // mine the overlay - logger.Info("mining a new overlay address to target the selected neighborhood", "target", targetNeighborhood) - newSwarmAddress, newNonce, err := nbhdutil.MineOverlay(ctx, *pubKey, networkID, targetNeighborhood) - if err != nil { - return nil, fmt.Errorf("mine overlay address: %w", err) - } - - if nonceExists { - logger.Info("Override nonce and clean state for neighborhood", "old_none", hex.EncodeToString(nonce), "new_nonce", hex.EncodeToString(newNonce)) - logger.Warning("you have another 10 seconds to change your mind and kill this process with CTRL-C...") - time.Sleep(10 * time.Second) - - err := ioutil.RemoveContent(filepath.Join(o.DataDir, ioutil.DataPathKademlia)) - if err != nil { - return nil, fmt.Errorf("delete %s: %w", ioutil.DataPathKademlia, err) - } - - if err := stateStore.ClearForHopping(); err != nil { - return nil, fmt.Errorf("clearing stateStore %w", err) - } - resetReserve = true - } - - swarmAddress = newSwarmAddress - nonce = newNonce - err = setOverlay(stateStore, swarmAddress, nonce) - if err != nil { - return nil, fmt.Errorf("statestore: save new overlay: %w", err) - } - changedOverlay = true - } - } - - b.stateStoreCloser = stateStore - // Check if the batchstore exists. If not, we can assume it's missing - // due to a migration or it's a fresh install. - batchStoreExists, err := batchStoreExists(stateStore) - if err != nil { - return nil, fmt.Errorf("batchstore: exists: %w", err) - } - - addressbook := addressbook.New(stateStore) - - logger.Info("using overlay address", "address", swarmAddress) - - // this will set overlay if it was not set before - if err = checkOverlay(stateStore, swarmAddress); err != nil { - return nil, fmt.Errorf("check overlay address: %w", err) - } - - var ( - chequebookService chequebook.Service = new(noOpChequebookService) - chequeStore chequebook.ChequeStore - cashoutService chequebook.CashoutService - erc20Service erc20.Service - ) - - chainEnabled := isChainEnabled(o, o.BlockchainRpcEndpoint, logger) - - var batchStore postage.Storer = new(postage.NoOpBatchStore) - var evictFn func([]byte) error - - if chainEnabled { - batchStore, err = batchstore.New( - stateStore, - func(id []byte) error { - return evictFn(id) - }, - reserveCapacity, - logger, - ) - if err != nil { - return nil, fmt.Errorf("batchstore: %w", err) - } - } - - chainBackend, overlayEthAddress, chainID, transactionMonitor, transactionService, err := InitChain( - ctx, - logger, - stateStore, - o.BlockchainRpcEndpoint, - o.ChainID, - signer, - o.BlockTime, - chainEnabled, - o.MinimumGasTipCap, - ) - if err != nil { - return nil, fmt.Errorf("init chain: %w", err) - } - - logger.Info("using chain with network", "chain_id", chainID, "network_id", networkID) - - b.ethClientCloser = chainBackend.Close - b.transactionCloser = tracerCloser - b.transactionMonitorCloser = transactionMonitor - - beeNodeMode := api.LightMode - if o.FullNodeMode { - beeNodeMode = api.FullMode - } else if !chainEnabled { - beeNodeMode = api.UltraLightMode - } - - // Create api.Probe in healthy state and switch to ready state after all components have been constructed - probe := api.NewProbe() - probe.SetHealthy(api.ProbeStatusOK) - defer func(probe *api.Probe) { - if err != nil { - probe.SetHealthy(api.ProbeStatusNOK) - } else { - probe.SetReady(api.ProbeStatusOK) - } - }(probe) - - stamperStore, err := InitStamperStore(logger, o.DataDir, stateStore) - if err != nil { - return nil, fmt.Errorf("failed to initialize stamper store: %w", err) - } - b.stamperStoreCloser = stamperStore - - var apiService *api.Service - - if o.APIAddr != "" { - if o.MutexProfile { - _ = runtime.SetMutexProfileFraction(1) - } - if o.BlockProfile { - runtime.SetBlockProfileRate(1) - } - - apiListener, err := (&net.ListenConfig{}).Listen(ctx, "tcp", o.APIAddr) - if err != nil { - return nil, fmt.Errorf("api listener: %w", err) - } - - apiService = api.New( - *publicKey, - pssPrivateKey.PublicKey, - overlayEthAddress, - o.WhitelistedWithdrawalAddress, - logger, - transactionService, - batchStore, - beeNodeMode, - o.ChequebookEnable, - o.SwapEnable, - chainBackend, - o.CORSAllowedOrigins, - stamperStore, - ) - - apiService.Mount() - apiService.SetProbe(probe) - apiService.SetIsWarmingUp(true) - apiService.SetSwarmAddress(&swarmAddress) - - apiServer := &http.Server{ - IdleTimeout: 30 * time.Second, - ReadHeaderTimeout: 3 * time.Second, - Handler: apiService, - ErrorLog: stdlog.New(b.errorLogWriter, "", 0), - } - - go func() { - logger.Info("starting debug & api server", "address", apiListener.Addr()) - - if err := apiServer.Serve(apiListener); err != nil && !errors.Is(err, http.ErrServerClosed) { - logger.Debug("debug & api server failed to start", "error", err) - logger.Error(nil, "debug & api server failed to start") - } - }() - - b.apiServer = apiServer - b.apiCloser = apiServer - } - - // Sync the with the given Ethereum backend: - isSynced, _, err := transaction.IsSynced(ctx, chainBackend, maxDelay) - if err != nil { - return nil, fmt.Errorf("is synced: %w", err) - } - if !isSynced { - logger.Info("waiting to sync with the blockchain backend") - - err := transaction.WaitSynced(ctx, logger, chainBackend, maxDelay) - if err != nil { - return nil, fmt.Errorf("waiting backend sync: %w", err) - } - } - - if o.SwapEnable { - chequebookFactory, err := InitChequebookFactory(logger, chainBackend, chainID, transactionService, o.SwapFactoryAddress) - if err != nil { - return nil, fmt.Errorf("init chequebook factory: %w", err) - } - - erc20Address, err := chequebookFactory.ERC20Address(ctx) - if err != nil { - return nil, fmt.Errorf("factory fail: %w", err) - } - - erc20Service = erc20.New(transactionService, erc20Address) - - if o.ChequebookEnable && chainEnabled { - chequebookService, err = InitChequebookService( - ctx, - logger, - stateStore, - signer, - chainID, - chainBackend, - overlayEthAddress, - transactionService, - chequebookFactory, - o.SwapInitialDeposit, - erc20Service, - ) - if err != nil { - return nil, fmt.Errorf("init chequebook service: %w", err) - } - } - - chequeStore, cashoutService = initChequeStoreCashout( - stateStore, - chainBackend, - chequebookFactory, - chainID, - overlayEthAddress, - transactionService, - ) - } - - lightNodes := lightnode.NewContainer(swarmAddress) - - bootnodes := make([]ma.Multiaddr, 0, len(o.Bootnodes)) - - for _, a := range o.Bootnodes { - addr, err := ma.NewMultiaddr(a) - if err != nil { - logger.Debug("create bootnode multiaddress from string failed", "string", a, "error", err) - logger.Warning("create bootnode multiaddress from string failed", "string", a) - continue - } - - bootnodes = append(bootnodes, addr) - } - - // Perform checks related to payment threshold calculations here to not duplicate - // the checks in bootstrap process - paymentThreshold, ok := new(big.Int).SetString(o.PaymentThreshold, 10) - if !ok { - return nil, fmt.Errorf("invalid payment threshold: %s", paymentThreshold) - } - - if paymentThreshold.Cmp(big.NewInt(minPaymentThreshold)) < 0 { - return nil, fmt.Errorf("payment threshold below minimum generally accepted value, need at least %d", minPaymentThreshold) - } - - if paymentThreshold.Cmp(big.NewInt(maxPaymentThreshold)) > 0 { - return nil, fmt.Errorf("payment threshold above maximum generally accepted value, needs to be reduced to at most %d", maxPaymentThreshold) - } - - if o.PaymentTolerance < 0 { - return nil, fmt.Errorf("invalid payment tolerance: %d", o.PaymentTolerance) - } - - if o.PaymentEarly > 100 || o.PaymentEarly < 0 { - return nil, fmt.Errorf("invalid payment early: %d", o.PaymentEarly) - } - - detector, err := stabilization.NewDetector(stabilization.Config{ - PeriodDuration: 2 * time.Second, - NumPeriodsForStabilization: 5, - StabilizationFactor: 3, - MinimumPeriods: 2, - WarmupTime: warmupTime, - }) - if err != nil { - return nil, fmt.Errorf("rate stabilizer configuration failed: %w", err) - } - defer detector.Close() - - detector.OnMonitoringStart = func(t time.Time) { - logger.Info("node warmup check initiated. monitoring activity rate to determine readiness.", "startTime", t) - } - - warmupMeasurement := func(t time.Time, totalCount int) { - warmupDuration := t.Sub(warmupStartTime).Seconds() - logger.Info("node warmup complete. system is considered stable and ready.", - "stabilizationTime", t, - "totalMonitoredEvents", totalCount, - "warmupDurationSeconds", warmupDuration) - - nodeMetrics.WarmupDuration.Observe(warmupDuration) - pullSyncStartTime = t - } - detector.OnStabilized = warmupMeasurement - - detector.OnPeriodComplete = func(t time.Time, periodCount int, stDev float64) { - logger.Debug("node warmup check: period complete.", "periodEndTime", t, "eventsInPeriod", periodCount, "rateStdDev", stDev) - } - - var initBatchState *postage.ChainSnapshot - // Bootstrap node with postage snapshot only if it is running on mainnet, is a fresh - // install or explicitly asked by user to resync - if networkID == mainnetNetworkID && o.UsePostageSnapshot && (!batchStoreExists || o.Resync) { - start := time.Now() - logger.Info("cold postage start detected. fetching postage stamp snapshot from swarm") - initBatchState, err = bootstrapNode( - ctx, - addr, - swarmAddress, - nonce, - addressbook, - bootnodes, - lightNodes, - stateStore, - signer, - networkID, - log.Noop, - libp2pPrivateKey, - detector, - o, - ) - logger.Info("bootstrapper created", "elapsed", time.Since(start)) - if err != nil { - logger.Error(err, "bootstrapper failed to fetch batch state") - } - } - - var registry *prometheus.Registry - - if apiService != nil { - registry = apiService.MetricsRegistry() - } - - p2ps, err := libp2p.New(ctx, signer, networkID, swarmAddress, addr, addressbook, stateStore, lightNodes, logger, tracer, libp2p.Options{ - PrivateKey: libp2pPrivateKey, - NATAddr: o.NATAddr, - NATWSSAddr: o.NATWSSAddr, - EnableWS: o.EnableWS, - EnableWSS: o.EnableWSS, - WSSAddr: o.WSSAddr, - AutoTLSStorageDir: o.AutoTLSStorageDir, - AutoTLSDomain: o.AutoTLSDomain, - AutoTLSRegistrationEndpoint: o.AutoTLSRegistrationEndpoint, - AutoTLSCAEndpoint: o.AutoTLSCAEndpoint, - WelcomeMessage: o.WelcomeMessage, - FullNode: o.FullNodeMode, - Nonce: nonce, - ValidateOverlay: chainEnabled, - Registry: registry, - }) - if err != nil { - return nil, fmt.Errorf("p2p service: %w", err) - } - - apiService.SetP2P(p2ps) - - b.p2pService = p2ps - b.p2pHalter = p2ps - - post, err := postage.NewService(logger, stamperStore, batchStore, chainID) - if err != nil { - return nil, fmt.Errorf("postage service: %w", err) - } - b.postageServiceCloser = post - batchStore.SetBatchExpiryHandler(post) - - var ( - postageStampContractService postagecontract.Interface - batchSvc postage.EventUpdater - eventListener postage.Listener - ) - - chainCfg, found := config.GetByChainID(chainID) - postageStampContractAddress, postageSyncStart := chainCfg.PostageStampAddress, chainCfg.PostageStampStartBlock - if o.PostageContractAddress != "" { - if !common.IsHexAddress(o.PostageContractAddress) { - return nil, errors.New("malformed postage stamp address") - } - postageStampContractAddress = common.HexToAddress(o.PostageContractAddress) - if o.PostageContractStartBlock == 0 { - return nil, errors.New("postage contract start block option not provided") - } - postageSyncStart = o.PostageContractStartBlock - } else if !found { - return nil, errors.New("no known postage stamp addresses for this network") - } - - postageStampContractABI := abiutil.MustParseABI(chainCfg.PostageStampABI) - - bzzTokenAddress, err := postagecontract.LookupERC20Address(ctx, transactionService, postageStampContractAddress, postageStampContractABI, chainEnabled) - if err != nil { - return nil, fmt.Errorf("lookup erc20 postage address: %w", err) - } - - postageStampContractService = postagecontract.New( - overlayEthAddress, - postageStampContractAddress, - postageStampContractABI, - bzzTokenAddress, - transactionService, - post, - batchStore, - chainEnabled, - o.TrxDebugMode, - ) - - eventListener = listener.New(b.syncingStopped, logger, chainBackend, postageStampContractAddress, postageStampContractABI, o.BlockTime, postageSyncingStallingTimeout, postageSyncingBackoffTimeout) - b.listenerCloser = eventListener - - batchSvc, err = batchservice.New(stateStore, batchStore, logger, eventListener, overlayEthAddress.Bytes(), post, sha3.New256, o.Resync) - if err != nil { - return nil, fmt.Errorf("init batch service: %w", err) - } - - // Construct protocols. - pingPong := pingpong.New(p2ps, logger, tracer) - - if err = p2ps.AddProtocol(pingPong.Protocol()); err != nil { - return nil, fmt.Errorf("pingpong service: %w", err) - } - - hive := hive.New(p2ps, addressbook, networkID, o.BootnodeMode, o.AllowPrivateCIDRs, logger) - - if err = p2ps.AddProtocol(hive.Protocol()); err != nil { - return nil, fmt.Errorf("hive service: %w", err) - } - b.hiveCloser = hive - - var swapService *swap.Service - - kad, err := kademlia.New(swarmAddress, addressbook, hive, p2ps, detector, logger, - kademlia.Options{Bootnodes: bootnodes, BootnodeMode: o.BootnodeMode, StaticNodes: o.StaticNodes, DataDir: o.DataDir}) - if err != nil { - return nil, fmt.Errorf("unable to create kademlia: %w", err) - } - b.topologyCloser = kad - b.topologyHalter = kad - hive.SetAddPeersHandler(kad.AddPeers) - p2ps.SetPickyNotifier(kad) - - var path string - - if o.DataDir != "" { - logger.Info("using datadir", "path", o.DataDir) - path = filepath.Join(o.DataDir, ioutil.DataPathLocalstore) - } - - lo := &storer.Options{ - Address: swarmAddress, - CacheCapacity: o.CacheCapacity, - LdbOpenFilesLimit: o.DBOpenFilesLimit, - LdbBlockCacheCapacity: o.DBBlockCacheCapacity, - LdbWriteBufferSize: o.DBWriteBufferSize, - LdbDisableSeeksCompaction: o.DBDisableSeeksCompaction, - Batchstore: batchStore, - StateStore: stateStore, - RadiusSetter: kad, - StartupStabilizer: detector, - Logger: logger, - Tracer: tracer, - CacheMinEvictCount: cacheMinEvictCount, - MinimumStorageRadius: o.MinimumStorageRadius, - } - - if o.FullNodeMode && !o.BootnodeMode { - // configure reserve only for full node - lo.ReserveCapacity = reserveCapacity - lo.ReserveWakeUpDuration = reserveWakeUpDuration - lo.ReserveMinEvictCount = reserveMinEvictCount - lo.RadiusSetter = kad - lo.ReserveCapacityDoubling = o.ReserveCapacityDoubling - } - - localStore, err := storer.New(ctx, path, lo) - if err != nil { - return nil, fmt.Errorf("localstore: %w", err) - } - b.localstoreCloser = localStore - evictFn = func(id []byte) error { return localStore.EvictBatch(context.Background(), id) } - - if resetReserve { - logger.Warning("resetting the reserve") - err := localStore.ResetReserve(ctx) - if err != nil { - return nil, fmt.Errorf("reset reserve: %w", err) - } - } - - actLogic := accesscontrol.NewLogic(session) - accesscontrol := accesscontrol.NewController(actLogic) - b.accesscontrolCloser = accesscontrol - - var ( - syncErr atomic.Value - syncStatus atomic.Value - - syncStatusFn = func() (isDone bool, err error) { - iErr := syncErr.Load() - if iErr != nil { - err = iErr.(error) - } - isDone = syncStatus.Load() != nil - return isDone, err - } - ) - - if !o.SkipPostageSnapshot && !batchStoreExists && (networkID == mainnetNetworkID) && beeNodeMode != api.UltraLightMode { - chainBackend := NewSnapshotLogFilterer(logger, archiveSnapshotGetter{}) - - snapshotEventListener := listener.New(b.syncingStopped, logger, chainBackend, postageStampContractAddress, postageStampContractABI, o.BlockTime, postageSyncingStallingTimeout, postageSyncingBackoffTimeout) - - snapshotBatchSvc, err := batchservice.New(stateStore, batchStore, logger, snapshotEventListener, overlayEthAddress.Bytes(), post, sha3.New256, o.Resync) - if err != nil { - logger.Error(err, "failed to initialize batch service from snapshot, continuing outside snapshot block...") - } else { - err = snapshotBatchSvc.Start(ctx, postageSyncStart, initBatchState) - syncStatus.Store(true) - if err != nil { - syncErr.Store(err) - logger.Error(err, "failed to start batch service from snapshot, continuing outside snapshot block...") - } else { - postageSyncStart = chainBackend.maxBlockHeight - } - } - if errClose := snapshotEventListener.Close(); errClose != nil { - logger.Error(errClose, "failed to close event listener (snapshot) failure") - } - - } - - if batchSvc != nil && chainEnabled { - logger.Info("waiting to sync postage contract data, this may take a while... more info available in Debug loglevel") - - paused, err := postageStampContractService.Paused(ctx) - if err != nil { - logger.Error(err, "Error checking postage contract is paused") - } - - if paused { - return nil, errors.New("postage contract is paused") - } - - if o.FullNodeMode { - err = batchSvc.Start(ctx, postageSyncStart, initBatchState) - syncStatus.Store(true) - if err != nil { - syncErr.Store(err) - return nil, fmt.Errorf("unable to start batch service: %w", err) - } - } else { - go func() { - logger.Info("started postage contract data sync in the background...") - err := batchSvc.Start(ctx, postageSyncStart, initBatchState) - syncStatus.Store(true) - if err != nil { - syncErr.Store(err) - logger.Error(err, "unable to sync batches") - b.syncingStopped.Signal() // trigger shutdown in start.go - } - }() - } - - } - - minThreshold := big.NewInt(2 * refreshRate) - maxThreshold := big.NewInt(24 * refreshRate) - - if !o.FullNodeMode { - minThreshold = big.NewInt(2 * lightRefreshRate) - } - - lightPaymentThreshold := new(big.Int).Div(paymentThreshold, big.NewInt(lightFactor)) - - pricer := pricer.NewFixedPricer(swarmAddress, basePrice) - - if paymentThreshold.Cmp(minThreshold) < 0 { - return nil, fmt.Errorf("payment threshold below minimum generally accepted value, need at least %s", minThreshold) - } - - if paymentThreshold.Cmp(maxThreshold) > 0 { - return nil, fmt.Errorf("payment threshold above maximum generally accepted value, needs to be reduced to at most %s", maxThreshold) - } - - pricing := pricing.New(p2ps, logger, paymentThreshold, lightPaymentThreshold, minThreshold) - - if err = p2ps.AddProtocol(pricing.Protocol()); err != nil { - return nil, fmt.Errorf("pricing service: %w", err) - } - - addrs, err := p2ps.Addresses() - if err != nil { - return nil, fmt.Errorf("get server addresses: %w", err) - } - - for _, addr := range addrs { - logger.Debug("p2p address", "address", addr) - } - - var enforcedRefreshRate *big.Int - - if o.FullNodeMode { - enforcedRefreshRate = big.NewInt(refreshRate) - } else { - enforcedRefreshRate = big.NewInt(lightRefreshRate) - } - - acc, err := accounting.NewAccounting( - paymentThreshold, - o.PaymentTolerance, - o.PaymentEarly, - logger, - stateStore, - pricing, - new(big.Int).Set(enforcedRefreshRate), - lightFactor, - p2ps, - ) - if err != nil { - return nil, fmt.Errorf("accounting: %w", err) - } - b.accountingCloser = acc - - pseudosettleService := pseudosettle.New(p2ps, logger, stateStore, acc, new(big.Int).Set(enforcedRefreshRate), big.NewInt(lightRefreshRate), p2ps) - if err = p2ps.AddProtocol(pseudosettleService.Protocol()); err != nil { - return nil, fmt.Errorf("pseudosettle service: %w", err) - } - - acc.SetRefreshFunc(pseudosettleService.Pay) - - if o.SwapEnable && chainEnabled { - var priceOracle priceoracle.Service - swapService, priceOracle, err = InitSwap( - p2ps, - logger, - stateStore, - networkID, - overlayEthAddress, - chequebookService, - chequeStore, - cashoutService, - acc, - o.PriceOracleAddress, - chainID, - transactionService, - ) - if err != nil { - return nil, fmt.Errorf("init swap service: %w", err) - } - b.priceOracleCloser = priceOracle - - if o.ChequebookEnable { - acc.SetPayFunc(swapService.Pay) - } - } - - pricing.SetPaymentThresholdObserver(acc) - - pssService := pss.New(pssPrivateKey, logger) - gsocService := gsoc.New(logger) - b.pssCloser = pssService - b.gsocCloser = gsocService - - validStamp := postage.ValidStamp(batchStore) - - // metrics exposed on the status protocol - statusMetricsRegistry := prometheus.NewRegistry() - if localStore != nil { - statusMetricsRegistry.MustRegister(localStore.StatusMetrics()...) - } - if p2ps != nil { - statusMetricsRegistry.MustRegister(p2ps.StatusMetrics()...) - } - - nodeStatus := status.NewService(logger, p2ps, kad, beeNodeMode.String(), batchStore, localStore, statusMetricsRegistry) - if err = p2ps.AddProtocol(nodeStatus.Protocol()); err != nil { - return nil, fmt.Errorf("status service: %w", err) - } - - saludService := salud.New(nodeStatus, kad, localStore, logger, detector, api.FullMode.String(), salud.DefaultDurPercentile, salud.DefaultConnsPercentile) - b.saludCloser = saludService - - rC, unsub := saludService.SubscribeNetworkStorageRadius() - initialRadiusC := make(chan struct{}) - var networkR atomic.Uint32 - networkR.Store(uint32(swarm.MaxBins)) - - go func() { - for { - select { - case r := <-rC: - prev := networkR.Load() - networkR.Store(uint32(r)) - if prev == uint32(swarm.MaxBins) { - close(initialRadiusC) - } - if !o.FullNodeMode { // light and ultra-light nodes do not have a reserve worker to set the radius. - kad.SetStorageRadius(r) - } - case <-ctx.Done(): - unsub() - return - } - } - }() - - waitNetworkRFunc := func() (uint8, error) { - if networkR.Load() == uint32(swarm.MaxBins) { - select { - case <-initialRadiusC: - case <-ctx.Done(): - return 0, ctx.Err() - } - } - - local, network := localStore.StorageRadius(), uint8(networkR.Load()) - if local <= uint8(o.MinimumStorageRadius) { - return max(network, uint8(o.MinimumStorageRadius)), nil - } else { - return local, nil - } - } - - pushSyncProtocol := pushsync.New(swarmAddress, networkID, nonce, p2ps, localStore, waitNetworkRFunc, kad, o.FullNodeMode && !o.BootnodeMode, pssService.TryUnwrap, gsocService.Handle, validStamp, logger, acc, pricer, signer, tracer, detector, uint8(shallowReceiptTolerance)) - b.pushSyncCloser = pushSyncProtocol - - // set the pushSyncer in the PSS - pssService.SetPushSyncer(pushSyncProtocol) - - retrieval := retrieval.New(swarmAddress, waitNetworkRFunc, localStore, p2ps, kad, logger, acc, pricer, tracer, o.RetrievalCaching) - localStore.SetRetrievalService(retrieval) - - statusMetricsRegistry.MustRegister(retrieval.StatusMetrics()...) - - pusherService := pusher.New(networkID, localStore, pushSyncProtocol, batchStore, logger, detector, pusher.DefaultRetryCount) - b.pusherCloser = pusherService - - pusherService.AddFeed(localStore.PusherFeed()) - - pullSyncProtocol := pullsync.New(p2ps, localStore, pssService.TryUnwrap, gsocService.Handle, validStamp, logger, pullsync.DefaultMaxPage) - b.pullSyncCloser = pullSyncProtocol - - retrieveProtocolSpec := retrieval.Protocol() - pushSyncProtocolSpec := pushSyncProtocol.Protocol() - pullSyncProtocolSpec := pullSyncProtocol.Protocol() - - if o.FullNodeMode && !o.BootnodeMode { - logger.Info("starting in full mode") - } else { - if chainEnabled { - logger.Info("starting in light mode") - } else { - logger.Info("starting in ultra-light mode") - } - p2p.WithBlocklistStreams(p2p.DefaultBlocklistTime, retrieveProtocolSpec) - p2p.WithBlocklistStreams(p2p.DefaultBlocklistTime, pushSyncProtocolSpec) - p2p.WithBlocklistStreams(p2p.DefaultBlocklistTime, pullSyncProtocolSpec) - } - - if err = p2ps.AddProtocol(retrieveProtocolSpec); err != nil { - return nil, fmt.Errorf("retrieval service: %w", err) - } - if err = p2ps.AddProtocol(pushSyncProtocolSpec); err != nil { - return nil, fmt.Errorf("pushsync service: %w", err) - } - if err = p2ps.AddProtocol(pullSyncProtocolSpec); err != nil { - return nil, fmt.Errorf("pullsync protocol: %w", err) - } - - go func() { - sub, unsubscribe := detector.Subscribe() - defer unsubscribe() - <-sub - logger.Info("node warmup stabilization complete, updating API status") - apiService.SetIsWarmingUp(false) - }() - - stakingContractAddress := chainCfg.StakingAddress - if o.StakingContractAddress != "" { - if !common.IsHexAddress(o.StakingContractAddress) { - return nil, errors.New("malformed staking contract address") - } - stakingContractAddress = common.HexToAddress(o.StakingContractAddress) - } - - stakingContract := staking.New(overlayEthAddress, stakingContractAddress, abiutil.MustParseABI(chainCfg.StakingABI), bzzTokenAddress, transactionService, common.BytesToHash(nonce), o.TrxDebugMode, uint8(o.ReserveCapacityDoubling)) - - if chainEnabled { - - stake, err := stakingContract.GetPotentialStake(ctx) - if err != nil { - return nil, fmt.Errorf("get potential stake: %w", err) - } - - if stake.Cmp(big.NewInt(0)) > 0 { - - if changedOverlay { - logger.Debug("changing overlay address in staking contract") - tx, err := stakingContract.ChangeStakeOverlay(ctx, common.BytesToHash(nonce)) - if err != nil { - return nil, fmt.Errorf("cannot change staking overlay address: %v", err.Error()) - } - logger.Info("overlay address changed in staking contract", "transaction", tx) - } - - // make sure that the staking contract has the up to date height - tx, updated, err := stakingContract.UpdateHeight(ctx) - if err != nil { - return nil, fmt.Errorf("update height in staking contract: %w", err) - } - if updated { - logger.Info("updated new reserve capacity doubling height in the staking contract", "transaction", tx, "new_height", o.ReserveCapacityDoubling) - } - - // Check if the staked amount is sufficient to cover the additional neighborhoods. - // The staked amount must be at least 2^h * MinimumStake. - if o.ReserveCapacityDoubling > 0 && stake.Cmp(big.NewInt(0).Mul(big.NewInt(1<> 1 - isFullySynced := func() bool { - return pullerService.SyncRate() == 0 && saludService.IsHealthy() && localStore.ReserveSize() >= reserveTreshold - } - - syncCheckTicker := time.NewTicker(2 * time.Second) - go func() { - defer syncCheckTicker.Stop() - for { - select { - case <-ctx.Done(): - return - case <-syncCheckTicker.C: - synced := isFullySynced() - logger.Debug("sync status check", "synced", synced, "reserveSize", localStore.ReserveSize(), "syncRate", pullerService.SyncRate()) - if synced { - fullSyncTime := pullSyncStartTime.Sub(t) - logger.Info("full sync done", "duration", fullSyncTime) - nodeMetrics.FullSyncDuration.Observe(fullSyncTime.Minutes()) - syncCheckTicker.Stop() - return - } - } - } - }() - } - - if o.EnableStorageIncentives { - - redistributionContractAddress := chainCfg.RedistributionAddress - if o.RedistributionContractAddress != "" { - if !common.IsHexAddress(o.RedistributionContractAddress) { - return nil, errors.New("malformed redistribution contract address") - } - redistributionContractAddress = common.HexToAddress(o.RedistributionContractAddress) - } - - redistributionContract := redistribution.New(swarmAddress, overlayEthAddress, logger, transactionService, redistributionContractAddress, abiutil.MustParseABI(chainCfg.RedistributionABI), o.TrxDebugMode) - - isFullySynced := func() bool { - reserveTreshold := reserveCapacity * 5 / 10 - logger.Debug("Sync status check evaluated", "stabilized", detector.IsStabilized()) - return localStore.ReserveSize() >= reserveTreshold && pullerService.SyncRate() == 0 && detector.IsStabilized() - } - - agent, err = storageincentives.New( - swarmAddress, - overlayEthAddress, - chainBackend, - redistributionContract, - postageStampContractService, - stakingContract, - localStore, - isFullySynced, - o.BlockTime, - storageincentives.DefaultBlocksPerRound, - storageincentives.DefaultBlocksPerPhase, - stateStore, - batchStore, - erc20Service, - transactionService, - saludService, - logger, - ) - if err != nil { - return nil, fmt.Errorf("storage incentives agent: %w", err) - } - b.storageIncetivesCloser = agent - } - - } - multiResolver := multiresolver.NewMultiResolver( - multiresolver.WithConnectionConfigs(o.ResolverConnectionCfgs), - multiresolver.WithLogger(o.Logger), - multiresolver.WithDefaultCIDResolver(), - ) - b.resolverCloser = multiResolver - - feedFactory := factory.New(localStore.Download(true)) - steward := steward.New(localStore, retrieval, localStore.Cache()) - - extraOpts := api.ExtraOptions{ - Pingpong: pingPong, - TopologyDriver: kad, - LightNodes: lightNodes, - Accounting: acc, - Pseudosettle: pseudosettleService, - Swap: swapService, - Chequebook: chequebookService, - BlockTime: o.BlockTime, - Storer: localStore, - Resolver: multiResolver, - Pss: pssService, - Gsoc: gsocService, - FeedFactory: feedFactory, - Post: post, - AccessControl: accesscontrol, - PostageContract: postageStampContractService, - Staking: stakingContract, - Steward: steward, - SyncStatus: syncStatusFn, - NodeStatus: nodeStatus, - PinIntegrity: localStore.PinIntegrity(), - } - - if o.APIAddr != "" { - // register metrics from components - apiService.MustRegisterMetrics(p2ps.Metrics()...) - apiService.MustRegisterMetrics(pingPong.Metrics()...) - apiService.MustRegisterMetrics(acc.Metrics()...) - apiService.MustRegisterMetrics(localStore.Metrics()...) - apiService.MustRegisterMetrics(kad.Metrics()...) - apiService.MustRegisterMetrics(saludService.Metrics()...) - apiService.MustRegisterMetrics(stateStoreMetrics.Metrics()...) - apiService.MustRegisterMetrics(getMetrics(nodeMetrics)...) - - if pullerService != nil { - apiService.MustRegisterMetrics(pullerService.Metrics()...) - } - - if agent != nil { - apiService.MustRegisterMetrics(agent.Metrics()...) - } - - apiService.MustRegisterMetrics(pushSyncProtocol.Metrics()...) - apiService.MustRegisterMetrics(pusherService.Metrics()...) - apiService.MustRegisterMetrics(pullSyncProtocol.Metrics()...) - apiService.MustRegisterMetrics(retrieval.Metrics()...) - apiService.MustRegisterMetrics(lightNodes.Metrics()...) - apiService.MustRegisterMetrics(hive.Metrics()...) - - if bs, ok := batchStore.(metrics.Collector); ok { - apiService.MustRegisterMetrics(bs.Metrics()...) - } - if ls, ok := eventListener.(metrics.Collector); ok { - apiService.MustRegisterMetrics(ls.Metrics()...) - } - if pssServiceMetrics, ok := pssService.(metrics.Collector); ok { - apiService.MustRegisterMetrics(pssServiceMetrics.Metrics()...) - } - if swapBackendMetrics, ok := chainBackend.(metrics.Collector); ok { - apiService.MustRegisterMetrics(swapBackendMetrics.Metrics()...) - } - - if l, ok := logger.(metrics.Collector); ok { - apiService.MustRegisterMetrics(l.Metrics()...) - } - apiService.MustRegisterMetrics(pseudosettleService.Metrics()...) - if swapService != nil { - apiService.MustRegisterMetrics(swapService.Metrics()...) - } - - apiService.Configure(signer, tracer, api.Options{ - CORSAllowedOrigins: o.CORSAllowedOrigins, - WsPingPeriod: 60 * time.Second, - }, extraOpts, chainID, erc20Service) - - apiService.EnableFullAPI() - - apiService.SetRedistributionAgent(agent) - - // api metrics are constructed on api.Service.Configure - statusMetricsRegistry.MustRegister(apiService.StatusMetrics()...) - } - - if err := kad.Start(ctx); err != nil { - return nil, fmt.Errorf("start kademlia: %w", err) - } - - if err := p2ps.Ready(); err != nil { - return nil, fmt.Errorf("p2ps ready: %w", err) - } - - return b, nil -} - func (b *Bee) SyncingStopped() chan struct{} { return b.syncingStopped.C } diff --git a/pkg/node/node_js.go b/pkg/node/node_js.go new file mode 100644 index 00000000000..107718bc1c6 --- /dev/null +++ b/pkg/node/node_js.go @@ -0,0 +1,1207 @@ +//go:build js + +package node + +import ( + "context" + "crypto/ecdsa" + "encoding/hex" + "errors" + "fmt" + stdlog "log" + "math/big" + "net/http" + "path/filepath" + "runtime" + "sync/atomic" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethersphere/bee/v2/pkg/accesscontrol" + "github.com/ethersphere/bee/v2/pkg/accounting" + "github.com/ethersphere/bee/v2/pkg/addressbook" + "github.com/ethersphere/bee/v2/pkg/api" + "github.com/ethersphere/bee/v2/pkg/config" + "github.com/ethersphere/bee/v2/pkg/crypto" + "github.com/ethersphere/bee/v2/pkg/feeds/factory" + "github.com/ethersphere/bee/v2/pkg/gsoc" + "github.com/ethersphere/bee/v2/pkg/hive" + "github.com/ethersphere/bee/v2/pkg/log" + "github.com/ethersphere/bee/v2/pkg/metrics" + "github.com/ethersphere/bee/v2/pkg/p2p" + "github.com/ethersphere/bee/v2/pkg/p2p/libp2p" + "github.com/ethersphere/bee/v2/pkg/pingpong" + "github.com/ethersphere/bee/v2/pkg/postage" + "github.com/ethersphere/bee/v2/pkg/postage/batchservice" + "github.com/ethersphere/bee/v2/pkg/postage/batchstore" + "github.com/ethersphere/bee/v2/pkg/postage/listener" + "github.com/ethersphere/bee/v2/pkg/postage/postagecontract" + "github.com/ethersphere/bee/v2/pkg/pricer" + "github.com/ethersphere/bee/v2/pkg/pricing" + "github.com/ethersphere/bee/v2/pkg/pss" + "github.com/ethersphere/bee/v2/pkg/puller" + "github.com/ethersphere/bee/v2/pkg/pullsync" + "github.com/ethersphere/bee/v2/pkg/pusher" + "github.com/ethersphere/bee/v2/pkg/pushsync" + "github.com/ethersphere/bee/v2/pkg/resolver/multiresolver" + "github.com/ethersphere/bee/v2/pkg/retrieval" + "github.com/ethersphere/bee/v2/pkg/salud" + "github.com/ethersphere/bee/v2/pkg/settlement/pseudosettle" + "github.com/ethersphere/bee/v2/pkg/settlement/swap" + "github.com/ethersphere/bee/v2/pkg/settlement/swap/chequebook" + "github.com/ethersphere/bee/v2/pkg/settlement/swap/erc20" + "github.com/ethersphere/bee/v2/pkg/settlement/swap/priceoracle" + "github.com/ethersphere/bee/v2/pkg/stabilization" + "github.com/ethersphere/bee/v2/pkg/status" + "github.com/ethersphere/bee/v2/pkg/steward" + "github.com/ethersphere/bee/v2/pkg/storageincentives" + "github.com/ethersphere/bee/v2/pkg/storageincentives/redistribution" + "github.com/ethersphere/bee/v2/pkg/storageincentives/staking" + "github.com/ethersphere/bee/v2/pkg/storer" + "github.com/ethersphere/bee/v2/pkg/swarm" + "github.com/ethersphere/bee/v2/pkg/topology/kademlia" + "github.com/ethersphere/bee/v2/pkg/topology/lightnode" + "github.com/ethersphere/bee/v2/pkg/tracing" + "github.com/ethersphere/bee/v2/pkg/transaction" + "github.com/ethersphere/bee/v2/pkg/util/abiutil" + "github.com/ethersphere/bee/v2/pkg/util/ioutil" + "github.com/ethersphere/bee/v2/pkg/util/nbhdutil" + "github.com/ethersphere/bee/v2/pkg/util/syncutil" + ma "github.com/multiformats/go-multiaddr" + "github.com/prometheus/client_golang/prometheus" + "golang.org/x/crypto/sha3" + + wasmhttp "github.com/nlepage/go-wasm-http-server/v2" +) + +func NewBee( + ctx context.Context, + addr string, + publicKey *ecdsa.PublicKey, + signer crypto.Signer, + networkID uint64, + logger log.Logger, + libp2pPrivateKey, + pssPrivateKey *ecdsa.PrivateKey, + session accesscontrol.Session, + o *Options, +) (b *Bee, err error) { + // start time for node warmup duration measurement + warmupStartTime := time.Now() + var pullSyncStartTime time.Time + + nodeMetrics := newMetrics() + + tracer, tracerCloser, err := tracing.NewTracer(&tracing.Options{ + Enabled: o.TracingEnabled, + Endpoint: o.TracingEndpoint, + ServiceName: o.TracingServiceName, + }) + if err != nil { + return nil, fmt.Errorf("tracer: %w", err) + } + + if err := validatePublicAddress(o.NATAddr); err != nil { + return nil, fmt.Errorf("invalid NAT address %s: %w", o.NATAddr, err) + } + + if err := validatePublicAddress(o.NATWSSAddr); err != nil { + return nil, fmt.Errorf("invalid NAT WSS address %s: %w", o.NATWSSAddr, err) + } + + ctx, ctxCancel := context.WithCancel(ctx) + defer func() { + // if there's been an error on this function + // we'd like to cancel the p2p context so that + // incoming connections will not be possible + if err != nil { + ctxCancel() + } + }() + + // light nodes have zero warmup time for pull/pushsync protocols + warmupTime := o.WarmupTime + if !o.FullNodeMode { + warmupTime = 0 + } + + sink := ioutil.WriterFunc(func(p []byte) (int, error) { + logger.Error(nil, string(p)) + return len(p), nil + }) + + b = &Bee{ + ctxCancel: ctxCancel, + errorLogWriter: sink, + tracerCloser: tracerCloser, + syncingStopped: syncutil.NewSignaler(), + } + + defer func(b *Bee) { + if err != nil { + logger.Error(err, "got error, shutting down...") + if err2 := b.Shutdown(); err2 != nil { + logger.Error(err2, "got error while shutting down") + } + } + }(b) + + if !o.FullNodeMode && o.ReserveCapacityDoubling != 0 { + return nil, fmt.Errorf("reserve capacity doubling is only allowed for full nodes") + } + + if o.ReserveCapacityDoubling < 0 || o.ReserveCapacityDoubling > maxAllowedDoubling { + return nil, fmt.Errorf("config reserve capacity doubling has to be between default: 0 and maximum: %d", maxAllowedDoubling) + } + shallowReceiptTolerance := maxAllowedDoubling - o.ReserveCapacityDoubling + + reserveCapacity := (1 << o.ReserveCapacityDoubling) * storer.DefaultReserveCapacity + + stateStore, stateStoreMetrics, err := InitStateStore(logger, o.DataDir, o.StatestoreCacheCapacity) + if err != nil { + return nil, fmt.Errorf("init state store: %w", err) + } + + pubKey, err := signer.PublicKey() + if err != nil { + return nil, fmt.Errorf("signer public key: %w", err) + } + + nonce, nonceExists, err := overlayNonceExists(stateStore) + if err != nil { + return nil, fmt.Errorf("check presence of nonce: %w", err) + } + + swarmAddress, err := crypto.NewOverlayAddress(*pubKey, networkID, nonce) + if err != nil { + return nil, fmt.Errorf("compute overlay address: %w", err) + } + + targetNeighborhood := o.TargetNeighborhood + if targetNeighborhood == "" && !nonceExists && o.NeighborhoodSuggester != "" { + logger.Info("fetching target neighborhood from suggester", "url", o.NeighborhoodSuggester) + targetNeighborhood, err = nbhdutil.FetchNeighborhood(&http.Client{}, o.NeighborhoodSuggester) + if err != nil { + return nil, fmt.Errorf("neighborhood suggestion: %w", err) + } + } + + var changedOverlay, resetReserve bool + if targetNeighborhood != "" { + neighborhood, err := swarm.ParseBitStrAddress(targetNeighborhood) + if err != nil { + return nil, fmt.Errorf("invalid neighborhood. %s", targetNeighborhood) + } + + if swarm.Proximity(swarmAddress.Bytes(), neighborhood.Bytes()) < uint8(len(targetNeighborhood)) { + // mine the overlay + logger.Info("mining a new overlay address to target the selected neighborhood", "target", targetNeighborhood) + newSwarmAddress, newNonce, err := nbhdutil.MineOverlay(ctx, *pubKey, networkID, targetNeighborhood) + if err != nil { + return nil, fmt.Errorf("mine overlay address: %w", err) + } + + if nonceExists { + logger.Info("Override nonce and clean state for neighborhood", "old_none", hex.EncodeToString(nonce), "new_nonce", hex.EncodeToString(newNonce)) + logger.Warning("you have another 10 seconds to change your mind and kill this process with CTRL-C...") + time.Sleep(10 * time.Second) + + err := ioutil.RemoveContent(filepath.Join(o.DataDir, ioutil.DataPathKademlia)) + if err != nil { + return nil, fmt.Errorf("delete %s: %w", ioutil.DataPathKademlia, err) + } + + if err := stateStore.ClearForHopping(); err != nil { + return nil, fmt.Errorf("clearing stateStore %w", err) + } + resetReserve = true + } + + swarmAddress = newSwarmAddress + nonce = newNonce + err = setOverlay(stateStore, swarmAddress, nonce) + if err != nil { + return nil, fmt.Errorf("statestore: save new overlay: %w", err) + } + changedOverlay = true + } + } + + b.stateStoreCloser = stateStore + // Check if the batchstore exists. If not, we can assume it's missing + // due to a migration or it's a fresh install. + batchStoreExists, err := batchStoreExists(stateStore) + if err != nil { + return nil, fmt.Errorf("batchstore: exists: %w", err) + } + + addressbook := addressbook.New(stateStore) + + logger.Info("using overlay address", "address", swarmAddress) + + // this will set overlay if it was not set before + if err = checkOverlay(stateStore, swarmAddress); err != nil { + return nil, fmt.Errorf("check overlay address: %w", err) + } + + var ( + chequebookService chequebook.Service = new(noOpChequebookService) + chequeStore chequebook.ChequeStore + cashoutService chequebook.CashoutService + erc20Service erc20.Service + ) + + chainEnabled := isChainEnabled(o, o.BlockchainRpcEndpoint, logger) + + var batchStore postage.Storer = new(postage.NoOpBatchStore) + var evictFn func([]byte) error + + if chainEnabled { + batchStore, err = batchstore.New( + stateStore, + func(id []byte) error { + return evictFn(id) + }, + reserveCapacity, + logger, + ) + if err != nil { + return nil, fmt.Errorf("batchstore: %w", err) + } + } + + chainBackend, overlayEthAddress, chainID, transactionMonitor, transactionService, err := InitChain( + ctx, + logger, + stateStore, + o.BlockchainRpcEndpoint, + o.ChainID, + signer, + o.BlockTime, + chainEnabled, + o.MinimumGasTipCap, + ) + if err != nil { + return nil, fmt.Errorf("init chain: %w", err) + } + + logger.Info("using chain with network", "chain_id", chainID, "network_id", networkID) + + b.ethClientCloser = chainBackend.Close + b.transactionCloser = tracerCloser + b.transactionMonitorCloser = transactionMonitor + + beeNodeMode := api.LightMode + if o.FullNodeMode { + beeNodeMode = api.FullMode + } else if !chainEnabled { + beeNodeMode = api.UltraLightMode + } + + // Create api.Probe in healthy state and switch to ready state after all components have been constructed + probe := api.NewProbe() + probe.SetHealthy(api.ProbeStatusOK) + defer func(probe *api.Probe) { + if err != nil { + probe.SetHealthy(api.ProbeStatusNOK) + } else { + probe.SetReady(api.ProbeStatusOK) + } + }(probe) + + stamperStore, err := InitStamperStore(logger, o.DataDir, stateStore) + if err != nil { + return nil, fmt.Errorf("failed to initialize stamper store: %w", err) + } + b.stamperStoreCloser = stamperStore + + var apiService *api.Service + + if o.APIAddr != "" { + if o.MutexProfile { + _ = runtime.SetMutexProfileFraction(1) + } + if o.BlockProfile { + runtime.SetBlockProfileRate(1) + } + + apiService = api.New( + *publicKey, + pssPrivateKey.PublicKey, + overlayEthAddress, + o.WhitelistedWithdrawalAddress, + logger, + transactionService, + batchStore, + beeNodeMode, + o.ChequebookEnable, + o.SwapEnable, + chainBackend, + o.CORSAllowedOrigins, + stamperStore, + ) + + apiService.Mount() + apiService.SetProbe(probe) + apiService.SetIsWarmingUp(true) + apiService.SetSwarmAddress(&swarmAddress) + + apiServer := &http.Server{ + IdleTimeout: 30 * time.Second, + ReadHeaderTimeout: 3 * time.Second, + Handler: apiService, + ErrorLog: stdlog.New(b.errorLogWriter, "", 0), + } + + go func() { + logger.Info("starting debug & api server") + + if _, err := wasmhttp.Serve(apiServer.Handler); err != nil && !errors.Is(err, http.ErrServerClosed) { + logger.Debug("debug & api server failed to start", "error", err) + logger.Error(nil, "debug & api server failed to start") + } + }() + + b.apiServer = apiServer + b.apiCloser = apiServer + } + + // Sync the with the given Ethereum backend: + isSynced, _, err := transaction.IsSynced(ctx, chainBackend, maxDelay) + if err != nil { + return nil, fmt.Errorf("is synced: %w", err) + } + if !isSynced { + logger.Info("waiting to sync with the blockchain backend") + + err := transaction.WaitSynced(ctx, logger, chainBackend, maxDelay) + if err != nil { + return nil, fmt.Errorf("waiting backend sync: %w", err) + } + } + + if o.SwapEnable { + chequebookFactory, err := InitChequebookFactory(logger, chainBackend, chainID, transactionService, o.SwapFactoryAddress) + if err != nil { + return nil, fmt.Errorf("init chequebook factory: %w", err) + } + + erc20Address, err := chequebookFactory.ERC20Address(ctx) + if err != nil { + return nil, fmt.Errorf("factory fail: %w", err) + } + + erc20Service = erc20.New(transactionService, erc20Address) + + if o.ChequebookEnable && chainEnabled { + chequebookService, err = InitChequebookService( + ctx, + logger, + stateStore, + signer, + chainID, + chainBackend, + overlayEthAddress, + transactionService, + chequebookFactory, + o.SwapInitialDeposit, + erc20Service, + ) + if err != nil { + return nil, fmt.Errorf("init chequebook service: %w", err) + } + } + + chequeStore, cashoutService = initChequeStoreCashout( + stateStore, + chainBackend, + chequebookFactory, + chainID, + overlayEthAddress, + transactionService, + ) + } + + lightNodes := lightnode.NewContainer(swarmAddress) + + bootnodes := make([]ma.Multiaddr, 0, len(o.Bootnodes)) + + for _, a := range o.Bootnodes { + addr, err := ma.NewMultiaddr(a) + if err != nil { + logger.Debug("create bootnode multiaddress from string failed", "string", a, "error", err) + logger.Warning("create bootnode multiaddress from string failed", "string", a) + continue + } + + bootnodes = append(bootnodes, addr) + } + + // Perform checks related to payment threshold calculations here to not duplicate + // the checks in bootstrap process + paymentThreshold, ok := new(big.Int).SetString(o.PaymentThreshold, 10) + if !ok { + return nil, fmt.Errorf("invalid payment threshold: %s", paymentThreshold) + } + + if paymentThreshold.Cmp(big.NewInt(minPaymentThreshold)) < 0 { + return nil, fmt.Errorf("payment threshold below minimum generally accepted value, need at least %d", minPaymentThreshold) + } + + if paymentThreshold.Cmp(big.NewInt(maxPaymentThreshold)) > 0 { + return nil, fmt.Errorf("payment threshold above maximum generally accepted value, needs to be reduced to at most %d", maxPaymentThreshold) + } + + if o.PaymentTolerance < 0 { + return nil, fmt.Errorf("invalid payment tolerance: %d", o.PaymentTolerance) + } + + if o.PaymentEarly > 100 || o.PaymentEarly < 0 { + return nil, fmt.Errorf("invalid payment early: %d", o.PaymentEarly) + } + + detector, err := stabilization.NewDetector(stabilization.Config{ + PeriodDuration: 2 * time.Second, + NumPeriodsForStabilization: 5, + StabilizationFactor: 3, + MinimumPeriods: 2, + WarmupTime: warmupTime, + }) + if err != nil { + return nil, fmt.Errorf("rate stabilizer configuration failed: %w", err) + } + defer detector.Close() + + detector.OnMonitoringStart = func(t time.Time) { + logger.Info("node warmup check initiated. monitoring activity rate to determine readiness.", "startTime", t) + } + + warmupMeasurement := func(t time.Time, totalCount int) { + warmupDuration := t.Sub(warmupStartTime).Seconds() + logger.Info("node warmup complete. system is considered stable and ready.", + "stabilizationTime", t, + "totalMonitoredEvents", totalCount, + "warmupDurationSeconds", warmupDuration) + + nodeMetrics.WarmupDuration.Observe(warmupDuration) + pullSyncStartTime = t + } + detector.OnStabilized = warmupMeasurement + + detector.OnPeriodComplete = func(t time.Time, periodCount int, stDev float64) { + logger.Debug("node warmup check: period complete.", "periodEndTime", t, "eventsInPeriod", periodCount, "rateStdDev", stDev) + } + + var initBatchState *postage.ChainSnapshot + // Bootstrap node with postage snapshot only if it is running on mainnet, is a fresh + // install or explicitly asked by user to resync + if networkID == mainnetNetworkID && o.UsePostageSnapshot && (!batchStoreExists || o.Resync) { + start := time.Now() + logger.Info("cold postage start detected. fetching postage stamp snapshot from swarm") + initBatchState, err = bootstrapNode( + ctx, + addr, + swarmAddress, + nonce, + addressbook, + bootnodes, + lightNodes, + stateStore, + signer, + networkID, + log.Noop, + libp2pPrivateKey, + detector, + o, + ) + logger.Info("bootstrapper created", "elapsed", time.Since(start)) + if err != nil { + logger.Error(err, "bootstrapper failed to fetch batch state") + } + } + + var registry *prometheus.Registry + + if apiService != nil { + registry = apiService.MetricsRegistry() + } + + p2ps, err := libp2p.New(ctx, signer, networkID, swarmAddress, addr, addressbook, stateStore, lightNodes, logger, tracer, libp2p.Options{ + PrivateKey: libp2pPrivateKey, + NATAddr: o.NATAddr, + NATWSSAddr: o.NATWSSAddr, + EnableWS: o.EnableWS, + EnableWSS: o.EnableWSS, + WSSAddr: o.WSSAddr, + AutoTLSStorageDir: o.AutoTLSStorageDir, + AutoTLSDomain: o.AutoTLSDomain, + AutoTLSRegistrationEndpoint: o.AutoTLSRegistrationEndpoint, + AutoTLSCAEndpoint: o.AutoTLSCAEndpoint, + WelcomeMessage: o.WelcomeMessage, + FullNode: o.FullNodeMode, + Nonce: nonce, + ValidateOverlay: chainEnabled, + Registry: registry, + }) + if err != nil { + return nil, fmt.Errorf("p2p service: %w", err) + } + + apiService.SetP2P(p2ps) + + b.p2pService = p2ps + b.p2pHalter = p2ps + + post, err := postage.NewService(logger, stamperStore, batchStore, chainID) + if err != nil { + return nil, fmt.Errorf("postage service: %w", err) + } + b.postageServiceCloser = post + batchStore.SetBatchExpiryHandler(post) + + var ( + postageStampContractService postagecontract.Interface + batchSvc postage.EventUpdater + eventListener postage.Listener + ) + + chainCfg, found := config.GetByChainID(chainID) + postageStampContractAddress, postageSyncStart := chainCfg.PostageStampAddress, chainCfg.PostageStampStartBlock + if o.PostageContractAddress != "" { + if !common.IsHexAddress(o.PostageContractAddress) { + return nil, errors.New("malformed postage stamp address") + } + postageStampContractAddress = common.HexToAddress(o.PostageContractAddress) + if o.PostageContractStartBlock == 0 { + return nil, errors.New("postage contract start block option not provided") + } + postageSyncStart = o.PostageContractStartBlock + } else if !found { + return nil, errors.New("no known postage stamp addresses for this network") + } + + postageStampContractABI := abiutil.MustParseABI(chainCfg.PostageStampABI) + + bzzTokenAddress, err := postagecontract.LookupERC20Address(ctx, transactionService, postageStampContractAddress, postageStampContractABI, chainEnabled) + if err != nil { + return nil, fmt.Errorf("lookup erc20 postage address: %w", err) + } + + postageStampContractService = postagecontract.New( + overlayEthAddress, + postageStampContractAddress, + postageStampContractABI, + bzzTokenAddress, + transactionService, + post, + batchStore, + chainEnabled, + o.TrxDebugMode, + ) + + eventListener = listener.New(b.syncingStopped, logger, chainBackend, postageStampContractAddress, postageStampContractABI, o.BlockTime, postageSyncingStallingTimeout, postageSyncingBackoffTimeout) + b.listenerCloser = eventListener + + batchSvc, err = batchservice.New(stateStore, batchStore, logger, eventListener, overlayEthAddress.Bytes(), post, sha3.New256, o.Resync) + if err != nil { + return nil, fmt.Errorf("init batch service: %w", err) + } + + // Construct protocols. + pingPong := pingpong.New(p2ps, logger, tracer) + + if err = p2ps.AddProtocol(pingPong.Protocol()); err != nil { + return nil, fmt.Errorf("pingpong service: %w", err) + } + + hive := hive.New(p2ps, addressbook, networkID, o.BootnodeMode, o.AllowPrivateCIDRs, logger) + + if err = p2ps.AddProtocol(hive.Protocol()); err != nil { + return nil, fmt.Errorf("hive service: %w", err) + } + b.hiveCloser = hive + + var swapService *swap.Service + + kad, err := kademlia.New(swarmAddress, addressbook, hive, p2ps, detector, logger, + kademlia.Options{Bootnodes: bootnodes, BootnodeMode: o.BootnodeMode, StaticNodes: o.StaticNodes, DataDir: o.DataDir}) + if err != nil { + return nil, fmt.Errorf("unable to create kademlia: %w", err) + } + b.topologyCloser = kad + b.topologyHalter = kad + hive.SetAddPeersHandler(kad.AddPeers) + p2ps.SetPickyNotifier(kad) + + var path string + + if o.DataDir != "" { + logger.Info("using datadir", "path", o.DataDir) + path = filepath.Join(o.DataDir, ioutil.DataPathLocalstore) + } + + lo := &storer.Options{ + Address: swarmAddress, + CacheCapacity: o.CacheCapacity, + LdbOpenFilesLimit: o.DBOpenFilesLimit, + LdbBlockCacheCapacity: o.DBBlockCacheCapacity, + LdbWriteBufferSize: o.DBWriteBufferSize, + LdbDisableSeeksCompaction: o.DBDisableSeeksCompaction, + Batchstore: batchStore, + StateStore: stateStore, + RadiusSetter: kad, + StartupStabilizer: detector, + Logger: logger, + Tracer: tracer, + CacheMinEvictCount: cacheMinEvictCount, + MinimumStorageRadius: o.MinimumStorageRadius, + } + + if o.FullNodeMode && !o.BootnodeMode { + // configure reserve only for full node + lo.ReserveCapacity = reserveCapacity + lo.ReserveWakeUpDuration = reserveWakeUpDuration + lo.ReserveMinEvictCount = reserveMinEvictCount + lo.RadiusSetter = kad + lo.ReserveCapacityDoubling = o.ReserveCapacityDoubling + } + + localStore, err := storer.New(ctx, path, lo) + if err != nil { + return nil, fmt.Errorf("localstore: %w", err) + } + b.localstoreCloser = localStore + evictFn = func(id []byte) error { return localStore.EvictBatch(context.Background(), id) } + + if resetReserve { + logger.Warning("resetting the reserve") + err := localStore.ResetReserve(ctx) + if err != nil { + return nil, fmt.Errorf("reset reserve: %w", err) + } + } + + actLogic := accesscontrol.NewLogic(session) + accesscontrol := accesscontrol.NewController(actLogic) + b.accesscontrolCloser = accesscontrol + + var ( + syncErr atomic.Value + syncStatus atomic.Value + + syncStatusFn = func() (isDone bool, err error) { + iErr := syncErr.Load() + if iErr != nil { + err = iErr.(error) + } + isDone = syncStatus.Load() != nil + return isDone, err + } + ) + + if !o.SkipPostageSnapshot && !batchStoreExists && (networkID == mainnetNetworkID) && beeNodeMode != api.UltraLightMode { + chainBackend := NewSnapshotLogFilterer(logger, archiveSnapshotGetter{}) + + snapshotEventListener := listener.New(b.syncingStopped, logger, chainBackend, postageStampContractAddress, postageStampContractABI, o.BlockTime, postageSyncingStallingTimeout, postageSyncingBackoffTimeout) + + snapshotBatchSvc, err := batchservice.New(stateStore, batchStore, logger, snapshotEventListener, overlayEthAddress.Bytes(), post, sha3.New256, o.Resync) + if err != nil { + logger.Error(err, "failed to initialize batch service from snapshot, continuing outside snapshot block...") + } else { + err = snapshotBatchSvc.Start(ctx, postageSyncStart, initBatchState) + syncStatus.Store(true) + if err != nil { + syncErr.Store(err) + logger.Error(err, "failed to start batch service from snapshot, continuing outside snapshot block...") + } else { + postageSyncStart = chainBackend.maxBlockHeight + } + } + if errClose := snapshotEventListener.Close(); errClose != nil { + logger.Error(errClose, "failed to close event listener (snapshot) failure") + } + + } + + if batchSvc != nil && chainEnabled { + logger.Info("waiting to sync postage contract data, this may take a while... more info available in Debug loglevel") + + paused, err := postageStampContractService.Paused(ctx) + if err != nil { + logger.Error(err, "Error checking postage contract is paused") + } + + if paused { + return nil, errors.New("postage contract is paused") + } + + if o.FullNodeMode { + err = batchSvc.Start(ctx, postageSyncStart, initBatchState) + syncStatus.Store(true) + if err != nil { + syncErr.Store(err) + return nil, fmt.Errorf("unable to start batch service: %w", err) + } + } else { + go func() { + logger.Info("started postage contract data sync in the background...") + err := batchSvc.Start(ctx, postageSyncStart, initBatchState) + syncStatus.Store(true) + if err != nil { + syncErr.Store(err) + logger.Error(err, "unable to sync batches") + b.syncingStopped.Signal() // trigger shutdown in start.go + } + }() + } + + } + + minThreshold := big.NewInt(2 * refreshRate) + maxThreshold := big.NewInt(24 * refreshRate) + + if !o.FullNodeMode { + minThreshold = big.NewInt(2 * lightRefreshRate) + } + + lightPaymentThreshold := new(big.Int).Div(paymentThreshold, big.NewInt(lightFactor)) + + pricer := pricer.NewFixedPricer(swarmAddress, basePrice) + + if paymentThreshold.Cmp(minThreshold) < 0 { + return nil, fmt.Errorf("payment threshold below minimum generally accepted value, need at least %s", minThreshold) + } + + if paymentThreshold.Cmp(maxThreshold) > 0 { + return nil, fmt.Errorf("payment threshold above maximum generally accepted value, needs to be reduced to at most %s", maxThreshold) + } + + pricing := pricing.New(p2ps, logger, paymentThreshold, lightPaymentThreshold, minThreshold) + + if err = p2ps.AddProtocol(pricing.Protocol()); err != nil { + return nil, fmt.Errorf("pricing service: %w", err) + } + + addrs, err := p2ps.Addresses() + if err != nil { + return nil, fmt.Errorf("get server addresses: %w", err) + } + + for _, addr := range addrs { + logger.Debug("p2p address", "address", addr) + } + + var enforcedRefreshRate *big.Int + + if o.FullNodeMode { + enforcedRefreshRate = big.NewInt(refreshRate) + } else { + enforcedRefreshRate = big.NewInt(lightRefreshRate) + } + + acc, err := accounting.NewAccounting( + paymentThreshold, + o.PaymentTolerance, + o.PaymentEarly, + logger, + stateStore, + pricing, + new(big.Int).Set(enforcedRefreshRate), + lightFactor, + p2ps, + ) + if err != nil { + return nil, fmt.Errorf("accounting: %w", err) + } + b.accountingCloser = acc + + pseudosettleService := pseudosettle.New(p2ps, logger, stateStore, acc, new(big.Int).Set(enforcedRefreshRate), big.NewInt(lightRefreshRate), p2ps) + if err = p2ps.AddProtocol(pseudosettleService.Protocol()); err != nil { + return nil, fmt.Errorf("pseudosettle service: %w", err) + } + + acc.SetRefreshFunc(pseudosettleService.Pay) + + if o.SwapEnable && chainEnabled { + var priceOracle priceoracle.Service + swapService, priceOracle, err = InitSwap( + p2ps, + logger, + stateStore, + networkID, + overlayEthAddress, + chequebookService, + chequeStore, + cashoutService, + acc, + o.PriceOracleAddress, + chainID, + transactionService, + ) + if err != nil { + return nil, fmt.Errorf("init swap service: %w", err) + } + b.priceOracleCloser = priceOracle + + if o.ChequebookEnable { + acc.SetPayFunc(swapService.Pay) + } + } + + pricing.SetPaymentThresholdObserver(acc) + + pssService := pss.New(pssPrivateKey, logger) + gsocService := gsoc.New(logger) + b.pssCloser = pssService + b.gsocCloser = gsocService + + validStamp := postage.ValidStamp(batchStore) + + // metrics exposed on the status protocol + statusMetricsRegistry := prometheus.NewRegistry() + if localStore != nil { + statusMetricsRegistry.MustRegister(localStore.StatusMetrics()...) + } + if p2ps != nil { + statusMetricsRegistry.MustRegister(p2ps.StatusMetrics()...) + } + + nodeStatus := status.NewService(logger, p2ps, kad, beeNodeMode.String(), batchStore, localStore, statusMetricsRegistry) + if err = p2ps.AddProtocol(nodeStatus.Protocol()); err != nil { + return nil, fmt.Errorf("status service: %w", err) + } + + saludService := salud.New(nodeStatus, kad, localStore, logger, detector, api.FullMode.String(), salud.DefaultDurPercentile, salud.DefaultConnsPercentile) + b.saludCloser = saludService + + rC, unsub := saludService.SubscribeNetworkStorageRadius() + initialRadiusC := make(chan struct{}) + var networkR atomic.Uint32 + networkR.Store(uint32(swarm.MaxBins)) + + go func() { + for { + select { + case r := <-rC: + prev := networkR.Load() + networkR.Store(uint32(r)) + if prev == uint32(swarm.MaxBins) { + close(initialRadiusC) + } + if !o.FullNodeMode { // light and ultra-light nodes do not have a reserve worker to set the radius. + kad.SetStorageRadius(r) + } + case <-ctx.Done(): + unsub() + return + } + } + }() + + waitNetworkRFunc := func() (uint8, error) { + if networkR.Load() == uint32(swarm.MaxBins) { + select { + case <-initialRadiusC: + case <-ctx.Done(): + return 0, ctx.Err() + } + } + + local, network := localStore.StorageRadius(), uint8(networkR.Load()) + if local <= uint8(o.MinimumStorageRadius) { + return max(network, uint8(o.MinimumStorageRadius)), nil + } else { + return local, nil + } + } + + pushSyncProtocol := pushsync.New(swarmAddress, networkID, nonce, p2ps, localStore, waitNetworkRFunc, kad, o.FullNodeMode && !o.BootnodeMode, pssService.TryUnwrap, gsocService.Handle, validStamp, logger, acc, pricer, signer, tracer, detector, uint8(shallowReceiptTolerance)) + b.pushSyncCloser = pushSyncProtocol + + // set the pushSyncer in the PSS + pssService.SetPushSyncer(pushSyncProtocol) + + retrieval := retrieval.New(swarmAddress, waitNetworkRFunc, localStore, p2ps, kad, logger, acc, pricer, tracer, o.RetrievalCaching) + localStore.SetRetrievalService(retrieval) + + statusMetricsRegistry.MustRegister(retrieval.StatusMetrics()...) + + pusherService := pusher.New(networkID, localStore, pushSyncProtocol, batchStore, logger, detector, pusher.DefaultRetryCount) + b.pusherCloser = pusherService + + pusherService.AddFeed(localStore.PusherFeed()) + + pullSyncProtocol := pullsync.New(p2ps, localStore, pssService.TryUnwrap, gsocService.Handle, validStamp, logger, pullsync.DefaultMaxPage) + b.pullSyncCloser = pullSyncProtocol + + retrieveProtocolSpec := retrieval.Protocol() + pushSyncProtocolSpec := pushSyncProtocol.Protocol() + pullSyncProtocolSpec := pullSyncProtocol.Protocol() + + if o.FullNodeMode && !o.BootnodeMode { + logger.Info("starting in full mode") + } else { + if chainEnabled { + logger.Info("starting in light mode") + } else { + logger.Info("starting in ultra-light mode") + } + p2p.WithBlocklistStreams(p2p.DefaultBlocklistTime, retrieveProtocolSpec) + p2p.WithBlocklistStreams(p2p.DefaultBlocklistTime, pushSyncProtocolSpec) + p2p.WithBlocklistStreams(p2p.DefaultBlocklistTime, pullSyncProtocolSpec) + } + + if err = p2ps.AddProtocol(retrieveProtocolSpec); err != nil { + return nil, fmt.Errorf("retrieval service: %w", err) + } + if err = p2ps.AddProtocol(pushSyncProtocolSpec); err != nil { + return nil, fmt.Errorf("pushsync service: %w", err) + } + if err = p2ps.AddProtocol(pullSyncProtocolSpec); err != nil { + return nil, fmt.Errorf("pullsync protocol: %w", err) + } + + go func() { + sub, unsubscribe := detector.Subscribe() + defer unsubscribe() + <-sub + logger.Info("node warmup stabilization complete, updating API status") + apiService.SetIsWarmingUp(false) + }() + + stakingContractAddress := chainCfg.StakingAddress + if o.StakingContractAddress != "" { + if !common.IsHexAddress(o.StakingContractAddress) { + return nil, errors.New("malformed staking contract address") + } + stakingContractAddress = common.HexToAddress(o.StakingContractAddress) + } + + stakingContract := staking.New(overlayEthAddress, stakingContractAddress, abiutil.MustParseABI(chainCfg.StakingABI), bzzTokenAddress, transactionService, common.BytesToHash(nonce), o.TrxDebugMode, uint8(o.ReserveCapacityDoubling)) + + if chainEnabled { + + stake, err := stakingContract.GetPotentialStake(ctx) + if err != nil { + return nil, fmt.Errorf("get potential stake: %w", err) + } + + if stake.Cmp(big.NewInt(0)) > 0 { + + if changedOverlay { + logger.Debug("changing overlay address in staking contract") + tx, err := stakingContract.ChangeStakeOverlay(ctx, common.BytesToHash(nonce)) + if err != nil { + return nil, fmt.Errorf("cannot change staking overlay address: %v", err.Error()) + } + logger.Info("overlay address changed in staking contract", "transaction", tx) + } + + // make sure that the staking contract has the up to date height + tx, updated, err := stakingContract.UpdateHeight(ctx) + if err != nil { + return nil, fmt.Errorf("update height in staking contract: %w", err) + } + if updated { + logger.Info("updated new reserve capacity doubling height in the staking contract", "transaction", tx, "new_height", o.ReserveCapacityDoubling) + } + + // Check if the staked amount is sufficient to cover the additional neighborhoods. + // The staked amount must be at least 2^h * MinimumStake. + if o.ReserveCapacityDoubling > 0 && stake.Cmp(big.NewInt(0).Mul(big.NewInt(1<> 1 + isFullySynced := func() bool { + return pullerService.SyncRate() == 0 && saludService.IsHealthy() && localStore.ReserveSize() >= reserveTreshold + } + + syncCheckTicker := time.NewTicker(2 * time.Second) + go func() { + defer syncCheckTicker.Stop() + for { + select { + case <-ctx.Done(): + return + case <-syncCheckTicker.C: + synced := isFullySynced() + logger.Debug("sync status check", "synced", synced, "reserveSize", localStore.ReserveSize(), "syncRate", pullerService.SyncRate()) + if synced { + fullSyncTime := pullSyncStartTime.Sub(t) + logger.Info("full sync done", "duration", fullSyncTime) + nodeMetrics.FullSyncDuration.Observe(fullSyncTime.Minutes()) + syncCheckTicker.Stop() + return + } + } + } + }() + } + + if o.EnableStorageIncentives { + + redistributionContractAddress := chainCfg.RedistributionAddress + if o.RedistributionContractAddress != "" { + if !common.IsHexAddress(o.RedistributionContractAddress) { + return nil, errors.New("malformed redistribution contract address") + } + redistributionContractAddress = common.HexToAddress(o.RedistributionContractAddress) + } + + redistributionContract := redistribution.New(swarmAddress, overlayEthAddress, logger, transactionService, redistributionContractAddress, abiutil.MustParseABI(chainCfg.RedistributionABI), o.TrxDebugMode) + + isFullySynced := func() bool { + reserveTreshold := reserveCapacity * 5 / 10 + logger.Debug("Sync status check evaluated", "stabilized", detector.IsStabilized()) + return localStore.ReserveSize() >= reserveTreshold && pullerService.SyncRate() == 0 && detector.IsStabilized() + } + + agent, err = storageincentives.New( + swarmAddress, + overlayEthAddress, + chainBackend, + redistributionContract, + postageStampContractService, + stakingContract, + localStore, + isFullySynced, + o.BlockTime, + storageincentives.DefaultBlocksPerRound, + storageincentives.DefaultBlocksPerPhase, + stateStore, + batchStore, + erc20Service, + transactionService, + saludService, + logger, + ) + if err != nil { + return nil, fmt.Errorf("storage incentives agent: %w", err) + } + b.storageIncetivesCloser = agent + } + + } + multiResolver := multiresolver.NewMultiResolver( + multiresolver.WithConnectionConfigs(o.ResolverConnectionCfgs), + multiresolver.WithLogger(o.Logger), + multiresolver.WithDefaultCIDResolver(), + ) + b.resolverCloser = multiResolver + + feedFactory := factory.New(localStore.Download(true)) + steward := steward.New(localStore, retrieval, localStore.Cache()) + + extraOpts := api.ExtraOptions{ + Pingpong: pingPong, + TopologyDriver: kad, + LightNodes: lightNodes, + Accounting: acc, + Pseudosettle: pseudosettleService, + Swap: swapService, + Chequebook: chequebookService, + BlockTime: o.BlockTime, + Storer: localStore, + Resolver: multiResolver, + Pss: pssService, + Gsoc: gsocService, + FeedFactory: feedFactory, + Post: post, + AccessControl: accesscontrol, + PostageContract: postageStampContractService, + Staking: stakingContract, + Steward: steward, + SyncStatus: syncStatusFn, + NodeStatus: nodeStatus, + PinIntegrity: localStore.PinIntegrity(), + } + + if o.APIAddr != "" { + // register metrics from components + apiService.MustRegisterMetrics(p2ps.Metrics()...) + apiService.MustRegisterMetrics(pingPong.Metrics()...) + apiService.MustRegisterMetrics(acc.Metrics()...) + apiService.MustRegisterMetrics(localStore.Metrics()...) + apiService.MustRegisterMetrics(kad.Metrics()...) + apiService.MustRegisterMetrics(saludService.Metrics()...) + apiService.MustRegisterMetrics(stateStoreMetrics.Metrics()...) + apiService.MustRegisterMetrics(getMetrics(nodeMetrics)...) + + if pullerService != nil { + apiService.MustRegisterMetrics(pullerService.Metrics()...) + } + + if agent != nil { + apiService.MustRegisterMetrics(agent.Metrics()...) + } + + apiService.MustRegisterMetrics(pushSyncProtocol.Metrics()...) + apiService.MustRegisterMetrics(pusherService.Metrics()...) + apiService.MustRegisterMetrics(pullSyncProtocol.Metrics()...) + apiService.MustRegisterMetrics(retrieval.Metrics()...) + apiService.MustRegisterMetrics(lightNodes.Metrics()...) + apiService.MustRegisterMetrics(hive.Metrics()...) + + if bs, ok := batchStore.(metrics.Collector); ok { + apiService.MustRegisterMetrics(bs.Metrics()...) + } + if ls, ok := eventListener.(metrics.Collector); ok { + apiService.MustRegisterMetrics(ls.Metrics()...) + } + if pssServiceMetrics, ok := pssService.(metrics.Collector); ok { + apiService.MustRegisterMetrics(pssServiceMetrics.Metrics()...) + } + if swapBackendMetrics, ok := chainBackend.(metrics.Collector); ok { + apiService.MustRegisterMetrics(swapBackendMetrics.Metrics()...) + } + + if l, ok := logger.(metrics.Collector); ok { + apiService.MustRegisterMetrics(l.Metrics()...) + } + apiService.MustRegisterMetrics(pseudosettleService.Metrics()...) + if swapService != nil { + apiService.MustRegisterMetrics(swapService.Metrics()...) + } + + apiService.Configure(signer, tracer, api.Options{ + CORSAllowedOrigins: o.CORSAllowedOrigins, + WsPingPeriod: 60 * time.Second, + }, extraOpts, chainID, erc20Service) + + apiService.EnableFullAPI() + + apiService.SetRedistributionAgent(agent) + + // api metrics are constructed on api.Service.Configure + statusMetricsRegistry.MustRegister(apiService.StatusMetrics()...) + } + + if err := kad.Start(ctx); err != nil { + return nil, fmt.Errorf("start kademlia: %w", err) + } + + if err := p2ps.Ready(); err != nil { + return nil, fmt.Errorf("p2ps ready: %w", err) + } + + return b, nil +} diff --git a/pkg/node/node_native.go b/pkg/node/node_native.go new file mode 100644 index 00000000000..498ef41df0d --- /dev/null +++ b/pkg/node/node_native.go @@ -0,0 +1,1211 @@ +//go:build !js + +package node + +import ( + "context" + "crypto/ecdsa" + "encoding/hex" + "errors" + "fmt" + stdlog "log" + "math/big" + "net" + "net/http" + "path/filepath" + "runtime" + "sync/atomic" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethersphere/bee/v2/pkg/accesscontrol" + "github.com/ethersphere/bee/v2/pkg/accounting" + "github.com/ethersphere/bee/v2/pkg/addressbook" + "github.com/ethersphere/bee/v2/pkg/api" + "github.com/ethersphere/bee/v2/pkg/config" + "github.com/ethersphere/bee/v2/pkg/crypto" + "github.com/ethersphere/bee/v2/pkg/feeds/factory" + "github.com/ethersphere/bee/v2/pkg/gsoc" + "github.com/ethersphere/bee/v2/pkg/hive" + "github.com/ethersphere/bee/v2/pkg/log" + "github.com/ethersphere/bee/v2/pkg/metrics" + "github.com/ethersphere/bee/v2/pkg/p2p" + "github.com/ethersphere/bee/v2/pkg/p2p/libp2p" + "github.com/ethersphere/bee/v2/pkg/pingpong" + "github.com/ethersphere/bee/v2/pkg/postage" + "github.com/ethersphere/bee/v2/pkg/postage/batchservice" + "github.com/ethersphere/bee/v2/pkg/postage/batchstore" + "github.com/ethersphere/bee/v2/pkg/postage/listener" + "github.com/ethersphere/bee/v2/pkg/postage/postagecontract" + "github.com/ethersphere/bee/v2/pkg/pricer" + "github.com/ethersphere/bee/v2/pkg/pricing" + "github.com/ethersphere/bee/v2/pkg/pss" + "github.com/ethersphere/bee/v2/pkg/puller" + "github.com/ethersphere/bee/v2/pkg/pullsync" + "github.com/ethersphere/bee/v2/pkg/pusher" + "github.com/ethersphere/bee/v2/pkg/pushsync" + "github.com/ethersphere/bee/v2/pkg/resolver/multiresolver" + "github.com/ethersphere/bee/v2/pkg/retrieval" + "github.com/ethersphere/bee/v2/pkg/salud" + "github.com/ethersphere/bee/v2/pkg/settlement/pseudosettle" + "github.com/ethersphere/bee/v2/pkg/settlement/swap" + "github.com/ethersphere/bee/v2/pkg/settlement/swap/chequebook" + "github.com/ethersphere/bee/v2/pkg/settlement/swap/erc20" + "github.com/ethersphere/bee/v2/pkg/settlement/swap/priceoracle" + "github.com/ethersphere/bee/v2/pkg/stabilization" + "github.com/ethersphere/bee/v2/pkg/status" + "github.com/ethersphere/bee/v2/pkg/steward" + "github.com/ethersphere/bee/v2/pkg/storageincentives" + "github.com/ethersphere/bee/v2/pkg/storageincentives/redistribution" + "github.com/ethersphere/bee/v2/pkg/storageincentives/staking" + "github.com/ethersphere/bee/v2/pkg/storer" + "github.com/ethersphere/bee/v2/pkg/swarm" + "github.com/ethersphere/bee/v2/pkg/topology/kademlia" + "github.com/ethersphere/bee/v2/pkg/topology/lightnode" + "github.com/ethersphere/bee/v2/pkg/tracing" + "github.com/ethersphere/bee/v2/pkg/transaction" + "github.com/ethersphere/bee/v2/pkg/util/abiutil" + "github.com/ethersphere/bee/v2/pkg/util/ioutil" + "github.com/ethersphere/bee/v2/pkg/util/nbhdutil" + "github.com/ethersphere/bee/v2/pkg/util/syncutil" + ma "github.com/multiformats/go-multiaddr" + "github.com/prometheus/client_golang/prometheus" + "golang.org/x/crypto/sha3" +) + +func NewBee( + ctx context.Context, + addr string, + publicKey *ecdsa.PublicKey, + signer crypto.Signer, + networkID uint64, + logger log.Logger, + libp2pPrivateKey, + pssPrivateKey *ecdsa.PrivateKey, + session accesscontrol.Session, + o *Options, +) (b *Bee, err error) { + // start time for node warmup duration measurement + warmupStartTime := time.Now() + var pullSyncStartTime time.Time + + nodeMetrics := newMetrics() + + tracer, tracerCloser, err := tracing.NewTracer(&tracing.Options{ + Enabled: o.TracingEnabled, + Endpoint: o.TracingEndpoint, + ServiceName: o.TracingServiceName, + }) + if err != nil { + return nil, fmt.Errorf("tracer: %w", err) + } + + if err := validatePublicAddress(o.NATAddr); err != nil { + return nil, fmt.Errorf("invalid NAT address %s: %w", o.NATAddr, err) + } + + if err := validatePublicAddress(o.NATWSSAddr); err != nil { + return nil, fmt.Errorf("invalid NAT WSS address %s: %w", o.NATWSSAddr, err) + } + + ctx, ctxCancel := context.WithCancel(ctx) + defer func() { + // if there's been an error on this function + // we'd like to cancel the p2p context so that + // incoming connections will not be possible + if err != nil { + ctxCancel() + } + }() + + // light nodes have zero warmup time for pull/pushsync protocols + warmupTime := o.WarmupTime + if !o.FullNodeMode { + warmupTime = 0 + } + + sink := ioutil.WriterFunc(func(p []byte) (int, error) { + logger.Error(nil, string(p)) + return len(p), nil + }) + + b = &Bee{ + ctxCancel: ctxCancel, + errorLogWriter: sink, + tracerCloser: tracerCloser, + syncingStopped: syncutil.NewSignaler(), + } + + defer func(b *Bee) { + if err != nil { + logger.Error(err, "got error, shutting down...") + if err2 := b.Shutdown(); err2 != nil { + logger.Error(err2, "got error while shutting down") + } + } + }(b) + + if !o.FullNodeMode && o.ReserveCapacityDoubling != 0 { + return nil, fmt.Errorf("reserve capacity doubling is only allowed for full nodes") + } + + if o.ReserveCapacityDoubling < 0 || o.ReserveCapacityDoubling > maxAllowedDoubling { + return nil, fmt.Errorf("config reserve capacity doubling has to be between default: 0 and maximum: %d", maxAllowedDoubling) + } + shallowReceiptTolerance := maxAllowedDoubling - o.ReserveCapacityDoubling + + reserveCapacity := (1 << o.ReserveCapacityDoubling) * storer.DefaultReserveCapacity + + stateStore, stateStoreMetrics, err := InitStateStore(logger, o.DataDir, o.StatestoreCacheCapacity) + if err != nil { + return nil, fmt.Errorf("init state store: %w", err) + } + + pubKey, err := signer.PublicKey() + if err != nil { + return nil, fmt.Errorf("signer public key: %w", err) + } + + nonce, nonceExists, err := overlayNonceExists(stateStore) + if err != nil { + return nil, fmt.Errorf("check presence of nonce: %w", err) + } + + swarmAddress, err := crypto.NewOverlayAddress(*pubKey, networkID, nonce) + if err != nil { + return nil, fmt.Errorf("compute overlay address: %w", err) + } + + targetNeighborhood := o.TargetNeighborhood + if targetNeighborhood == "" && !nonceExists && o.NeighborhoodSuggester != "" { + logger.Info("fetching target neighborhood from suggester", "url", o.NeighborhoodSuggester) + targetNeighborhood, err = nbhdutil.FetchNeighborhood(&http.Client{}, o.NeighborhoodSuggester) + if err != nil { + return nil, fmt.Errorf("neighborhood suggestion: %w", err) + } + } + + var changedOverlay, resetReserve bool + if targetNeighborhood != "" { + neighborhood, err := swarm.ParseBitStrAddress(targetNeighborhood) + if err != nil { + return nil, fmt.Errorf("invalid neighborhood. %s", targetNeighborhood) + } + + if swarm.Proximity(swarmAddress.Bytes(), neighborhood.Bytes()) < uint8(len(targetNeighborhood)) { + // mine the overlay + logger.Info("mining a new overlay address to target the selected neighborhood", "target", targetNeighborhood) + newSwarmAddress, newNonce, err := nbhdutil.MineOverlay(ctx, *pubKey, networkID, targetNeighborhood) + if err != nil { + return nil, fmt.Errorf("mine overlay address: %w", err) + } + + if nonceExists { + logger.Info("Override nonce and clean state for neighborhood", "old_none", hex.EncodeToString(nonce), "new_nonce", hex.EncodeToString(newNonce)) + logger.Warning("you have another 10 seconds to change your mind and kill this process with CTRL-C...") + time.Sleep(10 * time.Second) + + err := ioutil.RemoveContent(filepath.Join(o.DataDir, ioutil.DataPathKademlia)) + if err != nil { + return nil, fmt.Errorf("delete %s: %w", ioutil.DataPathKademlia, err) + } + + if err := stateStore.ClearForHopping(); err != nil { + return nil, fmt.Errorf("clearing stateStore %w", err) + } + resetReserve = true + } + + swarmAddress = newSwarmAddress + nonce = newNonce + err = setOverlay(stateStore, swarmAddress, nonce) + if err != nil { + return nil, fmt.Errorf("statestore: save new overlay: %w", err) + } + changedOverlay = true + } + } + + b.stateStoreCloser = stateStore + // Check if the batchstore exists. If not, we can assume it's missing + // due to a migration or it's a fresh install. + batchStoreExists, err := batchStoreExists(stateStore) + if err != nil { + return nil, fmt.Errorf("batchstore: exists: %w", err) + } + + addressbook := addressbook.New(stateStore) + + logger.Info("using overlay address", "address", swarmAddress) + + // this will set overlay if it was not set before + if err = checkOverlay(stateStore, swarmAddress); err != nil { + return nil, fmt.Errorf("check overlay address: %w", err) + } + + var ( + chequebookService chequebook.Service = new(noOpChequebookService) + chequeStore chequebook.ChequeStore + cashoutService chequebook.CashoutService + erc20Service erc20.Service + ) + + chainEnabled := isChainEnabled(o, o.BlockchainRpcEndpoint, logger) + + var batchStore postage.Storer = new(postage.NoOpBatchStore) + var evictFn func([]byte) error + + if chainEnabled { + batchStore, err = batchstore.New( + stateStore, + func(id []byte) error { + return evictFn(id) + }, + reserveCapacity, + logger, + ) + if err != nil { + return nil, fmt.Errorf("batchstore: %w", err) + } + } + + chainBackend, overlayEthAddress, chainID, transactionMonitor, transactionService, err := InitChain( + ctx, + logger, + stateStore, + o.BlockchainRpcEndpoint, + o.ChainID, + signer, + o.BlockTime, + chainEnabled, + o.MinimumGasTipCap, + ) + if err != nil { + return nil, fmt.Errorf("init chain: %w", err) + } + + logger.Info("using chain with network", "chain_id", chainID, "network_id", networkID) + + b.ethClientCloser = chainBackend.Close + b.transactionCloser = tracerCloser + b.transactionMonitorCloser = transactionMonitor + + beeNodeMode := api.LightMode + if o.FullNodeMode { + beeNodeMode = api.FullMode + } else if !chainEnabled { + beeNodeMode = api.UltraLightMode + } + + // Create api.Probe in healthy state and switch to ready state after all components have been constructed + probe := api.NewProbe() + probe.SetHealthy(api.ProbeStatusOK) + defer func(probe *api.Probe) { + if err != nil { + probe.SetHealthy(api.ProbeStatusNOK) + } else { + probe.SetReady(api.ProbeStatusOK) + } + }(probe) + + stamperStore, err := InitStamperStore(logger, o.DataDir, stateStore) + if err != nil { + return nil, fmt.Errorf("failed to initialize stamper store: %w", err) + } + b.stamperStoreCloser = stamperStore + + var apiService *api.Service + + if o.APIAddr != "" { + if o.MutexProfile { + _ = runtime.SetMutexProfileFraction(1) + } + if o.BlockProfile { + runtime.SetBlockProfileRate(1) + } + + apiListener, err := (&net.ListenConfig{}).Listen(ctx, "tcp", o.APIAddr) + if err != nil { + return nil, fmt.Errorf("api listener: %w", err) + } + + apiService = api.New( + *publicKey, + pssPrivateKey.PublicKey, + overlayEthAddress, + o.WhitelistedWithdrawalAddress, + logger, + transactionService, + batchStore, + beeNodeMode, + o.ChequebookEnable, + o.SwapEnable, + chainBackend, + o.CORSAllowedOrigins, + stamperStore, + ) + + apiService.Mount() + apiService.SetProbe(probe) + apiService.SetIsWarmingUp(true) + apiService.SetSwarmAddress(&swarmAddress) + + apiServer := &http.Server{ + IdleTimeout: 30 * time.Second, + ReadHeaderTimeout: 3 * time.Second, + Handler: apiService, + ErrorLog: stdlog.New(b.errorLogWriter, "", 0), + } + + go func() { + logger.Info("starting debug & api server", "address", apiListener.Addr()) + + if err := apiServer.Serve(apiListener); err != nil && !errors.Is(err, http.ErrServerClosed) { + logger.Debug("debug & api server failed to start", "error", err) + logger.Error(nil, "debug & api server failed to start") + } + }() + + b.apiServer = apiServer + b.apiCloser = apiServer + } + + // Sync the with the given Ethereum backend: + isSynced, _, err := transaction.IsSynced(ctx, chainBackend, maxDelay) + if err != nil { + return nil, fmt.Errorf("is synced: %w", err) + } + if !isSynced { + logger.Info("waiting to sync with the blockchain backend") + + err := transaction.WaitSynced(ctx, logger, chainBackend, maxDelay) + if err != nil { + return nil, fmt.Errorf("waiting backend sync: %w", err) + } + } + + if o.SwapEnable { + chequebookFactory, err := InitChequebookFactory(logger, chainBackend, chainID, transactionService, o.SwapFactoryAddress) + if err != nil { + return nil, fmt.Errorf("init chequebook factory: %w", err) + } + + erc20Address, err := chequebookFactory.ERC20Address(ctx) + if err != nil { + return nil, fmt.Errorf("factory fail: %w", err) + } + + erc20Service = erc20.New(transactionService, erc20Address) + + if o.ChequebookEnable && chainEnabled { + chequebookService, err = InitChequebookService( + ctx, + logger, + stateStore, + signer, + chainID, + chainBackend, + overlayEthAddress, + transactionService, + chequebookFactory, + o.SwapInitialDeposit, + erc20Service, + ) + if err != nil { + return nil, fmt.Errorf("init chequebook service: %w", err) + } + } + + chequeStore, cashoutService = initChequeStoreCashout( + stateStore, + chainBackend, + chequebookFactory, + chainID, + overlayEthAddress, + transactionService, + ) + } + + lightNodes := lightnode.NewContainer(swarmAddress) + + bootnodes := make([]ma.Multiaddr, 0, len(o.Bootnodes)) + + for _, a := range o.Bootnodes { + addr, err := ma.NewMultiaddr(a) + if err != nil { + logger.Debug("create bootnode multiaddress from string failed", "string", a, "error", err) + logger.Warning("create bootnode multiaddress from string failed", "string", a) + continue + } + + bootnodes = append(bootnodes, addr) + } + + // Perform checks related to payment threshold calculations here to not duplicate + // the checks in bootstrap process + paymentThreshold, ok := new(big.Int).SetString(o.PaymentThreshold, 10) + if !ok { + return nil, fmt.Errorf("invalid payment threshold: %s", paymentThreshold) + } + + if paymentThreshold.Cmp(big.NewInt(minPaymentThreshold)) < 0 { + return nil, fmt.Errorf("payment threshold below minimum generally accepted value, need at least %d", minPaymentThreshold) + } + + if paymentThreshold.Cmp(big.NewInt(maxPaymentThreshold)) > 0 { + return nil, fmt.Errorf("payment threshold above maximum generally accepted value, needs to be reduced to at most %d", maxPaymentThreshold) + } + + if o.PaymentTolerance < 0 { + return nil, fmt.Errorf("invalid payment tolerance: %d", o.PaymentTolerance) + } + + if o.PaymentEarly > 100 || o.PaymentEarly < 0 { + return nil, fmt.Errorf("invalid payment early: %d", o.PaymentEarly) + } + + detector, err := stabilization.NewDetector(stabilization.Config{ + PeriodDuration: 2 * time.Second, + NumPeriodsForStabilization: 5, + StabilizationFactor: 3, + MinimumPeriods: 2, + WarmupTime: warmupTime, + }) + if err != nil { + return nil, fmt.Errorf("rate stabilizer configuration failed: %w", err) + } + defer detector.Close() + + detector.OnMonitoringStart = func(t time.Time) { + logger.Info("node warmup check initiated. monitoring activity rate to determine readiness.", "startTime", t) + } + + warmupMeasurement := func(t time.Time, totalCount int) { + warmupDuration := t.Sub(warmupStartTime).Seconds() + logger.Info("node warmup complete. system is considered stable and ready.", + "stabilizationTime", t, + "totalMonitoredEvents", totalCount, + "warmupDurationSeconds", warmupDuration) + + nodeMetrics.WarmupDuration.Observe(warmupDuration) + pullSyncStartTime = t + } + detector.OnStabilized = warmupMeasurement + + detector.OnPeriodComplete = func(t time.Time, periodCount int, stDev float64) { + logger.Debug("node warmup check: period complete.", "periodEndTime", t, "eventsInPeriod", periodCount, "rateStdDev", stDev) + } + + var initBatchState *postage.ChainSnapshot + // Bootstrap node with postage snapshot only if it is running on mainnet, is a fresh + // install or explicitly asked by user to resync + if networkID == mainnetNetworkID && o.UsePostageSnapshot && (!batchStoreExists || o.Resync) { + start := time.Now() + logger.Info("cold postage start detected. fetching postage stamp snapshot from swarm") + initBatchState, err = bootstrapNode( + ctx, + addr, + swarmAddress, + nonce, + addressbook, + bootnodes, + lightNodes, + stateStore, + signer, + networkID, + log.Noop, + libp2pPrivateKey, + detector, + o, + ) + logger.Info("bootstrapper created", "elapsed", time.Since(start)) + if err != nil { + logger.Error(err, "bootstrapper failed to fetch batch state") + } + } + + var registry *prometheus.Registry + + if apiService != nil { + registry = apiService.MetricsRegistry() + } + + p2ps, err := libp2p.New(ctx, signer, networkID, swarmAddress, addr, addressbook, stateStore, lightNodes, logger, tracer, libp2p.Options{ + PrivateKey: libp2pPrivateKey, + NATAddr: o.NATAddr, + NATWSSAddr: o.NATWSSAddr, + EnableWS: o.EnableWS, + EnableWSS: o.EnableWSS, + WSSAddr: o.WSSAddr, + AutoTLSStorageDir: o.AutoTLSStorageDir, + AutoTLSDomain: o.AutoTLSDomain, + AutoTLSRegistrationEndpoint: o.AutoTLSRegistrationEndpoint, + AutoTLSCAEndpoint: o.AutoTLSCAEndpoint, + WelcomeMessage: o.WelcomeMessage, + FullNode: o.FullNodeMode, + Nonce: nonce, + ValidateOverlay: chainEnabled, + Registry: registry, + }) + if err != nil { + return nil, fmt.Errorf("p2p service: %w", err) + } + + apiService.SetP2P(p2ps) + + b.p2pService = p2ps + b.p2pHalter = p2ps + + post, err := postage.NewService(logger, stamperStore, batchStore, chainID) + if err != nil { + return nil, fmt.Errorf("postage service: %w", err) + } + b.postageServiceCloser = post + batchStore.SetBatchExpiryHandler(post) + + var ( + postageStampContractService postagecontract.Interface + batchSvc postage.EventUpdater + eventListener postage.Listener + ) + + chainCfg, found := config.GetByChainID(chainID) + postageStampContractAddress, postageSyncStart := chainCfg.PostageStampAddress, chainCfg.PostageStampStartBlock + if o.PostageContractAddress != "" { + if !common.IsHexAddress(o.PostageContractAddress) { + return nil, errors.New("malformed postage stamp address") + } + postageStampContractAddress = common.HexToAddress(o.PostageContractAddress) + if o.PostageContractStartBlock == 0 { + return nil, errors.New("postage contract start block option not provided") + } + postageSyncStart = o.PostageContractStartBlock + } else if !found { + return nil, errors.New("no known postage stamp addresses for this network") + } + + postageStampContractABI := abiutil.MustParseABI(chainCfg.PostageStampABI) + + bzzTokenAddress, err := postagecontract.LookupERC20Address(ctx, transactionService, postageStampContractAddress, postageStampContractABI, chainEnabled) + if err != nil { + return nil, fmt.Errorf("lookup erc20 postage address: %w", err) + } + + postageStampContractService = postagecontract.New( + overlayEthAddress, + postageStampContractAddress, + postageStampContractABI, + bzzTokenAddress, + transactionService, + post, + batchStore, + chainEnabled, + o.TrxDebugMode, + ) + + eventListener = listener.New(b.syncingStopped, logger, chainBackend, postageStampContractAddress, postageStampContractABI, o.BlockTime, postageSyncingStallingTimeout, postageSyncingBackoffTimeout) + b.listenerCloser = eventListener + + batchSvc, err = batchservice.New(stateStore, batchStore, logger, eventListener, overlayEthAddress.Bytes(), post, sha3.New256, o.Resync) + if err != nil { + return nil, fmt.Errorf("init batch service: %w", err) + } + + // Construct protocols. + pingPong := pingpong.New(p2ps, logger, tracer) + + if err = p2ps.AddProtocol(pingPong.Protocol()); err != nil { + return nil, fmt.Errorf("pingpong service: %w", err) + } + + hive := hive.New(p2ps, addressbook, networkID, o.BootnodeMode, o.AllowPrivateCIDRs, logger) + + if err = p2ps.AddProtocol(hive.Protocol()); err != nil { + return nil, fmt.Errorf("hive service: %w", err) + } + b.hiveCloser = hive + + var swapService *swap.Service + + kad, err := kademlia.New(swarmAddress, addressbook, hive, p2ps, detector, logger, + kademlia.Options{Bootnodes: bootnodes, BootnodeMode: o.BootnodeMode, StaticNodes: o.StaticNodes, DataDir: o.DataDir}) + if err != nil { + return nil, fmt.Errorf("unable to create kademlia: %w", err) + } + b.topologyCloser = kad + b.topologyHalter = kad + hive.SetAddPeersHandler(kad.AddPeers) + p2ps.SetPickyNotifier(kad) + + var path string + + if o.DataDir != "" { + logger.Info("using datadir", "path", o.DataDir) + path = filepath.Join(o.DataDir, ioutil.DataPathLocalstore) + } + + lo := &storer.Options{ + Address: swarmAddress, + CacheCapacity: o.CacheCapacity, + LdbOpenFilesLimit: o.DBOpenFilesLimit, + LdbBlockCacheCapacity: o.DBBlockCacheCapacity, + LdbWriteBufferSize: o.DBWriteBufferSize, + LdbDisableSeeksCompaction: o.DBDisableSeeksCompaction, + Batchstore: batchStore, + StateStore: stateStore, + RadiusSetter: kad, + StartupStabilizer: detector, + Logger: logger, + Tracer: tracer, + CacheMinEvictCount: cacheMinEvictCount, + MinimumStorageRadius: o.MinimumStorageRadius, + } + + if o.FullNodeMode && !o.BootnodeMode { + // configure reserve only for full node + lo.ReserveCapacity = reserveCapacity + lo.ReserveWakeUpDuration = reserveWakeUpDuration + lo.ReserveMinEvictCount = reserveMinEvictCount + lo.RadiusSetter = kad + lo.ReserveCapacityDoubling = o.ReserveCapacityDoubling + } + + localStore, err := storer.New(ctx, path, lo) + if err != nil { + return nil, fmt.Errorf("localstore: %w", err) + } + b.localstoreCloser = localStore + evictFn = func(id []byte) error { return localStore.EvictBatch(context.Background(), id) } + + if resetReserve { + logger.Warning("resetting the reserve") + err := localStore.ResetReserve(ctx) + if err != nil { + return nil, fmt.Errorf("reset reserve: %w", err) + } + } + + actLogic := accesscontrol.NewLogic(session) + accesscontrol := accesscontrol.NewController(actLogic) + b.accesscontrolCloser = accesscontrol + + var ( + syncErr atomic.Value + syncStatus atomic.Value + + syncStatusFn = func() (isDone bool, err error) { + iErr := syncErr.Load() + if iErr != nil { + err = iErr.(error) + } + isDone = syncStatus.Load() != nil + return isDone, err + } + ) + + if !o.SkipPostageSnapshot && !batchStoreExists && (networkID == mainnetNetworkID) && beeNodeMode != api.UltraLightMode { + chainBackend := NewSnapshotLogFilterer(logger, archiveSnapshotGetter{}) + + snapshotEventListener := listener.New(b.syncingStopped, logger, chainBackend, postageStampContractAddress, postageStampContractABI, o.BlockTime, postageSyncingStallingTimeout, postageSyncingBackoffTimeout) + + snapshotBatchSvc, err := batchservice.New(stateStore, batchStore, logger, snapshotEventListener, overlayEthAddress.Bytes(), post, sha3.New256, o.Resync) + if err != nil { + logger.Error(err, "failed to initialize batch service from snapshot, continuing outside snapshot block...") + } else { + err = snapshotBatchSvc.Start(ctx, postageSyncStart, initBatchState) + syncStatus.Store(true) + if err != nil { + syncErr.Store(err) + logger.Error(err, "failed to start batch service from snapshot, continuing outside snapshot block...") + } else { + postageSyncStart = chainBackend.maxBlockHeight + } + } + if errClose := snapshotEventListener.Close(); errClose != nil { + logger.Error(errClose, "failed to close event listener (snapshot) failure") + } + + } + + if batchSvc != nil && chainEnabled { + logger.Info("waiting to sync postage contract data, this may take a while... more info available in Debug loglevel") + + paused, err := postageStampContractService.Paused(ctx) + if err != nil { + logger.Error(err, "Error checking postage contract is paused") + } + + if paused { + return nil, errors.New("postage contract is paused") + } + + if o.FullNodeMode { + err = batchSvc.Start(ctx, postageSyncStart, initBatchState) + syncStatus.Store(true) + if err != nil { + syncErr.Store(err) + return nil, fmt.Errorf("unable to start batch service: %w", err) + } + } else { + go func() { + logger.Info("started postage contract data sync in the background...") + err := batchSvc.Start(ctx, postageSyncStart, initBatchState) + syncStatus.Store(true) + if err != nil { + syncErr.Store(err) + logger.Error(err, "unable to sync batches") + b.syncingStopped.Signal() // trigger shutdown in start.go + } + }() + } + + } + + minThreshold := big.NewInt(2 * refreshRate) + maxThreshold := big.NewInt(24 * refreshRate) + + if !o.FullNodeMode { + minThreshold = big.NewInt(2 * lightRefreshRate) + } + + lightPaymentThreshold := new(big.Int).Div(paymentThreshold, big.NewInt(lightFactor)) + + pricer := pricer.NewFixedPricer(swarmAddress, basePrice) + + if paymentThreshold.Cmp(minThreshold) < 0 { + return nil, fmt.Errorf("payment threshold below minimum generally accepted value, need at least %s", minThreshold) + } + + if paymentThreshold.Cmp(maxThreshold) > 0 { + return nil, fmt.Errorf("payment threshold above maximum generally accepted value, needs to be reduced to at most %s", maxThreshold) + } + + pricing := pricing.New(p2ps, logger, paymentThreshold, lightPaymentThreshold, minThreshold) + + if err = p2ps.AddProtocol(pricing.Protocol()); err != nil { + return nil, fmt.Errorf("pricing service: %w", err) + } + + addrs, err := p2ps.Addresses() + if err != nil { + return nil, fmt.Errorf("get server addresses: %w", err) + } + + for _, addr := range addrs { + logger.Debug("p2p address", "address", addr) + } + + var enforcedRefreshRate *big.Int + + if o.FullNodeMode { + enforcedRefreshRate = big.NewInt(refreshRate) + } else { + enforcedRefreshRate = big.NewInt(lightRefreshRate) + } + + acc, err := accounting.NewAccounting( + paymentThreshold, + o.PaymentTolerance, + o.PaymentEarly, + logger, + stateStore, + pricing, + new(big.Int).Set(enforcedRefreshRate), + lightFactor, + p2ps, + ) + if err != nil { + return nil, fmt.Errorf("accounting: %w", err) + } + b.accountingCloser = acc + + pseudosettleService := pseudosettle.New(p2ps, logger, stateStore, acc, new(big.Int).Set(enforcedRefreshRate), big.NewInt(lightRefreshRate), p2ps) + if err = p2ps.AddProtocol(pseudosettleService.Protocol()); err != nil { + return nil, fmt.Errorf("pseudosettle service: %w", err) + } + + acc.SetRefreshFunc(pseudosettleService.Pay) + + if o.SwapEnable && chainEnabled { + var priceOracle priceoracle.Service + swapService, priceOracle, err = InitSwap( + p2ps, + logger, + stateStore, + networkID, + overlayEthAddress, + chequebookService, + chequeStore, + cashoutService, + acc, + o.PriceOracleAddress, + chainID, + transactionService, + ) + if err != nil { + return nil, fmt.Errorf("init swap service: %w", err) + } + b.priceOracleCloser = priceOracle + + if o.ChequebookEnable { + acc.SetPayFunc(swapService.Pay) + } + } + + pricing.SetPaymentThresholdObserver(acc) + + pssService := pss.New(pssPrivateKey, logger) + gsocService := gsoc.New(logger) + b.pssCloser = pssService + b.gsocCloser = gsocService + + validStamp := postage.ValidStamp(batchStore) + + // metrics exposed on the status protocol + statusMetricsRegistry := prometheus.NewRegistry() + if localStore != nil { + statusMetricsRegistry.MustRegister(localStore.StatusMetrics()...) + } + if p2ps != nil { + statusMetricsRegistry.MustRegister(p2ps.StatusMetrics()...) + } + + nodeStatus := status.NewService(logger, p2ps, kad, beeNodeMode.String(), batchStore, localStore, statusMetricsRegistry) + if err = p2ps.AddProtocol(nodeStatus.Protocol()); err != nil { + return nil, fmt.Errorf("status service: %w", err) + } + + saludService := salud.New(nodeStatus, kad, localStore, logger, detector, api.FullMode.String(), salud.DefaultDurPercentile, salud.DefaultConnsPercentile) + b.saludCloser = saludService + + rC, unsub := saludService.SubscribeNetworkStorageRadius() + initialRadiusC := make(chan struct{}) + var networkR atomic.Uint32 + networkR.Store(uint32(swarm.MaxBins)) + + go func() { + for { + select { + case r := <-rC: + prev := networkR.Load() + networkR.Store(uint32(r)) + if prev == uint32(swarm.MaxBins) { + close(initialRadiusC) + } + if !o.FullNodeMode { // light and ultra-light nodes do not have a reserve worker to set the radius. + kad.SetStorageRadius(r) + } + case <-ctx.Done(): + unsub() + return + } + } + }() + + waitNetworkRFunc := func() (uint8, error) { + if networkR.Load() == uint32(swarm.MaxBins) { + select { + case <-initialRadiusC: + case <-ctx.Done(): + return 0, ctx.Err() + } + } + + local, network := localStore.StorageRadius(), uint8(networkR.Load()) + if local <= uint8(o.MinimumStorageRadius) { + return max(network, uint8(o.MinimumStorageRadius)), nil + } else { + return local, nil + } + } + + pushSyncProtocol := pushsync.New(swarmAddress, networkID, nonce, p2ps, localStore, waitNetworkRFunc, kad, o.FullNodeMode && !o.BootnodeMode, pssService.TryUnwrap, gsocService.Handle, validStamp, logger, acc, pricer, signer, tracer, detector, uint8(shallowReceiptTolerance)) + b.pushSyncCloser = pushSyncProtocol + + // set the pushSyncer in the PSS + pssService.SetPushSyncer(pushSyncProtocol) + + retrieval := retrieval.New(swarmAddress, waitNetworkRFunc, localStore, p2ps, kad, logger, acc, pricer, tracer, o.RetrievalCaching) + localStore.SetRetrievalService(retrieval) + + statusMetricsRegistry.MustRegister(retrieval.StatusMetrics()...) + + pusherService := pusher.New(networkID, localStore, pushSyncProtocol, batchStore, logger, detector, pusher.DefaultRetryCount) + b.pusherCloser = pusherService + + pusherService.AddFeed(localStore.PusherFeed()) + + pullSyncProtocol := pullsync.New(p2ps, localStore, pssService.TryUnwrap, gsocService.Handle, validStamp, logger, pullsync.DefaultMaxPage) + b.pullSyncCloser = pullSyncProtocol + + retrieveProtocolSpec := retrieval.Protocol() + pushSyncProtocolSpec := pushSyncProtocol.Protocol() + pullSyncProtocolSpec := pullSyncProtocol.Protocol() + + if o.FullNodeMode && !o.BootnodeMode { + logger.Info("starting in full mode") + } else { + if chainEnabled { + logger.Info("starting in light mode") + } else { + logger.Info("starting in ultra-light mode") + } + p2p.WithBlocklistStreams(p2p.DefaultBlocklistTime, retrieveProtocolSpec) + p2p.WithBlocklistStreams(p2p.DefaultBlocklistTime, pushSyncProtocolSpec) + p2p.WithBlocklistStreams(p2p.DefaultBlocklistTime, pullSyncProtocolSpec) + } + + if err = p2ps.AddProtocol(retrieveProtocolSpec); err != nil { + return nil, fmt.Errorf("retrieval service: %w", err) + } + if err = p2ps.AddProtocol(pushSyncProtocolSpec); err != nil { + return nil, fmt.Errorf("pushsync service: %w", err) + } + if err = p2ps.AddProtocol(pullSyncProtocolSpec); err != nil { + return nil, fmt.Errorf("pullsync protocol: %w", err) + } + + go func() { + sub, unsubscribe := detector.Subscribe() + defer unsubscribe() + <-sub + logger.Info("node warmup stabilization complete, updating API status") + apiService.SetIsWarmingUp(false) + }() + + stakingContractAddress := chainCfg.StakingAddress + if o.StakingContractAddress != "" { + if !common.IsHexAddress(o.StakingContractAddress) { + return nil, errors.New("malformed staking contract address") + } + stakingContractAddress = common.HexToAddress(o.StakingContractAddress) + } + + stakingContract := staking.New(overlayEthAddress, stakingContractAddress, abiutil.MustParseABI(chainCfg.StakingABI), bzzTokenAddress, transactionService, common.BytesToHash(nonce), o.TrxDebugMode, uint8(o.ReserveCapacityDoubling)) + + if chainEnabled { + + stake, err := stakingContract.GetPotentialStake(ctx) + if err != nil { + return nil, fmt.Errorf("get potential stake: %w", err) + } + + if stake.Cmp(big.NewInt(0)) > 0 { + + if changedOverlay { + logger.Debug("changing overlay address in staking contract") + tx, err := stakingContract.ChangeStakeOverlay(ctx, common.BytesToHash(nonce)) + if err != nil { + return nil, fmt.Errorf("cannot change staking overlay address: %v", err.Error()) + } + logger.Info("overlay address changed in staking contract", "transaction", tx) + } + + // make sure that the staking contract has the up to date height + tx, updated, err := stakingContract.UpdateHeight(ctx) + if err != nil { + return nil, fmt.Errorf("update height in staking contract: %w", err) + } + if updated { + logger.Info("updated new reserve capacity doubling height in the staking contract", "transaction", tx, "new_height", o.ReserveCapacityDoubling) + } + + // Check if the staked amount is sufficient to cover the additional neighborhoods. + // The staked amount must be at least 2^h * MinimumStake. + if o.ReserveCapacityDoubling > 0 && stake.Cmp(big.NewInt(0).Mul(big.NewInt(1<> 1 + isFullySynced := func() bool { + return pullerService.SyncRate() == 0 && saludService.IsHealthy() && localStore.ReserveSize() >= reserveTreshold + } + + syncCheckTicker := time.NewTicker(2 * time.Second) + go func() { + defer syncCheckTicker.Stop() + for { + select { + case <-ctx.Done(): + return + case <-syncCheckTicker.C: + synced := isFullySynced() + logger.Debug("sync status check", "synced", synced, "reserveSize", localStore.ReserveSize(), "syncRate", pullerService.SyncRate()) + if synced { + fullSyncTime := pullSyncStartTime.Sub(t) + logger.Info("full sync done", "duration", fullSyncTime) + nodeMetrics.FullSyncDuration.Observe(fullSyncTime.Minutes()) + syncCheckTicker.Stop() + return + } + } + } + }() + } + + if o.EnableStorageIncentives { + + redistributionContractAddress := chainCfg.RedistributionAddress + if o.RedistributionContractAddress != "" { + if !common.IsHexAddress(o.RedistributionContractAddress) { + return nil, errors.New("malformed redistribution contract address") + } + redistributionContractAddress = common.HexToAddress(o.RedistributionContractAddress) + } + + redistributionContract := redistribution.New(swarmAddress, overlayEthAddress, logger, transactionService, redistributionContractAddress, abiutil.MustParseABI(chainCfg.RedistributionABI), o.TrxDebugMode) + + isFullySynced := func() bool { + reserveTreshold := reserveCapacity * 5 / 10 + logger.Debug("Sync status check evaluated", "stabilized", detector.IsStabilized()) + return localStore.ReserveSize() >= reserveTreshold && pullerService.SyncRate() == 0 && detector.IsStabilized() + } + + agent, err = storageincentives.New( + swarmAddress, + overlayEthAddress, + chainBackend, + redistributionContract, + postageStampContractService, + stakingContract, + localStore, + isFullySynced, + o.BlockTime, + storageincentives.DefaultBlocksPerRound, + storageincentives.DefaultBlocksPerPhase, + stateStore, + batchStore, + erc20Service, + transactionService, + saludService, + logger, + ) + if err != nil { + return nil, fmt.Errorf("storage incentives agent: %w", err) + } + b.storageIncetivesCloser = agent + } + + } + multiResolver := multiresolver.NewMultiResolver( + multiresolver.WithConnectionConfigs(o.ResolverConnectionCfgs), + multiresolver.WithLogger(o.Logger), + multiresolver.WithDefaultCIDResolver(), + ) + b.resolverCloser = multiResolver + + feedFactory := factory.New(localStore.Download(true)) + steward := steward.New(localStore, retrieval, localStore.Cache()) + + extraOpts := api.ExtraOptions{ + Pingpong: pingPong, + TopologyDriver: kad, + LightNodes: lightNodes, + Accounting: acc, + Pseudosettle: pseudosettleService, + Swap: swapService, + Chequebook: chequebookService, + BlockTime: o.BlockTime, + Storer: localStore, + Resolver: multiResolver, + Pss: pssService, + Gsoc: gsocService, + FeedFactory: feedFactory, + Post: post, + AccessControl: accesscontrol, + PostageContract: postageStampContractService, + Staking: stakingContract, + Steward: steward, + SyncStatus: syncStatusFn, + NodeStatus: nodeStatus, + PinIntegrity: localStore.PinIntegrity(), + } + + if o.APIAddr != "" { + // register metrics from components + apiService.MustRegisterMetrics(p2ps.Metrics()...) + apiService.MustRegisterMetrics(pingPong.Metrics()...) + apiService.MustRegisterMetrics(acc.Metrics()...) + apiService.MustRegisterMetrics(localStore.Metrics()...) + apiService.MustRegisterMetrics(kad.Metrics()...) + apiService.MustRegisterMetrics(saludService.Metrics()...) + apiService.MustRegisterMetrics(stateStoreMetrics.Metrics()...) + apiService.MustRegisterMetrics(getMetrics(nodeMetrics)...) + + if pullerService != nil { + apiService.MustRegisterMetrics(pullerService.Metrics()...) + } + + if agent != nil { + apiService.MustRegisterMetrics(agent.Metrics()...) + } + + apiService.MustRegisterMetrics(pushSyncProtocol.Metrics()...) + apiService.MustRegisterMetrics(pusherService.Metrics()...) + apiService.MustRegisterMetrics(pullSyncProtocol.Metrics()...) + apiService.MustRegisterMetrics(retrieval.Metrics()...) + apiService.MustRegisterMetrics(lightNodes.Metrics()...) + apiService.MustRegisterMetrics(hive.Metrics()...) + + if bs, ok := batchStore.(metrics.Collector); ok { + apiService.MustRegisterMetrics(bs.Metrics()...) + } + if ls, ok := eventListener.(metrics.Collector); ok { + apiService.MustRegisterMetrics(ls.Metrics()...) + } + if pssServiceMetrics, ok := pssService.(metrics.Collector); ok { + apiService.MustRegisterMetrics(pssServiceMetrics.Metrics()...) + } + if swapBackendMetrics, ok := chainBackend.(metrics.Collector); ok { + apiService.MustRegisterMetrics(swapBackendMetrics.Metrics()...) + } + + if l, ok := logger.(metrics.Collector); ok { + apiService.MustRegisterMetrics(l.Metrics()...) + } + apiService.MustRegisterMetrics(pseudosettleService.Metrics()...) + if swapService != nil { + apiService.MustRegisterMetrics(swapService.Metrics()...) + } + + apiService.Configure(signer, tracer, api.Options{ + CORSAllowedOrigins: o.CORSAllowedOrigins, + WsPingPeriod: 60 * time.Second, + }, extraOpts, chainID, erc20Service) + + apiService.EnableFullAPI() + + apiService.SetRedistributionAgent(agent) + + // api metrics are constructed on api.Service.Configure + statusMetricsRegistry.MustRegister(apiService.StatusMetrics()...) + } + + if err := kad.Start(ctx); err != nil { + return nil, fmt.Errorf("start kademlia: %w", err) + } + + if err := p2ps.Ready(); err != nil { + return nil, fmt.Errorf("p2ps ready: %w", err) + } + + return b, nil +} diff --git a/pkg/p2p/libp2p/libp2p.go b/pkg/p2p/libp2p/libp2p.go index 0a26f7b287d..97780557274 100644 --- a/pkg/p2p/libp2p/libp2p.go +++ b/pkg/p2p/libp2p/libp2p.go @@ -11,38 +11,27 @@ import ( "errors" "fmt" "net" - "os" - "path/filepath" "runtime" "slices" - "strconv" "strings" "sync" "time" - ocprom "contrib.go.opencensus.io/exporter/prometheus" - "github.com/caddyserver/certmagic" "github.com/coreos/go-semver/semver" "github.com/ethersphere/bee/v2" "github.com/ethersphere/bee/v2/pkg/addressbook" "github.com/ethersphere/bee/v2/pkg/bzz" - beecrypto "github.com/ethersphere/bee/v2/pkg/crypto" "github.com/ethersphere/bee/v2/pkg/log" - m2 "github.com/ethersphere/bee/v2/pkg/metrics" "github.com/ethersphere/bee/v2/pkg/p2p" "github.com/ethersphere/bee/v2/pkg/p2p/libp2p/internal/blocklist" "github.com/ethersphere/bee/v2/pkg/p2p/libp2p/internal/breaker" "github.com/ethersphere/bee/v2/pkg/p2p/libp2p/internal/handshake" "github.com/ethersphere/bee/v2/pkg/p2p/libp2p/internal/reacher" - libp2pmock "github.com/ethersphere/bee/v2/pkg/p2p/libp2p/mock" - "github.com/ethersphere/bee/v2/pkg/storage" "github.com/ethersphere/bee/v2/pkg/swarm" "github.com/ethersphere/bee/v2/pkg/topology" - "github.com/ethersphere/bee/v2/pkg/topology/lightnode" "github.com/ethersphere/bee/v2/pkg/tracing" "github.com/libp2p/go-libp2p" "github.com/libp2p/go-libp2p/config" - "github.com/libp2p/go-libp2p/core/crypto" "github.com/libp2p/go-libp2p/core/event" "github.com/libp2p/go-libp2p/core/host" "github.com/libp2p/go-libp2p/core/network" @@ -51,21 +40,14 @@ import ( "github.com/libp2p/go-libp2p/core/protocol" "github.com/libp2p/go-libp2p/p2p/host/autonat" basichost "github.com/libp2p/go-libp2p/p2p/host/basic" - "github.com/libp2p/go-libp2p/p2p/host/peerstore/pstoremem" - rcmgr "github.com/libp2p/go-libp2p/p2p/host/resource-manager" lp2pswarm "github.com/libp2p/go-libp2p/p2p/net/swarm" libp2pping "github.com/libp2p/go-libp2p/p2p/protocol/ping" - "github.com/libp2p/go-libp2p/p2p/transport/tcp" - ws "github.com/libp2p/go-libp2p/p2p/transport/websocket" ma "github.com/multiformats/go-multiaddr" manet "github.com/multiformats/go-multiaddr/net" "github.com/multiformats/go-multistream" "github.com/prometheus/client_golang/prometheus" "go.uber.org/atomic" "go.uber.org/zap" - "go.uber.org/zap/zapcore" - - p2pforge "github.com/ipshipyard/p2p-forge/client" ) // loggerName is the tree path name of the logger for this package. @@ -162,383 +144,6 @@ type Options struct { autoTLSCertManager autoTLSCertManager } -func New(ctx context.Context, signer beecrypto.Signer, networkID uint64, overlay swarm.Address, addr string, ab addressbook.Putter, storer storage.StateStorer, lightNodes *lightnode.Container, logger log.Logger, tracer *tracing.Tracer, o Options) (s *Service, returnErr error) { - logger = logger.WithName(loggerName).Register() - - parsedAddr, err := parseAddress(addr) - if err != nil { - return nil, err - } - - var listenAddrs []string - - if parsedAddr.IP4 != "" { - listenAddrs = append(listenAddrs, fmt.Sprintf("/ip4/%s/tcp/%s", parsedAddr.IP4, parsedAddr.Port)) - if o.EnableWS { - listenAddrs = append(listenAddrs, fmt.Sprintf("/ip4/%s/tcp/%s/ws", parsedAddr.IP4, parsedAddr.Port)) - } - } - - if parsedAddr.IP6 != "" { - listenAddrs = append(listenAddrs, fmt.Sprintf("/ip6/%s/tcp/%s", parsedAddr.IP6, parsedAddr.Port)) - if o.EnableWS { - listenAddrs = append(listenAddrs, fmt.Sprintf("/ip6/%s/tcp/%s/ws", parsedAddr.IP6, parsedAddr.Port)) - } - } - - if o.EnableWSS { - parsedWssAddr, err := parseAddress(o.WSSAddr) - if err != nil { - return nil, err - } - - if parsedWssAddr.IP4 != "" { - listenAddrs = append(listenAddrs, fmt.Sprintf("/ip4/%s/tcp/%s/tls/sni/*.%s/ws", parsedWssAddr.IP4, parsedWssAddr.Port, o.AutoTLSDomain)) - } - - if parsedWssAddr.IP6 != "" { - listenAddrs = append(listenAddrs, fmt.Sprintf("/ip6/%s/tcp/%s/tls/sni/*.%s/ws", parsedWssAddr.IP6, parsedWssAddr.Port, o.AutoTLSDomain)) - } - } - - security := libp2p.DefaultSecurity - libp2pPeerstore, err := pstoremem.NewPeerstore() - if err != nil { - return nil, err - } - - if o.Registry != nil { - rcmgr.MustRegisterWith(o.Registry) - } - - _, err = ocprom.NewExporter(ocprom.Options{ - Namespace: m2.Namespace, - Registry: o.Registry, - }) - if err != nil { - return nil, err - } - - // Tweak certain settings - cfg := rcmgr.PartialLimitConfig{ - System: rcmgr.ResourceLimits{ - Streams: IncomingStreamCountLimit + OutgoingStreamCountLimit, - StreamsOutbound: OutgoingStreamCountLimit, - StreamsInbound: IncomingStreamCountLimit, - }, - } - - // Create our limits by using our cfg and replacing the default values with values from `scaledDefaultLimits` - limits := cfg.Build(rcmgr.InfiniteLimits) - - // The resource manager expects a limiter, se we create one from our limits. - limiter := rcmgr.NewFixedLimiter(limits) - - str, err := rcmgr.NewStatsTraceReporter() - if err != nil { - return nil, err - } - - rm, err := rcmgr.NewResourceManager(limiter, rcmgr.WithTraceReporter(str)) - if err != nil { - return nil, err - } - - var natManager basichost.NATManager - - var certManager autoTLSCertManager - var zapLogger *zap.Logger - - // AutoTLS is only needed for WSS - enableAutoTLS := o.EnableWSS - - if enableAutoTLS { - if o.autoTLSCertManager != nil { - certManager = o.autoTLSCertManager - } else { - // create a zap logger needed for cert manager to be as close to - // swarm logger as possible - l, err := zap.Config{ - Level: zap.NewAtomicLevelAt(zap.DebugLevel), - Development: false, - Sampling: &zap.SamplingConfig{ - Initial: 100, - Thereafter: 100, - }, - Encoding: "json", - EncoderConfig: zapcore.EncoderConfig{ - TimeKey: "time", - LevelKey: "level", - NameKey: "logger", - CallerKey: "caller", - FunctionKey: zapcore.OmitKey, - MessageKey: "msg", - StacktraceKey: "stacktrace", - LineEnding: zapcore.DefaultLineEnding, - EncodeLevel: zapcore.LowercaseLevelEncoder, - EncodeTime: zapcore.EpochTimeEncoder, - EncodeDuration: zapcore.SecondsDurationEncoder, - EncodeCaller: zapcore.ShortCallerEncoder, - }, - OutputPaths: []string{"stderr"}, - ErrorOutputPaths: []string{"stderr"}, - }.Build() - if err != nil { - return nil, err - } - - // assing zap logger as it needs to be synced when the service stops - zapLogger = l - - defer func() { - _ = zapLogger.Sync() - }() - - // Use AutoTLS storage dir with domain subdir for easier management - // of different registers. - storagePath := filepath.Join(o.AutoTLSStorageDir, o.AutoTLSDomain) - - if err := os.MkdirAll(storagePath, 0700); err != nil { - return nil, fmt.Errorf("create certificate storage directory %s: %w", storagePath, err) - } - - certManager, err = p2pforge.NewP2PForgeCertMgr( - p2pforge.WithForgeDomain(o.AutoTLSDomain), - p2pforge.WithForgeRegistrationEndpoint(o.AutoTLSRegistrationEndpoint), - p2pforge.WithCAEndpoint(o.AutoTLSCAEndpoint), - p2pforge.WithCertificateStorage(&certmagic.FileStorage{Path: storagePath}), - p2pforge.WithLogger(zapLogger.Sugar()), - p2pforge.WithUserAgent(userAgent()), - p2pforge.WithAllowPrivateForgeAddrs(), - p2pforge.WithRegistrationDelay(0), - p2pforge.WithOnCertLoaded(func() { - logger.Info("auto tls certificate is loaded") - }), - p2pforge.WithOnCertRenewed(func() { - logger.Info("auto tls certificate is renewed") - }), - ) - if err != nil { - return nil, fmt.Errorf("initialize AutoTLS: %w", err) - } - } - - defer func() { - if returnErr != nil { - // certificate manager has to be stopped if service is not - // constructed - certManager.Stop() - } - }() - - if err := certManager.Start(); err != nil { - return nil, fmt.Errorf("start AutoTLS certificate manager: %w", err) - } - - logger.Info("AutoTLS certificate manager initialized") - } - - opts := []libp2p.Option{ - libp2p.ListenAddrStrings(listenAddrs...), - security, - // Use dedicated peerstore instead the global DefaultPeerstore - libp2p.Peerstore(libp2pPeerstore), - libp2p.UserAgent(userAgent()), - libp2p.ResourceManager(rm), - } - - if o.NATAddr == "" && o.NATWSSAddr == "" { - opts = append(opts, - libp2p.NATManager(func(n network.Network) basichost.NATManager { - natManager = basichost.NewNATManager(n) - return natManager - }), - ) - } - - if o.PrivateKey != nil { - myKey, _, err := crypto.ECDSAKeyPairFromKey(o.PrivateKey) - if err != nil { - return nil, err - } - opts = append(opts, - libp2p.Identity(myKey), - ) - } - - transports := []libp2p.Option{ - libp2p.Transport(tcp.NewTCPTransport, tcp.DisableReuseport()), - } - - var tcpResolver handshake.AdvertisableAddressResolver - if o.NATAddr != "" { - r, err := newStaticAddressResolver(o.NATAddr, net.LookupIP) - if err != nil { - return nil, fmt.Errorf("static nat: %w", err) - } - tcpResolver = r - } - - var wssResolver handshake.AdvertisableAddressResolver - if o.EnableWSS && o.NATWSSAddr != "" { - r, err := newStaticAddressResolver(o.NATWSSAddr, net.LookupIP) - if err != nil { - return nil, fmt.Errorf("static wss nat: %w", err) - } - wssResolver = r - } - - if o.EnableWSS { - wsOpt := ws.WithTLSConfig(certManager.TLSConfig()) - transports = append(transports, libp2p.Transport(ws.New, wsOpt)) - // AddrsFactory takes the multiaddrs we're listening on and sets the multiaddrs to advertise to the network. - // We use the AutoTLS address factory so that the `*` in the AutoTLS address string is replaced with the - // actual IP address of the host once detected - certManagerAddressFactory := certManager.AddressFactory() - opts = append(opts, libp2p.AddrsFactory(func(addrs []ma.Multiaddr) []ma.Multiaddr { - addrs = includeNatResolvedAddresses(addrs, newCompositeAddressResolver(tcpResolver, wssResolver), logger) - - addrs = certManagerAddressFactory(addrs) - - slices.SortStableFunc(addrs, func(a, b ma.Multiaddr) int { - aPub := manet.IsPublicAddr(a) - bPub := manet.IsPublicAddr(b) - switch { - case aPub == bPub: - return 0 - case aPub: - return -1 - case bPub: - return 1 - } - return 0 - }) - return addrs - })) - } else if o.EnableWS { - transports = append(transports, libp2p.Transport(ws.New)) - } - - opts = append(opts, transports...) - - if o.hostFactory == nil { - // Use the default libp2p host creation - o.hostFactory = libp2p.New - } - - h, err := o.hostFactory(opts...) - if err != nil { - return nil, err - } - - if enableAutoTLS { - switch cm := certManager.(type) { - case *p2pforge.P2PForgeCertMgr: - cm.ProvideHost(h) - case *libp2pmock.MockP2PForgeCertMgr: - if err := cm.ProvideHost(h); err != nil { - return nil, fmt.Errorf("failed to provide host to MockP2PForgeCertMgr: %w", err) - } - default: - return nil, fmt.Errorf("unknown cert manager type") - } - } - // Support same non default security and transport options as - // original host. - dialer, err := o.hostFactory(append(transports, security)...) - if err != nil { - return nil, err - } - - if o.HeadersRWTimeout == 0 { - o.HeadersRWTimeout = defaultHeadersRWTimeout - } - - options := []autonat.Option{ - autonat.EnableService(dialer.Network()), - } - - val, err := strconv.ParseBool(reachabilityOverridePublic) - if err != nil { - return nil, err - } - if val { - options = append(options, autonat.WithReachability(network.ReachabilityPublic)) - } - - // If you want to help other peers to figure out if they are behind - // NATs, you can launch the server-side of AutoNAT too (AutoRelay - // already runs the client) - var autoNAT autonat.AutoNAT - if autoNAT, err = autonat.New(h, options...); err != nil { - return nil, fmt.Errorf("autonat: %w", err) - } - - handshakeService, err := handshake.New(signer, newCompositeAddressResolver(tcpResolver, wssResolver), overlay, networkID, o.FullNode, o.Nonce, newHostAddresser(h), o.WelcomeMessage, o.ValidateOverlay, h.ID(), logger) - if err != nil { - return nil, fmt.Errorf("handshake service: %w", err) - } - - // Create a new dialer for libp2p ping protocol. This ensures that the protocol - // uses a different set of keys to do ping. It prevents inconsistencies in peerstore as - // the addresses used are not dialable and hence should be cleaned up. We should create - // this host with the same transports and security options to be able to dial to other - // peers. - pingDialer, err := o.hostFactory(append(transports, security, libp2p.NoListenAddrs)...) - if err != nil { - return nil, err - } - - peerRegistry := newPeerRegistry() - s = &Service{ - ctx: ctx, - host: h, - natManager: natManager, - autonatDialer: dialer, - pingDialer: pingDialer, - handshakeService: handshakeService, - libp2pPeerstore: libp2pPeerstore, - metrics: newMetrics(), - networkID: networkID, - peers: peerRegistry, - addressbook: ab, - blocklist: blocklist.NewBlocklist(storer), - logger: logger, - tracer: tracer, - connectionBreaker: breaker.NewBreaker(breaker.Options{}), // use default options - ready: make(chan struct{}), - halt: make(chan struct{}), - lightNodes: lightNodes, - HeadersRWTimeout: o.HeadersRWTimeout, - autoNAT: autoNAT, - enableWS: o.EnableWS, - autoTLSCertManager: certManager, - zapLogger: zapLogger, - } - - peerRegistry.setDisconnecter(s) - - s.lightNodeLimit = defaultLightNodeLimit - if o.LightNodeLimit > 0 { - s.lightNodeLimit = o.LightNodeLimit - } - - // Construct protocols. - id := protocol.ID(p2p.NewSwarmStreamName(handshake.ProtocolName, handshake.ProtocolVersion, handshake.StreamName)) - matcher, err := s.protocolSemverMatcher(id) - if err != nil { - return nil, fmt.Errorf("protocol version match %s: %w", id, err) - } - - s.host.SetStreamHandlerMatch(id, matcher, s.handleIncoming) - - connMetricNotify := newConnMetricNotify(s.metrics) - h.Network().Notify(peerRegistry) // update peer registry on network events - h.Network().Notify(connMetricNotify) - - return s, nil -} - type parsedAddress struct { IP4 string IP6 string @@ -1494,33 +1099,6 @@ func (c *connectionNotifier) Connected(_ network.Network, _ network.Conn) { // isNetworkOrHostUnreachableError determines based on the // given error whether the host or network is reachable. -func isNetworkOrHostUnreachableError(err error) bool { - var de *lp2pswarm.DialError - if !errors.As(err, &de) { - return false - } - - // Since TransportError doesn't implement the Unwrap - // method we need to inspect the errors manually. - for i := range de.DialErrors { - var te *lp2pswarm.TransportError - if !errors.As(&de.DialErrors[i], &te) { - continue - } - - var ne *net.OpError - if !errors.As(te.Cause, &ne) || ne.Op != "dial" { - continue - } - - var se *os.SyscallError - if errors.As(ne, &se) && strings.HasPrefix(se.Syscall, "connect") && - (errors.Is(se.Err, errHostUnreachable) || errors.Is(se.Err, errNetworkUnreachable)) { - return true - } - } - return false -} type compositeAddressResolver struct { tcpResolver handshake.AdvertisableAddressResolver diff --git a/pkg/p2p/libp2p/libp2p_js.go b/pkg/p2p/libp2p/libp2p_js.go new file mode 100644 index 00000000000..ccaf3159cc9 --- /dev/null +++ b/pkg/p2p/libp2p/libp2p_js.go @@ -0,0 +1,253 @@ +//go:build js + +package libp2p + +import ( + "context" + "errors" + "fmt" + "net" + + ocprom "contrib.go.opencensus.io/exporter/prometheus" + "github.com/ethersphere/bee/v2/pkg/addressbook" + beecrypto "github.com/ethersphere/bee/v2/pkg/crypto" + "github.com/ethersphere/bee/v2/pkg/log" + m2 "github.com/ethersphere/bee/v2/pkg/metrics" + "github.com/ethersphere/bee/v2/pkg/p2p" + "github.com/ethersphere/bee/v2/pkg/p2p/libp2p/internal/blocklist" + "github.com/ethersphere/bee/v2/pkg/p2p/libp2p/internal/breaker" + "github.com/ethersphere/bee/v2/pkg/p2p/libp2p/internal/handshake" + "github.com/ethersphere/bee/v2/pkg/storage" + "github.com/ethersphere/bee/v2/pkg/swarm" + "github.com/ethersphere/bee/v2/pkg/topology/lightnode" + "github.com/ethersphere/bee/v2/pkg/tracing" + "github.com/libp2p/go-libp2p" + "github.com/libp2p/go-libp2p/core/crypto" + "github.com/libp2p/go-libp2p/core/network" + "github.com/libp2p/go-libp2p/core/protocol" + basichost "github.com/libp2p/go-libp2p/p2p/host/basic" + "github.com/libp2p/go-libp2p/p2p/host/peerstore/pstoremem" + rcmgr "github.com/libp2p/go-libp2p/p2p/host/resource-manager" + "go.uber.org/zap" + + lp2pswarm "github.com/libp2p/go-libp2p/p2p/net/swarm" + + wasmws "github.com/v1rtl/go-libp2p-wasmws" +) + +func New(ctx context.Context, signer beecrypto.Signer, networkID uint64, overlay swarm.Address, addr string, ab addressbook.Putter, storer storage.StateStorer, lightNodes *lightnode.Container, logger log.Logger, tracer *tracing.Tracer, o Options) (s *Service, returnErr error) { + logger = logger.WithName(loggerName).Register() + + security := libp2p.DefaultSecurity + libp2pPeerstore, err := pstoremem.NewPeerstore() + if err != nil { + return nil, err + } + + if o.Registry != nil { + rcmgr.MustRegisterWith(o.Registry) + } + + _, err = ocprom.NewExporter(ocprom.Options{ + Namespace: m2.Namespace, + Registry: o.Registry, + }) + if err != nil { + return nil, err + } + + // Tweak certain settings + cfg := rcmgr.PartialLimitConfig{ + System: rcmgr.ResourceLimits{ + Streams: IncomingStreamCountLimit + OutgoingStreamCountLimit, + StreamsOutbound: OutgoingStreamCountLimit, + StreamsInbound: IncomingStreamCountLimit, + }, + } + + // Create our limits by using our cfg and replacing the default values with values from `scaledDefaultLimits` + limits := cfg.Build(rcmgr.InfiniteLimits) + + // The resource manager expects a limiter, se we create one from our limits. + limiter := rcmgr.NewFixedLimiter(limits) + + str, err := rcmgr.NewStatsTraceReporter() + if err != nil { + return nil, err + } + + rm, err := rcmgr.NewResourceManager(limiter, rcmgr.WithTraceReporter(str)) + if err != nil { + return nil, err + } + + var natManager basichost.NATManager + + var zapLogger *zap.Logger + + opts := []libp2p.Option{ + libp2p.NoListenAddrs, + security, + // Use dedicated peerstore instead the global DefaultPeerstore + libp2p.Peerstore(libp2pPeerstore), + libp2p.UserAgent(userAgent()), + libp2p.ResourceManager(rm), + } + + if o.NATAddr == "" && o.NATWSSAddr == "" { + opts = append(opts, + libp2p.NATManager(func(n network.Network) basichost.NATManager { + natManager = basichost.NewNATManager(n) + return natManager + }), + ) + } + + if o.PrivateKey != nil { + myKey, _, err := crypto.ECDSAKeyPairFromKey(o.PrivateKey) + if err != nil { + return nil, err + } + opts = append(opts, + libp2p.Identity(myKey), + ) + } + + transports := []libp2p.Option{} + + var tcpResolver handshake.AdvertisableAddressResolver + if o.NATAddr != "" { + r, err := newStaticAddressResolver(o.NATAddr, net.LookupIP) + if err != nil { + return nil, fmt.Errorf("static nat: %w", err) + } + tcpResolver = r + } + + var wssResolver handshake.AdvertisableAddressResolver + if o.EnableWSS && o.NATWSSAddr != "" { + r, err := newStaticAddressResolver(o.NATWSSAddr, net.LookupIP) + if err != nil { + return nil, fmt.Errorf("static wss nat: %w", err) + } + wssResolver = r + } + + if o.EnableWS { + transports = append(transports, libp2p.Transport(wasmws.New)) + } + + opts = append(opts, transports...) + + if o.hostFactory == nil { + // Use the default libp2p host creation + o.hostFactory = libp2p.New + } + + h, err := o.hostFactory(opts...) + if err != nil { + return nil, err + } + + // Support same non default security and transport options as + // original host. + dialer, err := o.hostFactory(append(transports, security)...) + if err != nil { + return nil, err + } + + if o.HeadersRWTimeout == 0 { + o.HeadersRWTimeout = defaultHeadersRWTimeout + } + + // If you want to help other peers to figure out if they are behind + // NATs, you can launch the server-side of AutoNAT too (AutoRelay + // already runs the client) + + handshakeService, err := handshake.New(signer, newCompositeAddressResolver(tcpResolver, wssResolver), overlay, networkID, o.FullNode, o.Nonce, newHostAddresser(h), o.WelcomeMessage, o.ValidateOverlay, h.ID(), logger) + if err != nil { + return nil, fmt.Errorf("handshake service: %w", err) + } + + // Create a new dialer for libp2p ping protocol. This ensures that the protocol + // uses a different set of keys to do ping. It prevents inconsistencies in peerstore as + // the addresses used are not dialable and hence should be cleaned up. We should create + // this host with the same transports and security options to be able to dial to other + // peers. + pingDialer, err := o.hostFactory(append(transports, security, libp2p.NoListenAddrs)...) + if err != nil { + return nil, err + } + + peerRegistry := newPeerRegistry() + s = &Service{ + ctx: ctx, + host: h, + natManager: natManager, + autonatDialer: dialer, + pingDialer: pingDialer, + handshakeService: handshakeService, + libp2pPeerstore: libp2pPeerstore, + metrics: newMetrics(), + networkID: networkID, + peers: peerRegistry, + addressbook: ab, + blocklist: blocklist.NewBlocklist(storer), + logger: logger, + tracer: tracer, + connectionBreaker: breaker.NewBreaker(breaker.Options{}), // use default options + ready: make(chan struct{}), + halt: make(chan struct{}), + lightNodes: lightNodes, + HeadersRWTimeout: o.HeadersRWTimeout, + autoNAT: nil, + enableWS: o.EnableWS, + autoTLSCertManager: nil, + zapLogger: zapLogger, + } + + peerRegistry.setDisconnecter(s) + + s.lightNodeLimit = defaultLightNodeLimit + if o.LightNodeLimit > 0 { + s.lightNodeLimit = o.LightNodeLimit + } + + // Construct protocols. + id := protocol.ID(p2p.NewSwarmStreamName(handshake.ProtocolName, handshake.ProtocolVersion, handshake.StreamName)) + matcher, err := s.protocolSemverMatcher(id) + if err != nil { + return nil, fmt.Errorf("protocol version match %s: %w", id, err) + } + + s.host.SetStreamHandlerMatch(id, matcher, s.handleIncoming) + + connMetricNotify := newConnMetricNotify(s.metrics) + h.Network().Notify(peerRegistry) // update peer registry on network events + h.Network().Notify(connMetricNotify) + + return s, nil +} + +func isNetworkOrHostUnreachableError(err error) bool { + var de *lp2pswarm.DialError + if !errors.As(err, &de) { + return false + } + + // Since TransportError doesn't implement the Unwrap + // method we need to inspect the errors manually. + for i := range de.DialErrors { + var te *lp2pswarm.TransportError + if !errors.As(&de.DialErrors[i], &te) { + continue + } + + var ne *net.OpError + if !errors.As(te.Cause, &ne) || ne.Op != "dial" { + continue + } + + } + return false +} diff --git a/pkg/p2p/libp2p/libp2p_native.go b/pkg/p2p/libp2p/libp2p_native.go new file mode 100644 index 00000000000..66b99708c58 --- /dev/null +++ b/pkg/p2p/libp2p/libp2p_native.go @@ -0,0 +1,453 @@ +//go:build !js + +package libp2p + +import ( + "context" + "errors" + "fmt" + "net" + "os" + "path/filepath" + "slices" + "strconv" + "strings" + + ocprom "contrib.go.opencensus.io/exporter/prometheus" + "github.com/caddyserver/certmagic" + "github.com/ethersphere/bee/v2/pkg/addressbook" + beecrypto "github.com/ethersphere/bee/v2/pkg/crypto" + "github.com/ethersphere/bee/v2/pkg/log" + m2 "github.com/ethersphere/bee/v2/pkg/metrics" + "github.com/ethersphere/bee/v2/pkg/p2p" + "github.com/ethersphere/bee/v2/pkg/p2p/libp2p/internal/blocklist" + "github.com/ethersphere/bee/v2/pkg/p2p/libp2p/internal/breaker" + "github.com/ethersphere/bee/v2/pkg/p2p/libp2p/internal/handshake" + libp2pmock "github.com/ethersphere/bee/v2/pkg/p2p/libp2p/mock" + "github.com/ethersphere/bee/v2/pkg/storage" + "github.com/ethersphere/bee/v2/pkg/swarm" + "github.com/ethersphere/bee/v2/pkg/topology/lightnode" + "github.com/ethersphere/bee/v2/pkg/tracing" + p2pforge "github.com/ipshipyard/p2p-forge/client" + "github.com/libp2p/go-libp2p" + "github.com/libp2p/go-libp2p/core/crypto" + "github.com/libp2p/go-libp2p/core/network" + "github.com/libp2p/go-libp2p/core/protocol" + "github.com/libp2p/go-libp2p/p2p/host/autonat" + basichost "github.com/libp2p/go-libp2p/p2p/host/basic" + "github.com/libp2p/go-libp2p/p2p/host/peerstore/pstoremem" + rcmgr "github.com/libp2p/go-libp2p/p2p/host/resource-manager" + "github.com/libp2p/go-libp2p/p2p/transport/tcp" + ws "github.com/libp2p/go-libp2p/p2p/transport/websocket" + ma "github.com/multiformats/go-multiaddr" + manet "github.com/multiformats/go-multiaddr/net" + "go.uber.org/zap" + "go.uber.org/zap/zapcore" + + lp2pswarm "github.com/libp2p/go-libp2p/p2p/net/swarm" +) + +func New(ctx context.Context, signer beecrypto.Signer, networkID uint64, overlay swarm.Address, addr string, ab addressbook.Putter, storer storage.StateStorer, lightNodes *lightnode.Container, logger log.Logger, tracer *tracing.Tracer, o Options) (s *Service, returnErr error) { + logger = logger.WithName(loggerName).Register() + + parsedAddr, err := parseAddress(addr) + if err != nil { + return nil, err + } + + var listenAddrs []string + + if parsedAddr.IP4 != "" { + listenAddrs = append(listenAddrs, fmt.Sprintf("/ip4/%s/tcp/%s", parsedAddr.IP4, parsedAddr.Port)) + if o.EnableWS { + listenAddrs = append(listenAddrs, fmt.Sprintf("/ip4/%s/tcp/%s/ws", parsedAddr.IP4, parsedAddr.Port)) + } + } + + if parsedAddr.IP6 != "" { + listenAddrs = append(listenAddrs, fmt.Sprintf("/ip6/%s/tcp/%s", parsedAddr.IP6, parsedAddr.Port)) + if o.EnableWS { + listenAddrs = append(listenAddrs, fmt.Sprintf("/ip6/%s/tcp/%s/ws", parsedAddr.IP6, parsedAddr.Port)) + } + } + + if o.EnableWSS { + parsedWssAddr, err := parseAddress(o.WSSAddr) + if err != nil { + return nil, err + } + + if parsedWssAddr.IP4 != "" { + listenAddrs = append(listenAddrs, fmt.Sprintf("/ip4/%s/tcp/%s/tls/sni/*.%s/ws", parsedWssAddr.IP4, parsedWssAddr.Port, o.AutoTLSDomain)) + } + + if parsedWssAddr.IP6 != "" { + listenAddrs = append(listenAddrs, fmt.Sprintf("/ip6/%s/tcp/%s/tls/sni/*.%s/ws", parsedWssAddr.IP6, parsedWssAddr.Port, o.AutoTLSDomain)) + } + } + + security := libp2p.DefaultSecurity + libp2pPeerstore, err := pstoremem.NewPeerstore() + if err != nil { + return nil, err + } + + if o.Registry != nil { + rcmgr.MustRegisterWith(o.Registry) + } + + _, err = ocprom.NewExporter(ocprom.Options{ + Namespace: m2.Namespace, + Registry: o.Registry, + }) + if err != nil { + return nil, err + } + + // Tweak certain settings + cfg := rcmgr.PartialLimitConfig{ + System: rcmgr.ResourceLimits{ + Streams: IncomingStreamCountLimit + OutgoingStreamCountLimit, + StreamsOutbound: OutgoingStreamCountLimit, + StreamsInbound: IncomingStreamCountLimit, + }, + } + + // Create our limits by using our cfg and replacing the default values with values from `scaledDefaultLimits` + limits := cfg.Build(rcmgr.InfiniteLimits) + + // The resource manager expects a limiter, se we create one from our limits. + limiter := rcmgr.NewFixedLimiter(limits) + + str, err := rcmgr.NewStatsTraceReporter() + if err != nil { + return nil, err + } + + rm, err := rcmgr.NewResourceManager(limiter, rcmgr.WithTraceReporter(str)) + if err != nil { + return nil, err + } + + var natManager basichost.NATManager + + var certManager autoTLSCertManager + var zapLogger *zap.Logger + + // AutoTLS is only needed for WSS + enableAutoTLS := o.EnableWSS + + if enableAutoTLS { + if o.autoTLSCertManager != nil { + certManager = o.autoTLSCertManager + } else { + // create a zap logger needed for cert manager to be as close to + // swarm logger as possible + l, err := zap.Config{ + Level: zap.NewAtomicLevelAt(zap.DebugLevel), + Development: false, + Sampling: &zap.SamplingConfig{ + Initial: 100, + Thereafter: 100, + }, + Encoding: "json", + EncoderConfig: zapcore.EncoderConfig{ + TimeKey: "time", + LevelKey: "level", + NameKey: "logger", + CallerKey: "caller", + FunctionKey: zapcore.OmitKey, + MessageKey: "msg", + StacktraceKey: "stacktrace", + LineEnding: zapcore.DefaultLineEnding, + EncodeLevel: zapcore.LowercaseLevelEncoder, + EncodeTime: zapcore.EpochTimeEncoder, + EncodeDuration: zapcore.SecondsDurationEncoder, + EncodeCaller: zapcore.ShortCallerEncoder, + }, + OutputPaths: []string{"stderr"}, + ErrorOutputPaths: []string{"stderr"}, + }.Build() + if err != nil { + return nil, err + } + + // assing zap logger as it needs to be synced when the service stops + zapLogger = l + + defer func() { + _ = zapLogger.Sync() + }() + + // Use AutoTLS storage dir with domain subdir for easier management + // of different registers. + storagePath := filepath.Join(o.AutoTLSStorageDir, o.AutoTLSDomain) + + if err := os.MkdirAll(storagePath, 0700); err != nil { + return nil, fmt.Errorf("create certificate storage directory %s: %w", storagePath, err) + } + + certManager, err = p2pforge.NewP2PForgeCertMgr( + p2pforge.WithForgeDomain(o.AutoTLSDomain), + p2pforge.WithForgeRegistrationEndpoint(o.AutoTLSRegistrationEndpoint), + p2pforge.WithCAEndpoint(o.AutoTLSCAEndpoint), + p2pforge.WithCertificateStorage(&certmagic.FileStorage{Path: storagePath}), + p2pforge.WithLogger(zapLogger.Sugar()), + p2pforge.WithUserAgent(userAgent()), + p2pforge.WithAllowPrivateForgeAddrs(), + p2pforge.WithRegistrationDelay(0), + p2pforge.WithOnCertLoaded(func() { + logger.Info("auto tls certificate is loaded") + }), + p2pforge.WithOnCertRenewed(func() { + logger.Info("auto tls certificate is renewed") + }), + ) + if err != nil { + return nil, fmt.Errorf("initialize AutoTLS: %w", err) + } + } + + defer func() { + if returnErr != nil { + // certificate manager has to be stopped if service is not + // constructed + certManager.Stop() + } + }() + + if err := certManager.Start(); err != nil { + return nil, fmt.Errorf("start AutoTLS certificate manager: %w", err) + } + + logger.Info("AutoTLS certificate manager initialized") + } + + opts := []libp2p.Option{ + libp2p.ListenAddrStrings(listenAddrs...), + security, + // Use dedicated peerstore instead the global DefaultPeerstore + libp2p.Peerstore(libp2pPeerstore), + libp2p.UserAgent(userAgent()), + libp2p.ResourceManager(rm), + } + + if o.NATAddr == "" && o.NATWSSAddr == "" { + opts = append(opts, + libp2p.NATManager(func(n network.Network) basichost.NATManager { + natManager = basichost.NewNATManager(n) + return natManager + }), + ) + } + + if o.PrivateKey != nil { + myKey, _, err := crypto.ECDSAKeyPairFromKey(o.PrivateKey) + if err != nil { + return nil, err + } + opts = append(opts, + libp2p.Identity(myKey), + ) + } + + transports := []libp2p.Option{ + libp2p.Transport(tcp.NewTCPTransport, tcp.DisableReuseport()), + } + + var tcpResolver handshake.AdvertisableAddressResolver + if o.NATAddr != "" { + r, err := newStaticAddressResolver(o.NATAddr, net.LookupIP) + if err != nil { + return nil, fmt.Errorf("static nat: %w", err) + } + tcpResolver = r + } + + var wssResolver handshake.AdvertisableAddressResolver + if o.EnableWSS && o.NATWSSAddr != "" { + r, err := newStaticAddressResolver(o.NATWSSAddr, net.LookupIP) + if err != nil { + return nil, fmt.Errorf("static wss nat: %w", err) + } + wssResolver = r + } + + if o.EnableWSS { + wsOpt := ws.WithTLSConfig(certManager.TLSConfig()) + transports = append(transports, libp2p.Transport(ws.New, wsOpt)) + // AddrsFactory takes the multiaddrs we're listening on and sets the multiaddrs to advertise to the network. + // We use the AutoTLS address factory so that the `*` in the AutoTLS address string is replaced with the + // actual IP address of the host once detected + certManagerAddressFactory := certManager.AddressFactory() + opts = append(opts, libp2p.AddrsFactory(func(addrs []ma.Multiaddr) []ma.Multiaddr { + addrs = includeNatResolvedAddresses(addrs, newCompositeAddressResolver(tcpResolver, wssResolver), logger) + + addrs = certManagerAddressFactory(addrs) + + slices.SortStableFunc(addrs, func(a, b ma.Multiaddr) int { + aPub := manet.IsPublicAddr(a) + bPub := manet.IsPublicAddr(b) + switch { + case aPub == bPub: + return 0 + case aPub: + return -1 + case bPub: + return 1 + } + return 0 + }) + return addrs + })) + } else if o.EnableWS { + transports = append(transports, libp2p.Transport(ws.New)) + } + + opts = append(opts, transports...) + + if o.hostFactory == nil { + // Use the default libp2p host creation + o.hostFactory = libp2p.New + } + + h, err := o.hostFactory(opts...) + if err != nil { + return nil, err + } + + if enableAutoTLS { + switch cm := certManager.(type) { + case *p2pforge.P2PForgeCertMgr: + cm.ProvideHost(h) + case *libp2pmock.MockP2PForgeCertMgr: + if err := cm.ProvideHost(h); err != nil { + return nil, fmt.Errorf("failed to provide host to MockP2PForgeCertMgr: %w", err) + } + default: + return nil, fmt.Errorf("unknown cert manager type") + } + } + // Support same non default security and transport options as + // original host. + dialer, err := o.hostFactory(append(transports, security)...) + if err != nil { + return nil, err + } + + if o.HeadersRWTimeout == 0 { + o.HeadersRWTimeout = defaultHeadersRWTimeout + } + + options := []autonat.Option{ + autonat.EnableService(dialer.Network()), + } + + val, err := strconv.ParseBool(reachabilityOverridePublic) + if err != nil { + return nil, err + } + if val { + options = append(options, autonat.WithReachability(network.ReachabilityPublic)) + } + + // If you want to help other peers to figure out if they are behind + // NATs, you can launch the server-side of AutoNAT too (AutoRelay + // already runs the client) + var autoNAT autonat.AutoNAT + if autoNAT, err = autonat.New(h, options...); err != nil { + return nil, fmt.Errorf("autonat: %w", err) + } + + handshakeService, err := handshake.New(signer, newCompositeAddressResolver(tcpResolver, wssResolver), overlay, networkID, o.FullNode, o.Nonce, newHostAddresser(h), o.WelcomeMessage, o.ValidateOverlay, h.ID(), logger) + if err != nil { + return nil, fmt.Errorf("handshake service: %w", err) + } + + // Create a new dialer for libp2p ping protocol. This ensures that the protocol + // uses a different set of keys to do ping. It prevents inconsistencies in peerstore as + // the addresses used are not dialable and hence should be cleaned up. We should create + // this host with the same transports and security options to be able to dial to other + // peers. + pingDialer, err := o.hostFactory(append(transports, security, libp2p.NoListenAddrs)...) + if err != nil { + return nil, err + } + + peerRegistry := newPeerRegistry() + s = &Service{ + ctx: ctx, + host: h, + natManager: natManager, + autonatDialer: dialer, + pingDialer: pingDialer, + handshakeService: handshakeService, + libp2pPeerstore: libp2pPeerstore, + metrics: newMetrics(), + networkID: networkID, + peers: peerRegistry, + addressbook: ab, + blocklist: blocklist.NewBlocklist(storer), + logger: logger, + tracer: tracer, + connectionBreaker: breaker.NewBreaker(breaker.Options{}), // use default options + ready: make(chan struct{}), + halt: make(chan struct{}), + lightNodes: lightNodes, + HeadersRWTimeout: o.HeadersRWTimeout, + autoNAT: autoNAT, + enableWS: o.EnableWS, + autoTLSCertManager: certManager, + zapLogger: zapLogger, + } + + peerRegistry.setDisconnecter(s) + + s.lightNodeLimit = defaultLightNodeLimit + if o.LightNodeLimit > 0 { + s.lightNodeLimit = o.LightNodeLimit + } + + // Construct protocols. + id := protocol.ID(p2p.NewSwarmStreamName(handshake.ProtocolName, handshake.ProtocolVersion, handshake.StreamName)) + matcher, err := s.protocolSemverMatcher(id) + if err != nil { + return nil, fmt.Errorf("protocol version match %s: %w", id, err) + } + + s.host.SetStreamHandlerMatch(id, matcher, s.handleIncoming) + + connMetricNotify := newConnMetricNotify(s.metrics) + h.Network().Notify(peerRegistry) // update peer registry on network events + h.Network().Notify(connMetricNotify) + + return s, nil +} + +func isNetworkOrHostUnreachableError(err error) bool { + var de *lp2pswarm.DialError + if !errors.As(err, &de) { + return false + } + + // Since TransportError doesn't implement the Unwrap + // method we need to inspect the errors manually. + for i := range de.DialErrors { + var te *lp2pswarm.TransportError + if !errors.As(&de.DialErrors[i], &te) { + continue + } + + var ne *net.OpError + if !errors.As(te.Cause, &ne) || ne.Op != "dial" { + continue + } + + var se *os.SyscallError + if errors.As(ne, &se) && strings.HasPrefix(se.Syscall, "connect") && + (errors.Is(se.Err, errHostUnreachable) || errors.Is(se.Err, errNetworkUnreachable)) { + return true + } + } + return false +} diff --git a/wasm-demo/.gitignore b/wasm-demo/.gitignore new file mode 100644 index 00000000000..087729c1247 --- /dev/null +++ b/wasm-demo/.gitignore @@ -0,0 +1,37 @@ +# dependencies (bun install) +node_modules + +# output +out +dist +*.tgz + +# code coverage +coverage +*.lcov + +# logs +logs +_.log +report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# caches +.eslintcache +.cache +*.tsbuildinfo + +# IntelliJ based IDEs +.idea + +# Finder (MacOS) folder config +.DS_Store + +sw_bundle.js +bee.wasm diff --git a/wasm-demo/README.md b/wasm-demo/README.md new file mode 100644 index 00000000000..5f22dd62ed5 --- /dev/null +++ b/wasm-demo/README.md @@ -0,0 +1,15 @@ +# wasm-demo + +To install dependencies: + +```bash +bun install +``` + +To run: + +```bash +bun run +``` + +This project was created using `bun init` in bun v1.3.3. [Bun](https://bun.com) is a fast all-in-one JavaScript runtime. diff --git a/wasm-demo/bun.lock b/wasm-demo/bun.lock new file mode 100644 index 00000000000..a3aec2fe8e1 --- /dev/null +++ b/wasm-demo/bun.lock @@ -0,0 +1,116 @@ +{ + "lockfileVersion": 1, + "configVersion": 1, + "workspaces": { + "": { + "name": "wasm-demo", + "dependencies": { + "@zenfs/core": "^2.4.4", + }, + "devDependencies": { + "@types/bun": "latest", + "esbuild": "^0.27.1", + }, + "peerDependencies": { + "typescript": "^5", + }, + }, + }, + "packages": { + "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.1", "", { "os": "aix", "cpu": "ppc64" }, "sha512-HHB50pdsBX6k47S4u5g/CaLjqS3qwaOVE5ILsq64jyzgMhLuCuZ8rGzM9yhsAjfjkbgUPMzZEPa7DAp7yz6vuA=="], + + "@esbuild/android-arm": ["@esbuild/android-arm@0.27.1", "", { "os": "android", "cpu": "arm" }, "sha512-kFqa6/UcaTbGm/NncN9kzVOODjhZW8e+FRdSeypWe6j33gzclHtwlANs26JrupOntlcWmB0u8+8HZo8s7thHvg=="], + + "@esbuild/android-arm64": ["@esbuild/android-arm64@0.27.1", "", { "os": "android", "cpu": "arm64" }, "sha512-45fuKmAJpxnQWixOGCrS+ro4Uvb4Re9+UTieUY2f8AEc+t7d4AaZ6eUJ3Hva7dtrxAAWHtlEFsXFMAgNnGU9uQ=="], + + "@esbuild/android-x64": ["@esbuild/android-x64@0.27.1", "", { "os": "android", "cpu": "x64" }, "sha512-LBEpOz0BsgMEeHgenf5aqmn/lLNTFXVfoWMUox8CtWWYK9X4jmQzWjoGoNb8lmAYml/tQ/Ysvm8q7szu7BoxRQ=="], + + "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.27.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-veg7fL8eMSCVKL7IW4pxb54QERtedFDfY/ASrumK/SbFsXnRazxY4YykN/THYqFnFwJ0aVjiUrVG2PwcdAEqQQ=="], + + "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.27.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-+3ELd+nTzhfWb07Vol7EZ+5PTbJ/u74nC6iv4/lwIU99Ip5uuY6QoIf0Hn4m2HoV0qcnRivN3KSqc+FyCHjoVQ=="], + + "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.27.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-/8Rfgns4XD9XOSXlzUDepG8PX+AVWHliYlUkFI3K3GB6tqbdjYqdhcb4BKRd7C0BhZSoaCxhv8kTcBrcZWP+xg=="], + + "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.27.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-GITpD8dK9C+r+5yRT/UKVT36h/DQLOHdwGVwwoHidlnA168oD3uxA878XloXebK4Ul3gDBBIvEdL7go9gCUFzQ=="], + + "@esbuild/linux-arm": ["@esbuild/linux-arm@0.27.1", "", { "os": "linux", "cpu": "arm" }, "sha512-ieMID0JRZY/ZeCrsFQ3Y3NlHNCqIhTprJfDgSB3/lv5jJZ8FX3hqPyXWhe+gvS5ARMBJ242PM+VNz/ctNj//eA=="], + + "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.27.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-W9//kCrh/6in9rWIBdKaMtuTTzNj6jSeG/haWBADqLLa9P8O5YSRDzgD5y9QBok4AYlzS6ARHifAb75V6G670Q=="], + + "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.27.1", "", { "os": "linux", "cpu": "ia32" }, "sha512-VIUV4z8GD8rtSVMfAj1aXFahsi/+tcoXXNYmXgzISL+KB381vbSTNdeZHHHIYqFyXcoEhu9n5cT+05tRv13rlw=="], + + "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.27.1", "", { "os": "linux", "cpu": "none" }, "sha512-l4rfiiJRN7sTNI//ff65zJ9z8U+k6zcCg0LALU5iEWzY+a1mVZ8iWC1k5EsNKThZ7XCQ6YWtsZ8EWYm7r1UEsg=="], + + "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.27.1", "", { "os": "linux", "cpu": "none" }, "sha512-U0bEuAOLvO/DWFdygTHWY8C067FXz+UbzKgxYhXC0fDieFa0kDIra1FAhsAARRJbvEyso8aAqvPdNxzWuStBnA=="], + + "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.27.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-NzdQ/Xwu6vPSf/GkdmRNsOfIeSGnh7muundsWItmBsVpMoNPVpM61qNzAVY3pZ1glzzAxLR40UyYM23eaDDbYQ=="], + + "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.27.1", "", { "os": "linux", "cpu": "none" }, "sha512-7zlw8p3IApcsN7mFw0O1Z1PyEk6PlKMu18roImfl3iQHTnr/yAfYv6s4hXPidbDoI2Q0pW+5xeoM4eTCC0UdrQ=="], + + "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.27.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-cGj5wli+G+nkVQdZo3+7FDKC25Uh4ZVwOAK6A06Hsvgr8WqBBuOy/1s+PUEd/6Je+vjfm6stX0kmib5b/O2Ykw=="], + + "@esbuild/linux-x64": ["@esbuild/linux-x64@0.27.1", "", { "os": "linux", "cpu": "x64" }, "sha512-z3H/HYI9MM0HTv3hQZ81f+AKb+yEoCRlUby1F80vbQ5XdzEMyY/9iNlAmhqiBKw4MJXwfgsh7ERGEOhrM1niMA=="], + + "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.27.1", "", { "os": "none", "cpu": "arm64" }, "sha512-wzC24DxAvk8Em01YmVXyjl96Mr+ecTPyOuADAvjGg+fyBpGmxmcr2E5ttf7Im8D0sXZihpxzO1isus8MdjMCXQ=="], + + "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.27.1", "", { "os": "none", "cpu": "x64" }, "sha512-1YQ8ybGi2yIXswu6eNzJsrYIGFpnlzEWRl6iR5gMgmsrR0FcNoV1m9k9sc3PuP5rUBLshOZylc9nqSgymI+TYg=="], + + "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.27.1", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-5Z+DzLCrq5wmU7RDaMDe2DVXMRm2tTDvX2KU14JJVBN2CT/qov7XVix85QoJqHltpvAOZUAc3ndU56HSMWrv8g=="], + + "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.27.1", "", { "os": "openbsd", "cpu": "x64" }, "sha512-Q73ENzIdPF5jap4wqLtsfh8YbYSZ8Q0wnxplOlZUOyZy7B4ZKW8DXGWgTCZmF8VWD7Tciwv5F4NsRf6vYlZtqg=="], + + "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.27.1", "", { "os": "none", "cpu": "arm64" }, "sha512-ajbHrGM/XiK+sXM0JzEbJAen+0E+JMQZ2l4RR4VFwvV9JEERx+oxtgkpoKv1SevhjavK2z2ReHk32pjzktWbGg=="], + + "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.27.1", "", { "os": "sunos", "cpu": "x64" }, "sha512-IPUW+y4VIjuDVn+OMzHc5FV4GubIwPnsz6ubkvN8cuhEqH81NovB53IUlrlBkPMEPxvNnf79MGBoz8rZ2iW8HA=="], + + "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.27.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-RIVRWiljWA6CdVu8zkWcRmGP7iRRIIwvhDKem8UMBjPql2TXM5PkDVvvrzMtj1V+WFPB4K7zkIGM7VzRtFkjdg=="], + + "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.27.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-2BR5M8CPbptC1AK5JbJT1fWrHLvejwZidKx3UMSF0ecHMa+smhi16drIrCEggkgviBwLYd5nwrFLSl5Kho96RQ=="], + + "@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.1", "", { "os": "win32", "cpu": "x64" }, "sha512-d5X6RMYv6taIymSk8JBP+nxv8DQAMY6A51GPgusqLdK9wBz5wWIXy1KjTck6HnjE9hqJzJRdk+1p/t5soSbCtw=="], + + "@types/bun": ["@types/bun@1.3.4", "", { "dependencies": { "bun-types": "1.3.4" } }, "sha512-EEPTKXHP+zKGPkhRLv+HI0UEX8/o+65hqARxLy8Ov5rIxMBPNTjeZww00CIihrIQGEQBYg+0roO5qOnS/7boGA=="], + + "@types/node": ["@types/node@24.10.2", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-WOhQTZ4G8xZ1tjJTvKOpyEVSGgOTvJAfDK3FNFgELyaTpzhdgHVHeqW8V+UJvzF5BT+/B54T/1S2K6gd9c7bbA=="], + + "@xterm/xterm": ["@xterm/xterm@5.5.0", "", {}, "sha512-hqJHYaQb5OptNunnyAnkHyM8aCjZ1MEIDTQu1iIbbTD/xops91NB5yq1ZK/dC2JDbVWtF23zUtl9JE2NqwT87A=="], + + "@zenfs/core": ["@zenfs/core@2.4.4", "", { "dependencies": { "@types/node": "^24.1.0", "buffer": "^6.0.3", "eventemitter3": "^5.0.1", "kerium": "^1.3.4", "memium": "^0.3.10", "readable-stream": "^4.5.2", "utilium": "^2.5.0" }, "bin": { "make-index": "scripts/make-index.js", "zenfs-test": "scripts/test.js", "zci": "scripts/ci-cli.js" } }, "sha512-4ove99nTRyS3WPNlHN6i0AxMMYW85YBo2lVS/aNXU48dYOl78XiM98d4uly/9PwtllGJHEv9GHkomxS2eoOLFw=="], + + "abort-controller": ["abort-controller@3.0.0", "", { "dependencies": { "event-target-shim": "^5.0.0" } }, "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg=="], + + "base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="], + + "buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="], + + "bun-types": ["bun-types@1.3.4", "", { "dependencies": { "@types/node": "*" } }, "sha512-5ua817+BZPZOlNaRgGBpZJOSAQ9RQ17pkwPD0yR7CfJg+r8DgIILByFifDTa+IPDDxzf5VNhtNlcKqFzDgJvlQ=="], + + "esbuild": ["esbuild@0.27.1", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.1", "@esbuild/android-arm": "0.27.1", "@esbuild/android-arm64": "0.27.1", "@esbuild/android-x64": "0.27.1", "@esbuild/darwin-arm64": "0.27.1", "@esbuild/darwin-x64": "0.27.1", "@esbuild/freebsd-arm64": "0.27.1", "@esbuild/freebsd-x64": "0.27.1", "@esbuild/linux-arm": "0.27.1", "@esbuild/linux-arm64": "0.27.1", "@esbuild/linux-ia32": "0.27.1", "@esbuild/linux-loong64": "0.27.1", "@esbuild/linux-mips64el": "0.27.1", "@esbuild/linux-ppc64": "0.27.1", "@esbuild/linux-riscv64": "0.27.1", "@esbuild/linux-s390x": "0.27.1", "@esbuild/linux-x64": "0.27.1", "@esbuild/netbsd-arm64": "0.27.1", "@esbuild/netbsd-x64": "0.27.1", "@esbuild/openbsd-arm64": "0.27.1", "@esbuild/openbsd-x64": "0.27.1", "@esbuild/openharmony-arm64": "0.27.1", "@esbuild/sunos-x64": "0.27.1", "@esbuild/win32-arm64": "0.27.1", "@esbuild/win32-ia32": "0.27.1", "@esbuild/win32-x64": "0.27.1" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-yY35KZckJJuVVPXpvjgxiCuVEJT67F6zDeVTv4rizyPrfGBUpZQsvmxnN+C371c2esD/hNMjj4tpBhuueLN7aA=="], + + "event-target-shim": ["event-target-shim@5.0.1", "", {}, "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ=="], + + "eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="], + + "events": ["events@3.3.0", "", {}, "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q=="], + + "ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="], + + "kerium": ["kerium@1.3.8", "", { "dependencies": { "utilium": "^2.0.0" } }, "sha512-tOtOOsTZ85Wt4h8TwV4ItgTi6paj2U1buOC/v9Rdon0ndtqASm5fn/rBRfEsif1czW4+u73T7XPbQAIBUF4tzg=="], + + "memium": ["memium@0.3.11", "", { "dependencies": { "kerium": "^1.3.2", "utilium": "^2.0.0" } }, "sha512-CwmIpLVSG7UToDj2sYAZFDkpco30OPsXpaCnt+7Z7JQaulCjH5UvwJIctTIHmgQdFqk8pliBsPLnH5OZcDLZyQ=="], + + "process": ["process@0.11.10", "", {}, "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A=="], + + "readable-stream": ["readable-stream@4.7.0", "", { "dependencies": { "abort-controller": "^3.0.0", "buffer": "^6.0.3", "events": "^3.3.0", "process": "^0.11.10", "string_decoder": "^1.3.0" } }, "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg=="], + + "safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="], + + "string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="], + + "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], + + "undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], + + "utilium": ["utilium@2.5.8", "", { "dependencies": { "eventemitter3": "^5.0.1" }, "optionalDependencies": { "@xterm/xterm": "^5.5.0" }, "bin": { "lice": "scripts/lice.js" } }, "sha512-RNXAYoYH7hefayCW/2LU3SYCwDGQlDc8f4gPCRbMO6dJsYGGlzAhyHqYQGp73ox41JtPKjdqzddAbDhWQ9MrXQ=="], + } +} diff --git a/wasm-demo/index.html b/wasm-demo/index.html new file mode 100644 index 00000000000..6e3692a8f66 --- /dev/null +++ b/wasm-demo/index.html @@ -0,0 +1,12 @@ + + + + + + Browser Bee Demo + + + + + + diff --git a/wasm-demo/main.js b/wasm-demo/main.js new file mode 100644 index 00000000000..f15848b6a23 --- /dev/null +++ b/wasm-demo/main.js @@ -0,0 +1,12 @@ +if ("serviceWorker" in navigator) { + navigator.serviceWorker + .register("/sw_bundle.js", { scope: "/" }) + .then((registration) => { + console.log("Service Worker registered successfully:", registration); + }) + .catch((error) => { + console.error("Service Worker registration failed:", error); + }); +} else { + console.log("Service Worker is not supported in this browser."); +} diff --git a/wasm-demo/package.json b/wasm-demo/package.json new file mode 100644 index 00000000000..dbe77d38e6e --- /dev/null +++ b/wasm-demo/package.json @@ -0,0 +1,17 @@ +{ + "name": "wasm-demo", + "private": true, + "devDependencies": { + "@types/bun": "latest", + "esbuild": "^0.27.1" + }, + "peerDependencies": { + "typescript": "^5" + }, + "dependencies": { + "@zenfs/core": "^2.4.4" + }, + "scripts": { + "build": "bunx esbuild sw.js --bundle --platform=browser > sw_bundle.js" + } +} diff --git a/wasm-demo/sw.js b/wasm-demo/sw.js new file mode 100644 index 00000000000..7edea1d9b0d --- /dev/null +++ b/wasm-demo/sw.js @@ -0,0 +1,150 @@ +import "./wasm_exec.js"; + +import { configure, fs, InMemory } from "@zenfs/core"; + +const WASM_PATH = "bee.wasm"; + +self.addEventListener("activate", (event) => { + event.waitUntil( + clients.claim().catch((error) => { + console.error("Error during service worker activate:", error); + }), + ); +}); + +// Handle fetch events to serve the WASM module if needed +const path = new URL(self.registration.scope).pathname; +const handlerPromise = new Promise((setHandler) => { + self.wasmhttp = { path, setHandler }; +}); + +self.addEventListener("fetch", (e) => { + const { pathname } = new URL(e.request.url); + if (!pathname.startsWith(path)) return; + + e.respondWith(handlerPromise.then((handler) => handler(e.request))); +}); + +async function main() { + // Set up ZenFS in-memory filesystem + await configure({ + mounts: { + "/tmp": InMemory, + "/home/user": InMemory, + }, + }); + + // Create necessary directories + await fs.promises.mkdir("/home/user/.bee/keys", { + recursive: true, + mode: 0o700, + }); + + // Write key files + await fs.promises.writeFile( + "/home/user/.bee/keys/libp2p_v2.key", + JSON.stringify({ + address: + "049886e5793c6261f59e7b047a91c27226cdbc2ba5af60c9e26705c15441ec9e3f7daa7085a2a7665c338171eb2bf1b65a173636137405d825d0385bc4defacaf4", + crypto: { + cipher: "aes-128-ctr", + ciphertext: + "e35f6f83893bc6186119b85244b43d42b08f92891b6cb7c81f695c0a94ea2536c84fb84e3410618ddee7c814acdf35f1facc79597540e6fa3d460278ffa414311880676ef5fad8b06362b422c139ffb5cdbad530d371e645dc8e496b7b04f93c2ae23554cfc1452a414bf0c1324d326d45980d190ff784ebd9", + cipherparams: { iv: "f917c56ec7e2aa36fd592c63894aa18a" }, + kdf: "scrypt", + kdfparams: { + n: 32768, + r: 8, + p: 1, + dklen: 32, + salt: "dcbc48279045788f9b12ffa7989880290b190e50506e2d9596b4d476528cedd0", + }, + mac: "1482a352544e9cc13c1954acf9c313c9e25901c530262c7e198d4f221b76027a", + }, + version: 3, + id: "5117e84d-0a2b-4c4c-808d-1e9676903c8a", + }), + { mode: 0o600 }, + ); + + await fs.promises.writeFile( + "/home/user/.bee/keys/swarm.key", + JSON.stringify({ + address: "ed48f21d97fd09d08584f42c97f737bc549c49bf", + crypto: { + cipher: "aes-128-ctr", + ciphertext: + "85221a9ec6ff8328f80686ddaa6afe9c1da4b74e8494515cc34e1ff2b9567285", + cipherparams: { iv: "e01d72acdfb68338adcf99ae44f7aeb0" }, + kdf: "scrypt", + kdfparams: { + n: 32768, + r: 8, + p: 1, + dklen: 32, + salt: "7ac4dd27cfe9b796793270a6b4c84e9f717533161105a6425576da20aff0f554", + }, + mac: "ccaa689b4f09bab5580b515dfcb1a6fcbc7ced6d769a5b8232b1508eaa9c6dc3", + }, + version: 3, + id: "2f567b5f-122d-4625-a9f5-25c3285550e1", + }), + { mode: 0o600 }, + ); + + // Expose ZenFS for debugging purposes + self.ZenFS = fs; + + // Initialize Go runtime and set environment variables + const go = new Go(); + + go.env = { + HOME: "/home/user", + PATH: "/usr/bin:/usr/local/bin", + }; + + go.argv = [ + "bee.wasm", + "start", + "--password", + "testing", + "--bootnode", + "/dnsaddr/testnet.ethswarm.org", + "--data-dir", + "/home/user/.bee/sepolia", + "--verbosity", + "debug", + // '--blockchain-rpc-endpoint', + // 'https://ethereum-sepolia-rpc.publicnode.com/ac5b7f52aabd778861c2588f872f15c5fc34f0b343ec3d18ac2e91f5526e9c2b', + "--mainnet=false", + "--network-id=5", + ]; + + // Override Go's `fs.readFile` with ZenFS readFile functionality + const goImportObject = { + ...go.importObject, + fs: { + ...fs, + readFile: async (path) => { + try { + const data = await fs.promises.readFile(path, "utf8"); + return new TextEncoder().encode(data); // Return Uint8Array + } catch (error) { + console.error("Error reading file:", path, error); + throw error; + } + }, + }, + }; + + // Load and run the WASM binary + await WebAssembly.instantiateStreaming(fetch(WASM_PATH), goImportObject).then( + (result) => { + go.run(result.instance); + }, + ); +} + +main().catch((error) => { + console.error("Error running worker:", error); +}); diff --git a/wasm-demo/tsconfig.json b/wasm-demo/tsconfig.json new file mode 100644 index 00000000000..bfa0fead54e --- /dev/null +++ b/wasm-demo/tsconfig.json @@ -0,0 +1,29 @@ +{ + "compilerOptions": { + // Environment setup & latest features + "lib": ["ESNext"], + "target": "ESNext", + "module": "Preserve", + "moduleDetection": "force", + "jsx": "react-jsx", + "allowJs": true, + + // Bundler mode + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "noEmit": true, + + // Best practices + "strict": true, + "skipLibCheck": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedIndexedAccess": true, + "noImplicitOverride": true, + + // Some stricter flags (disabled by default) + "noUnusedLocals": false, + "noUnusedParameters": false, + "noPropertyAccessFromIndexSignature": false + } +} diff --git a/wasm-demo/wasm_exec.js b/wasm-demo/wasm_exec.js new file mode 100644 index 00000000000..d71af9e97e8 --- /dev/null +++ b/wasm-demo/wasm_exec.js @@ -0,0 +1,575 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +"use strict"; + +(() => { + const enosys = () => { + const err = new Error("not implemented"); + err.code = "ENOSYS"; + return err; + }; + + if (!globalThis.fs) { + let outputBuf = ""; + globalThis.fs = { + constants: { O_WRONLY: -1, O_RDWR: -1, O_CREAT: -1, O_TRUNC: -1, O_APPEND: -1, O_EXCL: -1, O_DIRECTORY: -1 }, // unused + writeSync(fd, buf) { + outputBuf += decoder.decode(buf); + const nl = outputBuf.lastIndexOf("\n"); + if (nl != -1) { + console.log(outputBuf.substring(0, nl)); + outputBuf = outputBuf.substring(nl + 1); + } + return buf.length; + }, + write(fd, buf, offset, length, position, callback) { + if (offset !== 0 || length !== buf.length || position !== null) { + callback(enosys()); + return; + } + const n = this.writeSync(fd, buf); + callback(null, n); + }, + chmod(path, mode, callback) { callback(enosys()); }, + chown(path, uid, gid, callback) { callback(enosys()); }, + close(fd, callback) { callback(enosys()); }, + fchmod(fd, mode, callback) { callback(enosys()); }, + fchown(fd, uid, gid, callback) { callback(enosys()); }, + fstat(fd, callback) { callback(enosys()); }, + fsync(fd, callback) { callback(null); }, + ftruncate(fd, length, callback) { callback(enosys()); }, + lchown(path, uid, gid, callback) { callback(enosys()); }, + link(path, link, callback) { callback(enosys()); }, + lstat(path, callback) { callback(enosys()); }, + mkdir(path, perm, callback) { callback(enosys()); }, + open(path, flags, mode, callback) { callback(enosys()); }, + read(fd, buffer, offset, length, position, callback) { callback(enosys()); }, + readdir(path, callback) { callback(enosys()); }, + readlink(path, callback) { callback(enosys()); }, + rename(from, to, callback) { callback(enosys()); }, + rmdir(path, callback) { callback(enosys()); }, + stat(path, callback) { callback(enosys()); }, + symlink(path, link, callback) { callback(enosys()); }, + truncate(path, length, callback) { callback(enosys()); }, + unlink(path, callback) { callback(enosys()); }, + utimes(path, atime, mtime, callback) { callback(enosys()); }, + }; + } + + if (!globalThis.process) { + globalThis.process = { + getuid() { return -1; }, + getgid() { return -1; }, + geteuid() { return -1; }, + getegid() { return -1; }, + getgroups() { throw enosys(); }, + pid: -1, + ppid: -1, + umask() { throw enosys(); }, + cwd() { throw enosys(); }, + chdir() { throw enosys(); }, + } + } + + if (!globalThis.path) { + globalThis.path = { + resolve(...pathSegments) { + return pathSegments.join("/"); + } + } + } + + if (!globalThis.crypto) { + throw new Error("globalThis.crypto is not available, polyfill required (crypto.getRandomValues only)"); + } + + if (!globalThis.performance) { + throw new Error("globalThis.performance is not available, polyfill required (performance.now only)"); + } + + if (!globalThis.TextEncoder) { + throw new Error("globalThis.TextEncoder is not available, polyfill required"); + } + + if (!globalThis.TextDecoder) { + throw new Error("globalThis.TextDecoder is not available, polyfill required"); + } + + const encoder = new TextEncoder("utf-8"); + const decoder = new TextDecoder("utf-8"); + + globalThis.Go = class { + constructor() { + this.argv = ["js"]; + this.env = {}; + this.exit = (code) => { + if (code !== 0) { + console.warn("exit code:", code); + } + }; + this._exitPromise = new Promise((resolve) => { + this._resolveExitPromise = resolve; + }); + this._pendingEvent = null; + this._scheduledTimeouts = new Map(); + this._nextCallbackTimeoutID = 1; + + const setInt64 = (addr, v) => { + this.mem.setUint32(addr + 0, v, true); + this.mem.setUint32(addr + 4, Math.floor(v / 4294967296), true); + } + + const setInt32 = (addr, v) => { + this.mem.setUint32(addr + 0, v, true); + } + + const getInt64 = (addr) => { + const low = this.mem.getUint32(addr + 0, true); + const high = this.mem.getInt32(addr + 4, true); + return low + high * 4294967296; + } + + const loadValue = (addr) => { + const f = this.mem.getFloat64(addr, true); + if (f === 0) { + return undefined; + } + if (!isNaN(f)) { + return f; + } + + const id = this.mem.getUint32(addr, true); + return this._values[id]; + } + + const storeValue = (addr, v) => { + const nanHead = 0x7FF80000; + + if (typeof v === "number" && v !== 0) { + if (isNaN(v)) { + this.mem.setUint32(addr + 4, nanHead, true); + this.mem.setUint32(addr, 0, true); + return; + } + this.mem.setFloat64(addr, v, true); + return; + } + + if (v === undefined) { + this.mem.setFloat64(addr, 0, true); + return; + } + + let id = this._ids.get(v); + if (id === undefined) { + id = this._idPool.pop(); + if (id === undefined) { + id = this._values.length; + } + this._values[id] = v; + this._goRefCounts[id] = 0; + this._ids.set(v, id); + } + this._goRefCounts[id]++; + let typeFlag = 0; + switch (typeof v) { + case "object": + if (v !== null) { + typeFlag = 1; + } + break; + case "string": + typeFlag = 2; + break; + case "symbol": + typeFlag = 3; + break; + case "function": + typeFlag = 4; + break; + } + this.mem.setUint32(addr + 4, nanHead | typeFlag, true); + this.mem.setUint32(addr, id, true); + } + + const loadSlice = (addr) => { + const array = getInt64(addr + 0); + const len = getInt64(addr + 8); + return new Uint8Array(this._inst.exports.mem.buffer, array, len); + } + + const loadSliceOfValues = (addr) => { + const array = getInt64(addr + 0); + const len = getInt64(addr + 8); + const a = new Array(len); + for (let i = 0; i < len; i++) { + a[i] = loadValue(array + i * 8); + } + return a; + } + + const loadString = (addr) => { + const saddr = getInt64(addr + 0); + const len = getInt64(addr + 8); + return decoder.decode(new DataView(this._inst.exports.mem.buffer, saddr, len)); + } + + const testCallExport = (a, b) => { + this._inst.exports.testExport0(); + return this._inst.exports.testExport(a, b); + } + + const timeOrigin = Date.now() - performance.now(); + this.importObject = { + _gotest: { + add: (a, b) => a + b, + callExport: testCallExport, + }, + gojs: { + // Go's SP does not change as long as no Go code is running. Some operations (e.g. calls, getters and setters) + // may synchronously trigger a Go event handler. This makes Go code get executed in the middle of the imported + // function. A goroutine can switch to a new stack if the current stack is too small (see morestack function). + // This changes the SP, thus we have to update the SP used by the imported function. + + // func wasmExit(code int32) + "runtime.wasmExit": (sp) => { + sp >>>= 0; + const code = this.mem.getInt32(sp + 8, true); + this.exited = true; + delete this._inst; + delete this._values; + delete this._goRefCounts; + delete this._ids; + delete this._idPool; + this.exit(code); + }, + + // func wasmWrite(fd uintptr, p unsafe.Pointer, n int32) + "runtime.wasmWrite": (sp) => { + sp >>>= 0; + const fd = getInt64(sp + 8); + const p = getInt64(sp + 16); + const n = this.mem.getInt32(sp + 24, true); + fs.writeSync(fd, new Uint8Array(this._inst.exports.mem.buffer, p, n)); + }, + + // func resetMemoryDataView() + "runtime.resetMemoryDataView": (sp) => { + sp >>>= 0; + this.mem = new DataView(this._inst.exports.mem.buffer); + }, + + // func nanotime1() int64 + "runtime.nanotime1": (sp) => { + sp >>>= 0; + setInt64(sp + 8, (timeOrigin + performance.now()) * 1000000); + }, + + // func walltime() (sec int64, nsec int32) + "runtime.walltime": (sp) => { + sp >>>= 0; + const msec = (new Date).getTime(); + setInt64(sp + 8, msec / 1000); + this.mem.setInt32(sp + 16, (msec % 1000) * 1000000, true); + }, + + // func scheduleTimeoutEvent(delay int64) int32 + "runtime.scheduleTimeoutEvent": (sp) => { + sp >>>= 0; + const id = this._nextCallbackTimeoutID; + this._nextCallbackTimeoutID++; + this._scheduledTimeouts.set(id, setTimeout( + () => { + this._resume(); + while (this._scheduledTimeouts.has(id)) { + // for some reason Go failed to register the timeout event, log and try again + // (temporary workaround for https://github.com/golang/go/issues/28975) + console.warn("scheduleTimeoutEvent: missed timeout event"); + this._resume(); + } + }, + getInt64(sp + 8), + )); + this.mem.setInt32(sp + 16, id, true); + }, + + // func clearTimeoutEvent(id int32) + "runtime.clearTimeoutEvent": (sp) => { + sp >>>= 0; + const id = this.mem.getInt32(sp + 8, true); + clearTimeout(this._scheduledTimeouts.get(id)); + this._scheduledTimeouts.delete(id); + }, + + // func getRandomData(r []byte) + "runtime.getRandomData": (sp) => { + sp >>>= 0; + crypto.getRandomValues(loadSlice(sp + 8)); + }, + + // func finalizeRef(v ref) + "syscall/js.finalizeRef": (sp) => { + sp >>>= 0; + const id = this.mem.getUint32(sp + 8, true); + this._goRefCounts[id]--; + if (this._goRefCounts[id] === 0) { + const v = this._values[id]; + this._values[id] = null; + this._ids.delete(v); + this._idPool.push(id); + } + }, + + // func stringVal(value string) ref + "syscall/js.stringVal": (sp) => { + sp >>>= 0; + storeValue(sp + 24, loadString(sp + 8)); + }, + + // func valueGet(v ref, p string) ref + "syscall/js.valueGet": (sp) => { + sp >>>= 0; + const result = Reflect.get(loadValue(sp + 8), loadString(sp + 16)); + sp = this._inst.exports.getsp() >>> 0; // see comment above + storeValue(sp + 32, result); + }, + + // func valueSet(v ref, p string, x ref) + "syscall/js.valueSet": (sp) => { + sp >>>= 0; + Reflect.set(loadValue(sp + 8), loadString(sp + 16), loadValue(sp + 32)); + }, + + // func valueDelete(v ref, p string) + "syscall/js.valueDelete": (sp) => { + sp >>>= 0; + Reflect.deleteProperty(loadValue(sp + 8), loadString(sp + 16)); + }, + + // func valueIndex(v ref, i int) ref + "syscall/js.valueIndex": (sp) => { + sp >>>= 0; + storeValue(sp + 24, Reflect.get(loadValue(sp + 8), getInt64(sp + 16))); + }, + + // valueSetIndex(v ref, i int, x ref) + "syscall/js.valueSetIndex": (sp) => { + sp >>>= 0; + Reflect.set(loadValue(sp + 8), getInt64(sp + 16), loadValue(sp + 24)); + }, + + // func valueCall(v ref, m string, args []ref) (ref, bool) + "syscall/js.valueCall": (sp) => { + sp >>>= 0; + try { + const v = loadValue(sp + 8); + const m = Reflect.get(v, loadString(sp + 16)); + const args = loadSliceOfValues(sp + 32); + const result = Reflect.apply(m, v, args); + sp = this._inst.exports.getsp() >>> 0; // see comment above + storeValue(sp + 56, result); + this.mem.setUint8(sp + 64, 1); + } catch (err) { + sp = this._inst.exports.getsp() >>> 0; // see comment above + storeValue(sp + 56, err); + this.mem.setUint8(sp + 64, 0); + } + }, + + // func valueInvoke(v ref, args []ref) (ref, bool) + "syscall/js.valueInvoke": (sp) => { + sp >>>= 0; + try { + const v = loadValue(sp + 8); + const args = loadSliceOfValues(sp + 16); + const result = Reflect.apply(v, undefined, args); + sp = this._inst.exports.getsp() >>> 0; // see comment above + storeValue(sp + 40, result); + this.mem.setUint8(sp + 48, 1); + } catch (err) { + sp = this._inst.exports.getsp() >>> 0; // see comment above + storeValue(sp + 40, err); + this.mem.setUint8(sp + 48, 0); + } + }, + + // func valueNew(v ref, args []ref) (ref, bool) + "syscall/js.valueNew": (sp) => { + sp >>>= 0; + try { + const v = loadValue(sp + 8); + const args = loadSliceOfValues(sp + 16); + const result = Reflect.construct(v, args); + sp = this._inst.exports.getsp() >>> 0; // see comment above + storeValue(sp + 40, result); + this.mem.setUint8(sp + 48, 1); + } catch (err) { + sp = this._inst.exports.getsp() >>> 0; // see comment above + storeValue(sp + 40, err); + this.mem.setUint8(sp + 48, 0); + } + }, + + // func valueLength(v ref) int + "syscall/js.valueLength": (sp) => { + sp >>>= 0; + setInt64(sp + 16, parseInt(loadValue(sp + 8).length)); + }, + + // valuePrepareString(v ref) (ref, int) + "syscall/js.valuePrepareString": (sp) => { + sp >>>= 0; + const str = encoder.encode(String(loadValue(sp + 8))); + storeValue(sp + 16, str); + setInt64(sp + 24, str.length); + }, + + // valueLoadString(v ref, b []byte) + "syscall/js.valueLoadString": (sp) => { + sp >>>= 0; + const str = loadValue(sp + 8); + loadSlice(sp + 16).set(str); + }, + + // func valueInstanceOf(v ref, t ref) bool + "syscall/js.valueInstanceOf": (sp) => { + sp >>>= 0; + this.mem.setUint8(sp + 24, (loadValue(sp + 8) instanceof loadValue(sp + 16)) ? 1 : 0); + }, + + // func copyBytesToGo(dst []byte, src ref) (int, bool) + "syscall/js.copyBytesToGo": (sp) => { + sp >>>= 0; + const dst = loadSlice(sp + 8); + const src = loadValue(sp + 32); + if (!(src instanceof Uint8Array || src instanceof Uint8ClampedArray)) { + this.mem.setUint8(sp + 48, 0); + return; + } + const toCopy = src.subarray(0, dst.length); + dst.set(toCopy); + setInt64(sp + 40, toCopy.length); + this.mem.setUint8(sp + 48, 1); + }, + + // func copyBytesToJS(dst ref, src []byte) (int, bool) + "syscall/js.copyBytesToJS": (sp) => { + sp >>>= 0; + const dst = loadValue(sp + 8); + const src = loadSlice(sp + 16); + if (!(dst instanceof Uint8Array || dst instanceof Uint8ClampedArray)) { + this.mem.setUint8(sp + 48, 0); + return; + } + const toCopy = src.subarray(0, dst.length); + dst.set(toCopy); + setInt64(sp + 40, toCopy.length); + this.mem.setUint8(sp + 48, 1); + }, + + "debug": (value) => { + console.log(value); + }, + } + }; + } + + async run(instance) { + if (!(instance instanceof WebAssembly.Instance)) { + throw new Error("Go.run: WebAssembly.Instance expected"); + } + this._inst = instance; + this.mem = new DataView(this._inst.exports.mem.buffer); + this._values = [ // JS values that Go currently has references to, indexed by reference id + NaN, + 0, + null, + true, + false, + globalThis, + this, + ]; + this._goRefCounts = new Array(this._values.length).fill(Infinity); // number of references that Go has to a JS value, indexed by reference id + this._ids = new Map([ // mapping from JS values to reference ids + [0, 1], + [null, 2], + [true, 3], + [false, 4], + [globalThis, 5], + [this, 6], + ]); + this._idPool = []; // unused ids that have been garbage collected + this.exited = false; // whether the Go program has exited + + // Pass command line arguments and environment variables to WebAssembly by writing them to the linear memory. + let offset = 4096; + + const strPtr = (str) => { + const ptr = offset; + const bytes = encoder.encode(str + "\0"); + new Uint8Array(this.mem.buffer, offset, bytes.length).set(bytes); + offset += bytes.length; + if (offset % 8 !== 0) { + offset += 8 - (offset % 8); + } + return ptr; + }; + + const argc = this.argv.length; + + const argvPtrs = []; + this.argv.forEach((arg) => { + argvPtrs.push(strPtr(arg)); + }); + argvPtrs.push(0); + + const keys = Object.keys(this.env).sort(); + keys.forEach((key) => { + argvPtrs.push(strPtr(`${key}=${this.env[key]}`)); + }); + argvPtrs.push(0); + + const argv = offset; + argvPtrs.forEach((ptr) => { + this.mem.setUint32(offset, ptr, true); + this.mem.setUint32(offset + 4, 0, true); + offset += 8; + }); + + // The linker guarantees global data starts from at least wasmMinDataAddr. + // Keep in sync with cmd/link/internal/ld/data.go:wasmMinDataAddr. + const wasmMinDataAddr = 4096 + 8192; + if (offset >= wasmMinDataAddr) { + throw new Error("total length of command line and environment variables exceeds limit"); + } + + this._inst.exports.run(argc, argv); + if (this.exited) { + this._resolveExitPromise(); + } + await this._exitPromise; + } + + _resume() { + if (this.exited) { + throw new Error("Go program has already exited"); + } + this._inst.exports.resume(); + if (this.exited) { + this._resolveExitPromise(); + } + } + + _makeFuncWrapper(id) { + const go = this; + return function () { + const event = { id: id, this: this, args: arguments }; + go._pendingEvent = event; + go._resume(); + return event.result; + }; + } + } +})();