diff --git a/Makefile b/Makefile index 2bd32d1eb..1d448bd7f 100644 --- a/Makefile +++ b/Makefile @@ -120,6 +120,8 @@ all: tools build lint test BUILD_TARGETS := build install +PACKAGES_E2E=$(shell go list ./... | grep '/e2e') + build: BUILD_ARGS=-o $(BUILDDIR)/ build-linux: GOOS=linux GOARCH=$(if $(findstring aarch64,$(shell uname -m)) || $(findstring arm64,$(shell uname -m)),arm64,amd64) LEDGER_ENABLED=false $(MAKE) build @@ -405,6 +407,9 @@ localnet-start-test: localnet-stop localnet-build-env localnet-build-nodes-test # you can read more about the debug mode here: ./contrib/images/babylond-dlv/README.md localnet-debug: localnet-stop localnet-build-dlv localnet-build-nodes +test-e2e: + go test -mod=readonly -timeout=25m -v $(PACKAGES_E2E) -count=1 --tags=e2e + localnet-stop: docker-compose down diff --git a/go.mod b/go.mod index f39de929c..27d75ae63 100644 --- a/go.mod +++ b/go.mod @@ -29,10 +29,11 @@ require ( github.com/boljen/go-bitmap v0.0.0-20151001105940-23cd2fb0ce7d github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce - github.com/cosmos/cosmos-proto v1.0.0-alpha8 + github.com/cosmos/cosmos-proto v1.0.0-alpha7 github.com/cosmos/ibc-go/v5 v5.1.0 github.com/golang/mock v1.6.0 github.com/jinzhu/copier v0.3.5 + github.com/ory/dockertest/v3 v3.9.1 google.golang.org/protobuf v1.28.2-0.20220831092852-f930b1dc76e8 ) @@ -52,7 +53,8 @@ require ( github.com/cosmos/btcutil v1.0.4 // indirect github.com/cosmos/go-bip39 v1.0.0 github.com/cosmos/iavl v0.19.4 // indirect - github.com/cosmos/ledger-cosmos-go v0.12.0 // indirect + github.com/cosmos/ledger-cosmos-go v0.11.1 // indirect + github.com/cosmos/ledger-go v0.9.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect github.com/dgraph-io/badger/v2 v2.2007.4 // indirect @@ -112,11 +114,11 @@ require ( github.com/tendermint/btcd v0.1.1 // indirect github.com/tendermint/crypto v0.0.0-20191022145703-50d29ede1e15 // indirect github.com/tendermint/go-amino v0.16.0 // indirect - github.com/zondax/hid v0.9.1 // indirect + github.com/zondax/hid v0.9.1-0.20220302062450-5552068d2266 // indirect go.etcd.io/bbolt v1.3.6 // indirect golang.org/x/crypto v0.1.0 // indirect - golang.org/x/sys v0.1.0 // indirect - golang.org/x/term v0.1.0 // indirect + golang.org/x/sys v0.2.0 // indirect + golang.org/x/term v0.2.0 // indirect golang.org/x/text v0.4.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect nhooyr.io/websocket v1.8.6 // indirect @@ -127,18 +129,27 @@ require ( cloud.google.com/go/compute v1.7.0 // indirect cloud.google.com/go/iam v0.4.0 // indirect cloud.google.com/go/storage v1.22.1 // indirect + github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect + github.com/Microsoft/go-winio v0.6.0 // indirect + github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect github.com/aws/aws-sdk-go v1.40.45 // indirect github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect - github.com/cenkalti/backoff/v4 v4.1.3 // indirect + github.com/cenkalti/backoff/v4 v4.2.0 // indirect github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e // indirect github.com/cockroachdb/apd/v2 v2.0.2 // indirect + github.com/containerd/continuity v0.3.0 // indirect github.com/cosmos/gorocksdb v1.2.0 // indirect github.com/creachadair/taskgroup v0.3.2 // indirect github.com/danieljoos/wincred v1.1.2 // indirect + github.com/docker/cli v20.10.21+incompatible // indirect + github.com/docker/docker v20.10.21+incompatible // indirect + github.com/docker/go-connections v0.4.0 // indirect + github.com/docker/go-units v0.5.0 // indirect github.com/felixge/httpsnoop v1.0.1 // indirect github.com/golang/glog v1.0.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/google/go-cmp v0.5.9 // indirect + github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/google/uuid v1.3.0 // indirect github.com/googleapis/enterprise-certificate-proxy v0.1.0 // indirect github.com/googleapis/gax-go/v2 v2.4.0 // indirect @@ -147,20 +158,30 @@ require ( github.com/hashicorp/go-getter v1.6.1 // indirect github.com/hashicorp/go-safetemp v1.0.0 // indirect github.com/hashicorp/go-version v1.6.0 // indirect + github.com/imdario/mergo v0.3.13 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/manifoldco/promptui v0.9.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/go-testing-interface v1.0.0 // indirect + github.com/moby/term v0.0.0-20221120202655-abb19827d345 // indirect + github.com/opencontainers/go-digest v1.0.0 // indirect + github.com/opencontainers/image-spec v1.1.0-rc2 // indirect + github.com/opencontainers/runc v1.1.4 // indirect github.com/pelletier/go-toml/v2 v2.0.5 // indirect github.com/regen-network/cosmos-proto v0.3.1 // indirect github.com/rogpeppe/go-internal v1.8.1 // indirect + github.com/sirupsen/logrus v1.9.0 // indirect github.com/ulikunitz/xz v0.5.8 // indirect - github.com/zondax/ledger-go v0.14.0 // indirect + github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect + github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect + github.com/xeipuuv/gojsonschema v1.2.0 // indirect go.opencensus.io v0.23.0 // indirect golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // indirect - golang.org/x/net v0.1.0 // indirect + golang.org/x/mod v0.7.0 // indirect + golang.org/x/net v0.2.0 // indirect golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783 // indirect + golang.org/x/tools v0.3.0 // indirect golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f // indirect google.golang.org/api v0.93.0 // indirect google.golang.org/appengine v1.6.7 // indirect diff --git a/go.sum b/go.sum index a45f8216b..4b479a7ca 100644 --- a/go.sum +++ b/go.sum @@ -79,6 +79,7 @@ github.com/Azure/azure-sdk-for-go/sdk/azcore v0.21.1/go.mod h1:fBF9PQNqB8scdgpZ3 github.com/Azure/azure-sdk-for-go/sdk/internal v0.8.3/go.mod h1:KLF4gFr6DcKFZwSuH8w8yEK6DpFl3LP5rhdvAb7Yz5I= github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.3.0/go.mod h1:tPaiy8S5bQ+S5sOiDlINkp7+Ef339+Nz5L5XO+cnOHo= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d h1:nalkkPQcITbvhmL4+C4cKA87NW0tfm3Kl9VXRoPywFg= @@ -88,7 +89,9 @@ github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3 github.com/DataDog/zstd v1.5.0/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= +github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= +github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= @@ -151,6 +154,7 @@ github.com/bmizerany/pat v0.0.0-20170815010413-6226ea591a40/go.mod h1:8rLXio+Wji github.com/boljen/go-bitmap v0.0.0-20151001105940-23cd2fb0ce7d h1:zsO4lp+bjv5XvPTF58Vq+qgmZEYZttJK+CWtSZhKenI= github.com/boljen/go-bitmap v0.0.0-20151001105940-23cd2fb0ce7d/go.mod h1:f1iKL6ZhUWvbk7PdWVmOaak10o86cqMUYEmn1CZNGEI= github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= +github.com/btcsuite/btcd v0.0.0-20190115013929-ed77733ec07d/go.mod h1:d3C0AkH6BRcvO8T0UEPu53cnw4IbV63x1bEjildYhO0= github.com/btcsuite/btcd v0.0.0-20190315201642-aa6e0f35703c/go.mod h1:DrZx5ec/dmnfpw9KyYoQyYo7d0KEvTkk/5M/vbZjAr8= github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= github.com/btcsuite/btcd v0.21.0-beta.0.20201114000516-e9c7a5ac6401/go.mod h1:Sv4JPQ3/M+teHz9Bo5jBpkNcP0x6r7rdihlNL/7tTAs= @@ -162,6 +166,7 @@ github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOF github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f h1:bAs4lUbRJpnnkd9VhRV3jjAVU7DJVjMaK+IsvSeZvFo= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= +github.com/btcsuite/btcutil v0.0.0-20180706230648-ab6388e0c60a/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= github.com/btcsuite/btcutil v0.0.0-20190207003914-4c204d697803/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= github.com/btcsuite/btcutil v1.0.2/go.mod h1:j9HUFwoQRsZL3V4n+qG+CUnEGHOarIxfC3Le2Yhbcts= @@ -182,8 +187,8 @@ github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= -github.com/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4= -github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= +github.com/cenkalti/backoff/v4 v4.2.0 h1:HN5dHm3WBOgndBH6E8V0q2jIYIR3s9yglV8k/+MN3u4= +github.com/cenkalti/backoff/v4 v4.2.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= @@ -191,6 +196,7 @@ github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghf github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E= github.com/cheggaaa/pb v1.0.27/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s= github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= @@ -198,6 +204,7 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5O github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA= github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= @@ -223,17 +230,20 @@ github.com/consensys/bavard v0.1.8-0.20210406032232-f3452dc9b572/go.mod h1:Bpd0/ github.com/consensys/bavard v0.1.8-0.20210915155054-088da2f7f54a/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= github.com/consensys/gnark-crypto v0.4.1-0.20210426202927-39ac3d4b3f1f/go.mod h1:815PAHg3wvysy0SyIqanF8gZ0Y1wjk/hrDHD/iT88+Q= github.com/consensys/gnark-crypto v0.5.3/go.mod h1:hOdPlWQV1gDLp7faZVeg8Y0iEPFaOUnCc4XeCCk96p0= +github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= github.com/containerd/continuity v0.3.0 h1:nisirsYROK15TAMVukJOUyGJjz4BNQJBVsNvAXZJ/eg= +github.com/containerd/continuity v0.3.0/go.mod h1:wJEAIwKOm/pBZuBd0JmeTvnLquTB1Ag8espWhkykbPM= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cosmos/btcutil v1.0.4 h1:n7C2ngKXo7UC9gNyMNLbzqz7Asuf+7Qv4gnX/rOdQ44= github.com/cosmos/btcutil v1.0.4/go.mod h1:Ffqc8Hn6TJUdDgHBwIZLtrLQC1KdJ9jGJl/TvgUaxbU= -github.com/cosmos/cosmos-proto v1.0.0-alpha8 h1:d3pCRuMYYvGA5bM0ZbbjKn+AoQD4A7dyNG2wzwWalUw= -github.com/cosmos/cosmos-proto v1.0.0-alpha8/go.mod h1:6/p+Bc4O8JKeZqe0VqUGTX31eoYqemTT4C1hLCWsO7I= +github.com/cosmos/cosmos-proto v1.0.0-alpha7 h1:yqYUOHF2jopwZh4dVQp3xgqwftE5/2hkrwIV6vkUbO0= +github.com/cosmos/cosmos-proto v1.0.0-alpha7/go.mod h1:dosO4pSAbJF8zWCzCoTWP7nNsjcvSUBQmniFxDg5daw= github.com/cosmos/cosmos-sdk v0.46.6 h1:K9EZsqOZ2jQX3bIQUpn7Hk/YCoaJWRLU56PzvpX8INk= github.com/cosmos/cosmos-sdk v0.46.6/go.mod h1:JNklMfXo7MhDF1j/jxZCmDyOYyqhVoKB22e8p1ATEqA= github.com/cosmos/cosmos-sdk/ics23/go v0.8.0 h1:iKclrn3YEOwk4jQHT2ulgzuXyxmzmPczUalMwW4XH9k= @@ -247,8 +257,10 @@ github.com/cosmos/iavl v0.19.4 h1:t82sN+Y0WeqxDLJRSpNd8YFX5URIrT+p8n6oJbJ2Dok= github.com/cosmos/iavl v0.19.4/go.mod h1:X9PKD3J0iFxdmgNLa7b2LYWdsGd90ToV5cAONApkEPw= github.com/cosmos/keyring v1.1.7-0.20210622111912-ef00f8ac3d76 h1:DdzS1m6o/pCqeZ8VOAit/gyATedRgjvkVI+UCrLpyuU= github.com/cosmos/keyring v1.1.7-0.20210622111912-ef00f8ac3d76/go.mod h1:0mkLWIoZuQ7uBoospo5Q9zIpqq6rYCPJDSUdeCJvPM8= -github.com/cosmos/ledger-cosmos-go v0.12.0 h1:TsoQAwrbeR2ioRkfhronb+ubhX5Egrwrv/7y8oZIdVI= -github.com/cosmos/ledger-cosmos-go v0.12.0/go.mod h1:wAKOrE4tZUzV9YDzJufeRQyv636YM4UOUwMbsxw5rG0= +github.com/cosmos/ledger-cosmos-go v0.11.1 h1:9JIYsGnXP613pb2vPjFeMMjBI5lEDsEaF6oYorTy6J4= +github.com/cosmos/ledger-cosmos-go v0.11.1/go.mod h1:J8//BsAGTo3OC/vDLjMRFLW6q0WAaXvHnVc7ZmE8iUY= +github.com/cosmos/ledger-go v0.9.2 h1:Nnao/dLwaVTk1Q5U9THldpUMMXU94BOTWPddSmVB6pI= +github.com/cosmos/ledger-go v0.9.2/go.mod h1:oZJ2hHAZROdlHiwTg4t7kP+GKIIkBT+o6c9QWFanOyI= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= 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.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= @@ -256,7 +268,9 @@ github.com/creachadair/taskgroup v0.3.2 h1:zlfutDS+5XG40AOxcHDSThxKzns8Tnr9jnr6V github.com/creachadair/taskgroup v0.3.2/go.mod h1:wieWwecHVzsidg2CsUnFinW1faVN4+kq+TDlRJQ0Wbk= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw= github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4= +github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/danieljoos/wincred v1.0.2/go.mod h1:SnuYRW9lp1oJrZX/dXJqr0cPK5gYXqx3EJbmjhLdK9U= github.com/danieljoos/wincred v1.1.2 h1:QLdCxFs1/Yl4zduvBdcHB8goaYk9RARS2SgLLRuAyr0= github.com/danieljoos/wincred v1.1.2/go.mod h1:GijpziifJoIBfYh+S7BbkdUTU4LfM+QnGqR5Vl2tAx0= @@ -288,9 +302,16 @@ github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8 github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/yU9ko= github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= +github.com/docker/cli v20.10.21+incompatible h1:qVkgyYUnOLQ98LtXBrwd/duVqPT2X4SHndOuGsfwyhU= +github.com/docker/cli v20.10.21+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v20.10.21+incompatible h1:UTLdBmHk3bEY+w8qeO5KttOhy6OmXWsl/FEet9Uswog= +github.com/docker/docker v20.10.21+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= +github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/dop251/goja v0.0.0-20211011172007-d99e4b8cbf48/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk= github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= @@ -329,6 +350,7 @@ github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/ github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= +github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= 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= @@ -375,6 +397,7 @@ github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0= github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= @@ -385,6 +408,7 @@ github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/E github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 h1:ZpnhV/YsD2/4cESfV5+Hoeu/iUR3ruzNvZ+yQfO03a0= github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/gateway v1.1.0 h1:u0SuhL9+Il+UbjM9VIE3ntfRujKbvVpFvNB4HbjeVQ0= github.com/gogo/gateway v1.1.0/go.mod h1:S7rR8FRQyG3QFESeSv4l2WnsyzlCLG0CzBbUUo/mbic= @@ -484,6 +508,8 @@ github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -583,6 +609,8 @@ github.com/huin/goupnp v1.0.3-0.20220313090229-ca81a64b4204/go.mod h1:ZxNlw5WqJj 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/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= +github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= github.com/improbable-eng/grpc-web v0.15.0 h1:BN+7z6uNXZ1tQGcNAuaU1YjsLTApzkjt2tzCixLaUPQ= github.com/improbable-eng/grpc-web v0.15.0/go.mod h1:1sy9HKV4Jt9aEs9JSnkWlRJPuPtwNr0l57L4f878wP8= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= @@ -730,6 +758,9 @@ github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RR github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/pointerstructure v1.2.0/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4= +github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU= +github.com/moby/term v0.0.0-20221120202655-abb19827d345 h1:J9c53/kxIH+2nTKBEfZYFMlhghtHpIHSXpm5VRGHSnU= +github.com/moby/term v0.0.0-20221120202655-abb19827d345/go.mod h1:15ce4BGCFxt7I5NQKT+HV0yEDxmf6fSysfEDiVo3zFM= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -738,6 +769,7 @@ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3Rllmb github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8= +github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg= github.com/mtibben/percent v0.2.1 h1:5gssi8Nqo8QU/r2pynCm+hBQHpkB/uNK7BJCFogWdzs= github.com/mtibben/percent v0.2.1/go.mod h1:KG9uO+SZkUp+VkRHsCdYQV3XSZrrSpR3O9ibNBTZrns= @@ -775,8 +807,13 @@ github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0-rc2 h1:2zx/Stx4Wc5pIPDvIxHXvXtQFW/7XWJGmnM7r3wg034= -github.com/opencontainers/runc v1.1.3 h1:vIXrkId+0/J2Ymu2m7VjGvbSlAId9XNRPhn2p4b+d8w= +github.com/opencontainers/image-spec v1.1.0-rc2/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ= +github.com/opencontainers/runc v1.1.4 h1:nRCz/8sKg6K6jgYAFLDlXzPeITBZJyX28DBVhWD+5dg= +github.com/opencontainers/runc v1.1.4/go.mod h1:1J5XiS+vdZ3wCyZybsuxXZWGrgSr8fFJHLXuG2PsnNg= +github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI= github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= @@ -787,6 +824,8 @@ github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJ github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= github.com/ory/dockertest v3.3.5+incompatible h1:iLLK6SQwIhcbrG783Dghaaa3WPzGc+4Emza6EbVUUGA= +github.com/ory/dockertest/v3 v3.9.1 h1:v4dkG+dlu76goxMiTT2j8zV7s4oPPEppKT8K8p2f1kY= +github.com/ory/dockertest/v3 v3.9.1/go.mod h1:42Ir9hmvaAPm0Mgibk6mBPi7SFvTXxEcnztDYOJ//uM= github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= @@ -888,6 +927,7 @@ github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0 github.com/sasha-s/go-deadlock v0.3.1 h1:sqv7fDNShgjcaxkO0JNcOAlr8B9+cV5Ey/OB71efZx0= github.com/sasha-s/go-deadlock v0.3.1/go.mod h1:F73l+cr82YSh10GxyRI6qZiCgK64VaZjwesgfQ1/iLM= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg= github.com/segmentio/fasthash v1.0.3/go.mod h1:waKX8l2N8yckOgmSsXJi7x1ZfdKZ4x7KRMzBtS3oedY= 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= @@ -898,7 +938,9 @@ 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/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= +github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= 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= @@ -949,6 +991,7 @@ github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= github.com/supranational/blst v0.3.8 h1:glwLF4oBRSJOTr05lRBgNwGQST0ndP2wg29fSeTRKCY= github.com/supranational/blst v0.3.8/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= +github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= 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/tendermint/btcd v0.1.1 h1:0VcxPfflS2zZ3RiOAHkBiFUcPvbtRj5O7zHmcJWHV7s= @@ -988,9 +1031,18 @@ github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/X github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= +github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= +github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc= github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= github.com/willf/bitset v1.1.3/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= +github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= 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/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= @@ -1000,10 +1052,9 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/zondax/hid v0.9.1 h1:gQe66rtmyZ8VeGFcOpbuH3r7erYtNEAezCAYu8LdkJo= -github.com/zondax/hid v0.9.1/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM= -github.com/zondax/ledger-go v0.14.0 h1:dlMC7aO8Wss1CxBq2I96kZ69Nh1ligzbs8UWOtq/AsA= -github.com/zondax/ledger-go v0.14.0/go.mod h1:fZ3Dqg6qcdXWSOJFKMG8GCTnD7slO/RL2feOQv8K320= +github.com/zondax/hid v0.9.0/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM= +github.com/zondax/hid v0.9.1-0.20220302062450-5552068d2266 h1:O9XLFXGkVswDFmH9LaYpqu+r/AAFWqr0DL6V00KEVFg= +github.com/zondax/hid v0.9.1-0.20220302062450-5552068d2266/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU= go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= @@ -1094,7 +1145,8 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.6.0 h1:b9gGHsz9/HhJ3HF5DHQytPpuwocVTChQJK3AvoLRD5I= +golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA= +golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 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= @@ -1157,8 +1209,8 @@ golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0= -golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= +golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1194,6 +1246,7 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1212,6 +1265,7 @@ golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1222,6 +1276,7 @@ golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1279,9 +1334,12 @@ golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210819135213-f52c844e1c1c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/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-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1297,14 +1355,15 @@ golang.org/x/sys v0.0.0-20220517195934-5e4e11fc645e/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= -golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 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 h1:g6Z6vPFA9dYBAF7DWcH6sCcOntplXsDKcliusYijMlw= -golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.2.0 h1:z85xZCsEl7bi/KwbNADeBYoOP0++7W1ipu+aGnpwzRM= +golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= 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= @@ -1385,7 +1444,8 @@ golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE= +golang.org/x/tools v0.3.0 h1:SrNbZl6ECOS1qFzgTdQfWXZM9XBkiA6tkFrH9YSTPHM= +golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= 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= @@ -1632,9 +1692,12 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= +gotest.tools/v3 v3.2.0 h1:I0DwBVMGAx26dttAj1BtJLAkVGncrkkUXfJLC4Flt/I= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -1646,7 +1709,8 @@ honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9 honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= nhooyr.io/websocket v1.8.6 h1:s+C3xAMLwGmlI31Nyn/eAehUlZPwfYZu2JXM621Q5/k= nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= -pgregory.net/rapid v0.5.3 h1:163N50IHFqr1phZens4FQOdPgfJscR7a562mjQqeo4M= +pgregory.net/rapid v0.4.7 h1:MTNRktPuv5FNqOO151TM9mDTa+XHcX6ypYeISDVD14g= +pgregory.net/rapid v0.4.7/go.mod h1:UYpPVyjFHzYBGHIxLFoupi8vwk6rXNzRY9OMvVxFIOU= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= diff --git a/test/e2e/README.md b/test/e2e/README.md new file mode 100644 index 000000000..0a09a5c72 --- /dev/null +++ b/test/e2e/README.md @@ -0,0 +1,17 @@ +# End-to-end Tests + +## Structure + +### `e2e` Package + +The `e2e` package defines an integration testing suite used for full +end-to-end testing functionality. The package is copy of Osmosis e2e testing +approach. + + +### Common Problems + +Please note that if the tests are stopped mid-way, the e2e framework might fail to start again due to duplicated containers. Make sure that +containers are removed before running the tests again: `docker containers rm -f $(docker containers ls -a -q)`. + +Additionally, Docker networks do not get auto-removed. Therefore, you can manually remove them by running `docker network prune`. diff --git a/test/e2e/configurer/base.go b/test/e2e/configurer/base.go new file mode 100644 index 000000000..4f50c852d --- /dev/null +++ b/test/e2e/configurer/base.go @@ -0,0 +1,188 @@ +package configurer + +import ( + "encoding/json" + "fmt" + "io" + "net/http" + "os" + "path" + "path/filepath" + "testing" + "time" + + "github.com/stretchr/testify/require" + + "github.com/babylonchain/babylon/test/e2e/containers" + "github.com/babylonchain/babylon/test/e2e/initialization" + + "github.com/babylonchain/babylon/test/e2e/configurer/chain" + "github.com/babylonchain/babylon/test/e2e/util" +) + +// baseConfigurer is the base implementation for the +// other 2 types of configurers. It is not meant to be used +// on its own. Instead, it is meant to be embedded +// by composition into more concrete configurers. +type baseConfigurer struct { + chainConfigs []*chain.Config + containerManager *containers.Manager + setupTests setupFn + syncUntilHeight int64 // the height until which to wait for validators to sync when first started. + t *testing.T +} + +// defaultSyncUntilHeight arbitrary small height to make sure the chain is making progress. +const defaultSyncUntilHeight = 3 + +func (bc *baseConfigurer) ClearResources() error { + bc.t.Log("tearing down e2e integration test suite...") + + if err := bc.containerManager.ClearResources(); err != nil { + return err + } + + for _, chainConfig := range bc.chainConfigs { + os.RemoveAll(chainConfig.DataDir) + } + return nil +} + +func (bc *baseConfigurer) GetChainConfig(chainIndex int) *chain.Config { + return bc.chainConfigs[chainIndex] +} + +func (bc *baseConfigurer) RunValidators() error { + for _, chainConfig := range bc.chainConfigs { + if err := bc.runValidators(chainConfig); err != nil { + return err + } + } + return nil +} + +func (bc *baseConfigurer) runValidators(chainConfig *chain.Config) error { + bc.t.Logf("starting %s validator containers...", chainConfig.Id) + for _, node := range chainConfig.NodeConfigs { + if err := node.Run(); err != nil { + return err + } + } + return nil +} + +func (bc *baseConfigurer) RunIBC() error { + // Run a relayer between every possible pair of chains. + for i := 0; i < len(bc.chainConfigs); i++ { + for j := i + 1; j < len(bc.chainConfigs); j++ { + if err := bc.runIBCRelayer(bc.chainConfigs[i], bc.chainConfigs[j]); err != nil { + return err + } + } + } + return nil +} + +func (bc *baseConfigurer) runIBCRelayer(chainConfigA *chain.Config, chainConfigB *chain.Config) error { + bc.t.Log("starting Hermes relayer container...") + + tmpDir, err := os.MkdirTemp("", "bbn-e2e-testnet-hermes-") + if err != nil { + return err + } + + hermesCfgPath := path.Join(tmpDir, "hermes") + + if err := os.MkdirAll(hermesCfgPath, 0o755); err != nil { + return err + } + + _, err = util.CopyFile( + filepath.Join("./scripts/", "hermes_bootstrap.sh"), + filepath.Join(hermesCfgPath, "hermes_bootstrap.sh"), + ) + if err != nil { + return err + } + + // we are using non validator nodes as validator are constantly sending bls + // transactions, which makes relayer operations failing + relayerNodeA := chainConfigA.NodeConfigs[2] + relayerNodeB := chainConfigB.NodeConfigs[2] + + hermesResource, err := bc.containerManager.RunHermesResource( + chainConfigA.Id, + relayerNodeA.Name, + relayerNodeA.Mnemonic, + chainConfigB.Id, + relayerNodeB.Name, + relayerNodeB.Mnemonic, + hermesCfgPath) + if err != nil { + return err + } + + endpoint := fmt.Sprintf("http://%s/state", hermesResource.GetHostPort("3031/tcp")) + + require.Eventually(bc.t, func() bool { + resp, err := http.Get(endpoint) + if err != nil { + return false + } + + defer resp.Body.Close() + + bz, err := io.ReadAll(resp.Body) + if err != nil { + return false + } + + var respBody map[string]interface{} + if err := json.Unmarshal(bz, &respBody); err != nil { + return false + } + + status, ok := respBody["status"].(string) + require.True(bc.t, ok) + result, ok := respBody["result"].(map[string]interface{}) + require.True(bc.t, ok) + + chains, ok := result["chains"].([]interface{}) + require.True(bc.t, ok) + + return status == "success" && len(chains) == 2 + }, + 5*time.Minute, + time.Second, + "hermes relayer not healthy") + + bc.t.Logf("started Hermes relayer container: %s", hermesResource.Container.ID) + + // XXX: Give time to both networks to start, otherwise we might see gRPC + // transport errors. + time.Sleep(10 * time.Second) + + // create the client, connection and channel between the two babylon chains + return bc.connectIBCChains(chainConfigA, chainConfigB) +} + +func (bc *baseConfigurer) connectIBCChains(chainA *chain.Config, chainB *chain.Config) error { + bc.t.Logf("connecting %s and %s chains via IBC", chainA.ChainMeta.Id, chainB.ChainMeta.Id) + cmd := []string{"hermes", "create", "channel", chainA.ChainMeta.Id, chainB.ChainMeta.Id, "--port-a=zoneconcierge", "--port-b=zoneconcierge"} + _, _, err := bc.containerManager.ExecHermesCmd(bc.t, cmd, "successfully opened init channel") + if err != nil { + return err + } + bc.t.Logf("connected %s and %s chains via IBC", chainA.ChainMeta.Id, chainB.ChainMeta.Id) + return nil +} + +func (bc *baseConfigurer) initializeChainConfigFromInitChain(initializedChain *initialization.Chain, chainConfig *chain.Config) { + chainConfig.ChainMeta = initializedChain.ChainMeta + chainConfig.NodeConfigs = make([]*chain.NodeConfig, 0, len(initializedChain.Nodes)) + setupTime := time.Now() + for i, validator := range initializedChain.Nodes { + conf := chain.NewNodeConfig(bc.t, validator, chainConfig.ValidatorInitConfigs[i], chainConfig.Id, bc.containerManager).WithSetupTime(setupTime) + chainConfig.NodeConfigs = append(chainConfig.NodeConfigs, conf) + } +} diff --git a/test/e2e/configurer/chain/chain.go b/test/e2e/configurer/chain/chain.go new file mode 100644 index 000000000..8b792f597 --- /dev/null +++ b/test/e2e/configurer/chain/chain.go @@ -0,0 +1,172 @@ +package chain + +import ( + "fmt" + "testing" + "time" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" + coretypes "github.com/tendermint/tendermint/rpc/core/types" + + "github.com/babylonchain/babylon/test/e2e/configurer/config" + "github.com/babylonchain/babylon/test/e2e/containers" + "github.com/babylonchain/babylon/test/e2e/initialization" +) + +type Config struct { + initialization.ChainMeta + + ValidatorInitConfigs []*initialization.NodeConfig + // voting period is number of blocks it takes to deposit, 1.2 seconds per validator to vote on the prop, and a buffer. + VotingPeriod float32 + ExpeditedVotingPeriod float32 + // upgrade proposal height for chain. + UpgradePropHeight int64 + LatestProposalNumber int + LatestLockNumber int + NodeConfigs []*NodeConfig + + LatestCodeId int + + t *testing.T + containerManager *containers.Manager +} + +const ( + // defaultNodeIndex to use for querying and executing transactions. + // It is used when we are indifferent about the node we are working with. + defaultNodeIndex = 0 + // waitUntilRepeatPauseTime is the time to wait between each check of the node status. + waitUntilRepeatPauseTime = 2 * time.Second + // waitUntilrepeatMax is the maximum number of times to repeat the wait until condition. + waitUntilrepeatMax = 60 +) + +func New(t *testing.T, containerManager *containers.Manager, id string, initValidatorConfigs []*initialization.NodeConfig) *Config { + numVal := float32(len(initValidatorConfigs)) + return &Config{ + ChainMeta: initialization.ChainMeta{ + Id: id, + }, + ValidatorInitConfigs: initValidatorConfigs, + VotingPeriod: config.PropDepositBlocks + numVal*config.PropVoteBlocks + config.PropBufferBlocks, + ExpeditedVotingPeriod: config.PropDepositBlocks + numVal*config.PropVoteBlocks + config.PropBufferBlocks - 2, + t: t, + containerManager: containerManager, + } +} + +// CreateNode returns new initialized NodeConfig. +func (c *Config) CreateNode(initNode *initialization.Node) *NodeConfig { + nodeConfig := &NodeConfig{ + Node: *initNode, + chainId: c.Id, + containerManager: c.containerManager, + t: c.t, + } + c.NodeConfigs = append(c.NodeConfigs, nodeConfig) + return nodeConfig +} + +// RemoveNode removes node and stops it from running. +func (c *Config) RemoveNode(nodeName string) error { + for i, node := range c.NodeConfigs { + if node.Name == nodeName { + c.NodeConfigs = append(c.NodeConfigs[:i], c.NodeConfigs[i+1:]...) + return node.Stop() + } + } + return fmt.Errorf("node %s not found", nodeName) +} + +// WaitUntilHeight waits for all validators to reach the specified height at the minimum. +// returns error, if any. +func (c *Config) WaitUntilHeight(height int64) { + // Ensure the nodes are making progress. + doneCondition := func(syncInfo coretypes.SyncInfo) bool { + curHeight := syncInfo.LatestBlockHeight + + if curHeight < height { + c.t.Logf("current block height is %d, waiting to reach: %d", curHeight, height) + return false + } + + return !syncInfo.CatchingUp + } + + for _, node := range c.NodeConfigs { + c.t.Logf("node container: %s, waiting to reach height %d", node.Name, height) + node.WaitUntil(doneCondition) + } +} + +// WaitForNumHeights waits for all nodes to go through a given number of heights. +func (c *Config) WaitForNumHeights(heightsToWait int64) { + node, err := c.GetDefaultNode() + require.NoError(c.t, err) + currentHeight, err := node.QueryCurrentHeight() + require.NoError(c.t, err) + c.WaitUntilHeight(currentHeight + heightsToWait) +} + +func (c *Config) SendIBC(dstChain *Config, recipient string, token sdk.Coin) { + c.t.Logf("IBC sending %s from %s to %s (%s)", token, c.Id, dstChain.Id, recipient) + + dstNode, err := dstChain.GetDefaultNode() + require.NoError(c.t, err) + + balancesDstPre, err := dstNode.QueryBalances(recipient) + require.NoError(c.t, err) + + cmd := []string{"hermes", "tx", "raw", "ft-transfer", dstChain.Id, c.Id, "transfer", "channel-0", token.Amount.String(), fmt.Sprintf("--denom=%s", token.Denom), fmt.Sprintf("--receiver=%s", recipient), "--timeout-height-offset=1000"} + _, _, err = c.containerManager.ExecHermesCmd(c.t, cmd, "Success") + require.NoError(c.t, err) + + require.Eventually( + c.t, + func() bool { + balancesDstPost, err := dstNode.QueryBalances(recipient) + require.NoError(c.t, err) + ibcCoin := balancesDstPost.Sub(balancesDstPre...) + if ibcCoin.Len() == 1 { + tokenPre := balancesDstPre.AmountOfNoDenomValidation(ibcCoin[0].Denom) + tokenPost := balancesDstPost.AmountOfNoDenomValidation(ibcCoin[0].Denom) + resPre := token.Amount + resPost := tokenPost.Sub(tokenPre) + return resPost.Uint64() == resPre.Uint64() + } else { + return false + } + }, + 5*time.Minute, + time.Second, + "tx not received on destination chain", + ) + + c.t.Log("successfully sent IBC tokens") +} + +// GetDefaultNode returns the default node of the chain. +// The default node is the first one created. Returns error if no +// ndoes created. +func (c *Config) GetDefaultNode() (*NodeConfig, error) { + return c.getNodeAtIndex(defaultNodeIndex) +} + +// GetPersistentPeers returns persistent peers from every node +// associated with a chain. +func (c *Config) GetPersistentPeers() []string { + peers := make([]string, len(c.NodeConfigs)) + for i, node := range c.NodeConfigs { + peers[i] = node.PeerId + } + return peers +} + +func (c *Config) getNodeAtIndex(nodeIndex int) (*NodeConfig, error) { + if nodeIndex > len(c.NodeConfigs) { + return nil, fmt.Errorf("node index (%d) is greter than the number of nodes available (%d)", nodeIndex, len(c.NodeConfigs)) + } + return c.NodeConfigs[nodeIndex], nil +} diff --git a/test/e2e/configurer/chain/commands.go b/test/e2e/configurer/chain/commands.go new file mode 100644 index 000000000..87e27f02c --- /dev/null +++ b/test/e2e/configurer/chain/commands.go @@ -0,0 +1,40 @@ +package chain + +import ( + "encoding/json" + "fmt" + + "github.com/stretchr/testify/require" +) + +// TODO for now all commands are not used and left here as an example +// QueryParams extracts the params for a given subspace and key. This is done generically via json to avoid having to +// specify the QueryParamResponse type (which may not exist for all params). +func (n *NodeConfig) QueryParams(subspace, key string, result any) { + cmd := []string{"babylond", "query", "params", "subspace", subspace, key, "--output=json"} + + out, _, err := n.containerManager.ExecCmd(n.t, n.Name, cmd, "") + require.NoError(n.t, err) + + err = json.Unmarshal(out.Bytes(), &result) + require.NoError(n.t, err) +} + +func (n *NodeConfig) FailIBCTransfer(from, recipient, amount string) { + n.LogActionF("IBC sending %s from %s to %s", amount, from, recipient) + + cmd := []string{"babylond", "tx", "ibc-transfer", "transfer", "transfer", "channel-0", recipient, amount, fmt.Sprintf("--from=%s", from)} + + _, _, err := n.containerManager.ExecTxCmdWithSuccessString(n.t, n.chainId, n.Name, cmd, "rate limit exceeded") + require.NoError(n.t, err) + + n.LogActionF("Failed to send IBC transfer (as expected)") +} + +func (n *NodeConfig) BankSend(amount string, sendAddress string, receiveAddress string) { + n.LogActionF("bank sending %s from address %s to %s", amount, sendAddress, receiveAddress) + cmd := []string{"babylond", "tx", "bank", "send", sendAddress, receiveAddress, amount, "--from=val"} + _, _, err := n.containerManager.ExecTxCmd(n.t, n.chainId, n.Name, cmd) + require.NoError(n.t, err) + n.LogActionF("successfully sent bank sent %s from address %s to %s", amount, sendAddress, receiveAddress) +} diff --git a/test/e2e/configurer/chain/node.go b/test/e2e/configurer/chain/node.go new file mode 100644 index 000000000..a98e3989c --- /dev/null +++ b/test/e2e/configurer/chain/node.go @@ -0,0 +1,145 @@ +package chain + +import ( + "context" + "fmt" + "regexp" + "strings" + "testing" + "time" + + "github.com/stretchr/testify/require" + rpchttp "github.com/tendermint/tendermint/rpc/client/http" + coretypes "github.com/tendermint/tendermint/rpc/core/types" + + "github.com/babylonchain/babylon/test/e2e/containers" + "github.com/babylonchain/babylon/test/e2e/initialization" +) + +type NodeConfig struct { + initialization.Node + + OperatorAddress string + SnapshotInterval uint64 + chainId string + rpcClient *rpchttp.HTTP + t *testing.T + containerManager *containers.Manager + + // Add this to help with logging / tracking time since start. + setupTime time.Time +} + +// NewNodeConfig returens new initialized NodeConfig. +func NewNodeConfig(t *testing.T, initNode *initialization.Node, initConfig *initialization.NodeConfig, chainId string, containerManager *containers.Manager) *NodeConfig { + return &NodeConfig{ + Node: *initNode, + SnapshotInterval: initConfig.SnapshotInterval, + chainId: chainId, + containerManager: containerManager, + t: t, + setupTime: time.Now(), + } +} + +// Run runs a node container for the given nodeIndex. +// The node configuration must be already added to the chain config prior to calling this +// method. +func (n *NodeConfig) Run() error { + n.t.Logf("starting node container: %s", n.Name) + resource, err := n.containerManager.RunNodeResource(n.chainId, n.Name, n.ConfigDir) + if err != nil { + return err + } + + hostPort := resource.GetHostPort("26657/tcp") + rpcClient, err := rpchttp.New("tcp://"+hostPort, "/websocket") + if err != nil { + return err + } + + n.rpcClient = rpcClient + + require.Eventually( + n.t, + func() bool { + // This fails if unsuccessful. + _, err := n.QueryCurrentHeight() + if err != nil { + return false + } + n.t.Logf("started node container: %s", n.Name) + return true + }, + 2*time.Minute, + time.Second, + "bbn node failed to produce blocks", + ) + + if err := n.extractOperatorAddressIfValidator(); err != nil { + return err + } + + return nil +} + +// Stop stops the node from running and removes its container. +func (n *NodeConfig) Stop() error { + n.t.Logf("stopping node container: %s", n.Name) + if err := n.containerManager.RemoveNodeResource(n.Name); err != nil { + return err + } + n.t.Logf("stopped node container: %s", n.Name) + return nil +} + +// WaitUntil waits until node reaches doneCondition. Return nil +// if reached, error otherwise. +func (n *NodeConfig) WaitUntil(doneCondition func(syncInfo coretypes.SyncInfo) bool) { + var latestBlockHeight int64 + for i := 0; i < waitUntilrepeatMax; i++ { + status, err := n.rpcClient.Status(context.Background()) + require.NoError(n.t, err) + latestBlockHeight = status.SyncInfo.LatestBlockHeight + // let the node produce a few blocks + if !doneCondition(status.SyncInfo) { + time.Sleep(waitUntilRepeatPauseTime) + continue + } + return + } + n.t.Errorf("node %s timed out waiting for condition, latest block height was %d", n.Name, latestBlockHeight) +} + +func (n *NodeConfig) extractOperatorAddressIfValidator() error { + if !n.IsValidator { + n.t.Logf("node (%s) is not a validator, skipping", n.Name) + return nil + } + + cmd := []string{"./babylond", "debug", "addr", n.PublicKey} + n.t.Logf("extracting validator operator addresses for validator: %s", n.Name) + _, errBuf, err := n.containerManager.ExecCmd(n.t, n.Name, cmd, "") + if err != nil { + return err + } + re := regexp.MustCompile("bbnvaloper(.{39})") + operAddr := fmt.Sprintf("%s\n", re.FindString(errBuf.String())) + n.OperatorAddress = strings.TrimSuffix(operAddr, "\n") + return nil +} + +func (n *NodeConfig) GetHostPort(portId string) (string, error) { + return n.containerManager.GetHostPort(n.Name, portId) +} + +func (n *NodeConfig) WithSetupTime(t time.Time) *NodeConfig { + n.setupTime = t + return n +} + +func (n *NodeConfig) LogActionF(msg string, args ...interface{}) { + timeSinceStart := time.Since(n.setupTime).Round(time.Millisecond) + s := fmt.Sprintf(msg, args...) + n.t.Logf("[%s] %s. From container %s", timeSinceStart, s, n.Name) +} diff --git a/test/e2e/configurer/chain/queries.go b/test/e2e/configurer/chain/queries.go new file mode 100644 index 000000000..ab869a686 --- /dev/null +++ b/test/e2e/configurer/chain/queries.go @@ -0,0 +1,131 @@ +package chain + +import ( + "context" + "encoding/json" + "fmt" + "io" + "net/http" + "time" + + sdkmath "cosmossdk.io/math" + + sdk "github.com/cosmos/cosmos-sdk/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/stretchr/testify/require" + tmabcitypes "github.com/tendermint/tendermint/abci/types" + + "github.com/babylonchain/babylon/test/e2e/util" +) + +func (n *NodeConfig) QueryGRPCGateway(path string, parameters ...string) ([]byte, error) { + if len(parameters)%2 != 0 { + return nil, fmt.Errorf("invalid number of parameters, must follow the format of key + value") + } + + // add the URL for the given validator ID, and pre-pend to to path. + hostPort, err := n.containerManager.GetHostPort(n.Name, "1317/tcp") + require.NoError(n.t, err) + endpoint := fmt.Sprintf("http://%s", hostPort) + fullQueryPath := fmt.Sprintf("%s/%s", endpoint, path) + + var resp *http.Response + require.Eventually(n.t, func() bool { + req, err := http.NewRequest("GET", fullQueryPath, nil) + if err != nil { + return false + } + + if len(parameters) > 0 { + q := req.URL.Query() + for i := 0; i < len(parameters); i += 2 { + q.Add(parameters[i], parameters[i+1]) + } + req.URL.RawQuery = q.Encode() + } + + resp, err = http.DefaultClient.Do(req) + if err != nil { + n.t.Logf("error while executing HTTP request: %s", err.Error()) + return false + } + + return resp.StatusCode != http.StatusServiceUnavailable + }, time.Minute, time.Millisecond*10, "failed to execute HTTP request") + + defer resp.Body.Close() + + bz, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("unexpected status code: %d, body: %s", resp.StatusCode, string(bz)) + } + return bz, nil +} + +// QueryBalancer returns balances at the address. +func (n *NodeConfig) QueryBalances(address string) (sdk.Coins, error) { + path := fmt.Sprintf("cosmos/bank/v1beta1/balances/%s", address) + bz, err := n.QueryGRPCGateway(path) + require.NoError(n.t, err) + + var balancesResp banktypes.QueryAllBalancesResponse + if err := util.Cdc.UnmarshalJSON(bz, &balancesResp); err != nil { + return sdk.Coins{}, err + } + return balancesResp.GetBalances(), nil +} + +func (n *NodeConfig) QuerySupplyOf(denom string) (sdkmath.Int, error) { + path := fmt.Sprintf("cosmos/bank/v1beta1/supply/%s", denom) + bz, err := n.QueryGRPCGateway(path) + require.NoError(n.t, err) + + var supplyResp banktypes.QuerySupplyOfResponse + if err := util.Cdc.UnmarshalJSON(bz, &supplyResp); err != nil { + return sdk.NewInt(0), err + } + return supplyResp.Amount.Amount, nil +} + +// QueryHashFromBlock gets block hash at a specific height. Otherwise, error. +func (n *NodeConfig) QueryHashFromBlock(height int64) (string, error) { + block, err := n.rpcClient.Block(context.Background(), &height) + if err != nil { + return "", err + } + return block.BlockID.Hash.String(), nil +} + +// QueryCurrentHeight returns the current block height of the node or error. +func (n *NodeConfig) QueryCurrentHeight() (int64, error) { + status, err := n.rpcClient.Status(context.Background()) + if err != nil { + return 0, err + } + return status.SyncInfo.LatestBlockHeight, nil +} + +// QueryLatestBlockTime returns the latest block time. +func (n *NodeConfig) QueryLatestBlockTime() time.Time { + status, err := n.rpcClient.Status(context.Background()) + require.NoError(n.t, err) + return status.SyncInfo.LatestBlockTime +} + +// QueryListSnapshots gets all snapshots currently created for a node. +func (n *NodeConfig) QueryListSnapshots() ([]*tmabcitypes.Snapshot, error) { + abciResponse, err := n.rpcClient.ABCIQuery(context.Background(), "/app/snapshots", nil) + if err != nil { + return nil, err + } + + var listSnapshots tmabcitypes.ResponseListSnapshots + if err := json.Unmarshal(abciResponse.Response.Value, &listSnapshots); err != nil { + return nil, err + } + + return listSnapshots.Snapshots, nil +} diff --git a/test/e2e/configurer/config/constants.go b/test/e2e/configurer/config/constants.go new file mode 100644 index 000000000..f399c94c3 --- /dev/null +++ b/test/e2e/configurer/config/constants.go @@ -0,0 +1,16 @@ +package config + +const ( + // if not skipping upgrade, how many blocks we allow for fork to run pre upgrade state creation + ForkHeightPreUpgradeOffset int64 = 60 + // estimated number of blocks it takes to submit for a proposal + PropSubmitBlocks float32 = 10 + // estimated number of blocks it takes to deposit for a proposal + PropDepositBlocks float32 = 10 + // number of blocks it takes to vote for a single validator to vote for a proposal + PropVoteBlocks float32 = 1.2 + // number of blocks used as a calculation buffer + PropBufferBlocks float32 = 6 + // max retries for json unmarshalling + MaxRetries = 60 +) diff --git a/test/e2e/configurer/current.go b/test/e2e/configurer/current.go new file mode 100644 index 000000000..32572e12e --- /dev/null +++ b/test/e2e/configurer/current.go @@ -0,0 +1,64 @@ +package configurer + +import ( + "os" + "testing" + "time" + + "github.com/babylonchain/babylon/test/e2e/configurer/chain" + "github.com/babylonchain/babylon/test/e2e/containers" + "github.com/babylonchain/babylon/test/e2e/initialization" +) + +type CurrentBranchConfigurer struct { + baseConfigurer +} + +var _ Configurer = (*CurrentBranchConfigurer)(nil) + +func NewCurrentBranchConfigurer(t *testing.T, chainConfigs []*chain.Config, setupTests setupFn, containerManager *containers.Manager) Configurer { + return &CurrentBranchConfigurer{ + baseConfigurer: baseConfigurer{ + chainConfigs: chainConfigs, + containerManager: containerManager, + setupTests: setupTests, + syncUntilHeight: defaultSyncUntilHeight, + t: t, + }, + } +} + +func (cb *CurrentBranchConfigurer) ConfigureChains() error { + cb.t.Logf("Current branch configure chains") + for _, chainConfig := range cb.chainConfigs { + if err := cb.ConfigureChain(chainConfig); err != nil { + return err + } + } + return nil +} + +func (cb *CurrentBranchConfigurer) ConfigureChain(chainConfig *chain.Config) error { + cb.t.Logf("starting e2e infrastructure from current branch for chain-id: %s", chainConfig.Id) + tmpDir, err := os.MkdirTemp("", "bbn-e2e-testnet-") + if err != nil { + return err + } + cb.t.Logf("temp directory for chain-id %v: %v", chainConfig.Id, tmpDir) + + initializedChain, err := initialization.InitChain( + chainConfig.Id, + tmpDir, + chainConfig.ValidatorInitConfigs, + time.Duration(chainConfig.VotingPeriod*1000000000), + time.Duration(chainConfig.ExpeditedVotingPeriod*1000000000), 0) + if err != nil { + return err + } + cb.initializeChainConfigFromInitChain(initializedChain, chainConfig) + return nil +} + +func (cb *CurrentBranchConfigurer) RunSetup() error { + return cb.setupTests(cb) +} diff --git a/test/e2e/configurer/factory.go b/test/e2e/configurer/factory.go new file mode 100644 index 000000000..8a587d1b4 --- /dev/null +++ b/test/e2e/configurer/factory.go @@ -0,0 +1,111 @@ +package configurer + +import ( + "testing" + + "github.com/babylonchain/babylon/test/e2e/configurer/chain" + "github.com/babylonchain/babylon/test/e2e/containers" + "github.com/babylonchain/babylon/test/e2e/initialization" +) + +type Configurer interface { + ConfigureChains() error + + ClearResources() error + + GetChainConfig(chainIndex int) *chain.Config + + RunSetup() error + + RunValidators() error + + RunIBC() error +} + +var ( + // Last nodes are non validator nodes to serve as the ones using relayer. Out + // validators are constantly sending bls transactions which make relayer operatrions + // fail constantly + + // each started validator container corresponds to one of + // the configurations below. + validatorConfigsChainA = []*initialization.NodeConfig{ + { + // this is a node that is used to state-sync from so its snapshot-interval + // is frequent. + Name: "babylon-default-a-1", + Pruning: "default", + PruningKeepRecent: "0", + PruningInterval: "0", + SnapshotInterval: 25, + SnapshotKeepRecent: 10, + IsValidator: true, + }, + { + Name: "babylon-default-a-2", + Pruning: "nothing", + PruningKeepRecent: "0", + PruningInterval: "0", + SnapshotInterval: 1500, + SnapshotKeepRecent: 2, + IsValidator: true, + }, + { + Name: "babylon-default-a-3", + Pruning: "nothing", + PruningKeepRecent: "0", + PruningInterval: "0", + SnapshotInterval: 1500, + SnapshotKeepRecent: 2, + IsValidator: false, + }, + } + validatorConfigsChainB = []*initialization.NodeConfig{ + { + Name: "babylon-default-b-1", + Pruning: "default", + PruningKeepRecent: "0", + PruningInterval: "0", + SnapshotInterval: 1500, + SnapshotKeepRecent: 2, + IsValidator: true, + }, + { + Name: "babylon-default-b-2", + Pruning: "nothing", + PruningKeepRecent: "0", + PruningInterval: "0", + SnapshotInterval: 1500, + SnapshotKeepRecent: 2, + IsValidator: true, + }, + { + Name: "babylon-default-b-3", + Pruning: "nothing", + PruningKeepRecent: "0", + PruningInterval: "0", + SnapshotInterval: 1500, + SnapshotKeepRecent: 2, + IsValidator: false, + }, + } +) + +// New returns a new Configurer. +// TODO currently only one configuration is available. Consider testing upgrades +// when necessary +func New(t *testing.T, isDebugLogEnabled bool) (Configurer, error) { + containerManager, err := containers.NewManager(isDebugLogEnabled) + if err != nil { + return nil, err + } + + return NewCurrentBranchConfigurer(t, + []*chain.Config{ + chain.New(t, containerManager, initialization.ChainAID, validatorConfigsChainA), + chain.New(t, containerManager, initialization.ChainBID, validatorConfigsChainB), + }, + withIBC(baseSetup), // base set up with IBC + containerManager, + ), nil +} diff --git a/test/e2e/configurer/setup.go b/test/e2e/configurer/setup.go new file mode 100644 index 000000000..4eea29e7d --- /dev/null +++ b/test/e2e/configurer/setup.go @@ -0,0 +1,24 @@ +package configurer + +type setupFn func(configurer Configurer) error + +func baseSetup(configurer Configurer) error { + if err := configurer.RunValidators(); err != nil { + return err + } + return nil +} + +func withIBC(setupHandler setupFn) setupFn { + return func(configurer Configurer) error { + if err := setupHandler(configurer); err != nil { + return err + } + + if err := configurer.RunIBC(); err != nil { + return err + } + + return nil + } +} diff --git a/test/e2e/containers/config.go b/test/e2e/containers/config.go new file mode 100644 index 000000000..1340d70fb --- /dev/null +++ b/test/e2e/containers/config.go @@ -0,0 +1,31 @@ +package containers + +// ImageConfig contains all images and their respective tags +// needed for running e2e tests. +type ImageConfig struct { + RelayerRepository string + RelayerTag string +} + +//nolint:deadcode +const ( + // name of babylon container produced by running `make localnet-build-env` + BabylonContainerName = "babylonchain/babylond" + + // TODO currently using image hosted by osmolab we should probably use our own + // Hermes repo/version for relayer + relayerRepository = "osmolabs/hermes" + relayerTag = "0.13.0" +) + +// Returns ImageConfig needed for running e2e test. +// If isUpgrade is true, returns images for running the upgrade +// If isFork is true, utilizes provided fork height to initiate fork logic +func NewImageConfig() ImageConfig { + config := ImageConfig{ + RelayerRepository: relayerRepository, + RelayerTag: relayerTag, + } + + return config +} diff --git a/test/e2e/containers/containers.go b/test/e2e/containers/containers.go new file mode 100644 index 000000000..622326f67 --- /dev/null +++ b/test/e2e/containers/containers.go @@ -0,0 +1,286 @@ +package containers + +import ( + "bytes" + "context" + "fmt" + "regexp" + "strings" + "testing" + "time" + + "github.com/ory/dockertest/v3" + "github.com/ory/dockertest/v3/docker" + "github.com/stretchr/testify/require" +) + +const ( + hermesContainerName = "hermes-relayer" + // The maximum number of times debug logs are printed to console + // per CLI command. + maxDebugLogsPerCommand = 3 +) + +var errRegex = regexp.MustCompile(`(E|e)rror`) + +// Manager is a wrapper around all Docker instances, and the Docker API. +// It provides utilities to run and interact with all Docker containers used within e2e testing. +type Manager struct { + ImageConfig + pool *dockertest.Pool + network *dockertest.Network + resources map[string]*dockertest.Resource + isDebugLogEnabled bool +} + +// NewManager creates a new Manager instance and initializes +// all Docker specific utilies. Returns an error if initialiation fails. +func NewManager(isDebugLogEnabled bool) (docker *Manager, err error) { + docker = &Manager{ + ImageConfig: NewImageConfig(), + resources: make(map[string]*dockertest.Resource), + isDebugLogEnabled: isDebugLogEnabled, + } + docker.pool, err = dockertest.NewPool("") + if err != nil { + return nil, err + } + docker.network, err = docker.pool.CreateNetwork("bbn-testnet") + if err != nil { + return nil, err + } + return docker, nil +} + +// ExecTxCmd Runs ExecTxCmdWithSuccessString searching for `code: 0` +func (m *Manager) ExecTxCmd(t *testing.T, chainId string, containerName string, command []string) (bytes.Buffer, bytes.Buffer, error) { + return m.ExecTxCmdWithSuccessString(t, chainId, containerName, command, "code: 0") +} + +// ExecTxCmdWithSuccessString Runs ExecCmd, with flags for txs added. +// namely adding flags `--chain-id={chain-id} -b=block --yes --keyring-backend=test "--log_format=json"`, +// and searching for `successStr` +func (m *Manager) ExecTxCmdWithSuccessString(t *testing.T, chainId string, containerName string, command []string, successStr string) (bytes.Buffer, bytes.Buffer, error) { + allTxArgs := []string{fmt.Sprintf("--chain-id=%s", chainId), "-b=block", "--yes", "--keyring-backend=test", "--log_format=json"} + txCommand := append(command, allTxArgs...) + return m.ExecCmd(t, containerName, txCommand, successStr) +} + +// ExecHermesCmd executes command on the hermes relaer container. +func (m *Manager) ExecHermesCmd(t *testing.T, command []string, success string) (bytes.Buffer, bytes.Buffer, error) { + return m.ExecCmd(t, hermesContainerName, command, success) +} + +// ExecCmd executes command by running it on the node container (specified by containerName) +// success is the output of the command that needs to be observed for the command to be deemed successful. +// It is found by checking if stdout or stderr contains the success string anywhere within it. +// returns container std out, container std err, and error if any. +// An error is returned if the command fails to execute or if the success string is not found in the output. +func (m *Manager) ExecCmd(t *testing.T, containerName string, command []string, success string) (bytes.Buffer, bytes.Buffer, error) { + if _, ok := m.resources[containerName]; !ok { + return bytes.Buffer{}, bytes.Buffer{}, fmt.Errorf("no resource %s found", containerName) + } + containerId := m.resources[containerName].Container.ID + + var ( + outBuf bytes.Buffer + errBuf bytes.Buffer + ) + + ctx, cancel := context.WithTimeout(context.Background(), 3*time.Minute) + defer cancel() + + if m.isDebugLogEnabled { + t.Logf("\n\nRunning: \"%s\", success condition is \"%s\"", command, success) + } + maxDebugLogTriesLeft := maxDebugLogsPerCommand + + // We use the `require.Eventually` function because it is only allowed to do one transaction per block without + // sequence numbers. For simplicity, we avoid keeping track of the sequence number and just use the `require.Eventually`. + require.Eventually( + t, + func() bool { + exec, err := m.pool.Client.CreateExec(docker.CreateExecOptions{ + Context: ctx, + AttachStdout: true, + AttachStderr: true, + Container: containerId, + User: "root", + Cmd: command, + }) + require.NoError(t, err) + + err = m.pool.Client.StartExec(exec.ID, docker.StartExecOptions{ + Context: ctx, + Detach: false, + OutputStream: &outBuf, + ErrorStream: &errBuf, + }) + if err != nil { + return false + } + + errBufString := errBuf.String() + // Note that this does not match all errors. + // This only works if CLI outpurs "Error" or "error" + // to stderr. + if (errRegex.MatchString(errBufString) || m.isDebugLogEnabled) && maxDebugLogTriesLeft > 0 { + t.Log("\nstderr:") + t.Log(errBufString) + + t.Log("\nstdout:") + t.Log(outBuf.String()) + // N.B: We should not be returning false here + // because some applications such as Hermes might log + // "error" to stderr when they function correctly, + // causing test flakiness. This log is needed only for + // debugging purposes. + maxDebugLogTriesLeft-- + } + + if success != "" { + return strings.Contains(outBuf.String(), success) || strings.Contains(errBufString, success) + } + + return true + }, + time.Minute, + 50*time.Millisecond, + "tx returned a non-zero code", + ) + + return outBuf, errBuf, nil +} + +// RunHermesResource runs a Hermes container. Returns the container resource and error if any. +// the name of the hermes container is "--relayer" +func (m *Manager) RunHermesResource(chainAID, osmoARelayerNodeName, osmoAValMnemonic, chainBID, osmoBRelayerNodeName, osmoBValMnemonic string, hermesCfgPath string) (*dockertest.Resource, error) { + hermesResource, err := m.pool.RunWithOptions( + &dockertest.RunOptions{ + Name: hermesContainerName, + Repository: m.RelayerRepository, + Tag: m.RelayerTag, + NetworkID: m.network.Network.ID, + Cmd: []string{ + "start", + }, + User: "root:root", + Mounts: []string{ + fmt.Sprintf("%s/:/root/hermes", hermesCfgPath), + }, + ExposedPorts: []string{ + "3031", + }, + PortBindings: map[docker.Port][]docker.PortBinding{ + "3031/tcp": {{HostIP: "", HostPort: "3031"}}, + }, + Env: []string{ + fmt.Sprintf("BBN_A_E2E_CHAIN_ID=%s", chainAID), + fmt.Sprintf("BBN_B_E2E_CHAIN_ID=%s", chainBID), + fmt.Sprintf("BBN_A_E2E_VAL_MNEMONIC=%s", osmoAValMnemonic), + fmt.Sprintf("BBN_B_E2E_VAL_MNEMONIC=%s", osmoBValMnemonic), + fmt.Sprintf("BBN_A_E2E_VAL_HOST=%s", osmoARelayerNodeName), + fmt.Sprintf("BBN_B_E2E_VAL_HOST=%s", osmoBRelayerNodeName), + }, + Entrypoint: []string{ + "sh", + "-c", + "chmod +x /root/hermes/hermes_bootstrap.sh && /root/hermes/hermes_bootstrap.sh", + }, + }, + noRestart, + ) + if err != nil { + return nil, err + } + m.resources[hermesContainerName] = hermesResource + return hermesResource, nil +} + +// RunNodeResource runs a node container. Assings containerName to the container. +// Mounts the container on valConfigDir volume on the running host. Returns the container resource and error if any. +func (m *Manager) RunNodeResource(chainId string, containerName, valCondifDir string) (*dockertest.Resource, error) { + runOpts := &dockertest.RunOptions{ + Name: containerName, + Repository: BabylonContainerName, + NetworkID: m.network.Network.ID, + User: "root:root", + Cmd: []string{"start"}, + Mounts: []string{ + fmt.Sprintf("%s/:/data/node0/babylond", valCondifDir), + }, + } + + resource, err := m.pool.RunWithOptions(runOpts, noRestart) + if err != nil { + return nil, err + } + + m.resources[containerName] = resource + + return resource, nil +} + +// PurgeResource purges the container resource and returns an error if any. +func (m *Manager) PurgeResource(resource *dockertest.Resource) error { + return m.pool.Purge(resource) +} + +// GetNodeResource returns the node resource for containerName. +func (m *Manager) GetNodeResource(containerName string) (*dockertest.Resource, error) { + resource, exists := m.resources[containerName] + if !exists { + return nil, fmt.Errorf("node resource not found: container name: %s", containerName) + } + return resource, nil +} + +// GetHostPort returns the port-forwarding address of the running host +// necessary to connect to the portId exposed inside the container. +// The container is determined by containerName. +// Returns the host-port or error if any. +func (m *Manager) GetHostPort(containerName string, portId string) (string, error) { + resource, err := m.GetNodeResource(containerName) + if err != nil { + return "", err + } + return resource.GetHostPort(portId), nil +} + +// RemoveNodeResource removes a node container specified by containerName. +// Returns error if any. +func (m *Manager) RemoveNodeResource(containerName string) error { + resource, err := m.GetNodeResource(containerName) + if err != nil { + return err + } + var opts docker.RemoveContainerOptions + opts.ID = resource.Container.ID + opts.Force = true + if err := m.pool.Client.RemoveContainer(opts); err != nil { + return err + } + delete(m.resources, containerName) + return nil +} + +// ClearResources removes all outstanding Docker resources created by the Manager. +func (m *Manager) ClearResources() error { + for _, resource := range m.resources { + if err := m.pool.Purge(resource); err != nil { + return err + } + } + + if err := m.pool.RemoveNetwork(m.network); err != nil { + return err + } + return nil +} + +func noRestart(config *docker.HostConfig) { + // in this case we don't want the nodes to restart on failure + config.RestartPolicy = docker.RestartPolicy{ + Name: "no", + } +} diff --git a/test/e2e/e2e_setup_test.go b/test/e2e/e2e_setup_test.go new file mode 100644 index 000000000..3a0113f69 --- /dev/null +++ b/test/e2e/e2e_setup_test.go @@ -0,0 +1,49 @@ +package e2e + +import ( + "testing" + + "github.com/stretchr/testify/suite" + + configurer "github.com/babylonchain/babylon/test/e2e/configurer" +) + +type IntegrationTestSuite struct { + suite.Suite + + configurer configurer.Configurer +} + +func TestIntegrationTestSuite(t *testing.T) { + suite.Run(t, new(IntegrationTestSuite)) +} + +func (s *IntegrationTestSuite) SetupSuite() { + s.T().Log("setting up e2e integration test suite...") + var ( + err error + ) + + // The e2e test flow is as follows: + // + // 1. Configure two chains - chan A and chain B. + // * For each chain, set up several validator nodes + // * Initialize configs and genesis for all them. + // 2. Start both networks. + // 3. Run IBC relayer betweeen the two chains. + // 4. Execute various e2e tests, including IBC + s.configurer, err = configurer.New(s.T(), true) + + s.Require().NoError(err) + + err = s.configurer.ConfigureChains() + s.Require().NoError(err) + + err = s.configurer.RunSetup() + s.Require().NoError(err) +} + +func (s *IntegrationTestSuite) TearDownSuite() { + err := s.configurer.ClearResources() + s.Require().NoError(err) +} diff --git a/test/e2e/e2e_test.go b/test/e2e/e2e_test.go new file mode 100644 index 000000000..d9473d884 --- /dev/null +++ b/test/e2e/e2e_test.go @@ -0,0 +1,15 @@ +//go:build e2e +// +build e2e + +package e2e + +// Most simple test, just checking that two chains are up and connected through +// ibc +func (s *IntegrationTestSuite) TestConnectIbc() { + chainA := s.configurer.GetChainConfig(0) + chainB := s.configurer.GetChainConfig(1) + _, err := chainA.GetDefaultNode() + s.NoError(err) + _, err = chainB.GetDefaultNode() + s.NoError(err) +} diff --git a/test/e2e/initialization/README.md b/test/e2e/initialization/README.md new file mode 100644 index 000000000..884b58a5a --- /dev/null +++ b/test/e2e/initialization/README.md @@ -0,0 +1,8 @@ +# initializaion package + +## Motivation + +This package contains all logic necessary for initializing configuration +data either for a new chain or a single node via Docker containers. + +Heavily based on Osmois init pacakage. diff --git a/test/e2e/initialization/chain.go b/test/e2e/initialization/chain.go new file mode 100644 index 000000000..690503b78 --- /dev/null +++ b/test/e2e/initialization/chain.go @@ -0,0 +1,35 @@ +package initialization + +const ( + keyringPassphrase = "testpassphrase" + keyringAppName = "testnet" +) + +// internalChain contains the same info as chain, but with the validator structs instead using the internal validator +// representation, with more derived data +type internalChain struct { + chainMeta ChainMeta + nodes []*internalNode +} + +func new(id, dataDir string) (*internalChain, error) { + chainMeta := ChainMeta{ + Id: id, + DataDir: dataDir, + } + return &internalChain{ + chainMeta: chainMeta, + }, nil +} + +func (c *internalChain) export() *Chain { + exportNodes := make([]*Node, 0, len(c.nodes)) + for _, v := range c.nodes { + exportNodes = append(exportNodes, v.export()) + } + + return &Chain{ + ChainMeta: c.chainMeta, + Nodes: exportNodes, + } +} diff --git a/test/e2e/initialization/chain/main.go b/test/e2e/initialization/chain/main.go new file mode 100644 index 000000000..074b7968f --- /dev/null +++ b/test/e2e/initialization/chain/main.go @@ -0,0 +1,56 @@ +package main + +import ( + "encoding/json" + "flag" + "fmt" + "os" + "time" + + "github.com/babylonchain/babylon/test/e2e/initialization" +) + +func main() { + var ( + valConfig []*initialization.NodeConfig + dataDir string + chainId string + config string + votingPeriod time.Duration + expeditedVotingPeriod time.Duration + forkHeight int + ) + + flag.StringVar(&dataDir, "data-dir", "", "chain data directory") + flag.StringVar(&chainId, "chain-id", "", "chain ID") + flag.StringVar(&config, "config", "", "serialized config") + flag.DurationVar(&votingPeriod, "voting-period", 30000000000, "voting period") + flag.DurationVar(&expeditedVotingPeriod, "expedited-voting-period", 20000000000, "expedited voting period") + flag.IntVar(&forkHeight, "fork-height", 0, "fork height") + + flag.Parse() + + err := json.Unmarshal([]byte(config), &valConfig) + if err != nil { + panic(err) + } + + if len(dataDir) == 0 { + panic("data-dir is required") + } + + if err := os.MkdirAll(dataDir, 0o755); err != nil { + panic(err) + } + + createdChain, err := initialization.InitChain(chainId, dataDir, valConfig, votingPeriod, expeditedVotingPeriod, forkHeight) + if err != nil { + panic(err) + } + + b, _ := json.Marshal(createdChain) + fileName := fmt.Sprintf("%v/%v-encode", dataDir, chainId) + if err = os.WriteFile(fileName, b, 0o777); err != nil { + panic(err) + } +} diff --git a/test/e2e/initialization/config.go b/test/e2e/initialization/config.go new file mode 100644 index 000000000..7ed392896 --- /dev/null +++ b/test/e2e/initialization/config.go @@ -0,0 +1,357 @@ +package initialization + +import ( + "encoding/json" + "fmt" + "path/filepath" + "time" + + "github.com/babylonchain/babylon/privval" + checkpointingtypes "github.com/babylonchain/babylon/x/checkpointing/types" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + ed25519 "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" + "github.com/cosmos/cosmos-sdk/server" + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + crisistypes "github.com/cosmos/cosmos-sdk/x/crisis/types" + "github.com/cosmos/cosmos-sdk/x/genutil" + genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" + staketypes "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/gogo/protobuf/proto" + tmjson "github.com/tendermint/tendermint/libs/json" + + "github.com/babylonchain/babylon/test/e2e/util" +) + +// NodeConfig is a confiuration for the node supplied from the test runner +// to initialization scripts. It should be backwards compatible with earlier +// versions. If this struct is updated, the change must be backported to earlier +// branches that might be used for upgrade testing. +type NodeConfig struct { + Name string // name of the config that will also be assigned to Docke container. + Pruning string // default, nothing, everything, or custom + PruningKeepRecent string // keep all of the last N states (only used with custom pruning) + PruningInterval string // delete old states from every Nth block (only used with custom pruning) + SnapshotInterval uint64 // statesync snapshot every Nth block (0 to disable) + SnapshotKeepRecent uint32 // number of recent snapshots to keep and serve (0 to keep all) + IsValidator bool // flag indicating whether a node should be a validator +} + +const ( + // common + BabylonDenom = "ubbn" + MinGasPrice = "0.000" + ValidatorWalletName = "val" + // chainA + ChainAID = "bbn-test-a" + BabylonBalanceA = 200000000000 + StakeAmountA = 100000000000 + // chainB + ChainBID = "bbn-test-b" + BabylonBalanceB = 500000000000 + StakeAmountB = 400000000000 + + EpochDuration = time.Second * 60 + TWAPPruningKeepPeriod = EpochDuration / 4 +) + +var ( + StakeAmountIntA = sdk.NewInt(StakeAmountA) + StakeAmountCoinA = sdk.NewCoin(BabylonDenom, StakeAmountIntA) + StakeAmountIntB = sdk.NewInt(StakeAmountB) + StakeAmountCoinB = sdk.NewCoin(BabylonDenom, StakeAmountIntB) + + InitBalanceStrA = fmt.Sprintf("%d%s", BabylonBalanceA, BabylonDenom) + InitBalanceStrB = fmt.Sprintf("%d%s", BabylonBalanceB, BabylonDenom) +) + +func addAccount(path, moniker, amountStr string, accAddr sdk.AccAddress, forkHeight int) error { + serverCtx := server.NewDefaultContext() + config := serverCtx.Config + + config.SetRoot(path) + config.Moniker = moniker + + coins, err := sdk.ParseCoinsNormalized(amountStr) + if err != nil { + return fmt.Errorf("failed to parse coins: %w", err) + } + + balances := banktypes.Balance{Address: accAddr.String(), Coins: coins.Sort()} + genAccount := authtypes.NewBaseAccount(accAddr, nil, 0, 0) + + // TODO: Make the SDK make it far cleaner to add an account to GenesisState + genFile := config.GenesisFile() + appState, genDoc, err := genutiltypes.GenesisStateFromGenFile(genFile) + if err != nil { + return fmt.Errorf("failed to unmarshal genesis state: %w", err) + } + + genDoc.InitialHeight = int64(forkHeight) + + authGenState := authtypes.GetGenesisStateFromAppState(util.Cdc, appState) + + accs, err := authtypes.UnpackAccounts(authGenState.Accounts) + if err != nil { + return fmt.Errorf("failed to get accounts from any: %w", err) + } + + if accs.Contains(accAddr) { + return fmt.Errorf("failed to add account to genesis state; account already exists: %s", accAddr) + } + + // Add the new account to the set of genesis accounts and sanitize the + // accounts afterwards. + accs = append(accs, genAccount) + accs = authtypes.SanitizeGenesisAccounts(accs) + + genAccs, err := authtypes.PackAccounts(accs) + if err != nil { + return fmt.Errorf("failed to convert accounts into any's: %w", err) + } + + authGenState.Accounts = genAccs + + authGenStateBz, err := util.Cdc.MarshalJSON(&authGenState) + if err != nil { + return fmt.Errorf("failed to marshal auth genesis state: %w", err) + } + + appState[authtypes.ModuleName] = authGenStateBz + + bankGenState := banktypes.GetGenesisStateFromAppState(util.Cdc, appState) + bankGenState.Balances = append(bankGenState.Balances, balances) + bankGenState.Balances = banktypes.SanitizeGenesisBalances(bankGenState.Balances) + + bankGenStateBz, err := util.Cdc.MarshalJSON(bankGenState) + if err != nil { + return fmt.Errorf("failed to marshal bank genesis state: %w", err) + } + + appState[banktypes.ModuleName] = bankGenStateBz + + appStateJSON, err := json.Marshal(appState) + if err != nil { + return fmt.Errorf("failed to marshal application genesis state: %w", err) + } + + genDoc.AppState = appStateJSON + return genutil.ExportGenesisFile(genDoc, genFile) +} + +//nolint:typecheck +func updateModuleGenesis[V proto.Message](appGenState map[string]json.RawMessage, moduleName string, protoVal V, updateGenesis func(V)) error { + if err := util.Cdc.UnmarshalJSON(appGenState[moduleName], protoVal); err != nil { + return err + } + updateGenesis(protoVal) + newGenState := protoVal + + bz, err := util.Cdc.MarshalJSON(newGenState) + if err != nil { + return err + } + appGenState[moduleName] = bz + return nil +} + +func initGenesis(chain *internalChain, votingPeriod, expeditedVotingPeriod time.Duration, forkHeight int) error { + // initialize a genesis file + configDir := chain.nodes[0].configDir() + + for _, val := range chain.nodes { + addr, err := val.keyInfo.GetAddress() + + if err != nil { + return err + } + + if chain.chainMeta.Id == ChainAID { + if err := addAccount(configDir, "", InitBalanceStrA, addr, forkHeight); err != nil { + return err + } + } else if chain.chainMeta.Id == ChainBID { + if err := addAccount(configDir, "", InitBalanceStrB, addr, forkHeight); err != nil { + return err + } + } + } + + // copy the genesis file to the remaining validators + for _, val := range chain.nodes[1:] { + _, err := util.CopyFile( + filepath.Join(configDir, "config", "genesis.json"), + filepath.Join(val.configDir(), "config", "genesis.json"), + ) + if err != nil { + return err + } + } + + serverCtx := server.NewDefaultContext() + config := serverCtx.Config + + config.SetRoot(chain.nodes[0].configDir()) + config.Moniker = chain.nodes[0].moniker + + genFilePath := config.GenesisFile() + appGenState, genDoc, err := genutiltypes.GenesisStateFromGenFile(genFilePath) + if err != nil { + return err + } + + err = updateModuleGenesis(appGenState, banktypes.ModuleName, &banktypes.GenesisState{}, updateBankGenesis) + if err != nil { + return err + } + + err = updateModuleGenesis(appGenState, staketypes.ModuleName, &staketypes.GenesisState{}, updateStakeGenesis) + if err != nil { + return err + } + + err = updateModuleGenesis(appGenState, crisistypes.ModuleName, &crisistypes.GenesisState{}, updateCrisisGenesis) + if err != nil { + return err + } + + err = updateModuleGenesis(appGenState, genutiltypes.ModuleName, &genutiltypes.GenesisState{}, updateGenUtilGenesis(chain)) + if err != nil { + return err + } + + err = updateModuleGenesis(appGenState, checkpointingtypes.ModuleName, checkpointingtypes.DefaultGenesis(), updateCheckpointingGenesis(chain)) + if err != nil { + return err + } + + bz, err := json.MarshalIndent(appGenState, "", " ") + if err != nil { + return err + } + + genDoc.AppState = bz + + genesisJson, err := tmjson.MarshalIndent(genDoc, "", " ") + if err != nil { + return err + } + + // write the updated genesis file to each validator + for _, val := range chain.nodes { + if err := util.WriteFile(filepath.Join(val.configDir(), "config", "genesis.json"), genesisJson); err != nil { + return err + } + } + return nil +} + +func updateBankGenesis(bankGenState *banktypes.GenesisState) { + bankGenState.DenomMetadata = append(bankGenState.DenomMetadata, banktypes.Metadata{ + Description: "An example stable token", + Display: BabylonDenom, + Base: BabylonDenom, + Symbol: BabylonDenom, + Name: BabylonDenom, + DenomUnits: []*banktypes.DenomUnit{ + { + Denom: BabylonDenom, + Exponent: 0, + }, + }, + }) +} + +func updateStakeGenesis(stakeGenState *staketypes.GenesisState) { + stakeGenState.Params = staketypes.Params{ + BondDenom: BabylonDenom, + MaxValidators: 100, + MaxEntries: 7, + HistoricalEntries: 10000, + UnbondingTime: 240000000000, + MinCommissionRate: sdk.ZeroDec(), + } +} + +func updateCrisisGenesis(crisisGenState *crisistypes.GenesisState) { + crisisGenState.ConstantFee.Denom = BabylonDenom +} + +func updateGenUtilGenesis(c *internalChain) func(*genutiltypes.GenesisState) { + return func(genUtilGenState *genutiltypes.GenesisState) { + // generate genesis txs + genTxs := make([]json.RawMessage, 0, len(c.nodes)) + for _, node := range c.nodes { + if !node.isValidator { + continue + } + + stakeAmountCoin := StakeAmountCoinA + if c.chainMeta.Id != ChainAID { + stakeAmountCoin = StakeAmountCoinB + } + createValmsg, err := node.buildCreateValidatorMsg(stakeAmountCoin) + if err != nil { + panic("genutil genesis setup failed: " + err.Error()) + } + + signedTx, err := node.signMsg(createValmsg) + if err != nil { + panic("genutil genesis setup failed: " + err.Error()) + } + + txRaw, err := util.Cdc.MarshalJSON(signedTx) + if err != nil { + panic("genutil genesis setup failed: " + err.Error()) + } + genTxs = append(genTxs, txRaw) + } + genUtilGenState.GenTxs = genTxs + } +} + +func updateCheckpointingGenesis(c *internalChain) func(*checkpointingtypes.GenesisState) { + return func(checkpointingGenState *checkpointingtypes.GenesisState) { + var genKeys []*checkpointingtypes.GenesisKey + + for _, node := range c.nodes { + if !node.isValidator { + continue + } + + proofOfPossession, err := privval.BuildPoP(node.consensusKey.PrivKey, node.consensusKey.BlsPrivKey) + + if err != nil { + panic("It should be possible to build proof of possesion from validator private keys") + } + + valPubKey, err := cryptocodec.FromTmPubKeyInterface(node.consensusKey.PubKey) + + if err != nil { + panic("It should be possible to retrieve validator public key") + } + + da, err := sdk.AccAddressFromBech32(node.consensusKey.DelegatorAddress) + + if err != nil { + panic("It should be possible to get validator address from delegator address") + } + + va := sdk.ValAddress(da) + + genKey := &checkpointingtypes.GenesisKey{ + ValidatorAddress: va.String(), + BlsKey: &checkpointingtypes.BlsKey{ + Pubkey: &node.consensusKey.BlsPubKey, + Pop: proofOfPossession, + }, + ValPubkey: valPubKey.(*ed25519.PubKey), + } + + genKeys = append(genKeys, genKey) + } + + checkpointingGenState.GenesisKeys = genKeys + } +} diff --git a/test/e2e/initialization/export.go b/test/e2e/initialization/export.go new file mode 100644 index 000000000..8e4224164 --- /dev/null +++ b/test/e2e/initialization/export.go @@ -0,0 +1,27 @@ +package initialization + +import "fmt" + +type ChainMeta struct { + DataDir string `json:"dataDir"` + Id string `json:"id"` +} + +type Node struct { + Name string `json:"name"` + ConfigDir string `json:"configDir"` + Mnemonic string `json:"mnemonic"` + PublicAddress string `json:"publicAddress"` + PublicKey string `json:"publicKey"` + PeerId string `json:"peerId"` + IsValidator bool `json:"isValidator"` +} + +type Chain struct { + ChainMeta ChainMeta `json:"chainMeta"` + Nodes []*Node `json:"validators"` +} + +func (c *ChainMeta) configDir() string { + return fmt.Sprintf("%s/%s", c.DataDir, c.Id) +} diff --git a/test/e2e/initialization/init.go b/test/e2e/initialization/init.go new file mode 100644 index 000000000..8dc02cee4 --- /dev/null +++ b/test/e2e/initialization/init.go @@ -0,0 +1,83 @@ +package initialization + +import ( + "errors" + "fmt" + "path/filepath" + "time" + + "github.com/babylonchain/babylon/app" + "github.com/babylonchain/babylon/test/e2e/util" +) + +func InitChain(id, dataDir string, nodeConfigs []*NodeConfig, votingPeriod, expeditedVotingPeriod time.Duration, forkHeight int) (*Chain, error) { + chain, err := new(id, dataDir) + if err != nil { + return nil, err + } + + for _, nodeConfig := range nodeConfigs { + newNode, err := newNode(chain, nodeConfig) + if err != nil { + return nil, err + } + chain.nodes = append(chain.nodes, newNode) + } + + if err := initGenesis(chain, votingPeriod, expeditedVotingPeriod, forkHeight); err != nil { + return nil, err + } + + var peers []string + for _, peer := range chain.nodes { + peerID := fmt.Sprintf("%s@%s:26656", peer.getNodeKey().ID(), peer.moniker) + peer.peerId = peerID + peers = append(peers, peerID) + } + + for _, node := range chain.nodes { + if err := node.initNodeConfigs(peers); err != nil { + return nil, err + } + } + + for _, node := range chain.nodes { + _, _ = app.CreateClientConfig(node.chain.chainMeta.Id, "test", node.configDir()) + } + + return chain.export(), nil +} + +func InitSingleNode(chainId, dataDir string, existingGenesisDir string, nodeConfig *NodeConfig, votingPeriod time.Duration, trustHeight int64, trustHash string, stateSyncRPCServers []string, persistentPeers []string) (*Node, error) { + if nodeConfig.IsValidator { + return nil, errors.New("creating individual validator nodes after starting up chain is not currently supported") + } + + chain, err := new(chainId, dataDir) + if err != nil { + return nil, err + } + + newNode, err := newNode(chain, nodeConfig) + if err != nil { + return nil, err + } + + _, err = util.CopyFile( + existingGenesisDir, + filepath.Join(newNode.configDir(), "config", "genesis.json"), + ) + if err != nil { + return nil, err + } + + if err := newNode.initNodeConfigs(persistentPeers); err != nil { + return nil, err + } + + if err := newNode.initStateSyncConfig(trustHeight, trustHash, stateSyncRPCServers); err != nil { + return nil, err + } + + return newNode.export(), nil +} diff --git a/test/e2e/initialization/init_test.go b/test/e2e/initialization/init_test.go new file mode 100644 index 000000000..cdebd2ef1 --- /dev/null +++ b/test/e2e/initialization/init_test.go @@ -0,0 +1,143 @@ +//go:build e2e +// +build e2e + +package initialization_test + +import ( + "fmt" + "os" + "path" + "path/filepath" + "testing" + "time" + + "github.com/stretchr/testify/require" + + "github.com/babylonchain/babylon/test/e2e/initialization" +) + +const forkHeight = 10 + +var expectedConfigFiles = []string{ + "app.toml", "config.toml", "genesis.json", "node_key.json", "priv_validator_key.json", +} + +// TestChainInit tests that chain initialization correctly initializes a full chain +// and produces the desired output with genesis, chain and validator configs. +func TestChainInit(t *testing.T) { + const id = initialization.ChainAID + + var ( + nodeConfigs = []*initialization.NodeConfig{ + { + Name: "0", + Pruning: "default", + PruningKeepRecent: "0", + PruningInterval: "0", + SnapshotInterval: 1500, + SnapshotKeepRecent: 2, + IsValidator: true, + }, + { + Name: "1", + Pruning: "nothing", + PruningKeepRecent: "0", + PruningInterval: "0", + SnapshotInterval: 100, + SnapshotKeepRecent: 1, + IsValidator: false, + }, + } + dataDir, err = os.MkdirTemp("", "bbn-e2e-testnet-test") + ) + + chain, err := initialization.InitChain(id, dataDir, nodeConfigs, time.Second*3, time.Second, forkHeight) + require.NoError(t, err) + + require.Equal(t, chain.ChainMeta.DataDir, dataDir) + require.Equal(t, chain.ChainMeta.Id, id) + + require.Equal(t, len(nodeConfigs), len(chain.Nodes)) + + actualNodes := chain.Nodes + + for i, expectedConfig := range nodeConfigs { + actualNode := actualNodes[i] + + validateNode(t, id, dataDir, expectedConfig, actualNode) + } +} + +// TestSingleNodeInit tests that node initialization correctly initializes a single node +// and produces the desired output with genesis, chain and validator config. +func TestSingleNodeInit(t *testing.T) { + const ( + id = initialization.ChainAID + ) + + var ( + existingChainNodeConfigs = []*initialization.NodeConfig{ + { + Name: "0", + Pruning: "default", + PruningKeepRecent: "0", + PruningInterval: "0", + SnapshotInterval: 1500, + SnapshotKeepRecent: 2, + IsValidator: true, + }, + { + Name: "1", + Pruning: "nothing", + PruningKeepRecent: "0", + PruningInterval: "0", + SnapshotInterval: 100, + SnapshotKeepRecent: 1, + IsValidator: true, + }, + } + expectedConfig = &initialization.NodeConfig{ + Name: "2", + Pruning: "everything", + PruningKeepRecent: "0", + PruningInterval: "0", + SnapshotInterval: 100, + SnapshotKeepRecent: 1, + IsValidator: false, + } + dataDir, err = os.MkdirTemp("", "bbn-e2e-testnet-test") + ) + + // Setup + existingChain, err := initialization.InitChain(id, dataDir, existingChainNodeConfigs, time.Second*3, time.Second, forkHeight) + require.NoError(t, err) + + actualNode, err := initialization.InitSingleNode(existingChain.ChainMeta.Id, dataDir, filepath.Join(existingChain.Nodes[0].ConfigDir, "config", "genesis.json"), expectedConfig, time.Second*3, 3, "testHash", []string{"some server"}, []string{"some server"}) + require.NoError(t, err) + + validateNode(t, id, dataDir, expectedConfig, actualNode) +} + +func validateNode(t *testing.T, chainId string, dataDir string, expectedConfig *initialization.NodeConfig, actualNode *initialization.Node) { + require.Equal(t, fmt.Sprintf("%s-node-%s", chainId, expectedConfig.Name), actualNode.Name) + require.Equal(t, expectedConfig.IsValidator, actualNode.IsValidator) + + expectedPath := fmt.Sprintf("%s/%s/%s-node-%s", dataDir, chainId, chainId, expectedConfig.Name) + + require.Equal(t, expectedPath, actualNode.ConfigDir) + + require.NotEmpty(t, actualNode.Mnemonic) + require.NotEmpty(t, actualNode.PublicAddress) + + if expectedConfig.IsValidator { + require.NotEmpty(t, actualNode.PeerId) + } + + for _, expectedFileName := range expectedConfigFiles { + expectedFilePath := path.Join(expectedPath, "config", expectedFileName) + _, err := os.Stat(expectedFilePath) + require.NoError(t, err) + } + _, err := os.Stat(path.Join(expectedPath, "keyring-test")) + require.NoError(t, err) +} diff --git a/test/e2e/initialization/node.go b/test/e2e/initialization/node.go new file mode 100644 index 000000000..617f81a71 --- /dev/null +++ b/test/e2e/initialization/node.go @@ -0,0 +1,463 @@ +package initialization + +import ( + "encoding/json" + "fmt" + "os" + "path" + "path/filepath" + "strings" + + "github.com/babylonchain/babylon/crypto/bls12381" + tmed25519 "github.com/tendermint/tendermint/crypto/ed25519" + + "github.com/babylonchain/babylon/privval" + sdkcrypto "github.com/cosmos/cosmos-sdk/crypto" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + "github.com/cosmos/cosmos-sdk/crypto/hd" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/server" + srvconfig "github.com/cosmos/cosmos-sdk/server/config" + sdk "github.com/cosmos/cosmos-sdk/types" + sdktx "github.com/cosmos/cosmos-sdk/types/tx" + txsigning "github.com/cosmos/cosmos-sdk/types/tx/signing" + authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing" + "github.com/cosmos/cosmos-sdk/x/genutil" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/cosmos/go-bip39" + "github.com/spf13/viper" + tmconfig "github.com/tendermint/tendermint/config" + tmos "github.com/tendermint/tendermint/libs/os" + "github.com/tendermint/tendermint/p2p" + tmtypes "github.com/tendermint/tendermint/types" + + "github.com/babylonchain/babylon/test/e2e/util" + + babylonApp "github.com/babylonchain/babylon/app" + "github.com/babylonchain/babylon/cmd/babylond/cmd" +) + +type internalNode struct { + chain *internalChain + moniker string + mnemonic string + keyInfo *keyring.Record + privateKey cryptotypes.PrivKey + consensusKey privval.WrappedFilePVKey + nodeKey p2p.NodeKey + peerId string + isValidator bool +} + +func newNode(chain *internalChain, nodeConfig *NodeConfig) (*internalNode, error) { + node := &internalNode{ + chain: chain, + moniker: fmt.Sprintf("%s-node-%s", chain.chainMeta.Id, nodeConfig.Name), + isValidator: nodeConfig.IsValidator, + } + // generate genesis files + if err := node.init(); err != nil { + return nil, err + } + // create keys + if err := node.createKey(ValidatorWalletName); err != nil { + return nil, err + } + if err := node.createNodeKey(); err != nil { + return nil, err + } + if err := node.createConsensusKey(); err != nil { + return nil, err + } + node.createAppConfig(nodeConfig) + return node, nil +} + +func (n *internalNode) configDir() string { + return fmt.Sprintf("%s/%s", n.chain.chainMeta.configDir(), n.moniker) +} + +func (n *internalNode) buildCreateValidatorMsg(amount sdk.Coin) (sdk.Msg, error) { + description := stakingtypes.NewDescription(n.moniker, "", "", "", "") + commissionRates := stakingtypes.CommissionRates{ + Rate: sdk.MustNewDecFromStr("0.1"), + MaxRate: sdk.MustNewDecFromStr("0.2"), + MaxChangeRate: sdk.MustNewDecFromStr("0.01"), + } + + // get the initial validator min self delegation + minSelfDelegation, _ := sdk.NewIntFromString("1") + + valPubKey, err := cryptocodec.FromTmPubKeyInterface(n.consensusKey.PubKey) + if err != nil { + return nil, err + } + + addr, err := n.keyInfo.GetAddress() + + if err != nil { + return nil, err + } + + return stakingtypes.NewMsgCreateValidator( + sdk.ValAddress(addr), + valPubKey, + amount, + description, + commissionRates, + minSelfDelegation, + ) +} + +func (n *internalNode) createConfig() error { + p := path.Join(n.configDir(), "config") + return os.MkdirAll(p, 0o755) +} + +func (n *internalNode) createAppConfig(nodeConfig *NodeConfig) { + // set application configuration + appCfgPath := filepath.Join(n.configDir(), "config", "app.toml") + + appConfig := cmd.DefaultBabylonConfig() + + appConfig.BaseConfig.Pruning = nodeConfig.Pruning + appConfig.BaseConfig.PruningKeepRecent = nodeConfig.PruningKeepRecent + appConfig.BaseConfig.PruningInterval = nodeConfig.PruningInterval + appConfig.API.Enable = true + appConfig.MinGasPrices = fmt.Sprintf("%s%s", MinGasPrice, BabylonDenom) + appConfig.StateSync.SnapshotInterval = nodeConfig.SnapshotInterval + appConfig.StateSync.SnapshotKeepRecent = nodeConfig.SnapshotKeepRecent + appConfig.SignerConfig.KeyName = ValidatorWalletName + + customTemplate := cmd.DefaultBabylonTemplate() + + srvconfig.SetConfigTemplate(customTemplate) + srvconfig.WriteConfigFile(appCfgPath, appConfig) +} + +func (n *internalNode) createNodeKey() error { + serverCtx := server.NewDefaultContext() + config := serverCtx.Config + + config.SetRoot(n.configDir()) + config.Moniker = n.moniker + + nodeKey, err := p2p.LoadOrGenNodeKey(config.NodeKeyFile()) + if err != nil { + return err + } + + n.nodeKey = *nodeKey + return nil +} + +func (n *internalNode) createConsensusKey() error { + serverCtx := server.NewDefaultContext() + config := serverCtx.Config + + config.SetRoot(n.configDir()) + config.Moniker = n.moniker + + pvKeyFile := config.PrivValidatorKeyFile() + if err := tmos.EnsureDir(filepath.Dir(pvKeyFile), 0o777); err != nil { + return err + } + + pvStateFile := config.PrivValidatorStateFile() + if err := tmos.EnsureDir(filepath.Dir(pvStateFile), 0o777); err != nil { + return err + } + + // privval.LoadOrGenWrappedFilePV() + privKey := tmed25519.GenPrivKeyFromSecret([]byte(n.mnemonic)) + blsPrivKey := bls12381.GenPrivKeyFromSecret([]byte(n.mnemonic)) + filePV := privval.NewWrappedFilePV(privKey, blsPrivKey, pvKeyFile, pvStateFile) + + accAddress, _ := n.keyInfo.GetAddress() + filePV.Save() + filePV.SetAccAddress(accAddress) + + n.consensusKey = filePV.Key + + return nil +} + +func (n *internalNode) createKeyFromMnemonic(name, mnemonic string) error { + kb, err := keyring.New(keyringAppName, keyring.BackendTest, n.configDir(), nil, util.Cdc) + + if err != nil { + return err + } + + keyringAlgos, _ := kb.SupportedAlgorithms() + algo, err := keyring.NewSigningAlgoFromString(string(hd.Secp256k1Type), keyringAlgos) + if err != nil { + return err + } + + info, err := kb.NewAccount(name, mnemonic, "", sdk.FullFundraiserPath, algo) + if err != nil { + return err + } + + privKeyArmor, err := kb.ExportPrivKeyArmor(name, keyringPassphrase) + if err != nil { + return err + } + + privKey, _, err := sdkcrypto.UnarmorDecryptPrivKey(privKeyArmor, keyringPassphrase) + if err != nil { + return err + } + + n.keyInfo = info + n.mnemonic = mnemonic + n.privateKey = privKey + + return nil +} + +func (n *internalNode) createKey(name string) error { + mnemonic, err := n.createMnemonic() + if err != nil { + return err + } + + return n.createKeyFromMnemonic(name, mnemonic) +} + +func (n *internalNode) export() *Node { + addr, err := n.keyInfo.GetAddress() + + if err != nil { + panic("address should be correct") + } + + pub, err := n.keyInfo.GetPubKey() + + if err != nil { + panic("pub key should be correct") + } + + return &Node{ + Name: n.moniker, + ConfigDir: n.configDir(), + Mnemonic: n.mnemonic, + PublicAddress: addr.String(), + PublicKey: pub.Address().String(), + PeerId: n.peerId, + IsValidator: n.isValidator, + } +} + +func (n *internalNode) getNodeKey() *p2p.NodeKey { + return &n.nodeKey +} + +func (n *internalNode) getGenesisDoc() (*tmtypes.GenesisDoc, error) { + serverCtx := server.NewDefaultContext() + config := serverCtx.Config + config.SetRoot(n.configDir()) + + genFile := config.GenesisFile() + doc := &tmtypes.GenesisDoc{} + + if _, err := os.Stat(genFile); err != nil { + if !os.IsNotExist(err) { + return nil, err + } + } else { + var err error + + doc, err = tmtypes.GenesisDocFromFile(genFile) + if err != nil { + return nil, fmt.Errorf("failed to read genesis doc from file: %w", err) + } + } + + return doc, nil +} + +func (n *internalNode) init() error { + if err := n.createConfig(); err != nil { + return err + } + + serverCtx := server.NewDefaultContext() + config := serverCtx.Config + + config.SetRoot(n.configDir()) + config.Moniker = n.moniker + + genDoc, err := n.getGenesisDoc() + if err != nil { + return err + } + + appState, err := json.MarshalIndent(babylonApp.ModuleBasics.DefaultGenesis(util.Cdc), "", " ") + if err != nil { + return fmt.Errorf("failed to JSON encode app genesis state: %w", err) + } + + genDoc.ChainID = n.chain.chainMeta.Id + genDoc.Validators = nil + genDoc.AppState = appState + + if err = genutil.ExportGenesisFile(genDoc, config.GenesisFile()); err != nil { + return fmt.Errorf("failed to export app genesis state: %w", err) + } + + tmconfig.WriteConfigFile(filepath.Join(config.RootDir, "config", "config.toml"), config) + return nil +} + +func (n *internalNode) createMnemonic() (string, error) { + entropySeed, err := bip39.NewEntropy(256) + if err != nil { + return "", err + } + + mnemonic, err := bip39.NewMnemonic(entropySeed) + if err != nil { + return "", err + } + + return mnemonic, nil +} + +func (n *internalNode) initNodeConfigs(persistentPeers []string) error { + tmCfgPath := filepath.Join(n.configDir(), "config", "config.toml") + + vpr := viper.New() + vpr.SetConfigFile(tmCfgPath) + if err := vpr.ReadInConfig(); err != nil { + return err + } + + valConfig := tmconfig.DefaultConfig() + if err := vpr.Unmarshal(valConfig); err != nil { + return err + } + + valConfig.P2P.ListenAddress = "tcp://0.0.0.0:26656" + valConfig.P2P.AddrBookStrict = false + valConfig.P2P.ExternalAddress = fmt.Sprintf("%s:%d", n.moniker, 26656) + valConfig.RPC.ListenAddress = "tcp://0.0.0.0:26657" + valConfig.StateSync.Enable = false + valConfig.LogLevel = "info" + valConfig.P2P.PersistentPeers = strings.Join(persistentPeers, ",") + valConfig.Storage.DiscardABCIResponses = true + + tmconfig.WriteConfigFile(tmCfgPath, valConfig) + return nil +} + +func (n *internalNode) initStateSyncConfig(trustHeight int64, trustHash string, stateSyncRPCServers []string) error { + tmCfgPath := filepath.Join(n.configDir(), "config", "config.toml") + + vpr := viper.New() + vpr.SetConfigFile(tmCfgPath) + if err := vpr.ReadInConfig(); err != nil { + return err + } + + valConfig := tmconfig.DefaultConfig() + if err := vpr.Unmarshal(valConfig); err != nil { + return err + } + + valConfig.StateSync = tmconfig.DefaultStateSyncConfig() + valConfig.StateSync.Enable = true + valConfig.StateSync.TrustHeight = trustHeight + valConfig.StateSync.TrustHash = trustHash + valConfig.StateSync.RPCServers = stateSyncRPCServers + + tmconfig.WriteConfigFile(tmCfgPath, valConfig) + return nil +} + +// signMsg returns a signed tx of the provided messages, +// signed by the validator, using 0 fees, a high gas limit, and a common memo. +func (n *internalNode) signMsg(msgs ...sdk.Msg) (*sdktx.Tx, error) { + txBuilder := util.EncodingConfig.TxConfig.NewTxBuilder() + + if err := txBuilder.SetMsgs(msgs...); err != nil { + return nil, err + } + + txBuilder.SetMemo(fmt.Sprintf("%s@%s:26656", n.nodeKey.ID(), n.moniker)) + txBuilder.SetFeeAmount(sdk.NewCoins()) + txBuilder.SetGasLimit(uint64(200000 * len(msgs))) + + // TODO: Find a better way to sign this tx with less code. + signerData := authsigning.SignerData{ + ChainID: n.chain.chainMeta.Id, + AccountNumber: 0, + Sequence: 0, + } + + pub, err := n.keyInfo.GetPubKey() + + if err != nil { + return nil, err + } + // For SIGN_MODE_DIRECT, calling SetSignatures calls setSignerInfos on + // TxBuilder under the hood, and SignerInfos is needed to generate the sign + // bytes. This is the reason for setting SetSignatures here, with a nil + // signature. + // + // Note: This line is not needed for SIGN_MODE_LEGACY_AMINO, but putting it + // also doesn't affect its generated sign bytes, so for code's simplicity + // sake, we put it here. + sig := txsigning.SignatureV2{ + PubKey: pub, + Data: &txsigning.SingleSignatureData{ + SignMode: txsigning.SignMode_SIGN_MODE_DIRECT, + Signature: nil, + }, + Sequence: 0, + } + + if err := txBuilder.SetSignatures(sig); err != nil { + return nil, err + } + + bytesToSign, err := util.EncodingConfig.TxConfig.SignModeHandler().GetSignBytes( + txsigning.SignMode_SIGN_MODE_DIRECT, + signerData, + txBuilder.GetTx(), + ) + if err != nil { + return nil, err + } + + sigBytes, err := n.privateKey.Sign(bytesToSign) + if err != nil { + return nil, err + } + + if err != nil { + return nil, err + } + + sig = txsigning.SignatureV2{ + PubKey: pub, + Data: &txsigning.SingleSignatureData{ + SignMode: txsigning.SignMode_SIGN_MODE_DIRECT, + Signature: sigBytes, + }, + Sequence: 0, + } + if err := txBuilder.SetSignatures(sig); err != nil { + return nil, err + } + + signedTx := txBuilder.GetTx() + bz, err := util.EncodingConfig.TxConfig.TxEncoder()(signedTx) + if err != nil { + return nil, err + } + + return decodeTx(bz) +} diff --git a/test/e2e/initialization/node/main.go b/test/e2e/initialization/node/main.go new file mode 100644 index 000000000..7ed274975 --- /dev/null +++ b/test/e2e/initialization/node/main.go @@ -0,0 +1,74 @@ +package main + +import ( + "encoding/json" + "flag" + "os" + "strings" + "time" + + "github.com/babylonchain/babylon/test/e2e/initialization" +) + +func main() { + var ( + nodeConfigStr string + + dataDir string + + existingGenesisDir string + + chainId string + + votingPeriod time.Duration + + stateSyncRPCServersStr string + + persistentPeersStr string + + trustHeight int64 + + trustHash string + ) + + flag.StringVar(&dataDir, "data-dir", "", "chain data directory") + flag.StringVar(&existingGenesisDir, "genesis-dir", "", "pre-existing genesis location") + flag.StringVar(&chainId, "chain-id", "", "chain ID") + flag.StringVar(&nodeConfigStr, "node-config", "", "serialized node config") + flag.DurationVar(&votingPeriod, "voting-period", 30000000000, "voting period") + flag.StringVar(&stateSyncRPCServersStr, "rpc-servers", "", "state sync RPC servers") + flag.StringVar(&persistentPeersStr, "peers", "", "state sync RPC servers") + flag.Int64Var(&trustHeight, "trust-height", 0, "trust Height") + flag.StringVar(&trustHash, "trust-hash", "", "trust hash") + + flag.Parse() + + if len(dataDir) == 0 { + panic("data-dir is required") + } + + var nodeConfig initialization.NodeConfig + err := json.Unmarshal([]byte(nodeConfigStr), &nodeConfig) + if err != nil { + panic(err) + } + + stateSyncRPCServers := strings.Split(stateSyncRPCServersStr, ",") + if len(stateSyncRPCServers) == 0 { + panic("rpc-servers is required, separated by commas") + } + + persistenrPeers := strings.Split(persistentPeersStr, ",") + if len(persistenrPeers) == 0 { + panic("persistent peers are required, separated by commas") + } + + if err := os.MkdirAll(dataDir, 0o755); err != nil { + panic(err) + } + + _, err = initialization.InitSingleNode(chainId, dataDir, existingGenesisDir, &nodeConfig, votingPeriod, trustHeight, trustHash, stateSyncRPCServers, persistenrPeers) + if err != nil { + panic(err) + } +} diff --git a/test/e2e/initialization/util.go b/test/e2e/initialization/util.go new file mode 100644 index 000000000..3346f2b5d --- /dev/null +++ b/test/e2e/initialization/util.go @@ -0,0 +1,47 @@ +package initialization + +import ( + "fmt" + + "github.com/cosmos/cosmos-sdk/codec/unknownproto" + sdktx "github.com/cosmos/cosmos-sdk/types/tx" + + "github.com/babylonchain/babylon/test/e2e/util" +) + +func decodeTx(txBytes []byte) (*sdktx.Tx, error) { + var raw sdktx.TxRaw + + // reject all unknown proto fields in the root TxRaw + err := unknownproto.RejectUnknownFieldsStrict(txBytes, &raw, util.EncodingConfig.InterfaceRegistry) + if err != nil { + return nil, fmt.Errorf("failed to reject unknown fields: %w", err) + } + + if err := util.Cdc.Unmarshal(txBytes, &raw); err != nil { + return nil, err + } + + var body sdktx.TxBody + if err := util.Cdc.Unmarshal(raw.BodyBytes, &body); err != nil { + return nil, fmt.Errorf("failed to decode tx: %w", err) + } + + var authInfo sdktx.AuthInfo + + // reject all unknown proto fields in AuthInfo + err = unknownproto.RejectUnknownFieldsStrict(raw.AuthInfoBytes, &authInfo, util.EncodingConfig.InterfaceRegistry) + if err != nil { + return nil, fmt.Errorf("failed to reject unknown fields: %w", err) + } + + if err := util.Cdc.Unmarshal(raw.AuthInfoBytes, &authInfo); err != nil { + return nil, fmt.Errorf("failed to decode auth info: %w", err) + } + + return &sdktx.Tx{ + Body: &body, + AuthInfo: &authInfo, + Signatures: raw.Signatures, + }, nil +} diff --git a/test/e2e/scripts/hermes_bootstrap.sh b/test/e2e/scripts/hermes_bootstrap.sh new file mode 100644 index 000000000..bed26faf5 --- /dev/null +++ b/test/e2e/scripts/hermes_bootstrap.sh @@ -0,0 +1,72 @@ +#!/bin/bash + +set -ex + +# initialize Hermes relayer configuration +mkdir -p /root/.hermes/ +touch /root/.hermes/config.toml + +# setup Hermes relayer configuration +tee /root/.hermes/config.toml <