diff --git a/go.mod b/go.mod index 8a074d32ca..203611e4b5 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.22.8 require ( github.com/VictoriaMetrics/fastcache v1.12.1 github.com/antithesishq/antithesis-sdk-go v0.3.8 - github.com/ava-labs/avalanchego v1.11.13-0.20241026214739-acb3d7d102a0 + github.com/ava-labs/avalanchego v1.11.13-0.20241113171850-4c199890fadb github.com/cespare/cp v0.1.0 github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233 github.com/davecgh/go-spew v1.1.1 @@ -76,17 +76,23 @@ require ( github.com/dlclark/regexp2 v1.7.0 // indirect github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-units v0.5.0 // indirect + github.com/emicklei/go-restful/v3 v3.11.0 // indirect github.com/ethereum/c-kzg-4844 v0.4.0 // indirect github.com/getsentry/sentry-go v0.18.0 // indirect github.com/go-logr/logr v1.4.1 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.3.0 // indirect + github.com/go-openapi/jsonpointer v0.19.6 // indirect + github.com/go-openapi/jsonreference v0.20.2 // indirect + github.com/go-openapi/swag v0.22.3 // indirect github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect github.com/google/btree v1.1.2 // indirect + github.com/google/gnostic-models v0.6.8 // indirect + github.com/google/gofuzz v1.2.0 // indirect github.com/google/pprof v0.0.0-20230207041349-798e818bf904 // indirect github.com/google/renameio/v2 v2.0.0 // indirect github.com/gorilla/mux v1.8.0 // indirect @@ -96,17 +102,25 @@ require ( github.com/huin/goupnp v1.3.0 // indirect github.com/jackpal/gateway v1.0.6 // indirect github.com/jackpal/go-nat-pmp v1.0.2 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/compress v1.15.15 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect github.com/magiconair/properties v1.8.6 // indirect + github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-runewidth v0.0.13 // indirect github.com/mattn/go-shellwords v1.0.12 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/pointerstructure v1.2.0 // indirect github.com/mmcloughlin/addchain v0.4.0 // indirect + github.com/moby/spdystream v0.2.0 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect github.com/mr-tron/base58 v1.2.0 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/pelletier/go-toml v1.9.5 // indirect @@ -124,7 +138,7 @@ require ( github.com/spf13/afero v1.8.2 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/subosito/gotenv v1.3.0 // indirect - github.com/supranational/blst v0.3.11 // indirect + github.com/supranational/blst v0.3.13 // indirect github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a // indirect github.com/tklauser/go-sysconf v0.3.12 // indirect github.com/tklauser/numcpus v0.6.1 // indirect @@ -141,14 +155,25 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.26.0 // indirect golang.org/x/net v0.28.0 // indirect + golang.org/x/oauth2 v0.21.0 // indirect golang.org/x/term v0.23.0 // indirect golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect gonum.org/v1/gonum v0.11.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240827150818-7e3bb234dfed // indirect google.golang.org/grpc v1.66.0 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect + k8s.io/api v0.29.0 // indirect + k8s.io/apimachinery v0.29.0 // indirect + k8s.io/client-go v0.29.0 // indirect + k8s.io/klog/v2 v2.110.1 // indirect + k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect + k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect rsc.io/tmplfunc v0.0.3 // indirect + sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect + sigs.k8s.io/yaml v1.3.0 // indirect ) diff --git a/go.sum b/go.sum index dd6bf4e4f6..d06ba20439 100644 --- a/go.sum +++ b/go.sum @@ -58,8 +58,10 @@ github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax github.com/antithesishq/antithesis-sdk-go v0.3.8 h1:OvGoHxIcOXFJLyn9IJQ5DzByZ3YVAWNBc394ObzDRb8= github.com/antithesishq/antithesis-sdk-go v0.3.8/go.mod h1:IUpT2DPAKh6i/YhSbt6Gl3v2yvUZjmKncl7U91fup7E= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= -github.com/ava-labs/avalanchego v1.11.13-0.20241026214739-acb3d7d102a0 h1:1T9OnvZP6XZ62EVWlfmrI8rrudyE6bM2Zt51pCHfS5o= -github.com/ava-labs/avalanchego v1.11.13-0.20241026214739-acb3d7d102a0/go.mod h1:gYlTU42Q4b29hzhUN22yclym5qwB3Si0jh4+LTn7DZM= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/ava-labs/avalanchego v1.11.13-0.20241113171850-4c199890fadb h1:ujIv6zAKQpZtlalLGSt9cx2AREJmYcv0wMmXGRAH6/g= +github.com/ava-labs/avalanchego v1.11.13-0.20241113171850-4c199890fadb/go.mod h1:86tO6F1FT8emclUwdQ2WCwAtAerqjm5A4IbV6XxNUyM= github.com/ava-labs/coreth v0.13.8 h1:f14X3KgwHl9LwzfxlN6S4bbn5VA2rhEsNnHaRLSTo/8= github.com/ava-labs/coreth v0.13.8/go.mod h1:t3BSv/eQv0AlDPMfEDCMMoD/jq1RkUsbFzQAFg5qBcE= github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g= @@ -171,6 +173,8 @@ github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA github.com/dop251/goja_nodejs v0.0.0-20211022123610-8dd9abb0616d/go.mod h1:DngW8aVqWbuLRMHItjPUyqdj+HWPvnQe8V8y1nDpIbM= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM= +github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= +github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= @@ -212,6 +216,7 @@ github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= @@ -220,6 +225,12 @@ github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AE github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= +github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= +github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= +github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= +github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= +github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= github.com/go-sourcemap/sourcemap v2.1.3+incompatible h1:W1iEw64niKVGogNgBN3ePyLFfuisuzeidWPMPWmECqU= github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= @@ -278,6 +289,8 @@ github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Z github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= +github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= +github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -288,10 +301,13 @@ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= @@ -324,6 +340,7 @@ github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB7 github.com/gorilla/rpc v1.2.0 h1:WvvdC2lNeT1SP32zrIce5l0ECBfbAlmrmSBsuc57wfk= github.com/gorilla/rpc v1.2.0/go.mod h1:V4h9r+4sF5HnzqbwIez0fKSpANP0zlYd3qR7p36jkTQ= github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= @@ -365,9 +382,13 @@ github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7Bd github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= @@ -404,6 +425,8 @@ github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2 github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= @@ -437,13 +460,22 @@ github.com/mitchellh/pointerstructure v1.2.0/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8oh github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY= github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU= github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU= +github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= +github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ= github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus= +github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= @@ -546,6 +578,7 @@ github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobt github.com/status-im/keycard-go v0.2.0/go.mod h1:wlp8ZLbsmrF6g6WjugPAx+IzoLrkdf9+mHxBEeo3Hbg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -555,12 +588,13 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.3.0 h1:mjC+YW8QpAdXibNi+vNWgzmgBH4+5l5dCXv8cNysBLI= github.com/subosito/gotenv v1.3.0/go.mod h1:YzJjq/33h7nrwdY+iHMhEOEEbW0ovIz0tB6t6PwAXzs= -github.com/supranational/blst v0.3.11 h1:LyU6FolezeWAhvQk0k6O/d49jqgO52MSDDfYgbeoEm4= -github.com/supranational/blst v0.3.11/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= +github.com/supranational/blst v0.3.13 h1:AYeSxdOMacwu7FBmpfloBz5pbFXDmJL33RuwnKtmTjk= +github.com/supranational/blst v0.3.13/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a h1:1ur3QoCqvE5fl+nylMaIr9PVV1w343YRDtsy+Rwu7XI= github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= @@ -740,6 +774,8 @@ golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs= +golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1020,6 +1056,8 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= @@ -1031,6 +1069,7 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWD gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= @@ -1048,8 +1087,26 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +k8s.io/api v0.29.0 h1:NiCdQMY1QOp1H8lfRyeEf8eOwV6+0xA6XEE44ohDX2A= +k8s.io/api v0.29.0/go.mod h1:sdVmXoz2Bo/cb77Pxi71IPTSErEW32xa4aXwKH7gfBA= +k8s.io/apimachinery v0.29.0 h1:+ACVktwyicPz0oc6MTMLwa2Pw3ouLAfAon1wPLtG48o= +k8s.io/apimachinery v0.29.0/go.mod h1:eVBxQ/cwiJxH58eK/jd/vAk4mrxmVlnpBH5J2GbMeis= +k8s.io/client-go v0.29.0 h1:KmlDtFcrdUzOYrBhXHgKw5ycWzc3ryPX5mQe0SkG3y8= +k8s.io/client-go v0.29.0/go.mod h1:yLkXH4HKMAywcrD82KMSmfYg2DlE8mepPR4JGSo5n38= +k8s.io/klog/v2 v2.110.1 h1:U/Af64HJf7FcwMcXyKm2RPM22WZzyR7OSpYj5tg3cL0= +k8s.io/klog/v2 v2.110.1/go.mod h1:YGtd1984u+GgbuZ7e08/yBuAfKLSO0+uR1Fhi6ExXjo= +k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 h1:aVUu9fTY98ivBPKR9Y5w/AuzbMm96cd3YHRTU83I780= +k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA= +k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI= +k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU= rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= +sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= +sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/plugin/evm/block.go b/plugin/evm/block.go index 9e7d96855a..fac7689768 100644 --- a/plugin/evm/block.go +++ b/plugin/evm/block.go @@ -199,7 +199,7 @@ func (b *Block) verify(predicateContext *precompileconfig.PredicateContext, writ // If the chain is still bootstrapping, we can assume that all blocks we are verifying have // been accepted by the network (so the predicate was validated by the network when the // block was originally verified). - if b.vm.bootstrapped { + if b.vm.bootstrapped.Get() { if err := b.verifyPredicates(predicateContext); err != nil { return fmt.Errorf("failed to verify predicates: %w", err) } diff --git a/plugin/evm/syncervm_test.go b/plugin/evm/syncervm_test.go index e87bd9403f..f200200db1 100644 --- a/plugin/evm/syncervm_test.go +++ b/plugin/evm/syncervm_test.go @@ -494,7 +494,7 @@ func testSyncerVM(t *testing.T, vmSetup *syncVMSetup, test syncTest) { // check we can transition to [NormalOp] state and continue to process blocks. require.NoError(syncerVM.SetState(context.Background(), snow.NormalOp)) - require.True(syncerVM.bootstrapped) + require.True(syncerVM.bootstrapped.Get()) // Generate blocks after we have entered normal consensus as well generateAndAcceptBlocks(t, syncerVM, blocksToBuild, func(_ int, gen *core.BlockGen) { diff --git a/plugin/evm/tx_gossip_test.go b/plugin/evm/tx_gossip_test.go index 36c47ffde5..0eaaa63e6d 100644 --- a/plugin/evm/tx_gossip_test.go +++ b/plugin/evm/tx_gossip_test.go @@ -20,7 +20,6 @@ import ( "github.com/ava-labs/avalanchego/snow/engine/common" "github.com/ava-labs/avalanchego/snow/engine/enginetest" "github.com/ava-labs/avalanchego/snow/validators" - "github.com/ava-labs/avalanchego/snow/validators/validatorstest" agoUtils "github.com/ava-labs/avalanchego/utils" "github.com/ava-labs/avalanchego/utils/logging" "github.com/ava-labs/avalanchego/utils/set" @@ -37,7 +36,7 @@ func TestEthTxGossip(t *testing.T) { require := require.New(t) ctx := context.Background() snowCtx := utils.TestSnowContext() - validatorState := &validatorstest.State{} + validatorState := utils.NewTestValidatorState() snowCtx.ValidatorState = validatorState responseSender := &enginetest.SenderStub{ @@ -155,14 +154,6 @@ func TestEthTxPushGossipOutbound(t *testing.T) { require := require.New(t) ctx := context.Background() snowCtx := utils.TestSnowContext() - snowCtx.ValidatorState = &validatorstest.State{ - GetCurrentHeightF: func(context.Context) (uint64, error) { - return 0, nil - }, - GetValidatorSetF: func(context.Context, uint64, ids.ID) (map[ids.NodeID]*validators.GetValidatorOutput, error) { - return nil, nil - }, - } sender := &enginetest.SenderStub{ SentAppGossip: make(chan []byte, 1), } diff --git a/plugin/evm/uptime/pausable_manager.go b/plugin/evm/uptime/pausable_manager.go index 3976fcae6c..6c437dd049 100644 --- a/plugin/evm/uptime/pausable_manager.go +++ b/plugin/evm/uptime/pausable_manager.go @@ -58,16 +58,10 @@ func (p *pausableManager) Disconnect(nodeID ids.NodeID) error { return nil } -// StartTracking starts tracking uptime for the nodes with the given IDs -// If a node is paused, it will not be tracked -func (p *pausableManager) StartTracking(nodeIDs []ids.NodeID) error { - activeNodeIDs := make([]ids.NodeID, 0, len(nodeIDs)) - for _, nodeID := range nodeIDs { - if !p.IsPaused(nodeID) { - activeNodeIDs = append(activeNodeIDs, nodeID) - } - } - return p.Manager.StartTracking(activeNodeIDs) +// IsConnected returns true if the node with the given ID is connected to this manager +// Note: Inner manager may have a different view of the connection status due to pausing +func (p *pausableManager) IsConnected(nodeID ids.NodeID) bool { + return p.connectedVdrs.Contains(nodeID) } // OnValidatorAdded is called when a validator is added. diff --git a/plugin/evm/uptime/pausable_manager_test.go b/plugin/evm/uptime/pausable_manager_test.go index e1f4f4a6f8..a910203773 100644 --- a/plugin/evm/uptime/pausable_manager_test.go +++ b/plugin/evm/uptime/pausable_manager_test.go @@ -31,6 +31,7 @@ func TestPausableManager(t *testing.T) { // Connect before tracking require.NoError(up.Connect(nodeID0)) addTime(clk, time.Second) + expectedUptime := 1 * time.Second // Pause before tracking up.OnValidatorStatusUpdated(vID, nodeID0, false) @@ -38,12 +39,15 @@ func TestPausableManager(t *testing.T) { // Elapse Time addTime(clk, time.Second) + // Since we have not started tracking this node yet, its observed uptime should + // be incremented even though it is actually paused. + expectedUptime += 1 * time.Second // Start tracking require.NoError(up.StartTracking([]ids.NodeID{nodeID0})) + // Uptime here should not increase after start tracking + // since the node is still paused after we started tracking currentTime := addTime(clk, time.Second) - // Uptime should not have increased since the node was paused - expectedUptime := 0 * time.Second checkUptime(t, up, nodeID0, expectedUptime, currentTime) // Disconnect @@ -114,15 +118,17 @@ func TestPausableManager(t *testing.T) { // Start tracking addTime(clk, time.Second) + // Uptime should be 1 since the node was paused before we started tracking + expectedUptime := 1 * time.Second require.NoError(up.StartTracking([]ids.NodeID{nodeID0})) // Connect and check uptime addTime(clk, 1*time.Second) + checkUptime(t, up, nodeID0, expectedUptime, clk.Time()) require.NoError(up.Connect(nodeID0)) currentTime := addTime(clk, 2*time.Second) - // Uptime should not have increased since the node was paused - expectedUptime := 0 * time.Second + // Uptime should not have increased since the node was paused after we started tracking checkUptime(t, up, nodeID0, expectedUptime, currentTime) // Disconnect and check uptime @@ -209,6 +215,81 @@ func TestPausableManager(t *testing.T) { checkUptime(t, up, nodeID0, expectedUptime, currentTime) }, }, + { + name: "Case 5: Node paused after we stop tracking", + testFunc: func(t *testing.T, up interfaces.PausableManager, clk *mockable.Clock, s uptime.State) { + require := require.New(t) + + // Start tracking and connect + require.NoError(up.StartTracking([]ids.NodeID{nodeID0})) + addTime(clk, time.Second) + require.NoError(up.Connect(nodeID0)) + + // Stop tracking + currentTime := addTime(clk, 2*time.Second) + expectedUptime := 2 * time.Second + checkUptime(t, up, nodeID0, expectedUptime, currentTime) + require.NoError(up.StopTracking([]ids.NodeID{nodeID0})) + + // Pause after a while + addTime(clk, 3*time.Second) + // expectedUptime should increase since we stopped tracking + expectedUptime += 3 * time.Second + up.OnValidatorStatusUpdated(vID, nodeID0, false) + require.True(up.IsPaused(nodeID0)) + // expectedUptime should increase since we stopped tracking (even if the node was paused) + currentTime = addTime(clk, 4*time.Second) + expectedUptime += 4 * time.Second + + // Start tracking and check elapsed time + require.NoError(up.StartTracking([]ids.NodeID{nodeID0})) + // Uptime have increased since the node was paused before we started tracking + // We should be optimistic and assume the node was online and active until we start tracking + require.True(up.IsPaused(nodeID0)) + checkUptime(t, up, nodeID0, expectedUptime, currentTime) + }, + }, + { + name: "Case 6: Paused node got resumed after we stop tracking", + testFunc: func(t *testing.T, up interfaces.PausableManager, clk *mockable.Clock, s uptime.State) { + require := require.New(t) + + // Start tracking and connect + require.NoError(up.StartTracking([]ids.NodeID{nodeID0})) + addTime(clk, time.Second) + require.NoError(up.Connect(nodeID0)) + + // Pause after a while + currentTime := addTime(clk, 2*time.Second) + // expectedUptime should increase + expectedUptime := 2 * time.Second + up.OnValidatorStatusUpdated(vID, nodeID0, false) + require.True(up.IsPaused(nodeID0)) + checkUptime(t, up, nodeID0, expectedUptime, currentTime) + + // Stop tracking + currentTime = addTime(clk, 3*time.Second) + // expectedUptime should not increase since the node was paused + checkUptime(t, up, nodeID0, expectedUptime, currentTime) + require.NoError(up.StopTracking([]ids.NodeID{nodeID0})) + + // Resume after a while + addTime(clk, 4*time.Second) + // expectedUptime should increase since we stopped tracking + expectedUptime += 4 * time.Second + up.OnValidatorStatusUpdated(vID, nodeID0, true) + require.False(up.IsPaused(nodeID0)) + // expectedUptime should increase since we stopped tracking + currentTime = addTime(clk, 5*time.Second) + expectedUptime += 5 * time.Second + + // Start tracking and check elapsed time + require.NoError(up.StartTracking([]ids.NodeID{nodeID0})) + // Uptime should have increased by 4 seconds since the node was resumed + // We should be optimistic and assume the node was online and active until we start tracking + checkUptime(t, up, nodeID0, expectedUptime, currentTime) + }, + }, } for _, test := range tests { diff --git a/plugin/evm/validators/interfaces/interface.go b/plugin/evm/validators/interfaces/interface.go index 197ab553a1..39b6b8c9e9 100644 --- a/plugin/evm/validators/interfaces/interface.go +++ b/plugin/evm/validators/interfaces/interface.go @@ -4,6 +4,8 @@ package interfaces import ( + "time" + "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/snow/uptime" "github.com/ava-labs/avalanchego/utils/set" @@ -12,21 +14,20 @@ import ( type State interface { uptime.State // AddValidator adds a new validator to the state - AddValidator(vID ids.ID, nodeID ids.NodeID, startTimestamp uint64, isActive bool) error + AddValidator(vdr Validator) error + // UpdateValidator updates the validator in the state + UpdateValidator(vdr Validator) error + // GetValidator returns the validator data for the given validation ID + GetValidator(vID ids.ID) (Validator, error) // DeleteValidator deletes the validator from the state DeleteValidator(vID ids.ID) error // WriteState writes the validator state to the disk WriteState() error - // SetStatus sets the active status of the validator with the given vID - SetStatus(vID ids.ID, isActive bool) error - // GetStatus returns the active status of the validator with the given vID - GetStatus(vID ids.ID) (bool, error) - // GetValidationIDs returns the validation IDs in the state GetValidationIDs() set.Set[ids.ID] - // GetValidatorIDs returns the validator node IDs in the state - GetValidatorIDs() set.Set[ids.NodeID] + // GetNodeIDs returns the validator node IDs in the state + GetNodeIDs() set.Set[ids.NodeID] // RegisterListener registers a listener to the state RegisterListener(StateCallbackListener) @@ -41,3 +42,14 @@ type StateCallbackListener interface { // OnValidatorStatusUpdated is called when a validator status is updated OnValidatorStatusUpdated(vID ids.ID, nodeID ids.NodeID, isActive bool) } + +type Validator struct { + ValidationID ids.ID `json:"validationID"` + NodeID ids.NodeID `json:"nodeID"` + Weight uint64 `json:"weight"` + StartTimestamp uint64 `json:"startTimestamp"` + IsActive bool `json:"isActive"` + IsSoV bool `json:"isSoV"` +} + +func (v *Validator) StartTime() time.Time { return time.Unix(int64(v.StartTimestamp), 0) } diff --git a/plugin/evm/validators/state.go b/plugin/evm/validators/state.go index 0be101f6bf..a8769466e9 100644 --- a/plugin/evm/validators/state.go +++ b/plugin/evm/validators/state.go @@ -18,19 +18,24 @@ var _ uptime.State = &state{} type dbUpdateStatus bool -var ErrAlreadyExists = fmt.Errorf("validator already exists") +var ( + ErrAlreadyExists = fmt.Errorf("validator already exists") + ErrImmutableField = fmt.Errorf("immutable field cannot be updated") +) const ( - updated dbUpdateStatus = true - deleted dbUpdateStatus = false + updatedStatus dbUpdateStatus = true + deletedStatus dbUpdateStatus = false ) type validatorData struct { UpDuration time.Duration `serialize:"true"` LastUpdated uint64 `serialize:"true"` NodeID ids.NodeID `serialize:"true"` + Weight uint64 `serialize:"true"` StartTime uint64 `serialize:"true"` IsActive bool `serialize:"true"` + IsSoV bool `serialize:"true"` validationID ids.ID // database key } @@ -83,7 +88,7 @@ func (s *state) SetUptime( data.UpDuration = upDuration data.setLastUpdated(lastUpdated) - s.updatedData[data.validationID] = updated + s.updatedData[data.validationID] = updatedStatus return nil } @@ -98,23 +103,57 @@ func (s *state) GetStartTime(nodeID ids.NodeID) (time.Time, error) { // AddValidator adds a new validator to the state // the new validator is marked as updated and will be written to the disk when WriteState is called -func (s *state) AddValidator(vID ids.ID, nodeID ids.NodeID, startTimestamp uint64, isActive bool) error { +func (s *state) AddValidator(vdr interfaces.Validator) error { data := &validatorData{ - NodeID: nodeID, - validationID: vID, - IsActive: isActive, - StartTime: startTimestamp, + NodeID: vdr.NodeID, + validationID: vdr.ValidationID, + IsActive: vdr.IsActive, + StartTime: vdr.StartTimestamp, UpDuration: 0, - LastUpdated: startTimestamp, + LastUpdated: vdr.StartTimestamp, + IsSoV: vdr.IsSoV, + Weight: vdr.Weight, } - if err := s.addData(vID, data); err != nil { + if err := s.addData(vdr.ValidationID, data); err != nil { return err } - s.updatedData[vID] = updated + s.updatedData[vdr.ValidationID] = updatedStatus for _, listener := range s.listeners { - listener.OnValidatorAdded(vID, nodeID, startTimestamp, isActive) + listener.OnValidatorAdded(vdr.ValidationID, vdr.NodeID, vdr.StartTimestamp, vdr.IsActive) + } + return nil +} + +// UpdateValidator updates the validator in the state +// returns an error if the validator does not exist or if the immutable fields are modified +func (s *state) UpdateValidator(vdr interfaces.Validator) error { + data, exists := s.data[vdr.ValidationID] + if !exists { + return database.ErrNotFound + } + // check immutable fields + if !data.constantsAreUnmodified(vdr) { + return ErrImmutableField + } + // check if mutable fields have changed + updated := false + if data.IsActive != vdr.IsActive { + data.IsActive = vdr.IsActive + updated = true + for _, listener := range s.listeners { + listener.OnValidatorStatusUpdated(data.validationID, data.NodeID, data.IsActive) + } + } + + if data.Weight != vdr.Weight { + data.Weight = vdr.Weight + updated = true + } + + if updated { + s.updatedData[vdr.ValidationID] = updatedStatus } return nil } @@ -130,7 +169,7 @@ func (s *state) DeleteValidator(vID ids.ID) error { delete(s.index, data.NodeID) // mark as deleted for WriteValidator - s.updatedData[data.validationID] = deleted + s.updatedData[data.validationID] = deletedStatus for _, listener := range s.listeners { listener.OnValidatorRemoved(vID, data.NodeID) @@ -144,7 +183,7 @@ func (s *state) WriteState() error { batch := s.db.NewBatch() for vID, updateStatus := range s.updatedData { switch updateStatus { - case updated: + case updatedStatus: data := s.data[vID] dataBytes, err := vdrCodec.Marshal(codecVersion, data) @@ -154,7 +193,7 @@ func (s *state) WriteState() error { if err := batch.Put(vID[:], dataBytes); err != nil { return err } - case deleted: + case deletedStatus: if err := batch.Delete(vID[:]); err != nil { return err } @@ -174,7 +213,7 @@ func (s *state) SetStatus(vID ids.ID, isActive bool) error { return database.ErrNotFound } data.IsActive = isActive - s.updatedData[vID] = updated + s.updatedData[vID] = updatedStatus for _, listener := range s.listeners { listener.OnValidatorStatusUpdated(vID, data.NodeID, isActive) @@ -182,15 +221,6 @@ func (s *state) SetStatus(vID ids.ID, isActive bool) error { return nil } -// GetStatus returns the active status of the validator with the given vID -func (s *state) GetStatus(vID ids.ID) (bool, error) { - data, exists := s.data[vID] - if !exists { - return false, database.ErrNotFound - } - return data.IsActive, nil -} - // GetValidationIDs returns the validation IDs in the state func (s *state) GetValidationIDs() set.Set[ids.ID] { ids := set.NewSet[ids.ID](len(s.data)) @@ -200,8 +230,8 @@ func (s *state) GetValidationIDs() set.Set[ids.ID] { return ids } -// GetValidatorIDs returns the validator IDs in the state -func (s *state) GetValidatorIDs() set.Set[ids.NodeID] { +// GetNodeIDs returns the node IDs of validators in the state +func (s *state) GetNodeIDs() set.Set[ids.NodeID] { ids := set.NewSet[ids.NodeID](len(s.index)) for nodeID := range s.index { ids.Add(nodeID) @@ -209,6 +239,22 @@ func (s *state) GetValidatorIDs() set.Set[ids.NodeID] { return ids } +// GetValidator returns the validator data for the given validationID +func (s *state) GetValidator(vID ids.ID) (interfaces.Validator, error) { + data, ok := s.data[vID] + if !ok { + return interfaces.Validator{}, database.ErrNotFound + } + return interfaces.Validator{ + ValidationID: data.validationID, + NodeID: data.NodeID, + StartTimestamp: data.StartTime, + IsActive: data.IsActive, + Weight: data.Weight, + IsSoV: data.IsSoV, + }, nil +} + // RegisterListener registers a listener to the state // OnValidatorAdded is called for all current validators on the provided listener before this function returns func (s *state) RegisterListener(listener interfaces.StateCallbackListener) { @@ -293,3 +339,12 @@ func (v *validatorData) getLastUpdated() time.Time { func (v *validatorData) getStartTime() time.Time { return time.Unix(int64(v.StartTime), 0) } + +// constantsAreUnmodified returns true if the constants of this validator have +// not been modified compared to the updated validator. +func (v *validatorData) constantsAreUnmodified(u interfaces.Validator) bool { + return v.validationID == u.ValidationID && + v.NodeID == u.NodeID && + v.IsSoV == u.IsSoV && + v.StartTime == u.StartTimestamp +} diff --git a/plugin/evm/validators/state_test.go b/plugin/evm/validators/state_test.go index e5e8244027..7ba6574785 100644 --- a/plugin/evm/validators/state_test.go +++ b/plugin/evm/validators/state_test.go @@ -36,13 +36,23 @@ func TestState(t *testing.T) { require.ErrorIs(err, database.ErrNotFound) // add new validator - state.AddValidator(vID, nodeID, uint64(startTime.Unix()), true) + vdr := interfaces.Validator{ + ValidationID: vID, + NodeID: nodeID, + Weight: 1, + StartTimestamp: uint64(startTime.Unix()), + IsActive: true, + IsSoV: true, + } + state.AddValidator(vdr) // adding the same validator should fail - err = state.AddValidator(vID, ids.GenerateTestNodeID(), uint64(startTime.Unix()), true) + err = state.AddValidator(vdr) require.ErrorIs(err, ErrAlreadyExists) // adding the same nodeID should fail - err = state.AddValidator(ids.GenerateTestID(), nodeID, uint64(startTime.Unix()), true) + newVdr := vdr + newVdr.ValidationID = ids.GenerateTestID() + err = state.AddValidator(newVdr) require.ErrorIs(err, ErrAlreadyExists) // get uptime @@ -62,11 +72,39 @@ func TestState(t *testing.T) { require.Equal(newLastUpdated, lastUpdated) // set status - require.NoError(state.SetStatus(vID, false)) + vdr.IsActive = false + require.NoError(state.UpdateValidator(vdr)) // get status - status, err := state.GetStatus(vID) + data, err := state.GetValidator(vID) + require.NoError(err) + require.False(data.IsActive) + + // set weight + newWeight := uint64(2) + vdr.Weight = newWeight + require.NoError(state.UpdateValidator(vdr)) + // get weight + data, err = state.GetValidator(vID) require.NoError(err) - require.False(status) + require.Equal(newWeight, data.Weight) + + // set a different node ID should fail + newNodeID := ids.GenerateTestNodeID() + vdr.NodeID = newNodeID + require.ErrorIs(state.UpdateValidator(vdr), ErrImmutableField) + + // set a different start time should fail + newStartTime := vdr.StartTime().Add(time.Hour) + vdr.StartTimestamp = uint64(newStartTime.Unix()) + require.ErrorIs(state.UpdateValidator(vdr), ErrImmutableField) + + // set SoV should fail + vdr.IsSoV = false + require.ErrorIs(state.UpdateValidator(vdr), ErrImmutableField) + + // set validation ID should result in not found + vdr.ValidationID = ids.GenerateTestID() + require.ErrorIs(state.UpdateValidator(vdr), database.ErrNotFound) // delete uptime require.NoError(state.DeleteValidator(vID)) @@ -88,7 +126,14 @@ func TestWriteValidator(t *testing.T) { nodeID := ids.GenerateTestNodeID() vID := ids.GenerateTestID() startTime := time.Now() - require.NoError(state.AddValidator(vID, nodeID, uint64(startTime.Unix()), true)) + require.NoError(state.AddValidator(interfaces.Validator{ + ValidationID: vID, + NodeID: nodeID, + Weight: 1, + StartTimestamp: uint64(startTime.Unix()), + IsActive: true, + IsSoV: true, + })) // write state, should reflect to DB require.NoError(state.WriteState()) @@ -132,8 +177,14 @@ func TestParseValidator(t *testing.T) { name: "nil", bytes: nil, expected: &validatorData{ - LastUpdated: 0, - StartTime: 0, + LastUpdated: 0, + StartTime: 0, + validationID: ids.Empty, + NodeID: ids.EmptyNodeID, + UpDuration: 0, + Weight: 0, + IsActive: false, + IsSoV: false, }, expectedErr: nil, }, @@ -141,8 +192,14 @@ func TestParseValidator(t *testing.T) { name: "empty", bytes: []byte{}, expected: &validatorData{ - LastUpdated: 0, - StartTime: 0, + LastUpdated: 0, + StartTime: 0, + validationID: ids.Empty, + NodeID: ids.EmptyNodeID, + UpDuration: 0, + Weight: 0, + IsActive: false, + IsSoV: false, }, expectedErr: nil, }, @@ -159,10 +216,14 @@ func TestParseValidator(t *testing.T) { 0x7e, 0xef, 0xe8, 0x8a, 0x45, 0xfb, 0x7a, 0xc4, 0xb0, 0x59, 0xc9, 0x33, 0x71, 0x0a, 0x57, 0x33, 0xff, 0x9f, 0x4b, 0xab, + // weight + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, // start time 0x00, 0x00, 0x00, 0x00, 0x00, 0x5B, 0x8D, 0x80, // status 0x01, + // is SoV + 0x01, }, expected: &validatorData{ UpDuration: time.Duration(6000000), @@ -170,6 +231,8 @@ func TestParseValidator(t *testing.T) { NodeID: testNodeID, StartTime: 6000000, IsActive: true, + Weight: 1, + IsSoV: true, }, }, { @@ -231,7 +294,14 @@ func TestStateListener(t *testing.T) { initialStartTime := time.Now() // add initial validator - require.NoError(state.AddValidator(initialvID, initialNodeID, uint64(initialStartTime.Unix()), true)) + require.NoError(state.AddValidator(interfaces.Validator{ + ValidationID: initialvID, + NodeID: initialNodeID, + Weight: 1, + StartTimestamp: uint64(initialStartTime.Unix()), + IsActive: true, + IsSoV: true, + })) // register listener mockListener.EXPECT().OnValidatorAdded(initialvID, initialNodeID, uint64(initialStartTime.Unix()), true) @@ -239,11 +309,23 @@ func TestStateListener(t *testing.T) { // add new validator mockListener.EXPECT().OnValidatorAdded(expectedvID, expectedNodeID, uint64(expectedStartTime.Unix()), true) - require.NoError(state.AddValidator(expectedvID, expectedNodeID, uint64(expectedStartTime.Unix()), true)) + vdr := interfaces.Validator{ + ValidationID: expectedvID, + NodeID: expectedNodeID, + Weight: 1, + StartTimestamp: uint64(expectedStartTime.Unix()), + IsActive: true, + IsSoV: true, + } + require.NoError(state.AddValidator(vdr)) // set status mockListener.EXPECT().OnValidatorStatusUpdated(expectedvID, expectedNodeID, false) - require.NoError(state.SetStatus(expectedvID, false)) + vdr.IsActive = false + require.NoError(state.UpdateValidator(vdr)) + + // set status twice should not trigger listener + require.NoError(state.UpdateValidator(vdr)) // remove validator mockListener.EXPECT().OnValidatorRemoved(expectedvID, expectedNodeID) diff --git a/plugin/evm/vm.go b/plugin/evm/vm.go index 8e169f6d4b..bbf85c0479 100644 --- a/plugin/evm/vm.go +++ b/plugin/evm/vm.go @@ -41,6 +41,10 @@ import ( "github.com/ava-labs/subnet-evm/params" "github.com/ava-labs/subnet-evm/peer" "github.com/ava-labs/subnet-evm/plugin/evm/message" + "github.com/ava-labs/subnet-evm/plugin/evm/uptime" + uptimeinterfaces "github.com/ava-labs/subnet-evm/plugin/evm/uptime/interfaces" + "github.com/ava-labs/subnet-evm/plugin/evm/validators" + validatorsinterfaces "github.com/ava-labs/subnet-evm/plugin/evm/validators/interfaces" "github.com/ava-labs/subnet-evm/triedb" "github.com/ava-labs/subnet-evm/triedb/hashdb" @@ -80,10 +84,13 @@ import ( "github.com/ava-labs/avalanchego/snow" "github.com/ava-labs/avalanchego/snow/consensus/snowman" "github.com/ava-labs/avalanchego/snow/engine/snowman/block" + avalancheUptime "github.com/ava-labs/avalanchego/snow/uptime" + avalancheValidators "github.com/ava-labs/avalanchego/snow/validators" "github.com/ava-labs/avalanchego/utils/perms" "github.com/ava-labs/avalanchego/utils/profiler" "github.com/ava-labs/avalanchego/utils/timer/mockable" "github.com/ava-labs/avalanchego/utils/units" + "github.com/ava-labs/avalanchego/version" "github.com/ava-labs/avalanchego/vms/components/chain" commonEng "github.com/ava-labs/avalanchego/snow/engine/common" @@ -129,6 +136,8 @@ const ( txGossipThrottlingPeriod = 10 * time.Second txGossipThrottlingLimit = 2 txGossipPollSize = 1 + + loadValidatorsFrequency = 1 * time.Minute ) // Define the API endpoints for the VM @@ -141,11 +150,12 @@ const ( var ( // Set last accepted key to be longer than the keys used to store accepted block IDs. - lastAcceptedKey = []byte("last_accepted_key") - acceptedPrefix = []byte("snowman_accepted") - metadataPrefix = []byte("metadata") - warpPrefix = []byte("warp") - ethDBPrefix = []byte("ethdb") + lastAcceptedKey = []byte("last_accepted_key") + acceptedPrefix = []byte("snowman_accepted") + metadataPrefix = []byte("metadata") + warpPrefix = []byte("warp") + ethDBPrefix = []byte("ethdb") + validatorsDBPrefix = []byte("validators") ) var ( @@ -216,6 +226,8 @@ type VM struct { // set to a prefixDB with the prefix [warpPrefix] warpDB database.Database + validatorsDB database.Database + toEngine chan<- commonEng.Message syntacticBlockValidator BlockValidator @@ -239,7 +251,7 @@ type VM struct { // Metrics sdkMetrics *prometheus.Registry - bootstrapped bool + bootstrapped avalancheUtils.Atomic[bool] logger SubnetEVMLogger // State sync server and client @@ -256,6 +268,9 @@ type VM struct { ethTxPushGossiper avalancheUtils.Atomic[*gossip.PushGossiper[*GossipEthTx]] ethTxPullGossiper gossip.Gossiper + uptimeManager uptimeinterfaces.PausableManager + validatorState validatorsinterfaces.State + chainAlias string // RPC handlers (should be stopped before closing chaindb) rpcHandlers []interface{ Stop() } @@ -491,6 +506,15 @@ func (vm *VM) Initialize( vm.Network = peer.NewNetwork(p2pNetwork, appSender, vm.networkCodec, chainCtx.NodeID, vm.config.MaxOutboundActiveRequests) vm.client = peer.NewNetworkClient(vm.Network) + vm.validatorState, err = validators.NewState(vm.validatorsDB) + if err != nil { + return fmt.Errorf("failed to initialize validator state: %w", err) + } + + // Initialize uptime manager + vm.uptimeManager = uptime.NewPausableManager(avalancheUptime.NewManager(vm.validatorState, &vm.clock)) + vm.validatorState.RegisterListener(vm.uptimeManager) + // Initialize warp backend offchainWarpMessages := make([][]byte, len(vm.config.WarpOffChainMessages)) for i, hexMsg := range vm.config.WarpOffChainMessages { @@ -674,38 +698,64 @@ func (vm *VM) initChainState(lastAcceptedBlock *types.Block) error { func (vm *VM) SetState(_ context.Context, state snow.State) error { switch state { case snow.StateSyncing: - vm.bootstrapped = false + vm.bootstrapped.Set(false) return nil case snow.Bootstrapping: - vm.bootstrapped = false - if err := vm.StateSyncClient.Error(); err != nil { - return err - } - // After starting bootstrapping, do not attempt to resume a previous state sync. - if err := vm.StateSyncClient.ClearOngoingSummary(); err != nil { - return err - } - // Ensure snapshots are initialized before bootstrapping (i.e., if state sync is skipped). - // Note calling this function has no effect if snapshots are already initialized. - vm.blockChain.InitializeSnapshots() - return nil + return vm.onBootstrapStarted() case snow.NormalOp: - // Initialize goroutines related to block building once we enter normal operation as there is no need to handle mempool gossip before this point. - if err := vm.initBlockBuilding(); err != nil { - return fmt.Errorf("failed to initialize block building: %w", err) - } - vm.bootstrapped = true - return nil + return vm.onNormalOperationsStarted() default: return snow.ErrUnknownState } } -// initBlockBuilding starts goroutines to manage block building -func (vm *VM) initBlockBuilding() error { +// onBootstrapStarted marks this VM as bootstrapping +func (vm *VM) onBootstrapStarted() error { + vm.bootstrapped.Set(false) + if err := vm.StateSyncClient.Error(); err != nil { + return err + } + // After starting bootstrapping, do not attempt to resume a previous state sync. + if err := vm.StateSyncClient.ClearOngoingSummary(); err != nil { + return err + } + // Ensure snapshots are initialized before bootstrapping (i.e., if state sync is skipped). + // Note calling this function has no effect if snapshots are already initialized. + vm.blockChain.InitializeSnapshots() + + return nil +} + +// onNormalOperationsStarted marks this VM as bootstrapped +func (vm *VM) onNormalOperationsStarted() error { + if vm.bootstrapped.Get() { + return nil + } + vm.bootstrapped.Set(true) + ctx, cancel := context.WithCancel(context.TODO()) vm.cancel = cancel + // update validators first + if err := vm.performValidatorUpdate(ctx); err != nil { + return fmt.Errorf("failed to update validators: %w", err) + } + vdrIDs := vm.validatorState.GetNodeIDs().List() + // Then start tracking with updated validators + // StartTracking initializes the uptime tracking with the known validators + // and update their uptime to account for the time we were being offline. + if err := vm.uptimeManager.StartTracking(vdrIDs); err != nil { + return fmt.Errorf("failed to start tracking uptime: %w", err) + } + // dispatch validator set update + vm.shutdownWg.Add(1) + go func() { + vm.dispatchUpdateValidators(ctx) + vm.shutdownWg.Done() + }() + + // Initialize goroutines related to block building + // once we enter normal operation as there is no need to handle mempool gossip before this point. ethTxGossipMarshaller := GossipEthTxMarshaller{} ethTxGossipClient := vm.Network.NewClient(p2p.TxGossipHandlerID, p2p.WithValidatorSampling(vm.validators)) ethTxGossipMetrics, err := gossip.NewMetrics(vm.sdkMetrics, ethTxGossipNamespace) @@ -714,7 +764,7 @@ func (vm *VM) initBlockBuilding() error { } ethTxPool, err := NewGossipEthTxPool(vm.txPool, vm.sdkMetrics) if err != nil { - return err + return fmt.Errorf("failed to initialize gossip eth tx pool: %w", err) } vm.shutdownWg.Add(1) go func() { @@ -772,7 +822,7 @@ func (vm *VM) initBlockBuilding() error { } if err := vm.Network.AddHandler(p2p.TxGossipHandlerID, vm.ethTxGossipHandler); err != nil { - return err + return fmt.Errorf("failed to add eth tx gossip handler: %w", err) } if vm.ethTxPullGossiper == nil { @@ -832,6 +882,15 @@ func (vm *VM) Shutdown(context.Context) error { if vm.cancel != nil { vm.cancel() } + if vm.bootstrapped.Get() { + vdrIDs := vm.validatorState.GetNodeIDs().List() + if err := vm.uptimeManager.StopTracking(vdrIDs); err != nil { + return fmt.Errorf("failed to stop tracking uptime: %w", err) + } + if err := vm.validatorState.WriteState(); err != nil { + return fmt.Errorf("failed to write validator: %w", err) + } + } vm.Network.Shutdown() if err := vm.StateSyncClient.Shutdown(); err != nil { log.Error("error stopping state syncer", "err", err) @@ -1280,12 +1339,15 @@ func (vm *VM) initializeDBs(avaDB database.Database) error { vm.db = versiondb.New(db) vm.acceptedBlockDB = prefixdb.New(acceptedPrefix, vm.db) vm.metadataDB = prefixdb.New(metadataPrefix, vm.db) - // Note warpDB is not part of versiondb because it is not necessary - // that warp signatures are committed to the database atomically with + // Note warpDB and validatorsDB are not part of versiondb because it is not necessary + // that they are committed to the database atomically with // the last accepted block. // [warpDB] is used to store warp message signatures // set to a prefixDB with the prefix [warpPrefix] vm.warpDB = prefixdb.New(warpPrefix, db) + // [validatorsDB] is used to store the current validator set and uptimes + // set to a prefixDB with the prefix [validatorsDBPrefix] + vm.validatorsDB = prefixdb.New(validatorsDBPrefix, db) return nil } @@ -1344,3 +1406,97 @@ func (vm *VM) createDatabase(dbConfig avalancheNode.DatabaseConfig) (database.Da return db, nil } + +func (vm *VM) Connected(ctx context.Context, nodeID ids.NodeID, version *version.Application) error { + if err := vm.uptimeManager.Connect(nodeID); err != nil { + return fmt.Errorf("uptime manager failed to connect node %s: %w", nodeID, err) + } + return vm.Network.Connected(ctx, nodeID, version) +} + +func (vm *VM) Disconnected(ctx context.Context, nodeID ids.NodeID) error { + if err := vm.uptimeManager.Disconnect(nodeID); err != nil { + return fmt.Errorf("uptime manager failed to disconnect node %s: %w", nodeID, err) + } + + return vm.Network.Disconnected(ctx, nodeID) +} + +func (vm *VM) dispatchUpdateValidators(ctx context.Context) { + ticker := time.NewTicker(loadValidatorsFrequency) + defer ticker.Stop() + + for { + select { + case <-ticker.C: + vm.ctx.Lock.Lock() + if err := vm.performValidatorUpdate(ctx); err != nil { + log.Error("failed to update validators", "error", err) + } + vm.ctx.Lock.Unlock() + case <-ctx.Done(): + return + } + } +} + +// performValidatorUpdate updates the validator state with the current validator set +// and writes the state to the database. +func (vm *VM) performValidatorUpdate(ctx context.Context) error { + now := time.Now() + log.Debug("performing validator update") + // get current validator set + currentValidatorSet, _, err := vm.ctx.ValidatorState.GetCurrentValidatorSet(ctx, vm.ctx.SubnetID) + if err != nil { + return fmt.Errorf("failed to get current validator set: %w", err) + } + + // load the current validator set into the validator state + if err := loadValidators(vm.validatorState, currentValidatorSet); err != nil { + return fmt.Errorf("failed to load current validators: %w", err) + } + + // write validators to the database + if err := vm.validatorState.WriteState(); err != nil { + return fmt.Errorf("failed to write validator state: %w", err) + } + + // TODO: add metrics + log.Debug("validator update complete", "duration", time.Since(now)) + return nil +} + +// loadValidators loads the [validators] into the validator state [validatorState] +func loadValidators(validatorState validatorsinterfaces.State, newValidators map[ids.ID]*avalancheValidators.GetCurrentValidatorOutput) error { + currentValidationIDs := validatorState.GetValidationIDs() + // first check if we need to delete any existing validators + for vID := range currentValidationIDs { + // if the validator is not in the new set of validators + // delete the validator + if _, exists := newValidators[vID]; !exists { + validatorState.DeleteValidator(vID) + } + } + + // then load the new validators + for newVID, newVdr := range newValidators { + currentVdr := validatorsinterfaces.Validator{ + ValidationID: newVID, + NodeID: newVdr.NodeID, + Weight: newVdr.Weight, + StartTimestamp: newVdr.StartTime, + IsActive: newVdr.IsActive, + IsSoV: newVdr.IsSoV, + } + if currentValidationIDs.Contains(newVID) { + if err := validatorState.UpdateValidator(currentVdr); err != nil { + return err + } + } else { + if err := validatorState.AddValidator(currentVdr); err != nil { + return err + } + } + } + return nil +} diff --git a/plugin/evm/vm_test.go b/plugin/evm/vm_test.go index 6f1753e62e..55fde2844f 100644 --- a/plugin/evm/vm_test.go +++ b/plugin/evm/vm_test.go @@ -34,10 +34,7 @@ import ( "github.com/ava-labs/avalanchego/snow/consensus/snowman" commonEng "github.com/ava-labs/avalanchego/snow/engine/common" "github.com/ava-labs/avalanchego/snow/engine/enginetest" - "github.com/ava-labs/avalanchego/snow/validators/validatorstest" "github.com/ava-labs/avalanchego/upgrade" - avalancheConstants "github.com/ava-labs/avalanchego/utils/constants" - "github.com/ava-labs/avalanchego/utils/crypto/bls" "github.com/ava-labs/avalanchego/utils/formatting" "github.com/ava-labs/avalanchego/utils/logging" "github.com/ava-labs/avalanchego/vms/components/chain" @@ -63,17 +60,14 @@ import ( "github.com/ava-labs/subnet-evm/vmerrs" avagoconstants "github.com/ava-labs/avalanchego/utils/constants" - avalancheWarp "github.com/ava-labs/avalanchego/vms/platformvm/warp" ) var ( - testNetworkID uint32 = avagoconstants.UnitTestID - testCChainID = ids.ID{'c', 'c', 'h', 'a', 'i', 'n', 't', 'e', 's', 't'} - testXChainID = ids.ID{'t', 'e', 's', 't', 'x'} - testMinGasPrice int64 = 225_000_000_000 + testNetworkID uint32 = avagoconstants.UnitTestID + + testMinGasPrice int64 = 225_000_000_000 testKeys []*ecdsa.PrivateKey testEthAddrs []common.Address // testEthAddrs[i] corresponds to testKeys[i] - testAvaxAssetID = ids.ID{1, 2, 3} username = "Johns" password = "CjasdjhiPeirbSenfeI13" // #nosec G101 @@ -139,40 +133,6 @@ func buildGenesisTest(t *testing.T, genesisJSON string) []byte { return genesisBytes } -func NewContext() *snow.Context { - ctx := utils.TestSnowContext() - ctx.NodeID = ids.GenerateTestNodeID() - ctx.NetworkID = testNetworkID - ctx.ChainID = testCChainID - ctx.AVAXAssetID = testAvaxAssetID - ctx.XChainID = testXChainID - aliaser := ctx.BCLookup.(ids.Aliaser) - _ = aliaser.Alias(testCChainID, "C") - _ = aliaser.Alias(testCChainID, testCChainID.String()) - _ = aliaser.Alias(testXChainID, "X") - _ = aliaser.Alias(testXChainID, testXChainID.String()) - ctx.ValidatorState = &validatorstest.State{ - GetSubnetIDF: func(_ context.Context, chainID ids.ID) (ids.ID, error) { - subnetID, ok := map[ids.ID]ids.ID{ - avalancheConstants.PlatformChainID: avalancheConstants.PrimaryNetworkID, - testXChainID: avalancheConstants.PrimaryNetworkID, - testCChainID: avalancheConstants.PrimaryNetworkID, - }[chainID] - if !ok { - return ids.Empty, errors.New("unknown chain") - } - return subnetID, nil - }, - } - blsSecretKey, err := bls.NewSecretKey() - if err != nil { - panic(err) - } - ctx.WarpSigner = avalancheWarp.NewSigner(blsSecretKey, ctx.NetworkID, ctx.ChainID) - ctx.PublicKey = bls.PublicFromSecretKey(blsSecretKey) - return ctx -} - // setupGenesis sets up the genesis // If [genesisJSON] is empty, defaults to using [genesisJSONLatest] func setupGenesis( @@ -188,7 +148,7 @@ func setupGenesis( genesisJSON = genesisJSONLatest } genesisBytes := buildGenesisTest(t, genesisJSON) - ctx := NewContext() + ctx := utils.TestSnowContext() baseDB := memdb.New() @@ -494,7 +454,7 @@ func TestBuildEthTxBlock(t *testing.T) { if err := restartedVM.Initialize( context.Background(), - NewContext(), + utils.TestSnowContext(), dbManager, genesisBytes, []byte(""), @@ -3155,7 +3115,7 @@ func TestParentBeaconRootBlock(t *testing.T) { func TestStandaloneDB(t *testing.T) { vm := &VM{} - ctx := NewContext() + ctx := utils.TestSnowContext() baseDB := memdb.New() atomicMemory := atomic.NewMemory(prefixdb.New([]byte{0}, baseDB)) ctx.SharedMemory = atomicMemory.NewSharedMemory(ctx.ChainID) diff --git a/plugin/evm/vm_upgrade_bytes_test.go b/plugin/evm/vm_upgrade_bytes_test.go index aa44d1d663..f8043e3499 100644 --- a/plugin/evm/vm_upgrade_bytes_test.go +++ b/plugin/evm/vm_upgrade_bytes_test.go @@ -111,6 +111,9 @@ func TestVMUpgradeBytesPrecompile(t *testing.T) { } }() // Set the VM's state to NormalOp to initialize the tx pool. + if err := vm.SetState(context.Background(), snow.Bootstrapping); err != nil { + t.Fatal(err) + } if err := vm.SetState(context.Background(), snow.NormalOp); err != nil { t.Fatal(err) } diff --git a/plugin/evm/vm_validators_state_test.go b/plugin/evm/vm_validators_state_test.go new file mode 100644 index 0000000000..8bf8c3546f --- /dev/null +++ b/plugin/evm/vm_validators_state_test.go @@ -0,0 +1,363 @@ +package evm + +import ( + "context" + "testing" + "time" + + "github.com/ava-labs/avalanchego/database" + "github.com/ava-labs/avalanchego/database/memdb" + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/snow" + commonEng "github.com/ava-labs/avalanchego/snow/engine/common" + "github.com/ava-labs/avalanchego/snow/engine/enginetest" + avagoValidators "github.com/ava-labs/avalanchego/snow/validators" + "github.com/ava-labs/avalanchego/snow/validators/validatorstest" + "github.com/ava-labs/subnet-evm/core" + "github.com/ava-labs/subnet-evm/plugin/evm/validators" + "github.com/ava-labs/subnet-evm/plugin/evm/validators/interfaces" + "github.com/ava-labs/subnet-evm/utils" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" +) + +func TestValidatorState(t *testing.T) { + require := require.New(t) + genesis := &core.Genesis{} + require.NoError(genesis.UnmarshalJSON([]byte(genesisJSONLatest))) + genesisJSON, err := genesis.MarshalJSON() + require.NoError(err) + + vm := &VM{} + ctx, dbManager, genesisBytes, issuer, _ := setupGenesis(t, string(genesisJSON)) + appSender := &enginetest.Sender{T: t} + appSender.CantSendAppGossip = true + testNodeIDs := []ids.NodeID{ + ids.GenerateTestNodeID(), + ids.GenerateTestNodeID(), + ids.GenerateTestNodeID(), + } + testValidationIDs := []ids.ID{ + ids.GenerateTestID(), + ids.GenerateTestID(), + ids.GenerateTestID(), + } + ctx.ValidatorState = &validatorstest.State{ + GetCurrentValidatorSetF: func(ctx context.Context, subnetID ids.ID) (map[ids.ID]*avagoValidators.GetCurrentValidatorOutput, uint64, error) { + return map[ids.ID]*avagoValidators.GetCurrentValidatorOutput{ + testValidationIDs[0]: { + NodeID: testNodeIDs[0], + PublicKey: nil, + Weight: 1, + }, + testValidationIDs[1]: { + NodeID: testNodeIDs[1], + PublicKey: nil, + Weight: 1, + }, + testValidationIDs[2]: { + NodeID: testNodeIDs[2], + PublicKey: nil, + Weight: 1, + }, + }, 0, nil + }, + } + appSender.SendAppGossipF = func(context.Context, commonEng.SendConfig, []byte) error { return nil } + err = vm.Initialize( + context.Background(), + ctx, + dbManager, + genesisBytes, + []byte(""), + []byte(""), + issuer, + []*commonEng.Fx{}, + appSender, + ) + require.NoError(err, "error initializing GenesisVM") + + // Test case 1: state should not be populated until bootstrapped + require.NoError(vm.SetState(context.Background(), snow.Bootstrapping)) + require.Equal(0, vm.validatorState.GetValidationIDs().Len()) + _, _, err = vm.uptimeManager.CalculateUptime(testNodeIDs[0]) + require.ErrorIs(database.ErrNotFound, err) + require.False(vm.uptimeManager.StartedTracking()) + + // Test case 2: state should be populated after bootstrapped + require.NoError(vm.SetState(context.Background(), snow.NormalOp)) + require.Len(vm.validatorState.GetValidationIDs(), 3) + _, _, err = vm.uptimeManager.CalculateUptime(testNodeIDs[0]) + require.NoError(err) + require.True(vm.uptimeManager.StartedTracking()) + + // Test case 3: restarting VM should not lose state + vm.Shutdown(context.Background()) + // Shutdown should stop tracking + require.False(vm.uptimeManager.StartedTracking()) + + vm = &VM{} + err = vm.Initialize( + context.Background(), + utils.TestSnowContext(), // this context does not have validators state, making VM to source it from the database + dbManager, + genesisBytes, + []byte(""), + []byte(""), + issuer, + []*commonEng.Fx{}, + appSender, + ) + require.NoError(err, "error initializing GenesisVM") + require.Len(vm.validatorState.GetValidationIDs(), 3) + _, _, err = vm.uptimeManager.CalculateUptime(testNodeIDs[0]) + require.NoError(err) + require.False(vm.uptimeManager.StartedTracking()) + + // Test case 4: new validators should be added to the state + newValidationID := ids.GenerateTestID() + newNodeID := ids.GenerateTestNodeID() + testState := &validatorstest.State{ + GetCurrentValidatorSetF: func(ctx context.Context, subnetID ids.ID) (map[ids.ID]*avagoValidators.GetCurrentValidatorOutput, uint64, error) { + return map[ids.ID]*avagoValidators.GetCurrentValidatorOutput{ + testValidationIDs[0]: { + NodeID: testNodeIDs[0], + PublicKey: nil, + Weight: 1, + }, + testValidationIDs[1]: { + NodeID: testNodeIDs[1], + PublicKey: nil, + Weight: 1, + }, + testValidationIDs[2]: { + NodeID: testNodeIDs[2], + PublicKey: nil, + Weight: 1, + }, + newValidationID: { + NodeID: newNodeID, + PublicKey: nil, + Weight: 1, + }, + }, 0, nil + }, + } + vm.ctx.ValidatorState = testState + // set VM as bootstrapped + require.NoError(vm.SetState(context.Background(), snow.Bootstrapping)) + require.NoError(vm.SetState(context.Background(), snow.NormalOp)) + + // new validator should be added to the state eventually after validatorsLoadFrequency + require.EventuallyWithT(func(c *assert.CollectT) { + assert.Len(c, vm.validatorState.GetNodeIDs(), 4) + newValidator, err := vm.validatorState.GetValidator(newValidationID) + assert.NoError(c, err) + assert.Equal(c, newNodeID, newValidator.NodeID) + }, loadValidatorsFrequency*2, 5*time.Second) +} + +func TestLoadNewValidators(t *testing.T) { + testNodeIDs := []ids.NodeID{ + ids.GenerateTestNodeID(), + ids.GenerateTestNodeID(), + ids.GenerateTestNodeID(), + } + testValidationIDs := []ids.ID{ + ids.GenerateTestID(), + ids.GenerateTestID(), + ids.GenerateTestID(), + } + tests := []struct { + name string + initialValidators map[ids.ID]*avagoValidators.GetCurrentValidatorOutput + newValidators map[ids.ID]*avagoValidators.GetCurrentValidatorOutput + registerMockListenerCalls func(*interfaces.MockStateCallbackListener) + expectedLoadErr error + }{ + { + name: "before empty/after empty", + initialValidators: map[ids.ID]*avagoValidators.GetCurrentValidatorOutput{}, + newValidators: map[ids.ID]*avagoValidators.GetCurrentValidatorOutput{}, + registerMockListenerCalls: func(*interfaces.MockStateCallbackListener) {}, + }, + { + name: "before empty/after one", + initialValidators: map[ids.ID]*avagoValidators.GetCurrentValidatorOutput{}, + newValidators: map[ids.ID]*avagoValidators.GetCurrentValidatorOutput{ + testValidationIDs[0]: { + NodeID: testNodeIDs[0], + IsActive: true, + StartTime: 0, + }, + }, + registerMockListenerCalls: func(mock *interfaces.MockStateCallbackListener) { + mock.EXPECT().OnValidatorAdded(testValidationIDs[0], testNodeIDs[0], uint64(0), true).Times(1) + }, + }, + { + name: "before one/after empty", + initialValidators: map[ids.ID]*avagoValidators.GetCurrentValidatorOutput{ + testValidationIDs[0]: { + NodeID: testNodeIDs[0], + IsActive: true, + StartTime: 0, + }, + }, + newValidators: map[ids.ID]*avagoValidators.GetCurrentValidatorOutput{}, + registerMockListenerCalls: func(mock *interfaces.MockStateCallbackListener) { + // initial validator will trigger first + mock.EXPECT().OnValidatorAdded(testValidationIDs[0], testNodeIDs[0], uint64(0), true).Times(1) + // then it will be removed + mock.EXPECT().OnValidatorRemoved(testValidationIDs[0], testNodeIDs[0]).Times(1) + }, + }, + { + name: "no change", + initialValidators: map[ids.ID]*avagoValidators.GetCurrentValidatorOutput{ + testValidationIDs[0]: { + NodeID: testNodeIDs[0], + IsActive: true, + StartTime: 0, + }, + }, + newValidators: map[ids.ID]*avagoValidators.GetCurrentValidatorOutput{ + testValidationIDs[0]: { + NodeID: testNodeIDs[0], + IsActive: true, + StartTime: 0, + }, + }, + registerMockListenerCalls: func(mock *interfaces.MockStateCallbackListener) { + mock.EXPECT().OnValidatorAdded(testValidationIDs[0], testNodeIDs[0], uint64(0), true).Times(1) + }, + }, + { + name: "status and weight change and new one", + initialValidators: map[ids.ID]*avagoValidators.GetCurrentValidatorOutput{ + testValidationIDs[0]: { + NodeID: testNodeIDs[0], + IsActive: true, + StartTime: 0, + Weight: 1, + }, + }, + newValidators: map[ids.ID]*avagoValidators.GetCurrentValidatorOutput{ + testValidationIDs[0]: { + NodeID: testNodeIDs[0], + IsActive: false, + StartTime: 0, + Weight: 2, + }, + testValidationIDs[1]: { + NodeID: testNodeIDs[1], + IsActive: true, + StartTime: 0, + }, + }, + registerMockListenerCalls: func(mock *interfaces.MockStateCallbackListener) { + // initial validator will trigger first + mock.EXPECT().OnValidatorAdded(testValidationIDs[0], testNodeIDs[0], uint64(0), true).Times(1) + // then it will be updated + mock.EXPECT().OnValidatorStatusUpdated(testValidationIDs[0], testNodeIDs[0], false).Times(1) + // new validator will be added + mock.EXPECT().OnValidatorAdded(testValidationIDs[1], testNodeIDs[1], uint64(0), true).Times(1) + }, + }, + { + name: "renew validation ID", + initialValidators: map[ids.ID]*avagoValidators.GetCurrentValidatorOutput{ + testValidationIDs[0]: { + NodeID: testNodeIDs[0], + IsActive: true, + StartTime: 0, + }, + }, + newValidators: map[ids.ID]*avagoValidators.GetCurrentValidatorOutput{ + testValidationIDs[1]: { + NodeID: testNodeIDs[0], + IsActive: true, + StartTime: 0, + }, + }, + registerMockListenerCalls: func(mock *interfaces.MockStateCallbackListener) { + // initial validator will trigger first + mock.EXPECT().OnValidatorAdded(testValidationIDs[0], testNodeIDs[0], uint64(0), true).Times(1) + // then it will be removed + mock.EXPECT().OnValidatorRemoved(testValidationIDs[0], testNodeIDs[0]).Times(1) + // new validator will be added + mock.EXPECT().OnValidatorAdded(testValidationIDs[1], testNodeIDs[0], uint64(0), true).Times(1) + }, + }, + { + name: "renew node ID", + initialValidators: map[ids.ID]*avagoValidators.GetCurrentValidatorOutput{ + testValidationIDs[0]: { + NodeID: testNodeIDs[0], + IsActive: true, + StartTime: 0, + }, + }, + newValidators: map[ids.ID]*avagoValidators.GetCurrentValidatorOutput{ + testValidationIDs[0]: { + NodeID: testNodeIDs[1], + IsActive: true, + StartTime: 0, + }, + }, + expectedLoadErr: validators.ErrImmutableField, + registerMockListenerCalls: func(mock *interfaces.MockStateCallbackListener) { + // initial validator will trigger first + mock.EXPECT().OnValidatorAdded(testValidationIDs[0], testNodeIDs[0], uint64(0), true).Times(1) + // then it won't be called since we don't track the node ID changes + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(tt *testing.T) { + require := require.New(tt) + db := memdb.New() + validatorState, err := validators.NewState(db) + require.NoError(err) + + // set initial validators + for vID, validator := range test.initialValidators { + err := validatorState.AddValidator(interfaces.Validator{ + ValidationID: vID, + NodeID: validator.NodeID, + Weight: validator.Weight, + StartTimestamp: validator.StartTime, + IsActive: validator.IsActive, + IsSoV: validator.IsSoV, + }) + require.NoError(err) + } + // enable mock listener + ctrl := gomock.NewController(tt) + mockListener := interfaces.NewMockStateCallbackListener(ctrl) + test.registerMockListenerCalls(mockListener) + + validatorState.RegisterListener(mockListener) + // load new validators + err = loadValidators(validatorState, test.newValidators) + if test.expectedLoadErr != nil { + require.Error(err) + return + } + require.NoError(err) + // check if the state is as expected + require.Equal(len(test.newValidators), validatorState.GetValidationIDs().Len()) + for vID, validator := range test.newValidators { + v, err := validatorState.GetValidator(vID) + require.NoError(err) + require.Equal(validator.NodeID, v.NodeID) + require.Equal(validator.Weight, v.Weight) + require.Equal(validator.StartTime, v.StartTimestamp) + require.Equal(validator.IsActive, v.IsActive) + require.Equal(validator.IsSoV, v.IsSoV) + } + }) + } +} diff --git a/plugin/evm/vm_warp_test.go b/plugin/evm/vm_warp_test.go index b80af6fdd5..2131b79d94 100644 --- a/plugin/evm/vm_warp_test.go +++ b/plugin/evm/vm_warp_test.go @@ -481,7 +481,7 @@ func TestReceiveWarpMessage(t *testing.T) { }, { name: "C-Chain message should be signed by subnet without RequirePrimaryNetworkSigners", - sourceChainID: testCChainID, + sourceChainID: vm.ctx.CChainID, msgFrom: fromPrimary, useSigners: signersSubnet, blockTime: upgrade.InitiallyActiveTime.Add(2 * blockGap), @@ -504,7 +504,7 @@ func TestReceiveWarpMessage(t *testing.T) { }, { name: "C-Chain message should be signed by primary with RequirePrimaryNetworkSigners (impacted)", - sourceChainID: testCChainID, + sourceChainID: vm.ctx.CChainID, msgFrom: fromPrimary, useSigners: signersPrimary, blockTime: reEnableTime.Add(2 * blockGap), diff --git a/scripts/versions.sh b/scripts/versions.sh index 98942e75d2..4c045d5632 100644 --- a/scripts/versions.sh +++ b/scripts/versions.sh @@ -4,7 +4,7 @@ # shellcheck disable=SC2034 # Don't export them as they're used in the context of other calls -AVALANCHE_VERSION=${AVALANCHE_VERSION:-'v1.11.12'} +AVALANCHE_VERSION=${AVALANCHE_VERSION:-'4c199890'} GINKGO_VERSION=${GINKGO_VERSION:-'v2.2.0'} # This won't be used, but it's here to make code syncs easier diff --git a/utils/snow.go b/utils/snow.go index 67c40ee293..00901fbad5 100644 --- a/utils/snow.go +++ b/utils/snow.go @@ -4,14 +4,25 @@ package utils import ( + "context" + "errors" + "github.com/ava-labs/avalanchego/api/metrics" "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/snow" + "github.com/ava-labs/avalanchego/snow/validators" "github.com/ava-labs/avalanchego/snow/validators/validatorstest" "github.com/ava-labs/avalanchego/upgrade/upgradetest" "github.com/ava-labs/avalanchego/utils/constants" "github.com/ava-labs/avalanchego/utils/crypto/bls" "github.com/ava-labs/avalanchego/utils/logging" + "github.com/ava-labs/avalanchego/vms/platformvm/warp" +) + +var ( + testCChainID = ids.ID{'c', 'c', 'h', 'a', 'i', 'n', 't', 'e', 's', 't'} + testXChainID = ids.ID{'t', 'e', 's', 't', 'x'} + testChainID = ids.ID{'t', 'e', 's', 't', 'c', 'h', 'a', 'i', 'n'} ) func TestSnowContext() *snow.Context { @@ -20,17 +31,56 @@ func TestSnowContext() *snow.Context { panic(err) } pk := bls.PublicFromSecretKey(sk) - return &snow.Context{ - NetworkID: constants.UnitTestID, + networkID := constants.UnitTestID + chainID := testChainID + + ctx := &snow.Context{ + NetworkID: networkID, SubnetID: ids.Empty, - ChainID: ids.Empty, - NodeID: ids.EmptyNodeID, + ChainID: chainID, + NodeID: ids.GenerateTestNodeID(), + XChainID: testXChainID, + CChainID: testCChainID, NetworkUpgrades: upgradetest.GetConfig(upgradetest.Latest), PublicKey: pk, + WarpSigner: warp.NewSigner(sk, networkID, chainID), Log: logging.NoLog{}, BCLookup: ids.NewAliaser(), Metrics: metrics.NewPrefixGatherer(), ChainDataDir: "", - ValidatorState: &validatorstest.State{}, + ValidatorState: NewTestValidatorState(), + } + + aliaser := ctx.BCLookup.(ids.Aliaser) + _ = aliaser.Alias(testCChainID, "C") + _ = aliaser.Alias(testCChainID, testCChainID.String()) + _ = aliaser.Alias(testXChainID, "X") + _ = aliaser.Alias(testXChainID, testXChainID.String()) + + return ctx +} + +func NewTestValidatorState() *validatorstest.State { + return &validatorstest.State{ + GetCurrentHeightF: func(context.Context) (uint64, error) { + return 0, nil + }, + GetSubnetIDF: func(_ context.Context, chainID ids.ID) (ids.ID, error) { + subnetID, ok := map[ids.ID]ids.ID{ + constants.PlatformChainID: constants.PrimaryNetworkID, + testXChainID: constants.PrimaryNetworkID, + testCChainID: constants.PrimaryNetworkID, + }[chainID] + if !ok { + return ids.Empty, errors.New("unknown chain") + } + return subnetID, nil + }, + GetValidatorSetF: func(context.Context, uint64, ids.ID) (map[ids.NodeID]*validators.GetValidatorOutput, error) { + return map[ids.NodeID]*validators.GetValidatorOutput{}, nil + }, + GetCurrentValidatorSetF: func(context.Context, ids.ID) (map[ids.ID]*validators.GetCurrentValidatorOutput, uint64, error) { + return map[ids.ID]*validators.GetCurrentValidatorOutput{}, 0, nil + }, } }